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.
Files changed (128) hide show
  1. package/NEOZIP_APPNOTE.md +625 -0
  2. package/README.md +44 -11
  3. package/WHATS_NEW.md +153 -10
  4. package/dist/browser/ZipkitBrowser.d.ts.map +1 -1
  5. package/dist/browser/ZipkitBrowser.js +4 -6
  6. package/dist/browser/ZipkitBrowser.js.map +1 -1
  7. package/dist/browser/zstd-browser-shim.d.ts +17 -9
  8. package/dist/browser/zstd-browser-shim.d.ts.map +1 -1
  9. package/dist/browser/zstd-browser-shim.js +99 -21
  10. package/dist/browser/zstd-browser-shim.js.map +1 -1
  11. package/dist/browser-esm/index.js +6534 -1275
  12. package/dist/browser-esm/index.js.map +4 -4
  13. package/dist/browser-umd/index.js +6534 -1275
  14. package/dist/browser-umd/index.js.map +4 -4
  15. package/dist/browser-umd/index.min.js +6 -3
  16. package/dist/core/ZipCompress.d.ts +6 -0
  17. package/dist/core/ZipCompress.d.ts.map +1 -1
  18. package/dist/core/ZipCompress.js +12 -11
  19. package/dist/core/ZipCompress.js.map +1 -1
  20. package/dist/core/ZipCopy.d.ts.map +1 -1
  21. package/dist/core/ZipCopy.js +33 -25
  22. package/dist/core/ZipCopy.js.map +1 -1
  23. package/dist/core/ZipDecompress.d.ts.map +1 -1
  24. package/dist/core/ZipDecompress.js +13 -8
  25. package/dist/core/ZipDecompress.js.map +1 -1
  26. package/dist/core/ZipEntry.d.ts +24 -0
  27. package/dist/core/ZipEntry.d.ts.map +1 -1
  28. package/dist/core/ZipEntry.js +146 -14
  29. package/dist/core/ZipEntry.js.map +1 -1
  30. package/dist/core/Zipkit.d.ts +43 -11
  31. package/dist/core/Zipkit.d.ts.map +1 -1
  32. package/dist/core/Zipkit.js +132 -82
  33. package/dist/core/Zipkit.js.map +1 -1
  34. package/dist/core/components/HashCalculator.d.ts +22 -1
  35. package/dist/core/components/HashCalculator.d.ts.map +1 -1
  36. package/dist/core/components/HashCalculator.js +49 -6
  37. package/dist/core/components/HashCalculator.js.map +1 -1
  38. package/dist/core/components/Util.d.ts +3 -4
  39. package/dist/core/components/Util.d.ts.map +1 -1
  40. package/dist/core/components/Util.js +10 -8
  41. package/dist/core/components/Util.js.map +1 -1
  42. package/dist/core/constants/Headers.d.ts +2 -3
  43. package/dist/core/constants/Headers.d.ts.map +1 -1
  44. package/dist/core/constants/Headers.js +15 -5
  45. package/dist/core/constants/Headers.js.map +1 -1
  46. package/dist/core/constants/MetaPaths.d.ts +38 -0
  47. package/dist/core/constants/MetaPaths.d.ts.map +1 -0
  48. package/dist/core/constants/MetaPaths.js +79 -0
  49. package/dist/core/constants/MetaPaths.js.map +1 -0
  50. package/dist/core/index.d.ts +5 -0
  51. package/dist/core/index.d.ts.map +1 -1
  52. package/dist/core/index.js +18 -1
  53. package/dist/core/index.js.map +1 -1
  54. package/dist/core/inflateZipPayload.d.ts +6 -0
  55. package/dist/core/inflateZipPayload.d.ts.map +1 -0
  56. package/dist/core/inflateZipPayload.js +28 -0
  57. package/dist/core/inflateZipPayload.js.map +1 -0
  58. package/dist/core/merkle/MerkleRoot.d.ts +109 -0
  59. package/dist/core/merkle/MerkleRoot.d.ts.map +1 -0
  60. package/dist/core/merkle/MerkleRoot.js +275 -0
  61. package/dist/core/merkle/MerkleRoot.js.map +1 -0
  62. package/dist/core/origin/OriginExtra.d.ts +43 -0
  63. package/dist/core/origin/OriginExtra.d.ts.map +1 -0
  64. package/dist/core/origin/OriginExtra.js +187 -0
  65. package/dist/core/origin/OriginExtra.js.map +1 -0
  66. package/dist/core/version.js +1 -1
  67. package/dist/core/zip64/Zip64.d.ts +138 -0
  68. package/dist/core/zip64/Zip64.d.ts.map +1 -0
  69. package/dist/core/zip64/Zip64.js +353 -0
  70. package/dist/core/zip64/Zip64.js.map +1 -0
  71. package/dist/node/ZipCompressNode.d.ts.map +1 -1
  72. package/dist/node/ZipCompressNode.js +40 -49
  73. package/dist/node/ZipCompressNode.js.map +1 -1
  74. package/dist/node/ZipCopyNode.d.ts.map +1 -1
  75. package/dist/node/ZipCopyNode.js +26 -26
  76. package/dist/node/ZipCopyNode.js.map +1 -1
  77. package/dist/node/ZipDecompressNode.d.ts.map +1 -1
  78. package/dist/node/ZipDecompressNode.js +21 -3
  79. package/dist/node/ZipDecompressNode.js.map +1 -1
  80. package/dist/node/ZipkitNode.d.ts +23 -1
  81. package/dist/node/ZipkitNode.d.ts.map +1 -1
  82. package/dist/node/ZipkitNode.js +158 -57
  83. package/dist/node/ZipkitNode.js.map +1 -1
  84. package/dist/node/ZstdNode.d.ts +14 -4
  85. package/dist/node/ZstdNode.d.ts.map +1 -1
  86. package/dist/node/ZstdNode.js +28 -4
  87. package/dist/node/ZstdNode.js.map +1 -1
  88. package/dist/node/buildZipBuffer.d.ts +49 -0
  89. package/dist/node/buildZipBuffer.d.ts.map +1 -0
  90. package/dist/node/buildZipBuffer.js +146 -0
  91. package/dist/node/buildZipBuffer.js.map +1 -0
  92. package/dist/node/index.d.ts +3 -0
  93. package/dist/node/index.d.ts.map +1 -1
  94. package/dist/node/index.js +6 -1
  95. package/dist/node/index.js.map +1 -1
  96. package/dist/node/inflateZipPayload.d.ts +5 -0
  97. package/dist/node/inflateZipPayload.d.ts.map +1 -0
  98. package/dist/node/inflateZipPayload.js +21 -0
  99. package/dist/node/inflateZipPayload.js.map +1 -0
  100. package/dist/types/index.js +2 -2
  101. package/node-esm.mjs +32 -0
  102. package/package.json +13 -6
  103. package/src/browser/ZipkitBrowser.ts +4 -6
  104. package/src/browser/zstd-browser-shim.ts +110 -28
  105. package/src/core/ZipCompress.ts +20 -13
  106. package/src/core/ZipCopy.ts +56 -27
  107. package/src/core/ZipDecompress.ts +14 -8
  108. package/src/core/ZipEntry.ts +200 -14
  109. package/src/core/Zipkit.ts +189 -98
  110. package/src/core/components/HashCalculator.ts +58 -6
  111. package/src/core/components/Util.ts +16 -7
  112. package/src/core/constants/Headers.ts +15 -4
  113. package/src/core/constants/MetaPaths.ts +86 -0
  114. package/src/core/index.ts +17 -0
  115. package/src/core/inflateZipPayload.ts +27 -0
  116. package/src/core/merkle/MerkleRoot.ts +312 -0
  117. package/src/core/origin/OriginExtra.ts +209 -0
  118. package/src/core/version.ts +1 -1
  119. package/src/core/zip64/Zip64.ts +473 -0
  120. package/src/node/ZipCompressNode.ts +40 -49
  121. package/src/node/ZipCopyNode.ts +31 -29
  122. package/src/node/ZipDecompressNode.ts +16 -3
  123. package/src/node/ZipkitNode.ts +203 -72
  124. package/src/node/ZstdNode.ts +33 -8
  125. package/src/node/buildZipBuffer.ts +221 -0
  126. package/src/node/index.ts +7 -0
  127. package/src/node/inflateZipPayload.ts +19 -0
  128. 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
+ }
@@ -8,8 +8,8 @@ export interface NeoZipKitInfo {
8
8
  }
9
9
 
10
10
  export const NEOZIPKIT_INFO: NeoZipKitInfo = {
11
- version: '0.7.0',
12
- releaseDate: '2026-04-06'
11
+ version: '1.5.0',
12
+ releaseDate: '2026-10-25'
13
13
  };
14
14
 
15
15
  // ============================================================================