@simplysm/sd-claude 14.0.88 → 14.0.89

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 (122) hide show
  1. package/claude/references/sd-simplysm14/README.md +17 -17
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +27 -53
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +37 -105
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +46 -43
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +22 -32
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +40 -55
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +40 -40
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +25 -53
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +70 -82
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +44 -39
  11. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +21 -36
  12. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +52 -65
  13. package/claude/references/sd-simplysm14/apis/angular/sheet.md +65 -70
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +33 -35
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +7 -7
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +29 -29
  17. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +45 -50
  18. package/claude/references/sd-simplysm14/apis/core-browser/README.md +42 -55
  19. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +62 -0
  20. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +13 -12
  21. package/claude/references/sd-simplysm14/apis/core-common/README.md +222 -98
  22. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +102 -53
  23. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +128 -0
  24. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +98 -64
  25. package/claude/references/sd-simplysm14/apis/core-common/errors.md +91 -0
  26. package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +34 -28
  27. package/claude/references/sd-simplysm14/apis/core-common/obj.md +104 -40
  28. package/claude/references/sd-simplysm14/apis/core-node/README.md +11 -8
  29. package/claude/references/sd-simplysm14/apis/core-node/consola.md +23 -31
  30. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +33 -22
  31. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +28 -25
  32. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +39 -53
  33. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +26 -29
  34. package/claude/references/sd-simplysm14/apis/core-node/worker.md +27 -29
  35. package/claude/references/sd-simplysm14/apis/excel/README.md +14 -14
  36. package/claude/references/sd-simplysm14/apis/lint/README.md +27 -21
  37. package/claude/references/sd-simplysm14/apis/lint/rules.md +89 -49
  38. package/claude/references/sd-simplysm14/apis/orm-common/README.md +5 -59
  39. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +98 -67
  40. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +107 -92
  41. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +99 -65
  42. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +83 -98
  43. package/claude/references/sd-simplysm14/apis/orm-common/types.md +62 -52
  44. package/claude/references/sd-simplysm14/apis/orm-node/README.md +62 -25
  45. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +27 -27
  46. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +12 -15
  47. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +92 -45
  48. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +226 -108
  49. package/claude/references/sd-simplysm14/apis/service-client/README.md +84 -86
  50. package/claude/references/sd-simplysm14/apis/service-client/orm.md +14 -11
  51. package/claude/references/sd-simplysm14/apis/service-client/transport.md +33 -10
  52. package/claude/references/sd-simplysm14/apis/service-common/README.md +37 -23
  53. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +9 -9
  54. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +13 -13
  55. package/claude/references/sd-simplysm14/apis/service-server/README.md +81 -65
  56. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +32 -35
  57. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +44 -33
  58. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +34 -45
  59. package/claude/references/sd-simplysm14/apis/storage/README.md +24 -18
  60. package/claude/skills/sd-demo/SKILL.md +6 -0
  61. package/claude/skills/sd-impl/SKILL.md +4 -7
  62. package/claude/skills/sd-spec/SKILL.md +31 -858
  63. package/claude/skills/sd-spec/references/spec-authoring.md +519 -0
  64. package/claude/workflows/sd-docs.js +84 -0
  65. package/package.json +1 -1
  66. package/claude/references/sd-simplysm14/apis/orm-common/query-builder.md +0 -29
  67. package/claude/skills/sd-demo/evals/fixtures/inventory-list/.specs/inventory/spec.md +0 -99
  68. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/package.json +0 -12
  69. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/index.ts +0 -3
  70. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inbound/inbound.list.ts +0 -150
  71. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inventory/inventory-master.list.ts +0 -143
  72. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/outbound/outbound.list.ts +0 -150
  73. package/claude/skills/sd-demo/evals/fixtures/inventory-list/pnpm-workspace.yaml +0 -2
  74. package/claude/skills/sd-demo/evals/fixtures/inventory-list/sd.config.ts +0 -12
  75. package/claude/skills/sd-demo/evals/golden.jsonl +0 -1
  76. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/package.json +0 -8
  77. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/src/.gitkeep +0 -0
  78. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tests/.gitkeep +0 -0
  79. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tsconfig.json +0 -10
  80. package/claude/skills/sd-dev/evals/golden.jsonl +0 -1
  81. package/claude/skills/sd-docs/SKILL.md +0 -46
  82. package/claude/skills/sd-docs/evals/fixtures/new-write/.claude/references/sd-simplysm14/README.md +0 -7
  83. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/package.json +0 -5
  84. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/src/index.ts +0 -3
  85. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/package.json +0 -6
  86. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/src/index.ts +0 -1
  87. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/package.json +0 -5
  88. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/src/index.ts +0 -8
  89. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/README.md +0 -7
  90. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/apis/foo/README.md +0 -3
  91. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/package.json +0 -5
  92. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/src/index.ts +0 -3
  93. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/package.json +0 -6
  94. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/src/index.ts +0 -1
  95. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/package.json +0 -5
  96. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/src/index.ts +0 -8
  97. package/claude/skills/sd-docs/evals/golden.jsonl +0 -2
  98. package/claude/skills/sd-impl/evals/fixtures/case-a-new-screen/.specs/260513120000_warehouse/spec.md +0 -101
  99. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/.specs/260513120000_warehouse/spec.md +0 -101
  100. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/packages/app/src/screens/box-register/box-register.view.ts +0 -46
  101. package/claude/skills/sd-impl/evals/fixtures/case-c-new-cross/.specs/260513120000_warehouse/spec.md +0 -89
  102. package/claude/skills/sd-impl/evals/fixtures/case-d-spec-modify/.specs/260513120000_warehouse/spec.md +0 -101
  103. package/claude/skills/sd-impl/evals/golden.jsonl +0 -4
  104. package/claude/skills/sd-manual/evals/fixtures/new-manual/src/notification.ts +0 -25
  105. package/claude/skills/sd-manual/evals/fixtures/update-manual/.claude/references/sd-simplysm14/manuals/notification.md +0 -14
  106. package/claude/skills/sd-manual/evals/fixtures/update-manual/src/notification.ts +0 -37
  107. package/claude/skills/sd-manual/evals/golden.jsonl +0 -2
  108. package/claude/skills/sd-review/evals/fixtures/code-review/src/foo.ts +0 -7
  109. package/claude/skills/sd-review/evals/fixtures/doc-review/docs/foo.md +0 -4
  110. package/claude/skills/sd-review/evals/golden.jsonl +0 -2
  111. package/claude/skills/sd-skill/evals/fixtures/existing-skill/.claude/skills/todo-format/SKILL.md +0 -14
  112. package/claude/skills/sd-skill/evals/fixtures/new-skill/.gitkeep +0 -0
  113. package/claude/skills/sd-skill/evals/golden.jsonl +0 -2
  114. package/claude/skills/sd-spec/evals/fixtures/case-a-split//355/232/214/354/235/230/353/241/235.md +0 -20
  115. package/claude/skills/sd-spec/evals/fixtures/case-b-detail/.specs/260513120000_warehouse/spec.md +0 -95
  116. package/claude/skills/sd-spec/evals/golden.jsonl +0 -2
  117. package/claude/skills/sd-unpack/evals/fixtures/eml-with-text-attachment/meeting.eml +0 -21
  118. package/claude/skills/sd-unpack/evals/fixtures/simple-eml/meeting.eml +0 -10
  119. package/claude/skills/sd-unpack/evals/golden.jsonl +0 -2
  120. package/claude/skills/sd-use/evals/fixtures/empty/.gitkeep +0 -0
  121. package/claude/skills/sd-use/evals/golden.jsonl +0 -6
  122. /package/claude/{skills/sd-docs/references/doc-rules.md → workflows/sd-docs.rules.md} +0 -0
@@ -0,0 +1,62 @@
1
+ # @simplysm/core-browser — DOM 요소 확장
2
+
3
+ DOM 요소를 다룰 때 함께 읽히는 묶음. `Element.prototype`/`HTMLElement.prototype` 에 등록되는 확장 메서드와, 이벤트 핸들러·다중 요소용 정적 함수(`copyElement`/`pasteToElement`/`getBounds`)로 구성. 패키지를 import 하면 프로토타입 메서드가 자동 등록되므로 별도 초기화 불필요.
4
+
5
+ ## Element 확장 메서드
6
+
7
+ `Element.prototype` 에 등록. import 만으로 활성화.
8
+
9
+ - `findAll<TEl>(selector: string): TEl[]` — 선택자 일치 하위 요소를 배열로 반환. 선택자를 trim 한 결과가 빈 문자열이면 `[]`. `querySelectorAll` 결과를 NodeList 대신 배열로 받고 빈 선택자 예외를 회피할 때.
10
+ - `findFirst<TEl>(selector: string): TEl | undefined` — 첫 일치 하위 요소 또는 `undefined`. 빈 선택자도 `undefined`, 미일치도 `undefined`. `querySelector` 의 `null` 을 `undefined` 로 정규화한 형태.
11
+ - `prependChild<TEl>(child: TEl): TEl` — 자식을 첫 번째 위치(`insertBefore(child, firstElementChild)`)에 삽입하고 그 요소 반환. 맨 앞에 끼울 때.
12
+ - `getParents(): Element[]` — 모든 조상 요소를 가까운 것부터 먼 순서로 배열 반환. 조상 체인 순회·특정 조상 포함 판정에.
13
+ - `findTabbableParent(): HTMLElement | undefined` — `tabbable` 라이브러리 기준 첫 탭 이동 가능 조상. 없으면 `undefined`. 포커스 위임 대상을 위로 탐색할 때.
14
+ - `findFirstTabbableChild(): HTMLElement | undefined` — TreeWalker 로 순회한 첫 탭 이동 가능 하위 요소. 없으면 `undefined`. 컨테이너 진입 시 자동 포커스 대상을 찾을 때.
15
+ - `isOffsetElement(): boolean` — `getComputedStyle().position` 이 relative/absolute/fixed/sticky 중 하나면 true, 아니면 false. offset parent(절대배치 기준) 역할 여부 판정에.
16
+ - `isVisible(): boolean` — `getClientRects().length > 0` 이고 `visibility !== "hidden"` 이고 `opacity !== "0"` 를 모두 만족하면 true. 화면 표시 여부 판정에(display:none 은 clientRects 가 비어 false).
17
+
18
+ ```ts
19
+ import "@simplysm/core-browser";
20
+ const rows = containerEl.findAll<HTMLElement>("tr");
21
+ const first = containerEl.findFirstTabbableChild();
22
+ ```
23
+
24
+ ## HTMLElement 확장 메서드
25
+
26
+ `HTMLElement.prototype` 에 등록. 위와 동일하게 import 만으로 활성화.
27
+
28
+ - `repaint(): void` — `offsetHeight` 접근으로 강제 동기 레이아웃(reflow)을 유발해 즉시 리페인트. 스타일 변경 직후 즉각 반영을 강제할 때.
29
+ - `getRelativeOffset(parent: HTMLElement | string): { top: number; left: number }` — 부모 기준 CSS `top`/`left` 좌표 계산. 뷰포트 위치(getBoundingClientRect)·부모 내부 스크롤(scrollTop/Left)·중간 요소 border 두께·CSS transform 까지 반영해, 드롭다운/팝업 위치 지정에 바로 쓸 수 있는 좌표 반환. 부모를 못 찾으면 `ArgumentError` throw.
30
+ - `parent: HTMLElement | string` — 기준 부모. 문자열이면 `this.closest(parent)` 로 조상 탐색, 요소면 직접 사용. `document.body` 나 `".container"` 식으로 지정.
31
+ - `scrollIntoViewIfNeeded(target, offset?): void` — 대상이 스크롤 영역의 상단/좌측 경계를 벗어났을 때만 그쪽으로 스크롤해 보이게 함. 하단/우측 방향은 처리하지 않고 브라우저 기본 포커스 스크롤에 위임. 고정 헤더/컬럼이 있는 테이블의 포커스 처리에.
32
+ - `target: { top: number; left: number }` — 컨테이너 내 대상 위치(offsetTop/offsetLeft 기준).
33
+ - `offset: { top: number; left: number }` — 가려지면 안 되는 영역 크기(고정 헤더 높이·고정 컬럼 너비). 기본 `{ top: 0, left: 0 }`.
34
+
35
+ ```ts
36
+ const { top, left } = popupEl.getRelativeOffset(".container");
37
+ scrollEl.scrollIntoViewIfNeeded({ top: cellTop, left: cellLeft }, { top: headerH, left: fixedW });
38
+ ```
39
+
40
+ ## 클립보드 / 경계 측정 정적 함수
41
+
42
+ 이벤트 핸들러로 붙이거나 다중 요소를 한 번에 처리하는 함수. 프로토타입 확장이 아니라 named export 이므로 직접 import.
43
+
44
+ - `copyElement(event: ClipboardEvent): void` — copy 이벤트 핸들러용. 이벤트 타겟 내 첫 `input/textarea` 의 `value` 를 클립보드 `text/plain` 으로 기록하고 `preventDefault`. clipboardData 가 없거나 타겟이 Element 가 아니거나 input 이 없으면 무동작.
45
+ - `event: ClipboardEvent` — copy 이벤트 객체. `el.addEventListener("copy", copyElement)` 로 등록.
46
+ - `pasteToElement(event: ClipboardEvent): void` — paste 이벤트 핸들러용. 타겟 내 첫 `input/textarea` 의 전체 `value` 를 클립보드 텍스트로 교체하고 `input` 이벤트 dispatch(bubbles: true) 후 `preventDefault`. 커서 위치·선택 영역은 무시하고 전체를 치환.
47
+ - `event: ClipboardEvent` — paste 이벤트 객체. `el.addEventListener("paste", pasteToElement)` 로 등록.
48
+ - `getBounds(els: Element[], timeout?: number): Promise<ElementBounds[]>` — `IntersectionObserver` 로 여러 요소의 뷰포트 기준 경계를 한 번에 측정. 중복은 제거하고 입력 순서대로 정렬해 반환. 빈 배열이면 즉시 `[]`. 모든 요소 관측 완료 시 resolve, 제한시간 초과 시 `TimeoutError` throw(어느 경우든 finally 에서 observer disconnect).
49
+ - `els: Element[]` — 측정 대상. 중복은 제거되고 결과는 입력 순서로 정렬됨.
50
+ - `timeout: number` — 제한시간(ms). 기본 `5000`. 초과 시 `TimeoutError`.
51
+ - `ElementBounds` (반환 항목 타입):
52
+ - `target: Element` — 측정된 요소.
53
+ - `top: number` — 뷰포트 기준 상단 위치(boundingClientRect.top).
54
+ - `left: number` — 뷰포트 기준 좌측 위치(boundingClientRect.left).
55
+ - `width: number` — 요소 너비(boundingClientRect.width).
56
+ - `height: number` — 요소 높이(boundingClientRect.height).
57
+
58
+ ```ts
59
+ inputEl.addEventListener("copy", copyElement);
60
+ inputEl.addEventListener("paste", pasteToElement);
61
+ const bounds = await getBounds([elA, elB], 3000);
62
+ ```
@@ -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,20 +17,20 @@ store.close();
17
17
 
18
18
  시그니처:
19
19
 
20
- - `new IndexedDbStore(dbName: string, dbVersion: number, storeConfigs: StoreConfig[])` — DB 이름·버전·스토어 설정으로 생성(연결은 지연).
20
+ - `new IndexedDbStore(dbName: string, dbVersion: number, storeConfigs: StoreConfig[])` — DB 이름·버전·스토어 설정으로 생성(연결은 지연, 첫 작업 시 오픈).
21
21
  - dbName: `string` — IndexedDB 데이터베이스 이름.
22
- - dbVersion: `number` — DB 버전. 올리면 `onupgradeneeded` 에서 누락 스토어를 생성.
22
+ - dbVersion: `number` — DB 버전. 올리면 `onupgradeneeded` 에서 누락 스토어를 생성. 스키마(스토어 추가) 변경 시 증가.
23
23
  - storeConfigs: `StoreConfig[]` — 생성할 오브젝트 스토어 목록.
24
- - `StoreConfig` — 스토어 설정.
24
+ - `StoreConfig` — 스토어 설정 항목.
25
25
  - name: `string` — 오브젝트 스토어 이름. upgrade 시 미존재면 `createObjectStore` 로 생성.
26
26
  - keyPath: `string` — 스토어 keyPath(레코드에서 키로 쓸 속성명).
27
- - `open(): Promise<IDBDatabase>` — 연결을 열어 반환. 이미 열렸으면 캐시 반환, 진행 중이면 같은 Promise 공유. `onupgradeneeded` 시 없는 스토어만 생성. `onversionchange`/`onclose` 시 내부 캐시(`_db`/`_opening`) 해제로 다음 호출에 재오픈. `onblocked` 면 "다른 연결에 의해 데이터베이스가 차단되었습니다" Error, `onerror` 면 원본 에러로 reject. 보통 CRUD 가 자동 호출하므로 직접 호출 불필요.
27
+ - `open(): Promise<IDBDatabase>` — 연결을 열어 반환. 이미 열렸으면 캐시 반환, 진행 중이면 같은 Promise 공유(중복 오픈 방지). `onupgradeneeded` 시 없는 스토어만 생성. `onversionchange`/`onclose` 시 내부 캐시(`_db`/`_opening`) 해제해 다음 호출에 재오픈. `onblocked` 면 `Error("다른 연결에 의해 데이터베이스가 차단되었습니다")`, `onerror` 면 원본 에러로 reject. CRUD 가 자동 호출하므로 직접 호출 불필요.
28
28
  - `withStore<TResult>(storeName, mode, fn): Promise<TResult>` — 트랜잭션 1건 안에서 `fn(store)` 실행 후 완료까지 대기. `fn` 이 throw 하면 `tx.abort()` 후 그 에러로 reject(롤백), 정상이면 `oncomplete` 시 결과 resolve, `onerror` 면 `tx.error` 로 reject. 커서 등 저수준 IDB 작업을 감쌀 때.
29
29
  - storeName: `string` — 트랜잭션 대상 스토어.
30
- - mode: `IDBTransactionMode` — `"readonly"` | `"readwrite"` | `"versionchange"`. 쓰기면 `"readwrite"`.
30
+ - mode: `IDBTransactionMode` — `"readonly"`(읽기 전용) | `"readwrite"`(읽기·쓰기) | `"versionchange"`. 쓰기 작업이면 `"readwrite"`.
31
31
  - fn: `(store: IDBObjectStore) => Promise<TResult>` — 스토어를 받아 작업하는 콜백.
32
- - `get<TValue>(storeName, key): Promise<TValue | undefined>` — 키로 단건 조회. 미존재 시 `undefined`.
33
- - `put(storeName, value): Promise<void>` — 레코드 upsert(value 에 keyPath 필드 포함 필요).
32
+ - `get<TValue>(storeName, key): Promise<TValue | undefined>` — 키로 단건 조회. 미존재 시 `undefined`(결측 그대로 반환).
33
+ - `put(storeName, value): Promise<void>` — 레코드 upsert. value 에 keyPath 속성이 포함돼야 함.
34
34
  - `delete(storeName, key): Promise<void>` — 키로 단건 삭제.
35
35
  - `getAll<TItem>(storeName): Promise<TItem[]>` — 스토어 전체 레코드 배열 반환.
36
36
  - `close(): void` — 연결을 닫고 내부 캐시 해제. 다음 작업 시 재오픈. 페이지 정리 시 호출.
@@ -42,7 +42,7 @@ store.close();
42
42
 
43
43
  ## IndexedDbVirtualFs
44
44
 
45
- `IndexedDbStore` 한 스토어를 경로 키 기반 가상 파일시스템처럼 다루는 래퍼. 키는 `keyField` 속성에 들어가는 전체 경로 문자열이고, 각 레코드는 `VirtualFsEntry`(파일/디렉터리 + 선택적 base64 데이터)다.
45
+ `IndexedDbStore` 한 스토어를 경로 키 기반 가상 파일시스템처럼 다루는 래퍼. 키는 `keyField` 속성에 들어가는 전체 경로 문자열이고, 각 레코드는 `VirtualFsEntry`(파일/디렉터리 + 선택적 base64 데이터).
46
46
 
47
47
  ```ts
48
48
  const fs = new IndexedDbVirtualFs(store, "files", "key");
@@ -58,18 +58,19 @@ const ok = await fs.deleteByPrefix("/root/a"); // 하위 전체 삭제, 삭제
58
58
  - db: `IndexedDbStore` — 백엔드 저장소.
59
59
  - storeName: `string` — 사용할 오브젝트 스토어 이름.
60
60
  - keyField: `string` — 레코드에서 경로 키를 담는 속성명(스토어 keyPath 와 일치해야 함).
61
- - `VirtualFsEntry` — 저장 엔트리.
61
+ - `VirtualFsEntry` — 저장 엔트리 타입.
62
62
  - kind: `"file" | "dir"` — 엔트리 종류. `"file"` = 파일, `"dir"` = 디렉터리. 자식 나열·디렉터리 판정에 사용.
63
63
  - dataBase64: `string` — 파일 내용 base64. 디렉터리거나 빈 파일이면 생략(undefined).
64
64
  - `getEntry(fullKey): Promise<VirtualFsEntry | undefined>` — 전체 경로 키로 단건 조회. 미존재 시 `undefined`.
65
65
  - `putEntry(fullKey, kind, dataBase64?): Promise<void>` — 엔트리 저장. `keyField` 에 `fullKey`, 그리고 `kind`/`dataBase64` 를 함께 기록.
66
+ - fullKey: `string` — 저장할 전체 경로 키.
66
67
  - kind: `"file" | "dir"` — 저장할 엔트리 종류.
67
68
  - dataBase64: `string` — 파일 데이터(base64). 디렉터리면 생략.
68
- - `deleteByPrefix(keyPrefix): Promise<boolean>` — 커서로 키가 `keyPrefix` 자신이거나 `keyPrefix + "/"` 로 시작하는 엔트리 전부 삭제(부분 prefix 오삭제 방지). 하나라도 지웠으면 `true`. 디렉터리 트리 통째 삭제에.
69
+ - `deleteByPrefix(keyPrefix): Promise<boolean>` — 커서로 키가 `keyPrefix` 자신이거나 `keyPrefix + "/"` 로 시작하는 엔트리 전부 삭제(같은 접두어를 가진 다른 형제 경로 오삭제 방지). 하나라도 지웠으면 `true`, 없으면 `false`. 디렉터리 트리 통째 삭제에.
69
70
  - `listChildren(prefix): Promise<{ name: string; isDirectory: boolean }[]>` — `prefix` 직계 자식만 집계. 키에서 prefix 제거 후 첫 세그먼트를 이름으로 삼고, 하위 세그먼트가 더 있거나 엔트리 `kind === "dir"` 면 디렉터리로 판정. 디렉터리 목록 표시용(재귀 아님).
70
71
  - 반환 항목 name: `string` — 직계 자식 이름(첫 경로 세그먼트).
71
72
  - 반환 항목 isDirectory: `boolean` — 디렉터리 여부.
72
- - `ensureDir(fullKeyBuilder, dirPath): Promise<void>` — `dirPath` 상의 각 중간 디렉터리를 부모부터 누적 경로마다 없으면 생성. `dirPath === "/"` 면 루트 1건만 생성. 단일 `withStore` 트랜잭션으로 처리(원자적). 파일 쓰기 전 상위 디렉터리 보장에.
73
+ - `ensureDir(fullKeyBuilder, dirPath): Promise<void>` — `dirPath` 상의 각 중간 디렉터리를 부모부터 누적 경로마다 없으면 생성. `dirPath === "/"` 면 루트 1건만 생성. 단일 `withStore("readwrite")` 트랜잭션으로 처리(원자적). 파일 쓰기 전 상위 디렉터리 보장에.
73
74
  - fullKeyBuilder: `(path: string) => string` — 누적 경로(예: `/a`, `/a/b`)를 실제 저장 key 로 변환하는 콜백.
74
75
  - dirPath: `string` — 보장할 디렉터리 경로(`/` 구분). 빈 세그먼트는 무시.
75
76
 
@@ -1,138 +1,262 @@
1
1
  # @simplysm/core-common
2
2
 
3
- 브라우저·Node 공용 유틸리티 패키지. 에러 계층, 불변 날짜/시간 값, 큐·이벤트·로거, 컬렉션 프로토타입 확장, 객체·문자열·숫자·바이트·경로·직렬화 네임스페이스, 타입 유틸리티를 제공한다. 패키지를 import 하면 부수효과로 `Array`/`Set`/`Map` 프로토타입 확장이 주입된다.
3
+ 런타임 무관(Node.js·브라우저·Worker) 공통 유틸리티 패키지. 타입(날짜·UUID), 에러 트리, 비동기 큐/이벤트, Array/Set/Map 프로토타입 확장, 객체/문자열/숫자/바이트/경로/JSON/XML 유틸 네임스페이스를 제공. 패키지를 import 하면 부수효과로 `Array`/`Set`/`Map` 프로토타입 확장이 주입된다.
4
4
 
5
5
  ## 사용 트리거 인덱스
6
6
 
7
- - **날짜·시간** `DateTime`/`DateOnly`/`Time` 불변 값과 포맷(`dt`). 날짜 계산·주차·포맷이 필요할 때. 자세히: [datetime.md](./datetime.md)
8
- - **컬렉션 확장** `Array`/`Set`/`Map` 프로토타입 메서드(groupBy·distinct·orderBy·diffs·toTree·getOrCreate 등). 배열·맵·셋 가공 시. 자세히: [array-ext.md](./array-ext.md)
9
- - **obj 네임스페이스** 깊은 clone·equal·merge·merge3, 경로 접근, pick/omit, 변환. 객체 비교·병합·정리 시. 자세히: [obj.md](./obj.md)
10
- - **직렬화 (json/xml/transfer)**커스텀 타입 보존 JSON·XML·Worker 전송. 영속화·통신·워커 전달 시. 자세히: [json-transfer.md](./json-transfer.md)
11
- - **에러** `SdError`/`ArgumentError`/`NotImplementedError`/`TimeoutError`. throw 메시지 체인·인자 덤프가 필요할 때. (아래 인라인)
12
- - **env** `env`/`parseBoolEnv`. 환경변수 read/write·불리언 파싱 시. (아래 인라인)
13
- - **값 타입** `Uuid`, `LazyGcMap`. UUID 생성·검증, 자동 만료 캐시 맵. (아래 인라인)
14
- - **큐·이벤트·로거** `DebounceQueue`/`SerialQueue`/`EventEmitter`/`createLogger`. 디바운스·직렬 실행·이벤트·태그 로깅 시. (아래 인라인)
15
- - **문자열·숫자·바이트·경로 (str/num/bytes/path/wait/err/primitive)**케이스 변환·조사·파싱·포맷·hex/base64·POSIX 경로·대기. (아래 인라인)
16
- - **코드 템플릿 태그·ZIP**`js`/`ts`/`html`/`tsql`/`mysql`/`pgsql` 하이라이팅 태그, `ZipArchive`. (아래 인라인)
17
- - **타입 유틸리티**`Bytes`/`PrimitiveType*`/`DeepPartial`/`Type`. 원시 타입 매핑·생성자 타입이 필요할 때. (아래 인라인)
7
+ - **에러 클래스** (`SdError`/`ArgumentError`/`NotImplementedError`/`TimeoutError`) 원인 체인을 가진 에러를 throw 하거나 `instanceof` 분기할 때. 자세히: [errors.md](./errors.md)
8
+ - **날짜/시간 타입** (`DateTime`/`DateOnly`/`Time`, `dt` 네임스페이스) 불변 날짜·시간 값을 만들고 파싱·산술·포맷할 때. 자세히: [datetime.md](./datetime.md)
9
+ - **Array 확장 메서드** (`Array.prototype` 전역 확장) `single`/`groupBy`/`distinct`/`orderBy`/`diffs`/`toTree` 컬렉션 가공이 필요할 때. 자세히: [array-ext.md](./array-ext.md)
10
+ - **객체 유틸** (`obj` 네임스페이스, `DeepPartial`/`Type`) — 깊은 복사·동등성·병합·체인 경로 접근·타입 안전 순회가 필요할 때. 자세히: [obj.md](./obj.md)
11
+ - **JSON/Worker 직렬화** (`json`/`transfer` 네임스페이스) 커스텀 타입(날짜·UUID·Map·Set·Error)을 보존하며 JSON 또는 Worker 메시지로 직렬화할 때. 자세히: [json-transfer.md](./json-transfer.md)
12
+ - **비동기 런타임** (`DebounceQueue`/`SerialQueue`/`EventEmitter`/`LazyGcMap`/`createLogger`) 디바운스·직렬 실행·타입 안전 이벤트·자동 만료 캐시·태그 로거가 필요할 때. 자세히: [async-runtime.md](./async-runtime.md)
13
+ - **`Uuid`** — UUID v4 생성·검증·바이트 변환이 필요할 때. (아래 인라인 "값 타입 보조 — Uuid")
14
+ - **환경변수** (`env`/`parseBoolEnv`) process.env / import.meta.env 를 런타임 무관하게 읽고 쓸 때. (아래 인라인 "환경변수")
15
+ - **Set/Map 확장** (`Set.prototype.adds`/`toggle`, `Map.prototype.getOrCreate`/`update`) — Set/Map 체이닝으로 다룰 때. (아래 인라인 "Set/Map 확장")
16
+ - **문자열 유틸** (`str` 네임스페이스) 한국어 조사·케이스 변환·전각 변환·빈 문자열 가드가 필요할 때. (아래 인라인 "str")
17
+ - **숫자 유틸** (`num` 네임스페이스) 느슨한 정수/실수 파싱·천단위 포맷이 필요할 때. (아래 인라인 "num")
18
+ - **바이트 유틸** (`bytes` 네임스페이스) — Uint8Array hex/base64/concat 변환이 필요할 때. (아래 인라인 "bytes")
19
+ - **경로 유틸** (`path` 네임스페이스) — 브라우저에서 POSIX 경로 join/basename/extname 이 필요할 때. (아래 인라인 "path")
20
+ - **XML 유틸** (`xml` 네임스페이스) — XML 파싱/직렬화가 필요할 때. (아래 인라인 "xml")
21
+ - **대기 유틸** (`wait` 네임스페이스) — 조건 폴링·지연이 필요할 때. (아래 인라인 "wait")
22
+ - **에러 메시지 추출** (`err` 네임스페이스) — catch 의 `unknown` 에서 메시지 문자열을 뽑을 때. (아래 인라인 "err")
23
+ - **원시 타입 추론** (`primitive` 네임스페이스, `PrimitiveType*`/`Bytes`) — 런타임 값에서 ORM 원시 타입 문자열을 얻을 때. (아래 인라인 "primitive / 공통 타입")
24
+ - **코드 템플릿 태그** (`js`/`ts`/`html`/`tsql`/`mysql`/`pgsql`) — IDE 하이라이팅 + 들여쓰기 정규화가 필요할 때. (아래 인라인 "템플릿 태그")
25
+ - **`ZipArchive`** — ZIP 읽기/쓰기/압축/해제가 필요할 때. (아래 인라인 "ZipArchive")
26
+
27
+ ## 값 타입 보조 — Uuid
18
28
 
19
- ## 에러
29
+ ```typescript
30
+ class Uuid {
31
+ static generate(): Uuid; // crypto.getRandomValues 기반 v4 생성
32
+ static fromBytes(bytes: Bytes): Uuid; // 16바이트 Uint8Array → Uuid (16바이트 아니면 ArgumentError)
33
+ constructor(uuid: string); // 형식 검증 후 보관 (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, 위반 시 ArgumentError)
34
+ toString(): string; // UUID 문자열 반환
35
+ toBytes(): Bytes; // 16바이트 Uint8Array 반환
36
+ }
37
+ ```
38
+
39
+ - `generate()` — 새 랜덤 UUID 가 필요할 때(엔티티 PK 등). 암호학적으로 안전한 난수 사용.
40
+ - `fromBytes(bytes)` / `toBytes()` — 바이너리 저장/전송과 문자열 표현 사이를 오갈 때. 입력 바이트 길이가 16 이 아니면 `ArgumentError` throw.
41
+ - `new Uuid(str)` — 외부에서 받은 문자열을 검증해 값으로 승격할 때. 형식 불일치 시 `ArgumentError` throw.
42
+
43
+ ```typescript
44
+ const id = Uuid.generate();
45
+ const restored = new Uuid(id.toString());
46
+ ```
47
+
48
+ ## 환경변수 — env / parseBoolEnv
49
+
50
+ ```typescript
51
+ function env(key: string): string | undefined; // 읽기: process.env 우선, 없으면 import.meta.env
52
+ function env(key: string, value: string): void; // 쓰기: process.env[key] = value (process 존재 시)
53
+ function parseBoolEnv(value: unknown): boolean; // "true"/"1"/"yes"/"on"(대소문자 무시) → true, 그 외 false
54
+ ```
55
+
56
+ - `env(key)` — 단일 인자. Node 면 `process.env`, 브라우저(Vite) 면 `import.meta.env` 에서 조회. 둘 다 없으면 `undefined`. "값 없음"을 빈 문자열로 치환하지 않음.
57
+ - `env(key, value)` — 2번째 인자 전달 시 쓰기 모드. `process` 가 없는 환경(순수 브라우저)에서는 아무 동작 안 함.
58
+ - `parseBoolEnv(value)` — boolean 플래그용 환경변수를 해석할 때. 위 4개 리터럴만 true.
59
+
60
+ ```typescript
61
+ if (parseBoolEnv(env("DEV"))) { /* 개발 모드 */ }
62
+ ```
63
+
64
+ ## Set/Map 확장
65
+
66
+ 전역 `Set.prototype` / `Map.prototype` 에 메서드를 추가(import 시 자동 적용, `enumerable: false`).
67
+
68
+ ```typescript
69
+ interface Set<T> {
70
+ adds(...values: T[]): this; // 여러 값 일괄 추가, this 반환(체이닝)
71
+ toggle(value: T, addOrDel?: "add" | "del"): this; // 토글: 인자 생략 시 있으면 제거/없으면 추가
72
+ }
73
+ interface Map<K, V> {
74
+ getOrCreate(key: K, newValue: V): V; // 값 직접 지정 (key 없을 때만 set)
75
+ getOrCreate(key: K, newValueFn: () => V): V; // 팩토리 지연 생성 (비싼 연산용)
76
+ update(key: K, updateFn: (v: V | undefined) => V): void; // 현재 값(없으면 undefined) → 새 값으로 갱신
77
+ }
78
+ ```
79
+
80
+ - `Set.toggle(value, addOrDel)` — `addOrDel` 생략 시 존재 여부로 자동 토글, `"add"` 면 강제 추가, `"del"` 면 강제 제거. 조건부 선택 상태(`isOn ? "add" : "del"`) 표현에 사용.
81
+ - `Map.getOrCreate` — 2번째 인자가 함수면 팩토리로 인식되어 호출됨. 함수 자체를 값으로 저장하려면 `() => myFn` 처럼 한 번 더 감싼다.
82
+ - `Map.update` — key 가 없어도 `updateFn(undefined)` 가 호출되어 새 값이 set 됨. 카운터 증가(`(v) => (v ?? 0) + 1`)·배열 누적에 사용.
83
+
84
+ ```typescript
85
+ new Set([1, 2]).adds(3, 4).toggle(2); // {1, 3, 4}
86
+ countMap.update("k", (v) => (v ?? 0) + 1);
87
+ ```
88
+
89
+ ## str
90
+
91
+ `import * as str` 로 사용하는 문자열 유틸 네임스페이스.
92
+
93
+ ```typescript
94
+ str.getKoreanSuffix(text: string, type: "을"|"은"|"이"|"와"|"랑"|"로"|"라"): string;
95
+ str.replaceFullWidth(str: string): string;
96
+ str.toPascalCase(str: string): string;
97
+ str.toCamelCase(str: string): string;
98
+ str.toKebabCase(str: string): string;
99
+ str.toSnakeCase(str: string): string;
100
+ str.isNullOrEmpty(str: string | undefined): str is "" | undefined;
101
+ str.insert(str: string, index: number, insertString: string): string;
102
+ ```
103
+
104
+ - `getKoreanSuffix(text, type)` — 마지막 글자 받침 유무로 조사 선택. `type` 은 쌍의 대표 글자: `"을"`=을/를, `"은"`=은/는, `"이"`=이/가, `"와"`=과/와, `"랑"`=이랑/랑, `"로"`=으로/로(단 받침 ㄹ이면 "로"), `"라"`=이라/라. 한글 아닌 글자로 끝나면 받침 없음 처리.
105
+ - `replaceFullWidth(str)` — 전각 영문/숫자/공백/괄호를 반각으로. 스캔된 바코드·일본어 입력 정규화에 사용.
106
+ - `toPascalCase`/`toCamelCase`/`toKebabCase`/`toSnakeCase` — `-` `_` `.` 구분자 또는 대문자 경계 기준 케이스 변환. kebab/snake 는 연속 대문자를 각각 분리(`XMLParser`→`x-m-l-parser`)하고 기존 구분자는 보존.
107
+ - `isNullOrEmpty(str)` — 타입 가드. true 면 `"" | undefined`, false 면 비어있지 않은 `string` 으로 좁혀짐.
108
+ - `insert(str, index, insertString)` — 지정 위치에 삽입한 새 문자열 반환(원본 불변).
109
+
110
+ ```typescript
111
+ str.getKoreanSuffix("사과", "을"); // "를"
112
+ str.toKebabCase("HelloWorld"); // "hello-world"
113
+ ```
114
+
115
+ ## num
20
116
 
21
- `SdError` 를 루트로 한 계층. 메시지는 상위→하위→원인 순서로 `" => "` 결합되어 출력된다.
117
+ ```typescript
118
+ num.parseInt(text: unknown): number | undefined; // 비숫자 제거 후 정수 (소수점 이하 버림)
119
+ num.parseFloat(text: unknown): number | undefined; // 비숫자 제거 후 실수
120
+ num.parseRoundedInt(text: unknown): number | undefined; // parseFloat 후 반올림
121
+ num.isNullOrEmpty(val: number | undefined): val is 0 | undefined;
122
+ num.format(val: number, digit?: { max?: number; min?: number }): string;
123
+ ```
22
124
 
23
- - `new SdError(cause: Error, ...messages)` / `new SdError(...messages)` ES2024 `cause` 활용. `cause` 있으면 stack 원인 stack 이어 붙임. `messages` 역순 결합(뒤 인자가 하위).
24
- - `new ArgumentError(argObj)` / `new ArgumentError(message, argObj)` 유효하지 않은 인자용. `argObj` 를 YAML 로 메시지에 포함(기본 메시지 "잘못된 인자입니다.").
25
- - `new NotImplementedError(message?)` — 미구현 분기·추상 스텁용. 메시지 "미구현(: message)".
26
- - `new TimeoutError(count?, message?)` — 대기 초과용. `count` 시도 횟수. `wait.until` 최대 시도 초과 자동 throw.
125
+ - `parseInt`/`parseFloat` `0-9 . -` 문자를 제거하고 파싱. 선행 `-` 음수 부호로 유지하고 중간 하이픈은 제거(`"010-1234"`→`101234`). 파싱 불가 시 결측(`undefined`) 그대로 반환. 숫자 입력은 그대로(정수는 trunc).
126
+ - `parseRoundedInt` — 소수 반올림이 필요할 때(`"12.6"`→13).
127
+ - `isNullOrEmpty(val)` — 타입 가드. null/undefined/0 이면 true → `0 | undefined`. "0 과 미입력을 같이 비움 처리"가 필요한 화면 가드용.
128
+ - `format(val, digit)` — `toLocaleString` 기반 천단위 구분. `digit.max` 최대 소수 자릿수, `digit.min` 최소(부족분 0 채움). `val` 결측이면 결과도 `undefined`(오버로드).
27
129
 
28
130
  ```typescript
29
- throw new SdError(err, "API 호출 실패"); // "API 호출 실패 => <원인 메시지>"
30
- throw new ArgumentError("잘못된 사용자", { userId }); // YAML 인자 덤프 포함
131
+ num.parseInt("1,234원"); // 1234
132
+ num.format(1234.5, { min: 2 }); // "1,234.50"
31
133
  ```
32
134
 
33
- ## env
135
+ ## bytes
34
136
 
35
- - `env(key): string | undefined` — 환경변수 읽기(`process.env` 우선, 없으면 `import.meta.env`).
36
- - `env(key, value): void` — `process.env` 에 쓰기(process 가 있을 때만).
37
- - `parseBoolEnv(value): boolean` — `"true"`/`"1"`/`"yes"`/`"on"`(대소문자 무시) `true`, 그 외 → `false`.
137
+ ```typescript
138
+ bytes.concat(arrays: Bytes[]): Bytes;
139
+ bytes.toHex(bytes: Bytes): string; // 소문자 hex
140
+ bytes.fromHex(hex: string): Bytes; // 홀수 길이/비hex 문자 → ArgumentError
141
+ bytes.toBase64(bytes: Bytes): string;
142
+ bytes.fromBase64(base64: string): Bytes; // 비base64 문자/잘못된 길이 → ArgumentError
143
+ ```
38
144
 
39
- ## 타입
145
+ - `concat(arrays)` — 여러 Uint8Array 를 하나로 이어붙인 새 배열. 청크 결합에 사용.
146
+ - `toHex`/`fromHex` — 바이너리를 16진 문자열로 표기/복원. `fromHex` 는 길이가 짝수이고 `[0-9a-fA-F]` 만 허용(위반 시 throw).
147
+ - `toBase64`/`fromBase64` — Node 의존 없는 자체 구현 base64. `fromBase64` 는 공백/패딩 정규화 후 검증, 길이 나머지 1 이면 throw.
40
148
 
41
- ### Uuid
149
+ ## path
42
150
 
43
- 암호학적으로 안전한 UUID v4(`crypto.getRandomValues` 기반) 불변 값 객체.
44
- - `Uuid.generate(): Uuid` v4 생성.
45
- - `Uuid.fromBytes(bytes): Uuid` 16바이트 `Uint8Array` 생성(길이≠16 이면 `ArgumentError`).
46
- - `new Uuid(uuidStr)` 형식(`8-4-4-4-12` hex) 검증, 불일치 시 `ArgumentError`.
47
- - `toString(): string` / `toBytes(): Uint8Array` — 문자열·16바이트 변환.
151
+ ```typescript
152
+ path.join(...segments: string[]): string; // POSIX(슬래시) join, 중복 슬래시 정리
153
+ path.basename(filePath: string, ext?: string): string; // 마지막 세그먼트, ext 일치 제거
154
+ path.extname(filePath: string): string; // 확장자( 포함), 숨김파일은 ""
155
+ ```
48
156
 
49
- ### LazyGcMap
157
+ - POSIX 슬래시(`/`) 전용. Windows 백슬래시 경로는 미지원. 브라우저·Capacitor 환경에서 Node `path` 대체용.
158
+ - `basename(p, ext)` — `ext` 가 끝과 일치하면 그만큼 잘라 확장자 없는 이름 반환.
159
+ - `extname` — `.gitignore` 같은 선행 점 파일은 빈 문자열(`""`).
50
160
 
51
- LRU 접근시간 기반 자동 만료 Map. **반드시 `dispose()` 호출** 해야 GC 타이머가 정리됨(아니면 메모리 누수).
52
- - `new LazyGcMap({ gcInterval?, expireTime, onExpire? })` — `expireTime`(ms): 마지막 접근 후 이 시간 지나면 삭제. `gcInterval`(ms): GC 주기, 기본 `expireTime/10`(최소 1000). `onExpire(key, value)`: 만료 시 콜백(async 가능, 에러 시 로그 후 계속).
53
- - `get(key)` — 조회(접근시간 갱신). `has(key)` — 존재 확인(갱신 안 함). `set(key, value)` — 저장(첫 set 에 GC 타이머 시작). `delete(key)` / `clear()`(인스턴스 재사용 가능) / `dispose()`(이후 사용 불가).
54
- - `getOrCreate(key, factory)` — 없으면 `factory()` 로 생성·저장(dispose 후 호출 시 throw).
55
- - `size` getter, `keys()`/`values()`/`entries()` 이터레이터.
161
+ ## xml
56
162
 
57
- ## 큐·이벤트·로거
163
+ ```typescript
164
+ xml.parse(str: string, options?: { stripTagPrefix?: boolean }): unknown;
165
+ xml.stringify(obj: unknown, options?: XmlBuilderOptions): string;
166
+ ```
58
167
 
59
- ### EventEmitter\<TEvents\>
168
+ - `parse` — `fast-xml-parser` 기반. 속성은 `$` 객체, 텍스트는 `_` 키, 자식 요소는 배열로. `stripTagPrefix: true` 면 `ns:tag` 의 네임스페이스 접두사 제거(속성 접두사는 유지).
169
+ - `stringify` — `$`(속성)·`_`(텍스트) 규약으로 XML 문자열 생성. `options` 로 fast-xml-parser 빌더 옵션 덮어쓰기.
60
170
 
61
- `EventTarget` 래퍼. `TEvents` 는 `{ 이벤트명: 데이터타입 }` 맵. 보통 상속해 사용.
62
- - `on(type, listener)` / `off(type, listener)` — 등록/해제(같은 리스너 중복 등록은 무시).
63
- - `emit(type, data?)` 발행(데이터 타입이 `void` 인자 생략).
64
- - `listenerCount(type): number` — 리스너 수.
65
- - `dispose()` — 모든 리스너 제거.
171
+ ```typescript
172
+ xml.parse('<root id="1"><item>hi</item></root>');
173
+ // { root: { $: { id: "1" }, item: [{ _: "hi" }] } }
174
+ ```
66
175
 
67
- ### DebounceQueue (extends EventEmitter\<{ error: SdError }\>)
176
+ ## wait
68
177
 
69
- 연속 호출 시 마지막만 실행하는 디바운스 큐. 실행 중 들어온 요청은 지연 없이 직후 즉시 처리.
70
- - `new DebounceQueue(delay?)` `delay`(ms) 생략 다음 이벤트 루프에 즉시.
71
- - `run(fn)` — 대기 함수 교체(이전 대기 폐기). `dispose()` — 타이머·대기 정리.
72
- - 작업 에러는 `"error"` 리스너가 있으면 emit, 없으면 내부 로거로 출력.
178
+ ```typescript
179
+ wait.until(forwarder: () => boolean | Promise<boolean>, milliseconds?: number, maxCount?: number): Promise<void>;
180
+ wait.time(millisecond: number): Promise<void>;
181
+ ```
73
182
 
74
- ### SerialQueue (extends EventEmitter\<{ error: SdError }\>)
183
+ - `until(fn, interval, maxCount)` — `fn` 이 true 될 때까지 `interval`(기본 100ms) 간격으로 폴링. 첫 호출에서 true 면 즉시 반환. `maxCount` 지정 시 초과하면 `TimeoutError` throw(미지정이면 무제한).
184
+ - `time(ms)` — `setTimeout` 기반 지연 Promise.
75
185
 
76
- 추가된 함수를 순차 실행. 한 작업이 실패해도 후속은 계속.
77
- - `new SerialQueue(gap?)` `gap`(ms) 작업 사이 간격(기본 0).
78
- - `run(fn)` — 큐에 추가·실행. `dispose()` — 대기분 비움(실행 중 작업은 완료).
79
- - 에러 처리는 DebounceQueue 와 동일.
186
+ ```typescript
187
+ await wait.until(() => ready, 100, 50); // 최대 50회(5초) 폴링
188
+ await wait.time(300);
189
+ ```
80
190
 
81
- ### createLogger
191
+ ## err
82
192
 
83
- - `createLogger(tag): ConsolaInstance` — `consola.withTag` 를 지연 생성하는 lazy 로거. 모듈 레벨에서 선언해도 이후 `setupConsola` 변경(level/reporters)이 반영됨. `vi.spyOn` 호환.
193
+ ```typescript
194
+ err.message(error: unknown): string; // Error 면 .message, 아니면 String(error)
195
+ ```
84
196
 
85
- ## 문자열·숫자·바이트·경로·대기
197
+ - catch 블록의 `unknown` 에러에서 안전하게 메시지 문자열을 뽑을 때.
86
198
 
87
- ### str 네임스페이스
88
- - `str.getKoreanSuffix(text, type)` 받침에 따라 한국어 조사 반환. `type`: `"을"`(을/를)·`"은"`(은/는)·`"이"`(이/가)·`"와"`(과/와)·`"랑"`(이랑/랑)·`"로"`(으로/로, ㄹ받침 예외)·`"라"`(이라/라).
89
- - `str.replaceFullWidth(s)` — 전각 영문·숫자·공백·괄호 → 반각.
90
- - `str.toPascalCase`/`toCamelCase`/`toKebabCase`/`toSnakeCase(s)` — 케이스 변환(연속 대문자 분리, 기존 구분자 유지).
91
- - `str.isNullOrEmpty(s): s is "" | undefined` — null/undefined/빈 문자열 타입 가드.
92
- - `str.insert(s, index, insertString)` — 지정 위치에 문자열 삽입.
199
+ ```typescript
200
+ try { /* ... */ } catch (e) { logger.error(err.message(e)); }
201
+ ```
93
202
 
94
- ### num 네임스페이스
95
- - `num.parseInt(text)` / `num.parseFloat(text)` — 비숫자 문자 제거 후 파싱(선행 `-` 만 음수 부호 유지). 실패 시 `undefined`. parseInt 는 소수부 버림.
96
- - `num.parseRoundedInt(text)` — float 파싱 후 반올림 정수.
97
- - `num.isNullOrEmpty(val): val is 0 | undefined` — null/undefined/0 타입 가드.
98
- - `num.format(val, digit?)` — 천 단위 구분 문자열. `digit: { max?, min? }` 소수 자릿수(min 부족분 0 채움). val 이 undefined 면 undefined.
203
+ ## primitive / 공통 타입
99
204
 
100
- ### bytes 네임스페이스 (`Bytes` = `Uint8Array`)
101
- - `bytes.concat(arrays)` 여러 Uint8Array 결합.
102
- - `bytes.toHex(bytes)` / `bytes.fromHex(hex)` — hex 왕복(소문자 출력. fromHex 는 홀수 길이·비hex 문자 시 `ArgumentError`).
103
- - `bytes.toBase64(bytes)` / `bytes.fromBase64(base64)` — base64 왕복(fromBase64 는 공백·패딩 정규화, 잘못된 문자·길이 시 `ArgumentError`).
205
+ ```typescript
206
+ primitive.typeStr(value: PrimitiveTypeMap[PrimitiveTypeStr]): PrimitiveTypeStr; // 런타임 값 → 원시 타입 문자열
104
207
 
105
- ### path 네임스페이스 (POSIX `/` 전용, 브라우저·Capacitor용)
106
- - `path.join(...segments)` 슬래시 결합(중복 슬래시 정리). `path.basename(filePath, ext?)` 파일명(ext 일치 시 제거). `path.extname(filePath)` — 확장자(숨김 파일은 빈 문자열).
208
+ type Bytes = Uint8Array;
209
+ type PrimitiveTypeMap = { string; number; boolean; DateTime; DateOnly; Time; Uuid; Bytes };
210
+ type PrimitiveTypeStr = keyof PrimitiveTypeMap;
211
+ type PrimitiveType = PrimitiveTypeMap[PrimitiveTypeStr] | undefined;
212
+ ```
107
213
 
108
- ### wait 네임스페이스
109
- - `wait.until(forwarder, milliseconds?, maxCount?)` — 조건이 true 될 때까지 대기. `milliseconds` 확인 간격(기본 100), `maxCount` 최대 시도(초과 시 `TimeoutError`, undefined 면 무제한).
110
- - `wait.time(millisecond)` — 지정 ms 대기 Promise.
214
+ - `primitive.typeStr(value)` — 값의 런타임 타입을 보고 `"string"|"number"|"boolean"|"DateTime"|"DateOnly"|"Time"|"Uuid"|"Bytes"` 중 하나 반환. 위 8종 외 값이면 `ArgumentError` throw. ORM 컬럼 타입 추론과 공유.
215
+ - `Bytes` — 바이너리 표준 별칭(`Uint8Array`). Buffer 대신 사용.
216
+ - `PrimitiveType` — 원시 타입 union + `undefined`. 결측 보존을 위해 `undefined` 포함.
217
+ - `DeepPartial<T>` / `Type<T>` 타입 유틸은 [obj.md](./obj.md) 의 "타입 유틸리티" 참조.
111
218
 
112
- ### err 네임스페이스
113
- - `err.message(error): string` — `unknown` 에러에서 메시지 추출(`Error` 면 `.message`, 아니면 `String()`). catch 블록용.
219
+ ## 템플릿 태그
114
220
 
115
- ### primitive 네임스페이스
116
- - `primitive.typeStr(value): PrimitiveTypeStr` 런타임 원시 타입 문자열(`"string"`|`"number"`|`"boolean"`|`"DateTime"`|`"DateOnly"`|`"Time"`|`"Uuid"`|`"Bytes"`). 미지원 타입은 `ArgumentError`.
221
+ ```typescript
222
+ js / ts / html / tsql / mysql / pgsql (strings: TemplateStringsArray, ...values: unknown[]): string;
223
+ ```
117
224
 
118
- ## 코드 템플릿 태그·ZIP
225
+ - 6개 태그 모두 동작은 동일: 보간 후 공통 들여쓰기 제거 + 앞뒤 빈 줄 trim. 차이는 IDE 의 언어 하이라이팅 힌트뿐(태그 이름이 곧 언어). 보간 값이 null/undefined 면 빈 문자열로 치환.
119
226
 
120
- 코드 하이라이팅용 태그 함수(동작은 문자열 결합 + 공통 들여쓰기 정규화로 동일):
121
- - `js`/`ts`/`html`/`tsql`/`mysql`/`pgsql` — 각각 JS·TS·HTML·MSSQL·MySQL·PostgreSQL 하이라이팅 의도. `` js`...` `` 형태로 사용.
227
+ ```typescript
228
+ const sql = mysql`
229
+ SELECT *
230
+ FROM users
231
+ `; // 공통 들여쓰기가 제거된 두 줄
232
+ ```
122
233
 
123
- ### ZipArchive
124
- ZIP 읽기/쓰기/압축/해제 클래스. 내부 캐시로 중복 해제 방지. **사용 후 `close()` 필수**.
125
- - `new ZipArchive(data?)` — `data`(`Blob | Bytes`) 생략 시 새 아카이브.
126
- - `get(fileName): Promise<Bytes | undefined>` — 특정 파일 추출. `exists(fileName): Promise<boolean>` — 존재 확인.
127
- - `extractAll(progressCallback?): Promise<Map<string, Bytes | undefined>>` — 전체 추출. `progressCallback(progress)` 의 `progress: ZipArchiveProgress { fileName, totalSize, extractedSize }`.
128
- - `write(fileName, bytes)` — 캐시에 파일 추가. `compress(): Promise<Bytes>` — 캐시 내용을 ZIP 으로 압축(내부에서 extractAll 호출, 대용량 메모리 주의). `close()` — 리더 닫고 캐시 비움.
234
+ ## ZipArchive
129
235
 
130
- ## 타입 유틸리티
236
+ ```typescript
237
+ class ZipArchive {
238
+ constructor(data?: Blob | Bytes); // 데이터 생략 시 새(쓰기용) 아카이브
239
+ extractAll(progressCallback?: (p: { fileName: string; totalSize: number; extractedSize: number }) => void): Promise<Map<string, Bytes | undefined>>;
240
+ get(fileName: string): Promise<Bytes | undefined>;
241
+ exists(fileName: string): Promise<boolean>;
242
+ write(fileName: string, bytes: Bytes): void; // 캐시에만 기록
243
+ compress(): Promise<Bytes>;
244
+ close(): Promise<void>;
245
+ }
246
+ ```
131
247
 
132
- `common.types` 타입(네임스페이스 아닌 직접 export):
133
- - `Bytes` = `Uint8Array` 바이너리 타입 별칭.
134
- - `PrimitiveTypeMap` — `{ string, number, boolean, DateTime, DateOnly, Time, Uuid, Bytes }` 원시 타입 매핑(orm-common 과 공유).
135
- - `PrimitiveTypeStr` = `keyof PrimitiveTypeMap` 원시 타입 문자열 key.
136
- - `PrimitiveType` = `PrimitiveTypeMap[PrimitiveTypeStr] | undefined` 원시 타입 union.
137
- - `DeepPartial<TObject>`모든 속성을 재귀적으로 optional 로(원시 타입은 그대로, object/array 에만 재귀).
138
- - `Type<TInstance>` — 클래스 생성자 타입(`new (...args) => TInstance`). DI·팩토리·`instanceof` 체크용.
248
+ - `constructor(data)` `Blob`/`Bytes` 전달 읽기용, 생략 시 쓰기용 빈 아카이브.
249
+ - `get`/`exists` 단일 파일 추출/존재 확인. 결과는 내부 캐시에 보관해 재추출 방지.
250
+ - `extractAll(cb)` — 전체 추출. `cb` 파일별 진행(`fileName`, 전체 바이트 `totalSize`, 누적 추출 `extractedSize`)을 보고.
251
+ - `write(name, bytes)` 캐시에만 기록. 실제 압축 산출은 `compress()` 호출 시.
252
+ - `compress()` 캐시(필요 `extractAll`)의 모든 파일을 ZIP 바이트로. 대용량은 전부 메모리 로드되므로 주의.
253
+ - `close()`리더 닫고 캐시 비움. `try/finally` 에서 호출 권장.
254
+
255
+ ```typescript
256
+ const archive = new ZipArchive(zipBytes);
257
+ try {
258
+ const content = await archive.get("file.txt");
259
+ } finally {
260
+ await archive.close();
261
+ }
262
+ ```