@cj-tech-master/excelts 4.2.0-canary.20260110111632.c88c61c → 4.2.1-canary.20260111102127.f808a37
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/THIRD_PARTY_NOTICES.md +0 -31
- package/dist/browser/index.browser.d.ts +1 -0
- package/dist/browser/index.browser.js +12 -0
- package/dist/{esm/modules/archive → browser/modules/archive/compression}/compress.base.js +1 -1
- package/dist/{types/modules/archive → browser/modules/archive/compression}/compress.browser.d.ts +2 -8
- package/dist/browser/modules/archive/{compress.browser.js → compression/compress.browser.js} +3 -11
- package/dist/browser/modules/archive/{compress.d.ts → compression/compress.d.ts} +2 -2
- package/dist/{esm/modules/archive → browser/modules/archive/compression}/compress.js +1 -1
- package/dist/browser/modules/archive/{crc32.browser.d.ts → compression/crc32.browser.d.ts} +1 -1
- package/dist/browser/modules/archive/{crc32.d.ts → compression/crc32.d.ts} +1 -1
- package/dist/browser/modules/archive/{crc32.js → compression/crc32.js} +1 -1
- package/dist/browser/modules/archive/{deflate-fallback.js → compression/deflate-fallback.js} +1 -1
- package/dist/browser/modules/archive/{streaming-compress.browser.d.ts → compression/streaming-compress.browser.d.ts} +2 -2
- package/dist/browser/modules/archive/{streaming-compress.browser.js → compression/streaming-compress.browser.js} +3 -3
- package/dist/browser/modules/archive/{streaming-compress.d.ts → compression/streaming-compress.d.ts} +2 -2
- package/dist/browser/modules/archive/{streaming-compress.js → compression/streaming-compress.js} +2 -2
- package/dist/browser/modules/archive/defaults.d.ts +1 -0
- package/dist/browser/modules/archive/defaults.js +6 -3
- package/dist/browser/modules/archive/index.base.d.ts +4 -4
- package/dist/browser/modules/archive/index.base.js +3 -6
- package/dist/browser/modules/archive/index.browser.d.ts +3 -4
- package/dist/browser/modules/archive/index.browser.js +3 -7
- package/dist/browser/modules/archive/index.d.ts +3 -4
- package/dist/browser/modules/archive/index.js +3 -5
- package/dist/browser/modules/archive/internal/byte-queue.d.ts +33 -0
- package/dist/browser/modules/archive/internal/byte-queue.js +407 -0
- package/dist/browser/modules/archive/io/archive-sink.d.ts +9 -0
- package/dist/browser/modules/archive/io/archive-sink.js +77 -0
- package/dist/browser/modules/archive/io/archive-source.d.ts +8 -0
- package/dist/browser/modules/archive/io/archive-source.js +107 -0
- package/dist/browser/modules/archive/{extract.d.ts → unzip/extract.d.ts} +2 -2
- package/dist/browser/modules/archive/unzip/index.d.ts +40 -0
- package/dist/browser/modules/archive/unzip/index.js +164 -0
- package/dist/browser/modules/archive/{parse.base.d.ts → unzip/stream.base.d.ts} +36 -2
- package/dist/browser/modules/archive/unzip/stream.base.js +1022 -0
- package/dist/browser/modules/archive/{parse.browser.d.ts → unzip/stream.browser.d.ts} +1 -1
- package/dist/browser/modules/archive/{parse.browser.js → unzip/stream.browser.js} +371 -110
- package/dist/browser/modules/archive/{parse.d.ts → unzip/stream.d.ts} +2 -2
- package/dist/{esm/modules/archive/parse.js → browser/modules/archive/unzip/stream.js} +6 -5
- package/dist/browser/modules/archive/{zip-parser.d.ts → unzip/zip-parser.d.ts} +1 -1
- package/dist/{esm/modules/archive → browser/modules/archive/unzip}/zip-parser.js +38 -24
- package/dist/browser/modules/archive/utils/async-queue.d.ts +7 -0
- package/dist/browser/modules/archive/utils/async-queue.js +103 -0
- package/dist/browser/modules/archive/utils/bytes.js +16 -16
- package/dist/browser/modules/archive/utils/compressibility.d.ts +10 -0
- package/dist/browser/modules/archive/utils/compressibility.js +57 -0
- package/dist/browser/modules/archive/utils/parse-buffer.js +21 -23
- package/dist/browser/modules/archive/utils/pattern-scanner.d.ts +21 -0
- package/dist/browser/modules/archive/utils/pattern-scanner.js +27 -0
- package/dist/browser/modules/archive/utils/timestamps.js +62 -1
- package/dist/browser/modules/archive/utils/zip-extra-fields.d.ts +1 -1
- package/dist/browser/modules/archive/utils/zip-extra-fields.js +26 -14
- package/dist/browser/modules/archive/zip/index.d.ts +42 -0
- package/dist/browser/modules/archive/zip/index.js +157 -0
- package/dist/browser/modules/archive/{streaming-zip.d.ts → zip/stream.d.ts} +28 -5
- package/dist/browser/modules/archive/{streaming-zip.js → zip/stream.js} +192 -48
- package/dist/browser/modules/archive/zip/zip-bytes.d.ts +73 -0
- package/dist/browser/modules/archive/zip/zip-bytes.js +239 -0
- package/dist/{esm/modules/archive → browser/modules/archive/zip}/zip-entry-metadata.js +3 -3
- package/dist/browser/modules/archive/{zip-records.d.ts → zip-spec/zip-records.d.ts} +20 -0
- package/dist/browser/modules/archive/zip-spec/zip-records.js +126 -0
- package/dist/browser/modules/excel/stream/workbook-reader.browser.js +1 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +1 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +1 -1
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +3 -6
- package/dist/browser/modules/excel/xlsx/xlsx.js +1 -1
- package/dist/browser/modules/stream/streams.browser.d.ts +28 -30
- package/dist/browser/modules/stream/streams.browser.js +830 -710
- package/dist/browser/modules/stream/streams.js +140 -58
- package/dist/cjs/modules/archive/{compress.base.js → compression/compress.base.js} +1 -1
- package/dist/cjs/modules/archive/{compress.browser.js → compression/compress.browser.js} +3 -11
- package/dist/cjs/modules/archive/{compress.js → compression/compress.js} +1 -1
- package/dist/cjs/modules/archive/{crc32.js → compression/crc32.js} +1 -1
- package/dist/cjs/modules/archive/{deflate-fallback.js → compression/deflate-fallback.js} +1 -1
- package/dist/cjs/modules/archive/{streaming-compress.browser.js → compression/streaming-compress.browser.js} +3 -3
- package/dist/cjs/modules/archive/{streaming-compress.js → compression/streaming-compress.js} +2 -2
- package/dist/cjs/modules/archive/defaults.js +7 -4
- package/dist/cjs/modules/archive/index.base.js +9 -19
- package/dist/cjs/modules/archive/index.browser.js +4 -10
- package/dist/cjs/modules/archive/index.js +4 -8
- package/dist/cjs/modules/archive/internal/byte-queue.js +411 -0
- package/dist/cjs/modules/archive/io/archive-sink.js +82 -0
- package/dist/cjs/modules/archive/io/archive-source.js +114 -0
- package/dist/cjs/modules/archive/unzip/index.js +170 -0
- package/dist/cjs/modules/archive/unzip/stream.base.js +1044 -0
- package/dist/cjs/modules/archive/{parse.browser.js → unzip/stream.browser.js} +372 -111
- package/dist/cjs/modules/archive/{parse.js → unzip/stream.js} +9 -8
- package/dist/cjs/modules/archive/{zip-parser.js → unzip/zip-parser.js} +47 -33
- package/dist/cjs/modules/archive/utils/async-queue.js +106 -0
- package/dist/cjs/modules/archive/utils/bytes.js +16 -16
- package/dist/cjs/modules/archive/utils/compressibility.js +60 -0
- package/dist/cjs/modules/archive/utils/parse-buffer.js +21 -23
- package/dist/cjs/modules/archive/utils/pattern-scanner.js +31 -0
- package/dist/cjs/modules/archive/utils/timestamps.js +64 -3
- package/dist/cjs/modules/archive/utils/zip-extra-fields.js +26 -14
- package/dist/cjs/modules/archive/zip/index.js +162 -0
- package/dist/cjs/modules/archive/{streaming-zip.js → zip/stream.js} +194 -50
- package/dist/cjs/modules/archive/zip/zip-bytes.js +242 -0
- package/dist/cjs/modules/archive/{zip-entry-metadata.js → zip/zip-entry-metadata.js} +5 -5
- package/dist/cjs/modules/archive/zip-spec/zip-records.js +136 -0
- package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +2 -2
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +4 -4
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +6 -9
- package/dist/cjs/modules/excel/xlsx/xlsx.js +2 -2
- package/dist/cjs/modules/stream/streams.browser.js +830 -710
- package/dist/cjs/modules/stream/streams.js +140 -58
- package/dist/esm/index.browser.js +12 -0
- package/dist/{browser/modules/archive → esm/modules/archive/compression}/compress.base.js +1 -1
- package/dist/esm/modules/archive/{compress.browser.js → compression/compress.browser.js} +3 -11
- package/dist/{browser/modules/archive → esm/modules/archive/compression}/compress.js +1 -1
- package/dist/esm/modules/archive/{crc32.js → compression/crc32.js} +1 -1
- package/dist/esm/modules/archive/{deflate-fallback.js → compression/deflate-fallback.js} +1 -1
- package/dist/esm/modules/archive/{streaming-compress.browser.js → compression/streaming-compress.browser.js} +3 -3
- package/dist/esm/modules/archive/{streaming-compress.js → compression/streaming-compress.js} +2 -2
- package/dist/esm/modules/archive/defaults.js +6 -3
- package/dist/esm/modules/archive/index.base.js +3 -6
- package/dist/esm/modules/archive/index.browser.js +3 -7
- package/dist/esm/modules/archive/index.js +3 -5
- package/dist/esm/modules/archive/internal/byte-queue.js +407 -0
- package/dist/esm/modules/archive/io/archive-sink.js +77 -0
- package/dist/esm/modules/archive/io/archive-source.js +107 -0
- package/dist/esm/modules/archive/unzip/index.js +164 -0
- package/dist/esm/modules/archive/unzip/stream.base.js +1022 -0
- package/dist/esm/modules/archive/{parse.browser.js → unzip/stream.browser.js} +371 -110
- package/dist/{browser/modules/archive/parse.js → esm/modules/archive/unzip/stream.js} +6 -5
- package/dist/{browser/modules/archive → esm/modules/archive/unzip}/zip-parser.js +38 -24
- package/dist/esm/modules/archive/utils/async-queue.js +103 -0
- package/dist/esm/modules/archive/utils/bytes.js +16 -16
- package/dist/esm/modules/archive/utils/compressibility.js +57 -0
- package/dist/esm/modules/archive/utils/parse-buffer.js +21 -23
- package/dist/esm/modules/archive/utils/pattern-scanner.js +27 -0
- package/dist/esm/modules/archive/utils/timestamps.js +62 -1
- package/dist/esm/modules/archive/utils/zip-extra-fields.js +26 -14
- package/dist/esm/modules/archive/zip/index.js +157 -0
- package/dist/esm/modules/archive/{streaming-zip.js → zip/stream.js} +192 -48
- package/dist/esm/modules/archive/zip/zip-bytes.js +239 -0
- package/dist/{browser/modules/archive → esm/modules/archive/zip}/zip-entry-metadata.js +3 -3
- package/dist/esm/modules/archive/zip-spec/zip-records.js +126 -0
- package/dist/esm/modules/excel/stream/workbook-reader.browser.js +1 -1
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +1 -1
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +3 -6
- package/dist/esm/modules/excel/xlsx/xlsx.js +1 -1
- package/dist/esm/modules/stream/streams.browser.js +830 -710
- package/dist/esm/modules/stream/streams.js +140 -58
- package/dist/iife/THIRD_PARTY_NOTICES.md +0 -31
- package/dist/iife/excelts.iife.js +6190 -4400
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +103 -31
- package/dist/types/index.browser.d.ts +1 -0
- package/dist/{browser/modules/archive → types/modules/archive/compression}/compress.browser.d.ts +2 -8
- package/dist/types/modules/archive/{streaming-compress.browser.d.ts → compression/streaming-compress.browser.d.ts} +1 -1
- package/dist/types/modules/archive/defaults.d.ts +1 -0
- package/dist/types/modules/archive/index.base.d.ts +4 -4
- package/dist/types/modules/archive/index.browser.d.ts +3 -4
- package/dist/types/modules/archive/index.d.ts +3 -4
- package/dist/types/modules/archive/internal/byte-queue.d.ts +33 -0
- package/dist/types/modules/archive/io/archive-sink.d.ts +9 -0
- package/dist/types/modules/archive/io/archive-source.d.ts +8 -0
- package/dist/types/modules/archive/unzip/index.d.ts +40 -0
- package/dist/types/modules/archive/{parse.base.d.ts → unzip/stream.base.d.ts} +38 -4
- package/dist/types/modules/archive/{parse.browser.d.ts → unzip/stream.browser.d.ts} +2 -2
- package/dist/types/modules/archive/{parse.d.ts → unzip/stream.d.ts} +3 -3
- package/dist/types/modules/archive/{zip-parser.d.ts → unzip/zip-parser.d.ts} +1 -1
- package/dist/types/modules/archive/utils/async-queue.d.ts +7 -0
- package/dist/types/modules/archive/utils/compressibility.d.ts +10 -0
- package/dist/types/modules/archive/utils/pattern-scanner.d.ts +21 -0
- package/dist/types/modules/archive/utils/zip-extra-fields.d.ts +1 -1
- package/dist/types/modules/archive/zip/index.d.ts +42 -0
- package/dist/types/modules/archive/{streaming-zip.d.ts → zip/stream.d.ts} +29 -6
- package/dist/types/modules/archive/zip/zip-bytes.d.ts +73 -0
- package/dist/types/modules/archive/{zip-entry-metadata.d.ts → zip/zip-entry-metadata.d.ts} +1 -1
- package/dist/types/modules/archive/{zip-records.d.ts → zip-spec/zip-records.d.ts} +20 -0
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +1 -1
- package/dist/types/modules/stream/streams.browser.d.ts +28 -30
- package/package.json +5 -1
- package/dist/browser/modules/archive/byte-queue.d.ts +0 -18
- package/dist/browser/modules/archive/byte-queue.js +0 -125
- package/dist/browser/modules/archive/parse.base.js +0 -644
- package/dist/browser/modules/archive/utils/zip-extra.d.ts +0 -18
- package/dist/browser/modules/archive/utils/zip-extra.js +0 -68
- package/dist/browser/modules/archive/zip-builder.d.ts +0 -117
- package/dist/browser/modules/archive/zip-builder.js +0 -292
- package/dist/browser/modules/archive/zip-constants.d.ts +0 -18
- package/dist/browser/modules/archive/zip-constants.js +0 -23
- package/dist/browser/modules/archive/zip-records.js +0 -84
- package/dist/cjs/modules/archive/byte-queue.js +0 -129
- package/dist/cjs/modules/archive/parse.base.js +0 -666
- package/dist/cjs/modules/archive/utils/zip-extra.js +0 -74
- package/dist/cjs/modules/archive/zip-builder.js +0 -297
- package/dist/cjs/modules/archive/zip-constants.js +0 -26
- package/dist/cjs/modules/archive/zip-records.js +0 -90
- package/dist/esm/modules/archive/byte-queue.js +0 -125
- package/dist/esm/modules/archive/parse.base.js +0 -644
- package/dist/esm/modules/archive/utils/zip-extra.js +0 -68
- package/dist/esm/modules/archive/zip-builder.js +0 -292
- package/dist/esm/modules/archive/zip-constants.js +0 -23
- package/dist/esm/modules/archive/zip-records.js +0 -84
- package/dist/types/modules/archive/byte-queue.d.ts +0 -18
- package/dist/types/modules/archive/utils/zip-extra.d.ts +0 -18
- package/dist/types/modules/archive/zip-builder.d.ts +0 -117
- package/dist/types/modules/archive/zip-constants.d.ts +0 -18
- /package/dist/browser/modules/archive/{compress.base.d.ts → compression/compress.base.d.ts} +0 -0
- /package/dist/browser/modules/archive/{crc32.base.d.ts → compression/crc32.base.d.ts} +0 -0
- /package/dist/browser/modules/archive/{crc32.base.js → compression/crc32.base.js} +0 -0
- /package/dist/browser/modules/archive/{crc32.browser.js → compression/crc32.browser.js} +0 -0
- /package/dist/browser/modules/archive/{deflate-fallback.d.ts → compression/deflate-fallback.d.ts} +0 -0
- /package/dist/browser/modules/archive/{streaming-compress.base.d.ts → compression/streaming-compress.base.d.ts} +0 -0
- /package/dist/browser/modules/archive/{streaming-compress.base.js → compression/streaming-compress.base.js} +0 -0
- /package/dist/browser/modules/archive/{extract.js → unzip/extract.js} +0 -0
- /package/dist/browser/modules/archive/{zip-entry-metadata.d.ts → zip/zip-entry-metadata.d.ts} +0 -0
- /package/dist/browser/modules/archive/{zip-entry-info.d.ts → zip-spec/zip-entry-info.d.ts} +0 -0
- /package/dist/browser/modules/archive/{zip-entry-info.js → zip-spec/zip-entry-info.js} +0 -0
- /package/dist/cjs/modules/archive/{crc32.base.js → compression/crc32.base.js} +0 -0
- /package/dist/cjs/modules/archive/{crc32.browser.js → compression/crc32.browser.js} +0 -0
- /package/dist/cjs/modules/archive/{streaming-compress.base.js → compression/streaming-compress.base.js} +0 -0
- /package/dist/cjs/modules/archive/{extract.js → unzip/extract.js} +0 -0
- /package/dist/cjs/modules/archive/{zip-entry-info.js → zip-spec/zip-entry-info.js} +0 -0
- /package/dist/esm/modules/archive/{crc32.base.js → compression/crc32.base.js} +0 -0
- /package/dist/esm/modules/archive/{crc32.browser.js → compression/crc32.browser.js} +0 -0
- /package/dist/esm/modules/archive/{streaming-compress.base.js → compression/streaming-compress.base.js} +0 -0
- /package/dist/esm/modules/archive/{extract.js → unzip/extract.js} +0 -0
- /package/dist/esm/modules/archive/{zip-entry-info.js → zip-spec/zip-entry-info.js} +0 -0
- /package/dist/types/modules/archive/{compress.base.d.ts → compression/compress.base.d.ts} +0 -0
- /package/dist/types/modules/archive/{compress.d.ts → compression/compress.d.ts} +0 -0
- /package/dist/types/modules/archive/{crc32.base.d.ts → compression/crc32.base.d.ts} +0 -0
- /package/dist/types/modules/archive/{crc32.browser.d.ts → compression/crc32.browser.d.ts} +0 -0
- /package/dist/types/modules/archive/{crc32.d.ts → compression/crc32.d.ts} +0 -0
- /package/dist/types/modules/archive/{deflate-fallback.d.ts → compression/deflate-fallback.d.ts} +0 -0
- /package/dist/types/modules/archive/{streaming-compress.base.d.ts → compression/streaming-compress.base.d.ts} +0 -0
- /package/dist/types/modules/archive/{streaming-compress.d.ts → compression/streaming-compress.d.ts} +0 -0
- /package/dist/types/modules/archive/{extract.d.ts → unzip/extract.d.ts} +0 -0
- /package/dist/types/modules/archive/{zip-entry-info.d.ts → zip-spec/zip-entry-info.d.ts} +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ZipTimestampMode } from "@archive/utils/timestamps";
|
|
2
|
+
import { type ArchiveSink } from "@archive/io/archive-sink";
|
|
3
|
+
import { type ArchiveSource } from "@archive/io/archive-source";
|
|
4
|
+
export interface ZipOptions {
|
|
5
|
+
level?: number;
|
|
6
|
+
timestamps?: ZipTimestampMode;
|
|
7
|
+
comment?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Default modification time for entries that don't specify `modTime`.
|
|
10
|
+
*
|
|
11
|
+
* If you need stable output across runs, either pass this explicitly or use `reproducible: true`.
|
|
12
|
+
*/
|
|
13
|
+
modTime?: Date;
|
|
14
|
+
/**
|
|
15
|
+
* If true, bias defaults toward reproducible output:
|
|
16
|
+
* - default `modTime` becomes 1980-01-01 00:00:00 (local time)
|
|
17
|
+
* - default `timestamps` becomes "dos" (no UTC extra field)
|
|
18
|
+
*/
|
|
19
|
+
reproducible?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* If true (default), automatically STORE incompressible data.
|
|
22
|
+
* If false, always follow `level` (DEFLATE when level > 0).
|
|
23
|
+
*/
|
|
24
|
+
smartStore?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface ZipEntryOptions {
|
|
27
|
+
level?: number;
|
|
28
|
+
modTime?: Date;
|
|
29
|
+
comment?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare class ZipArchive {
|
|
32
|
+
private readonly _options;
|
|
33
|
+
private readonly _entries;
|
|
34
|
+
private _sealed;
|
|
35
|
+
constructor(options?: ZipOptions);
|
|
36
|
+
add(name: string, source: ArchiveSource, options?: ZipEntryOptions): this;
|
|
37
|
+
stream(): AsyncIterable<Uint8Array>;
|
|
38
|
+
bytes(): Promise<Uint8Array>;
|
|
39
|
+
bytesSync(): Uint8Array;
|
|
40
|
+
pipeTo(sink: ArchiveSink): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export declare function zip(options?: ZipOptions): ZipArchive;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { DEFAULT_ZIP_LEVEL, DEFAULT_ZIP_TIMESTAMPS } from "../defaults.js";
|
|
2
|
+
import { ZipDeflateFile, StreamingZip } from "./stream.js";
|
|
3
|
+
import { createZip, createZipSync } from "./zip-bytes.js";
|
|
4
|
+
import { collect, pipeIterableToSink } from "../io/archive-sink.js";
|
|
5
|
+
import { toAsyncIterable, toUint8Array, toUint8ArraySync } from "../io/archive-source.js";
|
|
6
|
+
import { createAsyncQueue } from "../utils/async-queue.js";
|
|
7
|
+
const REPRODUCIBLE_ZIP_MOD_TIME = new Date(1980, 0, 1, 0, 0, 0);
|
|
8
|
+
export class ZipArchive {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this._entries = [];
|
|
11
|
+
this._sealed = false;
|
|
12
|
+
const reproducible = options.reproducible ?? false;
|
|
13
|
+
this._options = {
|
|
14
|
+
level: options.level ?? DEFAULT_ZIP_LEVEL,
|
|
15
|
+
timestamps: options.timestamps ?? (reproducible ? "dos" : DEFAULT_ZIP_TIMESTAMPS),
|
|
16
|
+
comment: options.comment,
|
|
17
|
+
modTime: options.modTime ?? (reproducible ? REPRODUCIBLE_ZIP_MOD_TIME : new Date()),
|
|
18
|
+
smartStore: options.smartStore ?? true
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
add(name, source, options) {
|
|
22
|
+
if (this._sealed) {
|
|
23
|
+
throw new Error("Cannot add entries after output has started");
|
|
24
|
+
}
|
|
25
|
+
if (!name) {
|
|
26
|
+
throw new Error("Entry name is required");
|
|
27
|
+
}
|
|
28
|
+
this._entries.push({ name, source, options });
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
stream() {
|
|
32
|
+
this._sealed = true;
|
|
33
|
+
const queue = createAsyncQueue();
|
|
34
|
+
const zip = new StreamingZip((err, data, final) => {
|
|
35
|
+
if (err) {
|
|
36
|
+
queue.fail(err);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (data.length) {
|
|
40
|
+
queue.push(data);
|
|
41
|
+
}
|
|
42
|
+
if (final) {
|
|
43
|
+
queue.close();
|
|
44
|
+
}
|
|
45
|
+
}, { comment: this._options.comment });
|
|
46
|
+
(async () => {
|
|
47
|
+
try {
|
|
48
|
+
for (const entry of this._entries) {
|
|
49
|
+
const level = entry.options?.level ?? this._options.level;
|
|
50
|
+
const file = new ZipDeflateFile(entry.name, {
|
|
51
|
+
level,
|
|
52
|
+
modTime: entry.options?.modTime ?? this._options.modTime,
|
|
53
|
+
timestamps: this._options.timestamps,
|
|
54
|
+
comment: entry.options?.comment,
|
|
55
|
+
smartStore: this._options.smartStore
|
|
56
|
+
});
|
|
57
|
+
zip.add(file);
|
|
58
|
+
// Feed data
|
|
59
|
+
if (entry.source instanceof Uint8Array ||
|
|
60
|
+
entry.source instanceof ArrayBuffer ||
|
|
61
|
+
typeof entry.source === "string" ||
|
|
62
|
+
(typeof Blob !== "undefined" && entry.source instanceof Blob)) {
|
|
63
|
+
const bytes = await toUint8Array(entry.source);
|
|
64
|
+
await file.push(bytes, true);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
for await (const chunk of toAsyncIterable(entry.source)) {
|
|
68
|
+
await file.push(chunk, false);
|
|
69
|
+
}
|
|
70
|
+
await file.push(new Uint8Array(0), true);
|
|
71
|
+
}
|
|
72
|
+
await file.complete();
|
|
73
|
+
}
|
|
74
|
+
zip.end();
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
queue.fail(e instanceof Error ? e : new Error(String(e)));
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
return queue.iterable;
|
|
81
|
+
}
|
|
82
|
+
async bytes() {
|
|
83
|
+
this._sealed = true;
|
|
84
|
+
const allSourcesInMemory = this._entries.every(e => e.source instanceof Uint8Array ||
|
|
85
|
+
e.source instanceof ArrayBuffer ||
|
|
86
|
+
typeof e.source === "string" ||
|
|
87
|
+
(typeof Blob !== "undefined" && e.source instanceof Blob));
|
|
88
|
+
const hasBlobSource = this._entries.some(e => typeof Blob !== "undefined" && e.source instanceof Blob);
|
|
89
|
+
// Fast-path: when all sources are already in memory and there are no
|
|
90
|
+
// per-entry compression overrides, use the single-buffer ZIP builder.
|
|
91
|
+
// This avoids the overhead of chunking + collecting from the streaming writer.
|
|
92
|
+
if (allSourcesInMemory) {
|
|
93
|
+
// Prefer the sync builder when possible (Node.js hot path): it avoids
|
|
94
|
+
// async/Promise overhead and uses zlib sync fast paths.
|
|
95
|
+
if (!hasBlobSource) {
|
|
96
|
+
const entries = this._entries.map(e => ({
|
|
97
|
+
name: e.name,
|
|
98
|
+
data: toUint8ArraySync(e.source),
|
|
99
|
+
level: e.options?.level,
|
|
100
|
+
modTime: e.options?.modTime,
|
|
101
|
+
comment: e.options?.comment
|
|
102
|
+
}));
|
|
103
|
+
return createZipSync(entries, {
|
|
104
|
+
level: this._options.level,
|
|
105
|
+
timestamps: this._options.timestamps,
|
|
106
|
+
modTime: this._options.modTime,
|
|
107
|
+
comment: this._options.comment,
|
|
108
|
+
smartStore: this._options.smartStore
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
const entries = await Promise.all(this._entries.map(async (e) => ({
|
|
112
|
+
name: e.name,
|
|
113
|
+
data: await toUint8Array(e.source),
|
|
114
|
+
level: e.options?.level,
|
|
115
|
+
modTime: e.options?.modTime,
|
|
116
|
+
comment: e.options?.comment
|
|
117
|
+
})));
|
|
118
|
+
return createZip(entries, {
|
|
119
|
+
level: this._options.level,
|
|
120
|
+
timestamps: this._options.timestamps,
|
|
121
|
+
modTime: this._options.modTime,
|
|
122
|
+
comment: this._options.comment,
|
|
123
|
+
smartStore: this._options.smartStore
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return collect(this.stream());
|
|
127
|
+
}
|
|
128
|
+
bytesSync() {
|
|
129
|
+
this._sealed = true;
|
|
130
|
+
const entries = this._entries.map(e => {
|
|
131
|
+
if (!(e.source instanceof Uint8Array) &&
|
|
132
|
+
!(e.source instanceof ArrayBuffer) &&
|
|
133
|
+
typeof e.source !== "string") {
|
|
134
|
+
throw new Error("bytesSync() only supports Uint8Array/ArrayBuffer/string sources");
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
name: e.name,
|
|
138
|
+
data: toUint8ArraySync(e.source),
|
|
139
|
+
modTime: e.options?.modTime,
|
|
140
|
+
comment: e.options?.comment
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
return createZipSync(entries, {
|
|
144
|
+
level: this._options.level,
|
|
145
|
+
timestamps: this._options.timestamps,
|
|
146
|
+
modTime: this._options.modTime,
|
|
147
|
+
comment: this._options.comment,
|
|
148
|
+
smartStore: this._options.smartStore
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
async pipeTo(sink) {
|
|
152
|
+
await pipeIterableToSink(this.stream(), sink);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
export function zip(options) {
|
|
156
|
+
return new ZipArchive(options);
|
|
157
|
+
}
|
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
* True Streaming ZIP creator - shared implementation.
|
|
3
3
|
*
|
|
4
4
|
* This module is intentionally platform-agnostic.
|
|
5
|
-
* - In Node builds it uses `./crc32` + `./streaming-compress` (zlib-backed).
|
|
5
|
+
* - In Node builds it uses `./compression/crc32` + `./compression/streaming-compress` (zlib-backed).
|
|
6
6
|
* - In browser builds the bundler aliases those imports to their browser variants.
|
|
7
7
|
*/
|
|
8
8
|
import type { ZipTimestampMode } from "@archive/utils/timestamps";
|
|
9
|
-
import type { ZipEntryInfo as UnzipZipEntryInfo } from "@archive/zip-entry-info";
|
|
9
|
+
import type { ZipEntryInfo as UnzipZipEntryInfo } from "@archive/zip-spec/zip-entry-info";
|
|
10
10
|
/**
|
|
11
11
|
* Internal entry info for central directory
|
|
12
12
|
*/
|
|
13
13
|
interface ZipEntryInfo {
|
|
14
14
|
name: Uint8Array;
|
|
15
15
|
extraField: Uint8Array;
|
|
16
|
+
comment: Uint8Array;
|
|
16
17
|
flags: number;
|
|
17
18
|
crc: number;
|
|
18
19
|
compressedSize: number;
|
|
@@ -34,43 +35,62 @@ export declare class ZipDeflateFile {
|
|
|
34
35
|
private _finalized;
|
|
35
36
|
private _headerEmitted;
|
|
36
37
|
private _ondata;
|
|
38
|
+
private _onerror;
|
|
37
39
|
private _centralDirEntryInfo;
|
|
38
40
|
private _pendingEnd;
|
|
39
41
|
private _emittedDataDescriptor;
|
|
40
42
|
private _localHeader;
|
|
43
|
+
private _deflateWanted;
|
|
44
|
+
private _pendingChunks;
|
|
45
|
+
private _sampleBuffer;
|
|
46
|
+
private _sampleLen;
|
|
47
|
+
private _smartStore;
|
|
41
48
|
private _completeResolve;
|
|
42
49
|
private _completeReject;
|
|
43
50
|
private _completePromise;
|
|
44
51
|
private _completeError;
|
|
45
52
|
private _dataQueue;
|
|
46
53
|
private _finalQueued;
|
|
54
|
+
private _pushChain;
|
|
47
55
|
readonly name: string;
|
|
48
56
|
readonly level: number;
|
|
49
57
|
readonly nameBytes: Uint8Array;
|
|
58
|
+
readonly commentBytes: Uint8Array;
|
|
50
59
|
readonly dosTime: number;
|
|
51
60
|
readonly dosDate: number;
|
|
52
61
|
readonly extraField: Uint8Array;
|
|
53
62
|
private readonly _flags;
|
|
54
|
-
private
|
|
63
|
+
private _compressionMethod;
|
|
55
64
|
private readonly _modTime;
|
|
56
65
|
constructor(name: string, options?: {
|
|
57
66
|
level?: number;
|
|
58
67
|
modTime?: Date;
|
|
59
68
|
timestamps?: ZipTimestampMode;
|
|
69
|
+
comment?: string;
|
|
70
|
+
smartStore?: boolean;
|
|
60
71
|
});
|
|
72
|
+
private _buildCompressionMethod;
|
|
73
|
+
private _initDeflateStream;
|
|
61
74
|
private _buildLocalHeader;
|
|
75
|
+
private _accumulateSample;
|
|
76
|
+
private _shouldDecide;
|
|
77
|
+
private _decideCompressionIfNeeded;
|
|
78
|
+
private _emitHeaderIfNeeded;
|
|
79
|
+
private _flushPendingChunks;
|
|
62
80
|
private _enqueueData;
|
|
63
81
|
private _flushQueue;
|
|
64
82
|
get ondata(): ((data: Uint8Array, final: boolean) => void) | undefined;
|
|
65
83
|
set ondata(cb: (data: Uint8Array, final: boolean) => void);
|
|
84
|
+
get onerror(): ((err: Error) => void) | undefined;
|
|
85
|
+
set onerror(cb: (err: Error) => void);
|
|
66
86
|
private _resolveComplete;
|
|
67
87
|
private _rejectComplete;
|
|
68
88
|
private _ensureCompletePromise;
|
|
69
89
|
private _tapCallback;
|
|
70
|
-
private _emitHeaderIfNeeded;
|
|
71
90
|
private _writeData;
|
|
72
91
|
private _endDeflateAndWait;
|
|
73
92
|
private _finalizeAfterWrite;
|
|
93
|
+
private _pushUnchained;
|
|
74
94
|
/**
|
|
75
95
|
* Push data - immediately compresses and outputs
|
|
76
96
|
* Returns a Promise that resolves when the write is complete.
|
|
@@ -108,10 +128,13 @@ export declare class StreamingZip {
|
|
|
108
128
|
private currentOffset;
|
|
109
129
|
private ended;
|
|
110
130
|
private endPending;
|
|
131
|
+
private zipComment;
|
|
111
132
|
private fileQueue;
|
|
112
133
|
private fileQueueIndex;
|
|
113
134
|
private activeFile;
|
|
114
|
-
constructor(callback: (err: Error | null, data: Uint8Array, final: boolean) => void
|
|
135
|
+
constructor(callback: (err: Error | null, data: Uint8Array, final: boolean) => void, options?: {
|
|
136
|
+
comment?: string;
|
|
137
|
+
});
|
|
115
138
|
add(file: ZipDeflateFile): void;
|
|
116
139
|
private _processNextFile;
|
|
117
140
|
private _finalize;
|
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
* True Streaming ZIP creator - shared implementation.
|
|
3
3
|
*
|
|
4
4
|
* This module is intentionally platform-agnostic.
|
|
5
|
-
* - In Node builds it uses `./crc32` + `./streaming-compress` (zlib-backed).
|
|
5
|
+
* - In Node builds it uses `./compression/crc32` + `./compression/streaming-compress` (zlib-backed).
|
|
6
6
|
* - In browser builds the bundler aliases those imports to their browser variants.
|
|
7
7
|
*/
|
|
8
|
-
import { crc32Update, crc32Finalize } from "
|
|
9
|
-
import { createDeflateStream } from "
|
|
10
|
-
import { DEFAULT_ZIP_LEVEL, DEFAULT_ZIP_TIMESTAMPS } from "
|
|
11
|
-
import { buildZipEntryMetadata } from "./zip-entry-metadata.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
8
|
+
import { crc32Update, crc32Finalize } from "../compression/crc32.browser.js";
|
|
9
|
+
import { createDeflateStream } from "../compression/streaming-compress.browser.js";
|
|
10
|
+
import { DEFAULT_ZIP_LEVEL, DEFAULT_ZIP_TIMESTAMPS } from "../defaults.js";
|
|
11
|
+
import { buildZipEntryMetadata, resolveZipCompressionMethod } from "./zip-entry-metadata.js";
|
|
12
|
+
import { decodeUtf8, encodeUtf8 } from "../utils/text.js";
|
|
13
|
+
import { isProbablyIncompressible } from "../utils/compressibility.js";
|
|
14
|
+
import { buildCentralDirectoryHeader, buildDataDescriptor, buildEndOfCentralDirectory, buildLocalFileHeader, VERSION_MADE_BY, VERSION_NEEDED } from "../zip-spec/zip-records.js";
|
|
14
15
|
/**
|
|
15
16
|
* True Streaming ZIP File - compresses chunk by chunk
|
|
16
17
|
*/
|
|
@@ -23,9 +24,15 @@ export class ZipDeflateFile {
|
|
|
23
24
|
this._finalized = false;
|
|
24
25
|
this._headerEmitted = false;
|
|
25
26
|
this._ondata = null;
|
|
27
|
+
this._onerror = null;
|
|
26
28
|
this._centralDirEntryInfo = null;
|
|
27
29
|
this._pendingEnd = false;
|
|
28
30
|
this._emittedDataDescriptor = false;
|
|
31
|
+
this._localHeader = null;
|
|
32
|
+
// Smart STORE: delay method selection until we sample data.
|
|
33
|
+
this._deflateWanted = null;
|
|
34
|
+
this._pendingChunks = [];
|
|
35
|
+
this._sampleLen = 0;
|
|
29
36
|
// Promise resolution for completion (including data descriptor)
|
|
30
37
|
this._completeResolve = null;
|
|
31
38
|
this._completeReject = null;
|
|
@@ -34,46 +41,70 @@ export class ZipDeflateFile {
|
|
|
34
41
|
// Queue for incoming data before ondata is set
|
|
35
42
|
this._dataQueue = [];
|
|
36
43
|
this._finalQueued = false;
|
|
44
|
+
// Serialize push() calls so callers don't need to await to preserve ordering.
|
|
45
|
+
this._pushChain = Promise.resolve();
|
|
37
46
|
this.name = name;
|
|
38
47
|
const modTime = options?.modTime ?? new Date();
|
|
39
48
|
this._modTime = modTime;
|
|
40
49
|
this.level = options?.level ?? DEFAULT_ZIP_LEVEL;
|
|
50
|
+
this._smartStore = options?.smartStore ?? true;
|
|
51
|
+
this._sampleBuffer = this._smartStore ? new Uint8Array(64 * 1024) : new Uint8Array(0);
|
|
41
52
|
const metadata = buildZipEntryMetadata({
|
|
42
53
|
name,
|
|
43
|
-
comment: "",
|
|
54
|
+
comment: options?.comment ?? "",
|
|
44
55
|
modTime,
|
|
45
56
|
timestamps: options?.timestamps ?? DEFAULT_ZIP_TIMESTAMPS,
|
|
46
57
|
useDataDescriptor: true,
|
|
47
|
-
deflate:
|
|
58
|
+
deflate: false
|
|
48
59
|
});
|
|
49
60
|
this.nameBytes = metadata.nameBytes;
|
|
61
|
+
this.commentBytes = metadata.commentBytes;
|
|
50
62
|
this.dosTime = metadata.dosTime;
|
|
51
63
|
this.dosDate = metadata.dosDate;
|
|
52
64
|
this.extraField = metadata.extraField;
|
|
53
65
|
this._flags = metadata.flags;
|
|
54
66
|
this._compressionMethod = metadata.compressionMethod;
|
|
55
|
-
//
|
|
56
|
-
if (this.
|
|
57
|
-
|
|
58
|
-
this.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this._enqueueData(chunk, false);
|
|
65
|
-
});
|
|
66
|
-
// Handle end - emit data descriptor
|
|
67
|
-
// IMPORTANT: Only use 'end' event, NOT 'finish'!
|
|
68
|
-
// Node.js zlib emits events in order: finish -> data -> end
|
|
69
|
-
this._deflate.on("end", () => {
|
|
70
|
-
if (this._pendingEnd && !this._emittedDataDescriptor) {
|
|
71
|
-
this._emittedDataDescriptor = true;
|
|
72
|
-
this._emitDataDescriptor();
|
|
73
|
-
}
|
|
74
|
-
});
|
|
67
|
+
// If smart store is disabled, decide method upfront and keep true streaming semantics.
|
|
68
|
+
if (!this._smartStore) {
|
|
69
|
+
const deflate = this.level > 0;
|
|
70
|
+
this._deflateWanted = deflate;
|
|
71
|
+
this._compressionMethod = this._buildCompressionMethod(deflate);
|
|
72
|
+
if (deflate) {
|
|
73
|
+
this._initDeflateStream();
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
75
76
|
}
|
|
76
|
-
|
|
77
|
+
// Level 0: always STORE.
|
|
78
|
+
if (this.level === 0) {
|
|
79
|
+
this._deflateWanted = false;
|
|
80
|
+
this._compressionMethod = this._buildCompressionMethod(false);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
_buildCompressionMethod(deflate) {
|
|
84
|
+
return resolveZipCompressionMethod(deflate);
|
|
85
|
+
}
|
|
86
|
+
_initDeflateStream() {
|
|
87
|
+
if (this._deflate) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this._deflate = createDeflateStream({ level: this.level });
|
|
91
|
+
this._deflate.on("error", (err) => {
|
|
92
|
+
this._rejectComplete(err);
|
|
93
|
+
});
|
|
94
|
+
// Handle compressed output - this is true streaming!
|
|
95
|
+
this._deflate.on("data", (chunk) => {
|
|
96
|
+
this._compressedSize += chunk.length;
|
|
97
|
+
this._enqueueData(chunk, false);
|
|
98
|
+
});
|
|
99
|
+
// Handle end - emit data descriptor
|
|
100
|
+
// IMPORTANT: Only use 'end' event, NOT 'finish'!
|
|
101
|
+
// Node.js zlib emits events in order: finish -> data -> end
|
|
102
|
+
this._deflate.on("end", () => {
|
|
103
|
+
if (this._pendingEnd && !this._emittedDataDescriptor) {
|
|
104
|
+
this._emittedDataDescriptor = true;
|
|
105
|
+
this._emitDataDescriptor();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
77
108
|
}
|
|
78
109
|
_buildLocalHeader() {
|
|
79
110
|
// CRC + sizes are written via data descriptor for true streaming.
|
|
@@ -90,6 +121,63 @@ export class ZipDeflateFile {
|
|
|
90
121
|
versionNeeded: VERSION_NEEDED
|
|
91
122
|
});
|
|
92
123
|
}
|
|
124
|
+
_accumulateSample(data) {
|
|
125
|
+
if (this._deflateWanted !== null) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (this._sampleLen >= this._sampleBuffer.length) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const take = Math.min(this._sampleBuffer.length - this._sampleLen, data.length);
|
|
132
|
+
if (take <= 0) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this._sampleBuffer.set(data.subarray(0, take), this._sampleLen);
|
|
136
|
+
this._sampleLen += take;
|
|
137
|
+
}
|
|
138
|
+
_shouldDecide(final) {
|
|
139
|
+
if (this._deflateWanted !== null) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
return final || this._sampleLen >= 16 * 1024;
|
|
143
|
+
}
|
|
144
|
+
_decideCompressionIfNeeded(final) {
|
|
145
|
+
if (this._deflateWanted !== null) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
// Match non-streaming builder semantics: empty files never need DEFLATE.
|
|
149
|
+
if (final && this._sampleLen === 0) {
|
|
150
|
+
this._deflateWanted = false;
|
|
151
|
+
this._compressionMethod = this._buildCompressionMethod(false);
|
|
152
|
+
this._localHeader = null;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Default to DEFLATE unless heuristic says STORE.
|
|
156
|
+
const sample = this._sampleBuffer.subarray(0, this._sampleLen);
|
|
157
|
+
const store = isProbablyIncompressible(sample);
|
|
158
|
+
this._deflateWanted = !store;
|
|
159
|
+
this._compressionMethod = this._buildCompressionMethod(this._deflateWanted);
|
|
160
|
+
this._localHeader = null;
|
|
161
|
+
if (this._deflateWanted) {
|
|
162
|
+
this._initDeflateStream();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
_emitHeaderIfNeeded() {
|
|
166
|
+
if (this._headerEmitted) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
this._emitHeader();
|
|
170
|
+
this._headerEmitted = true;
|
|
171
|
+
}
|
|
172
|
+
async _flushPendingChunks() {
|
|
173
|
+
if (this._pendingChunks.length === 0) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
for (const chunk of this._pendingChunks) {
|
|
177
|
+
await this._writeData(chunk);
|
|
178
|
+
}
|
|
179
|
+
this._pendingChunks = [];
|
|
180
|
+
}
|
|
93
181
|
_enqueueData(data, final) {
|
|
94
182
|
if (this._ondata) {
|
|
95
183
|
this._ondata(data, final);
|
|
@@ -121,13 +209,29 @@ export class ZipDeflateFile {
|
|
|
121
209
|
// Flush any queued data
|
|
122
210
|
this._flushQueue();
|
|
123
211
|
}
|
|
212
|
+
get onerror() {
|
|
213
|
+
return this._onerror ?? undefined;
|
|
214
|
+
}
|
|
215
|
+
set onerror(cb) {
|
|
216
|
+
this._onerror = cb;
|
|
217
|
+
// If an error already occurred, surface it immediately.
|
|
218
|
+
if (this._completeError) {
|
|
219
|
+
cb(this._completeError);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
124
222
|
_resolveComplete() {
|
|
125
223
|
if (this._completeResolve) {
|
|
126
224
|
this._completeResolve();
|
|
127
225
|
}
|
|
128
226
|
}
|
|
129
227
|
_rejectComplete(err) {
|
|
228
|
+
if (this._completeError) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
130
231
|
this._completeError = err;
|
|
232
|
+
if (this._onerror) {
|
|
233
|
+
this._onerror(err);
|
|
234
|
+
}
|
|
131
235
|
if (this._completeReject) {
|
|
132
236
|
this._completeReject(err);
|
|
133
237
|
}
|
|
@@ -153,13 +257,6 @@ export class ZipDeflateFile {
|
|
|
153
257
|
}
|
|
154
258
|
promise.then(() => callback()).catch(err => callback(err));
|
|
155
259
|
}
|
|
156
|
-
_emitHeaderIfNeeded() {
|
|
157
|
-
if (this._headerEmitted) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
this._emitHeader();
|
|
161
|
-
this._headerEmitted = true;
|
|
162
|
-
}
|
|
163
260
|
_writeData(data) {
|
|
164
261
|
if (data.length === 0) {
|
|
165
262
|
return Promise.resolve();
|
|
@@ -217,27 +314,60 @@ export class ZipDeflateFile {
|
|
|
217
314
|
this._emitDataDescriptor();
|
|
218
315
|
return completePromise;
|
|
219
316
|
}
|
|
220
|
-
|
|
221
|
-
* Push data - immediately compresses and outputs
|
|
222
|
-
* Returns a Promise that resolves when the write is complete.
|
|
223
|
-
* If final=true, it resolves after the data descriptor is emitted.
|
|
224
|
-
*/
|
|
225
|
-
push(data, final = false, callback) {
|
|
317
|
+
_pushUnchained(data, final, callback) {
|
|
226
318
|
if (this._finalized) {
|
|
227
319
|
const promise = Promise.reject(new Error("Cannot push to finalized ZipDeflateFile"));
|
|
228
320
|
this._tapCallback(promise, callback);
|
|
229
321
|
return promise;
|
|
230
322
|
}
|
|
323
|
+
if (this._deflateWanted === null) {
|
|
324
|
+
this._accumulateSample(data);
|
|
325
|
+
if (!this._shouldDecide(final)) {
|
|
326
|
+
if (data.length > 0) {
|
|
327
|
+
this._pendingChunks.push(data);
|
|
328
|
+
}
|
|
329
|
+
const promise = Promise.resolve();
|
|
330
|
+
this._tapCallback(promise, callback);
|
|
331
|
+
return promise;
|
|
332
|
+
}
|
|
333
|
+
this._decideCompressionIfNeeded(final);
|
|
334
|
+
this._emitHeaderIfNeeded();
|
|
335
|
+
const hadPendingChunks = this._pendingChunks.length > 0;
|
|
336
|
+
const flushPromise = this._flushPendingChunks();
|
|
337
|
+
let writePromise = flushPromise;
|
|
338
|
+
if (data.length > 0) {
|
|
339
|
+
writePromise = hadPendingChunks
|
|
340
|
+
? flushPromise.then(() => this._writeData(data))
|
|
341
|
+
: this._writeData(data);
|
|
342
|
+
}
|
|
343
|
+
const promise = final ? this._finalizeAfterWrite(writePromise) : writePromise;
|
|
344
|
+
this._tapCallback(promise, callback);
|
|
345
|
+
return promise;
|
|
346
|
+
}
|
|
231
347
|
this._emitHeaderIfNeeded();
|
|
232
348
|
const writePromise = this._writeData(data);
|
|
233
349
|
const promise = final ? this._finalizeAfterWrite(writePromise) : writePromise;
|
|
234
350
|
this._tapCallback(promise, callback);
|
|
235
351
|
return promise;
|
|
236
352
|
}
|
|
353
|
+
/**
|
|
354
|
+
* Push data - immediately compresses and outputs
|
|
355
|
+
* Returns a Promise that resolves when the write is complete.
|
|
356
|
+
* If final=true, it resolves after the data descriptor is emitted.
|
|
357
|
+
*/
|
|
358
|
+
push(data, final = false, callback) {
|
|
359
|
+
const promise = (this._pushChain = this._pushChain.then(() => this._pushUnchained(data, final, callback)));
|
|
360
|
+
// Prevent unhandled rejection when callers intentionally ignore the Promise.
|
|
361
|
+
promise.catch(() => { });
|
|
362
|
+
return promise;
|
|
363
|
+
}
|
|
237
364
|
/**
|
|
238
365
|
* Emit local file header with Data Descriptor flag
|
|
239
366
|
*/
|
|
240
367
|
_emitHeader() {
|
|
368
|
+
if (!this._localHeader) {
|
|
369
|
+
this._localHeader = this._buildLocalHeader();
|
|
370
|
+
}
|
|
241
371
|
this._enqueueData(this._localHeader, false);
|
|
242
372
|
}
|
|
243
373
|
/**
|
|
@@ -250,6 +380,7 @@ export class ZipDeflateFile {
|
|
|
250
380
|
this._centralDirEntryInfo = {
|
|
251
381
|
name: this.nameBytes,
|
|
252
382
|
extraField: this.extraField,
|
|
383
|
+
comment: this.commentBytes,
|
|
253
384
|
flags: this._flags,
|
|
254
385
|
crc: crcValue,
|
|
255
386
|
compressedSize: this._compressedSize,
|
|
@@ -288,7 +419,7 @@ export class ZipDeflateFile {
|
|
|
288
419
|
crc32: this._centralDirEntryInfo.crc,
|
|
289
420
|
lastModified: this._modTime,
|
|
290
421
|
localHeaderOffset: this._centralDirEntryInfo.offset,
|
|
291
|
-
comment:
|
|
422
|
+
comment: decodeUtf8(this._centralDirEntryInfo.comment),
|
|
292
423
|
externalAttributes: 0,
|
|
293
424
|
isEncrypted: false
|
|
294
425
|
};
|
|
@@ -305,7 +436,7 @@ export class ZipDeflateFile {
|
|
|
305
436
|
* Streaming ZIP Creator - processes files sequentially
|
|
306
437
|
*/
|
|
307
438
|
export class StreamingZip {
|
|
308
|
-
constructor(callback) {
|
|
439
|
+
constructor(callback, options) {
|
|
309
440
|
this.entries = [];
|
|
310
441
|
this.currentOffset = 0;
|
|
311
442
|
this.ended = false;
|
|
@@ -315,10 +446,12 @@ export class StreamingZip {
|
|
|
315
446
|
this.fileQueueIndex = 0;
|
|
316
447
|
this.activeFile = null;
|
|
317
448
|
this.callback = callback;
|
|
449
|
+
// Avoid per-instance TextEncoder allocations.
|
|
450
|
+
this.zipComment = options?.comment ? encodeUtf8(options.comment) : new Uint8Array(0);
|
|
318
451
|
}
|
|
319
452
|
add(file) {
|
|
320
453
|
if (this.ended) {
|
|
321
|
-
throw new Error("Cannot add files after calling end()");
|
|
454
|
+
throw new Error("Cannot add files after calling end() ");
|
|
322
455
|
}
|
|
323
456
|
this.fileQueue.push(file);
|
|
324
457
|
// If no active file, process this one
|
|
@@ -341,7 +474,18 @@ export class StreamingZip {
|
|
|
341
474
|
const file = this.fileQueue[this.fileQueueIndex++];
|
|
342
475
|
this.activeFile = file;
|
|
343
476
|
const startOffset = this.currentOffset;
|
|
477
|
+
const empty = new Uint8Array(0);
|
|
478
|
+
file.onerror = (err) => {
|
|
479
|
+
if (this.ended) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
this.ended = true;
|
|
483
|
+
this.callback(err, empty, true);
|
|
484
|
+
};
|
|
344
485
|
file.ondata = (data, final) => {
|
|
486
|
+
if (this.ended) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
345
489
|
this.currentOffset += data.length;
|
|
346
490
|
this.callback(null, data, false);
|
|
347
491
|
if (final) {
|
|
@@ -367,7 +511,7 @@ export class StreamingZip {
|
|
|
367
511
|
const header = buildCentralDirectoryHeader({
|
|
368
512
|
fileName: entry.name,
|
|
369
513
|
extraField: entry.extraField,
|
|
370
|
-
comment: empty,
|
|
514
|
+
comment: entry.comment ?? empty,
|
|
371
515
|
flags: entry.flags,
|
|
372
516
|
compressionMethod: entry.compressionMethod,
|
|
373
517
|
dosTime: entry.dosTime,
|
|
@@ -386,7 +530,7 @@ export class StreamingZip {
|
|
|
386
530
|
entryCount: this.entries.length,
|
|
387
531
|
centralDirSize,
|
|
388
532
|
centralDirOffset,
|
|
389
|
-
comment:
|
|
533
|
+
comment: this.zipComment
|
|
390
534
|
});
|
|
391
535
|
this.callback(null, eocd, true);
|
|
392
536
|
}
|