@jarenjs/json 0.9.2 → 0.34.2
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
package/src/pointer.js
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
//#region JSON Pointer (RFC 6901) + Relative JSON Pointer
|
|
2
2
|
// JSON Pointer: https://datatracker.ietf.org/doc/html/rfc6901
|
|
3
|
-
// Relative JSON Pointer:
|
|
4
|
-
//
|
|
3
|
+
// Relative JSON Pointer: draft-handrews-relative-json-pointer-01, the
|
|
4
|
+
// revision JSON Schema 2020-12 normatively references:
|
|
5
|
+
// https://datatracker.ietf.org/doc/html/draft-handrews-relative-json-pointer-01
|
|
6
|
+
//
|
|
7
|
+
// The newer draft-bhutton-relative-json-pointer-00 adds an index-
|
|
8
|
+
// manipulation form (`0+1`, `1-1`) that this grammar deliberately does not
|
|
9
|
+
// accept: the official format suite still asserts `+1/foo/bar` INVALID, so
|
|
10
|
+
// accepting it would have to arrive together with a dialect gate for the
|
|
11
|
+
// `relative-json-pointer` format tester.
|
|
5
12
|
//
|
|
6
13
|
// This module implements JSON Pointer as a two-stage compiler, mirroring
|
|
7
14
|
// the JSONPath engine in path.js:
|
|
8
15
|
//
|
|
9
16
|
// 1. `parseJSONPointer` / `parseRelativeJSONPointer` - strict, single-pass
|
|
10
|
-
// char-code parsers enforcing the full RFC 6901 /
|
|
17
|
+
// char-code parsers enforcing the full RFC 6901 / relative-pointer
|
|
18
|
+
// grammar.
|
|
11
19
|
// 2. `compileJSONPointer` / `compileRelativeJSONPointer` /
|
|
12
20
|
// `compileDataRef` - compile the parsed form into specialized getter
|
|
13
21
|
// closures. All decisions (member name decoding, array index parsing,
|
|
@@ -24,7 +32,8 @@ import {
|
|
|
24
32
|
isDigitCode,
|
|
25
33
|
} from '@jarenjs/core/scan';
|
|
26
34
|
|
|
27
|
-
import { NOTHING } from './segments.js';
|
|
35
|
+
import { NOTHING, scanArrayIndex } from './segments.js';
|
|
36
|
+
import { LabeledSyntaxError } from './errors.js';
|
|
28
37
|
|
|
29
38
|
/**
|
|
30
39
|
* Sentinel for the absence of a value, as distinct from the JSON value
|
|
@@ -39,12 +48,9 @@ const hasOwn = Object.hasOwn;
|
|
|
39
48
|
* Error thrown when a (relative) JSON Pointer is not valid RFC 6901 /
|
|
40
49
|
* draft-luff-relative-json-pointer syntax.
|
|
41
50
|
*/
|
|
42
|
-
export class JSONPointerSyntaxError extends
|
|
51
|
+
export class JSONPointerSyntaxError extends LabeledSyntaxError {
|
|
43
52
|
constructor(message, source, position) {
|
|
44
|
-
super(
|
|
45
|
-
this.name = 'JSONPointerSyntaxError';
|
|
46
|
-
this.source = source;
|
|
47
|
-
this.position = position;
|
|
53
|
+
super('JSONPointerSyntaxError', 'JSON Pointer', message, source, position);
|
|
48
54
|
}
|
|
49
55
|
}
|
|
50
56
|
|
|
@@ -171,38 +177,99 @@ export function parseRelativeJSONPointer(pointer) {
|
|
|
171
177
|
return { levels, hash: false, segments: scanSegments(pointer, pos) };
|
|
172
178
|
}
|
|
173
179
|
|
|
174
|
-
|
|
180
|
+
const RE_TILDE = /~/g;
|
|
181
|
+
const RE_SLASH = /\//g;
|
|
182
|
+
const RE_ESCAPED_SLASH = /~1/g;
|
|
183
|
+
const RE_ESCAPED_TILDE = /~0/g;
|
|
175
184
|
|
|
176
|
-
|
|
185
|
+
/**
|
|
186
|
+
* Encode a single reference token for use inside an RFC 6901 JSON
|
|
187
|
+
* Pointer: `~` becomes `~0` and `/` becomes `~1` (RFC 6901 section 3).
|
|
188
|
+
* The write-side inverse of the parser's decode; the escape-free common
|
|
189
|
+
* case returns the input unchanged.
|
|
190
|
+
* @param {string|number} segment - The raw member name or array index
|
|
191
|
+
* @returns {string} The encoded reference token
|
|
192
|
+
* @example
|
|
193
|
+
* encodeJSONPointerSegment('a/b'); // 'a~1b'
|
|
194
|
+
*/
|
|
195
|
+
export function encodeJSONPointerSegment(segment) {
|
|
196
|
+
const s = String(segment);
|
|
197
|
+
return (s.indexOf('~') < 0 && s.indexOf('/') < 0)
|
|
198
|
+
? s
|
|
199
|
+
: s.replace(RE_TILDE, '~0').replace(RE_SLASH, '~1');
|
|
200
|
+
}
|
|
177
201
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
202
|
+
/**
|
|
203
|
+
* Decode one RFC 6901 reference token: `~1` becomes `/` and `~0` becomes
|
|
204
|
+
* `~`. The order is normative (RFC 6901 section 4) and not an accident of
|
|
205
|
+
* implementation — decoding `~0` first would turn `~01` into `~1` and then
|
|
206
|
+
* into `/`, so a member literally named `~1` would come back as `/`.
|
|
207
|
+
* The escape-free common case returns the input unchanged.
|
|
208
|
+
* @param {string} token - One encoded reference token (no `/` separators)
|
|
209
|
+
* @returns {string} The decoded member name
|
|
210
|
+
* @example
|
|
211
|
+
* decodeJSONPointerSegment('a~1b'); // 'a/b'
|
|
212
|
+
*/
|
|
213
|
+
export function decodeJSONPointerSegment(token) {
|
|
214
|
+
return token.indexOf('~') < 0
|
|
215
|
+
? token
|
|
216
|
+
: token.replace(RE_ESCAPED_SLASH, '/').replace(RE_ESCAPED_TILDE, '~');
|
|
217
|
+
}
|
|
181
218
|
|
|
182
219
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
220
|
+
* Format decoded reference tokens as an RFC 6901 JSON Pointer; the
|
|
221
|
+
* inverse of `parseJSONPointer`. An empty array formats as the empty
|
|
222
|
+
* (whole-document) pointer.
|
|
223
|
+
* @param {(string|number)[]} segments - Decoded reference tokens
|
|
224
|
+
* @returns {string} The JSON Pointer
|
|
225
|
+
* @example
|
|
226
|
+
* formatJSONPointer(['a/b', 0]); // '/a~1b/0'
|
|
186
227
|
*/
|
|
187
|
-
function
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
228
|
+
export function formatJSONPointer(segments) {
|
|
229
|
+
let out = '';
|
|
230
|
+
for (let i = 0; i < segments.length; i++)
|
|
231
|
+
out += '/' + encodeJSONPointerSegment(segments[i]);
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Parse a JSON Pointer into a path array, narrowing every canonical array
|
|
237
|
+
* index to a number and leaving all other tokens as decoded strings.
|
|
238
|
+
*
|
|
239
|
+
* RFC 6901 has no types: `/items/0/id` addresses element 0 of an array and
|
|
240
|
+
* member `"0"` of an object with the same token. This function resolves that
|
|
241
|
+
* lexically, not against a document — a token is a number when it is `0` or a
|
|
242
|
+
* digit sequence without leading zeros within the array-index range, which is
|
|
243
|
+
* the same rule the pointer compiler uses to pre-parse indexes. `"01"`,
|
|
244
|
+
* `"1e0"`, `"-"` and `"1abc"` therefore stay strings.
|
|
245
|
+
*
|
|
246
|
+
* The result is the `(string|number)[]` shape that error reporters and
|
|
247
|
+
* diffing tools use for data locations, so it pairs directly with a
|
|
248
|
+
* validation error's `instancePath`. `formatJSONPointer` is the inverse.
|
|
249
|
+
* @param {string} pointer - The JSON Pointer (e.g. `/store/book/0`)
|
|
250
|
+
* @returns {(string|number)[]} Decoded tokens with array indexes as numbers
|
|
251
|
+
* @throws {JSONPointerSyntaxError} When the pointer violates the grammar
|
|
252
|
+
* @example
|
|
253
|
+
* parseJSONPointerPath('/items/0/id'); // ['items', 0, 'id']
|
|
254
|
+
* parseJSONPointerPath('/items/01'); // ['items', '01']
|
|
255
|
+
* parseJSONPointerPath('/a~1b'); // ['a/b']
|
|
256
|
+
*/
|
|
257
|
+
export function parseJSONPointerPath(pointer) {
|
|
258
|
+
const segments = parseJSONPointer(pointer);
|
|
259
|
+
/** @type {(string|number)[]} */
|
|
260
|
+
const path = segments;
|
|
261
|
+
for (let i = 0; i < segments.length; i++) {
|
|
262
|
+
const token = segments[i];
|
|
263
|
+
const index = scanArrayIndex(token, 0, token.length);
|
|
264
|
+
if (index !== -1) path[i] = index;
|
|
202
265
|
}
|
|
203
|
-
return
|
|
266
|
+
return path;
|
|
204
267
|
}
|
|
205
268
|
|
|
269
|
+
//#endregion
|
|
270
|
+
|
|
271
|
+
//#region compiler
|
|
272
|
+
|
|
206
273
|
/**
|
|
207
274
|
* One pointer hop: an array is addressed by the pre-parsed index, an
|
|
208
275
|
* object by the pre-decoded member name (own properties only), anything
|
|
@@ -247,6 +314,28 @@ function compileSegmentsGetter(segments) {
|
|
|
247
314
|
return v === NOTHING ? NOTHING : hop(v, name1, index1);
|
|
248
315
|
};
|
|
249
316
|
}
|
|
317
|
+
const name2 = segments[2];
|
|
318
|
+
const index2 = scanArrayIndex(name2, 0, name2.length);
|
|
319
|
+
if (slen === 3) {
|
|
320
|
+
return function pointerGetter3(root) {
|
|
321
|
+
const v0 = hop(root, name0, index0);
|
|
322
|
+
if (v0 === NOTHING) return NOTHING;
|
|
323
|
+
const v1 = hop(v0, name1, index1);
|
|
324
|
+
return v1 === NOTHING ? NOTHING : hop(v1, name2, index2);
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
const name3 = segments[3];
|
|
328
|
+
const index3 = scanArrayIndex(name3, 0, name3.length);
|
|
329
|
+
if (slen === 4) {
|
|
330
|
+
return function pointerGetter4(root) {
|
|
331
|
+
const v0 = hop(root, name0, index0);
|
|
332
|
+
if (v0 === NOTHING) return NOTHING;
|
|
333
|
+
const v1 = hop(v0, name1, index1);
|
|
334
|
+
if (v1 === NOTHING) return NOTHING;
|
|
335
|
+
const v2 = hop(v1, name2, index2);
|
|
336
|
+
return v2 === NOTHING ? NOTHING : hop(v2, name3, index3);
|
|
337
|
+
};
|
|
338
|
+
}
|
|
250
339
|
const names = segments;
|
|
251
340
|
const indexes = new Array(slen);
|
|
252
341
|
for (let i = 0; i < slen; i++)
|
|
@@ -326,11 +415,15 @@ function decodeSegmentRange(source, start, end, tilde) {
|
|
|
326
415
|
/**
|
|
327
416
|
* The last segment of `dataPath.slice(0, end)`, decoded lazily: the
|
|
328
417
|
* common escape-free case allocates nothing beyond the result slice.
|
|
329
|
-
*
|
|
418
|
+
*
|
|
419
|
+
* The root (`end === 0`) has no name, so it yields NOTHING rather than
|
|
420
|
+
* `''` — `''` is a member name a document can genuinely have (`{"": 1}`
|
|
421
|
+
* at `/`), and returning it for the root too would make the two
|
|
422
|
+
* indistinguishable to the caller.
|
|
330
423
|
*/
|
|
331
424
|
function lastSegmentOf(dataPath, end) {
|
|
332
425
|
if (end === 0)
|
|
333
|
-
return
|
|
426
|
+
return NOTHING;
|
|
334
427
|
const start = dataPath.lastIndexOf('/', end - 1) + 1;
|
|
335
428
|
for (let i = start; i < end; i++) {
|
|
336
429
|
if (dataPath.charCodeAt(i) === CC_TILDE)
|
|
@@ -339,6 +432,34 @@ function lastSegmentOf(dataPath, end) {
|
|
|
339
432
|
return dataPath.slice(start, end);
|
|
340
433
|
}
|
|
341
434
|
|
|
435
|
+
/**
|
|
436
|
+
* Read and validate the `hashIndex` compile option, defaulting to the
|
|
437
|
+
* historical `'string'`. An unknown value is rejected rather than ignored:
|
|
438
|
+
* silently falling back would hand a caller who meant `'number'` the exact
|
|
439
|
+
* behavior they were opting out of.
|
|
440
|
+
* @param {{ hashIndex?: string }} [options]
|
|
441
|
+
* @returns {'string'|'number'}
|
|
442
|
+
*/
|
|
443
|
+
function readHashIndexOption(options) {
|
|
444
|
+
if (options === undefined || options === null)
|
|
445
|
+
return 'string';
|
|
446
|
+
const mode = options.hashIndex;
|
|
447
|
+
if (mode === undefined || mode === 'string')
|
|
448
|
+
return 'string';
|
|
449
|
+
if (mode === 'number')
|
|
450
|
+
return 'number';
|
|
451
|
+
throw new TypeError(
|
|
452
|
+
`hashIndex must be 'string' or 'number', got ${JSON.stringify(mode)}`);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* The exclusive end of the *parent* of the location `dataPath.slice(0, end)`,
|
|
457
|
+
* or -1 when that location is the root and so has no parent.
|
|
458
|
+
*/
|
|
459
|
+
function parentEndOf(dataPath, end) {
|
|
460
|
+
return end === 0 ? -1 : dataPath.lastIndexOf('/', end - 1);
|
|
461
|
+
}
|
|
462
|
+
|
|
342
463
|
/**
|
|
343
464
|
* Walk `root` along the location path prefix `path.slice(0, end)`.
|
|
344
465
|
* Segments are decoded lazily per hop (escape-free segments are sliced
|
|
@@ -382,23 +503,65 @@ function walkPointerPrefix(root, path, end) {
|
|
|
382
503
|
*
|
|
383
504
|
* The relative part (level count, `#` form, trailing segments) compiles
|
|
384
505
|
* once; per call only `dataPath` - the current location in `dataRoot` as
|
|
385
|
-
* an RFC 6901 pointer - varies.
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
*
|
|
506
|
+
* an RFC 6901 pointer - varies.
|
|
507
|
+
*
|
|
508
|
+
* The root has no name: `0#` there yields `JSONPOINTER_NOTHING`, not `''`,
|
|
509
|
+
* so it stays distinguishable from the member named `''` (`{"": 1}` at
|
|
510
|
+
* `/`), which is a name a document can genuinely have.
|
|
511
|
+
*
|
|
512
|
+
* ### The `#` form and `hashIndex`
|
|
513
|
+
*
|
|
514
|
+
* Relative JSON Pointer says `#` yields the member *name* for an object
|
|
515
|
+
* member and the *index* — a number — for an array element. Telling those
|
|
516
|
+
* apart requires looking at the container, so the two modes cost different
|
|
517
|
+
* things and you choose per compile:
|
|
518
|
+
*
|
|
519
|
+
* - `hashIndex: 'string'` (**default**) answers from `dataPath` alone and
|
|
520
|
+
* never touches `dataRoot`: an array position comes back as the string
|
|
521
|
+
* `'1'`. This is the historical behavior the validator's `$data` keyword
|
|
522
|
+
* relies on, and it is a string operation — tens of nanoseconds.
|
|
523
|
+
* - `hashIndex: 'number'` is the draft's answer. It walks to the parent of
|
|
524
|
+
* the location to see whether it is an array, and returns `1` rather than
|
|
525
|
+
* `'1'` when it is. Object member names are unaffected. When the parent
|
|
526
|
+
* cannot be reached (the location does not exist in `dataRoot`) it falls
|
|
527
|
+
* back to the string, because nothing proves the position is an index.
|
|
528
|
+
*
|
|
529
|
+
* Neither mode verifies that the location itself exists; the caller is
|
|
530
|
+
* expected to pass a location it actually reached. The non-`#` form must
|
|
531
|
+
* walk regardless, because it returns the value.
|
|
389
532
|
*
|
|
390
533
|
* @param {string} pointer - The relative pointer (e.g. `1/sibling`, `0#`)
|
|
534
|
+
* @param {{ hashIndex?: 'string'|'number' }} [options] - `hashIndex`
|
|
535
|
+
* selects what the `#` form yields for an array position (default
|
|
536
|
+
* `'string'`)
|
|
391
537
|
* @returns {RelativeJsonPointerResolver} resolver returning
|
|
392
538
|
* the addressed value, or `JSONPOINTER_NOTHING`
|
|
393
539
|
* @throws {JSONPointerSyntaxError} When the pointer is not valid
|
|
540
|
+
* @throws {TypeError} When `hashIndex` is neither `'string'` nor `'number'`
|
|
394
541
|
* @example
|
|
395
542
|
* const resolve = compileRelativeJSONPointer('1/limits');
|
|
396
543
|
* resolve({ limits: { min: 2 } , value: 5 }, '/value'); // { min: 2 }
|
|
544
|
+
* @example
|
|
545
|
+
* const spec = compileRelativeJSONPointer('0#', { hashIndex: 'number' });
|
|
546
|
+
* spec({ a: ['x', 'y'] }, '/a/1'); // 1 (the number, per the draft)
|
|
397
547
|
*/
|
|
398
|
-
export function compileRelativeJSONPointer(pointer) {
|
|
548
|
+
export function compileRelativeJSONPointer(pointer, options = undefined) {
|
|
399
549
|
const { levels, hash, segments } = parseRelativeJSONPointer(pointer);
|
|
550
|
+
const numericHash = readHashIndexOption(options) === 'number';
|
|
400
551
|
if (hash) {
|
|
401
|
-
|
|
552
|
+
if (!numericHash) {
|
|
553
|
+
return function relativeHashResolver(dataRoot, dataPath) {
|
|
554
|
+
if (typeof dataPath !== 'string')
|
|
555
|
+
dataPath = '';
|
|
556
|
+
else if (dataPath.length !== 0 && dataPath.charCodeAt(0) !== CC_SLASH)
|
|
557
|
+
return NOTHING;
|
|
558
|
+
const end = trimLevels(dataPath, levels);
|
|
559
|
+
if (end < 0)
|
|
560
|
+
return NOTHING;
|
|
561
|
+
return lastSegmentOf(dataPath, end);
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
return function relativeHashIndexResolver(dataRoot, dataPath) {
|
|
402
565
|
if (typeof dataPath !== 'string')
|
|
403
566
|
dataPath = '';
|
|
404
567
|
else if (dataPath.length !== 0 && dataPath.charCodeAt(0) !== CC_SLASH)
|
|
@@ -406,7 +569,19 @@ export function compileRelativeJSONPointer(pointer) {
|
|
|
406
569
|
const end = trimLevels(dataPath, levels);
|
|
407
570
|
if (end < 0)
|
|
408
571
|
return NOTHING;
|
|
409
|
-
|
|
572
|
+
const name = lastSegmentOf(dataPath, end);
|
|
573
|
+
if (name === NOTHING)
|
|
574
|
+
return NOTHING;
|
|
575
|
+
// An index is only an index when its container is an array; the name
|
|
576
|
+
// of a `{"1": …}` member is the string "1" in every mode.
|
|
577
|
+
const parentEnd = parentEndOf(dataPath, end);
|
|
578
|
+
if (parentEnd < 0)
|
|
579
|
+
return name;
|
|
580
|
+
const parent = walkPointerPrefix(dataRoot, dataPath, parentEnd);
|
|
581
|
+
if (!Array.isArray(parent))
|
|
582
|
+
return name;
|
|
583
|
+
const index = scanArrayIndex(name, 0, name.length);
|
|
584
|
+
return index < 0 ? name : index;
|
|
410
585
|
};
|
|
411
586
|
}
|
|
412
587
|
const getter = compileSegmentsGetter(segments);
|
|
@@ -430,19 +605,25 @@ export function compileRelativeJSONPointer(pointer) {
|
|
|
430
605
|
* Pointer, a leading `/` an absolute JSON Pointer, and `''` the root.
|
|
431
606
|
*
|
|
432
607
|
* @param {string} ref - The reference string
|
|
608
|
+
* @param {{ hashIndex?: 'string'|'number' }} [options] - forwarded to
|
|
609
|
+
* {@link compileRelativeJSONPointer}; only the relative forms read it
|
|
433
610
|
* @returns {RelativeJsonPointerResolver} resolver returning
|
|
434
611
|
* the addressed value, or `JSONPOINTER_NOTHING`
|
|
435
612
|
* @throws {JSONPointerSyntaxError} When the reference is none of the
|
|
436
613
|
* accepted forms
|
|
614
|
+
* @throws {TypeError} When `hashIndex` is neither `'string'` nor `'number'`
|
|
437
615
|
*/
|
|
438
|
-
export function compileDataRef(ref) {
|
|
616
|
+
export function compileDataRef(ref, options = undefined) {
|
|
439
617
|
if (typeof ref !== 'string')
|
|
440
618
|
throw new JSONPointerSyntaxError('a data reference must be a string', String(ref), 0);
|
|
619
|
+
// Validate the option even on the forms that ignore it, so a typo is a
|
|
620
|
+
// compile-time error wherever it appears rather than only on `N#` refs.
|
|
621
|
+
readHashIndexOption(options);
|
|
441
622
|
if (ref.length === 0)
|
|
442
623
|
return getRoot;
|
|
443
624
|
const c = ref.charCodeAt(0);
|
|
444
625
|
if (isDigitCode(c))
|
|
445
|
-
return compileRelativeJSONPointer(ref);
|
|
626
|
+
return compileRelativeJSONPointer(ref, options);
|
|
446
627
|
if (c === CC_SLASH)
|
|
447
628
|
return compileSegmentsGetter(scanSegments(ref, 0));
|
|
448
629
|
throw new JSONPointerSyntaxError('a data reference must be empty, a JSON Pointer or a Relative JSON Pointer', ref, 0);
|