@sixtyninecommerce/client 0.29.0 → 0.31.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 +556 -16
- 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 +82 -38
- package/dist/commerce/customer.d.ts.map +1 -1
- package/dist/commerce/customer.js +146 -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 +189 -133
- package/dist/commerce/index.d.ts.map +1 -1
- package/dist/commerce/index.js +9 -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 +162 -123
- package/dist/commerce/products.js.map +1 -1
- package/dist/commerce/queries.d.ts +204 -49
- package/dist/commerce/queries.d.ts.map +1 -1
- package/dist/commerce/queries.js +1296 -737
- 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/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.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 -55
- 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 +10 -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/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 +5539 -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 +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -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 +1 -1
- package/dist/pricing/index.d.ts.map +1 -1
- package/dist/pricing/index.js +5 -2
- 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 +152 -13
- package/dist/pricing/validation.d.ts.map +1 -1
- package/dist/pricing/validation.js +167 -13
- 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 +95 -0
- package/dist/seo/availability.d.ts.map +1 -0
- package/dist/seo/availability.js +89 -0
- package/dist/seo/availability.js.map +1 -0
- package/dist/seo/index.d.ts +4 -0
- package/dist/seo/index.d.ts.map +1 -1
- package/dist/seo/index.js +6 -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 +34 -1
- package/dist/seo/json-ld.js.map +1 -1
- package/dist/seo/meta-tags.d.ts +40 -1
- package/dist/seo/meta-tags.d.ts.map +1 -1
- package/dist/seo/meta-tags.js +8 -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 +20 -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 +1306 -2429
- 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 +19 -3
package/dist/seo/artifacts.d.ts
CHANGED
|
@@ -1,10 +1,73 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Platform artifact URLs
|
|
3
|
-
* robots.txt, llms.txt and the
|
|
4
|
-
*
|
|
5
|
-
* `
|
|
2
|
+
* Platform artifact URLs, the framework-neutral artifact read, and the 404
|
|
3
|
+
* beacon. The platform renders sitemap.xml, robots.txt, llms.txt and the
|
|
4
|
+
* IndexNow key file per channel; the storefront serves them on its own origin
|
|
5
|
+
* (`createSeoArtifactResponder` from `@sixtyninecommerce/client/web`, or
|
|
6
|
+
* `createSeoArtifactHandler` from `@sixtyninecommerce/client/next`).
|
|
6
7
|
*/
|
|
8
|
+
import { type PublicArtifactRead } from '../core/public-artifact.js';
|
|
9
|
+
import type * as Plugins from '../generated/plugins.js';
|
|
7
10
|
import type { StoreContext } from '../types.js';
|
|
11
|
+
/** Every artifact the platform renders per store. */
|
|
12
|
+
export declare const SEO_ARTIFACTS: readonly ["sitemap", "sitemap-child", "robots", "llms-txt", "indexnow-key"];
|
|
13
|
+
export type SeoArtifact = (typeof SEO_ARTIFACTS)[number];
|
|
14
|
+
/** The `200` body a path item's GET declares, whatever its media type. */
|
|
15
|
+
type DeclaredBody<PathItem> = PathItem extends {
|
|
16
|
+
get: {
|
|
17
|
+
responses: {
|
|
18
|
+
200: {
|
|
19
|
+
content: infer Content;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
} ? Content[keyof Content] : never;
|
|
24
|
+
/**
|
|
25
|
+
* One artifact read — see `client.seo.artifacts.fetch`. Narrow on `status`:
|
|
26
|
+
* only a `200` has a `body`: the text (`robots.txt`, `llms.txt`, the IndexNow
|
|
27
|
+
* key) or XML (the sitemaps) the plugins service declares — a `string`.
|
|
28
|
+
*/
|
|
29
|
+
export type SeoArtifactResult = PublicArtifactRead<DeclaredBody<Plugins.paths['/seo/artifacts/{channelCode}/sitemap.xml']> | DeclaredBody<Plugins.paths['/seo/artifacts/{channelCode}/sitemaps/{file}']> | DeclaredBody<Plugins.paths['/seo/artifacts/{channelCode}/robots.txt']> | DeclaredBody<Plugins.paths['/seo/artifacts/{channelCode}/llms.txt']> | DeclaredBody<Plugins.paths['/seo/artifacts/{channelCode}/indexnow-key.txt']>>;
|
|
30
|
+
export interface SeoArtifactFetchOptions {
|
|
31
|
+
/**
|
|
32
|
+
* The child sitemap's file name (`products-1.xml`) — required for
|
|
33
|
+
* `'sitemap-child'`, refused for every other artifact.
|
|
34
|
+
*/
|
|
35
|
+
childFile?: string;
|
|
36
|
+
/** The validator your caller sent (`If-None-Match`); a match answers `304`. */
|
|
37
|
+
ifNoneMatch?: string;
|
|
38
|
+
/** Cancels the read; an abort through it rejects with the signal's reason. */
|
|
39
|
+
signal?: AbortSignal;
|
|
40
|
+
/** Deadline for the whole read, body included; past it the result is `503`. Default 15s. */
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
}
|
|
43
|
+
export interface SeoArtifactsModule {
|
|
44
|
+
/**
|
|
45
|
+
* Read one platform-rendered SEO artifact for this store — framework-neutral,
|
|
46
|
+
* so any server can serve it on the storefront's own origin.
|
|
47
|
+
*
|
|
48
|
+
* Never throws for an upstream problem: the result is `200` (body, content
|
|
49
|
+
* type, `ETag`), `304` (unchanged since `ifNoneMatch`), `404` (the platform
|
|
50
|
+
* has no such file for this store; `detail` says why — llms.txt is off, no
|
|
51
|
+
* SEO plugin or storefront URL, the child sitemap is gone) or `503` with
|
|
52
|
+
* `retryAfterSeconds` (another upstream status, in `upstreamStatus` with the
|
|
53
|
+
* platform's `detail`; or a network failure or the timeout, in `error`).
|
|
54
|
+
* Throws `StorefrontError` `INVALID_ARGUMENT` before any request
|
|
55
|
+
* for an unknown artifact, a missing or malformed `childFile` on
|
|
56
|
+
* `'sitemap-child'` (the platform's own rule: `<kind>-<n>.xml`), or a
|
|
57
|
+
* `childFile` on any other artifact; rejects with the signal's reason when
|
|
58
|
+
* YOUR `signal` aborts.
|
|
59
|
+
*
|
|
60
|
+
* Sends no credential (the files are public) and no `User-Agent` of the
|
|
61
|
+
* caller's, uses the client's configured `fetch`, and sets no cache policy.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* const robots = await client.seo.artifacts.fetch('robots');
|
|
66
|
+
* if (robots.status === 200) console.log(robots.etag, robots.body);
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
fetch(artifact: SeoArtifact, options?: SeoArtifactFetchOptions): Promise<SeoArtifactResult>;
|
|
70
|
+
}
|
|
8
71
|
export interface SeoArtifactUrls {
|
|
9
72
|
/** Platform URL of the sitemap index, or of one child file when `childFile` is given. */
|
|
10
73
|
sitemapUrl(childFile?: string): string;
|
|
@@ -14,6 +77,24 @@ export interface SeoArtifactUrls {
|
|
|
14
77
|
indexnowKeyUrl(): string;
|
|
15
78
|
}
|
|
16
79
|
export declare function createArtifactUrls(ctx: StoreContext): SeoArtifactUrls;
|
|
80
|
+
/** Upstream deadline when the caller names none. */
|
|
81
|
+
export declare const DEFAULT_ARTIFACT_TIMEOUT_MS = 15000;
|
|
82
|
+
/** Whether `name` is a child-sitemap file name the platform can serve. */
|
|
83
|
+
export declare function isSitemapChildFile(name: string): boolean;
|
|
84
|
+
export declare function isSeoArtifact(value: unknown): value is SeoArtifact;
|
|
85
|
+
/**
|
|
86
|
+
* The platform URL of one artifact, with every argument refused before a
|
|
87
|
+
* request is made. Reads only the builder it needs, so a caller may pass any
|
|
88
|
+
* object carrying them (`client.seo` does).
|
|
89
|
+
*/
|
|
90
|
+
export declare function seoArtifactUrl(urls: Partial<SeoArtifactUrls>, artifact: SeoArtifact, childFile: string | undefined): string;
|
|
91
|
+
/**
|
|
92
|
+
* The one artifact read every surface shares: `client.seo.artifacts.fetch`,
|
|
93
|
+
* the `/web` responder, the `/next` handler and the MCP preview. `init` adds
|
|
94
|
+
* framework keys to the request (the Next handler's `next: { tags }`).
|
|
95
|
+
*/
|
|
96
|
+
export declare function fetchSeoArtifact(urls: Partial<SeoArtifactUrls>, fetchFn: typeof globalThis.fetch, artifact: SeoArtifact, options: SeoArtifactFetchOptions | undefined, init?: Record<string, unknown>): Promise<SeoArtifactResult>;
|
|
97
|
+
export declare function createArtifactsModule(ctx: StoreContext, urls: SeoArtifactUrls): SeoArtifactsModule;
|
|
17
98
|
/**
|
|
18
99
|
* Fire-and-forget 404 report — call from the storefront's not-found handler
|
|
19
100
|
* so the platform can suggest redirects for dead URLs. Never throws, never
|
|
@@ -22,4 +103,5 @@ export declare function createArtifactUrls(ctx: StoreContext): SeoArtifactUrls;
|
|
|
22
103
|
export declare function reportNotFound(ctx: StoreContext, path: string, options?: {
|
|
23
104
|
referrer?: string;
|
|
24
105
|
}): void;
|
|
106
|
+
export {};
|
|
25
107
|
//# sourceMappingURL=artifacts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../src/seo/artifacts.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../src/seo/artifacts.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,kBAAkB,EAAsB,MAAM,4BAA4B,CAAC;AAEzF,OAAO,KAAK,KAAK,OAAO,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,qDAAqD;AACrD,eAAO,MAAM,aAAa,6EAMhB,CAAC;AAGX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,0EAA0E;AAC1E,KAAK,YAAY,CAAC,QAAQ,IAAI,QAAQ,SAAS;IAC7C,GAAG,EAAE;QAAE,SAAS,EAAE;YAAE,GAAG,EAAE;gBAAE,OAAO,EAAE,MAAM,OAAO,CAAA;aAAE,CAAA;SAAE,CAAA;KAAE,CAAC;CACzD,GACG,OAAO,CAAC,MAAM,OAAO,CAAC,GACtB,KAAK,CAAC;AAEV;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,CAC9C,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC,GACvE,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC,GAC3E,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC,GACtE,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC,GACpE,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC,CAC/E,CAAC;AAGF,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAGD,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,KAAK,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC7F;AAGD,MAAM,WAAW,eAAe;IAC9B,yFAAyF;IACzF,UAAU,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvC,SAAS,IAAI,MAAM,CAAC;IACpB,UAAU,IAAI,MAAM,CAAC;IACrB,6EAA6E;IAC7E,cAAc,IAAI,MAAM,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,GAAG,eAAe,CAoBrE;AAED,oDAAoD;AACpD,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAclD,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAMD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAElE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,OAAO,CAAC,eAAe,CAAC,EAC9B,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,MAAM,CA0BR;AAoBD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,OAAO,CAAC,eAAe,CAAC,EAC9B,OAAO,EAAE,OAAO,UAAU,CAAC,KAAK,EAChC,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,uBAAuB,GAAG,SAAS,EAC5C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,iBAAiB,CAAC,CAQ5B;AAED,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,eAAe,GACpB,kBAAkB,CAMpB;AAYD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9B,IAAI,CA+BN"}
|
package/dist/seo/artifacts.js
CHANGED
|
@@ -1,34 +1,128 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Platform artifact URLs
|
|
3
|
-
* robots.txt, llms.txt and the
|
|
4
|
-
*
|
|
5
|
-
* `
|
|
2
|
+
* Platform artifact URLs, the framework-neutral artifact read, and the 404
|
|
3
|
+
* beacon. The platform renders sitemap.xml, robots.txt, llms.txt and the
|
|
4
|
+
* IndexNow key file per channel; the storefront serves them on its own origin
|
|
5
|
+
* (`createSeoArtifactResponder` from `@sixtyninecommerce/client/web`, or
|
|
6
|
+
* `createSeoArtifactHandler` from `@sixtyninecommerce/client/next`).
|
|
6
7
|
*/
|
|
8
|
+
import { readPublicArtifact } from '../core/public-artifact.js';
|
|
9
|
+
import { StorefrontError } from '../errors.js';
|
|
10
|
+
/** Every artifact the platform renders per store. */
|
|
11
|
+
export const SEO_ARTIFACTS = [
|
|
12
|
+
'sitemap',
|
|
13
|
+
'sitemap-child',
|
|
14
|
+
'robots',
|
|
15
|
+
'llms-txt',
|
|
16
|
+
'indexnow-key',
|
|
17
|
+
];
|
|
7
18
|
export function createArtifactUrls(ctx) {
|
|
8
|
-
|
|
19
|
+
// Per call, never at build: the store's code can move under a running client (a rename), and
|
|
20
|
+
// the old code's URLs stop answering a while after it.
|
|
21
|
+
const base = () => `${ctx.pluginsUrl}/seo/artifacts/${encodeURIComponent(ctx.channel.code)}`;
|
|
9
22
|
return {
|
|
10
23
|
sitemapUrl(childFile) {
|
|
11
24
|
return childFile
|
|
12
|
-
? `${base}/sitemaps/${encodeURIComponent(childFile)}`
|
|
13
|
-
: `${base}/sitemap.xml`;
|
|
25
|
+
? `${base()}/sitemaps/${encodeURIComponent(childFile)}`
|
|
26
|
+
: `${base()}/sitemap.xml`;
|
|
14
27
|
},
|
|
15
28
|
robotsUrl() {
|
|
16
|
-
return `${base}/robots.txt`;
|
|
29
|
+
return `${base()}/robots.txt`;
|
|
17
30
|
},
|
|
18
31
|
llmsTxtUrl() {
|
|
19
|
-
return `${base}/llms.txt`;
|
|
32
|
+
return `${base()}/llms.txt`;
|
|
20
33
|
},
|
|
21
34
|
indexnowKeyUrl() {
|
|
22
|
-
return `${base}/indexnow-key.txt`;
|
|
35
|
+
return `${base()}/indexnow-key.txt`;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Upstream deadline when the caller names none. */
|
|
40
|
+
export const DEFAULT_ARTIFACT_TIMEOUT_MS = 15_000;
|
|
41
|
+
/**
|
|
42
|
+
* The platform's child-sitemap rule, verbatim from the plugins service
|
|
43
|
+
* (`seo/artifacts/artifacts.controller.ts`): `products-1.xml`, `collections-2.xml`.
|
|
44
|
+
* Checked here too so a crawler probing `/sitemaps/<anything>` is a `404` on
|
|
45
|
+
* the storefront, not a platform `400` a proxy would report as an outage.
|
|
46
|
+
* Locked to the controller AND the sitemap builder by
|
|
47
|
+
* `services/plugins/src/seo/__tests__/sitemap-child-name-parity.spec.ts`: widen
|
|
48
|
+
* the grammar there and here in the same change, and release this SDK before
|
|
49
|
+
* the platform emits the new names.
|
|
50
|
+
*/
|
|
51
|
+
const SITEMAP_CHILD_FILE = /^[a-z]+-\d+\.xml$/;
|
|
52
|
+
/** Whether `name` is a child-sitemap file name the platform can serve. */
|
|
53
|
+
export function isSitemapChildFile(name) {
|
|
54
|
+
return SITEMAP_CHILD_FILE.test(name);
|
|
55
|
+
}
|
|
56
|
+
function invalidArtifactArgument(message) {
|
|
57
|
+
return new StorefrontError(message, 'plugins', 400, 'INVALID_ARGUMENT');
|
|
58
|
+
}
|
|
59
|
+
export function isSeoArtifact(value) {
|
|
60
|
+
return typeof value === 'string' && SEO_ARTIFACTS.includes(value);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The platform URL of one artifact, with every argument refused before a
|
|
64
|
+
* request is made. Reads only the builder it needs, so a caller may pass any
|
|
65
|
+
* object carrying them (`client.seo` does).
|
|
66
|
+
*/
|
|
67
|
+
export function seoArtifactUrl(urls, artifact, childFile) {
|
|
68
|
+
if (!isSeoArtifact(artifact)) {
|
|
69
|
+
throw invalidArtifactArgument(`Unknown SEO artifact "${String(artifact)}". Expected one of: ${SEO_ARTIFACTS.join(', ')}.`);
|
|
70
|
+
}
|
|
71
|
+
if (artifact !== 'sitemap-child' && childFile !== undefined) {
|
|
72
|
+
throw invalidArtifactArgument(`childFile is only for 'sitemap-child'; '${artifact}' has no child files.`);
|
|
73
|
+
}
|
|
74
|
+
const builder = artifactUrlBuilder(urls, artifact);
|
|
75
|
+
if (artifact === 'sitemap-child') {
|
|
76
|
+
if (childFile === undefined || childFile === '') {
|
|
77
|
+
throw invalidArtifactArgument("'sitemap-child' requires childFile, the child sitemap's file name (e.g. products-1.xml).");
|
|
78
|
+
}
|
|
79
|
+
if (!isSitemapChildFile(childFile)) {
|
|
80
|
+
throw invalidArtifactArgument(`"${childFile}" is not a child sitemap file name; the platform serves <kind>-<n>.xml (e.g. products-1.xml).`);
|
|
81
|
+
}
|
|
82
|
+
return builder(childFile);
|
|
83
|
+
}
|
|
84
|
+
return builder();
|
|
85
|
+
}
|
|
86
|
+
function artifactUrlBuilder(urls, artifact) {
|
|
87
|
+
const builder = artifact === 'sitemap' || artifact === 'sitemap-child'
|
|
88
|
+
? urls.sitemapUrl
|
|
89
|
+
: artifact === 'robots'
|
|
90
|
+
? urls.robotsUrl
|
|
91
|
+
: artifact === 'llms-txt'
|
|
92
|
+
? urls.llmsTxtUrl
|
|
93
|
+
: urls.indexnowKeyUrl;
|
|
94
|
+
if (typeof builder !== 'function') {
|
|
95
|
+
throw new TypeError(`The client has no URL builder for the '${artifact}' artifact.`);
|
|
96
|
+
}
|
|
97
|
+
return builder;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The one artifact read every surface shares: `client.seo.artifacts.fetch`,
|
|
101
|
+
* the `/web` responder, the `/next` handler and the MCP preview. `init` adds
|
|
102
|
+
* framework keys to the request (the Next handler's `next: { tags }`).
|
|
103
|
+
*/
|
|
104
|
+
export function fetchSeoArtifact(urls, fetchFn, artifact, options, init) {
|
|
105
|
+
const url = seoArtifactUrl(urls, artifact, options?.childFile);
|
|
106
|
+
return readPublicArtifact(fetchFn, url, (response) => response.text(), {
|
|
107
|
+
timeoutMs: options?.timeoutMs ?? DEFAULT_ARTIFACT_TIMEOUT_MS,
|
|
108
|
+
...(options?.ifNoneMatch ? { ifNoneMatch: options.ifNoneMatch } : {}),
|
|
109
|
+
...(options?.signal ? { signal: options.signal } : {}),
|
|
110
|
+
...(init ? { init } : {}),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
export function createArtifactsModule(ctx, urls) {
|
|
114
|
+
return {
|
|
115
|
+
async fetch(artifact, options) {
|
|
116
|
+
return fetchSeoArtifact(urls, ctx.fetch, artifact, options);
|
|
23
117
|
},
|
|
24
118
|
};
|
|
25
119
|
}
|
|
26
120
|
const BEACON_TIMEOUT_MS = 2_000;
|
|
27
121
|
/**
|
|
28
|
-
* Per-process dedup set
|
|
29
|
-
* for random URLs can't grow it without bound.
|
|
30
|
-
* occasional re-reports are fine (the platform
|
|
31
|
-
* is not.
|
|
122
|
+
* Per-process dedup set of `<channel id>\0<path>`, capped so a long-lived
|
|
123
|
+
* server process being crawled for random URLs can't grow it without bound.
|
|
124
|
+
* Past the cap the set resets: occasional re-reports are fine (the platform
|
|
125
|
+
* aggregates), unbounded memory is not.
|
|
32
126
|
*/
|
|
33
127
|
const MAX_REPORTED_PATHS = 5_000;
|
|
34
128
|
const reportedPaths = new Set();
|
|
@@ -38,13 +132,18 @@ const reportedPaths = new Set();
|
|
|
38
132
|
* blocks rendering; deduped per process/session.
|
|
39
133
|
*/
|
|
40
134
|
export function reportNotFound(ctx, path, options) {
|
|
41
|
-
|
|
135
|
+
// Keyed by STORE and path: the set is per process, and a process may serve
|
|
136
|
+
// several stores (`createStorefrontClient({ resolveStore })`, or two clients
|
|
137
|
+
// side by side) — keyed by path alone, store B's dead `/sale` was never
|
|
138
|
+
// reported once store A had reported its own.
|
|
139
|
+
const key = `${ctx.channel.id}\u0000${path}`;
|
|
140
|
+
if (!path.startsWith('/') || path.length > 2000 || reportedPaths.has(key)) {
|
|
42
141
|
return;
|
|
43
142
|
}
|
|
44
143
|
if (reportedPaths.size >= MAX_REPORTED_PATHS) {
|
|
45
144
|
reportedPaths.clear();
|
|
46
145
|
}
|
|
47
|
-
reportedPaths.add(
|
|
146
|
+
reportedPaths.add(key);
|
|
48
147
|
try {
|
|
49
148
|
void ctx
|
|
50
149
|
.fetch(`${ctx.pluginsUrl}/seo/not-found-reports`, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../src/seo/artifacts.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../src/seo/artifacts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Availability for structured data: one variant's stock + merchant publication
|
|
3
|
+
* state, expressed as the schema.org `ItemAvailability` URL that
|
|
4
|
+
* `Offer.availability` wants and the OpenGraph token `product:availability`
|
|
5
|
+
* wants.
|
|
6
|
+
*
|
|
7
|
+
* These are separate vocabularies for the same fact, and `ProductSeoExtras`
|
|
8
|
+
* carries ONE `availability` string that feeds both tags. So the schema.org URL
|
|
9
|
+
* is the value a storefront passes, and `openGraphAvailability` translates it
|
|
10
|
+
* back for the OpenGraph tag. Anything the SDK does not recognise is passed
|
|
11
|
+
* through untouched, which is what every caller that predates this file gets.
|
|
12
|
+
*/
|
|
13
|
+
import type { ProductAvailabilityFragment } from '../generated/shop.js';
|
|
14
|
+
/** The three `ItemAvailability` members this SDK emits. */
|
|
15
|
+
export type SchemaOrgAvailability = 'https://schema.org/InStock' | 'https://schema.org/OutOfStock' | 'https://schema.org/PreOrder';
|
|
16
|
+
/**
|
|
17
|
+
* What `schemaOrgAvailability` reads; satisfied by every SDK variant shape AND
|
|
18
|
+
* by a search row. The stock signals are the ones `isPreorderOnly` reads, read
|
|
19
|
+
* the same way (`knownNothingSaleable`), so the feed and the badge cannot
|
|
20
|
+
* disagree about whether a pre-order is buyable today.
|
|
21
|
+
*/
|
|
22
|
+
export interface AvailabilityForSeo {
|
|
23
|
+
/**
|
|
24
|
+
* The merchant publication state in force. Absent or `null` (a search row
|
|
25
|
+
* that predates the attribute, an older engine) is read as "nothing in
|
|
26
|
+
* force" — the pre-order-free behaviour.
|
|
27
|
+
*/
|
|
28
|
+
availability?: ProductAvailabilityFragment | null;
|
|
29
|
+
/** Available-to-promise units — the precise signal, preferred when present. */
|
|
30
|
+
saleable?: number | null;
|
|
31
|
+
/** A search row's stock flag, the only stock signal `SEARCH_PRODUCTS` selects. */
|
|
32
|
+
inStock?: boolean | null;
|
|
33
|
+
/** Vendure's display string — `'IN_STOCK'` / `'OUT_OF_STOCK'`. */
|
|
34
|
+
stockLevel?: string | null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The schema.org `ItemAvailability` URL for a variant. Pass the result as
|
|
38
|
+
* `extras.availability` to `metaTagsFor` — JSON-LD emits it verbatim as
|
|
39
|
+
* `Offer.availability`, and the OpenGraph `product:availability` tag gets the
|
|
40
|
+
* matching Facebook token automatically.
|
|
41
|
+
*
|
|
42
|
+
* ```ts
|
|
43
|
+
* const extras = {
|
|
44
|
+
* availability: schemaOrgAvailability(variant),
|
|
45
|
+
* availabilityStarts: variant.availability?.availableFrom,
|
|
46
|
+
* ...
|
|
47
|
+
* };
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* | state | stock the storefront can SEE | result |
|
|
51
|
+
* |---|---|---|
|
|
52
|
+
* | `comingSoon` | anything | `OutOfStock` |
|
|
53
|
+
* | `preorder` | known nothing to buy | `PreOrder` |
|
|
54
|
+
* | `preorder` | known stock, or no signal | `InStock` |
|
|
55
|
+
* | anything else | known nothing to buy | `OutOfStock` |
|
|
56
|
+
* | anything else | known stock, or no signal | `InStock` |
|
|
57
|
+
*
|
|
58
|
+
* **Coming soon is `OutOfStock`, whatever the stock.** schema.org defines
|
|
59
|
+
* `PreOrder` as "Indicates that the item is available for pre-order" — an
|
|
60
|
+
* assertion that it can be ORDERED now for later delivery, which is the one
|
|
61
|
+
* thing this SDK guarantees a coming-soon item is not (`orderable: false`,
|
|
62
|
+
* `canAddToCart` false, and the engine refuses every add with
|
|
63
|
+
* `quantityAvailable: 0`, stock on hand or not). Emitting it would advertise a
|
|
64
|
+
* purchasable offer behind a disabled buy button — a Merchant Center
|
|
65
|
+
* availability-versus-landing-page mismatch. `OutOfStock` is the honest
|
|
66
|
+
* member for "not currently purchasable". (`SoldOut` is wrong the other way:
|
|
67
|
+
* it says the item HAD stock and it went, not that it has not launched.)
|
|
68
|
+
*
|
|
69
|
+
* **A pre-order is `PreOrder` only while there is nothing to buy outright** —
|
|
70
|
+
* exactly when `isPreorderOnly(variant)` is true, through the same
|
|
71
|
+
* `knownNothingSaleable` predicate. Stock never ENDS a pre-order state (the
|
|
72
|
+
* merchant may keep selling from the warehouse while it stands), but a variant
|
|
73
|
+
* with stock on hand ships today: the page shows the ordinary in-stock UI, and
|
|
74
|
+
* a feed saying "Pre-order, available 12 March" for it is the same mismatch in
|
|
75
|
+
* the other direction. So it is `InStock`, and the JSON-LD composer drops
|
|
76
|
+
* `availabilityStarts` for an `InStock` offer. When no stock signal is
|
|
77
|
+
* readable it is not claimed as a pre-order either, matching the label.
|
|
78
|
+
*
|
|
79
|
+
* Everything else is today's rule, read through the same predicate:
|
|
80
|
+
* `saleable` when the channel exposes it, then a search row's `inStock`, then
|
|
81
|
+
* the `stockLevel` string, and `InStock` when nothing says otherwise. An
|
|
82
|
+
* unknown state — one a later engine added — falls through to the stock rule
|
|
83
|
+
* rather than throwing, which is why this is a chain of comparisons and not a
|
|
84
|
+
* `switch`.
|
|
85
|
+
*/
|
|
86
|
+
export declare function schemaOrgAvailability(variant: AvailabilityForSeo): SchemaOrgAvailability;
|
|
87
|
+
/**
|
|
88
|
+
* The OpenGraph `product:availability` token for an availability value.
|
|
89
|
+
*
|
|
90
|
+
* Translates the three schema.org URLs into Facebook's vocabulary and passes
|
|
91
|
+
* anything else through verbatim — so a storefront that has always passed
|
|
92
|
+
* `'in stock'` keeps emitting `'in stock'`, byte for byte.
|
|
93
|
+
*/
|
|
94
|
+
export declare function openGraphAvailability(availability: string): string;
|
|
95
|
+
//# sourceMappingURL=availability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"availability.d.ts","sourceRoot":"","sources":["../../src/seo/availability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AAIxE,2DAA2D;AAC3D,MAAM,MAAM,qBAAqB,GAC7B,4BAA4B,GAC5B,+BAA+B,GAC/B,6BAA6B,CAAC;AAGlC;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,YAAY,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAClD,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,kBAAkB,GAAG,qBAAqB,CAMxF;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAKlE"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Availability for structured data: one variant's stock + merchant publication
|
|
3
|
+
* state, expressed as the schema.org `ItemAvailability` URL that
|
|
4
|
+
* `Offer.availability` wants and the OpenGraph token `product:availability`
|
|
5
|
+
* wants.
|
|
6
|
+
*
|
|
7
|
+
* These are separate vocabularies for the same fact, and `ProductSeoExtras`
|
|
8
|
+
* carries ONE `availability` string that feeds both tags. So the schema.org URL
|
|
9
|
+
* is the value a storefront passes, and `openGraphAvailability` translates it
|
|
10
|
+
* back for the OpenGraph tag. Anything the SDK does not recognise is passed
|
|
11
|
+
* through untouched, which is what every caller that predates this file gets.
|
|
12
|
+
*/
|
|
13
|
+
import { knownNothingSaleable } from '../pricing/validation.js';
|
|
14
|
+
/**
|
|
15
|
+
* The schema.org `ItemAvailability` URL for a variant. Pass the result as
|
|
16
|
+
* `extras.availability` to `metaTagsFor` — JSON-LD emits it verbatim as
|
|
17
|
+
* `Offer.availability`, and the OpenGraph `product:availability` tag gets the
|
|
18
|
+
* matching Facebook token automatically.
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* const extras = {
|
|
22
|
+
* availability: schemaOrgAvailability(variant),
|
|
23
|
+
* availabilityStarts: variant.availability?.availableFrom,
|
|
24
|
+
* ...
|
|
25
|
+
* };
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* | state | stock the storefront can SEE | result |
|
|
29
|
+
* |---|---|---|
|
|
30
|
+
* | `comingSoon` | anything | `OutOfStock` |
|
|
31
|
+
* | `preorder` | known nothing to buy | `PreOrder` |
|
|
32
|
+
* | `preorder` | known stock, or no signal | `InStock` |
|
|
33
|
+
* | anything else | known nothing to buy | `OutOfStock` |
|
|
34
|
+
* | anything else | known stock, or no signal | `InStock` |
|
|
35
|
+
*
|
|
36
|
+
* **Coming soon is `OutOfStock`, whatever the stock.** schema.org defines
|
|
37
|
+
* `PreOrder` as "Indicates that the item is available for pre-order" — an
|
|
38
|
+
* assertion that it can be ORDERED now for later delivery, which is the one
|
|
39
|
+
* thing this SDK guarantees a coming-soon item is not (`orderable: false`,
|
|
40
|
+
* `canAddToCart` false, and the engine refuses every add with
|
|
41
|
+
* `quantityAvailable: 0`, stock on hand or not). Emitting it would advertise a
|
|
42
|
+
* purchasable offer behind a disabled buy button — a Merchant Center
|
|
43
|
+
* availability-versus-landing-page mismatch. `OutOfStock` is the honest
|
|
44
|
+
* member for "not currently purchasable". (`SoldOut` is wrong the other way:
|
|
45
|
+
* it says the item HAD stock and it went, not that it has not launched.)
|
|
46
|
+
*
|
|
47
|
+
* **A pre-order is `PreOrder` only while there is nothing to buy outright** —
|
|
48
|
+
* exactly when `isPreorderOnly(variant)` is true, through the same
|
|
49
|
+
* `knownNothingSaleable` predicate. Stock never ENDS a pre-order state (the
|
|
50
|
+
* merchant may keep selling from the warehouse while it stands), but a variant
|
|
51
|
+
* with stock on hand ships today: the page shows the ordinary in-stock UI, and
|
|
52
|
+
* a feed saying "Pre-order, available 12 March" for it is the same mismatch in
|
|
53
|
+
* the other direction. So it is `InStock`, and the JSON-LD composer drops
|
|
54
|
+
* `availabilityStarts` for an `InStock` offer. When no stock signal is
|
|
55
|
+
* readable it is not claimed as a pre-order either, matching the label.
|
|
56
|
+
*
|
|
57
|
+
* Everything else is today's rule, read through the same predicate:
|
|
58
|
+
* `saleable` when the channel exposes it, then a search row's `inStock`, then
|
|
59
|
+
* the `stockLevel` string, and `InStock` when nothing says otherwise. An
|
|
60
|
+
* unknown state — one a later engine added — falls through to the stock rule
|
|
61
|
+
* rather than throwing, which is why this is a chain of comparisons and not a
|
|
62
|
+
* `switch`.
|
|
63
|
+
*/
|
|
64
|
+
export function schemaOrgAvailability(variant) {
|
|
65
|
+
const state = variant.availability?.state;
|
|
66
|
+
if (state === 'comingSoon')
|
|
67
|
+
return 'https://schema.org/OutOfStock';
|
|
68
|
+
const nothingSaleable = knownNothingSaleable(variant) === true;
|
|
69
|
+
if (state === 'preorder' && nothingSaleable)
|
|
70
|
+
return 'https://schema.org/PreOrder';
|
|
71
|
+
return nothingSaleable ? 'https://schema.org/OutOfStock' : 'https://schema.org/InStock';
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The OpenGraph `product:availability` token for an availability value.
|
|
75
|
+
*
|
|
76
|
+
* Translates the three schema.org URLs into Facebook's vocabulary and passes
|
|
77
|
+
* anything else through verbatim — so a storefront that has always passed
|
|
78
|
+
* `'in stock'` keeps emitting `'in stock'`, byte for byte.
|
|
79
|
+
*/
|
|
80
|
+
export function openGraphAvailability(availability) {
|
|
81
|
+
if (availability === 'https://schema.org/InStock')
|
|
82
|
+
return 'in stock';
|
|
83
|
+
if (availability === 'https://schema.org/OutOfStock')
|
|
84
|
+
return 'out of stock';
|
|
85
|
+
if (availability === 'https://schema.org/PreOrder')
|
|
86
|
+
return 'preorder';
|
|
87
|
+
return availability;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=availability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"availability.js","sourceRoot":"","sources":["../../src/seo/availability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AA+BhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAA2B;IAC/D,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC;IAC1C,IAAI,KAAK,KAAK,YAAY;QAAE,OAAO,+BAA+B,CAAC;IACnE,MAAM,eAAe,GAAG,oBAAoB,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAC/D,IAAI,KAAK,KAAK,UAAU,IAAI,eAAe;QAAE,OAAO,6BAA6B,CAAC;IAClF,OAAO,eAAe,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,4BAA4B,CAAC;AAC1F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,YAAoB;IACxD,IAAI,YAAY,KAAK,4BAA4B;QAAE,OAAO,UAAU,CAAC;IACrE,IAAI,YAAY,KAAK,+BAA+B;QAAE,OAAO,cAAc,CAAC;IAC5E,IAAI,YAAY,KAAK,6BAA6B;QAAE,OAAO,UAAU,CAAC;IACtE,OAAO,YAAY,CAAC;AACtB,CAAC"}
|
package/dist/seo/index.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ import type { StoreContext } from '../types.js';
|
|
|
2
2
|
import type { SeoModule } from './types.js';
|
|
3
3
|
export declare function createSeoModule(ctx: StoreContext): SeoModule;
|
|
4
4
|
export type { AiSummaryInput, LlmsTxtEntryInput, } from './ai.js';
|
|
5
|
+
export type { SeoArtifact, SeoArtifactFetchOptions, SeoArtifactResult, SeoArtifactsModule, } from './artifacts.js';
|
|
6
|
+
export { isSitemapChildFile, SEO_ARTIFACTS } from './artifacts.js';
|
|
7
|
+
export type { AvailabilityForSeo, SchemaOrgAvailability } from './availability.js';
|
|
8
|
+
export { openGraphAvailability, schemaOrgAvailability } from './availability.js';
|
|
5
9
|
export type { CmsMetaTagsInput, CmsSeoExtras, CollectionMetaTagsInput, CollectionSeoExtras, HreflangLinkTag, MetaTagsEntityInput, MetaTagsResult, OpenGraphTag, ProductMetaTagsInput, ProductSeoExtras, TwitterTag, } from './meta-tags.js';
|
|
6
10
|
export type { RawCmsSeo, RawCollectionSeo, RawProductMetadata } from './metadata-parser.js';
|
|
7
11
|
export { parseCmsItemMetadata, parseCollectionMetadata, parseProductMetadata, robotsFromFlags, } from './metadata-parser.js';
|
package/dist/seo/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/seo/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/seo/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAQhD,OAAO,KAAK,EAAkB,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5D,wBAAgB,eAAe,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,CA2E5D;AAED,YAAY,EACV,cAAc,EACd,iBAAiB,GAClB,MAAM,SAAS,CAAC;AACjB,YAAY,EACV,WAAW,EACX,uBAAuB,EACvB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACnE,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AACnF,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AACjF,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,uBAAuB,EACvB,mBAAmB,EACnB,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,GACX,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC5F,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,YAAY,EACZ,uBAAuB,EACvB,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,EACV,yBAAyB,EACzB,wBAAwB,EACxB,yBAAyB,EACzB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,MAAM,EACN,YAAY,EACZ,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,4BAA4B,EAC5B,gBAAgB,EAChB,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,gBAAgB,EAChB,aAAa,EACb,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,OAAO,EACP,SAAS,EACT,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/seo/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createContextAssetUrlResolver } from '../media/urls.js';
|
|
2
2
|
import { aiSummaryFor, llmsTxtEntryFor } from './ai.js';
|
|
3
3
|
import { alternatesFor } from './alternates.js';
|
|
4
|
-
import { createArtifactUrls, reportNotFound } from './artifacts.js';
|
|
4
|
+
import { createArtifactsModule, createArtifactUrls, reportNotFound } from './artifacts.js';
|
|
5
|
+
import { schemaOrgAvailability } from './availability.js';
|
|
5
6
|
import { canonicalFor } from './canonical.js';
|
|
6
7
|
import { metaTagsFor } from './meta-tags.js';
|
|
7
8
|
import { composePerBotDirectives } from './robots.js';
|
|
@@ -16,6 +17,7 @@ export function createSeoModule(ctx) {
|
|
|
16
17
|
robotsUrl: artifacts.robotsUrl,
|
|
17
18
|
llmsTxtUrl: artifacts.llmsTxtUrl,
|
|
18
19
|
indexnowKeyUrl: artifacts.indexnowKeyUrl,
|
|
20
|
+
artifacts: createArtifactsModule(ctx, artifacts),
|
|
19
21
|
reportNotFound(path, options) {
|
|
20
22
|
reportNotFound(ctx, path, options);
|
|
21
23
|
},
|
|
@@ -72,6 +74,7 @@ export function createSeoModule(ctx) {
|
|
|
72
74
|
xRobotsHeaders: [],
|
|
73
75
|
};
|
|
74
76
|
},
|
|
77
|
+
schemaOrgAvailability,
|
|
75
78
|
aiSummaryFor(entity) {
|
|
76
79
|
return aiSummaryFor(entity);
|
|
77
80
|
},
|
|
@@ -80,6 +83,8 @@ export function createSeoModule(ctx) {
|
|
|
80
83
|
},
|
|
81
84
|
};
|
|
82
85
|
}
|
|
86
|
+
export { isSitemapChildFile, SEO_ARTIFACTS } from './artifacts.js';
|
|
87
|
+
export { openGraphAvailability, schemaOrgAvailability } from './availability.js';
|
|
83
88
|
export { parseCmsItemMetadata, parseCollectionMetadata, parseProductMetadata, robotsFromFlags, } from './metadata-parser.js';
|
|
84
89
|
export { DEFAULT_PRODUCT_PATH_SEGMENT, isUserFacingRoot, normalizeProductPathSegment, pickCanonicalRootIndex, } from './product-path.js';
|
|
85
90
|
export { buildUrl, readUrlStrategy } from './url-builder.js';
|
package/dist/seo/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/seo/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAEjE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/seo/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-ld.d.ts","sourceRoot":"","sources":["../../src/seo/json-ld.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC5F,OAAO,KAAK,EAAoB,aAAa,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"json-ld.d.ts","sourceRoot":"","sources":["../../src/seo/json-ld.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC5F,OAAO,KAAK,EAAoB,aAAa,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAwB1D,MAAM,WAAW,kBAAkB;IACjC,gFAAgF;IAChF,eAAe,EAAE,gBAAgB,CAAC;IAClC,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACrC,OAAO,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC9D;AAYD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAiFvF;AAGD,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,iBAAiB,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;CAClD;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkB7F;AAsCD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,gFAAgF;IAChF,eAAe,EAAE,gBAAgB,CAAC;CACnC;AAQD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWvF;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWhF;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUtF"}
|
package/dist/seo/json-ld.js
CHANGED
|
@@ -24,6 +24,15 @@ function pickImageUrls(resolveAssetUrl, ...assets) {
|
|
|
24
24
|
}
|
|
25
25
|
return urls;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* The two `ItemAvailability` values `schemaOrgAvailability` returns for an
|
|
29
|
+
* offer that cannot be bought outright today — the only ones a future
|
|
30
|
+
* `availabilityStarts` is consistent with. `InStock` is deliberately absent.
|
|
31
|
+
*/
|
|
32
|
+
const NOT_PURCHASABLE_NOW = new Set([
|
|
33
|
+
'https://schema.org/PreOrder',
|
|
34
|
+
'https://schema.org/OutOfStock',
|
|
35
|
+
]);
|
|
27
36
|
export function composeProductJsonLd(input) {
|
|
28
37
|
const { product, metadata, canonical, title, description, extras, reviews } = input;
|
|
29
38
|
const out = {
|
|
@@ -50,9 +59,33 @@ export function composeProductJsonLd(input) {
|
|
|
50
59
|
};
|
|
51
60
|
if (extras.availability) {
|
|
52
61
|
// schema.org expects an ItemAvailability URL, but text is also widely
|
|
53
|
-
// accepted by validators. Pass-through whatever the storefront provides
|
|
62
|
+
// accepted by validators. Pass-through whatever the storefront provides —
|
|
63
|
+
// `schemaOrgAvailability(variant)` (`./availability.js`) is what produces
|
|
64
|
+
// the URL, and it is the only way `PreOrder` ever gets emitted: a
|
|
65
|
+
// pre-order with nothing on hand reads 0 stock, so anything derived from
|
|
66
|
+
// `stockLevel` alone would say `OutOfStock` and hide a product the
|
|
67
|
+
// merchant is actively taking orders for.
|
|
54
68
|
offer.availability = extras.availability;
|
|
55
69
|
}
|
|
70
|
+
if (extras.availabilityStarts && NOT_PURCHASABLE_NOW.has(extras.availability ?? '')) {
|
|
71
|
+
// `https://schema.org/availabilityStarts` — "The beginning of the
|
|
72
|
+
// availability of the product or service included in the offer", a
|
|
73
|
+
// property of `Offer` (verified against schema.org). It is the date half
|
|
74
|
+
// of the two merchant states: a `PreOrder` with no availability date is
|
|
75
|
+
// an item-level feed error, and a coming-soon item (mapped to
|
|
76
|
+
// `OutOfStock`, which says nothing about WHEN) has no other way to say
|
|
77
|
+
// it is arriving.
|
|
78
|
+
//
|
|
79
|
+
// Only beside an offer that is NOT purchasable now. A pre-order with stock
|
|
80
|
+
// on hand is `InStock` — it ships today — and "in stock, available from
|
|
81
|
+
// 12 March" contradicts itself; a storefront passing
|
|
82
|
+
// `variant.availability?.availableFrom` unconditionally must not produce
|
|
83
|
+
// that. A hand-written availability string is not recognised either: the
|
|
84
|
+
// SDK cannot tell whether that offer is purchasable now. Nothing is
|
|
85
|
+
// emitted without a date, so an offer from a storefront that predates this
|
|
86
|
+
// field is byte-identical.
|
|
87
|
+
offer.availabilityStarts = extras.availabilityStarts;
|
|
88
|
+
}
|
|
56
89
|
if (extras.condition) {
|
|
57
90
|
offer.itemCondition = `https://schema.org/${extras.condition === 'new'
|
|
58
91
|
? 'NewCondition'
|