@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 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/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
- const JsonPointer = require("@hyperjump/json-pointer");
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, "foo"); // "/foo"
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
- Contributing
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 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,149 +1,310 @@
1
- import curry from "just-curry-it";
1
+ /** @import * as API from "./index.d.ts" */
2
2
 
3
3
 
4
+ /** @type API.nil */
4
5
  export const nil = "";
5
6
 
6
- const EXISTS = Symbol("EXISTS");
7
-
8
- const segmentGenerator = (pointer) => {
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
- return (mode) => {
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 = unescape(pointer.slice(segmentStart, segmentEnd));
19
+ const segment = pointer.slice(segmentStart, segmentEnd);
28
20
  segmentStart = segmentEnd + 1;
29
21
 
30
- return segment;
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
- const nextSegment = segmentGenerator(pointer);
36
- const fn = (subject) => _get(nextSegment, subject, nil);
37
- return subject === undefined ? fn : fn(subject);
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
- const _get = (nextSegment, subject, cursor) => {
41
- if (!nextSegment(EXISTS)) {
42
- return subject;
43
- } else {
44
- const segment = nextSegment();
45
- return _get(nextSegment, applySegment(subject, segment, cursor), append(segment, cursor));
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
- const nextSegment = segmentGenerator(pointer);
51
- const fn = curry((subject, value) => _set(nextSegment, subject, value, nil));
52
- return subject === undefined ? fn : fn(subject, value);
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
- const _set = (nextSegment, subject, value, cursor) => {
56
- const segment = nextSegment();
57
- if (segment === undefined) {
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
- } else if (nextSegment(EXISTS)) {
60
- if (Array.isArray(subject)) {
61
- const clonedSubject = [...subject];
62
- clonedSubject[segment] = _set(nextSegment, applySegment(subject, segment, cursor), value, append(segment, cursor));
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
- return { ...subject, [segment]: value };
99
+ subject = { ...subject };
73
100
  } else {
74
- return applySegment(subject, segment, cursor);
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
- const nextSegment = segmentGenerator(pointer);
80
- const fn = curry((subject, value) => _assign(nextSegment, subject, value, nil));
81
- return subject === undefined ? fn : fn(subject, value);
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
- const _assign = (nextSegment, subject, value, cursor) => {
85
- const segment = nextSegment();
86
- if (segment === undefined) {
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
- const nextSegment = segmentGenerator(pointer);
97
- const fn = (subject) => _unset(nextSegment, subject, nil);
98
- return subject === undefined ? fn : fn(subject);
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
- const _unset = (nextSegment, subject, cursor) => {
102
- const segment = nextSegment();
103
- if (segment === undefined) {
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
- } else if (nextSegment(EXISTS)) {
106
- const value = applySegment(subject, segment, cursor);
107
- return { ...subject, [segment]: _unset(nextSegment, value, append(segment, cursor)) };
108
- } else if (Array.isArray(subject)) {
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
- // eslint-disable-next-line no-unused-vars
114
- const { [segment]: _, ...result } = subject;
115
- return result;
204
+ subject = { ...subject };
116
205
  } else {
117
- return applySegment(subject, segment, cursor);
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
- const nextSegment = segmentGenerator(pointer);
123
- const fn = (subject) => _remove(nextSegment, subject, nil);
124
- return subject === undefined ? fn : fn(subject);
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
- const _remove = (nextSegment, subject, cursor) => {
128
- const segment = nextSegment();
129
- if (segment === undefined) {
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
- export const append = curry((segment, pointer) => pointer + "/" + escape(segment));
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
- return value[computedSegment];
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.0.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": "mocha 'lib/**/*.spec.ts'"
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
- "@types/chai": "*",
26
- "@types/mocha": "*",
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
- "mocha": "*",
35
- "ts-node": "*",
36
- "typescript": "*"
37
- },
38
- "dependencies": {
39
- "just-curry-it": "^5.3.0"
29
+ "typescript-eslint": "*",
30
+ "vitest": "*"
40
31
  }
41
32
  }
@@ -1 +0,0 @@
1
- github: [jdesrosiers]