@aidc-toolkit/app-extension 1.0.28-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.
Files changed (41) hide show
  1. package/dist/index.cjs +3463 -630
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +575 -300
  4. package/dist/index.d.ts +575 -300
  5. package/dist/index.js +3452 -613
  6. package/dist/index.js.map +1 -1
  7. package/package.json +8 -9
  8. package/src/app-data.ts +94 -0
  9. package/src/app-extension.ts +162 -93
  10. package/src/app-utility-proxy.ts +154 -103
  11. package/src/descriptor.ts +33 -6
  12. package/src/generator/generator.ts +13 -12
  13. package/src/generator/locale-resources-generator.ts +30 -28
  14. package/src/gs1/character-set-proxy.ts +8 -8
  15. package/src/gs1/check-proxy.ts +14 -14
  16. package/src/gs1/gtin-creator-proxy.ts +12 -25
  17. package/src/gs1/gtin-descriptor.ts +0 -21
  18. package/src/gs1/gtin-validator-proxy.ts +34 -35
  19. package/src/gs1/identifier-creator-proxy.ts +44 -32
  20. package/src/gs1/identifier-descriptor.ts +15 -0
  21. package/src/gs1/identifier-type.ts +37 -0
  22. package/src/gs1/identifier-validator-proxy.ts +52 -19
  23. package/src/gs1/index.ts +8 -0
  24. package/src/gs1/non-gtin-creator-proxy.ts +22 -22
  25. package/src/gs1/non-gtin-validator-proxy.ts +22 -22
  26. package/src/gs1/prefix-manager-proxy.ts +199 -4
  27. package/src/gs1/service-proxy.ts +56 -0
  28. package/src/gs1/variable-measure-proxy.ts +61 -0
  29. package/src/index.ts +6 -0
  30. package/src/lib-proxy.ts +112 -70
  31. package/src/locale/en/locale-resources.ts +147 -34
  32. package/src/locale/fr/locale-resources.ts +147 -34
  33. package/src/locale/i18n.ts +2 -5
  34. package/src/proxy.ts +93 -100
  35. package/src/streaming.ts +13 -0
  36. package/src/type.ts +8 -7
  37. package/src/utility/character-set-proxy.ts +33 -32
  38. package/src/utility/reg-exp-proxy.ts +7 -6
  39. package/src/utility/string-proxy.ts +3 -7
  40. package/src/utility/transformer-proxy.ts +19 -13
  41. package/tsconfig.json +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aidc-toolkit/app-extension",
3
- "version": "1.0.28-beta",
3
+ "version": "1.0.32-beta",
4
4
  "description": "Application extension framework for AIDC Toolkit",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -19,21 +19,20 @@
19
19
  "url": "https://www.linkedin.com/in/kdean"
20
20
  },
21
21
  "scripts": {
22
- "locale-resources-generator": "tsx src/generator/locale-resources-generator.ts",
22
+ "generate-locale-resources": "tsx src/generator/locale-resources-generator.ts",
23
23
  "lint": "eslint",
24
24
  "tsc:core": "tsc --project tsconfig-src.json",
25
- "build:devx": "rimraf dist && npm run tsc:core -- --declarationMap --sourceMap",
26
- "build:dev": "tsup --define.mode=dev",
25
+ "build:dev": "npm run tsc:core -- --noEmit && tsup --define.mode=dev",
27
26
  "build:release": "npm run tsc:core -- --noEmit && tsup",
28
27
  "build:doc": "npm run build:dev"
29
28
  },
30
29
  "devDependencies": {
31
- "@aidc-toolkit/dev": "1.0.28-beta"
30
+ "@aidc-toolkit/dev": "1.0.32-beta"
32
31
  },
33
32
  "dependencies": {
34
- "@aidc-toolkit/core": "1.0.28-beta",
35
- "@aidc-toolkit/gs1": "1.0.28-beta",
36
- "@aidc-toolkit/utility": "1.0.28-beta",
37
- "i18next": "^25.7.2"
33
+ "@aidc-toolkit/core": "1.0.32-beta",
34
+ "@aidc-toolkit/gs1": "1.0.32-beta",
35
+ "@aidc-toolkit/utility": "1.0.32-beta",
36
+ "base64-js": "^1.5.1"
38
37
  }
39
38
  }
@@ -0,0 +1,94 @@
1
+ import { fromByteArray, toByteArray } from "base64-js";
2
+
3
+ /**
4
+ * Application data.
5
+ */
6
+ export type AppData = string | number | boolean | Date | Uint8Array | object;
7
+
8
+ /**
9
+ * Decode application data from an encoded string.
10
+ *
11
+ * @param stringData
12
+ * String data.
13
+ *
14
+ * @returns
15
+ * Decoded application data.
16
+ */
17
+ export function decodeAppData(stringData: string): AppData | undefined {
18
+ let decodedAppData: AppData | undefined;
19
+
20
+ try {
21
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Mapping is expected to be correct.
22
+ decodedAppData = JSON.parse(stringData, (_key, value: unknown) => {
23
+ let replacementValue = value;
24
+
25
+ // Decode string representing date/time and binary array and pass through other values unmodified.
26
+ if (typeof value === "string") {
27
+ // First capture group is type, second is data; simple split at ':' character.
28
+ const stringDataGroups = /^(?<type>\w+):(?<data>.*)$/u.exec(value)?.groups;
29
+
30
+ if (stringDataGroups !== undefined) {
31
+ const type = stringDataGroups["type"];
32
+ const data = stringDataGroups["data"];
33
+
34
+ switch (type) {
35
+ case "dateTime":
36
+ replacementValue = new Date(data);
37
+ break;
38
+
39
+ case "binary":
40
+ replacementValue = toByteArray(data);
41
+ break;
42
+ }
43
+ }
44
+ }
45
+
46
+ return replacementValue;
47
+ }) as AppData;
48
+ } catch {
49
+ // String data is not valid JSON; discard it.
50
+ decodedAppData = undefined;
51
+ }
52
+
53
+ return decodedAppData;
54
+ }
55
+
56
+ /**
57
+ * Encode an object to a format suitable for storage.
58
+ *
59
+ * @param data
60
+ * Object.
61
+ *
62
+ * @returns
63
+ * Object suitable for storage with date/time and binary types encoded as strings.
64
+ */
65
+ function encodeObject(data: object): object | string {
66
+ let mappedData: object | string;
67
+
68
+ if (data instanceof Date) {
69
+ mappedData = `dateTime:${data.toISOString()}`;
70
+ } else if (data instanceof Uint8Array) {
71
+ mappedData = `binary:${fromByteArray(data)}`;
72
+ } else {
73
+ mappedData = Object.fromEntries(Object.entries(data).map(([key, value]: [string, unknown]) =>
74
+ // Encode date/time and binary array as string and pass through other values unmodified.
75
+ [key, typeof value === "object" && value !== null ? encodeObject(value) : value]
76
+ ));
77
+ }
78
+
79
+ return mappedData;
80
+ }
81
+
82
+ /**
83
+ * Encode application data as a string for storage. Encoded string is in JSON format with date/time and binary data
84
+ * converted to identifiable strings for decoding.
85
+ *
86
+ * @param appData
87
+ * Application data.
88
+ *
89
+ * @returns
90
+ * Encoded application data.
91
+ */
92
+ export function encodeAppData(appData: AppData): string {
93
+ return JSON.stringify(typeof appData !== "object" ? appData : encodeObject(appData));
94
+ }
@@ -1,6 +1,9 @@
1
- import type { TypedAsyncFunction, TypedFunction, TypedSyncFunction } from "@aidc-toolkit/core";
1
+ import { getLogger, type Hyperlink, LogLevels, MemoryTransport, type Promisable } from "@aidc-toolkit/core";
2
+ import type { Logger } from "tslog";
3
+ import type { AppData } from "./app-data.js";
2
4
  import { i18nextAppExtension } from "./locale/i18n.js";
3
- import type { ErrorExtends, ResultError, SheetAddress, SheetRange } from "./type.js";
5
+ import type { StreamingCancelledCallback, StreamingConsumerCallback } from "./streaming.js";
6
+ import type { ErrorExtends, MatrixResult, SheetAddress, SheetRange, SingletonResult } from "./type.js";
4
7
 
5
8
  /**
6
9
  * Application extension.
@@ -14,10 +17,33 @@ import type { ErrorExtends, ResultError, SheetAddress, SheetRange } from "./type
14
17
  * @template TInvocationContext
15
18
  * Application-specific invocation context type.
16
19
  *
20
+ * @template TStreamingInvocationContext
21
+ * Application-specific streaming invocation context type.
22
+ *
17
23
  * @template TBigInt
18
24
  * Type to which big integer is mapped.
19
25
  */
20
- export abstract class AppExtension<ThrowError extends boolean, TError extends ErrorExtends<ThrowError>, TInvocationContext, TBigInt> {
26
+ export abstract class AppExtension<ThrowError extends boolean, TError extends ErrorExtends<ThrowError>, TInvocationContext, TStreamingInvocationContext, TBigInt> {
27
+ /**
28
+ * Application name prefix for properties and application data.
29
+ */
30
+ static readonly APPLICATION_NAME_PREFIX = "AIDCToolkit.";
31
+
32
+ /**
33
+ * Version property name.
34
+ */
35
+ static readonly VERSION_NAME = `${AppExtension.APPLICATION_NAME_PREFIX}version`;
36
+
37
+ /**
38
+ * Maximum logger messages length.
39
+ */
40
+ static readonly #MAXIMUM_LOGGER_MESSAGES_LENGTH = 120;
41
+
42
+ /**
43
+ * Truncate logger messages length.
44
+ */
45
+ static readonly #TRUNCATE_LOGGER_MESSAGES_LENGTH = 100;
46
+
21
47
  /**
22
48
  * Application version.
23
49
  */
@@ -34,14 +60,14 @@ export abstract class AppExtension<ThrowError extends boolean, TError extends Er
34
60
  readonly #throwError: ThrowError;
35
61
 
36
62
  /**
37
- * Maximum width supported by application.
63
+ * Logger.
38
64
  */
39
- #maximumWidth?: number;
65
+ readonly #logger: Logger<object>;
40
66
 
41
67
  /**
42
- * Maximum height supported by application.
68
+ * Logger memory transport.
43
69
  */
44
- #maximumHeight?: number;
70
+ readonly #memoryTransport: MemoryTransport<object>;
45
71
 
46
72
  /**
47
73
  * Constructor.
@@ -55,10 +81,31 @@ export abstract class AppExtension<ThrowError extends boolean, TError extends Er
55
81
  * @param throwError
56
82
  * If true, errors are reported through the throw/catch mechanism.
57
83
  */
58
- constructor(version: string, maximumSequenceCount: number, throwError: ThrowError) {
84
+ protected constructor(version: string, maximumSequenceCount: number, throwError: ThrowError) {
59
85
  this.#version = version;
60
86
  this.#maximumSequenceCount = maximumSequenceCount;
61
87
  this.#throwError = throwError;
88
+
89
+ // Running in production if version doesn't include a pre-release identifier.
90
+ const isProduction = !version.includes("-");
91
+
92
+ this.#logger = getLogger(isProduction ? LogLevels.Info : LogLevels.Debug, {
93
+ type: isProduction ? "hidden" : "pretty",
94
+ hideLogPositionForProduction: isProduction
95
+ });
96
+
97
+ this.#memoryTransport = new MemoryTransport(this.#logger, AppExtension.#MAXIMUM_LOGGER_MESSAGES_LENGTH, AppExtension.#TRUNCATE_LOGGER_MESSAGES_LENGTH);
98
+ }
99
+
100
+ /**
101
+ * Initialize the application extension.
102
+ */
103
+ async initialize(): Promise<void> {
104
+ const fileVersion = await this.getFileProperty(AppExtension.VERSION_NAME);
105
+
106
+ if (fileVersion !== this.#version) {
107
+ await this.setFileProperty(AppExtension.VERSION_NAME, this.#version);
108
+ }
62
109
  }
63
110
 
64
111
  /**
@@ -79,44 +126,28 @@ export abstract class AppExtension<ThrowError extends boolean, TError extends Er
79
126
  }
80
127
 
81
128
  /**
82
- * Get the maximum width supported by the application.
83
- *
84
- * @returns
85
- * Maximum width supported by the application.
129
+ * Get the logger.
86
130
  */
87
- async maximumWidth(): Promise<number> {
88
- this.#maximumWidth ??= await this.getMaximumWidth();
89
-
90
- return this.#maximumWidth;
131
+ get logger(): Logger<object> {
132
+ return this.#logger;
91
133
  }
92
134
 
93
135
  /**
94
- * Get the maximum width supported by the application.
95
- *
96
- * @returns
97
- * Maximum width supported by the application.
136
+ * Get the logger memory transport.
98
137
  */
99
- protected abstract getMaximumWidth(): number | Promise<number>;
138
+ get memoryTransport(): MemoryTransport<object> {
139
+ return this.#memoryTransport;
140
+ }
100
141
 
101
142
  /**
102
- * Get the maximum height supported by the application.
103
- *
104
- * @returns
105
- * Maximum height supported by the application.
143
+ * Get the maximum width supported by the application.
106
144
  */
107
- async maximumHeight(): Promise<number> {
108
- this.#maximumHeight ??= await this.getMaximumHeight();
109
-
110
- return this.#maximumHeight;
111
- }
145
+ abstract get maximumWidth(): number;
112
146
 
113
147
  /**
114
148
  * Get the maximum height supported by the application.
115
- *
116
- * @returns
117
- * Maximum height supported by the application.
118
149
  */
119
- protected abstract getMaximumHeight(): number | Promise<number>;
150
+ abstract get maximumHeight(): number;
120
151
 
121
152
  /**
122
153
  * Get the sheet address from an invocation context.
@@ -127,7 +158,7 @@ export abstract class AppExtension<ThrowError extends boolean, TError extends Er
127
158
  * @returns
128
159
  * Sheet address.
129
160
  */
130
- abstract getSheetAddress(invocationContext: TInvocationContext): SheetAddress | Promise<SheetAddress>;
161
+ abstract getSheetAddress(invocationContext: TInvocationContext): Promisable<SheetAddress>;
131
162
 
132
163
  /**
133
164
  * Get a parameter range from an invocation context.
@@ -141,7 +172,87 @@ export abstract class AppExtension<ThrowError extends boolean, TError extends Er
141
172
  * @returns
142
173
  * Sheet range or null if parameter is not a range.
143
174
  */
144
- abstract getParameterSheetRange(invocationContext: TInvocationContext, parameterNumber: number): SheetRange | null | Promise<SheetRange | null>;
175
+ abstract getParameterSheetRange(invocationContext: TInvocationContext, parameterNumber: number): Promisable<SheetRange | null>;
176
+
177
+ /**
178
+ * Set up streaming for a streaming function.
179
+ *
180
+ * @param streamingInvocationContext
181
+ * Streaming invocation context.
182
+ *
183
+ * @param streamingCancelledCallback
184
+ * Streaming cancelled callback, called when streaming is cancelled.
185
+ *
186
+ * @returns
187
+ * Streaming consumer callback, called when stream contents updated.
188
+ */
189
+ abstract setUpStreaming<TResult>(streamingInvocationContext: TStreamingInvocationContext, streamingCancelledCallback: StreamingCancelledCallback): StreamingConsumerCallback<TResult, ThrowError, TError>;
190
+
191
+ /**
192
+ * Get a property stored within the active file.
193
+ *
194
+ * @param name
195
+ * Property name.
196
+ *
197
+ * @returns
198
+ * Property value or undefined if no value is stored under the given name.
199
+ */
200
+ abstract getFileProperty(name: string): Promisable<string | undefined>;
201
+
202
+ /**
203
+ * Set a property to be stored within the active file.
204
+ *
205
+ * @param name
206
+ * Property name.
207
+ *
208
+ * @param value
209
+ * Property value or null to remove.
210
+ */
211
+ abstract setFileProperty(name: string, value: string | null): Promisable<void>;
212
+
213
+ /**
214
+ * Get application data stored within the active file.
215
+ *
216
+ * @param name
217
+ * Name under which data is stored.
218
+ *
219
+ * @returns
220
+ * Application data or undefined if no data is stored under the given name.
221
+ */
222
+ abstract getFileData(name: string): Promisable<AppData | undefined>;
223
+
224
+ /**
225
+ * Set application data to be stored within the active file.
226
+ *
227
+ * @param name
228
+ * Name under which to store data.
229
+ *
230
+ * @param appData
231
+ * Application data or null to remove.
232
+ */
233
+ abstract setFileData(name: string, appData: AppData | null): Promisable<void>;
234
+
235
+ /**
236
+ * Get application data stored and shared across multiple files.
237
+ *
238
+ * @param name
239
+ * Name under which data is stored.
240
+ *
241
+ * @returns
242
+ * Application ata or undefined if no data is stored under the given name.
243
+ */
244
+ abstract getSharedData(name: string): Promisable<AppData | undefined>;
245
+
246
+ /**
247
+ * Set application data to be stored and shared across multiple files.
248
+ *
249
+ * @param name
250
+ * Name under which to store data.
251
+ *
252
+ * @param appData
253
+ * Application data or null to remove.
254
+ */
255
+ abstract setSharedData(name: string, appData: AppData | null): Promisable<void>;
145
256
 
146
257
  /**
147
258
  * Validate a sequence count against the maximum supported by application.
@@ -169,7 +280,21 @@ export abstract class AppExtension<ThrowError extends boolean, TError extends Er
169
280
  * @returns
170
281
  * Mapped big integer value.
171
282
  */
172
- abstract mapBigInt(value: bigint): ResultError<TBigInt, ThrowError, TError>;
283
+ abstract mapBigInt(value: bigint): SingletonResult<TBigInt, ThrowError, TError>;
284
+
285
+ /**
286
+ * Map hyperlink results to a form suitable for the application.
287
+ *
288
+ * @param invocationContext
289
+ * Invocation context.
290
+ *
291
+ * @param matrixHyperlinkResults
292
+ * Matrix of hyperlink results from function call.
293
+ *
294
+ * @returns
295
+ * Matrix of results in a form suitable for the application.
296
+ */
297
+ abstract mapHyperlinkResults(invocationContext: TInvocationContext, matrixHyperlinkResults: MatrixResult<Hyperlink, ThrowError, TError>): Promisable<MatrixResult<unknown, ThrowError, TError>>;
173
298
 
174
299
  /**
175
300
  * Map a range error (thrown by the library) to an application-specific error. If errors are reported through the
@@ -187,60 +312,4 @@ export abstract class AppExtension<ThrowError extends boolean, TError extends Er
187
312
  * Message to include in the error.
188
313
  */
189
314
  abstract handleError(message: string): never;
190
-
191
- /**
192
- * Bind a synchronous method and wrap it in a try/catch for comprehensive error handling.
193
- *
194
- * @template TMethod
195
- * Method type.
196
- *
197
- * @param thisArg
198
- * The value to be passed as the `this` parameter to the method.
199
- *
200
- * @param method
201
- * Method to call.
202
- *
203
- * @returns
204
- * Function wrapped around the method.
205
- */
206
- bindSync<TMethod extends TypedSyncFunction<TMethod>>(thisArg: ThisParameterType<TMethod>, method: TMethod): TypedFunction<TMethod> {
207
- const boundMethod = method.bind(thisArg);
208
-
209
- return (...args: Parameters<TMethod>): ReturnType<TMethod> => {
210
- try {
211
- return boundMethod(...args);
212
- } catch (e: unknown) {
213
- // eslint-disable-next-line no-console -- Necessary for diagnostics.
214
- console.error(e);
215
-
216
- this.handleError(e instanceof Error ? e.message : String(e));
217
- }
218
- };
219
- }
220
-
221
- /**
222
- * Bind an asynchronous method and wrap it in a try/catch for comprehensive error handling.
223
- *
224
- * @template TMethod
225
- * Method type.
226
- *
227
- * @param thisArg
228
- * The value to be passed as the `this` parameter to the method.
229
- *
230
- * @param method
231
- * Method to call.
232
- *
233
- * @returns
234
- * Function wrapped around the method.
235
- */
236
- bindAsync<TMethod extends TypedAsyncFunction<TMethod>>(thisArg: ThisParameterType<TMethod>, method: TMethod): TypedAsyncFunction<TMethod> {
237
- const boundMethod = method.bind(thisArg);
238
-
239
- return async (...args: Parameters<TMethod>) => await boundMethod(...args).catch((e: unknown) => {
240
- // eslint-disable-next-line no-console -- Necessary for diagnostics.
241
- console.error(e);
242
-
243
- this.handleError(e instanceof Error ? e.message : String(e));
244
- });
245
- }
246
315
  }