@simplysm/capacitor-plugin-usb-storage 12.16.27 → 13.0.0-beta.10

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 (99) hide show
  1. package/README.md +233 -0
  2. package/android/src/main/java/kr/co/simplysm/capacitor/usbstorage/UsbStoragePlugin.java +60 -45
  3. package/dist/IUsbStoragePlugin.js +1 -1
  4. package/dist/IUsbStoragePlugin.js.map +7 -0
  5. package/dist/UsbStorage.js +58 -58
  6. package/dist/UsbStorage.js.map +7 -0
  7. package/dist/{IUsbStoragePlugin.d.ts → capacitor-plugin-usb-storage/src/IUsbStoragePlugin.d.ts} +6 -1
  8. package/dist/capacitor-plugin-usb-storage/src/IUsbStoragePlugin.d.ts.map +1 -0
  9. package/dist/{UsbStorage.d.ts → capacitor-plugin-usb-storage/src/UsbStorage.d.ts} +10 -20
  10. package/dist/capacitor-plugin-usb-storage/src/UsbStorage.d.ts.map +1 -0
  11. package/dist/{index.d.ts → capacitor-plugin-usb-storage/src/index.d.ts} +1 -0
  12. package/dist/capacitor-plugin-usb-storage/src/index.d.ts.map +1 -0
  13. package/dist/capacitor-plugin-usb-storage/src/web/IndexedDbStore.d.ts +16 -0
  14. package/dist/capacitor-plugin-usb-storage/src/web/IndexedDbStore.d.ts.map +1 -0
  15. package/dist/capacitor-plugin-usb-storage/src/web/UsbStorageWeb.d.ts +43 -0
  16. package/dist/capacitor-plugin-usb-storage/src/web/UsbStorageWeb.d.ts.map +1 -0
  17. package/dist/capacitor-plugin-usb-storage/src/web/VirtualUsbStorage.d.ts +32 -0
  18. package/dist/capacitor-plugin-usb-storage/src/web/VirtualUsbStorage.d.ts.map +1 -0
  19. package/dist/core-common/src/common.types.d.ts +74 -0
  20. package/dist/core-common/src/common.types.d.ts.map +1 -0
  21. package/dist/core-common/src/env.d.ts +6 -0
  22. package/dist/core-common/src/env.d.ts.map +1 -0
  23. package/dist/core-common/src/errors/argument-error.d.ts +25 -0
  24. package/dist/core-common/src/errors/argument-error.d.ts.map +1 -0
  25. package/dist/core-common/src/errors/not-implemented-error.d.ts +29 -0
  26. package/dist/core-common/src/errors/not-implemented-error.d.ts.map +1 -0
  27. package/dist/core-common/src/errors/sd-error.d.ts +27 -0
  28. package/dist/core-common/src/errors/sd-error.d.ts.map +1 -0
  29. package/dist/core-common/src/errors/timeout-error.d.ts +31 -0
  30. package/dist/core-common/src/errors/timeout-error.d.ts.map +1 -0
  31. package/dist/core-common/src/extensions/arr-ext.d.ts +15 -0
  32. package/dist/core-common/src/extensions/arr-ext.d.ts.map +1 -0
  33. package/dist/core-common/src/extensions/arr-ext.helpers.d.ts +19 -0
  34. package/dist/core-common/src/extensions/arr-ext.helpers.d.ts.map +1 -0
  35. package/dist/core-common/src/extensions/arr-ext.types.d.ts +215 -0
  36. package/dist/core-common/src/extensions/arr-ext.types.d.ts.map +1 -0
  37. package/dist/core-common/src/extensions/map-ext.d.ts +57 -0
  38. package/dist/core-common/src/extensions/map-ext.d.ts.map +1 -0
  39. package/dist/core-common/src/extensions/set-ext.d.ts +36 -0
  40. package/dist/core-common/src/extensions/set-ext.d.ts.map +1 -0
  41. package/dist/core-common/src/features/debounce-queue.d.ts +53 -0
  42. package/dist/core-common/src/features/debounce-queue.d.ts.map +1 -0
  43. package/dist/core-common/src/features/event-emitter.d.ts +66 -0
  44. package/dist/core-common/src/features/event-emitter.d.ts.map +1 -0
  45. package/dist/core-common/src/features/serial-queue.d.ts +47 -0
  46. package/dist/core-common/src/features/serial-queue.d.ts.map +1 -0
  47. package/dist/core-common/src/index.d.ts +32 -0
  48. package/dist/core-common/src/index.d.ts.map +1 -0
  49. package/dist/core-common/src/types/date-only.d.ts +152 -0
  50. package/dist/core-common/src/types/date-only.d.ts.map +1 -0
  51. package/dist/core-common/src/types/date-time.d.ts +96 -0
  52. package/dist/core-common/src/types/date-time.d.ts.map +1 -0
  53. package/dist/core-common/src/types/lazy-gc-map.d.ts +80 -0
  54. package/dist/core-common/src/types/lazy-gc-map.d.ts.map +1 -0
  55. package/dist/core-common/src/types/time.d.ts +68 -0
  56. package/dist/core-common/src/types/time.d.ts.map +1 -0
  57. package/dist/core-common/src/types/uuid.d.ts +35 -0
  58. package/dist/core-common/src/types/uuid.d.ts.map +1 -0
  59. package/dist/core-common/src/utils/bytes.d.ts +51 -0
  60. package/dist/core-common/src/utils/bytes.d.ts.map +1 -0
  61. package/dist/core-common/src/utils/date-format.d.ts +90 -0
  62. package/dist/core-common/src/utils/date-format.d.ts.map +1 -0
  63. package/dist/core-common/src/utils/json.d.ts +34 -0
  64. package/dist/core-common/src/utils/json.d.ts.map +1 -0
  65. package/dist/core-common/src/utils/num.d.ts +60 -0
  66. package/dist/core-common/src/utils/num.d.ts.map +1 -0
  67. package/dist/core-common/src/utils/obj.d.ts +258 -0
  68. package/dist/core-common/src/utils/obj.d.ts.map +1 -0
  69. package/dist/core-common/src/utils/path.d.ts +23 -0
  70. package/dist/core-common/src/utils/path.d.ts.map +1 -0
  71. package/dist/core-common/src/utils/primitive.d.ts +18 -0
  72. package/dist/core-common/src/utils/primitive.d.ts.map +1 -0
  73. package/dist/core-common/src/utils/str.d.ts +103 -0
  74. package/dist/core-common/src/utils/str.d.ts.map +1 -0
  75. package/dist/core-common/src/utils/template-strings.d.ts +84 -0
  76. package/dist/core-common/src/utils/template-strings.d.ts.map +1 -0
  77. package/dist/core-common/src/utils/transferable.d.ts +47 -0
  78. package/dist/core-common/src/utils/transferable.d.ts.map +1 -0
  79. package/dist/core-common/src/utils/wait.d.ts +19 -0
  80. package/dist/core-common/src/utils/wait.d.ts.map +1 -0
  81. package/dist/core-common/src/utils/xml.d.ts +36 -0
  82. package/dist/core-common/src/utils/xml.d.ts.map +1 -0
  83. package/dist/core-common/src/zip/sd-zip.d.ts +80 -0
  84. package/dist/core-common/src/zip/sd-zip.d.ts.map +1 -0
  85. package/dist/index.js +1 -0
  86. package/dist/index.js.map +7 -0
  87. package/dist/web/IndexedDbStore.js +76 -0
  88. package/dist/web/IndexedDbStore.js.map +7 -0
  89. package/dist/web/UsbStorageWeb.js +77 -15
  90. package/dist/web/UsbStorageWeb.js.map +7 -0
  91. package/dist/web/VirtualUsbStorage.js +80 -0
  92. package/dist/web/VirtualUsbStorage.js.map +7 -0
  93. package/package.json +11 -4
  94. package/dist/web/UsbStorageWeb.d.ts +0 -23
  95. package/src/IUsbStoragePlugin.ts +0 -20
  96. package/src/UsbStorage.ts +0 -82
  97. package/src/index.ts +0 -2
  98. package/src/web/UsbStorageWeb.ts +0 -27
  99. package/tsconfig.json +0 -8
package/README.md ADDED
@@ -0,0 +1,233 @@
1
+ # @simplysm/capacitor-plugin-usb-storage
2
+
3
+ A Capacitor plugin for accessing USB Mass Storage devices. On Android, it directly accesses the USB storage device's file system through the [libaums](https://github.com/magnusja/libaums) library, while in web environments it provides an IndexedDB-based virtual USB storage to support development and testing.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @simplysm/capacitor-plugin-usb-storage
9
+ npx cap sync
10
+ ```
11
+
12
+ ### Peer Dependencies
13
+
14
+ | Package | Version |
15
+ |--------|------|
16
+ | `@capacitor/core` | `^7.4.4` |
17
+
18
+ ### Internal Dependencies
19
+
20
+ | Package | Description |
21
+ |--------|------|
22
+ | `@simplysm/core-common` | Common utilities such as Base64 conversion, `Bytes` type |
23
+
24
+ ## Supported Platforms
25
+
26
+ | Platform | Supported | Implementation |
27
+ |--------|-----------|-----------|
28
+ | Android | Yes | USB Mass Storage access through libaums 0.9.1 |
29
+ | Web | Yes (emulation) | IndexedDB-based virtual USB storage |
30
+ | iOS | No | -- |
31
+
32
+ ### Android Requirements
33
+
34
+ - `compileSdk`: 35
35
+ - `minSdk`: 23 (Android 6.0 or higher)
36
+ - Maximum file read size: 100MB
37
+
38
+ ## Main API
39
+
40
+ ### UsbStorage (Static Class)
41
+
42
+ The main entry point of the plugin. All methods are static and operate asynchronously.
43
+
44
+ | Method | Return Type | Description |
45
+ |--------|-----------|------|
46
+ | `getDevices()` | `Promise<IUsbDeviceInfo[]>` | Retrieve list of connected USB devices |
47
+ | `requestPermission(filter)` | `Promise<boolean>` | Request USB device access permission |
48
+ | `hasPermission(filter)` | `Promise<boolean>` | Check if USB device access permission is granted |
49
+ | `readdir(filter, dirPath)` | `Promise<IUsbFileInfo[]>` | Read list of files/folders in directory |
50
+ | `read(filter, filePath)` | `Promise<Bytes \| undefined>` | Read file contents as binary |
51
+
52
+ ### Interfaces
53
+
54
+ #### IUsbDeviceInfo
55
+
56
+ Represents USB device information.
57
+
58
+ | Property | Type | Description |
59
+ |------|------|------|
60
+ | `deviceName` | `string` | Device name (system path) |
61
+ | `manufacturerName` | `string` | Manufacturer name |
62
+ | `productName` | `string` | Product name |
63
+ | `vendorId` | `number` | USB Vendor ID |
64
+ | `productId` | `number` | USB Product ID |
65
+
66
+ #### IUsbDeviceFilter
67
+
68
+ A filter for identifying specific USB devices. Specifies the device using the combination of `vendorId` and `productId`.
69
+
70
+ | Property | Type | Description |
71
+ |------|------|------|
72
+ | `vendorId` | `number` | USB Vendor ID |
73
+ | `productId` | `number` | USB Product ID |
74
+
75
+ #### IUsbFileInfo
76
+
77
+ Represents file or directory information.
78
+
79
+ | Property | Type | Description |
80
+ |------|------|------|
81
+ | `name` | `string` | File/directory name |
82
+ | `isDirectory` | `boolean` | Whether it's a directory |
83
+
84
+ ## Usage Examples
85
+
86
+ ### List Devices and Request Permission
87
+
88
+ ```typescript
89
+ import { UsbStorage } from "@simplysm/capacitor-plugin-usb-storage";
90
+
91
+ // Retrieve connected USB device list
92
+ const devices = await UsbStorage.getDevices();
93
+ console.log("Connected devices:", devices);
94
+
95
+ if (devices.length > 0) {
96
+ const device = devices[0];
97
+ const filter = { vendorId: device.vendorId, productId: device.productId };
98
+
99
+ // Check permission
100
+ const hasPerm = await UsbStorage.hasPermission(filter);
101
+ if (!hasPerm) {
102
+ // Request permission (displays system dialog on Android)
103
+ const granted = await UsbStorage.requestPermission(filter);
104
+ if (!granted) {
105
+ console.log("USB device access permission was denied.");
106
+ return;
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ ### Read Directory Contents
113
+
114
+ ```typescript
115
+ import { UsbStorage } from "@simplysm/capacitor-plugin-usb-storage";
116
+
117
+ const filter = { vendorId: 1234, productId: 5678 };
118
+
119
+ // Read root directory
120
+ const rootFiles = await UsbStorage.readdir(filter, "/");
121
+ for (const file of rootFiles) {
122
+ const type = file.isDirectory ? "[DIR]" : "[FILE]";
123
+ console.log(`${type} ${file.name}`);
124
+ }
125
+
126
+ // Read subdirectory
127
+ const subFiles = await UsbStorage.readdir(filter, "/Documents");
128
+ ```
129
+
130
+ ### Read File
131
+
132
+ ```typescript
133
+ import { UsbStorage } from "@simplysm/capacitor-plugin-usb-storage";
134
+
135
+ const filter = { vendorId: 1234, productId: 5678 };
136
+
137
+ // Read file as binary (Bytes)
138
+ const data = await UsbStorage.read(filter, "/data/config.json");
139
+ if (data != null) {
140
+ // Convert Bytes to string
141
+ const text = new TextDecoder().decode(data);
142
+ console.log("File contents:", text);
143
+ }
144
+ ```
145
+
146
+ ### Complete Flow Example
147
+
148
+ ```typescript
149
+ import { UsbStorage } from "@simplysm/capacitor-plugin-usb-storage";
150
+ import type { IUsbDeviceFilter } from "@simplysm/capacitor-plugin-usb-storage";
151
+
152
+ async function readUsbFile(filePath: string): Promise<string | undefined> {
153
+ // 1. Search for device
154
+ const devices = await UsbStorage.getDevices();
155
+ if (devices.length === 0) {
156
+ throw new Error("No USB device is connected.");
157
+ }
158
+
159
+ const device = devices[0];
160
+ const filter: IUsbDeviceFilter = {
161
+ vendorId: device.vendorId,
162
+ productId: device.productId,
163
+ };
164
+
165
+ // 2. Secure permission
166
+ const hasPerm = await UsbStorage.hasPermission(filter);
167
+ if (!hasPerm) {
168
+ const granted = await UsbStorage.requestPermission(filter);
169
+ if (!granted) {
170
+ throw new Error("USB device access permission is required.");
171
+ }
172
+ }
173
+
174
+ // 3. Read file
175
+ const data = await UsbStorage.read(filter, filePath);
176
+ if (data == null) {
177
+ return undefined;
178
+ }
179
+
180
+ return new TextDecoder().decode(data);
181
+ }
182
+ ```
183
+
184
+ ## Web Emulation (Development/Testing)
185
+
186
+ In web environments, the `UsbStorageWeb` class is automatically used, providing an IndexedDB-based virtual USB storage. Permission requests are always processed as approved.
187
+
188
+ `UsbStorageWeb` provides methods for adding virtual devices and files for development and testing purposes.
189
+
190
+ | Method | Description |
191
+ |--------|------|
192
+ | `addVirtualDevice(device)` | Register a virtual USB device |
193
+ | `addVirtualFile(filter, filePath, data)` | Add a file to virtual device (parent directories created automatically) |
194
+ | `addVirtualDirectory(filter, dirPath)` | Add a directory to virtual device |
195
+
196
+ ### Web Emulation Usage Example
197
+
198
+ ```typescript
199
+ import { UsbStorageWeb } from "@simplysm/capacitor-plugin-usb-storage/dist/web/UsbStorageWeb";
200
+
201
+ const web = new UsbStorageWeb();
202
+
203
+ // Add virtual device
204
+ await web.addVirtualDevice({
205
+ vendorId: 1234,
206
+ productId: 5678,
207
+ deviceName: "Virtual USB",
208
+ manufacturerName: "Test Manufacturer",
209
+ productName: "Test USB Drive",
210
+ });
211
+
212
+ const filter = { vendorId: 1234, productId: 5678 };
213
+
214
+ // Add virtual file
215
+ const content = new TextEncoder().encode("Hello, USB!");
216
+ await web.addVirtualFile(filter, "/test/hello.txt", content);
217
+
218
+ // Add virtual directory
219
+ await web.addVirtualDirectory(filter, "/test/subdir");
220
+ ```
221
+
222
+ ## Android Native Implementation Details
223
+
224
+ The Android native layer uses the `libaums` library to directly handle the USB Mass Storage protocol. Key operations are as follows.
225
+
226
+ - **Device Search**: Uses `UsbMassStorageDevice.getMassStorageDevices()` to query connected USB Mass Storage devices.
227
+ - **Permission Management**: Requests and verifies USB device access permission through Android's `UsbManager`. On Android 12 (API 31) and above, it uses `PendingIntent.FLAG_MUTABLE`, and on Android 13 (API 33) and above, it applies the `RECEIVER_NOT_EXPORTED` flag.
228
+ - **File System Access**: Mounts the file system of the first partition to perform directory navigation and file reading.
229
+ - **Data Transfer**: File data is Base64-encoded for transmission to the JavaScript layer.
230
+
231
+ ## License
232
+
233
+ MIT
@@ -32,6 +32,7 @@ public class UsbStoragePlugin extends Plugin {
32
32
 
33
33
  private static final String TAG = "UsbStoragePlugin";
34
34
  private static final String ACTION_USB_PERMISSION = "kr.co.simplysm.capacitor.usbstorage.USB_PERMISSION";
35
+ private static final long MAX_FILE_SIZE = 100L * 1024 * 1024; // 100MB
35
36
 
36
37
  @PluginMethod
37
38
  public void getDevices(PluginCall call) {
@@ -158,31 +159,37 @@ public class UsbStoragePlugin extends Plugin {
158
159
 
159
160
  UsbManager usbManager = (UsbManager) getContext().getSystemService(Context.USB_SERVICE);
160
161
  if (!usbManager.hasPermission(device.getUsbDevice())) {
161
- call.reject("USB 장치에 대한 접근 권한이 없습니다.");
162
+ call.reject("No permission for this USB device");
162
163
  return;
163
164
  }
164
165
 
165
166
  device.init();
167
+ try {
168
+ FileSystem fs = device.getPartitions().get(0).getFileSystem();
169
+ UsbFile root = fs.getRootDirectory();
170
+ UsbFile dir = root.search(path);
171
+
172
+ if (dir == null || !dir.isDirectory()) {
173
+ call.reject("Directory not found: " + path);
174
+ return;
175
+ }
166
176
 
167
- FileSystem fs = device.getPartitions().get(0).getFileSystem();
168
- UsbFile root = fs.getRootDirectory();
169
- UsbFile dir = root.search(path);
170
-
171
- if (dir == null || !dir.isDirectory()) {
172
- call.reject("Directory not found: " + path);
173
- return;
174
- }
177
+ UsbFile[] files = dir.listFiles();
175
178
 
176
- UsbFile[] files = dir.listFiles();
179
+ JSArray result = new JSArray();
180
+ for (UsbFile file : files) {
181
+ JSObject info = new JSObject();
182
+ info.put("name", file.getName());
183
+ info.put("isDirectory", file.isDirectory());
184
+ result.put(info);
185
+ }
177
186
 
178
- JSArray result = new JSArray();
179
- for (UsbFile file : files) {
180
- result.put(file.getName());
187
+ JSObject ret = new JSObject();
188
+ ret.put("files", result);
189
+ call.resolve(ret);
190
+ } finally {
191
+ device.close();
181
192
  }
182
-
183
- JSObject ret = new JSObject();
184
- ret.put("files", result);
185
- call.resolve(ret);
186
193
  } catch (Exception e) {
187
194
  Log.e(TAG, "readdir failed", e);
188
195
  call.reject("readdir failed: " + e.getMessage());
@@ -205,43 +212,51 @@ public class UsbStoragePlugin extends Plugin {
205
212
 
206
213
  UsbManager usbManager = (UsbManager) getContext().getSystemService(Context.USB_SERVICE);
207
214
  if (!usbManager.hasPermission(device.getUsbDevice())) {
208
- call.reject("USB 장치에 대한 접근 권한이 없습니다.");
215
+ call.reject("No permission for this USB device");
209
216
  return;
210
217
  }
211
218
 
212
219
  device.init();
220
+ try {
221
+ FileSystem fs = device.getPartitions().get(0).getFileSystem();
222
+ UsbFile root = fs.getRootDirectory();
223
+ UsbFile usbFile = root.search(path);
224
+
225
+ if (usbFile == null) {
226
+ JSObject ret = new JSObject();
227
+ ret.put("data", (String) null);
228
+ call.resolve(ret);
229
+ return;
230
+ }
213
231
 
214
- FileSystem fs = device.getPartitions().get(0).getFileSystem();
215
- UsbFile root = fs.getRootDirectory();
216
- UsbFile usbFile = root.search(path);
232
+ if (usbFile.isDirectory()) {
233
+ call.reject("Path is a directory: " + path);
234
+ return;
235
+ }
217
236
 
218
- if (usbFile == null) {
219
- JSObject ret = new JSObject();
220
- ret.put("data", (String) null);
221
- call.resolve(ret);
222
- return;
223
- }
237
+ long fileLength = usbFile.getLength();
238
+ if (fileLength > MAX_FILE_SIZE) {
239
+ call.reject("File too large: " + fileLength + " bytes (max " + MAX_FILE_SIZE + ")");
240
+ return;
241
+ }
242
+ ByteBuffer buffer = ByteBuffer.allocate((int) fileLength);
224
243
 
225
- if (usbFile.isDirectory()) {
226
- call.reject("해당 경로는 폴더입니다.");
227
- return;
228
- }
244
+ UsbFileInputStream inputStream = new UsbFileInputStream(usbFile);
245
+ byte[] tmpBuf = new byte[fs.getChunkSize()];
246
+ int count;
247
+ while ((count = inputStream.read(tmpBuf)) != -1) {
248
+ buffer.put(tmpBuf, 0, count);
249
+ }
250
+ inputStream.close();
229
251
 
230
- ByteBuffer buffer = ByteBuffer.allocate((int) usbFile.getLength());
252
+ String base64Data = Base64.encodeToString(buffer.array(), Base64.NO_WRAP);
231
253
 
232
- UsbFileInputStream inputStream = new UsbFileInputStream(usbFile);
233
- byte[] tmpBuf = new byte[fs.getChunkSize()];
234
- int count;
235
- while ((count = inputStream.read(tmpBuf)) != -1) {
236
- buffer.put(tmpBuf, 0, count);
254
+ JSObject ret = new JSObject();
255
+ ret.put("data", base64Data);
256
+ call.resolve(ret);
257
+ } finally {
258
+ device.close();
237
259
  }
238
- inputStream.close();
239
-
240
- String base64Data = Base64.encodeToString(buffer.array(), Base64.NO_WRAP);
241
-
242
- JSObject ret = new JSObject();
243
- ret.put("data", base64Data);
244
- call.resolve(ret);
245
260
  } catch (Exception e) {
246
261
  Log.e(TAG, "read failed", e);
247
262
  call.reject("read failed: " + e.getMessage());
@@ -256,7 +271,7 @@ public class UsbStoragePlugin extends Plugin {
256
271
  }).findFirst();
257
272
 
258
273
  if (!optDevice.isPresent()) {
259
- throw new Exception("USB 장치를 찾을 없습니다.");
274
+ throw new Exception("USB device not found: vendorId=" + vendorId + ", productId=" + productId);
260
275
  }
261
276
  return optDevice.get();
262
277
  }
@@ -1 +1 @@
1
- export {};
1
+ //# sourceMappingURL=IUsbStoragePlugin.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -1,63 +1,63 @@
1
1
  import { registerPlugin } from "@capacitor/core";
2
+ import { bytesFromBase64 } from "@simplysm/core-common";
2
3
  const UsbStoragePlugin = registerPlugin("UsbStorage", {
3
- web: async () => {
4
- const { UsbStorageWeb } = await import("./web/UsbStorageWeb");
5
- return new UsbStorageWeb();
6
- },
4
+ web: async () => {
5
+ const { UsbStorageWeb } = await import("./web/UsbStorageWeb");
6
+ return new UsbStorageWeb();
7
+ }
7
8
  });
8
- /**
9
- * USB 저장장치와 상호작용하기 위한 플러그인
10
- * - Android: libaums 라이브러리를 통한 USB Mass Storage 접근
11
- * - Browser: alert으로 안내 빈 값 반환
12
- */
13
- export class UsbStorage {
14
- /**
15
- * 연결된 USB 장치 목록을 가져옴
16
- * @returns 연결된 USB 장치 정보 배열
17
- */
18
- static async getDevices() {
19
- const result = await UsbStoragePlugin.getDevices();
20
- return result.devices;
21
- }
22
- /**
23
- * USB 장치 접근 권한을 요청
24
- * @param filter 권한을 요청할 USB 장치의 vendorId와 productId
25
- * @returns 권한 승인 여부
26
- */
27
- static async requestPermission(filter) {
28
- const result = await UsbStoragePlugin.requestPermission(filter);
29
- return result.granted;
30
- }
31
- /**
32
- * USB 장치 접근 권한이 있는지 확인
33
- * @param filter 권한을 확인할 USB 장치의 vendorId와 productId
34
- * @returns 권한 보유 여부
35
- */
36
- static async hasPermission(filter) {
37
- const result = await UsbStoragePlugin.hasPermission(filter);
38
- return result.granted;
39
- }
40
- /**
41
- * USB 저장장치의 디렉토리 내용을 읽어옴
42
- * @param filter 대상 USB 장치의 vendorId와 productId
43
- * @param dirPath 읽어올 디렉토리 경로
44
- * @returns 디렉토리 내 파일/폴더 이름 배열
45
- */
46
- static async readdir(filter, dirPath) {
47
- const result = await UsbStoragePlugin.readdir({ ...filter, path: dirPath });
48
- return result.files;
49
- }
50
- /**
51
- * USB 저장장치의 파일을 읽어옴
52
- * @param filter 대상 USB 장치의 vendorId와 productId
53
- * @param filePath 읽어올 파일 경로
54
- * @returns 파일 데이터를 담은 Buffer 또는 undefined
55
- */
56
- static async read(filter, filePath) {
57
- const result = await UsbStoragePlugin.read({ ...filter, path: filePath });
58
- if (result.data == null) {
59
- return undefined;
60
- }
61
- return Buffer.from(result.data, "base64");
9
+ class UsbStorage {
10
+ /**
11
+ * 연결된 USB 장치 목록을 가져옴
12
+ * @returns 연결된 USB 장치 정보 배열
13
+ */
14
+ static async getDevices() {
15
+ const result = await UsbStoragePlugin.getDevices();
16
+ return result.devices;
17
+ }
18
+ /**
19
+ * USB 장치 접근 권한을 요청
20
+ * @param filter 권한을 요청할 USB 장치의 vendorId와 productId
21
+ * @returns 권한 승인 여부
22
+ */
23
+ static async requestPermission(filter) {
24
+ const result = await UsbStoragePlugin.requestPermission(filter);
25
+ return result.granted;
26
+ }
27
+ /**
28
+ * USB 장치 접근 권한이 있는지 확인
29
+ * @param filter 권한을 확인할 USB 장치의 vendorId와 productId
30
+ * @returns 권한 보유 여부
31
+ */
32
+ static async hasPermission(filter) {
33
+ const result = await UsbStoragePlugin.hasPermission(filter);
34
+ return result.granted;
35
+ }
36
+ /**
37
+ * USB 저장장치의 디렉토리 내용을 읽어옴
38
+ * @param filter 대상 USB 장치의 vendorId와 productId
39
+ * @param dirPath 읽어올 디렉토리 경로
40
+ * @returns 디렉토리 내 파일/폴더 정보 배열
41
+ */
42
+ static async readdir(filter, dirPath) {
43
+ const result = await UsbStoragePlugin.readdir({ ...filter, path: dirPath });
44
+ return result.files;
45
+ }
46
+ /**
47
+ * USB 저장장치의 파일을 읽어옴
48
+ * @param filter 대상 USB 장치의 vendorId와 productId
49
+ * @param filePath 읽어올 파일 경로
50
+ * @returns 파일 데이터를 담은 Bytes 또는 undefined
51
+ */
52
+ static async read(filter, filePath) {
53
+ const result = await UsbStoragePlugin.read({ ...filter, path: filePath });
54
+ if (result.data == null) {
55
+ return void 0;
62
56
  }
57
+ return bytesFromBase64(result.data);
58
+ }
63
59
  }
60
+ export {
61
+ UsbStorage
62
+ };
63
+ //# sourceMappingURL=UsbStorage.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/UsbStorage.ts"],
4
+ "sourcesContent": ["import { registerPlugin } from \"@capacitor/core\";\nimport type { IUsbDeviceFilter, IUsbDeviceInfo, IUsbFileInfo, IUsbStoragePlugin } from \"./IUsbStoragePlugin\";\nimport type { Bytes } from \"@simplysm/core-common\";\nimport { bytesFromBase64 } from \"@simplysm/core-common\";\n\nconst UsbStoragePlugin = registerPlugin<IUsbStoragePlugin>(\"UsbStorage\", {\n web: async () => {\n const { UsbStorageWeb } = await import(\"./web/UsbStorageWeb\");\n return new UsbStorageWeb();\n },\n});\n\n/**\n * USB \uC800\uC7A5\uC7A5\uCE58\uC640 \uC0C1\uD638\uC791\uC6A9\uD558\uAE30 \uC704\uD55C \uD50C\uB7EC\uADF8\uC778\n * - Android: libaums \uB77C\uC774\uBE0C\uB7EC\uB9AC\uB97C \uD1B5\uD55C USB Mass Storage \uC811\uADFC\n * - Browser: IndexedDB \uAE30\uBC18 \uAC00\uC0C1 USB \uC800\uC7A5\uC18C \uC5D0\uBBAC\uB808\uC774\uC158\n */\nexport abstract class UsbStorage {\n /**\n * \uC5F0\uACB0\uB41C USB \uC7A5\uCE58 \uBAA9\uB85D\uC744 \uAC00\uC838\uC634\n * @returns \uC5F0\uACB0\uB41C USB \uC7A5\uCE58 \uC815\uBCF4 \uBC30\uC5F4\n */\n static async getDevices(): Promise<IUsbDeviceInfo[]> {\n const result = await UsbStoragePlugin.getDevices();\n return result.devices;\n }\n\n /**\n * USB \uC7A5\uCE58 \uC811\uADFC \uAD8C\uD55C\uC744 \uC694\uCCAD\n * @param filter \uAD8C\uD55C\uC744 \uC694\uCCAD\uD560 USB \uC7A5\uCE58\uC758 vendorId\uC640 productId\n * @returns \uAD8C\uD55C \uC2B9\uC778 \uC5EC\uBD80\n */\n static async requestPermission(filter: IUsbDeviceFilter): Promise<boolean> {\n const result = await UsbStoragePlugin.requestPermission(filter);\n return result.granted;\n }\n\n /**\n * USB \uC7A5\uCE58 \uC811\uADFC \uAD8C\uD55C\uC774 \uC788\uB294\uC9C0 \uD655\uC778\n * @param filter \uAD8C\uD55C\uC744 \uD655\uC778\uD560 USB \uC7A5\uCE58\uC758 vendorId\uC640 productId\n * @returns \uAD8C\uD55C \uBCF4\uC720 \uC5EC\uBD80\n */\n static async hasPermission(filter: IUsbDeviceFilter): Promise<boolean> {\n const result = await UsbStoragePlugin.hasPermission(filter);\n return result.granted;\n }\n\n /**\n * USB \uC800\uC7A5\uC7A5\uCE58\uC758 \uB514\uB809\uD1A0\uB9AC \uB0B4\uC6A9\uC744 \uC77D\uC5B4\uC634\n * @param filter \uB300\uC0C1 USB \uC7A5\uCE58\uC758 vendorId\uC640 productId\n * @param dirPath \uC77D\uC5B4\uC62C \uB514\uB809\uD1A0\uB9AC \uACBD\uB85C\n * @returns \uB514\uB809\uD1A0\uB9AC \uB0B4 \uD30C\uC77C/\uD3F4\uB354 \uC815\uBCF4 \uBC30\uC5F4\n */\n static async readdir(filter: IUsbDeviceFilter, dirPath: string): Promise<IUsbFileInfo[]> {\n const result = await UsbStoragePlugin.readdir({ ...filter, path: dirPath });\n return result.files;\n }\n\n /**\n * USB \uC800\uC7A5\uC7A5\uCE58\uC758 \uD30C\uC77C\uC744 \uC77D\uC5B4\uC634\n * @param filter \uB300\uC0C1 USB \uC7A5\uCE58\uC758 vendorId\uC640 productId\n * @param filePath \uC77D\uC5B4\uC62C \uD30C\uC77C \uACBD\uB85C\n * @returns \uD30C\uC77C \uB370\uC774\uD130\uB97C \uB2F4\uC740 Bytes \uB610\uB294 undefined\n */\n static async read(filter: IUsbDeviceFilter, filePath: string): Promise<Bytes | undefined> {\n const result = await UsbStoragePlugin.read({ ...filter, path: filePath });\n if (result.data == null) {\n return undefined;\n }\n return bytesFromBase64(result.data);\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,sBAAsB;AAG/B,SAAS,uBAAuB;AAEhC,MAAM,mBAAmB,eAAkC,cAAc;AAAA,EACvE,KAAK,YAAY;AACf,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,qBAAqB;AAC5D,WAAO,IAAI,cAAc;AAAA,EAC3B;AACF,CAAC;AAOM,MAAe,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA,EAK/B,aAAa,aAAwC;AACnD,UAAM,SAAS,MAAM,iBAAiB,WAAW;AACjD,WAAO,OAAO;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAa,kBAAkB,QAA4C;AACzE,UAAM,SAAS,MAAM,iBAAiB,kBAAkB,MAAM;AAC9D,WAAO,OAAO;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAa,cAAc,QAA4C;AACrE,UAAM,SAAS,MAAM,iBAAiB,cAAc,MAAM;AAC1D,WAAO,OAAO;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,QAAQ,QAA0B,SAA0C;AACvF,UAAM,SAAS,MAAM,iBAAiB,QAAQ,EAAE,GAAG,QAAQ,MAAM,QAAQ,CAAC;AAC1E,WAAO,OAAO;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,KAAK,QAA0B,UAA8C;AACxF,UAAM,SAAS,MAAM,iBAAiB,KAAK,EAAE,GAAG,QAAQ,MAAM,SAAS,CAAC;AACxE,QAAI,OAAO,QAAQ,MAAM;AACvB,aAAO;AAAA,IACT;AACA,WAAO,gBAAgB,OAAO,IAAI;AAAA,EACpC;AACF;",
6
+ "names": []
7
+ }
@@ -9,6 +9,10 @@ export interface IUsbDeviceFilter {
9
9
  vendorId: number;
10
10
  productId: number;
11
11
  }
12
+ export interface IUsbFileInfo {
13
+ name: string;
14
+ isDirectory: boolean;
15
+ }
12
16
  export interface IUsbStoragePlugin {
13
17
  getDevices(): Promise<{
14
18
  devices: IUsbDeviceInfo[];
@@ -22,7 +26,7 @@ export interface IUsbStoragePlugin {
22
26
  readdir(options: IUsbDeviceFilter & {
23
27
  path: string;
24
28
  }): Promise<{
25
- files: string[];
29
+ files: IUsbFileInfo[];
26
30
  }>;
27
31
  read(options: IUsbDeviceFilter & {
28
32
  path: string;
@@ -30,3 +34,4 @@ export interface IUsbStoragePlugin {
30
34
  data: string | null;
31
35
  }>;
32
36
  }
37
+ //# sourceMappingURL=IUsbStoragePlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IUsbStoragePlugin.d.ts","sourceRoot":"","sources":["../../../src/IUsbStoragePlugin.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,cAAc,EAAE,CAAA;KAAE,CAAC,CAAC;IACrD,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC5E,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACxE,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC,CAAC;IAC1F,IAAI,CAAC,OAAO,EAAE,gBAAgB,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CACtF"}
@@ -1,8 +1,9 @@
1
- import { IUsbDeviceInfo } from "./IUsbStoragePlugin";
1
+ import type { IUsbDeviceFilter, IUsbDeviceInfo, IUsbFileInfo } from "./IUsbStoragePlugin";
2
+ import type { Bytes } from "@simplysm/core-common";
2
3
  /**
3
4
  * USB 저장장치와 상호작용하기 위한 플러그인
4
5
  * - Android: libaums 라이브러리를 통한 USB Mass Storage 접근
5
- * - Browser: alert으로 안내 반환
6
+ * - Browser: IndexedDB 기반 가상 USB 저장소 에뮬레이션
6
7
  */
7
8
  export declare abstract class UsbStorage {
8
9
  /**
@@ -15,37 +16,26 @@ export declare abstract class UsbStorage {
15
16
  * @param filter 권한을 요청할 USB 장치의 vendorId와 productId
16
17
  * @returns 권한 승인 여부
17
18
  */
18
- static requestPermission(filter: {
19
- vendorId: number;
20
- productId: number;
21
- }): Promise<boolean>;
19
+ static requestPermission(filter: IUsbDeviceFilter): Promise<boolean>;
22
20
  /**
23
21
  * USB 장치 접근 권한이 있는지 확인
24
22
  * @param filter 권한을 확인할 USB 장치의 vendorId와 productId
25
23
  * @returns 권한 보유 여부
26
24
  */
27
- static hasPermission(filter: {
28
- vendorId: number;
29
- productId: number;
30
- }): Promise<boolean>;
25
+ static hasPermission(filter: IUsbDeviceFilter): Promise<boolean>;
31
26
  /**
32
27
  * USB 저장장치의 디렉토리 내용을 읽어옴
33
28
  * @param filter 대상 USB 장치의 vendorId와 productId
34
29
  * @param dirPath 읽어올 디렉토리 경로
35
- * @returns 디렉토리 내 파일/폴더 이름 배열
30
+ * @returns 디렉토리 내 파일/폴더 정보 배열
36
31
  */
37
- static readdir(filter: {
38
- vendorId: number;
39
- productId: number;
40
- }, dirPath: string): Promise<string[]>;
32
+ static readdir(filter: IUsbDeviceFilter, dirPath: string): Promise<IUsbFileInfo[]>;
41
33
  /**
42
34
  * USB 저장장치의 파일을 읽어옴
43
35
  * @param filter 대상 USB 장치의 vendorId와 productId
44
36
  * @param filePath 읽어올 파일 경로
45
- * @returns 파일 데이터를 담은 Buffer 또는 undefined
37
+ * @returns 파일 데이터를 담은 Bytes 또는 undefined
46
38
  */
47
- static read(filter: {
48
- vendorId: number;
49
- productId: number;
50
- }, filePath: string): Promise<Buffer | undefined>;
39
+ static read(filter: IUsbDeviceFilter, filePath: string): Promise<Bytes | undefined>;
51
40
  }
41
+ //# sourceMappingURL=UsbStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UsbStorage.d.ts","sourceRoot":"","sources":["../../../src/UsbStorage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,EAAqB,MAAM,qBAAqB,CAAC;AAC7G,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAUnD;;;;GAIG;AACH,8BAAsB,UAAU;IAC9B;;;OAGG;WACU,UAAU,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAKpD;;;;OAIG;WACU,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAK1E;;;;OAIG;WACU,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAKtE;;;;;OAKG;WACU,OAAO,CAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAKxF;;;;;OAKG;WACU,IAAI,CAAC,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;CAO1F"}
@@ -1,2 +1,3 @@
1
1
  export * from "./IUsbStoragePlugin";
2
2
  export * from "./UsbStorage";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface IStoreConfig {
2
+ name: string;
3
+ keyPath: string;
4
+ }
5
+ export declare class IndexedDbStore {
6
+ private readonly _dbName;
7
+ private readonly _dbVersion;
8
+ private readonly _storeConfigs;
9
+ constructor(_dbName: string, _dbVersion: number, _storeConfigs: IStoreConfig[]);
10
+ open(): Promise<IDBDatabase>;
11
+ withStore<T>(storeName: string, mode: IDBTransactionMode, fn: (store: IDBObjectStore) => Promise<T>): Promise<T>;
12
+ get<T>(storeName: string, key: IDBValidKey): Promise<T | undefined>;
13
+ put(storeName: string, value: unknown): Promise<void>;
14
+ getAll<T>(storeName: string): Promise<T[]>;
15
+ }
16
+ //# sourceMappingURL=IndexedDbStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IndexedDbStore.d.ts","sourceRoot":"","sources":["../../../../src/web/IndexedDbStore.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,cAAc;IAEvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,aAAa;gBAFb,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,YAAY,EAAE;IAG1C,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC;IAiB5B,SAAS,CAAC,CAAC,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,kBAAkB,EACxB,EAAE,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GACxC,OAAO,CAAC,CAAC,CAAC;IAyBP,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAUnE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrD,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;CASjD"}