@ai-ecoverse/slicc-kernel 1.39.1 → 1.39.3
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 +1 -1
- package/dist/kernel-worker.js +102 -26
- package/dist/kernel-worker.js.map +2 -2
- package/dist/node-process-worker.js +82 -14
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +291 -201
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +82 -14
- package/dist/process-worker.js.map +3 -3
- package/package.json +1 -1
package/dist/node.js
CHANGED
|
@@ -2606,6 +2606,210 @@ var OpfsFs = class {
|
|
|
2606
2606
|
}
|
|
2607
2607
|
};
|
|
2608
2608
|
|
|
2609
|
+
// src/fs/unlinked.ts
|
|
2610
|
+
var UNLINKED_DIR = ".slicc-unlinked";
|
|
2611
|
+
function pidOwner(proc) {
|
|
2612
|
+
return {
|
|
2613
|
+
id: `p${proc.pid}`,
|
|
2614
|
+
alive(id) {
|
|
2615
|
+
try {
|
|
2616
|
+
proc.kill(Number(id.slice(1)), 0);
|
|
2617
|
+
return true;
|
|
2618
|
+
} catch (err) {
|
|
2619
|
+
return err.code !== "ESRCH";
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
};
|
|
2623
|
+
}
|
|
2624
|
+
function webLocks() {
|
|
2625
|
+
return globalThis.navigator?.locks;
|
|
2626
|
+
}
|
|
2627
|
+
var within2 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
2628
|
+
var parentOf3 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
2629
|
+
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
2630
|
+
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
2631
|
+
var PathGate = class {
|
|
2632
|
+
tails = /* @__PURE__ */ new Map();
|
|
2633
|
+
async acquire(path) {
|
|
2634
|
+
const prev = this.tails.get(path) ?? Promise.resolve();
|
|
2635
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
2636
|
+
const tail = prev.then(() => promise);
|
|
2637
|
+
this.tails.set(path, tail);
|
|
2638
|
+
await prev;
|
|
2639
|
+
return () => {
|
|
2640
|
+
resolve();
|
|
2641
|
+
if (this.tails.get(path) === tail) this.tails.delete(path);
|
|
2642
|
+
};
|
|
2643
|
+
}
|
|
2644
|
+
async run(path, op) {
|
|
2645
|
+
const release = await this.acquire(path);
|
|
2646
|
+
try {
|
|
2647
|
+
return await op();
|
|
2648
|
+
} finally {
|
|
2649
|
+
release();
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
};
|
|
2653
|
+
var UnlinkedKeeper = class {
|
|
2654
|
+
fs;
|
|
2655
|
+
holders;
|
|
2656
|
+
rootOf;
|
|
2657
|
+
locks;
|
|
2658
|
+
busy;
|
|
2659
|
+
waiting = /* @__PURE__ */ new Set();
|
|
2660
|
+
owner;
|
|
2661
|
+
instance;
|
|
2662
|
+
gate = new PathGate();
|
|
2663
|
+
queue = Promise.resolve();
|
|
2664
|
+
count = 0;
|
|
2665
|
+
constructor(fs, holders, rootOf, options = {}) {
|
|
2666
|
+
const { locks, owner } = options;
|
|
2667
|
+
this.fs = fs;
|
|
2668
|
+
this.holders = holders;
|
|
2669
|
+
this.rootOf = rootOf;
|
|
2670
|
+
this.locks = locks;
|
|
2671
|
+
this.busy = options.busy ?? (() => false);
|
|
2672
|
+
this.owner = owner;
|
|
2673
|
+
this.instance = owner ? `${owner.id}~${crypto.randomUUID()}` : crypto.randomUUID();
|
|
2674
|
+
void locks?.request(lockName(this.instance), () => new Promise(() => void 0));
|
|
2675
|
+
}
|
|
2676
|
+
serial(op) {
|
|
2677
|
+
const next = this.queue.then(op);
|
|
2678
|
+
this.queue = next.catch(() => void 0);
|
|
2679
|
+
return next;
|
|
2680
|
+
}
|
|
2681
|
+
wrap(fs = this.fs) {
|
|
2682
|
+
const keeper = this;
|
|
2683
|
+
const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
|
|
2684
|
+
return {
|
|
2685
|
+
...fs,
|
|
2686
|
+
async rm(path, options) {
|
|
2687
|
+
const target = fs.resolvePath("/", path);
|
|
2688
|
+
const pinned = await keeper.pin(target);
|
|
2689
|
+
if (pinned.moved.has(target)) pinned.commit();
|
|
2690
|
+
else await keeper.settle(pinned, () => fs.rm(path, options));
|
|
2691
|
+
},
|
|
2692
|
+
async rename(from, to) {
|
|
2693
|
+
const target = fs.resolvePath("/", to);
|
|
2694
|
+
if (target === fs.resolvePath("/", from)) return fs.rename(from, to);
|
|
2695
|
+
const pinned = await keeper.pin(target);
|
|
2696
|
+
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
2697
|
+
},
|
|
2698
|
+
readdir: async (path) => visible(await fs.readdir(path)),
|
|
2699
|
+
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR)
|
|
2700
|
+
};
|
|
2701
|
+
}
|
|
2702
|
+
async settle(pinned, op) {
|
|
2703
|
+
try {
|
|
2704
|
+
await op();
|
|
2705
|
+
} catch (err) {
|
|
2706
|
+
await pinned.rollback();
|
|
2707
|
+
throw err;
|
|
2708
|
+
}
|
|
2709
|
+
pinned.commit();
|
|
2710
|
+
}
|
|
2711
|
+
async pin(target) {
|
|
2712
|
+
const held = /* @__PURE__ */ new Map();
|
|
2713
|
+
for (const holder of this.holders) {
|
|
2714
|
+
if (holder.owns(target)) continue;
|
|
2715
|
+
for (const path of holder.held()) {
|
|
2716
|
+
if (!within2(path, target) || holder.isKept(path)) continue;
|
|
2717
|
+
held.set(path, [...held.get(path) ?? [], holder]);
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
const paths = [...held.keys()].sort();
|
|
2721
|
+
const releases = [];
|
|
2722
|
+
for (const path of paths) releases.push(await this.gate.acquire(path));
|
|
2723
|
+
const hidden = /* @__PURE__ */ new Map();
|
|
2724
|
+
for (const path of paths) {
|
|
2725
|
+
const at = await this.hide(path);
|
|
2726
|
+
if (at !== void 0) hidden.set(path, at);
|
|
2727
|
+
}
|
|
2728
|
+
const done4 = () => {
|
|
2729
|
+
for (const release of releases) release();
|
|
2730
|
+
};
|
|
2731
|
+
const files = /* @__PURE__ */ new Map();
|
|
2732
|
+
for (const [path, at] of hidden) {
|
|
2733
|
+
const holders = held.get(path);
|
|
2734
|
+
const file = this.keptFile(at, { refs: holders.length + 1 });
|
|
2735
|
+
files.set(path, file);
|
|
2736
|
+
for (const holder of holders) holder.keep(path, file);
|
|
2737
|
+
}
|
|
2738
|
+
return {
|
|
2739
|
+
moved: new Set(hidden.keys()),
|
|
2740
|
+
commit: () => {
|
|
2741
|
+
for (const file of files.values()) file.release();
|
|
2742
|
+
done4();
|
|
2743
|
+
},
|
|
2744
|
+
rollback: async () => {
|
|
2745
|
+
for (const [path, file] of files) {
|
|
2746
|
+
for (const holder of held.get(path)) holder.unkeep(path, file);
|
|
2747
|
+
await this.serial(async () => {
|
|
2748
|
+
await Promise.allSettled([this.fs.rename(file.hidden, path)]);
|
|
2749
|
+
await this.tidy(parentOf3(file.hidden));
|
|
2750
|
+
});
|
|
2751
|
+
}
|
|
2752
|
+
done4();
|
|
2753
|
+
}
|
|
2754
|
+
};
|
|
2755
|
+
}
|
|
2756
|
+
keptFile(hidden, entry) {
|
|
2757
|
+
return {
|
|
2758
|
+
hidden,
|
|
2759
|
+
release: () => {
|
|
2760
|
+
if (--entry.refs === 0) void this.serial(() => this.drop(hidden));
|
|
2761
|
+
}
|
|
2762
|
+
};
|
|
2763
|
+
}
|
|
2764
|
+
hide(path) {
|
|
2765
|
+
return this.serial(async () => {
|
|
2766
|
+
const dir = dirOf(this.rootOf(path));
|
|
2767
|
+
const hidden = `${dir}/${this.instance}.${++this.count}`;
|
|
2768
|
+
try {
|
|
2769
|
+
await this.fs.mkdir(dir, { recursive: true });
|
|
2770
|
+
await this.fs.rename(path, hidden);
|
|
2771
|
+
return hidden;
|
|
2772
|
+
} catch {
|
|
2773
|
+
await this.tidy(dir);
|
|
2774
|
+
return void 0;
|
|
2775
|
+
}
|
|
2776
|
+
});
|
|
2777
|
+
}
|
|
2778
|
+
closed(path) {
|
|
2779
|
+
if (this.waiting.delete(path)) void this.serial(() => this.drop(path));
|
|
2780
|
+
}
|
|
2781
|
+
async drop(hidden) {
|
|
2782
|
+
if (this.busy(hidden)) {
|
|
2783
|
+
this.waiting.add(hidden);
|
|
2784
|
+
return;
|
|
2785
|
+
}
|
|
2786
|
+
await this.fs.rm(hidden).catch(() => void 0);
|
|
2787
|
+
await this.tidy(parentOf3(hidden));
|
|
2788
|
+
}
|
|
2789
|
+
async tidy(dir) {
|
|
2790
|
+
const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
|
|
2791
|
+
if (left.length === 0) await this.fs.rm(dir, { recursive: true }).catch(() => void 0);
|
|
2792
|
+
}
|
|
2793
|
+
sweep(root) {
|
|
2794
|
+
const query = this.locks?.query?.bind(this.locks);
|
|
2795
|
+
const owner = this.owner;
|
|
2796
|
+
if (!query && !owner) return Promise.resolve();
|
|
2797
|
+
return this.serial(async () => {
|
|
2798
|
+
const dir = dirOf(root);
|
|
2799
|
+
const names = await this.fs.readdir(dir).catch(() => void 0);
|
|
2800
|
+
if (!names) return;
|
|
2801
|
+
const held = query && new Set(((await query()).held ?? []).map((lock) => lock.name));
|
|
2802
|
+
for (const name2 of names) {
|
|
2803
|
+
const id = name2.slice(0, name2.lastIndexOf("."));
|
|
2804
|
+
const at = id.indexOf("~");
|
|
2805
|
+
const dead = id !== this.instance && (at < 0 ? held !== void 0 && !held.has(lockName(id)) : owner?.alive(id.slice(0, at)) === false);
|
|
2806
|
+
if (dead) await this.fs.rm(`${dir}/${name2}`, { recursive: true }).catch(() => void 0);
|
|
2807
|
+
}
|
|
2808
|
+
await this.tidy(dir);
|
|
2809
|
+
});
|
|
2810
|
+
}
|
|
2811
|
+
};
|
|
2812
|
+
|
|
2609
2813
|
// src/kernel/net/http1.ts
|
|
2610
2814
|
var HttpError = class extends Error {
|
|
2611
2815
|
status;
|
|
@@ -3674,186 +3878,6 @@ function withCommandDirs(fs, names) {
|
|
|
3674
3878
|
};
|
|
3675
3879
|
}
|
|
3676
3880
|
|
|
3677
|
-
// src/fs/unlinked.ts
|
|
3678
|
-
var UNLINKED_DIR = ".slicc-unlinked";
|
|
3679
|
-
function webLocks() {
|
|
3680
|
-
return globalThis.navigator?.locks;
|
|
3681
|
-
}
|
|
3682
|
-
var within2 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
3683
|
-
var parentOf3 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
3684
|
-
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
3685
|
-
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
3686
|
-
var PathGate = class {
|
|
3687
|
-
tails = /* @__PURE__ */ new Map();
|
|
3688
|
-
async acquire(path) {
|
|
3689
|
-
const prev = this.tails.get(path) ?? Promise.resolve();
|
|
3690
|
-
const { promise, resolve } = Promise.withResolvers();
|
|
3691
|
-
const tail = prev.then(() => promise);
|
|
3692
|
-
this.tails.set(path, tail);
|
|
3693
|
-
await prev;
|
|
3694
|
-
return () => {
|
|
3695
|
-
resolve();
|
|
3696
|
-
if (this.tails.get(path) === tail) this.tails.delete(path);
|
|
3697
|
-
};
|
|
3698
|
-
}
|
|
3699
|
-
async run(path, op) {
|
|
3700
|
-
const release = await this.acquire(path);
|
|
3701
|
-
try {
|
|
3702
|
-
return await op();
|
|
3703
|
-
} finally {
|
|
3704
|
-
release();
|
|
3705
|
-
}
|
|
3706
|
-
}
|
|
3707
|
-
};
|
|
3708
|
-
var UnlinkedKeeper = class {
|
|
3709
|
-
fs;
|
|
3710
|
-
holders;
|
|
3711
|
-
rootOf;
|
|
3712
|
-
locks;
|
|
3713
|
-
busy;
|
|
3714
|
-
waiting = /* @__PURE__ */ new Set();
|
|
3715
|
-
instance = crypto.randomUUID();
|
|
3716
|
-
gate = new PathGate();
|
|
3717
|
-
queue = Promise.resolve();
|
|
3718
|
-
count = 0;
|
|
3719
|
-
constructor(fs, holders, rootOf, locks, busy = () => false) {
|
|
3720
|
-
this.fs = fs;
|
|
3721
|
-
this.holders = holders;
|
|
3722
|
-
this.rootOf = rootOf;
|
|
3723
|
-
this.locks = locks;
|
|
3724
|
-
this.busy = busy;
|
|
3725
|
-
void locks?.request(lockName(this.instance), () => new Promise(() => void 0));
|
|
3726
|
-
}
|
|
3727
|
-
serial(op) {
|
|
3728
|
-
const next = this.queue.then(op);
|
|
3729
|
-
this.queue = next.catch(() => void 0);
|
|
3730
|
-
return next;
|
|
3731
|
-
}
|
|
3732
|
-
wrap() {
|
|
3733
|
-
const fs = this.fs;
|
|
3734
|
-
const keeper = this;
|
|
3735
|
-
const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
|
|
3736
|
-
return {
|
|
3737
|
-
...fs,
|
|
3738
|
-
async rm(path, options) {
|
|
3739
|
-
const target = fs.resolvePath("/", path);
|
|
3740
|
-
const pinned = await keeper.pin(target);
|
|
3741
|
-
if (pinned.moved.has(target)) pinned.commit();
|
|
3742
|
-
else await keeper.settle(pinned, () => fs.rm(path, options));
|
|
3743
|
-
},
|
|
3744
|
-
async rename(from, to) {
|
|
3745
|
-
const pinned = await keeper.pin(fs.resolvePath("/", to));
|
|
3746
|
-
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
3747
|
-
},
|
|
3748
|
-
readdir: async (path) => visible(await fs.readdir(path)),
|
|
3749
|
-
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR)
|
|
3750
|
-
};
|
|
3751
|
-
}
|
|
3752
|
-
async settle(pinned, op) {
|
|
3753
|
-
try {
|
|
3754
|
-
await op();
|
|
3755
|
-
} catch (err) {
|
|
3756
|
-
await pinned.rollback();
|
|
3757
|
-
throw err;
|
|
3758
|
-
}
|
|
3759
|
-
pinned.commit();
|
|
3760
|
-
}
|
|
3761
|
-
async pin(target) {
|
|
3762
|
-
const held = /* @__PURE__ */ new Map();
|
|
3763
|
-
for (const holder of this.holders) {
|
|
3764
|
-
if (holder.owns(target)) continue;
|
|
3765
|
-
for (const path of holder.opens.keys()) {
|
|
3766
|
-
if (!within2(path, target) || holder.unlinked.has(path)) continue;
|
|
3767
|
-
held.set(path, [...held.get(path) ?? [], holder]);
|
|
3768
|
-
}
|
|
3769
|
-
}
|
|
3770
|
-
const paths = [...held.keys()].sort();
|
|
3771
|
-
const releases = [];
|
|
3772
|
-
for (const path of paths) releases.push(await this.gate.acquire(path));
|
|
3773
|
-
const hidden = /* @__PURE__ */ new Map();
|
|
3774
|
-
for (const path of paths) {
|
|
3775
|
-
const at = await this.hide(path);
|
|
3776
|
-
if (at !== void 0) hidden.set(path, at);
|
|
3777
|
-
}
|
|
3778
|
-
const done4 = () => {
|
|
3779
|
-
for (const release of releases) release();
|
|
3780
|
-
};
|
|
3781
|
-
return {
|
|
3782
|
-
moved: new Set(hidden.keys()),
|
|
3783
|
-
commit: () => {
|
|
3784
|
-
for (const [path, at] of hidden) {
|
|
3785
|
-
const holders = held.get(path);
|
|
3786
|
-
const file = this.keptFile(at, { refs: holders.length });
|
|
3787
|
-
for (const holder of holders) holder.unlinked.set(path, file);
|
|
3788
|
-
}
|
|
3789
|
-
done4();
|
|
3790
|
-
},
|
|
3791
|
-
rollback: async () => {
|
|
3792
|
-
for (const [path, at] of hidden) {
|
|
3793
|
-
await this.serial(async () => {
|
|
3794
|
-
await Promise.allSettled([this.fs.rename(at, path)]);
|
|
3795
|
-
await this.tidy(parentOf3(at));
|
|
3796
|
-
});
|
|
3797
|
-
}
|
|
3798
|
-
done4();
|
|
3799
|
-
}
|
|
3800
|
-
};
|
|
3801
|
-
}
|
|
3802
|
-
keptFile(hidden, entry) {
|
|
3803
|
-
return {
|
|
3804
|
-
hidden,
|
|
3805
|
-
release: () => {
|
|
3806
|
-
if (--entry.refs === 0) void this.serial(() => this.drop(hidden));
|
|
3807
|
-
}
|
|
3808
|
-
};
|
|
3809
|
-
}
|
|
3810
|
-
hide(path) {
|
|
3811
|
-
return this.serial(async () => {
|
|
3812
|
-
const dir = dirOf(this.rootOf(path));
|
|
3813
|
-
const hidden = `${dir}/${this.instance}.${++this.count}`;
|
|
3814
|
-
try {
|
|
3815
|
-
await this.fs.mkdir(dir, { recursive: true });
|
|
3816
|
-
await this.fs.rename(path, hidden);
|
|
3817
|
-
return hidden;
|
|
3818
|
-
} catch {
|
|
3819
|
-
await this.tidy(dir);
|
|
3820
|
-
return void 0;
|
|
3821
|
-
}
|
|
3822
|
-
});
|
|
3823
|
-
}
|
|
3824
|
-
closed(path) {
|
|
3825
|
-
if (this.waiting.delete(path)) void this.serial(() => this.drop(path));
|
|
3826
|
-
}
|
|
3827
|
-
async drop(hidden) {
|
|
3828
|
-
if (this.busy(hidden)) {
|
|
3829
|
-
this.waiting.add(hidden);
|
|
3830
|
-
return;
|
|
3831
|
-
}
|
|
3832
|
-
await this.fs.rm(hidden).catch(() => void 0);
|
|
3833
|
-
await this.tidy(parentOf3(hidden));
|
|
3834
|
-
}
|
|
3835
|
-
async tidy(dir) {
|
|
3836
|
-
const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
|
|
3837
|
-
if (left.length === 0) await this.fs.rm(dir, { recursive: true }).catch(() => void 0);
|
|
3838
|
-
}
|
|
3839
|
-
sweep(root) {
|
|
3840
|
-
const query = this.locks?.query?.bind(this.locks);
|
|
3841
|
-
if (!query) return Promise.resolve();
|
|
3842
|
-
return this.serial(async () => {
|
|
3843
|
-
const dir = dirOf(root);
|
|
3844
|
-
const names = await this.fs.readdir(dir).catch(() => void 0);
|
|
3845
|
-
if (!names) return;
|
|
3846
|
-
const held = new Set(((await query()).held ?? []).map((lock) => lock.name));
|
|
3847
|
-
for (const name2 of names) {
|
|
3848
|
-
const owner = name2.slice(0, name2.lastIndexOf("."));
|
|
3849
|
-
if (owner === this.instance || held.has(lockName(owner))) continue;
|
|
3850
|
-
await this.fs.rm(`${dir}/${name2}`, { recursive: true }).catch(() => void 0);
|
|
3851
|
-
}
|
|
3852
|
-
await this.tidy(dir);
|
|
3853
|
-
});
|
|
3854
|
-
}
|
|
3855
|
-
};
|
|
3856
|
-
|
|
3857
3881
|
// src/fs/watch.ts
|
|
3858
3882
|
var WATCH_BATCH_LIMIT = 1e3;
|
|
3859
3883
|
var under2 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
|
|
@@ -4050,6 +4074,7 @@ var ChildTable = class {
|
|
|
4050
4074
|
if ("fd" in slot) return this.parentFds.get(slot.fd).retain();
|
|
4051
4075
|
if ("input" in slot) return bytesSource(slot.input);
|
|
4052
4076
|
if ("device" in slot) return deviceFile(slot.device, slot.access);
|
|
4077
|
+
if ("dir" in slot) return heldFile({ dir: slot.dir });
|
|
4053
4078
|
if ("capture" in slot) {
|
|
4054
4079
|
const chunks2 = [];
|
|
4055
4080
|
captured.set(n, chunks2);
|
|
@@ -4281,6 +4306,18 @@ var HeldPaths = class extends Map {
|
|
|
4281
4306
|
holding(path) {
|
|
4282
4307
|
return this.opens.has(this.keyOf(path));
|
|
4283
4308
|
}
|
|
4309
|
+
held() {
|
|
4310
|
+
return this.opens.keys();
|
|
4311
|
+
}
|
|
4312
|
+
isKept(path) {
|
|
4313
|
+
return this.unlinked.has(path);
|
|
4314
|
+
}
|
|
4315
|
+
keep(path, file) {
|
|
4316
|
+
this.unlinked.set(path, file);
|
|
4317
|
+
}
|
|
4318
|
+
unkeep(path) {
|
|
4319
|
+
this.unlinked.delete(path);
|
|
4320
|
+
}
|
|
4284
4321
|
hold(path, on, open = false, real) {
|
|
4285
4322
|
if (open) {
|
|
4286
4323
|
const key = real ?? this.keyOf(path);
|
|
@@ -5148,6 +5185,9 @@ async function run(entry, path, req) {
|
|
|
5148
5185
|
case "symlink":
|
|
5149
5186
|
await fs.symlink(req.arg2 ?? "", path);
|
|
5150
5187
|
return done;
|
|
5188
|
+
case "create":
|
|
5189
|
+
await createExclusive(fs, path);
|
|
5190
|
+
return done;
|
|
5151
5191
|
case "readlink":
|
|
5152
5192
|
return json2(await fs.readlink(path));
|
|
5153
5193
|
case "chmod":
|
|
@@ -5167,6 +5207,15 @@ async function run(entry, path, req) {
|
|
|
5167
5207
|
return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
|
|
5168
5208
|
}
|
|
5169
5209
|
}
|
|
5210
|
+
async function createExclusive(fs, path) {
|
|
5211
|
+
if (fs.createExclusive) return fs.createExclusive(path);
|
|
5212
|
+
if (await fs.lstat(path).then(
|
|
5213
|
+
() => true,
|
|
5214
|
+
() => false
|
|
5215
|
+
))
|
|
5216
|
+
throw syncError("EEXIST", path);
|
|
5217
|
+
await fs.writeFile(path, new Uint8Array(0));
|
|
5218
|
+
}
|
|
5170
5219
|
async function ranged(fs, path, req) {
|
|
5171
5220
|
const ops = rangedOps(fs);
|
|
5172
5221
|
if (!ops) throw syncError("ENOSYS", path);
|
|
@@ -8126,6 +8175,8 @@ var VfsNode = class {
|
|
|
8126
8175
|
};
|
|
8127
8176
|
var VfsNodes = class {
|
|
8128
8177
|
byPath = /* @__PURE__ */ new Map();
|
|
8178
|
+
kept = /* @__PURE__ */ new Map();
|
|
8179
|
+
keepsOnDisk = false;
|
|
8129
8180
|
fs;
|
|
8130
8181
|
entries = /* @__PURE__ */ new Map();
|
|
8131
8182
|
onClosed;
|
|
@@ -8208,15 +8259,47 @@ var VfsNodes = class {
|
|
|
8208
8259
|
return;
|
|
8209
8260
|
}
|
|
8210
8261
|
if (this.byPath.get(node.path) === node) this.byPath.delete(node.path);
|
|
8262
|
+
this.kept.get(node)?.release();
|
|
8263
|
+
this.kept.delete(node);
|
|
8211
8264
|
this.onClosed?.(node.path);
|
|
8212
8265
|
}
|
|
8266
|
+
held() {
|
|
8267
|
+
return this.byPath.keys();
|
|
8268
|
+
}
|
|
8269
|
+
owns() {
|
|
8270
|
+
return false;
|
|
8271
|
+
}
|
|
8272
|
+
isKept() {
|
|
8273
|
+
return false;
|
|
8274
|
+
}
|
|
8275
|
+
keep(path, file) {
|
|
8276
|
+
const node = this.byPath.get(path);
|
|
8277
|
+
if (!node) {
|
|
8278
|
+
file.release();
|
|
8279
|
+
return;
|
|
8280
|
+
}
|
|
8281
|
+
this.byPath.delete(path);
|
|
8282
|
+
node.renamedTo(file.hidden);
|
|
8283
|
+
this.byPath.set(file.hidden, node);
|
|
8284
|
+
this.kept.set(node, file);
|
|
8285
|
+
}
|
|
8286
|
+
unkeep(path, file) {
|
|
8287
|
+
const node = this.byPath.get(file.hidden);
|
|
8288
|
+
if (!node) return;
|
|
8289
|
+
this.byPath.delete(file.hidden);
|
|
8290
|
+
node.renamedTo(path);
|
|
8291
|
+
this.byPath.set(path, node);
|
|
8292
|
+
this.kept.delete(node);
|
|
8293
|
+
}
|
|
8213
8294
|
async flush(path) {
|
|
8214
8295
|
for (const [p, node] of this.byPath) if (within5(p, path)) await node.serial(() => node.flush());
|
|
8215
8296
|
}
|
|
8216
8297
|
async unlinking(path) {
|
|
8217
8298
|
for (const [held, node] of [...this.byPath]) {
|
|
8218
8299
|
if (!within5(held, path)) continue;
|
|
8219
|
-
await node.serial(async () =>
|
|
8300
|
+
await node.serial(async () => {
|
|
8301
|
+
await Promise.all([this.keepsOnDisk || node.keep(), node.limit()]);
|
|
8302
|
+
});
|
|
8220
8303
|
}
|
|
8221
8304
|
}
|
|
8222
8305
|
revoke(prefix) {
|
|
@@ -8346,6 +8429,7 @@ function keepingOpen(fs, nodes) {
|
|
|
8346
8429
|
});
|
|
8347
8430
|
},
|
|
8348
8431
|
writeFile: (path, content) => nodes.onEntry(fs.resolvePath("/", path), () => fs.writeFile(path, content)),
|
|
8432
|
+
createExclusive: async (path) => nodes.createExclusive(await nodes.entryKey(fs.resolvePath("/", path))),
|
|
8349
8433
|
symlink: (target, path) => nodes.onEntry(fs.resolvePath("/", path), () => fs.symlink(target, path)),
|
|
8350
8434
|
mkdir: (path, options) => nodes.onEntry(fs.resolvePath("/", path), () => fs.mkdir(path, options))
|
|
8351
8435
|
};
|
|
@@ -9171,13 +9255,13 @@ var Dying = class {
|
|
|
9171
9255
|
timer;
|
|
9172
9256
|
busy = 0;
|
|
9173
9257
|
finish;
|
|
9174
|
-
start(code, sig, header3,
|
|
9258
|
+
start(code, sig, header3, process2, finish) {
|
|
9175
9259
|
if (this.ending) return;
|
|
9176
9260
|
this.ending = { code, sig };
|
|
9177
9261
|
this.finish = finish;
|
|
9178
9262
|
this.arm();
|
|
9179
9263
|
Atomics.store(header3, SAB_I_KILLED, code);
|
|
9180
|
-
|
|
9264
|
+
process2.die();
|
|
9181
9265
|
}
|
|
9182
9266
|
async serving(work) {
|
|
9183
9267
|
this.busy++;
|
|
@@ -9213,8 +9297,8 @@ function notifiers(header3) {
|
|
|
9213
9297
|
}
|
|
9214
9298
|
};
|
|
9215
9299
|
}
|
|
9216
|
-
function publishStops(
|
|
9217
|
-
|
|
9300
|
+
function publishStops(process2, header3) {
|
|
9301
|
+
process2.onState((state) => {
|
|
9218
9302
|
Atomics.store(header3, SAB_I_STOP, state === "stopped" ? 1 : 0);
|
|
9219
9303
|
Atomics.notify(header3, SAB_I_STOP);
|
|
9220
9304
|
});
|
|
@@ -9284,7 +9368,7 @@ function holdingToken(opts, holds) {
|
|
|
9284
9368
|
function spawnWasmProcess(opts) {
|
|
9285
9369
|
const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
|
|
9286
9370
|
const header3 = new Int32Array(sab, 0, SAB_HEADER_I32);
|
|
9287
|
-
const
|
|
9371
|
+
const process2 = new WasmProcess(opts.pid, opts.fds, {
|
|
9288
9372
|
...inherited(opts),
|
|
9289
9373
|
...opts.identity ? { identity: opts.identity } : {},
|
|
9290
9374
|
...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
|
|
@@ -9312,14 +9396,14 @@ function spawnWasmProcess(opts) {
|
|
|
9312
9396
|
raise: (sig) => signal(sig),
|
|
9313
9397
|
...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header3, SAB_I_SIGNALS) } : {}
|
|
9314
9398
|
});
|
|
9315
|
-
publishStops(
|
|
9399
|
+
publishStops(process2, header3);
|
|
9316
9400
|
const holds = new HeldPaths();
|
|
9317
9401
|
opts.held?.add(holds);
|
|
9318
9402
|
const token = holdingToken(opts, holds);
|
|
9319
|
-
|
|
9403
|
+
process2.keptPath = (path) => holds.keptAt(path)?.hidden;
|
|
9320
9404
|
const worker = opts.createWorker();
|
|
9321
9405
|
const dying = new Dying();
|
|
9322
|
-
const dispatch2 = (req) => dying.serving(() => isWasmSyscall(req) ?
|
|
9406
|
+
const dispatch2 = (req) => dying.serving(() => isWasmSyscall(req) ? process2.syscall(req) : dispatchSyncFs(req));
|
|
9323
9407
|
const responder = attachSyncSabResponder(worker, sab, token, { dispatch: dispatch2 });
|
|
9324
9408
|
const lane = opts.program.abi === "js" ? openLane(token, dispatch2) : void 0;
|
|
9325
9409
|
const { promise: exited, resolve: settle } = Promise.withResolvers();
|
|
@@ -9329,7 +9413,7 @@ function spawnWasmProcess(opts) {
|
|
|
9329
9413
|
if (done4) return;
|
|
9330
9414
|
done4 = true;
|
|
9331
9415
|
dying.clear();
|
|
9332
|
-
endedBy = sig ??
|
|
9416
|
+
endedBy = sig ?? process2.execTermsig;
|
|
9333
9417
|
worker.removeEventListener("message", onMessage);
|
|
9334
9418
|
worker.removeEventListener("error", onError);
|
|
9335
9419
|
for (const tid of [...threads.keys()]) endThread(tid);
|
|
@@ -9338,7 +9422,7 @@ function spawnWasmProcess(opts) {
|
|
|
9338
9422
|
revokeSyncFsToken(token);
|
|
9339
9423
|
opts.held?.delete(holds);
|
|
9340
9424
|
worker.terminate();
|
|
9341
|
-
void
|
|
9425
|
+
void process2.exit().then(
|
|
9342
9426
|
() => settle(code),
|
|
9343
9427
|
() => settle(code)
|
|
9344
9428
|
);
|
|
@@ -9406,14 +9490,14 @@ function spawnWasmProcess(opts) {
|
|
|
9406
9490
|
tw.postMessage(tinit);
|
|
9407
9491
|
};
|
|
9408
9492
|
worker.postMessage(
|
|
9409
|
-
processInit(opts, sab,
|
|
9493
|
+
processInit(opts, sab, process2.umask, lane?.lane),
|
|
9410
9494
|
transferOf(opts, lane?.lane)
|
|
9411
9495
|
);
|
|
9412
9496
|
const signal = (sig) => {
|
|
9413
|
-
if (
|
|
9497
|
+
if (process2.signal(sig) !== "terminate") return;
|
|
9414
9498
|
const code = sig === SIG.KILL ? 137 : 128 + sig;
|
|
9415
9499
|
if (opts.program.abi !== "wasi" || done4) finish(code, sig);
|
|
9416
|
-
else dying.start(code, sig, header3,
|
|
9500
|
+
else dying.start(code, sig, header3, process2, () => finish(code, sig));
|
|
9417
9501
|
};
|
|
9418
9502
|
const kill = (code = 137) => finish(code, code > 128 && code < 160 ? code - 128 : void 0);
|
|
9419
9503
|
return {
|
|
@@ -9422,10 +9506,10 @@ function spawnWasmProcess(opts) {
|
|
|
9422
9506
|
kill,
|
|
9423
9507
|
signal,
|
|
9424
9508
|
termsig: () => endedBy,
|
|
9425
|
-
onState: (listener) =>
|
|
9509
|
+
onState: (listener) => process2.onState(listener),
|
|
9426
9510
|
memory: () => Atomics.load(header3, SAB_I_MEMORY) * 65536,
|
|
9427
|
-
ignoredSignals: (fork) =>
|
|
9428
|
-
umask: () =>
|
|
9511
|
+
ignoredSignals: (fork) => process2.inheritable(fork),
|
|
9512
|
+
umask: () => process2.umask,
|
|
9429
9513
|
writesBack: () => opts.program.abi === "wasi" && !done4
|
|
9430
9514
|
};
|
|
9431
9515
|
}
|
|
@@ -11013,15 +11097,20 @@ var Launcher = class {
|
|
|
11013
11097
|
enableCdp(this.net, this.cdp);
|
|
11014
11098
|
const fs = withCommandDirs(this.base, async () => new Set((await this.commands()).keys()));
|
|
11015
11099
|
this.nodes = new VfsNodes(fs, (path) => this.released(path));
|
|
11100
|
+
this.nodes.keepsOnDisk = true;
|
|
11016
11101
|
this.openFiles.add(this.nodes);
|
|
11102
|
+
const holders = [this.held, [this.nodes]];
|
|
11017
11103
|
this.unlinked = new UnlinkedKeeper(
|
|
11018
11104
|
fs,
|
|
11019
|
-
|
|
11105
|
+
{ [Symbol.iterator]: () => holders.flatMap((set2) => [...set2]).values() },
|
|
11020
11106
|
(path) => this.mountRoot(path),
|
|
11021
|
-
|
|
11022
|
-
|
|
11107
|
+
{
|
|
11108
|
+
locks: webLocks(),
|
|
11109
|
+
busy: (path) => [...this.openFiles].some((nodes) => nodes.holds(path)),
|
|
11110
|
+
owner: options.unlinkOwner
|
|
11111
|
+
}
|
|
11023
11112
|
);
|
|
11024
|
-
this.fs =
|
|
11113
|
+
this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
|
|
11025
11114
|
void this.unlinked.sweep("/");
|
|
11026
11115
|
this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
|
|
11027
11116
|
this.catalog = void 0;
|
|
@@ -12718,6 +12807,7 @@ async function createNodeKernel(options = {}) {
|
|
|
12718
12807
|
const transport = options.network?.transport;
|
|
12719
12808
|
const launcher = new Launcher({
|
|
12720
12809
|
fs,
|
|
12810
|
+
unlinkOwner: pidOwner(process),
|
|
12721
12811
|
createWorker: () => nodeWorker(file, live, terminated),
|
|
12722
12812
|
createDriverWorker: () => nodeWorker(
|
|
12723
12813
|
options.driverWorker ?? new URL("./node-driver-worker.js", import.meta.url),
|