@ai-ecoverse/slicc-kernel 1.84.0 → 1.85.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.
@@ -117,6 +117,9 @@ var MemoryMeta = class {
117
117
  async remove(paths) {
118
118
  for (const path of paths) this.entries.deleteTree(path);
119
119
  }
120
+ absent(path) {
121
+ return !this.entries.has(path);
122
+ }
120
123
  async links(dir) {
121
124
  return [...this.entries.entries()].map(([, e]) => e).filter((entry) => entry.dir === dir);
122
125
  }
@@ -136,6 +139,8 @@ function settle(transaction, value) {
136
139
  transaction.onabort = () => reject(transaction.error ?? new Error("metadata transaction aborted"));
137
140
  });
138
141
  }
142
+ var REMOVE_BATCH = 256;
143
+ var REMOVE_WAIT_MS = 5;
139
144
  var IndexedDbMeta = class {
140
145
  db;
141
146
  cache = new PathMap();
@@ -171,6 +176,7 @@ var IndexedDbMeta = class {
171
176
  async get(paths) {
172
177
  const missing4 = paths.filter((path) => !this.cache.has(path));
173
178
  if (missing4.length > 0) {
179
+ this.settleRemoves();
174
180
  const generation = this.generation;
175
181
  const [transaction, store] = this.transaction("readonly");
176
182
  const found = /* @__PURE__ */ new Map();
@@ -188,6 +194,7 @@ var IndexedDbMeta = class {
188
194
  return paths.map((path) => this.cache.get(path) ?? void 0);
189
195
  }
190
196
  async update(path, change) {
197
+ this.settleRemoves();
191
198
  const [transaction, store] = this.transaction("readwrite");
192
199
  const request2 = store.get(path);
193
200
  let next2;
@@ -201,6 +208,7 @@ var IndexedDbMeta = class {
201
208
  this.cache.set(path, next2 ?? null);
202
209
  }
203
210
  async move(from, to, root) {
211
+ this.settleRemoves();
204
212
  const [transaction, store] = this.transaction("readwrite");
205
213
  store.delete(to);
206
214
  store.delete(descendants(to));
@@ -217,7 +225,28 @@ var IndexedDbMeta = class {
217
225
  await settle(transaction, () => void 0);
218
226
  this.changed({ trees: [from, to] });
219
227
  }
228
+ absent(path) {
229
+ return this.cache.has(path) && this.cache.get(path) === null;
230
+ }
231
+ removing = /* @__PURE__ */ new Map();
232
+ removeTimer;
220
233
  async remove(paths) {
234
+ this.settleRemoves();
235
+ return this.removeNow(paths);
236
+ }
237
+ async removeFile(path) {
238
+ const known = this.cache.get(path);
239
+ if (!known) return this.remove([path]);
240
+ this.generation++;
241
+ this.cache.set(path, null);
242
+ this.removing.set(path, JSON.stringify(known));
243
+ if (this.removing.size >= REMOVE_BATCH) return this.flushRemoves();
244
+ this.removeTimer ??= setTimeout(
245
+ () => void this.flushRemoves().catch(() => void 0),
246
+ REMOVE_WAIT_MS
247
+ );
248
+ }
249
+ async removeNow(paths) {
221
250
  const [transaction, store] = this.transaction("readwrite");
222
251
  for (const path of paths) {
223
252
  store.delete(path);
@@ -226,12 +255,33 @@ var IndexedDbMeta = class {
226
255
  await settle(transaction, () => void 0);
227
256
  this.changed({ trees: paths });
228
257
  }
258
+ settleRemoves() {
259
+ if (this.removing.size > 0) void this.flushRemoves().catch(() => void 0);
260
+ }
261
+ flushRemoves() {
262
+ clearTimeout(this.removeTimer);
263
+ this.removeTimer = void 0;
264
+ const batch = this.removing;
265
+ if (batch.size === 0) return Promise.resolve();
266
+ this.removing = /* @__PURE__ */ new Map();
267
+ const [transaction, store] = this.transaction("readwrite");
268
+ for (const [path, seen] of batch) {
269
+ const request2 = store.get(path);
270
+ request2.onsuccess = () => {
271
+ if (request2.result && JSON.stringify(request2.result) === seen) store.delete(path);
272
+ };
273
+ }
274
+ const paths = [...batch.keys()];
275
+ return settle(transaction, () => void this.channel.postMessage({ paths }));
276
+ }
229
277
  async links(dir) {
278
+ this.settleRemoves();
230
279
  const [transaction, store] = this.transaction("readonly");
231
280
  const request2 = store.index("dir").getAll(dir);
232
281
  return settle(transaction, () => request2.result);
233
282
  }
234
283
  async under(path) {
284
+ this.settleRemoves();
235
285
  const [transaction, store] = this.transaction("readonly");
236
286
  const self = store.get(path);
237
287
  const below = store.getAll(descendants(path));
@@ -243,6 +293,7 @@ var IndexedDbMeta = class {
243
293
  );
244
294
  }
245
295
  async all() {
296
+ this.settleRemoves();
246
297
  const [transaction, store] = this.transaction("readonly");
247
298
  const request2 = store.getAll();
248
299
  return settle(transaction, () => request2.result);
@@ -806,7 +857,14 @@ function sidecar(meta) {
806
857
  const guarded2 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
807
858
  throw translate(err, pathOf(args), "sidecar", false);
808
859
  });
809
- return Object.fromEntries(META_OPS.map((op) => [op, guarded2(op)]));
860
+ const ops = Object.fromEntries(META_OPS.map((op) => [op, guarded2(op)]));
861
+ if (meta.absent) ops.absent = meta.absent.bind(meta);
862
+ const { removeFile } = meta;
863
+ if (removeFile)
864
+ ops.removeFile = (path) => removeFile.call(meta, path).catch((err) => {
865
+ throw translate(err, path, "sidecar", false);
866
+ });
867
+ return ops;
810
868
  }
811
869
  function missing2(err) {
812
870
  const code = err?.code;
@@ -1503,6 +1561,7 @@ var OpfsFs = class {
1503
1561
  throw error;
1504
1562
  }
1505
1563
  this.refuse(found.path, true);
1564
+ if (options.file && found.handle?.kind === "directory") throw fsError("EISDIR", path);
1506
1565
  if (found.handle) {
1507
1566
  const [dir, name2] = await this.parent(found.path);
1508
1567
  try {
@@ -1513,8 +1572,19 @@ var OpfsFs = class {
1513
1572
  } catch (err) {
1514
1573
  throw translate(err, path);
1515
1574
  }
1516
- this.changed(found.path);
1575
+ const file = found.handle.kind === "file";
1576
+ if (!file || this.remote?.watched()) this.changed(found.path);
1577
+ else this.forget(found.path);
1517
1578
  this.movePending(found.path);
1579
+ if (file && this.meta.absent?.(found.path)) {
1580
+ this.touch(parentOf3(found.path));
1581
+ return;
1582
+ }
1583
+ if (file && this.meta.removeFile) {
1584
+ await this.meta.removeFile(found.path);
1585
+ this.touch(parentOf3(found.path));
1586
+ return;
1587
+ }
1518
1588
  }
1519
1589
  await this.meta.remove([found.path]);
1520
1590
  this.touch(parentOf3(found.path));
@@ -8634,6 +8704,10 @@ var MountTable = class {
8634
8704
  ...error ? { error } : {}
8635
8705
  });
8636
8706
  }
8707
+ under(path) {
8708
+ for (const mount of this.mounts.values()) if (within6(mount.target, path)) return true;
8709
+ return false;
8710
+ }
8637
8711
  locate(path) {
8638
8712
  let best;
8639
8713
  for (const mount of this.mounts.values()) {
@@ -9203,9 +9277,12 @@ var MountFs = class {
9203
9277
  }
9204
9278
  async rm(path, options = {}) {
9205
9279
  const found = this.at(path);
9280
+ if (!found && options.recursive && this.table.under(normalizePath(path)))
9281
+ return this.rmAround(normalizePath(path), options);
9206
9282
  if (!found) return this.base.rm(path, options);
9207
9283
  const { mount, rel: rel2 } = found;
9208
9284
  const real = normalizePath(path);
9285
+ if (rel2 === "/" && options.recursive) await this.rmEach(real, mount, rel2, options);
9209
9286
  if (rel2 === "/") throw errnoError("EBUSY", `${real} is a mount point`);
9210
9287
  this.writable(mount, real);
9211
9288
  await this.table.commitUnder(real);
@@ -9216,16 +9293,33 @@ var MountFs = class {
9216
9293
  if (options.force && err.code === "ENOENT") return;
9217
9294
  throw err;
9218
9295
  }
9296
+ if (options.file && attr2.kind === "directory") throw errnoError("EISDIR", real);
9219
9297
  this.table.forget(mount, real);
9220
9298
  if (attr2.kind === "directory" && options.recursive) {
9221
- for (const entry of await this.table.readdir(mount, real, rel2)) {
9222
- await this.rm(`${real}/${entry.name}`, { recursive: true, force: options.force ?? false });
9223
- }
9299
+ await this.rmEach(real, mount, rel2, { recursive: true, force: options.force ?? false });
9224
9300
  this.table.forget(mount, real);
9225
9301
  }
9226
9302
  await mount.conn.call({ op: attr2.kind === "directory" ? "rmdir" : "unlink", path: rel2 });
9227
9303
  this.table.forget(mount, real);
9228
9304
  }
9305
+ async rmEach(real, mount, rel2, options) {
9306
+ let first;
9307
+ for (const entry of await this.table.readdir(mount, real, rel2)) {
9308
+ await this.rm(joinName(real, entry.name), options).catch((err) => {
9309
+ first ??= err;
9310
+ });
9311
+ }
9312
+ if (first) throw first;
9313
+ }
9314
+ async rmAround(path, options) {
9315
+ let first;
9316
+ for (const name2 of await this.base.readdir(path)) {
9317
+ await this.rm(joinName(path, name2), options).catch((err) => {
9318
+ first ??= err;
9319
+ });
9320
+ }
9321
+ throw first ?? errnoError("EBUSY", `${path} holds a mount point`);
9322
+ }
9229
9323
  async rename(from, to) {
9230
9324
  const a = this.at(from);
9231
9325
  const b = this.at(to);
@@ -9529,8 +9623,7 @@ async function run(entry, path, req) {
9529
9623
  await fs.link(path, fs.resolvePath(cwd, req.arg2 ?? ""), req.mode === 1);
9530
9624
  return done;
9531
9625
  case "unlink":
9532
- if ((await fs.lstat(path)).isDirectory) throw syncError("EISDIR", path);
9533
- await fs.rm(path);
9626
+ await fs.rm(path, { file: true });
9534
9627
  return done;
9535
9628
  case "rmdir":
9536
9629
  if (!(await fs.lstat(path)).isDirectory) throw syncError("ENOTDIR", path);
@@ -9764,6 +9857,26 @@ async function dispatchSyncFs(req) {
9764
9857
  return toErrno(err);
9765
9858
  }
9766
9859
  }
9860
+ var DENIED = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
9861
+ async function rmPermitted(entry, path, req, refused2) {
9862
+ const st = await entry.fs.lstat(path).catch(() => void 0);
9863
+ if (!st?.isDirectory) return refused2;
9864
+ const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
9865
+ if (unlisted) return unlisted;
9866
+ const names = await entry.fs.readdir(path).catch(() => void 0);
9867
+ if (!names) return refused2;
9868
+ let first;
9869
+ for (const name2 of names) {
9870
+ const child = await dispatchOne(entry, {
9871
+ ...req,
9872
+ path: joinName(path, name2)
9873
+ });
9874
+ if (!child.ok) first ??= child;
9875
+ }
9876
+ if (first) return first;
9877
+ const again = await guarded(entry, path, req);
9878
+ return again ?? await run(entry, path, req);
9879
+ }
9767
9880
  async function dispatchOne(entry, req) {
9768
9881
  const { fs, cwd } = entry;
9769
9882
  try {
@@ -9773,6 +9886,9 @@ async function dispatchOne(entry, req) {
9773
9886
  if (req.op === "access") return await accessOp(entry, path, req);
9774
9887
  if (req.op === "hold") return await holdOp(entry, path, req);
9775
9888
  const refused2 = await guarded(entry, path, req);
9889
+ if (refused2 && req.op === "rm" && !refused2.ok && DENIED.has(refused2.errno)) {
9890
+ return await rmPermitted(entry, path, req, refused2);
9891
+ }
9776
9892
  if (refused2) return refused2;
9777
9893
  if (req.op === "statfs") return json2(await usageAt(entry, path) ?? await storageUsage());
9778
9894
  if (entry.revoked?.(path)) return { ok: false, errno: "EIO", message: `${path} was unmounted` };