@bsv/dpp-profiles 0.3.0-beta.1

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.
Files changed (96) hide show
  1. package/LICENSE +58 -0
  2. package/README.md +64 -0
  3. package/dist/applicability.d.ts +96 -0
  4. package/dist/applicability.d.ts.map +1 -0
  5. package/dist/applicability.js +181 -0
  6. package/dist/applicability.js.map +1 -0
  7. package/dist/combinations.d.ts +30 -0
  8. package/dist/combinations.d.ts.map +1 -0
  9. package/dist/combinations.js +103 -0
  10. package/dist/combinations.js.map +1 -0
  11. package/dist/evidence-shapes.d.ts +198 -0
  12. package/dist/evidence-shapes.d.ts.map +1 -0
  13. package/dist/evidence-shapes.js +123 -0
  14. package/dist/evidence-shapes.js.map +1 -0
  15. package/dist/gs1-epc-binary.d.ts +81 -0
  16. package/dist/gs1-epc-binary.d.ts.map +1 -0
  17. package/dist/gs1-epc-binary.js +252 -0
  18. package/dist/gs1-epc-binary.js.map +1 -0
  19. package/dist/gs1-resolution.d.ts +392 -0
  20. package/dist/gs1-resolution.d.ts.map +1 -0
  21. package/dist/gs1-resolution.js +696 -0
  22. package/dist/gs1-resolution.js.map +1 -0
  23. package/dist/identifiers.d.ts +57 -0
  24. package/dist/identifiers.d.ts.map +1 -0
  25. package/dist/identifiers.js +117 -0
  26. package/dist/identifiers.js.map +1 -0
  27. package/dist/index.d.ts +235 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +124 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/manifest-v2.d.ts +87 -0
  32. package/dist/manifest-v2.d.ts.map +1 -0
  33. package/dist/manifest-v2.js +31 -0
  34. package/dist/manifest-v2.js.map +1 -0
  35. package/dist/mapping.d.ts +84 -0
  36. package/dist/mapping.d.ts.map +1 -0
  37. package/dist/mapping.js +88 -0
  38. package/dist/mapping.js.map +1 -0
  39. package/dist/projections.d.ts +233 -0
  40. package/dist/projections.d.ts.map +1 -0
  41. package/dist/projections.js +466 -0
  42. package/dist/projections.js.map +1 -0
  43. package/frozen.json +42 -0
  44. package/generated/consumer/battery-v2.json +2248 -0
  45. package/generated/consumer/battery-v3.json +4354 -0
  46. package/generated/consumer/general-v1.json +9 -0
  47. package/generated/consumer/general-v2.json +714 -0
  48. package/generated/consumer/textile-v1.json +101 -0
  49. package/generated/consumer/textile-v2.json +1033 -0
  50. package/generated/consumer/textile-v3.json +2011 -0
  51. package/generated/index.json +86 -0
  52. package/generated/mapping/battery@2.md +116 -0
  53. package/generated/mapping/battery@3.md +130 -0
  54. package/generated/mapping/general@2.md +50 -0
  55. package/generated/mapping/textile@2.md +61 -0
  56. package/generated/mapping/textile@3.md +73 -0
  57. package/generated/payload-schema/battery@2.public.schema.json +609 -0
  58. package/generated/payload-schema/battery@2.restricted.schema.json +377 -0
  59. package/generated/payload-schema/battery@3.public.schema.json +1364 -0
  60. package/generated/payload-schema/battery@3.restricted.schema.json +1097 -0
  61. package/generated/payload-schema/general@1.public.schema.json +37 -0
  62. package/generated/payload-schema/general@1.restricted.schema.json +9 -0
  63. package/generated/payload-schema/general@2.public.schema.json +352 -0
  64. package/generated/payload-schema/general@2.restricted.schema.json +52 -0
  65. package/generated/payload-schema/textile@1.public.schema.json +82 -0
  66. package/generated/payload-schema/textile@1.restricted.schema.json +22 -0
  67. package/generated/payload-schema/textile@2.public.schema.json +464 -0
  68. package/generated/payload-schema/textile@2.restricted.schema.json +78 -0
  69. package/generated/payload-schema/textile@3.public.schema.json +1299 -0
  70. package/generated/payload-schema/textile@3.restricted.schema.json +78 -0
  71. package/manifests/battery@2.json +4047 -0
  72. package/manifests/battery@3.json +6164 -0
  73. package/manifests/exchange/untp-0.7.0-jose@1.json +191 -0
  74. package/manifests/exchange/vc-di-ecdsa-rdfc-2019@1.json +74 -0
  75. package/manifests/exchange/vsc-draft-compat@0.1.0.json +64 -0
  76. package/manifests/general@1.json +148 -0
  77. package/manifests/general@2.json +1266 -0
  78. package/manifests/interoperability/epcis-json@1.json +69 -0
  79. package/manifests/interoperability/epcis-vsc@1.json +64 -0
  80. package/manifests/interoperability/gs1-digital-link@1.json +71 -0
  81. package/manifests/operator/federated-operators@1.json +54 -0
  82. package/manifests/operator/single-operator@1.json +52 -0
  83. package/manifests/textile@1.json +383 -0
  84. package/manifests/textile@2.json +1682 -0
  85. package/manifests/textile@3.json +2763 -0
  86. package/package.json +59 -0
  87. package/schemas/exchange-profile.schema.json +134 -0
  88. package/schemas/gs1/NOTICE.md +11 -0
  89. package/schemas/gs1/resolver-description-file-schema-1.2.0.json +124 -0
  90. package/schemas/gs1/resolver-linkset-context-1.2.1.jsonld +39 -0
  91. package/schemas/gs1/resolver-linkset-schema-1.2.1.json +96 -0
  92. package/schemas/interoperability-profile.schema.json +347 -0
  93. package/schemas/operator-profile.schema.json +108 -0
  94. package/schemas/product-identity.schema.json +45 -0
  95. package/schemas/profile-manifest-v2.schema.json +1087 -0
  96. package/schemas/profile-manifest.schema.json +291 -0
@@ -0,0 +1,252 @@
1
+ /**
2
+ * EPC binary strings, decoded for the one scope the discovery profile
3
+ * declares (`spec/gs1-discovery.md` §3): a serialised trade item under the
4
+ * GS1 EPC Tag Data Standard's SGTIN-96 and SGTIN-198 schemes. A compressed GS1
5
+ * Digital Link URI whose compression string begins `eh` or `ex` carries such a
6
+ * string (GS1 Digital Link URI: Compression Technical Standard for EPC binary
7
+ * strings, release 1.0.0, July 2025, §4.1 and §4.2), and a GS1-Conformant
8
+ * Resolver must decompress it (GS1-Conformant Resolver 1.2.1 §2.3).
9
+ *
10
+ * Everything here is arithmetic over a bit string: the two alphabets of §4.1
11
+ * and §4.2, the SGTIN partition table of the Tag Data Standard, the 38-bit
12
+ * integer serial of SGTIN-96 and the twenty seven-bit characters of SGTIN-198.
13
+ * Nothing is guessed: a header this module does not implement, a company
14
+ * prefix or item reference outside its partition's digit count, a serial with
15
+ * a character the GS1 encodable set 82 does not contain, or a string of the
16
+ * wrong length is refused by name. A decoding establishes what the bits say
17
+ * and no more; it is not evidence that the GTIN was allocated to anyone.
18
+ */
19
+ import { gs1CheckDigit } from './identifiers.js';
20
+ /** The EPC header of the SGTIN-96 scheme (Tag Data Standard). */
21
+ export const SGTIN_96_HEADER = 0x30;
22
+ /** The EPC header of the SGTIN-198 scheme (Tag Data Standard). */
23
+ export const SGTIN_198_HEADER = 0x36;
24
+ /** The file-safe, URI-safe base 64 alphabet of the compression standard §4.1 (RFC 4648 §5). */
25
+ export const URI_SAFE_BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
26
+ /**
27
+ * The SGTIN partition table of the Tag Data Standard: for each partition
28
+ * value, the bits and digits of the GS1 Company Prefix and of the item
29
+ * reference (indicator digit included). The two digit counts always sum to
30
+ * thirteen, so the GTIN-14 is the indicator digit, the company prefix, the
31
+ * rest of the item reference and a check digit.
32
+ */
33
+ export const SGTIN_PARTITIONS = [
34
+ { companyPrefixBits: 40, companyPrefixDigits: 12, itemReferenceBits: 4, itemReferenceDigits: 1 },
35
+ { companyPrefixBits: 37, companyPrefixDigits: 11, itemReferenceBits: 7, itemReferenceDigits: 2 },
36
+ { companyPrefixBits: 34, companyPrefixDigits: 10, itemReferenceBits: 10, itemReferenceDigits: 3 },
37
+ { companyPrefixBits: 30, companyPrefixDigits: 9, itemReferenceBits: 14, itemReferenceDigits: 4 },
38
+ { companyPrefixBits: 27, companyPrefixDigits: 8, itemReferenceBits: 17, itemReferenceDigits: 5 },
39
+ { companyPrefixBits: 24, companyPrefixDigits: 7, itemReferenceBits: 20, itemReferenceDigits: 6 },
40
+ { companyPrefixBits: 20, companyPrefixDigits: 6, itemReferenceBits: 24, itemReferenceDigits: 7 },
41
+ ];
42
+ /** The GS1 AI encodable character set 82, which the SGTIN-198 serial and every Digital Link qualifier value draw from. */
43
+ export const GS1_CHARACTER_SET_82 = /^[!"%&'()*+,\-./0-9:;<=>?A-Z_a-z]+$/;
44
+ const refuse = (reason, detail, header) => ({ ok: false, reason, detail, ...(header == null ? {} : { header }) });
45
+ /** A string of `0` and `1` from lower-case hexadecimal, four bits per character (compression standard §4.2.1). */
46
+ export function bitsFromHex(hex) {
47
+ if (hex.length === 0 || !/^[0-9a-f]+$/.test(hex))
48
+ return undefined;
49
+ let bits = '';
50
+ for (const character of hex)
51
+ bits += parseInt(character, 16).toString(2).padStart(4, '0');
52
+ return bits;
53
+ }
54
+ /** A string of `0` and `1` from the URI-safe base 64 alphabet, six bits per character (compression standard §4.1). */
55
+ export function bitsFromUriSafeBase64(text) {
56
+ if (text.length === 0)
57
+ return undefined;
58
+ let bits = '';
59
+ for (const character of text) {
60
+ const index = URI_SAFE_BASE64_ALPHABET.indexOf(character);
61
+ if (index === -1)
62
+ return undefined;
63
+ bits += index.toString(2).padStart(6, '0');
64
+ }
65
+ return bits;
66
+ }
67
+ function integerOf(bits) {
68
+ let value = 0n;
69
+ for (const bit of bits)
70
+ value = (value << 1n) | (bit === '1' ? 1n : 0n);
71
+ return value;
72
+ }
73
+ /**
74
+ * The serial of an SGTIN-198: twenty seven-bit characters, the string ending
75
+ * at the first all-zero character, everything after it required to be zero
76
+ * padding. The Tag Data Standard draws the characters from the encodable set
77
+ * 82; a character outside it, or an empty serial, is refused.
78
+ */
79
+ function decodeSerial198(bits) {
80
+ let serial = '';
81
+ let ended = false;
82
+ for (let index = 0; index < 20; index++) {
83
+ const code = Number(integerOf(bits.slice(index * 7, index * 7 + 7)));
84
+ if (code === 0) {
85
+ ended = true;
86
+ continue;
87
+ }
88
+ if (ended)
89
+ return { error: 'a character follows the serial terminator' };
90
+ const character = String.fromCharCode(code);
91
+ if (!GS1_CHARACTER_SET_82.test(character))
92
+ return { error: `character code ${code} is outside the GS1 encodable set 82` };
93
+ serial += character;
94
+ }
95
+ if (serial.length === 0)
96
+ return { error: 'the serial is empty' };
97
+ return { serial };
98
+ }
99
+ /** The Tag Data Standard's percent-escaping of the pure-identity URI's serial component. */
100
+ function escapeSerialForUri(serial) {
101
+ return serial.replace(/[%"&/<>?#]/g, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0')}`);
102
+ }
103
+ /**
104
+ * Decode an EPC binary string, given as bits, under the SGTIN-96 and SGTIN-198
105
+ * schemes. The bit count must be exactly the scheme's length; a hexadecimal
106
+ * encoding of a 198-bit string carries two padding bits, which must be zero.
107
+ */
108
+ export function decodeEpcBinaryBits(bits) {
109
+ if (bits.length < 8)
110
+ return refuse('length', `${bits.length} bits cannot carry an EPC header`);
111
+ const header = Number(integerOf(bits.slice(0, 8)));
112
+ const headerHex = header.toString(16).padStart(2, '0');
113
+ let scheme;
114
+ let serialBits;
115
+ if (header === SGTIN_96_HEADER) {
116
+ scheme = 'sgtin-96';
117
+ serialBits = 38;
118
+ if (bits.length !== 96)
119
+ return refuse('length', `SGTIN-96 is 96 bits; ${bits.length} were given`, headerHex);
120
+ }
121
+ else if (header === SGTIN_198_HEADER) {
122
+ scheme = 'sgtin-198';
123
+ serialBits = 140;
124
+ if (bits.length === 200) {
125
+ if (bits.slice(198) !== '00')
126
+ return refuse('length', 'the two padding bits after an SGTIN-198 must be zero', headerHex);
127
+ bits = bits.slice(0, 198);
128
+ }
129
+ else if (bits.length !== 198) {
130
+ return refuse('length', `SGTIN-198 is 198 bits (200 in hexadecimal with zero padding); ${bits.length} were given`, headerHex);
131
+ }
132
+ }
133
+ else {
134
+ return refuse('unsupported-epc-scheme', `EPC header ${headerHex} is not SGTIN-96 (30) or SGTIN-198 (36); only serialised trade items are decoded`, headerHex);
135
+ }
136
+ const filter = Number(integerOf(bits.slice(8, 11)));
137
+ const partition = Number(integerOf(bits.slice(11, 14)));
138
+ const layout = SGTIN_PARTITIONS[partition];
139
+ if (layout == null)
140
+ return refuse('partition', `partition value ${partition} is not defined for SGTIN`, headerHex);
141
+ let at = 14;
142
+ const companyPrefixValue = integerOf(bits.slice(at, at + layout.companyPrefixBits));
143
+ at += layout.companyPrefixBits;
144
+ const itemReferenceValue = integerOf(bits.slice(at, at + layout.itemReferenceBits));
145
+ at += layout.itemReferenceBits;
146
+ if (companyPrefixValue >= 10n ** BigInt(layout.companyPrefixDigits))
147
+ return refuse('company-prefix', `the company prefix exceeds ${layout.companyPrefixDigits} digits`, headerHex);
148
+ if (itemReferenceValue >= 10n ** BigInt(layout.itemReferenceDigits))
149
+ return refuse('item-reference', `the item reference exceeds ${layout.itemReferenceDigits} digits`, headerHex);
150
+ const companyPrefix = companyPrefixValue.toString().padStart(layout.companyPrefixDigits, '0');
151
+ const itemReference = itemReferenceValue.toString().padStart(layout.itemReferenceDigits, '0');
152
+ let serial;
153
+ const serialField = bits.slice(at, at + serialBits);
154
+ if (scheme === 'sgtin-96') {
155
+ serial = integerOf(serialField).toString();
156
+ }
157
+ else {
158
+ const decoded = decodeSerial198(serialField);
159
+ if ('error' in decoded)
160
+ return refuse('serial', decoded.error, headerHex);
161
+ serial = decoded.serial;
162
+ }
163
+ const thirteen = `${itemReference[0]}${companyPrefix}${itemReference.slice(1)}`;
164
+ const gtin = `${thirteen}${gs1CheckDigit(thirteen)}`;
165
+ return {
166
+ ok: true,
167
+ scheme,
168
+ header,
169
+ filter,
170
+ partition,
171
+ companyPrefix,
172
+ itemReference,
173
+ gtin,
174
+ serial,
175
+ epcUri: `urn:epc:id:sgtin:${companyPrefix}.${itemReference}.${escapeSerialForUri(serial)}`,
176
+ };
177
+ }
178
+ /**
179
+ * Decode the compression string of a special compressed GS1 Digital Link URI:
180
+ * the two characters after the slash select the alphabet (`eh` hexadecimal,
181
+ * `ex` URI-safe base 64) and the rest is the EPC binary string.
182
+ */
183
+ export function decodeEpcCompressionString(compressionString) {
184
+ if (compressionString.startsWith('eh')) {
185
+ const payload = compressionString.slice(2);
186
+ if (payload.length === 0)
187
+ return refuse('empty', 'nothing follows the eh prefix');
188
+ const bits = bitsFromHex(payload);
189
+ if (bits == null)
190
+ return refuse('not-hex', 'the characters after eh are not lower-case hexadecimal');
191
+ return decodeEpcBinaryBits(bits);
192
+ }
193
+ if (compressionString.startsWith('ex')) {
194
+ const payload = compressionString.slice(2);
195
+ if (payload.length === 0)
196
+ return refuse('empty', 'nothing follows the ex prefix');
197
+ const bits = bitsFromUriSafeBase64(payload);
198
+ if (bits == null)
199
+ return refuse('not-uri-safe-base64', 'the characters after ex are not from the URI-safe base 64 alphabet');
200
+ return decodeEpcBinaryBits(bits);
201
+ }
202
+ return refuse('unsupported-epc-scheme', 'the compression string begins with neither eh nor ex; the general GS1 Digital Link compression is not decoded here');
203
+ }
204
+ /**
205
+ * The inverse, for tests and for a writer that prints a compressed carrier:
206
+ * an SGTIN-96 or SGTIN-198 EPC binary string as bits, from its parts. Refuses
207
+ * a serial the scheme cannot carry rather than truncating it.
208
+ */
209
+ export function encodeSgtinBits(input) {
210
+ const layout = SGTIN_PARTITIONS[input.partition];
211
+ if (layout == null)
212
+ throw new Error('partition value must be 0 to 6');
213
+ if (!/^\d+$/.test(input.companyPrefix) || input.companyPrefix.length !== layout.companyPrefixDigits)
214
+ throw new Error(`a partition ${input.partition} company prefix has ${layout.companyPrefixDigits} digits`);
215
+ if (!/^\d+$/.test(input.itemReference) || input.itemReference.length !== layout.itemReferenceDigits)
216
+ throw new Error(`a partition ${input.partition} item reference has ${layout.itemReferenceDigits} digits`);
217
+ if (!Number.isInteger(input.filter) || input.filter < 0 || input.filter > 7)
218
+ throw new Error('the filter value is three bits');
219
+ const field = (value, width) => value.toString(2).padStart(width, '0');
220
+ let bits = field(BigInt(input.scheme === 'sgtin-96' ? SGTIN_96_HEADER : SGTIN_198_HEADER), 8) + field(BigInt(input.filter), 3) + field(BigInt(input.partition), 3);
221
+ bits += field(BigInt(input.companyPrefix), layout.companyPrefixBits) + field(BigInt(input.itemReference), layout.itemReferenceBits);
222
+ if (input.scheme === 'sgtin-96') {
223
+ if (!/^(0|[1-9]\d*)$/.test(input.serial) || BigInt(input.serial) >= 2n ** 38n)
224
+ throw new Error('an SGTIN-96 serial is an integer below 2^38 without leading zeros');
225
+ bits += field(BigInt(input.serial), 38);
226
+ }
227
+ else {
228
+ if (input.serial.length === 0 || input.serial.length > 20 || !GS1_CHARACTER_SET_82.test(input.serial))
229
+ throw new Error('an SGTIN-198 serial is 1 to 20 characters of the GS1 encodable set 82');
230
+ for (const character of input.serial)
231
+ bits += field(BigInt(character.charCodeAt(0)), 7);
232
+ bits = bits.padEnd(198, '0');
233
+ }
234
+ return bits;
235
+ }
236
+ /** Bits to the `eh` form: lower-case hexadecimal, zero-padded to a multiple of four bits. */
237
+ export function hexFromBits(bits) {
238
+ const padded = bits.padEnd(Math.ceil(bits.length / 4) * 4, '0');
239
+ let hex = '';
240
+ for (let at = 0; at < padded.length; at += 4)
241
+ hex += parseInt(padded.slice(at, at + 4), 2).toString(16);
242
+ return hex;
243
+ }
244
+ /** Bits to the `ex` form: the URI-safe base 64 alphabet, zero-padded to a multiple of six bits. */
245
+ export function uriSafeBase64FromBits(bits) {
246
+ const padded = bits.padEnd(Math.ceil(bits.length / 6) * 6, '0');
247
+ let text = '';
248
+ for (let at = 0; at < padded.length; at += 6)
249
+ text += URI_SAFE_BASE64_ALPHABET[parseInt(padded.slice(at, at + 6), 2)];
250
+ return text;
251
+ }
252
+ //# sourceMappingURL=gs1-epc-binary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gs1-epc-binary.js","sourceRoot":"","sources":["../src/gs1-epc-binary.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,392 @@
1
+ import { type EpcBinaryRefusalReason, type SgtinDecoding } from './gs1-epc-binary.js';
2
+ /**
3
+ * The GS1 Web vocabulary namespace the resolver standard expands `gs1:` to
4
+ * (GS1-Conformant Resolver 1.2.1 §2.14). The published linkset context maps
5
+ * the same prefix to `http://gs1.org/voc/`; `normaliseLinkType` treats the
6
+ * three spellings as one so a linkset from another resolver is read
7
+ * correctly and this module always writes the standard's own.
8
+ */
9
+ export declare const GS1_VOCABULARY = "https://ref.gs1.org/voc/";
10
+ /** The GS1 link types this profile uses, as full IRIs; a linkset key is always the full IRI. */
11
+ export declare const GS1_LINK_TYPES: {
12
+ readonly defaultLink: "https://ref.gs1.org/voc/defaultLink";
13
+ readonly defaultLinkMulti: "https://ref.gs1.org/voc/defaultLinkMulti";
14
+ readonly pip: "https://ref.gs1.org/voc/pip";
15
+ readonly sustainabilityInfo: "https://ref.gs1.org/voc/sustainabilityInfo";
16
+ readonly certificationInfo: "https://ref.gs1.org/voc/certificationInfo";
17
+ readonly epcis: "https://ref.gs1.org/voc/epcis";
18
+ readonly traceability: "https://ref.gs1.org/voc/traceability";
19
+ readonly recallStatus: "https://ref.gs1.org/voc/recallStatus";
20
+ readonly handledBy: "https://ref.gs1.org/voc/handledBy";
21
+ };
22
+ /**
23
+ * The namespace of the link types this standard owns, and the one relation it
24
+ * defines: a link to a passport's portable evidence package
25
+ * (`spec/portable-evidence.md`), for which the GS1 vocabulary has no term.
26
+ * The pinned linkset schema admits a link relation key only as a bare
27
+ * lower-case token or as an `http(s)` IRI of letters, digits, dots and
28
+ * slashes, so the relation is spelled without a hyphen under the programme's
29
+ * public host and versioned in its path; the standard's usual
30
+ * `bsv-blockchain.github.io` base cannot be a link relation under that schema.
31
+ */
32
+ export declare const DPP_LINK_NAMESPACE = "https://dpp.bsvb.net/link/";
33
+ export declare const DPP_EVIDENCE_PACKAGE_LINK_TYPE = "https://dpp.bsvb.net/link/evidencePackage/1";
34
+ /** The media types and relations of the resolver standard §2.9 and §2.10. */
35
+ export declare const LINKSET_MEDIA_TYPE = "application/linkset+json";
36
+ export declare const LINKSET_CONTEXT_URI = "https://ref.gs1.org/standards/resolver/1.2.1/linkset-context";
37
+ export declare const JSON_LD_CONTEXT_RELATION = "http://www.w3.org/ns/json-ld#context";
38
+ /** The one primary key this profile hosts, and its three key qualifiers in the order the URI syntax fixes. */
39
+ export declare const SUPPORTED_PRIMARY_KEY = "01";
40
+ export declare const GTIN_QUALIFIER_ORDER: readonly ["22", "10", "21"];
41
+ export type GtinQualifierAi = (typeof GTIN_QUALIFIER_ORDER)[number];
42
+ export interface AiValue {
43
+ ai: string;
44
+ value: string;
45
+ }
46
+ export interface DataAttribute extends AiValue {
47
+ /** True when this module knows the AI's format and the value met it; false when the AI is unknown here and only the character set was checked. */
48
+ validated: boolean;
49
+ }
50
+ export interface LinkTypeRequest {
51
+ /** The query parameter as written. */
52
+ raw: string;
53
+ /** The full IRI, when the value was a full IRI or a `gs1:` CURIE. */
54
+ iri?: string;
55
+ /** True for `linkset` (and its common alias `all`): the client wants the list, not a redirect. */
56
+ linkset?: boolean;
57
+ }
58
+ export interface Gs1Key {
59
+ ai: '01';
60
+ /** Fourteen digits. */
61
+ value: string;
62
+ qualifiers: AiValue[];
63
+ }
64
+ export interface ParsedUncompressedUri {
65
+ kind: 'uncompressed';
66
+ origin: string;
67
+ host: string;
68
+ /** The path before `/01`, without a trailing slash; empty for a root resolver. */
69
+ pathStem: string;
70
+ primaryKey: {
71
+ ai: '01';
72
+ value: string;
73
+ checkDigitValid: boolean;
74
+ demonstration: boolean;
75
+ };
76
+ /** In the order the syntax fixes: 22, then 10, then 21. */
77
+ qualifiers: AiValue[];
78
+ dataAttributes: DataAttribute[];
79
+ linkType?: LinkTypeRequest;
80
+ /** Query parameters that are neither AIs nor linkType, preserved for forwarding. */
81
+ otherQuery: Array<[string, string]>;
82
+ }
83
+ export interface ParsedCompressedUri {
84
+ kind: 'compressed';
85
+ origin: string;
86
+ host: string;
87
+ pathStem: string;
88
+ prefix: 'eh' | 'ex';
89
+ /** The whole compression string, prefix included. */
90
+ compressionString: string;
91
+ /** The query string as given, so it can be carried to the uncompressed form. */
92
+ query: string;
93
+ }
94
+ export type UriRefusalReason = 'not-a-url' | 'scheme' | 'percent-encoding' | 'no-primary-key' | 'unsupported-primary-key' | 'primary-key-not-a-gtin' | 'trailing-segment' | 'qualifier-order' | 'qualifier-repeated' | 'qualifier-unsupported' | 'qualifier-value' | 'key-in-query' | 'attribute-ai' | 'attribute-value';
95
+ export interface InvalidUri {
96
+ kind: 'invalid';
97
+ reason: UriRefusalReason;
98
+ detail: string;
99
+ }
100
+ export type ParsedGs1Uri = ParsedUncompressedUri | ParsedCompressedUri | InvalidUri;
101
+ /**
102
+ * Read a GS1 Digital Link URI in the grammar this profile hosts: primary key
103
+ * 01, its key qualifiers 22, 10 and 21 in that order, data attributes in the
104
+ * query, `linkType` beside them, and the special compressed form of the EPC
105
+ * binary standard. Refusals are answers, not exceptions, each with the
106
+ * standard's reason: a resolver turns them into 400 and a client into a
107
+ * finding. One trailing slash is tolerated (resolver standard §2.13).
108
+ *
109
+ * A URI this reads is syntactically a GS1 Digital Link URI and nothing more:
110
+ * the check digit is reported, not enforced, because a historical record may
111
+ * carry a GTIN that fails it, and allocation authority is never inferred.
112
+ */
113
+ export declare function parseGs1DigitalLinkUri(uri: string): ParsedGs1Uri;
114
+ export interface Decompressed {
115
+ ok: true;
116
+ /** The fully uncompressed URI under the stem the compressed form carried. */
117
+ uri: string;
118
+ key: Gs1Key;
119
+ epc: SgtinDecoding;
120
+ }
121
+ export type DecompressionResult = Decompressed | {
122
+ ok: false;
123
+ reason: 'not-compressed' | EpcBinaryRefusalReason;
124
+ detail: string;
125
+ header?: string;
126
+ };
127
+ /**
128
+ * Turn a special compressed GS1 Digital Link URI into its uncompressed form
129
+ * (compression standard §4.2.1 and §4.2.2): the stem up to the compression
130
+ * string is kept, the EPC binary string after `eh` or `ex` is decoded under
131
+ * the Tag Data Standard, and the result is `${stem}/01/{gtin}/21/{serial}`.
132
+ * A query string is carried across unchanged. An uncompressed URI is refused
133
+ * as `not-compressed` rather than echoed, so a caller never mistakes a
134
+ * pass-through for a decoding.
135
+ */
136
+ export declare function decompressGs1DigitalLink(uri: string): DecompressionResult;
137
+ /** The key an uncompressed parse names, or one built from its parts with the qualifiers put in canonical order. */
138
+ export declare function gs1KeyOf(source: ParsedUncompressedUri | {
139
+ gtin: string;
140
+ qualifiers?: AiValue[];
141
+ }): Gs1Key;
142
+ /**
143
+ * The canonical spelling of a key tuple, `01:{gtin14}` followed by each
144
+ * qualifier as `ai:value` in the syntax's order, joined by `|`. Two Digital
145
+ * Link URIs under different hosts with the same tuple name the same entity;
146
+ * the same serial under a different GTIN does not. Equivalence is a statement
147
+ * about identifiers only: it is not authority to bind a signed credential to
148
+ * a passport, which needs the binding evidence `spec/gs1-discovery.md` §2
149
+ * names.
150
+ */
151
+ export declare function gs1KeyTuple(key: Gs1Key | ParsedUncompressedUri): string;
152
+ export declare function sameGs1Key(a: Gs1Key | ParsedUncompressedUri, b: Gs1Key | ParsedUncompressedUri): boolean;
153
+ /** The granularity a key tuple names, by its most specific qualifier. */
154
+ export type KeyGranularity = 'model' | 'variant' | 'lot' | 'item';
155
+ export declare function granularityOf(qualifiers: readonly AiValue[]): KeyGranularity;
156
+ /** The uncompressed Digital Link URI of a key under a resolver root (origin plus optional stem, no trailing slash). */
157
+ export declare function uncompressedUri(resolverRoot: string, key: Gs1Key): string;
158
+ /** The path of the legacy evidence JSON answered at the API origin, and its alias, for an item key; undefined for any other granularity. */
159
+ export declare function legacyEvidencePaths(key: Gs1Key): {
160
+ legacy: string;
161
+ alias: string;
162
+ } | undefined;
163
+ export type Disclosure = 'public' | 'restricted';
164
+ export interface ResolutionLink {
165
+ /** The descriptive link relation type, as a full IRI. */
166
+ rel: string;
167
+ href: string;
168
+ title: string;
169
+ /** An IANA media type, when declared. */
170
+ type?: string;
171
+ /** BCP 47 language tags, when declared. */
172
+ hreflang?: string[];
173
+ context?: string[];
174
+ /** `single`: the entity's one default link (no optional attributes); `multi`: a default refined by request headers. */
175
+ default?: 'single' | 'multi';
176
+ /** Whether public discovery may return the link; a restricted link is only ever returned to an authorised audience, and access is enforced again at its target. */
177
+ disclosure: Disclosure;
178
+ /** Whether the resolver forwards the request's query string on redirect; the standard's default is to forward everything (§2.12). */
179
+ forwardQuery?: boolean;
180
+ }
181
+ export interface ResolutionRecord {
182
+ key: {
183
+ ai: '01';
184
+ value: string;
185
+ };
186
+ /** The qualifiers this record is registered at, in the syntax's order; a record with 21 carries neither 22 nor 10 (resolver standard §2.5.10). */
187
+ qualifiers: AiValue[];
188
+ /** The exact signed passport identifier bound to this entity, where the binding is established; never inferred from the key. */
189
+ passportId?: string;
190
+ /** A reference to the evidence or policy establishing that binding. */
191
+ bindingRef?: string;
192
+ links: ResolutionLink[];
193
+ /** The revision of the configuration the record was written under. */
194
+ configurationRevision: string;
195
+ description?: string;
196
+ }
197
+ /**
198
+ * Every rule a record must meet before it is stored, so the resolver validates
199
+ * at ingestion and not at every request (resolver standard §2.4): a GTIN
200
+ * whose check digit holds, qualifiers in order with no repeat and no TPX, a
201
+ * serial never registered beside a variant or a lot, at most one single
202
+ * default link with no optional attributes, and every link with an `https`
203
+ * target, a title, a relation the linkset schema can carry, and declared
204
+ * disclosure. An empty list means the record is admissible.
205
+ */
206
+ export declare function resolutionRecordProblems(record: ResolutionRecord): string[];
207
+ export interface ResolutionRequest {
208
+ key: Gs1Key;
209
+ /** Public discovery never sees a restricted link; an authorised audience sees it flagged. */
210
+ audience: 'public' | 'authorised';
211
+ /** The requested link type as a full IRI, `linkset` for the list, or absent for the default. */
212
+ linkType?: string | 'linkset';
213
+ /** Acceptable media types, most preferred first (`parseAcceptHeader`). */
214
+ accept?: string[];
215
+ /** Preferred languages, most preferred first (`parseAcceptLanguage`). */
216
+ acceptLanguage?: string[];
217
+ context?: string[];
218
+ /** The origin and stem the linkset anchors are written under. */
219
+ resolverRoot: string;
220
+ }
221
+ export interface ResolvedLink extends ResolutionLink {
222
+ /** The uncompressed URI of the record the link was registered at, never the request's. */
223
+ anchor: string;
224
+ /** The granularity of that record: a model-level link answering an item request is still model information. */
225
+ granularity: KeyGranularity;
226
+ record: ResolutionRecord;
227
+ }
228
+ export interface MatchedRecord {
229
+ record: ResolutionRecord;
230
+ anchor: string;
231
+ granularity: KeyGranularity;
232
+ }
233
+ export type ResolutionOutcome = {
234
+ outcome: 'no-records';
235
+ matched: [];
236
+ } | {
237
+ outcome: 'linkset';
238
+ matched: MatchedRecord[];
239
+ links: ResolvedLink[];
240
+ } | {
241
+ outcome: 'redirect';
242
+ matched: MatchedRecord[];
243
+ link: ResolvedLink;
244
+ links: ResolvedLink[];
245
+ } | {
246
+ outcome: 'multiple-choices';
247
+ matched: MatchedRecord[];
248
+ candidates: ResolvedLink[];
249
+ links: ResolvedLink[];
250
+ } | {
251
+ outcome: 'not-found';
252
+ matched: MatchedRecord[];
253
+ requested: string;
254
+ links: ResolvedLink[];
255
+ };
256
+ /** `text/html;q=0.9, application/json` to `['application/json', 'text/html']`: q-values honoured, zero excluded. */
257
+ export declare function parseAcceptHeader(header: string | undefined): string[];
258
+ /** `fr-CH, fr;q=0.9, en;q=0.8` to `['fr-CH', 'fr', 'en']`. */
259
+ export declare function parseAcceptLanguage(header: string | undefined): string[];
260
+ /**
261
+ * The records a request selects: the union the resolver standard §2.5.10
262
+ * fixes, most specific level first, so a link registered at the lot level is
263
+ * found under a serialised request and a link at the model level under every
264
+ * request. Each match keeps its own anchor and granularity.
265
+ */
266
+ export declare function matchRecords(records: readonly ResolutionRecord[], key: Gs1Key, resolverRoot: string): MatchedRecord[];
267
+ /**
268
+ * The best match among candidates of one link type (resolver standard §2.6.3,
269
+ * media type before language before context): the unique best, or every
270
+ * candidate when the best is not unique, so the resolver answers 300 rather
271
+ * than choosing.
272
+ */
273
+ export declare function bestMatch<L extends ResolutionLink>(candidates: readonly L[], request: Pick<ResolutionRequest, 'accept' | 'acceptLanguage' | 'context'>): {
274
+ unique: L;
275
+ } | {
276
+ tie: L[];
277
+ } | undefined;
278
+ /**
279
+ * Resolve a request against the records this resolver holds, exactly as the
280
+ * standard's flow chart does and without any I/O: which records the key
281
+ * selects, which links the audience may see, then the default, the requested
282
+ * type, the list, or the refusal. A restricted link is never returned to a
283
+ * public audience. The redirect target is the link's own `href`; forwarding
284
+ * the request's query string onto it (§2.12) is the route's step, because
285
+ * the query is the route's to read.
286
+ */
287
+ export declare function resolveLinks(records: readonly ResolutionRecord[], request: ResolutionRequest): ResolutionOutcome;
288
+ export interface LinksetLink {
289
+ href: string;
290
+ title: string;
291
+ type?: string;
292
+ hreflang?: string[];
293
+ context?: string[];
294
+ fwqs?: boolean;
295
+ public?: boolean;
296
+ }
297
+ /** One link context object of RFC 9264 as the GS1 schema shapes it: the anchor, an optional description and the links keyed by relation. */
298
+ export type LinksetEntry = {
299
+ anchor: string;
300
+ description?: string;
301
+ } & Record<string, unknown>;
302
+ export interface LinksetDocument {
303
+ linkset: LinksetEntry[];
304
+ }
305
+ /** One relation IRI for the three spellings of the GS1 vocabulary; anything else is returned as given. */
306
+ export declare function normaliseLinkType(iri: string): string;
307
+ /**
308
+ * The linkset a resolver returns for a request (resolver standard §2.9 and
309
+ * §2.10): one link context object per matched record, most specific first,
310
+ * its anchor the uncompressed URI of that record's own identifier, every link
311
+ * under its descriptive relation and a default additionally under
312
+ * `gs1:defaultLink` or `gs1:defaultLinkMulti`. Links a public audience may
313
+ * not see are absent, not blanked; a restricted link shown to an authorised
314
+ * audience says `public: false`. The document validates against the pinned
315
+ * linkset schema.
316
+ */
317
+ export declare function linksetDocument(records: readonly ResolutionRecord[], request: Omit<ResolutionRequest, 'linkType'>): LinksetDocument | undefined;
318
+ export interface LinkSelection {
319
+ rel: string;
320
+ mediaType?: string;
321
+ language?: string;
322
+ context?: string;
323
+ }
324
+ export type SelectedLink = {
325
+ outcome: 'selected';
326
+ link: LinksetLink;
327
+ anchor: string;
328
+ scope: {
329
+ key?: Gs1Key;
330
+ granularity: KeyGranularity | 'unknown';
331
+ };
332
+ } | {
333
+ outcome: 'ambiguous';
334
+ candidates: Array<{
335
+ link: LinksetLink;
336
+ anchor: string;
337
+ }>;
338
+ } | {
339
+ outcome: 'not-found';
340
+ available: string[];
341
+ };
342
+ /**
343
+ * A discovery client's choice over a linkset another resolver returned: the
344
+ * links under the requested relation (a full IRI or a `gs1:` CURIE) across
345
+ * every link context object, the best match by media type, language and
346
+ * context, and the anchor's own scope beside the result, so a link found at
347
+ * the model level under an item request is handed back as model
348
+ * information. An anchor that is not a Digital Link URI this module reads
349
+ * has scope `unknown`; it is not guessed.
350
+ */
351
+ export declare function selectLink(linkset: LinksetDocument, selection: LinkSelection): SelectedLink;
352
+ export interface ResolverDescriptionConfig {
353
+ /** The origin, with an optional stem, the resolver answers at; no trailing slash. */
354
+ resolverRoot: string;
355
+ name?: string;
356
+ contact?: {
357
+ fn?: string;
358
+ hasAddress?: {
359
+ streetAddress?: string;
360
+ locality?: string;
361
+ region?: string;
362
+ 'postal-code'?: string;
363
+ };
364
+ hasTelephone?: string;
365
+ };
366
+ supportedContextValuesEnumerated?: string[];
367
+ extensionProfile?: string;
368
+ }
369
+ export interface ResolverDescription {
370
+ name?: string;
371
+ resolverRoot: string;
372
+ supportedPrimaryKeys: ['01'];
373
+ supportedLinkType: Array<{
374
+ namespace: string;
375
+ prefix: string;
376
+ }>;
377
+ linkTypeDefaultCanBeLinkset: false;
378
+ supportedContextValuesEnumerated?: string[];
379
+ contact?: ResolverDescriptionConfig['contact'];
380
+ extensionProfile?: string;
381
+ jsonLdContextLocation: string;
382
+ }
383
+ /**
384
+ * The `/.well-known/gs1resolver` document (resolver standard §3) for the one
385
+ * scope this profile hosts: primary key 01 with every qualifier, the GS1
386
+ * vocabulary and this standard's own link namespace, `linkset` never the
387
+ * default, and the pinned GS1 context for JSON linksets. It is published only
388
+ * by an origin that actually provides the resolver contract; the legacy API
389
+ * origin does not describe itself as one.
390
+ */
391
+ export declare function describeResolver(config: ResolverDescriptionConfig): ResolverDescription;
392
+ //# sourceMappingURL=gs1-resolution.d.ts.map