@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/LICENSE +202 -0
- package/NOTICE +11 -0
- package/README.md +546 -0
- package/SECURITY.md +15 -0
- package/cjs/apg-lite.cjs +1221 -0
- package/cjs/errors/JSONPathError.cjs +44 -0
- package/cjs/errors/JSONPathParseError.cjs +8 -0
- package/cjs/grammar.cjs +2838 -0
- package/cjs/index.cjs +13 -0
- package/cjs/parse/ast/JSONPathQueryCST.cjs +101 -0
- package/cjs/parse/callbacks/cst.cjs +44 -0
- package/cjs/parse/evaluators/translate.cjs +13 -0
- package/cjs/parse/index.cjs +38 -0
- package/es/errors/JSONPathError.mjs +40 -0
- package/es/errors/JSONPathParseError.mjs +3 -0
- package/es/grammar.mjs +2834 -0
- package/es/index.mjs +4 -0
- package/es/parse/ast/JSONPathQueryCST.mjs +96 -0
- package/es/parse/callbacks/cst.mjs +39 -0
- package/es/parse/evaluators/translate.mjs +9 -0
- package/es/parse/index.mjs +33 -0
- package/package.json +81 -0
- package/types/index.d.ts +59 -0
package/es/index.mjs
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Ast as AST } from 'apg-lite';
|
|
2
|
+
import cstCallback from "../callbacks/cst.mjs";
|
|
3
|
+
class JSONPathQueryCST extends AST {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
|
|
7
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.1.1
|
|
8
|
+
this.callbacks['jsonpath-query'] = cstCallback('jsonpath-query');
|
|
9
|
+
this.callbacks['segments'] = cstCallback('segments');
|
|
10
|
+
this.callbacks['B'] = cstCallback('text');
|
|
11
|
+
this.callbacks['S'] = cstCallback('text');
|
|
12
|
+
|
|
13
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.2.1
|
|
14
|
+
this.callbacks['root-identifier'] = cstCallback('root-identifier');
|
|
15
|
+
|
|
16
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.3
|
|
17
|
+
this.callbacks['selector'] = cstCallback('selector');
|
|
18
|
+
|
|
19
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.3.1.1
|
|
20
|
+
this.callbacks['name-selector'] = cstCallback('name-selector');
|
|
21
|
+
this.callbacks['string-literal'] = cstCallback('string-literal');
|
|
22
|
+
this.callbacks['double-quoted'] = cstCallback('double-quoted');
|
|
23
|
+
this.callbacks['single-quoted'] = cstCallback('single-quoted');
|
|
24
|
+
|
|
25
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.3.2.1
|
|
26
|
+
this.callbacks['wildcard-selector'] = cstCallback('wildcard-selector');
|
|
27
|
+
|
|
28
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.3.3.1
|
|
29
|
+
this.callbacks['index-selector'] = cstCallback('index-selector');
|
|
30
|
+
|
|
31
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.3.4.1
|
|
32
|
+
this.callbacks['slice-selector'] = cstCallback('slice-selector');
|
|
33
|
+
this.callbacks['start'] = cstCallback('start');
|
|
34
|
+
this.callbacks['end'] = cstCallback('end');
|
|
35
|
+
this.callbacks['step'] = cstCallback('step');
|
|
36
|
+
|
|
37
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.3.5.1
|
|
38
|
+
this.callbacks['filter-selector'] = cstCallback('filter-selector');
|
|
39
|
+
this.callbacks['logical-expr'] = cstCallback('logical-expr');
|
|
40
|
+
this.callbacks['logical-or-expr'] = cstCallback('logical-or-expr');
|
|
41
|
+
this.callbacks['logical-and-expr'] = cstCallback('logical-and-expr');
|
|
42
|
+
this.callbacks['basic-expr'] = cstCallback('basic-expr');
|
|
43
|
+
this.callbacks['paren-expr'] = cstCallback('paren-expr');
|
|
44
|
+
this.callbacks['logical-not-op'] = cstCallback('logical-not-op');
|
|
45
|
+
this.callbacks['test-expr'] = cstCallback('test-expr');
|
|
46
|
+
this.callbacks['filter-query'] = cstCallback('filter-query');
|
|
47
|
+
this.callbacks['rel-query'] = cstCallback('rel-query');
|
|
48
|
+
this.callbacks['current-node-identifier'] = cstCallback('current-node-identifier');
|
|
49
|
+
this.callbacks['comparison-expr'] = cstCallback('comparison-expr');
|
|
50
|
+
this.callbacks['literal'] = cstCallback('literal');
|
|
51
|
+
this.callbacks['comparable'] = cstCallback('comparable');
|
|
52
|
+
this.callbacks['comparison-op'] = cstCallback('comparison-op');
|
|
53
|
+
this.callbacks['singular-query'] = cstCallback('singular-query');
|
|
54
|
+
this.callbacks['rel-singular-query'] = cstCallback('rel-singular-query');
|
|
55
|
+
this.callbacks['abs-singular-query'] = cstCallback('abs-singular-query');
|
|
56
|
+
this.callbacks['singular-query-segments'] = cstCallback('singular-query-segments');
|
|
57
|
+
this.callbacks['name-segment'] = cstCallback('name-segment');
|
|
58
|
+
this.callbacks['index-segment'] = cstCallback('index-segment');
|
|
59
|
+
this.callbacks['number'] = cstCallback('number');
|
|
60
|
+
this.callbacks['true'] = cstCallback('true');
|
|
61
|
+
this.callbacks['false'] = cstCallback('false');
|
|
62
|
+
this.callbacks['null'] = cstCallback('null');
|
|
63
|
+
|
|
64
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.4
|
|
65
|
+
this.callbacks['function-name'] = cstCallback('function-name');
|
|
66
|
+
this.callbacks['function-expr'] = cstCallback('function-expr');
|
|
67
|
+
this.callbacks['function-argument'] = cstCallback('function-argument');
|
|
68
|
+
|
|
69
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.5
|
|
70
|
+
this.callbacks['segment'] = cstCallback('segment');
|
|
71
|
+
|
|
72
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.5.1.1
|
|
73
|
+
this.callbacks['child-segment'] = cstCallback('child-segment');
|
|
74
|
+
this.callbacks['bracketed-selection'] = cstCallback('bracketed-selection');
|
|
75
|
+
this.callbacks['member-name-shorthand'] = cstCallback('member-name-shorthand');
|
|
76
|
+
|
|
77
|
+
// https://www.rfc-editor.org/rfc/rfc9535#section-2.5.2.1
|
|
78
|
+
this.callbacks['descendant-segment'] = cstCallback('descendant-segment');
|
|
79
|
+
|
|
80
|
+
// Surrogate named rules
|
|
81
|
+
this.callbacks['dot-prefix'] = cstCallback('text');
|
|
82
|
+
this.callbacks['double-dot-prefix'] = cstCallback('text');
|
|
83
|
+
this.callbacks['left-bracket'] = cstCallback('text');
|
|
84
|
+
this.callbacks['right-bracket'] = cstCallback('text');
|
|
85
|
+
this.callbacks['comma'] = cstCallback('text');
|
|
86
|
+
this.callbacks['colon'] = cstCallback('text');
|
|
87
|
+
this.callbacks['dquote'] = cstCallback('text');
|
|
88
|
+
this.callbacks['squote'] = cstCallback('text');
|
|
89
|
+
this.callbacks['questionmark'] = cstCallback('text');
|
|
90
|
+
this.callbacks['disjunction'] = cstCallback('text');
|
|
91
|
+
this.callbacks['conjunction'] = cstCallback('text');
|
|
92
|
+
this.callbacks['left-paren'] = cstCallback('text');
|
|
93
|
+
this.callbacks['right-paren'] = cstCallback('text');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export default JSONPathQueryCST;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { utilities, identifiers } from 'apg-lite';
|
|
2
|
+
import JSONPathParseError from "../../errors/JSONPathParseError.mjs";
|
|
3
|
+
const cst = ruleName => {
|
|
4
|
+
return (state, chars, phraseIndex, phraseLength, data) => {
|
|
5
|
+
if (!(typeof data === 'object' && data !== null && !Array.isArray(data))) {
|
|
6
|
+
throw new JSONPathParseError("parser's user data must be an object");
|
|
7
|
+
}
|
|
8
|
+
if (!data.stack) {
|
|
9
|
+
data.stack = [];
|
|
10
|
+
data.root = null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// drop the empty nodes
|
|
14
|
+
if (phraseLength === 0) return;
|
|
15
|
+
if (state === identifiers.SEM_PRE) {
|
|
16
|
+
const node = {
|
|
17
|
+
type: ruleName,
|
|
18
|
+
text: utilities.charsToString(chars, phraseIndex, phraseLength),
|
|
19
|
+
start: phraseIndex,
|
|
20
|
+
length: phraseLength,
|
|
21
|
+
children: []
|
|
22
|
+
};
|
|
23
|
+
if (data.stack.length > 0) {
|
|
24
|
+
const parent = data.stack[data.stack.length - 1];
|
|
25
|
+
const isTextWithinTextNode = parent.type === 'text' && node.type === 'text';
|
|
26
|
+
if (!isTextWithinTextNode) {
|
|
27
|
+
parent.children.push(node);
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
data.root = node;
|
|
31
|
+
}
|
|
32
|
+
data.stack.push(node);
|
|
33
|
+
}
|
|
34
|
+
if (state === identifiers.SEM_POST) {
|
|
35
|
+
data.stack.pop();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export default cst;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Parser } from 'apg-lite';
|
|
2
|
+
import Grammar from "../grammar.mjs";
|
|
3
|
+
import translateEvaluator from "./evaluators/translate.mjs";
|
|
4
|
+
import JSONPathQueryCST from "./ast/JSONPathQueryCST.mjs";
|
|
5
|
+
import JSONPathParseError from "../errors/JSONPathParseError.mjs";
|
|
6
|
+
const grammar = new Grammar();
|
|
7
|
+
const parse = (jsonPath, {
|
|
8
|
+
ast = new JSONPathQueryCST(),
|
|
9
|
+
evaluator = translateEvaluator
|
|
10
|
+
} = {}) => {
|
|
11
|
+
if (typeof jsonPath !== 'string') {
|
|
12
|
+
throw new TypeError('JSONPath must be a string');
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const parser = new Parser();
|
|
16
|
+
parser.ast = ast;
|
|
17
|
+
const result = parser.parse(grammar, 'jsonpath-query', jsonPath);
|
|
18
|
+
const computed = evaluator(ast, {
|
|
19
|
+
result
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
result,
|
|
23
|
+
ast,
|
|
24
|
+
computed
|
|
25
|
+
};
|
|
26
|
+
} catch (error) {
|
|
27
|
+
throw new JSONPathParseError('Unexpected error during JSONPath parsing', {
|
|
28
|
+
cause: error,
|
|
29
|
+
jsonPath
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
export default parse;
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@swaggerexpert/jsonpath",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"version": "1.0.0",
|
|
7
|
+
"description": "RCF 9535 implementation of JSONPath",
|
|
8
|
+
"main": "./cjs/index.cjs",
|
|
9
|
+
"types": "./types/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./types/index.d.ts",
|
|
13
|
+
"import": "./es/index.mjs",
|
|
14
|
+
"require": "./cjs/index.cjs"
|
|
15
|
+
},
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
"watch": {
|
|
19
|
+
"test": "{src,test}/*.js"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"grammar:compile": "node ./scripts/apg-js.js --lite --in=./src/grammar.bnf --out=./src/grammar.js && cd ./src",
|
|
24
|
+
"build": "npm run grammar:compile && npm run build:es && npm run build:cjs && npm run build:cjs:apg-lite",
|
|
25
|
+
"build:es": "cross-env BABEL_ENV=es babel src --out-dir es --extensions '.js' --out-file-extension '.mjs'",
|
|
26
|
+
"build:cjs": "cross-env BABEL_ENV=cjs babel src --out-dir cjs --extensions '.js' --out-file-extension '.cjs'",
|
|
27
|
+
"build:cjs:apg-lite": "cross-env BABEL_ENV=cjs babel node_modules/apg-lite/lib/parser.js --out-file ./cjs/apg-lite.cjs",
|
|
28
|
+
"test": "cross-env UPDATE_SNAPSHOT=new mocha",
|
|
29
|
+
"test:watch": "npm-watch test",
|
|
30
|
+
"watch": "npm-watch"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=12.20.0"
|
|
34
|
+
},
|
|
35
|
+
"type": "module",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/swaggerexpert/jsonpath.git"
|
|
39
|
+
},
|
|
40
|
+
"keywords": [
|
|
41
|
+
"jsonpath",
|
|
42
|
+
"parser",
|
|
43
|
+
"validator",
|
|
44
|
+
"rfc9535"
|
|
45
|
+
],
|
|
46
|
+
"author": "Vladimír Gorej <vladimir.gorej@gmail.com>",
|
|
47
|
+
"license": "Apache-2.0",
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/swaggerexpert/jsonpath/issues"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/swaggerexpert/jsonpath#readme",
|
|
52
|
+
"files": [
|
|
53
|
+
"es/",
|
|
54
|
+
"cjs/",
|
|
55
|
+
"types/",
|
|
56
|
+
"LICENSE",
|
|
57
|
+
"NOTICE",
|
|
58
|
+
"package.json",
|
|
59
|
+
"README.md",
|
|
60
|
+
"SECURITY.md"
|
|
61
|
+
],
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"apg-lite": "^1.0.4"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@babel/cli": "=7.26.4",
|
|
67
|
+
"@babel/core": "=7.26.10",
|
|
68
|
+
"@babel/preset-env": "=7.26.9",
|
|
69
|
+
"@commitlint/cli": "=19.8.0",
|
|
70
|
+
"@commitlint/config-conventional": "=19.8.0",
|
|
71
|
+
"apg-js": "^4.4.0",
|
|
72
|
+
"babel-plugin-module-resolver": "^5.0.2",
|
|
73
|
+
"chai": "=5.2.0",
|
|
74
|
+
"cross-env": "^7.0.3",
|
|
75
|
+
"husky": "=9.1.7",
|
|
76
|
+
"mocha": "=11.1.0",
|
|
77
|
+
"mocha-expect-snapshot": "^7.2.0",
|
|
78
|
+
"npm-watch": "^0.13.0",
|
|
79
|
+
"prettier": "^3.5.2"
|
|
80
|
+
}
|
|
81
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsing
|
|
3
|
+
*/
|
|
4
|
+
export function parse(jsonpath: string, options?: ParseOptions): ParseResult;
|
|
5
|
+
|
|
6
|
+
export interface ParseOptions {
|
|
7
|
+
readonly ast?: AST;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface AST {
|
|
11
|
+
readonly translate: (parts: Record<string, CSTNode>) => Record<string, CSTNode>;
|
|
12
|
+
readonly toXml: () => string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ParseResult {
|
|
16
|
+
readonly result: {
|
|
17
|
+
readonly success: boolean;
|
|
18
|
+
};
|
|
19
|
+
readonly ast: AST;
|
|
20
|
+
readonly computed: Record<string, CSTNode>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CSTNode {
|
|
24
|
+
type: string,
|
|
25
|
+
text: string,
|
|
26
|
+
start: number,
|
|
27
|
+
length: number,
|
|
28
|
+
children: CSTNode[],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Grammar
|
|
33
|
+
*/
|
|
34
|
+
export function Grammar(): Grammar;
|
|
35
|
+
|
|
36
|
+
export interface Grammar {
|
|
37
|
+
grammarObject: string; // Internal identifier
|
|
38
|
+
rules: Rule[]; // List of grammar rules
|
|
39
|
+
udts: UDT[]; // User-defined terminals (empty in this grammar)
|
|
40
|
+
toString(): string; // Method to return the grammar in ABNF format
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface Rule {
|
|
44
|
+
name: string; // Rule name
|
|
45
|
+
lower: string; // Lowercased rule name
|
|
46
|
+
index: number; // Rule index
|
|
47
|
+
isBkr: boolean; // Is this a back-reference?
|
|
48
|
+
opcodes?: Opcode[]; // List of opcodes for the rule
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type Opcode =
|
|
52
|
+
| { type: 1; children: number[] } // ALT (alternation)
|
|
53
|
+
| { type: 2; children: number[] } // CAT (concatenation)
|
|
54
|
+
| { type: 3; min: number; max: number } // REP (repetition)
|
|
55
|
+
| { type: 4; index: number } // RNM (rule reference)
|
|
56
|
+
| { type: 5; min: number; max: number } // TRG (terminal range)
|
|
57
|
+
| { type: 6 | 7; string: number[] }; // TBS or TLS (byte sequence or literal string)
|
|
58
|
+
|
|
59
|
+
export type UDT = {}; // User-defined terminals (empty in this grammar)
|