posthog-flag-toolkit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +184 -0
- package/dist/adapters/inngest.cjs +20 -0
- package/dist/adapters/inngest.cjs.map +1 -0
- package/dist/adapters/inngest.d.cts +29 -0
- package/dist/adapters/inngest.d.ts +29 -0
- package/dist/adapters/inngest.js +18 -0
- package/dist/adapters/inngest.js.map +1 -0
- package/dist/adapters/slack.cjs +75 -0
- package/dist/adapters/slack.cjs.map +1 -0
- package/dist/adapters/slack.d.cts +27 -0
- package/dist/adapters/slack.d.ts +27 -0
- package/dist/adapters/slack.js +73 -0
- package/dist/adapters/slack.js.map +1 -0
- package/dist/index.cjs +674 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +371 -0
- package/dist/index.d.ts +371 -0
- package/dist/index.js +647 -0
- package/dist/index.js.map +1 -0
- package/package.json +114 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/guardian/decision.ts","../src/logger.ts","../src/posthog/api.ts","../src/posthog/hogql.ts","../src/step-runner.ts","../src/guardian/thresholds.ts","../src/guardian/run-flag-guardian.ts","../src/registry/naming.ts","../src/registry/define.ts","../src/release-tracker/run-release-tracker.ts","../src/sync/run-flag-sync.ts"],"names":["baseUrl","evaluation"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { EvaluationMetrics } from \"../posthog/hogql.js\";\nimport type { GuardianThresholds } from \"./thresholds.js\";\n\nexport type DecisionKind =\n | \"insufficient_data\"\n | \"no_regression\"\n | \"regression_detected\"\n | \"auto_disabled\";\n\nexport interface Decision {\n kind: DecisionKind;\n reason: string;\n}\n\nexport function fmtPct(n: number | null | undefined): string {\n if (n == null) return \"—\";\n return `${(n * 100).toFixed(2)}%`;\n}\n\nexport function detectRegression(\n metrics: EvaluationMetrics,\n thresholds: GuardianThresholds,\n): Decision {\n const { treatment, control } = metrics;\n\n // Error rate regression: treatment >= Nx control AND both nonzero\n if (\n treatment.errorRate != null &&\n control.errorRate != null &&\n control.errorRate > 0 &&\n treatment.errorRate / control.errorRate >= thresholds.errorRateRatioThreshold\n ) {\n const ratio = treatment.errorRate / control.errorRate;\n return {\n kind: \"regression_detected\",\n reason: `error rate ratio ${ratio.toFixed(2)}x (${fmtPct(treatment.errorRate)} vs ${fmtPct(control.errorRate)})`,\n };\n }\n\n // Edge case: control error rate is 0 but treatment is non-trivial\n if (\n treatment.errorRate != null &&\n treatment.errorRate > 0.01 &&\n (control.errorRate == null || control.errorRate === 0)\n ) {\n return {\n kind: \"regression_detected\",\n reason: `treatment error rate ${fmtPct(treatment.errorRate)} vs control 0%`,\n };\n }\n\n // Publish success drop\n if (\n treatment.publishSuccessRate != null &&\n control.publishSuccessRate != null &&\n control.publishSuccessRate - treatment.publishSuccessRate >=\n thresholds.publishSuccessDropThreshold\n ) {\n const drop = control.publishSuccessRate - treatment.publishSuccessRate;\n return {\n kind: \"regression_detected\",\n reason: `publish success drop ${(drop * 100).toFixed(1)}pp (${fmtPct(treatment.publishSuccessRate)} vs ${fmtPct(control.publishSuccessRate)})`,\n };\n }\n\n return { kind: \"no_regression\", reason: \"all metrics within thresholds\" };\n}\n\nexport function meetsSampleFloor(\n metrics: EvaluationMetrics,\n thresholds: GuardianThresholds,\n): boolean {\n return (\n metrics.treatment.eventCount >= thresholds.minSampleSize &&\n metrics.treatment.uniqueUsers >= thresholds.minUniqueUsers &&\n metrics.control.eventCount >= thresholds.minSampleSize &&\n metrics.control.uniqueUsers >= thresholds.minUniqueUsers\n );\n}\n","export interface Logger {\n info(message: string, data?: Record<string, unknown>): void;\n warn(message: string, data?: Record<string, unknown>): void;\n error(message: string, data?: Record<string, unknown>): void;\n}\n\nexport const consoleLogger: Logger = {\n info: (msg, data) => console.log(`[posthog-flag-toolkit] ${msg}`, data ?? \"\"),\n warn: (msg, data) => console.warn(`[posthog-flag-toolkit] ${msg}`, data ?? \"\"),\n error: (msg, data) => console.error(`[posthog-flag-toolkit] ${msg}`, data ?? \"\"),\n};\n","import type {\n ActivityList,\n PostHogClientConfig,\n PostHogExperiment,\n PostHogExperimentList,\n PostHogFlag,\n PostHogFlagFilters,\n PostHogFlagList,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://us.posthog.com\";\nconst MAX_RETRIES = 3;\n\nfunction baseUrl(config: PostHogClientConfig): string {\n return config.baseUrl ?? DEFAULT_BASE_URL;\n}\n\nfunction headers(config: PostHogClientConfig): Record<string, string> {\n return { Authorization: `Bearer ${config.apiKey}` };\n}\n\nfunction jsonHeaders(config: PostHogClientConfig): Record<string, string> {\n return {\n ...headers(config),\n \"Content-Type\": \"application/json\",\n };\n}\n\n/**\n * Retry with exponential backoff on 429. PostHog returns `Retry-After` header;\n * we respect it, capped at MAX_RETRIES.\n */\nexport async function fetchWithRetry(\n url: string,\n init: RequestInit,\n retries = MAX_RETRIES,\n): Promise<Response> {\n let attempt = 0;\n while (true) {\n const res = await fetch(url, init);\n if (res.status !== 429 || attempt >= retries) return res;\n attempt++;\n const retryAfter = res.headers.get(\"Retry-After\");\n const waitMs = retryAfter\n ? Math.min(Number(retryAfter) * 1000, 30_000)\n : Math.min(1000 * 2 ** attempt, 30_000);\n await new Promise((r) => setTimeout(r, waitMs));\n }\n}\n\n// Read helpers\n\n/** Fetch every flag in the project, paginating through PostHog's cursor pagination. */\nexport async function fetchAllFlags(config: PostHogClientConfig): Promise<PostHogFlag[]> {\n const flags: PostHogFlag[] = [];\n let url: string | null =\n `${baseUrl(config)}/api/projects/${config.projectId}/feature_flags/?limit=200`;\n while (url) {\n const res = await fetchWithRetry(url, { headers: headers(config) });\n if (!res.ok) {\n throw new Error(\n `PostHog flag fetch failed: ${res.status} ${await res.text().catch(() => \"\")}`,\n );\n }\n const data = (await res.json()) as PostHogFlagList;\n flags.push(...data.results);\n url = data.next;\n }\n return flags;\n}\n\n/**\n * Note: PostHog's `?tags=` filter is substring-based — callers should re-filter\n * for exact tag matches via `hasTag()` to be safe.\n */\nexport async function fetchFlagsByTag(\n config: PostHogClientConfig,\n tag: string,\n): Promise<PostHogFlag[]> {\n const res = await fetchWithRetry(\n `${baseUrl(config)}/api/projects/${config.projectId}/feature_flags/?tags=${encodeURIComponent(tag)}&limit=200`,\n { headers: headers(config) },\n );\n if (!res.ok) {\n throw new Error(\n `PostHog flag fetch by tag failed: ${res.status} ${await res.text().catch(() => \"\")}`,\n );\n }\n const data = (await res.json()) as PostHogFlagList;\n return data.results;\n}\n\nexport async function fetchAllExperiments(\n config: PostHogClientConfig,\n): Promise<PostHogExperiment[]> {\n const res = await fetchWithRetry(\n `${baseUrl(config)}/api/projects/${config.projectId}/experiments/?limit=200`,\n { headers: headers(config) },\n );\n if (!res.ok) return [];\n const data = (await res.json()) as PostHogExperimentList;\n return data.results;\n}\n\n/**\n * Find when a specific tag was added to a flag by scanning PostHog's activity log.\n */\nexport async function findTagAddedAt(\n config: PostHogClientConfig,\n flagId: number,\n tag: string,\n): Promise<Date | null> {\n const res = await fetchWithRetry(\n `${baseUrl(config)}/api/projects/${config.projectId}/feature_flags/${flagId}/activity/?limit=100`,\n { headers: headers(config) },\n );\n if (!res.ok) return null;\n const data = (await res.json()) as ActivityList;\n for (const entry of data.results) {\n const changes = entry.detail?.changes ?? [];\n for (const change of changes) {\n if (change.field !== \"tags\") continue;\n const before = Array.isArray(change.before) ? (change.before as string[]) : [];\n const after = Array.isArray(change.after) ? (change.after as string[]) : [];\n if (!before.includes(tag) && after.includes(tag)) {\n return new Date(entry.created_at);\n }\n }\n }\n return null;\n}\n\n// Write helpers\n\nexport interface FlagPatch {\n name?: string;\n tags?: string[];\n active?: boolean;\n filters?: PostHogFlagFilters;\n}\n\n/** Pass only the fields you want to change. */\nexport async function patchFlag(\n config: PostHogClientConfig,\n flagId: number,\n patch: FlagPatch,\n options?: { dryRun?: boolean },\n): Promise<void> {\n if (options?.dryRun) return;\n const res = await fetchWithRetry(\n `${baseUrl(config)}/api/projects/${config.projectId}/feature_flags/${flagId}/`,\n {\n method: \"PATCH\",\n headers: jsonHeaders(config),\n body: JSON.stringify(patch),\n },\n );\n if (!res.ok) {\n throw new Error(`PostHog flag PATCH failed: ${res.status} ${await res.text().catch(() => \"\")}`);\n }\n}\n\nexport async function patchFlagTags(\n config: PostHogClientConfig,\n flagId: number,\n tags: string[],\n options?: { dryRun?: boolean },\n): Promise<void> {\n return patchFlag(config, flagId, { tags }, options);\n}\n\nexport interface CreateFlagBody {\n key: string;\n /** Goes into PostHog's `name` field (which is actually the description). */\n name: string;\n tags: string[];\n active?: boolean;\n filters?: PostHogFlagFilters;\n}\n\n/**\n * Defaults to `active: false, rollout: 0%` if filters is omitted.\n */\nexport async function createFlag(\n config: PostHogClientConfig,\n body: CreateFlagBody,\n options?: { dryRun?: boolean },\n): Promise<PostHogFlag | null> {\n if (options?.dryRun) return null;\n const payload = {\n key: body.key,\n name: body.name,\n tags: body.tags,\n active: body.active ?? false,\n filters: body.filters ?? {\n groups: [{ properties: [], rollout_percentage: 0 }],\n },\n creation_context: \"feature_flags\",\n };\n const res = await fetchWithRetry(\n `${baseUrl(config)}/api/projects/${config.projectId}/feature_flags/`,\n {\n method: \"POST\",\n headers: jsonHeaders(config),\n body: JSON.stringify(payload),\n },\n );\n if (!res.ok) {\n throw new Error(\n `PostHog flag CREATE failed: ${res.status} ${await res.text().catch(() => \"\")}`,\n );\n }\n return (await res.json()) as PostHogFlag;\n}\n\n// Pure utilities\n\n/**\n * Active, not deleted, and every release condition at 100% rollout. Flags with\n * zero groups are considered NOT released.\n */\nexport function isFullyReleased(flag: PostHogFlag): boolean {\n if (!flag.active || flag.deleted) return false;\n const groups = flag.filters?.groups ?? [];\n if (groups.length === 0) return false;\n return groups.every((g) => (g.rollout_percentage ?? 100) === 100);\n}\n\nexport function hasTag(flag: PostHogFlag, tag: string): boolean {\n return (flag.tags ?? []).includes(tag);\n}\n\nexport function withTagAdded(flag: PostHogFlag, tag: string): string[] {\n const existing = flag.tags ?? [];\n if (existing.includes(tag)) return existing;\n return [...existing, tag];\n}\n","import { fetchWithRetry } from \"./api.js\";\nimport type { PostHogClientConfig } from \"./types.js\";\n\nconst FLAG_KEY_REGEX = /^[a-z0-9_-]+$/;\n\nexport interface CohortMetrics {\n eventCount: number;\n uniqueUsers: number;\n errorRate: number | null;\n publishSuccessRate: number | null;\n}\n\nexport interface EvaluationMetrics {\n treatment: CohortMetrics;\n control: CohortMetrics;\n}\n\n/**\n * Normalize variant strings from PostHog. For boolean flags we get\n * \"true\"/\"false\"; for multivariate flags we get the variant key.\n * For v1 Guardian only boolean flags are supported.\n */\nfunction normalizeVariant(raw: string | null | undefined): \"treatment\" | \"control\" | \"unknown\" {\n if (raw == null) return \"unknown\";\n const s = String(raw).toLowerCase();\n if (s === \"false\" || s === \"0\") return \"control\";\n if (s === \"true\" || s === \"1\") return \"treatment\";\n return \"unknown\";\n}\n\n/**\n * Single HogQL round-trip for per-cohort metrics. Partitions by the\n * `$feature/<flag_key>` property the PostHog SDKs attach to every event.\n *\n * Flag key is validated against a strict regex before interpolation.\n */\nexport async function queryCohortMetrics(params: {\n config: PostHogClientConfig;\n flagKey: string;\n windowStart: Date;\n windowEnd: Date;\n /** Custom publish event name. Defaults to `post/publish.completed`. */\n publishEventName?: string;\n /** Custom publish success property. Defaults to `properties.success`. */\n publishSuccessProp?: string;\n}): Promise<EvaluationMetrics> {\n const { config, flagKey, windowStart, windowEnd } = params;\n const publishEvent = params.publishEventName ?? \"post/publish.completed\";\n const publishProp = params.publishSuccessProp ?? \"properties.success\";\n\n if (!FLAG_KEY_REGEX.test(flagKey)) {\n throw new Error(\n `Invalid flag key for HogQL interpolation: \"${flagKey}\". Must match ${FLAG_KEY_REGEX}`,\n );\n }\n\n const baseUrl = config.baseUrl ?? \"https://us.posthog.com\";\n const variantProp = `properties.$feature/${flagKey}`;\n\n const hogql = `\n SELECT\n toString(${variantProp}) AS variant,\n count() AS total_events,\n count(DISTINCT distinct_id) AS unique_users,\n countIf(event = '$exception') AS error_events,\n countIf(event = '${publishEvent}' AND ${publishProp} = true) AS publish_successes,\n countIf(event = '${publishEvent}') AS publish_total\n FROM events\n WHERE timestamp >= toDateTime('${windowStart.toISOString()}')\n AND timestamp < toDateTime('${windowEnd.toISOString()}')\n AND ${variantProp} IS NOT NULL\n GROUP BY variant\n `;\n\n const res = await fetchWithRetry(`${baseUrl}/api/projects/${config.projectId}/query/`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${config.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n query: { kind: \"HogQLQuery\", query: hogql },\n }),\n });\n if (!res.ok) {\n throw new Error(\n `PostHog HogQL query failed for ${flagKey}: ${res.status} ${await res.text().catch(() => \"\")}`,\n );\n }\n\n const data = (await res.json()) as {\n results: Array<[string, number, number, number, number, number]>;\n };\n\n const zero: CohortMetrics = {\n eventCount: 0,\n uniqueUsers: 0,\n errorRate: null,\n publishSuccessRate: null,\n };\n const metrics: EvaluationMetrics = {\n treatment: { ...zero },\n control: { ...zero },\n };\n\n for (const row of data.results ?? []) {\n const [variantRaw, totalEvents, uniqueUsers, errorEvents, publishSuccesses, publishTotal] = row;\n const cohort: CohortMetrics = {\n eventCount: Number(totalEvents ?? 0),\n uniqueUsers: Number(uniqueUsers ?? 0),\n errorRate: totalEvents > 0 ? Number(errorEvents ?? 0) / Number(totalEvents) : null,\n publishSuccessRate:\n publishTotal > 0 ? Number(publishSuccesses ?? 0) / Number(publishTotal) : null,\n };\n\n const variant = normalizeVariant(variantRaw);\n if (variant === \"treatment\") metrics.treatment = cohort;\n else if (variant === \"control\") metrics.control = cohort;\n }\n\n return metrics;\n}\n","/**\n * Abstraction that decouples orchestration functions from Inngest.\n * Inngest's `step.run()` returns `Promise<Jsonify<T>>` which isn't assignable\n * to `Promise<T>` — so we use `unknown` as the return type to stay compatible.\n */\nexport interface StepRunner {\n // biome-ignore lint/suspicious/noExplicitAny: Inngest step.run() returns Jsonify<T>, not T\n run<T>(id: string, fn: () => Promise<T>): Promise<any>;\n}\n\n/**\n * Simple implementation that just calls `fn()` directly — no durability,\n * suitable for BullMQ, Vercel cron, plain Node, or testing.\n */\nexport class SimpleStepRunner implements StepRunner {\n async run<T>(_id: string, fn: () => Promise<T>): Promise<T> {\n return fn();\n }\n}\n","export interface GuardianThresholds {\n windowMinutes: number;\n minSampleSize: number;\n minUniqueUsers: number;\n errorRateRatioThreshold: number;\n publishSuccessDropThreshold: number;\n cooldownMinutes: number;\n}\n\nexport const DEFAULT_THRESHOLDS: GuardianThresholds = {\n windowMinutes: 20,\n minSampleSize: 100,\n minUniqueUsers: 50,\n errorRateRatioThreshold: 2.0,\n publishSuccessDropThreshold: 0.15,\n cooldownMinutes: 30,\n};\n\nexport function mergeThresholds(overrides?: Partial<GuardianThresholds>): GuardianThresholds {\n return { ...DEFAULT_THRESHOLDS, ...overrides };\n}\n","/**\n * Auto-rollback cron logic. Scans flags tagged `guardian` and compares\n * treatment vs control cohorts. Pure function with StepRunner + callbacks.\n */\n\nimport type { Logger } from \"../logger.js\";\nimport { consoleLogger } from \"../logger.js\";\nimport { fetchFlagsByTag, hasTag, patchFlag } from \"../posthog/api.js\";\nimport type { EvaluationMetrics } from \"../posthog/hogql.js\";\nimport { queryCohortMetrics } from \"../posthog/hogql.js\";\nimport type { PostHogClientConfig, PostHogFlag } from \"../posthog/types.js\";\nimport type { StepRunner } from \"../step-runner.js\";\nimport { SimpleStepRunner } from \"../step-runner.js\";\nimport { type DecisionKind, detectRegression, meetsSampleFloor } from \"./decision.js\";\nimport type { GuardianThresholds } from \"./thresholds.js\";\nimport { mergeThresholds } from \"./thresholds.js\";\n\nconst GUARDIAN_TAG = \"guardian\";\nconst GUARDIAN_ENFORCE_TAG = \"guardian-enforce\";\n\nfunction isInCooldown(flag: PostHogFlag, cooldownMinutes: number): boolean {\n if (!flag.updated_at) return false;\n const updated = new Date(flag.updated_at).getTime();\n return Date.now() - updated < cooldownMinutes * 60 * 1000;\n}\n\nexport interface GuardianEvaluation {\n flag: PostHogFlag;\n metrics: EvaluationMetrics;\n decision: DecisionKind;\n reason: string;\n enforced: boolean;\n}\n\nexport interface GuardianCallbacks {\n onRegression?: (result: GuardianEvaluation) => void | Promise<void>;\n onEvaluated?: (result: GuardianEvaluation) => void | Promise<void>;\n onEnforced?: (flag: PostHogFlag, result: GuardianEvaluation) => void | Promise<void>;\n}\n\nexport interface FlagOutcome {\n flag_key: string;\n decision: DecisionKind;\n reason: string;\n}\n\nexport interface GuardianResult {\n flags_evaluated: number;\n regressions_detected: number;\n auto_disabled: number;\n outcomes: FlagOutcome[];\n}\n\nexport interface GuardianOptions {\n posthog: PostHogClientConfig;\n thresholds?: Partial<GuardianThresholds>;\n /** Custom HogQL config for publish event detection */\n publishEventName?: string;\n publishSuccessProp?: string;\n step?: StepRunner;\n dryRun?: boolean;\n callbacks?: GuardianCallbacks;\n logger?: Logger;\n}\n\nexport function posthogFlagUrl(\n projectId: string,\n flagId: number,\n baseUrl = \"https://us.posthog.com\",\n): string {\n return `${baseUrl}/project/${projectId}/feature_flags/${flagId}`;\n}\n\nexport async function runFlagGuardian(options: GuardianOptions): Promise<GuardianResult> {\n const {\n posthog,\n step = new SimpleStepRunner(),\n dryRun = false,\n callbacks = {},\n logger = consoleLogger,\n } = options;\n const thresholds = mergeThresholds(options.thresholds);\n\n logger.info(\"Flag guardian started\");\n\n const monitoredFlags: PostHogFlag[] = await step.run(\"fetch-guardian-flags\", async () => {\n const all = await fetchFlagsByTag(posthog, GUARDIAN_TAG);\n return all.filter((f) => f.active && !f.deleted && hasTag(f, GUARDIAN_TAG));\n });\n\n const windowEnd = new Date();\n const windowStart = new Date(windowEnd.getTime() - thresholds.windowMinutes * 60 * 1000);\n\n const regressions: GuardianEvaluation[] = [];\n const perFlagOutcomes: FlagOutcome[] = [];\n\n for (const flag of monitoredFlags) {\n const enforceEnabled = hasTag(flag, GUARDIAN_ENFORCE_TAG);\n\n if (isInCooldown(flag, thresholds.cooldownMinutes)) {\n perFlagOutcomes.push({\n flag_key: flag.key,\n decision: \"insufficient_data\",\n reason: \"cooldown\",\n });\n continue;\n }\n\n const metrics: EvaluationMetrics = await step.run(`evaluate-${flag.id}`, async () => {\n return queryCohortMetrics({\n config: posthog,\n flagKey: flag.key,\n windowStart,\n windowEnd,\n publishEventName: options.publishEventName,\n publishSuccessProp: options.publishSuccessProp,\n });\n });\n\n if (!meetsSampleFloor(metrics, thresholds)) {\n perFlagOutcomes.push({\n flag_key: flag.key,\n decision: \"insufficient_data\",\n reason: \"sample_size\",\n });\n continue;\n }\n\n const decision = detectRegression(metrics, thresholds);\n\n if (decision.kind === \"no_regression\") {\n const evaluation: GuardianEvaluation = {\n flag,\n metrics,\n decision: \"no_regression\",\n reason: decision.reason,\n enforced: false,\n };\n await callbacks.onEvaluated?.(evaluation);\n perFlagOutcomes.push({\n flag_key: flag.key,\n decision: \"no_regression\",\n reason: decision.reason,\n });\n continue;\n }\n\n // Regression detected — enforce if tagged, otherwise dry-run.\n let enforced = false;\n if (enforceEnabled && !dryRun) {\n await step.run(`enforce-${flag.id}`, async () => {\n await patchFlag(posthog, flag.id, { active: false }, { dryRun });\n });\n enforced = true;\n }\n\n const finalDecision: DecisionKind = enforced ? \"auto_disabled\" : \"regression_detected\";\n\n const evaluation: GuardianEvaluation = {\n flag,\n metrics,\n decision: finalDecision,\n reason: decision.reason,\n enforced,\n };\n\n await callbacks.onEvaluated?.(evaluation);\n if (decision.kind === \"regression_detected\") {\n await callbacks.onRegression?.(evaluation);\n }\n if (enforced) {\n await callbacks.onEnforced?.(flag, evaluation);\n }\n\n regressions.push(evaluation);\n perFlagOutcomes.push({\n flag_key: flag.key,\n decision: finalDecision,\n reason: decision.reason,\n });\n }\n\n const result: GuardianResult = {\n flags_evaluated: monitoredFlags.length,\n regressions_detected: regressions.length,\n auto_disabled: regressions.filter((r) => r.enforced).length,\n outcomes: perFlagOutcomes,\n };\n logger.info(\"Flag guardian completed\", {\n flags_evaluated: result.flags_evaluated,\n regressions_detected: result.regressions_detected,\n auto_disabled: result.auto_disabled,\n });\n return result;\n}\n","import type { Lifecycle } from \"./types.js\";\n\nexport function buildNamingRegex(areas: readonly string[]): RegExp {\n return new RegExp(`^(release|experiment|ops|tier)_(${areas.join(\"|\")})_[a-z0-9_]+$`);\n}\n\nexport function getLifecycle(key: string): Lifecycle {\n return key.split(\"_\")[0] as Lifecycle;\n}\n\nexport function getArea<A extends string>(key: string): A {\n return key.split(\"_\")[1] as A;\n}\n","import { buildNamingRegex, getArea, getLifecycle } from \"./naming.js\";\nimport type { FlagDefinition, Lifecycle } from \"./types.js\";\n\nexport interface RegistryConfig<A extends string> {\n areas: readonly A[];\n extraTags?: readonly string[];\n}\n\nexport type FeatureFlagKey<A extends string> = `${Lifecycle}_${A}_${string}`;\n\nexport interface Registry<A extends string> {\n /**\n * Define the full registry object. The `satisfies` constraint is applied\n * by the consumer at the call site for compile-time validation.\n */\n NAMING_REGEX: RegExp;\n\n /** Typed accessor for consumer code. */\n flag<K extends string>(name: K, registry: Record<K, FlagDefinition>): string;\n\n /** Get the lifecycle prefix from a flag key. */\n getLifecycle: (key: string) => Lifecycle;\n\n /** Get the area segment from a flag key. */\n getArea: (key: string) => A;\n}\n\n/**\n * Factory that produces a registry bound to an app's specific areas.\n *\n * Consumer usage:\n * ```ts\n * const { NAMING_REGEX, flag, getLifecycle, getArea } = createRegistry({\n * areas: [\"studio\", \"ai\", \"publish\"] as const,\n * });\n * ```\n */\nexport function createRegistry<A extends string>(config: RegistryConfig<A>): Registry<A> {\n const NAMING_REGEX = buildNamingRegex(config.areas);\n\n function flag<K extends string>(name: K, registry: Record<K, FlagDefinition>): string {\n return registry[name].key;\n }\n\n return {\n NAMING_REGEX,\n flag,\n getLifecycle,\n getArea: (key: string) => getArea<A>(key),\n };\n}\n","/**\n * Detects feature flags / experiments hitting 100% rollout and treats that\n * as a \"feature release.\" Pure function with StepRunner + callbacks.\n *\n * Idempotency, timestamps, and stale-flag dedupe all live in PostHog tags\n * (no DB tables): `released-detected-v1` marks first detection,\n * `stale-notified-YYYY-MM` is month-scoped so flags re-nag once per month.\n */\n\nimport type { Logger } from \"../logger.js\";\nimport { consoleLogger } from \"../logger.js\";\nimport {\n fetchAllExperiments,\n fetchAllFlags,\n findTagAddedAt,\n hasTag,\n isFullyReleased,\n patchFlagTags,\n withTagAdded,\n} from \"../posthog/api.js\";\nimport type { PostHogClientConfig, PostHogExperiment, PostHogFlag } from \"../posthog/types.js\";\nimport { getLifecycle } from \"../registry/naming.js\";\nimport type { FlagDefinition } from \"../registry/types.js\";\nimport type { StepRunner } from \"../step-runner.js\";\nimport { SimpleStepRunner } from \"../step-runner.js\";\n\nconst RELEASED_TAG = \"released-detected-v1\";\nconst STALE_THRESHOLD_DAYS = 30;\n\nfunction staleTagForNow(): string {\n const now = new Date();\n const yyyy = now.getUTCFullYear();\n const mm = String(now.getUTCMonth() + 1).padStart(2, \"0\");\n return `stale-notified-${yyyy}-${mm}`;\n}\n\nexport interface NewRelease {\n key: string;\n name: string;\n type: \"flag\" | \"experiment\";\n}\n\nexport interface StaleFlagInfo {\n key: string;\n name: string;\n daysSinceRelease: number;\n}\n\nexport interface OrphanFlag {\n key: string;\n}\n\nexport interface NamingViolation {\n key: string;\n}\n\nexport interface DigestPayload {\n newFlagReleases: NewRelease[];\n newExperimentReleases: NewRelease[];\n staleFlags: StaleFlagInfo[];\n orphans: OrphanFlag[];\n namingViolations: NamingViolation[];\n}\n\nexport interface ReleaseTrackerCallbacks {\n onNewRelease?: (flag: PostHogFlag, type: \"flag\" | \"experiment\") => void | Promise<void>;\n onStale?: (flag: PostHogFlag, daysSinceRelease: number) => void | Promise<void>;\n onOrphan?: (flag: PostHogFlag) => void | Promise<void>;\n onNamingViolation?: (flag: PostHogFlag) => void | Promise<void>;\n onDigestReady?: (digest: DigestPayload) => void | Promise<void>;\n}\n\nexport interface ReleaseTrackerResult {\n flags_checked: number;\n experiments_checked: number;\n new_flag_releases: number;\n new_experiment_releases: number;\n stale_flags_notified: number;\n orphans: number;\n naming_violations: number;\n}\n\nexport interface ReleaseTrackerOptions {\n posthog: PostHogClientConfig;\n registry: Record<string, FlagDefinition>;\n namingRegex: RegExp;\n namingExemptTag?: string;\n staleThresholdDays?: number;\n step?: StepRunner;\n dryRun?: boolean;\n callbacks?: ReleaseTrackerCallbacks;\n logger?: Logger;\n}\n\nexport async function runReleaseTracker(\n options: ReleaseTrackerOptions,\n): Promise<ReleaseTrackerResult> {\n const {\n posthog,\n registry,\n namingRegex,\n namingExemptTag = \"naming-exempt\",\n staleThresholdDays = STALE_THRESHOLD_DAYS,\n step = new SimpleStepRunner(),\n dryRun = false,\n callbacks = {},\n logger = consoleLogger,\n } = options;\n\n logger.info(\"Feature release tracker started\");\n\n const flags: PostHogFlag[] = await step.run(\"fetch-flags\", () => fetchAllFlags(posthog));\n\n const registryKeys = new Set<string>(Object.values(registry).map((d) => d.key));\n const orphans = flags\n .filter((f) => !f.deleted && !registryKeys.has(f.key))\n .map((f) => ({ key: f.key }));\n const namingViolations = flags\n .filter((f) => !f.deleted && !hasTag(f, namingExemptTag) && !namingRegex.test(f.key))\n .map((f) => ({ key: f.key }));\n\n for (const o of orphans) {\n const posthogFlag = flags.find((f) => f.key === o.key);\n if (posthogFlag) await callbacks.onOrphan?.(posthogFlag);\n }\n for (const v of namingViolations) {\n const posthogFlag = flags.find((f) => f.key === v.key);\n if (posthogFlag) await callbacks.onNamingViolation?.(posthogFlag);\n }\n\n const newlyReleased = flags.filter((f) => isFullyReleased(f) && !hasTag(f, RELEASED_TAG));\n\n const newFlagReleases: NewRelease[] = [];\n for (const flag of newlyReleased) {\n await step.run(`tag-released-${flag.id}`, async () => {\n await patchFlagTags(posthog, flag.id, withTagAdded(flag, RELEASED_TAG), {\n dryRun,\n });\n });\n await callbacks.onNewRelease?.(flag, \"flag\");\n newFlagReleases.push({\n key: flag.key,\n name: flag.name ?? flag.key,\n type: \"flag\",\n });\n }\n\n const experiments: PostHogExperiment[] = await step.run(\"fetch-experiments\", () =>\n fetchAllExperiments(posthog),\n );\n\n const newExperimentReleases: NewRelease[] = [];\n const flagsByKey = new Map(flags.map((f) => [f.key, f]));\n const now = Date.now();\n const completedExperiments = experiments.filter((e) => {\n if (e.archived) return false;\n if (!e.end_date) return false;\n return new Date(e.end_date).getTime() <= now;\n });\n\n for (const exp of completedExperiments) {\n const linkedFlag = flagsByKey.get(exp.feature_flag_key);\n if (!linkedFlag) continue;\n if (hasTag(linkedFlag, RELEASED_TAG)) continue;\n await step.run(`tag-experiment-released-${exp.id}`, async () => {\n await patchFlagTags(posthog, linkedFlag.id, withTagAdded(linkedFlag, RELEASED_TAG), {\n dryRun,\n });\n });\n await callbacks.onNewRelease?.(linkedFlag, \"experiment\");\n newExperimentReleases.push({\n key: exp.feature_flag_key,\n name: exp.name,\n type: \"experiment\",\n });\n }\n\n // Stale detection: only `release_*` and `experiment_*` flags\n const STALE_ELIGIBLE_LIFECYCLES = new Set([\"release\", \"experiment\"]);\n const staleTag = staleTagForNow();\n const staleCandidates = flags.filter(\n (f) =>\n hasTag(f, RELEASED_TAG) &&\n !hasTag(f, staleTag) &&\n STALE_ELIGIBLE_LIFECYCLES.has(getLifecycle(f.key)),\n );\n\n const staleFlags: StaleFlagInfo[] = [];\n for (const flag of staleCandidates) {\n const releasedAt: string | null = await step.run(`find-release-time-${flag.id}`, async () => {\n const t = await findTagAddedAt(posthog, flag.id, RELEASED_TAG);\n return t ? t.toISOString() : null;\n });\n if (!releasedAt) continue;\n const days = Math.floor((now - new Date(releasedAt).getTime()) / (24 * 60 * 60 * 1000));\n if (days < staleThresholdDays) continue;\n\n await step.run(`tag-stale-${flag.id}`, async () => {\n await patchFlagTags(posthog, flag.id, withTagAdded(flag, staleTag), {\n dryRun,\n });\n });\n await callbacks.onStale?.(flag, days);\n staleFlags.push({\n key: flag.key,\n name: flag.name ?? flag.key,\n daysSinceRelease: days,\n });\n }\n\n const anyNews =\n newFlagReleases.length +\n newExperimentReleases.length +\n staleFlags.length +\n orphans.length +\n namingViolations.length >\n 0;\n if (anyNews) {\n await callbacks.onDigestReady?.({\n newFlagReleases,\n newExperimentReleases,\n staleFlags,\n orphans,\n namingViolations,\n });\n }\n\n const result: ReleaseTrackerResult = {\n flags_checked: flags.length,\n experiments_checked: experiments.length,\n new_flag_releases: newFlagReleases.length,\n new_experiment_releases: newExperimentReleases.length,\n stale_flags_notified: staleFlags.length,\n orphans: orphans.length,\n naming_violations: namingViolations.length,\n };\n logger.info(\"Feature release tracker completed\", result as unknown as Record<string, unknown>);\n return result;\n}\n","/**\n * One-way sync from a local registry to PostHog. Pure function with\n * StepRunner + callbacks — no framework dependency.\n *\n * Safety principles:\n * - Never deletes flags from PostHog (orphans = human review only)\n * - Never touches active / rollout / conditions (PostHog owns state)\n * - Tag reconciliation is additive only (union, never subtract)\n * - New flags created in safe state (active: false, 0% rollout)\n * - Idempotent — second run is a no-op if registry == PostHog\n */\n\nimport type { Logger } from \"../logger.js\";\nimport { consoleLogger } from \"../logger.js\";\nimport { createFlag, fetchAllFlags, hasTag, patchFlag } from \"../posthog/api.js\";\nimport type { PostHogClientConfig, PostHogFlag, PostHogFlagFilters } from \"../posthog/types.js\";\nimport type { FlagDefinition } from \"../registry/types.js\";\nimport type { StepRunner } from \"../step-runner.js\";\nimport { SimpleStepRunner } from \"../step-runner.js\";\n\n/** Returns the merged array if the union grew, or null for an idempotent skip. */\nfunction unionTags(\n current: readonly string[] | null | undefined,\n wanted: readonly string[],\n): string[] | null {\n const currentSet = new Set(current ?? []);\n const merged = new Set(currentSet);\n for (const t of wanted) merged.add(t);\n if (merged.size === currentSet.size) return null;\n return [...merged];\n}\n\nfunction initialTagsFor(def: FlagDefinition): string[] {\n const tags = new Set<string>(def.tags ?? []);\n if (def.guardian) tags.add(\"guardian\");\n return [...tags];\n}\n\nexport interface FlagSyncCallbacks {\n onCreate?: (def: FlagDefinition) => void | Promise<void>;\n onOrphan?: (flag: PostHogFlag) => void | Promise<void>;\n onNamingViolation?: (flag: PostHogFlag) => void | Promise<void>;\n onReconcile?: (flag: PostHogFlag, patch: Record<string, unknown>) => void | Promise<void>;\n}\n\nexport interface FlagSyncResult {\n registry_size: number;\n posthog_size: number;\n created: number;\n reconciled: number;\n orphans: number;\n naming_violations: number;\n}\n\nexport interface FlagSyncOptions {\n posthog: PostHogClientConfig;\n registry: Record<string, FlagDefinition>;\n namingRegex: RegExp;\n namingExemptTag?: string;\n step?: StepRunner;\n dryRun?: boolean;\n callbacks?: FlagSyncCallbacks;\n logger?: Logger;\n}\n\nexport async function runFlagSync(options: FlagSyncOptions): Promise<FlagSyncResult> {\n const {\n posthog,\n registry,\n namingRegex,\n namingExemptTag = \"naming-exempt\",\n step = new SimpleStepRunner(),\n dryRun = false,\n callbacks = {},\n logger = consoleLogger,\n } = options;\n\n logger.info(\"Feature flag sync started\");\n\n const posthogFlags: PostHogFlag[] = await step.run(\"fetch-posthog-flags\", () =>\n fetchAllFlags(posthog),\n );\n const posthogByKey = new Map(posthogFlags.map((f) => [f.key, f]));\n\n const registryEntries: FlagDefinition[] = Object.values(registry);\n const registryKeys = new Set<string>(registryEntries.map((d) => d.key));\n\n const toCreate = registryEntries.filter((d) => !posthogByKey.has(d.key));\n const toReconcile = registryEntries\n .filter((d) => posthogByKey.has(d.key))\n .flatMap((d) => {\n const existing = posthogByKey.get(d.key);\n if (!existing) return [];\n return [{ def: d, existing }];\n });\n const orphans = posthogFlags.filter((f) => !f.deleted && !registryKeys.has(f.key));\n const namingViolations = posthogFlags.filter(\n (f) => !f.deleted && !hasTag(f, namingExemptTag) && !namingRegex.test(f.key),\n );\n\n const created: string[] = [];\n for (const def of toCreate) {\n await step.run(`create-${def.key}`, async () => {\n const filters: PostHogFlagFilters = {\n groups: [{ properties: [], rollout_percentage: 0 }],\n };\n await createFlag(\n posthog,\n {\n key: def.key,\n name: def.description,\n tags: initialTagsFor(def),\n active: false,\n filters,\n },\n { dryRun },\n );\n });\n created.push(def.key);\n await callbacks.onCreate?.(def);\n }\n\n const reconciled: string[] = [];\n for (const { def, existing } of toReconcile) {\n const patch: Record<string, unknown> = {};\n\n if (existing.name !== def.description) {\n patch.name = def.description;\n }\n\n const wanted: string[] = [...(def.tags ?? [])];\n if (def.guardian) wanted.push(\"guardian\");\n const newTags = unionTags(existing.tags, wanted);\n if (newTags) patch.tags = newTags;\n\n if (Object.keys(patch).length === 0) continue;\n\n await step.run(`reconcile-${def.key}`, async () => {\n await patchFlag(posthog, existing.id, patch as Parameters<typeof patchFlag>[2], { dryRun });\n });\n reconciled.push(def.key);\n await callbacks.onReconcile?.(existing, patch);\n }\n\n for (const orphan of orphans) {\n await callbacks.onOrphan?.(orphan);\n }\n for (const violator of namingViolations) {\n await callbacks.onNamingViolation?.(violator);\n }\n\n const result: FlagSyncResult = {\n registry_size: registryEntries.length,\n posthog_size: posthogFlags.length,\n created: created.length,\n reconciled: reconciled.length,\n orphans: orphans.length,\n naming_violations: namingViolations.length,\n };\n logger.info(\"Feature flag sync completed\", result as unknown as Record<string, unknown>);\n return result;\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal subset of PostHog's FeatureFlag schema — only the fields we use.
|
|
3
|
+
*
|
|
4
|
+
* IMPORTANT: PostHog's FeatureFlag schema has NO separate `description` field.
|
|
5
|
+
* The `name` field IS the description, kept under that name for backwards
|
|
6
|
+
* compatibility. From the OpenAPI spec:
|
|
7
|
+
*
|
|
8
|
+
* name: { type: string, description: "contains the description for the flag
|
|
9
|
+
* (field name `name` is kept for backwards-compatibility)" }
|
|
10
|
+
*/
|
|
11
|
+
interface PostHogClientConfig {
|
|
12
|
+
apiKey: string;
|
|
13
|
+
projectId: string;
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
}
|
|
16
|
+
interface PostHogFlagGroup {
|
|
17
|
+
variant?: string | null;
|
|
18
|
+
properties?: unknown[];
|
|
19
|
+
rollout_percentage?: number | null;
|
|
20
|
+
}
|
|
21
|
+
interface PostHogFlagFilters {
|
|
22
|
+
groups?: PostHogFlagGroup[];
|
|
23
|
+
}
|
|
24
|
+
interface PostHogFlag {
|
|
25
|
+
id: number;
|
|
26
|
+
key: string;
|
|
27
|
+
/** Description of the flag — PostHog calls this `name` for legacy reasons. */
|
|
28
|
+
name: string | null;
|
|
29
|
+
active: boolean;
|
|
30
|
+
deleted: boolean;
|
|
31
|
+
tags: string[] | null;
|
|
32
|
+
filters: PostHogFlagFilters | null;
|
|
33
|
+
created_at: string;
|
|
34
|
+
updated_at?: string;
|
|
35
|
+
}
|
|
36
|
+
interface PostHogFlagList {
|
|
37
|
+
results: PostHogFlag[];
|
|
38
|
+
next: string | null;
|
|
39
|
+
}
|
|
40
|
+
interface PostHogExperiment {
|
|
41
|
+
id: number;
|
|
42
|
+
name: string;
|
|
43
|
+
feature_flag_key: string;
|
|
44
|
+
start_date: string | null;
|
|
45
|
+
end_date: string | null;
|
|
46
|
+
archived: boolean;
|
|
47
|
+
}
|
|
48
|
+
interface PostHogExperimentList {
|
|
49
|
+
results: PostHogExperiment[];
|
|
50
|
+
}
|
|
51
|
+
interface ActivityEntry {
|
|
52
|
+
created_at: string;
|
|
53
|
+
activity: string;
|
|
54
|
+
detail: {
|
|
55
|
+
changes?: Array<{
|
|
56
|
+
field: string;
|
|
57
|
+
after: unknown;
|
|
58
|
+
before: unknown;
|
|
59
|
+
}>;
|
|
60
|
+
} | null;
|
|
61
|
+
}
|
|
62
|
+
interface ActivityList {
|
|
63
|
+
results: ActivityEntry[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface CohortMetrics {
|
|
67
|
+
eventCount: number;
|
|
68
|
+
uniqueUsers: number;
|
|
69
|
+
errorRate: number | null;
|
|
70
|
+
publishSuccessRate: number | null;
|
|
71
|
+
}
|
|
72
|
+
interface EvaluationMetrics {
|
|
73
|
+
treatment: CohortMetrics;
|
|
74
|
+
control: CohortMetrics;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Single HogQL round-trip for per-cohort metrics. Partitions by the
|
|
78
|
+
* `$feature/<flag_key>` property the PostHog SDKs attach to every event.
|
|
79
|
+
*
|
|
80
|
+
* Flag key is validated against a strict regex before interpolation.
|
|
81
|
+
*/
|
|
82
|
+
declare function queryCohortMetrics(params: {
|
|
83
|
+
config: PostHogClientConfig;
|
|
84
|
+
flagKey: string;
|
|
85
|
+
windowStart: Date;
|
|
86
|
+
windowEnd: Date;
|
|
87
|
+
/** Custom publish event name. Defaults to `post/publish.completed`. */
|
|
88
|
+
publishEventName?: string;
|
|
89
|
+
/** Custom publish success property. Defaults to `properties.success`. */
|
|
90
|
+
publishSuccessProp?: string;
|
|
91
|
+
}): Promise<EvaluationMetrics>;
|
|
92
|
+
|
|
93
|
+
interface GuardianThresholds {
|
|
94
|
+
windowMinutes: number;
|
|
95
|
+
minSampleSize: number;
|
|
96
|
+
minUniqueUsers: number;
|
|
97
|
+
errorRateRatioThreshold: number;
|
|
98
|
+
publishSuccessDropThreshold: number;
|
|
99
|
+
cooldownMinutes: number;
|
|
100
|
+
}
|
|
101
|
+
declare const DEFAULT_THRESHOLDS: GuardianThresholds;
|
|
102
|
+
declare function mergeThresholds(overrides?: Partial<GuardianThresholds>): GuardianThresholds;
|
|
103
|
+
|
|
104
|
+
type DecisionKind = "insufficient_data" | "no_regression" | "regression_detected" | "auto_disabled";
|
|
105
|
+
interface Decision {
|
|
106
|
+
kind: DecisionKind;
|
|
107
|
+
reason: string;
|
|
108
|
+
}
|
|
109
|
+
declare function fmtPct(n: number | null | undefined): string;
|
|
110
|
+
declare function detectRegression(metrics: EvaluationMetrics, thresholds: GuardianThresholds): Decision;
|
|
111
|
+
declare function meetsSampleFloor(metrics: EvaluationMetrics, thresholds: GuardianThresholds): boolean;
|
|
112
|
+
|
|
113
|
+
interface Logger {
|
|
114
|
+
info(message: string, data?: Record<string, unknown>): void;
|
|
115
|
+
warn(message: string, data?: Record<string, unknown>): void;
|
|
116
|
+
error(message: string, data?: Record<string, unknown>): void;
|
|
117
|
+
}
|
|
118
|
+
declare const consoleLogger: Logger;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Abstraction that decouples orchestration functions from Inngest.
|
|
122
|
+
* Inngest's `step.run()` returns `Promise<Jsonify<T>>` which isn't assignable
|
|
123
|
+
* to `Promise<T>` — so we use `unknown` as the return type to stay compatible.
|
|
124
|
+
*/
|
|
125
|
+
interface StepRunner {
|
|
126
|
+
run<T>(id: string, fn: () => Promise<T>): Promise<any>;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Simple implementation that just calls `fn()` directly — no durability,
|
|
130
|
+
* suitable for BullMQ, Vercel cron, plain Node, or testing.
|
|
131
|
+
*/
|
|
132
|
+
declare class SimpleStepRunner implements StepRunner {
|
|
133
|
+
run<T>(_id: string, fn: () => Promise<T>): Promise<T>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Auto-rollback cron logic. Scans flags tagged `guardian` and compares
|
|
138
|
+
* treatment vs control cohorts. Pure function with StepRunner + callbacks.
|
|
139
|
+
*/
|
|
140
|
+
|
|
141
|
+
interface GuardianEvaluation {
|
|
142
|
+
flag: PostHogFlag;
|
|
143
|
+
metrics: EvaluationMetrics;
|
|
144
|
+
decision: DecisionKind;
|
|
145
|
+
reason: string;
|
|
146
|
+
enforced: boolean;
|
|
147
|
+
}
|
|
148
|
+
interface GuardianCallbacks {
|
|
149
|
+
onRegression?: (result: GuardianEvaluation) => void | Promise<void>;
|
|
150
|
+
onEvaluated?: (result: GuardianEvaluation) => void | Promise<void>;
|
|
151
|
+
onEnforced?: (flag: PostHogFlag, result: GuardianEvaluation) => void | Promise<void>;
|
|
152
|
+
}
|
|
153
|
+
interface FlagOutcome {
|
|
154
|
+
flag_key: string;
|
|
155
|
+
decision: DecisionKind;
|
|
156
|
+
reason: string;
|
|
157
|
+
}
|
|
158
|
+
interface GuardianResult {
|
|
159
|
+
flags_evaluated: number;
|
|
160
|
+
regressions_detected: number;
|
|
161
|
+
auto_disabled: number;
|
|
162
|
+
outcomes: FlagOutcome[];
|
|
163
|
+
}
|
|
164
|
+
interface GuardianOptions {
|
|
165
|
+
posthog: PostHogClientConfig;
|
|
166
|
+
thresholds?: Partial<GuardianThresholds>;
|
|
167
|
+
/** Custom HogQL config for publish event detection */
|
|
168
|
+
publishEventName?: string;
|
|
169
|
+
publishSuccessProp?: string;
|
|
170
|
+
step?: StepRunner;
|
|
171
|
+
dryRun?: boolean;
|
|
172
|
+
callbacks?: GuardianCallbacks;
|
|
173
|
+
logger?: Logger;
|
|
174
|
+
}
|
|
175
|
+
declare function posthogFlagUrl(projectId: string, flagId: number, baseUrl?: string): string;
|
|
176
|
+
declare function runFlagGuardian(options: GuardianOptions): Promise<GuardianResult>;
|
|
177
|
+
|
|
178
|
+
/** Fetch every flag in the project, paginating through PostHog's cursor pagination. */
|
|
179
|
+
declare function fetchAllFlags(config: PostHogClientConfig): Promise<PostHogFlag[]>;
|
|
180
|
+
/**
|
|
181
|
+
* Note: PostHog's `?tags=` filter is substring-based — callers should re-filter
|
|
182
|
+
* for exact tag matches via `hasTag()` to be safe.
|
|
183
|
+
*/
|
|
184
|
+
declare function fetchFlagsByTag(config: PostHogClientConfig, tag: string): Promise<PostHogFlag[]>;
|
|
185
|
+
declare function fetchAllExperiments(config: PostHogClientConfig): Promise<PostHogExperiment[]>;
|
|
186
|
+
/**
|
|
187
|
+
* Find when a specific tag was added to a flag by scanning PostHog's activity log.
|
|
188
|
+
*/
|
|
189
|
+
declare function findTagAddedAt(config: PostHogClientConfig, flagId: number, tag: string): Promise<Date | null>;
|
|
190
|
+
interface FlagPatch {
|
|
191
|
+
name?: string;
|
|
192
|
+
tags?: string[];
|
|
193
|
+
active?: boolean;
|
|
194
|
+
filters?: PostHogFlagFilters;
|
|
195
|
+
}
|
|
196
|
+
/** Pass only the fields you want to change. */
|
|
197
|
+
declare function patchFlag(config: PostHogClientConfig, flagId: number, patch: FlagPatch, options?: {
|
|
198
|
+
dryRun?: boolean;
|
|
199
|
+
}): Promise<void>;
|
|
200
|
+
declare function patchFlagTags(config: PostHogClientConfig, flagId: number, tags: string[], options?: {
|
|
201
|
+
dryRun?: boolean;
|
|
202
|
+
}): Promise<void>;
|
|
203
|
+
interface CreateFlagBody {
|
|
204
|
+
key: string;
|
|
205
|
+
/** Goes into PostHog's `name` field (which is actually the description). */
|
|
206
|
+
name: string;
|
|
207
|
+
tags: string[];
|
|
208
|
+
active?: boolean;
|
|
209
|
+
filters?: PostHogFlagFilters;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Defaults to `active: false, rollout: 0%` if filters is omitted.
|
|
213
|
+
*/
|
|
214
|
+
declare function createFlag(config: PostHogClientConfig, body: CreateFlagBody, options?: {
|
|
215
|
+
dryRun?: boolean;
|
|
216
|
+
}): Promise<PostHogFlag | null>;
|
|
217
|
+
/**
|
|
218
|
+
* Active, not deleted, and every release condition at 100% rollout. Flags with
|
|
219
|
+
* zero groups are considered NOT released.
|
|
220
|
+
*/
|
|
221
|
+
declare function isFullyReleased(flag: PostHogFlag): boolean;
|
|
222
|
+
declare function hasTag(flag: PostHogFlag, tag: string): boolean;
|
|
223
|
+
declare function withTagAdded(flag: PostHogFlag, tag: string): string[];
|
|
224
|
+
|
|
225
|
+
type Lifecycle = "release" | "experiment" | "ops" | "tier";
|
|
226
|
+
interface FlagDefinition<K extends string = string> {
|
|
227
|
+
key: K;
|
|
228
|
+
description: string;
|
|
229
|
+
/** @Slack handle or email — shown in admin digests + orphan reports. */
|
|
230
|
+
owner: string;
|
|
231
|
+
/** Navigation tags applied additively to PostHog (e.g. "studio", "instagram"). */
|
|
232
|
+
tags?: readonly string[];
|
|
233
|
+
/** Opt into guardian monitoring on creation (adds the `guardian` tag). */
|
|
234
|
+
guardian?: boolean;
|
|
235
|
+
sunsetTarget?: string;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
interface RegistryConfig<A extends string> {
|
|
239
|
+
areas: readonly A[];
|
|
240
|
+
extraTags?: readonly string[];
|
|
241
|
+
}
|
|
242
|
+
type FeatureFlagKey<A extends string> = `${Lifecycle}_${A}_${string}`;
|
|
243
|
+
interface Registry<A extends string> {
|
|
244
|
+
/**
|
|
245
|
+
* Define the full registry object. The `satisfies` constraint is applied
|
|
246
|
+
* by the consumer at the call site for compile-time validation.
|
|
247
|
+
*/
|
|
248
|
+
NAMING_REGEX: RegExp;
|
|
249
|
+
/** Typed accessor for consumer code. */
|
|
250
|
+
flag<K extends string>(name: K, registry: Record<K, FlagDefinition>): string;
|
|
251
|
+
/** Get the lifecycle prefix from a flag key. */
|
|
252
|
+
getLifecycle: (key: string) => Lifecycle;
|
|
253
|
+
/** Get the area segment from a flag key. */
|
|
254
|
+
getArea: (key: string) => A;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Factory that produces a registry bound to an app's specific areas.
|
|
258
|
+
*
|
|
259
|
+
* Consumer usage:
|
|
260
|
+
* ```ts
|
|
261
|
+
* const { NAMING_REGEX, flag, getLifecycle, getArea } = createRegistry({
|
|
262
|
+
* areas: ["studio", "ai", "publish"] as const,
|
|
263
|
+
* });
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
declare function createRegistry<A extends string>(config: RegistryConfig<A>): Registry<A>;
|
|
267
|
+
|
|
268
|
+
declare function buildNamingRegex(areas: readonly string[]): RegExp;
|
|
269
|
+
declare function getLifecycle(key: string): Lifecycle;
|
|
270
|
+
declare function getArea<A extends string>(key: string): A;
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Detects feature flags / experiments hitting 100% rollout and treats that
|
|
274
|
+
* as a "feature release." Pure function with StepRunner + callbacks.
|
|
275
|
+
*
|
|
276
|
+
* Idempotency, timestamps, and stale-flag dedupe all live in PostHog tags
|
|
277
|
+
* (no DB tables): `released-detected-v1` marks first detection,
|
|
278
|
+
* `stale-notified-YYYY-MM` is month-scoped so flags re-nag once per month.
|
|
279
|
+
*/
|
|
280
|
+
|
|
281
|
+
interface NewRelease {
|
|
282
|
+
key: string;
|
|
283
|
+
name: string;
|
|
284
|
+
type: "flag" | "experiment";
|
|
285
|
+
}
|
|
286
|
+
interface StaleFlagInfo {
|
|
287
|
+
key: string;
|
|
288
|
+
name: string;
|
|
289
|
+
daysSinceRelease: number;
|
|
290
|
+
}
|
|
291
|
+
interface OrphanFlag {
|
|
292
|
+
key: string;
|
|
293
|
+
}
|
|
294
|
+
interface NamingViolation {
|
|
295
|
+
key: string;
|
|
296
|
+
}
|
|
297
|
+
interface DigestPayload {
|
|
298
|
+
newFlagReleases: NewRelease[];
|
|
299
|
+
newExperimentReleases: NewRelease[];
|
|
300
|
+
staleFlags: StaleFlagInfo[];
|
|
301
|
+
orphans: OrphanFlag[];
|
|
302
|
+
namingViolations: NamingViolation[];
|
|
303
|
+
}
|
|
304
|
+
interface ReleaseTrackerCallbacks {
|
|
305
|
+
onNewRelease?: (flag: PostHogFlag, type: "flag" | "experiment") => void | Promise<void>;
|
|
306
|
+
onStale?: (flag: PostHogFlag, daysSinceRelease: number) => void | Promise<void>;
|
|
307
|
+
onOrphan?: (flag: PostHogFlag) => void | Promise<void>;
|
|
308
|
+
onNamingViolation?: (flag: PostHogFlag) => void | Promise<void>;
|
|
309
|
+
onDigestReady?: (digest: DigestPayload) => void | Promise<void>;
|
|
310
|
+
}
|
|
311
|
+
interface ReleaseTrackerResult {
|
|
312
|
+
flags_checked: number;
|
|
313
|
+
experiments_checked: number;
|
|
314
|
+
new_flag_releases: number;
|
|
315
|
+
new_experiment_releases: number;
|
|
316
|
+
stale_flags_notified: number;
|
|
317
|
+
orphans: number;
|
|
318
|
+
naming_violations: number;
|
|
319
|
+
}
|
|
320
|
+
interface ReleaseTrackerOptions {
|
|
321
|
+
posthog: PostHogClientConfig;
|
|
322
|
+
registry: Record<string, FlagDefinition>;
|
|
323
|
+
namingRegex: RegExp;
|
|
324
|
+
namingExemptTag?: string;
|
|
325
|
+
staleThresholdDays?: number;
|
|
326
|
+
step?: StepRunner;
|
|
327
|
+
dryRun?: boolean;
|
|
328
|
+
callbacks?: ReleaseTrackerCallbacks;
|
|
329
|
+
logger?: Logger;
|
|
330
|
+
}
|
|
331
|
+
declare function runReleaseTracker(options: ReleaseTrackerOptions): Promise<ReleaseTrackerResult>;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* One-way sync from a local registry to PostHog. Pure function with
|
|
335
|
+
* StepRunner + callbacks — no framework dependency.
|
|
336
|
+
*
|
|
337
|
+
* Safety principles:
|
|
338
|
+
* - Never deletes flags from PostHog (orphans = human review only)
|
|
339
|
+
* - Never touches active / rollout / conditions (PostHog owns state)
|
|
340
|
+
* - Tag reconciliation is additive only (union, never subtract)
|
|
341
|
+
* - New flags created in safe state (active: false, 0% rollout)
|
|
342
|
+
* - Idempotent — second run is a no-op if registry == PostHog
|
|
343
|
+
*/
|
|
344
|
+
|
|
345
|
+
interface FlagSyncCallbacks {
|
|
346
|
+
onCreate?: (def: FlagDefinition) => void | Promise<void>;
|
|
347
|
+
onOrphan?: (flag: PostHogFlag) => void | Promise<void>;
|
|
348
|
+
onNamingViolation?: (flag: PostHogFlag) => void | Promise<void>;
|
|
349
|
+
onReconcile?: (flag: PostHogFlag, patch: Record<string, unknown>) => void | Promise<void>;
|
|
350
|
+
}
|
|
351
|
+
interface FlagSyncResult {
|
|
352
|
+
registry_size: number;
|
|
353
|
+
posthog_size: number;
|
|
354
|
+
created: number;
|
|
355
|
+
reconciled: number;
|
|
356
|
+
orphans: number;
|
|
357
|
+
naming_violations: number;
|
|
358
|
+
}
|
|
359
|
+
interface FlagSyncOptions {
|
|
360
|
+
posthog: PostHogClientConfig;
|
|
361
|
+
registry: Record<string, FlagDefinition>;
|
|
362
|
+
namingRegex: RegExp;
|
|
363
|
+
namingExemptTag?: string;
|
|
364
|
+
step?: StepRunner;
|
|
365
|
+
dryRun?: boolean;
|
|
366
|
+
callbacks?: FlagSyncCallbacks;
|
|
367
|
+
logger?: Logger;
|
|
368
|
+
}
|
|
369
|
+
declare function runFlagSync(options: FlagSyncOptions): Promise<FlagSyncResult>;
|
|
370
|
+
|
|
371
|
+
export { type ActivityEntry, type ActivityList, type CohortMetrics, type CreateFlagBody, DEFAULT_THRESHOLDS, type Decision, type DecisionKind, type DigestPayload, type EvaluationMetrics, type FeatureFlagKey, type FlagDefinition, type FlagOutcome, type FlagPatch, type FlagSyncCallbacks, type FlagSyncOptions, type FlagSyncResult, type GuardianCallbacks, type GuardianEvaluation, type GuardianOptions, type GuardianResult, type GuardianThresholds, type Lifecycle, type Logger, type NamingViolation, type NewRelease, type OrphanFlag, type PostHogClientConfig, type PostHogExperiment, type PostHogExperimentList, type PostHogFlag, type PostHogFlagFilters, type PostHogFlagGroup, type PostHogFlagList, type Registry, type RegistryConfig, type ReleaseTrackerCallbacks, type ReleaseTrackerOptions, type ReleaseTrackerResult, SimpleStepRunner, type StaleFlagInfo, type StepRunner, buildNamingRegex, consoleLogger, createFlag, createRegistry, detectRegression, fetchAllExperiments, fetchAllFlags, fetchFlagsByTag, findTagAddedAt, fmtPct, getArea, getLifecycle, hasTag, isFullyReleased, meetsSampleFloor, mergeThresholds, patchFlag, patchFlagTags, posthogFlagUrl, queryCohortMetrics, runFlagGuardian, runFlagSync, runReleaseTracker, withTagAdded };
|