@simplysm/sd-claude 14.0.87 → 14.0.88
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/claude/references/sd-simplysm14/README.md +17 -18
- package/claude/references/sd-simplysm14/apis/angular/README.md +61 -0
- package/claude/references/sd-simplysm14/apis/angular/controls.md +119 -0
- package/claude/references/sd-simplysm14/apis/angular/crud.md +50 -0
- package/claude/references/sd-simplysm14/apis/angular/directives.md +44 -0
- package/claude/references/sd-simplysm14/apis/angular/features.md +55 -0
- package/claude/references/sd-simplysm14/apis/angular/infra.md +74 -0
- package/claude/references/sd-simplysm14/apis/angular/layout.md +55 -0
- package/claude/references/sd-simplysm14/apis/angular/overlay.md +115 -0
- package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +64 -0
- package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +43 -0
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +70 -0
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +78 -0
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +80 -0
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +66 -0
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +71 -0
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +67 -0
- package/claude/references/sd-simplysm14/apis/core-browser/README.md +83 -0
- package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +79 -0
- package/claude/references/sd-simplysm14/apis/core-common/README.md +138 -0
- package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +72 -0
- package/claude/references/sd-simplysm14/apis/core-common/datetime.md +95 -0
- package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +47 -0
- package/claude/references/sd-simplysm14/apis/core-common/obj.md +53 -0
- package/claude/references/sd-simplysm14/apis/core-node/README.md +14 -0
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +51 -0
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +39 -0
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +38 -0
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +86 -0
- package/claude/references/sd-simplysm14/apis/core-node/pathx.md +42 -0
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +54 -0
- package/claude/references/sd-simplysm14/apis/excel/README.md +43 -0
- package/claude/references/sd-simplysm14/apis/excel/cell.md +54 -0
- package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +51 -0
- package/claude/references/sd-simplysm14/apis/excel/style.md +67 -0
- package/claude/references/sd-simplysm14/apis/excel/utils.md +35 -0
- package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +97 -0
- package/claude/references/sd-simplysm14/apis/excel/wrapper.md +83 -0
- package/claude/references/sd-simplysm14/apis/lint/README.md +43 -0
- package/claude/references/sd-simplysm14/apis/lint/rules.md +90 -0
- package/claude/references/sd-simplysm14/apis/orm-common/README.md +67 -0
- package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +80 -0
- package/claude/references/sd-simplysm14/apis/orm-common/expr.md +113 -0
- package/claude/references/sd-simplysm14/apis/orm-common/query-builder.md +29 -0
- package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +111 -0
- package/claude/references/sd-simplysm14/apis/orm-common/schema.md +162 -0
- package/claude/references/sd-simplysm14/apis/orm-common/types.md +52 -0
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +53 -0
- package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +94 -0
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +29 -0
- package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +70 -0
- package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +173 -0
- package/claude/references/sd-simplysm14/apis/service-client/README.md +152 -0
- package/claude/references/sd-simplysm14/apis/service-client/orm.md +45 -0
- package/claude/references/sd-simplysm14/apis/service-client/transport.md +36 -0
- package/claude/references/sd-simplysm14/apis/service-common/README.md +70 -0
- package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +48 -0
- package/claude/references/sd-simplysm14/apis/service-common/protocol.md +72 -0
- package/claude/references/sd-simplysm14/apis/service-server/README.md +102 -0
- package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +74 -0
- package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +51 -0
- package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +50 -0
- package/claude/references/sd-simplysm14/apis/storage/README.md +114 -0
- package/claude/skills/sd-docs/SKILL.md +17 -29
- package/claude/skills/sd-docs/references/{subagent-prompt.md → doc-rules.md} +25 -40
- package/package.json +1 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# @simplysm/core-common — obj 네임스페이스
|
|
2
|
+
|
|
3
|
+
`import { obj } from "@simplysm/core-common"`. 객체/컬렉션의 깊은 복사·비교·병합·경로 접근·키 변환을 다룰 때 함께 읽힌다. clone/equal/merge 는 `DateTime`/`DateOnly`/`Time`/`Uuid`/`Uint8Array`/`Date`/`RegExp` 커스텀 타입과 `Map`/`Set`/`Array`/`Error` 를 인지한다.
|
|
4
|
+
|
|
5
|
+
## clone / equal / merge / merge3
|
|
6
|
+
|
|
7
|
+
- `obj.clone(source): T` — 깊은 복사. 순환 참조 지원, 프로토타입 체인 유지, 위 커스텀 타입 보존, `Error`(cause·커스텀 속성 포함) 복사. 단 함수·Symbol 은 참조 유지, WeakMap/WeakSet 은 빈 객체화, getter/setter 는 현재 값으로 평가됨.
|
|
8
|
+
- `obj.equal(source, target, options?): boolean` — 깊은 동등 비교. `options`:
|
|
9
|
+
- `topLevelIncludes?: string[]` — 비교할 key 만 한정(최상위 객체 속성에만). 예: id·name 만 비교.
|
|
10
|
+
- `topLevelExcludes?: string[]` — 비교 제외 key(최상위). 예: updatedAt 무시.
|
|
11
|
+
- `ignoreArrayIndex?: boolean` — 배열 순서 무시(집합 동치 비교). true 면 O(n²).
|
|
12
|
+
- `shallow?: boolean` — 1단계 참조 비교. 대용량에서 성능용.
|
|
13
|
+
- null/undefined 인 속성은 비교에서 제외됨(키 개수 계산에서도 빠짐). Map key 에는 include/exclude 미적용.
|
|
14
|
+
- `obj.merge(source, target, opt?): TSource & TMergeTarget` — source 기반으로 target 을 깊은 병합한 새 객체(원본 불변). `opt`:
|
|
15
|
+
- `arrayProcess?: "replace"|"concat"` — 배열을 target 으로 교체(기본) 또는 Set 으로 합집합(중복 제거, 객체는 참조 비교).
|
|
16
|
+
- `useDelTargetNull?: boolean` — target 값이 `null` 이면 결과에서 해당 key 삭제(undefined 는 항상 source 유지). 타입이 다르면 target 으로 덮어씀.
|
|
17
|
+
- `obj.merge3(source, origin, target, optionsObj?): { conflict, result }` — 공통 조상 `origin` 기준 3-way 병합. 한쪽만 바뀌면 그 값, 양쪽 동일하면 그 값, 셋 다 다르면 `conflict:true`(origin 값 유지). `optionsObj: Record<key, { keys?, excludes?, ignoreArrayIndex? }>` 로 key 별 `equal` 옵션 지정.
|
|
18
|
+
- 옵션 타입: `obj.EqualOptions`, `obj.MergeOptions`, `obj.Merge3KeyOptions`.
|
|
19
|
+
|
|
20
|
+
## 부분 선택 / 키 변환
|
|
21
|
+
|
|
22
|
+
- `obj.omit(item, omitKeys)` — 지정 key 제외한 새 객체. 반환 `Omit<T,K>`.
|
|
23
|
+
- `obj.omitByFilter(item, omitKeyFn)` — `omitKeyFn(key)===true` 인 key 제외(예: `_` 접두 내부 속성 숨김).
|
|
24
|
+
- `obj.pick(item, pickKeys)` — 지정 key 만 선택. 반환 `Pick<T,K>`.
|
|
25
|
+
- `obj.keys(obj)` — 타입 안전 `Object.keys`. 반환 `(keyof T)[]`.
|
|
26
|
+
- `obj.entries(obj)` — 타입 안전 `Object.entries`. 반환 `[K, T[K]][]`.
|
|
27
|
+
- `obj.fromEntries(entryPairs)` — 타입 안전 `Object.fromEntries`.
|
|
28
|
+
- `obj.map(obj, fn)` — 각 엔트리를 `fn(key, value) => [newKey|null, newValue]` 로 변환한 새 객체. `newKey` 가 `null` 이면 원래 key 유지(값만 변환).
|
|
29
|
+
|
|
30
|
+
## 체인 경로 접근
|
|
31
|
+
|
|
32
|
+
- `obj.getChainValue(target, chain, optional?)` — `"a.b[0].c"` 경로로 값 조회. `optional:true` 면 중간 null/undefined 에서 에러 없이 `undefined`.
|
|
33
|
+
- `obj.getChainValueByDepth(target, key, depth, optional?)` — 같은 key 로 `depth` 단계 하강(예: `parent` 로 2단계). `depth<1` 이면 `ArgumentError`.
|
|
34
|
+
- `obj.setChainValue(target, chain, value)` — 경로로 값 설정(중간 객체 자동 생성). 빈 chain 이면 `ArgumentError`.
|
|
35
|
+
- `obj.deleteChainValue(target, chain)` — 경로로 값 삭제. 중간 경로가 없으면 조용히 반환.
|
|
36
|
+
|
|
37
|
+
## 정리 변환 (@mutates 표기는 원본 수정)
|
|
38
|
+
|
|
39
|
+
- `obj.clearUndefined(target)` — undefined/null 값 key 삭제(원본 수정).
|
|
40
|
+
- `obj.clear(target)` — 모든 key 삭제(원본 수정).
|
|
41
|
+
- `obj.nullToUndefined(target)` — `null` → `undefined` 재귀 변환(원본 수정, 순환 참조 안전). 커스텀 날짜/Uuid 타입은 그대로 둠. simplysm 의 null-free 규칙용.
|
|
42
|
+
- `obj.unflatten(flatObj)` — `{ "a.b.c": 1 }` → `{ a: { b: { c: 1 } } }`.
|
|
43
|
+
|
|
44
|
+
## 타입 유틸리티
|
|
45
|
+
|
|
46
|
+
- `obj.UndefToOptional<T>` — `undefined` 를 포함한 속성을 optional(`?`)로. 예: `{ b: string | undefined }` → `{ b?: string | undefined }`.
|
|
47
|
+
- `obj.OptionalToUndef<T>` — optional 속성을 `필수 + undefined` 유니온으로(역변환).
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
const next = obj.merge(prev, patch, { arrayProcess: "concat", useDelTargetNull: true });
|
|
51
|
+
if (!obj.equal(a, b, { topLevelExcludes: ["updatedAt"] })) save();
|
|
52
|
+
const city = obj.getChainValue(user, "profile.address.city", true);
|
|
53
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# @simplysm/core-node
|
|
2
|
+
|
|
3
|
+
Node.js 환경 전용 유틸리티 모음 — 파일시스템·자식 프로세스·경로 조작, 파일 감시, consola 로깅 설정, worker_threads 래퍼.
|
|
4
|
+
|
|
5
|
+
엔트리는 `cpx`/`fsx`/`pathx` 세 개의 네임스페이스 객체(`export * as`)와 fs-watcher·consola·worker 심볼을 재노출한다. 네임스페이스는 `import { fsx } from "@simplysm/core-node"` 후 `fsx.read(...)` 형태로 호출한다.
|
|
6
|
+
|
|
7
|
+
## 사용 트리거 인덱스
|
|
8
|
+
|
|
9
|
+
- **fsx** — 파일/디렉토리 존재 확인·생성·삭제·복사·읽기·쓰기(JSON 포함)·glob 검색·부모 디렉토리 탐색이 필요할 때. 네임스페이스 `import { fsx }`. 자세히: [fsx.md](./fsx.md)
|
|
10
|
+
- **cpx** — 자식 프로세스를 spawn 해 stdout/stderr 를 시스템 인코딩으로 디코딩 수집하거나 OS 코드페이지 인코딩을 감지할 때. 네임스페이스 `import { cpx }`. 자세히: [cpx.md](./cpx.md)
|
|
11
|
+
- **pathx** — 경로를 POSIX 슬래시로 정규화·resolve, 하위 경로 판정, 디렉토리 치환, 대상 목록 기준 파일 필터링이 필요할 때. 네임스페이스 `import { pathx }`. 자세히: [pathx.md](./pathx.md)
|
|
12
|
+
- **FsWatcher** — chokidar 기반으로 파일 변경을 감시하며 짧은 시간 내 이벤트를 병합해 콜백을 한 번만 호출하고 싶을 때. 자세히: [fs-watcher.md](./fs-watcher.md)
|
|
13
|
+
- **consola 설정** — 앱/CLI 의 consola 전역 reporter(콘솔 pretty 출력·`.logs` 파일 로테이션)를 환경(dev/prod)에 맞춰 구성할 때. `setupConsola`·`PrettyReporter`·`createFileReporter`·`withMaxLevel`. 자세히: [consola.md](./consola.md)
|
|
14
|
+
- **Worker / createWorker** — worker_threads 를 타입 안전한 메서드 프록시 + 이벤트로 래핑해 별도 스레드에서 함수를 실행할 때. 자세히: [worker.md](./worker.md)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @simplysm/core-node — consola 로깅 설정
|
|
2
|
+
|
|
3
|
+
전역 `consola` 로거의 level/reporter 를 환경(prod/dev/cli)에 맞춰 구성한다. 콘솔용 pretty 출력과 일자별 회전 파일 로그(콘솔과 동일한 평문, 색만 제거)를 제공한다.
|
|
4
|
+
|
|
5
|
+
## setupConsola
|
|
6
|
+
|
|
7
|
+
- `setupConsola(opts?: SetupConsolaOptions): void` — 전역 `consola` 의 `level`/`options.reporters` 를 환경에 맞게 설정. 앱·CLI 부트스트랩 시 1회 호출. level 은 모든 분기에서 `debug` 포함.
|
|
8
|
+
- `opts.cli?: boolean` — true 면 CLI 모드로 취급해 dev 분기를 사용(prod 파일 전용 분기를 건너뜀).
|
|
9
|
+
- `SetupConsolaOptions` — `{ cli?: boolean }`.
|
|
10
|
+
|
|
11
|
+
환경별 동작:
|
|
12
|
+
- **prod** (`cli` 아님 + `DEV` env 가 truthy 아님): `createFileReporter()` 만 — 콘솔 출력 없이 파일에만 debug 포함 기록.
|
|
13
|
+
- **dev + `SD_DEBUG` truthy**: `PrettyReporter` 만 — 콘솔에 debug 포함 출력.
|
|
14
|
+
- **dev** (그 외): `createFileReporter()` + `withMaxLevel(new PrettyReporter(), info)` — 파일엔 debug 까지, 콘솔엔 info 이하까지만.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
setupConsola({ cli: true });
|
|
18
|
+
import consola from "consola";
|
|
19
|
+
consola.info("started");
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## PrettyReporter
|
|
23
|
+
|
|
24
|
+
- `class PrettyReporter implements ConsolaReporter` — 컬러·아이콘·태그·스택 정리 콘솔 리포터. `new PrettyReporter()` 로 사용.
|
|
25
|
+
- level < 2(error/warn)는 stderr, 그 외는 stdout 으로 출력.
|
|
26
|
+
- 컬러 지원은 `NO_COLOR`(있으면 끔) → `FORCE_COLOR`(있으면 켬) → TTY → Windows 순으로 자동 판정.
|
|
27
|
+
- Error 인자는 메시지 + 정리된 스택(cwd 접두 제거, `file://` 제거)으로 출력하고, `cause` 체인을 들여쓰기로 재귀 출력.
|
|
28
|
+
- `box`/`trace` 타입 로그는 별도 포맷(box 는 ` > ` 접두, trace 는 스택 부착).
|
|
29
|
+
- `formatPlain(logObj, formatOptions?): string` — 색·날짜·뱃지 여백 없이 한 엔트리를 평문(멀티라인 가능)으로 포맷. 파일 리포터 등에서 콘솔과 동일한 표현(아이콘·tag·객체 inspect·스택)을 재사용하기 위한 진입점. `formatOptions` 에 `ctx.options.formatOptions` 를 넘기면 객체 펼침(`compact`) 등이 콘솔과 일치.
|
|
30
|
+
|
|
31
|
+
## createFileReporter
|
|
32
|
+
|
|
33
|
+
- `createFileReporter(options?: FileReporterOptions): ConsolaReporter` — `<cwd>/.logs/app.<YYYY-MM-DD>.log` 에 평문 라인으로 기록하는 리포터 생성. 일자 변경·크기 초과 시 회전(`app.<date>.<seq>.log`).
|
|
34
|
+
- `options.maxSize?: number` — 파일 회전 임계 바이트. 기본 20MB(`20 * 1024 * 1024`).
|
|
35
|
+
- `options.maxDays?: number` — 로그 보존 일수. 초과 일자 파일은 일자가 바뀔 때 정리됨. 기본 14.
|
|
36
|
+
- `FileReporterOptions` — `{ maxSize?: number; maxDays?: number }`.
|
|
37
|
+
|
|
38
|
+
라인 포맷: `<로컬시각> [<LEVEL>] <PrettyReporter 평문>` — 예 `2026-06-02 14:23:01.123 [ERROR] [api] ✖ 메시지 { id: 1 }`. 타임스탬프는 로컬 `YYYY-MM-DD HH:mm:ss.SSS`, `[<LEVEL>]` 은 `logObj.type` 대문자(`INFO`/`ERROR`/`WARN`/`DEBUG` 등 — grep 필터용). 본문은 `PrettyReporter.formatPlain` 으로 색만 제거하고 콘솔과 동일하게 생성되어 객체·배열은 `util` inspect 로 펼쳐지고 Error 는 메시지+스택으로 기록됨.
|
|
39
|
+
|
|
40
|
+
## withMaxLevel
|
|
41
|
+
|
|
42
|
+
- `withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter` — 리포터 래핑. `logObj.level > maxLevel` 인 로그는 위임하지 않고 버림. 특정 리포터에 최대 상세도 상한을 둘 때 사용(예: 콘솔엔 info 까지만).
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const reporter = withMaxLevel(new PrettyReporter(), LogLevels.info);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 주의사항
|
|
49
|
+
|
|
50
|
+
- prod 에서는 콘솔 출력이 없고 `.logs` 파일로만 남음 — 운영 로그 확인은 파일 기준.
|
|
51
|
+
- 파일 리포터는 `process.cwd()` 기준 `.logs` 에 기록하므로 작업 디렉토리에 의존.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# @simplysm/core-node — cpx
|
|
2
|
+
|
|
3
|
+
`import { cpx } from "@simplysm/core-node"`. 자식 프로세스 실행 네임스페이스. stdout/stderr 를 OS 시스템 인코딩(Windows 코드페이지 / POSIX `LANG`)으로 디코딩 수집하고, 0 이 아닌 종료 코드는 기본적으로 에러로 throw 한다.
|
|
4
|
+
|
|
5
|
+
## spawn / spawnSync
|
|
6
|
+
|
|
7
|
+
- `spawn(cmd: string, args: string[], options?): SpawnProcess` — 비동기 실행. `await` 하면 `SpawnResult` 반환. 기본 `stdio: "pipe"`.
|
|
8
|
+
- `spawnSync(cmd: string, args: string[], options?): SpawnResult` — 동기 실행, 결과 즉시 반환.
|
|
9
|
+
- `options` — Node `SpawnOptions`/`SpawnSyncOptions` + `reject?: boolean`.
|
|
10
|
+
- `options.reject?: boolean` — `false` 면 0 이 아닌 종료 코드라도 throw 하지 않고 `SpawnResult` 를 반환(종료 코드를 호출자가 직접 검사할 때). 미지정/`true` 면 실패 시 cmd·args·exitCode·출력 일부(최대 4000자)를 담은 메시지로 throw.
|
|
11
|
+
- `options.env` — `process.env` 위에 머지되어 자식에 전달(전체 교체 아님).
|
|
12
|
+
- `options.stdio` — `"pipe"`(또는 미지정)일 때만 stdout/stderr 캡처. `"inherit"` 등이면 결과 문자열은 빈 값.
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
const { stdout } = await cpx.spawn("git", ["rev-parse", "HEAD"]);
|
|
16
|
+
const r = cpx.spawnSync("node", ["-v"], { reject: false });
|
|
17
|
+
if (r.exitCode !== 0) { /* 직접 처리 */ }
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- `SpawnResult` — `{ stdout: string; stderr: string; exitCode: number }`. 시그널 종료 시 exitCode 는 1 로 채워짐.
|
|
21
|
+
- `SpawnProcess` — `spawn` 반환값. `PromiseLike<SpawnResult>`(then/catch 가능) + `get pid(): number | undefined` + `kill(signal?: NodeJS.Signals | number): boolean`. 완료 전 프로세스를 제어할 때 사용.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
const proc = cpx.spawn("long-task", []);
|
|
25
|
+
proc.kill("SIGTERM");
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 인코딩
|
|
29
|
+
|
|
30
|
+
- `getSystemEncoding(): string` — OS 시스템 인코딩 감지(Windows: `chcp` 코드페이지, POSIX: `LANG`/`LC_ALL` 의 `.` 뒤 부분). 결과를 캐시. 감지 실패 시 `"utf-8"`. spawn 결과 디코딩에 내부적으로 사용.
|
|
31
|
+
- `resetEncodingCache(): void` — 위 캐시 무효화(코드페이지 변경 후 재감지 / 테스트 용).
|
|
32
|
+
- `codePageToEncoding(codePage: number): string` — Windows 코드페이지 번호 → 인코딩명(예: 65001→utf-8, 949→euc-kr, 932→shift-jis). 매핑 없으면 `"utf-8"`.
|
|
33
|
+
- `decodeBytes(raw: Uint8Array, systemEncoding?: string): string` — 바이트 디코딩. 인코딩이 utf-8 이면 그대로 디코딩, 아니면 utf-8(fatal) 시도 후 실패 시 시스템 인코딩으로 폴백. `systemEncoding` 미지정 시 `getSystemEncoding()` 사용.
|
|
34
|
+
- `resolveStdioPipe(stdio): { stdout: boolean; stderr: boolean }` — `stdio` 옵션에서 stdout/stderr 가 pipe 인지 판정. 배열이면 인덱스 1/2 가 `"pipe"` 인지 검사, 단일 값/`null` 이면 둘 다 동일 판정. 캡처 여부 결정에 사용.
|
|
35
|
+
|
|
36
|
+
## 주의사항
|
|
37
|
+
|
|
38
|
+
- 실패 시 throw 가 기본 — silent skip 금지 원칙과 일치. 종료 코드를 직접 다루려면 명시적으로 `reject: false`.
|
|
39
|
+
- 인코딩 디코딩 비용을 피하려면 `stdio: "inherit"` 로 캡처를 끄면 됨(단 결과 문자열은 빈 값).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @simplysm/core-node — FsWatcher
|
|
2
|
+
|
|
3
|
+
chokidar 기반 파일 감시 래퍼. 짧은 시간 내 다발한 이벤트를 디바운스+병합해 콜백을 한 번만 호출하고, Windows EPERM 발생 시 watcher 를 자동 재시작한다. glob 패턴 watch 를 지원한다.
|
|
4
|
+
|
|
5
|
+
## API
|
|
6
|
+
|
|
7
|
+
- `FsWatcher.watch(paths: string[], options?: chokidar.ChokidarOptions): Promise<FsWatcher>` — 정적 팩토리. ready 까지 대기 후 인스턴스 반환. ready 전 에러가 나면 close 하고 throw. `paths` 는 glob 패턴 허용(메타문자 이전 base 디렉토리를 추출해 chokidar 에 등록하고, 실제 매칭은 minimatch 로 수행).
|
|
8
|
+
- `options` — chokidar `ChokidarOptions`. 내부에서 `ignoreInitial` 은 항상 `true` 로 강제(초기 스캔 이벤트 무시).
|
|
9
|
+
- `options.ignoreInitial: false` — 호출자가 명시하면 onChange 콜백이 **빈 배열로 1회 선호출**됨(실제 초기 파일 목록은 전달하지 않음 — 이벤트 병합 충돌 방지). 초기 1회 전체 빌드 트리거 용도.
|
|
10
|
+
- `onChange(opt: { delay?: number }, cb): this` — 변경 콜백 등록(체이닝 가능).
|
|
11
|
+
- `opt.delay?: number` — 디바운스 ms. 마지막 이벤트 후 이 시간만큼 잠잠하면 누적 변경을 한 번에 전달.
|
|
12
|
+
- `cb: (changeInfos: FsWatcherChangeInfo[]) => void | Promise<void>` — 병합된 변경 목록으로 호출.
|
|
13
|
+
- `close(): Promise<void>` — 디바운스 큐를 dispose 한 뒤 chokidar 종료.
|
|
14
|
+
|
|
15
|
+
## 타입
|
|
16
|
+
|
|
17
|
+
- `FsWatcherChangeInfo` — `{ event: FsWatcherEvent; path: PosixPath }`. path 는 POSIX 슬래시.
|
|
18
|
+
- `FsWatcherEvent` — `"add" | "addDir" | "change" | "unlink" | "unlinkDir"`. 파일/디렉토리의 생성("add"/"addDir")·변경("change")·삭제("unlink"/"unlinkDir") 구분.
|
|
19
|
+
|
|
20
|
+
## 이벤트 병합 규칙
|
|
21
|
+
|
|
22
|
+
같은 경로의 연속 이벤트는 디바운스 윈도 안에서 병합된다: `add+change`→`add`, `add+unlink`→제거(상쇄), `addDir+unlinkDir`→제거, `unlink+add`→`add`, `unlink+change`→`change`, `unlinkDir+addDir`→`addDir`. 그 외 조합은 최신 이벤트로 덮어씀. 생성 직후 삭제 등 상쇄된 변경은 콜백에 나타나지 않음.
|
|
23
|
+
|
|
24
|
+
## 사용 예
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
const watcher = await FsWatcher.watch(["src/**/*.ts"], { ignoreInitial: false });
|
|
28
|
+
watcher.onChange({ delay: 300 }, async (changes) => {
|
|
29
|
+
for (const { event, path } of changes) console.log(`${event}: ${path}`);
|
|
30
|
+
});
|
|
31
|
+
await watcher.close();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 주의사항
|
|
35
|
+
|
|
36
|
+
- `ignoreInitial` 을 다른 값으로 줘도 chokidar 에는 `true` 로 강제됨 — 초기 목록이 필요하면 `false` 로 빈 배열 선호출 신호를 받아 호출자가 직접 스캔.
|
|
37
|
+
- EPERM 자동 재시작은 최대 3회(1초 간격). 초과 시 error 로그 후 중단하므로 이후 변경이 감지되지 않을 수 있음.
|
|
38
|
+
- 모듈 로드 시 native `fs.FSWatcher.prototype.emit` 에 가드를 설치해, listener 없는 orphan `error` 이벤트로 인한 프로세스 종료를 방지함(부수효과 — import 만으로 1회 적용).
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# @simplysm/core-node — fsx
|
|
2
|
+
|
|
3
|
+
`import { fsx } from "@simplysm/core-node"`. Node `fs` 래퍼 네임스페이스. 대부분 함수는 동기(`...Sync`)와 비동기(Promise) 쌍으로 제공된다. IO 실패 시 `SdError(err, targetPath)` 로 경로를 포함해 throw 하고, 쓰기 계열은 상위 디렉토리를 자동 생성한다.
|
|
4
|
+
|
|
5
|
+
## 존재 확인
|
|
6
|
+
|
|
7
|
+
- `existsSync(targetPath: string): boolean` / `exists(targetPath: string): Promise<boolean>` — 파일/디렉토리 존재 여부. 비동기는 `fs.access` 실패를 throw 하지 않고 false 로 반환. 파일·디렉토리 구분은 없음.
|
|
8
|
+
|
|
9
|
+
## 디렉토리 생성
|
|
10
|
+
|
|
11
|
+
- `mkdirSync(targetPath: string): void` / `mkdir(targetPath: string): Promise<void>` — `recursive: true` 재귀 생성. 이미 존재하면 무시.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
await fsx.mkdir("dist/sub");
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 삭제
|
|
18
|
+
|
|
19
|
+
- `rmSync(targetPath: string): void` — 재귀·force 삭제, **재시도 없이 즉시 실패**. 파일 잠금 우려가 있으면 비동기 `rm` 사용.
|
|
20
|
+
- `rm(targetPath: string): Promise<void>` — 재귀·force 삭제, 일시 오류 시 500ms 간격 최대 6회 재시도(Windows 잠금 대응).
|
|
21
|
+
|
|
22
|
+
## 복사
|
|
23
|
+
|
|
24
|
+
- `copySync(sourcePath: string, targetPath: string, filter?): void` / `copy(...): Promise<void>` — 파일·디렉토리 재귀 복사. **source 미존재 시 아무 작업 없이 반환**. 파일 복사는 실패 시 500ms 간격 최대 7회(0~6) 재시도. 비동기는 디렉토리 하위를 `parallelAsync` 병렬 복사.
|
|
25
|
+
- `filter?: (absolutePath: string) => boolean` — 하위 항목마다 **절대 경로**로 호출, true 만 복사. 최상위 source 자신은 필터 대상 아님. 디렉토리에 false 면 그 디렉토리와 모든 하위를 건너뜀. 특정 확장자·폴더만 복사할 때 사용.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
await fsx.copy("src", "dist", (p) => !p.endsWith(".map"));
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 파일 읽기
|
|
32
|
+
|
|
33
|
+
- `readSync(targetPath: string): string` / `read(...): Promise<string>` — UTF-8 문자열로 읽기.
|
|
34
|
+
- `readBytesSync(targetPath: string): Uint8Array` / `readBytes(...): Promise<Uint8Array>` — 바이너리로 읽기.
|
|
35
|
+
- `readJsonSync<TData = unknown>(targetPath: string): TData` / `readJson<TData = unknown>(...): Promise<TData>` — `@simplysm/core-common` 의 `json.parse` 로 파싱(표준 `JSON` 아님 — Date 등 확장 타입 복원). 파싱 실패 시 경로 + 내용 500자 프리뷰를 포함해 throw.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
const cfg = await fsx.readJson<{ version: string }>("package.json");
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 파일 쓰기
|
|
42
|
+
|
|
43
|
+
- `writeSync(targetPath: string, data: string | Uint8Array): void` / `write(...): Promise<void>` — 상위 디렉토리 자동 생성 후 `flush: true` 로 기록(데이터 손실 방지 강제 플러시).
|
|
44
|
+
- `writeJsonSync(targetPath: string, data: unknown, options?): void` / `writeJson(...): Promise<void>` — `json.stringify` 로 직렬화 후 기록.
|
|
45
|
+
- `options.replacer?: (this, key: string | undefined, value: unknown) => unknown` — 직렬화 시 값 변환 replacer.
|
|
46
|
+
- `options.space?: string | number` — 들여쓰기(가독 출력용).
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
await fsx.writeJson("out.json", data, { space: 2 });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 디렉토리 읽기
|
|
53
|
+
|
|
54
|
+
- `readdirSync(targetPath: string): string[]` / `readdir(...): Promise<string[]>` — 직속 항목 이름(상대 이름) 배열. 절대 경로 아님.
|
|
55
|
+
|
|
56
|
+
## 파일 정보
|
|
57
|
+
|
|
58
|
+
- `statSync(targetPath: string): fs.Stats` / `stat(...): Promise<fs.Stats>` — 심볼릭 링크를 따라간 stat.
|
|
59
|
+
- `lstatSync(targetPath: string): fs.Stats` / `lstat(...): Promise<fs.Stats>` — 링크 자체의 stat(링크 비추적). 링크 여부 판별·복사 분기에 사용.
|
|
60
|
+
|
|
61
|
+
## Glob
|
|
62
|
+
|
|
63
|
+
- `globSync(pattern: string, options?: GlobOptions): string[]` / `glob(...): Promise<string[]>` — glob 매칭. 입력 패턴의 `\` 를 `/` 로 치환하고, 결과는 `path.resolve` 한 **절대 경로** 배열.
|
|
64
|
+
- `options?: GlobOptions` — `glob` 라이브러리 옵션(`dot`, `ignore`, `nodir` 등).
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
const files = await fsx.glob("src/**/*.ts", { ignore: ["**/*.d.ts"] });
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 유틸리티
|
|
71
|
+
|
|
72
|
+
- `clearEmptyDirectory(dirPath: string): Promise<void>` — 하위를 재귀 순회해 파일이 하나도 없는 빈 디렉토리를 삭제(하위 삭제로 비게 된 상위도 삭제). 미존재 시 무시.
|
|
73
|
+
- `findAllParentChildPathsSync(childGlob: string, fromPath: string, rootPath?: string): string[]` / `findAllParentChildPaths(...): Promise<string[]>` — fromPath 에서 루트 방향으로 부모 디렉토리를 순회하며 각 디렉토리에서 `childGlob` 매칭 파일을 수집(절대 경로). `tsconfig.json` 등 설정 파일 상향 탐색에 사용.
|
|
74
|
+
- `childGlob` — 각 디렉토리에서 검색할 glob 패턴.
|
|
75
|
+
- `fromPath` — 탐색 시작 경로.
|
|
76
|
+
- `rootPath?` — 탐색 중단 경로. 미지정이거나 fromPath 가 rootPath 하위가 아니면 파일시스템 루트까지 올라감.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const tsconfigs = await fsx.findAllParentChildPaths("tsconfig.json", srcDir, projectRoot);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 주의사항
|
|
83
|
+
|
|
84
|
+
- 동기 `rmSync`/`copySync` 는 재시도 동작이 비동기와 다름(rmSync 는 무재시도). Windows EPERM/잠금이 우려되면 비동기 버전 사용.
|
|
85
|
+
- glob 결과는 항상 절대 경로 — 상대 경로가 필요하면 호출 측에서 `path.relative` 처리.
|
|
86
|
+
- JSON IO 는 표준 `JSON` 이 아닌 `@simplysm/core-common` 의 `json` 사용 — Date 등 확장 타입 복원이 다름.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @simplysm/core-node — pathx
|
|
2
|
+
|
|
3
|
+
`import { pathx } from "@simplysm/core-node"`. 경로 정규화·판정·치환 유틸 네임스페이스. 내부 비교는 항상 POSIX 슬래시 기준이다.
|
|
4
|
+
|
|
5
|
+
## PosixPath 생성
|
|
6
|
+
|
|
7
|
+
- `posix(p: string): PosixPath` — `\` → `/` 치환만 수행(resolve·결합 안 함). 반환은 브랜드 타입 `PosixPath`.
|
|
8
|
+
- `posixResolve(...args: string[]): PosixPath` — `path.resolve` 로 절대 경로화한 뒤 슬래시 치환. 상대 입력은 cwd 기준으로 절대화.
|
|
9
|
+
- `PosixPath` — `string & { [POSIX]: never }` 브랜드 타입. `posix`/`posixResolve` 로만 생성 가능. POSIX 슬래시 경로임을 타입으로 보장할 때 쓰임(예: `FsWatcherChangeInfo.path`).
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
pathx.posix("C:\\a\\b"); // "C:/a/b"
|
|
13
|
+
pathx.posixResolve("a", "b"); // "<cwd>/a/b"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 판정·치환
|
|
17
|
+
|
|
18
|
+
- `isChildPath(childPath: string, parentPath: string): boolean` — child 가 parent 의 하위인지. **동일 경로는 false**. 내부적으로 양쪽을 `posixResolve` 정규화 후 `parent + "/"` 접두 비교.
|
|
19
|
+
- `changeFileDirectory(filePath: string, fromDirectory: string, toDirectory: string): string` — filePath 의 소속 디렉토리를 from→to 로 교체. `filePath === fromDirectory` 면 toDirectory 반환. from 하위가 아니면 `ArgumentError` throw. 빌드 출력 경로 산출에 사용.
|
|
20
|
+
- `basenameWithoutExt(filePath: string): string` — 마지막 확장자만 제거한 basename. `file.spec.ts` → `file.spec`.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
pathx.isChildPath("/a/b/c", "/a/b"); // true
|
|
24
|
+
pathx.changeFileDirectory("/src/x.ts", "/src", "/dist"); // "/dist/x.ts"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 타겟 필터링
|
|
28
|
+
|
|
29
|
+
- `filterByTargets(files: string[], targets: string[], cwd: string): string[]` — files 중 targets 경로와 일치하거나 그 하위인 것만 반환.
|
|
30
|
+
- `files` — 필터링할 파일 경로. cwd 하위 절대 경로 권장(cwd 외부 경로는 `../` 상대로 변환되어 매칭이 어려움).
|
|
31
|
+
- `targets` — cwd 기준 상대 경로(POSIX 슬래시 권장). **빈 배열이면 files 를 그대로 반환**("필터 없음" 의미).
|
|
32
|
+
- `cwd` — 절대 기준 디렉토리. files 의 cwd 상대 경로를 만들어 target 과 비교.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
pathx.filterByTargets(["/p/src/a.ts", "/p/tests/c.ts"], ["src"], "/p");
|
|
36
|
+
// → ["/p/src/a.ts"]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 주의사항
|
|
40
|
+
|
|
41
|
+
- `filterByTargets` 의 `targets` 가 비면 전체 통과 — 빈 결과를 기대하지 말 것.
|
|
42
|
+
- `changeFileDirectory` 는 범위 밖 입력을 silent 처리하지 않고 throw 하므로, 호출 전 `isChildPath` 로 보장하거나 throw 를 처리.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# @simplysm/core-node — Worker
|
|
2
|
+
|
|
3
|
+
worker_threads 를 타입 안전 RPC + 이벤트로 감싼다. 워커 파일은 `createWorker` 로 메서드 묶음을 `export default` 하고, 메인은 `Worker.create<typeof import("...")>()` 로 프록시를 만들어 메서드를 직접 호출한다. 인자/반환은 `@simplysm/core-common` 의 `transfer`(structured clone + transferList)로 직렬화된다.
|
|
4
|
+
|
|
5
|
+
## createWorker (워커 측)
|
|
6
|
+
|
|
7
|
+
- `createWorker<TMethods, TEvents>(methods: TMethods): { send; __methods; __events }` — 워커 스레드 내에서 메서드 핸들러를 등록. 반환값을 `export default` 한다. `parentPort` 가 없으면(메인에서 직접 실행) `SdError` throw.
|
|
8
|
+
- `methods: Record<string, (...args: any[]) => unknown>` — RPC 로 노출할 메서드. 반환은 동기/Promise 무관(메인에선 항상 Promise). 핸들러 throw 는 메인 호출에서 reject 로 전파.
|
|
9
|
+
- `TEvents extends Record<string, unknown>` — 워커→메인 이벤트 페이로드 타입 맵(선택, 기본은 이벤트 없음).
|
|
10
|
+
- `send<TEventName extends keyof TEvents & string>(event, data?): void` — 워커→메인 이벤트 전송.
|
|
11
|
+
- `__methods` / `__events` — 타입 추론 전용 필드(런타임 의미 없음). 메인의 `Worker.create` 가 시그니처를 끌어올 때 사용.
|
|
12
|
+
- 워커의 `process.stdout.write` 는 내부에서 가로채져 메시지 프로토콜로 메인의 stdout 에 전달됨(워커 로그가 메인 콘솔에 보임).
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
// worker.ts
|
|
16
|
+
interface Events { progress: number }
|
|
17
|
+
const methods = {
|
|
18
|
+
calc: (x: number) => { sender.send("progress", 50); return x * 2; },
|
|
19
|
+
};
|
|
20
|
+
const sender = createWorker<typeof methods, Events>(methods);
|
|
21
|
+
export default sender;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Worker.create (메인 측)
|
|
25
|
+
|
|
26
|
+
- `Worker.create<TModule extends WorkerModule>(filePath: string, opt?): WorkerProxy<TModule>` — 워커를 띄우고 프록시 반환. `.ts` 경로면 `lib/worker-dev-proxy.js`(tsx) 경유 로드, `.js` 면 직접 로드. `file://` URL / 절대 경로 모두 허용.
|
|
27
|
+
- `filePath` — 워커 파일 경로.
|
|
28
|
+
- `opt?: Omit<WorkerRawOptions, "stdout" | "stderr">` — `worker_threads` `WorkerOptions`(단 `stdout`/`stderr` 제외 — 내부에서 항상 캡처해 파이프). `opt.env` 는 `process.env` 위에 머지, `opt.argv` 는 워커 인자.
|
|
29
|
+
- `WorkerProxy<TModule>` — 프록시 형태:
|
|
30
|
+
- 각 메서드 — `(...args) => Promise<Awaited<R>>`. 호출하면 RPC. 워커 핸들러 에러는 reject.
|
|
31
|
+
- `on(event, listener)` / `off(event, listener)` — 워커 `send` 이벤트 구독/해제.
|
|
32
|
+
- `terminate(): Promise<void>` — 워커 종료. 대기 중 요청은 reject.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// main.ts
|
|
36
|
+
const worker = Worker.create<typeof import("./worker")>("./worker.ts");
|
|
37
|
+
worker.on("progress", (p) => console.log(p));
|
|
38
|
+
const r = await worker.calc(21); // 42
|
|
39
|
+
await worker.terminate();
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 타입
|
|
43
|
+
|
|
44
|
+
- `WorkerModule` — `{ default: { __methods; __events } }`. `Worker.create` 의 `TModule` 제약. `typeof import("./worker")` 로 충족.
|
|
45
|
+
- `PromisifyMethods<TMethods>` — 각 메서드 반환을 `Promise<Awaited<R>>` 로 매핑(워커 호출은 항상 비동기).
|
|
46
|
+
- `WorkerProxy<TModule>` — 위 프록시 타입(promise화 메서드 + on/off + terminate).
|
|
47
|
+
- `WorkerRequest` — `{ id; method; params }` 내부 요청 메시지.
|
|
48
|
+
- `WorkerResponse` — 내부 응답 유니온: `return`(반환값) / `error`(Error) / `event`(send 이벤트) / `log`(stdout 전달).
|
|
49
|
+
|
|
50
|
+
## 주의사항
|
|
51
|
+
|
|
52
|
+
- 비정상 종료(exit code≠0)·워커 error 시 대기 중 모든 요청이 reject 되고 error 로깅됨 — silent 무시 아님.
|
|
53
|
+
- 워커 메서드는 항상 Promise 반환 — 동기 시그니처여도 메인에선 `await` 필요.
|
|
54
|
+
- 인자/반환은 transfer 직렬화 가능한 값이어야 함(함수·일부 클래스 인스턴스 제약).
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @simplysm/excel
|
|
2
|
+
|
|
3
|
+
OOXML(.xlsx) 워크북을 ZIP 단위 lazy-load 로 읽고 쓰는 라이브러리. 대용량 파일에서도 접근한 셀에 필요한 XML 파트만 로드한다(모든 셀 메서드가 `async` 인 이유). 워크북 생성·시트 추가·셀 값/스타일·조건부 서식·이미지 삽입·Zod 스키마 기반 레코드 매핑을 제공한다.
|
|
4
|
+
|
|
5
|
+
## 사용 트리거 인덱스
|
|
6
|
+
|
|
7
|
+
- **ExcelWorkbook / ExcelWorksheet** — .xlsx 파일을 열거나 새로 만들고 시트를 다루며 바이트로 내보낼 때. 시트 단위 데이터 테이블·매트릭스·이미지·뷰(zoom/freeze) 처리 포함. 자세히: [workbook-worksheet.md](./workbook-worksheet.md)
|
|
8
|
+
- **ExcelCell / ExcelRow / ExcelCol** — 개별 셀의 값·수식·병합·스타일을 읽고 쓰거나 행/열 단위로 셀을 순회할 때. 자세히: [cell.md](./cell.md)
|
|
9
|
+
- **셀 스타일 (ExcelStyleOptions / ExcelFont)** — 셀 또는 워크북 default 의 배경·테두리·정렬·숫자형식·폰트를 지정할 때. 자세히: [style.md](./style.md)
|
|
10
|
+
- **조건부 서식 (ExcelConditionalRule / ExcelConditionalRuleStyle)** — 셀/범위에 값 비교·텍스트 매칭·수식 기반 native CF 규칙을 추가할 때. 자세히: [conditional-format.md](./conditional-format.md)
|
|
11
|
+
- **ExcelWrapper** — Zod 스키마로 헤더 매핑·타입 변환·유효성 검사를 자동화해 레코드 배열 ↔ Excel 을 변환할 때. 자세히: [wrapper.md](./wrapper.md)
|
|
12
|
+
- **ExcelUtils** — 셀 주소(A1 ↔ 좌표) 변환, Excel 날짜 시리얼 ↔ 타임스탬프 변환, 숫자형식 코드/ID/이름 상호 변환이 필요할 때. 자세히: [utils.md](./utils.md)
|
|
13
|
+
- **값/주소/형식 타입** — 셀 값 유니온·숫자형식 프리셋·셀 타입·주소 좌표 타입을 시그니처에서 참조할 때. 아래 인라인 섹션 참조.
|
|
14
|
+
- **OOXML XML-shape 타입** — `ExcelXml*` / `Excel*Data` 류. 라이브러리 내부 XML 파서/직렬화기가 쓰는 OOXML 노드 구조 타입. 아래 인라인 섹션 참조.
|
|
15
|
+
|
|
16
|
+
## 값/주소/형식 타입
|
|
17
|
+
|
|
18
|
+
`./types` 가 노출하는 사용자 대면 타입. 셀 값을 다루거나 메서드 시그니처를 해석할 때 참조.
|
|
19
|
+
|
|
20
|
+
- `ExcelValueType` = `number | string | DateOnly | DateTime | Time | boolean | undefined` — 셀이 가질 수 있는 값 유니온. `getValue()` 반환·`setValue()` 인자 타입. `undefined` = 빈 셀(읽기 시 값 없음, 쓰기 시 셀 삭제).
|
|
21
|
+
- `ExcelNumberFormat` = `"number" | "string" | "DateOnly" | "DateTime" | "Time"` — 숫자형식 프리셋 이름. 셀의 numFmt 를 의미 단위로 분류한 값. `"number"` = 일반 수치, `"string"` = 텍스트 형식, 나머지는 날짜/시간 시리얼 해석에 사용.
|
|
22
|
+
- `ExcelCellType` = `"s" | "b" | "str" | "n" | "inlineStr" | "e"` — OOXML 셀 `t` 속성. `"s"` = SharedString 인덱스 참조, `"b"` = boolean, `"str"` = 수식 결과 문자열, `"n"` = 숫자, `"inlineStr"` = 인라인 서식 텍스트, `"e"` = 에러(읽기 시 throw). 보통 직접 다루지 않고 `getValue`/`setValue` 가 자동 처리.
|
|
23
|
+
- `ExcelAddressPoint` = `{ r: number; c: number }` — 0 기반 행(`r`)·열(`c`) 좌표. 셀 단일 위치 단위.
|
|
24
|
+
- `ExcelAddressRangePoint` = `{ s: ExcelAddressPoint; e: ExcelAddressPoint }` — 범위 좌표. `s` = 시작(좌상단), `e` = 끝(우하단). `getRange()` 반환 타입.
|
|
25
|
+
- `ExcelBorderPosition` = `"left" | "right" | "top" | "bottom"` — 테두리 적용 변. `ExcelStyleOptions.border` 배열 원소.
|
|
26
|
+
- `ExcelHorizontalAlign` = `"center" | "left" | "right"` — 가로 정렬.
|
|
27
|
+
- `ExcelVerticalAlign` = `"center" | "top" | "bottom"` — 세로 정렬.
|
|
28
|
+
- `ExcelFontUnderline` = `"single" | "double" | "singleAccounting" | "doubleAccounting"` — 밑줄 종류. OOXML `<u val="...">` 의 val 에 그대로 매핑.
|
|
29
|
+
|
|
30
|
+
## OOXML XML-shape 타입
|
|
31
|
+
|
|
32
|
+
`./types` 가 함께 export 하는 다음 인터페이스·타입은 라이브러리 내부 XML 파서/직렬화기가 OOXML 파트의 파싱 결과(`xml2js` 스타일의 `$`=속성, 배열 래핑) 구조를 표현하는 데이터 모델이다. 일반 사용 흐름(값/스타일/시트 API)에서는 직접 다루지 않으며, OOXML 노드를 직접 조작·검증할 때만 참조한다.
|
|
33
|
+
|
|
34
|
+
- `ExcelXmlContentTypeData` — `[Content_Types].xml` 의 `Types`(Default/Override 파트 등록) 구조.
|
|
35
|
+
- `ExcelXmlRelationshipData` / `ExcelRelationshipData` — `*.rels` 의 `Relationships` 및 개별 `Relationship`(Id/Target/Type) 구조.
|
|
36
|
+
- `ExcelXmlWorkbookData` — `xl/workbook.xml` 의 `workbook`(bookViews/sheets 등) 구조.
|
|
37
|
+
- `ExcelXmlWorksheetData` — `xl/worksheets/sheetN.xml` 의 `worksheet`(sheetPr/dimension/sheetViews/cols/sheetData/mergeCells/conditionalFormatting/drawing 등) 구조.
|
|
38
|
+
- `ExcelXmlConditionalFormattingData` / `ExcelXmlCfRuleData` — `<conditionalFormatting>` 블록과 `<cfRule>`(type/operator/priority/dxfId/formula) 구조. `ExcelXmlCfRuleData["$"]["type"|"operator"]` 는 규칙 spec 빌드 시 내부적으로 참조된다.
|
|
39
|
+
- `ExcelRowData` / `ExcelCellData` — `<row r="..">` 과 `<c r=".." s=".." t="..">`(v=값, f=수식, is=인라인 문자열) 구조.
|
|
40
|
+
- `ExcelXmlDrawingData` — `xl/drawings/drawingN.xml` 의 `wsDr`(twoCellAnchor/pic/blipFill/spPr 등 이미지 앵커) 구조.
|
|
41
|
+
- `ExcelXmlSharedStringData` / `ExcelXmlSharedStringDataSi` / `ExcelXmlSharedStringDataText` — `xl/sharedStrings.xml` 의 `sst` 와 `si` 항목(단순 `t` 또는 서식 run `r[]`), 텍스트 노드(`space="preserve"` 보존) 구조.
|
|
42
|
+
- `ExcelXmlStyleData` 및 하위(`...Font` / `...Fill` / `...Border` / `...Xf` / `...Dxf`) — `xl/styles.xml` 의 `styleSheet`(numFmts/fonts/fills/borders/cellXfs/dxfs) 와 각 자원 노드 구조.
|
|
43
|
+
- `ExcelXml` = `{ readonly data: unknown; cleanup(): void }` — 모든 XML 파트 래퍼가 따르는 공통 인터페이스. `data` = 파싱된 노드 트리, `cleanup()` = 직렬화 전 마무리 정리.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# @simplysm/excel — ExcelCell / ExcelRow / ExcelCol
|
|
2
|
+
|
|
3
|
+
개별 셀의 값·수식·병합·스타일을 읽고 쓰거나, 행/열 단위로 셀을 순회할 때 함께 읽는 묶음. 모든 셀 I/O 가 `async` 인 이유는 셀 타입별로 필요한 XML 파트(SharedStrings/Styles)만 lazy-load 하기 때문이다. 인스턴스는 `ws.cell(r,c)` / `ws.row(r)` / `ws.col(c)` 로 얻으며 좌표는 모두 0 기반.
|
|
4
|
+
|
|
5
|
+
## ExcelCell
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
readonly addr: ExcelAddressPoint // { r, c } 0 기반
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### 값/수식
|
|
12
|
+
|
|
13
|
+
- `setValue(val: ExcelValueType): Promise<void>` — 셀 값 설정. `val` 타입별 분기: string → SharedString 으로 등록, boolean → `"1"/"0"`, number → 숫자 셀, `DateOnly`/`DateTime`/`Time` → Excel 날짜 시리얼 + 해당 numFmt 스타일 자동 적용, `undefined`/`null` → 셀 삭제. 그 외 타입은 throw.
|
|
14
|
+
- `getValue(): Promise<ExcelValueType>` — 셀 값 반환. 빈 셀이면 `undefined`. 셀 타입·numFmt 를 보고 string/boolean/number/`DateOnly`/`DateTime`/`Time` 로 복원. 셀 타입이 `"e"`(에러)면 throw.
|
|
15
|
+
- `setFormula(val: string | undefined): Promise<void>` — 수식 설정. `undefined` 면 셀 삭제. 설정 시 셀 타입은 `str` 로.
|
|
16
|
+
- `getFormula(): Promise<string | undefined>` — 셀 수식 반환(없으면 `undefined`).
|
|
17
|
+
|
|
18
|
+
### 병합
|
|
19
|
+
|
|
20
|
+
- `merge(r, c): Promise<void>` — 현재 셀을 시작점으로 끝 좌표 `(r, c)`(0 기반)까지 병합. 예: A1 에서 `merge(2, 2)` → A1:C3.
|
|
21
|
+
|
|
22
|
+
### 스타일
|
|
23
|
+
|
|
24
|
+
- `setStyle(opts: ExcelStyleOptions): Promise<void>` — 배경·테두리·정렬·숫자형식·폰트 적용. 기존 스타일이 있으면 clone 후 병합. 자세히: [style.md](./style.md).
|
|
25
|
+
- `getStyleId(): Promise<string | undefined>` — 셀의 스타일 ID(없으면 `undefined`).
|
|
26
|
+
- `setStyleId(styleId: string | undefined): Promise<void>` — 스타일 ID 를 직접 지정/해제. 이미 만들어진 스타일을 재사용할 때.
|
|
27
|
+
|
|
28
|
+
### 사용 예
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
await ws.cell(0, 0).setValue("이름");
|
|
32
|
+
await ws.cell(1, 0).setValue(new DateOnly(2026, 6, 1)); // 날짜 numFmt 자동
|
|
33
|
+
await ws.cell(0, 0).merge(0, 2); // A1:C1 병합
|
|
34
|
+
const v = await ws.cell(1, 0).getValue(); // DateOnly 인스턴스
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## ExcelRow
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
cell(c: number): ExcelCell // 이 행의 c열 셀(0 기반)
|
|
41
|
+
getCells(): Promise<ExcelCell[]> // 데이터 범위 폭만큼 셀 배열(인덱스=열)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- `getCells` 는 시트 데이터 범위의 시작~끝 열까지 셀을 채우며, 배열 인덱스가 열 번호와 일치(앞쪽 빈 열은 sparse).
|
|
45
|
+
|
|
46
|
+
## ExcelCol
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
cell(r: number): ExcelCell // 이 열의 r행 셀(0 기반)
|
|
50
|
+
getCells(): Promise<ExcelCell[]> // 데이터 범위 높이만큼 셀 배열(인덱스=행)
|
|
51
|
+
setWidth(size: number): Promise<void> // 열 너비 설정
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- `setWidth` 의 `size` — Excel 열 너비 단위(문자 폭 기준). 컬럼 폭을 데이터에 맞게 넓힐 때.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @simplysm/excel — 조건부 서식
|
|
2
|
+
|
|
3
|
+
셀/범위에 값 비교·텍스트 매칭·수식 기반의 native CF(Excel 조건부 서식) 규칙을 추가할 때 함께 읽는 묶음. `ws.addConditionalFormat` 에 `ExcelConditionalRule[]` 을 넘기며, 각 규칙은 `ExcelConditionalRuleStyle` 강조 스타일을 가진다.
|
|
4
|
+
|
|
5
|
+
## ws.addConditionalFormat
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
addConditionalFormat(opts: { ref: string; rules: ExcelConditionalRule[] }): Promise<void>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- `opts.ref: string` — 단일 셀(`"A1"`) 또는 범위(`"A1:B10"`) Excel 주소. 규칙 수식은 범위의 좌상단 셀 기준으로 생성된다.
|
|
12
|
+
- `opts.rules: ExcelConditionalRule[]` — 적용할 규칙 배열. 배열 순서가 priority(앞이 우선)이며, 호출 간에는 시트 전역 카운터로 이어붙는다. 빈 배열이면 no-op.
|
|
13
|
+
- 같은 시트에 여러 번 호출하면 호출마다 `<conditionalFormatting>` 블록이 누적된다. 정적 셀 스타일과의 합성은 Excel native CF 오버레이에 위임.
|
|
14
|
+
|
|
15
|
+
## ExcelConditionalRule
|
|
16
|
+
|
|
17
|
+
4개 변형의 유니온. 모든 변형이 `style: ExcelConditionalRuleStyle` 를 가진다.
|
|
18
|
+
|
|
19
|
+
- `{ type: "cellIs"; op: "<" | ">" | "<=" | ">=" | "=" | "<>"; value: number | string; style }` — 단일 값 비교. `op` = 비교 연산자, `value` = 비교 대상(숫자는 raw formula, 문자열은 따옴표 리터럴 formula 로 emit).
|
|
20
|
+
- `{ type: "cellIs"; op: "between" | "notBetween"; value: [number, number] | [string, string]; style }` — 구간 비교. `value` = `[a, b]` 튜플(양 끝 inclusive). `"between"` = 구간 안, `"notBetween"` = 구간 밖.
|
|
21
|
+
- `{ type: "text"; op: "contains" | "notContains" | "beginsWith" | "endsWith"; value: string; style }` — 텍스트 매칭. `value` = 비교 문자열. `"contains"`/`"notContains"` = 포함/미포함, `"beginsWith"`/`"endsWith"` = 시작/끝 일치. SEARCH 기반 대소문자 무시 고정.
|
|
22
|
+
- `{ type: "expression"; formula: string; style }` — 임의 수식 규칙. `formula` = Excel 수식 문자열(true 면 강조). 프리셋으로 표현 못하는 조건일 때.
|
|
23
|
+
|
|
24
|
+
## ExcelConditionalRuleStyle
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
interface ExcelConditionalRuleStyle {
|
|
28
|
+
background?: string;
|
|
29
|
+
fontColor?: string;
|
|
30
|
+
fontWeight?: "bold" | "normal";
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- `background?: string` — 강조 배경색. ARGB 8자리(예: `"00FFFF00"`).
|
|
35
|
+
- `fontColor?: string` — 강조 글자색. ARGB 8자리.
|
|
36
|
+
- `fontWeight?: "bold" | "normal"` — 글자 굵기. `"bold"` = 굵게, `"normal"` = base 가 bold 라도 강제 normal.
|
|
37
|
+
|
|
38
|
+
미지정 필드는 base 셀 스타일을 그대로 두고, 지정 필드만 OOXML dxf 로 emit 되어 native CF 오버레이로 합성된다.
|
|
39
|
+
|
|
40
|
+
## 사용 예
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
await ws.addConditionalFormat({
|
|
44
|
+
ref: "B2:B100",
|
|
45
|
+
rules: [
|
|
46
|
+
{ type: "cellIs", op: "<", value: 1000, style: { background: "00FF0000" } },
|
|
47
|
+
{ type: "cellIs", op: "between", value: [1000, 4999], style: { background: "00FFFF00" } },
|
|
48
|
+
{ type: "text", op: "contains", value: "긴급", style: { fontColor: "00FF0000", fontWeight: "bold" } },
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
```
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# @simplysm/excel — 셀 스타일
|
|
2
|
+
|
|
3
|
+
셀(`cell.setStyle`) 또는 워크북 default(`wb.setDefaultStyle`)의 배경·테두리·정렬·숫자형식·폰트를 지정할 때 함께 읽는 묶음. 두 API 모두 `ExcelStyleOptions` 를 받으며 `font` 는 `ExcelFont` 를 공유한다.
|
|
4
|
+
|
|
5
|
+
## ExcelStyleOptions
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
interface ExcelStyleOptions {
|
|
9
|
+
background?: string;
|
|
10
|
+
border?: ExcelBorderPosition[];
|
|
11
|
+
horizontalAlign?: ExcelHorizontalAlign;
|
|
12
|
+
verticalAlign?: ExcelVerticalAlign;
|
|
13
|
+
numberFormat?: ExcelNumberFormat;
|
|
14
|
+
numberFormatCode?: string;
|
|
15
|
+
font?: ExcelFont;
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- `background?: string` — 배경색. ARGB 8자리 16진수(예: `"00FF0000"` = 빨강). 셀 채우기 색이 필요할 때.
|
|
20
|
+
- `border?: ExcelBorderPosition[]` — 테두리를 그릴 변 배열. 원소 = `"left" | "right" | "top" | "bottom"`. 4변 모두면 `["left","right","top","bottom"]`.
|
|
21
|
+
- `horizontalAlign?: "center" | "left" | "right"` — 가로 정렬.
|
|
22
|
+
- `verticalAlign?: "center" | "top" | "bottom"` — 세로 정렬.
|
|
23
|
+
- `numberFormat?: "number" | "string" | "DateOnly" | "DateTime" | "Time"` — 숫자형식 프리셋. `"number"` = 일반 수치, `"string"` = 텍스트 형식, 나머지는 날짜/시간 표시 형식. 표준 형식 적용 시 사용.
|
|
24
|
+
- `numberFormatCode?: string` — 커스텀 Excel formatCode(예: `"0.000000"`, `"#,##0.00"`, `"0.00%"`). `numberFormat` 과 동시 지정 시 **이 필드가 우선**. 프리셋에 없는 세밀한 형식이 필요할 때.
|
|
25
|
+
- `font?: ExcelFont` — 폰트 묶음(아래). 미지정 속성은 워크북 default 폰트로 표시.
|
|
26
|
+
|
|
27
|
+
## ExcelFont
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
interface ExcelFont {
|
|
31
|
+
size?: number;
|
|
32
|
+
family?: string;
|
|
33
|
+
bold?: boolean;
|
|
34
|
+
italic?: boolean;
|
|
35
|
+
underline?: ExcelFontUnderline;
|
|
36
|
+
color?: string;
|
|
37
|
+
strike?: boolean;
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- `size?: number` — 폰트 크기(pt).
|
|
42
|
+
- `family?: string` — 폰트명(예: `"맑은 고딕"`, `"Calibri"`).
|
|
43
|
+
- `bold?: boolean` — 굵게. `true` 면 굵게.
|
|
44
|
+
- `italic?: boolean` — 기울임. `true` 면 이탤릭.
|
|
45
|
+
- `underline?: "single" | "double" | "singleAccounting" | "doubleAccounting"` — 밑줄 종류. OOXML `<u val="...">` val 에 그대로 매핑.
|
|
46
|
+
- `color?: string` — 글자색. ARGB 8자리(예: `"00FF0000"`).
|
|
47
|
+
- `strike?: boolean` — 취소선. `true` 면 가운데줄.
|
|
48
|
+
|
|
49
|
+
미지정 폰트 속성은 OOXML `<font>` 자식으로 emit 되지 않고 Excel 기본값으로 표시된다.
|
|
50
|
+
|
|
51
|
+
## cell.setStyle vs wb.setDefaultStyle
|
|
52
|
+
|
|
53
|
+
- `cell.setStyle(opts)` — 해당 셀에만 스타일 적용. 기존 셀 스타일이 있으면 clone 후 옵션을 병합.
|
|
54
|
+
- `wb.setDefaultStyle(opts)` — `xl/styles.xml` 의 `fonts[0]`/`fills[0]`/`borders[0]`(OOXML default 슬롯) 자체를 덮어써, fontId/fillId/borderId 를 명시하지 않은 모든 셀에 전역 적용. `horizontalAlign`/`verticalAlign`/`numberFormat`/`numberFormatCode` 는 0번 슬롯 개념이 없어 `cellXfs[0]` 에 박힌다. 옵션이 없는 자원은 0번 슬롯이 빈 슬롯으로 reset 되며, 미호출 시 원본이 보존된다.
|
|
55
|
+
|
|
56
|
+
### 사용 예
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
await wb.setDefaultStyle({ font: { family: "맑은 고딕", size: 10 }, horizontalAlign: "center" });
|
|
60
|
+
|
|
61
|
+
await ws.cell(0, 0).setStyle({
|
|
62
|
+
background: "00FFFF00",
|
|
63
|
+
border: ["left", "right", "top", "bottom"],
|
|
64
|
+
font: { bold: true, color: "00FF0000" },
|
|
65
|
+
numberFormatCode: "#,##0",
|
|
66
|
+
});
|
|
67
|
+
```
|