@ai-ecoverse/slicc-kernel 1.36.0 → 1.37.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 +3 -1
- package/dist/kernel-worker.js +391 -67
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +29 -12
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +394 -70
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +29 -12
- package/dist/process-worker.js.map +2 -2
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -4733,6 +4733,35 @@ async function scanFilesystems(fs, modules) {
|
|
|
4733
4733
|
}
|
|
4734
4734
|
return found;
|
|
4735
4735
|
}
|
|
4736
|
+
var SUFFIX = /^\.[A-Za-z0-9_+-][A-Za-z0-9._+-]*$/;
|
|
4737
|
+
async function scanBinfmts(fs, modules) {
|
|
4738
|
+
const found = /* @__PURE__ */ new Map();
|
|
4739
|
+
for (const pkg of await packagesIn(fs, modules)) {
|
|
4740
|
+
let manifest;
|
|
4741
|
+
try {
|
|
4742
|
+
manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
|
|
4743
|
+
} catch {
|
|
4744
|
+
continue;
|
|
4745
|
+
}
|
|
4746
|
+
const declared = manifest.slicc?.binfmt;
|
|
4747
|
+
if (!declared || typeof declared !== "object") continue;
|
|
4748
|
+
for (const [suffix, command] of Object.entries(declared)) {
|
|
4749
|
+
if (!SUFFIX.test(suffix) || typeof command !== "string" || !NAME.test(command)) continue;
|
|
4750
|
+
if (!found.has(suffix)) found.set(suffix, command);
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4753
|
+
return found;
|
|
4754
|
+
}
|
|
4755
|
+
function binfmtOf(binfmts, path) {
|
|
4756
|
+
const name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
4757
|
+
let best;
|
|
4758
|
+
for (const suffix of binfmts.keys()) {
|
|
4759
|
+
if (name2.length > suffix.length && name2.endsWith(suffix)) {
|
|
4760
|
+
if (best === void 0 || suffix.length > best.length) best = suffix;
|
|
4761
|
+
}
|
|
4762
|
+
}
|
|
4763
|
+
return best === void 0 ? void 0 : binfmts.get(best);
|
|
4764
|
+
}
|
|
4736
4765
|
|
|
4737
4766
|
// src/fs/commands.ts
|
|
4738
4767
|
var S_IFDIR2 = 16384;
|
|
@@ -4849,13 +4878,193 @@ function withCommandDirs(fs, names) {
|
|
|
4849
4878
|
};
|
|
4850
4879
|
}
|
|
4851
4880
|
|
|
4881
|
+
// src/fs/unlinked.ts
|
|
4882
|
+
var UNLINKED_DIR = ".slicc-unlinked";
|
|
4883
|
+
function webLocks() {
|
|
4884
|
+
return globalThis.navigator?.locks;
|
|
4885
|
+
}
|
|
4886
|
+
var within3 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
4887
|
+
var parentOf3 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
4888
|
+
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
4889
|
+
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
4890
|
+
var PathGate = class {
|
|
4891
|
+
tails = /* @__PURE__ */ new Map();
|
|
4892
|
+
async acquire(path) {
|
|
4893
|
+
const prev = this.tails.get(path) ?? Promise.resolve();
|
|
4894
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
4895
|
+
const tail = prev.then(() => promise);
|
|
4896
|
+
this.tails.set(path, tail);
|
|
4897
|
+
await prev;
|
|
4898
|
+
return () => {
|
|
4899
|
+
resolve();
|
|
4900
|
+
if (this.tails.get(path) === tail) this.tails.delete(path);
|
|
4901
|
+
};
|
|
4902
|
+
}
|
|
4903
|
+
async run(path, op) {
|
|
4904
|
+
const release = await this.acquire(path);
|
|
4905
|
+
try {
|
|
4906
|
+
return await op();
|
|
4907
|
+
} finally {
|
|
4908
|
+
release();
|
|
4909
|
+
}
|
|
4910
|
+
}
|
|
4911
|
+
};
|
|
4912
|
+
var UnlinkedKeeper = class {
|
|
4913
|
+
fs;
|
|
4914
|
+
holders;
|
|
4915
|
+
rootOf;
|
|
4916
|
+
locks;
|
|
4917
|
+
busy;
|
|
4918
|
+
waiting = /* @__PURE__ */ new Set();
|
|
4919
|
+
instance = crypto.randomUUID();
|
|
4920
|
+
gate = new PathGate();
|
|
4921
|
+
queue = Promise.resolve();
|
|
4922
|
+
count = 0;
|
|
4923
|
+
constructor(fs, holders, rootOf, locks, busy = () => false) {
|
|
4924
|
+
this.fs = fs;
|
|
4925
|
+
this.holders = holders;
|
|
4926
|
+
this.rootOf = rootOf;
|
|
4927
|
+
this.locks = locks;
|
|
4928
|
+
this.busy = busy;
|
|
4929
|
+
void locks?.request(lockName(this.instance), () => new Promise(() => void 0));
|
|
4930
|
+
}
|
|
4931
|
+
serial(op) {
|
|
4932
|
+
const next = this.queue.then(op);
|
|
4933
|
+
this.queue = next.catch(() => void 0);
|
|
4934
|
+
return next;
|
|
4935
|
+
}
|
|
4936
|
+
wrap() {
|
|
4937
|
+
const fs = this.fs;
|
|
4938
|
+
const keeper = this;
|
|
4939
|
+
const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
|
|
4940
|
+
return {
|
|
4941
|
+
...fs,
|
|
4942
|
+
async rm(path, options) {
|
|
4943
|
+
const target = fs.resolvePath("/", path);
|
|
4944
|
+
const pinned = await keeper.pin(target);
|
|
4945
|
+
if (pinned.moved.has(target)) pinned.commit();
|
|
4946
|
+
else await keeper.settle(pinned, () => fs.rm(path, options));
|
|
4947
|
+
},
|
|
4948
|
+
async rename(from, to) {
|
|
4949
|
+
const pinned = await keeper.pin(fs.resolvePath("/", to));
|
|
4950
|
+
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
4951
|
+
},
|
|
4952
|
+
readdir: async (path) => visible(await fs.readdir(path)),
|
|
4953
|
+
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR)
|
|
4954
|
+
};
|
|
4955
|
+
}
|
|
4956
|
+
async settle(pinned, op) {
|
|
4957
|
+
try {
|
|
4958
|
+
await op();
|
|
4959
|
+
} catch (err) {
|
|
4960
|
+
await pinned.rollback();
|
|
4961
|
+
throw err;
|
|
4962
|
+
}
|
|
4963
|
+
pinned.commit();
|
|
4964
|
+
}
|
|
4965
|
+
async pin(target) {
|
|
4966
|
+
const held = /* @__PURE__ */ new Map();
|
|
4967
|
+
for (const holder of this.holders) {
|
|
4968
|
+
if (holder.owns(target)) continue;
|
|
4969
|
+
for (const path of holder.opens.keys()) {
|
|
4970
|
+
if (!within3(path, target) || holder.unlinked.has(path)) continue;
|
|
4971
|
+
held.set(path, [...held.get(path) ?? [], holder]);
|
|
4972
|
+
}
|
|
4973
|
+
}
|
|
4974
|
+
const paths = [...held.keys()].sort();
|
|
4975
|
+
const releases = [];
|
|
4976
|
+
for (const path of paths) releases.push(await this.gate.acquire(path));
|
|
4977
|
+
const hidden = /* @__PURE__ */ new Map();
|
|
4978
|
+
for (const path of paths) {
|
|
4979
|
+
const at = await this.hide(path);
|
|
4980
|
+
if (at !== void 0) hidden.set(path, at);
|
|
4981
|
+
}
|
|
4982
|
+
const done4 = () => {
|
|
4983
|
+
for (const release of releases) release();
|
|
4984
|
+
};
|
|
4985
|
+
return {
|
|
4986
|
+
moved: new Set(hidden.keys()),
|
|
4987
|
+
commit: () => {
|
|
4988
|
+
for (const [path, at] of hidden) {
|
|
4989
|
+
const holders = held.get(path);
|
|
4990
|
+
const file = this.keptFile(at, { refs: holders.length });
|
|
4991
|
+
for (const holder of holders) holder.unlinked.set(path, file);
|
|
4992
|
+
}
|
|
4993
|
+
done4();
|
|
4994
|
+
},
|
|
4995
|
+
rollback: async () => {
|
|
4996
|
+
for (const [path, at] of hidden) {
|
|
4997
|
+
await this.serial(async () => {
|
|
4998
|
+
await Promise.allSettled([this.fs.rename(at, path)]);
|
|
4999
|
+
await this.tidy(parentOf3(at));
|
|
5000
|
+
});
|
|
5001
|
+
}
|
|
5002
|
+
done4();
|
|
5003
|
+
}
|
|
5004
|
+
};
|
|
5005
|
+
}
|
|
5006
|
+
keptFile(hidden, entry) {
|
|
5007
|
+
return {
|
|
5008
|
+
hidden,
|
|
5009
|
+
release: () => {
|
|
5010
|
+
if (--entry.refs === 0) void this.serial(() => this.drop(hidden));
|
|
5011
|
+
}
|
|
5012
|
+
};
|
|
5013
|
+
}
|
|
5014
|
+
hide(path) {
|
|
5015
|
+
return this.serial(async () => {
|
|
5016
|
+
const dir = dirOf(this.rootOf(path));
|
|
5017
|
+
const hidden = `${dir}/${this.instance}.${++this.count}`;
|
|
5018
|
+
try {
|
|
5019
|
+
await this.fs.mkdir(dir, { recursive: true });
|
|
5020
|
+
await this.fs.rename(path, hidden);
|
|
5021
|
+
return hidden;
|
|
5022
|
+
} catch {
|
|
5023
|
+
await this.tidy(dir);
|
|
5024
|
+
return void 0;
|
|
5025
|
+
}
|
|
5026
|
+
});
|
|
5027
|
+
}
|
|
5028
|
+
closed(path) {
|
|
5029
|
+
if (this.waiting.delete(path)) void this.serial(() => this.drop(path));
|
|
5030
|
+
}
|
|
5031
|
+
async drop(hidden) {
|
|
5032
|
+
if (this.busy(hidden)) {
|
|
5033
|
+
this.waiting.add(hidden);
|
|
5034
|
+
return;
|
|
5035
|
+
}
|
|
5036
|
+
await this.fs.rm(hidden).catch(() => void 0);
|
|
5037
|
+
await this.tidy(parentOf3(hidden));
|
|
5038
|
+
}
|
|
5039
|
+
async tidy(dir) {
|
|
5040
|
+
const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
|
|
5041
|
+
if (left.length === 0) await this.fs.rm(dir, { recursive: true }).catch(() => void 0);
|
|
5042
|
+
}
|
|
5043
|
+
sweep(root) {
|
|
5044
|
+
const query = this.locks?.query?.bind(this.locks);
|
|
5045
|
+
if (!query) return Promise.resolve();
|
|
5046
|
+
return this.serial(async () => {
|
|
5047
|
+
const dir = dirOf(root);
|
|
5048
|
+
const names = await this.fs.readdir(dir).catch(() => void 0);
|
|
5049
|
+
if (!names) return;
|
|
5050
|
+
const held = new Set(((await query()).held ?? []).map((lock) => lock.name));
|
|
5051
|
+
for (const name2 of names) {
|
|
5052
|
+
const owner = name2.slice(0, name2.lastIndexOf("."));
|
|
5053
|
+
if (owner === this.instance || held.has(lockName(owner))) continue;
|
|
5054
|
+
await this.fs.rm(`${dir}/${name2}`, { recursive: true }).catch(() => void 0);
|
|
5055
|
+
}
|
|
5056
|
+
await this.tidy(dir);
|
|
5057
|
+
});
|
|
5058
|
+
}
|
|
5059
|
+
};
|
|
5060
|
+
|
|
4852
5061
|
// src/fs/watch.ts
|
|
4853
5062
|
var WATCH_BATCH_LIMIT = 1e3;
|
|
4854
5063
|
var under2 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
|
|
4855
|
-
var
|
|
5064
|
+
var parentOf4 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
4856
5065
|
function touches(watcher, changed) {
|
|
4857
5066
|
return watcher.paths.some(
|
|
4858
|
-
(watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) :
|
|
5067
|
+
(watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) : parentOf4(changed) === watched)
|
|
4859
5068
|
);
|
|
4860
5069
|
}
|
|
4861
5070
|
var FsWatchers = class {
|
|
@@ -5237,7 +5446,7 @@ function parseSize(text) {
|
|
|
5237
5446
|
if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
|
|
5238
5447
|
return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
|
|
5239
5448
|
}
|
|
5240
|
-
var
|
|
5449
|
+
var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
5241
5450
|
var HeldSet = class extends Set {
|
|
5242
5451
|
idle;
|
|
5243
5452
|
constructor(idle) {
|
|
@@ -5250,18 +5459,47 @@ var HeldSet = class extends Set {
|
|
|
5250
5459
|
}
|
|
5251
5460
|
delete(holds) {
|
|
5252
5461
|
const removed = super.delete(holds);
|
|
5253
|
-
for (const path of holds.keys()) this.idle(path);
|
|
5462
|
+
for (const path of /* @__PURE__ */ new Set([...holds.keys(), ...holds.opens.keys()])) this.idle(path);
|
|
5463
|
+
for (const kept of holds.unlinked.values()) kept.release();
|
|
5464
|
+
holds.unlinked.clear();
|
|
5254
5465
|
return removed;
|
|
5255
5466
|
}
|
|
5256
5467
|
};
|
|
5257
5468
|
function heldUnder(held, target) {
|
|
5258
|
-
for (const paths of held) for (const path of paths.keys()) if (
|
|
5469
|
+
for (const paths of held) for (const path of paths.keys()) if (within4(path, target)) return true;
|
|
5259
5470
|
return false;
|
|
5260
5471
|
}
|
|
5261
5472
|
var HeldPaths = class extends Map {
|
|
5262
5473
|
revoked = /* @__PURE__ */ new Set();
|
|
5474
|
+
opens = /* @__PURE__ */ new Map();
|
|
5475
|
+
unlinked = /* @__PURE__ */ new Map();
|
|
5476
|
+
aliases = /* @__PURE__ */ new Map();
|
|
5477
|
+
owning;
|
|
5263
5478
|
onIdle;
|
|
5264
|
-
|
|
5479
|
+
keyOf(path) {
|
|
5480
|
+
return this.aliases.get(path) ?? path;
|
|
5481
|
+
}
|
|
5482
|
+
keptAt(path) {
|
|
5483
|
+
return this.unlinked.get(this.keyOf(path));
|
|
5484
|
+
}
|
|
5485
|
+
holding(path) {
|
|
5486
|
+
return this.opens.has(this.keyOf(path));
|
|
5487
|
+
}
|
|
5488
|
+
hold(path, on, open = false, real) {
|
|
5489
|
+
if (open) {
|
|
5490
|
+
const key = real ?? this.keyOf(path);
|
|
5491
|
+
if (key !== path) this.aliases.set(path, key);
|
|
5492
|
+
const count2 = (this.opens.get(key) ?? 0) + (on ? 1 : -1);
|
|
5493
|
+
if (count2 > 0) this.opens.set(key, count2);
|
|
5494
|
+
else {
|
|
5495
|
+
this.opens.delete(key);
|
|
5496
|
+
this.unlinked.get(key)?.release();
|
|
5497
|
+
this.unlinked.delete(key);
|
|
5498
|
+
for (const [alias, target] of this.aliases) if (target === key) this.aliases.delete(alias);
|
|
5499
|
+
this.onIdle?.(key);
|
|
5500
|
+
}
|
|
5501
|
+
return;
|
|
5502
|
+
}
|
|
5265
5503
|
const count = (this.get(path) ?? 0) + (on ? 1 : -1);
|
|
5266
5504
|
if (count > 0) this.set(path, count);
|
|
5267
5505
|
else {
|
|
@@ -5270,23 +5508,40 @@ var HeldPaths = class extends Map {
|
|
|
5270
5508
|
this.onIdle?.(path);
|
|
5271
5509
|
}
|
|
5272
5510
|
}
|
|
5511
|
+
async own(path, op) {
|
|
5512
|
+
this.owning = path;
|
|
5513
|
+
try {
|
|
5514
|
+
return await op();
|
|
5515
|
+
} finally {
|
|
5516
|
+
this.owning = void 0;
|
|
5517
|
+
}
|
|
5518
|
+
}
|
|
5519
|
+
owns(path) {
|
|
5520
|
+
return this.owning === path;
|
|
5521
|
+
}
|
|
5273
5522
|
revoke(prefix) {
|
|
5274
|
-
for (const path of this.keys()) if (
|
|
5523
|
+
for (const path of this.keys()) if (within4(path, prefix)) this.revoked.add(path);
|
|
5275
5524
|
}
|
|
5276
5525
|
renamed(from, to) {
|
|
5277
5526
|
for (const [path, count] of [...this]) {
|
|
5278
|
-
if (!
|
|
5527
|
+
if (!within4(path, from)) continue;
|
|
5279
5528
|
const moved2 = to + path.slice(from.length);
|
|
5280
5529
|
this.delete(path);
|
|
5281
5530
|
this.set(moved2, (this.get(moved2) ?? 0) + count);
|
|
5282
5531
|
if (this.revoked.delete(path)) this.revoked.add(moved2);
|
|
5283
5532
|
}
|
|
5533
|
+
for (const [path, count] of [...this.opens]) {
|
|
5534
|
+
if (!within4(path, from)) continue;
|
|
5535
|
+
const moved2 = to + path.slice(from.length);
|
|
5536
|
+
this.opens.delete(path);
|
|
5537
|
+
this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count);
|
|
5538
|
+
}
|
|
5284
5539
|
}
|
|
5285
5540
|
isRevoked(path) {
|
|
5286
5541
|
return this.revoked.has(path);
|
|
5287
5542
|
}
|
|
5288
5543
|
};
|
|
5289
|
-
var
|
|
5544
|
+
var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
5290
5545
|
var readonly = (mount) => mount.caps.readonly === true || mount.options.ro !== void 0;
|
|
5291
5546
|
function statOf(attr, path, mount) {
|
|
5292
5547
|
const mtime = new Date(attr.mtime);
|
|
@@ -5375,7 +5630,7 @@ var MountTable = class {
|
|
|
5375
5630
|
locate(path) {
|
|
5376
5631
|
let best;
|
|
5377
5632
|
for (const mount of this.mounts.values()) {
|
|
5378
|
-
if (
|
|
5633
|
+
if (within4(path, mount.target) && (!best || mount.target.length > best.target.length))
|
|
5379
5634
|
best = mount;
|
|
5380
5635
|
}
|
|
5381
5636
|
if (!best) return void 0;
|
|
@@ -5514,7 +5769,7 @@ var MountTable = class {
|
|
|
5514
5769
|
async commitUnder(prefix) {
|
|
5515
5770
|
const at = normalizePath(prefix);
|
|
5516
5771
|
await Promise.all(
|
|
5517
|
-
[...this.sessions.keys()].filter((p) =>
|
|
5772
|
+
[...this.sessions.keys()].filter((p) => within4(p, at)).map((p) => this.commit(p))
|
|
5518
5773
|
);
|
|
5519
5774
|
}
|
|
5520
5775
|
async commitOverlapping(real, offset, length2) {
|
|
@@ -5548,10 +5803,10 @@ var MountTable = class {
|
|
|
5548
5803
|
this.deps.changed?.(...full);
|
|
5549
5804
|
}
|
|
5550
5805
|
forget(mount, path) {
|
|
5551
|
-
for (const key of [...mount.attrs.keys()]) if (
|
|
5806
|
+
for (const key of [...mount.attrs.keys()]) if (within4(key, path)) mount.attrs.delete(key);
|
|
5552
5807
|
mount.lists.delete(path);
|
|
5553
|
-
mount.lists.delete(
|
|
5554
|
-
for (const key of [...mount.lists.keys()]) if (
|
|
5808
|
+
mount.lists.delete(parentOf5(path));
|
|
5809
|
+
for (const key of [...mount.lists.keys()]) if (within4(key, path)) mount.lists.delete(key);
|
|
5555
5810
|
}
|
|
5556
5811
|
async getattr(mount, path, rel2) {
|
|
5557
5812
|
if (mount.conn.failure) throw mount.conn.failure;
|
|
@@ -5616,7 +5871,7 @@ var MountFs = class {
|
|
|
5616
5871
|
if (attr.kind !== "symlink") return real;
|
|
5617
5872
|
if (hops >= MAX_LINKS4) throw errnoError("ELOOP", path);
|
|
5618
5873
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
5619
|
-
return this.follow(this.base.resolvePath(
|
|
5874
|
+
return this.follow(this.base.resolvePath(parentOf5(real), target), hops + 1, create);
|
|
5620
5875
|
}
|
|
5621
5876
|
async open(mount, rel2, write) {
|
|
5622
5877
|
return await mount.conn.call({
|
|
@@ -6130,6 +6385,8 @@ async function ranged(fs, path, req) {
|
|
|
6130
6385
|
else await ops.truncate(path, req.length ?? 0);
|
|
6131
6386
|
return done;
|
|
6132
6387
|
}
|
|
6388
|
+
var UNLINKED_OPS = /* @__PURE__ */ new Set(["read", "pread", "pwrite", "truncate"]);
|
|
6389
|
+
var OWN_OPS = /* @__PURE__ */ new Set(["rm", "unlink", "rmdir", "rename"]);
|
|
6133
6390
|
async function dispatchSyncFs(req) {
|
|
6134
6391
|
const entry = resolveSyncFsToken(req.token);
|
|
6135
6392
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
@@ -6138,11 +6395,22 @@ async function dispatchSyncFs(req) {
|
|
|
6138
6395
|
const path = fs.resolvePath(cwd, req.path);
|
|
6139
6396
|
if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
|
|
6140
6397
|
if (req.op === "hold") {
|
|
6141
|
-
|
|
6398
|
+
const [on, open] = [((req.mode ?? 0) & 1) === 1, ((req.mode ?? 0) & 2) === 2];
|
|
6399
|
+
const real = on && open ? await fs.realpath?.(path, true).catch(() => path) : void 0;
|
|
6400
|
+
entry.hold?.(path, on, open, real);
|
|
6142
6401
|
return done;
|
|
6143
6402
|
}
|
|
6144
6403
|
if (entry.revoked?.(path)) return { ok: false, errno: "EIO", message: `${path} was unmounted` };
|
|
6145
|
-
|
|
6404
|
+
if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
|
|
6405
|
+
const serve = async () => {
|
|
6406
|
+
const kept = entry.unlinked?.(path);
|
|
6407
|
+
return run(entry, kept && !await fs.exists(path) ? kept.hidden : path, req);
|
|
6408
|
+
};
|
|
6409
|
+
return await (entry.gate ? entry.gate(path, serve) : serve());
|
|
6410
|
+
}
|
|
6411
|
+
if (!entry.own || !OWN_OPS.has(req.op)) return await run(entry, path, req);
|
|
6412
|
+
const target = req.op === "rename" ? fs.resolvePath(cwd, req.arg2 ?? "") : path;
|
|
6413
|
+
return await entry.own(target, () => run(entry, path, req));
|
|
6146
6414
|
} catch (err) {
|
|
6147
6415
|
return toErrno(err);
|
|
6148
6416
|
}
|
|
@@ -7530,7 +7798,7 @@ function isMissing(err) {
|
|
|
7530
7798
|
if (err?.code === "ENOENT") return true;
|
|
7531
7799
|
return err instanceof Error && err.message.startsWith("ENOENT");
|
|
7532
7800
|
}
|
|
7533
|
-
function
|
|
7801
|
+
function within5(path, root) {
|
|
7534
7802
|
return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
|
|
7535
7803
|
}
|
|
7536
7804
|
var VfsNode = class {
|
|
@@ -7735,7 +8003,7 @@ var VfsNodes = class {
|
|
|
7735
8003
|
return run2;
|
|
7736
8004
|
}
|
|
7737
8005
|
async entryKey(path) {
|
|
7738
|
-
const parent =
|
|
8006
|
+
const parent = parentOf6(path);
|
|
7739
8007
|
const dir = await this.real(parent, true, () => realDir(this.fs, parent));
|
|
7740
8008
|
return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
|
|
7741
8009
|
}
|
|
@@ -7783,12 +8051,12 @@ var VfsNodes = class {
|
|
|
7783
8051
|
return node;
|
|
7784
8052
|
}
|
|
7785
8053
|
holds(prefix) {
|
|
7786
|
-
for (const path of this.byPath.keys()) if (
|
|
8054
|
+
for (const path of this.byPath.keys()) if (within5(path, prefix)) return true;
|
|
7787
8055
|
return false;
|
|
7788
8056
|
}
|
|
7789
8057
|
writes(prefix) {
|
|
7790
8058
|
for (const [path, node] of this.byPath)
|
|
7791
|
-
if (node.writers > 0 &&
|
|
8059
|
+
if (node.writers > 0 && within5(path, prefix)) return true;
|
|
7792
8060
|
return false;
|
|
7793
8061
|
}
|
|
7794
8062
|
closed(node, writable = false) {
|
|
@@ -7802,24 +8070,24 @@ var VfsNodes = class {
|
|
|
7802
8070
|
this.onClosed?.(node.path);
|
|
7803
8071
|
}
|
|
7804
8072
|
async flush(path) {
|
|
7805
|
-
for (const [p, node] of this.byPath) if (
|
|
8073
|
+
for (const [p, node] of this.byPath) if (within5(p, path)) await node.serial(() => node.flush());
|
|
7806
8074
|
}
|
|
7807
8075
|
async unlinking(path) {
|
|
7808
8076
|
for (const [held, node] of [...this.byPath]) {
|
|
7809
|
-
if (!
|
|
8077
|
+
if (!within5(held, path)) continue;
|
|
7810
8078
|
await node.serial(async () => void await Promise.all([node.keep(), node.limit()]));
|
|
7811
8079
|
}
|
|
7812
8080
|
}
|
|
7813
8081
|
revoke(prefix) {
|
|
7814
8082
|
for (const [path, node] of this.byPath) {
|
|
7815
|
-
if (!
|
|
8083
|
+
if (!within5(path, prefix)) continue;
|
|
7816
8084
|
node.revoke();
|
|
7817
8085
|
this.byPath.delete(path);
|
|
7818
8086
|
}
|
|
7819
8087
|
}
|
|
7820
8088
|
unlinked(path) {
|
|
7821
8089
|
for (const [held, node] of [...this.byPath]) {
|
|
7822
|
-
if (!
|
|
8090
|
+
if (!within5(held, path)) continue;
|
|
7823
8091
|
node.orphaned = true;
|
|
7824
8092
|
this.byPath.delete(held);
|
|
7825
8093
|
}
|
|
@@ -7828,8 +8096,8 @@ var VfsNodes = class {
|
|
|
7828
8096
|
if (from === to) return;
|
|
7829
8097
|
const moved2 = [];
|
|
7830
8098
|
for (const [p, node] of this.byPath) {
|
|
7831
|
-
if (
|
|
7832
|
-
else if (
|
|
8099
|
+
if (within5(p, from)) moved2.push(node);
|
|
8100
|
+
else if (within5(p, to)) {
|
|
7833
8101
|
node.orphaned = true;
|
|
7834
8102
|
this.byPath.delete(p);
|
|
7835
8103
|
}
|
|
@@ -7852,7 +8120,7 @@ async function present(fs, path, follow = false) {
|
|
|
7852
8120
|
}
|
|
7853
8121
|
}
|
|
7854
8122
|
var MAX_LINKS5 = 40;
|
|
7855
|
-
function
|
|
8123
|
+
function parentOf6(path) {
|
|
7856
8124
|
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
7857
8125
|
}
|
|
7858
8126
|
async function linkOf(fs, path) {
|
|
@@ -7865,7 +8133,7 @@ async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
|
7865
8133
|
const part = queue.shift();
|
|
7866
8134
|
if (part === ".") continue;
|
|
7867
8135
|
if (part === "..") {
|
|
7868
|
-
at =
|
|
8136
|
+
at = parentOf6(at);
|
|
7869
8137
|
continue;
|
|
7870
8138
|
}
|
|
7871
8139
|
const next = at === "/" ? `/${part}` : `${at}/${part}`;
|
|
@@ -7889,7 +8157,7 @@ function joinName(dir, name2) {
|
|
|
7889
8157
|
}
|
|
7890
8158
|
async function realTarget(fs, path) {
|
|
7891
8159
|
const budget = { hops: 0 };
|
|
7892
|
-
let dir = await realDir(fs,
|
|
8160
|
+
let dir = await realDir(fs, parentOf6(path), budget);
|
|
7893
8161
|
let name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
7894
8162
|
for (; ; ) {
|
|
7895
8163
|
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
@@ -7897,13 +8165,13 @@ async function realTarget(fs, path) {
|
|
|
7897
8165
|
if (budget.hops >= MAX_LINKS5) throw fsError("ELOOP", path);
|
|
7898
8166
|
budget.hops++;
|
|
7899
8167
|
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
7900
|
-
dir = await realDir(fs,
|
|
8168
|
+
dir = await realDir(fs, parentOf6(target), budget);
|
|
7901
8169
|
name2 = target.slice(target.lastIndexOf("/") + 1);
|
|
7902
8170
|
}
|
|
7903
8171
|
return joinName(dir, name2);
|
|
7904
8172
|
}
|
|
7905
8173
|
async function targetDir(fs, path) {
|
|
7906
|
-
return
|
|
8174
|
+
return parentOf6(await realTarget(fs, path).catch(() => path));
|
|
7907
8175
|
}
|
|
7908
8176
|
async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
7909
8177
|
const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
|
|
@@ -8292,7 +8560,7 @@ var WasmProcess = class {
|
|
|
8292
8560
|
const fd = this.fds.install(heldFile(), 3);
|
|
8293
8561
|
try {
|
|
8294
8562
|
const nodes = this.nodes;
|
|
8295
|
-
const path = req.orphan ? req.path : req.exclusive ? await nodes.entryKey(req.path) : await nodes.targetKey(req.path);
|
|
8563
|
+
const path = req.orphan ? req.path : req.exclusive ? await nodes.entryKey(req.path) : req.create ? await nodes.targetKey(req.path) : await this.keptOr(await nodes.targetKey(req.path));
|
|
8296
8564
|
const open = { ...req, path };
|
|
8297
8565
|
if (req.exclusive) {
|
|
8298
8566
|
await nodes.createExclusive(path);
|
|
@@ -8448,6 +8716,11 @@ var WasmProcess = class {
|
|
|
8448
8716
|
return { ok: true, kind: "json", json: await file.stat() };
|
|
8449
8717
|
}
|
|
8450
8718
|
}
|
|
8719
|
+
keptPath;
|
|
8720
|
+
async keptOr(path) {
|
|
8721
|
+
const kept = this.keptPath?.(path);
|
|
8722
|
+
return kept !== void 0 && !await present(this.options.fs, path, true) ? kept : path;
|
|
8723
|
+
}
|
|
8451
8724
|
async promote(req) {
|
|
8452
8725
|
if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
|
|
8453
8726
|
if (req.share !== void 0) {
|
|
@@ -8455,7 +8728,7 @@ var WasmProcess = class {
|
|
|
8455
8728
|
return;
|
|
8456
8729
|
}
|
|
8457
8730
|
if (req.path === void 0) throw new KernelError("EINVAL");
|
|
8458
|
-
const path = req.orphan ? req.path : await this.nodes.targetKey(req.path);
|
|
8731
|
+
const path = req.orphan ? req.path : await this.keptOr(await this.nodes.targetKey(req.path));
|
|
8459
8732
|
if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
|
|
8460
8733
|
const file = vfsFile(
|
|
8461
8734
|
this.options.fs,
|
|
@@ -8814,6 +9087,19 @@ function inherited(opts) {
|
|
|
8814
9087
|
...opts.umask !== void 0 ? { umask: opts.umask } : {}
|
|
8815
9088
|
};
|
|
8816
9089
|
}
|
|
9090
|
+
function holdingToken(opts, holds) {
|
|
9091
|
+
return mintSyncFsToken({
|
|
9092
|
+
fs: opts.fs,
|
|
9093
|
+
cwd: opts.cwd,
|
|
9094
|
+
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
9095
|
+
hold: (path, on, open, real) => holds.hold(path, on, open, real),
|
|
9096
|
+
unlinked: (path) => holds.keptAt(path),
|
|
9097
|
+
gate: (path, op) => opts.unlinkGate && holds.holding(path) ? opts.unlinkGate.run(holds.keyOf(path), op) : op(),
|
|
9098
|
+
own: (path, op) => holds.own(path, op),
|
|
9099
|
+
revoked: (path) => holds.isRevoked(path),
|
|
9100
|
+
renamed: (from, to) => holds.renamed(from, to)
|
|
9101
|
+
});
|
|
9102
|
+
}
|
|
8817
9103
|
function spawnWasmProcess(opts) {
|
|
8818
9104
|
const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
|
|
8819
9105
|
const header2 = new Int32Array(sab, 0, SAB_HEADER_I32);
|
|
@@ -8858,14 +9144,8 @@ function spawnWasmProcess(opts) {
|
|
|
8858
9144
|
});
|
|
8859
9145
|
const holds = new HeldPaths();
|
|
8860
9146
|
opts.held?.add(holds);
|
|
8861
|
-
const token =
|
|
8862
|
-
|
|
8863
|
-
cwd: opts.cwd,
|
|
8864
|
-
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
8865
|
-
hold: (path, on) => holds.hold(path, on),
|
|
8866
|
-
revoked: (path) => holds.isRevoked(path),
|
|
8867
|
-
renamed: (from, to) => holds.renamed(from, to)
|
|
8868
|
-
});
|
|
9147
|
+
const token = holdingToken(opts, holds);
|
|
9148
|
+
process.keptPath = (path) => holds.keptAt(path)?.hidden;
|
|
8869
9149
|
const worker = opts.createWorker();
|
|
8870
9150
|
const dying = new Dying();
|
|
8871
9151
|
const dispatch2 = (req) => dying.serving(() => isWasmSyscall(req) ? process.syscall(req) : dispatchSyncFs(req));
|
|
@@ -10383,6 +10663,7 @@ var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
|
|
|
10383
10663
|
var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
10384
10664
|
var WASM_MAGIC = [0, 97, 115, 109];
|
|
10385
10665
|
var SHEBANG_MAX = 256;
|
|
10666
|
+
var MAX_INTERPRETERS = 4;
|
|
10386
10667
|
var NOT_FOUND = 127;
|
|
10387
10668
|
var DECIDE_MS = 100;
|
|
10388
10669
|
var INIT_PID = 1;
|
|
@@ -10434,6 +10715,17 @@ function targetOf(command) {
|
|
|
10434
10715
|
...command.preopenRoot ? { preopenRoot: true } : {}
|
|
10435
10716
|
};
|
|
10436
10717
|
}
|
|
10718
|
+
function withScriptEnv(target, command) {
|
|
10719
|
+
if (!command) return target;
|
|
10720
|
+
return {
|
|
10721
|
+
...target,
|
|
10722
|
+
env: { ...target.env, ...command.env },
|
|
10723
|
+
unset: [
|
|
10724
|
+
...(target.unset ?? []).filter((key) => !Object.hasOwn(command.env ?? {}, key)),
|
|
10725
|
+
...command.unset ?? []
|
|
10726
|
+
]
|
|
10727
|
+
};
|
|
10728
|
+
}
|
|
10437
10729
|
function childHandle(handle) {
|
|
10438
10730
|
return {
|
|
10439
10731
|
pid: handle.pid,
|
|
@@ -10458,6 +10750,7 @@ var Launcher = class {
|
|
|
10458
10750
|
modulesDir;
|
|
10459
10751
|
env;
|
|
10460
10752
|
catalog;
|
|
10753
|
+
binfmts;
|
|
10461
10754
|
compiled = /* @__PURE__ */ new Map();
|
|
10462
10755
|
processes = /* @__PURE__ */ new Map();
|
|
10463
10756
|
zombies = /* @__PURE__ */ new Set();
|
|
@@ -10492,6 +10785,7 @@ var Launcher = class {
|
|
|
10492
10785
|
deciding = /* @__PURE__ */ new Map();
|
|
10493
10786
|
identities = { asked: 0, timedOut: 0 };
|
|
10494
10787
|
nodes;
|
|
10788
|
+
unlinked;
|
|
10495
10789
|
held = new HeldSet((path) => this.released(path));
|
|
10496
10790
|
processMounts;
|
|
10497
10791
|
booting = new AbortController();
|
|
@@ -10536,9 +10830,18 @@ var Launcher = class {
|
|
|
10536
10830
|
const fs = withCommandDirs(this.base, async () => new Set((await this.commands()).keys()));
|
|
10537
10831
|
this.nodes = new VfsNodes(fs, (path) => this.released(path));
|
|
10538
10832
|
this.openFiles.add(this.nodes);
|
|
10539
|
-
this.
|
|
10833
|
+
this.unlinked = new UnlinkedKeeper(
|
|
10834
|
+
fs,
|
|
10835
|
+
this.held,
|
|
10836
|
+
(path) => this.mountRoot(path),
|
|
10837
|
+
webLocks(),
|
|
10838
|
+
(path) => [...this.openFiles].some((nodes) => nodes.holds(path))
|
|
10839
|
+
);
|
|
10840
|
+
this.fs = keepingOpen(this.unlinked.wrap(), this.nodes);
|
|
10841
|
+
void this.unlinked.sweep("/");
|
|
10540
10842
|
this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
|
|
10541
10843
|
this.catalog = void 0;
|
|
10844
|
+
this.binfmts = void 0;
|
|
10542
10845
|
});
|
|
10543
10846
|
}
|
|
10544
10847
|
setRoutes(table) {
|
|
@@ -10548,6 +10851,10 @@ var Launcher = class {
|
|
|
10548
10851
|
this.catalog ??= this.roots().then((roots) => scanCommands(this.base, roots));
|
|
10549
10852
|
return this.catalog;
|
|
10550
10853
|
}
|
|
10854
|
+
interpreters() {
|
|
10855
|
+
this.binfmts ??= this.roots().then((roots) => scanBinfmts(this.base, roots));
|
|
10856
|
+
return this.binfmts;
|
|
10857
|
+
}
|
|
10551
10858
|
async roots() {
|
|
10552
10859
|
return [this.modulesDir, ...await pnpmGlobalRoots(this.base, this.pnpmHome)];
|
|
10553
10860
|
}
|
|
@@ -10593,30 +10900,28 @@ var Launcher = class {
|
|
|
10593
10900
|
const command = (await this.commands()).get(name2);
|
|
10594
10901
|
return command?.script ? command : void 0;
|
|
10595
10902
|
}
|
|
10596
|
-
async interpreted(file, argv, cwd) {
|
|
10903
|
+
async interpreted(file, argv, cwd, depth = 0) {
|
|
10597
10904
|
const command = await this.scriptCommand(file);
|
|
10598
10905
|
const script = command?.script ?? this.fs.resolvePath(cwd, file);
|
|
10599
|
-
const
|
|
10906
|
+
const head = await this.head(script);
|
|
10907
|
+
if (!head) return void 0;
|
|
10908
|
+
const binfmt = isWasm(head) ? void 0 : binfmtOf(await this.interpreters(), script);
|
|
10909
|
+
const words = shebang(head) ?? (binfmt ? [binfmt] : void 0);
|
|
10600
10910
|
if (!words) return void 0;
|
|
10601
10911
|
if (baseName(words[0]) === "env") words.shift();
|
|
10602
10912
|
const [interp, ...rest] = words;
|
|
10603
|
-
const found = interp ? await this.resolve(interp, interp, cwd) : void 0;
|
|
10604
|
-
if (!found) return void 0;
|
|
10605
|
-
const target = command ? {
|
|
10606
|
-
...found,
|
|
10607
|
-
env: { ...found.env, ...command.env },
|
|
10608
|
-
unset: [...found.unset ?? [], ...command.unset ?? []]
|
|
10609
|
-
} : found;
|
|
10610
10913
|
const arg = rest.join(" ");
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
|
|
10614
|
-
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
}
|
|
10914
|
+
const passed = [...arg ? [arg] : [], command ? script : file, ...argv.slice(1)];
|
|
10915
|
+
const found = interp ? await this.resolve(interp, interp, cwd) : void 0;
|
|
10916
|
+
if (!found) {
|
|
10917
|
+
if (!interp || depth >= MAX_INTERPRETERS || !await this.scriptCommand(interp)) {
|
|
10918
|
+
return void 0;
|
|
10919
|
+
}
|
|
10920
|
+
const inner = await this.interpreted(interp, [interp, ...passed], cwd, depth + 1);
|
|
10921
|
+
return inner && { ...inner, target: withScriptEnv(inner.target, command) };
|
|
10922
|
+
}
|
|
10923
|
+
const target = withScriptEnv(found, command);
|
|
10924
|
+
return { target, args: [...target.prefix ?? [], ...passed] };
|
|
10620
10925
|
}
|
|
10621
10926
|
async plan(file, argv, cwd) {
|
|
10622
10927
|
const direct = await this.resolve(file, argv[0] ?? file, cwd);
|
|
@@ -10624,8 +10929,10 @@ var Launcher = class {
|
|
|
10624
10929
|
return this.interpreted(file, argv, cwd);
|
|
10625
10930
|
}
|
|
10626
10931
|
async unrunnable(file, cwd) {
|
|
10627
|
-
const
|
|
10628
|
-
|
|
10932
|
+
const path = this.fs.resolvePath(cwd, file);
|
|
10933
|
+
const head = await this.base.readFileBuffer(path).catch(() => null);
|
|
10934
|
+
if (!head || head[0] === 35 && head[1] === 33) return "ENOENT";
|
|
10935
|
+
return binfmtOf(await this.interpreters(), path) ? "ENOENT" : "ENOEXEC";
|
|
10629
10936
|
}
|
|
10630
10937
|
module(path) {
|
|
10631
10938
|
return this.base.stat(path).then((st) => {
|
|
@@ -10741,6 +11048,7 @@ var Launcher = class {
|
|
|
10741
11048
|
openFiles: this.openFiles,
|
|
10742
11049
|
nodes: this.nodes,
|
|
10743
11050
|
held: this.held,
|
|
11051
|
+
unlinkGate: this.unlinked.gate,
|
|
10744
11052
|
statfs: (path) => this.mounts.statfs(path),
|
|
10745
11053
|
mounts: () => this.mounts.list(),
|
|
10746
11054
|
mount: (call, signal) => this.processMount(pid, call, signal),
|
|
@@ -10898,6 +11206,7 @@ var Launcher = class {
|
|
|
10898
11206
|
}
|
|
10899
11207
|
released(path) {
|
|
10900
11208
|
const at = normalizePath(path);
|
|
11209
|
+
this.unlinked.closed(at);
|
|
10901
11210
|
const open = [...this.openFiles].some((nodes) => nodes.writes(at)) || heldUnder(this.held, at);
|
|
10902
11211
|
if (!open) void this.mounts.commit(at).catch(() => void 0);
|
|
10903
11212
|
}
|
|
@@ -10993,10 +11302,23 @@ var Launcher = class {
|
|
|
10993
11302
|
}
|
|
10994
11303
|
};
|
|
10995
11304
|
}
|
|
10996
|
-
mount(spec) {
|
|
10997
|
-
|
|
10998
|
-
|
|
10999
|
-
|
|
11305
|
+
async mount(spec) {
|
|
11306
|
+
const fsa = spec.type === "fsa" && !spec.source.startsWith("fsa:");
|
|
11307
|
+
const entry = await this.mounts.mount(
|
|
11308
|
+
fsa ? { ...spec, source: `fsa:${crypto.randomUUID()}` } : spec,
|
|
11309
|
+
this.fs
|
|
11310
|
+
);
|
|
11311
|
+
void this.unlinked.sweep(entry.target);
|
|
11312
|
+
return entry;
|
|
11313
|
+
}
|
|
11314
|
+
mountRoot(path) {
|
|
11315
|
+
let root = "/";
|
|
11316
|
+
for (const { target } of this.mounts.list()) {
|
|
11317
|
+
if (target.length > root.length && (path === target || path.startsWith(`${target}/`))) {
|
|
11318
|
+
root = target;
|
|
11319
|
+
}
|
|
11320
|
+
}
|
|
11321
|
+
return root;
|
|
11000
11322
|
}
|
|
11001
11323
|
async insert(target, handle, source) {
|
|
11002
11324
|
const at = normalizePath(target);
|
|
@@ -11010,6 +11332,7 @@ var Launcher = class {
|
|
|
11010
11332
|
await this.media.put(slot.id, handle);
|
|
11011
11333
|
slot.medium.insert(handle);
|
|
11012
11334
|
slot.served.invalidate(true);
|
|
11335
|
+
void this.unlinked.sweep(at);
|
|
11013
11336
|
}
|
|
11014
11337
|
umount(target, detach = false) {
|
|
11015
11338
|
const at = normalizePath(target);
|
|
@@ -11085,6 +11408,7 @@ var Launcher = class {
|
|
|
11085
11408
|
}
|
|
11086
11409
|
async starting(argv, dir) {
|
|
11087
11410
|
this.catalog = void 0;
|
|
11411
|
+
this.binfmts = void 0;
|
|
11088
11412
|
const cwd = this.fs.resolvePath("/", dir ?? "/");
|
|
11089
11413
|
const [file = ""] = argv;
|
|
11090
11414
|
await this.base.mkdir(cwd, { recursive: true });
|