@simplysm/sd-claude 14.0.97 → 14.0.99

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 (77) hide show
  1. package/claude/references/sd-simplysm14/README.md +16 -16
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +81 -153
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +179 -205
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +71 -57
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +49 -109
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +58 -86
  7. package/claude/references/sd-simplysm14/apis/angular/kanban.md +32 -40
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +38 -52
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +86 -110
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +54 -86
  11. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +82 -74
  12. package/claude/references/sd-simplysm14/apis/angular/sheet.md +56 -80
  13. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +15 -15
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +21 -21
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +79 -53
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +9 -11
  17. package/claude/references/sd-simplysm14/apis/core-browser/README.md +15 -15
  18. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +20 -20
  19. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +18 -18
  20. package/claude/references/sd-simplysm14/apis/core-common/README.md +20 -49
  21. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +66 -55
  22. package/claude/references/sd-simplysm14/apis/core-common/collection-ext.md +83 -56
  23. package/claude/references/sd-simplysm14/apis/core-common/errors.md +32 -21
  24. package/claude/references/sd-simplysm14/apis/core-common/obj.md +57 -39
  25. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +36 -30
  26. package/claude/references/sd-simplysm14/apis/core-common/value-types.md +69 -41
  27. package/claude/references/sd-simplysm14/apis/core-node/README.md +4 -4
  28. package/claude/references/sd-simplysm14/apis/core-node/consola.md +15 -13
  29. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +11 -7
  30. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +8 -8
  31. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +29 -20
  32. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +14 -6
  33. package/claude/references/sd-simplysm14/apis/core-node/worker.md +3 -3
  34. package/claude/references/sd-simplysm14/apis/excel/README.md +3 -3
  35. package/claude/references/sd-simplysm14/apis/excel/cell.md +32 -32
  36. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +23 -24
  37. package/claude/references/sd-simplysm14/apis/excel/style.md +24 -30
  38. package/claude/references/sd-simplysm14/apis/excel/utils.md +20 -23
  39. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +60 -71
  40. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +36 -36
  41. package/claude/references/sd-simplysm14/apis/lint/README.md +7 -9
  42. package/claude/references/sd-simplysm14/apis/lint/recommended.md +59 -37
  43. package/claude/references/sd-simplysm14/apis/lint/rules.md +81 -74
  44. package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -6
  45. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +112 -78
  46. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +131 -75
  47. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +126 -82
  48. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +170 -113
  49. package/claude/references/sd-simplysm14/apis/orm-common/types.md +102 -48
  50. package/claude/references/sd-simplysm14/apis/orm-node/README.md +12 -13
  51. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +3 -3
  52. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +5 -5
  53. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +67 -65
  54. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +130 -123
  55. package/claude/references/sd-simplysm14/apis/service-client/README.md +63 -63
  56. package/claude/references/sd-simplysm14/apis/service-client/orm.md +22 -22
  57. package/claude/references/sd-simplysm14/apis/service-client/transport.md +30 -26
  58. package/claude/references/sd-simplysm14/apis/service-common/README.md +8 -8
  59. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +13 -6
  60. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +1 -1
  61. package/claude/references/sd-simplysm14/apis/service-server/README.md +43 -47
  62. package/claude/references/sd-simplysm14/apis/service-server/built-in-services.md +35 -0
  63. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +20 -19
  64. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +23 -25
  65. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +9 -9
  66. package/claude/references/sd-simplysm14/apis/storage/README.md +26 -26
  67. package/claude/references/sd-simplysm14/manuals/client-component.md +9 -1
  68. package/claude/references/sd-simplysm14/manuals/client-crud.md +1 -1
  69. package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -0
  70. package/claude/references/sd-simplysm14/manuals/client-service.md +1 -0
  71. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +1 -0
  72. package/claude/references/sd-simplysm14/manuals/client-ssg.md +1 -0
  73. package/claude/sd-system-prompt.md +11 -26
  74. package/claude/skills/sd-docs/references/subagent-prompt.md +4 -3
  75. package/claude/skills/sd-spec/SKILL.md +87 -18
  76. package/claude/skills/sd-spec/references/format.md +2 -2
  77. package/package.json +1 -1
@@ -1,19 +1,19 @@
1
1
  # @simplysm/core-browser — DOM 요소 확장
2
2
 
3
- DOM 요소를 다룰 때 함께 읽히는 묶음. `Element.prototype`/`HTMLElement.prototype` 에 등록되는 확장 메서드와, 이벤트 핸들러·다중 요소용 정적 함수(`copyElement`/`pasteToElement`/`getBounds`)로 구성. 패키지를 import 하면 프로토타입 메서드가 자동 등록되므로 별도 초기화는 필요 없다.
3
+ DOM 요소를 다룰 때 함께 읽히는 묶음. `Element.prototype`/`HTMLElement.prototype` 에 등록되는 확장 메서드와, 이벤트 핸들러·다중 요소용 정적 함수(`copyElement`/`pasteToElement`/`getBounds`)로 구성. 패키지를 import 하면 브라우저 환경에서 프로토타입 메서드가 자동 등록되므로 별도 초기화는 필요 없다(node/SSR 에선 `typeof Element` 가드로 등록을 건너뜀).
4
4
 
5
5
  ## Element 확장 메서드
6
6
 
7
7
  `Element.prototype` 에 등록. import 만으로 활성화.
8
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).
9
+ - `findAll<TEl extends Element = Element>(selector: string): TEl[]` — 선택자 일치 하위 요소를 `querySelectorAll` 결과의 배열로 반환. 선택자를 `trim` 한 결과가 빈 문자열이면 `[]`. NodeList 대신 배열로 받고 빈/공백 선택자 예외를 회피할 때.
10
+ - `findFirst<TEl extends Element = Element>(selector: string): TEl | undefined` — 첫 일치 하위 요소 또는 `undefined`. 빈/공백 선택자도 `undefined`, 미일치도 `undefined`. `querySelector` 의 `null` 을 `undefined` 로 정규화한 형태.
11
+ - `prependChild<TEl extends Element>(child: TEl): TEl` — 자식을 첫 번째 위치(`insertBefore(child, firstElementChild)`)에 삽입하고 그 요소를 반환. 맨 앞에 끼워 넣을 때.
12
+ - `getParents(): Element[]` — `parentNode` 를 타고 올라가며 모든 조상 Element 가까운 것부터 먼 순서로 배열 반환. 조상 체인 순회·특정 조상 포함 판정에.
13
+ - `findTabbableParent(): HTMLElement | undefined` — `parentElement` 를 위로 타며 `tabbable` 라이브러리 기준 첫 탭 이동 가능 조상을 반환. 없으면 `undefined`. 포커스 위임 대상을 위로 탐색할 때.
14
+ - `findFirstTabbableChild(): HTMLElement | undefined` — `TreeWalker(SHOW_ELEMENT)`깊이 우선 순회한 첫 탭 이동 가능(`isTabbable`) 하위 요소. 없으면 `undefined`. 컨테이너 진입 시 자동 포커스 대상을 찾을 때.
15
+ - `isOffsetElement(): boolean` — `getComputedStyle().position` 이 `relative`/`absolute`/`fixed`/`sticky` 중 하나면 `true`, 아니면(`static` 등) `false`. 절대배치 기준(offset parent) 역할 여부 판정에.
16
+ - `isVisible(): boolean` — `getClientRects().length > 0` 이고 `visibility !== "hidden"` 이고 `opacity !== "0"` 를 모두 만족하면 `true`. 화면 표시 여부 판정에(`display:none` 은 clientRects 가 비어 `false`).
17
17
 
18
18
  ```ts
19
19
  import "@simplysm/core-browser";
@@ -25,11 +25,11 @@ const first = containerEl.findFirstTabbableChild();
25
25
 
26
26
  `HTMLElement.prototype` 에 등록. 위와 동일하게 import 만으로 활성화.
27
27
 
28
- - `repaint(): void` — `offsetHeight` 접근으로 강제 동기 레이아웃(reflow)을 유발해 즉시 리페인트. 스타일 변경 직후 즉각 반영을 강제할 때.
29
- - `getRelativeOffset(parent: HTMLElement | string): { top: number; left: number }` — 부모 기준 CSS `top`/`left` 좌표 계산. 뷰포트 위치(getBoundingClientRect)·부모 내부 스크롤(scrollTop/Left)·중간 요소 border 두께·CSS transform 까지 반영해, 드롭다운/팝업 위치 지정에 바로 쓸 수 있는 좌표를 반환. 부모를 못 찾으면(`HTMLElement` 아님) `ArgumentError` throw.
28
+ - `repaint(): void` — `offsetHeight` 접근해 강제 동기 레이아웃(reflow)을 유발, 누적된 스타일 변경을 즉시 적용·리페인트시킨다. 스타일 변경 직후 즉각 반영을 강제할 때.
29
+ - `getRelativeOffset(parent: HTMLElement | string): { top: number; left: number }` — 부모 기준 CSS `top`/`left` 좌표 계산. 뷰포트 위치(`getBoundingClientRect`)·부모 내부 스크롤(`scrollTop`/`scrollLeft`)·중간 요소 border 두께·CSS `transform`(`DOMMatrix` 로 보정)까지 반영해, 드롭다운/팝업 위치 지정에 바로 쓸 좌표를 반환. 부모가 `HTMLElement` 가 아니면(선택자 미일치 등) `ArgumentError` throw.
30
30
  - `parent: HTMLElement | string` — 기준 부모. 문자열이면 `this.closest(parent)` 로 조상 탐색, 요소면 직접 사용(예: `document.body`, `".container"`).
31
- - `scrollIntoViewIfNeeded(target, offset?): void` — 대상이 스크롤 영역의 상단/좌측 경계를 벗어났을 때만 그쪽으로 스크롤해 보이게 함. 하단/우측 방향은 처리하지 않고 브라우저 기본 포커스 스크롤에 위임. 고정 헤더/컬럼이 있는 테이블의 포커스 처리에.
32
- - `target: { top: number; left: number }` — 컨테이너 내 대상 위치(offsetTop/offsetLeft 기준).
31
+ - `scrollIntoViewIfNeeded(target: { top: number; left: number }, offset?: { top: number; left: number }): void` — 대상이 스크롤 영역의 상단/좌측 경계를 벗어났을 때만(`target.top - scrollTop < offset.top`) 그쪽으로 스크롤해 보이게 함. 하단/우측 방향은 처리하지 않고 브라우저 기본 포커스 스크롤에 위임. 고정 헤더/컬럼이 있는 테이블의 포커스 처리에.
32
+ - `target: { top: number; left: number }` — 컨테이너 내 대상 위치(`offsetTop`/`offsetLeft` 기준).
33
33
  - `offset?: { top: number; left: number }` — 가려지면 안 되는 영역 크기(고정 헤더 높이·고정 컬럼 너비). 기본 `{ top: 0, left: 0 }`.
34
34
 
35
35
  ```ts
@@ -41,19 +41,19 @@ scrollEl.scrollIntoViewIfNeeded({ top: cellTop, left: cellLeft }, { top: headerH
41
41
 
42
42
  이벤트 핸들러로 붙이거나 다중 요소를 한 번에 처리하는 함수. 프로토타입 확장이 아니라 named export 이므로 직접 import.
43
43
 
44
- - `copyElement(event: ClipboardEvent): void` — copy 이벤트 핸들러용. 이벤트 타겟 내 첫 `input/textarea` 의 `value` 를 클립보드 `text/plain` 으로 기록하고 `preventDefault`. clipboardData 가 없거나 타겟이 Element 가 아니거나 input 없으면 무동작.
44
+ - `copyElement(event: ClipboardEvent): void` — copy 이벤트 핸들러용. 이벤트 타겟 내 첫 `input/textarea` 의 `value` 를 클립보드 `text/plain` 으로 기록하고 `preventDefault`. `clipboardData` 가 없거나 타겟이 Element 가 아니거나 input/textarea 없으면 무동작(기본 동작 유지).
45
45
  - `event: ClipboardEvent` — copy 이벤트 객체. `el.addEventListener("copy", copyElement)` 로 등록.
46
- - `pasteToElement(event: ClipboardEvent): void` — paste 이벤트 핸들러용. 타겟 내 첫 `input/textarea` 의 전체 `value` 를 클립보드 텍스트로 교체하고 `input` 이벤트 dispatch(`bubbles: true`) `preventDefault`. 커서 위치·선택 영역은 무시하고 전체를 치환.
46
+ - `pasteToElement(event: ClipboardEvent): void` — paste 이벤트 핸들러용. 타겟 내 첫 `input/textarea` 의 전체 `value` 를 클립보드 `text/plain` 텍스트로 교체하고 `input` 이벤트를 dispatch(`bubbles: true`) `preventDefault`. 커서 위치·선택 영역은 무시하고 전체를 치환. 조건 미충족 시 무동작.
47
47
  - `event: ClipboardEvent` — paste 이벤트 객체. `el.addEventListener("paste", pasteToElement)` 로 등록.
48
- - `getBounds(els: Element[], timeout?: number): Promise<ElementBounds[]>` — `IntersectionObserver` 로 여러 요소의 뷰포트 기준 경계를 한 번에 측정. 중복은 제거하고 입력 순서대로 정렬해 반환. 빈 배열이면 즉시 `[]`. 모든 요소 관측 완료 시 resolve, 제한시간 초과 시 `TimeoutError` throw(어느 경우든 finally 에서 observer disconnect).
48
+ - `getBounds(els: Element[], timeout?: number): Promise<ElementBounds[]>` — `IntersectionObserver` 로 여러 요소의 뷰포트 기준 경계를 한 번에 측정. 중복 요소는 제거하고 입력 순서대로 정렬해 반환. 빈 배열이면 즉시 `[]`. 모든 요소 관측 완료 시 resolve, 제한시간 초과 시 `TimeoutError` 로 reject(어느 경우든 `finally` 에서 observer `disconnect`).
49
49
  - `els: Element[]` — 측정 대상. 중복은 제거되고 결과는 입력 순서로 정렬됨.
50
50
  - `timeout?: number` — 제한시간(ms). 기본 `5000`. 초과 시 `TimeoutError`.
51
51
  - `ElementBounds` (반환 항목 타입):
52
52
  - `target: Element` — 측정된 요소.
53
- - `top: number` — 뷰포트 기준 상단 위치(boundingClientRect.top).
54
- - `left: number` — 뷰포트 기준 좌측 위치(boundingClientRect.left).
55
- - `width: number` — 요소 너비(boundingClientRect.width).
56
- - `height: number` — 요소 높이(boundingClientRect.height).
53
+ - `top: number` — 뷰포트 기준 상단 위치(`boundingClientRect.top`).
54
+ - `left: number` — 뷰포트 기준 좌측 위치(`boundingClientRect.left`).
55
+ - `width: number` — 요소 너비(`boundingClientRect.width`).
56
+ - `height: number` — 요소 높이(`boundingClientRect.height`).
57
57
 
58
58
  ```ts
59
59
  inputEl.addEventListener("copy", copyElement);
@@ -1,10 +1,10 @@
1
1
  # @simplysm/core-browser — IndexedDB 영속화
2
2
 
3
- 브라우저 IndexedDB 를 다룰 때 함께 읽히는 묶음. `IndexedDbStore` 는 연결·트랜잭션·KV CRUD 를 담당하고, `IndexedDbVirtualFs` 는 그 위에 경로 키 기반 가상 파일트리(entry put/get, prefix 삭제, 자식 나열, 디렉터리 보장) 얹는다.
3
+ 브라우저 IndexedDB 를 다룰 때 함께 읽히는 묶음. `IndexedDbStore` 는 연결·트랜잭션·키-값 CRUD 를 담당하고, `IndexedDbVirtualFs` 는 그 스토어를 경로 키 기반 가상 파일트리(entry put/get, prefix 삭제, 자식 나열, 디렉터리 보장) 다룬다.
4
4
 
5
5
  ## IndexedDbStore
6
6
 
7
- IndexedDB 연결을 지연 오픈·재사용하고, 스토어 단위 트랜잭션과 기본 KV 작업을 비동기로 감싼 클래스.
7
+ IndexedDB 연결을 지연 오픈·재사용하고, 스토어 단위 트랜잭션과 기본 키-값 작업을 비동기로 감싼 클래스.
8
8
 
9
9
  ```ts
10
10
  const store = new IndexedDbStore("appDb", 1, [{ name: "files", keyPath: "key" }]);
@@ -19,30 +19,30 @@ store.close();
19
19
 
20
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
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 가 자동 호출하므로 직접 호출 불필요.
28
- - `withStore<TResult>(storeName, mode, fn): Promise<TResult>` — 트랜잭션 1건 안에서 `fn(store)` 실행 완료까지 대기. `fn` 이 throw 하면 `tx.abort()` 후 그 에러로 reject(롤백), 정상이면 `oncomplete` 시 결과 resolve, `onerror` 면 `tx.error` 로 reject. 커서 등 저수준 IDB 작업을 감쌀 때.
27
+ - `open(): Promise<IDBDatabase>` — 연결을 열어 반환. 이미 열렸으면 캐시(`_db`) 반환, 진행 중이면 같은 Promise(`_opening`) 공유(중복 오픈 방지). `onupgradeneeded` 시 없는 스토어만 생성. `onversionchange`/`onclose` 시 내부 캐시를 해제해 다음 호출에 재오픈. `onblocked` 면 `Error("다른 연결에 의해 데이터베이스가 차단되었습니다")`, `onerror` 면 원본 `req.error` reject. CRUD 가 자동 호출하므로 직접 호출 불필요.
28
+ - `withStore<TResult>(storeName: string, mode: IDBTransactionMode, fn: (store: IDBObjectStore) => Promise<TResult>): Promise<TResult>` — 트랜잭션 1건 안에서 `fn(store)` 먼저 await 한 뒤 완료까지 대기. `fn` 이 throw 하면 `tx.abort()` 후 그 에러로 reject(롤백), 정상이면 `oncomplete` 시 결과 resolve, `onerror` 면 `tx.error` 로 reject. 커서 등 저수준 IDB 작업을 감쌀 때.
29
29
  - `storeName: string` — 트랜잭션 대상 스토어.
30
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 속성이 포함돼야 함.
34
- - `delete(storeName, key): Promise<void>` — 키로 단건 삭제.
35
- - `getAll<TItem>(storeName): Promise<TItem[]>` — 스토어 전체 레코드 배열 반환.
36
- - `close(): void` — 연결을 닫고 내부 캐시 해제. 다음 작업 시 재오픈. 페이지 정리 시 호출.
32
+ - `get<TValue>(storeName: string, key: IDBValidKey): Promise<TValue | undefined>` — 키로 단건 조회. 미존재 시 `undefined`(결측 그대로 반환).
33
+ - `put(storeName: string, value: unknown): Promise<void>` — 레코드 upsert. `value` 에 keyPath 속성이 포함돼야 함.
34
+ - `delete(storeName: string, key: IDBValidKey): Promise<void>` — 키로 단건 삭제.
35
+ - `getAll<TItem>(storeName: string): Promise<TItem[]>` — 스토어 전체 레코드 배열 반환.
36
+ - `close(): void` — 연결을 닫고 내부 캐시(`_db`/`_opening`) 해제. 다음 작업 시 자동 재오픈. 페이지 정리 시 호출.
37
37
 
38
38
  주의:
39
39
 
40
- - `withStore` 의 fn 이 throw 하면 트랜잭션 전체가 abort — 다건 쓰기를 하나의 `withStore` 안에 묶으면 원자성이 보장된다.
40
+ - `withStore` 의 `fn` 이 throw 하면 트랜잭션 전체가 abort — 다건 쓰기를 하나의 `withStore` 안에 묶으면 원자성이 보장된다.
41
41
  - 버전 변경(`onupgradeneeded`)은 누락 스토어 생성만 한다 — 기존 스토어 스키마 변경·인덱스 추가는 별도 처리가 필요하다.
42
42
 
43
43
  ## IndexedDbVirtualFs
44
44
 
45
- `IndexedDbStore` 의 한 스토어를 경로 키 기반 가상 파일시스템처럼 다루는 래퍼. 키는 `keyField` 속성에 들어가는 전체 경로 문자열이고, 각 레코드는 `VirtualFsEntry`(파일/디렉터리 + 선택적 base64 데이터).
45
+ `IndexedDbStore` 의 한 스토어를 경로 키 기반 가상 파일시스템처럼 다루는 래퍼. 키는 `keyField` 속성에 들어가는 전체 경로 문자열이고, 각 레코드는 `VirtualFsEntry`(파일/디렉터리 + 선택적 base64 데이터). 범위 조회는 `IDBKeyRange.bound(prefix, prefix + "￿")` 커서 기반.
46
46
 
47
47
  ```ts
48
48
  const fs = new IndexedDbVirtualFs(store, "files", "key");
@@ -60,17 +60,17 @@ const ok = await fs.deleteByPrefix("/root/a"); // 하위 전체 삭제, 삭제
60
60
  - `keyField: string` — 레코드에서 경로 키를 담는 속성명(스토어 keyPath 와 일치해야 함).
61
61
  - `VirtualFsEntry` — 저장 엔트리 타입.
62
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` 를 함께 기록.
63
+ - `dataBase64?: string` — 파일 내용 base64. 디렉터리거나 데이터를 넘기면 생략(undefined).
64
+ - `getEntry(fullKey: string): Promise<VirtualFsEntry | undefined>` — 전체 경로 키로 단건 조회. 미존재 시 `undefined`.
65
+ - `putEntry(fullKey: string, kind: "file" | "dir", dataBase64?: string): Promise<void>` — 엔트리 저장(upsert). `keyField` 에 `fullKey`, 그리고 `kind`/`dataBase64` 를 함께 기록.
66
66
  - `fullKey: string` — 저장할 전체 경로 키.
67
67
  - `kind: "file" | "dir"` — 저장할 엔트리 종류.
68
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"` 면 디렉터리로 판정. 디렉터리 목록 표시용(재귀 아님).
69
+ - `deleteByPrefix(keyPrefix: string): Promise<boolean>` — 커서로 키가 `keyPrefix` 자신이거나 `keyPrefix + "/"` 로 시작하는 엔트리를 전부 삭제(같은 접두어를 가진 형제 경로 오삭제 방지). 하나라도 지웠으면 `true`, 없으면 `false`. 디렉터리 트리 통째 삭제에.
70
+ - `listChildren(prefix: string): Promise<{ name: string; isDirectory: boolean }[]>` — `prefix` 직계 자식만 집계. 키에서 `prefix` 제거한 나머지의 첫 세그먼트를 이름으로 삼고, 하위 세그먼트가 더 있거나 엔트리 `kind === "dir"` 면 디렉터리로 판정(중복 세그먼트는 Map 으로 1회만). 디렉터리 목록 표시용(재귀 아님).
71
71
  - 반환 항목 `name: string` — 직계 자식 이름(첫 경로 세그먼트).
72
72
  - 반환 항목 `isDirectory: boolean` — 디렉터리 여부.
73
- - `ensureDir(fullKeyBuilder, dirPath): Promise<void>` — `dirPath` 상의 각 중간 디렉터리를 부모부터 누적 경로마다 없으면 생성. `dirPath === "/"` 면 루트 1건만 생성. 단일 `withStore("readwrite")` 트랜잭션으로 처리(원자적). 파일 쓰기 전 상위 디렉터리 보장에.
73
+ - `ensureDir(fullKeyBuilder: (path: string) => string, dirPath: string): Promise<void>` — `dirPath` 상의 각 중간 디렉터리를 부모부터 누적 경로마다 없으면(`store.get` 으로 확인) 생성. `dirPath === "/"` 면 루트 1건만 생성. 전체를 단일 `withStore("readwrite")` 트랜잭션으로 처리(원자적). 파일 쓰기 전 상위 디렉터리 보장에.
74
74
  - `fullKeyBuilder: (path: string) => string` — 누적 경로(예: `/a`, `/a/b`)를 실제 저장 key 로 변환하는 콜백.
75
75
  - `dirPath: string` — 보장할 디렉터리 경로(`/` 구분). 빈 세그먼트는 무시.
76
76
 
@@ -1,37 +1,37 @@
1
1
  # @simplysm/core-common
2
2
 
3
- 브라우저·Node 공용 기반 유틸. 날짜/시간 값 타입, 에러 클래스, 배열/Set/Map 확장, 객체 조작, 직렬화(json/xml/bytes/transfer), 비동기 큐·이벤트·대기, 로거·환경변수·원시타입 매핑을 제공.
3
+ 브라우저·Node 공용 기반 유틸. 날짜/시간 값 타입, 에러 클래스, 배열/Set/Map 프로토타입 확장, 객체 조작, 직렬화(json/xml/bytes/transfer), 비동기 큐·이벤트·대기, 문자열/숫자/경로 유틸, 로거·환경변수·원시타입 매핑을 제공.
4
4
 
5
5
  ## 사용 트리거 인덱스
6
6
 
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)
7
+ - **에러 클래스 (SdError·ArgumentError·NotImplementedError·TimeoutError)** — 원인 체인을 묶은 에러를 throw 하거나 `err.message` 안전 추출할 때. 자세히: [errors.md](./errors.md)
8
+ - **날짜/시간 값 타입 (DateTime·DateOnly·Time·Uuid)** — 불변 날짜/시간/식별자 값을 만들고 파싱·산술·포맷·주차계산할 때. 자세히: [value-types.md](./value-types.md)
9
+ - **배열/Set/Map 확장** — `arr.single/groupBy/toMap/toTree/distinct/orderBy/diffs/oneWayDiffs/merge` 등 프로토타입 확장 메서드와 `Set.adds/toggle`, `Map.getOrCreate/update` 를 쓸 때. 자세히: [collection-ext.md](./collection-ext.md)
10
+ - **객체 조작 (obj 네임스페이스)** — `obj.clone/equal/merge/merge3/pick/omit/getChainValue/keys/entries/map` 등 깊은 복사·비교·병합·체인 접근이 필요할 때. 자세히: [obj.md](./obj.md)
11
11
  - **직렬화 (json·xml·bytes·transfer)** — 커스텀 타입(DateTime/Uuid/Map/Error 등) 포함 객체를 JSON/XML 문자열, hex/base64, Worker 전송 형태로 변환할 때. 자세히: [serialization.md](./serialization.md)
12
12
  - **비동기 런타임 (큐·이벤트·대기·LazyGcMap)** — `DebounceQueue`/`SerialQueue` 로 호출 흐름을 제어하거나, `EventEmitter` 로 타입 안전 이벤트를 다루거나, `wait.until/time`, 자동 만료 Map 이 필요할 때. 자세히: [async-runtime.md](./async-runtime.md)
13
13
  - **str (문자열 유틸)** — 한국어 조사 선택, 전각→반각, 케이스 변환, 빈문자열 판별, 삽입. (아래 인라인)
14
14
  - **num (숫자 유틸)** — 비숫자 제거 후 정수/실수 파싱, 0/null 판별, 천단위 포맷. (아래 인라인)
15
15
  - **path (POSIX 경로 유틸)** — 브라우저용 join/basename/extname. (아래 인라인)
16
- - **dt (날짜 포맷 헬퍼)** — `DateTime`/`DateOnly`/`Time` 의 `toFormatString` 이 내부적으로 쓰는 C# 스타일 포맷 문자열 변환. (아래 인라인)
17
16
  - **primitive (원시타입 추론)** — 런타임 값에서 `PrimitiveTypeStr` 추론. (아래 인라인)
18
17
  - **template-strings (코드 하이라이팅 태그)** — `js/ts/html/tsql/mysql/pgsql` 템플릿 태그로 들여쓰기 정규화. (아래 인라인)
19
- - **ZipArchive** — ZIP 바이트를 읽고 파일 단위로 추출·추가·재압축할 때. (아래 인라인)
20
18
  - **env / createLogger** — 환경변수 읽기·쓰기, 모듈 레벨 안전 lazy 로거 생성. (아래 인라인)
21
19
  - **공용 타입 (common.types)** — `PrimitiveType`/`PrimitiveTypeStr`/`PrimitiveTypeMap`/`Bytes`/`DeepPartial`/`Type`. (아래 인라인)
22
20
 
21
+ > `dt` (날짜 포맷 헬퍼) 네임스페이스도 `import { dt }` 로 노출되지만, 보통 `DateTime`/`DateOnly`/`Time` 의 `toFormatString` 을 통해 간접 사용한다. 포맷 토큰 표는 [value-types.md](./value-types.md) 참조.
22
+
23
23
  ## str (문자열 유틸)
24
24
 
25
25
  `import { str } from "@simplysm/core-common"` 네임스페이스.
26
26
 
27
- - `getKoreanSuffix(text: string, type: "을"|"은"|"이"|"와"|"랑"|"로"|"라"): string` — 마지막 글자 받침 유무로 한국어 조사를 선택. type 은 조사 쌍 식별자: `"을"`=을/를, `"은"`=은/는, `"이"`=이/가, `"와"`=과/와, `"랑"`=이랑/랑, `"로"`=으로/로(받침이 ㄹ이면 "로"), `"라"`=이라/라. 한글이 아니거나 빈 문자열이면 받침 없는 형태 반환. 동적 메시지 조립 시 사용.
28
- - `replaceFullWidth(str: string): string` — 전각 영문·숫자·공백·괄호를 반각으로 치환. 외부 입력(엑셀·스캐너) 정규화 시.
27
+ - `getKoreanSuffix(text: string, type: "을"|"은"|"이"|"와"|"랑"|"로"|"라"): string` — 마지막 글자 받침 유무로 한국어 조사를 선택. type 은 조사 쌍 식별자: `"을"`=을/를, `"은"`=은/는, `"이"`=이/가, `"와"`=과/와, `"랑"`=이랑/랑, `"로"`=으로/로(받침이 ㄹ이면 "로"), `"라"`=이라/라. 한글(0xAC00~0xD7A3)이 아니거나 빈 문자열이면 받침 없는 형태 반환. 동적 메시지 조립 시 사용.
28
+ - `replaceFullWidth(str: string): string` — 전각 영문(A-Z/a-z)·숫자(0-9)·공백·괄호(())를 반각으로 치환. 외부 입력(엑셀·스캐너) 정규화 시.
29
29
  - `toPascalCase(str: string): string` — `-`/`_`/`.` 구분자 뒤 글자와 첫 글자를 대문자화. 식별자 변환 시.
30
30
  - `toCamelCase(str: string): string` — 구분자 뒤 글자는 대문자화하되 첫 글자는 소문자화.
31
31
  - `toKebabCase(str: string): string` — 대문자 앞에 `-` 삽입 후 소문자화. 연속 대문자도 글자별 분리(`XMLParser`→`x-m-l-parser`), 기존 `-`/`_` 구분자는 유지.
32
32
  - `toSnakeCase(str: string): string` — `toKebabCase` 와 동일 규칙이되 구분자가 `_`.
33
33
  - `isNullOrEmpty(str: string | undefined): str is "" | undefined` — null/undefined/빈문자열이면 true 인 타입 가드. else 분기에서 비어있지 않은 string 으로 좁혀짐.
34
- - `insert(str: string, index: number, insertString: string): string` — index 위치에 문자열 삽입한 새 문자열.
34
+ - `insert(str: string, index: number, insertString: string): string` — index 위치(0 기준)에 문자열 삽입한 새 문자열.
35
35
 
36
36
  ```ts
37
37
  import { str } from "@simplysm/core-common";
@@ -40,13 +40,13 @@ const label = "사과" + str.getKoreanSuffix("사과", "을") + " 담았습니
40
40
 
41
41
  ## num (숫자 유틸)
42
42
 
43
- `import { num } from "@simplysm/core-common"` 네임스페이스. 파싱 계열은 비숫자 문자(0-9·`-`·`.` 외)를 먼저 제거하므로 `"010-1234"` 같은 입력도 받음(선행 `-`만 부호로 유지).
43
+ `import { num } from "@simplysm/core-common"` 네임스페이스. 파싱 계열은 비숫자 문자(0-9·`-`·`.` 외)를 먼저 제거하므로 `"010-1234"` 같은 입력도 받음(선행 `-`만 부호로 유지, 중간 `-` 는 제거).
44
44
 
45
- - `parseInt(text: unknown): number | undefined` — 정수 파싱. number 면 `Math.trunc`, 소수 문자열이면 정수부만, 파싱 불가면 undefined.
45
+ - `parseInt(text: unknown): number | undefined` — 정수 파싱. number 면 `Math.trunc`, 소수 문자열이면 정수부만, 문자열·`"-"`·파싱 불가면 undefined.
46
46
  - `parseFloat(text: unknown): number | undefined` — 실수 파싱. number 는 그대로, 파싱 불가면 undefined.
47
47
  - `parseRoundedInt(text: unknown): number | undefined` — `parseFloat` 후 `Math.round`. 반올림 정수가 필요할 때.
48
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 반환.
49
+ - `format(val: number | undefined, digit?: { max?: number; min?: number }): string | undefined` — `toLocaleString` 기반 천단위 구분자 포맷. `max`=최대 소수 자릿수, `min`=최소 소수 자릿수(부족분 0 채움). val 이 undefined 면 undefined 반환.
50
50
 
51
51
  ```ts
52
52
  import { num } from "@simplysm/core-common";
@@ -56,29 +56,21 @@ num.parseInt("010-1234-5678"); // 1012345678
56
56
 
57
57
  ## path (POSIX 경로 유틸)
58
58
 
59
- `import { path } from "@simplysm/core-common"` 네임스페이스. POSIX 슬래시 경로만 지원(Windows 백슬래시 미지원). 브라우저·Capacitor 환경용 Node `path` 대체.
59
+ `import { path } from "@simplysm/core-common"` 네임스페이스. POSIX 슬래시(`/`) 경로만 지원(Windows 백슬래시 미지원). 브라우저·Capacitor 환경용 Node `path` 대체.
60
60
 
61
- - `join(...segments: string[]): string` — 슬래시로 결합하며 중간 세그먼트의 앞뒤 슬래시·빈 세그먼트 제거.
61
+ - `join(...segments: string[]): string` — 슬래시로 결합하며 세그먼트는 끝 슬래시만, 이후 세그먼트는 앞뒤 슬래시·빈 세그먼트 제거.
62
62
  - `basename(filePath: string, ext?: string): string` — 마지막 세그먼트 추출. `ext` 가 끝과 일치하면 제거.
63
- - `extname(filePath: string): string` — 마지막 `.` 이후 확장자(`.` 포함). 숨김파일(`.gitignore`)은 빈 문자열.
64
-
65
- ## dt (날짜 포맷 헬퍼)
66
-
67
- `import { dt } from "@simplysm/core-common"` 네임스페이스. 보통 `DateTime`/`DateOnly`/`Time` 의 `toFormatString` 을 통해 간접 사용.
68
-
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)로 변환.
63
+ - `extname(filePath: string): string` — 마지막 `.` 이후 확장자(`.` 포함). 숨김파일(`.gitignore` 처럼 앞 `.`)은 빈 문자열.
72
64
 
73
65
  ## primitive (원시타입 추론)
74
66
 
75
67
  `import { primitive } from "@simplysm/core-common"` 네임스페이스.
76
68
 
77
- - `typeStr(value): PrimitiveTypeStr` — 런타임 값에서 `"string"|"number"|"boolean"|"DateTime"|"DateOnly"|"Time"|"Uuid"|"Bytes"` 중 해당 문자열 반환. 지원하지 않는 타입이면 `ArgumentError` throw. ORM/직렬화에서 값 타입을 문자열 키로 다룰 때.
69
+ - `typeStr(value): PrimitiveTypeStr` — 런타임 값에서 `"string"|"number"|"boolean"|"DateTime"|"DateOnly"|"Time"|"Uuid"|"Bytes"`(Uint8Array→`"Bytes"`) 중 해당 문자열 반환. 지원하지 않는 타입이면 `ArgumentError` throw. ORM/직렬화에서 값 타입을 문자열 키로 다룰 때.
78
70
 
79
71
  ## template-strings (코드 하이라이팅 태그)
80
72
 
81
- `import { js, ts, html, tsql, mysql, pgsql } from "@simplysm/core-common"`. 모두 동일 동작 — 보간값을 문자열로 합친 뒤 공통 최소 들여쓰기를 제거하고 앞뒤 빈 줄을 잘라냄. 함수별 차이는 IDE 하이라이팅 언어 힌트뿐(js/ts/html/T-SQL/MySQL/PostgreSQL). 런타임 검증·이스케이프는 하지 않음.
73
+ `import { js, ts, html, tsql, mysql, pgsql } from "@simplysm/core-common"`. 모두 동일 동작 — 보간값을 문자열로 합친 뒤 공통 최소 들여쓰기를 제거하고 앞뒤 빈 줄을 잘라냄(보간값이 null/undefined 면 빈 문자열). 함수별 차이는 IDE 하이라이팅 언어 힌트뿐(js/ts/html/T-SQL/MySQL/PostgreSQL). 런타임 검증·이스케이프는 하지 않음.
82
74
 
83
75
  ```ts
84
76
  import { ts } from "@simplysm/core-common";
@@ -89,27 +81,6 @@ const code = ts`
89
81
  `; // 앞 공통 들여쓰기 제거된 문자열
90
82
  ```
91
83
 
92
- ## ZipArchive
93
-
94
- `import { ZipArchive } from "@simplysm/core-common"`. `@zip.js/zip.js` 래퍼. 읽기/쓰기를 한 인스턴스로 다루며 추출 결과를 내부 캐시에 보관. 사용 후 `close()` 필수.
95
-
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>` — 리더 닫고 캐시 비움.
103
-
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
- ```
112
-
113
84
  ## env / createLogger
114
85
 
115
86
  `import { env, parseBoolEnv, createLogger } from "@simplysm/core-common"`.
@@ -117,7 +88,7 @@ try {
117
88
  - `env(key: string): string | undefined` — 환경변수 읽기. `process.env` 우선, 없으면 `import.meta.env` fallback(Node 에선 보통 undefined).
118
89
  - `env(key: string, value: string): void` — `process.env[key]` 에 쓰기(process 없는 런타임이면 무시).
119
90
  - `parseBoolEnv(value: unknown): boolean` — `"true"/"1"/"yes"/"on"`(대소문자 무시)이면 true, 그 외 false. 환경변수 boolean 해석 시.
120
- - `createLogger(tag: string): ConsolaInstance` — consola 태그 로거를 첫 메서드 접근 시점까지 지연 생성하는 Proxy. 모듈 레벨에서 선언해도 이후 `setupConsola` 의 level/reporters 변경이 반영됨. 모듈 최상단에 로거를 두고 싶을 때 `consola.withTag()` 직접 호출 대신 사용.
91
+ - `createLogger(tag: string): ConsolaInstance` — consola 태그 로거를 첫 메서드 접근 시점까지 지연 생성하는 Proxy. 모듈 레벨에서 선언해도 이후 `setupConsola` 의 level/reporters 변경이 child 인스턴스에 반영됨. `consola.withTag()` 직접 호출 대신 사용(로깅 표준 매뉴얼 강제). tag 형식은 `<도메인>:<역할>` 또는 단일 토큰.
121
92
 
122
93
  ```ts
123
94
  import { createLogger, env, parseBoolEnv } from "@simplysm/core-common";
@@ -134,4 +105,4 @@ if (parseBoolEnv(env("DEV"))) logger.debug("dev mode");
134
105
  - `PrimitiveTypeStr = keyof PrimitiveTypeMap` — 위 매핑의 key 유니온.
135
106
  - `PrimitiveType = PrimitiveTypeMap[PrimitiveTypeStr] | undefined` — 모든 원시 값 유니온(undefined 포함).
136
107
  - `DeepPartial<TObject>` — 원시타입은 그대로 두고 객체/배열만 재귀적으로 optional 화. 부분 패치 입력 타입에.
137
- - `Type<TInstance>` — `new (...args) => TInstance` 생성자 타입. 팩토리·DI·`instanceof` 분기에서 클래스를 값으로 받을 때.
108
+ - `Type<TInstance>` — `new (...args: unknown[]) => TInstance` 생성자 타입. 팩토리·DI·`instanceof` 분기에서 클래스를 값으로 받을 때.
@@ -1,19 +1,21 @@
1
- # @simplysm/core-common — 비동기 런타임 (큐·이벤트·대기·LazyGcMap)
1
+ # @simplysm/core-common — async-runtime
2
2
 
3
- 호출 흐름 제어(디바운스/직렬 큐), 타입 안전 이벤트(EventEmitter), 대기 헬퍼(wait), 자동 만료 Map(LazyGcMap). 비동기 작업 조율·이벤트 배선·타이머 자원 정리가 필요할 때 함께 참조. 큐와 EventEmitter `dispose()` 로 자원을 정리해야 함.
3
+ 호출 흐름 제어(디바운스/직렬 큐), 타입 안전 이벤트(`EventEmitter`), 대기 헬퍼(`wait`), 자동 만료 Map(`LazyGcMap`). 큐와 `EventEmitter`/`LazyGcMap` `dispose()`(또는 LazyGcMap 은 `dispose`)타이머·리스너 자원을 정리해야 함. `import { DebounceQueue, SerialQueue, EventEmitter, LazyGcMap, wait } from "@simplysm/core-common"`.
4
+
5
+ 큐 실행 중 발생한 에러는 `"error"` 이벤트로 발행되며, 리스너가 없으면 `createLogger` 로 로그 출력된다.
4
6
 
5
7
  ## EventEmitter
6
8
 
7
9
  ```ts
8
- class EventEmitter<TEvents extends { [K in keyof TEvents]: unknown } = Record<string, unknown>> {}
10
+ class EventEmitter<TEvents extends Record<keyof TEvents, unknown> = Record<string, unknown>>
9
11
  ```
10
12
 
11
13
  `EventTarget` 기반 타입 안전 이벤트 이미터(브라우저·Node 공용). `TEvents` 는 이벤트명→데이터 타입 맵.
12
14
 
13
- - `on(type, listener: (data: TEvents[type]) => void): void` — 리스너 등록(같은 리스너 중복 등록은 무시).
15
+ - `on(type, listener: (data) => void): void` — 리스너 등록. 같은 이벤트에 같은 리스너 중복 등록은 무시.
14
16
  - `off(type, listener): void` — 리스너 제거.
15
- - `emit(type, ...args): void` — 발행. 데이터 타입이 `void` 면 인자 없이 호출.
16
- - `listenerCount(type): number` — 해당 이벤트의 리스너 수.
17
+ - `emit(type, ...args): void` — 발행. 데이터 타입이 `void` 면 인자 생략, 아니면 데이터 1개 전달.
18
+ - `listenerCount(type): number` — 해당 이벤트의 등록 리스너 수.
17
19
  - `dispose(): void` — 모든 리스너 제거.
18
20
 
19
21
  ```ts
@@ -22,89 +24,98 @@ class MyEmitter extends EventEmitter<MyEvents> {}
22
24
  const em = new MyEmitter();
23
25
  em.on("data", (d) => console.log(d)); // d: string
24
26
  em.emit("data", "hello");
25
- em.emit("done"); // void 는 인자 없이
27
+ em.emit("done"); // void 는 인자 없이
26
28
  ```
27
29
 
28
30
  ## DebounceQueue
29
31
 
30
32
  ```ts
31
- class DebounceQueue extends EventEmitter<{ error: SdError }> {
32
- constructor(delay?: number);
33
- }
33
+ class DebounceQueue extends EventEmitter<{ error: SdError }>
34
+ constructor(delay?: number)
34
35
  ```
35
36
 
36
- 연속 호출 **마지막 요청만** 실행하는 디바운스 큐. 입력 자동완성·연속 상태 변경 일괄 처리에.
37
+ 짧은 시간 내 여러 호출 **마지막 요청만** 실행, 이전 요청은 무시.
37
38
 
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
39
+ - `new DebounceQueue(delay?)` — `delay`(ms) 생략 시 다음 이벤트 루프에 즉시 실행.
40
+ - `run(fn: () => void | Promise<void>): void` — 큐에 함수 등록(이전 대기 함수는 교체). 실행 중에 들어온 요청은 디바운스 지연 없이 현재 실행 직후 즉시 처리(의도적 설계 — 실행 중 도착 요청 누락 방지).
41
+ - `dispose(): void` — 대기 함수·타이머 정리 후 상위 `EventEmitter.dispose` 호출.
42
+ - `"error"` 이벤트(`SdError`) `run` 으로 실행한 함수가 throw 하면 발행. 리스너 없으면 로그.
44
43
 
45
44
  ```ts
46
- class SerialQueue extends EventEmitter<{ error: SdError }> {
47
- constructor(gap?: number);
48
- }
45
+ const q = new DebounceQueue(300);
46
+ q.on("error", (e) => console.error(e));
47
+ input.addEventListener("input", () => q.run(() => search(input.value)));
49
48
  ```
50
49
 
51
- 큐에 등록된 함수를 **순차 실행**(이전 완료 후 다음). 에러가 나도 후속 작업은 계속.
52
-
53
- - `constructor(gap = 0)` — 각 작업 사이 간격(ms).
54
- - `run(fn: () => void | Promise<void>): void` — 큐에 추가하고 실행 시작.
55
- - `dispose(): void` — 대기 큐 비움(실행 중 작업은 완료됨, EventEmitter dispose 포함).
56
- - 작업 throw 시 `"error"` 리스너가 있으면 `SdError` emit, 없으면 내부 로거 출력.
50
+ ## SerialQueue
57
51
 
58
52
  ```ts
59
- const q = new SerialQueue();
60
- q.run(async () => { await save(a); });
61
- q.run(async () => { await save(b); }); // a 완료 후 실행
53
+ class SerialQueue extends EventEmitter<{ error: SdError }>
54
+ constructor(gap?: number)
62
55
  ```
63
56
 
64
- ## wait (`import { wait } from "@simplysm/core-common"`)
57
+ 등록된 함수를 **순차** 실행( 작업 완료 다음 시작). 한 작업이 throw 해도 후속 작업은 계속 실행.
65
58
 
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 화).
59
+ - `new SerialQueue(gap?)` — `gap`(ms, 기본 0) 작업 사이 간격(`wait.time` 으로 대기).
60
+ - `run(fn: () => void | Promise<void>): void` 큐에 추가하고 처리 시작.
61
+ - `dispose(): void` — 대기 큐 비움(실행 중 작업은 완료됨) 후 상위 `EventEmitter.dispose`.
62
+ - `"error"` 이벤트(`SdError`) — 작업 throw 시 발행. 리스너 없으면 로그.
68
63
 
69
64
  ```ts
70
- import { wait } from "@simplysm/core-common";
71
- await wait.until(() => isReady, 100, 50); // 100ms 간격, 50회 초과 시 TimeoutError
72
- await wait.time(300);
65
+ const q = new SerialQueue();
66
+ q.run(async () => { await save(1); });
67
+ q.run(async () => { await save(2); }); // 1 완료 후 실행
73
68
  ```
74
69
 
75
70
  ## LazyGcMap
76
71
 
77
72
  ```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
- }
73
+ class LazyGcMap<TKey, TValue>
74
+ constructor(options: {
75
+ gcInterval?: number;
76
+ expireTime: number;
77
+ onExpire?: (key: TKey, value: TValue) => void | Promise<void>;
78
+ })
85
79
  ```
86
80
 
87
- LRU 접근 시간 기반 자동 만료 Map. 지정 시간 동안 접근 없으면 GC 타이머가 삭제. 캐시·세션 보관에. **사용 후 `dispose()` 필수**(아니면 GC 타이머가 계속 돌아 메모리 누수).
81
+ LRU 방식 자동 만료 Map. 접근 마지막 접근 시간을 갱신하고, `expireTime`(ms) 동안 접근 없으면 자동 삭제. **사용 후 반드시 `dispose()` 호출**( 하면 GC 타이머가 계속 돌아 메모리 누수).
82
+
83
+ 옵션:
88
84
 
89
- 생성자 옵션:
90
- - `expireTime: number` — 마지막 접근 ms 지나면 만료(필수).
91
- - `gcInterval?: number` GC 주기(ms). 생략`expireTime/10`(최소 1000).
92
- - `onExpire?: (key, value) => void | Promise<void>` — 만료 시 콜백(비동기 가능). 콜백 throw 시 로그 출력 후 계속 진행.
85
+ - `expireTime: number` — 만료 시간(ms). 마지막 접근 후 이 시간 경과 시 삭제.
86
+ - `gcInterval?: number` — GC 주기(ms). 기본값 `max(expireTime / 10, 1000)`.
87
+ - `onExpire?: (key, value) => void | Promise<void>` — 만료 콜백. 비동기 가능, 콜백이 throw 하면 로그 후 계속.
93
88
 
94
89
  메서드:
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` 되면 재등록 항목은 삭제하지 않음(항목 참조 동일성으로 판정).
90
+
91
+ - `size` — 항목 수.
92
+ - `has(key)` — 존재 여부(접근 시간 갱신 함).
93
+ - `get(key): TValue | undefined` — 조회(접근 시간 갱신, LRU).
94
+ - `set(key, value): void` — 저장(GC 타이머 시작).
95
+ - `delete(key): boolean` 삭제(비면 타이머 중지).
96
+ - `getOrCreate(key, factory): TValue` — 없으면 `factory()` 로 생성·저장. dispose 후 호출하면 `Error`.
97
+ - `clear(): void` — 전부 삭제(인스턴스는 재사용 가능).
98
+ - `dispose(): void` — 타이머 중지 + 데이터 삭제(인스턴스 사용 종료).
99
+ - `values()/keys()/entries()` — Iterator.
101
100
 
102
101
  ```ts
103
102
  const cache = new LazyGcMap<string, Session>({ expireTime: 60000 });
104
103
  try {
105
- cache.set("u1", session);
106
- const s = cache.getOrCreate("u2", () => loadSession("u2"));
104
+ cache.set("sid", session);
105
+ cache.getOrCreate("sid2", () => createSession());
107
106
  } finally {
108
107
  cache.dispose();
109
108
  }
110
109
  ```
110
+
111
+ ## wait
112
+
113
+ `import { wait } from "@simplysm/core-common"` 네임스페이스.
114
+
115
+ - `wait.until(forwarder: () => boolean | Promise<boolean>, milliseconds?, maxCount?): Promise<void>` — 조건이 true 가 될 때까지 대기. `milliseconds`(기본 100) 간격으로 재확인. 첫 호출에서 true 면 즉시 반환. `maxCount` 지정 시 그 횟수 초과하면 `TimeoutError(count)` throw(미지정이면 무제한).
116
+ - `wait.time(millisecond: number): Promise<void>` — 지정 시간(ms) 대기(`setTimeout` 래퍼).
117
+
118
+ ```ts
119
+ await wait.until(() => isReady, 100, 50); // 100ms 간격, 최대 50회
120
+ await wait.time(500);
121
+ ```