cfg-kit-posthog
Version:
PostHog plugin for cfg-kit - Define PostHog feature flags as configuration
262 lines (259 loc) • 9.02 kB
JavaScript
;
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