ts-data-forge 1.5.0 → 1.5.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/dist/array/array-utils.d.mts +4 -72
- package/dist/array/array-utils.d.mts.map +1 -1
- package/dist/array/array-utils.mjs +4 -72
- package/dist/array/array-utils.mjs.map +1 -1
- package/dist/functional/optional.d.mts +5 -146
- package/dist/functional/optional.d.mts.map +1 -1
- package/dist/functional/optional.mjs +5 -146
- package/dist/functional/optional.mjs.map +1 -1
- package/dist/functional/result.d.mts +0 -164
- package/dist/functional/result.d.mts.map +1 -1
- package/dist/functional/result.mjs +0 -164
- package/dist/functional/result.mjs.map +1 -1
- package/dist/guard/has-key.d.mts +1 -1
- package/dist/guard/has-key.mjs +1 -1
- package/dist/json/json.d.mts +14 -438
- package/dist/json/json.d.mts.map +1 -1
- package/dist/json/json.mjs +14 -438
- package/dist/json/json.mjs.map +1 -1
- package/dist/number/num.d.mts +5 -102
- package/dist/number/num.d.mts.map +1 -1
- package/dist/number/num.mjs +5 -102
- package/dist/number/num.mjs.map +1 -1
- package/package.json +1 -1
- package/src/array/array-utils.mts +4 -72
- package/src/functional/optional.mts +5 -146
- package/src/functional/result.mts +0 -164
- package/src/guard/has-key.mts +1 -1
- package/src/json/json.mts +14 -438
- package/src/number/num.mts +5 -102
package/src/json/json.mts
CHANGED
|
@@ -44,89 +44,21 @@ export namespace Json {
|
|
|
44
44
|
* - On success: `Result.ok(parsedValue)` where `parsedValue` is the parsed JSON
|
|
45
45
|
* - On failure: `Result.err(errorMessage)` where `errorMessage` describes the parsing error
|
|
46
46
|
*
|
|
47
|
-
* @example
|
|
47
|
+
* @example
|
|
48
48
|
* ```typescript
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* //
|
|
52
|
-
*
|
|
53
|
-
* const num = Json.parse('42');
|
|
54
|
-
* // Result.ok(42)
|
|
55
|
-
*
|
|
56
|
-
* const bool = Json.parse('true');
|
|
57
|
-
* // Result.ok(true)
|
|
58
|
-
*
|
|
59
|
-
* const nul = Json.parse('null');
|
|
60
|
-
* // Result.ok(null)
|
|
61
|
-
* ```
|
|
62
|
-
*
|
|
63
|
-
* @example Parsing objects and arrays
|
|
64
|
-
* ```typescript
|
|
65
|
-
* const obj = Json.parse('{"name": "John", "age": 30, "active": true}');
|
|
66
|
-
* if (Result.isOk(obj)) {
|
|
67
|
-
* console.log(obj.value.name); // 'John'
|
|
68
|
-
* console.log(obj.value.age); // 30
|
|
69
|
-
* }
|
|
70
|
-
*
|
|
71
|
-
* const arr = Json.parse('[1, "two", true, null]');
|
|
72
|
-
* if (Result.isOk(arr)) {
|
|
73
|
-
* console.log(arr.value); // [1, 'two', true, null]
|
|
49
|
+
* const result = Json.parse('{"name": "John", "age": 30}');
|
|
50
|
+
* if (Result.isOk(result)) {
|
|
51
|
+
* console.log(result.value.name); // 'John'
|
|
74
52
|
* }
|
|
75
53
|
* ```
|
|
76
54
|
*
|
|
77
|
-
* @example
|
|
55
|
+
* @example
|
|
78
56
|
* ```typescript
|
|
79
57
|
* const invalid = Json.parse('invalid json');
|
|
80
58
|
* if (Result.isErr(invalid)) {
|
|
81
59
|
* console.log('Parse failed:', invalid.value);
|
|
82
|
-
* // Parse failed: Unexpected token i in JSON at position 0
|
|
83
|
-
* }
|
|
84
|
-
*
|
|
85
|
-
* const malformed = Json.parse('{"missing": quote}');
|
|
86
|
-
* if (Result.isErr(malformed)) {
|
|
87
|
-
* console.log('Parse failed:', malformed.value);
|
|
88
|
-
* // Parse failed: Unexpected token q in JSON at position 12
|
|
89
60
|
* }
|
|
90
61
|
* ```
|
|
91
|
-
*
|
|
92
|
-
* @example Using reviver for data transformation
|
|
93
|
-
* ```typescript
|
|
94
|
-
* // Convert ISO date strings to Date objects
|
|
95
|
-
* const dateReviver = (key: string, value: unknown): unknown => {
|
|
96
|
-
* if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
|
|
97
|
-
* return new Date(value);
|
|
98
|
-
* }
|
|
99
|
-
* return value;
|
|
100
|
-
* };
|
|
101
|
-
*
|
|
102
|
-
* const result = Json.parse(
|
|
103
|
-
* '{"event": "Meeting", "date": "2023-12-25T10:00:00Z"}',
|
|
104
|
-
* dateReviver
|
|
105
|
-
* );
|
|
106
|
-
* if (Result.isOk(result)) {
|
|
107
|
-
* console.log(result.value.date instanceof Date); // true
|
|
108
|
-
* }
|
|
109
|
-
* ```
|
|
110
|
-
*
|
|
111
|
-
* @example API response validation
|
|
112
|
-
* ```typescript
|
|
113
|
-
* import { isRecord, hasKey } from 'ts-data-forge';
|
|
114
|
-
*
|
|
115
|
-
* const validateApiResponse = (jsonString: string) => {
|
|
116
|
-
* const parseResult = Json.parse(jsonString);
|
|
117
|
-
*
|
|
118
|
-
* if (Result.isErr(parseResult)) {
|
|
119
|
-
* return Result.err(`Invalid JSON: ${parseResult.value}`);
|
|
120
|
-
* }
|
|
121
|
-
*
|
|
122
|
-
* const data = parseResult.value;
|
|
123
|
-
* if (!isRecord(data) || !hasKey(data, 'status')) {
|
|
124
|
-
* return Result.err('Missing required "status" field');
|
|
125
|
-
* }
|
|
126
|
-
*
|
|
127
|
-
* return Result.ok(data);
|
|
128
|
-
* };
|
|
129
|
-
* ```
|
|
130
62
|
*/
|
|
131
63
|
export const parse = (
|
|
132
64
|
text: string,
|
|
@@ -169,129 +101,24 @@ export namespace Json {
|
|
|
169
101
|
* - On success: `Result.ok(jsonString)` where `jsonString` is the serialized JSON
|
|
170
102
|
* - On failure: `Result.err(errorMessage)` where `errorMessage` describes the error
|
|
171
103
|
*
|
|
172
|
-
* @example
|
|
173
|
-
* ```typescript
|
|
174
|
-
* // Primitives
|
|
175
|
-
* const str = Json.stringify('hello');
|
|
176
|
-
* // Result.ok('"hello"')
|
|
177
|
-
*
|
|
178
|
-
* const num = Json.stringify(42);
|
|
179
|
-
* // Result.ok('42')
|
|
180
|
-
*
|
|
181
|
-
* const bool = Json.stringify(true);
|
|
182
|
-
* // Result.ok('true')
|
|
183
|
-
*
|
|
184
|
-
* const nul = Json.stringify(null);
|
|
185
|
-
* // Result.ok('null')
|
|
186
|
-
* ```
|
|
187
|
-
*
|
|
188
|
-
* @example Objects and arrays
|
|
104
|
+
* @example
|
|
189
105
|
* ```typescript
|
|
190
|
-
* const obj = { name: 'John', age: 30
|
|
106
|
+
* const obj = { name: 'John', age: 30 };
|
|
191
107
|
* const result = Json.stringify(obj);
|
|
192
108
|
* if (Result.isOk(result)) {
|
|
193
|
-
* console.log(result.value); // '{"name":"John","age":30
|
|
109
|
+
* console.log(result.value); // '{"name":"John","age":30}'
|
|
194
110
|
* }
|
|
195
|
-
*
|
|
196
|
-
* const arr = [1, 'two', true, null];
|
|
197
|
-
* const arrResult = Json.stringify(arr);
|
|
198
|
-
* if (Result.isOk(arrResult)) {
|
|
199
|
-
* console.log(arrResult.value); // '[1,"two",true,null]'
|
|
200
|
-
* }
|
|
201
|
-
* ```
|
|
202
|
-
*
|
|
203
|
-
* @example Formatted output
|
|
204
|
-
* ```typescript
|
|
205
|
-
* const data = { users: [{ name: 'Alice', id: 1 }, { name: 'Bob', id: 2 }] };
|
|
206
|
-
*
|
|
207
|
-
* // Pretty-print with 2 spaces
|
|
208
|
-
* const formatted = Json.stringify(data, undefined, 2);
|
|
209
|
-
* if (Result.isOk(formatted)) {
|
|
210
|
-
* console.log(formatted.value);
|
|
211
|
-
* // {
|
|
212
|
-
* // "users": [
|
|
213
|
-
* // {
|
|
214
|
-
* // "name": "Alice",
|
|
215
|
-
* // "id": 1
|
|
216
|
-
* // },
|
|
217
|
-
* // {
|
|
218
|
-
* // "name": "Bob",
|
|
219
|
-
* // "id": 2
|
|
220
|
-
* // }
|
|
221
|
-
* // ]
|
|
222
|
-
* // }
|
|
223
|
-
* }
|
|
224
|
-
*
|
|
225
|
-
* // Custom indentation string
|
|
226
|
-
* const tabbed = Json.stringify(data, undefined, '\t');
|
|
227
111
|
* ```
|
|
228
112
|
*
|
|
229
|
-
* @example Error handling
|
|
113
|
+
* @example Error handling
|
|
230
114
|
* ```typescript
|
|
231
|
-
* // Circular reference error
|
|
232
115
|
* const circular: any = { name: 'test' };
|
|
233
116
|
* circular.self = circular;
|
|
234
117
|
* const error = Json.stringify(circular);
|
|
235
118
|
* if (Result.isErr(error)) {
|
|
236
119
|
* console.log('Stringify failed:', error.value);
|
|
237
|
-
* // Stringify failed: Converting circular structure to JSON
|
|
238
|
-
* }
|
|
239
|
-
*
|
|
240
|
-
* // BigInt error
|
|
241
|
-
* const bigIntError = Json.stringify({ big: BigInt(123) });
|
|
242
|
-
* if (Result.isErr(bigIntError)) {
|
|
243
|
-
* console.log('Stringify failed:', bigIntError.value);
|
|
244
|
-
* // Stringify failed: Do not know how to serialize a BigInt
|
|
245
|
-
* }
|
|
246
|
-
* ```
|
|
247
|
-
*
|
|
248
|
-
* @example Using replacer for value transformation
|
|
249
|
-
* ```typescript
|
|
250
|
-
* // Filter out sensitive data
|
|
251
|
-
* const sensitiveData = { name: 'John', password: 'secret123', email: 'john@example.com' };
|
|
252
|
-
*
|
|
253
|
-
* const secureReplacer = (key: string, value: unknown) => {
|
|
254
|
-
* if (key === 'password') return '[REDACTED]';
|
|
255
|
-
* return value;
|
|
256
|
-
* };
|
|
257
|
-
*
|
|
258
|
-
* const safe = Json.stringify(sensitiveData, secureReplacer);
|
|
259
|
-
* if (Result.isOk(safe)) {
|
|
260
|
-
* console.log(safe.value);
|
|
261
|
-
* // '{"name":"John","password":"[REDACTED]","email":"john@example.com"}'
|
|
262
|
-
* }
|
|
263
|
-
*
|
|
264
|
-
* // Convert Dates to custom format
|
|
265
|
-
* const data = { event: 'Meeting', date: new Date('2023-12-25T10:00:00Z') };
|
|
266
|
-
* const dateReplacer = (key: string, value: unknown) => {
|
|
267
|
-
* if (value instanceof Date) return value.toISOString().split('T')[0]; // YYYY-MM-DD
|
|
268
|
-
* return value;
|
|
269
|
-
* };
|
|
270
|
-
*
|
|
271
|
-
* const result = Json.stringify(data, dateReplacer);
|
|
272
|
-
* if (Result.isOk(result)) {
|
|
273
|
-
* console.log(result.value); // '{"event":"Meeting","date":"2023-12-25"}'
|
|
274
120
|
* }
|
|
275
121
|
* ```
|
|
276
|
-
*
|
|
277
|
-
* @example Special value handling
|
|
278
|
-
* ```typescript
|
|
279
|
-
* // Functions, undefined, and symbols are omitted in objects
|
|
280
|
-
* const obj = {
|
|
281
|
-
* name: 'test',
|
|
282
|
-
* fn: () => {}, // omitted
|
|
283
|
-
* undef: undefined, // omitted
|
|
284
|
-
* sym: Symbol('test'), // omitted
|
|
285
|
-
* num: 42
|
|
286
|
-
* };
|
|
287
|
-
* const result = Json.stringify(obj);
|
|
288
|
-
* // Result.ok('{"name":"test","num":42}')
|
|
289
|
-
*
|
|
290
|
-
* // In arrays, they become null
|
|
291
|
-
* const arr = ['string', () => {}, undefined, Symbol('test'), 42];
|
|
292
|
-
* const arrResult = Json.stringify(arr);
|
|
293
|
-
* // Result.ok('["string",null,null,null,42]')
|
|
294
|
-
* ```
|
|
295
122
|
*/
|
|
296
123
|
export const stringify = (
|
|
297
124
|
value: unknown,
|
|
@@ -328,129 +155,21 @@ export namespace Json {
|
|
|
328
155
|
* - On success: `Result.ok(jsonString)` with only selected properties
|
|
329
156
|
* - On failure: `Result.err(errorMessage)` describing the serialization error
|
|
330
157
|
*
|
|
331
|
-
* @example
|
|
158
|
+
* @example
|
|
332
159
|
* ```typescript
|
|
333
160
|
* const user = {
|
|
334
161
|
* id: 1,
|
|
335
162
|
* name: 'Alice',
|
|
336
163
|
* email: 'alice@example.com',
|
|
337
|
-
* password: 'secret123'
|
|
338
|
-
* lastLogin: '2023-12-01'
|
|
164
|
+
* password: 'secret123'
|
|
339
165
|
* };
|
|
340
166
|
*
|
|
341
|
-
* // Include only public fields
|
|
342
167
|
* const publicFields = Json.stringifySelected(user, ['id', 'name', 'email']);
|
|
343
168
|
* if (Result.isOk(publicFields)) {
|
|
344
169
|
* console.log(publicFields.value);
|
|
345
170
|
* // '{"id":1,"name":"Alice","email":"alice@example.com"}'
|
|
346
171
|
* }
|
|
347
172
|
* ```
|
|
348
|
-
*
|
|
349
|
-
* @example Array property selection
|
|
350
|
-
* ```typescript
|
|
351
|
-
* const data = {
|
|
352
|
-
* users: [
|
|
353
|
-
* { id: 1, name: 'Alice', secret: 'hidden1' },
|
|
354
|
-
* { id: 2, name: 'Bob', secret: 'hidden2' }
|
|
355
|
-
* ],
|
|
356
|
-
* metadata: { total: 2, page: 1 }
|
|
357
|
-
* };
|
|
358
|
-
*
|
|
359
|
-
* // Select specific properties across nested structures
|
|
360
|
-
* const selected = Json.stringifySelected(data, ['users', 'id', 'name', 'total']);
|
|
361
|
-
* if (Result.isOk(selected)) {
|
|
362
|
-
* console.log(selected.value);
|
|
363
|
-
* // '{"users":[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}],"metadata":{"total":2}}'
|
|
364
|
-
* }
|
|
365
|
-
* ```
|
|
366
|
-
*
|
|
367
|
-
* @example Formatted output with selection
|
|
368
|
-
* ```typescript
|
|
369
|
-
* const config = {
|
|
370
|
-
* database: { host: 'localhost', password: 'secret' },
|
|
371
|
-
* api: { endpoint: '/api/v1', key: 'secret-key' },
|
|
372
|
-
* features: { debug: true, logging: true }
|
|
373
|
-
* };
|
|
374
|
-
*
|
|
375
|
-
* // Create a safe config for logging (exclude sensitive fields)
|
|
376
|
-
* const safeConfig = Json.stringifySelected(
|
|
377
|
-
* config,
|
|
378
|
-
* ['database', 'host', 'api', 'endpoint', 'features'],
|
|
379
|
-
* 2
|
|
380
|
-
* );
|
|
381
|
-
* if (Result.isOk(safeConfig)) {
|
|
382
|
-
* console.log(safeConfig.value);
|
|
383
|
-
* // {
|
|
384
|
-
* // "database": {
|
|
385
|
-
* // "host": "localhost"
|
|
386
|
-
* // },
|
|
387
|
-
* // "api": {
|
|
388
|
-
* // "endpoint": "/api/v1"
|
|
389
|
-
* // },
|
|
390
|
-
* // "features": {
|
|
391
|
-
* // "debug": true,
|
|
392
|
-
* // "logging": true
|
|
393
|
-
* // }
|
|
394
|
-
* // }
|
|
395
|
-
* }
|
|
396
|
-
* ```
|
|
397
|
-
*
|
|
398
|
-
* @example Working with arrays and indices
|
|
399
|
-
* ```typescript
|
|
400
|
-
* const matrix = [
|
|
401
|
-
* [1, 2, 3, 4],
|
|
402
|
-
* [5, 6, 7, 8],
|
|
403
|
-
* [9, 10, 11, 12]
|
|
404
|
-
* ];
|
|
405
|
-
*
|
|
406
|
-
* // Select only first two columns (indices 0 and 1)
|
|
407
|
-
* const columns = Json.stringifySelected(matrix, [0, 1]);
|
|
408
|
-
* if (Result.isOk(columns)) {
|
|
409
|
-
* console.log(columns.value);
|
|
410
|
-
* // '[[1,2],[5,6],[9,10]]'
|
|
411
|
-
* }
|
|
412
|
-
* ```
|
|
413
|
-
*
|
|
414
|
-
* @example API response filtering
|
|
415
|
-
* ```typescript
|
|
416
|
-
* // Simulate filtering an API response
|
|
417
|
-
* const fullResponse = {
|
|
418
|
-
* data: {
|
|
419
|
-
* users: [
|
|
420
|
-
* { id: 1, name: 'Alice', email: 'alice@example.com', internalId: 'usr_123' },
|
|
421
|
-
* { id: 2, name: 'Bob', email: 'bob@example.com', internalId: 'usr_456' }
|
|
422
|
-
* ]
|
|
423
|
-
* },
|
|
424
|
-
* metadata: {
|
|
425
|
-
* total: 2,
|
|
426
|
-
* internalVersion: '1.2.3',
|
|
427
|
-
* serverTime: Date.now()
|
|
428
|
-
* }
|
|
429
|
-
* };
|
|
430
|
-
*
|
|
431
|
-
* // Create public API response
|
|
432
|
-
* const publicResponse = Json.stringifySelected(
|
|
433
|
-
* fullResponse,
|
|
434
|
-
* ['data', 'users', 'id', 'name', 'email', 'metadata', 'total']
|
|
435
|
-
* );
|
|
436
|
-
*
|
|
437
|
-
* if (Result.isOk(publicResponse)) {
|
|
438
|
-
* console.log('Public API response:', publicResponse.value);
|
|
439
|
-
* // Only includes id, name, email for users and total in metadata
|
|
440
|
-
* }
|
|
441
|
-
* ```
|
|
442
|
-
*
|
|
443
|
-
* @example Error handling
|
|
444
|
-
* ```typescript
|
|
445
|
-
* const circular: any = { name: 'test' };
|
|
446
|
-
* circular.self = circular;
|
|
447
|
-
*
|
|
448
|
-
* const result = Json.stringifySelected(circular, ['name', 'self']);
|
|
449
|
-
* if (Result.isErr(result)) {
|
|
450
|
-
* console.log('Serialization failed:', result.value);
|
|
451
|
-
* // Even with property selection, circular references still cause errors
|
|
452
|
-
* }
|
|
453
|
-
* ```
|
|
454
173
|
*/
|
|
455
174
|
export const stringifySelected = (
|
|
456
175
|
value: unknown,
|
|
@@ -488,161 +207,18 @@ export namespace Json {
|
|
|
488
207
|
* - On success: `Result.ok(jsonString)` with all object keys sorted alphabetically
|
|
489
208
|
* - On failure: `Result.err(errorMessage)` describing the serialization error
|
|
490
209
|
*
|
|
491
|
-
* @example
|
|
210
|
+
* @example
|
|
492
211
|
* ```typescript
|
|
493
212
|
* const unsortedObj = {
|
|
494
213
|
* zebra: 'animal',
|
|
495
214
|
* apple: 'fruit',
|
|
496
|
-
* banana: 'fruit'
|
|
497
|
-
* aardvark: 'animal'
|
|
215
|
+
* banana: 'fruit'
|
|
498
216
|
* };
|
|
499
217
|
*
|
|
500
218
|
* const sorted = Json.stringifySortedKey(unsortedObj);
|
|
501
219
|
* if (Result.isOk(sorted)) {
|
|
502
220
|
* console.log(sorted.value);
|
|
503
|
-
* // '{"
|
|
504
|
-
* }
|
|
505
|
-
*
|
|
506
|
-
* // Compare with regular stringify (order not guaranteed)
|
|
507
|
-
* const regular = Json.stringify(unsortedObj);
|
|
508
|
-
* // Keys might appear in insertion order or engine-dependent order
|
|
509
|
-
* ```
|
|
510
|
-
*
|
|
511
|
-
* @example Nested object key sorting
|
|
512
|
-
* ```typescript
|
|
513
|
-
* const nestedObj = {
|
|
514
|
-
* user: {
|
|
515
|
-
* name: 'Alice',
|
|
516
|
-
* age: 30,
|
|
517
|
-
* address: {
|
|
518
|
-
* zip: '12345',
|
|
519
|
-
* city: 'New York',
|
|
520
|
-
* country: 'USA'
|
|
521
|
-
* }
|
|
522
|
-
* },
|
|
523
|
-
* settings: {
|
|
524
|
-
* theme: 'dark',
|
|
525
|
-
* language: 'en',
|
|
526
|
-
* notifications: {
|
|
527
|
-
* email: true,
|
|
528
|
-
* sms: false,
|
|
529
|
-
* push: true
|
|
530
|
-
* }
|
|
531
|
-
* }
|
|
532
|
-
* };
|
|
533
|
-
*
|
|
534
|
-
* const sorted = Json.stringifySortedKey(nestedObj, 2);
|
|
535
|
-
* if (Result.isOk(sorted)) {
|
|
536
|
-
* console.log(sorted.value);
|
|
537
|
-
* // {
|
|
538
|
-
* // "settings": {
|
|
539
|
-
* // "language": "en",
|
|
540
|
-
* // "notifications": {
|
|
541
|
-
* // "email": true,
|
|
542
|
-
* // "push": true,
|
|
543
|
-
* // "sms": false
|
|
544
|
-
* // },
|
|
545
|
-
* // "theme": "dark"
|
|
546
|
-
* // },
|
|
547
|
-
* // "user": {
|
|
548
|
-
* // "address": {
|
|
549
|
-
* // "city": "New York",
|
|
550
|
-
* // "country": "USA",
|
|
551
|
-
* // "zip": "12345"
|
|
552
|
-
* // },
|
|
553
|
-
* // "age": 30,
|
|
554
|
-
* // "name": "Alice"
|
|
555
|
-
* // }
|
|
556
|
-
* // }
|
|
557
|
-
* }
|
|
558
|
-
* ```
|
|
559
|
-
*
|
|
560
|
-
* @example Arrays with nested objects
|
|
561
|
-
* ```typescript
|
|
562
|
-
* const dataWithArrays = {
|
|
563
|
-
* users: [
|
|
564
|
-
* { name: 'Bob', id: 2, active: true },
|
|
565
|
-
* { name: 'Alice', id: 1, active: false }
|
|
566
|
-
* ],
|
|
567
|
-
* metadata: {
|
|
568
|
-
* version: '1.0',
|
|
569
|
-
* created: '2023-12-01',
|
|
570
|
-
* author: 'system'
|
|
571
|
-
* }
|
|
572
|
-
* };
|
|
573
|
-
*
|
|
574
|
-
* const sorted = Json.stringifySortedKey(dataWithArrays);
|
|
575
|
-
* if (Result.isOk(sorted)) {
|
|
576
|
-
* console.log(sorted.value);
|
|
577
|
-
* // Keys in objects within arrays are also sorted:
|
|
578
|
-
* // '{"metadata":{"author":"system","created":"2023-12-01","version":"1.0"},"users":[{"active":true,"id":2,"name":"Bob"},{"active":false,"id":1,"name":"Alice"}]}'
|
|
579
|
-
* }
|
|
580
|
-
* ```
|
|
581
|
-
*
|
|
582
|
-
* @example Configuration files and consistent output
|
|
583
|
-
* ```typescript
|
|
584
|
-
* // Useful for configuration files that need consistent ordering
|
|
585
|
-
* const config = {
|
|
586
|
-
* database: {
|
|
587
|
-
* port: 5432,
|
|
588
|
-
* host: 'localhost',
|
|
589
|
-
* name: 'myapp',
|
|
590
|
-
* username: 'admin'
|
|
591
|
-
* },
|
|
592
|
-
* server: {
|
|
593
|
-
* port: 3000,
|
|
594
|
-
* middleware: ['cors', 'helmet', 'compression'],
|
|
595
|
-
* routes: {
|
|
596
|
-
* api: '/api/v1',
|
|
597
|
-
* health: '/health',
|
|
598
|
-
* docs: '/docs'
|
|
599
|
-
* }
|
|
600
|
-
* }
|
|
601
|
-
* };
|
|
602
|
-
*
|
|
603
|
-
* const consistentConfig = Json.stringifySortedKey(config, 2);
|
|
604
|
-
* if (Result.isOk(consistentConfig)) {
|
|
605
|
-
* // Always produces the same key order regardless of object creation order
|
|
606
|
-
* console.log('Consistent config:', consistentConfig.value);
|
|
607
|
-
* }
|
|
608
|
-
* ```
|
|
609
|
-
*
|
|
610
|
-
* @example Deterministic hashing and comparison
|
|
611
|
-
* ```typescript
|
|
612
|
-
* // Useful when you need consistent JSON for hashing or comparison
|
|
613
|
-
* const createHash = async (obj: Record<string, unknown>) => {
|
|
614
|
-
* const sortedJson = Json.stringifySortedKey(obj);
|
|
615
|
-
* if (Result.isErr(sortedJson)) {
|
|
616
|
-
* throw new Error(`Failed to serialize: ${sortedJson.value}`);
|
|
617
|
-
* }
|
|
618
|
-
*
|
|
619
|
-
* // Now you can safely hash the JSON string
|
|
620
|
-
* const encoder = new TextEncoder();
|
|
621
|
-
* const data = encoder.encode(sortedJson.value);
|
|
622
|
-
* const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
623
|
-
* return Array.from(new Uint8Array(hashBuffer))
|
|
624
|
-
* .map(b => b.toString(16).padStart(2, '0'))
|
|
625
|
-
* .join('');
|
|
626
|
-
* };
|
|
627
|
-
*
|
|
628
|
-
* // These will produce the same hash regardless of property order:
|
|
629
|
-
* const obj1 = { b: 2, a: 1, c: 3 };
|
|
630
|
-
* const obj2 = { a: 1, c: 3, b: 2 };
|
|
631
|
-
* // createHash(obj1) === createHash(obj2) // true
|
|
632
|
-
* ```
|
|
633
|
-
*
|
|
634
|
-
* @example Error handling
|
|
635
|
-
* ```typescript
|
|
636
|
-
* const problematicObj = {
|
|
637
|
-
* normal: 'value',
|
|
638
|
-
* circular: {} as any
|
|
639
|
-
* };
|
|
640
|
-
* problematicObj.circular.self = problematicObj;
|
|
641
|
-
*
|
|
642
|
-
* const result = Json.stringifySortedKey(problematicObj);
|
|
643
|
-
* if (Result.isErr(result)) {
|
|
644
|
-
* console.log('Serialization failed:', result.value);
|
|
645
|
-
* // Even with key sorting, circular references still cause errors
|
|
221
|
+
* // '{"apple":"fruit","banana":"fruit","zebra":"animal"}'
|
|
646
222
|
* }
|
|
647
223
|
* ```
|
|
648
224
|
*/
|