Skip to content

Commit 7a15f7e

Browse files
authored
feat(evaluate): add support for minim realm (#17)
Refs #13
1 parent 6efb936 commit 7a15f7e

File tree

10 files changed

+263
-113
lines changed

10 files changed

+263
-113
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,27 @@ const map = new Map([
358358
evaluate(map, '/a/1', { realm: new MapSetEvaluationRealm() }); // => 'c'
359359
```
360360

361+
###### Minim Evaluation Realm
362+
363+
The Minim Evaluation Realm extends JSON Pointer evaluation to support Minim data structures,
364+
specifically ObjectElement, ArrayElement, and other element types from the [minim](https://github.com/refractproject/minim).
365+
366+
Minim is widely used in API description languages (e.g., OpenAPI, API Blueprint, AsyncAPI and other API Description processing tools)
367+
to represent structured API data. The Minim Evaluation Realm enables seamless JSON Pointer traversal for these structures.
368+
369+
370+
```js
371+
import { ObjectElement } from 'minim';
372+
import { evaluate } from '@swaggerexpert/json-pointer';
373+
import MinimEvaluationRealm from '@swaggerexpert/json-pointer/evaluate/realms/minim';
374+
375+
const objectElement = new ObjectElement({
376+
a: ['b', 'c']
377+
});
378+
379+
evaluate(objectElement, '/a/1', { realm: new MinimEvaluationRealm() }); // => 'c'
380+
```
381+
361382
###### Custom Evaluation Realms
362383

363384
The evaluation is designed to support **custom evaluation realms**,

package-lock.json

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
"chai": "=5.2.0",
8383
"cross-env": "^7.0.3",
8484
"husky": "=9.1.7",
85+
"minim": "^0.23.8",
8586
"mocha": "=11.1.0",
8687
"npm-watch": "^0.13.0",
8788
"prettier": "^3.5.2"

src/errors/JSONPointerKeyError.js

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,5 @@
11
import JSONPointerEvaluateError from './JSONPointerEvaluateError.js';
22

3-
class JSONPointerKeyError extends JSONPointerEvaluateError {
4-
constructor(message, options) {
5-
if (
6-
typeof message === 'undefined' &&
7-
(typeof options?.referenceToken === 'string' || typeof options?.referenceToken === 'number')
8-
) {
9-
message = `Invalid object key: '${options.referenceToken}' not found`;
10-
}
11-
12-
super(message, options);
13-
}
14-
}
3+
class JSONPointerKeyError extends JSONPointerEvaluateError {}
154

165
export default JSONPointerKeyError;

src/evaluate/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ const evaluate = (
6666

6767
if (realm.isObject(current)) {
6868
if (!realm.has(current, referenceToken) && strictObjects) {
69-
throw new JSONPointerKeyError(undefined, {
69+
throw new JSONPointerKeyError(`Invalid object key: '${referenceToken}' not found`, {
7070
jsonPointer,
7171
referenceTokens,
7272
referenceToken,

src/evaluate/realms/minim.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { ObjectElement, ArrayElement } from 'minim';
2+
3+
import EvaluationRealm from '../EvaluationRealm.js';
4+
import JSONPointerKeyError from '../../errors/JSONPointerKeyError.js';
5+
6+
class MinimEvaluationRealm extends EvaluationRealm {
7+
name = 'minim';
8+
9+
isArray(node) {
10+
return node instanceof ArrayElement && !(node instanceof ObjectElement);
11+
}
12+
13+
isObject(node) {
14+
return node instanceof ObjectElement;
15+
}
16+
17+
sizeOf(node) {
18+
if (this.isArray(node) || this.isObject(node)) {
19+
return node.length;
20+
}
21+
return 0;
22+
}
23+
24+
has(node, referenceToken) {
25+
if (this.isArray(node)) {
26+
return Number(referenceToken) < this.sizeOf(node);
27+
}
28+
if (this.isObject(node)) {
29+
const keys = node.keys();
30+
const uniqueKeys = new Set(keys);
31+
32+
if (keys.length !== uniqueKeys.size) {
33+
throw new JSONPointerKeyError(`Object keys must be unique for '${referenceToken}'`, {
34+
currentValue: node,
35+
referenceToken,
36+
});
37+
}
38+
39+
return node.hasKey(referenceToken);
40+
}
41+
return false;
42+
}
43+
44+
evaluate(node, referenceToken) {
45+
if (this.isArray(node)) {
46+
return node.get(Number(referenceToken));
47+
}
48+
return node.get(referenceToken);
49+
}
50+
}
51+
52+
export default MinimEvaluationRealm;

test/evaluate/index.js

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -71,56 +71,6 @@ describe('evaluate', function () {
7171
});
7272
});
7373

74-
context('valid JSON Pointers', function () {
75-
specify('should return entire document for ""', function () {
76-
assert.deepEqual(evaluate(data, ''), data);
77-
});
78-
79-
specify('should return array ["bar", "baz"] for "/foo"', function () {
80-
assert.deepEqual(evaluate(data, '/foo'), ['bar', 'baz']);
81-
});
82-
83-
specify('should return "bar" for "/foo/0"', function () {
84-
assert.strictEqual(evaluate(data, '/foo/0'), 'bar');
85-
});
86-
87-
specify('should return 0 for "/"', function () {
88-
assert.strictEqual(evaluate(data, '/'), 0);
89-
});
90-
91-
specify('should return 1 for "/a~1b"', function () {
92-
assert.strictEqual(evaluate(data, '/a~1b'), 1);
93-
});
94-
95-
specify('should return 2 for "/c%d"', function () {
96-
assert.strictEqual(evaluate(data, '/c%d'), 2);
97-
});
98-
99-
specify('should return 3 for "/e^f"', function () {
100-
assert.strictEqual(evaluate(data, '/e^f'), 3);
101-
});
102-
103-
specify('should return 4 for "/g|h"', function () {
104-
assert.strictEqual(evaluate(data, '/g|h'), 4);
105-
});
106-
107-
specify('should return 5 for "/i\\j"', function () {
108-
assert.strictEqual(evaluate(data, '/i\\j'), 5);
109-
});
110-
111-
specify('should return 6 for "/k\"l"', function () {
112-
assert.strictEqual(evaluate(data, '/k"l'), 6);
113-
});
114-
115-
specify('should return 7 for "/ "', function () {
116-
assert.strictEqual(evaluate(data, '/ '), 7);
117-
});
118-
119-
specify('should return 8 for "/m~0n"', function () {
120-
assert.strictEqual(evaluate(data, '/m~0n'), 8);
121-
});
122-
});
123-
12474
context('given custom evaluator option', function () {
12575
specify('should correctly use a default evaluator', function () {
12676
const result = evaluate(data, '/a~1b', { evaluator: referenceTokenListEvaluator });

test/evaluate/realms/map-set.js

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -71,56 +71,6 @@ describe('evaluate', function () {
7171
});
7272
});
7373

74-
context('valid JSON Pointers', function () {
75-
specify('should return entire document for ""', function () {
76-
assert.deepEqual(evaluate(data, '', { realm }), data);
77-
});
78-
79-
specify('should return Set(["bar", "baz"]) for "/foo"', function () {
80-
assert.deepEqual(evaluate(data, '/foo', { realm }), new Set(['bar', 'baz']));
81-
});
82-
83-
specify('should return "bar" for "/foo/0"', function () {
84-
assert.strictEqual(evaluate(data, '/foo/0', { realm }), 'bar');
85-
});
86-
87-
specify('should return 0 for "/"', function () {
88-
assert.strictEqual(evaluate(data, '/', { realm }), 0);
89-
});
90-
91-
specify('should return 1 for "/a~1b"', function () {
92-
assert.strictEqual(evaluate(data, '/a~1b', { realm }), 1);
93-
});
94-
95-
specify('should return 2 for "/c%d"', function () {
96-
assert.strictEqual(evaluate(data, '/c%d', { realm }), 2);
97-
});
98-
99-
specify('should return 3 for "/e^f"', function () {
100-
assert.strictEqual(evaluate(data, '/e^f', { realm }), 3);
101-
});
102-
103-
specify('should return 4 for "/g|h"', function () {
104-
assert.strictEqual(evaluate(data, '/g|h', { realm }), 4);
105-
});
106-
107-
specify('should return 5 for "/i\\j"', function () {
108-
assert.strictEqual(evaluate(data, '/i\\j', { realm }), 5);
109-
});
110-
111-
specify('should return 6 for "/k\"l"', function () {
112-
assert.strictEqual(evaluate(data, '/k"l', { realm }), 6);
113-
});
114-
115-
specify('should return 7 for "/ "', function () {
116-
assert.strictEqual(evaluate(data, '/ ', { realm }), 7);
117-
});
118-
119-
specify('should return 8 for "/m~0n"', function () {
120-
assert.strictEqual(evaluate(data, '/m~0n', { realm }), 8);
121-
});
122-
});
123-
12474
context('invalid JSON Pointers (should throw errors)', function () {
12575
specify('should throw JSONPointerEvaluateError for invalid JSON Pointer', function () {
12676
assert.throws(() => evaluate(data, 'invalid-pointer', { realm }), JSONPointerEvaluateError);

0 commit comments

Comments
 (0)