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.
- package/.gitattributes +16 -8
- package/.vscode/settings.json +3 -3
- package/.vscode/tasks.json +23 -23
- package/ASSESSMENT.md +249 -0
- package/README.md +538 -408
- package/__tests__/DBC/AE.test.ts +62 -62
- package/__tests__/DBC/ARRAY.test.ts +91 -91
- package/__tests__/DBC/DEFINED.test.ts +53 -53
- package/__tests__/DBC/DOM.test.ts +786 -0
- package/__tests__/DBC/Decorators.test.ts +367 -367
- package/__tests__/DBC/EQ.test.ts +13 -13
- package/__tests__/DBC/GREATER.test.ts +31 -31
- package/__tests__/DBC/HasAttribute.test.ts +60 -60
- package/__tests__/DBC/IF.test.ts +62 -62
- package/__tests__/DBC/INSTANCE.test.ts +13 -13
- package/__tests__/DBC/JSON.OP.test.ts +47 -47
- package/__tests__/DBC/JSON.Parse.test.ts +17 -17
- package/__tests__/DBC/OR.test.ts +14 -14
- package/__tests__/DBC/PLAIN_OBJECT.test.ts +109 -109
- package/__tests__/DBC/REGEX.test.ts +17 -17
- package/__tests__/DBC/TYPE.test.ts +13 -13
- package/__tests__/DBC/UNDEFINED.test.ts +45 -45
- package/__tests__/DBC/ZOD.test.ts +54 -54
- package/__tests__/DBC/onInfringement.test.ts +262 -0
- package/biome.json +45 -40
- package/dist/DBC/AE.js +172 -0
- package/dist/DBC/ARR/PLAIN_OBJECT.d.ts +0 -3
- package/dist/DBC/ARR/PLAIN_OBJECT.js +95 -0
- package/dist/DBC/ARRAY.d.ts +0 -3
- package/dist/DBC/ARRAY.js +90 -0
- package/dist/DBC/COMPARISON/GREATER.js +21 -0
- package/dist/DBC/COMPARISON/GREATER_OR_EQUAL.js +21 -0
- package/dist/DBC/COMPARISON/LESS.js +21 -0
- package/dist/DBC/COMPARISON/LESS_OR_EQUAL.js +21 -0
- package/dist/DBC/COMPARISON.js +98 -0
- package/dist/DBC/DEFINED.js +87 -0
- package/dist/DBC/DOM.d.ts +123 -0
- package/dist/DBC/DOM.js +362 -0
- package/dist/DBC/EQ/DIFFERENT.js +34 -0
- package/dist/DBC/EQ.js +101 -0
- package/dist/DBC/GREATER.js +99 -0
- package/dist/DBC/HasAttribute.js +101 -0
- package/dist/DBC/IF.js +96 -0
- package/dist/DBC/INSTANCE.js +122 -0
- package/dist/DBC/JSON.OP.js +120 -0
- package/dist/DBC/JSON.Parse.js +104 -0
- package/dist/DBC/OR.js +125 -0
- package/dist/DBC/REGEX.js +136 -0
- package/dist/DBC/TYPE.js +112 -0
- package/dist/DBC/UNDEFINED.js +87 -0
- package/dist/DBC/ZOD.js +99 -0
- package/dist/DBC.d.ts +18 -4
- package/dist/DBC.js +645 -0
- package/dist/Demo.d.ts +10 -0
- package/dist/Demo.js +713 -0
- package/dist/Test.html +18 -0
- package/dist/bundle.js +6140 -405
- package/dist/index.d.ts +22 -0
- package/dist/index.html +18 -0
- package/dist/index.js +22 -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 +32 -32
- package/package.json +71 -55
- package/src/DBC/AE.ts +269 -288
- package/src/DBC/ARR/PLAIN_OBJECT.ts +122 -133
- package/src/DBC/ARRAY.ts +117 -127
- package/src/DBC/COMPARISON/GREATER.ts +41 -46
- package/src/DBC/COMPARISON/GREATER_OR_EQUAL.ts +41 -45
- package/src/DBC/COMPARISON/LESS.ts +41 -45
- package/src/DBC/COMPARISON/LESS_OR_EQUAL.ts +41 -45
- package/src/DBC/COMPARISON.ts +149 -159
- package/src/DBC/DEFINED.ts +117 -122
- package/src/DBC/DOM.ts +453 -0
- package/src/DBC/EQ/DIFFERENT.ts +51 -57
- package/src/DBC/EQ.ts +154 -163
- package/src/DBC/HasAttribute.ts +149 -154
- package/src/DBC/IF.ts +173 -179
- package/src/DBC/INSTANCE.ts +168 -171
- package/src/DBC/JSON.OP.ts +178 -186
- package/src/DBC/JSON.Parse.ts +150 -157
- package/src/DBC/OR.ts +183 -187
- package/src/DBC/REGEX.ts +195 -196
- package/src/DBC/TYPE.ts +142 -149
- package/src/DBC/UNDEFINED.ts +115 -117
- package/src/DBC/ZOD.ts +130 -135
- package/src/DBC.ts +902 -904
- package/src/Demo.ts +537 -404
- package/src/index.ts +22 -0
- package/tsconfig.json +18 -18
- package/tsconfig.test.json +7 -7
- package/typedoc.json +16 -16
- package/webpack.config.js +27 -27
- package/Assessment.md +0 -507
package/dist/DBC/DOM.js
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { DBC } from "../DBC";
|
|
2
|
+
import { COMPARISON } from "./COMPARISON";
|
|
3
|
+
import { DEFINED } from "./DEFINED";
|
|
4
|
+
import { EQ } from "./EQ";
|
|
5
|
+
import { REGEX } from "./REGEX";
|
|
6
|
+
import { TYPE } from "./TYPE";
|
|
7
|
+
import { UNDEFINED } from "./UNDEFINED";
|
|
8
|
+
// ─── Contract registry ────────────────────────────────────────────────────────
|
|
9
|
+
/**
|
|
10
|
+
* Maps a `data-xdbc-<key>` attribute suffix to a check function.
|
|
11
|
+
* Populated by {@link registerDOMContract } and the built-in defaults below.
|
|
12
|
+
*/
|
|
13
|
+
const registry = new Map();
|
|
14
|
+
/**
|
|
15
|
+
* Registers a check function for a `data-xdbc-<key>` attribute.
|
|
16
|
+
* Call this before {@link scanDOM } to make a contract available declaratively.
|
|
17
|
+
*
|
|
18
|
+
* @param key The attribute suffix (e.g. `"type"` → `data-xdbc-type`).
|
|
19
|
+
* @param checkFn Receives the live field value and the raw attribute string.
|
|
20
|
+
* Return `true` when the value is valid, or a `string` message when it is not.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { registerDOMContract } from "xdbc/DBC/DOM";
|
|
25
|
+
* import { MY_CONTRACT } from "./MY_CONTRACT";
|
|
26
|
+
*
|
|
27
|
+
* registerDOMContract("my-contract", (value, attr) =>
|
|
28
|
+
* MY_CONTRACT.checkAlgorithm(value, attr),
|
|
29
|
+
* );
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export function registerDOMContract(key, checkFn) {
|
|
33
|
+
registry.set(key, checkFn);
|
|
34
|
+
}
|
|
35
|
+
// ─── Built-in registrations ───────────────────────────────────────────────────
|
|
36
|
+
// data-xdbc-regex="^\d*$"
|
|
37
|
+
registerDOMContract("regex", (value, attr) => {
|
|
38
|
+
let rx;
|
|
39
|
+
try {
|
|
40
|
+
rx = new RegExp(attr);
|
|
41
|
+
}
|
|
42
|
+
catch (_a) {
|
|
43
|
+
return `[XDBC] Invalid RegExp pattern: "${attr}"`;
|
|
44
|
+
}
|
|
45
|
+
return REGEX.checkAlgorithm(value, rx);
|
|
46
|
+
});
|
|
47
|
+
// data-xdbc-regex-input="^\d*$" (always validated on every keystroke, regardless of data-xdbc-validate-on)
|
|
48
|
+
registerDOMContract("regex-input", (value, attr) => {
|
|
49
|
+
let rx;
|
|
50
|
+
try {
|
|
51
|
+
rx = new RegExp(attr);
|
|
52
|
+
}
|
|
53
|
+
catch (_a) {
|
|
54
|
+
return `[XDBC] Invalid RegExp pattern: "${attr}"`;
|
|
55
|
+
}
|
|
56
|
+
return REGEX.checkAlgorithm(value, rx);
|
|
57
|
+
});
|
|
58
|
+
// data-xdbc-type="string|number"
|
|
59
|
+
registerDOMContract("type", (value, attr) => TYPE.checkAlgorithm(value, attr));
|
|
60
|
+
registerDOMContract("type-input", (value, attr) => TYPE.checkAlgorithm(value, attr));
|
|
61
|
+
// data-xdbc-eq="hello"
|
|
62
|
+
registerDOMContract("eq", (value, attr) => EQ.checkAlgorithm(value, attr, false));
|
|
63
|
+
registerDOMContract("eq-input", (value, attr) => EQ.checkAlgorithm(value, attr, false));
|
|
64
|
+
// data-xdbc-different="forbidden"
|
|
65
|
+
registerDOMContract("different", (value, attr) => EQ.checkAlgorithm(value, attr, true));
|
|
66
|
+
registerDOMContract("different-input", (value, attr) => EQ.checkAlgorithm(value, attr, true));
|
|
67
|
+
// data-xdbc-defined (attribute presence is enough; value ignored)
|
|
68
|
+
registerDOMContract("defined", (value) => DEFINED.checkAlgorithm(value));
|
|
69
|
+
registerDOMContract("defined-input", (value) => DEFINED.checkAlgorithm(value));
|
|
70
|
+
// data-xdbc-undefined
|
|
71
|
+
registerDOMContract("undefined", (value) => UNDEFINED.checkAlgorithm(value));
|
|
72
|
+
registerDOMContract("undefined-input", (value) => UNDEFINED.checkAlgorithm(value));
|
|
73
|
+
// data-xdbc-greater="5"
|
|
74
|
+
registerDOMContract("greater", (value, attr) => COMPARISON.checkAlgorithm(Number(value), Number(attr), false, false));
|
|
75
|
+
registerDOMContract("greater-input", (value, attr) => COMPARISON.checkAlgorithm(Number(value), Number(attr), false, false));
|
|
76
|
+
// data-xdbc-greater-or-equal="5"
|
|
77
|
+
registerDOMContract("greater-or-equal", (value, attr) => COMPARISON.checkAlgorithm(Number(value), Number(attr), true, false));
|
|
78
|
+
registerDOMContract("greater-or-equal-input", (value, attr) => COMPARISON.checkAlgorithm(Number(value), Number(attr), true, false));
|
|
79
|
+
// data-xdbc-less="100"
|
|
80
|
+
registerDOMContract("less", (value, attr) => COMPARISON.checkAlgorithm(Number(value), Number(attr), false, true));
|
|
81
|
+
registerDOMContract("less-input", (value, attr) => COMPARISON.checkAlgorithm(Number(value), Number(attr), false, true));
|
|
82
|
+
// data-xdbc-less-or-equal="100"
|
|
83
|
+
registerDOMContract("less-or-equal", (value, attr) => COMPARISON.checkAlgorithm(Number(value), Number(attr), true, true));
|
|
84
|
+
registerDOMContract("less-or-equal-input", (value, attr) => COMPARISON.checkAlgorithm(Number(value), Number(attr), true, true));
|
|
85
|
+
// data-xdbc-or="regex:^\d+$;;type:string;;eq:42"
|
|
86
|
+
// Fragments are separated by ";;". Each fragment is "<contract-key>:<attr-value>",
|
|
87
|
+
// where the split is on the FIRST ":" only, so colons in the value (e.g. regex) are safe.
|
|
88
|
+
// The OR passes as long as at least one fragment passes.
|
|
89
|
+
registerDOMContract("or", (value, attr) => {
|
|
90
|
+
const fragments = attr.split(";;");
|
|
91
|
+
const messages = [];
|
|
92
|
+
for (const fragment of fragments) {
|
|
93
|
+
const colonIdx = fragment.indexOf(":");
|
|
94
|
+
const key = colonIdx === -1 ? fragment.trim() : fragment.slice(0, colonIdx).trim();
|
|
95
|
+
const fragAttr = colonIdx === -1 ? "" : fragment.slice(colonIdx + 1);
|
|
96
|
+
const checkFn = registry.get(key);
|
|
97
|
+
if (!checkFn) {
|
|
98
|
+
console.warn(`[XDBC] data-xdbc-or: unknown contract key "${key}"`);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const result = checkFn(value, fragAttr);
|
|
102
|
+
if (result === true)
|
|
103
|
+
return true; // short-circuit on first pass
|
|
104
|
+
if (typeof result === "string")
|
|
105
|
+
messages.push(result);
|
|
106
|
+
}
|
|
107
|
+
return messages.length > 0
|
|
108
|
+
? `Value did not satisfy any of: ${messages.join(" | ")}`
|
|
109
|
+
: true;
|
|
110
|
+
});
|
|
111
|
+
// data-xdbc-or-input (same fragment syntax as data-xdbc-or, always fires on every keystroke)
|
|
112
|
+
registerDOMContract("or-input", (value, attr) => {
|
|
113
|
+
const fragments = attr.split(";;");
|
|
114
|
+
const messages = [];
|
|
115
|
+
for (const fragment of fragments) {
|
|
116
|
+
const colonIdx = fragment.indexOf(":");
|
|
117
|
+
const key = colonIdx === -1 ? fragment.trim() : fragment.slice(0, colonIdx).trim();
|
|
118
|
+
const fragAttr = colonIdx === -1 ? "" : fragment.slice(colonIdx + 1);
|
|
119
|
+
const checkFn = registry.get(key);
|
|
120
|
+
if (!checkFn) {
|
|
121
|
+
console.warn(`[XDBC] data-xdbc-or-input: unknown contract key "${key}"`);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const result = checkFn(value, fragAttr);
|
|
125
|
+
if (result === true)
|
|
126
|
+
return true;
|
|
127
|
+
if (typeof result === "string")
|
|
128
|
+
messages.push(result);
|
|
129
|
+
}
|
|
130
|
+
return messages.length > 0
|
|
131
|
+
? `Value did not satisfy any of: ${messages.join(" | ")}`
|
|
132
|
+
: true;
|
|
133
|
+
});
|
|
134
|
+
// ─── scanDOM ──────────────────────────────────────────────────────────────────
|
|
135
|
+
/**
|
|
136
|
+
* Scans the given **root** for `<input>` and `<textarea>` elements marked with the
|
|
137
|
+
* `data-xdbc` attribute, and binds XDBC contracts to their DOM events (blur by
|
|
138
|
+
* default; configurable per element via `data-xdbc-validate-on`).
|
|
139
|
+
*
|
|
140
|
+
* ### Supported attributes
|
|
141
|
+
*
|
|
142
|
+
* | Attribute | Example value | Contract |
|
|
143
|
+
* |----------------------------|----------------------------|-----------------|
|
|
144
|
+
* | `data-xdbc` | *(path or empty)* | marker / DBC path |
|
|
145
|
+
* | `data-xdbc-validate-on` | `input` \| `blur` | when to validate default contracts (default: `blur`) |
|
|
146
|
+
* | `data-xdbc-regex` | `^[a-z]+\.[a-z]{2,}$` | {@link REGEX} — validated per `data-xdbc-validate-on` |
|
|
147
|
+
* | `data-xdbc-regex-input` | `^[a-zA-Z0-9.\-]*$` | {@link REGEX} — always on every keystroke |
|
|
148
|
+
* | `data-xdbc-type` | `string\|number` | {@link TYPE} |
|
|
149
|
+
* | `data-xdbc-type-input` | `string\|number` | {@link TYPE} — always on every keystroke |
|
|
150
|
+
* | `data-xdbc-eq` | `hello` | {@link EQ} |
|
|
151
|
+
* | `data-xdbc-eq-input` | `hello` | {@link EQ} — always on every keystroke |
|
|
152
|
+
* | `data-xdbc-different` | `forbidden` | {@link EQ} (inverted) |
|
|
153
|
+
* | `data-xdbc-different-input` | `forbidden` | {@link EQ} (inverted) — always on every keystroke |
|
|
154
|
+
* | `data-xdbc-defined` | *(no value needed)* | {@link DEFINED} |
|
|
155
|
+
* | `data-xdbc-defined-input` | *(no value needed)* | {@link DEFINED} — always on every keystroke |
|
|
156
|
+
* | `data-xdbc-undefined` | *(no value needed)* | {@link UNDEFINED} |
|
|
157
|
+
* | `data-xdbc-undefined-input` | *(no value needed)* | {@link UNDEFINED} — always on every keystroke |
|
|
158
|
+
* | `data-xdbc-greater` | `5` | {@link COMPARISON} |
|
|
159
|
+
* | `data-xdbc-greater-input` | `5` | {@link COMPARISON} — always on every keystroke |
|
|
160
|
+
* | `data-xdbc-greater-or-equal` | `5` | {@link COMPARISON} |
|
|
161
|
+
* | `data-xdbc-greater-or-equal-input` | `5` | {@link COMPARISON} — always on every keystroke |
|
|
162
|
+
* | `data-xdbc-less` | `100` | {@link COMPARISON} |
|
|
163
|
+
* | `data-xdbc-less-input` | `100` | {@link COMPARISON} — always on every keystroke |
|
|
164
|
+
* | `data-xdbc-less-or-equal` | `100` | {@link COMPARISON} |
|
|
165
|
+
* | `data-xdbc-less-or-equal-input` | `100` | {@link COMPARISON} — always on every keystroke |
|
|
166
|
+
* | `data-xdbc-or` | `regex:^\d+$;;type:string` | {@link OR} (fragment syntax) |
|
|
167
|
+
* | `data-xdbc-or-input` | `regex:^\d+$;;eq:N/A` | {@link OR} — always on every keystroke |
|
|
168
|
+
*
|
|
169
|
+
* Use {@link registerDOMContract } to add further contracts at any time.
|
|
170
|
+
*
|
|
171
|
+
* ### OR fragment syntax
|
|
172
|
+
*
|
|
173
|
+
* Fragments are separated by `;;`. Each fragment is `<contract-key>:<value>`, where the
|
|
174
|
+
* split is on the **first** `:` only, so colons inside values (e.g. regex) are safe.
|
|
175
|
+
* The OR passes as long as **at least one** fragment passes.
|
|
176
|
+
*
|
|
177
|
+
* ```html
|
|
178
|
+
* <!-- digits OR exactly "N/A" -->
|
|
179
|
+
* <input data-xdbc data-xdbc-or="regex:^\d+$;;eq:N/A" />
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* ### Behaviour on infringement
|
|
183
|
+
*
|
|
184
|
+
* 1. The element's value is **reverted** to the last accepted state (blocking the invalid input).
|
|
185
|
+
* 2. The configured DBC instance's infringement settings are honoured — `logToConsole`,
|
|
186
|
+
* `onInfringement`, and `throwException` all fire in the usual order. Any throw is caught
|
|
187
|
+
* internally so it cannot propagate out of the DOM event handler.
|
|
188
|
+
*
|
|
189
|
+
* ### IME / composition awareness
|
|
190
|
+
*
|
|
191
|
+
* Validation is suspended during IME composition (e.g. CJK input) and runs once after
|
|
192
|
+
* `compositionend`, so partially composed characters are never incorrectly rejected.
|
|
193
|
+
*
|
|
194
|
+
* @param root Element or Document to scan (default: `document`).
|
|
195
|
+
* @returns A cleanup function that removes all bound event listeners.
|
|
196
|
+
*
|
|
197
|
+
* ### Validate-on
|
|
198
|
+
*
|
|
199
|
+
* By default all contracts are validated when focus **leaves** the field (`blur`). This lets
|
|
200
|
+
* users type freely and only enforces the contract on completion. Set
|
|
201
|
+
* `data-xdbc-validate-on="input"` on an element to revert invalid input on every keystroke.
|
|
202
|
+
*
|
|
203
|
+
* ### Progressive + whole-value validation
|
|
204
|
+
*
|
|
205
|
+
* Use `data-xdbc-regex-input` alongside `data-xdbc-regex` to apply two different patterns:
|
|
206
|
+
* one that allows partial input during typing, and one that enforces the full format on blur.
|
|
207
|
+
* `data-xdbc-regex-input` always fires on every `input` event, regardless of
|
|
208
|
+
* `data-xdbc-validate-on`.
|
|
209
|
+
*
|
|
210
|
+
* ```html
|
|
211
|
+
* <!-- Only validate full domain format when leaving the field (default blur behaviour) -->
|
|
212
|
+
* <!-- While typing, only allow characters that could appear in a domain name -->
|
|
213
|
+
* <input type="text" data-xdbc
|
|
214
|
+
* data-xdbc-regex="^[a-zA-Z0-9][a-zA-Z0-9.\-]*\.[a-zA-Z]{2,}$"
|
|
215
|
+
* data-xdbc-regex-input="^[a-zA-Z0-9.\-]*$" />
|
|
216
|
+
*
|
|
217
|
+
* <!-- Block non-digit keystrokes in real time (explicit input-time validation) -->
|
|
218
|
+
* <input type="text" data-xdbc data-xdbc-validate-on="input" data-xdbc-regex="^\d*$" />
|
|
219
|
+
* ```
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```html
|
|
223
|
+
* <input type="text" data-xdbc data-xdbc-regex="^[a-zA-Z0-9.\-]*\.[a-zA-Z]{2,}$" />
|
|
224
|
+
* <input type="text" data-xdbc="MyApp.DBC" data-xdbc-type="string" data-xdbc-greater="0" />
|
|
225
|
+
* <input type="text" data-xdbc data-xdbc-or="regex:^\d+$;;eq:N/A" />
|
|
226
|
+
* <textarea data-xdbc data-xdbc-regex="^[\w\s]*$"></textarea>
|
|
227
|
+
* ```
|
|
228
|
+
* ```ts
|
|
229
|
+
* import { scanDOM, registerDOMContract } from "xdbc/DBC/DOM";
|
|
230
|
+
*
|
|
231
|
+
* const cleanup = scanDOM();
|
|
232
|
+
* // later:
|
|
233
|
+
* cleanup();
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
export function scanDOM(root = document) {
|
|
237
|
+
var _a, _b;
|
|
238
|
+
const bound = [];
|
|
239
|
+
const elements = Array.from(root.querySelectorAll("[data-xdbc]"));
|
|
240
|
+
for (const el of elements) {
|
|
241
|
+
const dbcPath = el.dataset.xdbc || "WaXCode.DBC";
|
|
242
|
+
// Collect contracts: keys ending in "-input" always fire on the input event;
|
|
243
|
+
// all others respect data-xdbc-validate-on (default: blur).
|
|
244
|
+
const checksDefault = [];
|
|
245
|
+
const checksInput = [];
|
|
246
|
+
for (const [key, checkFn] of registry) {
|
|
247
|
+
// dataset converts "xdbc-greater-or-equal" → "xdbcGreaterOrEqual" via camelCase.
|
|
248
|
+
// Build the camelCase key from the registry key.
|
|
249
|
+
const datasetKey = `xdbc${key
|
|
250
|
+
.split("-")
|
|
251
|
+
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
252
|
+
.join("")}`;
|
|
253
|
+
if (datasetKey in el.dataset) {
|
|
254
|
+
const entry = { checkFn, attrValue: (_a = el.dataset[datasetKey]) !== null && _a !== void 0 ? _a : "" };
|
|
255
|
+
if (key.endsWith("-input")) {
|
|
256
|
+
checksInput.push(entry);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
checksDefault.push(entry);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (checksDefault.length === 0 && checksInput.length === 0)
|
|
264
|
+
continue;
|
|
265
|
+
const validateOn = ((_b = el.dataset.xdbcValidateOn) !== null && _b !== void 0 ? _b : "blur") === "input" ? "input" : "blur";
|
|
266
|
+
// lastInputValid: revert target when a -input check fails on a keystroke.
|
|
267
|
+
// Updated whenever checksInput passes.
|
|
268
|
+
// lastBlurValid: revert target when a blur/default check fails.
|
|
269
|
+
// Updated only when all checks (checksInput + checksDefault) pass together.
|
|
270
|
+
let lastInputValid = el.value;
|
|
271
|
+
let lastBlurValid = el.value;
|
|
272
|
+
let composing = false;
|
|
273
|
+
// Runs checks against the current el.value.
|
|
274
|
+
// revertTo: the value to restore on failure (pass null to skip revert/report).
|
|
275
|
+
// Returns true when all checks pass.
|
|
276
|
+
const runChecks = (checksToRun, revertTo) => {
|
|
277
|
+
const value = el.value;
|
|
278
|
+
for (const { checkFn, attrValue } of checksToRun) {
|
|
279
|
+
const result = checkFn(value, attrValue);
|
|
280
|
+
if (typeof result === "string") {
|
|
281
|
+
if (revertTo !== null) {
|
|
282
|
+
el.value = revertTo;
|
|
283
|
+
try {
|
|
284
|
+
DBC.getRegistered(dbcPath).reportParameterInfringement(result, el, undefined, el.name || el.id || "input", 0, value);
|
|
285
|
+
}
|
|
286
|
+
catch (_a) {
|
|
287
|
+
// swallowed — throwException must not propagate out of a DOM event handler
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return true;
|
|
294
|
+
};
|
|
295
|
+
// checksInput always runs with revert + report.
|
|
296
|
+
// checksDefault:
|
|
297
|
+
// • When includeDefault is true (blur event or validateOn==="input"):
|
|
298
|
+
// runs with full revert + report; on pass commits both lastInputValid and lastBlurValid.
|
|
299
|
+
// • When includeDefault is false (input event, blur mode):
|
|
300
|
+
// silently probed (no revert, no report) to track the last "fully valid" state
|
|
301
|
+
// so the blur listener can always revert to a sensible value.
|
|
302
|
+
const doValidate = (includeDefault) => {
|
|
303
|
+
if (!runChecks(checksInput, lastInputValid))
|
|
304
|
+
return;
|
|
305
|
+
// checksInput passed → this is a safe keystroke revert target going forward
|
|
306
|
+
lastInputValid = el.value;
|
|
307
|
+
if (checksDefault.length === 0) {
|
|
308
|
+
lastBlurValid = el.value;
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (includeDefault) {
|
|
312
|
+
if (runChecks(checksDefault, lastBlurValid)) {
|
|
313
|
+
lastBlurValid = el.value;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
// Silent probe: let partial input through but track last fully-valid state
|
|
318
|
+
if (runChecks(checksDefault, null)) {
|
|
319
|
+
lastBlurValid = el.value;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
const inputListener = () => {
|
|
324
|
+
if (composing)
|
|
325
|
+
return;
|
|
326
|
+
doValidate(validateOn === "input");
|
|
327
|
+
};
|
|
328
|
+
const blurListener = checksDefault.length > 0 && validateOn === "blur"
|
|
329
|
+
? () => {
|
|
330
|
+
doValidate(true);
|
|
331
|
+
}
|
|
332
|
+
: null;
|
|
333
|
+
const compositionStartListener = () => {
|
|
334
|
+
composing = true;
|
|
335
|
+
};
|
|
336
|
+
const compositionEndListener = () => {
|
|
337
|
+
composing = false;
|
|
338
|
+
doValidate(validateOn === "input");
|
|
339
|
+
};
|
|
340
|
+
el.addEventListener("input", inputListener);
|
|
341
|
+
if (blurListener)
|
|
342
|
+
el.addEventListener("blur", blurListener);
|
|
343
|
+
el.addEventListener("compositionstart", compositionStartListener);
|
|
344
|
+
el.addEventListener("compositionend", compositionEndListener);
|
|
345
|
+
bound.push({
|
|
346
|
+
element: el,
|
|
347
|
+
inputListener,
|
|
348
|
+
blurListener,
|
|
349
|
+
compositionStartListener,
|
|
350
|
+
compositionEndListener,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
return () => {
|
|
354
|
+
for (const { element, inputListener, blurListener, compositionStartListener, compositionEndListener, } of bound) {
|
|
355
|
+
element.removeEventListener("input", inputListener);
|
|
356
|
+
if (blurListener)
|
|
357
|
+
element.removeEventListener("blur", blurListener);
|
|
358
|
+
element.removeEventListener("compositionstart", compositionStartListener);
|
|
359
|
+
element.removeEventListener("compositionend", compositionEndListener);
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { EQ } from "../EQ";
|
|
2
|
+
/**
|
|
3
|
+
* DIFFERENT class for inequality comparisons.
|
|
4
|
+
*
|
|
5
|
+
* This class extends EQ and provides methods to check if a value is different (not equal)
|
|
6
|
+
* from a specified equivalent value. It inverts the equality check by always passing
|
|
7
|
+
* `true` for the invert parameter to the parent EQ class methods.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* The class provides precondition (PRE), postcondition (POST), and invariant (INVARIANT)
|
|
11
|
+
* checks for Design by Contract programming patterns.
|
|
12
|
+
*
|
|
13
|
+
* @see {@link COMPARISON}
|
|
14
|
+
* @see {@link EQ}
|
|
15
|
+
*/
|
|
16
|
+
export class DIFFERENT extends EQ {
|
|
17
|
+
/** See {@link EQ.PRE }. Always inverts equality check. */
|
|
18
|
+
static PRE(equivalent, _invert = false, path = undefined, hint = undefined, dbc = undefined) {
|
|
19
|
+
return EQ.PRE(equivalent, true, path, hint, dbc);
|
|
20
|
+
}
|
|
21
|
+
/** See {@link EQ.POST }. Always inverts equality check. */
|
|
22
|
+
static POST(equivalent, _invert = false, path = undefined, hint = undefined, dbc = undefined) {
|
|
23
|
+
return EQ.POST(equivalent, true, path, hint, dbc);
|
|
24
|
+
}
|
|
25
|
+
/** See {@link EQ.INVARIANT }. Always inverts equality check. */
|
|
26
|
+
static INVARIANT(equivalent, _invert = false, path = undefined, hint = undefined, dbc = undefined) {
|
|
27
|
+
return EQ.INVARIANT(equivalent, true, path, hint, dbc);
|
|
28
|
+
}
|
|
29
|
+
/** See {@link EQ.constructor }. */
|
|
30
|
+
constructor(equivalent) {
|
|
31
|
+
super(equivalent, true);
|
|
32
|
+
this.equivalent = equivalent;
|
|
33
|
+
}
|
|
34
|
+
}
|
package/dist/DBC/EQ.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { DBC } from "../DBC";
|
|
2
|
+
/**
|
|
3
|
+
* A {@link DBC } defining that two {@link object }s gotta be equal.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Maintainer: Callari, Salvatore (XDBC@WaXCode.net) */
|
|
7
|
+
export class EQ extends DBC {
|
|
8
|
+
// #region Condition checking.
|
|
9
|
+
/**
|
|
10
|
+
* Checks if the value **toCheck** is equal to the specified **equivalent**.
|
|
11
|
+
*
|
|
12
|
+
* @param toCheck The value that has to be equal to it's possible **equivalent** for this {@link DBC } to be fulfilled.
|
|
13
|
+
* @param equivalent The {@link object } the one **toCheck** has to be equal to in order for this {@link DBC } to be
|
|
14
|
+
* fulfilled.
|
|
15
|
+
*
|
|
16
|
+
* @returns TRUE if the value **toCheck** and the **equivalent** are equal to each other, otherwise FALSE. */
|
|
17
|
+
static checkAlgorithm(toCheck, equivalent, invert) {
|
|
18
|
+
if (!invert && equivalent !== toCheck) {
|
|
19
|
+
return `Value has to be equal to "${equivalent}"`;
|
|
20
|
+
}
|
|
21
|
+
if (invert && equivalent === toCheck) {
|
|
22
|
+
return `Value must not be equal to "${equivalent}"`;
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A parameter-decorator factory using the {@link EQ.checkAlgorithm } to determine whether this {@link DBC } is fulfilled
|
|
28
|
+
* by the tagged parameter.
|
|
29
|
+
*
|
|
30
|
+
* @param equivalent See {@link EQ.checkAlgorithm }.
|
|
31
|
+
* @param path See {@link DBC.decPrecondition }.
|
|
32
|
+
* @param dbc See {@link DBC.decPrecondition }.
|
|
33
|
+
*
|
|
34
|
+
* @returns See {@link DBC.decPrecondition }. */
|
|
35
|
+
static PRE(equivalent, invert = false, path = undefined, hint = undefined, dbc = undefined) {
|
|
36
|
+
return DBC.createPRE(EQ.checkAlgorithm, [equivalent, invert], dbc, path, hint);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A method-decorator factory using the {@link EQ.checkAlgorithm } to determine whether this {@link DBC } is fulfilled
|
|
40
|
+
* by the tagged method's returnvalue.
|
|
41
|
+
*
|
|
42
|
+
* @param equivalent See {@link EQ.checkAlgorithm }.
|
|
43
|
+
* @param path See {@link DBC.Postcondition }.
|
|
44
|
+
* @param dbc See {@link DBC.decPostcondition }.
|
|
45
|
+
*
|
|
46
|
+
* @returns See {@link DBC.decPostcondition }. */
|
|
47
|
+
static POST(equivalent, invert = false, path = undefined, hint = undefined, dbc = undefined) {
|
|
48
|
+
return DBC.createPOST(EQ.checkAlgorithm, [equivalent, invert], dbc, path, hint);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A field-decorator factory using the {@link EQ.checkAlgorithm } to determine whether this {@link DBC } is fulfilled
|
|
52
|
+
* by the tagged field.
|
|
53
|
+
*
|
|
54
|
+
* @param equivalent See {@link EQ.checkAlgorithm }.
|
|
55
|
+
* @param path See {@link DBC.decInvariant }.
|
|
56
|
+
* @param dbc See {@link DBC.decInvariant }.
|
|
57
|
+
*
|
|
58
|
+
* @returns See {@link DBC.decInvariant }. */
|
|
59
|
+
static INVARIANT(equivalent, invert = false, path = undefined, hint = undefined, dbc = undefined) {
|
|
60
|
+
return DBC.createINVARIANT(EQ, [equivalent, invert], dbc, path, hint);
|
|
61
|
+
}
|
|
62
|
+
// #endregion Condition checking.
|
|
63
|
+
// #region Referenced Condition checking.
|
|
64
|
+
//
|
|
65
|
+
// For usage in dynamic scenarios (like with AE-DBC).
|
|
66
|
+
//
|
|
67
|
+
/**
|
|
68
|
+
* Invokes the {@link EQ.checkAlgorithm } passing the value **toCheck**, {@link EQ.equivalent } and {@link EQ.invert }.
|
|
69
|
+
*
|
|
70
|
+
* @param toCheck See {@link EQ.checkAlgorithm }.
|
|
71
|
+
*
|
|
72
|
+
* @returns See {@link EQ.checkAlgorithm}. */
|
|
73
|
+
check(toCheck) {
|
|
74
|
+
return EQ.checkAlgorithm(toCheck, this.equivalent, this.invert);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Invokes the {@link EQ.checkAlgorithm } passing the value **toCheck** and the specified **type** .
|
|
78
|
+
*
|
|
79
|
+
* @param toCheck See {@link EQ.checkAlgorithm }.
|
|
80
|
+
*
|
|
81
|
+
* @returns The **CANDIDATE** **toCheck** doesn't fulfill this {@link EQ }.
|
|
82
|
+
*
|
|
83
|
+
* @throws A {@link DBC.Infringement } if the **CANDIDATE** **toCheck** does not fulfill this {@link EQ }.*/
|
|
84
|
+
static tsCheck(toCheck, equivalent, hint = undefined, id = undefined, dbc = undefined) {
|
|
85
|
+
const result = EQ.checkAlgorithm(toCheck, equivalent, false);
|
|
86
|
+
if (result === true) {
|
|
87
|
+
return toCheck;
|
|
88
|
+
}
|
|
89
|
+
DBC.reportTsCheckInfringement(`${id ? `(${id}) ` : ""}${result} ${hint ? `✨ ${hint} ✨` : ""}`, dbc);
|
|
90
|
+
return toCheck;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Creates this {@link EQ } by setting the protected property {@link EQ.equivalent } used by {@link EQ.check }.
|
|
94
|
+
*
|
|
95
|
+
* @param equivalent See {@link EQ.check }. */
|
|
96
|
+
constructor(equivalent, invert = false) {
|
|
97
|
+
super();
|
|
98
|
+
this.equivalent = equivalent;
|
|
99
|
+
this.invert = invert;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { DBC } from "../DBC";
|
|
2
|
+
/**
|
|
3
|
+
* A {@link DBC } defining that two {@link object }s gotta be equal.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Maintainer: Callari, Salvatore (XDBC@WaXCode.net) */
|
|
7
|
+
export class GREATER extends DBC {
|
|
8
|
+
// #region Condition checking.
|
|
9
|
+
/**
|
|
10
|
+
* Checks if the value **toCheck** is equal to the specified **equivalent**.
|
|
11
|
+
*
|
|
12
|
+
* @param toCheck The value that has to be equal to it's possible **equivalent** for this {@link DBC } to be fulfilled.
|
|
13
|
+
* @param equivalent The {@link object } the one **toCheck** has to be equal to in order for this {@link DBC } to be
|
|
14
|
+
* fulfilled.
|
|
15
|
+
*
|
|
16
|
+
* @returns TRUE if the value **toCheck** and the **equivalent** are equal to each other, otherwise FALSE. */
|
|
17
|
+
static checkAlgorithm(toCheck, equivalent, equalityPermitted, invert) {
|
|
18
|
+
if (equalityPermitted && !invert && toCheck < equivalent) {
|
|
19
|
+
return `Value has to to be greater than or equal to "${equivalent}"`;
|
|
20
|
+
}
|
|
21
|
+
if (equalityPermitted && invert && toCheck > equivalent) {
|
|
22
|
+
return `Value must not to be less than or equal to "${equivalent}"`;
|
|
23
|
+
}
|
|
24
|
+
if (!equalityPermitted && !invert && toCheck <= equivalent) {
|
|
25
|
+
return `Value has to to be greater than "${equivalent}"`;
|
|
26
|
+
}
|
|
27
|
+
if (!equalityPermitted && invert && toCheck >= equivalent) {
|
|
28
|
+
return `Value must not to be less than "${equivalent}"`;
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A parameter-decorator factory using the {@link GREATER.checkAlgorithm } to determine whether this {@link DBC } is fulfilled
|
|
34
|
+
* by the tagged parameter.
|
|
35
|
+
*
|
|
36
|
+
* @param equivalent See {@link GREATER.checkAlgorithm }.
|
|
37
|
+
* @param equalityPermitted See {@link GREATER.checkAlgorithm }.
|
|
38
|
+
* @param path See {@link DBC.decPrecondition }.
|
|
39
|
+
* @param dbc See {@link DBC.decPrecondition }.
|
|
40
|
+
*
|
|
41
|
+
* @returns See {@link DBC.decPrecondition }. */
|
|
42
|
+
static PRE(equivalent, equalityPermitted = false, invert = false, path = undefined, dbc = "WaXCode.DBC") {
|
|
43
|
+
return DBC.decPrecondition((value, target, methodName, parameterIndex) => {
|
|
44
|
+
return GREATER.checkAlgorithm(value, equivalent, equalityPermitted, invert);
|
|
45
|
+
}, dbc, path);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A method-decorator factory using the {@link GREATER.checkAlgorithm } to determine whether this {@link DBC } is fulfilled
|
|
49
|
+
* by the tagged method's returnvalue.
|
|
50
|
+
*
|
|
51
|
+
* @param equivalent See {@link GREATER.checkAlgorithm }.
|
|
52
|
+
* @param equalityPermitted See {@link GREATER.checkAlgorithm }.
|
|
53
|
+
* @param path See {@link DBC.Postcondition }.
|
|
54
|
+
* @param dbc See {@link DBC.decPostcondition }.
|
|
55
|
+
*
|
|
56
|
+
* @returns See {@link DBC.decPostcondition }. */
|
|
57
|
+
static POST(equivalent, equalityPermitted = false, invert = false, path = undefined, dbc = "WaXCode.DBC") {
|
|
58
|
+
return DBC.decPostcondition((value, target, propertyKey) => {
|
|
59
|
+
return GREATER.checkAlgorithm(value, equalityPermitted, equivalent, invert);
|
|
60
|
+
}, dbc, path);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* A field-decorator factory using the {@link GREATER.checkAlgorithm } to determine whether this {@link DBC } is fulfilled
|
|
64
|
+
* by the tagged field.
|
|
65
|
+
*
|
|
66
|
+
* @param equivalent See {@link GREATER.checkAlgorithm }.
|
|
67
|
+
* @param equalityPermitted See {@link GREATER.checkAlgorithm }.
|
|
68
|
+
* @param path See {@link DBC.decInvariant }.
|
|
69
|
+
* @param dbc See {@link DBC.decInvariant }.
|
|
70
|
+
*
|
|
71
|
+
* @returns See {@link DBC.decInvariant }. */
|
|
72
|
+
static INVARIANT(equivalent, equalityPermitted = false, invert = false, path = undefined, dbc = "WaXCode.DBC") {
|
|
73
|
+
return DBC.decInvariant([new GREATER(equivalent, equalityPermitted, invert)], path, dbc);
|
|
74
|
+
}
|
|
75
|
+
// #endregion Condition checking.
|
|
76
|
+
// #region Referenced Condition checking.
|
|
77
|
+
// #region Dynamic usage.
|
|
78
|
+
/**
|
|
79
|
+
* Invokes the {@link GREATER.checkAlgorithm } passing the value **toCheck**, {@link GREATER.equivalent } and {@link GREATER.invert }.
|
|
80
|
+
*
|
|
81
|
+
* @param toCheck See {@link GREATER.checkAlgorithm }.
|
|
82
|
+
*
|
|
83
|
+
* @returns See {@link GREATER.checkAlgorithm}. */
|
|
84
|
+
check(toCheck) {
|
|
85
|
+
return GREATER.checkAlgorithm(toCheck, this.equivalent, this.equalityPermitted, this.invert);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Creates this {@link GREATER } by setting the protected property {@link GREATER.equivalent }, {@link GREATER.equalityPermitted } and {@link GREATER.invert } used by {@link GREATER.check }.
|
|
89
|
+
*
|
|
90
|
+
* @param equivalent See {@link GREATER.check }.
|
|
91
|
+
* @param equalityPermitted See {@link GREATER.check }.
|
|
92
|
+
* @param invert See {@link GREATER.check }. */
|
|
93
|
+
constructor(equivalent, equalityPermitted = false, invert = false) {
|
|
94
|
+
super();
|
|
95
|
+
this.equivalent = equivalent;
|
|
96
|
+
this.equalityPermitted = equalityPermitted;
|
|
97
|
+
this.invert = invert;
|
|
98
|
+
}
|
|
99
|
+
}
|