ts-data-forge 14.11.1 → 14.12.0

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.
@@ -32,48 +32,73 @@ export declare namespace Json {
32
32
  * }
33
33
  *
34
34
  * assert.isTrue(Result.isErr(failed));
35
+ * ```
36
+ *
37
+ * @param text - A valid JSON string to parse. Can contain any valid JSON data
38
+ * type: primitives (string, number, boolean, null), arrays, or objects.
39
+ * @returns A `Result<JsonValue, string>` containing:
40
+ *
41
+ * - On success: `Result.ok(parsedValue)` where `parsedValue` is the parsed
42
+ * JSON.
43
+ * - On failure: `Result.err(errorMessage)` where `errorMessage` describes the
44
+ * parsing error
45
+ *
46
+ * The form that takes a reviver is deprecated: its result is whatever the
47
+ * reviver returned, which need not be JSON although it is typed
48
+ * `JsonValue`. Use {@link parseWithReviver}, which types it `unknown`.
49
+ */
50
+ export const parse: Parse;
51
+ /**
52
+ * Safely converts a JSON string into a JavaScript value, passing each value
53
+ * through `reviver` as `JSON.parse` does, without throwing exceptions.
35
54
  *
36
- * // With reviver
55
+ * The result is whatever the reviver returned for the root, built from what
56
+ * it returned for every child, so it is typed `unknown`: narrow it before
57
+ * use.
58
+ *
59
+ * @example
60
+ *
61
+ * ```ts
37
62
  * const jsonWithDate = '{"created": "2024-01-01T00:00:00.000Z"}';
38
63
  *
39
- * const withReviver = Json.parse(jsonWithDate, (key, value) =>
64
+ * const revived = Json.parseWithReviver(jsonWithDate, (key, value) =>
40
65
  * key === 'created' && typeof value === 'string' ? new Date(value) : value,
41
66
  * );
42
67
  *
43
- * assert.isTrue(Result.isOk(withReviver));
68
+ * assert.isTrue(Result.isOk(revived));
44
69
  *
45
- * // With a reviver, the result is whatever the reviver returned, not JSON:
46
- * // `created` is a `Date` here although it is typed `JsonValue`. Widen it to
47
- * // `unknown` before narrowing it.
48
- * const revived: unknown = withReviver.value;
70
+ * // The result is whatever the reviver returned, typed `unknown`: here
71
+ * // `created` is a `Date`, not JSON.
72
+ * const root = Result.unwrapOkOr(revived, undefined);
49
73
  *
50
- * assert.isTrue(isRecord(revived));
74
+ * assert.isTrue(isRecord(root));
51
75
  *
52
- * assert.instanceOf(revived['created'], Date);
76
+ * assert.instanceOf(root['created'], Date);
53
77
  * ```
54
78
  *
55
- * @param text - A valid JSON string to parse. Can contain any valid JSON data
56
- * type: primitives (string, number, boolean, null), arrays, or objects.
57
- * @param reviver - Optional function that transforms parsed values. Called
58
- * for each key-value pair in the JSON. The function receives the key name
59
- * and parsed value, and should return the transformed value. For nested
60
- * objects, inner objects are processed before outer objects, so the
61
- * `value` an outer object is passed holds what the reviver already
62
- * returned for its children, which need not be JSON (see `@returns`).
63
- * Returning
64
- * `undefined` for a child deletes that property from an object and leaves
65
- * a hole in an array. The root is passed with the key `''`.
66
- * @returns A `Result<JsonValue, string>` containing:
67
- *
68
- * - On success: `Result.ok(parsedValue)` where `parsedValue` is the parsed
69
- * JSON. With a reviver it is whatever the reviver returned for the root
70
- * (a `Date`, `undefined`, ...), which is not checked to be JSON although
71
- * it is typed `JsonValue`; widen it to `unknown` before narrowing it,
72
- * as the example does.
73
- * - On failure: `Result.err(errorMessage)` where `errorMessage` describes the
74
- * parsing error
79
+ * @param text - A valid JSON string to parse.
80
+ * @param reviver - Called for each key-value pair, children before their
81
+ * parent, so the `value` a parent is passed holds what the reviver already
82
+ * returned for its children, which need not be JSON. Returning `undefined`
83
+ * for a child deletes that property from an object and leaves a hole in an
84
+ * array. The root is passed with the key `''`. `undefined` parses as
85
+ * {@link parse} does, typed `unknown`.
86
+ * @returns `Result.ok` of what the reviver returned for the root, or
87
+ * `Result.err` of a message describing the parsing error.
75
88
  */
76
- const parse: (text: string, reviver?: (this: unknown, key: string, value: JsonValue) => unknown) => Result<JsonValue, string>;
89
+ export const parseWithReviver: (text: string, reviver: Reviver | undefined) => Result<unknown, string>;
90
+ /** What {@link parseWithReviver} calls for each key-value pair. */
91
+ export type Reviver = (this: unknown, key: string, value: unknown) => unknown;
92
+ /** The call signatures of {@link parse}. */
93
+ type Parse = {
94
+ (text: string): Result<JsonValue, string>;
95
+ /**
96
+ * @deprecated Use `Json.parseWithReviver`, whose result is typed
97
+ * `unknown`: a reviver need not return JSON. The next major removes this
98
+ * form (`NEXT_MAJOR.md`).
99
+ */
100
+ (text: string, reviver?: (this: unknown, key: string, value: JsonValue) => unknown): Result<JsonValue, string>;
101
+ };
77
102
  /**
78
103
  * Safely converts a JavaScript value to a JSON string without throwing
79
104
  * exceptions.
@@ -139,7 +164,7 @@ export declare namespace Json {
139
164
  * - On failure: `Result.err(errorMessage)` where `errorMessage` describes the
140
165
  * error
141
166
  */
142
- const stringify: (value: unknown, replacer?: (this: unknown, key: string, val: unknown) => unknown, space?: UintRangeInclusive<1, 10> | string) => Result<string | undefined, string>;
167
+ export const stringify: (value: unknown, replacer?: (this: unknown, key: string, val: unknown) => unknown, space?: UintRangeInclusive<1, 10> | string) => Result<string | undefined, string>;
143
168
  /**
144
169
  * Safely converts a JavaScript value to a JSON string, including only the
145
170
  * specified properties.
@@ -205,7 +230,7 @@ export declare namespace Json {
205
230
  * - On success: `Result.ok(jsonString)` with only selected properties
206
231
  * - On failure: `Result.err(errorMessage)` describing the serialization error
207
232
  */
208
- const stringifySelected: (value: unknown, propertiesToBeSelected?: readonly (number | string)[], space?: UintRangeInclusive<1, 10> | string) => Result<string | undefined, string>;
233
+ export const stringifySelected: (value: unknown, propertiesToBeSelected?: readonly (number | string)[], space?: UintRangeInclusive<1, 10> | string) => Result<string | undefined, string>;
209
234
  /**
210
235
  * Safely converts a JavaScript record to a JSON string with keys sorted
211
236
  * alphabetically at all levels.
@@ -283,6 +308,7 @@ export declare namespace Json {
283
308
  * alphabetically
284
309
  * - On failure: `Result.err(errorMessage)` describing the serialization error
285
310
  */
286
- const stringifySortedKey: (value: UnknownRecord, space?: UintRangeInclusive<1, 10> | string) => Result<string | undefined, string>;
311
+ export const stringifySortedKey: (value: UnknownRecord, space?: UintRangeInclusive<1, 10> | string) => Result<string | undefined, string>;
312
+ export {};
287
313
  }
288
314
  //# sourceMappingURL=json.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"json.d.mts","sourceRoot":"","sources":["../../src/json/json.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,kBAAkB,EAClB,aAAa,EACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAIjD;;;;GAIG;AACH,yBAAiB,IAAI,CAAC;IACpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkEG;IACI,MAAM,KAAK,SACV,MAAM,YACF,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,KAAK,OAAO,KAClE,MAAM,CAAC,SAAS,EAAE,MAAM,CAc1B,CAAC;IAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgEG;IACI,MAAM,SAAS,UACb,OAAO,aACH,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,UACxD,kBAAkB,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,KACzC,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,MAAM,CAQnC,CAAC;IAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgEG;IACI,MAAM,iBAAiB,UACrB,OAAO,2BACW,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,UAC7C,kBAAkB,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,KACzC,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,MAAM,CAUnC,CAAC;IAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4EG;IACI,MAAM,kBAAkB,UACtB,aAAa,UACZ,kBAAkB,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,KACzC,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,MAAM,CAoBnC,CAAC;CACH"}
1
+ {"version":3,"file":"json.d.mts","sourceRoot":"","sources":["../../src/json/json.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,kBAAkB,EAClB,aAAa,EACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAIjD;;;;GAIG;AACH,yBAAiB,IAAI,CAAC;IACpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;IACH,MAAM,CAAC,MAAM,KAAK,EAAE,KAQjB,CAAC;IAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACH,MAAM,CAAC,MAAM,gBAAgB,SACrB,MAAM,WACH,OAAO,GAAG,SAAS,KAC3B,MAAM,CAAC,OAAO,EAAE,MAAM,CAQxB,CAAC;IAEF,mEAAmE;IACnE,MAAM,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IAE9E,4CAA4C;IAI5C,KAAK,KAAK,GAAG;QACX,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAE1C;;;;WAIG;QACH,CACE,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,KAAK,OAAO,GAClE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;KAC9B,CAAC;IAGF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgEG;IACH,MAAM,CAAC,MAAM,SAAS,UACb,OAAO,aACH,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,UACxD,kBAAkB,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,KACzC,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,MAAM,CAQnC,CAAC;IAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgEG;IACH,MAAM,CAAC,MAAM,iBAAiB,UACrB,OAAO,2BACW,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,UAC7C,kBAAkB,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,KACzC,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,MAAM,CAUnC,CAAC;IAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4EG;IACH,MAAM,CAAC,MAAM,kBAAkB,UACtB,aAAa,UACZ,kBAAkB,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,KACzC,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,MAAM,CAoBnC,CAAC;;CACH"}
@@ -49,6 +49,15 @@ export var Json;
49
49
 
50
50
 
51
51
 
52
+ Json.parse = (text, reviver) =>
53
+
54
+ Json.parseWithReviver(text, reviver);
55
+
56
+
57
+
58
+
59
+
60
+
52
61
 
53
62
 
54
63
 
@@ -75,11 +84,15 @@ export var Json;
75
84
 
76
85
 
77
86
 
78
- Json.parse = (text, reviver) => {
79
- try {
80
- return Result.ok(JSON.parse(text,
81
87
 
82
- reviver));
88
+
89
+
90
+
91
+
92
+
93
+ Json.parseWithReviver = (text, reviver) => {
94
+ try {
95
+ return Result.ok(JSON.parse(text, reviver));
83
96
  }
84
97
  catch (error) {
85
98
  const errStr = unknownToString(error);
@@ -149,6 +162,7 @@ export var Json;
149
162
 
150
163
 
151
164
 
165
+
152
166
 
153
167
 
154
168
  Json.stringify = (value, replacer, space) => {
@@ -1 +1 @@
1
- {"version":3,"file":"json.mjs","sourceRoot":"","sources":["../../src/json/json.mts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"json.mjs","sourceRoot":"","sources":["../../src/json/json.mts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-data-forge",
3
- "version": "14.11.1",
3
+ "version": "14.12.0",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "typescript",
@@ -59,9 +59,9 @@
59
59
  "@types/react": "19.3.0",
60
60
  "@vitest/browser-playwright": "5.0.5",
61
61
  "eslint": "10.13.0",
62
- "eslint-config-typed": "7.6.0",
63
- "eslint-plugin-ts-fortress": "1.3.15",
64
- "eslint-plugin-ts-type-forge": "0.8.20",
62
+ "eslint-config-typed": "7.6.1",
63
+ "eslint-plugin-ts-fortress": "1.3.16",
64
+ "eslint-plugin-ts-type-forge": "0.8.21",
65
65
  "immer": "11.2.7",
66
66
  "playwright": "1.64.1",
67
67
  "prettier": "3.9.10",
package/src/json/json.mts CHANGED
@@ -39,59 +39,75 @@ export namespace Json {
39
39
  * }
40
40
  *
41
41
  * assert.isTrue(Result.isErr(failed));
42
+ * ```
43
+ *
44
+ * @param text - A valid JSON string to parse. Can contain any valid JSON data
45
+ * type: primitives (string, number, boolean, null), arrays, or objects.
46
+ * @returns A `Result<JsonValue, string>` containing:
47
+ *
48
+ * - On success: `Result.ok(parsedValue)` where `parsedValue` is the parsed
49
+ * JSON.
50
+ * - On failure: `Result.err(errorMessage)` where `errorMessage` describes the
51
+ * parsing error
42
52
  *
43
- * // With reviver
53
+ * The form that takes a reviver is deprecated: its result is whatever the
54
+ * reviver returned, which need not be JSON although it is typed
55
+ * `JsonValue`. Use {@link parseWithReviver}, which types it `unknown`.
56
+ */
57
+ export const parse: Parse = (
58
+ text: string,
59
+ reviver?: (this: unknown, key: string, value: JsonValue) => unknown,
60
+ ): Result<JsonValue, string> =>
61
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion -- without a reviver the result is JSON; with one, the deprecated form types it `JsonValue` as it always did
62
+ parseWithReviver(text, reviver as Reviver | undefined) as Result<
63
+ JsonValue,
64
+ string
65
+ >;
66
+
67
+ /**
68
+ * Safely converts a JSON string into a JavaScript value, passing each value
69
+ * through `reviver` as `JSON.parse` does, without throwing exceptions.
70
+ *
71
+ * The result is whatever the reviver returned for the root, built from what
72
+ * it returned for every child, so it is typed `unknown`: narrow it before
73
+ * use.
74
+ *
75
+ * @example
76
+ *
77
+ * ```ts
44
78
  * const jsonWithDate = '{"created": "2024-01-01T00:00:00.000Z"}';
45
79
  *
46
- * const withReviver = Json.parse(jsonWithDate, (key, value) =>
80
+ * const revived = Json.parseWithReviver(jsonWithDate, (key, value) =>
47
81
  * key === 'created' && typeof value === 'string' ? new Date(value) : value,
48
82
  * );
49
83
  *
50
- * assert.isTrue(Result.isOk(withReviver));
84
+ * assert.isTrue(Result.isOk(revived));
51
85
  *
52
- * // With a reviver, the result is whatever the reviver returned, not JSON:
53
- * // `created` is a `Date` here although it is typed `JsonValue`. Widen it to
54
- * // `unknown` before narrowing it.
55
- * const revived: unknown = withReviver.value;
86
+ * // The result is whatever the reviver returned, typed `unknown`: here
87
+ * // `created` is a `Date`, not JSON.
88
+ * const root = Result.unwrapOkOr(revived, undefined);
56
89
  *
57
- * assert.isTrue(isRecord(revived));
90
+ * assert.isTrue(isRecord(root));
58
91
  *
59
- * assert.instanceOf(revived['created'], Date);
92
+ * assert.instanceOf(root['created'], Date);
60
93
  * ```
61
94
  *
62
- * @param text - A valid JSON string to parse. Can contain any valid JSON data
63
- * type: primitives (string, number, boolean, null), arrays, or objects.
64
- * @param reviver - Optional function that transforms parsed values. Called
65
- * for each key-value pair in the JSON. The function receives the key name
66
- * and parsed value, and should return the transformed value. For nested
67
- * objects, inner objects are processed before outer objects, so the
68
- * `value` an outer object is passed holds what the reviver already
69
- * returned for its children, which need not be JSON (see `@returns`).
70
- * Returning
71
- * `undefined` for a child deletes that property from an object and leaves
72
- * a hole in an array. The root is passed with the key `''`.
73
- * @returns A `Result<JsonValue, string>` containing:
74
- *
75
- * - On success: `Result.ok(parsedValue)` where `parsedValue` is the parsed
76
- * JSON. With a reviver it is whatever the reviver returned for the root
77
- * (a `Date`, `undefined`, ...), which is not checked to be JSON although
78
- * it is typed `JsonValue`; widen it to `unknown` before narrowing it,
79
- * as the example does.
80
- * - On failure: `Result.err(errorMessage)` where `errorMessage` describes the
81
- * parsing error
95
+ * @param text - A valid JSON string to parse.
96
+ * @param reviver - Called for each key-value pair, children before their
97
+ * parent, so the `value` a parent is passed holds what the reviver already
98
+ * returned for its children, which need not be JSON. Returning `undefined`
99
+ * for a child deletes that property from an object and leaves a hole in an
100
+ * array. The root is passed with the key `''`. `undefined` parses as
101
+ * {@link parse} does, typed `unknown`.
102
+ * @returns `Result.ok` of what the reviver returned for the root, or
103
+ * `Result.err` of a message describing the parsing error.
82
104
  */
83
- export const parse = (
105
+ export const parseWithReviver = (
84
106
  text: string,
85
- reviver?: (this: unknown, key: string, value: JsonValue) => unknown,
86
- ): Result<JsonValue, string> => {
107
+ reviver: Reviver | undefined,
108
+ ): Result<unknown, string> => {
87
109
  try {
88
- return Result.ok(
89
- JSON.parse(
90
- text,
91
- // eslint-disable-next-line total-functions/no-unsafe-type-assertion
92
- reviver as (this: unknown, key: string, value: unknown) => unknown,
93
- ),
94
- );
110
+ return Result.ok(JSON.parse(text, reviver));
95
111
  } catch (error: unknown) {
96
112
  const errStr = unknownToString(error);
97
113
 
@@ -99,6 +115,28 @@ export namespace Json {
99
115
  }
100
116
  };
101
117
 
118
+ /** What {@link parseWithReviver} calls for each key-value pair. */
119
+ export type Reviver = (this: unknown, key: string, value: unknown) => unknown;
120
+
121
+ /** The call signatures of {@link parse}. */
122
+ // Two signatures rather than one with an optional reviver, so that the one
123
+ // taking a reviver carries `@deprecated` and the other does not.
124
+ /* eslint-disable @typescript-eslint/unified-signatures */
125
+ type Parse = {
126
+ (text: string): Result<JsonValue, string>;
127
+
128
+ /**
129
+ * @deprecated Use `Json.parseWithReviver`, whose result is typed
130
+ * `unknown`: a reviver need not return JSON. The next major removes this
131
+ * form (`NEXT_MAJOR.md`).
132
+ */
133
+ (
134
+ text: string,
135
+ reviver?: (this: unknown, key: string, value: JsonValue) => unknown,
136
+ ): Result<JsonValue, string>;
137
+ };
138
+ /* eslint-enable @typescript-eslint/unified-signatures */
139
+
102
140
  /**
103
141
  * Safely converts a JavaScript value to a JSON string without throwing
104
142
  * exceptions.
@@ -125,7 +125,9 @@ describe('parse', () => {
125
125
 
126
126
  expect(() => Json.parse('undefined')).not.toThrow();
127
127
  });
128
+ });
128
129
 
130
+ describe('parseWithReviver', () => {
129
131
  test('should use reviver function to transform values', () => {
130
132
  const dateReviver = (_key: string, value: unknown): unknown =>
131
133
  typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/u.test(value)
@@ -134,7 +136,7 @@ describe('parse', () => {
134
136
 
135
137
  const jsonString = '{"name":"test","created":"2023-12-01T10:00:00.000Z"}';
136
138
 
137
- const result = Json.parse(jsonString, dateReviver);
139
+ const result = Json.parseWithReviver(jsonString, dateReviver);
138
140
 
139
141
  assert.isTrue(Result.isOk(result));
140
142
 
@@ -157,7 +159,7 @@ describe('parse', () => {
157
159
  ? Result.unwrapOkOr(Num.safeParseInt(value), Number.NaN)
158
160
  : value;
159
161
 
160
- const result = Json.parse(
162
+ const result = Json.parseWithReviver(
161
163
  '{"number":"42","text":"hello"}',
162
164
  transformReviver,
163
165
  );
@@ -182,7 +184,7 @@ describe('parse', () => {
182
184
  });
183
185
 
184
186
  test('should return what the reviver returns for the root, even if it is not JSON', () => {
185
- const result = Json.parse('{"a":1}', (key, value) =>
187
+ const result = Json.parseWithReviver('{"a":1}', (key, value) =>
186
188
  key === '' ? undefined : value,
187
189
  );
188
190
 
@@ -192,7 +194,7 @@ describe('parse', () => {
192
194
  });
193
195
 
194
196
  test('should delete an object property and leave an array hole where the reviver returns undefined', () => {
195
- const result = Json.parse('{"a":1,"b":[1,2,3]}', (key, value) =>
197
+ const result = Json.parseWithReviver('{"a":1,"b":[1,2,3]}', (key, value) =>
196
198
  key === 'a' || key === '1' ? undefined : value,
197
199
  );
198
200
 
@@ -225,7 +227,7 @@ describe('parse', () => {
225
227
  test('should pass the reviver the children it has already revived', () => {
226
228
  const mut_seen: unknown[] = [];
227
229
 
228
- const result = Json.parse('{"inner":{"n":1}}', (key, value) => {
230
+ const result = Json.parseWithReviver('{"inner":{"n":1}}', (key, value) => {
229
231
  if (key === 'inner') {
230
232
  mut_seen.push(structuredClone(value));
231
233
 
@@ -243,6 +245,26 @@ describe('parse', () => {
243
245
 
244
246
  assert.deepStrictEqual(mut_seen, [{ n: 1 }, { inner: 'replaced' }]);
245
247
  });
248
+
249
+ test('parses as parse does with no reviver', () => {
250
+ assert.deepStrictEqual(
251
+ Json.parseWithReviver('{"a":[1,2]}', undefined),
252
+ Result.ok({ a: [1, 2] }),
253
+ );
254
+
255
+ assert.isTrue(Result.isErr(Json.parseWithReviver('{{{', undefined)));
256
+ });
257
+ });
258
+
259
+ describe('parse with a reviver (deprecated)', () => {
260
+ test('still revives, as parseWithReviver does', () => {
261
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- the form under test
262
+ const result = Json.parse('{"a":1}', (key, value) =>
263
+ key === 'a' ? 2 : value,
264
+ );
265
+
266
+ assert.deepStrictEqual(result, Result.ok({ a: 2 }));
267
+ });
246
268
  });
247
269
 
248
270
  describe('stringify', () => {