@rushstack/zipsync 0.1.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 (89) hide show
  1. package/CHANGELOG.json +31 -0
  2. package/CHANGELOG.md +11 -0
  3. package/LICENSE +24 -0
  4. package/README.md +48 -0
  5. package/bin/zipsync +2 -0
  6. package/lib/ZipSyncCommandLineParser.d.ts +17 -0
  7. package/lib/ZipSyncCommandLineParser.d.ts.map +1 -0
  8. package/lib/ZipSyncCommandLineParser.js +97 -0
  9. package/lib/ZipSyncCommandLineParser.js.map +1 -0
  10. package/lib/benchmark.test.d.ts +2 -0
  11. package/lib/benchmark.test.d.ts.map +1 -0
  12. package/lib/benchmark.test.js.map +1 -0
  13. package/lib/compress.d.ts +8 -0
  14. package/lib/compress.d.ts.map +1 -0
  15. package/lib/compress.js +121 -0
  16. package/lib/compress.js.map +1 -0
  17. package/lib/crc32.d.ts +3 -0
  18. package/lib/crc32.d.ts.map +1 -0
  19. package/lib/crc32.js +69 -0
  20. package/lib/crc32.js.map +1 -0
  21. package/lib/crc32.test.d.ts +2 -0
  22. package/lib/crc32.test.d.ts.map +1 -0
  23. package/lib/crc32.test.js.map +1 -0
  24. package/lib/fs.d.ts +13 -0
  25. package/lib/fs.d.ts.map +1 -0
  26. package/lib/fs.js +57 -0
  27. package/lib/fs.js.map +1 -0
  28. package/lib/hash.d.ts +3 -0
  29. package/lib/hash.d.ts.map +1 -0
  30. package/lib/hash.js +43 -0
  31. package/lib/hash.js.map +1 -0
  32. package/lib/index.d.ts +3 -0
  33. package/lib/index.d.ts.map +1 -0
  34. package/lib/index.js +10 -0
  35. package/lib/index.js.map +1 -0
  36. package/lib/index.test.d.ts +2 -0
  37. package/lib/index.test.d.ts.map +1 -0
  38. package/lib/index.test.js.map +1 -0
  39. package/lib/pack.d.ts +55 -0
  40. package/lib/pack.d.ts.map +1 -0
  41. package/lib/pack.js +415 -0
  42. package/lib/pack.js.map +1 -0
  43. package/lib/packWorker.d.ts +31 -0
  44. package/lib/packWorker.d.ts.map +1 -0
  45. package/lib/packWorker.js +60 -0
  46. package/lib/packWorker.js.map +1 -0
  47. package/lib/packWorkerAsync.d.ts +4 -0
  48. package/lib/packWorkerAsync.d.ts.map +1 -0
  49. package/lib/packWorkerAsync.js +79 -0
  50. package/lib/packWorkerAsync.js.map +1 -0
  51. package/lib/perf.d.ts +7 -0
  52. package/lib/perf.d.ts.map +1 -0
  53. package/lib/perf.js +56 -0
  54. package/lib/perf.js.map +1 -0
  55. package/lib/start.d.ts +2 -0
  56. package/lib/start.d.ts.map +1 -0
  57. package/lib/start.js +19 -0
  58. package/lib/start.js.map +1 -0
  59. package/lib/start.test.d.ts +2 -0
  60. package/lib/start.test.d.ts.map +1 -0
  61. package/lib/start.test.js.map +1 -0
  62. package/lib/testUtils.d.ts +9 -0
  63. package/lib/testUtils.d.ts.map +1 -0
  64. package/lib/testUtils.js +77 -0
  65. package/lib/testUtils.js.map +1 -0
  66. package/lib/unpack.d.ts +41 -0
  67. package/lib/unpack.d.ts.map +1 -0
  68. package/lib/unpack.js +370 -0
  69. package/lib/unpack.js.map +1 -0
  70. package/lib/unpackWorker.d.ts +31 -0
  71. package/lib/unpackWorker.d.ts.map +1 -0
  72. package/lib/unpackWorker.js +56 -0
  73. package/lib/unpackWorker.js.map +1 -0
  74. package/lib/unpackWorkerAsync.d.ts +4 -0
  75. package/lib/unpackWorkerAsync.d.ts.map +1 -0
  76. package/lib/unpackWorkerAsync.js +79 -0
  77. package/lib/unpackWorkerAsync.js.map +1 -0
  78. package/lib/workerAsync.test.d.ts +2 -0
  79. package/lib/workerAsync.test.d.ts.map +1 -0
  80. package/lib/workerAsync.test.js.map +1 -0
  81. package/lib/zipSyncUtils.d.ts +20 -0
  82. package/lib/zipSyncUtils.d.ts.map +1 -0
  83. package/lib/zipSyncUtils.js +9 -0
  84. package/lib/zipSyncUtils.js.map +1 -0
  85. package/lib/zipUtils.d.ts +127 -0
  86. package/lib/zipUtils.d.ts.map +1 -0
  87. package/lib/zipUtils.js +304 -0
  88. package/lib/zipUtils.js.map +1 -0
  89. package/package.json +31 -0
@@ -0,0 +1,20 @@
1
+ import type { IReadonlyPathTrieNode } from '@rushstack/lookup-by-path/lib/LookupByPath';
2
+ export declare const METADATA_FILENAME: string;
3
+ export declare const METADATA_VERSION: string;
4
+ export interface IDirQueueItem {
5
+ dir: string;
6
+ depth: number;
7
+ node?: IReadonlyPathTrieNode<boolean> | undefined;
8
+ }
9
+ export interface IMetadataFileRecord {
10
+ size: number;
11
+ sha1Hash: string;
12
+ }
13
+ export interface IMetadata {
14
+ version: string;
15
+ files: Record<string, IMetadataFileRecord>;
16
+ }
17
+ export type IZipSyncMode = 'pack' | 'unpack';
18
+ export type ZipSyncOptionCompression = 'store' | 'deflate' | 'zstd' | 'auto';
19
+ export declare const defaultBufferSize: number;
20
+ //# sourceMappingURL=zipSyncUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zipSyncUtils.d.ts","sourceRoot":"","sources":["../src/zipSyncUtils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AAExF,eAAO,MAAM,iBAAiB,EAAE,MAAoC,CAAC;AACrE,eAAO,MAAM,gBAAgB,EAAE,MAAc,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,qBAAqB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;CACnD;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CAC5C;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE7C,MAAM,MAAM,wBAAwB,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE7E,eAAO,MAAM,iBAAiB,EAAE,MAAgB,CAAC"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3
+ // See LICENSE in the project root for license information.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.defaultBufferSize = exports.METADATA_VERSION = exports.METADATA_FILENAME = void 0;
6
+ exports.METADATA_FILENAME = '__zipsync_metadata__.json';
7
+ exports.METADATA_VERSION = '1.0';
8
+ exports.defaultBufferSize = 1 << 25; // 32 MiB
9
+ //# sourceMappingURL=zipSyncUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zipSyncUtils.js","sourceRoot":"","sources":["../src/zipSyncUtils.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAI9C,QAAA,iBAAiB,GAAW,2BAA2B,CAAC;AACxD,QAAA,gBAAgB,GAAW,KAAK,CAAC;AAsBjC,QAAA,iBAAiB,GAAW,CAAC,IAAI,EAAE,CAAC,CAAC,SAAS","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport type { IReadonlyPathTrieNode } from '@rushstack/lookup-by-path/lib/LookupByPath';\n\nexport const METADATA_FILENAME: string = '__zipsync_metadata__.json';\nexport const METADATA_VERSION: string = '1.0';\n\nexport interface IDirQueueItem {\n dir: string;\n depth: number;\n node?: IReadonlyPathTrieNode<boolean> | undefined;\n}\n\nexport interface IMetadataFileRecord {\n size: number;\n sha1Hash: string;\n}\n\nexport interface IMetadata {\n version: string;\n files: Record<string, IMetadataFileRecord>;\n}\n\nexport type IZipSyncMode = 'pack' | 'unpack';\n\nexport type ZipSyncOptionCompression = 'store' | 'deflate' | 'zstd' | 'auto';\n\nexport const defaultBufferSize: number = 1 << 25; // 32 MiB\n"]}
@@ -0,0 +1,127 @@
1
+ export declare const STORE_COMPRESSION: 0;
2
+ export declare const DEFLATE_COMPRESSION: 8;
3
+ export declare const ZSTD_COMPRESSION: 93;
4
+ export type ZipMetaCompressionMethod = typeof STORE_COMPRESSION | typeof DEFLATE_COMPRESSION | typeof ZSTD_COMPRESSION;
5
+ export interface IFileEntry {
6
+ filename: string;
7
+ size: number;
8
+ compressedSize: number;
9
+ crc32: number;
10
+ sha1Hash: string;
11
+ localHeaderOffset: number;
12
+ compressionMethod: ZipMetaCompressionMethod;
13
+ dosDateTime: {
14
+ time: number;
15
+ date: number;
16
+ };
17
+ }
18
+ export interface ILocalFileHeader {
19
+ signature: number;
20
+ versionNeeded: number;
21
+ flags: number;
22
+ compressionMethod: number;
23
+ lastModTime: number;
24
+ lastModDate: number;
25
+ crc32: number;
26
+ compressedSize: number;
27
+ uncompressedSize: number;
28
+ filenameLength: number;
29
+ extraFieldLength: number;
30
+ }
31
+ export interface ICentralDirectoryHeader {
32
+ signature: number;
33
+ versionMadeBy: number;
34
+ versionNeeded: number;
35
+ flags: number;
36
+ compressionMethod: number;
37
+ lastModTime: number;
38
+ lastModDate: number;
39
+ crc32: number;
40
+ compressedSize: number;
41
+ uncompressedSize: number;
42
+ filenameLength: number;
43
+ extraFieldLength: number;
44
+ commentLength: number;
45
+ diskNumberStart: number;
46
+ internalFileAttributes: number;
47
+ externalFileAttributes: number;
48
+ localHeaderOffset: number;
49
+ }
50
+ export interface IEndOfCentralDirectory {
51
+ signature: number;
52
+ diskNumber: number;
53
+ centralDirStartDisk: number;
54
+ centralDirRecordsOnDisk: number;
55
+ totalCentralDirRecords: number;
56
+ centralDirSize: number;
57
+ centralDirOffset: number;
58
+ commentLength: number;
59
+ }
60
+ /**
61
+ * Convert a JS Date into packed DOS time/date fields used by classic ZIP.
62
+ * Seconds are stored /2 (range 0-29 => 0-58s). Years are offset from 1980.
63
+ */
64
+ export declare function dosDateTime(date: Date): {
65
+ time: number;
66
+ date: number;
67
+ };
68
+ /**
69
+ * Write the fixed portion of a local file header for an entry (with data descriptor flag set) and
70
+ * return the header buffer plus the variable-length filename buffer.
71
+ *
72
+ * Layout (little-endian):
73
+ * signature(4) versionNeeded(2) flags(2) method(2) modTime(2) modDate(2)
74
+ * crc32(4) compSize(4) uncompSize(4) nameLen(2) extraLen(2)
75
+ *
76
+ * Because we set bit 3 of the general purpose flag, crc32/compSize/uncompSize are zero here and the
77
+ * actual values appear later in a trailing data descriptor record. This enables streaming without
78
+ * buffering entire file contents beforehand.
79
+ */
80
+ export declare function writeLocalFileHeader(entry: IFileEntry): [fileHeaderWithoutVariableLengthData: Buffer, fileHeaderVariableLengthData: Buffer];
81
+ /**
82
+ * Write a central directory header referencing an already written local file entry.
83
+ * Central directory consolidates the final CRC + sizes (always present here) and provides a table
84
+ * for fast enumeration without scanning the archive sequentially.
85
+ */
86
+ export declare function writeCentralDirectoryHeader(entry: IFileEntry): Buffer[];
87
+ /**
88
+ * Write the trailing data descriptor for an entry. Only used because we set flag bit 3 in the
89
+ * local file header allowing deferred CRC/size calculation.
90
+ */
91
+ export declare function writeDataDescriptor(entry: IFileEntry): Buffer;
92
+ /**
93
+ * Write the EOCD record referencing the accumulated central directory. We omit archive comments
94
+ * and do not support ZIP64 (sufficient for build cache archive sizes today).
95
+ */
96
+ export declare function writeEndOfCentralDirectory(centralDirOffset: number, centralDirSize: number, entryCount: number): Buffer;
97
+ interface ILocalFileHeaderParseResult {
98
+ header: ILocalFileHeader;
99
+ nextOffset: number;
100
+ }
101
+ /**
102
+ * Parse a local file header at the provided offset. Minimal validation: signature check only.
103
+ * Returns header plus the offset pointing just past the variable-length name+extra field.
104
+ */
105
+ export declare function parseLocalFileHeader(buffer: Buffer, offset: number): ILocalFileHeaderParseResult;
106
+ export interface ICentralDirectoryHeaderParseResult {
107
+ header: ICentralDirectoryHeader;
108
+ filename: string;
109
+ nextOffset: number;
110
+ }
111
+ /**
112
+ * Parse a central directory header at the given offset (within a sliced central directory buffer).
113
+ * Returns header, filename string, and nextOffset pointing to the next structure.
114
+ */
115
+ export declare function parseCentralDirectoryHeader(buffer: Buffer, offset: number): ICentralDirectoryHeaderParseResult;
116
+ /**
117
+ * Locate the EOCD record by reverse scanning. Since we never write a comment the EOCD will be the
118
+ * first matching signature encountered scanning backwards from the end.
119
+ */
120
+ export declare function findEndOfCentralDirectory(buffer: Buffer): IEndOfCentralDirectory;
121
+ /**
122
+ * Slice out the (possibly compressed) file data bytes for a central directory entry.
123
+ * Caller will decompress if needed based on entry.header.compressionMethod.
124
+ */
125
+ export declare function getFileFromZip(zipBuffer: Buffer, entry: ICentralDirectoryHeaderParseResult): Buffer;
126
+ export {};
127
+ //# sourceMappingURL=zipUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zipUtils.d.ts","sourceRoot":"","sources":["../src/zipUtils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3
+ // See LICENSE in the project root for license information.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.ZSTD_COMPRESSION = exports.DEFLATE_COMPRESSION = exports.STORE_COMPRESSION = void 0;
6
+ exports.dosDateTime = dosDateTime;
7
+ exports.writeLocalFileHeader = writeLocalFileHeader;
8
+ exports.writeCentralDirectoryHeader = writeCentralDirectoryHeader;
9
+ exports.writeDataDescriptor = writeDataDescriptor;
10
+ exports.writeEndOfCentralDirectory = writeEndOfCentralDirectory;
11
+ exports.parseLocalFileHeader = parseLocalFileHeader;
12
+ exports.parseCentralDirectoryHeader = parseCentralDirectoryHeader;
13
+ exports.findEndOfCentralDirectory = findEndOfCentralDirectory;
14
+ exports.getFileFromZip = getFileFromZip;
15
+ /**
16
+ * Low-level ZIP structure helpers used by the zipsync pack/unpack pipeline.
17
+ *
18
+ * Spec reference: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
19
+ */
20
+ /**
21
+ * Local file header signature PK\x03\x04
22
+ */
23
+ const LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50; // PK\x03\x04
24
+ /**
25
+ * Central directory file header signature PK\x01\x02
26
+ */
27
+ const CENTRAL_DIR_HEADER_SIGNATURE = 0x02014b50; // PK\x01\x02
28
+ /**
29
+ * End of central directory signature PK\x05\x06
30
+ */
31
+ const END_OF_CENTRAL_DIR_SIGNATURE = 0x06054b50; // PK\x05\x06
32
+ /**
33
+ * Data descriptor signature PK\x07\x08
34
+ */
35
+ const DATA_DESCRIPTOR_SIGNATURE = 0x08074b50; // PK\x07\x08
36
+ exports.STORE_COMPRESSION = 0;
37
+ exports.DEFLATE_COMPRESSION = 8;
38
+ exports.ZSTD_COMPRESSION = 93;
39
+ function writeUInt32LE(buffer, value, offset) {
40
+ buffer.writeUInt32LE(value, offset);
41
+ }
42
+ function writeUInt16LE(buffer, value, offset) {
43
+ buffer.writeUInt16LE(value, offset);
44
+ }
45
+ function readUInt32LE(buffer, offset) {
46
+ return buffer.readUInt32LE(offset);
47
+ }
48
+ function readUInt16LE(buffer, offset) {
49
+ return buffer.readUInt16LE(offset);
50
+ }
51
+ /**
52
+ * Convert a JS Date into packed DOS time/date fields used by classic ZIP.
53
+ * Seconds are stored /2 (range 0-29 => 0-58s). Years are offset from 1980.
54
+ */
55
+ function dosDateTime(date) {
56
+ /* eslint-disable no-bitwise */
57
+ const time = ((date.getHours() & 0x1f) << 11) | ((date.getMinutes() & 0x3f) << 5) | ((date.getSeconds() / 2) & 0x1f);
58
+ const dateVal = (((date.getFullYear() - 1980) & 0x7f) << 9) |
59
+ (((date.getMonth() + 1) & 0xf) << 5) |
60
+ (date.getDate() & 0x1f);
61
+ /* eslint-enable no-bitwise */
62
+ return { time, date: dateVal };
63
+ }
64
+ /**
65
+ * Reusable scratch buffer for the fixed-length local file header (30 bytes).
66
+ * Using a single Buffer avoids per-file allocations; callers must copy/use synchronously.
67
+ */
68
+ const localFileHeaderBuffer = Buffer.allocUnsafe(30);
69
+ /**
70
+ * Write the fixed portion of a local file header for an entry (with data descriptor flag set) and
71
+ * return the header buffer plus the variable-length filename buffer.
72
+ *
73
+ * Layout (little-endian):
74
+ * signature(4) versionNeeded(2) flags(2) method(2) modTime(2) modDate(2)
75
+ * crc32(4) compSize(4) uncompSize(4) nameLen(2) extraLen(2)
76
+ *
77
+ * Because we set bit 3 of the general purpose flag, crc32/compSize/uncompSize are zero here and the
78
+ * actual values appear later in a trailing data descriptor record. This enables streaming without
79
+ * buffering entire file contents beforehand.
80
+ */
81
+ function writeLocalFileHeader(entry) {
82
+ const filenameBuffer = Buffer.from(entry.filename, 'utf8');
83
+ const { time, date } = entry.dosDateTime;
84
+ let offset = 0;
85
+ writeUInt32LE(localFileHeaderBuffer, LOCAL_FILE_HEADER_SIGNATURE, offset);
86
+ offset += 4;
87
+ writeUInt16LE(localFileHeaderBuffer, 20, offset); // version needed
88
+ offset += 2;
89
+ // General purpose bit flag: set bit 3 (0x0008) to indicate presence of data descriptor
90
+ // Per APPNOTE: when bit 3 is set, CRC-32 and sizes in local header are set to zero and
91
+ // the actual values are stored in the data descriptor that follows the file data.
92
+ writeUInt16LE(localFileHeaderBuffer, 0x0008, offset); // flags (data descriptor)
93
+ offset += 2;
94
+ writeUInt16LE(localFileHeaderBuffer, entry.compressionMethod, offset); // compression method (0=store,8=deflate)
95
+ offset += 2;
96
+ writeUInt16LE(localFileHeaderBuffer, time, offset); // last mod time
97
+ offset += 2;
98
+ writeUInt16LE(localFileHeaderBuffer, date, offset); // last mod date
99
+ offset += 2;
100
+ // With bit 3 set, these three fields MUST be zero in the local header
101
+ writeUInt32LE(localFileHeaderBuffer, 0, offset); // crc32 (placeholder, real value in data descriptor)
102
+ offset += 4;
103
+ writeUInt32LE(localFileHeaderBuffer, 0, offset); // compressed size (placeholder)
104
+ offset += 4;
105
+ writeUInt32LE(localFileHeaderBuffer, 0, offset); // uncompressed size (placeholder)
106
+ offset += 4;
107
+ writeUInt16LE(localFileHeaderBuffer, filenameBuffer.length, offset); // filename length
108
+ offset += 2;
109
+ writeUInt16LE(localFileHeaderBuffer, 0, offset); // extra field length
110
+ offset += 2;
111
+ return [localFileHeaderBuffer, filenameBuffer];
112
+ }
113
+ /**
114
+ * Reusable scratch buffer for central directory entries (fixed-length 46 bytes before filename)
115
+ */
116
+ const centralDirHeaderBuffer = Buffer.allocUnsafe(46);
117
+ /**
118
+ * Write a central directory header referencing an already written local file entry.
119
+ * Central directory consolidates the final CRC + sizes (always present here) and provides a table
120
+ * for fast enumeration without scanning the archive sequentially.
121
+ */
122
+ function writeCentralDirectoryHeader(entry) {
123
+ const filenameBuffer = Buffer.from(entry.filename, 'utf8');
124
+ const now = new Date();
125
+ const { time, date } = dosDateTime(now);
126
+ let offset = 0;
127
+ writeUInt32LE(centralDirHeaderBuffer, CENTRAL_DIR_HEADER_SIGNATURE, offset);
128
+ offset += 4;
129
+ writeUInt16LE(centralDirHeaderBuffer, 20, offset); // version made by
130
+ offset += 2;
131
+ writeUInt16LE(centralDirHeaderBuffer, 20, offset); // version needed
132
+ offset += 2;
133
+ // Mirror flags used in local header (bit 3 set to indicate data descriptor was used)
134
+ writeUInt16LE(centralDirHeaderBuffer, 0x0008, offset); // flags
135
+ offset += 2;
136
+ writeUInt16LE(centralDirHeaderBuffer, entry.compressionMethod, offset); // compression method
137
+ offset += 2;
138
+ writeUInt16LE(centralDirHeaderBuffer, time, offset); // last mod time
139
+ offset += 2;
140
+ writeUInt16LE(centralDirHeaderBuffer, date, offset); // last mod date
141
+ offset += 2;
142
+ writeUInt32LE(centralDirHeaderBuffer, entry.crc32, offset); // crc32
143
+ offset += 4;
144
+ writeUInt32LE(centralDirHeaderBuffer, entry.compressedSize, offset); // compressed size
145
+ offset += 4;
146
+ writeUInt32LE(centralDirHeaderBuffer, entry.size, offset); // uncompressed size
147
+ offset += 4;
148
+ writeUInt16LE(centralDirHeaderBuffer, filenameBuffer.length, offset); // filename length
149
+ offset += 2;
150
+ writeUInt16LE(centralDirHeaderBuffer, 0, offset); // extra field length
151
+ offset += 2;
152
+ writeUInt16LE(centralDirHeaderBuffer, 0, offset); // comment length
153
+ offset += 2;
154
+ writeUInt16LE(centralDirHeaderBuffer, 0, offset); // disk number start
155
+ offset += 2;
156
+ writeUInt16LE(centralDirHeaderBuffer, 0, offset); // internal file attributes
157
+ offset += 2;
158
+ writeUInt32LE(centralDirHeaderBuffer, 0, offset); // external file attributes
159
+ offset += 4;
160
+ writeUInt32LE(centralDirHeaderBuffer, entry.localHeaderOffset, offset); // local header offset
161
+ offset += 4;
162
+ return [centralDirHeaderBuffer, filenameBuffer];
163
+ }
164
+ /**
165
+ * Data descriptor: signature(4) crc32(4) compSize(4) uncompSize(4)
166
+ */
167
+ const dataDescriptorBuffer = Buffer.allocUnsafe(16);
168
+ /**
169
+ * Write the trailing data descriptor for an entry. Only used because we set flag bit 3 in the
170
+ * local file header allowing deferred CRC/size calculation.
171
+ */
172
+ function writeDataDescriptor(entry) {
173
+ let offset = 0;
174
+ writeUInt32LE(dataDescriptorBuffer, DATA_DESCRIPTOR_SIGNATURE, offset); // signature PK\x07\x08
175
+ offset += 4;
176
+ writeUInt32LE(dataDescriptorBuffer, entry.crc32, offset); // crc32
177
+ offset += 4;
178
+ writeUInt32LE(dataDescriptorBuffer, entry.compressedSize, offset); // compressed size
179
+ offset += 4;
180
+ writeUInt32LE(dataDescriptorBuffer, entry.size, offset); // uncompressed size
181
+ return dataDescriptorBuffer;
182
+ }
183
+ /**
184
+ * End of central directory (EOCD) record (22 bytes when comment length = 0)
185
+ */
186
+ const endOfCentralDirBuffer = Buffer.allocUnsafe(22);
187
+ /**
188
+ * Write the EOCD record referencing the accumulated central directory. We omit archive comments
189
+ * and do not support ZIP64 (sufficient for build cache archive sizes today).
190
+ */
191
+ function writeEndOfCentralDirectory(centralDirOffset, centralDirSize, entryCount) {
192
+ let offset = 0;
193
+ writeUInt32LE(endOfCentralDirBuffer, END_OF_CENTRAL_DIR_SIGNATURE, offset);
194
+ offset += 4;
195
+ writeUInt16LE(endOfCentralDirBuffer, 0, offset); // disk number
196
+ offset += 2;
197
+ writeUInt16LE(endOfCentralDirBuffer, 0, offset); // central dir start disk
198
+ offset += 2;
199
+ writeUInt16LE(endOfCentralDirBuffer, entryCount, offset); // central dir records on disk
200
+ offset += 2;
201
+ writeUInt16LE(endOfCentralDirBuffer, entryCount, offset); // total central dir records
202
+ offset += 2;
203
+ writeUInt32LE(endOfCentralDirBuffer, centralDirSize, offset); // central dir size
204
+ offset += 4;
205
+ writeUInt32LE(endOfCentralDirBuffer, centralDirOffset, offset); // central dir offset
206
+ offset += 4;
207
+ writeUInt16LE(endOfCentralDirBuffer, 0, offset); // comment length
208
+ return endOfCentralDirBuffer;
209
+ }
210
+ /**
211
+ * Parse a local file header at the provided offset. Minimal validation: signature check only.
212
+ * Returns header plus the offset pointing just past the variable-length name+extra field.
213
+ */
214
+ function parseLocalFileHeader(buffer, offset) {
215
+ const signature = readUInt32LE(buffer, offset);
216
+ if (signature !== LOCAL_FILE_HEADER_SIGNATURE) {
217
+ throw new Error(`Unexpected local file header signature at offset ${offset.toString(16)}: ${signature.toString(16)}`);
218
+ }
219
+ const header = {
220
+ signature,
221
+ versionNeeded: readUInt16LE(buffer, offset + 4),
222
+ flags: readUInt16LE(buffer, offset + 6),
223
+ compressionMethod: readUInt16LE(buffer, offset + 8),
224
+ lastModTime: readUInt16LE(buffer, offset + 10),
225
+ lastModDate: readUInt16LE(buffer, offset + 12),
226
+ crc32: readUInt32LE(buffer, offset + 14),
227
+ compressedSize: readUInt32LE(buffer, offset + 18),
228
+ uncompressedSize: readUInt32LE(buffer, offset + 22),
229
+ filenameLength: readUInt16LE(buffer, offset + 26),
230
+ extraFieldLength: readUInt16LE(buffer, offset + 28)
231
+ };
232
+ return {
233
+ header,
234
+ nextOffset: offset + 30 + header.filenameLength + header.extraFieldLength
235
+ };
236
+ }
237
+ /**
238
+ * Parse a central directory header at the given offset (within a sliced central directory buffer).
239
+ * Returns header, filename string, and nextOffset pointing to the next structure.
240
+ */
241
+ function parseCentralDirectoryHeader(buffer, offset) {
242
+ const signature = readUInt32LE(buffer, offset);
243
+ if (signature !== CENTRAL_DIR_HEADER_SIGNATURE) {
244
+ throw new Error(`Unexpected central directory signature at offset ${offset.toString(16)}: ${signature.toString(16)}`);
245
+ }
246
+ const header = {
247
+ signature,
248
+ versionMadeBy: readUInt16LE(buffer, offset + 4),
249
+ versionNeeded: readUInt16LE(buffer, offset + 6),
250
+ flags: readUInt16LE(buffer, offset + 8),
251
+ compressionMethod: readUInt16LE(buffer, offset + 10),
252
+ lastModTime: readUInt16LE(buffer, offset + 12),
253
+ lastModDate: readUInt16LE(buffer, offset + 14),
254
+ crc32: readUInt32LE(buffer, offset + 16),
255
+ compressedSize: readUInt32LE(buffer, offset + 20),
256
+ uncompressedSize: readUInt32LE(buffer, offset + 24),
257
+ filenameLength: readUInt16LE(buffer, offset + 28),
258
+ extraFieldLength: readUInt16LE(buffer, offset + 30),
259
+ commentLength: readUInt16LE(buffer, offset + 32),
260
+ diskNumberStart: readUInt16LE(buffer, offset + 34),
261
+ internalFileAttributes: readUInt16LE(buffer, offset + 36),
262
+ externalFileAttributes: readUInt32LE(buffer, offset + 38),
263
+ localHeaderOffset: readUInt32LE(buffer, offset + 42)
264
+ };
265
+ offset += 46;
266
+ const filename = buffer.toString('utf8', offset, offset + header.filenameLength);
267
+ return {
268
+ header,
269
+ filename,
270
+ nextOffset: offset + header.filenameLength + header.extraFieldLength + header.commentLength
271
+ };
272
+ }
273
+ /**
274
+ * Locate the EOCD record by reverse scanning. Since we never write a comment the EOCD will be the
275
+ * first matching signature encountered scanning backwards from the end.
276
+ */
277
+ function findEndOfCentralDirectory(buffer) {
278
+ for (let i = buffer.length - 22; i >= 0; i--) {
279
+ if (readUInt32LE(buffer, i) === END_OF_CENTRAL_DIR_SIGNATURE) {
280
+ return {
281
+ signature: readUInt32LE(buffer, i),
282
+ diskNumber: readUInt16LE(buffer, i + 4),
283
+ centralDirStartDisk: readUInt16LE(buffer, i + 6),
284
+ centralDirRecordsOnDisk: readUInt16LE(buffer, i + 8),
285
+ totalCentralDirRecords: readUInt16LE(buffer, i + 10),
286
+ centralDirSize: readUInt32LE(buffer, i + 12),
287
+ centralDirOffset: readUInt32LE(buffer, i + 16),
288
+ commentLength: readUInt16LE(buffer, i + 20)
289
+ };
290
+ }
291
+ }
292
+ throw new Error('End of central directory not found');
293
+ }
294
+ /**
295
+ * Slice out the (possibly compressed) file data bytes for a central directory entry.
296
+ * Caller will decompress if needed based on entry.header.compressionMethod.
297
+ */
298
+ function getFileFromZip(zipBuffer, entry) {
299
+ const { header: localFileHeader } = parseLocalFileHeader(zipBuffer, entry.header.localHeaderOffset);
300
+ const localDataOffset = entry.header.localHeaderOffset + 30 + localFileHeader.filenameLength + localFileHeader.extraFieldLength;
301
+ const fileZipBuffer = zipBuffer.subarray(localDataOffset, localDataOffset + entry.header.compressedSize);
302
+ return fileZipBuffer;
303
+ }
304
+ //# sourceMappingURL=zipUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zipUtils.js","sourceRoot":"","sources":["../src/zipUtils.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * Low-level ZIP structure helpers used by the zipsync pack/unpack pipeline.\n *\n * Spec reference: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT\n */\n\n/**\n * Local file header signature PK\\x03\\x04\n */\nconst LOCAL_FILE_HEADER_SIGNATURE: number = 0x04034b50; // PK\\x03\\x04\n/**\n * Central directory file header signature PK\\x01\\x02\n */\nconst CENTRAL_DIR_HEADER_SIGNATURE: number = 0x02014b50; // PK\\x01\\x02\n/**\n * End of central directory signature PK\\x05\\x06\n */\nconst END_OF_CENTRAL_DIR_SIGNATURE: number = 0x06054b50; // PK\\x05\\x06\n/**\n * Data descriptor signature PK\\x07\\x08\n */\nconst DATA_DESCRIPTOR_SIGNATURE: number = 0x08074b50; // PK\\x07\\x08\n\nexport const STORE_COMPRESSION: 0 = 0;\nexport const DEFLATE_COMPRESSION: 8 = 8;\nexport const ZSTD_COMPRESSION: 93 = 93;\nexport type ZipMetaCompressionMethod =\n | typeof STORE_COMPRESSION\n | typeof DEFLATE_COMPRESSION\n | typeof ZSTD_COMPRESSION;\n\nexport interface IFileEntry {\n filename: string;\n size: number;\n compressedSize: number;\n crc32: number;\n sha1Hash: string;\n localHeaderOffset: number;\n compressionMethod: ZipMetaCompressionMethod;\n dosDateTime: { time: number; date: number };\n}\n\nexport interface ILocalFileHeader {\n signature: number;\n versionNeeded: number;\n flags: number;\n compressionMethod: number;\n lastModTime: number;\n lastModDate: number;\n crc32: number;\n compressedSize: number;\n uncompressedSize: number;\n filenameLength: number;\n extraFieldLength: number;\n}\n\nexport interface ICentralDirectoryHeader {\n signature: number;\n versionMadeBy: number;\n versionNeeded: number;\n flags: number;\n compressionMethod: number;\n lastModTime: number;\n lastModDate: number;\n crc32: number;\n compressedSize: number;\n uncompressedSize: number;\n filenameLength: number;\n extraFieldLength: number;\n commentLength: number;\n diskNumberStart: number;\n internalFileAttributes: number;\n externalFileAttributes: number;\n localHeaderOffset: number;\n}\n\nexport interface IEndOfCentralDirectory {\n signature: number;\n diskNumber: number;\n centralDirStartDisk: number;\n centralDirRecordsOnDisk: number;\n totalCentralDirRecords: number;\n centralDirSize: number;\n centralDirOffset: number;\n commentLength: number;\n}\n\nfunction writeUInt32LE(buffer: Buffer, value: number, offset: number): void {\n buffer.writeUInt32LE(value, offset);\n}\n\nfunction writeUInt16LE(buffer: Buffer, value: number, offset: number): void {\n buffer.writeUInt16LE(value, offset);\n}\n\nfunction readUInt32LE(buffer: Buffer, offset: number): number {\n return buffer.readUInt32LE(offset);\n}\n\nfunction readUInt16LE(buffer: Buffer, offset: number): number {\n return buffer.readUInt16LE(offset);\n}\n\n/**\n * Convert a JS Date into packed DOS time/date fields used by classic ZIP.\n * Seconds are stored /2 (range 0-29 => 0-58s). Years are offset from 1980.\n */\nexport function dosDateTime(date: Date): { time: number; date: number } {\n /* eslint-disable no-bitwise */\n const time: number =\n ((date.getHours() & 0x1f) << 11) | ((date.getMinutes() & 0x3f) << 5) | ((date.getSeconds() / 2) & 0x1f);\n\n const dateVal: number =\n (((date.getFullYear() - 1980) & 0x7f) << 9) |\n (((date.getMonth() + 1) & 0xf) << 5) |\n (date.getDate() & 0x1f);\n /* eslint-enable no-bitwise */\n\n return { time, date: dateVal };\n}\n\n/**\n * Reusable scratch buffer for the fixed-length local file header (30 bytes).\n * Using a single Buffer avoids per-file allocations; callers must copy/use synchronously.\n */\nconst localFileHeaderBuffer: Buffer = Buffer.allocUnsafe(30);\n/**\n * Write the fixed portion of a local file header for an entry (with data descriptor flag set) and\n * return the header buffer plus the variable-length filename buffer.\n *\n * Layout (little-endian):\n * signature(4) versionNeeded(2) flags(2) method(2) modTime(2) modDate(2)\n * crc32(4) compSize(4) uncompSize(4) nameLen(2) extraLen(2)\n *\n * Because we set bit 3 of the general purpose flag, crc32/compSize/uncompSize are zero here and the\n * actual values appear later in a trailing data descriptor record. This enables streaming without\n * buffering entire file contents beforehand.\n */\nexport function writeLocalFileHeader(\n entry: IFileEntry\n): [fileHeaderWithoutVariableLengthData: Buffer, fileHeaderVariableLengthData: Buffer] {\n const filenameBuffer: Buffer = Buffer.from(entry.filename, 'utf8');\n\n const { time, date } = entry.dosDateTime;\n\n let offset: number = 0;\n writeUInt32LE(localFileHeaderBuffer, LOCAL_FILE_HEADER_SIGNATURE, offset);\n offset += 4;\n writeUInt16LE(localFileHeaderBuffer, 20, offset); // version needed\n offset += 2;\n // General purpose bit flag: set bit 3 (0x0008) to indicate presence of data descriptor\n // Per APPNOTE: when bit 3 is set, CRC-32 and sizes in local header are set to zero and\n // the actual values are stored in the data descriptor that follows the file data.\n writeUInt16LE(localFileHeaderBuffer, 0x0008, offset); // flags (data descriptor)\n offset += 2;\n writeUInt16LE(localFileHeaderBuffer, entry.compressionMethod, offset); // compression method (0=store,8=deflate)\n offset += 2;\n writeUInt16LE(localFileHeaderBuffer, time, offset); // last mod time\n offset += 2;\n writeUInt16LE(localFileHeaderBuffer, date, offset); // last mod date\n offset += 2;\n // With bit 3 set, these three fields MUST be zero in the local header\n writeUInt32LE(localFileHeaderBuffer, 0, offset); // crc32 (placeholder, real value in data descriptor)\n offset += 4;\n writeUInt32LE(localFileHeaderBuffer, 0, offset); // compressed size (placeholder)\n offset += 4;\n writeUInt32LE(localFileHeaderBuffer, 0, offset); // uncompressed size (placeholder)\n offset += 4;\n writeUInt16LE(localFileHeaderBuffer, filenameBuffer.length, offset); // filename length\n offset += 2;\n writeUInt16LE(localFileHeaderBuffer, 0, offset); // extra field length\n offset += 2;\n\n return [localFileHeaderBuffer, filenameBuffer];\n}\n\n/**\n * Reusable scratch buffer for central directory entries (fixed-length 46 bytes before filename)\n */\nconst centralDirHeaderBuffer: Buffer = Buffer.allocUnsafe(46);\n/**\n * Write a central directory header referencing an already written local file entry.\n * Central directory consolidates the final CRC + sizes (always present here) and provides a table\n * for fast enumeration without scanning the archive sequentially.\n */\nexport function writeCentralDirectoryHeader(entry: IFileEntry): Buffer[] {\n const filenameBuffer: Buffer = Buffer.from(entry.filename, 'utf8');\n\n const now: Date = new Date();\n const { time, date } = dosDateTime(now);\n\n let offset: number = 0;\n writeUInt32LE(centralDirHeaderBuffer, CENTRAL_DIR_HEADER_SIGNATURE, offset);\n offset += 4;\n writeUInt16LE(centralDirHeaderBuffer, 20, offset); // version made by\n offset += 2;\n writeUInt16LE(centralDirHeaderBuffer, 20, offset); // version needed\n offset += 2;\n // Mirror flags used in local header (bit 3 set to indicate data descriptor was used)\n writeUInt16LE(centralDirHeaderBuffer, 0x0008, offset); // flags\n offset += 2;\n writeUInt16LE(centralDirHeaderBuffer, entry.compressionMethod, offset); // compression method\n offset += 2;\n writeUInt16LE(centralDirHeaderBuffer, time, offset); // last mod time\n offset += 2;\n writeUInt16LE(centralDirHeaderBuffer, date, offset); // last mod date\n offset += 2;\n writeUInt32LE(centralDirHeaderBuffer, entry.crc32, offset); // crc32\n offset += 4;\n writeUInt32LE(centralDirHeaderBuffer, entry.compressedSize, offset); // compressed size\n offset += 4;\n writeUInt32LE(centralDirHeaderBuffer, entry.size, offset); // uncompressed size\n offset += 4;\n writeUInt16LE(centralDirHeaderBuffer, filenameBuffer.length, offset); // filename length\n offset += 2;\n writeUInt16LE(centralDirHeaderBuffer, 0, offset); // extra field length\n offset += 2;\n writeUInt16LE(centralDirHeaderBuffer, 0, offset); // comment length\n offset += 2;\n writeUInt16LE(centralDirHeaderBuffer, 0, offset); // disk number start\n offset += 2;\n writeUInt16LE(centralDirHeaderBuffer, 0, offset); // internal file attributes\n offset += 2;\n writeUInt32LE(centralDirHeaderBuffer, 0, offset); // external file attributes\n offset += 4;\n writeUInt32LE(centralDirHeaderBuffer, entry.localHeaderOffset, offset); // local header offset\n offset += 4;\n\n return [centralDirHeaderBuffer, filenameBuffer];\n}\n\n/**\n * Data descriptor: signature(4) crc32(4) compSize(4) uncompSize(4)\n */\nconst dataDescriptorBuffer: Buffer = Buffer.allocUnsafe(16);\n/**\n * Write the trailing data descriptor for an entry. Only used because we set flag bit 3 in the\n * local file header allowing deferred CRC/size calculation.\n */\nexport function writeDataDescriptor(entry: IFileEntry): Buffer {\n let offset: number = 0;\n writeUInt32LE(dataDescriptorBuffer, DATA_DESCRIPTOR_SIGNATURE, offset); // signature PK\\x07\\x08\n offset += 4;\n writeUInt32LE(dataDescriptorBuffer, entry.crc32, offset); // crc32\n offset += 4;\n writeUInt32LE(dataDescriptorBuffer, entry.compressedSize, offset); // compressed size\n offset += 4;\n writeUInt32LE(dataDescriptorBuffer, entry.size, offset); // uncompressed size\n return dataDescriptorBuffer;\n}\n\n/**\n * End of central directory (EOCD) record (22 bytes when comment length = 0)\n */\nconst endOfCentralDirBuffer: Buffer = Buffer.allocUnsafe(22);\n/**\n * Write the EOCD record referencing the accumulated central directory. We omit archive comments\n * and do not support ZIP64 (sufficient for build cache archive sizes today).\n */\nexport function writeEndOfCentralDirectory(\n centralDirOffset: number,\n centralDirSize: number,\n entryCount: number\n): Buffer {\n let offset: number = 0;\n writeUInt32LE(endOfCentralDirBuffer, END_OF_CENTRAL_DIR_SIGNATURE, offset);\n offset += 4;\n writeUInt16LE(endOfCentralDirBuffer, 0, offset); // disk number\n offset += 2;\n writeUInt16LE(endOfCentralDirBuffer, 0, offset); // central dir start disk\n offset += 2;\n writeUInt16LE(endOfCentralDirBuffer, entryCount, offset); // central dir records on disk\n offset += 2;\n writeUInt16LE(endOfCentralDirBuffer, entryCount, offset); // total central dir records\n offset += 2;\n writeUInt32LE(endOfCentralDirBuffer, centralDirSize, offset); // central dir size\n offset += 4;\n writeUInt32LE(endOfCentralDirBuffer, centralDirOffset, offset); // central dir offset\n offset += 4;\n writeUInt16LE(endOfCentralDirBuffer, 0, offset); // comment length\n\n return endOfCentralDirBuffer;\n}\n\ninterface ILocalFileHeaderParseResult {\n header: ILocalFileHeader;\n nextOffset: number;\n}\n\n/**\n * Parse a local file header at the provided offset. Minimal validation: signature check only.\n * Returns header plus the offset pointing just past the variable-length name+extra field.\n */\nexport function parseLocalFileHeader(buffer: Buffer, offset: number): ILocalFileHeaderParseResult {\n const signature: number = readUInt32LE(buffer, offset);\n if (signature !== LOCAL_FILE_HEADER_SIGNATURE) {\n throw new Error(\n `Unexpected local file header signature at offset ${offset.toString(16)}: ${signature.toString(16)}`\n );\n }\n const header: ILocalFileHeader = {\n signature,\n versionNeeded: readUInt16LE(buffer, offset + 4),\n flags: readUInt16LE(buffer, offset + 6),\n compressionMethod: readUInt16LE(buffer, offset + 8),\n lastModTime: readUInt16LE(buffer, offset + 10),\n lastModDate: readUInt16LE(buffer, offset + 12),\n crc32: readUInt32LE(buffer, offset + 14),\n compressedSize: readUInt32LE(buffer, offset + 18),\n uncompressedSize: readUInt32LE(buffer, offset + 22),\n filenameLength: readUInt16LE(buffer, offset + 26),\n extraFieldLength: readUInt16LE(buffer, offset + 28)\n };\n\n return {\n header,\n nextOffset: offset + 30 + header.filenameLength + header.extraFieldLength\n };\n}\n\nexport interface ICentralDirectoryHeaderParseResult {\n header: ICentralDirectoryHeader;\n filename: string;\n nextOffset: number;\n}\n\n/**\n * Parse a central directory header at the given offset (within a sliced central directory buffer).\n * Returns header, filename string, and nextOffset pointing to the next structure.\n */\nexport function parseCentralDirectoryHeader(\n buffer: Buffer,\n offset: number\n): ICentralDirectoryHeaderParseResult {\n const signature: number = readUInt32LE(buffer, offset);\n if (signature !== CENTRAL_DIR_HEADER_SIGNATURE) {\n throw new Error(\n `Unexpected central directory signature at offset ${offset.toString(16)}: ${signature.toString(16)}`\n );\n }\n const header: ICentralDirectoryHeader = {\n signature,\n versionMadeBy: readUInt16LE(buffer, offset + 4),\n versionNeeded: readUInt16LE(buffer, offset + 6),\n flags: readUInt16LE(buffer, offset + 8),\n compressionMethod: readUInt16LE(buffer, offset + 10),\n lastModTime: readUInt16LE(buffer, offset + 12),\n lastModDate: readUInt16LE(buffer, offset + 14),\n crc32: readUInt32LE(buffer, offset + 16),\n compressedSize: readUInt32LE(buffer, offset + 20),\n uncompressedSize: readUInt32LE(buffer, offset + 24),\n filenameLength: readUInt16LE(buffer, offset + 28),\n extraFieldLength: readUInt16LE(buffer, offset + 30),\n commentLength: readUInt16LE(buffer, offset + 32),\n diskNumberStart: readUInt16LE(buffer, offset + 34),\n internalFileAttributes: readUInt16LE(buffer, offset + 36),\n externalFileAttributes: readUInt32LE(buffer, offset + 38),\n localHeaderOffset: readUInt32LE(buffer, offset + 42)\n };\n\n offset += 46;\n\n const filename: string = buffer.toString('utf8', offset, offset + header.filenameLength);\n\n return {\n header,\n filename,\n nextOffset: offset + header.filenameLength + header.extraFieldLength + header.commentLength\n };\n}\n\n/**\n * Locate the EOCD record by reverse scanning. Since we never write a comment the EOCD will be the\n * first matching signature encountered scanning backwards from the end.\n */\nexport function findEndOfCentralDirectory(buffer: Buffer): IEndOfCentralDirectory {\n for (let i: number = buffer.length - 22; i >= 0; i--) {\n if (readUInt32LE(buffer, i) === END_OF_CENTRAL_DIR_SIGNATURE) {\n return {\n signature: readUInt32LE(buffer, i),\n diskNumber: readUInt16LE(buffer, i + 4),\n centralDirStartDisk: readUInt16LE(buffer, i + 6),\n centralDirRecordsOnDisk: readUInt16LE(buffer, i + 8),\n totalCentralDirRecords: readUInt16LE(buffer, i + 10),\n centralDirSize: readUInt32LE(buffer, i + 12),\n centralDirOffset: readUInt32LE(buffer, i + 16),\n commentLength: readUInt16LE(buffer, i + 20)\n };\n }\n }\n\n throw new Error('End of central directory not found');\n}\n\n/**\n * Slice out the (possibly compressed) file data bytes for a central directory entry.\n * Caller will decompress if needed based on entry.header.compressionMethod.\n */\nexport function getFileFromZip(zipBuffer: Buffer, entry: ICentralDirectoryHeaderParseResult): Buffer {\n const { header: localFileHeader } = parseLocalFileHeader(zipBuffer, entry.header.localHeaderOffset);\n const localDataOffset: number =\n entry.header.localHeaderOffset + 30 + localFileHeader.filenameLength + localFileHeader.extraFieldLength;\n const fileZipBuffer: Buffer = zipBuffer.subarray(\n localDataOffset,\n localDataOffset + entry.header.compressedSize\n );\n return fileZipBuffer;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@rushstack/zipsync",
3
+ "version": "0.1.0",
4
+ "description": "CLI tool for creating and extracting ZIP archives with intelligent filesystem synchronization",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/microsoft/rushstack.git",
8
+ "directory": "apps/zipsync"
9
+ },
10
+ "bin": {
11
+ "zipsync": "./bin/zipsync"
12
+ },
13
+ "license": "MIT",
14
+ "dependencies": {
15
+ "typescript": "~5.8.2",
16
+ "@rushstack/terminal": "0.17.0",
17
+ "@rushstack/ts-command-line": "5.0.4",
18
+ "@rushstack/lookup-by-path": "0.8.0"
19
+ },
20
+ "devDependencies": {
21
+ "eslint": "~9.25.1",
22
+ "local-node-rig": "1.0.0",
23
+ "@rushstack/heft": "0.75.0"
24
+ },
25
+ "scripts": {
26
+ "start": "node lib/start",
27
+ "build": "heft build --clean",
28
+ "_phase:build": "heft run --only build -- --clean",
29
+ "_phase:test": "heft run --only test -- --clean"
30
+ }
31
+ }