UNPKG

cfg-kit-posthog

Version:

PostHog plugin for cfg-kit - Define PostHog feature flags as configuration

262 lines (259 loc) 9.02 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { PostHogConfigSchema: () => PostHogConfigSchema, PostHogFeatureFlagSchema: () => PostHogFeatureFlagSchema, PostHogPlugin: () => PostHogPlugin, default: () => index_default, definePostHogConfig: () => definePostHogConfig, definePostHogFeatureFlag: () => definePostHogFeatureFlag }); module.exports = __toCommonJS(index_exports); var import_posthog_node = require("posthog-node"); var import_zod2 = require("zod"); var import_cfg_kit = require("cfg-kit"); var import_node_fetch = __toESM(require("node-fetch")); // src/schemas.ts var import_zod = require("zod"); var PostHogFeatureFlagSchema = import_zod.z.object({ key: import_zod.z.string().min(1, "Feature flag key is required"), name: import_zod.z.string().min(1, "Feature flag name is required"), description: import_zod.z.string().optional(), active: import_zod.z.boolean().default(true), filters: import_zod.z.object({ groups: import_zod.z.array(import_zod.z.object({ properties: import_zod.z.array(import_zod.z.object({ key: import_zod.z.string(), operator: import_zod.z.enum(["exact", "is_not", "icontains", "not_icontains", "regex", "not_regex", "gt", "gte", "lt", "lte", "is_set", "is_not_set"]).optional(), value: import_zod.z.union([import_zod.z.string(), import_zod.z.number(), import_zod.z.boolean(), import_zod.z.array(import_zod.z.string()), import_zod.z.array(import_zod.z.number())]).optional(), type: import_zod.z.enum(["person", "event", "element", "static-cohort", "behavioral"]).optional() })).default([]), rollout_percentage: import_zod.z.number().min(0).max(100).default(100), variant: import_zod.z.string().optional() })).default([{ properties: [], rollout_percentage: 100 }]) }).default({ groups: [{ properties: [], rollout_percentage: 100 }] }), variants: import_zod.z.array(import_zod.z.object({ key: import_zod.z.string(), name: import_zod.z.string().optional(), rollout_percentage: import_zod.z.number().min(0).max(100) })).optional(), tags: import_zod.z.array(import_zod.z.string()).default([]) }); var PostHogConfigSchema = import_zod.z.object({ featureFlags: import_zod.z.array(PostHogFeatureFlagSchema).default([]) }); // src/index.ts function toValidJsVarName(str) { let sanitized = str.replace(/^[^a-zA-Z_$]+/, ""); sanitized = sanitized.replace(/[^a-zA-Z0-9_$]/g, ""); const reservedKeywords = [ "break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do", "else", "export", "extends", "false", "finally", "for", "function", "if", "import", "in", "instanceof", "new", "null", "return", "super", "switch", "this", "throw", "true", "try", "typeof", "var", "void", "while", "with", "yield", "enum", "implements", "interface", "let", "package", "private", "protected", "public", "static", "interface", "await", "abstract", "boolean", "byte", "char", "double", "final", "float", "goto", "int", "long", "native", "short", "synchronized", "throws", "transient", "volatile" ]; if (reservedKeywords.includes(sanitized)) { sanitized = "_" + sanitized; } if (sanitized === "") { return "defaultVarName"; } return sanitized; } var PostHogPlugin = class extends import_cfg_kit.Plugin { posthog; config; projectId; constructor(config, options) { super(); this.config = config; this.projectId = options.projectId; this.posthog = options.posthog ?? new import_posthog_node.PostHog(options.apiKey, { host: options.host ?? "https://app.posthog.com" }); } async build() { return import_cfg_kit.pluginBuilder.buildEnv({ server: { POSTHOG_API_KEY: import_zod2.z.string().min(1), POSTHOG_HOST: import_zod2.z.string().optional(), POSTHOG_PROJECT_ID: import_zod2.z.string().min(1) }, client: {}, clientPrefix: "", runtimeEnv: {}, emptyStringAsUndefined: true }).defineConfig(({ serverField }) => { let featureFlagsAsFields = {}; for (const featureFlag of this.config.featureFlags) { featureFlagsAsFields[toValidJsVarName(featureFlag.key)] = serverField(import_zod2.z.string(), async ({ env }) => { var _a; if (!env.POSTHOG_API_KEY || !env.POSTHOG_PROJECT_ID) { console.log(`PostHog credentials not set for feature flag '${featureFlag.key}', returning placeholder ID`); return `placeholder-${featureFlag.key}-id`; } try { const response = await (0, import_node_fetch.default)(`${env.POSTHOG_HOST || "https://app.posthog.com"}/api/projects/${env.POSTHOG_PROJECT_ID}/feature_flags/`, { method: "GET", headers: { "Authorization": `Bearer ${env.POSTHOG_API_KEY}`, "Content-Type": "application/json" } }); if (!response.ok) { throw new Error(`Failed to fetch feature flags: ${response.statusText}`); } const existingFlags = await response.json(); const existingFlag = (_a = existingFlags.results) == null ? void 0 : _a.find((flag) => flag.key === featureFlag.key); if (existingFlag) { console.log(`Feature flag '${featureFlag.key}' already exists, skipping creation`); return existingFlag.id.toString(); } const createResponse = await (0, import_node_fetch.default)(`${env.POSTHOG_HOST || "https://app.posthog.com"}/api/projects/${env.POSTHOG_PROJECT_ID}/feature_flags/`, { method: "POST", headers: { "Authorization": `Bearer ${env.POSTHOG_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ key: featureFlag.key, name: featureFlag.name, description: featureFlag.description, active: featureFlag.active, filters: featureFlag.filters, variants: featureFlag.variants, tags: featureFlag.tags }) }); if (!createResponse.ok) { const errorText = await createResponse.text(); throw new Error(`Failed to create feature flag '${featureFlag.key}': ${createResponse.statusText} - ${errorText}`); } const createdFlag = await createResponse.json(); console.log(`Created feature flag '${featureFlag.key}' with ID: ${createdFlag.id}`); return createdFlag.id.toString(); } catch (error) { console.error(`Error managing feature flag '${featureFlag.key}':`, error); throw error; } }); } return { server: { ...featureFlagsAsFields } }; }); } }; function definePostHogFeatureFlag(featureFlag) { return PostHogFeatureFlagSchema.parse(featureFlag); } function definePostHogConfig(config) { return PostHogConfigSchema.parse(config); } var index_default = PostHogPlugin; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { PostHogConfigSchema, PostHogFeatureFlagSchema, PostHogPlugin, definePostHogConfig, definePostHogFeatureFlag }); //# sourceMappingURL=index.js.map