@simplysm/sd-claude 14.0.88 → 14.0.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude/references/sd-simplysm14/README.md +17 -17
- package/claude/references/sd-simplysm14/apis/angular/README.md +27 -53
- package/claude/references/sd-simplysm14/apis/angular/controls.md +37 -105
- package/claude/references/sd-simplysm14/apis/angular/crud.md +46 -43
- package/claude/references/sd-simplysm14/apis/angular/directives.md +22 -32
- package/claude/references/sd-simplysm14/apis/angular/features.md +40 -55
- package/claude/references/sd-simplysm14/apis/angular/infra.md +40 -40
- package/claude/references/sd-simplysm14/apis/angular/layout.md +25 -53
- package/claude/references/sd-simplysm14/apis/angular/overlay.md +70 -82
- package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +44 -39
- package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +21 -36
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +52 -65
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +65 -70
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +33 -35
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +7 -7
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +29 -29
- 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 +13 -12
- package/claude/references/sd-simplysm14/apis/core-common/README.md +222 -98
- package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +102 -53
- package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +128 -0
- package/claude/references/sd-simplysm14/apis/core-common/datetime.md +98 -64
- package/claude/references/sd-simplysm14/apis/core-common/errors.md +91 -0
- package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +34 -28
- package/claude/references/sd-simplysm14/apis/core-common/obj.md +104 -40
- package/claude/references/sd-simplysm14/apis/core-node/README.md +11 -8
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +23 -31
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +33 -22
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +28 -25
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +39 -53
- package/claude/references/sd-simplysm14/apis/core-node/pathx.md +26 -29
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +27 -29
- package/claude/references/sd-simplysm14/apis/excel/README.md +14 -14
- 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 +5 -59
- package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +98 -67
- package/claude/references/sd-simplysm14/apis/orm-common/expr.md +107 -92
- package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +99 -65
- package/claude/references/sd-simplysm14/apis/orm-common/schema.md +83 -98
- package/claude/references/sd-simplysm14/apis/orm-common/types.md +62 -52
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +62 -25
- package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +27 -27
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +12 -15
- 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 +84 -86
- package/claude/references/sd-simplysm14/apis/service-client/orm.md +14 -11
- package/claude/references/sd-simplysm14/apis/service-client/transport.md +33 -10
- package/claude/references/sd-simplysm14/apis/service-common/README.md +37 -23
- package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +9 -9
- package/claude/references/sd-simplysm14/apis/service-common/protocol.md +13 -13
- package/claude/references/sd-simplysm14/apis/service-server/README.md +81 -65
- package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +32 -35
- package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +44 -33
- package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +34 -45
- package/claude/references/sd-simplysm14/apis/storage/README.md +24 -18
- package/claude/skills/sd-demo/SKILL.md +6 -0
- package/claude/skills/sd-impl/SKILL.md +4 -7
- package/claude/skills/sd-spec/SKILL.md +31 -858
- package/claude/skills/sd-spec/references/spec-authoring.md +519 -0
- package/claude/workflows/sd-docs.js +84 -0
- package/package.json +1 -1
- 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/SKILL.md +0 -46
- 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
- /package/claude/{skills/sd-docs/references/doc-rules.md → workflows/sd-docs.rules.md} +0 -0
|
@@ -1,69 +1,69 @@
|
|
|
1
1
|
# @simplysm/capacitor-plugin-auto-update
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Capacitor 앱(Android)에서 APK 설치 인텐트를 실행하고, 서버 또는 외부 저장소의 최신 APK 를 받아 자동 업데이트하는 플러그인. 공개 심볼은 모두 `static` 멤버만 가진 abstract 클래스이거나 타입이라 인스턴스 없이 클래스명으로 직접 호출.
|
|
4
4
|
|
|
5
5
|
## 사용 트리거 인덱스
|
|
6
6
|
|
|
7
|
-
- **AutoUpdate** — 앱
|
|
8
|
-
- **ApkInstaller** —
|
|
9
|
-
- **ApkInstallerPlugin / VersionInfo** — Capacitor 네이티브 브리지 인터페이스
|
|
7
|
+
- **AutoUpdate** — 앱 부팅 시 "최신 확인 → 권한 → 다운로드 → 설치 → 앱 멈춤" 까지 한 번에 돌리는 자동 업데이트 오케스트레이터. 서버 기반(`run`) 또는 외부 저장소 기반(`runByExternalStorage`).
|
|
8
|
+
- **ApkInstaller** — APK 설치/권한/버전 조회를 직접 호출하는 저수준 정적 클래스. 자동 업데이트 흐름을 직접 짜거나 단건 설치·권한 처리만 필요할 때.
|
|
9
|
+
- **ApkInstallerPlugin / VersionInfo** — Capacitor 네이티브 브리지 인터페이스 및 버전 정보 타입. 직접 호출보다는 `ApkInstaller` 가 감싸므로 반환 타입 참조·웹 구현체 작성 시 참조용.
|
|
10
10
|
|
|
11
11
|
## AutoUpdate
|
|
12
12
|
|
|
13
|
-
`abstract class AutoUpdate` — 정적 메서드만 가진 고수준 오케스트레이터. 두 진입점 모두 내부에서 Android 여부 확인 → 설치 권한 확인/요청 → 버전 비교 → 다운로드/설치 → 무한 freeze
|
|
13
|
+
`abstract class AutoUpdate` — 정적 메서드만 가진 고수준 오케스트레이터. 두 진입점 모두 내부에서 Android 여부 확인 → 설치 권한 확인/요청 → 버전 비교 → 다운로드/설치 → 무한 freeze 순으로 일괄 처리. 진행 단계는 `log` 콜백으로 HTML 문자열을 흘려보내고, 도중 발생한 모든 예외를 잡아 `log` 로 에러 메시지를 표시한 뒤 영원히 resolve 되지 않는 무한 대기로 진입해 구버전 실행을 막는다. Android 가 아니면(`navigator.userAgent` 에 "android" 없음) `"Android만 지원됩니다."` 로 throw 되어 catch 에서 표시됨.
|
|
14
14
|
|
|
15
|
-
```
|
|
15
|
+
```typescript
|
|
16
16
|
static run(opt: { log: (messageHtml: string) => void; serviceClient: ServiceClient }): Promise<void>
|
|
17
17
|
static runByExternalStorage(opt: { log: (messageHtml: string) => void; dirPath: string }): Promise<void>
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- `runByExternalStorage` — 외부 저장소의 지정 디렉토리에서 APK 파일들을 스캔해 업데이트. 서버 없이 외부 저장소로 사이드로딩 배포할 때 사용.
|
|
24
|
-
- `log: (messageHtml: string) => void` — `run` 과 동일한 진행/오류 상태 콜백.
|
|
25
|
-
- `dirPath: string` — 외부 저장소(`FileSystem.getStoragePath("external")`) 기준 상대 디렉토리 경로. 이 폴더의 비-디렉토리 항목 중 확장자가 `.apk` 이고 파일명(확장자 제외)이 `[0-9.]` 로만 이뤄진 것을 버전으로 보고 `semver.maxSatisfying(..., "*")` 로 최신을 선정. 파일 없거나 유효 semver 없으면 반환. 설치 파일은 `<dirPath>/<version>.apk`.
|
|
20
|
+
- log: (messageHtml: string) => void — 진행/오류 상태를 HTML 문자열로 받는 콜백. "최신 버전 확인 중...", "권한 확인 중...", 다운로드 진행률 `(NN.NN%)`, 권한 활성화·재시도 버튼 HTML, 오류 메시지 등이 인자로 들어옴. 부팅 스플래시 등에 그대로 innerHTML 로 렌더하는 용도이며 매 단계마다 여러 번 호출됨. 버튼 등 인터랙티브 HTML 이 포함되므로 텍스트가 아닌 HTML 로 렌더해야 재시도/다운로드가 동작.
|
|
21
|
+
- serviceClient: ServiceClient (`run` 전용) — `@simplysm/service-client` 의 서비스 클라이언트. 내부에서 `getService<AutoUpdateService>("AutoUpdate").getLastVersion("android")` 로 최신 버전·다운로드 경로를 조회하고, `serviceClient.hostUrl + downloadPath` 로 `fetchUrlBytes` 다운로드. 서버가 버전 정보를 안 주면(`undefined`) 무동작 반환(업데이트 없음). 서버 연동 배포일 때 사용.
|
|
22
|
+
- dirPath: string (`runByExternalStorage` 전용) — 외부 저장소(`FileSystem.getStoragePath("external")`) 기준 상대 디렉토리 경로. 이 폴더의 비-디렉토리 항목 중 확장자가 `.apk` 이고 파일명(확장자 제외)이 `^[0-9.]*$` 인 것을 버전으로 보고 `semver.maxSatisfying(..., "*")` 로 최신을 선정. 서버 없이 USB/SD 등으로 사이드로딩 배포할 때 사용.
|
|
26
23
|
|
|
27
|
-
|
|
24
|
+
동작 차이: `run` 은 다운로드한 바이트를 `appCache` 저장소의 `latest.apk` 로 써서 설치하고, `runByExternalStorage` 는 외부 저장소의 `<dirPath>/<version>.apk` 를 직접 설치 대상으로 삼는다. 두 경로 모두 현재 버전(`ApkInstaller.getVersionInfo().versionName`)과 비교해 `semver.gt(최신, 현재)` 가 아니면(이미 최신·동일·낮음) 반환하고, 어느 한쪽이라도 유효한 semver 가 아니면 업데이트 확인을 건너뛴다.
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
28
27
|
// 앱 부트스트랩에서
|
|
29
28
|
await AutoUpdate.run({ log: (h) => (statusEl.innerHTML = h), serviceClient });
|
|
29
|
+
// 또는 오프라인 배포 폴더 기준
|
|
30
30
|
await AutoUpdate.runByExternalStorage({ log: (h) => (statusEl.innerHTML = h), dirPath: "myapp-apks" });
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
주의: 업데이트가 없으면 그냥 반환하지만,
|
|
33
|
+
주의: 업데이트가 없으면 그냥 반환하지만, 설치를 진행하거나 오류가 나면 무한 대기로 들어가 영원히 resolve 되지 않으므로 호출 후속 코드에 의존하지 말 것 — 후속 코드는 "업데이트 없음" 경로에서만 실행된다. 권한 미승인 시 설정 화면으로 보낸 뒤 최대 5분(1초 간격 300회) 동안 권한 부여를 폴링. APK manifest 에 `REQUEST_INSTALL_PACKAGES` 가 없거나(`manifest:false`) 권한 확인 자체가 실패하면 "APK 파일을 다시 다운로드하여 설치해야 합니다(코드)." 안내(코드 1·2, 다운로드 링크 버튼 포함)와 함께 throw.
|
|
34
34
|
|
|
35
35
|
## ApkInstaller
|
|
36
36
|
|
|
37
|
-
`abstract class ApkInstaller` —
|
|
37
|
+
`abstract class ApkInstaller` — APK 설치 관련 네이티브 호출을 감싼 저수준 정적 클래스. Android 는 실제 인텐트/권한을 다루고, 브라우저(web)는 `ApkInstallerWeb` 가 대체 구현(설치 시 미지원 alert 후 정상 반환, 권한은 항상 `granted:true`/`manifest:true`). `AutoUpdate` 가 내부에서 쓰며, 자동 업데이트 UX 를 직접 구성하거나 권한·설치만 단독으로 다룰 때 직접 호출.
|
|
38
38
|
|
|
39
|
-
```
|
|
39
|
+
```typescript
|
|
40
40
|
static checkPermissions(): Promise<{ granted: boolean; manifest: boolean }>
|
|
41
41
|
static requestPermissions(): Promise<void>
|
|
42
42
|
static install(apkUri: string): Promise<void>
|
|
43
43
|
static getVersionInfo(): Promise<VersionInfo>
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
- `install(apkUri)` — APK 설치 인텐트 실행.
|
|
51
|
-
- `apkUri: string` — `content://` 형태의 FileProvider URI. 로컬 파일 경로가 아니라 `@simplysm/capacitor-plugin-file-system` 의 `FileSystem.getUri(filePath)` 로 변환한 content URI 를 넘겨야 함.
|
|
52
|
-
- `getVersionInfo()` — 현재 설치된 앱의 버전 정보(`VersionInfo`) 조회. 서버/외부 버전과 비교할 현재 버전 기준값. (web 구현은 `env("__VER__") ?? "0.0.0"` / `versionCode "0"` 반환.)
|
|
46
|
+
- checkPermissions() → { granted, manifest } — 설치 권한 상태 조회. granted: boolean = `REQUEST_INSTALL_PACKAGES` 권한이 사용자에게 승인되었는지(false 면 `requestPermissions` 로 유도), manifest: boolean = 해당 권한이 앱 manifest 에 선언되어 있는지(false 면 권한 요청 자체가 불가 → APK 재설치 필요). 설치 전 사전 점검·재시도 폴링에 사용. 웹에서는 둘 다 true.
|
|
47
|
+
- requestPermissions() → void — 설치 권한 요청. Android 에서는 시스템 설정 화면으로 이동시키며 즉시 부여되지 않으므로 이후 `checkPermissions` 폴링으로 승인 여부를 확인해야 함. 웹에서는 동작 없음.
|
|
48
|
+
- install(apkUri: string) → void — APK 설치 인텐트 실행. apkUri 는 로컬 경로가 아니라 `content://` FileProvider URI (보통 `@simplysm/capacitor-plugin-file-system` 의 `FileSystem.getUri(파일경로)` 결과). 웹에서는 미지원 alert 후 반환.
|
|
49
|
+
- getVersionInfo() → VersionInfo — 현재 설치된 앱 버전 조회. 서버/외부 버전과 비교할 현재 버전 기준값. 웹 구현은 `versionName` 을 빌드 시 주입된 `env("__VER__")`(없으면 `"0.0.0"`), `versionCode` 를 `"0"` 으로 응답.
|
|
53
50
|
|
|
54
|
-
```
|
|
51
|
+
```typescript
|
|
55
52
|
const { granted, manifest } = await ApkInstaller.checkPermissions();
|
|
56
53
|
if (!granted) await ApkInstaller.requestPermissions();
|
|
57
|
-
|
|
58
|
-
await ApkInstaller.install(uri);
|
|
54
|
+
await ApkInstaller.install(await FileSystem.getUri(apkFilePath));
|
|
59
55
|
```
|
|
60
56
|
|
|
61
57
|
## ApkInstallerPlugin / VersionInfo
|
|
62
58
|
|
|
63
|
-
Capacitor 네이티브 브리지 타입. `ApkInstaller` 의 정적
|
|
59
|
+
Capacitor `registerPlugin("ApkInstaller")` 로 등록되는 네이티브 브리지 인터페이스와 버전 정보 타입. `ApkInstaller` 의 정적 메서드가 이 인터페이스 구현체(네이티브 또는 `ApkInstallerWeb`)에 위임하므로, 직접 구현·호출할 일은 드물고 주로 `getVersionInfo` 반환 형태 참조·웹 구현체 작성 시에만 본다.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
interface VersionInfo {
|
|
63
|
+
versionName: string;
|
|
64
|
+
versionCode: string;
|
|
65
|
+
}
|
|
64
66
|
|
|
65
|
-
```ts
|
|
66
|
-
interface VersionInfo { versionName: string; versionCode: string; }
|
|
67
67
|
interface ApkInstallerPlugin {
|
|
68
68
|
install(options: { uri: string }): Promise<void>;
|
|
69
69
|
checkPermissions(): Promise<{ granted: boolean; manifest: boolean }>;
|
|
@@ -72,9 +72,7 @@ interface ApkInstallerPlugin {
|
|
|
72
72
|
}
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
- `ApkInstallerPlugin.requestPermissions()` — 설치 권한 요청(설정 화면 이동).
|
|
80
|
-
- `ApkInstallerPlugin.getVersionInfo()` — 현재 앱 버전 정보(`VersionInfo`) 조회.
|
|
75
|
+
- VersionInfo.versionName: string — 사람이 읽는 표시 버전(semver, 예 `"1.2.3"`). `AutoUpdate` 의 버전 비교(`semver.gt`/`semver.valid`)는 이 값을 semver 로 사용.
|
|
76
|
+
- VersionInfo.versionCode: string — Android 빌드 버전 코드의 문자열 표현. 웹 구현에서는 `"0"` 고정. 현재 업데이트 판정 로직은 `versionName` 만 사용하며 이 값은 표시·식별용.
|
|
77
|
+
- ApkInstallerPlugin.install(options: { uri: string }) — content URI APK 설치. `ApkInstaller.install(apkUri)` 가 `{ uri: apkUri }` 형태로 래핑해 전달하며 uri 는 `content://` FileProvider URI.
|
|
78
|
+
- ApkInstallerPlugin.checkPermissions / requestPermissions / getVersionInfo — 각각 `ApkInstaller` 의 동명 정적 메서드가 그대로 위임하는 원본 브리지 메서드(권한 조회 / 권한 요청 / 현재 버전 조회).
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# @simplysm/capacitor-plugin-file-system
|
|
2
2
|
|
|
3
|
-
Capacitor 기반
|
|
3
|
+
Capacitor 기반 파일 시스템 접근 플러그인. 네이티브(Android)에서 외부/앱 저장소를 직접 다루고(Android 11+ 는 `MANAGE_EXTERNAL_STORAGE`, 10- 는 `READ/WRITE_EXTERNAL_STORAGE` 권한), 브라우저에서는 IndexedDB 기반 에뮬레이션으로 동일 API 를 제공.
|
|
4
4
|
|
|
5
5
|
## 사용 트리거 인덱스
|
|
6
6
|
|
|
7
|
-
- **FileSystem** — 파일 읽기/쓰기, 디렉토리 조회/생성, 삭제, 권한 확인/요청, 저장소
|
|
8
|
-
- **StorageType** — `FileSystem.getStoragePath` 인자로
|
|
7
|
+
- **FileSystem** — 파일 읽기/쓰기, 디렉토리 조회/생성, 삭제, 존재 확인, 권한 확인/요청, 저장소 경로/URI 조회가 필요할 때 쓰는 static 진입점 클래스(주 사용처).
|
|
8
|
+
- **StorageType** — `FileSystem.getStoragePath` 인자로 넘길 저장소 위치 유형 리터럴을 고를 때.
|
|
9
9
|
- **FileInfo** — `FileSystem.readdir` 가 반환하는 항목 정보(이름 + 디렉토리 여부). 디렉토리 순회 시.
|
|
10
|
-
- **FileSystemPlugin** — Capacitor 네이티브 브릿지 원형 인터페이스 타입. 보통 `FileSystem` 가 래핑하므로 직접 호출 불필요. 커스텀 web 구현
|
|
10
|
+
- **FileSystemPlugin** — Capacitor 네이티브 브릿지 원형 인터페이스 타입. 보통 `FileSystem` 가 래핑하므로 직접 호출 불필요. 커스텀 web 구현 작성·타입 참조 시에만.
|
|
11
11
|
|
|
12
12
|
## FileSystem (static 클래스)
|
|
13
13
|
|
|
14
|
-
모든
|
|
14
|
+
`abstract class`, 모든 멤버 `static async`. 인스턴스 생성 없이 `FileSystem.xxx()` 호출. 내부적으로 `registerPlugin<FileSystemPlugin>("FileSystem")` 으로 얻은 네이티브/web 구현에 위임하고, 플러그인의 `{ ... }` 래퍼 결과를 평탄화해 반환.
|
|
15
15
|
|
|
16
16
|
- `checkPermissions(): Promise<boolean>` — 파일 접근 권한 보유 여부. true 면 권한 있음. 읽기/쓰기 전 사전 확인용. 플러그인의 `{ granted }` 를 boolean 으로 풀어 반환.
|
|
17
17
|
- `requestPermissions(): Promise<void>` — 권한 요청. Android 11+ 는 설정 화면으로 이동, Android 10- 는 권한 대화상자 표시. `checkPermissions()` 가 false 일 때 호출.
|
|
@@ -58,8 +58,8 @@ for (const f of await FileSystem.readdir(`${base}/notes`)) {
|
|
|
58
58
|
- `readdir(options: { path: string }): Promise<{ files: FileInfo[] }>` — `path` 디렉토리 항목 목록을 `files` 로 반환.
|
|
59
59
|
- `getStoragePath(options: { type: StorageType }): Promise<{ path: string }>` — `type` 저장소의 실제 경로를 `path` 로 반환.
|
|
60
60
|
- `getUri(options: { path: string }): Promise<{ uri: string }>` — `path` 파일의 FileProvider URI 를 `uri` 로 반환.
|
|
61
|
-
- `writeFile(options: { path: string; data: string; encoding?: "utf8" | "base64" }): Promise<void>` — `data`(문자열)를 `encoding`(`"utf8"
|
|
62
|
-
- `readFile(options: { path: string; encoding?: "utf8" | "base64" }): Promise<{ data: string }>` — `path` 내용을 `encoding`(`"utf8"`=텍스트, `"base64"
|
|
61
|
+
- `writeFile(options: { path: string; data: string; encoding?: "utf8" | "base64" }): Promise<void>` — `data`(문자열)를 `encoding`(`"utf8"`=텍스트 그대로, `"base64"`=base64 문자열을 바이너리로 디코드해 기록, 생략 시 구현 기본)으로 `path` 에 기록.
|
|
62
|
+
- `readFile(options: { path: string; encoding?: "utf8" | "base64" }): Promise<{ data: string }>` — `path` 내용을 `encoding`(`"utf8"`=텍스트, `"base64"`=바이너리를 base64 문자열로 인코딩)으로 읽어 `data` 로 반환. 플러그인 레벨에서 바이너리는 항상 base64 문자열로 주고받음(`FileSystem` 가 `Bytes` ↔ base64 변환 담당).
|
|
63
63
|
- `remove(options: { path: string }): Promise<void>` — `path` 파일/디렉토리 재귀 삭제.
|
|
64
64
|
- `mkdir(options: { path: string }): Promise<void>` — `path` 디렉토리 재귀 생성.
|
|
65
65
|
- `exists(options: { path: string }): Promise<{ exists: boolean }>` — `path` 존재 여부를 `exists` 로 반환.
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
# @simplysm/capacitor-plugin-intent
|
|
2
2
|
|
|
3
|
-
Android 인텐트 송수신 Capacitor 플러그인. 브로드캐스트
|
|
3
|
+
Android 인텐트 송수신 Capacitor 플러그인. 브로드캐스트 수신/전송, 실행 인텐트 조회, `startActivityForResult` 외부 Activity 실행·결과 수신을 제공하며 바코드 스캐너·PDA 등 산업용 디바이스 연동에 사용. 웹 환경에서는 미지원 스텁(`IntentWeb`)이 경고 로그 후 빈/스텁 결과를 반환하므로 실제 인텐트 처리는 Android 디바이스에서만 일어남.
|
|
4
4
|
|
|
5
5
|
## 사용 트리거 인덱스
|
|
6
6
|
|
|
7
|
-
- **Intent** —
|
|
8
|
-
- **IntentResult / StartActivityForResultOptions / StartActivityForResultResult** — `Intent` 메서드의 입출력 타입. 콜백
|
|
9
|
-
- **IntentPlugin** — Capacitor 네이티브 플러그인
|
|
7
|
+
- **Intent** — 인텐트 작업의 정적 진입점. 브로드캐스트 구독/전송, 실행 인텐트 조회, newIntent 리스너, 외부 Activity 실행을 할 때. 아래 인라인 섹션 참조.
|
|
8
|
+
- **IntentResult / StartActivityForResultOptions / StartActivityForResultResult** — `Intent` 메서드의 입출력 타입. 콜백 결과·실행 옵션·반환값을 타입으로 다룰 때. 아래 인라인 섹션 참조.
|
|
9
|
+
- **IntentPlugin** — `registerPlugin` 에 쓰이는 Capacitor 네이티브 플러그인 계약. 보통 직접 호출하지 않고 `Intent` 정적 메서드를 거치며, 웹 스텁·네이티브 구현의 저수준 계약 확인용. 아래 인라인 섹션 참조.
|
|
10
10
|
|
|
11
11
|
## Intent (정적 클래스)
|
|
12
12
|
|
|
13
|
-
`abstract class Intent` — 정적 메서드만 가진 진입점.
|
|
13
|
+
`abstract class Intent` — 정적 메서드만 가진 진입점. 인스턴스화하지 않고 `Intent.xxx()` 형태로 호출하며 모든 메서드는 비동기(`Promise`). 내부적으로 `registerPlugin<IntentPlugin>("Intent")` 로 얻은 플러그인(웹은 `IntentWeb`)을 래핑.
|
|
14
14
|
|
|
15
|
-
- `Intent.subscribe(filters: string[], callback: (result: IntentResult) => void): Promise<() => Promise<void>>` — 브로드캐스트 수신기 등록. `filters` = 수신할 인텐트 액션 문자열 배열(예:
|
|
16
|
-
- `Intent.unsubscribeAll(): Promise<void>` — 등록된 모든 브로드캐스트 수신기를 한 번에 해제. 개별 해제 함수를 보관하지 않았거나
|
|
17
|
-
- `Intent.send(options: { action: string; extras?: Record<string, unknown> }): Promise<void>` — 브로드캐스트 전송. `action`(필수) = 전송할 인텐트 액션 문자열, `extras`(
|
|
18
|
-
- `Intent.getLaunchIntent(): Promise<IntentResult>` — 앱을 실행시킨
|
|
19
|
-
- `Intent.addListener(eventName: "newIntent", callback: (result: IntentResult) => void): Promise<PluginListenerHandle>` — 앱 실행 중 새로 들어오는 인텐트 리스너 등록. `eventName` 은 `"newIntent"` 리터럴
|
|
20
|
-
- `Intent.removeAllListeners(): Promise<void>` — `addListener` 로 등록한 모든 이벤트 리스너를 일괄 제거. 화면 정리 시 newIntent 리스너를 한 번에 해제할
|
|
21
|
-
- `Intent.startActivityForResult(options: StartActivityForResultOptions): Promise<StartActivityForResultResult>` — 외부 Activity 를 실행하고 결과를 수신. 결제·서명 등 외부 앱에 작업을 위임하고
|
|
15
|
+
- `Intent.subscribe(filters: string[], callback: (result: IntentResult) => void): Promise<() => Promise<void>>` — 브로드캐스트 수신기 등록. `filters` = 수신할 인텐트 액션 문자열 배열(예: `"com.symbol.datawedge.api.RESULT_ACTION"`). `callback` = 매칭 브로드캐스트 도착 시 호출되며, 등록 직후 플러그인이 보내는 `{ id }` 만 담긴 초기 resolve(`result.action == null`)는 내부에서 걸러져 호출되지 않음. 반환값은 구독 해제 함수로, 호출 시 내부 `id` 로 해당 수신기만 해제. 스캐너 등 지속 수신 화면 진입 시 등록하고 이탈 시 반환 함수로 해제하는 용도.
|
|
16
|
+
- `Intent.unsubscribeAll(): Promise<void>` — 등록된 모든 브로드캐스트 수신기를 한 번에 해제. 개별 해제 함수를 보관하지 않았거나 화면 정리 시 전체를 정리할 때.
|
|
17
|
+
- `Intent.send(options: { action: string; extras?: Record<string, unknown> }): Promise<void>` — 브로드캐스트 전송. `action`(필수) = 전송할 인텐트 액션 문자열, `extras`(선택, 미지정 가능) = 함께 보낼 추가 키-값 데이터. 스캐너 트리거 토글 등 디바이스에 명령을 보낼 때.
|
|
18
|
+
- `Intent.getLaunchIntent(): Promise<IntentResult>` — 앱을 실행시킨 인텐트 조회. 외부 인텐트로 앱이 기동됐는지·그 `extras` 를 앱 시작 시점에 읽을 때. 웹 스텁은 항상 빈 객체 `{}` 반환.
|
|
19
|
+
- `Intent.addListener(eventName: "newIntent", callback: (result: IntentResult) => void): Promise<PluginListenerHandle>` — 앱 실행 중 새로 들어오는 인텐트 리스너 등록. `eventName` 은 `"newIntent"` 리터럴 고정(이미 떠 있는 앱에 인텐트가 재전달되는 singleTop 등 상황에서 발생). 반환 핸들의 `handle.remove()` 로 개별 해제.
|
|
20
|
+
- `Intent.removeAllListeners(): Promise<void>` — `addListener` 로 등록한 모든 이벤트 리스너를 일괄 제거. 화면 정리 시 newIntent 리스너를 한 번에 해제할 때.
|
|
21
|
+
- `Intent.startActivityForResult(options: StartActivityForResultOptions): Promise<StartActivityForResultResult>` — 외부 Activity 를 실행하고 결과를 수신. 결제·서명 등 외부 앱에 작업을 위임하고 응답을 되받아야 할 때. 반환 `resultCode` 로 성공 판정(Android 규약상 `-1` = RESULT_OK, `0` = RESULT_CANCELED).
|
|
22
22
|
|
|
23
23
|
사용 예:
|
|
24
24
|
|
|
@@ -36,36 +36,36 @@ if (res.resultCode === -1) { /* RESULT_OK */ }
|
|
|
36
36
|
await unsub(); // 개별 해제
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
주의: 웹
|
|
39
|
+
주의: 웹 플랫폼(`IntentWeb`)에서는 `subscribe`·`send`·`startActivityForResult` 가 실제 동작 없이 경고 로그(`createLogger("capacitor:intent")` → `"웹 환경에서는 지원하지 않습니다."`)만 남긴다. 이때 `subscribe` 는 `{ id: "web-stub" }`(콜백 미호출), `getLaunchIntent` 는 `{}`, `startActivityForResult` 는 `{ resultCode: 0 }`(항상 CANCELED 로 보임)을 반환하고, `unsubscribe`·`unsubscribeAll` 은 무동작.
|
|
40
40
|
|
|
41
41
|
## 입출력 타입
|
|
42
42
|
|
|
43
|
-
`Intent` 메서드의 결과·옵션을
|
|
43
|
+
`Intent` 메서드의 결과·옵션을 타입으로 다룰 때 참조하는 인터페이스.
|
|
44
44
|
|
|
45
|
-
- `IntentResult` — `subscribe
|
|
46
|
-
- `action?: string` — 인텐트(브로드캐스트) 액션 문자열. 미수신/미설정일 수 있어 선택. `subscribe` 콜백에는 이 값이 채워진 실제 이벤트만
|
|
47
|
-
- `extras?: Record<string, unknown>` — 인텐트에 담긴 추가 키-값 데이터(스캔된 바코드 값 등).
|
|
48
|
-
- `StartActivityForResultOptions` — `startActivityForResult` 입력. 모든 필드 선택이며
|
|
49
|
-
- `action?: string` — 인텐트
|
|
50
|
-
- `uri?: string` — 인텐트 data URI.
|
|
51
|
-
- `extras?: Record<string, unknown>` — 대상 Activity 로 전달할 추가 데이터.
|
|
52
|
-
- `type?: string` — MIME 타입.
|
|
53
|
-
- `packageName?: string` — 실행할 특정 앱
|
|
54
|
-
- `className?: string` — 실행할 특정 Activity
|
|
55
|
-
- `flags?: number` — Android Intent flags
|
|
45
|
+
- `IntentResult` — `subscribe`·`addListener` 콜백 인자 및 `getLaunchIntent` 반환 타입.
|
|
46
|
+
- `action?: string` — 인텐트(브로드캐스트) 액션 문자열. 미수신/미설정일 수 있어 선택. `subscribe` 콜백에는 이 값이 채워진 실제 이벤트만 전달됨(초기 resolve 구분 기준).
|
|
47
|
+
- `extras?: Record<string, unknown>` — 인텐트에 담긴 추가 키-값 데이터(스캔된 바코드 값 등). 없을 수 있음.
|
|
48
|
+
- `StartActivityForResultOptions` — `startActivityForResult` 입력. 모든 필드 선택이며 대상 인텐트를 암시적(액션·URI·타입) 또는 명시적(패키지·클래스)으로 구성.
|
|
49
|
+
- `action?: string` — 인텐트 액션. 암시적 인텐트로 대상을 지정할 때.
|
|
50
|
+
- `uri?: string` — 인텐트 data URI. URI 로 대상(뷰어·다이얼러 등)을 지정할 때.
|
|
51
|
+
- `extras?: Record<string, unknown>` — 대상 Activity 로 전달할 추가 키-값 데이터.
|
|
52
|
+
- `type?: string` — MIME 타입. 데이터 형식으로 처리 대상 앱을 좁힐 때.
|
|
53
|
+
- `packageName?: string` — 실행할 특정 앱 패키지명. 명시적 인텐트화(특정 앱으로만 실행)할 때.
|
|
54
|
+
- `className?: string` — 실행할 특정 Activity 클래스명. `packageName` 과 함께 컴포넌트를 직접 지정할 때.
|
|
55
|
+
- `flags?: number` — Android Intent flags 비트값. 실행 모드(새 태스크 시작 등) 제어가 필요할 때.
|
|
56
56
|
- `StartActivityForResultResult` — `startActivityForResult` 반환.
|
|
57
|
-
- `resultCode: number` — Activity 결과 코드. Android 규약상 `-1` = RESULT_OK, `0` = RESULT_CANCELED. 웹 스텁은 `0`
|
|
57
|
+
- `resultCode: number` — Activity 결과 코드. Android 규약상 `-1` = RESULT_OK, `0` = RESULT_CANCELED. 성공 분기 판단에 사용(웹 스텁은 `0`).
|
|
58
58
|
- `data?: { action?: string; uri?: string; extras?: Record<string, unknown> }` — 결과 인텐트 데이터. `action` = 결과 인텐트 액션, `uri` = 결과 data URI, `extras` = 결과 추가 데이터. 취소 등으로 데이터가 없으면 부재.
|
|
59
59
|
|
|
60
60
|
## IntentPlugin
|
|
61
61
|
|
|
62
|
-
`interface IntentPlugin` — `registerPlugin<IntentPlugin>("Intent")` 에 쓰이는 Capacitor 네이티브 플러그인 계약. 웹 구현(`IntentWeb`)과 Android 네이티브가 이를 구현. 일반 사용 코드는 `Intent` 정적 메서드를 쓰고 이 인터페이스를 직접 호출하지
|
|
62
|
+
`interface IntentPlugin` — `registerPlugin<IntentPlugin>("Intent")` 에 쓰이는 Capacitor 네이티브 플러그인 계약. 웹 구현(`IntentWeb`)과 Android 네이티브가 이를 구현. 일반 사용 코드는 `Intent` 정적 메서드를 쓰고 이 인터페이스를 직접 호출하지 않으며, 네이티브 구현·웹 스텁이 충족해야 할 저수준 계약을 확인할 때만 참조.
|
|
63
63
|
|
|
64
|
-
- `subscribe(options: { filters: string[] }, callback: (result: IntentResult) => void): Promise<{ id: string }>` — 수신기 등록 후 해제용 `id` 반환. `Intent.subscribe` 가 이 `id` 를 보관해 반환 함수에서 `unsubscribe` 에 넘김.
|
|
64
|
+
- `subscribe(options: { filters: string[] }, callback: (result: IntentResult) => void): Promise<{ id: string }>` — 수신기 등록 후 해제용 `id` 반환. `Intent.subscribe` 가 이 `id` 를 클로저로 보관해 반환 함수에서 `unsubscribe` 에 넘김.
|
|
65
65
|
- `unsubscribe(options: { id: string }): Promise<void>` — 주어진 `id` 의 수신기만 해제.
|
|
66
66
|
- `unsubscribeAll(): Promise<void>` — 모든 수신기 해제.
|
|
67
67
|
- `send(options: { action: string; extras?: Record<string, unknown> }): Promise<void>` — 브로드캐스트 전송.
|
|
68
68
|
- `getLaunchIntent(): Promise<IntentResult>` — 실행 인텐트 조회.
|
|
69
|
-
- `addListener(eventName: "newIntent", listenerFunc: (data: IntentResult) => void): Promise<PluginListenerHandle>` — newIntent 이벤트 리스너 등록, 해제용 핸들 반환.
|
|
69
|
+
- `addListener(eventName: "newIntent", listenerFunc: (data: IntentResult) => void): Promise<PluginListenerHandle>` — `"newIntent"` 이벤트 리스너 등록, 해제용 표준 Capacitor 핸들 반환.
|
|
70
70
|
- `removeAllListeners(): Promise<void>` — 모든 리스너 제거.
|
|
71
71
|
- `startActivityForResult(options: StartActivityForResultOptions): Promise<StartActivityForResultResult>` — 외부 Activity 실행 및 결과 수신.
|
|
@@ -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 저장소로 에뮬레이션됨. export 심볼은 정적 클래스 `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)·반환 항목 형태를 다룰 때 참조.
|
|
10
9
|
|
|
11
|
-
## UsbStorage
|
|
10
|
+
## UsbStorage
|
|
12
11
|
|
|
13
|
-
모든
|
|
12
|
+
`abstract class` 이며 모든 멤버가 `static`. 인스턴스화하지 않고 `UsbStorage.메서드()` 로 호출. 내부적으로 `registerPlugin("UsbStorage")` 로 얻은 네이티브/웹 구현에 위임하고 래퍼 객체(`{ devices }` 등)에서 값을 꺼내 평탄화해 반환. 장치 지정은 항상 `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`) 로 읽음. 파일이 없거나 네이티브가 데이터 없음을 주면 `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
|
+
const entries = await UsbStorage.readdir(filter, "/");
|
|
34
|
+
for (const e of entries.filter((x) => !x.isDirectory)) {
|
|
35
|
+
const data = await UsbStorage.readFile(filter, `/${e.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
|
+
브라우저 전용 유틸리티. `Element`/`HTMLElement` 프로토타입 확장(사이드 이펙트)과 파일 다운로드·업로드, IndexedDB 래퍼를 제공.
|
|
4
|
+
|
|
5
|
+
> 패키지의 어떤 심볼이든 import 하면 `index.ts` 가 `import "./extensions/..."` 를 실행해 프로토타입 확장이 자동 등록됨(`element-ext`, `html-element-ext` 의 사이드 이펙트). 별도 초기화 호출 없이 `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`/`DownloadProgress`, `openFileDialog`) — Blob 을 파일로 내려받거나, URL 바이너리를 진행률과 함께 받거나, 파일 선택 대화상자를 코드에서 띄울 때. (아래 인라인 섹션)
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## 파일 다운로드/업로드
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
브라우저에서 파일을 내려받거나, URL 에서 바이너리를 받거나, 사용자가 파일을 고르게 할 때 쓰는 독립 함수 묶음.
|
|
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 을 objectURL 로 만들어 동적 `a[download]` 클릭으로 저장. objectURL 은 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` — 저장할 데이터.
|
|
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
|
+
```
|