@dsh-electron/dsh-plugin-multi-root-workspace 0.1.11 → 0.1.13

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.
Files changed (52) hide show
  1. package/CHANGELOG.en.md +25 -1
  2. package/CHANGELOG.md +25 -1
  3. package/README.en.md +10 -8
  4. package/README.md +10 -8
  5. package/cordis.patch.yml +7 -2
  6. package/lib/auto-review.js +1 -1
  7. package/lib/client.js +197 -74
  8. package/lib/client.js.map +1 -1
  9. package/lib/{command-BGdz7VBH.js → command-B9855H7y.js} +3 -2
  10. package/lib/command.js +1 -1
  11. package/lib/{compat-BiW-m_q0.js → compat-Bg2HFVMX.js} +3 -0
  12. package/lib/compat.js +1 -1
  13. package/lib/{sandbox-CXk5h-ct.js → dialects-Dr4rDR-H.js} +6 -264
  14. package/lib/{fs-BTXySwSG.js → fs-CfzJq2wd.js} +23 -8
  15. package/lib/fs.js +1 -1
  16. package/lib/index.js +7 -6
  17. package/lib/{instructions-MePoRigy.js → instructions-KsUwUFE0.js} +53 -37
  18. package/lib/instructions.js +1 -1
  19. package/lib/lsp.js +19 -16
  20. package/lib/native-win32-2waCRkPI.js +312 -0
  21. package/lib/process-fence.js +62 -12
  22. package/lib/protocol-CJOvAvOS.js +652 -0
  23. package/lib/{registry-KSsmU393.js → registry-Bv431TfO.js} +717 -74
  24. package/lib/registry-operation-C2ZV3Jhw.js +116 -0
  25. package/lib/registry.js +3 -2
  26. package/lib/{review-context-AH-SXHmj.js → review-context-NDpKeMCc.js} +1 -1
  27. package/lib/review-context.js +1 -1
  28. package/lib/sandbox-C-vCJCp3.js +278 -0
  29. package/lib/sandbox.js +1 -1
  30. package/lib/scope.js +101 -11
  31. package/lib/skills.js +48 -29
  32. package/lib/types/authority/native-win32.d.ts +7 -0
  33. package/lib/types/authority/native.d.ts +23 -0
  34. package/lib/types/authority/protocol.d.ts +70 -0
  35. package/lib/types/authority/recovery.d.ts +58 -0
  36. package/lib/types/client/RootSwitcher.d.ts +6 -3
  37. package/lib/types/client/files-tree/adapter.d.ts +63 -1
  38. package/lib/types/compat/authority-storage.d.ts +6 -0
  39. package/lib/types/compat/dsh-version.d.ts +4 -4
  40. package/lib/types/compat/open-in-app-device.d.ts +1 -1
  41. package/lib/types/compat/process-entrypoints.d.ts +2 -0
  42. package/lib/types/compat/process-profile.d.ts +3 -0
  43. package/lib/types/compat/sandbox-confine.d.ts +2 -0
  44. package/lib/types/fs.d.ts +4 -1
  45. package/lib/types/instructions.d.ts +0 -47
  46. package/lib/types/registry-operation.d.ts +31 -0
  47. package/lib/types/registry.d.ts +145 -6
  48. package/lib/types/scope.d.ts +28 -0
  49. package/lib/types/skills/catalog.d.ts +3 -0
  50. package/lib/types/skills/pool.d.ts +1 -1
  51. package/lib/workspace-files.js +63 -7
  52. package/package.json +38 -31
@@ -0,0 +1,652 @@
1
+ import { closeSync, lstatSync, mkdirSync, readFileSync, realpathSync, statSync, unlinkSync } from "node:fs";
2
+ import { createHash, randomUUID } from "node:crypto";
3
+ import { join } from "node:path";
4
+ import { z } from "zod";
5
+ import { Socket } from "node:net";
6
+ //#region src/authority/native.ts
7
+ /** Kernel-authenticated local IPC. No credential file and no inherited capability. */
8
+ function storeIdentity(root) {
9
+ mkdirSync(root, {
10
+ recursive: true,
11
+ mode: 448
12
+ });
13
+ const physical = realpathSync.native(root);
14
+ const info = statSync(physical, { bigint: true });
15
+ const id = createHash("sha256").update(`${info.dev}:${info.ino}:${process.platform === "win32" ? physical.toLowerCase() : physical}`).digest("hex");
16
+ if (process.platform === "win32") return {
17
+ root: physical,
18
+ id,
19
+ endpoint: `\\\\.\\pipe\\LOCAL\\dsh-registry-${id}`
20
+ };
21
+ const directory = `/tmp/dsh-registry-${process.getuid()}`;
22
+ try {
23
+ mkdirSync(directory, { mode: 448 });
24
+ } catch (error) {
25
+ if (error.code !== "EEXIST") throw error;
26
+ }
27
+ const entry = lstatSync(directory);
28
+ if (!entry.isDirectory() || entry.isSymbolicLink() || entry.uid !== process.getuid() || (entry.mode & 63) !== 0) throw new Error("unsafe registry IPC directory");
29
+ return {
30
+ root: physical,
31
+ id,
32
+ endpoint: join(directory, `${id.slice(0, 48)}.sock`)
33
+ };
34
+ }
35
+ let posixPromise;
36
+ async function loadPosix() {
37
+ const koffi = (await import("koffi")).default;
38
+ const libc = koffi.load(process.platform === "darwin" ? "/usr/lib/libSystem.B.dylib" : "libc.so.6");
39
+ const bind = (name, ret, args) => libc.func(name, ret, args);
40
+ const socketpair = bind("socketpair", "int", [
41
+ "int",
42
+ "int",
43
+ "int",
44
+ "void *"
45
+ ]);
46
+ const socket = bind("socket", "int", [
47
+ "int",
48
+ "int",
49
+ "int"
50
+ ]);
51
+ const connect = bind("connect", "int", [
52
+ "int",
53
+ "void *",
54
+ "uint"
55
+ ]);
56
+ const bindSocket = bind("bind", "int", [
57
+ "int",
58
+ "void *",
59
+ "uint"
60
+ ]);
61
+ const listen = bind("listen", "int", ["int", "int"]);
62
+ const accept = bind("accept", "int", [
63
+ "int",
64
+ "void *",
65
+ "void *"
66
+ ]);
67
+ const fcntlCall = libc.func("int fcntl(int fd, int command, ...)");
68
+ const fcntl = (fd, command, value) => fcntlCall(fd, command, "int", value);
69
+ const getsockopt = bind("getsockopt", "int", [
70
+ "int",
71
+ "int",
72
+ "int",
73
+ "void *",
74
+ "void *"
75
+ ]);
76
+ const poll = bind("poll", "int", [
77
+ "void *",
78
+ "uint",
79
+ "int"
80
+ ]);
81
+ let auditCheck;
82
+ if (process.platform === "darwin") {
83
+ const tokenType = koffi.struct({ val: koffi.array("uint32", 8) });
84
+ auditCheck = koffi.load("/usr/lib/libsandbox.dylib").func("sandbox_check_by_audit_token", "int", [
85
+ tokenType,
86
+ "void *",
87
+ "int"
88
+ ]);
89
+ }
90
+ function option(fd, level, name, size) {
91
+ const bytes = Buffer.alloc(size), length = Buffer.alloc(4);
92
+ length.writeUInt32LE(size);
93
+ if (getsockopt(fd, level, name, bytes, length) !== 0 || length.readUInt32LE() !== size) throw new Error("kernel peer attestation is unavailable");
94
+ return bytes;
95
+ }
96
+ function authenticate(fd) {
97
+ const uid = process.getuid();
98
+ if (process.platform === "darwin") {
99
+ const token = option(fd, 0, 6, 32);
100
+ if (token.readUInt32LE(4) !== uid || auditCheck({ val: [...new Uint32Array(token.buffer, token.byteOffset, 8)] }, null, 0) !== 0) throw new Error("registry IPC refuses a different user or sandboxed peer");
101
+ return () => {};
102
+ }
103
+ const cred = option(fd, 1, 17, 12);
104
+ const pidfd = option(fd, 1, 77, 4).readInt32LE();
105
+ try {
106
+ const pfd = Buffer.alloc(8);
107
+ pfd.writeInt32LE(pidfd);
108
+ pfd.writeInt16LE(1, 4);
109
+ const status = readFileSync(`/proc/${cred.readInt32LE()}/status`, "utf8");
110
+ const ids = /^Uid:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/m.exec(status);
111
+ if (cred.readUInt32LE(4) !== uid || ids === null || ids.slice(1).some((value) => Number(value) !== uid) || !/^NoNewPrivs:\s+0$/m.test(status) || poll(pfd, 1, 0) !== 0) throw new Error("registry IPC refuses a different user, exited or restricted peer");
112
+ return () => closeSync(pidfd);
113
+ } catch (error) {
114
+ closeSync(pidfd);
115
+ throw error;
116
+ }
117
+ }
118
+ function address(path) {
119
+ const data = Buffer.from(path);
120
+ if (data.length >= (process.platform === "darwin" ? 104 : 108)) throw new Error("registry IPC path is too long");
121
+ const bytes = Buffer.alloc(data.length + 3);
122
+ if (process.platform === "darwin") {
123
+ bytes[0] = bytes.length;
124
+ bytes[1] = 1;
125
+ } else bytes.writeUInt16LE(1);
126
+ data.copy(bytes, 2);
127
+ return bytes;
128
+ }
129
+ function open() {
130
+ const fd = socket(1, 1, 0);
131
+ if (fd < 0) throw new Error("cannot create registry IPC socket");
132
+ if (fcntl(fd, 2, 1) !== 0 || fcntl(fd, 4, process.platform === "darwin" ? 4 : 2048) !== 0) {
133
+ closeSync(fd);
134
+ throw new Error("cannot make registry socket non-inheritable/non-blocking");
135
+ }
136
+ return fd;
137
+ }
138
+ function channel(fd) {
139
+ let cleanup;
140
+ try {
141
+ if (fcntl(fd, 2, 1) !== 0 || fcntl(fd, 4, process.platform === "darwin" ? 4 : 2048) !== 0) throw new Error("cannot make registry connection non-inheritable/non-blocking");
142
+ cleanup = authenticate(fd);
143
+ const result = new Socket({
144
+ fd,
145
+ readable: true,
146
+ writable: true
147
+ });
148
+ result.on("close", cleanup);
149
+ result.on("error", () => {});
150
+ return result;
151
+ } catch (error) {
152
+ cleanup?.();
153
+ closeSync(fd);
154
+ throw error;
155
+ }
156
+ }
157
+ return {
158
+ open,
159
+ channel,
160
+ address,
161
+ connect,
162
+ bindSocket,
163
+ listen,
164
+ accept,
165
+ authenticate,
166
+ socketpair
167
+ };
168
+ }
169
+ function posix$1() {
170
+ return posixPromise ??= loadPosix();
171
+ }
172
+ async function connectNative(endpoint) {
173
+ if (process.platform === "win32") return (await import("./native-win32-2waCRkPI.js")).connectWindows(endpoint);
174
+ const api = await posix$1(), address = api.address(endpoint), fd = api.open();
175
+ if (api.connect(fd, address, address.length) !== 0) {
176
+ closeSync(fd);
177
+ throw new Error("registry authority is not ready");
178
+ }
179
+ return api.channel(fd);
180
+ }
181
+ /** Called only by the kernel lease holder. Never removes another epoch's socket. */
182
+ async function listenNative(endpoint, accepted) {
183
+ if (process.platform === "win32") return (await import("./native-win32-2waCRkPI.js")).listenWindows(endpoint, accepted);
184
+ const api = await posix$1(), address = api.address(endpoint), fd = api.open();
185
+ try {
186
+ unlinkSync(endpoint);
187
+ } catch (error) {
188
+ if (error.code !== "ENOENT") {
189
+ closeSync(fd);
190
+ throw error;
191
+ }
192
+ }
193
+ if (api.bindSocket(fd, address, address.length) !== 0 || api.listen(fd, 32) !== 0) {
194
+ closeSync(fd);
195
+ throw new Error("cannot listen for registry Hosts");
196
+ }
197
+ const inode = lstatSync(endpoint).ino;
198
+ let closed = false;
199
+ const timer = setInterval(() => {
200
+ for (let i = 0; i < 8 && !closed; i++) {
201
+ const client = api.accept(fd, null, null);
202
+ if (client < 0) break;
203
+ let channel;
204
+ try {
205
+ channel = api.channel(client);
206
+ accepted(channel);
207
+ } catch {
208
+ channel?.destroy();
209
+ }
210
+ }
211
+ }, 20);
212
+ timer.unref();
213
+ return { close() {
214
+ if (closed) return;
215
+ closed = true;
216
+ clearInterval(timer);
217
+ closeSync(fd);
218
+ try {
219
+ if (lstatSync(endpoint).ino === inode) unlinkSync(endpoint);
220
+ } catch {}
221
+ } };
222
+ }
223
+ /** Capability probe for native tests and diagnostics; not an authorization bypass. */
224
+ async function nativeHostAvailability() {
225
+ if (process.platform === "win32") return (await import("./native-win32-2waCRkPI.js")).windowsHostAvailability();
226
+ if (process.platform === "linux") {
227
+ if (!/^NoNewPrivs:\s+0$/m.test(readFileSync("/proc/self/status", "utf8"))) return {
228
+ available: false,
229
+ reason: "the Host itself has NoNewPrivs; shared IPC refuses restricted peers"
230
+ };
231
+ } else if (process.platform === "darwin") {
232
+ if ((await import("koffi")).default.load("/usr/lib/libsandbox.dylib").func("int sandbox_check(int pid, const char *operation, int filter)")(process.pid, null, 0) !== 0) return {
233
+ available: false,
234
+ reason: "the Host itself is sandboxed; shared IPC refuses sandboxed peers"
235
+ };
236
+ }
237
+ const api = await posix$1(), pair = Buffer.alloc(8);
238
+ if (api.socketpair(1, 1, 0, pair) !== 0) throw new Error("cannot probe registry IPC credentials");
239
+ try {
240
+ api.authenticate(pair.readInt32LE())();
241
+ } catch (error) {
242
+ if (process.platform === "linux" && String(error).includes("kernel peer attestation is unavailable")) return {
243
+ available: false,
244
+ reason: "the kernel lacks SO_PEERPIDFD; shared IPC requires a pinned peer identity"
245
+ };
246
+ throw error;
247
+ } finally {
248
+ closeSync(pair.readInt32LE());
249
+ closeSync(pair.readInt32LE(4));
250
+ }
251
+ return { available: true };
252
+ }
253
+ //#endregion
254
+ //#region src/authority/protocol.ts
255
+ /** Bounded, versioned requests on an already kernel-attested connection. */
256
+ const MAX_FRAME = 1048576;
257
+ const MAX_PENDING = 32;
258
+ const helloSchema = z.object({
259
+ protocol: z.literal(1),
260
+ store: z.string(),
261
+ epoch: z.string(),
262
+ limit: z.number().int()
263
+ }).strict();
264
+ const requestSchema = z.object({
265
+ protocol: z.literal(1),
266
+ store: z.string(),
267
+ id: z.string().uuid(),
268
+ epoch: z.string().nullable(),
269
+ method: z.enum([
270
+ "hello",
271
+ "snapshot",
272
+ "admit",
273
+ "add",
274
+ "removeAt",
275
+ "setAccess",
276
+ "setAlias",
277
+ "setSkillsEnabled",
278
+ "setAliasId",
279
+ "move",
280
+ "recheck"
281
+ ]),
282
+ args: z.array(z.unknown()).max(4)
283
+ }).strict();
284
+ const rootSchema = z.object({
285
+ id: z.string(),
286
+ path: z.string(),
287
+ access: z.enum([
288
+ "inherit",
289
+ "read-only",
290
+ "read-write"
291
+ ]),
292
+ addedAt: z.string(),
293
+ recordedPath: z.string().optional(),
294
+ readOnlyRecordedPath: z.string().optional(),
295
+ readWriteRecordedPath: z.string().optional(),
296
+ alias: z.string().optional(),
297
+ aliasId: z.unknown().optional(),
298
+ skillsEnabled: z.unknown().optional(),
299
+ state: z.enum([
300
+ "available",
301
+ "missing",
302
+ "redirected",
303
+ "invalid"
304
+ ]),
305
+ detail: z.string().optional(),
306
+ invalidReason: z.literal("membership-conflict").optional()
307
+ }).strict();
308
+ const snapshotSchema = z.object({
309
+ protocol: z.literal(1),
310
+ store: z.string(),
311
+ epoch: z.string(),
312
+ revision: z.number().int().nonnegative().safe(),
313
+ primary: z.string(),
314
+ roots: z.array(rootSchema).max(4096),
315
+ available: z.boolean()
316
+ }).strict();
317
+ const replySchema = z.discriminatedUnion("type", [
318
+ z.object({
319
+ type: z.literal("reply"),
320
+ id: z.string().uuid(),
321
+ value: z.unknown()
322
+ }).strict(),
323
+ z.object({
324
+ type: z.literal("error"),
325
+ id: z.string().uuid(),
326
+ code: z.string(),
327
+ message: z.string()
328
+ }).strict(),
329
+ z.object({
330
+ type: z.literal("change"),
331
+ value: snapshotSchema
332
+ }).strict()
333
+ ]);
334
+ var AuthorityRpcError = class extends Error {
335
+ code;
336
+ constructor(code, message) {
337
+ super(message);
338
+ this.code = code;
339
+ }
340
+ };
341
+ function receive(channel, body) {
342
+ let data = Buffer.alloc(0);
343
+ channel.on("data", (chunk) => {
344
+ try {
345
+ if (data.length + chunk.length > MAX_FRAME) throw new Error("registry IPC frame exceeds budget");
346
+ data = Buffer.concat([data, chunk]);
347
+ for (;;) {
348
+ const end = data.indexOf(10);
349
+ if (end < 0) break;
350
+ const line = data.subarray(0, end);
351
+ data = data.subarray(end + 1);
352
+ body(JSON.parse(line.toString("utf8")));
353
+ }
354
+ } catch {
355
+ channel.destroy();
356
+ }
357
+ });
358
+ }
359
+ function send(channel, value) {
360
+ const text = JSON.stringify(value) + "\n";
361
+ if (Buffer.byteLength(text) + (channel.writableLength ?? 0) > MAX_FRAME) {
362
+ channel.destroy();
363
+ return false;
364
+ }
365
+ channel.write(text);
366
+ return true;
367
+ }
368
+ var AuthorityClient = class AuthorityClient {
369
+ channel;
370
+ store;
371
+ limit;
372
+ pending = /* @__PURE__ */ new Map();
373
+ closed = false;
374
+ epoch;
375
+ constructor(channel, store, limit, onChange, disconnected) {
376
+ this.channel = channel;
377
+ this.store = store;
378
+ this.limit = limit;
379
+ channel.on("close", () => {
380
+ if (this.closed) return;
381
+ this.closed = true;
382
+ for (const request of this.pending.values()) {
383
+ clearTimeout(request.timer);
384
+ request.reject(new AuthorityRpcError("authority-disconnected", "registry request result is unknown; resynchronize before another mutation"));
385
+ }
386
+ this.pending.clear();
387
+ disconnected();
388
+ });
389
+ channel.on("error", () => channel.destroy());
390
+ receive(channel, (value) => {
391
+ const reply = replySchema.parse(value);
392
+ if (reply.type === "change") {
393
+ if (reply.value.store !== store || reply.value.epoch !== this.epoch) throw new Error("wrong registry epoch");
394
+ onChange(reply.value);
395
+ return;
396
+ }
397
+ const request = this.pending.get(reply.id);
398
+ if (!request) return;
399
+ if (reply.type === "reply" && request.method === "hello") {
400
+ const hello = helloSchema.parse(reply.value);
401
+ if (hello.store !== store || hello.limit !== this.limit) throw new Error("registry configuration mismatch");
402
+ this.epoch = hello.epoch;
403
+ }
404
+ this.pending.delete(reply.id);
405
+ clearTimeout(request.timer);
406
+ if (reply.type === "reply") request.resolve(reply.value);
407
+ else request.reject(new AuthorityRpcError(reply.code, reply.message));
408
+ });
409
+ }
410
+ static async connect(endpoint, store, limit, onChange, disconnected, signal) {
411
+ if (signal?.aborted) throw new AuthorityRpcError("authority-cancelled", "registry handshake was cancelled");
412
+ const channel = await connectNative(endpoint);
413
+ if (signal?.aborted) {
414
+ channel.destroy();
415
+ throw new AuthorityRpcError("authority-cancelled", "registry handshake was cancelled");
416
+ }
417
+ const client = new AuthorityClient(channel, store, limit, onChange, disconnected);
418
+ const onAbort = () => client.close();
419
+ signal?.addEventListener("abort", onAbort, { once: true });
420
+ try {
421
+ const hello = helloSchema.parse(await client.request("hello", [limit]));
422
+ if (hello.store !== store || hello.limit !== limit) throw new Error("registry configuration mismatch");
423
+ client.epoch = hello.epoch;
424
+ return client;
425
+ } catch (error) {
426
+ client.close();
427
+ if (signal?.aborted) throw new AuthorityRpcError("authority-cancelled", "registry handshake was cancelled");
428
+ throw error;
429
+ } finally {
430
+ signal?.removeEventListener("abort", onAbort);
431
+ }
432
+ }
433
+ request(method, args) {
434
+ if (this.closed || this.pending.size >= MAX_PENDING) return Promise.reject(new AuthorityRpcError("authority-unavailable", "registry connection is unavailable or busy"));
435
+ const id = randomUUID();
436
+ return new Promise((resolve, reject) => {
437
+ const timer = setTimeout(() => {
438
+ this.channel.destroy();
439
+ }, 5e3);
440
+ this.pending.set(id, {
441
+ method,
442
+ resolve,
443
+ reject,
444
+ timer
445
+ });
446
+ send(this.channel, {
447
+ protocol: 1,
448
+ store: this.store,
449
+ epoch: this.epoch ?? null,
450
+ id,
451
+ method,
452
+ args
453
+ });
454
+ });
455
+ }
456
+ close() {
457
+ this.channel.destroy();
458
+ }
459
+ };
460
+ var AuthorityServer = class AuthorityServer {
461
+ store;
462
+ epoch;
463
+ limit;
464
+ dispatch;
465
+ listener;
466
+ peers = /* @__PURE__ */ new Set();
467
+ readyPeers = /* @__PURE__ */ new Set();
468
+ closed = false;
469
+ inFlight = 0;
470
+ constructor(store, epoch, limit, dispatch) {
471
+ this.store = store;
472
+ this.epoch = epoch;
473
+ this.limit = limit;
474
+ this.dispatch = dispatch;
475
+ }
476
+ static async listen(endpoint, store, epoch, limit, dispatch) {
477
+ const server = new AuthorityServer(store, epoch, limit, dispatch);
478
+ server.listener = await listenNative(endpoint, (channel) => server.accept(channel));
479
+ return server;
480
+ }
481
+ accept(channel) {
482
+ if (this.closed || this.peers.size >= 32) {
483
+ channel.destroy();
484
+ return;
485
+ }
486
+ this.peers.add(channel);
487
+ let ready = false, pending = 0;
488
+ const seen = /* @__PURE__ */ new Map();
489
+ const timer = setTimeout(() => {
490
+ if (!ready) channel.destroy();
491
+ }, 5e3);
492
+ timer.unref();
493
+ channel.on("close", () => {
494
+ clearTimeout(timer);
495
+ this.peers.delete(channel);
496
+ this.readyPeers.delete(channel);
497
+ });
498
+ channel.on("error", () => channel.destroy());
499
+ receive(channel, (value) => {
500
+ const request = requestSchema.parse(value);
501
+ if (request.store !== this.store || pending >= MAX_PENDING) throw new Error("registry peer violated protocol");
502
+ if (request.method === "hello") {
503
+ if (ready || request.epoch !== null || request.args[0] !== this.limit) throw new Error("registry handshake mismatch");
504
+ ready = true;
505
+ this.readyPeers.add(channel);
506
+ clearTimeout(timer);
507
+ send(channel, {
508
+ type: "reply",
509
+ id: request.id,
510
+ value: {
511
+ protocol: 1,
512
+ store: this.store,
513
+ epoch: this.epoch,
514
+ limit: this.limit
515
+ }
516
+ });
517
+ return;
518
+ }
519
+ if (!ready || request.epoch !== this.epoch) throw new Error("registry epoch mismatch");
520
+ if (this.inFlight >= MAX_PENDING) {
521
+ send(channel, {
522
+ type: "error",
523
+ id: request.id,
524
+ code: "authority-busy",
525
+ message: "registry is busy; request was not submitted"
526
+ });
527
+ return;
528
+ }
529
+ const mutation = request.method !== "snapshot" && request.method !== "admit";
530
+ if (mutation && !seen.has(request.id) && seen.size >= 2048) {
531
+ channel.destroy();
532
+ return;
533
+ }
534
+ const fingerprint = createHash("sha256").update(JSON.stringify([request.method, request.args])).digest("hex");
535
+ const entry = seen.get(request.id);
536
+ if (entry && entry.fingerprint !== fingerprint) throw new Error("registry request ID reused with different arguments");
537
+ pending++;
538
+ this.inFlight++;
539
+ const run = entry ? entry.outcome.then((outcome) => {
540
+ if (!outcome.ok) throw new AuthorityRpcError(outcome.code, outcome.message);
541
+ return this.dispatch("snapshot", [request.args[0]]);
542
+ }) : this.dispatch(request.method, request.args);
543
+ if (mutation && !entry) seen.set(request.id, {
544
+ fingerprint,
545
+ outcome: run.then(() => ({ ok: true }), (error) => ({
546
+ ok: false,
547
+ code: String(error?.code ?? "registry-error").slice(0, 120),
548
+ message: String(error?.message ?? error).slice(0, 4096)
549
+ }))
550
+ });
551
+ run.then((result) => send(channel, {
552
+ type: "reply",
553
+ id: request.id,
554
+ value: result
555
+ }), (error) => send(channel, {
556
+ type: "error",
557
+ id: request.id,
558
+ code: String(error?.code ?? "registry-error"),
559
+ message: String(error?.message ?? error)
560
+ })).finally(() => {
561
+ pending--;
562
+ this.inFlight--;
563
+ });
564
+ });
565
+ }
566
+ changed(snapshot) {
567
+ if (this.closed) return;
568
+ for (const peer of this.readyPeers) send(peer, {
569
+ type: "change",
570
+ value: snapshot
571
+ });
572
+ }
573
+ close() {
574
+ if (this.closed) return;
575
+ this.closed = true;
576
+ this.listener?.close();
577
+ for (const peer of this.peers) peer.destroy();
578
+ this.peers.clear();
579
+ }
580
+ };
581
+ const ref = z.discriminatedUnion("kind", [
582
+ z.object({
583
+ kind: z.literal("id"),
584
+ id: z.string()
585
+ }).strict(),
586
+ z.object({
587
+ kind: z.literal("path"),
588
+ path: z.string()
589
+ }).strict(),
590
+ z.object({
591
+ kind: z.literal("ordinal"),
592
+ ordinal: z.number().int().positive()
593
+ }).strict(),
594
+ z.object({
595
+ kind: z.literal("entry"),
596
+ ordinal: z.number().int().positive(),
597
+ id: z.string(),
598
+ path: z.string(),
599
+ addedAt: z.string()
600
+ }).strict()
601
+ ]);
602
+ const primary = z.string().min(1);
603
+ const argumentSchemas = {
604
+ hello: z.tuple([z.number().int()]),
605
+ snapshot: z.tuple([primary]),
606
+ admit: z.tuple([primary]),
607
+ recheck: z.tuple([primary]),
608
+ add: z.tuple([primary, z.object({
609
+ path: z.string(),
610
+ alias: z.string().optional(),
611
+ access: z.enum([
612
+ "inherit",
613
+ "read-only",
614
+ "read-write"
615
+ ]).optional()
616
+ }).strict()]),
617
+ removeAt: z.tuple([primary, ref]),
618
+ setAccess: z.tuple([
619
+ primary,
620
+ ref,
621
+ z.enum([
622
+ "inherit",
623
+ "read-only",
624
+ "read-write"
625
+ ])
626
+ ]),
627
+ setAlias: z.tuple([
628
+ primary,
629
+ ref,
630
+ z.string().nullable()
631
+ ]),
632
+ setSkillsEnabled: z.tuple([
633
+ primary,
634
+ ref,
635
+ z.boolean()
636
+ ]),
637
+ setAliasId: z.tuple([
638
+ primary,
639
+ ref,
640
+ z.string()
641
+ ]),
642
+ move: z.tuple([
643
+ primary,
644
+ ref,
645
+ ref.nullable()
646
+ ])
647
+ };
648
+ function parseArguments(method, args) {
649
+ return argumentSchemas[method].parse(args);
650
+ }
651
+ //#endregion
652
+ export { snapshotSchema as a, parseArguments as i, AuthorityRpcError as n, nativeHostAvailability as o, AuthorityServer as r, storeIdentity as s, AuthorityClient as t };