@vtex/faststore 0.0.6 → 0.0.8

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 (488) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/dist/actions.json +152 -1
  3. package/dist/catalog.json +2541 -1278
  4. package/dist/cli/UnreachableCaseError-Crd168Vu.js +10 -0
  5. package/dist/cli/UnreachableCaseError-Crd168Vu.js.map +1 -0
  6. package/dist/cli/cacheControl-CEojH_TM.js +158 -0
  7. package/dist/cli/cacheControl-CEojH_TM.js.map +1 -0
  8. package/dist/cli/cachePolicy-m2vASMRW.js +26 -0
  9. package/dist/cli/cachePolicy-m2vASMRW.js.map +1 -0
  10. package/dist/cli/computeCoreSchemaHash-DPJZj-qJ.js +10 -0
  11. package/dist/cli/computeCoreSchemaHash-DPJZj-qJ.js.map +1 -0
  12. package/dist/cli/default-BCQHcwbL.js +253 -0
  13. package/dist/cli/default-BCQHcwbL.js.map +1 -0
  14. package/dist/cli/disableSuggestions-yO9UqiWn.js +21 -0
  15. package/dist/cli/disableSuggestions-yO9UqiWn.js.map +1 -0
  16. package/dist/cli/dist-C1JhdHfo.js +2001 -0
  17. package/dist/cli/dist-C1JhdHfo.js.map +1 -0
  18. package/dist/cli/graphql-DG9thCDG.js +14589 -0
  19. package/dist/cli/graphql-DG9thCDG.js.map +1 -0
  20. package/dist/cli/index.d.ts +799 -0
  21. package/dist/cli/index.js +36459 -0
  22. package/dist/cli/index.js.map +1 -0
  23. package/dist/cli/index.min-BsIHDfFK.js +613 -0
  24. package/dist/cli/index.min-BsIHDfFK.js.map +1 -0
  25. package/dist/cli/inlineTrace-uLgOJ3TH.js +52 -0
  26. package/dist/cli/inlineTrace-uLgOJ3TH.js.map +1 -0
  27. package/dist/cli/internalPlugin-DnuwN4_4.js +11 -0
  28. package/dist/cli/internalPlugin-DnuwN4_4.js.map +1 -0
  29. package/dist/cli/packageVersion-DQAnIEil.js +6 -0
  30. package/dist/cli/packageVersion-DQAnIEil.js.map +1 -0
  31. package/dist/cli/rolldown-runtime-BMI-E3GI.js +44 -0
  32. package/dist/cli/schemaIsSubgraph-DoGh3OYj.js +17 -0
  33. package/dist/cli/schemaIsSubgraph-DoGh3OYj.js.map +1 -0
  34. package/dist/cli/schemaReporting-K-me-3-v.js +199 -0
  35. package/dist/cli/schemaReporting-K-me-3-v.js.map +1 -0
  36. package/dist/cli/server-runtime.js +49879 -0
  37. package/dist/cli/server-runtime.js.map +1 -0
  38. package/dist/cli/traceTreeBuilder-BIcV2U7b.js +9118 -0
  39. package/dist/cli/traceTreeBuilder-BIcV2U7b.js.map +1 -0
  40. package/dist/cli/usageReporting-Bpl8vSZX.js +10201 -0
  41. package/dist/cli/usageReporting-Bpl8vSZX.js.map +1 -0
  42. package/dist/cli/v4-CBhFvC49.js +42 -0
  43. package/dist/cli/v4-CBhFvC49.js.map +1 -0
  44. package/dist/client.d.ts +5 -1
  45. package/dist/client.js +5 -1
  46. package/dist/components.d.ts +38 -9
  47. package/dist/components.js +42 -13
  48. package/dist/content-types.json +616 -0
  49. package/dist/core/dist/define-component.d.ts +43 -1
  50. package/dist/core/dist/index.d.ts +2 -2
  51. package/dist/docs/account-access-gate.md +4 -4
  52. package/dist/docs/account-address-book.md +519 -0
  53. package/dist/docs/account-entry-point.md +1 -1
  54. package/dist/docs/account-menu-identity.md +160 -0
  55. package/dist/docs/account-menu.md +107 -24
  56. package/dist/docs/alert-dialog.md +2 -2
  57. package/dist/docs/avatar.md +103 -0
  58. package/dist/docs/billing-address.md +17 -9
  59. package/dist/docs/card-entry.md +37 -6
  60. package/dist/docs/cardholder-document.md +16 -14
  61. package/dist/docs/cart-item.md +1 -1
  62. package/dist/docs/cart.md +8 -8
  63. package/dist/docs/client-profile-fieldset.md +7 -5
  64. package/dist/docs/copy-text.md +84 -0
  65. package/dist/docs/document-field.md +8 -8
  66. package/dist/docs/footer-link-group.md +2 -2
  67. package/dist/docs/header-root.md +1 -1
  68. package/dist/docs/incentives.md +2 -2
  69. package/dist/docs/logo.md +5 -5
  70. package/dist/docs/my-account-profile.md +5 -5
  71. package/dist/docs/navigation-link-list.md +2 -2
  72. package/dist/docs/navigation-link.md +3 -3
  73. package/dist/docs/order-cancel.md +52 -0
  74. package/dist/docs/order-delivery-address.md +63 -0
  75. package/dist/docs/order-delivery-group.md +83 -0
  76. package/dist/docs/order-details-back.md +41 -0
  77. package/dist/docs/order-details-header.md +48 -0
  78. package/dist/docs/order-list-row.md +46 -0
  79. package/dist/docs/order-list-search.md +101 -0
  80. package/dist/docs/order-list-views.md +97 -0
  81. package/dist/docs/order-list.md +94 -0
  82. package/dist/docs/order-payment.md +94 -0
  83. package/dist/docs/order-placed-date.md +49 -0
  84. package/dist/docs/order-placed-payment-summary.md +13 -15
  85. package/dist/docs/order-placed.md +225 -0
  86. package/dist/docs/order-progress.md +88 -0
  87. package/dist/docs/order-status-badge.md +91 -0
  88. package/dist/docs/order-summary.md +53 -0
  89. package/dist/docs/payment-method-badges.md +14 -3
  90. package/dist/docs/place-order.md +13 -9
  91. package/dist/docs/product-shelf.md +20 -13
  92. package/dist/docs/saved-cards.md +2 -2
  93. package/dist/docs/select.md +19 -6
  94. package/dist/docs/session.md +1 -1
  95. package/dist/docs/shipping-address.md +1 -1
  96. package/dist/docs/sign-in-redirect.md +6 -6
  97. package/dist/docs/toast.md +7 -3
  98. package/dist/index.d.ts +7 -3
  99. package/dist/index.js +2 -1
  100. package/dist/internal/client.d.ts +6 -1
  101. package/dist/internal/client.js +8 -3
  102. package/dist/internal/content.d.ts +2 -2
  103. package/dist/internal/server.d.ts +8 -1
  104. package/dist/internal/server.js +8 -1
  105. package/dist/matter/account-access-gate/account-access-gate.d.ts +2 -1
  106. package/dist/matter/account-access-gate/account-access-gate.js +19 -16
  107. package/dist/matter/account-access-gate/account-access-gate.js.map +1 -1
  108. package/dist/matter/account-address-book/account-address-book-delete.d.ts +60 -0
  109. package/dist/matter/account-address-book/account-address-book-delete.js +184 -0
  110. package/dist/matter/account-address-book/account-address-book-delete.js.map +1 -0
  111. package/dist/matter/account-address-book/account-address-book-deliverability.d.ts +19 -0
  112. package/dist/matter/account-address-book/account-address-book-deliverability.js +50 -0
  113. package/dist/matter/account-address-book/account-address-book-deliverability.js.map +1 -0
  114. package/dist/matter/account-address-book/account-address-book-form-parts.d.ts +42 -0
  115. package/dist/matter/account-address-book/account-address-book-form-parts.js +146 -0
  116. package/dist/matter/account-address-book/account-address-book-form-parts.js.map +1 -0
  117. package/dist/matter/account-address-book/account-address-book-profile-notice.d.ts +25 -0
  118. package/dist/matter/account-address-book/account-address-book-profile-notice.js +72 -0
  119. package/dist/matter/account-address-book/account-address-book-profile-notice.js.map +1 -0
  120. package/dist/matter/account-address-book/account-address-book.d.ts +120 -0
  121. package/dist/matter/account-address-book/account-address-book.js +469 -0
  122. package/dist/matter/account-address-book/account-address-book.js.map +1 -0
  123. package/dist/matter/account-address-book/account-address-country-switch.js +59 -0
  124. package/dist/matter/account-address-book/account-address-country-switch.js.map +1 -0
  125. package/dist/matter/account-address-book/account-address-country.d.ts +36 -0
  126. package/dist/matter/account-address-book/account-address-country.js +151 -0
  127. package/dist/matter/account-address-book/account-address-country.js.map +1 -0
  128. package/dist/matter/account-address-book/account-address-first-line.js +11 -0
  129. package/dist/matter/account-address-book/account-address-first-line.js.map +1 -0
  130. package/dist/matter/account-address-book/account-address-form-context.d.ts +55 -0
  131. package/dist/matter/account-address-book/account-address-form-context.js +14 -0
  132. package/dist/matter/account-address-book/account-address-form-context.js.map +1 -0
  133. package/dist/matter/account-address-book/account-address-form.d.ts +85 -0
  134. package/dist/matter/account-address-book/account-address-form.js +946 -0
  135. package/dist/matter/account-address-book/account-address-form.js.map +1 -0
  136. package/dist/matter/account-address-book/account-address-postal-autofill.js +189 -0
  137. package/dist/matter/account-address-book/account-address-postal-autofill.js.map +1 -0
  138. package/dist/matter/account-address-book/account-address-record.d.ts +26 -0
  139. package/dist/matter/account-address-book/account-address-record.js +138 -0
  140. package/dist/matter/account-address-book/account-address-record.js.map +1 -0
  141. package/dist/matter/account-entry-point/account-entry-point.js +11 -11
  142. package/dist/matter/account-entry-point/account-entry-point.js.map +1 -1
  143. package/dist/matter/account-menu/account-menu.d.ts +77 -3
  144. package/dist/matter/account-menu/account-menu.js +252 -19
  145. package/dist/matter/account-menu/account-menu.js.map +1 -1
  146. package/dist/matter/account-menu/sign-out-failure.js +50 -0
  147. package/dist/matter/account-menu/sign-out-failure.js.map +1 -0
  148. package/dist/matter/account-menu-identity/account-menu-identity.d.ts +21 -0
  149. package/dist/matter/account-menu-identity/account-menu-identity.js +88 -0
  150. package/dist/matter/account-menu-identity/account-menu-identity.js.map +1 -0
  151. package/dist/matter/billing-address/billing-address.d.ts +30 -17
  152. package/dist/matter/billing-address/billing-address.js +128 -33
  153. package/dist/matter/billing-address/billing-address.js.map +1 -1
  154. package/dist/matter/breadcrumb/breadcrumb.js +2 -2
  155. package/dist/matter/breadcrumb/breadcrumb.js.map +1 -1
  156. package/dist/matter/card-entry/card-capture.js +4 -3
  157. package/dist/matter/card-entry/card-capture.js.map +1 -1
  158. package/dist/matter/card-entry/card-entry.d.ts +5 -3
  159. package/dist/matter/card-entry/card-entry.js +78 -27
  160. package/dist/matter/card-entry/card-entry.js.map +1 -1
  161. package/dist/matter/cardholder-document/cardholder-document.d.ts +10 -9
  162. package/dist/matter/cardholder-document/cardholder-document.js +36 -16
  163. package/dist/matter/cardholder-document/cardholder-document.js.map +1 -1
  164. package/dist/matter/cart/cart.d.ts +7 -2
  165. package/dist/matter/cart/cart.js +29 -26
  166. package/dist/matter/cart/cart.js.map +1 -1
  167. package/dist/matter/cart-item/cart-item.js +2 -2
  168. package/dist/matter/cart-item/cart-item.js.map +1 -1
  169. package/dist/matter/client-preferences/client-preferences-fieldset.js +2 -2
  170. package/dist/matter/client-profile-form/client-profile-fieldset.js +9 -6
  171. package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -1
  172. package/dist/matter/identity-edit/identity-edit.js +3 -3
  173. package/dist/matter/identity-edit/identity-edit.js.map +1 -1
  174. package/dist/matter/identity-summary/identity-summary.js +4 -3
  175. package/dist/matter/identity-summary/identity-summary.js.map +1 -1
  176. package/dist/matter/my-account-profile/my-account-profile.d.ts +6 -3
  177. package/dist/matter/my-account-profile/my-account-profile.js +6 -5
  178. package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
  179. package/dist/matter/order-cancel/order-cancel.d.ts +110 -0
  180. package/dist/matter/order-cancel/order-cancel.js +344 -0
  181. package/dist/matter/order-cancel/order-cancel.js.map +1 -0
  182. package/dist/matter/order-delivery-address/order-delivery-address.d.ts +73 -0
  183. package/dist/matter/order-delivery-address/order-delivery-address.js +172 -0
  184. package/dist/matter/order-delivery-address/order-delivery-address.js.map +1 -0
  185. package/dist/matter/order-delivery-group/order-delivery-group.d.ts +128 -0
  186. package/dist/matter/order-delivery-group/order-delivery-group.js +408 -0
  187. package/dist/matter/order-delivery-group/order-delivery-group.js.map +1 -0
  188. package/dist/matter/order-details-header/order-details-header.d.ts +104 -0
  189. package/dist/matter/order-details-header/order-details-header.js +190 -0
  190. package/dist/matter/order-details-header/order-details-header.js.map +1 -0
  191. package/dist/matter/order-list/customer-orders-filter.d.ts +12 -0
  192. package/dist/matter/order-list/customer-orders-filter.js +30 -0
  193. package/dist/matter/order-list/customer-orders-filter.js.map +1 -0
  194. package/dist/matter/order-list/order-list.d.ts +121 -0
  195. package/dist/matter/order-list/order-list.js +395 -0
  196. package/dist/matter/order-list/order-list.js.map +1 -0
  197. package/dist/matter/order-list-row/order-list-row.d.ts +44 -0
  198. package/dist/matter/order-list-row/order-list-row.js +127 -0
  199. package/dist/matter/order-list-row/order-list-row.js.map +1 -0
  200. package/dist/matter/order-list-search/order-list-search.d.ts +72 -0
  201. package/dist/matter/order-list-search/order-list-search.js +296 -0
  202. package/dist/matter/order-list-search/order-list-search.js.map +1 -0
  203. package/dist/matter/order-list-views/order-list-views.d.ts +65 -0
  204. package/dist/matter/order-list-views/order-list-views.js +154 -0
  205. package/dist/matter/order-list-views/order-list-views.js.map +1 -0
  206. package/dist/matter/order-payment/order-payment.d.ts +83 -0
  207. package/dist/matter/order-payment/order-payment.js +258 -0
  208. package/dist/matter/order-payment/order-payment.js.map +1 -0
  209. package/dist/matter/order-placed/order-placed-deliveries.d.ts +145 -0
  210. package/dist/matter/order-placed/order-placed-deliveries.js +380 -0
  211. package/dist/matter/order-placed/order-placed-deliveries.js.map +1 -0
  212. package/dist/matter/order-placed/order-placed.d.ts +122 -0
  213. package/dist/matter/order-placed/order-placed.js +452 -0
  214. package/dist/matter/order-placed/order-placed.js.map +1 -0
  215. package/dist/matter/order-placed-date/order-placed-date.d.ts +45 -0
  216. package/dist/matter/order-placed-date/order-placed-date.js +92 -0
  217. package/dist/matter/order-placed-date/order-placed-date.js.map +1 -0
  218. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +5 -8
  219. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +29 -81
  220. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -1
  221. package/dist/matter/order-progress/order-progress.d.ts +85 -0
  222. package/dist/matter/order-progress/order-progress.js +203 -0
  223. package/dist/matter/order-progress/order-progress.js.map +1 -0
  224. package/dist/matter/order-summary/order-summary.d.ts +81 -0
  225. package/dist/matter/order-summary/order-summary.js +192 -0
  226. package/dist/matter/order-summary/order-summary.js.map +1 -0
  227. package/dist/matter/payment/cardholder-document-rule.d.ts +35 -0
  228. package/dist/matter/payment/cardholder-document-rule.js +35 -0
  229. package/dist/matter/payment/cardholder-document-rule.js.map +1 -0
  230. package/dist/matter/payment/payment-card-capture.js +38 -23
  231. package/dist/matter/payment/payment-card-capture.js.map +1 -1
  232. package/dist/matter/payment/payment-controller.js +12 -0
  233. package/dist/matter/payment/payment-controller.js.map +1 -1
  234. package/dist/matter/payment/payment-method-badges.js +4 -2
  235. package/dist/matter/payment/payment-method-badges.js.map +1 -1
  236. package/dist/matter/payment/payment.js +12 -0
  237. package/dist/matter/payment/payment.js.map +1 -1
  238. package/dist/matter/payment/use-payment-method-selection.d.ts +39 -0
  239. package/dist/matter/payment/use-payment-method-selection.js +30 -2
  240. package/dist/matter/payment/use-payment-method-selection.js.map +1 -1
  241. package/dist/matter/payment/use-payment-submit-gate.d.ts +16 -2
  242. package/dist/matter/payment/use-payment-submit-gate.js +42 -14
  243. package/dist/matter/payment/use-payment-submit-gate.js.map +1 -1
  244. package/dist/matter/place-order/place-order.d.ts +12 -7
  245. package/dist/matter/place-order/place-order.js +126 -103
  246. package/dist/matter/place-order/place-order.js.map +1 -1
  247. package/dist/matter/pricing/pricing.js +5 -5
  248. package/dist/matter/pricing/pricing.js.map +1 -1
  249. package/dist/matter/product-shelf/product-shelf.d.ts +61 -31
  250. package/dist/matter/product-shelf/product-shelf.js +157 -57
  251. package/dist/matter/product-shelf/product-shelf.js.map +1 -1
  252. package/dist/matter/region/region.js +1 -1
  253. package/dist/matter/saved-cards/saved-cards.js +27 -15
  254. package/dist/matter/saved-cards/saved-cards.js.map +1 -1
  255. package/dist/matter/session/session.js +2 -2
  256. package/dist/matter/session/session.js.map +1 -1
  257. package/dist/matter/shipping-address/country-descriptors.d.ts +1 -0
  258. package/dist/matter/shipping-address/country-descriptors.js +23 -1
  259. package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
  260. package/dist/matter/shipping-address/shipping-address.js +63 -15
  261. package/dist/matter/shipping-address/shipping-address.js.map +1 -1
  262. package/dist/matter/shipping-simulator/shipping-simulator.d.ts +1 -1
  263. package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
  264. package/dist/matter/sign-in-redirect/sign-in-redirect.js +48 -10
  265. package/dist/matter/sign-in-redirect/sign-in-redirect.js.map +1 -1
  266. package/dist/matter/variant-selector/variant-selector.js +2 -2
  267. package/dist/matter/variant-selector/variant-selector.js.map +1 -1
  268. package/dist/particles/address-summary/address-summary.js +2 -2
  269. package/dist/particles/address-summary/address-summary.js.map +1 -1
  270. package/dist/particles/alert-dialog/alert-dialog.d.ts +5 -1
  271. package/dist/particles/alert-dialog/alert-dialog.js +5 -2
  272. package/dist/particles/alert-dialog/alert-dialog.js.map +1 -1
  273. package/dist/particles/avatar/avatar.d.ts +17 -0
  274. package/dist/particles/avatar/avatar.js +65 -0
  275. package/dist/particles/avatar/avatar.js.map +1 -0
  276. package/dist/particles/copy-text/copy-text.d.ts +28 -0
  277. package/dist/particles/copy-text/copy-text.js +140 -0
  278. package/dist/particles/copy-text/copy-text.js.map +1 -0
  279. package/dist/particles/document-field/document-field.d.ts +34 -5
  280. package/dist/particles/document-field/document-field.js +115 -19
  281. package/dist/particles/document-field/document-field.js.map +1 -1
  282. package/dist/particles/field/field.js +1 -1
  283. package/dist/particles/field/field.js.map +1 -1
  284. package/dist/particles/footer-link-group/footer-link-group.d.ts +6 -2
  285. package/dist/particles/footer-link-group/footer-link-group.js +18 -7
  286. package/dist/particles/footer-link-group/footer-link-group.js.map +1 -1
  287. package/dist/particles/incentives/incentives.d.ts +6 -2
  288. package/dist/particles/incentives/incentives.js +7 -2
  289. package/dist/particles/incentives/incentives.js.map +1 -1
  290. package/dist/particles/logo/logo.d.ts +3 -2
  291. package/dist/particles/logo/logo.js +20 -8
  292. package/dist/particles/logo/logo.js.map +1 -1
  293. package/dist/particles/navigation-link/navigation-link.d.ts +2 -3
  294. package/dist/particles/navigation-link/navigation-link.js +4 -4
  295. package/dist/particles/navigation-link/navigation-link.js.map +1 -1
  296. package/dist/particles/navigation-link-list/navigation-link-list.d.ts +6 -2
  297. package/dist/particles/navigation-link-list/navigation-link-list.js +18 -7
  298. package/dist/particles/navigation-link-list/navigation-link-list.js.map +1 -1
  299. package/dist/particles/order-details-back/order-details-back.d.ts +28 -0
  300. package/dist/particles/order-details-back/order-details-back.js +52 -0
  301. package/dist/particles/order-details-back/order-details-back.js.map +1 -0
  302. package/dist/particles/order-status-badge/order-status-badge.d.ts +19 -0
  303. package/dist/particles/order-status-badge/order-status-badge.js +50 -0
  304. package/dist/particles/order-status-badge/order-status-badge.js.map +1 -0
  305. package/dist/particles/phone-field/phone-field.js +2 -2
  306. package/dist/particles/phone-field/phone-field.js.map +1 -1
  307. package/dist/particles/select/select.js +6 -6
  308. package/dist/particles/select/select.js.map +1 -1
  309. package/dist/particles/toast/toast.d.ts +1 -1
  310. package/dist/particles/toast/toast.js +32 -6
  311. package/dist/particles/toast/toast.js.map +1 -1
  312. package/dist/schema.graphql +1079 -54
  313. package/dist/sdk/actions/address-action-provider.d.ts +16 -0
  314. package/dist/sdk/actions/address-action-provider.js +20 -0
  315. package/dist/sdk/actions/address-action-provider.js.map +1 -0
  316. package/dist/sdk/actions/analytics-action-provider.js +1 -1
  317. package/dist/sdk/actions/index.d.ts +3 -1
  318. package/dist/sdk/actions/index.js +3 -1
  319. package/dist/sdk/actions/order-action-provider.d.ts +12 -0
  320. package/dist/sdk/actions/order-action-provider.js +16 -0
  321. package/dist/sdk/actions/order-action-provider.js.map +1 -0
  322. package/dist/sdk/actions/types.d.ts +6 -2
  323. package/dist/sdk/address/address-fields.js +38 -0
  324. package/dist/sdk/address/address-fields.js.map +1 -0
  325. package/dist/sdk/address/checkout-readiness.js +17 -0
  326. package/dist/sdk/address/checkout-readiness.js.map +1 -0
  327. package/dist/sdk/address/constants.js +60 -0
  328. package/dist/sdk/address/constants.js.map +1 -0
  329. package/dist/sdk/address/country-display.js +19 -0
  330. package/dist/sdk/address/country-display.js.map +1 -0
  331. package/dist/sdk/address/country-rules.js +30 -0
  332. package/dist/sdk/address/country-rules.js.map +1 -0
  333. package/dist/sdk/address/format-address.js +18 -0
  334. package/dist/sdk/address/format-address.js.map +1 -0
  335. package/dist/sdk/address/iso-countries.js +262 -0
  336. package/dist/sdk/address/iso-countries.js.map +1 -0
  337. package/dist/sdk/address/layouts.js +56 -0
  338. package/dist/sdk/address/layouts.js.map +1 -0
  339. package/dist/sdk/address/messages.js +97 -0
  340. package/dist/sdk/address/messages.js.map +1 -0
  341. package/dist/sdk/address/operations.d.ts +54 -0
  342. package/dist/sdk/address/operations.js +83 -0
  343. package/dist/sdk/address/operations.js.map +1 -0
  344. package/dist/sdk/address/postal-code-lookup-operations.js +23 -0
  345. package/dist/sdk/address/postal-code-lookup-operations.js.map +1 -0
  346. package/dist/sdk/address/postal-code.js +11 -0
  347. package/dist/sdk/address/postal-code.js.map +1 -0
  348. package/dist/sdk/address/schema.js +14 -0
  349. package/dist/sdk/address/schema.js.map +1 -0
  350. package/dist/sdk/address/server-fields.js +25 -0
  351. package/dist/sdk/address/server-fields.js.map +1 -0
  352. package/dist/sdk/address/switch-country.js +57 -0
  353. package/dist/sdk/address/switch-country.js.map +1 -0
  354. package/dist/sdk/address/types.d.ts +26 -0
  355. package/dist/sdk/address/use-address-actions.d.ts +29 -0
  356. package/dist/sdk/address/use-address-actions.js +247 -0
  357. package/dist/sdk/address/use-address-actions.js.map +1 -0
  358. package/dist/sdk/address/use-address-by-postal-code.js +57 -0
  359. package/dist/sdk/address/use-address-by-postal-code.js.map +1 -0
  360. package/dist/sdk/address/validate-address.js +62 -0
  361. package/dist/sdk/address/validate-address.js.map +1 -0
  362. package/dist/sdk/analytics/use-analytics-actions.js +2 -1
  363. package/dist/sdk/analytics/use-analytics-actions.js.map +1 -1
  364. package/dist/sdk/cache/storefront-cache.js +29 -4
  365. package/dist/sdk/cache/storefront-cache.js.map +1 -1
  366. package/dist/sdk/cart/use-cart-actions.js +9 -8
  367. package/dist/sdk/cart/use-cart-actions.js.map +1 -1
  368. package/dist/sdk/cart/use-cart-validate-on-open.js +2 -1
  369. package/dist/sdk/cart/use-cart-validate-on-open.js.map +1 -1
  370. package/dist/sdk/client/fetch-page-data.d.ts +11 -0
  371. package/dist/sdk/client/fetch-page-data.js +12 -1
  372. package/dist/sdk/client/fetch-page-data.js.map +1 -1
  373. package/dist/sdk/content/create-content-runtime.d.ts +22 -1
  374. package/dist/sdk/content/create-content-runtime.js +90 -9
  375. package/dist/sdk/content/create-content-runtime.js.map +1 -1
  376. package/dist/sdk/data/empty-content-value.js +16 -0
  377. package/dist/sdk/data/empty-content-value.js.map +1 -0
  378. package/dist/sdk/data/failed-fields.js +23 -0
  379. package/dist/sdk/data/failed-fields.js.map +1 -0
  380. package/dist/sdk/data/hot-data-provider.js +2 -1
  381. package/dist/sdk/data/hot-data-provider.js.map +1 -1
  382. package/dist/sdk/data/index.js +1 -1
  383. package/dist/sdk/data/page-reactive-data-root.d.ts +45 -7
  384. package/dist/sdk/data/page-reactive-data-root.js +88 -12
  385. package/dist/sdk/data/page-reactive-data-root.js.map +1 -1
  386. package/dist/sdk/data/variables-from-content.d.ts +43 -0
  387. package/dist/sdk/data/variables-from-content.js +57 -0
  388. package/dist/sdk/data/variables-from-content.js.map +1 -0
  389. package/dist/sdk/generated/fragment-types.js +2 -0
  390. package/dist/sdk/generated/fragment-types.js.map +1 -1
  391. package/dist/sdk/generated/gql.js +4 -1
  392. package/dist/sdk/generated/gql.js.map +1 -1
  393. package/dist/sdk/generated/graphql.d.ts +8 -0
  394. package/dist/sdk/generated/graphql.js +296 -1
  395. package/dist/sdk/generated/graphql.js.map +1 -1
  396. package/dist/sdk/i18n/messages/en-US.js +138 -1
  397. package/dist/sdk/i18n/messages/en-US.json +138 -1
  398. package/dist/sdk/i18n/messages/en-US.json.d.ts +137 -0
  399. package/dist/sdk/i18n/messages/es-ES.js +138 -1
  400. package/dist/sdk/i18n/messages/es-ES.json +138 -1
  401. package/dist/sdk/i18n/messages/pt-BR.js +138 -1
  402. package/dist/sdk/i18n/messages/pt-BR.json +138 -1
  403. package/dist/sdk/identity/use-identity-actions.js +5 -4
  404. package/dist/sdk/identity/use-identity-actions.js.map +1 -1
  405. package/dist/sdk/navigation/current-page.js +30 -0
  406. package/dist/sdk/navigation/current-page.js.map +1 -0
  407. package/dist/sdk/navigation/not-found-log.d.ts +28 -0
  408. package/dist/sdk/navigation/not-found-log.js +133 -0
  409. package/dist/sdk/navigation/not-found-log.js.map +1 -0
  410. package/dist/sdk/navigation/path.js +25 -0
  411. package/dist/sdk/navigation/path.js.map +1 -0
  412. package/dist/sdk/navigation/redirect.d.ts +11 -0
  413. package/dist/sdk/navigation/redirect.js +22 -0
  414. package/dist/sdk/navigation/redirect.js.map +1 -0
  415. package/dist/sdk/navigation/report.js +70 -0
  416. package/dist/sdk/navigation/report.js.map +1 -0
  417. package/dist/sdk/navigation/resolve-storefront-href.d.ts +12 -0
  418. package/dist/sdk/navigation/resolve-storefront-href.js +214 -0
  419. package/dist/sdk/navigation/resolve-storefront-href.js.map +1 -0
  420. package/dist/sdk/navigation/route-identity-provider.d.ts +11 -0
  421. package/dist/sdk/navigation/route-identity-provider.js +20 -0
  422. package/dist/sdk/navigation/route-identity-provider.js.map +1 -0
  423. package/dist/sdk/navigation/route-map-provider.d.ts +19 -0
  424. package/dist/sdk/navigation/route-map-provider.js +29 -0
  425. package/dist/sdk/navigation/route-map-provider.js.map +1 -0
  426. package/dist/sdk/navigation/storefront-link.d.ts +16 -0
  427. package/dist/sdk/navigation/storefront-link.js +66 -0
  428. package/dist/sdk/navigation/storefront-link.js.map +1 -0
  429. package/dist/sdk/navigation/types.d.ts +45 -0
  430. package/dist/sdk/navigation/use-storefront-navigation.d.ts +26 -0
  431. package/dist/sdk/navigation/use-storefront-navigation.js +73 -0
  432. package/dist/sdk/navigation/use-storefront-navigation.js.map +1 -0
  433. package/dist/sdk/navigation/use-storefront-route.d.ts +26 -0
  434. package/dist/sdk/navigation/use-storefront-route.js +87 -0
  435. package/dist/sdk/navigation/use-storefront-route.js.map +1 -0
  436. package/dist/sdk/navigation/use-storefront-search-params.d.ts +11 -0
  437. package/dist/sdk/navigation/use-storefront-search-params.js +16 -0
  438. package/dist/sdk/navigation/use-storefront-search-params.js.map +1 -0
  439. package/dist/sdk/orders/completion-badge.d.ts +8 -0
  440. package/dist/sdk/orders/completion-badge.js +47 -0
  441. package/dist/sdk/orders/completion-badge.js.map +1 -0
  442. package/dist/sdk/orders/order-operations.d.ts +19 -0
  443. package/dist/sdk/orders/order-operations.js +21 -0
  444. package/dist/sdk/orders/order-operations.js.map +1 -0
  445. package/dist/sdk/orders/use-order-actions.d.ts +23 -0
  446. package/dist/sdk/orders/use-order-actions.js +62 -0
  447. package/dist/sdk/orders/use-order-actions.js.map +1 -0
  448. package/dist/sdk/payment/gateway-origin.js +26 -13
  449. package/dist/sdk/payment/gateway-origin.js.map +1 -1
  450. package/dist/sdk/payment/gateway.d.ts +14 -2
  451. package/dist/sdk/payment/gateway.js +32 -14
  452. package/dist/sdk/payment/gateway.js.map +1 -1
  453. package/dist/sdk/payment/operations.js +11 -0
  454. package/dist/sdk/payment/operations.js.map +1 -1
  455. package/dist/sdk/payment/submission-registry.d.ts +2 -1
  456. package/dist/sdk/payment/submission-registry.js +12 -8
  457. package/dist/sdk/payment/submission-registry.js.map +1 -1
  458. package/dist/sdk/payment/use-payment-actions.d.ts +5 -3
  459. package/dist/sdk/payment/use-payment-actions.js +6 -3
  460. package/dist/sdk/payment/use-payment-actions.js.map +1 -1
  461. package/dist/sdk/payment/use-place-order.d.ts +6 -3
  462. package/dist/sdk/payment/use-place-order.js +334 -144
  463. package/dist/sdk/payment/use-place-order.js.map +1 -1
  464. package/dist/sdk/region/use-region-actions.js +2 -1
  465. package/dist/sdk/region/use-region-actions.js.map +1 -1
  466. package/dist/sdk/seo/page-alternates.d.ts +55 -0
  467. package/dist/sdk/seo/page-alternates.js +132 -0
  468. package/dist/sdk/seo/page-alternates.js.map +1 -0
  469. package/dist/sdk/session/read-server-session.d.ts +21 -0
  470. package/dist/sdk/session/read-server-session.js +61 -0
  471. package/dist/sdk/session/read-server-session.js.map +1 -0
  472. package/dist/sdk/session/use-session-actions.js +5 -4
  473. package/dist/sdk/session/use-session-actions.js.map +1 -1
  474. package/dist/sdk/session/use-session-revalidation.js +2 -1
  475. package/dist/sdk/session/use-session-revalidation.js.map +1 -1
  476. package/dist/sdk/shipping/use-shipping-actions.js +3 -2
  477. package/dist/sdk/shipping/use-shipping-actions.js.map +1 -1
  478. package/dist/utils/own-value.js +19 -0
  479. package/dist/utils/own-value.js.map +1 -0
  480. package/dist/utils/safe-href.d.ts +11 -0
  481. package/dist/utils/safe-href.js +31 -22
  482. package/dist/utils/safe-href.js.map +1 -1
  483. package/package.json +17 -2
  484. package/skills/storefront-asset/SKILL.md +111 -0
  485. package/skills/storefront-asset/references/api-side.md +153 -0
  486. package/skills/storefront-asset/references/findings.md +40 -0
  487. package/skills/storefront-asset/references/ui-side.md +94 -0
  488. package/src/styles/tailwind.css +27 -0
@@ -1 +1 @@
1
- {"version":3,"file":"create-content-runtime.js","names":[],"sources":["../../../src/sdk/content/create-content-runtime.tsx"],"sourcesContent":["import { createHmac, timingSafeEqual } from 'node:crypto';\nimport type { ContentPropertySchema, ContentTypeDefinition } from '@vtex/fs-core';\nimport { cookies, draftMode } from 'next/headers';\nimport { connection } from 'next/server';\nimport { cache, type FC, type ReactNode } from 'react';\n\nexport type ContentRuntimeConfig = {\n account: string;\n storeId: string;\n /** Canonical per-component models, verbatim from the aggregated content catalog, keyed by id. */\n contentTypes: Record<string, ContentTypeDefinition>;\n};\n\n/** The coordinates of one composite read. `locale` arrives per call, so one build serves many. */\nexport type ReadContentSlicesArgs = {\n contentTypeId: string;\n entryId: string;\n locale: string;\n /** `element.id → per-component model id` for every content-backed element in the scope. */\n elements: Record<string, string>;\n};\n\n/**\n * The scope's slices, keyed by `element.id`. A key is absent when its element must be omitted — a\n * missing key, a shape violation, or a whole-entry failure.\n *\n * The value type is unchecked on purpose: the generated page forwards it to a component whose\n * `$content` is `ContentValue<that component's model>`, a type that exists only in the component\n * package, and an index signature cannot satisfy a model's required properties. Stated once here\n * rather than cast at every emitted prop; the read below validates every value against its model.\n */\nexport type ContentSlices = {\n // biome-ignore lint/suspicious/noExplicitAny: the model's type lives in the component package, not here.\n [elementId: string]: any;\n};\n\nexport type ContentEntryBoundaryProps = {\n /** This element's slice, read at the top of the scope. `undefined` omits the component. */\n value: unknown;\n children: ReactNode;\n};\n\nexport type ContentRuntime = {\n readContentSlices: (args: ReadContentSlicesArgs) => Promise<ContentSlices>;\n ContentEntryBoundary: FC<ContentEntryBoundaryProps>;\n};\n\n/** The signed preview context the Draft Mode route stores; re-verified here before use. */\ntype PreviewClaims = {\n account: string;\n storeId: string;\n branch: string;\n /** Millisecond epoch the token was minted / expires; a captured cookie stops working past `exp`. */\n iat: number;\n exp: number;\n};\n\n/**\n * Internal result taxonomy: a read either finds a validated composite blob or fails for a definite,\n * loggable reason. A transient upstream failure resolves `found` from the last-good store below when\n * that store holds the entry, and degrades to `unavailable` — omitting the affected components —\n * only when it does not.\n */\nexport type ContentEntryResult =\n | { status: 'found'; blob: Record<string, unknown> }\n | { status: 'not-found' }\n | { status: 'unavailable'; reason: string }\n | { status: 'invalid'; reason: string };\n\n/**\n * One published read's outcome. `staleReason` is present only when the fresh read failed and the\n * store covered it, and the result is then an ordinary `found`: no consumer has a fifth renderable\n * status to learn, and only the log line tells a served blob from a fresh one.\n */\ntype PublishedRead = { result: ContentEntryResult; staleReason?: string };\n\nconst ENTRY_REQUEST_TIMEOUT_MS = 5000;\n\n/**\n * Retention ceilings for the last-good store, both estimated rather than measured: no production\n * store exists yet to size a real composite against, and the example schemas in this repo are demo\n * content, not authored pages, so neither is evidence of real scale.\n *\n * `MAX_RETAINED_BLOB_BYTES` bounds one entry. Ordinary editorial text (a title, a body, an image URL,\n * an alt text) stays in the hundreds of bytes to a few kilobytes per element regardless of how many\n * elements a page composes — even the largest composed layout in this repo (103 elements) does not\n * read as megabytes if every element stays plain text. The realistic way to exceed this is a single\n * field holding embedded binary — a base64 image pasted into a text field instead of uploaded through\n * the media widget — which is exactly the shape this guards against, independent of element count.\n *\n * `MAX_RETAINED_TOTAL_BYTES` bounds the whole store, and is the one that actually matters: the\n * per-blob cap alone does not bound total memory, because it multiplies by key count, and key count is\n * not small — a large store's pages and layouts, times its locales, is plausibly a few hundred keys.\n * A few hundred keys at a few megabytes each already exceeds a typical serverless heap (512 MB–1 GB)\n * on the per-blob cap alone; ~64 MB is a small slice of that heap while still holding many ordinary\n * pages at once.\n *\n * Both numbers count serialized JSON text (`blobByteLength`), not retained heap: a parsed object\n * typically costs more than its JSON text — V8 pays per-property and per-string object overhead that\n * the text itself does not — so the real footprint at either ceiling is larger than the number on the\n * tin, plausibly by a small multiple. Both ceilings still leave headroom against a 512 MB–1 GB heap at\n * that multiple; sizing them as retained heap instead would need a runtime memory measurement this\n * module has no cheap way to take.\n *\n * Both numbers are round and generous on purpose, not derived. `content.retention.skipped` below is\n * the real calibration path: once a production store exists, its log volume says whether either\n * ceiling needs to move.\n */\nconst MAX_RETAINED_BLOB_BYTES = 1_000_000;\nconst MAX_RETAINED_TOTAL_BYTES = 64_000_000;\n\n/** Server-only preview credential, supplied at deployment time; never reaches the browser. */\nconst PREVIEW_TOKEN_ENV = 'CONTENT_PLATFORM_PREVIEW_TOKEN';\n/** Shared broker secret used to verify the signed preview context cookie. */\nconst PREVIEW_SECRET_ENV = 'CONTENT_PREVIEW_SECRET';\n/** The signed preview context cookie the Draft Mode route sets. */\nconst PREVIEW_COOKIE = '__fs_content_preview';\n\n/**\n * Creates the one server-side content read surface for a store: a `React.cache`-deduped\n * `readContentSlices` the generated page or layout awaits once at the top, and the\n * `ContentEntryBoundary` the compiler wraps around each content-backed element. One read per scope\n * yields every element's `$content` value keyed by `element.id`, so a page issues at most two Data\n * Plane reads (its own entry plus its layout's) regardless of content-backed component count. Draft\n * Mode switches the same read to Control Plane's preview endpoint through a server-only credential.\n */\nexport function createContentRuntime(config: ContentRuntimeConfig): ContentRuntime {\n // The blob of the most recent successful published read, per entry, so a failing Data Plane\n // renders the last content that worked instead of holes. Not `React.cache`, whose lifetime is the\n // request and so shorter than the failure it has to cover, and not module scope, which every\n // runtime in the process would share — two of them on the same coordinates would then serve each\n // other's content. The generated consumer holds the runtime in a module-scope `const`, so the\n // closure lives as long as the process either way.\n //\n // `contentTypeId` and `entryId` are compiler-emitted literals for the life of the deployment (a\n // dynamic route gets one entry, not one per slug), so today's key domain is small: pages plus\n // layouts, times locales. That is an invariant of the generated caller, not of this function —\n // nothing here stops a future one from passing a request-derived value instead, and the moment it\n // does, this map grows with traffic rather than with the store's page count.\n const lastGood = new Map<string, Record<string, unknown>>();\n let retainedBytes = 0;\n\n /**\n * Whether a successful read's blob replaces what `key` already holds, weighed against both\n * ceilings. Skipping never deletes: whatever `key` held before, if anything, is left exactly as it\n * was — only the update is declined, so a rare oversized read cannot erase a smaller last-good value\n * that a future failure could still use.\n */\n function retainBlob(\n key: string,\n blob: Record<string, unknown>,\n fields: { contentTypeId: string; entryId: string; locale: string },\n ): void {\n const size = blobByteLength(blob);\n if (size > MAX_RETAINED_BLOB_BYTES) {\n logEvent('warn', 'content.retention.skipped', { ...fields, bytes: size, reason: 'blob-too-large' });\n return;\n }\n const existing = lastGood.get(key);\n const existingSize = existing ? blobByteLength(existing) : 0;\n if (retainedBytes - existingSize + size > MAX_RETAINED_TOTAL_BYTES) {\n logEvent('warn', 'content.retention.skipped', { ...fields, bytes: size, reason: 'total-cap-reached' });\n return;\n }\n lastGood.set(key, blob);\n retainedBytes += size - existingSize;\n }\n\n /**\n * The published read plus the fallback that covers it. The upstream is always called and the store\n * is read only in the `catch`, which is what makes this a fallback and not a cache: a publish is\n * visible on the very next request. A success ordinarily overwrites the entry (see `retainBlob` for\n * the two cases where it does not), a `not-found` leaves it alone so unpublishing still unpublishes,\n * and nothing is ever removed or expired by age.\n *\n * Failure is structural — whatever leaves `readPublishedEntry` as a throw — rather than a status\n * table, so a 403 or a 429 serves the stored blob, and a failure mode added there later is covered\n * without touching this.\n */\n async function readPublished(contentTypeId: string, entryId: string, locale: string): Promise<PublishedRead> {\n // NUL appears in no content-type id, entry id, or BCP-47 tag, so two distinct triples cannot\n // collide. Keyed on the same three values the memoisation below uses, so the two layers never\n // disagree about what \"the same read\" is.\n const key = `${contentTypeId}\\u0000${entryId}\\u0000${locale}`;\n try {\n const result = await readPublishedEntry(config.account, config.storeId, locale, contentTypeId, entryId);\n if (result.status === 'found') {\n retainBlob(key, result.blob, { contentTypeId, entryId, locale });\n }\n return { result };\n } catch (error) {\n const reason = transientReason(error);\n const stored = lastGood.get(key);\n // Nothing stored: omit the affected components while the rest of the route renders.\n if (stored === undefined) return { result: { status: 'unavailable', reason } };\n return { result: { status: 'found', blob: stored }, staleReason: reason };\n }\n }\n\n // React.cache collapses same-entry awaits within one request: N boundaries reading the same\n // composite entry share one upstream read and one read-log line. That is per-request memoisation\n // only — nothing is reused across requests — so it is `readPublished` above, not this, that\n // carries a value across the failure it covers. Either way a read never throws a route error.\n const read = cache(async (contentTypeId: string, entryId: string, locale: string): Promise<ContentEntryResult> => {\n // Content is read per request, never at build time. Nothing else on a content-only page is\n // request-dependent — `draftMode()` prerenders as disabled and an uncached `fetch` does not bail\n // out on its own — so without this the entry is captured by a static prerender and the store\n // stops tracking the CMS until its next deploy. `'use cache'` used to hold the route open with a\n // five-minute revalidate; removing it for a platform that does not stream took that with it.\n // Outside the fallback's try, which lives in `readPublished`, so the bail-out it throws during\n // static generation is never swallowed as a transient failure.\n await connection();\n const { isEnabled: draft } = await draftMode();\n let result: ContentEntryResult;\n let staleReason: string | undefined;\n if (draft) {\n // The preview branch neither reads the store nor writes it: an unpublished blob must not\n // outlive the request that asked for it, and a draft must never cover a published failure.\n result = await readPreviewEntry(config, contentTypeId, entryId, locale);\n } else {\n ({ result, staleReason } = await readPublished(contentTypeId, entryId, locale));\n }\n // A served blob is `found`, so the status alone would report it as a success.\n const stale = staleReason !== undefined;\n const { level, event } = stale ? STALE_READ_EVENT : READ_EVENTS[result.status];\n const reason =\n staleReason ?? (result.status === 'unavailable' || result.status === 'invalid' ? result.reason : undefined);\n // The success line is the cheapest evidence of the ≤2-reads-per-page guarantee, and its only\n // cost is production volume: nothing caches this read, so it would emit once or twice per page\n // view and bury the rare failure line. Suppressed there, kept everywhere the count is checked —\n // and the gate covers a genuine success only, so a covered outage stays audible in production.\n const suppressed = !stale && result.status === 'found' && process.env.NODE_ENV === 'production';\n if (!suppressed) {\n logEvent(level, event, {\n contentTypeId,\n entryId,\n locale,\n source: draft ? 'control-plane' : 'data-plane',\n ...(reason === undefined ? {} : { reason }),\n });\n }\n return result;\n });\n\n /**\n * The scope's one read, awaited before render: the composite entry, sliced into one `$content`\n * value per content-backed element. The values reach the components as literal props, so they are\n * part of the server render by construction — a `cloneElement` from a client-side provider lands\n * only on hydration, which is what made server HTML and client DOM disagree.\n *\n * The public shape takes one object, so a call site cannot silently swap two same-typed positional\n * arguments; `React.cache` keys on the positional ones underneath.\n */\n async function readContentSlices({\n contentTypeId,\n entryId,\n locale,\n elements,\n }: ReadContentSlicesArgs): Promise<ContentSlices> {\n const result = await read(contentTypeId, entryId, locale);\n if (result.status !== 'found') {\n // A whole-entry failure (404, invalid, or an upstream failure with nothing stored for the\n // entry) omits every component sharing it: no key is present, so every boundary renders\n // nothing. A failure the last-good store covered never arrives here — it arrives as `found`.\n return emptySlices();\n }\n const slices = emptySlices();\n const requested = Object.entries(elements);\n const omissions: ElementOmission[] = [];\n for (const [elementId, modelId] of requested) {\n const slice = sliceContent(result.blob, elementId, ownValue(config.contentTypes, modelId));\n if (slice.status === 'omitted') {\n // A missing or invalid key within an otherwise-valid entry omits only this one component.\n omissions.push({ elementId, modelId, reason: slice.reason });\n continue;\n }\n slices[elementId] = slice.content;\n }\n if (omissions.length > 0) {\n // One line per call rather than one per element: the operational fact is the ratio, and\n // `omitted === total` is what tells a blank scope from a partial one. The level says only that\n // the page rendered, so it stays `warn` at every ratio.\n logEvent('warn', 'content.elements.omitted', {\n contentTypeId,\n entryId,\n locale,\n omitted: omissions.length,\n total: requested.length,\n omissions,\n });\n }\n return slices;\n }\n\n return { readContentSlices, ContentEntryBoundary };\n}\n\n/**\n * The slice map, with no prototype. An element id is a schema string, so `__proto__` is a name the\n * map can be asked for; on a plain object the lookup would inherit `Object.prototype` and a boundary\n * would render an element whose `$content` is that object rather than omitting it. The failure path\n * returns this too — an entry that 404s must answer `undefined` for every key, including that one.\n */\nfunction emptySlices(): ContentSlices {\n return Object.create(null) as ContentSlices;\n}\n\n/**\n * Renders its element only when that element's slice is present — the omission rule the content RFC\n * states. It no longer delivers the value (the page passes `$content` as a literal prop), so it\n * clones nothing and what wraps it no longer decides whether content arrives.\n *\n * Module scope, not a closure over `createContentRuntime`: it reads nothing from the runtime's\n * configuration, so every store shares one component identity.\n */\nfunction ContentEntryBoundary({ value, children }: ContentEntryBoundaryProps): ReactNode {\n return value === undefined ? null : children;\n}\n\n/**\n * The published read: one composite entry from the Data Plane, live on every request. A definitive\n * 404 resolves a `not-found` sentinel; a transient failure (network, timeout, 5xx, malformed body)\n * throws, and `readPublished` decides between the stored blob and an omit.\n *\n * Nothing caches it. This read sat under `'use cache'` until Cache Components was removed for a\n * platform that does not stream, and the five-minute `cacheLife` and 24-hour `expire` went with it —\n * a transient failure used to throw inside the cache and Next kept serving the last-good entry for\n * up to a day. What replaced that is the last-good store, which is a fallback and not a cache: it\n * never shortens or skips this read, so the happy path is unchanged and a publish lands on the very\n * next request. The page cache, the two cache tags this read emitted and the tag-based revalidation\n * route they fed are still gone, and still owed by whatever restores caching.\n */\nasync function readPublishedEntry(\n account: string,\n storeId: string,\n locale: string,\n contentTypeId: string,\n entryId: string,\n): Promise<ContentEntryResult> {\n const url = buildDataPlaneUrl(account, storeId, locale, contentTypeId, entryId);\n const response = await fetch(url, { signal: AbortSignal.timeout(ENTRY_REQUEST_TIMEOUT_MS) });\n\n if (response.status === 404) return { status: 'not-found' };\n if (!response.ok) throw new Error(`content upstream status ${response.status}`);\n\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n throw new Error('content response is not JSON');\n }\n if (!isRecord(payload)) throw new Error('content response is not an object');\n\n return { status: 'found', blob: payload };\n}\n\n/**\n * The Draft Mode read: the same composite entry resolved through Control Plane's preview endpoint\n * with a server-only credential, bypassing all caches. The branch comes from a SIGNED preview\n * context cookie whose HMAC is re-verified here against the broker secret — a tampered cookie fails\n * closed, so a valid session cannot read an unauthorized branch's drafts. The credential never\n * enters browser code, logs, URLs, or props. Uncached, so its sentinels are never persisted.\n */\nasync function readPreviewEntry(\n config: ContentRuntimeConfig,\n contentTypeId: string,\n entryId: string,\n locale: string,\n): Promise<ContentEntryResult> {\n const token = process.env[PREVIEW_TOKEN_ENV];\n if (!token) return { status: 'unavailable', reason: 'preview credential unavailable' };\n const secret = process.env[PREVIEW_SECRET_ENV];\n if (!secret) return { status: 'unavailable', reason: 'preview secret unavailable' };\n\n const store = await cookies();\n const raw = store.get(PREVIEW_COOKIE)?.value;\n const claims = raw ? verifyPreviewContext(raw, secret) : null;\n if (!claims || claims.account !== config.account || claims.storeId !== config.storeId || !claims.branch) {\n return { status: 'unavailable', reason: 'invalid preview context' };\n }\n\n const { account, storeId } = config;\n const path = [account, storeId, contentTypeId, 'entries', 'preview', entryId].map(encodeURIComponent).join('/');\n const url = `https://${account}.vtexcommercestable.com.br/api/content-platform/manage/${path}?branchId=${encodeURIComponent(claims.branch)}&locale=${encodeURIComponent(locale)}`;\n return fetchPreview(url, token);\n}\n\nasync function fetchPreview(url: string, token: string): Promise<ContentEntryResult> {\n let response: Response;\n try {\n response = await fetch(url, {\n cache: 'no-store',\n headers: { Authorization: `Bearer ${token}` },\n signal: AbortSignal.timeout(ENTRY_REQUEST_TIMEOUT_MS),\n });\n } catch (error) {\n return { status: 'unavailable', reason: isTimeoutError(error) ? 'timeout' : 'network error' };\n }\n if (response.status === 404) return { status: 'not-found' };\n if (!response.ok) return { status: 'unavailable', reason: `upstream status ${response.status}` };\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n return { status: 'invalid', reason: 'response body is not JSON' };\n }\n if (!isRecord(payload)) return { status: 'invalid', reason: 'response body is not an object' };\n return { status: 'found', blob: payload };\n}\n\n/**\n * Verifies a signed preview context cookie (`base64url(claims).base64url(hmac)`) against the broker\n * secret with a constant-time comparison, returning the claims only when the signature holds and the\n * token has not expired — a captured cookie fails closed once past `exp`.\n */\nfunction verifyPreviewContext(raw: string, secret: string): PreviewClaims | null {\n const [body, signature] = raw.split('.');\n if (!body || !signature) return null;\n const expected = createHmac('sha256', secret).update(body).digest('base64url');\n const given = Buffer.from(signature, 'utf8');\n const want = Buffer.from(expected, 'utf8');\n if (given.length !== want.length || !timingSafeEqual(given, want)) return null;\n try {\n const claims = JSON.parse(Buffer.from(body, 'base64url').toString('utf8')) as PreviewClaims;\n if (typeof claims.branch !== 'string' || !Number.isFinite(claims.exp) || Date.now() >= claims.exp) {\n return null;\n }\n return claims;\n } catch {\n return null;\n }\n}\n\nfunction buildDataPlaneUrl(\n account: string,\n storeId: string,\n locale: string,\n contentTypeId: string,\n entryId: string,\n): string {\n const path = [account, storeId, contentTypeId, 'entries', entryId].map(encodeURIComponent).join('/');\n return `https://${account}.vtexcommercestable.com.br/api/content-platform/data/${path}?locale=${encodeURIComponent(locale)}`;\n}\n\ntype ContentSliceResult =\n | { status: 'sliced'; content: Record<string, unknown> }\n | { status: 'omitted'; reason: string };\n\n/**\n * Slices one element's value out of the composite blob by its `element.id` and validates it against\n * that element's per-component model. Omits only this component on a missing key, a non-object\n * value, an unknown model, or a shape violation — keyword constraints are the preflight's job.\n * Failing closed on an unknown model keeps an unvalidated blob from reaching `$content` (which would\n * crash the route as a React child).\n */\nfunction sliceContent(\n blob: Record<string, unknown>,\n elementId: string,\n model: ContentTypeDefinition | undefined,\n): ContentSliceResult {\n const raw = ownValue(blob, elementId);\n if (!isRecord(raw)) return { status: 'omitted', reason: 'missing or non-object value' };\n if (!model) return { status: 'omitted', reason: 'unknown model' };\n\n const content = projectObject(model.schema.properties, raw);\n const violation = findShapeViolation(model, content);\n if (violation !== null) return { status: 'omitted', reason: violation };\n return { status: 'sliced', content };\n}\n\n/** A closed object shape, at the root or one level down. */\ntype ClosedObjectShape = {\n required?: readonly string[];\n properties: Record<string, ContentPropertySchema>;\n};\n\n/**\n * Keeps declared keys only, at every level the vocabulary admits, so the injected value matches the\n * `ContentValue` type the component was compiled against. A value of the wrong shape passes through\n * untouched for the validator below to reject.\n */\nfunction projectObject(\n properties: Record<string, ContentPropertySchema>,\n raw: Record<string, unknown>,\n): Record<string, unknown> {\n const projected: Record<string, unknown> = {};\n for (const [key, property] of Object.entries(properties)) {\n const value = ownValue(raw, key);\n if (value === undefined) continue;\n if (property.type === 'array') {\n projected[key] = Array.isArray(value)\n ? value.map((member) => (isRecord(member) ? projectObject(property.items.properties, member) : member))\n : value;\n } else if (property.type === 'object') {\n projected[key] = isRecord(value) ? projectObject(property.properties, value) : value;\n } else {\n projected[key] = value;\n }\n }\n return projected;\n}\n\n/** Shape-level validation only (required presence, primitive type, enum membership), at every level. */\nfunction findShapeViolation(model: ContentTypeDefinition, content: Record<string, unknown>): string | null {\n return findObjectViolation('', model.schema, content);\n}\n\nfunction findObjectViolation(path: string, shape: ClosedObjectShape, value: Record<string, unknown>): string | null {\n for (const key of shape.required ?? []) {\n if (ownValue(value, key) === undefined) return `missing required property \"${joinPath(path, key)}\"`;\n }\n for (const [key, property] of Object.entries(shape.properties)) {\n const member = ownValue(value, key);\n if (member === undefined) continue;\n const violation = findPropertyViolation(joinPath(path, key), property, member);\n if (violation !== null) return violation;\n }\n return null;\n}\n\nfunction findPropertyViolation(path: string, property: ContentPropertySchema, value: unknown): string | null {\n if (property.type === 'array') {\n if (!Array.isArray(value)) return `property \"${path}\" does not match type \"array\"`;\n for (const [index, member] of value.entries()) {\n const itemPath = `${path}[${index}]`;\n if (!isRecord(member)) return `property \"${itemPath}\" does not match type \"object\"`;\n const violation = findObjectViolation(itemPath, property.items, member);\n if (violation !== null) return violation;\n }\n // minItems and maxItems are author-time bounds; the read path does not enforce them.\n return null;\n }\n if (property.type === 'object') {\n if (!isRecord(value)) return `property \"${path}\" does not match type \"object\"`;\n return findObjectViolation(path, property, value);\n }\n if (!matchesScalarType(property.type, value)) return `property \"${path}\" does not match type \"${property.type}\"`;\n if (property.enum && !property.enum.includes(value as string | number)) {\n return `property \"${path}\" is not an allowed enum value`;\n }\n return null;\n}\n\nfunction joinPath(path: string, key: string): string {\n return path.length === 0 ? key : `${path}.${key}`;\n}\n\nfunction matchesScalarType(type: 'string' | 'number' | 'integer' | 'boolean', value: unknown): boolean {\n switch (type) {\n case 'string':\n return typeof value === 'string';\n case 'boolean':\n return typeof value === 'boolean';\n case 'number':\n return typeof value === 'number' && Number.isFinite(value);\n case 'integer':\n return typeof value === 'number' && Number.isInteger(value);\n }\n}\n\ntype LogLevel = 'error' | 'warn' | 'info';\n\n/** The closed vocabulary a log consumer discriminates on. Nothing else is ever emitted. */\ntype ContentEvent =\n | 'content.read.ok'\n | 'content.read.stale'\n | 'content.read.missing'\n | 'content.read.invalid'\n | 'content.read.failed'\n | 'content.elements.omitted'\n | 'content.retention.skipped';\n\n/** One omitted element, as the aggregate line reports it. `reason` is a path, never a value. */\ntype ElementOmission = { elementId: string; modelId: string; reason: string };\n\n/**\n * Severity as a function of the read's status, and nothing else: `unavailable` means the runtime\n * could not read, which is the on-call signal; `not-found` and `invalid` mean the content is broken\n * while the runtime works. `source` is a payload field, so a preview read that cannot resolve is a\n * failure like any other. `invalid` is reachable from the preview branch only — the published path\n * throws on a bad body, which is why the store covers that case.\n */\nconst READ_EVENTS: Record<ContentEntryResult['status'], { level: LogLevel; event: ContentEvent }> = {\n found: { level: 'info', event: 'content.read.ok' },\n 'not-found': { level: 'warn', event: 'content.read.missing' },\n invalid: { level: 'warn', event: 'content.read.invalid' },\n unavailable: { level: 'error', event: 'content.read.failed' },\n};\n\n/**\n * The one line the status does not decide: the read failed and the stored blob covered it. `warn`\n * because the page rendered, which keeps `error` meaning exactly \"the shopper is seeing holes\". A\n * covered outage never escalates on its own, so alerting on one means filtering this event name.\n */\nconst STALE_READ_EVENT: { level: LogLevel; event: ContentEvent } = { level: 'warn', event: 'content.read.stale' };\n\n/**\n * The runtime's one log line: a constant message plus a payload whose `event` is the sole event\n * discriminator, so rewording prose cannot break a consumer. Nothing here ever carries a content\n * value, a preview token, or the broker secret.\n *\n * `entryId` is high-cardinality and stays log-only. Feeding these events to a metrics pipeline —\n * which no part of this runtime has today — means dropping or hashing it at that boundary; the six\n * event names are the bounded dimension set such a pipeline would inherit.\n */\nfunction logEvent(level: LogLevel, event: ContentEvent, fields: Record<string, unknown>): void {\n // `event` spreads last so a `fields` key of the same name cannot displace the discriminator every\n // consumer keys on. `fields` is a `Record<string, unknown>`, so nothing in the types would object.\n console[level]('[content-runtime]', { ...fields, event });\n}\n\nfunction transientReason(error: unknown): string {\n if (isTimeoutError(error)) return 'timeout';\n return error instanceof Error ? error.message : 'network error';\n}\n\n/**\n * The exact UTF-8 byte count of the serialized blob, not the JS string's UTF-16 code-unit count\n * (`.length`). Non-ASCII text — accented Latin, common in Portuguese-language content, at 2 bytes per\n * code unit, or CJK at 3 — makes those two numbers diverge, and a check against `.length` would let a\n * blob past both ceilings while genuinely over them in bytes.\n */\nfunction blobByteLength(blob: Record<string, unknown>): number {\n return Buffer.byteLength(JSON.stringify(blob), 'utf8');\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Own-property read. Every object keyed by an authored id here comes from `JSON.parse` or a plain\n * literal, so it carries `Object.prototype`: a key named `__proto__` or `constructor` would resolve\n * to something inherited instead of to `undefined`, and the absent-key branch would never be taken.\n */\nfunction ownValue<T>(source: Record<string, T>, key: string): T | undefined {\n return Object.hasOwn(source, key) ? source[key] : undefined;\n}\n\n/** Property check instead of `instanceof`: `AbortSignal.timeout` rejects with a `DOMException`. */\nfunction isTimeoutError(error: unknown): boolean {\n if (typeof error !== 'object' || error === null) return false;\n const { name } = error as { name?: unknown };\n return name === 'TimeoutError' || name === 'AbortError';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"create-content-runtime.js","names":[],"sources":["../../../src/sdk/content/create-content-runtime.tsx"],"sourcesContent":["import { createHmac, timingSafeEqual } from 'node:crypto';\nimport type { ContentPropertySchema, ContentTypeDefinition } from '@vtex/fs-core';\nimport { cookies, draftMode } from 'next/headers';\nimport { connection } from 'next/server';\nimport { cache, type FC, type ReactNode } from 'react';\nimport { ownValue } from '../../utils/own-value.js';\n\nexport type ContentRuntimeConfig = {\n account: string;\n storeId: string;\n /** Canonical per-component models, verbatim from the aggregated content catalog, keyed by id. */\n contentTypes: Record<string, ContentTypeDefinition>;\n};\n\n/** The coordinates of one composite read. `locale` arrives per call, so one build serves many. */\nexport type ReadContentSlicesArgs = {\n contentTypeId: string;\n entryId: string;\n locale: string;\n /** `element.id → per-component model id` for every content-backed element in the scope. */\n elements: Record<string, string>;\n};\n\n/**\n * The scope's slices, keyed by `element.id`. A key is absent when its element must be omitted — a\n * missing key, a shape violation, or a whole-entry failure.\n *\n * The value type is unchecked on purpose: the generated page forwards it to a component whose\n * `$content` is `ContentValue<that component's model>`, a type that exists only in the component\n * package, and an index signature cannot satisfy a model's required properties. Stated once here\n * rather than cast at every emitted prop; the read below validates every value against its model.\n */\nexport type ContentSlices = {\n // biome-ignore lint/suspicious/noExplicitAny: the model's type lives in the component package, not here.\n [elementId: string]: any;\n};\n\nexport type ContentEntryBoundaryProps = {\n /** This element's slice, read at the top of the scope. `undefined` omits the component. */\n value: unknown;\n children: ReactNode;\n};\n\n/** The coordinates of one item read. Object arguments, so two strings cannot be swapped at the call site. */\nexport type ReadContentItemArgs = {\n contentTypeId: string;\n slug: string;\n locale: string;\n};\n\n/**\n * One item read. `found` is the resolved entry. `not-found` is HTTP 404 only. `unavailable` is every\n * other failure with nothing stored. There is no `invalid` arm: a 200 JSON object is the blob.\n */\nexport type ContentItemResult =\n | { status: 'found'; blob: Record<string, unknown> }\n | { status: 'not-found' }\n | { status: 'unavailable'; reason: string };\n\nexport type ContentRuntime = {\n readContentSlices: (args: ReadContentSlicesArgs) => Promise<ContentSlices>;\n readContentItem: (args: ReadContentItemArgs) => Promise<ContentItemResult>;\n ContentEntryBoundary: FC<ContentEntryBoundaryProps>;\n};\n\n/** The signed preview context the Draft Mode route stores; re-verified here before use. */\ntype PreviewClaims = {\n account: string;\n storeId: string;\n branch: string;\n /** Millisecond epoch the token was minted / expires; a captured cookie stops working past `exp`. */\n iat: number;\n exp: number;\n};\n\n/**\n * Internal result taxonomy: a read either finds a validated composite blob or fails for a definite,\n * loggable reason. A transient upstream failure resolves `found` from the last-good store below when\n * that store holds the entry, and degrades to `unavailable` — omitting the affected components —\n * only when it does not.\n */\nexport type ContentEntryResult =\n | { status: 'found'; blob: Record<string, unknown> }\n | { status: 'not-found' }\n | { status: 'unavailable'; reason: string }\n | { status: 'invalid'; reason: string };\n\n/**\n * One published read's outcome. `staleReason` is present only when the fresh read failed and the\n * store covered it, and the result is then an ordinary `found`: no consumer has a fifth renderable\n * status to learn, and only the log line tells a served blob from a fresh one.\n */\ntype PublishedRead = { result: ContentEntryResult; staleReason?: string };\n\n/** Same shape as `PublishedRead`, on the item taxonomy (no `invalid` arm). */\ntype PublishedItemRead = { result: ContentItemResult; staleReason?: string };\n\nconst ENTRY_REQUEST_TIMEOUT_MS = 5000;\n\n/**\n * Retention ceilings for the last-good store, both estimated rather than measured: no production\n * store exists yet to size a real composite against, and the example schemas in this repo are demo\n * content, not authored pages, so neither is evidence of real scale.\n *\n * `MAX_RETAINED_BLOB_BYTES` bounds one entry. Ordinary editorial text (a title, a body, an image URL,\n * an alt text) stays in the hundreds of bytes to a few kilobytes per element regardless of how many\n * elements a page composes — even the largest composed layout in this repo (103 elements) does not\n * read as megabytes if every element stays plain text. The realistic way to exceed this is a single\n * field holding embedded binary — a base64 image pasted into a text field instead of uploaded through\n * the media widget — which is exactly the shape this guards against, independent of element count.\n *\n * `MAX_RETAINED_TOTAL_BYTES` bounds the whole store, and is the one that actually matters: the\n * per-blob cap alone does not bound total memory, because it multiplies by key count, and key count is\n * not small — a large store's pages and layouts, times its locales, is plausibly a few hundred keys.\n * A few hundred keys at a few megabytes each already exceeds a typical serverless heap (512 MB–1 GB)\n * on the per-blob cap alone; ~64 MB is a small slice of that heap while still holding many ordinary\n * pages at once.\n *\n * Both numbers count serialized JSON text (`blobByteLength`), not retained heap: a parsed object\n * typically costs more than its JSON text — V8 pays per-property and per-string object overhead that\n * the text itself does not — so the real footprint at either ceiling is larger than the number on the\n * tin, plausibly by a small multiple. Both ceilings still leave headroom against a 512 MB–1 GB heap at\n * that multiple; sizing them as retained heap instead would need a runtime memory measurement this\n * module has no cheap way to take.\n *\n * Both numbers are round and generous on purpose, not derived. `content.retention.skipped` below is\n * the real calibration path: once a production store exists, its log volume says whether either\n * ceiling needs to move.\n */\nconst MAX_RETAINED_BLOB_BYTES = 1_000_000;\nconst MAX_RETAINED_TOTAL_BYTES = 64_000_000;\n\n/** Server-only preview credential, supplied at deployment time; never reaches the browser. */\nconst PREVIEW_TOKEN_ENV = 'CONTENT_PLATFORM_PREVIEW_TOKEN';\n/** Shared broker secret used to verify the signed preview context cookie. */\nconst PREVIEW_SECRET_ENV = 'CONTENT_PREVIEW_SECRET';\n/** The signed preview context cookie the Draft Mode route sets. */\nconst PREVIEW_COOKIE = '__fs_content_preview';\n\n/**\n * Creates the one server-side content read surface for a store: a `React.cache`-deduped\n * `readContentSlices` the generated page or layout awaits once at the top, and the\n * `ContentEntryBoundary` the compiler wraps around each content-backed element. One read per scope\n * yields every element's `$content` value keyed by `element.id`, so a page issues at most two Data\n * Plane reads (its own entry plus its layout's) regardless of content-backed component count. Draft\n * Mode switches that composite read to Control Plane's preview endpoint through a server-only\n * credential. `readContentItem` reads one entry by slug and does not consult Draft Mode.\n */\nexport function createContentRuntime(config: ContentRuntimeConfig): ContentRuntime {\n // The blob of the most recent successful published read, per entry, so a failing Data Plane\n // renders the last content that worked instead of holes. Not `React.cache`, whose lifetime is the\n // request and so shorter than the failure it has to cover, and not module scope, which every\n // runtime in the process would share — two of them on the same coordinates would then serve each\n // other's content. The generated consumer holds the runtime in a module-scope `const`, so the\n // closure lives as long as the process either way.\n //\n // `contentTypeId` and `entryId` are compiler-emitted literals for the life of the deployment (a\n // dynamic route gets one entry, not one per slug), so today's key domain is small: pages plus\n // layouts, times locales. That is an invariant of the generated caller, not of this function —\n // nothing here stops a future one from passing a request-derived value instead, and the moment it\n // does, this map grows with traffic rather than with the store's page count.\n //\n // Item reads share this map and both ceilings. Their key starts with `item` and a NUL, then the\n // content type, slug, and locale, so it cannot equal a composite key even when the slug equals an\n // entry id. A slug is request-derived, so that part of the map grows with distinct items.\n const lastGood = new Map<string, Record<string, unknown>>();\n let retainedBytes = 0;\n\n type RetainFields =\n | { contentTypeId: string; entryId: string; locale: string }\n | { contentTypeId: string; slug: string; locale: string };\n\n /**\n * Whether a successful read's blob replaces what `key` already holds, weighed against both\n * ceilings. Skipping never deletes: whatever `key` held before, if anything, is left exactly as it\n * was — only the update is declined, so a rare oversized read cannot erase a smaller last-good value\n * that a future failure could still use.\n */\n function retainBlob(key: string, blob: Record<string, unknown>, fields: RetainFields): void {\n const size = blobByteLength(blob);\n if (size > MAX_RETAINED_BLOB_BYTES) {\n logEvent('warn', 'content.retention.skipped', { ...fields, bytes: size, reason: 'blob-too-large' });\n return;\n }\n const existing = lastGood.get(key);\n const existingSize = existing ? blobByteLength(existing) : 0;\n if (retainedBytes - existingSize + size > MAX_RETAINED_TOTAL_BYTES) {\n logEvent('warn', 'content.retention.skipped', { ...fields, bytes: size, reason: 'total-cap-reached' });\n return;\n }\n lastGood.set(key, blob);\n retainedBytes += size - existingSize;\n }\n\n /**\n * The published read plus the fallback that covers it. The upstream is always called and the store\n * is read only in the `catch`, which is what makes this a fallback and not a cache: a publish is\n * visible on the very next request. A success ordinarily overwrites the entry (see `retainBlob` for\n * the two cases where it does not), a `not-found` leaves it alone so unpublishing still unpublishes,\n * and nothing is ever removed or expired by age.\n *\n * Failure is structural — whatever leaves `readPublishedEntry` as a throw — rather than a status\n * table, so a 403 or a 429 serves the stored blob, and a failure mode added there later is covered\n * without touching this.\n */\n async function readPublished(contentTypeId: string, entryId: string, locale: string): Promise<PublishedRead> {\n // NUL appears in no content-type id, entry id, or BCP-47 tag, so two distinct triples cannot\n // collide. Keyed on the same three values the memoisation below uses, so the two layers never\n // disagree about what \"the same read\" is.\n const key = `${contentTypeId}\\u0000${entryId}\\u0000${locale}`;\n try {\n const result = await readPublishedEntry(config.account, config.storeId, locale, contentTypeId, entryId);\n if (result.status === 'found') {\n retainBlob(key, result.blob, { contentTypeId, entryId, locale });\n }\n return { result };\n } catch (error) {\n const reason = transientReason(error);\n const stored = lastGood.get(key);\n // Nothing stored: omit the affected components while the rest of the route renders.\n if (stored === undefined) return { result: { status: 'unavailable', reason } };\n return { result: { status: 'found', blob: stored }, staleReason: reason };\n }\n }\n\n /**\n * The by-slug read plus the same fallback. Not a parameter of `readPublished`: the key, the URL,\n * and the result taxonomy differ, and folding them together would put a slug on the composite log\n * line. A `not-found` returns before the map is read or written.\n */\n async function readPublishedItem(contentTypeId: string, slug: string, locale: string): Promise<PublishedItemRead> {\n // The `item` prefix is what keeps this off `${contentTypeId}\\u0000${entryId}\\u0000${locale}`.\n const key = `item\\u0000${contentTypeId}\\u0000${slug}\\u0000${locale}`;\n try {\n const result = await readItemBySlug(config.account, config.storeId, locale, contentTypeId, slug);\n if (result.status === 'found') {\n retainBlob(key, result.blob, { contentTypeId, slug, locale });\n }\n return { result };\n } catch (error) {\n const reason = transientReason(error);\n const stored = lastGood.get(key);\n if (stored === undefined) return { result: { status: 'unavailable', reason } };\n return { result: { status: 'found', blob: stored }, staleReason: reason };\n }\n }\n\n // React.cache collapses same-entry awaits within one request: N boundaries reading the same\n // composite entry share one upstream read and one read-log line. That is per-request memoisation\n // only — nothing is reused across requests — so it is `readPublished` above, not this, that\n // carries a value across the failure it covers. Either way a read never throws a route error.\n const read = cache(async (contentTypeId: string, entryId: string, locale: string): Promise<ContentEntryResult> => {\n // Content is read per request, never at build time. Nothing else on a content-only page is\n // request-dependent — `draftMode()` prerenders as disabled and an uncached `fetch` does not bail\n // out on its own — so without this the entry is captured by a static prerender and the store\n // stops tracking the CMS until its next deploy. `'use cache'` used to hold the route open with a\n // five-minute revalidate; removing it for a platform that does not stream took that with it.\n // Outside the fallback's try, which lives in `readPublished`, so the bail-out it throws during\n // static generation is never swallowed as a transient failure.\n await connection();\n const { isEnabled: draft } = await draftMode();\n let result: ContentEntryResult;\n let staleReason: string | undefined;\n if (draft) {\n // The preview branch neither reads the store nor writes it: an unpublished blob must not\n // outlive the request that asked for it, and a draft must never cover a published failure.\n result = await readPreviewEntry(config, contentTypeId, entryId, locale);\n } else {\n ({ result, staleReason } = await readPublished(contentTypeId, entryId, locale));\n }\n // A served blob is `found`, so the status alone would report it as a success.\n const stale = staleReason !== undefined;\n const { level, event } = stale ? STALE_READ_EVENT : READ_EVENTS[result.status];\n const reason =\n staleReason ?? (result.status === 'unavailable' || result.status === 'invalid' ? result.reason : undefined);\n // The success line is the cheapest evidence of the ≤2-reads-per-page guarantee, and its only\n // cost is production volume: nothing caches this read, so it would emit once or twice per page\n // view and bury the rare failure line. Suppressed there, kept everywhere the count is checked —\n // and the gate covers a genuine success only, so a covered outage stays audible in production.\n const suppressed = !stale && result.status === 'found' && process.env.NODE_ENV === 'production';\n if (!suppressed) {\n logEvent(level, event, {\n contentTypeId,\n entryId,\n locale,\n source: draft ? 'control-plane' : 'data-plane',\n ...(reason === undefined ? {} : { reason }),\n });\n }\n return result;\n });\n\n /**\n * The scope's one read, awaited before render: the composite entry, sliced into one `$content`\n * value per content-backed element. The values reach the components as literal props, so they are\n * part of the server render by construction — a `cloneElement` from a client-side provider lands\n * only on hydration, which is what made server HTML and client DOM disagree.\n *\n * The public shape takes one object, so a call site cannot silently swap two same-typed positional\n * arguments; `React.cache` keys on the positional ones underneath.\n */\n async function readContentSlices({\n contentTypeId,\n entryId,\n locale,\n elements,\n }: ReadContentSlicesArgs): Promise<ContentSlices> {\n const result = await read(contentTypeId, entryId, locale);\n if (result.status !== 'found') {\n // A whole-entry failure (404, invalid, or an upstream failure with nothing stored for the\n // entry) omits every component sharing it: no key is present, so every boundary renders\n // nothing. A failure the last-good store covered never arrives here — it arrives as `found`.\n return emptySlices();\n }\n const slices = emptySlices();\n const requested = Object.entries(elements);\n const omissions: ElementOmission[] = [];\n for (const [elementId, modelId] of requested) {\n const slice = sliceContent(result.blob, elementId, ownValue(config.contentTypes, modelId));\n if (slice.status === 'omitted') {\n // A missing or invalid key within an otherwise-valid entry omits only this one component.\n omissions.push({ elementId, modelId, reason: slice.reason });\n continue;\n }\n slices[elementId] = slice.content;\n }\n if (omissions.length > 0) {\n // One line per call rather than one per element: the operational fact is the ratio, and\n // `omitted === total` is what tells a blank scope from a partial one. The level says only that\n // the page rendered, so it stays `warn` at every ratio.\n logEvent('warn', 'content.elements.omitted', {\n contentTypeId,\n entryId,\n locale,\n omitted: omissions.length,\n total: requested.length,\n omissions,\n });\n }\n return slices;\n }\n\n // A separate cache from `read`. Positional key is content type, slug, locale. `connection()` is\n // outside `readPublishedItem`'s try, so a prerender bail-out propagates unlogged. Draft Mode is\n // not a branch this read has: there is no slug preview route, and a Data Plane `branch` query is\n // ignored by the platform.\n const readItem = cache(async (contentTypeId: string, slug: string, locale: string): Promise<ContentItemResult> => {\n await connection();\n const { result, staleReason } = await readPublishedItem(contentTypeId, slug, locale);\n const stale = staleReason !== undefined;\n const { level, event } = stale ? STALE_READ_EVENT : READ_EVENTS[result.status];\n const reason = staleReason ?? (result.status === 'unavailable' ? result.reason : undefined);\n const suppressed = !stale && result.status === 'found' && process.env.NODE_ENV === 'production';\n if (!suppressed) {\n logEvent(level, event, {\n contentTypeId,\n slug,\n locale,\n source: 'data-plane',\n ...(reason === undefined ? {} : { reason }),\n });\n }\n return result;\n });\n\n async function readContentItem({ contentTypeId, slug, locale }: ReadContentItemArgs): Promise<ContentItemResult> {\n return readItem(contentTypeId, slug, locale);\n }\n\n return { readContentSlices, readContentItem, ContentEntryBoundary };\n}\n\n/**\n * The slice map, with no prototype. An element id is a schema string, so `__proto__` is a name the\n * map can be asked for; on a plain object the lookup would inherit `Object.prototype` and a boundary\n * would render an element whose `$content` is that object rather than omitting it. The failure path\n * returns this too — an entry that 404s must answer `undefined` for every key, including that one.\n */\nfunction emptySlices(): ContentSlices {\n return Object.create(null) as ContentSlices;\n}\n\n/**\n * Renders its element only when that element's slice is present — the omission rule the content RFC\n * states. It no longer delivers the value (the page passes `$content` as a literal prop), so it\n * clones nothing and what wraps it no longer decides whether content arrives.\n *\n * Module scope, not a closure over `createContentRuntime`: it reads nothing from the runtime's\n * configuration, so every store shares one component identity.\n */\nfunction ContentEntryBoundary({ value, children }: ContentEntryBoundaryProps): ReactNode {\n return value === undefined ? null : children;\n}\n\n/**\n * The published read: one composite entry from the Data Plane, live on every request. A definitive\n * 404 resolves a `not-found` sentinel; a transient failure (network, timeout, 5xx, malformed body)\n * throws, and `readPublished` decides between the stored blob and an omit.\n *\n * Nothing caches it. This read sat under `'use cache'` until Cache Components was removed for a\n * platform that does not stream, and the five-minute `cacheLife` and 24-hour `expire` went with it —\n * a transient failure used to throw inside the cache and Next kept serving the last-good entry for\n * up to a day. What replaced that is the last-good store, which is a fallback and not a cache: it\n * never shortens or skips this read, so the happy path is unchanged and a publish lands on the very\n * next request. The page cache, the two cache tags this read emitted and the tag-based revalidation\n * route they fed are still gone, and still owed by whatever restores caching.\n */\nasync function readPublishedEntry(\n account: string,\n storeId: string,\n locale: string,\n contentTypeId: string,\n entryId: string,\n): Promise<ContentEntryResult> {\n const url = buildDataPlaneUrl(account, storeId, locale, contentTypeId, entryId);\n const response = await fetch(url, { signal: AbortSignal.timeout(ENTRY_REQUEST_TIMEOUT_MS) });\n\n if (response.status === 404) return { status: 'not-found' };\n if (!response.ok) throw new Error(`content upstream status ${response.status}`);\n\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n throw new Error('content response is not JSON');\n }\n if (!isRecord(payload)) throw new Error('content response is not an object');\n\n return { status: 'found', blob: payload };\n}\n\n/**\n * One entry from the Data Plane by slug. A 404 resolves `not-found` before `json()`. Any other\n * non-OK status, a non-JSON body, or a non-object body throws, and `readPublishedItem` decides\n * between the stored blob and `unavailable`. A 200 object is `found` with no field-schema check.\n * `locale_metadata` is the platform envelope (it carries an inline SVG) and this path has no slicer,\n * so that own property is removed before the blob is returned or retained. No other key is dropped.\n * A list body is not an object, so it throws rather than resolving `not-found`.\n */\nasync function readItemBySlug(\n account: string,\n storeId: string,\n locale: string,\n contentTypeId: string,\n slug: string,\n): Promise<ContentItemResult> {\n const url = buildDataPlaneSlugUrl(account, storeId, locale, contentTypeId, slug);\n const response = await fetch(url, { signal: AbortSignal.timeout(ENTRY_REQUEST_TIMEOUT_MS) });\n\n if (response.status === 404) return { status: 'not-found' };\n if (!response.ok) throw new Error(`content upstream status ${response.status}`);\n\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n throw new Error('content response is not JSON');\n }\n if (!isRecord(payload)) throw new Error('content response is not an object');\n if (Object.hasOwn(payload, 'locale_metadata')) {\n delete payload.locale_metadata;\n }\n\n return { status: 'found', blob: payload };\n}\n\n/**\n * The Draft Mode read: the same composite entry resolved through Control Plane's preview endpoint\n * with a server-only credential, bypassing all caches. The branch comes from a SIGNED preview\n * context cookie whose HMAC is re-verified here against the broker secret — a tampered cookie fails\n * closed, so a valid session cannot read an unauthorized branch's drafts. The credential never\n * enters browser code, logs, URLs, or props. Uncached, so its sentinels are never persisted.\n */\nasync function readPreviewEntry(\n config: ContentRuntimeConfig,\n contentTypeId: string,\n entryId: string,\n locale: string,\n): Promise<ContentEntryResult> {\n const token = process.env[PREVIEW_TOKEN_ENV];\n if (!token) return { status: 'unavailable', reason: 'preview credential unavailable' };\n const secret = process.env[PREVIEW_SECRET_ENV];\n if (!secret) return { status: 'unavailable', reason: 'preview secret unavailable' };\n\n const store = await cookies();\n const raw = store.get(PREVIEW_COOKIE)?.value;\n const claims = raw ? verifyPreviewContext(raw, secret) : null;\n if (!claims || claims.account !== config.account || claims.storeId !== config.storeId || !claims.branch) {\n return { status: 'unavailable', reason: 'invalid preview context' };\n }\n\n const { account, storeId } = config;\n const path = [account, storeId, contentTypeId, 'entries', 'preview', entryId].map(encodeURIComponent).join('/');\n const url = `https://${account}.vtexcommercestable.com.br/api/content-platform/manage/${path}?branchId=${encodeURIComponent(claims.branch)}&locale=${encodeURIComponent(locale)}`;\n return fetchPreview(url, token);\n}\n\nasync function fetchPreview(url: string, token: string): Promise<ContentEntryResult> {\n let response: Response;\n try {\n response = await fetch(url, {\n cache: 'no-store',\n headers: { Authorization: `Bearer ${token}` },\n signal: AbortSignal.timeout(ENTRY_REQUEST_TIMEOUT_MS),\n });\n } catch (error) {\n return { status: 'unavailable', reason: isTimeoutError(error) ? 'timeout' : 'network error' };\n }\n if (response.status === 404) return { status: 'not-found' };\n if (!response.ok) return { status: 'unavailable', reason: `upstream status ${response.status}` };\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n return { status: 'invalid', reason: 'response body is not JSON' };\n }\n if (!isRecord(payload)) return { status: 'invalid', reason: 'response body is not an object' };\n return { status: 'found', blob: payload };\n}\n\n/**\n * Verifies a signed preview context cookie (`base64url(claims).base64url(hmac)`) against the broker\n * secret with a constant-time comparison, returning the claims only when the signature holds and the\n * token has not expired — a captured cookie fails closed once past `exp`.\n */\nfunction verifyPreviewContext(raw: string, secret: string): PreviewClaims | null {\n const [body, signature] = raw.split('.');\n if (!body || !signature) return null;\n const expected = createHmac('sha256', secret).update(body).digest('base64url');\n const given = Buffer.from(signature, 'utf8');\n const want = Buffer.from(expected, 'utf8');\n if (given.length !== want.length || !timingSafeEqual(given, want)) return null;\n try {\n const claims = JSON.parse(Buffer.from(body, 'base64url').toString('utf8')) as PreviewClaims;\n if (typeof claims.branch !== 'string' || !Number.isFinite(claims.exp) || Date.now() >= claims.exp) {\n return null;\n }\n return claims;\n } catch {\n return null;\n }\n}\n\nfunction buildDataPlaneUrl(\n account: string,\n storeId: string,\n locale: string,\n contentTypeId: string,\n entryId: string,\n): string {\n const path = [account, storeId, contentTypeId, 'entries', entryId].map(encodeURIComponent).join('/');\n return `https://${account}.vtexcommercestable.com.br/api/content-platform/data/${path}?locale=${encodeURIComponent(locale)}`;\n}\n\n/** Same origin and `locale` query as `buildDataPlaneUrl`. The slug is one segment: it is not split on `/`. */\nfunction buildDataPlaneSlugUrl(\n account: string,\n storeId: string,\n locale: string,\n contentTypeId: string,\n slug: string,\n): string {\n const path = [account, storeId, contentTypeId, 'entries', 'slug', slug].map(encodeURIComponent).join('/');\n return `https://${account}.vtexcommercestable.com.br/api/content-platform/data/${path}?locale=${encodeURIComponent(locale)}`;\n}\n\ntype ContentSliceResult =\n | { status: 'sliced'; content: Record<string, unknown> }\n | { status: 'omitted'; reason: string };\n\n/**\n * Slices one element's value out of the composite blob by its `element.id` and validates it against\n * that element's per-component model. Omits only this component on a missing key, a non-object\n * value, an unknown model, or a shape violation — keyword constraints are the preflight's job.\n * Failing closed on an unknown model keeps an unvalidated blob from reaching `$content` (which would\n * crash the route as a React child).\n */\nfunction sliceContent(\n blob: Record<string, unknown>,\n elementId: string,\n model: ContentTypeDefinition | undefined,\n): ContentSliceResult {\n const raw = ownValue(blob, elementId);\n if (!isRecord(raw)) return { status: 'omitted', reason: 'missing or non-object value' };\n if (!model) return { status: 'omitted', reason: 'unknown model' };\n\n const content = projectObject(model.schema.properties, raw);\n const violation = findShapeViolation(model, content);\n if (violation !== null) return { status: 'omitted', reason: violation };\n return { status: 'sliced', content };\n}\n\n/** A closed object shape, at the root or one level down. */\ntype ClosedObjectShape = {\n required?: readonly string[];\n properties: Record<string, ContentPropertySchema>;\n};\n\n/**\n * Keeps declared keys only, at every level the vocabulary admits, so the injected value matches the\n * `ContentValue` type the component was compiled against. A value of the wrong shape passes through\n * untouched for the validator below to reject.\n */\nfunction projectObject(\n properties: Record<string, ContentPropertySchema>,\n raw: Record<string, unknown>,\n): Record<string, unknown> {\n const projected: Record<string, unknown> = {};\n for (const [key, property] of Object.entries(properties)) {\n const value = ownValue(raw, key);\n if (value === undefined) continue;\n if (property.type === 'array') {\n projected[key] = Array.isArray(value)\n ? value.map((member) => (isRecord(member) ? projectObject(property.items.properties, member) : member))\n : value;\n } else if (property.type === 'object') {\n projected[key] = isRecord(value) ? projectObject(property.properties, value) : value;\n } else {\n projected[key] = value;\n }\n }\n return projected;\n}\n\n/** Shape-level validation only (required presence, primitive type, enum membership), at every level. */\nfunction findShapeViolation(model: ContentTypeDefinition, content: Record<string, unknown>): string | null {\n return findObjectViolation('', model.schema, content);\n}\n\nfunction findObjectViolation(path: string, shape: ClosedObjectShape, value: Record<string, unknown>): string | null {\n for (const key of shape.required ?? []) {\n if (ownValue(value, key) === undefined) return `missing required property \"${joinPath(path, key)}\"`;\n }\n for (const [key, property] of Object.entries(shape.properties)) {\n const member = ownValue(value, key);\n if (member === undefined) continue;\n const violation = findPropertyViolation(joinPath(path, key), property, member);\n if (violation !== null) return violation;\n }\n return null;\n}\n\nfunction findPropertyViolation(path: string, property: ContentPropertySchema, value: unknown): string | null {\n if (property.type === 'array') {\n if (!Array.isArray(value)) return `property \"${path}\" does not match type \"array\"`;\n for (const [index, member] of value.entries()) {\n const itemPath = `${path}[${index}]`;\n if (!isRecord(member)) return `property \"${itemPath}\" does not match type \"object\"`;\n const violation = findObjectViolation(itemPath, property.items, member);\n if (violation !== null) return violation;\n }\n // minItems and maxItems are author-time bounds; the read path does not enforce them.\n return null;\n }\n if (property.type === 'object') {\n if (!isRecord(value)) return `property \"${path}\" does not match type \"object\"`;\n return findObjectViolation(path, property, value);\n }\n if (!matchesScalarType(property.type, value)) return `property \"${path}\" does not match type \"${property.type}\"`;\n if (property.enum && !property.enum.includes(value as string | number)) {\n return `property \"${path}\" is not an allowed enum value`;\n }\n return null;\n}\n\nfunction joinPath(path: string, key: string): string {\n return path.length === 0 ? key : `${path}.${key}`;\n}\n\nfunction matchesScalarType(type: 'string' | 'number' | 'integer' | 'boolean', value: unknown): boolean {\n switch (type) {\n case 'string':\n return typeof value === 'string';\n case 'boolean':\n return typeof value === 'boolean';\n case 'number':\n return typeof value === 'number' && Number.isFinite(value);\n case 'integer':\n return typeof value === 'number' && Number.isInteger(value);\n }\n}\n\ntype LogLevel = 'error' | 'warn' | 'info';\n\n/** The closed vocabulary a log consumer discriminates on. Nothing else is ever emitted. */\ntype ContentEvent =\n | 'content.read.ok'\n | 'content.read.stale'\n | 'content.read.missing'\n | 'content.read.invalid'\n | 'content.read.failed'\n | 'content.elements.omitted'\n | 'content.retention.skipped';\n\n/** One omitted element, as the aggregate line reports it. `reason` is a path, never a value. */\ntype ElementOmission = { elementId: string; modelId: string; reason: string };\n\n/**\n * Severity as a function of the read's status, and nothing else: `unavailable` means the runtime\n * could not read, which is the on-call signal; `not-found` and `invalid` mean the content is broken\n * while the runtime works. `source` is a payload field, so a preview read that cannot resolve is a\n * failure like any other. `invalid` is reachable from the preview branch only — the published path\n * throws on a bad body, which is why the store covers that case.\n */\nconst READ_EVENTS: Record<ContentEntryResult['status'], { level: LogLevel; event: ContentEvent }> = {\n found: { level: 'info', event: 'content.read.ok' },\n 'not-found': { level: 'warn', event: 'content.read.missing' },\n invalid: { level: 'warn', event: 'content.read.invalid' },\n unavailable: { level: 'error', event: 'content.read.failed' },\n};\n\n/**\n * The one line the status does not decide: the read failed and the stored blob covered it. `warn`\n * because the page rendered, which keeps `error` meaning exactly \"the shopper is seeing holes\". A\n * covered outage never escalates on its own, so alerting on one means filtering this event name.\n */\nconst STALE_READ_EVENT: { level: LogLevel; event: ContentEvent } = { level: 'warn', event: 'content.read.stale' };\n\n/**\n * The runtime's one log line: a constant message plus a payload whose `event` is the sole event\n * discriminator, so rewording prose cannot break a consumer. Nothing here ever carries a content\n * value, a preview token, or the broker secret.\n *\n * `entryId` is high-cardinality and stays log-only. Feeding these events to a metrics pipeline —\n * which no part of this runtime has today — means dropping or hashing it at that boundary; the six\n * event names are the bounded dimension set such a pipeline would inherit.\n */\nfunction logEvent(level: LogLevel, event: ContentEvent, fields: Record<string, unknown>): void {\n // `event` spreads last so a `fields` key of the same name cannot displace the discriminator every\n // consumer keys on. `fields` is a `Record<string, unknown>`, so nothing in the types would object.\n console[level]('[content-runtime]', { ...fields, event });\n}\n\nfunction transientReason(error: unknown): string {\n if (isTimeoutError(error)) return 'timeout';\n return error instanceof Error ? error.message : 'network error';\n}\n\n/**\n * The exact UTF-8 byte count of the serialized blob, not the JS string's UTF-16 code-unit count\n * (`.length`). Non-ASCII text — accented Latin, common in Portuguese-language content, at 2 bytes per\n * code unit, or CJK at 3 — makes those two numbers diverge, and a check against `.length` would let a\n * blob past both ceilings while genuinely over them in bytes.\n */\nfunction blobByteLength(blob: Record<string, unknown>): number {\n return Buffer.byteLength(JSON.stringify(blob), 'utf8');\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Property check instead of `instanceof`: `AbortSignal.timeout` rejects with a `DOMException`. */\nfunction isTimeoutError(error: unknown): boolean {\n if (typeof error !== 'object' || error === null) return false;\n const { name } = error as { name?: unknown };\n return name === 'TimeoutError' || name === 'AbortError';\n}\n"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ //#region src/sdk/data/empty-content-value.ts
2
+ /**
3
+ * The emptiness rule shared by the SDK helper that binds fragment variables and by the components
4
+ * that pick between a content field and the prop of the same name: `undefined`, `null`, `""`, a
5
+ * whitespace-only string and `[]` mean the merchant left the field unfilled. Trimming only decides
6
+ * emptiness; the value itself is sent and rendered as authored (content-models reference).
7
+ */
8
+ function isEmptyContentValue(value) {
9
+ if (value === void 0 || value === null) return true;
10
+ if (typeof value === "string") return value.trim() === "";
11
+ return Array.isArray(value) && value.length === 0;
12
+ }
13
+ //#endregion
14
+ export { isEmptyContentValue };
15
+
16
+ //# sourceMappingURL=empty-content-value.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"empty-content-value.js","names":[],"sources":["../../../src/sdk/data/empty-content-value.ts"],"sourcesContent":["/**\n * The emptiness rule shared by the SDK helper that binds fragment variables and by the components\n * that pick between a content field and the prop of the same name: `undefined`, `null`, `\"\"`, a\n * whitespace-only string and `[]` mean the merchant left the field unfilled. Trimming only decides\n * emptiness; the value itself is sent and rendered as authored (content-models reference).\n */\nexport function isEmptyContentValue(value: unknown): boolean {\n if (value === undefined || value === null) return true;\n if (typeof value === 'string') return value.trim() === '';\n return Array.isArray(value) && value.length === 0;\n}\n"],"mappings":";;;;;;;AAMA,SAAgB,oBAAoB,OAAyB;CAC3D,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM,OAAO;CAClD,IAAI,OAAO,UAAU,UAAU,OAAO,MAAM,KAAK,MAAM;CACvD,OAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW;AAClD"}
@@ -0,0 +1,23 @@
1
+ import { CombinedGraphQLErrors } from "@apollo/client/errors";
2
+ //#region src/sdk/data/failed-fields.ts
3
+ /**
4
+ * Response keys of the root fields a section cannot render from a result: `null` in `data` with an
5
+ * error whose `path` starts at them. With no data at all, every root an error's `path` starts at.
6
+ * Imported by both the server fetch and the client root, so it must stay free of either side.
7
+ */
8
+ function getFailedFields(data, error) {
9
+ if (!CombinedGraphQLErrors.is(error)) return [];
10
+ if (data != null && typeof data !== "object") return [];
11
+ const record = data;
12
+ const failed = [];
13
+ for (const { path } of error.errors) {
14
+ const key = path?.[0];
15
+ if (typeof key !== "string" || failed.includes(key)) continue;
16
+ if (record == null || record[key] === null) failed.push(key);
17
+ }
18
+ return failed;
19
+ }
20
+ //#endregion
21
+ export { getFailedFields };
22
+
23
+ //# sourceMappingURL=failed-fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failed-fields.js","names":[],"sources":["../../../src/sdk/data/failed-fields.ts"],"sourcesContent":["import type { ErrorLike } from '@apollo/client';\nimport { CombinedGraphQLErrors } from '@apollo/client/errors';\n\n/**\n * Response keys of the root fields a section cannot render from a result: `null` in `data` with an\n * error whose `path` starts at them. With no data at all, every root an error's `path` starts at.\n * Imported by both the server fetch and the client root, so it must stay free of either side.\n */\nexport function getFailedFields(data: unknown, error: ErrorLike | undefined): string[] {\n if (!CombinedGraphQLErrors.is(error)) return [];\n if (data != null && typeof data !== 'object') return [];\n const record = data as Record<string, unknown> | null | undefined;\n const failed: string[] = [];\n for (const { path } of error.errors) {\n const key = path?.[0];\n if (typeof key !== 'string' || failed.includes(key)) continue;\n if (record == null || record[key] === null) failed.push(key);\n }\n return failed;\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,gBAAgB,MAAe,OAAwC;CACrF,IAAI,CAAC,sBAAsB,GAAG,KAAK,GAAG,OAAO,CAAC;CAC9C,IAAI,QAAQ,QAAQ,OAAO,SAAS,UAAU,OAAO,CAAC;CACtD,MAAM,SAAS;CACf,MAAM,SAAmB,CAAC;CAC1B,KAAK,MAAM,EAAE,UAAU,MAAM,QAAQ;EACnC,MAAM,MAAM,OAAO;EACnB,IAAI,OAAO,QAAQ,YAAY,OAAO,SAAS,GAAG,GAAG;EACrD,IAAI,UAAU,QAAQ,OAAO,SAAS,MAAM,OAAO,KAAK,GAAG;CAC7D;CACA,OAAO;AACT"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { DataOverrideContext } from "./data-override-context.js";
3
+ import { loggableErrorName } from "../../utils/loggable-error-name.js";
3
4
  import { useQuery } from "@apollo/client/react";
4
5
  import { useEffect, useRef } from "react";
5
6
  import { jsx } from "react/jsx-runtime";
@@ -14,7 +15,7 @@ function HotDataProvider({ query, variables, field, fallbackData, children }) {
14
15
  useEffect(() => {
15
16
  if (error && !errorLoggedRef.current) {
16
17
  errorLoggedRef.current = true;
17
- console.error("[HotDataProvider] lazy fetch failed; falling back to composed page data.", error);
18
+ console.error("[HotDataProvider] lazy fetch failed; falling back to composed page data.", loggableErrorName(error));
18
19
  }
19
20
  }, [error]);
20
21
  let resolved = fallbackData;
@@ -1 +1 @@
1
- {"version":3,"file":"hot-data-provider.js","names":[],"sources":["../../../src/sdk/data/hot-data-provider.tsx"],"sourcesContent":["'use client';\n\nimport type { OperationVariables, TypedDocumentNode } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport type { ReactNode } from 'react';\nimport { useEffect, useRef } from 'react';\nimport { DataOverrideContext } from './data-override-context.js';\n\nexport type HotDataProviderProps<TData, TVariables extends OperationVariables> = Readonly<{\n query: TypedDocumentNode<TData, TVariables>;\n variables: TVariables;\n /** Top-level field of the fragment to forward as `$data`. */\n field: string;\n /**\n * Server-rendered value from the composed PAGE_QUERY result. It paints the first frame — `useQuery`\n * issues no request during SSR and the client cache is empty at hydration — and stays on screen on\n * error or a null payload.\n */\n fallbackData: unknown;\n children: ReactNode;\n}>;\n\nexport function HotDataProvider<TData, TVariables extends OperationVariables>({\n query,\n variables,\n field,\n fallbackData,\n children,\n}: HotDataProviderProps<TData, TVariables>): ReactNode {\n // Non-suspending, so the element never becomes a boundary and the route stays one contiguous\n // response. `cache-and-network` rather than `cache-first`: on a repeat client-side navigation the\n // cache is warm and the whole point of a hot field is that it refreshes anyway.\n const { data, error } = useQuery(query, {\n variables,\n errorPolicy: 'all',\n fetchPolicy: 'cache-and-network',\n });\n\n const errorLoggedRef = useRef(false);\n useEffect(() => {\n if (error && !errorLoggedRef.current) {\n errorLoggedRef.current = true;\n console.error('[HotDataProvider] lazy fetch failed; falling back to composed page data.', error);\n }\n }, [error]);\n\n let resolved: unknown = fallbackData;\n if (!error && data != null) {\n const fieldValue = (data as Record<string, unknown>)[field];\n if (fieldValue != null) {\n resolved = fieldValue;\n }\n }\n\n return <DataOverrideContext value={resolved}>{children}</DataOverrideContext>;\n}\n"],"mappings":";;;;;;AAsBA,SAAgB,gBAA8D,EAC5E,OACA,WACA,OACA,cACA,YACqD;CAIrD,MAAM,EAAE,MAAM,UAAU,SAAS,OAAO;EACtC;EACA,aAAa;EACb,aAAa;CACf,CAAC;CAED,MAAM,iBAAiB,OAAO,KAAK;CACnC,gBAAgB;EACd,IAAI,SAAS,CAAC,eAAe,SAAS;GACpC,eAAe,UAAU;GACzB,QAAQ,MAAM,4EAA4E,KAAK;EACjG;CACF,GAAG,CAAC,KAAK,CAAC;CAEV,IAAI,WAAoB;CACxB,IAAI,CAAC,SAAS,QAAQ,MAAM;EAC1B,MAAM,aAAc,KAAiC;EACrD,IAAI,cAAc,MAChB,WAAW;CAEf;CAEA,OAAO,oBAAC,qBAAD;EAAqB,OAAO;EAAW;CAA8B,CAAA;AAC9E"}
1
+ {"version":3,"file":"hot-data-provider.js","names":[],"sources":["../../../src/sdk/data/hot-data-provider.tsx"],"sourcesContent":["'use client';\n\nimport type { OperationVariables, TypedDocumentNode } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport type { ReactNode } from 'react';\nimport { useEffect, useRef } from 'react';\nimport { loggableErrorName } from '../../utils/loggable-error-name.js';\nimport { DataOverrideContext } from './data-override-context.js';\n\nexport type HotDataProviderProps<TData, TVariables extends OperationVariables> = Readonly<{\n query: TypedDocumentNode<TData, TVariables>;\n variables: TVariables;\n /** Top-level field of the fragment to forward as `$data`. */\n field: string;\n /**\n * Server-rendered value from the composed PAGE_QUERY result. It paints the first frame — `useQuery`\n * issues no request during SSR and the client cache is empty at hydration — and stays on screen on\n * error or a null payload.\n */\n fallbackData: unknown;\n children: ReactNode;\n}>;\n\nexport function HotDataProvider<TData, TVariables extends OperationVariables>({\n query,\n variables,\n field,\n fallbackData,\n children,\n}: HotDataProviderProps<TData, TVariables>): ReactNode {\n // Non-suspending, so the element never becomes a boundary and the route stays one contiguous\n // response. `cache-and-network` rather than `cache-first`: on a repeat client-side navigation the\n // cache is warm and the whole point of a hot field is that it refreshes anyway.\n const { data, error } = useQuery(query, {\n variables,\n errorPolicy: 'all',\n fetchPolicy: 'cache-and-network',\n });\n\n const errorLoggedRef = useRef(false);\n useEffect(() => {\n if (error && !errorLoggedRef.current) {\n errorLoggedRef.current = true;\n console.error(\n '[HotDataProvider] lazy fetch failed; falling back to composed page data.',\n loggableErrorName(error),\n );\n }\n }, [error]);\n\n let resolved: unknown = fallbackData;\n if (!error && data != null) {\n const fieldValue = (data as Record<string, unknown>)[field];\n if (fieldValue != null) {\n resolved = fieldValue;\n }\n }\n\n return <DataOverrideContext value={resolved}>{children}</DataOverrideContext>;\n}\n"],"mappings":";;;;;;;AAuBA,SAAgB,gBAA8D,EAC5E,OACA,WACA,OACA,cACA,YACqD;CAIrD,MAAM,EAAE,MAAM,UAAU,SAAS,OAAO;EACtC;EACA,aAAa;EACb,aAAa;CACf,CAAC;CAED,MAAM,iBAAiB,OAAO,KAAK;CACnC,gBAAgB;EACd,IAAI,SAAS,CAAC,eAAe,SAAS;GACpC,eAAe,UAAU;GACzB,QAAQ,MACN,4EACA,kBAAkB,KAAK,CACzB;EACF;CACF,GAAG,CAAC,KAAK,CAAC;CAEV,IAAI,WAAoB;CACxB,IAAI,CAAC,SAAS,QAAQ,MAAM;EAC1B,MAAM,aAAc,KAAiC;EACrD,IAAI,cAAc,MAChB,WAAW;CAEf;CAEA,OAAO,oBAAC,qBAAD;EAAqB,OAAO;EAAW;CAA8B,CAAA;AAC9E"}
@@ -1,8 +1,8 @@
1
1
  import { DataOverrideContext, useDataOverride } from "./data-override-context.js";
2
- import { HotDataProvider } from "./hot-data-provider.js";
3
2
  import { ListItemRefContext, useListItemRef } from "./list-item-ref-context.js";
4
3
  import { PageReactiveDataRoot, ReactiveRootContext } from "./page-reactive-data-root.js";
5
4
  import "./entity-resolution-context.js";
5
+ import { HotDataProvider } from "./hot-data-provider.js";
6
6
  import { MultipleReactiveDataProvider } from "./multiple-reactive-data-provider.js";
7
7
  import { ReactiveDataProvider } from "./reactive-data-provider.js";
8
8
  import { ReactiveListItemDataProvider } from "./reactive-list-item-data-provider.js";
@@ -16,6 +16,12 @@ export type PageReactiveDataRootProps<TData, TVars extends OperationVariables> =
16
16
  serverVariables?: TVars;
17
17
  /** Server-rendered PAGE_QUERY result. When present and shape-matched, seeded into the cache once. */
18
18
  initialData?: TData | null;
19
+ /**
20
+ * Root fields that failed on the server. Non-empty: `initialData` is not seeded and the first read
21
+ * for these `serverVariables` is `network-only`. Read on mount and when `serverVariables` change;
22
+ * a change to `failedFields` alone, with the same variables, sends nothing.
23
+ */
24
+ failedFields?: readonly string[];
19
25
  /** Reactive vars whose current values are merged into operation variables at render time. */
20
26
  clientVariables?: ClientVariablesMap<TVars>;
21
27
  children: ReactNode;
@@ -31,6 +37,8 @@ export type ReactiveRootContextValue<TData = unknown> = Readonly<{
31
37
  * Error from the underlying useQuery. With non-null root fields (e.g. `cart: Cart!`), a failed
32
38
  * fetch nulls the whole operation's `data` — descendants need this to tell "fetch failed" apart
33
39
  * from "entity is legitimately absent" (docs/storefront-core/reference-free-particle-model.md § Read side — data).
40
+ * A nullable root field that failed on the server reaches here too: the root skips the seed and
41
+ * asks again, and this carries the browser's error beside the partial data.
34
42
  */
35
43
  error: ErrorLike | undefined;
36
44
  /**
@@ -43,14 +51,28 @@ export type ReactiveRootContextValue<TData = unknown> = Readonly<{
43
51
  * Required, not optional, and deliberately so. This flag is the whole of what arms the gate in
44
52
  * `useReactiveEntity`, so a provider that omits it disarms the gate silently — measured: a value
45
53
  * that `satisfies ReactiveRootContextValue` without it reproduces the corrupting payload byte for
46
- * byte on an otherwise-fixed tree, and no test fails. `ReactiveRootContext` is public surface via
47
- * `sdk/data/index.ts`, so that provider need not live in this repo. Making it required moves that
48
- * failure to compile time.
54
+ * byte on an otherwise-fixed tree, and no test fails. `ReactiveRootContext` ships through the
55
+ * package's `./internal/client` entry, so that provider need not live in this repo. Making it
56
+ * required moves that failure to compile time.
49
57
  *
50
58
  * A hand-built context value stands in for a root's *output*, not for the reactive runtime, so it
51
59
  * declares `false` and its subtree keeps gating on nothing.
52
60
  */
53
61
  hasRoot: boolean;
62
+ /**
63
+ * Re-runs the page query against the network with the root's current variables, the ones its
64
+ * latest render passed to `useQuery`, so a caller that changes a reactive variable calls it from an
65
+ * effect, not in the same handler. Sends nothing unless the root's last committed render had every
66
+ * required variable, nor after the root unmounted. Never rejects: a failure of the read arrives on
67
+ * `rootQueryData`, `loading`, and `error`, as it does on a first load, a cancelled refetch is
68
+ * silent, and any other failure is logged by its name and swallowed.
69
+ *
70
+ * Optional, unlike `hasRoot`: a provider that omits it only loses the in-place retry, where a
71
+ * provider that omits `hasRoot` corrupts what `useReactiveEntity` builds. `PageReactiveDataRoot`
72
+ * always sets it, but a provider built outside this repo may not, and a caller then falls back to
73
+ * reloading the page.
74
+ */
75
+ refetch?: () => Promise<void>;
54
76
  }>;
55
77
  export declare const ReactiveRootContext: import("react").Context<Readonly<{
56
78
  /** Current root query data exposed to descendant `ReactiveDataProvider`s. */
@@ -63,6 +85,8 @@ export declare const ReactiveRootContext: import("react").Context<Readonly<{
63
85
  * Error from the underlying useQuery. With non-null root fields (e.g. `cart: Cart!`), a failed
64
86
  * fetch nulls the whole operation's `data` — descendants need this to tell "fetch failed" apart
65
87
  * from "entity is legitimately absent" (docs/storefront-core/reference-free-particle-model.md § Read side — data).
88
+ * A nullable root field that failed on the server reaches here too: the root skips the seed and
89
+ * asks again, and this carries the browser's error beside the partial data.
66
90
  */
67
91
  error: ErrorLike | undefined;
68
92
  /**
@@ -75,15 +99,29 @@ export declare const ReactiveRootContext: import("react").Context<Readonly<{
75
99
  * Required, not optional, and deliberately so. This flag is the whole of what arms the gate in
76
100
  * `useReactiveEntity`, so a provider that omits it disarms the gate silently — measured: a value
77
101
  * that `satisfies ReactiveRootContextValue` without it reproduces the corrupting payload byte for
78
- * byte on an otherwise-fixed tree, and no test fails. `ReactiveRootContext` is public surface via
79
- * `sdk/data/index.ts`, so that provider need not live in this repo. Making it required moves that
80
- * failure to compile time.
102
+ * byte on an otherwise-fixed tree, and no test fails. `ReactiveRootContext` ships through the
103
+ * package's `./internal/client` entry, so that provider need not live in this repo. Making it
104
+ * required moves that failure to compile time.
81
105
  *
82
106
  * A hand-built context value stands in for a root's *output*, not for the reactive runtime, so it
83
107
  * declares `false` and its subtree keeps gating on nothing.
84
108
  */
85
109
  hasRoot: boolean;
110
+ /**
111
+ * Re-runs the page query against the network with the root's current variables, the ones its
112
+ * latest render passed to `useQuery`, so a caller that changes a reactive variable calls it from an
113
+ * effect, not in the same handler. Sends nothing unless the root's last committed render had every
114
+ * required variable, nor after the root unmounted. Never rejects: a failure of the read arrives on
115
+ * `rootQueryData`, `loading`, and `error`, as it does on a first load, a cancelled refetch is
116
+ * silent, and any other failure is logged by its name and swallowed.
117
+ *
118
+ * Optional, unlike `hasRoot`: a provider that omits it only loses the in-place retry, where a
119
+ * provider that omits `hasRoot` corrupts what `useReactiveEntity` builds. `PageReactiveDataRoot`
120
+ * always sets it, but a provider built outside this repo may not, and a caller then falls back to
121
+ * reloading the page.
122
+ */
123
+ refetch?: () => Promise<void>;
86
124
  }>>;
87
- export declare function PageReactiveDataRoot<TData = unknown, TVars extends OperationVariables = OperationVariables>(props: PageReactiveDataRootProps<TData, TVars>): ReactNode;
125
+ export declare function PageReactiveDataRoot<TData = unknown, TVars extends OperationVariables = OperationVariables>({ query, serverVariables, initialData, failedFields, clientVariables, children }: PageReactiveDataRootProps<TData, TVars>): ReactNode;
88
126
  //#endregion
89
127
  //# sourceMappingURL=page-reactive-data-root.d.ts.map
@@ -1,8 +1,11 @@
1
1
  "use client";
2
+ import { loggableErrorName } from "../../utils/loggable-error-name.js";
3
+ import { getFailedFields } from "./failed-fields.js";
2
4
  import { useApolloClient, useQuery } from "@apollo/client/react";
3
- import { createContext, useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
5
+ import { createContext, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
6
  import { jsx } from "react/jsx-runtime";
5
7
  import { Kind } from "graphql";
8
+ import { canonicalStringify } from "@apollo/client/utilities";
6
9
  //#region src/sdk/data/page-reactive-data-root.tsx
7
10
  const REACTIVE_VAR_REGISTRY = {};
8
11
  function resolveReactiveVar(value) {
@@ -35,6 +38,16 @@ function getTopLevelFieldNames(query) {
35
38
  for (const sel of op.selectionSet.selections) if (sel.kind === Kind.FIELD) names.add((sel.alias ?? sel.name).value);
36
39
  return names;
37
40
  }
41
+ function stillShowsFailure(data, error) {
42
+ return data == null || getFailedFields(data, error).length > 0;
43
+ }
44
+ function withEntry(errors, key, error) {
45
+ if (errors.get(key) === error) return errors;
46
+ const next = new Map(errors);
47
+ if (error) next.set(key, error);
48
+ else next.delete(key);
49
+ return next;
50
+ }
38
51
  function shapeMatches(query, data) {
39
52
  if (data == null || typeof data !== "object") return false;
40
53
  const expected = getTopLevelFieldNames(query);
@@ -96,10 +109,25 @@ function useClientVariableValues(clientVariables) {
96
109
  useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
97
110
  return lastSnapshotRef.current?.value ?? {};
98
111
  }
99
- function PageReactiveDataRoot(props) {
100
- const { query, serverVariables, initialData, clientVariables, children } = props;
112
+ /**
113
+ * Apollo rejects a refetch that ends with no value, as when the root unmounts mid-flight, with a
114
+ * `DOMException` named `AbortError`, or a plain `Error` of that name where there is no `DOMException`.
115
+ * A property check, since a `DOMException` is not always an `Error` instance (jsdom). The read is
116
+ * guarded because a `.name` getter can throw, and `refetch` must not reject.
117
+ */
118
+ function isAbort(failure) {
119
+ if (typeof failure !== "object" || failure === null) return false;
120
+ try {
121
+ return failure.name === "AbortError";
122
+ } catch {
123
+ return false;
124
+ }
125
+ }
126
+ function PageReactiveDataRoot({ query, serverVariables, initialData, failedFields, clientVariables, children }) {
101
127
  const client = useApolloClient();
102
- const seededRef = useRef(false);
128
+ const seededKeysRef = useRef(/* @__PURE__ */ new Set());
129
+ const hasFailedFields = failedFields != null && failedFields.length > 0;
130
+ const serverVariablesKey = canonicalStringify(serverVariables ?? null);
103
131
  const clientValues = useClientVariableValues(clientVariables);
104
132
  const mergedVariables = {
105
133
  ...serverVariables ?? {},
@@ -112,7 +140,8 @@ function PageReactiveDataRoot(props) {
112
140
  return v !== null && v !== void 0;
113
141
  });
114
142
  useEffect(() => {
115
- if (seededRef.current) return;
143
+ if (seededKeysRef.current.has(serverVariablesKey)) return;
144
+ if (hasFailedFields) return;
116
145
  if (!serverVariables || initialData == null) return;
117
146
  if (!shapeMatches(query, initialData)) return;
118
147
  client.cache.writeQuery({
@@ -120,29 +149,76 @@ function PageReactiveDataRoot(props) {
120
149
  variables: serverVariables,
121
150
  data: initialData
122
151
  });
123
- seededRef.current = true;
152
+ seededKeysRef.current.add(serverVariablesKey);
124
153
  }, [
125
154
  client,
126
155
  query,
127
156
  serverVariables,
128
- initialData
157
+ serverVariablesKey,
158
+ initialData,
159
+ hasFailedFields
129
160
  ]);
130
- const { data, loading, error } = useQuery(query, {
161
+ const networkReadKeysRef = useRef(/* @__PURE__ */ new Set());
162
+ const nextFetchPolicy = useCallback((_current, { reason }) => {
163
+ if (!hasFailedFields) return "cache-first";
164
+ if (reason === "variables-changed" && !networkReadKeysRef.current.has(serverVariablesKey)) return "network-only";
165
+ networkReadKeysRef.current.add(serverVariablesKey);
166
+ return "cache-first";
167
+ }, [hasFailedFields, serverVariablesKey]);
168
+ const { data, loading, error, refetch: refetchQuery } = useQuery(query, {
131
169
  variables: mergedVariables,
132
- fetchPolicy: "cache-first",
170
+ fetchPolicy: hasFailedFields ? "network-only" : "cache-first",
171
+ nextFetchPolicy,
172
+ errorPolicy: "all",
133
173
  skip: !hasAllRequiredVariables
134
174
  });
175
+ const variablesKey = canonicalStringify(mergedVariables);
176
+ const [networkErrors, setNetworkErrors] = useState(() => /* @__PURE__ */ new Map());
177
+ const storedError = networkErrors.get(variablesKey);
178
+ const storedErrorApplies = storedError !== void 0 && !loading && stillShowsFailure(data, storedError);
179
+ useEffect(() => {
180
+ if (error) setNetworkErrors((errors) => withEntry(errors, variablesKey, error));
181
+ else if (loading || storedError !== void 0 && !storedErrorApplies) setNetworkErrors((errors) => withEntry(errors, variablesKey, void 0));
182
+ }, [
183
+ error,
184
+ loading,
185
+ variablesKey,
186
+ storedError,
187
+ storedErrorApplies
188
+ ]);
189
+ const rootError = error ?? (storedErrorApplies ? storedError : void 0);
190
+ const latestRef = useRef(null);
191
+ useLayoutEffect(() => {
192
+ latestRef.current = {
193
+ refetchQuery,
194
+ hasAllRequiredVariables
195
+ };
196
+ });
197
+ useLayoutEffect(() => () => {
198
+ latestRef.current = null;
199
+ }, []);
200
+ const refetch = useCallback(async () => {
201
+ const latest = latestRef.current;
202
+ if (!latest?.hasAllRequiredVariables) return;
203
+ try {
204
+ await latest.refetchQuery();
205
+ } catch (error_) {
206
+ if (!isAbort(error_)) console.error("[PageReactiveDataRoot] refetch failed", loggableErrorName(error_));
207
+ }
208
+ }, []);
135
209
  const contextValue = useMemo(() => ({
136
210
  rootQueryData: data ?? void 0,
137
211
  loading,
138
212
  hasAllRequiredVariables,
139
- error,
140
- hasRoot: true
213
+ error: rootError,
214
+ hasRoot: true,
215
+ refetch
141
216
  }), [
142
217
  data,
143
218
  loading,
144
219
  hasAllRequiredVariables,
145
- error
220
+ rootError,
221
+ refetch
146
222
  ]);
147
223
  return /* @__PURE__ */ jsx(ReactiveRootContext.Provider, {
148
224
  value: contextValue,