@depup/typebox 1.3.14-depup.0 → 1.3.22-depup.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 +2 -2
- package/build/format/email.mjs +1 -1
- package/build/format/idna/idn-hostname.mjs +21 -1
- package/build/format/json_pointer.d.mts +2 -2
- package/build/format/json_pointer.mjs +2 -2
- package/build/format/json_pointer_uri_fragment.d.mts +2 -2
- package/build/format/json_pointer_uri_fragment.mjs +2 -2
- package/build/format/relative_json_pointer.d.mts +2 -2
- package/build/format/relative_json_pointer.mjs +2 -2
- package/build/guard/emit.mjs +2 -4
- package/build/guard/guard.mjs +6 -18
- package/build/guard/string.mjs +16 -24
- package/build/schema/engine/_stack.mjs +11 -12
- package/build/schema/engine/additionalProperties.mjs +15 -1
- package/build/schema/resolve/resolve.d.mts +4 -1
- package/build/schema/resolve/resolve.mjs +202 -1
- package/build/schema/static/propertyNames.d.mts +5 -0
- package/build/schema/static/propertyNames.mjs +3 -0
- package/build/schema/static/schema.d.mts +3 -0
- package/build/system/memory/assign.mjs +2 -1
- package/build/system/memory/create.mjs +3 -4
- package/build/system/memory/discard.mjs +2 -2
- package/build/system/memory/freeze.d.mts +4 -0
- package/build/system/memory/freeze.mjs +6 -0
- package/build/system/memory/update.mjs +3 -2
- package/changes.json +1 -1
- package/package.json +3 -3
- package/readme.md +31 -31
- package/build/schema/resolve/ref.d.mts +0 -4
- package/build/schema/resolve/ref.mjs +0 -156
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ npm install @depup/typebox
|
|
|
13
13
|
|
|
14
14
|
| Field | Value |
|
|
15
15
|
|-------|-------|
|
|
16
|
-
| Original | [typebox](https://www.npmjs.com/package/typebox) @ 1.3.
|
|
17
|
-
| Processed | 2026-08-
|
|
16
|
+
| Original | [typebox](https://www.npmjs.com/package/typebox) @ 1.3.22 |
|
|
17
|
+
| Processed | 2026-08-30 |
|
|
18
18
|
| Smoke test | passed |
|
|
19
19
|
| Deps updated | 0 |
|
|
20
20
|
|
package/build/format/email.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const Email = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[^"\\]
|
|
1
|
+
const Email = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[^"\\]|\\[\x20-\x7e])*")@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*|\[(?:IPv6:[a-f0-9:]+|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})\])$/i;
|
|
2
2
|
/**
|
|
3
3
|
* Returns true if the value is an Email
|
|
4
4
|
* @specification https://datatracker.ietf.org/doc/html/rfc5322
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as FormatBidi from './format/bidi.mjs';
|
|
2
2
|
import * as LabelUnicode from './label/unicode.mjs';
|
|
3
3
|
import * as LabelPuny from './label/puny.mjs';
|
|
4
|
+
// ------------------------------------------------------------------
|
|
5
|
+
// IsLabel
|
|
6
|
+
// ------------------------------------------------------------------
|
|
4
7
|
function IsValidLabelLength(value) {
|
|
5
8
|
return value.length > 0 && value.length <= 63;
|
|
6
9
|
}
|
|
@@ -8,9 +11,26 @@ function IsLabel(value) {
|
|
|
8
11
|
return IsValidLabelLength(value) && (LabelPuny.IsPunyLabel(value) ||
|
|
9
12
|
LabelUnicode.IsUnicodeLabel(value));
|
|
10
13
|
}
|
|
14
|
+
// ------------------------------------------------------------------
|
|
15
|
+
// NormalizeHostname
|
|
16
|
+
//
|
|
17
|
+
// Normalizes a hostname for label validation. Maps fullwidth
|
|
18
|
+
// codepoints to their ASCII equivalent (e.g. 'a' U+FF41 maps to
|
|
19
|
+
// 'a'), then normalizes for NFC such that equivalent codepoint
|
|
20
|
+
// sequences can be uniformly compared. It also removes codepoints
|
|
21
|
+
// that IDNA mapping ignores, and converts full stop variants to
|
|
22
|
+
// ASCII '.' so the hostname can be split into labels.
|
|
23
|
+
// ------------------------------------------------------------------
|
|
11
24
|
function NormalizeHostname(value) {
|
|
12
|
-
return value
|
|
25
|
+
return value
|
|
26
|
+
.replace(/[\uff01-\uff5e]/g, (char) => String.fromCharCode(char.charCodeAt(0) - 0xfee0)) // RE_FULLWIDTH_MAPPING
|
|
27
|
+
.normalize('NFC')
|
|
28
|
+
.replace(/[\u00ad\u034f\u180b-\u180d\u200b\ufe00-\ufe0f\u{e0100}-\u{e01ef}]/gu, '') // RE_IGNORED_MAPPING
|
|
29
|
+
.replace(/[\u002E\u3002\uFF0E\uFF61]/g, '.'); // RE_FULL_STOP_MAPPING
|
|
13
30
|
}
|
|
31
|
+
// ------------------------------------------------------------------
|
|
32
|
+
// IsIdnHostname
|
|
33
|
+
// ------------------------------------------------------------------
|
|
14
34
|
export function IsIdnHostname(value) {
|
|
15
35
|
if (value.length === 0 || value.includes(' '))
|
|
16
36
|
return false;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns true if the value is a json pointer
|
|
3
|
-
* @specification
|
|
4
|
-
* @source ajv-formats
|
|
3
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc6901
|
|
4
|
+
* @source https://github.com/ajv-validator/ajv-formats
|
|
5
5
|
*/
|
|
6
6
|
export declare function IsJsonPointer(value: string): boolean;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const JsonPointer = /^(?:\/(?:[^~/]|~0|~1)*)*$/;
|
|
2
2
|
/**
|
|
3
3
|
* Returns true if the value is a json pointer
|
|
4
|
-
* @specification
|
|
5
|
-
* @source ajv-formats
|
|
4
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc6901
|
|
5
|
+
* @source https://github.com/ajv-validator/ajv-formats
|
|
6
6
|
*/
|
|
7
7
|
export function IsJsonPointer(value) {
|
|
8
8
|
return JsonPointer.test(value);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns true if the value is a json pointer uri fragment
|
|
3
|
-
* @specification
|
|
4
|
-
* @source ajv-formats
|
|
3
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc6901
|
|
4
|
+
* @source https://github.com/ajv-validator/ajv-formats
|
|
5
5
|
*/
|
|
6
6
|
export declare function IsJsonPointerUriFragment(value: string): boolean;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const JsonPointerUriFragment = /^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i;
|
|
2
2
|
/**
|
|
3
3
|
* Returns true if the value is a json pointer uri fragment
|
|
4
|
-
* @specification
|
|
5
|
-
* @source ajv-formats
|
|
4
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc6901
|
|
5
|
+
* @source https://github.com/ajv-validator/ajv-formats
|
|
6
6
|
*/
|
|
7
7
|
export function IsJsonPointerUriFragment(value) {
|
|
8
8
|
return JsonPointerUriFragment.test(value);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns true if the value is a relative json pointer
|
|
3
|
-
* @specification
|
|
4
|
-
* @source ajv-formats
|
|
3
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc6901
|
|
4
|
+
* @source https://github.com/ajv-validator/ajv-formats
|
|
5
5
|
*/
|
|
6
6
|
export declare function IsRelativeJsonPointer(value: string): boolean;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const RelativeJsonPointer = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;
|
|
2
2
|
/**
|
|
3
3
|
* Returns true if the value is a relative json pointer
|
|
4
|
-
* @specification
|
|
5
|
-
* @source ajv-formats
|
|
4
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc6901
|
|
5
|
+
* @source https://github.com/ajv-validator/ajv-formats
|
|
6
6
|
*/
|
|
7
7
|
export function IsRelativeJsonPointer(value) {
|
|
8
8
|
return RelativeJsonPointer.test(value);
|
package/build/guard/emit.mjs
CHANGED
|
@@ -106,15 +106,13 @@ export function IsMaxLength(value, length) {
|
|
|
106
106
|
// Array
|
|
107
107
|
// --------------------------------------------------------------------------
|
|
108
108
|
export function Every(value, offset, params, expression) {
|
|
109
|
-
return G.IsEqual(offset, '0')
|
|
110
|
-
? `${value}.every((${params[0]}, ${params[1]}) => ${expression})`
|
|
111
|
-
: `((value, callback) => { for(let index = ${offset}; index < value.length; index++) if (!callback(value[index], index)) return false; return true })(${value}, (${params[0]}, ${params[1]}) => ${expression})`;
|
|
109
|
+
return G.IsEqual(offset, '0') ? `${value}.every((${params[0]}, ${params[1]}) => (${expression}))` : `${value}.every((${params[0]}, ${params[1]}) => (${params[1]} < ${offset}) || (${expression}))`;
|
|
112
110
|
}
|
|
113
111
|
export function Some(value, params, expression) {
|
|
114
112
|
return `${value}.some((${params[0]}, ${params[1]}) => ${expression})`;
|
|
115
113
|
}
|
|
116
114
|
export function SomeAll(value, params, expression) {
|
|
117
|
-
return `((value
|
|
115
|
+
return `((value) => { let result = false; value.forEach((${params[0]}, ${params[1]}) => { if (${expression}) result = true }); return result })(${value})`;
|
|
118
116
|
}
|
|
119
117
|
export function Counted(value, params, expression) {
|
|
120
118
|
return `${value}.reduce((result, ${params[0]}, ${params[1]}) => (${expression}) ? ++result : result, 0)`;
|
package/build/guard/guard.mjs
CHANGED
|
@@ -140,36 +140,24 @@ export function IsMinLength(value, length) {
|
|
|
140
140
|
// --------------------------------------------------------------------------
|
|
141
141
|
/** Returns true if every element from offset satisfies the callback, short-circuiting on the first failure */
|
|
142
142
|
export function Every(value, offset, callback) {
|
|
143
|
-
|
|
144
|
-
if (!callback(value[index], index))
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
|
-
return true;
|
|
143
|
+
return value.every((item, index) => (index < offset) || callback(item, index));
|
|
148
144
|
}
|
|
149
145
|
/** Returns true if every element from offset satisfies the callback, using exhaustive enumeration */
|
|
150
146
|
export function EveryAll(value, offset, callback) {
|
|
151
147
|
let result = true;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
result = false;
|
|
155
|
-
}
|
|
148
|
+
value.forEach((item, index) => { if ((index >= offset) && !callback(item, index))
|
|
149
|
+
result = false; });
|
|
156
150
|
return result;
|
|
157
151
|
}
|
|
158
152
|
/** Returns true if some element satisfies the callback, short-circuiting on the first success */
|
|
159
153
|
export function Some(value, callback) {
|
|
160
|
-
|
|
161
|
-
if (callback(value[index], index))
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
return false;
|
|
154
|
+
return value.some((value, index) => callback(value, index));
|
|
165
155
|
}
|
|
166
156
|
/** Returns true if some element satisfies the callback, using exhaustive enumeration */
|
|
167
157
|
export function SomeAll(value, callback) {
|
|
168
158
|
let result = false;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
result = true;
|
|
172
|
-
}
|
|
159
|
+
value.forEach((item, index) => { if (callback(item, index))
|
|
160
|
+
result = true; });
|
|
173
161
|
return result;
|
|
174
162
|
}
|
|
175
163
|
/** Returns the count of elements that satisfy the callback */
|
package/build/guard/string.mjs
CHANGED
|
@@ -84,10 +84,11 @@ function NextGraphemeClusterIndex(value, clusterStart) {
|
|
|
84
84
|
// IsGraphemeCodePoint
|
|
85
85
|
// --------------------------------------------------------------------------
|
|
86
86
|
function IsGraphemeCodePoint(value) {
|
|
87
|
-
return (
|
|
87
|
+
return ((value >= 0x0300) && ( // above special range
|
|
88
|
+
IsHighSurrogate(value) ||
|
|
88
89
|
IsCombiningMark(value) ||
|
|
89
90
|
IsVariationSelector(value) ||
|
|
90
|
-
IsZeroWidthJoiner(value));
|
|
91
|
+
IsZeroWidthJoiner(value)));
|
|
91
92
|
}
|
|
92
93
|
// --------------------------------------------------------------------------
|
|
93
94
|
// GraphemeCount
|
|
@@ -103,30 +104,22 @@ export function GraphemeCount(value) {
|
|
|
103
104
|
return count;
|
|
104
105
|
}
|
|
105
106
|
// --------------------------------------------------------------------------
|
|
106
|
-
//
|
|
107
|
+
// IsMinLengthSegmented
|
|
107
108
|
// --------------------------------------------------------------------------
|
|
108
109
|
/** Checks if a string has at least a minimum number of grapheme clusters */
|
|
109
110
|
function IsMinLengthSegmented(value, minLength) {
|
|
110
|
-
//
|
|
111
|
-
// Inaccessible via public interface (review)
|
|
112
|
-
//
|
|
113
|
-
// deno-coverage-ignore-start
|
|
114
|
-
// ----------------------------------------------------------------
|
|
115
|
-
if (minLength === 0)
|
|
116
|
-
return true; // 0-length
|
|
117
|
-
// deno-coverage-ignore-stop
|
|
111
|
+
// if (minLength === 0) return true // 0-length (unreachable)
|
|
118
112
|
let count = 0;
|
|
119
113
|
let index = 0;
|
|
120
114
|
while (index < value.length) {
|
|
121
115
|
index = NextGraphemeClusterIndex(value, index);
|
|
122
|
-
count
|
|
123
|
-
if (count >= minLength)
|
|
116
|
+
if ((++count) >= minLength)
|
|
124
117
|
return true;
|
|
125
118
|
}
|
|
126
119
|
return false;
|
|
127
120
|
}
|
|
128
121
|
// --------------------------------------------------------------------------
|
|
129
|
-
//
|
|
122
|
+
// IsMaxLengthSegmented
|
|
130
123
|
// --------------------------------------------------------------------------
|
|
131
124
|
/** Checks if a string has at most a maximum number of grapheme clusters */
|
|
132
125
|
function IsMaxLengthSegmented(value, maxLength) {
|
|
@@ -134,8 +127,7 @@ function IsMaxLengthSegmented(value, maxLength) {
|
|
|
134
127
|
let index = 0;
|
|
135
128
|
while (index < value.length) {
|
|
136
129
|
index = NextGraphemeClusterIndex(value, index);
|
|
137
|
-
count
|
|
138
|
-
if (count > maxLength)
|
|
130
|
+
if ((++count) > maxLength)
|
|
139
131
|
return false;
|
|
140
132
|
}
|
|
141
133
|
return true;
|
|
@@ -147,30 +139,30 @@ function IsMaxLengthSegmented(value, maxLength) {
|
|
|
147
139
|
export function IsMinLength(value, minLength) {
|
|
148
140
|
if (minLength === 0)
|
|
149
141
|
return true; // 0-length
|
|
142
|
+
if (value.length < minLength)
|
|
143
|
+
return false; // length is upper bound on grapheme count
|
|
150
144
|
let index = 0;
|
|
151
|
-
while (
|
|
145
|
+
while (true) {
|
|
152
146
|
if (IsGraphemeCodePoint(value.charCodeAt(index))) {
|
|
153
147
|
return IsMinLengthSegmented(value, minLength);
|
|
154
148
|
}
|
|
155
|
-
index
|
|
156
|
-
if (index >= minLength)
|
|
149
|
+
if ((++index) >= minLength)
|
|
157
150
|
return true;
|
|
158
151
|
}
|
|
159
|
-
return false;
|
|
160
152
|
}
|
|
161
153
|
// --------------------------------------------------------------------------
|
|
162
154
|
// IsMaxLengthFast
|
|
163
155
|
// --------------------------------------------------------------------------
|
|
164
156
|
/** Fast check for maximum grapheme length, falls back to full check if needed */
|
|
165
157
|
export function IsMaxLength(value, maxLength) {
|
|
158
|
+
if (value.length <= maxLength)
|
|
159
|
+
return true; // length is upper bound on grapheme count
|
|
166
160
|
let index = 0;
|
|
167
|
-
while (
|
|
161
|
+
while (true) {
|
|
168
162
|
if (IsGraphemeCodePoint(value.charCodeAt(index))) {
|
|
169
163
|
return IsMaxLengthSegmented(value, maxLength);
|
|
170
164
|
}
|
|
171
|
-
index
|
|
172
|
-
if (index > maxLength)
|
|
165
|
+
if ((++index) > maxLength)
|
|
173
166
|
return false;
|
|
174
167
|
}
|
|
175
|
-
return true;
|
|
176
168
|
}
|
|
@@ -4,7 +4,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
4
4
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
5
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
6
|
};
|
|
7
|
-
var _Stack_instances, _Stack_PushResourceAnchors, _Stack_PopResourceAnchors
|
|
7
|
+
var _Stack_instances, _Stack_PushResourceAnchors, _Stack_PopResourceAnchors;
|
|
8
8
|
import * as Schema from '../types/index.mjs';
|
|
9
9
|
import { Guard as G } from '../../guard/index.mjs';
|
|
10
10
|
import { Resolve } from '../resolve/index.mjs';
|
|
@@ -58,23 +58,29 @@ export class Stack {
|
|
|
58
58
|
if (Schema.IsDynamicAnchor(schema))
|
|
59
59
|
this.dynamicAnchors.pop();
|
|
60
60
|
}
|
|
61
|
+
// ----------------------------------------------------------------
|
|
62
|
+
// Ref
|
|
63
|
+
// ----------------------------------------------------------------
|
|
61
64
|
Ref(ref) {
|
|
62
|
-
|
|
65
|
+
const root = this.schema;
|
|
66
|
+
return !ref.$ref.startsWith('#')
|
|
67
|
+
? Resolve.Ref(this.context, root, ref.$ref)
|
|
68
|
+
: Resolve.Ref(this.context, this.Base(), ref.$ref);
|
|
63
69
|
}
|
|
64
70
|
// ----------------------------------------------------------------
|
|
65
71
|
// RecursiveRef
|
|
66
72
|
// ----------------------------------------------------------------
|
|
67
73
|
RecursiveRef(recursiveRef) {
|
|
68
74
|
return Schema.IsRecursiveAnchorTrue(this.Base())
|
|
69
|
-
? Resolve.Ref(this.recursiveAnchors[0], recursiveRef.$recursiveRef)
|
|
70
|
-
: Resolve.Ref(this.Base(), recursiveRef.$recursiveRef);
|
|
75
|
+
? Resolve.Ref(this.context, this.recursiveAnchors[0], recursiveRef.$recursiveRef)
|
|
76
|
+
: Resolve.Ref(this.context, this.Base(), recursiveRef.$recursiveRef);
|
|
71
77
|
}
|
|
72
78
|
// ----------------------------------------------------------------
|
|
73
79
|
// DynamicRef
|
|
74
80
|
// ----------------------------------------------------------------
|
|
75
81
|
DynamicRef(dynamicRef) {
|
|
76
82
|
const root = this.schema;
|
|
77
|
-
return Resolve.DynamicRef(root, this.Base(), dynamicRef, this.dynamicAnchors);
|
|
83
|
+
return Resolve.DynamicRef(this.context, root, this.Base(), dynamicRef, this.dynamicAnchors);
|
|
78
84
|
}
|
|
79
85
|
}
|
|
80
86
|
_Stack_instances = new WeakSet(), _Stack_PushResourceAnchors = function _Stack_PushResourceAnchors(schema, isRoot = true) {
|
|
@@ -97,11 +103,4 @@ _Stack_instances = new WeakSet(), _Stack_PushResourceAnchors = function _Stack_P
|
|
|
97
103
|
this.dynamicAnchors.pop();
|
|
98
104
|
for (const key of G.Keys(current))
|
|
99
105
|
__classPrivateFieldGet(this, _Stack_instances, "m", _Stack_PopResourceAnchors).call(this, current[key], false);
|
|
100
|
-
}, _Stack_FromContext = function _Stack_FromContext(ref) {
|
|
101
|
-
return G.HasPropertyKey(this.context, ref.$ref) ? this.context[ref.$ref] : undefined;
|
|
102
|
-
}, _Stack_FromRef = function _Stack_FromRef(ref) {
|
|
103
|
-
const root = this.schema;
|
|
104
|
-
return !ref.$ref.startsWith('#')
|
|
105
|
-
? Resolve.Ref(root, ref.$ref)
|
|
106
|
-
: Resolve.Ref(this.Base(), ref.$ref);
|
|
107
106
|
};
|
|
@@ -7,7 +7,19 @@ import { UnicodeRegExp } from './_regexp.mjs';
|
|
|
7
7
|
import { EmitGuard as E, Guard as G } from '../../guard/index.mjs';
|
|
8
8
|
import { BuildSchemaPushStack, CheckSchemaPushStack, ErrorSchemaPushStack } from './schema.mjs';
|
|
9
9
|
// ------------------------------------------------------------------
|
|
10
|
-
//
|
|
10
|
+
// Optimization: IsAdditionalPropertiesIgnored
|
|
11
|
+
//
|
|
12
|
+
// We can ignore additionalProperties if the schema is true-like and
|
|
13
|
+
// we are not in an unevaluated context, noting that additional
|
|
14
|
+
// properties are considered tracked, evaluated keys.
|
|
15
|
+
// ------------------------------------------------------------------
|
|
16
|
+
function IsAdditionalPropertiesIgnored(context, additionalProperties) {
|
|
17
|
+
return !context.UseUnevaluated() &&
|
|
18
|
+
(G.IsEqual(additionalProperties, true) ||
|
|
19
|
+
(G.IsObject(additionalProperties) && G.IsEqual(G.Keys(additionalProperties).length, 0)));
|
|
20
|
+
}
|
|
21
|
+
// ------------------------------------------------------------------
|
|
22
|
+
// Optimization: GetPropertiesPattern
|
|
11
23
|
//
|
|
12
24
|
// Constructs a regular expression that matches all property keys
|
|
13
25
|
// and pattern properties defined in a schema. This approach unifies
|
|
@@ -71,6 +83,8 @@ export function BuildAdditionalPropertiesStandard(stack, context, schema, value)
|
|
|
71
83
|
// Build
|
|
72
84
|
// ------------------------------------------------------------------
|
|
73
85
|
export function BuildAdditionalProperties(stack, context, schema, value) {
|
|
86
|
+
if (IsAdditionalPropertiesIgnored(context, schema.additionalProperties))
|
|
87
|
+
return E.Constant(true);
|
|
74
88
|
return CanAdditionalPropertiesFast(context, schema, value)
|
|
75
89
|
? BuildAdditionalPropertiesFast(context, schema, value)
|
|
76
90
|
: BuildAdditionalPropertiesStandard(stack, context, schema, value);
|
|
@@ -1 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import * as Schema from '../types/index.mjs';
|
|
2
|
+
export declare const DefaultBase: URL;
|
|
3
|
+
export declare function Ref(context: Record<string, Schema.XSchema>, schema: Schema.XSchemaObject, ref: string): Schema.XSchema | undefined;
|
|
4
|
+
export declare function DynamicRef(context: Record<string, Schema.XSchema>, root: Schema.XSchemaObject, base: Schema.XSchemaObject, dynamicRef: Schema.XDynamicRef, dynamicAnchors: Schema.XDynamicAnchor[]): Schema.XSchema | undefined;
|
|
@@ -1 +1,202 @@
|
|
|
1
|
-
|
|
1
|
+
// deno-fmt-ignore-file
|
|
2
|
+
import { Guard } from '../../guard/index.mjs';
|
|
3
|
+
import { Pointer } from '../pointer/index.mjs';
|
|
4
|
+
import * as Schema from '../types/index.mjs';
|
|
5
|
+
// ------------------------------------------------------------------
|
|
6
|
+
// DefaultBase
|
|
7
|
+
// ------------------------------------------------------------------
|
|
8
|
+
export const DefaultBase = new URL('https://json-schema.org');
|
|
9
|
+
// ------------------------------------------------------------------
|
|
10
|
+
// Match: Id
|
|
11
|
+
// ------------------------------------------------------------------
|
|
12
|
+
function MatchId(schema, base, ref) {
|
|
13
|
+
if (schema.$id === ref.hash)
|
|
14
|
+
return schema;
|
|
15
|
+
const absoluteId = new URL(schema.$id, base.href);
|
|
16
|
+
const absoluteRef = new URL(ref.href, base.href);
|
|
17
|
+
if (Guard.IsEqual(absoluteId.pathname, absoluteRef.pathname)) {
|
|
18
|
+
return ref.hash.startsWith('#') ? MatchHash(schema, base, ref) : schema;
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
// ------------------------------------------------------------------
|
|
23
|
+
// Match: Anchor
|
|
24
|
+
// ------------------------------------------------------------------
|
|
25
|
+
function MatchAnchor(schema, base, ref) {
|
|
26
|
+
const absoluteAnchor = new URL(`#${schema.$anchor}`, base.href);
|
|
27
|
+
const absoluteRef = new URL(ref.href, base.href);
|
|
28
|
+
return Guard.IsEqual(absoluteAnchor.href, absoluteRef.href) ? schema : undefined;
|
|
29
|
+
}
|
|
30
|
+
// ------------------------------------------------------------------
|
|
31
|
+
// Match: DynamicAnchor
|
|
32
|
+
// ------------------------------------------------------------------
|
|
33
|
+
function MatchDynamicAnchor(schema, base, ref) {
|
|
34
|
+
const absoluteAnchor = new URL(`#${schema.$dynamicAnchor}`, base.href);
|
|
35
|
+
const absoluteRef = new URL(ref.href, base.href);
|
|
36
|
+
return Guard.IsEqual(absoluteAnchor.href, absoluteRef.href) ? schema : undefined;
|
|
37
|
+
}
|
|
38
|
+
// ------------------------------------------------------------------
|
|
39
|
+
// Match: Hash
|
|
40
|
+
//
|
|
41
|
+
// Resolves JSON Pointer fragments only. Plain anchor-style fragments
|
|
42
|
+
// (no leading '/') are handled exclusively by MatchAnchor and
|
|
43
|
+
// MatchDynamicAnchor to prevent accidentally resolving an anchor name
|
|
44
|
+
// as a pointer into the schema tree.
|
|
45
|
+
//
|
|
46
|
+
// ------------------------------------------------------------------
|
|
47
|
+
function MatchHash(schema, _base, ref) {
|
|
48
|
+
if (ref.href.endsWith('#'))
|
|
49
|
+
return schema;
|
|
50
|
+
if (!ref.hash.startsWith('#'))
|
|
51
|
+
return undefined;
|
|
52
|
+
const fragment = decodeURIComponent(ref.hash.slice(1));
|
|
53
|
+
if (!fragment.startsWith('/'))
|
|
54
|
+
return undefined;
|
|
55
|
+
return Pointer.Get(schema, fragment);
|
|
56
|
+
}
|
|
57
|
+
// ------------------------------------------------------------------
|
|
58
|
+
// Match
|
|
59
|
+
// ------------------------------------------------------------------
|
|
60
|
+
function Match(schema, base, ref) {
|
|
61
|
+
if (Schema.IsId(schema)) {
|
|
62
|
+
const result = MatchId(schema, base, ref);
|
|
63
|
+
if (!Guard.IsUndefined(result))
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
if (Schema.IsAnchor(schema)) {
|
|
67
|
+
const result = MatchAnchor(schema, base, ref);
|
|
68
|
+
if (!Guard.IsUndefined(result))
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
if (Schema.IsDynamicAnchor(schema)) {
|
|
72
|
+
const result = MatchDynamicAnchor(schema, base, ref);
|
|
73
|
+
if (!Guard.IsUndefined(result))
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
return MatchHash(schema, base, ref);
|
|
77
|
+
}
|
|
78
|
+
// ------------------------------------------------------------------
|
|
79
|
+
// FromArray
|
|
80
|
+
// ------------------------------------------------------------------
|
|
81
|
+
function FromArray(schema, base, ref) {
|
|
82
|
+
return schema.reduce((result, item) => {
|
|
83
|
+
const match = FromValue(item, base, ref);
|
|
84
|
+
return !Guard.IsUndefined(match) ? match : result;
|
|
85
|
+
}, undefined);
|
|
86
|
+
}
|
|
87
|
+
// ------------------------------------------------------------------
|
|
88
|
+
// FromObject
|
|
89
|
+
// ------------------------------------------------------------------
|
|
90
|
+
function SkipProperty(key) {
|
|
91
|
+
// these are explicit data encoded in a schema, they should not
|
|
92
|
+
// be considered valid targets used for de-referencing.
|
|
93
|
+
return Guard.IsEqual(key, 'const') || Guard.IsEqual(key, 'enum');
|
|
94
|
+
}
|
|
95
|
+
function FromObject(schema, base, ref) {
|
|
96
|
+
return Guard.Keys(schema).reduce((result, key) => {
|
|
97
|
+
if (SkipProperty(key))
|
|
98
|
+
return result;
|
|
99
|
+
const match = FromValue(schema[key], base, ref);
|
|
100
|
+
return !Guard.IsUndefined(match) ? match : result;
|
|
101
|
+
}, undefined);
|
|
102
|
+
}
|
|
103
|
+
// ------------------------------------------------------------------
|
|
104
|
+
// FromValue
|
|
105
|
+
// ------------------------------------------------------------------
|
|
106
|
+
function FromValue(schema, base, ref) {
|
|
107
|
+
const nextBase = Schema.IsSchemaObject(schema) && Schema.IsId(schema)
|
|
108
|
+
? new URL(schema.$id, base.href)
|
|
109
|
+
: base;
|
|
110
|
+
if (Schema.IsSchemaObject(schema)) {
|
|
111
|
+
const result = Match(schema, nextBase, ref);
|
|
112
|
+
if (!Guard.IsUndefined(result))
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
if (Guard.IsArray(schema))
|
|
116
|
+
return FromArray(schema, nextBase, ref);
|
|
117
|
+
if (Guard.IsObject(schema))
|
|
118
|
+
return FromObject(schema, nextBase, ref);
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
// ------------------------------------------------------------------
|
|
122
|
+
// CanonicalHref
|
|
123
|
+
//
|
|
124
|
+
// Returns the URL's href with its fragment removed, as a string.
|
|
125
|
+
// This is the canonical URI a document is identified and keyed by
|
|
126
|
+
// in context.
|
|
127
|
+
// ------------------------------------------------------------------
|
|
128
|
+
function CanonicalHref(url) {
|
|
129
|
+
return url.href.split('#')[0];
|
|
130
|
+
}
|
|
131
|
+
// ------------------------------------------------------------------
|
|
132
|
+
// RefContext: Phase 1
|
|
133
|
+
//
|
|
134
|
+
// A ContextRef targets a simple key name in the context and is
|
|
135
|
+
// used for fast resolution of inline referential TypeBox types.
|
|
136
|
+
// ------------------------------------------------------------------
|
|
137
|
+
function RefContext(context, ref) {
|
|
138
|
+
return Guard.HasPropertyKey(context, ref) ? context[ref] : undefined;
|
|
139
|
+
}
|
|
140
|
+
// ------------------------------------------------------------------
|
|
141
|
+
// RefLocal: Phase 2
|
|
142
|
+
//
|
|
143
|
+
// Resolves the ref's fragment within the current schema resource,
|
|
144
|
+
// either as a JSON Pointer or a plain-name fragment ($anchor,
|
|
145
|
+
// $dynamicAnchor), per the JSON Schema spec.
|
|
146
|
+
// ------------------------------------------------------------------
|
|
147
|
+
function RefLocal(schema, base, ref) {
|
|
148
|
+
return FromValue(schema, base, ref);
|
|
149
|
+
}
|
|
150
|
+
// ------------------------------------------------------------------
|
|
151
|
+
// RefRemote: Phase 3
|
|
152
|
+
//
|
|
153
|
+
// Locates the schema resource identified by the ref's base URI, then
|
|
154
|
+
// resolves the ref's fragment within that resource, either as a JSON
|
|
155
|
+
// Pointer or a plain-name fragment, per the JSON Schema spec.
|
|
156
|
+
// ------------------------------------------------------------------
|
|
157
|
+
function RefRemote(context, base, ref) {
|
|
158
|
+
const targetDocumentHref = CanonicalHref(ref);
|
|
159
|
+
if (Guard.IsEqual(targetDocumentHref, CanonicalHref(base)))
|
|
160
|
+
return undefined;
|
|
161
|
+
if (!Guard.HasPropertyKey(context, targetDocumentHref))
|
|
162
|
+
return undefined;
|
|
163
|
+
const remoteSchema = context[targetDocumentHref];
|
|
164
|
+
const remoteBase = (Schema.IsSchemaObject(remoteSchema) && Schema.IsId(remoteSchema)) ? new URL(remoteSchema.$id, DefaultBase.href) : new URL(targetDocumentHref);
|
|
165
|
+
return FromValue(remoteSchema, remoteBase, ref);
|
|
166
|
+
}
|
|
167
|
+
// ------------------------------------------------------------------
|
|
168
|
+
// Ref
|
|
169
|
+
// ------------------------------------------------------------------
|
|
170
|
+
export function Ref(context, schema, ref) {
|
|
171
|
+
const initialBase = Schema.IsId(schema) ? new URL(schema.$id, DefaultBase.href) : DefaultBase;
|
|
172
|
+
const initialRef = new URL(ref, initialBase.href);
|
|
173
|
+
return RefContext(context, ref) ?? RefLocal(schema, initialBase, initialRef) ?? RefRemote(context, initialBase, initialRef);
|
|
174
|
+
}
|
|
175
|
+
// ------------------------------------------------------------------
|
|
176
|
+
// DynamicRef
|
|
177
|
+
// ------------------------------------------------------------------
|
|
178
|
+
export function DynamicRef(context, root, base, dynamicRef, dynamicAnchors) {
|
|
179
|
+
// Resolve the static target using either the local base (for fragment‑only references)
|
|
180
|
+
// or the document root (for absolute URI references).
|
|
181
|
+
const fragmentTarget = dynamicRef.$dynamicRef.startsWith('#')
|
|
182
|
+
? Ref(context, base, dynamicRef.$dynamicRef)
|
|
183
|
+
: Ref(context, root, dynamicRef.$dynamicRef);
|
|
184
|
+
if (Guard.IsUndefined(fragmentTarget))
|
|
185
|
+
return undefined;
|
|
186
|
+
// Dynamic override only applies if the resolved target itself declares a $dynamicAnchor.
|
|
187
|
+
// If it does not, return the static target unchanged.
|
|
188
|
+
if (!Schema.IsSchemaObject(fragmentTarget) || !Schema.IsDynamicAnchor(fragmentTarget))
|
|
189
|
+
return fragmentTarget;
|
|
190
|
+
// Extract the fragment portion of the reference. According to the test suite,
|
|
191
|
+
// only plain fragment names (e.g., "#foo") trigger the dynamic scope; JSON
|
|
192
|
+
// Pointer fragments (e.g., "#/definitions/foo") bypass dynamic resolution.
|
|
193
|
+
const fragment = new URL(dynamicRef.$dynamicRef, DefaultBase).hash;
|
|
194
|
+
if (fragment.startsWith('#/'))
|
|
195
|
+
return fragmentTarget;
|
|
196
|
+
// Search the live dynamic anchor stack for a schema whose $dynamicAnchor matches the
|
|
197
|
+
// target's $dynamicAnchor. The stack reflects the current evaluation path, and
|
|
198
|
+
// find() returns the outermost (first encountered) match, which is the correct
|
|
199
|
+
// lexical scope per the specification.
|
|
200
|
+
const anchorTarget = dynamicAnchors.find(anchor => anchor.$dynamicAnchor === fragmentTarget.$dynamicAnchor);
|
|
201
|
+
return anchorTarget ?? fragmentTarget;
|
|
202
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { XSchema } from '../types/schema.mjs';
|
|
2
|
+
import type { XStaticSchema } from './schema.mjs';
|
|
3
|
+
export type XStaticPropertyNames<Stack extends string[], Root extends XSchema, Schema extends XSchema, StaticKey extends unknown = XStaticSchema<Stack, Root, Schema>, Result extends Record<string, unknown> = [StaticKey] extends [PropertyKey] ? {
|
|
4
|
+
[Key in StaticKey]?: unknown;
|
|
5
|
+
} : {}> = Result;
|
|
@@ -9,6 +9,7 @@ import type { XOneOf } from '../types/oneOf.mjs';
|
|
|
9
9
|
import type { XPatternProperties } from '../types/patternProperties.mjs';
|
|
10
10
|
import type { XPrefixItems } from '../types/prefixItems.mjs';
|
|
11
11
|
import type { XProperties } from '../types/properties.mjs';
|
|
12
|
+
import type { XPropertyNames } from '../types/propertyNames.mjs';
|
|
12
13
|
import type { XRef } from '../types/ref.mjs';
|
|
13
14
|
import type { XRequired } from '../types/required.mjs';
|
|
14
15
|
import type { XSchema } from '../types/schema.mjs';
|
|
@@ -25,6 +26,7 @@ import type { XStaticOneOf } from './oneOf.mjs';
|
|
|
25
26
|
import type { XStaticPatternProperties } from './patternProperties.mjs';
|
|
26
27
|
import type { XStaticPrefixItems } from './prefixItems.mjs';
|
|
27
28
|
import type { XStaticProperties } from './properties.mjs';
|
|
29
|
+
import type { XStaticPropertyNames } from './propertyNames.mjs';
|
|
28
30
|
import type { XStaticRef } from './ref.mjs';
|
|
29
31
|
import type { XStaticRequired } from './required.mjs';
|
|
30
32
|
import type { XStaticType } from './type.mjs';
|
|
@@ -41,6 +43,7 @@ type XFromKeywords<Stack extends string[], Root extends XSchema, Schema extends
|
|
|
41
43
|
Schema extends XPatternProperties<infer Properties extends Record<PropertyKey, XSchema>> ? XStaticPatternProperties<Stack, Root, Properties> : unknown,
|
|
42
44
|
Schema extends XPrefixItems<infer Types extends XSchema[]> ? XStaticPrefixItems<Stack, Root, Schema, Types> : unknown,
|
|
43
45
|
Schema extends XProperties<infer Properties extends Record<PropertyKey, XSchema>> ? XStaticProperties<Stack, Root, Schema, Properties> : unknown,
|
|
46
|
+
Schema extends XPropertyNames<infer Type extends XSchema> ? XStaticPropertyNames<Stack, Root, Type> : unknown,
|
|
44
47
|
Schema extends XRef<infer Ref extends string> ? XStaticRef<Stack, Root, Ref> : unknown,
|
|
45
48
|
Schema extends XRequired<infer Keys extends string[]> ? XStaticRequired<Stack, Root, Schema, Keys> : unknown,
|
|
46
49
|
Schema extends XType<infer TypeName extends string[] | string> ? XStaticType<TypeName> : unknown,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// deno-lint-ignore-file ban-types no-explicit-any
|
|
2
2
|
// deno-fmt-ignore-file
|
|
3
3
|
import { Metrics } from './metrics.mjs';
|
|
4
|
+
import { Freeze } from './freeze.mjs';
|
|
4
5
|
/**
|
|
5
6
|
* Performs an Object assign using the Left and Right object types. We track this operation as it
|
|
6
7
|
* creates a new GC handle per assignment.
|
|
7
8
|
*/
|
|
8
9
|
export function Assign(left, right) {
|
|
9
10
|
Metrics.assign += 1;
|
|
10
|
-
return { ...left, ...right };
|
|
11
|
+
return Freeze({ ...left, ...right });
|
|
11
12
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// deno-lint-ignore-file no-explicit-any
|
|
2
|
-
// deno-fmt-ignore-file
|
|
3
2
|
import { Settings } from '../settings/index.mjs';
|
|
4
3
|
import { Metrics } from './metrics.mjs';
|
|
4
|
+
import { Freeze } from './freeze.mjs';
|
|
5
5
|
function MergeHidden(left, right) {
|
|
6
6
|
for (const key of Object.keys(right)) {
|
|
7
7
|
Object.defineProperty(left, key, {
|
|
@@ -23,8 +23,7 @@ function Merge(left, right) {
|
|
|
23
23
|
*/
|
|
24
24
|
export function Create(hidden, enumerable, options = {}) {
|
|
25
25
|
Metrics.create += 1;
|
|
26
|
-
const settings = Settings.Get();
|
|
27
26
|
const withOptions = Merge(enumerable, options);
|
|
28
|
-
const withHidden =
|
|
29
|
-
return
|
|
27
|
+
const withHidden = Settings.Get().enumerableKind ? Merge(withOptions, hidden) : MergeHidden(withOptions, hidden);
|
|
28
|
+
return Freeze(withHidden);
|
|
30
29
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// deno-lint-ignore-file no-explicit-any
|
|
2
|
-
// deno-fmt-ignore-file
|
|
3
2
|
import { Guard } from '../../guard/index.mjs';
|
|
4
3
|
import { Metrics } from './metrics.mjs';
|
|
4
|
+
import { Freeze } from './freeze.mjs';
|
|
5
5
|
import { Clone } from './clone.mjs';
|
|
6
6
|
/** Discards multiple property keys from the given object value */
|
|
7
7
|
export function Discard(value, propertyKeys) {
|
|
@@ -14,5 +14,5 @@ export function Discard(value, propertyKeys) {
|
|
|
14
14
|
descriptor.value = Clone(descriptor.value);
|
|
15
15
|
Object.defineProperty(result, key, descriptor);
|
|
16
16
|
}
|
|
17
|
-
return result;
|
|
17
|
+
return Freeze(result);
|
|
18
18
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-explicit-any
|
|
2
|
+
import { Settings } from '../settings/index.mjs';
|
|
3
|
+
/** Conditionally freezes the value if `immutableTypes` is true, otherwise no action. */
|
|
4
|
+
export function Freeze(value) {
|
|
5
|
+
return Settings.Get().immutableTypes ? Object.freeze(value) : value;
|
|
6
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
// deno-
|
|
1
|
+
// deno-lint-ignore-file no-explicit-any
|
|
2
2
|
import { Settings } from '../settings/index.mjs';
|
|
3
3
|
import { Metrics } from './metrics.mjs';
|
|
4
|
+
import { Freeze } from './freeze.mjs';
|
|
4
5
|
import { Clone } from './clone.mjs';
|
|
5
6
|
/**
|
|
6
7
|
* Updates a value with new properties while preserving property enumerability. Use this function to modify
|
|
@@ -28,5 +29,5 @@ export function Update(current, hidden, enumerable) {
|
|
|
28
29
|
value: enumerable[key]
|
|
29
30
|
});
|
|
30
31
|
}
|
|
31
|
-
return result;
|
|
32
|
+
return Freeze(result);
|
|
32
33
|
}
|
package/changes.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@depup/typebox",
|
|
3
3
|
"description": "Json Schema Type Builder with Static Type Resolution for TypeScript (with updated dependencies)",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.22-depup.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typebox",
|
|
7
7
|
"depup",
|
|
@@ -94,8 +94,8 @@
|
|
|
94
94
|
"changes": {},
|
|
95
95
|
"depsUpdated": 0,
|
|
96
96
|
"originalPackage": "typebox",
|
|
97
|
-
"originalVersion": "1.3.
|
|
98
|
-
"processedAt": "2026-08-
|
|
97
|
+
"originalVersion": "1.3.22",
|
|
98
|
+
"processedAt": "2026-08-30T00:59:41.143Z",
|
|
99
99
|
"smokeTest": "passed"
|
|
100
100
|
}
|
|
101
101
|
}
|
package/readme.md
CHANGED
|
@@ -231,7 +231,7 @@ const result = Vector.Parse({ x: 1, y: 0, z: 0 }) // const result: {
|
|
|
231
231
|
|
|
232
232
|
[JSON Schema Test Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite) | [JSON Schema Compliance Suite](https://github.com/sinclairzx81/json-schema-compliance-suite)
|
|
233
233
|
|
|
234
|
-
TypeBox supports all major JSON Schema draft versions and tracks compliance against the official JSON Schema Test Suite. It also maintains a separate JavaScript compliance suite to track ecosystem adoption as JSON Schema moves toward the
|
|
234
|
+
TypeBox supports all major JSON Schema draft versions and tracks compliance against the official JSON Schema Test Suite. It also maintains a separate JavaScript compliance suite to track ecosystem adoption as JSON Schema moves toward the V1 candidate. The following table shows TypeBox specification coverage.
|
|
235
235
|
|
|
236
236
|
| Spec | 3 | 4 | 6 | 7 | 2019-09 | 2020-12 | v1 |
|
|
237
237
|
|:-----|:--|:--|:--|:--|:--|:--|:--|
|
|
@@ -248,7 +248,7 @@ TypeBox supports all major JSON Schema draft versions and tracks compliance agai
|
|
|
248
248
|
| dependencies | 17/18 | ✅ | ✅ | ✅ | - | - | - |
|
|
249
249
|
| dependentRequired | - | - | - | - | ✅ | ✅ | ✅ |
|
|
250
250
|
| dependentSchemas | - | - | - | - | ✅ | ✅ | ✅ |
|
|
251
|
-
| dynamicRef | - | - | - | - | - |
|
|
251
|
+
| dynamicRef | - | - | - | - | - | 40/44 | 21/27 |
|
|
252
252
|
| enum | 14/16 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
253
253
|
| exclusiveMaximum | - | - | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
254
254
|
| exclusiveMinimum | - | - | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
@@ -274,7 +274,7 @@ TypeBox supports all major JSON Schema draft versions and tracks compliance agai
|
|
|
274
274
|
| properties | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
275
275
|
| propertyNames | - | - | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
276
276
|
| recursiveRef | - | - | - | - | ✅ | - | - |
|
|
277
|
-
| ref | 22/27 | 37/45 | 67/70 | 75/78 | 79/81 | 77/79 |
|
|
277
|
+
| ref | 22/27 | 37/45 | 67/70 | 75/78 | 79/81 | 77/79 | 78/79 |
|
|
278
278
|
| required | 3/4 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
279
279
|
| type | 73/80 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
280
280
|
| unevaluatedItems | - | - | - | - | ✅ | ✅ | 70/71 |
|
|
@@ -283,40 +283,40 @@ TypeBox supports all major JSON Schema draft versions and tracks compliance agai
|
|
|
283
283
|
|
|
284
284
|
### Performance
|
|
285
285
|
|
|
286
|
-
TypeBox tracks comparative performance against AJV8 as the de facto
|
|
286
|
+
TypeBox tracks comparative performance against AJV8 as the de facto performance standard. For broader comparative benchmarks, refer to the community maintained projects below.
|
|
287
287
|
|
|
288
288
|
[Runtime Benchmarks](https://moltar.github.io/typescript-runtime-type-benchmarks/) | [Schema Benchmarks](https://schemabenchmarks.dev/)
|
|
289
289
|
|
|
290
290
|
The following table shows compilation performance for various JSON Schema structures. These benchmarks measure the time required to JIT compile schematics, with faster compilation resulting in faster application startup.
|
|
291
291
|
|
|
292
292
|
```python
|
|
293
|
-
|
|
294
|
-
│ Compile │ TB1X
|
|
295
|
-
|
|
296
|
-
│ Boolean │
|
|
297
|
-
│ Number │
|
|
298
|
-
│ String │
|
|
299
|
-
│ Null │
|
|
300
|
-
│ Literal_String │
|
|
301
|
-
│ Literal_Number │
|
|
302
|
-
│ Literal_Boolean │
|
|
303
|
-
│ Pattern │
|
|
304
|
-
│ Object_Open │
|
|
305
|
-
│ Object_Close │
|
|
306
|
-
│ Object_Vector3 │
|
|
307
|
-
│ Object_Basis3 │
|
|
308
|
-
│ Intersect_And │
|
|
309
|
-
│ Intersect_Structural │
|
|
310
|
-
│ Union_Or │
|
|
311
|
-
│ Union_Structural │
|
|
312
|
-
│ Tuple_Values │
|
|
313
|
-
│ Tuple_Objects │
|
|
314
|
-
│ Array_Numbers_4 │
|
|
315
|
-
│ Array_Numbers_8 │
|
|
316
|
-
│ Array_Numbers_16 │
|
|
317
|
-
│ Array_Objects_Open │
|
|
318
|
-
│ Array_Objects_Close │
|
|
319
|
-
|
|
293
|
+
┌──────────────────────┬──────────────┬──────────────┐
|
|
294
|
+
│ Compile │ TB1X │ AJV8 │
|
|
295
|
+
├──────────────────────┼──────────────┼──────────────┤
|
|
296
|
+
│ Boolean │ 50.4K ops/s │ 7.1K ops/s │
|
|
297
|
+
│ Number │ 129.9K ops/s │ 7.9K ops/s │
|
|
298
|
+
│ String │ 128.9K ops/s │ 9.1K ops/s │
|
|
299
|
+
│ Null │ 91.3K ops/s │ 8.4K ops/s │
|
|
300
|
+
│ Literal_String │ 63.1K ops/s │ 7.4K ops/s │
|
|
301
|
+
│ Literal_Number │ 74.5K ops/s │ 7.6K ops/s │
|
|
302
|
+
│ Literal_Boolean │ 143.3K ops/s │ 7.7K ops/s │
|
|
303
|
+
│ Pattern │ 94.2K ops/s │ 6.4K ops/s │
|
|
304
|
+
│ Object_Open │ 19.4K ops/s │ 1.3K ops/s │
|
|
305
|
+
│ Object_Close │ 17.6K ops/s │ 991 ops/s │
|
|
306
|
+
│ Object_Vector3 │ 46.1K ops/s │ 3.3K ops/s │
|
|
307
|
+
│ Object_Basis3 │ 16K ops/s │ 848 ops/s │
|
|
308
|
+
│ Intersect_And │ 41.3K ops/s │ 4.2K ops/s │
|
|
309
|
+
│ Intersect_Structural │ 25.3K ops/s │ 1.5K ops/s │
|
|
310
|
+
│ Union_Or │ 58.6K ops/s │ 2.4K ops/s │
|
|
311
|
+
│ Union_Structural │ 31.5K ops/s │ 1.8K ops/s │
|
|
312
|
+
│ Tuple_Values │ 18.2K ops/s │ 1.9K ops/s │
|
|
313
|
+
│ Tuple_Objects │ 3.9K ops/s │ 437 ops/s │
|
|
314
|
+
│ Array_Numbers_4 │ 114.5K ops/s │ 4.2K ops/s │
|
|
315
|
+
│ Array_Numbers_8 │ 128.3K ops/s │ 3.9K ops/s │
|
|
316
|
+
│ Array_Numbers_16 │ 128.4K ops/s │ 4K ops/s │
|
|
317
|
+
│ Array_Objects_Open │ 22K ops/s │ 780 ops/s │
|
|
318
|
+
│ Array_Objects_Close │ 22K ops/s │ 1K ops/s │
|
|
319
|
+
└──────────────────────┴──────────────┴──────────────┘
|
|
320
320
|
```
|
|
321
321
|
|
|
322
322
|
The following tables shows validation performance for various JSON Schema structures. These benchmarks measure overall validation throughput for JIT compiled schematics.
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import * as Schema from '../types/index.mjs';
|
|
2
|
-
export declare const DefaultBase: URL;
|
|
3
|
-
export declare function Ref(schema: Schema.XSchemaObject, ref: string): Schema.XSchema | undefined;
|
|
4
|
-
export declare function DynamicRef(root: Schema.XSchemaObject, base: Schema.XSchemaObject, dynamicRef: Schema.XDynamicRef, dynamicAnchors: Schema.XDynamicAnchor[]): Schema.XSchema | undefined;
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
// deno-fmt-ignore-file
|
|
2
|
-
import { Guard } from '../../guard/index.mjs';
|
|
3
|
-
import { Pointer } from '../pointer/index.mjs';
|
|
4
|
-
import * as Schema from '../types/index.mjs';
|
|
5
|
-
// ------------------------------------------------------------------
|
|
6
|
-
// DefaultBase
|
|
7
|
-
// ------------------------------------------------------------------
|
|
8
|
-
export const DefaultBase = new URL('https://json-schema.org');
|
|
9
|
-
// ------------------------------------------------------------------
|
|
10
|
-
// Match: Id
|
|
11
|
-
// ------------------------------------------------------------------
|
|
12
|
-
function MatchId(schema, base, ref) {
|
|
13
|
-
if (schema.$id === ref.hash)
|
|
14
|
-
return schema;
|
|
15
|
-
const absoluteId = new URL(schema.$id, base.href);
|
|
16
|
-
const absoluteRef = new URL(ref.href, base.href);
|
|
17
|
-
if (Guard.IsEqual(absoluteId.pathname, absoluteRef.pathname)) {
|
|
18
|
-
return ref.hash.startsWith('#') ? MatchHash(schema, base, ref) : schema;
|
|
19
|
-
}
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
// ------------------------------------------------------------------
|
|
23
|
-
// Match: Anchor
|
|
24
|
-
// ------------------------------------------------------------------
|
|
25
|
-
function MatchAnchor(schema, base, ref) {
|
|
26
|
-
const absoluteAnchor = new URL(`#${schema.$anchor}`, base.href);
|
|
27
|
-
const absoluteRef = new URL(ref.href, base.href);
|
|
28
|
-
return Guard.IsEqual(absoluteAnchor.href, absoluteRef.href) ? schema : undefined;
|
|
29
|
-
}
|
|
30
|
-
// ------------------------------------------------------------------
|
|
31
|
-
// Match: DynamicAnchor
|
|
32
|
-
// ------------------------------------------------------------------
|
|
33
|
-
function MatchDynamicAnchor(schema, base, ref) {
|
|
34
|
-
const absoluteAnchor = new URL(`#${schema.$dynamicAnchor}`, base.href);
|
|
35
|
-
const absoluteRef = new URL(ref.href, base.href);
|
|
36
|
-
return Guard.IsEqual(absoluteAnchor.href, absoluteRef.href) ? schema : undefined;
|
|
37
|
-
}
|
|
38
|
-
// ------------------------------------------------------------------
|
|
39
|
-
// Match: Hash
|
|
40
|
-
//
|
|
41
|
-
// Resolves JSON Pointer fragments only. Plain anchor-style fragments
|
|
42
|
-
// (no leading '/') are handled exclusively by MatchAnchor and
|
|
43
|
-
// MatchDynamicAnchor to prevent accidentally resolving an anchor name
|
|
44
|
-
// as a pointer into the schema tree.
|
|
45
|
-
//
|
|
46
|
-
// ------------------------------------------------------------------
|
|
47
|
-
function MatchHash(schema, _base, ref) {
|
|
48
|
-
if (ref.href.endsWith('#'))
|
|
49
|
-
return schema;
|
|
50
|
-
if (!ref.hash.startsWith('#'))
|
|
51
|
-
return undefined;
|
|
52
|
-
const fragment = decodeURIComponent(ref.hash.slice(1));
|
|
53
|
-
if (!fragment.startsWith('/'))
|
|
54
|
-
return undefined;
|
|
55
|
-
return Pointer.Get(schema, fragment);
|
|
56
|
-
}
|
|
57
|
-
// ------------------------------------------------------------------
|
|
58
|
-
// Match
|
|
59
|
-
// ------------------------------------------------------------------
|
|
60
|
-
function Match(schema, base, ref) {
|
|
61
|
-
if (Schema.IsId(schema)) {
|
|
62
|
-
const result = MatchId(schema, base, ref);
|
|
63
|
-
if (!Guard.IsUndefined(result))
|
|
64
|
-
return result;
|
|
65
|
-
}
|
|
66
|
-
if (Schema.IsAnchor(schema)) {
|
|
67
|
-
const result = MatchAnchor(schema, base, ref);
|
|
68
|
-
if (!Guard.IsUndefined(result))
|
|
69
|
-
return result;
|
|
70
|
-
}
|
|
71
|
-
if (Schema.IsDynamicAnchor(schema)) {
|
|
72
|
-
const result = MatchDynamicAnchor(schema, base, ref);
|
|
73
|
-
if (!Guard.IsUndefined(result))
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
return MatchHash(schema, base, ref);
|
|
77
|
-
}
|
|
78
|
-
// ------------------------------------------------------------------
|
|
79
|
-
// FromArray
|
|
80
|
-
// ------------------------------------------------------------------
|
|
81
|
-
function FromArray(schema, base, ref) {
|
|
82
|
-
return schema.reduce((result, item) => {
|
|
83
|
-
const match = FromValue(item, base, ref);
|
|
84
|
-
return !Guard.IsUndefined(match) ? match : result;
|
|
85
|
-
}, undefined);
|
|
86
|
-
}
|
|
87
|
-
// ------------------------------------------------------------------
|
|
88
|
-
// FromObject
|
|
89
|
-
// ------------------------------------------------------------------
|
|
90
|
-
function SkipProperty(key) {
|
|
91
|
-
// these are explicit data encoded in a schema, they should not
|
|
92
|
-
// be considered valid targets used for de-referencing.
|
|
93
|
-
return Guard.IsEqual(key, 'const') || Guard.IsEqual(key, 'enum');
|
|
94
|
-
}
|
|
95
|
-
function FromObject(schema, base, ref) {
|
|
96
|
-
return Guard.Keys(schema).reduce((result, key) => {
|
|
97
|
-
if (SkipProperty(key))
|
|
98
|
-
return result;
|
|
99
|
-
const match = FromValue(schema[key], base, ref);
|
|
100
|
-
return !Guard.IsUndefined(match) ? match : result;
|
|
101
|
-
}, undefined);
|
|
102
|
-
}
|
|
103
|
-
// ------------------------------------------------------------------
|
|
104
|
-
// FromValue
|
|
105
|
-
// ------------------------------------------------------------------
|
|
106
|
-
function FromValue(schema, base, ref) {
|
|
107
|
-
const nextBase = Schema.IsSchemaObject(schema) && Schema.IsId(schema)
|
|
108
|
-
? new URL(schema.$id, base.href)
|
|
109
|
-
: base;
|
|
110
|
-
if (Schema.IsSchemaObject(schema)) {
|
|
111
|
-
const result = Match(schema, nextBase, ref);
|
|
112
|
-
if (!Guard.IsUndefined(result))
|
|
113
|
-
return result;
|
|
114
|
-
}
|
|
115
|
-
if (Guard.IsArray(schema))
|
|
116
|
-
return FromArray(schema, nextBase, ref);
|
|
117
|
-
if (Guard.IsObject(schema))
|
|
118
|
-
return FromObject(schema, nextBase, ref);
|
|
119
|
-
return undefined;
|
|
120
|
-
}
|
|
121
|
-
// ------------------------------------------------------------------
|
|
122
|
-
// Ref
|
|
123
|
-
// ------------------------------------------------------------------
|
|
124
|
-
export function Ref(schema, ref) {
|
|
125
|
-
const initialBase = Schema.IsId(schema) ? new URL(schema.$id, DefaultBase.href) : DefaultBase;
|
|
126
|
-
const initialRef = new URL(ref, initialBase.href);
|
|
127
|
-
return FromValue(schema, initialBase, initialRef);
|
|
128
|
-
}
|
|
129
|
-
// ------------------------------------------------------------------
|
|
130
|
-
// DynamicRef
|
|
131
|
-
// ------------------------------------------------------------------
|
|
132
|
-
export function DynamicRef(root, base, dynamicRef, dynamicAnchors) {
|
|
133
|
-
// Resolve the static target using either the local base (for fragment‑only references)
|
|
134
|
-
// or the document root (for absolute URI references).
|
|
135
|
-
const fragmentTarget = dynamicRef.$dynamicRef.startsWith('#')
|
|
136
|
-
? Ref(base, dynamicRef.$dynamicRef)
|
|
137
|
-
: Ref(root, dynamicRef.$dynamicRef);
|
|
138
|
-
if (Guard.IsUndefined(fragmentTarget))
|
|
139
|
-
return undefined;
|
|
140
|
-
// Dynamic override only applies if the resolved target itself declares a $dynamicAnchor.
|
|
141
|
-
// If it does not, return the static target unchanged.
|
|
142
|
-
if (!Schema.IsSchemaObject(fragmentTarget) || !Schema.IsDynamicAnchor(fragmentTarget))
|
|
143
|
-
return fragmentTarget;
|
|
144
|
-
// Extract the fragment portion of the reference. According to the test suite,
|
|
145
|
-
// only plain fragment names (e.g., "#foo") trigger the dynamic scope; JSON
|
|
146
|
-
// Pointer fragments (e.g., "#/definitions/foo") bypass dynamic resolution.
|
|
147
|
-
const fragment = new URL(dynamicRef.$dynamicRef, DefaultBase).hash;
|
|
148
|
-
if (fragment.startsWith('#/'))
|
|
149
|
-
return fragmentTarget;
|
|
150
|
-
// Search the live dynamic anchor stack for a schema whose $dynamicAnchor matches the
|
|
151
|
-
// target's $dynamicAnchor. The stack reflects the current evaluation path, and
|
|
152
|
-
// find() returns the outermost (first encountered) match, which is the correct
|
|
153
|
-
// lexical scope per the specification.
|
|
154
|
-
const anchorTarget = dynamicAnchors.find(anchor => anchor.$dynamicAnchor === fragmentTarget.$dynamicAnchor);
|
|
155
|
-
return anchorTarget ?? fragmentTarget;
|
|
156
|
-
}
|