wysiwyv 0.1.0 → 0.1.1
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/README-INCLUDED-PLUGINS.md +576 -0
- package/README-INTEGRATION.md +45 -0
- package/README-PLUGIN-AUTHORING.md +177 -0
- package/README-USAGE.md +352 -0
- package/package.json +7 -2
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
| [README](README.md) | [USAGE](README-USAGE.md) | _INCLUDED PLUGINS_ | [PLUGIN AUTHORING](README-PLUGIN-AUTHORING.md) | [INTEGRATION](README-INTEGRATION.md) |
|
|
2
|
+
| :------------------ | :----------------------- | :----------------- | :--------------------------------------------- | :----------------------------------- |
|
|
3
|
+
|
|
4
|
+
# Included Plugins
|
|
5
|
+
|
|
6
|
+
- [Basic Syntax](#basic-syntax)
|
|
7
|
+
- [$and](#and) / [$or](#or)
|
|
8
|
+
- [$any](#any)
|
|
9
|
+
- [$array](#array) / [$object](#object) / [$plainobject](#plain-object)
|
|
10
|
+
- [$basicisodate](#basic-iso-date) / [$isodate](#iso-date) / [$strictisodate](#strict-iso-date)
|
|
11
|
+
- [$bool](#bool)
|
|
12
|
+
- [$email](#email)
|
|
13
|
+
- [$int](#int) / [$number](#number)
|
|
14
|
+
- [$string](#string)
|
|
15
|
+
- [$uuid](#uuid)
|
|
16
|
+
- [$val](#val)
|
|
17
|
+
|
|
18
|
+
## BASIC SYNTAX
|
|
19
|
+
|
|
20
|
+
Each plugin has a unique tag starting with a dollar sign, e.g. `$myplugin`.
|
|
21
|
+
|
|
22
|
+
Syntax varies by number of parameters being passed to the plugin.
|
|
23
|
+
|
|
24
|
+
### No Parameters
|
|
25
|
+
|
|
26
|
+
Matcher is a simple string with just the plugin tag:
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
{ "id": "$uuid" }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### One Parameter
|
|
33
|
+
|
|
34
|
+
Matcher is an object with the tag as its only key, and parameter as its value. (Here, the UUID version)
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
{ "id": { "$uuid": 4 } }
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Named Parameters
|
|
41
|
+
|
|
42
|
+
Matcher is an object with the tag as its only key, and a sub-object with the named parameters. Parameter names also start with `$`
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
{ "age": {
|
|
46
|
+
"$int": {
|
|
47
|
+
"$min": 20,
|
|
48
|
+
"$max": 80
|
|
49
|
+
}
|
|
50
|
+
}}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
[↑ top](#included-plugins)
|
|
54
|
+
|
|
55
|
+
## AND
|
|
56
|
+
|
|
57
|
+
Key: `$and`
|
|
58
|
+
|
|
59
|
+
Takes an array of predicates.
|
|
60
|
+
|
|
61
|
+
- If array is empty, returns success
|
|
62
|
+
- If ALL predicates are passed, returns success
|
|
63
|
+
- If ANY predicates fail:
|
|
64
|
+
- One error is recorded for the $and block itself
|
|
65
|
+
- All errors from failed predicates are returned
|
|
66
|
+
- The same path will be set for all for easy association
|
|
67
|
+
- No short-circuiting; all predicates are evaluated
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
{
|
|
71
|
+
"id": {
|
|
72
|
+
"$and": [
|
|
73
|
+
"$uuid",
|
|
74
|
+
{ "$val": "store_id" }
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
See [and-plugin.test.ts](tests/plugins/and-plugin.test.ts) for exhaustive cases
|
|
81
|
+
|
|
82
|
+
[↑ top](#included-plugins)
|
|
83
|
+
|
|
84
|
+
## ANY
|
|
85
|
+
|
|
86
|
+
Key: `$any`
|
|
87
|
+
|
|
88
|
+
Succeeds on any input value.
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
{ "biography": "$any" }
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
See [any-plugin.test.ts](tests/plugins/any-plugin.test.ts) for exhaustive cases
|
|
95
|
+
|
|
96
|
+
[↑ top](#included-plugins)
|
|
97
|
+
|
|
98
|
+
## ARRAY
|
|
99
|
+
|
|
100
|
+
Key: `$array`
|
|
101
|
+
|
|
102
|
+
Validates that the input is an array.
|
|
103
|
+
|
|
104
|
+
- No parameters: accepts any array
|
|
105
|
+
- Optional named parameters:
|
|
106
|
+
- `$length` — exact length match
|
|
107
|
+
- `$minlength` — minimum length (inclusive)
|
|
108
|
+
- `$maxlength` — maximum length (inclusive)
|
|
109
|
+
- `$each` — validate every element against a template
|
|
110
|
+
If _none_ of these are present, any parameter provided will be treated as `$each`
|
|
111
|
+
- All length constraints are checked independently; a value can fail multiple constraints
|
|
112
|
+
- ⚠️ Unknown parameters alongside recognized ones generate a config error
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
// bare — any array
|
|
116
|
+
{ "tags": "$array" }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
// length constraints
|
|
121
|
+
{ "tags": { "$array": { "$minlength": 1, "$maxlength": 10 } } }
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
// explicit $each — validate every element
|
|
126
|
+
{ "tags": {
|
|
127
|
+
"$array": {
|
|
128
|
+
"$each": "$string"
|
|
129
|
+
}
|
|
130
|
+
}}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
// implicit $each — validate every element
|
|
135
|
+
{ "ages": {
|
|
136
|
+
"$array": "$number"
|
|
137
|
+
}}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
// implicit each shorthand — if no recognized parameters are present,
|
|
142
|
+
// the params object itself becomes the element template
|
|
143
|
+
{ "team": {
|
|
144
|
+
"$array": {
|
|
145
|
+
"name": "$string",
|
|
146
|
+
"role": "$string"
|
|
147
|
+
}
|
|
148
|
+
}}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Note:** The implicit shorthand only activates when _no_ recognized parameters (`$length`, `$minlength`, `$maxlength`, `$each`) are present. Mixing recognized parameters with extra keys produces a config error.
|
|
152
|
+
|
|
153
|
+
See [array-plugin.test.ts](tests/plugins/array-plugin.test.ts) for exhaustive cases
|
|
154
|
+
|
|
155
|
+
[↑ top](#included-plugins)
|
|
156
|
+
|
|
157
|
+
## BASIC ISO DATE
|
|
158
|
+
|
|
159
|
+
Key: `$basicisodate`
|
|
160
|
+
|
|
161
|
+
Validates ISO 8601 Basic format — the compact form with no separators.
|
|
162
|
+
|
|
163
|
+
- Must be a string
|
|
164
|
+
- Format: `YYYYMMDDTHHmmssZ` (no dashes or colons)
|
|
165
|
+
- Seconds and fractional seconds are optional
|
|
166
|
+
- Timezone designator is required (`Z` or `±HHmm`)
|
|
167
|
+
- Case-insensitive `T` separator
|
|
168
|
+
- Validates numeric ranges for date/time components
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
// accepts "20201209T160953Z"
|
|
172
|
+
{ "timestamp": "$basicisodate" }
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
See [datetime-plugin.test.ts](tests/plugins/datetime-plugin.test.ts) for exhaustive cases
|
|
176
|
+
|
|
177
|
+
[↑ top](#included-plugins)
|
|
178
|
+
|
|
179
|
+
## BOOL
|
|
180
|
+
|
|
181
|
+
Key: `$bool`
|
|
182
|
+
|
|
183
|
+
Validates that the input is a boolean.
|
|
184
|
+
|
|
185
|
+
- Accepts `true` and `false` only
|
|
186
|
+
- Rejects truthy/falsy stand-ins: `"true"`, `"false"`, `0`, `1`, etc.
|
|
187
|
+
- No parameters
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
{ "active": "$bool" }
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
See [bool-plugin.test.ts](tests/plugins/bool-plugin.test.ts) for exhaustive cases
|
|
194
|
+
|
|
195
|
+
[↑ top](#included-plugins)
|
|
196
|
+
|
|
197
|
+
## EMAIL
|
|
198
|
+
|
|
199
|
+
Key: `$email`
|
|
200
|
+
|
|
201
|
+
Validates email address format.
|
|
202
|
+
|
|
203
|
+
- Must be a string
|
|
204
|
+
- Covers common email formats; not an exhaustive RFC 5322 implementation
|
|
205
|
+
- Rejects: missing `@`, double `@`, trailing dot in domain, consecutive dots in local part, spaces, bare addresses without a domain dot
|
|
206
|
+
- No parameters
|
|
207
|
+
|
|
208
|
+
```js
|
|
209
|
+
{ "contact": "$email" }
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
See [email-plugin.test.ts](tests/plugins/email-plugin.test.ts) for exhaustive cases
|
|
213
|
+
|
|
214
|
+
[↑ top](#included-plugins)
|
|
215
|
+
|
|
216
|
+
## INT
|
|
217
|
+
|
|
218
|
+
Key: `$int`
|
|
219
|
+
|
|
220
|
+
Validates that the input is an integer.
|
|
221
|
+
|
|
222
|
+
- Uses `Number.isInteger()` under the hood
|
|
223
|
+
- Accepts whole-valued floats like `1.0` and `-0`
|
|
224
|
+
- Rejects floats, NaN, Infinity, bigints, and non-numbers
|
|
225
|
+
- Optional named parameters:
|
|
226
|
+
- `$min` — inclusive lower bound (≥)
|
|
227
|
+
- `$max` — inclusive upper bound (≤)
|
|
228
|
+
- Both constraints are checked independently; a value can fail both
|
|
229
|
+
|
|
230
|
+
```js
|
|
231
|
+
// bare — any integer
|
|
232
|
+
{ "count": "$int" }
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
```js
|
|
236
|
+
// with bounds
|
|
237
|
+
{ "age": {
|
|
238
|
+
"$int": {
|
|
239
|
+
"$min": 0,
|
|
240
|
+
"$max": 150
|
|
241
|
+
}
|
|
242
|
+
}}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
See [int-plugin.test.ts](tests/plugins/int-plugin.test.ts) for exhaustive cases
|
|
246
|
+
|
|
247
|
+
[↑ top](#included-plugins)
|
|
248
|
+
|
|
249
|
+
## ISO DATE
|
|
250
|
+
|
|
251
|
+
Key: `$isodate`
|
|
252
|
+
|
|
253
|
+
Validates ISO 8601 Extended format — the common form with separators.
|
|
254
|
+
|
|
255
|
+
- Must be a string
|
|
256
|
+
- Format: `YYYY-MM-DDTHH:mm:ssZ` (with dashes and colons)
|
|
257
|
+
- Seconds and fractional seconds are optional
|
|
258
|
+
- Timezone designator is required (`Z` or `±HH:mm`)
|
|
259
|
+
- Case-insensitive `T` separator
|
|
260
|
+
- Validates numeric ranges for date/time components
|
|
261
|
+
- Also checks that the string parses to a valid JS Date (catches things like Feb 30)
|
|
262
|
+
|
|
263
|
+
```js
|
|
264
|
+
// accepts "2026-05-05T20:41:00Z", "2026-05-05T13:41:00-07:00"
|
|
265
|
+
{ "createdAt": "$isodate" }
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
See [datetime-plugin.test.ts](tests/plugins/datetime-plugin.test.ts) for exhaustive cases
|
|
269
|
+
|
|
270
|
+
[↑ top](#included-plugins)
|
|
271
|
+
|
|
272
|
+
## NUMBER
|
|
273
|
+
|
|
274
|
+
Key: `$number`
|
|
275
|
+
|
|
276
|
+
Validates that the input is a finite number.
|
|
277
|
+
|
|
278
|
+
- Accepts integers, floats, `-0`
|
|
279
|
+
- Rejects NaN, Infinity, -Infinity, bigints, and non-numbers
|
|
280
|
+
- Optional named parameters:
|
|
281
|
+
- `$min` — inclusive lower bound (≥)
|
|
282
|
+
- `$max` — inclusive upper bound (≤)
|
|
283
|
+
- `$gt` — exclusive lower bound (>)
|
|
284
|
+
- `$lt` — exclusive upper bound (<)
|
|
285
|
+
- All constraints are checked independently
|
|
286
|
+
|
|
287
|
+
```js
|
|
288
|
+
// bare — any finite number
|
|
289
|
+
{ "score": "$number" }
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
```js
|
|
293
|
+
// inclusive bounds
|
|
294
|
+
{ "rating": {
|
|
295
|
+
"$number": {
|
|
296
|
+
"$min": 0,
|
|
297
|
+
"$max": 5
|
|
298
|
+
}
|
|
299
|
+
}}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
// exclusive bounds
|
|
304
|
+
{ "temperature": {
|
|
305
|
+
"$number": {
|
|
306
|
+
"$gt": -273.15
|
|
307
|
+
}
|
|
308
|
+
}}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
See [number-plugin.test.ts](tests/plugins/number-plugin.test.ts) for exhaustive cases
|
|
312
|
+
|
|
313
|
+
[↑ top](#included-plugins)
|
|
314
|
+
|
|
315
|
+
## OBJECT
|
|
316
|
+
|
|
317
|
+
Key: `$object`
|
|
318
|
+
|
|
319
|
+
Validates that the input is an object in the broad sense — includes Date, RegExp, Map, Set, wrapped primitives, etc.
|
|
320
|
+
|
|
321
|
+
- Rejects `null`, arrays, functions, and classes
|
|
322
|
+
- For plain objects only, see [$plainobject](#plain-object)
|
|
323
|
+
- Optional named parameters (mutually exclusive):
|
|
324
|
+
- `$partial` — validate a subset of keys; extra keys in the candidate are allowed; missing expected keys produce errors
|
|
325
|
+
- `$eachElement` — validate every value in the object against a template
|
|
326
|
+
- Using both `$partial` and `$eachElement` together produces a config error
|
|
327
|
+
- Unknown parameters produce a config error
|
|
328
|
+
|
|
329
|
+
```js
|
|
330
|
+
// bare — accepts any object, including Date, Map, Set, etc.
|
|
331
|
+
{ "metadata": "$object" }
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
```js
|
|
335
|
+
// partial match — only check specified keys, ignore extras
|
|
336
|
+
{ "user": {
|
|
337
|
+
"$object": {
|
|
338
|
+
"$partial": {
|
|
339
|
+
"name": "$string",
|
|
340
|
+
"email": "$email"
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
```js
|
|
347
|
+
// each element — every value must match the template
|
|
348
|
+
{ "scores": {
|
|
349
|
+
"$object": {
|
|
350
|
+
"$eachElement": "$number"
|
|
351
|
+
}
|
|
352
|
+
}}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
```js
|
|
356
|
+
// each element with nesting
|
|
357
|
+
{ "teams": {
|
|
358
|
+
"$object": {
|
|
359
|
+
"$eachElement": { "$array": { "$length": 2 } }
|
|
360
|
+
}
|
|
361
|
+
}}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
See [object-plugin.test.ts](tests/plugins/object-plugin.test.ts) for exhaustive cases
|
|
365
|
+
|
|
366
|
+
[↑ top](#included-plugins)
|
|
367
|
+
|
|
368
|
+
## OR
|
|
369
|
+
|
|
370
|
+
Key: `$or`
|
|
371
|
+
|
|
372
|
+
Takes an array of predicates.
|
|
373
|
+
|
|
374
|
+
- If array is empty, returns an error
|
|
375
|
+
- Boolean algebra defines the OR identity as false
|
|
376
|
+
- If ANY predicates are passed, returns success
|
|
377
|
+
- Short-Circuiting: Further predicates are not evaluated
|
|
378
|
+
- If ALL predicates fail:
|
|
379
|
+
- One error is recorded for the $or block itself
|
|
380
|
+
- All errors from all predicates are returned
|
|
381
|
+
- The same path will be set for all for easy association
|
|
382
|
+
|
|
383
|
+
```js
|
|
384
|
+
{
|
|
385
|
+
"id": {
|
|
386
|
+
"$or": [
|
|
387
|
+
"$uuid",
|
|
388
|
+
"$number"
|
|
389
|
+
]
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
See [or-plugin.test.ts](tests/plugins/or-plugin.test.ts) for exhaustive cases
|
|
395
|
+
|
|
396
|
+
[↑ top](#included-plugins)
|
|
397
|
+
|
|
398
|
+
## PLAIN OBJECT
|
|
399
|
+
|
|
400
|
+
Key: `$plainobject`
|
|
401
|
+
|
|
402
|
+
Validates that the input is a plain object — only literal `{}`, `new Object()`, and `Object.create(null)`.
|
|
403
|
+
|
|
404
|
+
- Rejects Date, RegExp, Map, Set, wrapped primitives, and other non-plain objects
|
|
405
|
+
- Stricter than [$object](#object), which accepts any object type
|
|
406
|
+
- Same optional parameters as `$object`:
|
|
407
|
+
- `$partial` — validate a subset of keys
|
|
408
|
+
- `$eachElement` — validate every value
|
|
409
|
+
- `$partial` and `$eachElement` are mutually exclusive
|
|
410
|
+
|
|
411
|
+
```js
|
|
412
|
+
// bare — any plain object
|
|
413
|
+
{ "config": "$plainobject" }
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
```js
|
|
417
|
+
// with $partial
|
|
418
|
+
{ "config": {
|
|
419
|
+
"$plainobject": {
|
|
420
|
+
"$partial": {
|
|
421
|
+
"debug": "$bool",
|
|
422
|
+
"port": "$int"
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
See [object-plugin.test.ts](tests/plugins/object-plugin.test.ts) for exhaustive cases
|
|
429
|
+
|
|
430
|
+
[↑ top](#included-plugins)
|
|
431
|
+
|
|
432
|
+
## STRICT ISO DATE
|
|
433
|
+
|
|
434
|
+
Key: `$strictisodate`
|
|
435
|
+
|
|
436
|
+
Validates RFC 3339 format — a strict profile of ISO 8601 commonly used in APIs.
|
|
437
|
+
|
|
438
|
+
- Must be a string
|
|
439
|
+
- Format: `YYYY-MM-DDTHH:mm:ss.sssZ`
|
|
440
|
+
- Allows space as date/time separator (not just `T`)
|
|
441
|
+
- Seconds and fractional seconds are optional
|
|
442
|
+
- Timezone designator is required (`Z` or `±HH:mm`, including `-00:00`)
|
|
443
|
+
- Case-insensitive `T`/`t` separator
|
|
444
|
+
- Also checks that the string parses to a valid JS Date
|
|
445
|
+
|
|
446
|
+
```js
|
|
447
|
+
// accepts "2026-05-05T20:41:00Z", "2026-05-05 20:41:00Z"
|
|
448
|
+
{ "timestamp": "$strictisodate" }
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
See [datetime-plugin.test.ts](tests/plugins/datetime-plugin.test.ts) for exhaustive cases
|
|
452
|
+
|
|
453
|
+
[↑ top](#included-plugins)
|
|
454
|
+
|
|
455
|
+
## STRING
|
|
456
|
+
|
|
457
|
+
Key: `$string`
|
|
458
|
+
|
|
459
|
+
Validates that the input is a string.
|
|
460
|
+
|
|
461
|
+
- Accepts any primitive string value
|
|
462
|
+
- Rejects non-strings, including numbers, booleans, null, undefined, and boxed `new String()` objects
|
|
463
|
+
- No parameters
|
|
464
|
+
|
|
465
|
+
```js
|
|
466
|
+
{ "name": "$string" }
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
See [string-plugin.test.ts](tests/plugins/string-plugin.test.ts) for exhaustive cases
|
|
470
|
+
|
|
471
|
+
[↑ top](#included-plugins)
|
|
472
|
+
|
|
473
|
+
## UUID
|
|
474
|
+
|
|
475
|
+
Key: `$uuid`
|
|
476
|
+
|
|
477
|
+
Validates UUID format.
|
|
478
|
+
|
|
479
|
+
- No parameter: accepts any valid UUID (versions 1–8, NIL, and max)
|
|
480
|
+
- Single parameter (version): validates a specific UUID version
|
|
481
|
+
- `0` — NIL UUID (all zeros)
|
|
482
|
+
- `1` through `8` — specific version
|
|
483
|
+
- `"F"` — max UUID (all f's)
|
|
484
|
+
- Invalid version numbers produce a config error
|
|
485
|
+
- Must be a string
|
|
486
|
+
- Case-insensitive
|
|
487
|
+
|
|
488
|
+
```js
|
|
489
|
+
// any UUID
|
|
490
|
+
{ "id": "$uuid" }
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
```js
|
|
494
|
+
// specific version
|
|
495
|
+
{ "id": { "$uuid": 4 } }
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
```js
|
|
499
|
+
// NIL UUID
|
|
500
|
+
{ "id": { "$uuid": 0 } }
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
See [uuid-plugin.test.ts](tests/plugins/uuid-plugin.test.ts) for exhaustive cases
|
|
504
|
+
|
|
505
|
+
[↑ top](#included-plugins)
|
|
506
|
+
|
|
507
|
+
## VAL
|
|
508
|
+
|
|
509
|
+
Key: `$val`
|
|
510
|
+
|
|
511
|
+
Matches values by name — either against predefined values supplied at setup time, or by capturing and back-referencing values seen during validation.
|
|
512
|
+
|
|
513
|
+
### Predefined Values
|
|
514
|
+
|
|
515
|
+
Supply known values when creating the validator. The template checks that candidate values match.
|
|
516
|
+
|
|
517
|
+
```js
|
|
518
|
+
const wyv = makeWysiwyv({
|
|
519
|
+
pluginSetups: {
|
|
520
|
+
$val: {
|
|
521
|
+
expectedTeam: "Yankees",
|
|
522
|
+
fruit: "apple",
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
const expected = {
|
|
528
|
+
team: { $val: "expectedTeam" },
|
|
529
|
+
snack: { $val: "fruit" },
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
// passes if team === "Yankees" and snack === "apple"
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### Back-References
|
|
536
|
+
|
|
537
|
+
If a val name hasn't been predefined, the first occurrence captures whatever value appears in the candidate. Subsequent uses of the same name check that the value matches.
|
|
538
|
+
|
|
539
|
+
```js
|
|
540
|
+
const expected = {
|
|
541
|
+
billing: {
|
|
542
|
+
customerId: { $val: "cid" },
|
|
543
|
+
},
|
|
544
|
+
shipping: {
|
|
545
|
+
customerId: { $val: "cid" },
|
|
546
|
+
},
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
// passes if billing.customerId === shipping.customerId
|
|
550
|
+
// (whatever the value is)
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
### Combined
|
|
554
|
+
|
|
555
|
+
Predefined values and back-references work together. Predefined keys are checked immediately; undefined keys are captured on first encounter and enforced on subsequent encounters.
|
|
556
|
+
|
|
557
|
+
```js
|
|
558
|
+
const wyv = makeWysiwyv({
|
|
559
|
+
pluginSetups: {
|
|
560
|
+
$val: { region: "us-east-1" },
|
|
561
|
+
},
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
const expected = {
|
|
565
|
+
region: { $val: "region" },
|
|
566
|
+
primaryId: { $val: "id" },
|
|
567
|
+
backupId: { $val: "id" },
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
// region must be "us-east-1" (predefined)
|
|
571
|
+
// primaryId and backupId must match each other (back-reference)
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
See [vals-plugin.test.ts](tests/plugins/vals-plugin.test.ts) for exhaustive cases
|
|
575
|
+
|
|
576
|
+
[↑ top](#included-plugins)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
| [README](README.md) | [USAGE](README-USAGE.md) | [INCLUDED PLUGINS](README-INCLUDED-PLUGINS.md) | [PLUGIN AUTHORING](README-PLUGIN-AUTHORING.md) | _INTEGRATION_ |
|
|
2
|
+
| :------------------ | :----------------------- | :--------------------------------------------- | :--------------------------------------------- | ------------- |
|
|
3
|
+
|
|
4
|
+
# Integration
|
|
5
|
+
|
|
6
|
+
## jest
|
|
7
|
+
|
|
8
|
+
See [test-util](./test-util.ts)
|
|
9
|
+
|
|
10
|
+
## express
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
const wyv = makeWysiwyv();
|
|
14
|
+
|
|
15
|
+
export const validateBody =
|
|
16
|
+
(template: HookValue) =>
|
|
17
|
+
(req: Request, res: Response, next: NextFunction) => {
|
|
18
|
+
const result = wyv.validate(template, req.body);
|
|
19
|
+
if (result.success) return next();
|
|
20
|
+
res.status(400).json({
|
|
21
|
+
error: "Validation failed",
|
|
22
|
+
details: result.errors,
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const newUserTemplate = {
|
|
27
|
+
name: "$string",
|
|
28
|
+
email: "$email",
|
|
29
|
+
age: { $int: { $min: 18 } },
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
app.post("/users", validateBody(newUserTemplate), createUserHandler);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## zod
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
const toZodLike = (assessment: HookAssessment) => ({
|
|
39
|
+
success: assessment.success,
|
|
40
|
+
issues: assessment.errors.map((e) => ({
|
|
41
|
+
message: e.message,
|
|
42
|
+
path: e.path.split(".").filter(Boolean), // convert ".age" to ["age"]
|
|
43
|
+
})),
|
|
44
|
+
});
|
|
45
|
+
```
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
| [README](README.md) | [USAGE](README-USAGE.md) | [INCLUDED PLUGINS](README-INCLUDED-PLUGINS.md) | _PLUGIN AUTHORING_ | [INTEGRATION](README-INTEGRATION.md) |
|
|
2
|
+
| :------------------ | :----------------------- | :--------------------------------------------- | :----------------- | :----------------------------------- |
|
|
3
|
+
|
|
4
|
+
# Plugin Authoring
|
|
5
|
+
|
|
6
|
+
## 1. Pick a key.
|
|
7
|
+
|
|
8
|
+
Select a string to identify your plugin. It should start with a dollar sign and should be unique among loaded plugins. We can use `$us-ssn` as an example.
|
|
9
|
+
Your key will be used in several contexts:
|
|
10
|
+
|
|
11
|
+
1. In a matching template, to select your plugin for matching a given node.
|
|
12
|
+
2. In initial Wysiwyv factory, to specify plugin setup data.
|
|
13
|
+
3. When the engine is calling your plugin, to give you exclusive access to setup and scratch data.
|
|
14
|
+
|
|
15
|
+
[↑ top](#plugin-authoring)
|
|
16
|
+
|
|
17
|
+
## 2. Scaffold your plugin.
|
|
18
|
+
|
|
19
|
+
1. Start with a `WyvPlugin` object.
|
|
20
|
+
2. Populate `handles` with a function that will return true when passed your key.
|
|
21
|
+
3. Populate `handlers` with an object that maps your key to a function that should evaluate the node being evaluated.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
export const WYV_KEY_US_SSN = "$us-ssn";
|
|
25
|
+
const ssnWyvern: WyvPlugin = {
|
|
26
|
+
handles: (value) => [WYV_KEY_US_SSN].includes(value),
|
|
27
|
+
handlers: {
|
|
28
|
+
[WYV_KEY_US_SSN]: (value, expected, options) => {
|
|
29
|
+
// ...
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
[↑ top](#plugin-authoring)
|
|
36
|
+
|
|
37
|
+
## 3. Evaluate data and return your assessment.
|
|
38
|
+
|
|
39
|
+
1. The `value` parameter is the value to be evaluated.
|
|
40
|
+
The `expected` parameter is the template node the value should be assessed against.
|
|
41
|
+
1. If you have determined the value is acceptable, just
|
|
42
|
+
`return HookAssessor.SUCCESS;`
|
|
43
|
+
1. If you have one reason for rejecting a value
|
|
44
|
+
1. create an error with one of the helpers from `HookError.ts`:
|
|
45
|
+
`const e = errValue(3, 6, '.employee[3].id');`
|
|
46
|
+
2. return it with
|
|
47
|
+
`return HookAssessor.fault(myError);`
|
|
48
|
+
1. If you have multiple reasons for rejecting a value
|
|
49
|
+
1. start an assessement with:
|
|
50
|
+
`const errors = HookAssessment.start()`
|
|
51
|
+
2. add encountered errors one at a time with `errors.fault(myNextError);`
|
|
52
|
+
3. incorporate another assessent into your open one with `errors.include(myOtherErrors);`
|
|
53
|
+
4. when finished, just `return errors;`
|
|
54
|
+
1. All assessments follow the same format:
|
|
55
|
+
|
|
56
|
+
if no issues encountered:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
{
|
|
60
|
+
success: true,
|
|
61
|
+
errors: [],
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
if faults are found:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
{
|
|
69
|
+
success: false,
|
|
70
|
+
errors: [
|
|
71
|
+
{message: '...', path: '...'},
|
|
72
|
+
...,
|
|
73
|
+
],
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
[↑ top](#plugin-authoring)
|
|
78
|
+
|
|
79
|
+
## 4. Know your Options
|
|
80
|
+
|
|
81
|
+
The `options` parameter has several fields.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
options = {
|
|
85
|
+
path: string;
|
|
86
|
+
params: ParamsType;
|
|
87
|
+
setup: SetupType;
|
|
88
|
+
context: ContextType;
|
|
89
|
+
shared: Record<string, unknown>;
|
|
90
|
+
evaluate: WysiwyvEvaluatorFunction;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- `path`:
|
|
95
|
+
- Your current path in the original data structure.
|
|
96
|
+
- Components look like `.fieldname` for objects or `[#]` for arrays.
|
|
97
|
+
- Use this as the path value when making HookErrors
|
|
98
|
+
- `params`:
|
|
99
|
+
- Parameters to your plugin specified at the current position in the matching template object.
|
|
100
|
+
- Customize by defining a `MyHookParams` type reflecting how they should be passed.
|
|
101
|
+
- This can be any kind of data representable in JSON since it comes from the template.
|
|
102
|
+
`type WyvParams = { version: number }`
|
|
103
|
+
- `setup`:
|
|
104
|
+
- Parameters to your plugin specified at the time the matching engine is instantiated (makeWysiwyv)
|
|
105
|
+
- Customize by defining a `MyHookSetup` type similar to that for `params`
|
|
106
|
+
- How to provide the data is shown below, at [Register Plugin](#6-register-plugin)
|
|
107
|
+
- `context`:
|
|
108
|
+
- A private data store for any information your plugin needs to track from one instance to the next.
|
|
109
|
+
- Only plugins with the same `key` will see this data.
|
|
110
|
+
- Customize with a `MyHookContext` type similar to that for `params`.
|
|
111
|
+
- `shared`:
|
|
112
|
+
- A shared data store for any information that needs to be reused across multiple plugin types.
|
|
113
|
+
- This is the Wild West. No type enforcement, no privacy.
|
|
114
|
+
- `evaluate`:
|
|
115
|
+
- A callback function provided by the engine. See [Recurse and Descend](#5-recurse-and-descend)
|
|
116
|
+
|
|
117
|
+
⚠️ If you customize any types, all should be provided as type params when defining your plugin:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
const ssnWyvern: WyvPlugin<MyParams, MySetup, MyContext> = {...};
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
[↑ top](#plugin-authoring)
|
|
124
|
+
|
|
125
|
+
## 5. Recurse and Descend
|
|
126
|
+
|
|
127
|
+
Your plugin can behave like `$array` or `$object`, descending recursively into a subtree of the data. This could even be a virtual tree, generated during validation by your custom logic.
|
|
128
|
+
|
|
129
|
+
1. Open an assessment:
|
|
130
|
+
`const errors = HookAssessment.start();`
|
|
131
|
+
|
|
132
|
+
2. Call `options.evaluate` on the subtree:
|
|
133
|
+
|
|
134
|
+
- This is a function provided by the engine for recursive descent. Call it when you need to hand control back, for example in the case of evaluating individual array or object children.
|
|
135
|
+
- Call with the following arguments:
|
|
136
|
+
- `expected` A Wysiwyv template object to be evaluated on the subtree
|
|
137
|
+
- `candidate` The child (or derived) field or subtree being evaluated
|
|
138
|
+
- `path` Use the `options.path` parameter you received, appending information about how the child element is being selected (key, index, hash, etc)
|
|
139
|
+
|
|
140
|
+
`const subErrors = options.evaluate(subTemplate, subTree, path + 'my selector');`
|
|
141
|
+
|
|
142
|
+
3. Include any errors back into your assessment:
|
|
143
|
+
|
|
144
|
+
`errors.include(subErrors);`
|
|
145
|
+
|
|
146
|
+
4. Return your final assessment with errors from your level plus any errors from the recursion.
|
|
147
|
+
|
|
148
|
+
`return errors;`
|
|
149
|
+
|
|
150
|
+
[↑ top](#plugin-authoring)
|
|
151
|
+
|
|
152
|
+
## 6. Register Plugin
|
|
153
|
+
|
|
154
|
+
Pass your plugin into `config.plugins` when making your engine instance.
|
|
155
|
+
|
|
156
|
+
If you have preconfig data to provide, pass it in to `config.pluginSetups.$myPlugin`.
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
import { makeWysiwyv } from "./src/wysiwyv-core";
|
|
160
|
+
// or, if you want to include all default plugins:
|
|
161
|
+
// import { makeWysiwyv } from "./src/wysiwyv";
|
|
162
|
+
|
|
163
|
+
import usSsnWyvern from "./src/hooks/us-ssn";
|
|
164
|
+
|
|
165
|
+
const wyv = makeWysiwyv({
|
|
166
|
+
plugins: [usSsnWyvern],
|
|
167
|
+
pluginSetups: {
|
|
168
|
+
"$us-ssn": {
|
|
169
|
+
defaultFormat: "###-##-####",
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const result = wyv.validate(myExpected, myCandidate);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
[↑ top](#plugin-authoring)
|
package/README-USAGE.md
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
| [README](README.md) | _USAGE_ | [INCLUDED PLUGINS](README-INCLUDED-PLUGINS.md) | [PLUGIN AUTHORING](README-PLUGIN-AUTHORING.md) | [INTEGRATION](README-INTEGRATION.md) |
|
|
2
|
+
| :------------------ | :------ | :--------------------------------------------- | :--------------------------------------------- | :----------------------------------- |
|
|
3
|
+
|
|
4
|
+
# WYSIWYV - Usage
|
|
5
|
+
|
|
6
|
+
- [Basics](#basics)
|
|
7
|
+
- [Result Format](#result-format)
|
|
8
|
+
- [Literal Matching](#literal-matching)
|
|
9
|
+
- [Plugins](#plugins)
|
|
10
|
+
- [Escaping](#escaping)
|
|
11
|
+
- [Plugin Parameters](#plugin-parameters)
|
|
12
|
+
- [Initialization](#initialization)
|
|
13
|
+
- [Template Parameters](#template-parameters)
|
|
14
|
+
- [Composition](#composition)
|
|
15
|
+
|
|
16
|
+
## Basics
|
|
17
|
+
|
|
18
|
+
1. Get your data.
|
|
19
|
+
|
|
20
|
+
Any data will do; it does not need to be an object at root.
|
|
21
|
+
|
|
22
|
+
`const data = {name: "Joe"};`
|
|
23
|
+
|
|
24
|
+
2. Write your template.
|
|
25
|
+
|
|
26
|
+
You can start by copy-pasting your data, if that helps.
|
|
27
|
+
|
|
28
|
+
Anything that should be dynamic, replace with a matcher.
|
|
29
|
+
|
|
30
|
+
`const template = {name: "$string"};`
|
|
31
|
+
|
|
32
|
+
3. Make your validator
|
|
33
|
+
|
|
34
|
+
You can select custom plugin sets, add your own, or initialize data for smart plugins
|
|
35
|
+
|
|
36
|
+
`const wyv = makeWysiwyv();`
|
|
37
|
+
|
|
38
|
+
4. Run the validation
|
|
39
|
+
|
|
40
|
+
`const result = wyv.validate(template, data);`
|
|
41
|
+
|
|
42
|
+
5. Enjoy Success!
|
|
43
|
+
|
|
44
|
+
`console.log(result.success);`
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
true
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
[↑ top](#wysiwyv---usage)
|
|
51
|
+
|
|
52
|
+
## Result Format
|
|
53
|
+
|
|
54
|
+
The result format is consistent whether successful, partially successful, or if a validation is fully rejected.
|
|
55
|
+
|
|
56
|
+
Every error includes a detailed message and a full path to where in the comparison the error was encountered.
|
|
57
|
+
|
|
58
|
+
- Object keys look like `.key`
|
|
59
|
+
- Array indexes look like `[10]`
|
|
60
|
+
|
|
61
|
+
On success:
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
{ success: true, errors: [] }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
On errors:
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
{
|
|
71
|
+
success: false,
|
|
72
|
+
errors:[
|
|
73
|
+
{
|
|
74
|
+
message: "Type: Expected 'number', got value 'old enough'",
|
|
75
|
+
path: ".age"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
message: "Value: Expected 'Sales', got value 'Delivery'",
|
|
79
|
+
path: ".jobs[1].dept.name"
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
[↑ top](#wysiwyv---usage)
|
|
86
|
+
|
|
87
|
+
## Literal Matching
|
|
88
|
+
|
|
89
|
+
The core behavior is just a simple deep-comparison matcher. Your data should look exactly like your template. Only an exact match will achieve `success`!
|
|
90
|
+
|
|
91
|
+
All templates must consist only of values representable by JSON; the data being matched can be arbitrary, but plugins must be used to validate anything not representable in JSON.
|
|
92
|
+
|
|
93
|
+
### Valid Types:
|
|
94
|
+
|
|
95
|
+
- _scalars_: null, number, string, boolean
|
|
96
|
+
- _arrays_ with elements of any valid type
|
|
97
|
+
- _objects_ with string keys and values of any valid type
|
|
98
|
+
|
|
99
|
+
### Example
|
|
100
|
+
|
|
101
|
+
data:
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
{
|
|
105
|
+
name: "Jawn Deaux",
|
|
106
|
+
jobs:[
|
|
107
|
+
{
|
|
108
|
+
title: "CEO",
|
|
109
|
+
dept: {
|
|
110
|
+
name: "Delivery"
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
title: "Director",
|
|
115
|
+
dept: {
|
|
116
|
+
name: "Sales"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
template:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"name": "Jawn Deaux",
|
|
128
|
+
"jobs": [
|
|
129
|
+
{
|
|
130
|
+
"title": "CEO",
|
|
131
|
+
"dept": {
|
|
132
|
+
"name": "Delivery"
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"title": "Director",
|
|
137
|
+
"dept": {
|
|
138
|
+
"name": "Sales"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
[↑ top](#wysiwyv---usage)
|
|
146
|
+
|
|
147
|
+
## Plugins
|
|
148
|
+
|
|
149
|
+
Plugins are where everything gets flexible. You can assess data types, sub-object shapes, array length, array elements, combine assessments with boolean logic, or write your own plugins for anything we didn't think of.
|
|
150
|
+
|
|
151
|
+
We will gladly include or link any plugins you care to share!
|
|
152
|
+
|
|
153
|
+
- Every plugin is identified by a `key` -- a string starting with a dollar sign (`$`). For example: `$and`, `$uuid`.
|
|
154
|
+
|
|
155
|
+
- Plugins are used in place of values in the matching template.
|
|
156
|
+
|
|
157
|
+
static:
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"id": "John"
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
loose:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"id": "$string"
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
[↑ top](#wysiwyv---usage)
|
|
174
|
+
|
|
175
|
+
## Escaping
|
|
176
|
+
|
|
177
|
+
Sometimes you really will want to match a string starting with a dollar sign. Just use a second dollar sign (`$$`) in your matching string to indicate it's not meant to be a plugin key.
|
|
178
|
+
|
|
179
|
+
data (starts with single dollar):
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"name": "$heka"
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
matching template (double dollar):
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"name": "$$heka"
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
[↑ top](#wysiwyv---usage)
|
|
196
|
+
|
|
197
|
+
## Plugin Parameters
|
|
198
|
+
|
|
199
|
+
When referencing a plugin, it can be passed 0, 1, or more parameters.
|
|
200
|
+
|
|
201
|
+
0 Params: Just use the plugin key as a string.
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"datum-list": "$array"
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
1 Param: Use a simple object with plugin as key and parameter as value. The plugin's docs will tell you what argument a single parameter will correspond to, if there are multiple.
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
{
|
|
213
|
+
// default arg is $uuid.$version
|
|
214
|
+
"id": { "$uuid": 4 },
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
More Params: Use a nested object. The outer just has the plugin key, and the inner has the parameters paired with named keys.
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"datum-list": {
|
|
223
|
+
"$array": {
|
|
224
|
+
"$minlength": 2,
|
|
225
|
+
"$maxlength": 10
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
[↑ top](#wysiwyv---usage)
|
|
232
|
+
|
|
233
|
+
## Initialization
|
|
234
|
+
|
|
235
|
+
Plugins can accept pre-filled setup objects. For example, when matching values, you could have some that you got from another source during a test, but after setting up the validation template.
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
const wyv = makeWysiwyv({
|
|
239
|
+
pluginSetups: {
|
|
240
|
+
$val: {
|
|
241
|
+
entityId: 24601,
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const data = {
|
|
247
|
+
id: 24601,
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const template = {
|
|
251
|
+
id: { $val: "entityId" },
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const result = wyv.validate(template, data);
|
|
255
|
+
|
|
256
|
+
// result.success === true
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
[↑ top](#wysiwyv---usage)
|
|
260
|
+
|
|
261
|
+
## Template Parameters
|
|
262
|
+
|
|
263
|
+
### (Meta-Plugins)
|
|
264
|
+
|
|
265
|
+
Some plugins accept matcher templates as parameters.
|
|
266
|
+
|
|
267
|
+
- `$and` and `$or` each take an array of templates to evaluate
|
|
268
|
+
- `$array.$each` is an optional template that every element of an array must individually match
|
|
269
|
+
- `$object.$eachElement` and `$plainobject.$eachElement` are similar, every value (regardless of key) must match
|
|
270
|
+
- `$object.$partial` and `$plainobject.$partial` are optional (mutually exclusive with `.$eachElement`) templates. Every k-v present in the provided template must be present in the candidate data being evaluated. Extra candidate-object keys beyond those listed are ignored (accepted).
|
|
271
|
+
|
|
272
|
+
### $or template:
|
|
273
|
+
|
|
274
|
+
```json
|
|
275
|
+
{
|
|
276
|
+
"department": {
|
|
277
|
+
"$or": ["Sales", "Marketing", "Ideation", "Finance"]
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### $object.$partial
|
|
283
|
+
|
|
284
|
+
#### candidate data:
|
|
285
|
+
|
|
286
|
+
```js
|
|
287
|
+
{
|
|
288
|
+
id: 24680,
|
|
289
|
+
name: "Jane Doe-Smith",
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
#### matcher template:
|
|
294
|
+
|
|
295
|
+
```json
|
|
296
|
+
{
|
|
297
|
+
"$object": {
|
|
298
|
+
"$partial": {
|
|
299
|
+
"id": 24680
|
|
300
|
+
// ignores .name
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
[↑ top](#wysiwyv---usage)
|
|
307
|
+
|
|
308
|
+
## Composition
|
|
309
|
+
|
|
310
|
+
Any Meta-Plugins can be nested and alternated arbitrarily with other plugins and with core behavior. Because control is handed back to the engine for each node in descent, nested claims are treated identically to ones in the base tree.
|
|
311
|
+
|
|
312
|
+
data:
|
|
313
|
+
|
|
314
|
+
```js
|
|
315
|
+
{
|
|
316
|
+
people: [
|
|
317
|
+
{
|
|
318
|
+
name: "Jon Deo",
|
|
319
|
+
title: "CEO",
|
|
320
|
+
salary: 800_000,
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
name: "Don Jo",
|
|
324
|
+
department: "Burger-Flippin",
|
|
325
|
+
wage: 3.75,
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
template:
|
|
332
|
+
|
|
333
|
+
```js
|
|
334
|
+
{
|
|
335
|
+
people: {
|
|
336
|
+
$array: { $each: {
|
|
337
|
+
$and: [
|
|
338
|
+
{ $object: { $partial:
|
|
339
|
+
{ name: "$string" }
|
|
340
|
+
}},
|
|
341
|
+
{ $or: [
|
|
342
|
+
{ name: "$any", title: "$string", salary: "$number" },
|
|
343
|
+
{ name: "$any", department: "$string", wage: "$number" },
|
|
344
|
+
]},
|
|
345
|
+
],
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
[↑ top](#wysiwyv---usage)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wysiwyv",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"main": "./dist/wysiwyv.js",
|
|
5
5
|
"devDependencies": {
|
|
6
6
|
"@eslint/js": "^10.0.1",
|
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
"peerDependencies": {
|
|
16
16
|
"typescript": "^5"
|
|
17
17
|
},
|
|
18
|
+
"peerDependenciesMeta": {
|
|
19
|
+
"typescript": {
|
|
20
|
+
"optional": true
|
|
21
|
+
}
|
|
22
|
+
},
|
|
18
23
|
"exports": {
|
|
19
24
|
".": {
|
|
20
25
|
"types": "./dist/wysiwyv.d.ts",
|
|
@@ -28,7 +33,7 @@
|
|
|
28
33
|
"files": [
|
|
29
34
|
"dist",
|
|
30
35
|
"LICENSE",
|
|
31
|
-
"README
|
|
36
|
+
"README*.md"
|
|
32
37
|
],
|
|
33
38
|
"jsdelivr": "./dist/wysiwyv.min.js",
|
|
34
39
|
"scripts": {
|