@cj-tech-master/excelts 7.0.0 → 7.0.1

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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @cj-tech-master/excelts v7.0.0
2
+ * @cj-tech-master/excelts v7.0.1
3
3
  * TypeScript Excel Workbook Manager - Read and Write xlsx and csv Files.
4
4
  * (c) 2026 cjnoname
5
5
  * Released under the MIT License
@@ -36002,6 +36002,40 @@ self.onmessage = async function(event) {
36002
36002
  }
36003
36003
  //#endregion
36004
36004
  //#region src/modules/archive/unzip/stream.base.ts
36005
+ /**
36006
+ * Returns true when `err` is the Node.js ERR_STREAM_PREMATURE_CLOSE error.
36007
+ *
36008
+ * This error is emitted by `finished()` / `pipeline()` when a stream is
36009
+ * destroyed before it has properly ended (e.g. a consumer breaks out of a
36010
+ * `for await` loop, or the entry PassThrough is destroyed by an external
36011
+ * consumer). In the context of ZIP parsing, a premature close on an *entry*
36012
+ * stream is not a fatal error — the parse loop only needs to advance the ZIP
36013
+ * cursor past the entry's compressed data.
36014
+ */
36015
+ function isPrematureCloseError(err) {
36016
+ if (!(err instanceof Error)) return false;
36017
+ return err.code === "ERR_STREAM_PREMATURE_CLOSE" || err.message === "Premature close";
36018
+ }
36019
+ /**
36020
+ * Wait for an entry's writable side to finish, tolerating premature close.
36021
+ *
36022
+ * The parse loop calls this after pumping all compressed data into an entry.
36023
+ * It ensures the decompressed data has been flushed through the inflater →
36024
+ * entry pipeline before advancing the ZIP cursor.
36025
+ *
36026
+ * If the consumer has already destroyed / autodraining the entry (e.g. early
36027
+ * break, external destroy, Readable.from() wrapper), `finished()` rejects
36028
+ * with ERR_STREAM_PREMATURE_CLOSE. This is not an error for the parse loop
36029
+ * — the compressed data has been fully read from the ZIP cursor, so we
36030
+ * can safely continue.
36031
+ */
36032
+ async function awaitEntryCompletion(entry) {
36033
+ try {
36034
+ await finished(entry, { readable: false });
36035
+ } catch (err) {
36036
+ if (!isPrematureCloseError(err)) throw err;
36037
+ }
36038
+ }
36005
36039
  const DEFAULT_UNZIP_STREAM_HIGH_WATER_MARK = 256 * 1024;
36006
36040
  function autodrain(stream) {
36007
36041
  const draining = stream.pipe(new Transform({ transform(_chunk, _encoding, callback) {
@@ -36061,6 +36095,7 @@ self.onmessage = async function(event) {
36061
36095
  let available = source.getLength();
36062
36096
  if (available === 0) source.maybeReleaseWriteCallback?.();
36063
36097
  while (available > 0) {
36098
+ let pendingCandidate = false;
36064
36099
  while (true) {
36065
36100
  const idx = scanner.find(source);
36066
36101
  if (idx === -1) break;
@@ -36108,10 +36143,41 @@ self.onmessage = async function(event) {
36108
36143
  continue;
36109
36144
  }
36110
36145
  scanner.searchFrom = idx;
36146
+ pendingCandidate = true;
36147
+ if (source.isFinished() && idx + 16 <= available) {
36148
+ const descriptorCompressedSize = source.peekUint32LE(idx + 8);
36149
+ const expectedCompressedSize = bytesEmitted + idx >>> 0;
36150
+ if (descriptorCompressedSize !== null && descriptorCompressedSize === expectedCompressedSize) {
36151
+ if (idx > 0) if (source.peekChunks && source.discard) {
36152
+ const parts = source.peekChunks(idx);
36153
+ let written = 0;
36154
+ for (const part of parts) {
36155
+ output.write(part);
36156
+ written += part.length;
36157
+ }
36158
+ if (written > 0) {
36159
+ source.discard(written);
36160
+ bytesEmitted += written;
36161
+ scanner.onConsume(written);
36162
+ }
36163
+ } else {
36164
+ output.write(source.read(idx));
36165
+ bytesEmitted += idx;
36166
+ scanner.onConsume(idx);
36167
+ }
36168
+ done = true;
36169
+ source.maybeReleaseWriteCallback?.();
36170
+ cleanup();
36171
+ output.end();
36172
+ return;
36173
+ }
36174
+ }
36111
36175
  break;
36112
36176
  }
36113
- scanner.onNoMatch(available);
36114
- const flushLen = Math.max(0, available - keepTailBytes);
36177
+ if (!pendingCandidate) scanner.onNoMatch(available);
36178
+ let maxFlush = available - keepTailBytes;
36179
+ if (pendingCandidate) maxFlush = Math.min(maxFlush, scanner.searchFrom);
36180
+ const flushLen = Math.max(0, maxFlush);
36115
36181
  if (flushLen > 0) {
36116
36182
  if (source.peekChunks && source.discard) {
36117
36183
  const parts = source.peekChunks(flushLen);
@@ -36241,7 +36307,7 @@ self.onmessage = async function(event) {
36241
36307
  }
36242
36308
  }
36243
36309
  if (!skipping) inflater.end();
36244
- await finished(entry, { readable: false });
36310
+ await awaitEntryCompletion(entry);
36245
36311
  } finally {
36246
36312
  inflater.removeListener("error", onError);
36247
36313
  entry.removeListener("error", onError);
@@ -36308,7 +36374,7 @@ self.onmessage = async function(event) {
36308
36374
  if (sizesTrusted && fileSizeKnown && inflateRawSync && needsInflate && compressedSize <= thresholdBytes && uncompressedSize <= thresholdBytes) {
36309
36375
  const decompressedData = inflateRawSync(await io.pull(compressedSize));
36310
36376
  entry.end(decompressedData);
36311
- await finished(entry, { readable: false });
36377
+ await awaitEntryCompletion(entry);
36312
36378
  return;
36313
36379
  }
36314
36380
  const inflater = needsInflate ? inflateFactory() : new PassThrough({ highWaterMark: DEFAULT_UNZIP_STREAM_HIGH_WATER_MARK });
@@ -36320,7 +36386,15 @@ self.onmessage = async function(event) {
36320
36386
  }
36321
36387
  return;
36322
36388
  }
36323
- await pipeline(io.streamUntilDataDescriptor(), inflater, entry);
36389
+ try {
36390
+ await pipeline(io.streamUntilDataDescriptor(), inflater, entry);
36391
+ } catch (pipelineErr) {
36392
+ if (!isPrematureCloseError(pipelineErr)) throw pipelineErr;
36393
+ try {
36394
+ entry.size = (await readDataDescriptor(async (l) => io.pull(l))).uncompressedSize ?? 0;
36395
+ } catch {}
36396
+ return;
36397
+ }
36324
36398
  entry.size = (await readDataDescriptor(async (l) => io.pull(l))).uncompressedSize ?? 0;
36325
36399
  }
36326
36400
  //#endregion
@@ -36702,7 +36776,18 @@ onmessage = async (ev) => {
36702
36776
  this.finished = false;
36703
36777
  this._driverState = {};
36704
36778
  this._parsingDone = Promise.resolve();
36779
+ this._parserDoneFlag = false;
36780
+ this._parserError = null;
36781
+ this._parserDeferred = null;
36782
+ this._parserDonePromise = null;
36783
+ this._entryQueue = [];
36784
+ this._entryWaiter = null;
36785
+ this._entriesDone = false;
36705
36786
  this._opts = opts;
36787
+ this.on("error", (err) => {
36788
+ this._rejectParserDeferred(err);
36789
+ this._closeEntryQueue(err);
36790
+ });
36706
36791
  const hi = Math.max(64 * 1024, opts.inputHighWaterMarkBytes ?? 2 * 1024 * 1024);
36707
36792
  const lo = Math.max(32 * 1024, opts.inputLowWaterMarkBytes ?? Math.floor(hi / 4));
36708
36793
  this._inputHighWaterMarkBytes = hi;
@@ -36722,19 +36807,17 @@ onmessage = async (ev) => {
36722
36807
  },
36723
36808
  pushEntry: (entry) => {
36724
36809
  this.push(entry);
36810
+ this._enqueueEntry(entry);
36811
+ },
36812
+ pushEntryIfPiped: (_entry) => {
36813
+ this._enqueueEntry(_entry);
36725
36814
  },
36726
- pushEntryIfPiped: (_entry) => {},
36727
36815
  emitCrxHeader: (header) => {
36728
36816
  this.crxHeader = header;
36729
36817
  this.emit("crx-header", header);
36730
36818
  },
36731
36819
  emitError: (err) => {
36732
36820
  this.__emittedError = err;
36733
- if (this._writeCb) {
36734
- const cb = this._writeCb;
36735
- this._writeCb = void 0;
36736
- cb(err);
36737
- }
36738
36821
  this.emit("error", err);
36739
36822
  },
36740
36823
  emitClose: () => {
@@ -36753,11 +36836,21 @@ onmessage = async (ev) => {
36753
36836
  return createInflateRawFn();
36754
36837
  };
36755
36838
  this._parsingDone = runParseLoop(this._opts, io, emitter, inflateFactory, this._driverState);
36756
- this._parsingDone.catch((e) => {
36839
+ this._parsingDone.then(() => {
36840
+ if (this.__emittedError) {
36841
+ this._rejectParserDeferred(this.__emittedError);
36842
+ this._closeEntryQueue(this.__emittedError);
36843
+ } else {
36844
+ this._resolveParserDeferred();
36845
+ this._closeEntryQueue();
36846
+ }
36847
+ }, (e) => {
36757
36848
  if (!this.__emittedError || this.__emittedError !== e) {
36758
36849
  this.__emittedError = e;
36759
36850
  this.emit("error", e);
36760
36851
  }
36852
+ this._rejectParserDeferred(e);
36853
+ this._closeEntryQueue(e);
36761
36854
  this.emit("close");
36762
36855
  });
36763
36856
  });
@@ -37009,11 +37102,92 @@ onmessage = async (ev) => {
37009
37102
  });
37010
37103
  }
37011
37104
  promise() {
37012
- return new Promise((resolve, reject) => {
37013
- this.on("finish", resolve);
37014
- this.on("end", resolve);
37015
- this.on("error", reject);
37105
+ if (this._parserDoneFlag) return this._parserError ? Promise.reject(this._parserError) : Promise.resolve();
37106
+ if (this._parserDonePromise) return this._parserDonePromise;
37107
+ this._parserDonePromise = new Promise((resolve, reject) => {
37108
+ this._parserDeferred = {
37109
+ resolve,
37110
+ reject
37111
+ };
37016
37112
  });
37113
+ return this._parserDonePromise;
37114
+ }
37115
+ _resolveParserDeferred() {
37116
+ if (this._parserDoneFlag) return;
37117
+ this._parserDoneFlag = true;
37118
+ if (this._parserDeferred) {
37119
+ const { resolve } = this._parserDeferred;
37120
+ this._parserDeferred = null;
37121
+ resolve();
37122
+ }
37123
+ }
37124
+ _rejectParserDeferred(err) {
37125
+ if (this._parserDoneFlag) return;
37126
+ this._parserDoneFlag = true;
37127
+ this._parserError = err;
37128
+ if (this._parserDeferred) {
37129
+ const { reject } = this._parserDeferred;
37130
+ this._parserDeferred = null;
37131
+ reject(err);
37132
+ }
37133
+ }
37134
+ _enqueueEntry(entry) {
37135
+ if (this._entryWaiter) {
37136
+ const { resolve } = this._entryWaiter;
37137
+ this._entryWaiter = null;
37138
+ resolve({
37139
+ value: entry,
37140
+ done: false
37141
+ });
37142
+ } else this._entryQueue.push(entry);
37143
+ }
37144
+ _closeEntryQueue(err) {
37145
+ this._entriesDone = true;
37146
+ if (this._entryWaiter) {
37147
+ const waiter = this._entryWaiter;
37148
+ this._entryWaiter = null;
37149
+ if (err) waiter.reject(err);
37150
+ else waiter.resolve({
37151
+ value: void 0,
37152
+ done: true
37153
+ });
37154
+ }
37155
+ }
37156
+ [Symbol.asyncIterator]() {
37157
+ const iterator = {
37158
+ next: () => {
37159
+ if (this._entryQueue.length > 0) return Promise.resolve({
37160
+ value: this._entryQueue.shift(),
37161
+ done: false
37162
+ });
37163
+ if (this._entriesDone) {
37164
+ if (this._parserError) return Promise.reject(this._parserError);
37165
+ return Promise.resolve({
37166
+ value: void 0,
37167
+ done: true
37168
+ });
37169
+ }
37170
+ return new Promise((resolve, reject) => {
37171
+ this._entryWaiter = {
37172
+ resolve,
37173
+ reject
37174
+ };
37175
+ });
37176
+ },
37177
+ return: () => {
37178
+ this._entriesDone = true;
37179
+ this._entryQueue.length = 0;
37180
+ this._entryWaiter = null;
37181
+ return Promise.resolve({
37182
+ value: void 0,
37183
+ done: true
37184
+ });
37185
+ },
37186
+ [Symbol.asyncIterator]() {
37187
+ return iterator;
37188
+ }
37189
+ };
37190
+ return iterator;
37017
37191
  }
37018
37192
  };
37019
37193
  }