@simplysm/core-common 13.0.69 → 13.0.71

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 (151) hide show
  1. package/README.md +66 -267
  2. package/dist/common.types.d.ts +14 -14
  3. package/dist/errors/argument-error.d.ts +10 -10
  4. package/dist/errors/argument-error.d.ts.map +1 -1
  5. package/dist/errors/argument-error.js +2 -2
  6. package/dist/errors/argument-error.js.map +1 -1
  7. package/dist/errors/not-implemented-error.d.ts +8 -8
  8. package/dist/errors/not-implemented-error.js +2 -2
  9. package/dist/errors/not-implemented-error.js.map +1 -1
  10. package/dist/errors/sd-error.d.ts +10 -10
  11. package/dist/errors/sd-error.d.ts.map +1 -1
  12. package/dist/errors/timeout-error.d.ts +10 -10
  13. package/dist/errors/timeout-error.js +3 -3
  14. package/dist/errors/timeout-error.js.map +1 -1
  15. package/dist/extensions/arr-ext.d.ts +2 -2
  16. package/dist/extensions/arr-ext.helpers.d.ts +8 -8
  17. package/dist/extensions/arr-ext.helpers.js +1 -1
  18. package/dist/extensions/arr-ext.helpers.js.map +1 -1
  19. package/dist/extensions/arr-ext.js +13 -13
  20. package/dist/extensions/arr-ext.js.map +1 -1
  21. package/dist/extensions/arr-ext.types.d.ts +57 -57
  22. package/dist/extensions/arr-ext.types.d.ts.map +1 -1
  23. package/dist/extensions/map-ext.d.ts +16 -16
  24. package/dist/extensions/set-ext.d.ts +11 -11
  25. package/dist/features/debounce-queue.d.ts +17 -15
  26. package/dist/features/debounce-queue.d.ts.map +1 -1
  27. package/dist/features/debounce-queue.js +6 -6
  28. package/dist/features/debounce-queue.js.map +1 -1
  29. package/dist/features/event-emitter.d.ts +20 -20
  30. package/dist/features/event-emitter.js +17 -17
  31. package/dist/features/serial-queue.d.ts +11 -11
  32. package/dist/features/serial-queue.js +5 -5
  33. package/dist/features/serial-queue.js.map +1 -1
  34. package/dist/globals.d.ts +4 -4
  35. package/dist/types/date-only.d.ts +64 -64
  36. package/dist/types/date-only.d.ts.map +1 -1
  37. package/dist/types/date-only.js +63 -63
  38. package/dist/types/date-time.d.ts +37 -37
  39. package/dist/types/date-time.d.ts.map +1 -1
  40. package/dist/types/date-time.js +54 -37
  41. package/dist/types/date-time.js.map +1 -1
  42. package/dist/types/lazy-gc-map.d.ts +26 -26
  43. package/dist/types/lazy-gc-map.d.ts.map +1 -1
  44. package/dist/types/lazy-gc-map.js +26 -26
  45. package/dist/types/lazy-gc-map.js.map +1 -1
  46. package/dist/types/time.d.ts +25 -25
  47. package/dist/types/time.d.ts.map +1 -1
  48. package/dist/types/time.js +25 -25
  49. package/dist/types/time.js.map +1 -1
  50. package/dist/types/uuid.d.ts +11 -11
  51. package/dist/types/uuid.d.ts.map +1 -1
  52. package/dist/types/uuid.js +12 -12
  53. package/dist/types/uuid.js.map +1 -1
  54. package/dist/utils/bytes.d.ts +17 -17
  55. package/dist/utils/bytes.js +4 -4
  56. package/dist/utils/bytes.js.map +1 -1
  57. package/dist/utils/date-format.d.ts +45 -45
  58. package/dist/utils/date-format.js +1 -1
  59. package/dist/utils/date-format.js.map +1 -1
  60. package/dist/utils/error.d.ts +4 -4
  61. package/dist/utils/json.d.ts +17 -17
  62. package/dist/utils/json.js +3 -3
  63. package/dist/utils/json.js.map +1 -1
  64. package/dist/utils/num.d.ts +23 -23
  65. package/dist/utils/obj.d.ts +111 -111
  66. package/dist/utils/obj.d.ts.map +1 -1
  67. package/dist/utils/obj.js +3 -3
  68. package/dist/utils/obj.js.map +1 -1
  69. package/dist/utils/path.d.ts +10 -10
  70. package/dist/utils/primitive.d.ts +5 -5
  71. package/dist/utils/primitive.js +1 -1
  72. package/dist/utils/primitive.js.map +1 -1
  73. package/dist/utils/str.d.ts +46 -46
  74. package/dist/utils/str.d.ts.map +1 -1
  75. package/dist/utils/str.js +5 -5
  76. package/dist/utils/str.js.map +1 -1
  77. package/dist/utils/template-strings.d.ts +26 -26
  78. package/dist/utils/transferable.d.ts +18 -18
  79. package/dist/utils/transferable.js +1 -1
  80. package/dist/utils/transferable.js.map +1 -1
  81. package/dist/utils/wait.d.ts +9 -9
  82. package/dist/utils/xml.d.ts +13 -13
  83. package/dist/utils/xml.d.ts.map +1 -1
  84. package/dist/utils/xml.js +1 -0
  85. package/dist/utils/xml.js.map +1 -1
  86. package/dist/zip/sd-zip.d.ts +22 -22
  87. package/dist/zip/sd-zip.js +16 -16
  88. package/package.json +4 -4
  89. package/src/common.types.ts +17 -17
  90. package/src/errors/argument-error.ts +15 -15
  91. package/src/errors/not-implemented-error.ts +9 -9
  92. package/src/errors/sd-error.ts +12 -12
  93. package/src/errors/timeout-error.ts +12 -12
  94. package/src/extensions/arr-ext.helpers.ts +10 -10
  95. package/src/extensions/arr-ext.ts +57 -57
  96. package/src/extensions/arr-ext.types.ts +59 -59
  97. package/src/extensions/map-ext.ts +16 -16
  98. package/src/extensions/set-ext.ts +11 -11
  99. package/src/features/debounce-queue.ts +21 -19
  100. package/src/features/event-emitter.ts +25 -25
  101. package/src/features/serial-queue.ts +13 -13
  102. package/src/globals.ts +4 -4
  103. package/src/index.ts +1 -1
  104. package/src/types/date-only.ts +83 -83
  105. package/src/types/date-time.ts +64 -44
  106. package/src/types/lazy-gc-map.ts +45 -45
  107. package/src/types/time.ts +34 -34
  108. package/src/types/uuid.ts +17 -17
  109. package/src/utils/bytes.ts +35 -35
  110. package/src/utils/date-format.ts +65 -65
  111. package/src/utils/error.ts +4 -4
  112. package/src/utils/json.ts +39 -39
  113. package/src/utils/num.ts +23 -23
  114. package/src/utils/obj.ts +138 -138
  115. package/src/utils/path.ts +10 -10
  116. package/src/utils/primitive.ts +6 -6
  117. package/src/utils/str.ts +260 -261
  118. package/src/utils/template-strings.ts +29 -29
  119. package/src/utils/transferable.ts +284 -284
  120. package/src/utils/wait.ts +10 -10
  121. package/src/utils/xml.ts +20 -19
  122. package/src/zip/sd-zip.ts +25 -25
  123. package/tests/errors/errors.spec.ts +80 -0
  124. package/tests/extensions/array-extension.spec.ts +796 -0
  125. package/tests/extensions/map-extension.spec.ts +147 -0
  126. package/tests/extensions/set-extension.spec.ts +74 -0
  127. package/tests/types/date-only.spec.ts +638 -0
  128. package/tests/types/date-time.spec.ts +391 -0
  129. package/tests/types/lazy-gc-map.spec.ts +692 -0
  130. package/tests/types/time.spec.ts +559 -0
  131. package/tests/types/uuid.spec.ts +74 -0
  132. package/tests/utils/bytes-utils.spec.ts +230 -0
  133. package/tests/utils/date-format.spec.ts +373 -0
  134. package/tests/utils/debounce-queue.spec.ts +272 -0
  135. package/tests/utils/json.spec.ts +486 -0
  136. package/tests/utils/number.spec.ts +157 -0
  137. package/tests/utils/object.spec.ts +829 -0
  138. package/tests/utils/path.spec.ts +78 -0
  139. package/tests/utils/primitive.spec.ts +43 -0
  140. package/tests/utils/sd-event-emitter.spec.ts +216 -0
  141. package/tests/utils/serial-queue.spec.ts +365 -0
  142. package/tests/utils/string.spec.ts +281 -0
  143. package/tests/utils/template-strings.spec.ts +57 -0
  144. package/tests/utils/transferable.spec.ts +703 -0
  145. package/tests/utils/wait.spec.ts +145 -0
  146. package/tests/utils/xml.spec.ts +146 -0
  147. package/tests/zip/sd-zip.spec.ts +238 -0
  148. package/docs/extensions.md +0 -503
  149. package/docs/features.md +0 -109
  150. package/docs/types.md +0 -486
  151. package/docs/utils.md +0 -780
@@ -1,284 +1,284 @@
1
- import { DateTime } from "../types/date-time";
2
- import { DateOnly } from "../types/date-only";
3
- import { Time } from "../types/time";
4
- import { Uuid } from "../types/uuid";
5
-
6
- /**
7
- * Worker 전송 가능한 객체 타입
8
- *
9
- * 코드에서는 ArrayBuffer 사용됩니다.
10
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects
11
- */
12
- type Transferable = ArrayBuffer;
13
-
14
- /**
15
- * Transferable 변환 유틸리티 함수
16
- *
17
- * Worker 데이터 전송을 위한 직렬화/역직렬화를 수행합니다.
18
- * structuredClone이 지원하지 않는 커스텀 타입들을 처리합니다.
19
- *
20
- * 지원 타입:
21
- * - Date, DateTime, DateOnly, Time, Uuid, RegExp
22
- * - Error (cause, code, detail 포함)
23
- * - Uint8Array (다른 TypedArray는 미지원, 일반 객체로 처리됨)
24
- * - Array, Map, Set, 일반 객체
25
- *
26
- * @note 순환 참조가 있으면 transferableEncode TypeError 발생 (경로 정보 포함)
27
- * @note 동일 객체가 여러 곳에서 참조되면 캐시된 인코딩 결과를 재사용합니다
28
- *
29
- * @example
30
- * // Worker로 데이터 전송
31
- * const { result, transferList } = transferableEncode(data);
32
- * worker.postMessage(result, transferList);
33
- *
34
- * // Worker에서 데이터 수신
35
- * const decoded = transferableDecode(event.data);
36
- */
37
-
38
- //#region encode
39
-
40
- /**
41
- * 심플리즘 타입을 사용한 객체를 일반 객체로 변환
42
- * Worker에 전송할 있는 형태로 직렬화
43
- *
44
- * @throws 순환 참조 감지 TypeError
45
- */
46
- export function transferableEncode(obj: unknown): {
47
- result: unknown;
48
- transferList: Transferable[];
49
- } {
50
- const transferList: Transferable[] = [];
51
- const ancestors = new Set<object>();
52
- const cache = new Map<object, unknown>();
53
- const result = encodeImpl(obj, transferList, [], ancestors, cache);
54
- return { result, transferList };
55
- }
56
-
57
- function encodeImpl(
58
- obj: unknown,
59
- transferList: Transferable[],
60
- path: (string | number)[],
61
- ancestors: Set<object>,
62
- cache: Map<object, unknown>,
63
- ): unknown {
64
- if (obj == null) return obj;
65
-
66
- // 객체 타입 처리: 순환 감지 + 캐시
67
- if (typeof obj === "object") {
68
- // 순환 참조 감지 (현재 재귀 스택에 있는 객체)
69
- if (ancestors.has(obj)) {
70
- const currentPath = path.length > 0 ? path.join(".") : "root";
71
- throw new TypeError(`순환 참조가 감지되었습니다: ${currentPath}`);
72
- }
73
-
74
- // 캐시 히트 이전 인코딩 결과 재사용
75
- const cached = cache.get(obj);
76
- if (cached !== undefined) return cached;
77
-
78
- // 재귀 스택에 추가
79
- ancestors.add(obj);
80
- }
81
-
82
- let result: unknown;
83
-
84
- try {
85
- // 1. Uint8Array
86
- if (obj instanceof Uint8Array) {
87
- // SharedArrayBuffer 이미 공유 메모리이므로 transferList에 추가하지 않음
88
- // ArrayBuffer transferList 추가
89
- const isSharedArrayBuffer =
90
- typeof SharedArrayBuffer !== "undefined" && obj.buffer instanceof SharedArrayBuffer;
91
- const buffer = obj.buffer as ArrayBuffer;
92
- if (!isSharedArrayBuffer && !transferList.includes(buffer)) {
93
- transferList.push(buffer);
94
- }
95
- result = obj;
96
- }
97
- // 2. 특수 타입 변환 (JSON.stringify 없이 구조체로 변환)
98
- else if (obj instanceof Date) {
99
- result = { __type__: "Date", data: obj.getTime() };
100
- } else if (obj instanceof DateTime) {
101
- result = { __type__: "DateTime", data: obj.tick };
102
- } else if (obj instanceof DateOnly) {
103
- result = { __type__: "DateOnly", data: obj.tick };
104
- } else if (obj instanceof Time) {
105
- result = { __type__: "Time", data: obj.tick };
106
- } else if (obj instanceof Uuid) {
107
- result = { __type__: "Uuid", data: obj.toString() };
108
- } else if (obj instanceof RegExp) {
109
- result = { __type__: "RegExp", data: { source: obj.source, flags: obj.flags } };
110
- } else if (obj instanceof Error) {
111
- const errObj = obj as Error & {
112
- code?: unknown;
113
- detail?: unknown;
114
- };
115
- result = {
116
- __type__: "Error",
117
- data: {
118
- name: errObj.name,
119
- message: errObj.message,
120
- stack: errObj.stack,
121
- ...(errObj.code !== undefined ? { code: errObj.code } : {}),
122
- ...(errObj.detail !== undefined
123
- ? {
124
- detail: encodeImpl(
125
- errObj.detail,
126
- transferList,
127
- [...path, "detail"],
128
- ancestors,
129
- cache,
130
- ),
131
- }
132
- : {}),
133
- ...(errObj.cause !== undefined
134
- ? {
135
- cause: encodeImpl(errObj.cause, transferList, [...path, "cause"], ancestors, cache),
136
- }
137
- : {}),
138
- },
139
- };
140
- }
141
- // 3. 배열 재귀 순회
142
- else if (Array.isArray(obj)) {
143
- result = obj.map((item, idx) =>
144
- encodeImpl(item, transferList, [...path, idx], ancestors, cache),
145
- );
146
- }
147
- // 4. Map 재귀 순회
148
- else if (obj instanceof Map) {
149
- let idx = 0;
150
- result = new Map(
151
- Array.from(obj.entries()).map(([k, v]) => {
152
- const keyPath = [...path, `Map[${idx}].key`];
153
- const valuePath = [...path, `Map[${idx}].value`];
154
- idx++;
155
- return [
156
- encodeImpl(k, transferList, keyPath, ancestors, cache),
157
- encodeImpl(v, transferList, valuePath, ancestors, cache),
158
- ];
159
- }),
160
- );
161
- }
162
- // 5. Set 재귀 순회
163
- else if (obj instanceof Set) {
164
- let idx = 0;
165
- result = new Set(
166
- Array.from(obj).map((v) =>
167
- encodeImpl(v, transferList, [...path, `Set[${idx++}]`], ancestors, cache),
168
- ),
169
- );
170
- }
171
- // 6. 일반 객체 재귀 순회
172
- else if (typeof obj === "object") {
173
- const res: Record<string, unknown> = {};
174
- const record = obj as Record<string, unknown>;
175
- for (const key of Object.keys(record)) {
176
- res[key] = encodeImpl(record[key], transferList, [...path, key], ancestors, cache);
177
- }
178
- result = res;
179
- }
180
- // 7. 원시 타입
181
- else {
182
- return obj;
183
- }
184
-
185
- // 캐시 저장 (성공 시에만)
186
- if (typeof obj === "object") {
187
- cache.set(obj, result);
188
- }
189
-
190
- return result;
191
- } finally {
192
- // 재귀 스택에서 제거 (예외 시에도 반드시 실행)
193
- if (typeof obj === "object") {
194
- ancestors.delete(obj);
195
- }
196
- }
197
- }
198
-
199
- //#endregion
200
-
201
- //#region decode
202
-
203
- /**
204
- * serialize 객체를 심플리즘 타입 사용 객체로 변환
205
- * Worker로부터 받은 데이터를 역직렬화
206
- */
207
- export function transferableDecode(obj: unknown): unknown {
208
- if (obj == null) return obj;
209
-
210
- // 1. 특수 타입 복원
211
- if (typeof obj === "object" && "__type__" in obj && "data" in obj) {
212
- const typed = obj as { __type__: string; data: unknown };
213
- const data = typed.data;
214
-
215
- if (typed.__type__ === "Date" && typeof data === "number") return new Date(data);
216
- if (typed.__type__ === "DateTime" && typeof data === "number") return new DateTime(data);
217
- if (typed.__type__ === "DateOnly" && typeof data === "number") return new DateOnly(data);
218
- if (typed.__type__ === "Time" && typeof data === "number") return new Time(data);
219
- if (typed.__type__ === "Uuid" && typeof data === "string") return new Uuid(data);
220
- if (typed.__type__ === "RegExp" && typeof data === "object" && data !== null) {
221
- const regexData = data as { source: string; flags: string };
222
- return new RegExp(regexData.source, regexData.flags);
223
- }
224
- if (typed.__type__ === "Error" && typeof data === "object" && data !== null) {
225
- const errorData = data as {
226
- name: string;
227
- message: string;
228
- stack?: string;
229
- code?: unknown;
230
- cause?: unknown;
231
- detail?: unknown;
232
- };
233
- const err = new Error(errorData.message) as Error & {
234
- code?: unknown;
235
- detail?: unknown;
236
- };
237
-
238
- err.name = errorData.name;
239
- err.stack = errorData.stack;
240
-
241
- if (errorData.code !== undefined) err.code = errorData.code;
242
- if (errorData.cause !== undefined) (err as Error).cause = transferableDecode(errorData.cause);
243
- if (errorData.detail !== undefined) err.detail = transferableDecode(errorData.detail);
244
- return err;
245
- }
246
- }
247
-
248
- // 2. 배열 재귀 (새 배열 생성)
249
- if (Array.isArray(obj)) {
250
- return obj.map((item) => transferableDecode(item));
251
- }
252
-
253
- // 3. Map 재귀
254
- if (obj instanceof Map) {
255
- const newMap = new Map<unknown, unknown>();
256
- for (const [k, v] of obj) {
257
- newMap.set(transferableDecode(k), transferableDecode(v));
258
- }
259
- return newMap;
260
- }
261
-
262
- // 4. Set 재귀
263
- if (obj instanceof Set) {
264
- const newSet = new Set<unknown>();
265
- for (const v of obj) {
266
- newSet.add(transferableDecode(v));
267
- }
268
- return newSet;
269
- }
270
-
271
- // 5. 객체 재귀 (새 객체 생성)
272
- if (typeof obj === "object") {
273
- const record = obj as Record<string, unknown>;
274
- const result: Record<string, unknown> = {};
275
- for (const key of Object.keys(record)) {
276
- result[key] = transferableDecode(record[key]);
277
- }
278
- return result;
279
- }
280
-
281
- return obj;
282
- }
283
-
284
- //#endregion
1
+ import { DateTime } from "../types/date-time";
2
+ import { DateOnly } from "../types/date-only";
3
+ import { Time } from "../types/time";
4
+ import { Uuid } from "../types/uuid";
5
+
6
+ /**
7
+ * Object types that can be transferred between Workers
8
+ *
9
+ * Only ArrayBuffer is used in this code.
10
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects
11
+ */
12
+ type Transferable = ArrayBuffer;
13
+
14
+ /**
15
+ * Transferable conversion utility functions
16
+ *
17
+ * Performs serialization/deserialization for data transfer between Workers.
18
+ * Handles custom types that structuredClone does not support.
19
+ *
20
+ * Supported types:
21
+ * - Date, DateTime, DateOnly, Time, Uuid, RegExp
22
+ * - Error (including cause, code, detail)
23
+ * - Uint8Array (other TypedArrays not supported, handled as plain objects)
24
+ * - Array, Map, Set, plain objects
25
+ *
26
+ * @note Circular references cause TypeError in transferableEncode (includes path info)
27
+ * @note If the same object is referenced from multiple places, the cached encoding result is reused
28
+ *
29
+ * @example
30
+ * // Send data to Worker
31
+ * const { result, transferList } = transferableEncode(data);
32
+ * worker.postMessage(result, transferList);
33
+ *
34
+ * // Receive data from Worker
35
+ * const decoded = transferableDecode(event.data);
36
+ */
37
+
38
+ //#region encode
39
+
40
+ /**
41
+ * Convert objects using Simplysm types to plain objects
42
+ * Serializes in a form that can be sent to a Worker
43
+ *
44
+ * @throws TypeError if circular reference is detected
45
+ */
46
+ export function transferableEncode(obj: unknown): {
47
+ result: unknown;
48
+ transferList: Transferable[];
49
+ } {
50
+ const transferList: Transferable[] = [];
51
+ const ancestors = new Set<object>();
52
+ const cache = new Map<object, unknown>();
53
+ const result = encodeImpl(obj, transferList, [], ancestors, cache);
54
+ return { result, transferList };
55
+ }
56
+
57
+ function encodeImpl(
58
+ obj: unknown,
59
+ transferList: Transferable[],
60
+ path: (string | number)[],
61
+ ancestors: Set<object>,
62
+ cache: Map<object, unknown>,
63
+ ): unknown {
64
+ if (obj == null) return obj;
65
+
66
+ // 객체 타입 processing: 순환 감지 + 캐시
67
+ if (typeof obj === "object") {
68
+ // Circular reference detection (object in current recursion stack)
69
+ if (ancestors.has(obj)) {
70
+ const currentPath = path.length > 0 ? path.join(".") : "root";
71
+ throw new TypeError(`Circular reference detected: ${currentPath}`);
72
+ }
73
+
74
+ // If object was already encoded, reuse cached result
75
+ const cached = cache.get(obj);
76
+ if (cached !== undefined) return cached;
77
+
78
+ // Add to recursion stack
79
+ ancestors.add(obj);
80
+ }
81
+
82
+ let result: unknown;
83
+
84
+ try {
85
+ // 1. Uint8Array
86
+ if (obj instanceof Uint8Array) {
87
+ // SharedArrayBuffer is already shared memory, so don't add to transferList
88
+ // Add only ArrayBuffer to transferList for zero-copy transfer
89
+ const isSharedArrayBuffer =
90
+ typeof SharedArrayBuffer !== "undefined" && obj.buffer instanceof SharedArrayBuffer;
91
+ const buffer = obj.buffer as ArrayBuffer;
92
+ if (!isSharedArrayBuffer && !transferList.includes(buffer)) {
93
+ transferList.push(buffer);
94
+ }
95
+ result = obj;
96
+ }
97
+ // 2. Special type conversion (convert to tagged object without JSON.stringify)
98
+ else if (obj instanceof Date) {
99
+ result = { __type__: "Date", data: obj.getTime() };
100
+ } else if (obj instanceof DateTime) {
101
+ result = { __type__: "DateTime", data: obj.tick };
102
+ } else if (obj instanceof DateOnly) {
103
+ result = { __type__: "DateOnly", data: obj.tick };
104
+ } else if (obj instanceof Time) {
105
+ result = { __type__: "Time", data: obj.tick };
106
+ } else if (obj instanceof Uuid) {
107
+ result = { __type__: "Uuid", data: obj.toString() };
108
+ } else if (obj instanceof RegExp) {
109
+ result = { __type__: "RegExp", data: { source: obj.source, flags: obj.flags } };
110
+ } else if (obj instanceof Error) {
111
+ const errObj = obj as Error & {
112
+ code?: unknown;
113
+ detail?: unknown;
114
+ };
115
+ result = {
116
+ __type__: "Error",
117
+ data: {
118
+ name: errObj.name,
119
+ message: errObj.message,
120
+ stack: errObj.stack,
121
+ ...(errObj.code !== undefined ? { code: errObj.code } : {}),
122
+ ...(errObj.detail !== undefined
123
+ ? {
124
+ detail: encodeImpl(
125
+ errObj.detail,
126
+ transferList,
127
+ [...path, "detail"],
128
+ ancestors,
129
+ cache,
130
+ ),
131
+ }
132
+ : {}),
133
+ ...(errObj.cause !== undefined
134
+ ? {
135
+ cause: encodeImpl(errObj.cause, transferList, [...path, "cause"], ancestors, cache),
136
+ }
137
+ : {}),
138
+ },
139
+ };
140
+ }
141
+ // 3. Array recursion
142
+ else if (Array.isArray(obj)) {
143
+ result = obj.map((item, idx) =>
144
+ encodeImpl(item, transferList, [...path, idx], ancestors, cache),
145
+ );
146
+ }
147
+ // 4. Map recursion
148
+ else if (obj instanceof Map) {
149
+ let idx = 0;
150
+ result = new Map(
151
+ Array.from(obj.entries()).map(([k, v]) => {
152
+ const keyPath = [...path, `Map[${idx}].key`];
153
+ const valuePath = [...path, `Map[${idx}].value`];
154
+ idx++;
155
+ return [
156
+ encodeImpl(k, transferList, keyPath, ancestors, cache),
157
+ encodeImpl(v, transferList, valuePath, ancestors, cache),
158
+ ];
159
+ }),
160
+ );
161
+ }
162
+ // 5. Set recursion
163
+ else if (obj instanceof Set) {
164
+ let idx = 0;
165
+ result = new Set(
166
+ Array.from(obj).map((v) =>
167
+ encodeImpl(v, transferList, [...path, `Set[${idx++}]`], ancestors, cache),
168
+ ),
169
+ );
170
+ }
171
+ // 6. Plain object recursion
172
+ else if (typeof obj === "object") {
173
+ const res: Record<string, unknown> = {};
174
+ const record = obj as Record<string, unknown>;
175
+ for (const key of Object.keys(record)) {
176
+ res[key] = encodeImpl(record[key], transferList, [...path, key], ancestors, cache);
177
+ }
178
+ result = res;
179
+ }
180
+ // 7. Primitive types
181
+ else {
182
+ return obj;
183
+ }
184
+
185
+ // Save to cache (only on success)
186
+ if (typeof obj === "object") {
187
+ cache.set(obj, result);
188
+ }
189
+
190
+ return result;
191
+ } finally {
192
+ // Remove from recursion stack (must execute even on exception)
193
+ if (typeof obj === "object") {
194
+ ancestors.delete(obj);
195
+ }
196
+ }
197
+ }
198
+
199
+ //#endregion
200
+
201
+ //#region decode
202
+
203
+ /**
204
+ * Convert serialized objects to objects using Simplysm types
205
+ * Deserialize data received from a Worker
206
+ */
207
+ export function transferableDecode(obj: unknown): unknown {
208
+ if (obj == null) return obj;
209
+
210
+ // 1. Restore special types from tagged objects
211
+ if (typeof obj === "object" && "__type__" in obj && "data" in obj) {
212
+ const typed = obj as { __type__: string; data: unknown };
213
+ const data = typed.data;
214
+
215
+ if (typed.__type__ === "Date" && typeof data === "number") return new Date(data);
216
+ if (typed.__type__ === "DateTime" && typeof data === "number") return new DateTime(data);
217
+ if (typed.__type__ === "DateOnly" && typeof data === "number") return new DateOnly(data);
218
+ if (typed.__type__ === "Time" && typeof data === "number") return new Time(data);
219
+ if (typed.__type__ === "Uuid" && typeof data === "string") return new Uuid(data);
220
+ if (typed.__type__ === "RegExp" && typeof data === "object" && data !== null) {
221
+ const regexData = data as { source: string; flags: string };
222
+ return new RegExp(regexData.source, regexData.flags);
223
+ }
224
+ if (typed.__type__ === "Error" && typeof data === "object" && data !== null) {
225
+ const errorData = data as {
226
+ name: string;
227
+ message: string;
228
+ stack?: string;
229
+ code?: unknown;
230
+ cause?: unknown;
231
+ detail?: unknown;
232
+ };
233
+ const err = new Error(errorData.message) as Error & {
234
+ code?: unknown;
235
+ detail?: unknown;
236
+ };
237
+
238
+ err.name = errorData.name;
239
+ err.stack = errorData.stack;
240
+
241
+ if (errorData.code !== undefined) err.code = errorData.code;
242
+ if (errorData.cause !== undefined) (err as Error).cause = transferableDecode(errorData.cause);
243
+ if (errorData.detail !== undefined) err.detail = transferableDecode(errorData.detail);
244
+ return err;
245
+ }
246
+ }
247
+
248
+ // 2. Array recursion
249
+ if (Array.isArray(obj)) {
250
+ return obj.map((item) => transferableDecode(item));
251
+ }
252
+
253
+ // 3. Map recursion
254
+ if (obj instanceof Map) {
255
+ const newMap = new Map<unknown, unknown>();
256
+ for (const [k, v] of obj) {
257
+ newMap.set(transferableDecode(k), transferableDecode(v));
258
+ }
259
+ return newMap;
260
+ }
261
+
262
+ // 4. Set recursion
263
+ if (obj instanceof Set) {
264
+ const newSet = new Set<unknown>();
265
+ for (const v of obj) {
266
+ newSet.add(transferableDecode(v));
267
+ }
268
+ return newSet;
269
+ }
270
+
271
+ // 5. Object recursion
272
+ if (typeof obj === "object") {
273
+ const record = obj as Record<string, unknown>;
274
+ const result: Record<string, unknown> = {};
275
+ for (const key of Object.keys(record)) {
276
+ result[key] = transferableDecode(record[key]);
277
+ }
278
+ return result;
279
+ }
280
+
281
+ return obj;
282
+ }
283
+
284
+ //#endregion
package/src/utils/wait.ts CHANGED
@@ -1,19 +1,19 @@
1
1
  /**
2
- * 대기 유틸리티 함수
2
+ * Wait utility functions
3
3
  */
4
4
  import { TimeoutError } from "../errors/timeout-error";
5
5
 
6
6
  /**
7
- * 조건이 참이 때까지 대기
8
- * @param forwarder 조건 함수
9
- * @param milliseconds 체크 간격 (기본: 100ms)
10
- * @param maxCount 최대 시도 횟수 (undefined 무제한)
7
+ * Wait until a condition becomes true
8
+ * @param forwarder Condition function
9
+ * @param milliseconds Check interval (default: 100ms)
10
+ * @param maxCount Maximum number of attempts (undefined for unlimited)
11
11
  *
12
- * @note 조건이 번째 호출에서 true 즉시 반환됩니다.
12
+ * @note Returns immediately if the condition is true on the first call.
13
13
  * @example
14
- * // maxCount=3: 최대 3 조건 확인 모두 false면 TimeoutError
14
+ * // maxCount=3: checks condition up to 3 times, throws TimeoutError if all are false
15
15
  * await waitUntil(() => someCondition, 100, 3);
16
- * @throws TimeoutError 최대 시도 횟수 초과
16
+ * @throws TimeoutError when maximum number of attempts is exceeded
17
17
  */
18
18
  export async function waitUntil(
19
19
  forwarder: () => boolean | Promise<boolean>,
@@ -32,8 +32,8 @@ export async function waitUntil(
32
32
  }
33
33
 
34
34
  /**
35
- * 지정된 시간만큼 대기
36
- * @param millisecond 대기 시간 (ms)
35
+ * Wait for a specified amount of time
36
+ * @param millisecond Wait time (ms)
37
37
  */
38
38
  export async function waitTime(millisecond: number): Promise<void> {
39
39
  return new Promise<void>((resolve) => setTimeout(resolve, millisecond));