neozipkit 1.0.2 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NEOZIP_APPNOTE.md +625 -0
- package/README.md +44 -11
- package/WHATS_NEW.md +153 -10
- package/dist/browser/ZipkitBrowser.d.ts.map +1 -1
- package/dist/browser/ZipkitBrowser.js +4 -6
- package/dist/browser/ZipkitBrowser.js.map +1 -1
- package/dist/browser/zstd-browser-shim.d.ts +17 -9
- package/dist/browser/zstd-browser-shim.d.ts.map +1 -1
- package/dist/browser/zstd-browser-shim.js +99 -21
- package/dist/browser/zstd-browser-shim.js.map +1 -1
- package/dist/browser-esm/index.js +6534 -1275
- package/dist/browser-esm/index.js.map +4 -4
- package/dist/browser-umd/index.js +6534 -1275
- package/dist/browser-umd/index.js.map +4 -4
- package/dist/browser-umd/index.min.js +6 -3
- package/dist/core/ZipCompress.d.ts +6 -0
- package/dist/core/ZipCompress.d.ts.map +1 -1
- package/dist/core/ZipCompress.js +12 -11
- package/dist/core/ZipCompress.js.map +1 -1
- package/dist/core/ZipCopy.d.ts.map +1 -1
- package/dist/core/ZipCopy.js +33 -25
- package/dist/core/ZipCopy.js.map +1 -1
- package/dist/core/ZipDecompress.d.ts.map +1 -1
- package/dist/core/ZipDecompress.js +13 -8
- package/dist/core/ZipDecompress.js.map +1 -1
- package/dist/core/ZipEntry.d.ts +24 -0
- package/dist/core/ZipEntry.d.ts.map +1 -1
- package/dist/core/ZipEntry.js +146 -14
- package/dist/core/ZipEntry.js.map +1 -1
- package/dist/core/Zipkit.d.ts +43 -11
- package/dist/core/Zipkit.d.ts.map +1 -1
- package/dist/core/Zipkit.js +132 -82
- package/dist/core/Zipkit.js.map +1 -1
- package/dist/core/components/HashCalculator.d.ts +22 -1
- package/dist/core/components/HashCalculator.d.ts.map +1 -1
- package/dist/core/components/HashCalculator.js +49 -6
- package/dist/core/components/HashCalculator.js.map +1 -1
- package/dist/core/components/Util.d.ts +3 -4
- package/dist/core/components/Util.d.ts.map +1 -1
- package/dist/core/components/Util.js +10 -8
- package/dist/core/components/Util.js.map +1 -1
- package/dist/core/constants/Headers.d.ts +2 -3
- package/dist/core/constants/Headers.d.ts.map +1 -1
- package/dist/core/constants/Headers.js +15 -5
- package/dist/core/constants/Headers.js.map +1 -1
- package/dist/core/constants/MetaPaths.d.ts +38 -0
- package/dist/core/constants/MetaPaths.d.ts.map +1 -0
- package/dist/core/constants/MetaPaths.js +79 -0
- package/dist/core/constants/MetaPaths.js.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +18 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/inflateZipPayload.d.ts +6 -0
- package/dist/core/inflateZipPayload.d.ts.map +1 -0
- package/dist/core/inflateZipPayload.js +28 -0
- package/dist/core/inflateZipPayload.js.map +1 -0
- package/dist/core/merkle/MerkleRoot.d.ts +109 -0
- package/dist/core/merkle/MerkleRoot.d.ts.map +1 -0
- package/dist/core/merkle/MerkleRoot.js +275 -0
- package/dist/core/merkle/MerkleRoot.js.map +1 -0
- package/dist/core/origin/OriginExtra.d.ts +43 -0
- package/dist/core/origin/OriginExtra.d.ts.map +1 -0
- package/dist/core/origin/OriginExtra.js +187 -0
- package/dist/core/origin/OriginExtra.js.map +1 -0
- package/dist/core/version.js +1 -1
- package/dist/core/zip64/Zip64.d.ts +138 -0
- package/dist/core/zip64/Zip64.d.ts.map +1 -0
- package/dist/core/zip64/Zip64.js +353 -0
- package/dist/core/zip64/Zip64.js.map +1 -0
- package/dist/node/ZipCompressNode.d.ts.map +1 -1
- package/dist/node/ZipCompressNode.js +40 -49
- package/dist/node/ZipCompressNode.js.map +1 -1
- package/dist/node/ZipCopyNode.d.ts.map +1 -1
- package/dist/node/ZipCopyNode.js +26 -26
- package/dist/node/ZipCopyNode.js.map +1 -1
- package/dist/node/ZipDecompressNode.d.ts.map +1 -1
- package/dist/node/ZipDecompressNode.js +21 -3
- package/dist/node/ZipDecompressNode.js.map +1 -1
- package/dist/node/ZipkitNode.d.ts +23 -1
- package/dist/node/ZipkitNode.d.ts.map +1 -1
- package/dist/node/ZipkitNode.js +158 -57
- package/dist/node/ZipkitNode.js.map +1 -1
- package/dist/node/ZstdNode.d.ts +14 -4
- package/dist/node/ZstdNode.d.ts.map +1 -1
- package/dist/node/ZstdNode.js +28 -4
- package/dist/node/ZstdNode.js.map +1 -1
- package/dist/node/buildZipBuffer.d.ts +49 -0
- package/dist/node/buildZipBuffer.d.ts.map +1 -0
- package/dist/node/buildZipBuffer.js +146 -0
- package/dist/node/buildZipBuffer.js.map +1 -0
- package/dist/node/index.d.ts +3 -0
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +6 -1
- package/dist/node/index.js.map +1 -1
- package/dist/node/inflateZipPayload.d.ts +5 -0
- package/dist/node/inflateZipPayload.d.ts.map +1 -0
- package/dist/node/inflateZipPayload.js +21 -0
- package/dist/node/inflateZipPayload.js.map +1 -0
- package/dist/types/index.js +2 -2
- package/node-esm.mjs +32 -0
- package/package.json +13 -6
- package/src/browser/ZipkitBrowser.ts +4 -6
- package/src/browser/zstd-browser-shim.ts +110 -28
- package/src/core/ZipCompress.ts +20 -13
- package/src/core/ZipCopy.ts +56 -27
- package/src/core/ZipDecompress.ts +14 -8
- package/src/core/ZipEntry.ts +200 -14
- package/src/core/Zipkit.ts +189 -98
- package/src/core/components/HashCalculator.ts +58 -6
- package/src/core/components/Util.ts +16 -7
- package/src/core/constants/Headers.ts +15 -4
- package/src/core/constants/MetaPaths.ts +86 -0
- package/src/core/index.ts +17 -0
- package/src/core/inflateZipPayload.ts +27 -0
- package/src/core/merkle/MerkleRoot.ts +312 -0
- package/src/core/origin/OriginExtra.ts +209 -0
- package/src/core/version.ts +1 -1
- package/src/core/zip64/Zip64.ts +473 -0
- package/src/node/ZipCompressNode.ts +40 -49
- package/src/node/ZipCopyNode.ts +31 -29
- package/src/node/ZipDecompressNode.ts +16 -3
- package/src/node/ZipkitNode.ts +203 -72
- package/src/node/ZstdNode.ts +33 -8
- package/src/node/buildZipBuffer.ts +221 -0
- package/src/node/index.ts +7 -0
- package/src/node/inflateZipPayload.ts +19 -0
- package/src/types/index.ts +2 -2
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory and streaming ZIP writer.
|
|
3
|
+
* Zstd level is the raw zlib level (ZipWiki default 7). NeoEncrypt stays off.
|
|
4
|
+
* Caller extra blocks (ZipWiki 0x014F) are written beside optional 0x014E.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { closeSync, openSync, writeSync } from 'fs';
|
|
8
|
+
import { deflateRawSync } from 'zlib';
|
|
9
|
+
import ZipEntry from '../core/ZipEntry';
|
|
10
|
+
import { crc32, sha256 } from '../core/encryption/ZipCrypto';
|
|
11
|
+
import { compressSyncAtLevel } from './ZstdNode';
|
|
12
|
+
import {
|
|
13
|
+
assertBufferAllowsEntryZip64,
|
|
14
|
+
buildEndRecords,
|
|
15
|
+
} from '../core/zip64/Zip64';
|
|
16
|
+
|
|
17
|
+
export type ZipPrecompressedMember = {
|
|
18
|
+
method: number;
|
|
19
|
+
data: Buffer;
|
|
20
|
+
crc32: number;
|
|
21
|
+
uncompressedSize: number;
|
|
22
|
+
/** Complete extra-field bytes copied onto the local and central headers. */
|
|
23
|
+
extra?: Buffer;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type ZipBufferMember = {
|
|
27
|
+
name: string;
|
|
28
|
+
/** Uncompressed payload. Unused as stored bytes when `precompressed` is set. */
|
|
29
|
+
data: Buffer;
|
|
30
|
+
mtime?: Date;
|
|
31
|
+
/** DOS time word. Wins over `mtime` when set together with `dosDate`. */
|
|
32
|
+
dosTime?: number;
|
|
33
|
+
dosDate?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Complete extra-field records (id, size, payload), e.g. ZipWiki 0x014F.
|
|
36
|
+
* Ignored when `precompressed.extra` is set — that blob is copied as-is.
|
|
37
|
+
*/
|
|
38
|
+
additionalExtra?: Buffer;
|
|
39
|
+
/** Write Extra Field 0x014E (SHA-256 of uncompressed `data`). */
|
|
40
|
+
useSHA256?: boolean;
|
|
41
|
+
/** 0 store, 8 deflate, 93 zstd. Default 93. Ignored when `precompressed` is set. */
|
|
42
|
+
method?: 0 | 8 | 93;
|
|
43
|
+
/** zlib zstd level or raw deflate level. Default 7. */
|
|
44
|
+
level?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Force Zip64 Extra `0x0001` on this member even when sizes fit classic fields.
|
|
47
|
+
* Pair with `forceZip64` on `buildZipBufferSync` / `writeZipFileSync` options
|
|
48
|
+
* (or set on every member) so the EOCD is Zip64 as well.
|
|
49
|
+
*/
|
|
50
|
+
forceZip64?: boolean;
|
|
51
|
+
precompressed?: ZipPrecompressedMember;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
type FramedMember = {
|
|
55
|
+
local: Buffer;
|
|
56
|
+
payload: Buffer;
|
|
57
|
+
central: Buffer;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function dosFromDate(date: Date): { time: number; date: number } {
|
|
61
|
+
const year = date.getFullYear();
|
|
62
|
+
const month = date.getMonth() + 1;
|
|
63
|
+
const day = date.getDate();
|
|
64
|
+
const hours = date.getHours();
|
|
65
|
+
const minutes = date.getMinutes();
|
|
66
|
+
const seconds = Math.floor(date.getSeconds() / 2);
|
|
67
|
+
const y = Math.max(0, Math.min(127, year - 1980));
|
|
68
|
+
return {
|
|
69
|
+
time: ((hours & 0x1f) << 11) | ((minutes & 0x3f) << 5) | (seconds & 0x1f),
|
|
70
|
+
date: ((y & 0x7f) << 9) | ((month & 0x0f) << 5) | (day & 0x1f),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function compressPayload(
|
|
75
|
+
data: Buffer,
|
|
76
|
+
method: 0 | 8 | 93,
|
|
77
|
+
level: number,
|
|
78
|
+
): { method: number; data: Buffer } {
|
|
79
|
+
if (method === 0 || data.length === 0) {
|
|
80
|
+
return { method: 0, data };
|
|
81
|
+
}
|
|
82
|
+
const compressed =
|
|
83
|
+
method === 8
|
|
84
|
+
? deflateRawSync(data, { level: Math.max(1, Math.min(9, level)) })
|
|
85
|
+
: compressSyncAtLevel(data, Math.max(1, level));
|
|
86
|
+
if (compressed.length >= data.length) {
|
|
87
|
+
return { method: 0, data };
|
|
88
|
+
}
|
|
89
|
+
return { method, data: compressed };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function frameMembers(
|
|
93
|
+
members: ZipBufferMember[],
|
|
94
|
+
opts?: { forceZip64?: boolean }
|
|
95
|
+
): FramedMember[] {
|
|
96
|
+
const packTime = new Date();
|
|
97
|
+
const framed: FramedMember[] = [];
|
|
98
|
+
let offset = 0;
|
|
99
|
+
const forceAll = opts?.forceZip64 === true;
|
|
100
|
+
for (const member of members) {
|
|
101
|
+
const copied = member.precompressed;
|
|
102
|
+
const uncompressed = member.data ?? Buffer.alloc(0);
|
|
103
|
+
const level = member.level ?? 7;
|
|
104
|
+
const requested = member.method ?? 93;
|
|
105
|
+
const payload = copied
|
|
106
|
+
? {
|
|
107
|
+
method: copied.method,
|
|
108
|
+
data: copied.data,
|
|
109
|
+
uncompressedSize: copied.uncompressedSize,
|
|
110
|
+
}
|
|
111
|
+
: {
|
|
112
|
+
...compressPayload(uncompressed, requested, level),
|
|
113
|
+
uncompressedSize: uncompressed.length,
|
|
114
|
+
};
|
|
115
|
+
const crc = copied ? copied.crc32 >>> 0 : crc32(uncompressed) >>> 0;
|
|
116
|
+
const extra = copied?.extra ?? member.additionalExtra;
|
|
117
|
+
const dos =
|
|
118
|
+
member.dosTime !== undefined && member.dosDate !== undefined
|
|
119
|
+
? { time: member.dosTime, date: member.dosDate }
|
|
120
|
+
: dosFromDate(member.mtime ?? packTime);
|
|
121
|
+
|
|
122
|
+
const entry = new ZipEntry(member.name, null, false);
|
|
123
|
+
entry.bitFlags = 0;
|
|
124
|
+
entry.emitUnicodePath = false;
|
|
125
|
+
entry.isUpdated = true;
|
|
126
|
+
entry.filename = member.name;
|
|
127
|
+
entry.cmpMethod = payload.method;
|
|
128
|
+
entry.crc = crc;
|
|
129
|
+
entry.compressedSize = payload.data.length;
|
|
130
|
+
entry.uncompressedSize = payload.uncompressedSize;
|
|
131
|
+
entry.timeDateDOS = ((dos.date & 0xffff) << 16) | (dos.time & 0xffff);
|
|
132
|
+
entry.localHdrOffset = offset;
|
|
133
|
+
entry.forceZip64 = forceAll || member.forceZip64 === true;
|
|
134
|
+
assertBufferAllowsEntryZip64(
|
|
135
|
+
entry.uncompressedSize,
|
|
136
|
+
entry.compressedSize,
|
|
137
|
+
entry.localHdrOffset
|
|
138
|
+
);
|
|
139
|
+
if (!copied && member.useSHA256 === true) {
|
|
140
|
+
entry.sha256 = sha256(uncompressed);
|
|
141
|
+
}
|
|
142
|
+
if (extra && extra.length > 0) {
|
|
143
|
+
entry.additionalExtra = extra;
|
|
144
|
+
}
|
|
145
|
+
const local = entry.createLocalHdr();
|
|
146
|
+
const central = entry.centralDirEntry();
|
|
147
|
+
framed.push({ local, payload: payload.data, central });
|
|
148
|
+
offset += local.length + payload.data.length;
|
|
149
|
+
}
|
|
150
|
+
return framed;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function eocd(
|
|
154
|
+
entryCount: number,
|
|
155
|
+
centralSize: number,
|
|
156
|
+
centralOffset: number,
|
|
157
|
+
forceZip64?: boolean
|
|
158
|
+
): Buffer {
|
|
159
|
+
return buildEndRecords({
|
|
160
|
+
totalEntries: entryCount,
|
|
161
|
+
centralDirSize: centralSize,
|
|
162
|
+
centralDirOffset: centralOffset,
|
|
163
|
+
zip64EocdOffset: centralOffset + centralSize,
|
|
164
|
+
allowSizeOffsetZip64: false,
|
|
165
|
+
forceZip64: forceZip64 === true,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export type BuildZipBufferOptions = {
|
|
170
|
+
/** Force Zip64 EOCD + per-member Extra when sizes fit classic fields. */
|
|
171
|
+
forceZip64?: boolean;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/** Build a ZIP in one buffer. Kept for tests and atomic in-memory rewrites. */
|
|
175
|
+
export function buildZipBufferSync(
|
|
176
|
+
members: ZipBufferMember[],
|
|
177
|
+
options?: BuildZipBufferOptions
|
|
178
|
+
): Buffer {
|
|
179
|
+
const forceZip64 = options?.forceZip64 === true;
|
|
180
|
+
const framed = frameMembers(members, { forceZip64 });
|
|
181
|
+
const parts: Buffer[] = [];
|
|
182
|
+
let centralSize = 0;
|
|
183
|
+
let payloadBytes = 0;
|
|
184
|
+
for (const part of framed) {
|
|
185
|
+
parts.push(part.local, part.payload);
|
|
186
|
+
payloadBytes += part.local.length + part.payload.length;
|
|
187
|
+
}
|
|
188
|
+
for (const part of framed) {
|
|
189
|
+
parts.push(part.central);
|
|
190
|
+
centralSize += part.central.length;
|
|
191
|
+
}
|
|
192
|
+
parts.push(eocd(framed.length, centralSize, payloadBytes, forceZip64));
|
|
193
|
+
return Buffer.concat(parts);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Stream local headers, payloads, the central directory, and the EOCD to `filePath`. */
|
|
197
|
+
export function writeZipFileSync(
|
|
198
|
+
filePath: string,
|
|
199
|
+
members: ZipBufferMember[],
|
|
200
|
+
options?: BuildZipBufferOptions
|
|
201
|
+
): void {
|
|
202
|
+
const forceZip64 = options?.forceZip64 === true;
|
|
203
|
+
const framed = frameMembers(members, { forceZip64 });
|
|
204
|
+
const fd = openSync(filePath, 'w');
|
|
205
|
+
try {
|
|
206
|
+
let payloadBytes = 0;
|
|
207
|
+
let centralSize = 0;
|
|
208
|
+
for (const part of framed) {
|
|
209
|
+
writeSync(fd, part.local);
|
|
210
|
+
writeSync(fd, part.payload);
|
|
211
|
+
payloadBytes += part.local.length + part.payload.length;
|
|
212
|
+
}
|
|
213
|
+
for (const part of framed) {
|
|
214
|
+
writeSync(fd, part.central);
|
|
215
|
+
centralSize += part.central.length;
|
|
216
|
+
}
|
|
217
|
+
writeSync(fd, eocd(framed.length, centralSize, payloadBytes, forceZip64));
|
|
218
|
+
} finally {
|
|
219
|
+
closeSync(fd);
|
|
220
|
+
}
|
|
221
|
+
}
|
package/src/node/index.ts
CHANGED
|
@@ -34,6 +34,13 @@ export type { LegacyZstdDetectResult, LegacyZstdConfidence, DetectLegacyZstdOpti
|
|
|
34
34
|
|
|
35
35
|
// Efficient ZIP copying using ZipEntry directly
|
|
36
36
|
export { ZipCopyNode } from './ZipCopyNode';
|
|
37
|
+
export { buildZipBufferSync, writeZipFileSync } from './buildZipBuffer';
|
|
38
|
+
export type {
|
|
39
|
+
ZipBufferMember,
|
|
40
|
+
ZipPrecompressedMember,
|
|
41
|
+
BuildZipBufferOptions,
|
|
42
|
+
} from './buildZipBuffer';
|
|
43
|
+
export { inflateZipPayloadSync } from './inflateZipPayload';
|
|
37
44
|
export type {
|
|
38
45
|
CopyOptions,
|
|
39
46
|
CopyResult,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synchronous inflate for Node readers (store, raw deflate, native zstd).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { inflateRawSync } from 'zlib';
|
|
6
|
+
import { decompressSync } from './ZstdNode';
|
|
7
|
+
|
|
8
|
+
export function inflateZipPayloadSync(method: number, compressed: Buffer): Buffer {
|
|
9
|
+
if (method === 0) {
|
|
10
|
+
return Buffer.from(compressed);
|
|
11
|
+
}
|
|
12
|
+
if (method === 8) {
|
|
13
|
+
return inflateRawSync(compressed);
|
|
14
|
+
}
|
|
15
|
+
if (method === 93) {
|
|
16
|
+
return decompressSync(compressed);
|
|
17
|
+
}
|
|
18
|
+
throw new Error(`unsupported compression method ${method}`);
|
|
19
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -8,8 +8,8 @@ export interface NeoZipKitInfo {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export const NEOZIPKIT_INFO: NeoZipKitInfo = {
|
|
11
|
-
version: '
|
|
12
|
-
releaseDate: '2026-
|
|
11
|
+
version: '1.5.0',
|
|
12
|
+
releaseDate: '2026-10-25'
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
// ============================================================================
|