@vtex/faststore 0.0.5 → 0.0.6

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 (232) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/actions.json +181 -0
  3. package/dist/catalog.json +1543 -495
  4. package/dist/client.d.ts +2 -1
  5. package/dist/client.js +2 -1
  6. package/dist/components.d.ts +28 -7
  7. package/dist/components.js +31 -12
  8. package/dist/docs/access-code-sign-in.md +1 -1
  9. package/dist/docs/address-summary.md +24 -3
  10. package/dist/docs/alert-dialog.md +204 -0
  11. package/dist/docs/billing-address.md +284 -0
  12. package/dist/docs/card-entry.md +250 -0
  13. package/dist/docs/cardholder-document.md +142 -0
  14. package/dist/docs/client-preferences-fieldset.md +148 -0
  15. package/dist/docs/client-preferences.md +2 -1
  16. package/dist/docs/client-profile-fieldset.md +184 -0
  17. package/dist/docs/client-profile.md +1 -1
  18. package/dist/docs/document-field.md +58 -4
  19. package/dist/docs/field.md +317 -0
  20. package/dist/docs/form.md +307 -0
  21. package/dist/docs/identity-edit.md +6 -6
  22. package/dist/docs/identity-summary.md +5 -5
  23. package/dist/docs/installments.md +248 -0
  24. package/dist/docs/my-account-profile.md +1 -0
  25. package/dist/docs/order-placed-payment-summary.md +138 -0
  26. package/dist/docs/payment-method-badges.md +232 -0
  27. package/dist/docs/payment-methods.md +1 -0
  28. package/dist/docs/payment.md +218 -0
  29. package/dist/docs/phone-field.md +58 -4
  30. package/dist/docs/place-order.md +157 -0
  31. package/dist/docs/save-card.md +186 -0
  32. package/dist/docs/saved-cards.md +218 -0
  33. package/dist/docs/select.md +465 -0
  34. package/dist/docs/shipping-address.md +110 -258
  35. package/dist/index.d.ts +4 -1
  36. package/dist/internal/client.d.ts +3 -1
  37. package/dist/internal/client.js +3 -1
  38. package/dist/matter/access-code-sign-in/access-code-sign-in.js +9 -8
  39. package/dist/matter/access-code-sign-in/access-code-sign-in.js.map +1 -1
  40. package/dist/matter/billing-address/billing-address.d.ts +165 -0
  41. package/dist/matter/billing-address/billing-address.js +408 -0
  42. package/dist/matter/billing-address/billing-address.js.map +1 -0
  43. package/dist/matter/card-entry/card-capture.d.ts +25 -0
  44. package/dist/matter/card-entry/card-capture.js +25 -0
  45. package/dist/matter/card-entry/card-capture.js.map +1 -0
  46. package/dist/matter/card-entry/card-entry.d.ts +120 -0
  47. package/dist/matter/card-entry/card-entry.js +832 -0
  48. package/dist/matter/card-entry/card-entry.js.map +1 -0
  49. package/dist/matter/cardholder-document/cardholder-document.d.ts +45 -0
  50. package/dist/matter/cardholder-document/cardholder-document.js +70 -0
  51. package/dist/matter/cardholder-document/cardholder-document.js.map +1 -0
  52. package/dist/matter/client-preferences/client-preferences-fieldset.d.ts +77 -0
  53. package/dist/matter/client-preferences/client-preferences-fieldset.js +299 -0
  54. package/dist/matter/client-preferences/client-preferences-fieldset.js.map +1 -0
  55. package/dist/matter/client-preferences/client-preferences.d.ts +3 -5
  56. package/dist/matter/client-preferences/client-preferences.js +6 -6
  57. package/dist/matter/client-preferences/client-preferences.js.map +1 -1
  58. package/dist/matter/client-profile/client-profile.d.ts +4 -6
  59. package/dist/matter/client-profile/client-profile.js +9 -6
  60. package/dist/matter/client-profile/client-profile.js.map +1 -1
  61. package/dist/matter/client-profile-form/client-profile-fieldset.d.ts +119 -0
  62. package/dist/matter/client-profile-form/client-profile-fieldset.js +488 -0
  63. package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -0
  64. package/dist/matter/identity-edit/identity-edit.js +2 -2
  65. package/dist/matter/identity-edit/identity-edit.js.map +1 -1
  66. package/dist/matter/identity-summary/identity-summary.js +2 -2
  67. package/dist/matter/identity-summary/identity-summary.js.map +1 -1
  68. package/dist/matter/installments/installments-select.d.ts +42 -0
  69. package/dist/matter/installments/installments-select.js +167 -0
  70. package/dist/matter/installments/installments-select.js.map +1 -0
  71. package/dist/matter/installments/installments.d.ts +58 -0
  72. package/dist/matter/installments/installments.js +176 -0
  73. package/dist/matter/installments/installments.js.map +1 -0
  74. package/dist/matter/installments/use-installments-selection.d.ts +29 -0
  75. package/dist/matter/installments/use-installments-selection.js +95 -0
  76. package/dist/matter/installments/use-installments-selection.js.map +1 -0
  77. package/dist/matter/my-account-profile/my-account-profile.js +4 -4
  78. package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
  79. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +63 -0
  80. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +305 -0
  81. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -0
  82. package/dist/matter/payment/payment-card-capture.d.ts +19 -0
  83. package/dist/matter/payment/payment-card-capture.js +129 -0
  84. package/dist/matter/payment/payment-card-capture.js.map +1 -0
  85. package/dist/matter/payment/payment-controller.d.ts +24 -0
  86. package/dist/matter/payment/payment-controller.js +107 -0
  87. package/dist/matter/payment/payment-controller.js.map +1 -0
  88. package/dist/matter/payment/payment-method-badges.d.ts +57 -0
  89. package/dist/matter/payment/payment-method-badges.js +192 -0
  90. package/dist/matter/payment/payment-method-badges.js.map +1 -0
  91. package/dist/matter/payment/payment.d.ts +89 -0
  92. package/dist/matter/payment/payment.js +263 -0
  93. package/dist/matter/payment/payment.js.map +1 -0
  94. package/dist/matter/payment/use-payment-method-selection.d.ts +302 -0
  95. package/dist/matter/payment/use-payment-method-selection.js +458 -0
  96. package/dist/matter/payment/use-payment-method-selection.js.map +1 -0
  97. package/dist/matter/payment/use-payment-submit-gate.d.ts +111 -0
  98. package/dist/matter/payment/use-payment-submit-gate.js +144 -0
  99. package/dist/matter/payment/use-payment-submit-gate.js.map +1 -0
  100. package/dist/matter/place-order/place-order.d.ts +74 -0
  101. package/dist/matter/place-order/place-order.js +379 -0
  102. package/dist/matter/place-order/place-order.js.map +1 -0
  103. package/dist/matter/product-list/product-list.d.ts +1 -0
  104. package/dist/matter/product-list/product-list.js.map +1 -1
  105. package/dist/matter/product-shelf/product-shelf.d.ts +1 -0
  106. package/dist/matter/product-shelf/product-shelf.js +2 -0
  107. package/dist/matter/product-shelf/product-shelf.js.map +1 -1
  108. package/dist/matter/region/region.js +1 -1
  109. package/dist/matter/region/region.js.map +1 -1
  110. package/dist/matter/save-card/save-card.d.ts +49 -0
  111. package/dist/matter/save-card/save-card.js +188 -0
  112. package/dist/matter/save-card/save-card.js.map +1 -0
  113. package/dist/matter/saved-cards/saved-cards.d.ts +79 -0
  114. package/dist/matter/saved-cards/saved-cards.js +387 -0
  115. package/dist/matter/saved-cards/saved-cards.js.map +1 -0
  116. package/dist/matter/shipping-address/country-descriptors.js +20 -10
  117. package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
  118. package/dist/matter/shipping-address/shipping-address.d.ts +113 -174
  119. package/dist/matter/shipping-address/shipping-address.js +480 -791
  120. package/dist/matter/shipping-address/shipping-address.js.map +1 -1
  121. package/dist/matter/variant-selector/variant-selector.js +1 -1
  122. package/dist/particles/address-summary/address-summary.d.ts +24 -0
  123. package/dist/particles/address-summary/address-summary.js +83 -2
  124. package/dist/particles/address-summary/address-summary.js.map +1 -1
  125. package/dist/particles/alert-dialog/alert-dialog.d.ts +68 -0
  126. package/dist/particles/alert-dialog/alert-dialog.js +187 -0
  127. package/dist/particles/alert-dialog/alert-dialog.js.map +1 -0
  128. package/dist/particles/checkbox/checkbox.d.ts +7 -3
  129. package/dist/particles/checkbox/checkbox.js +4 -1
  130. package/dist/particles/checkbox/checkbox.js.map +1 -1
  131. package/dist/particles/document-field/document-field.d.ts +48 -5
  132. package/dist/particles/document-field/document-field.js +189 -32
  133. package/dist/particles/document-field/document-field.js.map +1 -1
  134. package/dist/particles/field/bound-field-registry.js +52 -0
  135. package/dist/particles/field/bound-field-registry.js.map +1 -0
  136. package/dist/particles/field/field.d.ts +116 -0
  137. package/dist/particles/field/field.js +497 -6
  138. package/dist/particles/field/field.js.map +1 -1
  139. package/dist/particles/fieldset/fieldset.js +2 -2
  140. package/dist/particles/fieldset/fieldset.js.map +1 -1
  141. package/dist/particles/form/form.d.ts +104 -0
  142. package/dist/particles/form/form.js +485 -0
  143. package/dist/particles/form/form.js.map +1 -0
  144. package/dist/particles/phone-field/phone-field.d.ts +38 -10
  145. package/dist/particles/phone-field/phone-field.js +183 -39
  146. package/dist/particles/phone-field/phone-field.js.map +1 -1
  147. package/dist/particles/postal-code-field/postal-code-field.js +2 -2
  148. package/dist/particles/postal-code-field/postal-code-field.js.map +1 -1
  149. package/dist/particles/select/field-select.d.ts +43 -0
  150. package/dist/particles/select/field-select.js +150 -0
  151. package/dist/particles/select/field-select.js.map +1 -0
  152. package/dist/particles/select/select.d.ts +2 -0
  153. package/dist/particles/select/select.js.map +1 -1
  154. package/dist/particles/shared/compose-refs.js +21 -0
  155. package/dist/particles/shared/compose-refs.js.map +1 -0
  156. package/dist/schema.graphql +701 -2
  157. package/dist/sdk/actions/analytics-action-provider.d.ts +15 -0
  158. package/dist/sdk/actions/analytics-action-provider.js +19 -0
  159. package/dist/sdk/actions/analytics-action-provider.js.map +1 -0
  160. package/dist/sdk/actions/index.d.ts +3 -1
  161. package/dist/sdk/actions/index.js +3 -1
  162. package/dist/sdk/actions/payment-action-provider.d.ts +22 -0
  163. package/dist/sdk/actions/payment-action-provider.js +26 -0
  164. package/dist/sdk/actions/payment-action-provider.js.map +1 -0
  165. package/dist/sdk/actions/types.d.ts +6 -2
  166. package/dist/sdk/analytics/analytics-destination.d.ts +43 -0
  167. package/dist/sdk/analytics/analytics-destination.js +90 -0
  168. package/dist/sdk/analytics/analytics-destination.js.map +1 -0
  169. package/dist/sdk/analytics/use-analytics-actions.d.ts +57 -0
  170. package/dist/sdk/analytics/use-analytics-actions.js +108 -0
  171. package/dist/sdk/analytics/use-analytics-actions.js.map +1 -0
  172. package/dist/sdk/cart/use-cart-actions.js +1 -1
  173. package/dist/sdk/data/entity-resolution-context.js +31 -0
  174. package/dist/sdk/data/entity-resolution-context.js.map +1 -0
  175. package/dist/sdk/data/entity-subscriber.js +18 -8
  176. package/dist/sdk/data/entity-subscriber.js.map +1 -1
  177. package/dist/sdk/data/index.js +1 -0
  178. package/dist/sdk/data/page-reactive-data-root.d.ts +36 -0
  179. package/dist/sdk/data/page-reactive-data-root.js +4 -2
  180. package/dist/sdk/data/page-reactive-data-root.js.map +1 -1
  181. package/dist/sdk/data/reactive-data-provider.js +7 -3
  182. package/dist/sdk/data/reactive-data-provider.js.map +1 -1
  183. package/dist/sdk/data/reactive-list-item-data-provider.js +12 -4
  184. package/dist/sdk/data/reactive-list-item-data-provider.js.map +1 -1
  185. package/dist/sdk/data/use-reactive-data-settled.js +39 -0
  186. package/dist/sdk/data/use-reactive-data-settled.js.map +1 -0
  187. package/dist/sdk/data/use-reactive-entity.js +5 -1
  188. package/dist/sdk/data/use-reactive-entity.js.map +1 -1
  189. package/dist/sdk/generated/fragment-types.js +2 -0
  190. package/dist/sdk/generated/fragment-types.js.map +1 -1
  191. package/dist/sdk/generated/graphql.js.map +1 -1
  192. package/dist/sdk/i18n/messages/en-US.js +3 -0
  193. package/dist/sdk/i18n/messages/en-US.json +3 -0
  194. package/dist/sdk/i18n/messages/en-US.json.d.ts +3 -0
  195. package/dist/sdk/i18n/messages/es-ES.js +3 -0
  196. package/dist/sdk/i18n/messages/es-ES.json +3 -0
  197. package/dist/sdk/i18n/messages/pt-BR.js +3 -0
  198. package/dist/sdk/i18n/messages/pt-BR.json +3 -0
  199. package/dist/sdk/identity/use-identity-actions.js +3 -2
  200. package/dist/sdk/identity/use-identity-actions.js.map +1 -1
  201. package/dist/sdk/payment/gateway-origin.js +35 -0
  202. package/dist/sdk/payment/gateway-origin.js.map +1 -0
  203. package/dist/sdk/payment/gateway.d.ts +85 -0
  204. package/dist/sdk/payment/gateway.js +183 -0
  205. package/dist/sdk/payment/gateway.js.map +1 -0
  206. package/dist/sdk/payment/operations.d.ts +52 -0
  207. package/dist/sdk/payment/operations.js +223 -0
  208. package/dist/sdk/payment/operations.js.map +1 -0
  209. package/dist/sdk/payment/security-code-mask.js +71 -0
  210. package/dist/sdk/payment/security-code-mask.js.map +1 -0
  211. package/dist/sdk/payment/submission-registry.d.ts +33 -0
  212. package/dist/sdk/payment/submission-registry.js +58 -0
  213. package/dist/sdk/payment/submission-registry.js.map +1 -0
  214. package/dist/sdk/payment/use-payment-actions.d.ts +78 -0
  215. package/dist/sdk/payment/use-payment-actions.js +276 -0
  216. package/dist/sdk/payment/use-payment-actions.js.map +1 -0
  217. package/dist/sdk/payment/use-place-order.d.ts +65 -0
  218. package/dist/sdk/payment/use-place-order.js +640 -0
  219. package/dist/sdk/payment/use-place-order.js.map +1 -0
  220. package/dist/sdk/shipping/use-shipping-actions.js +1 -1
  221. package/dist/utils/loggable-error-name.js +49 -0
  222. package/dist/utils/loggable-error-name.js.map +1 -0
  223. package/dist/utils/resolve-authored-copy.js +18 -0
  224. package/dist/utils/resolve-authored-copy.js.map +1 -0
  225. package/dist/utils/substitute-template.js +33 -0
  226. package/dist/utils/substitute-template.js.map +1 -0
  227. package/package.json +2 -1
  228. package/src/styles/tailwind.css +34 -0
  229. package/dist/docs/client-profile-form.md +0 -192
  230. package/dist/matter/client-profile-form/client-profile-form.d.ts +0 -126
  231. package/dist/matter/client-profile-form/client-profile-form.js +0 -662
  232. package/dist/matter/client-profile-form/client-profile-form.js.map +0 -1
@@ -33,6 +33,24 @@ export type ReactiveRootContextValue<TData = unknown> = Readonly<{
33
33
  * from "entity is legitimately absent" (docs/storefront-core/reference-free-particle-model.md § Read side — data).
34
34
  */
35
35
  error: ErrorLike | undefined;
36
+ /**
37
+ * True only under a real `PageReactiveDataRoot`. Every other field above is ambiguous between
38
+ * "no root at all" and "a root with nothing to give right now" — `rootQueryData: undefined` and
39
+ * `loading: false` describe both the context default and a root whose query is skipped for want
40
+ * of a reactive variable. `useReactiveEntity` needs that distinction to tell a placeholder slice
41
+ * apart from the only slice there is.
42
+ *
43
+ * Required, not optional, and deliberately so. This flag is the whole of what arms the gate in
44
+ * `useReactiveEntity`, so a provider that omits it disarms the gate silently — measured: a value
45
+ * 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.
49
+ *
50
+ * A hand-built context value stands in for a root's *output*, not for the reactive runtime, so it
51
+ * declares `false` and its subtree keeps gating on nothing.
52
+ */
53
+ hasRoot: boolean;
36
54
  }>;
37
55
  export declare const ReactiveRootContext: import("react").Context<Readonly<{
38
56
  /** Current root query data exposed to descendant `ReactiveDataProvider`s. */
@@ -47,6 +65,24 @@ export declare const ReactiveRootContext: import("react").Context<Readonly<{
47
65
  * from "entity is legitimately absent" (docs/storefront-core/reference-free-particle-model.md § Read side — data).
48
66
  */
49
67
  error: ErrorLike | undefined;
68
+ /**
69
+ * True only under a real `PageReactiveDataRoot`. Every other field above is ambiguous between
70
+ * "no root at all" and "a root with nothing to give right now" — `rootQueryData: undefined` and
71
+ * `loading: false` describe both the context default and a root whose query is skipped for want
72
+ * of a reactive variable. `useReactiveEntity` needs that distinction to tell a placeholder slice
73
+ * apart from the only slice there is.
74
+ *
75
+ * Required, not optional, and deliberately so. This flag is the whole of what arms the gate in
76
+ * `useReactiveEntity`, so a provider that omits it disarms the gate silently — measured: a value
77
+ * 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.
81
+ *
82
+ * A hand-built context value stands in for a root's *output*, not for the reactive runtime, so it
83
+ * declares `false` and its subtree keeps gating on nothing.
84
+ */
85
+ hasRoot: boolean;
50
86
  }>>;
51
87
  export declare function PageReactiveDataRoot<TData = unknown, TVars extends OperationVariables = OperationVariables>(props: PageReactiveDataRootProps<TData, TVars>): ReactNode;
52
88
  //#endregion
@@ -15,7 +15,8 @@ const ReactiveRootContext = createContext({
15
15
  rootQueryData: void 0,
16
16
  loading: false,
17
17
  hasAllRequiredVariables: false,
18
- error: void 0
18
+ error: void 0,
19
+ hasRoot: false
19
20
  });
20
21
  function getOperationDefinition(query) {
21
22
  for (const definition of query.definitions) if (definition.kind === Kind.OPERATION_DEFINITION) return definition;
@@ -135,7 +136,8 @@ function PageReactiveDataRoot(props) {
135
136
  rootQueryData: data ?? void 0,
136
137
  loading,
137
138
  hasAllRequiredVariables,
138
- error
139
+ error,
140
+ hasRoot: true
139
141
  }), [
140
142
  data,
141
143
  loading,
@@ -1 +1 @@
1
- {"version":3,"file":"page-reactive-data-root.js","names":[],"sources":["../../../src/sdk/data/page-reactive-data-root.tsx"],"sourcesContent":["'use client';\n\nimport type { ErrorLike, OperationVariables, ReactiveVar, TypedDocumentNode } from '@apollo/client';\nimport { useApolloClient, useQuery } from '@apollo/client/react';\nimport { type DocumentNode, Kind, type OperationDefinitionNode } from 'graphql';\nimport { createContext, type ReactNode, useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\n// Reactive vars resolvable by name from a Server Component. Functions cannot cross the\n// Server→Client boundary in RSC, so the compiler emits string keys and this component resolves\n// them at runtime. New reactive vars must be registered here to be addressable from generated\n// pages. Currently empty: cart context travels on the transport, so no cart entry is needed\n// (docs/storefront-core/reference-cart.md § Cart context).\nconst REACTIVE_VAR_REGISTRY: Record<string, ReactiveVar<unknown>> = {};\n\n/**\n * Compile-time-stable map keyed by the operation's variable names. Each value is either an Apollo\n * reactive variable (`makeVar`) or the string name of one registered in REACTIVE_VAR_REGISTRY —\n * Server Components must pass strings because functions cannot cross the RSC boundary. Vars that\n * have not yet resolved should hold `null`; the runtime treats both `null` and `undefined` as\n * \"not yet ready\" and keeps `useQuery` skipped.\n */\nexport type ClientVariablesMap<TVars> = Partial<{\n [K in keyof TVars]: ReactiveVar<TVars[K] | null> | string;\n}>;\n\nfunction resolveReactiveVar(value: ReactiveVar<unknown> | string): ReactiveVar<unknown> | undefined {\n if (typeof value === 'function') return value;\n const resolved = REACTIVE_VAR_REGISTRY[value];\n if (!resolved) {\n throw new Error(\n `PageReactiveDataRoot: clientVariables entry \"${value}\" is not registered. Add it to REACTIVE_VAR_REGISTRY in page-reactive-data-root.tsx.`,\n );\n }\n return resolved;\n}\n\nexport type PageReactiveDataRootProps<TData, TVars extends OperationVariables> = Readonly<{\n /** The composed PAGE_QUERY operation document. */\n query: TypedDocumentNode<TData, TVars>;\n /** Variables the server used when calling fetchPageData. Only used to seed the client cache. */\n serverVariables?: TVars;\n /** Server-rendered PAGE_QUERY result. When present and shape-matched, seeded into the cache once. */\n initialData?: TData | null;\n /** Reactive vars whose current values are merged into operation variables at render time. */\n clientVariables?: ClientVariablesMap<TVars>;\n children: ReactNode;\n}>;\n\nexport type ReactiveRootContextValue<TData = unknown> = Readonly<{\n /** Current root query data exposed to descendant `ReactiveDataProvider`s. */\n rootQueryData: TData | undefined;\n /** Whether the underlying useQuery is currently in flight. */\n loading: boolean;\n /** False when at least one required variable has not been provided yet. */\n hasAllRequiredVariables: boolean;\n /**\n * Error from the underlying useQuery. With non-null root fields (e.g. `cart: Cart!`), a failed\n * fetch nulls the whole operation's `data` — descendants need this to tell \"fetch failed\" apart\n * from \"entity is legitimately absent\" (docs/storefront-core/reference-free-particle-model.md § Read side — data).\n */\n error: ErrorLike | undefined;\n}>;\n\nexport const ReactiveRootContext = createContext<ReactiveRootContextValue>({\n rootQueryData: undefined,\n loading: false,\n hasAllRequiredVariables: false,\n error: undefined,\n});\n\nfunction getOperationDefinition(query: DocumentNode): OperationDefinitionNode | undefined {\n for (const definition of query.definitions) {\n if (definition.kind === Kind.OPERATION_DEFINITION) return definition;\n }\n return undefined;\n}\n\nfunction getRequiredVariableNames(query: DocumentNode): string[] {\n const op = getOperationDefinition(query);\n if (!op?.variableDefinitions) return [];\n const required: string[] = [];\n for (const def of op.variableDefinitions) {\n if (def.type.kind === Kind.NON_NULL_TYPE && def.defaultValue === undefined) {\n required.push(def.variable.name.value);\n }\n }\n return required;\n}\n\nfunction getTopLevelFieldNames(query: DocumentNode): Set<string> {\n const op = getOperationDefinition(query);\n const names = new Set<string>();\n if (!op) return names;\n for (const sel of op.selectionSet.selections) {\n if (sel.kind === Kind.FIELD) {\n names.add((sel.alias ?? sel.name).value);\n }\n }\n return names;\n}\n\nfunction shapeMatches(query: DocumentNode, data: unknown): boolean {\n if (data == null || typeof data !== 'object') return false;\n const expected = getTopLevelFieldNames(query);\n if (expected.size === 0) return false;\n const actual = data as Record<string, unknown>;\n // Require AT LEAST ONE top-level field name to be present in `data`. The operation's full\n // top-level shape may include fields the server did not return; partial seeds still help. A total\n // miss (no overlap at all) signals a shape mismatch and we skip the seed.\n for (const key of expected) {\n if (key in actual) return true;\n }\n return false;\n}\n\n/**\n * Reads the current values of every reactive var in `clientVariables` into a plain object via a\n * single `useSyncExternalStore` call (no hooks-in-loop). Apollo's `ReactiveVar.onNextChange` fires\n * once per registration, so the subscriber re-attaches after each notification to stay listening.\n */\nfunction useClientVariableValues<TVars>(clientVariables: ClientVariablesMap<TVars> | undefined): Partial<TVars> {\n // Keep the latest map identity reachable from inside subscribe()/getSnapshot() without forcing\n // useSyncExternalStore to tear down the underlying subscription.\n const varsRef = useRef(clientVariables);\n // react-doctor-disable-next-line no-ref-current-in-render -- keeps the latest map reachable from the stable subscribe()/getSnapshot() callbacks below\n varsRef.current = clientVariables;\n\n const subscribe = useCallback((notify: () => void): (() => void) => {\n const cv = varsRef.current;\n if (!cv) return () => {};\n\n const activeUnsubs: Array<() => void> = [];\n let cancelled = false;\n\n const attach = (rv: ReactiveVar<unknown>): void => {\n if (cancelled) return;\n const unsub = rv.onNextChange(() => {\n notify();\n // onNextChange fires only once — re-attach to keep listening for subsequent changes.\n attach(rv);\n });\n activeUnsubs.push(unsub);\n };\n\n for (const rv of Object.values(cv)) {\n if (rv === undefined || rv === null) continue;\n const resolved = resolveReactiveVar(rv as ReactiveVar<unknown> | string);\n if (resolved) attach(resolved);\n }\n\n return () => {\n cancelled = true;\n for (const u of activeUnsubs) u();\n };\n }, []);\n\n const lastSnapshotRef = useRef<{ json: string; value: Record<string, unknown> } | null>(null);\n\n const getSnapshot = (): string => {\n const cv = varsRef.current;\n const snap: Record<string, unknown> = {};\n if (cv) {\n for (const [key, reactiveVar] of Object.entries(cv)) {\n if (reactiveVar === undefined || reactiveVar === null) continue;\n const resolved = resolveReactiveVar(reactiveVar as ReactiveVar<unknown> | string);\n if (!resolved) continue;\n const value = resolved();\n if (value !== null && value !== undefined) snap[key] = value;\n }\n }\n const json = JSON.stringify(snap);\n // useSyncExternalStore expects a stable snapshot reference until the value changes (Object.is\n // identity check) — return the cached `json` whenever the contents are unchanged.\n if (lastSnapshotRef.current && lastSnapshotRef.current.json === json) {\n return lastSnapshotRef.current.json;\n }\n lastSnapshotRef.current = { json, value: snap };\n return json;\n };\n\n useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n return (lastSnapshotRef.current?.value ?? {}) as Partial<TVars>;\n}\n\nexport function PageReactiveDataRoot<TData = unknown, TVars extends OperationVariables = OperationVariables>(\n props: PageReactiveDataRootProps<TData, TVars>,\n): ReactNode {\n const { query, serverVariables, initialData, clientVariables, children } = props;\n const client = useApolloClient();\n const seededRef = useRef(false);\n\n const clientValues = useClientVariableValues<TVars>(clientVariables);\n const mergedVariables = { ...(serverVariables ?? ({} as TVars)), ...clientValues } as TVars;\n\n const requiredNames = getRequiredVariableNames(query);\n const mergedRecord = mergedVariables as Record<string, unknown>;\n const hasAllRequiredVariables = requiredNames.every((name) => {\n const v = mergedRecord[name];\n return v !== null && v !== undefined;\n });\n\n // Ref- and shape-guarded one-time cache seed from server props. We use `useEffect` (not\n // `useLayoutEffect`) to avoid the SSR-pass warning emitted by React 19 — the resulting \"first\n // render shows fallbackData, second render shows cache slice\" behaviour is acceptable because\n // initialData is the same value `ReactiveDataProvider` falls back to anyway.\n useEffect(() => {\n if (seededRef.current) return;\n if (!serverVariables || initialData == null) return;\n if (!shapeMatches(query, initialData)) return;\n client.cache.writeQuery({ query, variables: serverVariables, data: initialData });\n seededRef.current = true;\n }, [client, query, serverVariables, initialData]);\n\n const { data, loading, error } = useQuery(query, {\n variables: mergedVariables,\n fetchPolicy: 'cache-first',\n skip: !hasAllRequiredVariables,\n });\n\n const contextValue: ReactiveRootContextValue<TData> = useMemo(\n () => ({\n rootQueryData: (data ?? undefined) as TData | undefined,\n loading,\n hasAllRequiredVariables,\n error,\n }),\n [data, loading, hasAllRequiredVariables, error],\n );\n\n return (\n <ReactiveRootContext.Provider value={contextValue as ReactiveRootContextValue}>\n {children}\n </ReactiveRootContext.Provider>\n );\n}\n"],"mappings":";;;;;;AAYA,MAAM,wBAA8D,CAAC;AAarE,SAAS,mBAAmB,OAAwE;CAClG,IAAI,OAAO,UAAU,YAAY,OAAO;CACxC,MAAM,WAAW,sBAAsB;CACvC,IAAI,CAAC,UACH,MAAM,IAAI,MACR,gDAAgD,MAAM,qFACxD;CAEF,OAAO;AACT;AA6BA,MAAa,sBAAsB,cAAwC;CACzE,eAAe,KAAA;CACf,SAAS;CACT,yBAAyB;CACzB,OAAO,KAAA;AACT,CAAC;AAED,SAAS,uBAAuB,OAA0D;CACxF,KAAK,MAAM,cAAc,MAAM,aAC7B,IAAI,WAAW,SAAS,KAAK,sBAAsB,OAAO;AAG9D;AAEA,SAAS,yBAAyB,OAA+B;CAC/D,MAAM,KAAK,uBAAuB,KAAK;CACvC,IAAI,CAAC,IAAI,qBAAqB,OAAO,CAAC;CACtC,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,OAAO,GAAG,qBACnB,IAAI,IAAI,KAAK,SAAS,KAAK,iBAAiB,IAAI,iBAAiB,KAAA,GAC/D,SAAS,KAAK,IAAI,SAAS,KAAK,KAAK;CAGzC,OAAO;AACT;AAEA,SAAS,sBAAsB,OAAkC;CAC/D,MAAM,KAAK,uBAAuB,KAAK;CACvC,MAAM,wBAAQ,IAAI,IAAY;CAC9B,IAAI,CAAC,IAAI,OAAO;CAChB,KAAK,MAAM,OAAO,GAAG,aAAa,YAChC,IAAI,IAAI,SAAS,KAAK,OACpB,MAAM,KAAK,IAAI,SAAS,IAAI,KAAA,CAAM,KAAK;CAG3C,OAAO;AACT;AAEA,SAAS,aAAa,OAAqB,MAAwB;CACjE,IAAI,QAAQ,QAAQ,OAAO,SAAS,UAAU,OAAO;CACrD,MAAM,WAAW,sBAAsB,KAAK;CAC5C,IAAI,SAAS,SAAS,GAAG,OAAO;CAChC,MAAM,SAAS;CAIf,KAAK,MAAM,OAAO,UAChB,IAAI,OAAO,QAAQ,OAAO;CAE5B,OAAO;AACT;;;;;;AAOA,SAAS,wBAA+B,iBAAwE;CAG9G,MAAM,UAAU,OAAO,eAAe;CAEtC,QAAQ,UAAU;CAElB,MAAM,YAAY,aAAa,WAAqC;EAClE,MAAM,KAAK,QAAQ;EACnB,IAAI,CAAC,IAAI,aAAa,CAAC;EAEvB,MAAM,eAAkC,CAAC;EACzC,IAAI,YAAY;EAEhB,MAAM,UAAU,OAAmC;GACjD,IAAI,WAAW;GACf,MAAM,QAAQ,GAAG,mBAAmB;IAClC,OAAO;IAEP,OAAO,EAAE;GACX,CAAC;GACD,aAAa,KAAK,KAAK;EACzB;EAEA,KAAK,MAAM,MAAM,OAAO,OAAO,EAAE,GAAG;GAClC,IAAI,OAAO,KAAA,KAAa,OAAO,MAAM;GACrC,MAAM,WAAW,mBAAmB,EAAmC;GACvE,IAAI,UAAU,OAAO,QAAQ;EAC/B;EAEA,aAAa;GACX,YAAY;GACZ,KAAK,MAAM,KAAK,cAAc,EAAE;EAClC;CACF,GAAG,CAAC,CAAC;CAEL,MAAM,kBAAkB,OAAgE,IAAI;CAE5F,MAAM,oBAA4B;EAChC,MAAM,KAAK,QAAQ;EACnB,MAAM,OAAgC,CAAC;EACvC,IAAI,IACF,KAAK,MAAM,CAAC,KAAK,gBAAgB,OAAO,QAAQ,EAAE,GAAG;GACnD,IAAI,gBAAgB,KAAA,KAAa,gBAAgB,MAAM;GACvD,MAAM,WAAW,mBAAmB,WAA4C;GAChF,IAAI,CAAC,UAAU;GACf,MAAM,QAAQ,SAAS;GACvB,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,KAAK,OAAO;EACzD;EAEF,MAAM,OAAO,KAAK,UAAU,IAAI;EAGhC,IAAI,gBAAgB,WAAW,gBAAgB,QAAQ,SAAS,MAC9D,OAAO,gBAAgB,QAAQ;EAEjC,gBAAgB,UAAU;GAAE;GAAM,OAAO;EAAK;EAC9C,OAAO;CACT;CAEA,qBAAqB,WAAW,aAAa,WAAW;CAExD,OAAQ,gBAAgB,SAAS,SAAS,CAAC;AAC7C;AAEA,SAAgB,qBACd,OACW;CACX,MAAM,EAAE,OAAO,iBAAiB,aAAa,iBAAiB,aAAa;CAC3E,MAAM,SAAS,gBAAgB;CAC/B,MAAM,YAAY,OAAO,KAAK;CAE9B,MAAM,eAAe,wBAA+B,eAAe;CACnE,MAAM,kBAAkB;EAAE,GAAI,mBAAoB,CAAC;EAAc,GAAG;CAAa;CAEjF,MAAM,gBAAgB,yBAAyB,KAAK;CACpD,MAAM,eAAe;CACrB,MAAM,0BAA0B,cAAc,OAAO,SAAS;EAC5D,MAAM,IAAI,aAAa;EACvB,OAAO,MAAM,QAAQ,MAAM,KAAA;CAC7B,CAAC;CAMD,gBAAgB;EACd,IAAI,UAAU,SAAS;EACvB,IAAI,CAAC,mBAAmB,eAAe,MAAM;EAC7C,IAAI,CAAC,aAAa,OAAO,WAAW,GAAG;EACvC,OAAO,MAAM,WAAW;GAAE;GAAO,WAAW;GAAiB,MAAM;EAAY,CAAC;EAChF,UAAU,UAAU;CACtB,GAAG;EAAC;EAAQ;EAAO;EAAiB;CAAW,CAAC;CAEhD,MAAM,EAAE,MAAM,SAAS,UAAU,SAAS,OAAO;EAC/C,WAAW;EACX,aAAa;EACb,MAAM,CAAC;CACT,CAAC;CAED,MAAM,eAAgD,eAC7C;EACL,eAAgB,QAAQ,KAAA;EACxB;EACA;EACA;CACF,IACA;EAAC;EAAM;EAAS;EAAyB;CAAK,CAChD;CAEA,OACE,oBAAC,oBAAoB,UAArB;EAA8B,OAAO;EAClC;CAC2B,CAAA;AAElC"}
1
+ {"version":3,"file":"page-reactive-data-root.js","names":[],"sources":["../../../src/sdk/data/page-reactive-data-root.tsx"],"sourcesContent":["'use client';\n\nimport type { ErrorLike, OperationVariables, ReactiveVar, TypedDocumentNode } from '@apollo/client';\nimport { useApolloClient, useQuery } from '@apollo/client/react';\nimport { type DocumentNode, Kind, type OperationDefinitionNode } from 'graphql';\nimport { createContext, type ReactNode, useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\n// Reactive vars resolvable by name from a Server Component. Functions cannot cross the\n// Server→Client boundary in RSC, so the compiler emits string keys and this component resolves\n// them at runtime. New reactive vars must be registered here to be addressable from generated\n// pages. Currently empty: cart context travels on the transport, so no cart entry is needed\n// (docs/storefront-core/reference-cart.md § Cart context).\nconst REACTIVE_VAR_REGISTRY: Record<string, ReactiveVar<unknown>> = {};\n\n/**\n * Compile-time-stable map keyed by the operation's variable names. Each value is either an Apollo\n * reactive variable (`makeVar`) or the string name of one registered in REACTIVE_VAR_REGISTRY —\n * Server Components must pass strings because functions cannot cross the RSC boundary. Vars that\n * have not yet resolved should hold `null`; the runtime treats both `null` and `undefined` as\n * \"not yet ready\" and keeps `useQuery` skipped.\n */\nexport type ClientVariablesMap<TVars> = Partial<{\n [K in keyof TVars]: ReactiveVar<TVars[K] | null> | string;\n}>;\n\nfunction resolveReactiveVar(value: ReactiveVar<unknown> | string): ReactiveVar<unknown> | undefined {\n if (typeof value === 'function') return value;\n const resolved = REACTIVE_VAR_REGISTRY[value];\n if (!resolved) {\n throw new Error(\n `PageReactiveDataRoot: clientVariables entry \"${value}\" is not registered. Add it to REACTIVE_VAR_REGISTRY in page-reactive-data-root.tsx.`,\n );\n }\n return resolved;\n}\n\nexport type PageReactiveDataRootProps<TData, TVars extends OperationVariables> = Readonly<{\n /** The composed PAGE_QUERY operation document. */\n query: TypedDocumentNode<TData, TVars>;\n /** Variables the server used when calling fetchPageData. Only used to seed the client cache. */\n serverVariables?: TVars;\n /** Server-rendered PAGE_QUERY result. When present and shape-matched, seeded into the cache once. */\n initialData?: TData | null;\n /** Reactive vars whose current values are merged into operation variables at render time. */\n clientVariables?: ClientVariablesMap<TVars>;\n children: ReactNode;\n}>;\n\nexport type ReactiveRootContextValue<TData = unknown> = Readonly<{\n /** Current root query data exposed to descendant `ReactiveDataProvider`s. */\n rootQueryData: TData | undefined;\n /** Whether the underlying useQuery is currently in flight. */\n loading: boolean;\n /** False when at least one required variable has not been provided yet. */\n hasAllRequiredVariables: boolean;\n /**\n * Error from the underlying useQuery. With non-null root fields (e.g. `cart: Cart!`), a failed\n * fetch nulls the whole operation's `data` — descendants need this to tell \"fetch failed\" apart\n * from \"entity is legitimately absent\" (docs/storefront-core/reference-free-particle-model.md § Read side — data).\n */\n error: ErrorLike | undefined;\n /**\n * True only under a real `PageReactiveDataRoot`. Every other field above is ambiguous between\n * \"no root at all\" and \"a root with nothing to give right now\" — `rootQueryData: undefined` and\n * `loading: false` describe both the context default and a root whose query is skipped for want\n * of a reactive variable. `useReactiveEntity` needs that distinction to tell a placeholder slice\n * apart from the only slice there is.\n *\n * Required, not optional, and deliberately so. This flag is the whole of what arms the gate in\n * `useReactiveEntity`, so a provider that omits it disarms the gate silently — measured: a value\n * that `satisfies ReactiveRootContextValue` without it reproduces the corrupting payload byte for\n * byte on an otherwise-fixed tree, and no test fails. `ReactiveRootContext` is public surface via\n * `sdk/data/index.ts`, so that provider need not live in this repo. Making it required moves that\n * failure to compile time.\n *\n * A hand-built context value stands in for a root's *output*, not for the reactive runtime, so it\n * declares `false` and its subtree keeps gating on nothing.\n */\n hasRoot: boolean;\n}>;\n\nexport const ReactiveRootContext = createContext<ReactiveRootContextValue>({\n rootQueryData: undefined,\n loading: false,\n hasAllRequiredVariables: false,\n error: undefined,\n hasRoot: false,\n});\n\nfunction getOperationDefinition(query: DocumentNode): OperationDefinitionNode | undefined {\n for (const definition of query.definitions) {\n if (definition.kind === Kind.OPERATION_DEFINITION) return definition;\n }\n return undefined;\n}\n\nfunction getRequiredVariableNames(query: DocumentNode): string[] {\n const op = getOperationDefinition(query);\n if (!op?.variableDefinitions) return [];\n const required: string[] = [];\n for (const def of op.variableDefinitions) {\n if (def.type.kind === Kind.NON_NULL_TYPE && def.defaultValue === undefined) {\n required.push(def.variable.name.value);\n }\n }\n return required;\n}\n\nfunction getTopLevelFieldNames(query: DocumentNode): Set<string> {\n const op = getOperationDefinition(query);\n const names = new Set<string>();\n if (!op) return names;\n for (const sel of op.selectionSet.selections) {\n if (sel.kind === Kind.FIELD) {\n names.add((sel.alias ?? sel.name).value);\n }\n }\n return names;\n}\n\nfunction shapeMatches(query: DocumentNode, data: unknown): boolean {\n if (data == null || typeof data !== 'object') return false;\n const expected = getTopLevelFieldNames(query);\n if (expected.size === 0) return false;\n const actual = data as Record<string, unknown>;\n // Require AT LEAST ONE top-level field name to be present in `data`. The operation's full\n // top-level shape may include fields the server did not return; partial seeds still help. A total\n // miss (no overlap at all) signals a shape mismatch and we skip the seed.\n for (const key of expected) {\n if (key in actual) return true;\n }\n return false;\n}\n\n/**\n * Reads the current values of every reactive var in `clientVariables` into a plain object via a\n * single `useSyncExternalStore` call (no hooks-in-loop). Apollo's `ReactiveVar.onNextChange` fires\n * once per registration, so the subscriber re-attaches after each notification to stay listening.\n */\nfunction useClientVariableValues<TVars>(clientVariables: ClientVariablesMap<TVars> | undefined): Partial<TVars> {\n // Keep the latest map identity reachable from inside subscribe()/getSnapshot() without forcing\n // useSyncExternalStore to tear down the underlying subscription.\n const varsRef = useRef(clientVariables);\n // react-doctor-disable-next-line no-ref-current-in-render -- keeps the latest map reachable from the stable subscribe()/getSnapshot() callbacks below\n varsRef.current = clientVariables;\n\n const subscribe = useCallback((notify: () => void): (() => void) => {\n const cv = varsRef.current;\n if (!cv) return () => {};\n\n const activeUnsubs: Array<() => void> = [];\n let cancelled = false;\n\n const attach = (rv: ReactiveVar<unknown>): void => {\n if (cancelled) return;\n const unsub = rv.onNextChange(() => {\n notify();\n // onNextChange fires only once — re-attach to keep listening for subsequent changes.\n attach(rv);\n });\n activeUnsubs.push(unsub);\n };\n\n for (const rv of Object.values(cv)) {\n if (rv === undefined || rv === null) continue;\n const resolved = resolveReactiveVar(rv as ReactiveVar<unknown> | string);\n if (resolved) attach(resolved);\n }\n\n return () => {\n cancelled = true;\n for (const u of activeUnsubs) u();\n };\n }, []);\n\n const lastSnapshotRef = useRef<{ json: string; value: Record<string, unknown> } | null>(null);\n\n const getSnapshot = (): string => {\n const cv = varsRef.current;\n const snap: Record<string, unknown> = {};\n if (cv) {\n for (const [key, reactiveVar] of Object.entries(cv)) {\n if (reactiveVar === undefined || reactiveVar === null) continue;\n const resolved = resolveReactiveVar(reactiveVar as ReactiveVar<unknown> | string);\n if (!resolved) continue;\n const value = resolved();\n if (value !== null && value !== undefined) snap[key] = value;\n }\n }\n const json = JSON.stringify(snap);\n // useSyncExternalStore expects a stable snapshot reference until the value changes (Object.is\n // identity check) — return the cached `json` whenever the contents are unchanged.\n if (lastSnapshotRef.current && lastSnapshotRef.current.json === json) {\n return lastSnapshotRef.current.json;\n }\n lastSnapshotRef.current = { json, value: snap };\n return json;\n };\n\n useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n return (lastSnapshotRef.current?.value ?? {}) as Partial<TVars>;\n}\n\nexport function PageReactiveDataRoot<TData = unknown, TVars extends OperationVariables = OperationVariables>(\n props: PageReactiveDataRootProps<TData, TVars>,\n): ReactNode {\n const { query, serverVariables, initialData, clientVariables, children } = props;\n const client = useApolloClient();\n const seededRef = useRef(false);\n\n const clientValues = useClientVariableValues<TVars>(clientVariables);\n const mergedVariables = { ...(serverVariables ?? ({} as TVars)), ...clientValues } as TVars;\n\n const requiredNames = getRequiredVariableNames(query);\n const mergedRecord = mergedVariables as Record<string, unknown>;\n const hasAllRequiredVariables = requiredNames.every((name) => {\n const v = mergedRecord[name];\n return v !== null && v !== undefined;\n });\n\n // Ref- and shape-guarded one-time cache seed from server props. We use `useEffect` (not\n // `useLayoutEffect`) to avoid the SSR-pass warning emitted by React 19 — the resulting \"first\n // render shows fallbackData, second render shows cache slice\" behaviour is acceptable because\n // initialData is the same value `ReactiveDataProvider` falls back to anyway.\n useEffect(() => {\n if (seededRef.current) return;\n if (!serverVariables || initialData == null) return;\n if (!shapeMatches(query, initialData)) return;\n client.cache.writeQuery({ query, variables: serverVariables, data: initialData });\n seededRef.current = true;\n }, [client, query, serverVariables, initialData]);\n\n const { data, loading, error } = useQuery(query, {\n variables: mergedVariables,\n fetchPolicy: 'cache-first',\n skip: !hasAllRequiredVariables,\n });\n\n const contextValue: ReactiveRootContextValue<TData> = useMemo(\n () => ({\n rootQueryData: (data ?? undefined) as TData | undefined,\n loading,\n hasAllRequiredVariables,\n error,\n hasRoot: true,\n }),\n [data, loading, hasAllRequiredVariables, error],\n );\n\n return (\n <ReactiveRootContext.Provider value={contextValue as ReactiveRootContextValue}>\n {children}\n </ReactiveRootContext.Provider>\n );\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { DataOverrideContext } from "./data-override-context.js";
3
+ import { EntityResolutionContext } from "./entity-resolution-context.js";
3
4
  import { useReactiveEntity } from "./use-reactive-entity.js";
4
5
  import { jsx } from "react/jsx-runtime";
5
6
  //#region src/sdk/data/reactive-data-provider.tsx
@@ -12,10 +13,13 @@ import { jsx } from "react/jsx-runtime";
12
13
  * See docs/storefront-core/reference-data-layer-runtime.md § Per-component subscription and rfc-data-layer.md §"Reactive fetch".
13
14
  */
14
15
  function ReactiveDataProvider({ entityFragment, fragmentName, rootField, fallbackData, children }) {
15
- const resolved = useReactiveEntity(entityFragment, fragmentName, rootField, fallbackData);
16
- return /* @__PURE__ */ jsx(DataOverrideContext, {
16
+ const { data, resolved } = useReactiveEntity(entityFragment, fragmentName, rootField, fallbackData);
17
+ return /* @__PURE__ */ jsx(EntityResolutionContext, {
17
18
  value: resolved,
18
- children
19
+ children: /* @__PURE__ */ jsx(DataOverrideContext, {
20
+ value: data,
21
+ children
22
+ })
19
23
  });
20
24
  }
21
25
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"reactive-data-provider.js","names":[],"sources":["../../../src/sdk/data/reactive-data-provider.tsx"],"sourcesContent":["'use client';\n\nimport type { DocumentNode } from '@apollo/client';\nimport type { ReactNode } from 'react';\nimport { DataOverrideContext } from './data-override-context.js';\nimport { useReactiveEntity } from './use-reactive-entity.js';\n\nexport type ReactiveDataProviderProps = Readonly<{\n /** Derived `fragment <X>_<EntityType> on <EntityType>` document emitted by the compiler. */\n entityFragment: DocumentNode;\n /** Name of the derived fragment, passed to `useFragment`. */\n fragmentName: string;\n /** Top-level field of the page query (e.g. 'cart') used to extract the entity ref. */\n rootField: string;\n /** Server-rendered fragment payload; used until the cache resolves a complete entity slice. */\n fallbackData: unknown;\n children: ReactNode;\n}>;\n\n/**\n * Per-particle reactive subscriber (AD-4 / AD-D2).\n *\n * Resolves its entity slice via `useReactiveEntity` (the shared single-entity resolve; see that\n * hook's doc for the cache-read details) and writes it, unmodified, into `DataOverrideContext`.\n *\n * See docs/storefront-core/reference-data-layer-runtime.md § Per-component subscription and rfc-data-layer.md §\"Reactive fetch\".\n */\nexport function ReactiveDataProvider({\n entityFragment,\n fragmentName,\n rootField,\n fallbackData,\n children,\n}: ReactiveDataProviderProps): ReactNode {\n const resolved = useReactiveEntity(entityFragment, fragmentName, rootField, fallbackData);\n\n return <DataOverrideContext value={resolved}>{children}</DataOverrideContext>;\n}\n"],"mappings":";;;;;;;;;;;;;AA2BA,SAAgB,qBAAqB,EACnC,gBACA,cACA,WACA,cACA,YACuC;CACvC,MAAM,WAAW,kBAAkB,gBAAgB,cAAc,WAAW,YAAY;CAExF,OAAO,oBAAC,qBAAD;EAAqB,OAAO;EAAW;CAA8B,CAAA;AAC9E"}
1
+ {"version":3,"file":"reactive-data-provider.js","names":[],"sources":["../../../src/sdk/data/reactive-data-provider.tsx"],"sourcesContent":["'use client';\n\nimport type { DocumentNode } from '@apollo/client';\nimport type { ReactNode } from 'react';\nimport { DataOverrideContext } from './data-override-context.js';\nimport { EntityResolutionContext } from './entity-resolution-context.js';\nimport { useReactiveEntity } from './use-reactive-entity.js';\n\nexport type ReactiveDataProviderProps = Readonly<{\n /** Derived `fragment <X>_<EntityType> on <EntityType>` document emitted by the compiler. */\n entityFragment: DocumentNode;\n /** Name of the derived fragment, passed to `useFragment`. */\n fragmentName: string;\n /** Top-level field of the page query (e.g. 'cart') used to extract the entity ref. */\n rootField: string;\n /** Server-rendered fragment payload; used until the cache resolves a complete entity slice. */\n fallbackData: unknown;\n children: ReactNode;\n}>;\n\n/**\n * Per-particle reactive subscriber (AD-4 / AD-D2).\n *\n * Resolves its entity slice via `useReactiveEntity` (the shared single-entity resolve; see that\n * hook's doc for the cache-read details) and writes it, unmodified, into `DataOverrideContext`.\n *\n * See docs/storefront-core/reference-data-layer-runtime.md § Per-component subscription and rfc-data-layer.md §\"Reactive fetch\".\n */\nexport function ReactiveDataProvider({\n entityFragment,\n fragmentName,\n rootField,\n fallbackData,\n children,\n}: ReactiveDataProviderProps): ReactNode {\n const { data, resolved } = useReactiveEntity(entityFragment, fragmentName, rootField, fallbackData);\n\n return (\n <EntityResolutionContext value={resolved}>\n <DataOverrideContext value={data}>{children}</DataOverrideContext>\n </EntityResolutionContext>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AA4BA,SAAgB,qBAAqB,EACnC,gBACA,cACA,WACA,cACA,YACuC;CACvC,MAAM,EAAE,MAAM,aAAa,kBAAkB,gBAAgB,cAAc,WAAW,YAAY;CAElG,OACE,oBAAC,yBAAD;EAAyB,OAAO;EAC9B,UAAA,oBAAC,qBAAD;GAAqB,OAAO;GAAO;EAA8B,CAAA;CAC1C,CAAA;AAE7B"}
@@ -1,7 +1,10 @@
1
1
  "use client";
2
2
  import { DataOverrideContext } from "./data-override-context.js";
3
3
  import { useListItemRef } from "./list-item-ref-context.js";
4
+ import { ReactiveRootContext } from "./page-reactive-data-root.js";
5
+ import { EntityResolutionContext } from "./entity-resolution-context.js";
4
6
  import { useApolloClient, useFragment } from "@apollo/client/react";
7
+ import { useContext } from "react";
5
8
  import { jsx } from "react/jsx-runtime";
6
9
  //#region src/sdk/data/reactive-list-item-data-provider.tsx
7
10
  /**
@@ -30,10 +33,15 @@ function ReactiveListItemDataProvider({ entityFragment, fragmentName, children }
30
33
  fragmentName,
31
34
  from: from ?? ""
32
35
  });
33
- const resolved = from && fragmentResult.complete ? fragmentResult.data : ref?.fallbackData;
34
- return /* @__PURE__ */ jsx(DataOverrideContext, {
35
- value: resolved,
36
- children
36
+ const { hasRoot } = useContext(ReactiveRootContext);
37
+ const hit = Boolean(from) && fragmentResult.complete;
38
+ const resolved = hit ? fragmentResult.data : ref?.fallbackData;
39
+ return /* @__PURE__ */ jsx(EntityResolutionContext, {
40
+ value: !hasRoot || ref === void 0 || hit,
41
+ children: /* @__PURE__ */ jsx(DataOverrideContext, {
42
+ value: resolved,
43
+ children
44
+ })
37
45
  });
38
46
  }
39
47
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"reactive-list-item-data-provider.js","names":[],"sources":["../../../src/sdk/data/reactive-list-item-data-provider.tsx"],"sourcesContent":["'use client';\n\nimport type { DocumentNode } from '@apollo/client';\nimport { useApolloClient, useFragment } from '@apollo/client/react';\nimport type { ReactNode } from 'react';\nimport { DataOverrideContext } from './data-override-context.js';\nimport { useListItemRef } from './list-item-ref-context.js';\n\nexport type ReactiveListItemDataProviderProps = Readonly<{\n /** Derived `fragment <X>_<ListElementEntity> on <ListElementEntity>` document emitted by the compiler. */\n entityFragment: DocumentNode;\n /** Name of the derived fragment, passed to `useFragment`. */\n fragmentName: string;\n children: ReactNode;\n}>;\n\n/**\n * Per-iterated-line reactive subscriber.\n *\n * The list-item sibling of `ReactiveDataProvider`: instead of deriving the entity ref from a\n * top-level page-query field, it reads the current line's ref from `ListItemRefContext` (published\n * per line by the iterator, e.g. `CartList`) and subscribes to that one normalized entity\n * (`CartItem:<id>`) via `useFragment`. The hook never triggers a network request — it is purely a\n * cache reader. When the ref is unresolved or the cache slice is incomplete, it falls back to the\n * line's server-rendered slice (`fallbackData`) so descendants always see a value. The resolved\n * slice is published through the same `DataOverrideContext` seam reactive components read today.\n *\n * See docs/storefront-core/reference-free-particle-model.md § Per-item delivery for iterated matters.\n */\nexport function ReactiveListItemDataProvider({\n entityFragment,\n fragmentName,\n children,\n}: ReactiveListItemDataProviderProps): ReactNode {\n const ref = useListItemRef();\n const client = useApolloClient();\n\n // Derive a stable cache identifier when both __typename and id are present. `cache.identify`\n // returns undefined when the inputs are not normalisable, which we treat the same as a missing\n // ref — useFragment then has nothing to subscribe to and the line shows its fallback slice.\n let from: string | undefined;\n if (ref?.__typename != null && ref.id != null) {\n from = client.cache.identify({ __typename: ref.__typename, id: ref.id });\n }\n\n // useFragment must be called unconditionally to satisfy the Rules of Hooks. When `from` is\n // undefined, Apollo's useFragment returns `{ data: {}, complete: false }` and never reads the\n // cache; the empty-string sentinel keeps the hook invoked but the read a no-op.\n const fragmentResult = useFragment({\n fragment: entityFragment,\n fragmentName,\n from: from ?? '',\n });\n\n const resolved = from && fragmentResult.complete ? fragmentResult.data : ref?.fallbackData;\n\n return <DataOverrideContext value={resolved}>{children}</DataOverrideContext>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,6BAA6B,EAC3C,gBACA,cACA,YAC+C;CAC/C,MAAM,MAAM,eAAe;CAC3B,MAAM,SAAS,gBAAgB;CAK/B,IAAI;CACJ,IAAI,KAAK,cAAc,QAAQ,IAAI,MAAM,MACvC,OAAO,OAAO,MAAM,SAAS;EAAE,YAAY,IAAI;EAAY,IAAI,IAAI;CAAG,CAAC;CAMzE,MAAM,iBAAiB,YAAY;EACjC,UAAU;EACV;EACA,MAAM,QAAQ;CAChB,CAAC;CAED,MAAM,WAAW,QAAQ,eAAe,WAAW,eAAe,OAAO,KAAK;CAE9E,OAAO,oBAAC,qBAAD;EAAqB,OAAO;EAAW;CAA8B,CAAA;AAC9E"}
1
+ {"version":3,"file":"reactive-list-item-data-provider.js","names":[],"sources":["../../../src/sdk/data/reactive-list-item-data-provider.tsx"],"sourcesContent":["'use client';\n\nimport type { DocumentNode } from '@apollo/client';\nimport { useApolloClient, useFragment } from '@apollo/client/react';\nimport type { ReactNode } from 'react';\nimport { useContext } from 'react';\nimport { DataOverrideContext } from './data-override-context.js';\nimport { EntityResolutionContext } from './entity-resolution-context.js';\nimport { useListItemRef } from './list-item-ref-context.js';\nimport { ReactiveRootContext } from './page-reactive-data-root.js';\n\nexport type ReactiveListItemDataProviderProps = Readonly<{\n /** Derived `fragment <X>_<ListElementEntity> on <ListElementEntity>` document emitted by the compiler. */\n entityFragment: DocumentNode;\n /** Name of the derived fragment, passed to `useFragment`. */\n fragmentName: string;\n children: ReactNode;\n}>;\n\n/**\n * Per-iterated-line reactive subscriber.\n *\n * The list-item sibling of `ReactiveDataProvider`: instead of deriving the entity ref from a\n * top-level page-query field, it reads the current line's ref from `ListItemRefContext` (published\n * per line by the iterator, e.g. `CartList`) and subscribes to that one normalized entity\n * (`CartItem:<id>`) via `useFragment`. The hook never triggers a network request — it is purely a\n * cache reader. When the ref is unresolved or the cache slice is incomplete, it falls back to the\n * line's server-rendered slice (`fallbackData`) so descendants always see a value. The resolved\n * slice is published through the same `DataOverrideContext` seam reactive components read today.\n *\n * See docs/storefront-core/reference-free-particle-model.md § Per-item delivery for iterated matters.\n */\nexport function ReactiveListItemDataProvider({\n entityFragment,\n fragmentName,\n children,\n}: ReactiveListItemDataProviderProps): ReactNode {\n const ref = useListItemRef();\n const client = useApolloClient();\n\n // Derive a stable cache identifier when both __typename and id are present. `cache.identify`\n // returns undefined when the inputs are not normalisable, which we treat the same as a missing\n // ref — useFragment then has nothing to subscribe to and the line shows its fallback slice.\n let from: string | undefined;\n if (ref?.__typename != null && ref.id != null) {\n from = client.cache.identify({ __typename: ref.__typename, id: ref.id });\n }\n\n // useFragment must be called unconditionally to satisfy the Rules of Hooks. When `from` is\n // undefined, Apollo's useFragment returns `{ data: {}, complete: false }` and never reads the\n // cache; the empty-string sentinel keeps the hook invoked but the read a no-op.\n const fragmentResult = useFragment({\n fragment: entityFragment,\n fragmentName,\n from: from ?? '',\n });\n\n const { hasRoot } = useContext(ReactiveRootContext);\n const hit = Boolean(from) && fragmentResult.complete;\n const resolved = hit ? fragmentResult.data : ref?.fallbackData;\n\n // Resets the resolution verdict for the same reason it resets the slice: the line's own entity is\n // what this subtree reads, so an ancestor's verdict no longer describes it. Two escapes, mirroring\n // `useReactiveEntity`: no reactive root above means nothing is outstanding anywhere, and no iterator\n // above (`ref` undefined) means there is no reactive runtime here at all. Without the first, a line\n // rendered from plain props under a real ref would report unresolved forever.\n return (\n <EntityResolutionContext value={!hasRoot || ref === undefined || hit}>\n <DataOverrideContext value={resolved}>{children}</DataOverrideContext>\n </EntityResolutionContext>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,6BAA6B,EAC3C,gBACA,cACA,YAC+C;CAC/C,MAAM,MAAM,eAAe;CAC3B,MAAM,SAAS,gBAAgB;CAK/B,IAAI;CACJ,IAAI,KAAK,cAAc,QAAQ,IAAI,MAAM,MACvC,OAAO,OAAO,MAAM,SAAS;EAAE,YAAY,IAAI;EAAY,IAAI,IAAI;CAAG,CAAC;CAMzE,MAAM,iBAAiB,YAAY;EACjC,UAAU;EACV;EACA,MAAM,QAAQ;CAChB,CAAC;CAED,MAAM,EAAE,YAAY,WAAW,mBAAmB;CAClD,MAAM,MAAM,QAAQ,IAAI,KAAK,eAAe;CAC5C,MAAM,WAAW,MAAM,eAAe,OAAO,KAAK;CAOlD,OACE,oBAAC,yBAAD;EAAyB,OAAO,CAAC,WAAW,QAAQ,KAAA,KAAa;EAC/D,UAAA,oBAAC,qBAAD;GAAqB,OAAO;GAAW;EAA8B,CAAA;CAC9C,CAAA;AAE7B"}
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ import { ReactiveRootContext } from "./page-reactive-data-root.js";
3
+ import { useContext, useState } from "react";
4
+ //#region src/sdk/data/use-reactive-data-settled.ts
5
+ /**
6
+ * Has this page's reactive root query settled at least once since the caller mounted?
7
+ *
8
+ * `useReactiveEntity` answers "what is this entity's slice" and never "has it arrived": it returns
9
+ * `fallbackData` whenever the cache read is incomplete, so a `reactive` component's `$data` is
10
+ * defined from the first render — empty or not. That is fine for a read-only component, which just
11
+ * re-renders when the real values land, and wrong for a form, whose controls hand their seed to
12
+ * react-hook-form's `defaultValue`, which re-adopts it on every render only while the field stays
13
+ * clean: mounting on a placeholder seed before the real answer lands would make a shopper who starts
14
+ * typing dirty the field against that placeholder for good, and even a shopper who does not type
15
+ * would see the field flash from the placeholder to the real value. A form needs the one thing the
16
+ * entity slice cannot express: whether the answer it is holding is the server's or a placeholder.
17
+ *
18
+ * `PageReactiveDataRoot` already publishes that on `ReactiveRootContext.loading` — the in-flight
19
+ * state of the page query every reactive slice is read from. This hook latches it one way: `false`
20
+ * until the first settled render, `true` for the rest of the mount. The latch is what keeps the
21
+ * signal usable as a mount gate. A later refetch flips `loading` back to `true`, and a caller
22
+ * gating its children on the raw flag would unmount them mid-edit and re-seed over whatever the
23
+ * shopper had typed. Reactivity after that first paint is unaffected: the slice keeps arriving
24
+ * through `DataOverrideContext` exactly as before.
25
+ *
26
+ * With no `PageReactiveDataRoot` above — Storybook, unit tests, any tree passing `$data` as a plain
27
+ * prop — the context default carries `loading: false`, so this reads `true` on the first render and
28
+ * gates nothing. Callers keep their own `$data === undefined` check for those trees.
29
+ */
30
+ function useReactiveDataSettled() {
31
+ const { loading } = useContext(ReactiveRootContext);
32
+ const [settled, setSettled] = useState(false);
33
+ if (!loading && !settled) setSettled(true);
34
+ return settled;
35
+ }
36
+ //#endregion
37
+ export { useReactiveDataSettled };
38
+
39
+ //# sourceMappingURL=use-reactive-data-settled.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-reactive-data-settled.js","names":[],"sources":["../../../src/sdk/data/use-reactive-data-settled.ts"],"sourcesContent":["'use client';\n\nimport { useContext, useState } from 'react';\nimport { ReactiveRootContext } from './page-reactive-data-root.js';\n\n/**\n * Has this page's reactive root query settled at least once since the caller mounted?\n *\n * `useReactiveEntity` answers \"what is this entity's slice\" and never \"has it arrived\": it returns\n * `fallbackData` whenever the cache read is incomplete, so a `reactive` component's `$data` is\n * defined from the first render — empty or not. That is fine for a read-only component, which just\n * re-renders when the real values land, and wrong for a form, whose controls hand their seed to\n * react-hook-form's `defaultValue`, which re-adopts it on every render only while the field stays\n * clean: mounting on a placeholder seed before the real answer lands would make a shopper who starts\n * typing dirty the field against that placeholder for good, and even a shopper who does not type\n * would see the field flash from the placeholder to the real value. A form needs the one thing the\n * entity slice cannot express: whether the answer it is holding is the server's or a placeholder.\n *\n * `PageReactiveDataRoot` already publishes that on `ReactiveRootContext.loading` — the in-flight\n * state of the page query every reactive slice is read from. This hook latches it one way: `false`\n * until the first settled render, `true` for the rest of the mount. The latch is what keeps the\n * signal usable as a mount gate. A later refetch flips `loading` back to `true`, and a caller\n * gating its children on the raw flag would unmount them mid-edit and re-seed over whatever the\n * shopper had typed. Reactivity after that first paint is unaffected: the slice keeps arriving\n * through `DataOverrideContext` exactly as before.\n *\n * With no `PageReactiveDataRoot` above — Storybook, unit tests, any tree passing `$data` as a plain\n * prop — the context default carries `loading: false`, so this reads `true` on the first render and\n * gates nothing. Callers keep their own `$data === undefined` check for those trees.\n */\nexport function useReactiveDataSettled(): boolean {\n const { loading } = useContext(ReactiveRootContext);\n const [settled, setSettled] = useState(false);\n\n // Render-phase compare-and-set, not a ref: a render React abandons without committing (a suspended\n // transition, a Suspense retry) must not advance this latch, and a state update queued on that\n // abandoned attempt is exactly what React discards on retry — a ref write is not. No `settled ||\n // !loading` guard is needed: this re-invokes before the render commits, so the returned value\n // already reflects the latch this render just set.\n if (!loading && !settled) setSettled(true);\n\n return settled;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,yBAAkC;CAChD,MAAM,EAAE,YAAY,WAAW,mBAAmB;CAClD,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAO5C,IAAI,CAAC,WAAW,CAAC,SAAS,WAAW,IAAI;CAEzC,OAAO;AACT"}
@@ -32,7 +32,11 @@ function useReactiveEntity(entityFragment, fragmentName, rootField, fallbackData
32
32
  fragmentName,
33
33
  from: from ?? ""
34
34
  });
35
- return from && fragmentResult.complete ? fragmentResult.data : fallbackData;
35
+ const hit = Boolean(from) && fragmentResult.complete;
36
+ return {
37
+ data: hit ? fragmentResult.data : fallbackData,
38
+ resolved: !ctx.hasRoot || hit
39
+ };
36
40
  }
37
41
  //#endregion
38
42
  export { useReactiveEntity };
@@ -1 +1 @@
1
- {"version":3,"file":"use-reactive-entity.js","names":[],"sources":["../../../src/sdk/data/use-reactive-entity.ts"],"sourcesContent":["'use client';\n\nimport type { DocumentNode } from '@apollo/client';\nimport { useApolloClient, useFragment } from '@apollo/client/react';\nimport { useContext } from 'react';\nimport { ReactiveRootContext } from './page-reactive-data-root.js';\n\ntype EntityRef = {\n __typename?: string;\n id?: string | number;\n};\n\n/**\n * Shared single-entity resolve logic used by BOTH `ReactiveDataProvider` and `EntitySubscriber`\n * (AD-4 / AD-D2).\n *\n * Reads the root query data exposed by `PageReactiveDataRoot` via context, derives the entity ref\n * for the configured `rootField`, and subscribes to the cache via `useFragment`. The hook never\n * triggers a network request — it is purely a cache reader. When the entity ref is unresolved or\n * the cache slice is incomplete, falls back to `fallbackData` (the server-rendered payload) so\n * descendants always see a value.\n *\n * This hook is intentionally single-entity: it has no notion of merging/composing multiple\n * entities' slices — that's layered on top by `EntitySubscriber`.\n *\n * See docs/storefront-core/reference-data-layer-runtime.md § Per-component subscription and rfc-data-layer.md §\"Reactive fetch\".\n */\nexport function useReactiveEntity(\n entityFragment: DocumentNode,\n fragmentName: string,\n rootField: string,\n fallbackData: unknown,\n): unknown {\n const ctx = useContext(ReactiveRootContext);\n const client = useApolloClient();\n\n const root = ctx.rootQueryData as Record<string, unknown> | undefined;\n const rootSlice = root?.[rootField] as EntityRef | null | undefined;\n\n // Derive a stable cache identifier when both __typename and id are present. `cache.identify`\n // returns undefined when the inputs are not normalisable (no id, no keyFields), which we treat\n // the same as a missing ref — useFragment then has nothing to subscribe to.\n let from: string | undefined;\n if (rootSlice?.__typename != null && rootSlice.id != null) {\n from = client.cache.identify({ __typename: rootSlice.__typename, id: rootSlice.id });\n }\n\n // useFragment must be called unconditionally to satisfy the Rules of Hooks. When `from` is\n // undefined, Apollo's useFragment returns `{ data: {}, complete: false }` and never reads the\n // cache. Pass an empty string as a sentinel so the hook is invoked but the read is a no-op.\n const fragmentResult = useFragment({\n fragment: entityFragment,\n fragmentName,\n from: from ?? '',\n });\n\n return from && fragmentResult.complete ? fragmentResult.data : fallbackData;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,kBACd,gBACA,cACA,WACA,cACS;CACT,MAAM,MAAM,WAAW,mBAAmB;CAC1C,MAAM,SAAS,gBAAgB;CAG/B,MAAM,YADO,IAAI,gBACQ;CAKzB,IAAI;CACJ,IAAI,WAAW,cAAc,QAAQ,UAAU,MAAM,MACnD,OAAO,OAAO,MAAM,SAAS;EAAE,YAAY,UAAU;EAAY,IAAI,UAAU;CAAG,CAAC;CAMrF,MAAM,iBAAiB,YAAY;EACjC,UAAU;EACV;EACA,MAAM,QAAQ;CAChB,CAAC;CAED,OAAO,QAAQ,eAAe,WAAW,eAAe,OAAO;AACjE"}
1
+ {"version":3,"file":"use-reactive-entity.js","names":[],"sources":["../../../src/sdk/data/use-reactive-entity.ts"],"sourcesContent":["'use client';\n\nimport type { DocumentNode } from '@apollo/client';\nimport { useApolloClient, useFragment } from '@apollo/client/react';\nimport { useContext } from 'react';\nimport { ReactiveRootContext } from './page-reactive-data-root.js';\n\ntype EntityRef = {\n __typename?: string;\n id?: string | number;\n};\n\nexport type ReactiveEntitySlice = Readonly<{\n /** The slice descendants read: the cache's answer when there is one, `fallbackData` otherwise. */\n data: unknown;\n /**\n * Is `data` the cache's own answer for the ref the page query currently holds? False whenever\n * `fallbackData` is standing in for one under a live reactive root — the first render before the\n * page query lands, a root whose query is skipped for want of a variable, an entity ref that has\n * moved to a node this cache never stored, an evicted field. Always true with no reactive root\n * above, where `fallbackData` is not a placeholder but the only answer there is.\n */\n resolved: boolean;\n}>;\n\n/**\n * Shared single-entity resolve logic used by BOTH `ReactiveDataProvider` and `EntitySubscriber`\n * (AD-4 / AD-D2).\n *\n * Reads the root query data exposed by `PageReactiveDataRoot` via context, derives the entity ref\n * for the configured `rootField`, and subscribes to the cache via `useFragment`. The hook never\n * triggers a network request — it is purely a cache reader. When the entity ref is unresolved or\n * the cache slice is incomplete, falls back to `fallbackData` (the server-rendered payload) so\n * descendants always see a value.\n *\n * This hook is intentionally single-entity: it has no notion of merging/composing multiple\n * entities' slices — that's layered on top by `EntitySubscriber`.\n *\n * See docs/storefront-core/reference-data-layer-runtime.md § Per-component subscription and rfc-data-layer.md §\"Reactive fetch\".\n */\nexport function useReactiveEntity(\n entityFragment: DocumentNode,\n fragmentName: string,\n rootField: string,\n fallbackData: unknown,\n): ReactiveEntitySlice {\n const ctx = useContext(ReactiveRootContext);\n const client = useApolloClient();\n\n const root = ctx.rootQueryData as Record<string, unknown> | undefined;\n const rootSlice = root?.[rootField] as EntityRef | null | undefined;\n\n // Derive a stable cache identifier when both __typename and id are present. `cache.identify`\n // returns undefined when the inputs are not normalisable (no id, no keyFields), which we treat\n // the same as a missing ref — useFragment then has nothing to subscribe to.\n let from: string | undefined;\n if (rootSlice?.__typename != null && rootSlice.id != null) {\n from = client.cache.identify({ __typename: rootSlice.__typename, id: rootSlice.id });\n }\n\n // useFragment must be called unconditionally to satisfy the Rules of Hooks. When `from` is\n // undefined, Apollo's useFragment returns `{ data: {}, complete: false }` and never reads the\n // cache. Pass an empty string as a sentinel so the hook is invoked but the read is a no-op.\n const fragmentResult = useFragment({\n fragment: entityFragment,\n fragmentName,\n from: from ?? '',\n });\n\n const hit = Boolean(from) && fragmentResult.complete;\n\n // `hasRoot` is what separates \"no reactive runtime here\" from \"the reactive runtime has nothing\n // for this entity right now\" — two states the rest of this context value cannot tell apart, since\n // its own default carries `rootQueryData: undefined` exactly like a root whose query is skipped.\n // Without the distinction a prop-driven tree (Storybook, a unit test) would report an unresolved\n // slice forever and any caller gating on it would never render.\n return { data: hit ? fragmentResult.data : fallbackData, resolved: !ctx.hasRoot || hit };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwCA,SAAgB,kBACd,gBACA,cACA,WACA,cACqB;CACrB,MAAM,MAAM,WAAW,mBAAmB;CAC1C,MAAM,SAAS,gBAAgB;CAG/B,MAAM,YADO,IAAI,gBACQ;CAKzB,IAAI;CACJ,IAAI,WAAW,cAAc,QAAQ,UAAU,MAAM,MACnD,OAAO,OAAO,MAAM,SAAS;EAAE,YAAY,UAAU;EAAY,IAAI,UAAU;CAAG,CAAC;CAMrF,MAAM,iBAAiB,YAAY;EACjC,UAAU;EACV;EACA,MAAM,QAAQ;CAChB,CAAC;CAED,MAAM,MAAM,QAAQ,IAAI,KAAK,eAAe;CAO5C,OAAO;EAAE,MAAM,MAAM,eAAe,OAAO;EAAc,UAAU,CAAC,IAAI,WAAW;CAAI;AACzF"}
@@ -9,6 +9,8 @@ const possibleTypes = {
9
9
  "ClientProfile",
10
10
  "Collection",
11
11
  "Customization",
12
+ "Order",
13
+ "Payment",
12
14
  "Product",
13
15
  "SalesChannel",
14
16
  "Seller",
@@ -1 +1 @@
1
- {"version":3,"file":"fragment-types.js","names":[],"sources":["../../../src/sdk/generated/fragment-types.ts"],"sourcesContent":["// @generated — DO NOT EDIT. Run `pnpm codegen` to regenerate.\nexport type PossibleTypes = Record<string, string[]>;\nexport const possibleTypes: PossibleTypes = {\n Media: [\n 'Image',\n 'Video'\n ],\n Node: [\n 'Brand',\n 'Cart',\n 'Category',\n 'ClientPreferences',\n 'ClientProfile',\n 'Collection',\n 'Customization',\n 'Product',\n 'SalesChannel',\n 'Seller',\n 'Session',\n 'Shipping',\n 'Variant'\n ]\n};\n"],"mappings":";AAEA,MAAa,gBAA+B;CAC1C,OAAO,CACL,SACA,OACF;CACA,MAAM;EACJ;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"fragment-types.js","names":[],"sources":["../../../src/sdk/generated/fragment-types.ts"],"sourcesContent":["// @generated — DO NOT EDIT. Run `pnpm codegen` to regenerate.\nexport type PossibleTypes = Record<string, string[]>;\nexport const possibleTypes: PossibleTypes = {\n Media: [\n 'Image',\n 'Video'\n ],\n Node: [\n 'Brand',\n 'Cart',\n 'Category',\n 'ClientPreferences',\n 'ClientProfile',\n 'Collection',\n 'Customization',\n 'Order',\n 'Payment',\n 'Product',\n 'SalesChannel',\n 'Seller',\n 'Session',\n 'Shipping',\n 'Variant'\n ]\n};\n"],"mappings":";AAEA,MAAa,gBAA+B;CAC1C,OAAO,CACL,SACA,OACF;CACA,MAAM;EACJ;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}