@appfunnel-dev/sdk 2.0.0-canary.2 → 2.0.0-canary.3

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.
Files changed (37) hide show
  1. package/dist/{checkout-DiQvRT5q.d.ts → checkout-7Dy6IedP.d.ts} +3 -0
  2. package/dist/{checkout-CZmEvWfC.d.cts → checkout-Dz8cGkB_.d.cts} +3 -0
  3. package/dist/{chunk-7UC5VXOR.js → chunk-AKO6XKXP.js} +28 -8
  4. package/dist/chunk-AKO6XKXP.js.map +1 -0
  5. package/dist/{chunk-VQOD2Z6Q.cjs → chunk-CY4VBSMX.cjs} +5 -3
  6. package/dist/chunk-CY4VBSMX.cjs.map +1 -0
  7. package/dist/{chunk-Z3TWO2PW.cjs → chunk-JSRKA375.cjs} +29 -7
  8. package/dist/chunk-JSRKA375.cjs.map +1 -0
  9. package/dist/{chunk-UIR6TGEW.js → chunk-M6U3FNRW.js} +5 -3
  10. package/dist/chunk-M6U3FNRW.js.map +1 -0
  11. package/dist/driver-paddle.cjs +14 -14
  12. package/dist/driver-paddle.d.cts +1 -1
  13. package/dist/driver-paddle.d.ts +1 -1
  14. package/dist/driver-paddle.js +1 -1
  15. package/dist/driver-stripe.cjs +13 -13
  16. package/dist/driver-stripe.d.cts +1 -1
  17. package/dist/driver-stripe.d.ts +1 -1
  18. package/dist/driver-stripe.js +1 -1
  19. package/dist/index.cjs +56 -43
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +13 -5
  22. package/dist/index.d.ts +13 -5
  23. package/dist/index.js +18 -5
  24. package/dist/index.js.map +1 -1
  25. package/dist/{manifest-DQThneiG.d.cts → manifest-Cr2y1op6.d.cts} +40 -3
  26. package/dist/{manifest-DQThneiG.d.ts → manifest-Cr2y1op6.d.ts} +40 -3
  27. package/dist/manifest-entry.cjs +129 -20
  28. package/dist/manifest-entry.cjs.map +1 -1
  29. package/dist/manifest-entry.d.cts +28 -3
  30. package/dist/manifest-entry.d.ts +28 -3
  31. package/dist/manifest-entry.js +105 -5
  32. package/dist/manifest-entry.js.map +1 -1
  33. package/package.json +1 -1
  34. package/dist/chunk-7UC5VXOR.js.map +0 -1
  35. package/dist/chunk-UIR6TGEW.js.map +0 -1
  36. package/dist/chunk-VQOD2Z6Q.cjs.map +0 -1
  37. package/dist/chunk-Z3TWO2PW.cjs.map +0 -1
@@ -210,6 +210,8 @@ type Interval = 'day' | 'week' | 'month' | 'year' | 'one_time';
210
210
  /** What the platform hands the funnel per product (a single provider-resolved price). */
211
211
  interface ProductInput {
212
212
  id: string;
213
+ /** The catalog product KEY this input charges (the slot's assignment). Defaults to `id` (legacy/identity slots). */
214
+ catalogKey?: string;
213
215
  name?: string;
214
216
  displayName?: string;
215
217
  /** Provider-resolved price in **minor units** (the provider chose the currency). */
@@ -240,6 +242,8 @@ interface Money {
240
242
  */
241
243
  interface ResolvedProduct {
242
244
  id: string;
245
+ /** The catalog product KEY charged (the CHARGE identity; `id` is the SLOT/display identity). */
246
+ catalogKey: string;
243
247
  name: string;
244
248
  displayName: string;
245
249
  provider: string;
@@ -258,6 +262,8 @@ interface ResolvedProduct {
258
262
  /** Display-ready product the author reads (every amount locale-formatted). */
259
263
  interface Product {
260
264
  id: string;
265
+ /** The catalog product KEY charged (the CHARGE identity; `id` is the SLOT/display identity). */
266
+ catalogKey: string;
261
267
  name: string;
262
268
  displayName: string;
263
269
  provider: string;
@@ -456,8 +462,15 @@ interface FunnelDefinition {
456
462
  responses?: Record<string, VariableConfig>;
457
463
  /** `data.*` working/scratch variables (default values). */
458
464
  data?: Record<string, VariableConfig>;
459
- /** Product ids from the platform catalog this funnel uses (prices resolved at render). */
460
- products?: string[];
465
+ /**
466
+ * The catalog products this funnel sells, as SLOTS: a local, stable key each PAGE
467
+ * references (`useProduct('primary')`, `<Checkout product="primary">`) → the catalog product
468
+ * KEY it's assigned to. Swap the assignment to change the offer WITHOUT touching page code;
469
+ * `null` = a declared-but-unassigned slot. A bare `string[]` is legacy sugar for IDENTITY
470
+ * slots (`['pro'] ≡ { pro: 'pro' }`) — every existing funnel keeps working. Prices resolve
471
+ * from the project catalog per environment (Live/Test) at render.
472
+ */
473
+ products?: string[] | Record<string, string | null>;
461
474
  locales?: FunnelLocales;
462
475
  /**
463
476
  * When `true`, the funnel shows the **geo-specific** currency/price the payment
@@ -471,6 +484,19 @@ interface FunnelDefinition {
471
484
  }
472
485
  /** Identity helper for a funnel definition. */
473
486
  declare function defineFunnel(def: FunnelDefinition): FunnelDefinition;
487
+ /** A funnel product SLOT: the local key pages reference → the assigned catalog product key (null = unassigned). */
488
+ interface ProductSlot {
489
+ slot: string;
490
+ catalogKey: string | null;
491
+ }
492
+ /**
493
+ * Normalize `FunnelDefinition.products` to a slot list. A legacy `string[]` becomes IDENTITY
494
+ * slots (`['pro'] → [{ slot:'pro', catalogKey:'pro' }]`); a `{ slot: key|null }` map becomes its
495
+ * entries (insertion order preserved, so `useProducts()` is deterministic).
496
+ */
497
+ declare function normalizeProducts(products: FunnelDefinition['products']): ProductSlot[];
498
+ /** The unique, ASSIGNED catalog keys a funnel charges — for the promote gate + catalog reverse-lookup. */
499
+ declare function funnelCatalogKeys(products: FunnelDefinition['products']): string[];
474
500
 
475
501
  /**
476
502
  * Pure locale resolution — no React. Split out of {@link ./translation} so the
@@ -750,6 +776,10 @@ interface ManifestValidation {
750
776
  * though every page can proceed. Hard error.
751
777
  */
752
778
  noReachableFinish: boolean;
779
+ /** Slot names a PAGE references (`useProduct`/`<Checkout product>`) that the funnel doesn't declare — hard error. */
780
+ unknownProductRefs: string[];
781
+ /** Declared slots a page references but which have no catalog product assigned (map value `null`) — hard error. */
782
+ unassignedSlots: string[];
753
783
  }
754
784
  interface FunnelManifest {
755
785
  id: string;
@@ -759,7 +789,12 @@ interface FunnelManifest {
759
789
  nodes: string[];
760
790
  edges: ManifestEdge[];
761
791
  };
792
+ /** The unique, ASSIGNED catalog keys the funnel charges (for the promote gate + catalog reverse-lookup). */
762
793
  products: string[];
794
+ /** Product SLOTS: slot → assigned catalog key|null. `products` above is the unique assigned keys. */
795
+ productSlots: ProductSlot[];
796
+ /** Slot names PAGES reference (from the build's scan); validated against `productSlots`. Absent = no scan ran. */
797
+ productRefs?: string[];
763
798
  locales?: FunnelLocales;
764
799
  validation: ManifestValidation;
765
800
  }
@@ -770,8 +805,10 @@ interface CompileInput {
770
805
  key: string;
771
806
  meta?: PageMeta;
772
807
  }[];
808
+ /** Slot names the funnel's PAGES reference (from `scanProductRefs`) — cross-checked against the declared slots. */
809
+ productRefs?: string[];
773
810
  }
774
811
  /** Compile a funnel's spine into its declarative {@link FunnelManifest} IR. */
775
812
  declare function compileManifest(input: CompileInput): FunnelManifest;
776
813
 
777
- export { evaluateCondition as $, type Acquisition as A, type BrowserContext as B, type ClickIdContext as C, type Direction as D, type EdgeCondition as E, type FunnelContext as F, type Gate as G, type Route as H, type IdentityContext as I, type SystemContext as J, assignVariant as K, type LocaleContext as L, type ManifestEdge as M, bucketingSeed as N, type OsContext as O, type Product as P, buildAcquisition as Q, type RuntimeExperiment as R, type SessionContext as S, buildCatalog as T, type UtmContext as U, type VariableValue as V, compileManifest as W, currencyExponent as X, defineFunnel as Y, definePage as Z, entryGuard as _, type VariableConfig as a, evaluateGate as a0, expectedPathLength as a1, fnv1a as a2, formatMoney as a3, formatProduct as a4, hashToUnit as a5, isRtl as a6, isVariantKey as a7, nextPage as a8, outgoingKeys as a9, pageMeta as aa, parseSlotKey as ab, pickByWeight as ac, resolveExperiments as ad, resolveLocale as ae, resolveProduct as af, resolveRoute as ag, validateExperiments as ah, type FunnelPage as ai, weightsOf as aj, type FunnelSnapshot as b, type FunnelDefinition as c, buildContext as d, type ProductInput as e, type PageMeta as f, type PageContext as g, type BuildContextOptions as h, type CompileInput as i, type Condition as j, type ConditionOp as k, type DeviceContext as l, type ExperimentIssue as m, type ExperimentResolution as n, type ExperimentValidation as o, type ExperimentVariant as p, type FlowPage as q, type FunnelLocales as r, type FunnelManifest as s, type Interval as t, type ManifestPage as u, type ManifestValidation as v, type Money as w, type PageType as x, type Predicate as y, type ResolvedProduct as z };
814
+ export { evaluateCondition as $, type Acquisition as A, type BrowserContext as B, type ClickIdContext as C, type Direction as D, type EdgeCondition as E, type FunnelContext as F, type Gate as G, type Route as H, type IdentityContext as I, type SystemContext as J, assignVariant as K, type LocaleContext as L, type ManifestEdge as M, bucketingSeed as N, type OsContext as O, type Product as P, buildAcquisition as Q, type RuntimeExperiment as R, type SessionContext as S, buildCatalog as T, type UtmContext as U, type VariableValue as V, compileManifest as W, currencyExponent as X, defineFunnel as Y, definePage as Z, entryGuard as _, type VariableConfig as a, evaluateGate as a0, expectedPathLength as a1, fnv1a as a2, formatMoney as a3, formatProduct as a4, hashToUnit as a5, isRtl as a6, isVariantKey as a7, nextPage as a8, outgoingKeys as a9, pageMeta as aa, parseSlotKey as ab, pickByWeight as ac, resolveExperiments as ad, resolveLocale as ae, resolveProduct as af, resolveRoute as ag, validateExperiments as ah, type FunnelPage as ai, type ProductSlot as aj, funnelCatalogKeys as ak, normalizeProducts as al, weightsOf as am, type FunnelSnapshot as b, type FunnelDefinition as c, buildContext as d, type ProductInput as e, type PageMeta as f, type PageContext as g, type BuildContextOptions as h, type CompileInput as i, type Condition as j, type ConditionOp as k, type DeviceContext as l, type ExperimentIssue as m, type ExperimentResolution as n, type ExperimentValidation as o, type ExperimentVariant as p, type FlowPage as q, type FunnelLocales as r, type FunnelManifest as s, type Interval as t, type ManifestPage as u, type ManifestValidation as v, type Money as w, type PageType as x, type Predicate as y, type ResolvedProduct as z };
@@ -210,6 +210,8 @@ type Interval = 'day' | 'week' | 'month' | 'year' | 'one_time';
210
210
  /** What the platform hands the funnel per product (a single provider-resolved price). */
211
211
  interface ProductInput {
212
212
  id: string;
213
+ /** The catalog product KEY this input charges (the slot's assignment). Defaults to `id` (legacy/identity slots). */
214
+ catalogKey?: string;
213
215
  name?: string;
214
216
  displayName?: string;
215
217
  /** Provider-resolved price in **minor units** (the provider chose the currency). */
@@ -240,6 +242,8 @@ interface Money {
240
242
  */
241
243
  interface ResolvedProduct {
242
244
  id: string;
245
+ /** The catalog product KEY charged (the CHARGE identity; `id` is the SLOT/display identity). */
246
+ catalogKey: string;
243
247
  name: string;
244
248
  displayName: string;
245
249
  provider: string;
@@ -258,6 +262,8 @@ interface ResolvedProduct {
258
262
  /** Display-ready product the author reads (every amount locale-formatted). */
259
263
  interface Product {
260
264
  id: string;
265
+ /** The catalog product KEY charged (the CHARGE identity; `id` is the SLOT/display identity). */
266
+ catalogKey: string;
261
267
  name: string;
262
268
  displayName: string;
263
269
  provider: string;
@@ -456,8 +462,15 @@ interface FunnelDefinition {
456
462
  responses?: Record<string, VariableConfig>;
457
463
  /** `data.*` working/scratch variables (default values). */
458
464
  data?: Record<string, VariableConfig>;
459
- /** Product ids from the platform catalog this funnel uses (prices resolved at render). */
460
- products?: string[];
465
+ /**
466
+ * The catalog products this funnel sells, as SLOTS: a local, stable key each PAGE
467
+ * references (`useProduct('primary')`, `<Checkout product="primary">`) → the catalog product
468
+ * KEY it's assigned to. Swap the assignment to change the offer WITHOUT touching page code;
469
+ * `null` = a declared-but-unassigned slot. A bare `string[]` is legacy sugar for IDENTITY
470
+ * slots (`['pro'] ≡ { pro: 'pro' }`) — every existing funnel keeps working. Prices resolve
471
+ * from the project catalog per environment (Live/Test) at render.
472
+ */
473
+ products?: string[] | Record<string, string | null>;
461
474
  locales?: FunnelLocales;
462
475
  /**
463
476
  * When `true`, the funnel shows the **geo-specific** currency/price the payment
@@ -471,6 +484,19 @@ interface FunnelDefinition {
471
484
  }
472
485
  /** Identity helper for a funnel definition. */
473
486
  declare function defineFunnel(def: FunnelDefinition): FunnelDefinition;
487
+ /** A funnel product SLOT: the local key pages reference → the assigned catalog product key (null = unassigned). */
488
+ interface ProductSlot {
489
+ slot: string;
490
+ catalogKey: string | null;
491
+ }
492
+ /**
493
+ * Normalize `FunnelDefinition.products` to a slot list. A legacy `string[]` becomes IDENTITY
494
+ * slots (`['pro'] → [{ slot:'pro', catalogKey:'pro' }]`); a `{ slot: key|null }` map becomes its
495
+ * entries (insertion order preserved, so `useProducts()` is deterministic).
496
+ */
497
+ declare function normalizeProducts(products: FunnelDefinition['products']): ProductSlot[];
498
+ /** The unique, ASSIGNED catalog keys a funnel charges — for the promote gate + catalog reverse-lookup. */
499
+ declare function funnelCatalogKeys(products: FunnelDefinition['products']): string[];
474
500
 
475
501
  /**
476
502
  * Pure locale resolution — no React. Split out of {@link ./translation} so the
@@ -750,6 +776,10 @@ interface ManifestValidation {
750
776
  * though every page can proceed. Hard error.
751
777
  */
752
778
  noReachableFinish: boolean;
779
+ /** Slot names a PAGE references (`useProduct`/`<Checkout product>`) that the funnel doesn't declare — hard error. */
780
+ unknownProductRefs: string[];
781
+ /** Declared slots a page references but which have no catalog product assigned (map value `null`) — hard error. */
782
+ unassignedSlots: string[];
753
783
  }
754
784
  interface FunnelManifest {
755
785
  id: string;
@@ -759,7 +789,12 @@ interface FunnelManifest {
759
789
  nodes: string[];
760
790
  edges: ManifestEdge[];
761
791
  };
792
+ /** The unique, ASSIGNED catalog keys the funnel charges (for the promote gate + catalog reverse-lookup). */
762
793
  products: string[];
794
+ /** Product SLOTS: slot → assigned catalog key|null. `products` above is the unique assigned keys. */
795
+ productSlots: ProductSlot[];
796
+ /** Slot names PAGES reference (from the build's scan); validated against `productSlots`. Absent = no scan ran. */
797
+ productRefs?: string[];
763
798
  locales?: FunnelLocales;
764
799
  validation: ManifestValidation;
765
800
  }
@@ -770,8 +805,10 @@ interface CompileInput {
770
805
  key: string;
771
806
  meta?: PageMeta;
772
807
  }[];
808
+ /** Slot names the funnel's PAGES reference (from `scanProductRefs`) — cross-checked against the declared slots. */
809
+ productRefs?: string[];
773
810
  }
774
811
  /** Compile a funnel's spine into its declarative {@link FunnelManifest} IR. */
775
812
  declare function compileManifest(input: CompileInput): FunnelManifest;
776
813
 
777
- export { evaluateCondition as $, type Acquisition as A, type BrowserContext as B, type ClickIdContext as C, type Direction as D, type EdgeCondition as E, type FunnelContext as F, type Gate as G, type Route as H, type IdentityContext as I, type SystemContext as J, assignVariant as K, type LocaleContext as L, type ManifestEdge as M, bucketingSeed as N, type OsContext as O, type Product as P, buildAcquisition as Q, type RuntimeExperiment as R, type SessionContext as S, buildCatalog as T, type UtmContext as U, type VariableValue as V, compileManifest as W, currencyExponent as X, defineFunnel as Y, definePage as Z, entryGuard as _, type VariableConfig as a, evaluateGate as a0, expectedPathLength as a1, fnv1a as a2, formatMoney as a3, formatProduct as a4, hashToUnit as a5, isRtl as a6, isVariantKey as a7, nextPage as a8, outgoingKeys as a9, pageMeta as aa, parseSlotKey as ab, pickByWeight as ac, resolveExperiments as ad, resolveLocale as ae, resolveProduct as af, resolveRoute as ag, validateExperiments as ah, type FunnelPage as ai, weightsOf as aj, type FunnelSnapshot as b, type FunnelDefinition as c, buildContext as d, type ProductInput as e, type PageMeta as f, type PageContext as g, type BuildContextOptions as h, type CompileInput as i, type Condition as j, type ConditionOp as k, type DeviceContext as l, type ExperimentIssue as m, type ExperimentResolution as n, type ExperimentValidation as o, type ExperimentVariant as p, type FlowPage as q, type FunnelLocales as r, type FunnelManifest as s, type Interval as t, type ManifestPage as u, type ManifestValidation as v, type Money as w, type PageType as x, type Predicate as y, type ResolvedProduct as z };
814
+ export { evaluateCondition as $, type Acquisition as A, type BrowserContext as B, type ClickIdContext as C, type Direction as D, type EdgeCondition as E, type FunnelContext as F, type Gate as G, type Route as H, type IdentityContext as I, type SystemContext as J, assignVariant as K, type LocaleContext as L, type ManifestEdge as M, bucketingSeed as N, type OsContext as O, type Product as P, buildAcquisition as Q, type RuntimeExperiment as R, type SessionContext as S, buildCatalog as T, type UtmContext as U, type VariableValue as V, compileManifest as W, currencyExponent as X, defineFunnel as Y, definePage as Z, entryGuard as _, type VariableConfig as a, evaluateGate as a0, expectedPathLength as a1, fnv1a as a2, formatMoney as a3, formatProduct as a4, hashToUnit as a5, isRtl as a6, isVariantKey as a7, nextPage as a8, outgoingKeys as a9, pageMeta as aa, parseSlotKey as ab, pickByWeight as ac, resolveExperiments as ad, resolveLocale as ae, resolveProduct as af, resolveRoute as ag, validateExperiments as ah, type FunnelPage as ai, type ProductSlot as aj, funnelCatalogKeys as ak, normalizeProducts as al, weightsOf as am, type FunnelSnapshot as b, type FunnelDefinition as c, buildContext as d, type ProductInput as e, type PageMeta as f, type PageContext as g, type BuildContextOptions as h, type CompileInput as i, type Condition as j, type ConditionOp as k, type DeviceContext as l, type ExperimentIssue as m, type ExperimentResolution as n, type ExperimentValidation as o, type ExperimentVariant as p, type FlowPage as q, type FunnelLocales as r, type FunnelManifest as s, type Interval as t, type ManifestPage as u, type ManifestValidation as v, type Money as w, type PageType as x, type Predicate as y, type ResolvedProduct as z };
@@ -1,9 +1,102 @@
1
1
  'use strict';
2
2
 
3
- var chunkZ3TWO2PW_cjs = require('./chunk-Z3TWO2PW.cjs');
3
+ var chunkJSRKA375_cjs = require('./chunk-JSRKA375.cjs');
4
4
  var chunkLJYLGLFS_cjs = require('./chunk-LJYLGLFS.cjs');
5
5
 
6
+ // src/manifest/product-refs.ts
7
+ function mask(src) {
8
+ const out = src.split("");
9
+ const n = src.length;
10
+ let i = 0;
11
+ while (i < n) {
12
+ const c = src[i];
13
+ const d = src[i + 1];
14
+ if (c === "/" && d === "/") {
15
+ while (i < n && src[i] !== "\n") {
16
+ out[i] = " ";
17
+ i++;
18
+ }
19
+ } else if (c === "/" && d === "*") {
20
+ out[i] = " ";
21
+ out[i + 1] = " ";
22
+ i += 2;
23
+ while (i < n) {
24
+ if (src[i] === "*" && src[i + 1] === "/") {
25
+ out[i] = " ";
26
+ out[i + 1] = " ";
27
+ i += 2;
28
+ break;
29
+ }
30
+ if (src[i] !== "\n") out[i] = " ";
31
+ i++;
32
+ }
33
+ } else if (c === "'" || c === '"' || c === "`") {
34
+ i++;
35
+ while (i < n && src[i] !== c) {
36
+ if (src[i] === "\\") {
37
+ out[i] = " ";
38
+ i++;
39
+ if (i < n && src[i] !== "\n") out[i] = " ";
40
+ i++;
41
+ continue;
42
+ }
43
+ if (src[i] !== "\n") out[i] = " ";
44
+ i++;
45
+ }
46
+ i++;
47
+ } else i++;
48
+ }
49
+ return out.join("");
50
+ }
51
+ function literalAt(src, masked, open) {
52
+ const q = masked[open];
53
+ if (q !== "'" && q !== '"' && q !== "`") return null;
54
+ const close = masked.indexOf(q, open + 1);
55
+ if (close === -1) return null;
56
+ return src.slice(open + 1, close);
57
+ }
58
+ function skipWs(masked, i) {
59
+ while (i < masked.length && (masked[i] === " " || masked[i] === " " || masked[i] === "\n" || masked[i] === "\r")) i++;
60
+ return i;
61
+ }
62
+ function scanProductRefs(files) {
63
+ const seen = /* @__PURE__ */ new Set();
64
+ const order = [];
65
+ let dynamic = false;
66
+ const add = (v) => {
67
+ if (v && !seen.has(v)) {
68
+ seen.add(v);
69
+ order.push(v);
70
+ }
71
+ };
72
+ for (const [path, source] of Object.entries(files)) {
73
+ if (!/\.(tsx|jsx|ts|js)$/.test(path)) continue;
74
+ const masked = mask(source);
75
+ for (const m of masked.matchAll(/\buseProducts?\s*\(/g)) {
76
+ const plural = source.slice(m.index, m.index + 11) === "useProducts";
77
+ const j = skipWs(masked, m.index + m[0].length);
78
+ if (masked[j] === ")") {
79
+ if (plural) dynamic = true;
80
+ continue;
81
+ }
82
+ const lit = literalAt(source, masked, j);
83
+ if (lit !== null) add(lit);
84
+ else dynamic = true;
85
+ }
86
+ for (const m of masked.matchAll(/<(?:Checkout|Upsell)\b[^>]*?\bproduct\s*=\s*/g)) {
87
+ const j = m.index + m[0].length;
88
+ const lit = literalAt(source, masked, j);
89
+ if (lit !== null) add(lit);
90
+ else if (masked[j] === "{") dynamic = true;
91
+ }
92
+ }
93
+ return { slots: order, dynamic };
94
+ }
95
+
6
96
  // src/flow/scaffold.ts
97
+ function msgId(locale) {
98
+ return "msg_" + locale.replace(/[^a-zA-Z0-9]/g, "_");
99
+ }
7
100
  var DRIVERS = {
8
101
  stripe: { name: "stripeCheckoutDriver", sub: "driver-stripe" },
9
102
  paddle: { name: "paddleCheckoutDriver", sub: "driver-paddle" }
@@ -12,11 +105,13 @@ var HEADER = "// GENERATED by AppFunnel \u2014 do not edit. Built from funnel.ts
12
105
  function generateMount(opts) {
13
106
  const p = opts.importPrefix ?? "./";
14
107
  const driver = opts.checkout ? DRIVERS[opts.checkout] : null;
108
+ const locales = opts.messageLocales ?? [];
15
109
  const imports = [
16
110
  `import { createFunnelTree, type MountOpts, type RuntimePage } from '@appfunnel-dev/sdk'`,
17
111
  driver && `import { ${driver.name} } from '@appfunnel-dev/sdk/${driver.sub}'`,
18
112
  `import { config } from '${p}funnel'`,
19
- opts.hasLayout && `import Layout from '${p}layout'`
113
+ opts.hasLayout && `import Layout from '${p}layout'`,
114
+ ...locales.map((loc) => `import ${msgId(loc)} from '${p}messages/${loc}.json'`)
20
115
  ].filter(Boolean).join("\n");
21
116
  const loaders = opts.pageKeys.map(
22
117
  (k) => ` ${JSON.stringify(k)}: () => import(${JSON.stringify(`${p}pages/${k}`)}),`
@@ -25,8 +120,12 @@ function generateMount(opts) {
25
120
  "config",
26
121
  "pages",
27
122
  opts.hasLayout && "layout: Layout",
28
- driver && `checkoutDriver: ${driver.name}`
123
+ driver && `checkoutDriver: ${driver.name}`,
124
+ locales.length > 0 && "messages"
29
125
  ].filter(Boolean).join(", ");
126
+ const messagesConst = locales.length > 0 ? `
127
+ const messages = { ${locales.map((loc) => `${JSON.stringify(loc)}: ${msgId(loc)}`).join(", ")} }
128
+ ` : "";
30
129
  return `${HEADER}${imports}
31
130
 
32
131
  const loaders: Record<string, () => Promise<unknown>> = {
@@ -43,7 +142,7 @@ export const pages: RuntimePage[] = [
43
142
  .filter((k) => !flowKeys.has(k))
44
143
  .map((k) => ({ key: k, meta: {}, load: loaders[k] })),
45
144
  ] as RuntimePage[]
46
-
145
+ ${messagesConst}
47
146
  export const tree = createFunnelTree({ ${treeArgs} })
48
147
 
49
148
  export type { MountOpts }
@@ -74,6 +173,7 @@ import { tree, pages, type MountOpts } from './mount'
74
173
  export const manifest = compileManifest({
75
174
  funnel: config,
76
175
  pages: pages.map((pg) => ({ key: pg.key, meta: pg.meta })),
176
+ productRefs: ${JSON.stringify(opts.productRefs ?? [])},
77
177
  })
78
178
 
79
179
  export async function ssr(opts: MountOpts): Promise<string> {
@@ -97,67 +197,75 @@ if (el) createRoot(el).render(tree(${JSON.stringify(opts.data)}))
97
197
 
98
198
  Object.defineProperty(exports, "assignVariant", {
99
199
  enumerable: true,
100
- get: function () { return chunkZ3TWO2PW_cjs.assignVariant; }
200
+ get: function () { return chunkJSRKA375_cjs.assignVariant; }
101
201
  });
102
202
  Object.defineProperty(exports, "bucketingSeed", {
103
203
  enumerable: true,
104
- get: function () { return chunkZ3TWO2PW_cjs.bucketingSeed; }
204
+ get: function () { return chunkJSRKA375_cjs.bucketingSeed; }
105
205
  });
106
206
  Object.defineProperty(exports, "buildCatalog", {
107
207
  enumerable: true,
108
- get: function () { return chunkZ3TWO2PW_cjs.buildCatalog; }
208
+ get: function () { return chunkJSRKA375_cjs.buildCatalog; }
109
209
  });
110
210
  Object.defineProperty(exports, "compileManifest", {
111
211
  enumerable: true,
112
- get: function () { return chunkZ3TWO2PW_cjs.compileManifest; }
212
+ get: function () { return chunkJSRKA375_cjs.compileManifest; }
113
213
  });
114
214
  Object.defineProperty(exports, "currencyExponent", {
115
215
  enumerable: true,
116
- get: function () { return chunkZ3TWO2PW_cjs.currencyExponent; }
216
+ get: function () { return chunkJSRKA375_cjs.currencyExponent; }
117
217
  });
118
218
  Object.defineProperty(exports, "defineFunnel", {
119
219
  enumerable: true,
120
- get: function () { return chunkZ3TWO2PW_cjs.defineFunnel; }
220
+ get: function () { return chunkJSRKA375_cjs.defineFunnel; }
121
221
  });
122
222
  Object.defineProperty(exports, "formatMoney", {
123
223
  enumerable: true,
124
- get: function () { return chunkZ3TWO2PW_cjs.formatMoney; }
224
+ get: function () { return chunkJSRKA375_cjs.formatMoney; }
125
225
  });
126
226
  Object.defineProperty(exports, "formatProduct", {
127
227
  enumerable: true,
128
- get: function () { return chunkZ3TWO2PW_cjs.formatProduct; }
228
+ get: function () { return chunkJSRKA375_cjs.formatProduct; }
229
+ });
230
+ Object.defineProperty(exports, "funnelCatalogKeys", {
231
+ enumerable: true,
232
+ get: function () { return chunkJSRKA375_cjs.funnelCatalogKeys; }
129
233
  });
130
234
  Object.defineProperty(exports, "isRtl", {
131
235
  enumerable: true,
132
- get: function () { return chunkZ3TWO2PW_cjs.isRtl; }
236
+ get: function () { return chunkJSRKA375_cjs.isRtl; }
133
237
  });
134
238
  Object.defineProperty(exports, "isVariantKey", {
135
239
  enumerable: true,
136
- get: function () { return chunkZ3TWO2PW_cjs.isVariantKey; }
240
+ get: function () { return chunkJSRKA375_cjs.isVariantKey; }
241
+ });
242
+ Object.defineProperty(exports, "normalizeProducts", {
243
+ enumerable: true,
244
+ get: function () { return chunkJSRKA375_cjs.normalizeProducts; }
137
245
  });
138
246
  Object.defineProperty(exports, "pageMeta", {
139
247
  enumerable: true,
140
- get: function () { return chunkZ3TWO2PW_cjs.pageMeta; }
248
+ get: function () { return chunkJSRKA375_cjs.pageMeta; }
141
249
  });
142
250
  Object.defineProperty(exports, "parseSlotKey", {
143
251
  enumerable: true,
144
- get: function () { return chunkZ3TWO2PW_cjs.parseSlotKey; }
252
+ get: function () { return chunkJSRKA375_cjs.parseSlotKey; }
145
253
  });
146
254
  Object.defineProperty(exports, "resolveLocale", {
147
255
  enumerable: true,
148
- get: function () { return chunkZ3TWO2PW_cjs.resolveLocale; }
256
+ get: function () { return chunkJSRKA375_cjs.resolveLocale; }
149
257
  });
150
258
  Object.defineProperty(exports, "resolveProduct", {
151
259
  enumerable: true,
152
- get: function () { return chunkZ3TWO2PW_cjs.resolveProduct; }
260
+ get: function () { return chunkJSRKA375_cjs.resolveProduct; }
153
261
  });
154
262
  Object.defineProperty(exports, "validateExperiments", {
155
263
  enumerable: true,
156
- get: function () { return chunkZ3TWO2PW_cjs.validateExperiments; }
264
+ get: function () { return chunkJSRKA375_cjs.validateExperiments; }
157
265
  });
158
266
  Object.defineProperty(exports, "weightsOf", {
159
267
  enumerable: true,
160
- get: function () { return chunkZ3TWO2PW_cjs.weightsOf; }
268
+ get: function () { return chunkJSRKA375_cjs.weightsOf; }
161
269
  });
162
270
  Object.defineProperty(exports, "INLINE_SURFACES", {
163
271
  enumerable: true,
@@ -199,5 +307,6 @@ exports.generateDevEntry = generateDevEntry;
199
307
  exports.generateEntryClient = generateEntryClient;
200
308
  exports.generateEntryServer = generateEntryServer;
201
309
  exports.generateMount = generateMount;
310
+ exports.scanProductRefs = scanProductRefs;
202
311
  //# sourceMappingURL=manifest-entry.cjs.map
203
312
  //# sourceMappingURL=manifest-entry.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/flow/scaffold.ts"],"names":[],"mappings":"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","file":"manifest-entry.cjs","sourcesContent":["/**\n * Source generators for the funnel build \"machinery\" authors no longer write. `funnel.ts` (the\n * spine: id, responses, products, checkout, and the page list + declarative routing) plus the\n * `pages/` folder are the ONLY authored source. The build (renderer/builder) and the CLI dev server\n * call these to synthesize:\n * - `mount.tsx` — code-split page loaders + `createFunnelTree`\n * - `entry.client.tsx` — hydrate the SSR'd markup from injected data\n * - `entry.server.tsx` — `ssr()` + the compiled `manifest`\n *\n * All string templates, zero runtime deps — this module lives in the pure `@appfunnel-dev/sdk/\n * manifest` entry so tooling imports it without the funnel runtime.\n */\n\nexport interface ScaffoldOptions {\n\t/** Page component keys = `pages/<key>.tsx` filenames (base flow pages AND `@variant` pages). */\n\tpageKeys: string[]\n\t/** Import prefix to reach the funnel root: `'./'` at the root (build), `'../../'` in a subdir (CLI dev). */\n\timportPrefix?: string\n\t/** Whether a `layout.tsx` exists to wrap the pages. */\n\thasLayout?: boolean\n\t/** Whether a `styles.css` exists (client entry imports it). */\n\thasStyles?: boolean\n\t/** Checkout provider from `config.checkout` — wires the driver; omit for none. */\n\tcheckout?: 'stripe' | 'paddle' | null\n}\n\nconst DRIVERS = {\n\tstripe: { name: 'stripeCheckoutDriver', sub: 'driver-stripe' },\n\tpaddle: { name: 'paddleCheckoutDriver', sub: 'driver-paddle' },\n} as const\n\nconst HEADER = '// GENERATED by AppFunnel — do not edit. Built from funnel.ts + the pages/ folder.\\n'\n\n/** `mount.tsx` — the code-split loaders + tree, derived from `config.pages` and the `pages/` files. */\nexport function generateMount(opts: ScaffoldOptions): string {\n\tconst p = opts.importPrefix ?? './'\n\tconst driver = opts.checkout ? DRIVERS[opts.checkout] : null\n\n\tconst imports = [\n\t\t`import { createFunnelTree, type MountOpts, type RuntimePage } from '@appfunnel-dev/sdk'`,\n\t\tdriver && `import { ${driver.name} } from '@appfunnel-dev/sdk/${driver.sub}'`,\n\t\t`import { config } from '${p}funnel'`,\n\t\topts.hasLayout && `import Layout from '${p}layout'`,\n\t]\n\t\t.filter(Boolean)\n\t\t.join('\\n')\n\n\t// A static import() per page file so Vite emits one code-split chunk each.\n\tconst loaders = opts.pageKeys\n\t\t.map(\n\t\t\t(k) => `\\t${JSON.stringify(k)}: () => import(${JSON.stringify(`${p}pages/${k}`)}),`\n\t\t)\n\t\t.join('\\n')\n\n\tconst treeArgs = [\n\t\t'config',\n\t\t'pages',\n\t\topts.hasLayout && 'layout: Layout',\n\t\tdriver && `checkoutDriver: ${driver.name}`,\n\t]\n\t\t.filter(Boolean)\n\t\t.join(', ')\n\n\treturn `${HEADER}${imports}\n\nconst loaders: Record<string, () => Promise<unknown>> = {\n${loaders}\n}\n\n// Flow pages come from funnel.ts (order + meta + routing). Any remaining page file is an off-flow\n// \\`@variant\\` page (wired by experiment records at render time), appended so it's loadable.\nconst decls = config.pages ?? []\nconst flowKeys = new Set(decls.map((d) => d.key))\nexport const pages: RuntimePage[] = [\n\t...decls.map((d) => ({ key: d.key, meta: d, load: loaders[d.key] })),\n\t...Object.keys(loaders)\n\t\t.filter((k) => !flowKeys.has(k))\n\t\t.map((k) => ({ key: k, meta: {}, load: loaders[k] })),\n] as RuntimePage[]\n\nexport const tree = createFunnelTree({ ${treeArgs} })\n\nexport type { MountOpts }\n`\n}\n\n/** `entry.client.tsx` — hydrate the SSR'd markup from the injected `#__af_data`. */\nexport function generateEntryClient(opts: Pick<ScaffoldOptions, 'importPrefix' | 'hasStyles'>): string {\n\tconst p = opts.importPrefix ?? './'\n\tconst styles = opts.hasStyles ? `import '${p}styles.css'\\n` : ''\n\treturn `${HEADER}${styles}import { hydrateRoot } from 'react-dom/client'\nimport { tree, type MountOpts } from './mount'\n\nconst dataEl = document.getElementById('__af_data')\nconst data = (dataEl?.textContent ? JSON.parse(dataEl.textContent) : {}) as MountOpts & {\n\tinitialKey?: string | null\n}\nconst root = document.getElementById('root')\nif (root) hydrateRoot(root, tree({ ...data, initialKey: data.initialKey ?? undefined }))\n`\n}\n\n/** `entry.server.tsx` — `ssr(opts)` HTML + the compiled `manifest` (read by the build). */\nexport function generateEntryServer(opts: Pick<ScaffoldOptions, 'importPrefix'>): string {\n\tconst p = opts.importPrefix ?? './'\n\treturn `${HEADER}import { renderToReadableStream } from 'react-dom/server.browser'\nimport { compileManifest } from '@appfunnel-dev/sdk'\nimport { config } from '${p}funnel'\nimport { tree, pages, type MountOpts } from './mount'\n\nexport const manifest = compileManifest({\n\tfunnel: config,\n\tpages: pages.map((pg) => ({ key: pg.key, meta: pg.meta })),\n})\n\nexport async function ssr(opts: MountOpts): Promise<string> {\n\tconst stream = await renderToReadableStream(tree(opts))\n\tawait stream.allReady\n\treturn await new Response(stream).text()\n}\n`\n}\n\n/** Client-only dev entry (createRoot, no SSR) — for the CLI `dev` server. */\nexport function generateDevEntry(\n\topts: Pick<ScaffoldOptions, 'importPrefix' | 'hasStyles'> & { data: unknown }\n): string {\n\tconst p = opts.importPrefix ?? './'\n\tconst styles = opts.hasStyles ? `import '${p}styles.css'\\n` : ''\n\treturn `${HEADER}${styles}import { createRoot } from 'react-dom/client'\nimport { tree } from './mount'\n\nconst el = document.getElementById('root')\nif (el) createRoot(el).render(tree(${JSON.stringify(opts.data)}))\n`\n}\n"]}
1
+ {"version":3,"sources":["../src/manifest/product-refs.ts","../src/flow/scaffold.ts"],"names":[],"mappings":"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","file":"manifest-entry.cjs","sourcesContent":["/**\n * Pure, React-free page scan: find which product SLOTS a funnel's pages reference — the first\n * string-literal arg of `useProduct(...)` and the `product` prop of `<Checkout>` / `<Upsell>`.\n * Ships via `@appfunnel-dev/sdk/manifest` so the BUILD, the EDITOR, and the AI all run the SAME\n * check. The result feeds `compileManifest({ productRefs })`, which flags references to slots the\n * funnel doesn't declare (unknown) or hasn't assigned a catalog product to (unassigned).\n *\n * Non-literal args (`useProduct(selectedId)`, `product={expr}`) and the `useProducts()` wildcard are\n * DYNAMIC — they can't be statically enumerated, so they're reported via `dynamic` and never turned\n * into a hard error (validation relaxes when a funnel is dynamic). Mirrors the mask-then-read\n * discipline the funnel.ts code-mods use, so a `useProduct('x')` inside a comment or string literal\n * can't false-positive.\n */\n\nexport interface ProductRefScan {\n /** Concrete slot names referenced by LITERAL args, deduped, in first-seen order. */\n slots: string[]\n /** True if any ref is a non-literal arg or a `useProducts()` wildcard (not statically enumerable). */\n dynamic: boolean\n}\n\n/** Blank comment + string/template CONTENTS (keep quote delimiters + length) so scans don't fire inside them. */\nfunction mask(src: string): string {\n const out = src.split('')\n const n = src.length\n let i = 0\n while (i < n) {\n const c = src[i]\n const d = src[i + 1]\n if (c === '/' && d === '/') {\n while (i < n && src[i] !== '\\n') { out[i] = ' '; i++ }\n } else if (c === '/' && d === '*') {\n out[i] = ' '; out[i + 1] = ' '; i += 2\n while (i < n) {\n if (src[i] === '*' && src[i + 1] === '/') { out[i] = ' '; out[i + 1] = ' '; i += 2; break }\n if (src[i] !== '\\n') out[i] = ' '\n i++\n }\n } else if (c === \"'\" || c === '\"' || c === '`') {\n i++\n while (i < n && src[i] !== c) {\n if (src[i] === '\\\\') { out[i] = ' '; i++; if (i < n && src[i] !== '\\n') out[i] = ' '; i++; continue }\n if (src[i] !== '\\n') out[i] = ' '\n i++\n }\n i++ // closing delimiter (kept)\n } else i++\n }\n return out.join('')\n}\n\n/** A quoted string literal starting at `open` (a quote char in the MASK). Value read from the ORIGINAL. */\nfunction literalAt(src: string, masked: string, open: number): string | null {\n const q = masked[open]\n if (q !== \"'\" && q !== '\"' && q !== '`') return null\n const close = masked.indexOf(q, open + 1)\n if (close === -1) return null\n return src.slice(open + 1, close)\n}\n\n/** Advance past whitespace in `masked` from `i`. */\nfunction skipWs(masked: string, i: number): number {\n while (i < masked.length && (masked[i] === ' ' || masked[i] === '\\t' || masked[i] === '\\n' || masked[i] === '\\r')) i++\n return i\n}\n\nexport function scanProductRefs(files: Record<string, string>): ProductRefScan {\n const seen = new Set<string>()\n const order: string[] = []\n let dynamic = false\n const add = (v: string) => { if (v && !seen.has(v)) { seen.add(v); order.push(v) } }\n\n for (const [path, source] of Object.entries(files)) {\n if (!/\\.(tsx|jsx|ts|js)$/.test(path)) continue\n const masked = mask(source)\n\n // useProduct( … ) / useProducts( … ) — positions on the MASK (a call inside a string won't match).\n for (const m of masked.matchAll(/\\buseProducts?\\s*\\(/g)) {\n const plural = source.slice(m.index!, m.index! + 11) === 'useProducts'\n const j = skipWs(masked, m.index! + m[0].length)\n if (masked[j] === ')') { if (plural) dynamic = true; continue } // useProducts() = whole-catalog wildcard\n const lit = literalAt(source, masked, j)\n if (lit !== null) add(lit)\n else dynamic = true // useProduct(variable) — can't resolve statically\n }\n\n // <Checkout product=\"…\"> / <Upsell product=\"…\"> — the `product` attribute value.\n for (const m of masked.matchAll(/<(?:Checkout|Upsell)\\b[^>]*?\\bproduct\\s*=\\s*/g)) {\n const j = m.index! + m[0].length\n const lit = literalAt(source, masked, j)\n if (lit !== null) add(lit)\n else if (masked[j] === '{') dynamic = true // product={expr}\n }\n }\n return { slots: order, dynamic }\n}\n","/**\n * Source generators for the funnel build \"machinery\" authors no longer write. `funnel.ts` (the\n * spine: id, responses, products, checkout, and the page list + declarative routing) plus the\n * `pages/` folder are the ONLY authored source. The build (renderer/builder) and the CLI dev server\n * call these to synthesize:\n * - `mount.tsx` — code-split page loaders + `createFunnelTree`\n * - `entry.client.tsx` — hydrate the SSR'd markup from injected data\n * - `entry.server.tsx` — `ssr()` + the compiled `manifest`\n *\n * All string templates, zero runtime deps — this module lives in the pure `@appfunnel-dev/sdk/\n * manifest` entry so tooling imports it without the funnel runtime.\n */\n\nexport interface ScaffoldOptions {\n\t/** Page component keys = `pages/<key>.tsx` filenames (base flow pages AND `@variant` pages). */\n\tpageKeys: string[]\n\t/** Import prefix to reach the funnel root: `'./'` at the root (build), `'../../'` in a subdir (CLI dev). */\n\timportPrefix?: string\n\t/** Whether a `layout.tsx` exists to wrap the pages. */\n\thasLayout?: boolean\n\t/** Whether a `styles.css` exists (client entry imports it). */\n\thasStyles?: boolean\n\t/** Checkout provider from `config.checkout` — wires the driver; omit for none. */\n\tcheckout?: 'stripe' | 'paddle' | null\n\t/** Locales with a `messages/<locale>.json` catalog to bake into the mount (so `t()` resolves at render). */\n\tmessageLocales?: string[]\n\t/** Slot names the pages reference (from `scanProductRefs`), baked into the manifest for validation. */\n\tproductRefs?: string[]\n}\n\n/** A safe JS identifier for a message-catalog import (`en` → `msg_en`, `pt-BR` → `msg_pt_BR`). */\nfunction msgId(locale: string): string {\n\treturn 'msg_' + locale.replace(/[^a-zA-Z0-9]/g, '_')\n}\n\nconst DRIVERS = {\n\tstripe: { name: 'stripeCheckoutDriver', sub: 'driver-stripe' },\n\tpaddle: { name: 'paddleCheckoutDriver', sub: 'driver-paddle' },\n} as const\n\nconst HEADER = '// GENERATED by AppFunnel — do not edit. Built from funnel.ts + the pages/ folder.\\n'\n\n/** `mount.tsx` — the code-split loaders + tree, derived from `config.pages` and the `pages/` files. */\nexport function generateMount(opts: ScaffoldOptions): string {\n\tconst p = opts.importPrefix ?? './'\n\tconst driver = opts.checkout ? DRIVERS[opts.checkout] : null\n\n\tconst locales = opts.messageLocales ?? []\n\tconst imports = [\n\t\t`import { createFunnelTree, type MountOpts, type RuntimePage } from '@appfunnel-dev/sdk'`,\n\t\tdriver && `import { ${driver.name} } from '@appfunnel-dev/sdk/${driver.sub}'`,\n\t\t`import { config } from '${p}funnel'`,\n\t\topts.hasLayout && `import Layout from '${p}layout'`,\n\t\t...locales.map((loc) => `import ${msgId(loc)} from '${p}messages/${loc}.json'`),\n\t]\n\t\t.filter(Boolean)\n\t\t.join('\\n')\n\n\t// A static import() per page file so Vite emits one code-split chunk each.\n\tconst loaders = opts.pageKeys\n\t\t.map(\n\t\t\t(k) => `\\t${JSON.stringify(k)}: () => import(${JSON.stringify(`${p}pages/${k}`)}),`\n\t\t)\n\t\t.join('\\n')\n\n\tconst treeArgs = [\n\t\t'config',\n\t\t'pages',\n\t\topts.hasLayout && 'layout: Layout',\n\t\tdriver && `checkoutDriver: ${driver.name}`,\n\t\tlocales.length > 0 && 'messages',\n\t]\n\t\t.filter(Boolean)\n\t\t.join(', ')\n\n\t// The baked message catalog: locale → messages/<locale>.json (so `t()` resolves at render).\n\tconst messagesConst =\n\t\tlocales.length > 0\n\t\t\t? `\\nconst messages = { ${locales.map((loc) => `${JSON.stringify(loc)}: ${msgId(loc)}`).join(', ')} }\\n`\n\t\t\t: ''\n\n\treturn `${HEADER}${imports}\n\nconst loaders: Record<string, () => Promise<unknown>> = {\n${loaders}\n}\n\n// Flow pages come from funnel.ts (order + meta + routing). Any remaining page file is an off-flow\n// \\`@variant\\` page (wired by experiment records at render time), appended so it's loadable.\nconst decls = config.pages ?? []\nconst flowKeys = new Set(decls.map((d) => d.key))\nexport const pages: RuntimePage[] = [\n\t...decls.map((d) => ({ key: d.key, meta: d, load: loaders[d.key] })),\n\t...Object.keys(loaders)\n\t\t.filter((k) => !flowKeys.has(k))\n\t\t.map((k) => ({ key: k, meta: {}, load: loaders[k] })),\n] as RuntimePage[]\n${messagesConst}\nexport const tree = createFunnelTree({ ${treeArgs} })\n\nexport type { MountOpts }\n`\n}\n\n/** `entry.client.tsx` — hydrate the SSR'd markup from the injected `#__af_data`. */\nexport function generateEntryClient(opts: Pick<ScaffoldOptions, 'importPrefix' | 'hasStyles'>): string {\n\tconst p = opts.importPrefix ?? './'\n\tconst styles = opts.hasStyles ? `import '${p}styles.css'\\n` : ''\n\treturn `${HEADER}${styles}import { hydrateRoot } from 'react-dom/client'\nimport { tree, type MountOpts } from './mount'\n\nconst dataEl = document.getElementById('__af_data')\nconst data = (dataEl?.textContent ? JSON.parse(dataEl.textContent) : {}) as MountOpts & {\n\tinitialKey?: string | null\n}\nconst root = document.getElementById('root')\nif (root) hydrateRoot(root, tree({ ...data, initialKey: data.initialKey ?? undefined }))\n`\n}\n\n/** `entry.server.tsx` — `ssr(opts)` HTML + the compiled `manifest` (read by the build). */\nexport function generateEntryServer(opts: Pick<ScaffoldOptions, 'importPrefix' | 'productRefs'>): string {\n\tconst p = opts.importPrefix ?? './'\n\treturn `${HEADER}import { renderToReadableStream } from 'react-dom/server.browser'\nimport { compileManifest } from '@appfunnel-dev/sdk'\nimport { config } from '${p}funnel'\nimport { tree, pages, type MountOpts } from './mount'\n\nexport const manifest = compileManifest({\n\tfunnel: config,\n\tpages: pages.map((pg) => ({ key: pg.key, meta: pg.meta })),\n\tproductRefs: ${JSON.stringify(opts.productRefs ?? [])},\n})\n\nexport async function ssr(opts: MountOpts): Promise<string> {\n\tconst stream = await renderToReadableStream(tree(opts))\n\tawait stream.allReady\n\treturn await new Response(stream).text()\n}\n`\n}\n\n/** Client-only dev entry (createRoot, no SSR) — for the CLI `dev` server. */\nexport function generateDevEntry(\n\topts: Pick<ScaffoldOptions, 'importPrefix' | 'hasStyles'> & { data: unknown }\n): string {\n\tconst p = opts.importPrefix ?? './'\n\tconst styles = opts.hasStyles ? `import '${p}styles.css'\\n` : ''\n\treturn `${HEADER}${styles}import { createRoot } from 'react-dom/client'\nimport { tree } from './mount'\n\nconst el = document.getElementById('root')\nif (el) createRoot(el).render(tree(${JSON.stringify(opts.data)}))\n`\n}\n"]}
@@ -1,8 +1,29 @@
1
- export { i as CompileInput, j as Condition, k as ConditionOp, E as EdgeCondition, m as ExperimentIssue, o as ExperimentValidation, p as ExperimentVariant, q as FlowPage, c as FunnelDefinition, r as FunnelLocales, s as FunnelManifest, ai as FunnelPage, b as FunnelSnapshot, G as Gate, t as Interval, M as ManifestEdge, u as ManifestPage, v as ManifestValidation, w as Money, f as PageMeta, x as PageType, y as Predicate, P as Product, e as ProductInput, z as ResolvedProduct, H as Route, R as RuntimeExperiment, K as assignVariant, N as bucketingSeed, T as buildCatalog, W as compileManifest, X as currencyExponent, Y as defineFunnel, a3 as formatMoney, a4 as formatProduct, a6 as isRtl, a7 as isVariantKey, aa as pageMeta, ab as parseSlotKey, ae as resolveLocale, af as resolveProduct, ah as validateExperiments, aj as weightsOf } from './manifest-DQThneiG.cjs';
1
+ export { i as CompileInput, j as Condition, k as ConditionOp, E as EdgeCondition, m as ExperimentIssue, o as ExperimentValidation, p as ExperimentVariant, q as FlowPage, c as FunnelDefinition, r as FunnelLocales, s as FunnelManifest, ai as FunnelPage, b as FunnelSnapshot, G as Gate, t as Interval, M as ManifestEdge, u as ManifestPage, v as ManifestValidation, w as Money, f as PageMeta, x as PageType, y as Predicate, P as Product, e as ProductInput, aj as ProductSlot, z as ResolvedProduct, H as Route, R as RuntimeExperiment, K as assignVariant, N as bucketingSeed, T as buildCatalog, W as compileManifest, X as currencyExponent, Y as defineFunnel, a3 as formatMoney, a4 as formatProduct, ak as funnelCatalogKeys, a6 as isRtl, a7 as isVariantKey, al as normalizeProducts, aa as pageMeta, ab as parseSlotKey, ae as resolveLocale, af as resolveProduct, ah as validateExperiments, am as weightsOf } from './manifest-Cr2y1op6.cjs';
2
2
  import { C as CheckoutError, b as CheckoutIntent, U as UpsellKind, d as CheckoutSurface } from './capabilities-7_hy5f5G.cjs';
3
3
  export { a as CheckoutErrorCategory, c as CheckoutProvider, I as INLINE_SURFACES, O as OffSessionReliability, P as PROVIDER_PROFILES, e as ProviderProfile, S as SurfaceCapability, V as ValidationResult, f as checkoutError, i as isInlineSurface, g as isMerchantOfRecord, h as isOrchestrator, s as surfacesFor, v as validateCheckout, j as validateUpsell } from './capabilities-7_hy5f5G.cjs';
4
4
  import 'react';
5
5
 
6
+ /**
7
+ * Pure, React-free page scan: find which product SLOTS a funnel's pages reference — the first
8
+ * string-literal arg of `useProduct(...)` and the `product` prop of `<Checkout>` / `<Upsell>`.
9
+ * Ships via `@appfunnel-dev/sdk/manifest` so the BUILD, the EDITOR, and the AI all run the SAME
10
+ * check. The result feeds `compileManifest({ productRefs })`, which flags references to slots the
11
+ * funnel doesn't declare (unknown) or hasn't assigned a catalog product to (unassigned).
12
+ *
13
+ * Non-literal args (`useProduct(selectedId)`, `product={expr}`) and the `useProducts()` wildcard are
14
+ * DYNAMIC — they can't be statically enumerated, so they're reported via `dynamic` and never turned
15
+ * into a hard error (validation relaxes when a funnel is dynamic). Mirrors the mask-then-read
16
+ * discipline the funnel.ts code-mods use, so a `useProduct('x')` inside a comment or string literal
17
+ * can't false-positive.
18
+ */
19
+ interface ProductRefScan {
20
+ /** Concrete slot names referenced by LITERAL args, deduped, in first-seen order. */
21
+ slots: string[];
22
+ /** True if any ref is a non-literal arg or a `useProducts()` wildcard (not statically enumerable). */
23
+ dynamic: boolean;
24
+ }
25
+ declare function scanProductRefs(files: Record<string, string>): ProductRefScan;
26
+
6
27
  /**
7
28
  * Source generators for the funnel build "machinery" authors no longer write. `funnel.ts` (the
8
29
  * spine: id, responses, products, checkout, and the page list + declarative routing) plus the
@@ -26,13 +47,17 @@ interface ScaffoldOptions {
26
47
  hasStyles?: boolean;
27
48
  /** Checkout provider from `config.checkout` — wires the driver; omit for none. */
28
49
  checkout?: 'stripe' | 'paddle' | null;
50
+ /** Locales with a `messages/<locale>.json` catalog to bake into the mount (so `t()` resolves at render). */
51
+ messageLocales?: string[];
52
+ /** Slot names the pages reference (from `scanProductRefs`), baked into the manifest for validation. */
53
+ productRefs?: string[];
29
54
  }
30
55
  /** `mount.tsx` — the code-split loaders + tree, derived from `config.pages` and the `pages/` files. */
31
56
  declare function generateMount(opts: ScaffoldOptions): string;
32
57
  /** `entry.client.tsx` — hydrate the SSR'd markup from the injected `#__af_data`. */
33
58
  declare function generateEntryClient(opts: Pick<ScaffoldOptions, 'importPrefix' | 'hasStyles'>): string;
34
59
  /** `entry.server.tsx` — `ssr(opts)` HTML + the compiled `manifest` (read by the build). */
35
- declare function generateEntryServer(opts: Pick<ScaffoldOptions, 'importPrefix'>): string;
60
+ declare function generateEntryServer(opts: Pick<ScaffoldOptions, 'importPrefix' | 'productRefs'>): string;
36
61
  /** Client-only dev entry (createRoot, no SSR) — for the CLI `dev` server. */
37
62
  declare function generateDevEntry(opts: Pick<ScaffoldOptions, 'importPrefix' | 'hasStyles'> & {
38
63
  data: unknown;
@@ -181,4 +206,4 @@ interface CheckoutCompleteRequest {
181
206
  }
182
207
  type CheckoutCompleteResponse = StepSettled | StepPending | StepFailed;
183
208
 
184
- export { type CheckoutCompleteRequest, type CheckoutCompleteResponse, CheckoutError, CheckoutIntent, type CheckoutSessionRequest, type CheckoutSessionResponse, type CheckoutSettlement, CheckoutSurface, type ScaffoldOptions, type StepClientConfirm, type StepEmbedded, type StepFailed, type StepPending, type StepProviderCheckout, type StepRedirect, type StepSettled, UpsellKind, generateDevEntry, generateEntryClient, generateEntryServer, generateMount };
209
+ export { type CheckoutCompleteRequest, type CheckoutCompleteResponse, CheckoutError, CheckoutIntent, type CheckoutSessionRequest, type CheckoutSessionResponse, type CheckoutSettlement, CheckoutSurface, type ProductRefScan, type ScaffoldOptions, type StepClientConfirm, type StepEmbedded, type StepFailed, type StepPending, type StepProviderCheckout, type StepRedirect, type StepSettled, UpsellKind, generateDevEntry, generateEntryClient, generateEntryServer, generateMount, scanProductRefs };
@@ -1,8 +1,29 @@
1
- export { i as CompileInput, j as Condition, k as ConditionOp, E as EdgeCondition, m as ExperimentIssue, o as ExperimentValidation, p as ExperimentVariant, q as FlowPage, c as FunnelDefinition, r as FunnelLocales, s as FunnelManifest, ai as FunnelPage, b as FunnelSnapshot, G as Gate, t as Interval, M as ManifestEdge, u as ManifestPage, v as ManifestValidation, w as Money, f as PageMeta, x as PageType, y as Predicate, P as Product, e as ProductInput, z as ResolvedProduct, H as Route, R as RuntimeExperiment, K as assignVariant, N as bucketingSeed, T as buildCatalog, W as compileManifest, X as currencyExponent, Y as defineFunnel, a3 as formatMoney, a4 as formatProduct, a6 as isRtl, a7 as isVariantKey, aa as pageMeta, ab as parseSlotKey, ae as resolveLocale, af as resolveProduct, ah as validateExperiments, aj as weightsOf } from './manifest-DQThneiG.js';
1
+ export { i as CompileInput, j as Condition, k as ConditionOp, E as EdgeCondition, m as ExperimentIssue, o as ExperimentValidation, p as ExperimentVariant, q as FlowPage, c as FunnelDefinition, r as FunnelLocales, s as FunnelManifest, ai as FunnelPage, b as FunnelSnapshot, G as Gate, t as Interval, M as ManifestEdge, u as ManifestPage, v as ManifestValidation, w as Money, f as PageMeta, x as PageType, y as Predicate, P as Product, e as ProductInput, aj as ProductSlot, z as ResolvedProduct, H as Route, R as RuntimeExperiment, K as assignVariant, N as bucketingSeed, T as buildCatalog, W as compileManifest, X as currencyExponent, Y as defineFunnel, a3 as formatMoney, a4 as formatProduct, ak as funnelCatalogKeys, a6 as isRtl, a7 as isVariantKey, al as normalizeProducts, aa as pageMeta, ab as parseSlotKey, ae as resolveLocale, af as resolveProduct, ah as validateExperiments, am as weightsOf } from './manifest-Cr2y1op6.js';
2
2
  import { C as CheckoutError, b as CheckoutIntent, U as UpsellKind, d as CheckoutSurface } from './capabilities-7_hy5f5G.js';
3
3
  export { a as CheckoutErrorCategory, c as CheckoutProvider, I as INLINE_SURFACES, O as OffSessionReliability, P as PROVIDER_PROFILES, e as ProviderProfile, S as SurfaceCapability, V as ValidationResult, f as checkoutError, i as isInlineSurface, g as isMerchantOfRecord, h as isOrchestrator, s as surfacesFor, v as validateCheckout, j as validateUpsell } from './capabilities-7_hy5f5G.js';
4
4
  import 'react';
5
5
 
6
+ /**
7
+ * Pure, React-free page scan: find which product SLOTS a funnel's pages reference — the first
8
+ * string-literal arg of `useProduct(...)` and the `product` prop of `<Checkout>` / `<Upsell>`.
9
+ * Ships via `@appfunnel-dev/sdk/manifest` so the BUILD, the EDITOR, and the AI all run the SAME
10
+ * check. The result feeds `compileManifest({ productRefs })`, which flags references to slots the
11
+ * funnel doesn't declare (unknown) or hasn't assigned a catalog product to (unassigned).
12
+ *
13
+ * Non-literal args (`useProduct(selectedId)`, `product={expr}`) and the `useProducts()` wildcard are
14
+ * DYNAMIC — they can't be statically enumerated, so they're reported via `dynamic` and never turned
15
+ * into a hard error (validation relaxes when a funnel is dynamic). Mirrors the mask-then-read
16
+ * discipline the funnel.ts code-mods use, so a `useProduct('x')` inside a comment or string literal
17
+ * can't false-positive.
18
+ */
19
+ interface ProductRefScan {
20
+ /** Concrete slot names referenced by LITERAL args, deduped, in first-seen order. */
21
+ slots: string[];
22
+ /** True if any ref is a non-literal arg or a `useProducts()` wildcard (not statically enumerable). */
23
+ dynamic: boolean;
24
+ }
25
+ declare function scanProductRefs(files: Record<string, string>): ProductRefScan;
26
+
6
27
  /**
7
28
  * Source generators for the funnel build "machinery" authors no longer write. `funnel.ts` (the
8
29
  * spine: id, responses, products, checkout, and the page list + declarative routing) plus the
@@ -26,13 +47,17 @@ interface ScaffoldOptions {
26
47
  hasStyles?: boolean;
27
48
  /** Checkout provider from `config.checkout` — wires the driver; omit for none. */
28
49
  checkout?: 'stripe' | 'paddle' | null;
50
+ /** Locales with a `messages/<locale>.json` catalog to bake into the mount (so `t()` resolves at render). */
51
+ messageLocales?: string[];
52
+ /** Slot names the pages reference (from `scanProductRefs`), baked into the manifest for validation. */
53
+ productRefs?: string[];
29
54
  }
30
55
  /** `mount.tsx` — the code-split loaders + tree, derived from `config.pages` and the `pages/` files. */
31
56
  declare function generateMount(opts: ScaffoldOptions): string;
32
57
  /** `entry.client.tsx` — hydrate the SSR'd markup from the injected `#__af_data`. */
33
58
  declare function generateEntryClient(opts: Pick<ScaffoldOptions, 'importPrefix' | 'hasStyles'>): string;
34
59
  /** `entry.server.tsx` — `ssr(opts)` HTML + the compiled `manifest` (read by the build). */
35
- declare function generateEntryServer(opts: Pick<ScaffoldOptions, 'importPrefix'>): string;
60
+ declare function generateEntryServer(opts: Pick<ScaffoldOptions, 'importPrefix' | 'productRefs'>): string;
36
61
  /** Client-only dev entry (createRoot, no SSR) — for the CLI `dev` server. */
37
62
  declare function generateDevEntry(opts: Pick<ScaffoldOptions, 'importPrefix' | 'hasStyles'> & {
38
63
  data: unknown;
@@ -181,4 +206,4 @@ interface CheckoutCompleteRequest {
181
206
  }
182
207
  type CheckoutCompleteResponse = StepSettled | StepPending | StepFailed;
183
208
 
184
- export { type CheckoutCompleteRequest, type CheckoutCompleteResponse, CheckoutError, CheckoutIntent, type CheckoutSessionRequest, type CheckoutSessionResponse, type CheckoutSettlement, CheckoutSurface, type ScaffoldOptions, type StepClientConfirm, type StepEmbedded, type StepFailed, type StepPending, type StepProviderCheckout, type StepRedirect, type StepSettled, UpsellKind, generateDevEntry, generateEntryClient, generateEntryServer, generateMount };
209
+ export { type CheckoutCompleteRequest, type CheckoutCompleteResponse, CheckoutError, CheckoutIntent, type CheckoutSessionRequest, type CheckoutSessionResponse, type CheckoutSettlement, CheckoutSurface, type ProductRefScan, type ScaffoldOptions, type StepClientConfirm, type StepEmbedded, type StepFailed, type StepPending, type StepProviderCheckout, type StepRedirect, type StepSettled, UpsellKind, generateDevEntry, generateEntryClient, generateEntryServer, generateMount, scanProductRefs };