@aidc-toolkit/gs1 1.0.31-beta → 1.0.33-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.
Files changed (110) hide show
  1. package/README.md +290 -17
  2. package/dist/character-set.d.ts +17 -2
  3. package/dist/character-set.d.ts.map +1 -1
  4. package/dist/character-set.js +44 -11
  5. package/dist/character-set.js.map +1 -1
  6. package/dist/gcp-length-cache.d.ts +81 -0
  7. package/dist/gcp-length-cache.d.ts.map +1 -0
  8. package/dist/gcp-length-cache.js +232 -0
  9. package/dist/gcp-length-cache.js.map +1 -0
  10. package/dist/gcp-length-data.d.ts +108 -0
  11. package/dist/gcp-length-data.d.ts.map +1 -0
  12. package/dist/gcp-length-data.js +53 -0
  13. package/dist/gcp-length-data.js.map +1 -0
  14. package/dist/gcp-length.d.ts +61 -0
  15. package/dist/gcp-length.d.ts.map +1 -0
  16. package/dist/gcp-length.js +300 -0
  17. package/dist/gcp-length.js.map +1 -0
  18. package/dist/gtin-creator.d.ts +0 -17
  19. package/dist/gtin-creator.d.ts.map +1 -1
  20. package/dist/gtin-creator.js +1 -93
  21. package/dist/gtin-creator.js.map +1 -1
  22. package/dist/gtin-validator.d.ts +1 -46
  23. package/dist/gtin-validator.d.ts.map +1 -1
  24. package/dist/gtin-validator.js +31 -125
  25. package/dist/gtin-validator.js.map +1 -1
  26. package/dist/identifier-validator.d.ts +1 -4
  27. package/dist/identifier-validator.d.ts.map +1 -1
  28. package/dist/identifier-validator.js +2 -5
  29. package/dist/identifier-validator.js.map +1 -1
  30. package/dist/index.d.ts +5 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +4 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/locale/en/locale-resources.d.ts +39 -35
  35. package/dist/locale/en/locale-resources.d.ts.map +1 -1
  36. package/dist/locale/en/locale-resources.js +6 -2
  37. package/dist/locale/en/locale-resources.js.map +1 -1
  38. package/dist/locale/fr/locale-resources.d.ts +39 -35
  39. package/dist/locale/fr/locale-resources.d.ts.map +1 -1
  40. package/dist/locale/fr/locale-resources.js +6 -2
  41. package/dist/locale/fr/locale-resources.js.map +1 -1
  42. package/dist/locale/i18n.d.ts +4 -4
  43. package/dist/locale/i18n.d.ts.map +1 -1
  44. package/dist/locale/i18n.js +6 -7
  45. package/dist/locale/i18n.js.map +1 -1
  46. package/dist/non-numeric-identifier-validator.js +1 -1
  47. package/dist/non-numeric-identifier-validator.js.map +1 -1
  48. package/dist/numeric-identifier-validator.d.ts.map +1 -1
  49. package/dist/numeric-identifier-validator.js +2 -2
  50. package/dist/numeric-identifier-validator.js.map +1 -1
  51. package/dist/prefix-manager.d.ts +35 -0
  52. package/dist/prefix-manager.d.ts.map +1 -1
  53. package/dist/prefix-manager.js +56 -0
  54. package/dist/prefix-manager.js.map +1 -1
  55. package/dist/prefix-validator.d.ts +5 -4
  56. package/dist/prefix-validator.d.ts.map +1 -1
  57. package/dist/prefix-validator.js +18 -22
  58. package/dist/prefix-validator.js.map +1 -1
  59. package/dist/serializable-numeric-identifier-validator.d.ts +26 -0
  60. package/dist/serializable-numeric-identifier-validator.d.ts.map +1 -1
  61. package/dist/serializable-numeric-identifier-validator.js +19 -0
  62. package/dist/serializable-numeric-identifier-validator.js.map +1 -1
  63. package/dist/variable-measure.d.ts +68 -0
  64. package/dist/variable-measure.d.ts.map +1 -0
  65. package/dist/variable-measure.js +210 -0
  66. package/dist/variable-measure.js.map +1 -0
  67. package/dist/verified-by-gs1.d.ts +22 -0
  68. package/dist/verified-by-gs1.d.ts.map +1 -0
  69. package/dist/verified-by-gs1.js +46 -0
  70. package/dist/verified-by-gs1.js.map +1 -0
  71. package/package.json +12 -11
  72. package/src/character-set.ts +55 -11
  73. package/src/gcp-length-cache.ts +271 -0
  74. package/src/gcp-length-data.ts +136 -0
  75. package/src/gcp-length.ts +380 -0
  76. package/src/gtin-creator.ts +1 -117
  77. package/src/gtin-validator.ts +42 -173
  78. package/src/identifier-validator.ts +2 -5
  79. package/src/index.ts +7 -1
  80. package/src/locale/en/locale-resources.ts +7 -3
  81. package/src/locale/fr/locale-resources.ts +7 -3
  82. package/src/locale/i18n.ts +7 -8
  83. package/src/locale/i18next.d.ts +2 -0
  84. package/src/non-numeric-identifier-validator.ts +1 -1
  85. package/src/numeric-identifier-validator.ts +3 -3
  86. package/src/prefix-manager.ts +65 -0
  87. package/src/prefix-validator.ts +19 -23
  88. package/src/serializable-numeric-identifier-validator.ts +36 -0
  89. package/src/variable-measure.ts +268 -0
  90. package/src/verified-by-gs1.ts +54 -0
  91. package/test/character-set.test.ts +46 -0
  92. package/test/creator.test.ts +5 -5
  93. package/test/data/gcpprefixformatlist-1.json +662625 -0
  94. package/test/data/gcpprefixformatlist-2.json +735431 -0
  95. package/test/gcp-length.test.ts +344 -0
  96. package/test/gtin-creator.ts +4 -4
  97. package/test/gtin-validator.test.ts +205 -113
  98. package/test/gtin-validator.ts +30 -0
  99. package/test/identifier-creator.ts +6 -6
  100. package/test/non-numeric-identifier-creator.ts +0 -8
  101. package/test/non-serializable-numeric-identifier-creator.ts +4 -54
  102. package/test/numeric-identifier-creator.ts +4 -4
  103. package/test/prefix-manager.test.ts +5 -5
  104. package/test/serializable-numeric-identifier-creator.ts +32 -19
  105. package/test/validator.test.ts +6 -6
  106. package/test/variable-measure-rcn.test.ts +63 -68
  107. package/test/verified-by-gs1.test.ts +55 -0
  108. package/tsconfig-src.json +7 -1
  109. package/tsconfig-src.tsbuildinfo +1 -0
  110. package/tsconfig-tsup.json +7 -0
@@ -1,5 +1,6 @@
1
1
  import { type CharacterSetValidation, NUMERIC_CREATOR } from "@aidc-toolkit/utility";
2
- import { checkDigit, hasValidCheckDigit, isValidPriceOrWeightCheckDigit } from "./check.js";
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, positionOffset?: number): void {
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, positionOffset);
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 && indicatorDigit !== gtin14.charAt(0)) {
211
- const partialGTIN14 = indicatorDigit + gtin14.substring(1, GTINLengths.GTIN14 - 1);
200
+ if (indicatorDigit.length !== 0 && !gtin14.startsWith(indicatorDigit)) {
201
+ const partialGTIN14 = `${indicatorDigit}${gtin14.substring(1, GTINLengths.GTIN14 - 1)}`;
212
202
 
213
- gtin14 = partialGTIN14 + checkDigit(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 as number:
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 as number:
243
+ case GTINLengths.GTIN12:
254
244
  // GTIN is GTIN-12.
255
245
  normalizedGTIN = gtin;
256
246
  break;
257
247
 
258
- case GTINLengths.GTIN8 as number:
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 as number:
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 as number:
313
- if (gtin.startsWith("0")) {
314
- throw new RangeError(i18nextGS1.t("Identifier.invalidGTIN13AtRetail"));
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 as number:
314
+ case GTINLengths.GTIN12:
324
315
  // Validate prefix requiring exact match for prefix type.
325
- PrefixValidator.validate(PrefixTypes.UPCCompanyPrefix, false, false, gtin, true, true);
326
-
316
+ prefixType = PrefixTypes.UPCCompanyPrefix;
327
317
  gtinLevelRestriction = GTINLevels.Any;
328
318
  break;
329
319
 
330
- case GTINLengths.GTIN8 as number:
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
- PrefixValidator.validate(PrefixTypes.GS18Prefix, false, false, gtin, true, true);
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 as number:
343
- // Validate prefix supporting any prefix type.
344
- PrefixValidator.validate(PrefixTypes.GS1CompanyPrefix, true, true, gtin.substring(1), true, true);
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, positionOffset?: number): void {
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, positionOffset);
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, type RCNReference, GTINValidator } from "./gtin-validator.js";
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";
@@ -1,4 +1,7 @@
1
1
  export default {
2
+ AI64CharacterSetValidator: {
3
+ lengthMustBeMultipleOf4: "Length {{length, number}} must be a multiple of 4"
4
+ },
2
5
  Check: {
3
6
  lengthOfStringForPriceOrWeightMustBeExactly: "Length {{length, number}} of string for price or weight must be 4 or 5",
4
7
  priceOrWeightComponent: "price or weight",
@@ -13,7 +16,6 @@ export default {
13
16
  invalidZeroSuppressibleGTIN12: "GTIN-12 not zero-suppressible",
14
17
  invalidZeroSuppressedGTIN12AsGTIN13: "Invalid zero-suppressed GTIN-12 as GTIN-13",
15
18
  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
19
  invalidGTINAtRetail: "GTIN not supported at retail consumer trade item level",
18
20
  invalidGTINAtOtherThanRetail: "GTIN not supported at other than retail consumer trade item level",
19
21
  invalidRCNLength: "RCN length must match format length",
@@ -35,6 +37,8 @@ export default {
35
37
  gs1CompanyPrefixCantStartWith000000: "GS1 Company Prefix can't start with \"000000\"",
36
38
  upcCompanyPrefixCantStartWith0000: "U.P.C. Company Prefix can't start with \"0000\"",
37
39
  gs18PrefixCantStartWith0: "GS1-8 Prefix can't start with \"0\"",
38
- identifierTypeNotSupportedByGS18Prefix: "{{identifierType}} not supported by GS1-8 Prefix"
40
+ identifierTypeNotSupportedByGS18Prefix: "{{identifierType}} not supported by GS1-8 Prefix",
41
+ gs1CompanyPrefixLengthDataFileNotFound: "GS1 Company Prefix length data \"{{key}}\" not found",
42
+ gs1CompanyPrefixLengthDataNotLoaded: "GS1 Company Prefix length data not loaded"
39
43
  }
40
- } as const;
44
+ };
@@ -1,4 +1,7 @@
1
1
  export default {
2
+ AI64CharacterSetValidator: {
3
+ lengthMustBeMultipleOf4: "La longueur {{length, number}} doit être un multiple de 4"
4
+ },
2
5
  Check: {
3
6
  lengthOfStringForPriceOrWeightMustBeExactly: "La longueur {{length, number}} de la chaîne pour le prix ou le poids doit être 4 ou 5",
4
7
  priceOrWeightComponent: "prix ou poids",
@@ -13,7 +16,6 @@ export default {
13
16
  invalidZeroSuppressibleGTIN12: "Le GTIN-12 ne peut pas être supprimé par zéro",
14
17
  invalidZeroSuppressedGTIN12AsGTIN13: "GTIN-12 non valide avec zéro supprimé en tant que GTIN-13",
15
18
  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
19
  invalidGTINAtRetail: "Le GTIN n'est pas pris en charge au niveau des articles de consommation au détail",
18
20
  invalidGTINAtOtherThanRetail: "Le GTIN n'est pas pris en charge à d'autres niveaux que ceux des articles de consommation au détail",
19
21
  invalidRCNLength: "La longueur du RCN doit correspondre à la longueur du format",
@@ -35,6 +37,8 @@ export default {
35
37
  gs1CompanyPrefixCantStartWith000000: "Le préfixe de l'entreprise GS1 ne peut pas commencer par \"000000\"",
36
38
  upcCompanyPrefixCantStartWith0000: "Le préfixe de l'entreprise U.P.C. ne peut pas commencer par \"0000\"",
37
39
  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"
40
+ identifierTypeNotSupportedByGS18Prefix: "{{identifierType}} non pris en charge par le préfixe GS1-8",
41
+ gs1CompanyPrefixLengthDataFileNotFound: "Données de longueur du préfixe d'entreprise GS1 «{{key}}» introuvables",
42
+ gs1CompanyPrefixLengthDataNotLoaded: "Les données relatives à la longueur du préfixe d'entreprise GS1 n'ont pas été chargées"
39
43
  }
40
- } as const;
44
+ };
@@ -1,5 +1,5 @@
1
- import { i18nCoreInit, type I18nEnvironment } from "@aidc-toolkit/core";
2
- import { i18nUtilityInit, utilityResources } from "@aidc-toolkit/utility";
1
+ import { i18nCoreInit, type I18nEnvironment, i18nInit } from "@aidc-toolkit/core";
2
+ import { i18nUtilityInit } from "@aidc-toolkit/utility";
3
3
  import i18next, { type i18n, type Resource } from "i18next";
4
4
  import enLocaleResources from "./en/locale-resources.js";
5
5
  import frLocaleResources from "./fr/locale-resources.js";
@@ -12,9 +12,9 @@ export const gs1NS = "aidct_gs1";
12
12
  export type GS1LocaleResources = typeof enLocaleResources;
13
13
 
14
14
  /**
15
- * GS1 resources.
15
+ * GS1 resource bundle.
16
16
  */
17
- export const gs1Resources: Resource = {
17
+ export const gs1ResourceBundle: Resource = {
18
18
  en: {
19
19
  aidct_gs1: enLocaleResources
20
20
  },
@@ -36,9 +36,8 @@ export const i18nextGS1: i18n = i18next.createInstance();
36
36
  * Debug setting.
37
37
  *
38
38
  * @returns
39
- * Void promise.
39
+ * GS1 resource bundle.
40
40
  */
41
- export async function i18nGS1Init(environment: I18nEnvironment, debug = false): Promise<void> {
42
- await i18nUtilityInit(environment, debug);
43
- await i18nCoreInit(i18nextGS1, environment, debug, gs1NS, utilityResources, gs1Resources);
41
+ export async function i18nGS1Init(environment: I18nEnvironment, debug = false): Promise<Resource> {
42
+ return i18nInit(i18nextGS1, environment, debug, gs1NS, gs1ResourceBundle, i18nCoreInit, i18nUtilityInit);
44
43
  }
@@ -1,3 +1,4 @@
1
+ import type { CoreLocaleResources } from "@aidc-toolkit/core";
1
2
  import type { UtilityLocaleResources } from "@aidc-toolkit/utility";
2
3
  import type { GS1LocaleResources } from "./i18n.js";
3
4
 
@@ -11,6 +12,7 @@ declare module "i18next" {
11
12
  interface CustomTypeOptions {
12
13
  defaultNS: "aidct_gs1";
13
14
  resources: {
15
+ aidct_core: CoreLocaleResources;
14
16
  aidct_utility: UtilityLocaleResources;
15
17
  aidct_gs1: GS1LocaleResources;
16
18
  };
@@ -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.
@@ -56,12 +56,12 @@ export abstract class NumericIdentifierValidator<TNumericIdentifierType extends
56
56
  /**
57
57
  * @inheritDoc
58
58
  */
59
- validate(identifier: string, validation?: NumericIdentifierValidation): void {
59
+ override 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, validation?.positionOffset);
62
+ super.validatePrefix(identifier);
63
63
  } else {
64
- super.validatePrefix(identifier.substring(this.#prefixPosition), validation?.positionOffset === undefined ? this.#prefixPosition : validation.positionOffset + this.#prefixPosition);
64
+ super.validatePrefix(identifier.substring(this.#prefixPosition));
65
65
  }
66
66
 
67
67
  // Validate the length.
@@ -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
  }