@swaggerexpert/jsonpath 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/README.md ADDED
@@ -0,0 +1,546 @@
1
+ # @swaggerexpert/jsonpath
2
+
3
+ [![npmversion](https://img.shields.io/npm/v/%40swaggerexpert%2Fjsonpath?style=flat-square&label=npm%20package&color=%234DC81F&link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F%40swaggerexpert%2Fjsonpath)](https://www.npmjs.com/package/@swaggerexpert/jsonpath)
4
+ [![npm](https://img.shields.io/npm/dm/@swaggerexpert/jsonpath)](https://www.npmjs.com/package/@swaggerexpert/jsonpath)
5
+ [![Test workflow](https://github.com/swaggerexpert/jsonpath/actions/workflows/test.yml/badge.svg)](https://github.com/swaggerexpert/jsonpath/actions)
6
+ [![Dependabot enabled](https://img.shields.io/badge/Dependabot-enabled-blue.svg)](https://dependabot.com/)
7
+ [![try on RunKit](https://img.shields.io/badge/try%20on-RunKit-brightgreen.svg?style=flat)](https://npm.runkit.com/@swaggerexpert/jsonpath)
8
+ [![Tidelift](https://tidelift.com/badges/package/npm/@swaggerexpert%2Fjsonpath)](https://tidelift.com/subscription/pkg/npm-.swaggerexpert-jsonpath?utm_source=npm-swaggerexpert-jsonpath&utm_medium=referral&utm_campaign=readme)
9
+
10
+ `@swaggerexpert/jsonpath` is a **parser** and **validator** for [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535) Query Expressions for JSON - **JSONPath**.
11
+
12
+ The development of this library contributed to the identification and formal submission of [Errata 8343](https://www.rfc-editor.org/errata/eid8343) against RFC 9535.
13
+
14
+ <table>
15
+ <tr>
16
+ <td align="right" valign="middle">
17
+ <img src="https://cdn2.hubspot.net/hubfs/4008838/website/logos/logos_for_download/Tidelift_primary-shorthand-logo.png" alt="Tidelift" width="60" />
18
+ </td>
19
+ <td valign="middle">
20
+ <a href="https://tidelift.com/subscription/pkg/npm-.swaggerexpert-jsonpath?utm_source=npm-swaggerexpert-jsonpath&utm_medium=referral&utm_campaign=readme">
21
+ Get professionally supported @swaggerexpert/jsonpath with Tidelift Subscription.
22
+ </a>
23
+ </td>
24
+ </tr>
25
+ </table>
26
+
27
+ ## Table of Contents
28
+
29
+ - [Getting started](#getting-started)
30
+ - [Installation](#installation)
31
+ - [Usage](#usage)
32
+ - [Parsing](#parsing)
33
+ - [Errors](#errors)
34
+ - [Grammar](#grammar)
35
+ - [More about JSONPath](#more-about-jsonpath)
36
+ - [License](#license)
37
+
38
+ ## Getting started
39
+
40
+ ### Installation
41
+
42
+ You can install `@swaggerexpert/jsonpath` using `npm`:
43
+
44
+ ```sh
45
+ $ npm install @swaggerexpert/jsonpath
46
+ ```
47
+
48
+ ### Usage
49
+
50
+ `@swaggerexpert/jsonpath` currently supports **parsing** and **validation**.
51
+ Both parser and validator are based on a superset of [ABNF](https://www.rfc-editor.org/rfc/rfc5234) ([SABNF](https://cs.github.com/ldthomas/apg-js2/blob/master/SABNF.md))
52
+ and use [apg-lite](https://github.com/ldthomas/apg-lite) parser generator.
53
+
54
+ #### Parsing
55
+
56
+ Parsing a JSONPath Query expression is as simple as importing the **parse** function and calling it.
57
+
58
+ ```js
59
+ import { parse } from '@swaggerexpert/jsonpath';
60
+
61
+ const parseResult = parse('$.store.book[0].title');
62
+ ```
63
+
64
+ or
65
+
66
+ ```js
67
+ import { parse, JSONPathQueryCST } from '@swaggerexpert/jsonpath';
68
+
69
+ const parseResult = parse('$.store.book[0].title', { ast: new JSONPathQueryCST() });
70
+ ```
71
+
72
+ **parseResult** variable has the following shape:
73
+
74
+ ```
75
+ {
76
+ result: {
77
+ success: true,
78
+ state: 101,
79
+ stateName: 'MATCH',
80
+ length: 21,
81
+ matched: 21,
82
+ maxMatched: 21,
83
+ maxTreeDepth: 21,
84
+ nodeHits: 298
85
+ },
86
+ ast: <JSONPathQueryCST>,
87
+ computed: {
88
+ stack: [],
89
+ root: {
90
+ type: 'jsonpath-query',
91
+ text: '$.store.book[0].title',
92
+ start: 0,
93
+ length: 21,
94
+ children: [
95
+ {
96
+ type: 'root-identifier',
97
+ text: '$',
98
+ start: 0,
99
+ length: 1,
100
+ children: []
101
+ },
102
+ {
103
+ type: 'segments',
104
+ text: '.store.book[0].title',
105
+ start: 1,
106
+ length: 20,
107
+ children: [
108
+ {
109
+ type: 'segment',
110
+ text: '.store',
111
+ start: 1,
112
+ length: 6,
113
+ children: [
114
+ {
115
+ type: 'child-segment',
116
+ text: '.store',
117
+ start: 1,
118
+ length: 6,
119
+ children: [
120
+ {
121
+ type: 'text',
122
+ text: '.',
123
+ start: 1,
124
+ length: 1,
125
+ children: []
126
+ },
127
+ {
128
+ type: 'member-name-shorthand',
129
+ text: 'store',
130
+ start: 2,
131
+ length: 5,
132
+ children: []
133
+ }
134
+ ]
135
+ }
136
+ ]
137
+ },
138
+ {
139
+ type: 'segment',
140
+ text: '.book',
141
+ start: 7,
142
+ length: 5,
143
+ children: [
144
+ {
145
+ type: 'child-segment',
146
+ text: '.book',
147
+ start: 7,
148
+ length: 5,
149
+ children: [
150
+ {
151
+ type: 'text',
152
+ text: '.',
153
+ start: 7,
154
+ length: 1,
155
+ children: []
156
+ },
157
+ {
158
+ type: 'member-name-shorthand',
159
+ text: 'book',
160
+ start: 8,
161
+ length: 4,
162
+ children: []
163
+ }
164
+ ]
165
+ }
166
+ ]
167
+ },
168
+ {
169
+ type: 'segment',
170
+ text: '[0]',
171
+ start: 12,
172
+ length: 3,
173
+ children: [
174
+ {
175
+ type: 'child-segment',
176
+ text: '[0]',
177
+ start: 12,
178
+ length: 3,
179
+ children: [
180
+ {
181
+ type: 'bracketed-selection',
182
+ text: '[0]',
183
+ start: 12,
184
+ length: 3,
185
+ children: [
186
+ {
187
+ type: 'text',
188
+ text: '[',
189
+ start: 12,
190
+ length: 1,
191
+ children: []
192
+ },
193
+ {
194
+ type: 'selector',
195
+ text: '0',
196
+ start: 13,
197
+ length: 1,
198
+ children: [
199
+ {
200
+ type: 'index-selector',
201
+ text: '0',
202
+ start: 13,
203
+ length: 1,
204
+ children: []
205
+ }
206
+ ]
207
+ },
208
+ {
209
+ type: 'text',
210
+ text: ']',
211
+ start: 14,
212
+ length: 1,
213
+ children: []
214
+ }
215
+ ]
216
+ }
217
+ ]
218
+ }
219
+ ]
220
+ },
221
+ {
222
+ type: 'segment',
223
+ text: '.title',
224
+ start: 15,
225
+ length: 6,
226
+ children: [
227
+ {
228
+ type: 'child-segment',
229
+ text: '.title',
230
+ start: 15,
231
+ length: 6,
232
+ children: [
233
+ {
234
+ type: 'text',
235
+ text: '.',
236
+ start: 15,
237
+ length: 1,
238
+ children: []
239
+ },
240
+ {
241
+ type: 'member-name-shorthand',
242
+ text: 'title',
243
+ start: 16,
244
+ length: 5,
245
+ children: []
246
+ }
247
+ ]
248
+ }
249
+ ]
250
+ }
251
+ ]
252
+ }
253
+ ]
254
+ }
255
+ }
256
+ }
257
+ ```
258
+
259
+ ###### Interpreting AST as XML
260
+
261
+ ```js
262
+ import { parse } from '@swaggerexpert/jsonpath';
263
+
264
+ const parseResult = parse('$.store.book[0].title');
265
+ const xml = parseResult.ast.toXml();
266
+ ```
267
+
268
+ #### Errors
269
+
270
+ `@swaggerexpert/jsonpath` provides a structured error class hierarchy,
271
+ enabling precise error handling across JSONPath operations, including parsing.
272
+
273
+ ```js
274
+ import { JSONPathError, JSONPathParseError } from '@swaggerexpert/jsonpath';
275
+ ```
276
+
277
+ **JSONPathError** is the base class for all JSONPath errors.
278
+
279
+ #### Grammar
280
+
281
+ New grammar instance can be created in following way:
282
+
283
+ ```js
284
+ import { Grammar } from '@swaggerexpert/jsonpath';
285
+
286
+ const grammar = new Grammar();
287
+ ```
288
+
289
+ To obtain original ABNF (SABNF) grammar as a string:
290
+
291
+ ```js
292
+ import { Grammar } from '@swaggerexpert/jsonpath';
293
+
294
+ const grammar = new Grammar();
295
+
296
+ grammar.toString();
297
+ // or
298
+ String(grammar);
299
+ ```
300
+
301
+ ## More about JSONPath
302
+
303
+ JSONPath is defined by the following [ABNF](https://tools.ietf.org/html/rfc5234) syntax
304
+
305
+ [comment]: <> (SPDX-FileCopyrightText: Copyright &#40;c&#41; 2024 IETF Trust and the persons identified as the document authors. All rights reserved.)
306
+ [comment]: <> (SPDX-License-Identifier: BSD-3-Clause)
307
+
308
+ ```abnf
309
+ ; JSONPath: Query Expressions for JSON
310
+ ; https://www.rfc-editor.org/rfc/rfc9535
311
+
312
+ ; https://www.rfc-editor.org/rfc/rfc9535#section-2.1.1
313
+ jsonpath-query = root-identifier segments
314
+ segments = *(S segment)
315
+
316
+ B = %x20 / ; Space
317
+ %x09 / ; Horizontal tab
318
+ %x0A / ; Line feed or New line
319
+ %x0D ; Carriage return
320
+ S = *B ; optional blank space
321
+
322
+ ; https://www.rfc-editor.org/rfc/rfc9535#section-2.2.1
323
+ root-identifier = "$"
324
+
325
+ ; https://www.rfc-editor.org/rfc/rfc9535#section-2.3
326
+ selector = name-selector /
327
+ wildcard-selector /
328
+ slice-selector /
329
+ index-selector /
330
+ filter-selector
331
+
332
+ ; https://www.rfc-editor.org/rfc/rfc9535#section-2.3.1.1
333
+ name-selector = string-literal
334
+
335
+ string-literal = dquote *double-quoted dquote / ; "string", MODIFICATION: surrogate text rule used
336
+ squote *single-quoted squote ; 'string', MODIFICATION: surrogate text rule used
337
+
338
+ double-quoted = unescaped /
339
+ %x27 / ; '
340
+ ESC %x22 / ; \"
341
+ ESC escapable
342
+
343
+ single-quoted = unescaped /
344
+ %x22 / ; "
345
+ ESC %x27 / ; \'
346
+ ESC escapable
347
+
348
+ ESC = %x5C ; \ backslash
349
+
350
+ unescaped = %x20-21 / ; see RFC 8259
351
+ ; omit 0x22 "
352
+ %x23-26 /
353
+ ; omit 0x27 '
354
+ %x28-5B /
355
+ ; omit 0x5C \
356
+ %x5D-D7FF /
357
+ ; skip surrogate code points
358
+ %xE000-10FFFF
359
+
360
+ escapable = %x62 / ; b BS backspace U+0008
361
+ %x66 / ; f FF form feed U+000C
362
+ %x6E / ; n LF line feed U+000A
363
+ %x72 / ; r CR carriage return U+000D
364
+ %x74 / ; t HT horizontal tab U+0009
365
+ "/" / ; / slash (solidus) U+002F
366
+ "\" / ; \ backslash (reverse solidus) U+005C
367
+ (%x75 hexchar) ; uXXXX U+XXXX
368
+
369
+ hexchar = non-surrogate /
370
+ (high-surrogate "\" %x75 low-surrogate)
371
+ non-surrogate = ((DIGIT / "A"/"B"/"C" / "E"/"F") 3HEXDIG) /
372
+ ("D" %x30-37 2HEXDIG )
373
+ high-surrogate = "D" ("8"/"9"/"A"/"B") 2HEXDIG
374
+ low-surrogate = "D" ("C"/"D"/"E"/"F") 2HEXDIG
375
+
376
+ HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
377
+
378
+ ; https://www.rfc-editor.org/rfc/rfc9535#section-2.3.2.1
379
+ wildcard-selector = "*"
380
+
381
+ ; https://www.rfc-editor.org/rfc/rfc9535#section-2.3.3.1
382
+ index-selector = int ; decimal integer
383
+
384
+ int = "0" /
385
+ (["-"] DIGIT1 *DIGIT) ; - optional
386
+ DIGIT1 = %x31-39 ; 1-9 non-zero digit
387
+
388
+ ; https://www.rfc-editor.org/rfc/rfc9535#section-2.3.4.1
389
+ slice-selector = [start S] colon S [end S] [colon [S step ]] ; MODIFICATION: surrogate text rule used
390
+
391
+ start = int ; included in selection
392
+ end = int ; not included in selection
393
+ step = int ; default: 1
394
+
395
+ ; https://www.rfc-editor.org/rfc/rfc9535#section-2.3.5.1
396
+ filter-selector = questionmark S logical-expr ; MODIFICATION: surrogate text rule used
397
+
398
+ logical-expr = logical-or-expr
399
+ logical-or-expr = logical-and-expr *(S disjunction S logical-and-expr) ; MODIFICATION: surrogate text rule used
400
+ ; disjunction
401
+ ; binds less tightly than conjunction
402
+ logical-and-expr = basic-expr *(S conjunction S basic-expr) ; MODIFICATION: surrogate text rule used
403
+ ; conjunction
404
+ ; binds more tightly than disjunction
405
+
406
+ basic-expr = paren-expr /
407
+ comparison-expr /
408
+ test-expr
409
+
410
+ paren-expr = [logical-not-op S] left-paren S logical-expr S right-paren ; MODIFICATION: surrogate text rule used
411
+ ; parenthesized expression
412
+ logical-not-op = "!" ; logical NOT operator
413
+
414
+ test-expr = [logical-not-op S]
415
+ (filter-query / ; existence/non-existence
416
+ function-expr) ; LogicalType or NodesType
417
+ filter-query = rel-query / jsonpath-query
418
+ rel-query = current-node-identifier segments
419
+ current-node-identifier = "@"
420
+
421
+ comparison-expr = comparable S comparison-op S comparable
422
+ literal = number / string-literal /
423
+ true / false / null
424
+ comparable = literal /
425
+ singular-query / ; singular query value
426
+ function-expr ; ValueType
427
+ comparison-op = "==" / "!=" /
428
+ "<=" / ">=" /
429
+ "<" / ">"
430
+
431
+ singular-query = rel-singular-query / abs-singular-query
432
+ rel-singular-query = current-node-identifier singular-query-segments
433
+ abs-singular-query = root-identifier singular-query-segments
434
+ singular-query-segments = *(S (name-segment / index-segment))
435
+ name-segment = (left-bracket name-selector right-bracket) / ; MODIFICATION: surrogate text rule used
436
+ (dot-prefix member-name-shorthand) ; MODIFICATION: surrogate text rule used
437
+ index-segment = left-bracket index-selector right-bracket ; MODIFICATION: surrogate text rule used
438
+
439
+ number = (int / "-0") [ frac ] [ exp ] ; decimal number
440
+ frac = "." 1*DIGIT ; decimal fraction
441
+ exp = "e" [ "-" / "+" ] 1*DIGIT ; decimal exponent
442
+ true = %x74.72.75.65 ; true
443
+ false = %x66.61.6c.73.65 ; false
444
+ null = %x6e.75.6c.6c ; null
445
+
446
+ ; https://www.rfc-editor.org/rfc/rfc9535#section-2.4
447
+ function-name = function-name-first *function-name-char
448
+ function-name-first = LCALPHA
449
+ function-name-char = function-name-first / "_" / DIGIT
450
+ LCALPHA = %x61-7A ; "a".."z"
451
+
452
+ function-expr = function-name left-paren S [function-argument ; MODIFICATION: surrogate text rule used
453
+ *(S comma S function-argument)] S right-paren ; MODIFICATION: surrogate text rule used
454
+ function-argument = logical-expr / ; MODIFICATION: PEG-ordered
455
+ function-expr /
456
+ filter-query / ; (includes singular-query)
457
+ literal
458
+
459
+
460
+ ; https://www.rfc-editor.org/rfc/rfc9535#section-2.5
461
+ segment = child-segment / descendant-segment
462
+
463
+ ; https://www.rfc-editor.org/rfc/rfc9535#section-2.5.1.1
464
+ child-segment = bracketed-selection /
465
+ (dot-prefix ; MODIFICATION: surrogate text rule used
466
+ (wildcard-selector /
467
+ member-name-shorthand))
468
+
469
+ bracketed-selection = left-bracket S selector *(S comma S selector) S right-bracket
470
+ ; MODIFICATION: surrogate text rule used
471
+
472
+ member-name-shorthand = name-first *name-char
473
+ name-first = ALPHA /
474
+ "_" /
475
+ %x80-D7FF /
476
+ ; skip surrogate code points
477
+ %xE000-10FFFF
478
+ name-char = name-first / DIGIT
479
+
480
+ DIGIT = %x30-39 ; 0-9
481
+ ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
482
+
483
+ ; https://www.rfc-editor.org/rfc/rfc9535#section-2.5.2.1
484
+ descendant-segment = double-dot-prefix (bracketed-selection / ; MODIFICATION: surrogate text rule used
485
+ wildcard-selector /
486
+ member-name-shorthand)
487
+
488
+ ; https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths
489
+ normalized-path = root-identifier *(normal-index-segment)
490
+ normal-index-segment = "[" normal-selector "]"
491
+ normal-selector = normal-name-selector / normal-index-selector
492
+ normal-name-selector = %x27 *normal-single-quoted %x27 ; 'string'
493
+ normal-single-quoted = normal-unescaped /
494
+ ESC normal-escapable
495
+ normal-unescaped = ; omit %x0-1F control codes
496
+ %x20-26 /
497
+ ; omit 0x27 '
498
+ %x28-5B /
499
+ ; omit 0x5C \
500
+ %x5D-D7FF /
501
+ ; skip surrogate code points
502
+ %xE000-10FFFF
503
+
504
+ normal-escapable = %x62 / ; b BS backspace U+0008
505
+ %x66 / ; f FF form feed U+000C
506
+ %x6E / ; n LF line feed U+000A
507
+ %x72 / ; r CR carriage return U+000D
508
+ %x74 / ; t HT horizontal tab U+0009
509
+ "'" / ; ' apostrophe U+0027
510
+ "\" / ; \ backslash (reverse solidus) U+005C
511
+ (%x75 normal-hexchar)
512
+ ; certain values u00xx U+00XX
513
+ normal-hexchar = "0" "0"
514
+ (
515
+ ("0" %x30-37) / ; "00"-"07"
516
+ ; omit U+0008-U+000A BS HT LF
517
+ ("0" %x62) / ; "0b"
518
+ ; omit U+000C-U+000D FF CR
519
+ ("0" %x65-66) / ; "0e"-"0f"
520
+ ("1" normal-HEXDIG)
521
+ )
522
+ normal-HEXDIG = DIGIT / %x61-66 ; "0"-"9", "a"-"f"
523
+ normal-index-selector = "0" / (DIGIT1 *DIGIT)
524
+ ; non-negative decimal integer
525
+
526
+ ; Surrogate named rules
527
+ dot-prefix = "."
528
+ double-dot-prefix = ".."
529
+ left-bracket = "["
530
+ right-bracket = "]"
531
+ left-paren = "("
532
+ right-paren = ")"
533
+ comma = ","
534
+ colon = ":"
535
+ dquote = %x22 ; "
536
+ squote = %x27 ; '
537
+ questionmark = "?"
538
+ disjunction = "||"
539
+ conjunction = "&&"
540
+ ```
541
+
542
+ ## License
543
+
544
+ `@swaggerexpert/jsonpath` is licensed under [Apache 2.0 license](https://github.com/swaggerexpert/jsonpath/blob/main/LICENSE).
545
+ `@swaggerexpert/jsonpath` comes with an explicit [NOTICE](https://github.com/swaggerexpert/jsonpath/blob/main/NOTICE) file
546
+ containing additional legal notices and information.
package/SECURITY.md ADDED
@@ -0,0 +1,15 @@
1
+ # Security Policy
2
+
3
+ If you believe you've found an exploitable security issue in @swaggerexpert/jsonpath, please don't create a public issue.
4
+
5
+ ## Supported Versions
6
+
7
+ | Version | Supported |
8
+ |---------|--------------------|
9
+ | ^1.0.0 | :white_check_mark: |
10
+
11
+ ## Reporting a Vulnerability
12
+
13
+ To report a vulnerability please send an email with the details to contact@swaggerexpert.com.
14
+
15
+ I'll acknowledge receipt of your report ASAP, and set expectations on how I plan to handle it.