@simplysm/sd-claude 14.0.75 → 14.0.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/claude/output-styles/sd-tone.md +128 -0
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +28 -89
  3. package/claude/references/sd-simplysm14/apis/angular/app-structure.md +75 -32
  4. package/claude/references/sd-simplysm14/apis/angular/buttons.md +65 -29
  5. package/claude/references/sd-simplysm14/apis/angular/crud.md +86 -21
  6. package/claude/references/sd-simplysm14/apis/angular/forms.md +168 -42
  7. package/claude/references/sd-simplysm14/apis/angular/infrastructure.md +200 -49
  8. package/claude/references/sd-simplysm14/apis/angular/kanban.md +64 -20
  9. package/claude/references/sd-simplysm14/apis/angular/layout.md +75 -30
  10. package/claude/references/sd-simplysm14/apis/angular/modal.md +92 -40
  11. package/claude/references/sd-simplysm14/apis/angular/routing.md +86 -25
  12. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +72 -41
  13. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +113 -21
  14. package/claude/references/sd-simplysm14/apis/angular/sheet.md +108 -33
  15. package/claude/references/sd-simplysm14/apis/angular/toast.md +81 -30
  16. package/claude/references/sd-simplysm14/apis/angular/visual.md +140 -32
  17. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +46 -43
  18. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +59 -48
  19. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +17 -7
  20. package/claude/references/sd-simplysm14/apis/core-common/README.md +43 -116
  21. package/claude/references/sd-simplysm14/apis/core-common/extensions.md +74 -109
  22. package/claude/references/sd-simplysm14/apis/core-common/features.md +40 -35
  23. package/claude/references/sd-simplysm14/apis/core-common/types.md +80 -106
  24. package/claude/references/sd-simplysm14/apis/core-common/utils.md +142 -111
  25. package/claude/references/sd-simplysm14/apis/core-node/README.md +7 -16
  26. package/claude/references/sd-simplysm14/apis/core-node/consola.md +33 -38
  27. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +25 -33
  28. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +27 -38
  29. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +32 -60
  30. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +14 -45
  31. package/claude/references/sd-simplysm14/apis/core-node/worker.md +35 -81
  32. package/claude/references/sd-simplysm14/apis/excel/README.md +178 -80
  33. package/claude/references/sd-simplysm14/apis/lint/README.md +5 -0
  34. package/claude/references/sd-simplysm14/apis/orm-node/README.md +1 -1
  35. package/claude/references/sd-simplysm14/apis/sd-claude/README.md +28 -5
  36. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +1 -1
  37. package/claude/references/sd-simplysm14/apis/service-client/README.md +57 -50
  38. package/claude/references/sd-simplysm14/apis/service-server/README.md +8 -15
  39. package/claude/references/sd-simplysm14/apis/service-server/auth.md +24 -16
  40. package/claude/references/sd-simplysm14/apis/service-server/builtin-services.md +55 -31
  41. package/claude/references/sd-simplysm14/apis/service-server/define-service.md +28 -44
  42. package/claude/references/sd-simplysm14/apis/service-server/internals.md +59 -18
  43. package/claude/references/sd-simplysm14/apis/service-server/server.md +37 -46
  44. package/claude/references/sd-simplysm14/manuals/client-component.md +3 -1
  45. package/claude/references/sd-simplysm14/manuals/logging.md +9 -8
  46. package/claude/rules/sd-base-rules.md +380 -217
  47. package/claude/settings.json +1 -0
  48. package/claude/skills/sd-commit/SKILL.md +31 -8
  49. package/claude/skills/sd-docs/SKILL.md +15 -10
  50. package/claude/skills/sd-docs/references/subagent-prompt.md +26 -8
  51. package/claude/skills/sd-impl/SKILL.md +1 -1
  52. package/claude/skills/sd-skill/references/skill-authoring.md +1 -1
  53. package/claude/skills/sd-spec/SKILL.md +22 -13
  54. package/claude/skills/sd-spec/references/spec-authoring.md +1 -1
  55. package/claude/skills/sd-unpack/SKILL.md +150 -26
  56. package/claude/skills/sd-unpack/scripts/handlers/__pycache__/_common.cpython-314.pyc +0 -0
  57. package/claude/skills/sd-unpack/scripts/handlers/__pycache__/eml_handler.cpython-314.pyc +0 -0
  58. package/claude/skills/sd-unpack/scripts/handlers/__pycache__/office_com.cpython-314.pyc +0 -0
  59. package/claude/skills/sd-unpack/scripts/handlers/__pycache__/pdf_handler.cpython-314.pyc +0 -0
  60. package/claude/skills/sd-unpack/scripts/handlers/_common.py +17 -2
  61. package/claude/skills/sd-unpack/scripts/handlers/eml_handler.py +100 -24
  62. package/claude/skills/sd-unpack/scripts/handlers/msg_handler.py +140 -27
  63. package/claude/skills/sd-unpack/scripts/handlers/office_com.py +698 -107
  64. package/claude/skills/sd-unpack/scripts/handlers/office_worker.py +34 -26
  65. package/claude/skills/sd-unpack/scripts/handlers/pdf_handler.py +130 -8
  66. package/package.json +1 -1
@@ -1,81 +1,53 @@
1
1
  ## @simplysm/core-node — fsx
2
2
 
3
- `import { fsx } from "@simplysm/core-node"` 파일/디렉토리 IO 헬퍼. 동기·비동기 쌍으로 제공. 모든 함수는 오류를 `SdError(원본, targetPath)` 로 wrap 해서 throw.
3
+ `import { fsx } from "@simplysm/core-node"`. 모든 함수는 동기(`*Sync`) + 비동기 제공. 실패 경로 정보를 포함한 `SdError` throw.
4
4
 
5
- ### 존재/생성/삭제
5
+ ### 존재 확인 / 디렉토리
6
6
 
7
- ```ts
8
- fsx.existsSync(p): boolean
9
- fsx.exists(p): Promise<boolean>
10
- fsx.mkdirSync(p): void // recursive
11
- fsx.mkdir(p): Promise<void> // recursive
12
- fsx.rmSync(p): void // recursive, force, 재시도 없음
13
- fsx.rm(p): Promise<void> // recursive, force, 6회/500ms 재시도 (파일 잠금 대응)
14
- ```
7
+ - `existsSync(p) / exists(p): boolean | Promise<boolean>` — 파일·디렉토리 존재 여부.
8
+ - `mkdirSync(p) / mkdir(p): void | Promise<void>` — 재귀 생성. 이미 있으면 무시.
9
+ - `readdirSync(p) / readdir(p): string[]` — 자식 이름 배열(절대 경로 아님).
15
10
 
16
- 동기 rm 은 재시도 없음 — Windows 잠금 가능성 있으면 비동기 `rm` 사용.
11
+ ### 삭제
12
+
13
+ - `rmSync(p)` — 재귀+force. **재시도 없음**, 파일 잠금 시 즉시 실패.
14
+ - `rm(p)` — 재귀+force, **최대 6회 / 500ms 간격 재시도**. 일시적 잠금 회피용.
17
15
 
18
16
  ### 복사
19
17
 
20
- ```ts
21
- fsx.copySync(src, dst, filter?): void
22
- fsx.copy(src, dst, filter?): Promise<void>
23
- ```
18
+ - `copySync(src, dst, filter?) / copy(src, dst, filter?)` — 재귀 복사.
19
+ - `src`: 원본 경로. 존재하지 않으면 no-op.
20
+ - `dst`: 대상 경로. 상위 디렉토리 자동 생성.
21
+ - `filter(absolutePath): boolean`: 하위 항목 1개에 대한 포함 여부. true=포함, false=제외. **최상위 src 는 적용 대상 아님**. 디렉토리에 false 반환 시 그 하위 전체 스킵.
22
+ - 파일 복사 실패 시 최대 6회 / 500ms 재시도.
24
23
 
25
- - `src` 없으면 no-op.
26
- - `filter(absolutePath): boolean` — 각 하위 항목 절대 경로로 호출. 최상위 `src` 자체는 필터 대상 아님. 디렉토리에 false 반환 시 그 하위 전체 스킵.
27
- - 파일 복사 6회/500ms 재시도 (sync 는 busy-wait).
24
+ ### 읽기 / 쓰기
28
25
 
29
- ### 읽기/쓰기
26
+ - `readSync(p) / read(p): string` — UTF-8 텍스트.
27
+ - `readBytesSync(p) / readBytes(p): Uint8Array` — 바이너리.
28
+ - `readJsonSync<T>(p) / readJson<T>(p): T` — `@simplysm/core-common` 의 `json.parse` 사용. 파싱 실패 시 본문 앞 500자 미리보기 포함 에러.
29
+ - `writeSync(p, data) / write(p, data)` — `data: string | Uint8Array`. 상위 디렉토리 자동 생성, `flush: true`.
30
+ - `writeJsonSync(p, data, opts?) / writeJson(p, data, opts?)` — `opts.replacer`: JSON.stringify 와 동일한 replacer. `opts.space`: 들여쓰기(숫자=스페이스 수, 문자열=리터럴).
30
31
 
31
- ```ts
32
- fsx.readSync(p): string // utf-8
33
- fsx.read(p): Promise<string>
34
- fsx.readBytesSync(p): Uint8Array
35
- fsx.readBytes(p): Promise<Uint8Array>
36
- fsx.readJsonSync<T>(p): T // @simplysm/core-common json.parse
37
- fsx.readJson<T>(p): Promise<T>
38
-
39
- fsx.writeSync(p, data: string|Uint8Array): void // 상위 디렉토리 자동 생성, flush:true
40
- fsx.write(p, data): Promise<void>
41
- fsx.writeJsonSync(p, data, { replacer?, space? }?): void
42
- fsx.writeJson(p, data, { replacer?, space? }?): Promise<void>
43
- ```
32
+ ### Stats
44
33
 
45
- JSON 파싱 실패 `SdError` 메시지에 파일 경로 + 본문 500자 미리보기 포함.
34
+ - `statSync / stat` `fs.Stats`. 심볼릭 링크 따라감.
35
+ - `lstatSync / lstat` — `fs.Stats`. 심볼릭 링크 안 따라감 (링크 자체 정보).
46
36
 
47
- ### 디렉토리/정보
37
+ ### Glob
48
38
 
49
- ```ts
50
- fsx.readdirSync(p): string[]
51
- fsx.readdir(p): Promise<string[]>
52
- fsx.statSync(p): fs.Stats // 심볼릭 링크 따라감
53
- fsx.stat(p): Promise<fs.Stats>
54
- fsx.lstatSync(p): fs.Stats // 심볼릭 링크 따라가지 않음
55
- fsx.lstat(p): Promise<fs.Stats>
56
- ```
57
-
58
- ### Glob / 트리 유틸
39
+ - `globSync(pattern, options?) / glob(pattern, options?): string[]` — `glob` 패키지 래핑. 패턴 내 백슬래시는 슬래시로 치환 후 호출. 결과는 **항상 절대 경로**로 정규화.
40
+ - `options`: `glob` 패키지의 `GlobOptions`(예: `dot: true` 로 dotfile 포함).
59
41
 
60
- ```ts
61
- fsx.globSync(pattern, options?: GlobOptions): string[] // 항상 절대 경로, 백슬래시 → 슬래시
62
- fsx.glob(pattern, options?): Promise<string[]>
42
+ ### 트리 유틸
63
43
 
64
- fsx.clearEmptyDirectory(dirPath): Promise<void> // 디렉토리 재귀 삭제
44
+ - `clearEmptyDirectory(dirPath)` 하위까지 재귀 순회, 파일이 없는 디렉토리만 삭제. 파일이 하나라도 있으면 보존.
45
+ - `findAllParentChildPathsSync(childGlob, fromPath, rootPath?) / findAllParentChildPaths(...)` — `fromPath` 부터 루트 방향으로 부모 디렉토리를 따라가며 각 디렉토리에서 `childGlob` 패턴 매칭 파일을 수집. `rootPath` 도달 시 종료(지정 없으면 FS 루트까지). 모노레포 루트 탐색 등에 사용.
65
46
 
66
- fsx.findAllParentChildPathsSync(childGlob, fromPath, rootPath?): string[]
67
- fsx.findAllParentChildPaths(childGlob, fromPath, rootPath?): Promise<string[]>
68
- ```
69
-
70
- `findAllParentChildPaths*`: `fromPath` 에서 루트 방향으로 부모를 순회하며 각 디렉토리에 `childGlob` 적용. `rootPath` 미지정 또는 `fromPath` 가 `rootPath` 하위가 아니면 파일 시스템 루트까지 진행. `pnpm-workspace.yaml` 같은 ancestor 설정 파일 탐색에 사용.
71
-
72
- 사용 예:
47
+ ###
73
48
 
74
49
  ```ts
75
50
  import { fsx } from "@simplysm/core-node";
76
-
77
- await fsx.mkdir(path.resolve(out, "dist"));
78
- const tsFiles = await fsx.glob("src/**/*.ts");
79
- const cfg = await fsx.readJson<MyConfig>("sd.config.json");
80
- await fsx.copy(src, dst, (p) => !p.includes(`${path.sep}node_modules${path.sep}`));
51
+ await fsx.copy("src", "dist", (p) => !p.includes("node_modules"));
52
+ const cfg = await fsx.readJson<{ name: string }>("package.json");
81
53
  ```
@@ -1,55 +1,24 @@
1
1
  ## @simplysm/core-node — pathx
2
2
 
3
- `import { pathx } from "@simplysm/core-node"` — 경로 변환·비교 유틸. POSIX 스타일 통일과 cwd 기준 target 필터링용.
3
+ `import { pathx } from "@simplysm/core-node"`. POSIX 슬래시 경로 정규화·비교·필터링.
4
4
 
5
- ### PosixPath 브랜드 타입
5
+ ### 타입
6
6
 
7
- ```ts
8
- type PosixPath = string & { [POSIX]: never };
9
- ```
10
-
11
- `posix()` 또는 `posixResolve()` 결과에만 부여. 슬래시 경로임을 타입으로 보장.
12
-
13
- ### 변환
14
-
15
- ```ts
16
- pathx.posix(p: string): PosixPath
17
- // 백슬래시 → 슬래시 만. resolve 안 함.
18
- // posix("C:\\Users\\test") === "C:/Users/test"
19
-
20
- pathx.posixResolve(...args: string[]): PosixPath
21
- // path.resolve 후 슬래시화. 항상 절대 경로.
22
- ```
23
-
24
- ### 비교/판정
7
+ - `PosixPath` — `string & { [POSIX]: never }` 브랜드 타입. `posix()` / `posixResolve()` 만 생성 가능. 글로브·minimatch·URL 호환 경로 표현용.
25
8
 
26
- ```ts
27
- pathx.isChildPath(child, parent): boolean
28
- // posixResolve 정규화 후 비교. 동일 경로면 false.
29
-
30
- pathx.changeFileDirectory(filePath, fromDir, toDir): string
31
- // filePath 의 fromDir 부분을 toDir 로 치환.
32
- // filePath === fromDir 이면 toDir 반환.
33
- // filePath 가 fromDir 하위가 아니면 ArgumentError throw.
34
-
35
- pathx.basenameWithoutExt(filePath): string
36
- // path.basename(p, path.extname(p)). "a/b/c.spec.ts" → "c.spec"
37
- ```
38
-
39
- ### target 필터링
40
-
41
- ```ts
42
- pathx.filterByTargets(files: string[], targets: string[], cwd: string): string[]
43
- ```
9
+ ### 함수
44
10
 
45
- - `targets` 비면 `files` 그대로 반환.
46
- - `target` cwd 기준 상대 경로 (POSIX 권장).
47
- - `files` 원소를 cwd 기준 상대 POSIX변환 `relative === target || relative.startsWith(target + "/")` 매칭.
48
- - `files` cwd 하위 절대 경로 가정 외부 경로는 `../` 로 변환되어 매칭에서 빠짐.
11
+ - `posix(p): PosixPath` 백슬래시→슬래시 치환만. resolve 안 함.
12
+ - `posixResolve(...args): PosixPath``path.resolve(...args)` 슬래시 치환. 절대 경로 보장.
13
+ - `changeFileDirectory(filePath, fromDirectory, toDirectory): string` `filePath` 디렉토리 prefix `fromDirectory`→`toDirectory` 갈아끼움. `filePath === fromDirectory` `toDirectory` 반환. `filePath` 가 `fromDirectory` 하위가 아니면 `ArgumentError` throw.
14
+ - `basenameWithoutExt(filePath): string` 마지막 확장자 1개 제거한 basename. `"a.spec.ts"` `"a.spec"`.
15
+ - `isChildPath(childPath, parentPath): boolean` — `child` 가 `parent` **엄격 하위**인지 (동일 경로면 false). 내부적으로 `posixResolve` 정규화 후 `parent + "/"` prefix 매칭.
16
+ - `filterByTargets(files, targets, cwd): string[]` — `files`(cwd 하위 절대 경로 배열)에서 `targets`(cwd 기준 상대 POSIX 경로) 와 일치 또는 하위 항목만 통과. `targets` 가 빈 배열이면 `files` 그대로 반환. sd-cli 의 `-t` 옵션 처리에 사용.
49
17
 
50
- CLI 의 `-t <project>` 처럼 사용자 지정 부분 경로로 후보를 좁힐 때 사용.
18
+ ###
51
19
 
52
20
  ```ts
53
- const files = ["/proj/src/a.ts", "/proj/tests/c.ts"];
54
- pathx.filterByTargets(files, ["src"], "/proj"); // ["/proj/src/a.ts"]
21
+ import { pathx } from "@simplysm/core-node";
22
+ const out = pathx.changeFileDirectory("/proj/src/a.ts", "/proj/src", "/proj/dist"); // /proj/dist/a.ts
23
+ pathx.isChildPath("/a/b/c", "/a/b"); // true
55
24
  ```
@@ -1,111 +1,65 @@
1
1
  ## @simplysm/core-node — Worker
2
2
 
3
- `worker_threads` 위의 타입 안전 RPC. 워커 측에서 `createWorker(methods)` default export 하면, 메인 측에서 `Worker.create<typeof import("./worker")>(path)` 로 메서드 직접 호출 + 이벤트 수신 가능한 Proxy 를 얻는다. 메시지 인코딩은 `@simplysm/core-common``transfer.encode/decode` 사용 (Uint8Array 등 transferable 자동 처리).
3
+ 타입 안전한 `worker_threads` 래퍼. 메인에서는 `Worker.create()` 로 프록시 호출, 워커에서는 `createWorker()` 메서드·이벤트 노출.
4
4
 
5
- ### 타입
5
+ ### 워커 스크립트 — `createWorker`
6
6
 
7
7
  ```ts
8
- interface WorkerModule {
9
- default: {
10
- __methods: Record<string, (...args: any[]) => unknown>;
11
- __events: Record<string, unknown>;
12
- };
8
+ createWorker<TMethods, TEvents = Record<string, never>>(methods: TMethods): {
9
+ send<K extends keyof TEvents & string>(event: K, data?: TEvents[K]): void;
10
+ __methods: TMethods;
11
+ __events: TEvents;
13
12
  }
14
-
15
- type PromisifyMethods<T> = {
16
- [K in keyof T]: T[K] extends (...args: infer P) => infer R
17
- ? (...args: P) => Promise<Awaited<R>>
18
- : never;
19
- };
20
-
21
- type WorkerProxy<TModule extends WorkerModule> =
22
- PromisifyMethods<TModule["default"]["__methods"]> & {
23
- on<E extends keyof TModule["default"]["__events"] & string>(
24
- event: E, listener: (data: TModule["default"]["__events"][E]) => void,
25
- ): void;
26
- off<E extends keyof TModule["default"]["__events"] & string>(event: E, listener): void;
27
- terminate(): Promise<void>;
28
- };
29
13
  ```
30
14
 
31
- 메서드는 항상 `Promise<Awaited<R>>` 노출. `on`/`off`/`terminate` 예약어 워커 메서드명으로 사용 금지.
15
+ - `methods`: 워커가 제공할 메서드 맵. 값은 동기/비동기 함수. 동기 반환도 메인에서는 Promise 로 받음.
16
+ - `TEvents`: 메인으로 발행할 이벤트 시그니처(`{ eventName: payloadType }`).
17
+ - 반환 객체의 `send(event, data?)` 로 메인에 이벤트 push. `__methods`/`__events` 는 타입 추론 전용(런타임 미사용).
18
+ - `parentPort` 없으면 throw (반드시 worker thread 에서 import).
19
+ - `process.stdout.write` 를 가로채 메인의 stdout 으로 전달(worker thread 의 stdout 미전달 한계 우회).
20
+ - 메서드 처리 중 throw → `Error` 직렬화되어 메인 호출 Promise reject.
21
+
22
+ 워커 파일은 반드시 `export default createWorker(...)` 형태로 export.
32
23
 
33
- ### Worker.create (메인 측)
24
+ ### 메인 — `Worker.create`
34
25
 
35
26
  ```ts
36
27
  Worker.create<TModule extends WorkerModule>(
37
28
  filePath: string,
38
29
  opt?: Omit<WorkerOptions, "stdout" | "stderr">,
39
- ): WorkerProxy<TModule>;
40
- ```
41
-
42
- - `filePath`: `file://` URL 또는 절대 경로.
43
- - 실행 모드는 `import.meta.filename` 확장자로 결정:
44
- - `.ts` (개발/tsx) → 내부 `lib/worker-dev-proxy.js` 를 띄우고 워커 파일을 argv 로 전달, tsx 로 동적 로드.
45
- - `.js` (프로덕션) → 워커 파일을 그대로 `new Worker(workerPath, ...)`.
46
- - 항상 `stdout: true, stderr: true` 로 워커 띄우고 메인의 stdout/stderr 로 pipe. 워커 내부의 `process.stdout.write` 도 메시지 프로토콜(`type: "log"`)로 메인에 전달되어 그대로 stdout 에 출력 — 워커의 `console.log` 가 메인 터미널에 보임.
47
- - `opt.env` 는 `process.env` 와 머지되어 워커에 전달.
48
- - 워커 비정상 종료(`exit code !== 0`) 또는 `error` 발생 시 대기 중 모든 호출이 reject. `terminate()` 호출 시에도 in-flight 요청은 모두 reject.
49
-
50
- ### createWorker (워커 측)
51
-
52
- ```ts
53
- function createWorker<
54
- TMethods extends Record<string, (...args: any[]) => unknown>,
55
- TEvents extends Record<string, unknown> = Record<string, never>,
56
- >(methods: TMethods): {
57
- send<E extends keyof TEvents & string>(event: E, data?: TEvents[E]): void;
58
- __methods: TMethods;
59
- __events: TEvents;
60
- };
30
+ ): WorkerProxy<TModule>
61
31
  ```
62
32
 
63
- - 워커 스레드에서만 호출 (`parentPort == null` 이면 `SdError` throw).
64
- - `methods` 함수는 메인에서 `worker.<name>(...args)` 호출되어 결과/throw 그대로 전달됨. async 함수도 await 처리됨.
65
- - 없는 메서드/잘못된 메시지 형식 호출 `SdError` 응답.
66
- - 반환값 객체에 `send(event, data?)` 가 있어 워커 메인 단방향 이벤트 가능. `TEvents` 제네릭으로 이벤트 이름·페이로드 타입 보장. 반환값은 반드시 `export default` 해야 메인의 `typeof import(...)` 추론이 동작.
33
+ - `TModule`: 워커 파일의 `typeof import("./worker")`. 메서드 시그니처·이벤트 타입 추론 소스.
34
+ - `filePath`: 워커 파일 경로. `file://` URL 또는 절대 경로 모두 허용.
35
+ - `opt`: Node `WorkerOptions` `stdout/stderr` 제외(고정 `true`). `env` `process.env` 병합. `argv` 는 dev 모드에서 워커 경로 뒤에 추가.
36
+ - 개발(`import.meta.filename` `.ts`) → 내부 `worker-dev-proxy.js` 워커로 띄우고 tsx 사용자 워커 동적 로드. 프로덕션(`.js`) 사용자 파일 직접 실행.
37
+ - 워커 stdout/stderr 는 메인 프로세스로 파이프.
67
38
 
68
- ### WorkerRequest / WorkerResponse
39
+ ### 반환 프록시 — `WorkerProxy<TModule>`
69
40
 
70
- 내부 메시지 프로토콜 (사용자가 직접 다룰 없음).
41
+ - `methods` (스프레드된 키): 각 메서드 → `(...args) => Promise<Awaited<R>>`. 호출마다 UUID 부여, `@simplysm/core-common` 의 `transfer.encode` 로 직렬화하여 `postMessage`.
42
+ - `on(event, listener)` / `off(event, listener)` — 워커가 `send()` 로 발행한 이벤트 구독/해제.
43
+ - `terminate(): Promise<void>` — 대기 중인 모든 요청을 reject 후 워커 종료.
71
44
 
72
- ```ts
73
- interface WorkerRequest { id: string; method: string; params: unknown[]; }
45
+ ### 비정상 종료
74
46
 
75
- type WorkerResponse =
76
- | { request: WorkerRequest; type: "return"; body?: unknown }
77
- | { request: WorkerRequest; type: "error"; body: Error }
78
- | { type: "event"; event: string; body?: unknown }
79
- | { type: "log"; body: string };
80
- ```
47
+ - 워커 `exit` (코드≠0, 사용자 terminate 아님) 또는 `error` 발생 시 대기 중인 모든 호출이 `Error("워커가 비정상 종료되었습니다 (코드: N) (method: X)")` 등으로 reject.
81
48
 
82
- ### 사용
49
+ ### 예
83
50
 
84
51
  ```ts
85
52
  // worker.ts
86
53
  import { createWorker } from "@simplysm/core-node";
87
-
88
- interface Events { progress: number; }
89
-
90
- const methods = {
91
- async heavy(x: number) {
92
- sender.send("progress", 50);
93
- return x * 2;
94
- },
95
- };
96
-
97
- const sender = createWorker<typeof methods, Events>(methods);
54
+ interface E { progress: number }
55
+ const methods = { calc: (n: number) => n * 2 };
56
+ const sender = createWorker<typeof methods, E>(methods);
98
57
  export default sender;
99
- ```
100
58
 
101
- ```ts
102
59
  // main.ts
103
60
  import { Worker } from "@simplysm/core-node";
104
-
105
- const worker = Worker.create<typeof import("./worker")>(
106
- new URL("./worker.ts", import.meta.url).href,
107
- );
108
- worker.on("progress", (p) => consola.info(`${p}%`));
109
- const result = await worker.heavy(21); // 42
110
- await worker.terminate();
61
+ const w = Worker.create<typeof import("./worker")>("./worker.ts");
62
+ w.on("progress", (p) => console.log(p));
63
+ console.log(await w.calc(21)); // 42
64
+ await w.terminate();
111
65
  ```
@@ -1,95 +1,193 @@
1
1
  # @simplysm/excel
2
2
 
3
- OOXML(xlsx) 워크북을 lazy-load 로 읽고 쓰는 클래스 묶음. ZIP 내부 XML 을 접근 시점에만 파싱한다.
3
+ OOXML(xlsx) ZIP lazy 로 읽고 쓰는 neutral 패키지. 단위 접근 시점에만 해당 XML(SharedStrings/Styles 등)로드하므로 대용량 파일 메모리 효율적.
4
4
 
5
5
  ## 사용 트리거 인덱스
6
- - **`ExcelWorkbook`** — xlsx 바이트/Blob 을 열거나 새 워크북을 만들 때. 사용 후 `close()` 필수.
7
- - **`ExcelWorkbook.setDefaultStyle`** — 워크북 전역(폰트·정렬·numFmt 등) 셀 표준을 한번에 적용.
8
- - **`ExcelWorksheet`** — 시트 단위 셀 접근의 진입점. 이름·범위 조회.
9
- - **`getDataTable` / `setDataMatrix` / `setRecords`** — 시트와 레코드 배열(또는 2D 매트릭스) 간 입출력.
10
- - **`copyCell` / `copyRow` / `copyCellStyle` / `copyRowStyle` / `insertCopyRow`** — 셀/행 복제·삽입(템플릿 시트 채울 때).
11
- - **`setZoom` / `freezeAt`** — 시트 뷰 보기 설정(확대·틀 고정).
12
- - **`setTabColor`** — 시트 탭 색 ARGB 지정.
13
- - **`addConditionalFormat`** — 셀/범위에 조건부 서식 규칙 적용.
14
- - **`addImage`** — 시트에 이미지(png/jpg 등) 삽입.
15
- - **`ExcelCell`** — 단일 셀의 값·수식·스타일·병합.
16
- - **`ExcelRow`** — 행 단위 셀 일괄 접근.
17
- - **`ExcelCol`** — 열 단위 셀 접근 + 열 너비 설정.
18
- - **`ExcelWrapper`** — Zod 스키마로 헤더·타입을 정의해 레코드 배열로 read/write.
19
- - **`ExcelUtils`** — `"A1"`↔좌표, 범위 주소, Excel 직렬 날짜 ↔ 타임스탬프, numFmt 변환.
20
- - **`ExcelValueType`** — 셀에 넣고 뺄 수 있는 값의 union (number/string/boolean/DateOnly/DateTime/Time/undefined).
21
- - **`ExcelStyleOptions`** — `setStyle` / `setDefaultStyle` 입력 옵션 (배경·테두리·정렬·numFmt·폰트).
22
- - **`ExcelFont`** — `ExcelStyleOptions.font` 의 폰트 속성(크기·family·bold·italic·underline·color·strike).
23
- - **`ExcelConditionalRule` / `ExcelConditionalRuleStyle`** — `addConditionalFormat` 의 규칙·강조 스타일 타입.
24
- - **`ExcelAddressPoint` / `ExcelAddressRangePoint`** — 0 기반 좌표 / 범위 좌표.
25
- - **`ExcelNumberFormat` / `ExcelBorderPosition` / `ExcelHorizontalAlign` / `ExcelVerticalAlign` / `ExcelFontUnderline`** — 스타일 옵션의 enum literal 들.
26
-
27
- ## ExcelWorkbook
28
- ```typescript
29
- new ExcelWorkbook(arg?: Blob | Bytes)
30
- getWorksheetNames(): Promise<string[]>
31
- addWorksheet(name): Promise<ExcelWorksheet>
32
- getWorksheet(nameOrIndex: string | number): Promise<ExcelWorksheet> // 인덱스 0 기반
33
- setDefaultStyle(opts: ExcelStyleOptions): Promise<void> // fonts[0]/fills[0]/borders[0] 덮어쓰기, 모든 셀에 전역 적용
34
- toBytes(): Promise<Bytes>
35
- toBlob(): Promise<Blob> // xlsx MIME
36
- close(): Promise<void> // 멱등, 이후 모든 메서드는 throw
37
- ```
38
- 패턴: `const wb = new ExcelWorkbook(bytes); try { ... } finally { await wb.close(); }`. 인자 생략 시 빈 워크북.
39
6
 
40
- ## ExcelWorksheet
7
+ - 워크북 열기/생성/저장/해제 → [Workbook](#workbook)
8
+ - 워크시트 추가·조회·이름·뷰(탭색/zoom/freeze) → [Worksheet 기본](#worksheet-기본)
9
+ - 행/열/셀 접근, 데이터 범위, 2D 셀 조회 → [Row · Col · Cell 접근](#row--col--cell-접근)
10
+ - 셀 값/수식/병합 읽고 쓰기 → [Cell 값·수식·병합](#cell-값수식병합)
11
+ - 셀 스타일, 워크북 default 스타일 → [Style](#style)
12
+ - 조건부 서식 → [Conditional Format](#conditional-format)
13
+ - 이미지 삽입 → [Image](#image)
14
+ - 데이터 테이블(레코드 배열) ↔ 시트 → [Data Matrix · Records](#data-matrix--records)
15
+ - Zod 스키마 기반 타입 안전 입출력 → [ExcelWrapper](#excelwrapper)
16
+ - 셀 주소 ↔ 좌표, Excel 날짜 ↔ JS tick → [ExcelUtils](#excelutils)
17
+ - 타입(주소·값·스타일·조건부 규칙 등) → [타입](#타입)
18
+
19
+ ---
20
+
21
+ ## Workbook
22
+
23
+ `new ExcelWorkbook(arg?: Blob | Bytes)` — 인자 없으면 빈 워크북 생성(기본 OOXML 골격: `[Content_Types].xml`, `_rels/.rels`, `xl/workbook.xml`, `xl/_rels/workbook.xml.rels`). 인자가 있으면 ZIP 을 lazy reader 로 열기.
24
+
25
+ 핵심 메서드:
26
+ - `getWorksheetNames(): Promise<string[]>` — 시트명 배열.
27
+ - `addWorksheet(name): Promise<ExcelWorksheet>` — 새 시트 추가 + ContentTypes/Rels 갱신.
28
+ - `getWorksheet(nameOrIndex: string | number): Promise<ExcelWorksheet>` — 이름 또는 0 기반 인덱스. 없으면 throw.
29
+ - `setDefaultStyle(opts: ExcelStyleOptions): Promise<void>` — `styles.xml` 의 `fonts[0]/fills[0]/borders[0]` (default 자원 슬롯) 및 `cellXfs[0]` 를 옵션으로 덮어쓰기. 셀 xf 가 자원 id 미지정이면 0번 슬롯이 fallback 되어 전역 적용. 옵션 없는 자원은 0번을 빈 슬롯으로 reset. 미호출 시 원본 보존.
30
+ - `toBytes(): Promise<Bytes>` / `toBlob(): Promise<Blob>` — ZIP 직렬화. Blob 의 mime = `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`.
31
+ - `close(): Promise<void>` — ZIP 리더와 캐시 해제. 호출 후 다른 메서드 호출 시 throw. 재호출 safe(no-op). **반드시 try/finally 로 호출**.
32
+
41
33
  ```typescript
42
- getName() / setName(newName)
43
- row(r) / col(c) / cell(r, c) // 모두 0 기반, 동기 반환
44
- getRange(): Promise<ExcelAddressRangePoint>
45
- getCells(): Promise<ExcelCell[][]> // 전체 2D
46
- getDataTable(opt?: { headerRowIndex?; checkEndColIndex?; usableHeaderNameFn? })
47
- // 레코드 배열. 중복 헤더면 throw
48
- setDataMatrix(matrix: ExcelValueType[][]) // 0,0 부터
49
- setRecords(records: Record<string, ExcelValueType>[]) // 첫 행 헤더 자동
50
- copyCell / copyRow / copyCellStyle / copyRowStyle / insertCopyRow
51
- setZoom(percent) / freezeAt({ r?, c? }) / setTabColor(color) // color: ARGB 8자리
52
- addConditionalFormat({ ref, rules: ExcelConditionalRule[] }) // 호출마다 priority 누적
53
- addImage({ bytes, ext, from, to? }) // ext 는 mime lookup. to 생략 시 from+1,+1
34
+ const wb = new ExcelWorkbook(bytes);
35
+ try {
36
+ const ws = await wb.getWorksheet(0);
37
+ // ...
38
+ } finally { await wb.close(); }
54
39
  ```
55
40
 
56
- ## ExcelCell / ExcelRow / ExcelCol
41
+ ## Worksheet 기본
42
+
43
+ `ExcelWorksheet` 는 `addWorksheet` / `getWorksheet` 로만 획득(직접 생성자 호출 X).
44
+
45
+ - `getName(): Promise<string>` / `setName(newName: string): Promise<void>`
46
+ - `getRange(): Promise<ExcelAddressRangePoint>` — 데이터가 존재하는 범위(시트의 `<dimension>` 기반).
47
+ - `getCells(): Promise<ExcelCell[][]>` — `range` 전체를 2차원 배열로.
48
+ - `setTabColor(color: string): Promise<void>` — 시트 탭 색. ARGB 8자리 16진수(예: `"00FF0000"`).
49
+ - `setZoom(percent: number): Promise<void>` — 보기 확대 비율(%).
50
+ - `freezeAt(point: { r?: number; c?: number }): Promise<void>` — 행/열 틀 고정.
51
+ - `copyRow(srcR, targetR)` — 행 덮어쓰기 복사.
52
+ - `copyCell(srcAddr, targetAddr)` — 셀 복사.
53
+ - `copyRowStyle(srcR, targetR)` / `copyCellStyle(srcAddr, targetAddr)` — 스타일만 복사.
54
+ - `insertCopyRow(srcR, targetR)` — `targetR` 위치에 행 삽입 복사. 이하 기존 행은 한 칸 아래로 밀린다. 삽입 지점을 관통하는 다중행 병합은 1행 확장. 원본의 단일행 병합은 복제.
55
+
56
+ ## Row · Col · Cell 접근
57
+
58
+ `ExcelWorksheet` 인스턴스 메서드 (모두 0 기반 인덱스, 인스턴스는 캐싱됨):
59
+ - `row(r): ExcelRow` / `col(c): ExcelCol` / `cell(r, c): ExcelCell`
60
+
61
+ `ExcelRow`:
62
+ - `cell(c): ExcelCell` — 같은 행의 셀.
63
+ - `getCells(): Promise<ExcelCell[]>` — 시트 `range` 의 열 범위만큼.
64
+
65
+ `ExcelCol`:
66
+ - `cell(r): ExcelCell` — 같은 열의 셀.
67
+ - `getCells(): Promise<ExcelCell[]>` — 시트 `range` 의 행 범위만큼.
68
+ - `setWidth(size: number): Promise<void>` — 열 너비(OOXML 단위).
69
+
70
+ ## Cell 값·수식·병합
71
+
72
+ `ExcelCell.addr: ExcelAddressPoint` — 0 기반 `{r, c}`.
73
+
74
+ - `getValue(): Promise<ExcelValueType>` — 셀 타입과 styles.xml 의 numFmt 를 보고 자동 변환:
75
+ - `s`(SharedString) → `string`
76
+ - `str`/`inlineStr` → `string`
77
+ - `b` → `boolean`
78
+ - `n` 또는 타입 미지정 → `numFmt` 가 날짜 계열이면 `DateOnly`/`DateTime`/`Time`, 아니면 `number`. 텍스트 형식(numFmtId 49)이면 `string`.
79
+ - `e`(error) → throw.
80
+ - `setValue(val: ExcelValueType): Promise<void>` — 값 타입으로 자동 분기:
81
+ - `undefined` → 셀 삭제
82
+ - `string` → SharedString 으로 등록(`t="s"`)
83
+ - `boolean` → `t="b"`, `"1"`/`"0"`
84
+ - `number` → numeric
85
+ - `DateOnly`/`DateTime`/`Time` → Excel 날짜 숫자로 변환 + 해당 numFmt(14/22/18) 자동 적용
86
+ - 그 외 타입 → throw.
87
+ - `setFormula(val: string | undefined): Promise<void>` / `getFormula(): Promise<string | undefined>` — 수식 설정/조회. `undefined` 전달 시 셀 삭제.
88
+ - `merge(r, c): Promise<void>` — 현재 셀에서 `(r, c)` 까지 병합(양 끝 inclusive). 예: `cell(0,0).merge(2,2)` → A1:C3.
89
+
90
+ ## Style
91
+
92
+ - `cell.setStyle(opts: ExcelStyleOptions): Promise<void>` — 셀 스타일 적용. 기존 styleId 가 있으면 clone + override.
93
+ - `cell.getStyleId(): Promise<string | undefined>` / `setStyleId(id)` — 원시 cellXfs id 조작.
94
+ - `wb.setDefaultStyle(opts)` — 워크북 default. [Workbook](#workbook) 참고.
95
+
96
+ `ExcelStyleOptions`:
97
+ - `background?: string` — 셀 배경. ARGB 8자리(예: `"00FF0000"`).
98
+ - `border?: ("left" | "right" | "top" | "bottom")[]` — 활성화할 변. style 은 thin.
99
+ - `horizontalAlign?: "left" | "center" | "right"` / `verticalAlign?: "top" | "center" | "bottom"`.
100
+ - `numberFormat?: "number" | "string" | "DateOnly" | "DateTime" | "Time"` — 프리셋 numFmtId 매핑(0/49/14/22/18).
101
+ - `numberFormatCode?: string` — 임의 Excel formatCode(예: `"0.000000"`). `numberFormat` 보다 우선.
102
+ - `font?: ExcelFont` — `{ size?, family?, bold?, italic?, underline?: "single"|"double"|"singleAccounting"|"doubleAccounting", color?: ARGB8, strike? }`. 미지정 속성은 emit 안 함(Excel 기본값 표시).
103
+
104
+ ## Conditional Format
105
+
106
+ `ws.addConditionalFormat({ ref, rules }): Promise<void>` — 셀/범위에 조건부 서식 규칙 추가. `ref` = `"A1"` 또는 `"A1:B10"`. `rules` 배열 순서가 priority(앞 우선). 같은 시트에 여러 번 호출하면 블록이 누적되고 priority 가 시트 전역으로 이어붙음. 빈 배열이면 no-op.
107
+
108
+ `ExcelConditionalRule` 종류:
109
+ - `{ type: "cellIs", op: "<"|">"|"<="|">="|"="|"<>", value: number|string, style }` — 단일 비교. `value` 가 string 이면 따옴표 리터럴 formula 로 emit, number 면 raw formula.
110
+ - `{ type: "cellIs", op: "between"|"notBetween", value: [a,b], style }` — 양 끝 inclusive 구간.
111
+ - `{ type: "text", op: "contains"|"notContains"|"beginsWith"|"endsWith", value: string, style }` — SEARCH 기반(대소문자 무시) 고정.
112
+ - `{ type: "expression", formula: string, style }` — 임의 수식 식.
113
+
114
+ `ExcelConditionalRuleStyle`:
115
+ - `background?: ARGB8` — 강조 배경.
116
+ - `fontColor?: ARGB8` — 글자색.
117
+ - `fontWeight?: "bold" | "normal"` — `"normal"` 은 base 가 bold 라도 강제 normal.
118
+
119
+ 미지정 필드는 base 셀 스타일을 유지하며, 지정 필드만 OOXML dxf 로 emit 되어 Excel native CF 오버레이로 합성.
120
+
57
121
  ```typescript
58
- // ExcelCell
59
- cell.addr // { r, c }
60
- getValue() / setValue(val: ExcelValueType) // string/number/boolean/DateOnly/DateTime/Time/undefined
61
- getFormula() / setFormula(val) // 셀 타입 "str" 로 설정
62
- merge(endR, endC) // 현재 셀부터 (endR,endC) 까지
63
- getStyleId() / setStyleId(id)
64
- setStyle(opts: ExcelStyleOptions) // 기존 스타일에 clone-merge
65
-
66
- // ExcelRow / ExcelCol
67
- row.cell(c) / col.cell(r)
68
- row.getCells() / col.getCells()
69
- col.setWidth(size)
122
+ await ws.addConditionalFormat({
123
+ ref: "B2:B100",
124
+ rules: [{ type: "cellIs", op: "<", value: 1000, style: { background: "00FF0000" } }],
125
+ });
70
126
  ```
71
- 값 setter 에 `undefined` → 셀 삭제. Date/Time 계열은 numFmt 자동 설정.
127
+
128
+ ## Image
129
+
130
+ `ws.addImage(opts): Promise<void>`
131
+ - `bytes: Bytes` — 이미지 바이너리.
132
+ - `ext: string` — 확장자(`png`/`jpg` 등). `mime` 라이브러리로 MIME 결정, 미인식 시 throw.
133
+ - `from: { r, c, rOff?: number|string, cOff?: number|string }` — 시작 위치(0 기반 행/열, `rOff/cOff` 는 EMU 오프셋).
134
+ - `to?: { r, c, rOff?, cOff? }` — 끝 위치. 생략 시 `from.r+1`, `from.c+1` 의 1×1 셀 크기.
135
+
136
+ 기존 drawing 이 있으면 거기에 picture 를 추가하고, 없으면 `xl/drawings/drawing{N}.xml` 신규 생성 + ContentTypes/sheet rels 연결.
137
+
138
+ ## Data Matrix · Records
139
+
140
+ `ws.setDataMatrix(matrix: ExcelValueType[][]): Promise<void>` — 2D 배열(행 우선)을 (0,0) 부터 기록. 셀 값은 `setValue` 와 동일 규칙으로 자동 분기.
141
+
142
+ `ws.setRecords(records: Record<string, ExcelValueType>[]): Promise<void>` — 0행에 헤더(레코드들의 key union, 빈 문자열 제외), 1행부터 데이터.
143
+
144
+ `ws.getDataTable(opt?): Promise<Record<string, ExcelValueType>[]>`:
145
+ - `opt.headerRowIndex?: number` — 헤더 행 인덱스(기본 = `range.s.r`).
146
+ - `opt.checkEndColIndex?: number` — 이 열이 비어 있으면 데이터 종료로 판정.
147
+ - `opt.usableHeaderNameFn?: (headerName) => boolean` — 사용할 헤더 필터. 헤더가 string 타입인 컬럼만 후보. 헤더 중복 발견 시 throw.
72
148
 
73
149
  ## ExcelWrapper
74
- ```typescript
75
- new ExcelWrapper(schema: z.ZodObject) // .describe() 가 Excel 헤더명
76
- read(file, wsNameOrIndex = 0, { excludes? }?): Promise<z.infer<S>[]> // 내부에서 워크북 close
77
- write(wsName, records, { excludes? }?): Promise<ExcelWorkbook> // 호출자가 close 필수
78
- ```
79
- write: 모든 테두리, 필수(non-optional/nullable/default) 비-boolean 필드 헤더는 노란 배경, 첫 행 freeze, 줌 85%.
150
+
151
+ `new ExcelWrapper<TSchema>(schema)` — Zod 스키마로 Excel ↔ 레코드 변환. 필드의 `.describe("...")` 가 Excel 헤더 이름. describe 없으면 필드 key.
152
+
153
+ - `read(file, wsNameOrIndex = 0, options?: { excludes? }): Promise<z.infer<TSchema>[]>`
154
+ - 시트의 헤더 행에서 스키마와 매칭되는 컬럼만 추출.
155
+ - 행을 변환(`ZodString`→string, `ZodNumber`→`num.parseFloat`, `ZodBoolean`→`"1"/"true"`/`"0"/"false"`/Boolean 강제), `safeParse` 검증, 실패 throw.
156
+ - 전 컬럼이 null/empty 인 행은 skip.
157
+ - 데이터 0행이면 throw.
158
+ - `options.excludes` 로 특정 필드 제외.
159
+ - 내부적으로 ExcelWorkbook 을 열고 finally 에서 close.
160
+ - `write(wsName, records, options?: { excludes? }): Promise<ExcelWorkbook>`
161
+ - 헤더 = describe 이름 또는 key.
162
+ - 데이터 본문 기록 후 전체 셀에 4변 테두리.
163
+ - 필수 필드(Optional/Nullable/Default 가 아님) + boolean 아닌 필드의 헤더 셀에 노란색(`"00FFFF00"`) 배경.
164
+ - `setZoom(85)`, `freezeAt({ r: 0 })`.
165
+ - 반환된 워크북의 `close()` 는 호출자 책임. 내부 throw 시에는 자동 close 후 rethrow.
80
166
 
81
167
  ## ExcelUtils
82
- ```typescript
83
- stringifyAddr({r,c}) / parseCellAddr("B3") // {r:2,c:1}
84
- stringifyColAddr(0) // "A" parseColAddr("AA") // 26
85
- stringifyRangeAddr / parseRangeAddr("A1:C3")
86
- convertTimeTickToNumber(tick) / convertNumberToTimeTick(serial) // Excel 1899-12-30 기준
87
- convertNumFmtIdToName / convertNumFmtCodeToName / convertNumFmtNameToId
88
- ```
89
168
 
90
- ## 타입 요약
91
- - `ExcelValueType` = `number | string | DateOnly | DateTime | Time | boolean | undefined`.
92
- - `ExcelStyleOptions`: `background`(ARGB 8자리), `border: ExcelBorderPosition[]`, `horizontalAlign`, `verticalAlign`, `numberFormat` (`"number"|"string"|"DateOnly"|"DateTime"|"Time"`), `numberFormatCode` (커스텀, 우선), `font: ExcelFont`.
93
- - `ExcelFont`: `size, family, bold, italic, underline, color(ARGB), strike`. 미지정 속성은 OOXML 에 emit 안 됨 → Excel 기본값.
94
- - `ExcelConditionalRule`: `{type:"cellIs", op, value, style}` | `{type:"text", op:"contains"|"notContains"|"beginsWith"|"endsWith", value, style}` | `{type:"expression", formula, style}`. `style` = `ExcelConditionalRuleStyle` (`background`, `fontColor`, `fontWeight`).
95
- - `ExcelAddressPoint = {r,c}` / `ExcelAddressRangePoint = {s,e}` 모두 0 기반.
169
+ 정적 메서드 모음.
170
+
171
+ - `stringifyAddr({r,c})` `"B3"`. `stringifyRowAddr(r)` `"3"`. `stringifyColAddr(c)` `"AA"` 등. `c` 범위 0~16383, 초과 시 throw.
172
+ - `parseCellAddr("B3")` `{r:2, c:1}`. `parseRowAddr`/`parseColAddr` 동일 패턴.
173
+ - `parseRangeAddr("A1:C3")` `{s, e}`. 단일 입력 `s === e`.
174
+ - `stringifyRangeAddr({s,e})` `"A1:C3"`. `s === e` 단일 주소만.
175
+ - `convertTimeTickToNumber(tick)` / `convertNumberToTimeTick(value)` — JS ms tick ↔ Excel serial date. Excel 기준일 = 1899-12-30.
176
+ - `convertNumFmtCodeToName(code)` → `ExcelNumberFormat`. `"General"` = number. `yy`/`dd`/`mm` 패턴 검출(`hh:mm`/`mm:ss` 의 mm 은 분으로 제외)로 Date/Time 분류. 매칭 실패 시 throw.
177
+ - `convertNumFmtIdToName(id)` → `ExcelNumberFormat`. Excel 내장 ID 범위 기반(숫자: 0–13,37–40,48 / Date: 14–17,27–31,34–36,50–58 / DateTime: 22 / Time: 18–21,32–33,45–47 / string: 49). 매칭 실패 시 throw.
178
+ - `convertNumFmtNameToId(name)` — number→0, DateOnly→14, DateTime→22, Time→18, string→49.
179
+
180
+ ## 타입
181
+
182
+ - `ExcelValueType = number | string | DateOnly | DateTime | Time | boolean | undefined` — 셀 값 도메인. `undefined` = 빈 셀.
183
+ - `ExcelNumberFormat = "number" | "string" | "DateOnly" | "DateTime" | "Time"` — 숫자 형식 프리셋 이름.
184
+ - `ExcelCellType = "s" | "b" | "str" | "n" | "inlineStr" | "e"` — OOXML 의 셀 `t` 속성: SharedString / boolean / 수식결과문자열 / 숫자 / 인라인문자열 / 에러.
185
+ - `ExcelAddressPoint = { r: number; c: number }` — 0 기반 좌표.
186
+ - `ExcelAddressRangePoint = { s: ExcelAddressPoint; e: ExcelAddressPoint }` — start/end inclusive.
187
+ - `ExcelBorderPosition = "left" | "right" | "top" | "bottom"`.
188
+ - `ExcelHorizontalAlign = "center" | "left" | "right"` / `ExcelVerticalAlign = "center" | "top" | "bottom"`.
189
+ - `ExcelFontUnderline = "single" | "double" | "singleAccounting" | "doubleAccounting"` — OOXML `<u val>` 그대로.
190
+ - `ExcelFont`, `ExcelStyleOptions` — [Style](#style) 참고.
191
+ - `ExcelConditionalRule`, `ExcelConditionalRuleStyle` — [Conditional Format](#conditional-format) 참고.
192
+ - `ExcelXml` — `{ data: unknown; cleanup(): void }`. ZipCache 가 보관하는 XML 객체의 공통 인터페이스(내부용).
193
+ - `Excel*Data` 인터페이스들 — OOXML XML 의 JS 객체 표현(xml2js 스타일, 내부 처리용). 외부 코드에서 직접 다룰 일 없음.
@@ -41,6 +41,11 @@ export default [...recommended, { /* overrides */ }];
41
41
  - `process.env`/`import.meta.env` 직접 접근 금지 → `env("...")` 호출 강제. `env("NODE_ENV")` 자체 금지.
42
42
  - `=== undefined` / `!== undefined` 금지 → `== null` / `!= null` 통일.
43
43
  - TS 블록 전용: `naming-convention` 으로 `private` 멤버에 leading underscore(`_foo`) 강제, `strict-boolean-expressions` (nullable boolean/object 허용), `ban-ts-comment` (`ts-expect-error` 는 3자 이상 설명 필수), `only-throw-error`, `no-floating-promises` 등.
44
+ - `unused-imports/no-unused-imports` error, `unused-imports/no-unused-vars` error (vars=all, args=after-used, 둘 다 `^_` 시작 식별자는 제외).
45
+ - `@typescript-eslint/no-misused-promises`: `checksVoidReturn.arguments=false`, `inheritedMethods=false` (Angular 이벤트 핸들러 패턴 허용).
46
+ - TS 블록에 등록되는 `@simplysm/*` 룰: `ng-no-async-effect`, `no-hard-private`, `no-subpath-imports-from-simplysm`, `ts-no-unused-injects`, `ts-no-unused-protected-readonly` 는 error, `ts-no-throw-not-implemented-error` 는 warn.
47
+ - HTML 블록에 등록되는 `@simplysm/*` 룰: `ng-template-no-strict-null-check` error, `ng-template-sd-require-binding-attrs` error, `ng-template-no-todo-comments` warn.
48
+ - JS 블록에 등록되는 `@simplysm/*` 룰: `no-subpath-imports-from-simplysm`, `no-hard-private` (둘 다 error).
44
49
 
45
50
  ## `./eslint-plugin`
46
51