@zip.js/zip.js 2.8.52 → 2.8.53

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.
@@ -83,6 +83,7 @@ function createWebWorkerInterface(workerData, config) {
83
83
  worker,
84
84
  workerAlive: false,
85
85
  terminated: false,
86
+ startupError: null,
86
87
  interface: {
87
88
  run: async () => {
88
89
  try {
@@ -110,6 +111,13 @@ function createWebWorkerInterface(workerData, config) {
110
111
  }
111
112
 
112
113
  async function runWebWorker(workerData, config) {
114
+ if (!workerData.worker) {
115
+ const { startupError } = workerData;
116
+ workerData.startupError = null;
117
+ const error = startupError || new Error(ERR_WORKER_STARTUP_TIMEOUT);
118
+ error.workerStartupFailed = true;
119
+ throw error;
120
+ }
113
121
  let resolveResult, rejectResult;
114
122
  const result = new Promise((resolve, reject) => {
115
123
  resolveResult = resolve;
@@ -323,11 +331,12 @@ function onWorkerError(event, workerData) {
323
331
  if (!workerAlive) {
324
332
  workerData.worker = null;
325
333
  }
334
+ let error = event.error || new Error(event.message || ERROR_EVENT_TYPE);
335
+ if (!workerAlive) {
336
+ error = Object.assign(new Error(error.message || ERROR_EVENT_TYPE), { workerStartupFailed: true });
337
+ workerData.startupError = error;
338
+ }
326
339
  if (rejectResult) {
327
- let error = event.error || new Error(event.message || ERROR_EVENT_TYPE);
328
- if (!workerAlive) {
329
- error = Object.assign(new Error(error.message || ERROR_EVENT_TYPE), { workerStartupFailed: true });
330
- }
331
340
  rejectResult(error);
332
341
  if (writer) {
333
342
  writer.releaseLock();
@@ -93,6 +93,9 @@ const VERSION_DEFLATE = 0x14;
93
93
  const VERSION_ZIP64 = 0x2D;
94
94
  const VERSION_AES = 0x33;
95
95
 
96
+ const VERSION_MADE_BY_MSDOS = 0x0014;
97
+ const VERSION_MADE_BY_UNIX = 0x0300;
98
+
96
99
  const DIRECTORY_SIGNATURE = "/";
97
100
 
98
101
  const HEADER_SIZE = 30;
@@ -176,6 +179,8 @@ export {
176
179
  FILE_ATTR_UNIX_SETGID_MASK,
177
180
  FILE_ATTR_UNIX_STICKY_MASK,
178
181
  VERSION_DEFLATE,
182
+ VERSION_MADE_BY_MSDOS,
183
+ VERSION_MADE_BY_UNIX,
179
184
  VERSION_ZIP64,
180
185
  VERSION_AES,
181
186
  HEADER_SIZE,
package/lib/core/io.js CHANGED
@@ -791,9 +791,8 @@ class SplitDataReader extends Reader {
791
791
 
792
792
  async init() {
793
793
  const reader = this;
794
- const { readers } = reader;
795
794
  reader.lastDiskNumber = 0;
796
- await Promise.all(readers.map(diskReader => initStream(diskReader)));
795
+ const readers = reader.readers = await Promise.all(reader.readers.map(initDiskReader));
797
796
  reader.diskOffsets = readers.map(diskReader => {
798
797
  const diskOffset = reader.size;
799
798
  reader.size += diskReader.size;
@@ -988,6 +987,16 @@ async function initStream(stream, initSize) {
988
987
  }
989
988
  }
990
989
 
990
+ async function initDiskReader(diskReader) {
991
+ diskReader = new GenericReader(diskReader);
992
+ await initStream(diskReader);
993
+ if (diskReader.size === UNDEFINED_VALUE || !diskReader.readUint8Array) {
994
+ diskReader = new BlobReader(await streamToBlob(diskReader.readable));
995
+ await initStream(diskReader);
996
+ }
997
+ return diskReader;
998
+ }
999
+
991
1000
  function readUint8Array(reader, offset, size) {
992
1001
  return reader.readUint8Array(offset, size);
993
1002
  }
@@ -39,6 +39,7 @@ const OPTION_CHECK_PASSWORD_ONLY = "checkPasswordOnly";
39
39
  const OPTION_CHECK_OVERLAPPING_ENTRY_ONLY = "checkOverlappingEntryOnly";
40
40
  const OPTION_CHECK_OVERLAPPING_ENTRY = "checkOverlappingEntry";
41
41
  const OPTION_CHECK_AMBIGUITY = "checkAmbiguity";
42
+ const OPTION_CHECK_LOCAL_DIRECTORY = "checkLocalDirectory";
42
43
  const OPTION_CHECK_SIGNATURE = "checkSignature";
43
44
  const OPTION_CHECK_CRC32 = "checkCrc32";
44
45
  const OPTION_CHECK_AUTHENTICATION_CODE = "checkAuthenticationCode";
@@ -88,6 +89,7 @@ export {
88
89
  OPTION_CHECK_OVERLAPPING_ENTRY_ONLY,
89
90
  OPTION_CHECK_OVERLAPPING_ENTRY,
90
91
  OPTION_CHECK_AMBIGUITY,
92
+ OPTION_CHECK_LOCAL_DIRECTORY,
91
93
  OPTION_CHECK_SIGNATURE,
92
94
  OPTION_CHECK_CRC32,
93
95
  OPTION_CHECK_AUTHENTICATION_CODE,
@@ -70,6 +70,10 @@ const PROPERTY_NAME_SETUID = "setuid";
70
70
  const PROPERTY_NAME_SETGID = "setgid";
71
71
  const PROPERTY_NAME_STICKY = "sticky";
72
72
  const PROPERTY_NAME_BITFLAG = "bitFlag";
73
+ const PROPERTY_NAME_RAW_BITFLAG = "rawBitFlag";
74
+ const PROPERTY_NAME_FILENAME_LENGTH = "filenameLength";
75
+ const PROPERTY_NAME_EXTRA_FIELD_LENGTH = "extraFieldLength";
76
+ const PROPERTY_NAME_UNIX_EXTERNAL_UPPER = "unixExternalUpper";
73
77
  const PROPERTY_NAME_FILENAME_UTF8 = "filenameUTF8";
74
78
  const PROPERTY_NAME_COMMENT_UTF8 = "commentUTF8";
75
79
  const PROPERTY_NAME_RAW_EXTRA_FIELD = "rawExtraField";
@@ -122,10 +126,14 @@ const PROPERTY_NAMES = [
122
126
  PROPERTY_NAME_UID,
123
127
  PROPERTY_NAME_GID,
124
128
  PROPERTY_NAME_UNIX_MODE,
129
+ PROPERTY_NAME_UNIX_EXTERNAL_UPPER,
125
130
  PROPERTY_NAME_SETUID,
126
131
  PROPERTY_NAME_SETGID,
127
132
  PROPERTY_NAME_STICKY,
128
133
  PROPERTY_NAME_BITFLAG,
134
+ PROPERTY_NAME_RAW_BITFLAG,
135
+ PROPERTY_NAME_FILENAME_LENGTH,
136
+ PROPERTY_NAME_EXTRA_FIELD_LENGTH,
129
137
  PROPERTY_NAME_FILENAME_UTF8,
130
138
  PROPERTY_NAME_COMMENT_UTF8,
131
139
  PROPERTY_NAME_RAW_EXTRA_FIELD,
@@ -150,6 +150,7 @@ import {
150
150
  OPTION_CHECK_OVERLAPPING_ENTRY_ONLY,
151
151
  OPTION_CHECK_OVERLAPPING_ENTRY,
152
152
  OPTION_CHECK_AMBIGUITY,
153
+ OPTION_CHECK_LOCAL_DIRECTORY,
153
154
  OPTION_CHECK_SIGNATURE,
154
155
  OPTION_CHECK_CRC32,
155
156
  OPTION_CHECK_AUTHENTICATION_CODE,
@@ -236,7 +237,7 @@ class ZipReader {
236
237
  if (reader.size < END_OF_CENTRAL_DIR_LENGTH) {
237
238
  throw new Error(ERR_BAD_FORMAT);
238
239
  }
239
- const strictness = getStrictness(getOptionValue(zipReader, options, OPTION_STRICTNESS), getOptionValue(zipReader, options, OPTION_CHECK_AMBIGUITY));
240
+ const strictness = getStrictness(options, zipReader.options);
240
241
  const checkAmbiguity = strictness == STRICTNESS_STRICT;
241
242
  const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
242
243
  const maxAppendedDataSize = getMaxAppendedDataSize(getOptionValue(zipReader, options, OPTION_MAX_APPENDED_DATA_SIZE), strictness);
@@ -458,6 +459,7 @@ class ZipReader {
458
459
  index: indexFile,
459
460
  versionMadeBy,
460
461
  msDosCompatible,
462
+ zip64: false,
461
463
  compressedSize: 0,
462
464
  uncompressedSize: 0,
463
465
  commentLength,
@@ -697,9 +699,12 @@ class ZipEntry {
697
699
  extraFieldLength,
698
700
  filenameLength
699
701
  } = localDirectory;
700
- const checkAmbiguity = getStrictness(getOptionValue(zipEntry, options, OPTION_STRICTNESS), getOptionValue(zipEntry, options, OPTION_CHECK_AMBIGUITY)) == STRICTNESS_STRICT;
702
+ const checkLocalDirectoryOption = getOptionValue(zipEntry, options, OPTION_CHECK_LOCAL_DIRECTORY);
703
+ const entryStrictness = getStrictness(options, zipEntry.options);
704
+ const checkLocalDirectory = getCheckLocalDirectory(checkLocalDirectoryOption, entryStrictness);
705
+ const checkLocalFilename = getCheckLocalFilename(checkLocalDirectoryOption, entryStrictness);
701
706
  let rawLocalFilename = EMPTY_UINT8_ARRAY;
702
- if (checkAmbiguity && (filenameLength || extraFieldLength)) {
707
+ if (checkLocalFilename && (filenameLength || extraFieldLength)) {
703
708
  const trailingDataArray = await readUint8Array(reader, localHeaderOffset + HEADER_SIZE, filenameLength + extraFieldLength);
704
709
  rawLocalFilename = trailingDataArray.subarray(0, filenameLength);
705
710
  localDirectory.rawExtraField = trailingDataArray.subarray(filenameLength);
@@ -708,17 +713,26 @@ class ZipEntry {
708
713
  await readUint8Array(reader, localHeaderOffset + HEADER_SIZE + filenameLength, extraFieldLength) :
709
714
  EMPTY_UINT8_ARRAY;
710
715
  }
716
+ if (checkLocalFilename) {
717
+ localDirectory.rawFilename = rawLocalFilename;
718
+ }
711
719
  readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
712
- if (checkAmbiguity) {
713
- checkLocalDirectory(zipEntry, localDirectory, rawLocalFilename);
720
+ if (checkLocalDirectory) {
721
+ validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename);
714
722
  }
715
- const { lastAccessDate, creationDate } = localDirectory;
723
+ const { lastAccessDate, creationDate, uid, gid } = localDirectory;
716
724
  if (lastAccessDate) {
717
725
  fileEntry.lastAccessDate = lastAccessDate;
718
726
  }
719
727
  if (creationDate) {
720
728
  fileEntry.creationDate = creationDate;
721
729
  }
730
+ if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
731
+ fileEntry.uid = uid;
732
+ }
733
+ if (gid !== UNDEFINED_VALUE && fileEntry.gid === UNDEFINED_VALUE) {
734
+ fileEntry.gid = gid;
735
+ }
722
736
  const encrypted = zipEntry.encrypted && localDirectory.encrypted && !passThrough;
723
737
  const zipCrypto = encrypted && !extraFieldAES;
724
738
  if (!passThrough) {
@@ -954,10 +968,12 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
954
968
  directory.extraFieldNTFS = extraFieldNTFS;
955
969
  }
956
970
  const extraFieldUnix = extraField.get(EXTRAFIELD_TYPE_UNIX);
971
+ let unixIdsRead;
957
972
  if (extraFieldUnix) {
958
- readExtraFieldUnix(extraFieldUnix, directory, false);
973
+ unixIdsRead = readExtraFieldUnix(extraFieldUnix, directory, false);
959
974
  directory.extraFieldUnix = extraFieldUnix;
960
- } else {
975
+ }
976
+ if (!unixIdsRead) {
961
977
  const extraFieldInfoZip = extraField.get(EXTRAFIELD_TYPE_INFOZIP);
962
978
  if (extraFieldInfoZip) {
963
979
  readExtraFieldUnix(extraFieldInfoZip, directory, true);
@@ -1060,7 +1076,7 @@ function readExtraFieldNTFS(extraFieldNTFS, directory) {
1060
1076
  const creationDate = getDateNTFS(rawCreationDate);
1061
1077
  const extraFieldData = { lastModDate, lastAccessDate, creationDate };
1062
1078
  Object.assign(extraFieldNTFS, extraFieldData);
1063
- Object.assign(directory, extraFieldData);
1079
+ Object.assign(directory, extraFieldData, { rawLastAccessDate, rawCreationDate });
1064
1080
  }
1065
1081
  }
1066
1082
 
@@ -1104,6 +1120,7 @@ function readExtraFieldUnix(extraField, directory, isInfoZip) {
1104
1120
  if (gid !== UNDEFINED_VALUE) {
1105
1121
  directory.gid = gid;
1106
1122
  }
1123
+ return uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE;
1107
1124
  } catch {
1108
1125
  // ignored
1109
1126
  }
@@ -1176,26 +1193,25 @@ async function detectOverlappingEntry({
1176
1193
  }
1177
1194
  if (dataDescriptorLength) {
1178
1195
  const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
1179
- const dataDescriptorSignature = dataDescriptorArray.length == dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH &&
1180
- getUint32(getDataView(dataDescriptorArray), 0) == DATA_DESCRIPTOR_RECORD_SIGNATURE;
1181
- if (dataDescriptorSignature) {
1182
- const readCrc32 = getUint32(getDataView(dataDescriptorArray), 4);
1183
- let readCompressedSize;
1184
- let readUncompressedSize;
1185
- if (extraFieldZip64) {
1186
- readCompressedSize = getBigUint64(getDataView(dataDescriptorArray), 8);
1187
- readUncompressedSize = getBigUint64(getDataView(dataDescriptorArray), 16);
1188
- } else {
1189
- readCompressedSize = getUint32(getDataView(dataDescriptorArray), 8);
1190
- readUncompressedSize = getUint32(getDataView(dataDescriptorArray), 12);
1191
- }
1192
- const matchCrc32 = (fileEntry.encrypted && !fileEntry.zipCrypto) || readCrc32 == crc32;
1196
+ const dataDescriptorView = getDataView(dataDescriptorArray);
1197
+ let signature = dataDescriptorArray.length == dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH &&
1198
+ getUint32(dataDescriptorView, 0) == DATA_DESCRIPTOR_RECORD_SIGNATURE;
1199
+ if (signature) {
1200
+ const signedDataDescriptor = readDataDescriptor(dataDescriptorView, DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH, extraFieldZip64);
1201
+ const matchCrc32 = (fileEntry.encrypted && !fileEntry.zipCrypto) || signedDataDescriptor.crc32 == crc32;
1193
1202
  if (matchCrc32 &&
1194
- readCompressedSize == compressedSize &&
1195
- readUncompressedSize == uncompressedSize) {
1203
+ signedDataDescriptor.compressedSize == compressedSize &&
1204
+ signedDataDescriptor.uncompressedSize == uncompressedSize) {
1196
1205
  dataDescriptorLength += DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
1206
+ } else {
1207
+ signature = false;
1197
1208
  }
1198
1209
  }
1210
+ if (dataDescriptorArray.length >= dataDescriptorLength) {
1211
+ const localDataDescriptor = readDataDescriptor(dataDescriptorView, signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0, extraFieldZip64);
1212
+ localDataDescriptor.signature = signature;
1213
+ fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
1214
+ }
1199
1215
  }
1200
1216
  const range = {
1201
1217
  start: offset,
@@ -1212,6 +1228,20 @@ async function detectOverlappingEntry({
1212
1228
  readRanges.set(index, range);
1213
1229
  }
1214
1230
 
1231
+ function readDataDescriptor(dataDescriptorView, offset, extraFieldZip64) {
1232
+ const crc32 = getUint32(dataDescriptorView, offset);
1233
+ let compressedSize;
1234
+ let uncompressedSize;
1235
+ if (extraFieldZip64) {
1236
+ compressedSize = getBigUint64(dataDescriptorView, offset + 4);
1237
+ uncompressedSize = getBigUint64(dataDescriptorView, offset + 12);
1238
+ } else {
1239
+ compressedSize = getUint32(dataDescriptorView, offset + 4);
1240
+ uncompressedSize = getUint32(dataDescriptorView, offset + 8);
1241
+ }
1242
+ return { crc32, compressedSize, uncompressedSize };
1243
+ }
1244
+
1215
1245
  function getDiskOffset(reader, diskNumber) {
1216
1246
  return reader.getDiskOffset ? reader.getDiskOffset(diskNumber) : 0;
1217
1247
  }
@@ -1220,14 +1250,40 @@ function isStrictnessValue(value) {
1220
1250
  return value === STRICTNESS_STRICT || value === STRICTNESS_BALANCED || value === STRICTNESS_TOLERANT;
1221
1251
  }
1222
1252
 
1223
- function getStrictness(strictness, checkAmbiguity) {
1224
- if (strictness === UNDEFINED_VALUE) {
1225
- return checkAmbiguity ? STRICTNESS_STRICT : STRICTNESS_BALANCED;
1253
+ function getStrictness(options, inheritedOptions) {
1254
+ return resolveStrictness(options, resolveStrictness(inheritedOptions, STRICTNESS_BALANCED));
1255
+ }
1256
+
1257
+ function resolveStrictness(options, inheritedStrictness) {
1258
+ const strictness = options[OPTION_STRICTNESS];
1259
+ if (strictness !== UNDEFINED_VALUE) {
1260
+ if (!isStrictnessValue(strictness)) {
1261
+ throw new Error(ERR_INVALID_STRICTNESS);
1262
+ }
1263
+ return strictness;
1264
+ }
1265
+ const checkAmbiguity = options[OPTION_CHECK_AMBIGUITY];
1266
+ if (checkAmbiguity === UNDEFINED_VALUE) {
1267
+ return inheritedStrictness;
1268
+ }
1269
+ if (checkAmbiguity) {
1270
+ return STRICTNESS_STRICT;
1271
+ }
1272
+ return inheritedStrictness == STRICTNESS_TOLERANT ? STRICTNESS_TOLERANT : STRICTNESS_BALANCED;
1273
+ }
1274
+
1275
+ function getCheckLocalDirectory(checkLocalDirectory, strictness) {
1276
+ if (checkLocalDirectory === UNDEFINED_VALUE) {
1277
+ return strictness != STRICTNESS_TOLERANT;
1226
1278
  }
1227
- if (!isStrictnessValue(strictness)) {
1228
- throw new Error(ERR_INVALID_STRICTNESS);
1279
+ return Boolean(checkLocalDirectory);
1280
+ }
1281
+
1282
+ function getCheckLocalFilename(checkLocalFilename, strictness) {
1283
+ if (checkLocalFilename === UNDEFINED_VALUE) {
1284
+ return strictness == STRICTNESS_STRICT;
1229
1285
  }
1230
- return strictness;
1286
+ return Boolean(checkLocalFilename);
1231
1287
  }
1232
1288
 
1233
1289
  function getFilenameValidation(filenameValidation, strictness) {
@@ -1375,10 +1431,11 @@ async function readSignature(reader, view, anchoredOffset, signatureOffset, size
1375
1431
  return UNDEFINED_VALUE;
1376
1432
  }
1377
1433
 
1378
- function checkLocalDirectory(zipEntry, localDirectory, rawLocalFilename) {
1434
+ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename) {
1379
1435
  const { rawFilename } = zipEntry;
1380
- if (rawLocalFilename.length != rawFilename.length ||
1381
- rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte])) {
1436
+ if (checkLocalFilename &&
1437
+ (rawLocalFilename.length != rawFilename.length ||
1438
+ rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte]))) {
1382
1439
  throwAmbiguousArchive("mismatched local file header (filename)");
1383
1440
  }
1384
1441
  if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
@@ -122,12 +122,15 @@ import {
122
122
  FILE_ATTR_UNIX_TYPE_DIR,
123
123
  FILE_ATTR_UNIX_TYPE_FILE,
124
124
  FILE_ATTR_UNIX_TYPE_MASK,
125
+ FILE_ATTR_UNIX_TYPE_SYMLINK,
125
126
  FILE_ATTR_UNIX_EXECUTABLE_MASK,
126
127
  FILE_ATTR_UNIX_DEFAULT_MASK,
127
128
  FILE_ATTR_UNIX_SETUID_MASK,
128
129
  FILE_ATTR_UNIX_SETGID_MASK,
129
130
  FILE_ATTR_UNIX_STICKY_MASK,
130
131
  VERSION_DEFLATE,
132
+ VERSION_MADE_BY_MSDOS,
133
+ VERSION_MADE_BY_UNIX,
131
134
  VERSION_ZIP64,
132
135
  VERSION_AES,
133
136
  DIRECTORY_SIGNATURE,
@@ -157,11 +160,12 @@ import {
157
160
  initStream,
158
161
  GenericWriter,
159
162
  GenericReader,
163
+ BlobReader,
160
164
  ownsWritable
161
165
  } from "./io.js";
162
166
  import { encodeText } from "./util/encode-text.js";
163
167
  import { getDataView } from "./util/array.js";
164
- import { toCompatibleReadable } from "./util/compatible-streams.js";
168
+ import { toCompatibleReadable, streamToBlob } from "./util/compatible-streams.js";
165
169
  import {
166
170
  PROPERTY_NAME_LAST_MODIFICATION_DATE,
167
171
  PROPERTY_NAME_LAST_ACCESS_DATE,
@@ -225,6 +229,7 @@ import {
225
229
 
226
230
  const ERR_DUPLICATED_NAME = "File already exists";
227
231
  const ERR_INVALID_COMMENT = "Zip file comment exceeds 64KB";
232
+ const ERR_INVALID_COMMENT_TYPE = "Invalid zip file comment (must be a Uint8Array)";
228
233
  const ERR_INVALID_ENTRY_COMMENT = "File entry comment exceeds 64KB";
229
234
  const ERR_INVALID_ENTRY_NAME = "File entry name exceeds 64KB";
230
235
  const ERR_INVALID_VERSION = "Version exceeds 65535";
@@ -293,6 +298,10 @@ class ZipWriter {
293
298
  }
294
299
  reader = new GenericReader(reader);
295
300
  await initStream(reader);
301
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
302
+ reader = new BlobReader(await streamToBlob(reader.readable));
303
+ await initStream(reader);
304
+ }
296
305
  const { ZipReader } = await import("./zip-reader.js");
297
306
  const zipReader = new ZipReader(reader.readable);
298
307
  const entries = await zipReader.getEntries();
@@ -441,6 +450,9 @@ class ZipWriter {
441
450
  const zipWriter = this;
442
451
  const { pendingAddFileCalls, writer } = this;
443
452
  const { writable } = writer;
453
+ if (!(comment instanceof Uint8Array)) {
454
+ throw new Error(ERR_INVALID_COMMENT_TYPE);
455
+ }
444
456
  if (getLength(comment) > MAX_16_BITS) {
445
457
  throw new Error(ERR_INVALID_COMMENT);
446
458
  }
@@ -524,6 +536,7 @@ export {
524
536
  getEntriesSize,
525
537
  ERR_DUPLICATED_NAME,
526
538
  ERR_INVALID_COMMENT,
539
+ ERR_INVALID_COMMENT_TYPE,
527
540
  ERR_INVALID_ENTRY_NAME,
528
541
  ERR_INVALID_ENTRY_COMMENT,
529
542
  ERR_INVALID_VERSION,
@@ -569,8 +582,6 @@ async function addFile(zipWriter, name, reader, options) {
569
582
  const diskOffset = getDiskOffset(zipWriter.writer);
570
583
  const diskNumber = getDiskNumber(zipWriter.writer);
571
584
  options = Object.assign({}, options, attributesInfo.resolvedOptions, metadataInfo.resolvedOptions, sizesInfo.resolvedOptions, {
572
- internalFileAttribute: metadataInfo.resolvedOptions.internalFileAttributes,
573
- externalFileAttribute: attributesInfo.resolvedOptions.externalFileAttributes,
574
585
  signature: options[PROPERTY_NAME_SIGNATURE],
575
586
  crc32: options.crc32 === UNDEFINED_VALUE ? options[PROPERTY_NAME_SIGNATURE] : options.crc32,
576
587
  offset: zipWriter.offset - diskOffset,
@@ -584,14 +595,20 @@ async function addFile(zipWriter, name, reader, options) {
584
595
  zipWriter.files.delete(name);
585
596
  throw error;
586
597
  }
587
- Object.assign(fileEntry, { name, comment, extraField });
598
+ Object.assign(fileEntry, {
599
+ name,
600
+ comment,
601
+ extraField,
602
+ [PROPERTY_NAME_DEPRECATED_INTERNAL_FILE_ATTRIBUTES]: fileEntry.internalFileAttributes,
603
+ [PROPERTY_NAME_DEPRECATED_EXTERNAL_FILE_ATTRIBUTES]: fileEntry.externalFileAttributes
604
+ });
588
605
  return new Entry(fileEntry);
589
606
  }
590
607
 
591
608
  function resolveAttributes(zipWriter, name, options) {
592
609
  name = name.trim();
593
610
  let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
594
- let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? 20 : 768);
611
+ let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? VERSION_MADE_BY_MSDOS : VERSION_MADE_BY_UNIX);
595
612
  const executable = getOptionValue(zipWriter, options, PROPERTY_NAME_EXECUTABLE);
596
613
  const uid = getNumberOptionValue(zipWriter, options, PROPERTY_NAME_UID);
597
614
  const gid = getNumberOptionValue(zipWriter, options, PROPERTY_NAME_GID);
@@ -625,7 +642,7 @@ function resolveAttributes(zipWriter, name, options) {
625
642
  const hasMsDosProvided = msdosAttributesRaw !== UNDEFINED_VALUE || msdosAttributes !== UNDEFINED_VALUE;
626
643
  if (hasUnixMetadata) {
627
644
  msDosCompatible = false;
628
- versionMadeBy = (versionMadeBy & MAX_16_BITS) | (3 << 8);
645
+ versionMadeBy = (versionMadeBy & MAX_8_BITS) | VERSION_MADE_BY_UNIX;
629
646
  } else if (hasMsDosProvided) {
630
647
  msDosCompatible = true;
631
648
  versionMadeBy = (versionMadeBy & MAX_8_BITS);
@@ -665,11 +682,10 @@ function resolveAttributes(zipWriter, name, options) {
665
682
  externalFileAttributes = FILE_ATTR_UNIX_DEFAULT_MASK << 16;
666
683
  }
667
684
  }
668
- let unixExternalUpper;
669
685
  if (!msDosCompatible) {
670
686
  const unixModeProvided = unixMode !== UNDEFINED_VALUE || Boolean(setuid || setgid || sticky);
671
- unixExternalUpper = (externalFileAttributes >> 16) & MAX_16_BITS;
672
- unixMode = unixMode === UNDEFINED_VALUE ? unixExternalUpper : (unixMode & MAX_16_BITS);
687
+ const defaultUnixMode = (externalFileAttributes >> 16) & MAX_16_BITS;
688
+ unixMode = unixMode === UNDEFINED_VALUE ? defaultUnixMode : (unixMode & MAX_16_BITS);
673
689
  if (setuid) {
674
690
  unixMode |= FILE_ATTR_UNIX_SETUID_MASK;
675
691
  } else {
@@ -698,17 +714,20 @@ function resolveAttributes(zipWriter, name, options) {
698
714
  if (hasMsDosProvided) {
699
715
  externalFileAttributes = (externalFileAttributes & MAX_32_BITS) | (msdosAttributesRaw & MAX_8_BITS);
700
716
  }
717
+ const unixExternalUpper = (externalFileAttributes >> 16) & MAX_16_BITS;
718
+ const symlink = unixMode !== UNDEFINED_VALUE && ((unixMode & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_SYMLINK);
701
719
  return {
702
720
  name,
703
721
  resolvedOptions: {
704
722
  versionMadeBy,
705
- msDosCompatible,
723
+ msDosCompatible: Boolean(msDosCompatible),
706
724
  externalFileAttributes,
707
725
  unixExternalUpper,
708
726
  uid,
709
727
  gid,
710
728
  unixMode,
711
729
  unixExtraFieldType,
730
+ symlink,
712
731
  setuid,
713
732
  setgid,
714
733
  sticky,
@@ -941,6 +960,9 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
941
960
  if (writerOptions[OPTION_SIGN_CENTRAL_DIRECTORY]) {
942
961
  throw new Error(ERR_UNDETERMINED_SIZE);
943
962
  }
963
+ if (comment !== UNDEFINED_VALUE && !(comment instanceof Uint8Array)) {
964
+ throw new Error(ERR_INVALID_COMMENT_TYPE);
965
+ }
944
966
  const commentLength = getLength(comment);
945
967
  if (commentLength > MAX_16_BITS) {
946
968
  throw new Error(ERR_INVALID_COMMENT);
@@ -1222,6 +1244,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lock }, entryI
1222
1244
  uid,
1223
1245
  gid,
1224
1246
  unixMode,
1247
+ symlink,
1225
1248
  setuid,
1226
1249
  setgid,
1227
1250
  sticky,
@@ -1258,6 +1281,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lock }, entryI
1258
1281
  uid,
1259
1282
  gid,
1260
1283
  unixMode,
1284
+ symlink: Boolean(symlink),
1261
1285
  setuid,
1262
1286
  setgid,
1263
1287
  sticky,
@@ -1338,8 +1362,8 @@ async function createFileEntry(reader, writer, { diskNumberStart, lock }, entryI
1338
1362
  rawLastModDate,
1339
1363
  creationDate,
1340
1364
  lastAccessDate,
1341
- encrypted,
1342
- zipCrypto,
1365
+ encrypted: Boolean(encrypted),
1366
+ zipCrypto: Boolean(zipCrypto),
1343
1367
  size: metadataSize + compressedSize,
1344
1368
  compressionMethod,
1345
1369
  version,
@@ -33,6 +33,7 @@ export {
33
33
  ZipWriterStream,
34
34
  ERR_DUPLICATED_NAME,
35
35
  ERR_INVALID_COMMENT,
36
+ ERR_INVALID_COMMENT_TYPE,
36
37
  ERR_INVALID_ENTRY_NAME,
37
38
  ERR_INVALID_ENTRY_COMMENT,
38
39
  ERR_INVALID_VERSION,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "A JavaScript library to zip and unzip files in the browser, Deno and Node.js",
4
4
  "author": "Gildas Lormeau",
5
5
  "license": "BSD-3-Clause",
6
- "version": "2.8.52",
6
+ "version": "2.8.53",
7
7
  "type": "module",
8
8
  "keywords": [
9
9
  "zip",
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "scripts": {
37
37
  "lint": "npx eslint . && deno lint",
38
- "test": "npm run test-node && npm run test-deno && npm run test-bun && npm run test-node-dist && npm run test-deno-dist && npm run test-bun-dist && npm run test-web-streams-polyfill && npm run test-types",
38
+ "test": "npm run test-node && npm run test-deno && npm run test-bun && npm run test-node-dist && npm run test-deno-dist && npm run test-bun-dist && npm run test-web-streams-polyfill && npm run test-types && npm run test-api-symmetry && npm run test-api-shape",
39
39
  "test-browsers": "npm run test-chrome && npm run test-firefox && npm run test-safari",
40
40
  "serve-tests": "http-server -p 8888 -c-1 .",
41
41
  "test-all": "npm run lint && npm run test && npm run test-browsers",
@@ -46,11 +46,13 @@
46
46
  "test-deno": "cd ./tests && deno test --allow-read ./deno-runner.js",
47
47
  "test-web-streams-polyfill": "cd ./tests && deno run --allow-read ./web-streams-polyfill-runner.js",
48
48
  "test-node": "cd ./tests && node ./node-runner.js",
49
- "test-bun": "cd ./tests && bun test ./bun-runner.js --timeout 30000",
49
+ "test-bun": "cd ./tests && bun test ./bun-runner.js",
50
50
  "test-deno-dist": "./tests/dist-runner.sh deno",
51
51
  "test-node-dist": "./tests/dist-runner.sh node",
52
52
  "test-bun-dist": "./tests/dist-runner.sh bun",
53
53
  "test-types": "tsc -p ./tests/types/tsconfig.json",
54
+ "test-api-symmetry": "node ./tests/api-symmetry/audit.js",
55
+ "test-api-shape": "node ./tests/api-symmetry/shape.js",
54
56
  "test-firefox": "node ./tests/browser-runner.js firefox",
55
57
  "test-chrome": "node ./tests/browser-runner.js chrome",
56
58
  "test-safari": "node ./tests/browser-runner.js safari"