@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.
- package/CHANGELOG.md +45 -0
- package/dist/actions.json +181 -0
- package/dist/catalog.json +1543 -495
- package/dist/client.d.ts +2 -1
- package/dist/client.js +2 -1
- package/dist/components.d.ts +28 -7
- package/dist/components.js +31 -12
- package/dist/docs/access-code-sign-in.md +1 -1
- package/dist/docs/address-summary.md +24 -3
- package/dist/docs/alert-dialog.md +204 -0
- package/dist/docs/billing-address.md +284 -0
- package/dist/docs/card-entry.md +250 -0
- package/dist/docs/cardholder-document.md +142 -0
- package/dist/docs/client-preferences-fieldset.md +148 -0
- package/dist/docs/client-preferences.md +2 -1
- package/dist/docs/client-profile-fieldset.md +184 -0
- package/dist/docs/client-profile.md +1 -1
- package/dist/docs/document-field.md +58 -4
- package/dist/docs/field.md +317 -0
- package/dist/docs/form.md +307 -0
- package/dist/docs/identity-edit.md +6 -6
- package/dist/docs/identity-summary.md +5 -5
- package/dist/docs/installments.md +248 -0
- package/dist/docs/my-account-profile.md +1 -0
- package/dist/docs/order-placed-payment-summary.md +138 -0
- package/dist/docs/payment-method-badges.md +232 -0
- package/dist/docs/payment-methods.md +1 -0
- package/dist/docs/payment.md +218 -0
- package/dist/docs/phone-field.md +58 -4
- package/dist/docs/place-order.md +157 -0
- package/dist/docs/save-card.md +186 -0
- package/dist/docs/saved-cards.md +218 -0
- package/dist/docs/select.md +465 -0
- package/dist/docs/shipping-address.md +110 -258
- package/dist/index.d.ts +4 -1
- package/dist/internal/client.d.ts +3 -1
- package/dist/internal/client.js +3 -1
- package/dist/matter/access-code-sign-in/access-code-sign-in.js +9 -8
- package/dist/matter/access-code-sign-in/access-code-sign-in.js.map +1 -1
- package/dist/matter/billing-address/billing-address.d.ts +165 -0
- package/dist/matter/billing-address/billing-address.js +408 -0
- package/dist/matter/billing-address/billing-address.js.map +1 -0
- package/dist/matter/card-entry/card-capture.d.ts +25 -0
- package/dist/matter/card-entry/card-capture.js +25 -0
- package/dist/matter/card-entry/card-capture.js.map +1 -0
- package/dist/matter/card-entry/card-entry.d.ts +120 -0
- package/dist/matter/card-entry/card-entry.js +832 -0
- package/dist/matter/card-entry/card-entry.js.map +1 -0
- package/dist/matter/cardholder-document/cardholder-document.d.ts +45 -0
- package/dist/matter/cardholder-document/cardholder-document.js +70 -0
- package/dist/matter/cardholder-document/cardholder-document.js.map +1 -0
- package/dist/matter/client-preferences/client-preferences-fieldset.d.ts +77 -0
- package/dist/matter/client-preferences/client-preferences-fieldset.js +299 -0
- package/dist/matter/client-preferences/client-preferences-fieldset.js.map +1 -0
- package/dist/matter/client-preferences/client-preferences.d.ts +3 -5
- package/dist/matter/client-preferences/client-preferences.js +6 -6
- package/dist/matter/client-preferences/client-preferences.js.map +1 -1
- package/dist/matter/client-profile/client-profile.d.ts +4 -6
- package/dist/matter/client-profile/client-profile.js +9 -6
- package/dist/matter/client-profile/client-profile.js.map +1 -1
- package/dist/matter/client-profile-form/client-profile-fieldset.d.ts +119 -0
- package/dist/matter/client-profile-form/client-profile-fieldset.js +488 -0
- package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -0
- package/dist/matter/identity-edit/identity-edit.js +2 -2
- package/dist/matter/identity-edit/identity-edit.js.map +1 -1
- package/dist/matter/identity-summary/identity-summary.js +2 -2
- package/dist/matter/identity-summary/identity-summary.js.map +1 -1
- package/dist/matter/installments/installments-select.d.ts +42 -0
- package/dist/matter/installments/installments-select.js +167 -0
- package/dist/matter/installments/installments-select.js.map +1 -0
- package/dist/matter/installments/installments.d.ts +58 -0
- package/dist/matter/installments/installments.js +176 -0
- package/dist/matter/installments/installments.js.map +1 -0
- package/dist/matter/installments/use-installments-selection.d.ts +29 -0
- package/dist/matter/installments/use-installments-selection.js +95 -0
- package/dist/matter/installments/use-installments-selection.js.map +1 -0
- package/dist/matter/my-account-profile/my-account-profile.js +4 -4
- package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +63 -0
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +305 -0
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -0
- package/dist/matter/payment/payment-card-capture.d.ts +19 -0
- package/dist/matter/payment/payment-card-capture.js +129 -0
- package/dist/matter/payment/payment-card-capture.js.map +1 -0
- package/dist/matter/payment/payment-controller.d.ts +24 -0
- package/dist/matter/payment/payment-controller.js +107 -0
- package/dist/matter/payment/payment-controller.js.map +1 -0
- package/dist/matter/payment/payment-method-badges.d.ts +57 -0
- package/dist/matter/payment/payment-method-badges.js +192 -0
- package/dist/matter/payment/payment-method-badges.js.map +1 -0
- package/dist/matter/payment/payment.d.ts +89 -0
- package/dist/matter/payment/payment.js +263 -0
- package/dist/matter/payment/payment.js.map +1 -0
- package/dist/matter/payment/use-payment-method-selection.d.ts +302 -0
- package/dist/matter/payment/use-payment-method-selection.js +458 -0
- package/dist/matter/payment/use-payment-method-selection.js.map +1 -0
- package/dist/matter/payment/use-payment-submit-gate.d.ts +111 -0
- package/dist/matter/payment/use-payment-submit-gate.js +144 -0
- package/dist/matter/payment/use-payment-submit-gate.js.map +1 -0
- package/dist/matter/place-order/place-order.d.ts +74 -0
- package/dist/matter/place-order/place-order.js +379 -0
- package/dist/matter/place-order/place-order.js.map +1 -0
- package/dist/matter/product-list/product-list.d.ts +1 -0
- package/dist/matter/product-list/product-list.js.map +1 -1
- package/dist/matter/product-shelf/product-shelf.d.ts +1 -0
- package/dist/matter/product-shelf/product-shelf.js +2 -0
- package/dist/matter/product-shelf/product-shelf.js.map +1 -1
- package/dist/matter/region/region.js +1 -1
- package/dist/matter/region/region.js.map +1 -1
- package/dist/matter/save-card/save-card.d.ts +49 -0
- package/dist/matter/save-card/save-card.js +188 -0
- package/dist/matter/save-card/save-card.js.map +1 -0
- package/dist/matter/saved-cards/saved-cards.d.ts +79 -0
- package/dist/matter/saved-cards/saved-cards.js +387 -0
- package/dist/matter/saved-cards/saved-cards.js.map +1 -0
- package/dist/matter/shipping-address/country-descriptors.js +20 -10
- package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
- package/dist/matter/shipping-address/shipping-address.d.ts +113 -174
- package/dist/matter/shipping-address/shipping-address.js +480 -791
- package/dist/matter/shipping-address/shipping-address.js.map +1 -1
- package/dist/matter/variant-selector/variant-selector.js +1 -1
- package/dist/particles/address-summary/address-summary.d.ts +24 -0
- package/dist/particles/address-summary/address-summary.js +83 -2
- package/dist/particles/address-summary/address-summary.js.map +1 -1
- package/dist/particles/alert-dialog/alert-dialog.d.ts +68 -0
- package/dist/particles/alert-dialog/alert-dialog.js +187 -0
- package/dist/particles/alert-dialog/alert-dialog.js.map +1 -0
- package/dist/particles/checkbox/checkbox.d.ts +7 -3
- package/dist/particles/checkbox/checkbox.js +4 -1
- package/dist/particles/checkbox/checkbox.js.map +1 -1
- package/dist/particles/document-field/document-field.d.ts +48 -5
- package/dist/particles/document-field/document-field.js +189 -32
- package/dist/particles/document-field/document-field.js.map +1 -1
- package/dist/particles/field/bound-field-registry.js +52 -0
- package/dist/particles/field/bound-field-registry.js.map +1 -0
- package/dist/particles/field/field.d.ts +116 -0
- package/dist/particles/field/field.js +497 -6
- package/dist/particles/field/field.js.map +1 -1
- package/dist/particles/fieldset/fieldset.js +2 -2
- package/dist/particles/fieldset/fieldset.js.map +1 -1
- package/dist/particles/form/form.d.ts +104 -0
- package/dist/particles/form/form.js +485 -0
- package/dist/particles/form/form.js.map +1 -0
- package/dist/particles/phone-field/phone-field.d.ts +38 -10
- package/dist/particles/phone-field/phone-field.js +183 -39
- package/dist/particles/phone-field/phone-field.js.map +1 -1
- package/dist/particles/postal-code-field/postal-code-field.js +2 -2
- package/dist/particles/postal-code-field/postal-code-field.js.map +1 -1
- package/dist/particles/select/field-select.d.ts +43 -0
- package/dist/particles/select/field-select.js +150 -0
- package/dist/particles/select/field-select.js.map +1 -0
- package/dist/particles/select/select.d.ts +2 -0
- package/dist/particles/select/select.js.map +1 -1
- package/dist/particles/shared/compose-refs.js +21 -0
- package/dist/particles/shared/compose-refs.js.map +1 -0
- package/dist/schema.graphql +701 -2
- package/dist/sdk/actions/analytics-action-provider.d.ts +15 -0
- package/dist/sdk/actions/analytics-action-provider.js +19 -0
- package/dist/sdk/actions/analytics-action-provider.js.map +1 -0
- package/dist/sdk/actions/index.d.ts +3 -1
- package/dist/sdk/actions/index.js +3 -1
- package/dist/sdk/actions/payment-action-provider.d.ts +22 -0
- package/dist/sdk/actions/payment-action-provider.js +26 -0
- package/dist/sdk/actions/payment-action-provider.js.map +1 -0
- package/dist/sdk/actions/types.d.ts +6 -2
- package/dist/sdk/analytics/analytics-destination.d.ts +43 -0
- package/dist/sdk/analytics/analytics-destination.js +90 -0
- package/dist/sdk/analytics/analytics-destination.js.map +1 -0
- package/dist/sdk/analytics/use-analytics-actions.d.ts +57 -0
- package/dist/sdk/analytics/use-analytics-actions.js +108 -0
- package/dist/sdk/analytics/use-analytics-actions.js.map +1 -0
- package/dist/sdk/cart/use-cart-actions.js +1 -1
- package/dist/sdk/data/entity-resolution-context.js +31 -0
- package/dist/sdk/data/entity-resolution-context.js.map +1 -0
- package/dist/sdk/data/entity-subscriber.js +18 -8
- package/dist/sdk/data/entity-subscriber.js.map +1 -1
- package/dist/sdk/data/index.js +1 -0
- package/dist/sdk/data/page-reactive-data-root.d.ts +36 -0
- package/dist/sdk/data/page-reactive-data-root.js +4 -2
- package/dist/sdk/data/page-reactive-data-root.js.map +1 -1
- package/dist/sdk/data/reactive-data-provider.js +7 -3
- package/dist/sdk/data/reactive-data-provider.js.map +1 -1
- package/dist/sdk/data/reactive-list-item-data-provider.js +12 -4
- package/dist/sdk/data/reactive-list-item-data-provider.js.map +1 -1
- package/dist/sdk/data/use-reactive-data-settled.js +39 -0
- package/dist/sdk/data/use-reactive-data-settled.js.map +1 -0
- package/dist/sdk/data/use-reactive-entity.js +5 -1
- package/dist/sdk/data/use-reactive-entity.js.map +1 -1
- package/dist/sdk/generated/fragment-types.js +2 -0
- package/dist/sdk/generated/fragment-types.js.map +1 -1
- package/dist/sdk/generated/graphql.js.map +1 -1
- package/dist/sdk/i18n/messages/en-US.js +3 -0
- package/dist/sdk/i18n/messages/en-US.json +3 -0
- package/dist/sdk/i18n/messages/en-US.json.d.ts +3 -0
- package/dist/sdk/i18n/messages/es-ES.js +3 -0
- package/dist/sdk/i18n/messages/es-ES.json +3 -0
- package/dist/sdk/i18n/messages/pt-BR.js +3 -0
- package/dist/sdk/i18n/messages/pt-BR.json +3 -0
- package/dist/sdk/identity/use-identity-actions.js +3 -2
- package/dist/sdk/identity/use-identity-actions.js.map +1 -1
- package/dist/sdk/payment/gateway-origin.js +35 -0
- package/dist/sdk/payment/gateway-origin.js.map +1 -0
- package/dist/sdk/payment/gateway.d.ts +85 -0
- package/dist/sdk/payment/gateway.js +183 -0
- package/dist/sdk/payment/gateway.js.map +1 -0
- package/dist/sdk/payment/operations.d.ts +52 -0
- package/dist/sdk/payment/operations.js +223 -0
- package/dist/sdk/payment/operations.js.map +1 -0
- package/dist/sdk/payment/security-code-mask.js +71 -0
- package/dist/sdk/payment/security-code-mask.js.map +1 -0
- package/dist/sdk/payment/submission-registry.d.ts +33 -0
- package/dist/sdk/payment/submission-registry.js +58 -0
- package/dist/sdk/payment/submission-registry.js.map +1 -0
- package/dist/sdk/payment/use-payment-actions.d.ts +78 -0
- package/dist/sdk/payment/use-payment-actions.js +276 -0
- package/dist/sdk/payment/use-payment-actions.js.map +1 -0
- package/dist/sdk/payment/use-place-order.d.ts +65 -0
- package/dist/sdk/payment/use-place-order.js +640 -0
- package/dist/sdk/payment/use-place-order.js.map +1 -0
- package/dist/sdk/shipping/use-shipping-actions.js +1 -1
- package/dist/utils/loggable-error-name.js +49 -0
- package/dist/utils/loggable-error-name.js.map +1 -0
- package/dist/utils/resolve-authored-copy.js +18 -0
- package/dist/utils/resolve-authored-copy.js.map +1 -0
- package/dist/utils/substitute-template.js +33 -0
- package/dist/utils/substitute-template.js.map +1 -0
- package/package.json +2 -1
- package/src/styles/tailwind.css +34 -0
- package/dist/docs/client-profile-form.md +0 -192
- package/dist/matter/client-profile-form/client-profile-form.d.ts +0 -126
- package/dist/matter/client-profile-form/client-profile-form.js +0 -662
- package/dist/matter/client-profile-form/client-profile-form.js.map +0 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ActionProviderProps } from "./inject-actions.js";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
//#region src/sdk/actions/analytics-action-provider.d.ts
|
|
4
|
+
export type AnalyticsActionProviderProps = ActionProviderProps;
|
|
5
|
+
/**
|
|
6
|
+
* Compiler-emitted runtime wrapper for the `analytics` action namespace. The compiler wraps every
|
|
7
|
+
* element whose definition declares `actions: { analytics: [...] }` with
|
|
8
|
+
* `<AnalyticsActionProvider methods={[...]}>`, calls `useAnalyticsActions` once, narrows the
|
|
9
|
+
* surface to the declared `methods`, and publishes it through `ActionsOverrideContext` (merging with
|
|
10
|
+
* any outer namespace provider, per `useInjectActions`) for `useActionsOverride` to read further
|
|
11
|
+
* down. A namespace a component does not declare is never wrapped, so its hook is never called.
|
|
12
|
+
*/
|
|
13
|
+
export declare function AnalyticsActionProvider(props: AnalyticsActionProviderProps): ReactNode;
|
|
14
|
+
//#endregion
|
|
15
|
+
//# sourceMappingURL=analytics-action-provider.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useAnalyticsActions } from "../analytics/use-analytics-actions.js";
|
|
3
|
+
import { useInjectActions } from "./inject-actions.js";
|
|
4
|
+
//#region src/sdk/actions/analytics-action-provider.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Compiler-emitted runtime wrapper for the `analytics` action namespace. The compiler wraps every
|
|
7
|
+
* element whose definition declares `actions: { analytics: [...] }` with
|
|
8
|
+
* `<AnalyticsActionProvider methods={[...]}>`, calls `useAnalyticsActions` once, narrows the
|
|
9
|
+
* surface to the declared `methods`, and publishes it through `ActionsOverrideContext` (merging with
|
|
10
|
+
* any outer namespace provider, per `useInjectActions`) for `useActionsOverride` to read further
|
|
11
|
+
* down. A namespace a component does not declare is never wrapped, so its hook is never called.
|
|
12
|
+
*/
|
|
13
|
+
function AnalyticsActionProvider(props) {
|
|
14
|
+
return useInjectActions("analytics", useAnalyticsActions(), props);
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { AnalyticsActionProvider };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=analytics-action-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics-action-provider.js","names":[],"sources":["../../../src/sdk/actions/analytics-action-provider.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactNode } from 'react';\nimport { useAnalyticsActions } from '../analytics/use-analytics-actions.js';\nimport { type ActionProviderProps, useInjectActions } from './inject-actions.js';\n\nexport type AnalyticsActionProviderProps = ActionProviderProps;\n\n/**\n * Compiler-emitted runtime wrapper for the `analytics` action namespace. The compiler wraps every\n * element whose definition declares `actions: { analytics: [...] }` with\n * `<AnalyticsActionProvider methods={[...]}>`, calls `useAnalyticsActions` once, narrows the\n * surface to the declared `methods`, and publishes it through `ActionsOverrideContext` (merging with\n * any outer namespace provider, per `useInjectActions`) for `useActionsOverride` to read further\n * down. A namespace a component does not declare is never wrapped, so its hook is never called.\n */\nexport function AnalyticsActionProvider(props: AnalyticsActionProviderProps): ReactNode {\n return useInjectActions('analytics', useAnalyticsActions(), props);\n}\n"],"mappings":";;;;;;;;;;;;AAgBA,SAAgB,wBAAwB,OAAgD;CACtF,OAAO,iBAAiB,aAAa,oBAAoB,GAAG,KAAK;AACnE"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { ComponentActions } from "./types.js";
|
|
2
|
+
import { AnalyticsActionProvider, AnalyticsActionProviderProps } from "./analytics-action-provider.js";
|
|
2
3
|
import { CartActionProvider, CartActionProviderProps } from "./cart-action-provider.js";
|
|
3
4
|
import { IdentityActionProvider, IdentityActionProviderProps } from "./identity-action-provider.js";
|
|
5
|
+
import { PaymentActionProvider, PaymentActionProviderProps } from "./payment-action-provider.js";
|
|
4
6
|
import { RegionActionProvider, RegionActionProviderProps } from "./region-action-provider.js";
|
|
5
7
|
import { SessionActionProvider, SessionActionProviderProps } from "./session-action-provider.js";
|
|
6
8
|
import { ShippingActionProvider, ShippingActionProviderProps } from "./shipping-action-provider.js";
|
|
7
|
-
export { CartActionProvider, type CartActionProviderProps, IdentityActionProvider, type IdentityActionProviderProps, RegionActionProvider, type RegionActionProviderProps, SessionActionProvider, type SessionActionProviderProps, ShippingActionProvider, type ShippingActionProviderProps };
|
|
9
|
+
export { AnalyticsActionProvider, type AnalyticsActionProviderProps, CartActionProvider, type CartActionProviderProps, IdentityActionProvider, type IdentityActionProviderProps, PaymentActionProvider, type PaymentActionProviderProps, RegionActionProvider, type RegionActionProviderProps, SessionActionProvider, type SessionActionProviderProps, ShippingActionProvider, type ShippingActionProviderProps };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { AnalyticsActionProvider } from "./analytics-action-provider.js";
|
|
1
2
|
import { CartActionProvider } from "./cart-action-provider.js";
|
|
2
3
|
import { IdentityActionProvider } from "./identity-action-provider.js";
|
|
4
|
+
import { PaymentActionProvider } from "./payment-action-provider.js";
|
|
3
5
|
import { RegionActionProvider } from "./region-action-provider.js";
|
|
4
6
|
import { SessionActionProvider } from "./session-action-provider.js";
|
|
5
7
|
import { ShippingActionProvider } from "./shipping-action-provider.js";
|
|
6
|
-
export { CartActionProvider, IdentityActionProvider, RegionActionProvider, SessionActionProvider, ShippingActionProvider };
|
|
8
|
+
export { AnalyticsActionProvider, CartActionProvider, IdentityActionProvider, PaymentActionProvider, RegionActionProvider, SessionActionProvider, ShippingActionProvider };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ActionProviderProps } from "./inject-actions.js";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
//#region src/sdk/actions/payment-action-provider.d.ts
|
|
4
|
+
export type PaymentActionProviderProps = ActionProviderProps;
|
|
5
|
+
/**
|
|
6
|
+
* Compiler-emitted runtime wrapper for the `payment` action namespace. The compiler wraps every
|
|
7
|
+
* element whose definition declares `actions: { payment: [...] }` with
|
|
8
|
+
* `<PaymentActionProvider methods={[...]}>`, calls `usePaymentActions` once, narrows the surface to
|
|
9
|
+
* the declared `methods`, and publishes it through `ActionsOverrideContext` (merging with any outer
|
|
10
|
+
* namespace provider, per `useInjectActions`) for `useActionsOverride` to read further down. A
|
|
11
|
+
* namespace a component does not declare is never wrapped, so its hook is never called
|
|
12
|
+
* (specs/actions.md FR-6).
|
|
13
|
+
*
|
|
14
|
+
* Narrowing matters more here than elsewhere: a component that declares only `setMethod` never receives
|
|
15
|
+
* `sendPayments`, because its own provider replaces the whole `payment` namespace an outer provider
|
|
16
|
+
* published (specs/payment-action-namespace.md, Decision 5). Context does not narrow a component that
|
|
17
|
+
* declares no `payment` method at all: rendered inside one that does, it can read the outer set
|
|
18
|
+
* (specs/sfs-3395-action-provider-context-delivery.md, Decision 2).
|
|
19
|
+
*/
|
|
20
|
+
export declare function PaymentActionProvider(props: PaymentActionProviderProps): ReactNode;
|
|
21
|
+
//#endregion
|
|
22
|
+
//# sourceMappingURL=payment-action-provider.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useInjectActions } from "./inject-actions.js";
|
|
3
|
+
import { usePaymentActions } from "../payment/use-payment-actions.js";
|
|
4
|
+
//#region src/sdk/actions/payment-action-provider.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Compiler-emitted runtime wrapper for the `payment` action namespace. The compiler wraps every
|
|
7
|
+
* element whose definition declares `actions: { payment: [...] }` with
|
|
8
|
+
* `<PaymentActionProvider methods={[...]}>`, calls `usePaymentActions` once, narrows the surface to
|
|
9
|
+
* the declared `methods`, and publishes it through `ActionsOverrideContext` (merging with any outer
|
|
10
|
+
* namespace provider, per `useInjectActions`) for `useActionsOverride` to read further down. A
|
|
11
|
+
* namespace a component does not declare is never wrapped, so its hook is never called
|
|
12
|
+
* (specs/actions.md FR-6).
|
|
13
|
+
*
|
|
14
|
+
* Narrowing matters more here than elsewhere: a component that declares only `setMethod` never receives
|
|
15
|
+
* `sendPayments`, because its own provider replaces the whole `payment` namespace an outer provider
|
|
16
|
+
* published (specs/payment-action-namespace.md, Decision 5). Context does not narrow a component that
|
|
17
|
+
* declares no `payment` method at all: rendered inside one that does, it can read the outer set
|
|
18
|
+
* (specs/sfs-3395-action-provider-context-delivery.md, Decision 2).
|
|
19
|
+
*/
|
|
20
|
+
function PaymentActionProvider(props) {
|
|
21
|
+
return useInjectActions("payment", usePaymentActions(), props);
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { PaymentActionProvider };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=payment-action-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-action-provider.js","names":[],"sources":["../../../src/sdk/actions/payment-action-provider.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactNode } from 'react';\nimport { usePaymentActions } from '../payment/use-payment-actions.js';\nimport { type ActionProviderProps, useInjectActions } from './inject-actions.js';\n\nexport type PaymentActionProviderProps = ActionProviderProps;\n\n/**\n * Compiler-emitted runtime wrapper for the `payment` action namespace. The compiler wraps every\n * element whose definition declares `actions: { payment: [...] }` with\n * `<PaymentActionProvider methods={[...]}>`, calls `usePaymentActions` once, narrows the surface to\n * the declared `methods`, and publishes it through `ActionsOverrideContext` (merging with any outer\n * namespace provider, per `useInjectActions`) for `useActionsOverride` to read further down. A\n * namespace a component does not declare is never wrapped, so its hook is never called\n * (specs/actions.md FR-6).\n *\n * Narrowing matters more here than elsewhere: a component that declares only `setMethod` never receives\n * `sendPayments`, because its own provider replaces the whole `payment` namespace an outer provider\n * published (specs/payment-action-namespace.md, Decision 5). Context does not narrow a component that\n * declares no `payment` method at all: rendered inside one that does, it can read the outer set\n * (specs/sfs-3395-action-provider-context-delivery.md, Decision 2).\n */\nexport function PaymentActionProvider(props: PaymentActionProviderProps): ReactNode {\n return useInjectActions('payment', usePaymentActions(), props);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,sBAAsB,OAA8C;CAClF,OAAO,iBAAiB,WAAW,kBAAkB,GAAG,KAAK;AAC/D"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { AnalyticsActions } from "../analytics/use-analytics-actions.js";
|
|
1
2
|
import { CartActions } from "../cart/use-cart-actions.js";
|
|
2
3
|
import { IdentityActions } from "../identity/use-identity-actions.js";
|
|
4
|
+
import { PaymentActions } from "../payment/use-payment-actions.js";
|
|
3
5
|
import { RegionActions } from "../region/use-region-actions.js";
|
|
4
6
|
import { SessionActions } from "../session/use-session-actions.js";
|
|
5
7
|
import { ShippingActions } from "../shipping/use-shipping-actions.js";
|
|
@@ -7,12 +9,14 @@ import { ShippingActions } from "../shipping/use-shipping-actions.js";
|
|
|
7
9
|
/**
|
|
8
10
|
* The `$actions` prop the compiler injects (and `mockAction` builds for tests). Each namespace is
|
|
9
11
|
* optional and each method is optional within it, because a component only receives the methods it
|
|
10
|
-
* declared under `actions: { ... }`. Ships the `cart`, `identity`, `
|
|
11
|
-
* `shipping` namespaces.
|
|
12
|
+
* declared under `actions: { ... }`. Ships the `analytics`, `cart`, `identity`, `payment`, `region`,
|
|
13
|
+
* `session`, and `shipping` namespaces.
|
|
12
14
|
*/
|
|
13
15
|
export type ComponentActions = {
|
|
16
|
+
analytics?: Partial<AnalyticsActions>;
|
|
14
17
|
cart?: Partial<CartActions>;
|
|
15
18
|
identity?: Partial<IdentityActions>;
|
|
19
|
+
payment?: Partial<PaymentActions>;
|
|
16
20
|
region?: Partial<RegionActions>;
|
|
17
21
|
session?: Partial<SessionActions>;
|
|
18
22
|
shipping?: Partial<ShippingActions>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/sdk/analytics/analytics-destination.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Configurable analytics destination (specs/make-the-analytics-destination-configurable-for.md;
|
|
5
|
+
* credit-card epic T120; RFC `docs/features/credit-card/rfc.md` § D14 — Analytics events
|
|
6
|
+
* (cross-cutting)). `use-analytics-actions.ts`'s `pushToDataLayer` used to hardcode
|
|
7
|
+
* `window.dataLayer`. This module narrows "configurable destination" to the *name* of the
|
|
8
|
+
* `window` array the two standard payment events (`add_payment_info`, `purchase`) are pushed to —
|
|
9
|
+
* not a vendor id, not a transport, not a fan-out list. The payload and its GA4 shape are
|
|
10
|
+
* unchanged; only the array it lands in differs.
|
|
11
|
+
*
|
|
12
|
+
* Resolution precedence: the nearest `AnalyticsDestinationProvider` -> the build-inlined
|
|
13
|
+
* `process.env.NEXT_PUBLIC_ANALYTICS_DESTINATION` -> `DEFAULT_ANALYTICS_DESTINATION`. Resolution
|
|
14
|
+
* happens at call time, inside `track` (see `use-analytics-actions.ts`) — never once at module
|
|
15
|
+
* scope — so a build-time env change and a test's `vi.stubEnv` are both observed without a remount.
|
|
16
|
+
*
|
|
17
|
+
* A destination that is not a plain identifier, or that names `__proto__` / `constructor` /
|
|
18
|
+
* `prototype`, is never written to `window`: it is rejected, logged once, and resolution falls
|
|
19
|
+
* back to the default instead. A misconfiguration can only cost an analytics event — it can never
|
|
20
|
+
* block payment submission or order placement, the same invariant `use-analytics-actions.ts`
|
|
21
|
+
* already holds.
|
|
22
|
+
*/
|
|
23
|
+
/** The name of the `window` property the standard analytics events are pushed to. */
|
|
24
|
+
export type AnalyticsDestination = string;
|
|
25
|
+
export type AnalyticsDestinationProviderProps = Readonly<{
|
|
26
|
+
/**
|
|
27
|
+
* The `window` property name events are pushed to. Must be a build-time or merchant-authored
|
|
28
|
+
* constant — never wire this from user input. The identifier pattern and denylist block the
|
|
29
|
+
* known prototype-pollution keys, but a value that passes validation could still name a live,
|
|
30
|
+
* load-bearing global (`__NEXT_P`, `_gaq`, or a merchant queue with a hijacked `push`).
|
|
31
|
+
*/
|
|
32
|
+
destination: AnalyticsDestination;
|
|
33
|
+
children: ReactNode;
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* Publishes a destination to the subtree — for a host that renders the components by hand (an
|
|
37
|
+
* embedder, a Storybook story, a test) and wants to supply or override the destination in React
|
|
38
|
+
* rather than through the build. Optional: an unprovided tree is the normal case, and the nearest
|
|
39
|
+
* provider wins over any outer one.
|
|
40
|
+
*/
|
|
41
|
+
export declare function AnalyticsDestinationProvider({ destination, children }: AnalyticsDestinationProviderProps): ReactNode;
|
|
42
|
+
//#endregion
|
|
43
|
+
//# sourceMappingURL=analytics-destination.d.ts.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/sdk/analytics/analytics-destination.tsx
|
|
5
|
+
/** The sink every store used before this task, and the fallback for anything invalid. */
|
|
6
|
+
const DEFAULT_ANALYTICS_DESTINATION = "dataLayer";
|
|
7
|
+
/** A plain property name: no dots, no dashes, no spaces, no leading digit. */
|
|
8
|
+
const DESTINATION_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
9
|
+
/** Never written to `window`, whatever the build says. */
|
|
10
|
+
const UNSAFE_DESTINATION_KEYS = [
|
|
11
|
+
"__proto__",
|
|
12
|
+
"constructor",
|
|
13
|
+
"prototype"
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* A plain, safe `window` property name: matches the identifier pattern and is absent from the
|
|
17
|
+
* unsafe-key denylist. Operates on the value exactly as given — trimming, and treating
|
|
18
|
+
* empty-after-trim as unset, are `resolveAnalyticsDestination`'s job, not this predicate's.
|
|
19
|
+
*/
|
|
20
|
+
function isValidAnalyticsDestination(value) {
|
|
21
|
+
return DESTINATION_PATTERN.test(value) && !UNSAFE_DESTINATION_KEYS.some((unsafeKey) => unsafeKey === value);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Returns the first non-empty-after-trim candidate in precedence order — `override`, then the
|
|
25
|
+
* build-inlined env var — together with whether that candidate is valid. Resolution never walks
|
|
26
|
+
* past that first configured candidate: an invalid `override` is never rescued by a valid env
|
|
27
|
+
* var, it falls straight through to `resolveAnalyticsDestination`'s default, exactly like an
|
|
28
|
+
* invalid env var with no override does. Validation itself is `isValidAnalyticsDestination`'s
|
|
29
|
+
* job — this function only picks which single candidate gets validated.
|
|
30
|
+
*/
|
|
31
|
+
function firstConfiguredCandidate(override) {
|
|
32
|
+
const envValue = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_ANALYTICS_DESTINATION : void 0;
|
|
33
|
+
for (const candidate of [override, envValue]) {
|
|
34
|
+
if (candidate === void 0) continue;
|
|
35
|
+
const trimmed = candidate.trim();
|
|
36
|
+
if (trimmed.length === 0) continue;
|
|
37
|
+
return {
|
|
38
|
+
value: trimmed,
|
|
39
|
+
valid: isValidAnalyticsDestination(trimmed)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Precedence: `override` (context) -> NEXT_PUBLIC_ANALYTICS_DESTINATION -> DEFAULT.
|
|
45
|
+
* Trims; treats empty-after-trim as unset. A present-but-invalid value logs once via
|
|
46
|
+
* console.error and resolves to DEFAULT. Never throws.
|
|
47
|
+
*
|
|
48
|
+
* `hasLoggedRejection`, when passed, is a mutable latch: the first rejection while it is `false`
|
|
49
|
+
* logs and flips it to `true`; every later rejection while it stays `true` is silent. Omit it —
|
|
50
|
+
* every direct/unit-test call does — to log every rejection. `useAnalyticsActions` is the only
|
|
51
|
+
* caller that owns one, scoped to its own hook instance via `useRef`, so "logs once" means once
|
|
52
|
+
* per mounted `useAnalyticsActions()`, not once for the whole page.
|
|
53
|
+
*/
|
|
54
|
+
function resolveAnalyticsDestination(options) {
|
|
55
|
+
const outcome = firstConfiguredCandidate(options?.override);
|
|
56
|
+
if (outcome === void 0) return DEFAULT_ANALYTICS_DESTINATION;
|
|
57
|
+
if (outcome.valid) return outcome.value;
|
|
58
|
+
const hasLoggedRejection = options?.hasLoggedRejection;
|
|
59
|
+
if (!hasLoggedRejection?.current) {
|
|
60
|
+
const message = `[analytics.track] ${JSON.stringify(outcome.value)} is not a valid analytics destination; falling back to ${JSON.stringify(DEFAULT_ANALYTICS_DESTINATION)}`;
|
|
61
|
+
console.error(message);
|
|
62
|
+
if (hasLoggedRejection) hasLoggedRejection.current = true;
|
|
63
|
+
}
|
|
64
|
+
return DEFAULT_ANALYTICS_DESTINATION;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Private, so the only read is `useAnalyticsDestination` — which, unlike `useStorefrontContext`,
|
|
68
|
+
* never throws when unprovided: an unprovided tree is the normal case here, not a bug.
|
|
69
|
+
*/
|
|
70
|
+
const AnalyticsDestinationContext = createContext(void 0);
|
|
71
|
+
/**
|
|
72
|
+
* Publishes a destination to the subtree — for a host that renders the components by hand (an
|
|
73
|
+
* embedder, a Storybook story, a test) and wants to supply or override the destination in React
|
|
74
|
+
* rather than through the build. Optional: an unprovided tree is the normal case, and the nearest
|
|
75
|
+
* provider wins over any outer one.
|
|
76
|
+
*/
|
|
77
|
+
function AnalyticsDestinationProvider({ destination, children }) {
|
|
78
|
+
return /* @__PURE__ */ jsx(AnalyticsDestinationContext.Provider, {
|
|
79
|
+
value: destination,
|
|
80
|
+
children
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/** The nearest provided destination, or `undefined` when no provider is in the tree. Never throws. */
|
|
84
|
+
function useAnalyticsDestination() {
|
|
85
|
+
return useContext(AnalyticsDestinationContext);
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
export { AnalyticsDestinationProvider, DEFAULT_ANALYTICS_DESTINATION, isValidAnalyticsDestination, resolveAnalyticsDestination, useAnalyticsDestination };
|
|
89
|
+
|
|
90
|
+
//# sourceMappingURL=analytics-destination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics-destination.js","names":[],"sources":["../../../src/sdk/analytics/analytics-destination.tsx"],"sourcesContent":["'use client';\n\nimport { createContext, type ReactNode, useContext } from 'react';\n\n/**\n * Configurable analytics destination (specs/make-the-analytics-destination-configurable-for.md;\n * credit-card epic T120; RFC `docs/features/credit-card/rfc.md` § D14 — Analytics events\n * (cross-cutting)). `use-analytics-actions.ts`'s `pushToDataLayer` used to hardcode\n * `window.dataLayer`. This module narrows \"configurable destination\" to the *name* of the\n * `window` array the two standard payment events (`add_payment_info`, `purchase`) are pushed to —\n * not a vendor id, not a transport, not a fan-out list. The payload and its GA4 shape are\n * unchanged; only the array it lands in differs.\n *\n * Resolution precedence: the nearest `AnalyticsDestinationProvider` -> the build-inlined\n * `process.env.NEXT_PUBLIC_ANALYTICS_DESTINATION` -> `DEFAULT_ANALYTICS_DESTINATION`. Resolution\n * happens at call time, inside `track` (see `use-analytics-actions.ts`) — never once at module\n * scope — so a build-time env change and a test's `vi.stubEnv` are both observed without a remount.\n *\n * A destination that is not a plain identifier, or that names `__proto__` / `constructor` /\n * `prototype`, is never written to `window`: it is rejected, logged once, and resolution falls\n * back to the default instead. A misconfiguration can only cost an analytics event — it can never\n * block payment submission or order placement, the same invariant `use-analytics-actions.ts`\n * already holds.\n */\n\n/** The name of the `window` property the standard analytics events are pushed to. */\nexport type AnalyticsDestination = string;\n\n/** The sink every store used before this task, and the fallback for anything invalid. */\nexport const DEFAULT_ANALYTICS_DESTINATION = 'dataLayer';\n\n/** A plain property name: no dots, no dashes, no spaces, no leading digit. */\nconst DESTINATION_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\n/** Never written to `window`, whatever the build says. */\nconst UNSAFE_DESTINATION_KEYS = ['__proto__', 'constructor', 'prototype'] as const;\n\n/**\n * A plain, safe `window` property name: matches the identifier pattern and is absent from the\n * unsafe-key denylist. Operates on the value exactly as given — trimming, and treating\n * empty-after-trim as unset, are `resolveAnalyticsDestination`'s job, not this predicate's.\n */\nexport function isValidAnalyticsDestination(value: string): boolean {\n return DESTINATION_PATTERN.test(value) && !UNSAFE_DESTINATION_KEYS.some((unsafeKey) => unsafeKey === value);\n}\n\n/** The first configured candidate in precedence order, paired with whether it is valid. */\ntype CandidateOutcome = Readonly<{ value: string; valid: boolean }>;\n\n/**\n * Returns the first non-empty-after-trim candidate in precedence order — `override`, then the\n * build-inlined env var — together with whether that candidate is valid. Resolution never walks\n * past that first configured candidate: an invalid `override` is never rescued by a valid env\n * var, it falls straight through to `resolveAnalyticsDestination`'s default, exactly like an\n * invalid env var with no override does. Validation itself is `isValidAnalyticsDestination`'s\n * job — this function only picks which single candidate gets validated.\n */\nfunction firstConfiguredCandidate(override: string | undefined): CandidateOutcome | undefined {\n // `typeof process !== 'undefined'` guards a client bundle with no `process` shim at all (a\n // Vite/Rollup/esbuild embedder, US-2's stated host) — unlike a direct read, `typeof` never\n // throws when the identifier doesn't exist at all. The member expression itself stays literal\n // so Next's build-time inlining can still statically replace it (Decision 5).\n const envValue = typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_ANALYTICS_DESTINATION : undefined;\n\n for (const candidate of [override, envValue]) {\n if (candidate === undefined) continue;\n const trimmed = candidate.trim();\n if (trimmed.length === 0) continue;\n return { value: trimmed, valid: isValidAnalyticsDestination(trimmed) };\n }\n return undefined;\n}\n\n/**\n * Precedence: `override` (context) -> NEXT_PUBLIC_ANALYTICS_DESTINATION -> DEFAULT.\n * Trims; treats empty-after-trim as unset. A present-but-invalid value logs once via\n * console.error and resolves to DEFAULT. Never throws.\n *\n * `hasLoggedRejection`, when passed, is a mutable latch: the first rejection while it is `false`\n * logs and flips it to `true`; every later rejection while it stays `true` is silent. Omit it —\n * every direct/unit-test call does — to log every rejection. `useAnalyticsActions` is the only\n * caller that owns one, scoped to its own hook instance via `useRef`, so \"logs once\" means once\n * per mounted `useAnalyticsActions()`, not once for the whole page.\n */\nexport function resolveAnalyticsDestination(options?: {\n override?: string;\n hasLoggedRejection?: { current: boolean };\n}): AnalyticsDestination {\n const outcome = firstConfiguredCandidate(options?.override);\n if (outcome === undefined) return DEFAULT_ANALYTICS_DESTINATION;\n if (outcome.valid) return outcome.value;\n\n const hasLoggedRejection = options?.hasLoggedRejection;\n if (!hasLoggedRejection?.current) {\n const message =\n `[analytics.track] ${JSON.stringify(outcome.value)} is not a valid analytics destination; ` +\n `falling back to ${JSON.stringify(DEFAULT_ANALYTICS_DESTINATION)}`;\n console.error(message);\n if (hasLoggedRejection) hasLoggedRejection.current = true;\n }\n return DEFAULT_ANALYTICS_DESTINATION;\n}\n\nexport type AnalyticsDestinationProviderProps = Readonly<{\n /**\n * The `window` property name events are pushed to. Must be a build-time or merchant-authored\n * constant — never wire this from user input. The identifier pattern and denylist block the\n * known prototype-pollution keys, but a value that passes validation could still name a live,\n * load-bearing global (`__NEXT_P`, `_gaq`, or a merchant queue with a hijacked `push`).\n */\n destination: AnalyticsDestination;\n children: ReactNode;\n}>;\n\n/**\n * Private, so the only read is `useAnalyticsDestination` — which, unlike `useStorefrontContext`,\n * never throws when unprovided: an unprovided tree is the normal case here, not a bug.\n */\nconst AnalyticsDestinationContext = createContext<AnalyticsDestination | undefined>(undefined);\n\n/**\n * Publishes a destination to the subtree — for a host that renders the components by hand (an\n * embedder, a Storybook story, a test) and wants to supply or override the destination in React\n * rather than through the build. Optional: an unprovided tree is the normal case, and the nearest\n * provider wins over any outer one.\n */\nexport function AnalyticsDestinationProvider({ destination, children }: AnalyticsDestinationProviderProps): ReactNode {\n return <AnalyticsDestinationContext.Provider value={destination}>{children}</AnalyticsDestinationContext.Provider>;\n}\n\n/** The nearest provided destination, or `undefined` when no provider is in the tree. Never throws. */\nexport function useAnalyticsDestination(): AnalyticsDestination | undefined {\n return useContext(AnalyticsDestinationContext);\n}\n"],"mappings":";;;;;AA6BA,MAAa,gCAAgC;;AAG7C,MAAM,sBAAsB;;AAG5B,MAAM,0BAA0B;CAAC;CAAa;CAAe;AAAW;;;;;;AAOxE,SAAgB,4BAA4B,OAAwB;CAClE,OAAO,oBAAoB,KAAK,KAAK,KAAK,CAAC,wBAAwB,MAAM,cAAc,cAAc,KAAK;AAC5G;;;;;;;;;AAaA,SAAS,yBAAyB,UAA4D;CAK5F,MAAM,WAAW,OAAO,YAAY,cAAc,QAAQ,IAAI,oCAAoC,KAAA;CAElG,KAAK,MAAM,aAAa,CAAC,UAAU,QAAQ,GAAG;EAC5C,IAAI,cAAc,KAAA,GAAW;EAC7B,MAAM,UAAU,UAAU,KAAK;EAC/B,IAAI,QAAQ,WAAW,GAAG;EAC1B,OAAO;GAAE,OAAO;GAAS,OAAO,4BAA4B,OAAO;EAAE;CACvE;AAEF;;;;;;;;;;;;AAaA,SAAgB,4BAA4B,SAGnB;CACvB,MAAM,UAAU,yBAAyB,SAAS,QAAQ;CAC1D,IAAI,YAAY,KAAA,GAAW,OAAO;CAClC,IAAI,QAAQ,OAAO,OAAO,QAAQ;CAElC,MAAM,qBAAqB,SAAS;CACpC,IAAI,CAAC,oBAAoB,SAAS;EAChC,MAAM,UACJ,qBAAqB,KAAK,UAAU,QAAQ,KAAK,EAAE,yDAChC,KAAK,UAAU,6BAA6B;EACjE,QAAQ,MAAM,OAAO;EACrB,IAAI,oBAAoB,mBAAmB,UAAU;CACvD;CACA,OAAO;AACT;;;;;AAiBA,MAAM,8BAA8B,cAAgD,KAAA,CAAS;;;;;;;AAQ7F,SAAgB,6BAA6B,EAAE,aAAa,YAA0D;CACpH,OAAO,oBAAC,4BAA4B,UAA7B;EAAsC,OAAO;EAAc;CAA+C,CAAA;AACnH;;AAGA,SAAgB,0BAA4D;CAC1E,OAAO,WAAW,2BAA2B;AAC/C"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import "../../core/dist/index.js";
|
|
2
|
+
//#region src/sdk/analytics/use-analytics-actions.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The `analytics` action namespace (specs/analytics-events.md) — a dispatcher-flavor namespace
|
|
5
|
+
* (docs/storefront-core/adr-0010-actions-own-the-write-side.md; docs/storefront-core/rfc-actions.md
|
|
6
|
+
* § "Three binding flavors") that pushes the two standard payment analytics events,
|
|
7
|
+
* `add_payment_info` and `purchase`, to a `window` array — `window.dataLayer` by default, the
|
|
8
|
+
* GTM/GA4-recognizable event convention. It never touches the Apollo cache, never issues a GraphQL
|
|
9
|
+
* operation, and never throws: a broken tag-manager script can never block payment submission or
|
|
10
|
+
* order placement.
|
|
11
|
+
*
|
|
12
|
+
* The destination array is configurable at build time
|
|
13
|
+
* (specs/make-the-analytics-destination-configurable-for.md; T120) — see
|
|
14
|
+
* `./analytics-destination.js` for the resolution precedence, validation, and the
|
|
15
|
+
* `AnalyticsDestinationProvider`. The pushed entry's shape is a data *contract* (a well-known
|
|
16
|
+
* browser-global array shape), not a *provider* — no vendor SDK, no server call. A future
|
|
17
|
+
* platform/foundation epic may replace this sink with a real `AnalyticsContext`; the
|
|
18
|
+
* `track(event: AnalyticsEvent): void` signature and `AnalyticsActions` type are the stable public
|
|
19
|
+
* contract every call site depends on until then (US-4).
|
|
20
|
+
*/
|
|
21
|
+
export type AnalyticsMoney = Readonly<{
|
|
22
|
+
amount: number;
|
|
23
|
+
currencyCode: string;
|
|
24
|
+
}>;
|
|
25
|
+
export type AnalyticsLineItem = Readonly<{
|
|
26
|
+
sku: string;
|
|
27
|
+
name: string;
|
|
28
|
+
price: AnalyticsMoney;
|
|
29
|
+
quantity: number;
|
|
30
|
+
}>;
|
|
31
|
+
/** Dispatched by T7's CardEntry, before opening the transaction. */
|
|
32
|
+
export type AddPaymentInfoEvent = Readonly<{
|
|
33
|
+
name: 'add_payment_info';
|
|
34
|
+
value: AnalyticsMoney;
|
|
35
|
+
/**
|
|
36
|
+
* Derived from the selected method's `groupName` at the moment this fires (T140, Decision 5).
|
|
37
|
+
* Optional because the event fires on the cart total, before a method is necessarily selected —
|
|
38
|
+
* omitted rather than a guessed literal, so a downstream consumer never reads a false payment kind.
|
|
39
|
+
* `toEcommercePayload` below drops the corresponding key entirely when this is absent.
|
|
40
|
+
*/
|
|
41
|
+
paymentType?: 'Credit Card' | 'Debit Card';
|
|
42
|
+
items?: readonly AnalyticsLineItem[];
|
|
43
|
+
}>;
|
|
44
|
+
/** Dispatched by T13's usePlaceOrder, after a successful gateway callback. */
|
|
45
|
+
export type PurchaseEvent = Readonly<{
|
|
46
|
+
name: 'purchase';
|
|
47
|
+
transactionId: string;
|
|
48
|
+
value: AnalyticsMoney;
|
|
49
|
+
items?: readonly AnalyticsLineItem[];
|
|
50
|
+
}>;
|
|
51
|
+
export type AnalyticsEvent = AddPaymentInfoEvent | PurchaseEvent;
|
|
52
|
+
export type AnalyticsActions = Readonly<{
|
|
53
|
+
/** Push a standard analytics event to window.dataLayer. Fire-and-forget; never throws. */
|
|
54
|
+
track: (event: AnalyticsEvent) => void;
|
|
55
|
+
}>;
|
|
56
|
+
//#endregion
|
|
57
|
+
//# sourceMappingURL=use-analytics-actions.d.ts.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { defineActions } from "../../core/dist/actions/registry.js";
|
|
3
|
+
import "../../core/dist/index.js";
|
|
4
|
+
import { DEFAULT_ANALYTICS_DESTINATION, resolveAnalyticsDestination, useAnalyticsDestination } from "./analytics-destination.js";
|
|
5
|
+
import { useCallback, useMemo, useRef } from "react";
|
|
6
|
+
//#region src/sdk/analytics/use-analytics-actions.ts
|
|
7
|
+
function toEcommerceItems(items) {
|
|
8
|
+
return (items ?? []).map((item) => ({
|
|
9
|
+
item_id: item.sku,
|
|
10
|
+
item_name: item.name,
|
|
11
|
+
price: item.price.amount,
|
|
12
|
+
quantity: item.quantity
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
function toEcommercePayload(event) {
|
|
16
|
+
if (event.name === "add_payment_info") return {
|
|
17
|
+
currency: event.value.currencyCode,
|
|
18
|
+
value: event.value.amount,
|
|
19
|
+
...event.paymentType !== void 0 ? { payment_type: event.paymentType } : {},
|
|
20
|
+
items: toEcommerceItems(event.items)
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
transaction_id: event.transactionId,
|
|
24
|
+
currency: event.value.currencyCode,
|
|
25
|
+
value: event.value.amount,
|
|
26
|
+
items: toEcommerceItems(event.items)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function pushToDataLayer(event, destination) {
|
|
30
|
+
if (typeof window === "undefined") return;
|
|
31
|
+
let layer = Reflect.get(window, destination);
|
|
32
|
+
if (layer === void 0) {
|
|
33
|
+
layer = [];
|
|
34
|
+
Reflect.set(window, destination, layer);
|
|
35
|
+
} else if (!Array.isArray(layer)) {
|
|
36
|
+
console.error(`[analytics.track] window.${destination} already exists and is not an array; the event was dropped`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
layer.push({
|
|
40
|
+
event: event.name,
|
|
41
|
+
ecommerce: toEcommercePayload(event)
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The `analytics` namespace hook. Registered with the first-party action registry via
|
|
46
|
+
* `analyticsActions` below; the compiler-emitted `AnalyticsActionProvider` calls it and threads
|
|
47
|
+
* the narrowed methods into each consumer's `$actions.analytics`. Dispatcher flavor: no
|
|
48
|
+
* `useMutation`, no cache read or write (ADR-0010). Fire-and-forget — `track` never throws, so a
|
|
49
|
+
* broken tag-manager script can never block payment submission or order placement.
|
|
50
|
+
*/
|
|
51
|
+
function useAnalyticsActions() {
|
|
52
|
+
const override = useAnalyticsDestination();
|
|
53
|
+
const hasLoggedRejectionRef = useRef(false);
|
|
54
|
+
const track = useCallback((event) => {
|
|
55
|
+
if (typeof window === "undefined") return;
|
|
56
|
+
let destination = DEFAULT_ANALYTICS_DESTINATION;
|
|
57
|
+
try {
|
|
58
|
+
destination = resolveAnalyticsDestination({
|
|
59
|
+
override,
|
|
60
|
+
hasLoggedRejection: hasLoggedRejectionRef
|
|
61
|
+
});
|
|
62
|
+
pushToDataLayer(event, destination);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(`[analytics.track] window.${destination} push failed`, error);
|
|
65
|
+
}
|
|
66
|
+
}, [override]);
|
|
67
|
+
return useMemo(() => ({ track }), [track]);
|
|
68
|
+
}
|
|
69
|
+
defineActions({
|
|
70
|
+
namespace: "analytics",
|
|
71
|
+
description: "Dispatcher-flavor namespace that pushes standard add_payment_info / purchase events to window.dataLayer. Never touches the Apollo cache.",
|
|
72
|
+
provider: {
|
|
73
|
+
name: "AnalyticsActionProvider",
|
|
74
|
+
importPath: "@vtex/faststore/internal/client"
|
|
75
|
+
},
|
|
76
|
+
methods: { track: {
|
|
77
|
+
description: "Push a standard analytics event (add_payment_info | purchase) to window.dataLayer. Fire-and-forget; never throws.",
|
|
78
|
+
payload: [
|
|
79
|
+
{
|
|
80
|
+
name: "name",
|
|
81
|
+
type: "string"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "currencyCode",
|
|
85
|
+
type: "string"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "amount",
|
|
89
|
+
type: "number"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "paymentType",
|
|
93
|
+
type: "string",
|
|
94
|
+
optional: true
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "transactionId",
|
|
98
|
+
type: "string",
|
|
99
|
+
optional: true
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
} },
|
|
103
|
+
use: useAnalyticsActions
|
|
104
|
+
});
|
|
105
|
+
//#endregion
|
|
106
|
+
export { useAnalyticsActions };
|
|
107
|
+
|
|
108
|
+
//# sourceMappingURL=use-analytics-actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-analytics-actions.js","names":[],"sources":["../../../src/sdk/analytics/use-analytics-actions.ts"],"sourcesContent":["'use client';\n\nimport { defineActions } from '@vtex/fs-core';\nimport { useCallback, useMemo, useRef } from 'react';\nimport {\n type AnalyticsDestination,\n DEFAULT_ANALYTICS_DESTINATION,\n resolveAnalyticsDestination,\n useAnalyticsDestination,\n} from './analytics-destination.js';\n\n/**\n * The `analytics` action namespace (specs/analytics-events.md) — a dispatcher-flavor namespace\n * (docs/storefront-core/adr-0010-actions-own-the-write-side.md; docs/storefront-core/rfc-actions.md\n * § \"Three binding flavors\") that pushes the two standard payment analytics events,\n * `add_payment_info` and `purchase`, to a `window` array — `window.dataLayer` by default, the\n * GTM/GA4-recognizable event convention. It never touches the Apollo cache, never issues a GraphQL\n * operation, and never throws: a broken tag-manager script can never block payment submission or\n * order placement.\n *\n * The destination array is configurable at build time\n * (specs/make-the-analytics-destination-configurable-for.md; T120) — see\n * `./analytics-destination.js` for the resolution precedence, validation, and the\n * `AnalyticsDestinationProvider`. The pushed entry's shape is a data *contract* (a well-known\n * browser-global array shape), not a *provider* — no vendor SDK, no server call. A future\n * platform/foundation epic may replace this sink with a real `AnalyticsContext`; the\n * `track(event: AnalyticsEvent): void` signature and `AnalyticsActions` type are the stable public\n * contract every call site depends on until then (US-4).\n */\n\nexport type AnalyticsMoney = Readonly<{\n amount: number;\n currencyCode: string;\n}>;\n\nexport type AnalyticsLineItem = Readonly<{\n sku: string;\n name: string;\n price: AnalyticsMoney;\n quantity: number;\n}>;\n\n/** Dispatched by T7's CardEntry, before opening the transaction. */\nexport type AddPaymentInfoEvent = Readonly<{\n name: 'add_payment_info';\n value: AnalyticsMoney;\n /**\n * Derived from the selected method's `groupName` at the moment this fires (T140, Decision 5).\n * Optional because the event fires on the cart total, before a method is necessarily selected —\n * omitted rather than a guessed literal, so a downstream consumer never reads a false payment kind.\n * `toEcommercePayload` below drops the corresponding key entirely when this is absent.\n */\n paymentType?: 'Credit Card' | 'Debit Card';\n items?: readonly AnalyticsLineItem[];\n}>;\n\n/** Dispatched by T13's usePlaceOrder, after a successful gateway callback. */\nexport type PurchaseEvent = Readonly<{\n name: 'purchase';\n transactionId: string;\n value: AnalyticsMoney;\n items?: readonly AnalyticsLineItem[];\n}>;\n\nexport type AnalyticsEvent = AddPaymentInfoEvent | PurchaseEvent;\n\nexport type AnalyticsActions = Readonly<{\n /** Push a standard analytics event to window.dataLayer. Fire-and-forget; never throws. */\n track: (event: AnalyticsEvent) => void;\n}>;\n\n/** The shape pushed onto window.dataLayer — GTM/GA4's own event convention. */\nexport type AnalyticsDataLayerEvent = Readonly<{\n event: 'add_payment_info' | 'purchase';\n ecommerce: Record<string, unknown>;\n}>;\n\nfunction toEcommerceItems(items: readonly AnalyticsLineItem[] | undefined) {\n return (items ?? []).map((item) => ({\n item_id: item.sku,\n item_name: item.name,\n price: item.price.amount,\n quantity: item.quantity,\n }));\n}\n\nfunction toEcommercePayload(event: AnalyticsEvent): Record<string, unknown> {\n if (event.name === 'add_payment_info') {\n return {\n currency: event.value.currencyCode,\n value: event.value.amount,\n // T140, Decision 5: the key itself must be absent when `paymentType` is, not merely `undefined`\n // — `pushToDataLayer` pushes this object as-is with no serialization step to strip it, and\n // `'payment_type' in ecommerce` would otherwise still report true.\n ...(event.paymentType !== undefined ? { payment_type: event.paymentType } : {}),\n items: toEcommerceItems(event.items),\n };\n }\n return {\n transaction_id: event.transactionId,\n currency: event.value.currencyCode,\n value: event.value.amount,\n items: toEcommerceItems(event.items),\n };\n}\n\n// `Reflect.get`/`Reflect.set` below match `storefront-provider.tsx`'s existing pattern for a\n// dynamic `window` key — they are not a security mechanism. `Reflect.set(target, k, v)` invokes\n// an inherited setter and creates no own property, exactly like `window[k] = v` would; it adds no\n// hardening on its own. What actually blocks prototype pollution is\n// `isValidAnalyticsDestination`'s denylist and identifier pattern, checked by the caller before\n// `destination` ever reaches this function, plus the non-array guard immediately below.\n//\n// The cast below, not a `declare global` augmentation of `Window`, is what keeps this typed: a\n// global augmentation here would ship inside this package's own `.d.ts` output and hand every\n// consumer's `window.dataLayer` this package's narrow event union, wrong for a store that also\n// pushes its own GTM events (packages/components/scripts/dist-type-boundary.test.ts). `destination`\n// is a runtime string anyway — `Window` has nothing to look up a dynamic key against — so the local\n// cast is also the only type-safe option, not merely the non-leaking one.\nfunction pushToDataLayer(event: AnalyticsEvent, destination: AnalyticsDestination): void {\n if (typeof window === 'undefined') return;\n\n let layer = Reflect.get(window, destination) as AnalyticsDataLayerEvent[] | undefined;\n if (layer === undefined) {\n layer = [];\n Reflect.set(window, destination, layer);\n } else if (!Array.isArray(layer)) {\n console.error(`[analytics.track] window.${destination} already exists and is not an array; the event was dropped`);\n return;\n }\n\n layer.push({ event: event.name, ecommerce: toEcommercePayload(event) });\n}\n\n/**\n * The `analytics` namespace hook. Registered with the first-party action registry via\n * `analyticsActions` below; the compiler-emitted `AnalyticsActionProvider` calls it and threads\n * the narrowed methods into each consumer's `$actions.analytics`. Dispatcher flavor: no\n * `useMutation`, no cache read or write (ADR-0010). Fire-and-forget — `track` never throws, so a\n * broken tag-manager script can never block payment submission or order placement.\n */\nexport function useAnalyticsActions(): AnalyticsActions {\n const override = useAnalyticsDestination();\n // Scoped to this hook instance, not the module: \"logs once\" means once per mounted\n // `useAnalyticsActions()`, so two independent call sites each still get their own first warning.\n const hasLoggedRejectionRef = useRef(false);\n\n const track = useCallback<AnalyticsActions['track']>(\n (event) => {\n // The SSR guard runs before any destination resolution is attempted — not just before the\n // write. Preserved from the pre-T120 behaviour (US-3, \"SSR — no destination resolution\n // attempted at all\").\n if (typeof window === 'undefined') return;\n let destination: AnalyticsDestination = DEFAULT_ANALYTICS_DESTINATION;\n try {\n destination = resolveAnalyticsDestination({\n override,\n hasLoggedRejection: hasLoggedRejectionRef,\n });\n pushToDataLayer(event, destination);\n } catch (error) {\n console.error(`[analytics.track] window.${destination} push failed`, error);\n }\n },\n [override],\n );\n\n return useMemo(() => ({ track }), [track]);\n}\n\n/**\n * Declares the `analytics` action namespace — the single source of truth for its method/payload\n * metadata, runtime provider, and `use` hook (mirrors `cartActions`, `shippingActions`). The\n * native catalog builder (`scripts/build-catalog.mjs`) statically reads this call to emit\n * `actions.json`; at runtime `AnalyticsActionProvider` calls `useAnalyticsActions` directly.\n *\n * The `payload` below is a flattened, best-effort scalar approximation of `AnalyticsEvent` for\n * catalog/docs tooling: `ActionPayloadField.type` supports only scalar primitives and cannot\n * express a discriminated union or a nested `items` array. The TS `AnalyticsEvent` union above is\n * the source of truth for the real call shape.\n */\nexport const analyticsActions = defineActions({\n namespace: 'analytics',\n description:\n 'Dispatcher-flavor namespace that pushes standard add_payment_info / purchase events to window.dataLayer. Never touches the Apollo cache.',\n provider: { name: 'AnalyticsActionProvider', importPath: '@vtex/faststore/internal/client' },\n methods: {\n track: {\n description:\n 'Push a standard analytics event (add_payment_info | purchase) to window.dataLayer. Fire-and-forget; never throws.',\n payload: [\n { name: 'name', type: 'string' },\n { name: 'currencyCode', type: 'string' },\n { name: 'amount', type: 'number' },\n { name: 'paymentType', type: 'string', optional: true },\n { name: 'transactionId', type: 'string', optional: true },\n ],\n },\n },\n use: useAnalyticsActions,\n});\n"],"mappings":";;;;;;AA6EA,SAAS,iBAAiB,OAAiD;CACzE,QAAQ,SAAS,CAAC,EAAA,CAAG,KAAK,UAAU;EAClC,SAAS,KAAK;EACd,WAAW,KAAK;EAChB,OAAO,KAAK,MAAM;EAClB,UAAU,KAAK;CACjB,EAAE;AACJ;AAEA,SAAS,mBAAmB,OAAgD;CAC1E,IAAI,MAAM,SAAS,oBACjB,OAAO;EACL,UAAU,MAAM,MAAM;EACtB,OAAO,MAAM,MAAM;EAInB,GAAI,MAAM,gBAAgB,KAAA,IAAY,EAAE,cAAc,MAAM,YAAY,IAAI,CAAC;EAC7E,OAAO,iBAAiB,MAAM,KAAK;CACrC;CAEF,OAAO;EACL,gBAAgB,MAAM;EACtB,UAAU,MAAM,MAAM;EACtB,OAAO,MAAM,MAAM;EACnB,OAAO,iBAAiB,MAAM,KAAK;CACrC;AACF;AAeA,SAAS,gBAAgB,OAAuB,aAAyC;CACvF,IAAI,OAAO,WAAW,aAAa;CAEnC,IAAI,QAAQ,QAAQ,IAAI,QAAQ,WAAW;CAC3C,IAAI,UAAU,KAAA,GAAW;EACvB,QAAQ,CAAC;EACT,QAAQ,IAAI,QAAQ,aAAa,KAAK;CACxC,OAAO,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;EAChC,QAAQ,MAAM,4BAA4B,YAAY,2DAA2D;EACjH;CACF;CAEA,MAAM,KAAK;EAAE,OAAO,MAAM;EAAM,WAAW,mBAAmB,KAAK;CAAE,CAAC;AACxE;;;;;;;;AASA,SAAgB,sBAAwC;CACtD,MAAM,WAAW,wBAAwB;CAGzC,MAAM,wBAAwB,OAAO,KAAK;CAE1C,MAAM,QAAQ,aACX,UAAU;EAIT,IAAI,OAAO,WAAW,aAAa;EACnC,IAAI,cAAoC;EACxC,IAAI;GACF,cAAc,4BAA4B;IACxC;IACA,oBAAoB;GACtB,CAAC;GACD,gBAAgB,OAAO,WAAW;EACpC,SAAS,OAAO;GACd,QAAQ,MAAM,4BAA4B,YAAY,eAAe,KAAK;EAC5E;CACF,GACA,CAAC,QAAQ,CACX;CAEA,OAAO,eAAe,EAAE,MAAM,IAAI,CAAC,KAAK,CAAC;AAC3C;AAagC,cAAc;CAC5C,WAAW;CACX,aACE;CACF,UAAU;EAAE,MAAM;EAA2B,YAAY;CAAkC;CAC3F,SAAS,EACP,OAAO;EACL,aACE;EACF,SAAS;GACP;IAAE,MAAM;IAAQ,MAAM;GAAS;GAC/B;IAAE,MAAM;IAAgB,MAAM;GAAS;GACvC;IAAE,MAAM;IAAU,MAAM;GAAS;GACjC;IAAE,MAAM;IAAe,MAAM;IAAU,UAAU;GAAK;GACtD;IAAE,MAAM;IAAiB,MAAM;IAAU,UAAU;GAAK;EAC1D;CACF,EACF;CACA,KAAK;AACP,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { defineActions } from "../../core/dist/actions/registry.js";
|
|
3
3
|
import "../../core/dist/index.js";
|
|
4
|
-
import { cartCouponErrorsVar, cartLastAddVar, cartUserErrorsVar, orderFormIdVar } from "../state/cart.js";
|
|
5
4
|
import { CART_COUPON_APPLY, CART_COUPON_REMOVE, CART_ITEMS_REMOVE, CART_ITEM_ADD, CART_ITEM_QUANTITY_DECREASE, CART_ITEM_QUANTITY_INCREASE, CART_ITEM_REMOVE, CART_VALIDATE, CartFragment } from "./operations.js";
|
|
5
|
+
import { cartCouponErrorsVar, cartLastAddVar, cartUserErrorsVar, orderFormIdVar } from "../state/cart.js";
|
|
6
6
|
import { buildOptimisticDecrease, buildOptimisticIncrease, buildOptimisticRemove, buildOptimisticRemoveItems } from "./optimistic.js";
|
|
7
7
|
import { markAvailabilityFresh, trackCartWrite } from "./write-gate.js";
|
|
8
8
|
import { useApolloClient, useMutation } from "@apollo/client/react";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
//#region src/sdk/data/entity-resolution-context.ts
|
|
4
|
+
/**
|
|
5
|
+
* Is the `$data` reaching this subtree the cache's own answer for the entity the page query
|
|
6
|
+
* currently points at, or the server-rendered `fallbackData` standing in for one?
|
|
7
|
+
*
|
|
8
|
+
* `DataOverrideContext` carries the slice and cannot carry this: a fallback slice and a resolved
|
|
9
|
+
* one are the same shape, and an entity whose saved values are genuinely absent looks exactly like
|
|
10
|
+
* an entity whose cache read regressed. Only `useReactiveEntity` knows which of the two it just
|
|
11
|
+
* handed down, so it is the one that publishes here — see its `resolved` return.
|
|
12
|
+
*
|
|
13
|
+
* Default `true`: with no reactive provider above, nothing is outstanding. Storybook, unit tests and
|
|
14
|
+
* any tree handing `$data` over as a plain prop read the default and gate nothing, exactly as they
|
|
15
|
+
* do with `useReactiveDataSettled`.
|
|
16
|
+
*/
|
|
17
|
+
const EntityResolutionContext = createContext(true);
|
|
18
|
+
/**
|
|
19
|
+
* Every entity slice composed into this subtree's `$data` is the cache's own answer.
|
|
20
|
+
*
|
|
21
|
+
* Unlike `useReactiveDataSettled`, this is not latched: it re-arms on its own whenever the entity
|
|
22
|
+
* ref moves or its cache read stops being complete, which is the whole point — a latched page-level
|
|
23
|
+
* signal cannot tell a form that the data under it has gone back to being a placeholder.
|
|
24
|
+
*/
|
|
25
|
+
function useEntityDataResolved() {
|
|
26
|
+
return useContext(EntityResolutionContext);
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { EntityResolutionContext, useEntityDataResolved };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=entity-resolution-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-resolution-context.js","names":[],"sources":["../../../src/sdk/data/entity-resolution-context.ts"],"sourcesContent":["'use client';\n\nimport { createContext, useContext } from 'react';\n\n/**\n * Is the `$data` reaching this subtree the cache's own answer for the entity the page query\n * currently points at, or the server-rendered `fallbackData` standing in for one?\n *\n * `DataOverrideContext` carries the slice and cannot carry this: a fallback slice and a resolved\n * one are the same shape, and an entity whose saved values are genuinely absent looks exactly like\n * an entity whose cache read regressed. Only `useReactiveEntity` knows which of the two it just\n * handed down, so it is the one that publishes here — see its `resolved` return.\n *\n * Default `true`: with no reactive provider above, nothing is outstanding. Storybook, unit tests and\n * any tree handing `$data` over as a plain prop read the default and gate nothing, exactly as they\n * do with `useReactiveDataSettled`.\n */\nexport const EntityResolutionContext = createContext<boolean>(true);\n\n/**\n * Every entity slice composed into this subtree's `$data` is the cache's own answer.\n *\n * Unlike `useReactiveDataSettled`, this is not latched: it re-arms on its own whenever the entity\n * ref moves or its cache read stops being complete, which is the whole point — a latched page-level\n * signal cannot tell a form that the data under it has gone back to being a placeholder.\n */\nexport function useEntityDataResolved(): boolean {\n return useContext(EntityResolutionContext);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAiBA,MAAa,0BAA0B,cAAuB,IAAI;;;;;;;;AASlE,SAAgB,wBAAiC;CAC/C,OAAO,WAAW,uBAAuB;AAC3C"}
|
|
@@ -1,7 +1,8 @@
|
|
|
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
|
-
import { useContext } from "react";
|
|
5
|
+
import { useContext, useMemo } from "react";
|
|
5
6
|
import { jsx } from "react/jsx-runtime";
|
|
6
7
|
//#region src/sdk/data/entity-subscriber.tsx
|
|
7
8
|
/**
|
|
@@ -12,14 +13,23 @@ import { jsx } from "react/jsx-runtime";
|
|
|
12
13
|
*/
|
|
13
14
|
function EntitySubscriber({ entityFragment, fragmentName, rootField, fallbackData, reset, children }) {
|
|
14
15
|
const outer = useContext(DataOverrideContext);
|
|
15
|
-
const
|
|
16
|
-
const
|
|
16
|
+
const outerResolved = useContext(EntityResolutionContext);
|
|
17
|
+
const { data, resolved } = useReactiveEntity(entityFragment, fragmentName, rootField, fallbackData);
|
|
18
|
+
const value = useMemo(() => reset ? { [rootField]: data } : {
|
|
17
19
|
...outer && typeof outer === "object" ? outer : {},
|
|
18
|
-
[rootField]:
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
[rootField]: data
|
|
21
|
+
}, [
|
|
22
|
+
reset,
|
|
23
|
+
rootField,
|
|
24
|
+
data,
|
|
25
|
+
outer
|
|
26
|
+
]);
|
|
27
|
+
return /* @__PURE__ */ jsx(EntityResolutionContext, {
|
|
28
|
+
value: reset ? resolved : outerResolved && resolved,
|
|
29
|
+
children: /* @__PURE__ */ jsx(DataOverrideContext, {
|
|
30
|
+
value,
|
|
31
|
+
children
|
|
32
|
+
})
|
|
23
33
|
});
|
|
24
34
|
}
|
|
25
35
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-subscriber.js","names":[],"sources":["../../../src/sdk/data/entity-subscriber.tsx"],"sourcesContent":["'use client';\n\nimport type { DocumentNode } from '@apollo/client';\nimport type { ReactNode } from 'react';\nimport { useContext } from 'react';\nimport { DataOverrideContext } from './data-override-context.js';\nimport { useReactiveEntity } from './use-reactive-entity.js';\n\nexport type EntitySubscriberProps = 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, and the key\n * under which the resolved slice is merged into the composed `$data` object. */\n rootField: string;\n /** Server-rendered fragment payload; used until the cache resolves a complete entity slice. */\n fallbackData: unknown;\n /**\n * When `true`, RESETS the context to `{ [rootField]: resolved }`, discarding any outer\n * `DataOverrideContext` value — this is the outermost subscriber of a component's nested stack,\n * and prevents a parent component's `$data` from leaking into this component's descendants. When\n * `false`, merges this subscriber's slice into the SAME-stack outer value instead.\n */\n reset: boolean;\n children: ReactNode;\n}>;\n\n/**\n * Per-entity subscriber used internally by `MultipleReactiveDataProvider` to compose several\n * entities' cache slices into one keyed `$data` object for a single component. Resolves its own\n * slice exactly like `ReactiveDataProvider` (see `useReactiveEntity`), then merges it under\n * `rootField` into the outer `DataOverrideContext` value.\n */\nexport function EntitySubscriber({\n entityFragment,\n fragmentName,\n rootField,\n fallbackData,\n reset,\n children,\n}: EntitySubscriberProps): ReactNode {\n const outer = useContext(DataOverrideContext);\n const resolved = useReactiveEntity(entityFragment, fragmentName, rootField, fallbackData);\n\n const value =
|
|
1
|
+
{"version":3,"file":"entity-subscriber.js","names":[],"sources":["../../../src/sdk/data/entity-subscriber.tsx"],"sourcesContent":["'use client';\n\nimport type { DocumentNode } from '@apollo/client';\nimport type { ReactNode } from 'react';\nimport { useContext, useMemo } 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 EntitySubscriberProps = 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, and the key\n * under which the resolved slice is merged into the composed `$data` object. */\n rootField: string;\n /** Server-rendered fragment payload; used until the cache resolves a complete entity slice. */\n fallbackData: unknown;\n /**\n * When `true`, RESETS the context to `{ [rootField]: resolved }`, discarding any outer\n * `DataOverrideContext` value — this is the outermost subscriber of a component's nested stack,\n * and prevents a parent component's `$data` from leaking into this component's descendants. When\n * `false`, merges this subscriber's slice into the SAME-stack outer value instead.\n */\n reset: boolean;\n children: ReactNode;\n}>;\n\n/**\n * Per-entity subscriber used internally by `MultipleReactiveDataProvider` to compose several\n * entities' cache slices into one keyed `$data` object for a single component. Resolves its own\n * slice exactly like `ReactiveDataProvider` (see `useReactiveEntity`), then merges it under\n * `rootField` into the outer `DataOverrideContext` value.\n */\nexport function EntitySubscriber({\n entityFragment,\n fragmentName,\n rootField,\n fallbackData,\n reset,\n children,\n}: EntitySubscriberProps): ReactNode {\n const outer = useContext(DataOverrideContext);\n const outerResolved = useContext(EntityResolutionContext);\n const { data, resolved } = useReactiveEntity(entityFragment, fragmentName, rootField, fallbackData);\n\n // Memoised because this object is the context value: a fresh identity on every render makes every\n // consumer below re-render, and in a form that is every keystroke. Its two sibling providers pass a\n // stable reference already.\n const value = useMemo(\n () => (reset ? { [rootField]: data } : { ...(outer && typeof outer === 'object' ? outer : {}), [rootField]: data }),\n [reset, rootField, data, outer],\n );\n\n // Mirrors the data merge exactly: the composed `$data` is resolved only when every slice merged\n // into it is. `reset` discards the outer verdict for the same reason it discards the outer value —\n // that verdict belongs to an ancestor component's own entities, not to this component's.\n const resolution = reset ? resolved : outerResolved && resolved;\n\n return (\n <EntityResolutionContext value={resolution}>\n <DataOverrideContext value={value}>{children}</DataOverrideContext>\n </EntityResolutionContext>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAmCA,SAAgB,iBAAiB,EAC/B,gBACA,cACA,WACA,cACA,OACA,YACmC;CACnC,MAAM,QAAQ,WAAW,mBAAmB;CAC5C,MAAM,gBAAgB,WAAW,uBAAuB;CACxD,MAAM,EAAE,MAAM,aAAa,kBAAkB,gBAAgB,cAAc,WAAW,YAAY;CAKlG,MAAM,QAAQ,cACL,QAAQ,GAAG,YAAY,KAAK,IAAI;EAAE,GAAI,SAAS,OAAO,UAAU,WAAW,QAAQ,CAAC;GAAK,YAAY;CAAK,GACjH;EAAC;EAAO;EAAW;EAAM;CAAK,CAChC;CAOA,OACE,oBAAC,yBAAD;EAAyB,OAHR,QAAQ,WAAW,iBAAiB;EAInD,UAAA,oBAAC,qBAAD;GAA4B;GAAQ;EAA8B,CAAA;CAC3C,CAAA;AAE7B"}
|
package/dist/sdk/data/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { DataOverrideContext, useDataOverride } from "./data-override-context.js
|
|
|
2
2
|
import { HotDataProvider } from "./hot-data-provider.js";
|
|
3
3
|
import { ListItemRefContext, useListItemRef } from "./list-item-ref-context.js";
|
|
4
4
|
import { PageReactiveDataRoot, ReactiveRootContext } from "./page-reactive-data-root.js";
|
|
5
|
+
import "./entity-resolution-context.js";
|
|
5
6
|
import { MultipleReactiveDataProvider } from "./multiple-reactive-data-provider.js";
|
|
6
7
|
import { ReactiveDataProvider } from "./reactive-data-provider.js";
|
|
7
8
|
import { ReactiveListItemDataProvider } from "./reactive-list-item-data-provider.js";
|