@jarenjs/json 0.9.2 → 0.34.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/ARCHITECTURE.md +86 -13
- package/README.md +248 -23
- package/dist/types/canonical.d.ts +37 -0
- package/dist/types/cow.d.ts +28 -0
- package/dist/types/errors.d.ts +45 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/jslt/errors.d.ts +15 -8
- package/dist/types/jslt/index.d.ts +22 -0
- package/dist/types/jslt/packs/finance.d.ts +119 -0
- package/dist/types/jslt/packs/index.d.ts +310 -0
- package/dist/types/jslt/packs/math.d.ts +159 -0
- package/dist/types/jslt/packs/stats.d.ts +48 -0
- package/dist/types/jslt/registry.d.ts +65 -0
- package/dist/types/jtlt/errors.d.ts +3 -6
- package/dist/types/option-variants.d.ts +29 -0
- package/dist/types/patch.d.ts +214 -0
- package/dist/types/path.d.ts +139 -9
- package/dist/types/pointer.d.ts +100 -9
- package/dist/types/query/compile.d.ts +12 -0
- package/dist/types/query/errors.d.ts +72 -8
- package/dist/types/query/index.d.ts +317 -25
- package/dist/types/query/normalize.d.ts +24 -0
- package/dist/types/query/operators.d.ts +241 -1
- package/dist/types/query/runtime.d.ts +5 -8
- package/dist/types/query/types.d.ts +34 -0
- package/dist/types/segments.d.ts +31 -0
- package/dist/types/write.d.ts +204 -0
- package/dist/types/xquery/parse.d.ts +2 -3
- package/docs/JSLT-FORMAT.md +74 -3
- package/docs/JSLT-PRELUDE.md +1 -1
- package/docs/QUERY-FORMAT.md +695 -33
- package/package.json +18 -4
- package/schemas/geojson.draft-07.schema.json +323 -0
- package/schemas/geojson.jaren.schema.json +863 -0
- package/schemas/geojson.schema.json +172 -0
- package/schemas/jaren-jslt.authoring.schema.json +142 -0
- package/schemas/jaren-jslt.draft-07.schema.json +152 -11
- package/schemas/jaren-jslt.llm-profile.schema.json +782 -0
- package/schemas/jaren-jslt.schema.json +152 -11
- package/schemas/jaren-query.draft-07.schema.json +152 -11
- package/schemas/jaren-query.llm-profile.schema.json +619 -0
- package/schemas/jaren-query.schema.json +82 -15
- package/src/basic.js +1 -1
- package/src/canonical.js +170 -0
- package/src/cow.js +106 -0
- package/src/errors.js +68 -0
- package/src/index.js +3 -0
- package/src/jslt/dispatch.js +178 -28
- package/src/jslt/errors.js +19 -14
- package/src/jslt/index.js +37 -29
- package/src/jslt/packs/finance.js +49 -0
- package/src/jslt/packs/index.js +18 -0
- package/src/jslt/packs/math.js +46 -0
- package/src/jslt/packs/stats.js +65 -0
- package/src/jslt/registry.js +200 -0
- package/src/jslt/stylesheet.js +14 -23
- package/src/jtlt/desugar.js +2 -3
- package/src/jtlt/errors.js +6 -12
- package/src/jtlt/index.js +12 -29
- package/src/jtlt/template.js +9 -18
- package/src/option-variants.js +54 -0
- package/src/patch.js +1052 -0
- package/src/path.js +319 -52
- package/src/pointer.js +225 -44
- package/src/query/compile.js +790 -75
- package/src/query/errors.js +72 -12
- package/src/query/index.js +274 -42
- package/src/query/normalize.js +489 -78
- package/src/query/operators.js +620 -23
- package/src/query/runtime.js +5 -19
- package/src/query/types.js +213 -0
- package/src/segments.js +409 -64
- package/src/write.js +660 -0
- package/src/xquery/parse.js +37 -53
|
@@ -1,18 +1,15 @@
|
|
|
1
|
+
import { CodedDocPathError } from '../errors.js';
|
|
1
2
|
/**
|
|
2
3
|
* Error thrown when a JTLT template is rejected at compile time
|
|
3
4
|
* (`TL0xxx` codes).
|
|
4
5
|
*/
|
|
5
|
-
export declare class JtltCompileError extends
|
|
6
|
-
code: any;
|
|
7
|
-
docPath: any;
|
|
6
|
+
export declare class JtltCompileError extends CodedDocPathError {
|
|
8
7
|
constructor(code: any, message: any, docPath: any, cause?: undefined);
|
|
9
8
|
}
|
|
10
9
|
/**
|
|
11
10
|
* Error thrown when rendering with a compiled JTLT template fails
|
|
12
11
|
* (`TL2xxx` codes).
|
|
13
12
|
*/
|
|
14
|
-
export declare class JtltRuntimeError extends
|
|
15
|
-
code: any;
|
|
16
|
-
docPath: any;
|
|
13
|
+
export declare class JtltRuntimeError extends CodedDocPathError {
|
|
17
14
|
constructor(code: any, message: any, docPath: any, cause?: undefined);
|
|
18
15
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Per-document compilation-variant caching, shared by the JSLT
|
|
3
|
+
* and JTLT one-call entry points. A document compiles differently under
|
|
4
|
+
* different options, so the cache key is the FULL option tuple — every
|
|
5
|
+
* option that changes what compiles must be part of the derived key, or
|
|
6
|
+
* a second call with different options silently reuses the first
|
|
7
|
+
* compilation (the cache-poisoning bug the JTLT cache had before the
|
|
8
|
+
* health pass). Identity-compared option values (hook functions,
|
|
9
|
+
* function-extension registries) are interned to stable per-process ids
|
|
10
|
+
* so the key is a flat string and lookup is O(1) instead of a linear
|
|
11
|
+
* scan over an unbounded variants array.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* A stable per-process id for an identity-compared option value.
|
|
15
|
+
* `null`/`undefined` share id 0 ("absent").
|
|
16
|
+
* @param {any} value
|
|
17
|
+
* @returns {number}
|
|
18
|
+
*/
|
|
19
|
+
export declare function identityOf(value: any): number;
|
|
20
|
+
/**
|
|
21
|
+
* A two-axis compilation cache: documents by identity (weak, entries
|
|
22
|
+
* die with the document), variants per document by derived option key
|
|
23
|
+
* (bounded LRU — the old variants array grew without bound).
|
|
24
|
+
* @param {number} [limitPerDocument] - variant bound per document
|
|
25
|
+
* @returns {{ getOrCompile: (document: object, key: string, compile: () => any) => any }}
|
|
26
|
+
*/
|
|
27
|
+
export declare function createOptionVariantCache(limitPerDocument?: number): {
|
|
28
|
+
getOrCompile: (document: object, key: string, compile: () => any) => any;
|
|
29
|
+
};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { CodedError } from '@jarenjs/core/errors';
|
|
2
|
+
import { CodedDocPathError } from './errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* Error thrown when a JSON Patch document is rejected at compile time
|
|
5
|
+
* (`JP0xxx` codes). `docPath` is an RFC 6901 JSON Pointer into the
|
|
6
|
+
* patch document (e.g. `/2/from`). A wrapped pointer syntax error is
|
|
7
|
+
* exposed through `cause`.
|
|
8
|
+
*/
|
|
9
|
+
export declare class JsonPatchCompileError extends CodedDocPathError {
|
|
10
|
+
constructor(code: any, message: any, docPath: any, cause?: undefined);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Error thrown when applying a compiled JSON Patch fails (`JP2xxx`
|
|
14
|
+
* codes). `docPath` points at the failing operation in the patch
|
|
15
|
+
* document; `dataPath` is the operation's target location in the
|
|
16
|
+
* document being patched — both render, separately identifiable, per
|
|
17
|
+
* the `@jarenjs/core` coded contract (`at` = document, `in data` =
|
|
18
|
+
* data).
|
|
19
|
+
*/
|
|
20
|
+
export declare class JsonPatchRuntimeError extends CodedError {
|
|
21
|
+
constructor(code: any, reason: any, docPath: any, dataPath: any);
|
|
22
|
+
}
|
|
23
|
+
export type JsonPatchOperation = {
|
|
24
|
+
op: 'add' | 'remove' | 'replace' | 'move' | 'copy' | 'test';
|
|
25
|
+
path: string;
|
|
26
|
+
value?: any;
|
|
27
|
+
from?: string;
|
|
28
|
+
};
|
|
29
|
+
export type JsonPatchApplier = (doc: any) => any;
|
|
30
|
+
export type JsonPatchChangesApplier = (doc: any) => {
|
|
31
|
+
doc: any;
|
|
32
|
+
changes: string[];
|
|
33
|
+
};
|
|
34
|
+
export type JsonPatchOptions = {
|
|
35
|
+
/**
|
|
36
|
+
* - Apply in place instead of copy-on-write.
|
|
37
|
+
* Faster, but the input document is modified and a failing operation
|
|
38
|
+
* leaves it partially patched (application is no longer atomic).
|
|
39
|
+
*/
|
|
40
|
+
mutate?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* - How operation values enter the
|
|
43
|
+
* result: `'share'` (default) inserts them by reference, so results of
|
|
44
|
+
* repeated applications share structure with the patch document and
|
|
45
|
+
* must be treated as immutable; `'fresh'` deep-copies per application.
|
|
46
|
+
* In-place mode always behaves as `'fresh'`.
|
|
47
|
+
*/
|
|
48
|
+
values?: 'share' | 'fresh';
|
|
49
|
+
/**
|
|
50
|
+
* - Track changed locations: the applier
|
|
51
|
+
* returns `{ doc, changes }` where `changes` is an array of JSON
|
|
52
|
+
* Pointers, one per successful write, in application order and not
|
|
53
|
+
* deduplicated. The reported pointer is chosen to be *sound for
|
|
54
|
+
* invalidation* — everything at or below it (plus the identity of its
|
|
55
|
+
* ancestors) may have changed, and nothing outside the reported set
|
|
56
|
+
* did: object writes, array replaces and array appends report the
|
|
57
|
+
* written location itself; array inserts and removes that shift later
|
|
58
|
+
* elements report the parent array's pointer; a root write reports
|
|
59
|
+
* `''`. `test` operations report nothing.
|
|
60
|
+
*/
|
|
61
|
+
changes?: boolean;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Compile a JSON Patch (RFC 6902) into a reusable applier.
|
|
65
|
+
*
|
|
66
|
+
* The patch document is validated once (`JsonPatchCompileError`,
|
|
67
|
+
* `JP0xxx`, with a `docPath` into the patch document); every pointer is
|
|
68
|
+
* pre-parsed and each operation becomes a specialized closure. Applying
|
|
69
|
+
* is copy-on-write: the input document is never mutated, untouched
|
|
70
|
+
* subtrees are shared with the result, and application is atomic - a
|
|
71
|
+
* failing operation (`JsonPatchRuntimeError`, `JP2xxx`) leaves nothing
|
|
72
|
+
* behind.
|
|
73
|
+
*
|
|
74
|
+
* With `changes: true` the applier is specialized at compile time to
|
|
75
|
+
* also report the changed locations: it returns `{ doc, changes }`,
|
|
76
|
+
* where `changes` holds one JSON Pointer per successful write with the
|
|
77
|
+
* invalidation-sound semantics documented on `JsonPatchOptions` — the
|
|
78
|
+
* primitive dirty-path consumers (view re-rendering, rule dependency
|
|
79
|
+
* memoization) build on.
|
|
80
|
+
*
|
|
81
|
+
* @param {JsonPatchOperation[]} patch - The RFC 6902 patch document
|
|
82
|
+
* @param {JsonPatchOptions} [options] - Application options
|
|
83
|
+
* @returns {JsonPatchApplier | JsonPatchChangesApplier} applier
|
|
84
|
+
* returning the patched document (or `{ doc, changes }` with the
|
|
85
|
+
* `changes` option)
|
|
86
|
+
* @throws {JsonPatchCompileError} When the patch document is invalid
|
|
87
|
+
* @example
|
|
88
|
+
* const apply = compileJSONPatch([
|
|
89
|
+
* { op: 'test', path: '/version', value: 5 },
|
|
90
|
+
* { op: 'replace', path: '/user/name', value: 'Bob' },
|
|
91
|
+
* { op: 'add', path: '/user/tags/-', value: 'admin' },
|
|
92
|
+
* ]);
|
|
93
|
+
* const next = apply(doc); // doc is untouched
|
|
94
|
+
* @example
|
|
95
|
+
* const applyTracked = compileJSONPatch(
|
|
96
|
+
* [{ op: 'replace', path: '/user/name', value: 'Bob' }],
|
|
97
|
+
* { changes: true });
|
|
98
|
+
* const { doc: next2, changes } = applyTracked(doc);
|
|
99
|
+
* // changes: ['/user/name']
|
|
100
|
+
*/
|
|
101
|
+
export declare function compileJSONPatch(patch: JsonPatchOperation[], options?: JsonPatchOptions): JsonPatchApplier | JsonPatchChangesApplier;
|
|
102
|
+
/**
|
|
103
|
+
* Apply a JSON Patch (RFC 6902) to a document in one shot. Compiles the
|
|
104
|
+
* patch and applies it once; on hot paths prefer `compileJSONPatch` and
|
|
105
|
+
* reuse the applier.
|
|
106
|
+
*
|
|
107
|
+
* @param {any} doc - The document to patch (never mutated unless
|
|
108
|
+
* `options.mutate` is set)
|
|
109
|
+
* @param {JsonPatchOperation[]} patch - The RFC 6902 patch document
|
|
110
|
+
* @param {JsonPatchOptions} [options] - Application options
|
|
111
|
+
* @returns {any} The patched document, or `{ doc, changes }` when
|
|
112
|
+
* `options.changes` is set
|
|
113
|
+
* @throws {JsonPatchCompileError} When the patch document is invalid
|
|
114
|
+
* @throws {JsonPatchRuntimeError} When an operation fails to apply
|
|
115
|
+
*/
|
|
116
|
+
export declare function applyJSONPatch(doc: any, patch: JsonPatchOperation[], options?: JsonPatchOptions): any;
|
|
117
|
+
/**
|
|
118
|
+
* Returns true when `patch` is a structurally valid RFC 6902 patch
|
|
119
|
+
* document (an array of well-formed operation objects with valid
|
|
120
|
+
* pointers). Runtime applicability against a document is not checked.
|
|
121
|
+
* @param {any} patch - The candidate patch document
|
|
122
|
+
* @returns {boolean}
|
|
123
|
+
*/
|
|
124
|
+
export declare function isValidJSONPatch(patch: any): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Compute a JSON Patch (RFC 6902) that transforms `source` into
|
|
127
|
+
* `target`: `applyJSONPatch(source, createJSONPatch(source, target))`
|
|
128
|
+
* is deep-equal to `target`.
|
|
129
|
+
*
|
|
130
|
+
* Objects diff member-wise; arrays trim the common prefix/suffix and
|
|
131
|
+
* diff the middle index-wise, so in-place edits and head/tail
|
|
132
|
+
* insertions produce minimal patches while a mid-array insertion falls
|
|
133
|
+
* back to correct (but larger) per-index replaces. Emitted `value`
|
|
134
|
+
* members share references with `target`.
|
|
135
|
+
*
|
|
136
|
+
* `arrayDiff: 'minimal'` aligns the changed middle instead, so a
|
|
137
|
+
* mid-array insertion or deletion is emitted as one `add`/`remove` —
|
|
138
|
+
* the smallest patch, which is what matters when patches go over the
|
|
139
|
+
* wire. The alignment minimizes the patch itself (edit distance with
|
|
140
|
+
* substitutions, not a longest common subsequence: LCS maximizes kept
|
|
141
|
+
* elements, which costs a delete plus an insert on a permutation where
|
|
142
|
+
* one rewrite would do), so it never takes more alignment steps than
|
|
143
|
+
* the index-wise pairing.
|
|
144
|
+
*
|
|
145
|
+
* It is opt-in because it costs O(m*n) time and space in the length of
|
|
146
|
+
* that middle, against the default's linear pass; above a fixed cell
|
|
147
|
+
* budget a single array falls back to the index-wise diff, so the mode
|
|
148
|
+
* never turns a large diff quadratic. Both modes produce patches that
|
|
149
|
+
* reproduce `target` exactly.
|
|
150
|
+
*
|
|
151
|
+
* @param {any} source - The original document
|
|
152
|
+
* @param {any} target - The desired document
|
|
153
|
+
* @param {{ arrayDiff?: 'index' | 'minimal' }} [options] - `arrayDiff`
|
|
154
|
+
* selects the array strategy: `'index'` (default) or `'minimal'`
|
|
155
|
+
* @returns {JsonPatchOperation[]} The patch document (empty when equal)
|
|
156
|
+
* @throws {TypeError} When `arrayDiff` is not a known mode
|
|
157
|
+
* @example
|
|
158
|
+
* createJSONPatch({ a: 1, b: 2 }, { a: 1, b: 3, c: 4 });
|
|
159
|
+
* // [{ op: 'replace', path: '/b', value: 3 },
|
|
160
|
+
* // { op: 'add', path: '/c', value: 4 }]
|
|
161
|
+
* @example
|
|
162
|
+
* const before = [{ id: 1 }, { id: 2 }, { id: 3, n: 0 }];
|
|
163
|
+
* const after = [{ id: 1 }, { id: 9 }, { id: 2 }, { id: 3, n: 1 }];
|
|
164
|
+
* createJSONPatch(before, after, { arrayDiff: 'minimal' });
|
|
165
|
+
* // [{ op: 'add', path: '/1', value: { id: 9 } },
|
|
166
|
+
* // { op: 'replace', path: '/3/n', value: 1 }]
|
|
167
|
+
*/
|
|
168
|
+
export declare function createJSONPatch(source: any, target: any, options?: {
|
|
169
|
+
arrayDiff?: 'index' | 'minimal';
|
|
170
|
+
}): JsonPatchOperation[];
|
|
171
|
+
export type JsonMergePatchApplier = (doc: any) => any;
|
|
172
|
+
/**
|
|
173
|
+
* Compile a JSON Merge Patch (RFC 7396) into a reusable applier.
|
|
174
|
+
*
|
|
175
|
+
* The patch pre-splits once into remove/set/merge plans per level.
|
|
176
|
+
* Applying is copy-on-write and identity-preserving: unchanged subtrees
|
|
177
|
+
* (and a wholly unchanged document) come back by reference, set values
|
|
178
|
+
* are shared with the patch document, and the input is never mutated.
|
|
179
|
+
*
|
|
180
|
+
* @param {any} patch - The merge patch (any JSON value; a non-object
|
|
181
|
+
* replaces the document wholesale)
|
|
182
|
+
* @returns {JsonMergePatchApplier} applier returning the patched document
|
|
183
|
+
* @example
|
|
184
|
+
* const apply = compileMergePatch({ age: 31, temp: null });
|
|
185
|
+
* apply({ name: 'Alice', age: 30, temp: 'x' });
|
|
186
|
+
* // { name: 'Alice', age: 31 }
|
|
187
|
+
*/
|
|
188
|
+
export declare function compileMergePatch(patch: any): JsonMergePatchApplier;
|
|
189
|
+
/**
|
|
190
|
+
* Apply a JSON Merge Patch (RFC 7396) to a document in one shot. On hot
|
|
191
|
+
* paths prefer `compileMergePatch` and reuse the applier.
|
|
192
|
+
*
|
|
193
|
+
* @param {any} doc - The document to patch (never mutated)
|
|
194
|
+
* @param {any} patch - The merge patch
|
|
195
|
+
* @returns {any} The patched document
|
|
196
|
+
*/
|
|
197
|
+
export declare function applyMergePatch(doc: any, patch: any): any;
|
|
198
|
+
/**
|
|
199
|
+
* Compute a JSON Merge Patch (RFC 7396) that transforms `source` into
|
|
200
|
+
* `target`. Removed members become `null`, nested objects diff
|
|
201
|
+
* recursively, and arrays (or any kind change) replace wholesale.
|
|
202
|
+
*
|
|
203
|
+
* RFC 7396 cannot represent a member whose target value is `null`:
|
|
204
|
+
* the diff emits `null` (a removal), so applying yields an absent
|
|
205
|
+
* member instead. Emitted values share references with `target`.
|
|
206
|
+
*
|
|
207
|
+
* @param {any} source - The original document
|
|
208
|
+
* @param {any} target - The desired document
|
|
209
|
+
* @returns {any} The merge patch (`{}` when nothing changed)
|
|
210
|
+
* @example
|
|
211
|
+
* createMergePatch({ a: 'b', c: 1 }, { a: 'x' });
|
|
212
|
+
* // { a: 'x', c: null }
|
|
213
|
+
*/
|
|
214
|
+
export declare function createMergePatch(source: any, target: any): any;
|
package/dist/types/path.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LabeledSyntaxError } from './errors.js';
|
|
1
2
|
/**
|
|
2
3
|
* Sentinel for the absence of a value ("Nothing" in RFC 9535 terms), as
|
|
3
4
|
* distinct from the JSON value `null`.
|
|
@@ -7,9 +8,7 @@ export declare const JSONPATH_NOTHING: symbol;
|
|
|
7
8
|
* Error thrown when a JSONPath query is not valid RFC 9535 syntax
|
|
8
9
|
* (including queries that are not well-typed per section 2.4.3).
|
|
9
10
|
*/
|
|
10
|
-
export declare class JSONPathSyntaxError extends
|
|
11
|
-
source: any;
|
|
12
|
-
position: any;
|
|
11
|
+
export declare class JSONPathSyntaxError extends LabeledSyntaxError {
|
|
13
12
|
constructor(message: any, source: any, position: any);
|
|
14
13
|
}
|
|
15
14
|
export type JSONPathNameSelector = {
|
|
@@ -101,11 +100,34 @@ export type JSONPathQuery = ((data: any) => any[]) & {
|
|
|
101
100
|
values: (data: any) => any[];
|
|
102
101
|
first: (data: any) => any;
|
|
103
102
|
exists: (data: any) => boolean;
|
|
103
|
+
iterate: (data: any) => Generator<any>;
|
|
104
104
|
nodes: (data: any) => JSONPathNode[];
|
|
105
105
|
paths: (data: any) => string[];
|
|
106
106
|
source: string;
|
|
107
107
|
ast: JSONPathAst;
|
|
108
108
|
};
|
|
109
|
+
export type JSONPathFunction = {
|
|
110
|
+
/**
|
|
111
|
+
* - Declared parameter types
|
|
112
|
+
*/
|
|
113
|
+
params: ('value' | 'nodes' | 'logical')[];
|
|
114
|
+
/**
|
|
115
|
+
* - Declared result type
|
|
116
|
+
*/
|
|
117
|
+
returns: 'value' | 'nodes' | 'logical';
|
|
118
|
+
/**
|
|
119
|
+
* - The implementation
|
|
120
|
+
*/
|
|
121
|
+
evaluate: (...args: any[]) => any;
|
|
122
|
+
};
|
|
123
|
+
export type JSONPathOptions = {
|
|
124
|
+
/**
|
|
125
|
+
* - Custom
|
|
126
|
+
* function extensions, by name. A name must match the RFC's
|
|
127
|
+
* `function-name` production and must not redefine a built-in.
|
|
128
|
+
*/
|
|
129
|
+
pathFunctions?: Record<string, JSONPathFunction>;
|
|
130
|
+
};
|
|
109
131
|
/**
|
|
110
132
|
* Selects a named object member (RFC 9535 name selector).
|
|
111
133
|
* @typedef {Object} JSONPathNameSelector
|
|
@@ -173,19 +195,44 @@ export type JSONPathQuery = ((data: any) => any[]) & {
|
|
|
173
195
|
* values: (data: any) => any[],
|
|
174
196
|
* first: (data: any) => any,
|
|
175
197
|
* exists: (data: any) => boolean,
|
|
198
|
+
* iterate: (data: any) => Generator<any>,
|
|
176
199
|
* nodes: (data: any) => JSONPathNode[],
|
|
177
200
|
* paths: (data: any) => string[],
|
|
178
201
|
* source: string,
|
|
179
202
|
* ast: JSONPathAst,
|
|
180
203
|
* }} JSONPathQuery
|
|
181
204
|
*/
|
|
205
|
+
/**
|
|
206
|
+
* A custom JSONPath function extension (RFC 9535 section 2.4). The
|
|
207
|
+
* declared types are what the parser type-checks call sites against
|
|
208
|
+
* (section 2.4.3), exactly as it does for the five built-ins.
|
|
209
|
+
*
|
|
210
|
+
* `evaluate` receives one argument per declared parameter: a `value`
|
|
211
|
+
* parameter arrives as a JSON value or `JSONPATH_NOTHING`, a `nodes`
|
|
212
|
+
* parameter as an array of the selected values, a `logical` parameter
|
|
213
|
+
* as a boolean. Its result must match `returns` - a `value` function
|
|
214
|
+
* may return `undefined` to mean Nothing.
|
|
215
|
+
* @typedef {Object} JSONPathFunction
|
|
216
|
+
* @property {('value'|'nodes'|'logical')[]} params - Declared parameter types
|
|
217
|
+
* @property {'value'|'nodes'|'logical'} returns - Declared result type
|
|
218
|
+
* @property {(...args: any[]) => any} evaluate - The implementation
|
|
219
|
+
*/
|
|
220
|
+
/**
|
|
221
|
+
* Options accepted by the JSONPath entry points.
|
|
222
|
+
* @typedef {Object} JSONPathOptions
|
|
223
|
+
* @property {Record<string, JSONPathFunction>} [pathFunctions] - Custom
|
|
224
|
+
* function extensions, by name. A name must match the RFC's
|
|
225
|
+
* `function-name` production and must not redefine a built-in.
|
|
226
|
+
*/
|
|
182
227
|
/**
|
|
183
228
|
* Parse a JSONPath query string into an AST.
|
|
184
229
|
* @param {string} source - The JSONPath expression (e.g. `$.store.book[?@.price < 10].title`)
|
|
230
|
+
* @param {JSONPathOptions} [options] - Parse options
|
|
185
231
|
* @returns {JSONPathAst} The parsed query AST
|
|
186
232
|
* @throws {JSONPathSyntaxError} When the query violates the RFC 9535 grammar
|
|
233
|
+
* @throws {TypeError} When `options.pathFunctions` is not a valid registry
|
|
187
234
|
*/
|
|
188
|
-
export declare function parseJSONPath(source: string): JSONPathAst;
|
|
235
|
+
export declare function parseJSONPath(source: string, options?: JSONPathOptions): JSONPathAst;
|
|
189
236
|
/**
|
|
190
237
|
* Compile a JSONPath query (RFC 9535) into a reusable query function.
|
|
191
238
|
*
|
|
@@ -196,6 +243,9 @@ export declare function parseJSONPath(source: string): JSONPathAst;
|
|
|
196
243
|
* - `query(data)` / `query.values(data)` - array of matched values
|
|
197
244
|
* - `query.first(data)` - first matched value, or `undefined`
|
|
198
245
|
* - `query.exists(data)` - true when the query selects at least one node
|
|
246
|
+
* - `query.iterate(data)` - a generator yielding matched values on
|
|
247
|
+
* demand, in document order; `first`/`exists` are one pull of it, so
|
|
248
|
+
* none of the three builds a nodelist it does not need
|
|
199
249
|
* - `query.nodes(data)` - array of `{ path, value }` with normalized paths
|
|
200
250
|
* - `query.paths(data)` - array of normalized paths (RFC 9535 section 2.7)
|
|
201
251
|
* - `query.source` - the original query string
|
|
@@ -203,33 +253,113 @@ export declare function parseJSONPath(source: string): JSONPathAst;
|
|
|
203
253
|
* compiled path mode must agree with the eagerly compiled value mode)
|
|
204
254
|
*
|
|
205
255
|
* @param {string} source - The JSONPath expression
|
|
256
|
+
* @param {JSONPathOptions} [options] - Compile options
|
|
206
257
|
* @returns {JSONPathQuery} The compiled query function
|
|
207
258
|
* @throws {JSONPathSyntaxError} When the query is not valid RFC 9535
|
|
259
|
+
* @throws {TypeError} When `options.pathFunctions` is not a valid registry
|
|
208
260
|
* @example
|
|
209
261
|
* const q = compileJSONPath('$.store.book[?@.price < 10].title');
|
|
210
262
|
* q(data); // ['Sayings of the Century', 'Moby Dick']
|
|
211
263
|
* q.paths(data); // ["$['store']['book'][0]['title']", ...]
|
|
212
264
|
*/
|
|
213
|
-
export declare function compileJSONPath(source: string): JSONPathQuery;
|
|
265
|
+
export declare function compileJSONPath(source: string, options?: JSONPathOptions): JSONPathQuery;
|
|
214
266
|
/**
|
|
215
267
|
* Apply a JSONPath query to a JSON value in one call. Compiled queries
|
|
216
|
-
* are cached (
|
|
217
|
-
* string reuse the compiled function.
|
|
268
|
+
* are cached (bounded LRU, 512 entries), so repeated calls with the
|
|
269
|
+
* same query string reuse the compiled function. A query compiled
|
|
270
|
+
* against a function-extension registry is cached under that registry.
|
|
218
271
|
* @param {string} source - The JSONPath expression
|
|
219
272
|
* @param {any} data - The JSON value to query
|
|
273
|
+
* @param {JSONPathOptions} [options] - Compile options
|
|
220
274
|
* @returns {any[]} Array of matched values
|
|
221
275
|
* @throws {JSONPathSyntaxError} When the query is not valid RFC 9535
|
|
222
276
|
*/
|
|
223
|
-
export declare function queryJSONPath(source: string, data: any): any[];
|
|
277
|
+
export declare function queryJSONPath(source: string, data: any, options?: JSONPathOptions): any[];
|
|
224
278
|
/**
|
|
225
279
|
* Validates a JSONPath expression strictly against the RFC 9535 grammar,
|
|
226
280
|
* including well-typedness of function expressions. Unlike the heuristic
|
|
227
281
|
* `isValidJSONPath` in basic.js, this uses the full parser.
|
|
282
|
+
*
|
|
283
|
+
* Without options this recognizes the five built-in functions and
|
|
284
|
+
* nothing else, which is what the registered `json-path` string format
|
|
285
|
+
* asserts: a format is a property of the string itself, so it must mean
|
|
286
|
+
* the same thing in every schema, independent of which extensions some
|
|
287
|
+
* host happens to have installed. Pass `options.pathFunctions` to
|
|
288
|
+
* validate against a registry instead - a host that wants its
|
|
289
|
+
* extensions asserted registers a tester bound to them.
|
|
290
|
+
*
|
|
228
291
|
* @param {string} str - The JSONPath expression to validate
|
|
292
|
+
* @param {JSONPathOptions} [options] - Compile options
|
|
229
293
|
* @returns {boolean} True when the string is a valid RFC 9535 query
|
|
230
294
|
* @example
|
|
231
295
|
* isValidJSONPathStrict('$.store.book[?@.price < 10]'); // true
|
|
232
296
|
* isValidJSONPathStrict('$.store.book[0 5]'); // false
|
|
233
297
|
* isValidJSONPathStrict('@.name'); // false (queries start at $)
|
|
234
298
|
*/
|
|
235
|
-
export declare function isValidJSONPathStrict(str: string): boolean;
|
|
299
|
+
export declare function isValidJSONPathStrict(str: string, options?: JSONPathOptions): boolean;
|
|
300
|
+
/**
|
|
301
|
+
* The head of a variable-rooted path string: `$` followed by a variable
|
|
302
|
+
* name. The Jaren query format writes a path relative to a bound
|
|
303
|
+
* variable as `$name` plus ordinary RFC 9535 segments, and the query
|
|
304
|
+
* normalizer splits on exactly this production - so schema-time
|
|
305
|
+
* validation and compile-time parsing agree on where the segments start.
|
|
306
|
+
*/
|
|
307
|
+
export declare const RE_JSONPATH_VARIABLE_HEAD: RegExp;
|
|
308
|
+
/**
|
|
309
|
+
* Validates a variable-rooted path string: `$name`, optionally followed
|
|
310
|
+
* by RFC 9535 segments (`$book.price`, `$b[?@.isbn]`, `$item`).
|
|
311
|
+
*
|
|
312
|
+
* This is the counterpart of `isValidJSONPathStrict` for paths whose
|
|
313
|
+
* root is a bound variable rather than the document. Such a string is
|
|
314
|
+
* not a valid RFC 9535 query - the RFC's root identifier is `$` alone -
|
|
315
|
+
* so it can only be checked by recognizing the head and validating the
|
|
316
|
+
* tail as segments, which is what the query normalizer does before it
|
|
317
|
+
* raises `JQ0004`. Without this, a schema could only pattern-check the
|
|
318
|
+
* head and had to leave the segment grammar to the compiler.
|
|
319
|
+
*
|
|
320
|
+
* @param {string} str - The variable-rooted path string to validate
|
|
321
|
+
* @param {JSONPathOptions} [options] - Parse options
|
|
322
|
+
* @returns {boolean} True when the string is a well-formed variable-rooted path
|
|
323
|
+
* @example
|
|
324
|
+
* isValidJSONPathSegments('$book.price'); // true
|
|
325
|
+
* isValidJSONPathSegments('$book'); // true (no segments)
|
|
326
|
+
* isValidJSONPathSegments('$book.price['); // false (unterminated segment)
|
|
327
|
+
* isValidJSONPathSegments('$.price'); // false (no variable name; that is json-path)
|
|
328
|
+
*/
|
|
329
|
+
export declare function isValidJSONPathSegments(str: string, options?: JSONPathOptions): boolean;
|
|
330
|
+
/**
|
|
331
|
+
* Convert a singular JSONPath query - which includes every RFC 9535
|
|
332
|
+
* normalized path - to an RFC 6901 JSON Pointer, so the two addressing
|
|
333
|
+
* standards compose.
|
|
334
|
+
*
|
|
335
|
+
* Any singular form is accepted (`$['store']['book'][0]`, `$.store.book[0]`);
|
|
336
|
+
* non-singular queries and negative (from-the-end) indexes are rejected,
|
|
337
|
+
* because a pointer cannot express them.
|
|
338
|
+
*
|
|
339
|
+
* @param {string} source - A singular JSONPath query
|
|
340
|
+
* @returns {string} The equivalent JSON Pointer
|
|
341
|
+
* @throws {JSONPathSyntaxError} When the query is invalid, not singular,
|
|
342
|
+
* or uses a negative index
|
|
343
|
+
* @example
|
|
344
|
+
* jsonPointerFromJSONPath("$['store']['book'][0]['a/b']"); // '/store/book/0/a~1b'
|
|
345
|
+
*/
|
|
346
|
+
export declare function jsonPointerFromJSONPath(source: string): string;
|
|
347
|
+
/**
|
|
348
|
+
* Convert an RFC 6901 JSON Pointer to an RFC 9535 normalized path.
|
|
349
|
+
*
|
|
350
|
+
* A pointer token is one text with two readings (RFC 6901 lets `"2"`
|
|
351
|
+
* address both a `"2"` member and array element 2); a normalized path
|
|
352
|
+
* must pick one. Convention: a token that is a valid array index (digits,
|
|
353
|
+
* no leading zeros) becomes an index selector `[2]`, everything else a
|
|
354
|
+
* name selector `['name']`. A pointer addressing an object member that
|
|
355
|
+
* merely looks like an index is therefore converted to the index form -
|
|
356
|
+
* convert with the document in hand (e.g. via `query.paths`) when that
|
|
357
|
+
* distinction matters.
|
|
358
|
+
*
|
|
359
|
+
* @param {string} pointer - The JSON Pointer (e.g. `/store/book/0`)
|
|
360
|
+
* @returns {string} The normalized path (e.g. `$['store']['book'][0]`)
|
|
361
|
+
* @throws {JSONPointerSyntaxError} When the pointer is not valid RFC 6901
|
|
362
|
+
* @example
|
|
363
|
+
* jsonPathFromJSONPointer('/store/book/0'); // "$['store']['book'][0]"
|
|
364
|
+
*/
|
|
365
|
+
export declare function jsonPathFromJSONPointer(pointer: string): string;
|
package/dist/types/pointer.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LabeledSyntaxError } from './errors.js';
|
|
1
2
|
/**
|
|
2
3
|
* Sentinel for the absence of a value, as distinct from the JSON value
|
|
3
4
|
* `null`. This is the same sentinel as `JSONPATH_NOTHING` in path.js, so
|
|
@@ -8,9 +9,7 @@ export declare const JSONPOINTER_NOTHING: symbol;
|
|
|
8
9
|
* Error thrown when a (relative) JSON Pointer is not valid RFC 6901 /
|
|
9
10
|
* draft-luff-relative-json-pointer syntax.
|
|
10
11
|
*/
|
|
11
|
-
export declare class JSONPointerSyntaxError extends
|
|
12
|
-
source: any;
|
|
13
|
-
position: any;
|
|
12
|
+
export declare class JSONPointerSyntaxError extends LabeledSyntaxError {
|
|
14
13
|
constructor(message: any, source: any, position: any);
|
|
15
14
|
}
|
|
16
15
|
/**
|
|
@@ -63,6 +62,62 @@ export type RelativeJsonPointerResolver = (dataRoot: any, dataPath: string) => a
|
|
|
63
62
|
* @throws {JSONPointerSyntaxError} When the pointer violates the grammar
|
|
64
63
|
*/
|
|
65
64
|
export declare function parseRelativeJSONPointer(pointer: string): RelativeJsonPointer;
|
|
65
|
+
/**
|
|
66
|
+
* Encode a single reference token for use inside an RFC 6901 JSON
|
|
67
|
+
* Pointer: `~` becomes `~0` and `/` becomes `~1` (RFC 6901 section 3).
|
|
68
|
+
* The write-side inverse of the parser's decode; the escape-free common
|
|
69
|
+
* case returns the input unchanged.
|
|
70
|
+
* @param {string|number} segment - The raw member name or array index
|
|
71
|
+
* @returns {string} The encoded reference token
|
|
72
|
+
* @example
|
|
73
|
+
* encodeJSONPointerSegment('a/b'); // 'a~1b'
|
|
74
|
+
*/
|
|
75
|
+
export declare function encodeJSONPointerSegment(segment: string | number): string;
|
|
76
|
+
/**
|
|
77
|
+
* Decode one RFC 6901 reference token: `~1` becomes `/` and `~0` becomes
|
|
78
|
+
* `~`. The order is normative (RFC 6901 section 4) and not an accident of
|
|
79
|
+
* implementation — decoding `~0` first would turn `~01` into `~1` and then
|
|
80
|
+
* into `/`, so a member literally named `~1` would come back as `/`.
|
|
81
|
+
* The escape-free common case returns the input unchanged.
|
|
82
|
+
* @param {string} token - One encoded reference token (no `/` separators)
|
|
83
|
+
* @returns {string} The decoded member name
|
|
84
|
+
* @example
|
|
85
|
+
* decodeJSONPointerSegment('a~1b'); // 'a/b'
|
|
86
|
+
*/
|
|
87
|
+
export declare function decodeJSONPointerSegment(token: string): string;
|
|
88
|
+
/**
|
|
89
|
+
* Format decoded reference tokens as an RFC 6901 JSON Pointer; the
|
|
90
|
+
* inverse of `parseJSONPointer`. An empty array formats as the empty
|
|
91
|
+
* (whole-document) pointer.
|
|
92
|
+
* @param {(string|number)[]} segments - Decoded reference tokens
|
|
93
|
+
* @returns {string} The JSON Pointer
|
|
94
|
+
* @example
|
|
95
|
+
* formatJSONPointer(['a/b', 0]); // '/a~1b/0'
|
|
96
|
+
*/
|
|
97
|
+
export declare function formatJSONPointer(segments: (string | number)[]): string;
|
|
98
|
+
/**
|
|
99
|
+
* Parse a JSON Pointer into a path array, narrowing every canonical array
|
|
100
|
+
* index to a number and leaving all other tokens as decoded strings.
|
|
101
|
+
*
|
|
102
|
+
* RFC 6901 has no types: `/items/0/id` addresses element 0 of an array and
|
|
103
|
+
* member `"0"` of an object with the same token. This function resolves that
|
|
104
|
+
* lexically, not against a document — a token is a number when it is `0` or a
|
|
105
|
+
* digit sequence without leading zeros within the array-index range, which is
|
|
106
|
+
* the same rule the pointer compiler uses to pre-parse indexes. `"01"`,
|
|
107
|
+
* `"1e0"`, `"-"` and `"1abc"` therefore stay strings.
|
|
108
|
+
*
|
|
109
|
+
* The result is the `(string|number)[]` shape that error reporters and
|
|
110
|
+
* diffing tools use for data locations, so it pairs directly with a
|
|
111
|
+
* validation error's `instancePath`. `formatJSONPointer` is the inverse.
|
|
112
|
+
* @param {string} pointer - The JSON Pointer (e.g. `/store/book/0`)
|
|
113
|
+
* @returns {(string|number)[]} Decoded tokens with array indexes as numbers
|
|
114
|
+
* @throws {JSONPointerSyntaxError} When the pointer violates the grammar
|
|
115
|
+
* @example
|
|
116
|
+
* parseJSONPointerPath('/items/0/id'); // ['items', 0, 'id']
|
|
117
|
+
* parseJSONPointerPath('/items/01'); // ['items', '01']
|
|
118
|
+
* parseJSONPointerPath('/a~1b'); // ['a/b']
|
|
119
|
+
*/
|
|
120
|
+
export declare function parseJSONPointerPath(pointer: string): (string | number)[];
|
|
66
121
|
/**
|
|
67
122
|
* Compile a JSON Pointer (RFC 6901) into a reusable getter.
|
|
68
123
|
*
|
|
@@ -85,20 +140,51 @@ export declare function compileJSONPointer(pointer: string): JsonPointerGetter;
|
|
|
85
140
|
*
|
|
86
141
|
* The relative part (level count, `#` form, trailing segments) compiles
|
|
87
142
|
* once; per call only `dataPath` - the current location in `dataRoot` as
|
|
88
|
-
* an RFC 6901 pointer - varies.
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
143
|
+
* an RFC 6901 pointer - varies.
|
|
144
|
+
*
|
|
145
|
+
* The root has no name: `0#` there yields `JSONPOINTER_NOTHING`, not `''`,
|
|
146
|
+
* so it stays distinguishable from the member named `''` (`{"": 1}` at
|
|
147
|
+
* `/`), which is a name a document can genuinely have.
|
|
148
|
+
*
|
|
149
|
+
* ### The `#` form and `hashIndex`
|
|
150
|
+
*
|
|
151
|
+
* Relative JSON Pointer says `#` yields the member *name* for an object
|
|
152
|
+
* member and the *index* — a number — for an array element. Telling those
|
|
153
|
+
* apart requires looking at the container, so the two modes cost different
|
|
154
|
+
* things and you choose per compile:
|
|
155
|
+
*
|
|
156
|
+
* - `hashIndex: 'string'` (**default**) answers from `dataPath` alone and
|
|
157
|
+
* never touches `dataRoot`: an array position comes back as the string
|
|
158
|
+
* `'1'`. This is the historical behavior the validator's `$data` keyword
|
|
159
|
+
* relies on, and it is a string operation — tens of nanoseconds.
|
|
160
|
+
* - `hashIndex: 'number'` is the draft's answer. It walks to the parent of
|
|
161
|
+
* the location to see whether it is an array, and returns `1` rather than
|
|
162
|
+
* `'1'` when it is. Object member names are unaffected. When the parent
|
|
163
|
+
* cannot be reached (the location does not exist in `dataRoot`) it falls
|
|
164
|
+
* back to the string, because nothing proves the position is an index.
|
|
165
|
+
*
|
|
166
|
+
* Neither mode verifies that the location itself exists; the caller is
|
|
167
|
+
* expected to pass a location it actually reached. The non-`#` form must
|
|
168
|
+
* walk regardless, because it returns the value.
|
|
92
169
|
*
|
|
93
170
|
* @param {string} pointer - The relative pointer (e.g. `1/sibling`, `0#`)
|
|
171
|
+
* @param {{ hashIndex?: 'string'|'number' }} [options] - `hashIndex`
|
|
172
|
+
* selects what the `#` form yields for an array position (default
|
|
173
|
+
* `'string'`)
|
|
94
174
|
* @returns {RelativeJsonPointerResolver} resolver returning
|
|
95
175
|
* the addressed value, or `JSONPOINTER_NOTHING`
|
|
96
176
|
* @throws {JSONPointerSyntaxError} When the pointer is not valid
|
|
177
|
+
* @throws {TypeError} When `hashIndex` is neither `'string'` nor `'number'`
|
|
97
178
|
* @example
|
|
98
179
|
* const resolve = compileRelativeJSONPointer('1/limits');
|
|
99
180
|
* resolve({ limits: { min: 2 } , value: 5 }, '/value'); // { min: 2 }
|
|
181
|
+
* @example
|
|
182
|
+
* const spec = compileRelativeJSONPointer('0#', { hashIndex: 'number' });
|
|
183
|
+
* spec({ a: ['x', 'y'] }, '/a/1'); // 1 (the number, per the draft)
|
|
100
184
|
*/
|
|
101
|
-
export declare function compileRelativeJSONPointer(pointer: string
|
|
185
|
+
export declare function compileRelativeJSONPointer(pointer: string, options?: {
|
|
186
|
+
hashIndex?: 'string' | 'number';
|
|
187
|
+
}): RelativeJsonPointerResolver;
|
|
102
188
|
/**
|
|
103
189
|
* Compile a data reference - the accepted forms of the validator's
|
|
104
190
|
* `data`/`$data` keywords - into a reusable resolver. The dispatch is
|
|
@@ -106,9 +192,14 @@ export declare function compileRelativeJSONPointer(pointer: string): RelativeJso
|
|
|
106
192
|
* Pointer, a leading `/` an absolute JSON Pointer, and `''` the root.
|
|
107
193
|
*
|
|
108
194
|
* @param {string} ref - The reference string
|
|
195
|
+
* @param {{ hashIndex?: 'string'|'number' }} [options] - forwarded to
|
|
196
|
+
* {@link compileRelativeJSONPointer}; only the relative forms read it
|
|
109
197
|
* @returns {RelativeJsonPointerResolver} resolver returning
|
|
110
198
|
* the addressed value, or `JSONPOINTER_NOTHING`
|
|
111
199
|
* @throws {JSONPointerSyntaxError} When the reference is none of the
|
|
112
200
|
* accepted forms
|
|
201
|
+
* @throws {TypeError} When `hashIndex` is neither `'string'` nor `'number'`
|
|
113
202
|
*/
|
|
114
|
-
export declare function compileDataRef(ref: string
|
|
203
|
+
export declare function compileDataRef(ref: string, options?: {
|
|
204
|
+
hashIndex?: 'string' | 'number';
|
|
205
|
+
}): RelativeJsonPointerResolver;
|
|
@@ -13,6 +13,18 @@ export declare const UNBOUND: unique symbol;
|
|
|
13
13
|
* @returns {(frame: any[]) => boolean}
|
|
14
14
|
*/
|
|
15
15
|
export declare function compileExistsTest(node: object): (frame: any[]) => boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Compile a query's AST root, optionally instrumenting every node
|
|
18
|
+
* evaluation against a step limit.
|
|
19
|
+
* @param {object} root - the AST root from normalizeQuery
|
|
20
|
+
* @param {{ slot: number, limit: number } | null} steps - the step
|
|
21
|
+
* counter's frame slot and its limit, or null for no instrumentation
|
|
22
|
+
* @returns {(frame: any[]) => any} the root getter
|
|
23
|
+
*/
|
|
24
|
+
export declare function compileQueryRoot(root: object, steps: {
|
|
25
|
+
slot: number;
|
|
26
|
+
limit: number;
|
|
27
|
+
} | null): (frame: any[]) => any;
|
|
16
28
|
/**
|
|
17
29
|
* Compile a normalized AST node into its getter closure.
|
|
18
30
|
* @param {object} node - a frozen AST node from normalize.js
|