@sixtyninecommerce/client 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/admin/forms.js +19 -0
- package/dist/admin/forms.js.map +1 -1
- package/dist/admin/http.d.ts +9 -0
- package/dist/admin/http.d.ts.map +1 -1
- package/dist/admin/http.js +3 -0
- package/dist/admin/http.js.map +1 -1
- package/dist/admin/index.d.ts +63 -0
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +67 -0
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/redirects.d.ts +6 -0
- package/dist/admin/redirects.d.ts.map +1 -1
- package/dist/admin/redirects.js +12 -0
- package/dist/admin/redirects.js.map +1 -1
- package/dist/admin/types.d.ts +63 -0
- package/dist/admin/types.d.ts.map +1 -1
- package/dist/cache-tags.d.ts +19 -0
- package/dist/cache-tags.d.ts.map +1 -1
- package/dist/cache-tags.js +19 -0
- package/dist/cache-tags.js.map +1 -1
- package/dist/client.d.ts +17 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +17 -0
- package/dist/client.js.map +1 -1
- package/dist/commerce/age-verification.js +3 -3
- package/dist/commerce/age-verification.js.map +1 -1
- package/dist/commerce/branding.js +7 -0
- package/dist/commerce/branding.js.map +1 -1
- package/dist/commerce/cart.js +25 -0
- package/dist/commerce/cart.js.map +1 -1
- package/dist/commerce/checkout.d.ts +68 -0
- package/dist/commerce/checkout.d.ts.map +1 -1
- package/dist/commerce/checkout.js +83 -0
- package/dist/commerce/checkout.js.map +1 -1
- package/dist/commerce/customer.d.ts +58 -0
- package/dist/commerce/customer.d.ts.map +1 -1
- package/dist/commerce/customer.js +65 -0
- package/dist/commerce/customer.js.map +1 -1
- package/dist/commerce/pickup-location-parser.d.ts +18 -0
- package/dist/commerce/pickup-location-parser.d.ts.map +1 -1
- package/dist/commerce/pickup-location-parser.js +28 -0
- package/dist/commerce/pickup-location-parser.js.map +1 -1
- package/dist/commerce/placement-error.d.ts +52 -0
- package/dist/commerce/placement-error.d.ts.map +1 -1
- package/dist/commerce/placement-error.js +87 -0
- package/dist/commerce/placement-error.js.map +1 -1
- package/dist/commerce/products.d.ts +48 -0
- package/dist/commerce/products.d.ts.map +1 -1
- package/dist/commerce/products.js +92 -0
- package/dist/commerce/products.js.map +1 -1
- package/dist/commerce/queries.d.ts +113 -27
- package/dist/commerce/queries.d.ts.map +1 -1
- package/dist/commerce/queries.js +188 -2
- package/dist/commerce/queries.js.map +1 -1
- package/dist/commerce/quotes.js +33 -0
- package/dist/commerce/quotes.js.map +1 -1
- package/dist/commerce/saved-lists.d.ts +7 -7
- package/dist/commerce/saved-lists.d.ts.map +1 -1
- package/dist/commerce/saved-lists.js +1 -1
- package/dist/commerce/search.js +3 -0
- package/dist/commerce/search.js.map +1 -1
- package/dist/commerce/shipping.js +3 -3
- package/dist/commerce/shipping.js.map +1 -1
- package/dist/commerce/stock-notifications.d.ts +11 -0
- package/dist/commerce/stock-notifications.d.ts.map +1 -1
- package/dist/commerce/stock-notifications.js +11 -0
- package/dist/commerce/stock-notifications.js.map +1 -1
- package/dist/commerce/vat-validation.js +4 -4
- package/dist/commerce/vat-validation.js.map +1 -1
- package/dist/constants.d.ts +31 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +32 -0
- package/dist/constants.js.map +1 -1
- package/dist/content/asset-urls.d.ts +50 -0
- package/dist/content/asset-urls.d.ts.map +1 -1
- package/dist/content/asset-urls.js +92 -0
- package/dist/content/asset-urls.js.map +1 -1
- package/dist/content/assets.d.ts +15 -0
- package/dist/content/assets.d.ts.map +1 -1
- package/dist/content/assets.js +15 -0
- package/dist/content/assets.js.map +1 -1
- package/dist/content/collection-pages.d.ts +5 -0
- package/dist/content/collection-pages.d.ts.map +1 -1
- package/dist/content/collection-pages.js +8 -0
- package/dist/content/collection-pages.js.map +1 -1
- package/dist/content/collections.d.ts +14 -0
- package/dist/content/collections.d.ts.map +1 -1
- package/dist/content/collections.js +16 -0
- package/dist/content/collections.js.map +1 -1
- package/dist/content/filtered-products.d.ts +18 -0
- package/dist/content/filtered-products.d.ts.map +1 -1
- package/dist/content/filtered-products.js +16 -0
- package/dist/content/filtered-products.js.map +1 -1
- package/dist/content/form-field-types.d.ts +6 -0
- package/dist/content/form-field-types.d.ts.map +1 -1
- package/dist/content/form-field-types.js +12 -0
- package/dist/content/form-field-types.js.map +1 -1
- package/dist/content/forms.d.ts +16 -0
- package/dist/content/forms.d.ts.map +1 -1
- package/dist/content/forms.js +23 -0
- package/dist/content/forms.js.map +1 -1
- package/dist/content/items.d.ts +26 -0
- package/dist/content/items.d.ts.map +1 -1
- package/dist/content/items.js +33 -0
- package/dist/content/items.js.map +1 -1
- package/dist/content/resolve.js +6 -0
- package/dist/content/resolve.js.map +1 -1
- package/dist/content/sitemap.d.ts +5 -0
- package/dist/content/sitemap.d.ts.map +1 -1
- package/dist/content/sitemap.js +5 -0
- package/dist/content/sitemap.js.map +1 -1
- package/dist/core/context.js +6 -0
- package/dist/core/context.js.map +1 -1
- package/dist/core/headers.js +2 -0
- package/dist/core/headers.js.map +1 -1
- package/dist/core/http.d.ts +8 -0
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/http.js +89 -1
- package/dist/core/http.js.map +1 -1
- package/dist/credit/credit-status.js +2 -2
- package/dist/credit/credit-status.js.map +1 -1
- package/dist/credit/payment-terms.js +2 -2
- package/dist/credit/payment-terms.js.map +1 -1
- package/dist/currency/exchange.d.ts +11 -0
- package/dist/currency/exchange.d.ts.map +1 -1
- package/dist/currency/exchange.js +11 -0
- package/dist/currency/exchange.js.map +1 -1
- package/dist/errors.d.ts +50 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +48 -2
- package/dist/errors.js.map +1 -1
- package/dist/events/envelope.d.ts +60 -0
- package/dist/events/envelope.d.ts.map +1 -1
- package/dist/events/envelope.js +12 -0
- package/dist/events/envelope.js.map +1 -1
- package/dist/events/index.d.ts +14 -1
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +13 -0
- package/dist/events/index.js.map +1 -1
- package/dist/events/payloads.d.ts +19 -0
- package/dist/events/payloads.d.ts.map +1 -1
- package/dist/events/payloads.js +12 -0
- package/dist/events/payloads.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/media/index.js +2 -0
- package/dist/media/index.js.map +1 -1
- package/dist/media/urls.d.ts +52 -0
- package/dist/media/urls.d.ts.map +1 -1
- package/dist/media/urls.js +38 -0
- package/dist/media/urls.js.map +1 -1
- package/dist/media/video.d.ts +16 -0
- package/dist/media/video.d.ts.map +1 -1
- package/dist/media/video.js +16 -0
- package/dist/media/video.js.map +1 -1
- package/dist/next/artifact-handler.d.ts +22 -0
- package/dist/next/artifact-handler.d.ts.map +1 -1
- package/dist/next/artifact-handler.js +22 -0
- package/dist/next/artifact-handler.js.map +1 -1
- package/dist/next/event-revalidate-handler.d.ts +67 -0
- package/dist/next/event-revalidate-handler.d.ts.map +1 -1
- package/dist/next/event-revalidate-handler.js +48 -0
- package/dist/next/event-revalidate-handler.js.map +1 -1
- package/dist/next/index.d.ts +6 -0
- package/dist/next/index.d.ts.map +1 -1
- package/dist/next/index.js +6 -0
- package/dist/next/index.js.map +1 -1
- package/dist/next/json-ld.d.ts +9 -0
- package/dist/next/json-ld.d.ts.map +1 -1
- package/dist/next/json-ld.js +9 -0
- package/dist/next/json-ld.js.map +1 -1
- package/dist/next/metadata.d.ts +18 -0
- package/dist/next/metadata.d.ts.map +1 -1
- package/dist/next/metadata.js +21 -0
- package/dist/next/metadata.js.map +1 -1
- package/dist/next/redirect-middleware.d.ts +39 -0
- package/dist/next/redirect-middleware.d.ts.map +1 -1
- package/dist/next/redirect-middleware.js +32 -0
- package/dist/next/redirect-middleware.js.map +1 -1
- package/dist/next/revalidate-handler.d.ts +30 -0
- package/dist/next/revalidate-handler.d.ts.map +1 -1
- package/dist/next/revalidate-handler.js +22 -0
- package/dist/next/revalidate-handler.js.map +1 -1
- package/dist/order-scope.d.ts +27 -0
- package/dist/order-scope.d.ts.map +1 -1
- package/dist/order-scope.js +29 -0
- package/dist/order-scope.js.map +1 -1
- package/dist/pricing/format.d.ts +17 -0
- package/dist/pricing/format.d.ts.map +1 -1
- package/dist/pricing/format.js +19 -0
- package/dist/pricing/format.js.map +1 -1
- package/dist/pricing/index.d.ts +8 -1
- package/dist/pricing/index.d.ts.map +1 -1
- package/dist/pricing/index.js +18 -2
- package/dist/pricing/index.js.map +1 -1
- package/dist/pricing/tax-display.d.ts +77 -0
- package/dist/pricing/tax-display.d.ts.map +1 -1
- package/dist/pricing/tax-display.js +85 -0
- package/dist/pricing/tax-display.js.map +1 -1
- package/dist/pricing/tiers.d.ts +9 -0
- package/dist/pricing/tiers.d.ts.map +1 -1
- package/dist/pricing/tiers.js +9 -0
- package/dist/pricing/tiers.js.map +1 -1
- package/dist/pricing/validation.d.ts +56 -1
- package/dist/pricing/validation.d.ts.map +1 -1
- package/dist/pricing/validation.js +61 -0
- package/dist/pricing/validation.js.map +1 -1
- package/dist/redirects/index.d.ts +1 -0
- package/dist/redirects/index.d.ts.map +1 -1
- package/dist/redirects/index.js +1 -0
- package/dist/redirects/index.js.map +1 -1
- package/dist/redirects/store.d.ts +20 -0
- package/dist/redirects/store.d.ts.map +1 -1
- package/dist/redirects/store.js +57 -0
- package/dist/redirects/store.js.map +1 -1
- package/dist/redirects/types.d.ts +24 -0
- package/dist/redirects/types.d.ts.map +1 -1
- package/dist/redirects/types.js +1 -0
- package/dist/redirects/types.js.map +1 -1
- package/dist/revalidation/event-plan.d.ts +48 -0
- package/dist/revalidation/event-plan.d.ts.map +1 -1
- package/dist/revalidation/event-plan.js +61 -0
- package/dist/revalidation/event-plan.js.map +1 -1
- package/dist/revalidation/event-verify.d.ts +25 -0
- package/dist/revalidation/event-verify.d.ts.map +1 -1
- package/dist/revalidation/event-verify.js +27 -0
- package/dist/revalidation/event-verify.js.map +1 -1
- package/dist/revalidation/types.d.ts +20 -0
- package/dist/revalidation/types.d.ts.map +1 -1
- package/dist/revalidation/types.js +10 -0
- package/dist/revalidation/types.js.map +1 -1
- package/dist/revalidation/url-context.d.ts +25 -0
- package/dist/revalidation/url-context.d.ts.map +1 -1
- package/dist/revalidation/url-context.js +17 -0
- package/dist/revalidation/url-context.js.map +1 -1
- package/dist/revalidation/verify.d.ts +8 -0
- package/dist/revalidation/verify.d.ts.map +1 -1
- package/dist/revalidation/verify.js +8 -0
- package/dist/revalidation/verify.js.map +1 -1
- package/dist/seo/ai.d.ts +17 -0
- package/dist/seo/ai.d.ts.map +1 -1
- package/dist/seo/ai.js +12 -0
- package/dist/seo/ai.js.map +1 -1
- package/dist/seo/alternates.js +13 -0
- package/dist/seo/alternates.js.map +1 -1
- package/dist/seo/artifacts.d.ts +13 -0
- package/dist/seo/artifacts.d.ts.map +1 -1
- package/dist/seo/artifacts.js +19 -0
- package/dist/seo/artifacts.js.map +1 -1
- package/dist/seo/canonical.d.ts +15 -0
- package/dist/seo/canonical.d.ts.map +1 -1
- package/dist/seo/canonical.js +24 -0
- package/dist/seo/canonical.js.map +1 -1
- package/dist/seo/index.js +6 -0
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/json-ld.d.ts +13 -0
- package/dist/seo/json-ld.d.ts.map +1 -1
- package/dist/seo/json-ld.js +32 -0
- package/dist/seo/json-ld.js.map +1 -1
- package/dist/seo/locale-tag.d.ts +31 -0
- package/dist/seo/locale-tag.d.ts.map +1 -1
- package/dist/seo/locale-tag.js +31 -0
- package/dist/seo/locale-tag.js.map +1 -1
- package/dist/seo/meta-tags.d.ts +29 -0
- package/dist/seo/meta-tags.d.ts.map +1 -1
- package/dist/seo/meta-tags.js +28 -0
- package/dist/seo/meta-tags.js.map +1 -1
- package/dist/seo/metadata-parser.d.ts +24 -0
- package/dist/seo/metadata-parser.d.ts.map +1 -1
- package/dist/seo/metadata-parser.js +38 -0
- package/dist/seo/metadata-parser.js.map +1 -1
- package/dist/seo/metadata-types.d.ts +16 -0
- package/dist/seo/metadata-types.d.ts.map +1 -1
- package/dist/seo/product-path.d.ts +96 -0
- package/dist/seo/product-path.d.ts.map +1 -1
- package/dist/seo/product-path.js +96 -0
- package/dist/seo/product-path.js.map +1 -1
- package/dist/seo/robots.d.ts +17 -0
- package/dist/seo/robots.d.ts.map +1 -1
- package/dist/seo/robots.js +12 -0
- package/dist/seo/robots.js.map +1 -1
- package/dist/seo/types.d.ts +39 -0
- package/dist/seo/types.d.ts.map +1 -1
- package/dist/seo/url-builder.d.ts +31 -0
- package/dist/seo/url-builder.d.ts.map +1 -1
- package/dist/seo/url-builder.js +36 -0
- package/dist/seo/url-builder.js.map +1 -1
- package/dist/types.d.ts +1163 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware factory that applies the tenant's platform-managed redirects.
|
|
3
|
+
*
|
|
4
|
+
* Zero `next` imports by design: it takes a `Request` and returns a plain
|
|
5
|
+
* `Response`, which is exactly what a Next.js middleware may return. It never
|
|
6
|
+
* throws — an unmatched path, a cold cache or an unreachable platform all
|
|
7
|
+
* resolve to `undefined`, meaning "carry on with normal routing".
|
|
8
|
+
*/
|
|
1
9
|
import { createRedirectStore } from '../redirects/store.js';
|
|
10
|
+
/**
|
|
11
|
+
* Build the destination URL against the request origin, so a stored path
|
|
12
|
+
* redirects on the storefront's own host and an absolute `https://` target
|
|
13
|
+
* leaves it.
|
|
14
|
+
*/
|
|
2
15
|
function resolveTarget(to, request, preserveQuery) {
|
|
3
16
|
const requestUrl = new URL(request.url);
|
|
4
17
|
const target = new URL(to, requestUrl.origin);
|
|
@@ -11,6 +24,25 @@ function resolveTarget(to, request, preserveQuery) {
|
|
|
11
24
|
}
|
|
12
25
|
return target.toString();
|
|
13
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Usage (App Router):
|
|
29
|
+
* ```ts
|
|
30
|
+
* // src/middleware.ts
|
|
31
|
+
* import { createRedirectMiddleware } from '@sixtyninecommerce/client/next';
|
|
32
|
+
*
|
|
33
|
+
* const redirects = createRedirectMiddleware({
|
|
34
|
+
* storeCode: process.env.STORE_CODE!,
|
|
35
|
+
* pluginsUrl: process.env.PLUGINS_URL,
|
|
36
|
+
* onError: (error) => console.error('[redirects]', error),
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* export async function middleware(request: Request) {
|
|
40
|
+
* return (await redirects(request)) ?? undefined;
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* export const config = { matcher: ['/((?!_next|api|.*\\..*).*)'] };
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
14
46
|
export function createRedirectMiddleware(options) {
|
|
15
47
|
const preserveQuery = options.preserveQuery ?? true;
|
|
16
48
|
const store = 'store' in options ? options.store : createRedirectStore(options);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redirect-middleware.js","sourceRoot":"","sources":["../../src/next/redirect-middleware.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"redirect-middleware.js","sourceRoot":"","sources":["../../src/next/redirect-middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAwB5D;;;;GAIG;AACH,SAAS,aAAa,CAAC,EAAU,EAAE,OAAgB,EAAE,aAAsB;IACzE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAE9C,IAAI,aAAa,EAAE,CAAC;QAClB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,UAAU,CAAC,YAAY,EAAE,CAAC;YACnD,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAAsF;IAEtF,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC;IACpD,MAAM,KAAK,GACT,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAA8B,CAAC,CAAC;IAE3F,MAAM,UAAU,GAAG,KAAK,EAAE,OAAgB,EAAiC,EAAE;QAC3E,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAE7B,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;YACxB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,OAAO,EAAE,EAAE,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE;SACvE,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,CAAuB,CAAC;AACpE,CAAC"}
|
|
@@ -1,10 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route-handler factory for the storefront's /api/revalidate endpoint.
|
|
3
|
+
* Framework functions (`revalidatePath`/`revalidateTag` from `next/cache`)
|
|
4
|
+
* are INJECTED so this package never imports `next`; the handler itself uses
|
|
5
|
+
* only web-standard Request/Response and is unit-testable anywhere.
|
|
6
|
+
*
|
|
7
|
+
* Requires ISR-capable hosting (the default Next server build) — under
|
|
8
|
+
* `output: 'export'` there is nothing to revalidate at request time.
|
|
9
|
+
*
|
|
10
|
+
* It speaks only the legacy SEO revalidation webhook, which the platform no
|
|
11
|
+
* longer sends (its sender is retired) — a route mounted with it receives
|
|
12
|
+
* nothing. A platform event envelope is refused here with a 400 (no `paths`),
|
|
13
|
+
* so point an event subscription at `createEventRevalidateHandler` instead,
|
|
14
|
+
* on its own route.
|
|
15
|
+
*/
|
|
1
16
|
import type { RevalidateWebhookPayload } from '../revalidation/types.js';
|
|
17
|
+
/**
|
|
18
|
+
* @deprecated Options of the legacy SEO webhook handler — use
|
|
19
|
+
* `EventRevalidateHandlerOptions` with `createEventRevalidateHandler`.
|
|
20
|
+
*/
|
|
2
21
|
export interface RevalidateHandlerOptions {
|
|
22
|
+
/** The shared secret the legacy webhook was signed with. Server-only env. */
|
|
3
23
|
secret: string;
|
|
24
|
+
/** Pass `revalidatePath` from 'next/cache'. */
|
|
4
25
|
revalidatePath(path: string, type?: 'page' | 'layout'): void;
|
|
26
|
+
/** Pass `revalidateTag` from 'next/cache' (optional — tags are skipped without it). */
|
|
5
27
|
revalidateTag?(tag: string): void;
|
|
28
|
+
/** Reject deliveries whose occurred_at is older/newer than this. Default 300s. */
|
|
6
29
|
toleranceSeconds?: number;
|
|
30
|
+
/** Observe accepted deliveries (logging, metrics). Errors are swallowed. */
|
|
7
31
|
onEvent?(payload: RevalidateWebhookPayload): void | Promise<void>;
|
|
8
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* @deprecated The legacy SEO revalidation webhook, which the platform no longer
|
|
35
|
+
* sends. Kept, unchanged, so existing `/api/revalidate` routes still compile.
|
|
36
|
+
* Storefronts subscribe to platform events and mount
|
|
37
|
+
* `createEventRevalidateHandler` at `/api/revalidate/events`.
|
|
38
|
+
*/
|
|
9
39
|
export declare function createRevalidateHandler(options: RevalidateHandlerOptions): (request: Request) => Promise<Response>;
|
|
10
40
|
//# sourceMappingURL=revalidate-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"revalidate-handler.d.ts","sourceRoot":"","sources":["../../src/next/revalidate-handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"revalidate-handler.d.ts","sourceRoot":"","sources":["../../src/next/revalidate-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAKzE;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC7D,uFAAuF;IACvF,aAAa,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,OAAO,CAAC,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnE;AASD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,wBAAwB,GAChC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAiEzC"}
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route-handler factory for the storefront's /api/revalidate endpoint.
|
|
3
|
+
* Framework functions (`revalidatePath`/`revalidateTag` from `next/cache`)
|
|
4
|
+
* are INJECTED so this package never imports `next`; the handler itself uses
|
|
5
|
+
* only web-standard Request/Response and is unit-testable anywhere.
|
|
6
|
+
*
|
|
7
|
+
* Requires ISR-capable hosting (the default Next server build) — under
|
|
8
|
+
* `output: 'export'` there is nothing to revalidate at request time.
|
|
9
|
+
*
|
|
10
|
+
* It speaks only the legacy SEO revalidation webhook, which the platform no
|
|
11
|
+
* longer sends (its sender is retired) — a route mounted with it receives
|
|
12
|
+
* nothing. A platform event envelope is refused here with a 400 (no `paths`),
|
|
13
|
+
* so point an event subscription at `createEventRevalidateHandler` instead,
|
|
14
|
+
* on its own route.
|
|
15
|
+
*/
|
|
1
16
|
import { verifyWebhookSignature } from '../revalidation/verify.js';
|
|
2
17
|
const DEFAULT_TOLERANCE_SECONDS = 300;
|
|
3
18
|
function json(status, body) {
|
|
@@ -6,6 +21,12 @@ function json(status, body) {
|
|
|
6
21
|
headers: { 'Content-Type': 'application/json' },
|
|
7
22
|
});
|
|
8
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* @deprecated The legacy SEO revalidation webhook, which the platform no longer
|
|
26
|
+
* sends. Kept, unchanged, so existing `/api/revalidate` routes still compile.
|
|
27
|
+
* Storefronts subscribe to platform events and mount
|
|
28
|
+
* `createEventRevalidateHandler` at `/api/revalidate/events`.
|
|
29
|
+
*/
|
|
9
30
|
export function createRevalidateHandler(options) {
|
|
10
31
|
const tolerance = (options.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS) * 1000;
|
|
11
32
|
return async (request) => {
|
|
@@ -56,6 +77,7 @@ export function createRevalidateHandler(options) {
|
|
|
56
77
|
await options.onEvent(payload);
|
|
57
78
|
}
|
|
58
79
|
catch {
|
|
80
|
+
// observer errors must not fail the delivery
|
|
59
81
|
}
|
|
60
82
|
}
|
|
61
83
|
return json(200, { revalidated: true, paths, tags });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"revalidate-handler.js","sourceRoot":"","sources":["../../src/next/revalidate-handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"revalidate-handler.js","sourceRoot":"","sources":["../../src/next/revalidate-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAmBtC,SAAS,IAAI,CAAC,MAAc,EAAE,IAAY;IACxC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;QACxC,MAAM;QACN,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;KAChD,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,OAAiC;IAEjC,MAAM,SAAS,GAAG,CAAC,OAAO,CAAC,gBAAgB,IAAI,yBAAyB,CAAC,GAAG,IAAI,CAAC;IAEjF,OAAO,KAAK,EAAE,OAAgB,EAAqB,EAAE;QACnD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;YACrC,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;YAE7D,IAAI,CAAC,CAAC,MAAM,sBAAsB,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;gBACxE,OAAO,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;YACnD,CAAC;YAED,IAAI,OAAiC,CAAC;YACtC,IAAI,CAAC;gBACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAA6B,CAAC;YAC5D,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;YACnD,CAAC;YACD,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,CAAC,WAAW,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzF,OAAO,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;YACvD,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YACnD,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,GAAG,SAAS,EAAE,CAAC;gBAC9E,OAAO,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,mCAAmC,EAAE,CAAC,CAAC;YACnE,CAAC;YAED,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;gBAC5B,OAAO,CAAC,cAAc,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;gBACtC,KAAK,GAAG,CAAC,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACN,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;oBACjC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;wBACrD,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;wBAC7B,KAAK,EAAE,CAAC;oBACV,CAAC;gBACH,CAAC;YACH,CAAC;YAED,IAAI,IAAI,GAAG,CAAC,CAAC;YACb,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;gBAC1B,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;oBACrC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAC9C,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;wBAC3B,IAAI,EAAE,CAAC;oBACT,CAAC;gBACH,CAAC;YACH,CAAC;YAED,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBACpB,IAAI,CAAC;oBACH,MAAM,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBACjC,CAAC;gBAAC,MAAM,CAAC;oBACP,6CAA6C;gBAC/C,CAAC;YACH,CAAC;YAED,OAAO,IAAI,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,IAAI,CAAC,GAAG,EAAE;gBACf,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,qBAAqB;aACtE,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/order-scope.d.ts
CHANGED
|
@@ -1,3 +1,30 @@
|
|
|
1
1
|
import type { OrderScope } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Does an order belong to `scope`?
|
|
4
|
+
*
|
|
5
|
+
* THE CASE DIFFERS ON PURPOSE, and this helper is the one place that has to
|
|
6
|
+
* know. The wire and storage value is lower-case (`'consumer'` / `'trade'`) —
|
|
7
|
+
* it is a Vendure `string` custom field with a `varchar(16)` column and a
|
|
8
|
+
* `'consumer'` default — while the client-side {@link OrderScope} union and the
|
|
9
|
+
* `OrderScope` GraphQL enum are upper-case, because that is what a GraphQL enum
|
|
10
|
+
* looks like. So the obvious comparison,
|
|
11
|
+
*
|
|
12
|
+
* order.customFields.orderScope === client.getOrderScope() // never true
|
|
13
|
+
*
|
|
14
|
+
* silently never matches, and a storefront filtering its own order list on it
|
|
15
|
+
* shows an empty page with no error anywhere.
|
|
16
|
+
*
|
|
17
|
+
* Two rules, both mirroring the engine (`scopeOf` in
|
|
18
|
+
* `services/vendure/src/order-scope/order-scope.ts`):
|
|
19
|
+
*
|
|
20
|
+
* - the comparison is case-insensitive;
|
|
21
|
+
* - an ABSENT, null or unrecognised value reads as `CONSUMER`. Every order
|
|
22
|
+
* placed before the scope existed has no value, and the engine treats those
|
|
23
|
+
* as consumer orders — so must a storefront, or a consumer shop's history
|
|
24
|
+
* page loses everything the customer bought before the feature shipped.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const mine = orders.filter((o) => orderScopeMatches(o.customFields?.orderScope, 'TRADE'));
|
|
28
|
+
*/
|
|
2
29
|
export declare function orderScopeMatches(customFieldValue: string | null | undefined, scope: OrderScope | string): boolean;
|
|
3
30
|
//# sourceMappingURL=order-scope.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"order-scope.d.ts","sourceRoot":"","sources":["../src/order-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"order-scope.d.ts","sourceRoot":"","sources":["../src/order-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,CAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC3C,KAAK,EAAE,UAAU,GAAG,MAAM,GACzB,OAAO,CAOT"}
|
package/dist/order-scope.js
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does an order belong to `scope`?
|
|
3
|
+
*
|
|
4
|
+
* THE CASE DIFFERS ON PURPOSE, and this helper is the one place that has to
|
|
5
|
+
* know. The wire and storage value is lower-case (`'consumer'` / `'trade'`) —
|
|
6
|
+
* it is a Vendure `string` custom field with a `varchar(16)` column and a
|
|
7
|
+
* `'consumer'` default — while the client-side {@link OrderScope} union and the
|
|
8
|
+
* `OrderScope` GraphQL enum are upper-case, because that is what a GraphQL enum
|
|
9
|
+
* looks like. So the obvious comparison,
|
|
10
|
+
*
|
|
11
|
+
* order.customFields.orderScope === client.getOrderScope() // never true
|
|
12
|
+
*
|
|
13
|
+
* silently never matches, and a storefront filtering its own order list on it
|
|
14
|
+
* shows an empty page with no error anywhere.
|
|
15
|
+
*
|
|
16
|
+
* Two rules, both mirroring the engine (`scopeOf` in
|
|
17
|
+
* `services/vendure/src/order-scope/order-scope.ts`):
|
|
18
|
+
*
|
|
19
|
+
* - the comparison is case-insensitive;
|
|
20
|
+
* - an ABSENT, null or unrecognised value reads as `CONSUMER`. Every order
|
|
21
|
+
* placed before the scope existed has no value, and the engine treats those
|
|
22
|
+
* as consumer orders — so must a storefront, or a consumer shop's history
|
|
23
|
+
* page loses everything the customer bought before the feature shipped.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const mine = orders.filter((o) => orderScopeMatches(o.customFields?.orderScope, 'TRADE'));
|
|
27
|
+
*/
|
|
1
28
|
export function orderScopeMatches(customFieldValue, scope) {
|
|
2
29
|
const normalized = typeof customFieldValue === 'string' ? customFieldValue.trim().toUpperCase() : '';
|
|
3
30
|
const effective = normalized === 'CONSUMER' || normalized === 'TRADE' ? normalized : 'CONSUMER';
|
|
31
|
+
// Both sides are normalised: a JS caller passing 'trade' must match — the helper
|
|
32
|
+
// exists to absorb exactly this casing split, on either argument.
|
|
4
33
|
return effective === String(scope).trim().toUpperCase();
|
|
5
34
|
}
|
|
6
35
|
//# sourceMappingURL=order-scope.js.map
|
package/dist/order-scope.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"order-scope.js","sourceRoot":"","sources":["../src/order-scope.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"order-scope.js","sourceRoot":"","sources":["../src/order-scope.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,iBAAiB,CAC/B,gBAA2C,EAC3C,KAA0B;IAE1B,MAAM,UAAU,GACd,OAAO,gBAAgB,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,MAAM,SAAS,GAAG,UAAU,KAAK,UAAU,IAAI,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC;IAChG,iFAAiF;IACjF,kEAAkE;IAClE,OAAO,SAAS,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAC1D,CAAC"}
|
package/dist/pricing/format.d.ts
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format a price in minor units to a human-readable string.
|
|
3
|
+
* Uses Intl.NumberFormat for locale-aware formatting.
|
|
4
|
+
*
|
|
5
|
+
* The platform stores money in a currency's minor units, so the divisor is
|
|
6
|
+
* `10 ** fractionDigits`, NOT a hardcoded 100: EUR/USD have 2 digits (÷100),
|
|
7
|
+
* JPY 0 (÷1 — `2999` is ¥2,999, not ¥30), BHD 3 (÷1000). The digit count comes
|
|
8
|
+
* from the same Intl formatter, so it always matches how the value is rendered.
|
|
9
|
+
*
|
|
10
|
+
* Vendure `LanguageCode` region variants use underscores (`pt_BR`, `zh_Hans`),
|
|
11
|
+
* which are not valid BCP-47 separators and make `Intl.NumberFormat` throw —
|
|
12
|
+
* normalize to hyphens so a channel default language can be passed straight in.
|
|
13
|
+
*/
|
|
1
14
|
export declare function formatPrice(amount: number, currencyCode: string, locale?: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Format a price range from two minor-unit amounts.
|
|
17
|
+
* Returns "€10.00 - €20.00" or the single price if min === max.
|
|
18
|
+
*/
|
|
2
19
|
export declare function formatPriceRange(min: number, max: number, currencyCode: string, locale?: string): string;
|
|
3
20
|
//# sourceMappingURL=format.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/pricing/format.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/pricing/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CASzF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAKR"}
|
package/dist/pricing/format.js
CHANGED
|
@@ -1,11 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format a price in minor units to a human-readable string.
|
|
3
|
+
* Uses Intl.NumberFormat for locale-aware formatting.
|
|
4
|
+
*
|
|
5
|
+
* The platform stores money in a currency's minor units, so the divisor is
|
|
6
|
+
* `10 ** fractionDigits`, NOT a hardcoded 100: EUR/USD have 2 digits (÷100),
|
|
7
|
+
* JPY 0 (÷1 — `2999` is ¥2,999, not ¥30), BHD 3 (÷1000). The digit count comes
|
|
8
|
+
* from the same Intl formatter, so it always matches how the value is rendered.
|
|
9
|
+
*
|
|
10
|
+
* Vendure `LanguageCode` region variants use underscores (`pt_BR`, `zh_Hans`),
|
|
11
|
+
* which are not valid BCP-47 separators and make `Intl.NumberFormat` throw —
|
|
12
|
+
* normalize to hyphens so a channel default language can be passed straight in.
|
|
13
|
+
*/
|
|
1
14
|
export function formatPrice(amount, currencyCode, locale) {
|
|
2
15
|
const format = new Intl.NumberFormat((locale ?? 'en').replace(/_/g, '-'), {
|
|
3
16
|
style: 'currency',
|
|
4
17
|
currency: currencyCode,
|
|
5
18
|
});
|
|
19
|
+
// `currency` style always resolves a digit count; `?? 2` is a defensive
|
|
20
|
+
// fallback for the base type only (`maximumFractionDigits` is `number | undefined`).
|
|
6
21
|
const fractionDigits = format.resolvedOptions().maximumFractionDigits ?? 2;
|
|
7
22
|
return format.format(amount / 10 ** fractionDigits);
|
|
8
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Format a price range from two minor-unit amounts.
|
|
26
|
+
* Returns "€10.00 - €20.00" or the single price if min === max.
|
|
27
|
+
*/
|
|
9
28
|
export function formatPriceRange(min, max, currencyCode, locale) {
|
|
10
29
|
if (min === max) {
|
|
11
30
|
return formatPrice(min, currencyCode, locale);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/pricing/format.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/pricing/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,YAAoB,EAAE,MAAe;IAC/E,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE;QACxE,KAAK,EAAE,UAAU;QACjB,QAAQ,EAAE,YAAY;KACvB,CAAC,CAAC;IACH,wEAAwE;IACxE,qFAAqF;IACrF,MAAM,cAAc,GAAG,MAAM,CAAC,eAAe,EAAE,CAAC,qBAAqB,IAAI,CAAC,CAAC;IAC3E,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,EAAE,IAAI,cAAc,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAAW,EACX,GAAW,EACX,YAAoB,EACpB,MAAe;IAEf,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;QAChB,OAAO,WAAW,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,CAAC,MAAM,WAAW,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,CAAC;AACjG,CAAC"}
|
package/dist/pricing/index.d.ts
CHANGED
|
@@ -3,6 +3,13 @@ import type { PricingModule, StoreContext } from '../types.js';
|
|
|
3
3
|
export { formatPrice, formatPriceRange } from './format.js';
|
|
4
4
|
export { ACTIVE_TAX_DISPLAY_MODE, DEFAULT_TAX_DISPLAY_POLICY, identityStamp, parseTaxDisplayPolicy, parseTaxExclusiveCountries, resolveTaxDisplayMode, taxDisplayModeFromEnum, } from './tax-display.js';
|
|
5
5
|
export { computeTierTable, getTierForQuantity } from './tiers.js';
|
|
6
|
-
export { validateOrderMinimums } from './validation.js';
|
|
6
|
+
export { backorderedQuantityForCart, isBackorderOnly, maxOrderableQuantity, type OrderableStockInput, validateOrderMinimums, } from './validation.js';
|
|
7
|
+
/**
|
|
8
|
+
* @param http Optional Shop API client. Without it the module is fully offline
|
|
9
|
+
* (formatting, tier maths, the pure policy helpers) and
|
|
10
|
+
* `getTaxDisplayMode()` degrades to the channel storage mode rather than
|
|
11
|
+
* failing — which is what the `@sixtyninecommerce/client/pricing` subpath and
|
|
12
|
+
* the existing single-argument callers get.
|
|
13
|
+
*/
|
|
7
14
|
export declare function createPricingModule(ctx: StoreContext, http?: HttpClient): PricingModule;
|
|
8
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pricing/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAEV,aAAa,EACb,YAAY,EAGb,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pricing/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAEV,aAAa,EACb,YAAY,EAGb,MAAM,aAAa,CAAC;AAiBrB,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,aAAa,EACb,qBAAqB,EACrB,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EACL,0BAA0B,EAC1B,eAAe,EACf,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAEzB;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,aAAa,CA8GvF"}
|
package/dist/pricing/index.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { formatPrice, formatPriceRange } from './format.js';
|
|
2
2
|
import { ACTIVE_TAX_DISPLAY_MODE, identityStamp, parseTaxDisplayPolicy, resolveTaxDisplayMode, taxDisplayModeFromEnum, } from './tax-display.js';
|
|
3
3
|
import { computeTierTable, getTierForQuantity } from './tiers.js';
|
|
4
|
-
import { validateOrderMinimums } from './validation.js';
|
|
4
|
+
import { backorderedQuantityForCart, isBackorderOnly, maxOrderableQuantity, validateOrderMinimums, } from './validation.js';
|
|
5
5
|
export { formatPrice, formatPriceRange } from './format.js';
|
|
6
6
|
export { ACTIVE_TAX_DISPLAY_MODE, DEFAULT_TAX_DISPLAY_POLICY, identityStamp, parseTaxDisplayPolicy, parseTaxExclusiveCountries, resolveTaxDisplayMode, taxDisplayModeFromEnum, } from './tax-display.js';
|
|
7
7
|
export { computeTierTable, getTierForQuantity } from './tiers.js';
|
|
8
|
-
export { validateOrderMinimums } from './validation.js';
|
|
8
|
+
export { backorderedQuantityForCart, isBackorderOnly, maxOrderableQuantity, validateOrderMinimums, } from './validation.js';
|
|
9
|
+
/**
|
|
10
|
+
* @param http Optional Shop API client. Without it the module is fully offline
|
|
11
|
+
* (formatting, tier maths, the pure policy helpers) and
|
|
12
|
+
* `getTaxDisplayMode()` degrades to the channel storage mode rather than
|
|
13
|
+
* failing — which is what the `@sixtyninecommerce/client/pricing` subpath and
|
|
14
|
+
* the existing single-argument callers get.
|
|
15
|
+
*/
|
|
9
16
|
export function createPricingModule(ctx, http) {
|
|
17
|
+
/** The pre-policy behaviour: display in whatever basis the channel stores. */
|
|
10
18
|
const storageMode = () => (ctx.pricesIncludeTax ? 'gross' : 'net');
|
|
19
|
+
/** The cached mode, but only while it belongs to the current identity. */
|
|
11
20
|
const cachedMode = () => {
|
|
12
21
|
const cached = ctx.taxDisplay;
|
|
13
22
|
return cached && cached.stamp === identityStamp(ctx) ? cached.mode : null;
|
|
@@ -28,10 +37,14 @@ export function createPricingModule(ctx, http) {
|
|
|
28
37
|
mode = taxDisplayModeFromEnum(data?.activeTaxDisplayMode);
|
|
29
38
|
}
|
|
30
39
|
catch {
|
|
40
|
+
// Unreachable engine, an expired session, an engine predating the
|
|
41
|
+
// query — never fail a render over the display mode.
|
|
31
42
|
mode = null;
|
|
32
43
|
}
|
|
33
44
|
if (mode === null)
|
|
34
45
|
return storageMode();
|
|
46
|
+
// Stamp AFTER the round-trip: the response may have rotated the session
|
|
47
|
+
// token, and the answer belongs to the identity that now holds.
|
|
35
48
|
ctx.taxDisplay = { stamp: identityStamp(ctx), mode };
|
|
36
49
|
return mode;
|
|
37
50
|
},
|
|
@@ -85,6 +98,9 @@ export function createPricingModule(ctx, http) {
|
|
|
85
98
|
};
|
|
86
99
|
},
|
|
87
100
|
validateOrderMinimums,
|
|
101
|
+
maxOrderableQuantity,
|
|
102
|
+
isBackorderOnly,
|
|
103
|
+
backorderedQuantityForCart,
|
|
88
104
|
};
|
|
89
105
|
}
|
|
90
106
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pricing/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pricing/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which tax figure a shopper sees: incl. (`gross`) or excl. (`net`) tax.
|
|
3
|
+
*
|
|
4
|
+
* `Channel.pricesIncludeTax` is a STORAGE mode — it says which of `price` /
|
|
5
|
+
* `priceWithTax` the merchant typed in, not which one this shopper should be
|
|
6
|
+
* quoted. Those differ in real shops: US sales tax varies by state and even by
|
|
7
|
+
* district, so a US shopper must never be quoted a tax-inclusive price, and
|
|
8
|
+
* Dutch B2B buyers work in ex-VAT numbers, while Dutch consumers keep seeing
|
|
9
|
+
* the gross price the law requires. One channel, three audiences.
|
|
10
|
+
*
|
|
11
|
+
* So the engine resolves the mode per request and exposes it as
|
|
12
|
+
* `Query.activeTaxDisplayMode` — that query is the authority, and
|
|
13
|
+
* `client.pricing.getTaxDisplayMode()` is how a storefront reads it. The pure
|
|
14
|
+
* helpers below mirror the engine's rules
|
|
15
|
+
* (`services/vendure/src/tax-display/tax-display-policy.ts`) RULE FOR RULE so a
|
|
16
|
+
* client can preview a mode — a "who sees what" table, an SSR guess before the
|
|
17
|
+
* first round-trip — without one. Change one, change both.
|
|
18
|
+
*
|
|
19
|
+
* The channel policy behind it (three `public: true` Channel customFields, all
|
|
20
|
+
* defaulting to today's behaviour):
|
|
21
|
+
*
|
|
22
|
+
* taxDisplayDefault channel | gross | net (default `channel`)
|
|
23
|
+
* taxDisplayB2b inherit | gross | net (default `inherit`)
|
|
24
|
+
* taxExclusiveCountries JSON ["US"] (default empty)
|
|
25
|
+
*/
|
|
1
26
|
import type { StoreContext, TaxDisplayInputs, TaxDisplayMode, TaxDisplayPolicy } from '../types.js';
|
|
27
|
+
/** Every channel reads this until a merchant changes something. */
|
|
2
28
|
export declare const DEFAULT_TAX_DISPLAY_POLICY: TaxDisplayPolicy;
|
|
29
|
+
/**
|
|
30
|
+
* The resolved mode for the active shopper. Public, anonymous-safe, and it
|
|
31
|
+
* never errors server-side — a lookup failure there falls back to the channel
|
|
32
|
+
* default rather than failing the request.
|
|
33
|
+
*
|
|
34
|
+
* The answer depends on the request's identity, so the SDK sends it with the
|
|
35
|
+
* same `Authorization` / `x-order-scope` headers as every other call.
|
|
36
|
+
*/
|
|
3
37
|
export declare const ACTIVE_TAX_DISPLAY_MODE = "\n query ActiveTaxDisplayMode {\n activeTaxDisplayMode\n }\n";
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the mode for one shopper — the engine's rule, offline.
|
|
40
|
+
*
|
|
41
|
+
* Precedence — country, then B2B, then the default — and the order is the
|
|
42
|
+
* point. A US dealer is a US shopper first: their state's sales tax is the
|
|
43
|
+
* reason the price cannot be quoted gross, and that reason does not go away
|
|
44
|
+
* because they also happen to be a trade account.
|
|
45
|
+
*
|
|
46
|
+
* This is a PREVIEW. `getTaxDisplayMode()` is what a storefront renders from;
|
|
47
|
+
* only the server knows the shopper's resolved country (active order shipping
|
|
48
|
+
* address, else their default shipping address).
|
|
49
|
+
*/
|
|
4
50
|
export declare function resolveTaxDisplayMode(policy: TaxDisplayPolicy, inputs: TaxDisplayInputs): TaxDisplayMode;
|
|
51
|
+
/**
|
|
52
|
+
* Read the policy off `activeChannel.customFields`. LENIENT by design: an
|
|
53
|
+
* unreadable value degrades to the pre-policy default rather than throwing
|
|
54
|
+
* while a catalogue page renders. The dashboard's write-side validation is what
|
|
55
|
+
* stops a bad value being stored.
|
|
56
|
+
*/
|
|
5
57
|
export declare function parseTaxDisplayPolicy(fields: unknown): TaxDisplayPolicy;
|
|
58
|
+
/**
|
|
59
|
+
* The tax-exclusive country list, normalised to upper-case alpha-2 with
|
|
60
|
+
* duplicates dropped. Anything unreadable is an EMPTY list — the rule is
|
|
61
|
+
* opt-in, so falling open means "no country override", never "every country".
|
|
62
|
+
*/
|
|
6
63
|
export declare function parseTaxExclusiveCountries(raw: unknown): readonly string[];
|
|
64
|
+
/**
|
|
65
|
+
* Map the Shop API enum (`GROSS` / `NET`, upper-case like `OrderScope`) to the
|
|
66
|
+
* SDK's lower-case union. Returns `null` for anything else — an engine that
|
|
67
|
+
* predates the enum, a null, a typo — so the caller can fall back to the
|
|
68
|
+
* channel storage mode instead of rendering a guess.
|
|
69
|
+
*/
|
|
7
70
|
export declare function taxDisplayModeFromEnum(value: unknown): TaxDisplayMode | null;
|
|
71
|
+
/**
|
|
72
|
+
* Identity fingerprint of the current request context — the cache key for a
|
|
73
|
+
* resolved mode.
|
|
74
|
+
*
|
|
75
|
+
* The mode is a function of WHO is asking: the session (`Authorization`) and
|
|
76
|
+
* the storefront scope (`x-order-scope`) are the two things the SDK can change
|
|
77
|
+
* mid-session that also change the answer. Sign in, sign out, or switch to the
|
|
78
|
+
* trade portal and the stamp changes, so the next read re-resolves instead of
|
|
79
|
+
* showing the previous shopper's mode.
|
|
80
|
+
*
|
|
81
|
+
* A country change is the one non-identity input that can flip the mode; it is
|
|
82
|
+
* handled by clearing the cache in `setShippingAddress`, since the stamp cannot
|
|
83
|
+
* see it.
|
|
84
|
+
*/
|
|
8
85
|
export declare function identityStamp(ctx: Pick<StoreContext, 'authToken' | 'orderScope'>): string;
|
|
9
86
|
//# sourceMappingURL=tax-display.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tax-display.d.ts","sourceRoot":"","sources":["../../src/pricing/tax-display.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tax-display.d.ts","sourceRoot":"","sources":["../../src/pricing/tax-display.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EACV,YAAY,EAGZ,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAWrB,mEAAmE;AACnE,eAAO,MAAM,0BAA0B,EAAE,gBAIxC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,sEAInC,CAAC;AAaF;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,gBAAgB,GACvB,cAAc,CAShB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAWvE;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,CAoB1E;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAM5E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,YAAY,EAAE,WAAW,GAAG,YAAY,CAAC,GAAG,MAAM,CAEzF"}
|
|
@@ -1,22 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which tax figure a shopper sees: incl. (`gross`) or excl. (`net`) tax.
|
|
3
|
+
*
|
|
4
|
+
* `Channel.pricesIncludeTax` is a STORAGE mode — it says which of `price` /
|
|
5
|
+
* `priceWithTax` the merchant typed in, not which one this shopper should be
|
|
6
|
+
* quoted. Those differ in real shops: US sales tax varies by state and even by
|
|
7
|
+
* district, so a US shopper must never be quoted a tax-inclusive price, and
|
|
8
|
+
* Dutch B2B buyers work in ex-VAT numbers, while Dutch consumers keep seeing
|
|
9
|
+
* the gross price the law requires. One channel, three audiences.
|
|
10
|
+
*
|
|
11
|
+
* So the engine resolves the mode per request and exposes it as
|
|
12
|
+
* `Query.activeTaxDisplayMode` — that query is the authority, and
|
|
13
|
+
* `client.pricing.getTaxDisplayMode()` is how a storefront reads it. The pure
|
|
14
|
+
* helpers below mirror the engine's rules
|
|
15
|
+
* (`services/vendure/src/tax-display/tax-display-policy.ts`) RULE FOR RULE so a
|
|
16
|
+
* client can preview a mode — a "who sees what" table, an SSR guess before the
|
|
17
|
+
* first round-trip — without one. Change one, change both.
|
|
18
|
+
*
|
|
19
|
+
* The channel policy behind it (three `public: true` Channel customFields, all
|
|
20
|
+
* defaulting to today's behaviour):
|
|
21
|
+
*
|
|
22
|
+
* taxDisplayDefault channel | gross | net (default `channel`)
|
|
23
|
+
* taxDisplayB2b inherit | gross | net (default `inherit`)
|
|
24
|
+
* taxExclusiveCountries JSON ["US"] (default empty)
|
|
25
|
+
*/
|
|
26
|
+
/** ISO-3166 alpha-2: exactly two ASCII letters. */
|
|
1
27
|
const ALPHA2 = /^[A-Za-z]{2}$/;
|
|
28
|
+
/** Channel-wide default. `channel` follows `Channel.pricesIncludeTax`. */
|
|
2
29
|
const TAX_DISPLAY_DEFAULTS = ['channel', 'gross', 'net'];
|
|
30
|
+
/** B2B override. `inherit` leaves B2B shoppers on the channel default. */
|
|
3
31
|
const TAX_DISPLAY_B2B_MODES = ['inherit', 'gross', 'net'];
|
|
32
|
+
/** Every channel reads this until a merchant changes something. */
|
|
4
33
|
export const DEFAULT_TAX_DISPLAY_POLICY = {
|
|
5
34
|
default: 'channel',
|
|
6
35
|
b2b: 'inherit',
|
|
7
36
|
countries: [],
|
|
8
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* The resolved mode for the active shopper. Public, anonymous-safe, and it
|
|
40
|
+
* never errors server-side — a lookup failure there falls back to the channel
|
|
41
|
+
* default rather than failing the request.
|
|
42
|
+
*
|
|
43
|
+
* The answer depends on the request's identity, so the SDK sends it with the
|
|
44
|
+
* same `Authorization` / `x-order-scope` headers as every other call.
|
|
45
|
+
*/
|
|
9
46
|
export const ACTIVE_TAX_DISPLAY_MODE = `
|
|
10
47
|
query ActiveTaxDisplayMode {
|
|
11
48
|
activeTaxDisplayMode
|
|
12
49
|
}
|
|
13
50
|
`;
|
|
51
|
+
/**
|
|
52
|
+
* Trim and upper-case one country code, or `null` when it is not a plausible
|
|
53
|
+
* alpha-2 code. Applied to both the stored list and the country being judged,
|
|
54
|
+
* so `"us"` and `" US "` can never disagree with `"US"`.
|
|
55
|
+
*/
|
|
14
56
|
function normalizeCountryCode(raw) {
|
|
15
57
|
if (typeof raw !== 'string')
|
|
16
58
|
return null;
|
|
17
59
|
const trimmed = raw.trim();
|
|
18
60
|
return ALPHA2.test(trimmed) ? trimmed.toUpperCase() : null;
|
|
19
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolve the mode for one shopper — the engine's rule, offline.
|
|
64
|
+
*
|
|
65
|
+
* Precedence — country, then B2B, then the default — and the order is the
|
|
66
|
+
* point. A US dealer is a US shopper first: their state's sales tax is the
|
|
67
|
+
* reason the price cannot be quoted gross, and that reason does not go away
|
|
68
|
+
* because they also happen to be a trade account.
|
|
69
|
+
*
|
|
70
|
+
* This is a PREVIEW. `getTaxDisplayMode()` is what a storefront renders from;
|
|
71
|
+
* only the server knows the shopper's resolved country (active order shipping
|
|
72
|
+
* address, else their default shipping address).
|
|
73
|
+
*/
|
|
20
74
|
export function resolveTaxDisplayMode(policy, inputs) {
|
|
21
75
|
const country = normalizeCountryCode(inputs.countryCode);
|
|
22
76
|
if (country !== null && policy.countries.includes(country))
|
|
@@ -27,6 +81,12 @@ export function resolveTaxDisplayMode(policy, inputs) {
|
|
|
27
81
|
return policy.default;
|
|
28
82
|
return inputs.pricesIncludeTax ? 'gross' : 'net';
|
|
29
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Read the policy off `activeChannel.customFields`. LENIENT by design: an
|
|
86
|
+
* unreadable value degrades to the pre-policy default rather than throwing
|
|
87
|
+
* while a catalogue page renders. The dashboard's write-side validation is what
|
|
88
|
+
* stops a bad value being stored.
|
|
89
|
+
*/
|
|
30
90
|
export function parseTaxDisplayPolicy(fields) {
|
|
31
91
|
const raw = (fields ?? {});
|
|
32
92
|
return {
|
|
@@ -35,6 +95,11 @@ export function parseTaxDisplayPolicy(fields) {
|
|
|
35
95
|
countries: parseTaxExclusiveCountries(raw.taxExclusiveCountries),
|
|
36
96
|
};
|
|
37
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* The tax-exclusive country list, normalised to upper-case alpha-2 with
|
|
100
|
+
* duplicates dropped. Anything unreadable is an EMPTY list — the rule is
|
|
101
|
+
* opt-in, so falling open means "no country override", never "every country".
|
|
102
|
+
*/
|
|
38
103
|
export function parseTaxExclusiveCountries(raw) {
|
|
39
104
|
if (typeof raw !== 'string')
|
|
40
105
|
return [];
|
|
@@ -59,6 +124,12 @@ export function parseTaxExclusiveCountries(raw) {
|
|
|
59
124
|
}
|
|
60
125
|
return codes;
|
|
61
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Map the Shop API enum (`GROSS` / `NET`, upper-case like `OrderScope`) to the
|
|
129
|
+
* SDK's lower-case union. Returns `null` for anything else — an engine that
|
|
130
|
+
* predates the enum, a null, a typo — so the caller can fall back to the
|
|
131
|
+
* channel storage mode instead of rendering a guess.
|
|
132
|
+
*/
|
|
62
133
|
export function taxDisplayModeFromEnum(value) {
|
|
63
134
|
if (typeof value !== 'string')
|
|
64
135
|
return null;
|
|
@@ -69,6 +140,20 @@ export function taxDisplayModeFromEnum(value) {
|
|
|
69
140
|
return 'net';
|
|
70
141
|
return null;
|
|
71
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Identity fingerprint of the current request context — the cache key for a
|
|
145
|
+
* resolved mode.
|
|
146
|
+
*
|
|
147
|
+
* The mode is a function of WHO is asking: the session (`Authorization`) and
|
|
148
|
+
* the storefront scope (`x-order-scope`) are the two things the SDK can change
|
|
149
|
+
* mid-session that also change the answer. Sign in, sign out, or switch to the
|
|
150
|
+
* trade portal and the stamp changes, so the next read re-resolves instead of
|
|
151
|
+
* showing the previous shopper's mode.
|
|
152
|
+
*
|
|
153
|
+
* A country change is the one non-identity input that can flip the mode; it is
|
|
154
|
+
* handled by clearing the cache in `setShippingAddress`, since the stamp cannot
|
|
155
|
+
* see it.
|
|
156
|
+
*/
|
|
72
157
|
export function identityStamp(ctx) {
|
|
73
158
|
return `${ctx.authToken ?? ''}|${ctx.orderScope ?? ''}`;
|
|
74
159
|
}
|