@zip.js/zip.js 2.8.60 → 2.9.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.
@@ -33,7 +33,7 @@
33
33
  // Every table MUST stay a PACKED_SMI array: build with array literals (not `new Array(n)`,
34
34
  // which is HOLEY) and store the signed int32 XOR result (no `>>> 0`). An unsigned or holey
35
35
  // table becomes a V8 FixedDoubleArray whose every hot-loop lookup unboxes a double (~1.6x
36
- // slower). Signedness is irrelevant to the result — the reads mask/shift it and the final
36
+ // slower). Signedness is irrelevant to the result: the reads mask/shift it and the final
37
37
  // `~crc` normalizes it. Do NOT reintroduce `>>> 0` here or switch to `new Array(256)`.
38
38
  const T = [[], [], [], [], [], [], [], []];
39
39
  for (let n = 0; n < 256; n++) {
@@ -28,7 +28,7 @@
28
28
 
29
29
  /* global TextDecoder */
30
30
 
31
- const CP437 = "\0☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ".split("");
31
+ const CP437 = "\0\u263A\u263B\u2665\u2666\u2663\u2660\u2022\u25D8\u25CB\u25D9\u2642\u2640\u266A\u266B\u263C\u25BA\u25C4\u2195\u203C\u00B6\u00A7\u25AC\u21A8\u2191\u2193\u2192\u2190\u221F\u2194\u25B2\u25BC !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u2302\u00C7\u00FC\u00E9\u00E2\u00E4\u00E0\u00E5\u00E7\u00EA\u00EB\u00E8\u00EF\u00EE\u00EC\u00C4\u00C5\u00C9\u00E6\u00C6\u00F4\u00F6\u00F2\u00FB\u00F9\u00FF\u00D6\u00DC\u00A2\u00A3\u00A5\u20A7\u0192\u00E1\u00ED\u00F3\u00FA\u00F1\u00D1\u00AA\u00BA\u00BF\u2310\u00AC\u00BD\u00BC\u00A1\u00AB\u00BB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\u00DF\u0393\u03C0\u03A3\u03C3\u00B5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\u00B1\u2265\u2264\u2320\u2321\u00F7\u2248\u00B0\u2219\u00B7\u221A\u207F\u00B2\u25A0\u00A0".split("");
32
32
  const VALID_CP437 = CP437.length == 256;
33
33
 
34
34
  export {
@@ -26,6 +26,6 @@
26
26
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
- const VERSION = "2.8.60";
29
+ const VERSION = "2.9.0";
30
30
 
31
31
  export { VERSION };
@@ -540,12 +540,13 @@ class ZipDirectoryEntry extends ZipEntry {
540
540
  if (options.bufferedWrite === UNDEFINED_VALUE) {
541
541
  options.bufferedWrite = true;
542
542
  }
543
- const writeOrderGuaranteed = !options.bufferedWrite ||
544
- (options.keepOrder !== false && zipEntry.children.every(child => !child.children.length));
545
- return getEntriesSize(options, zipEntry.getChildren({ recursive: true }).filter(child => !isImplicitDirectory(child)).map(child => {
543
+ const entries = zipEntry.getChildren({ recursive: true }).filter(child => !isImplicitDirectory(child)).map(child => {
546
544
  const { name, entryOptions } = getChildEntryOptions(child, zipEntry, options);
547
545
  return { name, size: child.directory ? 0 : getDeterminedSize(child, isPassThrough(child, options)), options: entryOptions };
548
- }), writeOrderGuaranteed, options.globalComment);
546
+ });
547
+ const writeOrderGuaranteed = !options.bufferedWrite ||
548
+ (entries.every(entry => entry.options.keepOrder !== false) && zipEntry.children.every(child => !child.children.length));
549
+ return getEntriesSize(options, entries, writeOrderGuaranteed, options.globalComment);
549
550
  }
550
551
 
551
552
  getChildByName(name) {
@@ -205,6 +205,7 @@ const WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA = "unknown zip64 extensible data";
205
205
  const WARNING_WRAPPED_ENTRIES_COUNT = "wrapped entries count";
206
206
  const WARNING_APPENDED_DATA = "appended data";
207
207
  const WARNING_PREPENDED_DATA = "prepended data";
208
+ const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
208
209
  const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
209
210
  const WARNING_DUPLICATE_FILENAME = "duplicate filename";
210
211
  const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
@@ -298,6 +299,7 @@ class ZipReader {
298
299
  let diskNumber = getUint16(endOfDirectoryView, 6);
299
300
  let filesLength = getUint16(endOfDirectoryView, 10);
300
301
  let prependedDataLength = 0;
302
+ let prependedCentralDirectory;
301
303
  let startOffset;
302
304
  let zip64EndOfDirectory;
303
305
  let zip64EndOfDirectoryVersion2;
@@ -401,6 +403,7 @@ class ZipReader {
401
403
  directoryDataOffset = expectedDirectoryDataOffset;
402
404
  if (directoryDataOffset > originalDirectoryDataOffset) {
403
405
  prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
406
+ prependedCentralDirectory = storedPointsAtDirectory;
404
407
  }
405
408
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
406
409
  directoryView = getDataView(directoryArray);
@@ -628,6 +631,9 @@ class ZipReader {
628
631
  if (prependedDataLength || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
629
632
  addWarning(warnings, WARNING_PREPENDED_DATA);
630
633
  }
634
+ if (prependedCentralDirectory) {
635
+ addWarning(warnings, WARNING_PREPENDED_CENTRAL_DIRECTORY);
636
+ }
631
637
  if (extractPrependedData) {
632
638
  zipReader.prependedData = startOffset > splitZipSignatureLength ?
633
639
  await readUint8Array(reader, splitZipSignatureLength, startOffset - splitZipSignatureLength) :
@@ -756,6 +762,7 @@ export {
756
762
  WARNING_WRAPPED_ENTRIES_COUNT,
757
763
  WARNING_APPENDED_DATA,
758
764
  WARNING_PREPENDED_DATA,
765
+ WARNING_PREPENDED_CENTRAL_DIRECTORY,
759
766
  WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
760
767
  WARNING_DUPLICATE_FILENAME,
761
768
  WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
@@ -56,17 +56,16 @@
56
56
  * the insertion order of the `fileEntries` map. addFile() reserves each entry's slot synchronously in
57
57
  * add() call order, before initializing the reader, so concurrent entries whose readers
58
58
  * initialize at different speeds are still listed in call order.
59
- * - `fileEntry.lockFileEntry` chains every entry to the previous one to reach getFileEntry(); awaiting it
60
- * serializes the physical writes when `keepOrder` is set. The physical layout therefore follows
61
- * the order in which the readers finish initializing, not necessarily the central directory
62
- * order; each entry records its own `offset`, so the archive stays consistent either way.
59
+ * - `fileEntry.lockFileEntry` chains every entry to the previous one; awaiting it serializes the
60
+ * physical writes when `keepOrder` is set. addFile() claims the link in the same synchronous
61
+ * prologue as the central directory slot, so the physical layout follows the add() call order
62
+ * as well, whatever the readers do. The lock is released for every entry, including the ones
63
+ * failing before they reach getFileEntry().
63
64
  * - `zipWriter.lockWriter`/`writerLocked` is the mutex over the writer; only the holder may write
64
65
  * into it or read the position variables above.
65
- * - An entry writes directly into the writer (i.e. without being buffered) only when the writer
66
- * is unlocked and no buffered entry is in flight (`bufferedWrites` == 0). This avoids a
67
- * deadlock, it is not an optimization: an entry written directly acquires the writer lock
68
- * before awaiting the lock of the previous entry, while a buffered predecessor needs the
69
- * writer lock to be flushed.
66
+ * - Both write paths await the lock of the previous entry before requesting the writer lock.
67
+ * Requesting it first would deadlock an entry written directly into the writer (i.e. without
68
+ * being buffered) against a buffered predecessor, which needs that same lock to be flushed.
70
69
  * - `lastFileEntry` is the tail of the lock chain (the most recently added entry).
71
70
  *
72
71
  * Failures:
@@ -689,6 +688,13 @@ async function addFile(zipWriter, name, reader, options) {
689
688
  const { comment } = metadataInfo;
690
689
  const extraField = options[PROPERTY_NAME_EXTRA_FIELD];
691
690
  zipWriter.fileEntries.set(name, UNDEFINED_VALUE);
691
+ const previousFileEntry = zipWriter.lastFileEntry;
692
+ const pendingFileEntry = {};
693
+ let releaseLockFileEntry;
694
+ if (metadataInfo.resolvedOptions.keepOrder) {
695
+ pendingFileEntry.lockFileEntry = new Promise(resolve => releaseLockFileEntry = resolve);
696
+ }
697
+ zipWriter.lastFileEntry = pendingFileEntry;
692
698
  let fileEntry;
693
699
  try {
694
700
  const { resolvedOptions } = metadataInfo;
@@ -710,10 +716,21 @@ async function addFile(zipWriter, name, reader, options) {
710
716
  const headerInfo = getHeaderInfo(options);
711
717
  const dataDescriptorInfo = getDataDescriptorInfo(options);
712
718
  const metadataSize = getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray);
713
- fileEntry = await getFileEntry(zipWriter, name, reader, { headerInfo, dataDescriptorInfo, metadataSize }, options);
719
+ fileEntry = await getFileEntry(zipWriter, name, reader, {
720
+ headerInfo,
721
+ dataDescriptorInfo,
722
+ metadataSize,
723
+ fileEntry: pendingFileEntry,
724
+ previousFileEntry,
725
+ releaseLockFileEntry
726
+ }, options);
714
727
  } catch (error) {
715
728
  zipWriter.fileEntries.delete(name);
716
729
  throw error;
730
+ } finally {
731
+ if (releaseLockFileEntry) {
732
+ releaseLockFileEntry(previousFileEntry && previousFileEntry.lockFileEntry);
733
+ }
717
734
  }
718
735
  Object.assign(fileEntry, {
719
736
  name,
@@ -1101,7 +1118,6 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1101
1118
  }
1102
1119
  const usdz = writerOptions[OPTION_USDZ];
1103
1120
  const files = new Map();
1104
- let layoutDependsOnWriteOrder = Boolean(usdz);
1105
1121
  const initialOffset = writerOptions[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : writerOptions[OPTION_OFFSET];
1106
1122
  let offset = initialOffset;
1107
1123
  let minimumEntrySize = INFINITY_VALUE;
@@ -1147,9 +1163,7 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1147
1163
  minimumEntrySize = Math.min(minimumEntrySize, entrySize);
1148
1164
  offset += entrySize;
1149
1165
  }
1150
- if (files.size && offset - minimumEntrySize >= MAX_32_BITS) {
1151
- layoutDependsOnWriteOrder = true;
1152
- }
1166
+ const layoutDependsOnWriteOrder = files.size > 1 && (usdz || offset - minimumEntrySize >= MAX_32_BITS);
1153
1167
  if (layoutDependsOnWriteOrder && !writeOrderGuaranteed) {
1154
1168
  throw new Error(ERR_UNDETERMINED_SIZE);
1155
1169
  }
@@ -1177,27 +1191,23 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1177
1191
  signal
1178
1192
  } = options;
1179
1193
  const {
1180
- headerInfo
1194
+ headerInfo,
1195
+ fileEntry: pendingFileEntry,
1196
+ previousFileEntry,
1197
+ releaseLockFileEntry
1181
1198
  } = entryInfo;
1182
1199
  const usdz = zipWriter.options[OPTION_USDZ];
1183
- const previousFileEntry = zipWriter.lastFileEntry;
1184
- let fileEntry = {};
1200
+ let fileEntry = pendingFileEntry;
1185
1201
  let bufferedWrite;
1186
1202
  let releaseLockWriter;
1187
- let releaseLockCurrentFileEntry;
1188
1203
  let writingBufferedEntryData;
1189
1204
  let writingEntryData;
1190
1205
  let writerSizeBeforeEntry;
1191
1206
  let flushedBufferedSize = 0;
1192
1207
  let fileWriter;
1208
+ const lockPreviousFileEntry = keepOrder && previousFileEntry ? previousFileEntry.lockFileEntry : UNDEFINED_VALUE;
1193
1209
  fileEntries.set(name, fileEntry);
1194
- zipWriter.lastFileEntry = fileEntry;
1195
1210
  try {
1196
- let lockPreviousFileEntry;
1197
- if (keepOrder) {
1198
- lockPreviousFileEntry = previousFileEntry && previousFileEntry.lockFileEntry;
1199
- requestLockCurrentFileEntry();
1200
- }
1201
1211
  if (options.bufferedWrite || !keepOrder || zipWriter.writerLocked || zipWriter.bufferedWrites || (!dataDescriptor && !emptyEntry)) {
1202
1212
  bufferedWrite = true;
1203
1213
  zipWriter.bufferedWrites++;
@@ -1210,6 +1220,7 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1210
1220
  await initStream(writer);
1211
1221
  } else {
1212
1222
  fileWriter = writer;
1223
+ await lockPreviousFileEntry;
1213
1224
  await requestLockWriter();
1214
1225
  }
1215
1226
  await initStream(fileWriter);
@@ -1222,7 +1233,6 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1222
1233
  }
1223
1234
  const { localHeaderArray } = headerInfo;
1224
1235
  if (!bufferedWrite) {
1225
- await lockPreviousFileEntry;
1226
1236
  await skipDiskIfNeeded();
1227
1237
  }
1228
1238
  const diskNumberStart = getDiskNumber(writer);
@@ -1287,8 +1297,8 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1287
1297
  if (bufferedWrite) {
1288
1298
  zipWriter.bufferedWrites--;
1289
1299
  }
1290
- if (releaseLockCurrentFileEntry) {
1291
- releaseLockCurrentFileEntry();
1300
+ if (releaseLockFileEntry) {
1301
+ releaseLockFileEntry(lockPreviousFileEntry);
1292
1302
  }
1293
1303
  if (releaseLockWriter) {
1294
1304
  releaseLockWriter();
@@ -1302,10 +1312,6 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
1302
1312
  }
1303
1313
  }
1304
1314
 
1305
- function requestLockCurrentFileEntry() {
1306
- fileEntry.lockFileEntry = new Promise(resolve => releaseLockCurrentFileEntry = resolve);
1307
- }
1308
-
1309
1315
  async function requestLockWriter() {
1310
1316
  zipWriter.writerLocked = true;
1311
1317
  const { lockWriter } = zipWriter;
@@ -66,6 +66,7 @@ export {
66
66
  WARNING_WRAPPED_ENTRIES_COUNT,
67
67
  WARNING_APPENDED_DATA,
68
68
  WARNING_PREPENDED_DATA,
69
+ WARNING_PREPENDED_CENTRAL_DIRECTORY,
69
70
  WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
70
71
  WARNING_DUPLICATE_FILENAME,
71
72
  WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
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.60",
6
+ "version": "2.9.0",
7
7
  "type": "module",
8
8
  "keywords": [
9
9
  "zip",
@@ -39,6 +39,7 @@
39
39
  "bump-minor": "npm version minor --no-git-tag-version && npm run bump-commit",
40
40
  "bump-major": "npm version major --no-git-tag-version && npm run bump-commit",
41
41
  "bump-commit": "npm run build && git commit -m \"bump up version\" package.json package-lock.json deno.json lib/core/version.js dist index.cjs index-native.cjs index.min.js index-native.min.js lib/core/web-worker-inline-wasm.js lib/core/web-worker-inline-native.js",
42
+ "release": "sh ./release.sh",
42
43
  "lint": "npx eslint . && deno lint",
43
44
  "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 && npm run test-api-mangling",
44
45
  "test-browsers": "npm run test-chrome && npm run test-firefox && npm run test-safari",
package/release.sh ADDED
@@ -0,0 +1,47 @@
1
+ #!/bin/sh
2
+ # Creates the GitHub release for the version declared in package.json, which triggers the publish
3
+ # workflow. It only encodes the checks a release must pass anyway: the released commit is the pushed
4
+ # master head, its tests concluded green (the publish gate would otherwise fail after the release
5
+ # exists, and recovering means deleting the release and the tag), and the tag targets the branch
6
+ # because the API rejects abbreviated commit SHAs as a target.
7
+ set -e
8
+
9
+ notes_file=$1
10
+ if [ -z "$notes_file" ]; then
11
+ echo "usage: npm run release -- <notes-file>" >&2
12
+ exit 1
13
+ fi
14
+ if [ ! -f "$notes_file" ]; then
15
+ echo "notes file not found: $notes_file" >&2
16
+ exit 1
17
+ fi
18
+ version=$(node -p "require('./package.json').version")
19
+ tag="v$version"
20
+ if gh release view "$tag" >/dev/null 2>&1; then
21
+ echo "release $tag already exists" >&2
22
+ exit 1
23
+ fi
24
+ if [ -n "$(git status --porcelain)" ]; then
25
+ echo "the working tree is not clean" >&2
26
+ exit 1
27
+ fi
28
+ git fetch origin master
29
+ sha=$(git rev-parse HEAD)
30
+ if [ "$sha" != "$(git rev-parse origin/master)" ]; then
31
+ echo "HEAD is not origin/master, push the bump commit first" >&2
32
+ exit 1
33
+ fi
34
+ run=$(gh api "repos/{owner}/{repo}/actions/workflows/test.yml/runs?head_sha=$sha&per_page=1" \
35
+ --jq 'if (.workflow_runs | length) == 0 then "none none none" else (.workflow_runs[0] | "\(.status) \(.conclusion) \(.html_url)") end')
36
+ run_status=$(echo "$run" | cut -d " " -f 1)
37
+ conclusion=$(echo "$run" | cut -d " " -f 2)
38
+ url=$(echo "$run" | cut -d " " -f 3)
39
+ if [ "$run_status" = "none" ]; then
40
+ echo "no test run found for $sha yet, wait for it and run this again" >&2
41
+ exit 1
42
+ fi
43
+ if [ "$run_status" != "completed" ] || [ "$conclusion" != "success" ]; then
44
+ echo "the tests of $sha concluded $run_status/$conclusion, only a green commit can be released: $url" >&2
45
+ exit 1
46
+ fi
47
+ gh release create "$tag" --target master --notes-file "$notes_file"