@pubid/pubid 0.1.23 → 0.1.25
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/artifact/corpus.json.gz +0 -0
- package/dist/flavors/iso/grammar.js +9 -1
- package/dist/flavors/iso/index.js +34 -6
- package/dist/flavors/iso/model.js +15 -18
- package/dist/flavors/nist/index.js +13 -0
- package/dist/model/identifier.d.ts +2 -0
- package/dist/model/identifier.js +4 -0
- package/dist/model/subset-match.d.ts +36 -0
- package/dist/model/subset-match.js +133 -0
- package/package.json +4 -2
- package/vendor/parg/iso.json +8510 -1
- package/vendor/parg/nist.json +9588 -1
package/artifact/corpus.json.gz
CHANGED
|
Binary file
|
|
@@ -118,6 +118,14 @@ function buildRules() {
|
|
|
118
118
|
rule("tc_type", () => literalAlternation(TC_TYPES).as("tc_type"));
|
|
119
119
|
rule("sc_type", () => str("SC/QC").or(str("SC")).as("sc_type"));
|
|
120
120
|
rule("wg_type", () => literalAlternation(WG_TYPES).as("wg_type"));
|
|
121
|
+
// Bare TC reference (pubid-iso#285): "184/SC 4/WG 12 N10291" — the
|
|
122
|
+
// ISO/TC route without the ORIGINATOR prefix; the SC segment is
|
|
123
|
+
// required so bare numbers cannot leak into the route.
|
|
124
|
+
rule("bare_tc_document", () => digits.as("tc_number")
|
|
125
|
+
.then(str("/"), rules["sc_type"], space, digits.as("sc_number"))
|
|
126
|
+
.then(str("/"), rules["wg_type"], space, digits.as("wg_number")).maybe()
|
|
127
|
+
.then(space, str("N"), space.maybe(), digits.as("number"))
|
|
128
|
+
.then(str(":"), yearDigits.as("year")).maybe());
|
|
121
129
|
rule("tc_subcommittee_part", () => str("/")
|
|
122
130
|
.then(rules["sc_type"], space, digits.as("sc_number"), str("/"), rules["wg_type"], space, digits.as("wg_number"))
|
|
123
131
|
.or(str("/").then(rules["sc_type"], space, digits.as("sc_number"))));
|
|
@@ -200,7 +208,7 @@ function buildRules() {
|
|
|
200
208
|
.as("base")
|
|
201
209
|
.then(space.maybe(), str("|"), space.maybe(), rules["idf_identifier"])
|
|
202
210
|
.as("joint_identifier"));
|
|
203
|
-
rule("identifier", () => rules["
|
|
211
|
+
rule("identifier", () => rules["bare_tc_document"].or(rules["nsb_iso_identifier"], rules["tc_document"], rules["directives_identifiers"], rules["jcgm_identifier"], rules["iso_r_supplement_identifier"], rules["iso_r_identifier"], rules["supplement_supplement_supplement_identifier"], rules["supplement_supplement_identifier"], rules["supplement_identifier"], rules["joint_identifier"], rules["identifier_copublishers"]));
|
|
204
212
|
rule("root", () => rules["identifier"]);
|
|
205
213
|
return rules;
|
|
206
214
|
}
|
|
@@ -147,6 +147,13 @@ class IsoBuilder {
|
|
|
147
147
|
}
|
|
148
148
|
if (tree["supplements"] !== undefined)
|
|
149
149
|
return this.buildBundled(tree);
|
|
150
|
+
// Bare TC reference (pubid-iso#285): tc_type is absent by
|
|
151
|
+
// construction; sc_type is the route's required segment and cannot
|
|
152
|
+
// appear on a standard identifier. Canonicalizes the publisher to ISO.
|
|
153
|
+
if (tree["tc_type"] === undefined && tree["sc_type"] !== undefined && tree["tc_number"] !== undefined) {
|
|
154
|
+
tree["tc_type"] = "TC";
|
|
155
|
+
return this.buildTc(tree);
|
|
156
|
+
}
|
|
150
157
|
if (tree["tc_type"] !== undefined)
|
|
151
158
|
return this.buildTc(tree);
|
|
152
159
|
return this.buildStandard(tree);
|
|
@@ -499,12 +506,25 @@ function parseIsoUrn(urn) {
|
|
|
499
506
|
else
|
|
500
507
|
supp.number = parts.shift();
|
|
501
508
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
509
|
+
// The legacy year-overloaded wire ("amd:2007:v2" -- the number rode
|
|
510
|
+
// the version slot): a v-token with no preceding number carries the
|
|
511
|
+
// NUMBER. The RFC 5141-bis order ("amd:1:2007:v2") keeps the number
|
|
512
|
+
// in its own slot, so a v-token after a number is the true
|
|
513
|
+
// <supplversion>.
|
|
514
|
+
const consumeV = () => {
|
|
515
|
+
if ((parts[0] ?? "").startsWith("v")) {
|
|
516
|
+
const value = parts.shift().slice(1).split(".")[0];
|
|
517
|
+
if (supp.number === undefined)
|
|
518
|
+
supp.number = value;
|
|
519
|
+
else
|
|
520
|
+
supp.version = value;
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
consumeV();
|
|
506
524
|
if (supp.year === undefined && /^\d{4}$/.test(parts[0] ?? ""))
|
|
507
525
|
supp.year = parts.shift();
|
|
526
|
+
// a v-token after the year would otherwise start a type-less wrap
|
|
527
|
+
consumeV();
|
|
508
528
|
if (parts[0] !== undefined && !/^\d+$/.test(parts[0]) &&
|
|
509
529
|
!/^(amd|cor|sup|add|v\d+|stage-)/i.test(parts[0])) {
|
|
510
530
|
supp.languages = parts.shift().split(",").map((l) => l.toUpperCase()).join("/");
|
|
@@ -570,10 +590,16 @@ function parseIsoUrn(urn) {
|
|
|
570
590
|
}
|
|
571
591
|
const suppTree = {};
|
|
572
592
|
if (supp.stage !== undefined) {
|
|
573
|
-
|
|
593
|
+
// A harmonized code resolves within the supplement's own type
|
|
594
|
+
// family -- the IS-family default would turn a staged Corrigendum
|
|
595
|
+
// into a plain IS (mirrors the gem's build_identifier).
|
|
596
|
+
const stage = /^\d\d\.\d\d$/.test(supp.stage)
|
|
597
|
+
? ISO_TYPED_STAGES.find((st) => st.harmonized?.includes(supp.stage) && st.typeKey === supp.type) ??
|
|
598
|
+
ISO_TYPED_STAGES.find((st) => st.harmonized?.includes(supp.stage) && st.typeKey === "is")
|
|
599
|
+
: ISO_TYPED_STAGES.find((st) => st.stageCode === supp.stage);
|
|
574
600
|
suppTree["type_with_stage"] = stage !== undefined
|
|
575
601
|
? stage.abbr[0] ?? ""
|
|
576
|
-
: supp.stage.toUpperCase();
|
|
602
|
+
: supp.type?.toUpperCase() ?? supp.stage.toUpperCase();
|
|
577
603
|
}
|
|
578
604
|
if (supp.type !== undefined && suppTree["type_with_stage"] === undefined) {
|
|
579
605
|
suppTree["type_with_stage"] = supp.type.toUpperCase();
|
|
@@ -582,6 +608,8 @@ function parseIsoUrn(urn) {
|
|
|
582
608
|
suppTree["number"] = supp.number;
|
|
583
609
|
if (supp.year !== undefined)
|
|
584
610
|
suppTree["date"] = supp.year;
|
|
611
|
+
if (supp.version !== undefined)
|
|
612
|
+
suppTree["supplement_version"] = supp.version;
|
|
585
613
|
if (supp.languages !== undefined)
|
|
586
614
|
suppTree["languages"] = supp.languages;
|
|
587
615
|
tree = { base: tree, ...suppTree };
|
|
@@ -465,14 +465,9 @@ class IsoUrnGenerator extends BaseUrnGenerator {
|
|
|
465
465
|
const stageCode = ts.entry.stageCode;
|
|
466
466
|
if (stageCode === "published")
|
|
467
467
|
return undefined;
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
if (stageAbbrUrn !== undefined) {
|
|
472
|
-
if (id.stage_iteration !== undefined)
|
|
473
|
-
return `${stageAbbrUrn}.${id.stage_iteration.render() ?? ""}`;
|
|
474
|
-
return stageAbbrUrn;
|
|
475
|
-
}
|
|
468
|
+
// RFC 5141 carries the stage as the harmonized code only; the typed
|
|
469
|
+
// abbreviations live in the human face. Legacy abbreviation URNs are
|
|
470
|
+
// accepted on ingestion and re-canonicalized here.
|
|
476
471
|
const harmonized = ts.entry.harmonized;
|
|
477
472
|
if (harmonized === undefined || harmonized.length === 0)
|
|
478
473
|
return undefined;
|
|
@@ -558,17 +553,15 @@ class IsoUrnGenerator extends BaseUrnGenerator {
|
|
|
558
553
|
parts.push(stageComp);
|
|
559
554
|
parts.push(supp.typeKey() === "suppl" || supp.typeKey() === "add" ? "sup" : supp.typeKey());
|
|
560
555
|
const iter = supp.typeKey() === "suppl" || supp.stage_iteration === undefined ? undefined : supp.stage_iteration.render();
|
|
556
|
+
// RFC 5141 supplement slots in order: number, year (the bis
|
|
557
|
+
// additive extension), then the published version.
|
|
558
|
+
if (supp.number !== undefined)
|
|
559
|
+
parts.push(supp.number);
|
|
561
560
|
const d = urnDate(supp);
|
|
562
|
-
if (d !== undefined)
|
|
561
|
+
if (d !== undefined)
|
|
563
562
|
parts.push(d);
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
}
|
|
567
|
-
else {
|
|
568
|
-
if (supp.number !== undefined)
|
|
569
|
-
parts.push(supp.number);
|
|
570
|
-
parts.push(iter !== undefined ? `v1.${iter}` : "v1");
|
|
571
|
-
}
|
|
563
|
+
const version = supp.supplement_version ?? 1;
|
|
564
|
+
parts.push(iter !== undefined ? `v${version}.${iter}` : `v${version}`);
|
|
572
565
|
if (supp.languages !== undefined && supp.languages.length > 0) {
|
|
573
566
|
parts.push(supp.languages.map((l) => l.code).join(","));
|
|
574
567
|
}
|
|
@@ -616,10 +609,14 @@ const supplementPublisherMappings = () => [
|
|
|
616
609
|
},
|
|
617
610
|
},
|
|
618
611
|
];
|
|
619
|
-
const SUPPLEMENT_MAPPINGS = keyValue(...ISO_MAPPINGS, ...supplementPublisherMappings(), stageMapping(), { wire: "base", to: "base" }
|
|
612
|
+
const SUPPLEMENT_MAPPINGS = keyValue(...ISO_MAPPINGS, ...supplementPublisherMappings(), stageMapping(), { wire: "base", to: "base" },
|
|
613
|
+
// omitted when unset, so parsed human faces keep their hashes
|
|
614
|
+
{ wire: "supplement_version", to: "supplement_version" });
|
|
620
615
|
export class IsoSupplementIdentifier extends IsoSingleIdentifier {
|
|
621
616
|
static attributes = extendAttributes(IsoSingleIdentifier, {
|
|
622
617
|
base: { type: IsoIdentifier },
|
|
618
|
+
// The RFC 5141 <supplversion> (corrected version of the supplement).
|
|
619
|
+
supplement_version: { type: "string" },
|
|
623
620
|
});
|
|
624
621
|
static mappings = SUPPLEMENT_MAPPINGS;
|
|
625
622
|
render() {
|
|
@@ -301,6 +301,14 @@ function cast(type, value, h, policy, series = "") {
|
|
|
301
301
|
}
|
|
302
302
|
case "part_number":
|
|
303
303
|
return s(value);
|
|
304
|
+
case "n_letter_part": {
|
|
305
|
+
// Part type "n" with a letter value (pubid#555): the "1nA" /
|
|
306
|
+
// "1-1nA" annex tokens capture whole, the caster strips the n.
|
|
307
|
+
const v = s(value);
|
|
308
|
+
if (v === undefined)
|
|
309
|
+
return undefined;
|
|
310
|
+
return { part: new NistPart({ type: "n", value: v.replace(/^n/, "").toUpperCase() }) };
|
|
311
|
+
}
|
|
304
312
|
case "letter_number": {
|
|
305
313
|
if (!isObj(value))
|
|
306
314
|
return undefined;
|
|
@@ -413,6 +421,11 @@ function cast(type, value, h, policy, series = "") {
|
|
|
413
421
|
case "appendix":
|
|
414
422
|
case "errata":
|
|
415
423
|
case "index":
|
|
424
|
+
// The component's value is the long form either way (pubid#584):
|
|
425
|
+
// both spellings parse, the render prints the abbreviated indx.
|
|
426
|
+
if (s(value) === "indx")
|
|
427
|
+
return "index";
|
|
428
|
+
return s(value);
|
|
416
429
|
case "insert":
|
|
417
430
|
return s(value);
|
|
418
431
|
case "revision": {
|
|
@@ -56,6 +56,8 @@ export declare abstract class BaseIdentifier {
|
|
|
56
56
|
/** Ruby to_urn: the flavor's UrnGenerator, else the base template. */
|
|
57
57
|
toUrn(): string;
|
|
58
58
|
fromHash(hash: Record<string, unknown>): BaseIdentifier;
|
|
59
|
+
/** SubsetMatch: an all-parts reference skips the part attributes. */
|
|
60
|
+
subsetAllPartsWildcard(): boolean;
|
|
59
61
|
protected classAttributes(): AttributeTable;
|
|
60
62
|
/** Wire key for an attribute: custom mapping or the attribute name. */
|
|
61
63
|
protected wireKeyFor(name: string): {
|
package/dist/model/identifier.js
CHANGED
|
@@ -104,6 +104,10 @@ export class BaseIdentifier {
|
|
|
104
104
|
fromHash(hash) {
|
|
105
105
|
return this.constructor.fromHash(hash);
|
|
106
106
|
}
|
|
107
|
+
/** SubsetMatch: an all-parts reference skips the part attributes. */
|
|
108
|
+
subsetAllPartsWildcard() {
|
|
109
|
+
return Boolean(this["all_parts"]);
|
|
110
|
+
}
|
|
107
111
|
classAttributes() {
|
|
108
112
|
return this.constructor.attributes;
|
|
109
113
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subset match: `subsetMatch(reference, candidate)` — the TypeScript port of
|
|
3
|
+
* Pubid::SubsetMatch (lib/pubid/subset_match.rb), the semantics behind Ruby's
|
|
4
|
+
* `Identifier#===`.
|
|
5
|
+
*
|
|
6
|
+
* The match is true when every part that `reference` states matches
|
|
7
|
+
* `candidate`. A part the reference leaves nil or empty is a wildcard. The
|
|
8
|
+
* operator is NOT symmetric: the receiver is the reference (`ISO 9001` is
|
|
9
|
+
* matched by `ISO 9001:2015`, not the other way round).
|
|
10
|
+
*
|
|
11
|
+
* Rules (see the Ruby module for the normative docs):
|
|
12
|
+
* - Both objects must be of the same concrete class; a reference never falls
|
|
13
|
+
* back to the base document of a wrapper.
|
|
14
|
+
* - A default value is stated: `ISO 9001` (published stage) does not match
|
|
15
|
+
* `ISO/DIS 9001`.
|
|
16
|
+
* - A collection matches by position, and the reference may be shorter
|
|
17
|
+
* (`ISO/IEC 9001` matches `ISO/IEC/IEEE 9001`; `ISO/IEEE 9001` does not).
|
|
18
|
+
* - A STRICT attribute (class `subsetStrict` declaration) is exempt from the
|
|
19
|
+
* wildcards: the reference always states it, a stated collection must match
|
|
20
|
+
* in full, and nested matches run in BOTH directions with the class's own
|
|
21
|
+
* rule.
|
|
22
|
+
* - A reference with `allParts` set skips part/parts/subpart/allParts.
|
|
23
|
+
* - Nested identifiers and components recurse with their own match; every
|
|
24
|
+
* other value compares with deep scalar equality.
|
|
25
|
+
*/
|
|
26
|
+
import { BaseIdentifier } from "./identifier.js";
|
|
27
|
+
import { Component } from "./component.js";
|
|
28
|
+
/** One attribute: theirs satisfies the reference value mine. */
|
|
29
|
+
export declare function valueMatch(mine: unknown, theirs: unknown): boolean;
|
|
30
|
+
/** Strict attributes compare exactly, composing nested matches both ways. */
|
|
31
|
+
export declare function exactMatch(mine: unknown, theirs: unknown): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* True when `candidate` holds every part that `reference` states. Both must
|
|
34
|
+
* be identifiers or components of the same concrete class.
|
|
35
|
+
*/
|
|
36
|
+
export declare function subsetMatch(reference: BaseIdentifier | Component, candidate: unknown): boolean;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subset match: `subsetMatch(reference, candidate)` — the TypeScript port of
|
|
3
|
+
* Pubid::SubsetMatch (lib/pubid/subset_match.rb), the semantics behind Ruby's
|
|
4
|
+
* `Identifier#===`.
|
|
5
|
+
*
|
|
6
|
+
* The match is true when every part that `reference` states matches
|
|
7
|
+
* `candidate`. A part the reference leaves nil or empty is a wildcard. The
|
|
8
|
+
* operator is NOT symmetric: the receiver is the reference (`ISO 9001` is
|
|
9
|
+
* matched by `ISO 9001:2015`, not the other way round).
|
|
10
|
+
*
|
|
11
|
+
* Rules (see the Ruby module for the normative docs):
|
|
12
|
+
* - Both objects must be of the same concrete class; a reference never falls
|
|
13
|
+
* back to the base document of a wrapper.
|
|
14
|
+
* - A default value is stated: `ISO 9001` (published stage) does not match
|
|
15
|
+
* `ISO/DIS 9001`.
|
|
16
|
+
* - A collection matches by position, and the reference may be shorter
|
|
17
|
+
* (`ISO/IEC 9001` matches `ISO/IEC/IEEE 9001`; `ISO/IEEE 9001` does not).
|
|
18
|
+
* - A STRICT attribute (class `subsetStrict` declaration) is exempt from the
|
|
19
|
+
* wildcards: the reference always states it, a stated collection must match
|
|
20
|
+
* in full, and nested matches run in BOTH directions with the class's own
|
|
21
|
+
* rule.
|
|
22
|
+
* - A reference with `allParts` set skips part/parts/subpart/allParts.
|
|
23
|
+
* - Nested identifiers and components recurse with their own match; every
|
|
24
|
+
* other value compares with deep scalar equality.
|
|
25
|
+
*/
|
|
26
|
+
import { BaseIdentifier } from "./identifier.js";
|
|
27
|
+
import { Component } from "./component.js";
|
|
28
|
+
/** Attributes an all-parts reference does not restrict. */
|
|
29
|
+
const ALL_PARTS_ATTRIBUTES = ["part", "parts", "subpart", "allParts"];
|
|
30
|
+
function isSubsetMatchable(v) {
|
|
31
|
+
return v instanceof BaseIdentifier || v instanceof Component;
|
|
32
|
+
}
|
|
33
|
+
/** True for undefined/null and for an empty string or collection. */
|
|
34
|
+
function blank(value) {
|
|
35
|
+
if (value === undefined || value === null)
|
|
36
|
+
return true;
|
|
37
|
+
if (typeof value === "string")
|
|
38
|
+
return value === "";
|
|
39
|
+
if (Array.isArray(value))
|
|
40
|
+
return value.length === 0;
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Exact equality for non-identifier values: scalars by identity, plain
|
|
45
|
+
* objects and arrays structurally (Ruby's `==` on the lutaml values).
|
|
46
|
+
*/
|
|
47
|
+
function scalarEqual(mine, theirs) {
|
|
48
|
+
if (mine === theirs)
|
|
49
|
+
return true;
|
|
50
|
+
if (Array.isArray(mine) && Array.isArray(theirs)) {
|
|
51
|
+
return mine.length === theirs.length && mine.every((v, i) => scalarEqual(v, theirs[i]));
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(mine) || Array.isArray(theirs))
|
|
54
|
+
return false;
|
|
55
|
+
if (mine !== null && theirs !== null &&
|
|
56
|
+
typeof mine === "object" && typeof theirs === "object") {
|
|
57
|
+
const a = mine;
|
|
58
|
+
const b = theirs;
|
|
59
|
+
const keys = Object.keys(a);
|
|
60
|
+
if (keys.length !== Object.keys(b).length)
|
|
61
|
+
return false;
|
|
62
|
+
return keys.every((k) => k in b && scalarEqual(a[k], b[k]));
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
/** A shorter reference collection matches the leading elements. */
|
|
67
|
+
function collectionMatch(mine, theirs) {
|
|
68
|
+
if (!Array.isArray(theirs))
|
|
69
|
+
return false;
|
|
70
|
+
return mine.length <= theirs.length && mine.every((v, i) => valueMatch(v, theirs[i]));
|
|
71
|
+
}
|
|
72
|
+
/** A strict collection matches in full, never as a prefix. */
|
|
73
|
+
function exactCollectionMatch(mine, theirs) {
|
|
74
|
+
if (!Array.isArray(theirs))
|
|
75
|
+
return false;
|
|
76
|
+
return mine.length === theirs.length && mine.every((v, i) => exactMatch(v, theirs[i]));
|
|
77
|
+
}
|
|
78
|
+
/** One attribute: theirs satisfies the reference value mine. */
|
|
79
|
+
export function valueMatch(mine, theirs) {
|
|
80
|
+
if (blank(mine))
|
|
81
|
+
return true;
|
|
82
|
+
if (Array.isArray(mine))
|
|
83
|
+
return collectionMatch(mine, theirs);
|
|
84
|
+
if (isSubsetMatchable(mine))
|
|
85
|
+
return subsetMatch(mine, theirs);
|
|
86
|
+
return scalarEqual(mine, theirs);
|
|
87
|
+
}
|
|
88
|
+
/** Strict attributes compare exactly, composing nested matches both ways. */
|
|
89
|
+
export function exactMatch(mine, theirs) {
|
|
90
|
+
if (blank(mine) && blank(theirs))
|
|
91
|
+
return true;
|
|
92
|
+
if (Array.isArray(mine))
|
|
93
|
+
return exactCollectionMatch(mine, theirs);
|
|
94
|
+
if (isSubsetMatchable(mine)) {
|
|
95
|
+
return subsetMatch(mine, theirs) && isSubsetMatchable(theirs) && subsetMatch(theirs, mine);
|
|
96
|
+
}
|
|
97
|
+
return scalarEqual(mine, theirs);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* True when `candidate` holds every part that `reference` states. Both must
|
|
101
|
+
* be identifiers or components of the same concrete class.
|
|
102
|
+
*/
|
|
103
|
+
export function subsetMatch(reference, candidate) {
|
|
104
|
+
if (reference === candidate)
|
|
105
|
+
return true;
|
|
106
|
+
if (!(candidate instanceof reference.constructor))
|
|
107
|
+
return false;
|
|
108
|
+
const ctor = reference.constructor;
|
|
109
|
+
// Components (and any class without a declared attribute table) match
|
|
110
|
+
// conservatively: equal serialized forms. A component that wants the
|
|
111
|
+
// wildcard/strict rules declares a `static attributes` table.
|
|
112
|
+
if (!ctor.attributes || typeof ctor.attributes !== "object") {
|
|
113
|
+
const a = reference.toWire?.() ?? null;
|
|
114
|
+
const b = candidate?.toWire?.() ?? null;
|
|
115
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
116
|
+
}
|
|
117
|
+
const strict = new Set(ctor.subsetStrict ?? []);
|
|
118
|
+
const ignored = new Set([
|
|
119
|
+
...(ctor.subsetIgnoredAttributes ?? []),
|
|
120
|
+
...(reference.subsetAllPartsWildcard?.()
|
|
121
|
+
? ALL_PARTS_ATTRIBUTES
|
|
122
|
+
: []),
|
|
123
|
+
]);
|
|
124
|
+
return Object.keys(ctor.attributes).every((name) => {
|
|
125
|
+
if (ignored.has(name))
|
|
126
|
+
return true;
|
|
127
|
+
const mine = reference[name];
|
|
128
|
+
const theirs = candidate[name];
|
|
129
|
+
if (strict.has(name))
|
|
130
|
+
return exactMatch(mine, theirs);
|
|
131
|
+
return valueMatch(mine, theirs);
|
|
132
|
+
});
|
|
133
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pubid/pubid",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"description": "TypeScript PubID identifiers: resolution, normalization and canonical hashes for every identifier in the pubid-testsuite corpus (41 standards bodies), with a conformance runner mirroring the Ruby reference",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"test": "tsc -p tsconfig.test.json && node --test dist-test/test/*.test.js",
|
|
15
15
|
"conformance": "tsc -p tsconfig.json && node dist/conformance/cli.js",
|
|
16
16
|
"prepublishOnly": "tsc -p tsconfig.json",
|
|
17
|
-
"artifact": "tsc -p tsconfig.json && node scripts/build-artifact.mjs"
|
|
17
|
+
"artifact": "tsc -p tsconfig.json && node scripts/build-artifact.mjs",
|
|
18
|
+
"doc:api": "node scripts/api-reference.mjs"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
21
|
"parsanol": "^0.10.4",
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@types/node": "^20.14.0",
|
|
26
|
+
"typedoc": "^0.28.20",
|
|
25
27
|
"typescript": "^5.6.0"
|
|
26
28
|
},
|
|
27
29
|
"files": [
|