@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.
Files changed (84) hide show
  1. package/README.md +225 -18
  2. package/cjs/errors/JSONNormalizedPathError.cjs +8 -0
  3. package/cjs/errors/JSONPathError.cjs +1 -1
  4. package/cjs/errors/{JSONPathCompileError.cjs → JSONPathEvaluateError.cjs} +2 -2
  5. package/cjs/evaluate/evaluators/comparable.cjs +44 -0
  6. package/cjs/evaluate/evaluators/comparison-expr.cjs +37 -0
  7. package/cjs/evaluate/evaluators/filter-query.cjs +182 -0
  8. package/cjs/evaluate/evaluators/function-expr.cjs +106 -0
  9. package/cjs/evaluate/evaluators/literal.cjs +25 -0
  10. package/cjs/evaluate/evaluators/logical-expr.cjs +96 -0
  11. package/cjs/evaluate/evaluators/singular-query.cjs +103 -0
  12. package/cjs/evaluate/functions/count.cjs +35 -0
  13. package/cjs/evaluate/functions/index.cjs +15 -0
  14. package/cjs/evaluate/functions/length.cjs +42 -0
  15. package/cjs/evaluate/functions/match.cjs +49 -0
  16. package/cjs/evaluate/functions/search.cjs +49 -0
  17. package/cjs/evaluate/functions/value.cjs +36 -0
  18. package/cjs/evaluate/index.cjs +182 -0
  19. package/cjs/evaluate/realms/EvaluationRealm.cjs +154 -0
  20. package/cjs/evaluate/realms/json/index.cjs +246 -0
  21. package/cjs/evaluate/utils/guards.cjs +129 -0
  22. package/cjs/evaluate/utils/i-regexp.cjs +118 -0
  23. package/cjs/evaluate/visitors/bracketed-selection.cjs +35 -0
  24. package/cjs/evaluate/visitors/filter-selector.cjs +43 -0
  25. package/cjs/evaluate/visitors/index-selector.cjs +55 -0
  26. package/cjs/evaluate/visitors/name-selector.cjs +38 -0
  27. package/cjs/evaluate/visitors/segment.cjs +99 -0
  28. package/cjs/evaluate/visitors/selector.cjs +47 -0
  29. package/cjs/evaluate/visitors/slice-selector.cjs +115 -0
  30. package/cjs/evaluate/visitors/wildcard-selector.cjs +32 -0
  31. package/cjs/index.cjs +16 -7
  32. package/cjs/normalized-path.cjs +145 -0
  33. package/cjs/parse/callbacks/cst.cjs +2 -4
  34. package/cjs/parse/index.cjs +3 -1
  35. package/cjs/parse/translators/ASTTranslator/index.cjs +1 -1
  36. package/cjs/parse/translators/ASTTranslator/transformers.cjs +246 -5
  37. package/cjs/parse/translators/CSTOptimizedTranslator.cjs +1 -3
  38. package/cjs/parse/translators/CSTTranslator.cjs +1 -2
  39. package/cjs/test/index.cjs +4 -2
  40. package/es/errors/JSONNormalizedPathError.mjs +3 -0
  41. package/es/errors/JSONPathError.mjs +1 -1
  42. package/es/errors/JSONPathEvaluateError.mjs +3 -0
  43. package/es/evaluate/evaluators/comparable.mjs +38 -0
  44. package/es/evaluate/evaluators/comparison-expr.mjs +31 -0
  45. package/es/evaluate/evaluators/filter-query.mjs +175 -0
  46. package/es/evaluate/evaluators/function-expr.mjs +99 -0
  47. package/es/evaluate/evaluators/literal.mjs +21 -0
  48. package/es/evaluate/evaluators/logical-expr.mjs +89 -0
  49. package/es/evaluate/evaluators/singular-query.mjs +97 -0
  50. package/es/evaluate/functions/count.mjs +30 -0
  51. package/es/evaluate/functions/index.mjs +13 -0
  52. package/es/evaluate/functions/length.mjs +37 -0
  53. package/es/evaluate/functions/match.mjs +44 -0
  54. package/es/evaluate/functions/search.mjs +44 -0
  55. package/es/evaluate/functions/value.mjs +31 -0
  56. package/es/evaluate/index.mjs +174 -0
  57. package/es/evaluate/realms/EvaluationRealm.mjs +148 -0
  58. package/es/evaluate/realms/json/index.mjs +240 -0
  59. package/es/evaluate/utils/guards.mjs +114 -0
  60. package/es/evaluate/utils/i-regexp.mjs +113 -0
  61. package/es/evaluate/visitors/bracketed-selection.mjs +29 -0
  62. package/es/evaluate/visitors/filter-selector.mjs +37 -0
  63. package/es/evaluate/visitors/index-selector.mjs +51 -0
  64. package/es/evaluate/visitors/name-selector.mjs +34 -0
  65. package/es/evaluate/visitors/segment.mjs +91 -0
  66. package/es/evaluate/visitors/selector.mjs +41 -0
  67. package/es/evaluate/visitors/slice-selector.mjs +111 -0
  68. package/es/evaluate/visitors/wildcard-selector.mjs +28 -0
  69. package/es/index.mjs +7 -3
  70. package/es/normalized-path.mjs +136 -0
  71. package/es/parse/callbacks/cst.mjs +2 -4
  72. package/es/parse/index.mjs +3 -1
  73. package/es/parse/translators/ASTTranslator/index.mjs +1 -1
  74. package/es/parse/translators/ASTTranslator/transformers.mjs +246 -5
  75. package/es/parse/translators/CSTOptimizedTranslator.mjs +1 -3
  76. package/es/parse/translators/CSTTranslator.mjs +1 -2
  77. package/es/test/index.mjs +4 -2
  78. package/package.json +4 -2
  79. package/types/index.d.ts +114 -11
  80. package/cjs/compile.cjs +0 -50
  81. package/cjs/escape.cjs +0 -59
  82. package/es/compile.mjs +0 -45
  83. package/es/errors/JSONPathCompileError.mjs +0 -3
  84. 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;