@simplysm/sd-claude 14.0.98 → 14.0.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/claude/references/sd-simplysm14/README.md +16 -16
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +81 -153
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +179 -205
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +71 -57
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +49 -109
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +58 -86
  7. package/claude/references/sd-simplysm14/apis/angular/kanban.md +32 -40
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +38 -52
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +86 -110
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +54 -86
  11. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +82 -74
  12. package/claude/references/sd-simplysm14/apis/angular/sheet.md +56 -80
  13. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +15 -15
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +21 -21
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +79 -53
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +9 -11
  17. package/claude/references/sd-simplysm14/apis/core-browser/README.md +15 -15
  18. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +20 -20
  19. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +18 -18
  20. package/claude/references/sd-simplysm14/apis/core-common/README.md +20 -49
  21. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +66 -55
  22. package/claude/references/sd-simplysm14/apis/core-common/collection-ext.md +83 -56
  23. package/claude/references/sd-simplysm14/apis/core-common/errors.md +32 -21
  24. package/claude/references/sd-simplysm14/apis/core-common/obj.md +57 -39
  25. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +36 -30
  26. package/claude/references/sd-simplysm14/apis/core-common/value-types.md +69 -41
  27. package/claude/references/sd-simplysm14/apis/core-node/README.md +4 -4
  28. package/claude/references/sd-simplysm14/apis/core-node/consola.md +15 -13
  29. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +11 -7
  30. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +8 -8
  31. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +29 -20
  32. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +14 -6
  33. package/claude/references/sd-simplysm14/apis/core-node/worker.md +3 -3
  34. package/claude/references/sd-simplysm14/apis/excel/README.md +3 -3
  35. package/claude/references/sd-simplysm14/apis/excel/cell.md +32 -32
  36. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +23 -24
  37. package/claude/references/sd-simplysm14/apis/excel/style.md +24 -30
  38. package/claude/references/sd-simplysm14/apis/excel/utils.md +20 -23
  39. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +60 -71
  40. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +36 -36
  41. package/claude/references/sd-simplysm14/apis/lint/README.md +7 -9
  42. package/claude/references/sd-simplysm14/apis/lint/recommended.md +59 -37
  43. package/claude/references/sd-simplysm14/apis/lint/rules.md +81 -74
  44. package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -6
  45. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +112 -78
  46. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +131 -75
  47. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +126 -82
  48. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +170 -113
  49. package/claude/references/sd-simplysm14/apis/orm-common/types.md +102 -48
  50. package/claude/references/sd-simplysm14/apis/orm-node/README.md +12 -13
  51. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +3 -3
  52. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +5 -5
  53. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +67 -65
  54. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +130 -123
  55. package/claude/references/sd-simplysm14/apis/service-client/README.md +63 -63
  56. package/claude/references/sd-simplysm14/apis/service-client/orm.md +22 -22
  57. package/claude/references/sd-simplysm14/apis/service-client/transport.md +30 -26
  58. package/claude/references/sd-simplysm14/apis/service-common/README.md +8 -8
  59. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +13 -6
  60. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +1 -1
  61. package/claude/references/sd-simplysm14/apis/service-server/README.md +43 -47
  62. package/claude/references/sd-simplysm14/apis/service-server/built-in-services.md +35 -0
  63. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +20 -19
  64. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +23 -25
  65. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +9 -9
  66. package/claude/references/sd-simplysm14/apis/storage/README.md +26 -26
  67. package/claude/references/sd-simplysm14/manuals/client-component.md +9 -1
  68. package/claude/references/sd-simplysm14/manuals/client-crud.md +1 -1
  69. package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -0
  70. package/claude/references/sd-simplysm14/manuals/client-service.md +1 -0
  71. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +1 -0
  72. package/claude/references/sd-simplysm14/manuals/client-ssg.md +1 -0
  73. package/claude/sd-system-prompt.md +11 -26
  74. package/claude/skills/sd-docs/references/subagent-prompt.md +4 -3
  75. package/claude/skills/sd-spec/SKILL.md +87 -18
  76. package/claude/skills/sd-spec/references/format.md +2 -2
  77. package/package.json +1 -1
@@ -1,27 +1,27 @@
1
1
  # @simplysm/capacitor-plugin-auto-update
2
2
 
3
- Android APK 자동 업데이트 Capacitor 플러그인. 부팅 시 서버 또는 외부 저장소의 최신 APK 를 받아 설치하는 오케스트레이터(`AutoUpdate`)와, APK 설치·설치 권한·앱 버전 조회를 다루는 저수준 정적 클래스(`ApkInstaller`)·타입을 제공. 공개 심볼은 모두 `static` 멤버만 가진 abstract 클래스이거나 인터페이스라 인스턴스 없이 클래스명으로 직접 호출. 비-Android(웹)에서는 폴백 구현으로 설치는 no-op, 권한은 항상 통과.
3
+ Android APK 자동 업데이트 Capacitor 플러그인. 부팅 시 서버(`ServiceClient`) 또는 외부 저장소 폴더의 최신 APK 를 받아 설치하는 오케스트레이터(`AutoUpdate`)와, APK 설치·설치 권한·앱 버전 조회를 다루는 저수준 정적 클래스(`ApkInstaller`)·네이티브 브리지 타입을 제공. 공개 심볼은 모두 `static` 멤버만 가진 abstract 클래스이거나 인터페이스라 인스턴스 없이 클래스명으로 직접 호출. 비-Android(웹)에서는 폴백 구현으로 설치는 미지원 alert 후 no-op, 권한은 항상 통과, 버전은 빌드 주입 env 사용.
4
4
 
5
5
  ## 사용 트리거 인덱스
6
6
 
7
- - **AutoUpdate** — 앱 부팅 시점에 "최신 확인 → 권한 → 다운로드 → 설치 → 앱 멈춤" 까지 한 번에 도는 진입점. 서버(`ServiceClient`) 연동 업데이트면 `run`, USB/외장 저장소 폴더에서 가져오면 `runByExternalStorage`.
7
+ - **AutoUpdate** — 앱 부팅 시점에 "최신 확인 → 권한 → 다운로드 → 설치 → 앱 freeze" 한 번에 도는 진입점. 서버 연동 배포면 `run`, USB/SD 등 외부 저장소 폴더 사이드로딩이면 `runByExternalStorage`.
8
8
  - **ApkInstaller** — `AutoUpdate` 를 거치지 않고 설치 권한 확인/요청, 특정 APK 설치, 현재 앱 버전 직접 조회가 필요할 때 쓰는 저수준 정적 클래스.
9
- - **ApkInstallerPlugin / VersionInfo** — Capacitor 네이티브 브리지 인터페이스와 버전 정보 형태. `ApkInstaller` 가 감싸므로 직접 호출보다는 반환 타입 참조용.
9
+ - **ApkInstallerPlugin / VersionInfo** — Capacitor 네이티브 브리지 인터페이스와 버전 정보 형태. `ApkInstaller` 가 감싸므로 직접 호출보다는 반환 타입 참조·웹 폴백 구현 작성용.
10
10
 
11
11
  ## AutoUpdate
12
12
 
13
- `abstract class AutoUpdate` — 정적 메서드만 가진 부트 시 업데이트 오케스트레이터. 두 진입점 모두 Android 여부 확인 → 설치 권한 확인/요청 → 버전 비교 → 다운로드/설치 → 무한 freeze 순으로 일괄 처리. 진행 단계는 `log` 콜백으로 HTML 문자열을 흘려보내고, 도중 발생한 모든 예외를 잡아 `log` 로 오류 메시지를 표시한 뒤 영원히 resolve 되지 않는 무한 대기로 진입해 구버전 실행을 막음.
13
+ `abstract class AutoUpdate` — 정적 메서드만 가진 부트 시 업데이트 오케스트레이터. 두 진입점 모두 Android 여부 확인 → 설치 권한 확인/요청 → 버전 비교 → 다운로드/설치 → 무한 freeze 순으로 일괄 처리. 진행 단계마다 `log` 콜백으로 HTML 문자열을 흘려보내고, 도중 발생한 모든 예외를 잡아 `log` 로 오류 메시지를 표시한 뒤 영원히 resolve 되지 않는 무한 대기(`_freezeApp`)로 진입해 구버전 실행을 막음.
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
- - opt.log: (messageHtml: string) => void — 진행/오류 상태를 HTML 문자열로 받는 콜백. 매 단계마다 여러 번 호출되며 `"최신 버전 확인 중..."`, `"권한 확인 중..."`, 다운로드 진행률 `(NN.NN%)`, 권한 활성화·재시도 버튼 HTML, 오류 메시지 등이 인자로 들어옴. 부팅 스플래시 등에 그대로 `innerHTML` 로 렌더하는 용도 — 재시도/다운로드 버튼이 HTML 이므로 텍스트가 아닌 HTML 로 렌더해야 동작.
21
- - opt.serviceClient: ServiceClient (`run` 전용) — `@simplysm/service-client` 의 서비스 클라이언트. 내부에서 `getService<AutoUpdateService>("AutoUpdate").getLastVersion("android")` 로 최신 버전·다운로드 경로를 조회하고 `serviceClient.hostUrl + downloadPath` 로 `fetchUrlBytes` 다운로드. 서버 연동 배포일 때 사용.
20
+ - opt.log: (messageHtml: string) => void — 진행/오류 상태를 HTML 문자열로 받는 콜백. 매 단계마다 여러 번 호출되며 `"최신 버전 확인 중..."`·`"권한 확인 중..."`·`"최신 버전 파일 다운로드 중...(NN.NN%)"`·권한 활성화/재시도 버튼 HTML·"최신 버전을 설치하고 재시작해 주세요." 설치 안내·오류 메시지 등이 인자로 들어옴. 재시도/다운로드 버튼이 HTML 이므로 텍스트가 아니라 `innerHTML` 등 HTML 로 렌더해야 동작.
21
+ - opt.serviceClient: ServiceClient (`run` 전용) — `@simplysm/service-client` 의 서비스 클라이언트. 내부에서 `getService<AutoUpdateService>("AutoUpdate").getLastVersion("android")` 로 서버의 최신 버전(`version`)·다운로드 경로(`downloadPath`)를 조회하고 `serviceClient.hostUrl + downloadPath` 로 `fetchUrlBytes` 다운로드. 서버 연동 배포일 때 사용.
22
22
  - opt.dirPath: string (`runByExternalStorage` 전용) — `external` 저장소(`FileSystem.getStoragePath("external")`) 루트 기준 상대 디렉토리 경로. 이 폴더의 비-디렉토리 항목 중 확장자가 `.apk` 이고 파일명(확장자 제외)이 `^[0-9.]*$`(숫자·점) 인 것을 버전으로 보고 `semver.maxSatisfying(..., "*")` 로 최신을 선정. 서버 없이 USB/SD 등으로 사이드로딩 배포할 때 사용.
23
23
 
24
- 동작 차이: `run` 은 다운로드한 바이트를 `appCache` 저장소의 `latest.apk` 로 써서 설치하고, `runByExternalStorage` 는 외부 저장소의 `<dirPath>/<version>.apk` 를 직접 설치 대상으로 삼음. 두 경로 모두 현재 버전(`ApkInstaller.getVersionInfo().versionName`)과 비교해 `semver.gt(최신, 현재)` 가 아니면(이미 최신·동일·낮음) 반환하고, 어느 한쪽이라도 유효한 semver 가 아니면 업데이트 확인을 건너뜀.
24
+ 동작 차이: `run` 은 다운로드한 바이트를 `appCache` 저장소의 `latest.apk` 로 써서 설치 대상으로 삼고, `runByExternalStorage` 는 외부 저장소의 `<dirPath>/<version>.apk` 를 직접 설치 대상으로 삼음. 두 경로 모두 현재 버전(`ApkInstaller.getVersionInfo().versionName`)과 비교해 `semver.gt(최신, 현재)` 가 아니면(이미 최신·동일·낮음) 반환하고, 최신 또는 현재 중 어느 한쪽이라도 유효한 semver(`semver.valid`)가 아니면 업데이트 확인을 건너뜀.
25
25
 
26
26
  ```typescript
27
27
  // 앱 부트스트랩에서 (서버 기반)
@@ -32,14 +32,14 @@ await AutoUpdate.runByExternalStorage({ log: (h) => (statusEl.innerHTML = h), di
32
32
 
33
33
  주의사항:
34
34
 
35
- - 업데이트가 없거나 이미 최신이면 그냥 반환(freeze 안 함)하므로 후속 정상 부트로 이어가면 됨. 반대로 설치를 진행하거나 오류가 나면 무한 대기로 들어가 영원히 resolve 되지 않으니 호출 후속 코드에 의존하지 말 것 — 후속 코드는 "업데이트 없음" 경로에서만 실행됨.
36
- - Android 외 환경(`navigator.userAgent` 에 "android" 없음)이면 `"Android만 지원됩니다."` throw → catch 에서 표시 후 freeze.
37
- - 권한 미승인 시 설정 화면으로 보낸 뒤 최대 5분(1초 간격 300회) 동안 권한 부여를 폴링 대기.
38
- - manifest 에 `REQUEST_INSTALL_PACKAGES` 가 없거나(`manifest:false`) 권한 확인 자체가 실패하면 "APK 파일을 다시 다운로드하여 설치해야 합니다(코드)." 안내(코드 1·2, `run` 은 다운로드 링크 버튼 포함)와 함께 throw.
35
+ - 업데이트가 없거나 이미 최신이면 그냥 반환(freeze 안 함)하므로 후속 정상 부트로 이어감. 반대로 설치를 진행하거나 오류가 나면 무한 대기로 들어가 영원히 resolve 되지 않으니 호출 후속 코드에 의존하지 말 것 — 후속 코드는 "업데이트 없음" 경로에서만 실행됨.
36
+ - Android 외 환경(`navigator.userAgent` 에 `"android"` 없음)이면 `"Android만 지원됩니다."` throw → catch 에서 표시 후 freeze.
37
+ - 권한 미승인(`granted:false`)`requestPermissions` 로 설정 화면으로 보낸 뒤 최대 5분(1초 간격 300회) 동안 `checkPermissions` 로 권한 부여를 폴링 대기.
38
+ - manifest 에 `REQUEST_INSTALL_PACKAGES` 가 선언되어 있지 않거나(`manifest:false`) 권한 확인 자체가 예외로 실패하면 "APK 파일을 다시 다운로드하여 설치해야 합니다(코드)." 안내(코드 1=manifest 누락, 2=확인 실패; `run` 은 다운로드 링크 버튼 HTML 포함)와 함께 throw.
39
39
 
40
40
  ## ApkInstaller
41
41
 
42
- `abstract class ApkInstaller` — APK 설치 관련 네이티브 호출을 감싼 저수준 정적 클래스. Android 는 실제 인텐트/권한을 다루고, 브라우저(web)는 폴백 구현(설치 시 미지원 alert 후 정상 반환, 권한은 항상 `granted:true`/`manifest:true`, 버전은 `__VER__` env 또는 `0.0.0`). `AutoUpdate` 가 내부에서 쓰며, 자동 업데이트 UX 를 직접 구성하거나 권한·설치만 단독으로 다룰 때 직접 호출.
42
+ `abstract class ApkInstaller` — APK 설치 관련 네이티브 호출을 감싼 저수준 정적 클래스. Android 는 실제 인텐트/권한을 다루고, 브라우저(web)는 폴백 구현(설치 시 미지원 alert 후 정상 반환, 권한은 항상 `granted:true`/`manifest:true`, 버전은 `env("__VER__")` 또는 `"0.0.0"`). `AutoUpdate` 가 내부에서 쓰며, 자동 업데이트 UX 를 직접 구성하거나 권한·설치만 단독으로 다룰 때 직접 호출.
43
43
 
44
44
  ```typescript
45
45
  static checkPermissions(): Promise<{ granted: boolean; manifest: boolean }>
@@ -50,7 +50,7 @@ static getVersionInfo(): Promise<VersionInfo>
50
50
 
51
51
  - checkPermissions(): Promise<{ granted: boolean; manifest: boolean }> — 설치 권한 상태 조회. `granted` = `REQUEST_INSTALL_PACKAGES` 권한이 사용자에게 승인되었는지(false 면 `requestPermissions` 로 유도), `manifest` = 해당 권한이 앱 manifest 에 선언되어 있는지(false 면 권한 요청 자체가 불가 → APK 재설치 필요). 설치 전 사전 점검·재시도 폴링에 사용. 웹에서는 둘 다 true.
52
52
  - requestPermissions(): Promise<void> — 설치 권한 요청. Android 에서는 시스템 설정 화면으로 이동시키며 즉시 부여되지 않으므로 이후 `checkPermissions` 폴링으로 승인 여부를 확인해야 함. 웹에서는 동작 없음.
53
- - install(apkUri: string): Promise<void> — APK 설치 인텐트 실행. `apkUri` 는 로컬 파일 경로가 아니라 `content://` FileProvider URI (보통 `@simplysm/capacitor-plugin-file-system` 의 `FileSystem.getUri(파일경로)` 결과). 내부에서 `{ uri: apkUri }` 로 래핑해 플러그인에 전달. 웹에서는 미지원 alert 후 반환.
53
+ - install(apkUri: string): Promise<void> — APK 설치 인텐트 실행. `apkUri` 는 로컬 파일 경로가 아니라 `content://` FileProvider URI(보통 `@simplysm/capacitor-plugin-file-system` 의 `FileSystem.getUri(파일경로)` 결과). 내부에서 `{ uri: apkUri }` 로 래핑해 플러그인에 전달. 웹에서는 미지원 alert 후 반환.
54
54
  - getVersionInfo(): Promise<VersionInfo> — 현재 설치된 앱 버전 조회. 서버/외부 버전과 비교할 현재 버전 기준값. 웹 구현은 `versionName` 을 빌드 시 주입된 `env("__VER__")`(없으면 `"0.0.0"`), `versionCode` 를 `"0"` 으로 응답.
55
55
 
56
56
  ```typescript
@@ -62,7 +62,7 @@ await ApkInstaller.install(await FileSystem.getUri(apkFilePath));
62
62
 
63
63
  ## ApkInstallerPlugin / VersionInfo
64
64
 
65
- Capacitor `registerPlugin("ApkInstaller")` 로 등록되는 네이티브 브리지 인터페이스와 버전 정보 타입. `ApkInstaller` 의 정적 메서드가 이 인터페이스 구현체(네이티브 또는 폴백)에 위임하므로, 직접 구현·호출할 일은 드물고 주로 `getVersionInfo` 반환 형태 참조·웹 구현체 작성 시 본다.
65
+ Capacitor `registerPlugin("ApkInstaller")` 로 등록되는 네이티브 브리지 인터페이스와 버전 정보 타입. `ApkInstaller` 의 정적 메서드가 이 인터페이스 구현체(네이티브 또는 `ApkInstallerWeb` 폴백)에 위임하므로, 직접 구현·호출할 일은 드물고 주로 `getVersionInfo` 반환 형태 참조·웹 구현체 작성 시 본다.
66
66
 
67
67
  ```typescript
68
68
  interface VersionInfo {
@@ -78,7 +78,7 @@ interface ApkInstallerPlugin {
78
78
  }
79
79
  ```
80
80
 
81
- - VersionInfo.versionName: string — 사람이 읽는 표시 버전(semver, 예 `"1.2.3"`). `AutoUpdate` 의 버전 비교(`semver.gt`/`semver.valid`) 이 값을 semver 로 사용.
81
+ - VersionInfo.versionName: string — 사람이 읽는 표시 버전(semver, 예 `"1.2.3"`). `AutoUpdate` 의 버전 비교(`semver.gt`/`semver.valid`) 이 값을 semver 로 사용.
82
82
  - VersionInfo.versionCode: string — Android 빌드 버전 코드의 문자열 표현. 웹 구현에서는 `"0"` 고정. 현재 업데이트 판정 로직은 `versionName` 만 사용하며 이 값은 표시·식별용.
83
83
  - ApkInstallerPlugin.install(options: { uri: string }): Promise<void> — content URI APK 설치(네이티브 측 진입점). `ApkInstaller.install(apkUri)` 가 `{ uri: apkUri }` 로 래핑해 호출하며 uri 는 `content://` FileProvider URI.
84
84
  - ApkInstallerPlugin.checkPermissions / requestPermissions / getVersionInfo — 각각 `ApkInstaller` 의 동명 정적 메서드가 그대로 위임하는 원본 브리지 메서드(권한 조회 / 권한 요청 / 현재 버전 조회). 의미는 위 `ApkInstaller` 항목과 동일.
@@ -1,32 +1,32 @@
1
1
  # @simplysm/capacitor-plugin-file-system
2
2
 
3
- Capacitor 네이티브 파일 시스템 접근 플러그인. Android 에서는 OS 파일 시스템(11+ `MANAGE_EXTERNAL_STORAGE`, 10- `READ/WRITE_EXTERNAL_STORAGE` 권한)에, 웹에서는 IndexedDB 기반 가상 파일 시스템 에뮬레이션에 동일 API 로 접근한다. 진입점은 `FileSystem` 정적 클래스이며, 입출력 형태는 `StorageType`·`FileInfo`·`FileSystemPlugin` 타입으로 기술된다.
3
+ Capacitor 파일 시스템 접근 플러그인. Android 에서는 OS 파일 시스템(11+ `MANAGE_EXTERNAL_STORAGE`, 10- `READ/WRITE_EXTERNAL_STORAGE` 권한)에, 웹에서는 IndexedDB 기반 가상 파일 시스템 에뮬레이션(`capacitor_web_virtual_fs`)에 동일 API 로 접근한다. 진입점은 `FileSystem` 정적 클래스이며, 입출력 형태는 `StorageType`·`FileInfo`·`FileSystemPlugin` 타입으로 기술된다.
4
4
 
5
5
  ## 사용 트리거 인덱스
6
6
 
7
- - **FileSystem** — 앱에서 단말 파일을 읽고 쓰거나 디렉토리·권한·저장소 경로·파일 URI 를 다룰 때의 진입점. 모든 메서드가 `static async` 이며 `new` 없이 `FileSystem.메서드()` 로 호출(abstract class).
7
+ - **FileSystem** — 앱/웹에서 파일을 읽고 쓰거나 디렉토리·권한·저장소 경로·파일 URI 를 다룰 때의 진입점. 모든 메서드가 `static async` 이며 `new` 없이 `FileSystem.메서드()` 로 호출(abstract class).
8
8
  - **StorageType** — `getStoragePath` 인자. 외부/앱 전용/캐시 등 어느 표준 저장소의 기준 경로를 얻을지 고를 때.
9
9
  - **FileInfo** — `readdir` 결과 항목 1건의 타입(이름 + 디렉토리 여부). 디렉토리 나열 결과를 순회·분기할 때.
10
10
  - **FileSystemPlugin** — 저수준 Capacitor 플러그인 계약 인터페이스(옵션 객체 기반 원형). 보통 직접 쓰지 않고 `FileSystem` 래퍼를 쓰며, 커스텀 web 구현이나 옵션·반환 타입 참조가 필요할 때만 사용.
11
11
 
12
12
  ## FileSystem
13
13
 
14
- 모든 파일 작업의 진입점. 추상 클래스의 정적 메서드 모음이라 인스턴스화 없이 호출한다. 내부적으로 `registerPlugin<FileSystemPlugin>("FileSystem")` 로 얻은 네이티브 구현(웹은 `FileSystemWeb`)에 위임하고, 플러그인의 `{ ... }` 래퍼 결과를 평탄화해 반환한다. 모든 메서드는 Promise 반환.
14
+ 모든 파일 작업의 진입점인 abstract class. 정적 메서드만 모여 있어 인스턴스화 없이 호출한다. 내부적으로 `registerPlugin<FileSystemPlugin>("FileSystem", { web: () => new FileSystemWeb() })` 로 얻은 구현에 위임하고, 플러그인의 래핑 객체(`{ granted }` 등) 결과를 평탄화해 반환한다. 모든 메서드는 Promise 반환.
15
15
 
16
16
  - `static checkPermissions(): Promise<boolean>` — 파일 접근 권한 보유 여부. `true` = 권한 있음, `false` = 없음. 웹은 항상 `true`. 읽기/쓰기 전 게이트로 호출. 플러그인의 `{ granted }` 를 boolean 으로 풀어 반환.
17
17
  - `static requestPermissions(): Promise<void>` — 권한 요청. Android 11+ 는 설정 화면으로 이동, Android 10- 는 권한 대화상자 표시. 웹은 무동작. `checkPermissions()` 가 `false` 일 때 호출.
18
- - `static readdir(dirPath: string): Promise<FileInfo[]>` — `dirPath` 디렉토리의 직접 하위 항목을 나열. 디렉토리가 없으면 throw. 각 항목은 `FileInfo`(이름·디렉토리 여부). 폴더 내용을 훑을 때.
18
+ - `static readdir(dirPath: string): Promise<FileInfo[]>` — `dirPath` 디렉토리의 직접 하위 항목을 나열. 각 항목은 `FileInfo`(이름·디렉토리 여부). 구현은 디렉토리 항목 자체가 없어도 하위에 파일 경로가 있으면 중간 경로를 `isDirectory: true` 로 취급(암시적 디렉토리). 대상이 디렉토리가 아니거나 없으면 throw.
19
19
  - `static getStoragePath(type: StorageType): Promise<string>` — `type` 에 해당하는 표준 저장소 위치의 기준 경로 문자열. 경로를 하드코딩하지 말고 이 값으로 베이스를 얻어 join. 웹은 `/webfs/<type>` 가상 경로를 자동 생성 후 반환.
20
- - `static getUri(filePath: string): Promise<string>` — 파일을 외부에 넘길 수 있는 URI. Android 는 FileProvider content:// URI(타 앱 공유·뷰어 인텐트용), 웹은 `URL.createObjectURL` Blob URL. 웹의 Blob URL 은 사용 후 반드시 `URL.revokeObjectURL(uri)` 로 해제(미해제 시 메모리 누수). 파일이 없으면 throw.
21
- - `static writeFile(filePath: string, data: string | Bytes): Promise<void>` — 파일 쓰기(상위 디렉토리 자동 생성). `data` 가 `string` 이면 utf8 로, `Bytes`(Uint8Array)이면 base64변환해 바이너리 저장(`bytes.toBase64`, cross-realm 안전). 텍스트면 문자열, 바이너리면 `Bytes` 를 넘김.
20
+ - `static getUri(filePath: string): Promise<string>` — 파일을 외부에 넘길 수 있는 URI. Android 는 FileProvider URI(타 앱 공유·뷰어 인텐트용), 웹은 파일 내용으로 만든 Blob 의 `URL.createObjectURL` Blob URL. 웹의 Blob URL 은 사용 후 반드시 `URL.revokeObjectURL(uri)` 로 해제(미해제 시 메모리 누수). 웹은 파일이 없으면 throw.
21
+ - `static writeFile(filePath: string, data: string | Bytes): Promise<void>` — 파일 쓰기. `data` 가 `string` 이면 `encoding: "utf8"` 로, `Bytes`(Uint8Array)이면 `bytes.toBase64`base64 변환 `encoding: "base64"` 로 기록(cross-realm 안전). 텍스트면 문자열, 바이너리면 `Bytes` 를 넘김. 웹은 상위 디렉토리를 자동 생성.
22
22
  - `static readFile(filePath: string): Promise<Bytes>` / `static readFile(filePath: string, encoding: "utf8"): Promise<string>` — 파일 읽기(오버로드). 반환 타입이 인자로 갈리므로 바이너리는 인자 없이, 텍스트는 `"utf8"` 명시. 파일이 없으면 throw.
23
- - `encoding` 생략 — base64 로 읽어 `Bytes`(`bytes.fromBase64`) 반환. 이미지·바이너리 파일용.
23
+ - `encoding` 생략 — base64 로 읽어 `bytes.fromBase64` 로 디코드한 `Bytes` 반환. 이미지·바이너리 파일용.
24
24
  - `encoding: "utf8"` — utf8 텍스트 `string` 반환. 텍스트 파일을 문자열로 바로 다룰 때.
25
- - `static remove(targetPath: string): Promise<void>` — 파일/디렉토리 재귀 삭제(디렉토리면 하위 전체). 실패 시 throw. 정리·덮어쓰기 전 제거 시.
25
+ - `static remove(targetPath: string): Promise<void>` — 파일/디렉토리 재귀 삭제(디렉토리면 하위 전체). 웹은 경로 접두사 매칭으로 하위 전체 삭제, 실패 시 throw. 정리·덮어쓰기 전 제거 시.
26
26
  - `static mkdir(targetPath: string): Promise<void>` — 디렉토리 재귀 생성(중간 상위 경로까지, 이미 있으면 무동작). `writeFile` 은 부모를 자동 생성하므로 빈 디렉토리만 보장하면 될 때 사용.
27
27
  - `static exists(targetPath: string): Promise<boolean>` — 경로 존재 여부. `true` = 파일·디렉토리 존재, `false` = 없음. 읽기/삭제 전 분기 조건.
28
28
 
29
- `data` 의 `Bytes` 와 `readFile` 반환 `Bytes` 는 `@simplysm/core-common` 의 바이트 배열 타입(Uint8Array 계열). 바이너리는 내부에서 base64 왕복 처리한다.
29
+ `data` 의 `Bytes` 와 `readFile` 반환 `Bytes` 는 `@simplysm/core-common` 의 `Bytes` 타입(`Uint8Array`). 바이너리는 내부에서 base64 왕복 처리한다.
30
30
 
31
31
  사용 예:
32
32
 
@@ -39,7 +39,7 @@ const base = await FileSystem.getStoragePath("externalFiles");
39
39
  await FileSystem.mkdir(`${base}/notes`);
40
40
  await FileSystem.writeFile(`${base}/notes/memo.txt`, "hello"); // utf8
41
41
  const text = await FileSystem.readFile(`${base}/notes/memo.txt`, "utf8"); // string
42
- const raw = await FileSystem.readFile(`${base}/notes/memo.txt`); // Bytes
42
+ const raw = await FileSystem.readFile(`${base}/notes/memo.txt`); // Bytes (Uint8Array)
43
43
  for (const f of await FileSystem.readdir(`${base}/notes`)) {
44
44
  if (!f.isDirectory) console.log(f.name);
45
45
  }
@@ -48,7 +48,7 @@ const uri = await FileSystem.getUri(`${base}/notes/memo.txt`); // 웹: 사용
48
48
 
49
49
  ## StorageType
50
50
 
51
- `getStoragePath(type)` 가 받는 저장소 유형 리터럴. 직접 경로를 만들지 말고 이 유형으로 플랫폼 표준 위치를 얻는다.
51
+ `getStoragePath(type)` 가 받는 저장소 유형 리터럴 유니언. 직접 경로를 만들지 말고 이 유형으로 플랫폼 표준 위치를 얻는다.
52
52
 
53
53
  ```ts
54
54
  type StorageType =
@@ -56,13 +56,13 @@ type StorageType =
56
56
  | "appData" | "appFiles" | "appCache";
57
57
  ```
58
58
 
59
- - `"external"` — 외부 저장소 루트(`Environment.getExternalStorageDirectory`). 사용자/타 앱과 공유되는 공용 영역 전체에 접근할 때. `MANAGE_EXTERNAL_STORAGE` 권한 필요.
60
- - `"externalFiles"` — 앱 전용 외부 파일 디렉토리. 앱이 소유하는 외부 영속 파일(앱 제거 시 함께 삭제)을 둘 때.
61
- - `"externalCache"` — 앱 전용 외부 캐시 디렉토리. 공간 부족 시 OS 가 회수할 수 있는 외부 임시 캐시를 둘 때.
62
- - `"externalMedia"` — 앱 전용 외부 미디어 디렉토리. 이미지·동영상 등 미디어 산출물을 외부 미디어 영역에 둘 때.
63
- - `"appData"` — 앱 데이터 디렉토리(내부 저장소). 비공개 데이터 루트가 필요할 때.
64
- - `"appFiles"` — 앱 파일 디렉토리(내부 저장소). 비공개 영속 파일을 둘 때.
65
- - `"appCache"` — 앱 캐시 디렉토리(내부 저장소). 재생성 가능한 내부 임시 캐시를 둘 때.
59
+ - `"external"` — 외부 저장소 루트(`Environment.getExternalStorageDirectory`). 공용 외부 영역 전체에 접근할 때.
60
+ - `"externalFiles"` — 앱 전용 외부 파일 디렉토리. 앱이 소유하는 외부 영속 파일을 둘 때.
61
+ - `"externalCache"` — 앱 전용 외부 캐시 디렉토리. 외부 임시 캐시를 둘 때.
62
+ - `"externalMedia"` — 앱 전용 외부 미디어 디렉토리. 이미지·동영상 등 미디어 산출물을 둘 때.
63
+ - `"appData"` — 앱 데이터 디렉토리. 앱 데이터 루트가 필요할 때.
64
+ - `"appFiles"` — 앱 파일 디렉토리. 앱 영속 파일을 둘 때.
65
+ - `"appCache"` — 앱 캐시 디렉토리. 재생성 가능한 내부 임시 캐시를 둘 때.
66
66
 
67
67
  웹에서는 각 유형이 `/webfs/<type>` 가상 경로로 매핑되며 호출 시 해당 디렉토리를 자동 생성한다.
68
68
 
@@ -82,15 +82,15 @@ interface FileInfo {
82
82
 
83
83
  ## FileSystemPlugin
84
84
 
85
- 저수준 Capacitor 플러그인 계약 인터페이스. `FileSystem` 정적 메서드가 내부에서 위임하는 원형으로, 메서드가 옵션 객체(`{ path }`, `{ type }`, `{ path, data, encoding }`)를 받고 결과도 래핑 객체(`{ granted }`, `{ files }`, `{ path }`, `{ uri }`, `{ data }`, `{ exists }`)로 반환한다. 보통 직접 호출하지 않으며, 커스텀 web 구현(`FileSystemPlugin` 구현)을 작성하거나 옵션·반환 타입을 참조해야 할 때만 사용한다.
85
+ 저수준 Capacitor 플러그인 계약 인터페이스. `FileSystem` 정적 메서드가 내부에서 위임하는 원형이며, 네이티브 구현과 웹 구현(`FileSystemWeb`)이 공유한다. 메서드가 옵션 객체(`{ path }`, `{ type }`, `{ path, data, encoding }`)를 받고 결과도 래핑 객체(`{ granted }`, `{ files }`, `{ path }`, `{ uri }`, `{ data }`, `{ exists }`)로 반환한다. 보통 직접 호출하지 않으며, 커스텀 web 구현을 작성하거나 옵션·반환 타입을 참조해야 할 때만 사용.
86
86
 
87
87
  - `checkPermissions(): Promise<{ granted: boolean }>` — 권한 보유 여부를 `granted` 로 반환.
88
88
  - `requestPermissions(): Promise<void>` — 권한 요청.
89
89
  - `readdir(options: { path: string }): Promise<{ files: FileInfo[] }>` — `path` 디렉토리 항목 목록을 `files` 로 반환.
90
90
  - `getStoragePath(options: { type: StorageType }): Promise<{ path: string }>` — `type` 저장소의 실제 경로를 `path` 로 반환.
91
91
  - `getUri(options: { path: string }): Promise<{ uri: string }>` — `path` 파일의 외부 전달용 `uri` 반환.
92
- - `writeFile(options: { path: string; data: string; encoding?: "utf8" | "base64" }): Promise<void>` — `path` 에 `data`(문자열) 기록. `encoding` = 입력 데이터 해석 방식. `"utf8"` = 텍스트 그대로, `"base64"` = base64 문자열을 바이너리로 디코드해 기록. 래퍼 `FileSystem.writeFile` 이 문자열은 `"utf8"`, `Bytes` 는 `"base64"` 로 자동 지정.
93
- - `readFile(options: { path: string; encoding?: "utf8" | "base64" }): Promise<{ data: string }>` — `path` 읽어 `data`(문자열) 반환. `encoding` = 반환 문자열 형식. `"utf8"` = 텍스트, `"base64"` = 바이너리를 base64 문자열로 인코딩. 플러그인 레벨에서 바이너리는 항상 base64 문자열로 주고받고, `FileSystem` 가 `Bytes` ↔ base64 변환을 담당.
92
+ - `writeFile(options: { path: string; data: string; encoding?: "utf8" | "base64" }): Promise<void>` — `path` 에 `data`(문자열) 기록. `encoding` = 입력 데이터 해석 방식. `"utf8"` = 텍스트 그대로 인코딩, `"base64"` = base64 문자열을 바이너리로 디코드해 기록. 래퍼 `FileSystem.writeFile` 이 문자열은 `"utf8"`, `Bytes` 는 `"base64"` 로 자동 지정.
93
+ - `readFile(options: { path: string; encoding?: "utf8" | "base64" }): Promise<{ data: string }>` — `path` 읽어 `data`(문자열) 반환. `encoding` = 반환 문자열 형식. `"utf8"` = 텍스트, `"base64"` = 바이너리를 base64 문자열로 반환. 플러그인 레벨에서 바이너리는 항상 base64 문자열로 주고받고, `FileSystem` 가 `Bytes` ↔ base64 변환을 담당.
94
94
  - `remove(options: { path: string }): Promise<void>` — `path` 파일/디렉토리 재귀 삭제.
95
95
  - `mkdir(options: { path: string }): Promise<void>` — `path` 디렉토리 재귀 생성.
96
96
  - `exists(options: { path: string }): Promise<{ exists: boolean }>` — `path` 존재 여부를 `exists` 로 반환.
@@ -1,84 +1,110 @@
1
1
  # @simplysm/capacitor-plugin-intent
2
2
 
3
- Android 인텐트 송수신 Capacitor 플러그인. 정적 클래스 `Intent` 로 브로드캐스트 구독/전송·실행 인텐트 조회·newIntent 리스닝·startActivityForResult 외부 Activity 실행을 수행한다. 바코드 스캐너·PDA(예: Zebra DataWedge)산업용 디바이스 연동 시 사용. 웹 환경(`IntentWeb` 스텁)에서는 실제 동작 없이 경고 로그만 남기고 무해한 기본값을 반환하므로 실제 인텐트 처리는 Android 네이티브에서만 일어난다.
3
+ Android 인텐트 연동 Capacitor 플러그인. 정적 클래스 `Intent` 로 브로드캐스트 송수신, 실행 인텐트 조회, `newIntent` 리스닝, `startActivityForResult` 외부 Activity 실행을 수행. 산업용 디바이스(바코드 스캐너, PDA) 연동용. 웹 환경(`IntentWeb` 스텁)에서는 실제 동작 없이 경고 로그만 남기고 무해한 기본값을 반환하므로 인텐트 처리는 Android 네이티브에서만 일어난다.
4
4
 
5
5
  ## 사용 트리거 인덱스
6
6
 
7
- - **Intent** — 인텐트 작업의 정적 진입점(추상 클래스). 브로드캐스트 수신기 등록/해제(`subscribe`/`unsubscribeAll`), 브로드캐스트 전송(`send`), 앱 실행 인텐트 조회(`getLaunchIntent`), 실행 중 새 인텐트 리스닝(`addListener`/`removeAllListeners`), 외부 Activity 실행 후 결과 수신(`startActivityForResult`) 필요할 때. 아래 인라인 섹션 참조.
8
- - **결과·옵션 타입** (`IntentResult`, `StartActivityForResultOptions`, `StartActivityForResultResult`) `Intent` 메서드의 콜백 인자·반환·실행 옵션을 타입으로 다룰 때. 아래 인라인 섹션 참조.
9
- - **IntentPlugin** — `registerPlugin` 에 쓰이는 Capacitor 네이티브 플러그인 저수준 계약. 보통 직접 호출하지 않고 `Intent` 정적 메서드를 거치며, 네이티브 구현·웹 스텁의 계약 확인용. 아래 인라인 섹션 참조.
7
+ - 브로드캐스트 수신/전송, 실행 인텐트 조회, `newIntent` 리스닝, `startActivityForResult` `Intent` 정적 클래스 (아래 인라인).
8
+ - 콜백 인자·옵션·결과 타입 → `IntentResult` / `StartActivityForResultOptions` / `StartActivityForResultResult` (아래 인라인).
9
+ - 네이티브 플러그인 저수준 계약 `IntentPlugin` (아래 인라인, 보통 직접 호출하지 않음).
10
10
 
11
- ## Intent (정적 클래스)
11
+ ## Intent 정적 클래스
12
12
 
13
- `abstract class Intent` — 정적 메서드만 가진 진입점. 인스턴스화하지 않고 `Intent.xxx()` 형태로 호출하며 모든 메서드는 비동기(`Promise`). 내부적으로 `registerPlugin<IntentPlugin>("Intent")` 로 얻은 플러그인(웹은 `IntentWeb`)을 래핑한다.
13
+ `abstract class Intent` — 인스턴스화 불가, `Intent.xxx()` 형태로만 호출. 모든 메서드 비동기. 내부적으로 `registerPlugin<IntentPlugin>("Intent")` 로 얻은 플러그인(웹은 `IntentWeb`)을 래핑.
14
14
 
15
- - `Intent.subscribe(filters: string[], callback: (result: IntentResult) => void): Promise<() => Promise<void>>` — 브로드캐스트 수신기를 등록하고, 호출하면 해당 구독만 해제하는 함수를 resolve.
16
- - `filters: string[]` 수신할 인텐트 액션 문자열 배열(예: `"com.symbol.datawedge.api.RESULT_ACTION"`). 액션들로 들어오는 브로드캐스트만 콜백으로 전달. 스캐너 결과 액션 등 수신 대상 액션을 등록할 때 지정.
17
- - `callback: (result: IntentResult) => void` — 매칭 브로드캐스트 도착 시마다 호출. 등록 직후 플러그인이 보내는 `{ id }` 만 담긴 초기 resolve(`result.action == null`)는 내부에서 걸러져 호출되지 않으며, `action` 이 채워진 실제 수신 시점에만 호출.
18
- - 반환: 구독 해제 함수 `() => Promise<void>`. `await` 한 결과가 이 함수이며 호출(다시 `await`) 시 내부 `id` 로 해당 수신기만 해제. 화면 진입 시 등록하고 이탈 시 반환 함수로 해제하는 용도.
19
- - `Intent.unsubscribeAll(): Promise<void>` — 등록된 모든 브로드캐스트 수신기를 한 번에 해제. 개별 해제 함수를 보관하지 않았거나 화면 정리 시 전체를 일괄로 끊을 때.
20
- - `Intent.send(options: { action: string; extras?: Record<string, unknown> }): Promise<void>` — 브로드캐스트 전송. 스캐너 트리거 토글 등 디바이스 제어 명령 송신에 사용.
21
- - `action: string` — 전송할 인텐트 액션 문자열(필수). 디바이스 API 가 정의한 명령 액션을 지정.
22
- - `extras?: Record<string, unknown>` — 함께 보낼 추가 키-값 데이터(선택). 명령 파라미터(예: 스캐너 트리거 토글 값) 담을 때.
23
- - `Intent.getLaunchIntent(): Promise<IntentResult>` — 앱을 실행시킨 인텐트(action·extras)를 조회. 외부 인텐트로 앱이 기동됐는지·그 `extras` 를 앱 시작 시점에 읽을 때. 웹 스텁은 항상 빈 객체 `{}` 반환.
24
- - `Intent.addListener(eventName: "newIntent", callback: (result: IntentResult) => void): Promise<PluginListenerHandle>` — 앱이 이미 실행 중인 상태에서 새로 도착하는 인텐트(`onNewIntent`)를 리스닝.
25
- - `eventName: "newIntent"` — 리스닝 대상 이벤트(현재 이 리터럴 하나만 지원). 이미 떠 있는 앱에 인텐트가 재전달되는 singleTop 등 상황에서 발생.
26
- - `callback: (result: IntentResult) => void` — 새 인텐트 수신 시 호출. `subscribe` 와 달리 액션 필터 없이 들어오는 새 인텐트의 `action`·`extras` 를 받음.
27
- - 반환: `PluginListenerHandle`(@capacitor/core). `handle.remove()` 로 이 리스너만 개별 해제.
28
- - `Intent.removeAllListeners(): Promise<void>` — `addListener` 로 등록한 모든 이벤트 리스너를 일괄 제거. `subscribe`(브로드캐스트 수신기)와는 별개 채널이므로 둘 다 쓰면 각각 정리해야 함.
29
- - `Intent.startActivityForResult(options: StartActivityForResultOptions): Promise<StartActivityForResultResult>` — 외부 Activity 를 실행하고 그 결과 코드·데이터를 `await` 로 직접 수신. 결제·서명 등 외부 앱에 1회성 작업을 위임하고 응답을 되받아야 할 때. 반환 `resultCode` 로 성공 판정(Android 규약상 `-1` = RESULT_OK, `0` = RESULT_CANCELED). 옵션·결과 필드는 아래 "결과·옵션 타입" 참조.
30
-
31
- `subscribe`/`unsubscribeAll` 은 구독 핸들로 끊거나 일괄로 끊는 두 경로를 제공하므로, 화면별로는 `subscribe` 의 반환 함수를, 전역 정리에는 `unsubscribeAll` 을 쓴다. `addListener`/`removeAllListeners` 는 newIntent 이벤트용 동일 패턴이며 브로드캐스트 수신기와 별개 채널이다.
15
+ ```ts
16
+ Intent.subscribe(filters: string[], callback: (result: IntentResult) => void): Promise<() => Promise<void>>
17
+ Intent.unsubscribeAll(): Promise<void>
18
+ Intent.send(options: { action: string; extras?: Record<string, unknown> }): Promise<void>
19
+ Intent.getLaunchIntent(): Promise<IntentResult>
20
+ Intent.addListener(eventName: "newIntent", callback: (result: IntentResult) => void): Promise<PluginListenerHandle>
21
+ Intent.removeAllListeners(): Promise<void>
22
+ Intent.startActivityForResult(options: StartActivityForResultOptions): Promise<StartActivityForResultResult>
23
+ ```
32
24
 
33
- 사용 예:
25
+ - `subscribe(filters, callback)`: `filters` 의 액션 문자열들에 대한 브로드캐스트 수신기 등록. `callback` 은 `result.action != null` 인 실제 수신 시점에만 호출됨(등록 직후 `{ id }` 만 담긴 초기 resolve 는 내부에서 걸러짐). 반환값은 구독 해제 함수이며, 호출 시 내부 `id` 로 해당 구독만 해제. 스캐너 결과 액션 수신 등 화면 진입 시 등록·이탈 시 해제 용도.
26
+ - `unsubscribeAll()`: 등록된 모든 브로드캐스트 수신기를 일괄 해제. 개별 해제 함수를 보관하지 않고 전역 정리할 때.
27
+ - `send(options)`: `options.action` 액션으로 브로드캐스트 전송, `options.extras` 로 추가 데이터 동봉. 스캐너 트리거 토글 등 디바이스 제어 명령 송신에 사용.
28
+ - `getLaunchIntent()`: 앱을 실행시킨 인텐트(`action`·`extras`)를 `IntentResult` 로 조회. 외부 인텐트로 앱이 기동됐는지·그 데이터를 시작 시점에 읽을 때. 웹 스텁은 항상 빈 객체 `{}` 반환.
29
+ - `addListener("newIntent", callback)`: 앱 실행 중 새로 도착하는 인텐트를 리스닝. `eventName` 은 `"newIntent"` 리터럴 고정. 반환된 `PluginListenerHandle` 의 `handle.remove()` 로 이 리스너만 개별 해제. `subscribe`(브로드캐스트)와는 별개 채널.
30
+ - `removeAllListeners()`: `addListener` 로 등록한 모든 이벤트 리스너를 일괄 제거.
31
+ - `startActivityForResult(options)`: 외부 Activity 를 실행하고 결과 코드·데이터를 `await` 로 직접 수신. 결제 앱·이미지 선택기 등 결과를 되받는 외부 화면 호출에 사용. 반환 `resultCode` 로 성공 판정(Android 규약상 `-1` = RESULT_OK, `0` = RESULT_CANCELED).
34
32
 
35
33
  ```ts
36
- // 바코드 스캐너 결과 브로드캐스트 구독
34
+ // 브로드캐스트 수신 (DataWedge 결과 액션)
37
35
  const unsub = await Intent.subscribe(
38
36
  ["com.symbol.datawedge.api.RESULT_ACTION"],
39
37
  (result) => console.log(result.action, result.extras),
40
38
  );
41
- // 스캐너 트리거 토글 전송
39
+
40
+ // 스캔 트리거 토글 전송
42
41
  await Intent.send({
43
42
  action: "com.symbol.datawedge.api.ACTION",
44
43
  extras: { "com.symbol.datawedge.api.SOFT_SCAN_TRIGGER": "TOGGLE_SCANNING" },
45
44
  });
45
+
46
46
  // 외부 Activity 실행 후 결과 수신
47
47
  const res = await Intent.startActivityForResult({ action: "com.example.PAY", extras: { amount: 1000 } });
48
48
  if (res.resultCode === -1) { /* RESULT_OK */ }
49
- await unsub(); // 개별 해제
49
+
50
+ await unsub(); // 개별 구독 해제
50
51
  ```
51
52
 
52
- 주의: 웹 플랫폼(`IntentWeb`)에서는 `subscribe`·`send`·`startActivityForResult` 가 실제 동작 없이 경고 로그(`createLogger("capacitor:intent")` → `"웹 환경에서는 지원하지 않습니다."`)만 남긴다. 이때 `subscribe` 는 `{ id: "web-stub" }`(콜백 미호출), `getLaunchIntent` 는 `{}`(경고 없음), `startActivityForResult` 는 `{ resultCode: 0 }`(항상 CANCELED 로 보임)을 반환하고, `unsubscribe`·`unsubscribeAll` 은 무동작. 웹·하이브리드 공용 코드에서 호출해도 예외는 나지 않으나 인텐트 동작은 Android 에서만 발생함을 전제로 작성할 것.
53
+ 웹 플랫폼(`IntentWeb`)에서는 `subscribe`·`send`·`startActivityForResult` 가 실제 동작 없이 경고 로그(`createLogger("capacitor:intent")` → `"웹 환경에서는 지원하지 않습니다."`)만 남긴다. 이때 `subscribe` 는 `{ id: "web-stub" }`(콜백 미호출), `getLaunchIntent` 는 경고 없이 `{}`, `startActivityForResult` 는 `{ resultCode: 0 }`(항상 CANCELED 로 보임)을 반환하고, `unsubscribe`·`unsubscribeAll` 은 무동작. 호출해도 예외는 나지 않으나 인텐트 동작은 Android 에서만 발생함을 전제로 작성할 것.
53
54
 
54
- ## 결과·옵션 타입
55
+ ## 타입
55
56
 
56
- `Intent` 메서드의 결과·옵션을 타입으로 다룰 때 참조하는 인터페이스. "값 없음"은 그대로 `undefined` 로 전달되므로 호출부에서 optional 로 다룰 것.
57
+ `Intent` 메서드의 콜백 인자·옵션·결과를 다루는 인터페이스. "값 없음"은 그대로 `undefined` 로 전달되므로 호출부에서 optional 로 다룰 것.
57
58
 
58
- - `IntentResult` — `subscribe`·`addListener` 콜백 인자 및 `getLaunchIntent` 반환 타입.
59
- - `action?: string` — 인텐트(브로드캐스트) 액션 문자열. 미수신/미설정일 수 있어 선택. `subscribe` 콜백에는 이 값이 채워진 실제 이벤트만 전달됨(초기 resolve 구분 기준).
60
- - `extras?: Record<string, unknown>` — 인텐트에 담긴 추가 키-값 데이터(스캔된 바코드 값 등). 없을 수 있음.
61
- - `StartActivityForResultOptions` — `startActivityForResult` 입력. 모든 필드 선택이며 대상 인텐트를 암시적(액션·URI·타입) 또는 명시적(패키지·클래스)으로 구성.
62
- - `action?: string` — 인텐트 액션. 암시적 인텐트로 처리 앱을 띄울 때.
63
- - `uri?: string` — 인텐트 data URI. URI 로 대상(뷰어·다이얼러 등)을 지정할 때.
64
- - `extras?: Record<string, unknown>` — 대상 Activity 로 전달할 추가 키-값 데이터(호출 파라미터).
65
- - `type?: string` — MIME 타입. 데이터 형식으로 처리 대상 앱을 좁힐 때.
66
- - `packageName?: string` — 실행할 특정 앱 패키지명. 명시적 인텐트화(특정 앱으로만 실행)할 때.
67
- - `className?: string` — 실행할 특정 Activity 클래스명. `packageName` 과 함께 컴포넌트를 직접 지정할 때.
68
- - `flags?: number` — Android Intent flags 비트값(`Intent.FLAG_*`). 실행 모드(새 태스크 시작 등) 제어가 필요할 때.
69
- - `StartActivityForResultResult` — `startActivityForResult` 반환.
70
- - `resultCode: number` — Activity 결과 코드(필수). Android 규약상 `-1` = RESULT_OK, `0` = RESULT_CANCELED. 성공/취소 분기 판단에 사용(웹 스텁은 항상 `0`).
71
- - `data?: { action?: string; uri?: string; extras?: Record<string, unknown> }` — 결과 인텐트 데이터(선택, 결과 없으면 부재). `action` = 결과 인텐트 액션, `uri` = 결과 data URI, `extras` = 결과 추가 데이터. 결과 본문은 `data?.extras` 형태로 접근.
59
+ ```ts
60
+ interface IntentResult {
61
+ action?: string;
62
+ extras?: Record<string, unknown>;
63
+ }
64
+
65
+ interface StartActivityForResultOptions {
66
+ action?: string;
67
+ uri?: string;
68
+ extras?: Record<string, unknown>;
69
+ type?: string;
70
+ packageName?: string;
71
+ className?: string;
72
+ flags?: number;
73
+ }
74
+
75
+ interface StartActivityForResultResult {
76
+ resultCode: number;
77
+ data?: { action?: string; uri?: string; extras?: Record<string, unknown> };
78
+ }
79
+ ```
80
+
81
+ - `IntentResult.action`: 인텐트(브로드캐스트) 액션 문자열. 미수신/미설정일 수 있어 선택. `subscribe` 콜백에는 이 값이 채워진 실제 이벤트만 전달됨.
82
+ - `IntentResult.extras`: 인텐트에 담긴 추가 키-값 데이터(스캔된 바코드 값 등). 없을 수 있음.
83
+ - `StartActivityForResultOptions.action`: 인텐트 액션. 암시적 인텐트로 처리 앱을 띄울 때.
84
+ - `StartActivityForResultOptions.uri`: 인텐트 data URI. URI 로 대상(뷰어·다이얼러 등)을 지정할 때.
85
+ - `StartActivityForResultOptions.extras`: 대상 Activity 로 전달할 추가 키-값 데이터(호출 파라미터).
86
+ - `StartActivityForResultOptions.type`: MIME 타입(예: `"image/*"`). 데이터 형식으로 처리 대상을 좁힐 때.
87
+ - `StartActivityForResultOptions.packageName`: 실행할 특정 앱 패키지명. 명시적으로 특정 앱만 실행할 때.
88
+ - `StartActivityForResultOptions.className`: 실행할 특정 Activity 클래스명. `packageName` 과 함께 컴포넌트를 직접 지정할 때.
89
+ - `StartActivityForResultOptions.flags`: Android Intent flags 비트값(정수). 실행 모드 제어가 필요할 때.
90
+ - `StartActivityForResultResult.resultCode`: Activity 결과 코드(필수). `-1` = RESULT_OK, `0` = RESULT_CANCELED. 성공/취소 분기 판단에 사용(웹 스텁은 항상 `0`).
91
+ - `StartActivityForResultResult.data`: 결과 인텐트 데이터(선택, 결과 없으면 부재). `action` = 결과 인텐트 액션, `uri` = 결과 data URI, `extras` = 결과 추가 데이터. 결과 본문은 `data?.extras` 로 접근.
72
92
 
73
93
  ## IntentPlugin
74
94
 
75
- `interface IntentPlugin` — `registerPlugin<IntentPlugin>("Intent")` 에 쓰이는 Capacitor 네이티브 플러그인 계약. 웹 구현(`IntentWeb`)과 Android 네이티브가 이를 구현. 일반 사용 코드는 `Intent` 정적 메서드를 쓰고 이 인터페이스를 직접 호출하지 않으며, 네이티브 구현·웹 스텁이 충족해야 할 저수준 계약을 확인할 때만 참조.
95
+ `interface IntentPlugin` — `registerPlugin<IntentPlugin>("Intent")` 에 쓰이는 Capacitor 네이티브 플러그인 계약. 웹 구현(`IntentWeb`)과 Android 네이티브가 구현. 일반 코드는 `Intent` 정적 메서드를 쓰고 이 인터페이스를 직접 호출하지 않으며, 네이티브 구현·웹 스텁의 저수준 계약 확인용으로만 참조.
96
+
97
+ ```ts
98
+ subscribe(options: { filters: string[] }, callback: (result: IntentResult) => void): Promise<{ id: string }>
99
+ unsubscribe(options: { id: string }): Promise<void>
100
+ unsubscribeAll(): Promise<void>
101
+ send(options: { action: string; extras?: Record<string, unknown> }): Promise<void>
102
+ getLaunchIntent(): Promise<IntentResult>
103
+ addListener(eventName: "newIntent", listenerFunc: (data: IntentResult) => void): Promise<PluginListenerHandle>
104
+ removeAllListeners(): Promise<void>
105
+ startActivityForResult(options: StartActivityForResultOptions): Promise<StartActivityForResultResult>
106
+ ```
76
107
 
77
- - `subscribe(options: { filters: string[] }, callback: (result: IntentResult) => void): Promise<{ id: string }>` — 수신기 등록 후 해제용 `id` 반환. `Intent.subscribe` 가 이 `id` 를 클로저로 보관해 반환 함수에서 `unsubscribe` 에 넘김.
78
- - `unsubscribe(options: { id: string }): Promise<void>` — 주어진 `id` 의 수신기만 해제. 단일 구독 해제는 `IntentPlugin` 에만 있고 `Intent` 클래스에서는 `subscribe` 가 돌려주는 해제 함수로 대체됨.
79
- - `unsubscribeAll(): Promise<void>` 모든 수신기 해제.
80
- - `send(options: { action: string; extras?: Record<string, unknown> }): Promise<void>` — 브로드캐스트 전송.
81
- - `getLaunchIntent(): Promise<IntentResult>` — 실행 인텐트 조회.
82
- - `addListener(eventName: "newIntent", listenerFunc: (data: IntentResult) => void): Promise<PluginListenerHandle>` — `"newIntent"` 이벤트 리스너 등록, 해제용 표준 Capacitor 핸들 반환.
83
- - `removeAllListeners(): Promise<void>` — 모든 리스너 제거.
84
- - `startActivityForResult(options: StartActivityForResultOptions): Promise<StartActivityForResultResult>` — 외부 Activity 실행 및 결과 수신.
108
+ - `subscribe`: 수신기 등록 후 해제용 `id` 반환. `Intent.subscribe` 가 이 `id` 를 클로저로 보관해 반환 함수에서 `unsubscribe` 에 넘김.
109
+ - `unsubscribe`: 주어진 `id` 의 수신기만 해제. 단일 구독 해제는 인터페이스에만 있고, `Intent` 클래스에서는 `subscribe` 가 돌려주는 해제 함수로 대체됨.
110
+ - 그 외(`unsubscribeAll`·`send`·`getLaunchIntent`·`addListener`·`removeAllListeners`·`startActivityForResult`) 동명의 `Intent` 정적 메서드와 동일 동작의 계약.
@@ -14,13 +14,11 @@
14
14
 
15
15
  모든 USB 저장 작업의 진입점. 추상 클래스의 정적 메서드 모음이라 `new` 없이 `UsbStorage.메서드()` 로 호출한다. 내부적으로 `registerPlugin<UsbStoragePlugin>("UsbStorage")` 로 얻은 네이티브 구현(웹은 `UsbStorageWeb`)에 위임하고, 플러그인의 `{ ... }` 래퍼 결과를 평탄화해 반환한다. 대상 장치는 항상 `UsbDeviceFilter`(`{ vendorId, productId }`) 로 지정하며 readdir/readFile 도 매 호출마다 filter 를 받는다(상태 없음).
16
16
 
17
- - `static getDevices(): Promise<UsbDeviceInfo[]>` — 현재 연결된 USB 장치 목록 조회. 배열이면 연결된 장치 없음. 권한과 무관하게 열거되며, 반환 항목의 `vendorId`/`productId` 를 추려 이후 호출의 filter 로 사용. 목록 UI 를 채우거나 흐름의 첫 단계에서 호출. 플러그인의 `{ devices }` 를 배열로 풀어 반환.
18
- - `static checkPermissions(filter: UsbDeviceFilter): Promise<boolean>` — 다이얼로그 없이 지정 장치 접근 권한 보유 여부만 확인. true = 이미 보유(requestPermissions 생략 가능), false = 미보유(요청 필요). 웹은 항상 true. `readdir`/`readFile` 전 게이트로 호출. 플러그인의 `{ granted }` 를 boolean 으로 풀어 반환.
19
- - `static requestPermissions(filter: UsbDeviceFilter): Promise<boolean>` — 지정 장치 접근 권한을 사용자에게 요청(다이얼로그). true = 승인, false = 거부(이때 읽기 중단). 웹은 항상 true. `checkPermissions` 가 false 일 때 호출. 플러그인의 `{ granted }` 를 boolean 으로 풀어 반환.
20
- - `static readdir(filter: UsbDeviceFilter, dirPath: string): Promise<UsbFileInfo[]>` — 지정 장치의 디렉토리 항목 나열. `filter` = 대상 장치, `dirPath` = 나열할 디렉토리 경로(루트는 `"/"`). 각 항목은 `UsbFileInfo`(이름·디렉토리 여부)로, 트리를 순회하려면 `isDirectory === true` 인 항목을 다시 readdir. 폴더 내용을 훑을 때. 플러그인의 `{ files }` 를 배열로 풀어 반환.
21
- - `static readFile(filter: UsbDeviceFilter, filePath: string): Promise<Bytes | undefined>` — 지정 장치의 파일 읽기. `filter` = 대상 장치, `filePath` = 읽을 파일 경로. 플러그인이 base64 문자열로 준 데이터를 `bytes.fromBase64` 로 `Bytes`(Uint8Array 계열)로 디코드해 반환. 파일이 없거나 데이터가 `null` 이면 `undefined`(결측 그대로 전파 `""`·기본값 치환 금지). 읽은 바이너리를 가공·저장할 때.
22
-
23
- 반환 `Bytes` 는 `@simplysm/core-common` 의 바이트 배열 타입. 플러그인 레벨에서는 base64 문자열로 주고받고, `UsbStorage.readFile` 가 base64 ↔ `Bytes` 변환을 담당한다.
17
+ - `static getDevices(): Promise<UsbDeviceInfo[]>` — 현재 연결된 USB 장치 목록 조회. 플러그인의 `{ devices }` 배열로 풀어 반환하며, 반환 항목의 `vendorId`/`productId` 를 추려 이후 호출의 filter 로 사용. 목록 UI 를 채우거나 흐름의 첫 단계에서 호출.
18
+ - `static checkPermissions(filter: UsbDeviceFilter): Promise<boolean>` — `filter` 장치의 접근 권한 보유 여부 확인. 플러그인의 `{ granted }` boolean 으로 풀어 반환. true = 보유, false = 미보유(이때 `requestPermissions` 필요). 구현은 항상 true. `readdir`/`readFile` 전 게이트로 호출.
19
+ - `static requestPermissions(filter: UsbDeviceFilter): Promise<boolean>` — `filter` 장치 접근 권한을 사용자에게 요청. 플러그인의 `{ granted }` 를 boolean 으로 풀어 반환. true = 승인, false = 거부(이때 읽기 중단). 구현은 항상 true. `checkPermissions` 가 false 일 때 호출.
20
+ - `static readdir(filter: UsbDeviceFilter, dirPath: string): Promise<UsbFileInfo[]>` — `filter` 장치의 `dirPath` 디렉토리 항목 나열. 플러그인의 `{ files }` 배열로 풀어 반환. 각 항목은 `UsbFileInfo`(이름·디렉토리 여부)로, 트리를 순회하려면 `isDirectory === true` 인 항목을 다시 readdir. 루트는 `"/"`.
21
+ - `static readFile(filter: UsbDeviceFilter, filePath: string): Promise<Bytes | undefined>` — `filter` 장치의 `filePath` 파일 읽기. 플러그인이 base64 문자열로 준 `data` `bytes.fromBase64` 로 `Bytes`(`@simplysm/core-common`)로 디코드해 반환. 플러그인의 `data` `null` 이면(파일 없음/데이터 없음) `undefined` 반환결측을 값으로 치환하지 않고 그대로 전파.
24
22
 
25
23
  사용 예:
26
24
 
@@ -54,8 +52,8 @@ interface UsbDeviceFilter {
54
52
  }
55
53
  ```
56
54
 
57
- - `vendorId: number` — 대상 USB 장치 벤더 ID(제조사 식별 코드). `UsbDeviceInfo.vendorId` 를 그대로 넘김. 같은 제조사여도 모델 구분은 `productId` 로.
58
- - `productId: number` — 대상 USB 장치 제품 ID(모델 식별 코드). `UsbDeviceInfo.productId` 를 그대로 넘김. `vendorId` 와 조합해 하나의 장치를 특정(웹 구현은 `${vendorId}:${productId}` 를 장치 키로 사용).
55
+ - `vendorId: number` — 대상 USB 장치 벤더 ID. `UsbDeviceInfo.vendorId` 를 그대로 넘김. 같은 제조사여도 모델 구분은 `productId` 로.
56
+ - `productId: number` — 대상 USB 장치 제품 ID. `UsbDeviceInfo.productId` 를 그대로 넘김. `vendorId` 와 조합해 하나의 장치를 특정(웹 구현은 `${vendorId}:${productId}` 를 장치 키로 사용).
59
57
 
60
58
  ## UsbDeviceInfo
61
59
 
@@ -71,8 +69,8 @@ interface UsbDeviceInfo {
71
69
  }
72
70
  ```
73
71
 
74
- - `deviceName: string` — OS 가 부여한 장치 시스템 이름(식별 문자열). 디버깅·로그·표시용.
75
- - `manufacturerName: string` — 제조사 표시명. 사람이 읽는 장치 라벨을 만들거나 동일 제품명을 구분할 때.
72
+ - `deviceName: string` — 장치 시스템 이름(식별 문자열). 디버깅·로그·표시용.
73
+ - `manufacturerName: string` — 제조사 표시명. 사람이 읽는 장치 라벨을 만들 때.
76
74
  - `productName: string` — 제품 표시명. 목록 UI 의 장치 항목 라벨에 사용.
77
75
  - `vendorId: number` — 벤더 ID. 이 값을 `UsbDeviceFilter.vendorId` 로 옮겨 권한·읽기 호출에 사용.
78
76
  - `productId: number` — 제품 ID. 이 값을 `UsbDeviceFilter.productId` 로 옮겨 권한·읽기 호출에 사용.
@@ -1,18 +1,18 @@
1
1
  # @simplysm/core-browser
2
2
 
3
- 브라우저 전용 유틸리티 — DOM 요소 탐색·위치 계산·가시성 확장, IndexedDB 영속화 및 가상 파일시스템, Blob 다운로드·URL 바이너리 수신·파일 선택 대화상자.
3
+ 브라우저 전용 유틸리티 — `Element`/`HTMLElement` 프로토타입 확장(조회·순회·위치·가시성)과 클립보드/경계 측정 함수, IndexedDB 키-값 영속화 및 그 위의 가상 파일트리, Blob 다운로드·URL 바이너리 수신·파일 선택 대화상자.
4
4
 
5
- 이 패키지의 심볼을 하나라도 import 하면 `index.ts` 가 `element-ext`/`html-element-ext` 를 사이드 이펙트로 실행해 `Element`/`HTMLElement` 프로토타입 확장 메서드가 전역에서 자동 활성화된다(별도 초기화 불필요).
5
+ 이 패키지의 심볼을 하나라도 import 하면 `index.ts` 가 `element-ext`/`html-element-ext` 를 사이드 이펙트로 실행한다. 그 시점에 `typeof Element !== "undefined"` 가드를 거쳐 브라우저에서만 `Element`/`HTMLElement` 프로토타입에 확장 메서드를 등록한다(SSR/node 에선 가드로 건너뜀, 별도 초기화 불필요).
6
6
 
7
7
  ## 사용 트리거 인덱스
8
8
 
9
- - **DOM 요소 확장** — DOM 조회(findAll/findFirst)·조상/자식 순회·탭 이동 대상 탐색·offset/가시성 판정·부모 기준 상대 좌표 계산·가림 보정 스크롤·강제 리페인트·클립보드 복사/붙여넣기 핸들러·다중 요소 경계 측정이 필요할 때. 자세히: [dom-element.md](./dom-element.md)
10
- - **IndexedDB 영속화** — 브라우저 IndexedDB 에 키-값을 영구 저장(IndexedDbStore)하거나, 그 위에 경로 키 기반 가상 파일트리(IndexedDbVirtualFs)를 올릴 때. 자세히: [indexed-db.md](./indexed-db.md)
11
- - **파일 입출력** — 메모리 Blob 을 파일로 내려받거나(downloadBlob), URL 에서 진행률과 함께 바이너리를 받거나(fetchUrlBytes), 파일 선택 대화상자를 코드로 열 때(openFileDialog). 아래 인라인 섹션 참조.
9
+ - **DOM 요소 확장** — DOM 조회(`findAll`/`findFirst`)·조상·자식 순회·탭 이동 대상 탐색·offset/가시성 판정·부모 기준 상대 좌표·가림 보정 스크롤·강제 리페인트, 그리고 클립보드 복사/붙여넣기 이벤트 핸들러·다중 요소 경계 측정이 필요할 때. 자세히: [dom-element.md](./dom-element.md)
10
+ - **IndexedDB 영속화** — 브라우저 IndexedDB 에 키-값을 영구 저장(`IndexedDbStore`)하거나, 그 위에 경로 키 기반 가상 파일트리(`IndexedDbVirtualFs`)를 올릴 때. 자세히: [indexed-db.md](./indexed-db.md)
11
+ - **파일 입출력** — 메모리 Blob 을 파일로 내려받거나(`downloadBlob`), URL 에서 진행률과 함께 바이너리를 받거나(`fetchUrlBytes`), 파일 선택 대화상자를 코드로 열 때(`openFileDialog`). 아래 인라인 섹션 참조.
12
12
 
13
13
  ## 파일 입출력
14
14
 
15
- 데이터를 파일로 내보내거나, 외부 바이너리를 받거나, 사용자에게 파일을 고르게 하는 단발성 함수 묶음.
15
+ 데이터를 파일로 내보내거나, 외부 바이너리를 받거나, 사용자에게 파일을 고르게 하는 단발성 함수 묶음. named export 이므로 직접 import.
16
16
 
17
17
  ### downloadBlob
18
18
 
@@ -20,10 +20,10 @@
20
20
  function downloadBlob(blob: Blob, fileName: string): void;
21
21
  ```
22
22
 
23
- Blob object URL 만들어 동적 `a[download]` 클릭으로 저장하고, object URL 은 1초 뒤 revoke 한다. 클릭 직후 반환되며 다운로드 완료를 기다리지 않는다. 다운로드 버튼 핸들러에서 즉시 저장할 때.
23
+ `blob` 으로 object URL 만들어 동적 `a[download]` 요소를 클릭해 저장하고, object URL 은 1초 뒤 `setTimeout` 으로 revoke 한다(`finally` 에 있어 클릭이 throw 해도 revoke 됨). 클릭 직후 동기 반환하며 다운로드 완료는 기다리지 않는다. 다운로드 버튼 핸들러에서 즉시 저장할 때.
24
24
 
25
25
  - `blob: Blob` — 저장할 데이터. 엑셀·이미지·텍스트 등 메모리에서 만든 Blob 을 그대로 전달.
26
- - `fileName: string` — 저장 파일명. `sanitize-filename` 으로 OS 금지 문자·예약어를 제거한 추가로 `[`·`]` 도 제거하며, 결과가 빈 문자열이면 `"download"` 로 대체. 확장자 포함, 사용자 입력 파일명을 그대로 넣어도 안전.
26
+ - `fileName: string` — 저장 파일명. `sanitize-filename` 으로 OS 금지 문자·예약어를 제거하고 추가로 `[`·`]` 도 제거한 뒤 사용하며, 결과가 빈 문자열이면 `"download"` 로 대체(예: 예약어 `CON.txt` → `"download"`). 확장자 포함, 사용자 입력 파일명을 그대로 넣어도 안전.
27
27
 
28
28
  ```ts
29
29
  downloadBlob(new Blob([buf], { type: "application/pdf" }), "보고서[2026].pdf");
@@ -39,12 +39,12 @@ function fetchUrlBytes(
39
39
  ): Promise<Uint8Array>;
40
40
  ```
41
41
 
42
- URL 바이너리를 스트림 reader 로 다운로드하며 진행률을 보고한다. 큰 파일을 진행 바와 함께 받을 때.
42
+ `fetch` 응답 본문을 스트림 reader 로 청크 단위 수신해 `Uint8Array` 로 반환하며 진행률을 보고한다. 큰 파일을 진행 바와 함께 받을 때.
43
43
 
44
- - `url: string` — 다운로드 대상 URL. `response.ok` 가 아니면 `Error("다운로드 실패: <status> <statusText>")`, 본문 reader 가 없으면 `Error("응답 본문을 읽을 수 없습니다")` throw.
45
- - `options.onProgress?: (progress: DownloadProgress) => void` — 청크 수신마다 호출되는 진행 콜백. `Content-Length` 헤더가 있는 경로에서만 호출됨(헤더가 없으면 청크를 모아 `bytes.concat` 으로 마지막에 한 번 병합chunked encoding 이라 중간 보고 없음). 진행 바 갱신이 필요할 때만 전달.
44
+ - `url: string` — 다운로드 대상 URL. `response.ok` 가 아니면 `Error("다운로드 실패: <status> <statusText>")`, 본문 reader 가 없으면 `Error("응답 본문을 읽을 수 없습니다")` throw. 종료 시 `finally` 에서 `reader.releaseLock()` 호출(에러 경로 포함).
45
+ - `options.onProgress?: (progress: DownloadProgress) => void` — 청크 수신마다 호출되는 진행 콜백. `Content-Length` 헤더가 있는 경로에서만 호출됨. 헤더가 없으면 청크를 모아 `bytes.concat` 으로 마지막에 한 번 병합(chunked encoding 이라 중간 진행 보고 없음). 진행 바 갱신이 필요할 때만 전달.
46
46
  - `DownloadProgress.receivedLength: number` — 지금까지 받은 누적 바이트 수.
47
- - `DownloadProgress.contentLength: number` — 전체 바이트 수(`Content-Length` 헤더 값, 없으면 0). 헤더가 있으면 그 크기로 버퍼를 사전 할당하고, 수신량이 헤더 값을 초과·미달하면 무결성 위반으로 Error throw.
47
+ - `DownloadProgress.contentLength: number` — 전체 바이트 수(`Content-Length` 헤더 값, 없으면 0). 헤더가 있으면 그 크기로 버퍼를 사전 할당하고, 수신량이 헤더 값을 초과하면 `"Content-Length를 초과"`, 미달하면 `"Content-Length보다 부족"` Error 로 무결성을 검증한다.
48
48
 
49
49
  ```ts
50
50
  const data = await fetchUrlBytes("/api/file", {
@@ -58,11 +58,11 @@ const data = await fetchUrlBytes("/api/file", {
58
58
  function openFileDialog(options?: { accept?: string; multiple?: boolean }): Promise<File[] | undefined>;
59
59
  ```
60
60
 
61
- 동적 `input[type=file]` 을 만들어 클릭해 파일 선택 대화상자를 표시한다. 업로드 버튼 핸들러에서 호출.
61
+ 동적 `input[type=file]` 을 만들어 `click()` 으로 파일 선택 대화상자를 표시하고, 선택/취소 결과를 Promise 로 반환한다. 업로드 버튼 핸들러에서 호출.
62
62
 
63
63
  - `options.accept?: string` — 허용 MIME/확장자 필터(input `accept` 에 그대로 전달, 예: `".png,.jpg"`, `"image/*"`). 미지정 시 제한 없음.
64
- - `options.multiple?: boolean` — 다중 선택 허용. true 면 여러 파일 선택 가능, 기본 `false`(단일). 여러 파일을 한 번에 받을 화면이면 true.
65
- - 반환: 선택 파일이 있으면 `File[]`, 사용자가 취소하거나(`cancel` 이벤트) 0개 선택이면 `undefined`. 결측을 빈 배열로 뭉개지 않으므로 `== null` 로 취소를 구분.
64
+ - `options.multiple?: boolean` — 다중 선택 허용. `true` 면 여러 파일 선택 가능, 기본 `false`(단일). 여러 파일을 한 번에 받을 화면이면 `true`.
65
+ - 반환: 선택 파일이 있으면 `File[]`, 0개 선택이거나 사용자가 취소(`cancel` 이벤트)하면 `undefined`. 결측을 빈 배열로 뭉개지 않으므로 `== null` 로 취소를 구분.
66
66
 
67
67
  ```ts
68
68
  const files = await openFileDialog({ accept: ".csv", multiple: true });