@sixtyninecommerce/client 0.29.0 → 0.30.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.
Files changed (47) hide show
  1. package/README.md +8 -2
  2. package/dist/commerce/products.d.ts.map +1 -1
  3. package/dist/commerce/products.js +5 -0
  4. package/dist/commerce/products.js.map +1 -1
  5. package/dist/commerce/queries.d.ts +15 -9
  6. package/dist/commerce/queries.d.ts.map +1 -1
  7. package/dist/commerce/queries.js +16 -0
  8. package/dist/commerce/queries.js.map +1 -1
  9. package/dist/commerce/saved-lists.d.ts +7 -7
  10. package/dist/commerce/saved-lists.d.ts.map +1 -1
  11. package/dist/commerce/saved-lists.js +1 -0
  12. package/dist/commerce/saved-lists.js.map +1 -1
  13. package/dist/commerce/search.d.ts.map +1 -1
  14. package/dist/commerce/search.js +8 -0
  15. package/dist/commerce/search.js.map +1 -1
  16. package/dist/index.d.ts +3 -3
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/pricing/index.d.ts +1 -1
  21. package/dist/pricing/index.d.ts.map +1 -1
  22. package/dist/pricing/index.js +5 -2
  23. package/dist/pricing/index.js.map +1 -1
  24. package/dist/pricing/validation.d.ts +89 -1
  25. package/dist/pricing/validation.d.ts.map +1 -1
  26. package/dist/pricing/validation.js +107 -1
  27. package/dist/pricing/validation.js.map +1 -1
  28. package/dist/seo/availability.d.ts +95 -0
  29. package/dist/seo/availability.d.ts.map +1 -0
  30. package/dist/seo/availability.js +89 -0
  31. package/dist/seo/availability.js.map +1 -0
  32. package/dist/seo/index.d.ts +2 -0
  33. package/dist/seo/index.d.ts.map +1 -1
  34. package/dist/seo/index.js +3 -0
  35. package/dist/seo/index.js.map +1 -1
  36. package/dist/seo/json-ld.d.ts.map +1 -1
  37. package/dist/seo/json-ld.js +34 -1
  38. package/dist/seo/json-ld.js.map +1 -1
  39. package/dist/seo/meta-tags.d.ts +40 -1
  40. package/dist/seo/meta-tags.d.ts.map +1 -1
  41. package/dist/seo/meta-tags.js +8 -1
  42. package/dist/seo/meta-tags.js.map +1 -1
  43. package/dist/seo/types.d.ts +11 -0
  44. package/dist/seo/types.d.ts.map +1 -1
  45. package/dist/types.d.ts +177 -4
  46. package/dist/types.d.ts.map +1 -1
  47. package/package.json +3 -3
@@ -1,11 +1,11 @@
1
1
  import { formatPrice, formatPriceRange } from './format.js';
2
2
  import { ACTIVE_TAX_DISPLAY_MODE, identityStamp, parseTaxDisplayPolicy, resolveTaxDisplayMode, taxDisplayModeFromEnum, } from './tax-display.js';
3
3
  import { computeTierTable, getTierForQuantity } from './tiers.js';
4
- import { backorderedQuantityForCart, isBackorderOnly, maxOrderableQuantity, validateOrderMinimums, } from './validation.js';
4
+ import { backorderedQuantityForCart, canAddToCart, isBackorderOnly, isComingSoon, isPreorderOnly, maxOrderableQuantity, validateOrderMinimums, } from './validation.js';
5
5
  export { formatPrice, formatPriceRange } from './format.js';
6
6
  export { ACTIVE_TAX_DISPLAY_MODE, DEFAULT_TAX_DISPLAY_POLICY, identityStamp, parseTaxDisplayPolicy, parseTaxExclusiveCountries, resolveTaxDisplayMode, taxDisplayModeFromEnum, } from './tax-display.js';
7
7
  export { computeTierTable, getTierForQuantity } from './tiers.js';
8
- export { backorderedQuantityForCart, isBackorderOnly, maxOrderableQuantity, validateOrderMinimums, } from './validation.js';
8
+ export { backorderedQuantityForCart, canAddToCart, isBackorderOnly, isComingSoon, isPreorderOnly, maxOrderableQuantity, validateOrderMinimums, } from './validation.js';
9
9
  /**
10
10
  * @param http Optional Shop API client. Without it the module is fully offline
11
11
  * (formatting, tier maths, the pure policy helpers) and
@@ -100,6 +100,9 @@ export function createPricingModule(ctx, http) {
100
100
  validateOrderMinimums,
101
101
  maxOrderableQuantity,
102
102
  isBackorderOnly,
103
+ isComingSoon,
104
+ isPreorderOnly,
105
+ canAddToCart,
103
106
  backorderedQuantityForCart,
104
107
  };
105
108
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pricing/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pricing/index.ts"],"names":[],"mappings":"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"}
@@ -43,12 +43,22 @@ export declare function validateOrderMinimums(order: {
43
43
  export type { OrderableStockInput };
44
44
  /**
45
45
  * How many units of a variant the current shopper may put in the cart:
46
- * `max(0, saleable) + backorderAllowance`.
46
+ * `max(0, saleable) + backorderAllowance` — or `0` when the merchant is not
47
+ * accepting orders for it at all (`availability.orderable === false`, i.e.
48
+ * "coming soon").
47
49
  *
48
50
  * Returns `null` when the channel does not expose stock (`saleable == null`) —
49
51
  * the storefront cannot know a ceiling then and should let the server decide,
50
52
  * exactly as before backorders existed. A `null` allowance counts as 0, so a
51
53
  * shopper the policy does not admit gets plain `saleable`.
54
+ *
55
+ * The coming-soon clamp comes FIRST and ignores stock: a coming-soon variant
56
+ * with units on hand is still refused by the server (`InsufficientStockError`,
57
+ * `quantityAvailable: 0`), so a stepper that let the shopper reach that number
58
+ * would be offering something no add-to-cart can deliver. `0` is returned
59
+ * rather than `null` even on a channel that hides stock, because `orderable:
60
+ * false` is a merchant decision the storefront CAN read — there is nothing to
61
+ * defer to the server about.
52
62
  */
53
63
  export declare function maxOrderableQuantity(variant: OrderableStockInput): number | null;
54
64
  /**
@@ -56,6 +66,84 @@ export declare function maxOrderableQuantity(variant: OrderableStockInput): numb
56
66
  * allowance. The signal to label "Backorder" instead of "Out of stock".
57
67
  */
58
68
  export declare function isBackorderOnly(variant: OrderableStockInput): boolean;
69
+ /**
70
+ * Whether the merchant is showing this variant as "coming soon": visible, but
71
+ * not orderable by anyone — not from stock on hand, and not on backorder.
72
+ *
73
+ * The signal to render a "Coming soon" badge (with `availability.availableFrom`
74
+ * as the date when it is set) and to disable add-to-cart. Stock never ends the
75
+ * state: a coming-soon variant with units on hand is still refused.
76
+ *
77
+ * Deliberately a `===` comparison and not a `switch`: a state this SDK release
78
+ * has never heard of must read as "not coming soon", never as `undefined`.
79
+ */
80
+ export declare function isComingSoon(variant: OrderableStockInput): boolean;
81
+ /**
82
+ * Does the storefront KNOW there is nothing to buy outright?
83
+ *
84
+ * Three answers, and the third is the point: `true` known empty, `false` known
85
+ * to have stock, `null` when no signal is readable and nothing may be asserted
86
+ * either way.
87
+ *
88
+ * The signals, most precise first — and which one a shape carries depends on
89
+ * the document it came from:
90
+ *
91
+ * - `saleable`, the precise number. It is `null` whenever the channel does not
92
+ * expose stock numbers — which is the DEFAULT
93
+ * (`Channel.customFields.exposeStockToStorefront` is off until a merchant
94
+ * turns it on), so on an ordinary retail tenant every shopper sees `null`.
95
+ * - `inStock`, the only stock signal a SEARCH row has: `SEARCH_PRODUCTS`
96
+ * selects neither `saleable` nor `stockLevel`, so without this a search grid
97
+ * could never tell a 0-stock pre-order from one with stock.
98
+ * - `stockLevel`, the coarse display string every VARIANT document selects.
99
+ * Any value other than `'OUT_OF_STOCK'` means there is something to sell (a
100
+ * custom `StockDisplayStrategy` may emit `'LOW_STOCK'` and the like).
101
+ */
102
+ export declare function knownNothingSaleable(variant: OrderableStockInput): boolean | null;
103
+ /**
104
+ * Whether this variant can be had ONLY as a pre-order: the merchant has it on
105
+ * pre-order AND the storefront can see there is nothing to buy outright.
106
+ *
107
+ * The label is "Pre-order", not "Out of stock". How MANY units this shopper may
108
+ * pre-order is `backorderAllowance` — pre-orders borrow the backorder cap, so
109
+ * an anonymous visitor sees `state: 'preorder'` with a `null` allowance and
110
+ * cannot in fact order beyond stock. Use `canAddToCart` for the button.
111
+ *
112
+ * **A `null` `saleable` is the DEFAULT, not an edge case, and it is not read as
113
+ * zero here.** `exposeStockToStorefront` is off until a merchant turns it on,
114
+ * so on an ordinary retail tenant `saleable` is `null` for everybody. Stock
115
+ * never ends a pre-order state either (the merchant may keep selling from the
116
+ * warehouse while the state stands), so reading `null` as "nothing saleable"
117
+ * badged a SKU with forty units on hand "Pre-order, ships 12 March" while the
118
+ * add succeeded from stock and the parcel shipped that afternoon. It now falls
119
+ * back to a search row's `inStock`, then to `stockLevel`, and answers `false`
120
+ * when no signal is knowable — "I cannot prove this is pre-order-only" rather
121
+ * than a wrong promise.
122
+ *
123
+ * **It does NOT match `isBackorderOnly`, despite the shape.** That one reads
124
+ * `saleable ?? 0` and cannot be wrong for the same reason: its
125
+ * `backorderAllowance > 0` clause is never true on a stock-hiding channel,
126
+ * since the allowance is `null` under exactly the same gate. The availability
127
+ * state has no such gate — it is public — so this helper needs the stock
128
+ * question answered properly.
129
+ */
130
+ export declare function isPreorderOnly(variant: OrderableStockInput): boolean;
131
+ /**
132
+ * Whether the buy button should be enabled for this shopper, right now.
133
+ *
134
+ * `true` unless the storefront can prove otherwise: the ceiling is `0` (coming
135
+ * soon, or out of stock with no allowance). A `null` ceiling — the channel
136
+ * hides stock — stays `true` and lets the server decide, exactly as before
137
+ * backorders and pre-orders existed.
138
+ *
139
+ * This is the ONE rule a storefront needs. In particular it is NOT
140
+ * `availability.orderable`: an anonymous shopper sees `orderable: true` on a
141
+ * pre-order they cannot order beyond stock, because the units come from
142
+ * `backorderAllowance`, which is `null` for them. The server refuses either way
143
+ * (`InsufficientStockError` on add, `INSUFFICIENT_STOCK` at placement) — this
144
+ * just stops the storefront offering it.
145
+ */
146
+ export declare function canAddToCart(variant: OrderableStockInput): boolean;
59
147
  /**
60
148
  * Units of a cart line physical stock will not cover — the "N on backorder"
61
149
  * hint BEFORE placement, when `backorderedQuantity` is still 0 — computed the
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/pricing/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAwB,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGpG;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE;IACL,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,EACD,WAAW,EAAE;IACX,6FAA6F;IAC7F,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kGAAkG;IAClG,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,GACA,qBAAqB,CAgDvB;AAED,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,GAAG,IAAI,CAGhF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAErE;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAC9C,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAIf"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/pricing/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAwB,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGpG;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE;IACL,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,EACD,WAAW,EAAE;IACX,6FAA6F;IAC7F,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kGAAkG;IAClG,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,GACA,qBAAqB,CAgDvB;AAED,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,GAAG,IAAI,CAIhF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAErE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAElE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,GAAG,IAAI,CAMjF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAEpE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAElE;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAC9C,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAIf"}
@@ -61,14 +61,26 @@ export function validateOrderMinimums(order, constraints) {
61
61
  }
62
62
  /**
63
63
  * How many units of a variant the current shopper may put in the cart:
64
- * `max(0, saleable) + backorderAllowance`.
64
+ * `max(0, saleable) + backorderAllowance` — or `0` when the merchant is not
65
+ * accepting orders for it at all (`availability.orderable === false`, i.e.
66
+ * "coming soon").
65
67
  *
66
68
  * Returns `null` when the channel does not expose stock (`saleable == null`) —
67
69
  * the storefront cannot know a ceiling then and should let the server decide,
68
70
  * exactly as before backorders existed. A `null` allowance counts as 0, so a
69
71
  * shopper the policy does not admit gets plain `saleable`.
72
+ *
73
+ * The coming-soon clamp comes FIRST and ignores stock: a coming-soon variant
74
+ * with units on hand is still refused by the server (`InsufficientStockError`,
75
+ * `quantityAvailable: 0`), so a stepper that let the shopper reach that number
76
+ * would be offering something no add-to-cart can deliver. `0` is returned
77
+ * rather than `null` even on a channel that hides stock, because `orderable:
78
+ * false` is a merchant decision the storefront CAN read — there is nothing to
79
+ * defer to the server about.
70
80
  */
71
81
  export function maxOrderableQuantity(variant) {
82
+ if (variant.availability?.orderable === false)
83
+ return 0;
72
84
  if (variant.saleable == null)
73
85
  return null;
74
86
  return Math.max(0, variant.saleable) + Math.max(0, variant.backorderAllowance ?? 0);
@@ -80,6 +92,100 @@ export function maxOrderableQuantity(variant) {
80
92
  export function isBackorderOnly(variant) {
81
93
  return (variant.saleable ?? 0) <= 0 && (variant.backorderAllowance ?? 0) > 0;
82
94
  }
95
+ /**
96
+ * Whether the merchant is showing this variant as "coming soon": visible, but
97
+ * not orderable by anyone — not from stock on hand, and not on backorder.
98
+ *
99
+ * The signal to render a "Coming soon" badge (with `availability.availableFrom`
100
+ * as the date when it is set) and to disable add-to-cart. Stock never ends the
101
+ * state: a coming-soon variant with units on hand is still refused.
102
+ *
103
+ * Deliberately a `===` comparison and not a `switch`: a state this SDK release
104
+ * has never heard of must read as "not coming soon", never as `undefined`.
105
+ */
106
+ export function isComingSoon(variant) {
107
+ return variant.availability?.state === 'comingSoon';
108
+ }
109
+ /**
110
+ * Does the storefront KNOW there is nothing to buy outright?
111
+ *
112
+ * Three answers, and the third is the point: `true` known empty, `false` known
113
+ * to have stock, `null` when no signal is readable and nothing may be asserted
114
+ * either way.
115
+ *
116
+ * The signals, most precise first — and which one a shape carries depends on
117
+ * the document it came from:
118
+ *
119
+ * - `saleable`, the precise number. It is `null` whenever the channel does not
120
+ * expose stock numbers — which is the DEFAULT
121
+ * (`Channel.customFields.exposeStockToStorefront` is off until a merchant
122
+ * turns it on), so on an ordinary retail tenant every shopper sees `null`.
123
+ * - `inStock`, the only stock signal a SEARCH row has: `SEARCH_PRODUCTS`
124
+ * selects neither `saleable` nor `stockLevel`, so without this a search grid
125
+ * could never tell a 0-stock pre-order from one with stock.
126
+ * - `stockLevel`, the coarse display string every VARIANT document selects.
127
+ * Any value other than `'OUT_OF_STOCK'` means there is something to sell (a
128
+ * custom `StockDisplayStrategy` may emit `'LOW_STOCK'` and the like).
129
+ */
130
+ export function knownNothingSaleable(variant) {
131
+ if (typeof variant.saleable === 'number')
132
+ return variant.saleable <= 0;
133
+ if (typeof variant.inStock === 'boolean')
134
+ return !variant.inStock;
135
+ if (variant.stockLevel === 'OUT_OF_STOCK')
136
+ return true;
137
+ if (typeof variant.stockLevel === 'string' && variant.stockLevel !== '')
138
+ return false;
139
+ return null;
140
+ }
141
+ /**
142
+ * Whether this variant can be had ONLY as a pre-order: the merchant has it on
143
+ * pre-order AND the storefront can see there is nothing to buy outright.
144
+ *
145
+ * The label is "Pre-order", not "Out of stock". How MANY units this shopper may
146
+ * pre-order is `backorderAllowance` — pre-orders borrow the backorder cap, so
147
+ * an anonymous visitor sees `state: 'preorder'` with a `null` allowance and
148
+ * cannot in fact order beyond stock. Use `canAddToCart` for the button.
149
+ *
150
+ * **A `null` `saleable` is the DEFAULT, not an edge case, and it is not read as
151
+ * zero here.** `exposeStockToStorefront` is off until a merchant turns it on,
152
+ * so on an ordinary retail tenant `saleable` is `null` for everybody. Stock
153
+ * never ends a pre-order state either (the merchant may keep selling from the
154
+ * warehouse while the state stands), so reading `null` as "nothing saleable"
155
+ * badged a SKU with forty units on hand "Pre-order, ships 12 March" while the
156
+ * add succeeded from stock and the parcel shipped that afternoon. It now falls
157
+ * back to a search row's `inStock`, then to `stockLevel`, and answers `false`
158
+ * when no signal is knowable — "I cannot prove this is pre-order-only" rather
159
+ * than a wrong promise.
160
+ *
161
+ * **It does NOT match `isBackorderOnly`, despite the shape.** That one reads
162
+ * `saleable ?? 0` and cannot be wrong for the same reason: its
163
+ * `backorderAllowance > 0` clause is never true on a stock-hiding channel,
164
+ * since the allowance is `null` under exactly the same gate. The availability
165
+ * state has no such gate — it is public — so this helper needs the stock
166
+ * question answered properly.
167
+ */
168
+ export function isPreorderOnly(variant) {
169
+ return variant.availability?.state === 'preorder' && knownNothingSaleable(variant) === true;
170
+ }
171
+ /**
172
+ * Whether the buy button should be enabled for this shopper, right now.
173
+ *
174
+ * `true` unless the storefront can prove otherwise: the ceiling is `0` (coming
175
+ * soon, or out of stock with no allowance). A `null` ceiling — the channel
176
+ * hides stock — stays `true` and lets the server decide, exactly as before
177
+ * backorders and pre-orders existed.
178
+ *
179
+ * This is the ONE rule a storefront needs. In particular it is NOT
180
+ * `availability.orderable`: an anonymous shopper sees `orderable: true` on a
181
+ * pre-order they cannot order beyond stock, because the units come from
182
+ * `backorderAllowance`, which is `null` for them. The server refuses either way
183
+ * (`InsufficientStockError` on add, `INSUFFICIENT_STOCK` at placement) — this
184
+ * just stops the storefront offering it.
185
+ */
186
+ export function canAddToCart(variant) {
187
+ return maxOrderableQuantity(variant) !== 0;
188
+ }
83
189
  /**
84
190
  * Units of a cart line physical stock will not cover — the "N on backorder"
85
191
  * hint BEFORE placement, when `backorderedQuantity` is still 0 — computed the
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/pricing/validation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/pricing/validation.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 { ProductAvailability } from '../types.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?: ProductAvailability | 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;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,2DAA2D;AAC3D,MAAM,MAAM,qBAAqB,GAC7B,4BAA4B,GAC5B,+BAA+B,GAC/B,6BAA6B,CAAC;AAElC;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,YAAY,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC1C,+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;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AA8BhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
@@ -2,6 +2,8 @@ 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 { AvailabilityForSeo, SchemaOrgAvailability } from './availability.js';
6
+ export { openGraphAvailability, schemaOrgAvailability } from './availability.js';
5
7
  export type { CmsMetaTagsInput, CmsSeoExtras, CollectionMetaTagsInput, CollectionSeoExtras, HreflangLinkTag, MetaTagsEntityInput, MetaTagsResult, OpenGraphTag, ProductMetaTagsInput, ProductSeoExtras, TwitterTag, } from './meta-tags.js';
6
8
  export type { RawCmsSeo, RawCollectionSeo, RawProductMetadata } from './metadata-parser.js';
7
9
  export { parseCmsItemMetadata, parseCollectionMetadata, parseProductMetadata, robotsFromFlags, } from './metadata-parser.js';
@@ -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;AAOhD,OAAO,KAAK,EAAkB,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5D,wBAAgB,eAAe,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,CAyE5D;AAED,YAAY,EACV,cAAc,EACd,iBAAiB,GAClB,MAAM,SAAS,CAAC;AACjB,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"}
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,CA0E5D;AAED,YAAY,EACV,cAAc,EACd,iBAAiB,GAClB,MAAM,SAAS,CAAC;AACjB,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
@@ -2,6 +2,7 @@ import { createContextAssetUrlResolver } from '../media/urls.js';
2
2
  import { aiSummaryFor, llmsTxtEntryFor } from './ai.js';
3
3
  import { alternatesFor } from './alternates.js';
4
4
  import { 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';
@@ -72,6 +73,7 @@ export function createSeoModule(ctx) {
72
73
  xRobotsHeaders: [],
73
74
  };
74
75
  },
76
+ schemaOrgAvailability,
75
77
  aiSummaryFor(entity) {
76
78
  return aiSummaryFor(entity);
77
79
  },
@@ -80,6 +82,7 @@ export function createSeoModule(ctx) {
80
82
  },
81
83
  };
82
84
  }
85
+ export { openGraphAvailability, schemaOrgAvailability } from './availability.js';
83
86
  export { parseCmsItemMetadata, parseCollectionMetadata, parseProductMetadata, robotsFromFlags, } from './metadata-parser.js';
84
87
  export { DEFAULT_PRODUCT_PATH_SEGMENT, isUserFacingRoot, normalizeProductPathSegment, pickCanonicalRootIndex, } from './product-path.js';
85
88
  export { buildUrl, readUrlStrategy } from './url-builder.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/seo/index.ts"],"names":[],"mappings":"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"}
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;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAE7D,MAAM,UAAU,eAAe,CAAC,GAAiB;IAC/C,MAAM,SAAS,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAC1C,2EAA2E;IAC3E,oCAAoC;IACpC,MAAM,eAAe,GAAG,6BAA6B,CAAC,GAAG,CAAC,CAAC;IAC3D,OAAO;QACL,UAAU,EAAE,SAAS,CAAC,UAAU;QAChC,SAAS,EAAE,SAAS,CAAC,SAAS;QAC9B,UAAU,EAAE,SAAS,CAAC,UAAU;QAChC,cAAc,EAAE,SAAS,CAAC,cAAc;QACxC,cAAc,CAAC,IAAY,EAAE,OAA+B;YAC1D,cAAc,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QACrC,CAAC;QACD,YAAY,CAAC,MAAsB,EAAE,MAAe;YAClD,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YACjC,OAAO,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QACD,aAAa,CAAC,MAAsB;YAClC,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YACjC,OAAO,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,CAAC,MAAsB;YAC5B,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YACjC,MAAM,SAAS,GAAG,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC5C,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC9C,OAAO;gBACL,SAAS;gBACT,UAAU;gBACV,QAAQ,EAAE,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,mBAAmB,CAAC;aAC7D,CAAC;QACJ,CAAC;QACD,QAAQ,CAAC,QAAkB,EAAE,MAAe;YAC1C,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YACjC,OAAO,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QACzC,CAAC;QACD,WAAW,CAAC,MAAM;YAChB,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YACjC,OAAO,WAAW,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,MAAM,CAAC,CAAC;QAC7E,CAAC;QACD,kBAAkB,CAAC,MAAM;YACvB,4EAA4E;YAC5E,uEAAuE;YACvE,qEAAqE;YACrE,sEAAsE;YACtE,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC9B,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;oBAC7B,OAAO;wBACL,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;wBACtC,IAAI,EAAE,EAAE;wBACR,QAAQ,EAAE,EAAE;wBACZ,cAAc,EAAE,EAAE;qBACnB,CAAC;gBACJ,CAAC;gBACD,OAAO,uBAAuB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAClD,CAAC;YACD,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,EAAE,UAAU,IAAI,IAAI,CAAC;YACvD,OAAO;gBACL,OAAO,EAAE;oBACP,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI;oBACvD,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI;iBAC3D;gBACD,IAAI,EAAE,EAAE;gBACR,QAAQ,EAAE,EAAE;gBACZ,cAAc,EAAE,EAAE;aACnB,CAAC;QACJ,CAAC;QACD,qBAAqB;QACrB,YAAY,CAAC,MAAM;YACjB,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QACD,eAAe,CAAC,MAAM;YACpB,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC;KACF,CAAC;AACJ,CAAC;AAOD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAejF,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAoB9B,OAAO,EACL,4BAA4B,EAC5B,gBAAgB,EAChB,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAgB3B,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC"}
@@ -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;AAuB1D,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;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyDvF;AAED,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;AAqCD,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"}
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;AAuB1D,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;AAED,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;AAqCD,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"}
@@ -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'