schema-shield 0.0.5 → 1.0.0
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.md +194 -66
- package/dist/formats.d.ts.map +1 -1
- package/dist/index.d.ts +13 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +705 -348
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/index.mjs +705 -348
- package/dist/keywords/array-keywords.d.ts.map +1 -1
- package/dist/keywords/object-keywords.d.ts.map +1 -1
- package/dist/keywords/other-keywords.d.ts.map +1 -1
- package/dist/keywords/string-keywords.d.ts.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +6 -5
- package/dist/utils.d.ts.map +1 -1
- package/lib/formats.ts +125 -130
- package/lib/index.ts +247 -96
- package/lib/keywords/array-keywords.ts +60 -46
- package/lib/keywords/object-keywords.ts +155 -53
- package/lib/keywords/other-keywords.ts +68 -28
- package/lib/keywords/string-keywords.ts +32 -6
- package/lib/types.ts +1 -13
- package/lib/utils.ts +202 -44
- package/package.json +2 -2
- package/tsconfig.json +4 -4
package/README.md
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
|
-
# SchemaShield
|
|
1
|
+
# SchemaShield 🛡️
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Validation for Modern Architectures: Secure, Stack-Safe, and Domain-Aware.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
SchemaShield is a secure interpreter for JSON Schema engineered for strict environments and complex domain logic. It prioritizes **architectural stability** and **developer experience** over raw synthetic throughput.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- **Security by Design:** Zero Code Generation. Fully compatible with strict Content Security Policy (CSP).
|
|
8
|
+
- **Production Stability:** Stack-safe traversal. Designed to minimize stack usage and reduce the risk of stack overflows, even on deeply nested schemas.
|
|
9
|
+
- **Domain Integrated:** Validates runtime objects (Classes, Dates, Streams) alongside serialized JSON.
|
|
10
|
+
- **Transparent Debugging:** Pure JavaScript execution means clean stack traces and no black-box generated code.
|
|
8
11
|
|
|
9
12
|
## Table of Contents
|
|
10
13
|
|
|
11
14
|
- [Table of Contents](#table-of-contents)
|
|
12
|
-
- [
|
|
15
|
+
- [Why SchemaShield?](#why-schemashield)
|
|
16
|
+
- [Comparison with Other Approaches](#comparison-with-other-approaches)
|
|
13
17
|
- [Usage](#usage)
|
|
18
|
+
- [Performance](#performance)
|
|
19
|
+
- [Understanding Performance Context](#understanding-performance-context)
|
|
20
|
+
- [1. Modern Runtimes (Bun)](#1-modern-runtimes-bun)
|
|
21
|
+
- [2. Standard Runtimes (Node.js)](#2-standard-runtimes-nodejs)
|
|
22
|
+
- [Edge \& Serverless Ready](#edge--serverless-ready)
|
|
23
|
+
- [Features](#features)
|
|
24
|
+
- [Security Philosophy: Hermetic Validation](#security-philosophy-hermetic-validation)
|
|
25
|
+
- [Why No Remote References?](#why-no-remote-references)
|
|
14
26
|
- [No Code Generation](#no-code-generation)
|
|
15
27
|
- [Error Handling](#error-handling)
|
|
16
28
|
- [Adding Custom Types](#adding-custom-types)
|
|
@@ -23,7 +35,8 @@ Despite its feature-rich and easy extendable nature, SchemaShield is designed to
|
|
|
23
35
|
- [Method Signature](#method-signature-2)
|
|
24
36
|
- [Example: Adding a Custom Keyword](#example-adding-a-custom-keyword)
|
|
25
37
|
- [Complex example: Adding a Custom Keyword that uses the instance](#complex-example-adding-a-custom-keyword-that-uses-the-instance)
|
|
26
|
-
- [
|
|
38
|
+
- [Supported Formats](#supported-formats)
|
|
39
|
+
- [Validating Runtime Objects](#validating-runtime-objects)
|
|
27
40
|
- [More on Error Handling](#more-on-error-handling)
|
|
28
41
|
- [ValidationError Properties](#validationerror-properties)
|
|
29
42
|
- [Get the path to the error location](#get-the-path-to-the-error-location)
|
|
@@ -32,22 +45,39 @@ Despite its feature-rich and easy extendable nature, SchemaShield is designed to
|
|
|
32
45
|
- [Immutable Mode](#immutable-mode)
|
|
33
46
|
- [TypeScript Support](#typescript-support)
|
|
34
47
|
- [Known Limitations](#known-limitations)
|
|
35
|
-
- [
|
|
36
|
-
- [
|
|
48
|
+
- [1. Dynamic ID Scope Resolution (Scope Alteration)](#1-dynamic-id-scope-resolution-scope-alteration)
|
|
49
|
+
- [2. Unicode Length Validation](#2-unicode-length-validation)
|
|
37
50
|
- [Testing](#testing)
|
|
38
51
|
- [Contribute](#contribute)
|
|
39
52
|
- [Legal](#legal)
|
|
40
53
|
|
|
41
|
-
##
|
|
54
|
+
## Why SchemaShield?
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
|
|
56
|
+
Most validators optimize for "operations per second" in synthetic benchmarks, often sacrificing security, stability, or maintainability. SchemaShield optimizes for the **Total Cost of Ownership** of your software.
|
|
57
|
+
|
|
58
|
+
| Feature | JIT Compilers | SchemaShield | Why it matters |
|
|
59
|
+
| :------------------- | :------------------------------------------------------------------- | :--------------------------------------------------------- | :-------------------------------------------------------------------------------------- |
|
|
60
|
+
| **Security Model** | **Reactive**<br>(Requires config to prevent Prototype Pollution/DoS) | **Preventive**<br>(Hermetic & Immutable by design) | "Secure by default" prevents human error and vulnerabilities in production. |
|
|
61
|
+
| **Debug Experience** | **Black Box**<br>(Debugs opaque generated strings/code) | **White Box**<br>(Standard JS stack traces) | Drastically reduces time-to-fix when validation fails in complex logic. |
|
|
62
|
+
| **Stability** | **Recursive**<br>(Risk of Stack Overflow on deep data) | **Flat Loop**<br>(Constant memory usage) | Protects your server availability against malicious deep-payload attacks. |
|
|
63
|
+
| **Domain Logic** | **Disconnected**<br>(Hard to validate Class Instances/State) | **Integrated**<br>(Native `instanceof` & state validation) | Unifies DTO validation and Business Rules, eliminating "spaghetti code" in controllers. |
|
|
64
|
+
| **Ecosystem** | **Fragmented**<br>(Requires plugins for errors/formats) | **Cohesive**<br>(Advanced error trees & formats built-in) | Reduces dependency fatigue and maintenance burden. |
|
|
65
|
+
|
|
66
|
+
> **The Trade-off:** While JIT compilers can be faster in raw throughput on V8 (Node.js), SchemaShield offers a balanced architecture where validation is never the bottleneck in real-world I/O bound applications (Database/Network APIs).
|
|
67
|
+
|
|
68
|
+
### Comparison with Other Approaches
|
|
69
|
+
|
|
70
|
+
| Feature | SchemaShield | JIT Compilers | Classic Interpreters |
|
|
71
|
+
| :---------------------------- | :-------------------------------------------------- | :------------------------------- | :-------------------- |
|
|
72
|
+
| **Architecture** | **Secure Flat Interpreter** | JIT Compiler (eval/new Function) | Recursive Interpreter |
|
|
73
|
+
| **Relative Speed** | **High (~60%)** | Reference (100%) | Low (1% - 20%) |
|
|
74
|
+
| **CSP Compliance** | **Native (100% Safe)** | Requires Build Config | Variable |
|
|
75
|
+
| **Edge Ready** | **Native** | Complex Setup | Variable |
|
|
76
|
+
| **Stack Safety** | **Minimized stack usage (non-recursive core loop)** | Risk of Overflow | Risk of Overflow |
|
|
77
|
+
| **Class Instance Validation** | **Native** | No | No |
|
|
78
|
+
| **Debug Experience** | **Clean Stack Trace** | Opaque Generated Code | Variable |
|
|
79
|
+
|
|
80
|
+
> **Note:** Stack Safety refers to the risk of stack overflow errors when validating deeply nested data structures. SchemaShield's flat interpreter design minimizes this risk in its core validation loop. Keep in mind that custom keywords can still introduce recursion if not implemented carefully.
|
|
51
81
|
|
|
52
82
|
## Usage
|
|
53
83
|
|
|
@@ -56,7 +86,7 @@ Despite its feature-rich and easy extendable nature, SchemaShield is designed to
|
|
|
56
86
|
```bash
|
|
57
87
|
npm install schema-shield
|
|
58
88
|
# or
|
|
59
|
-
|
|
89
|
+
bun add schema-shield
|
|
60
90
|
```
|
|
61
91
|
|
|
62
92
|
**2. Import the SchemaShield class**
|
|
@@ -70,10 +100,11 @@ const { SchemaShield } = require("schema-shield");
|
|
|
70
100
|
**3. Instantiate the SchemaShield class**
|
|
71
101
|
|
|
72
102
|
```javascript
|
|
73
|
-
const schemaShield = new SchemaShield(
|
|
103
|
+
const schemaShield = new SchemaShield();
|
|
74
104
|
```
|
|
75
105
|
|
|
76
|
-
**`immutable`** (optional): Set to `true` to ensure that input data remains unmodified during validation. Default is `false` for better performance.
|
|
106
|
+
- **`immutable`** (optional): Set to `true` to ensure that input data remains unmodified during validation. Default is `false` for better performance.
|
|
107
|
+
- **`failFast`** (optional): Set to `false` to receive detailed error objects on validation failure. Default is `true` for lightweight failure indication.
|
|
77
108
|
|
|
78
109
|
**3.5. Add custom types, keywords, and formats (optional)**
|
|
79
110
|
|
|
@@ -128,9 +159,92 @@ if (validationResult.valid) {
|
|
|
128
159
|
**`validationResult`**: Contains the following properties:
|
|
129
160
|
|
|
130
161
|
- `data`: The validated (and potentially modified) data.
|
|
131
|
-
- `error`: A `ValidationError` instance if validation failed, otherwise null
|
|
162
|
+
- `error`: A `ValidationError` instance if validation failed (when `failFast: false`), `true` if validation failed in fail-fast mode, otherwise `null`.
|
|
132
163
|
- `valid`: true if validation was successful, otherwise false.
|
|
133
164
|
|
|
165
|
+
> Note: When SchemaShield is instantiated with `{ failFast: false }`, `validationResult.error` will contain a detailed `ValidationError` instance if validation fails. In `failFast: true` mode, `error` is just `true` as a lightweight sentinel.
|
|
166
|
+
|
|
167
|
+
**6. Intelligent Defaults**
|
|
168
|
+
|
|
169
|
+
SchemaShield applies `default` values only when necessary to fulfill the schema contract. A `default` value is injected if and only if:
|
|
170
|
+
|
|
171
|
+
1. The property is missing in the input data.
|
|
172
|
+
2. The property is marked as `required` in the schema.
|
|
173
|
+
|
|
174
|
+
## Performance
|
|
175
|
+
|
|
176
|
+
SchemaShield is engineered with a **Flat Loop Interpreter** architecture. This design choice implies zero compilation overhead, making it exceptionally stable and significantly faster in modern runtimes.
|
|
177
|
+
|
|
178
|
+
### Understanding Performance Context
|
|
179
|
+
|
|
180
|
+
In real-world applications, validation latency is often negligible compared to Network I/O (~20-100ms) or Database queries (~5-50ms).
|
|
181
|
+
|
|
182
|
+
SchemaShield is engineered to be **"Elite Fast" (Sufficiently Fast)**:
|
|
183
|
+
|
|
184
|
+
- **Zero Compilation Overhead:** Ideal for Serverless/Edge cold-starts.
|
|
185
|
+
- **Predictable Throughput:** Consistent performance regardless of schema complexity.
|
|
186
|
+
|
|
187
|
+
While JIT compilers may show higher numbers in micro-benchmarks on V8, SchemaShield processes thousands of requests per second—more than enough for high-traffic APIs—without the architectural risks of code generation.
|
|
188
|
+
|
|
189
|
+
### 1. Modern Runtimes (Bun)
|
|
190
|
+
|
|
191
|
+
In runtimes using JavaScriptCore (like Bun), SchemaShield outperforms JIT compilers because it avoids the heavy cost of runtime code generation and optimization overhead.
|
|
192
|
+
|
|
193
|
+
| Validator | Relative Speed | Context |
|
|
194
|
+
| :----------------- | :------------- | :----------- |
|
|
195
|
+
| **SchemaShield** | **100%** | **Fastest** |
|
|
196
|
+
| ajv | ~55% | JIT Compiler |
|
|
197
|
+
| @exodus/schemasafe | ~12% | Interpreter |
|
|
198
|
+
| jsonschema | ~2% | Legacy |
|
|
199
|
+
|
|
200
|
+
### 2. Standard Runtimes (Node.js)
|
|
201
|
+
|
|
202
|
+
In V8-based environments (Node.js), SchemaShield maintains elite performance for a secure interpreter, being roughly **50x faster** than legacy libraries.
|
|
203
|
+
|
|
204
|
+
| Validator | Relative Speed | Context |
|
|
205
|
+
| :----------------- | :------------- | :------------------ |
|
|
206
|
+
| ajv | 100% | Reference (JIT) |
|
|
207
|
+
| @exodus/schemasafe | ~75% | Interpreter |
|
|
208
|
+
| **SchemaShield** | **~60%** | **Secure Standard** |
|
|
209
|
+
| jsonschema | ~1% | Legacy |
|
|
210
|
+
|
|
211
|
+
**Key Takeaway:** SchemaShield delivers consistent high performance in Node.js without the security risks, memory leaks, or "cold start" latency associated with code generation.
|
|
212
|
+
|
|
213
|
+
## Edge & Serverless Ready
|
|
214
|
+
|
|
215
|
+
SchemaShield is designed to run seamlessly in restrictive environments like **Cloudflare Workers**, **Vercel Edge Functions**, **Deno Deploy**, and **Bun**.
|
|
216
|
+
|
|
217
|
+
- **Zero Dependencies:** No strict reliance on Node.js built-ins.
|
|
218
|
+
- **CSP Compliant:** Works in environments where `eval()` and `new Function()` are banned for security.
|
|
219
|
+
- **Instant Startup:** No compilation overhead, minimizing "cold start" latency in Serverless functions.
|
|
220
|
+
|
|
221
|
+
## Features
|
|
222
|
+
|
|
223
|
+
- Supports draft-06 and draft-07 of the [JSON Schema](https://json-schema.org/) specification.
|
|
224
|
+
- Full support for internal references ($ref) and anchors ($id).
|
|
225
|
+
- No Code Generation for Enhanced Safety and Validation Flexibility.
|
|
226
|
+
- Custom type, keyword, and format validators.
|
|
227
|
+
- Runtime object validation: first-class support for business logic checks (type checking and schema validation).
|
|
228
|
+
- Immutable mode for data protection.
|
|
229
|
+
- Lightweight and fast.
|
|
230
|
+
- Easy to use and extend.
|
|
231
|
+
- No dependencies.
|
|
232
|
+
- TypeScript support.
|
|
233
|
+
|
|
234
|
+
## Security Philosophy: Hermetic Validation
|
|
235
|
+
|
|
236
|
+
SchemaShield adopts a **Zero Trust** and **Hermetic Architecture** approach. Unlike validators that allow runtime network access, SchemaShield is strictly synchronous and offline by design.
|
|
237
|
+
|
|
238
|
+
### Why No Remote References?
|
|
239
|
+
|
|
240
|
+
Allowing a validator to fetch schemas from remote URLs (`$ref: "https://..."`) at runtime introduces critical security vectors and stability issues:
|
|
241
|
+
|
|
242
|
+
1. **SSRF (Server-Side Request Forgery):** Prevents attackers from manipulating schemas to force internal network scanning or access metadata services.
|
|
243
|
+
2. **Supply Chain Attacks:** Eliminates the risk of a remote schema being silently compromised, which could alter validation logic without code deployment.
|
|
244
|
+
3. **Deterministic Reliability:** Validation never fails due to network latency, DNS issues, or third-party server downtime.
|
|
245
|
+
|
|
246
|
+
**Recommendation:** Treat schemas as **code dependencies**, not dynamic assets. Download and bundle remote schemas locally during your build process to ensure immutable, versioned, and audit-ready validation.
|
|
247
|
+
|
|
134
248
|
## No Code Generation
|
|
135
249
|
|
|
136
250
|
Unlike some other validation libraries that rely on code generation to achieve fast performance, SchemaShield does not use code generation.
|
|
@@ -146,18 +260,21 @@ class CustomDate extends Date {}
|
|
|
146
260
|
schemaShield.addType("custom-date-class", (data) => data instanceof CustomDate);
|
|
147
261
|
```
|
|
148
262
|
|
|
149
|
-
You can see a full example of this in the [No Code Generation opened possibilities](#no-code-generation-opened-possibilities) section.
|
|
150
|
-
|
|
151
263
|
## Error Handling
|
|
152
264
|
|
|
153
|
-
SchemaShield provides comprehensive error handling for schema validation. When a validation error occurs
|
|
265
|
+
SchemaShield provides comprehensive error handling for schema validation. When a validation error occurs:
|
|
266
|
+
|
|
267
|
+
- If the instance was created with `{ failFast: true }` (the default), the `error` property will be `true` as a lightweight sentinel indicating that validation failed.
|
|
268
|
+
- If the instance was created with `{ failFast: false }`, the `error` property will contain a `ValidationError` instance with rich debugging information.
|
|
269
|
+
|
|
270
|
+
This error object has the `getPath()` method, which is particularly useful for quickly identifying the location of an error in both the schema and the data.
|
|
154
271
|
|
|
155
272
|
**Example:**
|
|
156
273
|
|
|
157
274
|
```javascript
|
|
158
275
|
import { SchemaShield } from "schema-shield";
|
|
159
276
|
|
|
160
|
-
const schemaShield = new SchemaShield();
|
|
277
|
+
const schemaShield = new SchemaShield({ failFast: false });
|
|
161
278
|
|
|
162
279
|
const schema = {
|
|
163
280
|
type: "object",
|
|
@@ -218,7 +335,7 @@ In this example, we'll add a custom type called age that validates if a given nu
|
|
|
218
335
|
```javascript
|
|
219
336
|
import { SchemaShield } from "schema-shield";
|
|
220
337
|
|
|
221
|
-
const schemaShield = new SchemaShield();
|
|
338
|
+
const schemaShield = new SchemaShield({ failFast: false });
|
|
222
339
|
|
|
223
340
|
// Custom type 'age' validator function
|
|
224
341
|
const ageValidator = (data) => {
|
|
@@ -275,9 +392,9 @@ addFormat(name: string, validator: FormatFunction, overwrite?: boolean): void;
|
|
|
275
392
|
In this example, we'll add a custom format called ssn that validates if a given string is a valid U.S. Social Security Number (SSN).
|
|
276
393
|
|
|
277
394
|
```javascript
|
|
278
|
-
import { SchemaShield } from "
|
|
395
|
+
import { SchemaShield } from "schema-shield";
|
|
279
396
|
|
|
280
|
-
const schemaShield = new SchemaShield();
|
|
397
|
+
const schemaShield = new SchemaShield({ failFast: false });
|
|
281
398
|
|
|
282
399
|
// Custom format 'ssn' validator function
|
|
283
400
|
const ssnValidator = (data) => {
|
|
@@ -314,21 +431,21 @@ if (validationResult.valid) {
|
|
|
314
431
|
|
|
315
432
|
## Adding Custom Keywords
|
|
316
433
|
|
|
317
|
-
SchemaShield allows you to add custom keywords for validation using the addKeyword method. This is the most powerful method for adding custom validation logic to SchemaShield because it allows to interact with the entire schema and data being validated at the level of the keyword.
|
|
434
|
+
SchemaShield allows you to add custom keywords for validation using the `addKeyword` method. This is the most powerful method for adding custom validation logic to SchemaShield because it allows you to interact with the entire schema and data being validated at the level of the keyword.
|
|
318
435
|
|
|
319
436
|
### Method Signature
|
|
320
437
|
|
|
321
438
|
```javascript
|
|
322
|
-
type Result = void | ValidationError;
|
|
439
|
+
type Result = void | ValidationError | true;
|
|
323
440
|
|
|
324
441
|
interface DefineErrorOptions {
|
|
325
442
|
item?: any; // Final item in the path
|
|
326
|
-
cause?: ValidationError; // Cause of the error
|
|
443
|
+
cause?: ValidationError | true; // Cause of the error (or true in failFast mode)
|
|
327
444
|
data?: any; // Data that caused the error
|
|
328
445
|
}
|
|
329
446
|
|
|
330
447
|
interface DefineErrorFunction {
|
|
331
|
-
(message: string, options?: DefineErrorOptions): ValidationError;
|
|
448
|
+
(message: string, options?: DefineErrorOptions): ValidationError | true;
|
|
332
449
|
}
|
|
333
450
|
|
|
334
451
|
interface ValidateFunction {
|
|
@@ -349,8 +466,9 @@ interface TypeFunction {
|
|
|
349
466
|
}
|
|
350
467
|
|
|
351
468
|
declare class SchemaShield {
|
|
352
|
-
constructor(
|
|
353
|
-
|
|
469
|
+
constructor(options?: {
|
|
470
|
+
immutable?: boolean;
|
|
471
|
+
failFast?: boolean;
|
|
354
472
|
});
|
|
355
473
|
compile(schema: any): Validator;
|
|
356
474
|
addType(name: string, validator: TypeFunction, overwrite?: boolean): void;
|
|
@@ -377,7 +495,7 @@ addKeyword(name: string, validator: KeywordFunction, overwrite?: boolean): void;
|
|
|
377
495
|
```
|
|
378
496
|
|
|
379
497
|
- `name`: The name of the custom keyword. This should be a unique string that does not conflict with existing keywords.
|
|
380
|
-
- `validator`: A `KeywordFunction` that takes four arguments: `schema`, `data`, `defineError`, and `instance` (The SchemaShield instance that is currently running the validation). The function should not return anything if the data is valid for the custom keyword, and should return a `ValidationError` instance if the data is invalid
|
|
498
|
+
- `validator`: A `KeywordFunction` that takes four arguments: `schema`, `data`, `defineError`, and `instance` (The SchemaShield instance that is currently running the validation). The function should not return anything if the data is valid for the custom keyword, and should return a `ValidationError` instance if the data is invalid when `failFast` is `false`, or `true` when `failFast` is `true`.
|
|
381
499
|
- `overwrite` (optional): Set to true to overwrite an existing keyword with the same name. Default is false. If set to false and a keyword with the same name already exists, an error will be thrown.
|
|
382
500
|
|
|
383
501
|
#### About the `defineError` Function
|
|
@@ -386,10 +504,13 @@ Take into account that the error must be generated using the `defineError` funct
|
|
|
386
504
|
|
|
387
505
|
- `message`: A string that describes the validation error.
|
|
388
506
|
- `options`: An optional object with properties that provide more context for the error:
|
|
507
|
+
|
|
389
508
|
- `item`?: An optional value representing the final item in the path where the validation error occurred. (e.g. index of an array item)
|
|
390
|
-
- `cause`?: An optional `ValidationError` that represents the cause of the current error.
|
|
509
|
+
- `cause`?: An optional `ValidationError` (or `true` in fail-fast mode) that represents the cause of the current error.
|
|
391
510
|
- `data`?: An optional value representing the data that caused the validation error.
|
|
392
511
|
|
|
512
|
+
When the SchemaShield instance is created with `failFast: true`, `defineError` returns `true` instead of a `ValidationError`, and keyword implementations should simply `return` whatever `defineError` gives them.
|
|
513
|
+
|
|
393
514
|
#### About the `instance` Argument
|
|
394
515
|
|
|
395
516
|
The `instance` argument is the SchemaShield instance that is currently running the validation. This can be used to access to other `types`, `keywords` or `formats` that have been added to the instance.
|
|
@@ -399,9 +520,9 @@ The `instance` argument is the SchemaShield instance that is currently running t
|
|
|
399
520
|
In this example, we'll add a custom keyword called divisibleBy that validates if a given number is divisible by a specified divisor.
|
|
400
521
|
|
|
401
522
|
```javascript
|
|
402
|
-
import { SchemaShield, ValidationError } from "
|
|
523
|
+
import { SchemaShield, ValidationError } from "schema-shield";
|
|
403
524
|
|
|
404
|
-
const schemaShield = new SchemaShield();
|
|
525
|
+
const schemaShield = new SchemaShield({ failFast: false });
|
|
405
526
|
|
|
406
527
|
// Custom keyword 'divisibleBy' validator function
|
|
407
528
|
const divisibleByValidator = (schema, data, defineError, instance) => {
|
|
@@ -450,7 +571,7 @@ In this example we'll add a custom keyword called `prefixedUsername` that will v
|
|
|
450
571
|
```javascript
|
|
451
572
|
import { SchemaShield, ValidationError } from "schema-shield";
|
|
452
573
|
|
|
453
|
-
const schemaShield = new SchemaShield();
|
|
574
|
+
const schemaShield = new SchemaShield({ failFast: false });
|
|
454
575
|
|
|
455
576
|
// Custom type validator: nonEmptyString
|
|
456
577
|
const nonEmptyStringValidator = (data) =>
|
|
@@ -479,7 +600,7 @@ const prefixedUsername = (schema, data, defineError, instance) => {
|
|
|
479
600
|
// Get the validators for the specified types and formats from the instance
|
|
480
601
|
// (if they exist)
|
|
481
602
|
const typeValidator = instance.getType(validType);
|
|
482
|
-
const
|
|
603
|
+
const prefixKeyword = instance.getKeyword(prefixValidator);
|
|
483
604
|
const formatValidator = instance.getFormat(validFormat);
|
|
484
605
|
|
|
485
606
|
for (let i = 0; i < data.length; i++) {
|
|
@@ -506,8 +627,8 @@ const prefixedUsername = (schema, data, defineError, instance) => {
|
|
|
506
627
|
}
|
|
507
628
|
|
|
508
629
|
// Validate that the data has the correct prefix if specified
|
|
509
|
-
if (
|
|
510
|
-
const error =
|
|
630
|
+
if (prefixKeyword) {
|
|
631
|
+
const error = prefixKeyword(schema, item, defineError, instance);
|
|
511
632
|
if (error) {
|
|
512
633
|
return defineError(`Invalid prefix: ${prefixValidator}`, {
|
|
513
634
|
cause: error,
|
|
@@ -546,16 +667,30 @@ if (validationResult.valid) {
|
|
|
546
667
|
}
|
|
547
668
|
```
|
|
548
669
|
|
|
549
|
-
##
|
|
670
|
+
## Supported Formats
|
|
671
|
+
|
|
672
|
+
SchemaShield includes built-in validators for the following formats:
|
|
550
673
|
|
|
551
|
-
|
|
674
|
+
- **Date & Time:** `date`, `time`, `date-time`, `duration`.
|
|
675
|
+
- **Email:** `email`, `idn-email`.
|
|
676
|
+
- **Hostnames:** `hostname`, `idn-hostname`.
|
|
677
|
+
- **IP Addresses:** `ipv4`, `ipv6`.
|
|
678
|
+
- **Resource Identifiers:** `uuid`, `uri`, `uri-reference`, `uri-template`, `iri`, `iri-reference`.
|
|
679
|
+
- **JSON Pointers:** `json-pointer`, `relative-json-pointer`.
|
|
680
|
+
- **Regex:** `regex`.
|
|
681
|
+
|
|
682
|
+
You can override any of these or add new ones using `schemaShield.addFormat`.
|
|
683
|
+
|
|
684
|
+
## Validating Runtime Objects
|
|
685
|
+
|
|
686
|
+
JSON Schema is traditionally for serialized JSON text. SchemaShield extends this concept to **JavaScript Objects**. It allows validation of class instances, Dates, and internal application state directly.
|
|
552
687
|
|
|
553
688
|
For example, imagine you have a custom class representing a project and another representing an employee. You could create a custom validator to ensure that only employees with the right qualifications are assigned to a specific project:
|
|
554
689
|
|
|
555
690
|
```javascript
|
|
556
691
|
import { SchemaShield, ValidationError } from "schema-shield";
|
|
557
692
|
|
|
558
|
-
const schemaShield = new SchemaShield();
|
|
693
|
+
const schemaShield = new SchemaShield({ failFast: false });
|
|
559
694
|
|
|
560
695
|
// Custom classes
|
|
561
696
|
class Project {
|
|
@@ -653,7 +788,7 @@ const dataToValidate = {
|
|
|
653
788
|
};
|
|
654
789
|
|
|
655
790
|
// Validate the data
|
|
656
|
-
const validationResult = validator(
|
|
791
|
+
const validationResult = validator(dataToValidate);
|
|
657
792
|
|
|
658
793
|
if (validationResult.valid) {
|
|
659
794
|
console.log("Assignment is valid:", validationResult.data);
|
|
@@ -666,9 +801,9 @@ In this example, SchemaShield safely accesses instances of custom classes and ut
|
|
|
666
801
|
|
|
667
802
|
## More on Error Handling
|
|
668
803
|
|
|
669
|
-
SchemaShield provides a `ValidationError` class to handle errors that occur during schema validation. When a validation error is encountered, a `ValidationError` instance is returned in the error property of the validation result
|
|
804
|
+
SchemaShield provides a `ValidationError` class to handle errors that occur during schema validation. When a validation error is encountered, a `ValidationError` instance is returned in the error property of the validation result when `failFast: false` is used; otherwise `error` is `true`.
|
|
670
805
|
|
|
671
|
-
This error instance uses the
|
|
806
|
+
This error instance uses the [`Error.cause`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) property. This allows you to analyze the whole error chain or retrieve the root cause of the error using the `getCause()`, `getTree()`, and `getPath()` methods.
|
|
672
807
|
|
|
673
808
|
### ValidationError Properties
|
|
674
809
|
|
|
@@ -692,7 +827,7 @@ You can use the `getPath` method to get the JSON Pointer path to the error locat
|
|
|
692
827
|
```javascript
|
|
693
828
|
import { SchemaShield } from "schema-shield";
|
|
694
829
|
|
|
695
|
-
const schemaShield = new SchemaShield();
|
|
830
|
+
const schemaShield = new SchemaShield({ failFast: false });
|
|
696
831
|
|
|
697
832
|
const schema = {
|
|
698
833
|
type: "object",
|
|
@@ -768,7 +903,7 @@ interface ErrorTree {
|
|
|
768
903
|
```javascript
|
|
769
904
|
import { SchemaShield } from "schema-shield";
|
|
770
905
|
|
|
771
|
-
const schemaShield = new SchemaShield();
|
|
906
|
+
const schemaShield = new SchemaShield({ failFast: false });
|
|
772
907
|
|
|
773
908
|
const schema = {
|
|
774
909
|
type: "object",
|
|
@@ -856,7 +991,7 @@ You can use the `getCause()` method to retrieve the root cause of a validation e
|
|
|
856
991
|
```javascript
|
|
857
992
|
import { SchemaShield } from "schema-shield";
|
|
858
993
|
|
|
859
|
-
const schemaShield = new SchemaShield();
|
|
994
|
+
const schemaShield = new SchemaShield({ failFast: false });
|
|
860
995
|
|
|
861
996
|
const schema = {
|
|
862
997
|
type: "object",
|
|
@@ -937,26 +1072,19 @@ With the built in TypeScript support, you can take advantage of features like st
|
|
|
937
1072
|
|
|
938
1073
|
## Known Limitations
|
|
939
1074
|
|
|
940
|
-
SchemaShield is
|
|
941
|
-
|
|
942
|
-
### Schema References and Schema Definitions
|
|
943
|
-
|
|
944
|
-
SchemaShield currently does not support schema references and schema definitions (i.e. `$ref` and `definitions`). This is planned to be addressed in future updates of SchemaShield.
|
|
1075
|
+
SchemaShield is optimized for local execution and strict security.
|
|
945
1076
|
|
|
946
|
-
|
|
1077
|
+
### 1. Dynamic ID Scope Resolution (Scope Alteration)
|
|
947
1078
|
|
|
948
|
-
|
|
1079
|
+
SchemaShield resolves references based on static JSON Pointers and unique IDs. It does not support changing the resolution base URI dynamically based on nested `$id` properties within sub-schemas.
|
|
949
1080
|
|
|
950
|
-
|
|
1081
|
+
- **Impact:** Rare edge-cases in draft-07 involving complex relative URI resolution inside nested scopes are not supported.
|
|
951
1082
|
|
|
952
|
-
|
|
1083
|
+
### 2. Unicode Length Validation
|
|
953
1084
|
|
|
954
|
-
|
|
955
|
-
- `idn-hostname`
|
|
956
|
-
- `iri`
|
|
957
|
-
- `iri-reference`
|
|
1085
|
+
SchemaShield validates `minLength` and `maxLength` based on JavaScript's `length` property (UTF-16 code units), not Unicode Code Points.
|
|
958
1086
|
|
|
959
|
-
|
|
1087
|
+
- **Impact:** Emoji or surrogate pairs may be counted as length 2.
|
|
960
1088
|
|
|
961
1089
|
## Testing
|
|
962
1090
|
|
|
@@ -994,5 +1122,5 @@ We appreciate your interest in contributing to SchemaShield and look forward to
|
|
|
994
1122
|
|
|
995
1123
|
## Legal
|
|
996
1124
|
|
|
997
|
-
Author: [Masquerade Circus](http://masquerade-circus.net).
|
|
1125
|
+
Author: [Masquerade Circus](http://masquerade-circus.net).
|
|
998
1126
|
License [Apache-2.0](https://opensource.org/licenses/Apache-2.0)
|
package/dist/formats.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formats.d.ts","sourceRoot":"","sources":["../lib/formats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"formats.d.ts","sourceRoot":"","sources":["../lib/formats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAwCzC,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,KAAK,CAoM1D,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
/****************** Path: lib/index.ts ******************/
|
|
1
2
|
import { DefineErrorFunction, ValidationError } from "./utils";
|
|
2
|
-
export
|
|
3
|
+
export { ValidationError } from "./utils";
|
|
4
|
+
export { deepClone } from "./utils";
|
|
5
|
+
export type Result = void | ValidationError | true;
|
|
3
6
|
export interface KeywordFunction {
|
|
4
7
|
(schema: CompiledSchema, data: any, defineError: DefineErrorFunction, instance: SchemaShield): Result;
|
|
5
8
|
}
|
|
@@ -19,7 +22,7 @@ export interface CompiledSchema {
|
|
|
19
22
|
export interface Validator {
|
|
20
23
|
(data: any): {
|
|
21
24
|
data: any;
|
|
22
|
-
error: ValidationError | null;
|
|
25
|
+
error: ValidationError | null | true;
|
|
23
26
|
valid: boolean;
|
|
24
27
|
};
|
|
25
28
|
compiledSchema: CompiledSchema;
|
|
@@ -29,8 +32,12 @@ export declare class SchemaShield {
|
|
|
29
32
|
private formats;
|
|
30
33
|
private keywords;
|
|
31
34
|
private immutable;
|
|
32
|
-
|
|
35
|
+
private rootSchema;
|
|
36
|
+
private idRegistry;
|
|
37
|
+
private failFast;
|
|
38
|
+
constructor({ immutable, failFast }?: {
|
|
33
39
|
immutable?: boolean;
|
|
40
|
+
failFast?: boolean;
|
|
34
41
|
});
|
|
35
42
|
addType(name: string, validator: TypeFunction, overwrite?: boolean): void;
|
|
36
43
|
getType(type: string): TypeFunction | false;
|
|
@@ -38,8 +45,11 @@ export declare class SchemaShield {
|
|
|
38
45
|
getFormat(format: string): FormatFunction | false;
|
|
39
46
|
addKeyword(name: string, validator: KeywordFunction, overwrite?: boolean): void;
|
|
40
47
|
getKeyword(keyword: string): KeywordFunction | false;
|
|
48
|
+
getSchemaRef(path: string): CompiledSchema | undefined;
|
|
49
|
+
getSchemaById(id: string): CompiledSchema | undefined;
|
|
41
50
|
compile(schema: any): Validator;
|
|
42
51
|
private compileSchema;
|
|
43
52
|
isSchemaLike(subSchema: any): boolean;
|
|
53
|
+
private linkReferences;
|
|
44
54
|
}
|
|
45
55
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,eAAe,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,OAAO,EACL,mBAAmB,EACnB,eAAe,EAMhB,MAAM,SAAS,CAAC;AAMjB,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,eAAe,GAAG,IAAI,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,CACE,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,GAAG,EACT,WAAW,EAAE,mBAAmB,EAChC,QAAQ,EAAE,YAAY,GACrB,MAAM,CAAC;CACX;AAED,MAAM,WAAW,YAAY;IAC3B,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,CAAC,IAAI,EAAE,GAAG,GAAG;QACX,IAAI,EAAE,GAAG,CAAC;QACV,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI,CAAC;QACrC,KAAK,EAAE,OAAO,CAAC;KAChB,CAAC;IACF,cAAc,EAAE,cAAc,CAAC;CAChC;AAOD,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAA4C;IACzD,OAAO,CAAC,OAAO,CAA8C;IAC7D,OAAO,CAAC,QAAQ,CAA+C;IAC/D,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,UAAU,CAA0C;IAC5D,OAAO,CAAC,QAAQ,CAAiB;gBAErB,EACV,SAAiB,EACjB,QAAe,EAChB,GAAE;QACD,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACf;IAqBN,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,UAAQ;IAOhE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,KAAK;IAI3C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,UAAQ;IAOpE,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,KAAK;IAIjD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,UAAQ;IAOtE,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,GAAG,KAAK;IAIpD,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAOtD,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIrD,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,SAAS;IAkC/B,OAAO,CAAC,aAAa;IAgMrB,YAAY,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO;IAerC,OAAO,CAAC,cAAc;CA4DvB"}
|