@sonordev/site-kit 1.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +376 -0
- package/dist/SetupWizard-Cki06kB0.d.mts +12 -0
- package/dist/SetupWizard-Cki06kB0.d.ts +12 -0
- package/dist/analytics/index.d.mts +93 -0
- package/dist/analytics/index.d.ts +93 -0
- package/dist/analytics/index.js +89 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/index.mjs +71 -0
- package/dist/analytics/index.mjs.map +1 -0
- package/dist/api-CWtoFJCO.d.mts +137 -0
- package/dist/api-CWtoFJCO.d.ts +137 -0
- package/dist/blog/index.d.mts +305 -0
- package/dist/blog/index.d.ts +305 -0
- package/dist/blog/index.js +1578 -0
- package/dist/blog/index.js.map +1 -0
- package/dist/blog/index.mjs +1562 -0
- package/dist/blog/index.mjs.map +1 -0
- package/dist/blog/server.d.mts +229 -0
- package/dist/blog/server.d.ts +229 -0
- package/dist/blog/server.js +692 -0
- package/dist/blog/server.js.map +1 -0
- package/dist/blog/server.mjs +666 -0
- package/dist/blog/server.mjs.map +1 -0
- package/dist/chunk-24277A3Q.mjs +968 -0
- package/dist/chunk-24277A3Q.mjs.map +1 -0
- package/dist/chunk-373TK6TZ.js +321 -0
- package/dist/chunk-373TK6TZ.js.map +1 -0
- package/dist/chunk-3MYZS6PD.js +30 -0
- package/dist/chunk-3MYZS6PD.js.map +1 -0
- package/dist/chunk-43GBM4SX.js +283 -0
- package/dist/chunk-43GBM4SX.js.map +1 -0
- package/dist/chunk-4XPGGLVP.mjs +53 -0
- package/dist/chunk-4XPGGLVP.mjs.map +1 -0
- package/dist/chunk-622GAQP5.js +2008 -0
- package/dist/chunk-622GAQP5.js.map +1 -0
- package/dist/chunk-6BIPAKL4.mjs +28 -0
- package/dist/chunk-6BIPAKL4.mjs.map +1 -0
- package/dist/chunk-6ZCISNAB.mjs +343 -0
- package/dist/chunk-6ZCISNAB.mjs.map +1 -0
- package/dist/chunk-72MQFHYJ.js +1429 -0
- package/dist/chunk-72MQFHYJ.js.map +1 -0
- package/dist/chunk-7557OTHW.js +62 -0
- package/dist/chunk-7557OTHW.js.map +1 -0
- package/dist/chunk-7FUV73JZ.js +981 -0
- package/dist/chunk-7FUV73JZ.js.map +1 -0
- package/dist/chunk-7RF6PVHA.mjs +324 -0
- package/dist/chunk-7RF6PVHA.mjs.map +1 -0
- package/dist/chunk-7RYCHO6D.mjs +134 -0
- package/dist/chunk-7RYCHO6D.mjs.map +1 -0
- package/dist/chunk-7UKPRW25.mjs +1999 -0
- package/dist/chunk-7UKPRW25.mjs.map +1 -0
- package/dist/chunk-7URAOG2M.js +14864 -0
- package/dist/chunk-7URAOG2M.js.map +1 -0
- package/dist/chunk-AFAO3TGS.mjs +810 -0
- package/dist/chunk-AFAO3TGS.mjs.map +1 -0
- package/dist/chunk-BYLIU6XG.js +343 -0
- package/dist/chunk-BYLIU6XG.js.map +1 -0
- package/dist/chunk-D63MUKZ6.mjs +4423 -0
- package/dist/chunk-D63MUKZ6.mjs.map +1 -0
- package/dist/chunk-DDKW2FNA.js +390 -0
- package/dist/chunk-DDKW2FNA.js.map +1 -0
- package/dist/chunk-DQYMKR27.mjs +341 -0
- package/dist/chunk-DQYMKR27.mjs.map +1 -0
- package/dist/chunk-DW5UJKHH.js +221 -0
- package/dist/chunk-DW5UJKHH.js.map +1 -0
- package/dist/chunk-EEZCR6E6.js +50 -0
- package/dist/chunk-EEZCR6E6.js.map +1 -0
- package/dist/chunk-GCJXQ4AG.mjs +59 -0
- package/dist/chunk-GCJXQ4AG.mjs.map +1 -0
- package/dist/chunk-JGNQK2G6.mjs +14845 -0
- package/dist/chunk-JGNQK2G6.mjs.map +1 -0
- package/dist/chunk-JTLOJLWQ.mjs +563 -0
- package/dist/chunk-JTLOJLWQ.mjs.map +1 -0
- package/dist/chunk-K23A4G76.mjs +202 -0
- package/dist/chunk-K23A4G76.mjs.map +1 -0
- package/dist/chunk-KKU3K7RG.js +336 -0
- package/dist/chunk-KKU3K7RG.js.map +1 -0
- package/dist/chunk-KUGMH4ZF.js +571 -0
- package/dist/chunk-KUGMH4ZF.js.map +1 -0
- package/dist/chunk-LBVWVP72.js +110 -0
- package/dist/chunk-LBVWVP72.js.map +1 -0
- package/dist/chunk-LIVWLY2P.js +138 -0
- package/dist/chunk-LIVWLY2P.js.map +1 -0
- package/dist/chunk-M2T6R7BA.mjs +1003 -0
- package/dist/chunk-M2T6R7BA.mjs.map +1 -0
- package/dist/chunk-MV3QN7PW.mjs +47 -0
- package/dist/chunk-MV3QN7PW.mjs.map +1 -0
- package/dist/chunk-OB7E654K.js +72 -0
- package/dist/chunk-OB7E654K.js.map +1 -0
- package/dist/chunk-OIIKTGRL.mjs +380 -0
- package/dist/chunk-OIIKTGRL.mjs.map +1 -0
- package/dist/chunk-P3UWIUJS.mjs +1427 -0
- package/dist/chunk-P3UWIUJS.mjs.map +1 -0
- package/dist/chunk-PKN27UMH.mjs +136 -0
- package/dist/chunk-PKN27UMH.mjs.map +1 -0
- package/dist/chunk-QXV4667R.mjs +105 -0
- package/dist/chunk-QXV4667R.mjs.map +1 -0
- package/dist/chunk-S7FRYNSU.mjs +315 -0
- package/dist/chunk-S7FRYNSU.mjs.map +1 -0
- package/dist/chunk-TFLQX7K7.mjs +68 -0
- package/dist/chunk-TFLQX7K7.mjs.map +1 -0
- package/dist/chunk-UWE5PCYJ.mjs +279 -0
- package/dist/chunk-UWE5PCYJ.mjs.map +1 -0
- package/dist/chunk-UYFDNX2F.js +4469 -0
- package/dist/chunk-UYFDNX2F.js.map +1 -0
- package/dist/chunk-W4PALSGM.js +350 -0
- package/dist/chunk-W4PALSGM.js.map +1 -0
- package/dist/chunk-WECQ6KOB.js +1008 -0
- package/dist/chunk-WECQ6KOB.js.map +1 -0
- package/dist/chunk-XQQWI6WB.js +814 -0
- package/dist/chunk-XQQWI6WB.js.map +1 -0
- package/dist/chunk-XZJOZJB6.js +140 -0
- package/dist/chunk-XZJOZJB6.js.map +1 -0
- package/dist/chunk-ZSMWDLMK.js +63 -0
- package/dist/chunk-ZSMWDLMK.js.map +1 -0
- package/dist/cli/index.js +37243 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/index.mjs +37209 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/commerce/index.d.mts +170 -0
- package/dist/commerce/index.d.ts +170 -0
- package/dist/commerce/index.js +174 -0
- package/dist/commerce/index.js.map +1 -0
- package/dist/commerce/index.mjs +5 -0
- package/dist/commerce/index.mjs.map +1 -0
- package/dist/commerce/server.d.mts +107 -0
- package/dist/commerce/server.d.ts +107 -0
- package/dist/commerce/server.js +187 -0
- package/dist/commerce/server.js.map +1 -0
- package/dist/commerce/server.mjs +177 -0
- package/dist/commerce/server.mjs.map +1 -0
- package/dist/config/index.d.mts +43 -0
- package/dist/config/index.d.ts +43 -0
- package/dist/config/index.js +66 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/index.mjs +64 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/engage/index.d.mts +33 -0
- package/dist/engage/index.d.ts +33 -0
- package/dist/engage/index.js +22 -0
- package/dist/engage/index.js.map +1 -0
- package/dist/engage/index.mjs +5 -0
- package/dist/engage/index.mjs.map +1 -0
- package/dist/forms/index.d.mts +437 -0
- package/dist/forms/index.d.ts +437 -0
- package/dist/forms/index.js +1168 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/forms/index.mjs +1142 -0
- package/dist/forms/index.mjs.map +1 -0
- package/dist/generators-2XKQMPKH.mjs +4 -0
- package/dist/generators-2XKQMPKH.mjs.map +1 -0
- package/dist/generators-DTMO36DV.js +33 -0
- package/dist/generators-DTMO36DV.js.map +1 -0
- package/dist/images/index.d.mts +4 -0
- package/dist/images/index.d.ts +4 -0
- package/dist/images/index.js +46 -0
- package/dist/images/index.js.map +1 -0
- package/dist/images/index.mjs +5 -0
- package/dist/images/index.mjs.map +1 -0
- package/dist/images/server.d.mts +69 -0
- package/dist/images/server.d.ts +69 -0
- package/dist/images/server.js +21 -0
- package/dist/images/server.js.map +1 -0
- package/dist/images/server.mjs +4 -0
- package/dist/images/server.mjs.map +1 -0
- package/dist/index.d.mts +846 -0
- package/dist/index.d.ts +846 -0
- package/dist/index.js +2623 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2416 -0
- package/dist/index.mjs.map +1 -0
- package/dist/layout/index.d.mts +53 -0
- package/dist/layout/index.d.ts +53 -0
- package/dist/layout/index.js +187 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/layout/index.mjs +185 -0
- package/dist/layout/index.mjs.map +1 -0
- package/dist/llms/index.d.mts +448 -0
- package/dist/llms/index.d.ts +448 -0
- package/dist/llms/index.js +581 -0
- package/dist/llms/index.js.map +1 -0
- package/dist/llms/index.mjs +529 -0
- package/dist/llms/index.mjs.map +1 -0
- package/dist/manifest/index.d.mts +62 -0
- package/dist/manifest/index.d.ts +62 -0
- package/dist/manifest/index.js +85 -0
- package/dist/manifest/index.js.map +1 -0
- package/dist/manifest/index.mjs +83 -0
- package/dist/manifest/index.mjs.map +1 -0
- package/dist/middleware/index.d.mts +63 -0
- package/dist/middleware/index.d.ts +63 -0
- package/dist/middleware/index.js +54 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/index.mjs +51 -0
- package/dist/middleware/index.mjs.map +1 -0
- package/dist/migrator-2MQHOFDQ.mjs +4 -0
- package/dist/migrator-2MQHOFDQ.mjs.map +1 -0
- package/dist/migrator-THJCF6MZ.js +37 -0
- package/dist/migrator-THJCF6MZ.js.map +1 -0
- package/dist/redirects/index.d.mts +78 -0
- package/dist/redirects/index.d.ts +78 -0
- package/dist/redirects/index.js +26 -0
- package/dist/redirects/index.js.map +1 -0
- package/dist/redirects/index.mjs +5 -0
- package/dist/redirects/index.mjs.map +1 -0
- package/dist/reputation/index.d.mts +57 -0
- package/dist/reputation/index.d.ts +57 -0
- package/dist/reputation/index.js +21 -0
- package/dist/reputation/index.js.map +1 -0
- package/dist/reputation/index.mjs +4 -0
- package/dist/reputation/index.mjs.map +1 -0
- package/dist/robots/index.d.mts +38 -0
- package/dist/robots/index.d.ts +38 -0
- package/dist/robots/index.js +52 -0
- package/dist/robots/index.js.map +1 -0
- package/dist/robots/index.mjs +50 -0
- package/dist/robots/index.mjs.map +1 -0
- package/dist/routing-B5XS-6_W.d.mts +118 -0
- package/dist/routing-DZYzyDHw.d.ts +118 -0
- package/dist/scanner-GAF5PO5F.js +53 -0
- package/dist/scanner-GAF5PO5F.js.map +1 -0
- package/dist/scanner-LKJKW7IT.mjs +4 -0
- package/dist/scanner-LKJKW7IT.mjs.map +1 -0
- package/dist/securityHeaders-nwZ6nP4g.d.mts +24 -0
- package/dist/securityHeaders-nwZ6nP4g.d.ts +24 -0
- package/dist/seo/index.d.mts +600 -0
- package/dist/seo/index.d.ts +600 -0
- package/dist/seo/index.js +883 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/seo/index.mjs +773 -0
- package/dist/seo/index.mjs.map +1 -0
- package/dist/seo/register-sitemap-cli.js +151 -0
- package/dist/seo/register-sitemap-cli.js.map +1 -0
- package/dist/seo/register-sitemap-cli.mjs +144 -0
- package/dist/seo/register-sitemap-cli.mjs.map +1 -0
- package/dist/seo/server.d.mts +107 -0
- package/dist/seo/server.d.ts +107 -0
- package/dist/seo/server.js +207 -0
- package/dist/seo/server.js.map +1 -0
- package/dist/seo/server.mjs +186 -0
- package/dist/seo/server.mjs.map +1 -0
- package/dist/server-api-EWXKOQZA.mjs +4 -0
- package/dist/server-api-EWXKOQZA.mjs.map +1 -0
- package/dist/server-api-GJPNRYUP.js +81 -0
- package/dist/server-api-GJPNRYUP.js.map +1 -0
- package/dist/setup/client.d.mts +60 -0
- package/dist/setup/client.d.ts +60 -0
- package/dist/setup/client.js +31 -0
- package/dist/setup/client.js.map +1 -0
- package/dist/setup/client.mjs +6 -0
- package/dist/setup/client.mjs.map +1 -0
- package/dist/setup/index.d.mts +5 -0
- package/dist/setup/index.d.ts +5 -0
- package/dist/setup/index.js +35 -0
- package/dist/setup/index.js.map +1 -0
- package/dist/setup/index.mjs +6 -0
- package/dist/setup/index.mjs.map +1 -0
- package/dist/setup/server.d.mts +14 -0
- package/dist/setup/server.d.ts +14 -0
- package/dist/setup/server.js +13 -0
- package/dist/setup/server.js.map +1 -0
- package/dist/setup/server.mjs +4 -0
- package/dist/setup/server.mjs.map +1 -0
- package/dist/site-config/index.d.mts +24 -0
- package/dist/site-config/index.d.ts +24 -0
- package/dist/site-config/index.js +17 -0
- package/dist/site-config/index.js.map +1 -0
- package/dist/site-config/index.mjs +4 -0
- package/dist/site-config/index.mjs.map +1 -0
- package/dist/sitemap/index.d.mts +96 -0
- package/dist/sitemap/index.d.ts +96 -0
- package/dist/sitemap/index.js +288 -0
- package/dist/sitemap/index.js.map +1 -0
- package/dist/sitemap/index.mjs +285 -0
- package/dist/sitemap/index.mjs.map +1 -0
- package/dist/socket-loader-J26QHHOB.js +16 -0
- package/dist/socket-loader-J26QHHOB.js.map +1 -0
- package/dist/socket-loader-R7S2YJ2J.mjs +14 -0
- package/dist/socket-loader-R7S2YJ2J.mjs.map +1 -0
- package/dist/types-0dmq3k20.d.mts +168 -0
- package/dist/types-0dmq3k20.d.ts +168 -0
- package/dist/types-Blb2QNkV.d.mts +263 -0
- package/dist/types-Blb2QNkV.d.ts +263 -0
- package/dist/types-BnCwwUX3.d.mts +250 -0
- package/dist/types-BnCwwUX3.d.ts +250 -0
- package/dist/types-CGlnp43R.d.mts +312 -0
- package/dist/types-CGlnp43R.d.ts +312 -0
- package/dist/types-D08004rU.d.mts +179 -0
- package/dist/types-D08004rU.d.ts +179 -0
- package/dist/types-DNSYU7qI.d.mts +127 -0
- package/dist/types-DNSYU7qI.d.ts +127 -0
- package/dist/types-KZP_VWZp.d.mts +266 -0
- package/dist/types-KZP_VWZp.d.ts +266 -0
- package/dist/useEventModal-BVTx69XE.d.mts +274 -0
- package/dist/useEventModal-Dx1dItTJ.d.ts +274 -0
- package/dist/web-vitals-444RLW3B.js +252 -0
- package/dist/web-vitals-444RLW3B.js.map +1 -0
- package/dist/web-vitals-KPICZIEF.mjs +241 -0
- package/dist/web-vitals-KPICZIEF.mjs.map +1 -0
- package/package.json +192 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/redirects/index.ts"],"names":[],"mappings":";;;AA0CA,IAAM,kBAAA,GAAqB,8BAAA;AAG3B,IAAM,UAAA,uBAAiB,GAAA,EAAuD;AAE9E,SAAS,mBAAA,CAAoB,QAA4B,MAAA,EAAoC;AAC3F,EAAA,IAAI,MAAA,EAAQ,OAAO,CAAA,IAAA,EAAO,MAAM,CAAA,CAAA;AAChC,EAAA,OAAO,CAAA,OAAA,EAAU,UAAU,EAAE,CAAA,CAAA;AAC/B;AAOA,eAAsB,mBAAmB,MAAA,EAAiD;AACxF,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,EAAA,MAAM,YAAA,GAAe,OAAO,YAAA,IAAgB,GAAA;AAC5C,EAAA,MAAM,OAAA,GAAU,OAAO,YAAA,IAAgB,kBAAA;AAEvC,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,KAAW,OAAO,OAAA,KAAY,WAAA,IAAe,OAAA,CAAQ,GAAA,GAAO,OAAA,CAAQ,GAAA,CAAI,2BAAA,IAA+B,OAAA,CAAQ,IAAI,eAAA,GAAmB,MAAA,CAAA;AAC5J,EAAA,MAAM,MAAA,GAAS,OAAO,MAAA,KAAW,QAAA,GAAW,MAAA,GAAS,MAAA;AACrD,EAAA,IAAI,SAAS,MAAA,CAAO,MAAA;AAEpB,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,MAAA,EAAQ;AACtB,IAAA,MAAM,aAAa,MAAM,aAAA,CAAc,EAAE,MAAA,EAAQ,SAAS,CAAA;AAC1D,IAAA,MAAA,GAAS,UAAA,EAAY,MAAA;AAAA,EACvB;AAEA,EAAA,MAAM,QAAA,GAAW,mBAAA,CAAoB,MAAA,EAAQ,MAAM,CAAA;AACnD,EAAA,MAAM,MAAA,GAAS,UAAA,CAAW,GAAA,CAAI,QAAQ,CAAA;AACtC,EAAA,IAAI,MAAA,IAAU,GAAA,GAAM,MAAA,CAAO,MAAA,EAAQ;AACjC,IAAA,OAAO,MAAA,CAAO,KAAA;AAAA,EAChB;AAEA,EAAA,IAAI;AACF,IAAA,IAAI,GAAA;AACJ,IAAA,MAAM,OAAA,GAAkC,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAE7E,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,GAAA,GAAM,GAAG,OAAO,CAAA,yBAAA,CAAA;AAChB,MAAA,OAAA,CAAQ,WAAW,CAAA,GAAI,MAAA;AAAA,IACzB,WAAW,MAAA,EAAQ;AACjB,MAAA,GAAA,GAAM,CAAA,EAAG,OAAO,CAAA,iCAAA,EAAoC,kBAAA,CAAmB,MAAM,CAAC,CAAA,CAAA;AAAA,IAChF,CAAA,MAAO;AACL,MAAA,OAAO,EAAC;AAAA,IACV;AAEA,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,MAC3B,OAAA;AAAA,MACA,IAAA,EAAM,EAAE,UAAA,EAAY,YAAA;AAAa,KAClC,CAAA;AAED,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,sCAAA,EAAyC,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AACnE,MAAA,OAAO,MAAA,EAAQ,SAAS,EAAC;AAAA,IAC3B;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,IAAA,MAAM,KAAA,GAAA,CAAS,KAAK,SAAA,IAAa,IAAI,MAAA,CAAO,CAAC,CAAA,KAAoB,CAAA,CAAE,UAAU,CAAA;AAC7E,IAAA,UAAA,CAAW,GAAA,CAAI,UAAU,EAAE,KAAA,EAAO,QAAQ,GAAA,GAAM,YAAA,GAAe,KAAM,CAAA;AACrE,IAAA,OAAO,KAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA,CAAM,wCAAwC,KAAK,CAAA;AAC3D,IAAA,OAAO,MAAA,EAAQ,SAAS,EAAC;AAAA,EAC3B;AACF;AAMA,eAAsB,sBAAA,CACpB,SACA,MAAA,EACmC;AACnC,EAAA,MAAM,QAAA,GAAW,QAAQ,OAAA,CAAQ,QAAA;AAGjC,EAAA,IACE,QAAA,CAAS,UAAA,CAAW,QAAQ,CAAA,IAC5B,QAAA,CAAS,UAAA,CAAW,MAAM,CAAA,IAC1B,QAAA,CAAS,QAAA,CAAS,GAAG,CAAA,EACrB;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,KAAA,GAAQ,MAAM,kBAAA,CAAmB,MAAM,CAAA;AAG7C,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,CAAK,CAAA,CAAA,KAAK;AAE5B,IAAA,IAAI,CAAA,CAAE,SAAA,KAAc,QAAA,EAAU,OAAO,IAAA;AAGrC,IAAA,MAAM,cAAA,GAAiB,CAAA,CAAE,SAAA,CAAU,QAAA,CAAS,GAAG,CAAA,GAC3C,CAAA,CAAE,SAAA,CAAU,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GACvB,CAAA,CAAE,SAAA;AACN,IAAA,MAAM,cAAA,GAAiB,SAAS,QAAA,CAAS,GAAG,IACxC,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GACpB,QAAA;AAEJ,IAAA,OAAO,cAAA,KAAmB,cAAA;AAAA,EAC5B,CAAC,CAAA;AAED,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO,MAAA;AAAA,EACT;AAGA,EAAA,MAAM,cAAc,IAAI,GAAA,CAAI,KAAA,CAAM,OAAA,EAAS,QAAQ,GAAG,CAAA;AAGtD,EAAA,OAAA,CAAQ,OAAA,CAAQ,YAAA,CAAa,OAAA,CAAQ,CAAC,OAAO,GAAA,KAAQ;AACnD,IAAA,WAAA,CAAY,YAAA,CAAa,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAAA,EACzC,CAAC,CAAA;AAGD,EAAA,gBAAA,CAAiB,QAAQ,KAAA,CAAM,SAAA,EAAW,OAAO,MAAM,CAAA,CAAE,MAAM,MAAM;AAAA,EAAC,CAAC,CAAA;AAGvE,EAAA,MAAM,UAAA,GAAa,QAAA,CAAS,KAAA,CAAM,aAAa,CAAA;AAC/C,EAAA,OAAO,YAAA,CAAa,QAAA,CAAS,WAAA,EAAa,UAAU,CAAA;AACtD;AAKA,eAAe,gBAAA,CAAiB,MAAA,EAAwB,QAAA,EAAkB,YAAA,EAAsC;AAC9G,EAAA,IAAI;AACF,IAAA,MAAM,OAAA,GAAU,OAAO,YAAA,IAAgB,kBAAA;AACvC,IAAA,MAAM,MAAA,GAAS,YAAA,IAAgB,MAAA,CAAO,MAAA,IAAA,CAAW,MAAM,aAAA,CAAc,EAAE,MAAA,EAAQ,MAAA,CAAO,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,CAAA,GAAI,MAAA;AACnH,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,MAAM,KAAA,CAAM,CAAA,EAAG,OAAO,CAAA,6BAAA,CAAA,EAAiC;AAAA,MACrD,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,MAAM,IAAA,CAAK,SAAA,CAAU,EAAE,MAAA,EAAQ,SAAA,EAAW,UAAU;AAAA,KACrD,CAAA;AAAA,EACH,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AAmBA,eAAsB,sBAAsB,MAAA,EAIxC;AACF,EAAA,MAAM,KAAA,GAAQ,MAAM,kBAAA,CAAmB,EAAE,GAAG,MAAA,EAAQ,YAAA,EAAc,GAAG,CAAA;AAErE,EAAA,OAAO,KAAA,CAAM,IAAI,CAAA,CAAA,MAAM;AAAA,IACrB,QAAQ,CAAA,CAAE,SAAA;AAAA,IACV,aAAa,CAAA,CAAE,OAAA;AAAA,IACf,SAAA,EAAW,CAAA,CAAE,aAAA,KAAkB,KAAA,IAAS,EAAE,aAAA,KAAkB;AAAA,GAC9D,CAAE,CAAA;AACJ;AAKO,SAAS,kBAAA,GAA2B;AACzC,EAAA,UAAA,CAAW,KAAA,EAAM;AACnB","file":"chunk-QXV4667R.mjs","sourcesContent":["/**\n * Managed Redirects - Next.js Middleware Helper\n * \n * Fetches redirect rules from Portal and applies them.\n * Supports API key only (domain resolved from Portal) or domain-based lookup.\n * \n * Usage in middleware.ts (API key only):\n * \n * import { handleManagedRedirects } from '@sonordev/site-kit/redirects'\n * \n * export async function middleware(request: NextRequest) {\n * const redirect = await handleManagedRedirects(request, {\n * apiKey: process.env.NEXT_PUBLIC_UPTRADE_API_KEY,\n * portalApiUrl: process.env.NEXT_PUBLIC_UPTRADE_API_URL,\n * })\n * if (redirect) return redirect\n * return NextResponse.next()\n * }\n * \n * Or with domain (legacy):\n * const redirect = await handleManagedRedirects(request, { domain: 'example.com' })\n */\n\nimport { NextRequest, NextResponse } from 'next/server'\nimport { getSiteConfig } from '../site-config'\n\nexport interface RedirectRule {\n from_path: string\n to_path: string\n redirect_type: '301' | '302' | '307' | '308'\n is_enabled: boolean\n}\n\nexport interface RedirectConfig {\n /** Domain to fetch redirects for (optional when apiKey is set) */\n domain?: string\n /** Project API key; when set, redirects are fetched by key (no domain needed) */\n apiKey?: string\n portalApiUrl?: string\n cacheSeconds?: number\n}\n\nconst DEFAULT_PORTAL_URL = 'https://api.uptrademedia.com'\n\n// Cache for redirect rules keyed by apiKey or 'domain:'+domain\nconst rulesCache = new Map<string, { rules: RedirectRule[]; expiry: number }>()\n\nfunction getRedirectCacheKey(apiKey: string | undefined, domain: string | undefined): string {\n if (apiKey) return `key:${apiKey}`\n return `domain:${domain || ''}`\n}\n\n/**\n * Fetch redirect rules from Portal API.\n * When apiKey is set (or from env), uses key-based endpoint (no domain required).\n * Otherwise uses domain query (or domain from getSiteConfig when only env API key is set).\n */\nexport async function fetchRedirectRules(config: RedirectConfig): Promise<RedirectRule[]> {\n const now = Date.now()\n const cacheSeconds = config.cacheSeconds ?? 300\n const baseUrl = config.portalApiUrl || DEFAULT_PORTAL_URL\n\n const rawKey = config.apiKey ?? (typeof process !== 'undefined' && process.env ? (process.env.NEXT_PUBLIC_UPTRADE_API_KEY || process.env.UPTRADE_API_KEY) : undefined)\n const apiKey = typeof rawKey === 'string' ? rawKey : undefined\n let domain = config.domain\n\n if (!apiKey && !domain) {\n const siteConfig = await getSiteConfig({ apiUrl: baseUrl })\n domain = siteConfig?.domain\n }\n\n const cacheKey = getRedirectCacheKey(apiKey, domain)\n const cached = rulesCache.get(cacheKey)\n if (cached && now < cached.expiry) {\n return cached.rules\n }\n\n try {\n let url: string\n const headers: Record<string, string> = { 'Content-Type': 'application/json' }\n\n if (apiKey) {\n url = `${baseUrl}/api/public/seo/redirects`\n headers['x-api-key'] = apiKey\n } else if (domain) {\n url = `${baseUrl}/api/public/seo/redirects?domain=${encodeURIComponent(domain)}`\n } else {\n return []\n }\n\n const res = await fetch(url, {\n headers,\n next: { revalidate: cacheSeconds },\n })\n\n if (!res.ok) {\n console.error(`[site-kit] Failed to fetch redirects: ${res.status}`)\n return cached?.rules ?? []\n }\n\n const data = await res.json()\n const rules = (data.redirects || []).filter((r: RedirectRule) => r.is_enabled)\n rulesCache.set(cacheKey, { rules, expiry: now + cacheSeconds * 1000 })\n return rules\n } catch (error) {\n console.error('[site-kit] Error fetching redirects:', error)\n return cached?.rules ?? []\n }\n}\n\n/**\n * Handle managed redirects in middleware\n * Returns a NextResponse.redirect if a match is found, otherwise undefined\n */\nexport async function handleManagedRedirects(\n request: NextRequest,\n config: RedirectConfig,\n): Promise<NextResponse | undefined> {\n const pathname = request.nextUrl.pathname\n \n // Skip for static assets and API routes\n if (\n pathname.startsWith('/_next') ||\n pathname.startsWith('/api') ||\n pathname.includes('.') // Has file extension\n ) {\n return undefined\n }\n\n const rules = await fetchRedirectRules(config)\n \n // Find matching redirect\n const match = rules.find(r => {\n // Exact match\n if (r.from_path === pathname) return true\n \n // Match with trailing slash variance\n const normalizedFrom = r.from_path.endsWith('/') \n ? r.from_path.slice(0, -1) \n : r.from_path\n const normalizedPath = pathname.endsWith('/') \n ? pathname.slice(0, -1) \n : pathname\n \n return normalizedFrom === normalizedPath\n })\n\n if (!match) {\n return undefined\n }\n\n // Build redirect URL\n const redirectUrl = new URL(match.to_path, request.url)\n \n // Preserve query params\n request.nextUrl.searchParams.forEach((value, key) => {\n redirectUrl.searchParams.set(key, value)\n })\n\n // Track hit (fire and forget); domain may be from config or resolved by apiKey\n trackRedirectHit(config, match.from_path, config.domain).catch(() => {})\n\n // Return redirect response\n const statusCode = parseInt(match.redirect_type) as 301 | 302 | 307 | 308\n return NextResponse.redirect(redirectUrl, statusCode)\n}\n\n/**\n * Track redirect hit for analytics (needs domain; resolved from config or getSiteConfig when using apiKey)\n */\nasync function trackRedirectHit(config: RedirectConfig, fromPath: string, domainForHit?: string): Promise<void> {\n try {\n const baseUrl = config.portalApiUrl || DEFAULT_PORTAL_URL\n const domain = domainForHit ?? config.domain ?? (await getSiteConfig({ apiKey: config.apiKey, apiUrl: baseUrl }))?.domain\n if (!domain) return\n await fetch(`${baseUrl}/api/public/seo/redirects/hit`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ domain, from_path: fromPath }),\n })\n } catch {\n // Ignore tracking errors\n }\n}\n\n/**\n * Generate Next.js redirects config from Portal\n * Use this in next.config.js for build-time redirects\n * \n * Usage in next.config.js:\n * \n * const { generateNextRedirects } = require('@sonordev/site-kit/redirects')\n * \n * module.exports = {\n * async redirects() {\n * return generateNextRedirects({\n * domain: 'example.com',\n * portalApiUrl: process.env.PORTAL_API_URL,\n * })\n * }\n * }\n */\nexport async function generateNextRedirects(config: RedirectConfig): Promise<Array<{\n source: string\n destination: string\n permanent: boolean\n}>> {\n const rules = await fetchRedirectRules({ ...config, cacheSeconds: 0 })\n \n return rules.map(r => ({\n source: r.from_path,\n destination: r.to_path,\n permanent: r.redirect_type === '301' || r.redirect_type === '308',\n }))\n}\n\n/**\n * Clear redirect cache (useful for development)\n */\nexport function clearRedirectCache(): void {\n rulesCache.clear()\n}\n"]}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
// src/forms/formsApi.ts
|
|
2
|
+
var config = null;
|
|
3
|
+
function configureFormsApi(options) {
|
|
4
|
+
config = options;
|
|
5
|
+
}
|
|
6
|
+
function getConfig() {
|
|
7
|
+
if (config) return config;
|
|
8
|
+
if (typeof window !== "undefined") {
|
|
9
|
+
const apiUrl = window.__SITE_KIT_API_URL__;
|
|
10
|
+
const apiKey = window.__SITE_KIT_API_KEY__;
|
|
11
|
+
if (apiUrl && apiKey) {
|
|
12
|
+
return { baseUrl: apiUrl, apiKey };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
throw new Error(
|
|
16
|
+
"Forms API not configured. Either wrap your app in SiteKitProvider or call configureFormsApi() first."
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
async function apiRequest(method, path, body) {
|
|
20
|
+
const { baseUrl, apiKey, getAuthToken } = getConfig();
|
|
21
|
+
const headers = {
|
|
22
|
+
"Content-Type": "application/json"
|
|
23
|
+
};
|
|
24
|
+
if (apiKey) {
|
|
25
|
+
headers["x-api-key"] = apiKey;
|
|
26
|
+
} else if (getAuthToken) {
|
|
27
|
+
const token = await getAuthToken();
|
|
28
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
29
|
+
}
|
|
30
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
31
|
+
method,
|
|
32
|
+
headers,
|
|
33
|
+
body: body ? JSON.stringify(body) : void 0
|
|
34
|
+
});
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
const error = await response.json().catch(() => ({ message: response.statusText }));
|
|
37
|
+
throw new Error(error.message || `API error: ${response.status}`);
|
|
38
|
+
}
|
|
39
|
+
if (response.status === 204) {
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
return response.json();
|
|
43
|
+
}
|
|
44
|
+
var formsApi = {
|
|
45
|
+
/**
|
|
46
|
+
* List all forms for a project
|
|
47
|
+
*/
|
|
48
|
+
async list(options = {}) {
|
|
49
|
+
const params = new URLSearchParams();
|
|
50
|
+
if (options.projectId) params.set("projectId", options.projectId);
|
|
51
|
+
if (options.formType) params.set("formType", options.formType);
|
|
52
|
+
if (options.isActive !== void 0) params.set("isActive", String(options.isActive));
|
|
53
|
+
if (options.search) params.set("search", options.search);
|
|
54
|
+
const query = params.toString();
|
|
55
|
+
const result = await apiRequest("GET", `/forms${query ? `?${query}` : ""}`);
|
|
56
|
+
return result.data;
|
|
57
|
+
},
|
|
58
|
+
/**
|
|
59
|
+
* Get a single form by ID or slug
|
|
60
|
+
*/
|
|
61
|
+
async get(idOrSlug) {
|
|
62
|
+
return apiRequest("GET", `/forms/${idOrSlug}`);
|
|
63
|
+
},
|
|
64
|
+
/**
|
|
65
|
+
* Create a new form
|
|
66
|
+
*/
|
|
67
|
+
async create(input) {
|
|
68
|
+
return apiRequest("POST", "/forms", input);
|
|
69
|
+
},
|
|
70
|
+
/**
|
|
71
|
+
* Update an existing form
|
|
72
|
+
*/
|
|
73
|
+
async update(id, input) {
|
|
74
|
+
return apiRequest("PUT", `/forms/${id}`, input);
|
|
75
|
+
},
|
|
76
|
+
/**
|
|
77
|
+
* Delete a form
|
|
78
|
+
*/
|
|
79
|
+
async delete(id) {
|
|
80
|
+
return apiRequest("DELETE", `/forms/${id}`);
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* Add a field to an existing form
|
|
84
|
+
*/
|
|
85
|
+
async addField(formId, field2) {
|
|
86
|
+
const form = await formsApi.get(formId);
|
|
87
|
+
const maxSortOrder = Math.max(0, ...(form.fields || []).map((f) => f.sortOrder || 0));
|
|
88
|
+
return formsApi.update(formId, {
|
|
89
|
+
fields: [
|
|
90
|
+
...form.fields || [],
|
|
91
|
+
{ ...field2, sortOrder: field2.sortOrder ?? maxSortOrder + 1 }
|
|
92
|
+
]
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
/**
|
|
96
|
+
* Update a field in an existing form
|
|
97
|
+
*/
|
|
98
|
+
async updateField(formId, fieldSlug, updates) {
|
|
99
|
+
const form = await formsApi.get(formId);
|
|
100
|
+
return formsApi.update(formId, {
|
|
101
|
+
fields: (form.fields || []).map(
|
|
102
|
+
(f) => f.slug === fieldSlug ? { ...f, ...updates } : f
|
|
103
|
+
)
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
/**
|
|
107
|
+
* Remove a field from a form
|
|
108
|
+
*/
|
|
109
|
+
async removeField(formId, fieldSlug) {
|
|
110
|
+
const form = await formsApi.get(formId);
|
|
111
|
+
return formsApi.update(formId, {
|
|
112
|
+
fields: (form.fields || []).filter((f) => f.slug !== fieldSlug)
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
/**
|
|
116
|
+
* Reorder fields in a form
|
|
117
|
+
*/
|
|
118
|
+
async reorderFields(formId, fieldSlugs) {
|
|
119
|
+
const form = await formsApi.get(formId);
|
|
120
|
+
const fieldsMap = new Map((form.fields || []).map((f) => [f.slug, f]));
|
|
121
|
+
const orderedFields = fieldSlugs.map((slug, index) => {
|
|
122
|
+
const field2 = fieldsMap.get(slug);
|
|
123
|
+
if (!field2) throw new Error(`Field not found: ${slug}`);
|
|
124
|
+
return { ...field2, sortOrder: index };
|
|
125
|
+
});
|
|
126
|
+
return formsApi.update(formId, { fields: orderedFields });
|
|
127
|
+
},
|
|
128
|
+
/**
|
|
129
|
+
* Clone a form
|
|
130
|
+
*/
|
|
131
|
+
async clone(formId, newSlug, newName) {
|
|
132
|
+
const form = await formsApi.get(formId);
|
|
133
|
+
return formsApi.create({
|
|
134
|
+
projectId: form.projectId,
|
|
135
|
+
slug: newSlug,
|
|
136
|
+
name: newName || `${form.name} (Copy)`,
|
|
137
|
+
description: form.description,
|
|
138
|
+
formType: form.formType,
|
|
139
|
+
successMessage: form.successMessage,
|
|
140
|
+
redirectUrl: form.redirectUrl,
|
|
141
|
+
notificationEmails: form.notificationEmails,
|
|
142
|
+
submitButtonText: form.submitButtonText,
|
|
143
|
+
layout: form.layout,
|
|
144
|
+
showProgress: form.showProgress,
|
|
145
|
+
enableSaveDraft: form.enableSaveDraft,
|
|
146
|
+
isActive: false,
|
|
147
|
+
// Clone as inactive
|
|
148
|
+
fields: (form.fields || []).map(({ id, ...field2 }) => field2),
|
|
149
|
+
// Remove IDs
|
|
150
|
+
steps: (form.steps || []).map(({ id, ...step }) => step)
|
|
151
|
+
// Remove IDs
|
|
152
|
+
});
|
|
153
|
+
},
|
|
154
|
+
/**
|
|
155
|
+
* Activate or deactivate a form
|
|
156
|
+
*/
|
|
157
|
+
async setActive(formId, isActive) {
|
|
158
|
+
return formsApi.update(formId, { isActive });
|
|
159
|
+
},
|
|
160
|
+
/**
|
|
161
|
+
* Sync a form definition to the backend
|
|
162
|
+
* Creates the form if it doesn't exist, updates if it does
|
|
163
|
+
* Perfect for defining forms in code during development
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```tsx
|
|
167
|
+
* // In your app initialization or form component
|
|
168
|
+
* await formsApi.sync({
|
|
169
|
+
* slug: 'contact',
|
|
170
|
+
* name: 'Contact Form',
|
|
171
|
+
* formType: 'prospect',
|
|
172
|
+
* fields: [
|
|
173
|
+
* field.text('name', 'Your Name', { isRequired: true }),
|
|
174
|
+
* field.email('email', 'Email Address'),
|
|
175
|
+
* field.phone('phone', 'Phone Number'),
|
|
176
|
+
* field.textarea('message', 'Message'),
|
|
177
|
+
* ]
|
|
178
|
+
* })
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
async sync(input) {
|
|
182
|
+
return apiRequest(
|
|
183
|
+
"POST",
|
|
184
|
+
"/api/public/forms/sync",
|
|
185
|
+
input
|
|
186
|
+
);
|
|
187
|
+
},
|
|
188
|
+
/**
|
|
189
|
+
* Sync multiple forms at once
|
|
190
|
+
* Useful for initializing all forms in your app
|
|
191
|
+
*/
|
|
192
|
+
async syncAll(forms) {
|
|
193
|
+
return Promise.all(forms.map((form) => formsApi.sync(form)));
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
var field = {
|
|
197
|
+
text: (slug, label, options) => ({
|
|
198
|
+
slug,
|
|
199
|
+
label,
|
|
200
|
+
fieldType: "text",
|
|
201
|
+
...options
|
|
202
|
+
}),
|
|
203
|
+
email: (slug, label, options) => ({
|
|
204
|
+
slug,
|
|
205
|
+
label,
|
|
206
|
+
fieldType: "email",
|
|
207
|
+
isRequired: true,
|
|
208
|
+
...options
|
|
209
|
+
}),
|
|
210
|
+
phone: (slug, label, options) => ({
|
|
211
|
+
slug,
|
|
212
|
+
label,
|
|
213
|
+
fieldType: "phone",
|
|
214
|
+
...options
|
|
215
|
+
}),
|
|
216
|
+
textarea: (slug, label, options) => ({
|
|
217
|
+
slug,
|
|
218
|
+
label,
|
|
219
|
+
fieldType: "textarea",
|
|
220
|
+
...options
|
|
221
|
+
}),
|
|
222
|
+
select: (slug, label, choices, options) => ({
|
|
223
|
+
slug,
|
|
224
|
+
label,
|
|
225
|
+
fieldType: "select",
|
|
226
|
+
options: choices,
|
|
227
|
+
...options
|
|
228
|
+
}),
|
|
229
|
+
radio: (slug, label, choices, options) => ({
|
|
230
|
+
slug,
|
|
231
|
+
label,
|
|
232
|
+
fieldType: "radio",
|
|
233
|
+
options: choices,
|
|
234
|
+
...options
|
|
235
|
+
}),
|
|
236
|
+
checkbox: (slug, label, options) => ({
|
|
237
|
+
slug,
|
|
238
|
+
label,
|
|
239
|
+
fieldType: "checkbox",
|
|
240
|
+
...options
|
|
241
|
+
}),
|
|
242
|
+
date: (slug, label, options) => ({
|
|
243
|
+
slug,
|
|
244
|
+
label,
|
|
245
|
+
fieldType: "date",
|
|
246
|
+
...options
|
|
247
|
+
}),
|
|
248
|
+
number: (slug, label, options) => ({
|
|
249
|
+
slug,
|
|
250
|
+
label,
|
|
251
|
+
fieldType: "number",
|
|
252
|
+
...options
|
|
253
|
+
}),
|
|
254
|
+
rating: (slug, label, options) => ({
|
|
255
|
+
slug,
|
|
256
|
+
label,
|
|
257
|
+
fieldType: "rating",
|
|
258
|
+
...options
|
|
259
|
+
}),
|
|
260
|
+
file: (slug, label, options) => ({
|
|
261
|
+
slug,
|
|
262
|
+
label,
|
|
263
|
+
fieldType: "file",
|
|
264
|
+
...options
|
|
265
|
+
}),
|
|
266
|
+
hidden: (slug, defaultValue) => ({
|
|
267
|
+
slug,
|
|
268
|
+
label: "",
|
|
269
|
+
fieldType: "hidden",
|
|
270
|
+
defaultValue
|
|
271
|
+
}),
|
|
272
|
+
heading: (slug, label) => ({
|
|
273
|
+
slug,
|
|
274
|
+
label,
|
|
275
|
+
fieldType: "heading"
|
|
276
|
+
}),
|
|
277
|
+
paragraph: (slug, label) => ({
|
|
278
|
+
slug,
|
|
279
|
+
label,
|
|
280
|
+
fieldType: "paragraph"
|
|
281
|
+
})
|
|
282
|
+
};
|
|
283
|
+
function defineForm(definition) {
|
|
284
|
+
const fieldsWithOrder = definition.fields.map((f, index) => ({
|
|
285
|
+
...f,
|
|
286
|
+
sortOrder: f.sortOrder ?? index
|
|
287
|
+
}));
|
|
288
|
+
return {
|
|
289
|
+
...definition,
|
|
290
|
+
fields: fieldsWithOrder,
|
|
291
|
+
formType: definition.formType || "contact",
|
|
292
|
+
successMessage: definition.successMessage || "Thank you for your submission!",
|
|
293
|
+
submitButtonText: definition.submitButtonText || "Submit",
|
|
294
|
+
layout: definition.layout || "stacked",
|
|
295
|
+
isActive: definition.isActive ?? true
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
async function initializeForms(forms) {
|
|
299
|
+
if (typeof window === "undefined") {
|
|
300
|
+
console.log(`[Site-Kit] ${forms.length} form(s) ready to sync: ${forms.map((f) => f.slug).join(", ")}`);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
const results = await formsApi.syncAll(forms);
|
|
305
|
+
const created = results.filter((r) => r.created).length;
|
|
306
|
+
const updated = results.filter((r) => r.updated).length;
|
|
307
|
+
console.log(`[Site-Kit] Forms synced: ${created} created, ${updated} updated`);
|
|
308
|
+
} catch (error) {
|
|
309
|
+
console.error("[Site-Kit] Failed to sync forms:", error);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export { configureFormsApi, defineForm, field, formsApi, initializeForms };
|
|
314
|
+
//# sourceMappingURL=chunk-S7FRYNSU.mjs.map
|
|
315
|
+
//# sourceMappingURL=chunk-S7FRYNSU.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/forms/formsApi.ts"],"names":["field"],"mappings":"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","file":"chunk-S7FRYNSU.mjs","sourcesContent":["/**\n * @sonordev/site-kit/forms - Forms API Client\n * \n * Full API client for form management. Allows developers to create, update,\n * and delete forms programmatically. Changes sync to Portal automatically.\n * \n * @example\n * ```tsx\n * import { formsApi } from '@sonordev/site-kit/forms'\n * \n * // Create a new form\n * const form = await formsApi.create({\n * projectId: 'xxx',\n * slug: 'contact-us',\n * name: 'Contact Form',\n * formType: 'prospect',\n * fields: [\n * { slug: 'name', label: 'Name', fieldType: 'text', isRequired: true },\n * { slug: 'email', label: 'Email', fieldType: 'email', isRequired: true },\n * { slug: 'message', label: 'Message', fieldType: 'textarea' },\n * ]\n * })\n * \n * // Update fields\n * await formsApi.update(form.id, {\n * fields: [...form.fields, { slug: 'phone', label: 'Phone', fieldType: 'phone' }]\n * })\n * ```\n */\n\n// ============================================\n// Types\n// ============================================\n\nexport type FormType = \n | 'prospect' // → CRM leads\n | 'contact' // → Contact form\n | 'support' // → Support tickets\n | 'feedback' // → Feedback entries\n | 'newsletter' // → Email subscribers\n | 'custom' // → Custom webhook\n\nexport type FieldType =\n | 'text'\n | 'email'\n | 'phone'\n | 'number'\n | 'textarea'\n | 'select'\n | 'multi_select'\n | 'radio'\n | 'checkbox'\n | 'date'\n | 'time'\n | 'datetime'\n | 'url'\n | 'file'\n | 'rating'\n | 'slider'\n | 'hidden'\n | 'heading'\n | 'paragraph'\n\nexport type FieldWidth = 'full' | 'half' | 'third' | 'quarter'\n\nexport interface FieldOption {\n value: string\n label: string\n disabled?: boolean\n}\n\nexport interface FieldValidation {\n min?: number\n max?: number\n minLength?: number\n maxLength?: number\n pattern?: string\n patternMessage?: string\n}\n\nexport interface FieldConditional {\n field: string\n operator: 'equals' | 'not_equals' | 'contains' | 'not_contains' | 'is_empty' | 'not_empty' | 'greater_than' | 'less_than'\n value?: string | number | boolean\n}\n\nexport interface FormField {\n id?: string\n slug: string\n label: string\n fieldType: FieldType\n placeholder?: string\n helpText?: string\n defaultValue?: string\n isRequired?: boolean\n validation?: FieldValidation\n options?: FieldOption[]\n conditional?: FieldConditional\n width?: FieldWidth\n sortOrder?: number\n destinationField?: string\n stepId?: string\n}\n\nexport interface FormStep {\n id?: string\n stepNumber: number\n title?: string\n description?: string\n condition?: FieldConditional\n}\n\nexport interface Form {\n id: string\n projectId: string\n slug: string\n name: string\n description?: string\n formType: FormType\n successMessage: string\n redirectUrl?: string\n notificationEmails?: string[]\n submitButtonText: string\n layout: 'stacked' | 'inline' | 'grid'\n showProgress: boolean\n enableSaveDraft: boolean\n isActive: boolean\n createdAt: string\n updatedAt: string\n fields?: FormField[]\n steps?: FormStep[]\n}\n\nexport interface CreateFormInput {\n projectId: string\n slug: string\n name: string\n description?: string\n formType?: FormType\n successMessage?: string\n redirectUrl?: string\n notificationEmails?: string[]\n submitButtonText?: string\n layout?: 'stacked' | 'inline' | 'grid'\n showProgress?: boolean\n enableSaveDraft?: boolean\n isActive?: boolean\n fields?: FormField[]\n steps?: FormStep[]\n}\n\nexport interface UpdateFormInput {\n slug?: string\n name?: string\n description?: string\n formType?: FormType\n successMessage?: string\n redirectUrl?: string\n notificationEmails?: string[]\n submitButtonText?: string\n layout?: 'stacked' | 'inline' | 'grid'\n showProgress?: boolean\n enableSaveDraft?: boolean\n isActive?: boolean\n fields?: FormField[]\n steps?: FormStep[]\n}\n\nexport interface FormsListOptions {\n projectId?: string\n formType?: FormType\n isActive?: boolean\n search?: string\n}\n\n// ============================================\n// API Client Configuration\n// ============================================\n\ninterface FormsApiConfig {\n baseUrl: string\n apiKey?: string\n getAuthToken?: () => Promise<string> | string\n}\n\nlet config: FormsApiConfig | null = null\n\n/**\n * Configure the forms API client\n */\nexport function configureFormsApi(options: FormsApiConfig) {\n config = options\n}\n\n/**\n * Get API config, falling back to SiteKitProvider globals\n */\nfunction getConfig(): FormsApiConfig {\n if (config) return config\n \n // Try to get from window globals (set by SiteKitProvider)\n if (typeof window !== 'undefined') {\n const apiUrl = (window as any).__SITE_KIT_API_URL__\n const apiKey = (window as any).__SITE_KIT_API_KEY__\n \n if (apiUrl && apiKey) {\n return { baseUrl: apiUrl, apiKey }\n }\n }\n \n throw new Error(\n 'Forms API not configured. Either wrap your app in SiteKitProvider or call configureFormsApi() first.'\n )\n}\n\n// ============================================\n// HTTP Helpers\n// ============================================\n\nasync function apiRequest<T>(\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n body?: any\n): Promise<T> {\n const { baseUrl, apiKey, getAuthToken } = getConfig()\n \n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n }\n \n // Use API key for public endpoints, auth token for admin endpoints\n if (apiKey) {\n headers['x-api-key'] = apiKey\n } else if (getAuthToken) {\n const token = await getAuthToken()\n headers['Authorization'] = `Bearer ${token}`\n }\n \n const response = await fetch(`${baseUrl}${path}`, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n })\n \n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: response.statusText }))\n throw new Error(error.message || `API error: ${response.status}`)\n }\n \n if (response.status === 204) {\n return undefined as T\n }\n \n return response.json()\n}\n\n// ============================================\n// Forms API\n// ============================================\n\nexport const formsApi = {\n /**\n * List all forms for a project\n */\n async list(options: FormsListOptions = {}): Promise<Form[]> {\n const params = new URLSearchParams()\n if (options.projectId) params.set('projectId', options.projectId)\n if (options.formType) params.set('formType', options.formType)\n if (options.isActive !== undefined) params.set('isActive', String(options.isActive))\n if (options.search) params.set('search', options.search)\n \n const query = params.toString()\n const result = await apiRequest<{ data: Form[] }>('GET', `/forms${query ? `?${query}` : ''}`)\n return result.data\n },\n \n /**\n * Get a single form by ID or slug\n */\n async get(idOrSlug: string): Promise<Form> {\n return apiRequest<Form>('GET', `/forms/${idOrSlug}`)\n },\n \n /**\n * Create a new form\n */\n async create(input: CreateFormInput): Promise<Form> {\n return apiRequest<Form>('POST', '/forms', input)\n },\n \n /**\n * Update an existing form\n */\n async update(id: string, input: UpdateFormInput): Promise<Form> {\n return apiRequest<Form>('PUT', `/forms/${id}`, input)\n },\n \n /**\n * Delete a form\n */\n async delete(id: string): Promise<void> {\n return apiRequest<void>('DELETE', `/forms/${id}`)\n },\n \n /**\n * Add a field to an existing form\n */\n async addField(formId: string, field: FormField): Promise<Form> {\n const form = await formsApi.get(formId)\n const maxSortOrder = Math.max(0, ...(form.fields || []).map(f => f.sortOrder || 0))\n \n return formsApi.update(formId, {\n fields: [\n ...(form.fields || []),\n { ...field, sortOrder: field.sortOrder ?? maxSortOrder + 1 }\n ]\n })\n },\n \n /**\n * Update a field in an existing form\n */\n async updateField(formId: string, fieldSlug: string, updates: Partial<FormField>): Promise<Form> {\n const form = await formsApi.get(formId)\n \n return formsApi.update(formId, {\n fields: (form.fields || []).map(f => \n f.slug === fieldSlug ? { ...f, ...updates } : f\n )\n })\n },\n \n /**\n * Remove a field from a form\n */\n async removeField(formId: string, fieldSlug: string): Promise<Form> {\n const form = await formsApi.get(formId)\n \n return formsApi.update(formId, {\n fields: (form.fields || []).filter(f => f.slug !== fieldSlug)\n })\n },\n \n /**\n * Reorder fields in a form\n */\n async reorderFields(formId: string, fieldSlugs: string[]): Promise<Form> {\n const form = await formsApi.get(formId)\n const fieldsMap = new Map((form.fields || []).map(f => [f.slug, f]))\n \n const orderedFields = fieldSlugs.map((slug, index) => {\n const field = fieldsMap.get(slug)\n if (!field) throw new Error(`Field not found: ${slug}`)\n return { ...field, sortOrder: index }\n })\n \n return formsApi.update(formId, { fields: orderedFields })\n },\n \n /**\n * Clone a form\n */\n async clone(formId: string, newSlug: string, newName?: string): Promise<Form> {\n const form = await formsApi.get(formId)\n \n return formsApi.create({\n projectId: form.projectId,\n slug: newSlug,\n name: newName || `${form.name} (Copy)`,\n description: form.description,\n formType: form.formType,\n successMessage: form.successMessage,\n redirectUrl: form.redirectUrl,\n notificationEmails: form.notificationEmails,\n submitButtonText: form.submitButtonText,\n layout: form.layout,\n showProgress: form.showProgress,\n enableSaveDraft: form.enableSaveDraft,\n isActive: false, // Clone as inactive\n fields: (form.fields || []).map(({ id, ...field }) => field), // Remove IDs\n steps: (form.steps || []).map(({ id, ...step }) => step), // Remove IDs\n })\n },\n \n /**\n * Activate or deactivate a form\n */\n async setActive(formId: string, isActive: boolean): Promise<Form> {\n return formsApi.update(formId, { isActive })\n },\n\n /**\n * Sync a form definition to the backend\n * Creates the form if it doesn't exist, updates if it does\n * Perfect for defining forms in code during development\n * \n * @example\n * ```tsx\n * // In your app initialization or form component\n * await formsApi.sync({\n * slug: 'contact',\n * name: 'Contact Form',\n * formType: 'prospect',\n * fields: [\n * field.text('name', 'Your Name', { isRequired: true }),\n * field.email('email', 'Email Address'),\n * field.phone('phone', 'Phone Number'),\n * field.textarea('message', 'Message'),\n * ]\n * })\n * ```\n */\n async sync(input: Omit<CreateFormInput, 'projectId'>): Promise<Form & { synced: boolean; created: boolean; updated: boolean }> {\n return apiRequest<Form & { synced: boolean; created: boolean; updated: boolean }>(\n 'POST',\n '/api/public/forms/sync',\n input\n )\n },\n\n /**\n * Sync multiple forms at once\n * Useful for initializing all forms in your app\n */\n async syncAll(forms: Array<Omit<CreateFormInput, 'projectId'>>): Promise<Array<Form & { synced: boolean; created: boolean; updated: boolean }>> {\n return Promise.all(forms.map(form => formsApi.sync(form)))\n },\n}\n\n// ============================================\n// Field Builder Helpers\n// ============================================\n\n/**\n * Helper functions to build field definitions\n */\nexport const field = {\n text: (slug: string, label: string, options?: Partial<FormField>): FormField => ({\n slug,\n label,\n fieldType: 'text',\n ...options,\n }),\n \n email: (slug: string, label: string, options?: Partial<FormField>): FormField => ({\n slug,\n label,\n fieldType: 'email',\n isRequired: true,\n ...options,\n }),\n \n phone: (slug: string, label: string, options?: Partial<FormField>): FormField => ({\n slug,\n label,\n fieldType: 'phone',\n ...options,\n }),\n \n textarea: (slug: string, label: string, options?: Partial<FormField>): FormField => ({\n slug,\n label,\n fieldType: 'textarea',\n ...options,\n }),\n \n select: (slug: string, label: string, choices: Array<{ value: string; label: string }>, options?: Partial<FormField>): FormField => ({\n slug,\n label,\n fieldType: 'select',\n options: choices,\n ...options,\n }),\n \n radio: (slug: string, label: string, choices: Array<{ value: string; label: string }>, options?: Partial<FormField>): FormField => ({\n slug,\n label,\n fieldType: 'radio',\n options: choices,\n ...options,\n }),\n \n checkbox: (slug: string, label: string, options?: Partial<FormField>): FormField => ({\n slug,\n label,\n fieldType: 'checkbox',\n ...options,\n }),\n \n date: (slug: string, label: string, options?: Partial<FormField>): FormField => ({\n slug,\n label,\n fieldType: 'date',\n ...options,\n }),\n \n number: (slug: string, label: string, options?: Partial<FormField>): FormField => ({\n slug,\n label,\n fieldType: 'number',\n ...options,\n }),\n \n rating: (slug: string, label: string, options?: Partial<FormField>): FormField => ({\n slug,\n label,\n fieldType: 'rating',\n ...options,\n }),\n \n file: (slug: string, label: string, options?: Partial<FormField>): FormField => ({\n slug,\n label,\n fieldType: 'file',\n ...options,\n }),\n \n hidden: (slug: string, defaultValue: string): FormField => ({\n slug,\n label: '',\n fieldType: 'hidden',\n defaultValue,\n }),\n \n heading: (slug: string, label: string): FormField => ({\n slug,\n label,\n fieldType: 'heading',\n }),\n \n paragraph: (slug: string, label: string): FormField => ({\n slug,\n label,\n fieldType: 'paragraph',\n }),\n}\n\n// ============================================\n// Form Definition Helper\n// ============================================\n\nexport interface FormDefinition extends Omit<CreateFormInput, 'projectId'> {\n slug: string\n name: string\n fields: FormField[]\n}\n\n/**\n * Define a form declaratively\n * Returns a form definition that can be synced to the backend\n * \n * @example\n * ```tsx\n * // forms/contact.ts\n * import { defineForm, field } from '@sonordev/site-kit/forms'\n * \n * export const contactForm = defineForm({\n * slug: 'contact',\n * name: 'Contact Us',\n * formType: 'prospect',\n * successMessage: 'Thanks! We\\'ll be in touch soon.',\n * fields: [\n * field.text('name', 'Your Name', { isRequired: true }),\n * field.email('email', 'Email Address'),\n * field.phone('phone', 'Phone Number'),\n * field.select('service', 'Service Needed', [\n * { value: 'consultation', label: 'Free Consultation' },\n * { value: 'representation', label: 'Legal Representation' },\n * ]),\n * field.textarea('message', 'Tell us about your case'),\n * ]\n * })\n * \n * // Then in your app:\n * await formsApi.sync(contactForm)\n * ```\n */\nexport function defineForm(definition: FormDefinition): FormDefinition {\n // Ensure fields have sort order\n const fieldsWithOrder = definition.fields.map((f, index) => ({\n ...f,\n sortOrder: f.sortOrder ?? index,\n }))\n \n return {\n ...definition,\n fields: fieldsWithOrder,\n formType: definition.formType || 'contact',\n successMessage: definition.successMessage || 'Thank you for your submission!',\n submitButtonText: definition.submitButtonText || 'Submit',\n layout: definition.layout || 'stacked',\n isActive: definition.isActive ?? true,\n }\n}\n\n/**\n * Initialize multiple forms and sync them to the backend\n * Call this once during app initialization (e.g., in layout.tsx or _app.tsx)\n * \n * @example\n * ```tsx\n * // app/layout.tsx\n * import { initializeForms } from '@sonordev/site-kit/forms'\n * import { contactForm } from './forms/contact'\n * import { consultationForm } from './forms/consultation'\n * \n * // Initialize forms (runs once per build/server start in dev)\n * if (process.env.NODE_ENV === 'development') {\n * initializeForms([contactForm, consultationForm])\n * }\n * ```\n */\nexport async function initializeForms(forms: FormDefinition[]): Promise<void> {\n if (typeof window === 'undefined') {\n // Server-side: log that forms need to be synced\n console.log(`[Site-Kit] ${forms.length} form(s) ready to sync: ${forms.map(f => f.slug).join(', ')}`)\n return\n }\n \n try {\n const results = await formsApi.syncAll(forms)\n const created = results.filter(r => r.created).length\n const updated = results.filter(r => r.updated).length\n console.log(`[Site-Kit] Forms synced: ${created} created, ${updated} updated`)\n } catch (error) {\n console.error('[Site-Kit] Failed to sync forms:', error)\n }\n}\n"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
// src/images/ManagedFavicon.tsx
|
|
4
|
+
function toAbsoluteFaviconUrl(url, apiBase) {
|
|
5
|
+
if (!url) return void 0;
|
|
6
|
+
if (url.startsWith("http://") || url.startsWith("https://")) return url;
|
|
7
|
+
const base = apiBase.replace(/\/$/, "");
|
|
8
|
+
return url.startsWith("/") ? `${base}${url}` : `${base}/${url}`;
|
|
9
|
+
}
|
|
10
|
+
async function fetchFaviconData(apiUrl, apiKey) {
|
|
11
|
+
try {
|
|
12
|
+
const res = await fetch(`${apiUrl}/public/images/slot/favicon`, {
|
|
13
|
+
headers: {
|
|
14
|
+
"Content-Type": "application/json",
|
|
15
|
+
"x-api-key": apiKey
|
|
16
|
+
},
|
|
17
|
+
next: { revalidate: 3600 }
|
|
18
|
+
// Cache 1hr – favicon rarely changes, allows static generation
|
|
19
|
+
});
|
|
20
|
+
if (!res.ok) {
|
|
21
|
+
console.warn("[ManagedFavicon] Failed to fetch:", res.status);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const data = await res.json();
|
|
25
|
+
if (data.image && !data.is_placeholder) {
|
|
26
|
+
const raw = data.image.public_url || data.image.external_url;
|
|
27
|
+
const public_url = toAbsoluteFaviconUrl(raw, apiUrl);
|
|
28
|
+
return {
|
|
29
|
+
public_url: public_url || void 0,
|
|
30
|
+
mime_type: data.image.file?.mime_type,
|
|
31
|
+
is_placeholder: false
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
console.error("[ManagedFavicon] Error fetching favicon:", err);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function getManagedFaviconUrl(apiKey, apiUrl = process.env.NEXT_PUBLIC_UPTRADE_API_URL || "https://api.uptrademedia.com") {
|
|
41
|
+
const key = apiKey ?? process.env.NEXT_PUBLIC_UPTRADE_API_KEY ?? process.env.UPTRADE_API_KEY;
|
|
42
|
+
if (!key) return null;
|
|
43
|
+
const data = await fetchFaviconData(apiUrl, key);
|
|
44
|
+
return data?.public_url ?? null;
|
|
45
|
+
}
|
|
46
|
+
async function ManagedFavicon({
|
|
47
|
+
apiKey = process.env.NEXT_PUBLIC_UPTRADE_API_KEY,
|
|
48
|
+
apiUrl = process.env.NEXT_PUBLIC_UPTRADE_API_URL || "https://api.uptrademedia.com",
|
|
49
|
+
fallback = "/favicon.ico",
|
|
50
|
+
themeColor = "#4bbf39"
|
|
51
|
+
}) {
|
|
52
|
+
const faviconData = apiKey && apiUrl ? await fetchFaviconData(apiUrl, apiKey) : null;
|
|
53
|
+
const faviconUrl = faviconData?.public_url || fallback;
|
|
54
|
+
const mimeType = faviconData?.mime_type || "image/x-icon";
|
|
55
|
+
const isSvg = mimeType === "image/svg+xml" || faviconUrl.endsWith(".svg");
|
|
56
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
57
|
+
isSvg ? /* @__PURE__ */ jsx("link", { rel: "icon", type: "image/svg+xml", href: faviconUrl }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
58
|
+
/* @__PURE__ */ jsx("link", { rel: "icon", type: "image/png", sizes: "32x32", href: faviconUrl }),
|
|
59
|
+
/* @__PURE__ */ jsx("link", { rel: "icon", type: "image/png", sizes: "16x16", href: faviconUrl })
|
|
60
|
+
] }),
|
|
61
|
+
/* @__PURE__ */ jsx("link", { rel: "apple-touch-icon", sizes: "180x180", href: faviconUrl }),
|
|
62
|
+
/* @__PURE__ */ jsx("meta", { name: "theme-color", content: themeColor })
|
|
63
|
+
] });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { ManagedFavicon, fetchFaviconData, getManagedFaviconUrl };
|
|
67
|
+
//# sourceMappingURL=chunk-TFLQX7K7.mjs.map
|
|
68
|
+
//# sourceMappingURL=chunk-TFLQX7K7.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/images/ManagedFavicon.tsx"],"names":[],"mappings":"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","file":"chunk-TFLQX7K7.mjs","sourcesContent":["/**\n * ManagedFavicon Component\n * \n * Server component that renders favicon link tags using the project's logo.\n * When a logo is uploaded in Project Settings, it's automatically synced\n * to the 'favicon' slot in site_managed_images.\n * \n * Usage:\n * ```tsx\n * import { ManagedFavicon } from '@sonordev/site-kit/images'\n * \n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <head>\n * <ManagedFavicon />\n * </head>\n * <body>{children}</body>\n * </html>\n * )\n * }\n * ```\n * \n * Supports:\n * - SVG favicons (best for modern browsers, scales perfectly)\n * - PNG favicons with multiple sizes\n * - Apple touch icons\n * - Theme color for mobile browsers\n */\n\nimport React from 'react'\n\nexport interface ManagedFaviconProps {\n /**\n * API key for Portal API authentication\n * Defaults to NEXT_PUBLIC_UPTRADE_API_KEY env var\n */\n apiKey?: string\n \n /**\n * API URL (defaults to https://api.uptrademedia.com)\n */\n apiUrl?: string\n \n /**\n * Fallback favicon URL if no managed favicon is set\n */\n fallback?: string\n \n /**\n * Theme color for mobile browser chrome\n * Defaults to #4bbf39 (Uptrade brand primary)\n */\n themeColor?: string\n}\n\ninterface FaviconData {\n public_url?: string\n mime_type?: string\n is_placeholder?: boolean\n}\n\n/** If url is relative (e.g. /uploads/... from API), resolve against base so production can load it. */\nfunction toAbsoluteFaviconUrl(url: string | undefined, apiBase: string): string | undefined {\n if (!url) return undefined\n if (url.startsWith('http://') || url.startsWith('https://')) return url\n const base = apiBase.replace(/\\/$/, '')\n return url.startsWith('/') ? `${base}${url}` : `${base}/${url}`\n}\n\n/**\n * Server-side fetch of favicon data.\n * Exported for use in generateMetadata (single source for favicon URL).\n */\nexport async function fetchFaviconData(apiUrl: string, apiKey: string): Promise<FaviconData | null> {\n try {\n const res = await fetch(`${apiUrl}/public/images/slot/favicon`, {\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n next: { revalidate: 3600 }, // Cache 1hr – favicon rarely changes, allows static generation\n })\n\n if (!res.ok) {\n console.warn('[ManagedFavicon] Failed to fetch:', res.status)\n return null\n }\n\n const data = await res.json()\n if (data.image && !data.is_placeholder) {\n const raw = data.image.public_url || data.image.external_url\n const public_url = toAbsoluteFaviconUrl(raw, apiUrl)\n return {\n public_url: public_url || undefined,\n mime_type: data.image.file?.mime_type,\n is_placeholder: false,\n }\n }\n return null\n } catch (err) {\n console.error('[ManagedFavicon] Error fetching favicon:', err)\n return null\n }\n}\n\n/**\n * Return the managed favicon URL for the project (from API key).\n * Use in generateMetadata to set metadata.icons so favicon is consistent across all sites.\n */\nexport async function getManagedFaviconUrl(\n apiKey?: string,\n apiUrl: string = process.env.NEXT_PUBLIC_UPTRADE_API_URL || 'https://api.uptrademedia.com'\n): Promise<string | null> {\n const key = apiKey ?? process.env.NEXT_PUBLIC_UPTRADE_API_KEY ?? process.env.UPTRADE_API_KEY\n if (!key) return null\n const data = await fetchFaviconData(apiUrl, key)\n return data?.public_url ?? null\n}\n\nexport async function ManagedFavicon({\n apiKey = process.env.NEXT_PUBLIC_UPTRADE_API_KEY,\n apiUrl = process.env.NEXT_PUBLIC_UPTRADE_API_URL || 'https://api.uptrademedia.com',\n fallback = '/favicon.ico',\n themeColor = '#4bbf39',\n}: ManagedFaviconProps) {\n // Fetch favicon data during SSR\n const faviconData = apiKey && apiUrl ? await fetchFaviconData(apiUrl, apiKey) : null\n \n const faviconUrl = faviconData?.public_url || fallback\n const mimeType = faviconData?.mime_type || 'image/x-icon'\n const isSvg = mimeType === 'image/svg+xml' || faviconUrl.endsWith('.svg')\n\n return (\n <>\n {/* Primary favicon */}\n {isSvg ? (\n <link rel=\"icon\" type=\"image/svg+xml\" href={faviconUrl} />\n ) : (\n <>\n <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href={faviconUrl} />\n <link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href={faviconUrl} />\n </>\n )}\n \n {/* Apple touch icon (for iOS home screen) */}\n <link rel=\"apple-touch-icon\" sizes=\"180x180\" href={faviconUrl} />\n \n {/* Theme color for mobile browsers */}\n <meta name=\"theme-color\" content={themeColor} />\n </>\n )\n}"]}
|