@speclynx/apidom-json-path 4.0.2 → 4.0.3

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/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [4.0.3](https://github.com/speclynx/apidom/compare/v4.0.2...v4.0.3) (2026-03-11)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **release:** fix v4.0.2 failed release ([b4dc1c4](https://github.com/speclynx/apidom/commit/b4dc1c48e8d9b2986a70e49b5554eb0a166d7528))
11
+
6
12
  ## [4.0.2](https://github.com/speclynx/apidom/compare/v4.0.1...v4.0.2) (2026-03-11)
7
13
 
8
14
  **Note:** Version bump only for package @speclynx/apidom-json-path
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@speclynx/apidom-json-path",
3
- "version": "4.0.2",
3
+ "version": "4.0.3",
4
4
  "description": "Evaluate JSONPath expressions against ApiDOM.",
5
5
  "keywords": [
6
6
  "apidom",
@@ -55,13 +55,12 @@
55
55
  "license": "Apache-2.0",
56
56
  "dependencies": {
57
57
  "@babel/runtime-corejs3": "^7.28.4",
58
- "@speclynx/apidom-datamodel": "4.0.2",
59
- "@speclynx/apidom-error": "4.0.2",
58
+ "@speclynx/apidom-datamodel": "4.0.3",
59
+ "@speclynx/apidom-error": "4.0.3",
60
60
  "@swaggerexpert/jsonpath": "^4.0.3"
61
61
  },
62
62
  "files": [
63
- "src/**/*.mjs",
64
- "src/**/*.cjs",
63
+ "src/",
65
64
  "dist/",
66
65
  "types/apidom-json-path.d.ts",
67
66
  "LICENSES",
@@ -69,5 +68,5 @@
69
68
  "README.md",
70
69
  "CHANGELOG.md"
71
70
  ],
72
- "gitHead": "af1b05d4d5e48a11a3a03cd5699324e0f1b62765"
71
+ "gitHead": "6ccfa09c02232516215e7de3ead276641957e626"
73
72
  }
package/src/index.cjs ADDED
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
4
+ exports.__esModule = true;
5
+ exports.test = exports.parse = exports.functions = exports.evaluate = exports.XMLTranslator = exports.NormalizedPath = exports.JSONPathParseError = exports.JSONPathEvaluateError = exports.JSONPathError = exports.JSONNormalizedPathError = exports.JSONEvaluationRealm = exports.Grammar = exports.EvaluationRealm = exports.CSTTranslator = exports.CSTOptimizedTranslator = exports.ASTTranslator = void 0;
6
+ var _jsonpath = require("@swaggerexpert/jsonpath");
7
+ exports.parse = _jsonpath.parse;
8
+ exports.CSTTranslator = _jsonpath.CSTTranslator;
9
+ exports.CSTOptimizedTranslator = _jsonpath.CSTOptimizedTranslator;
10
+ exports.ASTTranslator = _jsonpath.ASTTranslator;
11
+ exports.XMLTranslator = _jsonpath.XMLTranslator;
12
+ exports.test = _jsonpath.test;
13
+ exports.NormalizedPath = _jsonpath.NormalizedPath;
14
+ exports.functions = _jsonpath.functions;
15
+ exports.EvaluationRealm = _jsonpath.EvaluationRealm;
16
+ exports.JSONEvaluationRealm = _jsonpath.JSONEvaluationRealm;
17
+ exports.Grammar = _jsonpath.Grammar;
18
+ exports.JSONPathError = _jsonpath.JSONPathError;
19
+ exports.JSONPathParseError = _jsonpath.JSONPathParseError;
20
+ exports.JSONNormalizedPathError = _jsonpath.JSONNormalizedPathError;
21
+ exports.JSONPathEvaluateError = _jsonpath.JSONPathEvaluateError;
22
+ var _realm = _interopRequireDefault(require("./realm.cjs"));
23
+ exports.ApiDOMEvaluationRealm = _realm.default;
24
+ const realm = new _realm.default();
25
+
26
+ /**
27
+ * Options for ApiDOM JSONPath evaluation.
28
+ * @public
29
+ */
30
+
31
+ /**
32
+ * Evaluate a JSONPath expression against an ApiDOM element.
33
+ *
34
+ * @param value - ApiDOM element to query
35
+ * @param expression - JSONPath expression
36
+ * @param options - Evaluation options
37
+ * @returns Array of matched values
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * import { ObjectElement } from '@speclynx/apidom-datamodel';
42
+ * import { evaluate } from '@speclynx/apidom-json-path';
43
+ *
44
+ * const element = new ObjectElement({ a: { b: [1, 2, 3] } });
45
+ * const results = evaluate(element, '$.a.b[*]');
46
+ * // => [NumberElement(1), NumberElement(2), NumberElement(3)]
47
+ * ```
48
+ *
49
+ * @public
50
+ */
51
+ const evaluate = (value, expression, options = {}) => {
52
+ return (0, _jsonpath.evaluate)(value, expression, {
53
+ ...options,
54
+ realm
55
+ });
56
+ };
57
+
58
+ /**
59
+ * Re-export all types
60
+ */
61
+ exports.evaluate = evaluate;
package/src/index.mjs ADDED
@@ -0,0 +1,65 @@
1
+ import { evaluate as baseEvaluate } from '@swaggerexpert/jsonpath';
2
+ export {
3
+ /**
4
+ * Parsing
5
+ */
6
+ parse, CSTTranslator, CSTOptimizedTranslator, ASTTranslator, XMLTranslator,
7
+ /**
8
+ * Testing
9
+ */
10
+ test,
11
+ /**
12
+ * Normalized Path
13
+ */
14
+ NormalizedPath,
15
+ /**
16
+ * Evaluation
17
+ */
18
+ functions, EvaluationRealm, JSONEvaluationRealm,
19
+ /**
20
+ * Grammar
21
+ */
22
+ Grammar,
23
+ /**
24
+ * Errors
25
+ */
26
+ JSONPathError, JSONPathParseError, JSONNormalizedPathError, JSONPathEvaluateError } from '@swaggerexpert/jsonpath';
27
+ import ApiDOMEvaluationRealm from "./realm.mjs";
28
+ export { ApiDOMEvaluationRealm };
29
+ const realm = new ApiDOMEvaluationRealm();
30
+
31
+ /**
32
+ * Options for ApiDOM JSONPath evaluation.
33
+ * @public
34
+ */
35
+
36
+ /**
37
+ * Evaluate a JSONPath expression against an ApiDOM element.
38
+ *
39
+ * @param value - ApiDOM element to query
40
+ * @param expression - JSONPath expression
41
+ * @param options - Evaluation options
42
+ * @returns Array of matched values
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * import { ObjectElement } from '@speclynx/apidom-datamodel';
47
+ * import { evaluate } from '@speclynx/apidom-json-path';
48
+ *
49
+ * const element = new ObjectElement({ a: { b: [1, 2, 3] } });
50
+ * const results = evaluate(element, '$.a.b[*]');
51
+ * // => [NumberElement(1), NumberElement(2), NumberElement(3)]
52
+ * ```
53
+ *
54
+ * @public
55
+ */
56
+ export const evaluate = (value, expression, options = {}) => {
57
+ return baseEvaluate(value, expression, {
58
+ ...options,
59
+ realm
60
+ });
61
+ };
62
+
63
+ /**
64
+ * Re-export all types
65
+ */
package/src/index.ts ADDED
@@ -0,0 +1,94 @@
1
+ import { evaluate as baseEvaluate } from '@swaggerexpert/jsonpath';
2
+
3
+ export {
4
+ /**
5
+ * Parsing
6
+ */
7
+ parse,
8
+ CSTTranslator,
9
+ CSTOptimizedTranslator,
10
+ ASTTranslator,
11
+ XMLTranslator,
12
+ /**
13
+ * Testing
14
+ */
15
+ test,
16
+ /**
17
+ * Normalized Path
18
+ */
19
+ NormalizedPath,
20
+ /**
21
+ * Evaluation
22
+ */
23
+ functions,
24
+ EvaluationRealm,
25
+ JSONEvaluationRealm,
26
+ /**
27
+ * Grammar
28
+ */
29
+ Grammar,
30
+ /**
31
+ * Errors
32
+ */
33
+ JSONPathError,
34
+ JSONPathParseError,
35
+ JSONNormalizedPathError,
36
+ JSONPathEvaluateError,
37
+ } from '@swaggerexpert/jsonpath';
38
+
39
+ import ApiDOMEvaluationRealm from './realm.ts';
40
+
41
+ export { ApiDOMEvaluationRealm };
42
+
43
+ const realm = new ApiDOMEvaluationRealm();
44
+
45
+ /**
46
+ * Options for ApiDOM JSONPath evaluation.
47
+ * @public
48
+ */
49
+ export interface ApiDOMEvaluateOptions {
50
+ /**
51
+ * Optional callback called for each match.
52
+ * @param value - The matched value
53
+ * @param normalizedPath - The normalized path to the match
54
+ */
55
+ callback?: (value: unknown, normalizedPath: string) => void;
56
+ /**
57
+ * Optional custom function registry.
58
+ * Can extend or override built-in functions (length, count, match, search, value).
59
+ */
60
+ functions?: Record<string, (...args: unknown[]) => unknown>;
61
+ }
62
+
63
+ /**
64
+ * Evaluate a JSONPath expression against an ApiDOM element.
65
+ *
66
+ * @param value - ApiDOM element to query
67
+ * @param expression - JSONPath expression
68
+ * @param options - Evaluation options
69
+ * @returns Array of matched values
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * import { ObjectElement } from '@speclynx/apidom-datamodel';
74
+ * import { evaluate } from '@speclynx/apidom-json-path';
75
+ *
76
+ * const element = new ObjectElement({ a: { b: [1, 2, 3] } });
77
+ * const results = evaluate(element, '$.a.b[*]');
78
+ * // => [NumberElement(1), NumberElement(2), NumberElement(3)]
79
+ * ```
80
+ *
81
+ * @public
82
+ */
83
+ export const evaluate = <T = unknown>(
84
+ value: unknown,
85
+ expression: string,
86
+ options: ApiDOMEvaluateOptions = {},
87
+ ): T[] => {
88
+ return baseEvaluate(value, expression, { ...options, realm });
89
+ };
90
+
91
+ /**
92
+ * Re-export all types
93
+ */
94
+ export type * from '@swaggerexpert/jsonpath';
package/src/realm.cjs ADDED
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.default = void 0;
5
+ var _apidomDatamodel = require("@speclynx/apidom-datamodel");
6
+ var _jsonpath = require("@swaggerexpert/jsonpath");
7
+ /**
8
+ * ApiDOM Evaluation Realm implementation for JSONPath.
9
+ * @public
10
+ */
11
+ class ApiDOMEvaluationRealm extends _jsonpath.EvaluationRealm {
12
+ name = 'apidom';
13
+ isObject(value) {
14
+ return (0, _apidomDatamodel.isObjectElement)(value);
15
+ }
16
+ isArray(value) {
17
+ return (0, _apidomDatamodel.isArrayElement)(value);
18
+ }
19
+ isString(value) {
20
+ return (0, _apidomDatamodel.isStringElement)(value);
21
+ }
22
+ isNumber(value) {
23
+ return (0, _apidomDatamodel.isNumberElement)(value);
24
+ }
25
+ isBoolean(value) {
26
+ return (0, _apidomDatamodel.isBooleanElement)(value);
27
+ }
28
+ isNull(value) {
29
+ return (0, _apidomDatamodel.isNullElement)(value);
30
+ }
31
+ getString(value) {
32
+ if ((0, _apidomDatamodel.isStringElement)(value)) return value.toValue();
33
+ if (typeof value === 'string') return value;
34
+ return undefined;
35
+ }
36
+ getProperty(value, key) {
37
+ if (!(0, _apidomDatamodel.isObjectElement)(value)) return undefined;
38
+ if (!value.hasKey(key)) return undefined;
39
+ return value.get(key);
40
+ }
41
+ hasProperty(value, key) {
42
+ if (!(0, _apidomDatamodel.isObjectElement)(value)) return false;
43
+ return value.hasKey(key);
44
+ }
45
+ getElement(value, index) {
46
+ if (!(0, _apidomDatamodel.isArrayElement)(value)) return undefined;
47
+ if (index < 0 || index >= value.length) return undefined;
48
+ return value.get(index);
49
+ }
50
+ getKeys(value) {
51
+ if (!(0, _apidomDatamodel.isObjectElement)(value)) return [];
52
+ return value.keys();
53
+ }
54
+ getLength(value) {
55
+ if ((0, _apidomDatamodel.isStringElement)(value)) return [...value.toValue()].length;
56
+ if ((0, _apidomDatamodel.isArrayElement)(value)) return value.length;
57
+ if ((0, _apidomDatamodel.isObjectElement)(value)) return value.length;
58
+ if (typeof value === 'string') return [...value].length;
59
+ if (Array.isArray(value)) return value.length;
60
+ return 0;
61
+ }
62
+ *entries(value) {
63
+ if ((0, _apidomDatamodel.isArrayElement)(value)) {
64
+ let index = 0;
65
+ for (const item of value) {
66
+ yield [index, item];
67
+ index += 1;
68
+ }
69
+ } else if ((0, _apidomDatamodel.isObjectElement)(value)) {
70
+ for (const member of value) {
71
+ yield [member.key.toValue(), member.value];
72
+ }
73
+ }
74
+ }
75
+ compare(left, operator, right) {
76
+ // Handle Nothing (undefined) comparisons
77
+ if (left === undefined || right === undefined) {
78
+ if (operator === '==') return left === undefined && right === undefined;
79
+ if (operator === '!=') return !(left === undefined && right === undefined);
80
+ return false;
81
+ }
82
+
83
+ // Check if both are numbers (elements or primitives)
84
+ const leftIsNumber = (0, _apidomDatamodel.isNumberElement)(left) || typeof left === 'number';
85
+ const rightIsNumber = (0, _apidomDatamodel.isNumberElement)(right) || typeof right === 'number';
86
+
87
+ // For numeric values, use primitive comparison with -0 normalization (RFC 9535)
88
+ if (leftIsNumber && rightIsNumber) {
89
+ const leftPrimitive = (0, _apidomDatamodel.isNumberElement)(left) ? left.toValue() : left;
90
+ const rightPrimitive = (0, _apidomDatamodel.isNumberElement)(right) ? right.toValue() : right;
91
+ const leftVal = Object.is(leftPrimitive, -0) ? 0 : leftPrimitive;
92
+ const rightVal = Object.is(rightPrimitive, -0) ? 0 : rightPrimitive;
93
+ switch (operator) {
94
+ case '==':
95
+ return leftVal === rightVal;
96
+ case '!=':
97
+ return leftVal !== rightVal;
98
+ case '<':
99
+ return leftVal < rightVal;
100
+ case '>':
101
+ return leftVal > rightVal;
102
+ case '<=':
103
+ return leftVal <= rightVal;
104
+ case '>=':
105
+ return leftVal >= rightVal;
106
+ default:
107
+ return false;
108
+ }
109
+ }
110
+
111
+ // Check if both are strings (elements or primitives)
112
+ const leftIsString = (0, _apidomDatamodel.isStringElement)(left) || typeof left === 'string';
113
+ const rightIsString = (0, _apidomDatamodel.isStringElement)(right) || typeof right === 'string';
114
+
115
+ // For string values, use primitive comparison
116
+ if (leftIsString && rightIsString) {
117
+ const leftVal = (0, _apidomDatamodel.isStringElement)(left) ? left.toValue() : left;
118
+ const rightVal = (0, _apidomDatamodel.isStringElement)(right) ? right.toValue() : right;
119
+ switch (operator) {
120
+ case '==':
121
+ return leftVal === rightVal;
122
+ case '!=':
123
+ return leftVal !== rightVal;
124
+ case '<':
125
+ return leftVal < rightVal;
126
+ case '>':
127
+ return leftVal > rightVal;
128
+ case '<=':
129
+ return leftVal <= rightVal;
130
+ case '>=':
131
+ return leftVal >= rightVal;
132
+ default:
133
+ return false;
134
+ }
135
+ }
136
+
137
+ // For other types (booleans, nulls), only equality operators are defined (RFC 9535)
138
+ if (operator === '==') {
139
+ return (0, _apidomDatamodel.refract)(left).equals(right);
140
+ }
141
+ if (operator === '!=') {
142
+ return !(0, _apidomDatamodel.refract)(left).equals(right);
143
+ }
144
+ // Comparison operators (<, >, <=, >=) are not defined for non-numeric, non-string types
145
+ return false;
146
+ }
147
+ }
148
+ var _default = exports.default = ApiDOMEvaluationRealm;
package/src/realm.mjs ADDED
@@ -0,0 +1,145 @@
1
+ import { isObjectElement, isArrayElement, isStringElement, isNumberElement, isBooleanElement, isNullElement, refract } from '@speclynx/apidom-datamodel';
2
+ import { EvaluationRealm } from '@swaggerexpert/jsonpath';
3
+
4
+ /**
5
+ * ApiDOM Evaluation Realm implementation for JSONPath.
6
+ * @public
7
+ */
8
+ class ApiDOMEvaluationRealm extends EvaluationRealm {
9
+ name = 'apidom';
10
+ isObject(value) {
11
+ return isObjectElement(value);
12
+ }
13
+ isArray(value) {
14
+ return isArrayElement(value);
15
+ }
16
+ isString(value) {
17
+ return isStringElement(value);
18
+ }
19
+ isNumber(value) {
20
+ return isNumberElement(value);
21
+ }
22
+ isBoolean(value) {
23
+ return isBooleanElement(value);
24
+ }
25
+ isNull(value) {
26
+ return isNullElement(value);
27
+ }
28
+ getString(value) {
29
+ if (isStringElement(value)) return value.toValue();
30
+ if (typeof value === 'string') return value;
31
+ return undefined;
32
+ }
33
+ getProperty(value, key) {
34
+ if (!isObjectElement(value)) return undefined;
35
+ if (!value.hasKey(key)) return undefined;
36
+ return value.get(key);
37
+ }
38
+ hasProperty(value, key) {
39
+ if (!isObjectElement(value)) return false;
40
+ return value.hasKey(key);
41
+ }
42
+ getElement(value, index) {
43
+ if (!isArrayElement(value)) return undefined;
44
+ if (index < 0 || index >= value.length) return undefined;
45
+ return value.get(index);
46
+ }
47
+ getKeys(value) {
48
+ if (!isObjectElement(value)) return [];
49
+ return value.keys();
50
+ }
51
+ getLength(value) {
52
+ if (isStringElement(value)) return [...value.toValue()].length;
53
+ if (isArrayElement(value)) return value.length;
54
+ if (isObjectElement(value)) return value.length;
55
+ if (typeof value === 'string') return [...value].length;
56
+ if (Array.isArray(value)) return value.length;
57
+ return 0;
58
+ }
59
+ *entries(value) {
60
+ if (isArrayElement(value)) {
61
+ let index = 0;
62
+ for (const item of value) {
63
+ yield [index, item];
64
+ index += 1;
65
+ }
66
+ } else if (isObjectElement(value)) {
67
+ for (const member of value) {
68
+ yield [member.key.toValue(), member.value];
69
+ }
70
+ }
71
+ }
72
+ compare(left, operator, right) {
73
+ // Handle Nothing (undefined) comparisons
74
+ if (left === undefined || right === undefined) {
75
+ if (operator === '==') return left === undefined && right === undefined;
76
+ if (operator === '!=') return !(left === undefined && right === undefined);
77
+ return false;
78
+ }
79
+
80
+ // Check if both are numbers (elements or primitives)
81
+ const leftIsNumber = isNumberElement(left) || typeof left === 'number';
82
+ const rightIsNumber = isNumberElement(right) || typeof right === 'number';
83
+
84
+ // For numeric values, use primitive comparison with -0 normalization (RFC 9535)
85
+ if (leftIsNumber && rightIsNumber) {
86
+ const leftPrimitive = isNumberElement(left) ? left.toValue() : left;
87
+ const rightPrimitive = isNumberElement(right) ? right.toValue() : right;
88
+ const leftVal = Object.is(leftPrimitive, -0) ? 0 : leftPrimitive;
89
+ const rightVal = Object.is(rightPrimitive, -0) ? 0 : rightPrimitive;
90
+ switch (operator) {
91
+ case '==':
92
+ return leftVal === rightVal;
93
+ case '!=':
94
+ return leftVal !== rightVal;
95
+ case '<':
96
+ return leftVal < rightVal;
97
+ case '>':
98
+ return leftVal > rightVal;
99
+ case '<=':
100
+ return leftVal <= rightVal;
101
+ case '>=':
102
+ return leftVal >= rightVal;
103
+ default:
104
+ return false;
105
+ }
106
+ }
107
+
108
+ // Check if both are strings (elements or primitives)
109
+ const leftIsString = isStringElement(left) || typeof left === 'string';
110
+ const rightIsString = isStringElement(right) || typeof right === 'string';
111
+
112
+ // For string values, use primitive comparison
113
+ if (leftIsString && rightIsString) {
114
+ const leftVal = isStringElement(left) ? left.toValue() : left;
115
+ const rightVal = isStringElement(right) ? right.toValue() : right;
116
+ switch (operator) {
117
+ case '==':
118
+ return leftVal === rightVal;
119
+ case '!=':
120
+ return leftVal !== rightVal;
121
+ case '<':
122
+ return leftVal < rightVal;
123
+ case '>':
124
+ return leftVal > rightVal;
125
+ case '<=':
126
+ return leftVal <= rightVal;
127
+ case '>=':
128
+ return leftVal >= rightVal;
129
+ default:
130
+ return false;
131
+ }
132
+ }
133
+
134
+ // For other types (booleans, nulls), only equality operators are defined (RFC 9535)
135
+ if (operator === '==') {
136
+ return refract(left).equals(right);
137
+ }
138
+ if (operator === '!=') {
139
+ return !refract(left).equals(right);
140
+ }
141
+ // Comparison operators (<, >, <=, >=) are not defined for non-numeric, non-string types
142
+ return false;
143
+ }
144
+ }
145
+ export default ApiDOMEvaluationRealm;
package/src/realm.ts ADDED
@@ -0,0 +1,172 @@
1
+ import {
2
+ ObjectElement,
3
+ ArrayElement,
4
+ isObjectElement,
5
+ isArrayElement,
6
+ isStringElement,
7
+ isNumberElement,
8
+ isBooleanElement,
9
+ isNullElement,
10
+ refract,
11
+ } from '@speclynx/apidom-datamodel';
12
+ import { EvaluationRealm } from '@swaggerexpert/jsonpath';
13
+
14
+ /**
15
+ * ApiDOM Evaluation Realm implementation for JSONPath.
16
+ * @public
17
+ */
18
+ class ApiDOMEvaluationRealm extends EvaluationRealm {
19
+ override name = 'apidom';
20
+
21
+ override isObject(value: unknown): value is ObjectElement {
22
+ return isObjectElement(value);
23
+ }
24
+
25
+ override isArray(value: unknown): value is ArrayElement {
26
+ return isArrayElement(value);
27
+ }
28
+
29
+ override isString(value: unknown): boolean {
30
+ return isStringElement(value);
31
+ }
32
+
33
+ override isNumber(value: unknown): boolean {
34
+ return isNumberElement(value);
35
+ }
36
+
37
+ override isBoolean(value: unknown): boolean {
38
+ return isBooleanElement(value);
39
+ }
40
+
41
+ override isNull(value: unknown): boolean {
42
+ return isNullElement(value);
43
+ }
44
+
45
+ override getString(value: unknown): string | undefined {
46
+ if (isStringElement(value)) return value.toValue() as string;
47
+ if (typeof value === 'string') return value;
48
+ return undefined;
49
+ }
50
+
51
+ override getProperty(value: unknown, key: string): unknown {
52
+ if (!isObjectElement(value)) return undefined;
53
+ if (!value.hasKey(key)) return undefined;
54
+ return value.get(key);
55
+ }
56
+
57
+ override hasProperty(value: unknown, key: string): boolean {
58
+ if (!isObjectElement(value)) return false;
59
+ return value.hasKey(key);
60
+ }
61
+
62
+ override getElement(value: unknown, index: number): unknown {
63
+ if (!isArrayElement(value)) return undefined;
64
+ if (index < 0 || index >= value.length) return undefined;
65
+ return value.get(index);
66
+ }
67
+
68
+ override getKeys(value: unknown): string[] {
69
+ if (!isObjectElement(value)) return [];
70
+ return value.keys() as string[];
71
+ }
72
+
73
+ override getLength(value: unknown): number {
74
+ if (isStringElement(value)) return [...(value.toValue() as string)].length;
75
+ if (isArrayElement(value)) return value.length;
76
+ if (isObjectElement(value)) return value.length;
77
+ if (typeof value === 'string') return [...value].length;
78
+ if (Array.isArray(value)) return value.length;
79
+ return 0;
80
+ }
81
+
82
+ override *entries(value: unknown): Iterable<[string | number, unknown]> {
83
+ if (isArrayElement(value)) {
84
+ let index = 0;
85
+ for (const item of value) {
86
+ yield [index, item];
87
+ index += 1;
88
+ }
89
+ } else if (isObjectElement(value)) {
90
+ for (const member of value) {
91
+ yield [member.key!.toValue() as string, member.value];
92
+ }
93
+ }
94
+ }
95
+
96
+ override compare(left: unknown, operator: string, right: unknown): boolean {
97
+ // Handle Nothing (undefined) comparisons
98
+ if (left === undefined || right === undefined) {
99
+ if (operator === '==') return left === undefined && right === undefined;
100
+ if (operator === '!=') return !(left === undefined && right === undefined);
101
+ return false;
102
+ }
103
+
104
+ // Check if both are numbers (elements or primitives)
105
+ const leftIsNumber = isNumberElement(left) || typeof left === 'number';
106
+ const rightIsNumber = isNumberElement(right) || typeof right === 'number';
107
+
108
+ // For numeric values, use primitive comparison with -0 normalization (RFC 9535)
109
+ if (leftIsNumber && rightIsNumber) {
110
+ const leftPrimitive = (isNumberElement(left) ? left.toValue() : left) as number;
111
+ const rightPrimitive = (isNumberElement(right) ? right.toValue() : right) as number;
112
+ const leftVal = Object.is(leftPrimitive, -0) ? 0 : leftPrimitive;
113
+ const rightVal = Object.is(rightPrimitive, -0) ? 0 : rightPrimitive;
114
+
115
+ switch (operator) {
116
+ case '==':
117
+ return leftVal === rightVal;
118
+ case '!=':
119
+ return leftVal !== rightVal;
120
+ case '<':
121
+ return leftVal < rightVal;
122
+ case '>':
123
+ return leftVal > rightVal;
124
+ case '<=':
125
+ return leftVal <= rightVal;
126
+ case '>=':
127
+ return leftVal >= rightVal;
128
+ default:
129
+ return false;
130
+ }
131
+ }
132
+
133
+ // Check if both are strings (elements or primitives)
134
+ const leftIsString = isStringElement(left) || typeof left === 'string';
135
+ const rightIsString = isStringElement(right) || typeof right === 'string';
136
+
137
+ // For string values, use primitive comparison
138
+ if (leftIsString && rightIsString) {
139
+ const leftVal = (isStringElement(left) ? left.toValue() : left) as string;
140
+ const rightVal = (isStringElement(right) ? right.toValue() : right) as string;
141
+
142
+ switch (operator) {
143
+ case '==':
144
+ return leftVal === rightVal;
145
+ case '!=':
146
+ return leftVal !== rightVal;
147
+ case '<':
148
+ return leftVal < rightVal;
149
+ case '>':
150
+ return leftVal > rightVal;
151
+ case '<=':
152
+ return leftVal <= rightVal;
153
+ case '>=':
154
+ return leftVal >= rightVal;
155
+ default:
156
+ return false;
157
+ }
158
+ }
159
+
160
+ // For other types (booleans, nulls), only equality operators are defined (RFC 9535)
161
+ if (operator === '==') {
162
+ return refract(left).equals(right);
163
+ }
164
+ if (operator === '!=') {
165
+ return !refract(left).equals(right);
166
+ }
167
+ // Comparison operators (<, >, <=, >=) are not defined for non-numeric, non-string types
168
+ return false;
169
+ }
170
+ }
171
+
172
+ export default ApiDOMEvaluationRealm;