@amritk/lint 0.3.2 → 0.3.3
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/dist/core/document.js +12 -10
- package/dist/core/formats.js +10 -13
- package/dist/core/glob.js +96 -118
- package/dist/core/index.js +31 -11
- package/dist/core/jsonpath.js +487 -561
- package/dist/core/lint.js +78 -85
- package/dist/core/plugin.js +21 -29
- package/dist/core/pointers.js +107 -151
- package/dist/core/ruleset.js +0 -0
- package/dist/core/runner.js +214 -272
- package/dist/core/types.js +4 -1
- package/dist/core/validate-ruleset.js +98 -112
- package/dist/fix/apply.js +58 -96
- package/dist/fix/index.js +7 -2
- package/dist/fix/plugin.js +15 -20
- package/dist/functions/alphabetical.js +39 -50
- package/dist/functions/casing.js +39 -45
- package/dist/functions/defined.js +7 -5
- package/dist/functions/enumeration.js +15 -25
- package/dist/functions/falsy.js +7 -5
- package/dist/functions/index.js +60 -44
- package/dist/functions/length.js +22 -32
- package/dist/functions/or.js +18 -24
- package/dist/functions/pattern.js +39 -49
- package/dist/functions/schema.js +93 -119
- package/dist/functions/truthy.js +7 -5
- package/dist/functions/typed-enum.js +33 -36
- package/dist/functions/undefined.js +7 -8
- package/dist/functions/unreferenced-reusable-object.js +35 -47
- package/dist/functions/xor.js +13 -16
- package/dist/index.js +114 -164
- package/dist/parsers/edit-model.js +316 -444
- package/dist/parsers/index.js +22 -19
- package/dist/parsers/json.js +39 -37
- package/dist/parsers/lines.js +23 -26
- package/dist/parsers/types.js +9 -7
- package/dist/parsers/yaml.js +137 -204
- package/dist/rules/openapi/fixers.js +166 -221
- package/dist/rules/openapi/formats.js +26 -27
- package/dist/rules/openapi/functions/example-validation.js +98 -138
- package/dist/rules/openapi/functions/helpers.js +8 -10
- package/dist/rules/openapi/functions/index.js +98 -72
- package/dist/rules/openapi/functions/oas-additional-operations.js +16 -22
- package/dist/rules/openapi/functions/oas-discriminator.js +24 -22
- package/dist/rules/openapi/functions/oas-example-external-value.js +13 -21
- package/dist/rules/openapi/functions/oas-example-value.js +24 -27
- package/dist/rules/openapi/functions/oas-mutually-exclusive.js +15 -19
- package/dist/rules/openapi/functions/oas-no-nullable.js +13 -21
- package/dist/rules/openapi/functions/oas-op-form-data-consume-check.js +21 -19
- package/dist/rules/openapi/functions/oas-op-id-unique.js +27 -27
- package/dist/rules/openapi/functions/oas-op-params.js +36 -42
- package/dist/rules/openapi/functions/oas-op-security-defined.js +40 -39
- package/dist/rules/openapi/functions/oas-op-success-response.js +11 -13
- package/dist/rules/openapi/functions/oas-path-param.js +75 -96
- package/dist/rules/openapi/functions/oas-schema-example-deprecated.js +33 -40
- package/dist/rules/openapi/functions/oas-schema.js +9 -14
- package/dist/rules/openapi/functions/oas-server-name-unique.js +21 -19
- package/dist/rules/openapi/functions/oas-server-variables.js +45 -49
- package/dist/rules/openapi/functions/oas-tag-defined.js +20 -20
- package/dist/rules/openapi/functions/oas-tag-kind.js +16 -16
- package/dist/rules/openapi/functions/oas-tag-parent-defined.js +40 -43
- package/dist/rules/openapi/functions/oas-tags-unique.js +18 -16
- package/dist/rules/openapi/functions/oas-unused-component.js +48 -58
- package/dist/rules/openapi/functions/ref-siblings.js +13 -11
- package/dist/rules/openapi/index.js +99 -117
- package/dist/rules/openapi/oas.js +524 -536
- package/dist/rules/openapi/schemas/index.js +17 -33
- package/dist/rules/openapi/schemas/oas20.json +1 -1592
- package/dist/rules/openapi/schemas/oas30.json +1 -1651
- package/dist/rules/openapi/schemas/oas31.json +1 -1412
- package/dist/rules/openapi/schemas/oas32.json +1 -1684
- package/package.json +5 -5
package/dist/core/jsonpath.js
CHANGED
|
@@ -1,610 +1,536 @@
|
|
|
1
|
-
const isObject = (value) => typeof value ===
|
|
2
|
-
// jsonpath-plus emits numeric array indices as strings and Linter historically
|
|
3
|
-
// normalized *any* all-digit segment (including object keys like "200") to a
|
|
4
|
-
// number. Replicate that exactly so source-map lookups are unchanged.
|
|
1
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5
2
|
const normalizeSegment = (segment) => {
|
|
6
|
-
|
|
7
|
-
return segment;
|
|
8
|
-
if (segment.length > 0 && /^\d+$/.test(segment))
|
|
9
|
-
return Number(segment);
|
|
3
|
+
if (typeof segment === "number")
|
|
10
4
|
return segment;
|
|
5
|
+
if (segment.length > 0 && /^\d+$/.test(segment))
|
|
6
|
+
return Number(segment);
|
|
7
|
+
return segment;
|
|
11
8
|
};
|
|
12
9
|
const normalizePath = (path) => path.map(normalizeSegment);
|
|
13
|
-
/**
|
|
14
|
-
* Renders a concrete path as the jsonpath-plus string form (`$['a'][0]`). This
|
|
15
|
-
* is what `@path` exposes inside a filter, so ruleset filters authored for
|
|
16
|
-
* Spectral (which run on jsonpath-plus) see the same value.
|
|
17
|
-
*/
|
|
18
10
|
const pathToJsonPathString = (path) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
11
|
+
let out = "$";
|
|
12
|
+
for (const segment of path) {
|
|
13
|
+
if (typeof segment === "number")
|
|
14
|
+
out += `[${segment}]`;
|
|
15
|
+
else
|
|
16
|
+
out += `['${segment.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}']`;
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
27
19
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
const compileCache = new Map();
|
|
32
|
-
const filterCache = new Map();
|
|
33
|
-
/**
|
|
34
|
-
* Rewrites jsonpath-plus' `@`-context tokens onto real identifiers, skipping any
|
|
35
|
-
* `@` that sits inside a quoted string literal (so an expression like
|
|
36
|
-
* `@.name.indexOf("@")` keeps the literal `@` intact). Longer tokens are matched
|
|
37
|
-
* first so `@parentProperty` is not eaten by `@parent`.
|
|
38
|
-
*/
|
|
20
|
+
const compileCache = /* @__PURE__ */ new Map();
|
|
21
|
+
const filterCache = /* @__PURE__ */ new Map();
|
|
39
22
|
const substituteContext = (source) => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (ch === '"' || ch === "'") {
|
|
56
|
-
quote = ch;
|
|
57
|
-
out += ch;
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
if (ch === '@') {
|
|
61
|
-
const rest = source.slice(i);
|
|
62
|
-
if (rest.startsWith('@parentProperty')) {
|
|
63
|
-
out += '_pp';
|
|
64
|
-
i += '@parentProperty'.length - 1;
|
|
65
|
-
}
|
|
66
|
-
else if (rest.startsWith('@parent')) {
|
|
67
|
-
out += '_parent';
|
|
68
|
-
i += '@parent'.length - 1;
|
|
69
|
-
}
|
|
70
|
-
else if (rest.startsWith('@property')) {
|
|
71
|
-
out += '_prop';
|
|
72
|
-
i += '@property'.length - 1;
|
|
73
|
-
}
|
|
74
|
-
else if (rest.startsWith('@path')) {
|
|
75
|
-
out += '_path';
|
|
76
|
-
i += '@path'.length - 1;
|
|
77
|
-
}
|
|
78
|
-
else if (rest.startsWith('@root')) {
|
|
79
|
-
out += '_root';
|
|
80
|
-
i += '@root'.length - 1;
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
out += '_v';
|
|
84
|
-
}
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
out += ch;
|
|
23
|
+
let out = "";
|
|
24
|
+
let quote = "";
|
|
25
|
+
for (let i = 0; i < source.length; i++) {
|
|
26
|
+
const ch = source[i];
|
|
27
|
+
if (quote) {
|
|
28
|
+
out += ch;
|
|
29
|
+
if (ch === "\\" && i + 1 < source.length) {
|
|
30
|
+
out += source[i + 1];
|
|
31
|
+
i++;
|
|
32
|
+
} else if (ch === quote) {
|
|
33
|
+
quote = "";
|
|
34
|
+
}
|
|
35
|
+
continue;
|
|
88
36
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (cached)
|
|
94
|
-
return cached;
|
|
95
|
-
const body = substituteContext(source);
|
|
96
|
-
let fn;
|
|
97
|
-
try {
|
|
98
|
-
// `_pp` (`@parentProperty`) is supplied directly by the caller rather than
|
|
99
|
-
// derived from a materialized path, so most filters never force a path
|
|
100
|
-
// allocation. `_path` (`@path`) is only materialized for filters that use it.
|
|
101
|
-
const compiled = new Function('_v', '_prop', '_parent', '_root', '_path', '_pp', `try { return !!(${body}); } catch (_e) { return false; }`);
|
|
102
|
-
fn = compiled;
|
|
37
|
+
if (ch === '"' || ch === "'") {
|
|
38
|
+
quote = ch;
|
|
39
|
+
out += ch;
|
|
40
|
+
continue;
|
|
103
41
|
}
|
|
104
|
-
|
|
105
|
-
|
|
42
|
+
if (ch === "@") {
|
|
43
|
+
const rest = source.slice(i);
|
|
44
|
+
if (rest.startsWith("@parentProperty")) {
|
|
45
|
+
out += "_pp";
|
|
46
|
+
i += "@parentProperty".length - 1;
|
|
47
|
+
} else if (rest.startsWith("@parent")) {
|
|
48
|
+
out += "_parent";
|
|
49
|
+
i += "@parent".length - 1;
|
|
50
|
+
} else if (rest.startsWith("@property")) {
|
|
51
|
+
out += "_prop";
|
|
52
|
+
i += "@property".length - 1;
|
|
53
|
+
} else if (rest.startsWith("@path")) {
|
|
54
|
+
out += "_path";
|
|
55
|
+
i += "@path".length - 1;
|
|
56
|
+
} else if (rest.startsWith("@root")) {
|
|
57
|
+
out += "_root";
|
|
58
|
+
i += "@root".length - 1;
|
|
59
|
+
} else {
|
|
60
|
+
out += "_v";
|
|
61
|
+
}
|
|
62
|
+
continue;
|
|
106
63
|
}
|
|
107
|
-
|
|
108
|
-
|
|
64
|
+
out += ch;
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
};
|
|
68
|
+
const compileFilter = (source) => {
|
|
69
|
+
const cached = filterCache.get(source);
|
|
70
|
+
if (cached)
|
|
71
|
+
return cached;
|
|
72
|
+
const body = substituteContext(source);
|
|
73
|
+
let fn;
|
|
74
|
+
try {
|
|
75
|
+
const compiled = new Function("_v", "_prop", "_parent", "_root", "_path", "_pp", `try { return !!(${body}); } catch (_e) { return false; }`);
|
|
76
|
+
fn = compiled;
|
|
77
|
+
} catch {
|
|
78
|
+
fn = () => false;
|
|
79
|
+
}
|
|
80
|
+
filterCache.set(source, fn);
|
|
81
|
+
return fn;
|
|
109
82
|
};
|
|
110
|
-
/** Splits bracket content on top-level commas, respecting quotes (and their escapes). */
|
|
111
83
|
const splitUnion = (content) => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
if (ch === '"' || ch === "'") {
|
|
130
|
-
quote = ch;
|
|
131
|
-
current += ch;
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
if (ch === '[' || ch === '(')
|
|
135
|
-
depth++;
|
|
136
|
-
else if (ch === ']' || ch === ')')
|
|
137
|
-
depth--;
|
|
138
|
-
if (ch === ',' && depth === 0) {
|
|
139
|
-
parts.push(current);
|
|
140
|
-
current = '';
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
current += ch;
|
|
84
|
+
const parts = [];
|
|
85
|
+
let depth = 0;
|
|
86
|
+
let quote = "";
|
|
87
|
+
let current = "";
|
|
88
|
+
for (let i = 0; i < content.length; i++) {
|
|
89
|
+
const ch = content[i];
|
|
90
|
+
if (quote) {
|
|
91
|
+
if (ch === "\\" && i + 1 < content.length) {
|
|
92
|
+
current += ch + content[i + 1];
|
|
93
|
+
i++;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (ch === quote)
|
|
97
|
+
quote = "";
|
|
98
|
+
current += ch;
|
|
99
|
+
continue;
|
|
144
100
|
}
|
|
145
|
-
|
|
146
|
-
|
|
101
|
+
if (ch === '"' || ch === "'") {
|
|
102
|
+
quote = ch;
|
|
103
|
+
current += ch;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (ch === "[" || ch === "(")
|
|
107
|
+
depth++;
|
|
108
|
+
else if (ch === "]" || ch === ")")
|
|
109
|
+
depth--;
|
|
110
|
+
if (ch === "," && depth === 0) {
|
|
111
|
+
parts.push(current);
|
|
112
|
+
current = "";
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
current += ch;
|
|
116
|
+
}
|
|
117
|
+
parts.push(current);
|
|
118
|
+
return parts;
|
|
147
119
|
};
|
|
148
|
-
/** Unquotes a `'...'` / `"..."` token, honoring backslash escapes; returns null when not a quoted literal. */
|
|
149
120
|
const unquote = (token) => {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
if (ch === quote) {
|
|
165
|
-
closed = true;
|
|
166
|
-
i++;
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
out += ch;
|
|
170
|
-
i++;
|
|
121
|
+
const t = token.trim();
|
|
122
|
+
const quote = t[0];
|
|
123
|
+
if (t.length < 2 || quote !== '"' && quote !== "'")
|
|
124
|
+
return null;
|
|
125
|
+
let out = "";
|
|
126
|
+
let i = 1;
|
|
127
|
+
let closed = false;
|
|
128
|
+
while (i < t.length) {
|
|
129
|
+
const ch = t[i];
|
|
130
|
+
if (ch === "\\" && i + 1 < t.length) {
|
|
131
|
+
out += t[i + 1];
|
|
132
|
+
i += 2;
|
|
133
|
+
continue;
|
|
171
134
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
135
|
+
if (ch === quote) {
|
|
136
|
+
closed = true;
|
|
137
|
+
i++;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
out += ch;
|
|
141
|
+
i++;
|
|
142
|
+
}
|
|
143
|
+
if (!closed || i !== t.length)
|
|
144
|
+
return null;
|
|
145
|
+
return out;
|
|
176
146
|
};
|
|
177
|
-
// A slice is `start:end` or `start:end:step`, each part optional and possibly
|
|
178
|
-
// negative, e.g. `0:2`, `-1:`, `::2`, `:`.
|
|
179
147
|
const SLICE_RE = /^-?\d*:-?\d*(:-?\d+)?$/;
|
|
180
148
|
const buildSlice = (trimmed) => {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
149
|
+
const [s, e, st] = trimmed.split(":");
|
|
150
|
+
const num = (value) => value === void 0 || value === "" ? void 0 : Number(value);
|
|
151
|
+
const selector = { kind: "slice" };
|
|
152
|
+
const start = num(s);
|
|
153
|
+
const end = num(e);
|
|
154
|
+
const step = num(st);
|
|
155
|
+
if (start !== void 0)
|
|
156
|
+
selector.start = start;
|
|
157
|
+
if (end !== void 0)
|
|
158
|
+
selector.end = end;
|
|
159
|
+
if (step !== void 0)
|
|
160
|
+
selector.step = step;
|
|
161
|
+
return selector;
|
|
194
162
|
};
|
|
195
163
|
const bracketSelector = (content, onError) => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
names.push(literal);
|
|
230
|
-
}
|
|
231
|
-
else if (/^-?\d+$/.test(token)) {
|
|
232
|
-
names.push(Number(token));
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
names.push(token);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
if (names.length === 1) {
|
|
239
|
-
const only = names[0];
|
|
240
|
-
return typeof only === 'number' ? { kind: 'index', index: only } : { kind: 'child', name: only };
|
|
164
|
+
const trimmed = content.trim();
|
|
165
|
+
if (trimmed === "*")
|
|
166
|
+
return { kind: "wildcard" };
|
|
167
|
+
if (trimmed.startsWith("?")) {
|
|
168
|
+
const open = trimmed.indexOf("(");
|
|
169
|
+
const close = trimmed.lastIndexOf(")");
|
|
170
|
+
const expr = open !== -1 && close > open ? trimmed.slice(open + 1, close) : trimmed.slice(1);
|
|
171
|
+
return { kind: "filter", test: compileFilter(expr), source: expr, usesPath: expr.includes("@path") };
|
|
172
|
+
}
|
|
173
|
+
if (trimmed.startsWith("(") && trimmed.endsWith(")")) {
|
|
174
|
+
const inner = trimmed.slice(1, -1).trim();
|
|
175
|
+
const lengthOnly = /^@\.length$/.test(inner);
|
|
176
|
+
const withOffset = /^@\.length\s*-\s*(\d+)$/.exec(inner);
|
|
177
|
+
if (lengthOnly)
|
|
178
|
+
return { kind: "scriptIndex", offset: 0 };
|
|
179
|
+
if (withOffset)
|
|
180
|
+
return { kind: "scriptIndex", offset: -Number(withOffset[1]) };
|
|
181
|
+
onError(`Unsupported script subscript "[${content}]"`);
|
|
182
|
+
return { kind: "none" };
|
|
183
|
+
}
|
|
184
|
+
if (!content.includes(",") && SLICE_RE.test(trimmed))
|
|
185
|
+
return buildSlice(trimmed);
|
|
186
|
+
const parts = splitUnion(content);
|
|
187
|
+
const names = [];
|
|
188
|
+
for (const part of parts) {
|
|
189
|
+
const token = part.trim();
|
|
190
|
+
const literal = unquote(token);
|
|
191
|
+
if (literal !== null) {
|
|
192
|
+
names.push(literal);
|
|
193
|
+
} else if (/^-?\d+$/.test(token)) {
|
|
194
|
+
names.push(Number(token));
|
|
195
|
+
} else {
|
|
196
|
+
names.push(token);
|
|
241
197
|
}
|
|
242
|
-
|
|
198
|
+
}
|
|
199
|
+
if (names.length === 1) {
|
|
200
|
+
const only = names[0];
|
|
201
|
+
return typeof only === "number" ? { kind: "index", index: only } : { kind: "child", name: only };
|
|
202
|
+
}
|
|
203
|
+
return { kind: "union", names };
|
|
243
204
|
};
|
|
244
|
-
/** Finds the index of the `]` that closes the `[` at `start`, respecting quotes (and escapes) and nesting. */
|
|
245
205
|
const findBracketEnd = (expression, start) => {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
return i;
|
|
270
|
-
}
|
|
206
|
+
let depth = 0;
|
|
207
|
+
let quote = "";
|
|
208
|
+
for (let i = start; i < expression.length; i++) {
|
|
209
|
+
const ch = expression[i];
|
|
210
|
+
if (quote) {
|
|
211
|
+
if (ch === "\\") {
|
|
212
|
+
i++;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (ch === quote)
|
|
216
|
+
quote = "";
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (ch === '"' || ch === "'")
|
|
220
|
+
quote = ch;
|
|
221
|
+
else if (ch === "[" || ch === "(")
|
|
222
|
+
depth++;
|
|
223
|
+
else if (ch === ")")
|
|
224
|
+
depth--;
|
|
225
|
+
else if (ch === "]") {
|
|
226
|
+
depth--;
|
|
227
|
+
if (depth === 0)
|
|
228
|
+
return i;
|
|
271
229
|
}
|
|
272
|
-
|
|
230
|
+
}
|
|
231
|
+
return -1;
|
|
273
232
|
};
|
|
274
233
|
const readName = (expression, start) => {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
234
|
+
let i = start;
|
|
235
|
+
while (i < expression.length && !".[]^~".includes(expression[i]))
|
|
236
|
+
i++;
|
|
237
|
+
return { name: expression.slice(start, i), end: i };
|
|
279
238
|
};
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
i++;
|
|
310
|
-
if (expression[i] === '*') {
|
|
311
|
-
steps.push({ recursive, selector: { kind: 'wildcard' } });
|
|
312
|
-
recursive = false;
|
|
313
|
-
i++;
|
|
314
|
-
continue;
|
|
315
|
-
}
|
|
316
|
-
const { name, end } = readName(expression, i);
|
|
317
|
-
steps.push({ recursive, selector: { kind: 'child', name } });
|
|
318
|
-
recursive = false;
|
|
319
|
-
i = end;
|
|
320
|
-
continue;
|
|
321
|
-
}
|
|
322
|
-
if (ch === '[') {
|
|
323
|
-
const end = findBracketEnd(expression, i);
|
|
324
|
-
if (end === -1) {
|
|
325
|
-
onError(`Unterminated "[" in "${expression}"`);
|
|
326
|
-
break;
|
|
327
|
-
}
|
|
328
|
-
const content = expression.slice(i + 1, end);
|
|
329
|
-
steps.push({ recursive, selector: bracketSelector(content, onError) });
|
|
330
|
-
recursive = false;
|
|
331
|
-
i = end + 1;
|
|
332
|
-
continue;
|
|
333
|
-
}
|
|
334
|
-
if (ch === '^') {
|
|
335
|
-
// Honor a pending `..` so `$..^` selects every node's parent (fixing a
|
|
336
|
-
// flag leak where the descent was dropped and the selector matched nothing).
|
|
337
|
-
steps.push({ recursive, selector: { kind: 'parent' } });
|
|
338
|
-
recursive = false;
|
|
339
|
-
i++;
|
|
340
|
-
continue;
|
|
341
|
-
}
|
|
342
|
-
if (ch === '~') {
|
|
343
|
-
steps.push({ recursive, selector: { kind: 'keys' } });
|
|
344
|
-
recursive = false;
|
|
345
|
-
i++;
|
|
346
|
-
continue;
|
|
347
|
-
}
|
|
348
|
-
if (ch === '*') {
|
|
349
|
-
steps.push({ recursive, selector: { kind: 'wildcard' } });
|
|
350
|
-
recursive = false;
|
|
351
|
-
i++;
|
|
352
|
-
continue;
|
|
353
|
-
}
|
|
354
|
-
// Bare name following `..` (e.g. `$..foo`) or other unexpected token.
|
|
355
|
-
if (recursive) {
|
|
356
|
-
const { name, end } = readName(expression, i);
|
|
357
|
-
if (end > i) {
|
|
358
|
-
steps.push({ recursive: true, selector: { kind: 'child', name } });
|
|
359
|
-
recursive = false;
|
|
360
|
-
i = end;
|
|
361
|
-
continue;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
239
|
+
const compileQuery = (expression) => {
|
|
240
|
+
const cached = compileCache.get(expression);
|
|
241
|
+
if (cached)
|
|
242
|
+
return cached;
|
|
243
|
+
const steps = [];
|
|
244
|
+
const errors = [];
|
|
245
|
+
const onError = (message) => {
|
|
246
|
+
errors.push(message);
|
|
247
|
+
};
|
|
248
|
+
let hasDescent = false;
|
|
249
|
+
let i = 0;
|
|
250
|
+
if (expression[0] === "$")
|
|
251
|
+
i = 1;
|
|
252
|
+
else
|
|
253
|
+
onError('JSONPath must start with "$"');
|
|
254
|
+
let recursive = false;
|
|
255
|
+
while (i < expression.length) {
|
|
256
|
+
const ch = expression[i];
|
|
257
|
+
if (ch === ".") {
|
|
258
|
+
if (expression[i + 1] === ".") {
|
|
259
|
+
recursive = true;
|
|
260
|
+
hasDescent = true;
|
|
261
|
+
i += 2;
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
i++;
|
|
265
|
+
if (expression[i] === "*") {
|
|
266
|
+
steps.push({ recursive, selector: { kind: "wildcard" } });
|
|
267
|
+
recursive = false;
|
|
364
268
|
i++;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
const { name, end } = readName(expression, i);
|
|
272
|
+
steps.push({ recursive, selector: { kind: "child", name } });
|
|
273
|
+
recursive = false;
|
|
274
|
+
i = end;
|
|
275
|
+
continue;
|
|
365
276
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
277
|
+
if (ch === "[") {
|
|
278
|
+
const end = findBracketEnd(expression, i);
|
|
279
|
+
if (end === -1) {
|
|
280
|
+
onError(`Unterminated "[" in "${expression}"`);
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
const content = expression.slice(i + 1, end);
|
|
284
|
+
steps.push({ recursive, selector: bracketSelector(content, onError) });
|
|
285
|
+
recursive = false;
|
|
286
|
+
i = end + 1;
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (ch === "^") {
|
|
290
|
+
steps.push({ recursive, selector: { kind: "parent" } });
|
|
291
|
+
recursive = false;
|
|
292
|
+
i++;
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (ch === "~") {
|
|
296
|
+
steps.push({ recursive, selector: { kind: "keys" } });
|
|
297
|
+
recursive = false;
|
|
298
|
+
i++;
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (ch === "*") {
|
|
302
|
+
steps.push({ recursive, selector: { kind: "wildcard" } });
|
|
303
|
+
recursive = false;
|
|
304
|
+
i++;
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (recursive) {
|
|
308
|
+
const { name, end } = readName(expression, i);
|
|
309
|
+
if (end > i) {
|
|
310
|
+
steps.push({ recursive: true, selector: { kind: "child", name } });
|
|
311
|
+
recursive = false;
|
|
312
|
+
i = end;
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
i++;
|
|
317
|
+
}
|
|
318
|
+
const compiled = {
|
|
319
|
+
expression,
|
|
320
|
+
steps,
|
|
321
|
+
hasDescent,
|
|
322
|
+
...errors.length > 0 ? { error: errors.join("; ") } : {}
|
|
323
|
+
};
|
|
324
|
+
compileCache.set(expression, compiled);
|
|
325
|
+
return compiled;
|
|
374
326
|
};
|
|
375
|
-
/** Materializes the concrete (un-normalized) path from root to `node`. */
|
|
376
327
|
const pathOf = (node) => {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
328
|
+
let depth = 0;
|
|
329
|
+
for (let n = node; n !== void 0 && n.parent !== void 0; n = n.parent)
|
|
330
|
+
depth++;
|
|
331
|
+
const out = new Array(depth);
|
|
332
|
+
let i = depth - 1;
|
|
333
|
+
for (let n = node; n !== void 0 && n.parent !== void 0; n = n.parent) {
|
|
334
|
+
out[i--] = n.key;
|
|
335
|
+
}
|
|
336
|
+
return out;
|
|
386
337
|
};
|
|
387
338
|
const applySelector = (node, selector, root, out) => {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
if (Array.isArray(value)) {
|
|
427
|
-
if (typeof name === 'number') {
|
|
428
|
-
const idx = name < 0 ? value.length + name : name;
|
|
429
|
-
if (idx >= 0 && idx < value.length)
|
|
430
|
-
out.push({ value: value[idx], parent: node, key: idx });
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
else if (isObject(value) && Object.hasOwn(value, name)) {
|
|
434
|
-
out.push({ value: value[name], parent: node, key: name });
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
case 'slice': {
|
|
440
|
-
if (!Array.isArray(value))
|
|
441
|
-
return;
|
|
442
|
-
const len = value.length;
|
|
443
|
-
const step = selector.step ?? 1;
|
|
444
|
-
if (step === 0)
|
|
445
|
-
return;
|
|
446
|
-
const clamp = (raw, fallback) => {
|
|
447
|
-
if (raw === undefined)
|
|
448
|
-
return fallback;
|
|
449
|
-
return raw < 0 ? raw + len : raw;
|
|
450
|
-
};
|
|
451
|
-
if (step > 0) {
|
|
452
|
-
const start = Math.max(0, clamp(selector.start, 0));
|
|
453
|
-
const end = Math.min(len, clamp(selector.end, len));
|
|
454
|
-
for (let idx = start; idx < end; idx += step)
|
|
455
|
-
out.push({ value: value[idx], parent: node, key: idx });
|
|
456
|
-
}
|
|
457
|
-
else {
|
|
458
|
-
const start = Math.min(len - 1, clamp(selector.start, len - 1));
|
|
459
|
-
const end = Math.max(-1, clamp(selector.end, -1));
|
|
460
|
-
for (let idx = start; idx > end; idx += step)
|
|
461
|
-
out.push({ value: value[idx], parent: node, key: idx });
|
|
462
|
-
}
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
case 'scriptIndex': {
|
|
466
|
-
// `[(@.length - N)]` indexes from the end; N === 0 (`@.length`) is out of range.
|
|
467
|
-
if (!Array.isArray(value))
|
|
468
|
-
return;
|
|
469
|
-
const idx = value.length + selector.offset;
|
|
339
|
+
const value = node.value;
|
|
340
|
+
switch (selector.kind) {
|
|
341
|
+
case "child": {
|
|
342
|
+
if (isObject(value)) {
|
|
343
|
+
if (Object.hasOwn(value, selector.name))
|
|
344
|
+
out.push({ value: value[selector.name], parent: node, key: selector.name });
|
|
345
|
+
} else if (Array.isArray(value) && /^\d+$/.test(selector.name)) {
|
|
346
|
+
const idx = Number(selector.name);
|
|
347
|
+
if (idx < value.length)
|
|
348
|
+
out.push({ value: value[idx], parent: node, key: idx });
|
|
349
|
+
}
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
case "index": {
|
|
353
|
+
if (Array.isArray(value)) {
|
|
354
|
+
const idx = selector.index < 0 ? value.length + selector.index : selector.index;
|
|
355
|
+
if (idx >= 0 && idx < value.length)
|
|
356
|
+
out.push({ value: value[idx], parent: node, key: idx });
|
|
357
|
+
} else if (isObject(value) && Object.hasOwn(value, selector.index)) {
|
|
358
|
+
out.push({ value: value[selector.index], parent: node, key: selector.index });
|
|
359
|
+
}
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
case "wildcard": {
|
|
363
|
+
if (Array.isArray(value)) {
|
|
364
|
+
for (let idx = 0; idx < value.length; idx++)
|
|
365
|
+
out.push({ value: value[idx], parent: node, key: idx });
|
|
366
|
+
} else if (isObject(value)) {
|
|
367
|
+
for (const key of Object.keys(value))
|
|
368
|
+
out.push({ value: value[key], parent: node, key });
|
|
369
|
+
}
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
case "union": {
|
|
373
|
+
for (const name of selector.names) {
|
|
374
|
+
if (Array.isArray(value)) {
|
|
375
|
+
if (typeof name === "number") {
|
|
376
|
+
const idx = name < 0 ? value.length + name : name;
|
|
470
377
|
if (idx >= 0 && idx < value.length)
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// `@parentProperty` is `node.key`; `@path` is materialized only when used.
|
|
476
|
-
const pp = node.key;
|
|
477
|
-
if (Array.isArray(value)) {
|
|
478
|
-
for (let idx = 0; idx < value.length; idx++) {
|
|
479
|
-
const child = { value: value[idx], parent: node, key: idx };
|
|
480
|
-
const path = selector.usesPath ? pathToJsonPathString(pathOf(child)) : '';
|
|
481
|
-
if (selector.test(value[idx], idx, value, root, path, pp))
|
|
482
|
-
out.push(child);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
else if (isObject(value)) {
|
|
486
|
-
for (const key of Object.keys(value)) {
|
|
487
|
-
const child = { value: value[key], parent: node, key };
|
|
488
|
-
const path = selector.usesPath ? pathToJsonPathString(pathOf(child)) : '';
|
|
489
|
-
if (selector.test(value[key], key, value, root, path, pp))
|
|
490
|
-
out.push(child);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
case 'parent': {
|
|
496
|
-
if (node.parent !== undefined)
|
|
497
|
-
out.push(node.parent);
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
case 'keys': {
|
|
501
|
-
if (node.parent === undefined)
|
|
502
|
-
return;
|
|
503
|
-
// The selected value is the node's own key, but it occupies the same path.
|
|
504
|
-
out.push({ value: node.key, parent: node.parent, key: node.key });
|
|
505
|
-
return;
|
|
378
|
+
out.push({ value: value[idx], parent: node, key: idx });
|
|
379
|
+
}
|
|
380
|
+
} else if (isObject(value) && Object.hasOwn(value, name)) {
|
|
381
|
+
out.push({ value: value[name], parent: node, key: name });
|
|
506
382
|
}
|
|
507
|
-
|
|
508
|
-
|
|
383
|
+
}
|
|
384
|
+
return;
|
|
509
385
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
386
|
+
case "slice": {
|
|
387
|
+
if (!Array.isArray(value))
|
|
388
|
+
return;
|
|
389
|
+
const len = value.length;
|
|
390
|
+
const step = selector.step ?? 1;
|
|
391
|
+
if (step === 0)
|
|
392
|
+
return;
|
|
393
|
+
const clamp = (raw, fallback) => {
|
|
394
|
+
if (raw === void 0)
|
|
395
|
+
return fallback;
|
|
396
|
+
return raw < 0 ? raw + len : raw;
|
|
397
|
+
};
|
|
398
|
+
if (step > 0) {
|
|
399
|
+
const start = Math.max(0, clamp(selector.start, 0));
|
|
400
|
+
const end = Math.min(len, clamp(selector.end, len));
|
|
401
|
+
for (let idx = start; idx < end; idx += step)
|
|
402
|
+
out.push({ value: value[idx], parent: node, key: idx });
|
|
403
|
+
} else {
|
|
404
|
+
const start = Math.min(len - 1, clamp(selector.start, len - 1));
|
|
405
|
+
const end = Math.max(-1, clamp(selector.end, -1));
|
|
406
|
+
for (let idx = start; idx > end; idx += step)
|
|
407
|
+
out.push({ value: value[idx], parent: node, key: idx });
|
|
408
|
+
}
|
|
409
|
+
return;
|
|
518
410
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
411
|
+
case "scriptIndex": {
|
|
412
|
+
if (!Array.isArray(value))
|
|
413
|
+
return;
|
|
414
|
+
const idx = value.length + selector.offset;
|
|
415
|
+
if (idx >= 0 && idx < value.length)
|
|
416
|
+
out.push({ value: value[idx], parent: node, key: idx });
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
case "filter": {
|
|
420
|
+
const pp = node.key;
|
|
421
|
+
if (Array.isArray(value)) {
|
|
422
|
+
for (let idx = 0; idx < value.length; idx++) {
|
|
423
|
+
const child = { value: value[idx], parent: node, key: idx };
|
|
424
|
+
const path = selector.usesPath ? pathToJsonPathString(pathOf(child)) : "";
|
|
425
|
+
if (selector.test(value[idx], idx, value, root, path, pp))
|
|
426
|
+
out.push(child);
|
|
427
|
+
}
|
|
428
|
+
} else if (isObject(value)) {
|
|
429
|
+
for (const key of Object.keys(value)) {
|
|
430
|
+
const child = { value: value[key], parent: node, key };
|
|
431
|
+
const path = selector.usesPath ? pathToJsonPathString(pathOf(child)) : "";
|
|
432
|
+
if (selector.test(value[key], key, value, root, path, pp))
|
|
433
|
+
out.push(child);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return;
|
|
522
437
|
}
|
|
438
|
+
case "parent": {
|
|
439
|
+
if (node.parent !== void 0)
|
|
440
|
+
out.push(node.parent);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
case "keys": {
|
|
444
|
+
if (node.parent === void 0)
|
|
445
|
+
return;
|
|
446
|
+
out.push({ value: node.key, parent: node.parent, key: node.key });
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
case "none":
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
const walkDescendants = (node, visit) => {
|
|
454
|
+
visit(node);
|
|
455
|
+
const value = node.value;
|
|
456
|
+
if (Array.isArray(value)) {
|
|
457
|
+
for (let idx = 0; idx < value.length; idx++)
|
|
458
|
+
walkDescendants({ value: value[idx], parent: node, key: idx }, visit);
|
|
459
|
+
} else if (isObject(value)) {
|
|
460
|
+
for (const key of Object.keys(value))
|
|
461
|
+
walkDescendants({ value: value[key], parent: node, key }, visit);
|
|
462
|
+
}
|
|
523
463
|
};
|
|
524
|
-
/** Applies a list of steps to an existing set of nodes. */
|
|
525
464
|
const applySteps = (root, initial, steps) => {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
applySelector(node, step.selector, root, next);
|
|
536
|
-
}
|
|
537
|
-
current = next;
|
|
465
|
+
let current = initial;
|
|
466
|
+
for (const step of steps) {
|
|
467
|
+
const next = [];
|
|
468
|
+
if (step.recursive) {
|
|
469
|
+
for (const node of current)
|
|
470
|
+
walkDescendants(node, (n) => applySelector(n, step.selector, root, next));
|
|
471
|
+
} else {
|
|
472
|
+
for (const node of current)
|
|
473
|
+
applySelector(node, step.selector, root, next);
|
|
538
474
|
}
|
|
539
|
-
|
|
475
|
+
current = next;
|
|
476
|
+
}
|
|
477
|
+
return current;
|
|
540
478
|
};
|
|
541
|
-
const runSteps = (root, steps) => applySteps(root, [{ value: root, parent:
|
|
479
|
+
const runSteps = (root, steps) => applySteps(root, [{ value: root, parent: void 0, key: void 0 }], steps);
|
|
542
480
|
const toMatches = (nodes) => {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
481
|
+
const out = new Array(nodes.length);
|
|
482
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
483
|
+
const node = nodes[i];
|
|
484
|
+
out[i] = { value: node.value, path: normalizePath(pathOf(node)) };
|
|
485
|
+
}
|
|
486
|
+
return out;
|
|
549
487
|
};
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
return [];
|
|
557
|
-
return toMatches(runSteps(data, compiled.steps));
|
|
488
|
+
const queryCompiled = (data, compiled) => {
|
|
489
|
+
if (compiled.error !== void 0)
|
|
490
|
+
return [];
|
|
491
|
+
if (data === null || data === void 0)
|
|
492
|
+
return [];
|
|
493
|
+
return toMatches(runSteps(data, compiled.steps));
|
|
558
494
|
};
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
for (let i = 0; i < compiled.length; i++)
|
|
573
|
-
out[i] = [];
|
|
574
|
-
return out;
|
|
575
|
-
}
|
|
576
|
-
const recursive = [];
|
|
577
|
-
for (let i = 0; i < compiled.length; i++) {
|
|
578
|
-
const c = compiled[i];
|
|
579
|
-
// Skip malformed paths entirely; they contribute no matches.
|
|
580
|
-
if (c.error !== undefined) {
|
|
581
|
-
out[i] = [];
|
|
582
|
-
continue;
|
|
583
|
-
}
|
|
584
|
-
const first = c.steps[0];
|
|
585
|
-
if (first?.recursive)
|
|
586
|
-
recursive.push(i);
|
|
587
|
-
else
|
|
588
|
-
out[i] = queryCompiled(data, c);
|
|
495
|
+
const queryMany = (data, compiled) => {
|
|
496
|
+
const out = new Array(compiled.length);
|
|
497
|
+
if (data === null || data === void 0) {
|
|
498
|
+
for (let i = 0; i < compiled.length; i++)
|
|
499
|
+
out[i] = [];
|
|
500
|
+
return out;
|
|
501
|
+
}
|
|
502
|
+
const recursive = [];
|
|
503
|
+
for (let i = 0; i < compiled.length; i++) {
|
|
504
|
+
const c = compiled[i];
|
|
505
|
+
if (c.error !== void 0) {
|
|
506
|
+
out[i] = [];
|
|
507
|
+
continue;
|
|
589
508
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
509
|
+
const first = c.steps[0];
|
|
510
|
+
if (first?.recursive)
|
|
511
|
+
recursive.push(i);
|
|
512
|
+
else
|
|
513
|
+
out[i] = queryCompiled(data, c);
|
|
514
|
+
}
|
|
515
|
+
if (recursive.length > 0) {
|
|
516
|
+
const firsts = recursive.map((i) => compiled[i].steps[0]);
|
|
517
|
+
const seeds = recursive.map(() => []);
|
|
518
|
+
const root = { value: data, parent: void 0, key: void 0 };
|
|
519
|
+
walkDescendants(root, (node) => {
|
|
520
|
+
for (let r = 0; r < recursive.length; r++)
|
|
521
|
+
applySelector(node, firsts[r].selector, data, seeds[r]);
|
|
522
|
+
});
|
|
523
|
+
for (let r = 0; r < recursive.length; r++) {
|
|
524
|
+
const c = compiled[recursive[r]];
|
|
525
|
+
out[recursive[r]] = toMatches(applySteps(data, seeds[r], c.steps.slice(1)));
|
|
606
526
|
}
|
|
607
|
-
|
|
527
|
+
}
|
|
528
|
+
return out;
|
|
529
|
+
};
|
|
530
|
+
const query = (data, expression) => queryCompiled(data, compileQuery(expression));
|
|
531
|
+
export {
|
|
532
|
+
compileQuery,
|
|
533
|
+
query,
|
|
534
|
+
queryCompiled,
|
|
535
|
+
queryMany
|
|
608
536
|
};
|
|
609
|
-
/** Runs a JSONPath expression and returns each match with its concrete path. */
|
|
610
|
-
export const query = (data, expression) => queryCompiled(data, compileQuery(expression));
|