@posthog/convex 1.0.9 → 2.0.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/README.md +88 -36
- package/dist/client/index.d.ts +17 -22
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +26 -41
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +4 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +1 -21
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/_generated/server.d.ts +11 -0
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js +1 -0
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/convex.config.d.ts +18 -1
- package/dist/component/convex.config.d.ts.map +1 -1
- package/dist/component/convex.config.js +21 -1
- package/dist/component/convex.config.js.map +1 -1
- package/dist/component/crons.d.ts +12 -0
- package/dist/component/crons.d.ts.map +1 -0
- package/dist/component/crons.js +42 -0
- package/dist/component/crons.js.map +1 -0
- package/dist/component/lib.d.ts +18 -32
- package/dist/component/lib.d.ts.map +1 -1
- package/dist/component/lib.js +90 -60
- package/dist/component/lib.js.map +1 -1
- package/dist/component/version.d.ts +1 -1
- package/dist/component/version.js +1 -1
- package/package.json +7 -6
- package/src/client/index.test.ts +85 -63
- package/src/client/index.ts +35 -60
- package/src/component/_generated/api.ts +4 -0
- package/src/component/_generated/component.ts +3 -27
- package/src/component/_generated/server.ts +11 -0
- package/src/component/convex.config.ts +21 -1
- package/src/component/crons.test.ts +62 -0
- package/src/component/crons.ts +52 -0
- package/src/component/lib.ts +86 -59
- package/src/component/version.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/server.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAUvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,EAAE,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAgB,CAAC;AAErE;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,YAAY,CAAC,SAAS,EAAE,UAAU,CACxC,CAAC;AAEvB;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,EAAE,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAmB,CAAC;AAE9E;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAAe,CAAC,SAAS,EAAE,UAAU,CAC3C,CAAC;AAE1B;;;;;;;;;;GAUG;AACH,eAAO,MAAM,MAAM,EAAE,aAAa,CAAC,SAAS,EAAE,QAAQ,CAAiB,CAAC;AAExE;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,SAAS,EAAE,UAAU,CACzC,CAAC;AAExB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,EAAE,iBAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/server.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAUvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;GAEG;AACH,KAAK,GAAG,GAAG;IACT,QAAQ,CAAC,sCAAsC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpE,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,wBAAwB,EAAE,MAAM,GAAG,SAAS,CAAC;IACtD,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;CACxC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,EAAE,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAgB,CAAC;AAErE;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,YAAY,CAAC,SAAS,EAAE,UAAU,CACxC,CAAC;AAEvB;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,EAAE,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAmB,CAAC;AAE9E;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAAe,CAAC,SAAS,EAAE,UAAU,CAC3C,CAAC;AAE1B;;;;;;;;;;GAUG;AACH,eAAO,MAAM,MAAM,EAAE,aAAa,CAAC,SAAS,EAAE,QAAQ,CAAiB,CAAC;AAExE;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,SAAS,EAAE,UAAU,CACzC,CAAC;AAExB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,EAAE,iBAAqC,CAAC;AAC/D,eAAO,MAAM,GAAG,EAAE,GAAmC,CAAC;AAEtD;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;AAExD;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;AAE9D;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC"}
|
|
@@ -75,4 +75,5 @@ export const internalAction = internalActionGeneric;
|
|
|
75
75
|
* @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.
|
|
76
76
|
*/
|
|
77
77
|
export const httpAction = httpActionGeneric;
|
|
78
|
+
export const env = process.env;
|
|
78
79
|
//# sourceMappingURL=server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/component/_generated/server.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG;AAaH,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/component/_generated/server.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG;AAaH,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAavB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,KAAK,GAAsC,YAAY,CAAC;AAErE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GACxB,oBAAoB,CAAC;AAEvB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAyC,eAAe,CAAC;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,uBAAuB,CAAC;AAE1B;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,MAAM,GAAuC,aAAa,CAAC;AAExE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GACzB,qBAAqB,CAAC;AAExB;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,UAAU,GAAsB,iBAAiB,CAAC;AAC/D,MAAM,CAAC,MAAM,GAAG,GAAQ,OAAO,CAAC,GAAqB,CAAC"}
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The component declares the env vars it needs so the installing app can wire them in
|
|
3
|
+
* `convex/convex.config.ts` (typically via `app.env.*` so existing project-level env vars
|
|
4
|
+
* pass straight through). All three are read via `process.env` inside the component's
|
|
5
|
+
* actions and cron — `POSTHOG_PERSONAL_API_KEY`'s presence is also what gates the local
|
|
6
|
+
* evaluation refresh cron.
|
|
7
|
+
*/
|
|
8
|
+
declare const _default: import("convex/server").ComponentDefinition<any, {
|
|
9
|
+
readonly POSTHOG_PROJECT_TOKEN: import("convex/values").VString<string, "required">;
|
|
10
|
+
readonly POSTHOG_HOST: import("convex/values").VString<string | undefined, "optional">;
|
|
11
|
+
readonly POSTHOG_PERSONAL_API_KEY: import("convex/values").VString<string | undefined, "optional">;
|
|
12
|
+
/**
|
|
13
|
+
* Polling interval for the local-evaluation refresh cron, in whole seconds. Optional
|
|
14
|
+
* (defaults to 60). Convex component env vars are string-typed on the wire, so this is
|
|
15
|
+
* parsed at module load — invalid values log a warning and fall back to the default.
|
|
16
|
+
*/
|
|
17
|
+
readonly POSTHOG_FLAGS_POLLING_INTERVAL_SECONDS: import("convex/values").VString<string | undefined, "optional">;
|
|
18
|
+
}>;
|
|
2
19
|
export default _default;
|
|
3
20
|
//# sourceMappingURL=convex.config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convex.config.d.ts","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"convex.config.d.ts","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;;;;;IAMC;;;;OAIG;;;AATP,wBAYE"}
|
|
@@ -1,3 +1,23 @@
|
|
|
1
1
|
import { defineComponent } from 'convex/server';
|
|
2
|
-
|
|
2
|
+
import { v } from 'convex/values';
|
|
3
|
+
/**
|
|
4
|
+
* The component declares the env vars it needs so the installing app can wire them in
|
|
5
|
+
* `convex/convex.config.ts` (typically via `app.env.*` so existing project-level env vars
|
|
6
|
+
* pass straight through). All three are read via `process.env` inside the component's
|
|
7
|
+
* actions and cron — `POSTHOG_PERSONAL_API_KEY`'s presence is also what gates the local
|
|
8
|
+
* evaluation refresh cron.
|
|
9
|
+
*/
|
|
10
|
+
export default defineComponent('posthog', {
|
|
11
|
+
env: {
|
|
12
|
+
POSTHOG_PROJECT_TOKEN: v.string(),
|
|
13
|
+
POSTHOG_HOST: v.optional(v.string()),
|
|
14
|
+
POSTHOG_PERSONAL_API_KEY: v.optional(v.string()),
|
|
15
|
+
/**
|
|
16
|
+
* Polling interval for the local-evaluation refresh cron, in whole seconds. Optional
|
|
17
|
+
* (defaults to 60). Convex component env vars are string-typed on the wire, so this is
|
|
18
|
+
* parsed at module load — invalid values log a warning and fall back to the default.
|
|
19
|
+
*/
|
|
20
|
+
POSTHOG_FLAGS_POLLING_INTERVAL_SECONDS: v.optional(v.string()),
|
|
21
|
+
},
|
|
22
|
+
});
|
|
3
23
|
//# sourceMappingURL=convex.config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convex.config.js","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"convex.config.js","sourceRoot":"","sources":["../../src/component/convex.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAA;AAEjC;;;;;;GAMG;AACH,eAAe,eAAe,CAAC,SAAS,EAAE;IACxC,GAAG,EAAE;QACH,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE;QACjC,YAAY,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACpC,wBAAwB,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAChD;;;;WAIG;QACH,sCAAsC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KAC/D;CACF,CAAC,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare const crons: import("convex/server").Crons;
|
|
2
|
+
export declare const DEFAULT_INTERVAL_SECONDS = 60;
|
|
3
|
+
/**
|
|
4
|
+
* Parse the optional `POSTHOG_FLAGS_POLLING_INTERVAL_SECONDS` env var into a positive integer.
|
|
5
|
+
*
|
|
6
|
+
* Convex component env vars are string-typed, so we coerce here. Invalid values fall back to
|
|
7
|
+
* the default rather than failing the deploy — flags will still refresh on the default cadence
|
|
8
|
+
* and the operator gets a warning to act on. Exported for unit testing.
|
|
9
|
+
*/
|
|
10
|
+
export declare function readPollingIntervalSeconds(): number;
|
|
11
|
+
export default crons;
|
|
12
|
+
//# sourceMappingURL=crons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crons.d.ts","sourceRoot":"","sources":["../../src/component/crons.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,KAAK,+BAAa,CAAA;AAExB,eAAO,MAAM,wBAAwB,KAAK,CAAA;AAE1C;;;;;;GAMG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAYnD;AAwBD,eAAe,KAAK,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { cronJobs } from 'convex/server';
|
|
2
|
+
import { api } from './_generated/api.js';
|
|
3
|
+
import { env } from './_generated/server.js';
|
|
4
|
+
const crons = cronJobs();
|
|
5
|
+
export const DEFAULT_INTERVAL_SECONDS = 60;
|
|
6
|
+
/**
|
|
7
|
+
* Parse the optional `POSTHOG_FLAGS_POLLING_INTERVAL_SECONDS` env var into a positive integer.
|
|
8
|
+
*
|
|
9
|
+
* Convex component env vars are string-typed, so we coerce here. Invalid values fall back to
|
|
10
|
+
* the default rather than failing the deploy — flags will still refresh on the default cadence
|
|
11
|
+
* and the operator gets a warning to act on. Exported for unit testing.
|
|
12
|
+
*/
|
|
13
|
+
export function readPollingIntervalSeconds() {
|
|
14
|
+
const raw = (env.POSTHOG_FLAGS_POLLING_INTERVAL_SECONDS ?? '').trim();
|
|
15
|
+
if (!raw)
|
|
16
|
+
return DEFAULT_INTERVAL_SECONDS;
|
|
17
|
+
const parsed = Number(raw);
|
|
18
|
+
if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed <= 0) {
|
|
19
|
+
console.warn(`[PostHog] POSTHOG_FLAGS_POLLING_INTERVAL_SECONDS="${raw}" is not a positive integer; ` +
|
|
20
|
+
`falling back to ${DEFAULT_INTERVAL_SECONDS}s.`);
|
|
21
|
+
return DEFAULT_INTERVAL_SECONDS;
|
|
22
|
+
}
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The refresh cron is registered only when `POSTHOG_PERSONAL_API_KEY` is configured for the
|
|
27
|
+
* component. Without it, local evaluation can't run, so there's no reason to pay the per-tick
|
|
28
|
+
* resource cost — particularly on idle dev deployments on the free tier.
|
|
29
|
+
*
|
|
30
|
+
* Toggling local evaluation on or off therefore requires redeploying the component, which
|
|
31
|
+
* `npx convex env set` triggers automatically in `npx convex dev`. The cron handler itself also
|
|
32
|
+
* guards against a stale registration where the env var was cleared after deploy.
|
|
33
|
+
*/
|
|
34
|
+
// Trim before checking, matching how `readConfig()` in `lib.ts` interprets the env var.
|
|
35
|
+
// `npx convex env set` can leave trailing whitespace; without the trim, a value like `" "` would
|
|
36
|
+
// register the cron but then no-op every tick once `readConfig()` rejects the trimmed-to-empty
|
|
37
|
+
// PAK — wasted function calls, especially painful on free-tier deployments.
|
|
38
|
+
if ((env.POSTHOG_PERSONAL_API_KEY ?? '').trim()) {
|
|
39
|
+
crons.interval('Refresh PostHog feature flag definitions', { seconds: readPollingIntervalSeconds() }, api.lib.refreshFlagDefinitions, {});
|
|
40
|
+
}
|
|
41
|
+
export default crons;
|
|
42
|
+
//# sourceMappingURL=crons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crons.js","sourceRoot":"","sources":["../../src/component/crons.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAA;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,wBAAwB,CAAA;AAE5C,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAA;AAExB,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAA;AAE1C;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B;IACxC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,sCAAsC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IACrE,IAAI,CAAC,GAAG;QAAE,OAAO,wBAAwB,CAAA;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IAC1B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QACzE,OAAO,CAAC,IAAI,CACV,qDAAqD,GAAG,+BAA+B;YACrF,mBAAmB,wBAAwB,IAAI,CAClD,CAAA;QACD,OAAO,wBAAwB,CAAA;IACjC,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,wFAAwF;AACxF,iGAAiG;AACjG,+FAA+F;AAC/F,4EAA4E;AAC5E,IAAI,CAAC,GAAG,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;IAChD,KAAK,CAAC,QAAQ,CACZ,0CAA0C,EAC1C,EAAE,OAAO,EAAE,0BAA0B,EAAE,EAAE,EACzC,GAAG,CAAC,GAAG,CAAC,sBAAsB,EAC9B,EAAE,CACH,CAAA;AACH,CAAC;AAED,eAAe,KAAK,CAAA"}
|
package/dist/component/lib.d.ts
CHANGED
|
@@ -5,41 +5,31 @@ export declare const capture: import("convex/server").RegisteredAction<"public",
|
|
|
5
5
|
sendFeatureFlags?: boolean | undefined;
|
|
6
6
|
timestamp?: number | undefined;
|
|
7
7
|
uuid?: string | undefined;
|
|
8
|
-
apiKey: string;
|
|
9
8
|
distinctId: string;
|
|
10
|
-
host: string;
|
|
11
9
|
event: string;
|
|
12
10
|
}, Promise<void>>;
|
|
13
11
|
export declare const identify: import("convex/server").RegisteredAction<"public", {
|
|
14
12
|
disableGeoip?: boolean | undefined;
|
|
15
13
|
properties?: string | undefined;
|
|
16
|
-
apiKey: string;
|
|
17
14
|
distinctId: string;
|
|
18
|
-
host: string;
|
|
19
15
|
}, Promise<void>>;
|
|
20
16
|
export declare const groupIdentify: import("convex/server").RegisteredAction<"public", {
|
|
21
17
|
disableGeoip?: boolean | undefined;
|
|
22
18
|
distinctId?: string | undefined;
|
|
23
19
|
properties?: string | undefined;
|
|
24
|
-
apiKey: string;
|
|
25
|
-
host: string;
|
|
26
20
|
groupKey: string;
|
|
27
21
|
groupType: string;
|
|
28
22
|
}, Promise<void>>;
|
|
29
23
|
export declare const alias: import("convex/server").RegisteredAction<"public", {
|
|
30
24
|
disableGeoip?: boolean | undefined;
|
|
31
25
|
alias: string;
|
|
32
|
-
apiKey: string;
|
|
33
26
|
distinctId: string;
|
|
34
|
-
host: string;
|
|
35
27
|
}, Promise<void>>;
|
|
36
28
|
export declare const captureException: import("convex/server").RegisteredAction<"public", {
|
|
37
29
|
distinctId?: string | undefined;
|
|
38
30
|
additionalProperties?: string | undefined;
|
|
39
31
|
errorName?: string | undefined;
|
|
40
32
|
errorStack?: string | undefined;
|
|
41
|
-
apiKey: string;
|
|
42
|
-
host: string;
|
|
43
33
|
errorMessage: string;
|
|
44
34
|
}, Promise<void>>;
|
|
45
35
|
export declare const evaluateFlag: import("convex/server").RegisteredAction<"public", {
|
|
@@ -48,9 +38,7 @@ export declare const evaluateFlag: import("convex/server").RegisteredAction<"pub
|
|
|
48
38
|
flagKeys?: string[] | undefined;
|
|
49
39
|
groupProperties?: any;
|
|
50
40
|
personProperties?: any;
|
|
51
|
-
apiKey: string;
|
|
52
41
|
distinctId: string;
|
|
53
|
-
host: string;
|
|
54
42
|
key: string;
|
|
55
43
|
}, Promise<import("@posthog/core").FeatureFlagValue | null>>;
|
|
56
44
|
export declare const evaluateFlagPayload: import("convex/server").RegisteredAction<"public", {
|
|
@@ -59,9 +47,7 @@ export declare const evaluateFlagPayload: import("convex/server").RegisteredActi
|
|
|
59
47
|
flagKeys?: string[] | undefined;
|
|
60
48
|
groupProperties?: any;
|
|
61
49
|
personProperties?: any;
|
|
62
|
-
apiKey: string;
|
|
63
50
|
distinctId: string;
|
|
64
|
-
host: string;
|
|
65
51
|
key: string;
|
|
66
52
|
}, Promise<string | number | boolean | {
|
|
67
53
|
[key: string]: import("@posthog/core").JsonType;
|
|
@@ -72,23 +58,32 @@ export declare const evaluateAllFlags: import("convex/server").RegisteredAction<
|
|
|
72
58
|
flagKeys?: string[] | undefined;
|
|
73
59
|
groupProperties?: any;
|
|
74
60
|
personProperties?: any;
|
|
75
|
-
apiKey: string;
|
|
76
61
|
distinctId: string;
|
|
77
|
-
host: string;
|
|
78
62
|
}, Promise<{
|
|
79
63
|
featureFlags: Record<string, unknown>;
|
|
80
64
|
featureFlagPayloads: Record<string, unknown>;
|
|
81
65
|
}>>;
|
|
82
66
|
/**
|
|
83
|
-
* Returns the
|
|
67
|
+
* Returns the cached flag definitions plus whether local evaluation is configured at all.
|
|
84
68
|
*
|
|
85
|
-
*
|
|
69
|
+
* `localEvalConfigured` reflects whether `POSTHOG_PERSONAL_API_KEY` is set on the component —
|
|
70
|
+
* the client uses this to distinguish "you haven't set up local eval" (throw, point the user
|
|
71
|
+
* at the remote `evaluateFlag` methods) from "PAK is set but the cron hasn't fetched yet"
|
|
72
|
+
* (return `undefined` gracefully). `data` is null until the first successful refresh.
|
|
73
|
+
*
|
|
74
|
+
* `data` is a JSON-stringified `FlagDefinitions` object (see `client/feature-flags/types.ts`).
|
|
86
75
|
*/
|
|
87
76
|
export declare const getFlagDefinitions: import("convex/server").RegisteredQuery<"public", {}, Promise<{
|
|
77
|
+
localEvalConfigured: boolean;
|
|
78
|
+
data: null;
|
|
79
|
+
fetchedAt: null;
|
|
80
|
+
etag: undefined;
|
|
81
|
+
} | {
|
|
82
|
+
localEvalConfigured: boolean;
|
|
88
83
|
data: string;
|
|
89
84
|
fetchedAt: number;
|
|
90
85
|
etag: string | undefined;
|
|
91
|
-
}
|
|
86
|
+
}>>;
|
|
92
87
|
export declare const _setFlagDefinitions: import("convex/server").RegisteredMutation<"internal", {
|
|
93
88
|
etag?: string | undefined;
|
|
94
89
|
data: string;
|
|
@@ -96,20 +91,11 @@ export declare const _setFlagDefinitions: import("convex/server").RegisteredMuta
|
|
|
96
91
|
export declare const _getCurrentEtag: import("convex/server").RegisteredQuery<"internal", {}, Promise<string | undefined>>;
|
|
97
92
|
/**
|
|
98
93
|
* Fetches flag definitions from PostHog's local-evaluation endpoint and stores them in the
|
|
99
|
-
* `flagDefinitions` table. Called by the
|
|
100
|
-
* `
|
|
101
|
-
*
|
|
102
|
-
* Args:
|
|
103
|
-
* - `apiKey` — the project API key (`phc_…`)
|
|
104
|
-
* - `personalApiKey` — a feature flags secure API key (`phs_…`, recommended) or personal API
|
|
105
|
-
* key (`phx_…`) with feature-flag read access; local eval is disabled if missing
|
|
106
|
-
* - `host` — optional, defaults to `https://us.i.posthog.com`
|
|
94
|
+
* `flagDefinitions` table. Called automatically by the cron registered in `crons.ts` when
|
|
95
|
+
* `POSTHOG_PERSONAL_API_KEY` is set, and also exposed publicly so the client's
|
|
96
|
+
* `reloadFeatureFlags(ctx)` method (parity with `posthog-node`) can trigger an on-demand refresh.
|
|
107
97
|
*/
|
|
108
|
-
export declare const refreshFlagDefinitions: import("convex/server").RegisteredAction<"public", {
|
|
109
|
-
host?: string | undefined;
|
|
110
|
-
apiKey: string;
|
|
111
|
-
personalApiKey: string;
|
|
112
|
-
}, Promise<{
|
|
98
|
+
export declare const refreshFlagDefinitions: import("convex/server").RegisteredAction<"public", {}, Promise<{
|
|
113
99
|
status: "skipped";
|
|
114
100
|
reason: "missing-keys";
|
|
115
101
|
} | {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"AAgFA,eAAO,MAAM,OAAO;;;;;;;;;iBA0BlB,CAAA;AAEF,eAAO,MAAM,QAAQ;;;;iBA+BnB,CAAA;AAEF,eAAO,MAAM,aAAa;;;;;;iBA0BxB,CAAA;AAEF,eAAO,MAAM,KAAK;;;;iBAgBhB,CAAA;AAEF,eAAO,MAAM,gBAAgB;;;;;;iBAiB3B,CAAA;AAmCF,eAAO,MAAM,YAAY;;;;;;;;4DAevB,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;;+CAa9B,CAAA;AAEF,eAAO,MAAM,gBAAgB;;;;;;;;;;GAiB3B,CAAA;AASF;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;GAU7B,CAAA;AAOF,eAAO,MAAM,mBAAmB;;;iBAW9B,CAAA;AAEF,eAAO,MAAM,eAAe,sFAM1B,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8JjC,CAAA"}
|
package/dist/component/lib.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PostHog as PostHogEdge } from 'posthog-node/edge';
|
|
2
|
-
import { action, internalMutation, internalQuery, query } from './_generated/server.js';
|
|
2
|
+
import { action, env, internalMutation, internalQuery, query } from './_generated/server.js';
|
|
3
3
|
import { api, internal } from './_generated/api.js';
|
|
4
4
|
import { v } from 'convex/values';
|
|
5
5
|
import { version } from './version.js';
|
|
@@ -16,6 +16,29 @@ class PostHog extends PostHogEdge {
|
|
|
16
16
|
return version;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
const DEFAULT_HOST = 'https://us.i.posthog.com';
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the credentials and host the component was configured with.
|
|
22
|
+
*
|
|
23
|
+
* Reads the typed `env` from `_generated/server` (declared in `convex.config.ts`). The
|
|
24
|
+
* installing app wires the values via `app.use(posthog, { env: { ... } })`, typically
|
|
25
|
+
* threading them straight through from its own deployment env vars. Trimming guards
|
|
26
|
+
* against accidental whitespace from `npx convex env set`.
|
|
27
|
+
*/
|
|
28
|
+
function readConfig() {
|
|
29
|
+
const projectToken = (env.POSTHOG_PROJECT_TOKEN ?? '').trim();
|
|
30
|
+
const host = (env.POSTHOG_HOST ?? '').trim() || DEFAULT_HOST;
|
|
31
|
+
const personalApiKey = (env.POSTHOG_PERSONAL_API_KEY ?? '').trim();
|
|
32
|
+
if (!projectToken) {
|
|
33
|
+
// Convex's typed env-var validation should prevent an empty `POSTHOG_PROJECT_TOKEN` at deploy time,
|
|
34
|
+
// but the gate is enforced at the app's `convex.config.ts`. Log loudly here so anyone hitting
|
|
35
|
+
// an unexpected empty value (e.g. the token was cleared post-deploy on a stale isolate) has a trail
|
|
36
|
+
// to follow rather than silently dropped events.
|
|
37
|
+
console.warn('[PostHog] POSTHOG_PROJECT_TOKEN is not configured; this event will be dropped. ' +
|
|
38
|
+
'Set it with `npx convex env set POSTHOG_PROJECT_TOKEN phc_…` and redeploy.');
|
|
39
|
+
}
|
|
40
|
+
return { projectToken, host, personalApiKey };
|
|
41
|
+
}
|
|
19
42
|
/**
|
|
20
43
|
* Cache PostHog clients across action invocations within the same Convex isolate.
|
|
21
44
|
*
|
|
@@ -23,14 +46,15 @@ class PostHog extends PostHogEdge {
|
|
|
23
46
|
* a fresh client per call (and tearing it down with `shutdown()`) is wasted work — the client
|
|
24
47
|
* carries no per-invocation state once `flush()` has drained its queue.
|
|
25
48
|
*
|
|
26
|
-
* Keyed by `
|
|
49
|
+
* Keyed by `projectToken|host` so a deployment that rotates its env vars (via `npx convex env set`)
|
|
50
|
+
* picks up the new client without restarting the isolate.
|
|
27
51
|
*/
|
|
28
52
|
const clientCache = new Map();
|
|
29
|
-
function getClient(
|
|
30
|
-
const key = `${
|
|
53
|
+
function getClient(projectToken, host) {
|
|
54
|
+
const key = `${projectToken}|${host}`;
|
|
31
55
|
let client = clientCache.get(key);
|
|
32
56
|
if (!client) {
|
|
33
|
-
client = new PostHog(
|
|
57
|
+
client = new PostHog(projectToken, { host, flushAt: 1, flushInterval: 0 });
|
|
34
58
|
clientCache.set(key, client);
|
|
35
59
|
}
|
|
36
60
|
return client;
|
|
@@ -49,8 +73,6 @@ function parseProperties(json) {
|
|
|
49
73
|
}
|
|
50
74
|
export const capture = action({
|
|
51
75
|
args: {
|
|
52
|
-
apiKey: v.string(),
|
|
53
|
-
host: v.string(),
|
|
54
76
|
distinctId: v.string(),
|
|
55
77
|
event: v.string(),
|
|
56
78
|
properties: v.optional(v.string()),
|
|
@@ -61,7 +83,10 @@ export const capture = action({
|
|
|
61
83
|
disableGeoip: v.optional(v.boolean()),
|
|
62
84
|
},
|
|
63
85
|
handler: async (_ctx, args) => {
|
|
64
|
-
const
|
|
86
|
+
const { projectToken, host } = readConfig();
|
|
87
|
+
if (!projectToken)
|
|
88
|
+
return;
|
|
89
|
+
const client = getClient(projectToken, host);
|
|
65
90
|
await client.captureImmediate({
|
|
66
91
|
distinctId: args.distinctId,
|
|
67
92
|
event: args.event,
|
|
@@ -76,14 +101,15 @@ export const capture = action({
|
|
|
76
101
|
});
|
|
77
102
|
export const identify = action({
|
|
78
103
|
args: {
|
|
79
|
-
apiKey: v.string(),
|
|
80
|
-
host: v.string(),
|
|
81
104
|
distinctId: v.string(),
|
|
82
105
|
properties: v.optional(v.string()),
|
|
83
106
|
disableGeoip: v.optional(v.boolean()),
|
|
84
107
|
},
|
|
85
108
|
handler: async (_ctx, args) => {
|
|
86
|
-
const
|
|
109
|
+
const { projectToken, host } = readConfig();
|
|
110
|
+
if (!projectToken)
|
|
111
|
+
return;
|
|
112
|
+
const client = getClient(projectToken, host);
|
|
87
113
|
// posthog-node's `identifyImmediate` is missing an `await` on `identifyStatelessImmediate`
|
|
88
114
|
// (packages/node/src/client.ts:674), so the returned promise resolves before the event hits
|
|
89
115
|
// the wire. We sidestep that by composing the `$identify` event the same way `identifyImmediate`
|
|
@@ -104,8 +130,6 @@ export const identify = action({
|
|
|
104
130
|
});
|
|
105
131
|
export const groupIdentify = action({
|
|
106
132
|
args: {
|
|
107
|
-
apiKey: v.string(),
|
|
108
|
-
host: v.string(),
|
|
109
133
|
groupType: v.string(),
|
|
110
134
|
groupKey: v.string(),
|
|
111
135
|
properties: v.optional(v.string()),
|
|
@@ -113,7 +137,10 @@ export const groupIdentify = action({
|
|
|
113
137
|
disableGeoip: v.optional(v.boolean()),
|
|
114
138
|
},
|
|
115
139
|
handler: async (_ctx, args) => {
|
|
116
|
-
const
|
|
140
|
+
const { projectToken, host } = readConfig();
|
|
141
|
+
if (!projectToken)
|
|
142
|
+
return;
|
|
143
|
+
const client = getClient(projectToken, host);
|
|
117
144
|
// posthog-node doesn't expose a `groupIdentifyImmediate`, so we send the same `$groupidentify`
|
|
118
145
|
// event via `captureImmediate` to keep parity with capture/identify/alias/captureException —
|
|
119
146
|
// resolve when the network call completes, without resorting to shutdown().
|
|
@@ -131,14 +158,15 @@ export const groupIdentify = action({
|
|
|
131
158
|
});
|
|
132
159
|
export const alias = action({
|
|
133
160
|
args: {
|
|
134
|
-
apiKey: v.string(),
|
|
135
|
-
host: v.string(),
|
|
136
161
|
distinctId: v.string(),
|
|
137
162
|
alias: v.string(),
|
|
138
163
|
disableGeoip: v.optional(v.boolean()),
|
|
139
164
|
},
|
|
140
165
|
handler: async (_ctx, args) => {
|
|
141
|
-
const
|
|
166
|
+
const { projectToken, host } = readConfig();
|
|
167
|
+
if (!projectToken)
|
|
168
|
+
return;
|
|
169
|
+
const client = getClient(projectToken, host);
|
|
142
170
|
await client.aliasImmediate({
|
|
143
171
|
distinctId: args.distinctId,
|
|
144
172
|
alias: args.alias,
|
|
@@ -148,8 +176,6 @@ export const alias = action({
|
|
|
148
176
|
});
|
|
149
177
|
export const captureException = action({
|
|
150
178
|
args: {
|
|
151
|
-
apiKey: v.string(),
|
|
152
|
-
host: v.string(),
|
|
153
179
|
distinctId: v.optional(v.string()),
|
|
154
180
|
errorMessage: v.string(),
|
|
155
181
|
errorStack: v.optional(v.string()),
|
|
@@ -157,7 +183,10 @@ export const captureException = action({
|
|
|
157
183
|
additionalProperties: v.optional(v.string()),
|
|
158
184
|
},
|
|
159
185
|
handler: async (_ctx, args) => {
|
|
160
|
-
const
|
|
186
|
+
const { projectToken, host } = readConfig();
|
|
187
|
+
if (!projectToken)
|
|
188
|
+
return;
|
|
189
|
+
const client = getClient(projectToken, host);
|
|
161
190
|
const error = new Error(args.errorMessage);
|
|
162
191
|
if (args.errorName)
|
|
163
192
|
error.name = args.errorName;
|
|
@@ -173,8 +202,6 @@ export const captureException = action({
|
|
|
173
202
|
// continuity flags, static cohorts, properties you don't have in your server context). They
|
|
174
203
|
// require an action context — that's the trade for not needing flag definitions cached upfront.
|
|
175
204
|
const remoteFlagsArgs = {
|
|
176
|
-
apiKey: v.string(),
|
|
177
|
-
host: v.string(),
|
|
178
205
|
distinctId: v.string(),
|
|
179
206
|
groups: v.optional(v.any()),
|
|
180
207
|
personProperties: v.optional(v.any()),
|
|
@@ -195,7 +222,10 @@ function remoteFlagsOptions(args) {
|
|
|
195
222
|
export const evaluateFlag = action({
|
|
196
223
|
args: { ...remoteFlagsArgs, key: v.string() },
|
|
197
224
|
handler: async (_ctx, args) => {
|
|
198
|
-
const
|
|
225
|
+
const { projectToken, host } = readConfig();
|
|
226
|
+
if (!projectToken)
|
|
227
|
+
return null;
|
|
228
|
+
const client = getClient(projectToken, host);
|
|
199
229
|
// Scope the request to just the flag the caller asked about — otherwise PostHog evaluates
|
|
200
230
|
// every flag in the project on every call. Honour an explicit `flagKeys` override when given.
|
|
201
231
|
const snapshot = await client.evaluateFlags(args.distinctId, {
|
|
@@ -209,7 +239,10 @@ export const evaluateFlag = action({
|
|
|
209
239
|
export const evaluateFlagPayload = action({
|
|
210
240
|
args: { ...remoteFlagsArgs, key: v.string() },
|
|
211
241
|
handler: async (_ctx, args) => {
|
|
212
|
-
const
|
|
242
|
+
const { projectToken, host } = readConfig();
|
|
243
|
+
if (!projectToken)
|
|
244
|
+
return null;
|
|
245
|
+
const client = getClient(projectToken, host);
|
|
213
246
|
const snapshot = await client.evaluateFlags(args.distinctId, {
|
|
214
247
|
...remoteFlagsOptions(args),
|
|
215
248
|
flagKeys: args.flagKeys ?? [args.key],
|
|
@@ -221,7 +254,10 @@ export const evaluateFlagPayload = action({
|
|
|
221
254
|
export const evaluateAllFlags = action({
|
|
222
255
|
args: remoteFlagsArgs,
|
|
223
256
|
handler: async (_ctx, args) => {
|
|
224
|
-
const
|
|
257
|
+
const { projectToken, host } = readConfig();
|
|
258
|
+
if (!projectToken)
|
|
259
|
+
return { featureFlags: {}, featureFlagPayloads: {} };
|
|
260
|
+
const client = getClient(projectToken, host);
|
|
225
261
|
const snapshot = await client.evaluateFlags(args.distinctId, remoteFlagsOptions(args));
|
|
226
262
|
const featureFlags = {};
|
|
227
263
|
const featureFlagPayloads = {};
|
|
@@ -238,25 +274,29 @@ export const evaluateAllFlags = action({
|
|
|
238
274
|
});
|
|
239
275
|
// --- Feature flag local evaluation ---
|
|
240
276
|
//
|
|
241
|
-
//
|
|
242
|
-
// `
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
// The action takes credentials as args. The consumer's app schedules the refresh cron and passes
|
|
246
|
-
// them in — typically via `posthog.refreshFlagDefinitions(ctx)` on the client class, which
|
|
247
|
-
// forwards the keys it was constructed with.
|
|
277
|
+
// Flag definitions are fetched on a cron registered inside this component (only when
|
|
278
|
+
// `POSTHOG_PERSONAL_API_KEY` is set — see `crons.ts`) and stored in the `flagDefinitions`
|
|
279
|
+
// table. Clients read them via `getFlagDefinitions` and evaluate flags locally — there is
|
|
280
|
+
// no per-call action for flag evaluation.
|
|
248
281
|
/**
|
|
249
|
-
* Returns the
|
|
282
|
+
* Returns the cached flag definitions plus whether local evaluation is configured at all.
|
|
283
|
+
*
|
|
284
|
+
* `localEvalConfigured` reflects whether `POSTHOG_PERSONAL_API_KEY` is set on the component —
|
|
285
|
+
* the client uses this to distinguish "you haven't set up local eval" (throw, point the user
|
|
286
|
+
* at the remote `evaluateFlag` methods) from "PAK is set but the cron hasn't fetched yet"
|
|
287
|
+
* (return `undefined` gracefully). `data` is null until the first successful refresh.
|
|
250
288
|
*
|
|
251
|
-
*
|
|
289
|
+
* `data` is a JSON-stringified `FlagDefinitions` object (see `client/feature-flags/types.ts`).
|
|
252
290
|
*/
|
|
253
291
|
export const getFlagDefinitions = query({
|
|
254
292
|
args: {},
|
|
255
293
|
handler: async (ctx) => {
|
|
294
|
+
const localEvalConfigured = !!(env.POSTHOG_PERSONAL_API_KEY ?? '').trim();
|
|
256
295
|
const row = await ctx.db.query('flagDefinitions').order('desc').first();
|
|
257
|
-
if (!row)
|
|
258
|
-
return null;
|
|
259
|
-
|
|
296
|
+
if (!row) {
|
|
297
|
+
return { localEvalConfigured, data: null, fetchedAt: null, etag: undefined };
|
|
298
|
+
}
|
|
299
|
+
return { localEvalConfigured, data: row.data, fetchedAt: row.fetchedAt, etag: row.etag };
|
|
260
300
|
},
|
|
261
301
|
});
|
|
262
302
|
// All three queries against `flagDefinitions` use `.order('desc').first()` so they all see the
|
|
@@ -285,32 +325,22 @@ export const _getCurrentEtag = internalQuery({
|
|
|
285
325
|
});
|
|
286
326
|
/**
|
|
287
327
|
* Fetches flag definitions from PostHog's local-evaluation endpoint and stores them in the
|
|
288
|
-
* `flagDefinitions` table. Called by the
|
|
289
|
-
* `
|
|
290
|
-
*
|
|
291
|
-
* Args:
|
|
292
|
-
* - `apiKey` — the project API key (`phc_…`)
|
|
293
|
-
* - `personalApiKey` — a feature flags secure API key (`phs_…`, recommended) or personal API
|
|
294
|
-
* key (`phx_…`) with feature-flag read access; local eval is disabled if missing
|
|
295
|
-
* - `host` — optional, defaults to `https://us.i.posthog.com`
|
|
328
|
+
* `flagDefinitions` table. Called automatically by the cron registered in `crons.ts` when
|
|
329
|
+
* `POSTHOG_PERSONAL_API_KEY` is set, and also exposed publicly so the client's
|
|
330
|
+
* `reloadFeatureFlags(ctx)` method (parity with `posthog-node`) can trigger an on-demand refresh.
|
|
296
331
|
*/
|
|
297
332
|
export const refreshFlagDefinitions = action({
|
|
298
|
-
args: {
|
|
299
|
-
|
|
300
|
-
personalApiKey
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const personalApiKey = args.personalApiKey.trim();
|
|
306
|
-
const host = (args.host?.trim() || '').replace(/\/$/, '') || 'https://us.i.posthog.com';
|
|
307
|
-
if (!projectApiKey || !personalApiKey) {
|
|
308
|
-
// Local evaluation requires both keys. Return a status rather than throwing so the caller
|
|
309
|
-
// (typically a cron) can surface it cleanly.
|
|
333
|
+
args: {},
|
|
334
|
+
handler: async (ctx) => {
|
|
335
|
+
const { projectToken, host, personalApiKey } = readConfig();
|
|
336
|
+
if (!projectToken || !personalApiKey) {
|
|
337
|
+
// The cron is conditionally registered on `POSTHOG_PERSONAL_API_KEY`, so reaching this branch
|
|
338
|
+
// means either env vars were cleared after deploy (cron still scheduled) or the project token wasn't
|
|
339
|
+
// configured. Return a status rather than throwing so the cron doesn't churn on errors.
|
|
310
340
|
return { status: 'skipped', reason: 'missing-keys' };
|
|
311
341
|
}
|
|
312
342
|
const etag = await ctx.runQuery(internal.lib._getCurrentEtag, {});
|
|
313
|
-
const url = `${host}/flags/definitions?token=${
|
|
343
|
+
const url = `${host.replace(/\/$/, '')}/flags/definitions?token=${projectToken}&send_cohorts`;
|
|
314
344
|
const headers = {
|
|
315
345
|
'Content-Type': 'application/json',
|
|
316
346
|
Authorization: `Bearer ${personalApiKey}`,
|
|
@@ -390,7 +420,7 @@ export const refreshFlagDefinitions = action({
|
|
|
390
420
|
if (looksCacheCold) {
|
|
391
421
|
const existing = await ctx.runQuery(api.lib.getFlagDefinitions, {});
|
|
392
422
|
const STALE_AFTER_MS = 5 * 60 * 1000;
|
|
393
|
-
if (existing === null) {
|
|
423
|
+
if (existing.fetchedAt === null) {
|
|
394
424
|
// No prior cache — write an empty snapshot so subsequent reads are deterministic and
|
|
395
425
|
// the UI shows "no flags" instead of "loading".
|
|
396
426
|
await ctx.runMutation(internal.lib._setFlagDefinitions, {
|
|
@@ -414,7 +444,7 @@ export const refreshFlagDefinitions = action({
|
|
|
414
444
|
// Recent cached defs — keep them while PostHog's cache potentially warms back up.
|
|
415
445
|
return { status: 'stale' };
|
|
416
446
|
}
|
|
417
|
-
console.warn(`[PostHog] Unexpected status ${response.status} fetching flag definitions from ${url.replace(
|
|
447
|
+
console.warn(`[PostHog] Unexpected status ${response.status} fetching flag definitions from ${url.replace(projectToken, '<token>')}. ` +
|
|
418
448
|
`Response body: ${bodyText}`);
|
|
419
449
|
return { status: 'error', reason: 'unexpected-status' };
|
|
420
450
|
}
|