@js-ak/excel-toolbox 1.2.6 โ†’ 1.2.7

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/README.md CHANGED
@@ -39,11 +39,11 @@ fs.writeFileSync("output.xlsx", resultBuffer);
39
39
  - ๐Ÿงฉ **Merge sheets** from multiple Excel files
40
40
  - ๐Ÿงผ **Clean sheet removal** โ€” by name or index
41
41
  - ๐Ÿ“Ž **Keeps styles and merged cells**
42
- - ๐Ÿชถ **Lightweight ZIP and XML handling**
42
+ - ๐Ÿƒ **Lightweight ZIP and XML handling**
43
43
 
44
44
  ## API
45
45
 
46
- ### `mergeSheetsToBaseFileSync(options)`
46
+ ### `mergeSheetsToBaseFileSync(options): Buffer`
47
47
 
48
48
  #### Parameters
49
49
 
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createSync = createSync;
4
4
  const node_buffer_1 = require("node:buffer");
5
5
  const node_zlib_1 = require("node:zlib");
6
- const utils_js_1 = require("./utils.js");
6
+ const index_js_1 = require("./utils/index.js");
7
7
  const constants_js_1 = require("./constants.js");
8
8
  /**
9
9
  * Creates a ZIP archive from a collection of files.
@@ -21,23 +21,23 @@ function createSync(files) {
21
21
  }
22
22
  const content = node_buffer_1.Buffer.isBuffer(rawContent) ? rawContent : node_buffer_1.Buffer.from(rawContent);
23
23
  const fileNameBuf = node_buffer_1.Buffer.from(filename, "utf8");
24
- const modTime = (0, utils_js_1.dosTime)(new Date());
25
- const crc = (0, utils_js_1.crc32)(content);
24
+ const modTime = (0, index_js_1.dosTime)(new Date());
25
+ const crc = (0, index_js_1.crc32)(content);
26
26
  const compressed = (0, node_zlib_1.deflateRawSync)(content);
27
27
  const compSize = compressed.length;
28
28
  const uncompSize = content.length;
29
29
  // Local file header
30
30
  const localHeader = node_buffer_1.Buffer.concat([
31
31
  constants_js_1.LOCAL_FILE_HEADER_SIG,
32
- (0, utils_js_1.toBytes)(20, 2),
33
- (0, utils_js_1.toBytes)(0, 2),
34
- (0, utils_js_1.toBytes)(8, 2),
32
+ (0, index_js_1.toBytes)(20, 2),
33
+ (0, index_js_1.toBytes)(0, 2),
34
+ (0, index_js_1.toBytes)(8, 2),
35
35
  modTime,
36
- (0, utils_js_1.toBytes)(crc, 4),
37
- (0, utils_js_1.toBytes)(compSize, 4),
38
- (0, utils_js_1.toBytes)(uncompSize, 4),
39
- (0, utils_js_1.toBytes)(fileNameBuf.length, 2),
40
- (0, utils_js_1.toBytes)(0, 2),
36
+ (0, index_js_1.toBytes)(crc, 4),
37
+ (0, index_js_1.toBytes)(compSize, 4),
38
+ (0, index_js_1.toBytes)(uncompSize, 4),
39
+ (0, index_js_1.toBytes)(fileNameBuf.length, 2),
40
+ (0, index_js_1.toBytes)(0, 2),
41
41
  ]);
42
42
  const localEntry = node_buffer_1.Buffer.concat([
43
43
  localHeader,
@@ -47,21 +47,21 @@ function createSync(files) {
47
47
  fileEntries.push(localEntry);
48
48
  const centralEntry = node_buffer_1.Buffer.concat([
49
49
  node_buffer_1.Buffer.from(constants_js_1.CENTRAL_DIR_HEADER_SIG),
50
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(20, 2)), // Version made by
51
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(20, 2)), // Version needed
52
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Flags
53
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(8, 2)), // Compression
50
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(20, 2)), // Version made by
51
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(20, 2)), // Version needed
52
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Flags
53
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(8, 2)), // Compression
54
54
  node_buffer_1.Buffer.from(modTime),
55
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(crc, 4)),
56
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(compSize, 4)),
57
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(uncompSize, 4)),
58
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(fileNameBuf.length, 2)),
59
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Extra field length
60
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Comment length
61
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Disk start
62
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Internal attrs
63
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 4)), // External attrs
64
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(offset, 4)),
55
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(crc, 4)),
56
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(compSize, 4)),
57
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(uncompSize, 4)),
58
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(fileNameBuf.length, 2)),
59
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Extra field length
60
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Comment length
61
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Disk start
62
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Internal attrs
63
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 4)), // External attrs
64
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(offset, 4)),
65
65
  fileNameBuf,
66
66
  ]);
67
67
  centralDirectory.push(centralEntry);
@@ -71,13 +71,13 @@ function createSync(files) {
71
71
  const centralDirOffset = offset;
72
72
  const endRecord = node_buffer_1.Buffer.concat([
73
73
  node_buffer_1.Buffer.from(constants_js_1.END_OF_CENTRAL_DIR_SIG),
74
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Disk #
75
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Start disk #
76
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(centralDirectory.length, 2)),
77
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(centralDirectory.length, 2)),
78
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(centralDirSize, 4)),
79
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(centralDirOffset, 4)),
80
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Comment length
74
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Disk #
75
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Start disk #
76
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirectory.length, 2)),
77
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirectory.length, 2)),
78
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirSize, 4)),
79
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirOffset, 4)),
80
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Comment length
81
81
  ]);
82
82
  return node_buffer_1.Buffer.concat(fileEntries.concat(centralDirectory).concat([endRecord]));
83
83
  }
@@ -8,7 +8,7 @@ const node_buffer_1 = require("node:buffer");
8
8
  const node_util_1 = __importDefault(require("node:util"));
9
9
  const node_zlib_1 = __importDefault(require("node:zlib"));
10
10
  const deflateRaw = node_util_1.default.promisify(node_zlib_1.default.deflateRaw);
11
- const utils_js_1 = require("./utils.js");
11
+ const index_js_1 = require("./utils/index.js");
12
12
  const constants_js_1 = require("./constants.js");
13
13
  /**
14
14
  * Creates a ZIP archive from a collection of files.
@@ -26,23 +26,23 @@ async function create(files) {
26
26
  }
27
27
  const content = node_buffer_1.Buffer.isBuffer(rawContent) ? rawContent : node_buffer_1.Buffer.from(rawContent);
28
28
  const fileNameBuf = node_buffer_1.Buffer.from(filename, "utf8");
29
- const modTime = (0, utils_js_1.dosTime)(new Date());
30
- const crc = (0, utils_js_1.crc32)(content);
29
+ const modTime = (0, index_js_1.dosTime)(new Date());
30
+ const crc = (0, index_js_1.crc32)(content);
31
31
  const compressed = await deflateRaw(content);
32
32
  const compSize = compressed.length;
33
33
  const uncompSize = content.length;
34
34
  // Local file header
35
35
  const localHeader = node_buffer_1.Buffer.concat([
36
36
  constants_js_1.LOCAL_FILE_HEADER_SIG,
37
- (0, utils_js_1.toBytes)(20, 2),
38
- (0, utils_js_1.toBytes)(0, 2),
39
- (0, utils_js_1.toBytes)(8, 2),
37
+ (0, index_js_1.toBytes)(20, 2),
38
+ (0, index_js_1.toBytes)(0, 2),
39
+ (0, index_js_1.toBytes)(8, 2),
40
40
  modTime,
41
- (0, utils_js_1.toBytes)(crc, 4),
42
- (0, utils_js_1.toBytes)(compSize, 4),
43
- (0, utils_js_1.toBytes)(uncompSize, 4),
44
- (0, utils_js_1.toBytes)(fileNameBuf.length, 2),
45
- (0, utils_js_1.toBytes)(0, 2),
41
+ (0, index_js_1.toBytes)(crc, 4),
42
+ (0, index_js_1.toBytes)(compSize, 4),
43
+ (0, index_js_1.toBytes)(uncompSize, 4),
44
+ (0, index_js_1.toBytes)(fileNameBuf.length, 2),
45
+ (0, index_js_1.toBytes)(0, 2),
46
46
  ]);
47
47
  const localEntry = node_buffer_1.Buffer.concat([
48
48
  localHeader,
@@ -52,21 +52,21 @@ async function create(files) {
52
52
  fileEntries.push(localEntry);
53
53
  const centralEntry = node_buffer_1.Buffer.concat([
54
54
  node_buffer_1.Buffer.from(constants_js_1.CENTRAL_DIR_HEADER_SIG),
55
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(20, 2)), // Version made by
56
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(20, 2)), // Version needed
57
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Flags
58
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(8, 2)), // Compression
55
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(20, 2)), // Version made by
56
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(20, 2)), // Version needed
57
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Flags
58
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(8, 2)), // Compression
59
59
  node_buffer_1.Buffer.from(modTime),
60
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(crc, 4)),
61
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(compSize, 4)),
62
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(uncompSize, 4)),
63
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(fileNameBuf.length, 2)),
64
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Extra field length
65
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Comment length
66
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Disk start
67
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Internal attrs
68
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 4)), // External attrs
69
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(offset, 4)),
60
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(crc, 4)),
61
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(compSize, 4)),
62
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(uncompSize, 4)),
63
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(fileNameBuf.length, 2)),
64
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Extra field length
65
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Comment length
66
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Disk start
67
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Internal attrs
68
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 4)), // External attrs
69
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(offset, 4)),
70
70
  fileNameBuf,
71
71
  ]);
72
72
  centralDirectory.push(centralEntry);
@@ -76,13 +76,13 @@ async function create(files) {
76
76
  const centralDirOffset = offset;
77
77
  const endRecord = node_buffer_1.Buffer.concat([
78
78
  node_buffer_1.Buffer.from(constants_js_1.END_OF_CENTRAL_DIR_SIG),
79
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Disk #
80
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Start disk #
81
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(centralDirectory.length, 2)),
82
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(centralDirectory.length, 2)),
83
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(centralDirSize, 4)),
84
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(centralDirOffset, 4)),
85
- node_buffer_1.Buffer.from((0, utils_js_1.toBytes)(0, 2)), // Comment length
79
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Disk #
80
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Start disk #
81
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirectory.length, 2)),
82
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirectory.length, 2)),
83
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirSize, 4)),
84
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(centralDirOffset, 4)),
85
+ node_buffer_1.Buffer.from((0, index_js_1.toBytes)(0, 2)), // Comment length
86
86
  ]);
87
87
  return node_buffer_1.Buffer.concat(fileEntries.concat(centralDirectory).concat([endRecord]));
88
88
  }
@@ -1,10 +1,44 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
5
38
  Object.defineProperty(exports, "__esModule", { value: true });
6
39
  exports.readSync = readSync;
7
40
  const node_zlib_1 = __importDefault(require("node:zlib"));
41
+ const Utils = __importStar(require("./utils/index.js"));
8
42
  /**
9
43
  * Parses a ZIP archive from a buffer and extracts the files within.
10
44
  *
@@ -27,22 +61,37 @@ function readSync(buffer) {
27
61
  const fileNameEnd = fileNameStart + fileNameLength;
28
62
  const fileName = buffer.subarray(fileNameStart, fileNameEnd).toString();
29
63
  const dataStart = fileNameEnd + extraFieldLength;
30
- const compressedSize = buffer.readUInt32LE(offset + 18);
31
64
  const useDataDescriptor = (generalPurposeBitFlag & 0x08) !== 0;
32
- if (useDataDescriptor) {
33
- throw new Error(`File ${fileName} uses data descriptor. Not supported in this minimal parser.`);
34
- }
35
- const compressedData = buffer.subarray(dataStart, dataStart + compressedSize);
65
+ let compressedData;
36
66
  let content;
37
67
  try {
38
- if (compressionMethod === 0) {
39
- content = compressedData;
40
- }
41
- else if (compressionMethod === 8) {
42
- content = node_zlib_1.default.inflateRawSync(compressedData);
68
+ if (useDataDescriptor) {
69
+ const { compressedSize, offset: ddOffset } = Utils.findDataDescriptor(buffer, dataStart);
70
+ compressedData = buffer.subarray(dataStart, dataStart + compressedSize);
71
+ if (compressionMethod === 0) {
72
+ content = compressedData;
73
+ }
74
+ else if (compressionMethod === 8) {
75
+ content = node_zlib_1.default.inflateRawSync(compressedData);
76
+ }
77
+ else {
78
+ throw new Error(`Unsupported compression method ${compressionMethod}`);
79
+ }
80
+ offset = ddOffset + 16; // Skip over data descriptor
43
81
  }
44
82
  else {
45
- throw new Error(`Unsupported compression method ${compressionMethod}`);
83
+ const compressedSize = buffer.readUInt32LE(offset + 18);
84
+ compressedData = buffer.subarray(dataStart, dataStart + compressedSize);
85
+ if (compressionMethod === 0) {
86
+ content = compressedData;
87
+ }
88
+ else if (compressionMethod === 8) {
89
+ content = node_zlib_1.default.inflateRawSync(compressedData);
90
+ }
91
+ else {
92
+ throw new Error(`Unsupported compression method ${compressionMethod}`);
93
+ }
94
+ offset = dataStart + compressedSize;
46
95
  }
47
96
  }
48
97
  catch (error) {
@@ -50,7 +99,6 @@ function readSync(buffer) {
50
99
  throw new Error(`Error unpacking file ${fileName}: ${message}`);
51
100
  }
52
101
  files[fileName] = content;
53
- offset = dataStart + compressedSize;
54
102
  }
55
103
  return files;
56
104
  }
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -6,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
39
  exports.read = read;
7
40
  const node_util_1 = __importDefault(require("node:util"));
8
41
  const node_zlib_1 = __importDefault(require("node:zlib"));
42
+ const Utils = __importStar(require("./utils/index.js"));
9
43
  const inflateRaw = node_util_1.default.promisify(node_zlib_1.default.inflateRaw);
10
44
  /**
11
45
  * Parses a ZIP archive from a buffer and extracts the files within.
@@ -29,22 +63,37 @@ async function read(buffer) {
29
63
  const fileNameEnd = fileNameStart + fileNameLength;
30
64
  const fileName = buffer.subarray(fileNameStart, fileNameEnd).toString();
31
65
  const dataStart = fileNameEnd + extraFieldLength;
32
- const compressedSize = buffer.readUInt32LE(offset + 18);
33
66
  const useDataDescriptor = (generalPurposeBitFlag & 0x08) !== 0;
34
- if (useDataDescriptor) {
35
- throw new Error(`File ${fileName} uses data descriptor. Not supported in this minimal parser.`);
36
- }
37
- const compressedData = buffer.subarray(dataStart, dataStart + compressedSize);
67
+ let compressedData;
38
68
  let content;
39
69
  try {
40
- if (compressionMethod === 0) {
41
- content = compressedData;
42
- }
43
- else if (compressionMethod === 8) {
44
- content = await inflateRaw(compressedData);
70
+ if (useDataDescriptor) {
71
+ const { compressedSize, offset: ddOffset } = Utils.findDataDescriptor(buffer, dataStart);
72
+ compressedData = buffer.subarray(dataStart, dataStart + compressedSize);
73
+ if (compressionMethod === 0) {
74
+ content = compressedData;
75
+ }
76
+ else if (compressionMethod === 8) {
77
+ content = await inflateRaw(compressedData);
78
+ }
79
+ else {
80
+ throw new Error(`Unsupported compression method ${compressionMethod}`);
81
+ }
82
+ offset = ddOffset + 16; // Skip over data descriptor
45
83
  }
46
84
  else {
47
- throw new Error(`Unsupported compression method ${compressionMethod}`);
85
+ const compressedSize = buffer.readUInt32LE(offset + 18);
86
+ compressedData = buffer.subarray(dataStart, dataStart + compressedSize);
87
+ if (compressionMethod === 0) {
88
+ content = compressedData;
89
+ }
90
+ else if (compressionMethod === 8) {
91
+ content = await inflateRaw(compressedData);
92
+ }
93
+ else {
94
+ throw new Error(`Unsupported compression method ${compressionMethod}`);
95
+ }
96
+ offset = dataStart + compressedSize;
48
97
  }
49
98
  }
50
99
  catch (error) {
@@ -52,7 +101,6 @@ async function read(buffer) {
52
101
  throw new Error(`Error unpacking file ${fileName}: ${message}`);
53
102
  }
54
103
  files[fileName] = content;
55
- offset = dataStart + compressedSize;
56
104
  }
57
105
  return files;
58
106
  }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.crc32 = crc32;
4
+ /**
5
+ * Precomputed CRC-32 lookup table for optimized checksum calculation.
6
+ * The table is generated using the standard IEEE 802.3 (Ethernet) polynomial:
7
+ * 0xEDB88320 (reversed representation of 0x04C11DB7).
8
+ *
9
+ * The table is immediately invoked and cached as a constant for performance,
10
+ * following the common implementation pattern for CRC algorithms.
11
+ */
12
+ const crcTable = (() => {
13
+ // Create a typed array for better performance with 256 32-bit unsigned integers
14
+ const table = new Uint32Array(256);
15
+ // Generate table entries for all possible byte values (0-255)
16
+ for (let i = 0; i < 256; i++) {
17
+ let crc = i; // Initialize with current byte value
18
+ // Process each bit (8 times)
19
+ for (let j = 0; j < 8; j++) {
20
+ /*
21
+ * CRC division algorithm:
22
+ * 1. If LSB is set (crc & 1), XOR with polynomial
23
+ * 2. Right-shift by 1 (unsigned)
24
+ *
25
+ * The polynomial 0xEDB88320 is:
26
+ * - Bit-reversed version of 0x04C11DB7
27
+ * - Uses reflected input/output algorithm
28
+ */
29
+ crc = crc & 1
30
+ ? 0xedb88320 ^ (crc >>> 1) // XOR with polynomial if LSB is set
31
+ : crc >>> 1; // Just shift right if LSB is not set
32
+ }
33
+ // Store final 32-bit value (>>> 0 ensures unsigned 32-bit representation)
34
+ table[i] = crc >>> 0;
35
+ }
36
+ return table;
37
+ })();
38
+ /**
39
+ * Computes a CRC-32 checksum for the given Buffer using the standard IEEE 802.3 polynomial.
40
+ * This implementation uses a precomputed lookup table for optimal performance.
41
+ *
42
+ * The algorithm follows these characteristics:
43
+ * - Polynomial: 0xEDB88320 (reversed representation of 0x04C11DB7)
44
+ * - Initial value: 0xFFFFFFFF (inverted by ~0)
45
+ * - Final XOR value: 0xFFFFFFFF (achieved by inverting the result)
46
+ * - Input and output reflection: Yes
47
+ *
48
+ * @param {Buffer} buf - The input buffer to calculate checksum for
49
+ * @returns {number} - The 32-bit unsigned CRC-32 checksum (0x00000000 to 0xFFFFFFFF)
50
+ */
51
+ function crc32(buf) {
52
+ // Initialize CRC with all 1's (0xFFFFFFFF) using bitwise NOT
53
+ let crc = ~0;
54
+ // Process each byte in the buffer
55
+ for (let i = 0; i < buf.length; i++) {
56
+ /*
57
+ * CRC update algorithm steps:
58
+ * 1. XOR current CRC with next byte (lowest 8 bits)
59
+ * 2. Use result as index in precomputed table (0-255)
60
+ * 3. XOR the table value with right-shifted CRC (8 bits)
61
+ *
62
+ * The operation breakdown:
63
+ * - (crc ^ buf[i]) - XOR with next byte
64
+ * - & 0xff - Isolate lowest 8 bits
65
+ * - crc >>> 8 - Shift CRC right by 8 bits (unsigned)
66
+ * - ^ crcTable[...] - XOR with precomputed table value
67
+ */
68
+ crc = (crc >>> 8) ^ crcTable[(crc ^ buf[i]) & 0xff];
69
+ }
70
+ /*
71
+ * Final processing:
72
+ * 1. Invert all bits (~crc) to match standard CRC-32 output
73
+ * 2. Convert to unsigned 32-bit integer (>>> 0)
74
+ */
75
+ return ~crc >>> 0;
76
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dosTime = dosTime;
4
+ const node_buffer_1 = require("node:buffer");
5
+ const to_bytes_js_1 = require("./to-bytes.js");
6
+ /**
7
+ * Converts a JavaScript Date object to a 4-byte Buffer in MS-DOS date/time format
8
+ * as specified in the ZIP file format specification (PKZIP APPNOTE.TXT).
9
+ *
10
+ * The MS-DOS date/time format packs both date and time into 4 bytes (32 bits) with
11
+ * the following bit layout:
12
+ *
13
+ * Time portion (2 bytes/16 bits):
14
+ * - Bits 00-04: Seconds divided by 2 (0-29, representing 0-58 seconds)
15
+ * - Bits 05-10: Minutes (0-59)
16
+ * - Bits 11-15: Hours (0-23)
17
+ *
18
+ * Date portion (2 bytes/16 bits):
19
+ * - Bits 00-04: Day (1-31)
20
+ * - Bits 05-08: Month (1-12)
21
+ * - Bits 09-15: Year offset from 1980 (0-127, representing 1980-2107)
22
+ *
23
+ * @param {Date} date - The JavaScript Date object to convert
24
+ * @returns {Buffer} - 4-byte Buffer containing:
25
+ * - Bytes 0-1: DOS time (hours, minutes, seconds/2)
26
+ * - Bytes 2-3: DOS date (year-1980, month, day)
27
+ * @throws {RangeError} - If the date is before 1980 or after 2107
28
+ */
29
+ function dosTime(date) {
30
+ // Pack time components into 2 bytes (16 bits):
31
+ // - Hours (5 bits) shifted left 11 positions (bits 11-15)
32
+ // - Minutes (6 bits) shifted left 5 positions (bits 5-10)
33
+ // - Seconds/2 (5 bits) in least significant bits (bits 0-4)
34
+ const time = (date.getHours() << 11) | // Hours occupy bits 11-15
35
+ (date.getMinutes() << 5) | // Minutes occupy bits 5-10
36
+ (Math.floor(date.getSeconds() / 2)); // Seconds/2 occupy bits 0-4
37
+ // Pack date components into 2 bytes (16 bits):
38
+ // - (Year-1980) (7 bits) shifted left 9 positions (bits 9-15)
39
+ // - Month (4 bits) shifted left 5 positions (bits 5-8)
40
+ // - Day (5 bits) in least significant bits (bits 0-4)
41
+ const day = ((date.getFullYear() - 1980) << 9) | // Years since 1980 (bits 9-15)
42
+ ((date.getMonth() + 1) << 5) | // Month 1-12 (bits 5-8)
43
+ date.getDate(); // Day 1-31 (bits 0-4)
44
+ // Combine both 2-byte values into a single 4-byte Buffer
45
+ // Note: Using little-endian byte order for each 2-byte segment
46
+ return node_buffer_1.Buffer.from([
47
+ ...(0, to_bytes_js_1.toBytes)(time, 2), // Convert time to 2 bytes (LSB first)
48
+ ...(0, to_bytes_js_1.toBytes)(day, 2), // Convert date to 2 bytes (LSB first)
49
+ ]);
50
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findDataDescriptor = findDataDescriptor;
4
+ /**
5
+ * Finds a Data Descriptor in a ZIP archive buffer.
6
+ *
7
+ * The Data Descriptor is an optional 16-byte structure that appears at the end of a file's compressed data.
8
+ * It contains the compressed size of the file, and must be used when the Local File Header does not contain this information.
9
+ *
10
+ * @param buffer - The buffer containing the ZIP archive data.
11
+ * @param start - The starting offset in the buffer to search for the Data Descriptor.
12
+ * @returns - An object with `offset` and `compressedSize` properties.
13
+ * @throws {Error} - If the Data Descriptor is not found.
14
+ */
15
+ function findDataDescriptor(buffer, start) {
16
+ const DATA_DESCRIPTOR_SIGNATURE = 0x08074b50;
17
+ const DATA_DESCRIPTOR_TOTAL_LENGTH = 16;
18
+ const COMPRESSED_SIZE_OFFSET_FROM_SIGNATURE = 8;
19
+ for (let i = start; i <= buffer.length - DATA_DESCRIPTOR_TOTAL_LENGTH; i++) {
20
+ if (buffer.readUInt32LE(i) === DATA_DESCRIPTOR_SIGNATURE) {
21
+ const compressedSize = buffer.readUInt32LE(i + COMPRESSED_SIZE_OFFSET_FROM_SIGNATURE);
22
+ return {
23
+ compressedSize,
24
+ offset: i,
25
+ };
26
+ }
27
+ }
28
+ throw new Error("Data Descriptor not found");
29
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./crc-32.js"), exports);
18
+ __exportStar(require("./dos-time.js"), exports);
19
+ __exportStar(require("./find-data-descriptor.js"), exports);
20
+ __exportStar(require("./to-bytes.js"), exports);
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toBytes = toBytes;
4
+ const node_buffer_1 = require("node:buffer");
5
+ /**
6
+ * Converts a numeric value into a fixed-length Buffer representation,
7
+ * storing the value in little-endian format with right-padding of zeros.
8
+ *
9
+ * This is particularly useful for binary protocols or file formats that
10
+ * require fixed-width numeric fields.
11
+ *
12
+ * @param {number} value - The numeric value to convert to bytes.
13
+ * Note: JavaScript numbers are IEEE 754 doubles, but only the
14
+ * integer portion will be used (up to 53-bit precision).
15
+ * @param {number} len - The desired length of the output Buffer in bytes.
16
+ * Must be a positive integer.
17
+ * @returns {Buffer} - A new Buffer of exactly `len` bytes containing:
18
+ * 1. The value's bytes in little-endian order (least significant byte first)
19
+ * 2. Zero padding in any remaining higher-order bytes
20
+ * @throws {RangeError} - If the value requires more bytes than `len` to represent
21
+ * (though this is currently not explicitly checked)
22
+ */
23
+ function toBytes(value, len) {
24
+ // Allocate a new Buffer of the requested length, automatically zero-filled
25
+ const buf = node_buffer_1.Buffer.alloc(len);
26
+ // Process each byte position from least significant to most significant
27
+ for (let i = 0; i < len; i++) {
28
+ // Store the least significant byte of the current value
29
+ buf[i] = value & 0xff; // Mask to get bottom 8 bits
30
+ // Right-shift the value by 8 bits to process the next byte
31
+ // Note: This uses unsigned right shift (>>> would be signed)
32
+ value >>= 8;
33
+ // If the loop completes with value != 0, we've overflowed the buffer length,
34
+ // but this isn't currently checked/handled
35
+ }
36
+ return buf;
37
+ }
@@ -1,6 +1,6 @@
1
1
  import { Buffer } from "node:buffer";
2
2
  import { deflateRawSync } from "node:zlib";
3
- import { crc32, dosTime, toBytes } from "./utils.js";
3
+ import { crc32, dosTime, toBytes } from "./utils/index.js";
4
4
  import { CENTRAL_DIR_HEADER_SIG, END_OF_CENTRAL_DIR_SIG, LOCAL_FILE_HEADER_SIG, } from "./constants.js";
5
5
  /**
6
6
  * Creates a ZIP archive from a collection of files.
@@ -2,7 +2,7 @@ import { Buffer } from "node:buffer";
2
2
  import util from "node:util";
3
3
  import zlib from "node:zlib";
4
4
  const deflateRaw = util.promisify(zlib.deflateRaw);
5
- import { crc32, dosTime, toBytes } from "./utils.js";
5
+ import { crc32, dosTime, toBytes } from "./utils/index.js";
6
6
  import { CENTRAL_DIR_HEADER_SIG, END_OF_CENTRAL_DIR_SIG, LOCAL_FILE_HEADER_SIG, } from "./constants.js";
7
7
  /**
8
8
  * Creates a ZIP archive from a collection of files.