@simplysm/sd-claude 14.0.47 → 14.0.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{claude/references/sd-simplysm14/sd-claude/usage.md → README.md} +2 -2
- package/claude/rules/sd-claude-rules.md +25 -10
- package/claude/rules/sd-options.md +11 -6
- package/claude/sd-subagent-start.sh +6 -0
- package/claude/settings.json +1 -12
- package/claude/skills/sd-check/SKILL.md +43 -12
- package/claude/skills/sd-claude-docs/SKILL.md +30 -58
- package/claude/skills/sd-claude-docs/references/package-claudemd.md +12 -0
- package/claude/skills/sd-claude-docs/references/package-doc-gen.md +26 -13
- package/claude/skills/sd-commit/SKILL.md +1 -1
- package/claude/skills/sd-debug/SKILL.md +5 -3
- package/claude/skills/sd-deliverable/SKILL.md +1 -1
- package/claude/skills/sd-dev/SKILL.md +14 -9
- package/claude/skills/sd-doc-extract/SKILL.md +8 -10
- package/claude/skills/sd-doc-extract/_common.py +8 -1
- package/claude/skills/sd-doc-extract/_extract_docx.py +74 -34
- package/claude/skills/sd-doc-extract/_extract_pdf.py +12 -1
- package/claude/skills/sd-doc-extract/_extract_pptx.py +103 -23
- package/claude/skills/sd-doc-extract/_extract_xlsb.py +93 -4
- package/claude/skills/sd-doc-extract/_extract_xlsx.py +98 -36
- package/claude/skills/sd-doc-extract/extract.py +22 -3
- package/claude/skills/sd-inner-clarify/SKILL.md +78 -0
- package/claude/skills/sd-inner-debug/SKILL.md +1 -1
- package/claude/skills/sd-inner-review/SKILL.md +13 -0
- package/claude/skills/sd-issue/SKILL.md +1 -1
- package/claude/skills/sd-outlook/SKILL.md +1 -1
- package/claude/skills/sd-plan/SKILL.md +50 -17
- package/claude/skills/sd-prompt/SKILL.md +180 -178
- package/claude/skills/sd-prompt/references/eval-runner.md +5 -30
- package/claude/skills/sd-prompt/references/sd-eval-env-template.md +23 -0
- package/claude/skills/sd-refactor/SKILL.md +2 -2
- package/claude/skills/sd-tdd/SKILL.md +45 -16
- package/claude/skills/sd-use/SKILL.md +84 -80
- package/claude/skills/sd-wbs/SKILL.md +84 -27
- package/{claude/references/sd-simplysm14/sd-claude/docs → docs}/assets.md +2 -3
- package/{claude/references/sd-simplysm14/sd-claude/docs → docs}/hooks.md +7 -6
- package/{claude/references/sd-simplysm14/sd-claude/docs → docs}/scripts.md +1 -9
- package/package.json +3 -2
- package/scripts/sync.mjs +4 -2
- package/claude/references/sd-simplysm14/angular/docs/bootstrap.md +0 -48
- package/claude/references/sd-simplysm14/angular/docs/directives.md +0 -236
- package/claude/references/sd-simplysm14/angular/docs/features.md +0 -379
- package/claude/references/sd-simplysm14/angular/docs/pipes.md +0 -32
- package/claude/references/sd-simplysm14/angular/docs/plugins.md +0 -37
- package/claude/references/sd-simplysm14/angular/docs/provider-types.md +0 -283
- package/claude/references/sd-simplysm14/angular/docs/providers.md +0 -379
- package/claude/references/sd-simplysm14/angular/docs/styling.md +0 -222
- package/claude/references/sd-simplysm14/angular/docs/type-utilities.md +0 -250
- package/claude/references/sd-simplysm14/angular/docs/ui-data.md +0 -275
- package/claude/references/sd-simplysm14/angular/docs/ui-form.md +0 -490
- package/claude/references/sd-simplysm14/angular/docs/ui-layout.md +0 -140
- package/claude/references/sd-simplysm14/angular/docs/ui-navigation.md +0 -273
- package/claude/references/sd-simplysm14/angular/docs/ui-overlay.md +0 -157
- package/claude/references/sd-simplysm14/angular/docs/ui-visual.md +0 -127
- package/claude/references/sd-simplysm14/angular/docs/utils.md +0 -295
- package/claude/references/sd-simplysm14/angular/usage.md +0 -489
- package/claude/references/sd-simplysm14/capacitor-plugin-auto-update/usage.md +0 -182
- package/claude/references/sd-simplysm14/capacitor-plugin-file-system/docs/file-operations.md +0 -154
- package/claude/references/sd-simplysm14/capacitor-plugin-file-system/docs/permissions.md +0 -84
- package/claude/references/sd-simplysm14/capacitor-plugin-file-system/docs/storage-paths.md +0 -107
- package/claude/references/sd-simplysm14/capacitor-plugin-file-system/docs/types.md +0 -83
- package/claude/references/sd-simplysm14/capacitor-plugin-file-system/usage.md +0 -133
- package/claude/references/sd-simplysm14/capacitor-plugin-intent/usage.md +0 -203
- package/claude/references/sd-simplysm14/capacitor-plugin-usb-storage/usage.md +0 -258
- package/claude/references/sd-simplysm14/core-browser/usage.md +0 -306
- package/claude/references/sd-simplysm14/core-common/docs/errors.md +0 -82
- package/claude/references/sd-simplysm14/core-common/docs/extensions.md +0 -167
- package/claude/references/sd-simplysm14/core-common/docs/features.md +0 -136
- package/claude/references/sd-simplysm14/core-common/docs/types.md +0 -245
- package/claude/references/sd-simplysm14/core-common/docs/utils.md +0 -591
- package/claude/references/sd-simplysm14/core-common/usage.md +0 -255
- package/claude/references/sd-simplysm14/core-node/docs/child-process.md +0 -182
- package/claude/references/sd-simplysm14/core-node/docs/features.md +0 -214
- package/claude/references/sd-simplysm14/core-node/docs/file-system.md +0 -509
- package/claude/references/sd-simplysm14/core-node/docs/file-watching.md +0 -139
- package/claude/references/sd-simplysm14/core-node/docs/logging.md +0 -180
- package/claude/references/sd-simplysm14/core-node/docs/path.md +0 -176
- package/claude/references/sd-simplysm14/core-node/docs/utilities-cpx.md +0 -194
- package/claude/references/sd-simplysm14/core-node/docs/utilities-fsx.md +0 -469
- package/claude/references/sd-simplysm14/core-node/docs/utilities-pathx.md +0 -151
- package/claude/references/sd-simplysm14/core-node/docs/worker-threads.md +0 -334
- package/claude/references/sd-simplysm14/core-node/docs/worker.md +0 -205
- package/claude/references/sd-simplysm14/core-node/usage.md +0 -259
- package/claude/references/sd-simplysm14/excel/docs/core-classes.md +0 -453
- package/claude/references/sd-simplysm14/excel/docs/types.md +0 -459
- package/claude/references/sd-simplysm14/excel/docs/utilities.md +0 -194
- package/claude/references/sd-simplysm14/excel/docs/wrapper.md +0 -73
- package/claude/references/sd-simplysm14/excel/usage.md +0 -134
- package/claude/references/sd-simplysm14/lint/usage.md +0 -130
- package/claude/references/sd-simplysm14/orm-common/docs/core.md +0 -188
- package/claude/references/sd-simplysm14/orm-common/docs/expression.md +0 -190
- package/claude/references/sd-simplysm14/orm-common/docs/models.md +0 -17
- package/claude/references/sd-simplysm14/orm-common/docs/query-builder.md +0 -97
- package/claude/references/sd-simplysm14/orm-common/docs/queryable-executable.md +0 -250
- package/claude/references/sd-simplysm14/orm-common/docs/schema-builders.md +0 -364
- package/claude/references/sd-simplysm14/orm-common/docs/types.md +0 -522
- package/claude/references/sd-simplysm14/orm-common/usage.md +0 -229
- package/claude/references/sd-simplysm14/orm-node/docs/connections.md +0 -137
- package/claude/references/sd-simplysm14/orm-node/docs/core.md +0 -131
- package/claude/references/sd-simplysm14/orm-node/docs/types.md +0 -173
- package/claude/references/sd-simplysm14/orm-node/usage.md +0 -143
- package/claude/references/sd-simplysm14/sd-cli/usage.md +0 -782
- package/claude/references/sd-simplysm14/service-client/docs/features.md +0 -217
- package/claude/references/sd-simplysm14/service-client/docs/main.md +0 -148
- package/claude/references/sd-simplysm14/service-client/docs/protocol.md +0 -53
- package/claude/references/sd-simplysm14/service-client/docs/transport.md +0 -131
- package/claude/references/sd-simplysm14/service-client/docs/types.md +0 -129
- package/claude/references/sd-simplysm14/service-client/usage.md +0 -202
- package/claude/references/sd-simplysm14/service-common/docs/app-structure.md +0 -175
- package/claude/references/sd-simplysm14/service-common/docs/events.md +0 -64
- package/claude/references/sd-simplysm14/service-common/docs/protocol.md +0 -331
- package/claude/references/sd-simplysm14/service-common/docs/service-types.md +0 -90
- package/claude/references/sd-simplysm14/service-common/docs/types.md +0 -19
- package/claude/references/sd-simplysm14/service-common/usage.md +0 -154
- package/claude/references/sd-simplysm14/service-server/docs/auth.md +0 -64
- package/claude/references/sd-simplysm14/service-server/docs/core.md +0 -174
- package/claude/references/sd-simplysm14/service-server/docs/legacy.md +0 -25
- package/claude/references/sd-simplysm14/service-server/docs/main.md +0 -88
- package/claude/references/sd-simplysm14/service-server/docs/protocol.md +0 -33
- package/claude/references/sd-simplysm14/service-server/docs/services.md +0 -94
- package/claude/references/sd-simplysm14/service-server/docs/transport-http.md +0 -93
- package/claude/references/sd-simplysm14/service-server/docs/transport-socket.md +0 -119
- package/claude/references/sd-simplysm14/service-server/docs/types.md +0 -36
- package/claude/references/sd-simplysm14/service-server/docs/utils.md +0 -22
- package/claude/references/sd-simplysm14/service-server/usage.md +0 -171
- package/claude/references/sd-simplysm14/storage/usage.md +0 -301
- package/claude/references/sd-simplysm14.md +0 -35
- package/claude/rules/sd-clarify.md +0 -23
- package/claude/sd-session-start.sh +0 -10
- /package/{claude/references/sd-simplysm14/sd-claude/docs → docs}/cli.md +0 -0
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
# @simplysm/core-browser
|
|
2
|
-
|
|
3
|
-
브라우저 전용 유틸리티 패키지. DOM 프로토타입 확장, 파일 다운로드/업로드, HTTP fetch, IndexedDB 추상화를 제공한다.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @simplysm/core-browser
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## API Overview
|
|
12
|
-
|
|
13
|
-
### Extensions
|
|
14
|
-
|
|
15
|
-
사이드 이펙트 모듈로, 패키지를 임포트하면 자동으로 `Element`와 `HTMLElement` 프로토타입에 메서드가 추가된다.
|
|
16
|
-
|
|
17
|
-
| API | Type | Description |
|
|
18
|
-
|-----|------|-------------|
|
|
19
|
-
| `ElementBounds` | interface | 요소 경계 정보 (target, top, left, width, height) |
|
|
20
|
-
| `copyElement` | function | copy 이벤트 핸들러에서 요소 내 input/textarea 값을 클립보드에 복사 |
|
|
21
|
-
| `pasteToElement` | function | paste 이벤트 핸들러에서 클립보드 텍스트를 요소 내 input/textarea에 붙여넣기 |
|
|
22
|
-
| `getBounds` | function | IntersectionObserver로 여러 요소의 경계 정보를 비동기 조회 |
|
|
23
|
-
| `Element.prototype.findAll` | prototype extension | 선택자와 일치하는 모든 하위 요소 검색 |
|
|
24
|
-
| `Element.prototype.findFirst` | prototype extension | 선택자와 일치하는 첫 번째 요소 검색 |
|
|
25
|
-
| `Element.prototype.prependChild` | prototype extension | 요소를 첫 번째 자식으로 삽입 |
|
|
26
|
-
| `Element.prototype.getParents` | prototype extension | 모든 부모 요소를 가까운 순서로 조회 |
|
|
27
|
-
| `Element.prototype.findTabbableParent` | prototype extension | 첫 번째 탭 이동 가능한 부모 요소 검색 (tabbable 사용) |
|
|
28
|
-
| `Element.prototype.findFirstTabbableChild` | prototype extension | 첫 번째 탭 이동 가능한 자식 요소 검색 (tabbable 사용) |
|
|
29
|
-
| `Element.prototype.isOffsetElement` | prototype extension | position이 relative/absolute/fixed/sticky인지 확인 |
|
|
30
|
-
| `Element.prototype.isVisible` | prototype extension | 요소가 화면에 보이는지 확인 (clientRects, visibility, opacity) |
|
|
31
|
-
| `HTMLElement.prototype.repaint` | prototype extension | 강제 리페인트 트리거 (offsetHeight 접근) |
|
|
32
|
-
| `HTMLElement.prototype.getRelativeOffset` | prototype extension | 부모 요소 기준 상대 위치 계산 (CSS top/left 용) |
|
|
33
|
-
| `HTMLElement.prototype.scrollIntoViewIfNeeded` | prototype extension | offset 영역에 가려진 경우 대상이 보이도록 스크롤 |
|
|
34
|
-
|
|
35
|
-
#### `ElementBounds`
|
|
36
|
-
|
|
37
|
-
```typescript
|
|
38
|
-
export interface ElementBounds {
|
|
39
|
-
target: Element;
|
|
40
|
-
top: number;
|
|
41
|
-
left: number;
|
|
42
|
-
width: number;
|
|
43
|
-
height: number;
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
| Field | Type | Description |
|
|
48
|
-
|-------|------|-------------|
|
|
49
|
-
| `target` | `Element` | 측정 대상 요소 |
|
|
50
|
-
| `top` | `number` | 뷰포트 기준 상단 위치 |
|
|
51
|
-
| `left` | `number` | 뷰포트 기준 좌측 위치 |
|
|
52
|
-
| `width` | `number` | 요소 너비 |
|
|
53
|
-
| `height` | `number` | 요소 높이 |
|
|
54
|
-
|
|
55
|
-
#### `copyElement`
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
export function copyElement(event: ClipboardEvent): void
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
copy 이벤트 핸들러와 함께 사용한다. 이벤트 타겟 요소 내의 첫 번째 `input` 또는 `textarea`를 찾아 그 값을 클립보드에 설정한다. 해당 요소가 없으면 아무 동작도 하지 않는다.
|
|
62
|
-
|
|
63
|
-
#### `pasteToElement`
|
|
64
|
-
|
|
65
|
-
```typescript
|
|
66
|
-
export function pasteToElement(event: ClipboardEvent): void
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
paste 이벤트 핸들러와 함께 사용한다. 이벤트 타겟 요소 내의 첫 번째 `input` 또는 `textarea`를 찾아 값 전체를 클립보드 텍스트로 교체한다. 커서 위치나 선택 영역은 고려하지 않는다. `input` 이벤트를 dispatch한다.
|
|
70
|
-
|
|
71
|
-
#### `getBounds`
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
export async function getBounds(els: Element[], timeout?: number): Promise<ElementBounds[]>
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
IntersectionObserver를 사용하여 여러 요소의 경계 정보를 비동기로 조회한다. 중복 요소는 자동 제거되며, 입력 순서대로 결과가 정렬된다. `timeout` 기본값은 5000ms이며, 시간 내에 응답이 없으면 `TimeoutError`를 던진다.
|
|
78
|
-
|
|
79
|
-
#### Element Prototype Extensions
|
|
80
|
-
|
|
81
|
-
`Element.prototype`에 추가되는 메서드들:
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
// 선택자와 일치하는 모든 하위 요소 검색 (빈 선택자 -> 빈 배열)
|
|
85
|
-
element.findAll<TEl extends Element = Element>(selector: string): TEl[]
|
|
86
|
-
|
|
87
|
-
// 선택자와 일치하는 첫 번째 요소 (빈 선택자 -> undefined)
|
|
88
|
-
element.findFirst<TEl extends Element = Element>(selector: string): TEl | undefined
|
|
89
|
-
|
|
90
|
-
// 요소를 첫 번째 자식으로 삽입
|
|
91
|
-
element.prependChild<TEl extends Element>(child: TEl): TEl
|
|
92
|
-
|
|
93
|
-
// 모든 부모 요소 배열 (가까운 순서)
|
|
94
|
-
element.getParents(): Element[]
|
|
95
|
-
|
|
96
|
-
// 첫 번째 탭 이동 가능한 부모 요소 (tabbable 라이브러리 사용)
|
|
97
|
-
element.findTabbableParent(): HTMLElement | undefined
|
|
98
|
-
|
|
99
|
-
// 첫 번째 탭 이동 가능한 자식 요소 (tabbable 라이브러리 사용)
|
|
100
|
-
element.findFirstTabbableChild(): HTMLElement | undefined
|
|
101
|
-
|
|
102
|
-
// position이 relative/absolute/fixed/sticky인지 확인
|
|
103
|
-
element.isOffsetElement(): boolean
|
|
104
|
-
|
|
105
|
-
// 화면에 보이는지 확인 (clientRects, visibility, opacity)
|
|
106
|
-
element.isVisible(): boolean
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
#### HTMLElement Prototype Extensions
|
|
110
|
-
|
|
111
|
-
`HTMLElement.prototype`에 추가되는 메서드들:
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
// 강제 리페인트 (offsetHeight 접근으로 reflow 트리거)
|
|
115
|
-
htmlElement.repaint(): void
|
|
116
|
-
|
|
117
|
-
// 부모 요소 기준 상대 위치 계산 (CSS top/left에 사용 가능)
|
|
118
|
-
// parent: HTMLElement 또는 CSS 선택자 문자열
|
|
119
|
-
// border 두께, CSS transform 변환 포함
|
|
120
|
-
// 부모를 찾을 수 없으면 ArgumentError throw
|
|
121
|
-
htmlElement.getRelativeOffset(parent: HTMLElement | string): { top: number; left: number }
|
|
122
|
-
|
|
123
|
-
// offset 영역에 가려진 경우 스크롤 조정
|
|
124
|
-
// target: 컨테이너 내 대상 위치 (offsetTop, offsetLeft)
|
|
125
|
-
// offset: 가려지면 안 되는 영역 크기 (기본값 { top: 0, left: 0 })
|
|
126
|
-
// 상단/좌측 방향만 처리, 하단/우측은 브라우저 기본 동작에 의존
|
|
127
|
-
htmlElement.scrollIntoViewIfNeeded(
|
|
128
|
-
target: { top: number; left: number },
|
|
129
|
-
offset?: { top: number; left: number }
|
|
130
|
-
): void
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### Utils
|
|
134
|
-
|
|
135
|
-
| API | Type | Description |
|
|
136
|
-
|-----|------|-------------|
|
|
137
|
-
| `downloadBlob` | function | Blob을 파일로 다운로드 (링크 클릭 방식) |
|
|
138
|
-
| `DownloadProgress` | interface | 다운로드 진행 정보 (receivedLength, contentLength) |
|
|
139
|
-
| `fetchUrlBytes` | function | URL에서 Uint8Array 다운로드 (진행 콜백 지원) |
|
|
140
|
-
| `openFileDialog` | function | 파일 선택 대화상자를 프로그래밍 방식으로 열기 |
|
|
141
|
-
| `StoreConfig` | interface | IndexedDbStore 스토어 설정 (name, keyPath) |
|
|
142
|
-
| `IndexedDbStore` | class | IndexedDB를 Promise 기반으로 래핑한 저수준 CRUD 클래스 |
|
|
143
|
-
| `VirtualFsEntry` | interface | 가상 파일시스템 엔트리 (kind, dataBase64) |
|
|
144
|
-
| `IndexedDbVirtualFs` | class | IndexedDB 기반 경로 기반 가상 파일시스템 |
|
|
145
|
-
|
|
146
|
-
#### `downloadBlob`
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
export function downloadBlob(blob: Blob, fileName: string): void
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
Blob을 파일로 다운로드한다. `<a>` 태그를 생성하여 클릭하는 방식이다. Object URL은 1초 후 자동 해제된다.
|
|
153
|
-
|
|
154
|
-
#### `DownloadProgress`
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
export interface DownloadProgress {
|
|
158
|
-
receivedLength: number;
|
|
159
|
-
contentLength: number;
|
|
160
|
-
}
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
| Field | Type | Description |
|
|
164
|
-
|-------|------|-------------|
|
|
165
|
-
| `receivedLength` | `number` | 현재까지 수신한 바이트 수 |
|
|
166
|
-
| `contentLength` | `number` | 전체 콘텐츠 길이 (Content-Length 헤더 값) |
|
|
167
|
-
|
|
168
|
-
#### `fetchUrlBytes`
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
export async function fetchUrlBytes(
|
|
172
|
-
url: string,
|
|
173
|
-
options?: { onProgress?: (progress: DownloadProgress) => void },
|
|
174
|
-
): Promise<Uint8Array>
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
URL에서 바이너리 데이터를 `Uint8Array`로 다운로드한다. `Content-Length` 헤더가 있으면 사전 할당으로 메모리 효율을 높이고, 없으면 청크 수집 후 `bytes.concat`으로 병합한다. 진행 콜백은 `options.onProgress`로 수신한다. 응답이 실패하면 Error를 던진다.
|
|
178
|
-
|
|
179
|
-
#### `openFileDialog`
|
|
180
|
-
|
|
181
|
-
```typescript
|
|
182
|
-
export function openFileDialog(options?: {
|
|
183
|
-
accept?: string;
|
|
184
|
-
multiple?: boolean;
|
|
185
|
-
}): Promise<File[] | undefined>
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
프로그래밍 방식으로 파일 선택 대화상자를 연다. `accept`로 파일 형식 필터, `multiple`로 다중 선택 여부를 지정한다. 파일 선택 시 `File[]` 반환, 취소 시 `undefined` 반환.
|
|
189
|
-
|
|
190
|
-
#### `StoreConfig`
|
|
191
|
-
|
|
192
|
-
```typescript
|
|
193
|
-
export interface StoreConfig {
|
|
194
|
-
name: string;
|
|
195
|
-
keyPath: string;
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
| Field | Type | Description |
|
|
200
|
-
|-------|------|-------------|
|
|
201
|
-
| `name` | `string` | object store 이름 |
|
|
202
|
-
| `keyPath` | `string` | 기본 키 경로 |
|
|
203
|
-
|
|
204
|
-
#### `IndexedDbStore`
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
export class IndexedDbStore {
|
|
208
|
-
constructor(dbName: string, dbVersion: number, storeConfigs: StoreConfig[])
|
|
209
|
-
|
|
210
|
-
open(): Promise<IDBDatabase>
|
|
211
|
-
withStore<TResult>(storeName: string, mode: IDBTransactionMode, fn: (store: IDBObjectStore) => Promise<TResult>): Promise<TResult>
|
|
212
|
-
get<TValue>(storeName: string, key: IDBValidKey): Promise<TValue | undefined>
|
|
213
|
-
put(storeName: string, value: unknown): Promise<void>
|
|
214
|
-
delete(storeName: string, key: IDBValidKey): Promise<void>
|
|
215
|
-
getAll<TItem>(storeName: string): Promise<TItem[]>
|
|
216
|
-
close(): void
|
|
217
|
-
}
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
IndexedDB를 Promise 기반으로 래핑한 저수준 클래스. 생성자에서 DB 이름, 버전, 스토어 설정을 받는다.
|
|
221
|
-
|
|
222
|
-
| Method | Description |
|
|
223
|
-
|--------|-------------|
|
|
224
|
-
| `open()` | DB 연결을 열고 `IDBDatabase`를 반환. 중복 호출에 안전 (이미 열려 있으면 기존 인스턴스 반환) |
|
|
225
|
-
| `withStore(storeName, mode, fn)` | 지정 스토어에서 트랜잭션을 열고 `fn`을 실행. fn이 에러를 던지면 트랜잭션을 abort한다 |
|
|
226
|
-
| `get(storeName, key)` | 키로 단일 항목 조회. 없으면 `undefined` |
|
|
227
|
-
| `put(storeName, value)` | 항목 추가/갱신 (keyPath에 해당하는 필드가 value에 포함되어야 함) |
|
|
228
|
-
| `delete(storeName, key)` | 키로 항목 삭제 |
|
|
229
|
-
| `getAll(storeName)` | 스토어의 모든 항목 조회 |
|
|
230
|
-
| `close()` | DB 연결 닫기 |
|
|
231
|
-
|
|
232
|
-
#### `VirtualFsEntry`
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
export interface VirtualFsEntry {
|
|
236
|
-
kind: "file" | "dir";
|
|
237
|
-
dataBase64?: string;
|
|
238
|
-
}
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
| Field | Type | Description |
|
|
242
|
-
|-------|------|-------------|
|
|
243
|
-
| `kind` | `"file" \| "dir"` | 엔트리 종류 (파일 또는 디렉토리) |
|
|
244
|
-
| `dataBase64` | `string \| undefined` | 파일 데이터 (Base64 인코딩, 디렉토리인 경우 없음) |
|
|
245
|
-
|
|
246
|
-
#### `IndexedDbVirtualFs`
|
|
247
|
-
|
|
248
|
-
```typescript
|
|
249
|
-
export class IndexedDbVirtualFs {
|
|
250
|
-
constructor(db: IndexedDbStore, storeName: string, keyField: string)
|
|
251
|
-
|
|
252
|
-
getEntry(fullKey: string): Promise<VirtualFsEntry | undefined>
|
|
253
|
-
putEntry(fullKey: string, kind: "file" | "dir", dataBase64?: string): Promise<void>
|
|
254
|
-
deleteByPrefix(keyPrefix: string): Promise<boolean>
|
|
255
|
-
listChildren(prefix: string): Promise<{ name: string; isDirectory: boolean }[]>
|
|
256
|
-
ensureDir(fullKeyBuilder: (path: string) => string, dirPath: string): Promise<void>
|
|
257
|
-
}
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
`IndexedDbStore` 위에 경로 기반 가상 파일시스템을 구현하는 클래스. 키는 `/path/to/file` 형태의 문자열.
|
|
261
|
-
|
|
262
|
-
| Method | Description |
|
|
263
|
-
|--------|-------------|
|
|
264
|
-
| `getEntry(fullKey)` | 경로에 해당하는 엔트리 조회. 없으면 `undefined` |
|
|
265
|
-
| `putEntry(fullKey, kind, dataBase64?)` | 엔트리 추가/갱신. `kind`는 `"file"` 또는 `"dir"` |
|
|
266
|
-
| `deleteByPrefix(keyPrefix)` | 접두사와 일치하는 모든 엔트리 삭제. 삭제된 항목이 있으면 `true` |
|
|
267
|
-
| `listChildren(prefix)` | 접두사 바로 아래 자식 목록 반환. 이름과 디렉토리 여부 포함 |
|
|
268
|
-
| `ensureDir(fullKeyBuilder, dirPath)` | 경로의 모든 중간 디렉토리를 재귀적으로 생성. 이미 있으면 건너뜀 |
|
|
269
|
-
|
|
270
|
-
## Usage Examples
|
|
271
|
-
|
|
272
|
-
### Blob 파일 다운로드
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
import { downloadBlob } from "@simplysm/core-browser";
|
|
276
|
-
|
|
277
|
-
const data = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
|
|
278
|
-
const blob = new Blob([data], { type: "application/octet-stream" });
|
|
279
|
-
downloadBlob(blob, "output.bin");
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
### IndexedDB CRUD
|
|
283
|
-
|
|
284
|
-
```typescript
|
|
285
|
-
import { IndexedDbStore } from "@simplysm/core-browser";
|
|
286
|
-
|
|
287
|
-
const store = new IndexedDbStore("myDb", 1, [{ name: "items", keyPath: "id" }]);
|
|
288
|
-
|
|
289
|
-
await store.put("items", { id: "key1", value: "hello" });
|
|
290
|
-
const item = await store.get<{ id: string; value: string }>("items", "key1");
|
|
291
|
-
const all = await store.getAll<{ id: string; value: string }>("items");
|
|
292
|
-
await store.delete("items", "key1");
|
|
293
|
-
store.close();
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
### DOM 프로토타입 확장 사용
|
|
297
|
-
|
|
298
|
-
```typescript
|
|
299
|
-
import "@simplysm/core-browser";
|
|
300
|
-
|
|
301
|
-
const container = document.querySelector(".container")!;
|
|
302
|
-
const buttons = container.findAll<HTMLButtonElement>("button");
|
|
303
|
-
const firstInput = container.findFirst<HTMLInputElement>("input[type=text]");
|
|
304
|
-
const parents = container.getParents();
|
|
305
|
-
const focusable = container.findFirstTabbableChild();
|
|
306
|
-
```
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
# Errors
|
|
2
|
-
|
|
3
|
-
## `SdError`
|
|
4
|
-
|
|
5
|
-
트리 구조 에러 체인을 지원하는 에러 클래스. ES2024 `cause` 속성을 활용한다.
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
export class SdError extends Error {
|
|
9
|
-
override cause?: Error;
|
|
10
|
-
|
|
11
|
-
/** 원인 에러를 감싸서 생성. 메시지는 역순으로 결합됨 */
|
|
12
|
-
constructor(cause: Error, ...messages: string[]);
|
|
13
|
-
/** 메시지만으로 생성 */
|
|
14
|
-
constructor(...messages: string[]);
|
|
15
|
-
}
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
생성자에 전달된 메시지들은 역순으로 결합된다. `cause`가 `Error` 인스턴스이면 그 메시지도 체인 끝에 추가된다.
|
|
19
|
-
|
|
20
|
-
```typescript
|
|
21
|
-
throw new SdError(err, "API 호출 실패", "사용자 로드 실패");
|
|
22
|
-
// message: "사용자 로드 실패 => API 호출 실패 => 원본 에러 메시지"
|
|
23
|
-
|
|
24
|
-
throw new SdError("잘못된 상태", "처리 불가");
|
|
25
|
-
// message: "처리 불가 => 잘못된 상태"
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## `ArgumentError`
|
|
29
|
-
|
|
30
|
-
유효하지 않은 인자를 전달받았을 때 발생하는 에러. 인자 객체를 YAML 형식으로 메시지에 포함하여 디버깅을 용이하게 한다.
|
|
31
|
-
|
|
32
|
-
```typescript
|
|
33
|
-
export class ArgumentError extends SdError {
|
|
34
|
-
/** 기본 메시지("잘못된 인자입니다.")와 함께 인자 객체를 YAML 형식으로 출력 */
|
|
35
|
-
constructor(argObj: Record<string, unknown>);
|
|
36
|
-
/** 커스텀 메시지와 함께 인자 객체를 YAML 형식으로 출력 */
|
|
37
|
-
constructor(message: string, argObj: Record<string, unknown>);
|
|
38
|
-
}
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
throw new ArgumentError({ userId: 123, name: null });
|
|
43
|
-
// message: "잘못된 인자입니다.\n\nuserId: 123\nname: null"
|
|
44
|
-
|
|
45
|
-
throw new ArgumentError("잘못된 사용자", { userId: 123 });
|
|
46
|
-
// message: "잘못된 사용자\n\nuserId: 123"
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## `NotImplementedError`
|
|
50
|
-
|
|
51
|
-
아직 구현되지 않은 기능이 호출되었을 때 발생하는 에러.
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
export class NotImplementedError extends SdError {
|
|
55
|
-
constructor(message?: string);
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
throw new NotImplementedError("서브클래스에서 구현 필요");
|
|
61
|
-
// message: "미구현: 서브클래스에서 구현 필요"
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## `TimeoutError`
|
|
65
|
-
|
|
66
|
-
대기 시간이 초과되었을 때 발생하는 에러. `wait.until()`에서 최대 시도 횟수를 초과하면 자동으로 발생한다.
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
export class TimeoutError extends SdError {
|
|
70
|
-
constructor(count?: number, message?: string);
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
| Parameter | Type | Description |
|
|
75
|
-
|-----------|------|-------------|
|
|
76
|
-
| `count` | `number \| undefined` | 시도 횟수 |
|
|
77
|
-
| `message` | `string \| undefined` | 추가 메시지 |
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
throw new TimeoutError(50, "API 응답 대기 초과");
|
|
81
|
-
// message: "대기 시간 초과(50회 시도): API 응답 대기 초과"
|
|
82
|
-
```
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
# Extensions (Prototype)
|
|
2
|
-
|
|
3
|
-
`@simplysm/core-common`을 import하면 `Array`, `Map`, `Set` 프로토타입 확장이 자동 등록된다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
import "@simplysm/core-common"; // side-effect import — 확장 등록
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
## Array Extensions (Immutable)
|
|
10
|
-
|
|
11
|
-
새 배열을 반환하며 원본 배열을 변경하지 않는다.
|
|
12
|
-
|
|
13
|
-
| Method | Signature | Description |
|
|
14
|
-
|--------|-----------|-------------|
|
|
15
|
-
| `single` | `(predicate?) => T \| undefined` | 조건에 맞는 단일 요소 반환. 2개 이상이면 `ArgumentError` 발생 |
|
|
16
|
-
| `first` | `(predicate?) => T \| undefined` | 첫 번째 요소 반환 |
|
|
17
|
-
| `last` | `(predicate?) => T \| undefined` | 마지막 요소 반환 |
|
|
18
|
-
| `filterExists` | `() => NonNullable<T>[]` | null/undefined 제거 |
|
|
19
|
-
| `ofType` | `(type) => TNarrow[]` | 특정 타입의 요소만 필터 (`PrimitiveTypeStr` 또는 생성자) |
|
|
20
|
-
| `mapAsync` | `(selector) => Promise<TResult[]>` | 비동기 매핑 (순차 실행) |
|
|
21
|
-
| `filterAsync` | `(predicate) => Promise<T[]>` | 비동기 필터 (순차 실행) |
|
|
22
|
-
| `mapMany` | `(selector?) => TResult[]` | 매핑 후 평탄화 (또는 중첩 배열 평탄화) |
|
|
23
|
-
| `mapManyAsync` | `(selector?) => Promise<TResult[]>` | 비동기 매핑 후 평탄화 (순차 실행) |
|
|
24
|
-
| `parallelAsync` | `(fn) => Promise<TResult[]>` | 비동기 병렬 처리 (`Promise.all` 사용) |
|
|
25
|
-
| `groupBy` | `(keySelector, valueSelector?) => { key, values }[]` | key 기준 그룹화. 객체 key는 O(n²), 원시 key는 O(n) |
|
|
26
|
-
| `toMap` | `(keySelector, valueSelector?) => Map<TKey, TValue>` | Map으로 변환. 중복 key이면 `ArgumentError` 발생 |
|
|
27
|
-
| `toMapAsync` | `(keySelector, valueSelector?) => Promise<Map<TKey, TValue>>` | 비동기 Map으로 변환 |
|
|
28
|
-
| `toArrayMap` | `(keySelector, valueSelector?) => Map<TKey, TValue[]>` | 그룹 Map으로 변환. O(n) 성능 |
|
|
29
|
-
| `toSetMap` | `(keySelector, valueSelector?) => Map<TKey, Set<TValue>>` | 그룹 Set Map으로 변환 |
|
|
30
|
-
| `toMapValues` | `(keySelector, valueSelector) => Map<TKey, TValue>` | 그룹화 후 그룹별로 값 변환 |
|
|
31
|
-
| `toObject` | `(keySelector, valueSelector?) => Record<string, TValue>` | 일반 객체로 변환. 중복 key이면 `ArgumentError` 발생 |
|
|
32
|
-
| `toTree` | `(keyProp, parentKey) => TreeArray<T>[]` | 평면 배열을 트리 구조로 변환. O(n) 복잡도 |
|
|
33
|
-
| `distinct` | `(options?) => T[]` | 중복 제거. 객체 배열에서 `keyFn` 없이 사용하면 O(n²) |
|
|
34
|
-
| `orderBy` | `(selector?) => T[]` | 오름차순 정렬 |
|
|
35
|
-
| `orderByDesc` | `(selector?) => T[]` | 내림차순 정렬 |
|
|
36
|
-
| `diffs` | `(target, options?) => ArrayDiffsResult<T, P>[]` | 두 배열 비교 (INSERT/DELETE/UPDATE) |
|
|
37
|
-
| `oneWayDiffs` | `(orgItems, keyPropNameOrGetValFn, options?) => ArrayOneWayDiffResult<T>[]` | 단방향 차이 계산 (create/update/same) |
|
|
38
|
-
| `merge` | `(target, options?) => (T \| P \| T&P)[]` | 두 배열 병합 |
|
|
39
|
-
| `sum` | `(selector?) => number` | 합계. 빈 배열이면 0 |
|
|
40
|
-
| `min` | `(selector?) => T \| undefined` | 최솟값 |
|
|
41
|
-
| `max` | `(selector?) => T \| undefined` | 최댓값 |
|
|
42
|
-
| `shuffle` | `() => T[]` | 무작위 섞기 |
|
|
43
|
-
|
|
44
|
-
### `toTree` 상세
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
interface Item {
|
|
48
|
-
id: number;
|
|
49
|
-
parentId?: number;
|
|
50
|
-
name: string;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const items: Item[] = [
|
|
54
|
-
{ id: 1, name: "root" },
|
|
55
|
-
{ id: 2, parentId: 1, name: "child1" },
|
|
56
|
-
{ id: 3, parentId: 2, name: "grandchild" },
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
const tree = items.toTree("id", "parentId");
|
|
60
|
-
// [{ id: 1, name: "root", children: [
|
|
61
|
-
// { id: 2, name: "child1", children: [
|
|
62
|
-
// { id: 3, name: "grandchild", children: [] }
|
|
63
|
-
// ]}
|
|
64
|
-
// ]}]
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### `diffs` 상세
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
const diffs = newItems.diffs(oldItems, { keys: ["id"], excludes: ["updatedAt"] });
|
|
71
|
-
// ArrayDiffsResult: { source: undefined, target: item } (INSERT)
|
|
72
|
-
// { source: item, target: undefined } (DELETE)
|
|
73
|
-
// { source: item, target: item } (UPDATE)
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### `oneWayDiffs` 상세
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
const diffs = newItems.oneWayDiffs(orgItems, "id", { includeSame: false });
|
|
80
|
-
// ArrayOneWayDiffResult: { type: "create", item, orgItem: undefined }
|
|
81
|
-
// { type: "update", item, orgItem }
|
|
82
|
-
// { type: "same", item, orgItem }
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Array Extensions (Mutable)
|
|
86
|
-
|
|
87
|
-
원본 배열을 직접 변경한다.
|
|
88
|
-
|
|
89
|
-
| Method | Signature | Description |
|
|
90
|
-
|--------|-----------|-------------|
|
|
91
|
-
| `insert` | `(index, ...items) => this` | 지정 위치에 항목 삽입 |
|
|
92
|
-
| `remove` | `(item \| selector) => this` | 항목 또는 조건에 맞는 항목 제거 |
|
|
93
|
-
| `toggle` | `(item) => this` | 항목 토글 (있으면 제거, 없으면 추가) |
|
|
94
|
-
| `clear` | `() => this` | 배열 비우기 |
|
|
95
|
-
| `distinctThis` | `(options?) => T[]` | 원본 배열에서 중복 제거 |
|
|
96
|
-
| `orderByThis` | `(selector?) => T[]` | 원본 배열을 오름차순 정렬 |
|
|
97
|
-
| `orderByDescThis` | `(selector?) => T[]` | 원본 배열을 내림차순 정렬 |
|
|
98
|
-
|
|
99
|
-
## Map Extensions
|
|
100
|
-
|
|
101
|
-
| Method | Signature | Description |
|
|
102
|
-
|--------|-----------|-------------|
|
|
103
|
-
| `getOrCreate` | `(key, newValue \| newValueFn) => V` | key가 없으면 새 값을 설정하고 반환. V 타입이 함수이면 팩토리로 감싸야 함 |
|
|
104
|
-
| `update` | `(key, updateFn) => void` | 기존 값을 기반으로 업데이트. key가 없어도 `updateFn`이 호출됨 |
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
const map = new Map<string, number[]>();
|
|
108
|
-
const arr = map.getOrCreate("key", []); // 없으면 [] 설정 후 반환
|
|
109
|
-
|
|
110
|
-
const countMap = new Map<string, number>();
|
|
111
|
-
countMap.update("key", (v) => (v ?? 0) + 1); // 카운터 증가
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
## Set Extensions
|
|
115
|
-
|
|
116
|
-
| Method | Signature | Description |
|
|
117
|
-
|--------|-----------|-------------|
|
|
118
|
-
| `adds` | `(...values: T[]) => this` | 여러 값을 한 번에 추가 |
|
|
119
|
-
| `toggle` | `(value, addOrDel?) => this` | 값 토글. `addOrDel`로 강제 추가/제거 가능 |
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
const set = new Set<number>([1, 2, 3]);
|
|
123
|
-
set.adds(4, 5, 6); // 여러 항목 추가
|
|
124
|
-
set.toggle(2); // 2가 있으므로 제거 → {1, 3, 4, 5, 6}
|
|
125
|
-
set.toggle(99, "add"); // 강제 추가
|
|
126
|
-
set.toggle(99, "del"); // 강제 제거
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
## Exported Types
|
|
130
|
-
|
|
131
|
-
### `ArrayDiffsResult<TOriginal, TOther>`
|
|
132
|
-
|
|
133
|
-
`diffs()` 메서드의 반환 타입. discriminated union으로 `source`와 `target` 중 하나가 `undefined`이면 INSERT/DELETE, 둘 다 있으면 UPDATE.
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
export type ArrayDiffsResult<TOriginal, TOther> =
|
|
137
|
-
| { source: undefined; target: TOther } // INSERT
|
|
138
|
-
| { source: TOriginal; target: undefined } // DELETE
|
|
139
|
-
| { source: TOriginal; target: TOther }; // UPDATE
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### `ArrayOneWayDiffResult<TItem>`
|
|
143
|
-
|
|
144
|
-
`oneWayDiffs()` 메서드의 반환 타입. `type` 필드로 분기.
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
export type ArrayOneWayDiffResult<TItem> =
|
|
148
|
-
| { type: "create"; item: TItem; orgItem: undefined }
|
|
149
|
-
| { type: "update"; item: TItem; orgItem: TItem }
|
|
150
|
-
| { type: "same"; item: TItem; orgItem: TItem };
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### `TreeArray<TNode>`
|
|
154
|
-
|
|
155
|
-
`toTree()` 메서드의 반환 타입. 원본 타입에 `children` 속성이 추가된다.
|
|
156
|
-
|
|
157
|
-
```typescript
|
|
158
|
-
export type TreeArray<TNode> = TNode & { children: TreeArray<TNode>[] };
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### `ComparableType`
|
|
162
|
-
|
|
163
|
-
`orderBy`, `orderByDesc`, `orderByThis`, `orderByDescThis`의 selector 반환 타입.
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
export type ComparableType = string | number | boolean | DateTime | DateOnly | Time | undefined;
|
|
167
|
-
```
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
# Features
|
|
2
|
-
|
|
3
|
-
## `EventEmitter<TEvents>`
|
|
4
|
-
|
|
5
|
-
EventTarget 기반의 타입 안전한 이벤트 이미터. 브라우저와 Node.js 모두에서 사용 가능하다.
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
export class EventEmitter<
|
|
9
|
-
TEvents extends { [K in keyof TEvents]: unknown } = Record<string, unknown>,
|
|
10
|
-
> {
|
|
11
|
-
on<TEventName extends keyof TEvents & string>(
|
|
12
|
-
type: TEventName,
|
|
13
|
-
listener: (data: TEvents[TEventName]) => void,
|
|
14
|
-
): void;
|
|
15
|
-
|
|
16
|
-
off<TEventName extends keyof TEvents & string>(
|
|
17
|
-
type: TEventName,
|
|
18
|
-
listener: (data: TEvents[TEventName]) => void,
|
|
19
|
-
): void;
|
|
20
|
-
|
|
21
|
-
emit<TEventName extends keyof TEvents & string>(
|
|
22
|
-
type: TEventName,
|
|
23
|
-
...args: TEvents[TEventName] extends void ? [] : [data: TEvents[TEventName]]
|
|
24
|
-
): void;
|
|
25
|
-
|
|
26
|
-
listenerCount<TEventName extends keyof TEvents & string>(type: TEventName): number;
|
|
27
|
-
|
|
28
|
-
dispose(): void;
|
|
29
|
-
}
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### 메서드
|
|
33
|
-
|
|
34
|
-
| Method | Description |
|
|
35
|
-
|--------|-------------|
|
|
36
|
-
| `on(type, listener)` | 이벤트 리스너 등록. 같은 리스너를 같은 이벤트에 중복 등록하면 무시됨 |
|
|
37
|
-
| `off(type, listener)` | 이벤트 리스너 제거 |
|
|
38
|
-
| `emit(type, data?)` | 이벤트 발행. `void` 타입 이벤트는 인자 없이 호출 |
|
|
39
|
-
| `listenerCount(type)` | 특정 이벤트의 등록된 리스너 수 반환 |
|
|
40
|
-
| `dispose()` | 모든 이벤트 리스너 제거 |
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
interface MyEvents {
|
|
44
|
-
data: string;
|
|
45
|
-
error: Error;
|
|
46
|
-
done: void;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
class MyService extends EventEmitter<MyEvents> {}
|
|
50
|
-
|
|
51
|
-
const svc = new MyService();
|
|
52
|
-
svc.on("data", (data) => console.log(data)); // data: string
|
|
53
|
-
svc.emit("data", "hello");
|
|
54
|
-
svc.emit("done"); // void 타입은 인자 없이 호출
|
|
55
|
-
svc.dispose(); // 모든 리스너 정리
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## `DebounceQueue`
|
|
61
|
-
|
|
62
|
-
짧은 시간 내에 여러 번 호출되면 마지막 요청만 실행하는 비동기 디바운스 큐.
|
|
63
|
-
|
|
64
|
-
`EventEmitter<{ error: SdError }>`를 상속한다. 에러 리스너가 없으면 `consola`로 로그 출력한다.
|
|
65
|
-
|
|
66
|
-
```typescript
|
|
67
|
-
export class DebounceQueue extends EventEmitter<{ error: SdError }> {
|
|
68
|
-
constructor(delay?: number);
|
|
69
|
-
|
|
70
|
-
run(fn: () => void | Promise<void>): void;
|
|
71
|
-
|
|
72
|
-
override dispose(): void;
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
| Parameter | Type | Description |
|
|
77
|
-
|-----------|------|-------------|
|
|
78
|
-
| `delay` | `number \| undefined` | 디바운스 지연 시간 (ms). 생략 시 즉시 실행 (다음 이벤트 루프) |
|
|
79
|
-
|
|
80
|
-
실행 중에 추가된 요청은 디바운스 지연 없이 현재 실행 완료 직후 즉시 처리된다.
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
const dq = new DebounceQueue(300);
|
|
84
|
-
dq.on("error", (err) => console.error(err));
|
|
85
|
-
dq.run(() => console.log("1")); // 무시됨
|
|
86
|
-
dq.run(() => console.log("2")); // 무시됨
|
|
87
|
-
dq.run(() => console.log("3")); // 300ms 후 실행
|
|
88
|
-
|
|
89
|
-
// 자원 정리
|
|
90
|
-
const dq2 = new DebounceQueue(100);
|
|
91
|
-
try {
|
|
92
|
-
dq2.run(() => { /* ... */ });
|
|
93
|
-
} finally {
|
|
94
|
-
dq2.dispose();
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
## `SerialQueue`
|
|
101
|
-
|
|
102
|
-
큐에 추가된 함수들을 순차적으로 실행하는 비동기 직렬 큐. 하나의 작업이 완료된 후에야 다음 작업이 시작된다. 에러가 발생해도 후속 작업은 계속 실행된다.
|
|
103
|
-
|
|
104
|
-
`EventEmitter<{ error: SdError }>`를 상속한다. 에러 리스너가 없으면 `consola`로 로그 출력한다.
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
export class SerialQueue extends EventEmitter<{ error: SdError }> {
|
|
108
|
-
constructor(gap?: number);
|
|
109
|
-
|
|
110
|
-
run(fn: () => void | Promise<void>): void;
|
|
111
|
-
|
|
112
|
-
override dispose(): void;
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
| Parameter | Type | Description |
|
|
117
|
-
|-----------|------|-------------|
|
|
118
|
-
| `gap` | `number` | 각 작업 사이의 간격 (ms). 기본값: 0 |
|
|
119
|
-
|
|
120
|
-
`dispose()`는 대기 중인 큐를 비운다 (현재 실행 중인 작업은 완료됨).
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
const sq = new SerialQueue();
|
|
124
|
-
sq.on("error", (err) => console.error(err));
|
|
125
|
-
sq.run(async () => await fetch("/api/1"));
|
|
126
|
-
sq.run(async () => await fetch("/api/2")); // 1 완료 후 실행
|
|
127
|
-
sq.run(async () => await fetch("/api/3")); // 2 완료 후 실행
|
|
128
|
-
|
|
129
|
-
// 100ms 간격으로 실행, 자원 정리
|
|
130
|
-
const sq2 = new SerialQueue(100);
|
|
131
|
-
try {
|
|
132
|
-
sq2.run(async () => { /* ... */ });
|
|
133
|
-
} finally {
|
|
134
|
-
sq2.dispose();
|
|
135
|
-
}
|
|
136
|
-
```
|