ts-data-forge 2.1.1 → 2.1.3
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/README.md +13 -15
- package/dist/array/array-utils.d.mts.map +1 -1
- package/dist/array/array-utils.mjs +9 -13
- package/dist/array/array-utils.mjs.map +1 -1
- package/dist/collections/imap.d.mts +1 -1
- package/dist/collections/imap.d.mts.map +1 -1
- package/dist/collections/imap.mjs +5 -5
- package/dist/collections/imap.mjs.map +1 -1
- package/dist/collections/iset-mapped.d.mts +1 -1
- package/dist/collections/iset-mapped.mjs +1 -1
- package/dist/collections/iset.d.mts +2 -2
- package/dist/collections/iset.mjs +4 -5
- package/dist/collections/iset.mjs.map +1 -1
- package/dist/collections/queue.d.mts +1 -1
- package/dist/collections/queue.d.mts.map +1 -1
- package/dist/collections/queue.mjs +7 -8
- package/dist/collections/queue.mjs.map +1 -1
- package/dist/collections/stack.d.mts +1 -1
- package/dist/collections/stack.d.mts.map +1 -1
- package/dist/collections/stack.mjs +6 -7
- package/dist/collections/stack.mjs.map +1 -1
- package/dist/entry-point.d.mts.map +1 -1
- package/dist/functional/match.d.mts.map +1 -1
- package/dist/functional/match.mjs +1 -6
- package/dist/functional/match.mjs.map +1 -1
- package/dist/functional/optional.d.mts.map +1 -1
- package/dist/functional/optional.mjs +6 -4
- package/dist/functional/optional.mjs.map +1 -1
- package/dist/functional/result.d.mts.map +1 -1
- package/dist/functional/result.mjs +1 -6
- package/dist/functional/result.mjs.map +1 -1
- package/dist/json/json.d.mts.map +1 -1
- package/dist/json/json.mjs +3 -3
- package/dist/json/json.mjs.map +1 -1
- package/dist/others/memoize-function.d.mts +1 -1
- package/dist/others/memoize-function.mjs +1 -1
- package/dist/others/unknown-to-string.d.mts +44 -77
- package/dist/others/unknown-to-string.d.mts.map +1 -1
- package/dist/others/unknown-to-string.mjs +51 -85
- package/dist/others/unknown-to-string.mjs.map +1 -1
- package/package.json +10 -1
- package/src/array/array-utils-creation.test.mts +9 -8
- package/src/array/array-utils-overload-type-error.test.mts +6 -2
- package/src/array/array-utils-search.test.mts +1 -1
- package/src/array/array-utils-slicing.test.mts +0 -42
- package/src/array/array-utils-transformation.test.mts +11 -186
- package/src/array/array-utils-validation.test.mts +27 -27
- package/src/array/array-utils.mts +8 -12
- package/src/collections/imap-mapped.test.mts +15 -11
- package/src/collections/imap.mts +6 -13
- package/src/collections/imap.test.mts +20 -19
- package/src/collections/iset-mapped.mts +1 -1
- package/src/collections/iset-mapped.test.mts +14 -104
- package/src/collections/iset.mts +5 -7
- package/src/collections/iset.test.mts +43 -34
- package/src/collections/queue.mts +12 -10
- package/src/collections/queue.test.mts +46 -44
- package/src/collections/stack.mts +10 -9
- package/src/collections/stack.test.mts +12 -10
- package/src/entry-point.mts +1 -0
- package/src/functional/match.mts +1 -5
- package/src/functional/optional.mts +6 -6
- package/src/functional/optional.test.mts +7 -7
- package/src/functional/result.mts +1 -5
- package/src/guard/is-non-empty-string.test.mts +1 -1
- package/src/guard/is-non-null-object.test.mts +3 -3
- package/src/guard/is-primitive.test.mts +6 -6
- package/src/guard/is-type.test.mts +8 -12
- package/src/iterator/range.test.mts +1 -1
- package/src/json/json.mts +4 -11
- package/src/json/json.test.mts +25 -28
- package/src/number/branded-types/finite-number.test.mts +2 -1
- package/src/number/branded-types/int.test.mts +2 -1
- package/src/number/branded-types/int16.test.mts +3 -2
- package/src/number/branded-types/int32.test.mts +3 -2
- package/src/number/branded-types/non-negative-finite-number.test.mts +3 -2
- package/src/number/branded-types/non-negative-int16.test.mts +3 -2
- package/src/number/branded-types/non-negative-int32.test.mts +3 -2
- package/src/number/branded-types/non-zero-finite-number.test.mts +4 -3
- package/src/number/branded-types/non-zero-int.test.mts +4 -3
- package/src/number/branded-types/non-zero-int16.test.mts +3 -2
- package/src/number/branded-types/non-zero-int32.test.mts +3 -2
- package/src/number/branded-types/non-zero-safe-int.test.mts +4 -3
- package/src/number/branded-types/non-zero-uint16.test.mts +3 -2
- package/src/number/branded-types/non-zero-uint32.test.mts +3 -2
- package/src/number/branded-types/positive-finite-number.test.mts +3 -2
- package/src/number/branded-types/positive-int.test.mts +3 -2
- package/src/number/branded-types/positive-int16.test.mts +3 -2
- package/src/number/branded-types/positive-int32.test.mts +3 -2
- package/src/number/branded-types/positive-safe-int.test.mts +3 -2
- package/src/number/branded-types/positive-uint16.test.mts +3 -2
- package/src/number/branded-types/positive-uint32.test.mts +3 -2
- package/src/number/branded-types/safe-int.test.mts +3 -2
- package/src/number/branded-types/safe-uint.test.mts +3 -2
- package/src/number/branded-types/uint.test.mts +3 -2
- package/src/number/branded-types/uint16.test.mts +3 -2
- package/src/number/branded-types/uint32.test.mts +3 -2
- package/src/object/object.test.mts +10 -10
- package/src/others/cast-readonly.test.mts +8 -8
- package/src/others/memoize-function.mts +1 -1
- package/src/others/memoize-function.test.mts +2 -2
- package/src/others/unknown-to-string.mts +52 -87
- package/src/others/unknown-to-string.test.mts +26 -58
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { expectType } from '../../expect-type.mjs';
|
|
2
|
+
import { range } from '../../iterator/index.mjs';
|
|
2
3
|
import { asSafeInt, SafeInt } from './safe-int.mjs';
|
|
3
4
|
|
|
4
5
|
describe('SafeInt', () => {
|
|
@@ -142,7 +143,7 @@ describe('SafeInt', () => {
|
|
|
142
143
|
const min = -20;
|
|
143
144
|
const max = 20;
|
|
144
145
|
|
|
145
|
-
for (
|
|
146
|
+
for (const _ of range(10)) {
|
|
146
147
|
const result = SafeInt.random(min, max);
|
|
147
148
|
expect(result).toBeGreaterThanOrEqual(min);
|
|
148
149
|
expect(result).toBeLessThanOrEqual(max);
|
|
@@ -152,7 +153,7 @@ describe('SafeInt', () => {
|
|
|
152
153
|
});
|
|
153
154
|
|
|
154
155
|
test('generates values within safe integer range', () => {
|
|
155
|
-
for (
|
|
156
|
+
for (const _ of range(10)) {
|
|
156
157
|
const result = SafeInt.random(-30, 30);
|
|
157
158
|
expect(result).toBeGreaterThanOrEqual(Number.MIN_SAFE_INTEGER);
|
|
158
159
|
expect(result).toBeLessThanOrEqual(Number.MAX_SAFE_INTEGER);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { expectType } from '../../expect-type.mjs';
|
|
2
|
+
import { range } from '../../iterator/index.mjs';
|
|
2
3
|
import { asSafeUint, isSafeUint, SafeUint } from './safe-uint.mjs';
|
|
3
4
|
|
|
4
5
|
describe('SafeUint', () => {
|
|
@@ -147,7 +148,7 @@ describe('SafeUint', () => {
|
|
|
147
148
|
const min = 0;
|
|
148
149
|
const max = 20;
|
|
149
150
|
|
|
150
|
-
for (
|
|
151
|
+
for (const _ of range(10)) {
|
|
151
152
|
const result = SafeUint.random(min, max);
|
|
152
153
|
expect(result).toBeGreaterThanOrEqual(min);
|
|
153
154
|
expect(result).toBeLessThanOrEqual(max);
|
|
@@ -158,7 +159,7 @@ describe('SafeUint', () => {
|
|
|
158
159
|
});
|
|
159
160
|
|
|
160
161
|
test('generates values within safe uint range', () => {
|
|
161
|
-
for (
|
|
162
|
+
for (const _ of range(10)) {
|
|
162
163
|
const result = SafeUint.random(0, 30);
|
|
163
164
|
expect(result).toBeGreaterThanOrEqual(0);
|
|
164
165
|
expect(result).toBeLessThanOrEqual(Number.MAX_SAFE_INTEGER);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { expectType } from '../../expect-type.mjs';
|
|
2
|
+
import { range } from '../../iterator/index.mjs';
|
|
2
3
|
import { asUint, isUint, Uint } from './uint.mjs';
|
|
3
4
|
|
|
4
5
|
describe('Uint', () => {
|
|
@@ -129,7 +130,7 @@ describe('Uint', () => {
|
|
|
129
130
|
const min = 0;
|
|
130
131
|
const max = 20;
|
|
131
132
|
|
|
132
|
-
for (
|
|
133
|
+
for (const _ of range(10)) {
|
|
133
134
|
const result = Uint.random(min, max);
|
|
134
135
|
expect(result).toBeGreaterThanOrEqual(min);
|
|
135
136
|
expect(result).toBeLessThanOrEqual(max);
|
|
@@ -140,7 +141,7 @@ describe('Uint', () => {
|
|
|
140
141
|
});
|
|
141
142
|
|
|
142
143
|
test('generates values starting from 0', () => {
|
|
143
|
-
for (
|
|
144
|
+
for (const _ of range(10)) {
|
|
144
145
|
const result = Uint.random(0, 30);
|
|
145
146
|
expect(result).toBeGreaterThanOrEqual(0);
|
|
146
147
|
expect(result).toBeLessThanOrEqual(30);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { expectType } from '../../expect-type.mjs';
|
|
2
|
+
import { range } from '../../iterator/index.mjs';
|
|
2
3
|
import { asNonZeroUint16 } from './non-zero-uint16.mjs';
|
|
3
4
|
import { asUint16, isUint16, Uint16 } from './uint16.mjs';
|
|
4
5
|
|
|
@@ -141,7 +142,7 @@ describe('Uint16', () => {
|
|
|
141
142
|
const min = 0;
|
|
142
143
|
const max = 20;
|
|
143
144
|
|
|
144
|
-
for (
|
|
145
|
+
for (const _ of range(10)) {
|
|
145
146
|
const result = Uint16.random(min, max);
|
|
146
147
|
expect(result).toBeGreaterThanOrEqual(min);
|
|
147
148
|
expect(result).toBeLessThanOrEqual(max);
|
|
@@ -152,7 +153,7 @@ describe('Uint16', () => {
|
|
|
152
153
|
});
|
|
153
154
|
|
|
154
155
|
test('generates values within Uint16 range', () => {
|
|
155
|
-
for (
|
|
156
|
+
for (const _ of range(10)) {
|
|
156
157
|
const result = Uint16.random(0, 30);
|
|
157
158
|
expect(result).toBeGreaterThanOrEqual(0);
|
|
158
159
|
expect(result).toBeLessThanOrEqual(65535);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { expectType } from '../../expect-type.mjs';
|
|
2
|
+
import { range } from '../../iterator/index.mjs';
|
|
2
3
|
import { asNonZeroUint32 } from './non-zero-uint32.mjs';
|
|
3
4
|
import { asUint32, isUint32, Uint32 } from './uint32.mjs';
|
|
4
5
|
|
|
@@ -141,7 +142,7 @@ describe('Uint32', () => {
|
|
|
141
142
|
const min = 0;
|
|
142
143
|
const max = 20;
|
|
143
144
|
|
|
144
|
-
for (
|
|
145
|
+
for (const _ of range(10)) {
|
|
145
146
|
const result = Uint32.random(min, max);
|
|
146
147
|
expect(result).toBeGreaterThanOrEqual(min);
|
|
147
148
|
expect(result).toBeLessThanOrEqual(max);
|
|
@@ -152,7 +153,7 @@ describe('Uint32', () => {
|
|
|
152
153
|
});
|
|
153
154
|
|
|
154
155
|
test('generates values within Uint32 range', () => {
|
|
155
|
-
for (
|
|
156
|
+
for (const _ of range(10)) {
|
|
156
157
|
const result = Uint32.random(0, 30);
|
|
157
158
|
expect(result).toBeGreaterThanOrEqual(0);
|
|
158
159
|
expect(result).toBeLessThanOrEqual(4294967295);
|
|
@@ -28,20 +28,20 @@ describe('shallowEq', () => {
|
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
describe('pick', () => {
|
|
31
|
-
test('
|
|
31
|
+
test('should pick specified keys', () => {
|
|
32
32
|
expect(Obj.pick({ a: 1, b: 2, c: 3 }, ['a', 'b'])).toStrictEqual({
|
|
33
33
|
a: 1,
|
|
34
34
|
b: 2,
|
|
35
35
|
});
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
test('should support curried form', () => {
|
|
38
|
+
test('pick should support curried form', () => {
|
|
39
39
|
const pickAB = Obj.pick(['a', 'b']);
|
|
40
40
|
const result = pickAB({ a: 1, b: 2, c: 3, d: 4 });
|
|
41
41
|
expect(result).toStrictEqual({ a: 1, b: 2 });
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
test('should work with pipe when curried', () => {
|
|
44
|
+
test('pick should work with pipe when curried', () => {
|
|
45
45
|
const pickIdAndName = Obj.pick(['id', 'name']);
|
|
46
46
|
const user = { id: 1, name: 'Alice', email: 'alice@example.com', age: 30 };
|
|
47
47
|
|
|
@@ -49,13 +49,13 @@ describe('pick', () => {
|
|
|
49
49
|
expect(result).toStrictEqual({ id: 1, name: 'Alice' });
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
test('should handle empty keys in curried form', () => {
|
|
52
|
+
test('pick should handle empty keys in curried form', () => {
|
|
53
53
|
const pickNone = Obj.pick([]);
|
|
54
54
|
const result = pickNone({ a: 1, b: 2 });
|
|
55
55
|
expect(result).toStrictEqual({});
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
-
test('should work for records that only partially contain the key in curried form', () => {
|
|
58
|
+
test('pick should work for records that only partially contain the key in curried form', () => {
|
|
59
59
|
const pickVisible = Obj.pick(['name', 'age']);
|
|
60
60
|
const user = {
|
|
61
61
|
id: 1,
|
|
@@ -71,17 +71,17 @@ describe('pick', () => {
|
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
describe('omit', () => {
|
|
74
|
-
test('
|
|
74
|
+
test('should omit specified keys', () => {
|
|
75
75
|
expect(Obj.omit({ a: 1, b: 2, c: 3 }, ['c'])).toStrictEqual({ a: 1, b: 2 });
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
test('should support curried form', () => {
|
|
78
|
+
test('omit should support curried form', () => {
|
|
79
79
|
const omitC = Obj.omit(['c']);
|
|
80
80
|
const result = omitC({ a: 1, b: 2, c: 3, d: 4 });
|
|
81
81
|
expect(result).toStrictEqual({ a: 1, b: 2, d: 4 });
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
test('should work with pipe when curried', () => {
|
|
84
|
+
test('omit should work with pipe when curried', () => {
|
|
85
85
|
const omitSensitive = Obj.omit(['password', 'email']);
|
|
86
86
|
const user = {
|
|
87
87
|
id: 1,
|
|
@@ -94,7 +94,7 @@ describe('omit', () => {
|
|
|
94
94
|
expect(result).toStrictEqual({ id: 1, name: 'Alice' });
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
-
test('should handle empty keys in curried form', () => {
|
|
97
|
+
test('omit should handle empty keys in curried form', () => {
|
|
98
98
|
const omitNone = Obj.omit([]);
|
|
99
99
|
const original = { a: 1, b: 2, c: 3 };
|
|
100
100
|
const result = omitNone(original);
|
|
@@ -107,7 +107,7 @@ describe('omit', () => {
|
|
|
107
107
|
expect(result).toStrictEqual({ a: 1, c: 3, e: 5 });
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
-
test('should work for records that only partially contain the key in curried form', () => {
|
|
110
|
+
test('omit should work for records that only partially contain the key in curried form', () => {
|
|
111
111
|
const omitSensitive = Obj.omit(['password', 'email']);
|
|
112
112
|
const user = {
|
|
113
113
|
id: 1,
|
|
@@ -25,16 +25,16 @@ describe('castReadonly', () => {
|
|
|
25
25
|
expect(Object.is(readonly, original)).toBe(true);
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
test('should work with primitives', () => {
|
|
28
|
+
test('castReadonly should work with primitives', () => {
|
|
29
29
|
expect(castReadonly(42)).toBe(42);
|
|
30
30
|
expect(castReadonly('hello')).toBe('hello');
|
|
31
31
|
expect(castReadonly(true)).toBe(true);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
test('should work with null and undefined', () => {
|
|
35
|
-
expect(castReadonly(null)).
|
|
34
|
+
test('castReadonly should work with null and undefined', () => {
|
|
35
|
+
expect(castReadonly(null)).toBeNull();
|
|
36
36
|
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
|
37
|
-
expect(castReadonly(undefined)).
|
|
37
|
+
expect(castReadonly(undefined)).toBeUndefined();
|
|
38
38
|
});
|
|
39
39
|
});
|
|
40
40
|
|
|
@@ -75,15 +75,15 @@ describe('castDeepReadonly', () => {
|
|
|
75
75
|
expect(readonly[1]?.meta.active).toBe(false);
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
test('should work with primitives', () => {
|
|
78
|
+
test('castDeepReadonly should work with primitives', () => {
|
|
79
79
|
expect(castDeepReadonly(42)).toBe(42);
|
|
80
80
|
expect(castDeepReadonly('hello')).toBe('hello');
|
|
81
81
|
expect(castDeepReadonly(true)).toBe(true);
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
test('should work with null and undefined', () => {
|
|
85
|
-
expect(castDeepReadonly(null)).
|
|
84
|
+
test('castDeepReadonly should work with null and undefined', () => {
|
|
85
|
+
expect(castDeepReadonly(null)).toBeNull();
|
|
86
86
|
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
|
87
|
-
expect(castDeepReadonly(undefined)).
|
|
87
|
+
expect(castDeepReadonly(undefined)).toBeUndefined();
|
|
88
88
|
});
|
|
89
89
|
});
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
* const memoizedFindPaths = memoizeFunction(
|
|
102
102
|
* findPaths,
|
|
103
103
|
* (start, end, visited = new Set()) =>
|
|
104
|
-
* `${start}->${end}:[${[...visited].
|
|
104
|
+
* `${start}->${end}:[${[...visited].toSorted().join(',')}]`
|
|
105
105
|
* );
|
|
106
106
|
* ```
|
|
107
107
|
*
|
|
@@ -37,12 +37,12 @@ describe('memoizeFunction', () => {
|
|
|
37
37
|
const memoized = memoizeFunction(mockFn, (x) => x);
|
|
38
38
|
|
|
39
39
|
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
|
40
|
-
expect(memoized(5)).
|
|
40
|
+
expect(memoized(5)).toBeUndefined();
|
|
41
41
|
expect(mockFn).toHaveBeenCalledTimes(1);
|
|
42
42
|
|
|
43
43
|
// Should use cache even for undefined
|
|
44
44
|
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
|
45
|
-
expect(memoized(5)).
|
|
45
|
+
expect(memoized(5)).toBeUndefined();
|
|
46
46
|
expect(mockFn).toHaveBeenCalledTimes(1);
|
|
47
47
|
});
|
|
48
48
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Result } from '../functional/index.mjs';
|
|
2
1
|
import { isNonNullish } from '../guard/index.mjs';
|
|
3
2
|
|
|
4
3
|
/**
|
|
@@ -20,22 +19,22 @@ import { isNonNullish } from '../guard/index.mjs';
|
|
|
20
19
|
* @param value - The unknown value to convert to string
|
|
21
20
|
* @param options - Optional configuration for the conversion
|
|
22
21
|
* @param options.prettyPrintObject - If true, objects are formatted with 2-space indentation
|
|
23
|
-
* @returns
|
|
22
|
+
* @returns The string representation of the value. For circular references or non-serializable objects, returns an error message string
|
|
24
23
|
*
|
|
25
24
|
* @example Basic type conversions
|
|
26
25
|
* ```typescript
|
|
27
26
|
* // Primitive types
|
|
28
|
-
* unknownToString('hello'); //
|
|
29
|
-
* unknownToString(123); //
|
|
30
|
-
* unknownToString(true); //
|
|
31
|
-
* unknownToString(null); //
|
|
32
|
-
* unknownToString(undefined); //
|
|
33
|
-
* unknownToString(Symbol('test')); //
|
|
34
|
-
* unknownToString(123n); //
|
|
27
|
+
* unknownToString('hello'); // 'hello'
|
|
28
|
+
* unknownToString(123); // '123'
|
|
29
|
+
* unknownToString(true); // 'true'
|
|
30
|
+
* unknownToString(null); // 'null'
|
|
31
|
+
* unknownToString(undefined); // 'undefined'
|
|
32
|
+
* unknownToString(Symbol('test')); // 'Symbol(test)'
|
|
33
|
+
* unknownToString(123n); // '123'
|
|
35
34
|
*
|
|
36
35
|
* // Function conversion
|
|
37
36
|
* const fn = () => 'test';
|
|
38
|
-
* unknownToString(fn); //
|
|
37
|
+
* unknownToString(fn); // "() => 'test'"
|
|
39
38
|
* ```
|
|
40
39
|
*
|
|
41
40
|
* @example Object stringification
|
|
@@ -43,24 +42,20 @@ import { isNonNullish } from '../guard/index.mjs';
|
|
|
43
42
|
* // Simple object
|
|
44
43
|
* const obj = { a: 1, b: 'hello', c: [1, 2, 3] };
|
|
45
44
|
* const result = unknownToString(obj);
|
|
46
|
-
*
|
|
47
|
-
* console.log(result.value); // '{"a":1,"b":"hello","c":[1,2,3]}'
|
|
48
|
-
* }
|
|
45
|
+
* console.log(result); // '{"a":1,"b":"hello","c":[1,2,3]}'
|
|
49
46
|
*
|
|
50
47
|
* // Pretty printing
|
|
51
48
|
* const prettyResult = unknownToString(obj, { prettyPrintObject: true });
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* // }
|
|
63
|
-
* }
|
|
49
|
+
* console.log(prettyResult);
|
|
50
|
+
* // {
|
|
51
|
+
* // "a": 1,
|
|
52
|
+
* // "b": "hello",
|
|
53
|
+
* // "c": [
|
|
54
|
+
* // 1,
|
|
55
|
+
* // 2,
|
|
56
|
+
* // 3
|
|
57
|
+
* // ]
|
|
58
|
+
* // }
|
|
64
59
|
* ```
|
|
65
60
|
*
|
|
66
61
|
* @example Error handling for circular references
|
|
@@ -70,19 +65,7 @@ import { isNonNullish } from '../guard/index.mjs';
|
|
|
70
65
|
* circular.self = circular;
|
|
71
66
|
*
|
|
72
67
|
* const result = unknownToString(circular);
|
|
73
|
-
*
|
|
74
|
-
* console.log(result.value.message);
|
|
75
|
-
* // "Converting circular structure to JSON"
|
|
76
|
-
* }
|
|
77
|
-
*
|
|
78
|
-
* // Handle with custom serialization
|
|
79
|
-
* const safeStringify = (value: unknown): string => {
|
|
80
|
-
* const result = unknownToString(value);
|
|
81
|
-
* return Result.isOk(result)
|
|
82
|
-
* ? result.value
|
|
83
|
-
* : `[Error: ${result.value.message}]`;
|
|
84
|
-
* };
|
|
85
|
-
* ```
|
|
68
|
+
* console.log(result); // "Converting circular structure to JSON"
|
|
86
69
|
*
|
|
87
70
|
* @example Logging and debugging utilities
|
|
88
71
|
* ```typescript
|
|
@@ -92,13 +75,9 @@ import { isNonNullish } from '../guard/index.mjs';
|
|
|
92
75
|
* const timestamp = new Date().toISOString();
|
|
93
76
|
* const dataStr = data !== undefined
|
|
94
77
|
* ? unknownToString(data, { prettyPrintObject: true })
|
|
95
|
-
* :
|
|
78
|
+
* : '';
|
|
96
79
|
*
|
|
97
|
-
*
|
|
98
|
-
* console.log(`[${timestamp}] ${message}`, dataStr.value);
|
|
99
|
-
* } else {
|
|
100
|
-
* console.log(`[${timestamp}] ${message} [Unstringifiable data]`);
|
|
101
|
-
* }
|
|
80
|
+
* console.log(`[${timestamp}] ${message}`, dataStr);
|
|
102
81
|
* }
|
|
103
82
|
* }
|
|
104
83
|
*
|
|
@@ -110,19 +89,11 @@ import { isNonNullish } from '../guard/index.mjs';
|
|
|
110
89
|
* ```typescript
|
|
111
90
|
* // Safe error response formatting
|
|
112
91
|
* function formatErrorResponse(error: unknown): string {
|
|
113
|
-
* const
|
|
92
|
+
* const errorStr = unknownToString(error, { prettyPrintObject: true });
|
|
114
93
|
*
|
|
115
|
-
* if (Result.isOk(result)) {
|
|
116
|
-
* return JSON.stringify({
|
|
117
|
-
* success: false,
|
|
118
|
-
* error: result.value
|
|
119
|
-
* });
|
|
120
|
-
* }
|
|
121
|
-
*
|
|
122
|
-
* // Fallback for unstringifiable errors
|
|
123
94
|
* return JSON.stringify({
|
|
124
95
|
* success: false,
|
|
125
|
-
* error:
|
|
96
|
+
* error: errorStr
|
|
126
97
|
* });
|
|
127
98
|
* }
|
|
128
99
|
*
|
|
@@ -138,78 +109,72 @@ import { isNonNullish } from '../guard/index.mjs';
|
|
|
138
109
|
* ```typescript
|
|
139
110
|
* // Date objects
|
|
140
111
|
* unknownToString(new Date('2023-01-01'));
|
|
141
|
-
* //
|
|
112
|
+
* // '"2023-01-01T00:00:00.000Z"' - JSON stringified
|
|
142
113
|
*
|
|
143
114
|
* // Regular expressions
|
|
144
115
|
* unknownToString(/test/gi);
|
|
145
|
-
* //
|
|
116
|
+
* // '{}' - RegExp has no enumerable properties
|
|
146
117
|
*
|
|
147
118
|
* // Arrays
|
|
148
119
|
* unknownToString([1, 'two', { three: 3 }]);
|
|
149
|
-
* //
|
|
120
|
+
* // '[1,"two",{"three":3}]'
|
|
150
121
|
*
|
|
151
122
|
* // Map and Set (converted to empty objects by JSON.stringify)
|
|
152
|
-
* unknownToString(new Map([['a', 1]])); //
|
|
153
|
-
* unknownToString(new Set([1, 2, 3])); //
|
|
123
|
+
* unknownToString(new Map([['a', 1]])); // '{}'
|
|
124
|
+
* unknownToString(new Set([1, 2, 3])); // '{}'
|
|
154
125
|
* ```
|
|
155
126
|
*
|
|
156
|
-
* @example
|
|
127
|
+
* @example Using with validation
|
|
157
128
|
* ```typescript
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* s.length > 0
|
|
169
|
-
* ? Result.ok(s)
|
|
170
|
-
* : Result.err(new Error('Empty string'))
|
|
171
|
-
* ))
|
|
172
|
-
* .value;
|
|
129
|
+
* // Simple validation helper
|
|
130
|
+
* function validateAndStringify(input: unknown): string {
|
|
131
|
+
* const str = unknownToString(input);
|
|
132
|
+
* const trimmed = str.trim();
|
|
133
|
+
*
|
|
134
|
+
* if (trimmed.length === 0) {
|
|
135
|
+
* throw new Error('Empty string');
|
|
136
|
+
* }
|
|
137
|
+
*
|
|
138
|
+
* return trimmed;
|
|
173
139
|
* }
|
|
174
140
|
* ```
|
|
175
141
|
*
|
|
176
|
-
*
|
|
142
|
+
* **Error Handling:**
|
|
143
|
+
* Circular references and non-serializable objects return descriptive error messages instead of throwing
|
|
177
144
|
* @see JSON.stringify - Underlying serialization for objects
|
|
178
145
|
*/
|
|
179
146
|
export const unknownToString = (
|
|
180
147
|
value: unknown,
|
|
181
148
|
options?: Partial<Readonly<{ prettyPrintObject: boolean }>>,
|
|
182
|
-
):
|
|
149
|
+
): string => {
|
|
183
150
|
switch (typeof value) {
|
|
184
151
|
case 'string':
|
|
185
|
-
return
|
|
152
|
+
return value;
|
|
186
153
|
|
|
187
154
|
case 'number':
|
|
188
155
|
case 'bigint':
|
|
189
156
|
case 'boolean':
|
|
190
157
|
case 'symbol':
|
|
191
158
|
case 'function':
|
|
192
|
-
return
|
|
159
|
+
return value.toString();
|
|
193
160
|
|
|
194
161
|
case 'object':
|
|
195
162
|
if (!isNonNullish(value)) {
|
|
196
|
-
return
|
|
163
|
+
return 'null';
|
|
197
164
|
}
|
|
198
165
|
try {
|
|
199
166
|
const stringified =
|
|
200
167
|
options?.prettyPrintObject === true
|
|
201
168
|
? JSON.stringify(value, undefined, 2)
|
|
202
169
|
: JSON.stringify(value);
|
|
203
|
-
return
|
|
170
|
+
return stringified;
|
|
204
171
|
} catch (error) {
|
|
205
|
-
return
|
|
206
|
-
error
|
|
207
|
-
|
|
208
|
-
: new Error('Failed to stringify object'),
|
|
209
|
-
);
|
|
172
|
+
return error instanceof Error
|
|
173
|
+
? error.message
|
|
174
|
+
: '[Circular or Non-serializable]';
|
|
210
175
|
}
|
|
211
176
|
|
|
212
177
|
case 'undefined':
|
|
213
|
-
return
|
|
178
|
+
return 'undefined';
|
|
214
179
|
}
|
|
215
180
|
};
|
|
@@ -1,76 +1,51 @@
|
|
|
1
|
-
import { Result } from '../functional/index.mjs';
|
|
2
1
|
import { unknownToString } from './unknown-to-string.mjs';
|
|
3
2
|
|
|
4
3
|
describe('unknownToString', () => {
|
|
5
4
|
test('string', () => {
|
|
6
5
|
const result = unknownToString('aaaaa');
|
|
7
|
-
expect(
|
|
8
|
-
if (Result.isOk(result)) {
|
|
9
|
-
expect(result.value).toBe('aaaaa');
|
|
10
|
-
}
|
|
6
|
+
expect(result).toBe('aaaaa');
|
|
11
7
|
expect(JSON.stringify('aaaaa')).toBe('"aaaaa"');
|
|
12
8
|
});
|
|
13
9
|
|
|
14
10
|
test('number', () => {
|
|
15
11
|
const result = unknownToString(1);
|
|
16
|
-
expect(
|
|
17
|
-
if (Result.isOk(result)) {
|
|
18
|
-
expect(result.value).toBe('1');
|
|
19
|
-
}
|
|
12
|
+
expect(result).toBe('1');
|
|
20
13
|
expect(JSON.stringify(1)).toBe('1');
|
|
21
14
|
});
|
|
22
15
|
|
|
23
16
|
test('boolean', () => {
|
|
24
17
|
const result = unknownToString(true);
|
|
25
|
-
expect(
|
|
26
|
-
if (Result.isOk(result)) {
|
|
27
|
-
expect(result.value).toBe('true');
|
|
28
|
-
}
|
|
18
|
+
expect(result).toBe('true');
|
|
29
19
|
expect(JSON.stringify(true)).toBe('true');
|
|
30
20
|
});
|
|
31
21
|
|
|
32
22
|
test('symbol', () => {
|
|
33
23
|
const result = unknownToString(Symbol('sym'));
|
|
34
|
-
expect(
|
|
35
|
-
if (Result.isOk(result)) {
|
|
36
|
-
expect(result.value).toBe('Symbol(sym)');
|
|
37
|
-
}
|
|
24
|
+
expect(result).toBe('Symbol(sym)');
|
|
38
25
|
expect(JSON.stringify(Symbol('sym'))).toBeUndefined();
|
|
39
26
|
});
|
|
40
27
|
|
|
41
28
|
test('function', () => {
|
|
42
29
|
const result = unknownToString(() => 0);
|
|
43
|
-
expect(
|
|
44
|
-
if (Result.isOk(result)) {
|
|
45
|
-
expect(result.value).toBe('() => 0');
|
|
46
|
-
}
|
|
30
|
+
expect(result).toBe('() => 0');
|
|
47
31
|
expect(JSON.stringify(() => 0)).toBeUndefined();
|
|
48
32
|
});
|
|
49
33
|
|
|
50
34
|
test('undefined', () => {
|
|
51
35
|
const result = unknownToString(undefined);
|
|
52
|
-
expect(
|
|
53
|
-
if (Result.isOk(result)) {
|
|
54
|
-
expect(result.value).toBe('undefined');
|
|
55
|
-
}
|
|
36
|
+
expect(result).toBe('undefined');
|
|
56
37
|
expect(JSON.stringify(undefined)).toBeUndefined();
|
|
57
38
|
});
|
|
58
39
|
|
|
59
40
|
test('null', () => {
|
|
60
41
|
const result = unknownToString(null);
|
|
61
|
-
expect(
|
|
62
|
-
if (Result.isOk(result)) {
|
|
63
|
-
expect(result.value).toBe('null');
|
|
64
|
-
}
|
|
42
|
+
expect(result).toBe('null');
|
|
65
43
|
expect(JSON.stringify(null)).toBe('null');
|
|
66
44
|
});
|
|
67
45
|
|
|
68
46
|
test('object', () => {
|
|
69
47
|
const result = unknownToString({ a: { b: 1 } });
|
|
70
|
-
expect(
|
|
71
|
-
if (Result.isOk(result)) {
|
|
72
|
-
expect(result.value).toBe('{"a":{"b":1}}');
|
|
73
|
-
}
|
|
48
|
+
expect(result).toBe('{"a":{"b":1}}');
|
|
74
49
|
expect(JSON.stringify({ a: { b: 1 } })).toBe('{"a":{"b":1}}');
|
|
75
50
|
});
|
|
76
51
|
|
|
@@ -79,36 +54,29 @@ describe('unknownToString', () => {
|
|
|
79
54
|
{ a: { b: 1 } },
|
|
80
55
|
{ prettyPrintObject: true },
|
|
81
56
|
);
|
|
82
|
-
expect(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
].join('\n'),
|
|
93
|
-
);
|
|
94
|
-
}
|
|
57
|
+
expect(result).toBe(
|
|
58
|
+
[
|
|
59
|
+
//
|
|
60
|
+
`{`,
|
|
61
|
+
` "a": {`,
|
|
62
|
+
` "b": 1`,
|
|
63
|
+
` }`,
|
|
64
|
+
`}`,
|
|
65
|
+
].join('\n'),
|
|
66
|
+
);
|
|
95
67
|
});
|
|
96
68
|
|
|
97
|
-
test('circular reference returns error', () => {
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
const result = unknownToString(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
69
|
+
test('circular reference returns error message', () => {
|
|
70
|
+
const mut_circular: { a: number; self?: unknown } = { a: 1 };
|
|
71
|
+
mut_circular.self = mut_circular;
|
|
72
|
+
const result = unknownToString(mut_circular);
|
|
73
|
+
// Should return an error message string instead of throwing
|
|
74
|
+
expect(typeof result).toBe('string');
|
|
75
|
+
expect(result).toMatch(/circular|serialize/iu);
|
|
105
76
|
});
|
|
106
77
|
|
|
107
78
|
test('BigInt value', () => {
|
|
108
79
|
const result = unknownToString(BigInt(123));
|
|
109
|
-
expect(
|
|
110
|
-
if (Result.isOk(result)) {
|
|
111
|
-
expect(result.value).toBe('123');
|
|
112
|
-
}
|
|
80
|
+
expect(result).toBe('123');
|
|
113
81
|
});
|
|
114
82
|
});
|