@hyperjump/json-pointer 1.0.1 → 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/README.md +40 -10
- package/lib/index.d.ts +35 -47
- package/lib/index.js +254 -89
- package/package.json +7 -16
- package/.github/FUNDING.yml +0 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
JSON Pointer
|
|
2
|
-
============
|
|
1
|
+
# JSON Pointer
|
|
3
2
|
|
|
4
3
|
This is an implementation of RFC-6901 JSON Pointer. JSON Pointer is designed for
|
|
5
4
|
referring to data values within a JSON document. It's designed to be URL
|
|
6
5
|
friendly so it can be used as a URL fragment that points to a specific part of
|
|
7
6
|
the JSON document.
|
|
8
7
|
|
|
9
|
-
Installation
|
|
10
|
-
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
11
10
|
Includes support for node.js (ES Modules, TypeScript) and browsers.
|
|
12
11
|
|
|
13
12
|
```bash
|
|
14
13
|
npm install @hyperjump/json-pointer
|
|
15
14
|
```
|
|
16
15
|
|
|
17
|
-
Usage
|
|
18
|
-
-----
|
|
16
|
+
## Usage
|
|
19
17
|
|
|
20
18
|
```javascript
|
|
21
|
-
|
|
19
|
+
import * as JsonPointer from "@hyperjump/json-pointer";
|
|
22
20
|
|
|
23
21
|
const value = {
|
|
24
22
|
"foo": {
|
|
@@ -27,7 +25,7 @@ const value = {
|
|
|
27
25
|
};
|
|
28
26
|
|
|
29
27
|
// Construct pointers
|
|
30
|
-
const fooPointer = JsonPointer.append(JsonPointer.nil
|
|
28
|
+
const fooPointer = JsonPointer.append("foo", JsonPointer.nil); // "/foo"
|
|
31
29
|
const fooBarPointer = JsonPointer.append(fooPointer, "bar"); // "/foo/bar"
|
|
32
30
|
|
|
33
31
|
// Get a value from a pointer
|
|
@@ -55,8 +53,40 @@ const deleteFooBar = JsonPointer.remove(fooBarPointer);
|
|
|
55
53
|
deleteFooBar(value); // { "foo": {} }
|
|
56
54
|
```
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
## API
|
|
57
|
+
|
|
58
|
+
* **nil**: ""
|
|
59
|
+
|
|
60
|
+
The empty pointer.
|
|
61
|
+
* **pointerSegments**: (pointer: string) => Generator\<string>
|
|
62
|
+
|
|
63
|
+
An iterator for the segments of a JSON Pointer that handles escaping.
|
|
64
|
+
* **append**: (segment: string, pointer: string) => string
|
|
65
|
+
|
|
66
|
+
Append a segment to a JSON Pointer.
|
|
67
|
+
* **get**: (pointer: string, subject: any) => any
|
|
68
|
+
|
|
69
|
+
Use a JSON Pointer to get a value. This function can be curried.
|
|
70
|
+
* **set**: (pointer: string, subject: any, value: any) => any
|
|
71
|
+
|
|
72
|
+
Immutably set a value using a JSON Pointer. Returns a new version of
|
|
73
|
+
`subject` with the value set. The original `subject` is not changed, but the
|
|
74
|
+
value isn't entirely cloned. Values that aren't changed will point to
|
|
75
|
+
the same value as the original. This function can be curried.
|
|
76
|
+
* **assign**: (pointer: string, subject: any, value: any) => void
|
|
77
|
+
|
|
78
|
+
Mutate a value using a JSON Pointer. This function can be curried.
|
|
79
|
+
* **unset**: (pointer: string, subject: any) => any
|
|
80
|
+
|
|
81
|
+
Immutably delete a value using a JSON Pointer. Returns a new version of
|
|
82
|
+
`subject` without the value. The original `subject` is not changed, but the
|
|
83
|
+
value isn't entirely cloned. Values that aren't changed will point to the
|
|
84
|
+
same value as the original. This function can be curried.
|
|
85
|
+
* **remove**: (pointer: string, subject: any) => void
|
|
86
|
+
|
|
87
|
+
Delete a value using a JSON Pointer. This function can be curried.
|
|
88
|
+
|
|
89
|
+
## Contributing
|
|
60
90
|
|
|
61
91
|
### Tests
|
|
62
92
|
|
package/lib/index.d.ts
CHANGED
|
@@ -1,52 +1,40 @@
|
|
|
1
|
-
export const nil: "";
|
|
2
|
-
export const append: (
|
|
3
|
-
(segment: string, pointer: string) => string
|
|
4
|
-
) & (
|
|
5
|
-
(segment: string) => (pointer: string) => string
|
|
6
|
-
);
|
|
7
|
-
export const get: (
|
|
8
|
-
(pointer: string, subject: Pointable) => unknown
|
|
9
|
-
) & (
|
|
10
|
-
(pointer: string) => Getter
|
|
11
|
-
);
|
|
12
|
-
export const set: (
|
|
13
|
-
<A extends Pointable>(pointer: string, subject: A, value: unknown) => A
|
|
14
|
-
) & (
|
|
15
|
-
(pointer: string) => Setter
|
|
16
|
-
);
|
|
17
|
-
export const assign: (
|
|
18
|
-
<A extends Pointable>(pointer: string, subject: A, value: unknown) => void
|
|
19
|
-
) & (
|
|
20
|
-
(pointer: string) => Assigner
|
|
21
|
-
);
|
|
22
|
-
export const unset: (
|
|
23
|
-
<A extends Pointable>(pointer: string, subject: A) => A
|
|
24
|
-
) & (
|
|
25
|
-
(pointer: string) => Unsetter
|
|
26
|
-
);
|
|
27
|
-
export const remove: (
|
|
28
|
-
(pointer: string, subject: Pointable) => void
|
|
29
|
-
) & (
|
|
30
|
-
(pointer: string) => Remover
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
export type Getter = (subject: Pointable) => unknown;
|
|
34
|
-
export type Setter = (
|
|
35
|
-
<A extends Pointable>(subject: A, value: unknown) => A
|
|
36
|
-
) & (
|
|
37
|
-
<A extends Pointable>(subject: A) => (value: unknown) => A
|
|
38
|
-
);
|
|
39
|
-
export type Assigner = (
|
|
40
|
-
<A extends Pointable>(subject: A, value: unknown) => void
|
|
41
|
-
) & (
|
|
42
|
-
<A extends Pointable>(subject: A) => (value: unknown) => void
|
|
43
|
-
);
|
|
44
|
-
export type Unsetter = <A extends Pointable>(subject: A) => A;
|
|
45
|
-
export type Remover = (subject: Pointable) => void;
|
|
46
|
-
|
|
47
1
|
export type Json = string | number | boolean | null | JsonObject | Json[];
|
|
48
2
|
export type JsonObject = {
|
|
49
3
|
[property: string]: Json;
|
|
50
4
|
};
|
|
51
5
|
|
|
52
|
-
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,149 +1,310 @@
|
|
|
1
|
-
import
|
|
1
|
+
/** @import * as API from "./index.d.ts" */
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
/** @type API.nil */
|
|
4
5
|
export const nil = "";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if (pointer.length > 0 && pointer[0] !== "/") {
|
|
7
|
+
/** @type API.pointerSegments */
|
|
8
|
+
export const pointerSegments = function* (pointer) {
|
|
9
|
+
if (pointer.length > 0 && !pointer.startsWith("/")) {
|
|
10
10
|
throw Error("Invalid JSON Pointer");
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
let segmentStart = 1;
|
|
14
14
|
let segmentEnd = 0;
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
if (mode === EXISTS) {
|
|
18
|
-
return segmentEnd < pointer.length;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (segmentEnd >= pointer.length) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
16
|
+
while (segmentEnd < pointer.length) {
|
|
25
17
|
const position = pointer.indexOf("/", segmentStart);
|
|
26
18
|
segmentEnd = position === -1 ? pointer.length : position;
|
|
27
|
-
const segment =
|
|
19
|
+
const segment = pointer.slice(segmentStart, segmentEnd);
|
|
28
20
|
segmentStart = segmentEnd + 1;
|
|
29
21
|
|
|
30
|
-
|
|
31
|
-
}
|
|
22
|
+
yield unescape(segment);
|
|
23
|
+
}
|
|
32
24
|
};
|
|
33
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
|
+
*/
|
|
34
42
|
export const get = (pointer, subject = undefined) => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
if (subject === undefined) {
|
|
44
|
+
const segments = [...pointerSegments(pointer)];
|
|
45
|
+
return (subject) => _get(segments, subject);
|
|
46
|
+
} else {
|
|
47
|
+
return _get(pointerSegments(pointer), subject);
|
|
48
|
+
}
|
|
38
49
|
};
|
|
39
50
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
/** @type (segments: Iterable<string>, subject: API.Json | undefined) => API.Json | undefined */
|
|
52
|
+
const _get = (segments, subject) => {
|
|
53
|
+
/** @type string */
|
|
54
|
+
let cursor = nil;
|
|
55
|
+
for (const segment of segments) {
|
|
56
|
+
subject = applySegment(subject, segment, cursor);
|
|
57
|
+
cursor = append(segment, cursor);
|
|
46
58
|
}
|
|
59
|
+
|
|
60
|
+
return subject;
|
|
47
61
|
};
|
|
48
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
|
+
*/
|
|
49
81
|
export const set = (pointer, subject = undefined, value = undefined) => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
82
|
+
if (subject === undefined) {
|
|
83
|
+
return (subject, value) => _set(pointerSegments(pointer), subject, value);
|
|
84
|
+
} else {
|
|
85
|
+
return _set(pointerSegments(pointer), subject, /** @type API.Json */ (value));
|
|
86
|
+
}
|
|
53
87
|
};
|
|
54
88
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
89
|
+
/** @type (segments: Generator<string>, subject: API.Json | undefined, value: API.Json, cursor?: string) => API.Json */
|
|
90
|
+
const _set = (segments, subject, value, cursor = nil) => {
|
|
91
|
+
const segment = segments.next();
|
|
92
|
+
if (segment.done) {
|
|
58
93
|
return value;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return clonedSubject;
|
|
64
|
-
} else {
|
|
65
|
-
return { ...subject, [segment]: _set(nextSegment, applySegment(subject, segment, cursor), value, append(segment, cursor)) };
|
|
66
|
-
}
|
|
67
|
-
} else if (Array.isArray(subject)) {
|
|
68
|
-
const clonedSubject = [...subject];
|
|
69
|
-
clonedSubject[computeSegment(subject, segment)] = value;
|
|
70
|
-
return clonedSubject;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (Array.isArray(subject)) {
|
|
97
|
+
subject = [...subject];
|
|
71
98
|
} else if (typeof subject === "object" && subject !== null) {
|
|
72
|
-
|
|
99
|
+
subject = { ...subject };
|
|
73
100
|
} else {
|
|
74
|
-
|
|
101
|
+
applySegment(subject, segment.value, cursor);
|
|
75
102
|
}
|
|
103
|
+
cursor = append(segment.value, cursor);
|
|
104
|
+
|
|
105
|
+
// currentSubject could also be an array, but this appeases the type system
|
|
106
|
+
const currentSubject = /** @type API.JsonObject */ (subject);
|
|
107
|
+
const computedSegment = computeSegment(subject, segment.value);
|
|
108
|
+
currentSubject[computedSegment] = _set(segments, currentSubject[computedSegment], value, cursor);
|
|
109
|
+
return currentSubject;
|
|
76
110
|
};
|
|
77
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
|
+
*/
|
|
78
130
|
export const assign = (pointer, subject = undefined, value = undefined) => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
131
|
+
if (subject === undefined) {
|
|
132
|
+
return (subject, value) => _assign(pointerSegments(pointer), subject, value);
|
|
133
|
+
} else {
|
|
134
|
+
return _assign(pointerSegments(pointer), subject, /** @type API.Json */ (value));
|
|
135
|
+
}
|
|
82
136
|
};
|
|
83
137
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
138
|
+
/** @type (segments: Generator<string>, subject: API.Json, value: API.Json, cursor?: string) => void */
|
|
139
|
+
const _assign = (segments, subject, value, cursor = nil) => {
|
|
140
|
+
/** @type string | undefined */
|
|
141
|
+
let lastSegment;
|
|
142
|
+
|
|
143
|
+
/** @type API.Json | undefined */
|
|
144
|
+
let currentSubject = subject;
|
|
145
|
+
|
|
146
|
+
/** @type API.Json | undefined */
|
|
147
|
+
let lastSubject;
|
|
148
|
+
|
|
149
|
+
for (let segment of segments) {
|
|
150
|
+
segment = computeSegment(currentSubject, segment);
|
|
151
|
+
lastSegment = segment;
|
|
152
|
+
lastSubject = currentSubject;
|
|
153
|
+
currentSubject = applySegment(currentSubject, segment, cursor);
|
|
154
|
+
cursor = append(segment, cursor);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (lastSegment === undefined) {
|
|
87
158
|
return;
|
|
88
|
-
} else if (!nextSegment(EXISTS) && !isScalar(subject)) {
|
|
89
|
-
subject[computeSegment(subject, segment)] = value;
|
|
90
|
-
} else {
|
|
91
|
-
_assign(nextSegment, applySegment(subject, segment, cursor), value, append(segment, cursor));
|
|
92
159
|
}
|
|
160
|
+
|
|
161
|
+
// lastSubject could also be an array, but this appeases the type system
|
|
162
|
+
/** @type API.JsonObject */ (lastSubject)[lastSegment] = value;
|
|
93
163
|
};
|
|
94
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
|
+
*/
|
|
95
181
|
export const unset = (pointer, subject = undefined) => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
182
|
+
if (subject === undefined) {
|
|
183
|
+
return (subject) => _unset(pointerSegments(pointer), subject);
|
|
184
|
+
} else {
|
|
185
|
+
return _unset(pointerSegments(pointer), subject);
|
|
186
|
+
}
|
|
99
187
|
};
|
|
100
188
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
189
|
+
/**
|
|
190
|
+
* @param {Generator<string>} segments
|
|
191
|
+
* @param {API.Json | undefined} [subject]
|
|
192
|
+
* @param {string} [cursor]
|
|
193
|
+
* @returns {API.JsonObject | API.Json[] | undefined}
|
|
194
|
+
*/
|
|
195
|
+
const _unset = (segments, subject, cursor = nil) => {
|
|
196
|
+
const segment = segments.next();
|
|
197
|
+
if (segment.done) {
|
|
104
198
|
return;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const clonedSubject = [...subject];
|
|
110
|
-
delete clonedSubject[computeSegment(subject, segment)];
|
|
111
|
-
return clonedSubject;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (Array.isArray(subject)) {
|
|
202
|
+
subject = [...subject];
|
|
112
203
|
} else if (typeof subject === "object" && subject !== null) {
|
|
113
|
-
|
|
114
|
-
const { [segment]: _, ...result } = subject;
|
|
115
|
-
return result;
|
|
204
|
+
subject = { ...subject };
|
|
116
205
|
} else {
|
|
117
|
-
|
|
206
|
+
applySegment(subject, segment.value, cursor);
|
|
118
207
|
}
|
|
208
|
+
cursor = append(segment.value, cursor);
|
|
209
|
+
|
|
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
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return currentSubject;
|
|
119
222
|
};
|
|
120
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
|
+
*/
|
|
121
240
|
export const remove = (pointer, subject = undefined) => {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
241
|
+
if (subject === undefined) {
|
|
242
|
+
return (subject) => _remove(pointerSegments(pointer), subject);
|
|
243
|
+
} else {
|
|
244
|
+
return _remove(pointerSegments(pointer), subject);
|
|
245
|
+
}
|
|
125
246
|
};
|
|
126
247
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
248
|
+
/** @type (segments: Generator<string>, subject: API.Json, cursor?: string) => void */
|
|
249
|
+
const _remove = (segments, subject, cursor = nil) => {
|
|
250
|
+
/** @type string | undefined */
|
|
251
|
+
let lastSegment;
|
|
252
|
+
|
|
253
|
+
/** @type API.Json | undefined */
|
|
254
|
+
let currentSubject = subject;
|
|
255
|
+
|
|
256
|
+
/** @type API.Json | undefined */
|
|
257
|
+
let lastSubject;
|
|
258
|
+
|
|
259
|
+
for (let segment of segments) {
|
|
260
|
+
segment = computeSegment(currentSubject, segment);
|
|
261
|
+
lastSegment = segment;
|
|
262
|
+
lastSubject = currentSubject;
|
|
263
|
+
currentSubject = applySegment(currentSubject, segment, cursor);
|
|
264
|
+
cursor = append(segment, cursor);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (lastSegment === undefined) {
|
|
130
268
|
return;
|
|
131
|
-
} else if (nextSegment(EXISTS)) {
|
|
132
|
-
const value = applySegment(subject, segment, cursor);
|
|
133
|
-
_remove(nextSegment, value, append(segment, cursor));
|
|
134
|
-
} else if (!isScalar(subject)) {
|
|
135
|
-
delete subject[segment];
|
|
136
|
-
} else {
|
|
137
|
-
applySegment(subject, segment, cursor);
|
|
138
269
|
}
|
|
270
|
+
|
|
271
|
+
// lastSubject could also be an array, but this appeases the type system
|
|
272
|
+
delete /** @type API.JsonObject */ (lastSubject)[lastSegment];
|
|
139
273
|
};
|
|
140
274
|
|
|
141
|
-
|
|
275
|
+
/** @type API.append */
|
|
276
|
+
export const append = (segment, pointer) => pointer + "/" + escape(segment);
|
|
142
277
|
|
|
278
|
+
/** @type (segment: string) => string */
|
|
143
279
|
const escape = (segment) => segment.toString().replace(/~/g, "~0").replace(/\//g, "~1");
|
|
280
|
+
|
|
281
|
+
/** @type (segment: string) => string */
|
|
144
282
|
const unescape = (segment) => segment.toString().replace(/~1/g, "/").replace(/~0/g, "~");
|
|
145
|
-
const computeSegment = (value, segment) => Array.isArray(value) && segment === "-" ? value.length : segment;
|
|
146
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 */
|
|
147
308
|
const applySegment = (value, segment, cursor = "") => {
|
|
148
309
|
if (value === undefined) {
|
|
149
310
|
throw TypeError(`Value at '${cursor}' is undefined and does not have property '${segment}'`);
|
|
@@ -153,8 +314,12 @@ const applySegment = (value, segment, cursor = "") => {
|
|
|
153
314
|
throw TypeError(`Value at '${cursor}' is a ${typeof value} and does not have property '${segment}'`);
|
|
154
315
|
} else {
|
|
155
316
|
const computedSegment = computeSegment(value, segment);
|
|
156
|
-
|
|
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
|
+
}
|
|
157
321
|
}
|
|
158
322
|
};
|
|
159
323
|
|
|
324
|
+
/** @type (value: API.Json) => value is string | number | boolean | null */
|
|
160
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.
|
|
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": "
|
|
10
|
+
"test": "vitest --watch=false",
|
|
11
|
+
"type-check": "tsc --noEmit"
|
|
12
12
|
},
|
|
13
13
|
"repository": "github:hyperjump-io/json-pointer",
|
|
14
14
|
"keywords": [
|
|
@@ -22,20 +22,11 @@
|
|
|
22
22
|
"url": "https://github.com/sponsors/jdesrosiers"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@
|
|
26
|
-
"@types/
|
|
27
|
-
"@typescript-eslint/eslint-plugin": "*",
|
|
28
|
-
"@typescript-eslint/parser": "*",
|
|
29
|
-
"chai": "*",
|
|
30
|
-
"eslint": "*",
|
|
31
|
-
"eslint-import-resolver-node": "*",
|
|
25
|
+
"@stylistic/eslint-plugin": "*",
|
|
26
|
+
"@types/node": "*",
|
|
32
27
|
"eslint-import-resolver-typescript": "*",
|
|
33
28
|
"eslint-plugin-import": "*",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"typescript": "*"
|
|
37
|
-
},
|
|
38
|
-
"dependencies": {
|
|
39
|
-
"just-curry-it": "^5.3.0"
|
|
29
|
+
"typescript-eslint": "*",
|
|
30
|
+
"vitest": "*"
|
|
40
31
|
}
|
|
41
32
|
}
|
package/.github/FUNDING.yml
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
github: [jdesrosiers]
|