UNPKG

cfg-kit-posthog

Version:

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

1 lines 14.4 kB
{"version":3,"sources":["../src/index.ts","../src/schemas.ts"],"sourcesContent":["import { PostHog } from 'posthog-node'\nimport { z } from 'zod'\nimport { Plugin, pluginBuilder, ConfigField } from 'cfg-kit'\nimport fetch from 'node-fetch'\nimport {\n PostHogFeatureFlagSchema,\n PostHogConfigSchema,\n PostHogFeatureFlag,\n PostHogConfig,\n} from './schemas'\n\nfunction toValidJsVarName(str: string) {\n // 1. Remove leading characters that are not letters, underscore, or dollar sign.\n // This ensures the name starts with a valid character.\n let sanitized = str.replace(/^[^a-zA-Z_$]+/, '');\n\n // 2. Replace any remaining invalid characters (not alphanumeric, underscore, or dollar sign) with an empty string.\n sanitized = sanitized.replace(/[^a-zA-Z0-9_$]/g, '');\n\n // 3. Ensure the name is not a JavaScript reserved keyword.\n // If it is, you might prepend an underscore or similar to avoid conflict.\n // This example includes a basic check for common keywords, but a comprehensive list is extensive.\n const reservedKeywords = [\n 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default',\n 'delete', 'do', 'else', 'export', 'extends', 'false', 'finally', 'for', 'function',\n 'if', 'import', 'in', 'instanceof', 'new', 'null', 'return', 'super', 'switch',\n 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield',\n 'enum', 'implements', 'interface', 'let', 'package', 'private', 'protected',\n 'public', 'static', 'interface', 'await', 'abstract', 'boolean', 'byte', 'char',\n 'double', 'final', 'float', 'goto', 'int', 'long', 'native', 'short', 'synchronized',\n 'throws', 'transient', 'volatile'\n ];\n if (reservedKeywords.includes(sanitized)) {\n sanitized = '_' + sanitized;\n }\n\n // 4. Handle empty strings after sanitization (e.g., if the original string was all invalid characters).\n // In such cases, you might return a default name or throw an error.\n if (sanitized === '') {\n return 'defaultVarName'; // Or handle as an error\n }\n\n return sanitized;\n}\n\n// Plugin interface\nexport interface PostHogPluginOptions {\n posthog?: PostHog\n apiKey: string\n host?: string\n projectId: string\n}\ntype PostHogPluginConfig = z.infer<typeof PostHogConfigSchema>\n\n// Re-export schemas and types for convenience\nexport {\n PostHogFeatureFlagSchema,\n PostHogConfigSchema,\n PostHogFeatureFlag,\n PostHogConfig,\n}\n\n// Main plugin class\nexport class PostHogPlugin extends Plugin {\n private posthog: PostHog\n private config: PostHogPluginConfig\n private projectId: string\n\n constructor(config: PostHogPluginConfig, options: PostHogPluginOptions) {\n super()\n this.config = config\n this.projectId = options.projectId\n // PostHog will be injected as peer dependency\n this.posthog = options.posthog ?? new PostHog(options.apiKey, {\n host: options.host ?? 'https://app.posthog.com',\n })\n }\n\n async build() {\n return pluginBuilder.buildEnv({\n server: {\n POSTHOG_API_KEY: z.string().min(1),\n POSTHOG_HOST: z.string().optional(),\n POSTHOG_PROJECT_ID: z.string().min(1),\n },\n client: {},\n clientPrefix: '',\n runtimeEnv: {},\n emptyStringAsUndefined: true,\n }).defineConfig(({ serverField }: any) => {\n let featureFlagsAsFields: Record<string, ConfigField<string, { POSTHOG_API_KEY: string; POSTHOG_HOST?: string | undefined; POSTHOG_PROJECT_ID: string; }>> = {}\n\n for (const featureFlag of this.config.featureFlags) {\n featureFlagsAsFields[toValidJsVarName(featureFlag.key)] = serverField(z.string(), async ({ env }: any) => {\n // If credentials are not available, return a placeholder ID\n if (!env.POSTHOG_API_KEY || !env.POSTHOG_PROJECT_ID) {\n console.log(`PostHog credentials not set for feature flag '${featureFlag.key}', returning placeholder ID`)\n return `placeholder-${featureFlag.key}-id`\n }\n\n try {\n // Check if feature flag exists\n const response = await fetch(`${env.POSTHOG_HOST || 'https://app.posthog.com'}/api/projects/${env.POSTHOG_PROJECT_ID}/feature_flags/`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${env.POSTHOG_API_KEY}`,\n 'Content-Type': 'application/json',\n },\n })\n\n if (!response.ok) {\n throw new Error(`Failed to fetch feature flags: ${response.statusText}`)\n }\n\n const existingFlags = await response.json() as { results: { id: string, key: string }[] }\n const existingFlag = existingFlags.results?.find((flag) => flag.key === featureFlag.key)\n\n if (existingFlag) {\n // Feature flag exists, return its ID without updating\n // As per requirements: \"only create feature flags if they are not defined\"\n console.log(`Feature flag '${featureFlag.key}' already exists, skipping creation`)\n return existingFlag.id.toString()\n }\n\n // Feature flag doesn't exist, create it\n const createResponse = await fetch(`${env.POSTHOG_HOST || 'https://app.posthog.com'}/api/projects/${env.POSTHOG_PROJECT_ID}/feature_flags/`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${env.POSTHOG_API_KEY}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n key: featureFlag.key,\n name: featureFlag.name,\n description: featureFlag.description,\n active: featureFlag.active,\n filters: featureFlag.filters,\n variants: featureFlag.variants,\n tags: featureFlag.tags,\n }),\n })\n\n if (!createResponse.ok) {\n const errorText = await createResponse.text()\n throw new Error(`Failed to create feature flag '${featureFlag.key}': ${createResponse.statusText} - ${errorText}`)\n }\n\n const createdFlag = await createResponse.json() as { id: string }\n console.log(`Created feature flag '${featureFlag.key}' with ID: ${createdFlag.id}`)\n return createdFlag.id.toString()\n\n } catch (error) {\n console.error(`Error managing feature flag '${featureFlag.key}':`, error)\n throw error\n }\n })\n }\n\n return {\n server: {\n ...featureFlagsAsFields,\n },\n }\n })\n }\n}\n\n// Helper functions for defining resources\nexport function definePostHogFeatureFlag(featureFlag: PostHogFeatureFlag): PostHogFeatureFlag {\n return PostHogFeatureFlagSchema.parse(featureFlag)\n}\n\nexport function definePostHogConfig(config: PostHogConfig): PostHogConfig {\n return PostHogConfigSchema.parse(config)\n}\n\n// Default export\nexport default PostHogPlugin ","import { z } from 'zod'\n\n// PostHog feature flag schema\nexport const PostHogFeatureFlagSchema = z.object({\n key: z.string().min(1, 'Feature flag key is required'),\n name: z.string().min(1, 'Feature flag name is required'),\n description: z.string().optional(),\n active: z.boolean().default(true),\n filters: z.object({\n groups: z.array(z.object({\n properties: z.array(z.object({\n key: z.string(),\n operator: z.enum(['exact', 'is_not', 'icontains', 'not_icontains', 'regex', 'not_regex', 'gt', 'gte', 'lt', 'lte', 'is_set', 'is_not_set']).optional(),\n value: z.union([z.string(), z.number(), z.boolean(), z.array(z.string()), z.array(z.number())]).optional(),\n type: z.enum(['person', 'event', 'element', 'static-cohort', 'behavioral']).optional(),\n })).default([]),\n rollout_percentage: z.number().min(0).max(100).default(100),\n variant: z.string().optional(),\n })).default([{\n properties: [],\n rollout_percentage: 100\n }])\n }).default({\n groups: [{\n properties: [],\n rollout_percentage: 100\n }]\n }),\n variants: z.array(z.object({\n key: z.string(),\n name: z.string().optional(),\n rollout_percentage: z.number().min(0).max(100),\n })).optional(),\n tags: z.array(z.string()).default([]),\n})\n\nexport const PostHogConfigSchema = z.object({\n featureFlags: z.array(PostHogFeatureFlagSchema).default([]),\n})\n\n// Types\nexport type PostHogFeatureFlag = z.infer<typeof PostHogFeatureFlagSchema>\nexport type PostHogConfig = z.infer<typeof PostHogConfigSchema> "],"mappings":"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","names":["z","z"]}