ata-validator 1.62.0 → 1.63.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 +68 -13
- package/build.mjs +1 -0
- package/index.browser.mjs +23 -4
- package/index.bundle.mjs +14 -4
- package/index.edge.mjs +14 -4
- package/index.js +7 -2
- package/lib/aot-build.js +18 -0
- package/lib/aot-impl.js +20 -0
- package/lib/codegen-engine.js +72 -3
- package/lib/compiled-common.js +2 -1
- package/lib/email-literal.js +4 -1
- package/lib/error-runtime-features.js +41 -0
- package/lib/error-runtime.browser.js +15 -0
- package/lib/error-runtime.js +1 -32
- package/lib/formats-source.js +39 -40
- package/lib/interpreter-core.js +17 -9
- package/lib/ir/emit-deps.mjs +131 -0
- package/lib/ir/emit.mjs +560 -0
- package/lib/ir/front.mjs +834 -0
- package/lib/ir/index.mjs +10 -0
- package/lib/ir/refs.mjs +108 -0
- package/lib/ir/run.mjs +520 -0
- package/lib/ir/table.mjs +233 -0
- package/lib/js-compiler-errors.js +25 -7
- package/lib/js-compiler.js +6 -2
- package/lib/kernels/collapse.mjs +114 -0
- package/lib/kernels/custom.mjs +70 -0
- package/lib/kernels/fill.mjs +6 -0
- package/lib/kernels/formats-small.mjs +40 -0
- package/lib/kernels/formats.mjs +29 -0
- package/lib/kernels/index.mjs +11 -0
- package/lib/kernels/kernels.mjs +285 -0
- package/lib/kernels/leaves.mjs +28 -0
- package/lib/kernels/order.mjs +25 -0
- package/lib/kernels/plan-rich.mjs +17 -0
- package/lib/kernels/plan.d.ts +9 -0
- package/lib/kernels/plan.mjs +128 -0
- package/lib/kernels/preprocess.mjs +223 -0
- package/lib/kernels/rank.mjs +97 -0
- package/lib/kernels/rt.mjs +249 -0
- package/lib/kernels/table.mjs +182 -0
- package/lib/kernels/unique.mjs +157 -0
- package/lib/native-positions.js +1 -1
- package/lib/openapi.d.ts +2 -0
- package/lib/openapi.js +128 -0
- package/lib/plan-compiler.js +84 -7
- package/lib/public-api.js +7 -2
- package/lib/rejections.js +3 -1
- package/lib/schema-helpers.js +12 -0
- package/lib/schema-helpers.mjs +20 -0
- package/lib/schema-order.js +4 -2
- package/lib/standard-schema.js +1 -1
- package/lib/validator-core.js +35 -30
- package/lib/version.js +1 -1
- package/lite.js +7 -2
- package/package.json +32 -13
package/lib/formats-source.js
CHANGED
|
@@ -96,14 +96,23 @@ function dateTimeSource (v, isStr) {
|
|
|
96
96
|
// third parameter here would silently receive a context object and emit
|
|
97
97
|
// `_st=[object Object]`. That declines the whole compile, which is how it was
|
|
98
98
|
// found.
|
|
99
|
+
// Membership in a set of ASCII characters as one shift: the 32-bit word for
|
|
100
|
+
// c's quarter of the range is picked by comparison and `>>>` reads bit c&31.
|
|
101
|
+
// Four numbers rather than a 128-entry table, so a module carries a few bytes,
|
|
102
|
+
// and one test per character where a chain of comparisons ran up to twelve.
|
|
103
|
+
// tests/test_format_char_masks.js checks every code point against the rule.
|
|
104
|
+
const maskSource = (v, w) => `((${v}<64?${v}<32?${w[0]}:${w[1]}:${v}<96?${w[2]}:${v}<128?${w[3]}:0)>>>${v}&1)`;
|
|
105
|
+
// A hostname label character: a letter, a digit or the hyphen.
|
|
106
|
+
const HOST_CHAR_WORDS = [0, 67051520, 134217726, 134217726];
|
|
107
|
+
const HOST_CHAR_SRC = (v) => maskSource(v, HOST_CHAR_WORDS);
|
|
108
|
+
|
|
99
109
|
function hostnameRangeSource (v, st, en) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
`const
|
|
104
|
-
`if(
|
|
105
|
-
`if(_c===45&&_pv===46)return false
|
|
106
|
-
`if(++_ll>63)return false;_pv=_c}` +
|
|
110
|
+
// An empty name never counts a label, so the `_ll===0` test at the end
|
|
111
|
+
// refuses it without a length test of its own.
|
|
112
|
+
return `if(${en}-(${st})>253)return false;let _ll=0,_pv=46;` +
|
|
113
|
+
`for(let _i=${st};_i<${en};_i++){const _c=${v}.charCodeAt(_i);` +
|
|
114
|
+
`if(_c===46){if(_ll===0||_pv===45)return false;_ll=0}` +
|
|
115
|
+
`else if(!${HOST_CHAR_SRC('_c')}||_c===45&&_pv===46||++_ll>63)return false;_pv=_c}` +
|
|
107
116
|
`if(_ll===0||_pv===45)return false`;
|
|
108
117
|
}
|
|
109
118
|
|
|
@@ -257,19 +266,14 @@ function uriTemplateSource (v, isStr) {
|
|
|
257
266
|
// trailing or doubled dot is not a mailbox. Domain is a hostname or a bracketed
|
|
258
267
|
// address literal.
|
|
259
268
|
// Local-part characters: alphanumerics, the RFC 5322 atext punctuation, and
|
|
260
|
-
// the dot that separates atoms
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
// not atext or the dot, so excluding them is 54 source bytes shorter than listing
|
|
269
|
-
// what is allowed, and a standalone module carries this expression. Checked
|
|
270
|
-
// against the allowed-list form over every code point from 0 to 65536 in
|
|
271
|
-
// tests/test_formats_single_pass.js.
|
|
272
|
-
const EMAIL_LOCAL_SRC = (v) => `(${v}>32&&${v}<127&&${v}!==34&&${v}!==40&&${v}!==41&&${v}!==44&&${v}!==58&&${v}!==59&&${v}!==60&&${v}!==62&&${v}!==64&&${v}!==91&&${v}!==92&&${v}!==93)`;
|
|
269
|
+
// the dot that separates atoms, as a mask (maskSource above): every printable
|
|
270
|
+
// character except the twelve that are neither atext nor the dot. It replaced a
|
|
271
|
+
// chain of twelve comparisons, which was more bytes in every module that checks
|
|
272
|
+
// an email and up to twelve tests per character. Checked against the allowed-list
|
|
273
|
+
// form over every code point from 0 to 65536 in tests/test_format_char_masks.js,
|
|
274
|
+
// and against the runtime's check by `tests/test_format_engine_parity.js`.
|
|
275
|
+
const EMAIL_LOCAL_WORDS = [0, 2751458554, 3355443198, 2147483647];
|
|
276
|
+
const EMAIL_LOCAL_SRC = (v) => maskSource(v, EMAIL_LOCAL_WORDS);
|
|
273
277
|
// Emitted rather than bound as a closure: a standalone module embeds format
|
|
274
278
|
// functions by their source text, so anything reaching a free variable would
|
|
275
279
|
// not survive the trip. The domain half reuses hostnameSource, which keeps
|
|
@@ -295,31 +299,26 @@ function emailSource (v, isStr) {
|
|
|
295
299
|
// character, so in an unquoted address the first one is the separator and the
|
|
296
300
|
// local part is checked on the way to it. A quoted local part may contain
|
|
297
301
|
// '@' and keeps the backward scan, which is the rare shape.
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
// lands in every emitted module that
|
|
302
|
-
//
|
|
303
|
-
`const _n0=${v}.length;let _at;` +
|
|
302
|
+
// Both shapes share their variables and one bounds test after the local
|
|
303
|
+
// part: an empty local part fails `_at<1`, an empty domain `_at===_n0-1`,
|
|
304
|
+
// and `_pd` holds the character before the '@' (46 when it is a dot, which
|
|
305
|
+
// a quoted part sets aside). This lands in every emitted module that
|
|
306
|
+
// validates an email, so its bytes are counted (tests/test_email_bytes.js).
|
|
307
|
+
`const _n0=${v}.length;let _at=-1,_qi,_qc,_pd=46;` +
|
|
304
308
|
`if(${v}.charCodeAt(0)===34){` +
|
|
305
|
-
`_at=${v}.lastIndexOf('@');` +
|
|
306
|
-
`
|
|
307
|
-
`
|
|
308
|
-
`if(_qc
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
`_at=-1;let _pd=false;` +
|
|
313
|
-
`for(let _i=0;_i<_n0;_i++){const _lc=${v}.charCodeAt(_i);if(_lc===64){_at=_i;break}if(!${EMAIL_LOCAL_SRC('_lc')})return false;if(_lc===46){if(_pd)return false;_pd=true}else _pd=false}` +
|
|
314
|
-
`if(_at<=0||_at===_n0-1||_at>64||${v}.charCodeAt(_at-1)===46)return false` +
|
|
315
|
-
`}` +
|
|
309
|
+
`_at=${v}.lastIndexOf('@');if(_at<2||${v}.charCodeAt(_at-1)!==34)return false;` +
|
|
310
|
+
`for(_qi=1;_qi<_at-1;_qi++){_qc=${v}.charCodeAt(_qi);` +
|
|
311
|
+
`if(_qc===92){_qc=${v}.charCodeAt(++_qi);if(_qc<32||_qc>126||_qi>=_at-1)return false}` +
|
|
312
|
+
`else if(_qc<32||_qc>126||_qc===34)return false}_pd=0` +
|
|
313
|
+
`}else for(_qi=0;_qi<_n0;_qi++){_qc=${v}.charCodeAt(_qi);if(_qc===64){_at=_qi;break}` +
|
|
314
|
+
`if(_qc===46?_pd===46:!${EMAIL_LOCAL_SRC('_qc')})return false;_pd=_qc}` +
|
|
315
|
+
`if(_at<1||_at>64||_at===_n0-1||_pd===46)return false;` +
|
|
316
316
|
// An address literal is rare enough to pay for its own slice inside the
|
|
317
317
|
// branch that needs it; the hostname path, which is every real address,
|
|
318
318
|
// reads the domain in place.
|
|
319
319
|
`if(${v}.charCodeAt(_at+1)===91){` +
|
|
320
|
-
`
|
|
321
|
-
`
|
|
322
|
-
`const _in=_dm.slice(1,-1);` +
|
|
320
|
+
`if(${v}.charCodeAt(_n0-1)!==93||_n0-_at<4)return false;` +
|
|
321
|
+
`const _in=${v}.slice(_at+2,-1);` +
|
|
323
322
|
// The literal is rare, and checked by expression rather than by the
|
|
324
323
|
// IPv6 and IPv4 parsers, which made it 72% of this check's source:
|
|
325
324
|
// the same answers (tests/test_ipv6_expression.js), a fraction of the bytes.
|
package/lib/interpreter-core.js
CHANGED
|
@@ -89,8 +89,6 @@ function indexSchemas(rootSchema, schemaMap, flat) {
|
|
|
89
89
|
state.rootBase = rootBase;
|
|
90
90
|
if (flat === true && !schemaMap && typeof rootSchema === 'object' && rootSchema !== null) {
|
|
91
91
|
state.resources.set(rootBase, rootSchema);
|
|
92
|
-
state.anchors.set(rootBase, new Map());
|
|
93
|
-
state.dynamicAnchors.set(rootBase, new Map());
|
|
94
92
|
state.nodeBase.set(rootSchema, rootBase);
|
|
95
93
|
return state;
|
|
96
94
|
}
|
|
@@ -107,11 +105,15 @@ function indexSchemas(rootSchema, schemaMap, flat) {
|
|
|
107
105
|
return state;
|
|
108
106
|
}
|
|
109
107
|
|
|
108
|
+
function anchorMap(maps, baseUri) {
|
|
109
|
+
let m = maps.get(baseUri);
|
|
110
|
+
if (m === undefined) maps.set(baseUri, (m = new Map()));
|
|
111
|
+
return m;
|
|
112
|
+
}
|
|
113
|
+
|
|
110
114
|
function indexResource(node, baseUri, state) {
|
|
111
115
|
if (typeof node !== 'object' || node === null) return;
|
|
112
116
|
if (!state.resources.has(baseUri)) state.resources.set(baseUri, node);
|
|
113
|
-
if (!state.anchors.has(baseUri)) state.anchors.set(baseUri, new Map());
|
|
114
|
-
if (!state.dynamicAnchors.has(baseUri)) state.dynamicAnchors.set(baseUri, new Map());
|
|
115
117
|
walkSchema(node, baseUri, state, true);
|
|
116
118
|
}
|
|
117
119
|
|
|
@@ -128,11 +130,14 @@ function walkSchema(node, baseUri, state, isResourceRoot) {
|
|
|
128
130
|
return;
|
|
129
131
|
}
|
|
130
132
|
|
|
131
|
-
|
|
133
|
+
// A resource's anchor maps are made at its first anchor: most schemas have
|
|
134
|
+
// none, and two empty maps per resource were a sixth of a small schema's
|
|
135
|
+
// interpreter. Every reader takes a missing map as no anchor.
|
|
136
|
+
if (typeof node.$anchor === 'string') anchorMap(state.anchors, baseUri).set(node.$anchor, node);
|
|
132
137
|
if (typeof node.$dynamicAnchor === 'string') {
|
|
133
|
-
state.dynamicAnchors
|
|
138
|
+
anchorMap(state.dynamicAnchors, baseUri).set(node.$dynamicAnchor, node);
|
|
134
139
|
// A $dynamicAnchor is also addressable as a plain anchor.
|
|
135
|
-
state.anchors
|
|
140
|
+
anchorMap(state.anchors, baseUri).set(node.$dynamicAnchor, node);
|
|
136
141
|
}
|
|
137
142
|
|
|
138
143
|
for (const kw of SCHEMA_KEYWORDS.single) {
|
|
@@ -847,7 +852,8 @@ class Interpreter {
|
|
|
847
852
|
// 2020-12 requires bookending for `$dynamicRef`; v1 removes the
|
|
848
853
|
// requirement. See the `$dynamicRef` branch in eval().
|
|
849
854
|
this.bookending = !opts.v1;
|
|
850
|
-
|
|
855
|
+
// Made at the first pattern.
|
|
856
|
+
this.patternCache = null;
|
|
851
857
|
// Plans are built on first visit; a schema node is never re-read after.
|
|
852
858
|
this.plans = new Map();
|
|
853
859
|
this.rootNode = this.node(rootSchema);
|
|
@@ -919,6 +925,7 @@ class Interpreter {
|
|
|
919
925
|
}
|
|
920
926
|
|
|
921
927
|
pattern(src) {
|
|
928
|
+
if (this.patternCache === null) this.patternCache = new Map();
|
|
922
929
|
let re = this.patternCache.get(src);
|
|
923
930
|
if (!re) {
|
|
924
931
|
if (/\\[pP]\{/.test(src)) {
|
|
@@ -1549,7 +1556,8 @@ function planCompiler() {
|
|
|
1549
1556
|
let installCompat = null;
|
|
1550
1557
|
let compatInstalled = null;
|
|
1551
1558
|
function setCompat(install) {
|
|
1552
|
-
|
|
1559
|
+
// esm.sh hands over a module of one function as { default: fn }.
|
|
1560
|
+
installCompat = typeof install === 'function' ? install : install && install.default;
|
|
1553
1561
|
compatInstalled = null;
|
|
1554
1562
|
}
|
|
1555
1563
|
function compatExt() {
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Generated by scripts/gen-ir-emit-deps.js from lib/js-compiler.js and lib/suggestions.js. Do not edit.
|
|
2
|
+
const FIXED_LITERAL = /[A-Za-z0-9 _\-:@#%&=,;'"<>!~`\/]/
|
|
3
|
+
function compilePatternInline(pattern, varName) {
|
|
4
|
+
let m = pattern.match(/^\^(\[[\w\-]+\])\{(\d+)\}\$$/)
|
|
5
|
+
if (m) {
|
|
6
|
+
const len = parseInt(m[2])
|
|
7
|
+
if (len <= 16) {
|
|
8
|
+
const checks = []
|
|
9
|
+
for (let i = 0; i < len; i++) {
|
|
10
|
+
const ck = charClassToCheck(m[1], `${varName}.charCodeAt(${i})`)
|
|
11
|
+
if (!ck) return null
|
|
12
|
+
checks.push(ck)
|
|
13
|
+
}
|
|
14
|
+
return `${varName}.length===${len}&&${checks.join('&&')}`
|
|
15
|
+
}
|
|
16
|
+
const rangeCheck = charClassToCheck(m[1], `${varName}.charCodeAt(_pi)`)
|
|
17
|
+
if (!rangeCheck) return null
|
|
18
|
+
return `${varName}.length===${len}&&(()=>{for(let _pi=0;_pi<${len};_pi++){if(!(${rangeCheck}))return false}return true})()`
|
|
19
|
+
}
|
|
20
|
+
m = pattern.match(/^\^(\[[\w\-]+\])\+\$$/)
|
|
21
|
+
if (m) {
|
|
22
|
+
const rangeCheck = charClassToCheck(m[1], `${varName}.charCodeAt(_pi)`)
|
|
23
|
+
if (!rangeCheck) return null
|
|
24
|
+
return `${varName}.length>0&&(()=>{for(let _pi=0;_pi<${varName}.length;_pi++){if(!(${rangeCheck}))return false}return true})()`
|
|
25
|
+
}
|
|
26
|
+
m = pattern.match(/^\^(\[[\w\-]+\])\{(\d+),(\d+)\}\$$/)
|
|
27
|
+
if (m) {
|
|
28
|
+
const rangeCheck = charClassToCheck(m[1], `${varName}.charCodeAt(_pi)`)
|
|
29
|
+
if (!rangeCheck) return null
|
|
30
|
+
const min = parseInt(m[2]), max = parseInt(m[3])
|
|
31
|
+
return `${varName}.length>=${min}&&${varName}.length<=${max}&&(()=>{for(let _pi=0;_pi<${varName}.length;_pi++){if(!(${rangeCheck}))return false}return true})()`
|
|
32
|
+
}
|
|
33
|
+
return fixedSequenceInline(pattern, varName)
|
|
34
|
+
}
|
|
35
|
+
function fixedSequenceInline(pattern, varName) {
|
|
36
|
+
if (pattern.length < 3 || pattern[0] !== '^' || pattern[pattern.length - 1] !== '$' || pattern[pattern.length - 2] === '\\') return null
|
|
37
|
+
const end = pattern.length - 1
|
|
38
|
+
const pieces = []
|
|
39
|
+
let i = 1
|
|
40
|
+
while (i < end) {
|
|
41
|
+
let piece
|
|
42
|
+
const c = pattern[i]
|
|
43
|
+
if (c === '[') {
|
|
44
|
+
const j = pattern.indexOf(']', i)
|
|
45
|
+
if (j < 0 || j >= end) return null
|
|
46
|
+
const cls = pattern.slice(i, j + 1)
|
|
47
|
+
if (!/^\[[\w-]+\]$/.test(cls)) return null
|
|
48
|
+
piece = { cls }
|
|
49
|
+
i = j + 1
|
|
50
|
+
} else if (c === '\\') {
|
|
51
|
+
const e = pattern[i + 1]
|
|
52
|
+
if (e === 'd') piece = { cls: '[0-9]' }
|
|
53
|
+
else if (e !== undefined && './()[]{}+*?^$|\\'.includes(e)) piece = { lit: e }
|
|
54
|
+
else return null
|
|
55
|
+
i += 2
|
|
56
|
+
} else if (FIXED_LITERAL.test(c)) {
|
|
57
|
+
piece = { lit: c }
|
|
58
|
+
i += 1
|
|
59
|
+
} else {
|
|
60
|
+
return null
|
|
61
|
+
}
|
|
62
|
+
let count = 1
|
|
63
|
+
if (pattern[i] === '{') {
|
|
64
|
+
const q = /^\{(\d+)\}/.exec(pattern.slice(i))
|
|
65
|
+
if (!q) return null
|
|
66
|
+
count = Number(q[1])
|
|
67
|
+
if (!Number.isSafeInteger(count) || count > 32 - pieces.length) return null
|
|
68
|
+
i += q[0].length
|
|
69
|
+
} else if (i < end && '*+?'.includes(pattern[i])) {
|
|
70
|
+
return null
|
|
71
|
+
}
|
|
72
|
+
for (let k = 0; k < count; k++) pieces.push(piece)
|
|
73
|
+
if (pieces.length > 32) return null
|
|
74
|
+
}
|
|
75
|
+
if (pieces.length === 0) return null
|
|
76
|
+
const checks = []
|
|
77
|
+
for (let k = 0; k < pieces.length; k++) {
|
|
78
|
+
const at = `${varName}.charCodeAt(${k})`
|
|
79
|
+
if (pieces[k].lit !== undefined) checks.push(`${at}===${pieces[k].lit.charCodeAt(0)}`)
|
|
80
|
+
else {
|
|
81
|
+
const ck = charClassToCheck(pieces[k].cls, at)
|
|
82
|
+
if (!ck) return null
|
|
83
|
+
checks.push(`(${ck})`)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return `${varName}.length===${pieces.length}&&${checks.join('&&')}`
|
|
87
|
+
}
|
|
88
|
+
function charClassToCheck(charClass, codeExpr) {
|
|
89
|
+
const inner = charClass.slice(1, -1)
|
|
90
|
+
const ranges = []
|
|
91
|
+
let i = 0
|
|
92
|
+
while (i < inner.length) {
|
|
93
|
+
if (i + 2 < inner.length && inner[i + 1] === '-') {
|
|
94
|
+
ranges.push([inner.charCodeAt(i), inner.charCodeAt(i + 2)])
|
|
95
|
+
i += 3
|
|
96
|
+
} else {
|
|
97
|
+
ranges.push([inner.charCodeAt(i), inner.charCodeAt(i)])
|
|
98
|
+
i++
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (ranges.length === 0) return null
|
|
102
|
+
const checks = ranges.map(([lo, hi]) =>
|
|
103
|
+
lo === hi ? `${codeExpr}===${lo}` : `(${codeExpr}>=${lo}&&${codeExpr}<=${hi})`
|
|
104
|
+
)
|
|
105
|
+
return `(${checks.join('||')})`
|
|
106
|
+
}
|
|
107
|
+
const FORMAT_HINTS = {
|
|
108
|
+
email: (val) => {
|
|
109
|
+
if (typeof val !== 'string') return null;
|
|
110
|
+
if (!val.includes('@')) return "missing '@' and domain part";
|
|
111
|
+
if (val.split('@').length > 2) return "multiple '@' characters";
|
|
112
|
+
const [, dom] = val.split('@');
|
|
113
|
+
if (!dom || !dom.includes('.')) return 'domain part missing dot';
|
|
114
|
+
return null;
|
|
115
|
+
},
|
|
116
|
+
date: (val) => {
|
|
117
|
+
if (typeof val !== 'string') return null;
|
|
118
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(val);
|
|
119
|
+
if (!m) return 'expected YYYY-MM-DD layout';
|
|
120
|
+
const mo = +m[2]; if (mo < 1 || mo > 12) return 'month must be 01-12';
|
|
121
|
+
const d = +m[3]; if (d < 1 || d > 31) return 'day must be 01-31';
|
|
122
|
+
return null;
|
|
123
|
+
},
|
|
124
|
+
uuid: (val) => {
|
|
125
|
+
if (typeof val !== 'string') return null;
|
|
126
|
+
if (!/^[0-9a-fA-F-]+$/.test(val)) return 'expected hex digits and dashes';
|
|
127
|
+
return 'expected 8-4-4-4-12 hex layout';
|
|
128
|
+
},
|
|
129
|
+
ipv4: (val) => typeof val === 'string' ? 'expected four 0-255 octets separated by dots' : null,
|
|
130
|
+
};
|
|
131
|
+
export { compilePatternInline, FORMAT_HINTS };
|