@aidc-toolkit/gs1 1.0.31-beta → 1.0.32-beta
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/gcp-length-cache.d.ts +42 -0
- package/dist/gcp-length-cache.d.ts.map +1 -0
- package/dist/gcp-length-cache.js +96 -0
- package/dist/gcp-length-cache.js.map +1 -0
- package/dist/gcp-length-data.d.ts +54 -0
- package/dist/gcp-length-data.d.ts.map +1 -0
- package/dist/gcp-length-data.js +29 -0
- package/dist/gcp-length-data.js.map +1 -0
- package/dist/gcp-length.d.ts +61 -0
- package/dist/gcp-length.d.ts.map +1 -0
- package/dist/gcp-length.js +301 -0
- package/dist/gcp-length.js.map +1 -0
- package/dist/gtin-creator.d.ts +0 -17
- package/dist/gtin-creator.d.ts.map +1 -1
- package/dist/gtin-creator.js +1 -93
- package/dist/gtin-creator.js.map +1 -1
- package/dist/gtin-validator.d.ts +1 -46
- package/dist/gtin-validator.d.ts.map +1 -1
- package/dist/gtin-validator.js +31 -125
- package/dist/gtin-validator.js.map +1 -1
- package/dist/identifier-validator.d.ts +1 -4
- package/dist/identifier-validator.d.ts.map +1 -1
- package/dist/identifier-validator.js +2 -5
- package/dist/identifier-validator.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/locale/en/locale-resources.d.ts +2 -1
- package/dist/locale/en/locale-resources.d.ts.map +1 -1
- package/dist/locale/en/locale-resources.js +3 -2
- package/dist/locale/en/locale-resources.js.map +1 -1
- package/dist/locale/fr/locale-resources.d.ts +2 -1
- package/dist/locale/fr/locale-resources.d.ts.map +1 -1
- package/dist/locale/fr/locale-resources.js +3 -2
- package/dist/locale/fr/locale-resources.js.map +1 -1
- package/dist/locale/i18n.d.ts +0 -3
- package/dist/locale/i18n.d.ts.map +1 -1
- package/dist/locale/i18n.js +2 -5
- package/dist/locale/i18n.js.map +1 -1
- package/dist/non-numeric-identifier-validator.js +1 -1
- package/dist/non-numeric-identifier-validator.js.map +1 -1
- package/dist/numeric-identifier-validator.js +2 -2
- package/dist/numeric-identifier-validator.js.map +1 -1
- package/dist/prefix-manager.d.ts +35 -0
- package/dist/prefix-manager.d.ts.map +1 -1
- package/dist/prefix-manager.js +56 -0
- package/dist/prefix-manager.js.map +1 -1
- package/dist/prefix-validator.d.ts +5 -4
- package/dist/prefix-validator.d.ts.map +1 -1
- package/dist/prefix-validator.js +18 -22
- package/dist/prefix-validator.js.map +1 -1
- package/dist/serializable-numeric-identifier-validator.d.ts +26 -0
- package/dist/serializable-numeric-identifier-validator.d.ts.map +1 -1
- package/dist/serializable-numeric-identifier-validator.js +19 -0
- package/dist/serializable-numeric-identifier-validator.js.map +1 -1
- package/dist/variable-measure.d.ts +68 -0
- package/dist/variable-measure.d.ts.map +1 -0
- package/dist/variable-measure.js +210 -0
- package/dist/variable-measure.js.map +1 -0
- package/dist/verified-by-gs1.d.ts +22 -0
- package/dist/verified-by-gs1.d.ts.map +1 -0
- package/dist/verified-by-gs1.js +46 -0
- package/dist/verified-by-gs1.js.map +1 -0
- package/package.json +8 -8
- package/src/gcp-length-cache.ts +117 -0
- package/src/gcp-length-data.ts +68 -0
- package/src/gcp-length.ts +418 -0
- package/src/gtin-creator.ts +1 -117
- package/src/gtin-validator.ts +42 -173
- package/src/identifier-validator.ts +2 -5
- package/src/index.ts +7 -1
- package/src/locale/en/locale-resources.ts +3 -2
- package/src/locale/fr/locale-resources.ts +3 -2
- package/src/locale/i18n.ts +2 -5
- package/src/non-numeric-identifier-validator.ts +1 -1
- package/src/numeric-identifier-validator.ts +2 -2
- package/src/prefix-manager.ts +65 -0
- package/src/prefix-validator.ts +19 -23
- package/src/serializable-numeric-identifier-validator.ts +36 -0
- package/src/variable-measure.ts +268 -0
- package/src/verified-by-gs1.ts +54 -0
- package/test/creator.test.ts +5 -5
- package/test/data/gcpprefixformatlist-1.json +662625 -0
- package/test/data/gcpprefixformatlist-2.json +735431 -0
- package/test/gcp-length.test.ts +405 -0
- package/test/gtin-creator.ts +4 -4
- package/test/gtin-validator.test.ts +205 -113
- package/test/gtin-validator.ts +30 -0
- package/test/identifier-creator.ts +6 -6
- package/test/non-numeric-identifier-creator.ts +0 -8
- package/test/non-serializable-numeric-identifier-creator.ts +4 -54
- package/test/numeric-identifier-creator.ts +4 -4
- package/test/prefix-manager.test.ts +5 -5
- package/test/serializable-numeric-identifier-creator.ts +32 -19
- package/test/validator.test.ts +6 -6
- package/test/variable-measure-rcn.test.ts +63 -68
- package/test/verified-by-gs1.test.ts +55 -0
- package/tsconfig-src.json +7 -1
- package/tsconfig-src.tsbuildinfo +1 -0
- package/tsconfig-tsup.json +7 -0
package/src/gtin-validator.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type CharacterSetValidation, NUMERIC_CREATOR } from "@aidc-toolkit/utility";
|
|
2
|
-
import {
|
|
2
|
+
import type { ParseKeys } from "i18next";
|
|
3
|
+
import { checkDigit, hasValidCheckDigit } from "./check.js";
|
|
3
4
|
import type { GTINDescriptor } from "./gtin-descriptor.js";
|
|
4
5
|
import { type GTINBaseLength, GTINLengths } from "./gtin-length.js";
|
|
5
6
|
import type { GTINType } from "./gtin-type.js";
|
|
@@ -39,21 +40,6 @@ export type GTINLevelKey = keyof typeof GTINLevels;
|
|
|
39
40
|
*/
|
|
40
41
|
export type GTINLevel = typeof GTINLevels[GTINLevelKey];
|
|
41
42
|
|
|
42
|
-
/**
|
|
43
|
-
* Restricted Circulation Number reference.
|
|
44
|
-
*/
|
|
45
|
-
export interface RCNReference {
|
|
46
|
-
/**
|
|
47
|
-
* Item reference.
|
|
48
|
-
*/
|
|
49
|
-
itemReference: number;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Price or weight (whole number only).
|
|
53
|
-
*/
|
|
54
|
-
priceOrWeight: number;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
43
|
/**
|
|
58
44
|
* GTIN validator.
|
|
59
45
|
*/
|
|
@@ -75,6 +61,12 @@ export class GTINValidator extends NumericIdentifierValidator<GTINType> implemen
|
|
|
75
61
|
maximumLength: 8
|
|
76
62
|
};
|
|
77
63
|
|
|
64
|
+
static readonly #ERROR_MESSAGE_PARSE_KEYS: Record<GTINLevel, ParseKeys | undefined> = {
|
|
65
|
+
[GTINLevels.Any]: undefined,
|
|
66
|
+
[GTINLevels.RetailConsumer]: "Identifier.invalidGTINAtRetail",
|
|
67
|
+
[GTINLevels.OtherThanRetailConsumer]: "Identifier.invalidGTINAtOtherThanRetail"
|
|
68
|
+
};
|
|
69
|
+
|
|
78
70
|
/**
|
|
79
71
|
* Prefix type.
|
|
80
72
|
*/
|
|
@@ -104,9 +96,9 @@ export class GTINValidator extends NumericIdentifierValidator<GTINType> implemen
|
|
|
104
96
|
/**
|
|
105
97
|
* @inheritDoc
|
|
106
98
|
*/
|
|
107
|
-
protected override validatePrefix(partialIdentifier: string
|
|
99
|
+
protected override validatePrefix(partialIdentifier: string): void {
|
|
108
100
|
// Delegate to prefix validator requiring exact match for prefix type.
|
|
109
|
-
PrefixValidator.validate(this.prefixType, false, false, partialIdentifier, true, true
|
|
101
|
+
PrefixValidator.validate(this.prefixType, false, false, partialIdentifier, true, true);
|
|
110
102
|
}
|
|
111
103
|
|
|
112
104
|
/**
|
|
@@ -199,18 +191,16 @@ export class GTINValidator extends NumericIdentifierValidator<GTINType> implemen
|
|
|
199
191
|
* GTIN-14.
|
|
200
192
|
*/
|
|
201
193
|
static convertToGTIN14(indicatorDigit: string, gtin: string): string {
|
|
202
|
-
GTINValidator.validateAny(gtin);
|
|
203
|
-
|
|
204
194
|
NUMERIC_CREATOR.validate(indicatorDigit, GTINValidator.#OPTIONAL_INDICATOR_DIGIT_VALIDATION);
|
|
205
195
|
|
|
206
196
|
// Check digit doesn't change by prepending zeros.
|
|
207
|
-
let gtin14 = gtin.padStart(GTINLengths.GTIN14, "0");
|
|
197
|
+
let gtin14 = GTINValidator.normalize(gtin).padStart(GTINLengths.GTIN14, "0");
|
|
208
198
|
|
|
209
199
|
// If indicator digit provided and is different, recalculate the check digit.
|
|
210
|
-
if (indicatorDigit.length !== 0 &&
|
|
211
|
-
const partialGTIN14 = indicatorDigit
|
|
200
|
+
if (indicatorDigit.length !== 0 && !gtin14.startsWith(indicatorDigit)) {
|
|
201
|
+
const partialGTIN14 = `${indicatorDigit}${gtin14.substring(1, GTINLengths.GTIN14 - 1)}`;
|
|
212
202
|
|
|
213
|
-
gtin14 = partialGTIN14
|
|
203
|
+
gtin14 = `${partialGTIN14}${checkDigit(partialGTIN14)}`;
|
|
214
204
|
}
|
|
215
205
|
|
|
216
206
|
return gtin14;
|
|
@@ -235,7 +225,7 @@ export class GTINValidator extends NumericIdentifierValidator<GTINType> implemen
|
|
|
235
225
|
let normalizedGTIN: string;
|
|
236
226
|
|
|
237
227
|
switch (gtinLength) {
|
|
238
|
-
case GTINLengths.GTIN13
|
|
228
|
+
case GTINLengths.GTIN13:
|
|
239
229
|
if (!gtin.startsWith("0")) {
|
|
240
230
|
// GTIN is GTIN-13.
|
|
241
231
|
normalizedGTIN = gtin;
|
|
@@ -250,12 +240,12 @@ export class GTINValidator extends NumericIdentifierValidator<GTINType> implemen
|
|
|
250
240
|
}
|
|
251
241
|
break;
|
|
252
242
|
|
|
253
|
-
case GTINLengths.GTIN12
|
|
243
|
+
case GTINLengths.GTIN12:
|
|
254
244
|
// GTIN is GTIN-12.
|
|
255
245
|
normalizedGTIN = gtin;
|
|
256
246
|
break;
|
|
257
247
|
|
|
258
|
-
case GTINLengths.GTIN8
|
|
248
|
+
case GTINLengths.GTIN8:
|
|
259
249
|
if (!gtin.startsWith("0")) {
|
|
260
250
|
// GTIN is GTIN-8.
|
|
261
251
|
normalizedGTIN = gtin;
|
|
@@ -265,7 +255,7 @@ export class GTINValidator extends NumericIdentifierValidator<GTINType> implemen
|
|
|
265
255
|
}
|
|
266
256
|
break;
|
|
267
257
|
|
|
268
|
-
case GTINLengths.GTIN14
|
|
258
|
+
case GTINLengths.GTIN14:
|
|
269
259
|
if (!gtin.startsWith("0")) {
|
|
270
260
|
// GTIN is GTIN-14.
|
|
271
261
|
normalizedGTIN = gtin;
|
|
@@ -306,43 +296,46 @@ export class GTINValidator extends NumericIdentifierValidator<GTINType> implemen
|
|
|
306
296
|
// Assume length-validated GTIN is the GTIN (true for all except zero-suppressed GTIN-12).
|
|
307
297
|
let lengthValidatedGTIN = gtin;
|
|
308
298
|
|
|
299
|
+
let prefixType: PrefixType | undefined;
|
|
300
|
+
let allowUPCCompanyPrefix = false;
|
|
301
|
+
let allowGS18Prefix = false;
|
|
302
|
+
let validatePrefix = gtin;
|
|
309
303
|
let gtinLevelRestriction: GTINLevel;
|
|
310
304
|
|
|
311
305
|
switch (gtin.length) {
|
|
312
|
-
case GTINLengths.GTIN13
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
// Validate prefix requiring exact match for prefix type.
|
|
318
|
-
PrefixValidator.validate(PrefixTypes.GS1CompanyPrefix, false, false, gtin, true, true);
|
|
319
|
-
|
|
306
|
+
case GTINLengths.GTIN13:
|
|
307
|
+
// Validate prefix with restrictions on prefix type depending on GTIN level.
|
|
308
|
+
prefixType = PrefixTypes.GS1CompanyPrefix;
|
|
309
|
+
allowUPCCompanyPrefix = gtinLevel !== GTINLevels.RetailConsumer;
|
|
310
|
+
allowGS18Prefix = gtinLevel === GTINLevels.Any;
|
|
320
311
|
gtinLevelRestriction = GTINLevels.Any;
|
|
321
312
|
break;
|
|
322
313
|
|
|
323
|
-
case GTINLengths.GTIN12
|
|
314
|
+
case GTINLengths.GTIN12:
|
|
324
315
|
// Validate prefix requiring exact match for prefix type.
|
|
325
|
-
|
|
326
|
-
|
|
316
|
+
prefixType = PrefixTypes.UPCCompanyPrefix;
|
|
327
317
|
gtinLevelRestriction = GTINLevels.Any;
|
|
328
318
|
break;
|
|
329
319
|
|
|
330
|
-
case GTINLengths.GTIN8
|
|
320
|
+
case GTINLengths.GTIN8:
|
|
331
321
|
// Zero-suppressed GTIN-12 always starts with 0.
|
|
332
322
|
if (!gtin.startsWith("0")) {
|
|
333
323
|
// Validate prefix requiring exact match for prefix type.
|
|
334
|
-
|
|
324
|
+
prefixType = PrefixTypes.GS18Prefix;
|
|
335
325
|
} else {
|
|
336
326
|
lengthValidatedGTIN = GTINValidator.zeroExpand(gtin);
|
|
327
|
+
prefixType = undefined;
|
|
337
328
|
}
|
|
338
329
|
|
|
339
330
|
gtinLevelRestriction = GTINLevels.RetailConsumer;
|
|
340
331
|
break;
|
|
341
332
|
|
|
342
|
-
case GTINLengths.GTIN14
|
|
343
|
-
// Validate prefix
|
|
344
|
-
|
|
345
|
-
|
|
333
|
+
case GTINLengths.GTIN14:
|
|
334
|
+
// Validate prefix with restrictions on prefix type depending on GTIN level.
|
|
335
|
+
prefixType = PrefixTypes.GS1CompanyPrefix;
|
|
336
|
+
allowUPCCompanyPrefix = true;
|
|
337
|
+
allowGS18Prefix = gtinLevel !== GTINLevels.OtherThanRetailConsumer || !gtin.startsWith("0");
|
|
338
|
+
validatePrefix = gtin.substring(1);
|
|
346
339
|
gtinLevelRestriction = GTINLevels.OtherThanRetailConsumer;
|
|
347
340
|
break;
|
|
348
341
|
|
|
@@ -350,6 +343,10 @@ export class GTINValidator extends NumericIdentifierValidator<GTINType> implemen
|
|
|
350
343
|
throw new RangeError(i18nextGS1.t("Identifier.invalidGTINLength"));
|
|
351
344
|
}
|
|
352
345
|
|
|
346
|
+
if (prefixType !== undefined) {
|
|
347
|
+
PrefixValidator.validate(prefixType, allowUPCCompanyPrefix, allowGS18Prefix, validatePrefix, true, true, GTINValidator.#ERROR_MESSAGE_PARSE_KEYS[gtinLevel]);
|
|
348
|
+
}
|
|
349
|
+
|
|
353
350
|
// Validating the check digit will also validate the characters.
|
|
354
351
|
if (!hasValidCheckDigit(lengthValidatedGTIN)) {
|
|
355
352
|
throw new RangeError(i18nextGS1.t("Identifier.invalidCheckDigit"));
|
|
@@ -374,134 +371,6 @@ export class GTINValidator extends NumericIdentifierValidator<GTINType> implemen
|
|
|
374
371
|
|
|
375
372
|
GTINValidator.validateAny(gtin14);
|
|
376
373
|
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Parse a Restricted Circulation Number (RCN) using a variable measure trade item format. The format is a 12- or
|
|
380
|
-
* 13-character string (for RCN-12 or RCN-13 respectively), containing the following:
|
|
381
|
-
*
|
|
382
|
-
* - '2' - The first character of the RCN.
|
|
383
|
-
* - '0'-'9' - The second character of the RCN (RCN-13 only).
|
|
384
|
-
* - 'I' - One or more, in sequence, for the item reference.
|
|
385
|
-
* - 'P' - One or more, in sequence, for the price or weight.
|
|
386
|
-
* - 'V' - Zero or one, for the price or weight check digit.
|
|
387
|
-
* - 'C' - The check digit of the entire RCN.
|
|
388
|
-
*
|
|
389
|
-
* The 'I', 'P', and 'V' formats may be in any order.
|
|
390
|
-
*
|
|
391
|
-
* Some examples:
|
|
392
|
-
*
|
|
393
|
-
* - 2IIIIIVPPPPC - RCN-12 with a five-digit item reference, a price or weight check digit, and a four-digit price
|
|
394
|
-
* or weight.
|
|
395
|
-
* - 23IIIIVPPPPPC - RCN-13 with a four-digit item reference, a price or weight check digit, and a five-digit price
|
|
396
|
-
* or weight.
|
|
397
|
-
* - 2IIIIIIPPPPC - RCN-12 with a six-digit item reference and a four-digit price or eight.
|
|
398
|
-
* - 29IIIIIPPPPPC - RCN-13 with a five-digit item reference and a five-digit price or weight.
|
|
399
|
-
*
|
|
400
|
-
* @param format
|
|
401
|
-
* Format.
|
|
402
|
-
*
|
|
403
|
-
* @param rcn
|
|
404
|
-
* RCN.
|
|
405
|
-
*
|
|
406
|
-
* @returns
|
|
407
|
-
* RCN reference.
|
|
408
|
-
*/
|
|
409
|
-
static parseVariableMeasureRCN(format: string, rcn: string): RCNReference {
|
|
410
|
-
const formatLength = format.length;
|
|
411
|
-
|
|
412
|
-
if (rcn.length !== formatLength) {
|
|
413
|
-
throw new RangeError(i18nextGS1.t("Identifier.invalidRCNLength"));
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
let validFormat = formatLength === 12 || formatLength === 13;
|
|
417
|
-
let validRCNPrefix = true;
|
|
418
|
-
|
|
419
|
-
let buildingItemReference = false;
|
|
420
|
-
let itemReference = "";
|
|
421
|
-
|
|
422
|
-
let buildingPriceOrWeight = false;
|
|
423
|
-
let priceOrWeight = "";
|
|
424
|
-
|
|
425
|
-
let priceOrWeightCheckDigit = "";
|
|
426
|
-
|
|
427
|
-
for (let index = 0; validFormat && index < formatLength; index++) {
|
|
428
|
-
const formatChar = format.charAt(index);
|
|
429
|
-
const rcnChar = rcn.charAt(index);
|
|
430
|
-
|
|
431
|
-
if (index === 0) {
|
|
432
|
-
validFormat = formatChar === "2";
|
|
433
|
-
validRCNPrefix = rcnChar === "2";
|
|
434
|
-
} else if (formatLength === 13 && index === 1) {
|
|
435
|
-
validFormat = NUMERIC_CREATOR.characterIndex(formatChar) !== undefined;
|
|
436
|
-
validRCNPrefix = rcnChar === formatChar;
|
|
437
|
-
} else if (index === formatLength - 1) {
|
|
438
|
-
validFormat = formatChar === "C";
|
|
439
|
-
} else {
|
|
440
|
-
switch (formatChar) {
|
|
441
|
-
case "I":
|
|
442
|
-
if (!buildingItemReference) {
|
|
443
|
-
// Item reference can't appear more than once.
|
|
444
|
-
validFormat = itemReference === "";
|
|
445
|
-
|
|
446
|
-
buildingItemReference = true;
|
|
447
|
-
buildingPriceOrWeight = false;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
itemReference += rcnChar;
|
|
451
|
-
break;
|
|
452
|
-
|
|
453
|
-
case "P":
|
|
454
|
-
if (!buildingPriceOrWeight) {
|
|
455
|
-
// Price or weight can't appear more than once.
|
|
456
|
-
validFormat = priceOrWeight === "";
|
|
457
|
-
|
|
458
|
-
buildingPriceOrWeight = true;
|
|
459
|
-
buildingItemReference = false;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
priceOrWeight += rcnChar;
|
|
463
|
-
break;
|
|
464
|
-
|
|
465
|
-
case "V":
|
|
466
|
-
// Price or weight check digit can't appear more than once.
|
|
467
|
-
validFormat = priceOrWeightCheckDigit === "";
|
|
468
|
-
|
|
469
|
-
buildingItemReference = false;
|
|
470
|
-
buildingPriceOrWeight = false;
|
|
471
|
-
|
|
472
|
-
priceOrWeightCheckDigit = rcnChar;
|
|
473
|
-
break;
|
|
474
|
-
|
|
475
|
-
default:
|
|
476
|
-
validFormat = false;
|
|
477
|
-
break;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
validFormat &&= itemReference !== "" && priceOrWeight !== "";
|
|
483
|
-
|
|
484
|
-
if (!validFormat) {
|
|
485
|
-
throw new RangeError(i18nextGS1.t("Identifier.invalidVariableMeasureRCNFormat"));
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
if (!validRCNPrefix) {
|
|
489
|
-
throw new RangeError(i18nextGS1.t("Identifier.invalidVariableMeasureRCNPrefix"));
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
if (priceOrWeightCheckDigit !== "" && !isValidPriceOrWeightCheckDigit(priceOrWeight, priceOrWeightCheckDigit)) {
|
|
493
|
-
throw new RangeError(i18nextGS1.t("Identifier.invalidVariableMeasurePriceOrWeight"));
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
if (!hasValidCheckDigit(rcn)) {
|
|
497
|
-
throw new RangeError(i18nextGS1.t("Identifier.invalidCheckDigit"));
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
return {
|
|
501
|
-
itemReference: Number(itemReference),
|
|
502
|
-
priceOrWeight: Number(priceOrWeight)
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
374
|
}
|
|
506
375
|
|
|
507
376
|
/**
|
|
@@ -120,13 +120,10 @@ export abstract class IdentifierValidator<TIdentifierType extends IdentifierType
|
|
|
120
120
|
*
|
|
121
121
|
* @param partialIdentifier
|
|
122
122
|
* Partial identifier.
|
|
123
|
-
*
|
|
124
|
-
* @param positionOffset
|
|
125
|
-
* Position offset within a larger string.
|
|
126
123
|
*/
|
|
127
|
-
protected validatePrefix(partialIdentifier: string
|
|
124
|
+
protected validatePrefix(partialIdentifier: string): void {
|
|
128
125
|
// Delegate to prefix validator with support for U.P.C. Company Prefix but not GS1-8 Prefix.
|
|
129
|
-
PrefixValidator.validate(this.prefixType, true, false, partialIdentifier, true, this.referenceCharacterSet === ContentCharacterSets.Numeric
|
|
126
|
+
PrefixValidator.validate(this.prefixType, true, false, partialIdentifier, true, this.referenceCharacterSet === ContentCharacterSets.Numeric);
|
|
130
127
|
}
|
|
131
128
|
|
|
132
129
|
/**
|
package/src/index.ts
CHANGED
|
@@ -47,13 +47,15 @@ export * from "./identifier-descriptors.js";
|
|
|
47
47
|
|
|
48
48
|
export * from "./identifier-validator.js";
|
|
49
49
|
export * from "./numeric-identifier-validator.js";
|
|
50
|
-
export { GTINLevels, type GTINLevelKey, type GTINLevel,
|
|
50
|
+
export { GTINLevels, type GTINLevelKey, type GTINLevel, GTINValidator } from "./gtin-validator.js";
|
|
51
51
|
export * from "./non-gtin-numeric-identifier-validator.js";
|
|
52
52
|
export * from "./non-serializable-numeric-identifier-validator.js";
|
|
53
53
|
export * from "./serializable-numeric-identifier-validator.js";
|
|
54
54
|
export * from "./non-numeric-identifier-validator.js";
|
|
55
55
|
export * from "./identifier-validators.js";
|
|
56
56
|
|
|
57
|
+
export * from "./verified-by-gs1.js";
|
|
58
|
+
|
|
57
59
|
export type * from "./identifier-creator.js";
|
|
58
60
|
export type * from "./numeric-identifier-creator.js";
|
|
59
61
|
export * from "./gtin-creator.js";
|
|
@@ -63,4 +65,8 @@ export * from "./serializable-numeric-identifier-creator.js";
|
|
|
63
65
|
export * from "./non-numeric-identifier-creator.js";
|
|
64
66
|
export * from "./identifier-creators.js";
|
|
65
67
|
|
|
68
|
+
export * from "./variable-measure.js";
|
|
69
|
+
|
|
70
|
+
export * from "./gcp-length-data.js";
|
|
71
|
+
export * from "./gcp-length-cache.js";
|
|
66
72
|
export * from "./prefix-manager.js";
|
|
@@ -13,7 +13,6 @@ export default {
|
|
|
13
13
|
invalidZeroSuppressibleGTIN12: "GTIN-12 not zero-suppressible",
|
|
14
14
|
invalidZeroSuppressedGTIN12AsGTIN13: "Invalid zero-suppressed GTIN-12 as GTIN-13",
|
|
15
15
|
invalidZeroSuppressedGTIN12AsGTIN14: "Invalid zero-suppressed GTIN-12 as GTIN-14",
|
|
16
|
-
invalidGTIN13AtRetail: "GTIN-13 at retail consumer trade item level can't start with zero",
|
|
17
16
|
invalidGTINAtRetail: "GTIN not supported at retail consumer trade item level",
|
|
18
17
|
invalidGTINAtOtherThanRetail: "GTIN not supported at other than retail consumer trade item level",
|
|
19
18
|
invalidRCNLength: "RCN length must match format length",
|
|
@@ -35,6 +34,8 @@ export default {
|
|
|
35
34
|
gs1CompanyPrefixCantStartWith000000: "GS1 Company Prefix can't start with \"000000\"",
|
|
36
35
|
upcCompanyPrefixCantStartWith0000: "U.P.C. Company Prefix can't start with \"0000\"",
|
|
37
36
|
gs18PrefixCantStartWith0: "GS1-8 Prefix can't start with \"0\"",
|
|
38
|
-
identifierTypeNotSupportedByGS18Prefix: "{{identifierType}} not supported by GS1-8 Prefix"
|
|
37
|
+
identifierTypeNotSupportedByGS18Prefix: "{{identifierType}} not supported by GS1-8 Prefix",
|
|
38
|
+
gs1CompanyPrefixLengthDataHTTPError: "HTTP error {{status, number}} loading GS1 Company Prefix length data",
|
|
39
|
+
gs1CompanyPrefixLengthDataNotLoaded: "GS1 Company Prefix length data not loaded"
|
|
39
40
|
}
|
|
40
41
|
} as const;
|
|
@@ -13,7 +13,6 @@ export default {
|
|
|
13
13
|
invalidZeroSuppressibleGTIN12: "Le GTIN-12 ne peut pas être supprimé par zéro",
|
|
14
14
|
invalidZeroSuppressedGTIN12AsGTIN13: "GTIN-12 non valide avec zéro supprimé en tant que GTIN-13",
|
|
15
15
|
invalidZeroSuppressedGTIN12AsGTIN14: "GTIN-12 non valide avec zéro supprimé en tant que GTIN-14",
|
|
16
|
-
invalidGTIN13AtRetail: "Le GTIN-13 au niveau des articles de consommation au détail ne peut pas commencer par zéro",
|
|
17
16
|
invalidGTINAtRetail: "Le GTIN n'est pas pris en charge au niveau des articles de consommation au détail",
|
|
18
17
|
invalidGTINAtOtherThanRetail: "Le GTIN n'est pas pris en charge à d'autres niveaux que ceux des articles de consommation au détail",
|
|
19
18
|
invalidRCNLength: "La longueur du RCN doit correspondre à la longueur du format",
|
|
@@ -35,6 +34,8 @@ export default {
|
|
|
35
34
|
gs1CompanyPrefixCantStartWith000000: "Le préfixe de l'entreprise GS1 ne peut pas commencer par \"000000\"",
|
|
36
35
|
upcCompanyPrefixCantStartWith0000: "Le préfixe de l'entreprise U.P.C. ne peut pas commencer par \"0000\"",
|
|
37
36
|
gs18PrefixCantStartWith0: "Le préfixe GS1-8 ne peut pas commencer par \"0\"",
|
|
38
|
-
identifierTypeNotSupportedByGS18Prefix: "{{identifierType}} non pris en charge par le préfixe GS1-8"
|
|
37
|
+
identifierTypeNotSupportedByGS18Prefix: "{{identifierType}} non pris en charge par le préfixe GS1-8",
|
|
38
|
+
gs1CompanyPrefixLengthDataHTTPError: "Erreur HTTP {{status, number}} lors du chargement des données de longueur du préfixe d'entreprise GS1",
|
|
39
|
+
gs1CompanyPrefixLengthDataNotLoaded: "Les données relatives à la longueur du préfixe d'entreprise GS1 n'ont pas été chargées"
|
|
39
40
|
}
|
|
40
41
|
} as const;
|
package/src/locale/i18n.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type I18nEnvironment, i18nFinalizeInit } from "@aidc-toolkit/core";
|
|
2
2
|
import { i18nUtilityInit, utilityResources } from "@aidc-toolkit/utility";
|
|
3
3
|
import i18next, { type i18n, type Resource } from "i18next";
|
|
4
4
|
import enLocaleResources from "./en/locale-resources.js";
|
|
@@ -34,11 +34,8 @@ export const i18nextGS1: i18n = i18next.createInstance();
|
|
|
34
34
|
*
|
|
35
35
|
* @param debug
|
|
36
36
|
* Debug setting.
|
|
37
|
-
*
|
|
38
|
-
* @returns
|
|
39
|
-
* Void promise.
|
|
40
37
|
*/
|
|
41
38
|
export async function i18nGS1Init(environment: I18nEnvironment, debug = false): Promise<void> {
|
|
42
39
|
await i18nUtilityInit(environment, debug);
|
|
43
|
-
await
|
|
40
|
+
await i18nFinalizeInit(i18nextGS1, environment, debug, gs1NS, utilityResources, gs1Resources);
|
|
44
41
|
}
|
|
@@ -30,7 +30,7 @@ export class NonNumericIdentifierValidator extends IdentifierValidator<NonNumeri
|
|
|
30
30
|
protected override createErrorMessage(_s: string): string {
|
|
31
31
|
return i18nextGS1.t("Identifier.referenceCantBeAllNumeric");
|
|
32
32
|
}
|
|
33
|
-
}(/\D/);
|
|
33
|
+
}(/\D/u);
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* True if the identifier requires a check character pair.
|
|
@@ -59,9 +59,9 @@ export abstract class NumericIdentifierValidator<TNumericIdentifierType extends
|
|
|
59
59
|
validate(identifier: string, validation?: NumericIdentifierValidation): void {
|
|
60
60
|
// Validate the prefix, with care taken for its position within the identifier.
|
|
61
61
|
if (this.#prefixPosition === 0) {
|
|
62
|
-
super.validatePrefix(identifier
|
|
62
|
+
super.validatePrefix(identifier);
|
|
63
63
|
} else {
|
|
64
|
-
super.validatePrefix(identifier.substring(this.#prefixPosition)
|
|
64
|
+
super.validatePrefix(identifier.substring(this.#prefixPosition));
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// Validate the length.
|
package/src/prefix-manager.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as GCPLength from "./gcp-length.js";
|
|
1
2
|
import type { GTINCreator } from "./gtin-creator.js";
|
|
2
3
|
import { GTIN_BASE_TYPES } from "./gtin-length.js";
|
|
3
4
|
import type { GTINType } from "./gtin-type.js";
|
|
@@ -17,6 +18,7 @@ import type { PrefixProvider } from "./prefix-provider.js";
|
|
|
17
18
|
import { type PrefixType, PrefixTypes } from "./prefix-type.js";
|
|
18
19
|
import { PrefixValidator } from "./prefix-validator.js";
|
|
19
20
|
import type { SerializableNumericIdentifierCreator } from "./serializable-numeric-identifier-creator.js";
|
|
21
|
+
import type { GCPLengthCache } from "./gcp-length-cache.js";
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* Prefix manager. This is the core class for identifier creation.
|
|
@@ -99,6 +101,11 @@ export class PrefixManager implements PrefixProvider {
|
|
|
99
101
|
*/
|
|
100
102
|
readonly #identifierCreators: Partial<IdentifierCreatorsRecord> = {};
|
|
101
103
|
|
|
104
|
+
/**
|
|
105
|
+
* GS1 Company Prefix length root.
|
|
106
|
+
*/
|
|
107
|
+
static #gcpLengthRoot: GCPLength.Root | undefined = undefined;
|
|
108
|
+
|
|
102
109
|
/**
|
|
103
110
|
* Constructor.
|
|
104
111
|
*
|
|
@@ -350,4 +357,62 @@ export class PrefixManager implements PrefixProvider {
|
|
|
350
357
|
get gmnCreator(): NonNumericIdentifierCreator {
|
|
351
358
|
return this.getIdentifierCreator(IdentifierTypes.GMN);
|
|
352
359
|
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Load GS1 Company Prefix length data.
|
|
363
|
+
*
|
|
364
|
+
* @param gcpLengthCache
|
|
365
|
+
* GS1 Company Prefix length cache.
|
|
366
|
+
*/
|
|
367
|
+
static async loadGCPLengthData(gcpLengthCache: GCPLengthCache): Promise<void> {
|
|
368
|
+
PrefixManager.#gcpLengthRoot = await GCPLength.loadData(gcpLengthCache);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Get the length of a GS1 Company Prefix for an identifier.
|
|
373
|
+
*
|
|
374
|
+
* @param identifierType
|
|
375
|
+
* Identifier type.
|
|
376
|
+
*
|
|
377
|
+
* @param identifier
|
|
378
|
+
* Identifier.
|
|
379
|
+
*
|
|
380
|
+
* @returns
|
|
381
|
+
* Length of GS1 Company Prefix, 0 if not a GS1 Company Prefix, or -1 if not found.
|
|
382
|
+
*/
|
|
383
|
+
static gcpLength(identifierType: IdentifierType, identifier: string): number {
|
|
384
|
+
if (PrefixManager.#gcpLengthRoot === undefined) {
|
|
385
|
+
throw new RangeError(i18nextGS1.t("Prefix.gs1CompanyPrefixLengthDataNotLoaded"));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return GCPLength.getFor(PrefixManager.#gcpLengthRoot, identifierType, identifier);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Get the date/time the GS1 Company Prefix length data was last updated.
|
|
393
|
+
*
|
|
394
|
+
* @returns
|
|
395
|
+
* Date/time the GS1 Company Prefix length data was last updated.
|
|
396
|
+
*/
|
|
397
|
+
static gcpLengthDateTime(): Date {
|
|
398
|
+
if (PrefixManager.#gcpLengthRoot === undefined) {
|
|
399
|
+
throw new RangeError(i18nextGS1.t("Prefix.gs1CompanyPrefixLengthDataNotLoaded"));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return PrefixManager.#gcpLengthRoot.dateTime;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Get the disclaimer for the GS1 Company Prefix length data.
|
|
407
|
+
*
|
|
408
|
+
* @returns
|
|
409
|
+
* Disclaimer for the GS1 Company Prefix length data.
|
|
410
|
+
*/
|
|
411
|
+
static gcpLengthDisclaimer(): string {
|
|
412
|
+
if (PrefixManager.#gcpLengthRoot === undefined) {
|
|
413
|
+
throw new RangeError(i18nextGS1.t("Prefix.gs1CompanyPrefixLengthDataNotLoaded"));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return PrefixManager.#gcpLengthRoot.disclaimer;
|
|
417
|
+
}
|
|
353
418
|
}
|
package/src/prefix-validator.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type CharacterSetValidation, NUMERIC_CREATOR } from "@aidc-toolkit/util
|
|
|
2
2
|
import { i18nextGS1 } from "./locale/i18n.js";
|
|
3
3
|
import type { PrefixProvider } from "./prefix-provider.js";
|
|
4
4
|
import { type PrefixType, PrefixTypes } from "./prefix-type.js";
|
|
5
|
+
import type { ParseKeys } from "i18next";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Prefix validation parameters.
|
|
@@ -36,7 +37,7 @@ const GS1_COMPANY_PREFIX_MAXIMUM_LENGTH = 12;
|
|
|
36
37
|
/**
|
|
37
38
|
* U.P.C. Company Prefix minimum length.
|
|
38
39
|
*/
|
|
39
|
-
const UPC_COMPANY_PREFIX_MINIMUM_LENGTH =
|
|
40
|
+
const UPC_COMPANY_PREFIX_MINIMUM_LENGTH = 5;
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* U.P.C. Company Prefix maximum length.
|
|
@@ -130,31 +131,31 @@ export const PrefixValidator = {
|
|
|
130
131
|
* @param isNumericIdentifier
|
|
131
132
|
* If true, the prefix is from a numeric identifier and its character set will be validated by the caller.
|
|
132
133
|
*
|
|
133
|
-
* @param
|
|
134
|
-
*
|
|
134
|
+
* @param errorMessageParseKey
|
|
135
|
+
* Parse key for error message when validating GS1 Company Prefix type.
|
|
135
136
|
*/
|
|
136
|
-
validate(prefixType: PrefixType, allowUPCCompanyPrefix: boolean, allowGS18Prefix: boolean, prefix: string, isFromIdentifier = false, isNumericIdentifier = false,
|
|
137
|
-
let
|
|
137
|
+
validate(prefixType: PrefixType, allowUPCCompanyPrefix: boolean, allowGS18Prefix: boolean, prefix: string, isFromIdentifier = false, isNumericIdentifier = false, errorMessageParseKey?: ParseKeys): void {
|
|
138
|
+
let validation: PrefixValidation;
|
|
138
139
|
|
|
139
140
|
// Validate the prefix type and determine the prefix validation parameters.
|
|
140
141
|
switch (prefixType) {
|
|
141
142
|
case PrefixTypes.GS1CompanyPrefix:
|
|
142
143
|
if (!prefix.startsWith("0")) {
|
|
143
|
-
|
|
144
|
+
validation = GS1_COMPANY_PREFIX_VALIDATION;
|
|
144
145
|
} else if (!prefix.startsWith("00000")) {
|
|
145
146
|
if (!allowUPCCompanyPrefix) {
|
|
146
|
-
throw new RangeError(i18nextGS1.t("Prefix.gs1CompanyPrefixCantStartWith0"));
|
|
147
|
+
throw new RangeError(i18nextGS1.t(errorMessageParseKey ?? "Prefix.gs1CompanyPrefixCantStartWith0"));
|
|
147
148
|
}
|
|
148
149
|
|
|
149
|
-
|
|
150
|
+
validation = UPC_COMPANY_PREFIX_AS_GS1_COMPANY_PREFIX_VALIDATION;
|
|
150
151
|
} else if (!prefix.startsWith("000000")) {
|
|
151
152
|
if (!allowGS18Prefix) {
|
|
152
|
-
throw new RangeError(i18nextGS1.t("Prefix.gs1CompanyPrefixCantStartWith00000"));
|
|
153
|
+
throw new RangeError(i18nextGS1.t(errorMessageParseKey ?? "Prefix.gs1CompanyPrefixCantStartWith00000"));
|
|
153
154
|
}
|
|
154
155
|
|
|
155
|
-
|
|
156
|
+
validation = GS1_8_PREFIX_AS_GS1_COMPANY_PREFIX_VALIDATION;
|
|
156
157
|
} else {
|
|
157
|
-
throw new RangeError(i18nextGS1.t("Prefix.gs1CompanyPrefixCantStartWith000000"));
|
|
158
|
+
throw new RangeError(i18nextGS1.t(errorMessageParseKey ?? "Prefix.gs1CompanyPrefixCantStartWith000000"));
|
|
158
159
|
}
|
|
159
160
|
break;
|
|
160
161
|
|
|
@@ -163,7 +164,7 @@ export const PrefixValidator = {
|
|
|
163
164
|
throw new RangeError(i18nextGS1.t("Prefix.upcCompanyPrefixCantStartWith0000"));
|
|
164
165
|
}
|
|
165
166
|
|
|
166
|
-
|
|
167
|
+
validation = UPC_COMPANY_PREFIX_VALIDATION;
|
|
167
168
|
break;
|
|
168
169
|
|
|
169
170
|
case PrefixTypes.GS18Prefix:
|
|
@@ -171,21 +172,16 @@ export const PrefixValidator = {
|
|
|
171
172
|
throw new RangeError(i18nextGS1.t("Prefix.gs18PrefixCantStartWith0"));
|
|
172
173
|
}
|
|
173
174
|
|
|
174
|
-
|
|
175
|
+
validation = GS1_8_PREFIX_VALIDATION;
|
|
175
176
|
break;
|
|
176
177
|
}
|
|
177
178
|
|
|
178
|
-
|
|
179
|
-
...baseValidation,
|
|
180
|
-
positionOffset
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
// If from key and numeric, key validation will take care of character set validation.
|
|
179
|
+
// If from identifier and numeric, identifier validation will take care of character set validation.
|
|
184
180
|
if (!isFromIdentifier) {
|
|
185
|
-
NUMERIC_CREATOR.validate(prefix,
|
|
181
|
+
NUMERIC_CREATOR.validate(prefix, validation);
|
|
186
182
|
} else if (!isNumericIdentifier) {
|
|
187
183
|
// Validate only the minimum length, allowing at least one character for the (possibly non-numeric) reference.
|
|
188
|
-
NUMERIC_CREATOR.validate(prefix.substring(0, Math.min(
|
|
184
|
+
NUMERIC_CREATOR.validate(prefix.substring(0, Math.min(validation.minimumLength, prefix.length - 1)), validation);
|
|
189
185
|
}
|
|
190
186
|
},
|
|
191
187
|
|
|
@@ -214,11 +210,11 @@ export const PrefixValidator = {
|
|
|
214
210
|
break;
|
|
215
211
|
|
|
216
212
|
case PrefixTypes.UPCCompanyPrefix:
|
|
217
|
-
gs1CompanyPrefix =
|
|
213
|
+
gs1CompanyPrefix = `0${prefix}`;
|
|
218
214
|
break;
|
|
219
215
|
|
|
220
216
|
case PrefixTypes.GS18Prefix:
|
|
221
|
-
gs1CompanyPrefix =
|
|
217
|
+
gs1CompanyPrefix = `00000${prefix}`;
|
|
222
218
|
break;
|
|
223
219
|
}
|
|
224
220
|
|