@pagefront/lint-commerce 0.13.0 → 0.14.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 CHANGED
@@ -27,14 +27,14 @@ sheet type.
27
27
 
28
28
  ## Catalogue
29
29
 
30
- Catalogue 0.17.0, supporting commerce releases 0.5.0 (Product Sheet spec
30
+ Catalogue 0.20.0, supporting commerce releases 0.5.0 (Product Sheet spec
31
31
  0.11.0, Organization Sheet spec 0.5.0, Collection Sheet spec 0.1.0), 0.4.0
32
32
  (Product Sheet spec 0.10.0, Organization Sheet spec 0.4.0) and 0.3.0, and the
33
- legacy draft line (format 0.9): 62 product rules (`PDP-E-001` to `PDP-E-021`, `PDP-W-001` to
34
- `PDP-W-040`, `PDP-I-001`), 11 organization rules (`ORG-E-001` to
35
- `ORG-E-004`, `ORG-W-001` to `ORG-W-007`) and 10 collection rules
33
+ legacy draft line (format 0.9): 64 product rules (`PDP-E-001` to `PDP-E-022`, `PDP-W-001` to
34
+ `PDP-W-041`, `PDP-I-001`), 12 organization rules (`ORG-E-001` to
35
+ `ORG-E-004`, `ORG-W-001` to `ORG-W-008`) and 11 collection rules
36
36
  (`COL-E-001` to `COL-E-005`, `COL-W-001`, `COL-W-002`, `COL-W-004` to
37
- `COL-W-006`; `COL-W-003` is reserved), each defined in
37
+ `COL-W-007`; `COL-W-003` is reserved), each defined in
38
38
  [`rules.md`](https://github.com/themachineweb/pagefront/blob/main/commerce/spec/rules.md)
39
39
  with its target, check, message and remediation. Every report carries the
40
40
  `catalogue_version` it was produced with.
@@ -209,6 +209,29 @@ with the Commerce Collection Sheet spec
209
209
  no such schema (at `/format_version`).
210
210
  - New export: `collectionCatalogue`. `SheetType` gains `"collection"`.
211
211
 
212
+ Added in 0.14.0 (catalogue 0.20.0; catalogues 0.18.0 and 0.19.0 were never
213
+ published on their own). Requires `@pagefront/lint-core` 0.4.0, unchanged.
214
+
215
+ - `PDP-E-022` (applies from release 0.6.0) — "Unavailable offers
216
+ without a price" in the Product Sheet spec: a `checkoutPageURLTemplate` on
217
+ an Offer whose `availability` is `OutOfStock`, `SoldOut` or `Discontinued`.
218
+ From the same change, `PDP-W-016` is silent on such an Offer, which the
219
+ spec publishes without a price.
220
+ - `PDP-W-041` (applies from release 0.6.0) — a purchase URL off the
221
+ canonical host: an Offer's `url`, or its `checkoutPageURLTemplate`
222
+ expanded with `quantity` set to 1, on another host than the canonical
223
+ host. The linter cannot see the seller's `pagefront:checkoutHosts`.
224
+ - `ORG-W-008` (applies from release 0.6.0) — `pagefront:checkoutHosts`
225
+ without the `seller` role, and one finding per entry that is not a bare
226
+ host (not a string, empty, or carrying a scheme, a path or a port).
227
+ - `COL-W-007` (applies from release 0.5.0) — a parent entry in `isPartOf`
228
+ without `pagefront:sheetUrl`, or with a blank one: the counterpart of
229
+ `COL-W-002` for a sheet's parents. A category's parents are category
230
+ sheets, so the producer that emitted the sheet knows their addresses.
231
+ - The three rules gated to release 0.6.0 wait for that release: the bundled
232
+ release manifest has no 0.6.0 row yet, and a sheet declaring 0.6.0 fails
233
+ Layer 1 (`PDP-E-018`, `ORG-E-004`) until it lands.
234
+
212
235
  Changed in 0.12.1 (catalogue 0.16.1):
213
236
 
214
237
  - `PDP-E-017` adds a type hint. When a reference matches no option but a
package/dist/catalogue.js CHANGED
@@ -20,6 +20,7 @@ import { pdpE018 } from "./rules/pdp-e-018.js";
20
20
  import { pdpE019 } from "./rules/pdp-e-019.js";
21
21
  import { pdpE020 } from "./rules/pdp-e-020.js";
22
22
  import { pdpE021 } from "./rules/pdp-e-021.js";
23
+ import { pdpE022 } from "./rules/pdp-e-022.js";
23
24
  import { pdpW001 } from "./rules/pdp-w-001.js";
24
25
  import { pdpW002 } from "./rules/pdp-w-002.js";
25
26
  import { pdpW003 } from "./rules/pdp-w-003.js";
@@ -60,6 +61,7 @@ import { pdpW037 } from "./rules/pdp-w-037.js";
60
61
  import { pdpW038 } from "./rules/pdp-w-038.js";
61
62
  import { pdpW039 } from "./rules/pdp-w-039.js";
62
63
  import { pdpW040 } from "./rules/pdp-w-040.js";
64
+ import { pdpW041 } from "./rules/pdp-w-041.js";
63
65
  import { pdpI001 } from "./rules/pdp-i-001.js";
64
66
  /**
65
67
  * The commerce product-sheet rule catalogue — the executable form of
@@ -71,6 +73,6 @@ import { pdpI001 } from "./rules/pdp-i-001.js";
71
73
  export const commerceCatalogue = {
72
74
  vertical: "commerce",
73
75
  releases: KNOWN_RELEASES,
74
- catalogueVersion: "0.17.0",
75
- rules: [pdpE001, pdpE002, pdpE003, pdpE004, pdpE005, pdpE006, pdpE007, pdpE008, pdpE009, pdpE010, pdpE011, pdpE012, pdpE013, pdpE014, pdpE015, pdpE016, pdpE017, pdpE018, pdpE019, pdpE020, pdpE021, pdpW001, pdpW002, pdpW003, pdpW004, pdpW005, pdpW006, pdpW007, pdpW008, pdpW009, pdpW010, pdpW011, pdpW012, pdpW013, pdpW014, pdpW015, pdpW016, pdpW017, pdpW018, pdpW019, pdpW020, pdpW021, pdpW022, pdpW023, pdpW024, pdpW025, pdpW026, pdpW027, pdpW028, pdpW029, pdpW030, pdpW031, pdpW032, pdpW033, pdpW034, pdpW035, pdpW036, pdpW037, pdpW038, pdpW039, pdpW040, pdpI001],
76
+ catalogueVersion: "0.20.0",
77
+ rules: [pdpE001, pdpE002, pdpE003, pdpE004, pdpE005, pdpE006, pdpE007, pdpE008, pdpE009, pdpE010, pdpE011, pdpE012, pdpE013, pdpE014, pdpE015, pdpE016, pdpE017, pdpE018, pdpE019, pdpE020, pdpE021, pdpE022, pdpW001, pdpW002, pdpW003, pdpW004, pdpW005, pdpW006, pdpW007, pdpW008, pdpW009, pdpW010, pdpW011, pdpW012, pdpW013, pdpW014, pdpW015, pdpW016, pdpW017, pdpW018, pdpW019, pdpW020, pdpW021, pdpW022, pdpW023, pdpW024, pdpW025, pdpW026, pdpW027, pdpW028, pdpW029, pdpW030, pdpW031, pdpW032, pdpW033, pdpW034, pdpW035, pdpW036, pdpW037, pdpW038, pdpW039, pdpW040, pdpW041, pdpI001],
76
78
  };
@@ -9,6 +9,7 @@ import { colW002 } from "./rules/col-w-002.js";
9
9
  import { colW004 } from "./rules/col-w-004.js";
10
10
  import { colW005 } from "./rules/col-w-005.js";
11
11
  import { colW006 } from "./rules/col-w-006.js";
12
+ import { colW007 } from "./rules/col-w-007.js";
12
13
  /**
13
14
  * The Commerce Collection Sheet rule catalogue — the executable form
14
15
  * of the "Commerce Collection Sheet rules" section of
@@ -25,6 +26,6 @@ import { colW006 } from "./rules/col-w-006.js";
25
26
  export const collectionCatalogue = {
26
27
  vertical: "commerce",
27
28
  releases: KNOWN_RELEASES,
28
- catalogueVersion: "0.17.0",
29
- rules: [colE001, colE002, colE003, colE004, colE005, colW001, colW002, colW004, colW005, colW006],
29
+ catalogueVersion: "0.20.0",
30
+ rules: [colE001, colE002, colE003, colE004, colE005, colW001, colW002, colW004, colW005, colW006, colW007],
30
31
  };
@@ -10,6 +10,7 @@ import { orgW004 } from "./rules/org-w-004.js";
10
10
  import { orgW005 } from "./rules/org-w-005.js";
11
11
  import { orgW006 } from "./rules/org-w-006.js";
12
12
  import { orgW007 } from "./rules/org-w-007.js";
13
+ import { orgW008 } from "./rules/org-w-008.js";
13
14
  /**
14
15
  * The Commerce Organization Sheet rule catalogue — the executable form
15
16
  * of the "Commerce Organization Sheet rules" section of
@@ -22,6 +23,6 @@ import { orgW007 } from "./rules/org-w-007.js";
22
23
  export const organizationCatalogue = {
23
24
  vertical: "commerce",
24
25
  releases: KNOWN_RELEASES,
25
- catalogueVersion: "0.17.0",
26
- rules: [orgE001, orgE002, orgE003, orgE004, orgW001, orgW002, orgW003, orgW004, orgW005, orgW006, orgW007],
26
+ catalogueVersion: "0.20.0",
27
+ rules: [orgE001, orgE002, orgE003, orgE004, orgW001, orgW002, orgW003, orgW004, orgW005, orgW006, orgW007, orgW008],
27
28
  };
@@ -0,0 +1,13 @@
1
+ import type { Rule } from "@pagefront/lint-core";
2
+ /**
3
+ * COL-W-007 — Parent entry without `pagefront:sheetUrl`.
4
+ * Transcribed from commerce/spec/rules.md (normative).
5
+ *
6
+ * A category's parents are category sheets and a collection's parent
7
+ * is a collection sheet, so the producer that emits the sheet has
8
+ * emitted its parents and knows their addresses. The hint is an
9
+ * optimisation that MAY be stale; the rule asks for its presence and
10
+ * never checks that it resolves. One finding per entry, at the entry's
11
+ * path.
12
+ */
13
+ export declare const colW007: Rule;
@@ -0,0 +1,32 @@
1
+ import { entryName, memberEntries, nonBlank } from "./col-shared.js";
2
+ /**
3
+ * COL-W-007 — Parent entry without `pagefront:sheetUrl`.
4
+ * Transcribed from commerce/spec/rules.md (normative).
5
+ *
6
+ * A category's parents are category sheets and a collection's parent
7
+ * is a collection sheet, so the producer that emits the sheet has
8
+ * emitted its parents and knows their addresses. The hint is an
9
+ * optimisation that MAY be stale; the rule asks for its presence and
10
+ * never checks that it resolves. One finding per entry, at the entry's
11
+ * path.
12
+ */
13
+ export const colW007 = {
14
+ id: "COL-W-007",
15
+ title: "Parent entry without pagefront:sheetUrl",
16
+ severity: "warning",
17
+ target: "$.data.isPartOf",
18
+ check: (match) => {
19
+ const hits = [];
20
+ for (const { entry, path } of memberEntries(match.value, match.path)) {
21
+ if (nonBlank(entry["pagefront:sheetUrl"]))
22
+ continue;
23
+ hits.push({ path, values: { name: entryName(entry) } });
24
+ }
25
+ return hits;
26
+ },
27
+ messageTemplate: "Parent `{name}` at `{path}` carries no `pagefront:sheetUrl`; consumers must reach its sheet via `url` + `alternate`.",
28
+ remediation: "Emit the parent's sheet and add `pagefront:sheetUrl` with its address. A category's parents are category sheets, and a collection nests only in a collection that has a sheet.",
29
+ introduced: "catalogue 0.20.0",
30
+ appliesFrom: "0.5.0",
31
+ specReference: "collection.md#hierarchy",
32
+ };
@@ -0,0 +1,15 @@
1
+ import type { Rule } from "@pagefront/lint-core";
2
+ /**
3
+ * ORG-W-008 — Checkout hosts without seller role, or not a bare host.
4
+ * Transcribed from commerce/spec/rules.md (normative).
5
+ *
6
+ * `pagefront:checkoutHosts` lists the hosts outside the canonical host
7
+ * on which the seller's offers are bought or checked out
8
+ * (organization.md, "Seller block"); it is admitted only where
9
+ * `commerceRole` includes `seller`, and each entry is a bare host name.
10
+ * One finding on the field when the role is absent, and one per entry
11
+ * that is not a bare host: a value that is not a string, is empty or
12
+ * whitespace-only, or carries a scheme, a path or a port. The entries
13
+ * are read as written; the rule compares them with nothing.
14
+ */
15
+ export declare const orgW008: Rule;
@@ -0,0 +1,59 @@
1
+ import { asRecord, commerceRoles } from "./org-shared.js";
2
+ /**
3
+ * ORG-W-008 — Checkout hosts without seller role, or not a bare host.
4
+ * Transcribed from commerce/spec/rules.md (normative).
5
+ *
6
+ * `pagefront:checkoutHosts` lists the hosts outside the canonical host
7
+ * on which the seller's offers are bought or checked out
8
+ * (organization.md, "Seller block"); it is admitted only where
9
+ * `commerceRole` includes `seller`, and each entry is a bare host name.
10
+ * One finding on the field when the role is absent, and one per entry
11
+ * that is not a bare host: a value that is not a string, is empty or
12
+ * whitespace-only, or carries a scheme, a path or a port. The entries
13
+ * are read as written; the rule compares them with nothing.
14
+ */
15
+ export const orgW008 = {
16
+ id: "ORG-W-008",
17
+ title: "Checkout hosts without seller role, or not a bare host",
18
+ severity: "warning",
19
+ target: "$.data",
20
+ check: (match) => {
21
+ const data = asRecord(match.value);
22
+ if (data === null || !Object.prototype.hasOwnProperty.call(data, "pagefront:checkoutHosts"))
23
+ return null;
24
+ const field = `${match.path}['pagefront:checkoutHosts']`;
25
+ const hits = [];
26
+ if (!commerceRoles(data).includes("seller")) {
27
+ hits.push({ path: field, values: { problem: "the sheet does not declare the `seller` role" } });
28
+ }
29
+ const hosts = data["pagefront:checkoutHosts"];
30
+ if (Array.isArray(hosts)) {
31
+ hosts.forEach((entry, i) => {
32
+ const problem = notBareHost(entry);
33
+ if (problem !== null)
34
+ hits.push({ path: `${field}[${i}]`, values: { problem } });
35
+ });
36
+ }
37
+ return hits.length > 0 ? hits : null;
38
+ },
39
+ messageTemplate: "`pagefront:checkoutHosts` at `{path}`: {problem}.",
40
+ remediation: "List bare host names only (`checkout.merchant.example`), and only on the seller's own sheet, one whose `pagefront:commerceRole` includes `seller`; otherwise remove the field.",
41
+ introduced: "release 0.6.0",
42
+ appliesFrom: "0.6.0",
43
+ specReference: "organization.md#seller-block-seller",
44
+ };
45
+ /** Why `entry` is not a bare host, or `null` when it is one. */
46
+ function notBareHost(entry) {
47
+ if (typeof entry !== "string")
48
+ return `entry ${JSON.stringify(entry)} is not a string`;
49
+ if (entry.trim() === "")
50
+ return "an entry is empty or whitespace-only";
51
+ const shown = `entry \`${entry}\``;
52
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(entry))
53
+ return `${shown} carries a scheme`;
54
+ if (entry.includes("/"))
55
+ return `${shown} carries a path`;
56
+ if (entry.includes(":"))
57
+ return `${shown} carries a port`;
58
+ return null;
59
+ }
@@ -0,0 +1,13 @@
1
+ import type { Rule } from "@pagefront/lint-core";
2
+ /**
3
+ * PDP-E-022 — Checkout template on an unavailable offer.
4
+ * Transcribed from commerce/spec/rules.md (normative).
5
+ *
6
+ * An Offer whose `availability` is OutOfStock, SoldOut or Discontinued
7
+ * cannot be bought now, so it MUST NOT carry `checkoutPageURLTemplate`
8
+ * ("Unavailable offers without a price"); `url` stays, since the page
9
+ * is where a shopper asks to be notified. Fires whether or not the
10
+ * offer states a price. Applies from release 0.6.0, whose Product Sheet
11
+ * spec 0.12.0 introduces the requirement.
12
+ */
13
+ export declare const pdpE022: Rule;
@@ -0,0 +1,33 @@
1
+ import { asObject } from "./configurator-pricing.js";
2
+ import { isUnavailable } from "./unavailable-offer.js";
3
+ /**
4
+ * PDP-E-022 — Checkout template on an unavailable offer.
5
+ * Transcribed from commerce/spec/rules.md (normative).
6
+ *
7
+ * An Offer whose `availability` is OutOfStock, SoldOut or Discontinued
8
+ * cannot be bought now, so it MUST NOT carry `checkoutPageURLTemplate`
9
+ * ("Unavailable offers without a price"); `url` stays, since the page
10
+ * is where a shopper asks to be notified. Fires whether or not the
11
+ * offer states a price. Applies from release 0.6.0, whose Product Sheet
12
+ * spec 0.12.0 introduces the requirement.
13
+ */
14
+ export const pdpE022 = {
15
+ id: "PDP-E-022",
16
+ title: "Checkout template on an unavailable offer",
17
+ severity: "error",
18
+ target: "$.data.offers[*]",
19
+ check: (match) => {
20
+ const offer = asObject(match.value);
21
+ if (offer === null || !Object.prototype.hasOwnProperty.call(offer, "checkoutPageURLTemplate"))
22
+ return null;
23
+ if (!isUnavailable(offer))
24
+ return null;
25
+ return { path: `${match.path}['checkoutPageURLTemplate']`, values: { availability: String(offer["availability"]) } };
26
+ },
27
+ messageTemplate: "Checkout template at `{path}` sits on an Offer whose `availability` is `{availability}`, which cannot be bought now.",
28
+ remediation: "Remove the template; keep `url`, the page where a shopper can ask to be notified.",
29
+ introduced: "release 0.6.0",
30
+ // The requirement is introduced by release 0.6.0 (Product Sheet spec 0.12.0).
31
+ appliesFrom: "0.6.0",
32
+ specReference: "product.md#tax-inclusion",
33
+ };
@@ -5,5 +5,9 @@ import type { Rule } from "@pagefront/lint-core";
5
5
  *
6
6
  * Bespoke cross-field check: `availability` and `priceCurrency` present,
7
7
  * but no `price`, no `priceSpecification`, no `pagefront:priceOnRequest`.
8
+ * Silent when `availability` is OutOfStock, SoldOut or Discontinued: an
9
+ * offer the page shows as unavailable is published without a price
10
+ * ("Unavailable offers without a price"), so the missing price is no
11
+ * sign of an extraction failure.
8
12
  */
9
13
  export declare const pdpW016: Rule;
@@ -1,9 +1,14 @@
1
+ import { isUnavailable } from "./unavailable-offer.js";
1
2
  /**
2
3
  * PDP-W-016 — Possibly-missed price.
3
4
  * Transcribed from commerce/spec/rules.md (normative).
4
5
  *
5
6
  * Bespoke cross-field check: `availability` and `priceCurrency` present,
6
7
  * but no `price`, no `priceSpecification`, no `pagefront:priceOnRequest`.
8
+ * Silent when `availability` is OutOfStock, SoldOut or Discontinued: an
9
+ * offer the page shows as unavailable is published without a price
10
+ * ("Unavailable offers without a price"), so the missing price is no
11
+ * sign of an extraction failure.
7
12
  */
8
13
  export const pdpW016 = {
9
14
  id: "PDP-W-016",
@@ -15,6 +20,8 @@ export const pdpW016 = {
15
20
  if (typeof offer !== "object" || offer === null)
16
21
  return null;
17
22
  const o = offer;
23
+ if (isUnavailable(o))
24
+ return null;
18
25
  return o["availability"] !== undefined &&
19
26
  o["priceCurrency"] !== undefined &&
20
27
  o["price"] === undefined &&
@@ -0,0 +1,15 @@
1
+ import type { Rule } from "@pagefront/lint-core";
2
+ /**
3
+ * PDP-W-041 — Purchase URL off the canonical host.
4
+ * Transcribed from commerce/spec/rules.md (normative).
5
+ *
6
+ * A purchase URL is an Offer's `url`, or its `checkoutPageURLTemplate`
7
+ * expanded with `quantity` set to 1 ("Where to buy", "Purchase host").
8
+ * Fires once for each whose host differs from the canonical host, the
9
+ * host of the envelope `canonical_url`. A `url` with no host is
10
+ * PDP-E-021's concern and a template that does not expand to an
11
+ * absolute https URL is PDP-E-019's; both are skipped here. The seller
12
+ * sheet's `pagefront:checkoutHosts`, which can declare the host, is out
13
+ * of the linter's sight: it never fetches. Applies from release 0.6.0.
14
+ */
15
+ export declare const pdpW041: Rule;
@@ -0,0 +1,53 @@
1
+ import { asObject, own } from "./configurator-pricing.js";
2
+ import { checkoutTemplateProblem, expandTemplate } from "./checkout-template.js";
3
+ import { hostOf, renderHost } from "./sheet-url-host.js";
4
+ /**
5
+ * PDP-W-041 — Purchase URL off the canonical host.
6
+ * Transcribed from commerce/spec/rules.md (normative).
7
+ *
8
+ * A purchase URL is an Offer's `url`, or its `checkoutPageURLTemplate`
9
+ * expanded with `quantity` set to 1 ("Where to buy", "Purchase host").
10
+ * Fires once for each whose host differs from the canonical host, the
11
+ * host of the envelope `canonical_url`. A `url` with no host is
12
+ * PDP-E-021's concern and a template that does not expand to an
13
+ * absolute https URL is PDP-E-019's; both are skipped here. The seller
14
+ * sheet's `pagefront:checkoutHosts`, which can declare the host, is out
15
+ * of the linter's sight: it never fetches. Applies from release 0.6.0.
16
+ */
17
+ export const pdpW041 = {
18
+ id: "PDP-W-041",
19
+ title: "Purchase URL off the canonical host",
20
+ severity: "warning",
21
+ target: "$.data.offers[*]",
22
+ check: (match, ctx) => {
23
+ const offer = asObject(match.value);
24
+ if (offer === null)
25
+ return null;
26
+ const canonicalHost = hostOf(ctx.sheet.canonical_url);
27
+ const hits = [];
28
+ const urlHost = hostOf(own(offer, "url"));
29
+ if (urlHost !== null && urlHost !== canonicalHost) {
30
+ hits.push({
31
+ path: `${match.path}['url']`,
32
+ values: { purchaseHost: urlHost, host: renderHost(canonicalHost) },
33
+ });
34
+ }
35
+ const template = own(offer, "checkoutPageURLTemplate");
36
+ if (typeof template === "string" && checkoutTemplateProblem(template) === null) {
37
+ const templateHost = hostOf(expandTemplate(template, { quantity: "1" }));
38
+ if (templateHost !== null && templateHost !== canonicalHost) {
39
+ hits.push({
40
+ path: `${match.path}['checkoutPageURLTemplate']`,
41
+ values: { purchaseHost: templateHost, host: renderHost(canonicalHost) },
42
+ });
43
+ }
44
+ }
45
+ return hits.length > 0 ? hits : null;
46
+ },
47
+ messageTemplate: "Purchase URL at `{path}` is on host `{purchaseHost}`; the canonical page is on `{host}`.",
48
+ remediation: "Move the purchase to the canonical host, or declare the host in `pagefront:checkoutHosts` on the seller's organization sheet.",
49
+ introduced: "release 0.6.0",
50
+ // The purchase-host rule is introduced by release 0.6.0 (Product Sheet spec 0.12.0).
51
+ appliesFrom: "0.6.0",
52
+ specReference: "product.md#where-to-buy",
53
+ };
@@ -0,0 +1,2 @@
1
+ /** True when the offer's `availability` is one of the three unavailable values. */
2
+ export declare function isUnavailable(offer: Record<string, unknown>): boolean;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Shared helper for the rules that read an Offer's availability as
3
+ * "cannot be bought now" (product.md, "Unavailable offers without a
4
+ * price"): OutOfStock, SoldOut and Discontinued, in the http or https
5
+ * form the schema accepts. The other availability values describe an
6
+ * item that can still be bought.
7
+ */
8
+ const UNAVAILABLE = /^https?:\/\/schema\.org\/(OutOfStock|SoldOut|Discontinued)$/;
9
+ /** True when the offer's `availability` is one of the three unavailable values. */
10
+ export function isUnavailable(offer) {
11
+ const availability = offer["availability"];
12
+ return typeof availability === "string" && UNAVAILABLE.test(availability);
13
+ }
package/dist/version.d.ts CHANGED
@@ -5,4 +5,4 @@
5
5
  * (test/linter-version.test.mjs) fails when they diverge. Regenerate
6
6
  * on version bump.
7
7
  */
8
- export declare const PACKAGE_VERSION = "0.13.0";
8
+ export declare const PACKAGE_VERSION = "0.14.0";
package/dist/version.js CHANGED
@@ -5,4 +5,4 @@
5
5
  * (test/linter-version.test.mjs) fails when they diverge. Regenerate
6
6
  * on version bump.
7
7
  */
8
- export const PACKAGE_VERSION = "0.13.0";
8
+ export const PACKAGE_VERSION = "0.14.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagefront/lint-commerce",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Pagefront commerce linter: the commerce rule catalogue and CLI, on the @pagefront/lint-core engine.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {