@appfunnel-dev/sdk 2.0.0-canary.3 → 2.0.0-canary.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-AKO6XKXP.js → chunk-AXXG3ODP.js} +10 -3
- package/dist/chunk-AXXG3ODP.js.map +1 -0
- package/dist/{chunk-M6U3FNRW.js → chunk-CFFMZYPE.js} +3 -3
- package/dist/{chunk-M6U3FNRW.js.map → chunk-CFFMZYPE.js.map} +1 -1
- package/dist/chunk-EMMSS5I5.cjs +37 -0
- package/dist/chunk-EMMSS5I5.cjs.map +1 -0
- package/dist/chunk-G3PMV62Z.js +33 -0
- package/dist/chunk-G3PMV62Z.js.map +1 -0
- package/dist/{chunk-JSRKA375.cjs → chunk-G44BUG6G.cjs} +10 -3
- package/dist/chunk-G44BUG6G.cjs.map +1 -0
- package/dist/{chunk-LJYLGLFS.cjs → chunk-QMAZGLGV.cjs} +2 -35
- package/dist/chunk-QMAZGLGV.cjs.map +1 -0
- package/dist/{chunk-YY375F2B.js → chunk-TNLRQPVY.js} +3 -33
- package/dist/chunk-TNLRQPVY.js.map +1 -0
- package/dist/{chunk-CY4VBSMX.cjs → chunk-VAOR77NW.cjs} +7 -7
- package/dist/{chunk-CY4VBSMX.cjs.map → chunk-VAOR77NW.cjs.map} +1 -1
- package/dist/chunk-WYUDL4FI.cjs +8 -0
- package/dist/chunk-WYUDL4FI.cjs.map +1 -0
- package/dist/chunk-ZZJG4EYL.js +6 -0
- package/dist/chunk-ZZJG4EYL.js.map +1 -0
- package/dist/driver-paddle.cjs +23 -22
- package/dist/driver-paddle.cjs.map +1 -1
- package/dist/driver-paddle.js +3 -2
- package/dist/driver-paddle.js.map +1 -1
- package/dist/driver-stripe.cjs +37 -36
- package/dist/driver-stripe.cjs.map +1 -1
- package/dist/driver-stripe.js +3 -2
- package/dist/driver-stripe.js.map +1 -1
- package/dist/index.cjs +541 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +151 -75
- package/dist/index.d.ts +151 -75
- package/dist/index.js +484 -34
- package/dist/index.js.map +1 -1
- package/dist/{manifest-Cr2y1op6.d.cts → manifest-Dl7faaKe.d.cts} +34 -0
- package/dist/{manifest-Cr2y1op6.d.ts → manifest-Dl7faaKe.d.ts} +34 -0
- package/dist/manifest-entry.cjs +45 -30
- package/dist/manifest-entry.cjs.map +1 -1
- package/dist/manifest-entry.d.cts +1 -1
- package/dist/manifest-entry.d.ts +1 -1
- package/dist/manifest-entry.js +10 -3
- package/dist/manifest-entry.js.map +1 -1
- package/dist/protocol.cjs +13 -0
- package/dist/protocol.cjs.map +1 -0
- package/dist/protocol.d.cts +182 -0
- package/dist/protocol.d.ts +182 -0
- package/dist/protocol.js +4 -0
- package/dist/protocol.js.map +1 -0
- package/package.json +14 -2
- package/dist/chunk-AKO6XKXP.js.map +0 -1
- package/dist/chunk-JSRKA375.cjs.map +0 -1
- package/dist/chunk-LJYLGLFS.cjs.map +0 -1
- package/dist/chunk-YY375F2B.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
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-
|
|
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-
|
|
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-Dl7faaKe.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-Dl7faaKe.cjs';
|
|
5
|
+
import { NavSnapshot } from './protocol.cjs';
|
|
5
6
|
import { a as CheckoutDriver, C as CheckoutDriverContext } from './checkout-Dz8cGkB_.cjs';
|
|
6
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-Dz8cGkB_.cjs';
|
|
7
8
|
import { ToasterProps } from 'sonner';
|
|
@@ -113,8 +114,16 @@ interface FunnelEventDataMap {
|
|
|
113
114
|
priceId?: string;
|
|
114
115
|
productName?: string;
|
|
115
116
|
surface?: string;
|
|
117
|
+
amount?: number;
|
|
118
|
+
currency?: string;
|
|
119
|
+
eventId?: string;
|
|
120
|
+
};
|
|
121
|
+
'checkout.payment_added': {
|
|
122
|
+
productId?: string;
|
|
123
|
+
amount?: number;
|
|
124
|
+
currency?: string;
|
|
125
|
+
eventId?: string;
|
|
116
126
|
};
|
|
117
|
-
'checkout.payment_added': Record<string, never>;
|
|
118
127
|
'checkout.failed': {
|
|
119
128
|
category: string;
|
|
120
129
|
declineCode?: string;
|
|
@@ -126,6 +135,13 @@ interface FunnelEventDataMap {
|
|
|
126
135
|
currency?: string;
|
|
127
136
|
email?: string;
|
|
128
137
|
eventId?: string;
|
|
138
|
+
productId?: string;
|
|
139
|
+
};
|
|
140
|
+
'subscription.created': {
|
|
141
|
+
amount?: number;
|
|
142
|
+
currency?: string;
|
|
143
|
+
productId?: string;
|
|
144
|
+
eventId?: string;
|
|
129
145
|
};
|
|
130
146
|
'experiment.exposure': {
|
|
131
147
|
experimentId: string;
|
|
@@ -171,6 +187,99 @@ declare function useTracker(): {
|
|
|
171
187
|
track: Tracker['track'];
|
|
172
188
|
};
|
|
173
189
|
|
|
190
|
+
/**
|
|
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
|
+
|
|
174
283
|
/**
|
|
175
284
|
* v2 `products` — the **read-only** product catalog.
|
|
176
285
|
*
|
|
@@ -325,6 +434,15 @@ interface FunnelProviderProps {
|
|
|
325
434
|
config?: FunnelDefinition;
|
|
326
435
|
/** Server / session-restored values overriding declared defaults. */
|
|
327
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;
|
|
328
446
|
/** The platform tracker; defaults to a silent console tracker (tests/dev). */
|
|
329
447
|
tracker?: Tracker;
|
|
330
448
|
/** Computed-context seed (funnel id, test/live mode, server-hydrated utm/session). */
|
|
@@ -349,7 +467,7 @@ interface FunnelProviderProps {
|
|
|
349
467
|
* Mounts a funnel: builds the writable {@link VariableStore} + the read-only
|
|
350
468
|
* {@link FunnelContext} (once) and provides the runtime to the tree via hooks.
|
|
351
469
|
*/
|
|
352
|
-
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, products, experiments, checkout, messages, locale: localeOverride, toaster, store, children, }: FunnelProviderProps): ReactNode;
|
|
353
471
|
/**
|
|
354
472
|
* The imperative handle. Does **not** subscribe — use it in event handlers and
|
|
355
473
|
* effects (`useFunnel().responses.set({...})`). For reactive reads use the hooks.
|
|
@@ -462,9 +580,20 @@ declare function useExperiment(id: string): string | undefined;
|
|
|
462
580
|
*
|
|
463
581
|
* Must live inside `<FunnelProvider>`.
|
|
464
582
|
*/
|
|
465
|
-
declare function FunnelView({ pages, initialKey, layout, fallback, prefetch: prefetchMode, onNavigate, }: {
|
|
583
|
+
declare function FunnelView({ pages, initialKey, trustInitialKey, layout, fallback, prefetch: prefetchMode, onNavigate, }: {
|
|
466
584
|
pages: RuntimePage[];
|
|
467
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;
|
|
468
597
|
layout?: ComponentType<{
|
|
469
598
|
children: ReactNode;
|
|
470
599
|
}>;
|
|
@@ -552,6 +681,23 @@ interface MountOpts {
|
|
|
552
681
|
/** The server-resolved session id backing `sessionValues` — seeds the tracker (fallback)
|
|
553
682
|
* and `context.session.id`. */
|
|
554
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;
|
|
555
701
|
}
|
|
556
702
|
/**
|
|
557
703
|
* The shape a funnel build's `mount.tsx` module must export — the named,
|
|
@@ -743,76 +889,6 @@ type ModalProps = OverlayCommon;
|
|
|
743
889
|
/** A centered dialog. Auto-binds to its `defineModal` — `<Modal>…`. */
|
|
744
890
|
declare function Modal(props: ModalProps): ReactNode;
|
|
745
891
|
|
|
746
|
-
/**
|
|
747
|
-
* The **`window.appfunnel` event bus** — the client-side integration surface
|
|
748
|
-
* (port of v0 `admin/lib/funnel/utils/appfunnelRuntime.ts`).
|
|
749
|
-
*
|
|
750
|
-
* Every taxonomy event the runtime emits is mirrored here, so on-page pixels and
|
|
751
|
-
* custom scripts integrate by **subscribing** — `window.appfunnel.on('purchase.complete', …)`
|
|
752
|
-
* — instead of the funnel hand-injecting pixel config per page. The pre-built pixel
|
|
753
|
-
* loaders (Meta/TikTok/GTM/Clarity) are just subscribers; CAPI stays the server-side
|
|
754
|
-
* default (this is the *client* sink of the same events). See doc 08 §3.
|
|
755
|
-
*
|
|
756
|
-
* Two deliberate properties vs. v0:
|
|
757
|
-
* - **Replay history** (`events`): async pixels load *after* `funnel.start`/`page.view`
|
|
758
|
-
* fire, so a late subscriber would miss them. The bus keeps a bounded history a
|
|
759
|
-
* subscriber can replay (the GTM `dataLayer` trick) — no lost events.
|
|
760
|
-
* - **Subscribe + read only.** The window surface exposes `on`/`off`/`events` + read
|
|
761
|
-
* getters; it has **no `emit`** (third-party code can't forge events) and **no
|
|
762
|
-
* mutating actions** (`setVariable`/navigation) — the funnel can't be driven through
|
|
763
|
-
* it. (v0's full-control Custom-JS API is deferred pending the bus-control-scope
|
|
764
|
-
* decision; doc 08 §3 + §4.)
|
|
765
|
-
*/
|
|
766
|
-
|
|
767
|
-
/** Read accessors the bus exposes — wired to the funnel's store + context. */
|
|
768
|
-
interface BusAccessors {
|
|
769
|
-
getVariable: (key: string) => unknown;
|
|
770
|
-
getVariables: () => Record<string, unknown>;
|
|
771
|
-
getCurrentPageId: () => string | null;
|
|
772
|
-
getCustomerId: () => string | null;
|
|
773
|
-
getVisitorId: () => string | null;
|
|
774
|
-
}
|
|
775
|
-
interface BusEvent {
|
|
776
|
-
event: string;
|
|
777
|
-
data: unknown;
|
|
778
|
-
/** Epoch ms the event was emitted (for ordering / replay). */
|
|
779
|
-
ts: number;
|
|
780
|
-
}
|
|
781
|
-
type Listener = (data: unknown) => void;
|
|
782
|
-
/** The public `window.appfunnel` surface — subscribe + read, never emit or mutate. */
|
|
783
|
-
interface AppFunnelBusApi extends BusAccessors {
|
|
784
|
-
/** Subscribe to an event (or `'*'` for all). Returns an unsubscribe fn. */
|
|
785
|
-
on(event: string, cb: Listener): () => void;
|
|
786
|
-
off(event: string, cb: Listener): void;
|
|
787
|
-
/** Bounded replay history — read past events, then `on()` for future ones. */
|
|
788
|
-
readonly events: BusEvent[];
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* The internal bus: the public {@link AppFunnelBusApi} plus `emit` (SDK-only) and
|
|
792
|
-
* `now` injection. The SDK holds this; only {@link AppFunnelBusApi} reaches `window`.
|
|
793
|
-
*/
|
|
794
|
-
interface FunnelBus extends AppFunnelBusApi {
|
|
795
|
-
emit(event: string, data?: unknown): void;
|
|
796
|
-
}
|
|
797
|
-
/** Create a bus over the given read accessors. `now` is injectable for tests. */
|
|
798
|
-
declare function createBus(accessors: BusAccessors, now?: () => number): FunnelBus;
|
|
799
|
-
/**
|
|
800
|
-
* Wrap a {@link Tracker} so every emitted taxonomy event is mirrored to the bus
|
|
801
|
-
* (the client sink). `identify()` also surfaces a `user.registered` to the bus, so
|
|
802
|
-
* pixels see registration. The server sink (the inner tracker → CAPI) is untouched.
|
|
803
|
-
*/
|
|
804
|
-
declare function withBus(tracker: Tracker, bus: FunnelBus): Tracker;
|
|
805
|
-
declare global {
|
|
806
|
-
interface Window {
|
|
807
|
-
appfunnel?: AppFunnelBusApi;
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* Attach the bus's public surface to `window.appfunnel` (client-side only).
|
|
812
|
-
* Returns a detach fn for unmount. No-op under SSR.
|
|
813
|
-
*/
|
|
814
|
-
declare function attachBus(bus: FunnelBus): () => void;
|
|
815
|
-
|
|
816
892
|
/**
|
|
817
893
|
* The **event catalog** — the single source of truth for *which* events exist and
|
|
818
894
|
* how each is routed (analytics vs. integrations vs. runtime-only). Pairs with the
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
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-
|
|
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-
|
|
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-Dl7faaKe.js';
|
|
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-Dl7faaKe.js';
|
|
5
|
+
import { NavSnapshot } from './protocol.js';
|
|
5
6
|
import { a as CheckoutDriver, C as CheckoutDriverContext } from './checkout-7Dy6IedP.js';
|
|
6
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-7Dy6IedP.js';
|
|
7
8
|
import { ToasterProps } from 'sonner';
|
|
@@ -113,8 +114,16 @@ interface FunnelEventDataMap {
|
|
|
113
114
|
priceId?: string;
|
|
114
115
|
productName?: string;
|
|
115
116
|
surface?: string;
|
|
117
|
+
amount?: number;
|
|
118
|
+
currency?: string;
|
|
119
|
+
eventId?: string;
|
|
120
|
+
};
|
|
121
|
+
'checkout.payment_added': {
|
|
122
|
+
productId?: string;
|
|
123
|
+
amount?: number;
|
|
124
|
+
currency?: string;
|
|
125
|
+
eventId?: string;
|
|
116
126
|
};
|
|
117
|
-
'checkout.payment_added': Record<string, never>;
|
|
118
127
|
'checkout.failed': {
|
|
119
128
|
category: string;
|
|
120
129
|
declineCode?: string;
|
|
@@ -126,6 +135,13 @@ interface FunnelEventDataMap {
|
|
|
126
135
|
currency?: string;
|
|
127
136
|
email?: string;
|
|
128
137
|
eventId?: string;
|
|
138
|
+
productId?: string;
|
|
139
|
+
};
|
|
140
|
+
'subscription.created': {
|
|
141
|
+
amount?: number;
|
|
142
|
+
currency?: string;
|
|
143
|
+
productId?: string;
|
|
144
|
+
eventId?: string;
|
|
129
145
|
};
|
|
130
146
|
'experiment.exposure': {
|
|
131
147
|
experimentId: string;
|
|
@@ -171,6 +187,99 @@ declare function useTracker(): {
|
|
|
171
187
|
track: Tracker['track'];
|
|
172
188
|
};
|
|
173
189
|
|
|
190
|
+
/**
|
|
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
|
+
|
|
174
283
|
/**
|
|
175
284
|
* v2 `products` — the **read-only** product catalog.
|
|
176
285
|
*
|
|
@@ -325,6 +434,15 @@ interface FunnelProviderProps {
|
|
|
325
434
|
config?: FunnelDefinition;
|
|
326
435
|
/** Server / session-restored values overriding declared defaults. */
|
|
327
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;
|
|
328
446
|
/** The platform tracker; defaults to a silent console tracker (tests/dev). */
|
|
329
447
|
tracker?: Tracker;
|
|
330
448
|
/** Computed-context seed (funnel id, test/live mode, server-hydrated utm/session). */
|
|
@@ -349,7 +467,7 @@ interface FunnelProviderProps {
|
|
|
349
467
|
* Mounts a funnel: builds the writable {@link VariableStore} + the read-only
|
|
350
468
|
* {@link FunnelContext} (once) and provides the runtime to the tree via hooks.
|
|
351
469
|
*/
|
|
352
|
-
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, products, experiments, checkout, messages, locale: localeOverride, toaster, store, children, }: FunnelProviderProps): ReactNode;
|
|
353
471
|
/**
|
|
354
472
|
* The imperative handle. Does **not** subscribe — use it in event handlers and
|
|
355
473
|
* effects (`useFunnel().responses.set({...})`). For reactive reads use the hooks.
|
|
@@ -462,9 +580,20 @@ declare function useExperiment(id: string): string | undefined;
|
|
|
462
580
|
*
|
|
463
581
|
* Must live inside `<FunnelProvider>`.
|
|
464
582
|
*/
|
|
465
|
-
declare function FunnelView({ pages, initialKey, layout, fallback, prefetch: prefetchMode, onNavigate, }: {
|
|
583
|
+
declare function FunnelView({ pages, initialKey, trustInitialKey, layout, fallback, prefetch: prefetchMode, onNavigate, }: {
|
|
466
584
|
pages: RuntimePage[];
|
|
467
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;
|
|
468
597
|
layout?: ComponentType<{
|
|
469
598
|
children: ReactNode;
|
|
470
599
|
}>;
|
|
@@ -552,6 +681,23 @@ interface MountOpts {
|
|
|
552
681
|
/** The server-resolved session id backing `sessionValues` — seeds the tracker (fallback)
|
|
553
682
|
* and `context.session.id`. */
|
|
554
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;
|
|
555
701
|
}
|
|
556
702
|
/**
|
|
557
703
|
* The shape a funnel build's `mount.tsx` module must export — the named,
|
|
@@ -743,76 +889,6 @@ type ModalProps = OverlayCommon;
|
|
|
743
889
|
/** A centered dialog. Auto-binds to its `defineModal` — `<Modal>…`. */
|
|
744
890
|
declare function Modal(props: ModalProps): ReactNode;
|
|
745
891
|
|
|
746
|
-
/**
|
|
747
|
-
* The **`window.appfunnel` event bus** — the client-side integration surface
|
|
748
|
-
* (port of v0 `admin/lib/funnel/utils/appfunnelRuntime.ts`).
|
|
749
|
-
*
|
|
750
|
-
* Every taxonomy event the runtime emits is mirrored here, so on-page pixels and
|
|
751
|
-
* custom scripts integrate by **subscribing** — `window.appfunnel.on('purchase.complete', …)`
|
|
752
|
-
* — instead of the funnel hand-injecting pixel config per page. The pre-built pixel
|
|
753
|
-
* loaders (Meta/TikTok/GTM/Clarity) are just subscribers; CAPI stays the server-side
|
|
754
|
-
* default (this is the *client* sink of the same events). See doc 08 §3.
|
|
755
|
-
*
|
|
756
|
-
* Two deliberate properties vs. v0:
|
|
757
|
-
* - **Replay history** (`events`): async pixels load *after* `funnel.start`/`page.view`
|
|
758
|
-
* fire, so a late subscriber would miss them. The bus keeps a bounded history a
|
|
759
|
-
* subscriber can replay (the GTM `dataLayer` trick) — no lost events.
|
|
760
|
-
* - **Subscribe + read only.** The window surface exposes `on`/`off`/`events` + read
|
|
761
|
-
* getters; it has **no `emit`** (third-party code can't forge events) and **no
|
|
762
|
-
* mutating actions** (`setVariable`/navigation) — the funnel can't be driven through
|
|
763
|
-
* it. (v0's full-control Custom-JS API is deferred pending the bus-control-scope
|
|
764
|
-
* decision; doc 08 §3 + §4.)
|
|
765
|
-
*/
|
|
766
|
-
|
|
767
|
-
/** Read accessors the bus exposes — wired to the funnel's store + context. */
|
|
768
|
-
interface BusAccessors {
|
|
769
|
-
getVariable: (key: string) => unknown;
|
|
770
|
-
getVariables: () => Record<string, unknown>;
|
|
771
|
-
getCurrentPageId: () => string | null;
|
|
772
|
-
getCustomerId: () => string | null;
|
|
773
|
-
getVisitorId: () => string | null;
|
|
774
|
-
}
|
|
775
|
-
interface BusEvent {
|
|
776
|
-
event: string;
|
|
777
|
-
data: unknown;
|
|
778
|
-
/** Epoch ms the event was emitted (for ordering / replay). */
|
|
779
|
-
ts: number;
|
|
780
|
-
}
|
|
781
|
-
type Listener = (data: unknown) => void;
|
|
782
|
-
/** The public `window.appfunnel` surface — subscribe + read, never emit or mutate. */
|
|
783
|
-
interface AppFunnelBusApi extends BusAccessors {
|
|
784
|
-
/** Subscribe to an event (or `'*'` for all). Returns an unsubscribe fn. */
|
|
785
|
-
on(event: string, cb: Listener): () => void;
|
|
786
|
-
off(event: string, cb: Listener): void;
|
|
787
|
-
/** Bounded replay history — read past events, then `on()` for future ones. */
|
|
788
|
-
readonly events: BusEvent[];
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* The internal bus: the public {@link AppFunnelBusApi} plus `emit` (SDK-only) and
|
|
792
|
-
* `now` injection. The SDK holds this; only {@link AppFunnelBusApi} reaches `window`.
|
|
793
|
-
*/
|
|
794
|
-
interface FunnelBus extends AppFunnelBusApi {
|
|
795
|
-
emit(event: string, data?: unknown): void;
|
|
796
|
-
}
|
|
797
|
-
/** Create a bus over the given read accessors. `now` is injectable for tests. */
|
|
798
|
-
declare function createBus(accessors: BusAccessors, now?: () => number): FunnelBus;
|
|
799
|
-
/**
|
|
800
|
-
* Wrap a {@link Tracker} so every emitted taxonomy event is mirrored to the bus
|
|
801
|
-
* (the client sink). `identify()` also surfaces a `user.registered` to the bus, so
|
|
802
|
-
* pixels see registration. The server sink (the inner tracker → CAPI) is untouched.
|
|
803
|
-
*/
|
|
804
|
-
declare function withBus(tracker: Tracker, bus: FunnelBus): Tracker;
|
|
805
|
-
declare global {
|
|
806
|
-
interface Window {
|
|
807
|
-
appfunnel?: AppFunnelBusApi;
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* Attach the bus's public surface to `window.appfunnel` (client-side only).
|
|
812
|
-
* Returns a detach fn for unmount. No-op under SSR.
|
|
813
|
-
*/
|
|
814
|
-
declare function attachBus(bus: FunnelBus): () => void;
|
|
815
|
-
|
|
816
892
|
/**
|
|
817
893
|
* The **event catalog** — the single source of truth for *which* events exist and
|
|
818
894
|
* how each is routed (analytics vs. integrations vs. runtime-only). Pairs with the
|