@simplysm/core-common 13.0.100 → 14.0.4

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.
Files changed (184) hide show
  1. package/README.md +86 -92
  2. package/dist/common.types.d.ts +14 -14
  3. package/dist/common.types.js +2 -1
  4. package/dist/common.types.js.map +1 -6
  5. package/dist/env.d.ts +8 -1
  6. package/dist/env.d.ts.map +1 -1
  7. package/dist/env.js +13 -9
  8. package/dist/env.js.map +1 -6
  9. package/dist/errors/argument-error.d.ts +10 -10
  10. package/dist/errors/argument-error.d.ts.map +1 -1
  11. package/dist/errors/argument-error.js +31 -14
  12. package/dist/errors/argument-error.js.map +1 -6
  13. package/dist/errors/not-implemented-error.d.ts +8 -8
  14. package/dist/errors/not-implemented-error.js +30 -12
  15. package/dist/errors/not-implemented-error.js.map +1 -6
  16. package/dist/errors/sd-error.d.ts +10 -10
  17. package/dist/errors/sd-error.d.ts.map +1 -1
  18. package/dist/errors/sd-error.js +45 -24
  19. package/dist/errors/sd-error.js.map +1 -6
  20. package/dist/errors/timeout-error.d.ts +10 -10
  21. package/dist/errors/timeout-error.js +34 -15
  22. package/dist/errors/timeout-error.js.map +1 -6
  23. package/dist/extensions/arr-ext.d.ts +2 -2
  24. package/dist/extensions/arr-ext.helpers.d.ts +10 -10
  25. package/dist/extensions/arr-ext.helpers.js +112 -89
  26. package/dist/extensions/arr-ext.helpers.js.map +1 -6
  27. package/dist/extensions/arr-ext.js +458 -422
  28. package/dist/extensions/arr-ext.js.map +1 -6
  29. package/dist/extensions/arr-ext.types.d.ts +57 -57
  30. package/dist/extensions/arr-ext.types.d.ts.map +1 -1
  31. package/dist/extensions/arr-ext.types.js +6 -1
  32. package/dist/extensions/arr-ext.types.js.map +1 -6
  33. package/dist/extensions/map-ext.d.ts +16 -16
  34. package/dist/extensions/map-ext.js +27 -22
  35. package/dist/extensions/map-ext.js.map +1 -6
  36. package/dist/extensions/set-ext.d.ts +11 -11
  37. package/dist/extensions/set-ext.js +32 -25
  38. package/dist/extensions/set-ext.js.map +1 -6
  39. package/dist/features/debounce-queue.d.ts +17 -17
  40. package/dist/features/debounce-queue.js +98 -70
  41. package/dist/features/debounce-queue.js.map +1 -6
  42. package/dist/features/event-emitter.d.ts +20 -20
  43. package/dist/features/event-emitter.js +101 -78
  44. package/dist/features/event-emitter.js.map +1 -6
  45. package/dist/features/serial-queue.d.ts +11 -11
  46. package/dist/features/serial-queue.js +78 -57
  47. package/dist/features/serial-queue.js.map +1 -6
  48. package/dist/globals.d.ts +4 -4
  49. package/dist/globals.js +9 -1
  50. package/dist/globals.js.map +1 -6
  51. package/dist/index.js +28 -27
  52. package/dist/index.js.map +1 -6
  53. package/dist/types/date-only.d.ts +64 -64
  54. package/dist/types/date-only.d.ts.map +1 -1
  55. package/dist/types/date-only.js +263 -252
  56. package/dist/types/date-only.js.map +1 -6
  57. package/dist/types/date-time.d.ts +36 -36
  58. package/dist/types/date-time.d.ts.map +1 -1
  59. package/dist/types/date-time.js +196 -288
  60. package/dist/types/date-time.js.map +1 -6
  61. package/dist/types/lazy-gc-map.d.ts +26 -26
  62. package/dist/types/lazy-gc-map.d.ts.map +1 -1
  63. package/dist/types/lazy-gc-map.js +202 -159
  64. package/dist/types/lazy-gc-map.js.map +1 -6
  65. package/dist/types/time.d.ts +23 -23
  66. package/dist/types/time.d.ts.map +1 -1
  67. package/dist/types/time.js +169 -158
  68. package/dist/types/time.js.map +1 -6
  69. package/dist/types/uuid.d.ts +11 -11
  70. package/dist/types/uuid.d.ts.map +1 -1
  71. package/dist/types/uuid.js +95 -70
  72. package/dist/types/uuid.js.map +1 -6
  73. package/dist/utils/bytes.d.ts +17 -17
  74. package/dist/utils/bytes.js +137 -81
  75. package/dist/utils/bytes.js.map +1 -6
  76. package/dist/utils/date-format.d.ts +40 -40
  77. package/dist/utils/date-format.js +187 -101
  78. package/dist/utils/date-format.js.map +1 -6
  79. package/dist/utils/error.d.ts +4 -4
  80. package/dist/utils/error.js +11 -6
  81. package/dist/utils/error.js.map +1 -6
  82. package/dist/utils/json.d.ts +19 -19
  83. package/dist/utils/json.js +187 -135
  84. package/dist/utils/json.js.map +1 -6
  85. package/dist/utils/num.d.ts +20 -20
  86. package/dist/utils/num.js +76 -34
  87. package/dist/utils/num.js.map +1 -6
  88. package/dist/utils/obj.d.ts +111 -111
  89. package/dist/utils/obj.d.ts.map +1 -1
  90. package/dist/utils/obj.js +706 -496
  91. package/dist/utils/obj.js.map +1 -6
  92. package/dist/utils/path.d.ts +10 -10
  93. package/dist/utils/path.js +35 -18
  94. package/dist/utils/path.js.map +1 -6
  95. package/dist/utils/primitive.d.ts +5 -5
  96. package/dist/utils/primitive.js +34 -14
  97. package/dist/utils/primitive.js.map +1 -6
  98. package/dist/utils/str.d.ts +38 -38
  99. package/dist/utils/str.js +217 -113
  100. package/dist/utils/str.js.map +1 -6
  101. package/dist/utils/template-strings.d.ts +26 -26
  102. package/dist/utils/template-strings.js +113 -40
  103. package/dist/utils/template-strings.js.map +1 -6
  104. package/dist/utils/transferable.d.ts +18 -18
  105. package/dist/utils/transferable.js +218 -151
  106. package/dist/utils/transferable.js.map +1 -6
  107. package/dist/utils/wait.d.ts +9 -9
  108. package/dist/utils/wait.js +30 -15
  109. package/dist/utils/wait.js.map +1 -6
  110. package/dist/utils/xml.d.ts +13 -13
  111. package/dist/utils/xml.js +84 -46
  112. package/dist/utils/xml.js.map +1 -6
  113. package/dist/utils/zip.d.ts +22 -22
  114. package/dist/utils/zip.js +172 -148
  115. package/dist/utils/zip.js.map +1 -6
  116. package/docs/array-extensions.md +430 -0
  117. package/docs/env.md +52 -0
  118. package/docs/errors.md +41 -56
  119. package/docs/features.md +82 -97
  120. package/docs/type-utilities.md +91 -0
  121. package/docs/types.md +221 -201
  122. package/docs/utils.md +319 -435
  123. package/package.json +7 -5
  124. package/src/common.types.ts +14 -14
  125. package/src/env.ts +12 -3
  126. package/src/errors/argument-error.ts +15 -15
  127. package/src/errors/not-implemented-error.ts +9 -9
  128. package/src/errors/sd-error.ts +12 -12
  129. package/src/errors/timeout-error.ts +12 -12
  130. package/src/extensions/arr-ext.helpers.ts +16 -16
  131. package/src/extensions/arr-ext.ts +35 -35
  132. package/src/extensions/arr-ext.types.ts +57 -57
  133. package/src/extensions/map-ext.ts +16 -16
  134. package/src/extensions/set-ext.ts +11 -11
  135. package/src/features/debounce-queue.ts +23 -23
  136. package/src/features/event-emitter.ts +25 -25
  137. package/src/features/serial-queue.ts +13 -13
  138. package/src/globals.ts +4 -4
  139. package/src/index.ts +5 -5
  140. package/src/types/date-only.ts +84 -83
  141. package/src/types/date-time.ts +43 -42
  142. package/src/types/lazy-gc-map.ts +44 -44
  143. package/src/types/time.ts +29 -29
  144. package/src/types/uuid.ts +15 -15
  145. package/src/utils/bytes.ts +35 -35
  146. package/src/utils/date-format.ts +59 -59
  147. package/src/utils/error.ts +4 -4
  148. package/src/utils/json.ts +41 -41
  149. package/src/utils/num.ts +20 -20
  150. package/src/utils/obj.ts +138 -138
  151. package/src/utils/path.ts +10 -10
  152. package/src/utils/primitive.ts +6 -6
  153. package/src/utils/str.ts +48 -48
  154. package/src/utils/template-strings.ts +29 -29
  155. package/src/utils/transferable.ts +38 -38
  156. package/src/utils/wait.ts +10 -10
  157. package/src/utils/xml.ts +19 -19
  158. package/src/utils/zip.ts +25 -25
  159. package/docs/extensions.md +0 -387
  160. package/tests/errors/errors.spec.ts +0 -80
  161. package/tests/extensions/array-extension.spec.ts +0 -654
  162. package/tests/extensions/map-extension.spec.ts +0 -117
  163. package/tests/extensions/set-extension.spec.ts +0 -67
  164. package/tests/types/date-only.spec.ts +0 -533
  165. package/tests/types/date-time.spec.ts +0 -246
  166. package/tests/types/lazy-gc-map.spec.ts +0 -606
  167. package/tests/types/time.spec.ts +0 -428
  168. package/tests/types/uuid.spec.ts +0 -74
  169. package/tests/utils/bytes-utils.spec.ts +0 -197
  170. package/tests/utils/date-format.spec.ts +0 -350
  171. package/tests/utils/debounce-queue.spec.ts +0 -226
  172. package/tests/utils/json.spec.ts +0 -400
  173. package/tests/utils/number.spec.ts +0 -136
  174. package/tests/utils/object.spec.ts +0 -810
  175. package/tests/utils/path.spec.ts +0 -70
  176. package/tests/utils/primitive.spec.ts +0 -43
  177. package/tests/utils/sd-event-emitter.spec.ts +0 -189
  178. package/tests/utils/serial-queue.spec.ts +0 -305
  179. package/tests/utils/string.spec.ts +0 -265
  180. package/tests/utils/template-strings.spec.ts +0 -48
  181. package/tests/utils/transferable.spec.ts +0 -639
  182. package/tests/utils/wait.spec.ts +0 -123
  183. package/tests/utils/xml.spec.ts +0 -146
  184. package/tests/utils/zip.spec.ts +0 -221
@@ -1,810 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { obj as objU, DateTime, DateOnly, Uuid } from "@simplysm/core-common";
3
-
4
- describe("object utils", () => {
5
- //#region clone
6
-
7
- describe("objClone()", () => {
8
- it("Clones primitive values", () => {
9
- expect(objU.clone(42)).toBe(42);
10
- expect(objU.clone("hello")).toBe("hello");
11
- expect(objU.clone(true)).toBe(true);
12
- expect(objU.clone(null)).toBe(null);
13
- expect(objU.clone(undefined)).toBe(undefined);
14
- });
15
-
16
- it("Deep clones array", () => {
17
- const arr = [1, [2, 3], { a: 4 }];
18
- const cloned = objU.clone(arr);
19
-
20
- expect(cloned).toEqual(arr);
21
- expect(cloned).not.toBe(arr);
22
- expect(cloned[1]).not.toBe(arr[1]);
23
- expect(cloned[2]).not.toBe(arr[2]);
24
- });
25
-
26
- it("Deep clones object", () => {
27
- const obj = { a: 1, b: { c: 2 }, d: [3, 4] };
28
- const cloned = objU.clone(obj);
29
-
30
- expect(cloned).toEqual(obj);
31
- expect(cloned).not.toBe(obj);
32
- expect(cloned.b).not.toBe(obj.b);
33
- expect(cloned.d).not.toBe(obj.d);
34
- });
35
-
36
- it("Clones Date", () => {
37
- const date = new Date(2024, 2, 15);
38
- const cloned = objU.clone(date);
39
-
40
- expect(cloned).toEqual(date);
41
- expect(cloned).not.toBe(date);
42
- });
43
-
44
- it("Clones DateTime", () => {
45
- const dt = new DateTime(2024, 3, 15, 10, 30);
46
- const cloned = objU.clone(dt);
47
-
48
- expect(cloned.tick).toBe(dt.tick);
49
- expect(cloned).not.toBe(dt);
50
- });
51
-
52
- it("Clones DateOnly", () => {
53
- const d = new DateOnly(2024, 3, 15);
54
- const cloned = objU.clone(d);
55
-
56
- expect(cloned.tick).toBe(d.tick);
57
- expect(cloned).not.toBe(d);
58
- });
59
-
60
- it("Clones Uuid", () => {
61
- const uuid = Uuid.generate();
62
- const cloned = objU.clone(uuid);
63
-
64
- expect(cloned.toString()).toBe(uuid.toString());
65
- expect(cloned).not.toBe(uuid);
66
- });
67
-
68
- it("Clones Map", () => {
69
- const map = new Map<string, number | { c: number }>([
70
- ["a", 1],
71
- ["b", { c: 2 }],
72
- ]);
73
- const cloned = objU.clone(map);
74
-
75
- expect(cloned.get("a")).toBe(1);
76
- expect(cloned.get("b")).toEqual({ c: 2 });
77
- expect(cloned.get("b")).not.toBe(map.get("b"));
78
- });
79
-
80
- it("Clones Set", () => {
81
- const obj = { a: 1 };
82
- const set = new Set([1, 2, obj]);
83
- const cloned = objU.clone(set);
84
-
85
- expect(cloned.has(1)).toBe(true);
86
- expect(cloned.has(2)).toBe(true);
87
- // Object in Set is cloned
88
- const clonedObj = Array.from(cloned).find((item) => typeof item === "object");
89
- expect(clonedObj).toEqual(obj);
90
- expect(clonedObj).not.toBe(obj);
91
- });
92
-
93
- it("Handles circular references", () => {
94
- const obj: Record<string, unknown> = { a: 1 };
95
- obj["self"] = obj;
96
-
97
- const cloned = objU.clone(obj);
98
-
99
- expect(cloned["a"]).toBe(1);
100
- expect(cloned["self"]).toBe(cloned);
101
- expect(cloned).not.toBe(obj);
102
- });
103
-
104
- it("Clones RegExp", () => {
105
- const regex = /test/gi;
106
- const cloned = objU.clone(regex);
107
-
108
- expect(cloned).toEqual(regex);
109
- expect(cloned).not.toBe(regex);
110
- expect(cloned.source).toBe("test");
111
- expect(cloned.flags).toBe("gi");
112
- });
113
-
114
- it("Clones Error", () => {
115
- const error = new Error("test error");
116
- const cloned = objU.clone(error);
117
-
118
- expect(cloned.message).toBe("test error");
119
- expect(cloned).not.toBe(error);
120
- });
121
-
122
- it("Clones Error cause", () => {
123
- const cause = new Error("cause error");
124
- const error = new Error("test error", { cause });
125
- const cloned = objU.clone(error);
126
-
127
- expect(cloned.message).toBe("test error");
128
- expect(cloned.cause).toBeInstanceOf(Error);
129
- expect((cloned.cause as Error).message).toBe("cause error");
130
- });
131
-
132
- it("Clones Error custom properties", () => {
133
- const error = new Error("test") as Error & { code: string; detail: object };
134
- error.code = "ERR_CODE";
135
- error.detail = { key: "value" };
136
- const cloned = objU.clone(error);
137
-
138
- expect(cloned.code).toBe("ERR_CODE");
139
- expect(cloned.detail).toEqual({ key: "value" });
140
- expect(cloned.detail).not.toBe(error.detail);
141
- });
142
-
143
- it("Clones Uint8Array", () => {
144
- const arr = new Uint8Array([1, 2, 3, 4, 5]);
145
- const cloned = objU.clone(arr);
146
-
147
- expect(cloned).toEqual(arr);
148
- expect(cloned).not.toBe(arr);
149
- expect(cloned.buffer).not.toBe(arr.buffer);
150
- });
151
-
152
- it("Symbol keys are not cloned", () => {
153
- // Object.keys() does not enumerate Symbol keys, so they are not cloned
154
- const sym = Symbol("test");
155
- const obj = { a: 1, [sym]: "symbol value" };
156
- const cloned = objU.clone(obj);
157
-
158
- expect(cloned.a).toBe(1);
159
- expect(cloned[sym]).toBeUndefined();
160
- });
161
- });
162
-
163
- //#endregion
164
-
165
- //#region equal
166
-
167
- describe("objU.equal()", () => {
168
- it("Compares primitive values", () => {
169
- expect(objU.equal(1, 1)).toBe(true);
170
- expect(objU.equal(1, 2)).toBe(false);
171
- expect(objU.equal("a", "a")).toBe(true);
172
- expect(objU.equal(null, null)).toBe(true);
173
- expect(objU.equal(undefined, undefined)).toBe(true);
174
- expect(objU.equal(null, undefined)).toBe(false);
175
- });
176
-
177
- it("Compares arrays", () => {
178
- expect(objU.equal([1, 2, 3], [1, 2, 3])).toBe(true);
179
- expect(objU.equal([1, 2, 3], [1, 2])).toBe(false);
180
- expect(objU.equal([1, 2, 3], [1, 3, 2])).toBe(false);
181
- });
182
-
183
- it("Compares objects", () => {
184
- expect(objU.equal({ a: 1, b: 2 }, { a: 1, b: 2 })).toBe(true);
185
- expect(objU.equal({ a: 1, b: 2 }, { a: 1, b: 3 })).toBe(false);
186
- expect(objU.equal({ a: 1 }, { a: 1, b: 2 })).toBe(false);
187
- });
188
-
189
- it("Compares nested objects", () => {
190
- expect(objU.equal({ a: { b: { c: 1 } } }, { a: { b: { c: 1 } } })).toBe(true);
191
- expect(objU.equal({ a: { b: { c: 1 } } }, { a: { b: { c: 2 } } })).toBe(false);
192
- });
193
-
194
- it("Compares DateTime", () => {
195
- const dt1 = new DateTime(2024, 3, 15);
196
- const dt2 = new DateTime(2024, 3, 15);
197
- const dt3 = new DateTime(2024, 3, 16);
198
-
199
- expect(objU.equal(dt1, dt2)).toBe(true);
200
- expect(objU.equal(dt1, dt3)).toBe(false);
201
- });
202
-
203
- it("Compares Uuid", () => {
204
- const uuid1 = new Uuid("12345678-9abc-def0-1234-56789abcdef0");
205
- const uuid2 = new Uuid("12345678-9abc-def0-1234-56789abcdef0");
206
- const uuid3 = new Uuid("12345678-9abc-def0-1234-56789abcdef1");
207
-
208
- expect(objU.equal(uuid1, uuid2)).toBe(true);
209
- expect(objU.equal(uuid1, uuid3)).toBe(false);
210
- });
211
-
212
- it("Compares RegExp", () => {
213
- const regex1 = /test/gi;
214
- const regex2 = /test/gi;
215
- const regex3 = /test/g;
216
- const regex4 = /other/gi;
217
-
218
- expect(objU.equal(regex1, regex2)).toBe(true);
219
- expect(objU.equal(regex1, regex3)).toBe(false); // Different flags
220
- expect(objU.equal(regex1, regex4)).toBe(false); // Different source
221
- });
222
-
223
- it("Compares Map", () => {
224
- const map1 = new Map([
225
- ["a", 1],
226
- ["b", 2],
227
- ]);
228
- const map2 = new Map([
229
- ["a", 1],
230
- ["b", 2],
231
- ]);
232
- const map3 = new Map([
233
- ["a", 1],
234
- ["b", 3],
235
- ]);
236
-
237
- expect(objU.equal(map1, map2)).toBe(true);
238
- expect(objU.equal(map1, map3)).toBe(false);
239
- });
240
-
241
- it("Compares Set", () => {
242
- const set1 = new Set([1, 2, 3]);
243
- const set2 = new Set([1, 2, 3]);
244
- const set3 = new Set([1, 2, 4]);
245
-
246
- expect(objU.equal(set1, set2)).toBe(true);
247
- expect(objU.equal(set1, set3)).toBe(false);
248
- });
249
-
250
- it("Compares only specific keys with topLevelIncludes option", () => {
251
- const obj1 = { a: 1, b: 2, c: 3 };
252
- const obj2 = { a: 1, b: 99, c: 99 };
253
-
254
- expect(objU.equal(obj1, obj2, { topLevelIncludes: ["a"] })).toBe(true);
255
- expect(objU.equal(obj1, obj2, { topLevelIncludes: ["a", "b"] })).toBe(false);
256
- });
257
-
258
- it("Excludes specific keys with topLevelExcludes option", () => {
259
- const obj1 = { a: 1, b: 2, c: 3 };
260
- const obj2 = { a: 1, b: 99, c: 99 };
261
-
262
- expect(objU.equal(obj1, obj2, { topLevelExcludes: ["b", "c"] })).toBe(true);
263
- });
264
-
265
- it("Ignores array order with ignoreArrayIndex option", () => {
266
- expect(objU.equal([1, 2, 3], [3, 2, 1], { ignoreArrayIndex: true })).toBe(true);
267
- });
268
-
269
- it("Performs shallow comparison with shallow option", () => {
270
- const inner = { c: 1 };
271
- const obj1 = { a: 1, b: inner };
272
- const obj2 = { a: 1, b: inner };
273
- const obj3 = { a: 1, b: { c: 1 } };
274
-
275
- expect(objU.equal(obj1, obj2, { shallow: true })).toBe(true);
276
- expect(objU.equal(obj1, obj3, { shallow: true })).toBe(false);
277
- });
278
- });
279
-
280
- //#endregion
281
-
282
- //#region merge
283
-
284
- describe("objU.merge()", () => {
285
- it("Copies target when source is null", () => {
286
- const target = { a: 1 };
287
- const result = objU.merge(null, target);
288
-
289
- expect(result).toEqual({ a: 1 });
290
- expect(result).not.toBe(target);
291
- });
292
-
293
- it("Copies source when target is undefined", () => {
294
- const source = { a: 1 };
295
- const result = objU.merge(source, undefined);
296
-
297
- expect(result).toEqual({ a: 1 });
298
- });
299
-
300
- it("Merges objects", () => {
301
- const source = { a: 1, b: 2 };
302
- const target = { b: 3, c: 4 };
303
- const result = objU.merge(source, target);
304
-
305
- expect(result).toEqual({ a: 1, b: 3, c: 4 });
306
- });
307
-
308
- it("Merges nested objects", () => {
309
- const source = { a: { b: 1, c: 2 } };
310
- const target = { a: { c: 3, d: 4 } };
311
- const result = objU.merge(source, target);
312
-
313
- expect(result).toEqual({ a: { b: 1, c: 3, d: 4 } });
314
- });
315
-
316
- it("Replaces array with arrayProcess: replace", () => {
317
- const source = { arr: [1, 2, 3] };
318
- const target = { arr: [4, 5] };
319
- const result = objU.merge(source, target, { arrayProcess: "replace" });
320
-
321
- expect(result.arr).toEqual([4, 5]);
322
- });
323
-
324
- it("Concatenates arrays with arrayProcess: concat", () => {
325
- const source = { arr: [1, 2, 3] };
326
- const target = { arr: [3, 4, 5] };
327
- const result = objU.merge(source, target, { arrayProcess: "concat" });
328
-
329
- // Duplicates removed via Set
330
- expect(result.arr).toEqual([1, 2, 3, 4, 5]);
331
- });
332
-
333
- it("Deletes when null with useDelTargetNull option", () => {
334
- const source = { a: 1, b: 2 };
335
- const target = { b: null };
336
- const result = objU.merge(source, target, { useDelTargetNull: true });
337
-
338
- expect(result).toEqual({ a: 1 });
339
- });
340
-
341
- it("Returns target when source is object and target is primitive", () => {
342
- const source = { a: 1 };
343
- const target = "string";
344
-
345
- const result = objU.merge(source, target as any);
346
-
347
- expect(result).toBe("string");
348
- });
349
-
350
- it("Returns target when source is primitive and target is object", () => {
351
- const source = "string";
352
- const target = { a: 1 };
353
-
354
- const result = objU.merge(source as any, target);
355
-
356
- expect(result).toEqual({ a: 1 });
357
- });
358
-
359
- it("Returns target when source is array and target is plain object", () => {
360
- const source = [1, 2, 3];
361
- const target = { a: 1 };
362
-
363
- const result = objU.merge(source as any, target);
364
-
365
- expect(result).toEqual({ a: 1 });
366
- });
367
-
368
- it("Returns target when source is plain object and target is array", () => {
369
- const source = { a: 1 };
370
- const target = [1, 2, 3];
371
-
372
- const result = objU.merge(source as any, target);
373
-
374
- expect(result).toEqual([1, 2, 3]);
375
- });
376
-
377
- it("Merges deeply nested objects (3+ levels)", () => {
378
- const source = {
379
- level1: {
380
- level2: {
381
- level3: {
382
- a: 1,
383
- b: 2,
384
- },
385
- x: 10,
386
- },
387
- y: 20,
388
- },
389
- z: 30,
390
- };
391
- const target = {
392
- level1: {
393
- level2: {
394
- level3: {
395
- b: 3,
396
- c: 4,
397
- },
398
- },
399
- },
400
- };
401
-
402
- const result = objU.merge(source, target);
403
-
404
- expect(result).toEqual({
405
- level1: {
406
- level2: {
407
- level3: {
408
- a: 1,
409
- b: 3,
410
- c: 4,
411
- },
412
- x: 10,
413
- },
414
- y: 20,
415
- },
416
- z: 30,
417
- });
418
- });
419
-
420
- it("Modifies only deep value in 4-level nesting", () => {
421
- const source = {
422
- a: {
423
- b: {
424
- c: {
425
- d: { value: 1 },
426
- },
427
- },
428
- },
429
- };
430
- const target = {
431
- a: {
432
- b: {
433
- c: {
434
- d: { value: 2 },
435
- },
436
- },
437
- },
438
- };
439
-
440
- const result = objU.merge(source, target);
441
-
442
- expect(result.a.b.c.d.value).toBe(2);
443
- });
444
-
445
- it("Clones new key-value in Map merge", () => {
446
- const sourceMap = new Map<string, { value: number }>([["key1", { value: 1 }]]);
447
- const targetObj = { value: 2 };
448
- const targetMap = new Map<string, { value: number }>([["key2", targetObj]]);
449
-
450
- const result = objU.merge(sourceMap, targetMap);
451
-
452
- // key2 value is cloned, should be different reference
453
- expect(result.get("key2")).toEqual({ value: 2 });
454
- expect(result.get("key2")).not.toBe(targetObj);
455
- });
456
- });
457
-
458
- describe("objU.merge3()", () => {
459
- it("Uses source value when only source changes", () => {
460
- const origin = { a: 1, b: 2 };
461
- const source = { a: 1, b: 3 };
462
- const target = { a: 1, b: 2 };
463
- const { conflict, result } = objU.merge3(source, origin, target);
464
-
465
- expect(conflict).toBe(false);
466
- expect(result).toEqual({ a: 1, b: 3 });
467
- });
468
-
469
- it("Uses target value when only target changes", () => {
470
- const origin = { a: 1, b: 2 };
471
- const source = { a: 1, b: 2 };
472
- const target = { a: 1, b: 4 };
473
- const { conflict, result } = objU.merge3(source, origin, target);
474
-
475
- expect(conflict).toBe(false);
476
- expect(result).toEqual({ a: 1, b: 4 });
477
- });
478
-
479
- it("Uses value without conflict when both change to same value", () => {
480
- const origin = { a: 1, b: 2 };
481
- const source = { a: 1, b: 5 };
482
- const target = { a: 1, b: 5 };
483
- const { conflict, result } = objU.merge3(source, origin, target);
484
-
485
- expect(conflict).toBe(false);
486
- expect(result).toEqual({ a: 1, b: 5 });
487
- });
488
-
489
- it("Returns conflict when both change to different values", () => {
490
- const origin = { a: 1, b: 2 };
491
- const source = { a: 1, b: 3 };
492
- const target = { a: 1, b: 4 };
493
- const { conflict, result } = objU.merge3(source, origin, target);
494
-
495
- expect(conflict).toBe(true);
496
- // Origin value preserved
497
- expect(result.b).toBe(2);
498
- });
499
-
500
- it("Returns conflict when only some keys conflict", () => {
501
- const origin = { a: 1, b: 2, c: 3 };
502
- const source = { a: 10, b: 20, c: 3 };
503
- const target = { a: 1, b: 30, c: 4 };
504
- const { conflict, result } = objU.merge3(source, origin, target);
505
-
506
- expect(conflict).toBe(true);
507
- expect(result.a).toBe(10); // Only source changed
508
- expect(result.b).toBe(2); // Both changed differently → conflict → origin preserved
509
- expect(result.c).toBe(4); // Only target changed
510
- });
511
-
512
- it("Detects conflict in nested objects", () => {
513
- const origin = { a: { b: 1, c: 2 } };
514
- const source = { a: { b: 10, c: 2 } };
515
- const target = { a: { b: 20, c: 2 } };
516
- const { conflict, result } = objU.merge3(source, origin, target);
517
-
518
- expect(conflict).toBe(true);
519
- expect(result.a.b).toBe(1); // Both changed differently → conflict → origin preserved
520
- expect(result.a.c).toBe(2);
521
- });
522
-
523
- it("Detects conflict in nested object when different internal keys change", () => {
524
- // merge3 compares at key level, so entire { a: {...} } is compared
525
- // If source.a differs from origin.a and target.a differs from origin.a, conflict
526
- const origin = { a: { b: 1, c: 2 } };
527
- const source = { a: { b: 10, c: 2 } };
528
- const target = { a: { b: 1, c: 20 } };
529
- const { conflict, result } = objU.merge3(source, origin, target);
530
-
531
- expect(conflict).toBe(true);
532
- expect(result.a.b).toBe(1); // Conflict → origin preserved
533
- expect(result.a.c).toBe(2);
534
- });
535
-
536
- it("Detects conflict in array", () => {
537
- const origin = { arr: [1, 2, 3] };
538
- const source = { arr: [1, 2, 4] };
539
- const target = { arr: [1, 2, 5] };
540
- const { conflict, result } = objU.merge3(source, origin, target);
541
-
542
- expect(conflict).toBe(true);
543
- expect(result.arr).toEqual([1, 2, 3]); // Conflict → origin preserved
544
- });
545
-
546
- it("Detects conflict in primitive value", () => {
547
- const origin = { value: "original" };
548
- const source = { value: "from source" };
549
- const target = { value: "from target" };
550
- const { conflict, result } = objU.merge3(source, origin, target);
551
-
552
- expect(conflict).toBe(true);
553
- expect(result.value).toBe("original"); // Conflict → origin preserved
554
- });
555
- });
556
-
557
- //#endregion
558
-
559
- //#region omit / pick
560
-
561
- describe("objU.omit()", () => {
562
- it("Excludes specific keys", () => {
563
- const obj = { a: 1, b: 2, c: 3 };
564
- const result = objU.omit(obj, ["b"]);
565
-
566
- expect(result).toEqual({ a: 1, c: 3 });
567
- });
568
-
569
- it("Excludes multiple keys", () => {
570
- const obj = { a: 1, b: 2, c: 3, d: 4 };
571
- const result = objU.omit(obj, ["a", "c"]);
572
-
573
- expect(result).toEqual({ b: 2, d: 4 });
574
- });
575
- });
576
-
577
- describe("objU.omitByFilter()", () => {
578
- it("Excludes keys matching condition", () => {
579
- const obj = { a: 1, b: 2, c: 3 };
580
- const result = objU.omitByFilter(obj, (key) => key === "b");
581
-
582
- expect(result).toEqual({ a: 1, c: 3 });
583
- });
584
- });
585
-
586
- describe("objU.pick()", () => {
587
- it("Selects only specific keys", () => {
588
- const obj = { a: 1, b: 2, c: 3 };
589
- const result = objU.pick(obj, ["a", "c"]);
590
-
591
- expect(result).toEqual({ a: 1, c: 3 });
592
- });
593
- });
594
-
595
- //#endregion
596
-
597
- //#region chain value
598
-
599
- describe("objU.getChainValue()", () => {
600
- it("Gets value using dot notation", () => {
601
- const obj = { a: { b: { c: 1 } } };
602
-
603
- expect(objU.getChainValue(obj, "a.b.c")).toBe(1);
604
- });
605
-
606
- it("Gets value using array notation", () => {
607
- const obj = { arr: [{ name: "first" }, { name: "second" }] };
608
-
609
- expect(objU.getChainValue(obj, "arr[1].name")).toBe("second");
610
- });
611
-
612
- it("Returns undefined for non-existent path with optional: true", () => {
613
- const obj = { a: 1 };
614
-
615
- expect(objU.getChainValue(obj, "b.c.d", true)).toBe(undefined);
616
- });
617
- });
618
-
619
- describe("objU.getChainValueByDepth()", () => {
620
- it("Descends by depth using same key", () => {
621
- const obj = {
622
- parent: {
623
- parent: {
624
- parent: {
625
- name: "leaf",
626
- },
627
- },
628
- },
629
- };
630
-
631
- const result = objU.getChainValueByDepth(obj, "parent", 2);
632
-
633
- expect(result).toEqual({ parent: { name: "leaf" } });
634
- });
635
-
636
- it("Throws error when depth is 0", () => {
637
- const obj = { parent: { name: "child" } };
638
-
639
- expect(() => objU.getChainValueByDepth(obj, "parent", 0)).toThrow(
640
- "depth must be 1 or greater",
641
- );
642
- });
643
-
644
- it("Descends one level when depth is 1", () => {
645
- const obj = { parent: { name: "child" } };
646
-
647
- const result = objU.getChainValueByDepth(obj, "parent", 1);
648
-
649
- expect(result).toEqual({ name: "child" });
650
- });
651
-
652
- it("Returns undefined when intermediate path missing with optional: true", () => {
653
- const obj = { parent: { name: "child" } };
654
-
655
- const result = objU.getChainValueByDepth(obj, "parent", 5, true);
656
-
657
- expect(result).toBe(undefined);
658
- });
659
-
660
- it("Throws error when intermediate path missing without optional", () => {
661
- const obj = { parent: undefined as unknown };
662
-
663
- // Without optional, trying to access property on undefined throws error
664
- // Current implementation only checks result == null inside optional condition
665
- // So without optional, error is possible
666
- expect(() => objU.getChainValueByDepth(obj as any, "parent", 2)).toThrow();
667
- });
668
- });
669
-
670
- describe("objU.setChainValue()", () => {
671
- it("Sets value using dot notation", () => {
672
- const obj: Record<string, unknown> = {};
673
- objU.setChainValue(obj, "a.b.c", 1);
674
-
675
- expect(obj).toEqual({ a: { b: { c: 1 } } });
676
- });
677
-
678
- it("Overwrites existing value", () => {
679
- const obj = { a: { b: { c: 1 } } };
680
- objU.setChainValue(obj, "a.b.c", 2);
681
-
682
- expect(obj.a.b.c).toBe(2);
683
- });
684
-
685
- it("Throws error for empty chain", () => {
686
- const obj: Record<string, unknown> = {};
687
-
688
- expect(() => objU.setChainValue(obj, "", 1)).toThrow();
689
- });
690
- });
691
-
692
- describe("objU.deleteChainValue()", () => {
693
- it("Deletes value at chain path", () => {
694
- const obj = { a: { b: { c: 1, d: 2 } } };
695
- objU.deleteChainValue(obj, "a.b.c");
696
-
697
- expect(obj.a.b).toEqual({ d: 2 });
698
- });
699
-
700
- it("Silently ignores non-existent path", () => {
701
- const obj = { a: 1 };
702
-
703
- // No error when intermediate path missing
704
- expect(() => objU.deleteChainValue(obj, "b.c.d")).not.toThrow();
705
- expect(obj).toEqual({ a: 1 });
706
- });
707
-
708
- it("Silently ignores undefined intermediate path", () => {
709
- const obj: Record<string, unknown> = { a: undefined };
710
-
711
- expect(() => objU.deleteChainValue(obj, "a.b.c")).not.toThrow();
712
- expect(obj).toEqual({ a: undefined });
713
- });
714
-
715
- it("Silently ignores null intermediate path", () => {
716
- const obj: Record<string, unknown> = { a: null };
717
-
718
- expect(() => objU.deleteChainValue(obj, "a.b.c")).not.toThrow();
719
- expect(obj).toEqual({ a: null });
720
- });
721
-
722
- it("Deletes using array index path", () => {
723
- const obj = { arr: [{ name: "first" }, { name: "second" }] };
724
- objU.deleteChainValue(obj, "arr[0].name");
725
-
726
- expect(obj.arr[0]).toEqual({});
727
- expect(obj.arr[1]).toEqual({ name: "second" });
728
- });
729
-
730
- it("Throws error for empty chain", () => {
731
- const obj = { a: 1 };
732
-
733
- expect(() => objU.deleteChainValue(obj, "")).toThrow();
734
- });
735
- });
736
-
737
- //#endregion
738
-
739
- //#region clear / transform
740
-
741
- describe("objU.clearUndefined()", () => {
742
- it("Deletes keys with undefined value", () => {
743
- const obj = { a: 1, b: undefined, c: 3 };
744
- const result = objU.clearUndefined(obj);
745
-
746
- expect(result).toEqual({ a: 1, c: 3 });
747
- expect("b" in result).toBe(false);
748
- });
749
- });
750
-
751
- describe("objU.clear()", () => {
752
- it("Deletes all keys", () => {
753
- const obj = { a: 1, b: 2, c: 3 };
754
- const result = objU.clear(obj);
755
-
756
- expect(Object.keys(result)).toHaveLength(0);
757
- });
758
- });
759
-
760
- describe("objU.nullToUndefined()", () => {
761
- it("Converts null to undefined", () => {
762
- expect(objU.nullToUndefined(null)).toBe(undefined);
763
- });
764
-
765
- it("Converts nested null to undefined", () => {
766
- const obj = { a: 1, b: null, c: { d: null } };
767
- const result = objU.nullToUndefined(obj);
768
-
769
- expect(result).toEqual({ a: 1, b: undefined, c: { d: undefined } });
770
- });
771
-
772
- it("Converts null in array to undefined", () => {
773
- const arr = [1, null, { a: null }];
774
- const result = objU.nullToUndefined(arr);
775
-
776
- expect(result).toEqual([1, undefined, { a: undefined }]);
777
- });
778
-
779
- it("Safely handles object with circular references", () => {
780
- const obj: Record<string, unknown> = { a: null };
781
- obj["self"] = obj;
782
- const result = objU.nullToUndefined(obj);
783
- expect(result).toBeDefined();
784
- expect((result as Record<string, unknown>)["a"]).toBeUndefined();
785
- });
786
-
787
- it("Safely handles array with circular references", () => {
788
- const arr: unknown[] = [null, 1];
789
- arr.push(arr);
790
- const result = objU.nullToUndefined(arr);
791
- expect(result).toBeDefined();
792
- expect((result as unknown[])[0]).toBeUndefined();
793
- expect((result as unknown[])[1]).toBe(1);
794
- });
795
- });
796
-
797
- describe("objU.unflatten()", () => {
798
- it("Converts flattened object to nested", () => {
799
- const flat = { "a.b.c": 1, "a.b.d": 2, "e": 3 };
800
- const result = objU.unflatten(flat);
801
-
802
- expect(result).toEqual({
803
- a: { b: { c: 1, d: 2 } },
804
- e: 3,
805
- });
806
- });
807
- });
808
-
809
- //#endregion
810
- });