@yesiree/jsonq 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +891 -0
- package/dist/cli.mjs +420 -0
- package/dist/index.cjs +394 -0
- package/dist/index.mjs +390 -0
- package/dist/index.umd.js +1 -0
- package/package.json +48 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
const METHODS = ['map', 'filter', 'join', 'sort', 'unique', 'first', 'last', 'count', 'sum', 'avg', 'min', 'max'];
|
|
2
|
+
|
|
3
|
+
function query(expression, data) {
|
|
4
|
+
const tokens = tokenizePipeline(expression);
|
|
5
|
+
return evaluatePipeline(tokens, data);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function tokenizePipeline(expr) {
|
|
9
|
+
const tokens = [];
|
|
10
|
+
let i = 0;
|
|
11
|
+
|
|
12
|
+
while (i < expr.length) {
|
|
13
|
+
while (i < expr.length && /\s/.test(expr[i])) i++;
|
|
14
|
+
if (i >= expr.length) break;
|
|
15
|
+
|
|
16
|
+
if (expr[i] === '$') {
|
|
17
|
+
tokens.push({ type: 'root' });
|
|
18
|
+
i++;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (expr[i] === '.') {
|
|
23
|
+
i++;
|
|
24
|
+
let name = '';
|
|
25
|
+
while (i < expr.length && /[a-zA-Z0-9_]/.test(expr[i])) {
|
|
26
|
+
name += expr[i++];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
while (i < expr.length && /\s/.test(expr[i])) i++;
|
|
30
|
+
if (i < expr.length && expr[i] === '(' && METHODS.includes(name)) {
|
|
31
|
+
i++;
|
|
32
|
+
let depth = 1;
|
|
33
|
+
let params = '';
|
|
34
|
+
while (i < expr.length && depth > 0) {
|
|
35
|
+
if (expr[i] === '(') depth++;
|
|
36
|
+
else if (expr[i] === ')') depth--;
|
|
37
|
+
if (depth > 0) params += expr[i];
|
|
38
|
+
i++;
|
|
39
|
+
}
|
|
40
|
+
tokens.push({ type: 'method', method: name, params });
|
|
41
|
+
} else {
|
|
42
|
+
tokens.push({ type: 'prop', name });
|
|
43
|
+
}
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (expr[i] === '[') {
|
|
48
|
+
i++;
|
|
49
|
+
let num = '';
|
|
50
|
+
while (i < expr.length && /\d/.test(expr[i])) {
|
|
51
|
+
num += expr[i++];
|
|
52
|
+
}
|
|
53
|
+
i++;
|
|
54
|
+
tokens.push({ type: 'index', value: parseInt(num) });
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let methodMatch = false;
|
|
59
|
+
for (const method of METHODS) {
|
|
60
|
+
if (expr.substr(i, method.length) === method) {
|
|
61
|
+
const afterMethod = i + method.length;
|
|
62
|
+
let j = afterMethod;
|
|
63
|
+
while (j < expr.length && /\s/.test(expr[j])) j++;
|
|
64
|
+
if (j < expr.length && expr[j] === '(') {
|
|
65
|
+
i = j + 1;
|
|
66
|
+
let depth = 1;
|
|
67
|
+
let params = '';
|
|
68
|
+
while (i < expr.length && depth > 0) {
|
|
69
|
+
if (expr[i] === '(') depth++;
|
|
70
|
+
else if (expr[i] === ')') depth--;
|
|
71
|
+
if (depth > 0) params += expr[i];
|
|
72
|
+
i++;
|
|
73
|
+
}
|
|
74
|
+
tokens.push({ type: 'method', method, params });
|
|
75
|
+
methodMatch = true;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (methodMatch) continue;
|
|
81
|
+
|
|
82
|
+
i++;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return tokens;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function evaluatePipeline(tokens, data) {
|
|
89
|
+
let result = data;
|
|
90
|
+
|
|
91
|
+
for (const token of tokens) {
|
|
92
|
+
switch (token.type) {
|
|
93
|
+
case 'root':
|
|
94
|
+
result = data;
|
|
95
|
+
break;
|
|
96
|
+
case 'prop':
|
|
97
|
+
result = result?.[token.name];
|
|
98
|
+
break;
|
|
99
|
+
case 'index':
|
|
100
|
+
result = result?.[token.value];
|
|
101
|
+
break;
|
|
102
|
+
case 'method':
|
|
103
|
+
result = applyMethod(token.method, token.params, result, data);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function applyMethod(method, params, acc, data, bindings = {}) {
|
|
112
|
+
switch (method) {
|
|
113
|
+
case 'map':
|
|
114
|
+
return acc.map((item, index) => evalExpr(params, item, index, data, { ...bindings, $array: acc }));
|
|
115
|
+
case 'filter':
|
|
116
|
+
return acc.filter((item, index) => evalExpr(params, item, index, data, { ...bindings, $array: acc }));
|
|
117
|
+
case 'join':
|
|
118
|
+
return acc.join(evalLiteral(params));
|
|
119
|
+
case 'sort': {
|
|
120
|
+
const descending = params.trim().startsWith('-');
|
|
121
|
+
const sortExpr = descending ? params.trim().slice(1) : params;
|
|
122
|
+
return [...acc].sort((a, b) => {
|
|
123
|
+
const av = evalExpr(sortExpr, a, 0, data, { ...bindings, $array: acc });
|
|
124
|
+
const bv = evalExpr(sortExpr, b, 0, data, { ...bindings, $array: acc });
|
|
125
|
+
const comparison = av < bv ? -1 : av > bv ? 1 : 0;
|
|
126
|
+
return descending ? -comparison : comparison;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
case 'unique':
|
|
130
|
+
return [...new Set(acc.map((item, index) => evalExpr(params || '$item', item, index, data, { ...bindings, $array: acc })))];
|
|
131
|
+
case 'first':
|
|
132
|
+
return acc[0];
|
|
133
|
+
case 'last':
|
|
134
|
+
return acc[acc.length - 1];
|
|
135
|
+
case 'count':
|
|
136
|
+
return acc.length;
|
|
137
|
+
case 'sum':
|
|
138
|
+
return acc.reduce((a, item, index) => a + evalExpr(params || '$item', item, index, data, { ...bindings, $array: acc }), 0);
|
|
139
|
+
case 'avg':
|
|
140
|
+
return acc.reduce((a, item, index) => a + evalExpr(params || '$item', item, index, data, { ...bindings, $array: acc }), 0) / acc.length;
|
|
141
|
+
case 'min':
|
|
142
|
+
return Math.min(...acc.map((item, index) => evalExpr(params || '$item', item, index, data, { ...bindings, $array: acc })));
|
|
143
|
+
case 'max':
|
|
144
|
+
return Math.max(...acc.map((item, index) => evalExpr(params || '$item', item, index, data, { ...bindings, $array: acc })));
|
|
145
|
+
default:
|
|
146
|
+
throw new Error(`Unknown method: ${method}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function evalLiteral(expr) {
|
|
151
|
+
const str = expr.match(/^["'](.*)["']$/);
|
|
152
|
+
if (str) return str[1];
|
|
153
|
+
if (!isNaN(expr)) return Number(expr);
|
|
154
|
+
if (expr === 'true') return true;
|
|
155
|
+
if (expr === 'false') return false;
|
|
156
|
+
if (expr === 'null') return null;
|
|
157
|
+
if (expr === 'undefined') return undefined;
|
|
158
|
+
throw new Error(`Invalid literal: ${expr}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function evalExpr(expr, $item, $index, $data, parentBindings = {}) {
|
|
162
|
+
expr = expr.trim();
|
|
163
|
+
|
|
164
|
+
const arrowMatch = expr.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*=>\s*(.+)$/);
|
|
165
|
+
if (arrowMatch) {
|
|
166
|
+
const [, paramName, body] = arrowMatch;
|
|
167
|
+
const tokens = tokenizeExpr(body);
|
|
168
|
+
const parsed = parseExpr(tokens);
|
|
169
|
+
const bindings = { ...parentBindings, [paramName]: $item };
|
|
170
|
+
return evalNode(parsed, $item, $index, $data, bindings);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const tokens = tokenizeExpr(expr);
|
|
174
|
+
const parsed = parseExpr(tokens);
|
|
175
|
+
return evalNode(parsed, $item, $index, $data, parentBindings);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function tokenizeExpr(expr) {
|
|
179
|
+
const tokens = [];
|
|
180
|
+
const regex = /(\$array|\$item|\$index|\$(?![a-zA-Z_])|\$[a-zA-Z_][a-zA-Z0-9_]*)|(_)|(@\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d{3})?(?:Z|[+-]\d{2}:?\d{2})?)?)|(-?\d+\.?\d*(?:e[+-]?\d+)?)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(&&|\|\||[!=<>]=?|=>|[+\-*/%!])|(\.)|(\[)|(\])|(\()|(\))|(\btrue\b|\bfalse\b|\bnull\b|\bundefined\b)|([a-zA-Z_][a-zA-Z0-9_]*)/g;
|
|
181
|
+
let match;
|
|
182
|
+
while ((match = regex.exec(expr)) !== null) {
|
|
183
|
+
const [full, variable, underscore, date, num, str, op, dot, lbracket, rbracket, lparen, rparen, keyword, ident] = match;
|
|
184
|
+
if (variable) tokens.push({ type: 'var', value: variable, pos: match.index });
|
|
185
|
+
else if (underscore) tokens.push({ type: 'var', value: '_', pos: match.index });
|
|
186
|
+
else if (date) tokens.push({ type: 'date', value: date.slice(1), pos: match.index });
|
|
187
|
+
else if (num) tokens.push({ type: 'num', value: Number(num), pos: match.index });
|
|
188
|
+
else if (str) tokens.push({ type: 'str', value: str.slice(1, -1), pos: match.index });
|
|
189
|
+
else if (op) tokens.push({ type: 'op', value: op, pos: match.index });
|
|
190
|
+
else if (dot) tokens.push({ type: 'dot', pos: match.index });
|
|
191
|
+
else if (lbracket) tokens.push({ type: 'lbracket', pos: match.index });
|
|
192
|
+
else if (rbracket) tokens.push({ type: 'rbracket', pos: match.index });
|
|
193
|
+
else if (lparen) tokens.push({ type: 'lparen', pos: match.index });
|
|
194
|
+
else if (rparen) tokens.push({ type: 'rparen', pos: match.index });
|
|
195
|
+
else if (keyword) tokens.push({ type: 'keyword', value: keyword, pos: match.index });
|
|
196
|
+
else if (ident) tokens.push({ type: 'ident', value: ident, pos: match.index });
|
|
197
|
+
}
|
|
198
|
+
tokens.expr = expr;
|
|
199
|
+
return tokens;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function parseExpr(tokens) {
|
|
203
|
+
let pos = 0;
|
|
204
|
+
|
|
205
|
+
function parseOr() {
|
|
206
|
+
let left = parseAnd();
|
|
207
|
+
while (tokens[pos]?.type === 'op' && tokens[pos].value === '||') {
|
|
208
|
+
pos++;
|
|
209
|
+
left = { type: 'binary', op: '||', left, right: parseAnd() };
|
|
210
|
+
}
|
|
211
|
+
return left;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function parseAnd() {
|
|
215
|
+
let left = parseComparison();
|
|
216
|
+
while (tokens[pos]?.type === 'op' && tokens[pos].value === '&&') {
|
|
217
|
+
pos++;
|
|
218
|
+
left = { type: 'binary', op: '&&', left, right: parseComparison() };
|
|
219
|
+
}
|
|
220
|
+
return left;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function parseComparison() {
|
|
224
|
+
let left = parseAddSub();
|
|
225
|
+
while (tokens[pos]?.type === 'op' && /^([!=<>]=?|==)$/.test(tokens[pos].value) && tokens[pos].value !== '!') {
|
|
226
|
+
const op = tokens[pos++].value;
|
|
227
|
+
left = { type: 'binary', op, left, right: parseAddSub() };
|
|
228
|
+
}
|
|
229
|
+
return left;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function parseAddSub() {
|
|
233
|
+
let left = parseMulDiv();
|
|
234
|
+
while (tokens[pos]?.type === 'op' && /^[+\-]$/.test(tokens[pos].value)) {
|
|
235
|
+
const op = tokens[pos++].value;
|
|
236
|
+
left = { type: 'binary', op, left, right: parseMulDiv() };
|
|
237
|
+
}
|
|
238
|
+
return left;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function parseMulDiv() {
|
|
242
|
+
let left = parseUnary();
|
|
243
|
+
while (tokens[pos]?.type === 'op' && /^[*/%]$/.test(tokens[pos].value)) {
|
|
244
|
+
const op = tokens[pos++].value;
|
|
245
|
+
left = { type: 'binary', op, left, right: parseUnary() };
|
|
246
|
+
}
|
|
247
|
+
return left;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function parseUnary() {
|
|
251
|
+
if (tokens[pos]?.type === 'op' && (tokens[pos].value === '!' || tokens[pos].value === '-')) {
|
|
252
|
+
const op = tokens[pos++].value;
|
|
253
|
+
return { type: 'unary', op, arg: parseUnary() };
|
|
254
|
+
}
|
|
255
|
+
return parseAccess();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function parseAccess() {
|
|
259
|
+
let node = parsePrimary();
|
|
260
|
+
while (tokens[pos]?.type === 'dot' || tokens[pos]?.type === 'lbracket') {
|
|
261
|
+
if (tokens[pos].type === 'dot') {
|
|
262
|
+
pos++;
|
|
263
|
+
const prop = tokens[pos++];
|
|
264
|
+
if (tokens[pos]?.type === 'lparen' && METHODS.includes(prop.value)) {
|
|
265
|
+
const openParenPos = pos;
|
|
266
|
+
pos++;
|
|
267
|
+
let depth = 1;
|
|
268
|
+
let paramStart = pos;
|
|
269
|
+
while (depth > 0 && pos < tokens.length) {
|
|
270
|
+
if (tokens[pos].type === 'lparen') depth++;
|
|
271
|
+
else if (tokens[pos].type === 'rparen') depth--;
|
|
272
|
+
if (depth > 0) pos++;
|
|
273
|
+
}
|
|
274
|
+
const paramEnd = pos;
|
|
275
|
+
pos++;
|
|
276
|
+
|
|
277
|
+
const paramExpr = tokens.expr.slice(
|
|
278
|
+
tokens[paramStart]?.pos || tokens[openParenPos].pos + 1,
|
|
279
|
+
tokens[paramEnd]?.pos || tokens[paramEnd - 1]?.pos + 1
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
node = { type: 'methodCall', obj: node, method: prop.value, params: paramExpr };
|
|
283
|
+
} else {
|
|
284
|
+
node = { type: 'access', obj: node, prop: prop.value };
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
pos++;
|
|
288
|
+
const index = parseOr();
|
|
289
|
+
pos++;
|
|
290
|
+
node = { type: 'index', obj: node, index };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return node;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function parsePrimary() {
|
|
297
|
+
const t = tokens[pos++];
|
|
298
|
+
if (!t) throw new Error('Unexpected end of expression');
|
|
299
|
+
if (t.type === 'var') return { type: 'var', name: t.value };
|
|
300
|
+
if (t.type === 'num') return { type: 'literal', value: t.value };
|
|
301
|
+
if (t.type === 'str') return { type: 'literal', value: t.value };
|
|
302
|
+
if (t.type === 'date') return { type: 'literal', value: new Date(t.value) };
|
|
303
|
+
if (t.type === 'keyword') {
|
|
304
|
+
if (t.value === 'true') return { type: 'literal', value: true };
|
|
305
|
+
if (t.value === 'false') return { type: 'literal', value: false };
|
|
306
|
+
if (t.value === 'null') return { type: 'literal', value: null };
|
|
307
|
+
if (t.value === 'undefined') return { type: 'literal', value: undefined };
|
|
308
|
+
}
|
|
309
|
+
if (t.type === 'ident') return { type: 'ident', name: t.value };
|
|
310
|
+
if (t.type === 'lparen') {
|
|
311
|
+
const expr = parseOr();
|
|
312
|
+
pos++;
|
|
313
|
+
return expr;
|
|
314
|
+
}
|
|
315
|
+
throw new Error(`Unexpected token: ${JSON.stringify(t)}`);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return parseOr();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function evalNode(node, $item, $index, $data, bindings = {}) {
|
|
322
|
+
switch (node.type) {
|
|
323
|
+
case 'var':
|
|
324
|
+
if (node.name === '_') return $item;
|
|
325
|
+
if (node.name === '$item') return $item;
|
|
326
|
+
if (node.name === '$index') return $index;
|
|
327
|
+
if (node.name === '$') return $data;
|
|
328
|
+
if (node.name === '$array') return bindings.$array;
|
|
329
|
+
if (node.name in bindings) return bindings[node.name];
|
|
330
|
+
throw new Error(`Unknown variable: ${node.name}`);
|
|
331
|
+
case 'literal':
|
|
332
|
+
return node.value;
|
|
333
|
+
case 'ident':
|
|
334
|
+
if (node.name in bindings) return bindings[node.name];
|
|
335
|
+
if ($item && typeof $item === 'object' && node.name in $item) return $item[node.name];
|
|
336
|
+
throw new Error(`Unknown identifier: ${node.name}`);
|
|
337
|
+
case 'access': {
|
|
338
|
+
const obj = evalNode(node.obj, $item, $index, $data, bindings);
|
|
339
|
+
return obj?.[node.prop];
|
|
340
|
+
}
|
|
341
|
+
case 'index': {
|
|
342
|
+
const arr = evalNode(node.obj, $item, $index, $data, bindings);
|
|
343
|
+
const idx = evalNode(node.index, $item, $index, $data, bindings);
|
|
344
|
+
return arr?.[idx];
|
|
345
|
+
}
|
|
346
|
+
case 'methodCall': {
|
|
347
|
+
const obj = evalNode(node.obj, $item, $index, $data, bindings);
|
|
348
|
+
if (!obj) return undefined;
|
|
349
|
+
return applyMethod(node.method, node.params, obj, $data, bindings);
|
|
350
|
+
}
|
|
351
|
+
case 'unary': {
|
|
352
|
+
const arg = evalNode(node.arg, $item, $index, $data, bindings);
|
|
353
|
+
if (node.op === '!') return !arg;
|
|
354
|
+
if (node.op === '-') return -arg;
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
case 'binary': {
|
|
358
|
+
let left = evalNode(node.left, $item, $index, $data, bindings);
|
|
359
|
+
let right = evalNode(node.right, $item, $index, $data, bindings);
|
|
360
|
+
|
|
361
|
+
if (left instanceof Date || right instanceof Date) {
|
|
362
|
+
if (!(left instanceof Date)) left = new Date(left);
|
|
363
|
+
if (!(right instanceof Date)) right = new Date(right);
|
|
364
|
+
left = left.getTime();
|
|
365
|
+
right = right.getTime();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
switch (node.op) {
|
|
369
|
+
case '+': return left + right;
|
|
370
|
+
case '-': return left - right;
|
|
371
|
+
case '*': return left * right;
|
|
372
|
+
case '/': return left / right;
|
|
373
|
+
case '%': return left % right;
|
|
374
|
+
case '==': return left == right;
|
|
375
|
+
case '!=': return left != right;
|
|
376
|
+
case '<': return left < right;
|
|
377
|
+
case '>': return left > right;
|
|
378
|
+
case '<=': return left <= right;
|
|
379
|
+
case '>=': return left >= right;
|
|
380
|
+
case '&&': return left && right;
|
|
381
|
+
case '||': return left || right;
|
|
382
|
+
}
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
default:
|
|
386
|
+
throw new Error(`Unknown node type: ${node.type}`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export { evaluatePipeline as evaluate, query, tokenizePipeline as tokenize };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).JsonQueryPipe={})}(this,function(e){"use strict";const t=["map","filter","join","sort","unique","first","last","count","sum","avg","min","max"];function r(e){const r=[];let n=0;for(;n<e.length;){for(;n<e.length&&/\s/.test(e[n]);)n++;if(n>=e.length)break;if("$"===e[n]){r.push({type:"root"}),n++;continue}if("."===e[n]){n++;let a="";for(;n<e.length&&/[a-zA-Z0-9_]/.test(e[n]);)a+=e[n++];for(;n<e.length&&/\s/.test(e[n]);)n++;if(n<e.length&&"("===e[n]&&t.includes(a)){n++;let t=1,u="";for(;n<e.length&&t>0;)"("===e[n]?t++:")"===e[n]&&t--,t>0&&(u+=e[n]),n++;r.push({type:"method",method:a,params:u})}else r.push({type:"prop",name:a});continue}if("["===e[n]){n++;let t="";for(;n<e.length&&/\d/.test(e[n]);)t+=e[n++];n++,r.push({type:"index",value:parseInt(t)});continue}let a=!1;for(const u of t)if(e.substr(n,u.length)===u){let t=n+u.length;for(;t<e.length&&/\s/.test(e[t]);)t++;if(t<e.length&&"("===e[t]){n=t+1;let o=1,i="";for(;n<e.length&&o>0;)"("===e[n]?o++:")"===e[n]&&o--,o>0&&(i+=e[n]),n++;r.push({type:"method",method:u,params:i}),a=!0;break}}a||n++}return r}function n(e,t){let r=t;for(const n of e)switch(n.type){case"root":r=t;break;case"prop":r=r?.[n.name];break;case"index":r=r?.[n.value];break;case"method":r=a(n.method,n.params,r,t)}return r}function a(e,t,r,n,a={}){switch(e){case"map":return r.map((e,o)=>u(t,e,o,n,{...a,$array:r}));case"filter":return r.filter((e,o)=>u(t,e,o,n,{...a,$array:r}));case"join":return r.join(function(e){const t=e.match(/^["'](.*)["']$/);if(t)return t[1];if(!isNaN(e))return Number(e);if("true"===e)return!0;if("false"===e)return!1;if("null"===e)return null;if("undefined"===e)return;throw new Error(`Invalid literal: ${e}`)}(t));case"sort":{const e=t.trim().startsWith("-"),o=e?t.trim().slice(1):t;return[...r].sort((t,i)=>{const s=u(o,t,0,n,{...a,$array:r}),l=u(o,i,0,n,{...a,$array:r}),p=s<l?-1:s>l?1:0;return e?-p:p})}case"unique":return[...new Set(r.map((e,o)=>u(t||"$item",e,o,n,{...a,$array:r})))];case"first":return r[0];case"last":return r[r.length-1];case"count":return r.length;case"sum":return r.reduce((e,o,i)=>e+u(t||"$item",o,i,n,{...a,$array:r}),0);case"avg":return r.reduce((e,o,i)=>e+u(t||"$item",o,i,n,{...a,$array:r}),0)/r.length;case"min":return Math.min(...r.map((e,o)=>u(t||"$item",e,o,n,{...a,$array:r})));case"max":return Math.max(...r.map((e,o)=>u(t||"$item",e,o,n,{...a,$array:r})));default:throw new Error(`Unknown method: ${e}`)}}function u(e,t,r,n,a={}){const u=(e=e.trim()).match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*=>\s*(.+)$/);if(u){const[,e,l]=u;return s(i(o(l)),t,r,n,{...a,[e]:t})}return s(i(o(e)),t,r,n,a)}function o(e){const t=[],r=/(\$array|\$item|\$index|\$(?![a-zA-Z_])|\$[a-zA-Z_][a-zA-Z0-9_]*)|(_)|(@\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d{3})?(?:Z|[+-]\d{2}:?\d{2})?)?)|(-?\d+\.?\d*(?:e[+-]?\d+)?)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(&&|\|\||[!=<>]=?|=>|[+\-*/%!])|(\.)|(\[)|(\])|(\()|(\))|(\btrue\b|\bfalse\b|\bnull\b|\bundefined\b)|([a-zA-Z_][a-zA-Z0-9_]*)/g;let n;for(;null!==(n=r.exec(e));){const[e,r,a,u,o,i,s,l,p,f,c,y,d,h]=n;r?t.push({type:"var",value:r,pos:n.index}):a?t.push({type:"var",value:"_",pos:n.index}):u?t.push({type:"date",value:u.slice(1),pos:n.index}):o?t.push({type:"num",value:Number(o),pos:n.index}):i?t.push({type:"str",value:i.slice(1,-1),pos:n.index}):s?t.push({type:"op",value:s,pos:n.index}):l?t.push({type:"dot",pos:n.index}):p?t.push({type:"lbracket",pos:n.index}):f?t.push({type:"rbracket",pos:n.index}):c?t.push({type:"lparen",pos:n.index}):y?t.push({type:"rparen",pos:n.index}):d?t.push({type:"keyword",value:d,pos:n.index}):h&&t.push({type:"ident",value:h,pos:n.index})}return t.expr=e,t}function i(e){let r=0;function n(){let t=a();for(;"op"===e[r]?.type&&"||"===e[r].value;)r++,t={type:"binary",op:"||",left:t,right:a()};return t}function a(){let t=u();for(;"op"===e[r]?.type&&"&&"===e[r].value;)r++,t={type:"binary",op:"&&",left:t,right:u()};return t}function u(){let t=o();for(;"op"===e[r]?.type&&/^([!=<>]=?|==)$/.test(e[r].value)&&"!"!==e[r].value;){t={type:"binary",op:e[r++].value,left:t,right:o()}}return t}function o(){let t=i();for(;"op"===e[r]?.type&&/^[+\-]$/.test(e[r].value);){t={type:"binary",op:e[r++].value,left:t,right:i()}}return t}function i(){let t=s();for(;"op"===e[r]?.type&&/^[*/%]$/.test(e[r].value);){t={type:"binary",op:e[r++].value,left:t,right:s()}}return t}function s(){if("op"===e[r]?.type&&("!"===e[r].value||"-"===e[r].value)){return{type:"unary",op:e[r++].value,arg:s()}}return function(){let a=function(){const t=e[r++];if(!t)throw new Error("Unexpected end of expression");if("var"===t.type)return{type:"var",name:t.value};if("num"===t.type)return{type:"literal",value:t.value};if("str"===t.type)return{type:"literal",value:t.value};if("date"===t.type)return{type:"literal",value:new Date(t.value)};if("keyword"===t.type){if("true"===t.value)return{type:"literal",value:!0};if("false"===t.value)return{type:"literal",value:!1};if("null"===t.value)return{type:"literal",value:null};if("undefined"===t.value)return{type:"literal",value:void 0}}if("ident"===t.type)return{type:"ident",name:t.value};if("lparen"===t.type){const e=n();return r++,e}throw new Error(`Unexpected token: ${JSON.stringify(t)}`)}();for(;"dot"===e[r]?.type||"lbracket"===e[r]?.type;)if("dot"===e[r].type){r++;const n=e[r++];if("lparen"===e[r]?.type&&t.includes(n.value)){const t=r;r++;let u=1,o=r;for(;u>0&&r<e.length;)"lparen"===e[r].type?u++:"rparen"===e[r].type&&u--,u>0&&r++;const i=r;r++;const s=e.expr.slice(e[o]?.pos||e[t].pos+1,e[i]?.pos||e[i-1]?.pos+1);a={type:"methodCall",obj:a,method:n.value,params:s}}else a={type:"access",obj:a,prop:n.value}}else{r++;const e=n();r++,a={type:"index",obj:a,index:e}}return a}()}return n()}function s(e,t,r,n,u={}){switch(e.type){case"var":if("_"===e.name)return t;if("$item"===e.name)return t;if("$index"===e.name)return r;if("$"===e.name)return n;if("$array"===e.name)return u.$array;if(e.name in u)return u[e.name];throw new Error(`Unknown variable: ${e.name}`);case"literal":return e.value;case"ident":if(e.name in u)return u[e.name];if(t&&"object"==typeof t&&e.name in t)return t[e.name];throw new Error(`Unknown identifier: ${e.name}`);case"access":{const a=s(e.obj,t,r,n,u);return a?.[e.prop]}case"index":{const a=s(e.obj,t,r,n,u),o=s(e.index,t,r,n,u);return a?.[o]}case"methodCall":{const o=s(e.obj,t,r,n,u);if(!o)return;return a(e.method,e.params,o,n,u)}case"unary":{const a=s(e.arg,t,r,n,u);if("!"===e.op)return!a;if("-"===e.op)return-a;break}case"binary":{let a=s(e.left,t,r,n,u),o=s(e.right,t,r,n,u);switch((a instanceof Date||o instanceof Date)&&(a instanceof Date||(a=new Date(a)),o instanceof Date||(o=new Date(o)),a=a.getTime(),o=o.getTime()),e.op){case"+":return a+o;case"-":return a-o;case"*":return a*o;case"/":return a/o;case"%":return a%o;case"==":return a==o;case"!=":return a!=o;case"<":return a<o;case">":return a>o;case"<=":return a<=o;case">=":return a>=o;case"&&":return a&&o;case"||":return a||o}break}default:throw new Error(`Unknown node type: ${e.type}`)}}e.evaluate=n,e.query=function(e,t){return n(r(e),t)},e.tokenize=r});
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yesiree/jsonq",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight library for querying and transforming JSON data using a simple expression syntax.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.mjs",
|
|
8
|
+
"browser": "dist/index.umd.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.cjs",
|
|
14
|
+
"browser": "./dist/index.umd.js",
|
|
15
|
+
"types": "./dist/index.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"jsonq": "dist/cli.mjs"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
26
|
+
"build": "rollup -c",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
31
|
+
"jest": "^30.2.0",
|
|
32
|
+
"rollup": "^4.9.0"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"json",
|
|
36
|
+
"query",
|
|
37
|
+
"filter",
|
|
38
|
+
"map",
|
|
39
|
+
"pipe",
|
|
40
|
+
"expression",
|
|
41
|
+
"parser"
|
|
42
|
+
],
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/trevorhreed/jsonq.git"
|
|
47
|
+
}
|
|
48
|
+
}
|