@ai-ecoverse/slicc-kernel 1.4.0 → 1.5.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.
- package/README.md +15 -2
- package/dist/index.js +158 -11
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +135 -30
- package/dist/kernel-worker.js.map +3 -3
- package/package.json +4 -4
package/dist/kernel-worker.js
CHANGED
|
@@ -59,34 +59,62 @@ function settle(transaction, value) {
|
|
|
59
59
|
}
|
|
60
60
|
var IndexedDbMeta = class {
|
|
61
61
|
db;
|
|
62
|
+
cache = /* @__PURE__ */ new Map();
|
|
63
|
+
channel;
|
|
64
|
+
generation = 0;
|
|
62
65
|
constructor(db) {
|
|
63
66
|
this.db = db;
|
|
64
67
|
db.onversionchange = () => db.close();
|
|
68
|
+
this.channel = new BroadcastChannel(`slicc-kernel-meta:${db.name}`);
|
|
69
|
+
this.channel.onmessage = ({ data }) => this.drop(data);
|
|
70
|
+
this.channel.unref?.();
|
|
71
|
+
}
|
|
72
|
+
drop({ paths = [], trees = [] }) {
|
|
73
|
+
this.generation++;
|
|
74
|
+
for (const path of paths) this.cache.delete(path);
|
|
75
|
+
for (const key of [...this.cache.keys()]) {
|
|
76
|
+
if (trees.some((tree) => under(key, tree))) this.cache.delete(key);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
changed(invalidation) {
|
|
80
|
+
this.drop(invalidation);
|
|
81
|
+
this.channel.postMessage(invalidation);
|
|
65
82
|
}
|
|
66
83
|
transaction(mode) {
|
|
67
84
|
const transaction = this.db.transaction(META_STORE, mode);
|
|
68
85
|
return [transaction, transaction.objectStore(META_STORE)];
|
|
69
86
|
}
|
|
70
87
|
async get(paths) {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
88
|
+
const missing3 = paths.filter((path) => !this.cache.has(path));
|
|
89
|
+
if (missing3.length > 0) {
|
|
90
|
+
const generation = this.generation;
|
|
91
|
+
const [transaction, store] = this.transaction("readonly");
|
|
92
|
+
const found = /* @__PURE__ */ new Map();
|
|
93
|
+
for (const path of missing3) {
|
|
94
|
+
const request2 = store.get(path);
|
|
95
|
+
request2.onsuccess = () => void found.set(path, request2.result ?? null);
|
|
96
|
+
}
|
|
97
|
+
await settle(transaction, () => void 0);
|
|
98
|
+
if (generation === this.generation)
|
|
99
|
+
for (const [path, entry] of found) this.cache.set(path, entry);
|
|
100
|
+
return paths.map(
|
|
101
|
+
(path) => (found.has(path) ? found.get(path) : this.cache.get(path)) ?? void 0
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return paths.map((path) => this.cache.get(path) ?? void 0);
|
|
80
105
|
}
|
|
81
106
|
async update(path, change) {
|
|
82
107
|
const [transaction, store] = this.transaction("readwrite");
|
|
83
108
|
const request2 = store.get(path);
|
|
109
|
+
let next;
|
|
84
110
|
request2.onsuccess = () => {
|
|
85
|
-
|
|
111
|
+
next = change(request2.result);
|
|
86
112
|
if (next) store.put(next);
|
|
87
113
|
else store.delete(path);
|
|
88
114
|
};
|
|
89
|
-
|
|
115
|
+
await settle(transaction, () => void 0);
|
|
116
|
+
this.changed({ paths: [path] });
|
|
117
|
+
this.cache.set(path, next ?? null);
|
|
90
118
|
}
|
|
91
119
|
async move(from, to, root) {
|
|
92
120
|
const [transaction, store] = this.transaction("readwrite");
|
|
@@ -102,7 +130,8 @@ var IndexedDbMeta = class {
|
|
|
102
130
|
}
|
|
103
131
|
store.put(moved(root(source.result), to));
|
|
104
132
|
};
|
|
105
|
-
|
|
133
|
+
await settle(transaction, () => void 0);
|
|
134
|
+
this.changed({ trees: [from, to] });
|
|
106
135
|
}
|
|
107
136
|
async remove(paths) {
|
|
108
137
|
const [transaction, store] = this.transaction("readwrite");
|
|
@@ -110,7 +139,8 @@ var IndexedDbMeta = class {
|
|
|
110
139
|
store.delete(path);
|
|
111
140
|
store.delete(descendants(path));
|
|
112
141
|
}
|
|
113
|
-
|
|
142
|
+
await settle(transaction, () => void 0);
|
|
143
|
+
this.changed({ trees: paths });
|
|
114
144
|
}
|
|
115
145
|
async links(dir) {
|
|
116
146
|
const [transaction, store] = this.transaction("readonly");
|
|
@@ -183,6 +213,7 @@ var S_IFLNK = 40960;
|
|
|
183
213
|
var PERM_MASK = 4095;
|
|
184
214
|
var READ_ATTEMPTS = 3;
|
|
185
215
|
var MAX_LINKS = 40;
|
|
216
|
+
var CHECK_MS = 1e3;
|
|
186
217
|
var EXECUTABLE = /\/node_modules\/(?:@[^/]+\/)?[^/]+\/bin\//;
|
|
187
218
|
var ERRNO_BY_DOM_ERROR = {
|
|
188
219
|
NotFoundError: "ENOENT",
|
|
@@ -222,37 +253,100 @@ function isHandle(child) {
|
|
|
222
253
|
var OpfsFs = class {
|
|
223
254
|
root;
|
|
224
255
|
meta;
|
|
225
|
-
|
|
256
|
+
dirs = /* @__PURE__ */ new Map();
|
|
257
|
+
checked = /* @__PURE__ */ new WeakMap();
|
|
258
|
+
channel;
|
|
259
|
+
constructor(root, meta = new MemoryMeta(), channel) {
|
|
226
260
|
this.root = root;
|
|
227
261
|
this.meta = meta;
|
|
262
|
+
this.channel = channel === void 0 ? void 0 : new BroadcastChannel(channel);
|
|
263
|
+
if (this.channel) {
|
|
264
|
+
this.channel.onmessage = ({ data }) => {
|
|
265
|
+
for (const path of data) this.forget(path);
|
|
266
|
+
};
|
|
267
|
+
this.channel.unref?.();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
changed(...paths) {
|
|
271
|
+
for (const path of paths) this.forget(path);
|
|
272
|
+
this.channel?.postMessage(paths);
|
|
228
273
|
}
|
|
229
274
|
resolvePath(base, path) {
|
|
230
275
|
return resolvePath(base, path);
|
|
231
276
|
}
|
|
277
|
+
async walk(parts, from, start) {
|
|
278
|
+
let dir = start;
|
|
279
|
+
for (let at = from; at < parts.length; at++) {
|
|
280
|
+
dir = await dir.getDirectoryHandle(parts[at]);
|
|
281
|
+
this.dirs.set(`/${parts.slice(0, at + 1).join("/")}`, dir);
|
|
282
|
+
this.checked.set(dir, performance.now());
|
|
283
|
+
}
|
|
284
|
+
return dir;
|
|
285
|
+
}
|
|
232
286
|
async dir(path) {
|
|
233
|
-
|
|
287
|
+
const parts = split(path);
|
|
288
|
+
let from = parts.length;
|
|
289
|
+
let cached;
|
|
290
|
+
for (; from > 0 && !cached; from--)
|
|
291
|
+
cached = this.dirs.get(`/${parts.slice(0, from).join("/")}`);
|
|
292
|
+
if (cached) from++;
|
|
293
|
+
const at = `/${parts.slice(0, from).join("/")}`;
|
|
294
|
+
if (cached && !await this.current(at, cached)) {
|
|
295
|
+
this.forget(at);
|
|
296
|
+
cached = void 0;
|
|
297
|
+
from = 0;
|
|
298
|
+
}
|
|
234
299
|
try {
|
|
235
|
-
|
|
300
|
+
return await this.walk(parts, from, cached ?? this.root);
|
|
236
301
|
} catch (err) {
|
|
237
302
|
throw translate(err, path);
|
|
238
303
|
}
|
|
239
|
-
|
|
304
|
+
}
|
|
305
|
+
async current(path, dir) {
|
|
306
|
+
if (performance.now() - (this.checked.get(dir) ?? -CHECK_MS) < CHECK_MS) return true;
|
|
307
|
+
const where = await this.root.resolve(dir).catch(() => null);
|
|
308
|
+
if (where === null || `/${where.join("/")}` !== path) return false;
|
|
309
|
+
this.checked.set(dir, performance.now());
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
forget(path) {
|
|
313
|
+
for (const key of [...this.dirs.keys()]) if (within(key, path)) this.dirs.delete(key);
|
|
314
|
+
}
|
|
315
|
+
async inDir(path, op) {
|
|
316
|
+
const dir = await this.dir(path);
|
|
317
|
+
try {
|
|
318
|
+
return await op(dir);
|
|
319
|
+
} catch (err) {
|
|
320
|
+
if (err?.name !== "NotFoundError" || await this.current(path, dir))
|
|
321
|
+
throw err;
|
|
322
|
+
this.forget(path);
|
|
323
|
+
return op(await this.dir(path));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async child(parent, name2, kind) {
|
|
327
|
+
return kind === "file" ? parent.getFileHandle(name2) : parent.getDirectoryHandle(name2);
|
|
240
328
|
}
|
|
241
329
|
async parent(path) {
|
|
242
330
|
const parts = split(path);
|
|
243
331
|
const name2 = parts.pop();
|
|
244
332
|
if (name2 === void 0) throw fsError("EBUSY", path);
|
|
245
|
-
return [
|
|
333
|
+
return [`/${parts.join("/")}`, name2];
|
|
246
334
|
}
|
|
247
335
|
async handle(path) {
|
|
248
336
|
if (split(path).length === 0) return this.root;
|
|
249
337
|
const [dir, name2] = await this.parent(path);
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
338
|
+
const known = this.dirs.has(path);
|
|
339
|
+
return this.inDir(dir, async (parent) => {
|
|
340
|
+
const [first, second] = known ? ["directory", "file"] : ["file", "directory"];
|
|
341
|
+
try {
|
|
342
|
+
return await this.child(parent, name2, first);
|
|
343
|
+
} catch (err) {
|
|
344
|
+
if (err?.name !== "TypeMismatchError") throw err;
|
|
345
|
+
}
|
|
346
|
+
return this.child(parent, name2, second);
|
|
347
|
+
}).catch((err) => {
|
|
348
|
+
throw translate(err, path);
|
|
349
|
+
});
|
|
256
350
|
}
|
|
257
351
|
real(path) {
|
|
258
352
|
return this.handle(path).then(
|
|
@@ -354,24 +448,25 @@ var OpfsFs = class {
|
|
|
354
448
|
}
|
|
355
449
|
async writeTarget(path) {
|
|
356
450
|
try {
|
|
357
|
-
return (await this.locate(path, true)).path;
|
|
451
|
+
return [(await this.locate(path, true)).path, true];
|
|
358
452
|
} catch (err) {
|
|
359
453
|
if (!missing(err)) throw err;
|
|
360
454
|
}
|
|
361
455
|
const [entry] = await this.meta.get([path]);
|
|
362
456
|
const wanted = entry?.link === void 0 ? path : resolvePath(parentOf2(path), entry.link);
|
|
363
|
-
return resolvePath((await this.locate(parentOf2(wanted), true)).path, baseOf(wanted));
|
|
457
|
+
return [resolvePath((await this.locate(parentOf2(wanted), true)).path, baseOf(wanted)), false];
|
|
364
458
|
}
|
|
365
459
|
async writeFile(path, content) {
|
|
366
|
-
const target = await this.writeTarget(path);
|
|
460
|
+
const [target, existed] = await this.writeTarget(path);
|
|
367
461
|
const [dir, name2] = await this.parent(target);
|
|
368
462
|
let file;
|
|
369
463
|
try {
|
|
370
|
-
file = await dir.getFileHandle(name2, { create: true });
|
|
464
|
+
file = await this.inDir(dir, (parent) => parent.getFileHandle(name2, { create: true }));
|
|
371
465
|
} catch (err) {
|
|
372
466
|
const code = err?.name === "TypeMismatchError" ? "EISDIR" : null;
|
|
373
467
|
throw code ? fsError(code, path) : translate(err, path);
|
|
374
468
|
}
|
|
469
|
+
if (!existed && content.length === 0) return;
|
|
375
470
|
const writable = await file.createWritable();
|
|
376
471
|
await writable.write(content);
|
|
377
472
|
await writable.close();
|
|
@@ -434,16 +529,21 @@ var OpfsFs = class {
|
|
|
434
529
|
if (found.handle) {
|
|
435
530
|
const [dir, name2] = await this.parent(found.path);
|
|
436
531
|
try {
|
|
437
|
-
await
|
|
532
|
+
await this.inDir(
|
|
533
|
+
dir,
|
|
534
|
+
(parent) => parent.removeEntry(name2, { recursive: options.recursive === true })
|
|
535
|
+
);
|
|
438
536
|
} catch (err) {
|
|
439
537
|
throw translate(err, path);
|
|
440
538
|
}
|
|
539
|
+
this.changed(found.path);
|
|
441
540
|
}
|
|
442
541
|
await this.meta.remove([found.path]);
|
|
443
542
|
}
|
|
444
543
|
async rename(from, to) {
|
|
445
544
|
if (from === to) return;
|
|
446
545
|
const source = await this.locate(from, false);
|
|
546
|
+
this.forget(source.path);
|
|
447
547
|
const parent = await this.locate(parentOf2(to), true);
|
|
448
548
|
const destination = resolvePath(parent.path, baseOf(to));
|
|
449
549
|
const sourceStat = await this.describe(source);
|
|
@@ -476,6 +576,7 @@ var OpfsFs = class {
|
|
|
476
576
|
const name2 = baseOf(destination);
|
|
477
577
|
const swap = targetStat?.isDirectory === true;
|
|
478
578
|
const staging = swap ? `.${name2}.${crypto.randomUUID()}` : name2;
|
|
579
|
+
this.forget(destination);
|
|
479
580
|
const timed = await this.explicitTimes(source.path);
|
|
480
581
|
const moved2 = await this.place(source.handle, dir, staging);
|
|
481
582
|
if (swap) await this.rm(destination, { recursive: true });
|
|
@@ -489,6 +590,7 @@ var OpfsFs = class {
|
|
|
489
590
|
copied ||= !placed;
|
|
490
591
|
}
|
|
491
592
|
if (copied) await this.restamp(destination, timed);
|
|
593
|
+
this.changed(source.path, destination);
|
|
492
594
|
}
|
|
493
595
|
async modified(path) {
|
|
494
596
|
const handle = await this.handle(path);
|
|
@@ -5798,6 +5900,9 @@ function act(session, req) {
|
|
|
5798
5900
|
else if (req.action === "signal") session.signal(signalNumber(req.signal));
|
|
5799
5901
|
else session.close();
|
|
5800
5902
|
}
|
|
5903
|
+
function dirsChannel(name2) {
|
|
5904
|
+
return name2 === false ? void 0 : `slicc-kernel-dirs:${name2}`;
|
|
5905
|
+
}
|
|
5801
5906
|
function serveKernel(port, deps) {
|
|
5802
5907
|
let launcher;
|
|
5803
5908
|
let remote;
|
|
@@ -5824,7 +5929,7 @@ function serveKernel(port, deps) {
|
|
|
5824
5929
|
const name2 = req.metadata ?? META_DB;
|
|
5825
5930
|
const meta = name2 === false ? void 0 : await deps.metadata?.(name2);
|
|
5826
5931
|
remote = req.transport ? new RemoteTransport(port, req.transport) : void 0;
|
|
5827
|
-
const fs = new OpfsFs(root, meta);
|
|
5932
|
+
const fs = new OpfsFs(root, meta, dirsChannel(name2));
|
|
5828
5933
|
await fs.reconcile();
|
|
5829
5934
|
const started = new Launcher({
|
|
5830
5935
|
fs,
|