@swaggerexpert/jsonpath 3.2.5 → 4.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 +225 -18
- package/cjs/errors/JSONNormalizedPathError.cjs +8 -0
- package/cjs/errors/JSONPathError.cjs +1 -1
- package/cjs/errors/{JSONPathCompileError.cjs → JSONPathEvaluateError.cjs} +2 -2
- package/cjs/evaluate/evaluators/comparable.cjs +44 -0
- package/cjs/evaluate/evaluators/comparison-expr.cjs +37 -0
- package/cjs/evaluate/evaluators/filter-query.cjs +182 -0
- package/cjs/evaluate/evaluators/function-expr.cjs +106 -0
- package/cjs/evaluate/evaluators/literal.cjs +25 -0
- package/cjs/evaluate/evaluators/logical-expr.cjs +96 -0
- package/cjs/evaluate/evaluators/singular-query.cjs +103 -0
- package/cjs/evaluate/functions/count.cjs +35 -0
- package/cjs/evaluate/functions/index.cjs +15 -0
- package/cjs/evaluate/functions/length.cjs +42 -0
- package/cjs/evaluate/functions/match.cjs +49 -0
- package/cjs/evaluate/functions/search.cjs +49 -0
- package/cjs/evaluate/functions/value.cjs +36 -0
- package/cjs/evaluate/index.cjs +182 -0
- package/cjs/evaluate/realms/EvaluationRealm.cjs +154 -0
- package/cjs/evaluate/realms/json/index.cjs +246 -0
- package/cjs/evaluate/utils/guards.cjs +129 -0
- package/cjs/evaluate/utils/i-regexp.cjs +118 -0
- package/cjs/evaluate/visitors/bracketed-selection.cjs +35 -0
- package/cjs/evaluate/visitors/filter-selector.cjs +43 -0
- package/cjs/evaluate/visitors/index-selector.cjs +55 -0
- package/cjs/evaluate/visitors/name-selector.cjs +38 -0
- package/cjs/evaluate/visitors/segment.cjs +99 -0
- package/cjs/evaluate/visitors/selector.cjs +47 -0
- package/cjs/evaluate/visitors/slice-selector.cjs +115 -0
- package/cjs/evaluate/visitors/wildcard-selector.cjs +32 -0
- package/cjs/index.cjs +16 -7
- package/cjs/normalized-path.cjs +145 -0
- package/cjs/parse/callbacks/cst.cjs +2 -4
- package/cjs/parse/index.cjs +3 -1
- package/cjs/parse/translators/ASTTranslator/index.cjs +1 -1
- package/cjs/parse/translators/ASTTranslator/transformers.cjs +246 -5
- package/cjs/parse/translators/CSTOptimizedTranslator.cjs +1 -3
- package/cjs/parse/translators/CSTTranslator.cjs +1 -2
- package/cjs/test/index.cjs +4 -2
- package/es/errors/JSONNormalizedPathError.mjs +3 -0
- package/es/errors/JSONPathError.mjs +1 -1
- package/es/errors/JSONPathEvaluateError.mjs +3 -0
- package/es/evaluate/evaluators/comparable.mjs +38 -0
- package/es/evaluate/evaluators/comparison-expr.mjs +31 -0
- package/es/evaluate/evaluators/filter-query.mjs +175 -0
- package/es/evaluate/evaluators/function-expr.mjs +99 -0
- package/es/evaluate/evaluators/literal.mjs +21 -0
- package/es/evaluate/evaluators/logical-expr.mjs +89 -0
- package/es/evaluate/evaluators/singular-query.mjs +97 -0
- package/es/evaluate/functions/count.mjs +30 -0
- package/es/evaluate/functions/index.mjs +13 -0
- package/es/evaluate/functions/length.mjs +37 -0
- package/es/evaluate/functions/match.mjs +44 -0
- package/es/evaluate/functions/search.mjs +44 -0
- package/es/evaluate/functions/value.mjs +31 -0
- package/es/evaluate/index.mjs +174 -0
- package/es/evaluate/realms/EvaluationRealm.mjs +148 -0
- package/es/evaluate/realms/json/index.mjs +240 -0
- package/es/evaluate/utils/guards.mjs +114 -0
- package/es/evaluate/utils/i-regexp.mjs +113 -0
- package/es/evaluate/visitors/bracketed-selection.mjs +29 -0
- package/es/evaluate/visitors/filter-selector.mjs +37 -0
- package/es/evaluate/visitors/index-selector.mjs +51 -0
- package/es/evaluate/visitors/name-selector.mjs +34 -0
- package/es/evaluate/visitors/segment.mjs +91 -0
- package/es/evaluate/visitors/selector.mjs +41 -0
- package/es/evaluate/visitors/slice-selector.mjs +111 -0
- package/es/evaluate/visitors/wildcard-selector.mjs +28 -0
- package/es/index.mjs +7 -3
- package/es/normalized-path.mjs +136 -0
- package/es/parse/callbacks/cst.mjs +2 -4
- package/es/parse/index.mjs +3 -1
- package/es/parse/translators/ASTTranslator/index.mjs +1 -1
- package/es/parse/translators/ASTTranslator/transformers.mjs +246 -5
- package/es/parse/translators/CSTOptimizedTranslator.mjs +1 -3
- package/es/parse/translators/CSTTranslator.mjs +1 -2
- package/es/test/index.mjs +4 -2
- package/package.json +4 -2
- package/types/index.d.ts +114 -11
- package/cjs/compile.cjs +0 -50
- package/cjs/escape.cjs +0 -59
- package/es/compile.mjs +0 -45
- package/es/errors/JSONPathCompileError.mjs +0 -3
- package/es/escape.mjs +0 -55
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.default = void 0;
|
|
5
|
+
var _index = _interopRequireDefault(require("../parse/index.cjs"));
|
|
6
|
+
var NormalizedPath = _interopRequireWildcard(require("../normalized-path.cjs"));
|
|
7
|
+
var _segment = _interopRequireDefault(require("./visitors/segment.cjs"));
|
|
8
|
+
var _index2 = _interopRequireDefault(require("./realms/json/index.cjs"));
|
|
9
|
+
var _JSONPathEvaluateError = _interopRequireDefault(require("../errors/JSONPathEvaluateError.cjs"));
|
|
10
|
+
var defaultFunctions = _interopRequireWildcard(require("./functions/index.cjs"));
|
|
11
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
12
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
+
/**
|
|
14
|
+
* JSONPath evaluation module.
|
|
15
|
+
*
|
|
16
|
+
* Provides the evaluate() function to execute JSONPath expressions against a value.
|
|
17
|
+
* Uses an explicit stack for tree traversal to avoid call stack overflow
|
|
18
|
+
* on deeply nested documents.
|
|
19
|
+
*
|
|
20
|
+
* @module evaluate
|
|
21
|
+
* @see https://www.rfc-editor.org/rfc/rfc9535
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {Object} EvaluateOptions
|
|
26
|
+
* @property {Function} [callback] - Optional callback (value, normalizedPath) => void
|
|
27
|
+
* Called for each match. Allows streaming results and collecting paths.
|
|
28
|
+
* @property {Object} [realm] - Optional custom evaluation realm.
|
|
29
|
+
* Default is JSONEvaluationRealm for plain objects/arrays.
|
|
30
|
+
* @property {Object} [functions] - Optional custom function registry.
|
|
31
|
+
* Can extend or override built-in functions (length, count, match, search, value).
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Evaluate a JSONPath expression against a value.
|
|
36
|
+
*
|
|
37
|
+
* @param {unknown} value - JSON value to query
|
|
38
|
+
* @param {string} expression - JSONPath expression
|
|
39
|
+
* @param {EvaluateOptions} [options] - Evaluation options
|
|
40
|
+
* @returns {unknown[]} - Array of matched values
|
|
41
|
+
* @throws {JSONPathEvaluateError} If the expression is invalid
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* // Simple query
|
|
45
|
+
* evaluate({ a: 1, b: 2 }, '$.a');
|
|
46
|
+
* // => [1]
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* // Wildcard
|
|
50
|
+
* evaluate({ store: { book: [{ title: 'A' }, { title: 'B' }] } }, '$.store.book[*].title');
|
|
51
|
+
* // => ['A', 'B']
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // With callback to collect paths
|
|
55
|
+
* const paths = [];
|
|
56
|
+
* evaluate(value, '$.store.book[*]', {
|
|
57
|
+
* callback: (v, path) => paths.push(path)
|
|
58
|
+
* });
|
|
59
|
+
*/
|
|
60
|
+
const evaluate = (value, expression, {
|
|
61
|
+
callback,
|
|
62
|
+
realm = new _index2.default(),
|
|
63
|
+
functions = defaultFunctions
|
|
64
|
+
} = {}) => {
|
|
65
|
+
// Parse the expression
|
|
66
|
+
const parseResult = (0, _index.default)(expression);
|
|
67
|
+
if (!parseResult.result.success) {
|
|
68
|
+
throw new _JSONPathEvaluateError.default(`Invalid JSONPath expression: ${expression}`, {
|
|
69
|
+
expression
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
// The tree is the AST root directly (JsonPathQuery node)
|
|
74
|
+
const ast = parseResult.tree;
|
|
75
|
+
const {
|
|
76
|
+
segments
|
|
77
|
+
} = ast;
|
|
78
|
+
const results = [];
|
|
79
|
+
|
|
80
|
+
// Handle empty query ($ with no segments)
|
|
81
|
+
if (segments.length === 0) {
|
|
82
|
+
results.push(value);
|
|
83
|
+
if (typeof callback === 'function') {
|
|
84
|
+
callback(value, '$');
|
|
85
|
+
}
|
|
86
|
+
return results;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Evaluation context with root for filter expressions
|
|
90
|
+
const ctx = {
|
|
91
|
+
realm,
|
|
92
|
+
root: value,
|
|
93
|
+
functions
|
|
94
|
+
};
|
|
95
|
+
const stack = [];
|
|
96
|
+
|
|
97
|
+
// Start with root value
|
|
98
|
+
stack.push({
|
|
99
|
+
value,
|
|
100
|
+
path: [],
|
|
101
|
+
segmentIndex: 0
|
|
102
|
+
});
|
|
103
|
+
while (stack.length > 0) {
|
|
104
|
+
const item = stack.pop();
|
|
105
|
+
const {
|
|
106
|
+
value,
|
|
107
|
+
path,
|
|
108
|
+
segmentIndex
|
|
109
|
+
} = item;
|
|
110
|
+
|
|
111
|
+
// If all segments processed, emit result
|
|
112
|
+
if (segmentIndex >= segments.length) {
|
|
113
|
+
const normalizedPath = NormalizedPath.from(path);
|
|
114
|
+
results.push(value);
|
|
115
|
+
if (typeof callback === 'function') {
|
|
116
|
+
callback(value, normalizedPath);
|
|
117
|
+
}
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const segment = segments[segmentIndex];
|
|
121
|
+
|
|
122
|
+
// Collect results from this segment
|
|
123
|
+
const segmentResults = [];
|
|
124
|
+
const emit = (selectedValue, pathSegment) => {
|
|
125
|
+
segmentResults.push({
|
|
126
|
+
value: selectedValue,
|
|
127
|
+
pathSegment
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Apply segment
|
|
132
|
+
(0, _segment.default)(ctx, value, segment, emit);
|
|
133
|
+
|
|
134
|
+
// For descendant segments, also push children for recursive descent
|
|
135
|
+
// Push descendants FIRST so they're processed AFTER current level results (LIFO)
|
|
136
|
+
if (segment.type === 'DescendantSegment') {
|
|
137
|
+
const descendants = [];
|
|
138
|
+
for (const [key, child] of realm.entries(value)) {
|
|
139
|
+
descendants.push({
|
|
140
|
+
value: child,
|
|
141
|
+
pathSegment: key
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Push descendants (in reverse for correct order)
|
|
146
|
+
// They stay at same segment index to continue recursive descent
|
|
147
|
+
for (let i = descendants.length - 1; i >= 0; i -= 1) {
|
|
148
|
+
const {
|
|
149
|
+
value: descendantValue,
|
|
150
|
+
pathSegment
|
|
151
|
+
} = descendants[i];
|
|
152
|
+
stack.push({
|
|
153
|
+
value: descendantValue,
|
|
154
|
+
path: [...path, pathSegment],
|
|
155
|
+
segmentIndex // Same segment for recursive descent
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Push results for next segment (in reverse order for correct output order)
|
|
161
|
+
// Push these AFTER descendants so they're processed FIRST (LIFO = document order)
|
|
162
|
+
for (let i = segmentResults.length - 1; i >= 0; i -= 1) {
|
|
163
|
+
const {
|
|
164
|
+
value: selectedValue,
|
|
165
|
+
pathSegment
|
|
166
|
+
} = segmentResults[i];
|
|
167
|
+
stack.push({
|
|
168
|
+
value: selectedValue,
|
|
169
|
+
path: [...path, pathSegment],
|
|
170
|
+
segmentIndex: segmentIndex + 1
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return results;
|
|
175
|
+
} catch (error) {
|
|
176
|
+
throw new _JSONPathEvaluateError.default('Unexpected error during JSONPath evaluation', {
|
|
177
|
+
cause: error,
|
|
178
|
+
expression
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var _default = exports.default = evaluate;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.default = void 0;
|
|
5
|
+
var _JSONPathError = _interopRequireDefault(require("../../errors/JSONPathError.cjs"));
|
|
6
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
/**
|
|
8
|
+
* Abstract base class for Evaluation Realms.
|
|
9
|
+
*
|
|
10
|
+
* Evaluation Realms provide an abstraction for accessing different data structures,
|
|
11
|
+
* allowing JSONPath evaluation to work with various data types.
|
|
12
|
+
*
|
|
13
|
+
* Subclasses must implement all abstract methods.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
class EvaluationRealm {
|
|
17
|
+
name = '';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check if value is object (has named properties).
|
|
21
|
+
* @param {unknown} value
|
|
22
|
+
* @returns {boolean}
|
|
23
|
+
*/
|
|
24
|
+
isObject(value) {
|
|
25
|
+
throw new _JSONPathError.default('Realm.isObject(value) must be implemented in a subclass');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if value is array (has indexed elements).
|
|
30
|
+
* @param {unknown} value
|
|
31
|
+
* @returns {boolean}
|
|
32
|
+
*/
|
|
33
|
+
isArray(value) {
|
|
34
|
+
throw new _JSONPathError.default('Realm.isArray(value) must be implemented in a subclass');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Check if value is string.
|
|
39
|
+
* @param {unknown} value
|
|
40
|
+
* @returns {boolean}
|
|
41
|
+
*/
|
|
42
|
+
isString(value) {
|
|
43
|
+
throw new _JSONPathError.default('Realm.isString(value) must be implemented in a subclass');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if value is number.
|
|
48
|
+
* @param {unknown} value
|
|
49
|
+
* @returns {boolean}
|
|
50
|
+
*/
|
|
51
|
+
isNumber(value) {
|
|
52
|
+
throw new _JSONPathError.default('Realm.isNumber(value) must be implemented in a subclass');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if value is boolean.
|
|
57
|
+
* @param {unknown} value
|
|
58
|
+
* @returns {boolean}
|
|
59
|
+
*/
|
|
60
|
+
isBoolean(value) {
|
|
61
|
+
throw new _JSONPathError.default('Realm.isBoolean(value) must be implemented in a subclass');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check if value is null.
|
|
66
|
+
* @param {unknown} value
|
|
67
|
+
* @returns {boolean}
|
|
68
|
+
*/
|
|
69
|
+
isNull(value) {
|
|
70
|
+
throw new _JSONPathError.default('Realm.isNull(value) must be implemented in a subclass');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get raw string value for regex operations.
|
|
75
|
+
* @param {unknown} value
|
|
76
|
+
* @returns {string | undefined}
|
|
77
|
+
*/
|
|
78
|
+
getString(value) {
|
|
79
|
+
throw new _JSONPathError.default('Realm.getString(value) must be implemented in a subclass');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get property by name from object value.
|
|
84
|
+
* @param {unknown} value
|
|
85
|
+
* @param {string} key
|
|
86
|
+
* @returns {unknown}
|
|
87
|
+
*/
|
|
88
|
+
getProperty(value, key) {
|
|
89
|
+
throw new _JSONPathError.default('Realm.getProperty(value, key) must be implemented in a subclass');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Check if object value has property.
|
|
94
|
+
* @param {unknown} value
|
|
95
|
+
* @param {string} key
|
|
96
|
+
* @returns {boolean}
|
|
97
|
+
*/
|
|
98
|
+
hasProperty(value, key) {
|
|
99
|
+
throw new _JSONPathError.default('Realm.hasProperty(value, key) must be implemented in a subclass');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get element by index from array value.
|
|
104
|
+
* @param {unknown} value
|
|
105
|
+
* @param {number} index
|
|
106
|
+
* @returns {unknown}
|
|
107
|
+
*/
|
|
108
|
+
getElement(value, index) {
|
|
109
|
+
throw new _JSONPathError.default('Realm.getElement(value, index) must be implemented in a subclass');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get all keys of object value.
|
|
114
|
+
* @param {unknown} value
|
|
115
|
+
* @returns {string[]}
|
|
116
|
+
*/
|
|
117
|
+
getKeys(value) {
|
|
118
|
+
throw new _JSONPathError.default('Realm.getKeys(value) must be implemented in a subclass');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get length of array or object value.
|
|
123
|
+
* @param {unknown} value
|
|
124
|
+
* @returns {number}
|
|
125
|
+
*/
|
|
126
|
+
getLength(value) {
|
|
127
|
+
throw new _JSONPathError.default('Realm.getLength(value) must be implemented in a subclass');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Iterate over entries as [key/index, value] pairs.
|
|
132
|
+
* For objects: yields [key, value] for each member.
|
|
133
|
+
* For arrays: yields [index, value] for each element.
|
|
134
|
+
* For other types: yields nothing.
|
|
135
|
+
* @param {unknown} value
|
|
136
|
+
* @returns {Iterable<[string | number, unknown]>}
|
|
137
|
+
*/
|
|
138
|
+
*entries(value) {
|
|
139
|
+
throw new _JSONPathError.default('Realm.entries(value) must be implemented in a subclass');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Compare two values using the specified operator.
|
|
144
|
+
* Per RFC 9535 Section 2.3.5.2.3.
|
|
145
|
+
* @param {unknown} left
|
|
146
|
+
* @param {string} operator - One of: ==, !=, <, <=, >, >=
|
|
147
|
+
* @param {unknown} right
|
|
148
|
+
* @returns {boolean}
|
|
149
|
+
*/
|
|
150
|
+
compare(left, operator, right) {
|
|
151
|
+
throw new _JSONPathError.default('Realm.compare(left, operator, right) must be implemented in a subclass');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
var _default = exports.default = EvaluationRealm;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.default = void 0;
|
|
5
|
+
var _EvaluationRealm = _interopRequireDefault(require("../EvaluationRealm.cjs"));
|
|
6
|
+
var _guards = require("../../utils/guards.cjs");
|
|
7
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
/**
|
|
9
|
+
* JSON Evaluation Realm for plain JavaScript objects and arrays.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* JSON Evaluation Realm implementation.
|
|
14
|
+
*/
|
|
15
|
+
class JSONEvaluationRealm extends _EvaluationRealm.default {
|
|
16
|
+
name = 'json';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Check if value is object (plain object with named properties).
|
|
20
|
+
* @param {unknown} value
|
|
21
|
+
* @returns {boolean}
|
|
22
|
+
*/
|
|
23
|
+
isObject(value) {
|
|
24
|
+
return (0, _guards.isPlainObject)(value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if value is array.
|
|
29
|
+
* @param {unknown} value
|
|
30
|
+
* @returns {boolean}
|
|
31
|
+
*/
|
|
32
|
+
isArray(value) {
|
|
33
|
+
return (0, _guards.isArray)(value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check if value is string.
|
|
38
|
+
* @param {unknown} value
|
|
39
|
+
* @returns {boolean}
|
|
40
|
+
*/
|
|
41
|
+
isString(value) {
|
|
42
|
+
return (0, _guards.isString)(value);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Check if value is number.
|
|
47
|
+
* @param {unknown} value
|
|
48
|
+
* @returns {boolean}
|
|
49
|
+
*/
|
|
50
|
+
isNumber(value) {
|
|
51
|
+
return (0, _guards.isNumber)(value);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if value is boolean.
|
|
56
|
+
* @param {unknown} value
|
|
57
|
+
* @returns {boolean}
|
|
58
|
+
*/
|
|
59
|
+
isBoolean(value) {
|
|
60
|
+
return (0, _guards.isBoolean)(value);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if value is null.
|
|
65
|
+
* @param {unknown} value
|
|
66
|
+
* @returns {boolean}
|
|
67
|
+
*/
|
|
68
|
+
isNull(value) {
|
|
69
|
+
return (0, _guards.isNull)(value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get raw string value for regex operations.
|
|
74
|
+
* @param {unknown} value
|
|
75
|
+
* @returns {string | undefined}
|
|
76
|
+
*/
|
|
77
|
+
getString(value) {
|
|
78
|
+
return (0, _guards.isString)(value) ? value : undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get property by name from object value.
|
|
83
|
+
* Returns undefined if property doesn't exist (Nothing).
|
|
84
|
+
* @param {unknown} value
|
|
85
|
+
* @param {string} key
|
|
86
|
+
* @returns {unknown}
|
|
87
|
+
*/
|
|
88
|
+
getProperty(value, key) {
|
|
89
|
+
if (!(0, _guards.isPlainObject)(value)) return undefined;
|
|
90
|
+
return Object.hasOwn(value, key) ? value[key] : undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check if object value has property.
|
|
95
|
+
* @param {unknown} value
|
|
96
|
+
* @param {string} key
|
|
97
|
+
* @returns {boolean}
|
|
98
|
+
*/
|
|
99
|
+
hasProperty(value, key) {
|
|
100
|
+
if (!(0, _guards.isPlainObject)(value)) return false;
|
|
101
|
+
return Object.hasOwn(value, key);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get element by index from array value.
|
|
106
|
+
* Returns undefined if index out of bounds (Nothing).
|
|
107
|
+
* @param {unknown} value
|
|
108
|
+
* @param {number} index
|
|
109
|
+
* @returns {unknown}
|
|
110
|
+
*/
|
|
111
|
+
getElement(value, index) {
|
|
112
|
+
if (!(0, _guards.isArray)(value)) return undefined;
|
|
113
|
+
if (index < 0 || index >= value.length) return undefined;
|
|
114
|
+
return value[index];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get all keys of object value.
|
|
119
|
+
* @param {unknown} value
|
|
120
|
+
* @returns {string[]}
|
|
121
|
+
*/
|
|
122
|
+
getKeys(value) {
|
|
123
|
+
if (!(0, _guards.isPlainObject)(value)) return [];
|
|
124
|
+
return Object.keys(value);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get length of value.
|
|
129
|
+
* Per RFC 9535 Section 2.4.5:
|
|
130
|
+
* - String: number of Unicode scalar values (not UTF-16 code units)
|
|
131
|
+
* - Array: number of elements
|
|
132
|
+
* - Object: number of members
|
|
133
|
+
* @param {unknown} value
|
|
134
|
+
* @returns {number}
|
|
135
|
+
*/
|
|
136
|
+
getLength(value) {
|
|
137
|
+
if ((0, _guards.isString)(value)) return [...value].length;
|
|
138
|
+
if ((0, _guards.isArray)(value)) return value.length;
|
|
139
|
+
if ((0, _guards.isPlainObject)(value)) return Object.keys(value).length;
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Iterate over entries as [key/index, value] pairs.
|
|
145
|
+
* For objects: yields [key, value] for each member.
|
|
146
|
+
* For arrays: yields [index, value] for each element.
|
|
147
|
+
* For other types: yields nothing.
|
|
148
|
+
* @param {unknown} value
|
|
149
|
+
* @returns {Iterable<[string | number, unknown]>}
|
|
150
|
+
*/
|
|
151
|
+
*entries(value) {
|
|
152
|
+
if (this.isArray(value)) {
|
|
153
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
154
|
+
yield [i, value[i]];
|
|
155
|
+
}
|
|
156
|
+
} else if (this.isObject(value)) {
|
|
157
|
+
for (const key of Object.keys(value)) {
|
|
158
|
+
yield [key, value[key]];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Deep equality check for JSON values.
|
|
165
|
+
* @param {unknown} a
|
|
166
|
+
* @param {unknown} b
|
|
167
|
+
* @returns {boolean}
|
|
168
|
+
*/
|
|
169
|
+
#deepEqual(a, b) {
|
|
170
|
+
// Primitive comparison
|
|
171
|
+
if (a === b) return true;
|
|
172
|
+
|
|
173
|
+
// Nothing comparison
|
|
174
|
+
if ((0, _guards.isNothing)(a) && (0, _guards.isNothing)(b)) return true;
|
|
175
|
+
if ((0, _guards.isNothing)(a) || (0, _guards.isNothing)(b)) return false;
|
|
176
|
+
|
|
177
|
+
// Null comparison
|
|
178
|
+
if ((0, _guards.isNull)(a) && (0, _guards.isNull)(b)) return true;
|
|
179
|
+
if ((0, _guards.isNull)(a) || (0, _guards.isNull)(b)) return false;
|
|
180
|
+
|
|
181
|
+
// Type must match for complex types
|
|
182
|
+
if (typeof a !== typeof b) return false;
|
|
183
|
+
|
|
184
|
+
// Array comparison
|
|
185
|
+
if ((0, _guards.isArray)(a) && (0, _guards.isArray)(b)) {
|
|
186
|
+
if (a.length !== b.length) return false;
|
|
187
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
188
|
+
if (!this.#deepEqual(a[i], b[i])) return false;
|
|
189
|
+
}
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Object comparison
|
|
194
|
+
if ((0, _guards.isPlainObject)(a) && (0, _guards.isPlainObject)(b)) {
|
|
195
|
+
const keysA = Object.keys(a);
|
|
196
|
+
const keysB = Object.keys(b);
|
|
197
|
+
if (keysA.length !== keysB.length) return false;
|
|
198
|
+
for (const key of keysA) {
|
|
199
|
+
if (!Object.hasOwn(b, key)) return false;
|
|
200
|
+
if (!this.#deepEqual(a[key], b[key])) return false;
|
|
201
|
+
}
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Compare two values using the specified operator.
|
|
209
|
+
* Per RFC 9535 Section 2.3.5.2.3.
|
|
210
|
+
* @param {unknown} left
|
|
211
|
+
* @param {string} operator - One of: ==, !=, <, <=, >, >=
|
|
212
|
+
* @param {unknown} right
|
|
213
|
+
* @returns {boolean}
|
|
214
|
+
*/
|
|
215
|
+
compare(left, operator, right) {
|
|
216
|
+
switch (operator) {
|
|
217
|
+
case '==':
|
|
218
|
+
return this.#deepEqual(left, right);
|
|
219
|
+
case '!=':
|
|
220
|
+
return !this.#deepEqual(left, right);
|
|
221
|
+
case '<':
|
|
222
|
+
if ((0, _guards.isNothing)(left) || (0, _guards.isNothing)(right)) return false;
|
|
223
|
+
if ((0, _guards.isNumber)(left) && (0, _guards.isNumber)(right)) return left < right;
|
|
224
|
+
if ((0, _guards.isString)(left) && (0, _guards.isString)(right)) return left < right;
|
|
225
|
+
return false;
|
|
226
|
+
case '<=':
|
|
227
|
+
if ((0, _guards.isNothing)(left) || (0, _guards.isNothing)(right)) return false;
|
|
228
|
+
if ((0, _guards.isNumber)(left) && (0, _guards.isNumber)(right)) return left <= right;
|
|
229
|
+
if ((0, _guards.isString)(left) && (0, _guards.isString)(right)) return left <= right;
|
|
230
|
+
return this.#deepEqual(left, right);
|
|
231
|
+
case '>':
|
|
232
|
+
if ((0, _guards.isNothing)(left) || (0, _guards.isNothing)(right)) return false;
|
|
233
|
+
if ((0, _guards.isNumber)(left) && (0, _guards.isNumber)(right)) return left > right;
|
|
234
|
+
if ((0, _guards.isString)(left) && (0, _guards.isString)(right)) return left > right;
|
|
235
|
+
return false;
|
|
236
|
+
case '>=':
|
|
237
|
+
if ((0, _guards.isNothing)(left) || (0, _guards.isNothing)(right)) return false;
|
|
238
|
+
if ((0, _guards.isNumber)(left) && (0, _guards.isNumber)(right)) return left >= right;
|
|
239
|
+
if ((0, _guards.isString)(left) && (0, _guards.isString)(right)) return left >= right;
|
|
240
|
+
return this.#deepEqual(left, right);
|
|
241
|
+
default:
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
var _default = exports.default = JSONEvaluationRealm;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.isString = exports.isPlainObject = exports.isObject = exports.isNumber = exports.isNull = exports.isNothing = exports.isNodelist = exports.isJsonValue = exports.isBoolean = exports.isArray = exports.coerceToValueType = void 0;
|
|
5
|
+
/**
|
|
6
|
+
* Type guards for JSON value types.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const objectTag = '[object Object]';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if value is an array.
|
|
13
|
+
* @param {unknown} value
|
|
14
|
+
* @returns {value is unknown[]}
|
|
15
|
+
*/
|
|
16
|
+
const isArray = value => Array.isArray(value);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Check if value is an object (not null, not array).
|
|
20
|
+
* @param {unknown} value
|
|
21
|
+
* @returns {value is object}
|
|
22
|
+
*/
|
|
23
|
+
exports.isArray = isArray;
|
|
24
|
+
const isObject = value => typeof value === 'object' && value !== null && !isArray(value);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if value is a plain object (created by Object constructor or object literal).
|
|
28
|
+
* @param {unknown} value
|
|
29
|
+
* @returns {value is Record<string, unknown>}
|
|
30
|
+
*/
|
|
31
|
+
exports.isObject = isObject;
|
|
32
|
+
const isPlainObject = value => {
|
|
33
|
+
if (!isObject(value)) return false;
|
|
34
|
+
const proto = Object.getPrototypeOf(value);
|
|
35
|
+
if (proto === null) return true;
|
|
36
|
+
return proto.constructor === Object && Object.prototype.toString.call(value) === objectTag;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Check if value is a string.
|
|
41
|
+
* @param {unknown} value
|
|
42
|
+
* @returns {value is string}
|
|
43
|
+
*/
|
|
44
|
+
exports.isPlainObject = isPlainObject;
|
|
45
|
+
const isString = value => typeof value === 'string';
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Check if value is a number.
|
|
49
|
+
* @param {unknown} value
|
|
50
|
+
* @returns {value is number}
|
|
51
|
+
*/
|
|
52
|
+
exports.isString = isString;
|
|
53
|
+
const isNumber = value => typeof value === 'number' && Number.isFinite(value);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if value is a boolean.
|
|
57
|
+
* @param {unknown} value
|
|
58
|
+
* @returns {value is boolean}
|
|
59
|
+
*/
|
|
60
|
+
exports.isNumber = isNumber;
|
|
61
|
+
const isBoolean = value => typeof value === 'boolean';
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if value is null.
|
|
65
|
+
* @param {unknown} value
|
|
66
|
+
* @returns {value is null}
|
|
67
|
+
*/
|
|
68
|
+
exports.isBoolean = isBoolean;
|
|
69
|
+
const isNull = value => value === null;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if value represents Nothing (undefined).
|
|
73
|
+
* Per RFC 9535, Nothing is used when a query returns no value.
|
|
74
|
+
* We use undefined since JSON has no undefined value.
|
|
75
|
+
* @param {unknown} value
|
|
76
|
+
* @returns {value is undefined}
|
|
77
|
+
*/
|
|
78
|
+
exports.isNull = isNull;
|
|
79
|
+
const isNothing = value => value === undefined;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Check if value is a valid JSON value.
|
|
83
|
+
* @param {unknown} value
|
|
84
|
+
* @returns {boolean}
|
|
85
|
+
*/
|
|
86
|
+
exports.isNothing = isNothing;
|
|
87
|
+
const isJsonValue = value => {
|
|
88
|
+
if (isNull(value) || isBoolean(value) || isString(value) || isNumber(value)) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
if (isArray(value)) {
|
|
92
|
+
return value.every(isJsonValue);
|
|
93
|
+
}
|
|
94
|
+
if (isPlainObject(value)) {
|
|
95
|
+
return Object.values(value).every(isJsonValue);
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Check if value is a nodelist (marked array from filter query).
|
|
102
|
+
* @param {unknown} value
|
|
103
|
+
* @returns {boolean}
|
|
104
|
+
*/
|
|
105
|
+
exports.isJsonValue = isJsonValue;
|
|
106
|
+
const isNodelist = value => isArray(value) && value._isNodelist === true;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Coerce a nodelist to a single value (ValueType).
|
|
110
|
+
* Per RFC 9535 Section 2.4.1: if function expects ValueType and receives NodesType,
|
|
111
|
+
* auto-convert: single node -> that node's value, otherwise -> Nothing.
|
|
112
|
+
*
|
|
113
|
+
* @param {unknown} value - Input value (may be a nodelist)
|
|
114
|
+
* @returns {unknown} - Coerced value or Nothing (undefined)
|
|
115
|
+
*/
|
|
116
|
+
exports.isNodelist = isNodelist;
|
|
117
|
+
const coerceToValueType = value => {
|
|
118
|
+
if (isNodelist(value)) {
|
|
119
|
+
// Single node: unwrap and return value
|
|
120
|
+
if (value.length === 1) {
|
|
121
|
+
return value[0];
|
|
122
|
+
}
|
|
123
|
+
// Empty or multiple nodes: return Nothing
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
// Not a nodelist, return as-is
|
|
127
|
+
return value;
|
|
128
|
+
};
|
|
129
|
+
exports.coerceToValueType = coerceToValueType;
|