@aidc-toolkit/gs1 1.0.32-beta → 1.0.34-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 (51) hide show
  1. package/README.md +290 -21
  2. package/dist/character-set.d.ts.map +1 -1
  3. package/dist/character-set.js +44 -11
  4. package/dist/character-set.js.map +1 -1
  5. package/dist/gcp-length-cache.d.ts +54 -15
  6. package/dist/gcp-length-cache.d.ts.map +1 -1
  7. package/dist/gcp-length-cache.js +181 -45
  8. package/dist/gcp-length-cache.js.map +1 -1
  9. package/dist/gcp-length-data.d.ts +74 -20
  10. package/dist/gcp-length-data.d.ts.map +1 -1
  11. package/dist/gcp-length-data.js +39 -15
  12. package/dist/gcp-length-data.js.map +1 -1
  13. package/dist/gcp-length.d.ts +1 -1
  14. package/dist/gcp-length.d.ts.map +1 -1
  15. package/dist/gcp-length.js +5 -6
  16. package/dist/gcp-length.js.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/locale/en/locale-resources.d.ts +39 -36
  22. package/dist/locale/en/locale-resources.d.ts.map +1 -1
  23. package/dist/locale/en/locale-resources.js +4 -1
  24. package/dist/locale/en/locale-resources.js.map +1 -1
  25. package/dist/locale/fr/locale-resources.d.ts +39 -36
  26. package/dist/locale/fr/locale-resources.d.ts.map +1 -1
  27. package/dist/locale/fr/locale-resources.js +4 -1
  28. package/dist/locale/fr/locale-resources.js.map +1 -1
  29. package/dist/locale/i18n.d.ts +6 -3
  30. package/dist/locale/i18n.d.ts.map +1 -1
  31. package/dist/locale/i18n.js +8 -6
  32. package/dist/locale/i18n.js.map +1 -1
  33. package/dist/numeric-identifier-validator.d.ts.map +1 -1
  34. package/dist/numeric-identifier-validator.js.map +1 -1
  35. package/dist/variable-measure.d.ts +4 -4
  36. package/dist/variable-measure.js +4 -4
  37. package/package.json +12 -10
  38. package/src/character-set.ts +55 -11
  39. package/src/gcp-length-cache.ts +208 -54
  40. package/src/gcp-length-data.ts +95 -27
  41. package/src/gcp-length.ts +5 -43
  42. package/src/index.ts +2 -1
  43. package/src/locale/en/locale-resources.ts +5 -2
  44. package/src/locale/fr/locale-resources.ts +5 -2
  45. package/src/locale/i18n.ts +9 -7
  46. package/src/locale/i18next.d.ts +2 -0
  47. package/src/numeric-identifier-validator.ts +1 -1
  48. package/src/variable-measure.ts +4 -4
  49. package/test/character-set.test.ts +46 -0
  50. package/test/gcp-length.test.ts +179 -240
  51. package/tsconfig-src.tsbuildinfo +1 -1
@@ -1,74 +1,222 @@
1
- import { Cache } from "@aidc-toolkit/core";
2
- import { type GCPLengthData, type GCPLengthHeader, parseGCPLengthHeader } from "./gcp-length-data.js";
1
+ import { type AppData, type AppDataStorage, Cache, omit, RemoteAppDataStorage } from "@aidc-toolkit/core";
2
+ import {
3
+ type GCPLengthAppData,
4
+ type GCPLengthData,
5
+ type GCPLengthHeader,
6
+ type GCPLengthSourceJSON,
7
+ isGCPLengthAppData,
8
+ isGCPLengthHeader
9
+ } from "./gcp-length-data.js";
3
10
  import { i18nextGS1 } from "./locale/i18n.js";
4
11
 
5
12
  /**
6
13
  * GS1 Company Prefix length cache.
7
14
  */
8
- export abstract class GCPLengthCache extends Cache<GCPLengthData, GCPLengthData | string> {
9
- }
15
+ export abstract class GCPLengthCache extends Cache<GCPLengthData, GCPLengthData | GCPLengthSourceJSON> {
16
+ /**
17
+ * Storage key for full application data object.
18
+ */
19
+ static APP_DATA_STORAGE_KEY = "gcp-length";
10
20
 
11
- /**
12
- * GS1 Company Prefix length cache with remote source.
13
- */
14
- export abstract class RemoteGCPLengthCache extends GCPLengthCache {
15
21
  /**
16
- * Default base URL pointing to AIDC Toolkit website data directory.
22
+ * Storage key for next check date/time.
17
23
  */
18
- static DEFAULT_BASE_URL = "https://aidc-toolkit.com/data/";
24
+ static NEXT_CHECK_DATE_TIME_STORAGE_KEY = "gcp-length-next-check-date-time";
19
25
 
20
26
  /**
21
- * File containing header information (date/time and disclaimer).
27
+ * Storage key for header information (date/time and disclaimer).
22
28
  */
23
- static SOURCE_HEADER_FILE_NAME = "gcp-length-header.json";
29
+ static HEADER_STORAGE_KEY = "gcp-length-header";
24
30
 
25
31
  /**
26
- * File containing tree data in binary form.
32
+ * Storage key for data only.
27
33
  */
28
- static SOURCE_DATA_FILE_NAME = "gcp-length-data.bin";
34
+ static DATA_STORAGE_KEY = "gcp-length-data";
29
35
 
30
36
  /**
31
- * Base URL.
37
+ * Application data storage.
32
38
  */
33
- readonly #baseURL: string;
39
+ readonly #appDataStorage: AppDataStorage<boolean>;
34
40
 
35
41
  /**
36
- * GS1 Company Prefix header data.
42
+ * GS1 Company Prefix application data.
37
43
  */
38
- #gcpLengthHeader?: GCPLengthHeader | undefined;
44
+ #cacheGCPLengthAppData?: GCPLengthAppData | undefined;
39
45
 
40
46
  /**
41
47
  * Constructor.
42
48
  *
43
- * @param baseURL
44
- * Base URL. The URL must end with a slash, and must host the {@linkcode SOURCE_HEADER_FILE_NAME} and {@linkcode
45
- * SOURCE_DATA_FILE_NAME} files.
49
+ * @param appDataStorage
50
+ * Application data storage.
46
51
  */
47
- constructor(baseURL: string = RemoteGCPLengthCache.DEFAULT_BASE_URL) {
52
+ constructor(appDataStorage: AppDataStorage<boolean>) {
48
53
  super();
49
54
 
50
- this.#baseURL = baseURL;
55
+ this.#appDataStorage = appDataStorage;
51
56
  }
52
57
 
53
58
  /**
54
- * Get a remote file. If an exception is thrown, retrying is delayed for ten minutes to prevent repeated network
55
- * calls.
59
+ * Get the application data storage.
60
+ */
61
+ get appDataStorage(): AppDataStorage<boolean> {
62
+ return this.#appDataStorage;
63
+ }
64
+
65
+ /**
66
+ * Load cached GS1 Company Prefix length application data from application data storage.
56
67
  *
57
- * @param fileName
58
- * File name relative to base URL.
68
+ * @returns
69
+ * GS1 Company Prefix length application data.
70
+ */
71
+ async #loadCache(): Promise<GCPLengthAppData> {
72
+ if (this.#cacheGCPLengthAppData === undefined) {
73
+ if (!this.appDataStorage.supportsBinary) {
74
+ // Everything is stored in a single object.
75
+ const appData = await this.appDataStorage.read(GCPLengthCache.APP_DATA_STORAGE_KEY);
76
+
77
+ if (isGCPLengthAppData(appData)) {
78
+ this.#cacheGCPLengthAppData = appData;
79
+ }
80
+ } else {
81
+ const nextCheckDateTime = await this.appDataStorage.read(GCPLengthCache.NEXT_CHECK_DATE_TIME_STORAGE_KEY);
82
+ const header = await this.appDataStorage.read(GCPLengthCache.HEADER_STORAGE_KEY);
83
+ const data = await this.appDataStorage.read(GCPLengthCache.DATA_STORAGE_KEY, true);
84
+
85
+ this.#cacheGCPLengthAppData = {
86
+ nextCheckDateTime: nextCheckDateTime instanceof Date ? nextCheckDateTime : undefined,
87
+ gcpLengthData: isGCPLengthHeader(header) && data instanceof Uint8Array ?
88
+ {
89
+ ...header,
90
+ data
91
+ } :
92
+ undefined
93
+ };
94
+ }
95
+ }
96
+
97
+ // If there's nothing in the cache yet, create an object with the next check date/time set to now.
98
+ if (this.#cacheGCPLengthAppData === undefined) {
99
+ this.#cacheGCPLengthAppData = {
100
+ nextCheckDateTime: new Date(),
101
+ gcpLengthData: undefined
102
+ };
103
+ }
104
+
105
+ return this.#cacheGCPLengthAppData;
106
+ }
107
+
108
+ /**
109
+ * @inheritDoc
110
+ */
111
+ override get nextCheckDateTime(): Promise<Date | undefined> {
112
+ return this.#loadCache().then(gcpLengthAppData => gcpLengthAppData.nextCheckDateTime);
113
+ }
114
+
115
+ /**
116
+ * @inheritDoc
117
+ */
118
+ override get cacheDateTime(): Promise<Date | undefined> {
119
+ return this.#loadCache().then(gcpLengthAppData => gcpLengthAppData.gcpLengthData?.dateTime);
120
+ }
121
+
122
+ /**
123
+ * @inheritDoc
124
+ */
125
+ override get cacheData(): Promise<GCPLengthData> {
126
+ return this.#loadCache().then((gcpLengthAppData) => {
127
+ if (gcpLengthAppData.gcpLengthData === undefined) {
128
+ // Application error; no localization necessary.
129
+ throw new Error("GS1 Company Prefix length application data not loaded");
130
+ }
131
+
132
+ return gcpLengthAppData.gcpLengthData;
133
+ });
134
+ }
135
+
136
+ /**
137
+ * @inheritDoc
138
+ */
139
+ override async update(nextCheckDateTime: Date, _cacheDateTime?: Date, cacheData?: GCPLengthData): Promise<void> {
140
+ this.#cacheGCPLengthAppData = {
141
+ nextCheckDateTime,
142
+ // Retain existing data if cache data is not provided.
143
+ gcpLengthData: cacheData ?? this.#cacheGCPLengthAppData?.gcpLengthData
144
+ };
145
+
146
+ if (!this.appDataStorage.supportsBinary) {
147
+ // Everything is stored in a single object.
148
+ await this.appDataStorage.write(GCPLengthCache.APP_DATA_STORAGE_KEY, this.#cacheGCPLengthAppData);
149
+ } else {
150
+ await this.appDataStorage.write(GCPLengthCache.NEXT_CHECK_DATE_TIME_STORAGE_KEY, nextCheckDateTime);
151
+
152
+ if (cacheData !== undefined) {
153
+ await this.appDataStorage.write(GCPLengthCache.HEADER_STORAGE_KEY, omit(cacheData, "data"));
154
+ await this.appDataStorage.write(GCPLengthCache.DATA_STORAGE_KEY, cacheData.data);
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * GS1 Company Prefix length cache with remote source.
162
+ */
163
+ export class RemoteGCPLengthCache extends GCPLengthCache {
164
+ /**
165
+ * Default base URL pointing to AIDC Toolkit website data directory.
166
+ */
167
+ static DEFAULT_BASE_URL = "https://aidc-toolkit.com/data";
168
+
169
+ /**
170
+ * Remote application data storage.
171
+ */
172
+ readonly #remoteAppDataStorage: RemoteAppDataStorage;
173
+
174
+ /**
175
+ * Remote GS1 Company Prefix header data.
176
+ */
177
+ #remoteGCPLengthHeader?: GCPLengthHeader | undefined;
178
+
179
+ /**
180
+ * Constructor.
181
+ *
182
+ * @param appDataStorage
183
+ * Application data storage.
184
+ *
185
+ * @param baseURL
186
+ * Base URL. The URL must not end with a slash and must host the `gcp-length-header.json` and `gcp-length-data.bin`
187
+ * files.
188
+ */
189
+ constructor(appDataStorage: AppDataStorage<boolean>, baseURL: string = RemoteGCPLengthCache.DEFAULT_BASE_URL) {
190
+ super(appDataStorage);
191
+
192
+ this.#remoteAppDataStorage = new RemoteAppDataStorage(baseURL);
193
+ }
194
+
195
+ /**
196
+ * Get remote application data. If an exception is thrown while retrieving or processing the data, retrying is
197
+ * delayed for ten minutes to prevent repeated network calls.
198
+ *
199
+ * @param pathKey
200
+ * Key relative to base URL.
201
+ *
202
+ * @param asBinary
203
+ * True if binary data is requested, false if string data is requested.
204
+ *
205
+ * @param callback
206
+ * Callback function to process the application data.
59
207
  *
60
208
  * @returns
61
- * Response.
209
+ * Application data.
62
210
  */
63
- async #getRemoteFile(fileName: string): Promise<Response> {
64
- return fetch(`${this.#baseURL}${fileName}`).then((response) => {
65
- if (!response.ok) {
66
- throw new RangeError(i18nextGS1.t("Prefix.gs1CompanyPrefixLengthDataHTTPError", {
67
- status: response.status
211
+ async #getRemoteAppData<T>(pathKey: string, asBinary: boolean, callback: (appData: AppData) => T): Promise<T> {
212
+ return this.#remoteAppDataStorage.read(pathKey, asBinary).then((appData) => {
213
+ if (appData === undefined) {
214
+ throw new RangeError(i18nextGS1.t("Prefix.gs1CompanyPrefixLengthDataFileNotFound", {
215
+ key: pathKey
68
216
  }));
69
217
  }
70
218
 
71
- return response;
219
+ return callback(appData);
72
220
  }).catch(async (e: unknown) => {
73
221
  // Try again in ten minutes.
74
222
  const nowPlus10Minutes = new Date();
@@ -83,35 +231,41 @@ export abstract class RemoteGCPLengthCache extends GCPLengthCache {
83
231
  /**
84
232
  * @inheritDoc
85
233
  */
86
- get sourceDateTime(): Promise<Date> {
87
- return this.#getRemoteFile(RemoteGCPLengthCache.SOURCE_HEADER_FILE_NAME).then(async response =>
88
- response.text()
89
- ).then((s) => {
90
- this.#gcpLengthHeader = parseGCPLengthHeader(s);
234
+ override get sourceDateTime(): Promise<Date> {
235
+ // Header is fetched on each call.
236
+ return this.#getRemoteAppData(RemoteGCPLengthCache.HEADER_STORAGE_KEY, false, (appData) => {
237
+ if (!isGCPLengthHeader(appData)) {
238
+ // Application error; no localization necessary.
239
+ throw new Error("Invalid GS1 Company Prefix length header");
240
+ }
91
241
 
92
- return this.#gcpLengthHeader.dateTime;
242
+ this.#remoteGCPLengthHeader = appData;
243
+
244
+ return this.#remoteGCPLengthHeader.dateTime;
93
245
  });
94
246
  }
95
247
 
96
248
  /**
97
249
  * @inheritDoc
98
250
  */
99
- get sourceData(): Promise<GCPLengthData> {
100
- if (this.#gcpLengthHeader === undefined) {
101
- // Application error; no localization necessary.
102
- throw new Error("GS1 Company Prefix length header not loaded");
103
- }
251
+ override get sourceData(): Promise<GCPLengthData> {
252
+ return this.#getRemoteAppData(RemoteGCPLengthCache.DATA_STORAGE_KEY, true, (appData) => {
253
+ const gcpLengthHeader = this.#remoteGCPLengthHeader;
104
254
 
105
- const gcpLengthHeader = this.#gcpLengthHeader;
255
+ if (gcpLengthHeader === undefined) {
256
+ // Application error; no localization necessary.
257
+ throw new Error("GS1 Company Prefix length header not loaded");
258
+ }
106
259
 
107
- // Clear header to allow for retry in case of failure.
108
- this.#gcpLengthHeader = undefined;
260
+ if (!(appData instanceof Uint8Array)) {
261
+ // Application error; no localization necessary.
262
+ throw new Error("Invalid GS1 Company Prefix length data");
263
+ }
109
264
 
110
- return this.#getRemoteFile(RemoteGCPLengthCache.SOURCE_DATA_FILE_NAME).then(async response =>
111
- response.arrayBuffer()
112
- ).then(a => ({
113
- ...gcpLengthHeader,
114
- data: new Uint8Array(a)
115
- }));
265
+ return {
266
+ ...gcpLengthHeader,
267
+ data: appData
268
+ };
269
+ });
116
270
  }
117
271
  }
@@ -1,3 +1,5 @@
1
+ import type { AppData } from "@aidc-toolkit/core";
2
+
1
3
  /**
2
4
  * GS1 Company Prefix length header.
3
5
  */
@@ -14,55 +16,121 @@ export interface GCPLengthHeader {
14
16
  }
15
17
 
16
18
  /**
17
- * Parse a JSON string representing a GS1 Company Prefix length header.
19
+ * Determine if application data object is GS1 Company Prefix length header.
18
20
  *
19
- * @param s
20
- * JSON string.
21
+ * @param appData
22
+ * Application data object.
21
23
  *
22
24
  * @returns
23
- * GS1 Company Prefix length header.
25
+ * True if application data object is GS1 Company Prefix length header.
24
26
  */
25
- export function parseGCPLengthHeader(s: string): GCPLengthHeader {
26
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- File format is known.
27
- return JSON.parse(s, (key, value: unknown) =>
28
- key === "dateTime" && typeof value === "string" ? new Date(value) : value
29
- ) as GCPLengthHeader;
27
+ export function isGCPLengthHeader(appData: AppData | undefined): appData is GCPLengthHeader {
28
+ // Property type check is necessary to guard against data corruption or changes in format.
29
+ return typeof appData === "object" && "dateTime" in appData && appData.dateTime instanceof Date && "disclaimer" in appData && typeof appData.disclaimer === "string";
30
30
  }
31
31
 
32
32
  /**
33
- * Generate a JSON string representing a GS1 Company Prefix length header.
34
- *
35
- * @param gcpLengthHeader
36
- * GS1 Company Prefix length header.
33
+ * GS1 Company Prefix length data.
34
+ */
35
+ export interface GCPLengthData extends GCPLengthHeader {
36
+ /**
37
+ * Tree data in binary form.
38
+ */
39
+ readonly data: Uint8Array;
40
+ }
41
+
42
+ /**
43
+ * Determine if application data object is GS1 Company Prefix length data.
37
44
  *
38
- * @param space
39
- * JSON string indentation.
45
+ * @param appData
46
+ * Application data object.
40
47
  *
41
48
  * @returns
42
- * JSON string.
49
+ * True if application data object is GS1 Company Prefix length data.
43
50
  */
44
- export function stringifyGCPLengthHeader(gcpLengthHeader: GCPLengthHeader, space?: string | number): string {
45
- return JSON.stringify(gcpLengthHeader, (key, value: unknown) =>
46
- key === "dateTime" && value instanceof Date ? value.toISOString() : value,
47
- space);
51
+ export function isGCPLengthData(appData: AppData | undefined): appData is GCPLengthData {
52
+ // Property type check is necessary to guard against data corruption or changes in format.
53
+ return isGCPLengthHeader(appData) && "data" in appData && appData.data instanceof Uint8Array;
48
54
  }
49
55
 
50
56
  /**
51
- * GS1 Company Prefix length data.
57
+ * GS1 Company Prefix length application data.
52
58
  */
53
- export interface GCPLengthData extends GCPLengthHeader {
59
+ export interface GCPLengthAppData {
54
60
  /**
55
- * Date/time the data was last updated.
61
+ * Next check date/time.
56
62
  */
57
- readonly dateTime: Date;
63
+ nextCheckDateTime: Date | undefined;
58
64
 
65
+ /**
66
+ * GS1 Company Prefix length data.
67
+ */
68
+ gcpLengthData: GCPLengthData | undefined;
69
+ }
70
+
71
+ /**
72
+ * Determine if application data object is GS1 Company Prefix length application data.
73
+ *
74
+ * @param appData
75
+ * Application data object.
76
+ *
77
+ * @returns
78
+ * True if application data object is GS1 Company Prefix length application data.
79
+ */
80
+ export function isGCPLengthAppData(appData: AppData | undefined): appData is GCPLengthAppData {
81
+ // Property type check is necessary to guard against data corruption or changes in format.
82
+ return typeof appData === "object" && "nextCheckDateTime" in appData && appData.nextCheckDateTime instanceof Date && (
83
+ !("gcpLengthData" in appData) || appData.gcpLengthData === undefined || (
84
+ typeof appData.gcpLengthData === "object" && appData.gcpLengthData !== null && isGCPLengthData(appData.gcpLengthData)
85
+ )
86
+ );
87
+ }
88
+
89
+ /**
90
+ * GS1 Company Prefix length JSON source file format.
91
+ */
92
+ export interface GCPLengthSourceJSON {
59
93
  /**
60
94
  * Disclaimer.
61
95
  */
62
- readonly disclaimer: string;
96
+ _disclaimer: string[];
63
97
 
64
98
  /**
65
- * Tree data in binary form.
99
+ * Format list.
66
100
  */
67
- readonly data: Uint8Array;
101
+ GCPPrefixFormatList: {
102
+ /**
103
+ * ISO data/time the table was last updated.
104
+ */
105
+ date: string;
106
+
107
+ /**
108
+ * Entries.
109
+ */
110
+ entry: Array<{
111
+ /**
112
+ * Identification key prefix start.
113
+ */
114
+ prefix: string;
115
+
116
+ /**
117
+ * Length of GS1 Company Prefix.
118
+ */
119
+ gcpLength: number;
120
+ }>;
121
+ };
122
+ }
123
+
124
+ /**
125
+ * Determine if application data object is GS1 Company Prefix length source JSON.
126
+ *
127
+ * @param appData
128
+ * Application data object.
129
+ *
130
+ * @returns
131
+ * True if application data object is GS1 Company Prefix length source JSON.
132
+ */
133
+ export function isGCPLengthSourceJSON(appData: AppData | undefined): appData is GCPLengthSourceJSON {
134
+ // Checking is minimal as this is generally used in a controlled environment (e.g., updating remote storage).
135
+ return typeof appData === "object" && "_disclaimer" in appData && "GCPPrefixFormatList" in appData;
68
136
  }
package/src/gcp-length.ts CHANGED
@@ -1,47 +1,12 @@
1
1
  import { omit } from "@aidc-toolkit/core";
2
2
  import type { GCPLengthCache } from "./gcp-length-cache.js";
3
- import type { GCPLengthData, GCPLengthHeader } from "./gcp-length-data.js";
3
+ import { type GCPLengthData, type GCPLengthHeader, isGCPLengthData } from "./gcp-length-data.js";
4
4
  import { GTINLengths } from "./gtin-length.js";
5
5
  import { GTINValidator } from "./gtin-validator.js";
6
6
  import { type IdentifierType, IdentifierTypes } from "./identifier-type.js";
7
7
  import { IdentifierValidators, isNumericIdentifierValidator } from "./identifier-validators.js";
8
8
  import { LeaderTypes } from "./leader-type.js";
9
9
 
10
- /**
11
- * GS1 Company Prefix length JSON source file format.
12
- */
13
- interface GCPLengthJSON {
14
- /**
15
- * Disclaimer.
16
- */
17
- _disclaimer: string[];
18
-
19
- /**
20
- * Format list.
21
- */
22
- GCPPrefixFormatList: {
23
- /**
24
- * ISO data/time the table was last updated.
25
- */
26
- date: string;
27
-
28
- /**
29
- * Entries.
30
- */
31
- entry: Array<{
32
- /**
33
- * Identification key prefix start.
34
- */
35
- prefix: string;
36
-
37
- /**
38
- * Length of GS1 Company Prefix.
39
- */
40
- gcpLength: number;
41
- }>;
42
- };
43
- }
44
-
45
10
  /**
46
11
  * Leaf of GS1 Company Prefix length tree.
47
12
  */
@@ -299,7 +264,7 @@ export async function loadData(gcpLengthCache: GCPLengthCache): Promise<Root | u
299
264
 
300
265
  let cacheData: GCPLengthData;
301
266
 
302
- if (typeof sourceData !== "string") {
267
+ if (isGCPLengthData(sourceData)) {
303
268
  root = {
304
269
  dateTime: sourceData.dateTime,
305
270
  disclaimer: sourceData.disclaimer,
@@ -310,13 +275,10 @@ export async function loadData(gcpLengthCache: GCPLengthCache): Promise<Root | u
310
275
 
311
276
  cacheData = sourceData;
312
277
  } else {
313
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- File format is known.
314
- const gcpLength = JSON.parse(sourceData) as GCPLengthJSON;
315
-
316
278
  root = {
317
- dateTime: new Date(gcpLength.GCPPrefixFormatList.date),
279
+ dateTime: new Date(sourceData.GCPPrefixFormatList.date),
318
280
  // Join disclaimer as a single string.
319
- disclaimer: `${gcpLength._disclaimer.reduce<string[]>((lines, line) => {
281
+ disclaimer: `${sourceData._disclaimer.reduce<string[]>((lines, line) => {
320
282
  if (lines.length === 0 || lines[lines.length - 1] === "" || line === "") {
321
283
  lines.push(line);
322
284
  } else {
@@ -331,7 +293,7 @@ export async function loadData(gcpLengthCache: GCPLengthCache): Promise<Root | u
331
293
 
332
294
  let branchesAdded = 1;
333
295
 
334
- for (const entry of gcpLength.GCPPrefixFormatList.entry) {
296
+ for (const entry of sourceData.GCPPrefixFormatList.entry) {
335
297
  branchesAdded += addEntry(root, entry.prefix, entry.gcpLength);
336
298
  }
337
299
 
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Copyright © 2024-2025 Dolphin Data Development Ltd. and AIDC Toolkit
2
+ * Copyright © 2024-2026 Dolphin Data Development Ltd. and AIDC Toolkit
3
3
  * contributors
4
4
  *
5
5
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+
17
18
  export * from "./locale/i18n.js";
18
19
 
19
20
  export * from "./character-set.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",
@@ -35,7 +38,7 @@ export default {
35
38
  upcCompanyPrefixCantStartWith0000: "U.P.C. Company Prefix can't start with \"0000\"",
36
39
  gs18PrefixCantStartWith0: "GS1-8 Prefix can't start with \"0\"",
37
40
  identifierTypeNotSupportedByGS18Prefix: "{{identifierType}} not supported by GS1-8 Prefix",
38
- gs1CompanyPrefixLengthDataHTTPError: "HTTP error {{status, number}} loading GS1 Company Prefix length data",
41
+ gs1CompanyPrefixLengthDataFileNotFound: "GS1 Company Prefix length data \"{{key}}\" not found",
39
42
  gs1CompanyPrefixLengthDataNotLoaded: "GS1 Company Prefix length data not loaded"
40
43
  }
41
- } 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",
@@ -35,7 +38,7 @@ export default {
35
38
  upcCompanyPrefixCantStartWith0000: "Le préfixe de l'entreprise U.P.C. ne peut pas commencer par \"0000\"",
36
39
  gs18PrefixCantStartWith0: "Le préfixe GS1-8 ne peut pas commencer par \"0\"",
37
40
  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",
41
+ gs1CompanyPrefixLengthDataFileNotFound: "Données de longueur du préfixe d'entreprise GS1 «{{key}}» introuvables",
39
42
  gs1CompanyPrefixLengthDataNotLoaded: "Les données relatives à la longueur du préfixe d'entreprise GS1 n'ont pas été chargées"
40
43
  }
41
- } as const;
44
+ };
@@ -1,5 +1,5 @@
1
- import { type I18nEnvironment, i18nFinalizeInit } 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
  },
@@ -34,8 +34,10 @@ export const i18nextGS1: i18n = i18next.createInstance();
34
34
  *
35
35
  * @param debug
36
36
  * Debug setting.
37
+ *
38
+ * @returns
39
+ * GS1 resource bundle.
37
40
  */
38
- export async function i18nGS1Init(environment: I18nEnvironment, debug = false): Promise<void> {
39
- await i18nUtilityInit(environment, debug);
40
- await i18nFinalizeInit(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);
41
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
  };
@@ -56,7 +56,7 @@ 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
62
  super.validatePrefix(identifier);