@pagefront/lint-commerce 0.10.0 → 0.12.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 +49 -16
- package/dist/catalogue.js +4 -2
- package/dist/generated/release-manifest.d.ts +30 -0
- package/dist/generated/release-manifest.js +36 -0
- package/dist/generated/schema-ids.d.ts +1 -0
- package/dist/generated/schema-ids.js +9 -0
- package/dist/generated/schema-properties.d.ts +1 -0
- package/dist/{schema-properties.js → generated/schema-properties.js} +2 -10
- package/dist/generated/schemas.d.ts +1 -0
- package/dist/generated/schemas.js +7326 -0
- package/dist/index.d.ts +8 -5
- package/dist/index.js +8 -5
- package/dist/organization-catalogue.js +3 -2
- package/dist/releases.d.ts +12 -26
- package/dist/releases.js +16 -30
- package/dist/rules/org-e-001.js +2 -0
- package/dist/rules/org-e-002.js +2 -0
- package/dist/rules/org-e-003.js +2 -0
- package/dist/rules/org-e-004.d.ts +11 -0
- package/dist/rules/org-e-004.js +18 -0
- package/dist/rules/org-w-001.js +2 -0
- package/dist/rules/org-w-002.js +2 -0
- package/dist/rules/org-w-003.js +2 -0
- package/dist/rules/org-w-004.js +2 -0
- package/dist/rules/org-w-005.js +2 -0
- package/dist/rules/org-w-006.js +2 -0
- package/dist/rules/pdp-e-001.js +2 -0
- package/dist/rules/pdp-e-002.js +2 -0
- package/dist/rules/pdp-e-003.js +2 -0
- package/dist/rules/pdp-e-004.js +2 -0
- package/dist/rules/pdp-e-005.js +2 -0
- package/dist/rules/pdp-e-006.js +2 -0
- package/dist/rules/pdp-e-007.js +2 -0
- package/dist/rules/pdp-e-008.js +2 -0
- package/dist/rules/pdp-e-009.js +2 -0
- package/dist/rules/pdp-e-010.js +2 -0
- package/dist/rules/pdp-e-011.js +2 -0
- package/dist/rules/pdp-e-012.js +2 -0
- package/dist/rules/pdp-e-013.js +2 -0
- package/dist/rules/pdp-e-014.js +2 -0
- package/dist/rules/pdp-e-015.js +2 -0
- package/dist/rules/pdp-e-016.js +2 -0
- package/dist/rules/pdp-e-017.d.ts +2 -0
- package/dist/rules/pdp-e-017.js +80 -0
- package/dist/rules/pdp-e-018.d.ts +11 -0
- package/dist/rules/pdp-e-018.js +18 -0
- package/dist/rules/pdp-i-001.js +5 -2
- package/dist/rules/pdp-w-001.js +2 -0
- package/dist/rules/pdp-w-002.js +2 -0
- package/dist/rules/pdp-w-003.js +2 -0
- package/dist/rules/pdp-w-004.js +2 -0
- package/dist/rules/pdp-w-005.js +2 -0
- package/dist/rules/pdp-w-006.js +2 -0
- package/dist/rules/pdp-w-007.js +2 -0
- package/dist/rules/pdp-w-008.js +2 -0
- package/dist/rules/pdp-w-009.js +2 -0
- package/dist/rules/pdp-w-010.js +2 -0
- package/dist/rules/pdp-w-011.js +2 -0
- package/dist/rules/pdp-w-012.js +2 -0
- package/dist/rules/pdp-w-013.js +2 -0
- package/dist/rules/pdp-w-014.js +2 -0
- package/dist/rules/pdp-w-015.js +2 -0
- package/dist/rules/pdp-w-016.js +2 -0
- package/dist/rules/pdp-w-017.js +2 -0
- package/dist/rules/pdp-w-018.js +2 -0
- package/dist/rules/pdp-w-019.js +2 -0
- package/dist/rules/pdp-w-020.js +2 -0
- package/dist/rules/pdp-w-021.js +2 -0
- package/dist/rules/pdp-w-022.js +2 -0
- package/dist/rules/pdp-w-023.js +2 -0
- package/dist/rules/pdp-w-024.js +2 -0
- package/dist/rules/pdp-w-025.js +2 -0
- package/dist/rules/pdp-w-026.js +2 -0
- package/dist/rules/pdp-w-027.js +2 -0
- package/dist/rules/pdp-w-028.js +2 -0
- package/dist/rules/pdp-w-029.js +2 -0
- package/dist/rules/pdp-w-030.js +2 -0
- package/dist/rules/pdp-w-031.js +2 -0
- package/dist/rules/pdp-w-032.js +2 -0
- package/dist/rules/pdp-w-033.js +2 -0
- package/dist/rules/pdp-w-034.js +2 -0
- package/dist/rules/pdp-w-035.js +2 -0
- package/dist/rules/pdp-w-036.js +2 -0
- package/dist/rules/pdp-w-037.js +2 -0
- package/dist/rules/pdp-w-038.js +2 -0
- package/dist/rules/pdp-w-039.js +2 -0
- package/dist/rules/release-schema.d.ts +18 -7
- package/dist/rules/release-schema.js +55 -15
- package/dist/schema-registry.d.ts +42 -0
- package/dist/schema-registry.js +52 -0
- package/dist/schema-validators.d.ts +7 -8
- package/dist/schema-validators.js +18 -4
- package/dist/validate-and-lint.d.ts +28 -21
- package/dist/validate-and-lint.js +60 -36
- package/dist/validate-organization.d.ts +5 -5
- package/dist/validate-organization.js +8 -18
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/dist/legacy-schemas.d.ts +0 -9
- package/dist/legacy-schemas.js +0 -2446
- package/dist/organization-schema.d.ts +0 -10
- package/dist/organization-schema.js +0 -443
- package/dist/product-schema.d.ts +0 -9
- package/dist/product-schema.js +0 -2015
- package/dist/schema-properties.d.ts +0 -10
package/dist/index.d.ts
CHANGED
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
* `validateOrganizationSheet` (Layer 1 for the Commerce Organization
|
|
8
8
|
* Sheet; no organization catalogue exists yet), the commerce
|
|
9
9
|
* catalogue object, and the types those signatures mention.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* (`RELEASE_MANIFEST`, `sheetSpecVersionFor`)
|
|
13
|
-
*
|
|
10
|
+
* The bundled schemas (`generated/`), the compiled validators and the
|
|
11
|
+
* individual rule modules are internal. The release manifest
|
|
12
|
+
* (`RELEASE_MANIFEST`, `sheetSpecVersionFor`, `schemaUrlFor`) and the
|
|
13
|
+
* list of bundled schema ids (`BUNDLED_SCHEMA_IDS`) are exported so
|
|
14
|
+
* callers can resolve a release to its sheet-spec versions and tell
|
|
15
|
+
* which `$schema` values this package validates.
|
|
14
16
|
*/
|
|
15
17
|
export { lintSheet } from "./lint-sheet.js";
|
|
16
18
|
export { validateAndLint } from "./validate-and-lint.js";
|
|
@@ -21,5 +23,6 @@ export { commerceCatalogue } from "./catalogue.js";
|
|
|
21
23
|
export { organizationCatalogue } from "./organization-catalogue.js";
|
|
22
24
|
export { detectSheetType, detectSheetLine } from "./sheet-type.js";
|
|
23
25
|
export type { SheetType, SheetLine } from "./sheet-type.js";
|
|
24
|
-
export { RELEASE_MANIFEST, KNOWN_RELEASES, sheetSpecVersionFor } from "./releases.js";
|
|
26
|
+
export { RELEASE_MANIFEST, KNOWN_RELEASES, CURRENT_RELEASE, sheetSpecVersionFor, schemaUrlFor, } from "./releases.js";
|
|
27
|
+
export { SCHEMA_IDS as BUNDLED_SCHEMA_IDS } from "./generated/schema-ids.js";
|
|
25
28
|
export type { Report, Finding, Summary, Severity, LintOptions, Sheet, } from "@pagefront/lint-core";
|
package/dist/index.js
CHANGED
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
* `validateOrganizationSheet` (Layer 1 for the Commerce Organization
|
|
8
8
|
* Sheet; no organization catalogue exists yet), the commerce
|
|
9
9
|
* catalogue object, and the types those signatures mention.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* (`RELEASE_MANIFEST`, `sheetSpecVersionFor`)
|
|
13
|
-
*
|
|
10
|
+
* The bundled schemas (`generated/`), the compiled validators and the
|
|
11
|
+
* individual rule modules are internal. The release manifest
|
|
12
|
+
* (`RELEASE_MANIFEST`, `sheetSpecVersionFor`, `schemaUrlFor`) and the
|
|
13
|
+
* list of bundled schema ids (`BUNDLED_SCHEMA_IDS`) are exported so
|
|
14
|
+
* callers can resolve a release to its sheet-spec versions and tell
|
|
15
|
+
* which `$schema` values this package validates.
|
|
14
16
|
*/
|
|
15
17
|
export { lintSheet } from "./lint-sheet.js";
|
|
16
18
|
export { validateAndLint } from "./validate-and-lint.js";
|
|
@@ -18,4 +20,5 @@ export { validateOrganizationSheet } from "./validate-organization.js";
|
|
|
18
20
|
export { commerceCatalogue } from "./catalogue.js";
|
|
19
21
|
export { organizationCatalogue } from "./organization-catalogue.js";
|
|
20
22
|
export { detectSheetType, detectSheetLine } from "./sheet-type.js";
|
|
21
|
-
export { RELEASE_MANIFEST, KNOWN_RELEASES, sheetSpecVersionFor } from "./releases.js";
|
|
23
|
+
export { RELEASE_MANIFEST, KNOWN_RELEASES, CURRENT_RELEASE, sheetSpecVersionFor, schemaUrlFor, } from "./releases.js";
|
|
24
|
+
export { SCHEMA_IDS as BUNDLED_SCHEMA_IDS } from "./generated/schema-ids.js";
|
|
@@ -2,6 +2,7 @@ import { KNOWN_RELEASES } from "./releases.js";
|
|
|
2
2
|
import { orgE001 } from "./rules/org-e-001.js";
|
|
3
3
|
import { orgE002 } from "./rules/org-e-002.js";
|
|
4
4
|
import { orgE003 } from "./rules/org-e-003.js";
|
|
5
|
+
import { orgE004 } from "./rules/org-e-004.js";
|
|
5
6
|
import { orgW001 } from "./rules/org-w-001.js";
|
|
6
7
|
import { orgW002 } from "./rules/org-w-002.js";
|
|
7
8
|
import { orgW003 } from "./rules/org-w-003.js";
|
|
@@ -20,6 +21,6 @@ import { orgW006 } from "./rules/org-w-006.js";
|
|
|
20
21
|
export const organizationCatalogue = {
|
|
21
22
|
vertical: "commerce",
|
|
22
23
|
releases: KNOWN_RELEASES,
|
|
23
|
-
catalogueVersion: "0.
|
|
24
|
-
rules: [orgE001, orgE002, orgE003, orgW001, orgW002, orgW003, orgW004, orgW005, orgW006],
|
|
24
|
+
catalogueVersion: "0.16.0",
|
|
25
|
+
rules: [orgE001, orgE002, orgE003, orgE004, orgW001, orgW002, orgW003, orgW004, orgW005, orgW006],
|
|
25
26
|
};
|
package/dist/releases.d.ts
CHANGED
|
@@ -1,36 +1,22 @@
|
|
|
1
|
+
import { RELEASE_MANIFEST } from "./generated/release-manifest.js";
|
|
1
2
|
import type { SheetType } from "./sheet-type.js";
|
|
2
3
|
/**
|
|
3
|
-
* The commerce release manifest (commerce/spec/releases.json)
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* The commerce release manifest (commerce/spec/releases.json): each
|
|
5
|
+
* release and the member versions it bundles. Bundled at build time by
|
|
6
|
+
* scripts/bundle-schemas.mjs, so the package always carries the
|
|
7
|
+
* manifest it was built from.
|
|
7
8
|
*/
|
|
8
|
-
export
|
|
9
|
-
readonly $comment: "The commerce release manifest: each release and the member versions it bundles. Consumed by the linter; the human-readable form is releases.md.";
|
|
10
|
-
readonly vertical: "commerce";
|
|
11
|
-
readonly schema_url_template: "https://themachineweb.org/pagefront/spec/commerce/{sheet}/v{version}.json";
|
|
12
|
-
readonly releases: readonly [{
|
|
13
|
-
readonly release: "0.3.0";
|
|
14
|
-
readonly date: "2026-10-01";
|
|
15
|
-
readonly vocabulary: "0.3.0";
|
|
16
|
-
readonly sheets: {
|
|
17
|
-
readonly product: "0.9.0";
|
|
18
|
-
readonly organization: "0.3.0";
|
|
19
|
-
};
|
|
20
|
-
}];
|
|
21
|
-
readonly legacy: {
|
|
22
|
-
readonly envelope_field: "format_version";
|
|
23
|
-
readonly format_versions: readonly ["0.9"];
|
|
24
|
-
readonly schemas: {
|
|
25
|
-
readonly product: "https://themachineweb.org/pagefront/spec/commerce/product/v0.9.json";
|
|
26
|
-
readonly organization: "https://themachineweb.org/pagefront/spec/commerce/organization/v0.9.json";
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
};
|
|
9
|
+
export { RELEASE_MANIFEST };
|
|
30
10
|
/** Every release the manifest lists, in manifest order. */
|
|
31
11
|
export declare const KNOWN_RELEASES: readonly string[];
|
|
12
|
+
/** The latest release the manifest lists. */
|
|
13
|
+
export declare const CURRENT_RELEASE: string;
|
|
32
14
|
/**
|
|
33
15
|
* The sheet-spec version a release bundles for a sheet type, or
|
|
34
16
|
* undefined when the manifest does not list the release.
|
|
35
17
|
*/
|
|
36
18
|
export declare function sheetSpecVersionFor(release: string, sheetType: SheetType): string | undefined;
|
|
19
|
+
/** The published URL of a sheet spec's JSON Schema, which is also its `$id`. */
|
|
20
|
+
export declare function schemaUrlFor(sheetType: SheetType, version: string): string;
|
|
21
|
+
/** The legacy draft line's schema URL for a sheet type. */
|
|
22
|
+
export declare function legacySchemaUrl(sheetType: SheetType): string;
|
package/dist/releases.js
CHANGED
|
@@ -1,37 +1,15 @@
|
|
|
1
|
+
import { RELEASE_MANIFEST } from "./generated/release-manifest.js";
|
|
1
2
|
/**
|
|
2
|
-
* The commerce release manifest (commerce/spec/releases.json)
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* The commerce release manifest (commerce/spec/releases.json): each
|
|
4
|
+
* release and the member versions it bundles. Bundled at build time by
|
|
5
|
+
* scripts/bundle-schemas.mjs, so the package always carries the
|
|
6
|
+
* manifest it was built from.
|
|
6
7
|
*/
|
|
7
|
-
export
|
|
8
|
-
"$comment": "The commerce release manifest: each release and the member versions it bundles. Consumed by the linter; the human-readable form is releases.md.",
|
|
9
|
-
"vertical": "commerce",
|
|
10
|
-
"schema_url_template": "https://themachineweb.org/pagefront/spec/commerce/{sheet}/v{version}.json",
|
|
11
|
-
"releases": [
|
|
12
|
-
{
|
|
13
|
-
"release": "0.3.0",
|
|
14
|
-
"date": "2026-10-01",
|
|
15
|
-
"vocabulary": "0.3.0",
|
|
16
|
-
"sheets": {
|
|
17
|
-
"product": "0.9.0",
|
|
18
|
-
"organization": "0.3.0"
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
],
|
|
22
|
-
"legacy": {
|
|
23
|
-
"envelope_field": "format_version",
|
|
24
|
-
"format_versions": [
|
|
25
|
-
"0.9"
|
|
26
|
-
],
|
|
27
|
-
"schemas": {
|
|
28
|
-
"product": "https://themachineweb.org/pagefront/spec/commerce/product/v0.9.json",
|
|
29
|
-
"organization": "https://themachineweb.org/pagefront/spec/commerce/organization/v0.9.json"
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
};
|
|
8
|
+
export { RELEASE_MANIFEST };
|
|
33
9
|
/** Every release the manifest lists, in manifest order. */
|
|
34
10
|
export const KNOWN_RELEASES = RELEASE_MANIFEST.releases.map((r) => r.release);
|
|
11
|
+
/** The latest release the manifest lists. */
|
|
12
|
+
export const CURRENT_RELEASE = KNOWN_RELEASES[KNOWN_RELEASES.length - 1];
|
|
35
13
|
/**
|
|
36
14
|
* The sheet-spec version a release bundles for a sheet type, or
|
|
37
15
|
* undefined when the manifest does not list the release.
|
|
@@ -39,3 +17,11 @@ export const KNOWN_RELEASES = RELEASE_MANIFEST.releases.map((r) => r.release);
|
|
|
39
17
|
export function sheetSpecVersionFor(release, sheetType) {
|
|
40
18
|
return RELEASE_MANIFEST.releases.find((r) => r.release === release)?.sheets[sheetType];
|
|
41
19
|
}
|
|
20
|
+
/** The published URL of a sheet spec's JSON Schema, which is also its `$id`. */
|
|
21
|
+
export function schemaUrlFor(sheetType, version) {
|
|
22
|
+
return RELEASE_MANIFEST.schema_url_template.replace("{sheet}", sheetType).replace("{version}", version);
|
|
23
|
+
}
|
|
24
|
+
/** The legacy draft line's schema URL for a sheet type. */
|
|
25
|
+
export function legacySchemaUrl(sheetType) {
|
|
26
|
+
return RELEASE_MANIFEST.legacy.schemas[sheetType];
|
|
27
|
+
}
|
package/dist/rules/org-e-001.js
CHANGED
|
@@ -13,5 +13,7 @@ export const orgE001 = {
|
|
|
13
13
|
messageTemplate: "The Sheet uses `{term}` but `@context` at `{path}` does not declare the `pagefront` prefix, so the term does not resolve to the Pagefront vocabulary.",
|
|
14
14
|
remediation: 'Declare the prefix: `"@context": ["https://schema.org", {"pagefront": "https://themachineweb.org/pagefront/vocabulary/"}]`.',
|
|
15
15
|
introduced: "v0.9",
|
|
16
|
+
appliesFrom: "0.3.0",
|
|
17
|
+
appliesToLegacy: true,
|
|
16
18
|
specReference: "vocabulary.md#how-to-use-this-vocabulary",
|
|
17
19
|
};
|
package/dist/rules/org-e-002.js
CHANGED
package/dist/rules/org-e-003.js
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Rule } from "@pagefront/lint-core";
|
|
2
|
+
/**
|
|
3
|
+
* ORG-E-004 — Unknown release.
|
|
4
|
+
* Transcribed from commerce/spec/rules.md (normative); the check is
|
|
5
|
+
* shared between the two catalogues (release-schema.ts).
|
|
6
|
+
*
|
|
7
|
+
* `appliesFrom` is "0.0.0": the rule judges the declared release
|
|
8
|
+
* itself, so it cannot be gated by it. It never fires on a sheet of a
|
|
9
|
+
* published release.
|
|
10
|
+
*/
|
|
11
|
+
export declare const orgE004: Rule;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { UNKNOWN_RELEASE, unknownRelease } from "./release-schema.js";
|
|
2
|
+
/**
|
|
3
|
+
* ORG-E-004 — Unknown release.
|
|
4
|
+
* Transcribed from commerce/spec/rules.md (normative); the check is
|
|
5
|
+
* shared between the two catalogues (release-schema.ts).
|
|
6
|
+
*
|
|
7
|
+
* `appliesFrom` is "0.0.0": the rule judges the declared release
|
|
8
|
+
* itself, so it cannot be gated by it. It never fires on a sheet of a
|
|
9
|
+
* published release.
|
|
10
|
+
*/
|
|
11
|
+
export const orgE004 = {
|
|
12
|
+
id: "ORG-E-004",
|
|
13
|
+
...UNKNOWN_RELEASE,
|
|
14
|
+
check: unknownRelease,
|
|
15
|
+
introduced: "release 0.4.0",
|
|
16
|
+
appliesFrom: "0.0.0",
|
|
17
|
+
specReference: "organization.md#the-envelope",
|
|
18
|
+
};
|
package/dist/rules/org-w-001.js
CHANGED
|
@@ -27,5 +27,7 @@ export const orgW001 = {
|
|
|
27
27
|
messageTemplate: "`pagefront:commerceRole` at `{path}` declares `brand_owner`, but the sheet carries no `brand` node.",
|
|
28
28
|
remediation: "Add the `Brand` node with `@id` and `name` — the `Brand.@id` is the identity Product Sheets resolve via `Product.brand`.",
|
|
29
29
|
introduced: "v0.9",
|
|
30
|
+
appliesFrom: "0.3.0",
|
|
31
|
+
appliesToLegacy: true,
|
|
30
32
|
specReference: "organization.md#role-discriminator--pagefrontcommercerole",
|
|
31
33
|
};
|
package/dist/rules/org-w-002.js
CHANGED
|
@@ -28,5 +28,7 @@ export const orgW002 = {
|
|
|
28
28
|
messageTemplate: "`pagefront:commerceRole` at `{path}` declares `seller`, but none of `hasMerchantReturnPolicy`, `pagefront:fulfillmentTerms`, `paymentAccepted`, or `location` is present.",
|
|
29
29
|
remediation: "Add the store-wide terms the seller role promises: the return policy, fulfillment terms, payment methods, or physical locations.",
|
|
30
30
|
introduced: "v0.9",
|
|
31
|
+
appliesFrom: "0.3.0",
|
|
32
|
+
appliesToLegacy: true,
|
|
31
33
|
specReference: "organization.md#seller-block-seller",
|
|
32
34
|
};
|
package/dist/rules/org-w-003.js
CHANGED
|
@@ -26,5 +26,7 @@ export const orgW003 = {
|
|
|
26
26
|
messageTemplate: "A `brand` node is present at `{path}`, but `pagefront:commerceRole` does not declare `brand_owner`.",
|
|
27
27
|
remediation: "Declare `brand_owner` in `pagefront:commerceRole`, or remove the `brand` node if this sheet is not the brand's sheet.",
|
|
28
28
|
introduced: "v0.9",
|
|
29
|
+
appliesFrom: "0.3.0",
|
|
30
|
+
appliesToLegacy: true,
|
|
29
31
|
specReference: "organization.md#role-discriminator--pagefrontcommercerole",
|
|
30
32
|
};
|
package/dist/rules/org-w-004.js
CHANGED
|
@@ -28,5 +28,7 @@ export const orgW004 = {
|
|
|
28
28
|
messageTemplate: "Seller content (`{fields}`) is present at `{path}`, but `pagefront:commerceRole` does not declare `seller`.",
|
|
29
29
|
remediation: "Declare `seller` in `pagefront:commerceRole`, or move the store-wide terms to the seller's own sheet.",
|
|
30
30
|
introduced: "v0.9",
|
|
31
|
+
appliesFrom: "0.3.0",
|
|
32
|
+
appliesToLegacy: true,
|
|
31
33
|
specReference: "organization.md#seller-block-seller",
|
|
32
34
|
};
|
package/dist/rules/org-w-005.js
CHANGED
|
@@ -50,5 +50,7 @@ export const orgW005 = {
|
|
|
50
50
|
messageTemplate: "`{field}` at `{path}` carries `{value}`, which does not match the {form}.",
|
|
51
51
|
remediation: "Correct the machine key: `iso6523Code` is `NNNN:register-number` (e.g. `0002:808641435` for a SIREN), `leiCode` is a 20-character ISO 17442 LEI. If no well-formed machine key exists for this register, drop the field and keep the display-form `identifier` — absence is conformant.",
|
|
52
52
|
introduced: "v0.9",
|
|
53
|
+
appliesFrom: "0.3.0",
|
|
54
|
+
appliesToLegacy: true,
|
|
53
55
|
specReference: "organization.md#company-registration-identity",
|
|
54
56
|
};
|
package/dist/rules/org-w-006.js
CHANGED
|
@@ -28,5 +28,7 @@ export const orgW006 = {
|
|
|
28
28
|
messageTemplate: "`pagefront:authorizedResellers` is present at `{path}`, but `pagefront:commerceRole` does not declare `brand_owner`.",
|
|
29
29
|
remediation: "Declare `brand_owner` in `pagefront:commerceRole` if this is the brand's own sheet; otherwise remove the field — only the brand's sheet can state whom the brand authorizes.",
|
|
30
30
|
introduced: "v0.9",
|
|
31
|
+
appliesFrom: "0.3.0",
|
|
32
|
+
appliesToLegacy: true,
|
|
31
33
|
specReference: "organization.md#authorized-resellers",
|
|
32
34
|
};
|
package/dist/rules/pdp-e-001.js
CHANGED
|
@@ -47,5 +47,7 @@ export const pdpE001 = {
|
|
|
47
47
|
messageTemplate: "Reference `{value}` at `{path}` does not resolve to any entity in this Sheet.",
|
|
48
48
|
remediation: "Either correct the reference to match an existing `@id`, or add the referenced entity to the Sheet. Same-document references (fragments, bare tokens, or full URLs on this Sheet's own base) must resolve within the Sheet, by exact string match. A pricing block's `configurator.@id` must equal the `@id` declared on the Sheet's `pagefront:configurator`. A full-URL `@id` on another document's base in `isVariantOf` or a `hasVariant` entry is a legitimate cross-Sheet reference (multi-Sheet variant family) and is validated for URL shape only — see PDP-W-021 for the traversal-hint recommendation that accompanies it.",
|
|
49
49
|
introduced: "v0.6",
|
|
50
|
+
appliesFrom: "0.3.0",
|
|
51
|
+
appliesToLegacy: true,
|
|
50
52
|
specReference: "product.md#itemoffered-references",
|
|
51
53
|
};
|
package/dist/rules/pdp-e-002.js
CHANGED
package/dist/rules/pdp-e-003.js
CHANGED
|
@@ -13,5 +13,7 @@ export const pdpE003 = {
|
|
|
13
13
|
messageTemplate: "The Sheet uses `{term}` but `@context` at `{path}` does not declare the `pagefront` prefix, so the term does not resolve to the Pagefront vocabulary.",
|
|
14
14
|
remediation: 'Declare the prefix: `"@context": ["https://schema.org", {"pagefront": "https://themachineweb.org/pagefront/vocabulary/"}]`.',
|
|
15
15
|
introduced: "v0.9",
|
|
16
|
+
appliesFrom: "0.3.0",
|
|
17
|
+
appliesToLegacy: true,
|
|
16
18
|
specReference: "vocabulary.md#how-to-use-this-vocabulary",
|
|
17
19
|
};
|
package/dist/rules/pdp-e-004.js
CHANGED
|
@@ -43,5 +43,7 @@ export const pdpE004 = {
|
|
|
43
43
|
messageTemplate: "Price specification at `{path}` states `{specPrice}` but its Offer's `price` is `{price}`.",
|
|
44
44
|
remediation: "Make the two amounts equal, or remove the duplicated amount from the specification entry and keep `valueAddedTaxIncluded` (and `priceCurrency`) on it.",
|
|
45
45
|
introduced: "v0.9",
|
|
46
|
+
appliesFrom: "0.3.0",
|
|
47
|
+
appliesToLegacy: true,
|
|
46
48
|
specReference: "product.md#tax-inclusion",
|
|
47
49
|
};
|
package/dist/rules/pdp-e-005.js
CHANGED
|
@@ -36,5 +36,7 @@ export const pdpE005 = {
|
|
|
36
36
|
messageTemplate: "Seller sheet URL at `{path}` is on host `{sheetUrlHost}`; the product sheet is on `{sheetHost}`.",
|
|
37
37
|
remediation: "Publish the seller's organization sheet on the storefront host.",
|
|
38
38
|
introduced: "v0.9",
|
|
39
|
+
appliesFrom: "0.3.0",
|
|
40
|
+
appliesToLegacy: true,
|
|
39
41
|
specReference: "organization.md#linking-contract-identity-only",
|
|
40
42
|
};
|
package/dist/rules/pdp-e-006.js
CHANGED
|
@@ -32,5 +32,7 @@ export const pdpE006 = {
|
|
|
32
32
|
messageTemplate: "Brand sheet URL at `{path}` is on host `{sheetUrlHost}`; the brand's `@id` is on `{idHost}`.",
|
|
33
33
|
remediation: "Point `pagefront:sheetUrl` at the sheet the brand publishes on its own host, or remove it.",
|
|
34
34
|
introduced: "v0.9",
|
|
35
|
+
appliesFrom: "0.3.0",
|
|
36
|
+
appliesToLegacy: true,
|
|
35
37
|
specReference: "organization.md#linking-contract-identity-only",
|
|
36
38
|
};
|
package/dist/rules/pdp-e-007.js
CHANGED
|
@@ -49,5 +49,7 @@ export const pdpE007 = {
|
|
|
49
49
|
messageTemplate: "Base configuration at `{path}` {problem}.",
|
|
50
50
|
remediation: "Name exactly one option for every priced dimension, spelled as the configurator's `options` spell it. If a dimension never changes the price, declare `affectsPrice: false` on it and leave it out.",
|
|
51
51
|
introduced: "v0.9 (2026-10-01 revision)",
|
|
52
|
+
appliesFrom: "0.3.0",
|
|
53
|
+
appliesToLegacy: true,
|
|
52
54
|
specReference: "product.md#configurator-pricing-and-price-bands",
|
|
53
55
|
};
|
package/dist/rules/pdp-e-008.js
CHANGED
|
@@ -37,5 +37,7 @@ export const pdpE008 = {
|
|
|
37
37
|
messageTemplate: "Modifier for base option `{option}` of `{dimension}` at `{path}` is `{modifier}`; the base option's modifier is `0`.",
|
|
38
38
|
remediation: "Set the base option's modifier to zero and express the other options of the dimension as differences from it. If the modifier is right, `basePrice` is the total of another configuration: correct `baseConfiguration` or `basePrice`.",
|
|
39
39
|
introduced: "v0.9 (2026-10-01 revision)",
|
|
40
|
+
appliesFrom: "0.3.0",
|
|
41
|
+
appliesToLegacy: true,
|
|
40
42
|
specReference: "product.md#configurator-pricing-and-price-bands",
|
|
41
43
|
};
|
package/dist/rules/pdp-e-009.js
CHANGED
|
@@ -58,5 +58,7 @@ export const pdpE009 = {
|
|
|
58
58
|
messageTemplate: "Modifier at `{path}` {problem}.",
|
|
59
59
|
remediation: "Spell dimension names and option values as the configurator spells them. Remove modifiers for dimensions that do not change the price, or remove `affectsPrice: false` from a dimension that does.",
|
|
60
60
|
introduced: "v0.9 (2026-10-01 revision)",
|
|
61
|
+
appliesFrom: "0.3.0",
|
|
62
|
+
appliesToLegacy: true,
|
|
61
63
|
specReference: "product.md#configurator-pricing-and-price-bands",
|
|
62
64
|
};
|
package/dist/rules/pdp-e-010.js
CHANGED
|
@@ -33,5 +33,7 @@ export const pdpE010 = {
|
|
|
33
33
|
messageTemplate: "Offer `price` at `{path}` is `{price}` but its pricing block's `basePrice` is `{basePrice}`.",
|
|
34
34
|
remediation: "Make the two amounts equal. If `price` is the amount the page shows on load, set `baseConfiguration` to the configuration the page preselects and `basePrice` to its total.",
|
|
35
35
|
introduced: "v0.9 (2026-10-01 revision)",
|
|
36
|
+
appliesFrom: "0.3.0",
|
|
37
|
+
appliesToLegacy: true,
|
|
36
38
|
specReference: "product.md#configurator-pricing-and-price-bands",
|
|
37
39
|
};
|
package/dist/rules/pdp-e-011.js
CHANGED
|
@@ -49,5 +49,7 @@ export const pdpE011 = {
|
|
|
49
49
|
messageTemplate: "`{bound}` at `{path}` is `{stated}` but the {extreme} non-excluded configuration computed from the pricing block totals `{computed}`.",
|
|
50
50
|
remediation: "Correct the bound, or correct the price list: a missing adjustment, a missing exclusion and a wrong modifier each move the computed band. If the price list is known to have gaps, declare `pricing: \"partial\"`.",
|
|
51
51
|
introduced: "v0.9 (2026-10-01 revision)",
|
|
52
|
+
appliesFrom: "0.3.0",
|
|
53
|
+
appliesToLegacy: true,
|
|
52
54
|
specReference: "product.md#configurator-pricing-and-price-bands",
|
|
53
55
|
};
|
package/dist/rules/pdp-e-012.js
CHANGED
|
@@ -28,5 +28,7 @@ export const pdpE012 = {
|
|
|
28
28
|
messageTemplate: "Offer at `{path}` prices configurator `{value}`, which the offer at `{firstPath}` already prices.",
|
|
29
29
|
remediation: "Keep one pricing block per configurator. Publish each further market's price list in that market's own Sheet.",
|
|
30
30
|
introduced: "v0.9 (2026-10-01 revision)",
|
|
31
|
+
appliesFrom: "0.3.0",
|
|
32
|
+
appliesToLegacy: true,
|
|
31
33
|
specReference: "product.md#configurator-pricing-and-price-bands",
|
|
32
34
|
};
|
package/dist/rules/pdp-e-013.js
CHANGED
|
@@ -22,5 +22,7 @@ export const pdpE013 = {
|
|
|
22
22
|
messageTemplate: "Dimension `{dimension}` at `{path}` declares `inputType: \"text\"` and enumerates options.",
|
|
23
23
|
remediation: "Remove `options` from a free-text dimension, or remove `inputType: \"text\"` from a dimension the buyer picks a value in.",
|
|
24
24
|
introduced: "v0.9 (2026-10-01 revision)",
|
|
25
|
+
appliesFrom: "0.3.0",
|
|
26
|
+
appliesToLegacy: true,
|
|
25
27
|
specReference: "product.md#configurator-pricing-and-price-bands",
|
|
26
28
|
};
|
package/dist/rules/pdp-e-014.js
CHANGED
|
@@ -30,5 +30,7 @@ export const pdpE014 = {
|
|
|
30
30
|
messageTemplate: "Size adjustment at `{path}` is in `{sizeSystem}` but the Sheet's sizing dimension is in `{dimensionSystem}`.",
|
|
31
31
|
remediation: "State the adjustment in the sizing dimension's system if the merchant states it there. If the merchant's advice is in another system, remove `sizeAdjustment` and keep the merchant's wording in `description`.",
|
|
32
32
|
introduced: "v0.9 (2026-10-01 revision)",
|
|
33
|
+
appliesFrom: "0.3.0",
|
|
34
|
+
appliesToLegacy: true,
|
|
33
35
|
specReference: "product.md#size-guidance-pagefrontsizechart-and-pagefrontsizerecommendation",
|
|
34
36
|
};
|
package/dist/rules/pdp-e-015.js
CHANGED
|
@@ -87,5 +87,7 @@ export const pdpE015 = {
|
|
|
87
87
|
messageTemplate: "Amounts mix numbers and strings; use one carrier throughout the Sheet.",
|
|
88
88
|
remediation: "Carry every amount the same way. Decimal strings are recommended when amounts have fractional parts.",
|
|
89
89
|
introduced: "v0.9 (2026-10-01 revision)",
|
|
90
|
+
appliesFrom: "0.3.0",
|
|
91
|
+
appliesToLegacy: true,
|
|
90
92
|
specReference: "product.md#amounts",
|
|
91
93
|
};
|
package/dist/rules/pdp-e-016.js
CHANGED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { asObject, own, pricedConfiguratorId, pricingBlock, seg } from "./configurator-pricing.js";
|
|
2
|
+
function declaredDimensions(configurator) {
|
|
3
|
+
const out = new Map();
|
|
4
|
+
const dimensions = configurator["dimensions"];
|
|
5
|
+
if (!Array.isArray(dimensions))
|
|
6
|
+
return out;
|
|
7
|
+
for (const entry of dimensions) {
|
|
8
|
+
const d = asObject(entry);
|
|
9
|
+
if (d === null || typeof d["name"] !== "string" || out.has(d["name"]))
|
|
10
|
+
continue;
|
|
11
|
+
const options = d["options"];
|
|
12
|
+
out.set(d["name"], {
|
|
13
|
+
values: Array.isArray(options) && options.length > 0
|
|
14
|
+
? options.map((option) => own(asObject(option), "value"))
|
|
15
|
+
: null,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
function unresolved(pairs, path, dimensions) {
|
|
21
|
+
const hits = [];
|
|
22
|
+
for (const [dimension, value] of Object.entries(asObject(pairs) ?? {})) {
|
|
23
|
+
const option = JSON.stringify(value);
|
|
24
|
+
const declared = dimensions.get(dimension);
|
|
25
|
+
let problem = null;
|
|
26
|
+
if (declared === undefined) {
|
|
27
|
+
problem = `the configurator declares no dimension \`${dimension}\``;
|
|
28
|
+
}
|
|
29
|
+
else if (declared.values !== null && !declared.values.some((v) => v === value)) {
|
|
30
|
+
problem = `\`${dimension}\` lists no option \`${option}\``;
|
|
31
|
+
}
|
|
32
|
+
if (problem !== null) {
|
|
33
|
+
hits.push({ path: `${path}${seg(dimension)}`, values: { dimension, option, problem } });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return hits;
|
|
37
|
+
}
|
|
38
|
+
export const pdpE017 = {
|
|
39
|
+
id: "PDP-E-017",
|
|
40
|
+
title: "Configurator reference does not resolve",
|
|
41
|
+
severity: "error",
|
|
42
|
+
target: "$.data",
|
|
43
|
+
check: (match) => {
|
|
44
|
+
const data = asObject(match.value);
|
|
45
|
+
const configurator = asObject(own(data, "pagefront:configurator"));
|
|
46
|
+
if (data === null || configurator === null)
|
|
47
|
+
return null;
|
|
48
|
+
const dimensions = declaredDimensions(configurator);
|
|
49
|
+
const hits = [];
|
|
50
|
+
const exclusions = configurator["exclusions"];
|
|
51
|
+
if (Array.isArray(exclusions)) {
|
|
52
|
+
exclusions.forEach((entry, index) => {
|
|
53
|
+
hits.push(...unresolved(entry, `${match.path}['pagefront:configurator']['exclusions'][${index}]`, dimensions));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// An adjustment's `when` refers to the configurator its pricing block
|
|
57
|
+
// names. A block naming another `@id` is PDP-E-001's finding, and its
|
|
58
|
+
// references are not resolved against this configurator.
|
|
59
|
+
const offers = own(data, "offers");
|
|
60
|
+
if (Array.isArray(offers)) {
|
|
61
|
+
offers.forEach((offer, offerIndex) => {
|
|
62
|
+
const block = pricingBlock(offer);
|
|
63
|
+
if (block === null || !Array.isArray(block["adjustments"]))
|
|
64
|
+
return;
|
|
65
|
+
if (pricedConfiguratorId(block) !== configurator["@id"])
|
|
66
|
+
return;
|
|
67
|
+
block["adjustments"].forEach((adjustment, index) => {
|
|
68
|
+
hits.push(...unresolved(asObject(adjustment)?.["when"], `${match.path}['offers'][${offerIndex}]['pagefront:configuratorPricing']['adjustments'][${index}]['when']`, dimensions));
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return hits.length > 0 ? hits : null;
|
|
73
|
+
},
|
|
74
|
+
messageTemplate: "Configuration reference at `{path}` (dimension `{dimension}`, option `{option}`) does not resolve: {problem}.",
|
|
75
|
+
remediation: "Name a dimension the configurator declares, and write the option exactly as its `value` is written in that dimension's `options`, including its JSON type. Remove an exclusion or adjustment that refers to an option the product no longer offers.",
|
|
76
|
+
introduced: "release 0.4.0",
|
|
77
|
+
// The requirement is introduced by release 0.4.0 (Product Sheet spec 0.10.0).
|
|
78
|
+
appliesFrom: "0.4.0",
|
|
79
|
+
specReference: "product.md#configurator-pricing-and-price-bands",
|
|
80
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Rule } from "@pagefront/lint-core";
|
|
2
|
+
/**
|
|
3
|
+
* PDP-E-018 — Unknown release.
|
|
4
|
+
* Transcribed from commerce/spec/rules.md (normative); the check is
|
|
5
|
+
* shared between the two catalogues (release-schema.ts).
|
|
6
|
+
*
|
|
7
|
+
* `appliesFrom` is "0.0.0": the rule judges the declared release
|
|
8
|
+
* itself, so it cannot be gated by it. It never fires on a sheet of a
|
|
9
|
+
* published release.
|
|
10
|
+
*/
|
|
11
|
+
export declare const pdpE018: Rule;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { UNKNOWN_RELEASE, unknownRelease } from "./release-schema.js";
|
|
2
|
+
/**
|
|
3
|
+
* PDP-E-018 — Unknown release.
|
|
4
|
+
* Transcribed from commerce/spec/rules.md (normative); the check is
|
|
5
|
+
* shared between the two catalogues (release-schema.ts).
|
|
6
|
+
*
|
|
7
|
+
* `appliesFrom` is "0.0.0": the rule judges the declared release
|
|
8
|
+
* itself, so it cannot be gated by it. It never fires on a sheet of a
|
|
9
|
+
* published release.
|
|
10
|
+
*/
|
|
11
|
+
export const pdpE018 = {
|
|
12
|
+
id: "PDP-E-018",
|
|
13
|
+
...UNKNOWN_RELEASE,
|
|
14
|
+
check: unknownRelease,
|
|
15
|
+
introduced: "release 0.4.0",
|
|
16
|
+
appliesFrom: "0.0.0",
|
|
17
|
+
specReference: "product.md#the-envelope",
|
|
18
|
+
};
|
package/dist/rules/pdp-i-001.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { DATA_BLOCK_PROPERTIES } from "../schema-properties.js";
|
|
1
|
+
import { DATA_BLOCK_PROPERTIES } from "../generated/schema-properties.js";
|
|
2
2
|
/**
|
|
3
3
|
* PDP-I-001 — Property outside Pagefront's modeled set.
|
|
4
4
|
* Transcribed from commerce/spec/rules.md (normative).
|
|
5
5
|
*
|
|
6
6
|
* The modeled set is the JSON Schema's dataBlock property inventory
|
|
7
|
-
*
|
|
7
|
+
* of the latest released Product Sheet schema (generated at build
|
|
8
|
+
* time, see scripts/bundle-schemas.mjs). The
|
|
8
9
|
* target `$.data.*` selects top-level data properties by design; any
|
|
9
10
|
* property outside the inventory fires, including unmodeled
|
|
10
11
|
* `pagefront:`-prefixed ones.
|
|
@@ -24,5 +25,7 @@ export const pdpI001 = {
|
|
|
24
25
|
messageTemplate: "Property `{property}` at `{path}` is valid Schema.org but is not explicitly modeled by Pagefront.",
|
|
25
26
|
remediation: "No action required. This finding is informational. If you find yourself using a Schema.org property frequently and would value formal Pagefront modeling for it, consider opening a spec proposal.",
|
|
26
27
|
introduced: "v0.6",
|
|
28
|
+
appliesFrom: "0.3.0",
|
|
29
|
+
appliesToLegacy: true,
|
|
27
30
|
specReference: "product.md#data--schemaorg-json-ld",
|
|
28
31
|
};
|
package/dist/rules/pdp-w-001.js
CHANGED
|
@@ -18,5 +18,7 @@ export const pdpW001 = {
|
|
|
18
18
|
messageTemplate: "No images declared at `{path}`.",
|
|
19
19
|
remediation: "Add at least one `ImageObject` to the `data.image` array. The primary image should typically declare role `hero` or `packshot`.",
|
|
20
20
|
introduced: "v0.6",
|
|
21
|
+
appliesFrom: "0.3.0",
|
|
22
|
+
appliesToLegacy: true,
|
|
21
23
|
specReference: "product.md#image-roles",
|
|
22
24
|
};
|
package/dist/rules/pdp-w-002.js
CHANGED
|
@@ -19,5 +19,7 @@ export const pdpW002 = {
|
|
|
19
19
|
messageTemplate: "No description at `{path}`.",
|
|
20
20
|
remediation: "Add a `description` field with a 1–3 paragraph prose description of the product. The description should match the Sheet's `inLanguage`.",
|
|
21
21
|
introduced: "v0.6",
|
|
22
|
+
appliesFrom: "0.3.0",
|
|
23
|
+
appliesToLegacy: true,
|
|
22
24
|
specReference: "product.md#required-and-recommended-core-fields",
|
|
23
25
|
};
|
package/dist/rules/pdp-w-003.js
CHANGED
|
@@ -18,5 +18,7 @@ export const pdpW003 = {
|
|
|
18
18
|
messageTemplate: "No brand declared at `{path}`.",
|
|
19
19
|
remediation: "Add a `brand` field with a typed `Brand` or `Organization` value, including at minimum `name` and optionally `url`.",
|
|
20
20
|
introduced: "v0.6",
|
|
21
|
+
appliesFrom: "0.3.0",
|
|
22
|
+
appliesToLegacy: true,
|
|
21
23
|
specReference: "product.md#required-and-recommended-core-fields",
|
|
22
24
|
};
|
package/dist/rules/pdp-w-004.js
CHANGED
|
@@ -26,5 +26,7 @@ export const pdpW004 = {
|
|
|
26
26
|
messageTemplate: "Offer at `{path}` has `price` but no `priceCurrency`.",
|
|
27
27
|
remediation: "Add a `priceCurrency` field with the ISO 4217 currency code matching the offer's currency.",
|
|
28
28
|
introduced: "v0.6",
|
|
29
|
+
appliesFrom: "0.3.0",
|
|
30
|
+
appliesToLegacy: true,
|
|
29
31
|
specReference: "product.md#offers",
|
|
30
32
|
};
|
package/dist/rules/pdp-w-005.js
CHANGED
|
@@ -40,5 +40,7 @@ export const pdpW005 = {
|
|
|
40
40
|
messageTemplate: "Material composition percentages at `{path}` sum to `{sum}`, not approximately 100.",
|
|
41
41
|
remediation: "Verify the composition data. If a material is missing, add it. If the values are correct but the publisher's source data uses percentages for a different denominator, omit `percentage` rather than use a value that doesn't represent a fraction of total composition.",
|
|
42
42
|
introduced: "v0.6",
|
|
43
|
+
appliesFrom: "0.3.0",
|
|
44
|
+
appliesToLegacy: true,
|
|
43
45
|
specReference: "product.md#materials-material-and-pagefrontmaterialcomposition",
|
|
44
46
|
};
|