@appfunnel-dev/sdk 2.0.0-canary.4 → 2.0.0-canary.7

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 (60) hide show
  1. package/README.md +1 -1
  2. package/dist/{checkout-Dz8cGkB_.d.cts → checkout-CMEdxpgo.d.cts} +13 -13
  3. package/dist/{checkout-7Dy6IedP.d.ts → checkout-D1NUPv4p.d.ts} +13 -13
  4. package/dist/{chunk-AKO6XKXP.js → chunk-7YQLPPPG.js} +66 -91
  5. package/dist/chunk-7YQLPPPG.js.map +1 -0
  6. package/dist/{chunk-JSRKA375.cjs → chunk-BPOAWI4G.cjs} +68 -94
  7. package/dist/chunk-BPOAWI4G.cjs.map +1 -0
  8. package/dist/{chunk-M6U3FNRW.js → chunk-CFFMZYPE.js} +3 -3
  9. package/dist/{chunk-M6U3FNRW.js.map → chunk-CFFMZYPE.js.map} +1 -1
  10. package/dist/chunk-EMMSS5I5.cjs +37 -0
  11. package/dist/chunk-EMMSS5I5.cjs.map +1 -0
  12. package/dist/chunk-G3PMV62Z.js +33 -0
  13. package/dist/chunk-G3PMV62Z.js.map +1 -0
  14. package/dist/{chunk-LJYLGLFS.cjs → chunk-QMAZGLGV.cjs} +2 -35
  15. package/dist/chunk-QMAZGLGV.cjs.map +1 -0
  16. package/dist/{chunk-YY375F2B.js → chunk-TNLRQPVY.js} +3 -33
  17. package/dist/chunk-TNLRQPVY.js.map +1 -0
  18. package/dist/{chunk-CY4VBSMX.cjs → chunk-VAOR77NW.cjs} +7 -7
  19. package/dist/{chunk-CY4VBSMX.cjs.map → chunk-VAOR77NW.cjs.map} +1 -1
  20. package/dist/chunk-WYUDL4FI.cjs +8 -0
  21. package/dist/chunk-WYUDL4FI.cjs.map +1 -0
  22. package/dist/chunk-ZZJG4EYL.js +6 -0
  23. package/dist/chunk-ZZJG4EYL.js.map +1 -0
  24. package/dist/driver-paddle.cjs +23 -22
  25. package/dist/driver-paddle.cjs.map +1 -1
  26. package/dist/driver-paddle.d.cts +1 -1
  27. package/dist/driver-paddle.d.ts +1 -1
  28. package/dist/driver-paddle.js +3 -2
  29. package/dist/driver-paddle.js.map +1 -1
  30. package/dist/driver-stripe.cjs +277 -224
  31. package/dist/driver-stripe.cjs.map +1 -1
  32. package/dist/driver-stripe.d.cts +18 -2
  33. package/dist/driver-stripe.d.ts +18 -2
  34. package/dist/driver-stripe.js +244 -192
  35. package/dist/driver-stripe.js.map +1 -1
  36. package/dist/index.cjs +750 -150
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.d.cts +151 -90
  39. package/dist/index.d.ts +151 -90
  40. package/dist/index.js +702 -98
  41. package/dist/index.js.map +1 -1
  42. package/dist/{manifest-Cr2y1op6.d.cts → manifest-CMe8yVkr.d.cts} +131 -88
  43. package/dist/{manifest-Cr2y1op6.d.ts → manifest-CMe8yVkr.d.ts} +131 -88
  44. package/dist/manifest-entry.cjs +62 -47
  45. package/dist/manifest-entry.cjs.map +1 -1
  46. package/dist/manifest-entry.d.cts +15 -15
  47. package/dist/manifest-entry.d.ts +15 -15
  48. package/dist/manifest-entry.js +25 -14
  49. package/dist/manifest-entry.js.map +1 -1
  50. package/dist/protocol.cjs +13 -0
  51. package/dist/protocol.cjs.map +1 -0
  52. package/dist/protocol.d.cts +182 -0
  53. package/dist/protocol.d.ts +182 -0
  54. package/dist/protocol.js +4 -0
  55. package/dist/protocol.js.map +1 -0
  56. package/package.json +14 -2
  57. package/dist/chunk-AKO6XKXP.js.map +0 -1
  58. package/dist/chunk-JSRKA375.cjs.map +0 -1
  59. package/dist/chunk-LJYLGLFS.cjs.map +0 -1
  60. package/dist/chunk-YY375F2B.js.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,9 +1,10 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ComponentType, ButtonHTMLAttributes } from 'react';
3
- import { V as VariableValue, a as VariableConfig, A as Acquisition, P as Product, D as Direction, F as FunnelContext, R as RuntimeExperiment, b as FunnelSnapshot, c as FunnelDefinition, d as buildContext, e as ProductInput, f as PageMeta, g as PageContext } from './manifest-Cr2y1op6.cjs';
4
- export { B as BrowserContext, h as BuildContextOptions, C as ClickIdContext, i as CompileInput, j as Condition, k as ConditionOp, l as DeviceContext, E as EdgeCondition, m as ExperimentIssue, n as ExperimentResolution, o as ExperimentValidation, p as ExperimentVariant, q as FlowPage, r as FunnelLocales, s as FunnelManifest, G as Gate, I as IdentityContext, t as Interval, L as LocaleContext, M as ManifestEdge, u as ManifestPage, v as ManifestValidation, w as Money, O as OsContext, x as PageType, y as Predicate, z as ResolvedProduct, H as Route, S as SessionContext, J as SystemContext, U as UtmContext, K as assignVariant, N as bucketingSeed, Q as buildAcquisition, T as buildCatalog, W as compileManifest, X as currencyExponent, Y as defineFunnel, Z as definePage, _ as entryGuard, $ as evaluateCondition, a0 as evaluateGate, a1 as expectedPathLength, a2 as fnv1a, a3 as formatMoney, a4 as formatProduct, a5 as hashToUnit, a6 as isRtl, a7 as isVariantKey, a8 as nextPage, a9 as outgoingKeys, aa as pageMeta, ab as parseSlotKey, ac as pickByWeight, ad as resolveExperiments, ae as resolveLocale, af as resolveProduct, ag as resolveRoute, ah as validateExperiments } from './manifest-Cr2y1op6.cjs';
5
- import { a as CheckoutDriver, C as CheckoutDriverContext } from './checkout-Dz8cGkB_.cjs';
6
- export { b as Checkout, c as CheckoutCallbacks, d as CheckoutHandle, e as CheckoutProps, f as CheckoutRequest, g as CheckoutResult, h as CheckoutResume, i as CheckoutStatus, F as FailureModalProps, j as FailureRoute, M as MockDriverOptions, O as OnFailedMap, U as Upsell, k as UpsellProps, l as UseCheckoutOptions, m as createMockDriver, n as driverWithEmail, u as useCheckout } from './checkout-Dz8cGkB_.cjs';
3
+ import { V as VariableValue, a as VariableConfig, A as Acquisition, O as Offering, D as Direction, F as FunnelContext, R as RuntimeExperiment, b as FunnelSnapshot, c as FunnelDefinition, d as buildContext, e as OfferingInput, P as PageMeta, f as PageContext } from './manifest-CMe8yVkr.cjs';
4
+ export { B as BrowserContext, g as BuildContextOptions, C as ClickIdContext, h as CompileInput, i as Condition, j as ConditionOp, k as DeviceContext, E as EdgeCondition, l as ExperimentArm, m as ExperimentResolution, n as FlowPage, o as FunnelLocales, p as FunnelManifest, G as Gate, I as IdentityContext, q as Interval, L as LocaleContext, M as ManifestEdge, r as ManifestPage, s as ManifestValidation, t as Money, u as OsContext, v as PageType, w as Predicate, x as ResolvedOffering, y as Route, S as SessionContext, z as SystemContext, U as UtmContext, H as assignVariant, J as bucketingSeed, K as buildAcquisition, N as buildCatalog, Q as compileManifest, T as currencyExponent, W as defineFunnel, X as definePage, Y as entryGuard, Z as evaluateCondition, _ as evaluateGate, $ as expectedPathLength, a0 as fnv1a, a1 as formatMoney, a2 as formatOffering, a3 as hashToUnit, a4 as isRtl, a5 as isVariantKey, a6 as nextPage, a7 as outgoingKeys, a8 as pageMeta, a9 as parseSlotKey, aa as pickByWeight, ab as resolveExperiments, ac as resolveLocale, ad as resolveOffering, ae as resolveRoute } from './manifest-CMe8yVkr.cjs';
5
+ import { NavSnapshot } from './protocol.cjs';
6
+ import { a as CheckoutDriver, C as CheckoutDriverContext } from './checkout-CMEdxpgo.cjs';
7
+ export { b as Checkout, c as CheckoutCallbacks, d as CheckoutHandle, e as CheckoutProps, f as CheckoutRequest, g as CheckoutResult, h as CheckoutResume, i as CheckoutStatus, F as FailureModalProps, j as FailureRoute, M as MockDriverOptions, O as OnFailedMap, U as Upsell, k as UpsellProps, l as UseCheckoutOptions, m as createMockDriver, n as driverWithEmail, u as useCheckout } from './checkout-CMEdxpgo.cjs';
7
8
  import { ToasterProps } from 'sonner';
8
9
  export { ToasterProps, toast } from 'sonner';
9
10
  export { C as CheckoutError, a as CheckoutErrorCategory, b as CheckoutIntent, c as CheckoutProvider, d as CheckoutSurface, I as INLINE_SURFACES, O as OffSessionReliability, P as PROVIDER_PROFILES, e as ProviderProfile, S as SurfaceCapability, U as UpsellKind, 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';
@@ -187,30 +188,123 @@ declare function useTracker(): {
187
188
  };
188
189
 
189
190
  /**
190
- * v2 `products` — the **read-only** product catalog.
191
+ * The **`window.appfunnel` event bus** — the client-side integration surface
192
+ * (port of v0 `admin/lib/funnel/utils/appfunnelRuntime.ts`).
193
+ *
194
+ * Every taxonomy event the runtime emits is mirrored here, so on-page pixels and
195
+ * custom scripts integrate by **subscribing** — `window.appfunnel.on('purchase.complete', …)`
196
+ * — instead of the funnel hand-injecting pixel config per page. The pre-built pixel
197
+ * loaders (Meta/TikTok/GTM/Clarity) are just subscribers; CAPI stays the server-side
198
+ * default (this is the *client* sink of the same events). See doc 08 §3.
199
+ *
200
+ * Two deliberate properties vs. v0:
201
+ * - **Replay history** (`events`): async pixels load *after* `funnel.start`/`page.view`
202
+ * fire, so a late subscriber would miss them. The bus keeps a bounded history a
203
+ * subscriber can replay (the GTM `dataLayer` trick) — no lost events.
204
+ * - **Subscribe + read + drive.** The window surface exposes `on`/`off`/`events` + read
205
+ * getters, and — resolving the deferred bus-control-scope decision (doc 08 §3/§4) —
206
+ * **flow control**: `goTo`/`goNext`/`goPrevious` + a `navigation()` snapshot, so a host
207
+ * page (the live editor, or an author's own external nav UI) can drive the funnel. It
208
+ * still has **no `emit`** (third-party code can't forge taxonomy events) and no
209
+ * `setVariable` (state stays funnel-owned). Flow control is a no-op (warns once) until a
210
+ * `<FunnelView>` mounts and registers itself via {@link FunnelBus.registerNavigation}.
211
+ */
212
+
213
+ /** Read accessors the bus exposes — wired to the funnel's store + context. */
214
+ interface BusAccessors {
215
+ getVariable: (key: string) => unknown;
216
+ getVariables: () => Record<string, unknown>;
217
+ getCurrentPageId: () => string | null;
218
+ getCustomerId: () => string | null;
219
+ getVisitorId: () => string | null;
220
+ }
221
+ interface BusEvent {
222
+ event: string;
223
+ data: unknown;
224
+ /** Epoch ms the event was emitted (for ordering / replay). */
225
+ ts: number;
226
+ }
227
+ type Listener = (data: unknown) => void;
228
+ /** The flow-control seam a mounted `<FunnelView>` registers into the bus (SDK-internal). */
229
+ interface NavigationControl {
230
+ /** Jump to a page key. Returns `false` if the key isn't a real page (no navigation happened). */
231
+ goTo(pageKey: string): boolean;
232
+ goNext(): void;
233
+ goPrevious(): void;
234
+ snapshot(): NavSnapshot | null;
235
+ }
236
+ /**
237
+ * The public `window.appfunnel` surface — subscribe + read + drive the flow. It never
238
+ * exposes `emit` (no forged events) or `setVariable` (state stays funnel-owned).
239
+ */
240
+ interface AppfunnelBusApi extends BusAccessors {
241
+ /** Subscribe to an event (or `'*'` for all). Returns an unsubscribe fn. */
242
+ on(event: string, cb: Listener): () => void;
243
+ off(event: string, cb: Listener): void;
244
+ /** Bounded replay history — read past events, then `on()` for future ones. */
245
+ readonly events: BusEvent[];
246
+ /** Jump the flow to a page key. Returns `false` for an unknown key (or before the flow mounts). */
247
+ goTo(pageKey: string): boolean;
248
+ /** Advance via the current page's `next` predicate (else the linear next). */
249
+ goNext(): void;
250
+ /** Step back to the previous page in flow history. */
251
+ goPrevious(): void;
252
+ /** The current flow snapshot, or `null` before a `<FunnelView>` mounts. */
253
+ navigation(): NavSnapshot | null;
254
+ }
255
+ /**
256
+ * The internal bus: the public {@link AppfunnelBusApi} plus `emit` (SDK-only) and
257
+ * `now` injection. The SDK holds this; only {@link AppfunnelBusApi} reaches `window`.
258
+ */
259
+ interface FunnelBus extends AppfunnelBusApi {
260
+ emit(event: string, data?: unknown): void;
261
+ /** SDK-only: a mounted `<FunnelView>` registers its flow control; returns an unregister fn. */
262
+ registerNavigation(nav: NavigationControl): () => void;
263
+ }
264
+ /** Create a bus over the given read accessors. `now` is injectable for tests. */
265
+ declare function createBus(accessors: BusAccessors, now?: () => number): FunnelBus;
266
+ /**
267
+ * Wrap a {@link Tracker} so every emitted taxonomy event is mirrored to the bus
268
+ * (the client sink). `identify()` also surfaces a `user.registered` to the bus, so
269
+ * pixels see registration. The server sink (the inner tracker → CAPI) is untouched.
270
+ */
271
+ declare function withBus(tracker: Tracker, bus: FunnelBus): Tracker;
272
+ declare global {
273
+ interface Window {
274
+ appfunnel?: AppfunnelBusApi;
275
+ }
276
+ }
277
+ /**
278
+ * Attach the bus's public surface to `window.appfunnel` (client-side only).
279
+ * Returns a detach fn for unmount. No-op under SSR.
280
+ */
281
+ declare function attachBus(bus: FunnelBus): () => void;
282
+
283
+ /**
284
+ * v2 `offerings` — the **read-only** offering catalog.
191
285
  *
192
- * **AppFunnel does not manage currency.** Multi-currency is entirely the payment
286
+ * **Appfunnel does not manage currency.** Multi-currency is entirely the payment
193
287
  * provider's job: the provider (or its preview API) resolves the visitor's price,
194
- * and we receive a single resolved `{ amount, currency }` per product. No FX, no
288
+ * and we receive a single resolved `{ amount, currency }` per offering. No FX, no
195
289
  * currency selection, no conversion on our side. (FX appears only for frozen-USD
196
290
  * *reporting*, never display — phase-3 §3.6b.) A provider that doesn't localize
197
291
  * simply gives one currency.
198
292
  *
199
- * Our only job is **display**: pick the product by logical id and **format** its
293
+ * Our only job is **display**: pick the offering by logical id and **format** its
200
294
  * amount. Formatting happens at read time in the funnel's **active locale** (so
201
295
  * switching language reformats `kr 79,00` ⇄ `$9.99`), while the **currency** is
202
296
  * whatever the provider resolved. Currency selection is the platform's; the
203
- * authoring funnel only ever names a product id.
297
+ * authoring funnel only ever names an offering id.
204
298
  *
205
299
  * The math itself (resolve/format/exponents) is pure and lives in {@link ./money}
206
300
  * so the server/tooling entry can use it without React — this module re-exports
207
301
  * it and adds the React wiring (provider + hooks).
208
302
  */
209
303
 
210
- /** `useProduct('monthly')` — one product, formatted in the active locale. */
211
- declare function useProduct(id: string | undefined): Product | undefined;
212
- /** `useProducts()` — every product, formatted in the active locale, in order. */
213
- declare function useProducts(): Product[];
304
+ /** `useOffering('monthly')` — one offering, formatted in the active locale. */
305
+ declare function useOffering(id: string | undefined): Offering | undefined;
306
+ /** `useOfferings()` — every offering, formatted in the active locale, in order. */
307
+ declare function useOfferings(): Offering[];
214
308
 
215
309
  /**
216
310
  * v2 localization — translated **text** (prices already localize via `Intl` in
@@ -340,12 +434,21 @@ interface FunnelProviderProps {
340
434
  config?: FunnelDefinition;
341
435
  /** Server / session-restored values overriding declared defaults. */
342
436
  sessionValues?: Record<string, VariableValue>;
437
+ /**
438
+ * FLASH-FREE HYDRATION (SSR-isolation Option B): when true, the store is seeded with
439
+ * DEFAULTS only for the initial (hydration) render — matching the build-time-prerendered
440
+ * DEFAULT-state markup — and `sessionValues` are applied as a POST-hydration state update in
441
+ * a mount effect (`store.setMany`), so a returning visitor's saved answers pop into
442
+ * already-visible controls without an empty-shell flash or a hydration mismatch. Off (the
443
+ * default) preserves the original behavior: `sessionValues` seed the store at construction
444
+ * (used by SSR itself and by non-prerendered mounts). The client entry sets this to true. */
445
+ deferSessionValues?: boolean;
343
446
  /** The platform tracker; defaults to a silent console tracker (tests/dev). */
344
447
  tracker?: Tracker;
345
448
  /** Computed-context seed (funnel id, test/live mode, server-hydrated utm/session). */
346
449
  context?: Parameters<typeof buildContext>[0];
347
- /** Products with their provider-resolved price (`{ amount, currency }`). */
348
- products?: ProductInput[];
450
+ /** Offerings with their provider-resolved price (`{ amount, currency }`). */
451
+ offerings?: OfferingInput[];
349
452
  /** Live page-level experiment records, platform/DB-sourced (not authored in funnel code). */
350
453
  experiments?: RuntimeExperiment[];
351
454
  /** The platform checkout driver; defaults to a mock that resolves successfully. */
@@ -364,7 +467,7 @@ interface FunnelProviderProps {
364
467
  * Mounts a funnel: builds the writable {@link VariableStore} + the read-only
365
468
  * {@link FunnelContext} (once) and provides the runtime to the tree via hooks.
366
469
  */
367
- declare function FunnelProvider({ config, sessionValues, tracker, context: contextOpts, products, experiments, checkout, messages, locale: localeOverride, toaster, store, children, }: FunnelProviderProps): ReactNode;
470
+ declare function FunnelProvider({ config, sessionValues, deferSessionValues, tracker, context: contextOpts, offerings, experiments, checkout, messages, locale: localeOverride, toaster, store, children, }: FunnelProviderProps): ReactNode;
368
471
  /**
369
472
  * The imperative handle. Does **not** subscribe — use it in event handlers and
370
473
  * effects (`useFunnel().responses.set({...})`). For reactive reads use the hooks.
@@ -477,9 +580,20 @@ declare function useExperiment(id: string): string | undefined;
477
580
  *
478
581
  * Must live inside `<FunnelProvider>`.
479
582
  */
480
- declare function FunnelView({ pages, initialKey, layout, fallback, prefetch: prefetchMode, onNavigate, }: {
583
+ declare function FunnelView({ pages, initialKey, trustInitialKey, layout, fallback, prefetch: prefetchMode, onNavigate, }: {
481
584
  pages: RuntimePage[];
482
585
  initialKey?: string;
586
+ /**
587
+ * FLASH-FREE HYDRATION (SSR-isolation Option B, FIX 1): honor `initialKey` VERBATIM as the entry
588
+ * page, WITHOUT re-evaluating its entry `guard` on the client. The SERVER already resolved the
589
+ * true entry — either by evaluating a declarative guard to pick the prerendered target-vs-bounce,
590
+ * or by running the real guard inside the fallback SSR render — and threaded the resolved key
591
+ * here. With `deferSessionValues` on, the client's initial snapshot is EMPTY (saved answers arrive
592
+ * post-hydration), so re-running the guard here would wrongly bounce a qualified visitor and
593
+ * mismatch the server markup. Set by the client entry (via createFunnelTree) alongside
594
+ * `deferSessionValues`; the guard still protects in-funnel navigation and non-deferred mounts.
595
+ */
596
+ trustInitialKey?: boolean;
483
597
  layout?: ComponentType<{
484
598
  children: ReactNode;
485
599
  }>;
@@ -524,8 +638,8 @@ declare function useAssets(): Record<string, string>;
524
638
  declare function useAsset(name: string): string | undefined;
525
639
  /** What the renderer injects when mounting a funnel build (SSR and hydration). */
526
640
  interface MountOpts {
527
- /** Platform-resolved product prices (one currency per visitor). */
528
- products?: ProductInput[];
641
+ /** Platform-resolved offering prices (one currency per visitor). */
642
+ offerings?: OfferingInput[];
529
643
  /** Explicit locale override (URL/param/geo); wins over the detected device locale. */
530
644
  locale?: string;
531
645
  mode?: 'test' | 'live';
@@ -567,6 +681,23 @@ interface MountOpts {
567
681
  /** The server-resolved session id backing `sessionValues` — seeds the tracker (fallback)
568
682
  * and `context.session.id`. */
569
683
  sessionId?: string | null;
684
+ /** Mount the cross-origin {@link EmbedBridge} — the Funnel Embed API (flow snapshot + goTo/goNext/
685
+ * goPrevious + screenshot, over the versioned wire protocol). Set ONLY by preview surfaces (the live
686
+ * editor + the sealed `/preview/<buildId>` build); a live prod funnel leaves it off so it can't be
687
+ * driven / can't leak its flow state to an embedder. */
688
+ embed?: boolean;
689
+ /** FLASH-FREE HYDRATION (SSR-isolation Option B): set by the CLIENT entry only. The initial
690
+ * render is seeded with DEFAULTS (matching the build-time default-state prerender it hydrates
691
+ * against), then `sessionValues` are applied post-hydration via `store.setMany` — a returning
692
+ * visitor's saved answers pop into already-visible controls, no empty flash, no mismatch. Unset
693
+ * on SSR (the server render must bake sessionValues into the markup, the original behavior). */
694
+ deferSessionValues?: boolean;
695
+ /** SSR-isolation Option B, FIX 1: render `initialKey` VERBATIM without evaluating its entry guard.
696
+ * Set by the build-time PRERENDER stage so a guarded page (paywall/upsell) prerenders its OWN
697
+ * default-state markup (the renderer decides target-vs-bounce at serve time by evaluating the
698
+ * guard against the visitor's session). Defaults to `deferSessionValues` (the client trusts the
699
+ * server-resolved key); the prerender sets it true explicitly while leaving sessionValues baked. */
700
+ trustInitialKey?: boolean;
570
701
  }
571
702
  /**
572
703
  * The shape a funnel build's `mount.tsx` module must export — the named,
@@ -758,76 +889,6 @@ type ModalProps = OverlayCommon;
758
889
  /** A centered dialog. Auto-binds to its `defineModal` — `<Modal>…`. */
759
890
  declare function Modal(props: ModalProps): ReactNode;
760
891
 
761
- /**
762
- * The **`window.appfunnel` event bus** — the client-side integration surface
763
- * (port of v0 `admin/lib/funnel/utils/appfunnelRuntime.ts`).
764
- *
765
- * Every taxonomy event the runtime emits is mirrored here, so on-page pixels and
766
- * custom scripts integrate by **subscribing** — `window.appfunnel.on('purchase.complete', …)`
767
- * — instead of the funnel hand-injecting pixel config per page. The pre-built pixel
768
- * loaders (Meta/TikTok/GTM/Clarity) are just subscribers; CAPI stays the server-side
769
- * default (this is the *client* sink of the same events). See doc 08 §3.
770
- *
771
- * Two deliberate properties vs. v0:
772
- * - **Replay history** (`events`): async pixels load *after* `funnel.start`/`page.view`
773
- * fire, so a late subscriber would miss them. The bus keeps a bounded history a
774
- * subscriber can replay (the GTM `dataLayer` trick) — no lost events.
775
- * - **Subscribe + read only.** The window surface exposes `on`/`off`/`events` + read
776
- * getters; it has **no `emit`** (third-party code can't forge events) and **no
777
- * mutating actions** (`setVariable`/navigation) — the funnel can't be driven through
778
- * it. (v0's full-control Custom-JS API is deferred pending the bus-control-scope
779
- * decision; doc 08 §3 + §4.)
780
- */
781
-
782
- /** Read accessors the bus exposes — wired to the funnel's store + context. */
783
- interface BusAccessors {
784
- getVariable: (key: string) => unknown;
785
- getVariables: () => Record<string, unknown>;
786
- getCurrentPageId: () => string | null;
787
- getCustomerId: () => string | null;
788
- getVisitorId: () => string | null;
789
- }
790
- interface BusEvent {
791
- event: string;
792
- data: unknown;
793
- /** Epoch ms the event was emitted (for ordering / replay). */
794
- ts: number;
795
- }
796
- type Listener = (data: unknown) => void;
797
- /** The public `window.appfunnel` surface — subscribe + read, never emit or mutate. */
798
- interface AppFunnelBusApi extends BusAccessors {
799
- /** Subscribe to an event (or `'*'` for all). Returns an unsubscribe fn. */
800
- on(event: string, cb: Listener): () => void;
801
- off(event: string, cb: Listener): void;
802
- /** Bounded replay history — read past events, then `on()` for future ones. */
803
- readonly events: BusEvent[];
804
- }
805
- /**
806
- * The internal bus: the public {@link AppFunnelBusApi} plus `emit` (SDK-only) and
807
- * `now` injection. The SDK holds this; only {@link AppFunnelBusApi} reaches `window`.
808
- */
809
- interface FunnelBus extends AppFunnelBusApi {
810
- emit(event: string, data?: unknown): void;
811
- }
812
- /** Create a bus over the given read accessors. `now` is injectable for tests. */
813
- declare function createBus(accessors: BusAccessors, now?: () => number): FunnelBus;
814
- /**
815
- * Wrap a {@link Tracker} so every emitted taxonomy event is mirrored to the bus
816
- * (the client sink). `identify()` also surfaces a `user.registered` to the bus, so
817
- * pixels see registration. The server sink (the inner tracker → CAPI) is untouched.
818
- */
819
- declare function withBus(tracker: Tracker, bus: FunnelBus): Tracker;
820
- declare global {
821
- interface Window {
822
- appfunnel?: AppFunnelBusApi;
823
- }
824
- }
825
- /**
826
- * Attach the bus's public surface to `window.appfunnel` (client-side only).
827
- * Returns a detach fn for unmount. No-op under SSR.
828
- */
829
- declare function attachBus(bus: FunnelBus): () => void;
830
-
831
892
  /**
832
893
  * The **event catalog** — the single source of truth for *which* events exist and
833
894
  * how each is routed (analytics vs. integrations vs. runtime-only). Pairs with the
@@ -1021,4 +1082,4 @@ interface ScriptProps {
1021
1082
  */
1022
1083
  declare function Script({ src, strategy, id, async, defer, nonce, attributes, onLoad, onError, }: ScriptProps): null;
1023
1084
 
1024
- export { Acquisition, type AppFunnelBusApi, AssetsContext, AssetsProvider, Back, type BackProps, type BusAccessors, type BusEvent, type CatalogEvent, CheckoutDriver, CheckoutDriverContext, Choice, type ChoiceOption, type ChoiceProps, type CreateFunnelTreeInput, Direction, EVENT_CATALOG, type EventMeta, type EventSource, type Formatters, type Funnel, type FunnelBus, FunnelContext, FunnelDefinition, type FunnelEventDataMap, FunnelProvider, type FunnelProviderProps, FunnelSnapshot, FunnelToaster, FunnelView, type KnownFunnelEvent, type MessageCatalog, Modal, type ModalHandler, type ModalHocProps, type ModalProps, type ModalState, type MountModule, type MountOpts, Namespace, type NavigationState, Next, type NextProps, PageContext, PageMeta, type PageModule, type PlatformTrackingConfig, type PluralCategory, Product, ProductInput, RuntimeExperiment, type RuntimePage, Script, type ScriptProps, Sheet, type SheetProps, type SheetSide, type TFunction, type Tracker, type Translation, VariableStore, attachBus, buildContext, createBus, createConsoleTracker, createFunnelStore, createFunnelTree, createPlatformTracker, defineModal, hideModal, integrationEvents, isIntegrationEvent, isTrackableEvent, newEventId, registerModal, removeModal, showModal, unregisterModal, useActiveLocale, useAsset, useAssets, useClickIds, useContextValue, useData, useDevice, useExperiment, useField, useFunnel, useLocale, useModal, useNavigation, usePage, useProduct, useProducts, useResponse, useSystem, useTracker, useTranslation, useUserAttribute, useUtm, withBus };
1085
+ export { Acquisition, type AppfunnelBusApi, AssetsContext, AssetsProvider, Back, type BackProps, type BusAccessors, type BusEvent, type CatalogEvent, CheckoutDriver, CheckoutDriverContext, Choice, type ChoiceOption, type ChoiceProps, type CreateFunnelTreeInput, Direction, EVENT_CATALOG, type EventMeta, type EventSource, type Formatters, type Funnel, type FunnelBus, FunnelContext, FunnelDefinition, type FunnelEventDataMap, FunnelProvider, type FunnelProviderProps, FunnelSnapshot, FunnelToaster, FunnelView, type KnownFunnelEvent, type MessageCatalog, Modal, type ModalHandler, type ModalHocProps, type ModalProps, type ModalState, type MountModule, type MountOpts, Namespace, type NavigationState, Next, type NextProps, Offering, OfferingInput, PageContext, PageMeta, type PageModule, type PlatformTrackingConfig, type PluralCategory, RuntimeExperiment, type RuntimePage, Script, type ScriptProps, Sheet, type SheetProps, type SheetSide, type TFunction, type Tracker, type Translation, VariableStore, attachBus, buildContext, createBus, createConsoleTracker, createFunnelStore, createFunnelTree, createPlatformTracker, defineModal, hideModal, integrationEvents, isIntegrationEvent, isTrackableEvent, newEventId, registerModal, removeModal, showModal, unregisterModal, useActiveLocale, useAsset, useAssets, useClickIds, useContextValue, useData, useDevice, useExperiment, useField, useFunnel, useLocale, useModal, useNavigation, useOffering, useOfferings, usePage, useResponse, useSystem, useTracker, useTranslation, useUserAttribute, useUtm, withBus };