@appfunnel-dev/sdk 2.0.0-canary.1 → 2.0.0-canary.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/capabilities-Dq22RCr_.d.cts +180 -0
- package/dist/capabilities-Dq22RCr_.d.ts +180 -0
- package/dist/checkout-ClaO5IYV.d.ts +333 -0
- package/dist/checkout-DBp4bCpC.d.cts +333 -0
- package/dist/chunk-7JLOF6CJ.cjs +172 -0
- package/dist/chunk-7JLOF6CJ.cjs.map +1 -0
- 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-JAO6AA4R.js +99 -0
- package/dist/chunk-JAO6AA4R.js.map +1 -0
- package/dist/chunk-OXQBEKZ5.js +157 -0
- package/dist/chunk-OXQBEKZ5.js.map +1 -0
- package/dist/chunk-VV7TFC64.cjs +106 -0
- package/dist/chunk-VV7TFC64.cjs.map +1 -0
- package/dist/chunk-VW2HVPR4.js +446 -0
- package/dist/chunk-VW2HVPR4.js.map +1 -0
- package/dist/chunk-WYUDL4FI.cjs +8 -0
- package/dist/chunk-WYUDL4FI.cjs.map +1 -0
- package/dist/chunk-Y4YNJ2EX.cjs +476 -0
- package/dist/chunk-Y4YNJ2EX.cjs.map +1 -0
- package/dist/chunk-ZZJG4EYL.js +6 -0
- package/dist/chunk-ZZJG4EYL.js.map +1 -0
- package/dist/driver-paddle.cjs +817 -0
- package/dist/driver-paddle.cjs.map +1 -0
- package/dist/driver-paddle.d.cts +10 -0
- package/dist/driver-paddle.d.ts +10 -0
- package/dist/driver-paddle.js +814 -0
- package/dist/driver-paddle.js.map +1 -0
- package/dist/driver-stripe.cjs +2312 -0
- package/dist/driver-stripe.cjs.map +1 -0
- package/dist/driver-stripe.d.cts +24 -0
- package/dist/driver-stripe.d.ts +24 -0
- package/dist/driver-stripe.js +2305 -0
- package/dist/driver-stripe.js.map +1 -0
- package/dist/index.cjs +2650 -841
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +343 -1005
- package/dist/index.d.ts +343 -1005
- package/dist/index.js +2381 -701
- package/dist/index.js.map +1 -1
- package/dist/manifest-B3Tdab0M.d.cts +920 -0
- package/dist/manifest-B3Tdab0M.d.ts +920 -0
- package/dist/manifest-entry.cjs +408 -0
- package/dist/manifest-entry.cjs.map +1 -0
- package/dist/manifest-entry.d.cts +192 -0
- package/dist/manifest-entry.d.ts +192 -0
- package/dist/manifest-entry.js +270 -0
- package/dist/manifest-entry.js.map +1 -0
- 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 +49 -4
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { af as CheckoutUsageScan } from './manifest-B3Tdab0M.js';
|
|
2
|
+
export { h as CompileInput, i as Condition, j as ConditionOp, E as EdgeCondition, l as ExperimentArm, n as FlowPage, c as FunnelDefinition, o as FunnelLocales, p as FunnelManifest, ag as FunnelPage, b as FunnelSnapshot, G as Gate, q as Interval, M as ManifestEdge, r as ManifestPage, s as ManifestValidation, t as Money, O as Offering, e as OfferingInput, ah as OfferingRefScan, ai as OfferingSlot, P as PageMeta, v as PageType, w as Predicate, x as ResolvedOffering, y as Route, R as RuntimeExperiment, H as assignVariant, J as bucketingSeed, N as buildCatalog, Q as compileManifest, T as currencyExponent, W as defineFunnel, Y as entryGuard, Z as evaluateCondition, a1 as formatMoney, a2 as formatOffering, aj as funnelCatalogKeys, a4 as isRtl, a5 as isVariantKey, ak as normalizeOfferings, a8 as pageMeta, a9 as parseSlotKey, ac as resolveLocale, ad as resolveOffering, al as scanOfferingRefs, am as weightsOf } from './manifest-B3Tdab0M.js';
|
|
3
|
+
import { C as CheckoutError, b as CheckoutIntent, U as UpsellKind, d as CheckoutSurface } from './capabilities-Dq22RCr_.js';
|
|
4
|
+
export { a as CheckoutErrorCategory, c as CheckoutProvider, D as DeviceClass, I as INLINE_SURFACES, O as OffSessionReliability, P as PROVIDER_PROFILES, e as ProviderProfile, S as SurfaceCapability, f as SurfacePreference, T as TrialShape, V as ValidationResult, g as checkoutError, i as isInlineSurface, h as isMerchantOfRecord, j as isOrchestrator, k as isRenderableSurface, r as renderableSurfaces, l as resolveSurface, s as surfacesFor, t as trialShapesFor, v as validateCheckout, n as validateTrial, m as validateUpsell } from './capabilities-Dq22RCr_.js';
|
|
5
|
+
import 'react';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Source generators for the funnel build "machinery" authors no longer write. `funnel.ts` (the
|
|
9
|
+
* spine: id, responses, offerings, checkout, and the page list + declarative routing) plus the
|
|
10
|
+
* `pages/` folder are the ONLY authored source. The build (renderer/builder) and the CLI dev server
|
|
11
|
+
* call these to synthesize:
|
|
12
|
+
* - `mount.tsx` — code-split page loaders + `createFunnelTree`
|
|
13
|
+
* - `entry.client.tsx` — hydrate the SSR'd markup from injected data
|
|
14
|
+
* - `entry.server.tsx` — `ssr()` + the compiled `manifest`
|
|
15
|
+
*
|
|
16
|
+
* All string templates, zero runtime deps — this module lives in the pure `@appfunnel-dev/sdk/
|
|
17
|
+
* manifest` entry so tooling imports it without the funnel runtime.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
interface ScaffoldOptions {
|
|
21
|
+
/** Page component keys = `pages/<key>.tsx` filenames (base flow pages AND `@variant` pages). */
|
|
22
|
+
pageKeys: string[];
|
|
23
|
+
/** Import prefix to reach the funnel root: `'./'` at the root (build), `'../../'` in a subdir (CLI dev). */
|
|
24
|
+
importPrefix?: string;
|
|
25
|
+
/** Whether a `layout.tsx` exists to wrap the pages. */
|
|
26
|
+
hasLayout?: boolean;
|
|
27
|
+
/** Whether a `styles.css` exists (client entry imports it). */
|
|
28
|
+
hasStyles?: boolean;
|
|
29
|
+
/** Checkout provider from `config.checkout` — wires the driver; omit for none. */
|
|
30
|
+
checkout?: 'stripe' | 'paddle' | null;
|
|
31
|
+
/** Locales with a `messages/<locale>.json` catalog to bake into the mount (so `t()` resolves at render). */
|
|
32
|
+
messageLocales?: string[];
|
|
33
|
+
/** Slot names the pages reference (from `scanOfferingRefs`), baked into the manifest for validation. */
|
|
34
|
+
offeringRefs?: string[];
|
|
35
|
+
/** Checkout-surface usage the pages express (from `scanOfferingRefs(...).checkout`), baked into the manifest for the publish capability gate. */
|
|
36
|
+
checkoutRefs?: CheckoutUsageScan;
|
|
37
|
+
}
|
|
38
|
+
/** `mount.tsx` — the code-split loaders + tree, derived from `config.pages` and the `pages/` files. */
|
|
39
|
+
declare function generateMount(opts: ScaffoldOptions): string;
|
|
40
|
+
/** `entry.client.tsx` — hydrate the SSR'd markup from the injected `#__af_data`. */
|
|
41
|
+
declare function generateEntryClient(opts: Pick<ScaffoldOptions, 'importPrefix' | 'hasStyles'>): string;
|
|
42
|
+
/** `entry.server.tsx` — `ssr(opts)` HTML + the compiled `manifest` (read by the build). */
|
|
43
|
+
declare function generateEntryServer(opts: Pick<ScaffoldOptions, 'importPrefix' | 'offeringRefs' | 'checkoutRefs'>): string;
|
|
44
|
+
/** Client-only dev entry (createRoot, no SSR) — for the CLI `dev` server. */
|
|
45
|
+
declare function generateDevEntry(opts: Pick<ScaffoldOptions, 'importPrefix' | 'hasStyles'> & {
|
|
46
|
+
data: unknown;
|
|
47
|
+
}): string;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The v2 checkout WIRE CONTRACT — the request/response types shared by the server
|
|
51
|
+
* services (apps/api, fresh per-flow checkout — v1 purchase.ts is frozen) and the
|
|
52
|
+
* client drivers (driver-stripe/driver-paddle). Both sides import THESE types from
|
|
53
|
+
* the pure manifest entry, so the contract cannot drift between them.
|
|
54
|
+
*
|
|
55
|
+
* Future-proofing rules (Wave-4, Simon 2026-07-02):
|
|
56
|
+
* - Responses discriminate on `step` (what the CLIENT must do next), NOT on the
|
|
57
|
+
* provider — a new provider reuses an existing step (`redirect`,
|
|
58
|
+
* `provider_checkout`) or adds one, without breaking existing drivers.
|
|
59
|
+
* - The server NEVER returns secret credentials — publishable/client tokens only;
|
|
60
|
+
* charges and verification happen server-side against the mode-resolved store.
|
|
61
|
+
* - `correlationId` is the client-minted idempotency key AND the correlation id
|
|
62
|
+
* the server writes into every provider object's metadata — one id links the
|
|
63
|
+
* trial fee to its subscription, the webhook to the funnel session, and the
|
|
64
|
+
* retry to the original attempt (kills v0's 5-minute-webhook-window linking).
|
|
65
|
+
* - Money facts settle SERVER-side: the browser only ever learns the outcome
|
|
66
|
+
* (`CheckoutSettlement`, with the server-minted `eventId` the pixel dedupes on).
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
/** `POST {apiBase}/campaign/{campaignId}/v2/checkout` — open a checkout attempt. */
|
|
70
|
+
interface CheckoutSessionRequest {
|
|
71
|
+
funnelId: string;
|
|
72
|
+
/** Render-time mode (doc 09 §2.4): resolves the product's binding + store credentials. */
|
|
73
|
+
mode: 'test' | 'live';
|
|
74
|
+
/** Logical catalog product key (`monthly`) — never a raw price id. */
|
|
75
|
+
productKey: string;
|
|
76
|
+
intent: CheckoutIntent;
|
|
77
|
+
/** For `upsell` — which off-session kind (validated against the capability matrix). */
|
|
78
|
+
kind?: UpsellKind;
|
|
79
|
+
/** The presenting surface; null/absent = off-session (upsell, no UI). */
|
|
80
|
+
surface?: CheckoutSurface | null;
|
|
81
|
+
/** Funnel session id (analytics + customer continuity), when one exists. */
|
|
82
|
+
sessionId?: string;
|
|
83
|
+
/** Signed-cookie-verified bare visitor id, when known. */
|
|
84
|
+
visitorId?: string;
|
|
85
|
+
/** Buyer email when the funnel collected it (Stripe Customer creation / receipts). */
|
|
86
|
+
email?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Client-minted UUID. Idempotency key for this attempt (safe re-POST on network
|
|
89
|
+
* retry) and the correlation id stamped into provider-object metadata.
|
|
90
|
+
*/
|
|
91
|
+
correlationId: string;
|
|
92
|
+
}
|
|
93
|
+
/** What the browser learned when the server settled the charge (or verified it). */
|
|
94
|
+
interface CheckoutSettlement {
|
|
95
|
+
ok: true;
|
|
96
|
+
/** Server-minted dedup id — the SDK emits `purchase.complete` with it (browser↔CAPI dedup). */
|
|
97
|
+
eventId: string;
|
|
98
|
+
amountMinor: number;
|
|
99
|
+
currency: string;
|
|
100
|
+
productKey: string;
|
|
101
|
+
}
|
|
102
|
+
/** Driver confirms in the browser with the provider's JS (Stripe Elements et al.). */
|
|
103
|
+
interface StepClientConfirm {
|
|
104
|
+
step: 'client_confirm';
|
|
105
|
+
provider: string;
|
|
106
|
+
publishableKey: string;
|
|
107
|
+
clientSecret: string;
|
|
108
|
+
/** Which secret this is — payment (charge now) vs setup (vault, e.g. free-trial start). */
|
|
109
|
+
secretKind: 'payment_intent' | 'setup_intent';
|
|
110
|
+
}
|
|
111
|
+
/** Driver mounts the provider-MANAGED inline checkout (Stripe Embedded). */
|
|
112
|
+
interface StepEmbedded {
|
|
113
|
+
step: 'embedded';
|
|
114
|
+
provider: string;
|
|
115
|
+
publishableKey: string;
|
|
116
|
+
clientSecret: string;
|
|
117
|
+
}
|
|
118
|
+
/** Driver navigates to the provider-hosted page (any provider's `redirect`). */
|
|
119
|
+
interface StepRedirect {
|
|
120
|
+
step: 'redirect';
|
|
121
|
+
provider: string;
|
|
122
|
+
url: string;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Driver opens the provider's own checkout UI (Paddle.js popup/inline, Whop later).
|
|
126
|
+
*
|
|
127
|
+
* WHAT the checkout opens is EXACTLY ONE of `items` | `transactionId`:
|
|
128
|
+
* - `items` — open a NEW checkout for these provider-native prices (the purchase shape;
|
|
129
|
+
* every response before 2026-07 carried this, so `items`-only stays valid unchanged);
|
|
130
|
+
* - `transactionId` — open the provider's checkout for an EXISTING server-created
|
|
131
|
+
* transaction (e.g. Paddle's payment-method-change transaction: the on-session
|
|
132
|
+
* re-collect leg of a failed off-session upgrade — `Checkout.open({ transactionId })`).
|
|
133
|
+
* A response carrying both or neither is malformed and the driver rejects it.
|
|
134
|
+
*/
|
|
135
|
+
interface StepProviderCheckout {
|
|
136
|
+
step: 'provider_checkout';
|
|
137
|
+
provider: string;
|
|
138
|
+
/** Provider client token / publishable credential for its JS SDK. */
|
|
139
|
+
clientToken: string;
|
|
140
|
+
/** Provider environment the client SDK must target (Paddle sandbox vs production). */
|
|
141
|
+
providerEnvironment: 'sandbox' | 'production';
|
|
142
|
+
/** Provider-native line items (e.g. Paddle price ids), resolved from the binding. */
|
|
143
|
+
items?: {
|
|
144
|
+
priceId: string;
|
|
145
|
+
quantity: number;
|
|
146
|
+
}[];
|
|
147
|
+
/** Provider-side id of an EXISTING transaction to open (see the exactly-one rule above). */
|
|
148
|
+
transactionId?: string;
|
|
149
|
+
/**
|
|
150
|
+
* Echoed into the provider checkout's custom data (carries the correlationId) — `items`
|
|
151
|
+
* path only. A `transactionId` checkout already exists provider-side with its own custom
|
|
152
|
+
* data (Paddle: inherited from the subscription), so the driver does not send this.
|
|
153
|
+
*/
|
|
154
|
+
customData: Record<string, string>;
|
|
155
|
+
}
|
|
156
|
+
/** The server completed the charge synchronously (off-session upsell). */
|
|
157
|
+
interface StepSettled {
|
|
158
|
+
step: 'settled';
|
|
159
|
+
result: CheckoutSettlement;
|
|
160
|
+
}
|
|
161
|
+
/** The attempt failed — normalized taxonomy, never a raw provider error. */
|
|
162
|
+
interface StepFailed {
|
|
163
|
+
step: 'failed';
|
|
164
|
+
error: CheckoutError;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* The provider hasn't confirmed yet — the charge is in flight and will settle
|
|
168
|
+
* async (webhook). The driver polls `/complete` until `settled`/`failed`. On the
|
|
169
|
+
* OPEN response this is the Paddle off-session tail (its confirmation can outlast
|
|
170
|
+
* the request); money stays truthful either way — the attempt is PENDING
|
|
171
|
+
* server-side and the webhook/poll converge on one outcome.
|
|
172
|
+
*/
|
|
173
|
+
interface StepPending {
|
|
174
|
+
step: 'pending';
|
|
175
|
+
}
|
|
176
|
+
type CheckoutSessionResponse = StepClientConfirm | StepEmbedded | StepRedirect | StepProviderCheckout | StepSettled | StepPending | StepFailed;
|
|
177
|
+
/**
|
|
178
|
+
* `POST {apiBase}/campaign/{campaignId}/v2/checkout/{correlationId}/complete` —
|
|
179
|
+
* after a client-side confirm/provider-checkout resolves, the browser asks the
|
|
180
|
+
* server to VERIFY with the provider and write the money facts. The server is the
|
|
181
|
+
* source of truth: it re-fetches the provider object; the browser's claim alone
|
|
182
|
+
* never creates a fact. Idempotent per correlationId (a re-POST returns the same
|
|
183
|
+
* settlement). MoR/async providers (Paddle) may verify via webhook before this
|
|
184
|
+
* call arrives — then it simply returns the already-written settlement.
|
|
185
|
+
*/
|
|
186
|
+
interface CheckoutCompleteRequest {
|
|
187
|
+
/** Provider-side handle the client got back, when it has one (e.g. Stripe intent id). */
|
|
188
|
+
providerRef?: string;
|
|
189
|
+
}
|
|
190
|
+
type CheckoutCompleteResponse = StepSettled | StepPending | StepFailed;
|
|
191
|
+
|
|
192
|
+
export { type CheckoutCompleteRequest, type CheckoutCompleteResponse, CheckoutError, CheckoutIntent, type CheckoutSessionRequest, type CheckoutSessionResponse, type CheckoutSettlement, CheckoutSurface, CheckoutUsageScan, type ScaffoldOptions, type StepClientConfirm, type StepEmbedded, type StepFailed, type StepPending, type StepProviderCheckout, type StepRedirect, type StepSettled, UpsellKind, generateDevEntry, generateEntryClient, generateEntryServer, generateMount };
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
export { assignVariant, bucketingSeed, buildCatalog, compileManifest, currencyExponent, defineFunnel, entryGuard, evaluateCondition, formatMoney, formatOffering, funnelCatalogKeys, isRtl, isVariantKey, normalizeOfferings, pageMeta, parseSlotKey, resolveLocale, resolveOffering, weightsOf } from './chunk-VW2HVPR4.js';
|
|
2
|
+
export { INLINE_SURFACES, PROVIDER_PROFILES, checkoutError, isInlineSurface, isMerchantOfRecord, isOrchestrator, isRenderableSurface, renderableSurfaces, resolveSurface, surfacesFor, trialShapesFor, validateCheckout, validateTrial, validateUpsell } from './chunk-OXQBEKZ5.js';
|
|
3
|
+
import './chunk-G3PMV62Z.js';
|
|
4
|
+
|
|
5
|
+
// src/manifest/offering-refs.ts
|
|
6
|
+
function mask(src) {
|
|
7
|
+
const out = src.split("");
|
|
8
|
+
const n = src.length;
|
|
9
|
+
let i = 0;
|
|
10
|
+
while (i < n) {
|
|
11
|
+
const c = src[i];
|
|
12
|
+
const d = src[i + 1];
|
|
13
|
+
if (c === "/" && d === "/") {
|
|
14
|
+
while (i < n && src[i] !== "\n") {
|
|
15
|
+
out[i] = " ";
|
|
16
|
+
i++;
|
|
17
|
+
}
|
|
18
|
+
} else if (c === "/" && d === "*") {
|
|
19
|
+
out[i] = " ";
|
|
20
|
+
out[i + 1] = " ";
|
|
21
|
+
i += 2;
|
|
22
|
+
while (i < n) {
|
|
23
|
+
if (src[i] === "*" && src[i + 1] === "/") {
|
|
24
|
+
out[i] = " ";
|
|
25
|
+
out[i + 1] = " ";
|
|
26
|
+
i += 2;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
if (src[i] !== "\n") out[i] = " ";
|
|
30
|
+
i++;
|
|
31
|
+
}
|
|
32
|
+
} else if (c === "'" || c === '"' || c === "`") {
|
|
33
|
+
i++;
|
|
34
|
+
while (i < n && src[i] !== c) {
|
|
35
|
+
if (src[i] === "\\") {
|
|
36
|
+
out[i] = " ";
|
|
37
|
+
i++;
|
|
38
|
+
if (i < n && src[i] !== "\n") out[i] = " ";
|
|
39
|
+
i++;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (src[i] !== "\n") out[i] = " ";
|
|
43
|
+
i++;
|
|
44
|
+
}
|
|
45
|
+
i++;
|
|
46
|
+
} else i++;
|
|
47
|
+
}
|
|
48
|
+
return out.join("");
|
|
49
|
+
}
|
|
50
|
+
function literalAt(src, masked, open) {
|
|
51
|
+
const q = masked[open];
|
|
52
|
+
if (q !== "'" && q !== '"' && q !== "`") return null;
|
|
53
|
+
const close = masked.indexOf(q, open + 1);
|
|
54
|
+
if (close === -1) return null;
|
|
55
|
+
return src.slice(open + 1, close);
|
|
56
|
+
}
|
|
57
|
+
function skipWs(masked, i) {
|
|
58
|
+
while (i < masked.length && (masked[i] === " " || masked[i] === " " || masked[i] === "\n" || masked[i] === "\r")) i++;
|
|
59
|
+
return i;
|
|
60
|
+
}
|
|
61
|
+
function tagAttrEnd(masked, from) {
|
|
62
|
+
let depth = 0;
|
|
63
|
+
for (let i = from; i < masked.length; i++) {
|
|
64
|
+
const c = masked[i];
|
|
65
|
+
if (c === "{") depth++;
|
|
66
|
+
else if (c === "}") {
|
|
67
|
+
if (depth > 0) depth--;
|
|
68
|
+
} else if (c === ">" && depth === 0) return i;
|
|
69
|
+
}
|
|
70
|
+
return masked.length;
|
|
71
|
+
}
|
|
72
|
+
function readAttr(src, masked, start, end, name) {
|
|
73
|
+
const re = new RegExp(`\\b${name}\\s*=\\s*`, "g");
|
|
74
|
+
re.lastIndex = start;
|
|
75
|
+
const m = re.exec(masked);
|
|
76
|
+
if (!m || m.index >= end) return null;
|
|
77
|
+
const at = m.index + m[0].length;
|
|
78
|
+
if (masked[at] === "{") return { dynamic: true };
|
|
79
|
+
const lit = literalAt(src, masked, at);
|
|
80
|
+
return lit !== null ? { value: lit } : { dynamic: true };
|
|
81
|
+
}
|
|
82
|
+
function scanOfferingRefs(files) {
|
|
83
|
+
const seen = /* @__PURE__ */ new Set();
|
|
84
|
+
const order = [];
|
|
85
|
+
let dynamic = false;
|
|
86
|
+
const add = (v) => {
|
|
87
|
+
if (v && !seen.has(v)) {
|
|
88
|
+
seen.add(v);
|
|
89
|
+
order.push(v);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const surfaceSeen = /* @__PURE__ */ new Set();
|
|
93
|
+
const surfaces = [];
|
|
94
|
+
const kindSeen = /* @__PURE__ */ new Set();
|
|
95
|
+
const upsellKinds = [];
|
|
96
|
+
let checkoutDynamic = false;
|
|
97
|
+
const addSurface = (v) => {
|
|
98
|
+
if (v && !surfaceSeen.has(v)) {
|
|
99
|
+
surfaceSeen.add(v);
|
|
100
|
+
surfaces.push(v);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const addKind = (v) => {
|
|
104
|
+
if (v && !kindSeen.has(v)) {
|
|
105
|
+
kindSeen.add(v);
|
|
106
|
+
upsellKinds.push(v);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
for (const [path, source] of Object.entries(files)) {
|
|
110
|
+
if (!/\.(tsx|jsx|ts|js)$/.test(path)) continue;
|
|
111
|
+
const masked = mask(source);
|
|
112
|
+
for (const m of masked.matchAll(/\buseOfferings?\s*\(/g)) {
|
|
113
|
+
const plural = source.slice(m.index, m.index + 12) === "useOfferings";
|
|
114
|
+
const j = skipWs(masked, m.index + m[0].length);
|
|
115
|
+
if (masked[j] === ")") {
|
|
116
|
+
if (plural) dynamic = true;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const lit = literalAt(source, masked, j);
|
|
120
|
+
if (lit !== null) add(lit);
|
|
121
|
+
else dynamic = true;
|
|
122
|
+
}
|
|
123
|
+
for (const m of masked.matchAll(/<(?:Checkout|Upsell)\b[^>]*?\boffering\s*=\s*/g)) {
|
|
124
|
+
const j = m.index + m[0].length;
|
|
125
|
+
const lit = literalAt(source, masked, j);
|
|
126
|
+
if (lit !== null) add(lit);
|
|
127
|
+
else if (masked[j] === "{") dynamic = true;
|
|
128
|
+
}
|
|
129
|
+
for (const m of masked.matchAll(/<(Checkout|Upsell)\b/g)) {
|
|
130
|
+
const comp = m[1];
|
|
131
|
+
const tagStart = m.index + m[0].length;
|
|
132
|
+
const tagEnd = tagAttrEnd(masked, tagStart);
|
|
133
|
+
const attr = (name) => readAttr(source, masked, tagStart, tagEnd, name);
|
|
134
|
+
const fallback = attr("fallback");
|
|
135
|
+
if (fallback?.value) addSurface(fallback.value);
|
|
136
|
+
else if (fallback?.dynamic) checkoutDynamic = true;
|
|
137
|
+
const recovery = attr("recoverySurface");
|
|
138
|
+
if (recovery?.value) addSurface(recovery.value);
|
|
139
|
+
else if (recovery?.dynamic) checkoutDynamic = true;
|
|
140
|
+
if (comp === "Checkout") {
|
|
141
|
+
const surface = attr("surface");
|
|
142
|
+
if (surface?.value) addSurface(surface.value);
|
|
143
|
+
else if (surface?.dynamic) checkoutDynamic = true;
|
|
144
|
+
} else {
|
|
145
|
+
const kind = attr("kind");
|
|
146
|
+
if (kind?.value) addKind(kind.value);
|
|
147
|
+
else if (kind?.dynamic) checkoutDynamic = true;
|
|
148
|
+
else addKind("subscription");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { slots: order, dynamic, checkout: { surfaces, upsellKinds, dynamic: checkoutDynamic } };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/flow/scaffold.ts
|
|
156
|
+
function msgId(locale) {
|
|
157
|
+
return "msg_" + locale.replace(/[^a-zA-Z0-9]/g, "_");
|
|
158
|
+
}
|
|
159
|
+
var DRIVERS = {
|
|
160
|
+
stripe: { name: "stripeCheckoutDriver", sub: "driver-stripe" },
|
|
161
|
+
paddle: { name: "paddleCheckoutDriver", sub: "driver-paddle" }
|
|
162
|
+
};
|
|
163
|
+
var HEADER = "// GENERATED by Appfunnel \u2014 do not edit. Built from funnel.ts + the pages/ folder.\n";
|
|
164
|
+
function generateMount(opts) {
|
|
165
|
+
const p = opts.importPrefix ?? "./";
|
|
166
|
+
const driver = opts.checkout ? DRIVERS[opts.checkout] : null;
|
|
167
|
+
const locales = opts.messageLocales ?? [];
|
|
168
|
+
const imports = [
|
|
169
|
+
`import { createFunnelTree, type MountOpts, type RuntimePage } from '@appfunnel-dev/sdk'`,
|
|
170
|
+
driver && `import { ${driver.name} } from '@appfunnel-dev/sdk/${driver.sub}'`,
|
|
171
|
+
`import { config } from '${p}funnel'`,
|
|
172
|
+
opts.hasLayout && `import Layout from '${p}layout'`,
|
|
173
|
+
...locales.map(
|
|
174
|
+
(loc) => `import ${msgId(loc)} from '${p}messages/${loc}.json'`
|
|
175
|
+
)
|
|
176
|
+
].filter(Boolean).join("\n");
|
|
177
|
+
const loaders = opts.pageKeys.map(
|
|
178
|
+
(k) => ` ${JSON.stringify(k)}: () => import(${JSON.stringify(`${p}pages/${k}`)}),`
|
|
179
|
+
).join("\n");
|
|
180
|
+
const treeArgs = [
|
|
181
|
+
"config",
|
|
182
|
+
"pages",
|
|
183
|
+
opts.hasLayout && "layout: Layout",
|
|
184
|
+
driver && `checkoutDriver: ${driver.name}`,
|
|
185
|
+
locales.length > 0 && "messages"
|
|
186
|
+
].filter(Boolean).join(", ");
|
|
187
|
+
const messagesConst = locales.length > 0 ? `
|
|
188
|
+
const messages = { ${locales.map((loc) => `${JSON.stringify(loc)}: ${msgId(loc)}`).join(", ")} }
|
|
189
|
+
` : "";
|
|
190
|
+
return `${HEADER}${imports}
|
|
191
|
+
|
|
192
|
+
const loaders: Record<string, () => Promise<unknown>> = {
|
|
193
|
+
${loaders}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Flow pages come from funnel.ts (order + meta + routing). A \`@variant\` page MAY also be declared
|
|
197
|
+
// there (with its \`@\` key) to carry its OWN optional routing (\`next\`) \u2014 it still collapses out of
|
|
198
|
+
// the linear flow by the \`@\` marker, but its meta rides along here. Any page file NOT declared is an
|
|
199
|
+
// off-flow \`@variant\` with no own routing, appended with empty meta so it's still loadable.
|
|
200
|
+
const decls = config.pages ?? []
|
|
201
|
+
const flowKeys = new Set(decls.map((d) => d.key))
|
|
202
|
+
export const pages: RuntimePage[] = [
|
|
203
|
+
...decls.map((d) => ({ key: d.key, meta: d, load: loaders[d.key] })),
|
|
204
|
+
...Object.keys(loaders)
|
|
205
|
+
.filter((k) => !flowKeys.has(k))
|
|
206
|
+
.map((k) => ({ key: k, meta: {}, load: loaders[k] })),
|
|
207
|
+
] as RuntimePage[]
|
|
208
|
+
${messagesConst}
|
|
209
|
+
export const tree = createFunnelTree({ ${treeArgs} })
|
|
210
|
+
|
|
211
|
+
export type { MountOpts }
|
|
212
|
+
`;
|
|
213
|
+
}
|
|
214
|
+
function generateEntryClient(opts) {
|
|
215
|
+
const p = opts.importPrefix ?? "./";
|
|
216
|
+
const styles = opts.hasStyles ? `import '${p}styles.css'
|
|
217
|
+
` : "";
|
|
218
|
+
return `${HEADER}${styles}import { hydrateRoot } from 'react-dom/client'
|
|
219
|
+
import { tree, type MountOpts } from './mount'
|
|
220
|
+
|
|
221
|
+
const dataEl = document.getElementById('__af_data')
|
|
222
|
+
const data = (dataEl?.textContent ? JSON.parse(dataEl.textContent) : {}) as MountOpts & {
|
|
223
|
+
initialKey?: string | null
|
|
224
|
+
}
|
|
225
|
+
const root = document.getElementById('root')
|
|
226
|
+
// FLASH-FREE HYDRATION (SSR-isolation Option B): always DEFER sessionValues on the client. The
|
|
227
|
+
// markup we hydrate against is DEFAULT-state (build-time prerendered, or an SSR render with empty
|
|
228
|
+
// sessionValues), so seeding the store with defaults for the first render is a byte-clean match \u2014
|
|
229
|
+
// no hydration mismatch. The visitor's saved answers (data.sessionValues, present on the "tail"
|
|
230
|
+
// path) are then applied post-hydration, popping into already-visible controls with no empty
|
|
231
|
+
// flash. On the cold path sessionValues is absent, so deferring is a no-op (identical output).
|
|
232
|
+
if (root) hydrateRoot(root, tree({ ...data, initialKey: data.initialKey ?? undefined, deferSessionValues: true }))
|
|
233
|
+
`;
|
|
234
|
+
}
|
|
235
|
+
function generateEntryServer(opts) {
|
|
236
|
+
const p = opts.importPrefix ?? "./";
|
|
237
|
+
return `${HEADER}import { renderToReadableStream } from 'react-dom/server.browser'
|
|
238
|
+
import { compileManifest } from '@appfunnel-dev/sdk'
|
|
239
|
+
import { config } from '${p}funnel'
|
|
240
|
+
import { tree, pages, type MountOpts } from './mount'
|
|
241
|
+
|
|
242
|
+
export const manifest = compileManifest({
|
|
243
|
+
funnel: config,
|
|
244
|
+
pages: pages.map((pg) => ({ key: pg.key, meta: pg.meta })),
|
|
245
|
+
offeringRefs: ${JSON.stringify(opts.offeringRefs ?? [])},
|
|
246
|
+
checkoutRefs: ${JSON.stringify(opts.checkoutRefs ?? { surfaces: [], upsellKinds: [], dynamic: false })},
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
export async function ssr(opts: MountOpts): Promise<string> {
|
|
250
|
+
const stream = await renderToReadableStream(tree(opts))
|
|
251
|
+
await stream.allReady
|
|
252
|
+
return await new Response(stream).text()
|
|
253
|
+
}
|
|
254
|
+
`;
|
|
255
|
+
}
|
|
256
|
+
function generateDevEntry(opts) {
|
|
257
|
+
const p = opts.importPrefix ?? "./";
|
|
258
|
+
const styles = opts.hasStyles ? `import '${p}styles.css'
|
|
259
|
+
` : "";
|
|
260
|
+
return `${HEADER}${styles}import { createRoot } from 'react-dom/client'
|
|
261
|
+
import { tree } from './mount'
|
|
262
|
+
|
|
263
|
+
const el = document.getElementById('root')
|
|
264
|
+
if (el) createRoot(el).render(tree(${JSON.stringify(opts.data)}))
|
|
265
|
+
`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export { generateDevEntry, generateEntryClient, generateEntryServer, generateMount, scanOfferingRefs };
|
|
269
|
+
//# sourceMappingURL=manifest-entry.js.map
|
|
270
|
+
//# sourceMappingURL=manifest-entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/manifest/offering-refs.ts","../src/flow/scaffold.ts"],"names":[],"mappings":"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","file":"manifest-entry.js","sourcesContent":["/**\n * Pure, React-free page scan: find which offering SLOTS a funnel's pages reference — the first\n * string-literal arg of `useOffering(...)` and the `offering` 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({ offeringRefs })`, which flags references to slots the\n * funnel doesn't declare (unknown) or hasn't assigned a catalog charge key to (unassigned).\n *\n * Non-literal args (`useOffering(selectedId)`, `offering={expr}`) and the `useOfferings()` 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 `useOffering('x')` inside a comment or string literal\n * can't false-positive.\n */\n\nexport interface OfferingRefScan {\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 `useOfferings()` wildcard (not statically enumerable). */\n dynamic: boolean\n /**\n * Checkout-SURFACE usage the pages express — feeds the publish (provider × surface) capability\n * gate. Absent-field back-compat: a manifest compiled before this existed simply omits it, and\n * the gate skips (no false blocks). See {@link CheckoutUsageScan}.\n */\n checkout: CheckoutUsageScan\n}\n\n/**\n * What the pages ask the checkout to DO, statically — the surfaces they mount and the off-session\n * upsell kinds they charge. Literal values only; a computed value (`surface={expr}`) sets\n * {@link dynamic} so the publish gate WARNS instead of hard-failing (it can't enumerate it).\n */\nexport interface CheckoutUsageScan {\n /**\n * Purchase SURFACES the pages request, deduped in first-seen order. Captures every literal\n * `<Checkout surface=\"…\">` plus the on-session re-collect surfaces `<Checkout|Upsell fallback=\"…\">`\n * and `recoverySurface=\"…\"` (a fallback re-collects a card → it IS a purchase surface the provider\n * must support). Validated with `validateCheckout(provider, surface)`.\n */\n surfaces: string[]\n /**\n * Off-session upsell KINDS from `<Upsell kind=\"…\">`, deduped. A `<Upsell>` with no `kind` attr\n * defaults to `'subscription'` (mirrors the runtime default), so an unadorned `<Upsell>` records\n * `'subscription'`. Validated with `validateUpsell(provider, paywallSurface, kind)`.\n */\n upsellKinds: string[]\n /** True if any surface/kind was a non-literal (computed) value → the gate WARNS, never blocks, on it. */\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\n/**\n * Index of the `>` that closes the opening tag begun at `from` (the char after `<Checkout`). Tracks\n * brace depth so a `surface={a > b}` attribute expression doesn't end the tag early; `>` at depth 0\n * closes it (covers `/>` too — the `/` is at depth 0 and we stop at `>`). Returns end-of-string if\n * the tag never closes (masked never fires a value scan past it).\n */\nfunction tagAttrEnd(masked: string, from: number): number {\n let depth = 0\n for (let i = from; i < masked.length; i++) {\n const c = masked[i]\n if (c === '{') depth++\n else if (c === '}') { if (depth > 0) depth-- }\n else if (c === '>' && depth === 0) return i\n }\n return masked.length\n}\n\n/**\n * Read a JSX attribute `name` inside the opening-tag span `[start, end)`: returns its literal value,\n * `{ dynamic: true }` for a `{expr}` value, or null when the attribute is absent. Positions are on\n * the MASK; the value is read from the ORIGINAL (so a string-literal value survives masking).\n */\nfunction readAttr(\n src: string,\n masked: string,\n start: number,\n end: number,\n name: string,\n): { value?: string; dynamic?: boolean } | null {\n const re = new RegExp(`\\\\b${name}\\\\s*=\\\\s*`, 'g')\n re.lastIndex = start\n const m = re.exec(masked)\n if (!m || m.index >= end) return null\n const at = m.index + m[0].length\n if (masked[at] === '{') return { dynamic: true }\n const lit = literalAt(src, masked, at)\n return lit !== null ? { value: lit } : { dynamic: true }\n}\n\nexport function scanOfferingRefs(files: Record<string, string>): OfferingRefScan {\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 // Checkout-surface usage (separate dynamic flag: a computed SURFACE relaxes the capability gate,\n // independent of whether an OFFERING ref is computed).\n const surfaceSeen = new Set<string>()\n const surfaces: string[] = []\n const kindSeen = new Set<string>()\n const upsellKinds: string[] = []\n let checkoutDynamic = false\n const addSurface = (v: string) => { if (v && !surfaceSeen.has(v)) { surfaceSeen.add(v); surfaces.push(v) } }\n const addKind = (v: string) => { if (v && !kindSeen.has(v)) { kindSeen.add(v); upsellKinds.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 // useOffering( … ) / useOfferings( … ) — positions on the MASK (a call inside a string won't match).\n for (const m of masked.matchAll(/\\buseOfferings?\\s*\\(/g)) {\n const plural = source.slice(m.index!, m.index! + 12) === 'useOfferings'\n const j = skipWs(masked, m.index! + m[0].length)\n if (masked[j] === ')') { if (plural) dynamic = true; continue } // useOfferings() = whole-catalog wildcard\n const lit = literalAt(source, masked, j)\n if (lit !== null) add(lit)\n else dynamic = true // useOffering(variable) — can't resolve statically\n }\n\n // <Checkout offering=\"…\"> / <Upsell offering=\"…\"> — the `offering` attribute value.\n for (const m of masked.matchAll(/<(?:Checkout|Upsell)\\b[^>]*?\\boffering\\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 // offering={expr}\n }\n\n // <Checkout …> / <Upsell …> checkout-SURFACE usage. Parse each opening tag's attribute span so\n // attribute order doesn't matter and a `>` inside a `{expr}` value can't truncate it.\n for (const m of masked.matchAll(/<(Checkout|Upsell)\\b/g)) {\n const comp = m[1]\n const tagStart = m.index! + m[0].length\n const tagEnd = tagAttrEnd(masked, tagStart)\n const attr = (name: string) => readAttr(source, masked, tagStart, tagEnd, name)\n // A fallback / recoverySurface re-collects a card ON-SESSION → it's a purchase surface too.\n const fallback = attr('fallback')\n if (fallback?.value) addSurface(fallback.value)\n else if (fallback?.dynamic) checkoutDynamic = true\n const recovery = attr('recoverySurface')\n if (recovery?.value) addSurface(recovery.value)\n else if (recovery?.dynamic) checkoutDynamic = true\n\n if (comp === 'Checkout') {\n const surface = attr('surface')\n if (surface?.value) addSurface(surface.value)\n else if (surface?.dynamic) checkoutDynamic = true\n } else {\n // <Upsell>: off-session; `kind` defaults to 'subscription' when the attr is absent.\n const kind = attr('kind')\n if (kind?.value) addKind(kind.value)\n else if (kind?.dynamic) checkoutDynamic = true\n else addKind('subscription')\n }\n }\n }\n return { slots: order, dynamic, checkout: { surfaces, upsellKinds, dynamic: checkoutDynamic } }\n}\n","/**\n * Source generators for the funnel build \"machinery\" authors no longer write. `funnel.ts` (the\n * spine: id, responses, offerings, 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\nimport type { CheckoutUsageScan } from '../manifest/offering-refs'\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 `scanOfferingRefs`), baked into the manifest for validation. */\n\tofferingRefs?: string[]\n\t/** Checkout-surface usage the pages express (from `scanOfferingRefs(...).checkout`), baked into the manifest for the publish capability gate. */\n\tcheckoutRefs?: CheckoutUsageScan\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 =\n\t'// 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 &&\n\t\t\t`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(\n\t\t\t(loc) => `import ${msgId(loc)} from '${p}messages/${loc}.json'`\n\t\t),\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) =>\n\t\t\t\t`\\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). A \\`@variant\\` page MAY also be declared\n// there (with its \\`@\\` key) to carry its OWN optional routing (\\`next\\`) — it still collapses out of\n// the linear flow by the \\`@\\` marker, but its meta rides along here. Any page file NOT declared is an\n// off-flow \\`@variant\\` with no own routing, appended with empty meta so it's still 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(\n\topts: Pick<ScaffoldOptions, 'importPrefix' | 'hasStyles'>\n): 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')\n// FLASH-FREE HYDRATION (SSR-isolation Option B): always DEFER sessionValues on the client. The\n// markup we hydrate against is DEFAULT-state (build-time prerendered, or an SSR render with empty\n// sessionValues), so seeding the store with defaults for the first render is a byte-clean match —\n// no hydration mismatch. The visitor's saved answers (data.sessionValues, present on the \"tail\"\n// path) are then applied post-hydration, popping into already-visible controls with no empty\n// flash. On the cold path sessionValues is absent, so deferring is a no-op (identical output).\nif (root) hydrateRoot(root, tree({ ...data, initialKey: data.initialKey ?? undefined, deferSessionValues: true }))\n`\n}\n\n/** `entry.server.tsx` — `ssr(opts)` HTML + the compiled `manifest` (read by the build). */\nexport function generateEntryServer(\n\topts: Pick<ScaffoldOptions, 'importPrefix' | 'offeringRefs' | 'checkoutRefs'>\n): 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\tofferingRefs: ${JSON.stringify(opts.offeringRefs ?? [])},\n\tcheckoutRefs: ${JSON.stringify(opts.checkoutRefs ?? { surfaces: [], upsellKinds: [], dynamic: false })},\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'> & {\n\t\tdata: unknown\n\t}\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"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkWYUDL4FI_cjs = require('./chunk-WYUDL4FI.cjs');
|
|
4
|
+
require('./chunk-EMMSS5I5.cjs');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Object.defineProperty(exports, "AF_PROTOCOL", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function () { return chunkWYUDL4FI_cjs.AF_PROTOCOL; }
|
|
11
|
+
});
|
|
12
|
+
//# sourceMappingURL=protocol.cjs.map
|
|
13
|
+
//# sourceMappingURL=protocol.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"protocol.cjs"}
|