@aidc-toolkit/app-extension 1.0.27-beta → 1.0.31-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/index.cjs +1115 -876
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -241
- package/dist/index.d.ts +79 -241
- package/dist/index.js +1113 -869
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/app-extension.ts +5 -5
- package/src/app-utility-proxy.ts +18 -24
- package/src/descriptor.ts +29 -259
- package/src/generator/generator.ts +89 -131
- package/src/generator/index.ts +0 -1
- package/src/generator/locale-resources-generator.ts +39 -14
- package/src/gs1/character-set-proxy.ts +5 -5
- package/src/gs1/check-proxy.ts +35 -42
- package/src/gs1/gtin-creator-proxy.ts +58 -0
- package/src/gs1/gtin-descriptor.ts +29 -0
- package/src/gs1/gtin-validator-proxy.ts +161 -0
- package/src/gs1/identifier-creator-proxy.ts +227 -0
- package/src/gs1/identifier-validator-proxy.ts +87 -0
- package/src/gs1/index.ts +5 -1
- package/src/gs1/non-gtin-creator-proxy.ts +119 -0
- package/src/gs1/non-gtin-validator-proxy.ts +119 -0
- package/src/gs1/prefix-definition-descriptor.ts +18 -0
- package/src/gs1/prefix-manager-proxy.ts +42 -0
- package/src/index.ts +1 -0
- package/src/lib-proxy.ts +1 -1
- package/src/proxy.ts +526 -0
- package/src/utility/character-set-descriptor.ts +5 -5
- package/src/utility/character-set-proxy.ts +31 -47
- package/src/utility/reg-exp-proxy.ts +11 -15
- package/src/utility/string-descriptor.ts +2 -2
- package/src/utility/transformer-descriptor.ts +3 -3
- package/src/utility/transformer-proxy.ts +16 -26
- package/tsconfig-src.json +1 -4
- package/tsconfig.json +1 -0
- package/src/generator/descriptor.ts +0 -125
- package/src/gs1/identifier-proxy.ts +0 -826
package/src/proxy.ts
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AbstractConstructor,
|
|
3
|
+
type Constructor,
|
|
4
|
+
getLogger,
|
|
5
|
+
type LogLevel,
|
|
6
|
+
LogLevels,
|
|
7
|
+
omit,
|
|
8
|
+
type TypedAbstractConstructor
|
|
9
|
+
} from "@aidc-toolkit/core";
|
|
10
|
+
import type { Logger } from "tslog";
|
|
11
|
+
import type { AppExtension } from "./app-extension.js";
|
|
12
|
+
import type {
|
|
13
|
+
ClassDescriptor,
|
|
14
|
+
ExtendsParameterDescriptor,
|
|
15
|
+
MethodDescriptor,
|
|
16
|
+
ParameterDescriptor
|
|
17
|
+
} from "./descriptor.js";
|
|
18
|
+
import { LibProxy } from "./lib-proxy.js";
|
|
19
|
+
import type { ErrorExtends } from "./type.js";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Remaining parameters past first parameter in constructor.
|
|
23
|
+
*/
|
|
24
|
+
type RemainingParameters<TConstructor extends TypedAbstractConstructor<TConstructor>> =
|
|
25
|
+
TConstructor extends abstract new (arg0: infer _P0, ...args: infer P) => InstanceType<TConstructor> ? P : never;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Proxy class constructor type with fixed constructor signature for concrete class and variable constructor for
|
|
29
|
+
* abstract class.
|
|
30
|
+
*
|
|
31
|
+
* @template T
|
|
32
|
+
* Proxy class type.
|
|
33
|
+
*
|
|
34
|
+
* @template IsAbstract
|
|
35
|
+
* True if the proxy class is abstract.
|
|
36
|
+
*
|
|
37
|
+
* @template TConstructor
|
|
38
|
+
* Proxy class constructor type.
|
|
39
|
+
*/
|
|
40
|
+
type ProxyClassConstructor<
|
|
41
|
+
ThrowError extends boolean, TError extends ErrorExtends<ThrowError>, TInvocationContext, TBigInt,
|
|
42
|
+
T extends LibProxy<ThrowError, TError, TInvocationContext, TBigInt>,
|
|
43
|
+
IsAbstract extends boolean,
|
|
44
|
+
TConstructor extends TypedAbstractConstructor<TConstructor>
|
|
45
|
+
> = IsAbstract extends true ?
|
|
46
|
+
AbstractConstructor<[appExtension: AppExtension<ThrowError, TError, TInvocationContext, TBigInt>, ...args: RemainingParameters<TConstructor>], T> :
|
|
47
|
+
Constructor<[appExtension: AppExtension<ThrowError, TError, TInvocationContext, TBigInt>], T>;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Class decorator type. Defines the parameters passed to a class decorator function and the return type as identical to
|
|
51
|
+
* the constructor.
|
|
52
|
+
*
|
|
53
|
+
* @template T
|
|
54
|
+
* Proxy class type.
|
|
55
|
+
*
|
|
56
|
+
* @template IsAbstract
|
|
57
|
+
* True if the proxy class is abstract.
|
|
58
|
+
*
|
|
59
|
+
* @template TConstructor
|
|
60
|
+
* Proxy class constructor type.
|
|
61
|
+
*
|
|
62
|
+
* @template TProxyClassConstructor
|
|
63
|
+
* Narrowed proxy class constructor type.
|
|
64
|
+
*/
|
|
65
|
+
type ClassDecorator<
|
|
66
|
+
ThrowError extends boolean, TError extends ErrorExtends<ThrowError>, TInvocationContext, TBigInt,
|
|
67
|
+
T extends LibProxy<ThrowError, TError, TInvocationContext, TBigInt>,
|
|
68
|
+
IsAbstract extends boolean,
|
|
69
|
+
TConstructor extends TypedAbstractConstructor<TConstructor>,
|
|
70
|
+
TProxyClassConstructor extends ProxyClassConstructor<ThrowError, TError, TInvocationContext, TBigInt, T, IsAbstract, TConstructor>
|
|
71
|
+
> = (Target: TProxyClassConstructor, context: ClassDecoratorContext<TProxyClassConstructor>) => TProxyClassConstructor;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Class method decorator type. Defines the parameters passed to a class method decorator function and the return type
|
|
75
|
+
* as identical to the method.
|
|
76
|
+
*
|
|
77
|
+
* @template TFunction
|
|
78
|
+
* Function type.
|
|
79
|
+
*/
|
|
80
|
+
type ClassMethodDecorator<TThis, TArguments extends unknown[], TReturn> =
|
|
81
|
+
(target: (this: TThis, ...args: TArguments) => TReturn, context: ClassMethodDecoratorContext<TThis>) => (this: TThis, ...args: TArguments) => TReturn;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Subset of method descriptor used during decoration process.
|
|
85
|
+
*/
|
|
86
|
+
type InterimMethodDescriptor = Omit<MethodDescriptor, "functionName" | "namespaceFunctionName">;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Subset of method descriptor used in call to decorator.
|
|
90
|
+
*/
|
|
91
|
+
type DecoratorMethodDescriptor = Omit<InterimMethodDescriptor, "name" | "parameterDescriptors"> & {
|
|
92
|
+
parameterDescriptors: Array<ParameterDescriptor | ExtendsParameterDescriptor>;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Subset of class descriptor used during decoration process.
|
|
97
|
+
*/
|
|
98
|
+
type InterimClassDescriptor =
|
|
99
|
+
Omit<ClassDescriptor, "name" | "namespaceClassName" | "objectName" | "methodDescriptors">;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Subset of class descriptor used in call to decorator.
|
|
103
|
+
*/
|
|
104
|
+
interface DecoratorClassDescriptor extends Omit<InterimClassDescriptor, "replaceParameterDescriptors"> {
|
|
105
|
+
readonly replaceParameterDescriptors?: ReadonlyArray<Omit<Required<ClassDescriptor>["replaceParameterDescriptors"][number], "replacement"> & {
|
|
106
|
+
readonly replacement: ParameterDescriptor | ExtendsParameterDescriptor;
|
|
107
|
+
}>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Expand a parameter descriptor to its full form with all required attributes.
|
|
112
|
+
*
|
|
113
|
+
* @param parameterDescriptor
|
|
114
|
+
* Parameter descriptor.
|
|
115
|
+
*
|
|
116
|
+
* @returns
|
|
117
|
+
* Parameter descriptor in its full form.
|
|
118
|
+
*/
|
|
119
|
+
export function expandParameterDescriptor(parameterDescriptor: ParameterDescriptor | ExtendsParameterDescriptor): ParameterDescriptor {
|
|
120
|
+
return !("extendsDescriptor" in parameterDescriptor) ?
|
|
121
|
+
parameterDescriptor :
|
|
122
|
+
{
|
|
123
|
+
...expandParameterDescriptor(parameterDescriptor.extendsDescriptor),
|
|
124
|
+
...parameterDescriptor
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Placeholder for interim descriptors.
|
|
130
|
+
*/
|
|
131
|
+
interface Interim {
|
|
132
|
+
/**
|
|
133
|
+
* Interim class descriptor.
|
|
134
|
+
*/
|
|
135
|
+
readonly classDescriptor: InterimClassDescriptor;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Interim method descriptors.
|
|
139
|
+
*/
|
|
140
|
+
readonly methodDescriptors: InterimMethodDescriptor[];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Target logger interface to be implemented by returned decorator class.
|
|
145
|
+
*/
|
|
146
|
+
interface TargetLogger {
|
|
147
|
+
/**
|
|
148
|
+
* Log a method call.
|
|
149
|
+
*
|
|
150
|
+
* @param logLevel
|
|
151
|
+
* Log level.
|
|
152
|
+
*
|
|
153
|
+
* @param methodName
|
|
154
|
+
* Method name.
|
|
155
|
+
*
|
|
156
|
+
* @param args
|
|
157
|
+
* Input arguments.
|
|
158
|
+
*
|
|
159
|
+
* @param result
|
|
160
|
+
* Output result.
|
|
161
|
+
*/
|
|
162
|
+
log: (logLevel: LogLevel, methodName: string, args: unknown[], result: unknown) => void;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Proxy class.
|
|
167
|
+
*/
|
|
168
|
+
export class Proxy {
|
|
169
|
+
/**
|
|
170
|
+
* Logger.
|
|
171
|
+
*/
|
|
172
|
+
// TODO Add configuration parameter to output JSON.
|
|
173
|
+
// TODO Change this to LogLevels.Trace when configuration available.
|
|
174
|
+
readonly #logger: Logger<unknown> = getLogger(LogLevels.Info);
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Abstract class descriptors map, keyed on declaration class name. Abstract classes are not used directly by target
|
|
178
|
+
* applications.
|
|
179
|
+
*/
|
|
180
|
+
readonly #abstractClassDescriptorsMap = new Map<string, ClassDescriptor>();
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Concrete class descriptors map, keyed on declaration class name.
|
|
184
|
+
*/
|
|
185
|
+
readonly #concreteClassDescriptorsMap = new Map<string, ClassDescriptor>();
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Interim object.
|
|
189
|
+
*/
|
|
190
|
+
#interim: Interim | undefined = undefined;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get the proper JSON representation of a value.
|
|
194
|
+
*
|
|
195
|
+
* @param value
|
|
196
|
+
* Value.
|
|
197
|
+
*
|
|
198
|
+
* @returns
|
|
199
|
+
* Replacement value.
|
|
200
|
+
*/
|
|
201
|
+
static #jsonValue(value: unknown): unknown {
|
|
202
|
+
let replacementValue: unknown;
|
|
203
|
+
|
|
204
|
+
switch (typeof value) {
|
|
205
|
+
case "string":
|
|
206
|
+
case "number":
|
|
207
|
+
case "boolean":
|
|
208
|
+
case "undefined":
|
|
209
|
+
replacementValue = value;
|
|
210
|
+
break;
|
|
211
|
+
|
|
212
|
+
case "bigint":
|
|
213
|
+
// Big integers not supported in JSON.
|
|
214
|
+
replacementValue = value >= Number.MIN_SAFE_INTEGER && value <= Number.MAX_SAFE_INTEGER ? Number(value) : value.toString(10);
|
|
215
|
+
break;
|
|
216
|
+
|
|
217
|
+
case "object":
|
|
218
|
+
if (value === null) {
|
|
219
|
+
replacementValue = value;
|
|
220
|
+
} else if (Array.isArray(value)) {
|
|
221
|
+
replacementValue = value.map(entry => Proxy.#jsonValue(entry));
|
|
222
|
+
} else {
|
|
223
|
+
replacementValue = Object.fromEntries(Object.entries(value).map(([k, v]) => [k, Proxy.#jsonValue(v)]));
|
|
224
|
+
}
|
|
225
|
+
break;
|
|
226
|
+
|
|
227
|
+
case "symbol":
|
|
228
|
+
case "function":
|
|
229
|
+
throw new Error(`Unsupported ${typeof value} value type`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return replacementValue;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Describe a proxy class.
|
|
237
|
+
*
|
|
238
|
+
* @template T
|
|
239
|
+
* Proxy class type.
|
|
240
|
+
*
|
|
241
|
+
* @template IsAbstract
|
|
242
|
+
* True if the proxy class is abstract.
|
|
243
|
+
*
|
|
244
|
+
* @template TConstructor
|
|
245
|
+
* Proxy class constructor type.
|
|
246
|
+
*
|
|
247
|
+
* @template TProxyClassConstructor
|
|
248
|
+
* Narrowed proxy class constructor type.
|
|
249
|
+
*
|
|
250
|
+
* @param isAbstract
|
|
251
|
+
* True if class is abstract.
|
|
252
|
+
*
|
|
253
|
+
* @param decoratorClassDescriptor
|
|
254
|
+
* Class descriptor.
|
|
255
|
+
*
|
|
256
|
+
* @returns
|
|
257
|
+
* Function with which to decorate the class.
|
|
258
|
+
*/
|
|
259
|
+
describeClass<
|
|
260
|
+
ThrowError extends boolean, TError extends ErrorExtends<ThrowError>, TInvocationContext, TBigInt,
|
|
261
|
+
T extends LibProxy<ThrowError, TError, TInvocationContext, TBigInt>,
|
|
262
|
+
IsAbstract extends boolean,
|
|
263
|
+
TConstructor extends TypedAbstractConstructor<TConstructor>,
|
|
264
|
+
TProxyClassConstructor extends ProxyClassConstructor<ThrowError, TError, TInvocationContext, TBigInt, T, IsAbstract, TConstructor>
|
|
265
|
+
>(isAbstract: IsAbstract, decoratorClassDescriptor: DecoratorClassDescriptor = {}): ClassDecorator<ThrowError, TError, TInvocationContext, TBigInt, T, IsAbstract, TConstructor, TProxyClassConstructor> {
|
|
266
|
+
const interimClassDescriptor: InterimClassDescriptor = decoratorClassDescriptor.replaceParameterDescriptors === undefined ?
|
|
267
|
+
omit(decoratorClassDescriptor, "replaceParameterDescriptors") :
|
|
268
|
+
{
|
|
269
|
+
...decoratorClassDescriptor,
|
|
270
|
+
replaceParameterDescriptors: decoratorClassDescriptor.replaceParameterDescriptors.map(replaceParameterDescriptor => ({
|
|
271
|
+
...replaceParameterDescriptor,
|
|
272
|
+
replacement: expandParameterDescriptor(replaceParameterDescriptor.replacement)
|
|
273
|
+
}))
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const interim: Interim = {
|
|
277
|
+
classDescriptor: interimClassDescriptor,
|
|
278
|
+
methodDescriptors: []
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
this.#interim = interim;
|
|
282
|
+
|
|
283
|
+
return (Target: TProxyClassConstructor, context: ClassDecoratorContext<TProxyClassConstructor>) => {
|
|
284
|
+
const name = context.name;
|
|
285
|
+
|
|
286
|
+
// Validate that class descriptor is applied within an appropriate class.
|
|
287
|
+
if (typeof name !== "string") {
|
|
288
|
+
throw new Error(`${String(name)} has an invalid name`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const namespacePrefix = decoratorClassDescriptor.namespace === undefined ? "" : `${decoratorClassDescriptor.namespace}.`;
|
|
292
|
+
const namespaceClassName = `${namespacePrefix}${name}`;
|
|
293
|
+
|
|
294
|
+
const abstractClassDescriptorsMap = this.#abstractClassDescriptorsMap;
|
|
295
|
+
const concreteClassDescriptorsMap = this.#concreteClassDescriptorsMap;
|
|
296
|
+
|
|
297
|
+
if (abstractClassDescriptorsMap.has(namespaceClassName) || concreteClassDescriptorsMap.has(namespaceClassName)) {
|
|
298
|
+
throw new Error(`Duplicate class ${namespaceClassName}`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Class hierarchy is known.
|
|
302
|
+
let baseClassType: typeof LibProxy = Target as unknown as typeof LibProxy;
|
|
303
|
+
let baseClassDescriptor: ClassDescriptor | undefined;
|
|
304
|
+
|
|
305
|
+
do {
|
|
306
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Class hierarchy is known.
|
|
307
|
+
baseClassType = Object.getPrototypeOf(baseClassType) as typeof LibProxy;
|
|
308
|
+
|
|
309
|
+
const namespaceBaseClassName = `${namespacePrefix}${baseClassType.name}`;
|
|
310
|
+
|
|
311
|
+
// Look first within the namespace and then in no namespace, in both abstract class descriptors map and concrete class descriptors map.
|
|
312
|
+
baseClassDescriptor =
|
|
313
|
+
abstractClassDescriptorsMap.get(namespaceBaseClassName) ?? abstractClassDescriptorsMap.get(baseClassType.name) ??
|
|
314
|
+
concreteClassDescriptorsMap.get(namespaceBaseClassName) ?? concreteClassDescriptorsMap.get(baseClassType.name);
|
|
315
|
+
} while (baseClassType !== LibProxy && baseClassDescriptor === undefined);
|
|
316
|
+
|
|
317
|
+
let interimMethodDescriptors: InterimMethodDescriptor[];
|
|
318
|
+
|
|
319
|
+
if (baseClassDescriptor !== undefined) {
|
|
320
|
+
const baseClassMethodDescriptors = baseClassDescriptor.methodDescriptors;
|
|
321
|
+
const replaceParameterDescriptors = decoratorClassDescriptor.replaceParameterDescriptors;
|
|
322
|
+
|
|
323
|
+
if (replaceParameterDescriptors !== undefined) {
|
|
324
|
+
const replacementParameterDescriptorsMap = new Map(replaceParameterDescriptors.map(replaceParameterDescriptor => [replaceParameterDescriptor.name, expandParameterDescriptor(replaceParameterDescriptor.replacement)]));
|
|
325
|
+
|
|
326
|
+
// Interim method descriptors for class have to be built as copies due to possible mutation of parameter descriptors.
|
|
327
|
+
interimMethodDescriptors = baseClassMethodDescriptors.map(baseClassMethodDescriptor => ({
|
|
328
|
+
...baseClassMethodDescriptor,
|
|
329
|
+
parameterDescriptors: baseClassMethodDescriptor.parameterDescriptors.map(parameterDescriptor => replacementParameterDescriptorsMap.get(parameterDescriptor.name) ?? parameterDescriptor)
|
|
330
|
+
}));
|
|
331
|
+
} else {
|
|
332
|
+
interimMethodDescriptors = baseClassMethodDescriptors.slice();
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
interimMethodDescriptors = [];
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Replace base class method descriptors with matching names or append new method descriptor.
|
|
339
|
+
for (const classInterimMethodDescriptor of interim.methodDescriptors) {
|
|
340
|
+
const existingIndex = interimMethodDescriptors.findIndex(interimMethodDescriptor => interimMethodDescriptor.name === classInterimMethodDescriptor.name);
|
|
341
|
+
|
|
342
|
+
if (existingIndex !== -1) {
|
|
343
|
+
interimMethodDescriptors[existingIndex] = classInterimMethodDescriptor;
|
|
344
|
+
} else {
|
|
345
|
+
interimMethodDescriptors.push(classInterimMethodDescriptor);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const methodDescriptors: MethodDescriptor[] = [];
|
|
350
|
+
|
|
351
|
+
const methodInfix = decoratorClassDescriptor.methodInfix;
|
|
352
|
+
|
|
353
|
+
for (const interimMethodDescriptor of interimMethodDescriptors) {
|
|
354
|
+
const methodName = interimMethodDescriptor.name;
|
|
355
|
+
const infixBefore = interimMethodDescriptor.infixBefore;
|
|
356
|
+
|
|
357
|
+
let functionName: string;
|
|
358
|
+
|
|
359
|
+
if (methodInfix === undefined || interimMethodDescriptor.ignoreInfix === true) {
|
|
360
|
+
// No other classes in the hierarchy or no infix required.
|
|
361
|
+
functionName = methodName;
|
|
362
|
+
} else if (infixBefore === undefined) {
|
|
363
|
+
// Other classes in the hierarchy and infix is postfix.
|
|
364
|
+
functionName = `${methodName}${methodInfix}`;
|
|
365
|
+
} else {
|
|
366
|
+
const insertIndex = methodName.indexOf(infixBefore);
|
|
367
|
+
|
|
368
|
+
if (insertIndex === -1) {
|
|
369
|
+
throw new Error(`Cannot find "${infixBefore}" in method ${methodName}`);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Other classes in the hierarchy and infix is in the middle of the string.
|
|
373
|
+
functionName = `${methodName.substring(0, insertIndex)}${methodInfix}${methodName.substring(insertIndex)}`;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const namespaceFunctionName = `${namespacePrefix}${functionName}`;
|
|
377
|
+
|
|
378
|
+
const methodDescriptor = {
|
|
379
|
+
...interimMethodDescriptor,
|
|
380
|
+
functionName,
|
|
381
|
+
namespaceFunctionName
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
methodDescriptors.push(methodDescriptor);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// First capture group is:
|
|
388
|
+
// - one or more uppercase letters followed by zero or more numbers; or
|
|
389
|
+
// - single uppercase letter followed by zero or more characters except uppercase letters or period.
|
|
390
|
+
//
|
|
391
|
+
// Second capture group is:
|
|
392
|
+
// - single uppercase letter followed by zero or more characters except period; or
|
|
393
|
+
// - zero characters (empty string).
|
|
394
|
+
//
|
|
395
|
+
// Third capture group, separated by optional period, is:
|
|
396
|
+
// - single uppercase letter followed by zero or more characters (remainder of string); or
|
|
397
|
+
// - zero characters (empty string).
|
|
398
|
+
const objectNameGroups = /^(?<namespaceFirstWord>[A-Z]+[0-9]*|[A-Z][^A-Z.]*)(?<namespaceRemaining>[A-Z][^.]*|)\.?(?<className>[A-Z].*|)$/.exec(namespaceClassName)?.groups;
|
|
399
|
+
|
|
400
|
+
if (objectNameGroups === undefined) {
|
|
401
|
+
throw new Error(`${namespaceClassName} is not a valid namespace-qualified class name`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const classDescriptor: ClassDescriptor = {
|
|
405
|
+
name,
|
|
406
|
+
...interimClassDescriptor,
|
|
407
|
+
namespaceClassName,
|
|
408
|
+
objectName: `${objectNameGroups["namespaceFirstWord"].toLowerCase()}${objectNameGroups["namespaceRemaining"]}${objectNameGroups["className"]}`,
|
|
409
|
+
methodDescriptors
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
(isAbstract ? abstractClassDescriptorsMap : concreteClassDescriptorsMap).set(namespaceClassName, classDescriptor);
|
|
413
|
+
|
|
414
|
+
const methodDescriptorsMap = new Map<string, MethodDescriptor>();
|
|
415
|
+
|
|
416
|
+
for (const methodDescriptor of methodDescriptors) {
|
|
417
|
+
methodDescriptorsMap.set(methodDescriptor.name, methodDescriptor);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
this.#interim = undefined;
|
|
421
|
+
|
|
422
|
+
const logger = this.#logger;
|
|
423
|
+
|
|
424
|
+
return class extends Target implements TargetLogger {
|
|
425
|
+
/**
|
|
426
|
+
* @inheritDoc
|
|
427
|
+
*/
|
|
428
|
+
log(logLevel: LogLevel, methodName: string, args: unknown[], result: unknown): void {
|
|
429
|
+
// // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Type hierarchy is known.
|
|
430
|
+
// const appExtension = (this as unknown as T).appExtension;
|
|
431
|
+
|
|
432
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Method name is known to be valid at this point.
|
|
433
|
+
const methodDescriptor = methodDescriptorsMap.get(methodName)!;
|
|
434
|
+
|
|
435
|
+
logger.log(logLevel, "", JSON.stringify({
|
|
436
|
+
namespace: decoratorClassDescriptor.namespace,
|
|
437
|
+
className: name,
|
|
438
|
+
methodName,
|
|
439
|
+
functionName: methodDescriptor.functionName,
|
|
440
|
+
parameters: methodDescriptor.parameterDescriptors.map((parameterDescriptor, index) => ({
|
|
441
|
+
name: parameterDescriptor.name,
|
|
442
|
+
value: args[index]
|
|
443
|
+
})),
|
|
444
|
+
result: Proxy.#jsonValue(result)
|
|
445
|
+
}, null, 2));
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Describe a proxy method.
|
|
453
|
+
*
|
|
454
|
+
* @template TFunction
|
|
455
|
+
* Function type.
|
|
456
|
+
*
|
|
457
|
+
* @param decoratorMethodDescriptor
|
|
458
|
+
* Method descriptor.
|
|
459
|
+
*
|
|
460
|
+
* @returns
|
|
461
|
+
* Function with which to decorate the method.
|
|
462
|
+
*/
|
|
463
|
+
describeMethod<TThis, TArguments extends unknown[], TReturn>(decoratorMethodDescriptor: DecoratorMethodDescriptor): ClassMethodDecorator<TThis, TArguments, TReturn> {
|
|
464
|
+
return (target: (this: TThis, ...args: TArguments) => TReturn, context: ClassMethodDecoratorContext<TThis>): (this: TThis, ...args: TArguments) => TReturn => {
|
|
465
|
+
const name = context.name;
|
|
466
|
+
|
|
467
|
+
// Validate that method descriptor is applied within an appropriate class and has a valid name.
|
|
468
|
+
if (this.#interim === undefined || typeof name !== "string" || context.static || context.private) {
|
|
469
|
+
throw new Error(`${String(name)} is not defined within a supported class, has an invalid name, is static, or is private`);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
let anyOptional = false;
|
|
473
|
+
|
|
474
|
+
// Expand all parameter descriptors.
|
|
475
|
+
const parameterDescriptors = decoratorMethodDescriptor.parameterDescriptors.map((decoratorParameterDescriptor) => {
|
|
476
|
+
const parameterDescriptor = expandParameterDescriptor(decoratorParameterDescriptor);
|
|
477
|
+
|
|
478
|
+
if (!parameterDescriptor.isRequired) {
|
|
479
|
+
anyOptional = true;
|
|
480
|
+
} else if (anyOptional) {
|
|
481
|
+
throw new Error(`Parameter ${parameterDescriptor.name} descriptor of method ${name} is required but prior parameter descriptor is optional`);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return parameterDescriptor;
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
this.#interim.methodDescriptors.push({
|
|
488
|
+
name,
|
|
489
|
+
...decoratorMethodDescriptor,
|
|
490
|
+
parameterDescriptors
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
return function methodProxy(this: TThis, ...args: TArguments): TReturn {
|
|
494
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Class has been modified to add log method.
|
|
495
|
+
const targetLogger = this as TargetLogger;
|
|
496
|
+
|
|
497
|
+
let result: TReturn;
|
|
498
|
+
|
|
499
|
+
try {
|
|
500
|
+
result = target.call(this, ...args);
|
|
501
|
+
|
|
502
|
+
// TODO Change this to LogLevels.Trace when configuration available.
|
|
503
|
+
targetLogger.log(LogLevels.Info, name, args, result);
|
|
504
|
+
} catch (e: unknown) {
|
|
505
|
+
targetLogger.log(LogLevels.Error, name, args, e instanceof Error ? `${e.name}: ${e.message}` : `Unknown exception: ${String(e)}`);
|
|
506
|
+
|
|
507
|
+
throw e;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return result;
|
|
511
|
+
};
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Get the class descriptors map.
|
|
517
|
+
*/
|
|
518
|
+
get classDescriptorsMap(): Map<string, ClassDescriptor> {
|
|
519
|
+
return this.#concreteClassDescriptorsMap;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Proxy object used by all proxy classes.
|
|
525
|
+
*/
|
|
526
|
+
export const proxy = new Proxy();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ParameterDescriptor, Types } from "../descriptor.js";
|
|
1
|
+
import { type ExtendsParameterDescriptor, type ParameterDescriptor, Types } from "../descriptor.js";
|
|
2
2
|
|
|
3
3
|
const exclusionParameterDescriptor: ParameterDescriptor = {
|
|
4
4
|
name: "exclusion",
|
|
@@ -7,25 +7,25 @@ const exclusionParameterDescriptor: ParameterDescriptor = {
|
|
|
7
7
|
isRequired: false
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
export const exclusionNoneParameterDescriptor:
|
|
10
|
+
export const exclusionNoneParameterDescriptor: ExtendsParameterDescriptor = {
|
|
11
11
|
extendsDescriptor: exclusionParameterDescriptor,
|
|
12
12
|
sortOrder: 0,
|
|
13
13
|
name: "exclusionNone"
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export const exclusionFirstZeroParameterDescriptor:
|
|
16
|
+
export const exclusionFirstZeroParameterDescriptor: ExtendsParameterDescriptor = {
|
|
17
17
|
extendsDescriptor: exclusionParameterDescriptor,
|
|
18
18
|
sortOrder: 1,
|
|
19
19
|
name: "exclusionFirstZero"
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
export const exclusionAllNumericParameterDescriptor:
|
|
22
|
+
export const exclusionAllNumericParameterDescriptor: ExtendsParameterDescriptor = {
|
|
23
23
|
extendsDescriptor: exclusionParameterDescriptor,
|
|
24
24
|
sortOrder: 2,
|
|
25
25
|
name: "exclusionAllNumeric"
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
export const exclusionAnyParameterDescriptor:
|
|
28
|
+
export const exclusionAnyParameterDescriptor: ExtendsParameterDescriptor = {
|
|
29
29
|
extendsDescriptor: exclusionParameterDescriptor,
|
|
30
30
|
sortOrder: 3,
|
|
31
31
|
name: "exclusionAny"
|