@cj-tech-master/excelts 6.0.0-beta.4 → 6.0.0-beta.5
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/dist/browser/modules/archive/create-archive.d.ts +23 -0
- package/dist/browser/modules/archive/create-archive.js +13 -0
- package/dist/browser/modules/archive/fs/archive-file.d.ts +1 -1
- package/dist/browser/modules/archive/fs/types.d.ts +1 -1
- package/dist/browser/modules/archive/index.base.d.ts +6 -4
- package/dist/browser/modules/archive/index.base.js +5 -5
- package/dist/browser/modules/archive/read-archive.d.ts +27 -0
- package/dist/browser/modules/archive/read-archive.js +12 -0
- package/dist/browser/modules/archive/unzip/index.d.ts +0 -27
- package/dist/browser/modules/archive/unzip/index.js +0 -4
- package/dist/browser/modules/archive/{io → unzip}/remote-zip-reader.d.ts +1 -1
- package/dist/browser/modules/archive/{io → unzip}/remote-zip-reader.js +3 -3
- package/dist/browser/modules/archive/unzip/zip-reader.d.ts +1 -2
- package/dist/browser/modules/archive/unzip/zip-reader.js +0 -3
- package/dist/browser/modules/archive/zip/index.d.ts +0 -24
- package/dist/browser/modules/archive/zip/index.js +0 -4
- package/dist/browser/modules/archive/zip/zip-archive.d.ts +1 -2
- package/dist/browser/modules/archive/zip/zip-archive.js +42 -140
- package/dist/browser/modules/archive/zip/zip-editor.js +117 -207
- package/dist/browser/modules/archive/zip/zip-output-pipeline.d.ts +55 -0
- package/dist/browser/modules/archive/zip/zip-output-pipeline.js +127 -0
- package/dist/cjs/modules/archive/create-archive.js +16 -0
- package/dist/cjs/modules/archive/index.base.js +7 -9
- package/dist/cjs/modules/archive/read-archive.js +15 -0
- package/dist/cjs/modules/archive/unzip/index.js +0 -5
- package/dist/cjs/modules/archive/{io → unzip}/remote-zip-reader.js +3 -3
- package/dist/cjs/modules/archive/unzip/zip-reader.js +0 -4
- package/dist/cjs/modules/archive/zip/index.js +0 -5
- package/dist/cjs/modules/archive/zip/zip-archive.js +40 -139
- package/dist/cjs/modules/archive/zip/zip-editor.js +115 -205
- package/dist/cjs/modules/archive/zip/zip-output-pipeline.js +130 -0
- package/dist/esm/modules/archive/create-archive.js +13 -0
- package/dist/esm/modules/archive/index.base.js +5 -5
- package/dist/esm/modules/archive/read-archive.js +12 -0
- package/dist/esm/modules/archive/unzip/index.js +0 -4
- package/dist/esm/modules/archive/{io → unzip}/remote-zip-reader.js +3 -3
- package/dist/esm/modules/archive/unzip/zip-reader.js +0 -3
- package/dist/esm/modules/archive/zip/index.js +0 -4
- package/dist/esm/modules/archive/zip/zip-archive.js +42 -140
- package/dist/esm/modules/archive/zip/zip-editor.js +117 -207
- package/dist/esm/modules/archive/zip/zip-output-pipeline.js +127 -0
- package/dist/iife/excelts.iife.js +1 -1
- package/dist/iife/excelts.iife.min.js +1 -1
- package/dist/types/modules/archive/create-archive.d.ts +23 -0
- package/dist/types/modules/archive/fs/archive-file.d.ts +1 -1
- package/dist/types/modules/archive/fs/types.d.ts +1 -1
- package/dist/types/modules/archive/index.base.d.ts +6 -4
- package/dist/types/modules/archive/read-archive.d.ts +27 -0
- package/dist/types/modules/archive/unzip/index.d.ts +0 -27
- package/dist/types/modules/archive/{io → unzip}/remote-zip-reader.d.ts +1 -1
- package/dist/types/modules/archive/unzip/zip-reader.d.ts +1 -2
- package/dist/types/modules/archive/zip/index.d.ts +0 -24
- package/dist/types/modules/archive/zip/zip-archive.d.ts +1 -2
- package/dist/types/modules/archive/zip/zip-output-pipeline.d.ts +55 -0
- package/package.json +1 -1
- package/dist/browser/modules/archive/formats/index.d.ts +0 -9
- package/dist/browser/modules/archive/formats/index.js +0 -28
- package/dist/cjs/modules/archive/formats/index.js +0 -32
- package/dist/esm/modules/archive/formats/index.js +0 -28
- package/dist/types/modules/archive/formats/index.d.ts +0 -9
- /package/dist/browser/modules/archive/{formats → shared}/types.d.ts +0 -0
- /package/dist/browser/modules/archive/{formats → shared}/types.js +0 -0
- /package/dist/cjs/modules/archive/{formats → shared}/types.js +0 -0
- /package/dist/esm/modules/archive/{formats → shared}/types.js +0 -0
- /package/dist/types/modules/archive/{formats → shared}/types.d.ts +0 -0
|
@@ -2,13 +2,12 @@ import { dateToZipDos } from "../zip-spec/timestamps.js";
|
|
|
2
2
|
import { encodeZipStringWithCodec, resolveZipStringCodec } from "../shared/text.js";
|
|
3
3
|
import { DEFAULT_ZIP_LEVEL, DEFAULT_ZIP_TIMESTAMPS, REPRODUCIBLE_ZIP_MOD_TIME } from "../shared/defaults.js";
|
|
4
4
|
import { BufferReader, HttpRangeReader } from "../io/random-access.js";
|
|
5
|
-
import { RemoteZipReader } from "../
|
|
5
|
+
import { RemoteZipReader } from "../unzip/remote-zip-reader.js";
|
|
6
6
|
import { toAsyncIterable, collectUint8ArrayStream, toUint8ArraySync, isSyncArchiveSource, isInMemoryArchiveSource, resolveArchiveSourceToBuffer } from "../io/archive-source.js";
|
|
7
7
|
import { collect, pipeIterableToSink } from "../io/archive-sink.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { ZipDeflateFile, StreamingZip, ZipRawFile } from "./stream.js";
|
|
8
|
+
import { throwIfAborted, toError } from "../shared/errors.js";
|
|
9
|
+
import { createZipOperation } from "./zip-output-pipeline.js";
|
|
10
|
+
import { ZipDeflateFile, ZipRawFile } from "./stream.js";
|
|
12
11
|
import { createZip, createZipSync } from "./zip-bytes.js";
|
|
13
12
|
import { FLAG_ENCRYPTED } from "../zip-spec/zip-records.js";
|
|
14
13
|
import { ZipEditView } from "./zip-edit-view.js";
|
|
@@ -360,226 +359,137 @@ export class ZipEditor {
|
|
|
360
359
|
const signalOpt = options.signal ?? this._streamDefaults.signal;
|
|
361
360
|
const onProgress = options.onProgress ?? this._streamDefaults.onProgress;
|
|
362
361
|
const progressIntervalMs = options.progressIntervalMs ?? this._streamDefaults.progressIntervalMs;
|
|
363
|
-
const { controller, cleanup: cleanupAbortLink } = createLinkedAbortController(signalOpt);
|
|
364
|
-
const signal = controller.signal;
|
|
365
362
|
const preservedMeta = this._buildRawPreservedEntries();
|
|
366
363
|
const sets = this._buildSetEntries();
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
364
|
+
return createZipOperation(preservedMeta.length + sets.length, {
|
|
365
|
+
comment: this._options.comment,
|
|
366
|
+
zip64: this._options.zip64,
|
|
367
|
+
codec: this._stringCodec
|
|
368
|
+
}, { signal: signalOpt, onProgress, progressIntervalMs }, async ({ zip, signal, progress }) => {
|
|
369
|
+
// Classify preserved entries using the pipeline's linked signal so that
|
|
370
|
+
// abort() properly cancels raw compressed streams.
|
|
371
|
+
const preservedRaw = [];
|
|
372
|
+
const preservedRecompressed = [];
|
|
373
|
+
for (const p of preservedMeta) {
|
|
374
|
+
const compressedData = this._remote.getRawCompressedStream(p.info.path, { signal });
|
|
375
|
+
if (compressedData) {
|
|
376
|
+
preservedRaw.push({ ...p, compressedData });
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
this._emitWarning(p.info.path, "raw_unavailable", `Cannot read raw compressed payload for entry "${p.info.path}".`);
|
|
380
|
+
if (this._options.preserve === "strict") {
|
|
381
|
+
throw new Error(`Cannot preserve entry "${p.info.path}" because its raw compressed payload is unavailable.`);
|
|
382
|
+
}
|
|
383
|
+
// We cannot re-encrypt entries; best-effort must not silently output decrypted content.
|
|
384
|
+
if (p.info.isEncrypted) {
|
|
385
|
+
this._emitWarning(p.info.path, "encryption_unsupported", `Cannot best-effort preserve encrypted entry "${p.info.path}" without raw passthrough.`);
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
// Best-effort fallback: extract and re-add the entry.
|
|
389
|
+
preservedRecompressed.push({ name: p.outName, info: p.info });
|
|
378
390
|
}
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
391
|
+
// Update entriesTotal now that we know how many entries survived classification.
|
|
392
|
+
const actualTotal = preservedRaw.length + preservedRecompressed.length + sets.length;
|
|
393
|
+
progress.set("entriesTotal", actualTotal);
|
|
394
|
+
// 1) Preserved entries: passthrough raw payload.
|
|
395
|
+
for (let i = 0; i < preservedRaw.length; i++) {
|
|
396
|
+
throwIfAborted(signal);
|
|
397
|
+
const entry = preservedRaw[i];
|
|
398
|
+
progress.update({ currentEntry: { name: entry.outName, index: i, bytesIn: 0 } });
|
|
399
|
+
const rawFile = this._buildPreservedRawFile(entry.outName, entry.info, entry.compressedData, this._options.zip64);
|
|
400
|
+
zip.add(rawFile);
|
|
401
|
+
// StreamingZip auto-starts passthrough files; await completion for accurate progress.
|
|
402
|
+
await rawFile.done();
|
|
403
|
+
progress.set("entriesDone", progress.snapshot.entriesDone + 1);
|
|
383
404
|
}
|
|
384
|
-
// Best-effort
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
entriesDone: 0,
|
|
393
|
-
bytesIn: 0,
|
|
394
|
-
bytesOut: 0,
|
|
395
|
-
zip64: this._options.zip64
|
|
396
|
-
}, onProgress, { intervalMs: progressIntervalMs });
|
|
397
|
-
const queue = createAsyncQueue({
|
|
398
|
-
onCancel: () => {
|
|
405
|
+
// 1b) Best-effort preserved entries: extract and re-add.
|
|
406
|
+
for (let k = 0; k < preservedRecompressed.length; k++) {
|
|
407
|
+
throwIfAborted(signal);
|
|
408
|
+
const idx = preservedRaw.length + k;
|
|
409
|
+
const entry = preservedRecompressed[k];
|
|
410
|
+
let entryBytesIn = 0;
|
|
411
|
+
progress.update({ currentEntry: { name: entry.name, index: idx, bytesIn: 0 } });
|
|
412
|
+
let data;
|
|
399
413
|
try {
|
|
400
|
-
|
|
414
|
+
data = await this._remote.extractEntry(entry.info);
|
|
401
415
|
}
|
|
402
|
-
catch {
|
|
403
|
-
|
|
416
|
+
catch (e) {
|
|
417
|
+
const err = toError(e);
|
|
418
|
+
this._emitWarning(entry.info.path, "unknown", `Failed to extract entry "${entry.info.path}" for best-effort preserve: ${err.message}`);
|
|
419
|
+
progress.set("entriesDone", progress.snapshot.entriesDone + 1);
|
|
420
|
+
continue;
|
|
404
421
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
422
|
+
const fallbackLevel = entry.info.compressionMethod === 0 ? 0 : this._options.level;
|
|
423
|
+
const file = new ZipDeflateFile(entry.name, buildZipDeflateFileOptions({
|
|
424
|
+
level: fallbackLevel,
|
|
425
|
+
modTime: entry.info.lastModified,
|
|
426
|
+
comment: entry.info.comment,
|
|
427
|
+
externalAttributes: entry.info.externalAttributes,
|
|
428
|
+
versionMadeBy: entry.info.versionMadeBy
|
|
429
|
+
}, {
|
|
430
|
+
level: this._options.level,
|
|
431
|
+
modTime: this._options.modTime,
|
|
432
|
+
timestamps: this._options.timestamps,
|
|
433
|
+
smartStore: this._options.smartStore,
|
|
434
|
+
zip64: this._options.zip64,
|
|
435
|
+
path: this._options.path,
|
|
436
|
+
encoding: this._options.encoding
|
|
437
|
+
}));
|
|
438
|
+
zip.add(file);
|
|
439
|
+
entryBytesIn += data.length;
|
|
414
440
|
progress.mutate(s => {
|
|
415
|
-
s.
|
|
441
|
+
s.bytesIn += data.length;
|
|
442
|
+
s.currentEntry = { name: entry.name, index: idx, bytesIn: entryBytesIn };
|
|
416
443
|
});
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
if (progress.snapshot.phase === "running") {
|
|
421
|
-
progress.update({ phase: "done" });
|
|
422
|
-
}
|
|
423
|
-
queue.close();
|
|
444
|
+
await file.push(data, true);
|
|
445
|
+
await file.complete();
|
|
446
|
+
progress.set("entriesDone", progress.snapshot.entriesDone + 1);
|
|
424
447
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
// 1) Preserved entries: passthrough raw payload.
|
|
445
|
-
for (let i = 0; i < preservedRaw.length; i++) {
|
|
446
|
-
throwIfAborted(signal);
|
|
447
|
-
const entry = preservedRaw[i];
|
|
448
|
-
progress.update({ currentEntry: { name: entry.outName, index: i, bytesIn: 0 } });
|
|
449
|
-
const rawFile = this._buildPreservedRawFile(entry.outName, entry.info, entry.compressedData, this._options.zip64);
|
|
450
|
-
zip.add(rawFile);
|
|
451
|
-
// StreamingZip auto-starts passthrough files; await completion for accurate progress.
|
|
452
|
-
await rawFile.done();
|
|
453
|
-
progress.set("entriesDone", progress.snapshot.entriesDone + 1);
|
|
454
|
-
}
|
|
455
|
-
// 1b) Best-effort preserved entries: extract and re-add.
|
|
456
|
-
for (let k = 0; k < preservedRecompressed.length; k++) {
|
|
457
|
-
throwIfAborted(signal);
|
|
458
|
-
const idx = preservedRaw.length + k;
|
|
459
|
-
const entry = preservedRecompressed[k];
|
|
460
|
-
let entryBytesIn = 0;
|
|
461
|
-
progress.update({ currentEntry: { name: entry.name, index: idx, bytesIn: 0 } });
|
|
462
|
-
let data;
|
|
463
|
-
try {
|
|
464
|
-
data = await this._remote.extractEntry(entry.info);
|
|
465
|
-
}
|
|
466
|
-
catch (e) {
|
|
467
|
-
const err = toError(e);
|
|
468
|
-
this._emitWarning(entry.info.path, "unknown", `Failed to extract entry "${entry.info.path}" for best-effort preserve: ${err.message}`);
|
|
469
|
-
progress.set("entriesDone", progress.snapshot.entriesDone + 1);
|
|
470
|
-
continue;
|
|
471
|
-
}
|
|
472
|
-
const fallbackLevel = entry.info.compressionMethod === 0 ? 0 : this._options.level;
|
|
473
|
-
const file = new ZipDeflateFile(entry.name, buildZipDeflateFileOptions({
|
|
474
|
-
level: fallbackLevel,
|
|
475
|
-
modTime: entry.info.lastModified,
|
|
476
|
-
comment: entry.info.comment,
|
|
477
|
-
externalAttributes: entry.info.externalAttributes,
|
|
478
|
-
versionMadeBy: entry.info.versionMadeBy
|
|
479
|
-
}, {
|
|
480
|
-
level: this._options.level,
|
|
481
|
-
modTime: this._options.modTime,
|
|
482
|
-
timestamps: this._options.timestamps,
|
|
483
|
-
smartStore: this._options.smartStore,
|
|
484
|
-
zip64: this._options.zip64,
|
|
485
|
-
path: this._options.path,
|
|
486
|
-
encoding: this._options.encoding
|
|
487
|
-
}));
|
|
488
|
-
zip.add(file);
|
|
489
|
-
entryBytesIn += data.length;
|
|
448
|
+
// 2) Set/update entries: compress from source.
|
|
449
|
+
for (let j = 0; j < sets.length; j++) {
|
|
450
|
+
throwIfAborted(signal);
|
|
451
|
+
const idx = preservedRaw.length + preservedRecompressed.length + j;
|
|
452
|
+
const entry = sets[j];
|
|
453
|
+
let entryBytesIn = 0;
|
|
454
|
+
progress.update({ currentEntry: { name: entry.name, index: idx, bytesIn: 0 } });
|
|
455
|
+
const file = new ZipDeflateFile(entry.name, buildZipDeflateFileOptions(entry.options, {
|
|
456
|
+
level: this._options.level,
|
|
457
|
+
modTime: this._options.modTime,
|
|
458
|
+
timestamps: this._options.timestamps,
|
|
459
|
+
smartStore: this._options.smartStore,
|
|
460
|
+
zip64: this._options.zip64,
|
|
461
|
+
path: this._options.path,
|
|
462
|
+
encoding: this._options.encoding
|
|
463
|
+
}));
|
|
464
|
+
zip.add(file);
|
|
465
|
+
const onChunk = (chunk) => {
|
|
466
|
+
entryBytesIn += chunk.length;
|
|
490
467
|
progress.mutate(s => {
|
|
491
|
-
s.bytesIn +=
|
|
468
|
+
s.bytesIn += chunk.length;
|
|
492
469
|
s.currentEntry = { name: entry.name, index: idx, bytesIn: entryBytesIn };
|
|
493
470
|
});
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}
|
|
498
|
-
// 2) Set/update entries: compress from source.
|
|
499
|
-
for (let j = 0; j < sets.length; j++) {
|
|
471
|
+
};
|
|
472
|
+
if (isSyncArchiveSource(entry.source)) {
|
|
473
|
+
const bytes = toUint8ArraySync(entry.source);
|
|
500
474
|
throwIfAborted(signal);
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
let entryBytesIn = 0;
|
|
504
|
-
progress.update({ currentEntry: { name: entry.name, index: idx, bytesIn: 0 } });
|
|
505
|
-
const file = new ZipDeflateFile(entry.name, buildZipDeflateFileOptions(entry.options, {
|
|
506
|
-
level: this._options.level,
|
|
507
|
-
modTime: this._options.modTime,
|
|
508
|
-
timestamps: this._options.timestamps,
|
|
509
|
-
smartStore: this._options.smartStore,
|
|
510
|
-
zip64: this._options.zip64,
|
|
511
|
-
path: this._options.path,
|
|
512
|
-
encoding: this._options.encoding
|
|
513
|
-
}));
|
|
514
|
-
zip.add(file);
|
|
515
|
-
const onChunk = (chunk) => {
|
|
516
|
-
entryBytesIn += chunk.length;
|
|
517
|
-
progress.mutate(s => {
|
|
518
|
-
s.bytesIn += chunk.length;
|
|
519
|
-
s.currentEntry = { name: entry.name, index: idx, bytesIn: entryBytesIn };
|
|
520
|
-
});
|
|
521
|
-
};
|
|
522
|
-
if (isSyncArchiveSource(entry.source)) {
|
|
523
|
-
const bytes = toUint8ArraySync(entry.source);
|
|
524
|
-
throwIfAborted(signal);
|
|
525
|
-
onChunk(bytes);
|
|
526
|
-
await file.push(bytes, true);
|
|
527
|
-
}
|
|
528
|
-
else {
|
|
529
|
-
// Streaming path (includes Blob via toAsyncIterable(Blob) which prefers Blob.stream())
|
|
530
|
-
for await (const chunk of toAsyncIterable(entry.source, { signal, onChunk })) {
|
|
531
|
-
throwIfAborted(signal);
|
|
532
|
-
await file.push(chunk, false);
|
|
533
|
-
}
|
|
534
|
-
throwIfAborted(signal);
|
|
535
|
-
await file.push(new Uint8Array(0), true);
|
|
536
|
-
}
|
|
537
|
-
await file.complete();
|
|
538
|
-
progress.set("entriesDone", progress.snapshot.entriesDone + 1);
|
|
539
|
-
}
|
|
540
|
-
throwIfAborted(signal);
|
|
541
|
-
zip.end();
|
|
542
|
-
}
|
|
543
|
-
catch (e) {
|
|
544
|
-
const err = toError(e);
|
|
545
|
-
if (err.name === "AbortError") {
|
|
546
|
-
progress.update({ phase: "aborted" });
|
|
547
|
-
try {
|
|
548
|
-
zip.abort(err);
|
|
549
|
-
}
|
|
550
|
-
catch {
|
|
551
|
-
// ignore
|
|
552
|
-
}
|
|
475
|
+
onChunk(bytes);
|
|
476
|
+
await file.push(bytes, true);
|
|
553
477
|
}
|
|
554
478
|
else {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
}
|
|
563
|
-
catch {
|
|
564
|
-
// ignore
|
|
479
|
+
// Streaming path (includes Blob via toAsyncIterable(Blob) which prefers Blob.stream())
|
|
480
|
+
for await (const chunk of toAsyncIterable(entry.source, { signal, onChunk })) {
|
|
481
|
+
throwIfAborted(signal);
|
|
482
|
+
await file.push(chunk, false);
|
|
483
|
+
}
|
|
484
|
+
throwIfAborted(signal);
|
|
485
|
+
await file.push(new Uint8Array(0), true);
|
|
565
486
|
}
|
|
566
|
-
|
|
567
|
-
progress.
|
|
487
|
+
await file.complete();
|
|
488
|
+
progress.set("entriesDone", progress.snapshot.entriesDone + 1);
|
|
568
489
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
signal,
|
|
573
|
-
abort(reason) {
|
|
574
|
-
controller.abort(reason);
|
|
575
|
-
},
|
|
576
|
-
pointer() {
|
|
577
|
-
return progress.snapshot.bytesOut;
|
|
578
|
-
},
|
|
579
|
-
progress() {
|
|
580
|
-
return progress.snapshotCopy();
|
|
581
|
-
}
|
|
582
|
-
};
|
|
490
|
+
throwIfAborted(signal);
|
|
491
|
+
zip.end();
|
|
492
|
+
});
|
|
583
493
|
}
|
|
584
494
|
/**
|
|
585
495
|
* Get the output as a single Uint8Array.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared output pipeline for ZIP streaming operations.
|
|
3
|
+
*
|
|
4
|
+
* Both `ZipArchive` and `ZipEditor` use an identical scaffolding pattern for
|
|
5
|
+
* their `operation()` method: signal/abort wiring, ProgressEmitter, async queue,
|
|
6
|
+
* StreamingZip callback, error handling, and return object construction.
|
|
7
|
+
* This module extracts that shared boilerplate into a single reusable function.
|
|
8
|
+
*/
|
|
9
|
+
import { StreamingZip } from "./stream.js";
|
|
10
|
+
import { ProgressEmitter } from "../shared/progress.js";
|
|
11
|
+
import type { ZipStringCodec, ZipStringEncoding } from "../shared/text.js";
|
|
12
|
+
import type { Zip64Mode } from "../zip-spec/zip-records.js";
|
|
13
|
+
import type { ZipOperation, ZipProgress, ZipStreamOptions } from "./progress.js";
|
|
14
|
+
/** Resolved signal/progress options passed to the pipeline. */
|
|
15
|
+
export interface ZipPipelineOptions {
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
onProgress?: ZipStreamOptions["onProgress"];
|
|
18
|
+
progressIntervalMs?: number;
|
|
19
|
+
}
|
|
20
|
+
/** Options for constructing the internal StreamingZip instance. */
|
|
21
|
+
export interface ZipPipelineZipOptions {
|
|
22
|
+
comment?: string;
|
|
23
|
+
zip64?: Zip64Mode;
|
|
24
|
+
encoding?: ZipStringEncoding;
|
|
25
|
+
codec?: ZipStringCodec;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Callback that processes entries within the pipeline.
|
|
29
|
+
*
|
|
30
|
+
* The pipeline sets up all scaffolding (abort, progress, queue, StreamingZip)
|
|
31
|
+
* and then calls this function to do the actual per-entry work. The callback
|
|
32
|
+
* receives:
|
|
33
|
+
*
|
|
34
|
+
* - `zip` — the StreamingZip instance to add files to
|
|
35
|
+
* - `signal` — the linked AbortSignal
|
|
36
|
+
* - `progress` — the ProgressEmitter to update
|
|
37
|
+
*
|
|
38
|
+
* The callback MUST call `zip.end()` when all entries have been added.
|
|
39
|
+
* It SHOULD call `throwIfAborted(signal)` between entries.
|
|
40
|
+
*/
|
|
41
|
+
export type ZipPipelineProcessFn = (ctx: {
|
|
42
|
+
zip: StreamingZip;
|
|
43
|
+
signal: AbortSignal;
|
|
44
|
+
progress: ProgressEmitter<ZipProgress>;
|
|
45
|
+
}) => Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Create a streaming ZIP operation with all shared boilerplate wired up.
|
|
48
|
+
*
|
|
49
|
+
* @param entriesTotal - Total number of entries (for progress reporting)
|
|
50
|
+
* @param zipOptions - Options for the StreamingZip instance
|
|
51
|
+
* @param pipelineOpts - Signal, progress callback, and interval
|
|
52
|
+
* @param processFn - Callback that adds entries to the StreamingZip
|
|
53
|
+
* @returns A `ZipOperation` handle
|
|
54
|
+
*/
|
|
55
|
+
export declare function createZipOperation(entriesTotal: number, zipOptions: ZipPipelineZipOptions, pipelineOpts: ZipPipelineOptions, processFn: ZipPipelineProcessFn): ZipOperation;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared output pipeline for ZIP streaming operations.
|
|
3
|
+
*
|
|
4
|
+
* Both `ZipArchive` and `ZipEditor` use an identical scaffolding pattern for
|
|
5
|
+
* their `operation()` method: signal/abort wiring, ProgressEmitter, async queue,
|
|
6
|
+
* StreamingZip callback, error handling, and return object construction.
|
|
7
|
+
* This module extracts that shared boilerplate into a single reusable function.
|
|
8
|
+
*/
|
|
9
|
+
import { StreamingZip } from "./stream.js";
|
|
10
|
+
import { createAsyncQueue } from "../shared/async-queue.js";
|
|
11
|
+
import { createLinkedAbortController, createAbortError, toError } from "../shared/errors.js";
|
|
12
|
+
import { ProgressEmitter } from "../shared/progress.js";
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// Pipeline
|
|
15
|
+
// =============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* Create a streaming ZIP operation with all shared boilerplate wired up.
|
|
18
|
+
*
|
|
19
|
+
* @param entriesTotal - Total number of entries (for progress reporting)
|
|
20
|
+
* @param zipOptions - Options for the StreamingZip instance
|
|
21
|
+
* @param pipelineOpts - Signal, progress callback, and interval
|
|
22
|
+
* @param processFn - Callback that adds entries to the StreamingZip
|
|
23
|
+
* @returns A `ZipOperation` handle
|
|
24
|
+
*/
|
|
25
|
+
export function createZipOperation(entriesTotal, zipOptions, pipelineOpts, processFn) {
|
|
26
|
+
const { controller, cleanup: cleanupAbortLink } = createLinkedAbortController(pipelineOpts.signal);
|
|
27
|
+
const signal = controller.signal;
|
|
28
|
+
const progress = new ProgressEmitter({
|
|
29
|
+
type: "zip",
|
|
30
|
+
phase: "running",
|
|
31
|
+
entriesTotal,
|
|
32
|
+
entriesDone: 0,
|
|
33
|
+
bytesIn: 0,
|
|
34
|
+
bytesOut: 0,
|
|
35
|
+
zip64: zipOptions.zip64 ?? "auto"
|
|
36
|
+
}, pipelineOpts.onProgress, { intervalMs: pipelineOpts.progressIntervalMs });
|
|
37
|
+
const queue = createAsyncQueue({
|
|
38
|
+
onCancel: () => {
|
|
39
|
+
try {
|
|
40
|
+
controller.abort("cancelled");
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// ignore
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
const zip = new StreamingZip((err, data, final) => {
|
|
48
|
+
if (err) {
|
|
49
|
+
progress.update({ phase: progress.snapshot.phase === "aborted" ? "aborted" : "error" });
|
|
50
|
+
queue.fail(err);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (data.length) {
|
|
54
|
+
progress.mutate(s => {
|
|
55
|
+
s.bytesOut += data.length;
|
|
56
|
+
});
|
|
57
|
+
queue.push(data);
|
|
58
|
+
}
|
|
59
|
+
if (final) {
|
|
60
|
+
if (progress.snapshot.phase === "running") {
|
|
61
|
+
progress.update({ phase: "done" });
|
|
62
|
+
}
|
|
63
|
+
queue.close();
|
|
64
|
+
}
|
|
65
|
+
}, {
|
|
66
|
+
comment: zipOptions.comment,
|
|
67
|
+
zip64: zipOptions.zip64,
|
|
68
|
+
encoding: zipOptions.encoding,
|
|
69
|
+
codec: zipOptions.codec
|
|
70
|
+
});
|
|
71
|
+
const onAbort = () => {
|
|
72
|
+
const err = createAbortError(signal.reason);
|
|
73
|
+
progress.update({ phase: "aborted" });
|
|
74
|
+
try {
|
|
75
|
+
zip.abort(err);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// ignore
|
|
79
|
+
}
|
|
80
|
+
queue.fail(err);
|
|
81
|
+
};
|
|
82
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
83
|
+
(async () => {
|
|
84
|
+
try {
|
|
85
|
+
await processFn({ zip, signal, progress });
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
const err = toError(e);
|
|
89
|
+
if (err.name === "AbortError") {
|
|
90
|
+
progress.update({ phase: "aborted" });
|
|
91
|
+
try {
|
|
92
|
+
zip.abort(err);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// ignore
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
progress.update({ phase: "error" });
|
|
100
|
+
}
|
|
101
|
+
queue.fail(err);
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
try {
|
|
105
|
+
signal.removeEventListener("abort", onAbort);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// ignore
|
|
109
|
+
}
|
|
110
|
+
cleanupAbortLink();
|
|
111
|
+
progress.emitNow();
|
|
112
|
+
}
|
|
113
|
+
})();
|
|
114
|
+
return {
|
|
115
|
+
iterable: queue.iterable,
|
|
116
|
+
signal,
|
|
117
|
+
abort(reason) {
|
|
118
|
+
controller.abort(reason);
|
|
119
|
+
},
|
|
120
|
+
pointer() {
|
|
121
|
+
return progress.snapshot.bytesOut;
|
|
122
|
+
},
|
|
123
|
+
progress() {
|
|
124
|
+
return progress.snapshotCopy();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.zip = zip;
|
|
4
|
+
const tar_archive_1 = require("./tar/tar-archive.js");
|
|
5
|
+
const zip_archive_1 = require("./zip/zip-archive.js");
|
|
6
|
+
function zip(options = {}) {
|
|
7
|
+
if (options.format === "tar") {
|
|
8
|
+
return new tar_archive_1.TarArchive({
|
|
9
|
+
modTime: options.modTime,
|
|
10
|
+
signal: options.signal,
|
|
11
|
+
onProgress: options.onProgress,
|
|
12
|
+
progressIntervalMs: options.progressIntervalMs
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return new zip_archive_1.ZipArchive({ ...options, format: "zip" });
|
|
16
|
+
}
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* from this file and then layer their platform-specific bindings.
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.base64ToUint8Array = exports.isTarDataEntry = exports.isTarHardLink = exports.isTarSymlink = exports.isTarDirectory = exports.isTarFile = exports.untar = exports.parseTarStream = exports.parseTar = exports.tarSync = exports.tar = exports.createTarReader = exports.createTarArchive = exports.TarReaderEntry = exports.TarReader = exports.TarArchive = exports.TAR_TYPE = exports.TAR_BLOCK_SIZE = exports.
|
|
11
|
-
exports.encryptionMethodFromAesKeyStrength = exports.getAesKeyStrength = exports.isAesEncryption = exports.getEncryptionMethodName = exports.randomBytes = exports.buildAesExtraField = exports.aesEncryptedSize = exports.aesEncrypt = exports.aesDecrypt = exports.COMPRESSION_METHOD_AES = exports.AES_PASSWORD_VERIFY_LENGTH = exports.AES_AUTH_CODE_LENGTH = exports.AES_KEY_LENGTH = exports.AES_SALT_LENGTH = exports.AES_EXTRA_FIELD_ID = exports.AES_VERSION_AE2 = exports.AES_VERSION_AE1 = exports.AES_VENDOR_ID = exports.zipCryptoEncrypt = exports.zipCryptoDecrypt = exports.zipCryptoInitKeys = exports.ZIP_CRYPTO_HEADER_SIZE = exports.uint8ArrayToString = exports.stringToUint8Array =
|
|
10
|
+
exports.concatUint8Arrays = exports.uint8ArrayToBase64 = exports.base64ToUint8Array = exports.isTarDataEntry = exports.isTarHardLink = exports.isTarSymlink = exports.isTarDirectory = exports.isTarFile = exports.untar = exports.parseTarStream = exports.parseTar = exports.tarSync = exports.tar = exports.createTarReader = exports.createTarArchive = exports.TarReaderEntry = exports.TarReader = exports.TarArchive = exports.TAR_TYPE = exports.TAR_BLOCK_SIZE = exports.UnzipEntry = exports.ZipReader = exports.ZipEditPlan = exports.editZipUrl = exports.editZip = exports.ZipEditor = exports.ZipArchive = exports.unzip = exports.zip = exports.EntrySizeMismatchError = exports.UnsupportedCompressionError = exports.FileTooLargeError = exports.PasswordRequiredError = exports.DecryptionError = exports.EocdNotFoundError = exports.InvalidZipSignatureError = exports.ZipParseError = exports.ArchiveError = exports.throwIfAborted = exports.isAbortError = exports.createAbortError = exports.AbortError = exports.Crc32MismatchError = exports.RemoteZipReader = exports.HttpRangeError = exports.RangeNotSupportedError = exports.BufferReader = exports.HttpRangeReader = exports.toReadableStream = exports.toAsyncIterable = void 0;
|
|
11
|
+
exports.encryptionMethodFromAesKeyStrength = exports.getAesKeyStrength = exports.isAesEncryption = exports.getEncryptionMethodName = exports.randomBytes = exports.buildAesExtraField = exports.aesEncryptedSize = exports.aesEncrypt = exports.aesDecrypt = exports.COMPRESSION_METHOD_AES = exports.AES_PASSWORD_VERIFY_LENGTH = exports.AES_AUTH_CODE_LENGTH = exports.AES_KEY_LENGTH = exports.AES_SALT_LENGTH = exports.AES_EXTRA_FIELD_ID = exports.AES_VERSION_AE2 = exports.AES_VERSION_AE1 = exports.AES_VENDOR_ID = exports.zipCryptoEncrypt = exports.zipCryptoDecrypt = exports.zipCryptoInitKeys = exports.ZIP_CRYPTO_HEADER_SIZE = exports.uint8ArrayToString = exports.stringToUint8Array = void 0;
|
|
12
12
|
var archive_source_1 = require("./io/archive-source.js");
|
|
13
13
|
Object.defineProperty(exports, "toAsyncIterable", { enumerable: true, get: function () { return archive_source_1.toAsyncIterable; } });
|
|
14
14
|
Object.defineProperty(exports, "toReadableStream", { enumerable: true, get: function () { return archive_source_1.toReadableStream; } });
|
|
@@ -18,7 +18,7 @@ Object.defineProperty(exports, "HttpRangeReader", { enumerable: true, get: funct
|
|
|
18
18
|
Object.defineProperty(exports, "BufferReader", { enumerable: true, get: function () { return random_access_1.BufferReader; } });
|
|
19
19
|
Object.defineProperty(exports, "RangeNotSupportedError", { enumerable: true, get: function () { return random_access_1.RangeNotSupportedError; } });
|
|
20
20
|
Object.defineProperty(exports, "HttpRangeError", { enumerable: true, get: function () { return random_access_1.HttpRangeError; } });
|
|
21
|
-
var remote_zip_reader_1 = require("./
|
|
21
|
+
var remote_zip_reader_1 = require("./unzip/remote-zip-reader.js");
|
|
22
22
|
Object.defineProperty(exports, "RemoteZipReader", { enumerable: true, get: function () { return remote_zip_reader_1.RemoteZipReader; } });
|
|
23
23
|
Object.defineProperty(exports, "Crc32MismatchError", { enumerable: true, get: function () { return remote_zip_reader_1.Crc32MismatchError; } });
|
|
24
24
|
// Abort and Error types - all from centralized errors module
|
|
@@ -39,21 +39,19 @@ Object.defineProperty(exports, "FileTooLargeError", { enumerable: true, get: fun
|
|
|
39
39
|
Object.defineProperty(exports, "UnsupportedCompressionError", { enumerable: true, get: function () { return errors_1.UnsupportedCompressionError; } });
|
|
40
40
|
Object.defineProperty(exports, "EntrySizeMismatchError", { enumerable: true, get: function () { return errors_1.EntrySizeMismatchError; } });
|
|
41
41
|
// High-level APIs
|
|
42
|
+
var create_archive_1 = require("./create-archive.js");
|
|
43
|
+
Object.defineProperty(exports, "zip", { enumerable: true, get: function () { return create_archive_1.zip; } });
|
|
44
|
+
var read_archive_1 = require("./read-archive.js");
|
|
45
|
+
Object.defineProperty(exports, "unzip", { enumerable: true, get: function () { return read_archive_1.unzip; } });
|
|
42
46
|
var zip_1 = require("./zip/index.js");
|
|
43
|
-
Object.defineProperty(exports, "zip", { enumerable: true, get: function () { return zip_1.zip; } });
|
|
44
47
|
Object.defineProperty(exports, "ZipArchive", { enumerable: true, get: function () { return zip_1.ZipArchive; } });
|
|
45
48
|
Object.defineProperty(exports, "ZipEditor", { enumerable: true, get: function () { return zip_1.ZipEditor; } });
|
|
46
49
|
Object.defineProperty(exports, "editZip", { enumerable: true, get: function () { return zip_1.editZip; } });
|
|
47
50
|
Object.defineProperty(exports, "editZipUrl", { enumerable: true, get: function () { return zip_1.editZipUrl; } });
|
|
48
51
|
Object.defineProperty(exports, "ZipEditPlan", { enumerable: true, get: function () { return zip_1.ZipEditPlan; } });
|
|
49
52
|
var unzip_1 = require("./unzip/index.js");
|
|
50
|
-
Object.defineProperty(exports, "unzip", { enumerable: true, get: function () { return unzip_1.unzip; } });
|
|
51
53
|
Object.defineProperty(exports, "ZipReader", { enumerable: true, get: function () { return unzip_1.ZipReader; } });
|
|
52
54
|
Object.defineProperty(exports, "UnzipEntry", { enumerable: true, get: function () { return unzip_1.UnzipEntry; } });
|
|
53
|
-
// Format registry (ZIP/TAR dispatch)
|
|
54
|
-
var formats_1 = require("./formats/index.js");
|
|
55
|
-
Object.defineProperty(exports, "createArchive", { enumerable: true, get: function () { return formats_1.createArchive; } });
|
|
56
|
-
Object.defineProperty(exports, "createReader", { enumerable: true, get: function () { return formats_1.createReader; } });
|
|
57
55
|
// TAR archive support (unified API compatible with ZIP)
|
|
58
56
|
// Note: Gzip helpers are exported separately in index.ts / index.browser.ts
|
|
59
57
|
var index_browser_1 = require("./tar/index.browser.js");
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.unzip = unzip;
|
|
4
|
+
const tar_archive_1 = require("./tar/tar-archive.js");
|
|
5
|
+
const zip_reader_1 = require("./unzip/zip-reader.js");
|
|
6
|
+
function unzip(source, options = {}) {
|
|
7
|
+
if (options.format === "tar") {
|
|
8
|
+
return new tar_archive_1.TarReader(source, {
|
|
9
|
+
signal: options.signal,
|
|
10
|
+
onProgress: options.onProgress,
|
|
11
|
+
progressIntervalMs: options.progressIntervalMs
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
return new zip_reader_1.ZipReader(source, { ...options, format: "zip" });
|
|
15
|
+
}
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ZipReader = exports.UnzipEntry = void 0;
|
|
4
|
-
exports.unzip = unzip;
|
|
5
|
-
const formats_1 = require("../formats/index.js");
|
|
6
4
|
var zip_reader_1 = require("./zip-reader");
|
|
7
5
|
Object.defineProperty(exports, "UnzipEntry", { enumerable: true, get: function () { return zip_reader_1.UnzipEntry; } });
|
|
8
6
|
Object.defineProperty(exports, "ZipReader", { enumerable: true, get: function () { return zip_reader_1.ZipReader; } });
|
|
9
|
-
function unzip(source, options) {
|
|
10
|
-
return (0, formats_1.createReader)(source, options);
|
|
11
|
-
}
|