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/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 Basic parsing
47
+ * @example
48
48
  * ```typescript
49
- * // Parse simple values
50
- * const str = Json.parse('"hello"');
51
- * // Result.ok('hello')
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 Error handling
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 Basic stringification
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, active: true };
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,"active":true}'
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 for problematic values
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 Basic property selection
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 Basic key sorting
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
- * // '{"aardvark":"animal","apple":"fruit","banana":"fruit","zebra":"animal"}'
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
  */