@simplysm/core-node 13.0.100 → 14.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 +21 -21
- package/dist/features/fs-watcher.d.ts.map +1 -1
- package/dist/features/fs-watcher.js +176 -114
- package/dist/features/fs-watcher.js.map +1 -6
- package/dist/index.js +6 -7
- package/dist/index.js.map +1 -6
- package/dist/utils/fs.d.ts +96 -96
- package/dist/utils/fs.d.ts.map +1 -1
- package/dist/utils/fs.js +437 -272
- package/dist/utils/fs.js.map +1 -6
- package/dist/utils/path.d.ts +22 -22
- package/dist/utils/path.js +103 -45
- package/dist/utils/path.js.map +1 -6
- package/dist/worker/create-worker.d.ts +3 -3
- package/dist/worker/create-worker.js +106 -81
- package/dist/worker/create-worker.js.map +1 -6
- package/dist/worker/types.d.ts +14 -14
- package/dist/worker/types.js +4 -1
- package/dist/worker/types.js.map +1 -6
- package/dist/worker/worker.d.ts +5 -5
- package/dist/worker/worker.js +168 -132
- package/dist/worker/worker.js.map +1 -6
- package/lib/worker-dev-proxy.js +15 -0
- package/package.json +8 -6
- package/src/features/fs-watcher.ts +53 -42
- package/src/index.ts +3 -3
- package/src/utils/fs.ts +111 -120
- package/src/utils/path.ts +26 -26
- package/src/worker/create-worker.ts +10 -10
- package/src/worker/types.ts +14 -14
- package/src/worker/worker.ts +29 -29
- package/README.md +0 -112
- package/docs/features.md +0 -91
- package/docs/fs.md +0 -309
- package/docs/path.md +0 -120
- package/docs/worker.md +0 -168
- package/tests/utils/fs-watcher.spec.ts +0 -286
- package/tests/utils/fs.spec.ts +0 -705
- package/tests/utils/path.spec.ts +0 -179
- package/tests/worker/fixtures/test-worker.ts +0 -35
- package/tests/worker/sd-worker.spec.ts +0 -174
package/dist/worker/worker.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { WorkerOptions as WorkerRawOptions } from "worker_threads";
|
|
2
2
|
import type { WorkerModule, WorkerProxy } from "./types";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* 타입 안전한 Worker 래퍼.
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
7
|
* // worker.ts
|
|
@@ -16,11 +16,11 @@ import type { WorkerModule, WorkerProxy } from "./types";
|
|
|
16
16
|
*/
|
|
17
17
|
export declare const Worker: {
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* 타입 안전한 Worker Proxy를 생성한다.
|
|
20
20
|
*
|
|
21
|
-
* @param filePath -
|
|
22
|
-
* @param opt -
|
|
23
|
-
* @returns Proxy
|
|
21
|
+
* @param filePath - 워커 파일 경로 (file:// URL 또는 절대 경로)
|
|
22
|
+
* @param opt - 워커 옵션
|
|
23
|
+
* @returns Proxy 객체 (메서드 직접 호출, on(), terminate() 지원)
|
|
24
24
|
*/
|
|
25
25
|
create<TModule extends WorkerModule>(filePath: string, opt?: Omit<WorkerRawOptions, "stdout" | "stderr">): WorkerProxy<TModule>;
|
|
26
26
|
};
|
package/dist/worker/worker.js
CHANGED
|
@@ -4,143 +4,179 @@ import path from "path";
|
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { Worker as WorkerRaw } from "worker_threads";
|
|
6
6
|
const logger = consola.withTag("sd-worker");
|
|
7
|
+
//#region WorkerInternal
|
|
8
|
+
/**
|
|
9
|
+
* Worker의 내부 구현 클래스.
|
|
10
|
+
* Proxy를 통해 외부에 노출된다.
|
|
11
|
+
*
|
|
12
|
+
* 개발 환경(.ts 파일)에서는 TypeScript 워커 파일을 tsx를 통해 실행한다.
|
|
13
|
+
* 프로덕션 환경(.js 파일)에서는 Worker를 직접 생성한다.
|
|
14
|
+
*/
|
|
7
15
|
class WorkerInternal extends EventEmitter {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
_worker;
|
|
17
|
+
_isTerminated = false;
|
|
18
|
+
_pendingRequests = new Map();
|
|
19
|
+
constructor(filePath, opt) {
|
|
20
|
+
super();
|
|
21
|
+
const ext = path.extname(import.meta.filename);
|
|
22
|
+
// 타입 가드를 통해 env 객체 추출
|
|
23
|
+
const envObj = opt?.env != null && typeof opt.env === "object" ? opt.env : {};
|
|
24
|
+
// 개발 환경(.ts 파일)에서는 tsx를 통해 실행
|
|
25
|
+
// worker-dev-proxy.js: tsx를 통해 TypeScript 워커 파일을 동적으로 로드하는 프록시
|
|
26
|
+
if (ext === ".ts") {
|
|
27
|
+
// file:// URL이면 절대 경로로 변환 (worker-dev-proxy.js가 다시 pathToFileURL을 적용)
|
|
28
|
+
const workerPath = filePath.startsWith("file://") ? fileURLToPath(filePath) : filePath;
|
|
29
|
+
this._worker = new WorkerRaw(path.resolve(import.meta.dirname, "../../lib/worker-dev-proxy.js"), {
|
|
30
|
+
stdout: true,
|
|
31
|
+
stderr: true,
|
|
32
|
+
...opt,
|
|
33
|
+
env: {
|
|
34
|
+
...process.env,
|
|
35
|
+
...envObj,
|
|
36
|
+
},
|
|
37
|
+
argv: [workerPath, ...(opt?.argv ?? [])],
|
|
38
|
+
});
|
|
28
39
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
else {
|
|
41
|
+
// 프로덕션 환경 (.js 파일)
|
|
42
|
+
// file:// URL이면 변환; 그렇지 않으면 절대 경로를 그대로 사용
|
|
43
|
+
const workerPath = filePath.startsWith("file://") ? fileURLToPath(filePath) : filePath;
|
|
44
|
+
this._worker = new WorkerRaw(workerPath, {
|
|
45
|
+
stdout: true,
|
|
46
|
+
stderr: true,
|
|
47
|
+
...opt,
|
|
48
|
+
env: {
|
|
49
|
+
...process.env,
|
|
50
|
+
...envObj,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
39
53
|
}
|
|
40
|
-
|
|
54
|
+
// 워커의 stdout/stderr를 메인 프로세스로 파이프
|
|
55
|
+
this._worker.stdout.pipe(process.stdout);
|
|
56
|
+
this._worker.stderr.pipe(process.stderr);
|
|
57
|
+
this._worker.on("exit", (code) => {
|
|
58
|
+
if (!this._isTerminated && code !== 0) {
|
|
59
|
+
logger.error(`워커가 비정상 종료되었습니다 (코드: ${code})`);
|
|
60
|
+
// 비정상 종료 시 대기 중인 모든 요청을 거부
|
|
61
|
+
this._rejectAllPending(new Error(`워커가 비정상 종료되었습니다 (코드: ${code})`));
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
this._worker.on("error", (err) => {
|
|
65
|
+
logger.error("워커 오류:", err);
|
|
66
|
+
// 워커 오류 시 대기 중인 모든 요청을 거부
|
|
67
|
+
this._rejectAllPending(err);
|
|
68
|
+
});
|
|
69
|
+
this._worker.on("message", (serializedResponse) => {
|
|
70
|
+
const decoded = transfer.decode(serializedResponse);
|
|
71
|
+
// 응답 구조 검증
|
|
72
|
+
if (decoded == null || typeof decoded !== "object" || !("type" in decoded)) {
|
|
73
|
+
logger.warn("워커로부터 잘못된 응답 형식:", decoded);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const response = decoded;
|
|
77
|
+
if (response.type === "event") {
|
|
78
|
+
this.emit(response.event, response.body);
|
|
79
|
+
}
|
|
80
|
+
else if (response.type === "log") {
|
|
81
|
+
process.stdout.write(response.body);
|
|
82
|
+
}
|
|
83
|
+
else if (response.type === "return") {
|
|
84
|
+
const pending = this._pendingRequests.get(response.request.id);
|
|
85
|
+
if (pending) {
|
|
86
|
+
this._pendingRequests.delete(response.request.id);
|
|
87
|
+
pending.resolve(response.body);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// response.type === "error"
|
|
92
|
+
const pending = this._pendingRequests.get(response.request.id);
|
|
93
|
+
if (pending) {
|
|
94
|
+
this._pendingRequests.delete(response.request.id);
|
|
95
|
+
pending.reject(response.body);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
41
99
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
this._worker.on("error", (err) => {
|
|
51
|
-
logger.error("Worker error:", err);
|
|
52
|
-
this._rejectAllPending(err);
|
|
53
|
-
});
|
|
54
|
-
this._worker.on("message", (serializedResponse) => {
|
|
55
|
-
const decoded = transfer.decode(serializedResponse);
|
|
56
|
-
if (decoded == null || typeof decoded !== "object" || !("type" in decoded)) {
|
|
57
|
-
logger.warn("Invalid response format from worker:", decoded);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
const response = decoded;
|
|
61
|
-
if (response.type === "event") {
|
|
62
|
-
this.emit(response.event, response.body);
|
|
63
|
-
} else if (response.type === "log") {
|
|
64
|
-
process.stdout.write(response.body);
|
|
65
|
-
} else if (response.type === "return") {
|
|
66
|
-
const pending = this._pendingRequests.get(response.request.id);
|
|
67
|
-
if (pending) {
|
|
68
|
-
this._pendingRequests.delete(response.request.id);
|
|
69
|
-
pending.resolve(response.body);
|
|
100
|
+
/**
|
|
101
|
+
* 대기 중인 모든 요청을 거부한다.
|
|
102
|
+
*/
|
|
103
|
+
_rejectAllPending(err) {
|
|
104
|
+
for (const [_id, { method, reject }] of this._pendingRequests) {
|
|
105
|
+
reject(new Error(`${err.message} (method: ${method})`));
|
|
70
106
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
107
|
+
this._pendingRequests.clear();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 워커 메서드를 호출한다.
|
|
111
|
+
*/
|
|
112
|
+
call(method, params) {
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
const request = {
|
|
115
|
+
id: Uuid.generate().toString(),
|
|
116
|
+
method,
|
|
117
|
+
params,
|
|
118
|
+
};
|
|
119
|
+
this._pendingRequests.set(request.id, { method, resolve, reject });
|
|
120
|
+
const serialized = transfer.encode(request);
|
|
121
|
+
this._worker.postMessage(serialized.result, serialized.transferList);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 워커를 종료한다.
|
|
126
|
+
*/
|
|
127
|
+
async terminate() {
|
|
128
|
+
this._isTerminated = true;
|
|
129
|
+
this._rejectAllPending(new Error("워커가 종료되었습니다"));
|
|
130
|
+
await this._worker.terminate();
|
|
86
131
|
}
|
|
87
|
-
this._pendingRequests.clear();
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Calls a worker method.
|
|
91
|
-
*/
|
|
92
|
-
call(method, params) {
|
|
93
|
-
return new Promise((resolve, reject) => {
|
|
94
|
-
const request = {
|
|
95
|
-
id: Uuid.generate().toString(),
|
|
96
|
-
method,
|
|
97
|
-
params
|
|
98
|
-
};
|
|
99
|
-
this._pendingRequests.set(request.id, { method, resolve, reject });
|
|
100
|
-
const serialized = transfer.encode(request);
|
|
101
|
-
this._worker.postMessage(serialized.result, serialized.transferList);
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Terminates the worker.
|
|
106
|
-
*/
|
|
107
|
-
async terminate() {
|
|
108
|
-
this._isTerminated = true;
|
|
109
|
-
this._rejectAllPending(new Error("Worker terminated"));
|
|
110
|
-
await this._worker.terminate();
|
|
111
|
-
}
|
|
112
132
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region Worker
|
|
135
|
+
/**
|
|
136
|
+
* 타입 안전한 Worker 래퍼.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* // worker.ts
|
|
140
|
+
* export default createWorker({
|
|
141
|
+
* add: (a: number, b: number) => a + b,
|
|
142
|
+
* });
|
|
143
|
+
*
|
|
144
|
+
* // main.ts
|
|
145
|
+
* const worker = Worker.create<typeof import("./worker.js")>("./worker.ts");
|
|
146
|
+
* const result = await worker.add(10, 20); // 30
|
|
147
|
+
* await worker.terminate();
|
|
148
|
+
*/
|
|
149
|
+
export const Worker = {
|
|
150
|
+
/**
|
|
151
|
+
* 타입 안전한 Worker Proxy를 생성한다.
|
|
152
|
+
*
|
|
153
|
+
* @param filePath - 워커 파일 경로 (file:// URL 또는 절대 경로)
|
|
154
|
+
* @param opt - 워커 옵션
|
|
155
|
+
* @returns Proxy 객체 (메서드 직접 호출, on(), terminate() 지원)
|
|
156
|
+
*/
|
|
157
|
+
create(filePath, opt) {
|
|
158
|
+
const internal = new WorkerInternal(filePath, opt);
|
|
159
|
+
return new Proxy({}, {
|
|
160
|
+
get(_target, prop) {
|
|
161
|
+
// 예약된 메서드: on, off, terminate
|
|
162
|
+
if (prop === "on") {
|
|
163
|
+
return (event, listener) => {
|
|
164
|
+
internal.on(event, listener);
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (prop === "off") {
|
|
168
|
+
return (event, listener) => {
|
|
169
|
+
internal.off(event, listener);
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (prop === "terminate") {
|
|
173
|
+
return () => internal.terminate();
|
|
174
|
+
}
|
|
175
|
+
// 그 외의 경우 워커 메서드로 처리
|
|
176
|
+
return (...args) => internal.call(prop, args);
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
},
|
|
145
180
|
};
|
|
146
|
-
//#
|
|
181
|
+
//#endregion
|
|
182
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -1,6 +1 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/worker/worker.ts"],
|
|
4
|
-
"mappings": "AAAA,SAAS,cAAc,UAAU,YAAY;AAC7C,OAAO,aAAa;AACpB,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAE9B,SAAS,UAAU,iBAAiB;AAGpC,MAAM,SAAS,QAAQ,QAAQ,WAAW;AAW1C,MAAM,uBAAuB,aAAsC;AAAA,EAChD;AAAA,EACT,gBAAgB;AAAA,EACP,mBAAmB,oBAAI,IAGtC;AAAA,EAEF,YAAY,UAAkB,KAAmD;AAC/E,UAAM;AAEN,UAAM,MAAM,KAAK,QAAQ,YAAY,QAAQ;AAG7C,UAAM,SAAS,KAAK,OAAO,QAAQ,OAAO,IAAI,QAAQ,WAAW,IAAI,MAAM,CAAC;AAI5E,QAAI,QAAQ,OAAO;AAEjB,YAAM,aAAa,SAAS,WAAW,SAAS,IAAI,cAAc,QAAQ,IAAI;AAC9E,WAAK,UAAU,IAAI;AAAA,QACjB,KAAK,QAAQ,YAAY,SAAS,+BAA+B;AAAA,QACjE;AAAA,UACE,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,GAAG;AAAA,UACH,KAAK;AAAA,YACH,GAAG,QAAQ;AAAA,YACX,GAAG;AAAA,UACL;AAAA,UACA,MAAM,CAAC,YAAY,GAAI,KAAK,QAAQ,CAAC,CAAE;AAAA,QACzC;AAAA,MACF;AAAA,IACF,OAAO;AAGL,YAAM,aAAa,SAAS,WAAW,SAAS,IAAI,cAAc,QAAQ,IAAI;AAC9E,WAAK,UAAU,IAAI,UAAU,YAAY;AAAA,QACvC,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,GAAG;AAAA,QACH,KAAK;AAAA,UACH,GAAG,QAAQ;AAAA,UACX,GAAG;AAAA,QACL;AAAA,MACF,CAAC;AAAA,IACH;AAGA,SAAK,QAAQ,OAAO,KAAK,QAAQ,MAAM;AACvC,SAAK,QAAQ,OAAO,KAAK,QAAQ,MAAM;AAEvC,SAAK,QAAQ,GAAG,QAAQ,CAAC,SAAS;AAChC,UAAI,CAAC,KAAK,iBAAiB,SAAS,GAAG;AACrC,eAAO,MAAM,yBAAyB,IAAI,GAAG;AAE7C,aAAK,kBAAkB,IAAI,MAAM,yBAAyB,IAAI,GAAG,CAAC;AAAA,MACpE;AAAA,IACF,CAAC;AAED,SAAK,QAAQ,GAAG,SAAS,CAAC,QAAQ;AAChC,aAAO,MAAM,iBAAiB,GAAG;AAEjC,WAAK,kBAAkB,GAAG;AAAA,IAC5B,CAAC;AAED,SAAK,QAAQ,GAAG,WAAW,CAAC,uBAAgC;AAC1D,YAAM,UAAU,SAAS,OAAO,kBAAkB;AAGlD,UAAI,WAAW,QAAQ,OAAO,YAAY,YAAY,EAAE,UAAU,UAAU;AAC1E,eAAO,KAAK,wCAAwC,OAAO;AAC3D;AAAA,MACF;AACA,YAAM,WAAW;AAEjB,UAAI,SAAS,SAAS,SAAS;AAC7B,aAAK,KAAK,SAAS,OAAO,SAAS,IAAI;AAAA,MACzC,WAAW,SAAS,SAAS,OAAO;AAClC,gBAAQ,OAAO,MAAM,SAAS,IAAI;AAAA,MACpC,WAAW,SAAS,SAAS,UAAU;AACrC,cAAM,UAAU,KAAK,iBAAiB,IAAI,SAAS,QAAQ,EAAE;AAC7D,YAAI,SAAS;AACX,eAAK,iBAAiB,OAAO,SAAS,QAAQ,EAAE;AAChD,kBAAQ,QAAQ,SAAS,IAAI;AAAA,QAC/B;AAAA,MACF,OAAO;AAEL,cAAM,UAAU,KAAK,iBAAiB,IAAI,SAAS,QAAQ,EAAE;AAC7D,YAAI,SAAS;AACX,eAAK,iBAAiB,OAAO,SAAS,QAAQ,EAAE;AAChD,kBAAQ,OAAO,SAAS,IAAI;AAAA,QAC9B;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,KAAkB;AAC1C,eAAW,CAAC,KAAK,EAAE,QAAQ,OAAO,CAAC,KAAK,KAAK,kBAAkB;AAC7D,aAAO,IAAI,MAAM,GAAG,IAAI,OAAO,aAAa,MAAM,GAAG,CAAC;AAAA,IACxD;AACA,SAAK,iBAAiB,MAAM;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,QAAgB,QAAqC;AACxD,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,UAAyB;AAAA,QAC7B,IAAI,KAAK,SAAS,EAAE,SAAS;AAAA,QAC7B;AAAA,QACA;AAAA,MACF;AAEA,WAAK,iBAAiB,IAAI,QAAQ,IAAI,EAAE,QAAQ,SAAS,OAAO,CAAC;AAEjE,YAAM,aAAa,SAAS,OAAO,OAAO;AAC1C,WAAK,QAAQ,YAAY,WAAW,QAAQ,WAAW,YAAY;AAAA,IACrE,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAA2B;AAC/B,SAAK,gBAAgB;AACrB,SAAK,kBAAkB,IAAI,MAAM,mBAAmB,CAAC;AACrD,UAAM,KAAK,QAAQ,UAAU;AAAA,EAC/B;AACF;AAoBO,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQpB,OACE,UACA,KACsB;AACtB,UAAM,WAAW,IAAI,eAAe,UAAU,GAAG;AAEjD,WAAO,IAAI,MAAM,CAAC,GAA2B;AAAA,MAC3C,IAAI,SAAS,MAAc;AAEzB,YAAI,SAAS,MAAM;AACjB,iBAAO,CAAC,OAAe,aAAsC;AAC3D,qBAAS,GAAG,OAAO,QAAQ;AAAA,UAC7B;AAAA,QACF;AACA,YAAI,SAAS,OAAO;AAClB,iBAAO,CAAC,OAAe,aAAsC;AAC3D,qBAAS,IAAI,OAAO,QAAQ;AAAA,UAC9B;AAAA,QACF;AACA,YAAI,SAAS,aAAa;AACxB,iBAAO,MAAM,SAAS,UAAU;AAAA,QAClC;AAGA,eAAO,IAAI,SAAoB,SAAS,KAAK,MAAM,IAAI;AAAA,MACzD;AAAA,IACF,CAAC;AAAA,EACH;AACF;",
|
|
5
|
-
"names": []
|
|
6
|
-
}
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["..\\..\\src\\worker\\worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { tsImport } from "tsx/esm/api";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
|
|
5
|
+
// Assume argv[2] contains the actual worker file path
|
|
6
|
+
const workerFile = process.argv[2];
|
|
7
|
+
if (!workerFile) {
|
|
8
|
+
throw new Error("Worker file path is required as argument!");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// If file:// URL is already passed, use it as-is; otherwise convert
|
|
12
|
+
const workerFileUrl = workerFile.startsWith("file://")
|
|
13
|
+
? workerFile
|
|
14
|
+
: pathToFileURL(workerFile).href;
|
|
15
|
+
await tsImport(workerFileUrl, import.meta.url);
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/core-node",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"author": "
|
|
3
|
+
"version": "14.0.1",
|
|
4
|
+
"description": "심플리즘 패키지 - 코어 (node)",
|
|
5
|
+
"author": "심플리즘",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -14,9 +14,8 @@
|
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"files": [
|
|
16
16
|
"dist",
|
|
17
|
-
"docs",
|
|
18
17
|
"src",
|
|
19
|
-
"
|
|
18
|
+
"lib"
|
|
20
19
|
],
|
|
21
20
|
"sideEffects": false,
|
|
22
21
|
"dependencies": {
|
|
@@ -25,6 +24,9 @@
|
|
|
25
24
|
"glob": "^13.0.6",
|
|
26
25
|
"minimatch": "^10.2.4",
|
|
27
26
|
"tsx": "^4.21.0",
|
|
28
|
-
"@simplysm/core-common": "
|
|
27
|
+
"@simplysm/core-common": "14.0.1"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^20.14.8"
|
|
29
31
|
}
|
|
30
32
|
}
|
|
@@ -8,11 +8,11 @@ import { type NormPath, norm } from "../utils/path";
|
|
|
8
8
|
|
|
9
9
|
//#region Helpers
|
|
10
10
|
|
|
11
|
-
/** Glob
|
|
11
|
+
/** Glob 메타문자 패턴 */
|
|
12
12
|
const GLOB_CHARS_RE = /[*?{[\]]/;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Glob 패턴에서 기본 디렉토리를 추출한다.
|
|
16
16
|
* @example extractGlobBase("/home/user/src/**\/*.ts") → "/home/user/src"
|
|
17
17
|
*/
|
|
18
18
|
function extractGlobBase(globPath: string): string {
|
|
@@ -30,22 +30,22 @@ function extractGlobBase(globPath: string): string {
|
|
|
30
30
|
//#region Types
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
33
|
+
* 지원되는 파일 변경 이벤트 타입 목록.
|
|
34
34
|
*/
|
|
35
35
|
const FS_WATCHER_EVENTS = ["add", "addDir", "change", "unlink", "unlinkDir"] as const;
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
38
|
+
* 파일 변경 이벤트 타입.
|
|
39
39
|
*/
|
|
40
40
|
export type FsWatcherEvent = (typeof FS_WATCHER_EVENTS)[number];
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
43
|
+
* 파일 변경 정보.
|
|
44
44
|
*/
|
|
45
45
|
export interface FsWatcherChangeInfo {
|
|
46
|
-
/**
|
|
46
|
+
/** 변경 이벤트 타입 */
|
|
47
47
|
event: FsWatcherEvent;
|
|
48
|
-
/**
|
|
48
|
+
/** 변경된 파일/디렉토리 경로 (정규화됨) */
|
|
49
49
|
path: NormPath;
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -54,13 +54,13 @@ export interface FsWatcherChangeInfo {
|
|
|
54
54
|
//#region FsWatcher
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* Chokidar
|
|
58
|
-
*
|
|
57
|
+
* Chokidar 기반 파일 시스템 감시 래퍼.
|
|
58
|
+
* 짧은 시간 내에 발생하는 이벤트를 병합하여 콜백을 한 번만 호출한다.
|
|
59
59
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
60
|
+
* **주의**: chokidar의 `ignoreInitial` 옵션은 내부적으로 항상 `true`로 설정된다.
|
|
61
|
+
* `options.ignoreInitial: false`를 전달하면 첫 번째 `onChange` 호출 시 빈 배열로 콜백이 호출되지만,
|
|
62
|
+
* 실제 초기 파일 목록은 포함되지 않는다.
|
|
63
|
+
* 이는 이벤트 병합 로직과의 충돌을 방지하기 위한 의도적인 동작이다.
|
|
64
64
|
*
|
|
65
65
|
* @example
|
|
66
66
|
* const watcher = await FsWatcher.watch(["src/**\/*.ts"]);
|
|
@@ -70,24 +70,35 @@ export interface FsWatcherChangeInfo {
|
|
|
70
70
|
* }
|
|
71
71
|
* });
|
|
72
72
|
*
|
|
73
|
-
* //
|
|
73
|
+
* // 종료
|
|
74
74
|
* await watcher.close();
|
|
75
75
|
*/
|
|
76
76
|
export class FsWatcher {
|
|
77
77
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
78
|
+
* 파일 감시를 시작한다 (비동기).
|
|
79
|
+
* ready 이벤트가 발생할 때까지 대기한다.
|
|
80
80
|
*
|
|
81
|
-
* @param paths -
|
|
82
|
-
* @param options - chokidar
|
|
81
|
+
* @param paths - 감시할 파일/디렉토리 경로 또는 glob 패턴 배열
|
|
82
|
+
* @param options - chokidar 옵션
|
|
83
83
|
*/
|
|
84
84
|
static async watch(paths: string[], options?: chokidar.ChokidarOptions): Promise<FsWatcher> {
|
|
85
85
|
return new Promise<FsWatcher>((resolve, reject) => {
|
|
86
86
|
const watcher = new FsWatcher(paths, options);
|
|
87
|
-
|
|
87
|
+
|
|
88
|
+
const onReady = () => {
|
|
89
|
+
watcher._watcher.removeListener("error", onError);
|
|
88
90
|
resolve(watcher);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
+
};
|
|
92
|
+
const onError = (err: unknown) => {
|
|
93
|
+
watcher._watcher.removeListener("ready", onReady);
|
|
94
|
+
watcher.close().then(
|
|
95
|
+
() => reject(err),
|
|
96
|
+
() => reject(err),
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
watcher._watcher.once("ready", onReady);
|
|
101
|
+
watcher._watcher.once("error", onError);
|
|
91
102
|
});
|
|
92
103
|
}
|
|
93
104
|
|
|
@@ -111,7 +122,7 @@ export class FsWatcher {
|
|
|
111
122
|
}
|
|
112
123
|
}
|
|
113
124
|
|
|
114
|
-
//
|
|
125
|
+
// 중복 경로 제거
|
|
115
126
|
const uniquePaths = [...new Set(watchPaths)];
|
|
116
127
|
|
|
117
128
|
this._watcher = chokidar.watch(uniquePaths, {
|
|
@@ -121,18 +132,18 @@ export class FsWatcher {
|
|
|
121
132
|
});
|
|
122
133
|
this._ignoreInitial = options?.ignoreInitial ?? this._ignoreInitial;
|
|
123
134
|
|
|
124
|
-
//
|
|
135
|
+
// 감시 중 발생하는 오류를 로깅
|
|
125
136
|
this._watcher.on("error", (err) => {
|
|
126
|
-
this._logger.error("FsWatcher
|
|
137
|
+
this._logger.error("FsWatcher 오류:", err);
|
|
127
138
|
});
|
|
128
139
|
}
|
|
129
140
|
|
|
130
141
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
142
|
+
* 파일 변경 이벤트 핸들러를 등록한다.
|
|
143
|
+
* 지정된 지연 시간 동안 이벤트를 수집하여 콜백을 한 번 호출한다.
|
|
133
144
|
*
|
|
134
|
-
* @param opt.delay -
|
|
135
|
-
* @param cb -
|
|
145
|
+
* @param opt.delay - 이벤트 병합 대기 시간 (ms)
|
|
146
|
+
* @param cb - 변경 이벤트 콜백
|
|
136
147
|
*/
|
|
137
148
|
onChange(
|
|
138
149
|
opt: { delay?: number },
|
|
@@ -143,7 +154,7 @@ export class FsWatcher {
|
|
|
143
154
|
|
|
144
155
|
let changeInfoMap = new Map<string, EventName>();
|
|
145
156
|
|
|
146
|
-
//
|
|
157
|
+
// ignoreInitial이 false이면 초기에 빈 배열로 콜백 호출
|
|
147
158
|
if (!this._ignoreInitial) {
|
|
148
159
|
fnQ.run(async () => {
|
|
149
160
|
await cb([]);
|
|
@@ -151,22 +162,22 @@ export class FsWatcher {
|
|
|
151
162
|
}
|
|
152
163
|
|
|
153
164
|
this._watcher.on("all", (event, filePath) => {
|
|
154
|
-
//
|
|
165
|
+
// 지원되는 이벤트만 처리
|
|
155
166
|
if (!FS_WATCHER_EVENTS.includes(event as FsWatcherEvent)) return;
|
|
156
167
|
|
|
157
|
-
//
|
|
168
|
+
// glob matcher가 존재하면 패턴 필터링 적용
|
|
158
169
|
if (this._globMatchers.length > 0) {
|
|
159
170
|
const posixFilePath = filePath.replace(/\\/g, "/");
|
|
160
171
|
if (!this._globMatchers.some((m) => m.match(posixFilePath))) return;
|
|
161
172
|
}
|
|
162
173
|
|
|
163
174
|
/*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
* - add + change → add (
|
|
167
|
-
* - add + unlink →
|
|
168
|
-
* - unlink + add → add (
|
|
169
|
-
* -
|
|
175
|
+
* 이벤트 병합 전략:
|
|
176
|
+
* 같은 파일에 대해 짧은 시간 내에 여러 이벤트가 발생하면, 최종 상태만 전달한다.
|
|
177
|
+
* - add + change → add (생성 직후 수정은 생성으로 간주)
|
|
178
|
+
* - add + unlink → 변경 없음 (생성 직후 삭제는 변경 없음으로 간주)
|
|
179
|
+
* - unlink + add → add (삭제 후 재생성은 생성으로 간주)
|
|
180
|
+
* - 그 외 → 최신 이벤트로 덮어쓰기
|
|
170
181
|
*/
|
|
171
182
|
if (!changeInfoMap.has(filePath)) {
|
|
172
183
|
changeInfoMap.set(filePath, event);
|
|
@@ -174,19 +185,19 @@ export class FsWatcher {
|
|
|
174
185
|
const prevEvent = changeInfoMap.get(filePath)!;
|
|
175
186
|
|
|
176
187
|
if (prevEvent === "add" && event === "change") {
|
|
177
|
-
// add
|
|
188
|
+
// add 후 change → add 유지
|
|
178
189
|
changeInfoMap.set(filePath, "add");
|
|
179
190
|
} else if (
|
|
180
191
|
(prevEvent === "add" && event === "unlink") ||
|
|
181
192
|
(prevEvent === "addDir" && event === "unlinkDir")
|
|
182
193
|
) {
|
|
183
|
-
// add
|
|
194
|
+
// add 후 unlink → 변경 없음 (삭제)
|
|
184
195
|
changeInfoMap.delete(filePath);
|
|
185
196
|
} else if (prevEvent === "unlink" && (event === "add" || event === "change")) {
|
|
186
|
-
// unlink
|
|
197
|
+
// unlink 후 add/change → add (파일 재생성)
|
|
187
198
|
changeInfoMap.set(filePath, "add");
|
|
188
199
|
} else if (prevEvent === "unlinkDir" && event === "addDir") {
|
|
189
|
-
// unlinkDir
|
|
200
|
+
// unlinkDir 후 addDir → addDir (디렉토리 재생성)
|
|
190
201
|
changeInfoMap.set(filePath, "addDir");
|
|
191
202
|
} else {
|
|
192
203
|
changeInfoMap.set(filePath, event);
|
|
@@ -213,7 +224,7 @@ export class FsWatcher {
|
|
|
213
224
|
}
|
|
214
225
|
|
|
215
226
|
/**
|
|
216
|
-
*
|
|
227
|
+
* 파일 감시자를 종료한다.
|
|
217
228
|
*/
|
|
218
229
|
async close(): Promise<void> {
|
|
219
230
|
for (const q of this._debounceQueues) {
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
//
|
|
1
|
+
// 유틸리티
|
|
2
2
|
export * as fsx from "./utils/fs";
|
|
3
3
|
export * as pathx from "./utils/path";
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// 기능
|
|
6
6
|
export * from "./features/fs-watcher";
|
|
7
7
|
|
|
8
|
-
//
|
|
8
|
+
// 워커
|
|
9
9
|
export * from "./worker/types";
|
|
10
10
|
export * from "./worker/worker";
|
|
11
11
|
export * from "./worker/create-worker";
|