libfw-client 0.4.0 → 0.4.2
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/index.d.ts +40 -2
- package/index.js +163 -40
- package/package.json +1 -1
- package/pkg/libfw_client.d.ts +5 -10
- package/pkg/libfw_client.js +52 -36
- package/pkg/libfw_client_bg.wasm +0 -0
- package/pkg/libfw_client_bg.wasm.d.ts +5 -4
- package/pkg/package.json +1 -1
- package/zip.js +19 -0
package/index.d.ts
CHANGED
|
@@ -36,6 +36,17 @@ export interface UploadEntry {
|
|
|
36
36
|
mtime: number;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* A `File` together with a caller-provided virtual path override. Passed
|
|
41
|
+
* verbatim to `options.resolveUploadPath`; when no resolver is configured,
|
|
42
|
+
* `relPath` itself is used as the upload path.
|
|
43
|
+
*/
|
|
44
|
+
export interface UploadFileEntry {
|
|
45
|
+
file: File;
|
|
46
|
+
/** Virtual path (POSIX separators) to upload the file as. */
|
|
47
|
+
relPath?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
39
50
|
/** Adaptive-tuning parameters the engine is currently tuned to. */
|
|
40
51
|
export interface TuningParams {
|
|
41
52
|
/** Cross-file transfer concurrency. */
|
|
@@ -171,6 +182,30 @@ export interface LibfwClientOptions {
|
|
|
171
182
|
tuneTtlMs?: number;
|
|
172
183
|
/** Optional progress/state listener. Tuning updates arrive as `{ type: 'tuning', phase, params, stats }`. */
|
|
173
184
|
onEvent?: (event: LibfwEvent | LibfwTuningEvent) => void;
|
|
185
|
+
/**
|
|
186
|
+
* Pre-selected directory handle (or a resolver returning one) used instead
|
|
187
|
+
* of `showDirectoryPicker()` for every fs-mode download/upload.
|
|
188
|
+
*/
|
|
189
|
+
directoryHandle?:
|
|
190
|
+
| FileSystemDirectoryHandle
|
|
191
|
+
| (() => FileSystemDirectoryHandle | Promise<FileSystemDirectoryHandle>);
|
|
192
|
+
/**
|
|
193
|
+
* Map a virtual server path to the display name used for fs-mode on-disk
|
|
194
|
+
* paths (directories/files created in the picked download folder),
|
|
195
|
+
* browser downloads, `.zip` entry names and the `.zip` archive name. May
|
|
196
|
+
* be async; returned values are still validated against zip-slip.
|
|
197
|
+
*/
|
|
198
|
+
resolveDisplayName?: (path: string) => string | Promise<string>;
|
|
199
|
+
/**
|
|
200
|
+
* Map an upload entry to its virtual path on the server. `entry` is the
|
|
201
|
+
* original item passed to `upload()` (a bare `File` or an
|
|
202
|
+
* `UploadFileEntry` wrapper). `defaultPath` is
|
|
203
|
+
* `entry.relPath || file.webkitRelativePath || file.name`. May be async.
|
|
204
|
+
*/
|
|
205
|
+
resolveUploadPath?: (
|
|
206
|
+
entry: File | UploadFileEntry,
|
|
207
|
+
defaultPath: string
|
|
208
|
+
) => string | Promise<string>;
|
|
174
209
|
}
|
|
175
210
|
|
|
176
211
|
/**
|
|
@@ -204,9 +239,12 @@ export declare class LibfwClient {
|
|
|
204
239
|
*
|
|
205
240
|
* @param token bearer token
|
|
206
241
|
* @param filePath virtual server path of the file to download
|
|
242
|
+
* @param opts optional overrides; `fileName` is the local name the file is
|
|
243
|
+
* saved as (leaf name only — parent directories of `filePath` still
|
|
244
|
+
* apply in fs mode).
|
|
207
245
|
* @returns total bytes written
|
|
208
246
|
*/
|
|
209
|
-
downloadFile(token: string, filePath: string): Promise<number>;
|
|
247
|
+
downloadFile(token: string, filePath: string, opts?: { fileName?: string }): Promise<number>;
|
|
210
248
|
|
|
211
249
|
/**
|
|
212
250
|
* Upload files to the server.
|
|
@@ -220,7 +258,7 @@ export declare class LibfwClient {
|
|
|
220
258
|
*/
|
|
221
259
|
upload(
|
|
222
260
|
token: string,
|
|
223
|
-
files?: FileList | File[] | UploadEntry[],
|
|
261
|
+
files?: FileList | File[] | UploadFileEntry[] | UploadEntry[],
|
|
224
262
|
): Promise<number>;
|
|
225
263
|
|
|
226
264
|
/** Pause the active transfer. */
|
package/index.js
CHANGED
|
@@ -245,6 +245,26 @@ export class LibfwClient {
|
|
|
245
245
|
* @param {(event: {type: string, done: number, total: number, path?: string, error?: string}) => void} [options.onEvent]
|
|
246
246
|
* optional progress/state listener. Tuning updates arrive as
|
|
247
247
|
* `{ type: 'tuning', phase, params, stats }` events.
|
|
248
|
+
* @param {FileSystemDirectoryHandle|(() => FileSystemDirectoryHandle|Promise<FileSystemDirectoryHandle>)} [options.directoryHandle]
|
|
249
|
+
* pre-selected directory handle (or a resolver returning one) used
|
|
250
|
+
* instead of popping `showDirectoryPicker()` for every fs-mode
|
|
251
|
+
* download/upload. Callers that already hold a handle (e.g. from a
|
|
252
|
+
* picker of their own, or a persisted handle) can inject it here.
|
|
253
|
+
* @param {(path: string) => string|Promise<string>} [options.resolveDisplayName]
|
|
254
|
+
* optional mapping from a virtual server path to the display name
|
|
255
|
+
* used for fs-mode on-disk paths (`_ensureFileHandle` — the
|
|
256
|
+
* directories/files actually created in the picked folder), browser
|
|
257
|
+
* downloads (`_downloadName`), `.zip` entry names (fallback folder
|
|
258
|
+
* downloads) and the `.zip` archive name. The resolver may be sync
|
|
259
|
+
* or async (return a `Promise`); returned values are still validated
|
|
260
|
+
* against zip-slip/path traversal.
|
|
261
|
+
* @param {(entry: File|{file: File, relPath?: string}, defaultPath: string) => string|Promise<string>} [options.resolveUploadPath]
|
|
262
|
+
* optional mapping from an upload entry to its virtual path on the
|
|
263
|
+
* server. `entry` is the ORIGINAL item passed to `upload()` — either
|
|
264
|
+
* a bare `File` or a caller wrapper such as `{ file, relPath }` — so
|
|
265
|
+
* custom metadata survives into the resolver. `defaultPath` is
|
|
266
|
+
* `entry.relPath || file.webkitRelativePath || file.name`. The
|
|
267
|
+
* resolver may be sync or async (return a `Promise`).
|
|
248
268
|
*/
|
|
249
269
|
constructor(options = {}) {
|
|
250
270
|
this._options = {
|
|
@@ -265,6 +285,9 @@ export class LibfwClient {
|
|
|
265
285
|
autoTune: false,
|
|
266
286
|
tuneTtlMs: 3600000,
|
|
267
287
|
onEvent: null,
|
|
288
|
+
directoryHandle: null,
|
|
289
|
+
resolveDisplayName: null,
|
|
290
|
+
resolveUploadPath: null,
|
|
268
291
|
...options,
|
|
269
292
|
};
|
|
270
293
|
/** @type {WasmEngine|null} */
|
|
@@ -283,9 +306,11 @@ export class LibfwClient {
|
|
|
283
306
|
this._uploadPlan = [];
|
|
284
307
|
/**
|
|
285
308
|
* Active browser-download fallback state, or `null`.
|
|
286
|
-
* @type {{isFolder:boolean, buffers:Map<string,Uint8Array[]>, order:string[], sizes:Map<string,number
|
|
309
|
+
* @type {{isFolder:boolean, buffers:Map<string,Uint8Array[]>, order:string[], sizes:Map<string,number>, total:number, buffered:number}|null}
|
|
287
310
|
*/
|
|
288
311
|
this._fallback = null;
|
|
312
|
+
/** Leaf-name override for the current single-file download, or `null`. */
|
|
313
|
+
this._singleFileName = null;
|
|
289
314
|
}
|
|
290
315
|
|
|
291
316
|
// ------------------------------------------------------------------ setup
|
|
@@ -434,6 +459,22 @@ export class LibfwClient {
|
|
|
434
459
|
);
|
|
435
460
|
}
|
|
436
461
|
|
|
462
|
+
/**
|
|
463
|
+
* Obtain the destination directory handle for fs-mode transfers: the
|
|
464
|
+
* injected `directoryHandle` option (a handle or a resolver function)
|
|
465
|
+
* wins; otherwise `showDirectoryPicker()` is popped.
|
|
466
|
+
* @returns {Promise<FileSystemDirectoryHandle>}
|
|
467
|
+
* @private
|
|
468
|
+
*/
|
|
469
|
+
async _pickDirectory() {
|
|
470
|
+
const provided = this._options.directoryHandle;
|
|
471
|
+
if (provided) {
|
|
472
|
+
const handle = typeof provided === 'function' ? await provided() : provided;
|
|
473
|
+
if (handle) return handle;
|
|
474
|
+
}
|
|
475
|
+
return window.showDirectoryPicker();
|
|
476
|
+
}
|
|
477
|
+
|
|
437
478
|
/**
|
|
438
479
|
* Resolve the effective download mode from the `downloadMode` option:
|
|
439
480
|
* an explicit `'fs'`/`'browser'` wins; `'auto'` falls back to the browser
|
|
@@ -469,7 +510,7 @@ export class LibfwClient {
|
|
|
469
510
|
if (this._effectiveMode() === 'browser') {
|
|
470
511
|
return this._downloadViaBrowser(engine, token, dirPath, true);
|
|
471
512
|
}
|
|
472
|
-
this._dirHandle = await
|
|
513
|
+
this._dirHandle = await this._pickDirectory();
|
|
473
514
|
this._fileHandles.clear();
|
|
474
515
|
try {
|
|
475
516
|
return await engine.download_folder(this._options.baseUrl, token, dirPath);
|
|
@@ -491,22 +532,35 @@ export class LibfwClient {
|
|
|
491
532
|
*
|
|
492
533
|
* @param {string} token bearer token
|
|
493
534
|
* @param {string} filePath virtual server path of the file to download
|
|
535
|
+
* @param {{fileName?: string}} [opts] optional overrides; `fileName` is the
|
|
536
|
+
* local name the file is saved as (leaf name only — parent
|
|
537
|
+
* directories of `filePath` still apply in fs mode).
|
|
494
538
|
* @returns {Promise<number>} total bytes transferred
|
|
495
539
|
* @throws {LibfwError}
|
|
496
540
|
*/
|
|
497
|
-
async downloadFile(token, filePath) {
|
|
541
|
+
async downloadFile(token, filePath, opts = {}) {
|
|
498
542
|
const engine = await this._ready();
|
|
499
543
|
if (!filePath) throw new LibfwError('downloadFile requires a file path', 'path');
|
|
500
|
-
if (
|
|
501
|
-
|
|
544
|
+
if (opts.fileName !== undefined) {
|
|
545
|
+
const name = String(opts.fileName);
|
|
546
|
+
// Reject anything that could escape the destination directory (the
|
|
547
|
+
// FS Access API would throw a raw TypeError anyway; fail uniformly).
|
|
548
|
+
if (!name || name === '.' || name === '..' || /[/\\]/.test(name)) {
|
|
549
|
+
throw new LibfwError(`invalid fileName: ${name}`, 'path');
|
|
550
|
+
}
|
|
502
551
|
}
|
|
503
|
-
this.
|
|
504
|
-
this._fileHandles.clear();
|
|
552
|
+
this._singleFileName = opts.fileName ? String(opts.fileName) : null;
|
|
505
553
|
try {
|
|
554
|
+
if (this._effectiveMode() === 'browser') {
|
|
555
|
+
return await this._downloadViaBrowser(engine, token, filePath, false);
|
|
556
|
+
}
|
|
557
|
+
this._dirHandle = await this._pickDirectory();
|
|
558
|
+
this._fileHandles.clear();
|
|
506
559
|
return await engine.download_file(this._options.baseUrl, token, filePath);
|
|
507
560
|
} catch (err) {
|
|
508
561
|
throw toLibfwError(err);
|
|
509
562
|
} finally {
|
|
563
|
+
this._singleFileName = null;
|
|
510
564
|
await this._flushWritables();
|
|
511
565
|
await this._syncResumeOffsets();
|
|
512
566
|
}
|
|
@@ -531,7 +585,7 @@ export class LibfwClient {
|
|
|
531
585
|
* @private
|
|
532
586
|
*/
|
|
533
587
|
async _downloadViaBrowser(engine, token, path, isFolder) {
|
|
534
|
-
this._fallback = { isFolder, buffers: new Map(), order: [], sizes: new Map(), total: 0 };
|
|
588
|
+
this._fallback = { isFolder, buffers: new Map(), order: [], sizes: new Map(), total: 0, buffered: 0 };
|
|
535
589
|
try {
|
|
536
590
|
const total = isFolder
|
|
537
591
|
? await engine.download_folder(this._options.baseUrl, token, path)
|
|
@@ -539,22 +593,36 @@ export class LibfwClient {
|
|
|
539
593
|
const { buffers, order, sizes } = this._fallback;
|
|
540
594
|
if (isFolder) {
|
|
541
595
|
const entries = [];
|
|
596
|
+
// Distinct virtual paths may map to the same display name (a custom
|
|
597
|
+
// `resolveDisplayName` is not required to be injective); duplicate
|
|
598
|
+
// names would silently shadow each other on extraction.
|
|
599
|
+
const names = new Set();
|
|
600
|
+
const pushEntry = (name, data) => {
|
|
601
|
+
if (names.has(name)) {
|
|
602
|
+
throw new LibfwError(`duplicate download entry name: ${name}`, 'path');
|
|
603
|
+
}
|
|
604
|
+
names.add(name);
|
|
605
|
+
entries.push({ name, data });
|
|
606
|
+
};
|
|
542
607
|
for (const p of order) {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
608
|
+
pushEntry(
|
|
609
|
+
await this._safeEntryName(p),
|
|
610
|
+
this._concatBuffers(buffers.get(p)?.chunks || [])
|
|
611
|
+
);
|
|
547
612
|
}
|
|
548
613
|
// Include files that were announced but produced no bytes (empty).
|
|
549
614
|
for (const p of sizes.keys()) {
|
|
550
615
|
if (!buffers.has(p)) {
|
|
551
|
-
|
|
616
|
+
pushEntry(await this._safeEntryName(p), new Uint8Array(0));
|
|
552
617
|
}
|
|
553
618
|
}
|
|
554
|
-
this._triggerBrowserDownload(createZip(entries), this._archiveName(path));
|
|
619
|
+
this._triggerBrowserDownload(createZip(entries), await this._archiveName(path));
|
|
555
620
|
} else {
|
|
556
621
|
const data = this._concatBuffers(buffers.get(path)?.chunks || []);
|
|
557
|
-
this._triggerBrowserDownload(
|
|
622
|
+
this._triggerBrowserDownload(
|
|
623
|
+
new Blob([data], { type: 'application/octet-stream' }),
|
|
624
|
+
this._singleFileName || (await this._downloadName(path))
|
|
625
|
+
);
|
|
558
626
|
}
|
|
559
627
|
return total;
|
|
560
628
|
} catch (err) {
|
|
@@ -584,25 +652,36 @@ export class LibfwClient {
|
|
|
584
652
|
}
|
|
585
653
|
|
|
586
654
|
/**
|
|
587
|
-
* Strip a leading `/` so an entry path is archive/OS friendly
|
|
655
|
+
* Strip a leading `/` so an entry path is archive/OS/disk friendly,
|
|
656
|
+
* applying the `resolveDisplayName` option when configured. The resolver
|
|
657
|
+
* may be sync or async (return a `Promise`); both fs-mode on-disk paths
|
|
658
|
+
* (`_ensureFileHandle`) and browser-fallback archive/file names go
|
|
659
|
+
* through here, so a display-name mapping applies uniformly to every
|
|
660
|
+
* download destination.
|
|
588
661
|
* @param {string} path
|
|
589
|
-
* @returns {string}
|
|
662
|
+
* @returns {Promise<string>}
|
|
590
663
|
* @private
|
|
591
664
|
*/
|
|
592
|
-
_cleanPath(path) {
|
|
593
|
-
|
|
665
|
+
async _cleanPath(path) {
|
|
666
|
+
const resolve = this._options.resolveDisplayName;
|
|
667
|
+
let resolved = null;
|
|
668
|
+
if (typeof resolve === 'function') {
|
|
669
|
+
resolved = await resolve(String(path));
|
|
670
|
+
}
|
|
671
|
+
return String(resolved != null ? resolved : path).replace(/^\/+/, '');
|
|
594
672
|
}
|
|
595
673
|
|
|
596
674
|
/**
|
|
597
|
-
* Validate a virtual path for use as a ZIP entry name
|
|
598
|
-
* traversal (`..`), absolute/drive-letter
|
|
599
|
-
* separators that could escape the archive on
|
|
675
|
+
* Validate a virtual path for use as a ZIP entry name or an on-disk
|
|
676
|
+
* fs-mode path, rejecting any traversal (`..`), absolute/drive-letter
|
|
677
|
+
* prefixes or Windows-style separators that could escape the archive on
|
|
678
|
+
* extraction (zip-slip) or the picked download directory.
|
|
600
679
|
* @param {string} path
|
|
601
|
-
* @returns {string}
|
|
680
|
+
* @returns {Promise<string>}
|
|
602
681
|
* @private
|
|
603
682
|
*/
|
|
604
|
-
_safeEntryName(path) {
|
|
605
|
-
const cleaned = this._cleanPath(path);
|
|
683
|
+
async _safeEntryName(path) {
|
|
684
|
+
const cleaned = await this._cleanPath(path);
|
|
606
685
|
const segs = String(cleaned).split('/');
|
|
607
686
|
if (segs.some((seg) => seg === '..' || seg.includes('\\') || /^[a-zA-Z]:/.test(seg))) {
|
|
608
687
|
throw new LibfwError(`unsafe path in download: ${path}`, 'path');
|
|
@@ -623,22 +702,22 @@ export class LibfwClient {
|
|
|
623
702
|
/**
|
|
624
703
|
* Derive a safe file name from a virtual path.
|
|
625
704
|
* @param {string} path
|
|
626
|
-
* @returns {string}
|
|
705
|
+
* @returns {Promise<string>}
|
|
627
706
|
* @private
|
|
628
707
|
*/
|
|
629
|
-
_downloadName(path) {
|
|
630
|
-
const name = this._cleanPath(path).split('/').pop();
|
|
708
|
+
async _downloadName(path) {
|
|
709
|
+
const name = (await this._cleanPath(path)).split('/').pop();
|
|
631
710
|
return name || 'download';
|
|
632
711
|
}
|
|
633
712
|
|
|
634
713
|
/**
|
|
635
714
|
* Derive the `.zip` archive name for a folder download.
|
|
636
715
|
* @param {string} path
|
|
637
|
-
* @returns {string}
|
|
716
|
+
* @returns {Promise<string>}
|
|
638
717
|
* @private
|
|
639
718
|
*/
|
|
640
|
-
_archiveName(path) {
|
|
641
|
-
const base = this._cleanPath(path).split('/').pop() || 'download';
|
|
719
|
+
async _archiveName(path) {
|
|
720
|
+
const base = (await this._cleanPath(path)).split('/').pop() || 'download';
|
|
642
721
|
return `${base.replace(/[^\w.\- ]+/g, '_') || 'download'}.zip`;
|
|
643
722
|
}
|
|
644
723
|
|
|
@@ -703,6 +782,18 @@ export class LibfwClient {
|
|
|
703
782
|
}
|
|
704
783
|
buf.chunks.push(data);
|
|
705
784
|
buf.len += data.length;
|
|
785
|
+
// Runtime backstop for the in-memory cap: `onFileStart` pre-checks use
|
|
786
|
+
// the engine's declared size; an under-reported size (or a skipped
|
|
787
|
+
// fileStart on an unusual path) must still fail instead of growing the
|
|
788
|
+
// buffer unbounded.
|
|
789
|
+
this._fallback.buffered += data.length;
|
|
790
|
+
const max = this._maxFallbackBytes();
|
|
791
|
+
if (max > 0 && this._fallback.buffered > max) {
|
|
792
|
+
throw new LibfwError(
|
|
793
|
+
`browser download buffered more than the ${max}-byte in-memory limit`,
|
|
794
|
+
'too-large'
|
|
795
|
+
);
|
|
796
|
+
}
|
|
706
797
|
return;
|
|
707
798
|
}
|
|
708
799
|
let entry = this._writables.get(path);
|
|
@@ -797,7 +888,9 @@ export class LibfwClient {
|
|
|
797
888
|
* @private
|
|
798
889
|
*/
|
|
799
890
|
async _ensureFileHandle(path) {
|
|
800
|
-
|
|
891
|
+
// Map through the display-name resolver (sync or async) so fs-mode
|
|
892
|
+
// streaming lands under the same names the browser fallback would use.
|
|
893
|
+
const segments = splitPath(await this._safeEntryName(path));
|
|
801
894
|
if (segments.length === 0) {
|
|
802
895
|
throw new LibfwError(`invalid download path: ${path}`, 'path');
|
|
803
896
|
}
|
|
@@ -805,7 +898,7 @@ export class LibfwClient {
|
|
|
805
898
|
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
806
899
|
dir = await dir.getDirectoryHandle(segments[i], { create: true });
|
|
807
900
|
}
|
|
808
|
-
const name = segments[segments.length - 1];
|
|
901
|
+
const name = this._singleFileName || segments[segments.length - 1];
|
|
809
902
|
const handle = await dir.getFileHandle(name, { create: true });
|
|
810
903
|
return { dir, name, handle };
|
|
811
904
|
}
|
|
@@ -836,7 +929,9 @@ export class LibfwClient {
|
|
|
836
929
|
async _resolveFileHandle(path) {
|
|
837
930
|
if (!this._dirHandle) return null;
|
|
838
931
|
try {
|
|
839
|
-
|
|
932
|
+
// Same display-name mapping as `_ensureFileHandle` so a resume finds
|
|
933
|
+
// the file previously written under its resolved (display) name.
|
|
934
|
+
const segments = splitPath(await this._safeEntryName(path));
|
|
840
935
|
if (segments.length === 0) return null;
|
|
841
936
|
let dir = this._dirHandle;
|
|
842
937
|
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
@@ -945,12 +1040,14 @@ export class LibfwClient {
|
|
|
945
1040
|
*
|
|
946
1041
|
* If `files` is omitted, `showDirectoryPicker()` is used to select a
|
|
947
1042
|
* local folder whose structure is mirrored on the server. Otherwise
|
|
948
|
-
* `files` may be a `FileList`, an array of `File`s,
|
|
1043
|
+
* `files` may be a `FileList`, an array of `File`s, an array of
|
|
1044
|
+
* `{ file, relPath }` wrappers (custom per-file virtual paths, passed
|
|
1045
|
+
* verbatim to `resolveUploadPath`), or an array of
|
|
949
1046
|
* `{ path, size, mtime }` plan entries (when you want to drive reading
|
|
950
1047
|
* yourself).
|
|
951
1048
|
*
|
|
952
1049
|
* @param {string} token bearer token
|
|
953
|
-
* @param {FileList|File[]|Array<{path:string,size:number,mtime:number}>} [files]
|
|
1050
|
+
* @param {FileList|File[]|Array<{file:File,relPath?:string}>|Array<{path:string,size:number,mtime:number}>} [files]
|
|
954
1051
|
* @returns {Promise<number>} total bytes uploaded
|
|
955
1052
|
* @throws {LibfwError}
|
|
956
1053
|
*/
|
|
@@ -977,7 +1074,7 @@ export class LibfwClient {
|
|
|
977
1074
|
if (typeof window === 'undefined' || typeof window.showDirectoryPicker !== 'function') {
|
|
978
1075
|
throw new LibfwError('File System Access API is not available in this browser', 'unsupported');
|
|
979
1076
|
}
|
|
980
|
-
const dir = await
|
|
1077
|
+
const dir = await this._pickDirectory();
|
|
981
1078
|
this._dirHandle = dir;
|
|
982
1079
|
this._uploadPlan = await this._collectDirectoryFiles(dir, '');
|
|
983
1080
|
} else {
|
|
@@ -1031,9 +1128,35 @@ export class LibfwClient {
|
|
|
1031
1128
|
}
|
|
1032
1129
|
const list = Array.from(files || []);
|
|
1033
1130
|
const plan = [];
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1131
|
+
const resolve = this._options.resolveUploadPath;
|
|
1132
|
+
const seen = new Set();
|
|
1133
|
+
for (const entry of list) {
|
|
1134
|
+
// Accept bare `File`s as well as wrappers like `{ file, relPath }`;
|
|
1135
|
+
// the resolver receives the ORIGINAL entry so caller-side metadata
|
|
1136
|
+
// (e.g. a custom `relPath`) is visible without WeakMap workarounds.
|
|
1137
|
+
const file = entry instanceof File ? entry : entry && entry.file instanceof File ? entry.file : null;
|
|
1138
|
+
if (!file) continue;
|
|
1139
|
+
// A wrapper-provided `relPath` is the most specific default; fall
|
|
1140
|
+
// back to the browser-provided relative path, then the file name.
|
|
1141
|
+
const defaultPath =
|
|
1142
|
+
(entry && typeof entry === 'object' && typeof entry.relPath === 'string' && entry.relPath
|
|
1143
|
+
? entry.relPath
|
|
1144
|
+
: null) ||
|
|
1145
|
+
file.webkitRelativePath ||
|
|
1146
|
+
file.name;
|
|
1147
|
+
// The resolver may be async; a `null`/`undefined` mapping falls back
|
|
1148
|
+
// to the default path (same leniency as `resolveDisplayName`).
|
|
1149
|
+
let path = defaultPath;
|
|
1150
|
+
if (typeof resolve === 'function') {
|
|
1151
|
+
const mapped = await resolve(entry, defaultPath);
|
|
1152
|
+
if (mapped != null) path = String(mapped);
|
|
1153
|
+
}
|
|
1154
|
+
// A mapping that collides would silently shadow one File with another
|
|
1155
|
+
// (_uploadFiles keeps only the last) and upload the same path twice.
|
|
1156
|
+
if (seen.has(path)) {
|
|
1157
|
+
throw new LibfwError(`duplicate upload path: ${path}`, 'path');
|
|
1158
|
+
}
|
|
1159
|
+
seen.add(path);
|
|
1037
1160
|
this._uploadFiles.set(path, file);
|
|
1038
1161
|
plan.push({ path, size: file.size, mtime: Math.floor(file.lastModified / 1000) });
|
|
1039
1162
|
}
|
package/package.json
CHANGED
package/pkg/libfw_client.d.ts
CHANGED
|
@@ -17,14 +17,10 @@ export class LibfwClient {
|
|
|
17
17
|
done_bytes(): number;
|
|
18
18
|
/**
|
|
19
19
|
* Download a single file at `file_path` into the chosen local directory.
|
|
20
|
-
*
|
|
21
|
-
* Resolves with the number of bytes written.
|
|
22
20
|
*/
|
|
23
21
|
download_file(base_url: string, token: string, file_path: string): Promise<any>;
|
|
24
22
|
/**
|
|
25
23
|
* Download every file under the virtual `dirPath` (empty = root).
|
|
26
|
-
*
|
|
27
|
-
* Resolves with the number of bytes written.
|
|
28
24
|
*/
|
|
29
25
|
download_folder(base_url: string, token: string, dir_path: string): Promise<any>;
|
|
30
26
|
/**
|
|
@@ -74,8 +70,6 @@ export class LibfwClient {
|
|
|
74
70
|
tune_state(): any;
|
|
75
71
|
/**
|
|
76
72
|
* Upload the files reported by the JS `getFileList` callback.
|
|
77
|
-
*
|
|
78
|
-
* Resolves with the number of bytes uploaded.
|
|
79
73
|
*/
|
|
80
74
|
upload(base_url: string, token: string): Promise<any>;
|
|
81
75
|
}
|
|
@@ -109,10 +103,11 @@ export interface InitOutput {
|
|
|
109
103
|
readonly libfwclient_tune_state: (a: number) => any;
|
|
110
104
|
readonly libfwclient_upload: (a: number, b: number, c: number, d: number, e: number) => any;
|
|
111
105
|
readonly start: () => void;
|
|
112
|
-
readonly
|
|
113
|
-
readonly
|
|
114
|
-
readonly
|
|
115
|
-
readonly
|
|
106
|
+
readonly wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue__core_ed718c3d60ebd546___result__Result_____wasm_bindgen_b2c787b844a3ac79___JsError___true_: (a: number, b: number, c: any) => [number, number];
|
|
107
|
+
readonly wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___js_sys_209827a80d159db4___Function_fn_wasm_bindgen_b2c787b844a3ac79___JsValue_____wasm_bindgen_b2c787b844a3ac79___sys__Undefined___js_sys_209827a80d159db4___Function_fn_wasm_bindgen_b2c787b844a3ac79___JsValue_____wasm_bindgen_b2c787b844a3ac79___sys__Undefined_______true_: (a: number, b: number, c: any, d: any) => void;
|
|
108
|
+
readonly wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue______true_: (a: number, b: number, c: any) => void;
|
|
109
|
+
readonly wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue______true__2: (a: number, b: number, c: any) => void;
|
|
110
|
+
readonly wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke_______true_: (a: number, b: number) => void;
|
|
116
111
|
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
|
117
112
|
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
118
113
|
readonly __wbindgen_exn_store: (a: number) => void;
|
package/pkg/libfw_client.js
CHANGED
|
@@ -30,8 +30,6 @@ export class LibfwClient {
|
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* Download a single file at `file_path` into the chosen local directory.
|
|
33
|
-
*
|
|
34
|
-
* Resolves with the number of bytes written.
|
|
35
33
|
* @param {string} base_url
|
|
36
34
|
* @param {string} token
|
|
37
35
|
* @param {string} file_path
|
|
@@ -49,8 +47,6 @@ export class LibfwClient {
|
|
|
49
47
|
}
|
|
50
48
|
/**
|
|
51
49
|
* Download every file under the virtual `dirPath` (empty = root).
|
|
52
|
-
*
|
|
53
|
-
* Resolves with the number of bytes written.
|
|
54
50
|
* @param {string} base_url
|
|
55
51
|
* @param {string} token
|
|
56
52
|
* @param {string} dir_path
|
|
@@ -157,8 +153,6 @@ export class LibfwClient {
|
|
|
157
153
|
}
|
|
158
154
|
/**
|
|
159
155
|
* Upload the files reported by the JS `getFileList` callback.
|
|
160
|
-
*
|
|
161
|
-
* Resolves with the number of bytes uploaded.
|
|
162
156
|
* @param {string} base_url
|
|
163
157
|
* @param {string} token
|
|
164
158
|
* @returns {Promise<any>}
|
|
@@ -250,6 +244,9 @@ function __wbg_get_imports() {
|
|
|
250
244
|
__wbg_abort_4426bc6bd4153680: function() { return handleError(function (arg0) {
|
|
251
245
|
arg0.abort();
|
|
252
246
|
}, arguments); },
|
|
247
|
+
__wbg_abort_8bae0f33e7833997: function(arg0) {
|
|
248
|
+
arg0.abort();
|
|
249
|
+
},
|
|
253
250
|
__wbg_apply_3ac86a26fdb56c05: function() { return handleError(function (arg0, arg1, arg2) {
|
|
254
251
|
const ret = arg0.apply(arg1, arg2);
|
|
255
252
|
return ret;
|
|
@@ -270,9 +267,16 @@ function __wbg_get_imports() {
|
|
|
270
267
|
const ret = arg0.call(arg1, arg2);
|
|
271
268
|
return ret;
|
|
272
269
|
}, arguments); },
|
|
270
|
+
__wbg_catch_c1a60df4c30d76d3: function(arg0, arg1) {
|
|
271
|
+
const ret = arg0.catch(arg1);
|
|
272
|
+
return ret;
|
|
273
|
+
},
|
|
273
274
|
__wbg_clearInterval_2e2069e95ad09d4f: function(arg0, arg1) {
|
|
274
275
|
arg0.clearInterval(arg1);
|
|
275
276
|
},
|
|
277
|
+
__wbg_clearTimeout_8f80437be2324e09: function(arg0, arg1) {
|
|
278
|
+
arg0.clearTimeout(arg1);
|
|
279
|
+
},
|
|
276
280
|
__wbg_encodeURIComponent_d0140ae6e13eb27b: function(arg0, arg1) {
|
|
277
281
|
const ret = encodeURIComponent(getStringFromWasm0(arg0, arg1));
|
|
278
282
|
return ret;
|
|
@@ -384,6 +388,10 @@ function __wbg_get_imports() {
|
|
|
384
388
|
const ret = new Array();
|
|
385
389
|
return ret;
|
|
386
390
|
},
|
|
391
|
+
__wbg_new_4339b2a2675a03e3: function() { return handleError(function () {
|
|
392
|
+
const ret = new AbortController();
|
|
393
|
+
return ret;
|
|
394
|
+
}, arguments); },
|
|
387
395
|
__wbg_new_6b1dd7bed0e5462c: function() { return handleError(function () {
|
|
388
396
|
const ret = new XMLHttpRequest();
|
|
389
397
|
return ret;
|
|
@@ -395,7 +403,7 @@ function __wbg_get_imports() {
|
|
|
395
403
|
const a = state0.a;
|
|
396
404
|
state0.a = 0;
|
|
397
405
|
try {
|
|
398
|
-
return
|
|
406
|
+
return wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___js_sys_209827a80d159db4___Function_fn_wasm_bindgen_b2c787b844a3ac79___JsValue_____wasm_bindgen_b2c787b844a3ac79___sys__Undefined___js_sys_209827a80d159db4___Function_fn_wasm_bindgen_b2c787b844a3ac79___JsValue_____wasm_bindgen_b2c787b844a3ac79___sys__Undefined_______true_(a, state0.b, arg0, arg1);
|
|
399
407
|
} finally {
|
|
400
408
|
state0.a = a;
|
|
401
409
|
}
|
|
@@ -429,7 +437,7 @@ function __wbg_get_imports() {
|
|
|
429
437
|
const a = state0.a;
|
|
430
438
|
state0.a = 0;
|
|
431
439
|
try {
|
|
432
|
-
return
|
|
440
|
+
return wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___js_sys_209827a80d159db4___Function_fn_wasm_bindgen_b2c787b844a3ac79___JsValue_____wasm_bindgen_b2c787b844a3ac79___sys__Undefined___js_sys_209827a80d159db4___Function_fn_wasm_bindgen_b2c787b844a3ac79___JsValue_____wasm_bindgen_b2c787b844a3ac79___sys__Undefined_______true_(a, state0.b, arg0, arg1);
|
|
433
441
|
} finally {
|
|
434
442
|
state0.a = a;
|
|
435
443
|
}
|
|
@@ -448,10 +456,6 @@ function __wbg_get_imports() {
|
|
|
448
456
|
const ret = Date.now();
|
|
449
457
|
return ret;
|
|
450
458
|
},
|
|
451
|
-
__wbg_of_5f1b88183ddb5d94: function(arg0, arg1) {
|
|
452
|
-
const ret = Array.of(arg0, arg1);
|
|
453
|
-
return ret;
|
|
454
|
-
},
|
|
455
459
|
__wbg_open_90286a4ce0ae2098: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) {
|
|
456
460
|
arg0.open(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4), arg5 !== 0);
|
|
457
461
|
}, arguments); },
|
|
@@ -469,10 +473,6 @@ function __wbg_get_imports() {
|
|
|
469
473
|
__wbg_queueMicrotask_6a09b7bc46549209: function(arg0) {
|
|
470
474
|
queueMicrotask(arg0);
|
|
471
475
|
},
|
|
472
|
-
__wbg_race_ac5c7b465abcfa15: function(arg0) {
|
|
473
|
-
const ret = Promise.race(arg0);
|
|
474
|
-
return ret;
|
|
475
|
-
},
|
|
476
476
|
__wbg_read_8afa15f12a160ef8: function(arg0) {
|
|
477
477
|
const ret = arg0.read();
|
|
478
478
|
return ret;
|
|
@@ -501,10 +501,6 @@ function __wbg_get_imports() {
|
|
|
501
501
|
__wbg_setRequestHeader_fe390ff50d349432: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
|
502
502
|
arg0.setRequestHeader(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
|
503
503
|
}, arguments); },
|
|
504
|
-
__wbg_setTimeout_725a27c387d005c7: function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
|
505
|
-
const ret = arg0.setTimeout(arg1, arg2, arg3);
|
|
506
|
-
return ret;
|
|
507
|
-
}, arguments); },
|
|
508
504
|
__wbg_setTimeout_cfa2cf195c3738db: function() { return handleError(function (arg0, arg1, arg2) {
|
|
509
505
|
const ret = arg0.setTimeout(arg1, arg2);
|
|
510
506
|
return ret;
|
|
@@ -537,6 +533,13 @@ function __wbg_get_imports() {
|
|
|
537
533
|
__wbg_set_onreadystatechange_14ce44725c7e0789: function(arg0, arg1) {
|
|
538
534
|
arg0.onreadystatechange = arg1;
|
|
539
535
|
},
|
|
536
|
+
__wbg_set_signal_c4ef8faddb4c1446: function(arg0, arg1) {
|
|
537
|
+
arg0.signal = arg1;
|
|
538
|
+
},
|
|
539
|
+
__wbg_signal_dad7cb35193abd31: function(arg0) {
|
|
540
|
+
const ret = arg0.signal;
|
|
541
|
+
return ret;
|
|
542
|
+
},
|
|
540
543
|
__wbg_stack_3b0d974bbf31e44f: function(arg0, arg1) {
|
|
541
544
|
const ret = arg1.stack;
|
|
542
545
|
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
@@ -576,6 +579,10 @@ function __wbg_get_imports() {
|
|
|
576
579
|
const ret = arg0.then(arg1);
|
|
577
580
|
return ret;
|
|
578
581
|
},
|
|
582
|
+
__wbg_then_e0960b859f3ff223: function(arg0, arg1) {
|
|
583
|
+
const ret = arg0.then(arg1);
|
|
584
|
+
return ret;
|
|
585
|
+
},
|
|
579
586
|
__wbg_total_21672ff1bd8d23ea: function(arg0) {
|
|
580
587
|
const ret = arg0.total;
|
|
581
588
|
return ret;
|
|
@@ -585,26 +592,31 @@ function __wbg_get_imports() {
|
|
|
585
592
|
return ret;
|
|
586
593
|
}, arguments); },
|
|
587
594
|
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
|
588
|
-
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx:
|
|
589
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
595
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 209, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
|
596
|
+
const ret = makeMutClosure(arg0, arg1, wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue__core_ed718c3d60ebd546___result__Result_____wasm_bindgen_b2c787b844a3ac79___JsError___true_);
|
|
590
597
|
return ret;
|
|
591
598
|
},
|
|
592
599
|
__wbindgen_cast_0000000000000002: function(arg0, arg1) {
|
|
593
|
-
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [
|
|
594
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
600
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 5, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
601
|
+
const ret = makeMutClosure(arg0, arg1, wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue______true_);
|
|
595
602
|
return ret;
|
|
596
603
|
},
|
|
597
604
|
__wbindgen_cast_0000000000000003: function(arg0, arg1) {
|
|
598
|
-
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx:
|
|
599
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
605
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("ProgressEvent")], shim_idx: 5, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
606
|
+
const ret = makeMutClosure(arg0, arg1, wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue______true__2);
|
|
607
|
+
return ret;
|
|
608
|
+
},
|
|
609
|
+
__wbindgen_cast_0000000000000004: function(arg0, arg1) {
|
|
610
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 8, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
611
|
+
const ret = makeMutClosure(arg0, arg1, wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke_______true_);
|
|
600
612
|
return ret;
|
|
601
613
|
},
|
|
602
|
-
|
|
614
|
+
__wbindgen_cast_0000000000000005: function(arg0) {
|
|
603
615
|
// Cast intrinsic for `F64 -> Externref`.
|
|
604
616
|
const ret = arg0;
|
|
605
617
|
return ret;
|
|
606
618
|
},
|
|
607
|
-
|
|
619
|
+
__wbindgen_cast_0000000000000006: function(arg0, arg1) {
|
|
608
620
|
// Cast intrinsic for `Ref(String) -> Externref`.
|
|
609
621
|
const ret = getStringFromWasm0(arg0, arg1);
|
|
610
622
|
return ret;
|
|
@@ -625,23 +637,27 @@ function __wbg_get_imports() {
|
|
|
625
637
|
};
|
|
626
638
|
}
|
|
627
639
|
|
|
628
|
-
function
|
|
629
|
-
wasm.
|
|
640
|
+
function wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke_______true_(arg0, arg1) {
|
|
641
|
+
wasm.wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke_______true_(arg0, arg1);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue______true_(arg0, arg1, arg2) {
|
|
645
|
+
wasm.wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue______true_(arg0, arg1, arg2);
|
|
630
646
|
}
|
|
631
647
|
|
|
632
|
-
function
|
|
633
|
-
wasm.
|
|
648
|
+
function wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue______true__2(arg0, arg1, arg2) {
|
|
649
|
+
wasm.wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue______true__2(arg0, arg1, arg2);
|
|
634
650
|
}
|
|
635
651
|
|
|
636
|
-
function
|
|
637
|
-
const ret = wasm.
|
|
652
|
+
function wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue__core_ed718c3d60ebd546___result__Result_____wasm_bindgen_b2c787b844a3ac79___JsError___true_(arg0, arg1, arg2) {
|
|
653
|
+
const ret = wasm.wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue__core_ed718c3d60ebd546___result__Result_____wasm_bindgen_b2c787b844a3ac79___JsError___true_(arg0, arg1, arg2);
|
|
638
654
|
if (ret[1]) {
|
|
639
655
|
throw takeFromExternrefTable0(ret[0]);
|
|
640
656
|
}
|
|
641
657
|
}
|
|
642
658
|
|
|
643
|
-
function
|
|
644
|
-
wasm.
|
|
659
|
+
function wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___js_sys_209827a80d159db4___Function_fn_wasm_bindgen_b2c787b844a3ac79___JsValue_____wasm_bindgen_b2c787b844a3ac79___sys__Undefined___js_sys_209827a80d159db4___Function_fn_wasm_bindgen_b2c787b844a3ac79___JsValue_____wasm_bindgen_b2c787b844a3ac79___sys__Undefined_______true_(arg0, arg1, arg2, arg3) {
|
|
660
|
+
wasm.wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___js_sys_209827a80d159db4___Function_fn_wasm_bindgen_b2c787b844a3ac79___JsValue_____wasm_bindgen_b2c787b844a3ac79___sys__Undefined___js_sys_209827a80d159db4___Function_fn_wasm_bindgen_b2c787b844a3ac79___JsValue_____wasm_bindgen_b2c787b844a3ac79___sys__Undefined_______true_(arg0, arg1, arg2, arg3);
|
|
645
661
|
}
|
|
646
662
|
|
|
647
663
|
const LibfwClientFinalization = (typeof FinalizationRegistry === 'undefined')
|
package/pkg/libfw_client_bg.wasm
CHANGED
|
Binary file
|
|
@@ -19,10 +19,11 @@ export const libfwclient_total_bytes: (a: number) => number;
|
|
|
19
19
|
export const libfwclient_tune_state: (a: number) => any;
|
|
20
20
|
export const libfwclient_upload: (a: number, b: number, c: number, d: number, e: number) => any;
|
|
21
21
|
export const start: () => void;
|
|
22
|
-
export const
|
|
23
|
-
export const
|
|
24
|
-
export const
|
|
25
|
-
export const
|
|
22
|
+
export const wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue__core_ed718c3d60ebd546___result__Result_____wasm_bindgen_b2c787b844a3ac79___JsError___true_: (a: number, b: number, c: any) => [number, number];
|
|
23
|
+
export const wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___js_sys_209827a80d159db4___Function_fn_wasm_bindgen_b2c787b844a3ac79___JsValue_____wasm_bindgen_b2c787b844a3ac79___sys__Undefined___js_sys_209827a80d159db4___Function_fn_wasm_bindgen_b2c787b844a3ac79___JsValue_____wasm_bindgen_b2c787b844a3ac79___sys__Undefined_______true_: (a: number, b: number, c: any, d: any) => void;
|
|
24
|
+
export const wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue______true_: (a: number, b: number, c: any) => void;
|
|
25
|
+
export const wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke___wasm_bindgen_b2c787b844a3ac79___JsValue______true__2: (a: number, b: number, c: any) => void;
|
|
26
|
+
export const wasm_bindgen_b2c787b844a3ac79___convert__closures_____invoke_______true_: (a: number, b: number) => void;
|
|
26
27
|
export const __wbindgen_malloc: (a: number, b: number) => number;
|
|
27
28
|
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
28
29
|
export const __wbindgen_exn_store: (a: number) => void;
|
package/pkg/package.json
CHANGED
package/zip.js
CHANGED
|
@@ -51,8 +51,21 @@ export function createZip(entries) {
|
|
|
51
51
|
const central = [];
|
|
52
52
|
let offset = 0;
|
|
53
53
|
|
|
54
|
+
// This writer uses classic 32-bit ZIP fields (no ZIP64): entry sizes, the
|
|
55
|
+
// local-header offset, the central-directory size/offset and the entry
|
|
56
|
+
// count are all stored as u32/u16. Exceeding any of them would silently
|
|
57
|
+
// corrupt the archive, so fail loudly instead.
|
|
58
|
+
const MAX_U32 = 0xffffffff;
|
|
59
|
+
const MAX_U16 = 0xffff;
|
|
60
|
+
if (entries.length > MAX_U16) {
|
|
61
|
+
throw new Error(`too many zip entries: ${entries.length} > ${MAX_U16}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
54
64
|
for (const entry of entries) {
|
|
55
65
|
const nameBytes = encoder.encode(entry.name);
|
|
66
|
+
if (nameBytes.length > MAX_U16) {
|
|
67
|
+
throw new Error(`zip entry name too long: ${entry.name.slice(0, 64)}…`);
|
|
68
|
+
}
|
|
56
69
|
const data = entry.data;
|
|
57
70
|
const crc = crc32(data);
|
|
58
71
|
|
|
@@ -75,6 +88,9 @@ export function createZip(entries) {
|
|
|
75
88
|
body.push(local, data);
|
|
76
89
|
central.push({ nameBytes, crc, size: data.length, offset });
|
|
77
90
|
offset += local.length + data.length;
|
|
91
|
+
if (offset > MAX_U32) {
|
|
92
|
+
throw new Error('zip archive exceeds the 4 GiB limit of this writer');
|
|
93
|
+
}
|
|
78
94
|
}
|
|
79
95
|
|
|
80
96
|
// Central directory.
|
|
@@ -104,6 +120,9 @@ export function createZip(entries) {
|
|
|
104
120
|
dir.push(cd);
|
|
105
121
|
cdSize += cd.length;
|
|
106
122
|
}
|
|
123
|
+
if (cdSize > MAX_U32) {
|
|
124
|
+
throw new Error('zip central directory exceeds the 4 GiB limit of this writer');
|
|
125
|
+
}
|
|
107
126
|
|
|
108
127
|
const cdOffset = offset;
|
|
109
128
|
|