@tracepass/dpp-validate 0.4.0 → 0.5.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/dist/index.d.ts CHANGED
@@ -13,6 +13,9 @@ export type { ConditionalRule } from "./rules.js";
13
13
  export { IN_SCOPE_BATTERY_CATEGORIES, BATTERY_FIELD_GATES, GATED_FIELD_KEYS, batteryFieldApplicability, gateForKey, } from "./battery-applicability.js";
14
14
  export type { Applicability, FieldGate } from "./battery-applicability.js";
15
15
  export { checkPublishReady } from "./vendor/publish-gate.js";
16
+ export { effectiveRequired, effectiveRequiredForBattery } from "./vendor/publish-gate.js";
17
+ export { isInScopeBatteryCategory } from "./vendor/battery-scope.js";
18
+ export { isFieldAbsent, isFieldValueAbsent } from "./vendor/field-emptiness.js";
16
19
  export { getPartyRoles, isRequiredRole, allRolesForCategory, CATEGORY_PARTY_ROLES } from "./vendor/required-roles.js";
17
20
  export type { CategoryKey } from "./vendor/required-roles.js";
18
21
  export { derivePassportCounts } from "./vendor/counts.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,WAAW,GACZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,gBAAgB,EAChB,yBAAyB,EACzB,UAAU,GACX,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAI3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACtH,YAAY,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,WAAW,GACZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,gBAAgB,EAChB,yBAAyB,EACzB,UAAU,GACX,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAI3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAC1F,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACtH,YAAY,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -12,6 +12,11 @@ export { IN_SCOPE_BATTERY_CATEGORIES, BATTERY_FIELD_GATES, GATED_FIELD_KEYS, bat
12
12
  // Re-exported because a passport's publish-readiness and required economic
13
13
  // operators are part of judging whether it is compliant, not separate concerns.
14
14
  export { checkPublishReady } from "./vendor/publish-gate.js";
15
+ // Exported alongside the gate: a caller rendering a form needs the same
16
+ // per-category answer the gate uses, or its asterisks disagree with its verdict.
17
+ export { effectiveRequired, effectiveRequiredForBattery } from "./vendor/publish-gate.js";
18
+ export { isInScopeBatteryCategory } from "./vendor/battery-scope.js";
19
+ export { isFieldAbsent, isFieldValueAbsent } from "./vendor/field-emptiness.js";
15
20
  export { getPartyRoles, isRequiredRole, allRolesForCategory, CATEGORY_PARTY_ROLES } from "./vendor/required-roles.js";
16
21
  export { derivePassportCounts } from "./vendor/counts.js";
17
22
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAWlD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,gBAAgB,EAChB,yBAAyB,EACzB,UAAU,GACX,MAAM,4BAA4B,CAAC;AAGpC,2EAA2E;AAC3E,gFAAgF;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAEtH,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAWlD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,gBAAgB,EAChB,yBAAyB,EACzB,UAAU,GACX,MAAM,4BAA4B,CAAC;AAGpC,2EAA2E;AAC3E,gFAAgF;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,wEAAwE;AACxE,iFAAiF;AACjF,OAAO,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAC1F,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAEtH,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Which battery categories are in scope for the EU battery passport at all.
3
+ *
4
+ * Art. 77 of Reg (EU) 2023/1542 hangs the passport on LMT, EV and industrial
5
+ * batteries only. Portable and SLI batteries are covered by the Regulation for
6
+ * other duties (labelling, collection, removability) but carry NO passport
7
+ * obligation, and neither does industrial ≤ 2 kWh. The Commission's own data-point
8
+ * guidance is scoped the same way — it enumerates applicability for "EV, LMT and
9
+ * industrial batteries" and never addresses the other three categories.
10
+ *
11
+ * The template's `batteryCategory` enum offers all six values because it also
12
+ * describes batteries we hold data about; the enum is NOT the scope list. Read
13
+ * this module to answer "is a passport due", never the enum.
14
+ *
15
+ * Lives on its own so the compliance engine and the publish gate can share one
16
+ * definition without importing each other. Both did carry their own copy, kept
17
+ * in agreement by hand.
18
+ */
19
+ /** Categories that owe a battery passport (BAT-1 / Art. 77(1)). */
20
+ export declare const IN_SCOPE_BATTERY_CATEGORIES: readonly ["LMT", "EV", "industrial_gt_2kwh"];
21
+ export type InScopeBatteryCategory = (typeof IN_SCOPE_BATTERY_CATEGORIES)[number];
22
+ /**
23
+ * True when `category` owes a battery passport.
24
+ *
25
+ * An unset category returns `false` — absence of a category is not evidence of
26
+ * an obligation. Callers that need to distinguish "out of scope" from "we don't
27
+ * know yet" must check for `undefined` themselves; BAT-1 does exactly that so it
28
+ * can raise `unverifiable_conditional` rather than silently passing.
29
+ */
30
+ export declare function isInScopeBatteryCategory(category: string | undefined | null): boolean;
31
+ //# sourceMappingURL=battery-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"battery-scope.d.ts","sourceRoot":"","sources":["../../src/vendor/battery-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,mEAAmE;AACnE,eAAO,MAAM,2BAA2B,8CAI9B,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAChC,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAC;AAI/C;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAClC,OAAO,CAET"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Which battery categories are in scope for the EU battery passport at all.
3
+ *
4
+ * Art. 77 of Reg (EU) 2023/1542 hangs the passport on LMT, EV and industrial
5
+ * batteries only. Portable and SLI batteries are covered by the Regulation for
6
+ * other duties (labelling, collection, removability) but carry NO passport
7
+ * obligation, and neither does industrial ≤ 2 kWh. The Commission's own data-point
8
+ * guidance is scoped the same way — it enumerates applicability for "EV, LMT and
9
+ * industrial batteries" and never addresses the other three categories.
10
+ *
11
+ * The template's `batteryCategory` enum offers all six values because it also
12
+ * describes batteries we hold data about; the enum is NOT the scope list. Read
13
+ * this module to answer "is a passport due", never the enum.
14
+ *
15
+ * Lives on its own so the compliance engine and the publish gate can share one
16
+ * definition without importing each other. Both did carry their own copy, kept
17
+ * in agreement by hand.
18
+ */
19
+ /** Categories that owe a battery passport (BAT-1 / Art. 77(1)). */
20
+ export const IN_SCOPE_BATTERY_CATEGORIES = [
21
+ "LMT",
22
+ "EV",
23
+ "industrial_gt_2kwh",
24
+ ];
25
+ const IN_SCOPE = new Set(IN_SCOPE_BATTERY_CATEGORIES);
26
+ /**
27
+ * True when `category` owes a battery passport.
28
+ *
29
+ * An unset category returns `false` — absence of a category is not evidence of
30
+ * an obligation. Callers that need to distinguish "out of scope" from "we don't
31
+ * know yet" must check for `undefined` themselves; BAT-1 does exactly that so it
32
+ * can raise `unverifiable_conditional` rather than silently passing.
33
+ */
34
+ export function isInScopeBatteryCategory(category) {
35
+ return category != null && IN_SCOPE.has(category);
36
+ }
37
+ //# sourceMappingURL=battery-scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"battery-scope.js","sourceRoot":"","sources":["../../src/vendor/battery-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,mEAAmE;AACnE,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,KAAK;IACL,IAAI;IACJ,oBAAoB;CACZ,CAAC;AAKX,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAS,2BAA2B,CAAC,CAAC;AAE9D;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CACtC,QAAmC;IAEnC,OAAO,QAAQ,IAAI,IAAI,IAAI,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACpD,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Is a passport field's value absent?
3
+ *
4
+ * This looks trivial and is not — the same question is asked in a dozen places
5
+ * (publish gate, compliance verdict, bulk approve, gap reports, the extraction
6
+ * pipeline, the review UI) and getting it subtly wrong has already cost real
7
+ * data twice.
8
+ *
9
+ * ── An empty ARRAY is an answer, not a gap ──
10
+ *
11
+ * `hazardousSubstances: []` asserts "we checked, there are none" — a positive
12
+ * compliance claim, the same kind as `svhcPresent: false`. Treating it as
13
+ * absent discards a declared finding and re-queues settled work.
14
+ *
15
+ * The trap is that **`String([]) === ""`**. Any check written as
16
+ * `String(v).trim() === ""` therefore swallows every empty array silently. A
17
+ * repair pass built that way flagged 60 of them, two on PUBLISHED passports,
18
+ * and would have wiped genuine "none apply" declarations.
19
+ *
20
+ * ── `false` and `0` are values ──
21
+ *
22
+ * `svhcPresent: false` is the ECHA register's actual answer.
23
+ * `recycledContentPercent: 0` is a measurement. `if (!value)` discards both,
24
+ * which is why that shortcut is never correct here.
25
+ *
26
+ * ── Whitespace is absent ──
27
+ *
28
+ * `" "` is what an empty form field or a trimmed-to-nothing extraction
29
+ * leaves behind. The long-standing inline checks used `value === ""` and so
30
+ * counted a whitespace string as filled — enough to satisfy a required field
31
+ * in the publish gate.
32
+ */
33
+ /**
34
+ * True when the field holds no value.
35
+ *
36
+ * Absent: `null`, `undefined`, `""`, whitespace-only strings.
37
+ * Present: `false`, `0`, `[]`, `{}`, and every non-blank scalar.
38
+ *
39
+ * Note `[]` and `{}` count as PRESENT. An empty collection is a declaration
40
+ * that the collection is empty; callers that need "no entries" should test
41
+ * `.length` themselves, which reads as the different question it is.
42
+ */
43
+ export declare function isFieldValueAbsent(value: unknown): boolean;
44
+ /** Convenience for the common `!field || absent(field.value)` shape. */
45
+ export declare function isFieldAbsent(field: {
46
+ value?: unknown;
47
+ } | undefined | null): boolean;
48
+ //# sourceMappingURL=field-emptiness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-emptiness.d.ts","sourceRoot":"","sources":["../../src/vendor/field-emptiness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAI1D;AAED,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAEpF"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Is a passport field's value absent?
3
+ *
4
+ * This looks trivial and is not — the same question is asked in a dozen places
5
+ * (publish gate, compliance verdict, bulk approve, gap reports, the extraction
6
+ * pipeline, the review UI) and getting it subtly wrong has already cost real
7
+ * data twice.
8
+ *
9
+ * ── An empty ARRAY is an answer, not a gap ──
10
+ *
11
+ * `hazardousSubstances: []` asserts "we checked, there are none" — a positive
12
+ * compliance claim, the same kind as `svhcPresent: false`. Treating it as
13
+ * absent discards a declared finding and re-queues settled work.
14
+ *
15
+ * The trap is that **`String([]) === ""`**. Any check written as
16
+ * `String(v).trim() === ""` therefore swallows every empty array silently. A
17
+ * repair pass built that way flagged 60 of them, two on PUBLISHED passports,
18
+ * and would have wiped genuine "none apply" declarations.
19
+ *
20
+ * ── `false` and `0` are values ──
21
+ *
22
+ * `svhcPresent: false` is the ECHA register's actual answer.
23
+ * `recycledContentPercent: 0` is a measurement. `if (!value)` discards both,
24
+ * which is why that shortcut is never correct here.
25
+ *
26
+ * ── Whitespace is absent ──
27
+ *
28
+ * `" "` is what an empty form field or a trimmed-to-nothing extraction
29
+ * leaves behind. The long-standing inline checks used `value === ""` and so
30
+ * counted a whitespace string as filled — enough to satisfy a required field
31
+ * in the publish gate.
32
+ */
33
+ /**
34
+ * True when the field holds no value.
35
+ *
36
+ * Absent: `null`, `undefined`, `""`, whitespace-only strings.
37
+ * Present: `false`, `0`, `[]`, `{}`, and every non-blank scalar.
38
+ *
39
+ * Note `[]` and `{}` count as PRESENT. An empty collection is a declaration
40
+ * that the collection is empty; callers that need "no entries" should test
41
+ * `.length` themselves, which reads as the different question it is.
42
+ */
43
+ export function isFieldValueAbsent(value) {
44
+ if (value === null || value === undefined)
45
+ return true;
46
+ if (typeof value === "string")
47
+ return value.trim() === "";
48
+ return false;
49
+ }
50
+ /** Convenience for the common `!field || absent(field.value)` shape. */
51
+ export function isFieldAbsent(field) {
52
+ return !field || isFieldValueAbsent(field.value);
53
+ }
54
+ //# sourceMappingURL=field-emptiness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-emptiness.js","sourceRoot":"","sources":["../../src/vendor/field-emptiness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IAC1D,OAAO,KAAK,CAAC;AACf,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,aAAa,CAAC,KAA6C;IACzE,OAAO,CAAC,KAAK,IAAI,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACnD,CAAC"}
@@ -6,8 +6,19 @@
6
6
  *
7
7
  * One rule, one implementation. If the product policy for publishing
8
8
  * changes (e.g. "warnings block", "min coverage %"), change it here.
9
+ *
10
+ * ⚠️ MIRRORED INTO THE PUBLIC npm PACKAGE @tracepass/dpp-validate.
11
+ * `tracepass-open/packages/dpp-validate/src/vendor/publish-gate.ts` is a hand-copy of this
12
+ * file — nothing generates one from the other, and a stale copy fails silently
13
+ * (it still compiles and still returns a plausible answer). tracepass-open
14
+ * gates on a hash of this file in CI, so after changing it:
15
+ *
16
+ * cd ../tracepass-open && node scripts/build-mirror-manifest.mjs \
17
+ * && npm run check:mirrors
18
+ *
19
+ * and commit `mirror-manifest.json` with the code.
9
20
  */
10
- import type { Passport, Template } from "@tracepass/dpp-types";
21
+ import type { Passport, Template, TemplateField } from "@tracepass/dpp-types";
11
22
  export interface PublishCheck {
12
23
  ready: boolean;
13
24
  /** Field keys that have no value set. */
@@ -27,13 +38,70 @@ export interface PublishCheck {
27
38
  */
28
39
  blockerType?: "hard" | "gap";
29
40
  }
41
+ /**
42
+ * Resolves whether a template field is effectively required for a specific
43
+ * battery category:
44
+ *
45
+ * 1. No `requiredBy` map → fall back to `validation.required`.
46
+ * This is the backward-compat path: ALL 11 non-battery templates and
47
+ * any battery field without a `requiredBy` entry behave exactly as before.
48
+ * 2. Category present in the map → "required" is true; "conditional" and
49
+ * "notApplicable" are false.
50
+ * 3. Category absent from the map, or unset on the passport → base required.
51
+ *
52
+ * `requiredBy` maps are keyed ONLY by the three in-scope categories, so step 3
53
+ * is what a portable or SLI battery would otherwise land on — see
54
+ * `effectiveRequiredForBattery`, which callers holding a battery passport should
55
+ * use instead.
56
+ *
57
+ * @param tf A template field (from the template's `fields` array).
58
+ * @param category The resolved `batteryCategory` value from the passport, or
59
+ * `undefined` when the field is unset.
60
+ */
61
+ export declare function effectiveRequired(tf: Pick<TemplateField, "validation">, category: string | undefined): boolean;
62
+ /**
63
+ * Battery-aware wrapper around `effectiveRequired`.
64
+ *
65
+ * A portable, SLI or industrial-≤2 kWh battery owes NO battery passport at all
66
+ * (Art. 77(1) — see `isInScopeBatteryCategory`), so no Annex XIII field is
67
+ * mandatory for it. Resolving those categories through `effectiveRequired`
68
+ * alone lands on the base `required` flag, which would demand the full
69
+ * category-agnostic field set from a battery that has no passport obligation —
70
+ * an obligation we would be inventing.
71
+ *
72
+ * An UNSET category still falls through to the base flag. That is deliberate:
73
+ * absence of a category is not evidence of exemption, and treating "not yet
74
+ * filled in" as "nothing required" would let a genuine EV battery publish empty.
75
+ * BAT-1 raises a warning for the unset case instead.
76
+ *
77
+ * Non-battery templates never reach the scope branch — they carry no
78
+ * `batteryCategory`, so `category` is `undefined` and behaviour is unchanged.
79
+ */
80
+ export declare function effectiveRequiredForBattery(tf: Pick<TemplateField, "validation">, category: string | undefined): boolean;
81
+ /**
82
+ * True when the template EXPLICITLY marks this field `notApplicable` for the
83
+ * given category — i.e. the Regulation says it must not be filled or displayed.
84
+ *
85
+ * Distinct from "not required" in the way that matters for a compliance
86
+ * surface: a `conditional` field may legitimately be filled and simply isn't
87
+ * mandatory, whereas a `notApplicable` one carries an instruction NOT to report
88
+ * it (the Annex XIII 4(b) state-of-health exclusions are the live example — an
89
+ * EV battery must leave the granular metrics blank and an LMT battery must
90
+ * leave SOCE blank). Rendering those as "not provided" reads as missing data
91
+ * when the truth is that there is nothing to provide.
92
+ *
93
+ * Returns false when the category is unset or the field carries no map —
94
+ * absence of information is never grounds for asserting non-applicability.
95
+ */
96
+ export declare function notApplicableForCategory(tf: Pick<TemplateField, "validation">, category: string | undefined): boolean;
30
97
  /**
31
98
  * Returns whether `passport` satisfies every publish precondition given
32
99
  * its `template`. A passport is publish-ready iff:
33
100
  * - status is in PUBLISHABLE_STATUSES (already-published + terminal
34
101
  * states return ready:false with a specific reason),
35
- * - every template field with validation.required has a non-empty
36
- * value AND status === "approved".
102
+ * - every template field whose effective required flag (resolved via
103
+ * `effectiveRequired` — which consults `requiredBy` when present) is
104
+ * true has a non-empty value AND status === "approved".
37
105
  *
38
106
  * `undefined` template means we can't evaluate — treat as not-ready.
39
107
  */
@@ -1 +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"}
1
+ {"version":3,"file":"publish-gate.d.ts","sourceRoot":"","sources":["../../src/vendor/publish-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI9E,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;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAUT;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,2BAA2B,CACzC,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAKT;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAIT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAC7B,YAAY,CA6Ed"}
@@ -6,19 +6,107 @@
6
6
  *
7
7
  * One rule, one implementation. If the product policy for publishing
8
8
  * changes (e.g. "warnings block", "min coverage %"), change it here.
9
+ *
10
+ * ⚠️ MIRRORED INTO THE PUBLIC npm PACKAGE @tracepass/dpp-validate.
11
+ * `tracepass-open/packages/dpp-validate/src/vendor/publish-gate.ts` is a hand-copy of this
12
+ * file — nothing generates one from the other, and a stale copy fails silently
13
+ * (it still compiles and still returns a plausible answer). tracepass-open
14
+ * gates on a hash of this file in CI, so after changing it:
15
+ *
16
+ * cd ../tracepass-open && node scripts/build-mirror-manifest.mjs \
17
+ * && npm run check:mirrors
18
+ *
19
+ * and commit `mirror-manifest.json` with the code.
9
20
  */
21
+ import { isInScopeBatteryCategory } from "./battery-scope.js";
22
+ import { isFieldAbsent } from "./field-emptiness.js";
10
23
  const PUBLISHABLE_STATUSES = new Set([
11
24
  "draft",
12
25
  "in_review",
13
26
  "approved",
14
27
  ]);
28
+ /**
29
+ * Resolves whether a template field is effectively required for a specific
30
+ * battery category:
31
+ *
32
+ * 1. No `requiredBy` map → fall back to `validation.required`.
33
+ * This is the backward-compat path: ALL 11 non-battery templates and
34
+ * any battery field without a `requiredBy` entry behave exactly as before.
35
+ * 2. Category present in the map → "required" is true; "conditional" and
36
+ * "notApplicable" are false.
37
+ * 3. Category absent from the map, or unset on the passport → base required.
38
+ *
39
+ * `requiredBy` maps are keyed ONLY by the three in-scope categories, so step 3
40
+ * is what a portable or SLI battery would otherwise land on — see
41
+ * `effectiveRequiredForBattery`, which callers holding a battery passport should
42
+ * use instead.
43
+ *
44
+ * @param tf A template field (from the template's `fields` array).
45
+ * @param category The resolved `batteryCategory` value from the passport, or
46
+ * `undefined` when the field is unset.
47
+ */
48
+ export function effectiveRequired(tf, category) {
49
+ const { requiredBy, required } = tf.validation;
50
+ // Path 1 & 3: no map, or category not a key in the map → base required.
51
+ if (!requiredBy || category === undefined || !(category in requiredBy)) {
52
+ return required;
53
+ }
54
+ // Path 2: explicit per-category applicability.
55
+ return requiredBy[category] === "required";
56
+ }
57
+ /**
58
+ * Battery-aware wrapper around `effectiveRequired`.
59
+ *
60
+ * A portable, SLI or industrial-≤2 kWh battery owes NO battery passport at all
61
+ * (Art. 77(1) — see `isInScopeBatteryCategory`), so no Annex XIII field is
62
+ * mandatory for it. Resolving those categories through `effectiveRequired`
63
+ * alone lands on the base `required` flag, which would demand the full
64
+ * category-agnostic field set from a battery that has no passport obligation —
65
+ * an obligation we would be inventing.
66
+ *
67
+ * An UNSET category still falls through to the base flag. That is deliberate:
68
+ * absence of a category is not evidence of exemption, and treating "not yet
69
+ * filled in" as "nothing required" would let a genuine EV battery publish empty.
70
+ * BAT-1 raises a warning for the unset case instead.
71
+ *
72
+ * Non-battery templates never reach the scope branch — they carry no
73
+ * `batteryCategory`, so `category` is `undefined` and behaviour is unchanged.
74
+ */
75
+ export function effectiveRequiredForBattery(tf, category) {
76
+ if (category !== undefined && !isInScopeBatteryCategory(category)) {
77
+ return false;
78
+ }
79
+ return effectiveRequired(tf, category);
80
+ }
81
+ /**
82
+ * True when the template EXPLICITLY marks this field `notApplicable` for the
83
+ * given category — i.e. the Regulation says it must not be filled or displayed.
84
+ *
85
+ * Distinct from "not required" in the way that matters for a compliance
86
+ * surface: a `conditional` field may legitimately be filled and simply isn't
87
+ * mandatory, whereas a `notApplicable` one carries an instruction NOT to report
88
+ * it (the Annex XIII 4(b) state-of-health exclusions are the live example — an
89
+ * EV battery must leave the granular metrics blank and an LMT battery must
90
+ * leave SOCE blank). Rendering those as "not provided" reads as missing data
91
+ * when the truth is that there is nothing to provide.
92
+ *
93
+ * Returns false when the category is unset or the field carries no map —
94
+ * absence of information is never grounds for asserting non-applicability.
95
+ */
96
+ export function notApplicableForCategory(tf, category) {
97
+ const { requiredBy } = tf.validation;
98
+ if (!requiredBy || category === undefined)
99
+ return false;
100
+ return requiredBy[category] === "notApplicable";
101
+ }
15
102
  /**
16
103
  * Returns whether `passport` satisfies every publish precondition given
17
104
  * its `template`. A passport is publish-ready iff:
18
105
  * - status is in PUBLISHABLE_STATUSES (already-published + terminal
19
106
  * states return ready:false with a specific reason),
20
- * - every template field with validation.required has a non-empty
21
- * value AND status === "approved".
107
+ * - every template field whose effective required flag (resolved via
108
+ * `effectiveRequired` — which consults `requiredBy` when present) is
109
+ * true has a non-empty value AND status === "approved".
22
110
  *
23
111
  * `undefined` template means we can't evaluate — treat as not-ready.
24
112
  */
@@ -50,16 +138,29 @@ export function checkPublishReady(passport, template) {
50
138
  blockerType: "hard",
51
139
  };
52
140
  }
141
+ // Read the passport's battery category so effectiveRequired can resolve
142
+ // per-category applicability. For non-battery passports (no batteryCategory
143
+ // field) this is always undefined and effectiveRequired falls back to the
144
+ // plain `required` boolean — no behaviour change for other templates.
145
+ const categoryField = passport.fields["batteryCategory"];
146
+ const category = categoryField && categoryField.value != null && categoryField.value !== ""
147
+ ? String(categoryField.value)
148
+ : undefined;
53
149
  const missingFields = [];
54
150
  const unapprovedFields = [];
55
151
  for (const tf of template.fields) {
56
- if (!tf.validation.required)
152
+ // `anticipated` fields rest on a delegated act that has not been adopted,
153
+ // so no instrument in force compels the data and their absence cannot block
154
+ // publication. Skip them before the required check.
155
+ if (tf.validation.anticipated)
156
+ continue;
157
+ if (!effectiveRequiredForBattery(tf, category))
57
158
  continue;
58
159
  const f = passport.fields[tf.key];
59
- if (!f ||
60
- f.value === null ||
61
- f.value === undefined ||
62
- f.value === "") {
160
+ // Shared helper: `[]` stays a valid answer ("none apply"), while a
161
+ // whitespace-only string now counts as missing — the inline `=== ""` it
162
+ // replaces let " " satisfy a required field and publish a blank.
163
+ if (isFieldAbsent(f)) {
63
164
  missingFields.push(tf.key);
64
165
  }
65
166
  else if (f.status !== "approved") {
@@ -1 +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"}
1
+ {"version":3,"file":"publish-gate.js","sourceRoot":"","sources":["../../src/vendor/publish-gate.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tracepass/dpp-validate",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
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
5
  "keywords": [
6
6
  "digital-product-passport",
@@ -45,7 +45,7 @@
45
45
  "clean": "rm -rf dist"
46
46
  },
47
47
  "dependencies": {
48
- "@tracepass/dpp-types": "^0.4.0"
48
+ "@tracepass/dpp-types": "^0.5.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "typescript": "^5.6.3"