@loancrate/json-selector 2.0.0 → 3.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.
Files changed (50) hide show
  1. package/README.md +50 -3
  2. package/dist/__generated__/parser.d.ts.map +1 -1
  3. package/dist/access.d.ts +6 -6
  4. package/dist/access.d.ts.map +1 -1
  5. package/dist/ast.d.ts +6 -3
  6. package/dist/ast.d.ts.map +1 -1
  7. package/dist/evaluate.d.ts +5 -5
  8. package/dist/evaluate.d.ts.map +1 -1
  9. package/dist/format.d.ts.map +1 -1
  10. package/dist/json-selector.esm.js +3510 -0
  11. package/dist/json-selector.esm.js.map +1 -0
  12. package/dist/json-selector.umd.js +3575 -0
  13. package/dist/json-selector.umd.js.map +1 -0
  14. package/dist/visitor.d.ts +2 -1
  15. package/dist/visitor.d.ts.map +1 -1
  16. package/package.json +32 -20
  17. package/src/__generated__/parser.js +2730 -0
  18. package/src/access.ts +534 -0
  19. package/src/ast.ts +114 -0
  20. package/src/evaluate.ts +269 -0
  21. package/src/format.ts +144 -0
  22. package/src/get.ts +9 -0
  23. package/src/grammar.pegjs +226 -0
  24. package/src/index.ts +7 -0
  25. package/src/parse.ts +7 -0
  26. package/src/set.ts +13 -0
  27. package/src/util.ts +70 -0
  28. package/src/visitor.ts +79 -0
  29. package/dist/__generated__/parser.js +0 -2855
  30. package/dist/__generated__/parser.js.map +0 -1
  31. package/dist/access.js +0 -444
  32. package/dist/access.js.map +0 -1
  33. package/dist/ast.js +0 -3
  34. package/dist/ast.js.map +0 -1
  35. package/dist/evaluate.js +0 -168
  36. package/dist/evaluate.js.map +0 -1
  37. package/dist/format.js +0 -119
  38. package/dist/format.js.map +0 -1
  39. package/dist/get.js +0 -9
  40. package/dist/get.js.map +0 -1
  41. package/dist/index.js +0 -26
  42. package/dist/index.js.map +0 -1
  43. package/dist/parse.js +0 -10
  44. package/dist/parse.js.map +0 -1
  45. package/dist/set.js +0 -12
  46. package/dist/set.js.map +0 -1
  47. package/dist/util.js +0 -70
  48. package/dist/util.js.map +0 -1
  49. package/dist/visitor.js +0 -39
  50. package/dist/visitor.js.map +0 -1
package/src/access.ts ADDED
@@ -0,0 +1,534 @@
1
+ import { JsonSelector } from "./ast";
2
+ import {
3
+ compare,
4
+ evaluateJsonSelector,
5
+ filter,
6
+ flatten,
7
+ normalizeSlice,
8
+ project,
9
+ slice,
10
+ } from "./evaluate";
11
+ import { formatJsonSelector } from "./format";
12
+ import {
13
+ asArray,
14
+ findId,
15
+ findIdIndex,
16
+ getField,
17
+ getIndex,
18
+ isArray,
19
+ isFalseOrEmpty,
20
+ isObject,
21
+ } from "./util";
22
+ import { visitJsonSelector } from "./visitor";
23
+
24
+ export interface UnboundAccessor {
25
+ readonly selector: JsonSelector;
26
+ isValidContext(context: unknown, rootContext?: unknown): boolean;
27
+ get(context: unknown, rootContext?: unknown): unknown;
28
+ set(value: unknown, context: unknown, rootContext?: unknown): void;
29
+ delete(context: unknown, rootContext?: unknown): void;
30
+ }
31
+
32
+ abstract class BaseAccessor implements UnboundAccessor {
33
+ constructor(readonly selector: JsonSelector) {}
34
+ abstract isValidContext(context: unknown, rootContext?: unknown): boolean;
35
+ abstract get(context: unknown, rootContext?: unknown): unknown;
36
+ abstract set(value: unknown, context: unknown, rootContext?: unknown): void;
37
+ abstract delete(context: unknown, rootContext?: unknown): void;
38
+ }
39
+
40
+ abstract class ReadOnlyAccessor extends BaseAccessor {
41
+ constructor(selector: JsonSelector) {
42
+ super(selector);
43
+ }
44
+
45
+ isValidContext(): boolean {
46
+ return true;
47
+ }
48
+
49
+ set(): void {
50
+ // ignored
51
+ }
52
+
53
+ delete(): void {
54
+ // ignored
55
+ }
56
+ }
57
+
58
+ class ConstantAccessor<T> extends ReadOnlyAccessor {
59
+ constructor(selector: JsonSelector, readonly value: T) {
60
+ super(selector);
61
+ }
62
+
63
+ get(): T {
64
+ return this.value;
65
+ }
66
+ }
67
+
68
+ class ContextAccessor extends ReadOnlyAccessor {
69
+ constructor() {
70
+ super({ type: "current" });
71
+ }
72
+
73
+ get(context: unknown): unknown {
74
+ return context;
75
+ }
76
+ }
77
+ class RootContextAccessor extends ReadOnlyAccessor {
78
+ constructor() {
79
+ super({ type: "root" });
80
+ }
81
+
82
+ get(context: unknown, rootContext = context): unknown {
83
+ return rootContext;
84
+ }
85
+ }
86
+
87
+ export function makeJsonSelectorAccessor(
88
+ selector: JsonSelector
89
+ ): UnboundAccessor {
90
+ return visitJsonSelector<UnboundAccessor, undefined>(
91
+ selector,
92
+ {
93
+ current() {
94
+ return new ContextAccessor();
95
+ },
96
+ root() {
97
+ return new RootContextAccessor();
98
+ },
99
+ literal(selector) {
100
+ return new ConstantAccessor(selector, selector.value);
101
+ },
102
+ identifier(selector) {
103
+ const { id } = selector;
104
+ const Accessor = class extends BaseAccessor {
105
+ constructor() {
106
+ super(selector);
107
+ }
108
+ isValidContext(context: unknown) {
109
+ return isObject(context);
110
+ }
111
+ get(context: unknown) {
112
+ return getField(context, id);
113
+ }
114
+ set(value: unknown, context: unknown) {
115
+ if (isObject(context)) {
116
+ context[id] = value;
117
+ }
118
+ }
119
+ delete(context: unknown) {
120
+ if (isObject(context)) {
121
+ delete context[id];
122
+ }
123
+ }
124
+ };
125
+ return new Accessor();
126
+ },
127
+ fieldAccess(selector) {
128
+ const { expression, field } = selector;
129
+ const base = makeJsonSelectorAccessor(expression);
130
+ const Accessor = class extends BaseAccessor {
131
+ constructor() {
132
+ super(selector);
133
+ }
134
+ isValidContext(context: unknown, rootContext = context) {
135
+ return isObject(base.get(context, rootContext));
136
+ }
137
+ get(context: unknown, rootContext = context) {
138
+ return getField(base.get(context, rootContext), field);
139
+ }
140
+ set(value: unknown, context: unknown, rootContext = context) {
141
+ const obj = base.get(context, rootContext);
142
+ if (isObject(obj)) {
143
+ obj[field] = value;
144
+ }
145
+ }
146
+ delete(context: unknown, rootContext = context) {
147
+ const obj = base.get(context, rootContext);
148
+ if (isObject(obj)) {
149
+ delete obj[field];
150
+ }
151
+ }
152
+ };
153
+ return new Accessor();
154
+ },
155
+ indexAccess(selector) {
156
+ const { expression, index } = selector;
157
+ const base = makeJsonSelectorAccessor(expression);
158
+ const Accessor = class extends BaseAccessor {
159
+ constructor() {
160
+ super(selector);
161
+ }
162
+ isValidContext(context: unknown, rootContext = context) {
163
+ return isArray(base.get(context, rootContext));
164
+ }
165
+ get(context: unknown, rootContext = context) {
166
+ return getIndex(base.get(context, rootContext), index);
167
+ }
168
+ set(value: unknown, context: unknown, rootContext = context) {
169
+ const arr = base.get(context, rootContext);
170
+ if (isArray(arr)) {
171
+ arr[index] = value;
172
+ }
173
+ }
174
+ delete(context: unknown, rootContext = context) {
175
+ const arr = base.get(context, rootContext);
176
+ if (isArray(arr)) {
177
+ arr.splice(index, 1);
178
+ }
179
+ }
180
+ };
181
+ return new Accessor();
182
+ },
183
+ idAccess(selector) {
184
+ const { expression, id } = selector;
185
+ const base = makeJsonSelectorAccessor(expression);
186
+ const Accessor = class extends BaseAccessor {
187
+ constructor() {
188
+ super(selector);
189
+ }
190
+ isValidContext(context: unknown, rootContext = context) {
191
+ return isArray(base.get(context, rootContext));
192
+ }
193
+ get(context: unknown, rootContext = context) {
194
+ return findId(base.get(context, rootContext), id);
195
+ }
196
+ set(value: unknown, context: unknown, rootContext = context) {
197
+ const arr = base.get(context, rootContext);
198
+ if (isArray(arr)) {
199
+ const index = findIdIndex(arr, id);
200
+ if (index >= 0) {
201
+ arr[index] = value;
202
+ }
203
+ }
204
+ }
205
+ delete(context: unknown, rootContext = context) {
206
+ const arr = base.get(context, rootContext);
207
+ if (isArray(arr)) {
208
+ const index = findIdIndex(arr, id);
209
+ if (index >= 0) {
210
+ arr.splice(index, 1);
211
+ }
212
+ }
213
+ }
214
+ };
215
+ return new Accessor();
216
+ },
217
+ project(selector) {
218
+ const { expression, projection } = selector;
219
+ const base = makeJsonSelectorAccessor(expression);
220
+ const proj = projection && makeJsonSelectorAccessor(projection);
221
+ const Accessor = class extends BaseAccessor {
222
+ constructor() {
223
+ super(selector);
224
+ }
225
+ isValidContext(context: unknown, rootContext = context) {
226
+ return isArray(base.get(context, rootContext));
227
+ }
228
+ get(context: unknown, rootContext = context) {
229
+ return project(base.get(context, rootContext), projection, context);
230
+ }
231
+ set(value: unknown, context: unknown, rootContext = context) {
232
+ const arr = base.get(context, rootContext);
233
+ if (isArray(arr)) {
234
+ if (proj) {
235
+ for (const element of arr) {
236
+ proj.set(value, element, rootContext);
237
+ }
238
+ } else {
239
+ replaceArray(arr, asArray(value));
240
+ }
241
+ }
242
+ }
243
+ delete(context: unknown, rootContext = context) {
244
+ const arr = base.get(context, rootContext);
245
+ if (isArray(arr)) {
246
+ if (proj) {
247
+ for (const element of arr) {
248
+ proj.delete(element, rootContext);
249
+ }
250
+ } else {
251
+ arr.length = 0;
252
+ }
253
+ }
254
+ }
255
+ };
256
+ return new Accessor();
257
+ },
258
+ filter(selector) {
259
+ const { expression, condition } = selector;
260
+ const base = makeJsonSelectorAccessor(expression);
261
+ const Accessor = class extends BaseAccessor {
262
+ constructor() {
263
+ super(selector);
264
+ }
265
+ isValidContext(context: unknown, rootContext = context) {
266
+ return isArray(base.get(context, rootContext));
267
+ }
268
+ get(context: unknown, rootContext = context) {
269
+ return filter(base.get(context, rootContext), condition, context);
270
+ }
271
+ set(value: unknown, context: unknown, rootContext = context) {
272
+ const arr = base.get(context, rootContext);
273
+ if (isArray(arr)) {
274
+ replaceArray(
275
+ arr,
276
+ invertedFilter(arr, condition, rootContext).concat(
277
+ asArray(value)
278
+ )
279
+ );
280
+ }
281
+ }
282
+ delete(context: unknown, rootContext = context) {
283
+ const arr = base.get(context, rootContext);
284
+ if (isArray(arr)) {
285
+ replaceArray(arr, invertedFilter(arr, condition, rootContext));
286
+ }
287
+ }
288
+ };
289
+ return new Accessor();
290
+ },
291
+ slice(selector) {
292
+ const { expression, start, end, step } = selector;
293
+ const base = makeJsonSelectorAccessor(expression);
294
+ const Accessor = class extends BaseAccessor {
295
+ constructor() {
296
+ super(selector);
297
+ }
298
+ isValidContext(context: unknown, rootContext = context) {
299
+ return isArray(base.get(context, rootContext));
300
+ }
301
+ get(context: unknown, rootContext = context) {
302
+ return slice(base.get(context, rootContext), start, end, step);
303
+ }
304
+ set(value: unknown, context: unknown, rootContext = context) {
305
+ const arr = base.get(context, rootContext);
306
+ if (isArray(arr)) {
307
+ replaceArray(
308
+ arr,
309
+ invertedSlice(arr, start, end, step).concat(asArray(value))
310
+ );
311
+ }
312
+ }
313
+ delete(context: unknown, rootContext = context) {
314
+ const arr = base.get(context, rootContext);
315
+ if (isArray(arr)) {
316
+ replaceArray(arr, invertedSlice(arr, start, end, step));
317
+ }
318
+ }
319
+ };
320
+ return new Accessor();
321
+ },
322
+ flatten(selector) {
323
+ const { expression } = selector;
324
+ const base = makeJsonSelectorAccessor(expression);
325
+ const Accessor = class extends BaseAccessor {
326
+ constructor() {
327
+ super(selector);
328
+ }
329
+ isValidContext(context: unknown, rootContext = context) {
330
+ return isArray(base.get(context, rootContext));
331
+ }
332
+ get(context: unknown, rootContext = context) {
333
+ return flatten(base.get(context, rootContext));
334
+ }
335
+ set(value: unknown, context: unknown, rootContext = context) {
336
+ const arr = base.get(context, rootContext);
337
+ if (isArray(arr)) {
338
+ replaceArray(arr, asArray(value));
339
+ }
340
+ }
341
+ delete(context: unknown, rootContext = context) {
342
+ const arr = base.get(context, rootContext);
343
+ if (isArray(arr)) {
344
+ arr.length = 0;
345
+ }
346
+ }
347
+ };
348
+ return new Accessor();
349
+ },
350
+ not(selector) {
351
+ const { expression } = selector;
352
+ const base = makeJsonSelectorAccessor(expression);
353
+ const Accessor = class extends ReadOnlyAccessor {
354
+ constructor() {
355
+ super(selector);
356
+ }
357
+ get(context: unknown, rootContext = context) {
358
+ return isFalseOrEmpty(base.get(context, rootContext));
359
+ }
360
+ };
361
+ return new Accessor();
362
+ },
363
+ compare(selector) {
364
+ const { lhs, rhs, operator } = selector;
365
+ const la = makeJsonSelectorAccessor(lhs);
366
+ const ra = makeJsonSelectorAccessor(rhs);
367
+ const Accessor = class extends ReadOnlyAccessor {
368
+ constructor() {
369
+ super(selector);
370
+ }
371
+ get(context: unknown, rootContext = context) {
372
+ const lv = la.get(context, rootContext);
373
+ const rv = ra.get(context, rootContext);
374
+ return compare(lv, rv, operator);
375
+ }
376
+ };
377
+ return new Accessor();
378
+ },
379
+ and(selector) {
380
+ const { lhs, rhs } = selector;
381
+ const la = makeJsonSelectorAccessor(lhs);
382
+ const ra = makeJsonSelectorAccessor(rhs);
383
+ const Accessor = class extends ReadOnlyAccessor {
384
+ constructor() {
385
+ super(selector);
386
+ }
387
+ get(context: unknown, rootContext = context) {
388
+ const lv = la.get(context, rootContext);
389
+ return isFalseOrEmpty(lv) ? lv : ra.get(context, rootContext);
390
+ }
391
+ };
392
+ return new Accessor();
393
+ },
394
+ or(selector) {
395
+ const { lhs, rhs } = selector;
396
+ const la = makeJsonSelectorAccessor(lhs);
397
+ const ra = makeJsonSelectorAccessor(rhs);
398
+ const Accessor = class extends ReadOnlyAccessor {
399
+ constructor() {
400
+ super(selector);
401
+ }
402
+ get(context: unknown, rootContext = context) {
403
+ const lv = la.get(context, rootContext);
404
+ return !isFalseOrEmpty(lv) ? lv : ra.get(context, rootContext);
405
+ }
406
+ };
407
+ return new Accessor();
408
+ },
409
+ pipe(selector) {
410
+ const { lhs, rhs } = selector;
411
+ const la = makeJsonSelectorAccessor(lhs);
412
+ const ra = makeJsonSelectorAccessor(rhs);
413
+ const Accessor = class extends BaseAccessor {
414
+ constructor() {
415
+ super(selector);
416
+ }
417
+ isValidContext(context: unknown, rootContext = context) {
418
+ return ra.isValidContext(la.get(context, rootContext), rootContext);
419
+ }
420
+ get(context: unknown, rootContext = context) {
421
+ return ra.get(la.get(context, rootContext), rootContext);
422
+ }
423
+ set(value: unknown, context: unknown, rootContext = context) {
424
+ ra.set(value, la.get(context, rootContext), rootContext);
425
+ }
426
+ delete(context: unknown, rootContext = context) {
427
+ ra.delete(la.get(context, rootContext), rootContext);
428
+ }
429
+ };
430
+ return new Accessor();
431
+ },
432
+ },
433
+ undefined
434
+ );
435
+ }
436
+
437
+ export interface Accessor<T> {
438
+ readonly selector: JsonSelector;
439
+ readonly valid: boolean;
440
+ readonly path: string;
441
+ get(): T;
442
+ set(value: T): void;
443
+ delete(): void;
444
+ }
445
+
446
+ export function bindJsonSelectorAccessor(
447
+ unbound: UnboundAccessor,
448
+ context: unknown,
449
+ rootContext = context
450
+ ): Accessor<unknown> {
451
+ const { selector } = unbound;
452
+ const valid = unbound.isValidContext(context, rootContext);
453
+ return {
454
+ selector,
455
+ valid,
456
+ path: formatJsonSelector(selector),
457
+ get() {
458
+ return unbound.get(context, rootContext);
459
+ },
460
+ set(value: unknown) {
461
+ unbound.set(value, context, rootContext);
462
+ },
463
+ delete() {
464
+ unbound.delete(context, rootContext);
465
+ },
466
+ };
467
+ }
468
+
469
+ export function accessWithJsonSelector(
470
+ selector: JsonSelector,
471
+ context: unknown,
472
+ rootContext = context
473
+ ): Accessor<unknown> {
474
+ return bindJsonSelectorAccessor(
475
+ makeJsonSelectorAccessor(selector),
476
+ context,
477
+ rootContext
478
+ );
479
+ }
480
+
481
+ function replaceArray(
482
+ target: unknown[],
483
+ source: readonly unknown[]
484
+ ): unknown[] {
485
+ target.length = 0;
486
+ target.push(...source);
487
+ return target;
488
+ }
489
+
490
+ function invertedFilter(
491
+ value: unknown[],
492
+ condition: JsonSelector,
493
+ rootContext: unknown
494
+ ): unknown[] {
495
+ return value.filter((e) =>
496
+ isFalseOrEmpty(evaluateJsonSelector(condition, e, rootContext))
497
+ );
498
+ }
499
+
500
+ export function invertedSlice(
501
+ value: unknown[],
502
+ start: number | undefined,
503
+ end?: number,
504
+ step?: number
505
+ ): unknown[] {
506
+ ({ start, end, step } = normalizeSlice(value.length, start, end, step));
507
+ const collected: unknown[] = [];
508
+ if (step > 0) {
509
+ if (start >= end) {
510
+ return value;
511
+ }
512
+ let skip = start;
513
+ for (let i = 0; i < value.length; ++i) {
514
+ if (i < skip || i >= end) {
515
+ collected.push(value[i]);
516
+ } else {
517
+ skip += step;
518
+ }
519
+ }
520
+ } else {
521
+ if (start <= end) {
522
+ return value;
523
+ }
524
+ let skip = start;
525
+ for (let i = value.length - 1; i >= 0; --i) {
526
+ if (i > skip || i <= end) {
527
+ collected.push(value[i]);
528
+ } else {
529
+ skip += step;
530
+ }
531
+ }
532
+ }
533
+ return collected;
534
+ }
package/src/ast.ts ADDED
@@ -0,0 +1,114 @@
1
+ import { JsonValue } from "type-fest";
2
+
3
+ export type JsonSelectorNodeType = JsonSelector["type"];
4
+
5
+ export interface JsonSelectorCurrent {
6
+ type: "current";
7
+ }
8
+
9
+ export interface JsonSelectorRoot {
10
+ type: "root";
11
+ }
12
+
13
+ export interface JsonSelectorLiteral {
14
+ type: "literal";
15
+ value: JsonValue;
16
+ }
17
+
18
+ export interface JsonSelectorIdentifier {
19
+ type: "identifier";
20
+ id: string;
21
+ }
22
+
23
+ export interface JsonSelectorFieldAccess {
24
+ type: "fieldAccess";
25
+ expression: JsonSelector;
26
+ field: string;
27
+ }
28
+
29
+ export interface JsonSelectorIndexAccess {
30
+ type: "indexAccess";
31
+ expression: JsonSelector;
32
+ index: number;
33
+ }
34
+
35
+ export interface JsonSelectorIdAccess {
36
+ type: "idAccess";
37
+ expression: JsonSelector;
38
+ id: string;
39
+ }
40
+
41
+ export interface JsonSelectorProject {
42
+ type: "project";
43
+ expression: JsonSelector;
44
+ projection?: JsonSelector;
45
+ }
46
+
47
+ export interface JsonSelectorFilter {
48
+ type: "filter";
49
+ expression: JsonSelector;
50
+ condition: JsonSelector;
51
+ }
52
+
53
+ export interface JsonSelectorSlice {
54
+ type: "slice";
55
+ expression: JsonSelector;
56
+ start?: number;
57
+ end?: number;
58
+ step?: number;
59
+ }
60
+
61
+ export interface JsonSelectorFlatten {
62
+ type: "flatten";
63
+ expression: JsonSelector;
64
+ }
65
+
66
+ export interface JsonSelectorNot {
67
+ type: "not";
68
+ expression: JsonSelector;
69
+ }
70
+
71
+ export type JsonSelectorCompareOperator = "<" | "<=" | "==" | ">=" | ">" | "!=";
72
+
73
+ export interface JsonSelectorCompare {
74
+ type: "compare";
75
+ operator: JsonSelectorCompareOperator;
76
+ lhs: JsonSelector;
77
+ rhs: JsonSelector;
78
+ }
79
+
80
+ export interface JsonSelectorAnd {
81
+ type: "and";
82
+ lhs: JsonSelector;
83
+ rhs: JsonSelector;
84
+ }
85
+
86
+ export interface JsonSelectorOr {
87
+ type: "or";
88
+ lhs: JsonSelector;
89
+ rhs: JsonSelector;
90
+ }
91
+
92
+ export interface JsonSelectorPipe {
93
+ type: "pipe";
94
+ lhs: JsonSelector;
95
+ rhs: JsonSelector;
96
+ }
97
+
98
+ export type JsonSelector =
99
+ | JsonSelectorCurrent
100
+ | JsonSelectorRoot
101
+ | JsonSelectorLiteral
102
+ | JsonSelectorIdentifier
103
+ | JsonSelectorFieldAccess
104
+ | JsonSelectorIndexAccess
105
+ | JsonSelectorIdAccess
106
+ | JsonSelectorProject
107
+ | JsonSelectorFilter
108
+ | JsonSelectorSlice
109
+ | JsonSelectorFlatten
110
+ | JsonSelectorNot
111
+ | JsonSelectorCompare
112
+ | JsonSelectorAnd
113
+ | JsonSelectorOr
114
+ | JsonSelectorPipe;