@swaggerexpert/jsonpath 2.0.0 → 2.2.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 CHANGED
@@ -34,8 +34,10 @@ The development of this library contributed to the identification and formal sub
34
34
  - [Installation](#installation)
35
35
  - [Usage](#usage)
36
36
  - [Parsing](#parsing)
37
+ - [Normalized paths](#normalized-paths)
37
38
  - [Translators](#translators)
38
39
  - [CST](#cst-translator)
40
+ - [CST Optimized](#cst-optimized-translator)
39
41
  - [XML](#xml-translator)
40
42
  - [Statistics](#statistics)
41
43
  - [Tracing](#tracing)
@@ -70,14 +72,6 @@ import { parse } from '@swaggerexpert/jsonpath';
70
72
  const parseResult = parse('$.store.book[0].title');
71
73
  ```
72
74
 
73
- or
74
-
75
- ```js
76
- import { parse, CSTTranslator } from '@swaggerexpert/jsonpath';
77
-
78
- const parseResult = parse('$.store.book[0].title', { translator: new CSTTranslator() });
79
- ```
80
-
81
75
  **parseResult** variable has the following shape:
82
76
 
83
77
  ```
@@ -91,6 +85,31 @@ const parseResult = parse('$.store.book[0].title', { translator: new CSTTranslat
91
85
 
92
86
  [TypeScript typings](https://github.com/swaggerexpert/jsonpath/blob/main/types/index.d.ts) are available for all fields attached to parse result object returned by the `parse` function.
93
87
 
88
+
89
+ ##### Normalized paths
90
+
91
+ [comment]: <> (SPDX-FileCopyrightText: Copyright &#40;c&#41; 2024 IETF Trust and the persons identified as the document authors. All rights reserved.)
92
+ [comment]: <> (SPDX-License-Identifier: BSD-3-Clause)
93
+
94
+ [Normalized Path](https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths) is a JSONPath query with restricted syntax.
95
+ A Normalized Path represents the identity of a node in a specific value.
96
+ There is precisely one Normalized Path identifying any particular node in a value.
97
+ Normalized Paths provide a predictable format that simplifies testing and post-processing of nodelists, e.g., to remove duplicate nodes.
98
+ Normalized Paths use the canonical bracket notation, rather than dot notation.
99
+ Single quotes are used in Normalized Paths to delimit string member names. This reduces the number of characters that need escaping when Normalized Paths appear in strings delimited by double quotes.
100
+
101
+ Parsing in normalized path mode can be enabled by setting `normalized` option to `true`.
102
+
103
+ ```js
104
+ import { parse } from '@swaggerexpert/jsonpath';
105
+
106
+ parse("$['a']", { normalized: true });
107
+ parse("$[1]", { normalized: true });
108
+ parse("$[2]", { normalized: true });
109
+ parse("$['a']['b'][1]", { normalized: true });
110
+ parse("$['\\u000b']", { normalized: true });
111
+ ```
112
+
94
113
  ##### Translators
95
114
 
96
115
  `@swaggerexpert/jsonpath` provides several translators to convert the parse result into different tree representations.
@@ -130,6 +149,17 @@ interface CSTNode {
130
149
  }
131
150
  ```
132
151
 
152
+ ###### CST Optimized translator
153
+
154
+ Same as CST, but optimizes the tree for more optimized representation. By default, it collapses
155
+ fragmented `single-quoted` or `double-quoted` nodes into a single node.
156
+
157
+ ```js
158
+ import { parse, CSTOptimizedTranslator } from '@swaggerexpert/jsonpath';
159
+
160
+ const { tree: CST } = parse('$.store.book[0].title', { translator: new CSTOptimizedTranslator() });
161
+ ```
162
+
133
163
  ###### XML translator
134
164
 
135
165
  ```js
package/cjs/grammar.cjs CHANGED
@@ -16,9 +16,9 @@ function grammar() {
16
16
  // ALT = 41
17
17
  // CAT = 60
18
18
  // REP = 32
19
- // RNM = 174
20
- // TLS = 66
21
- // TBS = 30
19
+ // RNM = 178
20
+ // TLS = 64
21
+ // TBS = 28
22
22
  // TRG = 20
23
23
  // --- SABNF superset opcodes
24
24
  // UDT = 0
@@ -2247,17 +2247,17 @@ function grammar() {
2247
2247
  children: [1, 2, 3]
2248
2248
  }; // CAT
2249
2249
  this.rules[69].opcodes[1] = {
2250
- type: 7,
2251
- string: [91]
2252
- }; // TLS
2250
+ type: 4,
2251
+ index: 80
2252
+ }; // RNM(left-bracket)
2253
2253
  this.rules[69].opcodes[2] = {
2254
2254
  type: 4,
2255
2255
  index: 70
2256
2256
  }; // RNM(normal-selector)
2257
2257
  this.rules[69].opcodes[3] = {
2258
- type: 7,
2259
- string: [93]
2260
- }; // TLS
2258
+ type: 4,
2259
+ index: 81
2260
+ }; // RNM(right-bracket)
2261
2261
 
2262
2262
  /* normal-selector */
2263
2263
  this.rules[70].opcodes = [];
@@ -2281,9 +2281,9 @@ function grammar() {
2281
2281
  children: [1, 2, 4]
2282
2282
  }; // CAT
2283
2283
  this.rules[71].opcodes[1] = {
2284
- type: 6,
2285
- string: [39]
2286
- }; // TBS
2284
+ type: 4,
2285
+ index: 87
2286
+ }; // RNM(squote)
2287
2287
  this.rules[71].opcodes[2] = {
2288
2288
  type: 3,
2289
2289
  min: 0,
@@ -2294,9 +2294,9 @@ function grammar() {
2294
2294
  index: 72
2295
2295
  }; // RNM(normal-single-quoted)
2296
2296
  this.rules[71].opcodes[4] = {
2297
- type: 6,
2298
- string: [39]
2299
- }; // TBS
2297
+ type: 4,
2298
+ index: 87
2299
+ }; // RNM(squote)
2300
2300
 
2301
2301
  /* normal-single-quoted */
2302
2302
  this.rules[72].opcodes = [];
@@ -2784,9 +2784,9 @@ function grammar() {
2784
2784
  str += "\n";
2785
2785
  str += "; https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths\n";
2786
2786
  str += "normalized-path = root-identifier *(normal-index-segment)\n";
2787
- str += "normal-index-segment = \"[\" normal-selector \"]\"\n";
2787
+ str += "normal-index-segment = left-bracket normal-selector right-bracket ; MODIFICATION: surrogate text rule used\n";
2788
2788
  str += "normal-selector = normal-name-selector / normal-index-selector\n";
2789
- str += "normal-name-selector = %x27 *normal-single-quoted %x27 ; 'string'\n";
2789
+ str += "normal-name-selector = squote *normal-single-quoted squote ; 'string', MODIFICATION: surrogate text rule used\n";
2790
2790
  str += "normal-single-quoted = normal-unescaped /\n";
2791
2791
  str += " ESC normal-escapable\n";
2792
2792
  str += "normal-unescaped = ; omit %x0-1F control codes\n";
package/cjs/index.cjs CHANGED
@@ -1,13 +1,15 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- exports.parse = exports.XMLTranslator = exports.Grammar = exports.CSTTranslator = void 0;
4
+ exports.parse = exports.XMLTranslator = exports.Grammar = exports.CSTTranslator = exports.CSTOptimizedTranslator = void 0;
5
5
  var _grammar = _interopRequireDefault(require("./grammar.cjs"));
6
6
  exports.Grammar = _grammar.default;
7
7
  var _index = _interopRequireDefault(require("./parse/index.cjs"));
8
8
  exports.parse = _index.default;
9
9
  var _CSTTranslator = _interopRequireDefault(require("./parse/translators/CSTTranslator.cjs"));
10
10
  exports.CSTTranslator = _CSTTranslator.default;
11
+ var _CSTOptimizedTranslator = _interopRequireDefault(require("./parse/translators/CSTOptimizedTranslator.cjs"));
12
+ exports.CSTOptimizedTranslator = _CSTOptimizedTranslator.default;
11
13
  var _XMLTranslator = _interopRequireDefault(require("./parse/translators/XMLTranslator.cjs"));
12
14
  exports.XMLTranslator = _XMLTranslator.default;
13
15
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -22,9 +22,15 @@ const cst = ruleName => {
22
22
  children: []
23
23
  };
24
24
  if (data.stack.length > 0) {
25
+ var _data$options, _data$options2;
25
26
  const parent = data.stack[data.stack.length - 1];
26
- const isTextWithinTextNode = parent.type === 'text' && node.type === 'text';
27
- if (!isTextWithinTextNode) {
27
+ const prevSibling = parent.children[parent.children.length - 1];
28
+ const isTextNodeWithinTextNode = parent.type === 'text' && node.type === 'text';
29
+ const shouldCollapse = ((_data$options = data.options) == null ? void 0 : _data$options.optimize) && ((_data$options2 = data.options) == null || (_data$options2 = _data$options2.collapsibleTypes) == null ? void 0 : _data$options2.includes(node.type)) && (prevSibling == null ? void 0 : prevSibling.type) === node.type;
30
+ if (shouldCollapse) {
31
+ prevSibling.text += node.text;
32
+ prevSibling.length += node.length;
33
+ } else if (!isTextNodeWithinTextNode) {
28
34
  parent.children.push(node);
29
35
  }
30
36
  } else {
@@ -9,9 +9,11 @@ var _JSONPathParseError = _interopRequireDefault(require("../errors/JSONPathPars
9
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
10
  const grammar = new _grammar.default();
11
11
  const parse = (jsonPath, {
12
- translator = new _CSTTranslator.default(),
12
+ normalized = false,
13
13
  stats = false,
14
- trace = false
14
+ trace = false,
15
+ translator = new _CSTTranslator.default(),
16
+ test = 3
15
17
  } = {}) => {
16
18
  if (typeof jsonPath !== 'string') {
17
19
  throw new TypeError('JSONPath must be a string');
@@ -22,7 +24,8 @@ const parse = (jsonPath, {
22
24
  if (translator) parser.ast = translator;
23
25
  if (stats) parser.stats = new _apgLite.Stats();
24
26
  if (trace) parser.trace = new _apgLite.Trace();
25
- const result = parser.parse(grammar, 'jsonpath-query', jsonPath);
27
+ const startRule = normalized ? 'normalized-path' : 'jsonpath-query';
28
+ const result = parser.parse(grammar, startRule, jsonPath);
26
29
  return {
27
30
  result,
28
31
  tree: (_parser$ast = parser.ast) == null ? void 0 : _parser$ast.getTree(),
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.default = void 0;
5
+ var _CSTTranslator = _interopRequireDefault(require("./CSTTranslator.cjs"));
6
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
7
+ class CSTOptimizedTranslator extends _CSTTranslator.default {
8
+ collapsibleTypes = ['single-quoted', 'double-quoted'];
9
+ constructor({
10
+ collapsibleTypes
11
+ } = {}) {
12
+ super();
13
+ if (Array.isArray(collapsibleTypes)) {
14
+ this.collapsibleTypes = collapsibleTypes;
15
+ }
16
+ }
17
+ getTree() {
18
+ const options = {
19
+ optimize: true,
20
+ collapsibleTypes: this.collapsibleTypes
21
+ };
22
+ const data = {
23
+ stack: [],
24
+ root: null,
25
+ options
26
+ };
27
+ this.translate(data);
28
+ delete data.stack;
29
+ delete data.options;
30
+ return data;
31
+ }
32
+ }
33
+ var _default = exports.default = CSTOptimizedTranslator;
@@ -82,6 +82,14 @@ class CSTTranslator extends _apgLite.Ast {
82
82
  // https://www.rfc-editor.org/rfc/rfc9535#section-2.5.2.1
83
83
  this.callbacks['descendant-segment'] = (0, _cst.default)('descendant-segment');
84
84
 
85
+ // https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths
86
+ this.callbacks['normalized-path'] = (0, _cst.default)('normalized-path');
87
+ this.callbacks['normal-index-segment'] = (0, _cst.default)('normal-index-segment');
88
+ this.callbacks['normal-selector'] = (0, _cst.default)('normal-selector');
89
+ this.callbacks['normal-name-selector'] = (0, _cst.default)('normal-name-selector');
90
+ this.callbacks['normal-index-selector'] = (0, _cst.default)('normal-index-selector');
91
+ this.callbacks['normal-single-quoted'] = (0, _cst.default)('normal-single-quoted');
92
+
85
93
  // Surrogate named rules
86
94
  this.callbacks['dot-prefix'] = (0, _cst.default)('text');
87
95
  this.callbacks['double-dot-prefix'] = (0, _cst.default)('text');
package/es/grammar.mjs CHANGED
@@ -12,9 +12,9 @@ export default function grammar() {
12
12
  // ALT = 41
13
13
  // CAT = 60
14
14
  // REP = 32
15
- // RNM = 174
16
- // TLS = 66
17
- // TBS = 30
15
+ // RNM = 178
16
+ // TLS = 64
17
+ // TBS = 28
18
18
  // TRG = 20
19
19
  // --- SABNF superset opcodes
20
20
  // UDT = 0
@@ -2243,17 +2243,17 @@ export default function grammar() {
2243
2243
  children: [1, 2, 3]
2244
2244
  }; // CAT
2245
2245
  this.rules[69].opcodes[1] = {
2246
- type: 7,
2247
- string: [91]
2248
- }; // TLS
2246
+ type: 4,
2247
+ index: 80
2248
+ }; // RNM(left-bracket)
2249
2249
  this.rules[69].opcodes[2] = {
2250
2250
  type: 4,
2251
2251
  index: 70
2252
2252
  }; // RNM(normal-selector)
2253
2253
  this.rules[69].opcodes[3] = {
2254
- type: 7,
2255
- string: [93]
2256
- }; // TLS
2254
+ type: 4,
2255
+ index: 81
2256
+ }; // RNM(right-bracket)
2257
2257
 
2258
2258
  /* normal-selector */
2259
2259
  this.rules[70].opcodes = [];
@@ -2277,9 +2277,9 @@ export default function grammar() {
2277
2277
  children: [1, 2, 4]
2278
2278
  }; // CAT
2279
2279
  this.rules[71].opcodes[1] = {
2280
- type: 6,
2281
- string: [39]
2282
- }; // TBS
2280
+ type: 4,
2281
+ index: 87
2282
+ }; // RNM(squote)
2283
2283
  this.rules[71].opcodes[2] = {
2284
2284
  type: 3,
2285
2285
  min: 0,
@@ -2290,9 +2290,9 @@ export default function grammar() {
2290
2290
  index: 72
2291
2291
  }; // RNM(normal-single-quoted)
2292
2292
  this.rules[71].opcodes[4] = {
2293
- type: 6,
2294
- string: [39]
2295
- }; // TBS
2293
+ type: 4,
2294
+ index: 87
2295
+ }; // RNM(squote)
2296
2296
 
2297
2297
  /* normal-single-quoted */
2298
2298
  this.rules[72].opcodes = [];
@@ -2780,9 +2780,9 @@ export default function grammar() {
2780
2780
  str += "\n";
2781
2781
  str += "; https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths\n";
2782
2782
  str += "normalized-path = root-identifier *(normal-index-segment)\n";
2783
- str += "normal-index-segment = \"[\" normal-selector \"]\"\n";
2783
+ str += "normal-index-segment = left-bracket normal-selector right-bracket ; MODIFICATION: surrogate text rule used\n";
2784
2784
  str += "normal-selector = normal-name-selector / normal-index-selector\n";
2785
- str += "normal-name-selector = %x27 *normal-single-quoted %x27 ; 'string'\n";
2785
+ str += "normal-name-selector = squote *normal-single-quoted squote ; 'string', MODIFICATION: surrogate text rule used\n";
2786
2786
  str += "normal-single-quoted = normal-unescaped /\n";
2787
2787
  str += " ESC normal-escapable\n";
2788
2788
  str += "normal-unescaped = ; omit %x0-1F control codes\n";
package/es/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  export { default as Grammar } from "./grammar.mjs";
2
2
  export { default as parse } from "./parse/index.mjs";
3
3
  export { default as CSTTranslator } from "./parse/translators/CSTTranslator.mjs";
4
+ export { default as CSTOptimizedTranslator } from "./parse/translators/CSTOptimizedTranslator.mjs";
4
5
  export { default as XMLTranslator } from "./parse/translators/XMLTranslator.mjs";
@@ -17,9 +17,15 @@ const cst = ruleName => {
17
17
  children: []
18
18
  };
19
19
  if (data.stack.length > 0) {
20
+ var _data$options, _data$options2;
20
21
  const parent = data.stack[data.stack.length - 1];
21
- const isTextWithinTextNode = parent.type === 'text' && node.type === 'text';
22
- if (!isTextWithinTextNode) {
22
+ const prevSibling = parent.children[parent.children.length - 1];
23
+ const isTextNodeWithinTextNode = parent.type === 'text' && node.type === 'text';
24
+ const shouldCollapse = ((_data$options = data.options) === null || _data$options === void 0 ? void 0 : _data$options.optimize) && ((_data$options2 = data.options) === null || _data$options2 === void 0 || (_data$options2 = _data$options2.collapsibleTypes) === null || _data$options2 === void 0 ? void 0 : _data$options2.includes(node.type)) && (prevSibling === null || prevSibling === void 0 ? void 0 : prevSibling.type) === node.type;
25
+ if (shouldCollapse) {
26
+ prevSibling.text += node.text;
27
+ prevSibling.length += node.length;
28
+ } else if (!isTextNodeWithinTextNode) {
23
29
  parent.children.push(node);
24
30
  }
25
31
  } else {
@@ -4,9 +4,11 @@ import CSTTranslator from "./translators/CSTTranslator.mjs";
4
4
  import JSONPathParseError from "../errors/JSONPathParseError.mjs";
5
5
  const grammar = new Grammar();
6
6
  const parse = (jsonPath, {
7
- translator = new CSTTranslator(),
7
+ normalized = false,
8
8
  stats = false,
9
- trace = false
9
+ trace = false,
10
+ translator = new CSTTranslator(),
11
+ test = 3
10
12
  } = {}) => {
11
13
  if (typeof jsonPath !== 'string') {
12
14
  throw new TypeError('JSONPath must be a string');
@@ -17,7 +19,8 @@ const parse = (jsonPath, {
17
19
  if (translator) parser.ast = translator;
18
20
  if (stats) parser.stats = new Stats();
19
21
  if (trace) parser.trace = new Trace();
20
- const result = parser.parse(grammar, 'jsonpath-query', jsonPath);
22
+ const startRule = normalized ? 'normalized-path' : 'jsonpath-query';
23
+ const result = parser.parse(grammar, startRule, jsonPath);
21
24
  return {
22
25
  result,
23
26
  tree: (_parser$ast = parser.ast) === null || _parser$ast === void 0 ? void 0 : _parser$ast.getTree(),
@@ -0,0 +1,28 @@
1
+ import CSTTranslator from "./CSTTranslator.mjs";
2
+ class CSTOptimizedTranslator extends CSTTranslator {
3
+ collapsibleTypes = ['single-quoted', 'double-quoted'];
4
+ constructor({
5
+ collapsibleTypes
6
+ } = {}) {
7
+ super();
8
+ if (Array.isArray(collapsibleTypes)) {
9
+ this.collapsibleTypes = collapsibleTypes;
10
+ }
11
+ }
12
+ getTree() {
13
+ const options = {
14
+ optimize: true,
15
+ collapsibleTypes: this.collapsibleTypes
16
+ };
17
+ const data = {
18
+ stack: [],
19
+ root: null,
20
+ options
21
+ };
22
+ this.translate(data);
23
+ delete data.stack;
24
+ delete data.options;
25
+ return data;
26
+ }
27
+ }
28
+ export default CSTOptimizedTranslator;
@@ -77,6 +77,14 @@ class CSTTranslator extends AST {
77
77
  // https://www.rfc-editor.org/rfc/rfc9535#section-2.5.2.1
78
78
  this.callbacks['descendant-segment'] = cstCallback('descendant-segment');
79
79
 
80
+ // https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths
81
+ this.callbacks['normalized-path'] = cstCallback('normalized-path');
82
+ this.callbacks['normal-index-segment'] = cstCallback('normal-index-segment');
83
+ this.callbacks['normal-selector'] = cstCallback('normal-selector');
84
+ this.callbacks['normal-name-selector'] = cstCallback('normal-name-selector');
85
+ this.callbacks['normal-index-selector'] = cstCallback('normal-index-selector');
86
+ this.callbacks['normal-single-quoted'] = cstCallback('normal-single-quoted');
87
+
80
88
  // Surrogate named rules
81
89
  this.callbacks['dot-prefix'] = cstCallback('text');
82
90
  this.callbacks['double-dot-prefix'] = cstCallback('text');
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "2.0.0",
6
+ "version": "2.2.0",
7
7
  "description": "RCF 9535 implementation of JSONPath",
8
8
  "main": "./cjs/index.cjs",
9
9
  "types": "./types/index.d.ts",
package/types/index.d.ts CHANGED
@@ -4,9 +4,10 @@
4
4
  export function parse(jsonpath: string, options?: ParseOptions): ParseResult;
5
5
 
6
6
  export interface ParseOptions {
7
- readonly translator?: Translator | null;
7
+ readonly normalized?: boolean;
8
8
  readonly stats?: boolean;
9
9
  readonly trace?: boolean;
10
+ readonly translator?: Translator | null;
10
11
  }
11
12
 
12
13
  export interface Translator<TTree = unknown> {
@@ -15,6 +16,10 @@ export interface Translator<TTree = unknown> {
15
16
  export declare class CSTTranslator implements Translator<CSTTree> {
16
17
  getTree(): CSTTree;
17
18
  }
19
+ export declare class CSTOptimizedTranslator implements CSTTranslator {
20
+ constructor(options?: { collapsibleTypes?: string[] });
21
+ getTree(): CSTTree;
22
+ }
18
23
  export declare class XMLTranslator implements Translator<XMLTree> {
19
24
  getTree(): XMLTree;
20
25
  }