@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/es/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ export { default as Grammar } from "./grammar.mjs";
2
+ export { default as parse } from "./parse/index.mjs";
3
+ export { default as JSONPathQueryCST } from "./parse/ast/JSONPathQueryCST.mjs";
4
+ export { default as translateEvaluator } from "./parse/evaluators/translate.mjs";
@@ -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,9 @@
1
+ const translateEvaluator = (ast, {
2
+ result
3
+ }) => {
4
+ if (!result.success) return null;
5
+ const parts = {};
6
+ ast.translate(parts);
7
+ return parts;
8
+ };
9
+ export default translateEvaluator;
@@ -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
+ }
@@ -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)