isomorphic-git 1.40.7 → 1.40.8

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/index.cjs CHANGED
@@ -5021,6 +5021,67 @@ async function _walk({
5021
5021
  return walk(root)
5022
5022
  }
5023
5023
 
5024
+ /**
5025
+ * Recursively creates the directory at `filepath`, creating any missing parent
5026
+ * directories along the way. This is a portable replacement for
5027
+ * `fs.mkdir(path, { recursive: true })`: recursive `mkdir` is NOT part of
5028
+ * isomorphic-git's required `fs` contract (see docs/fs.md — `mkdir` is only
5029
+ * `mkdir(path[, mode])`) and is unimplemented by some backends such as
5030
+ * lightning-fs, so we build it here on top of a single-level `mkdir(path)`
5031
+ * primitive only — mirroring how `rmRecursive` provides `rm({ recursive: true })`.
5032
+ *
5033
+ * It takes the raw single-level `mkdir` *function* (typically `fs._mkdir`)
5034
+ * rather than the FileSystem wrapper, to stay decoupled from that type.
5035
+ *
5036
+ * `EEXIST` is treated as success, so two callers concurrently creating the same
5037
+ * directory (e.g. several reflog writes into `.git/logs/refs/*` during a stash)
5038
+ * don't fail each other. On backends whose layers are only eventually
5039
+ * consistent — e.g. an async copy-on-write overlay — a just-created parent may
5040
+ * not yet be visible when its child is created, so `mkdir` can throw a
5041
+ * transient `ENOENT`; those are retried a bounded number of times rather than
5042
+ * surfaced as a spurious failure.
5043
+ *
5044
+ * @param {(filepath: string) => Promise<unknown>} mkdir - Creates a single directory (non-recursive).
5045
+ * @param {string} filepath - The directory to create.
5046
+ * @param {number} [retries=10] - Max transient-ENOENT retries after the parent exists.
5047
+ * @returns {Promise<void>}
5048
+ */
5049
+ async function mkdirp(mkdir, filepath, retries = 10) {
5050
+ try {
5051
+ await mkdir(filepath);
5052
+ } catch (err) {
5053
+ // Some fs implementations signal success by yielding a null error.
5054
+ if (err === null) return
5055
+ // The directory already exists — that's fine.
5056
+ if (err.code === 'EEXIST') return
5057
+ // Anything other than a missing parent is a real error.
5058
+ if (err.code !== 'ENOENT') throw err
5059
+ const parent = dirname(filepath);
5060
+ // Stop if we've walked past the filesystem root.
5061
+ if (parent === '.' || parent === '/' || parent === filepath) throw err
5062
+ // Create the missing parent(s) first, honoring the same retry budget.
5063
+ await mkdirp(mkdir, parent, retries);
5064
+ // Then (re)create this directory. On an eventually-consistent backend the
5065
+ // parent may not be visible immediately after its mkdir resolves, so a
5066
+ // repeated ENOENT here is transient — retry a bounded number of times,
5067
+ // yielding between attempts so the pending write can settle.
5068
+ for (let attempt = 0; ; attempt++) {
5069
+ try {
5070
+ await mkdir(filepath);
5071
+ return
5072
+ } catch (err2) {
5073
+ if (err2 === null || err2.code === 'EEXIST') return
5074
+ if (err2.code === 'ENOENT' && attempt < retries) {
5075
+ // Give the backend a turn of the event loop to become consistent.
5076
+ await new Promise(resolve => setTimeout(resolve, 0));
5077
+ continue
5078
+ }
5079
+ throw err2
5080
+ }
5081
+ }
5082
+ }
5083
+ }
5084
+
5024
5085
  /**
5025
5086
  * Removes the directory at the specified filepath recursively. Used internally to replicate the behavior of
5026
5087
  * fs.promises.rm({ recursive: true, force: true }) from Node.js 14 and above when not available. If the provided
@@ -5213,29 +5274,17 @@ class FileSystem {
5213
5274
  * Make a directory (or series of nested directories) without throwing an error if it already exists.
5214
5275
  *
5215
5276
  * @param {string} filepath - The path to the directory.
5216
- * @param {boolean} [_selfCall=false] - Internal flag to prevent infinite recursion.
5217
5277
  * @returns {Promise<void>}
5218
5278
  */
5219
- async mkdir(filepath, _selfCall = false) {
5220
- try {
5221
- await this._mkdir(filepath);
5222
- } catch (err) {
5223
- // If err is null then operation succeeded!
5224
- if (err === null) return
5225
- // If the directory already exists, that's OK!
5226
- if (err.code === 'EEXIST') return
5227
- // Avoid infinite loops of failure
5228
- if (_selfCall) throw err
5229
- // If we got a "no such file or directory error" backup and try again.
5230
- if (err.code === 'ENOENT') {
5231
- const parent = dirname(filepath);
5232
- // Check to see if we've gone too far
5233
- if (parent === '.' || parent === '/' || parent === filepath) throw err
5234
- // Infinite recursion, what could go wrong?
5235
- await this.mkdir(parent);
5236
- await this.mkdir(filepath, true);
5237
- }
5238
- }
5279
+ async mkdir(filepath) {
5280
+ // Recursively create the directory, using only single-level `_mkdir` so we
5281
+ // don't depend on `{ recursive: true }` (which isn't part of the required
5282
+ // `fs` contract — see docs/fs.md — and is unimplemented by some backends
5283
+ // e.g. lightning-fs). The helper tolerates EEXIST and the transient ENOENT
5284
+ // that eventually-consistent backends throw when a freshly created parent
5285
+ // isn't visible yet, which is what made concurrent reflog writes into
5286
+ // `.git/logs/refs/*` fail intermittently.
5287
+ await mkdirp(this._mkdir, filepath);
5239
5288
  }
5240
5289
 
5241
5290
  /**
@@ -9580,8 +9629,8 @@ function filterCapabilities(server, client) {
9580
9629
 
9581
9630
  const pkg = {
9582
9631
  name: 'isomorphic-git',
9583
- version: '1.40.7',
9584
- agent: 'git/isomorphic-git@1.40.7',
9632
+ version: '1.40.8',
9633
+ agent: 'git/isomorphic-git@1.40.8',
9585
9634
  };
9586
9635
 
9587
9636
  class FIFO {
package/index.js CHANGED
@@ -5008,6 +5008,67 @@ async function _walk({
5008
5008
  return walk(root)
5009
5009
  }
5010
5010
 
5011
+ /**
5012
+ * Recursively creates the directory at `filepath`, creating any missing parent
5013
+ * directories along the way. This is a portable replacement for
5014
+ * `fs.mkdir(path, { recursive: true })`: recursive `mkdir` is NOT part of
5015
+ * isomorphic-git's required `fs` contract (see docs/fs.md — `mkdir` is only
5016
+ * `mkdir(path[, mode])`) and is unimplemented by some backends such as
5017
+ * lightning-fs, so we build it here on top of a single-level `mkdir(path)`
5018
+ * primitive only — mirroring how `rmRecursive` provides `rm({ recursive: true })`.
5019
+ *
5020
+ * It takes the raw single-level `mkdir` *function* (typically `fs._mkdir`)
5021
+ * rather than the FileSystem wrapper, to stay decoupled from that type.
5022
+ *
5023
+ * `EEXIST` is treated as success, so two callers concurrently creating the same
5024
+ * directory (e.g. several reflog writes into `.git/logs/refs/*` during a stash)
5025
+ * don't fail each other. On backends whose layers are only eventually
5026
+ * consistent — e.g. an async copy-on-write overlay — a just-created parent may
5027
+ * not yet be visible when its child is created, so `mkdir` can throw a
5028
+ * transient `ENOENT`; those are retried a bounded number of times rather than
5029
+ * surfaced as a spurious failure.
5030
+ *
5031
+ * @param {(filepath: string) => Promise<unknown>} mkdir - Creates a single directory (non-recursive).
5032
+ * @param {string} filepath - The directory to create.
5033
+ * @param {number} [retries=10] - Max transient-ENOENT retries after the parent exists.
5034
+ * @returns {Promise<void>}
5035
+ */
5036
+ async function mkdirp(mkdir, filepath, retries = 10) {
5037
+ try {
5038
+ await mkdir(filepath);
5039
+ } catch (err) {
5040
+ // Some fs implementations signal success by yielding a null error.
5041
+ if (err === null) return
5042
+ // The directory already exists — that's fine.
5043
+ if (err.code === 'EEXIST') return
5044
+ // Anything other than a missing parent is a real error.
5045
+ if (err.code !== 'ENOENT') throw err
5046
+ const parent = dirname(filepath);
5047
+ // Stop if we've walked past the filesystem root.
5048
+ if (parent === '.' || parent === '/' || parent === filepath) throw err
5049
+ // Create the missing parent(s) first, honoring the same retry budget.
5050
+ await mkdirp(mkdir, parent, retries);
5051
+ // Then (re)create this directory. On an eventually-consistent backend the
5052
+ // parent may not be visible immediately after its mkdir resolves, so a
5053
+ // repeated ENOENT here is transient — retry a bounded number of times,
5054
+ // yielding between attempts so the pending write can settle.
5055
+ for (let attempt = 0; ; attempt++) {
5056
+ try {
5057
+ await mkdir(filepath);
5058
+ return
5059
+ } catch (err2) {
5060
+ if (err2 === null || err2.code === 'EEXIST') return
5061
+ if (err2.code === 'ENOENT' && attempt < retries) {
5062
+ // Give the backend a turn of the event loop to become consistent.
5063
+ await new Promise(resolve => setTimeout(resolve, 0));
5064
+ continue
5065
+ }
5066
+ throw err2
5067
+ }
5068
+ }
5069
+ }
5070
+ }
5071
+
5011
5072
  /**
5012
5073
  * Removes the directory at the specified filepath recursively. Used internally to replicate the behavior of
5013
5074
  * fs.promises.rm({ recursive: true, force: true }) from Node.js 14 and above when not available. If the provided
@@ -5200,29 +5261,17 @@ class FileSystem {
5200
5261
  * Make a directory (or series of nested directories) without throwing an error if it already exists.
5201
5262
  *
5202
5263
  * @param {string} filepath - The path to the directory.
5203
- * @param {boolean} [_selfCall=false] - Internal flag to prevent infinite recursion.
5204
5264
  * @returns {Promise<void>}
5205
5265
  */
5206
- async mkdir(filepath, _selfCall = false) {
5207
- try {
5208
- await this._mkdir(filepath);
5209
- } catch (err) {
5210
- // If err is null then operation succeeded!
5211
- if (err === null) return
5212
- // If the directory already exists, that's OK!
5213
- if (err.code === 'EEXIST') return
5214
- // Avoid infinite loops of failure
5215
- if (_selfCall) throw err
5216
- // If we got a "no such file or directory error" backup and try again.
5217
- if (err.code === 'ENOENT') {
5218
- const parent = dirname(filepath);
5219
- // Check to see if we've gone too far
5220
- if (parent === '.' || parent === '/' || parent === filepath) throw err
5221
- // Infinite recursion, what could go wrong?
5222
- await this.mkdir(parent);
5223
- await this.mkdir(filepath, true);
5224
- }
5225
- }
5266
+ async mkdir(filepath) {
5267
+ // Recursively create the directory, using only single-level `_mkdir` so we
5268
+ // don't depend on `{ recursive: true }` (which isn't part of the required
5269
+ // `fs` contract — see docs/fs.md — and is unimplemented by some backends
5270
+ // e.g. lightning-fs). The helper tolerates EEXIST and the transient ENOENT
5271
+ // that eventually-consistent backends throw when a freshly created parent
5272
+ // isn't visible yet, which is what made concurrent reflog writes into
5273
+ // `.git/logs/refs/*` fail intermittently.
5274
+ await mkdirp(this._mkdir, filepath);
5226
5275
  }
5227
5276
 
5228
5277
  /**
@@ -9567,8 +9616,8 @@ function filterCapabilities(server, client) {
9567
9616
 
9568
9617
  const pkg = {
9569
9618
  name: 'isomorphic-git',
9570
- version: '1.40.7',
9571
- agent: 'git/isomorphic-git@1.40.7',
9619
+ version: '1.40.8',
9620
+ agent: 'git/isomorphic-git@1.40.8',
9572
9621
  };
9573
9622
 
9574
9623
  class FIFO {