@harryy/react-file-browser 0.0.0
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/LICENSE +21 -0
- package/README.md +247 -0
- package/dist/adapters/cloud-adapter-base.d.ts +27 -0
- package/dist/adapters/cloud-adapter-base.d.ts.map +1 -0
- package/dist/adapters/in-memory/in-memory-file-browser-adapter.d.ts +53 -0
- package/dist/adapters/in-memory/in-memory-file-browser-adapter.d.ts.map +1 -0
- package/dist/adapters/in-memory/index.d.ts +2 -0
- package/dist/adapters/in-memory/index.d.ts.map +1 -0
- package/dist/adapters/in-memory/index.js +270 -0
- package/dist/adapters/in-memory/index.js.map +1 -0
- package/dist/adapters/r2/index.d.ts +7 -0
- package/dist/adapters/r2/index.d.ts.map +1 -0
- package/dist/adapters/r2/index.js +11 -0
- package/dist/adapters/r2/index.js.map +1 -0
- package/dist/adapters/s3/index.d.ts +7 -0
- package/dist/adapters/s3/index.d.ts.map +1 -0
- package/dist/adapters/s3/index.js +11 -0
- package/dist/adapters/s3/index.js.map +1 -0
- package/dist/adapters/s3-compatible-adapter.d.ts +50 -0
- package/dist/adapters/s3-compatible-adapter.d.ts.map +1 -0
- package/dist/adapters/supabase/index.d.ts +7 -0
- package/dist/adapters/supabase/index.d.ts.map +1 -0
- package/dist/adapters/supabase/index.js +216 -0
- package/dist/adapters/supabase/index.js.map +1 -0
- package/dist/adapters/supabase/supabase-file-browser-adapter.d.ts +88 -0
- package/dist/adapters/supabase/supabase-file-browser-adapter.d.ts.map +1 -0
- package/dist/chunks/browser-4lIkMGbo.js +436 -0
- package/dist/chunks/browser-4lIkMGbo.js.map +1 -0
- package/dist/chunks/cloud-adapter-base-CBUoZYLS.js +39 -0
- package/dist/chunks/cloud-adapter-base-CBUoZYLS.js.map +1 -0
- package/dist/chunks/path-CPm7s9NF.js +52 -0
- package/dist/chunks/path-CPm7s9NF.js.map +1 -0
- package/dist/chunks/s3-compatible-adapter-D9agsn9e.js +317 -0
- package/dist/chunks/s3-compatible-adapter-D9agsn9e.js.map +1 -0
- package/dist/components/file-browser.d.ts +28 -0
- package/dist/components/file-browser.d.ts.map +1 -0
- package/dist/core/path.d.ts +9 -0
- package/dist/core/path.d.ts.map +1 -0
- package/dist/core/types.d.ts +84 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/upload-drop.d.ts +7 -0
- package/dist/core/upload-drop.d.ts.map +1 -0
- package/dist/core/use-file-browser.d.ts +78 -0
- package/dist/core/use-file-browser.d.ts.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3212 -0
- package/dist/index.js.map +1 -0
- package/dist/theme/index.d.ts +25 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/index.js +28 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/transfers/file-browser-provider.d.ts +13 -0
- package/dist/transfers/file-browser-provider.d.ts.map +1 -0
- package/dist/transfers/transfer-manager.d.ts +118 -0
- package/dist/transfers/transfer-manager.d.ts.map +1 -0
- package/package.json +137 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hariom Sharma
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# react-file-browser
|
|
2
|
+
|
|
3
|
+
A standalone, storage-agnostic React file manager component. Ships UI and client logic only, with
|
|
4
|
+
zero server code. Storage access goes through a host-provided adapter, so the same component drives
|
|
5
|
+
S3, Cloudflare R2, Supabase Storage, an in-memory store, or any backend you implement.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/react-file-browser)
|
|
8
|
+
[](https://www.npmjs.com/package/react-file-browser)
|
|
9
|
+
[](https://github.com/harryy2510/react-file-browser/actions/workflows/ci.yml)
|
|
10
|
+
[](https://bundlephobia.com/package/react-file-browser)
|
|
11
|
+
[](https://www.npmjs.com/package/react-file-browser)
|
|
12
|
+
[](LICENSE)
|
|
13
|
+
|
|
14
|
+
**[Live demo →](https://harryy2510.github.io/react-file-browser/)**
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **Storage-agnostic** — one component, pluggable adapters. Ships adapters for S3, R2, Supabase, and
|
|
19
|
+
in-memory, or implement the `FileBrowserAdapter` interface for any backend.
|
|
20
|
+
- **Capability-gated UI** — controls appear only when the adapter supports them. Omit `rename` and the
|
|
21
|
+
rename affordance disappears; no config flags to keep in sync.
|
|
22
|
+
- **Resumable uploads** — session-scoped transfer manager with multipart support, a floating transfer
|
|
23
|
+
widget, progress, and a resume prompt after a hard refresh.
|
|
24
|
+
- **Grid and list views**, search, filter, sort, drag-and-drop move, cut/copy/paste, multi-select,
|
|
25
|
+
keyboard shortcuts, file preview, and a details panel.
|
|
26
|
+
- **Bulk download** — uses the adapter's `bulkDownloadUrl` when present, otherwise builds a client-side
|
|
27
|
+
zip from signed URLs.
|
|
28
|
+
- **Upload policies** — reject files by MIME type, size, remaining quota, or a custom validator before
|
|
29
|
+
they enqueue.
|
|
30
|
+
- **Themeable** with CSS variables that inherit your Tailwind v4 / design-system tokens. No global CSS
|
|
31
|
+
shipped.
|
|
32
|
+
- **Read-only mode**, comfortable/compact density, ESM-only, fully typed.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
bun add react-file-browser
|
|
38
|
+
# or: npm install react-file-browser
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`react` and `react-dom` (>=18 <20) are peer dependencies. The AWS SDK and Supabase client are
|
|
42
|
+
optional peers, needed only for their respective adapters.
|
|
43
|
+
|
|
44
|
+
## Quick start
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import { FileBrowser, FileBrowserProvider } from "react-file-browser";
|
|
48
|
+
import { InMemoryFileBrowserAdapter } from "react-file-browser/adapters/in-memory";
|
|
49
|
+
|
|
50
|
+
const adapter = new InMemoryFileBrowserAdapter();
|
|
51
|
+
|
|
52
|
+
export function App() {
|
|
53
|
+
return (
|
|
54
|
+
<FileBrowserProvider>
|
|
55
|
+
<FileBrowser adapter={adapter} />
|
|
56
|
+
</FileBrowserProvider>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`FileBrowserProvider` belongs above your router. It owns the session-scoped `TransferManager`, the
|
|
62
|
+
floating transfer widget, and the refresh guard. `FileBrowser` can mount on any page or modal and can
|
|
63
|
+
unmount while transfers keep running.
|
|
64
|
+
|
|
65
|
+
## Mount points
|
|
66
|
+
|
|
67
|
+
After a hard refresh, browsers cannot restore the original `File` object automatically. Use
|
|
68
|
+
`resolveRestoredUpload` to reattach the host adapter and a user-selected file when the resume prompt
|
|
69
|
+
is accepted:
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
<FileBrowserProvider
|
|
73
|
+
resolveRestoredUpload={async (upload) => {
|
|
74
|
+
const file = await askUserForFile(upload.name);
|
|
75
|
+
return file ? { adapter, file } : undefined;
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
<App />
|
|
79
|
+
</FileBrowserProvider>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Adapters
|
|
83
|
+
|
|
84
|
+
The core contract is the `FileBrowserAdapter` interface. Available built-in adapters:
|
|
85
|
+
|
|
86
|
+
- `react-file-browser/adapters/in-memory`
|
|
87
|
+
- `react-file-browser/adapters/s3`
|
|
88
|
+
- `react-file-browser/adapters/r2`
|
|
89
|
+
- `react-file-browser/adapters/supabase`
|
|
90
|
+
|
|
91
|
+
The in-memory adapter is usable for tests and demos. It can opt into multipart methods with
|
|
92
|
+
`capabilities: { multipart: true }` and `multipartPartSize` so resumable upload flows are testable
|
|
93
|
+
without cloud credentials. Cloud adapter subpaths fail closed when used without options, so importing
|
|
94
|
+
them is safe.
|
|
95
|
+
|
|
96
|
+
### S3 and R2
|
|
97
|
+
|
|
98
|
+
S3 and R2 use the S3-compatible SDK surface when you provide a client and bucket:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { S3Client } from "@aws-sdk/client-s3";
|
|
102
|
+
import { S3FileBrowserAdapter } from "react-file-browser/adapters/s3";
|
|
103
|
+
|
|
104
|
+
const adapter = new S3FileBrowserAdapter({
|
|
105
|
+
bucket: "assets",
|
|
106
|
+
client: new S3Client({ region: "us-east-1" }),
|
|
107
|
+
prefix: "users/123",
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`R2FileBrowserAdapter` accepts the same options. Both require `@aws-sdk/client-s3` and
|
|
112
|
+
`@aws-sdk/s3-request-presigner` to be installed.
|
|
113
|
+
|
|
114
|
+
### Supabase
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { createClient } from "@supabase/supabase-js";
|
|
118
|
+
import { SupabaseFileBrowserAdapter } from "react-file-browser/adapters/supabase";
|
|
119
|
+
|
|
120
|
+
const supabase = createClient(url, anonKey);
|
|
121
|
+
const adapter = new SupabaseFileBrowserAdapter({
|
|
122
|
+
bucket: "assets",
|
|
123
|
+
client: supabase,
|
|
124
|
+
prefix: "users/123",
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Custom backends
|
|
129
|
+
|
|
130
|
+
Implement the `FileBrowserAdapter` interface directly, or pass a custom `implementation` to any cloud
|
|
131
|
+
adapter to delegate required and optional methods:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { S3FileBrowserAdapter } from "react-file-browser/adapters/s3";
|
|
135
|
+
|
|
136
|
+
const adapter = new S3FileBrowserAdapter({
|
|
137
|
+
implementation: hostBackedAdapter,
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Only `list`, `delete`, `signedUrl`, and `upload` are required. Everything else is optional and toggles
|
|
142
|
+
UI capabilities by presence. If an adapter omits `createFolder`, `move`, `rename`, `copy`, multipart,
|
|
143
|
+
or `bulkDownloadUrl`, those controls are hidden. Recursive folder drops are rejected when
|
|
144
|
+
`createFolder` is absent rather than flattened.
|
|
145
|
+
|
|
146
|
+
## API reference
|
|
147
|
+
|
|
148
|
+
### `<FileBrowser>` props
|
|
149
|
+
|
|
150
|
+
| Prop | Type | Default | Description |
|
|
151
|
+
| --- | --- | --- | --- |
|
|
152
|
+
| `adapter` | `FileBrowserAdapter` | — (required) | Storage backend. |
|
|
153
|
+
| `initialPath` | `string` | `"/"` | Directory to open on mount. |
|
|
154
|
+
| `density` | `"comfortable" \| "compact"` | `"comfortable"` | Row/tile density. |
|
|
155
|
+
| `readOnly` | `boolean` | `false` | Hides all mutating affordances. |
|
|
156
|
+
| `showDetailsPanel` | `boolean` | `true` | Toggles the right-hand details panel. |
|
|
157
|
+
| `uploadPolicy` | `FileBrowserUploadPolicy` | — | Reject files before they enqueue. |
|
|
158
|
+
| `warnZipSizeBytes` | `number` | — | Warn before building a large client-side zip. |
|
|
159
|
+
|
|
160
|
+
### `<FileBrowserProvider>` props
|
|
161
|
+
|
|
162
|
+
| Prop | Type | Default | Description |
|
|
163
|
+
| --- | --- | --- | --- |
|
|
164
|
+
| `manager` | `TransferManager` | auto-created | Bring your own transfer manager. |
|
|
165
|
+
| `options` | `TransferManagerOptions` | — | Storage, storage key, id factory, clock. |
|
|
166
|
+
| `resolveRestoredUpload` | `(upload) => ... \| undefined` | — | Reattach a `File` when resuming after reload. |
|
|
167
|
+
| `showFloatingWidget` | `boolean` | `true` | Toggles the floating transfer widget. |
|
|
168
|
+
|
|
169
|
+
### `FileBrowserAdapter` interface
|
|
170
|
+
|
|
171
|
+
**Required:** `list`, `delete`, `signedUrl`, `upload`.
|
|
172
|
+
|
|
173
|
+
**Optional (each toggles a UI capability):** `createFolder`, `rename`, `move`, `copy`, `stat`,
|
|
174
|
+
`exists`, `createMultipartUpload`, `uploadPart`, `completeMultipartUpload`, `abortMultipartUpload`,
|
|
175
|
+
`bulkDownloadUrl`.
|
|
176
|
+
|
|
177
|
+
Throw `FileBrowserAdapterError` with a code (`access_denied`, `aborted`, `conflict`, `invalid_path`,
|
|
178
|
+
`not_found`, `not_supported`) for correct error mapping, and `FileBrowserBulkActionError` for partial
|
|
179
|
+
bulk failures so successful paths stay applied and failed paths roll back.
|
|
180
|
+
|
|
181
|
+
### Headless usage
|
|
182
|
+
|
|
183
|
+
`useFileBrowser({ adapter, initialPath })` exposes the full state and actions if you want to build your
|
|
184
|
+
own UI. The `FileBrowser` component is a consumer of this hook.
|
|
185
|
+
|
|
186
|
+
### Entry points
|
|
187
|
+
|
|
188
|
+
| Import | Contents |
|
|
189
|
+
| --- | --- |
|
|
190
|
+
| `react-file-browser` | `FileBrowser`, `FileBrowserProvider`, `useFileBrowser`, `TransferManager`, adapter types and errors, path utilities. |
|
|
191
|
+
| `react-file-browser/adapters/in-memory` | `InMemoryFileBrowserAdapter`, `createInMemoryFileBrowserAdapter`. |
|
|
192
|
+
| `react-file-browser/adapters/s3` | `S3FileBrowserAdapter`. |
|
|
193
|
+
| `react-file-browser/adapters/r2` | `R2FileBrowserAdapter`. |
|
|
194
|
+
| `react-file-browser/adapters/supabase` | `SupabaseFileBrowserAdapter`. |
|
|
195
|
+
| `react-file-browser/theme` | `FILE_BROWSER_THEME_CONTRACT`, `getFileBrowserDensityAttributes`. |
|
|
196
|
+
|
|
197
|
+
## Styling
|
|
198
|
+
|
|
199
|
+
The package does not ship global CSS. Components use utility classes and `--fb-*` CSS variables that
|
|
200
|
+
fall back to your Tailwind v4 / design-system tokens:
|
|
201
|
+
|
|
202
|
+
```css
|
|
203
|
+
--fb-accent: var(--color-primary-500, oklch(.54 .19 285));
|
|
204
|
+
--fb-surface: var(--color-white, #fff);
|
|
205
|
+
--fb-border: var(--color-gray-200, #e5e5ee);
|
|
206
|
+
--fb-radius: var(--radius-lg, 10px);
|
|
207
|
+
--fb-gap: var(--spacing, .25rem);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Add the library source to your Tailwind v4 content/source setup so host builds include the classes.
|
|
211
|
+
The demo app defines a full theme; the package itself does not. `FILE_BROWSER_THEME_CONTRACT`
|
|
212
|
+
documents every token and its default.
|
|
213
|
+
|
|
214
|
+
## UI behavior
|
|
215
|
+
|
|
216
|
+
- `readOnly` hides upload, create, rename, move, copy, and delete affordances while keeping browsing,
|
|
217
|
+
preview, details, and download available.
|
|
218
|
+
- `uploadPolicy` can reject files before enqueueing transfers by MIME type, extension, max file size,
|
|
219
|
+
remaining quota, or a custom validator. Rejections render as an alert and valid files in the same
|
|
220
|
+
batch still upload.
|
|
221
|
+
- Upload conflicts surface a replace / keep both / skip dialog with apply-to-all support.
|
|
222
|
+
- Partial bulk failures can be reported with `FileBrowserBulkActionError`; successful paths stay
|
|
223
|
+
applied and failed paths are surfaced in a dialog.
|
|
224
|
+
- Single-file download uses `signedUrl`. Folder and multi-select bulk download uses `bulkDownloadUrl`
|
|
225
|
+
when present, otherwise it builds a client zip from signed URLs after the configured size warning.
|
|
226
|
+
- Move-capable adapters enable the destination tree picker, Cut/Paste, and drag onto folders.
|
|
227
|
+
- Keyboard shortcuts include Enter preview/open, F2 rename, Delete confirm, Cmd/Ctrl+A selection, and
|
|
228
|
+
Cmd/Ctrl+C/X/V for adapter-gated copy, cut, and paste.
|
|
229
|
+
|
|
230
|
+
## Required backend lifecycle
|
|
231
|
+
|
|
232
|
+
The package never creates server routes or lifecycle rules. Hosts must configure:
|
|
233
|
+
|
|
234
|
+
- Abort incomplete multipart uploads after N days.
|
|
235
|
+
- Expire temporary bulk-download zip objects after 4 to 8 hours.
|
|
236
|
+
- Keep zip output outside browsed/listed user prefixes.
|
|
237
|
+
|
|
238
|
+
Skipping these leaks storage cost.
|
|
239
|
+
|
|
240
|
+
## Contributing
|
|
241
|
+
|
|
242
|
+
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup, conventions, and
|
|
243
|
+
the release process. Please follow the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
[MIT](LICENSE) © Hariom Sharma
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { FileBrowserAdapter, FileBrowserListOptions, FileBrowserListResult, FileBrowserUploadOptions, FileNode } from '../core/types';
|
|
2
|
+
export type CloudAdapterName = 'S3' | 'R2' | 'Supabase';
|
|
3
|
+
export type CloudFileBrowserAdapterOptions = {
|
|
4
|
+
implementation?: FileBrowserAdapter;
|
|
5
|
+
};
|
|
6
|
+
export declare class UnsupportedCloudFileBrowserAdapter implements FileBrowserAdapter {
|
|
7
|
+
private readonly adapterName;
|
|
8
|
+
readonly rename?: NonNullable<FileBrowserAdapter['rename']>;
|
|
9
|
+
readonly createFolder?: NonNullable<FileBrowserAdapter['createFolder']>;
|
|
10
|
+
readonly move?: NonNullable<FileBrowserAdapter['move']>;
|
|
11
|
+
readonly copy?: NonNullable<FileBrowserAdapter['copy']>;
|
|
12
|
+
readonly stat?: NonNullable<FileBrowserAdapter['stat']>;
|
|
13
|
+
readonly exists?: NonNullable<FileBrowserAdapter['exists']>;
|
|
14
|
+
readonly createMultipartUpload?: NonNullable<FileBrowserAdapter['createMultipartUpload']>;
|
|
15
|
+
readonly uploadPart?: NonNullable<FileBrowserAdapter['uploadPart']>;
|
|
16
|
+
readonly completeMultipartUpload?: NonNullable<FileBrowserAdapter['completeMultipartUpload']>;
|
|
17
|
+
readonly abortMultipartUpload?: NonNullable<FileBrowserAdapter['abortMultipartUpload']>;
|
|
18
|
+
readonly bulkDownloadUrl?: NonNullable<FileBrowserAdapter['bulkDownloadUrl']>;
|
|
19
|
+
private readonly implementation?;
|
|
20
|
+
constructor(adapterName: CloudAdapterName, options?: CloudFileBrowserAdapterOptions);
|
|
21
|
+
list(path: string, opts?: FileBrowserListOptions): Promise<FileBrowserListResult>;
|
|
22
|
+
delete(paths: string[]): Promise<void>;
|
|
23
|
+
signedUrl(path: string): Promise<string>;
|
|
24
|
+
upload(path: string, file: File, opts?: FileBrowserUploadOptions): Promise<FileNode>;
|
|
25
|
+
private error;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=cloud-adapter-base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-adapter-base.d.ts","sourceRoot":"","sources":["../../src/adapters/cloud-adapter-base.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,QAAQ,EACR,MAAM,eAAe,CAAA;AAEtB,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,IAAI,GAAG,UAAU,CAAA;AAEvD,MAAM,MAAM,8BAA8B,GAAG;IAC5C,cAAc,CAAC,EAAE,kBAAkB,CAAA;CACnC,CAAA;AAED,qBAAa,kCAAmC,YAAW,kBAAkB;IAgB3E,OAAO,CAAC,QAAQ,CAAC,WAAW;IAf7B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC3D,QAAQ,CAAC,YAAY,CAAC,EAAE,WAAW,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAC,CAAA;IACvE,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAA;IACvD,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAA;IACvD,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAA;IACvD,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC3D,QAAQ,CAAC,qBAAqB,CAAC,EAAE,WAAW,CAAC,kBAAkB,CAAC,uBAAuB,CAAC,CAAC,CAAA;IACzF,QAAQ,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC,CAAA;IACnE,QAAQ,CAAC,uBAAuB,CAAC,EAAE,WAAW,CAAC,kBAAkB,CAAC,yBAAyB,CAAC,CAAC,CAAA;IAC7F,QAAQ,CAAC,oBAAoB,CAAC,EAAE,WAAW,CAAC,kBAAkB,CAAC,sBAAsB,CAAC,CAAC,CAAA;IACvF,QAAQ,CAAC,eAAe,CAAC,EAAE,WAAW,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,CAAC,CAAA;IAE7E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAoB;gBAGlC,WAAW,EAAE,gBAAgB,EAC9C,OAAO,GAAE,8BAAmC;IAuC7C,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IASjF,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQtC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQxC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAUpF,OAAO,CAAC,KAAK;CAMb"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { FileBrowserAdapter, FileBrowserListOptions, FileBrowserListResult, FileBrowserUploadOptions, FileNode } from '../../core/types';
|
|
2
|
+
export type InMemoryFileBrowserCapability = 'createFolder' | 'rename' | 'move' | 'copy' | 'stat' | 'exists' | 'bulkDownloadUrl' | 'multipart';
|
|
3
|
+
export type InMemoryFileBrowserAdapterOptions = {
|
|
4
|
+
capabilities?: Partial<Record<InMemoryFileBrowserCapability, boolean>>;
|
|
5
|
+
multipartPartSize?: number;
|
|
6
|
+
pageSize?: number;
|
|
7
|
+
now?: () => Date;
|
|
8
|
+
};
|
|
9
|
+
export declare class InMemoryFileBrowserAdapter implements FileBrowserAdapter {
|
|
10
|
+
readonly createFolder?: NonNullable<FileBrowserAdapter['createFolder']>;
|
|
11
|
+
readonly rename?: NonNullable<FileBrowserAdapter['rename']>;
|
|
12
|
+
readonly move?: NonNullable<FileBrowserAdapter['move']>;
|
|
13
|
+
readonly copy?: NonNullable<FileBrowserAdapter['copy']>;
|
|
14
|
+
readonly stat?: NonNullable<FileBrowserAdapter['stat']>;
|
|
15
|
+
readonly exists?: NonNullable<FileBrowserAdapter['exists']>;
|
|
16
|
+
readonly bulkDownloadUrl?: NonNullable<FileBrowserAdapter['bulkDownloadUrl']>;
|
|
17
|
+
readonly createMultipartUpload?: NonNullable<FileBrowserAdapter['createMultipartUpload']>;
|
|
18
|
+
readonly uploadPart?: NonNullable<FileBrowserAdapter['uploadPart']>;
|
|
19
|
+
readonly completeMultipartUpload?: NonNullable<FileBrowserAdapter['completeMultipartUpload']>;
|
|
20
|
+
readonly abortMultipartUpload?: NonNullable<FileBrowserAdapter['abortMultipartUpload']>;
|
|
21
|
+
private readonly entries;
|
|
22
|
+
private readonly multipartUploads;
|
|
23
|
+
private readonly multipartPartSize;
|
|
24
|
+
private readonly pageSize?;
|
|
25
|
+
private readonly now;
|
|
26
|
+
private etagSequence;
|
|
27
|
+
private multipartSequence;
|
|
28
|
+
constructor(options?: InMemoryFileBrowserAdapterOptions);
|
|
29
|
+
list(path: string, opts?: FileBrowserListOptions): Promise<FileBrowserListResult>;
|
|
30
|
+
private createFolderEntry;
|
|
31
|
+
delete(paths: string[]): Promise<void>;
|
|
32
|
+
signedUrl(path: string): Promise<string>;
|
|
33
|
+
upload(path: string, file: File, opts?: FileBrowserUploadOptions): Promise<FileNode>;
|
|
34
|
+
private nextAvailablePath;
|
|
35
|
+
private renameEntry;
|
|
36
|
+
private moveEntries;
|
|
37
|
+
private copyEntries;
|
|
38
|
+
private statEntry;
|
|
39
|
+
private existsEntries;
|
|
40
|
+
private createBulkDownloadUrl;
|
|
41
|
+
private createMultipartUploadEntry;
|
|
42
|
+
private uploadMultipartPart;
|
|
43
|
+
private completeMultipartUploadEntry;
|
|
44
|
+
private abortMultipartUploadEntry;
|
|
45
|
+
private getMultipartUpload;
|
|
46
|
+
private collectTree;
|
|
47
|
+
private assertFolderExists;
|
|
48
|
+
private getEntry;
|
|
49
|
+
private timestamp;
|
|
50
|
+
private nextEtag;
|
|
51
|
+
}
|
|
52
|
+
export declare function createInMemoryFileBrowserAdapter(options?: InMemoryFileBrowserAdapterOptions): InMemoryFileBrowserAdapter;
|
|
53
|
+
//# sourceMappingURL=in-memory-file-browser-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-memory-file-browser-adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/in-memory/in-memory-file-browser-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/in-memory/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,0BAA0B,EAC1B,gCAAgC,EAChC,KAAK,iCAAiC,EACtC,KAAK,6BAA6B,EAClC,MAAM,kCAAkC,CAAA"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { a as e, c as t, i as n, n as r, o as i, r as a, s as o } from "../../chunks/path-CPm7s9NF.js";
|
|
2
|
+
import { t as s } from "../../chunks/browser-4lIkMGbo.js";
|
|
3
|
+
//#region src/adapters/in-memory/in-memory-file-browser-adapter.ts
|
|
4
|
+
var c = {
|
|
5
|
+
createFolder: !0,
|
|
6
|
+
rename: !0,
|
|
7
|
+
move: !0,
|
|
8
|
+
copy: !0,
|
|
9
|
+
stat: !0,
|
|
10
|
+
exists: !0,
|
|
11
|
+
bulkDownloadUrl: !0,
|
|
12
|
+
multipart: !1
|
|
13
|
+
}, l = class {
|
|
14
|
+
createFolder;
|
|
15
|
+
rename;
|
|
16
|
+
move;
|
|
17
|
+
copy;
|
|
18
|
+
stat;
|
|
19
|
+
exists;
|
|
20
|
+
bulkDownloadUrl;
|
|
21
|
+
createMultipartUpload;
|
|
22
|
+
uploadPart;
|
|
23
|
+
completeMultipartUpload;
|
|
24
|
+
abortMultipartUpload;
|
|
25
|
+
entries = /* @__PURE__ */ new Map();
|
|
26
|
+
multipartUploads = /* @__PURE__ */ new Map();
|
|
27
|
+
multipartPartSize;
|
|
28
|
+
pageSize;
|
|
29
|
+
now;
|
|
30
|
+
etagSequence = 0;
|
|
31
|
+
multipartSequence = 0;
|
|
32
|
+
constructor(e = {}) {
|
|
33
|
+
let t = {
|
|
34
|
+
...c,
|
|
35
|
+
...e.capabilities
|
|
36
|
+
};
|
|
37
|
+
this.pageSize = e.pageSize, this.multipartPartSize = Math.max(1, e.multipartPartSize ?? 5 * 1024 * 1024), this.now = e.now ?? (() => /* @__PURE__ */ new Date()), this.entries.set("/", {
|
|
38
|
+
path: "/",
|
|
39
|
+
name: "",
|
|
40
|
+
kind: "folder",
|
|
41
|
+
modifiedAt: this.timestamp()
|
|
42
|
+
}), t.createFolder && (this.createFolder = this.createFolderEntry.bind(this)), t.rename && (this.rename = this.renameEntry.bind(this)), t.move && (this.move = this.moveEntries.bind(this)), t.copy && (this.copy = this.copyEntries.bind(this)), t.stat && (this.stat = this.statEntry.bind(this)), t.exists && (this.exists = this.existsEntries.bind(this)), t.bulkDownloadUrl && (this.bulkDownloadUrl = this.createBulkDownloadUrl.bind(this)), t.multipart && (this.createMultipartUpload = this.createMultipartUploadEntry.bind(this), this.uploadPart = this.uploadMultipartPart.bind(this), this.completeMultipartUpload = this.completeMultipartUploadEntry.bind(this), this.abortMultipartUpload = this.abortMultipartUploadEntry.bind(this));
|
|
43
|
+
}
|
|
44
|
+
list(e, n = {}) {
|
|
45
|
+
p(n.signal);
|
|
46
|
+
let r = i(e);
|
|
47
|
+
if (this.getEntry(r).kind !== "folder") throw new t("invalid_path", `Cannot list a file path: ${r}`);
|
|
48
|
+
let o = Array.from(this.entries.values()).filter((e) => e.path !== "/").filter((e) => a(e.path) === r).sort(f).map(d);
|
|
49
|
+
if (!this.pageSize) return Promise.resolve({ items: o });
|
|
50
|
+
let s = n.cursor ? Number.parseInt(n.cursor, 10) : 0, c = Number.isFinite(s) && s > 0 ? s : 0, l = o.slice(c, c + this.pageSize), u = c + l.length;
|
|
51
|
+
return Promise.resolve({
|
|
52
|
+
items: l,
|
|
53
|
+
cursor: u < o.length ? String(u) : void 0
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
createFolderEntry(e) {
|
|
57
|
+
let n = i(e);
|
|
58
|
+
if (n === "/") return Promise.resolve(d(this.getEntry("/")));
|
|
59
|
+
if (this.entries.has(n)) throw new t("conflict", `A file browser entry already exists at ${n}`);
|
|
60
|
+
this.assertFolderExists(a(n));
|
|
61
|
+
let o = {
|
|
62
|
+
path: n,
|
|
63
|
+
name: r(n),
|
|
64
|
+
kind: "folder",
|
|
65
|
+
modifiedAt: this.timestamp(),
|
|
66
|
+
etag: this.nextEtag()
|
|
67
|
+
};
|
|
68
|
+
return this.entries.set(n, o), Promise.resolve(d(o));
|
|
69
|
+
}
|
|
70
|
+
delete(e) {
|
|
71
|
+
for (let r of e.map(i)) {
|
|
72
|
+
if (r === "/") throw new t("invalid_path", "Cannot delete the root folder");
|
|
73
|
+
this.getEntry(r);
|
|
74
|
+
for (let e of Array.from(this.entries.keys())) n(r, e) && this.entries.delete(e);
|
|
75
|
+
}
|
|
76
|
+
return Promise.resolve();
|
|
77
|
+
}
|
|
78
|
+
signedUrl(e) {
|
|
79
|
+
let n = this.getEntry(e);
|
|
80
|
+
if (n.kind !== "file" || !n.blob) throw new t("invalid_path", `Cannot create a signed URL for a folder: ${n.path}`);
|
|
81
|
+
return Promise.resolve(URL.createObjectURL(n.blob));
|
|
82
|
+
}
|
|
83
|
+
upload(e, n, o = {}) {
|
|
84
|
+
p(o.signal);
|
|
85
|
+
let s = i(e);
|
|
86
|
+
if (this.assertFolderExists(a(s)), this.entries.has(s) && o.onConflict !== "replace") if (o.onConflict === "keep-both") s = this.nextAvailablePath(s);
|
|
87
|
+
else throw new t("conflict", `A file browser entry already exists at ${s}`);
|
|
88
|
+
o.onProgress?.(0, n.size), p(o.signal);
|
|
89
|
+
let c = {
|
|
90
|
+
path: s,
|
|
91
|
+
name: r(s) || n.name,
|
|
92
|
+
kind: "file",
|
|
93
|
+
size: n.size,
|
|
94
|
+
mimeType: n.type || void 0,
|
|
95
|
+
modifiedAt: this.timestamp(),
|
|
96
|
+
etag: this.nextEtag(),
|
|
97
|
+
blob: n
|
|
98
|
+
};
|
|
99
|
+
return this.entries.set(s, c), o.onProgress?.(n.size, n.size), Promise.resolve(d(c));
|
|
100
|
+
}
|
|
101
|
+
nextAvailablePath(n) {
|
|
102
|
+
let i = a(n), o = r(n), s = o.lastIndexOf("."), c = s > 0, l = c ? o.slice(0, s) : o, u = c ? o.slice(s) : "";
|
|
103
|
+
for (let t = 1; t < 2 ** 53 - 1; t += 1) {
|
|
104
|
+
let n = e(i, `${l} (${t})${u}`);
|
|
105
|
+
if (!this.entries.has(n)) return n;
|
|
106
|
+
}
|
|
107
|
+
throw new t("conflict", `Could not allocate a unique file browser path for ${n}`);
|
|
108
|
+
}
|
|
109
|
+
renameEntry(e, n) {
|
|
110
|
+
let r = i(e), a = this.getEntry(r), o = n.trim();
|
|
111
|
+
if (!o || o.includes("/") || o.includes("\\")) throw new t("invalid_path", `Invalid file browser name: ${n}`);
|
|
112
|
+
let s = {
|
|
113
|
+
...a,
|
|
114
|
+
name: o,
|
|
115
|
+
modifiedAt: this.timestamp(),
|
|
116
|
+
etag: this.nextEtag()
|
|
117
|
+
};
|
|
118
|
+
return this.entries.set(r, s), Promise.resolve(d(s));
|
|
119
|
+
}
|
|
120
|
+
async moveEntries(e, t) {
|
|
121
|
+
await this.copyEntries(e, t);
|
|
122
|
+
for (let t of e) await this.delete([t]);
|
|
123
|
+
}
|
|
124
|
+
copyEntries(n, a) {
|
|
125
|
+
let s = i(a);
|
|
126
|
+
this.assertFolderExists(s);
|
|
127
|
+
let c = /* @__PURE__ */ new Map();
|
|
128
|
+
for (let a of n.map(i)) {
|
|
129
|
+
let n = this.getEntry(a), i = e(s, r(n.path));
|
|
130
|
+
for (let e of this.collectTree(n.path)) {
|
|
131
|
+
let a = o(e.path, n.path, i);
|
|
132
|
+
if (this.entries.has(a) || c.has(a)) throw new t("conflict", `A file browser entry already exists at ${a}`);
|
|
133
|
+
c.set(a, {
|
|
134
|
+
...e,
|
|
135
|
+
path: a,
|
|
136
|
+
name: e.path === n.path ? r(a) : e.name,
|
|
137
|
+
modifiedAt: this.timestamp(),
|
|
138
|
+
etag: this.nextEtag()
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
for (let [e, t] of c) this.entries.set(e, t);
|
|
143
|
+
return Promise.resolve();
|
|
144
|
+
}
|
|
145
|
+
statEntry(e) {
|
|
146
|
+
return Promise.resolve(d(this.getEntry(e)));
|
|
147
|
+
}
|
|
148
|
+
existsEntries(e) {
|
|
149
|
+
return Promise.resolve(Object.fromEntries(e.map((e) => {
|
|
150
|
+
let t = i(e);
|
|
151
|
+
return [t, this.entries.has(t)];
|
|
152
|
+
})));
|
|
153
|
+
}
|
|
154
|
+
async createBulkDownloadUrl(e) {
|
|
155
|
+
let t = {};
|
|
156
|
+
for (let n of e) {
|
|
157
|
+
this.getEntry(n);
|
|
158
|
+
let e = this.collectTree(n);
|
|
159
|
+
for (let n of e) n.kind === "folder" || !n.blob || (t[m(n.path)] = new Uint8Array(await n.blob.arrayBuffer()));
|
|
160
|
+
}
|
|
161
|
+
let n = new Blob([s(t)], { type: "application/zip" });
|
|
162
|
+
return {
|
|
163
|
+
url: URL.createObjectURL(n),
|
|
164
|
+
expiresAt: new Date(this.now().getTime() + 14400 * 1e3).toISOString()
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
createMultipartUploadEntry(e, n) {
|
|
168
|
+
let r = i(e);
|
|
169
|
+
if (this.assertFolderExists(a(r)), this.entries.get(r)?.kind === "folder") throw new t("conflict", `A file browser entry already exists at ${r}`);
|
|
170
|
+
this.multipartSequence += 1;
|
|
171
|
+
let o = `memory-multipart-${this.multipartSequence}`;
|
|
172
|
+
return this.multipartUploads.set(o, {
|
|
173
|
+
path: r,
|
|
174
|
+
size: n,
|
|
175
|
+
partSize: this.multipartPartSize,
|
|
176
|
+
parts: /* @__PURE__ */ new Map()
|
|
177
|
+
}), Promise.resolve({
|
|
178
|
+
uploadId: o,
|
|
179
|
+
partSize: this.multipartPartSize
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
uploadMultipartPart({ chunk: e, onProgress: t, partNumber: n, signal: r, uploadId: i }) {
|
|
183
|
+
return Promise.resolve().then(() => {
|
|
184
|
+
p(r);
|
|
185
|
+
let a = this.getMultipartUpload(i), o = `memory-part-${n}`;
|
|
186
|
+
return a.parts.set(n, {
|
|
187
|
+
blob: e,
|
|
188
|
+
etag: o
|
|
189
|
+
}), t?.(e.size), p(r), { etag: o };
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
completeMultipartUploadEntry({ parts: e, uploadId: n }) {
|
|
193
|
+
return Promise.resolve().then(() => {
|
|
194
|
+
let i = this.getMultipartUpload(n), a = [];
|
|
195
|
+
for (let r of e.sort((e, t) => e.partNumber - t.partNumber)) {
|
|
196
|
+
let e = i.parts.get(r.partNumber);
|
|
197
|
+
if (!e || e.etag !== r.etag) throw new t("not_found", `No uploaded part ${r.partNumber} exists for ${n}`);
|
|
198
|
+
a.push(e.blob);
|
|
199
|
+
}
|
|
200
|
+
let o = new Blob(a);
|
|
201
|
+
if (o.size !== i.size) throw new t("invalid_path", `Multipart upload ${n} size mismatch`);
|
|
202
|
+
let s = {
|
|
203
|
+
path: i.path,
|
|
204
|
+
name: r(i.path),
|
|
205
|
+
kind: "file",
|
|
206
|
+
size: o.size,
|
|
207
|
+
modifiedAt: this.timestamp(),
|
|
208
|
+
etag: this.nextEtag(),
|
|
209
|
+
blob: o
|
|
210
|
+
};
|
|
211
|
+
return this.entries.set(i.path, s), this.multipartUploads.delete(n), d(s);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
abortMultipartUploadEntry(e) {
|
|
215
|
+
return Promise.resolve().then(() => {
|
|
216
|
+
this.getMultipartUpload(e), this.multipartUploads.delete(e);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
getMultipartUpload(e) {
|
|
220
|
+
let n = this.multipartUploads.get(e);
|
|
221
|
+
if (!n) throw new t("not_found", `No multipart upload exists for ${e}`);
|
|
222
|
+
return n;
|
|
223
|
+
}
|
|
224
|
+
collectTree(e) {
|
|
225
|
+
let t = i(e);
|
|
226
|
+
return Array.from(this.entries.values()).filter((e) => n(t, e.path)).sort((e, t) => e.path.localeCompare(t.path));
|
|
227
|
+
}
|
|
228
|
+
assertFolderExists(e) {
|
|
229
|
+
let n = this.getEntry(e);
|
|
230
|
+
if (n.kind !== "folder") throw new t("invalid_path", `Expected a folder at ${n.path}`);
|
|
231
|
+
}
|
|
232
|
+
getEntry(e) {
|
|
233
|
+
let n = i(e), r = this.entries.get(n);
|
|
234
|
+
if (!r) throw new t("not_found", `No file browser entry exists at ${n}`);
|
|
235
|
+
return r;
|
|
236
|
+
}
|
|
237
|
+
timestamp() {
|
|
238
|
+
return this.now().toISOString();
|
|
239
|
+
}
|
|
240
|
+
nextEtag() {
|
|
241
|
+
return this.etagSequence += 1, `memory-${this.etagSequence}`;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
function u(e) {
|
|
245
|
+
return new l(e);
|
|
246
|
+
}
|
|
247
|
+
function d(e) {
|
|
248
|
+
let t = {
|
|
249
|
+
path: e.path,
|
|
250
|
+
name: e.name,
|
|
251
|
+
kind: e.kind
|
|
252
|
+
};
|
|
253
|
+
return e.size !== void 0 && (t.size = e.size), e.mimeType !== void 0 && (t.mimeType = e.mimeType), e.modifiedAt !== void 0 && (t.modifiedAt = e.modifiedAt), e.etag !== void 0 && (t.etag = e.etag), e.thumbnailUrl !== void 0 && (t.thumbnailUrl = e.thumbnailUrl), t;
|
|
254
|
+
}
|
|
255
|
+
function f(e, t) {
|
|
256
|
+
return e.kind === t.kind ? e.name.localeCompare(t.name, void 0, {
|
|
257
|
+
numeric: !0,
|
|
258
|
+
sensitivity: "base"
|
|
259
|
+
}) : e.kind === "folder" ? -1 : 1;
|
|
260
|
+
}
|
|
261
|
+
function p(e) {
|
|
262
|
+
if (e?.aborted) throw new t("aborted", "Operation was aborted");
|
|
263
|
+
}
|
|
264
|
+
function m(e) {
|
|
265
|
+
return e.replace(/^\/+/, "") || "download";
|
|
266
|
+
}
|
|
267
|
+
//#endregion
|
|
268
|
+
export { l as InMemoryFileBrowserAdapter, u as createInMemoryFileBrowserAdapter };
|
|
269
|
+
|
|
270
|
+
//# sourceMappingURL=index.js.map
|