xdbc 1.0.211 → 1.0.213
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/.gitattributes +8 -0
- package/.github/workflows/ci.yml +51 -0
- package/.vscode/tasks.json +23 -23
- package/CODE_OF_CONDUCT.md +1 -1
- package/README.md +1 -0
- package/__tests__/DBC/AE.test.ts +1 -1
- package/__tests__/DBC/DEFINED.test.ts +49 -49
- package/__tests__/DBC/Decorators.test.ts +334 -332
- package/__tests__/DBC/HasAttribute.test.ts +56 -52
- package/__tests__/DBC/IF.test.ts +57 -57
- package/__tests__/DBC/OR.test.ts +1 -1
- package/__tests__/DBC/UNDEFINED.test.ts +41 -41
- package/__tests__/DBC/ZOD.test.ts +50 -50
- package/biome.json +40 -33
- package/dist/DBC/AE.d.ts +117 -0
- package/dist/DBC/COMPARISON/GREATER.d.ts +13 -0
- package/dist/DBC/COMPARISON/GREATER_OR_EQUAL.d.ts +13 -0
- package/dist/DBC/COMPARISON/LESS.d.ts +13 -0
- package/dist/DBC/COMPARISON/LESS_OR_EQUAL.d.ts +13 -0
- package/dist/DBC/COMPARISON.d.ts +70 -0
- package/dist/DBC/DEFINED.d.ts +62 -0
- package/dist/DBC/EQ/DIFFERENT.d.ts +26 -0
- package/dist/DBC/EQ.d.ts +70 -0
- package/dist/DBC/HasAttribute.d.ts +72 -0
- package/dist/DBC/IF.d.ts +90 -0
- package/dist/DBC/INSTANCE.d.ts +84 -0
- package/dist/DBC/JSON.OP.d.ts +94 -0
- package/dist/DBC/JSON.Parse.d.ts +69 -0
- package/dist/DBC/OR.d.ts +104 -0
- package/dist/DBC/REGEX.d.ts +96 -0
- package/dist/DBC/TYPE.d.ts +75 -0
- package/dist/DBC/UNDEFINED.d.ts +62 -0
- package/dist/DBC/ZOD.d.ts +72 -0
- package/dist/DBC.d.ts +244 -0
- package/dist/Demo.d.ts +20 -0
- package/dist/bundle.js +2297 -0
- package/docs/assets/highlight.css +22 -22
- package/docs/assets/icons.js +17 -17
- package/docs/assets/main.js +60 -60
- package/docs/assets/style.css +1640 -1640
- package/docs/classes/DBC.DBC.html +98 -98
- package/docs/classes/DBC_AE.AE.html +160 -160
- package/docs/classes/DBC_EQ.EQ.html +131 -131
- package/docs/classes/DBC_GREATER.GREATER.html +139 -139
- package/docs/classes/DBC_INSTANCE.INSTANCE.html +130 -130
- package/docs/classes/DBC_JSON.OP.JSON_OP.html +138 -138
- package/docs/classes/DBC_JSON.Parse.JSON_Parse.html +129 -129
- package/docs/classes/DBC_OR.OR.html +137 -137
- package/docs/classes/DBC_REGEX.REGEX.html +136 -136
- package/docs/classes/DBC_TYPE.TYPE.html +130 -130
- package/docs/classes/Demo.Demo.html +14 -14
- package/docs/hierarchy.html +1 -1
- package/docs/index.html +1 -1
- package/docs/modules/DBC.html +1 -1
- package/docs/modules/DBC_AE.html +1 -1
- package/docs/modules/DBC_EQ.html +1 -1
- package/docs/modules/DBC_GREATER.html +1 -1
- package/docs/modules/DBC_INSTANCE.html +1 -1
- package/docs/modules/DBC_JSON.OP.html +1 -1
- package/docs/modules/DBC_JSON.Parse.html +1 -1
- package/docs/modules/DBC_OR.html +1 -1
- package/docs/modules/DBC_REGEX.html +1 -1
- package/docs/modules/DBC_TYPE.html +1 -1
- package/docs/modules/Demo.html +1 -1
- package/jest.config.js +29 -18
- package/package.json +6 -2
- package/src/DBC/AE.ts +14 -9
- package/src/DBC/COMPARISON/GREATER.ts +2 -2
- package/src/DBC/COMPARISON.ts +159 -136
- package/src/DBC/DEFINED.ts +10 -10
- package/src/DBC/EQ/DIFFERENT.ts +3 -3
- package/src/DBC/EQ.ts +25 -9
- package/src/DBC/HasAttribute.ts +17 -3
- package/src/DBC/IF.ts +63 -19
- package/src/DBC/INSTANCE.ts +29 -14
- package/src/DBC/JSON.OP.ts +18 -3
- package/src/DBC/JSON.Parse.ts +21 -4
- package/src/DBC/OR.ts +12 -7
- package/src/DBC/REGEX.ts +30 -21
- package/src/DBC/TYPE.ts +15 -11
- package/src/DBC/UNDEFINED.ts +7 -10
- package/src/DBC/ZOD.ts +14 -9
- package/src/DBC.ts +165 -69
- package/src/Demo.ts +21 -18
- package/test.drawio +0 -0
- package/tsconfig.json +3 -5
- package/tsconfig.test.json +6 -11
- package/webpack.config.js +1 -1
package/src/DBC.ts
CHANGED
|
@@ -30,7 +30,9 @@ export class DBC {
|
|
|
30
30
|
DBC.dbcCache.set(path, resolved);
|
|
31
31
|
return resolved;
|
|
32
32
|
}
|
|
33
|
-
throw new Error(
|
|
33
|
+
throw new Error(
|
|
34
|
+
`[XDBC] DBC instance not found at path "${path}". Ensure a DBC instance is registered there.`,
|
|
35
|
+
);
|
|
34
36
|
}
|
|
35
37
|
// #endregion Internal caches.
|
|
36
38
|
// #region Parameter-value requests.
|
|
@@ -48,8 +50,14 @@ export class DBC {
|
|
|
48
50
|
* Generate a unique key for storing parameter value requests.
|
|
49
51
|
* Format: "ClassName:methodName"
|
|
50
52
|
*/
|
|
51
|
-
private static getRequestKey(
|
|
52
|
-
|
|
53
|
+
private static getRequestKey(
|
|
54
|
+
target: object,
|
|
55
|
+
methodName: string | symbol,
|
|
56
|
+
): string {
|
|
57
|
+
const className =
|
|
58
|
+
typeof target === "function"
|
|
59
|
+
? target.name
|
|
60
|
+
: target.constructor?.name || "Unknown";
|
|
53
61
|
return `${className}:${String(methodName)}`;
|
|
54
62
|
}
|
|
55
63
|
/**
|
|
@@ -74,7 +82,7 @@ export class DBC {
|
|
|
74
82
|
if (DBC.paramValueRequests.has(key)) {
|
|
75
83
|
const paramMap = DBC.paramValueRequests.get(key)!;
|
|
76
84
|
if (paramMap.has(index)) {
|
|
77
|
-
paramMap.get(index)
|
|
85
|
+
paramMap.get(index)?.push(receptor);
|
|
78
86
|
} else {
|
|
79
87
|
paramMap.set(index, new Array<(value: unknown) => undefined>(receptor));
|
|
80
88
|
}
|
|
@@ -90,25 +98,62 @@ export class DBC {
|
|
|
90
98
|
return undefined;
|
|
91
99
|
}
|
|
92
100
|
/**
|
|
93
|
-
* A
|
|
94
|
-
* also usable on setters.
|
|
95
|
-
* When found it will invoke the "receptor" registered there, inter alia by {@link requestParamValue }, with the
|
|
96
|
-
* parameter's value.
|
|
101
|
+
* A decorator usable on both **methods** (including setters) and **classes**.
|
|
97
102
|
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
103
|
+
* - **On a method**: wraps the method so that any {@link DBC } preconditions registered on its parameters
|
|
104
|
+
* via XDBC parameter-decorator factories are dispatched with the actual argument values on each call.
|
|
105
|
+
* - **On a class**: wraps the constructor so that preconditions registered on constructor parameters are
|
|
106
|
+
* dispatched **before** the original constructor body runs, upholding true precondition semantics.
|
|
101
107
|
*
|
|
102
|
-
*
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
* In both cases the "receptor" callbacks enlisted in {@link paramValueRequests } by {@link requestParamValue }
|
|
109
|
+
* are invoked with the live argument values.
|
|
110
|
+
*
|
|
111
|
+
* @param targetOrConstructor When used as a **method** decorator: the {@link object } hosting the tagged method.
|
|
112
|
+
* When used as a **class** decorator: the class constructor.
|
|
113
|
+
* @param propertyKey *(method decorator only)* The tagged method's name as provided by the runtime.
|
|
114
|
+
* @param descriptor *(method decorator only)* The {@link PropertyDescriptor } as provided by the runtime.
|
|
115
|
+
*
|
|
116
|
+
* @returns When used as a **method** decorator: the (modified) {@link PropertyDescriptor }.
|
|
117
|
+
* When used as a **class** decorator: a replacement constructor that performs precondition checks. */
|
|
118
|
+
// biome-ignore lint/suspicious/noExplicitAny: Must handle both method and class decorator signatures
|
|
119
|
+
public static ParamvalueProvider(target: object, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor;
|
|
120
|
+
// biome-ignore lint/suspicious/noExplicitAny: Must handle both method and class decorator signatures
|
|
121
|
+
public static ParamvalueProvider<T extends new (...args: any[]) => any>(constructor: T): T;
|
|
122
|
+
// biome-ignore lint/suspicious/noExplicitAny: Must handle both method and class decorator signatures
|
|
123
|
+
public static ParamvalueProvider(...args: any[]): any {
|
|
124
|
+
if (args.length === 1 && typeof args[0] === "function") {
|
|
125
|
+
// #region Class decorator path
|
|
126
|
+
// biome-ignore lint/suspicious/noExplicitAny: Must accept any constructor signature
|
|
127
|
+
const constructor = args[0] as new (...args: any[]) => any;
|
|
128
|
+
const key = `${constructor.name}:undefined`;
|
|
129
|
+
// biome-ignore lint/suspicious/noExplicitAny: Must accept any constructor signature
|
|
130
|
+
const WrappedClass = class extends constructor {
|
|
131
|
+
// biome-ignore lint/suspicious/noExplicitAny: Must accept any constructor signature
|
|
132
|
+
constructor(...ctorArgs: any[]) {
|
|
133
|
+
if (DBC.paramValueRequests.has(key)) {
|
|
134
|
+
const paramMap = DBC.paramValueRequests.get(key)!;
|
|
135
|
+
for (const index of paramMap.keys()) {
|
|
136
|
+
if (index < ctorArgs.length) {
|
|
137
|
+
for (const receptor of paramMap.get(index)!) {
|
|
138
|
+
receptor(ctorArgs[index]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
super(...ctorArgs);
|
|
144
|
+
}
|
|
145
|
+
} as typeof constructor;
|
|
146
|
+
Object.defineProperty(WrappedClass, "name", { value: constructor.name });
|
|
147
|
+
return WrappedClass;
|
|
148
|
+
// #endregion Class decorator path
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// #region Method decorator path
|
|
152
|
+
const [target, propertyKey, descriptor] = args as [object, string, PropertyDescriptor];
|
|
108
153
|
const originalMethod = descriptor.value;
|
|
109
|
-
const isStatic = typeof target ===
|
|
154
|
+
const isStatic = typeof target === "function";
|
|
110
155
|
// biome-ignore lint/suspicious/noExplicitAny: Gotta be any since parameter-values may be undefined.
|
|
111
|
-
descriptor.value = function (...
|
|
156
|
+
descriptor.value = function (...methodArgs: any[]) {
|
|
112
157
|
// #region Check if a value of one of the method's parameter has been requested and pass it to the
|
|
113
158
|
// receptor, if so.
|
|
114
159
|
const actualTarget = isStatic ? this : (this as any).constructor;
|
|
@@ -117,9 +162,9 @@ export class DBC {
|
|
|
117
162
|
if (DBC.paramValueRequests.has(key)) {
|
|
118
163
|
const paramMap = DBC.paramValueRequests.get(key)!;
|
|
119
164
|
for (const index of paramMap.keys()) {
|
|
120
|
-
if (index <
|
|
165
|
+
if (index < methodArgs.length) {
|
|
121
166
|
for (const receptor of paramMap.get(index)!) {
|
|
122
|
-
receptor(
|
|
167
|
+
receptor(methodArgs[index]);
|
|
123
168
|
}
|
|
124
169
|
}
|
|
125
170
|
}
|
|
@@ -128,10 +173,10 @@ export class DBC {
|
|
|
128
173
|
}
|
|
129
174
|
// #endregion Check if a value of one of the method's parameter has been requested and pass it to the
|
|
130
175
|
// receptor, if so.
|
|
131
|
-
return originalMethod.apply(this,
|
|
176
|
+
return originalMethod.apply(this, methodArgs);
|
|
132
177
|
};
|
|
133
|
-
|
|
134
178
|
return descriptor;
|
|
179
|
+
// #endregion Method decorator path
|
|
135
180
|
}
|
|
136
181
|
// #endregion Parameter-value requests.
|
|
137
182
|
// #region Class
|
|
@@ -151,7 +196,11 @@ export class DBC {
|
|
|
151
196
|
dbc = "WaXCode.DBC",
|
|
152
197
|
) {
|
|
153
198
|
let dbcInstance: DBC | undefined;
|
|
154
|
-
return (
|
|
199
|
+
return (
|
|
200
|
+
target: unknown,
|
|
201
|
+
propertyKey: string | symbol,
|
|
202
|
+
descriptor: PropertyDescriptor,
|
|
203
|
+
) => {
|
|
155
204
|
if (!dbcInstance) dbcInstance = DBC.getDBC(dbc);
|
|
156
205
|
if (!dbcInstance.executionSettings.checkInvariants) {
|
|
157
206
|
return;
|
|
@@ -163,7 +212,7 @@ export class DBC {
|
|
|
163
212
|
// #region Replace original property.
|
|
164
213
|
Object.defineProperty(target, propertyKey, {
|
|
165
214
|
get() {
|
|
166
|
-
if (!dbcInstance
|
|
215
|
+
if (!dbcInstance?.executionSettings.checkInvariants) {
|
|
167
216
|
return;
|
|
168
217
|
}
|
|
169
218
|
|
|
@@ -173,7 +222,7 @@ export class DBC {
|
|
|
173
222
|
const result = contract.check(realValue);
|
|
174
223
|
|
|
175
224
|
if (typeof result === "string") {
|
|
176
|
-
dbcInstance
|
|
225
|
+
dbcInstance?.reportFieldInfringement(
|
|
177
226
|
result,
|
|
178
227
|
target as object,
|
|
179
228
|
path,
|
|
@@ -186,7 +235,7 @@ export class DBC {
|
|
|
186
235
|
return originalGetter ? (originalGetter as any)[propertyKey] : value;
|
|
187
236
|
},
|
|
188
237
|
set(newValue) {
|
|
189
|
-
if (!dbcInstance
|
|
238
|
+
if (!dbcInstance?.executionSettings.checkInvariants) {
|
|
190
239
|
return;
|
|
191
240
|
}
|
|
192
241
|
|
|
@@ -196,7 +245,7 @@ export class DBC {
|
|
|
196
245
|
const result = contract.check(realValue);
|
|
197
246
|
|
|
198
247
|
if (typeof result === "string") {
|
|
199
|
-
dbcInstance
|
|
248
|
+
dbcInstance?.reportFieldInfringement(
|
|
200
249
|
result,
|
|
201
250
|
target as object,
|
|
202
251
|
path,
|
|
@@ -244,7 +293,7 @@ export class DBC {
|
|
|
244
293
|
// #region Replace original property.
|
|
245
294
|
Object.defineProperty(target, propertyKey, {
|
|
246
295
|
set(newValue) {
|
|
247
|
-
if (!dbcInstance
|
|
296
|
+
if (!dbcInstance?.executionSettings.checkInvariants) {
|
|
248
297
|
return;
|
|
249
298
|
}
|
|
250
299
|
|
|
@@ -254,13 +303,13 @@ export class DBC {
|
|
|
254
303
|
const result = contract.check(realValue);
|
|
255
304
|
|
|
256
305
|
if (typeof result === "string") {
|
|
257
|
-
dbcInstance
|
|
306
|
+
dbcInstance?.reportFieldInfringement(
|
|
258
307
|
result,
|
|
259
308
|
target as object,
|
|
260
309
|
path,
|
|
261
310
|
propertyKey as string,
|
|
262
311
|
realValue,
|
|
263
|
-
hint
|
|
312
|
+
hint,
|
|
264
313
|
);
|
|
265
314
|
}
|
|
266
315
|
}
|
|
@@ -288,7 +337,11 @@ export class DBC {
|
|
|
288
337
|
// biome-ignore lint/suspicious/noExplicitAny: Necessary to intercept UNDEFINED and NULL.
|
|
289
338
|
public static decPostcondition(
|
|
290
339
|
// biome-ignore lint/suspicious/noExplicitAny: Necessary to intercept UNDEFINED and NULL.
|
|
291
|
-
check: (
|
|
340
|
+
check: (
|
|
341
|
+
toCheck: any,
|
|
342
|
+
target: object,
|
|
343
|
+
propertyKey: string,
|
|
344
|
+
) => boolean | string,
|
|
292
345
|
dbc: string | undefined = undefined,
|
|
293
346
|
path: string | undefined = undefined,
|
|
294
347
|
hint: string | undefined = undefined,
|
|
@@ -318,7 +371,7 @@ export class DBC {
|
|
|
318
371
|
path,
|
|
319
372
|
propertyKey,
|
|
320
373
|
realValue,
|
|
321
|
-
hint
|
|
374
|
+
hint,
|
|
322
375
|
);
|
|
323
376
|
}
|
|
324
377
|
|
|
@@ -344,7 +397,12 @@ export class DBC {
|
|
|
344
397
|
* @returns The **(target: object, methodName: string | symbol, parameterIndex: number ) => void** invoked by Typescript- */
|
|
345
398
|
protected static decPrecondition(
|
|
346
399
|
// biome-ignore lint/suspicious/noExplicitAny: Necessary to check any parameter value
|
|
347
|
-
check: (
|
|
400
|
+
check: (
|
|
401
|
+
value: unknown,
|
|
402
|
+
target: object,
|
|
403
|
+
methodName: string | symbol,
|
|
404
|
+
parameterIndex: number,
|
|
405
|
+
) => boolean | string,
|
|
348
406
|
dbc: string | undefined = undefined,
|
|
349
407
|
path: string | undefined = undefined,
|
|
350
408
|
hint: string | undefined = undefined,
|
|
@@ -371,7 +429,9 @@ export class DBC {
|
|
|
371
429
|
}
|
|
372
430
|
|
|
373
431
|
for (const singlePath of paths) {
|
|
374
|
-
const realValue = singlePath
|
|
432
|
+
const realValue = singlePath
|
|
433
|
+
? DBC.resolve(value, singlePath)
|
|
434
|
+
: value;
|
|
375
435
|
const result = check(realValue, target, methodName, parameterIndex);
|
|
376
436
|
|
|
377
437
|
if (typeof result === "string") {
|
|
@@ -382,7 +442,7 @@ export class DBC {
|
|
|
382
442
|
methodName as string,
|
|
383
443
|
parameterIndex,
|
|
384
444
|
realValue,
|
|
385
|
-
hint
|
|
445
|
+
hint,
|
|
386
446
|
);
|
|
387
447
|
}
|
|
388
448
|
}
|
|
@@ -459,19 +519,16 @@ export class DBC {
|
|
|
459
519
|
*/
|
|
460
520
|
public static createINVARIANT(
|
|
461
521
|
// biome-ignore lint/suspicious/noExplicitAny: Must accept any contract constructor
|
|
462
|
-
ContractClass: new (
|
|
522
|
+
ContractClass: new (
|
|
523
|
+
...args: any[]
|
|
524
|
+
) => { check: (toCheck: unknown | null | undefined) => boolean | string },
|
|
463
525
|
// biome-ignore lint/suspicious/noExplicitAny: Arguments vary per contract
|
|
464
526
|
ctorArgs: any[],
|
|
465
527
|
dbc?: string,
|
|
466
528
|
path?: string,
|
|
467
529
|
hint?: string,
|
|
468
530
|
) {
|
|
469
|
-
return DBC.decInvariant(
|
|
470
|
-
[new ContractClass(...ctorArgs)],
|
|
471
|
-
path,
|
|
472
|
-
dbc,
|
|
473
|
-
hint,
|
|
474
|
-
);
|
|
531
|
+
return DBC.decInvariant([new ContractClass(...ctorArgs)], path, dbc, hint);
|
|
475
532
|
}
|
|
476
533
|
// #endregion Contract Factory Helpers
|
|
477
534
|
// #region Execution Handling
|
|
@@ -481,10 +538,10 @@ export class DBC {
|
|
|
481
538
|
checkPostconditions: boolean;
|
|
482
539
|
checkInvariants: boolean;
|
|
483
540
|
} = {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
541
|
+
checkPreconditions: true,
|
|
542
|
+
checkPostconditions: true,
|
|
543
|
+
checkInvariants: true,
|
|
544
|
+
};
|
|
488
545
|
// #endregion Execution Handling
|
|
489
546
|
// #region Warning handling.
|
|
490
547
|
/** Stores settings concerning warnings. */
|
|
@@ -512,12 +569,18 @@ export class DBC {
|
|
|
512
569
|
const str = typeof value === "string" ? value : String(value);
|
|
513
570
|
return str.replace(/[<>&"']/g, (ch) => {
|
|
514
571
|
switch (ch) {
|
|
515
|
-
case "<":
|
|
516
|
-
|
|
517
|
-
case "
|
|
518
|
-
|
|
519
|
-
case "
|
|
520
|
-
|
|
572
|
+
case "<":
|
|
573
|
+
return "<";
|
|
574
|
+
case ">":
|
|
575
|
+
return ">";
|
|
576
|
+
case "&":
|
|
577
|
+
return "&";
|
|
578
|
+
case '"':
|
|
579
|
+
return """;
|
|
580
|
+
case "'":
|
|
581
|
+
return "'";
|
|
582
|
+
default:
|
|
583
|
+
return ch;
|
|
521
584
|
}
|
|
522
585
|
});
|
|
523
586
|
}
|
|
@@ -536,7 +599,14 @@ export class DBC {
|
|
|
536
599
|
hint: string | undefined = undefined,
|
|
537
600
|
): undefined {
|
|
538
601
|
const safeViolator = DBC.sanitize(violator);
|
|
539
|
-
const targetName =
|
|
602
|
+
const targetName =
|
|
603
|
+
typeof target === "function"
|
|
604
|
+
? DBC.sanitize(target.name)
|
|
605
|
+
: typeof target === "object" &&
|
|
606
|
+
target !== null &&
|
|
607
|
+
typeof target.constructor === "function"
|
|
608
|
+
? DBC.sanitize(target.constructor.name)
|
|
609
|
+
: DBC.sanitize(target);
|
|
540
610
|
const finalMessage: string = `[ From "${safeViolator}" in "${targetName}"${path ? ` > "${DBC.sanitize(path)}"` : ""}: ${message} ${hint ? `✨ ${hint} ✨` : ""}]`;
|
|
541
611
|
|
|
542
612
|
if (this.infringementSettings.throwException) {
|
|
@@ -572,7 +642,7 @@ export class DBC {
|
|
|
572
642
|
target,
|
|
573
643
|
value,
|
|
574
644
|
path,
|
|
575
|
-
hint
|
|
645
|
+
hint,
|
|
576
646
|
);
|
|
577
647
|
}
|
|
578
648
|
/**
|
|
@@ -622,7 +692,7 @@ export class DBC {
|
|
|
622
692
|
target,
|
|
623
693
|
value,
|
|
624
694
|
path,
|
|
625
|
-
hint
|
|
695
|
+
hint,
|
|
626
696
|
);
|
|
627
697
|
}
|
|
628
698
|
// #region Classes
|
|
@@ -670,10 +740,10 @@ export class DBC {
|
|
|
670
740
|
checkPostconditions: boolean;
|
|
671
741
|
checkInvariants: boolean;
|
|
672
742
|
} = {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
743
|
+
checkPreconditions: true,
|
|
744
|
+
checkPostconditions: true,
|
|
745
|
+
checkInvariants: true,
|
|
746
|
+
},
|
|
677
747
|
) {
|
|
678
748
|
this.infringementSettings = infringementSettings;
|
|
679
749
|
this.executionSettings = executionSettings;
|
|
@@ -727,20 +797,25 @@ export class DBC {
|
|
|
727
797
|
*
|
|
728
798
|
* @returns The requested {@link object }, NULL or UNDEFINED. */
|
|
729
799
|
public static resolve(toResolveFrom: unknown, path: string) {
|
|
730
|
-
if (!toResolveFrom || typeof path !== "string") {
|
|
800
|
+
if (!toResolveFrom || typeof path !== "string") {
|
|
801
|
+
return undefined;
|
|
802
|
+
}
|
|
731
803
|
|
|
732
804
|
// Security: block prototype pollution paths
|
|
733
805
|
const dangerousTokens = ["__proto__", "constructor", "prototype"];
|
|
734
806
|
|
|
735
807
|
const cachedParts = DBC.pathTokenCache.get(path);
|
|
736
|
-
const parts =
|
|
808
|
+
const parts =
|
|
809
|
+
cachedParts ?? path.replace(/\[(['"]?)(.*?)\1\]/g, ".$2").split(".");
|
|
737
810
|
|
|
738
811
|
if (!cachedParts) {
|
|
739
812
|
// Validate tokens before caching
|
|
740
813
|
for (const part of parts) {
|
|
741
814
|
const tokenName = part.replace(/\(.*\)$/, "");
|
|
742
815
|
if (dangerousTokens.indexOf(tokenName) >= 0) {
|
|
743
|
-
throw new Error(
|
|
816
|
+
throw new Error(
|
|
817
|
+
`[XDBC] Path "${path}" contains forbidden token "${tokenName}".`,
|
|
818
|
+
);
|
|
744
819
|
}
|
|
745
820
|
}
|
|
746
821
|
DBC.evictIfNeeded(DBC.pathTokenCache);
|
|
@@ -751,7 +826,9 @@ export class DBC {
|
|
|
751
826
|
let current: any = toResolveFrom;
|
|
752
827
|
|
|
753
828
|
for (const part of parts) {
|
|
754
|
-
if (current === null || typeof current === "undefined") {
|
|
829
|
+
if (current === null || typeof current === "undefined") {
|
|
830
|
+
return undefined;
|
|
831
|
+
}
|
|
755
832
|
|
|
756
833
|
const methodMatch = part.match(/(\w+)\((.*)\)/);
|
|
757
834
|
|
|
@@ -762,13 +839,32 @@ export class DBC {
|
|
|
762
839
|
|
|
763
840
|
if (typeof current[methodName] === "function") {
|
|
764
841
|
current = current[methodName].apply(current, args);
|
|
765
|
-
} else {
|
|
842
|
+
} else {
|
|
843
|
+
return undefined;
|
|
844
|
+
}
|
|
766
845
|
} else {
|
|
767
|
-
if (
|
|
846
|
+
if (
|
|
847
|
+
typeof window !== "undefined" &&
|
|
848
|
+
typeof HTMLElement !== "undefined" &&
|
|
849
|
+
current instanceof HTMLElement &&
|
|
850
|
+
part.startsWith("@")
|
|
851
|
+
) {
|
|
768
852
|
current = current.getAttribute(part.slice(1));
|
|
769
|
-
} else if (
|
|
770
|
-
|
|
771
|
-
|
|
853
|
+
} else if (
|
|
854
|
+
typeof current === "object" &&
|
|
855
|
+
current !== null &&
|
|
856
|
+
part in current
|
|
857
|
+
) {
|
|
858
|
+
current = current[part];
|
|
859
|
+
} else if (
|
|
860
|
+
typeof window !== "undefined" &&
|
|
861
|
+
typeof HTMLElement !== "undefined" &&
|
|
862
|
+
current instanceof HTMLElement
|
|
863
|
+
) {
|
|
864
|
+
current = undefined;
|
|
865
|
+
} else {
|
|
866
|
+
current = undefined;
|
|
867
|
+
}
|
|
772
868
|
}
|
|
773
869
|
}
|
|
774
870
|
|
package/src/Demo.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { DBC } from "./DBC";
|
|
2
|
-
import { REGEX } from "./DBC/REGEX";
|
|
3
|
-
import { EQ } from "./DBC/EQ";
|
|
4
|
-
import { TYPE } from "./DBC/TYPE";
|
|
5
2
|
import { AE } from "./DBC/AE";
|
|
6
|
-
import { INSTANCE } from "./DBC/INSTANCE";
|
|
7
3
|
import { GREATER } from "./DBC/COMPARISON/GREATER";
|
|
8
4
|
import { GREATER_OR_EQUAL } from "./DBC/COMPARISON/GREATER_OR_EQUAL";
|
|
9
5
|
import { LESS } from "./DBC/COMPARISON/LESS";
|
|
10
6
|
import { LESS_OR_EQUAL } from "./DBC/COMPARISON/LESS_OR_EQUAL";
|
|
7
|
+
import { EQ } from "./DBC/EQ";
|
|
11
8
|
import { DIFFERENT } from "./DBC/EQ/DIFFERENT";
|
|
9
|
+
import { INSTANCE } from "./DBC/INSTANCE";
|
|
10
|
+
import { REGEX } from "./DBC/REGEX";
|
|
11
|
+
import { TYPE } from "./DBC/TYPE";
|
|
12
12
|
/** Demonstrative use of **D**esign **B**y **C**ontract Decorators */
|
|
13
13
|
export class Demo {
|
|
14
14
|
// #region Check Property Decorator
|
|
@@ -32,65 +32,68 @@ export class Demo {
|
|
|
32
32
|
@DBC.ParamvalueProvider
|
|
33
33
|
public testEQAndPath(
|
|
34
34
|
@EQ.PRE("SELECT" as unknown as object, false, "tagName") o: HTMLElement,
|
|
35
|
-
) {
|
|
35
|
+
) {}
|
|
36
36
|
// #endregion Check EQ-DBC & Path to property of Parameter-value
|
|
37
37
|
// #region Check EQ-DBC & Path to property of Parameter-value with Inversion
|
|
38
38
|
@DBC.ParamvalueProvider
|
|
39
39
|
public testEQAndPathWithInversion(
|
|
40
40
|
@EQ.PRE("SELECT" as unknown as object, true, "tagName") o: HTMLElement,
|
|
41
|
-
) {
|
|
41
|
+
) {}
|
|
42
42
|
// #endregion Check EQ-DBC & Path to property of Parameter-value with Inversion
|
|
43
43
|
// #region Check TYPE
|
|
44
44
|
@DBC.ParamvalueProvider
|
|
45
|
-
public testTYPE(@TYPE.PRE("string") o: unknown) {
|
|
45
|
+
public testTYPE(@TYPE.PRE("string") o: unknown) {}
|
|
46
46
|
// #endregion Check TYPE
|
|
47
47
|
// #region Check AE
|
|
48
48
|
@DBC.ParamvalueProvider
|
|
49
|
-
public testAE(@AE.PRE([new TYPE("string")]) x: Array<unknown>) {
|
|
49
|
+
public testAE(@AE.PRE([new TYPE("string")]) x: Array<unknown>) {}
|
|
50
50
|
// #endregion Check AE
|
|
51
51
|
// #region Check REGEX with AE
|
|
52
52
|
@DBC.ParamvalueProvider
|
|
53
53
|
public testREGEXWithAE(
|
|
54
54
|
@AE.PRE(new REGEX(/^(?i:(NOW)|([+-]\d+[dmy]))$/i)) x: Array<string>,
|
|
55
|
-
) {
|
|
55
|
+
) {}
|
|
56
56
|
// #endregion Check REGEX with AE
|
|
57
57
|
// #region Check INSTANCE
|
|
58
58
|
@DBC.ParamvalueProvider
|
|
59
59
|
// biome-ignore lint/suspicious/noExplicitAny: Test
|
|
60
|
-
public testINSTANCE(@INSTANCE.PRE(Date) candidate: any): undefined {
|
|
60
|
+
public testINSTANCE(@INSTANCE.PRE(Date) candidate: any): undefined {}
|
|
61
61
|
// #endregion Check INSTANCE
|
|
62
62
|
// #region Check AE Range
|
|
63
63
|
@DBC.ParamvalueProvider
|
|
64
64
|
public testAERange(
|
|
65
65
|
@AE.PRE([new TYPE("string"), new REGEX(/^abc$/)], 1, 2) x: Array<unknown>,
|
|
66
|
-
) {
|
|
66
|
+
) {}
|
|
67
67
|
// #endregion Check AE Range
|
|
68
68
|
// #region Check AE Index
|
|
69
69
|
@DBC.ParamvalueProvider
|
|
70
70
|
public testAEIndex(
|
|
71
71
|
@AE.PRE([new TYPE("string"), new REGEX(/^abc$/)], 1) x: Array<unknown>,
|
|
72
|
-
) {
|
|
72
|
+
) {}
|
|
73
73
|
// #endregion Check AE Index
|
|
74
74
|
// #region Check Comparison
|
|
75
75
|
@DBC.ParamvalueProvider
|
|
76
|
-
public testGREATER(@GREATER.PRE(2) input: number) {
|
|
76
|
+
public testGREATER(@GREATER.PRE(2) input: number) {}
|
|
77
77
|
|
|
78
78
|
@DBC.ParamvalueProvider
|
|
79
|
-
public testGREATER_OR_EQUAL(@GREATER_OR_EQUAL.PRE(2) input: number) {
|
|
79
|
+
public testGREATER_OR_EQUAL(@GREATER_OR_EQUAL.PRE(2) input: number) {}
|
|
80
80
|
|
|
81
81
|
@DBC.ParamvalueProvider
|
|
82
|
-
public testLESS(@LESS.PRE(20) input: number) {
|
|
82
|
+
public testLESS(@LESS.PRE(20) input: number) {}
|
|
83
83
|
|
|
84
84
|
@DBC.ParamvalueProvider
|
|
85
|
-
public testLESS_OR_EQUAL(@LESS_OR_EQUAL.PRE(20) input: number) {
|
|
85
|
+
public testLESS_OR_EQUAL(@LESS_OR_EQUAL.PRE(20) input: number) {}
|
|
86
86
|
|
|
87
87
|
@DBC.ParamvalueProvider
|
|
88
|
-
public testDIFFERENT(@DIFFERENT.PRE(20) input: number) {
|
|
88
|
+
public testDIFFERENT(@DIFFERENT.PRE(20) input: number) {}
|
|
89
89
|
// #endregion Check Comparison
|
|
90
90
|
|
|
91
91
|
// #region Check Static Method with ParamvalueProvider
|
|
92
92
|
@DBC.ParamvalueProvider
|
|
93
|
-
public static testStaticMethod(
|
|
93
|
+
public static testStaticMethod(
|
|
94
|
+
@TYPE.PRE("string") message: string,
|
|
95
|
+
@TYPE.PRE("number") count: number,
|
|
96
|
+
): string {
|
|
94
97
|
return `${message} repeated ${count} times`;
|
|
95
98
|
}
|
|
96
99
|
// #endregion Check Static Method with ParamvalueProvider
|
package/test.drawio
ADDED
|
File without changes
|
package/tsconfig.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"target": "ES6",
|
|
4
|
+
"declaration": true,
|
|
4
5
|
"experimentalDecorators": true,
|
|
5
6
|
"emitDecoratorMetadata": true,
|
|
6
7
|
"outDir": "./dist",
|
|
@@ -13,8 +14,5 @@
|
|
|
13
14
|
"include": [
|
|
14
15
|
"./src/**/*.ts" // or your source directories
|
|
15
16
|
],
|
|
16
|
-
"exclude": [
|
|
17
|
-
|
|
18
|
-
"__tests__"
|
|
19
|
-
]
|
|
20
|
-
}
|
|
17
|
+
"exclude": ["node_modules", "__tests__"]
|
|
18
|
+
}
|
package/tsconfig.test.json
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"include": [
|
|
9
|
-
"./src/**/*.ts",
|
|
10
|
-
"./__tests__/**/*.ts"
|
|
11
|
-
]
|
|
12
|
-
}
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"typeRoots": ["./node_modules/@types"]
|
|
5
|
+
},
|
|
6
|
+
"include": ["./src/**/*.ts", "./__tests__/**/*.ts"]
|
|
7
|
+
}
|
package/webpack.config.js
CHANGED