@hyperjump/json-pointer 1.1.0 → 1.1.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2018 Jason Desrosiers
3
+ Copyright (c) 2018 Hyperjump Software, LLC
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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 type Pointable = JsonObject | Json[];
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,16 @@
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[0] !== "/") {
9
+ if (pointer.length > 0 && !pointer.startsWith("/")) {
10
+ throw Error("Invalid JSON Pointer");
11
+ }
12
+
13
+ if (/~(?![01])/.test(pointer)) {
5
14
  throw Error("Invalid JSON Pointer");
6
15
  }
7
16
 
@@ -18,6 +27,22 @@ export const pointerSegments = function* (pointer) {
18
27
  }
19
28
  };
20
29
 
30
+ /**
31
+ * @overload
32
+ * @param {string} pointer
33
+ * @return {API.Getter}
34
+ *
35
+ * @overload
36
+ * @param {string} pointer
37
+ * @param {API.Json} subject
38
+ * @return {API.Json | undefined}
39
+ *
40
+ * @param {string} pointer
41
+ * @param {API.Json} [subject]
42
+ * @return {API.Json | undefined | API.Getter}
43
+ *
44
+ * @type API.get
45
+ */
21
46
  export const get = (pointer, subject = undefined) => {
22
47
  if (subject === undefined) {
23
48
  const segments = [...pointerSegments(pointer)];
@@ -27,7 +52,9 @@ export const get = (pointer, subject = undefined) => {
27
52
  }
28
53
  };
29
54
 
55
+ /** @type (segments: Iterable<string>, subject: API.Json | undefined) => API.Json | undefined */
30
56
  const _get = (segments, subject) => {
57
+ /** @type string */
31
58
  let cursor = nil;
32
59
  for (const segment of segments) {
33
60
  subject = applySegment(subject, segment, cursor);
@@ -37,15 +64,33 @@ const _get = (segments, subject) => {
37
64
  return subject;
38
65
  };
39
66
 
67
+ /**
68
+ * @overload
69
+ * @param {string} pointer
70
+ * @return {API.Setter}
71
+ *
72
+ * @overload
73
+ * @param {string} pointer
74
+ * @param {API.Json} subject
75
+ * @param {API.Json} value
76
+ * @return {API.Json}
77
+ *
78
+ * @param {string} pointer
79
+ * @param {API.Json} [subject]
80
+ * @param {API.Json} [value]
81
+ * @return {API.Json | API.Setter}
82
+ *
83
+ * @type API.set
84
+ */
40
85
  export const set = (pointer, subject = undefined, value = undefined) => {
41
86
  if (subject === undefined) {
42
- const segments = [...pointerSegments(pointer)];
43
- return (subject, value) => _set(segments.values(), subject, value);
87
+ return (subject, value) => _set(pointerSegments(pointer), subject, value);
44
88
  } else {
45
- return _set(pointerSegments(pointer), subject, value);
89
+ return _set(pointerSegments(pointer), subject, /** @type API.Json */ (value));
46
90
  }
47
91
  };
48
92
 
93
+ /** @type (segments: Generator<string>, subject: API.Json | undefined, value: API.Json, cursor?: string) => API.Json */
49
94
  const _set = (segments, subject, value, cursor = nil) => {
50
95
  const segment = segments.next();
51
96
  if (segment.done) {
@@ -61,45 +106,96 @@ const _set = (segments, subject, value, cursor = nil) => {
61
106
  }
62
107
  cursor = append(segment.value, cursor);
63
108
 
109
+ // currentSubject could also be an array, but this appeases the type system
110
+ const currentSubject = /** @type API.JsonObject */ (subject);
64
111
  const computedSegment = computeSegment(subject, segment.value);
65
- subject[computedSegment] = _set(segments, subject[computedSegment], value, cursor);
66
- return subject;
112
+ currentSubject[computedSegment] = _set(segments, currentSubject[computedSegment], value, cursor);
113
+ return currentSubject;
67
114
  };
68
115
 
116
+ /**
117
+ * @overload
118
+ * @param {string} pointer
119
+ * @returns {API.Assigner}
120
+ *
121
+ * @overload
122
+ * @param {string} pointer
123
+ * @param {API.Json} subject
124
+ * @param {API.Json} value
125
+ * @returns {void}
126
+ *
127
+ * @param {string} pointer
128
+ * @param {API.Json} [subject]
129
+ * @param {API.Json} [value]
130
+ * @returns {void | API.Assigner}
131
+ *
132
+ * @type API.assign
133
+ */
69
134
  export const assign = (pointer, subject = undefined, value = undefined) => {
70
135
  if (subject === undefined) {
71
- const segments = [...pointerSegments(pointer)];
72
- return (subject, value) => _assign(segments.values(), subject, value);
136
+ return (subject, value) => _assign(pointerSegments(pointer), subject, value);
73
137
  } else {
74
- return _assign(pointerSegments(pointer), subject, value);
138
+ return _assign(pointerSegments(pointer), subject, /** @type API.Json */ (value));
75
139
  }
76
140
  };
77
141
 
142
+ /** @type (segments: Generator<string>, subject: API.Json, value: API.Json, cursor?: string) => void */
78
143
  const _assign = (segments, subject, value, cursor = nil) => {
144
+ /** @type string | undefined */
79
145
  let lastSegment;
146
+
147
+ /** @type API.Json | undefined */
148
+ let currentSubject = subject;
149
+
150
+ /** @type API.Json | undefined */
80
151
  let lastSubject;
152
+
81
153
  for (let segment of segments) {
82
- segment = computeSegment(subject, segment);
154
+ segment = computeSegment(currentSubject, segment);
83
155
  lastSegment = segment;
84
- lastSubject = subject;
85
- subject = applySegment(subject, segment, cursor);
156
+ lastSubject = currentSubject;
157
+ currentSubject = applySegment(currentSubject, segment, cursor);
86
158
  cursor = append(segment, cursor);
87
159
  }
88
160
 
89
- if (lastSubject !== undefined) {
90
- lastSubject[lastSegment] = value;
161
+ if (lastSegment === undefined) {
162
+ return;
91
163
  }
164
+
165
+ // lastSubject could also be an array, but this appeases the type system
166
+ /** @type API.JsonObject */ (lastSubject)[lastSegment] = value;
92
167
  };
93
168
 
169
+ /**
170
+ * @overload
171
+ * @param {string} pointer
172
+ * @returns {API.Unsetter}
173
+ *
174
+ * @overload
175
+ * @param {string} pointer
176
+ * @param {API.Json} subject
177
+ * @returns {API.Json | undefined}
178
+ *
179
+ * @param {string} pointer
180
+ * @param {API.Json} [subject]
181
+ * @returns {API.Json | undefined | API.Unsetter}
182
+ *
183
+ * @type API.unset
184
+ */
94
185
  export const unset = (pointer, subject = undefined) => {
95
186
  if (subject === undefined) {
96
- const segments = [...pointerSegments(pointer)];
97
- return (subject) => _unset(segments.values(), subject);
187
+ return (subject) => _unset(pointerSegments(pointer), subject);
98
188
  } else {
99
189
  return _unset(pointerSegments(pointer), subject);
100
190
  }
101
191
  };
102
192
 
193
+ /**
194
+ * @param {Generator<string>} segments
195
+ * @param {API.Json | undefined} [subject]
196
+ * @param {string} [cursor]
197
+ * @returns {API.JsonObject | API.Json[] | undefined}
198
+ */
103
199
  const _unset = (segments, subject, cursor = nil) => {
104
200
  const segment = segments.next();
105
201
  if (segment.done) {
@@ -115,45 +211,104 @@ const _unset = (segments, subject, cursor = nil) => {
115
211
  }
116
212
  cursor = append(segment.value, cursor);
117
213
 
118
- const computedSegment = computeSegment(subject, segment.value);
119
- const unsetSubject = _unset(segments, subject[computedSegment], cursor);
120
- if (computedSegment in subject) {
121
- subject[computedSegment] = unsetSubject;
214
+ // currentSubject could also be an array, but this appeases the type system
215
+ const currentSubject = /** @type API.JsonObject */ (subject);
216
+ const computedSegment = computeSegment(currentSubject, segment.value);
217
+ const unsetSubject = _unset(segments, currentSubject[computedSegment], cursor);
218
+ if (computedSegment in currentSubject) {
219
+ if (unsetSubject === undefined) {
220
+ delete currentSubject[computedSegment];
221
+ } else {
222
+ currentSubject[computedSegment] = unsetSubject;
223
+ }
122
224
  }
123
- return subject;
225
+ return currentSubject;
124
226
  };
125
227
 
228
+ /**
229
+ * @overload
230
+ * @param {string} pointer
231
+ * @returns {API.Remover}
232
+ *
233
+ * @overload
234
+ * @param {string} pointer
235
+ * @param {API.Json} subject
236
+ * @returns {void}
237
+ *
238
+ * @param {string} pointer
239
+ * @param {API.Json} [subject]
240
+ * @returns {void | API.Remover}
241
+ *
242
+ * @type API.remove
243
+ */
126
244
  export const remove = (pointer, subject = undefined) => {
127
245
  if (subject === undefined) {
128
- const segments = [...pointerSegments(pointer)];
129
- return (subject) => _remove(segments.values(), subject);
246
+ return (subject) => _remove(pointerSegments(pointer), subject);
130
247
  } else {
131
248
  return _remove(pointerSegments(pointer), subject);
132
249
  }
133
250
  };
134
251
 
252
+ /** @type (segments: Generator<string>, subject: API.Json, cursor?: string) => void */
135
253
  const _remove = (segments, subject, cursor = nil) => {
254
+ /** @type string | undefined */
136
255
  let lastSegment;
256
+
257
+ /** @type API.Json | undefined */
258
+ let currentSubject = subject;
259
+
260
+ /** @type API.Json | undefined */
137
261
  let lastSubject;
262
+
138
263
  for (let segment of segments) {
139
- segment = computeSegment(subject, segment);
264
+ segment = computeSegment(currentSubject, segment);
140
265
  lastSegment = segment;
141
- lastSubject = subject;
142
- subject = applySegment(subject, segment, cursor);
266
+ lastSubject = currentSubject;
267
+ currentSubject = applySegment(currentSubject, segment, cursor);
143
268
  cursor = append(segment, cursor);
144
269
  }
145
270
 
146
- if (lastSubject !== undefined) {
147
- delete lastSubject[lastSegment];
271
+ if (lastSegment === undefined) {
272
+ return;
148
273
  }
274
+
275
+ // lastSubject could also be an array, but this appeases the type system
276
+ delete /** @type API.JsonObject */ (lastSubject)[lastSegment];
149
277
  };
150
278
 
279
+ /** @type API.append */
151
280
  export const append = (segment, pointer) => pointer + "/" + escape(segment);
152
281
 
282
+ /** @type (segment: string) => string */
153
283
  const escape = (segment) => segment.toString().replace(/~/g, "~0").replace(/\//g, "~1");
284
+
285
+ /** @type (segment: string) => string */
154
286
  const unescape = (segment) => segment.toString().replace(/~1/g, "/").replace(/~0/g, "~");
155
- const computeSegment = (value, segment) => Array.isArray(value) && segment === "-" ? value.length : segment;
156
287
 
288
+ /**
289
+ * @overload
290
+ * @param {API.Json[]} value
291
+ * @param {string} segment
292
+ * @returns {number}
293
+ *
294
+ * @overload
295
+ * @param {API.Json | undefined} value
296
+ * @param {string} segment
297
+ * @returns {string}
298
+ *
299
+ * @param {API.Json | undefined} value
300
+ * @param {string} segment
301
+ * @returns {string | number}
302
+ */
303
+ const computeSegment = (value, segment) => {
304
+ if (Array.isArray(value)) {
305
+ return segment === "-" ? value.length : parseInt(segment, 10);
306
+ } else {
307
+ return segment;
308
+ }
309
+ };
310
+
311
+ /** @type (value: API.Json | undefined, segment: string, cursor?: string) => API.Json | undefined */
157
312
  const applySegment = (value, segment, cursor = "") => {
158
313
  if (value === undefined) {
159
314
  throw TypeError(`Value at '${cursor}' is undefined and does not have property '${segment}'`);
@@ -163,8 +318,12 @@ const applySegment = (value, segment, cursor = "") => {
163
318
  throw TypeError(`Value at '${cursor}' is a ${typeof value} and does not have property '${segment}'`);
164
319
  } else {
165
320
  const computedSegment = computeSegment(value, segment);
166
- return value[computedSegment];
321
+ if (Object.hasOwn(value, computedSegment)) {
322
+ // value could also be an array, but this appeases the type system
323
+ return /** @type API.JsonObject */ (value)[computedSegment];
324
+ }
167
325
  }
168
326
  };
169
327
 
328
+ /** @type (value: API.Json) => value is string | number | boolean | null */
170
329
  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.0",
3
+ "version": "1.1.2",
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
- "@typescript-eslint/eslint-plugin": "*",
26
- "@typescript-eslint/parser": "*",
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
  }
@@ -1 +0,0 @@
1
- github: [jdesrosiers]