@burdenoff/website-sdk 2026.1031.3 → 2026.1101.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -8
- package/dist/analytics/index.js +19 -69
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +3 -69
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/avatar-live/index.js +1 -0
- package/dist/avatar-live/index.js.map +1 -1
- package/dist/avatar-live/index.mjs +1 -0
- package/dist/avatar-live/index.mjs.map +1 -1
- package/dist/avatar-live-view/index.js +10 -30
- package/dist/avatar-live-view/index.js.map +1 -1
- package/dist/avatar-live-view/index.mjs +4 -24
- package/dist/avatar-live-view/index.mjs.map +1 -1
- package/dist/chunk-23H4JUL4.js +41 -0
- package/dist/chunk-23H4JUL4.js.map +1 -0
- package/dist/chunk-34RX5P75.mjs +25 -0
- package/dist/chunk-34RX5P75.mjs.map +1 -0
- package/dist/chunk-3LFFY7AZ.js +184 -0
- package/dist/chunk-3LFFY7AZ.js.map +1 -0
- package/dist/chunk-3X26GE4S.js +2794 -0
- package/dist/chunk-3X26GE4S.js.map +1 -0
- package/dist/chunk-45GCA4GG.js +85 -0
- package/dist/chunk-45GCA4GG.js.map +1 -0
- package/dist/chunk-4SAGVLHN.js +876 -0
- package/dist/chunk-4SAGVLHN.js.map +1 -0
- package/dist/chunk-4U6YAII5.js +218 -0
- package/dist/chunk-4U6YAII5.js.map +1 -0
- package/dist/chunk-5GMTBOO6.js +21 -0
- package/dist/chunk-5GMTBOO6.js.map +1 -0
- package/dist/chunk-5JRJ57CL.mjs +329 -0
- package/dist/chunk-5JRJ57CL.mjs.map +1 -0
- package/dist/chunk-6C2DKIC7.js +4 -0
- package/dist/chunk-6C2DKIC7.js.map +1 -0
- package/dist/chunk-6IBFBWC5.mjs +17 -0
- package/dist/chunk-6IBFBWC5.mjs.map +1 -0
- package/dist/chunk-6KY2ZRVI.js +116 -0
- package/dist/chunk-6KY2ZRVI.js.map +1 -0
- package/dist/chunk-6VJT3SM4.mjs +182 -0
- package/dist/chunk-6VJT3SM4.mjs.map +1 -0
- package/dist/chunk-6YZUFMK2.mjs +40 -0
- package/dist/chunk-6YZUFMK2.mjs.map +1 -0
- package/dist/chunk-7A6R7CGG.js +47 -0
- package/dist/chunk-7A6R7CGG.js.map +1 -0
- package/dist/chunk-7PWL4V2R.js +4 -0
- package/dist/chunk-7PWL4V2R.js.map +1 -0
- package/dist/chunk-AA33WDYS.js +161 -0
- package/dist/chunk-AA33WDYS.js.map +1 -0
- package/dist/chunk-AHRGFXV2.js +525 -0
- package/dist/chunk-AHRGFXV2.js.map +1 -0
- package/dist/chunk-B63XLDZH.mjs +109 -0
- package/dist/chunk-B63XLDZH.mjs.map +1 -0
- package/dist/chunk-BX2USEWF.mjs +35 -0
- package/dist/chunk-BX2USEWF.mjs.map +1 -0
- package/dist/chunk-BYSZT35R.js +150 -0
- package/dist/chunk-BYSZT35R.js.map +1 -0
- package/dist/chunk-C6OJIHNO.js +12 -0
- package/dist/chunk-C6OJIHNO.js.map +1 -0
- package/dist/chunk-COOELBX7.mjs +63 -0
- package/dist/chunk-COOELBX7.mjs.map +1 -0
- package/dist/chunk-D2TW7MNC.mjs +2771 -0
- package/dist/chunk-D2TW7MNC.mjs.map +1 -0
- package/dist/chunk-D5KEZ6V6.mjs +3 -0
- package/dist/chunk-D5KEZ6V6.mjs.map +1 -0
- package/dist/chunk-E3GUGJMY.js +46 -0
- package/dist/chunk-E3GUGJMY.js.map +1 -0
- package/dist/chunk-EBZ2HGJ2.mjs +52 -0
- package/dist/chunk-EBZ2HGJ2.mjs.map +1 -0
- package/dist/chunk-EDLTUOIA.mjs +210 -0
- package/dist/chunk-EDLTUOIA.mjs.map +1 -0
- package/dist/chunk-EJQSOT4X.js +4 -0
- package/dist/chunk-EJQSOT4X.js.map +1 -0
- package/dist/chunk-EUPFS6MZ.mjs +2019 -0
- package/dist/chunk-EUPFS6MZ.mjs.map +1 -0
- package/dist/chunk-F3XWDC3B.js +77 -0
- package/dist/chunk-F3XWDC3B.js.map +1 -0
- package/dist/chunk-FFADMVIK.mjs +5127 -0
- package/dist/chunk-FFADMVIK.mjs.map +1 -0
- package/dist/chunk-FJWS7BKT.mjs +3 -0
- package/dist/chunk-FJWS7BKT.mjs.map +1 -0
- package/dist/chunk-FJZJDUOR.mjs +3 -0
- package/dist/chunk-FJZJDUOR.mjs.map +1 -0
- package/dist/chunk-GCFLCO2Z.mjs +145 -0
- package/dist/chunk-GCFLCO2Z.mjs.map +1 -0
- package/dist/chunk-GEXC4LRP.js +61 -0
- package/dist/chunk-GEXC4LRP.js.map +1 -0
- package/dist/chunk-GTRAXQGC.mjs +3 -0
- package/dist/chunk-GTRAXQGC.mjs.map +1 -0
- package/dist/chunk-IKOIRUKW.js +270 -0
- package/dist/chunk-IKOIRUKW.js.map +1 -0
- package/dist/chunk-IXTWOPRN.js +45 -0
- package/dist/chunk-IXTWOPRN.js.map +1 -0
- package/dist/chunk-JDXOXGOK.js +547 -0
- package/dist/chunk-JDXOXGOK.js.map +1 -0
- package/dist/chunk-JSLDWYV7.mjs +558 -0
- package/dist/chunk-JSLDWYV7.mjs.map +1 -0
- package/dist/chunk-JXLSTVBK.js +336 -0
- package/dist/chunk-JXLSTVBK.js.map +1 -0
- package/dist/chunk-KHYXK7FG.mjs +49 -0
- package/dist/chunk-KHYXK7FG.mjs.map +1 -0
- package/dist/chunk-KSMBOWQQ.js +2034 -0
- package/dist/chunk-KSMBOWQQ.js.map +1 -0
- package/dist/chunk-KU2PXHGB.mjs +83 -0
- package/dist/chunk-KU2PXHGB.mjs.map +1 -0
- package/dist/chunk-KZANIV3S.mjs +37 -0
- package/dist/chunk-KZANIV3S.mjs.map +1 -0
- package/dist/chunk-L4BP6YOB.js +20 -0
- package/dist/chunk-L4BP6YOB.js.map +1 -0
- package/dist/chunk-MRKA2OAF.mjs +75 -0
- package/dist/chunk-MRKA2OAF.mjs.map +1 -0
- package/dist/chunk-N5EC53MH.mjs +80 -0
- package/dist/chunk-N5EC53MH.mjs.map +1 -0
- package/dist/chunk-NZANQGDM.mjs +34 -0
- package/dist/chunk-NZANQGDM.mjs.map +1 -0
- package/dist/chunk-O4GS3XPZ.js +10 -0
- package/dist/chunk-O4GS3XPZ.js.map +1 -0
- package/dist/chunk-O76QDGRM.js +5154 -0
- package/dist/chunk-O76QDGRM.js.map +1 -0
- package/dist/chunk-OYEUNZJY.js +114 -0
- package/dist/chunk-OYEUNZJY.js.map +1 -0
- package/dist/chunk-PHXAGQNB.js +74 -0
- package/dist/chunk-PHXAGQNB.js.map +1 -0
- package/dist/chunk-PIHHPP7J.mjs +16 -0
- package/dist/chunk-PIHHPP7J.mjs.map +1 -0
- package/dist/chunk-PXRHVVOU.mjs +499 -0
- package/dist/chunk-PXRHVVOU.mjs.map +1 -0
- package/dist/chunk-QMA6U6AU.js +36 -0
- package/dist/chunk-QMA6U6AU.js.map +1 -0
- package/dist/chunk-R6J7IULF.js +44 -0
- package/dist/chunk-R6J7IULF.js.map +1 -0
- package/dist/chunk-R7G6TWIS.mjs +10 -0
- package/dist/chunk-R7G6TWIS.mjs.map +1 -0
- package/dist/chunk-RUMGFI6L.mjs +544 -0
- package/dist/chunk-RUMGFI6L.mjs.map +1 -0
- package/dist/chunk-S54JWSJF.js +565 -0
- package/dist/chunk-S54JWSJF.js.map +1 -0
- package/dist/chunk-SK5UGG6A.js +2041 -0
- package/dist/chunk-SK5UGG6A.js.map +1 -0
- package/dist/chunk-SKIYDKIS.mjs +70 -0
- package/dist/chunk-SKIYDKIS.mjs.map +1 -0
- package/dist/chunk-SZNS5IK6.mjs +322 -0
- package/dist/chunk-SZNS5IK6.mjs.map +1 -0
- package/dist/chunk-T7JIPZAC.js +66 -0
- package/dist/chunk-T7JIPZAC.js.map +1 -0
- package/dist/chunk-TGYQHO2E.mjs +864 -0
- package/dist/chunk-TGYQHO2E.mjs.map +1 -0
- package/dist/chunk-TT3MHN42.mjs +8 -0
- package/dist/chunk-TT3MHN42.mjs.map +1 -0
- package/dist/chunk-UM55U65W.mjs +268 -0
- package/dist/chunk-UM55U65W.mjs.map +1 -0
- package/dist/chunk-UQ2HALUD.js +72 -0
- package/dist/chunk-UQ2HALUD.js.map +1 -0
- package/dist/chunk-UXWT7G77.js +82 -0
- package/dist/chunk-UXWT7G77.js.map +1 -0
- package/dist/chunk-VNS5WDWO.mjs +7 -0
- package/dist/chunk-VNS5WDWO.mjs.map +1 -0
- package/dist/chunk-VRWZLGR7.js +4 -0
- package/dist/chunk-VRWZLGR7.js.map +1 -0
- package/dist/chunk-VZLDSNH4.mjs +22 -0
- package/dist/chunk-VZLDSNH4.mjs.map +1 -0
- package/dist/chunk-W6P3YETJ.js +92 -0
- package/dist/chunk-W6P3YETJ.js.map +1 -0
- package/dist/chunk-WPKFFQCR.mjs +90 -0
- package/dist/chunk-WPKFFQCR.mjs.map +1 -0
- package/dist/chunk-WTVTI5TO.mjs +22 -0
- package/dist/chunk-WTVTI5TO.mjs.map +1 -0
- package/dist/chunk-XJOBZ4RJ.js +44 -0
- package/dist/chunk-XJOBZ4RJ.js.map +1 -0
- package/dist/chunk-XP2JDNDU.js +325 -0
- package/dist/chunk-XP2JDNDU.js.map +1 -0
- package/dist/chunk-Y4V5CEWD.mjs +112 -0
- package/dist/chunk-Y4V5CEWD.mjs.map +1 -0
- package/dist/chunk-YERN2UWM.js +116 -0
- package/dist/chunk-YERN2UWM.js.map +1 -0
- package/dist/chunk-ZKJQWFAA.mjs +114 -0
- package/dist/chunk-ZKJQWFAA.mjs.map +1 -0
- package/dist/chunk-ZOON3S3Y.mjs +138 -0
- package/dist/chunk-ZOON3S3Y.mjs.map +1 -0
- package/dist/chunk-ZP6QRAYC.mjs +2024 -0
- package/dist/chunk-ZP6QRAYC.mjs.map +1 -0
- package/dist/chunk-ZSTFX7FV.js +9 -0
- package/dist/chunk-ZSTFX7FV.js.map +1 -0
- package/dist/client/index.js +23 -331
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +3 -330
- package/dist/client/index.mjs.map +1 -1
- package/dist/comparison/index.js +28 -111
- package/dist/comparison/index.js.map +1 -1
- package/dist/comparison/index.mjs +2 -107
- package/dist/comparison/index.mjs.map +1 -1
- package/dist/components/contact.js +21 -931
- package/dist/components/contact.js.map +1 -1
- package/dist/components/contact.mjs +12 -909
- package/dist/components/contact.mjs.map +1 -1
- package/dist/components/electron-download.js +12 -124
- package/dist/components/electron-download.js.map +1 -1
- package/dist/components/electron-download.mjs +4 -106
- package/dist/components/electron-download.mjs.map +1 -1
- package/dist/components/explore-cta.js +8 -112
- package/dist/components/explore-cta.js.map +1 -1
- package/dist/components/explore-cta.mjs +3 -114
- package/dist/components/explore-cta.mjs.map +1 -1
- package/dist/components/explore.js +62 -7833
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +13 -7800
- package/dist/components/explore.mjs.map +1 -1
- package/dist/components/launch-countdown.js +8 -83
- package/dist/components/launch-countdown.js.map +1 -1
- package/dist/components/launch-countdown.mjs +3 -85
- package/dist/components/launch-countdown.mjs.map +1 -1
- package/dist/components/newsletter.js +20 -993
- package/dist/components/newsletter.js.map +1 -1
- package/dist/components/newsletter.mjs +11 -971
- package/dist/components/newsletter.mjs.map +1 -1
- package/dist/components/partners.js +24 -1302
- package/dist/components/partners.js.map +1 -1
- package/dist/components/partners.mjs +15 -1276
- package/dist/components/partners.mjs.map +1 -1
- package/dist/components/pricing.js +49 -1249
- package/dist/components/pricing.js.map +1 -1
- package/dist/components/pricing.mjs +8 -1226
- package/dist/components/pricing.mjs.map +1 -1
- package/dist/components/problems-solved.js +8 -150
- package/dist/components/problems-solved.js.map +1 -1
- package/dist/components/problems-solved.mjs +4 -133
- package/dist/components/problems-solved.mjs.map +1 -1
- package/dist/components/resource-links.js +8 -304
- package/dist/components/resource-links.js.map +1 -1
- package/dist/components/resource-links.mjs +5 -308
- package/dist/components/resource-links.mjs.map +1 -1
- package/dist/components/social-links.js +8 -343
- package/dist/components/social-links.js.map +1 -1
- package/dist/components/social-links.mjs +5 -347
- package/dist/components/social-links.mjs.map +1 -1
- package/dist/components/unsubscribe.js +10 -627
- package/dist/components/unsubscribe.js.map +1 -1
- package/dist/components/unsubscribe.mjs +6 -611
- package/dist/components/unsubscribe.mjs.map +1 -1
- package/dist/components/waitlist.js +21 -1001
- package/dist/components/waitlist.js.map +1 -1
- package/dist/components/waitlist.mjs +12 -982
- package/dist/components/waitlist.mjs.map +1 -1
- package/dist/components/website-switcher.js +12 -175
- package/dist/components/website-switcher.js.map +1 -1
- package/dist/components/website-switcher.mjs +4 -157
- package/dist/components/website-switcher.mjs.map +1 -1
- package/dist/content/index.js +118 -3616
- package/dist/content/index.js.map +1 -1
- package/dist/content/index.mjs +12 -3567
- package/dist/content/index.mjs.map +1 -1
- package/dist/hooks/index.js +37 -2321
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +6 -2318
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.js +4145 -20599
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3766 -20567
- package/dist/index.mjs.map +1 -1
- package/dist/product/index.js +20 -393
- package/dist/product/index.js.map +1 -1
- package/dist/product/index.mjs +5 -394
- package/dist/product/index.mjs.map +1 -1
- package/dist/seo/index.js +8 -76
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/index.mjs +3 -78
- package/dist/seo/index.mjs.map +1 -1
- package/dist/store/index.js +51 -2650
- package/dist/store/index.js.map +1 -1
- package/dist/store/index.mjs +13 -2619
- package/dist/store/index.mjs.map +1 -1
- package/dist/utils/index.js +69 -134
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +5 -122
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/utils/product-slug.ts
|
|
4
|
+
var PRODUCT_SLUG_RENAMES = Object.freeze(
|
|
5
|
+
[Object.freeze({ legacy: "botlit", current: "botmanor" })]
|
|
6
|
+
);
|
|
7
|
+
var PRODUCT_SLUG_ALIASES = Object.freeze(
|
|
8
|
+
Object.fromEntries(PRODUCT_SLUG_RENAMES.map((r) => [r.legacy, r.current]))
|
|
9
|
+
);
|
|
10
|
+
var PRODUCT_SLUG_EMIT = Object.freeze({ productSlug: "current" });
|
|
11
|
+
function findRename(slug) {
|
|
12
|
+
const wanted = slug.toLowerCase();
|
|
13
|
+
return PRODUCT_SLUG_RENAMES.find(
|
|
14
|
+
(r) => r.legacy === wanted || r.current === wanted
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
function normalizeProductSlug(slug) {
|
|
18
|
+
if (slug == null) return slug;
|
|
19
|
+
return findRename(slug)?.current ?? slug;
|
|
20
|
+
}
|
|
21
|
+
function legacyProductSlug(slug) {
|
|
22
|
+
return findRename(slug)?.legacy ?? slug;
|
|
23
|
+
}
|
|
24
|
+
function productSlugVariants(slug) {
|
|
25
|
+
return [
|
|
26
|
+
.../* @__PURE__ */ new Set([slug, normalizeProductSlug(slug), legacyProductSlug(slug)])
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
function isSameProductSlug(a, b) {
|
|
30
|
+
if (a == null || b == null) return a === b;
|
|
31
|
+
return a === b || normalizeProductSlug(a) === normalizeProductSlug(b);
|
|
32
|
+
}
|
|
33
|
+
function emitProductSlug(slug) {
|
|
34
|
+
return PRODUCT_SLUG_EMIT.productSlug === "current" ? normalizeProductSlug(slug) : legacyProductSlug(slug);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
exports.PRODUCT_SLUG_ALIASES = PRODUCT_SLUG_ALIASES;
|
|
38
|
+
exports.PRODUCT_SLUG_EMIT = PRODUCT_SLUG_EMIT;
|
|
39
|
+
exports.PRODUCT_SLUG_RENAMES = PRODUCT_SLUG_RENAMES;
|
|
40
|
+
exports.emitProductSlug = emitProductSlug;
|
|
41
|
+
exports.isSameProductSlug = isSameProductSlug;
|
|
42
|
+
exports.legacyProductSlug = legacyProductSlug;
|
|
43
|
+
exports.normalizeProductSlug = normalizeProductSlug;
|
|
44
|
+
exports.productSlugVariants = productSlugVariants;
|
|
45
|
+
//# sourceMappingURL=chunk-E3GUGJMY.js.map
|
|
46
|
+
//# sourceMappingURL=chunk-E3GUGJMY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/product-slug.ts"],"names":[],"mappings":";;;AAoBO,IAAM,uBAAqD,MAAA,CAAO,MAAA;AAAA,EACvE,CAAC,OAAO,MAAA,CAAO,EAAE,QAAQ,QAAA,EAAU,OAAA,EAAS,UAAA,EAAY,CAAC;AAC3D;AAGO,IAAM,uBACX,MAAA,CAAO,MAAA;AAAA,EACL,MAAA,CAAO,WAAA,CAAY,oBAAA,CAAqB,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,MAAA,EAAQ,CAAA,CAAE,OAAO,CAAC,CAAC;AAC3E;AASK,IAAM,oBAER,MAAA,CAAO,MAAA,CAAO,EAAE,WAAA,EAAa,WAAW;AAE7C,SAAS,WAAW,IAAA,EAA6C;AAC/D,EAAA,MAAM,MAAA,GAAS,KAAK,WAAA,EAAY;AAChC,EAAA,OAAO,oBAAA,CAAqB,IAAA;AAAA,IAC1B,CAAC,CAAA,KAAM,CAAA,CAAE,MAAA,KAAW,MAAA,IAAU,EAAE,OAAA,KAAY;AAAA,GAC9C;AACF;AAOO,SAAS,qBACd,IAAA,EAC2B;AAC3B,EAAA,IAAI,IAAA,IAAQ,MAAM,OAAO,IAAA;AACzB,EAAA,OAAO,UAAA,CAAW,IAAI,CAAA,EAAG,OAAA,IAAW,IAAA;AACtC;AAGO,SAAS,kBAAkB,IAAA,EAAsB;AACtD,EAAA,OAAO,UAAA,CAAW,IAAI,CAAA,EAAG,MAAA,IAAU,IAAA;AACrC;AAGO,SAAS,oBAAoB,IAAA,EAAwB;AAE1D,EAAA,OAAO;AAAA,IACL,mBAAG,IAAI,GAAA,CAAI,CAAC,IAAA,EAAM,oBAAA,CAAqB,IAAI,CAAA,EAAG,iBAAA,CAAkB,IAAI,CAAC,CAAC;AAAA,GACxE;AACF;AAGO,SAAS,iBAAA,CACd,GACA,CAAA,EACS;AACT,EAAA,IAAI,CAAA,IAAK,IAAA,IAAQ,CAAA,IAAK,IAAA,SAAa,CAAA,KAAM,CAAA;AACzC,EAAA,OAAO,MAAM,CAAA,IAAK,oBAAA,CAAqB,CAAC,CAAA,KAAM,qBAAqB,CAAC,CAAA;AACtE;AAGO,SAAS,gBAAgB,IAAA,EAAsB;AACpD,EAAA,OAAO,kBAAkB,WAAA,KAAgB,SAAA,GACrC,qBAAqB,IAAI,CAAA,GACzB,kBAAkB,IAAI,CAAA;AAC5B","file":"chunk-E3GUGJMY.js","sourcesContent":["/**\n * Product slug aliases for products whose machine slug is being renamed\n * (today: `botlit` → `botmanor`, see botmanor-migration PLAN.md Stage 0 / WP-E).\n *\n * - Readers accept both slugs: compare with {@link isSameProductSlug}, look up\n * with {@link productSlugVariants}.\n * - Writers emit {@link emitProductSlug}, which follows {@link PRODUCT_SLUG_EMIT}\n * (`\"current\"` since PLAN.md Stage 5a, so writers emit `botmanor`).\n *\n * Mirrors `@burdenoff/be-sdk/product-alias` (product-slug concern only) so the\n * browser bundle does not pull in the backend SDK.\n */\n\n/** One product whose slug is being renamed. Both values are lowercase. */\nexport interface ProductSlugRename {\n readonly legacy: string;\n readonly current: string;\n}\n\n/** Every product slug rename in flight. */\nexport const PRODUCT_SLUG_RENAMES: readonly ProductSlugRename[] = Object.freeze(\n [Object.freeze({ legacy: \"botlit\", current: \"botmanor\" })],\n);\n\n/** Legacy slug → current slug, e.g. `{ botlit: \"botmanor\" }`. */\nexport const PRODUCT_SLUG_ALIASES: Readonly<Record<string, string>> =\n Object.freeze(\n Object.fromEntries(PRODUCT_SLUG_RENAMES.map((r) => [r.legacy, r.current])),\n );\n\n/** Which spelling a writer emits. */\nexport type ProductSlugGeneration = \"legacy\" | \"current\";\n\n/**\n * Which slug writers emit. Flipped to `\"current\"` at PLAN.md Stage 5a: stored\n * product slugs are migrated to `botmanor`, and every reader accepts both.\n */\nexport const PRODUCT_SLUG_EMIT: Readonly<{\n productSlug: ProductSlugGeneration;\n}> = Object.freeze({ productSlug: \"current\" });\n\nfunction findRename(slug: string): ProductSlugRename | undefined {\n const wanted = slug.toLowerCase();\n return PRODUCT_SLUG_RENAMES.find(\n (r) => r.legacy === wanted || r.current === wanted,\n );\n}\n\n/** `botlit` (any case) → `botmanor`; every other slug is returned unchanged. */\nexport function normalizeProductSlug(slug: string): string;\nexport function normalizeProductSlug(\n slug: string | null | undefined,\n): string | null | undefined;\nexport function normalizeProductSlug(\n slug: string | null | undefined,\n): string | null | undefined {\n if (slug == null) return slug;\n return findRename(slug)?.current ?? slug;\n}\n\n/** Old spelling of a renamed slug (`botmanor` → `botlit`); every other slug unchanged. */\nexport function legacyProductSlug(slug: string): string {\n return findRename(slug)?.legacy ?? slug;\n}\n\n/** Every slug a reader must accept for `slug`, the given one first. */\nexport function productSlugVariants(slug: string): string[] {\n // Canonical lowercase keys come after the given spelling, so \"BotLit\" yields both keys.\n return [\n ...new Set([slug, normalizeProductSlug(slug), legacyProductSlug(slug)]),\n ];\n}\n\n/** True when `a` and `b` are the same product under the rename. */\nexport function isSameProductSlug(\n a: string | null | undefined,\n b: string | null | undefined,\n): boolean {\n if (a == null || b == null) return a === b;\n return a === b || normalizeProductSlug(a) === normalizeProductSlug(b);\n}\n\n/** The slug writers should use today, from either spelling. */\nexport function emitProductSlug(slug: string): string {\n return PRODUCT_SLUG_EMIT.productSlug === \"current\"\n ? normalizeProductSlug(slug)\n : legacyProductSlug(slug);\n}\n"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import ReactMarkdown from 'react-markdown';
|
|
2
|
+
import rehypeSanitize from 'rehype-sanitize';
|
|
3
|
+
import remarkGfm from 'remark-gfm';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
function MarkdownLink({
|
|
7
|
+
href,
|
|
8
|
+
children,
|
|
9
|
+
// react-markdown passes the hast node to every override; it is not a DOM attribute.
|
|
10
|
+
node: _node,
|
|
11
|
+
...props
|
|
12
|
+
}) {
|
|
13
|
+
const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
|
|
14
|
+
return /* @__PURE__ */ jsx(
|
|
15
|
+
"a",
|
|
16
|
+
{
|
|
17
|
+
href,
|
|
18
|
+
...props,
|
|
19
|
+
...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
|
|
20
|
+
children
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
var defaultComponents = {
|
|
25
|
+
a: MarkdownLink
|
|
26
|
+
};
|
|
27
|
+
function Markdown({
|
|
28
|
+
children,
|
|
29
|
+
className,
|
|
30
|
+
inline,
|
|
31
|
+
components,
|
|
32
|
+
rehypePlugins
|
|
33
|
+
}) {
|
|
34
|
+
const content = /* @__PURE__ */ jsx(
|
|
35
|
+
ReactMarkdown,
|
|
36
|
+
{
|
|
37
|
+
remarkPlugins: [remarkGfm],
|
|
38
|
+
rehypePlugins: [rehypeSanitize, ...rehypePlugins ?? []],
|
|
39
|
+
components: { ...defaultComponents, ...components },
|
|
40
|
+
...inline ? { disallowedElements: ["p"], unwrapDisallowed: true } : {},
|
|
41
|
+
children
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
if (inline) {
|
|
45
|
+
return /* @__PURE__ */ jsx("span", { className, children: content });
|
|
46
|
+
}
|
|
47
|
+
return /* @__PURE__ */ jsx("div", { className, children: content });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { Markdown, MarkdownLink };
|
|
51
|
+
//# sourceMappingURL=chunk-EBZ2HGJ2.mjs.map
|
|
52
|
+
//# sourceMappingURL=chunk-EBZ2HGJ2.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/content/Markdown.tsx"],"names":[],"mappings":";;;;;AA8BO,SAAS,YAAA,CAAa;AAAA,EAC3B,IAAA;AAAA,EACA,QAAA;AAAA;AAAA,EAEA,IAAA,EAAM,KAAA;AAAA,EACN,GAAG;AACL,CAAA,EAAqC;AACnC,EAAA,MAAM,UAAA,GACJ,OAAO,IAAA,KAAS,QAAA,KACf,IAAA,CAAK,WAAW,SAAS,CAAA,IAAK,IAAA,CAAK,UAAA,CAAW,UAAU,CAAA,CAAA;AAC3D,EAAA,uBACE,GAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,IAAA;AAAA,MACC,GAAG,KAAA;AAAA,MACH,GAAI,UAAA,GACD,EAAE,MAAA,EAAQ,QAAA,EAAU,KAAK,qBAAA,EAAsB,GAC/C,EAAE,GAAA,EAAK,qBAAA,EAAsB;AAAA,MAEhC;AAAA;AAAA,GACH;AAEJ;AAEA,IAAM,iBAAA,GAAgC;AAAA,EACpC,CAAA,EAAG;AACL,CAAA;AAUO,SAAS,QAAA,CAAS;AAAA,EACvB,QAAA;AAAA,EACA,SAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF,CAAA,EAA6B;AAC3B,EAAA,MAAM,OAAA,mBACJ,GAAA;AAAA,IAAC,aAAA;AAAA,IAAA;AAAA,MACC,aAAA,EAAe,CAAC,SAAS,CAAA;AAAA,MACzB,eAAe,CAAC,cAAA,EAAgB,GAAI,aAAA,IAAiB,EAAG,CAAA;AAAA,MACxD,UAAA,EAAY,EAAE,GAAG,iBAAA,EAAmB,GAAG,UAAA,EAAW;AAAA,MACjD,GAAI,MAAA,GAAS,EAAE,kBAAA,EAAoB,CAAC,GAAG,CAAA,EAAG,gBAAA,EAAkB,IAAA,EAAK,GAAI,EAAC;AAAA,MAEtE;AAAA;AAAA,GACH;AAGF,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,uBAAO,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAuB,QAAA,EAAA,OAAA,EAAQ,CAAA;AAAA,EAC9C;AAEA,EAAA,uBAAO,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAuB,QAAA,EAAA,OAAA,EAAQ,CAAA;AAC7C","file":"chunk-EBZ2HGJ2.mjs","sourcesContent":["import type { ComponentProps, ReactNode } from \"react\";\n\nimport ReactMarkdown from \"react-markdown\";\nimport rehypeSanitize from \"rehype-sanitize\";\nimport remarkGfm from \"remark-gfm\";\n\nimport type { Components, ExtraProps, Options } from \"react-markdown\";\n\nexport interface MarkdownProps {\n /** Markdown source to render. */\n children: string;\n /** Additional classes for the wrapper element. */\n className?: string;\n /** Render without a wrapping block element, suitable for inline content. */\n inline?: boolean;\n /** Optional react-markdown component overrides. */\n components?: Components;\n /**\n * Extra rehype plugins. They run AFTER `rehype-sanitize`, on the already-safe tree, so they\n * are for annotating nodes an override then reads — never for adding markup, because\n * nothing sanitises what they insert.\n */\n rehypePlugins?: Options[\"rehypePlugins\"];\n}\n\n/**\n * The default markdown link: external links open in a new tab, every link gets safe `rel`\n * attributes. Exported so a caller overriding `a` (to add context around links, say) can\n * still render the same element.\n */\nexport function MarkdownLink({\n href,\n children,\n // react-markdown passes the hast node to every override; it is not a DOM attribute.\n node: _node,\n ...props\n}: ComponentProps<\"a\"> & ExtraProps) {\n const isExternal =\n typeof href === \"string\" &&\n (href.startsWith(\"http://\") || href.startsWith(\"https://\"));\n return (\n <a\n href={href}\n {...props}\n {...(isExternal\n ? { target: \"_blank\", rel: \"noopener noreferrer\" }\n : { rel: \"noopener noreferrer\" })}\n >\n {children}\n </a>\n );\n}\n\nconst defaultComponents: Components = {\n a: MarkdownLink,\n};\n\n/**\n * Shared markdown renderer for website content.\n *\n * - Uses `react-markdown` for standards-compliant parsing.\n * - Enables GitHub Flavored Markdown (tables, task lists, strikethrough, autolinks).\n * - Sanitizes HTML via `rehype-sanitize` so raw markup in markdown is stripped.\n * - External links open in a new tab with safe rel attributes.\n */\nexport function Markdown({\n children,\n className,\n inline,\n components,\n rehypePlugins,\n}: MarkdownProps): ReactNode {\n const content = (\n <ReactMarkdown\n remarkPlugins={[remarkGfm]}\n rehypePlugins={[rehypeSanitize, ...(rehypePlugins ?? [])]}\n components={{ ...defaultComponents, ...components }}\n {...(inline ? { disallowedElements: [\"p\"], unwrapDisallowed: true } : {})}\n >\n {children}\n </ReactMarkdown>\n );\n\n if (inline) {\n return <span className={className}>{content}</span>;\n }\n\n return <div className={className}>{content}</div>;\n}\n"]}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { Markdown } from './chunk-EBZ2HGJ2.mjs';
|
|
2
|
+
import { useProduct } from './chunk-GCFLCO2Z.mjs';
|
|
3
|
+
import { useWebSDK, useWebSDKConfig } from './chunk-5JRJ57CL.mjs';
|
|
4
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
5
|
+
import DOMPurify from 'isomorphic-dompurify';
|
|
6
|
+
import { jsx } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
var PUBLIC_CONTENT_PAGE_QUERY = `
|
|
9
|
+
query PublicContentPage($slug: String!, $productId: String) {
|
|
10
|
+
publicContentPage(slug: $slug, productId: $productId) {
|
|
11
|
+
id
|
|
12
|
+
title
|
|
13
|
+
slug
|
|
14
|
+
content
|
|
15
|
+
contentFormat
|
|
16
|
+
seoTitle
|
|
17
|
+
seoDescription
|
|
18
|
+
seoKeywords
|
|
19
|
+
lastUpdated
|
|
20
|
+
version
|
|
21
|
+
excerpt
|
|
22
|
+
thumbnail
|
|
23
|
+
author
|
|
24
|
+
publishedAt
|
|
25
|
+
readingTime
|
|
26
|
+
featured
|
|
27
|
+
category
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
var PUBLIC_RESOLVED_CONTENT_PAGE_QUERY = `
|
|
32
|
+
query PublicResolvedContentPage($slug: String!, $productId: String!) {
|
|
33
|
+
publicResolvedContentPage(slug: $slug, productId: $productId) {
|
|
34
|
+
id
|
|
35
|
+
title
|
|
36
|
+
slug
|
|
37
|
+
content
|
|
38
|
+
contentFormat
|
|
39
|
+
seoTitle
|
|
40
|
+
seoDescription
|
|
41
|
+
seoKeywords
|
|
42
|
+
lastUpdated
|
|
43
|
+
version
|
|
44
|
+
excerpt
|
|
45
|
+
thumbnail
|
|
46
|
+
author
|
|
47
|
+
publishedAt
|
|
48
|
+
readingTime
|
|
49
|
+
featured
|
|
50
|
+
category
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
var PUBLIC_CONTENT_PAGES_QUERY = `
|
|
55
|
+
query PublicContentPages($productId: String!, $limit: Int, $offset: Int) {
|
|
56
|
+
publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
|
|
57
|
+
items {
|
|
58
|
+
id
|
|
59
|
+
title
|
|
60
|
+
slug
|
|
61
|
+
lastUpdated
|
|
62
|
+
tags
|
|
63
|
+
seoDescription
|
|
64
|
+
excerpt
|
|
65
|
+
thumbnail
|
|
66
|
+
author
|
|
67
|
+
publishedAt
|
|
68
|
+
readingTime
|
|
69
|
+
featured
|
|
70
|
+
category
|
|
71
|
+
}
|
|
72
|
+
total
|
|
73
|
+
hasMore
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
`;
|
|
77
|
+
function resolveContentPageGate(resolvedProductId, hasProvider, productLoading) {
|
|
78
|
+
if (resolvedProductId) return "fetch";
|
|
79
|
+
return hasProvider && productLoading ? "wait" : "unavailable";
|
|
80
|
+
}
|
|
81
|
+
function useContentPage(slug, options) {
|
|
82
|
+
const client = useWebSDK();
|
|
83
|
+
const config = useWebSDKConfig();
|
|
84
|
+
const {
|
|
85
|
+
product,
|
|
86
|
+
loading: productLoading,
|
|
87
|
+
error: productError,
|
|
88
|
+
hasProvider
|
|
89
|
+
} = useProduct();
|
|
90
|
+
const resolvedProductId = product?.id || config.productId;
|
|
91
|
+
const gate = resolveContentPageGate(
|
|
92
|
+
resolvedProductId,
|
|
93
|
+
hasProvider,
|
|
94
|
+
productLoading
|
|
95
|
+
);
|
|
96
|
+
const enabled = options?.enabled !== false;
|
|
97
|
+
const [data, setData] = useState(null);
|
|
98
|
+
const [loading, setLoading] = useState(enabled);
|
|
99
|
+
const [error, setError] = useState(null);
|
|
100
|
+
const fetchPage = useCallback(async () => {
|
|
101
|
+
if (!enabled) {
|
|
102
|
+
setLoading(false);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (gate === "wait") return;
|
|
106
|
+
if (gate === "unavailable") {
|
|
107
|
+
setData(null);
|
|
108
|
+
setError(
|
|
109
|
+
productError ?? "Product unavailable \u2014 cannot load content page"
|
|
110
|
+
);
|
|
111
|
+
setLoading(false);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
setLoading(true);
|
|
115
|
+
setError(null);
|
|
116
|
+
try {
|
|
117
|
+
const query = options?.resolve ? PUBLIC_RESOLVED_CONTENT_PAGE_QUERY : PUBLIC_CONTENT_PAGE_QUERY;
|
|
118
|
+
const queryName = options?.resolve ? "publicResolvedContentPage" : "publicContentPage";
|
|
119
|
+
const result = await client.query(
|
|
120
|
+
query,
|
|
121
|
+
{
|
|
122
|
+
slug,
|
|
123
|
+
productId: resolvedProductId
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
if (result.errors?.length) {
|
|
127
|
+
setError(result.errors[0]?.message ?? "Unknown error");
|
|
128
|
+
setData(null);
|
|
129
|
+
} else {
|
|
130
|
+
setData(result.data?.[queryName] ?? null);
|
|
131
|
+
}
|
|
132
|
+
} catch (err) {
|
|
133
|
+
setError(err instanceof Error ? err.message : "Failed to fetch content");
|
|
134
|
+
setData(null);
|
|
135
|
+
} finally {
|
|
136
|
+
setLoading(false);
|
|
137
|
+
}
|
|
138
|
+
}, [
|
|
139
|
+
client,
|
|
140
|
+
enabled,
|
|
141
|
+
gate,
|
|
142
|
+
productError,
|
|
143
|
+
resolvedProductId,
|
|
144
|
+
slug,
|
|
145
|
+
options?.resolve
|
|
146
|
+
]);
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
fetchPage();
|
|
149
|
+
}, [fetchPage]);
|
|
150
|
+
return { data, loading, error, refetch: fetchPage };
|
|
151
|
+
}
|
|
152
|
+
function useContentPages(options) {
|
|
153
|
+
const client = useWebSDK();
|
|
154
|
+
const config = useWebSDKConfig();
|
|
155
|
+
const [data, setData] = useState(null);
|
|
156
|
+
const [loading, setLoading] = useState(true);
|
|
157
|
+
const [error, setError] = useState(null);
|
|
158
|
+
const fetchPages = useCallback(async () => {
|
|
159
|
+
setLoading(true);
|
|
160
|
+
setError(null);
|
|
161
|
+
try {
|
|
162
|
+
const result = await client.query(PUBLIC_CONTENT_PAGES_QUERY, {
|
|
163
|
+
productId: config.productId,
|
|
164
|
+
limit: options?.limit ?? 50,
|
|
165
|
+
offset: options?.offset ?? 0
|
|
166
|
+
});
|
|
167
|
+
if (result.errors?.length) {
|
|
168
|
+
setError(result.errors[0]?.message ?? "Unknown error");
|
|
169
|
+
setData(null);
|
|
170
|
+
} else {
|
|
171
|
+
setData(result.data?.publicContentPages ?? null);
|
|
172
|
+
}
|
|
173
|
+
} catch (err) {
|
|
174
|
+
setError(
|
|
175
|
+
err instanceof Error ? err.message : "Failed to fetch content pages"
|
|
176
|
+
);
|
|
177
|
+
setData(null);
|
|
178
|
+
} finally {
|
|
179
|
+
setLoading(false);
|
|
180
|
+
}
|
|
181
|
+
}, [client, config.productId, options?.limit, options?.offset]);
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
fetchPages();
|
|
184
|
+
}, [fetchPages]);
|
|
185
|
+
return { data, loading, error, refetch: fetchPages };
|
|
186
|
+
}
|
|
187
|
+
function ContentRenderer({
|
|
188
|
+
content,
|
|
189
|
+
format = "markdown",
|
|
190
|
+
className
|
|
191
|
+
}) {
|
|
192
|
+
if (format === "markdown") {
|
|
193
|
+
return /* @__PURE__ */ jsx(Markdown, { className, children: content });
|
|
194
|
+
}
|
|
195
|
+
if (format === "html") {
|
|
196
|
+
const sanitized = DOMPurify.sanitize(content);
|
|
197
|
+
return /* @__PURE__ */ jsx(
|
|
198
|
+
"div",
|
|
199
|
+
{
|
|
200
|
+
className,
|
|
201
|
+
dangerouslySetInnerHTML: { __html: sanitized }
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx("pre", { style: { whiteSpace: "pre-wrap", fontFamily: "inherit" }, children: content }) });
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export { ContentRenderer, useContentPage, useContentPages };
|
|
209
|
+
//# sourceMappingURL=chunk-EDLTUOIA.mjs.map
|
|
210
|
+
//# sourceMappingURL=chunk-EDLTUOIA.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/content/content-provider.tsx"],"names":[],"mappings":"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","file":"chunk-EDLTUOIA.mjs","sourcesContent":["/**\n * Content Provider\n *\n * React hooks for fetching public content pages from the global tenant service.\n * Content pages include privacy policies, terms of service, blogs, changelogs, etc.\n */\n\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\n\nimport DOMPurify from \"isomorphic-dompurify\";\n\nimport { Markdown } from \"./Markdown\";\nimport { useWebSDK, useWebSDKConfig } from \"../client/provider\";\nimport { useProduct } from \"../product/product-provider\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * A customer's written approval of a case study (BOFF-9971). Public callers\n * receive only the approval date; who approved it and where the approval is\n * filed stay with the people who manage content pages.\n */\nexport interface CustomerSignOffData {\n signedAt: string;\n signedBy?: string | null;\n documentRef?: string | null;\n}\n\nexport interface ContentPageData {\n id: string;\n title: string;\n slug: string;\n content: string;\n contentFormat: string;\n seoTitle?: string | null;\n seoDescription?: string | null;\n seoKeywords?: string | null;\n lastUpdated: string;\n version: number;\n tags?: string[];\n excerpt?: string | null;\n thumbnail?: string | null;\n author?: string | null;\n publishedAt?: string | null;\n readingTime?: number | null;\n featured?: boolean;\n category?: string | null;\n customerSignOff?: CustomerSignOffData | null;\n}\n\nexport interface ContentPageListItem {\n id: string;\n title: string;\n slug: string;\n lastUpdated: string;\n tags?: string[];\n excerpt?: string | null;\n thumbnail?: string | null;\n author?: string | null;\n publishedAt?: string | null;\n readingTime?: number | null;\n featured?: boolean;\n category?: string | null;\n seoDescription?: string | null;\n customerSignOff?: CustomerSignOffData | null;\n}\n\nexport interface ContentPageListData {\n items: ContentPageListItem[];\n total: number;\n hasMore: boolean;\n}\n\nexport interface UseContentPageOptions {\n /** Whether to use template variable replacement (resolvedContentPage) */\n resolve?: boolean;\n /**\n * Set to `false` to skip the request entirely — e.g. when the page was\n * already fetched at build time. The hook then settles immediately with\n * `data: null`, `loading: false`. Defaults to `true`.\n */\n enabled?: boolean;\n}\n\n// ============================================================================\n// GraphQL Queries\n// ============================================================================\n\nconst PUBLIC_CONTENT_PAGE_QUERY = `\n query PublicContentPage($slug: String!, $productId: String) {\n publicContentPage(slug: $slug, productId: $productId) {\n id\n title\n slug\n content\n contentFormat\n seoTitle\n seoDescription\n seoKeywords\n lastUpdated\n version\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n }\n`;\n\nconst PUBLIC_RESOLVED_CONTENT_PAGE_QUERY = `\n query PublicResolvedContentPage($slug: String!, $productId: String!) {\n publicResolvedContentPage(slug: $slug, productId: $productId) {\n id\n title\n slug\n content\n contentFormat\n seoTitle\n seoDescription\n seoKeywords\n lastUpdated\n version\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n }\n`;\n\nconst PUBLIC_CONTENT_PAGES_QUERY = `\n query PublicContentPages($productId: String!, $limit: Int, $offset: Int) {\n publicContentPages(productId: $productId, limit: $limit, offset: $offset) {\n items {\n id\n title\n slug\n lastUpdated\n tags\n seoDescription\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n total\n hasMore\n }\n }\n`;\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * Decides what `useContentPage` should do about the product-id dependency.\n *\n * The product id comes from `useProduct()` (a `publicProduct(slug)` lookup)\n * or falls back to `config.productId`. While that lookup is in flight there\n * is nothing to fetch — but once it has settled (or no ProductProvider is\n * mounted and no config id exists) waiting is indistinguishable from hanging:\n * the hook must settle with an error so consumers render their fallback\n * instead of a permanent loading skeleton (BOFF-6190).\n */\nexport type ContentPageGate = \"wait\" | \"fetch\" | \"unavailable\";\n\nexport function resolveContentPageGate(\n resolvedProductId: string | undefined | null,\n hasProvider: boolean,\n productLoading: boolean,\n): ContentPageGate {\n if (resolvedProductId) return \"fetch\";\n // Only an in-flight provider lookup is a real reason to keep waiting.\n // Without a provider the default context reports loading forever.\n return hasProvider && productLoading ? \"wait\" : \"unavailable\";\n}\n\n/**\n * Hook to fetch a single public content page by slug.\n *\n * @example\n * ```tsx\n * const { data, loading, error } = useContentPage('privacy-policy');\n * ```\n */\nexport function useContentPage(\n slug: string,\n options?: UseContentPageOptions,\n): {\n data: ContentPageData | null;\n loading: boolean;\n error: string | null;\n refetch: () => void;\n} {\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const {\n product,\n loading: productLoading,\n error: productError,\n hasProvider,\n } = useProduct();\n const resolvedProductId = product?.id || config.productId;\n const gate = resolveContentPageGate(\n resolvedProductId,\n hasProvider,\n productLoading,\n );\n const enabled = options?.enabled !== false;\n const [data, setData] = useState<ContentPageData | null>(null);\n const [loading, setLoading] = useState(enabled);\n const [error, setError] = useState<string | null>(null);\n\n const fetchPage = useCallback(async () => {\n if (!enabled) {\n setLoading(false);\n return;\n }\n if (gate === \"wait\") return; // Product lookup still in flight\n\n if (gate === \"unavailable\") {\n // The product lookup settled without an id (or there is no provider\n // and no config.productId): the fetch can never start. Settle with an\n // error so consumers render their fallback, not a permanent skeleton.\n setData(null);\n setError(\n productError ?? \"Product unavailable — cannot load content page\",\n );\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const query = options?.resolve\n ? PUBLIC_RESOLVED_CONTENT_PAGE_QUERY\n : PUBLIC_CONTENT_PAGE_QUERY;\n\n const queryName = options?.resolve\n ? \"publicResolvedContentPage\"\n : \"publicContentPage\";\n\n const result = await client.query<Record<string, ContentPageData | null>>(\n query,\n {\n slug,\n productId: resolvedProductId,\n },\n );\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Unknown error\");\n setData(null);\n } else {\n setData(result.data?.[queryName] ?? null);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to fetch content\");\n setData(null);\n } finally {\n setLoading(false);\n }\n }, [\n client,\n enabled,\n gate,\n productError,\n resolvedProductId,\n slug,\n options?.resolve,\n ]);\n\n useEffect(() => {\n fetchPage();\n }, [fetchPage]);\n\n return { data, loading, error, refetch: fetchPage };\n}\n\n/**\n * Hook to fetch a list of public content pages for the current product.\n *\n * @example\n * ```tsx\n * const { data, loading } = useContentPages({ limit: 10 });\n * ```\n */\nexport function useContentPages(options?: {\n limit?: number;\n offset?: number;\n}): {\n data: ContentPageListData | null;\n loading: boolean;\n error: string | null;\n refetch: () => void;\n} {\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const [data, setData] = useState<ContentPageListData | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const fetchPages = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n try {\n const result = await client.query<{\n publicContentPages: ContentPageListData;\n }>(PUBLIC_CONTENT_PAGES_QUERY, {\n productId: config.productId,\n limit: options?.limit ?? 50,\n offset: options?.offset ?? 0,\n });\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Unknown error\");\n setData(null);\n } else {\n setData(result.data?.publicContentPages ?? null);\n }\n } catch (err) {\n setError(\n err instanceof Error ? err.message : \"Failed to fetch content pages\",\n );\n setData(null);\n } finally {\n setLoading(false);\n }\n }, [client, config.productId, options?.limit, options?.offset]);\n\n useEffect(() => {\n fetchPages();\n }, [fetchPages]);\n\n return { data, loading, error, refetch: fetchPages };\n}\n\n/**\n * Content renderer component.\n *\n * - `markdown` (default): renders with the shared {@link Markdown} component\n * (react-markdown + remark-gfm + rehype-sanitize).\n * - `html`: sanitized with DOMPurify before rendering.\n * - anything else: plain-text fallback\n *\n * @example\n * ```tsx\n * <ContentRenderer content={page.content} format={page.contentFormat} />\n * ```\n */\nexport function ContentRenderer({\n content,\n format = \"markdown\",\n className,\n}: {\n content: string;\n format?: string;\n className?: string;\n}) {\n if (format === \"markdown\") {\n return <Markdown className={className}>{content}</Markdown>;\n }\n\n if (format === \"html\") {\n const sanitized = DOMPurify.sanitize(content);\n return (\n <div\n className={className}\n dangerouslySetInnerHTML={{ __html: sanitized }}\n />\n );\n }\n\n // Plain text fallback\n return (\n <div className={className}>\n <pre style={{ whiteSpace: \"pre-wrap\", fontFamily: \"inherit\" }}>\n {content}\n </pre>\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-EJQSOT4X.js"}
|