@sixtyninecommerce/client 0.30.0 → 0.32.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 +574 -14
- package/dist/admin/http.d.ts.map +1 -1
- package/dist/admin/http.js.map +1 -1
- package/dist/admin/types.d.ts.map +1 -1
- package/dist/build-client.d.ts +9 -0
- package/dist/build-client.d.ts.map +1 -0
- package/dist/build-client.js +58 -0
- package/dist/build-client.js.map +1 -0
- package/dist/cache-tags.d.ts +35 -1
- package/dist/cache-tags.d.ts.map +1 -1
- package/dist/cache-tags.js +39 -1
- package/dist/cache-tags.js.map +1 -1
- package/dist/client.d.ts +25 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +9 -62
- package/dist/client.js.map +1 -1
- package/dist/commerce/age-verification.d.ts +7 -4
- package/dist/commerce/age-verification.d.ts.map +1 -1
- package/dist/commerce/age-verification.js +6 -4
- package/dist/commerce/age-verification.js.map +1 -1
- package/dist/commerce/arguments.d.ts +13 -0
- package/dist/commerce/arguments.d.ts.map +1 -0
- package/dist/commerce/arguments.js +19 -0
- package/dist/commerce/arguments.js.map +1 -0
- package/dist/commerce/branding.d.ts.map +1 -1
- package/dist/commerce/branding.js.map +1 -1
- package/dist/commerce/capabilities.d.ts +40 -0
- package/dist/commerce/capabilities.d.ts.map +1 -0
- package/dist/commerce/capabilities.js +41 -0
- package/dist/commerce/capabilities.js.map +1 -0
- package/dist/commerce/cart.d.ts +9 -12
- package/dist/commerce/cart.d.ts.map +1 -1
- package/dist/commerce/cart.js +6 -1
- package/dist/commerce/cart.js.map +1 -1
- package/dist/commerce/checkout.d.ts +95 -15
- package/dist/commerce/checkout.d.ts.map +1 -1
- package/dist/commerce/checkout.js +230 -10
- package/dist/commerce/checkout.js.map +1 -1
- package/dist/commerce/customer.d.ts +83 -38
- package/dist/commerce/customer.d.ts.map +1 -1
- package/dist/commerce/customer.js +149 -10
- package/dist/commerce/customer.js.map +1 -1
- package/dist/commerce/favorites.d.ts +2 -5
- package/dist/commerce/favorites.d.ts.map +1 -1
- package/dist/commerce/favorites.js.map +1 -1
- package/dist/commerce/index.d.ts +194 -133
- package/dist/commerce/index.d.ts.map +1 -1
- package/dist/commerce/index.js +15 -0
- package/dist/commerce/index.js.map +1 -1
- package/dist/commerce/order-states.d.ts +18 -0
- package/dist/commerce/order-states.d.ts.map +1 -0
- package/dist/commerce/order-states.js +30 -0
- package/dist/commerce/order-states.js.map +1 -0
- package/dist/commerce/parcel-pricing.d.ts +48 -0
- package/dist/commerce/parcel-pricing.d.ts.map +1 -0
- package/dist/commerce/parcel-pricing.js +39 -0
- package/dist/commerce/parcel-pricing.js.map +1 -0
- package/dist/commerce/parked-orders.d.ts +22 -0
- package/dist/commerce/parked-orders.d.ts.map +1 -0
- package/dist/commerce/parked-orders.js +47 -0
- package/dist/commerce/parked-orders.js.map +1 -0
- package/dist/commerce/payments.d.ts +12 -5
- package/dist/commerce/payments.d.ts.map +1 -1
- package/dist/commerce/payments.js +10 -1
- package/dist/commerce/payments.js.map +1 -1
- package/dist/commerce/placement-error.d.ts +17 -0
- package/dist/commerce/placement-error.d.ts.map +1 -1
- package/dist/commerce/placement-error.js.map +1 -1
- package/dist/commerce/products.d.ts +92 -38
- package/dist/commerce/products.d.ts.map +1 -1
- package/dist/commerce/products.js +157 -123
- package/dist/commerce/products.js.map +1 -1
- package/dist/commerce/queries.d.ts +216 -49
- package/dist/commerce/queries.d.ts.map +1 -1
- package/dist/commerce/queries.js +1347 -743
- package/dist/commerce/queries.js.map +1 -1
- package/dist/commerce/quotes.d.ts +5 -4
- package/dist/commerce/quotes.d.ts.map +1 -1
- package/dist/commerce/quotes.js +1 -36
- package/dist/commerce/quotes.js.map +1 -1
- package/dist/commerce/receipt-claim.d.ts +35 -0
- package/dist/commerce/receipt-claim.d.ts.map +1 -0
- package/dist/commerce/receipt-claim.js +66 -0
- package/dist/commerce/receipt-claim.js.map +1 -0
- package/dist/commerce/recommendations.d.ts +3 -3
- package/dist/commerce/recommendations.d.ts.map +1 -1
- package/dist/commerce/recommendations.js +3 -1
- package/dist/commerce/recommendations.js.map +1 -1
- package/dist/commerce/returns.d.ts +7 -6
- package/dist/commerce/returns.d.ts.map +1 -1
- package/dist/commerce/returns.js +24 -3
- package/dist/commerce/returns.js.map +1 -1
- package/dist/commerce/reviews.d.ts +24 -15
- package/dist/commerce/reviews.d.ts.map +1 -1
- package/dist/commerce/reviews.js +67 -50
- package/dist/commerce/reviews.js.map +1 -1
- package/dist/commerce/saved-lists.d.ts +15 -15
- package/dist/commerce/saved-lists.d.ts.map +1 -1
- package/dist/commerce/saved-lists.js +67 -56
- package/dist/commerce/saved-lists.js.map +1 -1
- package/dist/commerce/search.d.ts +3 -2
- package/dist/commerce/search.d.ts.map +1 -1
- package/dist/commerce/search.js +2 -2
- package/dist/commerce/search.js.map +1 -1
- package/dist/commerce/shipping.d.ts +11 -4
- package/dist/commerce/shipping.d.ts.map +1 -1
- package/dist/commerce/shipping.js +17 -2
- package/dist/commerce/shipping.js.map +1 -1
- package/dist/commerce/stock-notifications.d.ts +2 -0
- package/dist/commerce/stock-notifications.d.ts.map +1 -1
- package/dist/commerce/stock-notifications.js +2 -2
- package/dist/commerce/stock-notifications.js.map +1 -1
- package/dist/commerce/store-pickup.d.ts +37 -0
- package/dist/commerce/store-pickup.d.ts.map +1 -0
- package/dist/commerce/store-pickup.js +66 -0
- package/dist/commerce/store-pickup.js.map +1 -0
- package/dist/commerce/subscriptions.d.ts +35 -37
- package/dist/commerce/subscriptions.d.ts.map +1 -1
- package/dist/commerce/subscriptions.js +159 -120
- package/dist/commerce/subscriptions.js.map +1 -1
- package/dist/commerce/vat-validation.d.ts +6 -3
- package/dist/commerce/vat-validation.d.ts.map +1 -1
- package/dist/commerce/vat-validation.js +24 -19
- package/dist/commerce/vat-validation.js.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/content/assets.d.ts +5 -4
- package/dist/content/assets.d.ts.map +1 -1
- package/dist/content/assets.js.map +1 -1
- package/dist/content/collection-pages.d.ts +3 -2
- package/dist/content/collection-pages.d.ts.map +1 -1
- package/dist/content/collection-pages.js.map +1 -1
- package/dist/content/collections.d.ts +5 -4
- package/dist/content/collections.d.ts.map +1 -1
- package/dist/content/collections.js.map +1 -1
- package/dist/content/filtered-products.d.ts.map +1 -1
- package/dist/content/filtered-products.js.map +1 -1
- package/dist/content/form-field-types.d.ts +3 -3
- package/dist/content/form-field-types.d.ts.map +1 -1
- package/dist/content/form-field-types.js.map +1 -1
- package/dist/content/forms.d.ts +4 -3
- package/dist/content/forms.d.ts.map +1 -1
- package/dist/content/forms.js +3 -0
- package/dist/content/forms.js.map +1 -1
- package/dist/content/index.d.ts +18 -17
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/items.d.ts +5 -4
- package/dist/content/items.d.ts.map +1 -1
- package/dist/content/items.js +2 -0
- package/dist/content/items.js.map +1 -1
- package/dist/content/menus.d.ts +3 -2
- package/dist/content/menus.d.ts.map +1 -1
- package/dist/content/menus.js +7 -1
- package/dist/content/menus.js.map +1 -1
- package/dist/content/resolve.d.ts +3 -2
- package/dist/content/resolve.d.ts.map +1 -1
- package/dist/content/resolve.js +1 -1
- package/dist/content/resolve.js.map +1 -1
- package/dist/content/sitemap.d.ts +3 -2
- package/dist/content/sitemap.d.ts.map +1 -1
- package/dist/content/sitemap.js.map +1 -1
- package/dist/content/strings.d.ts +16 -2
- package/dist/content/strings.d.ts.map +1 -1
- package/dist/content/strings.js +32 -4
- package/dist/content/strings.js.map +1 -1
- package/dist/core/context.d.ts +127 -1
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +436 -45
- package/dist/core/context.js.map +1 -1
- package/dist/core/currency.d.ts +14 -0
- package/dist/core/currency.d.ts.map +1 -0
- package/dist/core/currency.js +52 -0
- package/dist/core/currency.js.map +1 -0
- package/dist/core/deprecation.d.ts +23 -0
- package/dist/core/deprecation.d.ts.map +1 -0
- package/dist/core/deprecation.js +81 -0
- package/dist/core/deprecation.js.map +1 -0
- package/dist/core/http.d.ts +15 -1
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/http.js +55 -3
- package/dist/core/http.js.map +1 -1
- package/dist/core/public-artifact.d.ts +72 -0
- package/dist/core/public-artifact.d.ts.map +1 -0
- package/dist/core/public-artifact.js +122 -0
- package/dist/core/public-artifact.js.map +1 -0
- package/dist/core/unknown-field-rejection.d.ts +18 -0
- package/dist/core/unknown-field-rejection.d.ts.map +1 -0
- package/dist/core/unknown-field-rejection.js +37 -0
- package/dist/core/unknown-field-rejection.js.map +1 -0
- package/dist/credit/credit-status.d.ts +2 -0
- package/dist/credit/credit-status.d.ts.map +1 -1
- package/dist/credit/credit-status.js +71 -19
- package/dist/credit/credit-status.js.map +1 -1
- package/dist/credit/index.d.ts +1 -1
- package/dist/credit/payment-terms.d.ts +3 -3
- package/dist/credit/payment-terms.d.ts.map +1 -1
- package/dist/credit/payment-terms.js +28 -24
- package/dist/credit/payment-terms.js.map +1 -1
- package/dist/currency/exchange.d.ts +7 -7
- package/dist/currency/exchange.d.ts.map +1 -1
- package/dist/currency/exchange.js +5 -1
- package/dist/currency/exchange.js.map +1 -1
- package/dist/errors.d.ts +17 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -1
- package/dist/events/index.d.ts +20 -7
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +17 -5
- package/dist/events/index.js.map +1 -1
- package/dist/events/payloads.d.ts +7 -4
- package/dist/events/payloads.d.ts.map +1 -1
- package/dist/events/payloads.js +6 -3
- package/dist/events/payloads.js.map +1 -1
- package/dist/feeds/index.d.ts +5 -0
- package/dist/feeds/index.d.ts.map +1 -0
- package/dist/feeds/index.js +12 -0
- package/dist/feeds/index.js.map +1 -0
- package/dist/feeds/types.d.ts +18 -0
- package/dist/feeds/types.d.ts.map +1 -0
- package/dist/feeds/types.js +8 -0
- package/dist/feeds/types.js.map +1 -0
- package/dist/generated/cms.d.ts +2136 -0
- package/dist/generated/cms.d.ts.map +1 -0
- package/dist/generated/cms.js +4 -0
- package/dist/generated/cms.js.map +1 -0
- package/dist/generated/plugins.d.ts +550 -0
- package/dist/generated/plugins.d.ts.map +1 -0
- package/dist/generated/plugins.js +4 -0
- package/dist/generated/plugins.js.map +1 -0
- package/dist/generated/shop.d.ts +5663 -0
- package/dist/generated/shop.d.ts.map +1 -0
- package/dist/generated/shop.js +5 -0
- package/dist/generated/shop.js.map +1 -0
- package/dist/graphql-document.d.ts +29 -0
- package/dist/graphql-document.d.ts.map +1 -0
- package/dist/graphql-document.js +21 -0
- package/dist/graphql-document.js.map +1 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/media/urls.d.ts.map +1 -1
- package/dist/media/urls.js +12 -1
- package/dist/media/urls.js.map +1 -1
- package/dist/media/video.d.ts.map +1 -1
- package/dist/media/video.js.map +1 -1
- package/dist/multi-store.d.ts +200 -0
- package/dist/multi-store.d.ts.map +1 -0
- package/dist/multi-store.js +558 -0
- package/dist/multi-store.js.map +1 -0
- package/dist/navigation/routes.d.ts +4 -3
- package/dist/navigation/routes.d.ts.map +1 -1
- package/dist/navigation/routes.js.map +1 -1
- package/dist/next/artifact-handler.d.ts +21 -13
- package/dist/next/artifact-handler.d.ts.map +1 -1
- package/dist/next/artifact-handler.js +28 -66
- package/dist/next/artifact-handler.js.map +1 -1
- package/dist/next/event-revalidate-handler.d.ts +26 -6
- package/dist/next/event-revalidate-handler.d.ts.map +1 -1
- package/dist/next/event-revalidate-handler.js +126 -19
- package/dist/next/event-revalidate-handler.js.map +1 -1
- package/dist/next/feed-handler.d.ts +50 -0
- package/dist/next/feed-handler.d.ts.map +1 -0
- package/dist/next/feed-handler.js +61 -0
- package/dist/next/feed-handler.js.map +1 -0
- package/dist/next/index.d.ts +3 -1
- package/dist/next/index.d.ts.map +1 -1
- package/dist/next/index.js +1 -0
- package/dist/next/index.js.map +1 -1
- package/dist/next/metadata.d.ts.map +1 -1
- package/dist/next/metadata.js.map +1 -1
- package/dist/next/redirect-middleware.d.ts +5 -31
- package/dist/next/redirect-middleware.d.ts.map +1 -1
- package/dist/next/redirect-middleware.js +4 -46
- package/dist/next/redirect-middleware.js.map +1 -1
- package/dist/next/revalidate-handler.d.ts.map +1 -1
- package/dist/next/revalidate-handler.js.map +1 -1
- package/dist/pricing/index.d.ts.map +1 -1
- package/dist/pricing/index.js.map +1 -1
- package/dist/pricing/tax-display.js +1 -1
- package/dist/pricing/tax-display.js.map +1 -1
- package/dist/pricing/tiers.d.ts +3 -2
- package/dist/pricing/tiers.d.ts.map +1 -1
- package/dist/pricing/tiers.js.map +1 -1
- package/dist/pricing/validation.d.ts +88 -12
- package/dist/pricing/validation.d.ts.map +1 -1
- package/dist/pricing/validation.js +93 -12
- package/dist/pricing/validation.js.map +1 -1
- package/dist/redirects/index.d.ts.map +1 -1
- package/dist/redirects/index.js +2 -1
- package/dist/redirects/index.js.map +1 -1
- package/dist/redirects/store.d.ts.map +1 -1
- package/dist/redirects/store.js +6 -2
- package/dist/redirects/store.js.map +1 -1
- package/dist/redirects/types.d.ts +5 -2
- package/dist/redirects/types.d.ts.map +1 -1
- package/dist/revalidation/event-plan.d.ts +55 -4
- package/dist/revalidation/event-plan.d.ts.map +1 -1
- package/dist/revalidation/event-plan.js +115 -8
- package/dist/revalidation/event-plan.js.map +1 -1
- package/dist/revalidation/event-verify.d.ts +8 -37
- package/dist/revalidation/event-verify.d.ts.map +1 -1
- package/dist/revalidation/event-verify.js +7 -97
- package/dist/revalidation/event-verify.js.map +1 -1
- package/dist/revalidation/types.d.ts.map +1 -1
- package/dist/revalidation/url-context.d.ts +17 -0
- package/dist/revalidation/url-context.d.ts.map +1 -1
- package/dist/revalidation/url-context.js +35 -2
- package/dist/revalidation/url-context.js.map +1 -1
- package/dist/seo/ai.d.ts.map +1 -1
- package/dist/seo/ai.js.map +1 -1
- package/dist/seo/artifacts.d.ts +86 -4
- package/dist/seo/artifacts.d.ts.map +1 -1
- package/dist/seo/artifacts.js +115 -16
- package/dist/seo/artifacts.js.map +1 -1
- package/dist/seo/availability.d.ts +2 -2
- package/dist/seo/availability.d.ts.map +1 -1
- package/dist/seo/availability.js.map +1 -1
- package/dist/seo/index.d.ts +2 -0
- package/dist/seo/index.d.ts.map +1 -1
- package/dist/seo/index.js +3 -1
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/json-ld.d.ts.map +1 -1
- package/dist/seo/json-ld.js.map +1 -1
- package/dist/seo/meta-tags.d.ts.map +1 -1
- package/dist/seo/meta-tags.js.map +1 -1
- package/dist/seo/metadata-parser.d.ts.map +1 -1
- package/dist/seo/metadata-parser.js.map +1 -1
- package/dist/seo/metadata-types.d.ts.map +1 -1
- package/dist/seo/robots.d.ts.map +1 -1
- package/dist/seo/robots.js.map +1 -1
- package/dist/seo/types.d.ts +9 -2
- package/dist/seo/types.d.ts.map +1 -1
- package/dist/seo/url-builder.d.ts +5 -3
- package/dist/seo/url-builder.d.ts.map +1 -1
- package/dist/seo/url-builder.js +3 -1
- package/dist/seo/url-builder.js.map +1 -1
- package/dist/types.d.ts +1381 -2537
- package/dist/types.d.ts.map +1 -1
- package/dist/web/artifact-responder.d.ts +69 -0
- package/dist/web/artifact-responder.d.ts.map +1 -0
- package/dist/web/artifact-responder.js +90 -0
- package/dist/web/artifact-responder.js.map +1 -0
- package/dist/web/feed-responder.d.ts +72 -0
- package/dist/web/feed-responder.d.ts.map +1 -0
- package/dist/web/feed-responder.js +81 -0
- package/dist/web/feed-responder.js.map +1 -0
- package/dist/web/index.d.ts +23 -0
- package/dist/web/index.d.ts.map +1 -0
- package/dist/web/index.js +19 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/redirect-middleware.d.ts +76 -0
- package/dist/web/redirect-middleware.d.ts.map +1 -0
- package/dist/web/redirect-middleware.js +94 -0
- package/dist/web/redirect-middleware.js.map +1 -0
- package/dist/web/respond.d.ts +20 -0
- package/dist/web/respond.d.ts.map +1 -0
- package/dist/web/respond.js +52 -0
- package/dist/web/respond.js.map +1 -0
- package/package.json +18 -2
|
@@ -3,20 +3,23 @@
|
|
|
3
3
|
* storefront's own origin (fetch-and-serve — hosting-adapter-independent,
|
|
4
4
|
* unlike next.config rewrites).
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* A thin wrapper over `createSeoArtifactResponder` from
|
|
7
|
+
* `@sixtyninecommerce/client/web`: the same statuses, headers and bodies (the
|
|
8
|
+
* crawler UA is not forwarded, `X-Robots-Tag` is dropped, `Cache-Control` is
|
|
9
|
+
* the storefront's, `ETag`/`If-None-Match` pass through). Two things are Next's
|
|
10
|
+
* own:
|
|
11
|
+
* - the upstream fetch carries `next: { tags: ['seo-artifact'] }`, the tag both
|
|
12
|
+
* revalidation handlers purge;
|
|
13
|
+
* - it fetches with the GLOBAL `fetch`, read per request, not the client's
|
|
14
|
+
* configured one — Next's data cache reads `next.tags` only through the
|
|
15
|
+
* `fetch` it patched onto `globalThis`, and this handler has always used it.
|
|
11
16
|
*/
|
|
17
|
+
import { type MultiStoreClient } from '../multi-store.js';
|
|
18
|
+
import { type SeoArtifact } from '../seo/artifacts.js';
|
|
12
19
|
import type { StorefrontClient } from '../types.js';
|
|
13
|
-
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
cacheControl?: string;
|
|
17
|
-
/** Upstream fetch timeout. Default 15s. */
|
|
18
|
-
timeoutMs?: number;
|
|
19
|
-
}
|
|
20
|
+
import { type SeoArtifactResponderOptions } from '../web/artifact-responder.js';
|
|
21
|
+
export type { SeoArtifact } from '../seo/artifacts.js';
|
|
22
|
+
export type SeoArtifactHandlerOptions = SeoArtifactResponderOptions;
|
|
20
23
|
/**
|
|
21
24
|
* Usage (App Router):
|
|
22
25
|
* ```ts
|
|
@@ -25,8 +28,13 @@ export interface SeoArtifactHandlerOptions {
|
|
|
25
28
|
* // src/app/sitemaps/[name]/route.ts
|
|
26
29
|
* export const GET = createSeoArtifactHandler(getClient(), 'sitemap-child');
|
|
27
30
|
* ```
|
|
31
|
+
*
|
|
32
|
+
* Several stores in one app: pass the store router
|
|
33
|
+
* (`createStorefrontClient({ resolveStore })`). Each request is served the
|
|
34
|
+
* artifact of the store it resolves to, tagged for that store too, and a
|
|
35
|
+
* request no store serves is answered 404.
|
|
28
36
|
*/
|
|
29
|
-
export declare function createSeoArtifactHandler(client: StorefrontClient | Promise<StorefrontClient
|
|
37
|
+
export declare function createSeoArtifactHandler(client: StorefrontClient | Promise<StorefrontClient> | MultiStoreClient, artifact: SeoArtifact, options?: SeoArtifactHandlerOptions): (request: Request, context?: {
|
|
30
38
|
params?: Promise<{
|
|
31
39
|
name?: string;
|
|
32
40
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-handler.d.ts","sourceRoot":"","sources":["../../src/next/artifact-handler.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"artifact-handler.d.ts","sourceRoot":"","sources":["../../src/next/artifact-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAEL,KAAK,2BAA2B,EACjC,MAAM,8BAA8B,CAAC;AAGtC,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,MAAM,MAAM,yBAAyB,GAAG,2BAA2B,CAAC;AAEpE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,EACvE,QAAQ,EAAE,WAAW,EACrB,OAAO,CAAC,EAAE,yBAAyB,GAClC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CAuB5F"}
|
|
@@ -3,32 +3,22 @@
|
|
|
3
3
|
* storefront's own origin (fetch-and-serve — hosting-adapter-independent,
|
|
4
4
|
* unlike next.config rewrites).
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* A thin wrapper over `createSeoArtifactResponder` from
|
|
7
|
+
* `@sixtyninecommerce/client/web`: the same statuses, headers and bodies (the
|
|
8
|
+
* crawler UA is not forwarded, `X-Robots-Tag` is dropped, `Cache-Control` is
|
|
9
|
+
* the storefront's, `ETag`/`If-None-Match` pass through). Two things are Next's
|
|
10
|
+
* own:
|
|
11
|
+
* - the upstream fetch carries `next: { tags: ['seo-artifact'] }`, the tag both
|
|
12
|
+
* revalidation handlers purge;
|
|
13
|
+
* - it fetches with the GLOBAL `fetch`, read per request, not the client's
|
|
14
|
+
* configured one — Next's data cache reads `next.tags` only through the
|
|
15
|
+
* `fetch` it patched onto `globalThis`, and this handler has always used it.
|
|
11
16
|
*/
|
|
12
17
|
import { SEO_ARTIFACT_TAG } from '../cache-tags.js';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
case 'sitemap':
|
|
18
|
-
return client.seo.sitemapUrl();
|
|
19
|
-
case 'sitemap-child': {
|
|
20
|
-
if (!childName)
|
|
21
|
-
throw new Error('sitemap-child requires a file name');
|
|
22
|
-
return client.seo.sitemapUrl(childName);
|
|
23
|
-
}
|
|
24
|
-
case 'robots':
|
|
25
|
-
return client.seo.robotsUrl();
|
|
26
|
-
case 'llms-txt':
|
|
27
|
-
return client.seo.llmsTxtUrl();
|
|
28
|
-
case 'indexnow-key':
|
|
29
|
-
return client.seo.indexnowKeyUrl();
|
|
30
|
-
}
|
|
31
|
-
}
|
|
18
|
+
import { adapterCacheTags } from '../multi-store.js';
|
|
19
|
+
import { fetchSeoArtifact } from '../seo/artifacts.js';
|
|
20
|
+
import { buildSeoArtifactResponder, } from '../web/artifact-responder.js';
|
|
21
|
+
import { failureResponse } from '../web/respond.js';
|
|
32
22
|
/**
|
|
33
23
|
* Usage (App Router):
|
|
34
24
|
* ```ts
|
|
@@ -37,55 +27,27 @@ function upstreamUrl(client, artifact, childName) {
|
|
|
37
27
|
* // src/app/sitemaps/[name]/route.ts
|
|
38
28
|
* export const GET = createSeoArtifactHandler(getClient(), 'sitemap-child');
|
|
39
29
|
* ```
|
|
30
|
+
*
|
|
31
|
+
* Several stores in one app: pass the store router
|
|
32
|
+
* (`createStorefrontClient({ resolveStore })`). Each request is served the
|
|
33
|
+
* artifact of the store it resolves to, tagged for that store too, and a
|
|
34
|
+
* request no store serves is answered 404.
|
|
40
35
|
*/
|
|
41
36
|
export function createSeoArtifactHandler(client, artifact, options) {
|
|
42
|
-
const
|
|
43
|
-
|
|
37
|
+
const respond = buildSeoArtifactResponder(client, artifact, options, (resolved, which, read) => fetchSeoArtifact(resolved.seo, (input, init) => globalThis.fetch(input, init), which, read,
|
|
38
|
+
// Tagged so the `seo-artifact` purge both revalidation handlers issue
|
|
39
|
+
// reaches a cached upstream fetch; before this tag, nothing carried it.
|
|
40
|
+
// A store router's request also carries the store-scoped copy.
|
|
41
|
+
{ next: { tags: adapterCacheTags(resolved, SEO_ARTIFACT_TAG) } }));
|
|
44
42
|
return async (request, context) => {
|
|
43
|
+
let params;
|
|
45
44
|
try {
|
|
46
|
-
|
|
47
|
-
const params = context?.params ? await context.params : undefined;
|
|
48
|
-
const url = upstreamUrl(resolvedClient, artifact, params?.name);
|
|
49
|
-
const headers = {};
|
|
50
|
-
const ifNoneMatch = request.headers.get('if-none-match');
|
|
51
|
-
if (ifNoneMatch)
|
|
52
|
-
headers['If-None-Match'] = ifNoneMatch;
|
|
53
|
-
// Tagged so the `seo-artifact` purge both revalidation handlers issue
|
|
54
|
-
// reaches a cached upstream fetch; before this tag, nothing carried it.
|
|
55
|
-
const init = {
|
|
56
|
-
headers,
|
|
57
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
58
|
-
next: { tags: [SEO_ARTIFACT_TAG] },
|
|
59
|
-
};
|
|
60
|
-
const upstream = await fetch(url, init);
|
|
61
|
-
if (upstream.status === 304) {
|
|
62
|
-
return new Response(null, { status: 304, headers: { 'Cache-Control': cacheControl } });
|
|
63
|
-
}
|
|
64
|
-
if (!upstream.ok) {
|
|
65
|
-
return new Response(upstream.status === 404 ? 'Not found' : 'Upstream unavailable', {
|
|
66
|
-
status: upstream.status === 404 ? 404 : 503,
|
|
67
|
-
headers: {
|
|
68
|
-
'Content-Type': 'text/plain',
|
|
69
|
-
...(upstream.status !== 404 ? { 'Retry-After': '60' } : {}),
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
const body = await upstream.text();
|
|
74
|
-
const responseHeaders = {
|
|
75
|
-
'Content-Type': upstream.headers.get('content-type') ?? 'text/plain; charset=utf-8',
|
|
76
|
-
'Cache-Control': cacheControl,
|
|
77
|
-
};
|
|
78
|
-
const etag = upstream.headers.get('etag');
|
|
79
|
-
if (etag)
|
|
80
|
-
responseHeaders.ETag = etag;
|
|
81
|
-
return new Response(body, { status: 200, headers: responseHeaders });
|
|
45
|
+
params = context?.params ? await context.params : undefined;
|
|
82
46
|
}
|
|
83
47
|
catch (error) {
|
|
84
|
-
return
|
|
85
|
-
status: 503,
|
|
86
|
-
headers: { 'Content-Type': 'text/plain', 'Retry-After': '60' },
|
|
87
|
-
});
|
|
48
|
+
return failureResponse(error);
|
|
88
49
|
}
|
|
50
|
+
return respond(request, params);
|
|
89
51
|
};
|
|
90
52
|
}
|
|
91
53
|
//# sourceMappingURL=artifact-handler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-handler.js","sourceRoot":"","sources":["../../src/next/artifact-handler.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"artifact-handler.js","sourceRoot":"","sources":["../../src/next/artifact-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAyB,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAoB,MAAM,qBAAqB,CAAC;AAEzE,OAAO,EACL,yBAAyB,GAE1B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAOpD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAuE,EACvE,QAAqB,EACrB,OAAmC;IAEnC,MAAM,OAAO,GAAG,yBAAyB,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAC7F,gBAAgB,CACd,QAAQ,CAAC,GAAG,EACZ,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,EAC9C,KAAK,EACL,IAAI;IACJ,sEAAsE;IACtE,wEAAwE;IACxE,+DAA+D;IAC/D,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,CAAC,EAAE,EAAE,CACjE,CACF,CAAC;IAEF,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAqB,EAAE;QACnD,IAAI,MAAqC,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -20,16 +20,28 @@
|
|
|
20
20
|
* by the `delivered_at` window, so there is no dedupe store to run.
|
|
21
21
|
*/
|
|
22
22
|
import type { PlatformEventEnvelope } from '../events/envelope.js';
|
|
23
|
+
import { type MultiStoreClient } from '../multi-store.js';
|
|
23
24
|
import { type RevalidationPlan, type RevalidationPlanOptions } from '../revalidation/event-plan.js';
|
|
24
25
|
import { type UrlContext, type UrlContextSource } from '../revalidation/url-context.js';
|
|
25
|
-
export interface EventRevalidateHandlerOptions extends RevalidationPlanOptions {
|
|
26
|
-
/**
|
|
27
|
-
|
|
26
|
+
export interface EventRevalidateHandlerOptions extends Omit<RevalidationPlanOptions, 'storeScoped'> {
|
|
27
|
+
/**
|
|
28
|
+
* The subscription's signing secret, shown once when it was created. Server-only env.
|
|
29
|
+
*
|
|
30
|
+
* Several stores on one route: each store's subscription has its OWN secret,
|
|
31
|
+
* so pass a function from the store code to that store's secret. A store it
|
|
32
|
+
* answers `undefined` (or `''`) for is treated like an unset secret — 500,
|
|
33
|
+
* retried, never 401: a subscription is created before its secret can be
|
|
34
|
+
* deployed, and a 401 streak would auto-disable it meanwhile. The code is
|
|
35
|
+
* read from the body before it is verified, and the signature over that same
|
|
36
|
+
* body is what binds it: a delivery claiming store B must be signed with B's
|
|
37
|
+
* secret.
|
|
38
|
+
*/
|
|
39
|
+
secret: string | ((storeCode: string) => string | undefined);
|
|
28
40
|
/** Pass `revalidatePath` from 'next/cache'. */
|
|
29
41
|
revalidatePath(path: string, type?: 'page' | 'layout'): void;
|
|
30
42
|
/**
|
|
31
|
-
* Pass `revalidateTag` from 'next/cache'. Required: product
|
|
32
|
-
* events revalidate by tag alone. It is called with the tag only — if your
|
|
43
|
+
* Pass `revalidateTag` from 'next/cache'. Required: product, variant,
|
|
44
|
+
* collection, facet, price-list and menu events revalidate by tag alone. It is called with the tag only — if your
|
|
33
45
|
* Next version's `revalidateTag` takes a cache-profile argument, pass a
|
|
34
46
|
* wrapper that supplies one.
|
|
35
47
|
*/
|
|
@@ -37,8 +49,16 @@ export interface EventRevalidateHandlerOptions extends RevalidationPlanOptions {
|
|
|
37
49
|
/**
|
|
38
50
|
* The channel's URL rule set and languages, used to compose each event path
|
|
39
51
|
* per locale — typically `urlContextFromClient(client)`.
|
|
52
|
+
*
|
|
53
|
+
* Several stores in one app: pass the store router
|
|
54
|
+
* (`createStorefrontClient({ resolveStore })`). Each event is then planned
|
|
55
|
+
* for its own store (the envelope's `channel_code`): its paths are composed
|
|
56
|
+
* on that store's address, `toRoutePath` receives the store code, and every
|
|
57
|
+
* tag is purged store-scoped (`store:<code>:<tag>`), which is what the
|
|
58
|
+
* router's reads carry beside the bare tags. A whole-site plan still
|
|
59
|
+
* revalidates the whole app (`revalidatePath('/', 'layout')`).
|
|
40
60
|
*/
|
|
41
|
-
urls: UrlContext | UrlContextSource;
|
|
61
|
+
urls: UrlContext | UrlContextSource | MultiStoreClient;
|
|
42
62
|
/** Reject deliveries whose `delivered_at` is further than this from now. Default 300s. */
|
|
43
63
|
toleranceSeconds?: number;
|
|
44
64
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-revalidate-handler.d.ts","sourceRoot":"","sources":["../../src/next/event-revalidate-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAE7B,MAAM,+BAA+B,CAAC;AAKvC,OAAO,
|
|
1
|
+
{"version":3,"file":"event-revalidate-handler.d.ts","sourceRoot":"","sources":["../../src/next/event-revalidate-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAuC,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAE7B,MAAM,+BAA+B,CAAC;AAKvC,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,gBAAgB,EAGtB,MAAM,gCAAgC,CAAC;AAGxC,MAAM,WAAW,6BACf,SAAQ,IAAI,CAAC,uBAAuB,EAAE,aAAa,CAAC;IACpD;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7D,+CAA+C;IAC/C,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC7D;;;;;OAKG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;;;;;;;;;OAWG;IACH,IAAI,EAAE,UAAU,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;IACvD,0FAA0F;IAC1F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,CAAC,QAAQ,EAAE,qBAAqB,EAAE,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxF;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACjE;AAgBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,6BAA6B,GACrC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAyLzC"}
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
* Stateless by design: revalidating twice is harmless, and a replay is bounded
|
|
20
20
|
* by the `delivered_at` window, so there is no dedupe store to run.
|
|
21
21
|
*/
|
|
22
|
+
import { forgetStoreCode, isMultiStoreClient } from '../multi-store.js';
|
|
22
23
|
import { classifyEvent, EventPayloadError, planFromIntent, resolveMaxPaths, } from '../revalidation/event-plan.js';
|
|
23
24
|
import { DEFAULT_EVENT_TOLERANCE_SECONDS, verifyEventDelivery, } from '../revalidation/event-verify.js';
|
|
24
|
-
import { urlContextFromChannelSettings, } from '../revalidation/url-context.js';
|
|
25
|
+
import { forgetStoreCodeOf, urlContextFromChannelSettings, urlContextFromClientFactory, } from '../revalidation/url-context.js';
|
|
25
26
|
function json(status, body) {
|
|
26
27
|
return new Response(JSON.stringify(body), {
|
|
27
28
|
status,
|
|
@@ -52,35 +53,63 @@ export function createEventRevalidateHandler(options) {
|
|
|
52
53
|
throw new TypeError(`toleranceSeconds must be a positive number, got ${toleranceSeconds}`);
|
|
53
54
|
}
|
|
54
55
|
const maxPaths = resolveMaxPaths(options.maxPaths);
|
|
55
|
-
const
|
|
56
|
-
if (typeof
|
|
56
|
+
const urlsOption = options.urls;
|
|
57
|
+
if (typeof urlsOption !== 'function' && (typeof urlsOption !== 'object' || urlsOption === null)) {
|
|
57
58
|
throw new TypeError('urls is required — pass urlContextFromClient(client) or a UrlContext');
|
|
58
59
|
}
|
|
59
60
|
const report = options.onError ?? defaultOnError;
|
|
61
|
+
// Several stores: every piece of per-store state below is keyed by the
|
|
62
|
+
// event's store, so one store's rule set or URL context is never used to
|
|
63
|
+
// compose another store's paths. One store: a single key, as before.
|
|
64
|
+
const stores = isMultiStoreClient(urlsOption) ? urlsOption : null;
|
|
65
|
+
const sources = new Map();
|
|
66
|
+
const sourceFor = (store) => {
|
|
67
|
+
if (!stores)
|
|
68
|
+
return urlsOption;
|
|
69
|
+
let source = sources.get(store);
|
|
70
|
+
if (!source) {
|
|
71
|
+
source = urlContextFromClientFactory(() => stores.forStore(store));
|
|
72
|
+
sources.set(store, source);
|
|
73
|
+
}
|
|
74
|
+
return source;
|
|
75
|
+
};
|
|
76
|
+
const keyOf = (envelope) => (stores ? envelope.channel_code : '');
|
|
60
77
|
// The rule set the last `channel.url_settings_changed` delivery carried. It
|
|
61
78
|
// is newer than anything `urls` can return: the client's channel snapshot is
|
|
62
79
|
// taken at startup, and a static UrlContext never changes.
|
|
63
|
-
|
|
64
|
-
const resolveUrls = async () =>
|
|
80
|
+
const adopted = new Map();
|
|
81
|
+
const resolveUrls = async (key) => {
|
|
82
|
+
const held = adopted.get(key);
|
|
83
|
+
if (held)
|
|
84
|
+
return held;
|
|
85
|
+
const source = sourceFor(key);
|
|
86
|
+
return typeof source === 'function' ? await source() : source;
|
|
87
|
+
};
|
|
65
88
|
const planOptions = { maxPaths, toRoutePath: options.toRoutePath };
|
|
66
|
-
// Path events whose URLs could not be composed since the last one that could
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
89
|
+
// Path events whose URLs could not be composed since the last one that could,
|
|
90
|
+
// per store: one store's breakage neither hides another's nor is re-reported
|
|
91
|
+
// because a different store's event mapped in between. Only the first of a
|
|
92
|
+
// streak is reported, so a broken URL source does not flood the log; every
|
|
93
|
+
// one of them is still visible through `onEvent`.
|
|
94
|
+
const mappingFailures = new Map();
|
|
95
|
+
const planPaths = async (intent, envelope, store) => {
|
|
71
96
|
try {
|
|
72
|
-
const plan = planFromIntent(intent, await resolveUrls(), planOptions);
|
|
73
|
-
mappingFailures
|
|
97
|
+
const plan = planFromIntent(intent, await resolveUrls(keyOf(envelope)), planOptions, store);
|
|
98
|
+
mappingFailures.delete(keyOf(envelope));
|
|
74
99
|
return plan;
|
|
75
100
|
}
|
|
76
101
|
catch (error) {
|
|
77
102
|
// Never a 5xx: delivery is ordered, so a failure that cannot clear would
|
|
78
103
|
// stall the subscription on this event for good without ever tripping the
|
|
79
104
|
// auto-disable. The whole site needs no URL at all.
|
|
80
|
-
|
|
105
|
+
const streak = mappingFailures.get(keyOf(envelope)) ?? 0;
|
|
106
|
+
if (streak === 0)
|
|
81
107
|
report(error, envelope);
|
|
82
|
-
mappingFailures
|
|
83
|
-
return {
|
|
108
|
+
mappingFailures.set(keyOf(envelope), streak + 1);
|
|
109
|
+
return {
|
|
110
|
+
...planFromIntent({ kind: 'all' }, null, undefined, store),
|
|
111
|
+
degraded: 'url_mapping_failed',
|
|
112
|
+
};
|
|
84
113
|
}
|
|
85
114
|
};
|
|
86
115
|
return async (request) => {
|
|
@@ -96,24 +125,80 @@ export function createEventRevalidateHandler(options) {
|
|
|
96
125
|
catch {
|
|
97
126
|
return json(400, { error: 'unreadable request body' });
|
|
98
127
|
}
|
|
99
|
-
|
|
128
|
+
let secret;
|
|
129
|
+
if (typeof options.secret === 'function') {
|
|
130
|
+
const claimed = claimedStore(rawBody);
|
|
131
|
+
// No store named: nothing to verify the signature with, so the delivery
|
|
132
|
+
// is as unauthenticated as a bad signature. The platform always names one.
|
|
133
|
+
if (claimed === null)
|
|
134
|
+
return json(401, { error: 'the delivery names no store' });
|
|
135
|
+
const found = options.secret(claimed);
|
|
136
|
+
// A store with no secret yet is the misconfiguration above, per store:
|
|
137
|
+
// 500, retried once it is deployed (ADR 2026-09-10, decision 3).
|
|
138
|
+
if (typeof found !== 'string' || found === '') {
|
|
139
|
+
return json(500, { error: 'revalidation secret is not configured for this store' });
|
|
140
|
+
}
|
|
141
|
+
secret = found;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
secret = options.secret;
|
|
145
|
+
}
|
|
146
|
+
const verified = await verifyEventDelivery(rawBody, request.headers, secret, {
|
|
100
147
|
toleranceSeconds,
|
|
101
148
|
});
|
|
102
149
|
if (!verified.ok)
|
|
103
150
|
return json(verified.status, { error: verified.reason });
|
|
104
151
|
const { envelope } = verified;
|
|
152
|
+
if (stores && envelope.channel_code === '') {
|
|
153
|
+
return json(400, { error: 'the envelope names no store (channel_code)' });
|
|
154
|
+
}
|
|
155
|
+
const key = keyOf(envelope);
|
|
156
|
+
const store = stores ? envelope.channel_code : undefined;
|
|
105
157
|
let plan;
|
|
106
158
|
try {
|
|
107
159
|
const intent = classifyEvent(envelope);
|
|
160
|
+
const source = sourceFor(key);
|
|
108
161
|
if (intent.kind === 'all' && intent.channelSettings) {
|
|
109
|
-
adopted
|
|
162
|
+
adopted.set(key, urlContextFromChannelSettings(intent.channelSettings));
|
|
110
163
|
if (typeof source === 'function')
|
|
111
164
|
source.invalidate?.();
|
|
112
165
|
}
|
|
166
|
+
// head.changed / channel.renamed: nothing in the payload is a rule set to
|
|
167
|
+
// adopt, so a source that can be told to re-read is invalidated and the
|
|
168
|
+
// adopted rule set dropped with it — the next path event reads the context
|
|
169
|
+
// fresh. A static UrlContext, or a function with no `invalidate`, cannot be
|
|
170
|
+
// made to re-read: dropping an adopted rule set there would fall back to
|
|
171
|
+
// whatever it returns, which may be the OLDER startup snapshot.
|
|
172
|
+
if ((intent.kind === 'tags' || intent.kind === 'none') &&
|
|
173
|
+
intent.invalidateUrlContext &&
|
|
174
|
+
typeof source === 'function' &&
|
|
175
|
+
typeof source.invalidate === 'function') {
|
|
176
|
+
adopted.delete(key);
|
|
177
|
+
source.invalidate();
|
|
178
|
+
}
|
|
179
|
+
// channel.renamed: the client's channel resolver may still hold the OLD code's answer
|
|
180
|
+
// (its token rotated by the rename) for minutes. Dropping it now means the next request
|
|
181
|
+
// asks the CMS, which answers the old code as the store under its new code, and no
|
|
182
|
+
// request has to fail first (W8-16b). Best-effort: a client that cannot be reached here
|
|
183
|
+
// still heals on its first refused request.
|
|
184
|
+
if (envelope.type === 'channel.renamed') {
|
|
185
|
+
const previous = previousCodeOf(envelope);
|
|
186
|
+
if (previous !== null) {
|
|
187
|
+
try {
|
|
188
|
+
if (stores)
|
|
189
|
+
forgetStoreCode(stores, previous);
|
|
190
|
+
else
|
|
191
|
+
await forgetStoreCodeOf(source, previous);
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
report(error, envelope);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
113
198
|
plan =
|
|
114
199
|
intent.kind === 'paths'
|
|
115
|
-
? await planPaths(intent, envelope)
|
|
116
|
-
: planFromIntent(intent, null, planOptions);
|
|
200
|
+
? await planPaths(intent, envelope, store)
|
|
201
|
+
: planFromIntent(intent, null, planOptions, store);
|
|
117
202
|
if (plan.all)
|
|
118
203
|
options.revalidatePath('/', 'layout');
|
|
119
204
|
for (const path of plan.paths)
|
|
@@ -147,4 +232,26 @@ export function createEventRevalidateHandler(options) {
|
|
|
147
232
|
});
|
|
148
233
|
};
|
|
149
234
|
}
|
|
235
|
+
/** A `channel.renamed` payload's old code, or null when it names none. */
|
|
236
|
+
function previousCodeOf(envelope) {
|
|
237
|
+
const data = envelope.data;
|
|
238
|
+
const code = data?.previous_code;
|
|
239
|
+
return typeof code === 'string' && code !== '' ? code : null;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The store a delivery CLAIMS to be for — read from the body before it is
|
|
243
|
+
* verified, which is safe because the signature over that same body is then
|
|
244
|
+
* checked with that store's secret: a delivery claiming store B verifies only
|
|
245
|
+
* under B's secret. `null` when the body names no store.
|
|
246
|
+
*/
|
|
247
|
+
function claimedStore(rawBody) {
|
|
248
|
+
let claimed;
|
|
249
|
+
try {
|
|
250
|
+
claimed = JSON.parse(rawBody)?.channel_code;
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
return typeof claimed === 'string' && claimed !== '' ? claimed : null;
|
|
256
|
+
}
|
|
150
257
|
//# sourceMappingURL=event-revalidate-handler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-revalidate-handler.js","sourceRoot":"","sources":["../../src/next/event-revalidate-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,cAAc,EAId,eAAe,GAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,+BAA+B,EAC/B,mBAAmB,GACpB,MAAM,iCAAiC,CAAC;AACzC,OAAO,
|
|
1
|
+
{"version":3,"file":"event-revalidate-handler.js","sourceRoot":"","sources":["../../src/next/event-revalidate-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route-handler factory that proxies a platform-rendered product feed onto the
|
|
3
|
+
* storefront's own origin — same fetch-and-serve shape as
|
|
4
|
+
* `createSeoArtifactHandler`, so the merchant centre only ever sees the
|
|
5
|
+
* storefront domain (hosting-adapter-independent, unlike next.config rewrites).
|
|
6
|
+
*
|
|
7
|
+
* A thin wrapper over `createProductFeedResponder` from
|
|
8
|
+
* `@sixtyninecommerce/client/web`: the same statuses, headers and bodies (the
|
|
9
|
+
* crawler UA is not forwarded, `X-Robots-Tag` is dropped, `Cache-Control` is
|
|
10
|
+
* the storefront's, `ETag`/`If-None-Match` pass through, and the body is
|
|
11
|
+
* buffered so a timeout is a clean 503, never a truncated 200 — the responder
|
|
12
|
+
* says why). Two things are Next's own:
|
|
13
|
+
* - the upstream fetch carries `next: { tags: ['product-feed'] }`;
|
|
14
|
+
* - it fetches with the GLOBAL `fetch`, read per request — the one Next patches
|
|
15
|
+
* and reads `next.tags` through.
|
|
16
|
+
*/
|
|
17
|
+
import { type MultiStoreClient } from '../multi-store.js';
|
|
18
|
+
import type { StorefrontClient } from '../types.js';
|
|
19
|
+
import { type ProductFeedResponderOptions } from '../web/feed-responder.js';
|
|
20
|
+
export interface ProductFeedHandlerOptions extends ProductFeedResponderOptions {
|
|
21
|
+
/**
|
|
22
|
+
* The feed's token (Dashboard → Marketing → Product feeds). Use on a static
|
|
23
|
+
* route. Omit it on a dynamic `[token]` route — the handler then reads the
|
|
24
|
+
* `token` route param instead.
|
|
25
|
+
*/
|
|
26
|
+
token?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Usage (App Router):
|
|
30
|
+
* ```ts
|
|
31
|
+
* // Static route — src/app/product-feed.xml/route.ts
|
|
32
|
+
* export const GET = createProductFeedHandler(getClient(), {
|
|
33
|
+
* token: process.env.GOOGLE_FEED_TOKEN,
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // Dynamic route — src/app/feeds/[token]/route.ts
|
|
37
|
+
* export const GET = createProductFeedHandler(getClient());
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* Several stores in one app: pass the store router and mount it on the
|
|
41
|
+
* dynamic `[token]` route — each store's feed has its own token — and each
|
|
42
|
+
* request is proxied from the store it resolves to (a request no store serves
|
|
43
|
+
* is answered 404).
|
|
44
|
+
*/
|
|
45
|
+
export declare function createProductFeedHandler(client: StorefrontClient | Promise<StorefrontClient> | MultiStoreClient, options?: ProductFeedHandlerOptions): (request: Request, context?: {
|
|
46
|
+
params?: Promise<{
|
|
47
|
+
token?: string;
|
|
48
|
+
}>;
|
|
49
|
+
}) => Promise<Response>;
|
|
50
|
+
//# sourceMappingURL=feed-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feed-handler.d.ts","sourceRoot":"","sources":["../../src/next/feed-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAEL,KAAK,2BAA2B,EAEjC,MAAM,0BAA0B,CAAC;AAIlC,MAAM,WAAW,yBAA0B,SAAQ,2BAA2B;IAC5E;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,EACvE,OAAO,CAAC,EAAE,yBAAyB,GAClC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CA2B7F"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route-handler factory that proxies a platform-rendered product feed onto the
|
|
3
|
+
* storefront's own origin — same fetch-and-serve shape as
|
|
4
|
+
* `createSeoArtifactHandler`, so the merchant centre only ever sees the
|
|
5
|
+
* storefront domain (hosting-adapter-independent, unlike next.config rewrites).
|
|
6
|
+
*
|
|
7
|
+
* A thin wrapper over `createProductFeedResponder` from
|
|
8
|
+
* `@sixtyninecommerce/client/web`: the same statuses, headers and bodies (the
|
|
9
|
+
* crawler UA is not forwarded, `X-Robots-Tag` is dropped, `Cache-Control` is
|
|
10
|
+
* the storefront's, `ETag`/`If-None-Match` pass through, and the body is
|
|
11
|
+
* buffered so a timeout is a clean 503, never a truncated 200 — the responder
|
|
12
|
+
* says why). Two things are Next's own:
|
|
13
|
+
* - the upstream fetch carries `next: { tags: ['product-feed'] }`;
|
|
14
|
+
* - it fetches with the GLOBAL `fetch`, read per request — the one Next patches
|
|
15
|
+
* and reads `next.tags` through.
|
|
16
|
+
*/
|
|
17
|
+
import { PRODUCT_FEED_TAG } from '../cache-tags.js';
|
|
18
|
+
import { readPublicArtifact } from '../core/public-artifact.js';
|
|
19
|
+
import { adapterCacheTags } from '../multi-store.js';
|
|
20
|
+
import { buildProductFeedResponder, readFeedBytes, } from '../web/feed-responder.js';
|
|
21
|
+
import { failureResponse } from '../web/respond.js';
|
|
22
|
+
/**
|
|
23
|
+
* Usage (App Router):
|
|
24
|
+
* ```ts
|
|
25
|
+
* // Static route — src/app/product-feed.xml/route.ts
|
|
26
|
+
* export const GET = createProductFeedHandler(getClient(), {
|
|
27
|
+
* token: process.env.GOOGLE_FEED_TOKEN,
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* // Dynamic route — src/app/feeds/[token]/route.ts
|
|
31
|
+
* export const GET = createProductFeedHandler(getClient());
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Several stores in one app: pass the store router and mount it on the
|
|
35
|
+
* dynamic `[token]` route — each store's feed has its own token — and each
|
|
36
|
+
* request is proxied from the store it resolves to (a request no store serves
|
|
37
|
+
* is answered 404).
|
|
38
|
+
*/
|
|
39
|
+
export function createProductFeedHandler(client, options) {
|
|
40
|
+
const respond = buildProductFeedResponder(client, options, 'createProductFeedHandler requires a feed token — pass options.token or mount it on a [token] route', (resolved) => (url, read) => readPublicArtifact((input, init) => globalThis.fetch(input, init), url, readFeedBytes, {
|
|
41
|
+
...read,
|
|
42
|
+
// Tagged for the same reason `createSeoArtifactHandler` tags its fetch:
|
|
43
|
+
// an upstream fetch that carries no tag cannot be purged at all, so a
|
|
44
|
+
// storefront that regenerates a feed on demand would have no way to drop
|
|
45
|
+
// the cached copy. The revalidation handlers deliberately do NOT purge
|
|
46
|
+
// it — see `cache-tags.ts`. A store router's request also carries the
|
|
47
|
+
// store-scoped copy.
|
|
48
|
+
init: { next: { tags: adapterCacheTags(resolved, PRODUCT_FEED_TAG) } },
|
|
49
|
+
}));
|
|
50
|
+
return async (request, context) => {
|
|
51
|
+
let params;
|
|
52
|
+
try {
|
|
53
|
+
params = context?.params ? await context.params : undefined;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
return failureResponse(error);
|
|
57
|
+
}
|
|
58
|
+
return respond(request, params);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=feed-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feed-handler.js","sourceRoot":"","sources":["../../src/next/feed-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAyB,MAAM,mBAAmB,CAAC;AAE5E,OAAO,EACL,yBAAyB,EAEzB,aAAa,GACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAYpD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAuE,EACvE,OAAmC;IAEnC,MAAM,OAAO,GAAG,yBAAyB,CACvC,MAAM,EACN,OAAO,EACP,oGAAoG,EACpG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAC1B,kBAAkB,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE;QACrF,GAAG,IAAI;QACP,wEAAwE;QACxE,sEAAsE;QACtE,yEAAyE;QACzE,uEAAuE;QACvE,sEAAsE;QACtE,qBAAqB;QACrB,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,CAAC,EAAE,EAAE;KACvE,CAAC,CACL,CAAC;IAEF,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAqB,EAAE;QACnD,IAAI,MAAsC,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/next/index.d.ts
CHANGED
|
@@ -13,10 +13,12 @@ export type { SeoArtifact, SeoArtifactHandlerOptions } from './artifact-handler.
|
|
|
13
13
|
export { createSeoArtifactHandler } from './artifact-handler.js';
|
|
14
14
|
export type { EventRevalidateHandlerOptions } from './event-revalidate-handler.js';
|
|
15
15
|
export { createEventRevalidateHandler } from './event-revalidate-handler.js';
|
|
16
|
+
export type { ProductFeedHandlerOptions } from './feed-handler.js';
|
|
17
|
+
export { createProductFeedHandler } from './feed-handler.js';
|
|
16
18
|
export { jsonLdScriptProps, serializeJsonLd } from './json-ld.js';
|
|
17
19
|
export type { NextCompatibleMetadata } from './metadata.js';
|
|
18
20
|
export { extraOpenGraphTags, toNextMetadata } from './metadata.js';
|
|
19
|
-
export type { RedirectMiddleware, RedirectMiddlewareOptions } from './redirect-middleware.js';
|
|
21
|
+
export type { MultiStoreRedirectMiddleware, MultiStoreRedirectMiddlewareOptions, RedirectMiddleware, RedirectMiddlewareOptions, } from './redirect-middleware.js';
|
|
20
22
|
export { createRedirectMiddleware } from './redirect-middleware.js';
|
|
21
23
|
export type { RevalidateHandlerOptions } from './revalidate-handler.js';
|
|
22
24
|
export { createRevalidateHandler } from './revalidate-handler.js';
|