@ai-ecoverse/slicc-kernel 1.9.0 → 1.10.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/dist/node.js CHANGED
@@ -252,7 +252,7 @@ function serveTransport(peer, transport) {
252
252
  }
253
253
 
254
254
  // src/client/protocol.ts
255
- var PROTOCOL = [1, 0];
255
+ var PROTOCOL = [1, 1];
256
256
  var FS_METHODS = [
257
257
  "readFile",
258
258
  "writeFile",
@@ -345,6 +345,7 @@ function serveClient(port, host) {
345
345
  let state = "new";
346
346
  const terminals = /* @__PURE__ */ new Map();
347
347
  const groups = /* @__PURE__ */ new Set();
348
+ const watches = /* @__PURE__ */ new Map();
348
349
  let net;
349
350
  const ended = Promise.withResolvers();
350
351
  const send = (message2, transfer = []) => {
@@ -356,6 +357,8 @@ function serveClient(port, host) {
356
357
  send({ bye });
357
358
  state = "closed";
358
359
  net?.close();
360
+ for (const unwatch of watches.values()) unwatch();
361
+ watches.clear();
359
362
  for (const session of terminals.values()) session.close();
360
363
  terminals.clear();
361
364
  const pids = [...groups];
@@ -449,6 +452,19 @@ function serveClient(port, host) {
449
452
  throw new Error(`unknown file system call ${req.method}`);
450
453
  }
451
454
  return fsCall(l.fs, req.method, req.args);
455
+ case "watch": {
456
+ const unwatch = l.watchers.watch(
457
+ req.paths,
458
+ { recursive: req.recursive },
459
+ (change) => send({ watch: req.id, change })
460
+ );
461
+ watches.set(req.id, unwatch);
462
+ return { result: req.id };
463
+ }
464
+ case "unwatch":
465
+ watches.get(req.watch)?.();
466
+ watches.delete(req.watch);
467
+ return { result: true };
452
468
  case "detach":
453
469
  return { result: true };
454
470
  default:
@@ -1269,6 +1285,89 @@ function withCommandDirs(fs, names) {
1269
1285
  };
1270
1286
  }
1271
1287
 
1288
+ // src/fs/watch.ts
1289
+ var WATCH_BATCH_LIMIT = 1e3;
1290
+ var under2 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
1291
+ var parentOf3 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
1292
+ function touches(watcher, changed) {
1293
+ return watcher.paths.some(
1294
+ (watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) : parentOf3(changed) === watched)
1295
+ );
1296
+ }
1297
+ var FsWatchers = class {
1298
+ watchers = /* @__PURE__ */ new Set();
1299
+ watch(paths, options, notify) {
1300
+ const watcher = {
1301
+ paths: paths.map(normalizePath),
1302
+ recursive: options.recursive === true,
1303
+ notify,
1304
+ pending: /* @__PURE__ */ new Set(),
1305
+ scheduled: false
1306
+ };
1307
+ this.watchers.add(watcher);
1308
+ return () => {
1309
+ this.watchers.delete(watcher);
1310
+ };
1311
+ }
1312
+ changed(...paths) {
1313
+ for (const watcher of this.watchers) {
1314
+ for (const path of paths) {
1315
+ const changed = normalizePath(path);
1316
+ if (touches(watcher, changed)) watcher.pending.add(changed);
1317
+ }
1318
+ if (watcher.pending.size === 0 || watcher.scheduled) continue;
1319
+ watcher.scheduled = true;
1320
+ setTimeout(() => this.flush(watcher), 0);
1321
+ }
1322
+ }
1323
+ flush(watcher) {
1324
+ watcher.scheduled = false;
1325
+ const paths = [...watcher.pending];
1326
+ watcher.pending.clear();
1327
+ if (!this.watchers.has(watcher)) return;
1328
+ watcher.notify(paths.length > WATCH_BATCH_LIMIT ? { overflow: true } : { paths });
1329
+ }
1330
+ wrap(fs) {
1331
+ const after = (run2, changed) => async (...args) => {
1332
+ try {
1333
+ await run2(...args);
1334
+ } finally {
1335
+ this.changed(...changed(...args));
1336
+ }
1337
+ };
1338
+ const overrides = {
1339
+ writeFile: after(fs.writeFile.bind(fs), (path) => [path]),
1340
+ mkdir: async (path, options) => {
1341
+ const created = [];
1342
+ if (options?.recursive) {
1343
+ const parts = normalizePath(path).split("/").filter(Boolean);
1344
+ for (let at = 1; at < parts.length; at++) {
1345
+ const ancestor = `/${parts.slice(0, at).join("/")}`;
1346
+ if (created.length > 0 || !await fs.exists(ancestor)) created.push(ancestor);
1347
+ }
1348
+ }
1349
+ try {
1350
+ await fs.mkdir(path, options);
1351
+ } finally {
1352
+ this.changed(...created, path);
1353
+ }
1354
+ },
1355
+ rm: after(fs.rm.bind(fs), (path) => [path]),
1356
+ rename: after(fs.rename.bind(fs), (from, to) => [from, to]),
1357
+ symlink: after(fs.symlink.bind(fs), (_target, path) => [path]),
1358
+ chmod: after(fs.chmod.bind(fs), (path) => [path]),
1359
+ utimes: after(fs.utimes.bind(fs), (path) => [path])
1360
+ };
1361
+ return new Proxy(fs, {
1362
+ get(target, key, receiver) {
1363
+ if (Object.hasOwn(overrides, key)) return overrides[key];
1364
+ const value = Reflect.get(target, key, receiver);
1365
+ return typeof value === "function" ? value.bind(target) : value;
1366
+ }
1367
+ });
1368
+ }
1369
+ };
1370
+
1272
1371
  // src/kernel/pipe.ts
1273
1372
  var PIPE_CAPACITY = 64 * 1024;
1274
1373
  var PipeError = class extends Error {
@@ -5866,11 +5965,12 @@ var Launcher = class {
5866
5965
  net = new LoopbackNet();
5867
5966
  ca;
5868
5967
  transport;
5968
+ watchers = new FsWatchers();
5869
5969
  nextPid = 1e3;
5870
5970
  boot = Date.now();
5871
5971
  terminals = 0;
5872
5972
  constructor(options) {
5873
- this.base = options.fs;
5973
+ this.base = this.watchers.wrap(options.fs);
5874
5974
  this.createWorker = options.createWorker;
5875
5975
  this.modulesDir = options.modules ?? "/node_modules";
5876
5976
  this.env = { ...networkEnv(), ...options.env };
@@ -5881,7 +5981,7 @@ var Launcher = class {
5881
5981
  engine: kernelTlsEngine(packageTlsEngine(options.fs, this.modulesDir)),
5882
5982
  ca: this.ca
5883
5983
  });
5884
- this.fs = withCommandDirs(options.fs, async () => new Set((await this.commands()).keys()));
5984
+ this.fs = withCommandDirs(this.base, async () => new Set((await this.commands()).keys()));
5885
5985
  }
5886
5986
  commands() {
5887
5987
  this.catalog ??= scanCommands(this.base, this.modulesDir);
@@ -6510,6 +6610,8 @@ var Channel = class {
6510
6610
  remote;
6511
6611
  failure;
6512
6612
  pending = /* @__PURE__ */ new Map();
6613
+ watching = /* @__PURE__ */ new Map();
6614
+ protocol = PROTOCOL;
6513
6615
  nextId = 0;
6514
6616
  port;
6515
6617
  release;
@@ -6549,6 +6651,10 @@ var Channel = class {
6549
6651
  return this.request(call).done;
6550
6652
  }
6551
6653
  receive(data) {
6654
+ if (data.watch !== void 0) {
6655
+ this.watching.get(data.watch)?.(data.change);
6656
+ return;
6657
+ }
6552
6658
  if (data.bye !== void 0) {
6553
6659
  this.end(new KernelGoneError(data.bye));
6554
6660
  return;
@@ -6661,7 +6767,33 @@ function fsOn(channel) {
6661
6767
  realpath: (path) => fs("realpath", path),
6662
6768
  symlink: (target, path) => done4("symlink", target, path),
6663
6769
  readlink: (path) => fs("readlink", path),
6664
- exists: (path) => fs("exists", path)
6770
+ exists: (path) => fs("exists", path),
6771
+ async watch(paths, options, onChange) {
6772
+ if (channel.protocol[1] < 1) {
6773
+ throw new KernelCallError(
6774
+ `this kernel speaks protocol ${channel.protocol.join(".")}, which has no watch`,
6775
+ "ENOSYS"
6776
+ );
6777
+ }
6778
+ const { id, done: done5 } = channel.request({
6779
+ op: "watch",
6780
+ paths,
6781
+ recursive: options.recursive === true
6782
+ });
6783
+ channel.watching.set(id, onChange);
6784
+ try {
6785
+ await done5;
6786
+ } catch (error) {
6787
+ channel.watching.delete(id);
6788
+ throw error;
6789
+ }
6790
+ return {
6791
+ close: () => {
6792
+ if (!channel.watching.delete(id)) return;
6793
+ void channel.call({ op: "unwatch", watch: id }).catch(() => void 0);
6794
+ }
6795
+ };
6796
+ }
6665
6797
  };
6666
6798
  }
6667
6799
  async function greet(channel, lock, timeoutMs) {
@@ -6691,6 +6823,7 @@ async function attachKernel(port, options = {}) {
6691
6823
  const own = await holdLock(locks);
6692
6824
  const channel = new Channel(port, own.release);
6693
6825
  const hello = await greet(channel, own.name, options.timeoutMs ?? 1e4);
6826
+ channel.protocol = hello.protocol;
6694
6827
  if (hello.lock && locks) void locks.request(hello.lock, () => channel.end(new KernelGoneError()));
6695
6828
  const transport = new RemoteTransport(
6696
6829
  { postMessage: (call) => channel.post(call) },