@sonordev/site-kit 5.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/agent-manifest.json +8 -5
  2. package/dist/AnalyticsProvider-2LCSA6G3.mjs +10 -0
  3. package/dist/{AnalyticsProvider-KCLRFOX6.mjs.map → AnalyticsProvider-2LCSA6G3.mjs.map} +1 -1
  4. package/dist/AnalyticsProvider-V2U3TQMH.js +31 -0
  5. package/dist/{AnalyticsProvider-TXM3DRLI.js.map → AnalyticsProvider-V2U3TQMH.js.map} +1 -1
  6. package/dist/{BlogViewTracker-J6LRGUHU.js → BlogViewTracker-COTPD7WL.js} +6 -6
  7. package/dist/{BlogViewTracker-J6LRGUHU.js.map → BlogViewTracker-COTPD7WL.js.map} +1 -1
  8. package/dist/{BlogViewTracker-PZWAWB3O.mjs → BlogViewTracker-HG25ONRP.mjs} +5 -5
  9. package/dist/{BlogViewTracker-PZWAWB3O.mjs.map → BlogViewTracker-HG25ONRP.mjs.map} +1 -1
  10. package/dist/ChatWidget-4QUJYFXU.js +20 -0
  11. package/dist/{ChatWidget-LD54Z2PQ.js.map → ChatWidget-4QUJYFXU.js.map} +1 -1
  12. package/dist/ChatWidget-RWO7R7EJ.mjs +11 -0
  13. package/dist/{ChatWidget-PMDPSZXG.mjs.map → ChatWidget-RWO7R7EJ.mjs.map} +1 -1
  14. package/dist/EngageWidget-LF4AC4QN.mjs +9 -0
  15. package/dist/{EngageWidget-4CBFRN4Y.mjs.map → EngageWidget-LF4AC4QN.mjs.map} +1 -1
  16. package/dist/EngageWidget-RSOXB3YM.js +18 -0
  17. package/dist/{EngageWidget-BF5NMQ5A.js.map → EngageWidget-RSOXB3YM.js.map} +1 -1
  18. package/dist/{FileField-VXADIOMN.js → FileField-S2GUM3QB.js} +6 -6
  19. package/dist/{FileField-VXADIOMN.js.map → FileField-S2GUM3QB.js.map} +1 -1
  20. package/dist/{FileField-FFRHS3XP.mjs → FileField-YJ7DZXC3.mjs} +5 -5
  21. package/dist/{FileField-FFRHS3XP.mjs.map → FileField-YJ7DZXC3.mjs.map} +1 -1
  22. package/dist/{FormSpotlight-JG4ZHS56.js → FormSpotlight-7WAXLXZA.js} +15 -15
  23. package/dist/{FormSpotlight-JG4ZHS56.js.map → FormSpotlight-7WAXLXZA.js.map} +1 -1
  24. package/dist/{FormSpotlight-KE46EDP5.mjs → FormSpotlight-HLH5LQKI.mjs} +3 -3
  25. package/dist/{FormSpotlight-KE46EDP5.mjs.map → FormSpotlight-HLH5LQKI.mjs.map} +1 -1
  26. package/dist/{FormStage-5Z53Z2WY.js → FormStage-LEZYERJB.js} +13 -13
  27. package/dist/{FormStage-5Z53Z2WY.js.map → FormStage-LEZYERJB.js.map} +1 -1
  28. package/dist/{FormStage-PZKWEXZE.mjs → FormStage-Q5JW75AF.mjs} +3 -3
  29. package/dist/{FormStage-PZKWEXZE.mjs.map → FormStage-Q5JW75AF.mjs.map} +1 -1
  30. package/dist/ManagedForm-H4SEFSAF.js +23 -0
  31. package/dist/{ManagedForm-E4WHMARH.js.map → ManagedForm-H4SEFSAF.js.map} +1 -1
  32. package/dist/ManagedForm-U3NFIVEC.mjs +14 -0
  33. package/dist/{ManagedForm-VU2TKV6J.mjs.map → ManagedForm-U3NFIVEC.mjs.map} +1 -1
  34. package/dist/{ManagedNewsletterForm-PG4EX3W6.js → ManagedNewsletterForm-KT2NGUNW.js} +8 -8
  35. package/dist/{ManagedNewsletterForm-PG4EX3W6.js.map → ManagedNewsletterForm-KT2NGUNW.js.map} +1 -1
  36. package/dist/{ManagedNewsletterForm-6DET3VGZ.mjs → ManagedNewsletterForm-YKYW7B6P.mjs} +7 -7
  37. package/dist/{ManagedNewsletterForm-6DET3VGZ.mjs.map → ManagedNewsletterForm-YKYW7B6P.mjs.map} +1 -1
  38. package/dist/{SignalCore-RZVTLUC2.mjs → SignalCore-2BH5PUGS.mjs} +5 -5
  39. package/dist/{SignalCore-RZVTLUC2.mjs.map → SignalCore-2BH5PUGS.mjs.map} +1 -1
  40. package/dist/{SignalCore-TCUVXVMN.js → SignalCore-JM4ODFL2.js} +11 -11
  41. package/dist/{SignalCore-TCUVXVMN.js.map → SignalCore-JM4ODFL2.js.map} +1 -1
  42. package/dist/SitemapSync-5CXS2JGB.mjs +8 -0
  43. package/dist/{SitemapSync-T75NAFB3.mjs.map → SitemapSync-5CXS2JGB.mjs.map} +1 -1
  44. package/dist/SitemapSync-J4UMJRTO.js +17 -0
  45. package/dist/{SitemapSync-AJU4DM3G.js.map → SitemapSync-J4UMJRTO.js.map} +1 -1
  46. package/dist/analytics/index.js +9 -9
  47. package/dist/analytics/index.mjs +4 -4
  48. package/dist/blog/index.d.ts +9 -5
  49. package/dist/blog/index.d.ts.map +1 -1
  50. package/dist/blog/index.js +20 -20
  51. package/dist/blog/index.mjs +3 -3
  52. package/dist/blog/server-ui.js +14 -14
  53. package/dist/blog/server-ui.mjs +2 -2
  54. package/dist/{chunk-6QQFEJIP.js → chunk-2EJX56YJ.js} +8 -8
  55. package/dist/{chunk-6QQFEJIP.js.map → chunk-2EJX56YJ.js.map} +1 -1
  56. package/dist/{chunk-SXPINIJG.mjs → chunk-2XHEUWHV.mjs} +4 -4
  57. package/dist/{chunk-SXPINIJG.mjs.map → chunk-2XHEUWHV.mjs.map} +1 -1
  58. package/dist/{chunk-7T4VMXKD.js → chunk-44ZTBQH4.js} +3 -3
  59. package/dist/{chunk-7T4VMXKD.js.map → chunk-44ZTBQH4.js.map} +1 -1
  60. package/dist/chunk-4SQ6PROR.mjs +6 -0
  61. package/dist/{chunk-JPTJGLKS.mjs.map → chunk-4SQ6PROR.mjs.map} +1 -1
  62. package/dist/{chunk-PPCTL4B5.mjs → chunk-4TU246Q4.mjs} +10 -3
  63. package/dist/chunk-4TU246Q4.mjs.map +1 -0
  64. package/dist/{chunk-YSZB2DRX.mjs → chunk-5G2ZRQMO.mjs} +3 -3
  65. package/dist/{chunk-YSZB2DRX.mjs.map → chunk-5G2ZRQMO.mjs.map} +1 -1
  66. package/dist/{chunk-FTBABL3T.mjs → chunk-5W56B52Z.mjs} +3 -3
  67. package/dist/{chunk-FTBABL3T.mjs.map → chunk-5W56B52Z.mjs.map} +1 -1
  68. package/dist/{chunk-XPAHKYX7.mjs → chunk-6YKNDQYJ.mjs} +3 -3
  69. package/dist/{chunk-XPAHKYX7.mjs.map → chunk-6YKNDQYJ.mjs.map} +1 -1
  70. package/dist/{chunk-UV6KQUSL.mjs → chunk-A3VGZAZQ.mjs} +3 -3
  71. package/dist/{chunk-UV6KQUSL.mjs.map → chunk-A3VGZAZQ.mjs.map} +1 -1
  72. package/dist/{chunk-JY3KZDEM.js → chunk-ACLBRIYD.js} +3 -3
  73. package/dist/{chunk-JY3KZDEM.js.map → chunk-ACLBRIYD.js.map} +1 -1
  74. package/dist/{chunk-MGPHBVOK.js → chunk-ACWGG4VW.js} +9 -9
  75. package/dist/{chunk-MGPHBVOK.js.map → chunk-ACWGG4VW.js.map} +1 -1
  76. package/dist/{chunk-J4JXAFSW.mjs → chunk-ADBLBGCQ.mjs} +3 -3
  77. package/dist/{chunk-J4JXAFSW.mjs.map → chunk-ADBLBGCQ.mjs.map} +1 -1
  78. package/dist/{chunk-4UHO6DPJ.mjs → chunk-ADOGOLYO.mjs} +4 -4
  79. package/dist/{chunk-4UHO6DPJ.mjs.map → chunk-ADOGOLYO.mjs.map} +1 -1
  80. package/dist/{chunk-IX5RAZWX.js → chunk-BGBJSMXT.js} +6 -6
  81. package/dist/{chunk-IX5RAZWX.js.map → chunk-BGBJSMXT.js.map} +1 -1
  82. package/dist/{chunk-YVE65W47.mjs → chunk-BUDV7QHH.mjs} +4 -4
  83. package/dist/{chunk-YVE65W47.mjs.map → chunk-BUDV7QHH.mjs.map} +1 -1
  84. package/dist/{chunk-3SMG4EXR.mjs → chunk-BVLGJ2CZ.mjs} +3 -3
  85. package/dist/{chunk-3SMG4EXR.mjs.map → chunk-BVLGJ2CZ.mjs.map} +1 -1
  86. package/dist/{chunk-OGPS3DKE.js → chunk-CBMHJPR7.js} +5 -5
  87. package/dist/{chunk-OGPS3DKE.js.map → chunk-CBMHJPR7.js.map} +1 -1
  88. package/dist/{chunk-VACFVPNF.js → chunk-CGQO7MFO.js} +14 -14
  89. package/dist/{chunk-VACFVPNF.js.map → chunk-CGQO7MFO.js.map} +1 -1
  90. package/dist/{chunk-FQ3JQV3M.js → chunk-CKPFETIO.js} +4 -4
  91. package/dist/{chunk-FQ3JQV3M.js.map → chunk-CKPFETIO.js.map} +1 -1
  92. package/dist/{chunk-MVJQ4ZA2.mjs → chunk-CYQB5GLU.mjs} +4 -4
  93. package/dist/{chunk-MVJQ4ZA2.mjs.map → chunk-CYQB5GLU.mjs.map} +1 -1
  94. package/dist/{chunk-GCZMDY2T.js → chunk-DKRXPXOR.js} +3 -3
  95. package/dist/{chunk-GCZMDY2T.js.map → chunk-DKRXPXOR.js.map} +1 -1
  96. package/dist/{chunk-PZE5L2FK.mjs → chunk-DTNNZJYM.mjs} +4 -4
  97. package/dist/{chunk-PZE5L2FK.mjs.map → chunk-DTNNZJYM.mjs.map} +1 -1
  98. package/dist/{chunk-HCN2US4M.js → chunk-E3GHVAMK.js} +18 -18
  99. package/dist/{chunk-HCN2US4M.js.map → chunk-E3GHVAMK.js.map} +1 -1
  100. package/dist/{chunk-5JNLZRLX.mjs → chunk-EKTMXHBI.mjs} +3 -3
  101. package/dist/{chunk-5JNLZRLX.mjs.map → chunk-EKTMXHBI.mjs.map} +1 -1
  102. package/dist/{chunk-LLKXPJSE.js → chunk-EZLK2OQL.js} +7 -7
  103. package/dist/{chunk-LLKXPJSE.js.map → chunk-EZLK2OQL.js.map} +1 -1
  104. package/dist/{chunk-BQY3FJ3R.js → chunk-FMOOQKZU.js} +8 -8
  105. package/dist/{chunk-BQY3FJ3R.js.map → chunk-FMOOQKZU.js.map} +1 -1
  106. package/dist/{chunk-QI2H5KK6.mjs → chunk-FZISCIJV.mjs} +4 -4
  107. package/dist/{chunk-QI2H5KK6.mjs.map → chunk-FZISCIJV.mjs.map} +1 -1
  108. package/dist/{chunk-62ODUWUX.js → chunk-GYAH4KFC.js} +4 -4
  109. package/dist/{chunk-62ODUWUX.js.map → chunk-GYAH4KFC.js.map} +1 -1
  110. package/dist/{chunk-JIUVBSQZ.js → chunk-GZVSGQKT.js} +3 -3
  111. package/dist/{chunk-JIUVBSQZ.js.map → chunk-GZVSGQKT.js.map} +1 -1
  112. package/dist/{chunk-3FZ76VI5.js → chunk-HPIY7AR3.js} +14 -14
  113. package/dist/{chunk-3FZ76VI5.js.map → chunk-HPIY7AR3.js.map} +1 -1
  114. package/dist/{chunk-5OHHYGD6.js → chunk-IQLYFQKZ.js} +15 -15
  115. package/dist/{chunk-5OHHYGD6.js.map → chunk-IQLYFQKZ.js.map} +1 -1
  116. package/dist/{chunk-DX6D7HDI.js → chunk-JEI3CSY4.js} +4 -4
  117. package/dist/{chunk-DX6D7HDI.js.map → chunk-JEI3CSY4.js.map} +1 -1
  118. package/dist/{chunk-LZY374FB.js → chunk-MCXHQHIX.js} +4 -4
  119. package/dist/{chunk-LZY374FB.js.map → chunk-MCXHQHIX.js.map} +1 -1
  120. package/dist/{chunk-FCKL5CF3.mjs → chunk-NIJVVW4G.mjs} +3 -3
  121. package/dist/{chunk-FCKL5CF3.mjs.map → chunk-NIJVVW4G.mjs.map} +1 -1
  122. package/dist/{chunk-YGRHEEPX.mjs → chunk-NSTY7ZZA.mjs} +7 -7
  123. package/dist/{chunk-YGRHEEPX.mjs.map → chunk-NSTY7ZZA.mjs.map} +1 -1
  124. package/dist/{chunk-HEUCDF3O.mjs → chunk-OAJVITNO.mjs} +3 -3
  125. package/dist/{chunk-HEUCDF3O.mjs.map → chunk-OAJVITNO.mjs.map} +1 -1
  126. package/dist/{chunk-SW6KWUNI.mjs → chunk-OTHKWMDB.mjs} +3 -3
  127. package/dist/{chunk-SW6KWUNI.mjs.map → chunk-OTHKWMDB.mjs.map} +1 -1
  128. package/dist/{chunk-EISVUD75.mjs → chunk-ROJ25QM3.mjs} +3 -3
  129. package/dist/{chunk-EISVUD75.mjs.map → chunk-ROJ25QM3.mjs.map} +1 -1
  130. package/dist/{chunk-FDWDXMJL.mjs → chunk-SXNKLHOX.mjs} +3 -3
  131. package/dist/{chunk-FDWDXMJL.mjs.map → chunk-SXNKLHOX.mjs.map} +1 -1
  132. package/dist/{chunk-MPO6TRLS.js → chunk-TAM2D54C.js} +14 -14
  133. package/dist/{chunk-MPO6TRLS.js.map → chunk-TAM2D54C.js.map} +1 -1
  134. package/dist/{chunk-MWXBRCM2.mjs → chunk-TECD4AP7.mjs} +3 -3
  135. package/dist/{chunk-MWXBRCM2.mjs.map → chunk-TECD4AP7.mjs.map} +1 -1
  136. package/dist/{chunk-AXYKBQVI.mjs → chunk-UMWVTIIZ.mjs} +8 -8
  137. package/dist/{chunk-AXYKBQVI.mjs.map → chunk-UMWVTIIZ.mjs.map} +1 -1
  138. package/dist/{chunk-5JQLLF2T.js → chunk-VCNPTLK2.js} +12 -12
  139. package/dist/{chunk-5JQLLF2T.js.map → chunk-VCNPTLK2.js.map} +1 -1
  140. package/dist/{chunk-4D2G5ZNP.js → chunk-VIIGOCX4.js} +15 -15
  141. package/dist/{chunk-4D2G5ZNP.js.map → chunk-VIIGOCX4.js.map} +1 -1
  142. package/dist/{chunk-QH3WVTEK.mjs → chunk-VWVUYL5J.mjs} +4 -4
  143. package/dist/{chunk-QH3WVTEK.mjs.map → chunk-VWVUYL5J.mjs.map} +1 -1
  144. package/dist/{chunk-KWH6RAVY.js → chunk-WMNRTCYY.js} +11 -11
  145. package/dist/{chunk-KWH6RAVY.js.map → chunk-WMNRTCYY.js.map} +1 -1
  146. package/dist/{chunk-N3RWPAR5.js → chunk-WZMM57R2.js} +5 -5
  147. package/dist/{chunk-N3RWPAR5.js.map → chunk-WZMM57R2.js.map} +1 -1
  148. package/dist/chunk-YPEX4RDE.js +8 -0
  149. package/dist/{chunk-S65JSRWW.js.map → chunk-YPEX4RDE.js.map} +1 -1
  150. package/dist/{chunk-UABMMR7U.js → chunk-Z33JXJTJ.js} +10 -3
  151. package/dist/chunk-Z33JXJTJ.js.map +1 -0
  152. package/dist/{chunk-UFURXSAI.mjs → chunk-ZFZMN4LT.mjs} +3 -3
  153. package/dist/{chunk-UFURXSAI.mjs.map → chunk-ZFZMN4LT.mjs.map} +1 -1
  154. package/dist/cli/index.js +3 -3
  155. package/dist/cli/index.mjs +1 -1
  156. package/dist/client/index.js +4 -4
  157. package/dist/client/index.mjs +3 -3
  158. package/dist/commerce/index.js +56 -56
  159. package/dist/commerce/index.mjs +5 -5
  160. package/dist/commerce/server-result.test.d.ts +2 -0
  161. package/dist/commerce/server-result.test.d.ts.map +1 -0
  162. package/dist/commerce/server.d.ts +51 -0
  163. package/dist/commerce/server.d.ts.map +1 -1
  164. package/dist/commerce/server.js +57 -24
  165. package/dist/commerce/server.js.map +1 -1
  166. package/dist/commerce/server.mjs +54 -25
  167. package/dist/commerce/server.mjs.map +1 -1
  168. package/dist/engage/index.js +10 -10
  169. package/dist/engage/index.mjs +7 -7
  170. package/dist/fleet/index.js +8 -8
  171. package/dist/fleet/index.mjs +4 -4
  172. package/dist/forms/index.js +16 -16
  173. package/dist/forms/index.mjs +9 -9
  174. package/dist/forms/server.js +10 -10
  175. package/dist/forms/server.mjs +8 -8
  176. package/dist/forms/static.js +7 -7
  177. package/dist/forms/static.mjs +6 -6
  178. package/dist/images/index.js +11 -11
  179. package/dist/images/index.mjs +4 -4
  180. package/dist/index.js +63 -63
  181. package/dist/index.mjs +9 -9
  182. package/dist/layout/client.js +8 -8
  183. package/dist/layout/client.mjs +7 -7
  184. package/dist/layout/index.js +10 -10
  185. package/dist/layout/index.mjs +8 -8
  186. package/dist/llms/generateLLMsTxt.d.ts.map +1 -1
  187. package/dist/llms/humanize-industry.d.ts +9 -0
  188. package/dist/llms/humanize-industry.d.ts.map +1 -0
  189. package/dist/llms/humanize-industry.test.d.ts +2 -0
  190. package/dist/llms/humanize-industry.test.d.ts.map +1 -0
  191. package/dist/llms/index.js +12 -12
  192. package/dist/llms/index.mjs +2 -2
  193. package/dist/maps/index.js +5 -5
  194. package/dist/maps/index.mjs +3 -3
  195. package/dist/search/index.js +4 -4
  196. package/dist/search/index.mjs +3 -3
  197. package/dist/seo/client.js +5 -5
  198. package/dist/seo/client.mjs +4 -4
  199. package/dist/seo/index.js +5 -5
  200. package/dist/seo/index.mjs +4 -4
  201. package/dist/server/index.js +3 -3
  202. package/dist/server/index.mjs +2 -2
  203. package/dist/shared/version.d.ts +1 -1
  204. package/dist/signal/index.js +11 -11
  205. package/dist/signal/index.mjs +2 -2
  206. package/dist/sitemap/index.js +2 -2
  207. package/dist/sitemap/index.mjs +1 -1
  208. package/dist/sync/index.js +16 -16
  209. package/dist/sync/index.mjs +4 -4
  210. package/package.json +1 -1
  211. package/dist/AnalyticsProvider-KCLRFOX6.mjs +0 -10
  212. package/dist/AnalyticsProvider-TXM3DRLI.js +0 -31
  213. package/dist/ChatWidget-LD54Z2PQ.js +0 -20
  214. package/dist/ChatWidget-PMDPSZXG.mjs +0 -11
  215. package/dist/EngageWidget-4CBFRN4Y.mjs +0 -9
  216. package/dist/EngageWidget-BF5NMQ5A.js +0 -18
  217. package/dist/ManagedForm-E4WHMARH.js +0 -23
  218. package/dist/ManagedForm-VU2TKV6J.mjs +0 -14
  219. package/dist/SitemapSync-AJU4DM3G.js +0 -17
  220. package/dist/SitemapSync-T75NAFB3.mjs +0 -8
  221. package/dist/chunk-JPTJGLKS.mjs +0 -6
  222. package/dist/chunk-PPCTL4B5.mjs.map +0 -1
  223. package/dist/chunk-S65JSRWW.js +0 -8
  224. package/dist/chunk-UABMMR7U.js.map +0 -1
@@ -3,6 +3,10 @@
3
3
  require('../chunk-SNAQBZPT.js');
4
4
 
5
5
  // src/commerce/server.ts
6
+ function isFrameworkSignal(err) {
7
+ const digest = err?.digest;
8
+ return typeof digest === "string" && (digest === "DYNAMIC_SERVER_USAGE" || digest.startsWith("NEXT_"));
9
+ }
6
10
  async function apiFetch(config, endpoint) {
7
11
  try {
8
12
  const response = await fetch(`${config.apiUrl}${endpoint}`, {
@@ -12,11 +16,15 @@ async function apiFetch(config, endpoint) {
12
16
  "Content-Type": "application/json"
13
17
  }
14
18
  });
15
- if (!response.ok) return null;
16
- return response.json();
19
+ if (!response.ok) {
20
+ console.error(`[site-kit/commerce] GET ${endpoint} failed: ${response.status}`);
21
+ return { ok: false, data: null };
22
+ }
23
+ return { ok: true, data: await response.json() };
17
24
  } catch (error) {
18
- console.error("API fetch error:", error);
19
- return null;
25
+ if (isFrameworkSignal(error)) throw error;
26
+ console.error(`[site-kit/commerce] GET ${endpoint} failed:`, error);
27
+ return { ok: false, data: null };
20
28
  }
21
29
  }
22
30
  async function apiPost(config, endpoint, body) {
@@ -30,11 +38,15 @@ async function apiPost(config, endpoint, body) {
30
38
  },
31
39
  body: JSON.stringify(body)
32
40
  });
33
- if (!response.ok) return null;
34
- return response.json();
41
+ if (!response.ok) {
42
+ console.error(`[site-kit/commerce] POST ${endpoint} failed: ${response.status}`);
43
+ return { ok: false, data: null };
44
+ }
45
+ return { ok: true, data: await response.json() };
35
46
  } catch (error) {
36
- console.error("API fetch error:", error);
37
- return null;
47
+ if (isFrameworkSignal(error)) throw error;
48
+ console.error(`[site-kit/commerce] POST ${endpoint} failed:`, error);
49
+ return { ok: false, data: null };
38
50
  }
39
51
  }
40
52
  function transformOffering(data) {
@@ -80,18 +92,20 @@ function transformOffering(data) {
80
92
  };
81
93
  }
82
94
  async function getProductPaths(config) {
83
- const data = await apiFetch(
95
+ const { ok, data } = await apiFetch(
84
96
  config,
85
97
  `/public/commerce/offerings?type=product&status=active&fields=slug`
86
98
  );
99
+ if (!ok) console.error("[site-kit/commerce] getProductPaths failed \u2014 0 product pages will be prerendered");
87
100
  if (!data?.offerings) return [];
88
101
  return data.offerings.map((item) => ({ params: { slug: item.slug } }));
89
102
  }
90
103
  async function getEventPaths(config) {
91
- const data = await apiFetch(
104
+ const { ok, data } = await apiFetch(
92
105
  config,
93
106
  `/public/commerce/offerings?type=event&status=active&fields=slug`
94
107
  );
108
+ if (!ok) console.error("[site-kit/commerce] getEventPaths failed \u2014 0 event pages will be prerendered");
95
109
  if (!data?.offerings) return [];
96
110
  return data.offerings.map((item) => ({ params: { slug: item.slug } }));
97
111
  }
@@ -104,22 +118,29 @@ async function getOfferingPaths(config, type) {
104
118
  typeParam = `&type=${type}`;
105
119
  }
106
120
  }
107
- const data = await apiFetch(
121
+ const { ok, data } = await apiFetch(
108
122
  config,
109
123
  `/public/commerce/offerings?status=active&fields=slug${typeParam}`
110
124
  );
125
+ if (!ok) console.error("[site-kit/commerce] getOfferingPaths failed \u2014 0 pages will be prerendered");
111
126
  if (!data?.offerings) return [];
112
127
  return data.offerings.map((item) => ({ params: { slug: item.slug } }));
113
128
  }
114
129
  async function getOfferingBySlug(config, slug) {
115
- const data = await apiFetch(
130
+ return (await getOfferingBySlugResult(config, slug)).data;
131
+ }
132
+ async function getOfferingBySlugResult(config, slug) {
133
+ const { ok, data } = await apiFetch(
116
134
  config,
117
135
  `/public/commerce/offerings/${slug}`
118
136
  );
119
- if (!data?.offering) return null;
120
- return transformOffering(data.offering);
137
+ if (!data?.offering) return { ok, data: null };
138
+ return { ok: true, data: transformOffering(data.offering) };
121
139
  }
122
140
  async function getOfferings(config, options = {}) {
141
+ return (await getOfferingsResult(config, options)).data;
142
+ }
143
+ async function getOfferingsResult(config, options = {}) {
123
144
  const params = new URLSearchParams();
124
145
  params.set("status", options.status || "active");
125
146
  if (options.type) {
@@ -135,15 +156,18 @@ async function getOfferings(config, options = {}) {
135
156
  if (options.limit) {
136
157
  params.set("limit", options.limit.toString());
137
158
  }
138
- const data = await apiFetch(
159
+ const { ok, data } = await apiFetch(
139
160
  config,
140
161
  `/public/commerce/offerings?${params.toString()}`
141
162
  );
142
- if (!data?.offerings) return [];
143
- return data.offerings.map(transformOffering);
163
+ if (!data?.offerings) return { ok, data: [] };
164
+ return { ok: true, data: data.offerings.map(transformOffering) };
144
165
  }
145
166
  async function getUpcomingEvents(config, options = {}) {
146
- const data = await apiPost(
167
+ return (await getUpcomingEventsResult(config, options)).data;
168
+ }
169
+ async function getUpcomingEventsResult(config, options = {}) {
170
+ const primary = await apiPost(
147
171
  config,
148
172
  "/public/commerce/events",
149
173
  {
@@ -153,8 +177,8 @@ async function getUpcomingEvents(config, options = {}) {
153
177
  includePast: false
154
178
  }
155
179
  );
156
- if (data?.events) {
157
- return data.events.map(transformOffering);
180
+ if (primary.ok && primary.data?.events) {
181
+ return { ok: true, data: primary.data.events.map(transformOffering) };
158
182
  }
159
183
  const params = new URLSearchParams();
160
184
  params.set("type", "event");
@@ -170,12 +194,17 @@ async function getUpcomingEvents(config, options = {}) {
170
194
  config,
171
195
  `/public/commerce/offerings?${params.toString()}`
172
196
  );
173
- if (!legacy?.offerings) return [];
174
- return legacy.offerings.map(transformOffering);
197
+ if (legacy.ok && legacy.data?.offerings) {
198
+ return { ok: true, data: legacy.data.offerings.map(transformOffering) };
199
+ }
200
+ return { ok: primary.ok || legacy.ok, data: [] };
175
201
  }
176
202
  async function getNextEvent(config, category) {
177
- const events = await getUpcomingEvents(config, { limit: 1, category });
178
- return events[0] || null;
203
+ return (await getNextEventResult(config, category)).data;
204
+ }
205
+ async function getNextEventResult(config, category) {
206
+ const { ok, data } = await getUpcomingEventsResult(config, { limit: 1, category });
207
+ return { ok, data: data[0] || null };
179
208
  }
180
209
  function generateOfferingMetadata(offering, siteUrl) {
181
210
  if (!offering) return null;
@@ -209,10 +238,14 @@ exports.createServerConfig = createServerConfig;
209
238
  exports.generateOfferingMetadata = generateOfferingMetadata;
210
239
  exports.getEventPaths = getEventPaths;
211
240
  exports.getNextEvent = getNextEvent;
241
+ exports.getNextEventResult = getNextEventResult;
212
242
  exports.getOfferingBySlug = getOfferingBySlug;
243
+ exports.getOfferingBySlugResult = getOfferingBySlugResult;
213
244
  exports.getOfferingPaths = getOfferingPaths;
214
245
  exports.getOfferings = getOfferings;
246
+ exports.getOfferingsResult = getOfferingsResult;
215
247
  exports.getProductPaths = getProductPaths;
216
248
  exports.getUpcomingEvents = getUpcomingEvents;
249
+ exports.getUpcomingEventsResult = getUpcomingEventsResult;
217
250
  //# sourceMappingURL=server.js.map
218
251
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commerce/server.ts"],"names":[],"mappings":"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","file":"server.js","sourcesContent":["/**\n * @sonordev/site-kit/commerce - Server-side utilities\n * \n * Helpers for server-side rendering and dynamic routes.\n * Use in Next.js getStaticPaths, getStaticProps, or App Router.\n * \n * All data fetching goes through the Portal API.\n */\n\nimport type { CommerceOffering, OfferingType } from './types'\n\ninterface ServerConfig {\n apiUrl: string\n apiKey: string\n projectId: string\n}\n\ninterface SlugItem {\n slug: string\n}\n\nasync function apiFetch<T>(config: ServerConfig, endpoint: string): Promise<T | null> {\n try {\n const response = await fetch(`${config.apiUrl}${endpoint}`, {\n headers: {\n 'X-API-Key': config.apiKey,\n 'X-Project-ID': config.projectId,\n 'Content-Type': 'application/json',\n },\n })\n\n if (!response.ok) return null\n return response.json()\n } catch (error) {\n console.error('API fetch error:', error)\n return null\n }\n}\n\nasync function apiPost<T>(\n config: ServerConfig,\n endpoint: string,\n body: Record<string, unknown>,\n): Promise<T | null> {\n try {\n const response = await fetch(`${config.apiUrl}${endpoint}`, {\n method: 'POST',\n headers: {\n 'X-API-Key': config.apiKey,\n 'X-Project-ID': config.projectId,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n })\n\n if (!response.ok) return null\n return response.json()\n } catch (error) {\n console.error('API fetch error:', error)\n return null\n }\n}\n\nfunction transformOffering(data: Record<string, unknown>): CommerceOffering {\n return {\n id: data.id as string,\n project_id: data.project_id as string,\n type: data.type as OfferingType,\n status: data.status as 'draft' | 'active' | 'archived' | 'sold_out',\n name: data.name as string,\n slug: data.slug as string,\n description: data.description as string | undefined,\n short_description: data.short_description as string | undefined,\n long_description: data.long_description as string | undefined,\n featured_image_url: data.featured_image_url as string | undefined,\n gallery_images: (data.gallery_image_urls as string[]) || [],\n price_type: (data.price_type as 'fixed' | 'variable' | 'quote' | 'free') || 'fixed',\n price: data.price as number | undefined,\n compare_at_price: data.compare_at_price as number | undefined,\n currency: (data.currency as string) || 'USD',\n price_is_public: (data.price_is_public as boolean) ?? true,\n billing_period: data.billing_period as 'weekly' | 'monthly' | 'quarterly' | 'yearly' | undefined,\n track_inventory: data.track_inventory as boolean | undefined,\n inventory_count: data.inventory_count as number | undefined,\n allow_backorder: data.allow_backorder as boolean | undefined,\n duration_minutes: data.duration_minutes as number | undefined,\n capacity: data.capacity as number | undefined,\n location: data.location as string | undefined,\n is_virtual: data.is_virtual as boolean | undefined,\n virtual_meeting_url: data.virtual_meeting_url as string | undefined,\n requires_booking: data.requires_booking as boolean | undefined,\n booking_lead_time_hours: data.booking_lead_time_hours as number | undefined,\n category: data.category as CommerceOffering['category'],\n category_id: data.category_id as string | undefined,\n tags: (data.tags as string[]) || [],\n seo_title: data.seo_title as string | undefined,\n seo_description: data.seo_description as string | undefined,\n features: (data.features as string[]) || [],\n specifications: (data.specifications as Record<string, string>) || {},\n schedules: (data.schedules as CommerceOffering['schedules']) || [],\n variants: (data.variants as CommerceOffering['variants']) || [],\n created_at: data.created_at as string,\n updated_at: data.updated_at as string,\n }\n}\n\n// ============================================\n// Static Path Generators\n// ============================================\n\n/**\n * Get all product slugs for static generation\n * \n * @example Next.js Pages Router\n * export async function getStaticPaths() {\n * const paths = await getProductPaths(config)\n * return { paths, fallback: 'blocking' }\n * }\n */\nexport async function getProductPaths(config: ServerConfig): Promise<{ params: { slug: string } }[]> {\n const data = await apiFetch<{ offerings: SlugItem[] }>(\n config,\n `/public/commerce/offerings?type=product&status=active&fields=slug`\n )\n \n if (!data?.offerings) return []\n \n return data.offerings.map((item: SlugItem) => ({ params: { slug: item.slug } }))\n}\n\n/**\n * Get all event slugs for static generation\n */\nexport async function getEventPaths(config: ServerConfig): Promise<{ params: { slug: string } }[]> {\n const data = await apiFetch<{ offerings: SlugItem[] }>(\n config,\n `/public/commerce/offerings?type=event&status=active&fields=slug`\n )\n \n if (!data?.offerings) return []\n \n return data.offerings.map((item: SlugItem) => ({ params: { slug: item.slug } }))\n}\n\n/**\n * Get all offering slugs for static generation (any type)\n */\nexport async function getOfferingPaths(\n config: ServerConfig,\n type?: OfferingType | OfferingType[]\n): Promise<{ params: { slug: string } }[]> {\n let typeParam = ''\n if (type) {\n if (Array.isArray(type)) {\n typeParam = `&type=${type.join(',')}`\n } else {\n typeParam = `&type=${type}`\n }\n }\n \n const data = await apiFetch<{ offerings: SlugItem[] }>(\n config,\n `/public/commerce/offerings?status=active&fields=slug${typeParam}`\n )\n \n if (!data?.offerings) return []\n \n return data.offerings.map((item: SlugItem) => ({ params: { slug: item.slug } }))\n}\n\n// ============================================\n// Data Fetchers\n// ============================================\n\n/**\n * Fetch a single offering by slug (server-side)\n * \n * @example Next.js Pages Router\n * export async function getStaticProps({ params }) {\n * const product = await getOfferingBySlug(config, params.slug)\n * if (!product) return { notFound: true }\n * return { props: { product }, revalidate: 60 }\n * }\n */\nexport async function getOfferingBySlug(\n config: ServerConfig,\n slug: string\n): Promise<CommerceOffering | null> {\n const data = await apiFetch<{ offering: Record<string, unknown> }>(\n config,\n `/public/commerce/offerings/${slug}`\n )\n \n if (!data?.offering) return null\n \n return transformOffering(data.offering)\n}\n\n/**\n * Fetch all offerings of a type (server-side)\n */\nexport async function getOfferings(\n config: ServerConfig,\n options: {\n type?: OfferingType | OfferingType[]\n category?: string\n limit?: number\n status?: string\n } = {}\n): Promise<CommerceOffering[]> {\n const params = new URLSearchParams()\n params.set('status', options.status || 'active')\n \n if (options.type) {\n if (Array.isArray(options.type)) {\n params.set('type', options.type.join(','))\n } else {\n params.set('type', options.type)\n }\n }\n \n if (options.category) {\n params.set('category', options.category)\n }\n \n if (options.limit) {\n params.set('limit', options.limit.toString())\n }\n \n const data = await apiFetch<{ offerings: Record<string, unknown>[] }>(\n config,\n `/public/commerce/offerings?${params.toString()}`\n )\n \n if (!data?.offerings) return []\n \n return data.offerings.map(transformOffering)\n}\n\n/**\n * Fetch upcoming events (server-side), WITH schedules and category embedded.\n *\n * Uses POST /public/commerce/events — the same endpoint the client fetcher\n * uses — because the GET offerings list does NOT embed commerce_schedules,\n * which made this helper return events with empty `schedules[]` and forced\n * sites to hand-roll their own fetcher for any calendar UI (the Ludlow PTO\n * lesson). Falls back to the legacy offerings query if the events endpoint\n * is unavailable (older self-hosted API).\n */\nexport async function getUpcomingEvents(\n config: ServerConfig,\n options: { limit?: number; category?: string } = {}\n): Promise<CommerceOffering[]> {\n const data = await apiPost<{ events: Record<string, unknown>[] }>(\n config,\n '/public/commerce/events',\n {\n type: 'event',\n category: options.category,\n limit: options.limit,\n includePast: false,\n },\n )\n\n if (data?.events) {\n return data.events.map(transformOffering)\n }\n\n // Legacy fallback: offerings list (no schedules embedded).\n const params = new URLSearchParams()\n params.set('type', 'event')\n params.set('status', 'active')\n params.set('upcoming', 'true')\n\n if (options.category) {\n params.set('category', options.category)\n }\n\n if (options.limit) {\n params.set('limit', options.limit.toString())\n }\n\n const legacy = await apiFetch<{ offerings: Record<string, unknown>[] }>(\n config,\n `/public/commerce/offerings?${params.toString()}`\n )\n\n if (!legacy?.offerings) return []\n\n return legacy.offerings.map(transformOffering)\n}\n\n/**\n * Get next upcoming event (server-side)\n */\nexport async function getNextEvent(\n config: ServerConfig,\n category?: string\n): Promise<CommerceOffering | null> {\n const events = await getUpcomingEvents(config, { limit: 1, category })\n return events[0] || null\n}\n\n// ============================================\n// Metadata Helpers\n// ============================================\n\n/**\n * Generate page metadata for an offering\n * \n * @example Next.js App Router\n * export async function generateMetadata({ params }) {\n * const product = await getOfferingBySlug(config, params.slug)\n * return generateOfferingMetadata(product, 'https://example.com')\n * }\n */\nexport function generateOfferingMetadata(\n offering: CommerceOffering | null,\n siteUrl: string\n): {\n title: string\n description: string\n openGraph: {\n title: string\n description: string\n images: string[]\n type: string\n url: string\n }\n} | null {\n if (!offering) return null\n \n const title = offering.seo_title || offering.name\n const description = offering.seo_description || offering.short_description || offering.description || ''\n const images = offering.featured_image_url ? [offering.featured_image_url] : []\n \n const typeMap: Record<OfferingType, string> = {\n product: 'product',\n service: 'website',\n event: 'website',\n class: 'website',\n subscription: 'product',\n }\n \n return {\n title,\n description,\n openGraph: {\n title,\n description,\n images,\n type: typeMap[offering.type] || 'website',\n url: `${siteUrl}/${offering.type}s/${offering.slug}`,\n },\n }\n}\n\n// ============================================\n// Export config builder\n// ============================================\n\n/**\n * Create a server config for API calls\n * \n * @param apiUrl - Portal API URL (e.g., 'https://api.sonor.io')\n * @param apiKey - Project API key\n * @param projectId - Project ID\n */\nexport function createServerConfig(\n apiUrl: string,\n apiKey: string,\n projectId: string\n): ServerConfig {\n return { apiUrl, apiKey, projectId }\n}\n"]}
1
+ {"version":3,"sources":["../../src/commerce/server.ts"],"names":[],"mappings":"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","file":"server.js","sourcesContent":["/**\n * @sonordev/site-kit/commerce - Server-side utilities\n * \n * Helpers for server-side rendering and dynamic routes.\n * Use in Next.js getStaticPaths, getStaticProps, or App Router.\n * \n * All data fetching goes through the Portal API.\n */\n\nimport type { CommerceOffering, OfferingType } from './types'\n\ninterface ServerConfig {\n apiUrl: string\n apiKey: string\n projectId: string\n}\n\ninterface SlugItem {\n slug: string\n}\n\n/**\n * Outcome of a server-side fetch.\n *\n * `ok` is the entire point. A helper that returns an empty array on failure lets\n * the calling page state a business fact it does not know: \"no upcoming events\",\n * \"no products available\" — when the truth is only that the API was unreachable.\n * QCR's /public-cruises told customers the business had no cruises during a\n * transient failure while three were on sale (2026-08-27).\n *\n * Branch on `ok` in new code. The legacy helpers returning a bare array or null\n * are kept for compatibility, but they cannot express this difference — an empty\n * result from them is indistinguishable from an outage.\n */\nexport interface ServerResult<T> {\n /** false = the request failed. `ok: true` with empty data means genuinely empty. */\n ok: boolean\n data: T\n}\n\n/**\n * Next throws to signal control flow, not failure — a DynamicServerError during\n * the static pass means \"this route can't be prerendered\", and redirect()/\n * notFound() throw too. Catching those and returning a failure logs a fake fetch\n * error on every build and makes a real API outage look identical to the\n * framework doing its job. Always rethrow.\n */\nfunction isFrameworkSignal(err: unknown): boolean {\n const digest = (err as { digest?: unknown } | null)?.digest\n return (\n typeof digest === 'string' &&\n (digest === 'DYNAMIC_SERVER_USAGE' || digest.startsWith('NEXT_'))\n )\n}\n\nasync function apiFetch<T>(\n config: ServerConfig,\n endpoint: string,\n): Promise<ServerResult<T | null>> {\n try {\n const response = await fetch(`${config.apiUrl}${endpoint}`, {\n headers: {\n 'X-API-Key': config.apiKey,\n 'X-Project-ID': config.projectId,\n 'Content-Type': 'application/json',\n },\n })\n\n if (!response.ok) {\n console.error(`[site-kit/commerce] GET ${endpoint} failed: ${response.status}`)\n return { ok: false, data: null }\n }\n return { ok: true, data: (await response.json()) as T }\n } catch (error) {\n if (isFrameworkSignal(error)) throw error\n console.error(`[site-kit/commerce] GET ${endpoint} failed:`, error)\n return { ok: false, data: null }\n }\n}\n\nasync function apiPost<T>(\n config: ServerConfig,\n endpoint: string,\n body: Record<string, unknown>,\n): Promise<ServerResult<T | null>> {\n try {\n const response = await fetch(`${config.apiUrl}${endpoint}`, {\n method: 'POST',\n headers: {\n 'X-API-Key': config.apiKey,\n 'X-Project-ID': config.projectId,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n })\n\n if (!response.ok) {\n console.error(`[site-kit/commerce] POST ${endpoint} failed: ${response.status}`)\n return { ok: false, data: null }\n }\n return { ok: true, data: (await response.json()) as T }\n } catch (error) {\n if (isFrameworkSignal(error)) throw error\n console.error(`[site-kit/commerce] POST ${endpoint} failed:`, error)\n return { ok: false, data: null }\n }\n}\n\nfunction transformOffering(data: Record<string, unknown>): CommerceOffering {\n return {\n id: data.id as string,\n project_id: data.project_id as string,\n type: data.type as OfferingType,\n status: data.status as 'draft' | 'active' | 'archived' | 'sold_out',\n name: data.name as string,\n slug: data.slug as string,\n description: data.description as string | undefined,\n short_description: data.short_description as string | undefined,\n long_description: data.long_description as string | undefined,\n featured_image_url: data.featured_image_url as string | undefined,\n gallery_images: (data.gallery_image_urls as string[]) || [],\n price_type: (data.price_type as 'fixed' | 'variable' | 'quote' | 'free') || 'fixed',\n price: data.price as number | undefined,\n compare_at_price: data.compare_at_price as number | undefined,\n currency: (data.currency as string) || 'USD',\n price_is_public: (data.price_is_public as boolean) ?? true,\n billing_period: data.billing_period as 'weekly' | 'monthly' | 'quarterly' | 'yearly' | undefined,\n track_inventory: data.track_inventory as boolean | undefined,\n inventory_count: data.inventory_count as number | undefined,\n allow_backorder: data.allow_backorder as boolean | undefined,\n duration_minutes: data.duration_minutes as number | undefined,\n capacity: data.capacity as number | undefined,\n location: data.location as string | undefined,\n is_virtual: data.is_virtual as boolean | undefined,\n virtual_meeting_url: data.virtual_meeting_url as string | undefined,\n requires_booking: data.requires_booking as boolean | undefined,\n booking_lead_time_hours: data.booking_lead_time_hours as number | undefined,\n category: data.category as CommerceOffering['category'],\n category_id: data.category_id as string | undefined,\n tags: (data.tags as string[]) || [],\n seo_title: data.seo_title as string | undefined,\n seo_description: data.seo_description as string | undefined,\n features: (data.features as string[]) || [],\n specifications: (data.specifications as Record<string, string>) || {},\n schedules: (data.schedules as CommerceOffering['schedules']) || [],\n variants: (data.variants as CommerceOffering['variants']) || [],\n created_at: data.created_at as string,\n updated_at: data.updated_at as string,\n }\n}\n\n// ============================================\n// Static Path Generators\n// ============================================\n\n/**\n * Get all product slugs for static generation\n * \n * @example Next.js Pages Router\n * export async function getStaticPaths() {\n * const paths = await getProductPaths(config)\n * return { paths, fallback: 'blocking' }\n * }\n */\nexport async function getProductPaths(config: ServerConfig): Promise<{ params: { slug: string } }[]> {\n const { ok, data } = await apiFetch<{ offerings: SlugItem[] }>(\n config,\n `/public/commerce/offerings?type=product&status=active&fields=slug`\n )\n\n // A failed path fetch prerenders ZERO product pages and looks like a healthy\n // build. Say so loudly rather than shipping a site with its catalogue missing.\n if (!ok) console.error('[site-kit/commerce] getProductPaths failed — 0 product pages will be prerendered')\n if (!data?.offerings) return []\n\n return data.offerings.map((item: SlugItem) => ({ params: { slug: item.slug } }))\n}\n\n/**\n * Get all event slugs for static generation\n */\nexport async function getEventPaths(config: ServerConfig): Promise<{ params: { slug: string } }[]> {\n const { ok, data } = await apiFetch<{ offerings: SlugItem[] }>(\n config,\n `/public/commerce/offerings?type=event&status=active&fields=slug`\n )\n\n if (!ok) console.error('[site-kit/commerce] getEventPaths failed — 0 event pages will be prerendered')\n if (!data?.offerings) return []\n\n return data.offerings.map((item: SlugItem) => ({ params: { slug: item.slug } }))\n}\n\n/**\n * Get all offering slugs for static generation (any type)\n */\nexport async function getOfferingPaths(\n config: ServerConfig,\n type?: OfferingType | OfferingType[]\n): Promise<{ params: { slug: string } }[]> {\n let typeParam = ''\n if (type) {\n if (Array.isArray(type)) {\n typeParam = `&type=${type.join(',')}`\n } else {\n typeParam = `&type=${type}`\n }\n }\n \n const { ok, data } = await apiFetch<{ offerings: SlugItem[] }>(\n config,\n `/public/commerce/offerings?status=active&fields=slug${typeParam}`\n )\n\n if (!ok) console.error('[site-kit/commerce] getOfferingPaths failed — 0 pages will be prerendered')\n if (!data?.offerings) return []\n\n return data.offerings.map((item: SlugItem) => ({ params: { slug: item.slug } }))\n}\n\n// ============================================\n// Data Fetchers\n// ============================================\n\n/**\n * Fetch a single offering by slug (server-side)\n * \n * @example Next.js Pages Router\n * export async function getStaticProps({ params }) {\n * const product = await getOfferingBySlug(config, params.slug)\n * if (!product) return { notFound: true }\n * return { props: { product }, revalidate: 60 }\n * }\n */\nexport async function getOfferingBySlug(\n config: ServerConfig,\n slug: string\n): Promise<CommerceOffering | null> {\n return (await getOfferingBySlugResult(config, slug)).data\n}\n\n/**\n * As getOfferingBySlug, but distinguishes \"no such offering\" (ok: true, null)\n * from \"could not reach the API\" (ok: false, null). Use this before rendering a\n * 404 — a notFound() on a transient failure de-indexes a page that exists.\n */\nexport async function getOfferingBySlugResult(\n config: ServerConfig,\n slug: string\n): Promise<ServerResult<CommerceOffering | null>> {\n const { ok, data } = await apiFetch<{ offering: Record<string, unknown> }>(\n config,\n `/public/commerce/offerings/${slug}`\n )\n\n if (!data?.offering) return { ok, data: null }\n\n return { ok: true, data: transformOffering(data.offering) }\n}\n\n/**\n * Fetch all offerings of a type (server-side)\n */\nexport async function getOfferings(\n config: ServerConfig,\n options: {\n type?: OfferingType | OfferingType[]\n category?: string\n limit?: number\n status?: string\n } = {}\n): Promise<CommerceOffering[]> {\n return (await getOfferingsResult(config, options)).data\n}\n\n/**\n * As getOfferings, but distinguishes an empty catalogue from a failed request.\n * Branch on `ok` before rendering \"nothing available\" copy.\n */\nexport async function getOfferingsResult(\n config: ServerConfig,\n options: {\n type?: OfferingType | OfferingType[]\n category?: string\n limit?: number\n status?: string\n } = {}\n): Promise<ServerResult<CommerceOffering[]>> {\n const params = new URLSearchParams()\n params.set('status', options.status || 'active')\n \n if (options.type) {\n if (Array.isArray(options.type)) {\n params.set('type', options.type.join(','))\n } else {\n params.set('type', options.type)\n }\n }\n \n if (options.category) {\n params.set('category', options.category)\n }\n \n if (options.limit) {\n params.set('limit', options.limit.toString())\n }\n \n const { ok, data } = await apiFetch<{ offerings: Record<string, unknown>[] }>(\n config,\n `/public/commerce/offerings?${params.toString()}`\n )\n\n if (!data?.offerings) return { ok, data: [] }\n\n return { ok: true, data: data.offerings.map(transformOffering) }\n}\n\n/**\n * Fetch upcoming events (server-side), WITH schedules and category embedded.\n *\n * Uses POST /public/commerce/events — the same endpoint the client fetcher\n * uses — because the GET offerings list does NOT embed commerce_schedules,\n * which made this helper return events with empty `schedules[]` and forced\n * sites to hand-roll their own fetcher for any calendar UI (the Ludlow PTO\n * lesson). Falls back to the legacy offerings query if the events endpoint\n * is unavailable (older self-hosted API).\n */\nexport async function getUpcomingEvents(\n config: ServerConfig,\n options: { limit?: number; category?: string } = {}\n): Promise<CommerceOffering[]> {\n return (await getUpcomingEventsResult(config, options)).data\n}\n\n/**\n * As getUpcomingEvents, but tells you whether the list is empty because there is\n * nothing scheduled or because the API could not be reached.\n *\n * ALWAYS prefer this when the UI renders copy like \"no upcoming events\". Doing\n * otherwise is what put \"No public cruises currently on the schedule\" in front\n * of QCR's customers while three cruises were on sale — and, because the page\n * was statically revalidated, cached that claim for the whole revalidate window.\n */\nexport async function getUpcomingEventsResult(\n config: ServerConfig,\n options: { limit?: number; category?: string } = {}\n): Promise<ServerResult<CommerceOffering[]>> {\n const primary = await apiPost<{ events: Record<string, unknown>[] }>(\n config,\n '/public/commerce/events',\n {\n type: 'event',\n category: options.category,\n limit: options.limit,\n includePast: false,\n },\n )\n\n if (primary.ok && primary.data?.events) {\n return { ok: true, data: primary.data.events.map(transformOffering) }\n }\n\n // Legacy fallback: offerings list (no schedules embedded).\n const params = new URLSearchParams()\n params.set('type', 'event')\n params.set('status', 'active')\n params.set('upcoming', 'true')\n\n if (options.category) {\n params.set('category', options.category)\n }\n\n if (options.limit) {\n params.set('limit', options.limit.toString())\n }\n\n const legacy = await apiFetch<{ offerings: Record<string, unknown>[] }>(\n config,\n `/public/commerce/offerings?${params.toString()}`\n )\n\n if (legacy.ok && legacy.data?.offerings) {\n return { ok: true, data: legacy.data.offerings.map(transformOffering) }\n }\n\n // Nothing usable came back. It is only honestly \"empty\" if at least one call\n // actually reached the API; if both failed the caller must not claim empty.\n return { ok: primary.ok || legacy.ok, data: [] }\n}\n\n/**\n * Get next upcoming event (server-side)\n */\nexport async function getNextEvent(\n config: ServerConfig,\n category?: string\n): Promise<CommerceOffering | null> {\n return (await getNextEventResult(config, category)).data\n}\n\n/**\n * As getNextEvent, but distinguishes \"nothing scheduled\" from \"fetch failed\".\n */\nexport async function getNextEventResult(\n config: ServerConfig,\n category?: string\n): Promise<ServerResult<CommerceOffering | null>> {\n const { ok, data } = await getUpcomingEventsResult(config, { limit: 1, category })\n return { ok, data: data[0] || null }\n}\n\n// ============================================\n// Metadata Helpers\n// ============================================\n\n/**\n * Generate page metadata for an offering\n * \n * @example Next.js App Router\n * export async function generateMetadata({ params }) {\n * const product = await getOfferingBySlug(config, params.slug)\n * return generateOfferingMetadata(product, 'https://example.com')\n * }\n */\nexport function generateOfferingMetadata(\n offering: CommerceOffering | null,\n siteUrl: string\n): {\n title: string\n description: string\n openGraph: {\n title: string\n description: string\n images: string[]\n type: string\n url: string\n }\n} | null {\n if (!offering) return null\n \n const title = offering.seo_title || offering.name\n const description = offering.seo_description || offering.short_description || offering.description || ''\n const images = offering.featured_image_url ? [offering.featured_image_url] : []\n \n const typeMap: Record<OfferingType, string> = {\n product: 'product',\n service: 'website',\n event: 'website',\n class: 'website',\n subscription: 'product',\n }\n \n return {\n title,\n description,\n openGraph: {\n title,\n description,\n images,\n type: typeMap[offering.type] || 'website',\n url: `${siteUrl}/${offering.type}s/${offering.slug}`,\n },\n }\n}\n\n// ============================================\n// Export config builder\n// ============================================\n\n/**\n * Create a server config for API calls\n * \n * @param apiUrl - Portal API URL (e.g., 'https://api.sonor.io')\n * @param apiKey - Project API key\n * @param projectId - Project ID\n */\nexport function createServerConfig(\n apiUrl: string,\n apiKey: string,\n projectId: string\n): ServerConfig {\n return { apiUrl, apiKey, projectId }\n}\n"]}
@@ -1,6 +1,10 @@
1
1
  import '../chunk-BEXTDHO4.mjs';
2
2
 
3
3
  // src/commerce/server.ts
4
+ function isFrameworkSignal(err) {
5
+ const digest = err?.digest;
6
+ return typeof digest === "string" && (digest === "DYNAMIC_SERVER_USAGE" || digest.startsWith("NEXT_"));
7
+ }
4
8
  async function apiFetch(config, endpoint) {
5
9
  try {
6
10
  const response = await fetch(`${config.apiUrl}${endpoint}`, {
@@ -10,11 +14,15 @@ async function apiFetch(config, endpoint) {
10
14
  "Content-Type": "application/json"
11
15
  }
12
16
  });
13
- if (!response.ok) return null;
14
- return response.json();
17
+ if (!response.ok) {
18
+ console.error(`[site-kit/commerce] GET ${endpoint} failed: ${response.status}`);
19
+ return { ok: false, data: null };
20
+ }
21
+ return { ok: true, data: await response.json() };
15
22
  } catch (error) {
16
- console.error("API fetch error:", error);
17
- return null;
23
+ if (isFrameworkSignal(error)) throw error;
24
+ console.error(`[site-kit/commerce] GET ${endpoint} failed:`, error);
25
+ return { ok: false, data: null };
18
26
  }
19
27
  }
20
28
  async function apiPost(config, endpoint, body) {
@@ -28,11 +36,15 @@ async function apiPost(config, endpoint, body) {
28
36
  },
29
37
  body: JSON.stringify(body)
30
38
  });
31
- if (!response.ok) return null;
32
- return response.json();
39
+ if (!response.ok) {
40
+ console.error(`[site-kit/commerce] POST ${endpoint} failed: ${response.status}`);
41
+ return { ok: false, data: null };
42
+ }
43
+ return { ok: true, data: await response.json() };
33
44
  } catch (error) {
34
- console.error("API fetch error:", error);
35
- return null;
45
+ if (isFrameworkSignal(error)) throw error;
46
+ console.error(`[site-kit/commerce] POST ${endpoint} failed:`, error);
47
+ return { ok: false, data: null };
36
48
  }
37
49
  }
38
50
  function transformOffering(data) {
@@ -78,18 +90,20 @@ function transformOffering(data) {
78
90
  };
79
91
  }
80
92
  async function getProductPaths(config) {
81
- const data = await apiFetch(
93
+ const { ok, data } = await apiFetch(
82
94
  config,
83
95
  `/public/commerce/offerings?type=product&status=active&fields=slug`
84
96
  );
97
+ if (!ok) console.error("[site-kit/commerce] getProductPaths failed \u2014 0 product pages will be prerendered");
85
98
  if (!data?.offerings) return [];
86
99
  return data.offerings.map((item) => ({ params: { slug: item.slug } }));
87
100
  }
88
101
  async function getEventPaths(config) {
89
- const data = await apiFetch(
102
+ const { ok, data } = await apiFetch(
90
103
  config,
91
104
  `/public/commerce/offerings?type=event&status=active&fields=slug`
92
105
  );
106
+ if (!ok) console.error("[site-kit/commerce] getEventPaths failed \u2014 0 event pages will be prerendered");
93
107
  if (!data?.offerings) return [];
94
108
  return data.offerings.map((item) => ({ params: { slug: item.slug } }));
95
109
  }
@@ -102,22 +116,29 @@ async function getOfferingPaths(config, type) {
102
116
  typeParam = `&type=${type}`;
103
117
  }
104
118
  }
105
- const data = await apiFetch(
119
+ const { ok, data } = await apiFetch(
106
120
  config,
107
121
  `/public/commerce/offerings?status=active&fields=slug${typeParam}`
108
122
  );
123
+ if (!ok) console.error("[site-kit/commerce] getOfferingPaths failed \u2014 0 pages will be prerendered");
109
124
  if (!data?.offerings) return [];
110
125
  return data.offerings.map((item) => ({ params: { slug: item.slug } }));
111
126
  }
112
127
  async function getOfferingBySlug(config, slug) {
113
- const data = await apiFetch(
128
+ return (await getOfferingBySlugResult(config, slug)).data;
129
+ }
130
+ async function getOfferingBySlugResult(config, slug) {
131
+ const { ok, data } = await apiFetch(
114
132
  config,
115
133
  `/public/commerce/offerings/${slug}`
116
134
  );
117
- if (!data?.offering) return null;
118
- return transformOffering(data.offering);
135
+ if (!data?.offering) return { ok, data: null };
136
+ return { ok: true, data: transformOffering(data.offering) };
119
137
  }
120
138
  async function getOfferings(config, options = {}) {
139
+ return (await getOfferingsResult(config, options)).data;
140
+ }
141
+ async function getOfferingsResult(config, options = {}) {
121
142
  const params = new URLSearchParams();
122
143
  params.set("status", options.status || "active");
123
144
  if (options.type) {
@@ -133,15 +154,18 @@ async function getOfferings(config, options = {}) {
133
154
  if (options.limit) {
134
155
  params.set("limit", options.limit.toString());
135
156
  }
136
- const data = await apiFetch(
157
+ const { ok, data } = await apiFetch(
137
158
  config,
138
159
  `/public/commerce/offerings?${params.toString()}`
139
160
  );
140
- if (!data?.offerings) return [];
141
- return data.offerings.map(transformOffering);
161
+ if (!data?.offerings) return { ok, data: [] };
162
+ return { ok: true, data: data.offerings.map(transformOffering) };
142
163
  }
143
164
  async function getUpcomingEvents(config, options = {}) {
144
- const data = await apiPost(
165
+ return (await getUpcomingEventsResult(config, options)).data;
166
+ }
167
+ async function getUpcomingEventsResult(config, options = {}) {
168
+ const primary = await apiPost(
145
169
  config,
146
170
  "/public/commerce/events",
147
171
  {
@@ -151,8 +175,8 @@ async function getUpcomingEvents(config, options = {}) {
151
175
  includePast: false
152
176
  }
153
177
  );
154
- if (data?.events) {
155
- return data.events.map(transformOffering);
178
+ if (primary.ok && primary.data?.events) {
179
+ return { ok: true, data: primary.data.events.map(transformOffering) };
156
180
  }
157
181
  const params = new URLSearchParams();
158
182
  params.set("type", "event");
@@ -168,12 +192,17 @@ async function getUpcomingEvents(config, options = {}) {
168
192
  config,
169
193
  `/public/commerce/offerings?${params.toString()}`
170
194
  );
171
- if (!legacy?.offerings) return [];
172
- return legacy.offerings.map(transformOffering);
195
+ if (legacy.ok && legacy.data?.offerings) {
196
+ return { ok: true, data: legacy.data.offerings.map(transformOffering) };
197
+ }
198
+ return { ok: primary.ok || legacy.ok, data: [] };
173
199
  }
174
200
  async function getNextEvent(config, category) {
175
- const events = await getUpcomingEvents(config, { limit: 1, category });
176
- return events[0] || null;
201
+ return (await getNextEventResult(config, category)).data;
202
+ }
203
+ async function getNextEventResult(config, category) {
204
+ const { ok, data } = await getUpcomingEventsResult(config, { limit: 1, category });
205
+ return { ok, data: data[0] || null };
177
206
  }
178
207
  function generateOfferingMetadata(offering, siteUrl) {
179
208
  if (!offering) return null;
@@ -203,6 +232,6 @@ function createServerConfig(apiUrl, apiKey, projectId) {
203
232
  return { apiUrl, apiKey, projectId };
204
233
  }
205
234
 
206
- export { createServerConfig, generateOfferingMetadata, getEventPaths, getNextEvent, getOfferingBySlug, getOfferingPaths, getOfferings, getProductPaths, getUpcomingEvents };
235
+ export { createServerConfig, generateOfferingMetadata, getEventPaths, getNextEvent, getNextEventResult, getOfferingBySlug, getOfferingBySlugResult, getOfferingPaths, getOfferings, getOfferingsResult, getProductPaths, getUpcomingEvents, getUpcomingEventsResult };
207
236
  //# sourceMappingURL=server.mjs.map
208
237
  //# sourceMappingURL=server.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commerce/server.ts"],"names":[],"mappings":"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","file":"server.mjs","sourcesContent":["/**\n * @sonordev/site-kit/commerce - Server-side utilities\n * \n * Helpers for server-side rendering and dynamic routes.\n * Use in Next.js getStaticPaths, getStaticProps, or App Router.\n * \n * All data fetching goes through the Portal API.\n */\n\nimport type { CommerceOffering, OfferingType } from './types'\n\ninterface ServerConfig {\n apiUrl: string\n apiKey: string\n projectId: string\n}\n\ninterface SlugItem {\n slug: string\n}\n\nasync function apiFetch<T>(config: ServerConfig, endpoint: string): Promise<T | null> {\n try {\n const response = await fetch(`${config.apiUrl}${endpoint}`, {\n headers: {\n 'X-API-Key': config.apiKey,\n 'X-Project-ID': config.projectId,\n 'Content-Type': 'application/json',\n },\n })\n\n if (!response.ok) return null\n return response.json()\n } catch (error) {\n console.error('API fetch error:', error)\n return null\n }\n}\n\nasync function apiPost<T>(\n config: ServerConfig,\n endpoint: string,\n body: Record<string, unknown>,\n): Promise<T | null> {\n try {\n const response = await fetch(`${config.apiUrl}${endpoint}`, {\n method: 'POST',\n headers: {\n 'X-API-Key': config.apiKey,\n 'X-Project-ID': config.projectId,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n })\n\n if (!response.ok) return null\n return response.json()\n } catch (error) {\n console.error('API fetch error:', error)\n return null\n }\n}\n\nfunction transformOffering(data: Record<string, unknown>): CommerceOffering {\n return {\n id: data.id as string,\n project_id: data.project_id as string,\n type: data.type as OfferingType,\n status: data.status as 'draft' | 'active' | 'archived' | 'sold_out',\n name: data.name as string,\n slug: data.slug as string,\n description: data.description as string | undefined,\n short_description: data.short_description as string | undefined,\n long_description: data.long_description as string | undefined,\n featured_image_url: data.featured_image_url as string | undefined,\n gallery_images: (data.gallery_image_urls as string[]) || [],\n price_type: (data.price_type as 'fixed' | 'variable' | 'quote' | 'free') || 'fixed',\n price: data.price as number | undefined,\n compare_at_price: data.compare_at_price as number | undefined,\n currency: (data.currency as string) || 'USD',\n price_is_public: (data.price_is_public as boolean) ?? true,\n billing_period: data.billing_period as 'weekly' | 'monthly' | 'quarterly' | 'yearly' | undefined,\n track_inventory: data.track_inventory as boolean | undefined,\n inventory_count: data.inventory_count as number | undefined,\n allow_backorder: data.allow_backorder as boolean | undefined,\n duration_minutes: data.duration_minutes as number | undefined,\n capacity: data.capacity as number | undefined,\n location: data.location as string | undefined,\n is_virtual: data.is_virtual as boolean | undefined,\n virtual_meeting_url: data.virtual_meeting_url as string | undefined,\n requires_booking: data.requires_booking as boolean | undefined,\n booking_lead_time_hours: data.booking_lead_time_hours as number | undefined,\n category: data.category as CommerceOffering['category'],\n category_id: data.category_id as string | undefined,\n tags: (data.tags as string[]) || [],\n seo_title: data.seo_title as string | undefined,\n seo_description: data.seo_description as string | undefined,\n features: (data.features as string[]) || [],\n specifications: (data.specifications as Record<string, string>) || {},\n schedules: (data.schedules as CommerceOffering['schedules']) || [],\n variants: (data.variants as CommerceOffering['variants']) || [],\n created_at: data.created_at as string,\n updated_at: data.updated_at as string,\n }\n}\n\n// ============================================\n// Static Path Generators\n// ============================================\n\n/**\n * Get all product slugs for static generation\n * \n * @example Next.js Pages Router\n * export async function getStaticPaths() {\n * const paths = await getProductPaths(config)\n * return { paths, fallback: 'blocking' }\n * }\n */\nexport async function getProductPaths(config: ServerConfig): Promise<{ params: { slug: string } }[]> {\n const data = await apiFetch<{ offerings: SlugItem[] }>(\n config,\n `/public/commerce/offerings?type=product&status=active&fields=slug`\n )\n \n if (!data?.offerings) return []\n \n return data.offerings.map((item: SlugItem) => ({ params: { slug: item.slug } }))\n}\n\n/**\n * Get all event slugs for static generation\n */\nexport async function getEventPaths(config: ServerConfig): Promise<{ params: { slug: string } }[]> {\n const data = await apiFetch<{ offerings: SlugItem[] }>(\n config,\n `/public/commerce/offerings?type=event&status=active&fields=slug`\n )\n \n if (!data?.offerings) return []\n \n return data.offerings.map((item: SlugItem) => ({ params: { slug: item.slug } }))\n}\n\n/**\n * Get all offering slugs for static generation (any type)\n */\nexport async function getOfferingPaths(\n config: ServerConfig,\n type?: OfferingType | OfferingType[]\n): Promise<{ params: { slug: string } }[]> {\n let typeParam = ''\n if (type) {\n if (Array.isArray(type)) {\n typeParam = `&type=${type.join(',')}`\n } else {\n typeParam = `&type=${type}`\n }\n }\n \n const data = await apiFetch<{ offerings: SlugItem[] }>(\n config,\n `/public/commerce/offerings?status=active&fields=slug${typeParam}`\n )\n \n if (!data?.offerings) return []\n \n return data.offerings.map((item: SlugItem) => ({ params: { slug: item.slug } }))\n}\n\n// ============================================\n// Data Fetchers\n// ============================================\n\n/**\n * Fetch a single offering by slug (server-side)\n * \n * @example Next.js Pages Router\n * export async function getStaticProps({ params }) {\n * const product = await getOfferingBySlug(config, params.slug)\n * if (!product) return { notFound: true }\n * return { props: { product }, revalidate: 60 }\n * }\n */\nexport async function getOfferingBySlug(\n config: ServerConfig,\n slug: string\n): Promise<CommerceOffering | null> {\n const data = await apiFetch<{ offering: Record<string, unknown> }>(\n config,\n `/public/commerce/offerings/${slug}`\n )\n \n if (!data?.offering) return null\n \n return transformOffering(data.offering)\n}\n\n/**\n * Fetch all offerings of a type (server-side)\n */\nexport async function getOfferings(\n config: ServerConfig,\n options: {\n type?: OfferingType | OfferingType[]\n category?: string\n limit?: number\n status?: string\n } = {}\n): Promise<CommerceOffering[]> {\n const params = new URLSearchParams()\n params.set('status', options.status || 'active')\n \n if (options.type) {\n if (Array.isArray(options.type)) {\n params.set('type', options.type.join(','))\n } else {\n params.set('type', options.type)\n }\n }\n \n if (options.category) {\n params.set('category', options.category)\n }\n \n if (options.limit) {\n params.set('limit', options.limit.toString())\n }\n \n const data = await apiFetch<{ offerings: Record<string, unknown>[] }>(\n config,\n `/public/commerce/offerings?${params.toString()}`\n )\n \n if (!data?.offerings) return []\n \n return data.offerings.map(transformOffering)\n}\n\n/**\n * Fetch upcoming events (server-side), WITH schedules and category embedded.\n *\n * Uses POST /public/commerce/events — the same endpoint the client fetcher\n * uses — because the GET offerings list does NOT embed commerce_schedules,\n * which made this helper return events with empty `schedules[]` and forced\n * sites to hand-roll their own fetcher for any calendar UI (the Ludlow PTO\n * lesson). Falls back to the legacy offerings query if the events endpoint\n * is unavailable (older self-hosted API).\n */\nexport async function getUpcomingEvents(\n config: ServerConfig,\n options: { limit?: number; category?: string } = {}\n): Promise<CommerceOffering[]> {\n const data = await apiPost<{ events: Record<string, unknown>[] }>(\n config,\n '/public/commerce/events',\n {\n type: 'event',\n category: options.category,\n limit: options.limit,\n includePast: false,\n },\n )\n\n if (data?.events) {\n return data.events.map(transformOffering)\n }\n\n // Legacy fallback: offerings list (no schedules embedded).\n const params = new URLSearchParams()\n params.set('type', 'event')\n params.set('status', 'active')\n params.set('upcoming', 'true')\n\n if (options.category) {\n params.set('category', options.category)\n }\n\n if (options.limit) {\n params.set('limit', options.limit.toString())\n }\n\n const legacy = await apiFetch<{ offerings: Record<string, unknown>[] }>(\n config,\n `/public/commerce/offerings?${params.toString()}`\n )\n\n if (!legacy?.offerings) return []\n\n return legacy.offerings.map(transformOffering)\n}\n\n/**\n * Get next upcoming event (server-side)\n */\nexport async function getNextEvent(\n config: ServerConfig,\n category?: string\n): Promise<CommerceOffering | null> {\n const events = await getUpcomingEvents(config, { limit: 1, category })\n return events[0] || null\n}\n\n// ============================================\n// Metadata Helpers\n// ============================================\n\n/**\n * Generate page metadata for an offering\n * \n * @example Next.js App Router\n * export async function generateMetadata({ params }) {\n * const product = await getOfferingBySlug(config, params.slug)\n * return generateOfferingMetadata(product, 'https://example.com')\n * }\n */\nexport function generateOfferingMetadata(\n offering: CommerceOffering | null,\n siteUrl: string\n): {\n title: string\n description: string\n openGraph: {\n title: string\n description: string\n images: string[]\n type: string\n url: string\n }\n} | null {\n if (!offering) return null\n \n const title = offering.seo_title || offering.name\n const description = offering.seo_description || offering.short_description || offering.description || ''\n const images = offering.featured_image_url ? [offering.featured_image_url] : []\n \n const typeMap: Record<OfferingType, string> = {\n product: 'product',\n service: 'website',\n event: 'website',\n class: 'website',\n subscription: 'product',\n }\n \n return {\n title,\n description,\n openGraph: {\n title,\n description,\n images,\n type: typeMap[offering.type] || 'website',\n url: `${siteUrl}/${offering.type}s/${offering.slug}`,\n },\n }\n}\n\n// ============================================\n// Export config builder\n// ============================================\n\n/**\n * Create a server config for API calls\n * \n * @param apiUrl - Portal API URL (e.g., 'https://api.sonor.io')\n * @param apiKey - Project API key\n * @param projectId - Project ID\n */\nexport function createServerConfig(\n apiUrl: string,\n apiKey: string,\n projectId: string\n): ServerConfig {\n return { apiUrl, apiKey, projectId }\n}\n"]}
1
+ {"version":3,"sources":["../../src/commerce/server.ts"],"names":[],"mappings":"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","file":"server.mjs","sourcesContent":["/**\n * @sonordev/site-kit/commerce - Server-side utilities\n * \n * Helpers for server-side rendering and dynamic routes.\n * Use in Next.js getStaticPaths, getStaticProps, or App Router.\n * \n * All data fetching goes through the Portal API.\n */\n\nimport type { CommerceOffering, OfferingType } from './types'\n\ninterface ServerConfig {\n apiUrl: string\n apiKey: string\n projectId: string\n}\n\ninterface SlugItem {\n slug: string\n}\n\n/**\n * Outcome of a server-side fetch.\n *\n * `ok` is the entire point. A helper that returns an empty array on failure lets\n * the calling page state a business fact it does not know: \"no upcoming events\",\n * \"no products available\" — when the truth is only that the API was unreachable.\n * QCR's /public-cruises told customers the business had no cruises during a\n * transient failure while three were on sale (2026-08-27).\n *\n * Branch on `ok` in new code. The legacy helpers returning a bare array or null\n * are kept for compatibility, but they cannot express this difference — an empty\n * result from them is indistinguishable from an outage.\n */\nexport interface ServerResult<T> {\n /** false = the request failed. `ok: true` with empty data means genuinely empty. */\n ok: boolean\n data: T\n}\n\n/**\n * Next throws to signal control flow, not failure — a DynamicServerError during\n * the static pass means \"this route can't be prerendered\", and redirect()/\n * notFound() throw too. Catching those and returning a failure logs a fake fetch\n * error on every build and makes a real API outage look identical to the\n * framework doing its job. Always rethrow.\n */\nfunction isFrameworkSignal(err: unknown): boolean {\n const digest = (err as { digest?: unknown } | null)?.digest\n return (\n typeof digest === 'string' &&\n (digest === 'DYNAMIC_SERVER_USAGE' || digest.startsWith('NEXT_'))\n )\n}\n\nasync function apiFetch<T>(\n config: ServerConfig,\n endpoint: string,\n): Promise<ServerResult<T | null>> {\n try {\n const response = await fetch(`${config.apiUrl}${endpoint}`, {\n headers: {\n 'X-API-Key': config.apiKey,\n 'X-Project-ID': config.projectId,\n 'Content-Type': 'application/json',\n },\n })\n\n if (!response.ok) {\n console.error(`[site-kit/commerce] GET ${endpoint} failed: ${response.status}`)\n return { ok: false, data: null }\n }\n return { ok: true, data: (await response.json()) as T }\n } catch (error) {\n if (isFrameworkSignal(error)) throw error\n console.error(`[site-kit/commerce] GET ${endpoint} failed:`, error)\n return { ok: false, data: null }\n }\n}\n\nasync function apiPost<T>(\n config: ServerConfig,\n endpoint: string,\n body: Record<string, unknown>,\n): Promise<ServerResult<T | null>> {\n try {\n const response = await fetch(`${config.apiUrl}${endpoint}`, {\n method: 'POST',\n headers: {\n 'X-API-Key': config.apiKey,\n 'X-Project-ID': config.projectId,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n })\n\n if (!response.ok) {\n console.error(`[site-kit/commerce] POST ${endpoint} failed: ${response.status}`)\n return { ok: false, data: null }\n }\n return { ok: true, data: (await response.json()) as T }\n } catch (error) {\n if (isFrameworkSignal(error)) throw error\n console.error(`[site-kit/commerce] POST ${endpoint} failed:`, error)\n return { ok: false, data: null }\n }\n}\n\nfunction transformOffering(data: Record<string, unknown>): CommerceOffering {\n return {\n id: data.id as string,\n project_id: data.project_id as string,\n type: data.type as OfferingType,\n status: data.status as 'draft' | 'active' | 'archived' | 'sold_out',\n name: data.name as string,\n slug: data.slug as string,\n description: data.description as string | undefined,\n short_description: data.short_description as string | undefined,\n long_description: data.long_description as string | undefined,\n featured_image_url: data.featured_image_url as string | undefined,\n gallery_images: (data.gallery_image_urls as string[]) || [],\n price_type: (data.price_type as 'fixed' | 'variable' | 'quote' | 'free') || 'fixed',\n price: data.price as number | undefined,\n compare_at_price: data.compare_at_price as number | undefined,\n currency: (data.currency as string) || 'USD',\n price_is_public: (data.price_is_public as boolean) ?? true,\n billing_period: data.billing_period as 'weekly' | 'monthly' | 'quarterly' | 'yearly' | undefined,\n track_inventory: data.track_inventory as boolean | undefined,\n inventory_count: data.inventory_count as number | undefined,\n allow_backorder: data.allow_backorder as boolean | undefined,\n duration_minutes: data.duration_minutes as number | undefined,\n capacity: data.capacity as number | undefined,\n location: data.location as string | undefined,\n is_virtual: data.is_virtual as boolean | undefined,\n virtual_meeting_url: data.virtual_meeting_url as string | undefined,\n requires_booking: data.requires_booking as boolean | undefined,\n booking_lead_time_hours: data.booking_lead_time_hours as number | undefined,\n category: data.category as CommerceOffering['category'],\n category_id: data.category_id as string | undefined,\n tags: (data.tags as string[]) || [],\n seo_title: data.seo_title as string | undefined,\n seo_description: data.seo_description as string | undefined,\n features: (data.features as string[]) || [],\n specifications: (data.specifications as Record<string, string>) || {},\n schedules: (data.schedules as CommerceOffering['schedules']) || [],\n variants: (data.variants as CommerceOffering['variants']) || [],\n created_at: data.created_at as string,\n updated_at: data.updated_at as string,\n }\n}\n\n// ============================================\n// Static Path Generators\n// ============================================\n\n/**\n * Get all product slugs for static generation\n * \n * @example Next.js Pages Router\n * export async function getStaticPaths() {\n * const paths = await getProductPaths(config)\n * return { paths, fallback: 'blocking' }\n * }\n */\nexport async function getProductPaths(config: ServerConfig): Promise<{ params: { slug: string } }[]> {\n const { ok, data } = await apiFetch<{ offerings: SlugItem[] }>(\n config,\n `/public/commerce/offerings?type=product&status=active&fields=slug`\n )\n\n // A failed path fetch prerenders ZERO product pages and looks like a healthy\n // build. Say so loudly rather than shipping a site with its catalogue missing.\n if (!ok) console.error('[site-kit/commerce] getProductPaths failed — 0 product pages will be prerendered')\n if (!data?.offerings) return []\n\n return data.offerings.map((item: SlugItem) => ({ params: { slug: item.slug } }))\n}\n\n/**\n * Get all event slugs for static generation\n */\nexport async function getEventPaths(config: ServerConfig): Promise<{ params: { slug: string } }[]> {\n const { ok, data } = await apiFetch<{ offerings: SlugItem[] }>(\n config,\n `/public/commerce/offerings?type=event&status=active&fields=slug`\n )\n\n if (!ok) console.error('[site-kit/commerce] getEventPaths failed — 0 event pages will be prerendered')\n if (!data?.offerings) return []\n\n return data.offerings.map((item: SlugItem) => ({ params: { slug: item.slug } }))\n}\n\n/**\n * Get all offering slugs for static generation (any type)\n */\nexport async function getOfferingPaths(\n config: ServerConfig,\n type?: OfferingType | OfferingType[]\n): Promise<{ params: { slug: string } }[]> {\n let typeParam = ''\n if (type) {\n if (Array.isArray(type)) {\n typeParam = `&type=${type.join(',')}`\n } else {\n typeParam = `&type=${type}`\n }\n }\n \n const { ok, data } = await apiFetch<{ offerings: SlugItem[] }>(\n config,\n `/public/commerce/offerings?status=active&fields=slug${typeParam}`\n )\n\n if (!ok) console.error('[site-kit/commerce] getOfferingPaths failed — 0 pages will be prerendered')\n if (!data?.offerings) return []\n\n return data.offerings.map((item: SlugItem) => ({ params: { slug: item.slug } }))\n}\n\n// ============================================\n// Data Fetchers\n// ============================================\n\n/**\n * Fetch a single offering by slug (server-side)\n * \n * @example Next.js Pages Router\n * export async function getStaticProps({ params }) {\n * const product = await getOfferingBySlug(config, params.slug)\n * if (!product) return { notFound: true }\n * return { props: { product }, revalidate: 60 }\n * }\n */\nexport async function getOfferingBySlug(\n config: ServerConfig,\n slug: string\n): Promise<CommerceOffering | null> {\n return (await getOfferingBySlugResult(config, slug)).data\n}\n\n/**\n * As getOfferingBySlug, but distinguishes \"no such offering\" (ok: true, null)\n * from \"could not reach the API\" (ok: false, null). Use this before rendering a\n * 404 — a notFound() on a transient failure de-indexes a page that exists.\n */\nexport async function getOfferingBySlugResult(\n config: ServerConfig,\n slug: string\n): Promise<ServerResult<CommerceOffering | null>> {\n const { ok, data } = await apiFetch<{ offering: Record<string, unknown> }>(\n config,\n `/public/commerce/offerings/${slug}`\n )\n\n if (!data?.offering) return { ok, data: null }\n\n return { ok: true, data: transformOffering(data.offering) }\n}\n\n/**\n * Fetch all offerings of a type (server-side)\n */\nexport async function getOfferings(\n config: ServerConfig,\n options: {\n type?: OfferingType | OfferingType[]\n category?: string\n limit?: number\n status?: string\n } = {}\n): Promise<CommerceOffering[]> {\n return (await getOfferingsResult(config, options)).data\n}\n\n/**\n * As getOfferings, but distinguishes an empty catalogue from a failed request.\n * Branch on `ok` before rendering \"nothing available\" copy.\n */\nexport async function getOfferingsResult(\n config: ServerConfig,\n options: {\n type?: OfferingType | OfferingType[]\n category?: string\n limit?: number\n status?: string\n } = {}\n): Promise<ServerResult<CommerceOffering[]>> {\n const params = new URLSearchParams()\n params.set('status', options.status || 'active')\n \n if (options.type) {\n if (Array.isArray(options.type)) {\n params.set('type', options.type.join(','))\n } else {\n params.set('type', options.type)\n }\n }\n \n if (options.category) {\n params.set('category', options.category)\n }\n \n if (options.limit) {\n params.set('limit', options.limit.toString())\n }\n \n const { ok, data } = await apiFetch<{ offerings: Record<string, unknown>[] }>(\n config,\n `/public/commerce/offerings?${params.toString()}`\n )\n\n if (!data?.offerings) return { ok, data: [] }\n\n return { ok: true, data: data.offerings.map(transformOffering) }\n}\n\n/**\n * Fetch upcoming events (server-side), WITH schedules and category embedded.\n *\n * Uses POST /public/commerce/events — the same endpoint the client fetcher\n * uses — because the GET offerings list does NOT embed commerce_schedules,\n * which made this helper return events with empty `schedules[]` and forced\n * sites to hand-roll their own fetcher for any calendar UI (the Ludlow PTO\n * lesson). Falls back to the legacy offerings query if the events endpoint\n * is unavailable (older self-hosted API).\n */\nexport async function getUpcomingEvents(\n config: ServerConfig,\n options: { limit?: number; category?: string } = {}\n): Promise<CommerceOffering[]> {\n return (await getUpcomingEventsResult(config, options)).data\n}\n\n/**\n * As getUpcomingEvents, but tells you whether the list is empty because there is\n * nothing scheduled or because the API could not be reached.\n *\n * ALWAYS prefer this when the UI renders copy like \"no upcoming events\". Doing\n * otherwise is what put \"No public cruises currently on the schedule\" in front\n * of QCR's customers while three cruises were on sale — and, because the page\n * was statically revalidated, cached that claim for the whole revalidate window.\n */\nexport async function getUpcomingEventsResult(\n config: ServerConfig,\n options: { limit?: number; category?: string } = {}\n): Promise<ServerResult<CommerceOffering[]>> {\n const primary = await apiPost<{ events: Record<string, unknown>[] }>(\n config,\n '/public/commerce/events',\n {\n type: 'event',\n category: options.category,\n limit: options.limit,\n includePast: false,\n },\n )\n\n if (primary.ok && primary.data?.events) {\n return { ok: true, data: primary.data.events.map(transformOffering) }\n }\n\n // Legacy fallback: offerings list (no schedules embedded).\n const params = new URLSearchParams()\n params.set('type', 'event')\n params.set('status', 'active')\n params.set('upcoming', 'true')\n\n if (options.category) {\n params.set('category', options.category)\n }\n\n if (options.limit) {\n params.set('limit', options.limit.toString())\n }\n\n const legacy = await apiFetch<{ offerings: Record<string, unknown>[] }>(\n config,\n `/public/commerce/offerings?${params.toString()}`\n )\n\n if (legacy.ok && legacy.data?.offerings) {\n return { ok: true, data: legacy.data.offerings.map(transformOffering) }\n }\n\n // Nothing usable came back. It is only honestly \"empty\" if at least one call\n // actually reached the API; if both failed the caller must not claim empty.\n return { ok: primary.ok || legacy.ok, data: [] }\n}\n\n/**\n * Get next upcoming event (server-side)\n */\nexport async function getNextEvent(\n config: ServerConfig,\n category?: string\n): Promise<CommerceOffering | null> {\n return (await getNextEventResult(config, category)).data\n}\n\n/**\n * As getNextEvent, but distinguishes \"nothing scheduled\" from \"fetch failed\".\n */\nexport async function getNextEventResult(\n config: ServerConfig,\n category?: string\n): Promise<ServerResult<CommerceOffering | null>> {\n const { ok, data } = await getUpcomingEventsResult(config, { limit: 1, category })\n return { ok, data: data[0] || null }\n}\n\n// ============================================\n// Metadata Helpers\n// ============================================\n\n/**\n * Generate page metadata for an offering\n * \n * @example Next.js App Router\n * export async function generateMetadata({ params }) {\n * const product = await getOfferingBySlug(config, params.slug)\n * return generateOfferingMetadata(product, 'https://example.com')\n * }\n */\nexport function generateOfferingMetadata(\n offering: CommerceOffering | null,\n siteUrl: string\n): {\n title: string\n description: string\n openGraph: {\n title: string\n description: string\n images: string[]\n type: string\n url: string\n }\n} | null {\n if (!offering) return null\n \n const title = offering.seo_title || offering.name\n const description = offering.seo_description || offering.short_description || offering.description || ''\n const images = offering.featured_image_url ? [offering.featured_image_url] : []\n \n const typeMap: Record<OfferingType, string> = {\n product: 'product',\n service: 'website',\n event: 'website',\n class: 'website',\n subscription: 'product',\n }\n \n return {\n title,\n description,\n openGraph: {\n title,\n description,\n images,\n type: typeMap[offering.type] || 'website',\n url: `${siteUrl}/${offering.type}s/${offering.slug}`,\n },\n }\n}\n\n// ============================================\n// Export config builder\n// ============================================\n\n/**\n * Create a server config for API calls\n * \n * @param apiUrl - Portal API URL (e.g., 'https://api.sonor.io')\n * @param apiKey - Project API key\n * @param projectId - Project ID\n */\nexport function createServerConfig(\n apiUrl: string,\n apiKey: string,\n projectId: string\n): ServerConfig {\n return { apiUrl, apiKey, projectId }\n}\n"]}
@@ -1,16 +1,16 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
- var chunkIX5RAZWX_js = require('../chunk-IX5RAZWX.js');
5
- var chunk5OHHYGD6_js = require('../chunk-5OHHYGD6.js');
6
- require('../chunk-5JQLLF2T.js');
7
- require('../chunk-62ODUWUX.js');
4
+ var chunkBGBJSMXT_js = require('../chunk-BGBJSMXT.js');
5
+ var chunkIQLYFQKZ_js = require('../chunk-IQLYFQKZ.js');
6
+ require('../chunk-VCNPTLK2.js');
7
+ require('../chunk-GYAH4KFC.js');
8
8
  require('../chunk-KHPJ3CEL.js');
9
9
  require('../chunk-RC4WWQXR.js');
10
10
  require('../chunk-VJMPIRSD.js');
11
- require('../chunk-4D2G5ZNP.js');
12
- require('../chunk-FQ3JQV3M.js');
13
- require('../chunk-S65JSRWW.js');
11
+ require('../chunk-VIIGOCX4.js');
12
+ require('../chunk-CKPFETIO.js');
13
+ require('../chunk-YPEX4RDE.js');
14
14
  require('../chunk-SNAQBZPT.js');
15
15
 
16
16
  // src/engage/echo-config.ts
@@ -42,15 +42,15 @@ function getEchoWidgetProps(config = SONOR_PUBLIC_ECHO_DEFAULTS) {
42
42
 
43
43
  Object.defineProperty(exports, "DesignRenderer", {
44
44
  enumerable: true,
45
- get: function () { return chunkIX5RAZWX_js.DesignRenderer; }
45
+ get: function () { return chunkBGBJSMXT_js.DesignRenderer; }
46
46
  });
47
47
  Object.defineProperty(exports, "EngageWidget", {
48
48
  enumerable: true,
49
- get: function () { return chunkIX5RAZWX_js.EngageWidget; }
49
+ get: function () { return chunkBGBJSMXT_js.EngageWidget; }
50
50
  });
51
51
  Object.defineProperty(exports, "ChatWidget", {
52
52
  enumerable: true,
53
- get: function () { return chunk5OHHYGD6_js.ChatWidget; }
53
+ get: function () { return chunkIQLYFQKZ_js.ChatWidget; }
54
54
  });
55
55
  exports.SONOR_PUBLIC_ECHO_DEFAULTS = SONOR_PUBLIC_ECHO_DEFAULTS;
56
56
  exports.getEchoWidgetProps = getEchoWidgetProps;
@@ -1,14 +1,14 @@
1
1
  'use client';
2
- export { DesignRenderer, EngageWidget } from '../chunk-QI2H5KK6.mjs';
3
- export { ChatWidget } from '../chunk-SXPINIJG.mjs';
4
- import '../chunk-YVE65W47.mjs';
5
- import '../chunk-UFURXSAI.mjs';
2
+ export { DesignRenderer, EngageWidget } from '../chunk-FZISCIJV.mjs';
3
+ export { ChatWidget } from '../chunk-2XHEUWHV.mjs';
4
+ import '../chunk-BUDV7QHH.mjs';
5
+ import '../chunk-ZFZMN4LT.mjs';
6
6
  import '../chunk-TMVKRQGJ.mjs';
7
7
  import '../chunk-ZZQAGCSI.mjs';
8
8
  import '../chunk-6EDVCUAG.mjs';
9
- import '../chunk-4UHO6DPJ.mjs';
10
- import '../chunk-FDWDXMJL.mjs';
11
- import '../chunk-JPTJGLKS.mjs';
9
+ import '../chunk-ADOGOLYO.mjs';
10
+ import '../chunk-SXNKLHOX.mjs';
11
+ import '../chunk-4SQ6PROR.mjs';
12
12
  import '../chunk-BEXTDHO4.mjs';
13
13
 
14
14
  // src/engage/echo-config.ts