@simplysm/core-node 13.0.84 → 13.0.86
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 +288 -31
- package/package.json +2 -2
- package/docs/filesystem.md +0 -254
- package/docs/fs-watcher.md +0 -145
- package/docs/path.md +0 -154
- package/docs/worker.md +0 -209
package/README.md
CHANGED
|
@@ -1,48 +1,305 @@
|
|
|
1
1
|
# @simplysm/core-node
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Node.js 환경 전용 유틸리티. 파일시스템, 경로, 파일 감시, Worker 스레드를 제공한다.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Installation
|
|
5
|
+
## 설치
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
8
|
npm install @simplysm/core-node
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
**의존성:** `@simplysm/core-common`, `chokidar`, `consola`, `glob`, `minimatch`, `tsx`
|
|
12
|
+
|
|
13
|
+
## 모듈 구조
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
@simplysm/core-node
|
|
17
|
+
├── fsx # 파일시스템 유틸리티 (namespace)
|
|
18
|
+
├── pathx # 경로 유틸리티 (namespace)
|
|
19
|
+
├── FsWatcher # 파일 감시 (class)
|
|
20
|
+
├── Worker # 타입 안전한 Worker 스레드 (object)
|
|
21
|
+
├── createWorker # Worker 팩토리 (function)
|
|
22
|
+
└── types # NormPath, WorkerModule, WorkerProxy, ...
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 주요 사용법
|
|
14
28
|
|
|
15
|
-
|
|
16
|
-
|---|---|---|
|
|
17
|
-
| Filesystem Utilities | File/directory CRUD, glob, JSON read/write | [docs/filesystem.md](docs/filesystem.md) |
|
|
18
|
-
| Path Utilities | Path normalization, POSIX conversion, child path checks | [docs/path.md](docs/path.md) |
|
|
19
|
-
| File Watcher | Chokidar-based watcher with event debouncing and merging | [docs/fs-watcher.md](docs/fs-watcher.md) |
|
|
20
|
-
| Worker | Type-safe worker thread wrapper with RPC and events | [docs/worker.md](docs/worker.md) |
|
|
29
|
+
### fsx -- 파일시스템 유틸리티
|
|
21
30
|
|
|
22
|
-
|
|
31
|
+
모든 함수는 async/sync 쌍으로 제공된다 (예: `read` / `readSync`).
|
|
23
32
|
|
|
24
33
|
```typescript
|
|
25
|
-
import { fsx
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
import { fsx } from "@simplysm/core-node";
|
|
35
|
+
|
|
36
|
+
// 존재 확인
|
|
37
|
+
await fsx.exists("/path/to/file");
|
|
38
|
+
fsx.existsSync("/path/to/file");
|
|
39
|
+
|
|
40
|
+
// 디렉토리 생성 (재귀)
|
|
41
|
+
await fsx.mkdir("/path/to/dir");
|
|
42
|
+
fsx.mkdirSync("/path/to/dir");
|
|
43
|
+
|
|
44
|
+
// 삭제
|
|
45
|
+
// async: 6회 재시도, 500ms 간격 (파일 잠금 등 일시적 오류 대응)
|
|
46
|
+
// sync: 재시도 없이 즉시 실패
|
|
47
|
+
await fsx.rm("/path/to/target");
|
|
48
|
+
fsx.rmSync("/path/to/target");
|
|
49
|
+
|
|
50
|
+
// 복사 (필터 지원)
|
|
51
|
+
// 필터는 자식 경로에만 적용, 최상위 sourcePath는 필터링 대상이 아님
|
|
52
|
+
// 디렉토리에 false를 반환하면 해당 디렉토리와 모든 하위 내용을 건너뜀
|
|
53
|
+
await fsx.copy(src, dst, (absPath) => !absPath.includes("node_modules"));
|
|
54
|
+
fsx.copySync(src, dst);
|
|
55
|
+
|
|
56
|
+
// 읽기
|
|
57
|
+
const text = await fsx.read("/path/to/file.txt"); // UTF-8 문자열
|
|
58
|
+
const buf = await fsx.readBuffer("/path/to/file.bin"); // Buffer
|
|
59
|
+
const config = await fsx.readJson<Config>("/path/config.json"); // JSON 파싱 (core-common의 json.parse 사용)
|
|
60
|
+
|
|
61
|
+
// 쓰기 (부모 디렉토리 자동 생성)
|
|
62
|
+
await fsx.write("/path/to/file.txt", content); // string | Uint8Array
|
|
63
|
+
await fsx.writeJson("/path/to/config.json", data, { space: 2 });
|
|
64
|
+
|
|
65
|
+
// 디렉토리 읽기
|
|
66
|
+
const entries = await fsx.readdir("/path/to/dir"); // string[]
|
|
67
|
+
|
|
68
|
+
// 파일 정보
|
|
69
|
+
const stats = await fsx.stat("/path/to/file"); // 심볼릭 링크 따라감
|
|
70
|
+
const lstats = await fsx.lstat("/path/to/symlink"); // 심볼릭 링크 자체 정보
|
|
71
|
+
|
|
72
|
+
// Glob 패턴 검색 (절대 경로 반환)
|
|
73
|
+
const files = await fsx.glob("**/*.ts", { cwd: "/project" });
|
|
74
|
+
const filesSync = fsx.globSync("**/*.ts", { cwd: "/project" });
|
|
75
|
+
|
|
76
|
+
// 빈 디렉토리 정리 (재귀적으로 빈 디렉토리 삭제)
|
|
77
|
+
await fsx.clearEmptyDirectory("/path/to/dir");
|
|
78
|
+
|
|
79
|
+
// 상위 디렉토리 탐색
|
|
80
|
+
// fromPath에서 루트까지 각 디렉토리에서 childGlob 패턴에 매칭되는 파일 검색
|
|
81
|
+
const paths = await fsx.findAllParentChildPaths("package.json", "/project/src/deep");
|
|
82
|
+
const pathsSync = fsx.findAllParentChildPathsSync("package.json", "/project/src/deep", "/project"); // rootPath 지정 가능
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### pathx -- 경로 유틸리티
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { pathx } from "@simplysm/core-node";
|
|
89
|
+
import type { NormPath } from "@simplysm/core-node";
|
|
90
|
+
|
|
91
|
+
// POSIX 변환 (여러 인자를 path.join 후 변환)
|
|
92
|
+
pathx.posix("C:\\Users\\test"); // "C:/Users/test"
|
|
93
|
+
pathx.posix("src", "index.ts"); // "src/index.ts"
|
|
94
|
+
|
|
95
|
+
// 정규화 (절대 경로, 브랜드 타입 NormPath 반환)
|
|
96
|
+
const norm: NormPath = pathx.norm("relative/path");
|
|
97
|
+
const norm2: NormPath = pathx.norm("base", "relative"); // 여러 경로 조합 가능
|
|
98
|
+
|
|
99
|
+
// 디렉토리 변경
|
|
100
|
+
pathx.changeFileDirectory("/a/b/c.txt", "/a", "/x"); // "/x/b/c.txt"
|
|
101
|
+
// filePath가 fromDirectory 하위가 아니면 ArgumentError 발생
|
|
102
|
+
|
|
103
|
+
// 확장자 없는 파일명
|
|
104
|
+
pathx.basenameWithoutExt("/path/file.spec.ts"); // "file.spec"
|
|
105
|
+
|
|
106
|
+
// 자식 경로 확인
|
|
107
|
+
pathx.isChildPath("/a/b/c", "/a/b"); // true
|
|
108
|
+
pathx.isChildPath("/a/b", "/a/b"); // false (동일 경로)
|
|
109
|
+
|
|
110
|
+
// 대상 경로 필터링
|
|
111
|
+
pathx.filterByTargets(
|
|
112
|
+
["/proj/src/a.ts", "/proj/tests/c.ts"],
|
|
113
|
+
["src"],
|
|
114
|
+
"/proj"
|
|
115
|
+
);
|
|
116
|
+
// ["/proj/src/a.ts"]
|
|
117
|
+
// targets가 빈 배열이면 files를 그대로 반환
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### FsWatcher -- 파일 감시
|
|
121
|
+
|
|
122
|
+
Chokidar 기반, 이벤트 디바운싱 및 glob 패턴 지원.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { FsWatcher } from "@simplysm/core-node";
|
|
126
|
+
import type { FsWatcherEvent, FsWatcherChangeInfo } from "@simplysm/core-node";
|
|
127
|
+
|
|
128
|
+
// 감시 시작 (ready 이벤트까지 대기)
|
|
129
|
+
// 경로 또는 glob 패턴 모두 사용 가능
|
|
130
|
+
const watcher = await FsWatcher.watch([
|
|
131
|
+
"/project/src", // 디렉토리 경로
|
|
132
|
+
"/project/lib/**/*.js", // glob 패턴
|
|
133
|
+
]);
|
|
134
|
+
|
|
135
|
+
// 변경 이벤트 핸들러 등록
|
|
136
|
+
// delay ms 동안 이벤트를 모아 한 번에 콜백 호출
|
|
137
|
+
watcher.onChange({ delay: 300 }, (changes: FsWatcherChangeInfo[]) => {
|
|
138
|
+
for (const { event, path } of changes) {
|
|
139
|
+
// event: "add" | "addDir" | "change" | "unlink" | "unlinkDir"
|
|
140
|
+
// path: NormPath (정규화된 절대 경로)
|
|
41
141
|
}
|
|
42
142
|
});
|
|
43
143
|
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
144
|
+
// 종료
|
|
145
|
+
await watcher.close();
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**이벤트 병합 전략** (같은 파일에 대해 짧은 시간 내 여러 이벤트 발생 시):
|
|
149
|
+
- `add` + `change` -> `add` (생성 직후 수정은 생성으로 처리)
|
|
150
|
+
- `add` + `unlink` -> 제거 (생성 후 즉시 삭제는 무변경)
|
|
151
|
+
- `unlink` + `add` -> `add` (삭제 후 재생성은 생성으로 처리)
|
|
152
|
+
- `unlinkDir` + `addDir` -> `addDir`
|
|
153
|
+
- 그 외 -> 마지막 이벤트로 덮어쓰기
|
|
154
|
+
|
|
155
|
+
### Worker -- 타입 안전한 Worker 스레드
|
|
156
|
+
|
|
157
|
+
#### Worker 파일 작성 (worker.ts)
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { createWorker } from "@simplysm/core-node";
|
|
161
|
+
|
|
162
|
+
// 이벤트 없는 Worker
|
|
163
|
+
export default createWorker({
|
|
164
|
+
add: (a: number, b: number) => a + b,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// 이벤트가 있는 Worker
|
|
168
|
+
interface MyEvents {
|
|
169
|
+
progress: number;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const methods = {
|
|
173
|
+
processFile: async (path: string) => {
|
|
174
|
+
sender.send("progress", 50);
|
|
175
|
+
// ... 처리 ...
|
|
176
|
+
sender.send("progress", 100);
|
|
177
|
+
return "done";
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const sender = createWorker<typeof methods, MyEvents>(methods);
|
|
182
|
+
export default sender;
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### 메인 스레드에서 사용
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { Worker } from "@simplysm/core-node";
|
|
189
|
+
|
|
190
|
+
// Worker 생성 (타입 추론을 위해 typeof import 사용)
|
|
191
|
+
const worker = Worker.create<typeof import("./worker")>("./worker.ts");
|
|
192
|
+
|
|
193
|
+
// 옵션 전달 (WorkerOptions에서 stdout/stderr 제외)
|
|
194
|
+
const worker2 = Worker.create<typeof import("./worker")>("./worker.ts", {
|
|
195
|
+
env: { NODE_ENV: "production" },
|
|
196
|
+
argv: ["--verbose"],
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// 메서드 호출 (자동 Promise 래핑)
|
|
200
|
+
const result = await worker.add(10, 20); // 30
|
|
201
|
+
|
|
202
|
+
// 이벤트 수신
|
|
203
|
+
const handler = (value: number) => { /* ... */ };
|
|
204
|
+
worker.on("progress", handler);
|
|
205
|
+
worker.off("progress", handler); // 이벤트 해제
|
|
206
|
+
|
|
207
|
+
// 종료
|
|
47
208
|
await worker.terminate();
|
|
48
209
|
```
|
|
210
|
+
|
|
211
|
+
**특징:**
|
|
212
|
+
- `.ts` 파일은 tsx를 통해 자동 실행 (개발 환경)
|
|
213
|
+
- UUID 기반 요청 추적으로 동시 요청 지원
|
|
214
|
+
- Worker 크래시 시 대기 중인 모든 요청 자동 reject
|
|
215
|
+
- Worker 내 `process.stdout.write`는 메인 스레드로 자동 전달
|
|
216
|
+
- `@simplysm/core-common`의 `transfer`를 사용하여 메시지 직렬화
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## API 레퍼런스
|
|
221
|
+
|
|
222
|
+
### fsx (namespace)
|
|
223
|
+
|
|
224
|
+
| 함수 | 시그니처 | 설명 |
|
|
225
|
+
|------|----------|------|
|
|
226
|
+
| `exists` | `(targetPath: string) => Promise<boolean>` | 파일/디렉토리 존재 확인 |
|
|
227
|
+
| `existsSync` | `(targetPath: string) => boolean` | 동기 버전 |
|
|
228
|
+
| `mkdir` | `(targetPath: string) => Promise<void>` | 디렉토리 재귀 생성 |
|
|
229
|
+
| `mkdirSync` | `(targetPath: string) => void` | 동기 버전 |
|
|
230
|
+
| `rm` | `(targetPath: string) => Promise<void>` | 삭제 (재시도 포함) |
|
|
231
|
+
| `rmSync` | `(targetPath: string) => void` | 동기 버전 (재시도 없음) |
|
|
232
|
+
| `copy` | `(src, dst, filter?) => Promise<void>` | 파일/디렉토리 복사 |
|
|
233
|
+
| `copySync` | `(src, dst, filter?) => void` | 동기 버전 |
|
|
234
|
+
| `read` | `(targetPath: string) => Promise<string>` | UTF-8 텍스트 읽기 |
|
|
235
|
+
| `readSync` | `(targetPath: string) => string` | 동기 버전 |
|
|
236
|
+
| `readBuffer` | `(targetPath: string) => Promise<Buffer>` | 바이너리 읽기 |
|
|
237
|
+
| `readBufferSync` | `(targetPath: string) => Buffer` | 동기 버전 |
|
|
238
|
+
| `readJson` | `<T>(targetPath: string) => Promise<T>` | JSON 파일 읽기 |
|
|
239
|
+
| `readJsonSync` | `<T>(targetPath: string) => T` | 동기 버전 |
|
|
240
|
+
| `write` | `(targetPath, data: string \| Uint8Array) => Promise<void>` | 파일 쓰기 (부모 디렉토리 자동 생성) |
|
|
241
|
+
| `writeSync` | `(targetPath, data: string \| Uint8Array) => void` | 동기 버전 |
|
|
242
|
+
| `writeJson` | `(targetPath, data, options?) => Promise<void>` | JSON 파일 쓰기 |
|
|
243
|
+
| `writeJsonSync` | `(targetPath, data, options?) => void` | 동기 버전 |
|
|
244
|
+
| `readdir` | `(targetPath: string) => Promise<string[]>` | 디렉토리 내용 읽기 |
|
|
245
|
+
| `readdirSync` | `(targetPath: string) => string[]` | 동기 버전 |
|
|
246
|
+
| `stat` | `(targetPath: string) => Promise<fs.Stats>` | 파일 정보 (심링크 따라감) |
|
|
247
|
+
| `statSync` | `(targetPath: string) => fs.Stats` | 동기 버전 |
|
|
248
|
+
| `lstat` | `(targetPath: string) => Promise<fs.Stats>` | 파일 정보 (심링크 자체) |
|
|
249
|
+
| `lstatSync` | `(targetPath: string) => fs.Stats` | 동기 버전 |
|
|
250
|
+
| `glob` | `(pattern, options?) => Promise<string[]>` | Glob 패턴 검색 (절대 경로 반환) |
|
|
251
|
+
| `globSync` | `(pattern, options?) => string[]` | 동기 버전 |
|
|
252
|
+
| `clearEmptyDirectory` | `(dirPath: string) => Promise<void>` | 빈 디렉토리 재귀 삭제 |
|
|
253
|
+
| `findAllParentChildPaths` | `(childGlob, fromPath, rootPath?) => Promise<string[]>` | 상위 디렉토리 탐색 |
|
|
254
|
+
| `findAllParentChildPathsSync` | `(childGlob, fromPath, rootPath?) => string[]` | 동기 버전 |
|
|
255
|
+
|
|
256
|
+
### pathx (namespace)
|
|
257
|
+
|
|
258
|
+
| 함수 | 시그니처 | 설명 |
|
|
259
|
+
|------|----------|------|
|
|
260
|
+
| `posix` | `(...args: string[]) => string` | POSIX 경로 변환 (path.join 후 `\` -> `/`) |
|
|
261
|
+
| `norm` | `(...paths: string[]) => NormPath` | 절대 경로 정규화 (브랜드 타입) |
|
|
262
|
+
| `changeFileDirectory` | `(filePath, fromDir, toDir) => string` | 파일의 기준 디렉토리 변경 |
|
|
263
|
+
| `basenameWithoutExt` | `(filePath: string) => string` | 확장자 제외한 파일명 |
|
|
264
|
+
| `isChildPath` | `(childPath, parentPath) => boolean` | 자식 경로 여부 확인 |
|
|
265
|
+
| `filterByTargets` | `(files, targets, cwd) => string[]` | 대상 경로 기준 파일 필터링 |
|
|
266
|
+
|
|
267
|
+
### FsWatcher (class)
|
|
268
|
+
|
|
269
|
+
| 멤버 | 시그니처 | 설명 |
|
|
270
|
+
|------|----------|------|
|
|
271
|
+
| `static watch` | `(paths: string[], options?) => Promise<FsWatcher>` | 감시 시작 (ready까지 대기) |
|
|
272
|
+
| `onChange` | `(opt: { delay?: number }, cb) => this` | 변경 이벤트 핸들러 등록 (체이닝 가능) |
|
|
273
|
+
| `close` | `() => Promise<void>` | 감시 종료 |
|
|
274
|
+
|
|
275
|
+
### Worker (object)
|
|
276
|
+
|
|
277
|
+
| 멤버 | 시그니처 | 설명 |
|
|
278
|
+
|------|----------|------|
|
|
279
|
+
| `create` | `<TModule>(filePath, opt?) => WorkerProxy<TModule>` | 타입 안전한 Worker 프록시 생성 |
|
|
280
|
+
|
|
281
|
+
### createWorker (function)
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
function createWorker<
|
|
285
|
+
TMethods extends Record<string, (...args: any[]) => unknown>,
|
|
286
|
+
TEvents extends Record<string, unknown> = Record<string, never>,
|
|
287
|
+
>(methods: TMethods): {
|
|
288
|
+
send<K extends keyof TEvents & string>(event: K, data?: TEvents[K]): void;
|
|
289
|
+
__methods: TMethods;
|
|
290
|
+
__events: TEvents;
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### 타입
|
|
295
|
+
|
|
296
|
+
| 타입 | 설명 |
|
|
297
|
+
|------|------|
|
|
298
|
+
| `NormPath` | 정규화된 경로 브랜드 타입 (`string & { [NORM]: never }`) |
|
|
299
|
+
| `FsWatcherEvent` | `"add" \| "addDir" \| "change" \| "unlink" \| "unlinkDir"` |
|
|
300
|
+
| `FsWatcherChangeInfo` | `{ event: FsWatcherEvent; path: NormPath }` |
|
|
301
|
+
| `WorkerModule` | Worker 모듈 타입 구조 (타입 추론용) |
|
|
302
|
+
| `WorkerProxy<TModule>` | Worker 프록시 타입 (메서드 + `on`/`off`/`terminate`) |
|
|
303
|
+
| `PromisifyMethods<T>` | 메서드 반환값을 Promise로 래핑하는 매핑 타입 |
|
|
304
|
+
| `WorkerRequest` | 내부 Worker 요청 메시지 타입 |
|
|
305
|
+
| `WorkerResponse` | 내부 Worker 응답 메시지 타입 (return/error/event/log) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/core-node",
|
|
3
|
-
"version": "13.0.
|
|
3
|
+
"version": "13.0.86",
|
|
4
4
|
"description": "Simplysm package - Core module (node)",
|
|
5
5
|
"author": "simplysm",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -25,6 +25,6 @@
|
|
|
25
25
|
"glob": "^13.0.6",
|
|
26
26
|
"minimatch": "^10.2.4",
|
|
27
27
|
"tsx": "^4.21.0",
|
|
28
|
-
"@simplysm/core-common": "13.0.
|
|
28
|
+
"@simplysm/core-common": "13.0.86"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/docs/filesystem.md
DELETED
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
# Filesystem Utilities (`fsx`)
|
|
2
|
-
|
|
3
|
-
Imported as a namespace:
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
import { fsx } from "@simplysm/core-node";
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
All functions wrap Node.js `fs` operations with automatic `SdError` wrapping for improved stack traces. Most functions come in sync and async pairs.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## API Reference
|
|
14
|
-
|
|
15
|
-
### exists / existsSync
|
|
16
|
-
|
|
17
|
-
```typescript
|
|
18
|
-
function exists(targetPath: string): Promise<boolean>;
|
|
19
|
-
function existsSync(targetPath: string): boolean;
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
Checks if a file or directory exists.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
### mkdir / mkdirSync
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
function mkdir(targetPath: string): Promise<void>;
|
|
30
|
-
function mkdirSync(targetPath: string): void;
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Creates a directory recursively (like `mkdir -p`).
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
### rm / rmSync
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
function rm(targetPath: string): Promise<void>;
|
|
41
|
-
function rmSync(targetPath: string): void;
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Deletes a file or directory recursively with `force: true`.
|
|
45
|
-
|
|
46
|
-
- **`rm`** (async): Retries up to 6 times with a 500ms interval for transient errors (e.g., file locks).
|
|
47
|
-
- **`rmSync`**: Fails immediately without retries.
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
### copy / copySync
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
function copy(
|
|
55
|
-
sourcePath: string,
|
|
56
|
-
targetPath: string,
|
|
57
|
-
filter?: (absolutePath: string) => boolean,
|
|
58
|
-
): Promise<void>;
|
|
59
|
-
|
|
60
|
-
function copySync(
|
|
61
|
-
sourcePath: string,
|
|
62
|
-
targetPath: string,
|
|
63
|
-
filter?: (absolutePath: string) => boolean,
|
|
64
|
-
): void;
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Copies a file or directory. If `sourcePath` does not exist, returns silently.
|
|
68
|
-
|
|
69
|
-
**Parameters:**
|
|
70
|
-
- `sourcePath` -- Source path to copy from.
|
|
71
|
-
- `targetPath` -- Destination path.
|
|
72
|
-
- `filter` -- Optional filter receiving the absolute path of each child. Return `true` to include, `false` to exclude. The top-level `sourcePath` itself is not filtered; only its children (recursively) are subject to the filter. Returning `false` for a directory skips it and all its contents.
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
### read / readSync
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
function read(targetPath: string): Promise<string>;
|
|
80
|
-
function readSync(targetPath: string): string;
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
Reads a file as a UTF-8 string.
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
### readBuffer / readBufferSync
|
|
88
|
-
|
|
89
|
-
```typescript
|
|
90
|
-
function readBuffer(targetPath: string): Promise<Buffer>;
|
|
91
|
-
function readBufferSync(targetPath: string): Buffer;
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Reads a file as a Buffer.
|
|
95
|
-
|
|
96
|
-
---
|
|
97
|
-
|
|
98
|
-
### readJson / readJsonSync
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
function readJson<TData = unknown>(targetPath: string): Promise<TData>;
|
|
102
|
-
function readJsonSync<TData = unknown>(targetPath: string): TData;
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Reads and parses a JSON file using `JsonConvert` from `@simplysm/core-common`. On parse failure, the error includes a truncated preview of the file contents (up to 500 characters).
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
### write / writeSync
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
function write(targetPath: string, data: string | Uint8Array): Promise<void>;
|
|
113
|
-
function writeSync(targetPath: string, data: string | Uint8Array): void;
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Writes data to a file. Automatically creates parent directories if they do not exist. Uses `flush: true` for immediate disk write.
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
### writeJson / writeJsonSync
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
function writeJson(
|
|
124
|
-
targetPath: string,
|
|
125
|
-
data: unknown,
|
|
126
|
-
options?: {
|
|
127
|
-
replacer?: (this: unknown, key: string | undefined, value: unknown) => unknown;
|
|
128
|
-
space?: string | number;
|
|
129
|
-
},
|
|
130
|
-
): Promise<void>;
|
|
131
|
-
|
|
132
|
-
function writeJsonSync(
|
|
133
|
-
targetPath: string,
|
|
134
|
-
data: unknown,
|
|
135
|
-
options?: {
|
|
136
|
-
replacer?: (this: unknown, key: string | undefined, value: unknown) => unknown;
|
|
137
|
-
space?: string | number;
|
|
138
|
-
},
|
|
139
|
-
): void;
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Writes data to a JSON file using `JsonConvert`.
|
|
143
|
-
|
|
144
|
-
**Parameters:**
|
|
145
|
-
- `data` -- Data to serialize.
|
|
146
|
-
- `options.replacer` -- Custom JSON replacer function.
|
|
147
|
-
- `options.space` -- Indentation (number of spaces or string).
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
### readdir / readdirSync
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
function readdir(targetPath: string): Promise<string[]>;
|
|
155
|
-
function readdirSync(targetPath: string): string[];
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Reads the contents of a directory (file and directory names only, not full paths).
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
### stat / statSync
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
function stat(targetPath: string): Promise<fs.Stats>;
|
|
166
|
-
function statSync(targetPath: string): fs.Stats;
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
Gets file/directory information. Follows symbolic links.
|
|
170
|
-
|
|
171
|
-
---
|
|
172
|
-
|
|
173
|
-
### lstat / lstatSync
|
|
174
|
-
|
|
175
|
-
```typescript
|
|
176
|
-
function lstat(targetPath: string): Promise<fs.Stats>;
|
|
177
|
-
function lstatSync(targetPath: string): fs.Stats;
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
Gets file/directory information. Does **not** follow symbolic links.
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
### glob / globSync
|
|
185
|
-
|
|
186
|
-
```typescript
|
|
187
|
-
function glob(pattern: string, options?: GlobOptions): Promise<string[]>;
|
|
188
|
-
function globSync(pattern: string, options?: GlobOptions): string[];
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
Searches for files using a glob pattern. Backslashes in the pattern are automatically converted to forward slashes. Returns an array of **absolute paths**.
|
|
192
|
-
|
|
193
|
-
---
|
|
194
|
-
|
|
195
|
-
### clearEmptyDirectory
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
function clearEmptyDirectory(dirPath: string): Promise<void>;
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
Recursively searches and deletes empty directories under the specified path. If all children of a directory are deleted and it becomes empty, it is also removed.
|
|
202
|
-
|
|
203
|
-
---
|
|
204
|
-
|
|
205
|
-
### findAllParentChildPaths / findAllParentChildPathsSync
|
|
206
|
-
|
|
207
|
-
```typescript
|
|
208
|
-
function findAllParentChildPaths(
|
|
209
|
-
childGlob: string,
|
|
210
|
-
fromPath: string,
|
|
211
|
-
rootPath?: string,
|
|
212
|
-
): Promise<string[]>;
|
|
213
|
-
|
|
214
|
-
function findAllParentChildPathsSync(
|
|
215
|
-
childGlob: string,
|
|
216
|
-
fromPath: string,
|
|
217
|
-
rootPath?: string,
|
|
218
|
-
): string[];
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
Traverses parent directories from `fromPath` toward the root, collecting all file paths matching `childGlob` in each directory.
|
|
222
|
-
|
|
223
|
-
**Parameters:**
|
|
224
|
-
- `childGlob` -- Glob pattern to match in each directory.
|
|
225
|
-
- `fromPath` -- Starting directory.
|
|
226
|
-
- `rootPath` -- Optional boundary; stops searching at this directory. Must be a parent of `fromPath`.
|
|
227
|
-
|
|
228
|
-
---
|
|
229
|
-
|
|
230
|
-
## Usage Examples
|
|
231
|
-
|
|
232
|
-
```typescript
|
|
233
|
-
import { fsx } from "@simplysm/core-node";
|
|
234
|
-
|
|
235
|
-
// Read and write JSON
|
|
236
|
-
const config = await fsx.readJson<{ port: number }>("config.json");
|
|
237
|
-
await fsx.writeJson("output.json", { port: 3000 }, { space: 2 });
|
|
238
|
-
|
|
239
|
-
// Copy with filter (skip node_modules)
|
|
240
|
-
await fsx.copy("src", "dist", (absPath) => !absPath.includes("node_modules"));
|
|
241
|
-
|
|
242
|
-
// Find all package.json files from a subdirectory up to the project root
|
|
243
|
-
const packageFiles = fsx.findAllParentChildPathsSync(
|
|
244
|
-
"package.json",
|
|
245
|
-
"/project/packages/my-lib/src",
|
|
246
|
-
"/project",
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
// Clean up empty directories after a build
|
|
250
|
-
await fsx.clearEmptyDirectory("dist");
|
|
251
|
-
|
|
252
|
-
// Glob search
|
|
253
|
-
const tsFiles = await fsx.glob("src/**/*.ts");
|
|
254
|
-
```
|
package/docs/fs-watcher.md
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
# File Watcher (`FsWatcher`)
|
|
2
|
-
|
|
3
|
-
```typescript
|
|
4
|
-
import { FsWatcher } from "@simplysm/core-node";
|
|
5
|
-
```
|
|
6
|
-
|
|
7
|
-
A chokidar-based file system watcher that debounces and merges rapid file change events, delivering a single consolidated callback.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## API Reference
|
|
12
|
-
|
|
13
|
-
### FsWatcherEvent
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
type FsWatcherEvent = "add" | "addDir" | "change" | "unlink" | "unlinkDir";
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Supported file change event types.
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
### FsWatcherChangeInfo
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
interface FsWatcherChangeInfo {
|
|
27
|
-
event: FsWatcherEvent;
|
|
28
|
-
path: NormPath;
|
|
29
|
-
}
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Describes a single file change with its event type and normalized path.
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
### FsWatcher
|
|
37
|
-
|
|
38
|
-
#### FsWatcher.watch
|
|
39
|
-
|
|
40
|
-
```typescript
|
|
41
|
-
static async watch(
|
|
42
|
-
paths: string[],
|
|
43
|
-
options?: chokidar.ChokidarOptions,
|
|
44
|
-
): Promise<FsWatcher>;
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Starts watching files and resolves once the watcher is ready.
|
|
48
|
-
|
|
49
|
-
**Parameters:**
|
|
50
|
-
- `paths` -- Array of file paths, directory paths, or glob patterns to watch.
|
|
51
|
-
- `options` -- Chokidar options. Note: `ignoreInitial` is internally forced to `true`.
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
#### FsWatcher#onChange
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
onChange(
|
|
59
|
-
opt: { delay?: number },
|
|
60
|
-
cb: (changeInfos: FsWatcherChangeInfo[]) => void | Promise<void>,
|
|
61
|
-
): this;
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Registers a change event handler. Events occurring within the `delay` window are merged and delivered in a single callback.
|
|
65
|
-
|
|
66
|
-
**Parameters:**
|
|
67
|
-
- `opt.delay` -- Debounce delay in milliseconds.
|
|
68
|
-
- `cb` -- Callback receiving an array of merged change events.
|
|
69
|
-
|
|
70
|
-
**Event merging strategy:**
|
|
71
|
-
|
|
72
|
-
When multiple events occur for the same file within the debounce window:
|
|
73
|
-
|
|
74
|
-
| Previous Event | New Event | Result |
|
|
75
|
-
|---|---|---|
|
|
76
|
-
| `add` | `change` | `add` (modification right after creation) |
|
|
77
|
-
| `add` | `unlink` | removed (created then deleted = no change) |
|
|
78
|
-
| `unlink` | `add`/`change` | `add` (recreated after deletion) |
|
|
79
|
-
| `addDir` | `unlinkDir` | removed (no change) |
|
|
80
|
-
| `unlinkDir` | `addDir` | `addDir` (recreated) |
|
|
81
|
-
| any | any (other) | latest event wins |
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
#### FsWatcher#close
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
async close(): Promise<void>;
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Closes the file watcher and disposes all debounce queues.
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## Usage Examples
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
import { FsWatcher } from "@simplysm/core-node";
|
|
99
|
-
|
|
100
|
-
// Watch TypeScript files with 300ms debounce
|
|
101
|
-
const watcher = await FsWatcher.watch(["src/**/*.ts"]);
|
|
102
|
-
|
|
103
|
-
watcher.onChange({ delay: 300 }, (changes) => {
|
|
104
|
-
for (const { path, event } of changes) {
|
|
105
|
-
console.log(`${event}: ${path}`);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// Watch multiple patterns
|
|
110
|
-
const multiWatcher = await FsWatcher.watch([
|
|
111
|
-
"src/**/*.ts",
|
|
112
|
-
"config/**/*.json",
|
|
113
|
-
]);
|
|
114
|
-
|
|
115
|
-
multiWatcher.onChange({ delay: 500 }, async (changes) => {
|
|
116
|
-
const tsChanges = changes.filter((c) => c.path.endsWith(".ts"));
|
|
117
|
-
const configChanges = changes.filter((c) => c.path.endsWith(".json"));
|
|
118
|
-
|
|
119
|
-
if (tsChanges.length > 0) {
|
|
120
|
-
// Rebuild TypeScript
|
|
121
|
-
}
|
|
122
|
-
if (configChanges.length > 0) {
|
|
123
|
-
// Reload configuration
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// Clean up
|
|
128
|
-
await watcher.close();
|
|
129
|
-
await multiWatcher.close();
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### ignoreInitial behavior
|
|
133
|
-
|
|
134
|
-
By default, `ignoreInitial` is `true` and the callback is not invoked until an actual change occurs. If set to `false` in options, the callback is called once immediately with an empty array (the actual initial file list is not included -- this is intentional to avoid conflicts with event merging).
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
const watcher = await FsWatcher.watch(["src/**/*.ts"], {
|
|
138
|
-
ignoreInitial: false,
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
watcher.onChange({ delay: 300 }, (changes) => {
|
|
142
|
-
// First call: changes = [] (initial trigger)
|
|
143
|
-
// Subsequent calls: actual file changes
|
|
144
|
-
});
|
|
145
|
-
```
|
package/docs/path.md
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# Path Utilities (`pathx`)
|
|
2
|
-
|
|
3
|
-
Imported as a namespace:
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
import { pathx } from "@simplysm/core-node";
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
Provides path normalization, POSIX conversion, child path detection, and target-based filtering utilities.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## API Reference
|
|
14
|
-
|
|
15
|
-
### NormPath
|
|
16
|
-
|
|
17
|
-
```typescript
|
|
18
|
-
type NormPath = string & { [NORM]: never };
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
A branded string type representing a normalized absolute path. Can only be created through `norm()`. Useful for ensuring path consistency throughout an application.
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
### posix
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
function posix(...args: string[]): string;
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Converts path segments to a POSIX-style path (backslashes replaced with forward slashes). Segments are joined using `path.join()` before conversion.
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
pathx.posix("C:\\Users\\test"); // "C:/Users/test"
|
|
35
|
-
pathx.posix("src", "index.ts"); // "src/index.ts"
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
### changeFileDirectory
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
function changeFileDirectory(
|
|
44
|
-
filePath: string,
|
|
45
|
-
fromDirectory: string,
|
|
46
|
-
toDirectory: string,
|
|
47
|
-
): string;
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Changes the base directory of a file path. Throws `ArgumentError` if `filePath` is not inside `fromDirectory`.
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
pathx.changeFileDirectory("/a/b/c.txt", "/a", "/x");
|
|
54
|
-
// "/x/b/c.txt"
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
### basenameWithoutExt
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
function basenameWithoutExt(filePath: string): string;
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Returns the filename without extension.
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
pathx.basenameWithoutExt("file.txt"); // "file"
|
|
69
|
-
pathx.basenameWithoutExt("/path/to/file.spec.ts"); // "file.spec"
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
### isChildPath
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
function isChildPath(childPath: string, parentPath: string): boolean;
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Checks if `childPath` is a child of `parentPath`. Returns `false` if the paths are identical. Paths are normalized internally.
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
pathx.isChildPath("/a/b/c", "/a/b"); // true
|
|
84
|
-
pathx.isChildPath("/a/b", "/a/b/c"); // false
|
|
85
|
-
pathx.isChildPath("/a/b", "/a/b"); // false (same path)
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
### norm
|
|
91
|
-
|
|
92
|
-
```typescript
|
|
93
|
-
function norm(...paths: string[]): NormPath;
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Normalizes and resolves path segments to an absolute `NormPath`. Uses platform-specific separators.
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
const p = pathx.norm("/some/path"); // NormPath
|
|
100
|
-
const q = pathx.norm("relative", "path"); // NormPath (resolved to absolute)
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
### filterByTargets
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
function filterByTargets(
|
|
109
|
-
files: string[],
|
|
110
|
-
targets: string[],
|
|
111
|
-
cwd: string,
|
|
112
|
-
): string[];
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
Filters an array of absolute file paths to only include those that match or are children of the given target paths.
|
|
116
|
-
|
|
117
|
-
**Parameters:**
|
|
118
|
-
- `files` -- Absolute file paths to filter.
|
|
119
|
-
- `targets` -- Target paths relative to `cwd` (POSIX style recommended).
|
|
120
|
-
- `cwd` -- Current working directory (absolute path).
|
|
121
|
-
|
|
122
|
-
Returns `files` unmodified if `targets` is empty.
|
|
123
|
-
|
|
124
|
-
```typescript
|
|
125
|
-
const files = ["/proj/src/a.ts", "/proj/src/b.ts", "/proj/tests/c.ts"];
|
|
126
|
-
pathx.filterByTargets(files, ["src"], "/proj");
|
|
127
|
-
// ["/proj/src/a.ts", "/proj/src/b.ts"]
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## Usage Examples
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
import { pathx } from "@simplysm/core-node";
|
|
136
|
-
|
|
137
|
-
// Normalize paths for consistent comparison
|
|
138
|
-
const a = pathx.norm("/project/src/../lib");
|
|
139
|
-
const b = pathx.norm("/project/lib");
|
|
140
|
-
console.log(a === b); // true
|
|
141
|
-
|
|
142
|
-
// Remap file paths from one directory to another
|
|
143
|
-
const newPath = pathx.changeFileDirectory(
|
|
144
|
-
"/build/src/utils/helper.ts",
|
|
145
|
-
"/build/src",
|
|
146
|
-
"/dist",
|
|
147
|
-
);
|
|
148
|
-
// "/dist/utils/helper.ts"
|
|
149
|
-
|
|
150
|
-
// Filter files by target directories
|
|
151
|
-
const allFiles = ["/proj/src/a.ts", "/proj/docs/b.md", "/proj/tests/c.ts"];
|
|
152
|
-
const srcOnly = pathx.filterByTargets(allFiles, ["src", "tests"], "/proj");
|
|
153
|
-
// ["/proj/src/a.ts", "/proj/tests/c.ts"]
|
|
154
|
-
```
|
package/docs/worker.md
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
# Worker
|
|
2
|
-
|
|
3
|
-
```typescript
|
|
4
|
-
import { createWorker, Worker } from "@simplysm/core-node";
|
|
5
|
-
```
|
|
6
|
-
|
|
7
|
-
Type-safe wrapper around Node.js `worker_threads`. Define methods in a worker file with `createWorker`, then call them from the main thread via `Worker.create` with full type inference.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## API Reference
|
|
12
|
-
|
|
13
|
-
### createWorker
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
function createWorker<
|
|
17
|
-
TMethods extends Record<string, (...args: any[]) => unknown>,
|
|
18
|
-
TEvents extends Record<string, unknown> = Record<string, never>,
|
|
19
|
-
>(
|
|
20
|
-
methods: TMethods,
|
|
21
|
-
): {
|
|
22
|
-
send<TEventName extends keyof TEvents & string>(
|
|
23
|
-
event: TEventName,
|
|
24
|
-
data?: TEvents[TEventName],
|
|
25
|
-
): void;
|
|
26
|
-
__methods: TMethods;
|
|
27
|
-
__events: TEvents;
|
|
28
|
-
};
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Factory function for defining a worker module. Must be the default export of the worker file.
|
|
32
|
-
|
|
33
|
-
**Parameters:**
|
|
34
|
-
- `methods` -- Object mapping method names to handler functions.
|
|
35
|
-
|
|
36
|
-
**Returns** an object with:
|
|
37
|
-
- `send(event, data?)` -- Sends a custom event from the worker to the main thread.
|
|
38
|
-
- `__methods` / `__events` -- Type-only markers used for inference (not accessed at runtime).
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
### Worker.create
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
const Worker: {
|
|
46
|
-
create<TModule extends WorkerModule>(
|
|
47
|
-
filePath: string,
|
|
48
|
-
opt?: Omit<WorkerOptions, "stdout" | "stderr">,
|
|
49
|
-
): WorkerProxy<TModule>;
|
|
50
|
-
};
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Creates a type-safe worker proxy. In development (`.ts` files), the worker is executed via `tsx` automatically. In production (`.js` files), the Worker is created directly.
|
|
54
|
-
|
|
55
|
-
**Parameters:**
|
|
56
|
-
- `filePath` -- Worker file path (absolute path or `file://` URL).
|
|
57
|
-
- `opt` -- Standard `WorkerOptions` (except `stdout`/`stderr`, which are managed internally).
|
|
58
|
-
|
|
59
|
-
**Returns** a `WorkerProxy` with:
|
|
60
|
-
- All worker methods available as async functions.
|
|
61
|
-
- `on(event, listener)` -- Subscribe to worker events.
|
|
62
|
-
- `off(event, listener)` -- Unsubscribe from worker events.
|
|
63
|
-
- `terminate()` -- Terminates the worker thread.
|
|
64
|
-
|
|
65
|
-
---
|
|
66
|
-
|
|
67
|
-
### WorkerModule
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
interface WorkerModule {
|
|
71
|
-
default: {
|
|
72
|
-
__methods: Record<string, (...args: any[]) => unknown>;
|
|
73
|
-
__events: Record<string, unknown>;
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Type structure of a worker module. Used for type inference with `Worker.create<typeof import("./worker")>()`.
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
### WorkerProxy
|
|
83
|
-
|
|
84
|
-
```typescript
|
|
85
|
-
type WorkerProxy<TModule extends WorkerModule> = PromisifyMethods<
|
|
86
|
-
TModule["default"]["__methods"]
|
|
87
|
-
> & {
|
|
88
|
-
on<K extends keyof TModule["default"]["__events"] & string>(
|
|
89
|
-
event: K,
|
|
90
|
-
listener: (data: TModule["default"]["__events"][K]) => void,
|
|
91
|
-
): void;
|
|
92
|
-
off<K extends keyof TModule["default"]["__events"] & string>(
|
|
93
|
-
event: K,
|
|
94
|
-
listener: (data: TModule["default"]["__events"][K]) => void,
|
|
95
|
-
): void;
|
|
96
|
-
terminate(): Promise<void>;
|
|
97
|
-
};
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
The proxy type returned by `Worker.create()`. All worker methods are promisified (return `Promise<Awaited<R>>`).
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
### PromisifyMethods
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
type PromisifyMethods<TMethods> = {
|
|
108
|
-
[K in keyof TMethods]: TMethods[K] extends (...args: infer P) => infer R
|
|
109
|
-
? (...args: P) => Promise<Awaited<R>>
|
|
110
|
-
: never;
|
|
111
|
-
};
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Utility type that wraps all method return values in `Promise`. Worker communication is always asynchronous via `postMessage`.
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
## Usage Examples
|
|
119
|
-
|
|
120
|
-
### Basic worker
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
// math-worker.ts
|
|
124
|
-
import { createWorker } from "@simplysm/core-node";
|
|
125
|
-
|
|
126
|
-
export default createWorker({
|
|
127
|
-
add: (a: number, b: number) => a + b,
|
|
128
|
-
multiply: (a: number, b: number) => a * b,
|
|
129
|
-
});
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
// main.ts
|
|
134
|
-
import { Worker } from "@simplysm/core-node";
|
|
135
|
-
|
|
136
|
-
const worker = Worker.create<typeof import("./math-worker")>(
|
|
137
|
-
new URL("./math-worker.ts", import.meta.url).href,
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
const sum = await worker.add(10, 20); // 30
|
|
141
|
-
const product = await worker.multiply(3, 4); // 12
|
|
142
|
-
|
|
143
|
-
await worker.terminate();
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### Worker with events
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
// process-worker.ts
|
|
150
|
-
import { createWorker } from "@simplysm/core-node";
|
|
151
|
-
|
|
152
|
-
interface ProcessEvents {
|
|
153
|
-
progress: number;
|
|
154
|
-
status: string;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const methods = {
|
|
158
|
-
processData: async (items: string[]) => {
|
|
159
|
-
const results: string[] = [];
|
|
160
|
-
for (let i = 0; i < items.length; i++) {
|
|
161
|
-
sender.send("progress", ((i + 1) / items.length) * 100);
|
|
162
|
-
sender.send("status", `Processing ${items[i]}`);
|
|
163
|
-
results.push(items[i].toUpperCase());
|
|
164
|
-
}
|
|
165
|
-
return results;
|
|
166
|
-
},
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
const sender = createWorker<typeof methods, ProcessEvents>(methods);
|
|
170
|
-
export default sender;
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
```typescript
|
|
174
|
-
// main.ts
|
|
175
|
-
import { Worker } from "@simplysm/core-node";
|
|
176
|
-
|
|
177
|
-
const worker = Worker.create<typeof import("./process-worker")>(
|
|
178
|
-
new URL("./process-worker.ts", import.meta.url).href,
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
worker.on("progress", (pct) => {
|
|
182
|
-
console.log(`Progress: ${pct}%`);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
worker.on("status", (msg) => {
|
|
186
|
-
console.log(`Status: ${msg}`);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
const results = await worker.processData(["hello", "world"]);
|
|
190
|
-
// Progress: 50%
|
|
191
|
-
// Status: Processing hello
|
|
192
|
-
// Progress: 100%
|
|
193
|
-
// Status: Processing world
|
|
194
|
-
// results: ["HELLO", "WORLD"]
|
|
195
|
-
|
|
196
|
-
await worker.terminate();
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Worker with options
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
const worker = Worker.create<typeof import("./my-worker")>(
|
|
203
|
-
new URL("./my-worker.ts", import.meta.url).href,
|
|
204
|
-
{
|
|
205
|
-
env: { NODE_ENV: "production" },
|
|
206
|
-
argv: ["--verbose"],
|
|
207
|
-
},
|
|
208
|
-
);
|
|
209
|
-
```
|