@poe-platform/safe-fs 0.1.82 → 0.1.84

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,3 +1,4 @@
1
+ import { FsError } from "../../contracts/errors.js";
1
2
  import { quotaCapabilities } from "../capabilities.js";
2
3
  export class FileSystemQuotaError extends Error {
3
4
  maxBytes;
@@ -7,19 +8,46 @@ export class FileSystemQuotaError extends Error {
7
8
  this.name = "FileSystemQuotaError";
8
9
  }
9
10
  }
10
- async function usedBytes(fs, options) {
11
+ async function usedBytes(fs, options, change) {
11
12
  let total = 0;
13
+ let possibleAliases = 0;
12
14
  const pending = ["/"];
13
15
  while (pending.length) {
16
+ options?.signal?.throwIfAborted();
14
17
  const directory = pending.pop();
15
18
  for (const entry of await fs.readdir(directory, options)) {
19
+ options?.signal?.throwIfAborted();
16
20
  const path = `${directory === "/" ? "" : directory}/${entry.name}`;
17
21
  if (entry.type === "directory")
18
22
  pending.push(path);
19
- else
20
- total += (await fs.lstat(path, options)).size;
23
+ else {
24
+ const stat = await fs.lstat(path, options);
25
+ total += stat.size;
26
+ if (!change || stat.type !== "file")
27
+ continue;
28
+ const scope = change.stat.identityScope;
29
+ const comparable = [scope, stat.identityScope].every(value => typeof value === "symbol" || typeof value === "object" && value !== null)
30
+ && [change.stat.dev, change.stat.ino, stat.dev, stat.ino].every(value => typeof value === "number" && Number.isSafeInteger(value) && value >= 0);
31
+ const compare = comparable ? undefined : fs.compareEntry;
32
+ const comparison = comparable
33
+ ? scope === stat.identityScope && change.stat.dev === stat.dev && change.stat.ino === stat.ino ? "same" : "distinct"
34
+ : compare === undefined ? "unknown" : await compare.call(fs, change.path, fs, path, options);
35
+ options?.signal?.throwIfAborted();
36
+ if (comparison !== "same" && comparison !== "distinct" && comparison !== "unknown")
37
+ throw new FsError("EIO", { syscall: "compareEntry", path: change.path, dest: path, message: "invalid entry comparison" });
38
+ if (comparison === "same") {
39
+ total += change.delta;
40
+ possibleAliases++;
41
+ }
42
+ else if (comparison !== "distinct") {
43
+ total += Math.max(0, change.delta);
44
+ possibleAliases++;
45
+ }
46
+ }
21
47
  }
22
48
  }
49
+ if (change && possibleAliases === 0)
50
+ total += Math.max(0, change.delta);
23
51
  return total;
24
52
  }
25
53
  async function existingBytes(fs, path, options) {
@@ -43,8 +71,20 @@ export function withFileSystemQuota(fs, options) {
43
71
  return result;
44
72
  };
45
73
  const assertDelta = async (path, nextBytes, fsOptions) => {
46
- const current = await existingBytes(fs, path, fsOptions);
47
- if (await usedBytes(fs, fsOptions) - current + nextBytes > options.maxBytes)
74
+ fsOptions?.signal?.throwIfAborted();
75
+ let current;
76
+ try {
77
+ current = await fs.stat(path, fsOptions);
78
+ }
79
+ catch (error) {
80
+ if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
81
+ throw error;
82
+ }
83
+ const projected = current?.type === "file" && nextBytes > current.size
84
+ ? await usedBytes(fs, fsOptions, { path, stat: current, delta: nextBytes - current.size })
85
+ : await usedBytes(fs, fsOptions) - (current?.type === "directory" ? 0 : current?.size ?? 0) + nextBytes;
86
+ fsOptions?.signal?.throwIfAborted();
87
+ if (projected > options.maxBytes)
48
88
  throw new FileSystemQuotaError(options.maxBytes);
49
89
  };
50
90
  const mutations = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poe-platform/safe-fs",
3
- "version": "0.1.82",
3
+ "version": "0.1.84",
4
4
  "description": "Composable filesystem with a portable core and explicit Node adapters",
5
5
  "type": "module",
6
6
  "license": "MIT",