@simplysm/sd-claude 14.0.88 → 14.0.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude/references/sd-simplysm14/README.md +16 -17
- package/claude/references/sd-simplysm14/apis/angular/README.md +39 -43
- package/claude/references/sd-simplysm14/apis/angular/controls.md +174 -80
- package/claude/references/sd-simplysm14/apis/angular/crud.md +41 -50
- package/claude/references/sd-simplysm14/apis/angular/directives.md +60 -26
- package/claude/references/sd-simplysm14/apis/angular/features.md +109 -37
- package/claude/references/sd-simplysm14/apis/angular/infra.md +61 -44
- package/claude/references/sd-simplysm14/apis/angular/layout.md +39 -31
- package/claude/references/sd-simplysm14/apis/angular/overlay.md +73 -85
- package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +54 -39
- package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +55 -30
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +71 -67
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +82 -72
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +35 -36
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +71 -43
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +38 -30
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +45 -50
- package/claude/references/sd-simplysm14/apis/core-browser/README.md +42 -55
- package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +62 -0
- package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +39 -38
- package/claude/references/sd-simplysm14/apis/core-common/README.md +95 -103
- package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +59 -54
- package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +86 -0
- package/claude/references/sd-simplysm14/apis/core-common/datetime.md +57 -66
- package/claude/references/sd-simplysm14/apis/core-common/errors.md +86 -0
- package/claude/references/sd-simplysm14/apis/core-common/obj.md +60 -42
- package/claude/references/sd-simplysm14/apis/core-common/serialization.md +55 -0
- package/claude/references/sd-simplysm14/apis/core-node/README.md +10 -8
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +29 -32
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +34 -22
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +29 -25
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +40 -53
- package/claude/references/sd-simplysm14/apis/core-node/pathx.md +22 -29
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +31 -31
- package/claude/references/sd-simplysm14/apis/excel/README.md +26 -26
- package/claude/references/sd-simplysm14/apis/excel/cell.md +37 -29
- package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +29 -15
- package/claude/references/sd-simplysm14/apis/excel/style.md +33 -27
- package/claude/references/sd-simplysm14/apis/excel/utils.md +29 -19
- package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +78 -55
- package/claude/references/sd-simplysm14/apis/excel/wrapper.md +42 -45
- package/claude/references/sd-simplysm14/apis/lint/README.md +27 -21
- package/claude/references/sd-simplysm14/apis/lint/rules.md +89 -49
- package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -62
- package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +149 -67
- package/claude/references/sd-simplysm14/apis/orm-common/expr.md +111 -99
- package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +115 -72
- package/claude/references/sd-simplysm14/apis/orm-common/schema.md +134 -92
- package/claude/references/sd-simplysm14/apis/orm-common/types.md +67 -52
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +63 -26
- package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +51 -40
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +10 -12
- package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +92 -45
- package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +226 -108
- package/claude/references/sd-simplysm14/apis/service-client/README.md +90 -88
- package/claude/references/sd-simplysm14/apis/service-client/orm.md +37 -29
- package/claude/references/sd-simplysm14/apis/service-client/transport.md +45 -20
- package/claude/references/sd-simplysm14/apis/service-common/README.md +89 -40
- package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +126 -34
- package/claude/references/sd-simplysm14/apis/service-common/protocol.md +109 -54
- package/claude/references/sd-simplysm14/apis/service-server/README.md +70 -66
- package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +47 -47
- package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +71 -34
- package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +31 -32
- package/claude/references/sd-simplysm14/apis/storage/README.md +34 -28
- package/claude/references/sd-simplysm14/manuals/client-app-structure.md +142 -140
- package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -1
- package/claude/references/sd-simplysm14/manuals/client-service.md +19 -7
- package/claude/references/sd-simplysm14/manuals/client-shared-data.md +2 -2
- package/claude/references/sd-simplysm14/manuals/client-system-log.md +11 -3
- package/claude/references/sd-simplysm14/manuals/data-log.md +0 -1
- package/claude/references/sd-simplysm14/manuals/orm.md +16 -0
- package/claude/rules/sd-design-rules.md +10 -0
- package/claude/skills/sd-docs/SKILL.md +58 -46
- package/claude/skills/sd-docs/references/{doc-rules.md → subagent-prompt.md} +103 -103
- package/claude/skills/sd-impl/SKILL.md +1 -1
- package/claude/skills/sd-spec/SKILL.md +858 -858
- package/claude/skills/sd-spec/references/example-spec.md +26 -36
- package/package.json +1 -1
- package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +0 -47
- package/claude/references/sd-simplysm14/apis/orm-common/query-builder.md +0 -29
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/.specs/inventory/spec.md +0 -99
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/package.json +0 -12
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/index.ts +0 -3
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inbound/inbound.list.ts +0 -150
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inventory/inventory-master.list.ts +0 -143
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/outbound/outbound.list.ts +0 -150
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/pnpm-workspace.yaml +0 -2
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/sd.config.ts +0 -12
- package/claude/skills/sd-demo/evals/golden.jsonl +0 -1
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/package.json +0 -8
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/src/.gitkeep +0 -0
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tests/.gitkeep +0 -0
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tsconfig.json +0 -10
- package/claude/skills/sd-dev/evals/golden.jsonl +0 -1
- package/claude/skills/sd-docs/evals/fixtures/new-write/.claude/references/sd-simplysm14/README.md +0 -7
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/src/index.ts +0 -3
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/package.json +0 -6
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/src/index.ts +0 -1
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/src/index.ts +0 -8
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/README.md +0 -7
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/apis/foo/README.md +0 -3
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/src/index.ts +0 -3
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/package.json +0 -6
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/src/index.ts +0 -1
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/src/index.ts +0 -8
- package/claude/skills/sd-docs/evals/golden.jsonl +0 -2
- package/claude/skills/sd-impl/evals/fixtures/case-a-new-screen/.specs/260513120000_warehouse/spec.md +0 -101
- package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/.specs/260513120000_warehouse/spec.md +0 -101
- package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/packages/app/src/screens/box-register/box-register.view.ts +0 -46
- package/claude/skills/sd-impl/evals/fixtures/case-c-new-cross/.specs/260513120000_warehouse/spec.md +0 -89
- package/claude/skills/sd-impl/evals/fixtures/case-d-spec-modify/.specs/260513120000_warehouse/spec.md +0 -101
- package/claude/skills/sd-impl/evals/golden.jsonl +0 -4
- package/claude/skills/sd-manual/evals/fixtures/new-manual/src/notification.ts +0 -25
- package/claude/skills/sd-manual/evals/fixtures/update-manual/.claude/references/sd-simplysm14/manuals/notification.md +0 -14
- package/claude/skills/sd-manual/evals/fixtures/update-manual/src/notification.ts +0 -37
- package/claude/skills/sd-manual/evals/golden.jsonl +0 -2
- package/claude/skills/sd-review/evals/fixtures/code-review/src/foo.ts +0 -7
- package/claude/skills/sd-review/evals/fixtures/doc-review/docs/foo.md +0 -4
- package/claude/skills/sd-review/evals/golden.jsonl +0 -2
- package/claude/skills/sd-skill/evals/fixtures/existing-skill/.claude/skills/todo-format/SKILL.md +0 -14
- package/claude/skills/sd-skill/evals/fixtures/new-skill/.gitkeep +0 -0
- package/claude/skills/sd-skill/evals/golden.jsonl +0 -2
- package/claude/skills/sd-spec/evals/fixtures/case-a-split//355/232/214/354/235/230/353/241/235.md +0 -20
- package/claude/skills/sd-spec/evals/fixtures/case-b-detail/.specs/260513120000_warehouse/spec.md +0 -95
- package/claude/skills/sd-spec/evals/golden.jsonl +0 -2
- package/claude/skills/sd-unpack/evals/fixtures/eml-with-text-attachment/meeting.eml +0 -21
- package/claude/skills/sd-unpack/evals/fixtures/simple-eml/meeting.eml +0 -10
- package/claude/skills/sd-unpack/evals/golden.jsonl +0 -2
- package/claude/skills/sd-use/evals/fixtures/empty/.gitkeep +0 -0
- package/claude/skills/sd-use/evals/golden.jsonl +0 -6
|
@@ -1,67 +1,62 @@
|
|
|
1
1
|
# @simplysm/capacitor-plugin-usb-storage
|
|
2
2
|
|
|
3
|
-
USB Mass Storage 장치를
|
|
3
|
+
Capacitor 플러그인. 연결된 USB Mass Storage 장치를 열거하고 권한을 얻은 뒤 디렉토리/파일을 읽는다. Android 는 libaums 네이티브 구현, 브라우저는 IndexedDB 기반 가상 USB 저장소로 에뮬레이션된다. entry 가 노출하는 심볼은 정적 클래스 `UsbStorage` 와 입출력 타입 4종.
|
|
4
4
|
|
|
5
5
|
## 사용 트리거 인덱스
|
|
6
6
|
|
|
7
|
-
- **UsbStorage** — USB 장치
|
|
8
|
-
- **UsbDeviceFilter /
|
|
9
|
-
- **UsbStoragePlugin** — `UsbStorage` 가 내부적으로 감싸는 Capacitor 네이티브 인터페이스. 보통 직접 호출하지 않음.
|
|
7
|
+
- **UsbStorage** — USB 저장 장치 접근의 진입점. "장치 목록 조회 → 권한 확인/요청 → readdir/readFile" 흐름을 정적 메서드로 호출할 때.
|
|
8
|
+
- **UsbDeviceInfo / UsbDeviceFilter / UsbFileInfo / UsbStoragePlugin** — 위 메서드의 입출력 타입. 장치 식별(vendorId/productId), 반환 항목(`name`/`isDirectory`) 형태를 다룰 때 참조.
|
|
10
9
|
|
|
11
|
-
## UsbStorage
|
|
10
|
+
## UsbStorage
|
|
12
11
|
|
|
13
|
-
모든
|
|
12
|
+
`abstract class` 이며 모든 멤버가 `static` — 인스턴스화하지 않고 `UsbStorage.메서드()` 로 호출한다. 내부적으로 `registerPlugin<UsbStoragePlugin>("UsbStorage")` 로 얻은 네이티브(Android)/웹 구현에 위임하고, 래퍼 객체(`{ devices }`·`{ granted }`·`{ files }`·`{ data }`)에서 값을 꺼내 평탄화해 반환한다. 대상 장치는 항상 `UsbDeviceFilter`(= `{ vendorId, productId }`) 로 지정하며 readdir/readFile 도 매 호출마다 filter 를 받는다(상태 없음).
|
|
14
13
|
|
|
15
|
-
`static getDevices(): Promise<UsbDeviceInfo[]>`
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
`static
|
|
19
|
-
- `filter
|
|
20
|
-
|
|
21
|
-
`static checkPermissions(filter: UsbDeviceFilter): Promise<boolean>`
|
|
22
|
-
- 권한 요청 다이얼로그 없이 현재 보유 권한만 확인. `true`=이미 보유, `false`=미보유(이때 `requestPermissions` 필요). web 구현은 항상 `true`.
|
|
23
|
-
|
|
24
|
-
`static readdir(filter: UsbDeviceFilter, dirPath: string): Promise<UsbFileInfo[]>`
|
|
25
|
-
- `filter` 장치의 `dirPath` 디렉토리 하위 항목 목록을 반환. `dirPath`=읽을 디렉토리 경로(루트는 `"/"`).
|
|
26
|
-
|
|
27
|
-
`static readFile(filter: UsbDeviceFilter, filePath: string): Promise<Bytes | undefined>`
|
|
28
|
-
- `filter` 장치의 `filePath` 파일 내용을 `Bytes`(`@simplysm/core-common`)로 반환. `filePath`=읽을 파일 경로. 네이티브가 데이터 없음(null)을 주면 `undefined` 반환 — 결측을 빈 Bytes 로 치환하지 않고 그대로 전파. 내부적으로 base64 → `bytes.fromBase64` 변환.
|
|
14
|
+
- `static getDevices(): Promise<UsbDeviceInfo[]>` — 현재 연결된 USB 장치 목록 조회. 권한과 무관하게 열거되며, 반환 항목의 `vendorId`/`productId` 를 추려 이후 호출의 filter 로 사용. 흐름의 첫 단계.
|
|
15
|
+
- `static requestPermissions(filter: UsbDeviceFilter): Promise<boolean>` — 지정 장치 접근 권한을 사용자에게 요청(다이얼로그). 반환 true=승인, false=거부(이때 읽기 중단). 권한이 없을 때 읽기 직전 호출. 브라우저 웹 구현은 항상 true 를 반환.
|
|
16
|
+
- `static checkPermissions(filter: UsbDeviceFilter): Promise<boolean>` — 다이얼로그 없이 현재 권한 보유 여부만 확인. true=이미 보유(requestPermissions 생략 가능), false=미보유(요청 필요). 권한이 이미 있을 때 요청을 건너뛰려는 분기 기준. 브라우저 웹 구현은 항상 true 를 반환.
|
|
17
|
+
- `static readdir(filter: UsbDeviceFilter, dirPath: string): Promise<UsbFileInfo[]>` — 지정 장치의 `dirPath`(읽을 디렉토리 경로, 루트는 `"/"`) 바로 아래 항목 목록 조회. 각 항목은 `name`+`isDirectory` 로 파일/폴더 구분. 트리를 순회하려면 `isDirectory === true` 인 항목을 다시 readdir. 웹 구현은 장치가 없거나 대상이 디렉토리가 아니면 빈 배열.
|
|
18
|
+
- `static readFile(filter: UsbDeviceFilter, filePath: string): Promise<Bytes | undefined>` — 지정 장치의 `filePath`(읽을 파일 경로) 내용을 `Bytes`(`@simplysm/core-common`) 로 읽음. 파일이 없거나 네이티브가 데이터 없음(`null`)을 주면 `undefined` 반환 — "빈 파일" 과 "없음" 을 구분하려면 이 `undefined` 를 그대로 검사(`""`·기본값 치환 금지). 내부에서 base64 응답을 `bytes.fromBase64` 로 복원.
|
|
29
19
|
|
|
30
20
|
사용 예:
|
|
31
21
|
|
|
32
22
|
```ts
|
|
33
|
-
|
|
34
|
-
|
|
23
|
+
import { UsbStorage } from "@simplysm/capacitor-plugin-usb-storage";
|
|
24
|
+
|
|
25
|
+
const [device] = await UsbStorage.getDevices();
|
|
26
|
+
if (!device) return;
|
|
27
|
+
const filter = { vendorId: device.vendorId, productId: device.productId };
|
|
28
|
+
|
|
35
29
|
if (!(await UsbStorage.checkPermissions(filter))) {
|
|
36
30
|
if (!(await UsbStorage.requestPermissions(filter))) return; // 거부 시 중단
|
|
37
31
|
}
|
|
38
|
-
const files = await UsbStorage.readdir(filter, "/");
|
|
39
|
-
const data = await UsbStorage.readFile(filter, "/data.bin"); // Bytes | undefined
|
|
40
|
-
```
|
|
41
32
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- `deviceName: string` — OS 가 보고하는 장치 시스템 이름.
|
|
50
|
-
- `manufacturerName: string` — 제조사 이름.
|
|
51
|
-
- `productName: string` — 제품 이름.
|
|
52
|
-
- `vendorId: number` — 벤더 ID. 그대로 `UsbDeviceFilter.vendorId` 구성에 사용.
|
|
53
|
-
- `productId: number` — 제품 ID. 그대로 `UsbDeviceFilter.productId` 구성에 사용.
|
|
54
|
-
|
|
55
|
-
`interface UsbFileInfo` — `readdir` 반환 배열 요소
|
|
56
|
-
- `name: string` — 항목(파일/폴더) 이름.
|
|
57
|
-
- `isDirectory: boolean` — `true`=디렉토리(하위 readdir 가능), `false`=파일(readFile 대상). 재귀 탐색·파일 필터링 분기에 사용.
|
|
58
|
-
|
|
59
|
-
## UsbStoragePlugin (네이티브 인터페이스)
|
|
60
|
-
|
|
61
|
-
`registerPlugin<UsbStoragePlugin>("UsbStorage")` 로 등록되는 Capacitor 인터페이스. `UsbStorage` 정적 메서드가 이를 감싸며 `{ ... }` 래퍼 객체에서 값을 꺼내므로 보통 직접 호출 불필요. 직접 다뤄야 할 때만 참조.
|
|
33
|
+
for (const entry of await UsbStorage.readdir(filter, "/")) {
|
|
34
|
+
if (entry.isDirectory) continue; // 폴더면 하위 readdir 로 순회
|
|
35
|
+
const data = await UsbStorage.readFile(filter, `/${entry.name}`);
|
|
36
|
+
if (data === undefined) continue; // 파일 없음/데이터 없음
|
|
37
|
+
// data: Bytes 사용
|
|
38
|
+
}
|
|
39
|
+
```
|
|
62
40
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
- `
|
|
41
|
+
## 입출력 타입
|
|
42
|
+
|
|
43
|
+
`UsbStoragePlugin` 은 Capacitor 가 `registerPlugin<UsbStoragePlugin>("UsbStorage")` 로 등록하는 네이티브 측 계약이며, 위 `UsbStorage` 정적 메서드가 이를 1:1 로 감싸 쓰기 쉬운 형태(배열·boolean·Bytes)로 변환한다. 보통 직접 호출하지 않고 타입 참조용으로만 본다.
|
|
44
|
+
|
|
45
|
+
- `UsbDeviceInfo` — `getDevices()` 가 돌려주는 장치 정보 1건.
|
|
46
|
+
- `deviceName: string` — OS 가 부여한 장치 시스템 이름. 화면 표시·로그용.
|
|
47
|
+
- `manufacturerName: string` — 제조사 이름. 사용자에게 장치를 식별시키거나 동일 제품명을 구분할 때.
|
|
48
|
+
- `productName: string` — 제품 이름. 사용자에게 어떤 장치인지 보여줄 때.
|
|
49
|
+
- `vendorId: number` — USB 벤더 ID. 이후 filter 의 `vendorId` 로 그대로 사용.
|
|
50
|
+
- `productId: number` — USB 제품 ID. `vendorId` 와 조합해 장치를 유일하게 지정, 이후 filter 의 `productId` 로 그대로 사용.
|
|
51
|
+
- `UsbDeviceFilter` — 접근 대상 장치를 지정하는 키. 권한·읽기 메서드 모두 이 형태를 받음. 보통 `UsbDeviceInfo` 에서 두 ID 만 추려 만듦.
|
|
52
|
+
- `vendorId: number` — 대상 장치 벤더 ID. `UsbDeviceInfo.vendorId` 를 그대로 넘김.
|
|
53
|
+
- `productId: number` — 대상 장치 제품 ID. `UsbDeviceInfo.productId` 를 그대로 넘김.
|
|
54
|
+
- `UsbFileInfo` — `readdir()` 가 돌려주는 디렉토리 항목 1건.
|
|
55
|
+
- `name: string` — 파일/폴더 이름(경로가 아닌 단일 세그먼트). readFile/하위 readdir 경로를 만들 때 부모 경로에 이어붙임.
|
|
56
|
+
- `isDirectory: boolean` — 디렉토리 여부. true 면 폴더(다시 readdir 대상), false 면 파일(readFile 대상). 트리 순회·파일 필터링의 분기 기준.
|
|
57
|
+
- `UsbStoragePlugin` — Capacitor `registerPlugin` 대상 인터페이스. `UsbStorage` 가 평탄화하기 전의 원형 시그니처로, 메서드는 옵션 객체 입력 / 래퍼 객체 출력을 가짐.
|
|
58
|
+
- `getDevices(): Promise<{ devices: UsbDeviceInfo[] }>` — 장치 목록을 `devices` 키로 반환.
|
|
59
|
+
- `requestPermissions(options: UsbDeviceFilter): Promise<{ granted: boolean }>` — 권한 요청 결과를 `granted` 로 반환.
|
|
60
|
+
- `checkPermissions(options: UsbDeviceFilter): Promise<{ granted: boolean }>` — 권한 보유 여부를 `granted` 로 반환.
|
|
61
|
+
- `readdir(options: UsbDeviceFilter & { path: string }): Promise<{ files: UsbFileInfo[] }>` — filter 에 `path`(대상 디렉토리 경로)를 합친 입력으로 항목을 `files` 로 반환.
|
|
62
|
+
- `readFile(options: UsbDeviceFilter & { path: string }): Promise<{ data: string | null }>` — filter 에 `path`(대상 파일 경로)를 합친 입력으로 파일을 base64 문자열 `data` 로 반환. 데이터 없으면 `null`(상위 `UsbStorage.readFile` 가 `undefined` 로 변환).
|
|
@@ -1,83 +1,70 @@
|
|
|
1
1
|
# @simplysm/core-browser
|
|
2
2
|
|
|
3
|
-
브라우저 전용
|
|
3
|
+
브라우저 전용 유틸리티 묶음. DOM 요소 탐색·위치 계산·가시성 판정 확장, IndexedDB 영속화 및 그 위의 가상 파일시스템, 파일 다운로드·선택 헬퍼를 제공.
|
|
4
|
+
|
|
5
|
+
> 패키지의 어떤 심볼이든 import 하면 `index.ts` 가 `import "./extensions/element-ext"`·`"./extensions/html-element-ext"` 를 실행해 `Element`/`HTMLElement` 프로토타입 확장이 사이드 이펙트로 자동 등록됨. 별도 초기화 없이 `el.findAll(...)` 식으로 호출 가능.
|
|
4
6
|
|
|
5
7
|
## 사용 트리거 인덱스
|
|
6
8
|
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **다운로드·파일선택·fetch** (`downloadBlob`, `openFileDialog`, `fetchUrlBytes`) — Blob 저장, 파일 선택 다이얼로그, 진행률 포함 바이너리 다운로드가 필요할 때. (아래 인라인)
|
|
11
|
-
- **IndexedDB 저장소/가상 파일시스템** (`IndexedDbStore`, `IndexedDbVirtualFs`) — 브라우저 IndexedDB 에 KV 저장하거나 경로 기반 가상 파일트리를 다룰 때. 자세히: [indexed-db.md](./indexed-db.md)
|
|
9
|
+
- **DOM 요소 확장·헬퍼** — DOM 조회(`findAll`/`findFirst`), 조상/탭 이동 가능 요소 탐색, offset·가시성 판정, 부모 기준 상대 좌표 계산, 가림 보정 스크롤, 강제 리페인트, 클립보드 복사/붙여넣기 핸들러, 다중 요소 경계 측정이 필요할 때. 자세히: [dom-element.md](./dom-element.md)
|
|
10
|
+
- **IndexedDB 영속화** (`IndexedDbStore`, `IndexedDbVirtualFs`) — 브라우저 IndexedDB 에 KV 영구 저장하거나, 그 위에 경로 키 기반 가상 파일트리(파일/디렉터리)를 올릴 때. 자세히: [indexed-db.md](./indexed-db.md)
|
|
11
|
+
- **downloadBlob / fetchUrlBytes / openFileDialog** — 생성한 Blob 을 파일로 저장하거나, 진행률을 보며 URL 바이너리를 받거나, 파일 선택 대화상자를 코드로 열 때. (아래 인라인 섹션)
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## 파일·다운로드 유틸
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
생성한 데이터를 파일로 내보내거나, 외부 바이너리를 받거나, 사용자에게 파일을 고르게 할 때 쓰는 단발성 함수들.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
- `findFirst<TEl>(selector: string): TEl | undefined` — 첫 일치 하위 요소 또는 `undefined`. 빈 선택자면 `undefined`, 미일치도 `undefined`. `querySelector` 의 `null` 을 `undefined` 로 정규화한 형태.
|
|
19
|
-
- `prependChild<TEl>(child: TEl): TEl` — 자식을 첫 번째 위치(`insertBefore(child, firstElementChild)`)로 삽입하고 그 요소 반환. 맨 앞에 끼울 때.
|
|
20
|
-
- `getParents(): Element[]` — 모든 조상 요소를 가까운 것부터 먼 순서로 배열 반환. 조상 체인 순회·특정 조상 포함 판정에.
|
|
21
|
-
- `findTabbableParent(): HTMLElement | undefined` — `tabbable` 기준 첫 탭 이동 가능 조상. 포커스 위임 대상을 위로 탐색할 때.
|
|
22
|
-
- `findFirstTabbableChild(): HTMLElement | undefined` — TreeWalker 로 순회한 첫 탭 이동 가능 하위 요소. 컨테이너 진입 시 자동 포커스 대상 찾을 때.
|
|
23
|
-
- `isOffsetElement(): boolean` — `position` 이 relative/absolute/fixed/sticky 중 하나면 true. offset parent(절대배치 기준) 역할 여부 판정에.
|
|
24
|
-
- `isVisible(): boolean` — `getClientRects().length > 0` + `visibility !== "hidden"` + `opacity !== "0"` 를 모두 만족하면 true. 화면 표시 여부 판정에(display:none 은 clientRects 가 비어 false).
|
|
17
|
+
### downloadBlob
|
|
25
18
|
|
|
26
19
|
```ts
|
|
27
|
-
|
|
28
|
-
const rows = containerEl.findAll<HTMLElement>("tr");
|
|
29
|
-
const first = containerEl.findFirstTabbableChild();
|
|
20
|
+
function downloadBlob(blob: Blob, fileName: string): void;
|
|
30
21
|
```
|
|
31
22
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
`HTMLElement.prototype` 에 등록되는 메서드. 위와 동일하게 import 만으로 활성화.
|
|
23
|
+
Blob 을 object URL 로 만들어 동적 `a[download]` 클릭으로 저장하고, object URL 은 1초 뒤 revoke. 클릭 직후 함수가 반환됨(다운로드 완료를 기다리지 않음). 화면 다운로드 버튼 핸들러에서 즉시 저장할 때.
|
|
35
24
|
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
- parent: `HTMLElement | string` — 기준 부모. 문자열이면 `this.closest(parent)` 로 조상 탐색, 요소면 직접 사용. `document.body` 나 `".container"` 식으로 지정.
|
|
39
|
-
- `scrollIntoViewIfNeeded(target, offset?): void` — 대상이 스크롤 영역의 상단/좌측 경계를 벗어났을 때만 스크롤하여 보이게 함. 하단/우측은 처리하지 않고 브라우저 기본 포커스 스크롤에 위임. 고정 헤더/컬럼 테이블의 포커스 처리에.
|
|
40
|
-
- target: `{ top: number; left: number }` — 컨테이너 내 대상 위치(offsetTop/offsetLeft).
|
|
41
|
-
- offset: `{ top: number; left: number }` — 가려지면 안 되는 영역 크기(고정 헤더 높이·고정 컬럼 너비). 기본 `{ top: 0, left: 0 }`.
|
|
25
|
+
- blob: Blob — 저장할 데이터. 엑셀·이미지·텍스트 등 메모리에서 만든 Blob 을 그대로 전달.
|
|
26
|
+
- fileName: string — 저장 파일명. `sanitize-filename` 으로 OS 금지 문자·예약어를 제거한 뒤 추가로 `[`·`]` 도 제거하며, 결과가 빈 문자열이면 `"download"` 로 대체. 확장자 포함, 사용자 입력 파일명을 그대로 넣어도 안전.
|
|
42
27
|
|
|
43
28
|
```ts
|
|
44
|
-
|
|
45
|
-
scrollEl.scrollIntoViewIfNeeded({ top: cellTop, left: cellLeft }, { top: headerH, left: fixedW });
|
|
29
|
+
downloadBlob(new Blob([buf], { type: "application/pdf" }), "보고서[2026].pdf");
|
|
46
30
|
```
|
|
47
31
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
- `copyElement(event: ClipboardEvent): void` — copy 이벤트 핸들러용. 이벤트 타겟 내 첫 `input/textarea` 의 `value` 를 클립보드 `text/plain` 으로 기록하고 `preventDefault`. clipboardData 없거나 타겟이 Element 아니거나 input 없으면 무동작.
|
|
51
|
-
- event: `ClipboardEvent` — copy 이벤트 객체. `el.addEventListener("copy", copyElement)` 로 등록.
|
|
52
|
-
- `pasteToElement(event: ClipboardEvent): void` — paste 이벤트 핸들러용. 타겟 내 첫 `input/textarea` 의 전체 `value` 를 클립보드 텍스트로 교체하고 `input` 이벤트 dispatch 후 `preventDefault`. 커서 위치·선택 영역은 무시(전체 치환).
|
|
53
|
-
- event: `ClipboardEvent` — paste 이벤트 객체.
|
|
54
|
-
- `getBounds(els: Element[], timeout?: number): Promise<ElementBounds[]>` — `IntersectionObserver` 로 여러 요소의 뷰포트 기준 경계를 한 번에 측정. 중복 제거 후 입력 순서대로 정렬해 반환. 빈 배열이면 즉시 `[]`. 모든 요소 관측 완료 시 resolve, 제한시간 초과 시 `TimeoutError` throw.
|
|
55
|
-
- els: `Element[]` — 측정 대상. 중복은 제거되고 입력 순서로 정렬됨.
|
|
56
|
-
- timeout: `number` — 제한시간(ms). 기본 `5000`. 초과 시 `TimeoutError`.
|
|
57
|
-
- `ElementBounds`(반환 타입) — `target: Element`(측정 요소), `top`/`left`(뷰포트 기준 위치), `width`/`height`(요소 크기). 모두 `boundingClientRect` 값.
|
|
32
|
+
### fetchUrlBytes / DownloadProgress
|
|
58
33
|
|
|
59
34
|
```ts
|
|
60
|
-
|
|
61
|
-
|
|
35
|
+
interface DownloadProgress { receivedLength: number; contentLength: number }
|
|
36
|
+
function fetchUrlBytes(
|
|
37
|
+
url: string,
|
|
38
|
+
options?: { onProgress?: (progress: DownloadProgress) => void },
|
|
39
|
+
): Promise<Uint8Array>;
|
|
62
40
|
```
|
|
63
41
|
|
|
64
|
-
|
|
42
|
+
URL 바이너리를 스트림 reader 로 다운로드하며 진행률을 보고. 큰 파일을 진행 바와 함께 받을 때.
|
|
65
43
|
|
|
66
|
-
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
-
|
|
70
|
-
- options.accept: `string` — 허용 MIME/확장자 필터(input `accept`). 미지정 시 제한 없음. 예: `".png,.jpg"`.
|
|
71
|
-
- options.multiple: `boolean` — 다중 선택 허용. 기본 `false`. 여러 파일 받을 때 `true`.
|
|
72
|
-
- `fetchUrlBytes(url, options?): Promise<Uint8Array>` — URL 바이너리를 스트림으로 다운로드. `response.ok` 아니거나 본문 없으면 Error throw. Content-Length 가 있으면 그 크기로 사전 할당하며 수신량이 그보다 초과/미달이면 Error, 없으면 청크를 모아 `bytes.concat` 으로 병합(chunked encoding).
|
|
73
|
-
- url: `string` — 다운로드 대상 URL.
|
|
74
|
-
- options.onProgress: `(progress: DownloadProgress) => void` — 청크 수신마다 호출(Content-Length 가 있는 경로에서만).
|
|
75
|
-
- `DownloadProgress`(콜백 인자 타입) — `receivedLength`(누적 수신 바이트), `contentLength`(전체 바이트, Content-Length).
|
|
44
|
+
- url: string — 다운로드 대상 URL. `response.ok` 가 아니면 `Error("다운로드 실패: <status> <statusText>")`, 본문 reader 가 없으면 `Error("응답 본문을 읽을 수 없습니다")` throw.
|
|
45
|
+
- options.onProgress: (progress: DownloadProgress) => void — 청크 수신마다 호출되는 진행 콜백. `Content-Length` 헤더가 있는 경로에서만 호출됨(헤더가 없으면 청크를 모아 `bytes.concat` 으로 마지막에 한 번 병합 → chunked encoding 이라 중간 보고 없음). 진행 바 갱신이 필요할 때만 전달.
|
|
46
|
+
- DownloadProgress.receivedLength: number — 지금까지 받은 누적 바이트 수.
|
|
47
|
+
- DownloadProgress.contentLength: number — 전체 바이트 수(`Content-Length` 헤더 값, 없으면 0). 헤더가 있으면 그 크기로 버퍼를 사전 할당하고, 수신량이 헤더 값을 초과·미달하면 무결성 위반으로 Error throw.
|
|
76
48
|
|
|
77
49
|
```ts
|
|
78
|
-
downloadBlob(new Blob([buf]), "보고서.xlsx");
|
|
79
|
-
const files = await openFileDialog({ accept: ".csv", multiple: true });
|
|
80
50
|
const data = await fetchUrlBytes("/api/file", {
|
|
81
51
|
onProgress: (p) => setPct(p.receivedLength / p.contentLength),
|
|
82
52
|
});
|
|
83
53
|
```
|
|
54
|
+
|
|
55
|
+
### openFileDialog
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
function openFileDialog(options?: { accept?: string; multiple?: boolean }): Promise<File[] | undefined>;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
동적 `input[type=file]` 을 만들어 클릭, 파일 선택 대화상자를 표시. 업로드 버튼 핸들러에서 호출.
|
|
62
|
+
|
|
63
|
+
- options.accept: string — 허용 MIME/확장자 필터(input `accept` 에 그대로 전달, 예: `".png,.jpg"`, `"image/*"`). 미지정 시 제한 없음.
|
|
64
|
+
- options.multiple: boolean — 다중 선택 허용. true 면 여러 파일 선택 가능, 기본 `false`(단일). 여러 파일을 한 번에 받을 화면이면 true.
|
|
65
|
+
- 반환: 선택 파일이 있으면 `File[]`, 사용자가 취소하거나(`cancel` 이벤트) 0개 선택이면 `undefined`. 결측을 빈 배열로 뭉개지 않으므로 `== null` 로 취소를 구분.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const files = await openFileDialog({ accept: ".csv", multiple: true });
|
|
69
|
+
if (files == null) return; // 취소
|
|
70
|
+
```
|
|
@@ -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 까지 반영해, 드롭다운/팝업 위치 지정에 바로 쓸 수 있는 좌표 반환. 부모를 못 찾으면(`HTMLElement` 아님) `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
|
-
# @simplysm/core-browser — IndexedDB
|
|
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,23 +17,23 @@ store.close();
|
|
|
17
17
|
|
|
18
18
|
시그니처:
|
|
19
19
|
|
|
20
|
-
-
|
|
21
|
-
- dbName:
|
|
22
|
-
- dbVersion:
|
|
23
|
-
- storeConfigs:
|
|
24
|
-
-
|
|
25
|
-
- name:
|
|
26
|
-
- keyPath:
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
- storeName:
|
|
30
|
-
- mode:
|
|
31
|
-
- fn:
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
20
|
+
- new IndexedDbStore(dbName: string, dbVersion: number, storeConfigs: StoreConfig[]) — DB 이름·버전·스토어 설정으로 생성(연결은 지연, 첫 작업 시 오픈).
|
|
21
|
+
- dbName: string — IndexedDB 데이터베이스 이름.
|
|
22
|
+
- dbVersion: number — DB 버전. 올리면 `onupgradeneeded` 에서 누락 스토어를 생성. 스키마(스토어 추가) 변경 시 증가.
|
|
23
|
+
- storeConfigs: StoreConfig[] — 생성할 오브젝트 스토어 목록.
|
|
24
|
+
- StoreConfig — 스토어 설정 항목.
|
|
25
|
+
- name: string — 오브젝트 스토어 이름. upgrade 시 미존재면 `createObjectStore` 로 생성.
|
|
26
|
+
- keyPath: string — 스토어 keyPath(레코드에서 키로 쓸 속성명).
|
|
27
|
+
- open(): Promise<IDBDatabase> — 연결을 열어 반환. 이미 열렸으면 캐시 반환, 진행 중이면 같은 Promise 공유(중복 오픈 방지). `onupgradeneeded` 시 없는 스토어만 생성. `onversionchange`/`onclose` 시 내부 캐시(`_db`/`_opening`)를 해제해 다음 호출에 재오픈. `onblocked` 면 `Error("다른 연결에 의해 데이터베이스가 차단되었습니다")`, `onerror` 면 원본 에러로 reject. CRUD 가 자동 호출하므로 직접 호출 불필요.
|
|
28
|
+
- withStore<TResult>(storeName, mode, fn): Promise<TResult> — 트랜잭션 1건 안에서 `fn(store)` 실행 후 완료까지 대기. `fn` 이 throw 하면 `tx.abort()` 후 그 에러로 reject(롤백), 정상이면 `oncomplete` 시 결과 resolve, `onerror` 면 `tx.error` 로 reject. 커서 등 저수준 IDB 작업을 감쌀 때.
|
|
29
|
+
- storeName: string — 트랜잭션 대상 스토어.
|
|
30
|
+
- mode: IDBTransactionMode — `"readonly"`(읽기 전용) | `"readwrite"`(읽기·쓰기) | `"versionchange"`. 쓰기 작업이면 `"readwrite"`.
|
|
31
|
+
- fn: (store: IDBObjectStore) => Promise<TResult> — 스토어를 받아 작업하는 콜백.
|
|
32
|
+
- get<TValue>(storeName, key): Promise<TValue | undefined> — 키로 단건 조회. 미존재 시 `undefined`(결측 그대로 반환).
|
|
33
|
+
- put(storeName, value): Promise<void> — 레코드 upsert. value 에 keyPath 속성이 포함돼야 함.
|
|
34
|
+
- delete(storeName, key): Promise<void> — 키로 단건 삭제.
|
|
35
|
+
- getAll<TItem>(storeName): Promise<TItem[]> — 스토어 전체 레코드 배열 반환.
|
|
36
|
+
- close(): void — 연결을 닫고 내부 캐시 해제. 다음 작업 시 재오픈. 페이지 정리 시 호출.
|
|
37
37
|
|
|
38
38
|
주의:
|
|
39
39
|
|
|
@@ -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");
|
|
@@ -54,24 +54,25 @@ const ok = await fs.deleteByPrefix("/root/a"); // 하위 전체 삭제, 삭제
|
|
|
54
54
|
|
|
55
55
|
시그니처:
|
|
56
56
|
|
|
57
|
-
-
|
|
58
|
-
- db:
|
|
59
|
-
- storeName:
|
|
60
|
-
- keyField:
|
|
61
|
-
-
|
|
62
|
-
- kind:
|
|
63
|
-
- dataBase64:
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
|
|
71
|
-
- 반환 항목
|
|
72
|
-
-
|
|
73
|
-
|
|
74
|
-
-
|
|
57
|
+
- new IndexedDbVirtualFs(db: IndexedDbStore, storeName: string, keyField: string) — 백엔드 store·스토어 이름·키 필드명으로 생성.
|
|
58
|
+
- db: IndexedDbStore — 백엔드 저장소.
|
|
59
|
+
- storeName: string — 사용할 오브젝트 스토어 이름.
|
|
60
|
+
- keyField: string — 레코드에서 경로 키를 담는 속성명(스토어 keyPath 와 일치해야 함).
|
|
61
|
+
- VirtualFsEntry — 저장 엔트리 타입.
|
|
62
|
+
- kind: "file" | "dir" — 엔트리 종류. `"file"` = 파일, `"dir"` = 디렉터리. 자식 나열·디렉터리 판정에 사용.
|
|
63
|
+
- dataBase64: string — 파일 내용 base64. 디렉터리거나 빈 파일이면 생략(undefined).
|
|
64
|
+
- getEntry(fullKey): Promise<VirtualFsEntry | undefined> — 전체 경로 키로 단건 조회. 미존재 시 `undefined`.
|
|
65
|
+
- putEntry(fullKey, kind, dataBase64?): Promise<void> — 엔트리 저장. `keyField` 에 `fullKey`, 그리고 `kind`/`dataBase64` 를 함께 기록.
|
|
66
|
+
- fullKey: string — 저장할 전체 경로 키.
|
|
67
|
+
- kind: "file" | "dir" — 저장할 엔트리 종류.
|
|
68
|
+
- dataBase64: string — 파일 데이터(base64). 디렉터리면 생략.
|
|
69
|
+
- deleteByPrefix(keyPrefix): Promise<boolean> — 커서로 키가 `keyPrefix` 자신이거나 `keyPrefix + "/"` 로 시작하는 엔트리 전부 삭제(같은 접두어를 가진 다른 형제 경로 오삭제 방지). 하나라도 지웠으면 `true`, 없으면 `false`. 디렉터리 트리 통째 삭제에.
|
|
70
|
+
- listChildren(prefix): Promise<{ name: string; isDirectory: boolean }[]> — `prefix` 직계 자식만 집계. 키에서 prefix 제거 후 첫 세그먼트를 이름으로 삼고, 하위 세그먼트가 더 있거나 엔트리 `kind === "dir"` 면 디렉터리로 판정. 디렉터리 목록 표시용(재귀 아님).
|
|
71
|
+
- 반환 항목 name: string — 직계 자식 이름(첫 경로 세그먼트).
|
|
72
|
+
- 반환 항목 isDirectory: boolean — 디렉터리 여부.
|
|
73
|
+
- ensureDir(fullKeyBuilder, dirPath): Promise<void> — `dirPath` 상의 각 중간 디렉터리를 부모부터 누적 경로마다 없으면 생성. `dirPath === "/"` 면 루트 1건만 생성. 단일 `withStore("readwrite")` 트랜잭션으로 처리(원자적). 파일 쓰기 전 상위 디렉터리 보장에.
|
|
74
|
+
- fullKeyBuilder: (path: string) => string — 누적 경로(예: `/a`, `/a/b`)를 실제 저장 key 로 변환하는 콜백.
|
|
75
|
+
- dirPath: string — 보장할 디렉터리 경로(`/` 구분). 빈 세그먼트는 무시.
|
|
75
76
|
|
|
76
77
|
주의:
|
|
77
78
|
|