@appfunnel-dev/sdk 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +69 -3
- package/dist/{checkout-BYcOfyg_.d.ts → checkout-BPYU4wXF.d.ts} +50 -16
- package/dist/{checkout-BM1T-u6_.d.cts → checkout-BzP__Mxe.d.cts} +50 -16
- package/dist/{chunk-PN7CRJAF.js → chunk-AW2YURFE.js} +60 -19
- package/dist/chunk-AW2YURFE.js.map +1 -0
- package/dist/{chunk-MBTHQP4Z.cjs → chunk-DTSLJ24T.cjs} +11 -3
- package/dist/chunk-DTSLJ24T.cjs.map +1 -0
- package/dist/{chunk-NA7DRFOA.cjs → chunk-MBSXEIXT.cjs} +60 -19
- package/dist/chunk-MBSXEIXT.cjs.map +1 -0
- package/dist/{chunk-KVMNVX6J.js → chunk-USAECFWO.js} +11 -3
- package/dist/chunk-USAECFWO.js.map +1 -0
- package/dist/driver-paddle.cjs +14 -14
- package/dist/driver-paddle.d.cts +1 -1
- package/dist/driver-paddle.d.ts +1 -1
- package/dist/driver-paddle.js +1 -1
- package/dist/driver-stripe.cjs +31 -15
- package/dist/driver-stripe.cjs.map +1 -1
- package/dist/driver-stripe.d.cts +1 -1
- package/dist/driver-stripe.d.ts +1 -1
- package/dist/driver-stripe.js +18 -2
- package/dist/driver-stripe.js.map +1 -1
- package/dist/index.cjs +78 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -6
- package/dist/index.d.ts +25 -6
- package/dist/index.js +13 -2
- package/dist/index.js.map +1 -1
- package/dist/{manifest-BajvtPWs.d.cts → manifest-Dp1oRvqp.d.cts} +23 -0
- package/dist/{manifest-BajvtPWs.d.ts → manifest-Dp1oRvqp.d.ts} +23 -0
- package/dist/manifest-entry.cjs +9 -1
- package/dist/manifest-entry.cjs.map +1 -1
- package/dist/manifest-entry.d.cts +15 -2
- package/dist/manifest-entry.d.ts +15 -2
- package/dist/manifest-entry.js +9 -1
- package/dist/manifest-entry.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-KVMNVX6J.js.map +0 -1
- package/dist/chunk-MBTHQP4Z.cjs.map +0 -1
- package/dist/chunk-NA7DRFOA.cjs.map +0 -1
- package/dist/chunk-PN7CRJAF.js.map +0 -1
package/dist/manifest-entry.cjs
CHANGED
|
@@ -137,6 +137,7 @@ function scanOfferingRefs(files) {
|
|
|
137
137
|
const kindSeen = /* @__PURE__ */ new Set();
|
|
138
138
|
const upsellKinds = [];
|
|
139
139
|
let checkoutDynamic = false;
|
|
140
|
+
const offeringless = [];
|
|
140
141
|
const addSurface = (v) => {
|
|
141
142
|
if (v && !surfaceSeen.has(v)) {
|
|
142
143
|
surfaceSeen.add(v);
|
|
@@ -184,6 +185,13 @@ function scanOfferingRefs(files) {
|
|
|
184
185
|
const tagStart = m.index + m[0].length;
|
|
185
186
|
const tagEnd = tagAttrEnd(masked, tagStart);
|
|
186
187
|
const attr = (name) => readAttr(source, masked, tagStart, tagEnd, name);
|
|
188
|
+
if (!attr("offering") && !/\{\s*\.\.\./.test(masked.slice(tagStart, tagEnd))) {
|
|
189
|
+
offeringless.push({
|
|
190
|
+
file: path,
|
|
191
|
+
line: source.slice(0, m.index).split("\n").length,
|
|
192
|
+
component: comp
|
|
193
|
+
});
|
|
194
|
+
}
|
|
187
195
|
const managed = readBoolAttr(masked, tagStart, tagEnd, "managed");
|
|
188
196
|
if (managed?.dynamic) checkoutDynamic = true;
|
|
189
197
|
const isManaged = managed?.value === true;
|
|
@@ -221,7 +229,7 @@ function scanOfferingRefs(files) {
|
|
|
221
229
|
}
|
|
222
230
|
}
|
|
223
231
|
}
|
|
224
|
-
return { slots: order, dynamic, checkout: { surfaces, managedSurfaces, upsellKinds, dynamic: checkoutDynamic } };
|
|
232
|
+
return { slots: order, dynamic, checkout: { surfaces, managedSurfaces, upsellKinds, dynamic: checkoutDynamic }, offeringless };
|
|
225
233
|
}
|
|
226
234
|
|
|
227
235
|
// src/flow/scaffold.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/manifest/offering-refs.ts","../src/flow/scaffold.ts"],"names":["PAYMENT_RENDERINGS","surface"],"mappings":";;;;;;;;AAmGA,SAAS,YAAA,CAAa,GAAA,EAAa,IAAA,EAAc,SAAA,EAA4B;AAC3E,EAAA,MAAM,CAAA,GAAI,IAAI,IAAI,CAAA;AAClB,EAAA,KAAA,IAAS,IAAI,IAAA,GAAO,CAAA,EAAG,CAAA,GAAI,GAAA,CAAI,QAAQ,CAAA,EAAA,EAAK;AAC1C,IAAA,MAAM,CAAA,GAAI,IAAI,CAAC,CAAA;AACf,IAAA,IAAI,MAAM,IAAA,EAAM;AAAE,MAAA,CAAA,EAAA;AAAK,MAAA;AAAA,IAAS;AAChC,IAAA,IAAI,CAAA,KAAM,GAAG,OAAO,CAAA;AACpB,IAAA,IAAI,CAAC,SAAA,IAAa,CAAA,KAAM,IAAA,EAAM,OAAO,EAAA;AAAA,EACvC;AACA,EAAA,OAAO,EAAA;AACT;AAGA,IAAM,SAAA,GAAY,OAAA;AA4ClB,SAAS,KAAK,GAAA,EAAqB;AACjC,EAAA,MAAM,GAAA,GAAM,GAAA,CAAI,KAAA,CAAM,EAAE,CAAA;AACxB,EAAA,MAAM,IAAI,GAAA,CAAI,MAAA;AACd,EAAA,IAAI,CAAA,GAAI,CAAA;AACR,EAAA,OAAO,IAAI,CAAA,EAAG;AACZ,IAAA,MAAM,CAAA,GAAI,IAAI,CAAC,CAAA;AACf,IAAA,MAAM,CAAA,GAAI,GAAA,CAAI,CAAA,GAAI,CAAC,CAAA;AACnB,IAAA,IAAI,CAAA,KAAM,GAAA,IAAO,CAAA,KAAM,GAAA,EAAK;AAC1B,MAAA,OAAO,CAAA,GAAI,CAAA,IAAK,GAAA,CAAI,CAAC,MAAM,IAAA,EAAM;AAAE,QAAA,GAAA,CAAI,CAAC,CAAA,GAAI,GAAA;AAAK,QAAA,CAAA,EAAA;AAAA,MAAI;AAAA,IACvD,CAAA,MAAA,IAAW,CAAA,KAAM,GAAA,IAAO,CAAA,KAAM,GAAA,EAAK;AACjC,MAAA,GAAA,CAAI,CAAC,CAAA,GAAI,GAAA;AAAK,MAAA,GAAA,CAAI,CAAA,GAAI,CAAC,CAAA,GAAI,GAAA;AAAK,MAAA,CAAA,IAAK,CAAA;AACrC,MAAA,OAAO,IAAI,CAAA,EAAG;AACZ,QAAA,IAAI,GAAA,CAAI,CAAC,CAAA,KAAM,GAAA,IAAO,IAAI,CAAA,GAAI,CAAC,MAAM,GAAA,EAAK;AAAE,UAAA,GAAA,CAAI,CAAC,CAAA,GAAI,GAAA;AAAK,UAAA,GAAA,CAAI,CAAA,GAAI,CAAC,CAAA,GAAI,GAAA;AAAK,UAAA,CAAA,IAAK,CAAA;AAAG,UAAA;AAAA,QAAM;AAC1F,QAAA,IAAI,IAAI,CAAC,CAAA,KAAM,IAAA,EAAM,GAAA,CAAI,CAAC,CAAA,GAAI,GAAA;AAC9B,QAAA,CAAA,EAAA;AAAA,MACF;AAAA,IACF,WAAW,CAAA,KAAM,GAAA,IAAO,CAAA,KAAM,GAAA,IAAO,MAAM,GAAA,EAAK;AAC9C,MAAA,MAAM,YAAY,CAAA,KAAM,GAAA;AAGxB,MAAA,IAAI,CAAC,SAAA,IAAa,CAAA,GAAI,CAAA,IAAK,SAAA,CAAU,KAAK,GAAA,CAAI,CAAA,GAAI,CAAC,CAAC,CAAA,EAAG;AAAE,QAAA,CAAA,EAAA;AAAK,QAAA;AAAA,MAAS;AACvE,MAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,GAAA,EAAK,CAAA,EAAG,SAAS,CAAA;AAC5C,MAAA,IAAI,UAAU,EAAA,EAAI;AAAE,QAAA,CAAA,EAAA;AAAK,QAAA;AAAA,MAAS;AAClC,MAAA,KAAA,IAAS,CAAA,GAAI,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK,IAAI,GAAA,CAAI,CAAC,CAAA,KAAM,IAAA,EAAM,GAAA,CAAI,CAAC,CAAA,GAAI,GAAA;AAClE,MAAA,CAAA,GAAI,KAAA,GAAQ,CAAA;AAAA,IACd,CAAA,MAAO,CAAA,EAAA;AAAA,EACT;AACA,EAAA,OAAO,GAAA,CAAI,KAAK,EAAE,CAAA;AACpB;AAGA,SAAS,SAAA,CAAU,GAAA,EAAa,MAAA,EAAgB,IAAA,EAA6B;AAC3E,EAAA,MAAM,CAAA,GAAI,OAAO,IAAI,CAAA;AACrB,EAAA,IAAI,MAAM,GAAA,IAAO,CAAA,KAAM,GAAA,IAAO,CAAA,KAAM,KAAK,OAAO,IAAA;AAChD,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,OAAA,CAAQ,CAAA,EAAG,OAAO,CAAC,CAAA;AACxC,EAAA,IAAI,KAAA,KAAU,IAAI,OAAO,IAAA;AACzB,EAAA,OAAO,GAAA,CAAI,KAAA,CAAM,IAAA,GAAO,CAAA,EAAG,KAAK,CAAA;AAClC;AAGA,SAAS,MAAA,CAAO,QAAgB,CAAA,EAAmB;AACjD,EAAA,OAAO,IAAI,MAAA,CAAO,MAAA,KAAW,OAAO,CAAC,CAAA,KAAM,OAAO,MAAA,CAAO,CAAC,CAAA,KAAM,GAAA,IAAQ,OAAO,CAAC,CAAA,KAAM,QAAQ,MAAA,CAAO,CAAC,MAAM,IAAA,CAAA,EAAO,CAAA,EAAA;AACnH,EAAA,OAAO,CAAA;AACT;AAQA,SAAS,UAAA,CAAW,QAAgB,IAAA,EAAsB;AACxD,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,KAAA,IAAS,CAAA,GAAI,IAAA,EAAM,CAAA,GAAI,MAAA,CAAO,QAAQ,CAAA,EAAA,EAAK;AACzC,IAAA,MAAM,CAAA,GAAI,OAAO,CAAC,CAAA;AAClB,IAAA,IAAI,MAAM,GAAA,EAAK,KAAA,EAAA;AAAA,SAAA,IACN,MAAM,GAAA,EAAK;AAAE,MAAA,IAAI,QAAQ,CAAA,EAAG,KAAA,EAAA;AAAA,IAAQ,CAAA,MAAA,IACpC,CAAA,KAAM,GAAA,IAAO,KAAA,KAAU,GAAG,OAAO,CAAA;AAAA,EAC5C;AACA,EAAA,OAAO,MAAA,CAAO,MAAA;AAChB;AAOA,SAAS,QAAA,CACP,GAAA,EACA,MAAA,EACA,KAAA,EACA,KACA,IAAA,EAC8C;AAC9C,EAAA,MAAM,KAAK,IAAI,MAAA,CAAO,CAAA,GAAA,EAAM,IAAI,aAAa,GAAG,CAAA;AAChD,EAAA,EAAA,CAAG,SAAA,GAAY,KAAA;AACf,EAAA,MAAM,CAAA,GAAI,EAAA,CAAG,IAAA,CAAK,MAAM,CAAA;AACxB,EAAA,IAAI,CAAC,CAAA,IAAK,CAAA,CAAE,KAAA,IAAS,KAAK,OAAO,IAAA;AACjC,EAAA,MAAM,EAAA,GAAK,CAAA,CAAE,KAAA,GAAQ,CAAA,CAAE,CAAC,CAAA,CAAE,MAAA;AAC1B,EAAA,IAAI,OAAO,EAAE,CAAA,KAAM,KAAK,OAAO,EAAE,SAAS,IAAA,EAAK;AAC/C,EAAA,MAAM,GAAA,GAAM,SAAA,CAAU,GAAA,EAAK,MAAA,EAAQ,EAAE,CAAA;AACrC,EAAA,OAAO,GAAA,KAAQ,OAAO,EAAE,KAAA,EAAO,KAAI,GAAI,EAAE,SAAS,IAAA,EAAK;AACzD;AAoBA,SAAS,YAAA,CACP,MAAA,EACA,KAAA,EACA,GAAA,EACA,IAAA,EAC+C;AAC/C,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,KAAA,IAAS,CAAA,GAAI,KAAA,EAAO,CAAA,GAAI,GAAA,EAAK,CAAA,EAAA,EAAK;AAChC,IAAA,MAAM,CAAA,GAAI,OAAO,CAAC,CAAA;AAClB,IAAA,IAAI,MAAM,GAAA,EAAK;AAAE,MAAA,KAAA,EAAA;AAAS,MAAA;AAAA,IAAS;AACnC,IAAA,IAAI,MAAM,GAAA,EAAK;AAAE,MAAA,IAAI,QAAQ,CAAA,EAAG,KAAA,EAAA;AAAS,MAAA;AAAA,IAAS;AAClD,IAAA,IAAI,KAAA,KAAU,CAAA,IAAK,CAAA,KAAM,IAAA,CAAK,CAAC,CAAA,IAAK,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,MAAM,MAAM,IAAA,EAAM;AAE/E,IAAA,IAAI,CAAA,GAAI,SAAS,CAAC,IAAA,CAAK,KAAK,MAAA,CAAO,CAAA,GAAI,CAAC,CAAC,CAAA,EAAG;AAC5C,IAAA,IAAI,CAAA,GAAI,IAAI,IAAA,CAAK,MAAA;AACjB,IAAA,IAAI,UAAU,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,IAAK,GAAG,CAAA,EAAG;AACtC,IAAA,CAAA,GAAI,MAAA,CAAO,QAAQ,CAAC,CAAA;AACpB,IAAA,IAAI,OAAO,CAAC,CAAA,KAAM,KAAK,OAAO,EAAE,OAAO,IAAA,EAAK;AAC5C,IAAA,CAAA,GAAI,MAAA,CAAO,MAAA,EAAQ,CAAA,GAAI,CAAC,CAAA;AACxB,IAAA,IAAI,OAAO,CAAC,CAAA,KAAM,KAAK,OAAO,EAAE,SAAS,IAAA,EAAK;AAC9C,IAAA,MAAM,OAAA,GAAU,wBAAwB,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA,GAAI,CAAA,EAAG,GAAG,CAAC,CAAA;AACrE,IAAA,OAAO,OAAA,GAAU,EAAE,KAAA,EAAO,OAAA,CAAQ,CAAC,MAAM,MAAA,EAAO,GAAI,EAAE,OAAA,EAAS,IAAA,EAAK;AAAA,EACtE;AACA,EAAA,OAAO,IAAA;AACT;AAEO,SAAS,iBAAiB,KAAA,EAAgD;AAC/E,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,MAAM,GAAA,GAAM,CAAC,CAAA,KAAc;AAAE,IAAA,IAAI,CAAA,IAAK,CAAC,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,EAAG;AAAE,MAAA,IAAA,CAAK,IAAI,CAAC,CAAA;AAAG,MAAA,KAAA,CAAM,KAAK,CAAC,CAAA;AAAA,IAAE;AAAA,EAAE,CAAA;AAInF,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAY;AACpC,EAAA,MAAM,WAAqB,EAAC;AAC5B,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAY;AACpC,EAAA,MAAM,kBAA4B,EAAC;AACnC,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAY;AACjC,EAAA,MAAM,cAAwB,EAAC;AAC/B,EAAA,IAAI,eAAA,GAAkB,KAAA;AACtB,EAAA,MAAM,UAAA,GAAa,CAAC,CAAA,KAAc;AAAE,IAAA,IAAI,CAAA,IAAK,CAAC,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,EAAG;AAAE,MAAA,WAAA,CAAY,IAAI,CAAC,CAAA;AAAG,MAAA,QAAA,CAAS,KAAK,CAAC,CAAA;AAAA,IAAE;AAAA,EAAE,CAAA;AAC3G,EAAA,MAAM,UAAA,GAAa,CAAC,CAAA,KAAc;AAAE,IAAA,IAAI,CAAA,IAAK,CAAC,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,EAAG;AAAE,MAAA,WAAA,CAAY,IAAI,CAAC,CAAA;AAAG,MAAA,eAAA,CAAgB,KAAK,CAAC,CAAA;AAAA,IAAE;AAAA,EAAE,CAAA;AAClH,EAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAAc;AAAE,IAAA,IAAI,CAAA,IAAK,CAAC,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA,EAAG;AAAE,MAAA,QAAA,CAAS,IAAI,CAAC,CAAA;AAAG,MAAA,WAAA,CAAY,KAAK,CAAC,CAAA;AAAA,IAAE;AAAA,EAAE,CAAA;AAErG,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,MAAM,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAClD,IAAA,IAAI,CAAC,oBAAA,CAAqB,IAAA,CAAK,IAAI,CAAA,EAAG;AACtC,IAAA,MAAM,MAAA,GAAS,KAAK,MAAM,CAAA;AAG1B,IAAA,KAAA,MAAW,CAAA,IAAK,MAAA,CAAO,QAAA,CAAS,uBAAuB,CAAA,EAAG;AACxD,MAAA,MAAM,MAAA,GAAS,OAAO,KAAA,CAAM,CAAA,CAAE,OAAQ,CAAA,CAAE,KAAA,GAAS,EAAE,CAAA,KAAM,cAAA;AACzD,MAAA,MAAM,CAAA,GAAI,OAAO,MAAA,EAAQ,CAAA,CAAE,QAAS,CAAA,CAAE,CAAC,EAAE,MAAM,CAAA;AAC/C,MAAA,IAAI,MAAA,CAAO,CAAC,CAAA,KAAM,GAAA,EAAK;AAAE,QAAA,IAAI,QAAQ,OAAA,GAAU,IAAA;AAAM,QAAA;AAAA,MAAS;AAC9D,MAAA,MAAM,GAAA,GAAM,SAAA,CAAU,MAAA,EAAQ,MAAA,EAAQ,CAAC,CAAA;AACvC,MAAA,IAAI,GAAA,KAAQ,IAAA,EAAM,GAAA,CAAI,GAAG,CAAA;AAAA,WACpB,OAAA,GAAU,IAAA;AAAA,IACjB;AAMA,IAAA,KAAA,MAAW,KAAK,MAAA,CAAO,QAAA;AAAA,MACrB;AAAA,KACF,EAAG;AACD,MAAA,MAAM,CAAA,GAAI,CAAA,CAAE,KAAA,GAAS,CAAA,CAAE,CAAC,CAAA,CAAE,MAAA;AAC1B,MAAA,MAAM,GAAA,GAAM,SAAA,CAAU,MAAA,EAAQ,MAAA,EAAQ,CAAC,CAAA;AACvC,MAAA,IAAI,GAAA,KAAQ,IAAA,EAAM,GAAA,CAAI,GAAG,CAAA;AAAA,WAAA,IAChB,MAAA,CAAO,CAAC,CAAA,KAAM,GAAA,EAAK,OAAA,GAAU,IAAA;AAAA,IACxC;AAIA,IAAA,KAAA,MAAW,CAAA,IAAK,MAAA,CAAO,QAAA,CAAS,4CAA4C,CAAA,EAAG;AAC7E,MAAA,MAAM,IAAA,GAAO,EAAE,CAAC,CAAA;AAChB,MAAA,MAAM,WAAW,IAAA,KAAS,cAAA;AAI1B,MAAA,MAAM,QAAA,GAAW,IAAA,KAAS,gBAAA,IAAoB,IAAA,KAAS,cAAA;AACvD,MAAA,MAAM,QAAA,GAAW,CAAA,CAAE,KAAA,GAAS,CAAA,CAAE,CAAC,CAAA,CAAE,MAAA;AACjC,MAAA,MAAM,MAAA,GAAS,UAAA,CAAW,MAAA,EAAQ,QAAQ,CAAA;AAC1C,MAAA,MAAM,IAAA,GAAO,CAAC,IAAA,KAAiB,QAAA,CAAS,QAAQ,MAAA,EAAQ,QAAA,EAAU,QAAQ,IAAI,CAAA;AAK9E,MAAA,MAAM,OAAA,GAAU,YAAA,CAAa,MAAA,EAAQ,QAAA,EAAU,QAAQ,SAAS,CAAA;AAChE,MAAA,IAAI,OAAA,EAAS,SAAS,eAAA,GAAkB,IAAA;AACxC,MAAA,MAAM,SAAA,GAAY,SAAS,KAAA,KAAU,IAAA;AACrC,MAAA,MAAM,cAAwB,EAAC;AAG/B,MAAA,MAAM,QAAA,GAAW,KAAK,iBAAiB,CAAA;AACvC,MAAA,IAAI,QAAA,EAAU,KAAA,EAAO,WAAA,CAAY,IAAA,CAAK,SAAS,KAAK,CAAA;AAAA,WAAA,IAC3C,QAAA,EAAU,SAAS,eAAA,GAAkB,IAAA;AAE9C,MAAA,IAAI,QAAA,EAAU;AAQZ,QAAA,MAAM,IAAA,GAAO,KAAK,MAAM,CAAA;AACxB,QAAA,IAAI,IAAA,EAAM,KAAA,EAAO,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAA;AAAA,aAAA,IAC1B,IAAA,EAAM,SAAS,eAAA,GAAkB,IAAA;AAAA,MAC5C;AAKA,MAAA,MAAM,OAAA,GAAU,IAAA,KAAS,UAAA,GAAa,IAAA,CAAK,SAAS,CAAA,GAAI,IAAA;AACxD,MAAA,IAAI,OAAA,EAAS,SAAS,eAAA,GAAkB,IAAA;AAAA,WAAA,IAC/B,OAAA,EAAS,KAAA,EAAO,WAAA,CAAY,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA,WAClD;AAOH,QAAA,MAAM,KAAA,GAAQ,QAAA,GAAW,IAAA,CAAK,OAAO,CAAA,GAAI,IAAA;AAGzC,QAAA,IAAI,KAAA,EAAO,SAAS,eAAA,GAAkB,IAAA;AAAA,aAAA,IAC7B,KAAA,EAAO,KAAA,KAAU,OAAA,IAAW,KAAA,EAAO,UAAU,UAAA,EAAY;AAEhE,UAAA,WAAA,CAAY,IAAA,CAAK,MAAM,KAAK,CAAA;AAAA,QAC9B,CAAA,MAAO;AAKL,UAAA,IAAI,QAAA,IAAY,CAAC,QAAA,EAAU,WAAA,CAAY,KAAK,OAAO,CAAA;AAKnD,UAAA,MAAM,OAAA,GAAU,KAAK,SAAS,CAAA;AAC9B,UAAA,IAAI,OAAA,EAAS,SAAS,eAAA,GAAkB,IAAA;AAAA,eAAA,IAC/B,SAAS,KAAA,EAAO;AACvB,YAAA,WAAA,CAAY,IAAA;AAAA,cACVA,oCAAA,CAAmB,OAAA,CAAQ,KAAqC,CAAA,IAAK,OAAA,CAAQ;AAAA,aAC/E;AAAA,UACF,CAAA,MAAA,IAAW,CAAC,QAAA,EAAU,WAAA,CAAY,KAAK,MAAM,CAAA;AAAA,QAC/C;AAAA,MACF;AAEA,MAAA,KAAA,MAAWC,YAAW,WAAA,EAAa;AACjC,QAAA,UAAA,CAAWA,QAAO,CAAA;AAClB,QAAA,IAAI,SAAA,aAAsBA,QAAO,CAAA;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,OAAA,EAAS,QAAA,EAAU,EAAE,QAAA,EAAU,eAAA,EAAiB,WAAA,EAAa,OAAA,EAAS,eAAA,EAAgB,EAAE;AACjH;;;AC5XA,SAAS,MAAM,MAAA,EAAwB;AACtC,EAAA,OAAO,MAAA,GAAS,MAAA,CAAO,OAAA,CAAQ,eAAA,EAAiB,GAAG,CAAA;AACpD;AAEA,IAAM,OAAA,GAAU;AAAA,EACf,MAAA,EAAQ,EAAE,IAAA,EAAM,sBAAA,EAAwB,KAAK,eAAA,EAAgB;AAAA,EAC7D,MAAA,EAAQ,EAAE,IAAA,EAAM,sBAAA,EAAwB,KAAK,eAAA;AAC9C,CAAA;AAEA,IAAM,MAAA,GACL,2FAAA;AAGM,SAAS,cAAc,IAAA,EAA+B;AAC5D,EAAA,MAAM,CAAA,GAAI,KAAK,YAAA,IAAgB,IAAA;AAC/B,EAAA,MAAM,SAAS,IAAA,CAAK,QAAA,GAAW,OAAA,CAAQ,IAAA,CAAK,QAAQ,CAAA,GAAI,IAAA;AAExD,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,cAAA,IAAkB,EAAC;AACxC,EAAA,MAAM,OAAA,GAAU;AAAA,IACf,CAAA,uFAAA,CAAA;AAAA,IACA,UACC,CAAA,SAAA,EAAY,MAAA,CAAO,IAAI,CAAA,4BAAA,EAA+B,OAAO,GAAG,CAAA,CAAA,CAAA;AAAA,IACjE,2BAA2B,CAAC,CAAA,OAAA,CAAA;AAAA,IAC5B,IAAA,CAAK,SAAA,IAAa,CAAA,oBAAA,EAAuB,CAAC,CAAA,OAAA,CAAA;AAAA,IAC1C,GAAG,OAAA,CAAQ,GAAA;AAAA,MACV,CAAC,QAAQ,CAAA,OAAA,EAAU,KAAA,CAAM,GAAG,CAAC,CAAA,OAAA,EAAU,CAAC,CAAA,SAAA,EAAY,GAAG,CAAA,MAAA;AAAA;AACxD,GACD,CACE,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,IAAI,CAAA;AAGX,EAAA,MAAM,OAAA,GAAU,KAAK,QAAA,CACnB,GAAA;AAAA,IACA,CAAC,CAAA,KACA,CAAA,CAAA,EAAK,IAAA,CAAK,UAAU,CAAC,CAAC,CAAA,eAAA,EAAkB,IAAA,CAAK,UAAU,CAAA,EAAG,CAAC,CAAA,MAAA,EAAS,CAAC,EAAE,CAAC,CAAA,EAAA;AAAA,GAC1E,CACC,KAAK,IAAI,CAAA;AAEX,EAAA,MAAM,QAAA,GAAW;AAAA,IAChB,QAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAK,SAAA,IAAa,gBAAA;AAAA,IAClB,MAAA,IAAU,CAAA,gBAAA,EAAmB,MAAA,CAAO,IAAI,CAAA,CAAA;AAAA,IACxC,OAAA,CAAQ,SAAS,CAAA,IAAK;AAAA,GACvB,CACE,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,IAAI,CAAA;AAGX,EAAA,MAAM,aAAA,GACL,OAAA,CAAQ,MAAA,GAAS,CAAA,GACd;AAAA,mBAAA,EAAwB,QAAQ,GAAA,CAAI,CAAC,GAAA,KAAQ,CAAA,EAAG,KAAK,SAAA,CAAU,GAAG,CAAC,CAAA,EAAA,EAAK,MAAM,GAAG,CAAC,EAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,CAAA,GAChG,EAAA;AAEJ,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,OAAO;;AAAA;AAAA,EAGzB,OAAO;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeP,aAAa;AAAA,uCAAA,EAC0B,QAAQ,CAAA;;AAAA;AAAA,CAAA;AAIjD;AAGO,SAAS,oBACf,IAAA,EACS;AACT,EAAA,MAAM,CAAA,GAAI,KAAK,YAAA,IAAgB,IAAA;AAC/B,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,SAAA,GAAY,CAAA,QAAA,EAAW,CAAC,CAAA;AAAA,CAAA,GAAkB,EAAA;AAC9D,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,MAAM,CAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAgB1B;AAGO,SAAS,oBACf,IAAA,EACS;AACT,EAAA,MAAM,CAAA,GAAI,KAAK,YAAA,IAAgB,IAAA;AAC/B,EAAA,OAAO,GAAG,MAAM,CAAA;AAAA;AAAA,wBAAA,EAES,CAAC,CAAA;AAAA;;AAAA;AAAA;AAAA;AAAA,eAAA,EAMV,KAAK,SAAA,CAAU,IAAA,CAAK,YAAA,IAAgB,EAAE,CAAC,CAAA;AAAA,eAAA,EACvC,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,YAAA,IAAgB,EAAE,QAAA,EAAU,EAAC,EAAG,WAAA,EAAa,EAAC,EAAG,OAAA,EAAS,KAAA,EAAO,CAAC,CAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AASvG;AAGO,SAAS,iBACf,IAAA,EAGS;AACT,EAAA,MAAM,CAAA,GAAI,KAAK,YAAA,IAAgB,IAAA;AAC/B,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,SAAA,GAAY,CAAA,QAAA,EAAW,CAAC,CAAA;AAAA,CAAA,GAAkB,EAAA;AAC9D,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,MAAM,CAAA;AAAA;;AAAA;AAAA,mCAAA,EAIW,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,CAAA;AAE9D","file":"manifest-entry.cjs","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>` /\n * `<CheckoutButton>` / `<UpsellButton>`.\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\n// The one place the payment WORDS become surface words. Imported rather than re-listed: the runtime\n// maps `payment` through this exact table (see `resolvePresentation` in ../commerce/checkout), so the\n// publish gate can't drift from what will actually be requested. Both modules are React-free and ship\n// through the same manifest entry.\nimport { PAYMENT_RENDERINGS, type PaymentMode } from '../commerce/capabilities'\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, in the ONE vocabulary the\n * publish gate and the wire both speak ({@link CheckoutSurface}). Validated with\n * `validateCheckout(provider, surface)`.\n *\n * Captured from three spellings, all of which mean the same thing to the gate:\n * - the author axes — `payment` (WHAT the provider renders: `wallets`→`express`, `card`→`element`,\n * `full`→`embedded`, absent/`auto`→`auto`) and `opens` (WHERE it is presented: recorded as\n * itself). A `<CheckoutButton>` records BOTH, because both have to be possible: our sheet has to\n * exist on the provider, AND the provider has to be able to render what we put in it.\n * - the deprecated `surface=\"…\"`, recorded as itself. A tag carrying it takes the legacy path at\n * runtime, so the scan reads it INSTEAD of the axes rather than as well as them — same rule,\n * same result, one place.\n * - the on-session re-collect surface `recoverySurface=\"…\"` (a recovery\n * re-collects a card → it IS a purchase surface the provider must support).\n *\n * `'auto'` is passed through un-expanded, as it always has been: it is a per-device PREFERENCE and\n * only the gate knows the provider, so the gate resolves it (see `promote-gate.ts`).\n */\n surfaces: string[]\n /**\n * The SUBSET of {@link surfaces} whose tag also carried `managed` — Stripe **Managed Payments**,\n * which resolves the provider to a DIFFERENT capability profile (`profileFor(provider, { managed:\n * true })`) with a narrower surface set. Validated with `validateCheckout(provider, surface, {\n * managed: true })`, which is what catches `<Checkout payment=\"card\" managed>`: under managed\n * the provider collects the card in its own frame, so the Elements surfaces are refused.\n *\n * Per-TAG, and a SUBSET rather than a partition, on purpose:\n * - per-tag, because `managed` is a property of one checkout (it's a flag on the Checkout Session\n * the tag opens), so a funnel may legitimately hold a plain `element` paywall beside a managed\n * `embedded` one, and validating either against the other's profile would invent a failure;\n * - a subset, because `surfaces` keeps meaning \"every surface this funnel mounts\". Leaving the\n * managed ones in it means the base-profile check is byte-for-byte what it was, and a gate\n * that predates this field still validates them (against the wider profile — the old, lenient\n * answer, never a newly-invented block).\n *\n * OPTIONAL only for shape tolerance: the scan always emits it, but a `CheckoutUsageScan` literal\n * written before this field existed (the scaffold's empty default, an older manifest) still\n * type-checks and still means \"nothing managed\".\n */\n managedSurfaces?: string[]\n /**\n * Off-session upsell KINDS the author WROTE on `<UpsellButton kind=\"…\">`, deduped. A tag with no `kind` records NOTHING: the kind is derived at attempt time\n * from the offering's interval, and the slot's catalog assignment isn't in the source the scan\n * reads. Validated with `validateUpsell(provider, paywallSurface, kind)` — so this list is\n * \"kinds to check\", not \"kinds this funnel charges\".\n */\n upsellKinds: string[]\n /** True if any surface/kind/`managed` was a non-literal (computed) value → the gate WARNS, never blocks, on it. */\n dynamic: boolean\n}\n\n/**\n * Index of the unescaped quote that CLOSES the string opened at `open`, or -1 when there isn't one.\n *\n * `multiline` is the language rule, not a tolerance: a `'`/`\"` string may not contain a raw newline,\n * so one that reaches the end of its line is not a string at all — only a backtick spans lines. A\n * backslash escapes the next character (including a newline: a line continuation is legal).\n */\nfunction closingQuote(src: string, open: number, multiline: boolean): number {\n const q = src[open]\n for (let i = open + 1; i < src.length; i++) {\n const c = src[i]\n if (c === '\\\\') { i++; continue }\n if (c === q) return i\n if (!multiline && c === '\\n') return -1\n }\n return -1\n}\n\n/** Characters an identifier or number can END with — JS never opens a string directly after one. */\nconst WORD_CHAR = /[\\w$]/\n\n/**\n * Blank comment + string/template CONTENTS (keep quote delimiters + length) so scans don't fire\n * inside them.\n *\n * The hard part is that this runs over TSX, where the same characters are BOTH string delimiters and\n * ordinary prose: `you're`, `don't`, `let's`, `3\" tall` are JSX TEXT. A masker that opens a string on\n * every quote it meets opens one it can never close — and then blanks everything after it, so the\n * scan silently stops seeing the file at its first contraction. That is a false NEGATIVE, and it was\n * the COMMON case, not an exotic one: quiz copy is contractions all the way down. (Found in\n * gracen-onboarding, where `each other's` in a benefits list hid the `<UpsellButton>` 100 lines\n * below it from the publish capability gate.)\n *\n * Fixing it does NOT mean parsing JSX. This stays a linear character scan; two local rules are\n * enough, and both are one-sided — a quote we wrongly DECLINE to open only ever leaves prose\n * unmasked, never a real string, so the worst case stays a missed ref rather than an invented one:\n *\n * 1. A `'`/`\"` directly after an identifier character does not open a string. The `'` in `don't`\n * follows an `n`; real JS only opens a string after an operator, a delimiter, or whitespace.\n * Backticks are EXEMPT — `` styled.div`…` `` / `` css`…` `` are tagged templates, and those do\n * legitimately follow a word character.\n * 2. A `'`/`\"` with no unescaped partner before the end of its line does not open a string, per\n * {@link closingQuote}. Not a guess: the alternative is a file that doesn't compile.\n *\n * Rule 2 is what bounds the damage of anything the pair still misjudges to ONE LINE — quote parity\n * can no longer leak into the rest of the file, which is the property that failed here.\n *\n * KNOWN LIMITS, stated rather than papered over. These blank MORE than they should — a missed ref,\n * the direction this scan has always been willing to fail in:\n * - an apostrophe in JSX text that is preceded by whitespace AND pairs with a same-kind quote later\n * on the SAME line still blanks the span between the two, so `<p>the '90s</p><Checkout … />`\n * written on one line stays invisible. Put the tag on its own line (every formatter already does).\n * - regex literals aren't recognised, so the `'` in `/'/g` reads as that same lone apostrophe —\n * contained to its line by rule 2.\n * - a stray backtick in JSX prose still runs to the file's next backtick. Rule 1 can't help it\n * (tagged templates) and rule 2 must not (template literals span lines); a backtick in prose is\n * rare in a way an apostrophe is not.\n *\n * And the one that fails the OTHER way, so it is worth naming: rule 1 declines a string that a\n * keyword opens with no space after it — `return'…'`, `case'…':`. Legal JS that nothing writes and\n * no formatter emits, but its contents would go unmasked, so a `useOffering('x')` spelled inside one\n * would be counted. That is the only shape here that can invent a ref rather than miss one.\n */\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 const multiline = c === '`'\n // Rule 1, then rule 2. A quote that fails either test is left exactly as it stands: an\n // ordinary character in the mask, which is all an apostrophe in prose ever was.\n if (!multiline && i > 0 && WORD_CHAR.test(src[i - 1])) { i++; continue }\n const close = closingQuote(src, i, multiline)\n if (close === -1) { i++; continue }\n for (let k = i + 1; k < close; k++) if (src[k] !== '\\n') out[k] = ' '\n i = close + 1 // both delimiters kept — literalAt() reads the value back off the ORIGINAL\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\n/**\n * Read a BOOLEAN JSX attribute (`managed`) inside the opening-tag span `[start, end)`. Returns the\n * literal when one is written, `{ dynamic: true }` when the value is a runtime decision, and null\n * when the attribute is absent.\n *\n * Only the three LITERAL spellings are read — bare (`managed`), `managed={true}`, `managed={false}`.\n * Anything else (`managed={expr}`, `managed=\"…\"`) is reported dynamic rather than guessed at: a\n * wrong guess in the \"on\" direction invents a publish block on a funnel that never asked for the\n * variant, and a wrong guess in the \"off\" direction is the hole this field exists to close. Dynamic\n * costs a warning, which is the relaxation discipline the rest of this scan already follows.\n *\n * Unlike {@link readAttr} this walks the span at BRACE DEPTH 0 instead of matching the name with a\n * regex, because a valueless attribute has no `=` to anchor on and the bare word occurs in two\n * places that are NOT the attribute: inside an attribute expression (`onSuccess={() => managed(x)}`)\n * and as the tail of a longer name (`data-managed`). Depth tracking rules out the first; requiring\n * whitespace before the name and a non-name character after it rules out the second. A `managed`\n * inside a string VALUE never reaches here — the mask blanked it.\n */\nfunction readBoolAttr(\n masked: string,\n start: number,\n end: number,\n name: string,\n): { value?: boolean; dynamic?: boolean } | null {\n let depth = 0\n for (let i = start; i < end; i++) {\n const c = masked[i]\n if (c === '{') { depth++; continue }\n if (c === '}') { if (depth > 0) depth--; continue }\n if (depth !== 0 || c !== name[0] || masked.slice(i, i + name.length) !== name) continue\n // JSX separates attributes with whitespace; anything else before/after means a different name.\n if (i > start && !/\\s/.test(masked[i - 1])) continue\n let j = i + name.length\n if (/[\\w$:-]/.test(masked[j] ?? ' ')) continue\n j = skipWs(masked, j)\n if (masked[j] !== '=') return { value: true } // bare attribute → true\n j = skipWs(masked, j + 1)\n if (masked[j] !== '{') return { dynamic: true } // a JSX string value, not a boolean literal\n const literal = /^\\s*(true|false)\\s*\\}/.exec(masked.slice(j + 1, end))\n return literal ? { value: literal[1] === 'true' } : { dynamic: true }\n }\n return null\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 managedSeen = new Set<string>()\n const managedSurfaces: 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 addManaged = (v: string) => { if (v && !managedSeen.has(v)) { managedSeen.add(v); managedSurfaces.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|CheckoutButton|UpsellButton offering=\"…\"> — the `offering` attribute value.\n // Longest name first: `\\b` after `Checkout` can't match `<CheckoutButton`, and the alternation\n // has to reach the longer branch. (`<UpsellOffer>` — a component several templates define — is\n // rejected by the same `\\b`, which is why it isn't in this list and must never be matched.)\n for (const m of masked.matchAll(\n /<(?:CheckoutButton|Checkout|UpsellButton)\\b[^>]*?\\boffering\\s*=\\s*/g,\n )) {\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-SURFACE usage on every checkout tag. 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(/<(CheckoutButton|Checkout|UpsellButton)\\b/g)) {\n const comp = m[1]\n const isUpsell = comp === 'UpsellButton'\n // A BUTTON presents the payment somewhere else, and `opens` (default `'sheet'`) says where.\n // `<Checkout>` has no such prop: it renders the payment in the page, so the container is the\n // page and there is nothing to record for it.\n const isButton = comp === 'CheckoutButton' || comp === 'UpsellButton'\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 // `managed` (Stripe Managed Payments) is read PER TAG, because it selects a different provider\n // profile for this checkout alone — the surfaces below belong to whichever profile THIS tag\n // asked for. A computed `managed` can't claim them for the variant (that would block a funnel\n // that may well be running plain Stripe), so it only sets the dynamic flag → the gate warns.\n const managed = readBoolAttr(masked, tagStart, tagEnd, 'managed')\n if (managed?.dynamic) checkoutDynamic = true\n const isManaged = managed?.value === true\n const tagSurfaces: string[] = []\n\n // A recoverySurface re-collects a card ON-SESSION → it's a purchase surface too.\n const recovery = attr('recoverySurface')\n if (recovery?.value) tagSurfaces.push(recovery.value)\n else if (recovery?.dynamic) checkoutDynamic = true\n\n if (isUpsell) {\n // Record only a kind the AUTHOR wrote. An absent `kind` is no longer 'subscription' — it is\n // DERIVED at attempt time from the offering's own interval (`upsellKindFor`), which the scan\n // cannot see: the slot's catalog assignment lives in the project, not in the source. Guessing\n // 'subscription' here would make the publish gate reject a bare one-time upsell on a provider\n // that can't stack subscriptions (Paddle: `offSessionUpsells: ['one-time','upgrade']`) — a\n // funnel the runtime would charge correctly. Recording nothing lets the gate validate only\n // what it actually knows.\n const kind = attr('kind')\n if (kind?.value) addKind(kind.value)\n else if (kind?.dynamic) checkoutDynamic = true\n }\n\n // The one-word `surface` spelling, on `<Checkout>` only. A tag carrying it takes that path at\n // runtime and the axes are typed `never` beside it, so reading it INSTEAD of them mirrors what\n // will actually render. `'auto'` passes through for the gate to resolve, as before.\n const surface = comp === 'Checkout' ? attr('surface') : null\n if (surface?.dynamic) checkoutDynamic = true\n else if (surface?.value) tagSurfaces.push(surface.value)\n else {\n // The AXES. Both are flat string attributes on purpose — an object-valued prop would read as\n // `{ dynamic: true }` here and degrade the publish gate to a warning, which is exactly why\n // the API grew two scalars instead of one `presentation={{ … }}`.\n //\n // WHERE. Only a BUTTON has this question; `<Checkout>` renders in the page, and the page is\n // not a surface anyone has to support.\n const opens = isButton ? attr('opens') : null\n // Can't tell our overlay from a hand-off to the provider → warn rather than guess: recording\n // the wrong one either invents a block or hides a real one.\n if (opens?.dynamic) checkoutDynamic = true\n else if (opens?.value === 'popup' || opens?.value === 'redirect') {\n // The provider paints the whole thing — no rendering of ours to record beside it.\n tagSurfaces.push(opens.value)\n } else {\n // Our sheet: it has to exist on the provider AND host what we put in it, so both words are\n // recorded. An UPSELL's default recovery container is provider-derived (`sheet` where there\n // is one, else the provider's best), so it records nothing here — unchanged from before\n // `recover` existed, and the gate never had a word for it.\n if (isButton && !isUpsell) tagSurfaces.push('sheet')\n // WHAT the provider renders. Absent or `'auto'` records `'auto'`, which the gate resolves\n // per provider + device — the same treatment `surface=\"auto\"` has always had. On an upsell\n // an absent `payment` means \"whatever the sheet's default is\", which is likewise nothing to\n // record; only a NAMED payment UI is a claim about the provider.\n const payment = attr('payment')\n if (payment?.dynamic) checkoutDynamic = true\n else if (payment?.value) {\n tagSurfaces.push(\n PAYMENT_RENDERINGS[payment.value as Exclude<PaymentMode, 'auto'>] ?? payment.value,\n )\n } else if (!isUpsell) tagSurfaces.push('auto')\n }\n }\n\n for (const surface of tagSurfaces) {\n addSurface(surface)\n if (isManaged) addManaged(surface)\n }\n }\n }\n return { slots: order, dynamic, checkout: { surfaces, managedSurfaces, 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"]}
|
|
1
|
+
{"version":3,"sources":["../src/manifest/offering-refs.ts","../src/flow/scaffold.ts"],"names":["PAYMENT_RENDERINGS","surface"],"mappings":"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","file":"manifest-entry.cjs","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>` /\n * `<CheckoutButton>` / `<UpsellButton>`.\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\n// The one place the payment WORDS become surface words. Imported rather than re-listed: the runtime\n// maps `payment` through this exact table (see `resolvePresentation` in ../commerce/checkout), so the\n// publish gate can't drift from what will actually be requested. Both modules are React-free and ship\n// through the same manifest entry.\nimport { PAYMENT_RENDERINGS, type PaymentMode } from '../commerce/capabilities'\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 * Checkout tags that name NO offering — an authoring error the build fails on.\n *\n * This is the hole the `product` → `offering` rename left open. `<Checkout product=\"plan\">` is a\n * tag with no `offering` attribute: it contributes no ref above, so the slot never reaches\n * validation and the publish gate passes vacuously — then `CheckoutImpl` reads `offering` as\n * `undefined` and the charge resolves to nothing. Nothing else in the deploy path sees it: the\n * funnel build is a vite/esbuild transform, so types are stripped rather than checked.\n *\n * A tag carrying a SPREAD (`<Checkout {...props} />`) is not reported — the offering may well\n * arrive through it, and the scan cannot see inside. That is the same relaxation the rest of this\n * module applies to anything it cannot statically enumerate.\n */\n offeringless: OfferinglessCheckout[]\n}\n\n/** One checkout tag that names no offering — located for an operator-legible build error. */\nexport interface OfferinglessCheckout {\n /** Source path as given to the scan. */\n file: string\n /** 1-based line of the tag's `<`. */\n line: number\n /** `Checkout` | `CheckoutButton` | `UpsellButton`. */\n component: string\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, in the ONE vocabulary the\n * publish gate and the wire both speak ({@link CheckoutSurface}). Validated with\n * `validateCheckout(provider, surface)`.\n *\n * Captured from three spellings, all of which mean the same thing to the gate:\n * - the author axes — `payment` (WHAT the provider renders: `wallets`→`express`, `card`→`element`,\n * `full`→`embedded`, absent/`auto`→`auto`) and `opens` (WHERE it is presented: recorded as\n * itself). A `<CheckoutButton>` records BOTH, because both have to be possible: our sheet has to\n * exist on the provider, AND the provider has to be able to render what we put in it.\n * - the deprecated `surface=\"…\"`, recorded as itself. A tag carrying it takes the legacy path at\n * runtime, so the scan reads it INSTEAD of the axes rather than as well as them — same rule,\n * same result, one place.\n * - the on-session re-collect surface `recoverySurface=\"…\"` (a recovery\n * re-collects a card → it IS a purchase surface the provider must support).\n *\n * `'auto'` is passed through un-expanded, as it always has been: it is a per-device PREFERENCE and\n * only the gate knows the provider, so the gate resolves it (see `promote-gate.ts`).\n */\n surfaces: string[]\n /**\n * The SUBSET of {@link surfaces} whose tag also carried `managed` — Stripe **Managed Payments**,\n * which resolves the provider to a DIFFERENT capability profile (`profileFor(provider, { managed:\n * true })`) with a narrower surface set. Validated with `validateCheckout(provider, surface, {\n * managed: true })`, which is what catches `<Checkout payment=\"card\" managed>`: under managed\n * the provider collects the card in its own frame, so the Elements surfaces are refused.\n *\n * Per-TAG, and a SUBSET rather than a partition, on purpose:\n * - per-tag, because `managed` is a property of one checkout (it's a flag on the Checkout Session\n * the tag opens), so a funnel may legitimately hold a plain `element` paywall beside a managed\n * `embedded` one, and validating either against the other's profile would invent a failure;\n * - a subset, because `surfaces` keeps meaning \"every surface this funnel mounts\". Leaving the\n * managed ones in it means the base-profile check is byte-for-byte what it was, and a gate\n * that predates this field still validates them (against the wider profile — the old, lenient\n * answer, never a newly-invented block).\n *\n * OPTIONAL only for shape tolerance: the scan always emits it, but a `CheckoutUsageScan` literal\n * written before this field existed (the scaffold's empty default, an older manifest) still\n * type-checks and still means \"nothing managed\".\n */\n managedSurfaces?: string[]\n /**\n * Off-session upsell KINDS the author WROTE on `<UpsellButton kind=\"…\">`, deduped. A tag with no `kind` records NOTHING: the kind is derived at attempt time\n * from the offering's interval, and the slot's catalog assignment isn't in the source the scan\n * reads. Validated with `validateUpsell(provider, paywallSurface, kind)` — so this list is\n * \"kinds to check\", not \"kinds this funnel charges\".\n */\n upsellKinds: string[]\n /** True if any surface/kind/`managed` was a non-literal (computed) value → the gate WARNS, never blocks, on it. */\n dynamic: boolean\n}\n\n/**\n * Index of the unescaped quote that CLOSES the string opened at `open`, or -1 when there isn't one.\n *\n * `multiline` is the language rule, not a tolerance: a `'`/`\"` string may not contain a raw newline,\n * so one that reaches the end of its line is not a string at all — only a backtick spans lines. A\n * backslash escapes the next character (including a newline: a line continuation is legal).\n */\nfunction closingQuote(src: string, open: number, multiline: boolean): number {\n const q = src[open]\n for (let i = open + 1; i < src.length; i++) {\n const c = src[i]\n if (c === '\\\\') { i++; continue }\n if (c === q) return i\n if (!multiline && c === '\\n') return -1\n }\n return -1\n}\n\n/** Characters an identifier or number can END with — JS never opens a string directly after one. */\nconst WORD_CHAR = /[\\w$]/\n\n/**\n * Blank comment + string/template CONTENTS (keep quote delimiters + length) so scans don't fire\n * inside them.\n *\n * The hard part is that this runs over TSX, where the same characters are BOTH string delimiters and\n * ordinary prose: `you're`, `don't`, `let's`, `3\" tall` are JSX TEXT. A masker that opens a string on\n * every quote it meets opens one it can never close — and then blanks everything after it, so the\n * scan silently stops seeing the file at its first contraction. That is a false NEGATIVE, and it was\n * the COMMON case, not an exotic one: quiz copy is contractions all the way down. (Found in\n * gracen-onboarding, where `each other's` in a benefits list hid the `<UpsellButton>` 100 lines\n * below it from the publish capability gate.)\n *\n * Fixing it does NOT mean parsing JSX. This stays a linear character scan; two local rules are\n * enough, and both are one-sided — a quote we wrongly DECLINE to open only ever leaves prose\n * unmasked, never a real string, so the worst case stays a missed ref rather than an invented one:\n *\n * 1. A `'`/`\"` directly after an identifier character does not open a string. The `'` in `don't`\n * follows an `n`; real JS only opens a string after an operator, a delimiter, or whitespace.\n * Backticks are EXEMPT — `` styled.div`…` `` / `` css`…` `` are tagged templates, and those do\n * legitimately follow a word character.\n * 2. A `'`/`\"` with no unescaped partner before the end of its line does not open a string, per\n * {@link closingQuote}. Not a guess: the alternative is a file that doesn't compile.\n *\n * Rule 2 is what bounds the damage of anything the pair still misjudges to ONE LINE — quote parity\n * can no longer leak into the rest of the file, which is the property that failed here.\n *\n * KNOWN LIMITS, stated rather than papered over. These blank MORE than they should — a missed ref,\n * the direction this scan has always been willing to fail in:\n * - an apostrophe in JSX text that is preceded by whitespace AND pairs with a same-kind quote later\n * on the SAME line still blanks the span between the two, so `<p>the '90s</p><Checkout … />`\n * written on one line stays invisible. Put the tag on its own line (every formatter already does).\n * - regex literals aren't recognised, so the `'` in `/'/g` reads as that same lone apostrophe —\n * contained to its line by rule 2.\n * - a stray backtick in JSX prose still runs to the file's next backtick. Rule 1 can't help it\n * (tagged templates) and rule 2 must not (template literals span lines); a backtick in prose is\n * rare in a way an apostrophe is not.\n *\n * And the one that fails the OTHER way, so it is worth naming: rule 1 declines a string that a\n * keyword opens with no space after it — `return'…'`, `case'…':`. Legal JS that nothing writes and\n * no formatter emits, but its contents would go unmasked, so a `useOffering('x')` spelled inside one\n * would be counted. That is the only shape here that can invent a ref rather than miss one.\n */\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 const multiline = c === '`'\n // Rule 1, then rule 2. A quote that fails either test is left exactly as it stands: an\n // ordinary character in the mask, which is all an apostrophe in prose ever was.\n if (!multiline && i > 0 && WORD_CHAR.test(src[i - 1])) { i++; continue }\n const close = closingQuote(src, i, multiline)\n if (close === -1) { i++; continue }\n for (let k = i + 1; k < close; k++) if (src[k] !== '\\n') out[k] = ' '\n i = close + 1 // both delimiters kept — literalAt() reads the value back off the ORIGINAL\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\n/**\n * Read a BOOLEAN JSX attribute (`managed`) inside the opening-tag span `[start, end)`. Returns the\n * literal when one is written, `{ dynamic: true }` when the value is a runtime decision, and null\n * when the attribute is absent.\n *\n * Only the three LITERAL spellings are read — bare (`managed`), `managed={true}`, `managed={false}`.\n * Anything else (`managed={expr}`, `managed=\"…\"`) is reported dynamic rather than guessed at: a\n * wrong guess in the \"on\" direction invents a publish block on a funnel that never asked for the\n * variant, and a wrong guess in the \"off\" direction is the hole this field exists to close. Dynamic\n * costs a warning, which is the relaxation discipline the rest of this scan already follows.\n *\n * Unlike {@link readAttr} this walks the span at BRACE DEPTH 0 instead of matching the name with a\n * regex, because a valueless attribute has no `=` to anchor on and the bare word occurs in two\n * places that are NOT the attribute: inside an attribute expression (`onSuccess={() => managed(x)}`)\n * and as the tail of a longer name (`data-managed`). Depth tracking rules out the first; requiring\n * whitespace before the name and a non-name character after it rules out the second. A `managed`\n * inside a string VALUE never reaches here — the mask blanked it.\n */\nfunction readBoolAttr(\n masked: string,\n start: number,\n end: number,\n name: string,\n): { value?: boolean; dynamic?: boolean } | null {\n let depth = 0\n for (let i = start; i < end; i++) {\n const c = masked[i]\n if (c === '{') { depth++; continue }\n if (c === '}') { if (depth > 0) depth--; continue }\n if (depth !== 0 || c !== name[0] || masked.slice(i, i + name.length) !== name) continue\n // JSX separates attributes with whitespace; anything else before/after means a different name.\n if (i > start && !/\\s/.test(masked[i - 1])) continue\n let j = i + name.length\n if (/[\\w$:-]/.test(masked[j] ?? ' ')) continue\n j = skipWs(masked, j)\n if (masked[j] !== '=') return { value: true } // bare attribute → true\n j = skipWs(masked, j + 1)\n if (masked[j] !== '{') return { dynamic: true } // a JSX string value, not a boolean literal\n const literal = /^\\s*(true|false)\\s*\\}/.exec(masked.slice(j + 1, end))\n return literal ? { value: literal[1] === 'true' } : { dynamic: true }\n }\n return null\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 managedSeen = new Set<string>()\n const managedSurfaces: string[] = []\n const kindSeen = new Set<string>()\n const upsellKinds: string[] = []\n let checkoutDynamic = false\n const offeringless: OfferinglessCheckout[] = []\n const addSurface = (v: string) => { if (v && !surfaceSeen.has(v)) { surfaceSeen.add(v); surfaces.push(v) } }\n const addManaged = (v: string) => { if (v && !managedSeen.has(v)) { managedSeen.add(v); managedSurfaces.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|CheckoutButton|UpsellButton offering=\"…\"> — the `offering` attribute value.\n // Longest name first: `\\b` after `Checkout` can't match `<CheckoutButton`, and the alternation\n // has to reach the longer branch. (`<UpsellOffer>` — a component several templates define — is\n // rejected by the same `\\b`, which is why it isn't in this list and must never be matched.)\n for (const m of masked.matchAll(\n /<(?:CheckoutButton|Checkout|UpsellButton)\\b[^>]*?\\boffering\\s*=\\s*/g,\n )) {\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-SURFACE usage on every checkout tag. 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(/<(CheckoutButton|Checkout|UpsellButton)\\b/g)) {\n const comp = m[1]\n const isUpsell = comp === 'UpsellButton'\n // A BUTTON presents the payment somewhere else, and `opens` (default `'sheet'`) says where.\n // `<Checkout>` has no such prop: it renders the payment in the page, so the container is the\n // page and there is nothing to record for it.\n const isButton = comp === 'CheckoutButton' || comp === 'UpsellButton'\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\n // NO OFFERING NAMED. Recorded here rather than in the ref loop above because only this loop\n // parses the tag's real attribute span — the ref loop's `[^>]*?` stops at the first `>`, which\n // an arrow-function attribute (`onSuccess={() => …}`) supplies long before the tag ends. A\n // spread could carry the offering invisibly, so a tag with one is left alone.\n if (!attr('offering') && !/\\{\\s*\\.\\.\\./.test(masked.slice(tagStart, tagEnd))) {\n offeringless.push({\n file: path,\n line: source.slice(0, m.index!).split('\\n').length,\n component: comp,\n })\n }\n // `managed` (Stripe Managed Payments) is read PER TAG, because it selects a different provider\n // profile for this checkout alone — the surfaces below belong to whichever profile THIS tag\n // asked for. A computed `managed` can't claim them for the variant (that would block a funnel\n // that may well be running plain Stripe), so it only sets the dynamic flag → the gate warns.\n const managed = readBoolAttr(masked, tagStart, tagEnd, 'managed')\n if (managed?.dynamic) checkoutDynamic = true\n const isManaged = managed?.value === true\n const tagSurfaces: string[] = []\n\n // A recoverySurface re-collects a card ON-SESSION → it's a purchase surface too.\n const recovery = attr('recoverySurface')\n if (recovery?.value) tagSurfaces.push(recovery.value)\n else if (recovery?.dynamic) checkoutDynamic = true\n\n if (isUpsell) {\n // Record only a kind the AUTHOR wrote. An absent `kind` is no longer 'subscription' — it is\n // DERIVED at attempt time from the offering's own interval (`upsellKindFor`), which the scan\n // cannot see: the slot's catalog assignment lives in the project, not in the source. Guessing\n // 'subscription' here would make the publish gate reject a bare one-time upsell on a provider\n // that can't stack subscriptions (Paddle: `offSessionUpsells: ['one-time','upgrade']`) — a\n // funnel the runtime would charge correctly. Recording nothing lets the gate validate only\n // what it actually knows.\n const kind = attr('kind')\n if (kind?.value) addKind(kind.value)\n else if (kind?.dynamic) checkoutDynamic = true\n }\n\n // The one-word `surface` spelling, on `<Checkout>` only. A tag carrying it takes that path at\n // runtime and the axes are typed `never` beside it, so reading it INSTEAD of them mirrors what\n // will actually render. `'auto'` passes through for the gate to resolve, as before.\n const surface = comp === 'Checkout' ? attr('surface') : null\n if (surface?.dynamic) checkoutDynamic = true\n else if (surface?.value) tagSurfaces.push(surface.value)\n else {\n // The AXES. Both are flat string attributes on purpose — an object-valued prop would read as\n // `{ dynamic: true }` here and degrade the publish gate to a warning, which is exactly why\n // the API grew two scalars instead of one `presentation={{ … }}`.\n //\n // WHERE. Only a BUTTON has this question; `<Checkout>` renders in the page, and the page is\n // not a surface anyone has to support.\n const opens = isButton ? attr('opens') : null\n // Can't tell our overlay from a hand-off to the provider → warn rather than guess: recording\n // the wrong one either invents a block or hides a real one.\n if (opens?.dynamic) checkoutDynamic = true\n else if (opens?.value === 'popup' || opens?.value === 'redirect') {\n // The provider paints the whole thing — no rendering of ours to record beside it.\n tagSurfaces.push(opens.value)\n } else {\n // Our sheet: it has to exist on the provider AND host what we put in it, so both words are\n // recorded. An UPSELL's default recovery container is provider-derived (`sheet` where there\n // is one, else the provider's best), so it records nothing here — unchanged from before\n // `recover` existed, and the gate never had a word for it.\n if (isButton && !isUpsell) tagSurfaces.push('sheet')\n // WHAT the provider renders. Absent or `'auto'` records `'auto'`, which the gate resolves\n // per provider + device — the same treatment `surface=\"auto\"` has always had. On an upsell\n // an absent `payment` means \"whatever the sheet's default is\", which is likewise nothing to\n // record; only a NAMED payment UI is a claim about the provider.\n const payment = attr('payment')\n if (payment?.dynamic) checkoutDynamic = true\n else if (payment?.value) {\n tagSurfaces.push(\n PAYMENT_RENDERINGS[payment.value as Exclude<PaymentMode, 'auto'>] ?? payment.value,\n )\n } else if (!isUpsell) tagSurfaces.push('auto')\n }\n }\n\n for (const surface of tagSurfaces) {\n addSurface(surface)\n if (isManaged) addManaged(surface)\n }\n }\n }\n return { slots: order, dynamic, checkout: { surfaces, managedSurfaces, upsellKinds, dynamic: checkoutDynamic }, offeringless }\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"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { al as CheckoutUsageScan } from './manifest-
|
|
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, am as FunnelPage, b as FunnelSnapshot, q as Gate, r as Interval, M as ManifestEdge, s as ManifestPage, t as ManifestValidation, u as Money, O as Offering, e as OfferingInput, an as OfferingRefScan, ao as OfferingSlot, P as PageMeta, y as PageType, z as Predicate, H as ResolvedOffering, J as Route, R as RuntimeExperiment, Q as assignVariant, T as bucketingSeed, X as buildCatalog, Y as compileManifest, Z as currencyExponent, _ as defineFunnel, a0 as entryGuard, a1 as evaluateCondition, a5 as formatMoney, a6 as formatOffering, ap as funnelCatalogKeys, aa as isRtl, ab as isVariantKey, aq as normalizeOfferings, ae as pageMeta, af as parseSlotKey, ai as resolveLocale, aj as resolveOffering, ar as scanOfferingRefs, as as weightsOf } from './manifest-
|
|
1
|
+
import { al as CheckoutUsageScan } from './manifest-Dp1oRvqp.cjs';
|
|
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, am as FunnelPage, b as FunnelSnapshot, q as Gate, r as Interval, M as ManifestEdge, s as ManifestPage, t as ManifestValidation, u as Money, O as Offering, e as OfferingInput, an as OfferingRefScan, ao as OfferingSlot, P as PageMeta, y as PageType, z as Predicate, H as ResolvedOffering, J as Route, R as RuntimeExperiment, Q as assignVariant, T as bucketingSeed, X as buildCatalog, Y as compileManifest, Z as currencyExponent, _ as defineFunnel, a0 as entryGuard, a1 as evaluateCondition, a5 as formatMoney, a6 as formatOffering, ap as funnelCatalogKeys, aa as isRtl, ab as isVariantKey, aq as normalizeOfferings, ae as pageMeta, af as parseSlotKey, ai as resolveLocale, aj as resolveOffering, ar as scanOfferingRefs, as as weightsOf } from './manifest-Dp1oRvqp.cjs';
|
|
3
3
|
import { C as CheckoutError, b as CheckoutIntent, U as UpsellKind, d as CheckoutSurface } from './capabilities-BtnJi2JD.cjs';
|
|
4
4
|
export { a as CheckoutErrorCategory, c as CheckoutProvider, D as DeviceClass, H as HostedPaymentMode, I as INLINE_SURFACES, M as MANAGED_PROFILES, O as OffSessionReliability, e as OpensIn, P as PAYMENT_RENDERINGS, f as PROVIDER_HOSTED_SURFACES, g as PROVIDER_PROFILES, h as PaymentMode, i as PaymentRendering, j as ProviderProfile, k as ProviderVariant, S as SurfaceCapability, l as SurfacePreference, T as TrialShape, V as ValidationResult, m as checkoutError, n as isCompleteSurface, o as isInlineSurface, p as isMerchantOfRecord, q as isOrchestrator, r as isProviderHostedSurface, s as isRenderableSurface, t as profileFor, u as renderableSurfaces, v as resolvePayment, w as resolveSurface, x as supportsManaged, y as surfacesFor, E as trialShapesFor, z as validateCheckout, F as validateTrial, A as validateTrialSurface, B as validateUpsell } from './capabilities-BtnJi2JD.cjs';
|
|
5
5
|
import 'react';
|
|
@@ -128,6 +128,19 @@ interface CheckoutSettlement {
|
|
|
128
128
|
amountMinor: number;
|
|
129
129
|
currency: string;
|
|
130
130
|
productKey: string;
|
|
131
|
+
/**
|
|
132
|
+
* HOW this settlement acquired the customer, decided SERVER-side by the flow that ran.
|
|
133
|
+
*
|
|
134
|
+
* The browser must not infer this from catalog config: whether a trial is free or paid is a
|
|
135
|
+
* property of the flow the server selected. A trial settlement emits `subscription.trial_started`
|
|
136
|
+
* instead of the `purchase.complete` + `subscription.created` pair — one event for one
|
|
137
|
+
* settlement, and a taxonomy that can tell a trial apart from an outright sale.
|
|
138
|
+
* Absent/`direct` = an ordinary charge.
|
|
139
|
+
*
|
|
140
|
+
* `amountMinor` stays the money that ACTUALLY moved either way — 0 for a free trial, the fee
|
|
141
|
+
* for a paid one — so whatever event fires carries a value nobody has to caveat.
|
|
142
|
+
*/
|
|
143
|
+
acquisition?: 'direct' | 'free_trial' | 'paid_trial';
|
|
131
144
|
}
|
|
132
145
|
/** Driver confirms in the browser with the provider's JS (Stripe Elements et al.). */
|
|
133
146
|
interface StepClientConfirm {
|
package/dist/manifest-entry.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { al as CheckoutUsageScan } from './manifest-
|
|
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, am as FunnelPage, b as FunnelSnapshot, q as Gate, r as Interval, M as ManifestEdge, s as ManifestPage, t as ManifestValidation, u as Money, O as Offering, e as OfferingInput, an as OfferingRefScan, ao as OfferingSlot, P as PageMeta, y as PageType, z as Predicate, H as ResolvedOffering, J as Route, R as RuntimeExperiment, Q as assignVariant, T as bucketingSeed, X as buildCatalog, Y as compileManifest, Z as currencyExponent, _ as defineFunnel, a0 as entryGuard, a1 as evaluateCondition, a5 as formatMoney, a6 as formatOffering, ap as funnelCatalogKeys, aa as isRtl, ab as isVariantKey, aq as normalizeOfferings, ae as pageMeta, af as parseSlotKey, ai as resolveLocale, aj as resolveOffering, ar as scanOfferingRefs, as as weightsOf } from './manifest-
|
|
1
|
+
import { al as CheckoutUsageScan } from './manifest-Dp1oRvqp.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, am as FunnelPage, b as FunnelSnapshot, q as Gate, r as Interval, M as ManifestEdge, s as ManifestPage, t as ManifestValidation, u as Money, O as Offering, e as OfferingInput, an as OfferingRefScan, ao as OfferingSlot, P as PageMeta, y as PageType, z as Predicate, H as ResolvedOffering, J as Route, R as RuntimeExperiment, Q as assignVariant, T as bucketingSeed, X as buildCatalog, Y as compileManifest, Z as currencyExponent, _ as defineFunnel, a0 as entryGuard, a1 as evaluateCondition, a5 as formatMoney, a6 as formatOffering, ap as funnelCatalogKeys, aa as isRtl, ab as isVariantKey, aq as normalizeOfferings, ae as pageMeta, af as parseSlotKey, ai as resolveLocale, aj as resolveOffering, ar as scanOfferingRefs, as as weightsOf } from './manifest-Dp1oRvqp.js';
|
|
3
3
|
import { C as CheckoutError, b as CheckoutIntent, U as UpsellKind, d as CheckoutSurface } from './capabilities-BtnJi2JD.js';
|
|
4
4
|
export { a as CheckoutErrorCategory, c as CheckoutProvider, D as DeviceClass, H as HostedPaymentMode, I as INLINE_SURFACES, M as MANAGED_PROFILES, O as OffSessionReliability, e as OpensIn, P as PAYMENT_RENDERINGS, f as PROVIDER_HOSTED_SURFACES, g as PROVIDER_PROFILES, h as PaymentMode, i as PaymentRendering, j as ProviderProfile, k as ProviderVariant, S as SurfaceCapability, l as SurfacePreference, T as TrialShape, V as ValidationResult, m as checkoutError, n as isCompleteSurface, o as isInlineSurface, p as isMerchantOfRecord, q as isOrchestrator, r as isProviderHostedSurface, s as isRenderableSurface, t as profileFor, u as renderableSurfaces, v as resolvePayment, w as resolveSurface, x as supportsManaged, y as surfacesFor, E as trialShapesFor, z as validateCheckout, F as validateTrial, A as validateTrialSurface, B as validateUpsell } from './capabilities-BtnJi2JD.js';
|
|
5
5
|
import 'react';
|
|
@@ -128,6 +128,19 @@ interface CheckoutSettlement {
|
|
|
128
128
|
amountMinor: number;
|
|
129
129
|
currency: string;
|
|
130
130
|
productKey: string;
|
|
131
|
+
/**
|
|
132
|
+
* HOW this settlement acquired the customer, decided SERVER-side by the flow that ran.
|
|
133
|
+
*
|
|
134
|
+
* The browser must not infer this from catalog config: whether a trial is free or paid is a
|
|
135
|
+
* property of the flow the server selected. A trial settlement emits `subscription.trial_started`
|
|
136
|
+
* instead of the `purchase.complete` + `subscription.created` pair — one event for one
|
|
137
|
+
* settlement, and a taxonomy that can tell a trial apart from an outright sale.
|
|
138
|
+
* Absent/`direct` = an ordinary charge.
|
|
139
|
+
*
|
|
140
|
+
* `amountMinor` stays the money that ACTUALLY moved either way — 0 for a free trial, the fee
|
|
141
|
+
* for a paid one — so whatever event fires carries a value nobody has to caveat.
|
|
142
|
+
*/
|
|
143
|
+
acquisition?: 'direct' | 'free_trial' | 'paid_trial';
|
|
131
144
|
}
|
|
132
145
|
/** Driver confirms in the browser with the provider's JS (Stripe Elements et al.). */
|
|
133
146
|
interface StepClientConfirm {
|
package/dist/manifest-entry.js
CHANGED
|
@@ -136,6 +136,7 @@ function scanOfferingRefs(files) {
|
|
|
136
136
|
const kindSeen = /* @__PURE__ */ new Set();
|
|
137
137
|
const upsellKinds = [];
|
|
138
138
|
let checkoutDynamic = false;
|
|
139
|
+
const offeringless = [];
|
|
139
140
|
const addSurface = (v) => {
|
|
140
141
|
if (v && !surfaceSeen.has(v)) {
|
|
141
142
|
surfaceSeen.add(v);
|
|
@@ -183,6 +184,13 @@ function scanOfferingRefs(files) {
|
|
|
183
184
|
const tagStart = m.index + m[0].length;
|
|
184
185
|
const tagEnd = tagAttrEnd(masked, tagStart);
|
|
185
186
|
const attr = (name) => readAttr(source, masked, tagStart, tagEnd, name);
|
|
187
|
+
if (!attr("offering") && !/\{\s*\.\.\./.test(masked.slice(tagStart, tagEnd))) {
|
|
188
|
+
offeringless.push({
|
|
189
|
+
file: path,
|
|
190
|
+
line: source.slice(0, m.index).split("\n").length,
|
|
191
|
+
component: comp
|
|
192
|
+
});
|
|
193
|
+
}
|
|
186
194
|
const managed = readBoolAttr(masked, tagStart, tagEnd, "managed");
|
|
187
195
|
if (managed?.dynamic) checkoutDynamic = true;
|
|
188
196
|
const isManaged = managed?.value === true;
|
|
@@ -220,7 +228,7 @@ function scanOfferingRefs(files) {
|
|
|
220
228
|
}
|
|
221
229
|
}
|
|
222
230
|
}
|
|
223
|
-
return { slots: order, dynamic, checkout: { surfaces, managedSurfaces, upsellKinds, dynamic: checkoutDynamic } };
|
|
231
|
+
return { slots: order, dynamic, checkout: { surfaces, managedSurfaces, upsellKinds, dynamic: checkoutDynamic }, offeringless };
|
|
224
232
|
}
|
|
225
233
|
|
|
226
234
|
// src/flow/scaffold.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/manifest/offering-refs.ts","../src/flow/scaffold.ts"],"names":["surface"],"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>` /\n * `<CheckoutButton>` / `<UpsellButton>`.\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\n// The one place the payment WORDS become surface words. Imported rather than re-listed: the runtime\n// maps `payment` through this exact table (see `resolvePresentation` in ../commerce/checkout), so the\n// publish gate can't drift from what will actually be requested. Both modules are React-free and ship\n// through the same manifest entry.\nimport { PAYMENT_RENDERINGS, type PaymentMode } from '../commerce/capabilities'\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, in the ONE vocabulary the\n * publish gate and the wire both speak ({@link CheckoutSurface}). Validated with\n * `validateCheckout(provider, surface)`.\n *\n * Captured from three spellings, all of which mean the same thing to the gate:\n * - the author axes — `payment` (WHAT the provider renders: `wallets`→`express`, `card`→`element`,\n * `full`→`embedded`, absent/`auto`→`auto`) and `opens` (WHERE it is presented: recorded as\n * itself). A `<CheckoutButton>` records BOTH, because both have to be possible: our sheet has to\n * exist on the provider, AND the provider has to be able to render what we put in it.\n * - the deprecated `surface=\"…\"`, recorded as itself. A tag carrying it takes the legacy path at\n * runtime, so the scan reads it INSTEAD of the axes rather than as well as them — same rule,\n * same result, one place.\n * - the on-session re-collect surface `recoverySurface=\"…\"` (a recovery\n * re-collects a card → it IS a purchase surface the provider must support).\n *\n * `'auto'` is passed through un-expanded, as it always has been: it is a per-device PREFERENCE and\n * only the gate knows the provider, so the gate resolves it (see `promote-gate.ts`).\n */\n surfaces: string[]\n /**\n * The SUBSET of {@link surfaces} whose tag also carried `managed` — Stripe **Managed Payments**,\n * which resolves the provider to a DIFFERENT capability profile (`profileFor(provider, { managed:\n * true })`) with a narrower surface set. Validated with `validateCheckout(provider, surface, {\n * managed: true })`, which is what catches `<Checkout payment=\"card\" managed>`: under managed\n * the provider collects the card in its own frame, so the Elements surfaces are refused.\n *\n * Per-TAG, and a SUBSET rather than a partition, on purpose:\n * - per-tag, because `managed` is a property of one checkout (it's a flag on the Checkout Session\n * the tag opens), so a funnel may legitimately hold a plain `element` paywall beside a managed\n * `embedded` one, and validating either against the other's profile would invent a failure;\n * - a subset, because `surfaces` keeps meaning \"every surface this funnel mounts\". Leaving the\n * managed ones in it means the base-profile check is byte-for-byte what it was, and a gate\n * that predates this field still validates them (against the wider profile — the old, lenient\n * answer, never a newly-invented block).\n *\n * OPTIONAL only for shape tolerance: the scan always emits it, but a `CheckoutUsageScan` literal\n * written before this field existed (the scaffold's empty default, an older manifest) still\n * type-checks and still means \"nothing managed\".\n */\n managedSurfaces?: string[]\n /**\n * Off-session upsell KINDS the author WROTE on `<UpsellButton kind=\"…\">`, deduped. A tag with no `kind` records NOTHING: the kind is derived at attempt time\n * from the offering's interval, and the slot's catalog assignment isn't in the source the scan\n * reads. Validated with `validateUpsell(provider, paywallSurface, kind)` — so this list is\n * \"kinds to check\", not \"kinds this funnel charges\".\n */\n upsellKinds: string[]\n /** True if any surface/kind/`managed` was a non-literal (computed) value → the gate WARNS, never blocks, on it. */\n dynamic: boolean\n}\n\n/**\n * Index of the unescaped quote that CLOSES the string opened at `open`, or -1 when there isn't one.\n *\n * `multiline` is the language rule, not a tolerance: a `'`/`\"` string may not contain a raw newline,\n * so one that reaches the end of its line is not a string at all — only a backtick spans lines. A\n * backslash escapes the next character (including a newline: a line continuation is legal).\n */\nfunction closingQuote(src: string, open: number, multiline: boolean): number {\n const q = src[open]\n for (let i = open + 1; i < src.length; i++) {\n const c = src[i]\n if (c === '\\\\') { i++; continue }\n if (c === q) return i\n if (!multiline && c === '\\n') return -1\n }\n return -1\n}\n\n/** Characters an identifier or number can END with — JS never opens a string directly after one. */\nconst WORD_CHAR = /[\\w$]/\n\n/**\n * Blank comment + string/template CONTENTS (keep quote delimiters + length) so scans don't fire\n * inside them.\n *\n * The hard part is that this runs over TSX, where the same characters are BOTH string delimiters and\n * ordinary prose: `you're`, `don't`, `let's`, `3\" tall` are JSX TEXT. A masker that opens a string on\n * every quote it meets opens one it can never close — and then blanks everything after it, so the\n * scan silently stops seeing the file at its first contraction. That is a false NEGATIVE, and it was\n * the COMMON case, not an exotic one: quiz copy is contractions all the way down. (Found in\n * gracen-onboarding, where `each other's` in a benefits list hid the `<UpsellButton>` 100 lines\n * below it from the publish capability gate.)\n *\n * Fixing it does NOT mean parsing JSX. This stays a linear character scan; two local rules are\n * enough, and both are one-sided — a quote we wrongly DECLINE to open only ever leaves prose\n * unmasked, never a real string, so the worst case stays a missed ref rather than an invented one:\n *\n * 1. A `'`/`\"` directly after an identifier character does not open a string. The `'` in `don't`\n * follows an `n`; real JS only opens a string after an operator, a delimiter, or whitespace.\n * Backticks are EXEMPT — `` styled.div`…` `` / `` css`…` `` are tagged templates, and those do\n * legitimately follow a word character.\n * 2. A `'`/`\"` with no unescaped partner before the end of its line does not open a string, per\n * {@link closingQuote}. Not a guess: the alternative is a file that doesn't compile.\n *\n * Rule 2 is what bounds the damage of anything the pair still misjudges to ONE LINE — quote parity\n * can no longer leak into the rest of the file, which is the property that failed here.\n *\n * KNOWN LIMITS, stated rather than papered over. These blank MORE than they should — a missed ref,\n * the direction this scan has always been willing to fail in:\n * - an apostrophe in JSX text that is preceded by whitespace AND pairs with a same-kind quote later\n * on the SAME line still blanks the span between the two, so `<p>the '90s</p><Checkout … />`\n * written on one line stays invisible. Put the tag on its own line (every formatter already does).\n * - regex literals aren't recognised, so the `'` in `/'/g` reads as that same lone apostrophe —\n * contained to its line by rule 2.\n * - a stray backtick in JSX prose still runs to the file's next backtick. Rule 1 can't help it\n * (tagged templates) and rule 2 must not (template literals span lines); a backtick in prose is\n * rare in a way an apostrophe is not.\n *\n * And the one that fails the OTHER way, so it is worth naming: rule 1 declines a string that a\n * keyword opens with no space after it — `return'…'`, `case'…':`. Legal JS that nothing writes and\n * no formatter emits, but its contents would go unmasked, so a `useOffering('x')` spelled inside one\n * would be counted. That is the only shape here that can invent a ref rather than miss one.\n */\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 const multiline = c === '`'\n // Rule 1, then rule 2. A quote that fails either test is left exactly as it stands: an\n // ordinary character in the mask, which is all an apostrophe in prose ever was.\n if (!multiline && i > 0 && WORD_CHAR.test(src[i - 1])) { i++; continue }\n const close = closingQuote(src, i, multiline)\n if (close === -1) { i++; continue }\n for (let k = i + 1; k < close; k++) if (src[k] !== '\\n') out[k] = ' '\n i = close + 1 // both delimiters kept — literalAt() reads the value back off the ORIGINAL\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\n/**\n * Read a BOOLEAN JSX attribute (`managed`) inside the opening-tag span `[start, end)`. Returns the\n * literal when one is written, `{ dynamic: true }` when the value is a runtime decision, and null\n * when the attribute is absent.\n *\n * Only the three LITERAL spellings are read — bare (`managed`), `managed={true}`, `managed={false}`.\n * Anything else (`managed={expr}`, `managed=\"…\"`) is reported dynamic rather than guessed at: a\n * wrong guess in the \"on\" direction invents a publish block on a funnel that never asked for the\n * variant, and a wrong guess in the \"off\" direction is the hole this field exists to close. Dynamic\n * costs a warning, which is the relaxation discipline the rest of this scan already follows.\n *\n * Unlike {@link readAttr} this walks the span at BRACE DEPTH 0 instead of matching the name with a\n * regex, because a valueless attribute has no `=` to anchor on and the bare word occurs in two\n * places that are NOT the attribute: inside an attribute expression (`onSuccess={() => managed(x)}`)\n * and as the tail of a longer name (`data-managed`). Depth tracking rules out the first; requiring\n * whitespace before the name and a non-name character after it rules out the second. A `managed`\n * inside a string VALUE never reaches here — the mask blanked it.\n */\nfunction readBoolAttr(\n masked: string,\n start: number,\n end: number,\n name: string,\n): { value?: boolean; dynamic?: boolean } | null {\n let depth = 0\n for (let i = start; i < end; i++) {\n const c = masked[i]\n if (c === '{') { depth++; continue }\n if (c === '}') { if (depth > 0) depth--; continue }\n if (depth !== 0 || c !== name[0] || masked.slice(i, i + name.length) !== name) continue\n // JSX separates attributes with whitespace; anything else before/after means a different name.\n if (i > start && !/\\s/.test(masked[i - 1])) continue\n let j = i + name.length\n if (/[\\w$:-]/.test(masked[j] ?? ' ')) continue\n j = skipWs(masked, j)\n if (masked[j] !== '=') return { value: true } // bare attribute → true\n j = skipWs(masked, j + 1)\n if (masked[j] !== '{') return { dynamic: true } // a JSX string value, not a boolean literal\n const literal = /^\\s*(true|false)\\s*\\}/.exec(masked.slice(j + 1, end))\n return literal ? { value: literal[1] === 'true' } : { dynamic: true }\n }\n return null\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 managedSeen = new Set<string>()\n const managedSurfaces: 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 addManaged = (v: string) => { if (v && !managedSeen.has(v)) { managedSeen.add(v); managedSurfaces.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|CheckoutButton|UpsellButton offering=\"…\"> — the `offering` attribute value.\n // Longest name first: `\\b` after `Checkout` can't match `<CheckoutButton`, and the alternation\n // has to reach the longer branch. (`<UpsellOffer>` — a component several templates define — is\n // rejected by the same `\\b`, which is why it isn't in this list and must never be matched.)\n for (const m of masked.matchAll(\n /<(?:CheckoutButton|Checkout|UpsellButton)\\b[^>]*?\\boffering\\s*=\\s*/g,\n )) {\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-SURFACE usage on every checkout tag. 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(/<(CheckoutButton|Checkout|UpsellButton)\\b/g)) {\n const comp = m[1]\n const isUpsell = comp === 'UpsellButton'\n // A BUTTON presents the payment somewhere else, and `opens` (default `'sheet'`) says where.\n // `<Checkout>` has no such prop: it renders the payment in the page, so the container is the\n // page and there is nothing to record for it.\n const isButton = comp === 'CheckoutButton' || comp === 'UpsellButton'\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 // `managed` (Stripe Managed Payments) is read PER TAG, because it selects a different provider\n // profile for this checkout alone — the surfaces below belong to whichever profile THIS tag\n // asked for. A computed `managed` can't claim them for the variant (that would block a funnel\n // that may well be running plain Stripe), so it only sets the dynamic flag → the gate warns.\n const managed = readBoolAttr(masked, tagStart, tagEnd, 'managed')\n if (managed?.dynamic) checkoutDynamic = true\n const isManaged = managed?.value === true\n const tagSurfaces: string[] = []\n\n // A recoverySurface re-collects a card ON-SESSION → it's a purchase surface too.\n const recovery = attr('recoverySurface')\n if (recovery?.value) tagSurfaces.push(recovery.value)\n else if (recovery?.dynamic) checkoutDynamic = true\n\n if (isUpsell) {\n // Record only a kind the AUTHOR wrote. An absent `kind` is no longer 'subscription' — it is\n // DERIVED at attempt time from the offering's own interval (`upsellKindFor`), which the scan\n // cannot see: the slot's catalog assignment lives in the project, not in the source. Guessing\n // 'subscription' here would make the publish gate reject a bare one-time upsell on a provider\n // that can't stack subscriptions (Paddle: `offSessionUpsells: ['one-time','upgrade']`) — a\n // funnel the runtime would charge correctly. Recording nothing lets the gate validate only\n // what it actually knows.\n const kind = attr('kind')\n if (kind?.value) addKind(kind.value)\n else if (kind?.dynamic) checkoutDynamic = true\n }\n\n // The one-word `surface` spelling, on `<Checkout>` only. A tag carrying it takes that path at\n // runtime and the axes are typed `never` beside it, so reading it INSTEAD of them mirrors what\n // will actually render. `'auto'` passes through for the gate to resolve, as before.\n const surface = comp === 'Checkout' ? attr('surface') : null\n if (surface?.dynamic) checkoutDynamic = true\n else if (surface?.value) tagSurfaces.push(surface.value)\n else {\n // The AXES. Both are flat string attributes on purpose — an object-valued prop would read as\n // `{ dynamic: true }` here and degrade the publish gate to a warning, which is exactly why\n // the API grew two scalars instead of one `presentation={{ … }}`.\n //\n // WHERE. Only a BUTTON has this question; `<Checkout>` renders in the page, and the page is\n // not a surface anyone has to support.\n const opens = isButton ? attr('opens') : null\n // Can't tell our overlay from a hand-off to the provider → warn rather than guess: recording\n // the wrong one either invents a block or hides a real one.\n if (opens?.dynamic) checkoutDynamic = true\n else if (opens?.value === 'popup' || opens?.value === 'redirect') {\n // The provider paints the whole thing — no rendering of ours to record beside it.\n tagSurfaces.push(opens.value)\n } else {\n // Our sheet: it has to exist on the provider AND host what we put in it, so both words are\n // recorded. An UPSELL's default recovery container is provider-derived (`sheet` where there\n // is one, else the provider's best), so it records nothing here — unchanged from before\n // `recover` existed, and the gate never had a word for it.\n if (isButton && !isUpsell) tagSurfaces.push('sheet')\n // WHAT the provider renders. Absent or `'auto'` records `'auto'`, which the gate resolves\n // per provider + device — the same treatment `surface=\"auto\"` has always had. On an upsell\n // an absent `payment` means \"whatever the sheet's default is\", which is likewise nothing to\n // record; only a NAMED payment UI is a claim about the provider.\n const payment = attr('payment')\n if (payment?.dynamic) checkoutDynamic = true\n else if (payment?.value) {\n tagSurfaces.push(\n PAYMENT_RENDERINGS[payment.value as Exclude<PaymentMode, 'auto'>] ?? payment.value,\n )\n } else if (!isUpsell) tagSurfaces.push('auto')\n }\n }\n\n for (const surface of tagSurfaces) {\n addSurface(surface)\n if (isManaged) addManaged(surface)\n }\n }\n }\n return { slots: order, dynamic, checkout: { surfaces, managedSurfaces, 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"]}
|
|
1
|
+
{"version":3,"sources":["../src/manifest/offering-refs.ts","../src/flow/scaffold.ts"],"names":["surface"],"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>` /\n * `<CheckoutButton>` / `<UpsellButton>`.\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\n// The one place the payment WORDS become surface words. Imported rather than re-listed: the runtime\n// maps `payment` through this exact table (see `resolvePresentation` in ../commerce/checkout), so the\n// publish gate can't drift from what will actually be requested. Both modules are React-free and ship\n// through the same manifest entry.\nimport { PAYMENT_RENDERINGS, type PaymentMode } from '../commerce/capabilities'\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 * Checkout tags that name NO offering — an authoring error the build fails on.\n *\n * This is the hole the `product` → `offering` rename left open. `<Checkout product=\"plan\">` is a\n * tag with no `offering` attribute: it contributes no ref above, so the slot never reaches\n * validation and the publish gate passes vacuously — then `CheckoutImpl` reads `offering` as\n * `undefined` and the charge resolves to nothing. Nothing else in the deploy path sees it: the\n * funnel build is a vite/esbuild transform, so types are stripped rather than checked.\n *\n * A tag carrying a SPREAD (`<Checkout {...props} />`) is not reported — the offering may well\n * arrive through it, and the scan cannot see inside. That is the same relaxation the rest of this\n * module applies to anything it cannot statically enumerate.\n */\n offeringless: OfferinglessCheckout[]\n}\n\n/** One checkout tag that names no offering — located for an operator-legible build error. */\nexport interface OfferinglessCheckout {\n /** Source path as given to the scan. */\n file: string\n /** 1-based line of the tag's `<`. */\n line: number\n /** `Checkout` | `CheckoutButton` | `UpsellButton`. */\n component: string\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, in the ONE vocabulary the\n * publish gate and the wire both speak ({@link CheckoutSurface}). Validated with\n * `validateCheckout(provider, surface)`.\n *\n * Captured from three spellings, all of which mean the same thing to the gate:\n * - the author axes — `payment` (WHAT the provider renders: `wallets`→`express`, `card`→`element`,\n * `full`→`embedded`, absent/`auto`→`auto`) and `opens` (WHERE it is presented: recorded as\n * itself). A `<CheckoutButton>` records BOTH, because both have to be possible: our sheet has to\n * exist on the provider, AND the provider has to be able to render what we put in it.\n * - the deprecated `surface=\"…\"`, recorded as itself. A tag carrying it takes the legacy path at\n * runtime, so the scan reads it INSTEAD of the axes rather than as well as them — same rule,\n * same result, one place.\n * - the on-session re-collect surface `recoverySurface=\"…\"` (a recovery\n * re-collects a card → it IS a purchase surface the provider must support).\n *\n * `'auto'` is passed through un-expanded, as it always has been: it is a per-device PREFERENCE and\n * only the gate knows the provider, so the gate resolves it (see `promote-gate.ts`).\n */\n surfaces: string[]\n /**\n * The SUBSET of {@link surfaces} whose tag also carried `managed` — Stripe **Managed Payments**,\n * which resolves the provider to a DIFFERENT capability profile (`profileFor(provider, { managed:\n * true })`) with a narrower surface set. Validated with `validateCheckout(provider, surface, {\n * managed: true })`, which is what catches `<Checkout payment=\"card\" managed>`: under managed\n * the provider collects the card in its own frame, so the Elements surfaces are refused.\n *\n * Per-TAG, and a SUBSET rather than a partition, on purpose:\n * - per-tag, because `managed` is a property of one checkout (it's a flag on the Checkout Session\n * the tag opens), so a funnel may legitimately hold a plain `element` paywall beside a managed\n * `embedded` one, and validating either against the other's profile would invent a failure;\n * - a subset, because `surfaces` keeps meaning \"every surface this funnel mounts\". Leaving the\n * managed ones in it means the base-profile check is byte-for-byte what it was, and a gate\n * that predates this field still validates them (against the wider profile — the old, lenient\n * answer, never a newly-invented block).\n *\n * OPTIONAL only for shape tolerance: the scan always emits it, but a `CheckoutUsageScan` literal\n * written before this field existed (the scaffold's empty default, an older manifest) still\n * type-checks and still means \"nothing managed\".\n */\n managedSurfaces?: string[]\n /**\n * Off-session upsell KINDS the author WROTE on `<UpsellButton kind=\"…\">`, deduped. A tag with no `kind` records NOTHING: the kind is derived at attempt time\n * from the offering's interval, and the slot's catalog assignment isn't in the source the scan\n * reads. Validated with `validateUpsell(provider, paywallSurface, kind)` — so this list is\n * \"kinds to check\", not \"kinds this funnel charges\".\n */\n upsellKinds: string[]\n /** True if any surface/kind/`managed` was a non-literal (computed) value → the gate WARNS, never blocks, on it. */\n dynamic: boolean\n}\n\n/**\n * Index of the unescaped quote that CLOSES the string opened at `open`, or -1 when there isn't one.\n *\n * `multiline` is the language rule, not a tolerance: a `'`/`\"` string may not contain a raw newline,\n * so one that reaches the end of its line is not a string at all — only a backtick spans lines. A\n * backslash escapes the next character (including a newline: a line continuation is legal).\n */\nfunction closingQuote(src: string, open: number, multiline: boolean): number {\n const q = src[open]\n for (let i = open + 1; i < src.length; i++) {\n const c = src[i]\n if (c === '\\\\') { i++; continue }\n if (c === q) return i\n if (!multiline && c === '\\n') return -1\n }\n return -1\n}\n\n/** Characters an identifier or number can END with — JS never opens a string directly after one. */\nconst WORD_CHAR = /[\\w$]/\n\n/**\n * Blank comment + string/template CONTENTS (keep quote delimiters + length) so scans don't fire\n * inside them.\n *\n * The hard part is that this runs over TSX, where the same characters are BOTH string delimiters and\n * ordinary prose: `you're`, `don't`, `let's`, `3\" tall` are JSX TEXT. A masker that opens a string on\n * every quote it meets opens one it can never close — and then blanks everything after it, so the\n * scan silently stops seeing the file at its first contraction. That is a false NEGATIVE, and it was\n * the COMMON case, not an exotic one: quiz copy is contractions all the way down. (Found in\n * gracen-onboarding, where `each other's` in a benefits list hid the `<UpsellButton>` 100 lines\n * below it from the publish capability gate.)\n *\n * Fixing it does NOT mean parsing JSX. This stays a linear character scan; two local rules are\n * enough, and both are one-sided — a quote we wrongly DECLINE to open only ever leaves prose\n * unmasked, never a real string, so the worst case stays a missed ref rather than an invented one:\n *\n * 1. A `'`/`\"` directly after an identifier character does not open a string. The `'` in `don't`\n * follows an `n`; real JS only opens a string after an operator, a delimiter, or whitespace.\n * Backticks are EXEMPT — `` styled.div`…` `` / `` css`…` `` are tagged templates, and those do\n * legitimately follow a word character.\n * 2. A `'`/`\"` with no unescaped partner before the end of its line does not open a string, per\n * {@link closingQuote}. Not a guess: the alternative is a file that doesn't compile.\n *\n * Rule 2 is what bounds the damage of anything the pair still misjudges to ONE LINE — quote parity\n * can no longer leak into the rest of the file, which is the property that failed here.\n *\n * KNOWN LIMITS, stated rather than papered over. These blank MORE than they should — a missed ref,\n * the direction this scan has always been willing to fail in:\n * - an apostrophe in JSX text that is preceded by whitespace AND pairs with a same-kind quote later\n * on the SAME line still blanks the span between the two, so `<p>the '90s</p><Checkout … />`\n * written on one line stays invisible. Put the tag on its own line (every formatter already does).\n * - regex literals aren't recognised, so the `'` in `/'/g` reads as that same lone apostrophe —\n * contained to its line by rule 2.\n * - a stray backtick in JSX prose still runs to the file's next backtick. Rule 1 can't help it\n * (tagged templates) and rule 2 must not (template literals span lines); a backtick in prose is\n * rare in a way an apostrophe is not.\n *\n * And the one that fails the OTHER way, so it is worth naming: rule 1 declines a string that a\n * keyword opens with no space after it — `return'…'`, `case'…':`. Legal JS that nothing writes and\n * no formatter emits, but its contents would go unmasked, so a `useOffering('x')` spelled inside one\n * would be counted. That is the only shape here that can invent a ref rather than miss one.\n */\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 const multiline = c === '`'\n // Rule 1, then rule 2. A quote that fails either test is left exactly as it stands: an\n // ordinary character in the mask, which is all an apostrophe in prose ever was.\n if (!multiline && i > 0 && WORD_CHAR.test(src[i - 1])) { i++; continue }\n const close = closingQuote(src, i, multiline)\n if (close === -1) { i++; continue }\n for (let k = i + 1; k < close; k++) if (src[k] !== '\\n') out[k] = ' '\n i = close + 1 // both delimiters kept — literalAt() reads the value back off the ORIGINAL\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\n/**\n * Read a BOOLEAN JSX attribute (`managed`) inside the opening-tag span `[start, end)`. Returns the\n * literal when one is written, `{ dynamic: true }` when the value is a runtime decision, and null\n * when the attribute is absent.\n *\n * Only the three LITERAL spellings are read — bare (`managed`), `managed={true}`, `managed={false}`.\n * Anything else (`managed={expr}`, `managed=\"…\"`) is reported dynamic rather than guessed at: a\n * wrong guess in the \"on\" direction invents a publish block on a funnel that never asked for the\n * variant, and a wrong guess in the \"off\" direction is the hole this field exists to close. Dynamic\n * costs a warning, which is the relaxation discipline the rest of this scan already follows.\n *\n * Unlike {@link readAttr} this walks the span at BRACE DEPTH 0 instead of matching the name with a\n * regex, because a valueless attribute has no `=` to anchor on and the bare word occurs in two\n * places that are NOT the attribute: inside an attribute expression (`onSuccess={() => managed(x)}`)\n * and as the tail of a longer name (`data-managed`). Depth tracking rules out the first; requiring\n * whitespace before the name and a non-name character after it rules out the second. A `managed`\n * inside a string VALUE never reaches here — the mask blanked it.\n */\nfunction readBoolAttr(\n masked: string,\n start: number,\n end: number,\n name: string,\n): { value?: boolean; dynamic?: boolean } | null {\n let depth = 0\n for (let i = start; i < end; i++) {\n const c = masked[i]\n if (c === '{') { depth++; continue }\n if (c === '}') { if (depth > 0) depth--; continue }\n if (depth !== 0 || c !== name[0] || masked.slice(i, i + name.length) !== name) continue\n // JSX separates attributes with whitespace; anything else before/after means a different name.\n if (i > start && !/\\s/.test(masked[i - 1])) continue\n let j = i + name.length\n if (/[\\w$:-]/.test(masked[j] ?? ' ')) continue\n j = skipWs(masked, j)\n if (masked[j] !== '=') return { value: true } // bare attribute → true\n j = skipWs(masked, j + 1)\n if (masked[j] !== '{') return { dynamic: true } // a JSX string value, not a boolean literal\n const literal = /^\\s*(true|false)\\s*\\}/.exec(masked.slice(j + 1, end))\n return literal ? { value: literal[1] === 'true' } : { dynamic: true }\n }\n return null\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 managedSeen = new Set<string>()\n const managedSurfaces: string[] = []\n const kindSeen = new Set<string>()\n const upsellKinds: string[] = []\n let checkoutDynamic = false\n const offeringless: OfferinglessCheckout[] = []\n const addSurface = (v: string) => { if (v && !surfaceSeen.has(v)) { surfaceSeen.add(v); surfaces.push(v) } }\n const addManaged = (v: string) => { if (v && !managedSeen.has(v)) { managedSeen.add(v); managedSurfaces.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|CheckoutButton|UpsellButton offering=\"…\"> — the `offering` attribute value.\n // Longest name first: `\\b` after `Checkout` can't match `<CheckoutButton`, and the alternation\n // has to reach the longer branch. (`<UpsellOffer>` — a component several templates define — is\n // rejected by the same `\\b`, which is why it isn't in this list and must never be matched.)\n for (const m of masked.matchAll(\n /<(?:CheckoutButton|Checkout|UpsellButton)\\b[^>]*?\\boffering\\s*=\\s*/g,\n )) {\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-SURFACE usage on every checkout tag. 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(/<(CheckoutButton|Checkout|UpsellButton)\\b/g)) {\n const comp = m[1]\n const isUpsell = comp === 'UpsellButton'\n // A BUTTON presents the payment somewhere else, and `opens` (default `'sheet'`) says where.\n // `<Checkout>` has no such prop: it renders the payment in the page, so the container is the\n // page and there is nothing to record for it.\n const isButton = comp === 'CheckoutButton' || comp === 'UpsellButton'\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\n // NO OFFERING NAMED. Recorded here rather than in the ref loop above because only this loop\n // parses the tag's real attribute span — the ref loop's `[^>]*?` stops at the first `>`, which\n // an arrow-function attribute (`onSuccess={() => …}`) supplies long before the tag ends. A\n // spread could carry the offering invisibly, so a tag with one is left alone.\n if (!attr('offering') && !/\\{\\s*\\.\\.\\./.test(masked.slice(tagStart, tagEnd))) {\n offeringless.push({\n file: path,\n line: source.slice(0, m.index!).split('\\n').length,\n component: comp,\n })\n }\n // `managed` (Stripe Managed Payments) is read PER TAG, because it selects a different provider\n // profile for this checkout alone — the surfaces below belong to whichever profile THIS tag\n // asked for. A computed `managed` can't claim them for the variant (that would block a funnel\n // that may well be running plain Stripe), so it only sets the dynamic flag → the gate warns.\n const managed = readBoolAttr(masked, tagStart, tagEnd, 'managed')\n if (managed?.dynamic) checkoutDynamic = true\n const isManaged = managed?.value === true\n const tagSurfaces: string[] = []\n\n // A recoverySurface re-collects a card ON-SESSION → it's a purchase surface too.\n const recovery = attr('recoverySurface')\n if (recovery?.value) tagSurfaces.push(recovery.value)\n else if (recovery?.dynamic) checkoutDynamic = true\n\n if (isUpsell) {\n // Record only a kind the AUTHOR wrote. An absent `kind` is no longer 'subscription' — it is\n // DERIVED at attempt time from the offering's own interval (`upsellKindFor`), which the scan\n // cannot see: the slot's catalog assignment lives in the project, not in the source. Guessing\n // 'subscription' here would make the publish gate reject a bare one-time upsell on a provider\n // that can't stack subscriptions (Paddle: `offSessionUpsells: ['one-time','upgrade']`) — a\n // funnel the runtime would charge correctly. Recording nothing lets the gate validate only\n // what it actually knows.\n const kind = attr('kind')\n if (kind?.value) addKind(kind.value)\n else if (kind?.dynamic) checkoutDynamic = true\n }\n\n // The one-word `surface` spelling, on `<Checkout>` only. A tag carrying it takes that path at\n // runtime and the axes are typed `never` beside it, so reading it INSTEAD of them mirrors what\n // will actually render. `'auto'` passes through for the gate to resolve, as before.\n const surface = comp === 'Checkout' ? attr('surface') : null\n if (surface?.dynamic) checkoutDynamic = true\n else if (surface?.value) tagSurfaces.push(surface.value)\n else {\n // The AXES. Both are flat string attributes on purpose — an object-valued prop would read as\n // `{ dynamic: true }` here and degrade the publish gate to a warning, which is exactly why\n // the API grew two scalars instead of one `presentation={{ … }}`.\n //\n // WHERE. Only a BUTTON has this question; `<Checkout>` renders in the page, and the page is\n // not a surface anyone has to support.\n const opens = isButton ? attr('opens') : null\n // Can't tell our overlay from a hand-off to the provider → warn rather than guess: recording\n // the wrong one either invents a block or hides a real one.\n if (opens?.dynamic) checkoutDynamic = true\n else if (opens?.value === 'popup' || opens?.value === 'redirect') {\n // The provider paints the whole thing — no rendering of ours to record beside it.\n tagSurfaces.push(opens.value)\n } else {\n // Our sheet: it has to exist on the provider AND host what we put in it, so both words are\n // recorded. An UPSELL's default recovery container is provider-derived (`sheet` where there\n // is one, else the provider's best), so it records nothing here — unchanged from before\n // `recover` existed, and the gate never had a word for it.\n if (isButton && !isUpsell) tagSurfaces.push('sheet')\n // WHAT the provider renders. Absent or `'auto'` records `'auto'`, which the gate resolves\n // per provider + device — the same treatment `surface=\"auto\"` has always had. On an upsell\n // an absent `payment` means \"whatever the sheet's default is\", which is likewise nothing to\n // record; only a NAMED payment UI is a claim about the provider.\n const payment = attr('payment')\n if (payment?.dynamic) checkoutDynamic = true\n else if (payment?.value) {\n tagSurfaces.push(\n PAYMENT_RENDERINGS[payment.value as Exclude<PaymentMode, 'auto'>] ?? payment.value,\n )\n } else if (!isUpsell) tagSurfaces.push('auto')\n }\n }\n\n for (const surface of tagSurfaces) {\n addSurface(surface)\n if (isManaged) addManaged(surface)\n }\n }\n }\n return { slots: order, dynamic, checkout: { surfaces, managedSurfaces, upsellKinds, dynamic: checkoutDynamic }, offeringless }\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"]}
|
package/package.json
CHANGED