@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.
- package/README.md +66 -267
- package/dist/common.types.d.ts +14 -14
- package/dist/errors/argument-error.d.ts +10 -10
- package/dist/errors/argument-error.d.ts.map +1 -1
- package/dist/errors/argument-error.js +2 -2
- package/dist/errors/argument-error.js.map +1 -1
- package/dist/errors/not-implemented-error.d.ts +8 -8
- package/dist/errors/not-implemented-error.js +2 -2
- package/dist/errors/not-implemented-error.js.map +1 -1
- package/dist/errors/sd-error.d.ts +10 -10
- package/dist/errors/sd-error.d.ts.map +1 -1
- package/dist/errors/timeout-error.d.ts +10 -10
- package/dist/errors/timeout-error.js +3 -3
- package/dist/errors/timeout-error.js.map +1 -1
- package/dist/extensions/arr-ext.d.ts +2 -2
- package/dist/extensions/arr-ext.helpers.d.ts +8 -8
- package/dist/extensions/arr-ext.helpers.js +1 -1
- package/dist/extensions/arr-ext.helpers.js.map +1 -1
- package/dist/extensions/arr-ext.js +13 -13
- package/dist/extensions/arr-ext.js.map +1 -1
- package/dist/extensions/arr-ext.types.d.ts +57 -57
- package/dist/extensions/arr-ext.types.d.ts.map +1 -1
- package/dist/extensions/map-ext.d.ts +16 -16
- package/dist/extensions/set-ext.d.ts +11 -11
- package/dist/features/debounce-queue.d.ts +17 -15
- package/dist/features/debounce-queue.d.ts.map +1 -1
- package/dist/features/debounce-queue.js +6 -6
- package/dist/features/debounce-queue.js.map +1 -1
- package/dist/features/event-emitter.d.ts +20 -20
- package/dist/features/event-emitter.js +17 -17
- package/dist/features/serial-queue.d.ts +11 -11
- package/dist/features/serial-queue.js +5 -5
- package/dist/features/serial-queue.js.map +1 -1
- package/dist/globals.d.ts +4 -4
- package/dist/types/date-only.d.ts +64 -64
- package/dist/types/date-only.d.ts.map +1 -1
- package/dist/types/date-only.js +63 -63
- package/dist/types/date-time.d.ts +37 -37
- package/dist/types/date-time.d.ts.map +1 -1
- package/dist/types/date-time.js +54 -37
- package/dist/types/date-time.js.map +1 -1
- package/dist/types/lazy-gc-map.d.ts +26 -26
- package/dist/types/lazy-gc-map.d.ts.map +1 -1
- package/dist/types/lazy-gc-map.js +26 -26
- package/dist/types/lazy-gc-map.js.map +1 -1
- package/dist/types/time.d.ts +25 -25
- package/dist/types/time.d.ts.map +1 -1
- package/dist/types/time.js +25 -25
- package/dist/types/time.js.map +1 -1
- package/dist/types/uuid.d.ts +11 -11
- package/dist/types/uuid.d.ts.map +1 -1
- package/dist/types/uuid.js +12 -12
- package/dist/types/uuid.js.map +1 -1
- package/dist/utils/bytes.d.ts +17 -17
- package/dist/utils/bytes.js +4 -4
- package/dist/utils/bytes.js.map +1 -1
- package/dist/utils/date-format.d.ts +45 -45
- package/dist/utils/date-format.js +1 -1
- package/dist/utils/date-format.js.map +1 -1
- package/dist/utils/error.d.ts +4 -4
- package/dist/utils/json.d.ts +17 -17
- package/dist/utils/json.js +3 -3
- package/dist/utils/json.js.map +1 -1
- package/dist/utils/num.d.ts +23 -23
- package/dist/utils/obj.d.ts +111 -111
- package/dist/utils/obj.d.ts.map +1 -1
- package/dist/utils/obj.js +3 -3
- package/dist/utils/obj.js.map +1 -1
- package/dist/utils/path.d.ts +10 -10
- package/dist/utils/primitive.d.ts +5 -5
- package/dist/utils/primitive.js +1 -1
- package/dist/utils/primitive.js.map +1 -1
- package/dist/utils/str.d.ts +46 -46
- package/dist/utils/str.d.ts.map +1 -1
- package/dist/utils/str.js +5 -5
- package/dist/utils/str.js.map +1 -1
- package/dist/utils/template-strings.d.ts +26 -26
- package/dist/utils/transferable.d.ts +18 -18
- package/dist/utils/transferable.js +1 -1
- package/dist/utils/transferable.js.map +1 -1
- package/dist/utils/wait.d.ts +9 -9
- package/dist/utils/xml.d.ts +13 -13
- package/dist/utils/xml.d.ts.map +1 -1
- package/dist/utils/xml.js +1 -0
- package/dist/utils/xml.js.map +1 -1
- package/dist/zip/sd-zip.d.ts +22 -22
- package/dist/zip/sd-zip.js +16 -16
- package/package.json +4 -4
- package/src/common.types.ts +17 -17
- package/src/errors/argument-error.ts +15 -15
- package/src/errors/not-implemented-error.ts +9 -9
- package/src/errors/sd-error.ts +12 -12
- package/src/errors/timeout-error.ts +12 -12
- package/src/extensions/arr-ext.helpers.ts +10 -10
- package/src/extensions/arr-ext.ts +57 -57
- package/src/extensions/arr-ext.types.ts +59 -59
- package/src/extensions/map-ext.ts +16 -16
- package/src/extensions/set-ext.ts +11 -11
- package/src/features/debounce-queue.ts +21 -19
- package/src/features/event-emitter.ts +25 -25
- package/src/features/serial-queue.ts +13 -13
- package/src/globals.ts +4 -4
- package/src/index.ts +1 -1
- package/src/types/date-only.ts +83 -83
- package/src/types/date-time.ts +64 -44
- package/src/types/lazy-gc-map.ts +45 -45
- package/src/types/time.ts +34 -34
- package/src/types/uuid.ts +17 -17
- package/src/utils/bytes.ts +35 -35
- package/src/utils/date-format.ts +65 -65
- package/src/utils/error.ts +4 -4
- package/src/utils/json.ts +39 -39
- package/src/utils/num.ts +23 -23
- package/src/utils/obj.ts +138 -138
- package/src/utils/path.ts +10 -10
- package/src/utils/primitive.ts +6 -6
- package/src/utils/str.ts +260 -261
- package/src/utils/template-strings.ts +29 -29
- package/src/utils/transferable.ts +284 -284
- package/src/utils/wait.ts +10 -10
- package/src/utils/xml.ts +20 -19
- package/src/zip/sd-zip.ts +25 -25
- package/tests/errors/errors.spec.ts +80 -0
- package/tests/extensions/array-extension.spec.ts +796 -0
- package/tests/extensions/map-extension.spec.ts +147 -0
- package/tests/extensions/set-extension.spec.ts +74 -0
- package/tests/types/date-only.spec.ts +638 -0
- package/tests/types/date-time.spec.ts +391 -0
- package/tests/types/lazy-gc-map.spec.ts +692 -0
- package/tests/types/time.spec.ts +559 -0
- package/tests/types/uuid.spec.ts +74 -0
- package/tests/utils/bytes-utils.spec.ts +230 -0
- package/tests/utils/date-format.spec.ts +373 -0
- package/tests/utils/debounce-queue.spec.ts +272 -0
- package/tests/utils/json.spec.ts +486 -0
- package/tests/utils/number.spec.ts +157 -0
- package/tests/utils/object.spec.ts +829 -0
- package/tests/utils/path.spec.ts +78 -0
- package/tests/utils/primitive.spec.ts +43 -0
- package/tests/utils/sd-event-emitter.spec.ts +216 -0
- package/tests/utils/serial-queue.spec.ts +365 -0
- package/tests/utils/string.spec.ts +281 -0
- package/tests/utils/template-strings.spec.ts +57 -0
- package/tests/utils/transferable.spec.ts +703 -0
- package/tests/utils/wait.spec.ts +145 -0
- package/tests/utils/xml.spec.ts +146 -0
- package/tests/zip/sd-zip.spec.ts +238 -0
- package/docs/extensions.md +0 -503
- package/docs/features.md +0 -109
- package/docs/types.md +0 -486
- 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
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* - Date, DateTime, DateOnly, Time, Uuid, RegExp
|
|
22
|
-
* - Error (cause, code, detail
|
|
23
|
-
* - Uint8Array (
|
|
24
|
-
* - Array, Map, Set,
|
|
25
|
-
*
|
|
26
|
-
* @note
|
|
27
|
-
* @note
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* //
|
|
31
|
-
* const { result, transferList } = transferableEncode(data);
|
|
32
|
-
* worker.postMessage(result, transferList);
|
|
33
|
-
*
|
|
34
|
-
* //
|
|
35
|
-
* const decoded = transferableDecode(event.data);
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
//#region encode
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* @throws
|
|
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(
|
|
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
|
|
88
|
-
// ArrayBuffer
|
|
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.
|
|
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
|
-
*
|
|
205
|
-
*
|
|
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
|
|
10
|
-
* @param maxCount
|
|
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
|
|
12
|
+
* @note Returns immediately if the condition is true on the first call.
|
|
13
13
|
* @example
|
|
14
|
-
* // maxCount=3:
|
|
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
|
|
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));
|