xdbc 1.0.217 → 1.0.219

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 (120) hide show
  1. package/.gitattributes +16 -8
  2. package/.vscode/settings.json +3 -3
  3. package/.vscode/tasks.json +23 -23
  4. package/ASSESSMENT.md +249 -0
  5. package/README.md +538 -408
  6. package/__tests__/DBC/AE.test.ts +62 -62
  7. package/__tests__/DBC/ARRAY.test.ts +91 -91
  8. package/__tests__/DBC/DEFINED.test.ts +53 -53
  9. package/__tests__/DBC/DOM.test.ts +786 -0
  10. package/__tests__/DBC/Decorators.test.ts +367 -367
  11. package/__tests__/DBC/EQ.test.ts +13 -13
  12. package/__tests__/DBC/GREATER.test.ts +31 -31
  13. package/__tests__/DBC/HasAttribute.test.ts +60 -60
  14. package/__tests__/DBC/IF.test.ts +62 -62
  15. package/__tests__/DBC/INSTANCE.test.ts +13 -13
  16. package/__tests__/DBC/JSON.OP.test.ts +47 -47
  17. package/__tests__/DBC/JSON.Parse.test.ts +17 -17
  18. package/__tests__/DBC/OR.test.ts +14 -14
  19. package/__tests__/DBC/PLAIN_OBJECT.test.ts +109 -109
  20. package/__tests__/DBC/REGEX.test.ts +17 -17
  21. package/__tests__/DBC/TYPE.test.ts +13 -13
  22. package/__tests__/DBC/UNDEFINED.test.ts +45 -45
  23. package/__tests__/DBC/ZOD.test.ts +54 -54
  24. package/__tests__/DBC/onInfringement.test.ts +262 -0
  25. package/biome.json +45 -40
  26. package/dist/DBC/AE.js +172 -0
  27. package/dist/DBC/ARR/PLAIN_OBJECT.d.ts +0 -3
  28. package/dist/DBC/ARR/PLAIN_OBJECT.js +95 -0
  29. package/dist/DBC/ARRAY.d.ts +0 -3
  30. package/dist/DBC/ARRAY.js +90 -0
  31. package/dist/DBC/COMPARISON/GREATER.js +21 -0
  32. package/dist/DBC/COMPARISON/GREATER_OR_EQUAL.js +21 -0
  33. package/dist/DBC/COMPARISON/LESS.js +21 -0
  34. package/dist/DBC/COMPARISON/LESS_OR_EQUAL.js +21 -0
  35. package/dist/DBC/COMPARISON.js +98 -0
  36. package/dist/DBC/DEFINED.js +87 -0
  37. package/dist/DBC/DOM.d.ts +123 -0
  38. package/dist/DBC/DOM.js +362 -0
  39. package/dist/DBC/EQ/DIFFERENT.js +34 -0
  40. package/dist/DBC/EQ.js +101 -0
  41. package/dist/DBC/GREATER.js +99 -0
  42. package/dist/DBC/HasAttribute.js +101 -0
  43. package/dist/DBC/IF.js +96 -0
  44. package/dist/DBC/INSTANCE.js +122 -0
  45. package/dist/DBC/JSON.OP.js +120 -0
  46. package/dist/DBC/JSON.Parse.js +104 -0
  47. package/dist/DBC/OR.js +125 -0
  48. package/dist/DBC/REGEX.js +136 -0
  49. package/dist/DBC/TYPE.js +112 -0
  50. package/dist/DBC/UNDEFINED.js +87 -0
  51. package/dist/DBC/ZOD.js +99 -0
  52. package/dist/DBC.d.ts +18 -4
  53. package/dist/DBC.js +645 -0
  54. package/dist/Demo.d.ts +10 -0
  55. package/dist/Demo.js +713 -0
  56. package/dist/Test.html +18 -0
  57. package/dist/bundle.js +6140 -405
  58. package/dist/index.d.ts +22 -0
  59. package/dist/index.html +18 -0
  60. package/dist/index.js +22 -0
  61. package/docs/assets/highlight.css +22 -22
  62. package/docs/assets/icons.js +17 -17
  63. package/docs/assets/main.js +60 -60
  64. package/docs/assets/style.css +1640 -1640
  65. package/docs/classes/DBC.DBC.html +98 -98
  66. package/docs/classes/DBC_AE.AE.html +160 -160
  67. package/docs/classes/DBC_EQ.EQ.html +131 -131
  68. package/docs/classes/DBC_GREATER.GREATER.html +139 -139
  69. package/docs/classes/DBC_INSTANCE.INSTANCE.html +130 -130
  70. package/docs/classes/DBC_JSON.OP.JSON_OP.html +138 -138
  71. package/docs/classes/DBC_JSON.Parse.JSON_Parse.html +129 -129
  72. package/docs/classes/DBC_OR.OR.html +137 -137
  73. package/docs/classes/DBC_REGEX.REGEX.html +136 -136
  74. package/docs/classes/DBC_TYPE.TYPE.html +130 -130
  75. package/docs/classes/Demo.Demo.html +14 -14
  76. package/docs/hierarchy.html +1 -1
  77. package/docs/index.html +1 -1
  78. package/docs/modules/DBC.html +1 -1
  79. package/docs/modules/DBC_AE.html +1 -1
  80. package/docs/modules/DBC_EQ.html +1 -1
  81. package/docs/modules/DBC_GREATER.html +1 -1
  82. package/docs/modules/DBC_INSTANCE.html +1 -1
  83. package/docs/modules/DBC_JSON.OP.html +1 -1
  84. package/docs/modules/DBC_JSON.Parse.html +1 -1
  85. package/docs/modules/DBC_OR.html +1 -1
  86. package/docs/modules/DBC_REGEX.html +1 -1
  87. package/docs/modules/DBC_TYPE.html +1 -1
  88. package/docs/modules/Demo.html +1 -1
  89. package/jest.config.js +32 -32
  90. package/package.json +71 -55
  91. package/src/DBC/AE.ts +269 -288
  92. package/src/DBC/ARR/PLAIN_OBJECT.ts +122 -133
  93. package/src/DBC/ARRAY.ts +117 -127
  94. package/src/DBC/COMPARISON/GREATER.ts +41 -46
  95. package/src/DBC/COMPARISON/GREATER_OR_EQUAL.ts +41 -45
  96. package/src/DBC/COMPARISON/LESS.ts +41 -45
  97. package/src/DBC/COMPARISON/LESS_OR_EQUAL.ts +41 -45
  98. package/src/DBC/COMPARISON.ts +149 -159
  99. package/src/DBC/DEFINED.ts +117 -122
  100. package/src/DBC/DOM.ts +453 -0
  101. package/src/DBC/EQ/DIFFERENT.ts +51 -57
  102. package/src/DBC/EQ.ts +154 -163
  103. package/src/DBC/HasAttribute.ts +149 -154
  104. package/src/DBC/IF.ts +173 -179
  105. package/src/DBC/INSTANCE.ts +168 -171
  106. package/src/DBC/JSON.OP.ts +178 -186
  107. package/src/DBC/JSON.Parse.ts +150 -157
  108. package/src/DBC/OR.ts +183 -187
  109. package/src/DBC/REGEX.ts +195 -196
  110. package/src/DBC/TYPE.ts +142 -149
  111. package/src/DBC/UNDEFINED.ts +115 -117
  112. package/src/DBC/ZOD.ts +130 -135
  113. package/src/DBC.ts +902 -904
  114. package/src/Demo.ts +537 -404
  115. package/src/index.ts +22 -0
  116. package/tsconfig.json +18 -18
  117. package/tsconfig.test.json +7 -7
  118. package/typedoc.json +16 -16
  119. package/webpack.config.js +27 -27
  120. package/Assessment.md +0 -507
package/dist/DBC.js ADDED
@@ -0,0 +1,645 @@
1
+ /**
2
+ * Provides a **D**esign **B**y **C**ontract Framework using decorators.
3
+ *
4
+ * @remarks
5
+ * Maintainer: Callari, Salvatore (XDBC@WaXCode.net) */
6
+ export class DBC {
7
+ /** Evicts the oldest entry if the cache exceeds the maximum size. */
8
+ static evictIfNeeded(cache) {
9
+ if (cache.size > DBC.MAX_CACHE_SIZE) {
10
+ const oldest = cache.keys().next().value;
11
+ if (oldest !== undefined)
12
+ cache.delete(oldest);
13
+ }
14
+ }
15
+ static getHost() {
16
+ return typeof window !== "undefined" ? window : globalThis;
17
+ }
18
+ static getDBC(dbc) {
19
+ if (dbc instanceof DBC)
20
+ return dbc;
21
+ const path = dbc !== null && dbc !== void 0 ? dbc : "WaXCode.DBC";
22
+ if (DBC.dbcCache.has(path)) {
23
+ return DBC.dbcCache.get(path);
24
+ }
25
+ const resolved = DBC.resolveDBCPath(DBC.getHost(), path);
26
+ if (resolved) {
27
+ DBC.evictIfNeeded(DBC.dbcCache);
28
+ DBC.dbcCache.set(path, resolved);
29
+ return resolved;
30
+ }
31
+ throw new Error(`[XDBC] DBC instance not found at path "${path}". Ensure a DBC instance is registered there.`);
32
+ }
33
+ /**
34
+ * Returns the registered {@link DBC } instance at the specified path.
35
+ * Throws if no instance is registered there.
36
+ *
37
+ * @param path The dotted path to look up (default: `"WaXCode.DBC"`). */
38
+ static getRegistered(path) {
39
+ return DBC.getDBC(path);
40
+ }
41
+ /**
42
+ * Generate a unique key for storing parameter value requests.
43
+ * Format: "ClassName:methodName"
44
+ */
45
+ static getRequestKey(target, methodName) {
46
+ var _a;
47
+ const className = typeof target === "function"
48
+ ? target.name
49
+ : ((_a = target.constructor) === null || _a === void 0 ? void 0 : _a.name) || "Unknown";
50
+ return `${className}:${String(methodName)}`;
51
+ }
52
+ /**
53
+ * Make a request to get the value of a certain parameter of specific method in a specific {@link object }.
54
+ * That request gets enlisted in {@link paramValueRequests } which is used by {@link ParamvalueProvider} to invoke the
55
+ * given "receptor" with the parameter value stored in there. Thus a parameter decorator using this method will
56
+ * not receive any value of the top method is not tagged with {@link ParamvalueProvider}.
57
+ *
58
+ * @param target The {@link object } containing the method with the parameter which's value is requested.
59
+ * @param methodName The name of the method with the parameter which's value is requested.
60
+ * @param index The index of the parameter which's value is requested.
61
+ * @param receptor The method the requested parameter-value shall be passed to when it becomes available. */
62
+ static requestParamValue(target, methodName, index, receptor) {
63
+ var _a;
64
+ const key = DBC.getRequestKey(target, methodName);
65
+ if (DBC.paramValueRequests.has(key)) {
66
+ const paramMap = DBC.paramValueRequests.get(key);
67
+ if (paramMap.has(index)) {
68
+ (_a = paramMap.get(index)) === null || _a === void 0 ? void 0 : _a.push(receptor);
69
+ }
70
+ else {
71
+ paramMap.set(index, new Array(receptor));
72
+ }
73
+ }
74
+ else {
75
+ DBC.paramValueRequests.set(key, new Map([
76
+ [index, new Array(receptor)],
77
+ ]));
78
+ }
79
+ return undefined;
80
+ }
81
+ static ParamvalueProvider(...args) {
82
+ if (args.length === 1 && typeof args[0] === "function") {
83
+ // #region Class decorator path
84
+ const ctor = args[0];
85
+ const key = `${ctor.name}:undefined`;
86
+ const WrappedClass = class extends ctor {
87
+ constructor(...ctorArgs) {
88
+ if (DBC.paramValueRequests.has(key)) {
89
+ const paramMap = DBC.paramValueRequests.get(key);
90
+ for (const index of paramMap.keys()) {
91
+ if (index < ctorArgs.length) {
92
+ for (const receptor of paramMap.get(index)) {
93
+ receptor(ctorArgs[index]);
94
+ }
95
+ }
96
+ }
97
+ }
98
+ super(...ctorArgs);
99
+ }
100
+ };
101
+ Object.defineProperty(WrappedClass, "name", { value: ctor.name });
102
+ return WrappedClass;
103
+ // #endregion Class decorator path
104
+ }
105
+ // #region Method decorator path
106
+ const [target, propertyKey, descriptor] = args;
107
+ const originalMethod = descriptor.value;
108
+ const isStatic = typeof target === "function";
109
+ descriptor.value = function (...methodArgs) {
110
+ // #region Check if a value of one of the method's parameter has been requested and pass it to the
111
+ // receptor, if so.
112
+ const actualTarget = isStatic ? this : this.constructor;
113
+ const key = DBC.getRequestKey(actualTarget, propertyKey);
114
+ if (DBC.paramValueRequests.has(key)) {
115
+ const paramMap = DBC.paramValueRequests.get(key);
116
+ for (const index of paramMap.keys()) {
117
+ if (index < methodArgs.length) {
118
+ for (const receptor of paramMap.get(index)) {
119
+ receptor(methodArgs[index]);
120
+ }
121
+ }
122
+ }
123
+ }
124
+ else {
125
+ console.warn("No parameter value requests found for key:", key);
126
+ }
127
+ // #endregion Check if a value of one of the method's parameter has been requested and pass it to the
128
+ // receptor, if so.
129
+ return originalMethod.apply(this, methodArgs);
130
+ };
131
+ return descriptor;
132
+ // #endregion Method decorator path
133
+ }
134
+ // #endregion Parameter-value requests.
135
+ // #region Class
136
+ /**
137
+ * A property-decorator factory serving as a **D**esign **B**y **C**ontract Invariant.
138
+ * This invariant aims to check the instance of the class not the value to be get or set.
139
+ *
140
+ * @param contracts The {@link DBC }-Contracts the value shall uphold.
141
+ *
142
+ * @throws A {@link DBC.Infringement } whenever the property is tried to be get or set without the instance of it's class
143
+ * fulfilling the specified **contracts**. */
144
+ static decClassInvariant(contracts, path = undefined, dbc = "WaXCode.DBC") {
145
+ let dbcInstance;
146
+ return (target, propertyKey, descriptor) => {
147
+ if (!dbcInstance)
148
+ dbcInstance = DBC.getDBC(dbc);
149
+ if (!dbcInstance.executionSettings.checkInvariants) {
150
+ return;
151
+ }
152
+ const originalSetter = descriptor.set;
153
+ const originalGetter = descriptor.get;
154
+ let value;
155
+ // #region Replace original property.
156
+ Object.defineProperty(target, propertyKey, {
157
+ get() {
158
+ if (!(dbcInstance === null || dbcInstance === void 0 ? void 0 : dbcInstance.executionSettings.checkInvariants)) {
159
+ return;
160
+ }
161
+ const realValue = path ? DBC.resolve(this, path) : this;
162
+ // #region Check if all "contracts" are fulfilled.
163
+ for (const contract of contracts) {
164
+ const result = contract.check(realValue);
165
+ if (typeof result === "string") {
166
+ dbcInstance === null || dbcInstance === void 0 ? void 0 : dbcInstance.reportFieldInfringement(result, target, path, propertyKey, realValue);
167
+ }
168
+ }
169
+ // #endregion Check if all "contracts" are fulfilled.
170
+ return originalGetter ? originalGetter[propertyKey] : value;
171
+ },
172
+ set(newValue) {
173
+ if (!(dbcInstance === null || dbcInstance === void 0 ? void 0 : dbcInstance.executionSettings.checkInvariants)) {
174
+ return;
175
+ }
176
+ const realValue = path ? DBC.resolve(this, path) : this;
177
+ // #region Check if all "contracts" are fulfilled.
178
+ for (const contract of contracts) {
179
+ const result = contract.check(realValue);
180
+ if (typeof result === "string") {
181
+ dbcInstance === null || dbcInstance === void 0 ? void 0 : dbcInstance.reportFieldInfringement(result, target, path, propertyKey, realValue);
182
+ }
183
+ }
184
+ // #endregion Check if all "contracts" are fulfilled.
185
+ value = newValue;
186
+ },
187
+ enumerable: true,
188
+ configurable: true,
189
+ });
190
+ // #endregion Replace original property.
191
+ };
192
+ }
193
+ // #endregion Class
194
+ // #region Invariant
195
+ /**
196
+ * A property-decorator factory serving as a **D**esign **B**y **C**ontract Invariant.
197
+ * Since the value must be initialized or set according to the specified **contracts** the value will only be checked
198
+ * when assigning it.
199
+ *
200
+ * @param contracts The {@link DBC }-Contracts the value shall uphold.
201
+ *
202
+ * @throws A {@link DBC.Infringement } whenever the property is tried to be set to a value that does not comply to the
203
+ * specified **contracts**, by the returned method.*/
204
+ static decInvariant(contracts, path = undefined, dbc = undefined, hint = undefined) {
205
+ let dbcInstance;
206
+ return (target, propertyKey) => {
207
+ if (!dbcInstance)
208
+ dbcInstance = DBC.getDBC(dbc);
209
+ if (!dbcInstance.executionSettings.checkInvariants) {
210
+ return;
211
+ }
212
+ let value;
213
+ // #region Replace original property.
214
+ Object.defineProperty(target, propertyKey, {
215
+ set(newValue) {
216
+ if (!(dbcInstance === null || dbcInstance === void 0 ? void 0 : dbcInstance.executionSettings.checkInvariants)) {
217
+ return;
218
+ }
219
+ const realValue = path ? DBC.resolve(newValue, path) : newValue;
220
+ // #region Check if all "contracts" are fulfilled.
221
+ for (const contract of contracts) {
222
+ const result = contract.check(realValue);
223
+ if (typeof result === "string") {
224
+ dbcInstance === null || dbcInstance === void 0 ? void 0 : dbcInstance.reportFieldInfringement(result, target, path, propertyKey, realValue, hint);
225
+ }
226
+ }
227
+ // #endregion Check if all "contracts" are fulfilled.
228
+ value = newValue;
229
+ },
230
+ enumerable: true,
231
+ configurable: true,
232
+ });
233
+ // #endregion Replace original property.
234
+ };
235
+ }
236
+ // #endregion Invariant
237
+ // #region Postcondition
238
+ /**
239
+ * A method decorator factory checking the result of a method whenever it is invoked thus also usable on getters.
240
+ *
241
+ * @param check The **(toCheck: any, object, string) => boolean | string** to use for checking.
242
+ * @param dbc See {@link DBC.resolveDBCPath }.
243
+ * @param path The dotted path referring to the actual value to check, starting form the specified one.
244
+ *
245
+ * @returns The **( target : object, propertyKey : string, descriptor : PropertyDescriptor ) : PropertyDescriptor**
246
+ * invoked by Typescript.
247
+ */
248
+ static decPostcondition(check, dbc = undefined, path = undefined, hint = undefined) {
249
+ let dbcInstance;
250
+ return (target, propertyKey, descriptor) => {
251
+ const originalMethod = descriptor.value;
252
+ descriptor.value = (...args) => {
253
+ if (!dbcInstance)
254
+ dbcInstance = DBC.getDBC(dbc);
255
+ if (!dbcInstance.executionSettings.checkPostconditions) {
256
+ return;
257
+ }
258
+ // biome-ignore lint/complexity/noThisInStatic: <explanation>
259
+ const result = originalMethod.apply(this, args);
260
+ const realValue = path ? DBC.resolve(result, path) : result;
261
+ const checkResult = check(realValue, target, propertyKey);
262
+ if (typeof checkResult === "string") {
263
+ dbcInstance.reportReturnvalueInfringement(checkResult, target, path, propertyKey, realValue, hint);
264
+ }
265
+ return result;
266
+ };
267
+ return descriptor;
268
+ };
269
+ }
270
+ // #endregion Postcondition
271
+ // #region Decorator
272
+ // #region Precondition
273
+ /**
274
+ * A parameter-decorator factory that requests the tagged parameter's value passing it to the provided
275
+ * "check"-method when the value becomes available.
276
+ *
277
+ * @param check The "( unknown ) => void" to be invoked along with the tagged parameter's value as soon
278
+ * as it becomes available.
279
+ * @param dbc See {@link DBC.resolveDBCPath }.
280
+ * @param path The dotted path referring to the actual value to check, starting form the specified one.
281
+ * May contain :: to separate multiple paths.
282
+ *
283
+ * @returns The **(target: object, methodName: string | symbol, parameterIndex: number ) => void** invoked by Typescript- */
284
+ static decPrecondition(check, dbc = undefined, path = undefined, hint = undefined) {
285
+ const paths = path ? path.replace(/ /g, "").split("::") : [undefined];
286
+ let dbcInstance;
287
+ return (target, methodName, parameterIndex) => {
288
+ DBC.requestParamValue(target, methodName, parameterIndex, (value) => {
289
+ if (!dbcInstance)
290
+ dbcInstance = DBC.getDBC(dbc);
291
+ if (!dbcInstance.executionSettings.checkPreconditions) {
292
+ return;
293
+ }
294
+ for (const singlePath of paths) {
295
+ const realValue = singlePath
296
+ ? DBC.resolve(value, singlePath)
297
+ : value;
298
+ const result = check(realValue, target, methodName, parameterIndex);
299
+ if (typeof result === "string") {
300
+ dbcInstance.reportParameterInfringement(result, target, singlePath, methodName, parameterIndex, realValue, hint);
301
+ }
302
+ }
303
+ });
304
+ };
305
+ }
306
+ // #endregion Precondition
307
+ // #endregion Decorator
308
+ // #region Contract Factory Helpers
309
+ /**
310
+ * Creates a PRE decorator from a checkAlgorithm function and its bound arguments.
311
+ * Reduces boilerplate across contract classes.
312
+ *
313
+ * @param checkFn A function that takes (value, ...boundArgs) and returns true or an error string.
314
+ * @param boundArgs The arguments to bind to the check function after the value.
315
+ * @param dbc See {@link DBC.decPrecondition}.
316
+ * @param path See {@link DBC.decPrecondition}.
317
+ * @param hint See {@link DBC.decPrecondition}.
318
+ */
319
+ static createPRE(checkFn, boundArgs, dbc, path, hint) {
320
+ return DBC.decPrecondition((value, _target, _methodName, _parameterIndex) => {
321
+ return checkFn(value, ...boundArgs);
322
+ }, dbc, path, hint);
323
+ }
324
+ /**
325
+ * Creates a POST decorator from a checkAlgorithm function and its bound arguments.
326
+ *
327
+ * @param checkFn A function that takes (value, ...boundArgs) and returns true or an error string.
328
+ * @param boundArgs The arguments to bind to the check function after the value.
329
+ * @param dbc See {@link DBC.decPostcondition}.
330
+ * @param path See {@link DBC.decPostcondition}.
331
+ * @param hint See {@link DBC.decPostcondition}.
332
+ */
333
+ static createPOST(checkFn, boundArgs, dbc, path, hint) {
334
+ return DBC.decPostcondition((value, _target, _propertyKey) => {
335
+ return checkFn(value, ...boundArgs);
336
+ }, dbc, path, hint);
337
+ }
338
+ /**
339
+ * Creates an INVARIANT decorator from a contract constructor and its bound arguments.
340
+ *
341
+ * @param ContractClass A class with a constructor that produces an object with a `check` method.
342
+ * @param ctorArgs The arguments to pass to the contract constructor.
343
+ * @param dbc See {@link DBC.decInvariant}.
344
+ * @param path See {@link DBC.decInvariant}.
345
+ * @param hint See {@link DBC.decInvariant}.
346
+ */
347
+ static createINVARIANT(ContractClass, ctorArgs, dbc, path, hint) {
348
+ return DBC.decInvariant([new ContractClass(...ctorArgs)], path, dbc, hint);
349
+ }
350
+ /**
351
+ * Reports a warning.
352
+ *
353
+ * @param message The message containing the warning. */
354
+ reportWarning(message) {
355
+ if (this.warningSettings.logToConsole) {
356
+ console.warn(message);
357
+ }
358
+ }
359
+ /** Sanitizes a value for safe inclusion in error messages. */
360
+ static sanitize(value) {
361
+ const str = typeof value === "string" ? value : String(value);
362
+ return str.replace(/[<>&"']/g, (ch) => {
363
+ switch (ch) {
364
+ case "<":
365
+ return "&lt;";
366
+ case ">":
367
+ return "&gt;";
368
+ case "&":
369
+ return "&amp;";
370
+ case '"':
371
+ return "&quot;";
372
+ case "'":
373
+ return "&#39;";
374
+ default:
375
+ return ch;
376
+ }
377
+ });
378
+ }
379
+ /**
380
+ * Reports an infringement according to the {@link infringementSettings } also generating a proper {@link string }-wrapper
381
+ * for the given "message" & violator.
382
+ *
383
+ * @param message The {@link string } describing the infringement and it's provenience.
384
+ * @param violator The {@link string } describing or naming the violator. */
385
+ reportInfringement(message, violator, target, value, path, hint = undefined, type = "precondition") {
386
+ const safeViolator = DBC.sanitize(violator);
387
+ const targetName = typeof target === "function"
388
+ ? DBC.sanitize(target.name)
389
+ : typeof target === "object" &&
390
+ target !== null &&
391
+ typeof target.constructor === "function"
392
+ ? DBC.sanitize(target.constructor.name)
393
+ : DBC.sanitize(target);
394
+ const finalMessage = `[ From "${safeViolator}" in "${targetName}"${path ? ` > "${DBC.sanitize(path)}"` : ""}: ${message} ${hint ? `✨ ${hint} ✨` : ""}]`;
395
+ const infringement = new DBC.Infringement(finalMessage);
396
+ if (this.infringementSettings.logToConsole) {
397
+ console.log(finalMessage);
398
+ }
399
+ if (this.infringementSettings.onInfringement) {
400
+ this.infringementSettings.onInfringement(infringement, { type, value });
401
+ }
402
+ if (this.infringementSettings.throwException) {
403
+ throw infringement;
404
+ }
405
+ }
406
+ /**
407
+ * Reports a parameter-infringement via {@link reportInfringement } also generating a proper {@link string }-wrapper
408
+ * for the given "message","method", parameter-"index" & value.
409
+ *
410
+ * @param message The {@link string } describing the infringement and it's provenience.
411
+ * @param method The {@link string } describing or naming the violator.
412
+ * @param index The index of the parameter within the argument listing.
413
+ * @param value The parameter's value. */
414
+ reportParameterInfringement(message, target, path, method, index, value, hint = undefined) {
415
+ const properIndex = index + 1;
416
+ this.reportInfringement(`[ Parameter-value "${value}" of the ${properIndex}${properIndex === 1 ? "st" : properIndex === 2 ? "nd" : properIndex === 3 ? "rd" : "th"} parameter did not fulfill one of it's contracts: ${message} ]`, method, target, value, path, hint, "precondition");
417
+ }
418
+ /**
419
+ * Reports a field-infringement via {@link reportInfringement } also generating a proper {@link string }-wrapper
420
+ * for the given **message** & **name**.
421
+ *
422
+ * @param message A {@link string } describing the infringement and it's provenience.
423
+ * @param key The property key.
424
+ * @param path The dotted-path {@link string } that leads to the value not fulfilling the contract starting from
425
+ * the tagged one.
426
+ * @param value The value not fulfilling a contract. */
427
+ reportFieldInfringement(message, target, path, key, value, hint = undefined) {
428
+ this.reportInfringement(`[ New value for "${key}"${path === undefined ? "" : `.${path}`} with value "${value}" did not fulfill one of it's contracts: ${message} ]`, key, target, value, path, hint, "invariant");
429
+ }
430
+ /**
431
+ * Reports a returnvalue-infringement according via {@link reportInfringement } also generating a proper {@link string }-wrapper
432
+ * for the given "message","method" & value.
433
+ *
434
+ * @param message The {@link string } describing the infringement and it's provenience.
435
+ * @param method The {@link string } describing or naming the violator.
436
+ * @param value The parameter's value. */
437
+ reportReturnvalueInfringement(message, target, path, method, value, hint = undefined) {
438
+ this.reportInfringement(`[ Return-value "${value}" did not fulfill one of it's contracts: ${message} ]`, method, target, value, path, hint, "postcondition");
439
+ }
440
+ /**
441
+ * Routes an imperative infringement (from {@link tsCheck } or static {@link check } calls) through the
442
+ * registered DBC instance's {@link infringementSettings }, respecting whether to throw, log, or both.
443
+ * Falls back to throwing {@link DBC.Infringement } directly if no DBC instance is registered at the
444
+ * specified path, preserving the pre-existing behaviour for code that does not register a DBC instance.
445
+ *
446
+ * @param message The fully formatted infringement message.
447
+ * @param dbc The path to the DBC instance. Defaults to `"WaXCode.DBC"`. */
448
+ static reportTsCheckInfringement(message, dbc = undefined, value = undefined) {
449
+ const infringement = new DBC.Infringement(message);
450
+ let dbcInstance;
451
+ try {
452
+ dbcInstance = DBC.getDBC(dbc);
453
+ }
454
+ catch (_a) {
455
+ throw infringement;
456
+ }
457
+ if (dbcInstance.infringementSettings.logToConsole) {
458
+ console.log(message);
459
+ }
460
+ if (dbcInstance.infringementSettings.onInfringement) {
461
+ dbcInstance.infringementSettings.onInfringement(infringement, {
462
+ type: "precondition",
463
+ value,
464
+ });
465
+ }
466
+ if (dbcInstance.infringementSettings.throwException) {
467
+ throw infringement;
468
+ }
469
+ }
470
+ /**
471
+ * Constructs this {@link DBC } without mounting it on the global namespace.
472
+ * Use {@link DBC.register } to make the instance available at a specific path on globalThis.
473
+ *
474
+ * @param infringementSettings See {@link DBC.infringementSettings }.
475
+ * @param executionSettings See {@link DBC.executionSettings }. */
476
+ constructor(infringementSettings = { throwException: true, logToConsole: false }, executionSettings = {
477
+ checkPreconditions: true,
478
+ checkPostconditions: true,
479
+ checkInvariants: true,
480
+ }) {
481
+ var _a;
482
+ // #endregion Contract Factory Helpers
483
+ // #region Execution Handling
484
+ /** Stores settings concerning the execution of checks. */
485
+ this.executionSettings = {
486
+ checkPreconditions: true,
487
+ checkPostconditions: true,
488
+ checkInvariants: true,
489
+ };
490
+ // #endregion Execution Handling
491
+ // #region Warning handling.
492
+ /** Stores settings concerning warnings. */
493
+ this.warningSettings = { logToConsole: true };
494
+ // #endregion Warning handling.
495
+ // #region infringement handling.
496
+ /** Stores the settings concerning infringements */
497
+ this.infringementSettings = { throwException: true, logToConsole: false, onInfringement: undefined };
498
+ this.infringementSettings = Object.assign(Object.assign({}, infringementSettings), { onInfringement: (_a = infringementSettings.onInfringement) !== null && _a !== void 0 ? _a : undefined });
499
+ this.executionSettings = executionSettings;
500
+ }
501
+ /**
502
+ * Registers a {@link DBC } instance at the specified dotted path on globalThis (or window),
503
+ * making it available for decorator resolution via string paths.
504
+ *
505
+ * @param instance The {@link DBC } instance to register.
506
+ * @param path The dotted path to register at (default: `"WaXCode.DBC"`). */
507
+ static register(instance, path = "WaXCode.DBC") {
508
+ const segments = path.split(".");
509
+ let obj = DBC.getHost();
510
+ for (let i = 0; i < segments.length - 1; i++) {
511
+ if (obj[segments[i]] === undefined)
512
+ obj[segments[i]] = {};
513
+ obj = obj[segments[i]];
514
+ }
515
+ obj[segments[segments.length - 1]] = instance;
516
+ DBC.dbcCache.set(path, instance);
517
+ }
518
+ /**
519
+ * Executes a callback with an isolated {@link DBC } instance temporarily registered at the default path.
520
+ * The previous instance (if any) is restored after the callback completes — even if it throws.
521
+ * Useful for test isolation.
522
+ *
523
+ * @param fn The callback receiving the isolated {@link DBC } instance. */
524
+ static isolated(fn) {
525
+ const saved = DBC.dbcCache.get("WaXCode.DBC");
526
+ const testDbc = new DBC();
527
+ DBC.register(testDbc);
528
+ try {
529
+ fn(testDbc);
530
+ }
531
+ finally {
532
+ if (saved) {
533
+ DBC.register(saved);
534
+ }
535
+ else {
536
+ DBC.dbcCache.delete("WaXCode.DBC");
537
+ }
538
+ }
539
+ }
540
+ /**
541
+ * Resolves the desired {@link object } out a given one **toResolveFrom** using the specified **path**.
542
+ *
543
+ * @param toResolveFrom The {@link object } starting to resolve from.
544
+ * @param path The dotted path-{@link string }.
545
+ * This string uses ., [...], and () to represent accessing nested properties,
546
+ * array elements/object keys, and calling methods, respectively, mimicking JavaScript syntax to navigate
547
+ * an object's structure. Code, e.g. something like a.b( 1 as number ).c, will not be executed and
548
+ * thus make the retrieval fail.
549
+ *
550
+ * @returns The requested {@link object }, NULL or UNDEFINED. */
551
+ static resolve(toResolveFrom, path) {
552
+ if (!toResolveFrom || typeof path !== "string") {
553
+ return undefined;
554
+ }
555
+ // Security: block prototype pollution paths
556
+ const dangerousTokens = ["__proto__", "constructor", "prototype"];
557
+ const cachedParts = DBC.pathTokenCache.get(path);
558
+ const parts = cachedParts !== null && cachedParts !== void 0 ? cachedParts : path.replace(/\[(['"]?)(.*?)\1\]/g, ".$2").split(".");
559
+ if (!cachedParts) {
560
+ // Validate tokens before caching
561
+ for (const part of parts) {
562
+ const tokenName = part.replace(/\(.*\)$/, "");
563
+ if (dangerousTokens.indexOf(tokenName) >= 0) {
564
+ throw new Error(`[XDBC] Path "${path}" contains forbidden token "${tokenName}".`);
565
+ }
566
+ }
567
+ DBC.evictIfNeeded(DBC.pathTokenCache);
568
+ DBC.pathTokenCache.set(path, parts);
569
+ }
570
+ let current = toResolveFrom;
571
+ for (const part of parts) {
572
+ if (current === null || typeof current === "undefined") {
573
+ return undefined;
574
+ }
575
+ const methodMatch = part.match(/(\w+)\((.*)\)/);
576
+ if (methodMatch) {
577
+ const methodName = methodMatch[1];
578
+ const argsStr = methodMatch[2];
579
+ const args = argsStr.split(",").map((arg) => arg.trim());
580
+ if (typeof current[methodName] === "function") {
581
+ current = current[methodName].apply(current, args);
582
+ }
583
+ else {
584
+ return undefined;
585
+ }
586
+ }
587
+ else {
588
+ if (typeof window !== "undefined" &&
589
+ typeof HTMLElement !== "undefined" &&
590
+ current instanceof HTMLElement &&
591
+ part.startsWith("@")) {
592
+ current = current.getAttribute(part.slice(1));
593
+ }
594
+ else if (typeof current === "object" &&
595
+ current !== null &&
596
+ part in current) {
597
+ current = current[part];
598
+ }
599
+ else if (typeof window !== "undefined" &&
600
+ typeof HTMLElement !== "undefined" &&
601
+ current instanceof HTMLElement) {
602
+ current = undefined;
603
+ }
604
+ else {
605
+ current = undefined;
606
+ }
607
+ }
608
+ }
609
+ return current;
610
+ }
611
+ }
612
+ // #region Internal caches.
613
+ DBC.MAX_CACHE_SIZE = 1000;
614
+ DBC.dbcCache = new Map();
615
+ DBC.pathTokenCache = new Map();
616
+ // #endregion Internal caches.
617
+ // #region Parameter-value requests.
618
+ /** Stores all request for parameter values registered by {@link decPrecondition }. */
619
+ DBC.paramValueRequests = new Map();
620
+ // #region Classes
621
+ // #region Errors
622
+ /** An {@link Error } to be thrown whenever an infringement is detected. */
623
+ DBC.Infringement = class extends Error {
624
+ /**
625
+ * Constructs this {@link Error } by tagging the specified message-{@link string } as an XDBC-Infringement.
626
+ *
627
+ * @param message The {@link string } describing the infringement. */
628
+ constructor(message) {
629
+ super(`[ XDBC Infringement ${message}]`);
630
+ }
631
+ };
632
+ // #endregion Errors
633
+ // #endregion Classes
634
+ // #endregion infringement handling.
635
+ /**
636
+ * Resolves the specified dotted {@link string }-path to a {@link DBC }.
637
+ *
638
+ * @param obj The {@link object } to start resolving from.
639
+ * @param path The dotted {@link string }-path leading to the {@link DBC }.
640
+ *
641
+ * @returns The requested {@link DBC }.
642
+ */
643
+ DBC.resolveDBCPath = (obj, path) => path === null || path === void 0 ? void 0 : path.split(".").reduce((accumulator, current) => accumulator[current], obj);
644
+ // Register the default instance with standard settings.
645
+ DBC.register(new DBC());
package/dist/Demo.d.ts CHANGED
@@ -16,5 +16,15 @@ export declare class Demo {
16
16
  testLESS(input: number): void;
17
17
  testLESS_OR_EQUAL(input: number): void;
18
18
  testDIFFERENT(input: number): void;
19
+ testARRAY(x: unknown): void;
20
+ testPLAIN_OBJECT(x: unknown): void;
21
+ testDEFINED(x: unknown): void;
22
+ testUNDEFINED(x: unknown): void;
23
+ testHasAttribute(el: HTMLElement): void;
24
+ testIF(x: unknown): void;
25
+ testOR(x: unknown): void;
26
+ testJSON_OP(x: unknown): void;
27
+ testJSON_Parse(x: string): void;
28
+ testZOD(x: unknown): void;
19
29
  static testStaticMethod(message: string, count: number): string;
20
30
  }