@cj-tech-master/excelts 4.2.1-canary.20260111102127.f808a37 → 4.2.1
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 +31 -0
- package/dist/browser/index.browser.d.ts +0 -1
- package/dist/browser/index.browser.js +0 -12
- package/dist/browser/modules/archive/byte-queue.d.ts +18 -0
- package/dist/browser/modules/archive/byte-queue.js +125 -0
- package/dist/browser/modules/archive/{compression/compress.base.js → compress.base.js} +1 -1
- package/dist/browser/modules/archive/{compression/compress.browser.d.ts → compress.browser.d.ts} +8 -2
- package/dist/{esm/modules/archive/compression → browser/modules/archive}/compress.browser.js +11 -3
- package/dist/browser/modules/archive/{compression/compress.d.ts → compress.d.ts} +2 -2
- package/dist/{esm/modules/archive/compression → browser/modules/archive}/compress.js +1 -1
- package/dist/browser/modules/archive/{compression/crc32.browser.d.ts → crc32.browser.d.ts} +1 -1
- package/dist/browser/modules/archive/{compression/crc32.d.ts → crc32.d.ts} +1 -1
- package/dist/browser/modules/archive/{compression/crc32.js → crc32.js} +1 -1
- package/dist/browser/modules/archive/defaults.d.ts +0 -1
- package/dist/browser/modules/archive/defaults.js +3 -6
- package/dist/browser/modules/archive/{compression/deflate-fallback.js → deflate-fallback.js} +1 -1
- package/dist/browser/modules/archive/{unzip/extract.d.ts → extract.d.ts} +2 -2
- package/dist/browser/modules/archive/index.base.d.ts +4 -4
- package/dist/browser/modules/archive/index.base.js +6 -3
- package/dist/browser/modules/archive/index.browser.d.ts +4 -3
- package/dist/browser/modules/archive/index.browser.js +7 -3
- package/dist/browser/modules/archive/index.d.ts +4 -3
- package/dist/browser/modules/archive/index.js +5 -3
- package/dist/browser/modules/archive/{unzip/stream.base.d.ts → parse.base.d.ts} +2 -36
- package/dist/browser/modules/archive/parse.base.js +644 -0
- package/dist/browser/modules/archive/{unzip/stream.browser.d.ts → parse.browser.d.ts} +1 -1
- package/dist/{esm/modules/archive/unzip/stream.browser.js → browser/modules/archive/parse.browser.js} +110 -371
- package/dist/browser/modules/archive/{unzip/stream.d.ts → parse.d.ts} +2 -2
- package/dist/{esm/modules/archive/unzip/stream.js → browser/modules/archive/parse.js} +5 -6
- package/dist/browser/modules/archive/{compression/streaming-compress.browser.d.ts → streaming-compress.browser.d.ts} +2 -2
- package/dist/browser/modules/archive/{compression/streaming-compress.browser.js → streaming-compress.browser.js} +3 -3
- package/dist/browser/modules/archive/{compression/streaming-compress.d.ts → streaming-compress.d.ts} +2 -2
- package/dist/browser/modules/archive/{compression/streaming-compress.js → streaming-compress.js} +2 -2
- package/dist/browser/modules/archive/{zip/stream.d.ts → streaming-zip.d.ts} +5 -28
- package/dist/{esm/modules/archive/zip/stream.js → browser/modules/archive/streaming-zip.js} +48 -192
- package/dist/browser/modules/archive/utils/bytes.js +16 -16
- package/dist/browser/modules/archive/utils/parse-buffer.js +23 -21
- package/dist/browser/modules/archive/utils/timestamps.js +1 -62
- package/dist/browser/modules/archive/utils/zip-extra-fields.d.ts +1 -1
- package/dist/browser/modules/archive/utils/zip-extra-fields.js +14 -26
- package/dist/browser/modules/archive/utils/zip-extra.d.ts +18 -0
- package/dist/browser/modules/archive/utils/zip-extra.js +68 -0
- package/dist/browser/modules/archive/zip-builder.d.ts +117 -0
- package/dist/browser/modules/archive/zip-builder.js +292 -0
- package/dist/browser/modules/archive/zip-constants.d.ts +18 -0
- package/dist/browser/modules/archive/zip-constants.js +23 -0
- package/dist/{esm/modules/archive/zip → browser/modules/archive}/zip-entry-metadata.js +3 -3
- package/dist/{types/modules/archive/unzip → browser/modules/archive}/zip-parser.d.ts +1 -1
- package/dist/{esm/modules/archive/unzip → browser/modules/archive}/zip-parser.js +24 -38
- package/dist/browser/modules/archive/{zip-spec/zip-records.d.ts → zip-records.d.ts} +0 -20
- package/dist/browser/modules/archive/zip-records.js +84 -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 +6 -3
- package/dist/browser/modules/excel/xlsx/xlsx.js +1 -1
- package/dist/browser/modules/stream/streams.browser.d.ts +30 -28
- package/dist/browser/modules/stream/streams.browser.js +710 -830
- package/dist/browser/modules/stream/streams.js +58 -140
- package/dist/cjs/modules/archive/byte-queue.js +129 -0
- package/dist/cjs/modules/archive/{compression/compress.base.js → compress.base.js} +1 -1
- package/dist/cjs/modules/archive/{compression/compress.browser.js → compress.browser.js} +11 -3
- package/dist/cjs/modules/archive/{compression/compress.js → compress.js} +1 -1
- package/dist/cjs/modules/archive/{compression/crc32.js → crc32.js} +1 -1
- package/dist/cjs/modules/archive/defaults.js +4 -7
- package/dist/cjs/modules/archive/{compression/deflate-fallback.js → deflate-fallback.js} +1 -1
- package/dist/cjs/modules/archive/index.base.js +19 -9
- package/dist/cjs/modules/archive/index.browser.js +10 -4
- package/dist/cjs/modules/archive/index.js +8 -4
- package/dist/cjs/modules/archive/parse.base.js +666 -0
- package/dist/cjs/modules/archive/{unzip/stream.browser.js → parse.browser.js} +111 -372
- package/dist/cjs/modules/archive/{unzip/stream.js → parse.js} +8 -9
- package/dist/cjs/modules/archive/{compression/streaming-compress.browser.js → streaming-compress.browser.js} +3 -3
- package/dist/cjs/modules/archive/{compression/streaming-compress.js → streaming-compress.js} +2 -2
- package/dist/cjs/modules/archive/{zip/stream.js → streaming-zip.js} +50 -194
- package/dist/cjs/modules/archive/utils/bytes.js +16 -16
- package/dist/cjs/modules/archive/utils/parse-buffer.js +23 -21
- package/dist/cjs/modules/archive/utils/timestamps.js +3 -64
- package/dist/cjs/modules/archive/utils/zip-extra-fields.js +14 -26
- package/dist/cjs/modules/archive/utils/zip-extra.js +74 -0
- package/dist/cjs/modules/archive/zip-builder.js +297 -0
- package/dist/cjs/modules/archive/zip-constants.js +26 -0
- package/dist/cjs/modules/archive/{zip/zip-entry-metadata.js → zip-entry-metadata.js} +5 -5
- package/dist/cjs/modules/archive/{unzip/zip-parser.js → zip-parser.js} +33 -47
- package/dist/cjs/modules/archive/zip-records.js +90 -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 +9 -6
- package/dist/cjs/modules/excel/xlsx/xlsx.js +2 -2
- package/dist/cjs/modules/stream/streams.browser.js +710 -830
- package/dist/cjs/modules/stream/streams.js +58 -140
- package/dist/esm/index.browser.js +0 -12
- package/dist/esm/modules/archive/byte-queue.js +125 -0
- package/dist/esm/modules/archive/{compression/compress.base.js → compress.base.js} +1 -1
- package/dist/{browser/modules/archive/compression → esm/modules/archive}/compress.browser.js +11 -3
- package/dist/{browser/modules/archive/compression → esm/modules/archive}/compress.js +1 -1
- package/dist/esm/modules/archive/{compression/crc32.js → crc32.js} +1 -1
- package/dist/esm/modules/archive/defaults.js +3 -6
- package/dist/esm/modules/archive/{compression/deflate-fallback.js → deflate-fallback.js} +1 -1
- package/dist/esm/modules/archive/index.base.js +6 -3
- package/dist/esm/modules/archive/index.browser.js +7 -3
- package/dist/esm/modules/archive/index.js +5 -3
- package/dist/esm/modules/archive/parse.base.js +644 -0
- package/dist/{browser/modules/archive/unzip/stream.browser.js → esm/modules/archive/parse.browser.js} +110 -371
- package/dist/{browser/modules/archive/unzip/stream.js → esm/modules/archive/parse.js} +5 -6
- package/dist/esm/modules/archive/{compression/streaming-compress.browser.js → streaming-compress.browser.js} +3 -3
- package/dist/esm/modules/archive/{compression/streaming-compress.js → streaming-compress.js} +2 -2
- package/dist/{browser/modules/archive/zip/stream.js → esm/modules/archive/streaming-zip.js} +48 -192
- package/dist/esm/modules/archive/utils/bytes.js +16 -16
- package/dist/esm/modules/archive/utils/parse-buffer.js +23 -21
- package/dist/esm/modules/archive/utils/timestamps.js +1 -62
- package/dist/esm/modules/archive/utils/zip-extra-fields.js +14 -26
- package/dist/esm/modules/archive/utils/zip-extra.js +68 -0
- package/dist/esm/modules/archive/zip-builder.js +292 -0
- package/dist/esm/modules/archive/zip-constants.js +23 -0
- package/dist/{browser/modules/archive/zip → esm/modules/archive}/zip-entry-metadata.js +3 -3
- package/dist/{browser/modules/archive/unzip → esm/modules/archive}/zip-parser.js +24 -38
- package/dist/esm/modules/archive/zip-records.js +84 -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 +6 -3
- package/dist/esm/modules/excel/xlsx/xlsx.js +1 -1
- package/dist/esm/modules/stream/streams.browser.js +710 -830
- package/dist/esm/modules/stream/streams.js +58 -140
- package/dist/iife/THIRD_PARTY_NOTICES.md +31 -0
- package/dist/iife/excelts.iife.js +4425 -6215
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +31 -103
- package/dist/types/index.browser.d.ts +0 -1
- package/dist/types/modules/archive/byte-queue.d.ts +18 -0
- package/dist/types/modules/archive/{compression/compress.browser.d.ts → compress.browser.d.ts} +8 -2
- package/dist/types/modules/archive/defaults.d.ts +0 -1
- package/dist/types/modules/archive/index.base.d.ts +4 -4
- package/dist/types/modules/archive/index.browser.d.ts +4 -3
- package/dist/types/modules/archive/index.d.ts +4 -3
- package/dist/types/modules/archive/{unzip/stream.base.d.ts → parse.base.d.ts} +4 -38
- package/dist/types/modules/archive/{unzip/stream.browser.d.ts → parse.browser.d.ts} +2 -2
- package/dist/types/modules/archive/{unzip/stream.d.ts → parse.d.ts} +3 -3
- package/dist/types/modules/archive/{compression/streaming-compress.browser.d.ts → streaming-compress.browser.d.ts} +1 -1
- package/dist/types/modules/archive/{zip/stream.d.ts → streaming-zip.d.ts} +6 -29
- package/dist/types/modules/archive/utils/zip-extra-fields.d.ts +1 -1
- package/dist/types/modules/archive/utils/zip-extra.d.ts +18 -0
- package/dist/types/modules/archive/zip-builder.d.ts +117 -0
- package/dist/types/modules/archive/zip-constants.d.ts +18 -0
- package/dist/types/modules/archive/{zip/zip-entry-metadata.d.ts → zip-entry-metadata.d.ts} +1 -1
- package/dist/{browser/modules/archive/unzip → types/modules/archive}/zip-parser.d.ts +1 -1
- package/dist/types/modules/archive/{zip-spec/zip-records.d.ts → zip-records.d.ts} +0 -20
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +1 -1
- package/dist/types/modules/stream/streams.browser.d.ts +30 -28
- package/package.json +1 -5
- package/dist/browser/modules/archive/internal/byte-queue.d.ts +0 -33
- package/dist/browser/modules/archive/internal/byte-queue.js +0 -407
- package/dist/browser/modules/archive/io/archive-sink.d.ts +0 -9
- package/dist/browser/modules/archive/io/archive-sink.js +0 -77
- package/dist/browser/modules/archive/io/archive-source.d.ts +0 -8
- package/dist/browser/modules/archive/io/archive-source.js +0 -107
- package/dist/browser/modules/archive/unzip/index.d.ts +0 -40
- package/dist/browser/modules/archive/unzip/index.js +0 -164
- package/dist/browser/modules/archive/unzip/stream.base.js +0 -1022
- package/dist/browser/modules/archive/utils/async-queue.d.ts +0 -7
- package/dist/browser/modules/archive/utils/async-queue.js +0 -103
- package/dist/browser/modules/archive/utils/compressibility.d.ts +0 -10
- package/dist/browser/modules/archive/utils/compressibility.js +0 -57
- package/dist/browser/modules/archive/utils/pattern-scanner.d.ts +0 -21
- package/dist/browser/modules/archive/utils/pattern-scanner.js +0 -27
- package/dist/browser/modules/archive/zip/index.d.ts +0 -42
- package/dist/browser/modules/archive/zip/index.js +0 -157
- package/dist/browser/modules/archive/zip/zip-bytes.d.ts +0 -73
- package/dist/browser/modules/archive/zip/zip-bytes.js +0 -239
- package/dist/browser/modules/archive/zip-spec/zip-records.js +0 -126
- package/dist/cjs/modules/archive/internal/byte-queue.js +0 -411
- package/dist/cjs/modules/archive/io/archive-sink.js +0 -82
- package/dist/cjs/modules/archive/io/archive-source.js +0 -114
- package/dist/cjs/modules/archive/unzip/index.js +0 -170
- package/dist/cjs/modules/archive/unzip/stream.base.js +0 -1044
- package/dist/cjs/modules/archive/utils/async-queue.js +0 -106
- package/dist/cjs/modules/archive/utils/compressibility.js +0 -60
- package/dist/cjs/modules/archive/utils/pattern-scanner.js +0 -31
- package/dist/cjs/modules/archive/zip/index.js +0 -162
- package/dist/cjs/modules/archive/zip/zip-bytes.js +0 -242
- package/dist/cjs/modules/archive/zip-spec/zip-records.js +0 -136
- package/dist/esm/modules/archive/internal/byte-queue.js +0 -407
- package/dist/esm/modules/archive/io/archive-sink.js +0 -77
- package/dist/esm/modules/archive/io/archive-source.js +0 -107
- package/dist/esm/modules/archive/unzip/index.js +0 -164
- package/dist/esm/modules/archive/unzip/stream.base.js +0 -1022
- package/dist/esm/modules/archive/utils/async-queue.js +0 -103
- package/dist/esm/modules/archive/utils/compressibility.js +0 -57
- package/dist/esm/modules/archive/utils/pattern-scanner.js +0 -27
- package/dist/esm/modules/archive/zip/index.js +0 -157
- package/dist/esm/modules/archive/zip/zip-bytes.js +0 -239
- package/dist/esm/modules/archive/zip-spec/zip-records.js +0 -126
- package/dist/types/modules/archive/internal/byte-queue.d.ts +0 -33
- package/dist/types/modules/archive/io/archive-sink.d.ts +0 -9
- package/dist/types/modules/archive/io/archive-source.d.ts +0 -8
- package/dist/types/modules/archive/unzip/index.d.ts +0 -40
- package/dist/types/modules/archive/utils/async-queue.d.ts +0 -7
- package/dist/types/modules/archive/utils/compressibility.d.ts +0 -10
- package/dist/types/modules/archive/utils/pattern-scanner.d.ts +0 -21
- package/dist/types/modules/archive/zip/index.d.ts +0 -42
- package/dist/types/modules/archive/zip/zip-bytes.d.ts +0 -73
- /package/dist/browser/modules/archive/{compression/compress.base.d.ts → compress.base.d.ts} +0 -0
- /package/dist/browser/modules/archive/{compression/crc32.base.d.ts → crc32.base.d.ts} +0 -0
- /package/dist/browser/modules/archive/{compression/crc32.base.js → crc32.base.js} +0 -0
- /package/dist/browser/modules/archive/{compression/crc32.browser.js → crc32.browser.js} +0 -0
- /package/dist/browser/modules/archive/{compression/deflate-fallback.d.ts → deflate-fallback.d.ts} +0 -0
- /package/dist/browser/modules/archive/{unzip/extract.js → extract.js} +0 -0
- /package/dist/browser/modules/archive/{compression/streaming-compress.base.d.ts → streaming-compress.base.d.ts} +0 -0
- /package/dist/browser/modules/archive/{compression/streaming-compress.base.js → streaming-compress.base.js} +0 -0
- /package/dist/browser/modules/archive/{zip-spec/zip-entry-info.d.ts → zip-entry-info.d.ts} +0 -0
- /package/dist/browser/modules/archive/{zip-spec/zip-entry-info.js → zip-entry-info.js} +0 -0
- /package/dist/browser/modules/archive/{zip/zip-entry-metadata.d.ts → zip-entry-metadata.d.ts} +0 -0
- /package/dist/cjs/modules/archive/{compression/crc32.base.js → crc32.base.js} +0 -0
- /package/dist/cjs/modules/archive/{compression/crc32.browser.js → crc32.browser.js} +0 -0
- /package/dist/cjs/modules/archive/{unzip/extract.js → extract.js} +0 -0
- /package/dist/cjs/modules/archive/{compression/streaming-compress.base.js → streaming-compress.base.js} +0 -0
- /package/dist/cjs/modules/archive/{zip-spec/zip-entry-info.js → zip-entry-info.js} +0 -0
- /package/dist/esm/modules/archive/{compression/crc32.base.js → crc32.base.js} +0 -0
- /package/dist/esm/modules/archive/{compression/crc32.browser.js → crc32.browser.js} +0 -0
- /package/dist/esm/modules/archive/{unzip/extract.js → extract.js} +0 -0
- /package/dist/esm/modules/archive/{compression/streaming-compress.base.js → streaming-compress.base.js} +0 -0
- /package/dist/esm/modules/archive/{zip-spec/zip-entry-info.js → zip-entry-info.js} +0 -0
- /package/dist/types/modules/archive/{compression/compress.base.d.ts → compress.base.d.ts} +0 -0
- /package/dist/types/modules/archive/{compression/compress.d.ts → compress.d.ts} +0 -0
- /package/dist/types/modules/archive/{compression/crc32.base.d.ts → crc32.base.d.ts} +0 -0
- /package/dist/types/modules/archive/{compression/crc32.browser.d.ts → crc32.browser.d.ts} +0 -0
- /package/dist/types/modules/archive/{compression/crc32.d.ts → crc32.d.ts} +0 -0
- /package/dist/types/modules/archive/{compression/deflate-fallback.d.ts → deflate-fallback.d.ts} +0 -0
- /package/dist/types/modules/archive/{unzip/extract.d.ts → extract.d.ts} +0 -0
- /package/dist/types/modules/archive/{compression/streaming-compress.base.d.ts → streaming-compress.base.d.ts} +0 -0
- /package/dist/types/modules/archive/{compression/streaming-compress.d.ts → streaming-compress.d.ts} +0 -0
- /package/dist/types/modules/archive/{zip-spec/zip-entry-info.d.ts → zip-entry-info.d.ts} +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const EXTENDED_TIMESTAMP_ID = 0x5455;
|
|
2
|
+
function clampUint32(value) {
|
|
3
|
+
if (!Number.isFinite(value)) {
|
|
4
|
+
return 0;
|
|
5
|
+
}
|
|
6
|
+
if (value <= 0) {
|
|
7
|
+
return 0;
|
|
8
|
+
}
|
|
9
|
+
// 0xFFFFFFFF fits JS safe integer.
|
|
10
|
+
if (value >= 0xffffffff) {
|
|
11
|
+
return 0xffffffff;
|
|
12
|
+
}
|
|
13
|
+
return value >>> 0;
|
|
14
|
+
}
|
|
15
|
+
export function unixSecondsFromDate(date) {
|
|
16
|
+
return clampUint32(Math.floor(date.getTime() / 1000));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Build Info-ZIP "Extended Timestamp" extra field (0x5455).
|
|
20
|
+
* We write only mtime (UTC, Unix seconds) to minimize size.
|
|
21
|
+
*
|
|
22
|
+
* Layout:
|
|
23
|
+
* - Header ID: 2 bytes (0x5455)
|
|
24
|
+
* - Data size: 2 bytes
|
|
25
|
+
* - Flags: 1 byte (bit0 = mtime)
|
|
26
|
+
* - mtime: 4 bytes (Unix seconds)
|
|
27
|
+
*/
|
|
28
|
+
export function buildExtendedTimestampExtraFieldFromUnixSeconds(unixSeconds) {
|
|
29
|
+
const ts = clampUint32(unixSeconds);
|
|
30
|
+
// flags(1) + mtime(4)
|
|
31
|
+
const payloadSize = 5;
|
|
32
|
+
const out = new Uint8Array(4 + payloadSize);
|
|
33
|
+
const view = new DataView(out.buffer);
|
|
34
|
+
view.setUint16(0, EXTENDED_TIMESTAMP_ID, true);
|
|
35
|
+
view.setUint16(2, payloadSize, true);
|
|
36
|
+
out[4] = 0x01; // mtime present
|
|
37
|
+
view.setUint32(5, ts, true);
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
export function buildExtendedTimestampExtraFieldFromDate(date) {
|
|
41
|
+
return buildExtendedTimestampExtraFieldFromUnixSeconds(unixSecondsFromDate(date));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Parse Info-ZIP "Extended Timestamp" extra field (0x5455) and return mtime.
|
|
45
|
+
* Returns Unix seconds (UTC) if present.
|
|
46
|
+
*/
|
|
47
|
+
export function parseExtendedTimestampMtimeUnixSeconds(extraField) {
|
|
48
|
+
const view = new DataView(extraField.buffer, extraField.byteOffset, extraField.byteLength);
|
|
49
|
+
let offset = 0;
|
|
50
|
+
while (offset + 4 <= extraField.length) {
|
|
51
|
+
const headerId = view.getUint16(offset, true);
|
|
52
|
+
const dataSize = view.getUint16(offset + 2, true);
|
|
53
|
+
const dataStart = offset + 4;
|
|
54
|
+
const dataEnd = dataStart + dataSize;
|
|
55
|
+
if (dataEnd > extraField.length) {
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
if (headerId === EXTENDED_TIMESTAMP_ID && dataSize >= 1) {
|
|
59
|
+
const flags = extraField[dataStart];
|
|
60
|
+
if ((flags & 0x01) !== 0 && dataSize >= 5) {
|
|
61
|
+
// mtime is 4 bytes right after flags.
|
|
62
|
+
return view.getUint32(dataStart + 1, true) >>> 0;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
offset = dataEnd;
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZIP file format builder
|
|
3
|
+
*
|
|
4
|
+
* Implements ZIP file structure according to PKWARE's APPNOTE.TXT specification
|
|
5
|
+
* https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
|
|
6
|
+
*
|
|
7
|
+
* ZIP file structure:
|
|
8
|
+
* ┌──────────────────────────┐
|
|
9
|
+
* │ Local File Header 1 │
|
|
10
|
+
* │ File Data 1 │
|
|
11
|
+
* ├──────────────────────────┤
|
|
12
|
+
* │ Local File Header 2 │
|
|
13
|
+
* │ File Data 2 │
|
|
14
|
+
* ├──────────────────────────┤
|
|
15
|
+
* │ ... │
|
|
16
|
+
* ├──────────────────────────┤
|
|
17
|
+
* │ Central Directory 1 │
|
|
18
|
+
* │ Central Directory 2 │
|
|
19
|
+
* │ ... │
|
|
20
|
+
* ├──────────────────────────┤
|
|
21
|
+
* │ End of Central Directory │
|
|
22
|
+
* └──────────────────────────┘
|
|
23
|
+
*/
|
|
24
|
+
import { compress, compressSync } from "./compress.js";
|
|
25
|
+
import { crc32 } from "./crc32.js";
|
|
26
|
+
import { concatUint8Arrays, sumUint8ArrayLengths } from "./utils/bytes.js";
|
|
27
|
+
import {} from "./utils/timestamps.js";
|
|
28
|
+
import { buildZipEntryMetadata, resolveZipCompressionMethod } from "./zip-entry-metadata.js";
|
|
29
|
+
import { DEFAULT_ZIP_LEVEL, DEFAULT_ZIP_TIMESTAMPS } from "./defaults.js";
|
|
30
|
+
import { buildCentralDirectoryHeader, buildEndOfCentralDirectory, buildLocalFileHeader } from "./zip-records.js";
|
|
31
|
+
import { FLAG_UTF8, VERSION_MADE_BY, VERSION_NEEDED } from "./zip-constants.js";
|
|
32
|
+
const LOCAL_FILE_HEADER_FIXED_SIZE = 30;
|
|
33
|
+
function encodeZipComment(comment) {
|
|
34
|
+
// Keep empty comment as empty bytes (no encoding surprises).
|
|
35
|
+
return comment ? new TextEncoder().encode(comment) : new Uint8Array(0);
|
|
36
|
+
}
|
|
37
|
+
function shouldDeflate(level, data) {
|
|
38
|
+
return level > 0 && data.length > 0;
|
|
39
|
+
}
|
|
40
|
+
function computeLocalRecordSize(entry) {
|
|
41
|
+
return (LOCAL_FILE_HEADER_FIXED_SIZE +
|
|
42
|
+
entry.name.length +
|
|
43
|
+
entry.extraField.length +
|
|
44
|
+
entry.compressedData.length);
|
|
45
|
+
}
|
|
46
|
+
function buildProcessedEntry(entry, offset, settings, compressedData) {
|
|
47
|
+
const modDate = entry.modTime ?? settings.defaultModTime;
|
|
48
|
+
const isCompressed = shouldDeflate(settings.level, entry.data);
|
|
49
|
+
const metadata = buildZipEntryMetadata({
|
|
50
|
+
name: entry.name,
|
|
51
|
+
comment: entry.comment,
|
|
52
|
+
modTime: modDate,
|
|
53
|
+
timestamps: settings.timestamps,
|
|
54
|
+
useDataDescriptor: false,
|
|
55
|
+
deflate: isCompressed
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
name: metadata.nameBytes,
|
|
59
|
+
data: entry.data,
|
|
60
|
+
compressedData,
|
|
61
|
+
crc: crc32(entry.data),
|
|
62
|
+
compressionMethod: resolveZipCompressionMethod(isCompressed),
|
|
63
|
+
modTime: metadata.dosTime,
|
|
64
|
+
modDate: metadata.dosDate,
|
|
65
|
+
extraField: metadata.extraField,
|
|
66
|
+
comment: metadata.commentBytes,
|
|
67
|
+
offset
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function appendProcessedEntry(processedEntries, entry, compressedData, currentOffset, settings) {
|
|
71
|
+
const processedEntry = buildProcessedEntry(entry, currentOffset, settings, compressedData);
|
|
72
|
+
processedEntries.push(processedEntry);
|
|
73
|
+
return {
|
|
74
|
+
processedEntry,
|
|
75
|
+
nextOffset: currentOffset + computeLocalRecordSize(processedEntry)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function finalizeZip(processedEntries, zipComment, centralDirOffset) {
|
|
79
|
+
// Build ZIP structure
|
|
80
|
+
const chunks = [];
|
|
81
|
+
// Local file headers and data
|
|
82
|
+
for (const entry of processedEntries) {
|
|
83
|
+
chunks.push(buildLocalFileHeaderChunk(entry));
|
|
84
|
+
chunks.push(entry.compressedData);
|
|
85
|
+
}
|
|
86
|
+
chunks.push(...buildCentralDirectorySection(processedEntries, centralDirOffset, zipComment));
|
|
87
|
+
return concatUint8Arrays(chunks);
|
|
88
|
+
}
|
|
89
|
+
function buildLocalFileHeaderChunk(entry) {
|
|
90
|
+
return buildLocalFileHeader({
|
|
91
|
+
fileName: entry.name,
|
|
92
|
+
extraField: entry.extraField,
|
|
93
|
+
flags: FLAG_UTF8,
|
|
94
|
+
compressionMethod: entry.compressionMethod,
|
|
95
|
+
dosTime: entry.modTime,
|
|
96
|
+
dosDate: entry.modDate,
|
|
97
|
+
crc32: entry.crc,
|
|
98
|
+
compressedSize: entry.compressedData.length,
|
|
99
|
+
uncompressedSize: entry.data.length,
|
|
100
|
+
versionNeeded: VERSION_NEEDED
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function buildCentralDirHeaderChunk(entry) {
|
|
104
|
+
return buildCentralDirectoryHeader({
|
|
105
|
+
fileName: entry.name,
|
|
106
|
+
extraField: entry.extraField,
|
|
107
|
+
comment: entry.comment,
|
|
108
|
+
flags: FLAG_UTF8,
|
|
109
|
+
compressionMethod: entry.compressionMethod,
|
|
110
|
+
dosTime: entry.modTime,
|
|
111
|
+
dosDate: entry.modDate,
|
|
112
|
+
crc32: entry.crc,
|
|
113
|
+
compressedSize: entry.compressedData.length,
|
|
114
|
+
uncompressedSize: entry.data.length,
|
|
115
|
+
localHeaderOffset: entry.offset,
|
|
116
|
+
versionMadeBy: VERSION_MADE_BY,
|
|
117
|
+
versionNeeded: VERSION_NEEDED
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function buildCentralDirectorySection(processedEntries, centralDirOffset, zipComment) {
|
|
121
|
+
const chunks = [];
|
|
122
|
+
for (const entry of processedEntries) {
|
|
123
|
+
chunks.push(buildCentralDirHeaderChunk(entry));
|
|
124
|
+
}
|
|
125
|
+
const centralDirSize = sumUint8ArrayLengths(chunks);
|
|
126
|
+
chunks.push(buildEndOfCentralDirectory({
|
|
127
|
+
entryCount: processedEntries.length,
|
|
128
|
+
centralDirSize,
|
|
129
|
+
centralDirOffset,
|
|
130
|
+
comment: zipComment
|
|
131
|
+
}));
|
|
132
|
+
return chunks;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Create a ZIP file from entries (async)
|
|
136
|
+
*
|
|
137
|
+
* @param entries - Files to include in ZIP
|
|
138
|
+
* @param options - ZIP options
|
|
139
|
+
* @returns ZIP file as Uint8Array
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* const zip = await createZip([
|
|
144
|
+
* { name: "hello.txt", data: new TextEncoder().encode("Hello!") },
|
|
145
|
+
* { name: "folder/file.txt", data: new TextEncoder().encode("Nested!") }
|
|
146
|
+
* ], { level: 6 });
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
export async function createZip(entries, options = {}) {
|
|
150
|
+
const level = options.level ?? DEFAULT_ZIP_LEVEL;
|
|
151
|
+
const timestamps = options.timestamps ?? DEFAULT_ZIP_TIMESTAMPS;
|
|
152
|
+
const zipComment = encodeZipComment(options.comment);
|
|
153
|
+
const defaultModTime = new Date();
|
|
154
|
+
const settings = {
|
|
155
|
+
level,
|
|
156
|
+
timestamps,
|
|
157
|
+
defaultModTime
|
|
158
|
+
};
|
|
159
|
+
const compressOptions = {
|
|
160
|
+
level,
|
|
161
|
+
thresholdBytes: options.thresholdBytes
|
|
162
|
+
};
|
|
163
|
+
const compressedDatas = await Promise.all(entries.map(async (entry) => {
|
|
164
|
+
return shouldDeflate(level, entry.data) ? compress(entry.data, compressOptions) : entry.data;
|
|
165
|
+
}));
|
|
166
|
+
// Process entries
|
|
167
|
+
const processedEntries = [];
|
|
168
|
+
let currentOffset = 0;
|
|
169
|
+
for (let i = 0; i < entries.length; i++) {
|
|
170
|
+
const entry = entries[i];
|
|
171
|
+
const compressedData = compressedDatas[i];
|
|
172
|
+
const result = appendProcessedEntry(processedEntries, entry, compressedData, currentOffset, settings);
|
|
173
|
+
currentOffset = result.nextOffset;
|
|
174
|
+
}
|
|
175
|
+
return finalizeZip(processedEntries, zipComment, currentOffset);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Create a ZIP file from entries (sync)
|
|
179
|
+
*
|
|
180
|
+
* This is supported in both Node.js and browser builds.
|
|
181
|
+
*/
|
|
182
|
+
export function createZipSync(entries, options = {}) {
|
|
183
|
+
const level = options.level ?? DEFAULT_ZIP_LEVEL;
|
|
184
|
+
const timestamps = options.timestamps ?? DEFAULT_ZIP_TIMESTAMPS;
|
|
185
|
+
const zipComment = encodeZipComment(options.comment);
|
|
186
|
+
const defaultModTime = new Date();
|
|
187
|
+
const settings = {
|
|
188
|
+
level,
|
|
189
|
+
timestamps,
|
|
190
|
+
defaultModTime
|
|
191
|
+
};
|
|
192
|
+
const compressOptions = {
|
|
193
|
+
level,
|
|
194
|
+
thresholdBytes: options.thresholdBytes
|
|
195
|
+
};
|
|
196
|
+
// Process entries
|
|
197
|
+
const processedEntries = [];
|
|
198
|
+
let currentOffset = 0;
|
|
199
|
+
for (const entry of entries) {
|
|
200
|
+
// Compress data
|
|
201
|
+
const compressedData = shouldDeflate(level, entry.data)
|
|
202
|
+
? compressSync(entry.data, compressOptions)
|
|
203
|
+
: entry.data;
|
|
204
|
+
const result = appendProcessedEntry(processedEntries, entry, compressedData, currentOffset, settings);
|
|
205
|
+
currentOffset = result.nextOffset;
|
|
206
|
+
}
|
|
207
|
+
return finalizeZip(processedEntries, zipComment, currentOffset);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Streaming ZIP builder for large files
|
|
211
|
+
* Writes chunks to a callback as they are generated
|
|
212
|
+
*/
|
|
213
|
+
export class ZipBuilder {
|
|
214
|
+
/**
|
|
215
|
+
* Create a new ZIP builder
|
|
216
|
+
* @param options - ZIP options
|
|
217
|
+
*/
|
|
218
|
+
constructor(options = {}) {
|
|
219
|
+
this.entries = [];
|
|
220
|
+
this.currentOffset = 0;
|
|
221
|
+
this.finalized = false;
|
|
222
|
+
this.level = options.level ?? DEFAULT_ZIP_LEVEL;
|
|
223
|
+
this.zipComment = encodeZipComment(options.comment);
|
|
224
|
+
this.timestamps = options.timestamps ?? DEFAULT_ZIP_TIMESTAMPS;
|
|
225
|
+
this.compressOptions = {
|
|
226
|
+
level: this.level,
|
|
227
|
+
thresholdBytes: options.thresholdBytes
|
|
228
|
+
};
|
|
229
|
+
this.settings = {
|
|
230
|
+
level: this.level,
|
|
231
|
+
timestamps: this.timestamps,
|
|
232
|
+
defaultModTime: new Date()
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Add a file to the ZIP (async)
|
|
237
|
+
* @param entry - File entry
|
|
238
|
+
* @returns Local file header and compressed data chunks
|
|
239
|
+
*/
|
|
240
|
+
async addFile(entry) {
|
|
241
|
+
if (this.finalized) {
|
|
242
|
+
throw new Error("Cannot add files after finalizing");
|
|
243
|
+
}
|
|
244
|
+
// Compress data
|
|
245
|
+
const compressedData = shouldDeflate(this.level, entry.data)
|
|
246
|
+
? await compress(entry.data, this.compressOptions)
|
|
247
|
+
: entry.data;
|
|
248
|
+
const result = appendProcessedEntry(this.entries, entry, compressedData, this.currentOffset, this.settings);
|
|
249
|
+
this.currentOffset = result.nextOffset;
|
|
250
|
+
return [buildLocalFileHeaderChunk(result.processedEntry), compressedData];
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Add a file to the ZIP (sync)
|
|
254
|
+
* @param entry - File entry
|
|
255
|
+
* @returns Local file header and compressed data chunks
|
|
256
|
+
*/
|
|
257
|
+
addFileSync(entry) {
|
|
258
|
+
if (this.finalized) {
|
|
259
|
+
throw new Error("Cannot add files after finalizing");
|
|
260
|
+
}
|
|
261
|
+
// Compress data
|
|
262
|
+
const compressedData = shouldDeflate(this.level, entry.data)
|
|
263
|
+
? compressSync(entry.data, this.compressOptions)
|
|
264
|
+
: entry.data;
|
|
265
|
+
const result = appendProcessedEntry(this.entries, entry, compressedData, this.currentOffset, this.settings);
|
|
266
|
+
this.currentOffset = result.nextOffset;
|
|
267
|
+
return [buildLocalFileHeaderChunk(result.processedEntry), compressedData];
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Finalize the ZIP and return central directory + end record
|
|
271
|
+
* @returns Central directory and end of central directory chunks
|
|
272
|
+
*/
|
|
273
|
+
finalize() {
|
|
274
|
+
if (this.finalized) {
|
|
275
|
+
throw new Error("ZIP already finalized");
|
|
276
|
+
}
|
|
277
|
+
this.finalized = true;
|
|
278
|
+
return buildCentralDirectorySection(this.entries, this.currentOffset, this.zipComment);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Get current number of entries
|
|
282
|
+
*/
|
|
283
|
+
get entryCount() {
|
|
284
|
+
return this.entries.length;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Get current ZIP data size (without central directory)
|
|
288
|
+
*/
|
|
289
|
+
get dataSize() {
|
|
290
|
+
return this.currentOffset;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZIP format constants (PKWARE APPNOTE)
|
|
3
|
+
* Shared by ZIP writer and parser implementations.
|
|
4
|
+
*/
|
|
5
|
+
// Signatures
|
|
6
|
+
export const LOCAL_FILE_HEADER_SIG = 0x04034b50;
|
|
7
|
+
export const CENTRAL_DIR_HEADER_SIG = 0x02014b50;
|
|
8
|
+
export const END_OF_CENTRAL_DIR_SIG = 0x06054b50;
|
|
9
|
+
export const DATA_DESCRIPTOR_SIG = 0x08074b50;
|
|
10
|
+
export const ZIP64_END_OF_CENTRAL_DIR_SIG = 0x06064b50;
|
|
11
|
+
export const ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50;
|
|
12
|
+
// Versions
|
|
13
|
+
export const VERSION_NEEDED = 20; // 2.0 - supports DEFLATE
|
|
14
|
+
export const VERSION_MADE_BY = 20; // 2.0
|
|
15
|
+
// Compression methods
|
|
16
|
+
export const COMPRESSION_STORE = 0;
|
|
17
|
+
export const COMPRESSION_DEFLATE = 8;
|
|
18
|
+
// General purpose bit flags
|
|
19
|
+
export const FLAG_UTF8 = 0x0800;
|
|
20
|
+
export const FLAG_DATA_DESCRIPTOR = 0x0008;
|
|
21
|
+
// ZIP64 / sentinel sizes
|
|
22
|
+
export const UINT16_MAX = 0xffff;
|
|
23
|
+
export const UINT32_MAX = 0xffffffff;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { encodeUtf8 } from "
|
|
2
|
-
import { buildZipTimestampExtraField, dateToZipDos } from "
|
|
3
|
-
import { COMPRESSION_DEFLATE, COMPRESSION_STORE, FLAG_DATA_DESCRIPTOR, FLAG_UTF8 } from "
|
|
1
|
+
import { encodeUtf8 } from "./utils/text.js";
|
|
2
|
+
import { buildZipTimestampExtraField, dateToZipDos } from "./utils/timestamps.js";
|
|
3
|
+
import { COMPRESSION_DEFLATE, COMPRESSION_STORE, FLAG_DATA_DESCRIPTOR, FLAG_UTF8 } from "./zip-constants.js";
|
|
4
4
|
export function resolveZipCompressionMethod(deflate) {
|
|
5
5
|
return deflate ? COMPRESSION_DEFLATE : COMPRESSION_STORE;
|
|
6
6
|
}
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* Works in both Node.js and browser environments
|
|
4
4
|
* No dependency on Node.js stream module
|
|
5
5
|
*/
|
|
6
|
-
import { decompress, decompressSync } from "
|
|
7
|
-
import { BinaryReader } from "
|
|
8
|
-
import { resolveZipLastModifiedDateFromUnixSeconds } from "
|
|
9
|
-
import { parseZipExtraFields } from "
|
|
10
|
-
import { CENTRAL_DIR_HEADER_SIG, COMPRESSION_DEFLATE, COMPRESSION_STORE, LOCAL_FILE_HEADER_SIG, UINT16_MAX, UINT32_MAX, ZIP64_END_OF_CENTRAL_DIR_SIG } from "
|
|
6
|
+
import { decompress, decompressSync } from "./compress.js";
|
|
7
|
+
import { BinaryReader } from "./utils/binary.js";
|
|
8
|
+
import { resolveZipLastModifiedDateFromUnixSeconds } from "./utils/timestamps.js";
|
|
9
|
+
import { parseZipExtraFields } from "./utils/zip-extra-fields.js";
|
|
10
|
+
import { CENTRAL_DIR_HEADER_SIG, COMPRESSION_DEFLATE, COMPRESSION_STORE, END_OF_CENTRAL_DIR_SIG, LOCAL_FILE_HEADER_SIG, UINT16_MAX, UINT32_MAX, ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG, ZIP64_END_OF_CENTRAL_DIR_SIG } from "./zip-constants.js";
|
|
11
11
|
/**
|
|
12
12
|
* Find the End of Central Directory record
|
|
13
13
|
* Searches backwards from the end of the file
|
|
@@ -16,13 +16,9 @@ function findEndOfCentralDir(data) {
|
|
|
16
16
|
// EOCD is at least 22 bytes, search backwards
|
|
17
17
|
// Comment can be up to 65535 bytes
|
|
18
18
|
const minOffset = Math.max(0, data.length - 65557);
|
|
19
|
-
|
|
20
|
-
const b0 = 0x50;
|
|
21
|
-
const b1 = 0x4b;
|
|
22
|
-
const b2 = 0x05;
|
|
23
|
-
const b3 = 0x06;
|
|
19
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
24
20
|
for (let i = data.length - 22; i >= minOffset; i--) {
|
|
25
|
-
if (
|
|
21
|
+
if (view.getUint32(i, true) === END_OF_CENTRAL_DIR_SIG) {
|
|
26
22
|
return i;
|
|
27
23
|
}
|
|
28
24
|
}
|
|
@@ -37,11 +33,8 @@ function findZip64EOCDLocator(data, eocdOffset) {
|
|
|
37
33
|
if (locatorOffset < 0) {
|
|
38
34
|
return -1;
|
|
39
35
|
}
|
|
40
|
-
|
|
41
|
-
if (
|
|
42
|
-
data[locatorOffset + 1] === 0x4b &&
|
|
43
|
-
data[locatorOffset + 2] === 0x06 &&
|
|
44
|
-
data[locatorOffset + 3] === 0x07) {
|
|
36
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
37
|
+
if (view.getUint32(locatorOffset, true) === ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) {
|
|
45
38
|
return locatorOffset;
|
|
46
39
|
}
|
|
47
40
|
return -1;
|
|
@@ -51,7 +44,7 @@ function findZip64EOCDLocator(data, eocdOffset) {
|
|
|
51
44
|
*/
|
|
52
45
|
function parseZipEntries(data, options = {}) {
|
|
53
46
|
const { decodeStrings = true } = options;
|
|
54
|
-
|
|
47
|
+
const entries = [];
|
|
55
48
|
// Find End of Central Directory
|
|
56
49
|
const eocdOffset = findEndOfCentralDir(data);
|
|
57
50
|
if (eocdOffset === -1) {
|
|
@@ -103,10 +96,6 @@ function parseZipEntries(data, options = {}) {
|
|
|
103
96
|
}
|
|
104
97
|
}
|
|
105
98
|
}
|
|
106
|
-
// Preallocate to avoid repeated array growth on large archives.
|
|
107
|
-
if (totalEntries > 0) {
|
|
108
|
-
entries = new Array(totalEntries);
|
|
109
|
-
}
|
|
110
99
|
// Read Central Directory entries
|
|
111
100
|
const centralReader = new BinaryReader(data, centralDirOffset);
|
|
112
101
|
for (let i = 0; i < totalEntries; i++) {
|
|
@@ -155,26 +144,23 @@ function parseZipEntries(data, options = {}) {
|
|
|
155
144
|
// Check for UTF-8 flag (bit 11)
|
|
156
145
|
const isUtf8 = (flags & 0x800) !== 0;
|
|
157
146
|
const useUtf8 = decodeStrings && isUtf8;
|
|
158
|
-
const fileName =
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
localHeaderOffset = vars.offsetToLocalFileHeader ?? localHeaderOffset;
|
|
171
|
-
}
|
|
172
|
-
const comment = commentLength > 0 ? centralReader.readString(commentLength, useUtf8) : "";
|
|
147
|
+
const fileName = centralReader.readString(fileNameLength, useUtf8);
|
|
148
|
+
const extraField = centralReader.readBytes(extraFieldLength);
|
|
149
|
+
const comment = centralReader.readString(commentLength, useUtf8);
|
|
150
|
+
const vars = {
|
|
151
|
+
compressedSize,
|
|
152
|
+
uncompressedSize,
|
|
153
|
+
offsetToLocalFileHeader: localHeaderOffset
|
|
154
|
+
};
|
|
155
|
+
const extraFields = extraFieldLength > 0 ? parseZipExtraFields(extraField, vars) : {};
|
|
156
|
+
compressedSize = vars.compressedSize;
|
|
157
|
+
uncompressedSize = vars.uncompressedSize;
|
|
158
|
+
localHeaderOffset = vars.offsetToLocalFileHeader ?? localHeaderOffset;
|
|
173
159
|
const isDirectory = fileName.endsWith("/") || (externalAttributes & 0x10) !== 0;
|
|
174
160
|
const isEncrypted = (flags & 0x01) !== 0;
|
|
175
161
|
const unixSecondsMtime = extraFields.mtimeUnixSeconds;
|
|
176
162
|
const lastModified = resolveZipLastModifiedDateFromUnixSeconds(lastModDate, lastModTime, unixSecondsMtime);
|
|
177
|
-
entries
|
|
163
|
+
entries.push({
|
|
178
164
|
path: fileName,
|
|
179
165
|
isDirectory,
|
|
180
166
|
compressedSize,
|
|
@@ -186,7 +172,7 @@ function parseZipEntries(data, options = {}) {
|
|
|
186
172
|
comment,
|
|
187
173
|
externalAttributes,
|
|
188
174
|
isEncrypted
|
|
189
|
-
};
|
|
175
|
+
});
|
|
190
176
|
}
|
|
191
177
|
return entries;
|
|
192
178
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZIP record builders (PKWARE APPNOTE)
|
|
3
|
+
*
|
|
4
|
+
* Shared by streaming zip writer and buffer zip builder.
|
|
5
|
+
*/
|
|
6
|
+
import { CENTRAL_DIR_HEADER_SIG, DATA_DESCRIPTOR_SIG, END_OF_CENTRAL_DIR_SIG, LOCAL_FILE_HEADER_SIG, VERSION_MADE_BY, VERSION_NEEDED } from "./zip-constants.js";
|
|
7
|
+
export function buildLocalFileHeader(input) {
|
|
8
|
+
const versionNeeded = input.versionNeeded ?? VERSION_NEEDED;
|
|
9
|
+
const header = new Uint8Array(30 + input.fileName.length + input.extraField.length);
|
|
10
|
+
const view = new DataView(header.buffer);
|
|
11
|
+
view.setUint32(0, LOCAL_FILE_HEADER_SIG, true);
|
|
12
|
+
view.setUint16(4, versionNeeded, true);
|
|
13
|
+
view.setUint16(6, input.flags, true);
|
|
14
|
+
view.setUint16(8, input.compressionMethod, true);
|
|
15
|
+
view.setUint16(10, input.dosTime, true);
|
|
16
|
+
view.setUint16(12, input.dosDate, true);
|
|
17
|
+
view.setUint32(14, input.crc32, true);
|
|
18
|
+
view.setUint32(18, input.compressedSize, true);
|
|
19
|
+
view.setUint32(22, input.uncompressedSize, true);
|
|
20
|
+
view.setUint16(26, input.fileName.length, true);
|
|
21
|
+
view.setUint16(28, input.extraField.length, true);
|
|
22
|
+
header.set(input.fileName, 30);
|
|
23
|
+
if (input.extraField.length > 0) {
|
|
24
|
+
header.set(input.extraField, 30 + input.fileName.length);
|
|
25
|
+
}
|
|
26
|
+
return header;
|
|
27
|
+
}
|
|
28
|
+
export function buildCentralDirectoryHeader(input) {
|
|
29
|
+
const versionMadeBy = input.versionMadeBy ?? VERSION_MADE_BY;
|
|
30
|
+
const versionNeeded = input.versionNeeded ?? VERSION_NEEDED;
|
|
31
|
+
const externalAttributes = input.externalAttributes ?? 0;
|
|
32
|
+
const header = new Uint8Array(46 + input.fileName.length + input.extraField.length + input.comment.length);
|
|
33
|
+
const view = new DataView(header.buffer);
|
|
34
|
+
view.setUint32(0, CENTRAL_DIR_HEADER_SIG, true);
|
|
35
|
+
view.setUint16(4, versionMadeBy, true);
|
|
36
|
+
view.setUint16(6, versionNeeded, true);
|
|
37
|
+
view.setUint16(8, input.flags, true);
|
|
38
|
+
view.setUint16(10, input.compressionMethod, true);
|
|
39
|
+
view.setUint16(12, input.dosTime, true);
|
|
40
|
+
view.setUint16(14, input.dosDate, true);
|
|
41
|
+
view.setUint32(16, input.crc32, true);
|
|
42
|
+
view.setUint32(20, input.compressedSize, true);
|
|
43
|
+
view.setUint32(24, input.uncompressedSize, true);
|
|
44
|
+
view.setUint16(28, input.fileName.length, true);
|
|
45
|
+
view.setUint16(30, input.extraField.length, true);
|
|
46
|
+
view.setUint16(32, input.comment.length, true);
|
|
47
|
+
view.setUint16(34, 0, true); // disk number start
|
|
48
|
+
view.setUint16(36, 0, true); // internal file attributes
|
|
49
|
+
view.setUint32(38, externalAttributes, true);
|
|
50
|
+
view.setUint32(42, input.localHeaderOffset, true);
|
|
51
|
+
header.set(input.fileName, 46);
|
|
52
|
+
if (input.extraField.length > 0) {
|
|
53
|
+
header.set(input.extraField, 46 + input.fileName.length);
|
|
54
|
+
}
|
|
55
|
+
if (input.comment.length > 0) {
|
|
56
|
+
header.set(input.comment, 46 + input.fileName.length + input.extraField.length);
|
|
57
|
+
}
|
|
58
|
+
return header;
|
|
59
|
+
}
|
|
60
|
+
export function buildEndOfCentralDirectory(input) {
|
|
61
|
+
const record = new Uint8Array(22 + input.comment.length);
|
|
62
|
+
const view = new DataView(record.buffer);
|
|
63
|
+
view.setUint32(0, END_OF_CENTRAL_DIR_SIG, true);
|
|
64
|
+
view.setUint16(4, 0, true);
|
|
65
|
+
view.setUint16(6, 0, true);
|
|
66
|
+
view.setUint16(8, input.entryCount, true);
|
|
67
|
+
view.setUint16(10, input.entryCount, true);
|
|
68
|
+
view.setUint32(12, input.centralDirSize, true);
|
|
69
|
+
view.setUint32(16, input.centralDirOffset, true);
|
|
70
|
+
view.setUint16(20, input.comment.length, true);
|
|
71
|
+
if (input.comment.length > 0) {
|
|
72
|
+
record.set(input.comment, 22);
|
|
73
|
+
}
|
|
74
|
+
return record;
|
|
75
|
+
}
|
|
76
|
+
export function buildDataDescriptor(crc32, compressedSize, uncompressedSize) {
|
|
77
|
+
const descriptor = new Uint8Array(16);
|
|
78
|
+
const view = new DataView(descriptor.buffer);
|
|
79
|
+
view.setUint32(0, DATA_DESCRIPTOR_SIG, true);
|
|
80
|
+
view.setUint32(4, crc32, true);
|
|
81
|
+
view.setUint32(8, compressedSize, true);
|
|
82
|
+
view.setUint32(12, uncompressedSize, true);
|
|
83
|
+
return descriptor;
|
|
84
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* with filesystem-specific features (filename input + temp-file buffering).
|
|
10
10
|
*/
|
|
11
11
|
import { EventEmitter, Readable } from "../../stream/index.js";
|
|
12
|
-
import { createParse } from "../../archive/
|
|
12
|
+
import { createParse } from "../../archive/parse.js";
|
|
13
13
|
import { iterateStream } from "../utils/iterate-stream.js";
|
|
14
14
|
import { getWorksheetNoFromWorksheetPath, getWorksheetNoFromWorksheetRelsPath, normalizeZipPath, OOXML_PATHS, worksheetRelTarget } from "../utils/ooxml-paths.js";
|
|
15
15
|
import { parseSax } from "../utils/parse-sax.js";
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Node.js uses `workbook-writer.ts`, which extends the same base implementation
|
|
8
8
|
* with filesystem-specific features (filename output + image loading).
|
|
9
9
|
*/
|
|
10
|
-
import { Zip, ZipDeflate } from "../../archive/zip
|
|
10
|
+
import { Zip, ZipDeflate } from "../../archive/streaming-zip.js";
|
|
11
11
|
import { StreamBuf } from "../utils/stream-buf.js";
|
|
12
12
|
import { base64ToUint8Array } from "../../../utils/utils.js";
|
|
13
13
|
import { RelType } from "../xlsx/rel-type.js";
|
|
@@ -30,8 +30,8 @@ import { theme1Xml } from "./xml/theme1.js";
|
|
|
30
30
|
import { RelType } from "./rel-type.js";
|
|
31
31
|
import { StreamBuf } from "../utils/stream-buf.js";
|
|
32
32
|
import { bufferToString, base64ToUint8Array } from "../../../utils/utils.js";
|
|
33
|
-
import { StreamingZip, ZipDeflateFile } from "../../archive/zip
|
|
34
|
-
import { ZipParser } from "../../archive/
|
|
33
|
+
import { StreamingZip, ZipDeflateFile } from "../../archive/streaming-zip.js";
|
|
34
|
+
import { ZipParser } from "../../archive/index.js";
|
|
35
35
|
import { PassThrough, concatUint8Arrays } from "../../stream/index.js";
|
|
36
36
|
import { commentsPath, commentsRelTargetFromWorksheetName, ctrlPropPath, drawingPath, drawingRelsPath, OOXML_REL_TARGETS, pivotTableRelTargetFromWorksheetName, pivotCacheDefinitionRelTargetFromWorkbook, getCommentsIndexFromPath, getDrawingNameFromPath, getDrawingNameFromRelsPath, getMediaFilenameFromPath, mediaPath, getPivotCacheDefinitionNameFromPath, getPivotCacheDefinitionNameFromRelsPath, getPivotCacheRecordsNameFromPath, getPivotTableNameFromPath, getPivotTableNameFromRelsPath, pivotCacheDefinitionPath, pivotCacheDefinitionRelsPath, pivotCacheDefinitionRelTargetFromPivotTable, pivotCacheRecordsPath, pivotCacheRecordsRelTarget, pivotTablePath, pivotTableRelsPath, getTableNameFromPath, tablePath, tableRelTargetFromWorksheetName, themePath, getThemeNameFromPath, getVmlDrawingNameFromPath, getWorksheetNoFromWorksheetPath, getWorksheetNoFromWorksheetRelsPath, isBinaryEntryPath, normalizeZipPath, OOXML_PATHS, vmlDrawingRelTargetFromWorksheetName, vmlDrawingPath, worksheetPath, worksheetRelsPath } from "../utils/ooxml-paths.js";
|
|
37
37
|
class StreamingZipWriterAdapter {
|
|
@@ -126,7 +126,10 @@ class StreamingZipWriterAdapter {
|
|
|
126
126
|
timestamps: this.timestamps
|
|
127
127
|
});
|
|
128
128
|
this.zip.add(file);
|
|
129
|
-
file.push(buffer, true);
|
|
129
|
+
const pushResult = file.push(buffer, true);
|
|
130
|
+
if (pushResult && typeof pushResult.catch === "function") {
|
|
131
|
+
pushResult.catch((err) => this._emit("error", err));
|
|
132
|
+
}
|
|
130
133
|
}
|
|
131
134
|
finalize() {
|
|
132
135
|
if (this.finalized) {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import fs from "fs";
|
|
17
17
|
import { fileExists } from "../../../utils/utils.js";
|
|
18
18
|
import { XLSX as XLSXBase } from "./xlsx.browser.js";
|
|
19
|
-
import { Parse } from "../../archive/
|
|
19
|
+
import { Parse } from "../../archive/index.js";
|
|
20
20
|
import { Writable, pipeline } from "../../stream/index.js";
|
|
21
21
|
class XLSX extends XLSXBase {
|
|
22
22
|
constructor(workbook) {
|