@simplysm/core-node 13.0.0-beta.7 → 13.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/features/fs-watcher.d.ts.map +1 -0
- package/dist/features/fs-watcher.js +3 -3
- package/dist/features/fs-watcher.js.map +1 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -6
- package/dist/index.js.map +0 -1
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js.map +0 -1
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js.map +0 -1
- package/dist/worker/create-worker.d.ts.map +1 -0
- package/dist/worker/create-worker.js.map +0 -1
- package/dist/worker/types.d.ts.map +1 -0
- package/dist/worker/types.js.map +0 -1
- package/dist/worker/worker.d.ts.map +1 -0
- package/dist/worker/worker.js +4 -3
- package/dist/worker/worker.js.map +1 -2
- package/package.json +6 -5
- package/src/features/fs-watcher.ts +176 -0
- package/src/index.ts +11 -0
- package/src/utils/fs.ts +550 -0
- package/src/utils/path.ts +128 -0
- package/src/worker/create-worker.ts +141 -0
- package/src/worker/types.ts +86 -0
- package/src/worker/worker.ts +209 -0
- package/dist/core-common/src/common.types.d.ts +0 -74
- package/dist/core-common/src/common.types.d.ts.map +0 -1
- package/dist/core-common/src/env.d.ts +0 -6
- package/dist/core-common/src/env.d.ts.map +0 -1
- package/dist/core-common/src/errors/argument-error.d.ts +0 -25
- package/dist/core-common/src/errors/argument-error.d.ts.map +0 -1
- package/dist/core-common/src/errors/not-implemented-error.d.ts +0 -29
- package/dist/core-common/src/errors/not-implemented-error.d.ts.map +0 -1
- package/dist/core-common/src/errors/sd-error.d.ts +0 -27
- package/dist/core-common/src/errors/sd-error.d.ts.map +0 -1
- package/dist/core-common/src/errors/timeout-error.d.ts +0 -31
- package/dist/core-common/src/errors/timeout-error.d.ts.map +0 -1
- package/dist/core-common/src/extensions/arr-ext.d.ts +0 -15
- package/dist/core-common/src/extensions/arr-ext.d.ts.map +0 -1
- package/dist/core-common/src/extensions/arr-ext.helpers.d.ts +0 -19
- package/dist/core-common/src/extensions/arr-ext.helpers.d.ts.map +0 -1
- package/dist/core-common/src/extensions/arr-ext.types.d.ts +0 -215
- package/dist/core-common/src/extensions/arr-ext.types.d.ts.map +0 -1
- package/dist/core-common/src/extensions/map-ext.d.ts +0 -57
- package/dist/core-common/src/extensions/map-ext.d.ts.map +0 -1
- package/dist/core-common/src/extensions/set-ext.d.ts +0 -36
- package/dist/core-common/src/extensions/set-ext.d.ts.map +0 -1
- package/dist/core-common/src/features/debounce-queue.d.ts +0 -53
- package/dist/core-common/src/features/debounce-queue.d.ts.map +0 -1
- package/dist/core-common/src/features/event-emitter.d.ts +0 -66
- package/dist/core-common/src/features/event-emitter.d.ts.map +0 -1
- package/dist/core-common/src/features/serial-queue.d.ts +0 -47
- package/dist/core-common/src/features/serial-queue.d.ts.map +0 -1
- package/dist/core-common/src/index.d.ts +0 -32
- package/dist/core-common/src/index.d.ts.map +0 -1
- package/dist/core-common/src/types/date-only.d.ts +0 -152
- package/dist/core-common/src/types/date-only.d.ts.map +0 -1
- package/dist/core-common/src/types/date-time.d.ts +0 -96
- package/dist/core-common/src/types/date-time.d.ts.map +0 -1
- package/dist/core-common/src/types/lazy-gc-map.d.ts +0 -80
- package/dist/core-common/src/types/lazy-gc-map.d.ts.map +0 -1
- package/dist/core-common/src/types/time.d.ts +0 -68
- package/dist/core-common/src/types/time.d.ts.map +0 -1
- package/dist/core-common/src/types/uuid.d.ts +0 -35
- package/dist/core-common/src/types/uuid.d.ts.map +0 -1
- package/dist/core-common/src/utils/bytes.d.ts +0 -51
- package/dist/core-common/src/utils/bytes.d.ts.map +0 -1
- package/dist/core-common/src/utils/date-format.d.ts +0 -90
- package/dist/core-common/src/utils/date-format.d.ts.map +0 -1
- package/dist/core-common/src/utils/json.d.ts +0 -34
- package/dist/core-common/src/utils/json.d.ts.map +0 -1
- package/dist/core-common/src/utils/num.d.ts +0 -60
- package/dist/core-common/src/utils/num.d.ts.map +0 -1
- package/dist/core-common/src/utils/obj.d.ts +0 -258
- package/dist/core-common/src/utils/obj.d.ts.map +0 -1
- package/dist/core-common/src/utils/path.d.ts +0 -23
- package/dist/core-common/src/utils/path.d.ts.map +0 -1
- package/dist/core-common/src/utils/primitive.d.ts +0 -18
- package/dist/core-common/src/utils/primitive.d.ts.map +0 -1
- package/dist/core-common/src/utils/str.d.ts +0 -103
- package/dist/core-common/src/utils/str.d.ts.map +0 -1
- package/dist/core-common/src/utils/template-strings.d.ts +0 -84
- package/dist/core-common/src/utils/template-strings.d.ts.map +0 -1
- package/dist/core-common/src/utils/transferable.d.ts +0 -47
- package/dist/core-common/src/utils/transferable.d.ts.map +0 -1
- package/dist/core-common/src/utils/wait.d.ts +0 -19
- package/dist/core-common/src/utils/wait.d.ts.map +0 -1
- package/dist/core-common/src/utils/xml.d.ts +0 -36
- package/dist/core-common/src/utils/xml.d.ts.map +0 -1
- package/dist/core-common/src/zip/sd-zip.d.ts +0 -80
- package/dist/core-common/src/zip/sd-zip.d.ts.map +0 -1
- package/dist/core-node/src/features/fs-watcher.d.ts.map +0 -1
- package/dist/core-node/src/index.d.ts.map +0 -1
- package/dist/core-node/src/utils/fs.d.ts.map +0 -1
- package/dist/core-node/src/utils/path.d.ts.map +0 -1
- package/dist/core-node/src/worker/create-worker.d.ts.map +0 -1
- package/dist/core-node/src/worker/types.d.ts.map +0 -1
- package/dist/core-node/src/worker/worker.d.ts.map +0 -1
- /package/dist/{core-node/src/features → features}/fs-watcher.d.ts +0 -0
- /package/dist/{core-node/src/index.d.ts → index.d.ts} +0 -0
- /package/dist/{core-node/src/utils → utils}/fs.d.ts +0 -0
- /package/dist/{core-node/src/utils → utils}/path.d.ts +0 -0
- /package/dist/{core-node/src/worker → worker}/create-worker.d.ts +0 -0
- /package/dist/{core-node/src/worker → worker}/types.d.ts +0 -0
- /package/dist/{core-node/src/worker → worker}/worker.d.ts +0 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { parentPort } from "worker_threads";
|
|
2
|
+
import { SdError, transferableDecode, transferableEncode } from "@simplysm/core-common";
|
|
3
|
+
import type { WorkerRequest, WorkerResponse } from "./types";
|
|
4
|
+
|
|
5
|
+
//#region createSdWorker
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 워커 스레드에서 사용할 워커 팩토리.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // 이벤트 없는 워커
|
|
12
|
+
* export default createWorker({
|
|
13
|
+
* add: (a: number, b: number) => a + b,
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* // 이벤트 있는 워커
|
|
17
|
+
* interface MyEvents { progress: number; }
|
|
18
|
+
* const methods = {
|
|
19
|
+
* calc: (x: number) => { sender.send("progress", 50); return x * 2; },
|
|
20
|
+
* };
|
|
21
|
+
* const sender = createWorker<typeof methods, MyEvents>(methods);
|
|
22
|
+
* export default sender;
|
|
23
|
+
*/
|
|
24
|
+
export function createWorker<
|
|
25
|
+
TMethods extends Record<string, (...args: any[]) => unknown>,
|
|
26
|
+
TEvents extends Record<string, unknown> = Record<string, never>,
|
|
27
|
+
>(
|
|
28
|
+
methods: TMethods,
|
|
29
|
+
): {
|
|
30
|
+
send<K extends keyof TEvents & string>(event: K, data?: TEvents[K]): void;
|
|
31
|
+
__methods: TMethods;
|
|
32
|
+
__events: TEvents;
|
|
33
|
+
} {
|
|
34
|
+
if (parentPort === null) {
|
|
35
|
+
throw new SdError("이 스크립트는 워커 스레드에서 실행되어야 합니다 (parentPort 필요).");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const port = parentPort;
|
|
39
|
+
|
|
40
|
+
// Worker 스레드의 stdout은 메인 스레드로 자동 전달되지 않음
|
|
41
|
+
// stdout.write를 가로채서 메시지 프로토콜을 통해 메인 스레드로 전달
|
|
42
|
+
process.stdout.write = (
|
|
43
|
+
chunk: string | Uint8Array,
|
|
44
|
+
encodingOrCallback?: BufferEncoding | ((err?: Error) => void),
|
|
45
|
+
callback?: (err?: Error) => void,
|
|
46
|
+
): boolean => {
|
|
47
|
+
const body = typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk);
|
|
48
|
+
const response: WorkerResponse = { type: "log", body };
|
|
49
|
+
const serialized = transferableEncode(response);
|
|
50
|
+
port.postMessage(serialized.result, serialized.transferList);
|
|
51
|
+
|
|
52
|
+
const cb = typeof encodingOrCallback === "function" ? encodingOrCallback : callback;
|
|
53
|
+
if (cb) {
|
|
54
|
+
queueMicrotask(() => cb());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return true;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
port.on("message", async (serializedRequest: unknown) => {
|
|
61
|
+
const decoded = transferableDecode(serializedRequest);
|
|
62
|
+
|
|
63
|
+
// 요청 구조 검증
|
|
64
|
+
if (
|
|
65
|
+
decoded == null ||
|
|
66
|
+
typeof decoded !== "object" ||
|
|
67
|
+
!("id" in decoded) ||
|
|
68
|
+
!("method" in decoded) ||
|
|
69
|
+
!("params" in decoded)
|
|
70
|
+
) {
|
|
71
|
+
let decodedStr: string;
|
|
72
|
+
try {
|
|
73
|
+
decodedStr = JSON.stringify(decoded);
|
|
74
|
+
} catch {
|
|
75
|
+
decodedStr = String(decoded);
|
|
76
|
+
}
|
|
77
|
+
const errorResponse: WorkerResponse = {
|
|
78
|
+
type: "error",
|
|
79
|
+
request: { id: "unknown", method: "unknown", params: [] },
|
|
80
|
+
body: new SdError(`형식이 잘못된 워커 요청: ${decodedStr}`),
|
|
81
|
+
};
|
|
82
|
+
const serialized = transferableEncode(errorResponse);
|
|
83
|
+
port.postMessage(serialized.result, serialized.transferList);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const request = decoded as WorkerRequest;
|
|
87
|
+
|
|
88
|
+
const methodFn = methods[request.method] as ((...args: unknown[]) => unknown) | undefined;
|
|
89
|
+
|
|
90
|
+
if (methodFn == null) {
|
|
91
|
+
const response: WorkerResponse = {
|
|
92
|
+
request,
|
|
93
|
+
type: "error",
|
|
94
|
+
body: new SdError(`알 수 없는 메서드: ${request.method}`),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const serialized = transferableEncode(response);
|
|
98
|
+
port.postMessage(serialized.result, serialized.transferList);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const result = await methodFn(...request.params);
|
|
104
|
+
|
|
105
|
+
const response: WorkerResponse = {
|
|
106
|
+
request,
|
|
107
|
+
type: "return",
|
|
108
|
+
body: result,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const serialized = transferableEncode(response);
|
|
112
|
+
port.postMessage(serialized.result, serialized.transferList);
|
|
113
|
+
} catch (err) {
|
|
114
|
+
const response: WorkerResponse = {
|
|
115
|
+
request,
|
|
116
|
+
type: "error",
|
|
117
|
+
body: err instanceof Error ? err : new Error(String(err)),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const serialized = transferableEncode(response);
|
|
121
|
+
port.postMessage(serialized.result, serialized.transferList);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
__methods: methods,
|
|
127
|
+
__events: {} as TEvents,
|
|
128
|
+
send<K extends keyof TEvents & string>(event: K, data?: TEvents[K]) {
|
|
129
|
+
const response: WorkerResponse = {
|
|
130
|
+
type: "event",
|
|
131
|
+
event,
|
|
132
|
+
body: data,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const serialized = transferableEncode(response);
|
|
136
|
+
port.postMessage(serialized.result, serialized.transferList);
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
//#endregion
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
//#region Types
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `createWorker()`가 반환하는 워커 모듈의 타입 구조.
|
|
5
|
+
* `Worker.create<typeof import("./worker")>()`에서 타입 추론에 사용된다.
|
|
6
|
+
*
|
|
7
|
+
* @see createWorker - 워커 모듈 생성
|
|
8
|
+
* @see Worker.create - 워커 프록시 생성
|
|
9
|
+
*/
|
|
10
|
+
export interface WorkerModule {
|
|
11
|
+
default: {
|
|
12
|
+
__methods: Record<string, (...args: any[]) => unknown>;
|
|
13
|
+
__events: Record<string, unknown>;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 메서드 타입의 반환값을 Promise로 래핑하는 매핑 타입.
|
|
19
|
+
* 워커 메서드는 postMessage 기반으로 동작하여 항상 비동기이므로,
|
|
20
|
+
* 동기 메서드 타입도 `Promise<Awaited<R>>`로 변환한다.
|
|
21
|
+
*/
|
|
22
|
+
export type PromisifyMethods<T> = {
|
|
23
|
+
[K in keyof T]: T[K] extends (...args: infer P) => infer R ? (...args: P) => Promise<Awaited<R>> : never;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* SdWorker.create()가 반환하는 Proxy 타입.
|
|
28
|
+
* Promisified 메서드들 + on() + terminate() 제공.
|
|
29
|
+
*/
|
|
30
|
+
export type WorkerProxy<TModule extends WorkerModule> = PromisifyMethods<TModule["default"]["__methods"]> & {
|
|
31
|
+
/**
|
|
32
|
+
* 워커 이벤트 리스너 등록.
|
|
33
|
+
*/
|
|
34
|
+
on<K extends keyof TModule["default"]["__events"] & string>(
|
|
35
|
+
event: K,
|
|
36
|
+
listener: (data: TModule["default"]["__events"][K]) => void,
|
|
37
|
+
): void;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 워커 이벤트 리스너 제거.
|
|
41
|
+
*/
|
|
42
|
+
off<K extends keyof TModule["default"]["__events"] & string>(
|
|
43
|
+
event: K,
|
|
44
|
+
listener: (data: TModule["default"]["__events"][K]) => void,
|
|
45
|
+
): void;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 워커 종료.
|
|
49
|
+
*/
|
|
50
|
+
terminate(): Promise<void>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Worker 내부 요청 메시지.
|
|
55
|
+
*/
|
|
56
|
+
export interface WorkerRequest {
|
|
57
|
+
id: string;
|
|
58
|
+
method: string;
|
|
59
|
+
params: unknown[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Worker 내부 응답 메시지.
|
|
64
|
+
*/
|
|
65
|
+
export type WorkerResponse =
|
|
66
|
+
| {
|
|
67
|
+
request: WorkerRequest;
|
|
68
|
+
type: "return";
|
|
69
|
+
body?: unknown;
|
|
70
|
+
}
|
|
71
|
+
| {
|
|
72
|
+
request: WorkerRequest;
|
|
73
|
+
type: "error";
|
|
74
|
+
body: Error;
|
|
75
|
+
}
|
|
76
|
+
| {
|
|
77
|
+
type: "event";
|
|
78
|
+
event: string;
|
|
79
|
+
body?: unknown;
|
|
80
|
+
}
|
|
81
|
+
| {
|
|
82
|
+
type: "log";
|
|
83
|
+
body: string;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { EventEmitter, transferableDecode, transferableEncode, Uuid } from "@simplysm/core-common";
|
|
2
|
+
import consola from "consola";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import type { WorkerOptions as WorkerRawOptions } from "worker_threads";
|
|
6
|
+
import { Worker as WorkerRaw } from "worker_threads";
|
|
7
|
+
import type { WorkerModule, WorkerProxy, WorkerRequest, WorkerResponse } from "./types";
|
|
8
|
+
|
|
9
|
+
const logger = consola.withTag("sd-worker");
|
|
10
|
+
|
|
11
|
+
//#region WorkerInternal
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Worker 내부 구현 클래스.
|
|
15
|
+
* Proxy를 통해 외부에 노출됨.
|
|
16
|
+
*
|
|
17
|
+
* 개발 환경(.ts)에서는 tsx를 통해 TypeScript 워커 파일을 실행하고,
|
|
18
|
+
* 프로덕션 환경(.js)에서는 직접 Worker를 생성한다.
|
|
19
|
+
*/
|
|
20
|
+
class WorkerInternal extends EventEmitter<Record<string, unknown>> {
|
|
21
|
+
private readonly _worker: WorkerRaw;
|
|
22
|
+
private _isTerminated = false;
|
|
23
|
+
private readonly _pendingRequests = new Map<
|
|
24
|
+
string,
|
|
25
|
+
{ method: string; resolve: (value: unknown) => void; reject: (err: Error) => void }
|
|
26
|
+
>();
|
|
27
|
+
|
|
28
|
+
constructor(filePath: string, opt?: Omit<WorkerRawOptions, "stdout" | "stderr">) {
|
|
29
|
+
super();
|
|
30
|
+
|
|
31
|
+
const ext = path.extname(import.meta.filename);
|
|
32
|
+
|
|
33
|
+
// 타입 가드를 통한 env 객체 추출
|
|
34
|
+
const envObj = opt?.env != null && typeof opt.env === "object" ? opt.env : {};
|
|
35
|
+
|
|
36
|
+
// 개발 환경 (.ts 파일)인 경우 tsx를 통해 실행
|
|
37
|
+
// worker-dev-proxy.js: tsx로 TypeScript 워커 파일을 동적으로 로드하는 프록시
|
|
38
|
+
if (ext === ".ts") {
|
|
39
|
+
// file:// URL인 경우 절대 경로로 변환 (worker-dev-proxy.js에서 다시 pathToFileURL 적용)
|
|
40
|
+
const workerPath = filePath.startsWith("file://") ? fileURLToPath(filePath) : filePath;
|
|
41
|
+
this._worker = new WorkerRaw(path.resolve(import.meta.dirname, "../../lib/worker-dev-proxy.js"), {
|
|
42
|
+
stdout: true,
|
|
43
|
+
stderr: true,
|
|
44
|
+
...opt,
|
|
45
|
+
env: {
|
|
46
|
+
...process.env,
|
|
47
|
+
...envObj,
|
|
48
|
+
},
|
|
49
|
+
argv: [workerPath, ...(opt?.argv ?? [])],
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
// 프로덕션 환경 (.js 파일)
|
|
53
|
+
// file:// URL인 경우 변환, 이미 절대 경로인 경우 그대로 사용
|
|
54
|
+
const workerPath = filePath.startsWith("file://") ? fileURLToPath(filePath) : filePath;
|
|
55
|
+
this._worker = new WorkerRaw(workerPath, {
|
|
56
|
+
stdout: true,
|
|
57
|
+
stderr: true,
|
|
58
|
+
...opt,
|
|
59
|
+
env: {
|
|
60
|
+
...process.env,
|
|
61
|
+
...envObj,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 워커의 stdout/stderr를 메인에 출력
|
|
67
|
+
this._worker.stdout.pipe(process.stdout);
|
|
68
|
+
this._worker.stderr.pipe(process.stderr);
|
|
69
|
+
|
|
70
|
+
this._worker.on("exit", (code) => {
|
|
71
|
+
if (!this._isTerminated && code !== 0) {
|
|
72
|
+
logger.error(`워커가 오류와 함께 닫힘 (code: ${code})`);
|
|
73
|
+
// 비정상 종료 시 대기 중인 모든 요청 reject
|
|
74
|
+
this._rejectAllPending(new Error(`워커가 비정상 종료됨 (code: ${code})`));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this._worker.on("error", (err) => {
|
|
79
|
+
logger.error("워커 오류:", err);
|
|
80
|
+
// 워커 에러 시 대기 중인 모든 요청 reject
|
|
81
|
+
this._rejectAllPending(err);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
this._worker.on("message", (serializedResponse: unknown) => {
|
|
85
|
+
const decoded = transferableDecode(serializedResponse);
|
|
86
|
+
|
|
87
|
+
// 응답 구조 검증
|
|
88
|
+
if (decoded == null || typeof decoded !== "object" || !("type" in decoded)) {
|
|
89
|
+
logger.warn("워커에서 잘못된 형식의 응답:", decoded);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const response = decoded as WorkerResponse;
|
|
93
|
+
|
|
94
|
+
if (response.type === "event") {
|
|
95
|
+
this.emit(response.event, response.body);
|
|
96
|
+
} else if (response.type === "log") {
|
|
97
|
+
process.stdout.write(response.body);
|
|
98
|
+
} else if (response.type === "return") {
|
|
99
|
+
const pending = this._pendingRequests.get(response.request.id);
|
|
100
|
+
if (pending) {
|
|
101
|
+
this._pendingRequests.delete(response.request.id);
|
|
102
|
+
pending.resolve(response.body);
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
// response.type === "error"
|
|
106
|
+
const pending = this._pendingRequests.get(response.request.id);
|
|
107
|
+
if (pending) {
|
|
108
|
+
this._pendingRequests.delete(response.request.id);
|
|
109
|
+
pending.reject(response.body);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 대기 중인 모든 요청을 reject합니다.
|
|
117
|
+
*/
|
|
118
|
+
private _rejectAllPending(err: Error): void {
|
|
119
|
+
for (const [_id, { method, reject }] of this._pendingRequests) {
|
|
120
|
+
reject(new Error(`${err.message} (method: ${method})`));
|
|
121
|
+
}
|
|
122
|
+
this._pendingRequests.clear();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 워커 메서드 호출.
|
|
127
|
+
*/
|
|
128
|
+
call(method: string, params: unknown[]): Promise<unknown> {
|
|
129
|
+
return new Promise((resolve, reject) => {
|
|
130
|
+
const request: WorkerRequest = {
|
|
131
|
+
id: Uuid.new().toString(),
|
|
132
|
+
method,
|
|
133
|
+
params,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
this._pendingRequests.set(request.id, { method, resolve, reject });
|
|
137
|
+
|
|
138
|
+
const serialized = transferableEncode(request);
|
|
139
|
+
this._worker.postMessage(serialized.result, serialized.transferList);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 워커 종료.
|
|
145
|
+
*/
|
|
146
|
+
async terminate(): Promise<void> {
|
|
147
|
+
this._isTerminated = true;
|
|
148
|
+
this._rejectAllPending(new Error("워커가 종료됨"));
|
|
149
|
+
await this._worker.terminate();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//#endregion
|
|
154
|
+
|
|
155
|
+
//#region Worker
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* 타입 안전한 Worker 래퍼.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* // worker.ts
|
|
162
|
+
* export default createWorker({
|
|
163
|
+
* add: (a: number, b: number) => a + b,
|
|
164
|
+
* });
|
|
165
|
+
*
|
|
166
|
+
* // main.ts
|
|
167
|
+
* const worker = Worker.create<typeof import("./worker")>("./worker.ts");
|
|
168
|
+
* const result = await worker.add(10, 20); // 30
|
|
169
|
+
* await worker.terminate();
|
|
170
|
+
*/
|
|
171
|
+
export const Worker = {
|
|
172
|
+
/**
|
|
173
|
+
* 타입 안전한 Worker Proxy 생성.
|
|
174
|
+
*
|
|
175
|
+
* @param filePath - 워커 파일 경로 (file:// URL 또는 절대 경로)
|
|
176
|
+
* @param opt - Worker 옵션
|
|
177
|
+
* @returns Proxy 객체 (메서드 직접 호출, on(), terminate() 지원)
|
|
178
|
+
*/
|
|
179
|
+
create<TModule extends WorkerModule>(
|
|
180
|
+
filePath: string,
|
|
181
|
+
opt?: Omit<WorkerRawOptions, "stdout" | "stderr">,
|
|
182
|
+
): WorkerProxy<TModule> {
|
|
183
|
+
const internal = new WorkerInternal(filePath, opt);
|
|
184
|
+
|
|
185
|
+
return new Proxy({} as WorkerProxy<TModule>, {
|
|
186
|
+
get(_target, prop: string) {
|
|
187
|
+
// 예약된 메서드: on, off, terminate
|
|
188
|
+
if (prop === "on") {
|
|
189
|
+
return (event: string, listener: (data: unknown) => void) => {
|
|
190
|
+
internal.on(event, listener);
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if (prop === "off") {
|
|
194
|
+
return (event: string, listener: (data: unknown) => void) => {
|
|
195
|
+
internal.off(event, listener);
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (prop === "terminate") {
|
|
199
|
+
return () => internal.terminate();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// 그 외는 워커 메서드로 처리
|
|
203
|
+
return (...args: unknown[]) => internal.call(prop, args);
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
//#endregion
|
|
@@ -1,74 +0,0 @@
|
|
|
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
|
-
* Buffer 대신 사용하는 바이너리 타입
|
|
7
|
-
*/
|
|
8
|
-
export type Bytes = Uint8Array;
|
|
9
|
-
/**
|
|
10
|
-
* Primitive 타입 매핑
|
|
11
|
-
* orm-common과 공유
|
|
12
|
-
*/
|
|
13
|
-
export type PrimitiveTypeMap = {
|
|
14
|
-
string: string;
|
|
15
|
-
number: number;
|
|
16
|
-
boolean: boolean;
|
|
17
|
-
DateTime: DateTime;
|
|
18
|
-
DateOnly: DateOnly;
|
|
19
|
-
Time: Time;
|
|
20
|
-
Uuid: Uuid;
|
|
21
|
-
Bytes: Bytes;
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* Primitive 타입 문자열 키
|
|
25
|
-
*/
|
|
26
|
-
export type PrimitiveTypeStr = keyof PrimitiveTypeMap;
|
|
27
|
-
/**
|
|
28
|
-
* Primitive 타입 유니온
|
|
29
|
-
*/
|
|
30
|
-
export type PrimitiveType = PrimitiveTypeMap[PrimitiveTypeStr] | undefined;
|
|
31
|
-
/**
|
|
32
|
-
* 깊은 Partial 타입
|
|
33
|
-
*
|
|
34
|
-
* 객체의 모든 속성을 재귀적으로 선택적(optional)으로 만듭니다.
|
|
35
|
-
* Primitive 타입(string, number, boolean 등)은 그대로 유지하고,
|
|
36
|
-
* 객체/배열 타입만 재귀적으로 Partial을 적용합니다.
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```typescript
|
|
40
|
-
* interface User {
|
|
41
|
-
* name: string;
|
|
42
|
-
* profile: {
|
|
43
|
-
* age: number;
|
|
44
|
-
* address: { city: string };
|
|
45
|
-
* };
|
|
46
|
-
* }
|
|
47
|
-
*
|
|
48
|
-
* // 모든 깊이의 속성이 선택적이 됨
|
|
49
|
-
* const partial: DeepPartial<User> = {
|
|
50
|
-
* profile: { address: {} }
|
|
51
|
-
* };
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
export type DeepPartial<T> = Partial<{
|
|
55
|
-
[K in keyof T]: T[K] extends PrimitiveType ? T[K] : DeepPartial<T[K]>;
|
|
56
|
-
}>;
|
|
57
|
-
/**
|
|
58
|
-
* 생성자 타입
|
|
59
|
-
*
|
|
60
|
-
* 클래스 생성자를 타입으로 표현할 때 사용합니다.
|
|
61
|
-
* 주로 의존성 주입, 팩토리 패턴, instanceof 체크 등에서 활용됩니다.
|
|
62
|
-
*
|
|
63
|
-
* @example
|
|
64
|
-
* function create<T>(ctor: Type<T>): T {
|
|
65
|
-
* return new ctor();
|
|
66
|
-
* }
|
|
67
|
-
*
|
|
68
|
-
* class MyClass { name = "test"; }
|
|
69
|
-
* const instance = create(MyClass); // MyClass 인스턴스
|
|
70
|
-
*/
|
|
71
|
-
export interface Type<T> extends Function {
|
|
72
|
-
new (...args: unknown[]): T;
|
|
73
|
-
}
|
|
74
|
-
//# sourceMappingURL=common.types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"common.types.d.ts","sourceRoot":"","sources":["../../../../core-common/src/common.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAIpC;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG,UAAU,CAAC;AAM/B;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,gBAAgB,CAAC;AAEtD;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,gBAAgB,CAAC,gBAAgB,CAAC,GAAG,SAAS,CAAC;AAM3E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,OAAO,CAAC;KAClC,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACtE,CAAC,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,IAAI,CAAC,CAAC,CAAE,SAAQ,QAAQ;IACvC,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;CAC7B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../../core-common/src/env.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,GAAG,EAAE;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAKxB,CAAC"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { SdError } from "./sd-error";
|
|
2
|
-
/**
|
|
3
|
-
* 인수 오류
|
|
4
|
-
*
|
|
5
|
-
* 잘못된 인수를 받았을 때 발생시키는 에러이다.
|
|
6
|
-
* 인수 객체를 YAML 형식으로 메시지에 포함하여 디버깅을 용이하게 한다.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* // 인수 객체만 전달
|
|
10
|
-
* throw new ArgumentError({ userId: 123, name: null });
|
|
11
|
-
* // 결과 메시지: "인수가 잘못되었습니다.\n\nuserId: 123\nname: null"
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* // 커스텀 메시지와 인수 객체 전달
|
|
15
|
-
* throw new ArgumentError("유효하지 않은 사용자", { userId: 123 });
|
|
16
|
-
* // 결과 메시지: "유효하지 않은 사용자\n\nuserId: 123"
|
|
17
|
-
*/
|
|
18
|
-
export declare class ArgumentError extends SdError {
|
|
19
|
-
/** 기본 메시지("인수가 잘못되었습니다.")와 함께 인수 객체를 YAML 형식으로 출력 */
|
|
20
|
-
constructor(argObj: Record<string, unknown>);
|
|
21
|
-
/** 커스텀 메시지와 함께 인수 객체를 YAML 형식으로 출력 */
|
|
22
|
-
constructor(message: string, argObj: Record<string, unknown>);
|
|
23
|
-
constructor(arg1: Record<string, unknown> | string, arg2?: Record<string, unknown>);
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=argument-error.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"argument-error.d.ts","sourceRoot":"","sources":["../../../../../core-common/src/errors/argument-error.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,aAAc,SAAQ,OAAO;IACxC,qDAAqD;gBACzC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC3C,sCAAsC;gBAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;gBAChD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAYnF"}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { SdError } from "./sd-error";
|
|
2
|
-
/**
|
|
3
|
-
* 미구현 오류
|
|
4
|
-
*
|
|
5
|
-
* 아직 구현되지 않은 기능을 호출했을 때 발생시키는 에러이다.
|
|
6
|
-
* 추상 메서드 스텁, 향후 구현 예정인 분기 등에 사용한다.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* // 추상 메서드 구현 전
|
|
10
|
-
* class BaseService {
|
|
11
|
-
* process(): void {
|
|
12
|
-
* throw new NotImplementedError("서브클래스에서 구현 필요");
|
|
13
|
-
* }
|
|
14
|
-
* }
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* // 향후 구현 예정 분기
|
|
18
|
-
* switch (type) {
|
|
19
|
-
* case "A": return handleA();
|
|
20
|
-
* case "B": throw new NotImplementedError(`타입 ${type} 처리`);
|
|
21
|
-
* }
|
|
22
|
-
*/
|
|
23
|
-
export declare class NotImplementedError extends SdError {
|
|
24
|
-
/**
|
|
25
|
-
* @param message 추가 설명 메시지
|
|
26
|
-
*/
|
|
27
|
-
constructor(message?: string);
|
|
28
|
-
}
|
|
29
|
-
//# sourceMappingURL=not-implemented-error.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"not-implemented-error.d.ts","sourceRoot":"","sources":["../../../../../core-common/src/errors/not-implemented-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,mBAAoB,SAAQ,OAAO;IAC9C;;OAEG;gBACS,OAAO,CAAC,EAAE,MAAM;CAI7B"}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 오류의 Tree 구조 구성이 가능한 오류 클래스
|
|
3
|
-
* ES2024 cause 속성 활용
|
|
4
|
-
*
|
|
5
|
-
* @example
|
|
6
|
-
* // 원인 에러를 감싸서 생성
|
|
7
|
-
* try {
|
|
8
|
-
* await fetch(url);
|
|
9
|
-
* } catch (err) {
|
|
10
|
-
* throw new SdError(err, "API 호출 실패", "사용자 로드 실패");
|
|
11
|
-
* }
|
|
12
|
-
* // 결과 메시지: "사용자 로드 실패 => API 호출 실패 => 원본 에러 메시지"
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* // 메시지만으로 생성
|
|
16
|
-
* throw new SdError("잘못된 상태", "처리 불가");
|
|
17
|
-
* // 결과 메시지: "처리 불가 => 잘못된 상태"
|
|
18
|
-
*/
|
|
19
|
-
export declare class SdError extends Error {
|
|
20
|
-
cause?: Error;
|
|
21
|
-
/** 원인 에러를 감싸서 생성. 메시지는 역순으로 연결됨 (상위 메시지 => 하위 메시지 => 원인 메시지) */
|
|
22
|
-
constructor(cause: Error, ...messages: string[]);
|
|
23
|
-
/** 메시지만으로 생성. 메시지는 역순으로 연결됨 (상위 메시지 => 하위 메시지) */
|
|
24
|
-
constructor(...messages: string[]);
|
|
25
|
-
constructor(arg1?: unknown, ...messages: string[]);
|
|
26
|
-
}
|
|
27
|
-
//# sourceMappingURL=sd-error.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sd-error.d.ts","sourceRoot":"","sources":["../../../../../core-common/src/errors/sd-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,OAAQ,SAAQ,KAAK;IACvB,KAAK,CAAC,EAAE,KAAK,CAAC;IAEvB,gEAAgE;gBACpD,KAAK,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE;IAC/C,kDAAkD;gBACtC,GAAG,QAAQ,EAAE,MAAM,EAAE;gBACrB,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE;CA2BlD"}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { SdError } from "./sd-error";
|
|
2
|
-
/**
|
|
3
|
-
* 타임아웃 오류
|
|
4
|
-
*
|
|
5
|
-
* 대기 시간이 초과되었을 때 발생하는 에러이다.
|
|
6
|
-
* Wait.until() 등의 비동기 대기 함수에서 최대 시도 횟수를 초과하면 자동으로 발생한다.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* // Wait.until에서 자동 발생
|
|
10
|
-
* try {
|
|
11
|
-
* await Wait.until(() => isReady, 100, 50); // 100ms 간격, 최대 50회
|
|
12
|
-
* } catch (err) {
|
|
13
|
-
* if (err instanceof TimeoutError) {
|
|
14
|
-
* console.log("시간 초과");
|
|
15
|
-
* }
|
|
16
|
-
* }
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* // 직접 발생
|
|
20
|
-
* if (elapsed > maxTime) {
|
|
21
|
-
* throw new TimeoutError(undefined, "API 응답 대기 초과");
|
|
22
|
-
* }
|
|
23
|
-
*/
|
|
24
|
-
export declare class TimeoutError extends SdError {
|
|
25
|
-
/**
|
|
26
|
-
* @param count 시도 횟수
|
|
27
|
-
* @param message 추가 메시지
|
|
28
|
-
*/
|
|
29
|
-
constructor(count?: number, message?: string);
|
|
30
|
-
}
|
|
31
|
-
//# sourceMappingURL=timeout-error.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"timeout-error.d.ts","sourceRoot":"","sources":["../../../../../core-common/src/errors/timeout-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,YAAa,SAAQ,OAAO;IACvC;;;OAGG;gBACS,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAM7C"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Array 확장 메서드
|
|
3
|
-
*
|
|
4
|
-
* @remarks 각 메서드의 TSDoc은 타입 정의 파일(arr-ext.types.ts) 참조
|
|
5
|
-
*/
|
|
6
|
-
import "./map-ext";
|
|
7
|
-
import type { ReadonlyArrayExt, MutableArrayExt } from "./arr-ext.types";
|
|
8
|
-
declare global {
|
|
9
|
-
interface ReadonlyArray<T> extends ReadonlyArrayExt<T> {
|
|
10
|
-
}
|
|
11
|
-
interface Array<T> extends ReadonlyArrayExt<T>, MutableArrayExt<T> {
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
export type { ArrayDiffsResult, ArrayDiffs2Result, TreeArray, ComparableType } from "./arr-ext.types";
|
|
15
|
-
//# sourceMappingURL=arr-ext.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"arr-ext.d.ts","sourceRoot":"","sources":["../../../../../core-common/src/extensions/arr-ext.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,WAAW,CAAC;AAUnB,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EAIhB,MAAM,iBAAiB,CAAC;AA2uBzB,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,aAAa,CAAC,CAAC,CAAE,SAAQ,gBAAgB,CAAC,CAAC,CAAC;KAAG;IACzD,UAAU,KAAK,CAAC,CAAC,CAAE,SAAQ,gBAAgB,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC;KAAG;CACtE;AAID,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
|