@tracepass/dpp-validate 0.1.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/LICENSE +202 -0
- package/README.md +79 -0
- package/dist/battery-applicability.d.ts +84 -0
- package/dist/battery-applicability.d.ts.map +1 -0
- package/dist/battery-applicability.js +175 -0
- package/dist/battery-applicability.js.map +1 -0
- package/dist/eu-countries.d.ts +24 -0
- package/dist/eu-countries.d.ts.map +1 -0
- package/dist/eu-countries.js +37 -0
- package/dist/eu-countries.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/rules.d.ts +40 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +376 -0
- package/dist/rules.js.map +1 -0
- package/dist/types.d.ts +75 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +26 -0
- package/dist/types.js.map +1 -0
- package/dist/vendor/counts.d.ts +32 -0
- package/dist/vendor/counts.d.ts.map +1 -0
- package/dist/vendor/counts.js +28 -0
- package/dist/vendor/counts.js.map +1 -0
- package/dist/vendor/publish-gate.d.ts +41 -0
- package/dist/vendor/publish-gate.d.ts.map +1 -0
- package/dist/vendor/publish-gate.js +85 -0
- package/dist/vendor/publish-gate.js.map +1 -0
- package/dist/vendor/required-roles.d.ts +72 -0
- package/dist/vendor/required-roles.d.ts.map +1 -0
- package/dist/vendor/required-roles.js +144 -0
- package/dist/vendor/required-roles.js.map +1 -0
- package/dist/verdict.d.ts +30 -0
- package/dist/verdict.d.ts.map +1 -0
- package/dist/verdict.js +189 -0
- package/dist/verdict.js.map +1 -0
- package/package.json +22 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure "can this passport be published?" check, shared between:
|
|
3
|
+
* - GET /api/passports (list response includes publishReady per row)
|
|
4
|
+
* - POST /api/passports/[id]/publish (single-publish eligibility)
|
|
5
|
+
* - POST /api/passports/bulk-publish (skipped-reason messages)
|
|
6
|
+
*
|
|
7
|
+
* One rule, one implementation. If the product policy for publishing
|
|
8
|
+
* changes (e.g. "warnings block", "min coverage %"), change it here.
|
|
9
|
+
*/
|
|
10
|
+
import type { Passport, Template } from "@tracepass/dpp-types";
|
|
11
|
+
export interface PublishCheck {
|
|
12
|
+
ready: boolean;
|
|
13
|
+
/** Field keys that have no value set. */
|
|
14
|
+
missingFields: string[];
|
|
15
|
+
/** Field keys with a value but status != approved. */
|
|
16
|
+
unapprovedFields: string[];
|
|
17
|
+
/** Short human reason suitable for a tooltip or toast — undefined when ready. */
|
|
18
|
+
reason?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Distinguishes WHY the passport isn't ready:
|
|
21
|
+
* - "hard" → structural blocker that can't be bypassed
|
|
22
|
+
* (already-published, wrong status, template missing).
|
|
23
|
+
* - "gap" → required fields are missing or unapproved. The UI
|
|
24
|
+
* can offer to publish anyway with an explicit "I
|
|
25
|
+
* understand the risk" acknowledgement.
|
|
26
|
+
* - `undefined` when ready === true.
|
|
27
|
+
*/
|
|
28
|
+
blockerType?: "hard" | "gap";
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Returns whether `passport` satisfies every publish precondition given
|
|
32
|
+
* its `template`. A passport is publish-ready iff:
|
|
33
|
+
* - status is in PUBLISHABLE_STATUSES (already-published + terminal
|
|
34
|
+
* states return ready:false with a specific reason),
|
|
35
|
+
* - every template field with validation.required has a non-empty
|
|
36
|
+
* value AND status === "approved".
|
|
37
|
+
*
|
|
38
|
+
* `undefined` template means we can't evaluate — treat as not-ready.
|
|
39
|
+
*/
|
|
40
|
+
export declare function checkPublishReady(passport: Passport, template: Template | undefined): PublishCheck;
|
|
41
|
+
//# sourceMappingURL=publish-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-gate.d.ts","sourceRoot":"","sources":["../../src/vendor/publish-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,yCAAyC;IACzC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,sDAAsD;IACtD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC9B;AAQD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAC7B,YAAY,CAgEd"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure "can this passport be published?" check, shared between:
|
|
3
|
+
* - GET /api/passports (list response includes publishReady per row)
|
|
4
|
+
* - POST /api/passports/[id]/publish (single-publish eligibility)
|
|
5
|
+
* - POST /api/passports/bulk-publish (skipped-reason messages)
|
|
6
|
+
*
|
|
7
|
+
* One rule, one implementation. If the product policy for publishing
|
|
8
|
+
* changes (e.g. "warnings block", "min coverage %"), change it here.
|
|
9
|
+
*/
|
|
10
|
+
const PUBLISHABLE_STATUSES = new Set([
|
|
11
|
+
"draft",
|
|
12
|
+
"in_review",
|
|
13
|
+
"approved",
|
|
14
|
+
]);
|
|
15
|
+
/**
|
|
16
|
+
* Returns whether `passport` satisfies every publish precondition given
|
|
17
|
+
* its `template`. A passport is publish-ready iff:
|
|
18
|
+
* - status is in PUBLISHABLE_STATUSES (already-published + terminal
|
|
19
|
+
* states return ready:false with a specific reason),
|
|
20
|
+
* - every template field with validation.required has a non-empty
|
|
21
|
+
* value AND status === "approved".
|
|
22
|
+
*
|
|
23
|
+
* `undefined` template means we can't evaluate — treat as not-ready.
|
|
24
|
+
*/
|
|
25
|
+
export function checkPublishReady(passport, template) {
|
|
26
|
+
if (passport.status === "published") {
|
|
27
|
+
return {
|
|
28
|
+
ready: false,
|
|
29
|
+
missingFields: [],
|
|
30
|
+
unapprovedFields: [],
|
|
31
|
+
reason: "Already published",
|
|
32
|
+
blockerType: "hard",
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (!PUBLISHABLE_STATUSES.has(passport.status)) {
|
|
36
|
+
return {
|
|
37
|
+
ready: false,
|
|
38
|
+
missingFields: [],
|
|
39
|
+
unapprovedFields: [],
|
|
40
|
+
reason: `Cannot publish from status ${passport.status}`,
|
|
41
|
+
blockerType: "hard",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (!template) {
|
|
45
|
+
return {
|
|
46
|
+
ready: false,
|
|
47
|
+
missingFields: [],
|
|
48
|
+
unapprovedFields: [],
|
|
49
|
+
reason: "Template missing",
|
|
50
|
+
blockerType: "hard",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const missingFields = [];
|
|
54
|
+
const unapprovedFields = [];
|
|
55
|
+
for (const tf of template.fields) {
|
|
56
|
+
if (!tf.validation.required)
|
|
57
|
+
continue;
|
|
58
|
+
const f = passport.fields[tf.key];
|
|
59
|
+
if (!f ||
|
|
60
|
+
f.value === null ||
|
|
61
|
+
f.value === undefined ||
|
|
62
|
+
f.value === "") {
|
|
63
|
+
missingFields.push(tf.key);
|
|
64
|
+
}
|
|
65
|
+
else if (f.status !== "approved") {
|
|
66
|
+
unapprovedFields.push(tf.key);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (missingFields.length === 0 && unapprovedFields.length === 0) {
|
|
70
|
+
return { ready: true, missingFields: [], unapprovedFields: [] };
|
|
71
|
+
}
|
|
72
|
+
const parts = [];
|
|
73
|
+
if (missingFields.length > 0)
|
|
74
|
+
parts.push(`${missingFields.length} missing`);
|
|
75
|
+
if (unapprovedFields.length > 0)
|
|
76
|
+
parts.push(`${unapprovedFields.length} not approved`);
|
|
77
|
+
return {
|
|
78
|
+
ready: false,
|
|
79
|
+
missingFields,
|
|
80
|
+
unapprovedFields,
|
|
81
|
+
reason: `Required fields: ${parts.join(", ")}`,
|
|
82
|
+
blockerType: "gap",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=publish-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-gate.js","sourceRoot":"","sources":["../../src/vendor/publish-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAwBH,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAqB;IACvD,OAAO;IACP,WAAW;IACX,UAAU;CACX,CAAC,CAAC;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAkB,EAClB,QAA8B;IAE9B,IAAI,QAAQ,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;QACpC,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,aAAa,EAAE,EAAE;YACjB,gBAAgB,EAAE,EAAE;YACpB,MAAM,EAAE,mBAAmB;YAC3B,WAAW,EAAE,MAAM;SACpB,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,aAAa,EAAE,EAAE;YACjB,gBAAgB,EAAE,EAAE;YACpB,MAAM,EAAE,8BAA8B,QAAQ,CAAC,MAAM,EAAE;YACvD,WAAW,EAAE,MAAM;SACpB,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,aAAa,EAAE,EAAE;YACjB,gBAAgB,EAAE,EAAE;YACpB,MAAM,EAAE,kBAAkB;YAC1B,WAAW,EAAE,MAAM;SACpB,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,GAAa,EAAE,CAAC;IACnC,MAAM,gBAAgB,GAAa,EAAE,CAAC;IAEtC,KAAK,MAAM,EAAE,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ;YAAE,SAAS;QACtC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;QAClC,IACE,CAAC,CAAC;YACF,CAAC,CAAC,KAAK,KAAK,IAAI;YAChB,CAAC,CAAC,KAAK,KAAK,SAAS;YACrB,CAAC,CAAC,KAAK,KAAK,EAAE,EACd,CAAC;YACD,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;aAAM,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YACnC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC;IAClE,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,MAAM,UAAU,CAAC,CAAC;IAC5E,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,MAAM,eAAe,CAAC,CAAC;IAEvF,OAAO;QACL,KAAK,EAAE,KAAK;QACZ,aAAa;QACb,gBAAgB;QAChB,MAAM,EAAE,oBAAoB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAC9C,WAAW,EAAE,KAAK;KACnB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-category required and optional party roles.
|
|
3
|
+
*
|
|
4
|
+
* Source of truth for "which parties does this passport need before it can
|
|
5
|
+
* be considered complete." Used by:
|
|
6
|
+
* - the editor UI (red asterisks on required roles, hides irrelevant ones)
|
|
7
|
+
* - the CSV bulk-import column generator (only emits columns for roles
|
|
8
|
+
* this category actually uses)
|
|
9
|
+
* - the publish-precondition check (warns on missing required roles, in
|
|
10
|
+
* parallel to the existing missing-required-fields warning)
|
|
11
|
+
* - the per-passport completionPercentage calculation
|
|
12
|
+
*
|
|
13
|
+
* The TypeScript module is the single source of truth — the role map is
|
|
14
|
+
* NOT mirrored into the templates collection. Adding or removing roles
|
|
15
|
+
* from a category is a code change + deploy, deliberate so we don't drift
|
|
16
|
+
* the regulation interpretation across environments.
|
|
17
|
+
*
|
|
18
|
+
* v1 design calls (per Malin's confirmation):
|
|
19
|
+
* - Chemicals "downstream user" (REACH-specific role) → folded into
|
|
20
|
+
* `distributor` for now; revisit when a chemicals customer pulls.
|
|
21
|
+
* - Tyre "retreader" → ignored for v1; retreaded tyres treated as a
|
|
22
|
+
* separate passport whose `manufacturer` is the retreader.
|
|
23
|
+
*
|
|
24
|
+
* When a category's regulation tightens (a new delegated act lands and
|
|
25
|
+
* adds a mandate), update the entry here and the change ships in the
|
|
26
|
+
* next deploy — no template re-seed needed.
|
|
27
|
+
*/
|
|
28
|
+
import type { PartyRole } from "@tracepass/dpp-types";
|
|
29
|
+
/**
|
|
30
|
+
* The 12 categories the platform models. Mirrors the filenames in
|
|
31
|
+
* `templates/*.json`. We re-declare the union here rather than import
|
|
32
|
+
* a CategoryKey from elsewhere so this module stays the canonical
|
|
33
|
+
* place a future contributor looks when adding a 13th category.
|
|
34
|
+
*/
|
|
35
|
+
export type CategoryKey = "battery" | "chemicals" | "construction" | "electronics" | "fmcg" | "furniture" | "jewelry" | "packaging" | "steel" | "textile" | "toys" | "tyres";
|
|
36
|
+
export interface CategoryPartyRoles {
|
|
37
|
+
/** Roles that MUST be set before the passport is considered complete.
|
|
38
|
+
* The publish flow warns (not blocks) when these are missing — a
|
|
39
|
+
* user can still publish via the existing "I acknowledge incomplete"
|
|
40
|
+
* ack, which sets `publishedIncomplete: true`. */
|
|
41
|
+
required: readonly PartyRole[];
|
|
42
|
+
/** Roles that the editor surfaces but doesn't require. Order matters —
|
|
43
|
+
* the UI renders them in this order under the required block. */
|
|
44
|
+
optional: readonly PartyRole[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Per-category role requirements. Each entry's `required` is the
|
|
48
|
+
* regulator's mandate; `optional` is roles that a tenant might
|
|
49
|
+
* legitimately want to record but isn't compelled to.
|
|
50
|
+
*/
|
|
51
|
+
export declare const CATEGORY_PARTY_ROLES: Record<CategoryKey, CategoryPartyRoles>;
|
|
52
|
+
/**
|
|
53
|
+
* Get the required + optional role lists for a category. Returns
|
|
54
|
+
* `null` when the category isn't one of the 12 modeled categories,
|
|
55
|
+
* so callers can early-return rather than crash on legacy / unknown
|
|
56
|
+
* category strings.
|
|
57
|
+
*/
|
|
58
|
+
export declare function getPartyRoles(category: string): CategoryPartyRoles | null;
|
|
59
|
+
/**
|
|
60
|
+
* True when `role` is required for the given category. Used by the
|
|
61
|
+
* publish-precondition check + the editor's red-asterisk render.
|
|
62
|
+
* Unknown categories return false (don't block on what we can't
|
|
63
|
+
* interpret).
|
|
64
|
+
*/
|
|
65
|
+
export declare function isRequiredRole(category: string, role: PartyRole): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Combined role list (required first, then optional, in declaration
|
|
68
|
+
* order). Returns `[]` for unknown categories. Convenience for UI
|
|
69
|
+
* iteration when both sets need to render.
|
|
70
|
+
*/
|
|
71
|
+
export declare function allRolesForCategory(category: string): readonly PartyRole[];
|
|
72
|
+
//# sourceMappingURL=required-roles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"required-roles.d.ts","sourceRoot":"","sources":["../../src/vendor/required-roles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GACnB,SAAS,GACT,WAAW,GACX,cAAc,GACd,aAAa,GACb,MAAM,GACN,WAAW,GACX,SAAS,GACT,WAAW,GACX,OAAO,GACP,SAAS,GACT,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,WAAW,kBAAkB;IACjC;;;uDAGmD;IACnD,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;IAC/B;sEACkE;IAClE,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;CAChC;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,WAAW,EAAE,kBAAkB,CA2F/D,CAAC;AAEX;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,GACf,kBAAkB,GAAG,IAAI,CAE3B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAGzE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CAI1E"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-category required and optional party roles.
|
|
3
|
+
*
|
|
4
|
+
* Source of truth for "which parties does this passport need before it can
|
|
5
|
+
* be considered complete." Used by:
|
|
6
|
+
* - the editor UI (red asterisks on required roles, hides irrelevant ones)
|
|
7
|
+
* - the CSV bulk-import column generator (only emits columns for roles
|
|
8
|
+
* this category actually uses)
|
|
9
|
+
* - the publish-precondition check (warns on missing required roles, in
|
|
10
|
+
* parallel to the existing missing-required-fields warning)
|
|
11
|
+
* - the per-passport completionPercentage calculation
|
|
12
|
+
*
|
|
13
|
+
* The TypeScript module is the single source of truth — the role map is
|
|
14
|
+
* NOT mirrored into the templates collection. Adding or removing roles
|
|
15
|
+
* from a category is a code change + deploy, deliberate so we don't drift
|
|
16
|
+
* the regulation interpretation across environments.
|
|
17
|
+
*
|
|
18
|
+
* v1 design calls (per Malin's confirmation):
|
|
19
|
+
* - Chemicals "downstream user" (REACH-specific role) → folded into
|
|
20
|
+
* `distributor` for now; revisit when a chemicals customer pulls.
|
|
21
|
+
* - Tyre "retreader" → ignored for v1; retreaded tyres treated as a
|
|
22
|
+
* separate passport whose `manufacturer` is the retreader.
|
|
23
|
+
*
|
|
24
|
+
* When a category's regulation tightens (a new delegated act lands and
|
|
25
|
+
* adds a mandate), update the entry here and the change ships in the
|
|
26
|
+
* next deploy — no template re-seed needed.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Per-category role requirements. Each entry's `required` is the
|
|
30
|
+
* regulator's mandate; `optional` is roles that a tenant might
|
|
31
|
+
* legitimately want to record but isn't compelled to.
|
|
32
|
+
*/
|
|
33
|
+
export const CATEGORY_PARTY_ROLES = {
|
|
34
|
+
// Battery Regulation 2023/1542 Articles 47–50 require all three
|
|
35
|
+
// operators to be identifiable on the passport for serial-level
|
|
36
|
+
// batteries (LMT, EV, industrial >2 kWh).
|
|
37
|
+
battery: {
|
|
38
|
+
required: ["manufacturer", "recycler", "producerResponsibilityOrg"],
|
|
39
|
+
optional: ["importer", "authorisedRepresentative", "distributor"],
|
|
40
|
+
},
|
|
41
|
+
// RoHS / WEEE / RED / EMC — manufacturer is mandatory for any
|
|
42
|
+
// EU placement; AR + importer become mandatory for non-EU
|
|
43
|
+
// manufacturers under Article 4 of the relevant directive, but
|
|
44
|
+
// since we can't tell at schema time whether the manufacturer is
|
|
45
|
+
// EU or not, we keep them optional and let the editor surface
|
|
46
|
+
// them when the manufacturer's `country` is non-EU.
|
|
47
|
+
electronics: {
|
|
48
|
+
required: ["manufacturer"],
|
|
49
|
+
optional: ["importer", "authorisedRepresentative", "distributor", "recycler"],
|
|
50
|
+
},
|
|
51
|
+
// Toy Safety Directive 2009/48 Article 4 — both manufacturer and
|
|
52
|
+
// importer are mandatory for non-EU toys, the most common case.
|
|
53
|
+
toys: {
|
|
54
|
+
required: ["manufacturer", "importer"],
|
|
55
|
+
optional: ["authorisedRepresentative", "distributor"],
|
|
56
|
+
},
|
|
57
|
+
// PPWR 2025/40 Article 11 + national packaging EPR schemes —
|
|
58
|
+
// PRO required where the EPR scheme applies (most EU member states).
|
|
59
|
+
fmcg: {
|
|
60
|
+
required: ["manufacturer", "producerResponsibilityOrg"],
|
|
61
|
+
optional: ["importer", "distributor", "recycler"],
|
|
62
|
+
},
|
|
63
|
+
// PPWR Articles 7 + 45 — packaging materials themselves carry
|
|
64
|
+
// direct PRO obligations.
|
|
65
|
+
packaging: {
|
|
66
|
+
required: ["manufacturer", "producerResponsibilityOrg"],
|
|
67
|
+
optional: ["importer", "distributor", "recycler"],
|
|
68
|
+
},
|
|
69
|
+
// Textile EPR is national (DE, FR, NL have schemes; harmonised
|
|
70
|
+
// EU rules pending). Keep PRO optional until ESPR delegated act
|
|
71
|
+
// mandates it. Manufacturer canonical.
|
|
72
|
+
textile: {
|
|
73
|
+
required: ["manufacturer"],
|
|
74
|
+
optional: ["producerResponsibilityOrg", "importer", "distributor", "recycler"],
|
|
75
|
+
},
|
|
76
|
+
// No EU-level operator mandate beyond CE for upholstered furniture
|
|
77
|
+
// (national flammability rules in IT/UK/FR). Manufacturer canonical.
|
|
78
|
+
furniture: {
|
|
79
|
+
required: ["manufacturer"],
|
|
80
|
+
optional: ["importer", "distributor"],
|
|
81
|
+
},
|
|
82
|
+
// REACH places obligations on manufacturer + importer + downstream
|
|
83
|
+
// user. v1 maps "downstream user" to `distributor` per design call;
|
|
84
|
+
// revisit when a chemicals customer pulls.
|
|
85
|
+
chemicals: {
|
|
86
|
+
required: ["manufacturer"],
|
|
87
|
+
optional: ["importer", "distributor", "authorisedRepresentative"],
|
|
88
|
+
},
|
|
89
|
+
// CPR 2024/3110 — manufacturer mandatory; AR for non-EU
|
|
90
|
+
// construction-product placers.
|
|
91
|
+
construction: {
|
|
92
|
+
required: ["manufacturer"],
|
|
93
|
+
optional: ["importer", "distributor", "authorisedRepresentative"],
|
|
94
|
+
},
|
|
95
|
+
// Tyre Labelling Reg (EU) 2020/740 places obligations on the
|
|
96
|
+
// manufacturer. Retreader role deferred to v2 — see module doc.
|
|
97
|
+
tyres: {
|
|
98
|
+
required: ["manufacturer"],
|
|
99
|
+
optional: ["importer", "distributor"],
|
|
100
|
+
},
|
|
101
|
+
// CBAM places obligations on the importer of CBAM goods, not
|
|
102
|
+
// the steel-mill DPP itself. Manufacturer canonical here.
|
|
103
|
+
steel: {
|
|
104
|
+
required: ["manufacturer"],
|
|
105
|
+
optional: ["importer", "distributor"],
|
|
106
|
+
},
|
|
107
|
+
// REACH SVHC notification — manufacturer canonical; no specific
|
|
108
|
+
// party-mandate beyond that for fine jewellery.
|
|
109
|
+
jewelry: {
|
|
110
|
+
required: ["manufacturer"],
|
|
111
|
+
optional: ["importer", "distributor"],
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Get the required + optional role lists for a category. Returns
|
|
116
|
+
* `null` when the category isn't one of the 12 modeled categories,
|
|
117
|
+
* so callers can early-return rather than crash on legacy / unknown
|
|
118
|
+
* category strings.
|
|
119
|
+
*/
|
|
120
|
+
export function getPartyRoles(category) {
|
|
121
|
+
return CATEGORY_PARTY_ROLES[category] ?? null;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* True when `role` is required for the given category. Used by the
|
|
125
|
+
* publish-precondition check + the editor's red-asterisk render.
|
|
126
|
+
* Unknown categories return false (don't block on what we can't
|
|
127
|
+
* interpret).
|
|
128
|
+
*/
|
|
129
|
+
export function isRequiredRole(category, role) {
|
|
130
|
+
const roles = getPartyRoles(category);
|
|
131
|
+
return roles?.required.includes(role) ?? false;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Combined role list (required first, then optional, in declaration
|
|
135
|
+
* order). Returns `[]` for unknown categories. Convenience for UI
|
|
136
|
+
* iteration when both sets need to render.
|
|
137
|
+
*/
|
|
138
|
+
export function allRolesForCategory(category) {
|
|
139
|
+
const roles = getPartyRoles(category);
|
|
140
|
+
if (!roles)
|
|
141
|
+
return [];
|
|
142
|
+
return [...roles.required, ...roles.optional];
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=required-roles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"required-roles.js","sourceRoot":"","sources":["../../src/vendor/required-roles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compliance-verdict engine.
|
|
3
|
+
*
|
|
4
|
+
* Takes a passport, its template, and the category; returns a three-tier
|
|
5
|
+
* verdict (compliant / compliant_with_warnings / incomplete) with
|
|
6
|
+
* regulation-cited findings a caller can act on, then re-check after fixing
|
|
7
|
+
* — the read → fix → verify loop.
|
|
8
|
+
*
|
|
9
|
+
* Pure and IO-free: no database, no network, no clock.
|
|
10
|
+
*
|
|
11
|
+
* Composition:
|
|
12
|
+
* static tier — required fields (checkPublishReady) + required
|
|
13
|
+
* economic operators (required-roles) + format validation
|
|
14
|
+
* conditional tier — CONDITIONAL_RULES[category] + CROSS_CUTTING_RULES
|
|
15
|
+
* coverage — "evaluated" if the category has conditional rules,
|
|
16
|
+
* else "static-only"
|
|
17
|
+
*
|
|
18
|
+
* Each finding cites the article of the instrument that mandates it, taken
|
|
19
|
+
* from the template field's own `regulationRef`.
|
|
20
|
+
*/
|
|
21
|
+
import type { Passport, Template } from "@tracepass/dpp-types";
|
|
22
|
+
import type { ComplianceResult } from "./types.js";
|
|
23
|
+
/**
|
|
24
|
+
* Evaluate a passport's compliance. `template` undefined → we can't
|
|
25
|
+
* assess; returns an `incomplete` verdict with a single explanatory
|
|
26
|
+
* critical finding (mirrors checkPublishReady's "template missing" hard
|
|
27
|
+
* blocker rather than throwing).
|
|
28
|
+
*/
|
|
29
|
+
export declare function evaluateCompliance(passport: Passport, template: Template | undefined, category: string): ComplianceResult;
|
|
30
|
+
//# sourceMappingURL=verdict.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verdict.d.ts","sourceRoot":"","sources":["../src/verdict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAiB,MAAM,sBAAsB,CAAC;AAK9E,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAiEpB;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,QAAQ,EAAE,MAAM,GACf,gBAAgB,CA2GlB"}
|
package/dist/verdict.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compliance-verdict engine.
|
|
3
|
+
*
|
|
4
|
+
* Takes a passport, its template, and the category; returns a three-tier
|
|
5
|
+
* verdict (compliant / compliant_with_warnings / incomplete) with
|
|
6
|
+
* regulation-cited findings a caller can act on, then re-check after fixing
|
|
7
|
+
* — the read → fix → verify loop.
|
|
8
|
+
*
|
|
9
|
+
* Pure and IO-free: no database, no network, no clock.
|
|
10
|
+
*
|
|
11
|
+
* Composition:
|
|
12
|
+
* static tier — required fields (checkPublishReady) + required
|
|
13
|
+
* economic operators (required-roles) + format validation
|
|
14
|
+
* conditional tier — CONDITIONAL_RULES[category] + CROSS_CUTTING_RULES
|
|
15
|
+
* coverage — "evaluated" if the category has conditional rules,
|
|
16
|
+
* else "static-only"
|
|
17
|
+
*
|
|
18
|
+
* Each finding cites the article of the instrument that mandates it, taken
|
|
19
|
+
* from the template field's own `regulationRef`.
|
|
20
|
+
*/
|
|
21
|
+
import { checkPublishReady } from "./vendor/publish-gate.js";
|
|
22
|
+
import { getPartyRoles } from "./vendor/required-roles.js";
|
|
23
|
+
import { derivePassportCounts } from "./vendor/counts.js";
|
|
24
|
+
import { CONDITIONAL_RULES, CROSS_CUTTING_RULES } from "./rules.js";
|
|
25
|
+
/** Validate a filled value against a template field's format rules.
|
|
26
|
+
* Returns a finding (warning) on mismatch, or null when clean. Empty
|
|
27
|
+
* values are NOT checked here — presence is the static field check's
|
|
28
|
+
* job; this only judges the SHAPE of values that ARE set. */
|
|
29
|
+
function checkFieldFormat(tf, value) {
|
|
30
|
+
const v = tf.validation;
|
|
31
|
+
const base = {
|
|
32
|
+
type: "invalid_format",
|
|
33
|
+
severity: "warning",
|
|
34
|
+
target: tf.key,
|
|
35
|
+
regulation: undefined,
|
|
36
|
+
// Template JSON stores an absent article as an explicit `null`; findings
|
|
37
|
+
// expose it as `undefined` so consumers only handle one absent case.
|
|
38
|
+
article: tf.regulationRef?.article ?? undefined,
|
|
39
|
+
};
|
|
40
|
+
// enum / multi_enum: value(s) must be among the allowed options.
|
|
41
|
+
if ((tf.dataType === "enum" || tf.dataType === "multi_enum") && tf.enumOptions) {
|
|
42
|
+
const allowed = new Set(tf.enumOptions.map((o) => o.value));
|
|
43
|
+
const vals = Array.isArray(value) ? value : [value];
|
|
44
|
+
const bad = vals.filter((x) => !allowed.has(String(x)));
|
|
45
|
+
if (bad.length > 0) {
|
|
46
|
+
return { ...base, why: `Value "${bad.join(", ")}" is not one of the allowed options for ${tf.key}.`, fix: `Use one of: ${[...allowed].join(", ")}.` };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// string-shaped checks (string / url). Bounds use `!= null` so an
|
|
50
|
+
// explicit `null` in the template JSON (the seeded shape for "no
|
|
51
|
+
// bound" — not `undefined`) is correctly treated as "no constraint"
|
|
52
|
+
// rather than coercing to 0.
|
|
53
|
+
if (typeof value === "string") {
|
|
54
|
+
if (v.minLength != null && value.length < v.minLength) {
|
|
55
|
+
return { ...base, why: `${tf.key} is shorter than the required ${v.minLength} characters.`, fix: `Provide at least ${v.minLength} characters.` };
|
|
56
|
+
}
|
|
57
|
+
if (v.maxLength != null && value.length > v.maxLength) {
|
|
58
|
+
return { ...base, why: `${tf.key} exceeds the maximum ${v.maxLength} characters.`, fix: `Shorten to at most ${v.maxLength} characters.` };
|
|
59
|
+
}
|
|
60
|
+
if (v.pattern) {
|
|
61
|
+
let re = null;
|
|
62
|
+
try {
|
|
63
|
+
re = new RegExp(v.pattern);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
re = null;
|
|
67
|
+
}
|
|
68
|
+
if (re && !re.test(value)) {
|
|
69
|
+
return { ...base, why: `${tf.key} doesn't match the required format.`, fix: `Expected format: ${tf.aiHints?.expectedFormat ?? v.pattern}.` };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// numeric bounds — `!= null` for the same reason (template carries
|
|
74
|
+
// explicit `min: null` / `max: null`, which must not coerce to 0).
|
|
75
|
+
if (typeof value === "number") {
|
|
76
|
+
if (v.min != null && value < v.min) {
|
|
77
|
+
return { ...base, why: `${tf.key} (${value}) is below the minimum ${v.min}.`, fix: `Use a value ≥ ${v.min}.` };
|
|
78
|
+
}
|
|
79
|
+
if (v.max != null && value > v.max) {
|
|
80
|
+
return { ...base, why: `${tf.key} (${value}) is above the maximum ${v.max}.`, fix: `Use a value ≤ ${v.max}.` };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Evaluate a passport's compliance. `template` undefined → we can't
|
|
87
|
+
* assess; returns an `incomplete` verdict with a single explanatory
|
|
88
|
+
* critical finding (mirrors checkPublishReady's "template missing" hard
|
|
89
|
+
* blocker rather than throwing).
|
|
90
|
+
*/
|
|
91
|
+
export function evaluateCompliance(passport, template, category) {
|
|
92
|
+
const { completionPercentage } = derivePassportCounts(passport.fields);
|
|
93
|
+
if (!template) {
|
|
94
|
+
return {
|
|
95
|
+
verdict: "incomplete",
|
|
96
|
+
category,
|
|
97
|
+
conditionalCoverage: "static-only",
|
|
98
|
+
critical: [
|
|
99
|
+
{
|
|
100
|
+
type: "missing_field",
|
|
101
|
+
severity: "critical",
|
|
102
|
+
why: "No template is available for this passport, so compliance can't be evaluated.",
|
|
103
|
+
fix: "Ensure the passport references a valid category template.",
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
warnings: [],
|
|
107
|
+
checkedRules: [],
|
|
108
|
+
completionPercentage,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const critical = [];
|
|
112
|
+
const warnings = [];
|
|
113
|
+
const checkedRules = [];
|
|
114
|
+
// ── Static tier 1: required fields (reuse the publish gate) ────────
|
|
115
|
+
checkedRules.push("static:required-fields");
|
|
116
|
+
const publish = checkPublishReady(passport, template);
|
|
117
|
+
const fieldRef = (key) => template.fields.find((f) => f.key === key)?.regulationRef;
|
|
118
|
+
for (const key of publish.missingFields) {
|
|
119
|
+
const ref = fieldRef(key);
|
|
120
|
+
critical.push({
|
|
121
|
+
type: "missing_field",
|
|
122
|
+
severity: "critical",
|
|
123
|
+
target: key,
|
|
124
|
+
regulation: ref ? template.regulation?.number : undefined,
|
|
125
|
+
article: ref?.article ?? undefined,
|
|
126
|
+
why: `Required field "${key}" has no value.`,
|
|
127
|
+
fix: `Provide a value for ${key}.`,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
for (const key of publish.unapprovedFields) {
|
|
131
|
+
critical.push({
|
|
132
|
+
type: "unapproved_field",
|
|
133
|
+
severity: "critical",
|
|
134
|
+
target: key,
|
|
135
|
+
why: `Required field "${key}" has a value but isn't approved yet.`,
|
|
136
|
+
fix: `Review and approve ${key}.`,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
// ── Static tier 2: required economic-operator parties ──────────────
|
|
140
|
+
checkedRules.push("static:required-parties");
|
|
141
|
+
const roles = getPartyRoles(category);
|
|
142
|
+
if (roles) {
|
|
143
|
+
for (const role of roles.required) {
|
|
144
|
+
if (!passport.parties?.[role]) {
|
|
145
|
+
critical.push({
|
|
146
|
+
type: "missing_party",
|
|
147
|
+
severity: "critical",
|
|
148
|
+
target: role,
|
|
149
|
+
why: `Required economic operator "${role}" is not set for a ${category} passport.`,
|
|
150
|
+
fix: `Add the ${role} party (legal name + GS1 GLN where available).`,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// ── Static tier 3: format of filled values (warnings) ──────────────
|
|
156
|
+
checkedRules.push("static:format");
|
|
157
|
+
for (const tf of template.fields) {
|
|
158
|
+
const f = passport.fields[tf.key];
|
|
159
|
+
if (!f || f.value === null || f.value === undefined || f.value === "")
|
|
160
|
+
continue;
|
|
161
|
+
const bad = checkFieldFormat(tf, f.value);
|
|
162
|
+
if (bad)
|
|
163
|
+
warnings.push(bad);
|
|
164
|
+
}
|
|
165
|
+
// ── Conditional tier: per-category + cross-cutting ─────────────────
|
|
166
|
+
const categoryRules = CONDITIONAL_RULES[category] ?? [];
|
|
167
|
+
const conditionalCoverage = categoryRules.length > 0 ? "evaluated" : "static-only";
|
|
168
|
+
for (const rule of [...categoryRules, ...CROSS_CUTTING_RULES]) {
|
|
169
|
+
checkedRules.push(rule.id);
|
|
170
|
+
for (const finding of rule.run(passport, template)) {
|
|
171
|
+
(finding.severity === "critical" ? critical : warnings).push(finding);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const verdict = critical.length > 0
|
|
175
|
+
? "incomplete"
|
|
176
|
+
: warnings.length > 0
|
|
177
|
+
? "compliant_with_warnings"
|
|
178
|
+
: "compliant";
|
|
179
|
+
return {
|
|
180
|
+
verdict,
|
|
181
|
+
category,
|
|
182
|
+
conditionalCoverage,
|
|
183
|
+
critical,
|
|
184
|
+
warnings,
|
|
185
|
+
checkedRules,
|
|
186
|
+
completionPercentage,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=verdict.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verdict.js","sourceRoot":"","sources":["../src/verdict.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tracepass/dpp-validate",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Evaluate an EU Digital Product Passport against its category field-spec — a three-tier compliance verdict with regulation-cited findings. Pure functions, no third-party runtime dependencies.",
|
|
5
|
+
"keywords": ["digital-product-passport", "dpp", "espr", "compliance", "eu-regulation", "battery-passport", "validation", "circular-economy"],
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"author": "TracePass LTD (https://tracepass.eu)",
|
|
8
|
+
"homepage": "https://github.com/malinoto/tracepass-open/tree/main/packages/dpp-validate#readme",
|
|
9
|
+
"repository": { "type": "git", "url": "git+https://github.com/malinoto/tracepass-open.git", "directory": "packages/dpp-validate" },
|
|
10
|
+
"bugs": "https://github.com/malinoto/tracepass-open/issues",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } },
|
|
15
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"publishConfig": { "access": "public" },
|
|
18
|
+
"scripts": { "build": "tsc -p tsconfig.build.json", "clean": "rm -rf dist" },
|
|
19
|
+
"dependencies": { "@tracepass/dpp-types": "^0.1.0" },
|
|
20
|
+
"devDependencies": { "typescript": "^5.6.3" },
|
|
21
|
+
"engines": { "node": ">=18" }
|
|
22
|
+
}
|