@simplysm/sd-claude 14.0.91 → 14.0.93

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 (93) hide show
  1. package/claude/references/sd-simplysm14/README.md +7 -6
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +59 -39
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +119 -186
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +70 -31
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +55 -57
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +86 -105
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +48 -57
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +37 -47
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +82 -74
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +61 -50
  11. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +74 -57
  12. package/claude/references/sd-simplysm14/apis/angular/sheet.md +63 -72
  13. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +23 -18
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +21 -19
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +23 -18
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +72 -32
  17. package/claude/references/sd-simplysm14/apis/core-browser/README.md +18 -18
  18. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +29 -29
  19. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +41 -41
  20. package/claude/references/sd-simplysm14/apis/core-common/README.md +97 -90
  21. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +75 -51
  22. package/claude/references/sd-simplysm14/apis/core-common/collection-ext.md +81 -0
  23. package/claude/references/sd-simplysm14/apis/core-common/errors.md +27 -29
  24. package/claude/references/sd-simplysm14/apis/core-common/obj.md +44 -45
  25. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +34 -33
  26. package/claude/references/sd-simplysm14/apis/core-common/value-types.md +86 -0
  27. package/claude/references/sd-simplysm14/apis/core-node/README.md +6 -6
  28. package/claude/references/sd-simplysm14/apis/core-node/consola.md +3 -0
  29. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +2 -2
  30. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +1 -1
  31. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +2 -2
  32. package/claude/references/sd-simplysm14/apis/core-node/worker.md +6 -3
  33. package/claude/references/sd-simplysm14/apis/excel/README.md +10 -10
  34. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +4 -2
  35. package/claude/references/sd-simplysm14/apis/excel/utils.md +1 -1
  36. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +6 -6
  37. package/claude/references/sd-simplysm14/apis/lint/README.md +6 -32
  38. package/claude/references/sd-simplysm14/apis/lint/recommended.md +60 -0
  39. package/claude/references/sd-simplysm14/apis/lint/rules.md +17 -17
  40. package/claude/references/sd-simplysm14/apis/orm-common/README.md +15 -6
  41. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +68 -102
  42. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +75 -89
  43. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +87 -99
  44. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +110 -147
  45. package/claude/references/sd-simplysm14/apis/orm-common/types.md +48 -51
  46. package/claude/references/sd-simplysm14/apis/orm-node/README.md +8 -13
  47. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +5 -5
  48. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +9 -6
  49. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +9 -8
  50. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +23 -19
  51. package/claude/references/sd-simplysm14/apis/service-client/README.md +20 -12
  52. package/claude/references/sd-simplysm14/apis/service-client/orm.md +6 -6
  53. package/claude/references/sd-simplysm14/apis/service-client/transport.md +1 -1
  54. package/claude/references/sd-simplysm14/apis/service-common/README.md +35 -32
  55. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +23 -22
  56. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +23 -23
  57. package/claude/references/sd-simplysm14/apis/service-server/README.md +51 -43
  58. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +6 -6
  59. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +31 -21
  60. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +8 -8
  61. package/claude/references/sd-simplysm14/apis/storage/README.md +55 -49
  62. package/claude/references/sd-simplysm14/manuals/client-component.md +843 -740
  63. package/claude/references/sd-simplysm14/manuals/client-crud.md +8 -0
  64. package/claude/references/sd-simplysm14/manuals/client-demo.md +6 -16
  65. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +26 -0
  66. package/claude/references/sd-simplysm14/manuals/logging.md +1 -1
  67. package/claude/references/sd-simplysm14/manuals/orm.md +15 -1
  68. package/claude/rules/sd-design-rules.md +7 -0
  69. package/claude/sd-system-prompt.md +5 -8
  70. package/claude/skills/sd-debug/SKILL.md +43 -0
  71. package/claude/skills/sd-debug/workflow.js +390 -0
  72. package/claude/skills/sd-demo/SKILL.md +18 -20
  73. package/claude/skills/sd-dev/SKILL.md +127 -24
  74. package/claude/skills/sd-docs/SKILL.md +5 -3
  75. package/claude/skills/sd-docs/references/subagent-prompt.md +2 -3
  76. package/claude/skills/sd-impl/SKILL.md +18 -18
  77. package/claude/skills/sd-manual/SKILL.md +1 -0
  78. package/claude/skills/sd-review/SKILL.md +24 -18
  79. package/claude/skills/sd-review/workflow.js +324 -0
  80. package/claude/skills/sd-spec/SKILL.md +96 -679
  81. package/claude/skills/sd-spec/references/example-spec.md +28 -50
  82. package/claude/skills/sd-spec/references/format-analyze.md +232 -0
  83. package/claude/skills/sd-spec/references/format-design.md +248 -0
  84. package/claude/skills/sd-spec/workflow-analyze.js +615 -0
  85. package/claude/skills/sd-spec/workflow-design.js +667 -0
  86. package/claude/skills/sd-unpack/scripts/handlers/office_com.py +5 -1
  87. package/package.json +1 -1
  88. package/scripts/postinstall.mjs +157 -18
  89. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +0 -68
  90. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +0 -77
  91. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +0 -86
  92. package/claude/skills/sd-skill/SKILL.md +0 -245
  93. package/claude/skills/sd-skill/scripts/run_eval.py +0 -380
@@ -1,6 +1,6 @@
1
1
  # @simplysm/core-browser — IndexedDB 영속화
2
2
 
3
- 브라우저 IndexedDB 를 다룰 때 함께 읽히는 묶음. `IndexedDbStore` 는 연결·트랜잭션·KV CRUD 를 담당하고, `IndexedDbVirtualFs` 는 그 위에 경로 키 기반 가상 파일트리(entry put/get, prefix 삭제, 자식 나열, 디렉터리 보장)를 얹음.
3
+ 브라우저 IndexedDB 를 다룰 때 함께 읽히는 묶음. `IndexedDbStore` 는 연결·트랜잭션·KV CRUD 를 담당하고, `IndexedDbVirtualFs` 는 그 위에 경로 키 기반 가상 파일트리(entry put/get, prefix 삭제, 자식 나열, 디렉터리 보장)를 얹는다.
4
4
 
5
5
  ## IndexedDbStore
6
6
 
@@ -17,28 +17,28 @@ store.close();
17
17
 
18
18
  시그니처:
19
19
 
20
- - new IndexedDbStore(dbName: string, dbVersion: number, storeConfigs: StoreConfig[]) — DB 이름·버전·스토어 설정으로 생성(연결은 지연, 첫 작업 시 오픈).
21
- - dbName: string — IndexedDB 데이터베이스 이름.
22
- - dbVersion: number — DB 버전. 올리면 `onupgradeneeded` 에서 누락 스토어를 생성. 스키마(스토어 추가) 변경 시 증가.
23
- - storeConfigs: StoreConfig[] — 생성할 오브젝트 스토어 목록.
24
- - StoreConfig — 스토어 설정 항목.
25
- - name: string — 오브젝트 스토어 이름. upgrade 시 미존재면 `createObjectStore` 로 생성.
26
- - keyPath: string — 스토어 keyPath(레코드에서 키로 쓸 속성명).
27
- - open(): Promise<IDBDatabase> — 연결을 열어 반환. 이미 열렸으면 캐시 반환, 진행 중이면 같은 Promise 공유(중복 오픈 방지). `onupgradeneeded` 시 없는 스토어만 생성. `onversionchange`/`onclose` 시 내부 캐시(`_db`/`_opening`)를 해제해 다음 호출에 재오픈. `onblocked` 면 `Error("다른 연결에 의해 데이터베이스가 차단되었습니다")`, `onerror` 면 원본 에러로 reject. CRUD 가 자동 호출하므로 직접 호출 불필요.
28
- - withStore<TResult>(storeName, mode, fn): Promise<TResult> — 트랜잭션 1건 안에서 `fn(store)` 실행 후 완료까지 대기. `fn` 이 throw 하면 `tx.abort()` 후 그 에러로 reject(롤백), 정상이면 `oncomplete` 시 결과 resolve, `onerror` 면 `tx.error` 로 reject. 커서 등 저수준 IDB 작업을 감쌀 때.
29
- - storeName: string — 트랜잭션 대상 스토어.
30
- - mode: IDBTransactionMode — `"readonly"`(읽기 전용) | `"readwrite"`(읽기·쓰기) | `"versionchange"`. 쓰기 작업이면 `"readwrite"`.
31
- - fn: (store: IDBObjectStore) => Promise<TResult> — 스토어를 받아 작업하는 콜백.
32
- - get<TValue>(storeName, key): Promise<TValue | undefined> — 키로 단건 조회. 미존재 시 `undefined`(결측 그대로 반환).
33
- - put(storeName, value): Promise<void> — 레코드 upsert. value 에 keyPath 속성이 포함돼야 함.
34
- - delete(storeName, key): Promise<void> — 키로 단건 삭제.
35
- - getAll<TItem>(storeName): Promise<TItem[]> — 스토어 전체 레코드 배열 반환.
36
- - close(): void — 연결을 닫고 내부 캐시 해제. 다음 작업 시 재오픈. 페이지 정리 시 호출.
20
+ - `new IndexedDbStore(dbName: string, dbVersion: number, storeConfigs: StoreConfig[])` — DB 이름·버전·스토어 설정으로 생성(연결은 지연, 첫 작업 시 오픈).
21
+ - `dbName: string` — IndexedDB 데이터베이스 이름.
22
+ - `dbVersion: number` — DB 버전. 올리면 `onupgradeneeded` 에서 누락 스토어를 생성. 스키마(스토어 추가) 변경 시 증가.
23
+ - `storeConfigs: StoreConfig[]` — 생성할 오브젝트 스토어 목록.
24
+ - `StoreConfig` — 스토어 설정 항목.
25
+ - `name: string` — 오브젝트 스토어 이름. upgrade 시 미존재면 `createObjectStore` 로 생성.
26
+ - `keyPath: string` — 스토어 keyPath(레코드에서 키로 쓸 속성명).
27
+ - `open(): Promise<IDBDatabase>` — 연결을 열어 반환. 이미 열렸으면 캐시 반환, 진행 중이면 같은 Promise 공유(중복 오픈 방지). `onupgradeneeded` 시 없는 스토어만 생성. `onversionchange`/`onclose` 시 내부 캐시(`_db`/`_opening`)를 해제해 다음 호출에 재오픈. `onblocked` 면 `Error("다른 연결에 의해 데이터베이스가 차단되었습니다")`, `onerror` 면 원본 에러로 reject. CRUD 가 자동 호출하므로 직접 호출 불필요.
28
+ - `withStore<TResult>(storeName, mode, fn): Promise<TResult>` — 트랜잭션 1건 안에서 `fn(store)` 실행 후 완료까지 대기. `fn` 이 throw 하면 `tx.abort()` 후 그 에러로 reject(롤백), 정상이면 `oncomplete` 시 결과 resolve, `onerror` 면 `tx.error` 로 reject. 커서 등 저수준 IDB 작업을 감쌀 때.
29
+ - `storeName: string` — 트랜잭션 대상 스토어.
30
+ - `mode: IDBTransactionMode` — `"readonly"`(읽기 전용) | `"readwrite"`(읽기·쓰기) | `"versionchange"`(스키마 변경). 쓰기 작업이면 `"readwrite"`.
31
+ - `fn: (store: IDBObjectStore) => Promise<TResult>` — 스토어를 받아 작업하는 콜백.
32
+ - `get<TValue>(storeName, key): Promise<TValue | undefined>` — 키로 단건 조회. 미존재 시 `undefined`(결측 그대로 반환).
33
+ - `put(storeName, value): Promise<void>` — 레코드 upsert. value 에 keyPath 속성이 포함돼야 함.
34
+ - `delete(storeName, key): Promise<void>` — 키로 단건 삭제.
35
+ - `getAll<TItem>(storeName): Promise<TItem[]>` — 스토어 전체 레코드 배열 반환.
36
+ - `close(): void` — 연결을 닫고 내부 캐시 해제. 다음 작업 시 재오픈. 페이지 정리 시 호출.
37
37
 
38
38
  주의:
39
39
 
40
- - `withStore` 의 fn 이 throw 하면 트랜잭션 전체 abort — 다건 쓰기를 하나의 `withStore` 안에 묶으면 원자성 보장.
41
- - 버전 변경(`onupgradeneeded`)은 누락 스토어 생성만 — 기존 스토어 스키마 변경·인덱스 추가는 별도 처리 필요.
40
+ - `withStore` 의 fn 이 throw 하면 트랜잭션 전체가 abort — 다건 쓰기를 하나의 `withStore` 안에 묶으면 원자성이 보장된다.
41
+ - 버전 변경(`onupgradeneeded`)은 누락 스토어 생성만 한다 — 기존 스토어 스키마 변경·인덱스 추가는 별도 처리가 필요하다.
42
42
 
43
43
  ## IndexedDbVirtualFs
44
44
 
@@ -54,27 +54,27 @@ const ok = await fs.deleteByPrefix("/root/a"); // 하위 전체 삭제, 삭제
54
54
 
55
55
  시그니처:
56
56
 
57
- - new IndexedDbVirtualFs(db: IndexedDbStore, storeName: string, keyField: string) — 백엔드 store·스토어 이름·키 필드명으로 생성.
58
- - db: IndexedDbStore — 백엔드 저장소.
59
- - storeName: string — 사용할 오브젝트 스토어 이름.
60
- - keyField: string — 레코드에서 경로 키를 담는 속성명(스토어 keyPath 와 일치해야 함).
61
- - VirtualFsEntry — 저장 엔트리 타입.
62
- - kind: "file" | "dir" — 엔트리 종류. `"file"` = 파일, `"dir"` = 디렉터리. 자식 나열·디렉터리 판정에 사용.
63
- - dataBase64: string — 파일 내용 base64. 디렉터리거나 빈 파일이면 생략(undefined).
64
- - getEntry(fullKey): Promise<VirtualFsEntry | undefined> — 전체 경로 키로 단건 조회. 미존재 시 `undefined`.
65
- - putEntry(fullKey, kind, dataBase64?): Promise<void> — 엔트리 저장. `keyField` 에 `fullKey`, 그리고 `kind`/`dataBase64` 를 함께 기록.
66
- - fullKey: string — 저장할 전체 경로 키.
67
- - kind: "file" | "dir" — 저장할 엔트리 종류.
68
- - dataBase64: string — 파일 데이터(base64). 디렉터리면 생략.
69
- - deleteByPrefix(keyPrefix): Promise<boolean> — 커서로 키가 `keyPrefix` 자신이거나 `keyPrefix + "/"` 로 시작하는 엔트리 전부 삭제(같은 접두어를 가진 다른 형제 경로 오삭제 방지). 하나라도 지웠으면 `true`, 없으면 `false`. 디렉터리 트리 통째 삭제에.
70
- - listChildren(prefix): Promise<{ name: string; isDirectory: boolean }[]> — `prefix` 직계 자식만 집계. 키에서 prefix 제거 후 첫 세그먼트를 이름으로 삼고, 하위 세그먼트가 더 있거나 엔트리 `kind === "dir"` 면 디렉터리로 판정. 디렉터리 목록 표시용(재귀 아님).
71
- - 반환 항목 name: string — 직계 자식 이름(첫 경로 세그먼트).
72
- - 반환 항목 isDirectory: boolean — 디렉터리 여부.
73
- - ensureDir(fullKeyBuilder, dirPath): Promise<void> — `dirPath` 상의 각 중간 디렉터리를 부모부터 누적 경로마다 없으면 생성. `dirPath === "/"` 면 루트 1건만 생성. 단일 `withStore("readwrite")` 트랜잭션으로 처리(원자적). 파일 쓰기 전 상위 디렉터리 보장에.
74
- - fullKeyBuilder: (path: string) => string — 누적 경로(예: `/a`, `/a/b`)를 실제 저장 key 로 변환하는 콜백.
75
- - dirPath: string — 보장할 디렉터리 경로(`/` 구분). 빈 세그먼트는 무시.
57
+ - `new IndexedDbVirtualFs(db: IndexedDbStore, storeName: string, keyField: string)` — 백엔드 store·스토어 이름·키 필드명으로 생성.
58
+ - `db: IndexedDbStore` — 백엔드 저장소.
59
+ - `storeName: string` — 사용할 오브젝트 스토어 이름.
60
+ - `keyField: string` — 레코드에서 경로 키를 담는 속성명(스토어 keyPath 와 일치해야 함).
61
+ - `VirtualFsEntry` — 저장 엔트리 타입.
62
+ - `kind: "file" | "dir"` — 엔트리 종류. `"file"` = 파일, `"dir"` = 디렉터리. 자식 나열·디렉터리 판정에 사용.
63
+ - `dataBase64?: string` — 파일 내용 base64. 디렉터리거나 빈 파일이면 생략(undefined).
64
+ - `getEntry(fullKey): Promise<VirtualFsEntry | undefined>` — 전체 경로 키로 단건 조회. 미존재 시 `undefined`.
65
+ - `putEntry(fullKey, kind, dataBase64?): Promise<void>` — 엔트리 저장. `keyField` 에 `fullKey`, 그리고 `kind`/`dataBase64` 를 함께 기록.
66
+ - `fullKey: string` — 저장할 전체 경로 키.
67
+ - `kind: "file" | "dir"` — 저장할 엔트리 종류.
68
+ - `dataBase64?: string` — 파일 데이터(base64). 디렉터리면 생략.
69
+ - `deleteByPrefix(keyPrefix): Promise<boolean>` — 커서로 키가 `keyPrefix` 자신이거나 `keyPrefix + "/"` 로 시작하는 엔트리를 전부 삭제(같은 접두어를 가진 다른 형제 경로 오삭제 방지). 하나라도 지웠으면 `true`, 없으면 `false`. 디렉터리 트리 통째 삭제에.
70
+ - `listChildren(prefix): Promise<{ name: string; isDirectory: boolean }[]>` — `prefix` 직계 자식만 집계. 키에서 prefix 제거 후 첫 세그먼트를 이름으로 삼고, 하위 세그먼트가 더 있거나 엔트리 `kind === "dir"` 면 디렉터리로 판정. 디렉터리 목록 표시용(재귀 아님).
71
+ - 반환 항목 `name: string` — 직계 자식 이름(첫 경로 세그먼트).
72
+ - 반환 항목 `isDirectory: boolean` — 디렉터리 여부.
73
+ - `ensureDir(fullKeyBuilder, dirPath): Promise<void>` — `dirPath` 상의 각 중간 디렉터리를 부모부터 누적 경로마다 없으면 생성. `dirPath === "/"` 면 루트 1건만 생성. 단일 `withStore("readwrite")` 트랜잭션으로 처리(원자적). 파일 쓰기 전 상위 디렉터리 보장에.
74
+ - `fullKeyBuilder: (path: string) => string` — 누적 경로(예: `/a`, `/a/b`)를 실제 저장 key 로 변환하는 콜백.
75
+ - `dirPath: string` — 보장할 디렉터리 경로(`/` 구분). 빈 세그먼트는 무시.
76
76
 
77
77
  주의:
78
78
 
79
- - 모든 범위 조회는 `IDBKeyRange.bound(prefix, prefix + "￿")` 기반 — 호출측이 fullKey 규칙을 일관되게 유지해 prefix 가 정확한 경로 경계를 갖게 해야 함.
80
- - `listChildren` 은 직계만 반환(재귀 아님). 트리 전체 순회는 세그먼트별 반복 호출 필요.
79
+ - 모든 범위 조회는 `IDBKeyRange.bound(prefix, prefix + "￿")` 기반 — 호출측이 fullKey 규칙을 일관되게 유지해 prefix 가 정확한 경로 경계를 갖게 해야 한다.
80
+ - `listChildren` 은 직계만 반환(재귀 아님). 트리 전체 순회는 세그먼트별 반복 호출이 필요하다.
@@ -1,130 +1,137 @@
1
1
  # @simplysm/core-common
2
2
 
3
- 브라우저·Node 공용 유틸리티 패키지. 날짜/시간 값 타입, 에러 클래스, 배열/객체 조작, 직렬화, 비동기 큐, 로거, 환경변수 접근을 제공. 워크스페이스 거의 모든 패키지의 기반.
4
-
5
- > 부수효과 주의: 이 패키지를 import 하면 `Array.prototype`·`Set.prototype`·`Map.prototype` 에 확장 메서드가 설치됨(전역 prototype 변경). 확장 메서드는 `array.toMap(...)` 처럼 메서드로 직접 호출.
3
+ 브라우저·Node 공용 기반 유틸. 날짜/시간 값 타입, 에러 클래스, 배열/Set/Map 확장, 객체 조작, 직렬화(json/xml/bytes/transfer), 비동기 큐·이벤트·대기, 로거·환경변수·원시타입 매핑을 제공.
6
4
 
7
5
  ## 사용 트리거 인덱스
8
6
 
9
- - **에러 클래스** — `throw` 때, 에러 원인 체인을 만들 때, catch 에서 분기할 때. SdError/ArgumentError/NotImplementedError/TimeoutError. 자세히: [errors.md](./errors.md)
10
- - **날짜/시간 값 타입**날짜·시간을 불변 값으로 다루거나 파싱·포맷·산술할 때. DateTime/DateOnly/Time + `dt` 포맷 네임스페이스. 자세히: [datetime.md](./datetime.md)
11
- - **배열 확장 메서드** 배열을 그룹화·정렬·중복제거·Map변환·트리화·diff/merge 때. `Array.prototype` 확장. 자세히: [array-ext.md](./array-ext.md)
12
- - **객체 유틸 (`obj` 네임스페이스)** — 깊은 복사/비교/병합, 체인 경로 접근, key 변환을 할 때. 자세히: [obj.md](./obj.md)
13
- - **직렬화/Worker 전송** — 커스텀 타입(DateTime·Uuid·Map·Set·Error 등) 포함 데이터를 JSON·XML·바이트·Worker 메시지로 주고받을 때. `json`/`xml`/`bytes`/`transfer` 네임스페이스. 자세히: [serialization.md](./serialization.md)
14
- - **비동기 큐·이벤트·대기**디바운스/직렬 실행, 타입 안전 이벤트, 조건 대기, 자동 만료 Map 이 필요할 때. DebounceQueue/SerialQueue/EventEmitter/wait/LazyGcMap. 자세히: [async-runtime.md](./async-runtime.md)
15
- - **로거**모듈 어디서든 로그를 찍을 (아래 인라인).
16
- - **환경변수**환경변수를 읽고/쓰고/boolean 파싱할 (아래 인라인).
17
- - **문자열/숫자/경로 유틸** 한국어 조사, 케이스 변환, 숫자 파싱·포맷, POSIX 경로 조작 (아래 인라인).
18
- - **UUID·ZIP·템플릿 태그·Set/Map 확장·공용 타입** 단발성 유틸 (아래 인라인).
19
-
20
- ## 로거
21
-
22
- `createLogger(tag)` consola 기반 lazy logger 인스턴스 생성. 모듈 레벨에서 호출해도 안전(첫 메서드 접근 시점까지 `consola.withTag` 생성을 지연하므로 이후 `setupConsola()` 의 level/reporters 변경이 반영됨).
23
-
24
- - tag: string — 로그 prefix 로 표시되는 태그. 형식은 `<도메인>:<역할>` 또는 단일 토큰 권장. 메시지 본문에 `[패키지명]` 수동 prefix 를 넣지 말 것 — 그 역할은 tag 가 담당.
7
+ - **에러 클래스** — 원인 체인을 묶은 에러를 throw 하거나 catch 에서 메시지를 추출할 때. 자세히: [errors.md](./errors.md)
8
+ - **날짜/시간 값 타입 (DateTime·DateOnly·Time·Uuid)** — 불변 날짜/시간/식별자 값을 만들고 파싱·산술·포맷할 때. 자세히: [value-types.md](./value-types.md)
9
+ - **배열/Set/Map 확장**`arr.single/groupBy/toMap/distinct/orderBy/diffs/toTree` 프로토타입 확장 메서드와 `Set.adds/toggle`, `Map.getOrCreate/update` 쓸 때. 자세히: [collection-ext.md](./collection-ext.md)
10
+ - **객체 조작 (obj 네임스페이스)** — `obj.clone/equal/merge/merge3/pick/omit/getChainValue/keys/entries` 깊은 복사·비교·병합·체인 접근이 필요할 때. 자세히: [obj.md](./obj.md)
11
+ - **직렬화 (json·xml·bytes·transfer)** — 커스텀 타입(DateTime/Uuid/Map/Error 등) 포함 객체를 JSON/XML 문자열, hex/base64, Worker 전송 형태로 변환할 때. 자세히: [serialization.md](./serialization.md)
12
+ - **비동기 런타임 (큐·이벤트·대기·LazyGcMap)** `DebounceQueue`/`SerialQueue` 호출 흐름을 제어하거나, `EventEmitter` 로 타입 안전 이벤트를 다루거나, `wait.until/time`, 자동 만료 Map 이 필요할 때. 자세히: [async-runtime.md](./async-runtime.md)
13
+ - **str (문자열 유틸)** 한국어 조사 선택, 전각→반각, 케이스 변환, 빈문자열 판별, 삽입. (아래 인라인)
14
+ - **num (숫자 유틸)** 비숫자 제거 정수/실수 파싱, 0/null 판별, 천단위 포맷. (아래 인라인)
15
+ - **path (POSIX 경로 유틸)** 브라우저용 join/basename/extname. (아래 인라인)
16
+ - **dt (날짜 포맷 헬퍼)**`DateTime`/`DateOnly`/`Time` `toFormatString` 내부적으로 쓰는 C# 스타일 포맷 문자열 변환. (아래 인라인)
17
+ - **primitive (원시타입 추론)** — 런타임 값에서 `PrimitiveTypeStr` 추론. (아래 인라인)
18
+ - **template-strings (코드 하이라이팅 태그)** — `js/ts/html/tsql/mysql/pgsql` 템플릿 태그로 들여쓰기 정규화. (아래 인라인)
19
+ - **ZipArchive** — ZIP 바이트를 읽고 파일 단위로 추출·추가·재압축할 때. (아래 인라인)
20
+ - **env / createLogger** 환경변수 읽기·쓰기, 모듈 레벨 안전 lazy 로거 생성. (아래 인라인)
21
+ - **공용 타입 (common.types)** — `PrimitiveType`/`PrimitiveTypeStr`/`PrimitiveTypeMap`/`Bytes`/`DeepPartial`/`Type`. (아래 인라인)
22
+
23
+ ## str (문자열 유틸)
24
+
25
+ `import { str } from "@simplysm/core-common"` 네임스페이스.
26
+
27
+ - `getKoreanSuffix(text: string, type: "을"|"은"|"이"|"와"|"랑"|"로"|"라"): string` — 마지막 글자 받침 유무로 한국어 조사를 선택. type 은 조사 쌍 식별자: `"을"`=을/를, `"은"`=은/는, `"이"`=이/가, `"와"`=과/와, `"랑"`=이랑/랑, `"로"`=으로/로(받침이 ㄹ이면 "로"), `"라"`=이라/라. 한글이 아니거나 빈 문자열이면 받침 없는 형태 반환. 동적 메시지 조립 시 사용.
28
+ - `replaceFullWidth(str: string): string` — 전각 영문·숫자·공백·괄호를 반각으로 치환. 외부 입력(엑셀·스캐너) 정규화 시.
29
+ - `toPascalCase(str: string): string` — `-`/`_`/`.` 구분자 뒤 글자와 첫 글자를 대문자화. 식별자 변환 시.
30
+ - `toCamelCase(str: string): string` — 구분자 뒤 글자는 대문자화하되 첫 글자는 소문자화.
31
+ - `toKebabCase(str: string): string` — 대문자 앞에 `-` 삽입 후 소문자화. 연속 대문자도 글자별 분리(`XMLParser`→`x-m-l-parser`), 기존 `-`/`_` 구분자는 유지.
32
+ - `toSnakeCase(str: string): string` — `toKebabCase` 와 동일 규칙이되 구분자가 `_`.
33
+ - `isNullOrEmpty(str: string | undefined): str is "" | undefined` — null/undefined/빈문자열이면 true 인 타입 가드. else 분기에서 비어있지 않은 string 으로 좁혀짐.
34
+ - `insert(str: string, index: number, insertString: string): string` — index 위치에 문자열 삽입한 새 문자열.
25
35
 
26
36
  ```ts
27
- import { createLogger } from "@simplysm/core-common";
28
- const logger = createLogger("capacitor:auto-update");
29
- logger.info("최신 버전 확인 중");
30
- logger.error("checkPermissions 실패", err);
37
+ import { str } from "@simplysm/core-common";
38
+ const label = "사과" + str.getKoreanSuffix("사과", "을") + " 담았습니다."; // "사과를 담았습니다."
31
39
  ```
32
40
 
33
- `console.*` 직접 호출·`consola.withTag()` 직접 호출 금지 — 항상 `createLogger` 사용.
41
+ ## num (숫자 유틸)
34
42
 
35
- ## 환경변수
43
+ `import { num } from "@simplysm/core-common"` 네임스페이스. 파싱 계열은 비숫자 문자(0-9·`-`·`.` 외)를 먼저 제거하므로 `"010-1234"` 같은 입력도 받음(선행 `-`만 부호로 유지).
36
44
 
37
- - `env(key)`: `string | undefined` — 환경변수 읽기. `process.env[key]` 우선, 없으면 `import.meta.env[key]` fallback. Node·브라우저(Vite) 양쪽에서 동작.
38
- - `env(key, value)`: `void``process.env[key]` 쓰기 (Node 환경에서만 적용, 브라우저에선 무동작).
39
- - `parseBoolEnv(value)`: `boolean`환경변수 문자열을 boolean 으로 해석. `"true"|"1"|"yes"|"on"` (대소문자 무시) 이면 true, 그 외(빈 값·undefined 포함) false. 플래그성 env 판정에 사용.
45
+ - `parseInt(text: unknown): number | undefined` — 정수 파싱. number 면 `Math.trunc`, 소수 문자열이면 정수부만, 파싱 불가면 undefined.
46
+ - `parseFloat(text: unknown): number | undefined` — 실수 파싱. number 그대로, 파싱 불가면 undefined.
47
+ - `parseRoundedInt(text: unknown): number | undefined` — `parseFloat` `Math.round`. 반올림 정수가 필요할 때.
48
+ - `isNullOrEmpty(val: number | undefined): val is 0 | undefined` — null/undefined/0 이면 true 인 타입 가드. else 분기에서 0 아닌 number 로 좁혀짐.
49
+ - `format(val, digit?: { max?: number; min?: number }): string | undefined` — 천단위 구분자 포맷. `max`=최대 소수 자릿수, `min`=최소 소수 자릿수(부족분 0 채움). val 이 undefined 면 undefined 반환.
40
50
 
41
51
  ```ts
42
- import { env, parseBoolEnv } from "@simplysm/core-common";
43
- if (parseBoolEnv(env("DEV"))) { /* 개발 모드 분기 */ }
52
+ import { num } from "@simplysm/core-common";
53
+ num.format(1234.567, { max: 2 }); // "1,234.57"
54
+ num.parseInt("010-1234-5678"); // 1012345678
44
55
  ```
45
56
 
46
- ## 문자열 유틸 (`str` 네임스페이스)
57
+ ## path (POSIX 경로 유틸)
47
58
 
48
- - `str.getKoreanSuffix(text, type)`: string 받침 유무로 한국어 조사 선택. type: `"을"`(을/를)·`"은"`(은/는)·`"이"`(이/가)·`"와"`(과/와)·`"랑"`(이랑/랑)·`"로"`(으로/로, 받침 ㄹ 은 "로")·`"라"`(이라/라). 문자열·한글 아님이면 받침 없음 조사 반환. 동적 메시지 조립에 사용.
49
- - `str.replaceFullWidth(s)`: → string — 전각 영문/숫자/공백/괄호를 반각으로 변환. OCR·외부 입력 정규화에 사용.
50
- - `str.toPascalCase(s)` / `toCamelCase(s)` / `toKebabCase(s)` / `toSnakeCase(s)`: → string — 케이스 변환. `-`·`_`·`.` 구분자와 대문자 경계를 인식. 코드 생성·식별자 정규화에 사용.
51
- - `str.isNullOrEmpty(s)`: → `s is "" | undefined` — null/undefined/빈 문자열 판정(타입 가드). false 분기에서 non-empty string 으로 좁혀짐.
52
- - `str.insert(s, index, insertString)`: → string — index 위치에 문자열 삽입한 새 문자열.
53
-
54
- ```ts
55
- import { str } from "@simplysm/core-common";
56
- `${name}${str.getKoreanSuffix(name, "을")} 저장했습니다`;
57
- ```
59
+ `import { path } from "@simplysm/core-common"` 네임스페이스. POSIX 슬래시 경로만 지원(Windows 백슬래시 미지원). 브라우저·Capacitor 환경용 Node `path` 대체.
58
60
 
59
- ## 숫자 유틸 (`num` 네임스페이스)
61
+ - `join(...segments: string[]): string` — 슬래시로 결합하며 중간 세그먼트의 앞뒤 슬래시·빈 세그먼트 제거.
62
+ - `basename(filePath: string, ext?: string): string` — 마지막 세그먼트 추출. `ext` 가 끝과 일치하면 제거.
63
+ - `extname(filePath: string): string` — 마지막 `.` 이후 확장자(`.` 포함). 숨김파일(`.gitignore`)은 빈 문자열.
60
64
 
61
- - `num.parseInt(text)`: → `number | undefined` — 비숫자 문자 제거 후 정수 파싱. 소수점은 버림(`"12.34"`→12). 선행 `-` 만 음수 부호로 유지, 중간 `-` 제거(`"010-1234"`→101234). 파싱 불가면 undefined.
62
- - `num.parseFloat(text)`: → `number | undefined` — 위와 동일 규칙으로 실수 파싱.
63
- - `num.parseRoundedInt(text)`: → `number | undefined` — float 파싱 후 반올림한 정수. 소수 입력을 반올림 정수로 받을 때.
64
- - `num.isNullOrEmpty(val)`: → `val is 0 | undefined` — null/undefined/0 판정(타입 가드). false 분기에서 0 아닌 숫자로 좁혀짐.
65
- - `num.format(val, digit?)`: → string(또는 입력이 undefined 면 undefined) — 천 단위 구분자 포함 포맷. `digit.max`=최대 소수 자릿수, `digit.min`=최소 소수 자릿수(부족분 0 채움). `format(1234.567, { max: 2 })`→`"1,234.57"`.
65
+ ## dt (날짜 포맷 헬퍼)
66
66
 
67
- ## 경로 유틸 (`path` 네임스페이스)
67
+ `import { dt } from "@simplysm/core-common"` 네임스페이스. 보통 `DateTime`/`DateOnly`/`Time` 의 `toFormatString` 을 통해 간접 사용.
68
68
 
69
- POSIX 스타일(슬래시 `/`) 지원. 브라우저·Capacitor 환경용. Windows 백슬래시 경로 미지원.
69
+ - `format(formatString: string, args: { year?, month?, day?, hour?, minute?, second?, millisecond?, timezoneOffsetMinutes? }): string` — C# 스타일 포맷 토큰 치환. 토큰: `yyyy/yy`(연), `MM/M`(월), `ddd`(요일 한글)/`dd/d`(일), `tt`(AM/PM), `hh/h`(12시간)/`HH/H`(24시간), `mm/m`(분), `ss/s`(초), `fff/ff/f`(밀리초), `zzz/zz/z`(타임존 오프셋). 전달되지 않은 구성요소의 토큰은 치환되지 않음.
70
+ - `normalizeMonth(year, month, day): { year; month; day }` — 1-12 범위 밖 월을 연도로 이월하고, 대상 월 일수를 넘는 일은 말일로 보정.
71
+ - `convert12To24(rawHour: number, isPM: boolean): number` — 12시간제(1-12)+오전/오후를 24시간제(0-23)로 변환.
70
72
 
71
- - `path.join(...segments)`: → string — 세그먼트를 `/` 로 결합. 중간 중복 슬래시·빈 세그먼트 정리.
72
- - `path.basename(filePath, ext?)`: → string — 파일명 추출. ext 가 주어지고 끝나면 그 확장자 제거.
73
- - `path.extname(filePath)`: → string — 확장자 추출(`.` 포함). 숨김 파일(`.gitignore`)은 빈 문자열(Node 와 동일).
73
+ ## primitive (원시타입 추론)
74
74
 
75
- ## UUID (`Uuid` 클래스)
75
+ `import { primitive } from "@simplysm/core-common"` 네임스페이스.
76
76
 
77
- - `Uuid.generate()`: Uuid `crypto.getRandomValues` 기반 암호학적 안전 UUID v4 생성.
78
- - `new Uuid(uuidStr)` — 문자열로 생성. 형식 불일치면 ArgumentError throw.
79
- - `Uuid.fromBytes(bytes)`: → Uuid — 16바이트 Uint8Array 로 생성. 길이≠16 이면 ArgumentError.
80
- - 인스턴스: `toString()` → 문자열, `toBytes()` → 16바이트 Uint8Array.
77
+ - `typeStr(value): PrimitiveTypeStr` — 런타임 값에서 `"string"|"number"|"boolean"|"DateTime"|"DateOnly"|"Time"|"Uuid"|"Bytes"` 해당 문자열 반환. 지원하지 않는 타입이면 `ArgumentError` throw. ORM/직렬화에서 타입을 문자열 키로 다룰 때.
81
78
 
82
- ## ZIP (`ZipArchive` 클래스)
79
+ ## template-strings (코드 하이라이팅 태그)
83
80
 
84
- ZIP 읽기/쓰기/압축/해제. 동일 파일 중복 해제 방지용 내부 캐시 사용. 사용 `close()` 필수.
85
-
86
- - `new ZipArchive(data?)` — data(`Blob | Uint8Array`) 주면 읽기용, 생략하면 새 아카이브.
87
- - `extractAll(progressCallback?)`: → `Promise<Map<string, Bytes | undefined>>` — 모든 파일 추출. 콜백은 `{ fileName, totalSize, extractedSize }` 진행률 수신.
88
- - `get(fileName)`: → `Promise<Bytes | undefined>` — 단일 파일 추출(없으면 undefined).
89
- - `exists(fileName)`: → `Promise<boolean>` — 파일 존재 여부.
90
- - `write(fileName, bytes)`: → void — 파일을 캐시에 등록(아직 압축 전).
91
- - `compress()`: → `Promise<Bytes>` — 캐시된 전체 파일을 ZIP 바이트로 압축. 내부적으로 `extractAll()` 호출(대용량 시 메모리 주의).
92
- - `close()`: → `Promise<void>` — 리더 닫고 캐시 비움.
93
-
94
- ## 템플릿 태그 (코드 하이라이팅용)
95
-
96
- `js` / `ts` / `html` / `tsql` / `mysql` / `pgsql` — 모두 동일 동작: 템플릿 리터럴을 문자열로 결합하고 공통 들여쓰기를 제거(앞뒤 빈 줄 제거). IDE 하이라이팅·가독성 목적이며 SQL 이스케이프 등 기능 차이는 없음.
81
+ `import { js, ts, html, tsql, mysql, pgsql } from "@simplysm/core-common"`. 모두 동일 동작 보간값을 문자열로 합친 공통 최소 들여쓰기를 제거하고 앞뒤 빈 줄을 잘라냄. 함수별 차이는 IDE 하이라이팅 언어 힌트뿐(js/ts/html/T-SQL/MySQL/PostgreSQL). 런타임 검증·이스케이프는 하지 않음.
97
82
 
98
83
  ```ts
99
84
  import { ts } from "@simplysm/core-common";
100
85
  const code = ts`
101
- interface User { name: string; }
102
- `; // 들여쓰기 정규화된 문자열
86
+ interface User {
87
+ name: string;
88
+ }
89
+ `; // 앞 공통 들여쓰기 제거된 문자열
103
90
  ```
104
91
 
105
- ## Set 확장 메서드 (`Set.prototype`)
92
+ ## ZipArchive
93
+
94
+ `import { ZipArchive } from "@simplysm/core-common"`. `@zip.js/zip.js` 래퍼. 읽기/쓰기를 한 인스턴스로 다루며 추출 결과를 내부 캐시에 보관. 사용 후 `close()` 필수.
106
95
 
107
- - `set.adds(...values)`: this여러 값을 번에 add. 체이닝 가능.
108
- - `set.toggle(value, addOrDel?)`: this 토글. `addOrDel` 생략 있으면 제거/없으면 추가, `"add"`=강제 추가, `"del"`=강제 제거. 조건부 추가/제거를 줄로.
96
+ - `new ZipArchive(data?: Blob | Bytes)` `data` 생략 아카이브, 주면 읽기용 리더 구성(`Uint8Array`→`Uint8ArrayReader`, `Blob`→`BlobReader`).
97
+ - `extractAll(progressCallback?: (p: ZipArchiveProgress) => void): Promise<Map<string, Bytes | undefined>>` 전체 파일 추출. 콜백 인자 `ZipArchiveProgress` 필드: `fileName`=현재 파일명, `totalSize`=전체 비압축 크기, `extractedSize`=누적 추출 크기.
98
+ - `get(fileName: string): Promise<Bytes | undefined>` — 단일 파일 추출(없으면 undefined). 캐시 우선.
99
+ - `exists(fileName: string): Promise<boolean>` — 파일 존재 여부.
100
+ - `write(fileName: string, bytes: Bytes): void` — 캐시에 파일 등록(아직 압축 안 함).
101
+ - `compress(): Promise<Bytes>` — 캐시(및 원본 추출분)를 ZIP 바이트로 압축. 내부적으로 `extractAll()` 호출하므로 전체가 메모리에 로드됨.
102
+ - `close(): Promise<void>` — 리더 닫고 캐시 비움.
109
103
 
110
- ## Map 확장 메서드 (`Map.prototype`)
104
+ ```ts
105
+ const zip = new ZipArchive(zipBytes);
106
+ try {
107
+ const content = await zip.get("file.txt");
108
+ } finally {
109
+ await zip.close();
110
+ }
111
+ ```
111
112
 
112
- - `map.getOrCreate(key, newValue)` / `getOrCreate(key, newValueFn)`: → V — key 없으면 값(또는 팩토리 호출 결과) 설정 후 반환, 있으면 기존 값. 주의: V 가 함수 타입이면 두 번째 인자 함수가 팩토리로 인식되어 호출됨 — 함수를 값으로 저장하려면 `() => myFn` 으로 감쌀 것.
113
- - `map.update(key, updateFn)`: → void — `updateFn(현재값 | undefined)` 결과로 값 설정. key 가 없어도 호출됨. 카운터 증가·배열 누적에 사용. 예: `m.update(k, v => (v ?? 0) + 1)`.
113
+ ## env / createLogger
114
114
 
115
- ## 공용 타입 (`common.types`)
115
+ `import { env, parseBoolEnv, createLogger } from "@simplysm/core-common"`.
116
116
 
117
- - `Bytes` = `Uint8Array` 바이너리 데이터 별칭. Buffer 대신 사용.
118
- - `PrimitiveTypeMap` 원시 타입 문자열 실제 타입 매핑(`string`/`number`/`boolean`/`DateTime`/`DateOnly`/`Time`/`Uuid`/`Bytes`). orm-common 공유.
119
- - `PrimitiveTypeStr` = `keyof PrimitiveTypeMap` 원시 타입 문자열 key union.
120
- - `PrimitiveType` — 모든 원시 타입 값의 union(+ undefined).
121
- - `DeepPartial<T>` — 모든 속성을 재귀적으로 optional 화. 원시/날짜 타입은 그대로 두고 object/array 만 재귀.
122
- - `Type<TInstance>` — 클래스 생성자 타입(`new (...args) => TInstance`). DI·팩토리·instanceof 체크에 사용.
117
+ - `env(key: string): string | undefined` 환경변수 읽기. `process.env` 우선, 없으면 `import.meta.env` fallback(Node 에선 보통 undefined).
118
+ - `env(key: string, value: string): void` `process.env[key]` 쓰기(process 없는 런타임이면 무시).
119
+ - `parseBoolEnv(value: unknown): boolean` `"true"/"1"/"yes"/"on"`(대소문자 무시)이면 true, false. 환경변수 boolean 해석 시.
120
+ - `createLogger(tag: string): ConsolaInstance` — consola 태그 로거를 메서드 접근 시점까지 지연 생성하는 Proxy. 모듈 레벨에서 선언해도 이후 `setupConsola` 의 level/reporters 변경이 반영됨. 모듈 최상단에 로거를 두고 싶을 때 `consola.withTag()` 직접 호출 대신 사용.
123
121
 
124
- ## 원시 타입 추론 (`primitive` 네임스페이스)
122
+ ```ts
123
+ import { createLogger, env, parseBoolEnv } from "@simplysm/core-common";
124
+ const logger = createLogger("MyModule");
125
+ if (parseBoolEnv(env("DEV"))) logger.debug("dev mode");
126
+ ```
125
127
 
126
- - `primitive.typeStr(value)`: → PrimitiveTypeStr — 런타임 값에서 원시 타입 문자열 추론(`"hello"`→`"string"`, `new DateTime()`→`"DateTime"`, `Uint8Array`→`"Bytes"`). 지원 안 되는 타입이면 ArgumentError. ORM 컬럼 타입 판정 등에 사용.
128
+ ## 공용 타입 (common.types)
127
129
 
128
- ## 에러 메시지 추출 (`err` 네임스페이스)
130
+ `import type { ... } from "@simplysm/core-common"`. orm-common 등과 공유되는 원시타입 매핑·유틸 타입.
129
131
 
130
- - `err.message(e)`: string`unknown` 에러에서 메시지 추출. Error 면 `.message`, 아니면 `String(e)`. catch 블록의 `unknown` 안전하게 문자열화할 때.
132
+ - `Bytes = Uint8Array`바이너리 타입(Node `Buffer` 대신 사용).
133
+ - `PrimitiveTypeMap` — 원시타입 문자열 key → 실제 타입 매핑(`string/number/boolean/DateTime/DateOnly/Time/Uuid/Bytes`).
134
+ - `PrimitiveTypeStr = keyof PrimitiveTypeMap` — 위 매핑의 key 유니온.
135
+ - `PrimitiveType = PrimitiveTypeMap[PrimitiveTypeStr] | undefined` — 모든 원시 값 유니온(undefined 포함).
136
+ - `DeepPartial<TObject>` — 원시타입은 그대로 두고 객체/배열만 재귀적으로 optional 화. 부분 패치 입력 타입에.
137
+ - `Type<TInstance>` — `new (...args) => TInstance` 생성자 타입. 팩토리·DI·`instanceof` 분기에서 클래스를 값으로 받을 때.
@@ -1,86 +1,110 @@
1
- # @simplysm/core-common — 비동기 런타임
1
+ # @simplysm/core-common — 비동기 런타임 (큐·이벤트·대기·LazyGcMap)
2
2
 
3
- 디바운스/직렬 실행, 타입 안전 이벤트, 조건 대기, 자동 만료 Map 필요할 때 함께 읽히는 묶음. 클래스들은 `EventEmitter` 상속해 `"error"` 이벤트를 발행함.
3
+ 호출 흐름 제어(디바운스/직렬 큐), 타입 안전 이벤트(EventEmitter), 대기 헬퍼(wait), 자동 만료 Map(LazyGcMap). 비동기 작업 조율·이벤트 배선·타이머 자원 정리가 필요할 때 함께 참조. 큐와 EventEmitter `dispose()` 자원을 정리해야 함.
4
4
 
5
- ## EventEmitter<TEvents>
5
+ ## EventEmitter
6
6
 
7
- `EventTarget` 기반 타입 안전 이벤트 이미터(브라우저·Node 공용). 보통 상속해서 사용. `TEvents` 는 `{ 이벤트명: 데이터타입 }` 맵.
7
+ ```ts
8
+ class EventEmitter<TEvents extends { [K in keyof TEvents]: unknown } = Record<string, unknown>> {}
9
+ ```
10
+
11
+ `EventTarget` 기반 타입 안전 이벤트 이미터(브라우저·Node 공용). `TEvents` 는 이벤트명→데이터 타입 맵.
8
12
 
9
- - `on(type, listener)`: void — 리스너 등록. 같은 (type, listener) 중복 등록은 무시.
10
- - `off(type, listener)`: void — 리스너 제거.
11
- - `emit(type, data)`: void — 발행. 데이터 타입이 `void` 이벤트는 인자 없이 `emit(type)`.
12
- - `listenerCount(type)`: number — 해당 이벤트 리스너 수.
13
- - `dispose()`: void — 모든 리스너 제거.
13
+ - `on(type, listener: (data: TEvents[type]) => void): void` — 리스너 등록(같은 리스너 중복 등록은 무시).
14
+ - `off(type, listener): void` — 리스너 제거.
15
+ - `emit(type, ...args): void` — 발행. 데이터 타입이 `void` 인자 없이 호출.
16
+ - `listenerCount(type): number` — 해당 이벤트의 리스너 수.
17
+ - `dispose(): void` — 모든 리스너 제거.
14
18
 
15
19
  ```ts
16
- import { EventEmitter } from "@simplysm/core-common";
17
20
  interface MyEvents { data: string; done: void; }
18
21
  class MyEmitter extends EventEmitter<MyEvents> {}
19
- const e = new MyEmitter();
20
- e.on("data", (d) => console.log(d)); // d: string
21
- e.emit("data", "hello");
22
- e.emit("done");
22
+ const em = new MyEmitter();
23
+ em.on("data", (d) => console.log(d)); // d: string
24
+ em.emit("data", "hello");
25
+ em.emit("done"); // void 는 인자 없이
23
26
  ```
24
27
 
25
28
  ## DebounceQueue
26
29
 
27
- 짧은 시간 내 여러 호출 중 **마지막 요청만** 실행. 입력 자동완성·연속 상태 변경 일괄 처리에. `EventEmitter<{ error: SdError }>` 상속.
30
+ ```ts
31
+ class DebounceQueue extends EventEmitter<{ error: SdError }> {
32
+ constructor(delay?: number);
33
+ }
34
+ ```
35
+
36
+ 연속 호출 시 **마지막 요청만** 실행하는 디바운스 큐. 입력 자동완성·연속 상태 변경 일괄 처리에.
28
37
 
29
- - `new DebounceQueue(delay?)` — delay: number(ms). 생략 시 즉시(다음 이벤트 루프).
30
- - `run(fn)`: → void — `fn: () => void | Promise<void>` 등록. 이전 대기 fn 교체됨. delay 실행. 실행 들어온 요청은 delay 없이 현재 실행 직후 즉시 처리(요청 누락 방지).
31
- - `dispose()`: void — 대기 작업·타이머 정리(+ 리스너 제거).
32
- - fn 에서 throw 시 `"error"` 리스너가 있으면 SdError 로 emit, 없으면 내부 logger.error.
38
+ - `constructor(delay?)` — `delay`(ms) 생략 시 다음 이벤트 루프에 즉시 실행.
39
+ - `run(fn: () => void | Promise<void>): void` 큐에 등록. 대기 함수가 있으면 교체. 실행 중에 도착한 요청은 디바운스 없이 현재 실행 직후 즉시 처리(누락 방지 의도).
40
+ - `dispose(): void` — 대기 함수·타이머 정리(EventEmitter dispose 포함).
41
+ - 작업 throw 시 `"error"` 리스너가 있으면 `SdError` 로 emit, 없으면 내부 로거로 출력.
42
+
43
+ ## SerialQueue
33
44
 
34
45
  ```ts
35
- import { DebounceQueue } from "@simplysm/core-common";
36
- const q = new DebounceQueue(300);
37
- q.on("error", (err) => console.error(err));
38
- q.run(() => save(value)); // 300ms 안에 다시 run 하면 이전 건 취소
46
+ class SerialQueue extends EventEmitter<{ error: SdError }> {
47
+ constructor(gap?: number);
48
+ }
39
49
  ```
40
50
 
41
- ## SerialQueue
51
+ 큐에 등록된 함수를 **순차 실행**(이전 완료 후 다음). 에러가 나도 후속 작업은 계속.
42
52
 
43
- 큐에 넣은 작업을 **순차** 실행(앞 작업 완료 후 다음). 에러가 나도 후속 작업은 계속. `EventEmitter<{ error: SdError }>` 상속.
53
+ - `constructor(gap = 0)` 작업 사이 간격(ms).
54
+ - `run(fn: () => void | Promise<void>): void` — 큐에 추가하고 실행 시작.
55
+ - `dispose(): void` — 대기 큐 비움(실행 중 작업은 완료됨, EventEmitter dispose 포함).
56
+ - 작업 throw 시 `"error"` 리스너가 있으면 `SdError` emit, 없으면 내부 로거 출력.
44
57
 
45
- - `new SerialQueue(gap?)` — gap: number(ms, 기본 0). 각 작업 사이 대기 간격.
46
- - `run(fn)`: void — `fn: () => void | Promise<void>` 추가 후 처리 시작.
47
- - `dispose()`: void 대기 큐 비움(실행 중 작업은 완료됨)(+ 리스너 제거).
48
- - fn throw 처리: DebounceQueue 동일(`"error"` emit 또는 logger.error).
58
+ ```ts
59
+ const q = new SerialQueue();
60
+ q.run(async () => { await save(a); });
61
+ q.run(async () => { await save(b); }); // a 완료 후 실행
62
+ ```
49
63
 
50
- ## wait 네임스페이스
64
+ ## wait (`import { wait } from "@simplysm/core-common"`)
51
65
 
52
- - `wait.until(forwarder, milliseconds?, maxCount?)`: → `Promise<void>` — `forwarder()`(boolean | Promise<boolean>) true 될 때까지 대기.호출에서 true 면 즉시 반환. milliseconds=확인 간격(기본 100). maxCount=최대 시도 횟수(미지정 무제한). 초과 시 **TimeoutError throw**.
53
- - `wait.time(millisecond)`: → `Promise<void>` — 지정 시간만큼 sleep.
66
+ - `until(forwarder: () => boolean | Promise<boolean>, milliseconds = 100, maxCount?): Promise<void>` — 조건이 true 가 될 때까지 `milliseconds` 간격으로 폴링. 평가에서 true 면 즉시 반환. `maxCount` 지정 초과하면 `TimeoutError` throw(미지정이면 무제한).
67
+ - `time(millisecond: number): Promise<void>` — 지정 시간만큼 대기(`setTimeout` Promise 화).
54
68
 
55
69
  ```ts
56
70
  import { wait } from "@simplysm/core-common";
57
- await wait.until(() => isReady, 100, 50); // 100ms 간격 최대 50회, 초과 시 TimeoutError
58
- await wait.time(500);
71
+ await wait.until(() => isReady, 100, 50); // 100ms 간격, 50 초과 시 TimeoutError
72
+ await wait.time(300);
73
+ ```
74
+
75
+ ## LazyGcMap
76
+
77
+ ```ts
78
+ class LazyGcMap<TKey, TValue> {
79
+ constructor(options: {
80
+ gcInterval?: number;
81
+ expireTime: number;
82
+ onExpire?: (key: TKey, value: TValue) => void | Promise<void>;
83
+ });
84
+ }
59
85
  ```
60
86
 
61
- ## LazyGcMap<TKey, TValue>
87
+ LRU 접근 시간 기반 자동 만료 Map. 지정 시간 동안 접근 없으면 GC 타이머가 삭제. 캐시·세션 보관에. **사용 후 `dispose()` 필수**(아니면 GC 타이머가 계속 돌아 메모리 누수).
62
88
 
63
- 마지막 접근 이후 일정 시간 지나면 항목을 자동 삭제하는 Map(LRU 접근 시간 갱신). GC 는 항목이 있을 때만 타이머로 동작. 사용 후 **반드시 `dispose()`** — 안 하면 타이머가 남아 메모리 누수.
89
+ 생성자 옵션:
90
+ - `expireTime: number` — 마지막 접근 후 이 ms 가 지나면 만료(필수).
91
+ - `gcInterval?: number` — GC 주기(ms). 생략 시 `expireTime/10`(최소 1000).
92
+ - `onExpire?: (key, value) => void | Promise<void>` — 만료 시 콜백(비동기 가능). 콜백 throw 시 로그 출력 후 계속 진행.
64
93
 
65
- - `new LazyGcMap({ expireTime, gcInterval?, onExpire? })`
66
- - expireTime: number(ms) — 마지막 접근 시간 지나면 만료. (필수)
67
- - gcInterval?: number(ms) — GC 점검 주기. 기본 `max(expireTime/10, 1000)`.
68
- - onExpire?: `(key, value) => void | Promise<void>` 만료 시 콜백(비동기 가능). 콜백 throw logger.error 계속 진행. 만료 항목 정리·리소스 해제에.
69
- - `get(key)`: `V | undefined` — 조회(접근 시간 갱신=만료 연장).
70
- - `has(key)`: boolean 존재 확인(접근 시간 갱신 안 함).
71
- - `set(key, value)`: void 저장(GC 타이머 시작).
72
- - `getOrCreate(key, factory)`: → V — 없으면 factory()로 생성·저장 후 반환(있으면 접근 시간 갱신). dispose 후 호출 시 throw.
73
- - `delete(key)`: → boolean / `clear()`: → void(인스턴스 재사용 가능) / `dispose()`: → void(타이머 중지+정리, 이후 사용 불가).
74
- - `values()` / `keys()` / `entries()`: → Iterator — 순회.
75
- - `size`: → number — 항목 수.
94
+ 메서드:
95
+ - `get(key)`조회 + 접근 시간 갱신(LRU). `has(key)` — 존재 확인(시간 갱신 ).
96
+ - `set(key, value)`저장 + 접근 시간 설정, GC 타이머 시작.
97
+ - `getOrCreate(key, factory)`없으면 `factory()` 생성·저장. dispose호출 throw.
98
+ - `delete(key)` 삭제(비면 GC 중지). `clear()`전체 삭제(인스턴스 재사용 가능). `dispose()` — 정리 후 사용 불가.
99
+ - `size` 항목 수. `values()/keys()/entries()` 이터레이터.
100
+ - GC 는 중복 실행 방지(이전 GC 가 끝나야 다음). 만료 콜백 같은 key 로 `set` 되면 재등록 항목은 삭제하지 않음(항목 참조 동일성으로 판정).
76
101
 
77
102
  ```ts
78
- import { LazyGcMap } from "@simplysm/core-common";
79
- const cache = new LazyGcMap<string, Conn>({ expireTime: 60000, onExpire: (k, c) => c.close() });
103
+ const cache = new LazyGcMap<string, Session>({ expireTime: 60000 });
80
104
  try {
81
- const conn = cache.getOrCreate(key, () => createConn());
105
+ cache.set("u1", session);
106
+ const s = cache.getOrCreate("u2", () => loadSession("u2"));
82
107
  } finally {
83
- // 앱/모듈 종료 시
84
108
  cache.dispose();
85
109
  }
86
110
  ```