@contractspec/example.product-intent 0.0.0-canary-20260206014742 → 1.57.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.
@@ -1,28 +1,36 @@
1
1
  $ tsdown
2
2
  ℹ tsdown v0.20.3 powered by rolldown v1.0.0-rc.3
3
3
  ℹ config file: /home/runner/work/contractspec/contractspec/packages/examples/product-intent/tsdown.config.js
4
- ℹ entry: src/example.ts, src/index.ts, src/load-evidence.ts, src/script.ts, src/sync-actions.ts
4
+ ℹ entry: src/example.ts, src/index.ts, src/load-evidence.ts, src/posthog-signals.ts, src/script.ts, src/sync-actions.ts
5
5
  ℹ target: esnext
6
6
  ℹ tsconfig: tsconfig.json
7
7
  ℹ Build start
8
- ℹ Cleaning 16 files
9
- ℹ dist/script.js  6.38 kB │ gzip: 2.23 kB
10
- ℹ dist/sync-actions.js  5.71 kB │ gzip: 2.02 kB
11
- ℹ dist/load-evidence.js  2.31 kB │ gzip: 1.02 kB
12
- ℹ dist/example.js  0.81 kB │ gzip: 0.44 kB
13
- ℹ dist/index.js  0.27 kB │ gzip: 0.15 kB
14
- ℹ dist/script.js.map 11.38 kB │ gzip: 3.65 kB
15
- ℹ dist/sync-actions.js.map 10.31 kB │ gzip: 3.30 kB
16
- ℹ dist/load-evidence.js.map  4.01 kB │ gzip: 1.65 kB
17
- ℹ dist/example.js.map  1.22 kB │ gzip: 0.63 kB
18
- ℹ dist/load-evidence.d.ts.map  0.25 kB │ gzip: 0.18 kB
19
- ℹ dist/example.d.ts.map  0.13 kB │ gzip: 0.13 kB
20
- ℹ dist/load-evidence.d.ts  0.75 kB │ gzip: 0.41 kB
21
- ℹ dist/index.d.ts  0.32 kB │ gzip: 0.17 kB
22
- ℹ dist/example.d.ts  0.25 kB │ gzip: 0.17 kB
23
- ℹ dist/script.d.ts  0.01 kB │ gzip: 0.03 kB
24
- ℹ dist/sync-actions.d.ts  0.01 kB │ gzip: 0.03 kB
25
- ℹ 16 files, total: 44.11 kB
8
+ ℹ Cleaning 28 files
9
+ ℹ dist/posthog-signals.js  6.96 kB │ gzip: 2.20 kB
10
+ ℹ dist/script.js  6.55 kB │ gzip: 2.29 kB
11
+ ℹ dist/sync-actions.js  5.88 kB │ gzip: 2.07 kB
12
+ ℹ dist/load-evidence.js  2.67 kB │ gzip: 1.12 kB
13
+ ℹ dist/example.js  0.81 kB │ gzip: 0.44 kB
14
+ ℹ dist/index.js  0.50 kB │ gzip: 0.21 kB
15
+ ℹ dist/posthog-signals.js.map 14.02 kB │ gzip: 4.15 kB
16
+ ℹ dist/script.js.map 11.64 kB │ gzip: 3.74 kB
17
+ ℹ dist/sync-actions.js.map 10.53 kB │ gzip: 3.37 kB
18
+ ℹ dist/load-evidence.js.map  4.79 kB │ gzip: 1.86 kB
19
+ ℹ dist/libs/analytics/dist/funnel/analyzer.js.map  3.65 kB │ gzip: 1.39 kB
20
+ ℹ dist/libs/analytics/dist/funnel/analyzer.js  2.00 kB │ gzip: 0.84 kB
21
+ ℹ dist/example.js.map  1.22 kB │ gzip: 0.63 kB
22
+ ℹ dist/libs/analytics/dist/types.d.ts.map  0.95 kB │ gzip: 0.48 kB
23
+ ℹ dist/posthog-signals.d.ts.map  0.37 kB │ gzip: 0.23 kB
24
+ ℹ dist/load-evidence.d.ts.map  0.37 kB │ gzip: 0.23 kB
25
+ ℹ dist/example.d.ts.map  0.13 kB │ gzip: 0.13 kB
26
+ ℹ dist/load-evidence.d.ts  1.10 kB │ gzip: 0.49 kB
27
+ ℹ dist/posthog-signals.d.ts  1.00 kB │ gzip: 0.43 kB
28
+ ℹ dist/index.d.ts  0.67 kB │ gzip: 0.24 kB
29
+ ℹ dist/example.d.ts  0.25 kB │ gzip: 0.17 kB
30
+ ℹ dist/script.d.ts  0.01 kB │ gzip: 0.03 kB
31
+ ℹ dist/sync-actions.d.ts  0.01 kB │ gzip: 0.03 kB
32
+ ℹ dist/libs/analytics/dist/types.d.ts  0.50 kB │ gzip: 0.28 kB
33
+ ℹ 24 files, total: 76.58 kB
26
34
  src/load-evidence.ts (1:15) [UNRESOLVED_IMPORT] Warning: Could not resolve 'node:fs' in src/load-evidence.ts
27
35
  ╭─[ src/load-evidence.ts:1:16 ]
28
36
  │
@@ -73,7 +81,6 @@ src/script.ts (2:17) [UNRESOLVED_IMPORT] Warning: Could not resolve 'no
73
81
   │ Help: The "main" field here was ignored. Main fields must be configured explicitly when using the "neutral" platform.
74
82
  ───╯
75
83
 
76
- ✔ Build complete in 12262ms
77
84
  src/script.ts (3:30) [UNRESOLVED_IMPORT] Warning: Could not resolve 'node:url' in src/script.ts
78
85
  ╭─[ src/script.ts:3:31 ]
79
86
  │
@@ -85,7 +92,8 @@ src/script.ts (3:30) [UNRESOLVED_IMPORT] Warning: Could not resolve 'no
85
92
  ───╯
86
93
 
87
94
  [PLUGIN_TIMINGS] Warning: Your build spent significant time in plugins. Here is a breakdown:
88
- - tsdown:external (82%)
89
- - rolldown-plugin-dts:generate (18%)
95
+ - tsdown:external (76%)
96
+ - rolldown-plugin-dts:generate (24%)
90
97
  See https://rolldown.rs/options/checks#plugintimings for more details.
91
98
 
99
+ ✔ Build complete in 21052ms
@@ -3,27 +3,35 @@ $ tsc --noEmit
3
3
  $ tsdown
4
4
  ℹ tsdown v0.20.3 powered by rolldown v1.0.0-rc.3
5
5
  ℹ config file: /home/runner/work/contractspec/contractspec/packages/examples/product-intent/tsdown.config.js
6
- ℹ entry: src/example.ts, src/index.ts, src/load-evidence.ts, src/script.ts, src/sync-actions.ts
6
+ ℹ entry: src/example.ts, src/index.ts, src/load-evidence.ts, src/posthog-signals.ts, src/script.ts, src/sync-actions.ts
7
7
  ℹ target: esnext
8
8
  ℹ tsconfig: tsconfig.json
9
9
  ℹ Build start
10
- ℹ dist/script.js  6.38 kB │ gzip: 2.23 kB
11
- ℹ dist/sync-actions.js  5.71 kB │ gzip: 2.02 kB
12
- ℹ dist/load-evidence.js  2.31 kB │ gzip: 1.02 kB
13
- ℹ dist/example.js  0.81 kB │ gzip: 0.44 kB
14
- ℹ dist/index.js  0.27 kB │ gzip: 0.15 kB
15
- ℹ dist/script.js.map 11.38 kB │ gzip: 3.65 kB
16
- ℹ dist/sync-actions.js.map 10.31 kB │ gzip: 3.30 kB
17
- ℹ dist/load-evidence.js.map  4.01 kB │ gzip: 1.65 kB
18
- ℹ dist/example.js.map  1.22 kB │ gzip: 0.63 kB
19
- ℹ dist/load-evidence.d.ts.map  0.25 kB │ gzip: 0.18 kB
20
- ℹ dist/example.d.ts.map  0.13 kB │ gzip: 0.13 kB
21
- ℹ dist/load-evidence.d.ts  0.75 kB │ gzip: 0.41 kB
22
- ℹ dist/index.d.ts  0.32 kB │ gzip: 0.17 kB
23
- ℹ dist/example.d.ts  0.25 kB │ gzip: 0.17 kB
24
- ℹ dist/script.d.ts  0.01 kB │ gzip: 0.03 kB
25
- ℹ dist/sync-actions.d.ts  0.01 kB │ gzip: 0.03 kB
26
- ℹ 16 files, total: 44.11 kB
10
+ ℹ dist/posthog-signals.js  6.96 kB │ gzip: 2.20 kB
11
+ ℹ dist/script.js  6.55 kB │ gzip: 2.29 kB
12
+ ℹ dist/sync-actions.js  5.88 kB │ gzip: 2.07 kB
13
+ ℹ dist/load-evidence.js  2.67 kB │ gzip: 1.12 kB
14
+ ℹ dist/example.js  0.81 kB │ gzip: 0.44 kB
15
+ ℹ dist/index.js  0.50 kB │ gzip: 0.21 kB
16
+ ℹ dist/posthog-signals.js.map 14.02 kB │ gzip: 4.15 kB
17
+ ℹ dist/script.js.map 11.64 kB │ gzip: 3.74 kB
18
+ ℹ dist/sync-actions.js.map 10.53 kB │ gzip: 3.37 kB
19
+ ℹ dist/load-evidence.js.map  4.79 kB │ gzip: 1.86 kB
20
+ ℹ dist/libs/analytics/dist/funnel/analyzer.js.map  3.65 kB │ gzip: 1.39 kB
21
+ ℹ dist/libs/analytics/dist/funnel/analyzer.js  2.00 kB │ gzip: 0.84 kB
22
+ ℹ dist/example.js.map  1.22 kB │ gzip: 0.63 kB
23
+ ℹ dist/libs/analytics/dist/types.d.ts.map  0.95 kB │ gzip: 0.48 kB
24
+ ℹ dist/posthog-signals.d.ts.map  0.37 kB │ gzip: 0.23 kB
25
+ ℹ dist/load-evidence.d.ts.map  0.37 kB │ gzip: 0.23 kB
26
+ ℹ dist/example.d.ts.map  0.13 kB │ gzip: 0.13 kB
27
+ ℹ dist/load-evidence.d.ts  1.10 kB │ gzip: 0.49 kB
28
+ ℹ dist/posthog-signals.d.ts  1.00 kB │ gzip: 0.43 kB
29
+ ℹ dist/index.d.ts  0.67 kB │ gzip: 0.24 kB
30
+ ℹ dist/example.d.ts  0.25 kB │ gzip: 0.17 kB
31
+ ℹ dist/script.d.ts  0.01 kB │ gzip: 0.03 kB
32
+ ℹ dist/sync-actions.d.ts  0.01 kB │ gzip: 0.03 kB
33
+ ℹ dist/libs/analytics/dist/types.d.ts  0.50 kB │ gzip: 0.28 kB
34
+ ℹ 24 files, total: 76.58 kB
27
35
  src/load-evidence.ts (1:15) [UNRESOLVED_IMPORT] Warning: Could not resolve 'node:fs' in src/load-evidence.ts
28
36
  ╭─[ src/load-evidence.ts:1:16 ]
29
37
  │
@@ -85,8 +93,8 @@ src/script.ts (3:30) [UNRESOLVED_IMPORT] Warning: Could not resolve 'no
85
93
  ───╯
86
94
 
87
95
  [PLUGIN_TIMINGS] Warning: Your build spent significant time in plugins. Here is a breakdown:
88
- - rolldown-plugin-dts:generate (55%)
89
- - tsdown:external (45%)
96
+ ✔ Build complete in 23955ms
97
+ - rolldown-plugin-dts:generate (51%)
98
+ - tsdown:external (48%)
90
99
  See https://rolldown.rs/options/checks#plugintimings for more details.
91
100
 
92
- ✔ Build complete in 9777ms
package/CHANGELOG.md CHANGED
@@ -1,19 +1,23 @@
1
1
  # @contractspec/example.product-intent
2
2
 
3
- ## 0.0.0-canary-20260206014742
3
+ ## 1.57.0
4
4
 
5
5
  ### Minor Changes
6
6
 
7
+ - 8ecf3c1: Add typed PostHog read APIs and readers across contracts, providers, metering, evolution, analytics, observability, and examples.
7
8
  - a119963: Add project-management integrations (Linear, Jira, Notion), sync helpers for product intent outputs, and expose the new integration category across workspace tooling.
8
9
  - 11a5a05: feat: improve product intent
9
10
 
10
11
  ### Patch Changes
11
12
 
12
13
  - 47c48c2: Refine product-intent contracts, add core/module orchestration and bundle wiring, and expand tests/examples.
14
+ - Updated dependencies [8ecf3c1]
13
15
  - Updated dependencies [47c48c2]
14
16
  - Updated dependencies [a119963]
17
+ - Updated dependencies [4651e06]
18
+ - Updated dependencies [ad9d10a]
15
19
  - Updated dependencies [11a5a05]
16
- - @contractspec/lib.contracts@0.0.0-canary-20260206014742
17
- - @contractspec/lib.product-intent-utils@0.0.0-canary-20260206014742
18
- - @contractspec/integration.providers-impls@0.0.0-canary-20260206014742
19
- - @contractspec/lib.ai-agent@0.0.0-canary-20260206014742
20
+ - @contractspec/lib.contracts@1.57.0
21
+ - @contractspec/integration.providers-impls@1.57.0
22
+ - @contractspec/lib.product-intent-utils@1.57.0
23
+ - @contractspec/lib.ai-agent@1.57.0
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import example from "./example.js";
2
- import { DEFAULT_CHUNK_SIZE, DEFAULT_EVIDENCE_ROOT, DEFAULT_TRANSCRIPT_DIRS, EvidenceLoadOptions, loadEvidenceChunks } from "./load-evidence.js";
3
- export { DEFAULT_CHUNK_SIZE, DEFAULT_EVIDENCE_ROOT, DEFAULT_TRANSCRIPT_DIRS, type EvidenceLoadOptions, example, loadEvidenceChunks };
2
+ import { PosthogEvidenceOptions, loadPosthogEvidenceChunks, resolvePosthogEvidenceOptionsFromEnv } from "./posthog-signals.js";
3
+ import { DEFAULT_CHUNK_SIZE, DEFAULT_EVIDENCE_ROOT, DEFAULT_TRANSCRIPT_DIRS, EvidenceLoadOptions, EvidenceLoadWithSignalsOptions, loadEvidenceChunks, loadEvidenceChunksWithSignals } from "./load-evidence.js";
4
+ export { DEFAULT_CHUNK_SIZE, DEFAULT_EVIDENCE_ROOT, DEFAULT_TRANSCRIPT_DIRS, type EvidenceLoadOptions, type EvidenceLoadWithSignalsOptions, type PosthogEvidenceOptions, example, loadEvidenceChunks, loadEvidenceChunksWithSignals, loadPosthogEvidenceChunks, resolvePosthogEvidenceOptionsFromEnv };
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import example from "./example.js";
2
- import { DEFAULT_CHUNK_SIZE, DEFAULT_EVIDENCE_ROOT, DEFAULT_TRANSCRIPT_DIRS, loadEvidenceChunks } from "./load-evidence.js";
2
+ import { loadPosthogEvidenceChunks, resolvePosthogEvidenceOptionsFromEnv } from "./posthog-signals.js";
3
+ import { DEFAULT_CHUNK_SIZE, DEFAULT_EVIDENCE_ROOT, DEFAULT_TRANSCRIPT_DIRS, loadEvidenceChunks, loadEvidenceChunksWithSignals } from "./load-evidence.js";
3
4
 
4
- export { DEFAULT_CHUNK_SIZE, DEFAULT_EVIDENCE_ROOT, DEFAULT_TRANSCRIPT_DIRS, example, loadEvidenceChunks };
5
+ export { DEFAULT_CHUNK_SIZE, DEFAULT_EVIDENCE_ROOT, DEFAULT_TRANSCRIPT_DIRS, example, loadEvidenceChunks, loadEvidenceChunksWithSignals, loadPosthogEvidenceChunks, resolvePosthogEvidenceOptionsFromEnv };
@@ -0,0 +1,64 @@
1
+ //#region ../../libs/analytics/dist/funnel/analyzer.js
2
+ var FunnelAnalyzer = class {
3
+ analyze(events, definition) {
4
+ const windowMs = (definition.windowHours ?? 72) * 60 * 60 * 1e3;
5
+ const eventsByUser = groupByUser(events);
6
+ const stepCounts = definition.steps.map(() => 0);
7
+ for (const userEvents of eventsByUser.values()) this.evaluateUser(userEvents, definition.steps, windowMs).forEach((hit, stepIdx) => {
8
+ if (hit) stepCounts[stepIdx] = (stepCounts[stepIdx] ?? 0) + 1;
9
+ });
10
+ const totalUsers = eventsByUser.size;
11
+ return {
12
+ definition,
13
+ totalUsers,
14
+ steps: definition.steps.map((step, index) => {
15
+ const prevCount = index === 0 ? totalUsers : stepCounts[index - 1] || 1;
16
+ const count = stepCounts[index] ?? 0;
17
+ const conversionRate = prevCount === 0 ? 0 : Number((count / prevCount).toFixed(3));
18
+ return {
19
+ step,
20
+ count,
21
+ conversionRate,
22
+ dropOffRate: Number((1 - conversionRate).toFixed(3))
23
+ };
24
+ })
25
+ };
26
+ }
27
+ evaluateUser(events, steps, windowMs) {
28
+ const sorted = [...events].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
29
+ const completion = Array(steps.length).fill(false);
30
+ let cursor = 0;
31
+ let anchorTime;
32
+ for (const event of sorted) {
33
+ const step = steps[cursor];
34
+ if (!step) break;
35
+ if (event.name !== step.eventName) continue;
36
+ if (step.match && !step.match(event)) continue;
37
+ const eventTime = new Date(event.timestamp).getTime();
38
+ if (cursor === 0) {
39
+ anchorTime = eventTime;
40
+ completion[cursor] = true;
41
+ cursor += 1;
42
+ continue;
43
+ }
44
+ if (anchorTime && eventTime - anchorTime <= windowMs) {
45
+ completion[cursor] = true;
46
+ cursor += 1;
47
+ }
48
+ }
49
+ return completion;
50
+ }
51
+ };
52
+ function groupByUser(events) {
53
+ const map = /* @__PURE__ */ new Map();
54
+ for (const event of events) {
55
+ const list = map.get(event.userId) ?? [];
56
+ list.push(event);
57
+ map.set(event.userId, list);
58
+ }
59
+ return map;
60
+ }
61
+
62
+ //#endregion
63
+ export { FunnelAnalyzer };
64
+ //# sourceMappingURL=analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.js","names":[],"sources":["../../../../../../../libs/analytics/dist/funnel/analyzer.js"],"sourcesContent":["//#region src/funnel/analyzer.ts\nvar FunnelAnalyzer = class {\n\tanalyze(events, definition) {\n\t\tconst windowMs = (definition.windowHours ?? 72) * 60 * 60 * 1e3;\n\t\tconst eventsByUser = groupByUser(events);\n\t\tconst stepCounts = definition.steps.map(() => 0);\n\t\tfor (const userEvents of eventsByUser.values()) this.evaluateUser(userEvents, definition.steps, windowMs).forEach((hit, stepIdx) => {\n\t\t\tif (hit) stepCounts[stepIdx] = (stepCounts[stepIdx] ?? 0) + 1;\n\t\t});\n\t\tconst totalUsers = eventsByUser.size;\n\t\treturn {\n\t\t\tdefinition,\n\t\t\ttotalUsers,\n\t\t\tsteps: definition.steps.map((step, index) => {\n\t\t\t\tconst prevCount = index === 0 ? totalUsers : stepCounts[index - 1] || 1;\n\t\t\t\tconst count = stepCounts[index] ?? 0;\n\t\t\t\tconst conversionRate = prevCount === 0 ? 0 : Number((count / prevCount).toFixed(3));\n\t\t\t\treturn {\n\t\t\t\t\tstep,\n\t\t\t\t\tcount,\n\t\t\t\t\tconversionRate,\n\t\t\t\t\tdropOffRate: Number((1 - conversionRate).toFixed(3))\n\t\t\t\t};\n\t\t\t})\n\t\t};\n\t}\n\tevaluateUser(events, steps, windowMs) {\n\t\tconst sorted = [...events].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());\n\t\tconst completion = Array(steps.length).fill(false);\n\t\tlet cursor = 0;\n\t\tlet anchorTime;\n\t\tfor (const event of sorted) {\n\t\t\tconst step = steps[cursor];\n\t\t\tif (!step) break;\n\t\t\tif (event.name !== step.eventName) continue;\n\t\t\tif (step.match && !step.match(event)) continue;\n\t\t\tconst eventTime = new Date(event.timestamp).getTime();\n\t\t\tif (cursor === 0) {\n\t\t\t\tanchorTime = eventTime;\n\t\t\t\tcompletion[cursor] = true;\n\t\t\t\tcursor += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (anchorTime && eventTime - anchorTime <= windowMs) {\n\t\t\t\tcompletion[cursor] = true;\n\t\t\t\tcursor += 1;\n\t\t\t}\n\t\t}\n\t\treturn completion;\n\t}\n};\nfunction groupByUser(events) {\n\tconst map = /* @__PURE__ */ new Map();\n\tfor (const event of events) {\n\t\tconst list = map.get(event.userId) ?? [];\n\t\tlist.push(event);\n\t\tmap.set(event.userId, list);\n\t}\n\treturn map;\n}\n\n//#endregion\nexport { FunnelAnalyzer };\n//# sourceMappingURL=analyzer.js.map"],"mappings":";AACA,IAAI,iBAAiB,MAAM;CAC1B,QAAQ,QAAQ,YAAY;EAC3B,MAAM,YAAY,WAAW,eAAe,MAAM,KAAK,KAAK;EAC5D,MAAM,eAAe,YAAY,OAAO;EACxC,MAAM,aAAa,WAAW,MAAM,UAAU,EAAE;AAChD,OAAK,MAAM,cAAc,aAAa,QAAQ,CAAE,MAAK,aAAa,YAAY,WAAW,OAAO,SAAS,CAAC,SAAS,KAAK,YAAY;AACnI,OAAI,IAAK,YAAW,YAAY,WAAW,YAAY,KAAK;IAC3D;EACF,MAAM,aAAa,aAAa;AAChC,SAAO;GACN;GACA;GACA,OAAO,WAAW,MAAM,KAAK,MAAM,UAAU;IAC5C,MAAM,YAAY,UAAU,IAAI,aAAa,WAAW,QAAQ,MAAM;IACtE,MAAM,QAAQ,WAAW,UAAU;IACnC,MAAM,iBAAiB,cAAc,IAAI,IAAI,QAAQ,QAAQ,WAAW,QAAQ,EAAE,CAAC;AACnF,WAAO;KACN;KACA;KACA;KACA,aAAa,QAAQ,IAAI,gBAAgB,QAAQ,EAAE,CAAC;KACpD;KACA;GACF;;CAEF,aAAa,QAAQ,OAAO,UAAU;EACrC,MAAM,SAAS,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC;EAC5G,MAAM,aAAa,MAAM,MAAM,OAAO,CAAC,KAAK,MAAM;EAClD,IAAI,SAAS;EACb,IAAI;AACJ,OAAK,MAAM,SAAS,QAAQ;GAC3B,MAAM,OAAO,MAAM;AACnB,OAAI,CAAC,KAAM;AACX,OAAI,MAAM,SAAS,KAAK,UAAW;AACnC,OAAI,KAAK,SAAS,CAAC,KAAK,MAAM,MAAM,CAAE;GACtC,MAAM,YAAY,IAAI,KAAK,MAAM,UAAU,CAAC,SAAS;AACrD,OAAI,WAAW,GAAG;AACjB,iBAAa;AACb,eAAW,UAAU;AACrB,cAAU;AACV;;AAED,OAAI,cAAc,YAAY,cAAc,UAAU;AACrD,eAAW,UAAU;AACrB,cAAU;;;AAGZ,SAAO;;;AAGT,SAAS,YAAY,QAAQ;CAC5B,MAAM,sBAAsB,IAAI,KAAK;AACrC,MAAK,MAAM,SAAS,QAAQ;EAC3B,MAAM,OAAO,IAAI,IAAI,MAAM,OAAO,IAAI,EAAE;AACxC,OAAK,KAAK,MAAM;AAChB,MAAI,IAAI,MAAM,QAAQ,KAAK;;AAE5B,QAAO"}
@@ -0,0 +1,22 @@
1
+ //#region ../../libs/analytics/dist/types.d.ts
2
+ //#region src/types.d.ts
3
+ interface AnalyticsEvent {
4
+ name: string;
5
+ userId: string;
6
+ tenantId?: string;
7
+ timestamp: string | Date;
8
+ properties?: Record<string, unknown>;
9
+ }
10
+ interface FunnelStep {
11
+ id: string;
12
+ eventName: string;
13
+ match?: (event: AnalyticsEvent) => boolean;
14
+ }
15
+ interface FunnelDefinition {
16
+ name: string;
17
+ steps: FunnelStep[];
18
+ windowHours?: number;
19
+ }
20
+ //#endregion
21
+ export { FunnelDefinition };
22
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","names":["AnalyticsEvent","Date","Record","name","userId","tenantId","timestamp","properties","FunnelStep","id","eventName","match","event","FunnelDefinition","steps","windowHours","FunnelStepResult","step","count","conversionRate","dropOffRate","FunnelAnalysis","definition","totalUsers","CohortEvent","amount","CohortDefinition","bucket","periods","startDate","CohortStats","cohortKey","users","retention","ltv","CohortAnalysis","cohorts","ChurnSignal","score","drivers","GrowthMetric","current","previous","target","GrowthHypothesis","statement","metric","confidence","impact"],"sources":["../../../../../../libs/analytics/dist/types.d.ts"],"mappings":";;UACUA,cAAAA;EACRG,IAAAA;EACAC,MAAAA;EACAC,QAAAA;EACAC,SAAAA,WAAoBL,IAAAA;EACpBM,UAAAA,GAAaL,MAAAA;AAAAA;AAAAA,UAELM,UAAAA;EACRC,EAAAA;EACAC,SAAAA;EACAC,KAAAA,IAASC,KAAAA,EAAOZ,cAAAA;AAAAA;AAAAA,UAERa,gBAAAA;EACRV,IAAAA;EACAW,KAAAA,EAAON,UAAAA;EACPO,WAAAA;AAAAA"}
@@ -1,3 +1,4 @@
1
+ import { PosthogEvidenceOptions } from "./posthog-signals.js";
1
2
  import { EvidenceChunk } from "@contractspec/lib.contracts/product-intent/types";
2
3
 
3
4
  //#region src/load-evidence.d.ts
@@ -9,11 +10,15 @@ interface EvidenceLoadOptions {
9
10
  transcriptDirs?: string[];
10
11
  chunkSize?: number;
11
12
  }
13
+ interface EvidenceLoadWithSignalsOptions extends EvidenceLoadOptions {
14
+ posthog?: PosthogEvidenceOptions;
15
+ }
12
16
  /**
13
17
  * Load all transcript files under the given directories and return
14
18
  * EvidenceChunk objects ready for prompt formatting.
15
19
  */
16
20
  declare function loadEvidenceChunks(options?: EvidenceLoadOptions): EvidenceChunk[];
21
+ declare function loadEvidenceChunksWithSignals(options?: EvidenceLoadWithSignalsOptions): Promise<EvidenceChunk[]>;
17
22
  //#endregion
18
- export { DEFAULT_CHUNK_SIZE, DEFAULT_EVIDENCE_ROOT, DEFAULT_TRANSCRIPT_DIRS, EvidenceLoadOptions, loadEvidenceChunks };
23
+ export { DEFAULT_CHUNK_SIZE, DEFAULT_EVIDENCE_ROOT, DEFAULT_TRANSCRIPT_DIRS, EvidenceLoadOptions, EvidenceLoadWithSignalsOptions, loadEvidenceChunks, loadEvidenceChunksWithSignals };
19
24
  //# sourceMappingURL=load-evidence.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"load-evidence.d.ts","names":[],"sources":["../src/load-evidence.ts"],"mappings":";;;cAOa,qBAAA;AAAA,cACA,uBAAA;AAAA,cACA,kBAAA;AAAA,UAEI,mBAAA;EACf,YAAA;EACA,cAAA;EACA,SAAA;AAAA;;;;;iBAyCc,kBAAA,CACd,OAAA,GAAS,mBAAA,GACR,aAAA"}
1
+ {"version":3,"file":"load-evidence.d.ts","names":[],"sources":["../src/load-evidence.ts"],"mappings":";;;;cASa,qBAAA;AAAA,cACA,uBAAA;AAAA,cACA,kBAAA;AAAA,UAEI,mBAAA;EACf,YAAA;EACA,cAAA;EACA,SAAA;AAAA;AAAA,UAGe,8BAAA,SAAuC,mBAAA;EACtD,OAAA,GAAU,sBAAA;AAAA;;AATZ;;;iBAkDgB,kBAAA,CACd,OAAA,GAAS,mBAAA,GACR,aAAA;AAAA,iBAuBmB,6BAAA,CACpB,OAAA,GAAS,8BAAA,GACR,OAAA,CAAQ,aAAA"}
@@ -1,3 +1,4 @@
1
+ import { loadPosthogEvidenceChunks } from "./posthog-signals.js";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import { fileURLToPath } from "node:url";
@@ -62,7 +63,13 @@ function loadEvidenceChunks(options = {}) {
62
63
  }
63
64
  return chunks;
64
65
  }
66
+ async function loadEvidenceChunksWithSignals(options = {}) {
67
+ const baseChunks = loadEvidenceChunks(options);
68
+ if (!options.posthog) return baseChunks;
69
+ const posthogChunks = await loadPosthogEvidenceChunks(options.posthog);
70
+ return [...baseChunks, ...posthogChunks];
71
+ }
65
72
 
66
73
  //#endregion
67
- export { DEFAULT_CHUNK_SIZE, DEFAULT_EVIDENCE_ROOT, DEFAULT_TRANSCRIPT_DIRS, loadEvidenceChunks };
74
+ export { DEFAULT_CHUNK_SIZE, DEFAULT_EVIDENCE_ROOT, DEFAULT_TRANSCRIPT_DIRS, loadEvidenceChunks, loadEvidenceChunksWithSignals };
68
75
  //# sourceMappingURL=load-evidence.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"load-evidence.js","names":[],"sources":["../src/load-evidence.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { EvidenceChunk } from '@contractspec/lib.contracts/product-intent/types';\n\nconst MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));\n\nexport const DEFAULT_EVIDENCE_ROOT = path.join(MODULE_DIR, '../evidence');\nexport const DEFAULT_TRANSCRIPT_DIRS = ['interviews', 'tickets', 'public'];\nexport const DEFAULT_CHUNK_SIZE = 800;\n\nexport interface EvidenceLoadOptions {\n evidenceRoot?: string;\n transcriptDirs?: string[];\n chunkSize?: number;\n}\n\n/**\n * Remove YAML front matter from a file. Synthetic interview and ticket\n * files include a YAML header delimited by triple dashes.\n */\nfunction stripYamlFrontMatter(contents: string): string {\n const start = contents.indexOf('---');\n if (start === -1) return contents;\n const end = contents.indexOf('---', start + 3);\n if (end === -1) return contents;\n return contents.slice(end + 3).trimStart();\n}\n\n/**\n * Split a transcript into fixed-size chunks.\n */\nfunction chunkTranscript(\n fileId: string,\n text: string,\n chunkSize: number\n): EvidenceChunk[] {\n const chunks: EvidenceChunk[] = [];\n const clean = text.trim();\n for (let offset = 0, idx = 0; offset < clean.length; idx += 1) {\n const slice = clean.slice(offset, offset + chunkSize);\n chunks.push({\n chunkId: `${fileId}#c_${String(idx).padStart(2, '0')}`,\n text: slice,\n meta: { source: fileId },\n });\n offset += chunkSize;\n }\n return chunks;\n}\n\n/**\n * Load all transcript files under the given directories and return\n * EvidenceChunk objects ready for prompt formatting.\n */\nexport function loadEvidenceChunks(\n options: EvidenceLoadOptions = {}\n): EvidenceChunk[] {\n const evidenceRoot = options.evidenceRoot ?? DEFAULT_EVIDENCE_ROOT;\n const transcriptDirs = options.transcriptDirs ?? DEFAULT_TRANSCRIPT_DIRS;\n const chunkSize = options.chunkSize ?? DEFAULT_CHUNK_SIZE;\n\n const chunks: EvidenceChunk[] = [];\n for (const dir of transcriptDirs) {\n const fullDir = path.join(evidenceRoot, dir);\n if (!fs.existsSync(fullDir)) continue;\n for (const fileName of fs.readdirSync(fullDir)) {\n const ext = path.extname(fileName).toLowerCase();\n if (ext !== '.md') continue;\n const filePath = path.join(fullDir, fileName);\n const raw = fs.readFileSync(filePath, 'utf8');\n const withoutFrontMatter = stripYamlFrontMatter(raw);\n const baseId = path.parse(fileName).name;\n const fileChunks = chunkTranscript(baseId, withoutFrontMatter, chunkSize);\n chunks.push(...fileChunks);\n }\n }\n return chunks;\n}\n"],"mappings":";;;;;AAKA,MAAM,aAAa,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE/D,MAAa,wBAAwB,KAAK,KAAK,YAAY,cAAc;AACzE,MAAa,0BAA0B;CAAC;CAAc;CAAW;CAAS;AAC1E,MAAa,qBAAqB;;;;;AAYlC,SAAS,qBAAqB,UAA0B;CACtD,MAAM,QAAQ,SAAS,QAAQ,MAAM;AACrC,KAAI,UAAU,GAAI,QAAO;CACzB,MAAM,MAAM,SAAS,QAAQ,OAAO,QAAQ,EAAE;AAC9C,KAAI,QAAQ,GAAI,QAAO;AACvB,QAAO,SAAS,MAAM,MAAM,EAAE,CAAC,WAAW;;;;;AAM5C,SAAS,gBACP,QACA,MACA,WACiB;CACjB,MAAM,SAA0B,EAAE;CAClC,MAAM,QAAQ,KAAK,MAAM;AACzB,MAAK,IAAI,SAAS,GAAG,MAAM,GAAG,SAAS,MAAM,QAAQ,OAAO,GAAG;EAC7D,MAAM,QAAQ,MAAM,MAAM,QAAQ,SAAS,UAAU;AACrD,SAAO,KAAK;GACV,SAAS,GAAG,OAAO,KAAK,OAAO,IAAI,CAAC,SAAS,GAAG,IAAI;GACpD,MAAM;GACN,MAAM,EAAE,QAAQ,QAAQ;GACzB,CAAC;AACF,YAAU;;AAEZ,QAAO;;;;;;AAOT,SAAgB,mBACd,UAA+B,EAAE,EAChB;CACjB,MAAM,eAAe,QAAQ,gBAAgB;CAC7C,MAAM,iBAAiB,QAAQ,kBAAkB;CACjD,MAAM,YAAY,QAAQ,aAAa;CAEvC,MAAM,SAA0B,EAAE;AAClC,MAAK,MAAM,OAAO,gBAAgB;EAChC,MAAM,UAAU,KAAK,KAAK,cAAc,IAAI;AAC5C,MAAI,CAAC,GAAG,WAAW,QAAQ,CAAE;AAC7B,OAAK,MAAM,YAAY,GAAG,YAAY,QAAQ,EAAE;AAE9C,OADY,KAAK,QAAQ,SAAS,CAAC,aAAa,KACpC,MAAO;GACnB,MAAM,WAAW,KAAK,KAAK,SAAS,SAAS;GAE7C,MAAM,qBAAqB,qBADf,GAAG,aAAa,UAAU,OAAO,CACO;GACpD,MAAM,SAAS,KAAK,MAAM,SAAS,CAAC;GACpC,MAAM,aAAa,gBAAgB,QAAQ,oBAAoB,UAAU;AACzE,UAAO,KAAK,GAAG,WAAW;;;AAG9B,QAAO"}
1
+ {"version":3,"file":"load-evidence.js","names":[],"sources":["../src/load-evidence.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { EvidenceChunk } from '@contractspec/lib.contracts/product-intent/types';\nimport type { PosthogEvidenceOptions } from './posthog-signals';\nimport { loadPosthogEvidenceChunks } from './posthog-signals';\n\nconst MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));\n\nexport const DEFAULT_EVIDENCE_ROOT = path.join(MODULE_DIR, '../evidence');\nexport const DEFAULT_TRANSCRIPT_DIRS = ['interviews', 'tickets', 'public'];\nexport const DEFAULT_CHUNK_SIZE = 800;\n\nexport interface EvidenceLoadOptions {\n evidenceRoot?: string;\n transcriptDirs?: string[];\n chunkSize?: number;\n}\n\nexport interface EvidenceLoadWithSignalsOptions extends EvidenceLoadOptions {\n posthog?: PosthogEvidenceOptions;\n}\n\n/**\n * Remove YAML front matter from a file. Synthetic interview and ticket\n * files include a YAML header delimited by triple dashes.\n */\nfunction stripYamlFrontMatter(contents: string): string {\n const start = contents.indexOf('---');\n if (start === -1) return contents;\n const end = contents.indexOf('---', start + 3);\n if (end === -1) return contents;\n return contents.slice(end + 3).trimStart();\n}\n\n/**\n * Split a transcript into fixed-size chunks.\n */\nfunction chunkTranscript(\n fileId: string,\n text: string,\n chunkSize: number\n): EvidenceChunk[] {\n const chunks: EvidenceChunk[] = [];\n const clean = text.trim();\n for (let offset = 0, idx = 0; offset < clean.length; idx += 1) {\n const slice = clean.slice(offset, offset + chunkSize);\n chunks.push({\n chunkId: `${fileId}#c_${String(idx).padStart(2, '0')}`,\n text: slice,\n meta: { source: fileId },\n });\n offset += chunkSize;\n }\n return chunks;\n}\n\n/**\n * Load all transcript files under the given directories and return\n * EvidenceChunk objects ready for prompt formatting.\n */\nexport function loadEvidenceChunks(\n options: EvidenceLoadOptions = {}\n): EvidenceChunk[] {\n const evidenceRoot = options.evidenceRoot ?? DEFAULT_EVIDENCE_ROOT;\n const transcriptDirs = options.transcriptDirs ?? DEFAULT_TRANSCRIPT_DIRS;\n const chunkSize = options.chunkSize ?? DEFAULT_CHUNK_SIZE;\n\n const chunks: EvidenceChunk[] = [];\n for (const dir of transcriptDirs) {\n const fullDir = path.join(evidenceRoot, dir);\n if (!fs.existsSync(fullDir)) continue;\n for (const fileName of fs.readdirSync(fullDir)) {\n const ext = path.extname(fileName).toLowerCase();\n if (ext !== '.md') continue;\n const filePath = path.join(fullDir, fileName);\n const raw = fs.readFileSync(filePath, 'utf8');\n const withoutFrontMatter = stripYamlFrontMatter(raw);\n const baseId = path.parse(fileName).name;\n const fileChunks = chunkTranscript(baseId, withoutFrontMatter, chunkSize);\n chunks.push(...fileChunks);\n }\n }\n return chunks;\n}\n\nexport async function loadEvidenceChunksWithSignals(\n options: EvidenceLoadWithSignalsOptions = {}\n): Promise<EvidenceChunk[]> {\n const baseChunks = loadEvidenceChunks(options);\n if (!options.posthog) return baseChunks;\n const posthogChunks = await loadPosthogEvidenceChunks(options.posthog);\n return [...baseChunks, ...posthogChunks];\n}\n"],"mappings":";;;;;;AAOA,MAAM,aAAa,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE/D,MAAa,wBAAwB,KAAK,KAAK,YAAY,cAAc;AACzE,MAAa,0BAA0B;CAAC;CAAc;CAAW;CAAS;AAC1E,MAAa,qBAAqB;;;;;AAgBlC,SAAS,qBAAqB,UAA0B;CACtD,MAAM,QAAQ,SAAS,QAAQ,MAAM;AACrC,KAAI,UAAU,GAAI,QAAO;CACzB,MAAM,MAAM,SAAS,QAAQ,OAAO,QAAQ,EAAE;AAC9C,KAAI,QAAQ,GAAI,QAAO;AACvB,QAAO,SAAS,MAAM,MAAM,EAAE,CAAC,WAAW;;;;;AAM5C,SAAS,gBACP,QACA,MACA,WACiB;CACjB,MAAM,SAA0B,EAAE;CAClC,MAAM,QAAQ,KAAK,MAAM;AACzB,MAAK,IAAI,SAAS,GAAG,MAAM,GAAG,SAAS,MAAM,QAAQ,OAAO,GAAG;EAC7D,MAAM,QAAQ,MAAM,MAAM,QAAQ,SAAS,UAAU;AACrD,SAAO,KAAK;GACV,SAAS,GAAG,OAAO,KAAK,OAAO,IAAI,CAAC,SAAS,GAAG,IAAI;GACpD,MAAM;GACN,MAAM,EAAE,QAAQ,QAAQ;GACzB,CAAC;AACF,YAAU;;AAEZ,QAAO;;;;;;AAOT,SAAgB,mBACd,UAA+B,EAAE,EAChB;CACjB,MAAM,eAAe,QAAQ,gBAAgB;CAC7C,MAAM,iBAAiB,QAAQ,kBAAkB;CACjD,MAAM,YAAY,QAAQ,aAAa;CAEvC,MAAM,SAA0B,EAAE;AAClC,MAAK,MAAM,OAAO,gBAAgB;EAChC,MAAM,UAAU,KAAK,KAAK,cAAc,IAAI;AAC5C,MAAI,CAAC,GAAG,WAAW,QAAQ,CAAE;AAC7B,OAAK,MAAM,YAAY,GAAG,YAAY,QAAQ,EAAE;AAE9C,OADY,KAAK,QAAQ,SAAS,CAAC,aAAa,KACpC,MAAO;GACnB,MAAM,WAAW,KAAK,KAAK,SAAS,SAAS;GAE7C,MAAM,qBAAqB,qBADf,GAAG,aAAa,UAAU,OAAO,CACO;GACpD,MAAM,SAAS,KAAK,MAAM,SAAS,CAAC;GACpC,MAAM,aAAa,gBAAgB,QAAQ,oBAAoB,UAAU;AACzE,UAAO,KAAK,GAAG,WAAW;;;AAG9B,QAAO;;AAGT,eAAsB,8BACpB,UAA0C,EAAE,EAClB;CAC1B,MAAM,aAAa,mBAAmB,QAAQ;AAC9C,KAAI,CAAC,QAAQ,QAAS,QAAO;CAC7B,MAAM,gBAAgB,MAAM,0BAA0B,QAAQ,QAAQ;AACtE,QAAO,CAAC,GAAG,YAAY,GAAG,cAAc"}
@@ -0,0 +1,22 @@
1
+ import { FunnelDefinition } from "./libs/analytics/dist/types.js";
2
+ import { EvidenceChunk } from "@contractspec/lib.contracts/product-intent/types";
3
+ import { AnalyticsReader, DateRangeInput } from "@contractspec/lib.contracts/integrations/providers/analytics";
4
+
5
+ //#region src/posthog-signals.d.ts
6
+ interface PosthogEvidenceOptions {
7
+ reader: AnalyticsReader;
8
+ dateRange?: DateRangeInput;
9
+ eventNames?: string[];
10
+ limit?: number;
11
+ funnel?: FunnelDefinition;
12
+ includeFeatureFlags?: boolean;
13
+ }
14
+ interface PosthogEvidenceEnvOptions {
15
+ defaultLookbackDays?: number;
16
+ defaultLimit?: number;
17
+ }
18
+ declare function loadPosthogEvidenceChunks(options: PosthogEvidenceOptions): Promise<EvidenceChunk[]>;
19
+ declare function resolvePosthogEvidenceOptionsFromEnv(options?: PosthogEvidenceEnvOptions): PosthogEvidenceOptions | null;
20
+ //#endregion
21
+ export { PosthogEvidenceEnvOptions, PosthogEvidenceOptions, loadPosthogEvidenceChunks, resolvePosthogEvidenceOptionsFromEnv };
22
+ //# sourceMappingURL=posthog-signals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"posthog-signals.d.ts","names":[],"sources":["../src/posthog-signals.ts"],"mappings":";;;;;UAaiB,sBAAA;EACf,MAAA,EAAQ,eAAA;EACR,SAAA,GAAY,cAAA;EACZ,UAAA;EACA,KAAA;EACA,MAAA,GAAS,gBAAA;EACT,mBAAA;AAAA;AAAA,UAGe,yBAAA;EACf,mBAAA;EACA,YAAA;AAAA;AAAA,iBAGoB,yBAAA,CACpB,OAAA,EAAS,sBAAA,GACR,OAAA,CAAQ,aAAA;AAAA,iBAwNK,oCAAA,CACd,OAAA,GAAS,yBAAA,GACR,sBAAA"}
@@ -0,0 +1,205 @@
1
+ import { FunnelAnalyzer } from "./libs/analytics/dist/funnel/analyzer.js";
2
+ import { PosthogAnalyticsProvider } from "@contractspec/integration.providers-impls/impls/posthog";
3
+
4
+ //#region src/posthog-signals.ts
5
+ async function loadPosthogEvidenceChunks(options) {
6
+ const chunks = [];
7
+ const range = resolveRange(options.dateRange);
8
+ const eventSummary = await loadEventSummary(options, range);
9
+ if (eventSummary) chunks.push(eventSummary);
10
+ const funnelEvidence = await loadFunnelEvidence(options, range);
11
+ if (funnelEvidence) chunks.push(funnelEvidence);
12
+ const featureFlags = await loadFeatureFlagEvidence(options);
13
+ if (featureFlags) chunks.push(featureFlags);
14
+ return chunks;
15
+ }
16
+ async function loadEventSummary(options, range) {
17
+ if (!options.reader.queryHogQL) return null;
18
+ const eventFilter = buildEventFilter(options.eventNames);
19
+ const limit = options.limit ?? 10;
20
+ const rows = mapRows(await options.reader.queryHogQL({
21
+ query: [
22
+ "select",
23
+ " event as eventName,",
24
+ " count() as total",
25
+ "from events",
26
+ "where timestamp >= {dateFrom} and timestamp < {dateTo}",
27
+ eventFilter.clause ? `and ${eventFilter.clause}` : "",
28
+ "group by eventName",
29
+ "order by total desc",
30
+ `limit ${limit}`
31
+ ].filter(Boolean).join("\n"),
32
+ values: {
33
+ dateFrom: range.from.toISOString(),
34
+ dateTo: range.to.toISOString(),
35
+ ...eventFilter.values
36
+ }
37
+ }));
38
+ if (rows.length === 0) return null;
39
+ const lines = rows.map((row) => {
40
+ return `- ${asString(row.eventName) ?? "unknown"}: ${asNumber(row.total)}`;
41
+ });
42
+ return {
43
+ chunkId: `posthog:event_summary:${range.from.toISOString()}`,
44
+ text: [`PostHog event summary (${range.from.toISOString()} → ${range.to.toISOString()}):`, ...lines].join("\n"),
45
+ meta: {
46
+ source: "posthog",
47
+ kind: "event_summary",
48
+ dateFrom: range.from.toISOString(),
49
+ dateTo: range.to.toISOString()
50
+ }
51
+ };
52
+ }
53
+ async function loadFunnelEvidence(options, range) {
54
+ if (!options.funnel) return null;
55
+ if (!options.reader.getEvents) return null;
56
+ const events = [];
57
+ const eventNames = options.funnel.steps.map((step) => step.eventName);
58
+ for (const eventName of eventNames) (await options.reader.getEvents({
59
+ event: eventName,
60
+ dateRange: {
61
+ from: range.from,
62
+ to: range.to
63
+ },
64
+ limit: options.limit ?? 500
65
+ })).results.forEach((event) => {
66
+ events.push({
67
+ name: event.event,
68
+ userId: event.distinctId,
69
+ tenantId: typeof event.properties?.tenantId === "string" ? event.properties.tenantId : void 0,
70
+ timestamp: event.timestamp,
71
+ properties: event.properties
72
+ });
73
+ });
74
+ if (events.length === 0) return null;
75
+ const lines = new FunnelAnalyzer().analyze(events, options.funnel).steps.map((step) => {
76
+ return `- ${step.step.eventName}: ${step.count} (conversion ${step.conversionRate}, drop-off ${step.dropOffRate})`;
77
+ });
78
+ return {
79
+ chunkId: `posthog:funnel:${options.funnel.name}`,
80
+ text: [`PostHog funnel analysis — ${options.funnel.name}:`, ...lines].join("\n"),
81
+ meta: {
82
+ source: "posthog",
83
+ kind: "funnel",
84
+ funnelName: options.funnel.name,
85
+ dateFrom: range.from.toISOString(),
86
+ dateTo: range.to.toISOString()
87
+ }
88
+ };
89
+ }
90
+ async function loadFeatureFlagEvidence(options) {
91
+ if (!options.includeFeatureFlags) return null;
92
+ if (!options.reader.getFeatureFlags) return null;
93
+ const response = await options.reader.getFeatureFlags({ limit: 10 });
94
+ if (!response.results.length) return null;
95
+ return {
96
+ chunkId: "posthog:feature_flags",
97
+ text: ["PostHog feature flags:", ...response.results.map((flag) => {
98
+ return `- ${flag.key ?? "unknown"}: ${flag.active ? "active" : "inactive"}`;
99
+ })].join("\n"),
100
+ meta: {
101
+ source: "posthog",
102
+ kind: "feature_flags"
103
+ }
104
+ };
105
+ }
106
+ function resolveRange(dateRange) {
107
+ const now = /* @__PURE__ */ new Date();
108
+ return {
109
+ from: dateRange?.from instanceof Date ? dateRange.from : dateRange?.from ? new Date(dateRange.from) : /* @__PURE__ */ new Date(now.getTime() - 720 * 60 * 60 * 1e3),
110
+ to: dateRange?.to instanceof Date ? dateRange.to : dateRange?.to ? new Date(dateRange.to) : now
111
+ };
112
+ }
113
+ function buildEventFilter(eventNames) {
114
+ if (!eventNames || eventNames.length === 0) return {};
115
+ if (eventNames.length === 1) return {
116
+ clause: "event = {event0}",
117
+ values: { event0: eventNames[0] }
118
+ };
119
+ const values = {};
120
+ return {
121
+ clause: `(${eventNames.map((eventName, index) => {
122
+ values[`event${index}`] = eventName;
123
+ return `event = {event${index}}`;
124
+ }).join(" or ")})`,
125
+ values
126
+ };
127
+ }
128
+ function mapRows(result) {
129
+ if (!Array.isArray(result.results) || !Array.isArray(result.columns)) return [];
130
+ const columns = result.columns;
131
+ return result.results.flatMap((row) => {
132
+ if (!Array.isArray(row)) return [];
133
+ const record = {};
134
+ columns.forEach((column, index) => {
135
+ record[column] = row[index];
136
+ });
137
+ return [record];
138
+ });
139
+ }
140
+ function asString(value) {
141
+ if (typeof value === "string" && value.trim()) return value;
142
+ if (typeof value === "number") return String(value);
143
+ return null;
144
+ }
145
+ function asNumber(value) {
146
+ if (typeof value === "number" && Number.isFinite(value)) return value;
147
+ if (typeof value === "string" && value.trim()) {
148
+ const parsed = Number(value);
149
+ if (Number.isFinite(parsed)) return parsed;
150
+ }
151
+ return 0;
152
+ }
153
+ function resolvePosthogEvidenceOptionsFromEnv(options = {}) {
154
+ const projectId = process.env.POSTHOG_PROJECT_ID;
155
+ const personalApiKey = process.env.POSTHOG_PERSONAL_API_KEY;
156
+ if (!projectId || !personalApiKey) return null;
157
+ const lookbackDays = resolveNumberEnv("POSTHOG_EVIDENCE_LOOKBACK_DAYS", options.defaultLookbackDays ?? 30);
158
+ const limit = resolveNumberEnv("POSTHOG_EVIDENCE_LIMIT", options.defaultLimit ?? 10);
159
+ const now = /* @__PURE__ */ new Date();
160
+ const from = /* @__PURE__ */ new Date(now.getTime() - lookbackDays * 24 * 60 * 60 * 1e3);
161
+ const eventNames = resolveCsvEnv("POSTHOG_EVIDENCE_EVENTS");
162
+ const funnelSteps = resolveCsvEnv("POSTHOG_EVIDENCE_FUNNEL_STEPS");
163
+ const funnel = funnelSteps && funnelSteps.length ? {
164
+ name: "posthog-evidence-funnel",
165
+ steps: funnelSteps.map((eventName, index) => ({
166
+ id: `step_${index + 1}`,
167
+ eventName
168
+ }))
169
+ } : void 0;
170
+ return {
171
+ reader: new PosthogAnalyticsProvider({
172
+ host: process.env.POSTHOG_HOST,
173
+ projectId,
174
+ personalApiKey
175
+ }),
176
+ dateRange: {
177
+ from,
178
+ to: now
179
+ },
180
+ eventNames,
181
+ limit,
182
+ funnel,
183
+ includeFeatureFlags: resolveBooleanEnv("POSTHOG_EVIDENCE_FEATURE_FLAGS", true)
184
+ };
185
+ }
186
+ function resolveCsvEnv(key) {
187
+ const value = process.env[key];
188
+ if (!value) return void 0;
189
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
190
+ }
191
+ function resolveNumberEnv(key, fallback) {
192
+ const value = process.env[key];
193
+ if (!value) return fallback;
194
+ const parsed = Number(value);
195
+ return Number.isFinite(parsed) ? parsed : fallback;
196
+ }
197
+ function resolveBooleanEnv(key, fallback) {
198
+ const value = process.env[key];
199
+ if (value === void 0) return fallback;
200
+ return value.toLowerCase() === "true";
201
+ }
202
+
203
+ //#endregion
204
+ export { loadPosthogEvidenceChunks, resolvePosthogEvidenceOptionsFromEnv };
205
+ //# sourceMappingURL=posthog-signals.js.map