isomorphic-git 1.13.1 → 1.15.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.
package/README.md CHANGED
@@ -139,7 +139,7 @@ unless there is a major version bump.
139
139
  <!-- prettier-ignore-start -->
140
140
  <!-- markdownlint-disable -->
141
141
 
142
- <!-- autogenerated_by: __tests__/__helpers__/generate-docs.js -->
142
+ <!-- autogenerated_by: __tests__/__helpers__/generate-docs.cjs -->
143
143
 
144
144
  - [add](https://isomorphic-git.github.io/docs/add.html)
145
145
  - [addNote](https://isomorphic-git.github.io/docs/addNote.html)
@@ -195,6 +195,7 @@ unless there is a major version bump.
195
195
  - [status](https://isomorphic-git.github.io/docs/status.html)
196
196
  - [statusMatrix](https://isomorphic-git.github.io/docs/statusMatrix.html)
197
197
  - [tag](https://isomorphic-git.github.io/docs/tag.html)
198
+ - [updateIndex](https://isomorphic-git.github.io/docs/updateIndex.html)
198
199
  - [version](https://isomorphic-git.github.io/docs/version.html)
199
200
  - [walk](https://isomorphic-git.github.io/docs/walk.html)
200
201
  - [writeBlob](https://isomorphic-git.github.io/docs/writeBlob.html)
@@ -340,6 +341,7 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds
340
341
  <td align="center"><a href="https://github.com/mtlewis"><img src="https://avatars.githubusercontent.com/u/542836?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Mike Lewis</b></sub></a><br /><a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=mtlewis" title="Documentation">📖</a></td>
341
342
  <td align="center"><a href="https://twitter.com/SamVerschueren"><img src="https://avatars.githubusercontent.com/u/1913805?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Sam Verschueren</b></sub></a><br /><a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=SamVerschueren" title="Code">💻</a></td>
342
343
  <td align="center"><a href="http://vitorluizc.github.io/"><img src="https://avatars.githubusercontent.com/u/9027363?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Vitor Luiz Cavalcanti</b></sub></a><br /><a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=VitorLuizC" title="Documentation">📖</a></td>
344
+ <td align="center"><a href="https://www.platformdemos.com/"><img src="https://avatars.githubusercontent.com/u/4261788?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Shane McLaughlin</b></sub></a><br /><a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=mshanemc" title="Code">💻</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=mshanemc" title="Documentation">📖</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=mshanemc" title="Tests">⚠️</a></td>
343
345
  </tr>
344
346
  </table>
345
347
 
@@ -1,8 +1,8 @@
1
1
  [
2
2
  "HeadlessChrome 0.0.0 (Linux 0.0.0)",
3
3
  "Firefox 97.0.0 (Ubuntu 0.0.0)",
4
- "Chrome Mobile 96.0.4664 (Android 0.0.0)",
5
4
  "Chrome 79.0.3945 (Windows 10 0.0.0)",
5
+ "Mobile Safari 13.0.0 (iOS 13.0.0)",
6
6
  "Safari 13.1.0 (Mac OS X 10.15.4)",
7
- "Mobile Safari 13.0.0 (iOS 13.0.0)"
7
+ "Chrome Mobile 96.0.4664 (Android 0.0.0)"
8
8
  ]
package/index.cjs CHANGED
@@ -762,6 +762,10 @@ class GitIndex {
762
762
  this._dirty = true;
763
763
  }
764
764
 
765
+ has({ filepath }) {
766
+ return this._entries.has(filepath)
767
+ }
768
+
765
769
  render() {
766
770
  return this.entries
767
771
  .map(entry => `${entry.mode.toString(8)} ${entry.oid} ${entry.path}`)
@@ -871,7 +875,7 @@ class GitIndexManager {
871
875
  const filepath = `${gitdir}/index`;
872
876
  if (lock === null) lock = new AsyncLock({ maxPending: Infinity });
873
877
  let result;
874
- await lock.acquire(filepath, async function() {
878
+ await lock.acquire(filepath, async () => {
875
879
  // Acquire a file lock while we're reading the index
876
880
  // to make sure other processes aren't writing to it
877
881
  // simultaneously, which could result in a corrupted index.
@@ -891,6 +895,7 @@ class GitIndexManager {
891
895
  index._dirty = false;
892
896
  }
893
897
  });
898
+
894
899
  return result
895
900
  }
896
901
  }
@@ -3118,12 +3123,14 @@ HttpError.code = 'HttpError';
3118
3123
 
3119
3124
  class InvalidFilepathError extends BaseError {
3120
3125
  /**
3121
- * @param {'leading-slash'|'trailing-slash'} [reason]
3126
+ * @param {'leading-slash'|'trailing-slash'|'directory'} [reason]
3122
3127
  */
3123
3128
  constructor(reason) {
3124
3129
  let message = 'invalid filepath';
3125
3130
  if (reason === 'leading-slash' || reason === 'trailing-slash') {
3126
3131
  message = `"filepath" parameter should not include leading or trailing directory separators because these can cause problems on some platforms.`;
3132
+ } else if (reason === 'directory') {
3133
+ message = `"filepath" should not be a directory.`;
3127
3134
  }
3128
3135
  super(message);
3129
3136
  this.code = this.name = InvalidFilepathError.code;
@@ -4022,6 +4029,23 @@ function WORKDIR() {
4022
4029
 
4023
4030
  // @ts-check
4024
4031
 
4032
+ class MultipleGitError extends BaseError {
4033
+ /**
4034
+ * @param {Error[]} errors
4035
+ * @param {string} message
4036
+ */
4037
+ constructor(errors) {
4038
+ super(
4039
+ `There are multiple errors that were thrown by the method. Please refer to the "errors" property to see more`
4040
+ );
4041
+ this.code = this.name = MultipleGitError.code;
4042
+ this.data = { errors };
4043
+ this.errors = errors;
4044
+ }
4045
+ }
4046
+ /** @type {'MultipleGitError'} */
4047
+ MultipleGitError.code = 'MultipleGitError';
4048
+
4025
4049
  // I'm putting this in a Manager because I reckon it could benefit
4026
4050
  // from a LOT of cacheing.
4027
4051
  class GitIgnoreManager {
@@ -4412,12 +4436,6 @@ function assertParameter(name, value) {
4412
4436
  }
4413
4437
  }
4414
4438
 
4415
- function posixifyPathBuffer(buffer) {
4416
- let idx;
4417
- while (~(idx = buffer.indexOf(92))) buffer[idx] = 47;
4418
- return buffer
4419
- }
4420
-
4421
4439
  // @ts-check
4422
4440
 
4423
4441
  /**
@@ -4427,7 +4445,7 @@ function posixifyPathBuffer(buffer) {
4427
4445
  * @param {FsClient} args.fs - a file system implementation
4428
4446
  * @param {string} args.dir - The [working tree](dir-vs-gitdir.md) directory path
4429
4447
  * @param {string} [args.gitdir=join(dir, '.git')] - [required] The [git directory](dir-vs-gitdir.md) path
4430
- * @param {string} args.filepath - The path to the file to add to the index
4448
+ * @param {string|string[]} args.filepath - The path to the file to add to the index
4431
4449
  * @param {object} [args.cache] - a [cache](cache.md) object
4432
4450
  *
4433
4451
  * @returns {Promise<void>} Resolves successfully once the git index has been updated
@@ -4449,11 +4467,11 @@ async function add({
4449
4467
  assertParameter('fs', _fs);
4450
4468
  assertParameter('dir', dir);
4451
4469
  assertParameter('gitdir', gitdir);
4452
- assertParameter('filepath', filepath);
4470
+ assertParameter('filepaths', filepath);
4453
4471
 
4454
4472
  const fs = new FileSystem(_fs);
4455
- await GitIndexManager.acquire({ fs, gitdir, cache }, async function(index) {
4456
- await addToIndex({ dir, gitdir, fs, filepath, index });
4473
+ await GitIndexManager.acquire({ fs, gitdir, cache }, async index => {
4474
+ return addToIndex({ dir, gitdir, fs, filepath, index })
4457
4475
  });
4458
4476
  } catch (err) {
4459
4477
  err.caller = 'git.add';
@@ -4463,29 +4481,56 @@ async function add({
4463
4481
 
4464
4482
  async function addToIndex({ dir, gitdir, fs, filepath, index }) {
4465
4483
  // TODO: Should ignore UNLESS it's already in the index.
4466
- const ignored = await GitIgnoreManager.isIgnored({
4467
- fs,
4468
- dir,
4469
- gitdir,
4470
- filepath,
4484
+ filepath = Array.isArray(filepath) ? filepath : [filepath];
4485
+ const promises = filepath.map(async currentFilepath => {
4486
+ const ignored = await GitIgnoreManager.isIgnored({
4487
+ fs,
4488
+ dir,
4489
+ gitdir,
4490
+ filepath: currentFilepath,
4491
+ });
4492
+ if (ignored) return
4493
+ const stats = await fs.lstat(join(dir, currentFilepath));
4494
+ if (!stats) throw new NotFoundError(currentFilepath)
4495
+
4496
+ if (stats.isDirectory()) {
4497
+ const children = await fs.readdir(join(dir, currentFilepath));
4498
+ const promises = children.map(child =>
4499
+ addToIndex({
4500
+ dir,
4501
+ gitdir,
4502
+ fs,
4503
+ filepath: [join(currentFilepath, child)],
4504
+ index,
4505
+ })
4506
+ );
4507
+ await Promise.all(promises);
4508
+ } else {
4509
+ const object = stats.isSymbolicLink()
4510
+ ? await fs.readlink(join(dir, currentFilepath))
4511
+ : await fs.read(join(dir, currentFilepath));
4512
+ if (object === null) throw new NotFoundError(currentFilepath)
4513
+ const oid = await _writeObject({ fs, gitdir, type: 'blob', object });
4514
+ index.insert({ filepath: currentFilepath, stats, oid });
4515
+ }
4471
4516
  });
4472
- if (ignored) return
4473
- const stats = await fs.lstat(join(dir, filepath));
4474
- if (!stats) throw new NotFoundError(filepath)
4475
- if (stats.isDirectory()) {
4476
- const children = await fs.readdir(join(dir, filepath));
4477
- const promises = children.map(child =>
4478
- addToIndex({ dir, gitdir, fs, filepath: join(filepath, child), index })
4479
- );
4480
- await Promise.all(promises);
4481
- } else {
4482
- const object = stats.isSymbolicLink()
4483
- ? await fs.readlink(join(dir, filepath)).then(posixifyPathBuffer)
4484
- : await fs.read(join(dir, filepath));
4485
- if (object === null) throw new NotFoundError(filepath)
4486
- const oid = await _writeObject({ fs, gitdir, type: 'blob', object });
4487
- index.insert({ filepath, stats, oid });
4517
+
4518
+ const settledPromises = await Promise.allSettled(promises);
4519
+ const rejectedPromises = settledPromises
4520
+ .filter(settle => settle.status === 'rejected')
4521
+ .map(settle => settle.reason);
4522
+ if (rejectedPromises.length > 1) {
4523
+ throw new MultipleGitError(rejectedPromises)
4524
+ }
4525
+ if (rejectedPromises.length === 1) {
4526
+ throw rejectedPromises[0]
4488
4527
  }
4528
+
4529
+ const fulfilledPromises = settledPromises
4530
+ .filter(settle => settle.status === 'fulfilled' && settle.value)
4531
+ .map(settle => settle.value);
4532
+
4533
+ return fulfilledPromises
4489
4534
  }
4490
4535
 
4491
4536
  // @ts-check
@@ -5840,7 +5885,7 @@ async function analyze({
5840
5885
 
5841
5886
  // This is a kind of silly pattern but it worked so well for me in the past
5842
5887
  // and it makes intuitively demonstrating exhaustiveness so *easy*.
5843
- // This checks for the presense and/or absense of each of the 3 entries,
5888
+ // This checks for the presense and/or absence of each of the 3 entries,
5844
5889
  // converts that to a 3-bit binary representation, and then handles
5845
5890
  // every possible combination (2^3 or 8 cases) with a lookup table.
5846
5891
  const key = [!!stage, !!commit, !!workdir].map(Number).join('');
@@ -6873,8 +6918,8 @@ function filterCapabilities(server, client) {
6873
6918
 
6874
6919
  const pkg = {
6875
6920
  name: 'isomorphic-git',
6876
- version: '1.13.1',
6877
- agent: 'git/isomorphic-git@1.13.1',
6921
+ version: '1.15.1',
6922
+ agent: 'git/isomorphic-git@1.15.1',
6878
6923
  };
6879
6924
 
6880
6925
  class FIFO {
@@ -13450,6 +13495,162 @@ async function tag({
13450
13495
 
13451
13496
  // @ts-check
13452
13497
 
13498
+ /**
13499
+ * Register file contents in the working tree or object database to the git index (aka staging area).
13500
+ *
13501
+ * @param {object} args
13502
+ * @param {FsClient} args.fs - a file system client
13503
+ * @param {string} args.dir - The [working tree](dir-vs-gitdir.md) directory path
13504
+ * @param {string} [args.gitdir=join(dir, '.git')] - [required] The [git directory](dir-vs-gitdir.md) path
13505
+ * @param {string} args.filepath - File to act upon.
13506
+ * @param {string} [args.oid] - OID of the object in the object database to add to the index with the specified filepath.
13507
+ * @param {number} [args.mode = 100644] - The file mode to add the file to the index.
13508
+ * @param {boolean} [args.add] - Adds the specified file to the index if it does not yet exist in the index.
13509
+ * @param {boolean} [args.remove] - Remove the specified file from the index if it does not exist in the workspace anymore.
13510
+ * @param {boolean} [args.force] - Remove the specified file from the index, even if it still exists in the workspace.
13511
+ * @param {object} [args.cache] - a [cache](cache.md) object
13512
+ *
13513
+ * @returns {Promise<string | void>} Resolves successfully with the SHA-1 object id of the object written or updated in the index, or nothing if the file was removed.
13514
+ *
13515
+ * @example
13516
+ * await git.updateIndex({
13517
+ * fs,
13518
+ * dir: '/tutorial',
13519
+ * filepath: 'readme.md'
13520
+ * })
13521
+ *
13522
+ * @example
13523
+ * // Manually create a blob in the object database.
13524
+ * let oid = await git.writeBlob({
13525
+ * fs,
13526
+ * dir: '/tutorial',
13527
+ * blob: new Uint8Array([])
13528
+ * })
13529
+ *
13530
+ * // Write the object in the object database to the index.
13531
+ * await git.updateIndex({
13532
+ * fs,
13533
+ * dir: '/tutorial',
13534
+ * add: true,
13535
+ * filepath: 'readme.md',
13536
+ * oid
13537
+ * })
13538
+ */
13539
+ async function updateIndex({
13540
+ fs: _fs,
13541
+ dir,
13542
+ gitdir = join(dir, '.git'),
13543
+ cache = {},
13544
+ filepath,
13545
+ oid,
13546
+ mode,
13547
+ add,
13548
+ remove,
13549
+ force,
13550
+ }) {
13551
+ try {
13552
+ assertParameter('fs', _fs);
13553
+ assertParameter('gitdir', gitdir);
13554
+ assertParameter('filepath', filepath);
13555
+
13556
+ const fs = new FileSystem(_fs);
13557
+
13558
+ if (remove) {
13559
+ return await GitIndexManager.acquire(
13560
+ { fs, gitdir, cache },
13561
+ async function(index) {
13562
+ let fileStats;
13563
+
13564
+ if (!force) {
13565
+ // Check if the file is still present in the working directory
13566
+ fileStats = await fs.lstat(join(dir, filepath));
13567
+
13568
+ if (fileStats) {
13569
+ // Do nothing if we don't force and the file still exists in the workdir
13570
+ return
13571
+ }
13572
+ }
13573
+
13574
+ // Remove the file from the index if it's forced or the file does not exist
13575
+ index.delete({
13576
+ filepath,
13577
+ });
13578
+ }
13579
+ )
13580
+ }
13581
+
13582
+ // Test if it is a file and exists on disk if `remove` is not provided, only of no oid is provided
13583
+ let fileStats;
13584
+
13585
+ if (!oid) {
13586
+ fileStats = await fs.lstat(join(dir, filepath));
13587
+
13588
+ if (!fileStats) {
13589
+ throw new NotFoundError(
13590
+ `file at "${filepath}" on disk and "remove" not set`
13591
+ )
13592
+ }
13593
+
13594
+ if (fileStats.isDirectory()) {
13595
+ throw new InvalidFilepathError('directory')
13596
+ }
13597
+ }
13598
+
13599
+ return await GitIndexManager.acquire({ fs, gitdir, cache }, async function(
13600
+ index
13601
+ ) {
13602
+ if (!add && !index.has({ filepath })) {
13603
+ // If the index does not contain the filepath yet and `add` is not set, we should throw
13604
+ throw new NotFoundError(
13605
+ `file at "${filepath}" in index and "add" not set`
13606
+ )
13607
+ }
13608
+
13609
+ // By default we use 0 for the stats of the index file
13610
+ let stats = {
13611
+ ctime: new Date(0),
13612
+ mtime: new Date(0),
13613
+ dev: 0,
13614
+ ino: 0,
13615
+ mode,
13616
+ uid: 0,
13617
+ gid: 0,
13618
+ size: 0,
13619
+ };
13620
+
13621
+ if (!oid) {
13622
+ stats = fileStats;
13623
+
13624
+ // Write the file to the object database
13625
+ const object = stats.isSymbolicLink()
13626
+ ? await fs.readlink(join(dir, filepath))
13627
+ : await fs.read(join(dir, filepath));
13628
+
13629
+ oid = await _writeObject({
13630
+ fs,
13631
+ gitdir,
13632
+ type: 'blob',
13633
+ format: 'content',
13634
+ object,
13635
+ });
13636
+ }
13637
+
13638
+ index.insert({
13639
+ filepath,
13640
+ oid: oid,
13641
+ stats,
13642
+ });
13643
+
13644
+ return oid
13645
+ })
13646
+ } catch (err) {
13647
+ err.caller = 'git.updateIndex';
13648
+ throw err
13649
+ }
13650
+ }
13651
+
13652
+ // @ts-check
13653
+
13453
13654
  /**
13454
13655
  * Return the version number of isomorphic-git
13455
13656
  *
@@ -14220,6 +14421,7 @@ var index = {
14220
14421
  removeNote,
14221
14422
  renameBranch,
14222
14423
  resetIndex,
14424
+ updateIndex,
14223
14425
  resolveRef,
14224
14426
  status,
14225
14427
  statusMatrix,
@@ -14293,6 +14495,7 @@ exports.setConfig = setConfig;
14293
14495
  exports.status = status;
14294
14496
  exports.statusMatrix = statusMatrix;
14295
14497
  exports.tag = tag;
14498
+ exports.updateIndex = updateIndex;
14296
14499
  exports.version = version;
14297
14500
  exports.walk = walk;
14298
14501
  exports.writeBlob = writeBlob;
package/index.d.ts CHANGED
@@ -685,6 +685,7 @@ declare namespace index {
685
685
  export { removeNote };
686
686
  export { renameBranch };
687
687
  export { resetIndex };
688
+ export { updateIndex };
688
689
  export { resolveRef };
689
690
  export { status };
690
691
  export { statusMatrix };
@@ -751,7 +752,7 @@ export function WORKDIR(): Walker;
751
752
  * @param {FsClient} args.fs - a file system implementation
752
753
  * @param {string} args.dir - The [working tree](dir-vs-gitdir.md) directory path
753
754
  * @param {string} [args.gitdir=join(dir, '.git')] - [required] The [git directory](dir-vs-gitdir.md) path
754
- * @param {string} args.filepath - The path to the file to add to the index
755
+ * @param {string|string[]} args.filepath - The path to the file to add to the index
755
756
  * @param {object} [args.cache] - a [cache](cache.md) object
756
757
  *
757
758
  * @returns {Promise<void>} Resolves successfully once the git index has been updated
@@ -766,7 +767,7 @@ export function add({ fs: _fs, dir, gitdir, filepath, cache, }: {
766
767
  fs: CallbackFsClient | PromiseFsClient;
767
768
  dir: string;
768
769
  gitdir?: string;
769
- filepath: string;
770
+ filepath: string | string[];
770
771
  cache?: any;
771
772
  }): Promise<void>;
772
773
  /**
@@ -3107,6 +3108,59 @@ export function tag({ fs: _fs, dir, gitdir, ref, object, force, }: {
3107
3108
  object?: string;
3108
3109
  force?: boolean;
3109
3110
  }): Promise<void>;
3111
+ /**
3112
+ * Register file contents in the working tree or object database to the git index (aka staging area).
3113
+ *
3114
+ * @param {object} args
3115
+ * @param {FsClient} args.fs - a file system client
3116
+ * @param {string} args.dir - The [working tree](dir-vs-gitdir.md) directory path
3117
+ * @param {string} [args.gitdir=join(dir, '.git')] - [required] The [git directory](dir-vs-gitdir.md) path
3118
+ * @param {string} args.filepath - File to act upon.
3119
+ * @param {string} [args.oid] - OID of the object in the object database to add to the index with the specified filepath.
3120
+ * @param {number} [args.mode = 100644] - The file mode to add the file to the index.
3121
+ * @param {boolean} [args.add] - Adds the specified file to the index if it does not yet exist in the index.
3122
+ * @param {boolean} [args.remove] - Remove the specified file from the index if it does not exist in the workspace anymore.
3123
+ * @param {boolean} [args.force] - Remove the specified file from the index, even if it still exists in the workspace.
3124
+ * @param {object} [args.cache] - a [cache](cache.md) object
3125
+ *
3126
+ * @returns {Promise<string | void>} Resolves successfully with the SHA-1 object id of the object written or updated in the index, or nothing if the file was removed.
3127
+ *
3128
+ * @example
3129
+ * await git.updateIndex({
3130
+ * fs,
3131
+ * dir: '/tutorial',
3132
+ * filepath: 'readme.md'
3133
+ * })
3134
+ *
3135
+ * @example
3136
+ * // Manually create a blob in the object database.
3137
+ * let oid = await git.writeBlob({
3138
+ * fs,
3139
+ * dir: '/tutorial',
3140
+ * blob: new Uint8Array([])
3141
+ * })
3142
+ *
3143
+ * // Write the object in the object database to the index.
3144
+ * await git.updateIndex({
3145
+ * fs,
3146
+ * dir: '/tutorial',
3147
+ * add: true,
3148
+ * filepath: 'readme.md',
3149
+ * oid
3150
+ * })
3151
+ */
3152
+ export function updateIndex({ fs: _fs, dir, gitdir, cache, filepath, oid, mode, add, remove, force, }: {
3153
+ fs: CallbackFsClient | PromiseFsClient;
3154
+ dir: string;
3155
+ gitdir?: string;
3156
+ filepath: string;
3157
+ oid?: string;
3158
+ mode?: number;
3159
+ add?: boolean;
3160
+ remove?: boolean;
3161
+ force?: boolean;
3162
+ cache?: any;
3163
+ }): Promise<string | void>;
3110
3164
  /**
3111
3165
  * Return the version number of isomorphic-git
3112
3166
  *
@@ -3735,13 +3789,13 @@ declare namespace InternalError {
3735
3789
  }
3736
3790
  declare class InvalidFilepathError extends BaseError {
3737
3791
  /**
3738
- * @param {'leading-slash'|'trailing-slash'} [reason]
3792
+ * @param {'leading-slash'|'trailing-slash'|'directory'} [reason]
3739
3793
  */
3740
- constructor(reason?: "leading-slash" | "trailing-slash" | undefined);
3794
+ constructor(reason?: "leading-slash" | "trailing-slash" | "directory" | undefined);
3741
3795
  code: "InvalidFilepathError";
3742
3796
  name: "InvalidFilepathError";
3743
3797
  data: {
3744
- reason: "leading-slash" | "trailing-slash" | undefined;
3798
+ reason: "leading-slash" | "trailing-slash" | "directory" | undefined;
3745
3799
  };
3746
3800
  }
3747
3801
  declare namespace InvalidFilepathError {