@hyperjump/json-pointer 1.1.0 → 1.1.1
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/LICENSE +1 -1
- package/lib/index.d.ts +35 -48
- package/lib/index.js +185 -30
- package/package.json +6 -8
- package/.github/FUNDING.yml +0 -1
package/LICENSE
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -1,53 +1,40 @@
|
|
|
1
|
-
export const nil: "";
|
|
2
|
-
export const pointerSegments: (pointer: string) => Generator<string>;
|
|
3
|
-
export const append: (
|
|
4
|
-
(segment: string, pointer: string) => string
|
|
5
|
-
) & (
|
|
6
|
-
(segment: string) => (pointer: string) => string
|
|
7
|
-
);
|
|
8
|
-
export const get: (
|
|
9
|
-
(pointer: string, subject: Pointable) => unknown
|
|
10
|
-
) & (
|
|
11
|
-
(pointer: string) => Getter
|
|
12
|
-
);
|
|
13
|
-
export const set: (
|
|
14
|
-
<A extends Pointable>(pointer: string, subject: A, value: unknown) => A
|
|
15
|
-
) & (
|
|
16
|
-
(pointer: string) => Setter
|
|
17
|
-
);
|
|
18
|
-
export const assign: (
|
|
19
|
-
<A extends Pointable>(pointer: string, subject: A, value: unknown) => void
|
|
20
|
-
) & (
|
|
21
|
-
(pointer: string) => Assigner
|
|
22
|
-
);
|
|
23
|
-
export const unset: (
|
|
24
|
-
<A extends Pointable>(pointer: string, subject: A) => A
|
|
25
|
-
) & (
|
|
26
|
-
(pointer: string) => Unsetter
|
|
27
|
-
);
|
|
28
|
-
export const remove: (
|
|
29
|
-
(pointer: string, subject: Pointable) => void
|
|
30
|
-
) & (
|
|
31
|
-
(pointer: string) => Remover
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
export type Getter = (subject: Pointable) => unknown;
|
|
35
|
-
export type Setter = (
|
|
36
|
-
<A extends Pointable>(subject: A, value: unknown) => A
|
|
37
|
-
) & (
|
|
38
|
-
<A extends Pointable>(subject: A) => (value: unknown) => A
|
|
39
|
-
);
|
|
40
|
-
export type Assigner = (
|
|
41
|
-
<A extends Pointable>(subject: A, value: unknown) => void
|
|
42
|
-
) & (
|
|
43
|
-
<A extends Pointable>(subject: A) => (value: unknown) => void
|
|
44
|
-
);
|
|
45
|
-
export type Unsetter = <A extends Pointable>(subject: A) => A;
|
|
46
|
-
export type Remover = (subject: Pointable) => void;
|
|
47
|
-
|
|
48
1
|
export type Json = string | number | boolean | null | JsonObject | Json[];
|
|
49
2
|
export type JsonObject = {
|
|
50
3
|
[property: string]: Json;
|
|
51
4
|
};
|
|
52
5
|
|
|
53
|
-
export
|
|
6
|
+
export const nil: "";
|
|
7
|
+
|
|
8
|
+
export const pointerSegments: (pointer: string) => Generator<string>;
|
|
9
|
+
|
|
10
|
+
export const append: (segment: string, pointer: string) => string;
|
|
11
|
+
|
|
12
|
+
export const get: {
|
|
13
|
+
(pointer: string, subject: Json): Json | undefined;
|
|
14
|
+
(pointer: string): Getter;
|
|
15
|
+
};
|
|
16
|
+
export type Getter = (subject: Json) => Json | undefined;
|
|
17
|
+
|
|
18
|
+
export const set: {
|
|
19
|
+
(pointer: string, subject: Json, value: Json): Json;
|
|
20
|
+
(pointer: string): Setter;
|
|
21
|
+
};
|
|
22
|
+
export type Setter = (subject: Json, value: Json) => Json;
|
|
23
|
+
|
|
24
|
+
export const assign: {
|
|
25
|
+
(pointer: string, subject: Json, value: Json): void;
|
|
26
|
+
(pointer: string): Assigner;
|
|
27
|
+
};
|
|
28
|
+
export type Assigner = (subject: Json, value: Json) => void;
|
|
29
|
+
|
|
30
|
+
export const unset: {
|
|
31
|
+
(pointer: string, subject: Json): Json | undefined;
|
|
32
|
+
(pointer: string): Unsetter;
|
|
33
|
+
};
|
|
34
|
+
export type Unsetter = (subject: Json) => Json | undefined;
|
|
35
|
+
|
|
36
|
+
export const remove: {
|
|
37
|
+
(pointer: string, subject: Json): void;
|
|
38
|
+
(pointer: string): Remover;
|
|
39
|
+
};
|
|
40
|
+
export type Remover = (subject: Json) => void;
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
/** @import * as API from "./index.d.ts" */
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/** @type API.nil */
|
|
1
5
|
export const nil = "";
|
|
2
6
|
|
|
7
|
+
/** @type API.pointerSegments */
|
|
3
8
|
export const pointerSegments = function* (pointer) {
|
|
4
|
-
if (pointer.length > 0 && pointer
|
|
9
|
+
if (pointer.length > 0 && !pointer.startsWith("/")) {
|
|
5
10
|
throw Error("Invalid JSON Pointer");
|
|
6
11
|
}
|
|
7
12
|
|
|
@@ -18,6 +23,22 @@ export const pointerSegments = function* (pointer) {
|
|
|
18
23
|
}
|
|
19
24
|
};
|
|
20
25
|
|
|
26
|
+
/**
|
|
27
|
+
* @overload
|
|
28
|
+
* @param {string} pointer
|
|
29
|
+
* @return {API.Getter}
|
|
30
|
+
*
|
|
31
|
+
* @overload
|
|
32
|
+
* @param {string} pointer
|
|
33
|
+
* @param {API.Json} subject
|
|
34
|
+
* @return {API.Json | undefined}
|
|
35
|
+
*
|
|
36
|
+
* @param {string} pointer
|
|
37
|
+
* @param {API.Json} [subject]
|
|
38
|
+
* @return {API.Json | undefined | API.Getter}
|
|
39
|
+
*
|
|
40
|
+
* @type API.get
|
|
41
|
+
*/
|
|
21
42
|
export const get = (pointer, subject = undefined) => {
|
|
22
43
|
if (subject === undefined) {
|
|
23
44
|
const segments = [...pointerSegments(pointer)];
|
|
@@ -27,7 +48,9 @@ export const get = (pointer, subject = undefined) => {
|
|
|
27
48
|
}
|
|
28
49
|
};
|
|
29
50
|
|
|
51
|
+
/** @type (segments: Iterable<string>, subject: API.Json | undefined) => API.Json | undefined */
|
|
30
52
|
const _get = (segments, subject) => {
|
|
53
|
+
/** @type string */
|
|
31
54
|
let cursor = nil;
|
|
32
55
|
for (const segment of segments) {
|
|
33
56
|
subject = applySegment(subject, segment, cursor);
|
|
@@ -37,15 +60,33 @@ const _get = (segments, subject) => {
|
|
|
37
60
|
return subject;
|
|
38
61
|
};
|
|
39
62
|
|
|
63
|
+
/**
|
|
64
|
+
* @overload
|
|
65
|
+
* @param {string} pointer
|
|
66
|
+
* @return {API.Setter}
|
|
67
|
+
*
|
|
68
|
+
* @overload
|
|
69
|
+
* @param {string} pointer
|
|
70
|
+
* @param {API.Json} subject
|
|
71
|
+
* @param {API.Json} value
|
|
72
|
+
* @return {API.Json}
|
|
73
|
+
*
|
|
74
|
+
* @param {string} pointer
|
|
75
|
+
* @param {API.Json} [subject]
|
|
76
|
+
* @param {API.Json} [value]
|
|
77
|
+
* @return {API.Json | API.Setter}
|
|
78
|
+
*
|
|
79
|
+
* @type API.set
|
|
80
|
+
*/
|
|
40
81
|
export const set = (pointer, subject = undefined, value = undefined) => {
|
|
41
82
|
if (subject === undefined) {
|
|
42
|
-
|
|
43
|
-
return (subject, value) => _set(segments.values(), subject, value);
|
|
83
|
+
return (subject, value) => _set(pointerSegments(pointer), subject, value);
|
|
44
84
|
} else {
|
|
45
|
-
return _set(pointerSegments(pointer), subject, value);
|
|
85
|
+
return _set(pointerSegments(pointer), subject, /** @type API.Json */ (value));
|
|
46
86
|
}
|
|
47
87
|
};
|
|
48
88
|
|
|
89
|
+
/** @type (segments: Generator<string>, subject: API.Json | undefined, value: API.Json, cursor?: string) => API.Json */
|
|
49
90
|
const _set = (segments, subject, value, cursor = nil) => {
|
|
50
91
|
const segment = segments.next();
|
|
51
92
|
if (segment.done) {
|
|
@@ -61,45 +102,96 @@ const _set = (segments, subject, value, cursor = nil) => {
|
|
|
61
102
|
}
|
|
62
103
|
cursor = append(segment.value, cursor);
|
|
63
104
|
|
|
105
|
+
// currentSubject could also be an array, but this appeases the type system
|
|
106
|
+
const currentSubject = /** @type API.JsonObject */ (subject);
|
|
64
107
|
const computedSegment = computeSegment(subject, segment.value);
|
|
65
|
-
|
|
66
|
-
return
|
|
108
|
+
currentSubject[computedSegment] = _set(segments, currentSubject[computedSegment], value, cursor);
|
|
109
|
+
return currentSubject;
|
|
67
110
|
};
|
|
68
111
|
|
|
112
|
+
/**
|
|
113
|
+
* @overload
|
|
114
|
+
* @param {string} pointer
|
|
115
|
+
* @returns {API.Assigner}
|
|
116
|
+
*
|
|
117
|
+
* @overload
|
|
118
|
+
* @param {string} pointer
|
|
119
|
+
* @param {API.Json} subject
|
|
120
|
+
* @param {API.Json} value
|
|
121
|
+
* @returns {void}
|
|
122
|
+
*
|
|
123
|
+
* @param {string} pointer
|
|
124
|
+
* @param {API.Json} [subject]
|
|
125
|
+
* @param {API.Json} [value]
|
|
126
|
+
* @returns {void | API.Assigner}
|
|
127
|
+
*
|
|
128
|
+
* @type API.assign
|
|
129
|
+
*/
|
|
69
130
|
export const assign = (pointer, subject = undefined, value = undefined) => {
|
|
70
131
|
if (subject === undefined) {
|
|
71
|
-
|
|
72
|
-
return (subject, value) => _assign(segments.values(), subject, value);
|
|
132
|
+
return (subject, value) => _assign(pointerSegments(pointer), subject, value);
|
|
73
133
|
} else {
|
|
74
|
-
return _assign(pointerSegments(pointer), subject, value);
|
|
134
|
+
return _assign(pointerSegments(pointer), subject, /** @type API.Json */ (value));
|
|
75
135
|
}
|
|
76
136
|
};
|
|
77
137
|
|
|
138
|
+
/** @type (segments: Generator<string>, subject: API.Json, value: API.Json, cursor?: string) => void */
|
|
78
139
|
const _assign = (segments, subject, value, cursor = nil) => {
|
|
140
|
+
/** @type string | undefined */
|
|
79
141
|
let lastSegment;
|
|
142
|
+
|
|
143
|
+
/** @type API.Json | undefined */
|
|
144
|
+
let currentSubject = subject;
|
|
145
|
+
|
|
146
|
+
/** @type API.Json | undefined */
|
|
80
147
|
let lastSubject;
|
|
148
|
+
|
|
81
149
|
for (let segment of segments) {
|
|
82
|
-
segment = computeSegment(
|
|
150
|
+
segment = computeSegment(currentSubject, segment);
|
|
83
151
|
lastSegment = segment;
|
|
84
|
-
lastSubject =
|
|
85
|
-
|
|
152
|
+
lastSubject = currentSubject;
|
|
153
|
+
currentSubject = applySegment(currentSubject, segment, cursor);
|
|
86
154
|
cursor = append(segment, cursor);
|
|
87
155
|
}
|
|
88
156
|
|
|
89
|
-
if (
|
|
90
|
-
|
|
157
|
+
if (lastSegment === undefined) {
|
|
158
|
+
return;
|
|
91
159
|
}
|
|
160
|
+
|
|
161
|
+
// lastSubject could also be an array, but this appeases the type system
|
|
162
|
+
/** @type API.JsonObject */ (lastSubject)[lastSegment] = value;
|
|
92
163
|
};
|
|
93
164
|
|
|
165
|
+
/**
|
|
166
|
+
* @overload
|
|
167
|
+
* @param {string} pointer
|
|
168
|
+
* @returns {API.Unsetter}
|
|
169
|
+
*
|
|
170
|
+
* @overload
|
|
171
|
+
* @param {string} pointer
|
|
172
|
+
* @param {API.Json} subject
|
|
173
|
+
* @returns {API.Json | undefined}
|
|
174
|
+
*
|
|
175
|
+
* @param {string} pointer
|
|
176
|
+
* @param {API.Json} [subject]
|
|
177
|
+
* @returns {API.Json | undefined | API.Unsetter}
|
|
178
|
+
*
|
|
179
|
+
* @type API.unset
|
|
180
|
+
*/
|
|
94
181
|
export const unset = (pointer, subject = undefined) => {
|
|
95
182
|
if (subject === undefined) {
|
|
96
|
-
|
|
97
|
-
return (subject) => _unset(segments.values(), subject);
|
|
183
|
+
return (subject) => _unset(pointerSegments(pointer), subject);
|
|
98
184
|
} else {
|
|
99
185
|
return _unset(pointerSegments(pointer), subject);
|
|
100
186
|
}
|
|
101
187
|
};
|
|
102
188
|
|
|
189
|
+
/**
|
|
190
|
+
* @param {Generator<string>} segments
|
|
191
|
+
* @param {API.Json | undefined} [subject]
|
|
192
|
+
* @param {string} [cursor]
|
|
193
|
+
* @returns {API.JsonObject | API.Json[] | undefined}
|
|
194
|
+
*/
|
|
103
195
|
const _unset = (segments, subject, cursor = nil) => {
|
|
104
196
|
const segment = segments.next();
|
|
105
197
|
if (segment.done) {
|
|
@@ -115,45 +207,104 @@ const _unset = (segments, subject, cursor = nil) => {
|
|
|
115
207
|
}
|
|
116
208
|
cursor = append(segment.value, cursor);
|
|
117
209
|
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
210
|
+
// currentSubject could also be an array, but this appeases the type system
|
|
211
|
+
const currentSubject = /** @type API.JsonObject */ (subject);
|
|
212
|
+
const computedSegment = computeSegment(currentSubject, segment.value);
|
|
213
|
+
const unsetSubject = _unset(segments, currentSubject[computedSegment], cursor);
|
|
214
|
+
if (computedSegment in currentSubject) {
|
|
215
|
+
if (unsetSubject === undefined) {
|
|
216
|
+
delete currentSubject[computedSegment];
|
|
217
|
+
} else {
|
|
218
|
+
currentSubject[computedSegment] = unsetSubject;
|
|
219
|
+
}
|
|
122
220
|
}
|
|
123
|
-
return
|
|
221
|
+
return currentSubject;
|
|
124
222
|
};
|
|
125
223
|
|
|
224
|
+
/**
|
|
225
|
+
* @overload
|
|
226
|
+
* @param {string} pointer
|
|
227
|
+
* @returns {API.Remover}
|
|
228
|
+
*
|
|
229
|
+
* @overload
|
|
230
|
+
* @param {string} pointer
|
|
231
|
+
* @param {API.Json} subject
|
|
232
|
+
* @returns {void}
|
|
233
|
+
*
|
|
234
|
+
* @param {string} pointer
|
|
235
|
+
* @param {API.Json} [subject]
|
|
236
|
+
* @returns {void | API.Remover}
|
|
237
|
+
*
|
|
238
|
+
* @type API.remove
|
|
239
|
+
*/
|
|
126
240
|
export const remove = (pointer, subject = undefined) => {
|
|
127
241
|
if (subject === undefined) {
|
|
128
|
-
|
|
129
|
-
return (subject) => _remove(segments.values(), subject);
|
|
242
|
+
return (subject) => _remove(pointerSegments(pointer), subject);
|
|
130
243
|
} else {
|
|
131
244
|
return _remove(pointerSegments(pointer), subject);
|
|
132
245
|
}
|
|
133
246
|
};
|
|
134
247
|
|
|
248
|
+
/** @type (segments: Generator<string>, subject: API.Json, cursor?: string) => void */
|
|
135
249
|
const _remove = (segments, subject, cursor = nil) => {
|
|
250
|
+
/** @type string | undefined */
|
|
136
251
|
let lastSegment;
|
|
252
|
+
|
|
253
|
+
/** @type API.Json | undefined */
|
|
254
|
+
let currentSubject = subject;
|
|
255
|
+
|
|
256
|
+
/** @type API.Json | undefined */
|
|
137
257
|
let lastSubject;
|
|
258
|
+
|
|
138
259
|
for (let segment of segments) {
|
|
139
|
-
segment = computeSegment(
|
|
260
|
+
segment = computeSegment(currentSubject, segment);
|
|
140
261
|
lastSegment = segment;
|
|
141
|
-
lastSubject =
|
|
142
|
-
|
|
262
|
+
lastSubject = currentSubject;
|
|
263
|
+
currentSubject = applySegment(currentSubject, segment, cursor);
|
|
143
264
|
cursor = append(segment, cursor);
|
|
144
265
|
}
|
|
145
266
|
|
|
146
|
-
if (
|
|
147
|
-
|
|
267
|
+
if (lastSegment === undefined) {
|
|
268
|
+
return;
|
|
148
269
|
}
|
|
270
|
+
|
|
271
|
+
// lastSubject could also be an array, but this appeases the type system
|
|
272
|
+
delete /** @type API.JsonObject */ (lastSubject)[lastSegment];
|
|
149
273
|
};
|
|
150
274
|
|
|
275
|
+
/** @type API.append */
|
|
151
276
|
export const append = (segment, pointer) => pointer + "/" + escape(segment);
|
|
152
277
|
|
|
278
|
+
/** @type (segment: string) => string */
|
|
153
279
|
const escape = (segment) => segment.toString().replace(/~/g, "~0").replace(/\//g, "~1");
|
|
280
|
+
|
|
281
|
+
/** @type (segment: string) => string */
|
|
154
282
|
const unescape = (segment) => segment.toString().replace(/~1/g, "/").replace(/~0/g, "~");
|
|
155
|
-
const computeSegment = (value, segment) => Array.isArray(value) && segment === "-" ? value.length : segment;
|
|
156
283
|
|
|
284
|
+
/**
|
|
285
|
+
* @overload
|
|
286
|
+
* @param {API.Json[]} value
|
|
287
|
+
* @param {string} segment
|
|
288
|
+
* @returns {number}
|
|
289
|
+
*
|
|
290
|
+
* @overload
|
|
291
|
+
* @param {API.Json | undefined} value
|
|
292
|
+
* @param {string} segment
|
|
293
|
+
* @returns {string}
|
|
294
|
+
*
|
|
295
|
+
* @param {API.Json | undefined} value
|
|
296
|
+
* @param {string} segment
|
|
297
|
+
* @returns {string | number}
|
|
298
|
+
*/
|
|
299
|
+
const computeSegment = (value, segment) => {
|
|
300
|
+
if (Array.isArray(value)) {
|
|
301
|
+
return segment === "-" ? value.length : parseInt(segment, 10);
|
|
302
|
+
} else {
|
|
303
|
+
return segment;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/** @type (value: API.Json | undefined, segment: string, cursor?: string) => API.Json | undefined */
|
|
157
308
|
const applySegment = (value, segment, cursor = "") => {
|
|
158
309
|
if (value === undefined) {
|
|
159
310
|
throw TypeError(`Value at '${cursor}' is undefined and does not have property '${segment}'`);
|
|
@@ -163,8 +314,12 @@ const applySegment = (value, segment, cursor = "") => {
|
|
|
163
314
|
throw TypeError(`Value at '${cursor}' is a ${typeof value} and does not have property '${segment}'`);
|
|
164
315
|
} else {
|
|
165
316
|
const computedSegment = computeSegment(value, segment);
|
|
166
|
-
|
|
317
|
+
if (Object.hasOwn(value, computedSegment)) {
|
|
318
|
+
// value could also be an array, but this appeases the type system
|
|
319
|
+
return /** @type API.JsonObject */ (value)[computedSegment];
|
|
320
|
+
}
|
|
167
321
|
}
|
|
168
322
|
};
|
|
169
323
|
|
|
324
|
+
/** @type (value: API.Json) => value is string | number | boolean | null */
|
|
170
325
|
const isScalar = (value) => value === null || typeof value !== "object";
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperjump/json-pointer",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "An RFC-6901 JSON Pointer implementation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
7
7
|
"exports": "./lib/index.js",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"clean": "xargs -a .gitignore rm -rf",
|
|
10
9
|
"lint": "eslint lib",
|
|
11
|
-
"test": "vitest --watch=false"
|
|
10
|
+
"test": "vitest --watch=false",
|
|
11
|
+
"type-check": "tsc --noEmit"
|
|
12
12
|
},
|
|
13
13
|
"repository": "github:hyperjump-io/json-pointer",
|
|
14
14
|
"keywords": [
|
|
@@ -22,13 +22,11 @@
|
|
|
22
22
|
"url": "https://github.com/sponsors/jdesrosiers"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@
|
|
26
|
-
"@
|
|
27
|
-
"eslint": "*",
|
|
28
|
-
"eslint-import-resolver-node": "*",
|
|
25
|
+
"@stylistic/eslint-plugin": "*",
|
|
26
|
+
"@types/node": "*",
|
|
29
27
|
"eslint-import-resolver-typescript": "*",
|
|
30
28
|
"eslint-plugin-import": "*",
|
|
31
|
-
"typescript": "*",
|
|
29
|
+
"typescript-eslint": "*",
|
|
32
30
|
"vitest": "*"
|
|
33
31
|
}
|
|
34
32
|
}
|
package/.github/FUNDING.yml
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
github: [jdesrosiers]
|