@statelyai/sdk 0.15.0 → 0.16.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.
@@ -1,5 +1,5 @@
1
- import { a as createPendingExportManager, i as createEventRegistry, r as createWebSocketTransport } from "./transport-D2Ud05yg.mjs";
2
- import { INSPECTION_PROTOCOL_VERSION } from "./protocol.mjs";
1
+ import { i as createPendingExportManager, n as createWebSocketTransport, r as createEventRegistry } from "./transport-B_ig9Znn.mjs";
2
+ import { INSPECTION_PROTOCOL_VERSION, createInspectionRoomUrls, getInspectionRoomId } from "./protocol.mjs";
3
3
 
4
4
  //#region src/openBrowser.ts
5
5
  async function openBrowser(url) {
@@ -26,6 +26,15 @@ async function openBrowser(url) {
26
26
  });
27
27
  }
28
28
 
29
+ //#endregion
30
+ //#region src/version.ts
31
+ const SDK_VERSION = "0.16.1";
32
+ function inspectionRuntime() {
33
+ if (typeof window !== "undefined") return "browser";
34
+ if (typeof process !== "undefined" && process.versions?.node) return "node";
35
+ return "unknown";
36
+ }
37
+
29
38
  //#endregion
30
39
  //#region src/inspect.ts
31
40
  const defaultSerializeSnapshot = (snapshot) => ({
@@ -37,25 +46,23 @@ function isStoppedSnapshot(snapshot) {
37
46
  const status = snapshot?.status;
38
47
  return status === "done" || status === "stopped" || status === "error";
39
48
  }
40
- function generateSessionId() {
41
- if (!globalThis.crypto?.randomUUID) throw new Error("createInspector() requires crypto.randomUUID() to generate a secure session capability. Pass sessionId explicitly in older runtimes.");
49
+ function generateRoomId() {
50
+ if (!globalThis.crypto?.randomUUID) throw new Error("createInspector() requires crypto.randomUUID() to generate a secure room capability. Pass roomId explicitly in older runtimes.");
42
51
  return globalThis.crypto.randomUUID();
43
52
  }
44
- function createInspectorLaunchUrl(websocketUrl, sessionId) {
45
- try {
46
- const launchUrl = new URL(websocketUrl);
47
- if (launchUrl.protocol === "ws:") launchUrl.protocol = "http:";
48
- else if (launchUrl.protocol === "wss:") launchUrl.protocol = "https:";
49
- else return null;
50
- launchUrl.searchParams.set("session", sessionId);
51
- return launchUrl.toString();
52
- } catch {
53
- return null;
54
- }
55
- }
56
53
  function createInspector(options = {}) {
57
- const { url = "ws://localhost:4242", autoOpen = true, name, serializeSnapshot = defaultSerializeSnapshot, extractMachineConfig = defaultExtractMachineConfig, selectedActorId, panels, theme, readOnly, depth, capabilities, transport: injectedTransport } = options;
58
- const sessionId = options.sessionId ?? generateSessionId();
54
+ const environmentUrl = typeof process !== "undefined" ? process.env?.STATELY_INSPECT_URL : void 0;
55
+ const url = options.url ?? environmentUrl ?? "wss://sky.stately.ai";
56
+ const { inspectorBaseUrl, autoOpen = true, name, serializeSnapshot = defaultSerializeSnapshot, extractMachineConfig = defaultExtractMachineConfig, selectedSessionId: configuredSelectedSessionId, selectedActorId, panels, theme, readOnly, depth, capabilities, transport: injectedTransport } = options;
57
+ const roomId = options.roomId ?? options.sessionId ?? getInspectionRoomId(url) ?? generateRoomId();
58
+ const producerId = options.producerId ?? generateRoomId();
59
+ const roomUrls = injectedTransport ? null : createInspectionRoomUrls({
60
+ url,
61
+ roomId,
62
+ inspectorBaseUrl
63
+ });
64
+ const relayUrl = roomUrls?.relayUrl ?? null;
65
+ const inspectorUrl = roomUrls?.inspectorUrl ?? null;
59
66
  const displayName = name ?? "system:manual";
60
67
  let destroyed = false;
61
68
  let initSent = false;
@@ -63,14 +70,26 @@ function createInspector(options = {}) {
63
70
  let pendingTransportReady = false;
64
71
  let transportWasReady = false;
65
72
  let didAutoOpen = false;
73
+ let resolveReady;
74
+ let rejectReady;
75
+ let readySettled = false;
76
+ const ready = new Promise((resolve, reject) => {
77
+ resolveReady = resolve;
78
+ rejectReady = reject;
79
+ });
80
+ ready.catch(() => {});
66
81
  const pendingMessages = [];
67
82
  const events = createEventRegistry();
68
83
  const localListeners = {};
69
84
  const transport = injectedTransport ?? createWebSocketTransport({
70
- url,
71
- role: "client",
72
- sessionId,
73
- metadata: { name: displayName }
85
+ url: relayUrl,
86
+ role: "producer",
87
+ producerId,
88
+ metadata: {
89
+ name: displayName,
90
+ sdkVersion: SDK_VERSION,
91
+ runtime: inspectionRuntime()
92
+ }
74
93
  });
75
94
  const ownsTransport = !injectedTransport;
76
95
  const exportManager = createPendingExportManager((message) => send(message));
@@ -87,6 +106,8 @@ function createInspector(options = {}) {
87
106
  if (!initSent) sendInit();
88
107
  const queued = pendingMessages.splice(0);
89
108
  for (const message of queued) transport.send(message);
109
+ readySettled = true;
110
+ resolveReady();
90
111
  } else {
91
112
  pendingMessages.length = 0;
92
113
  initSent = false;
@@ -94,8 +115,7 @@ function createInspector(options = {}) {
94
115
  }
95
116
  if (autoOpen && !didAutoOpen && ownsTransport) {
96
117
  didAutoOpen = true;
97
- const launchUrl = createInspectorLaunchUrl(url, sessionId);
98
- if (launchUrl) openBrowser(launchUrl).catch((error) => {
118
+ if (inspectorUrl) openBrowser(inspectorUrl).catch((error) => {
99
119
  const failure = {
100
120
  code: "open_failed",
101
121
  message: error instanceof Error ? error.message : "Unable to open the inspector browser"
@@ -112,6 +132,15 @@ function createInspector(options = {}) {
112
132
  }
113
133
  handleTransportReady();
114
134
  });
135
+ transport.onError?.((failure) => {
136
+ const error = new Error(failure.message);
137
+ if (!readySettled) {
138
+ readySettled = true;
139
+ rejectReady(error);
140
+ }
141
+ emitLocal("error", failure);
142
+ events.emit("error", failure);
143
+ });
115
144
  transport.onMessage((msg) => {
116
145
  if (destroyed) return;
117
146
  switch (msg.type) {
@@ -120,6 +149,10 @@ function createInspector(options = {}) {
120
149
  break;
121
150
  case "@statelyai.error":
122
151
  exportManager.reject(new Error(msg.message), msg.requestId);
152
+ if (!readySettled) {
153
+ readySettled = true;
154
+ rejectReady(new Error(msg.message));
155
+ }
123
156
  emitLocal("error", {
124
157
  code: msg.code,
125
158
  message: msg.message
@@ -138,13 +171,14 @@ function createInspector(options = {}) {
138
171
  for (const handler of set) handler(data);
139
172
  }
140
173
  const actors = /* @__PURE__ */ new Map();
174
+ const manualSessionIds = /* @__PURE__ */ new Map();
141
175
  /** Tracks which actors we've already emitted local actor events for. */
142
176
  const locallyRegistered = /* @__PURE__ */ new Set();
143
177
  /** v5 emits transition facets separately; accumulate them until snapshot. */
144
178
  const pendingTransitions = /* @__PURE__ */ new Map();
145
179
  function emitRegisterOnce(entry) {
146
- if (locallyRegistered.has(entry.actorId)) return;
147
- locallyRegistered.add(entry.actorId);
180
+ if (locallyRegistered.has(entry.sessionId)) return;
181
+ locallyRegistered.add(entry.sessionId);
148
182
  emitLocal("actor", entry);
149
183
  emitLocal("register", entry);
150
184
  }
@@ -164,10 +198,10 @@ function createInspector(options = {}) {
164
198
  }
165
199
  function toEntry(actorRef, rootActorId, details) {
166
200
  return {
167
- actorId: actorRef.sessionId,
168
- parentActorId: details?.parentRef?.sessionId ?? actorRef._parent?.sessionId ?? null,
169
- rootId: rootActorId,
170
- id: details?.id ?? actorRef.id ?? actorRef.sessionId,
201
+ sessionId: actorRef.sessionId,
202
+ actorId: details?.id ?? actorRef.id ?? actorRef.sessionId,
203
+ parentSessionId: details?.parentRef?.sessionId ?? actorRef._parent?.sessionId ?? null,
204
+ rootSessionId: rootActorId,
171
205
  systemId: actorRef.systemId,
172
206
  snapshot: details?.snapshot ? serializeSnapshot(details.snapshot) : snapshotValue(actorRef),
173
207
  stopped: stoppedValue(actorRef, details?.snapshot),
@@ -176,9 +210,9 @@ function createInspector(options = {}) {
176
210
  }
177
211
  function toSystemEntry(entry) {
178
212
  return {
213
+ sessionId: entry.sessionId,
179
214
  actorId: entry.actorId,
180
- parentActorId: entry.parentActorId,
181
- id: entry.id,
215
+ parentSessionId: entry.parentSessionId,
182
216
  machine: entry.machineConfig,
183
217
  snapshot: entry.snapshot ?? void 0,
184
218
  ...entry.stopped && { stopped: true }
@@ -195,7 +229,7 @@ function createInspector(options = {}) {
195
229
  const snapshot = actorRef.getSnapshot();
196
230
  existing.snapshot = serializeSnapshot(snapshot);
197
231
  existing.stopped ||= isStoppedSnapshot(snapshot);
198
- } else actors.set(entry.actorId, entry);
232
+ } else actors.set(entry.sessionId, entry);
199
233
  emitRegisterOnce(entry);
200
234
  const children = actorRef.getSnapshot()?.children ?? {};
201
235
  for (const child of Object.values(children)) if (child) backfillActorTree(child, rootActorId);
@@ -204,12 +238,14 @@ function createInspector(options = {}) {
204
238
  const observedRootIds = /* @__PURE__ */ new Set();
205
239
  function sendInit() {
206
240
  for (const [rootActorId, { actor }] of inspectedRoots) backfillActorTree(actor, rootActorId);
241
+ const allEntries = Array.from(actors.values());
242
+ const selectedSessionId = configuredSelectedSessionId ?? selectedActorId ?? observedRootIds.values().next().value ?? allEntries[0]?.sessionId;
207
243
  send({
208
244
  type: "@statelyai.system.init",
209
245
  protocolVersion: INSPECTION_PROTOCOL_VERSION,
210
246
  mode: "inspecting",
211
- actors: Array.from(actors.values()).map(toSystemEntry),
212
- selectedActorId: selectedActorId ?? observedRootIds.values().next().value ?? sessionId,
247
+ actors: allEntries.map(toSystemEntry),
248
+ ...selectedSessionId && { selectedSessionId },
213
249
  ...theme && { theme },
214
250
  ...readOnly !== void 0 && { readOnly },
215
251
  ...depth !== void 0 && { depth },
@@ -223,13 +259,13 @@ function createInspector(options = {}) {
223
259
  function handleActor(actorRef, rootActorId, details) {
224
260
  if (actors.has(actorRef.sessionId)) return;
225
261
  const entry = toEntry(actorRef, rootActorId, details);
226
- actors.set(entry.actorId, entry);
262
+ actors.set(entry.sessionId, entry);
227
263
  emitRegisterOnce(entry);
228
264
  if (initSent) send({
229
265
  type: "@statelyai.system.actorRegistered",
266
+ sessionId: entry.sessionId,
230
267
  actorId: entry.actorId,
231
- parentActorId: entry.parentActorId,
232
- id: entry.id,
268
+ parentSessionId: entry.parentSessionId,
233
269
  machine: entry.machineConfig,
234
270
  snapshot: entry.snapshot ?? void 0
235
271
  });
@@ -254,7 +290,7 @@ function createInspector(options = {}) {
254
290
  }
255
291
  function normalizeSentEvent(sentEvent) {
256
292
  return {
257
- ...sentEvent.targetRef?.sessionId && { targetActorId: sentEvent.targetRef.sessionId },
293
+ ...sentEvent.targetRef?.sessionId && { targetSessionId: sentEvent.targetRef.sessionId },
258
294
  targetId: sentEvent.targetId,
259
295
  event: sentEvent.event,
260
296
  ...sentEvent.delay !== void 0 && { delay: sentEvent.delay },
@@ -265,23 +301,23 @@ function createInspector(options = {}) {
265
301
  return {
266
302
  previousSnapshot: actors.get(actorRef.sessionId)?.snapshot ?? null,
267
303
  event,
268
- sourceActorId: sourceRef?.sessionId ?? null,
304
+ sourceSessionId: sourceRef?.sessionId ?? null,
269
305
  actions: [],
270
306
  transitions: [],
271
307
  sent: []
272
308
  };
273
309
  }
274
- function handleEvent(actorId, event, sourceActorId) {
310
+ function handleEvent(sessionId, event, sourceSessionId) {
275
311
  const inspectedEvent = {
276
- actorId,
277
- sourceActorId,
312
+ sessionId,
313
+ sourceSessionId,
278
314
  event
279
315
  };
280
316
  if (initSent) send({
281
317
  type: "@statelyai.system.actorEvent",
282
- actorId,
318
+ sessionId,
283
319
  event,
284
- sourceActorId
320
+ sourceSessionId
285
321
  });
286
322
  emitLocal("event", inspectedEvent);
287
323
  }
@@ -298,8 +334,8 @@ function createInspector(options = {}) {
298
334
  if (!entry) return;
299
335
  const serialized = serializeSnapshot(snapshot);
300
336
  const inspectedSnapshot = {
301
- actorId: entry.actorId,
302
- sourceActorId: transition.sourceActorId,
337
+ sessionId: entry.sessionId,
338
+ sourceSessionId: transition.sourceSessionId,
303
339
  previousSnapshot: transition.previousSnapshot,
304
340
  snapshot: serialized,
305
341
  event: triggeringEvent ?? transition.event ?? null,
@@ -311,14 +347,14 @@ function createInspector(options = {}) {
311
347
  entry.snapshot = serialized;
312
348
  const stopped = isStoppedSnapshot(snapshot);
313
349
  entry.stopped ||= stopped;
314
- pendingTransitions.delete(entry.actorId);
350
+ pendingTransitions.delete(entry.sessionId);
315
351
  if (initSent) send({
316
352
  type: "@statelyai.system.actorSnapshot",
317
- actorId: entry.actorId,
353
+ sessionId: entry.sessionId,
318
354
  previousSnapshot: inspectedSnapshot.previousSnapshot,
319
355
  snapshot: inspectedSnapshot.snapshot,
320
356
  event: inspectedSnapshot.event,
321
- sourceActorId: inspectedSnapshot.sourceActorId,
357
+ sourceSessionId: inspectedSnapshot.sourceSessionId,
322
358
  actions: inspectedSnapshot.actions,
323
359
  transitions: inspectedSnapshot.transitions,
324
360
  sent: inspectedSnapshot.sent
@@ -326,12 +362,12 @@ function createInspector(options = {}) {
326
362
  emitLocal("snapshot", inspectedSnapshot);
327
363
  emitLocal("transition", inspectedSnapshot);
328
364
  if (stopped && !wasStopped) {
329
- pendingTransitions.delete(entry.actorId);
365
+ pendingTransitions.delete(entry.sessionId);
330
366
  if (initSent) send({
331
367
  type: "@statelyai.system.actorStopped",
332
- actorId: entry.actorId
368
+ sessionId: entry.sessionId
333
369
  });
334
- const stopped = { actorId: entry.actorId };
370
+ const stopped = { sessionId: entry.sessionId };
335
371
  emitLocal("stop", stopped);
336
372
  emitLocal("stopped", stopped);
337
373
  }
@@ -372,7 +408,7 @@ function createInspector(options = {}) {
372
408
  handleTransition(event.actorRef, event.snapshot, event.event, rootActorId, {
373
409
  previousSnapshot: actors.get(event.actorRef.sessionId)?.snapshot ?? null,
374
410
  event: event.event,
375
- sourceActorId: event.sourceRef?.sessionId ?? null,
411
+ sourceSessionId: event.sourceRef?.sessionId ?? null,
376
412
  actions: event.actions.map(normalizeAction),
377
413
  transitions: event.microsteps.map(normalizeTakenTransition),
378
414
  sent: event.sent.map(normalizeSentEvent)
@@ -400,37 +436,44 @@ function createInspector(options = {}) {
400
436
  inspectedRoots.delete(rootActorId);
401
437
  } };
402
438
  }
439
+ function getManualSessionId(actorId) {
440
+ return manualSessionIds.get(actorId) ?? `${producerId}:${actorId}`;
441
+ }
403
442
  function manualRegister(id, opts) {
404
- if (actors.has(id)) return;
443
+ const sessionId = getManualSessionId(id);
444
+ if (actors.has(sessionId)) return;
445
+ manualSessionIds.set(id, sessionId);
446
+ const parentSessionId = opts?.parent ? getManualSessionId(opts.parent) : null;
405
447
  const entry = {
448
+ sessionId,
406
449
  actorId: id,
407
- parentActorId: opts?.parent ?? null,
408
- rootId: sessionId,
409
- id,
450
+ parentSessionId,
451
+ rootSessionId: (parentSessionId && actors.get(parentSessionId)?.rootSessionId) ?? sessionId,
410
452
  systemId: opts?.systemId,
411
453
  snapshot: opts?.snapshot ?? null,
412
454
  stopped: false,
413
455
  machineConfig: opts?.machine ?? null
414
456
  };
415
- actors.set(id, entry);
457
+ actors.set(sessionId, entry);
416
458
  if (!initSent) sendInit();
417
459
  else send({
418
460
  type: "@statelyai.system.actorRegistered",
461
+ sessionId,
419
462
  actorId: id,
420
- parentActorId: entry.parentActorId,
421
- id,
463
+ parentSessionId: entry.parentSessionId,
422
464
  machine: entry.machineConfig,
423
465
  snapshot: entry.snapshot ?? void 0
424
466
  });
425
467
  emitRegisterOnce(entry);
426
468
  }
427
469
  function manualSnapshot(actorId, snapshot, event) {
428
- if (!actors.has(actorId)) manualRegister(actorId);
429
- const entry = actors.get(actorId);
470
+ const sessionId = getManualSessionId(actorId);
471
+ if (!actors.has(sessionId)) manualRegister(actorId);
472
+ const entry = actors.get(sessionId);
430
473
  const previousSnapshot = entry.snapshot;
431
474
  const inspectedSnapshot = {
432
- actorId,
433
- sourceActorId: null,
475
+ sessionId,
476
+ sourceSessionId: null,
434
477
  previousSnapshot,
435
478
  snapshot,
436
479
  event: event ?? null,
@@ -441,11 +484,11 @@ function createInspector(options = {}) {
441
484
  entry.snapshot = snapshot;
442
485
  if (initSent) send({
443
486
  type: "@statelyai.system.actorSnapshot",
444
- actorId,
487
+ sessionId,
445
488
  previousSnapshot,
446
489
  snapshot,
447
490
  event: event ?? null,
448
- sourceActorId: null,
491
+ sourceSessionId: null,
449
492
  actions: [],
450
493
  transitions: [],
451
494
  sent: []
@@ -455,18 +498,20 @@ function createInspector(options = {}) {
455
498
  }
456
499
  function manualEvent(actorId, event, eventOpts) {
457
500
  const eventObj = typeof event === "string" ? { type: event } : { ...event };
458
- if (!actors.has(actorId)) manualRegister(actorId);
459
- handleEvent(actorId, eventObj, eventOpts?.source ?? null);
501
+ const sessionId = getManualSessionId(actorId);
502
+ if (!actors.has(sessionId)) manualRegister(actorId);
503
+ handleEvent(sessionId, eventObj, eventOpts?.source ? getManualSessionId(eventOpts.source) : null);
460
504
  }
461
505
  function manualStop(actorId) {
462
- const entry = actors.get(actorId);
506
+ const sessionId = getManualSessionId(actorId);
507
+ const entry = actors.get(sessionId);
463
508
  if (!entry || entry.stopped) return;
464
509
  entry.stopped = true;
465
510
  if (initSent) send({
466
511
  type: "@statelyai.system.actorStopped",
467
- actorId
512
+ sessionId
468
513
  });
469
- const stopped = { actorId };
514
+ const stopped = { sessionId };
470
515
  emitLocal("stop", stopped);
471
516
  emitLocal("stopped", stopped);
472
517
  }
@@ -477,9 +522,14 @@ function createInspector(options = {}) {
477
522
  }
478
523
  return {
479
524
  actors,
525
+ roomId,
526
+ relayUrl,
527
+ producerId,
480
528
  get sessionId() {
481
- return sessionId;
529
+ return roomId;
482
530
  },
531
+ inspectorUrl,
532
+ ready,
483
533
  inspect: handleInspectionEvent,
484
534
  attach: attachActor,
485
535
  on(event, handler) {
@@ -1,3 +1,3 @@
1
1
  import { EditorCapabilities, EmbedEventHandler, EmbedEventMap, EmbedEventName, EmbedMode, ExportCallOptions, ExportFormat, ExportFormatMap, InspectionAction, InspectionSentEvent, InspectionTakenTransition } from "./protocol.mjs";
2
- import { a as InspectedTransition, c as InspectorInspectionEvent, d as createStatelyInspector, h as Transport, i as InspectedSnapshot, l as ManualActorOptions, n as CreateInspectorOptions, o as Inspector, r as InspectedEvent, s as InspectorEvents, t as AdoptedActor, u as createInspector } from "./inspect-DenVP7IE.mjs";
2
+ import { a as InspectedTransition, c as InspectorInspectionEvent, d as createStatelyInspector, f as Transport, i as InspectedSnapshot, l as ManualActorOptions, n as CreateInspectorOptions, o as Inspector, r as InspectedEvent, s as InspectorEvents, t as AdoptedActor, u as createInspector } from "./inspect-BScvqVcM.mjs";
3
3
  export { AdoptedActor, CreateInspectorOptions, EditorCapabilities, EmbedEventHandler, EmbedEventMap, EmbedEventName, EmbedMode, ExportCallOptions, ExportFormat, ExportFormatMap, InspectedEvent, InspectedSnapshot, InspectedTransition, InspectionAction, InspectionSentEvent, InspectionTakenTransition, Inspector, InspectorEvents, InspectorInspectionEvent, ManualActorOptions, Transport, createInspector, createStatelyInspector };
package/dist/inspect.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { n as createStatelyInspector, t as createInspector } from "./inspect-DWDTNMEc.mjs";
1
+ import { n as createStatelyInspector, t as createInspector } from "./inspect-C3Y_o_Np.mjs";
2
2
 
3
3
  export { createInspector, createStatelyInspector };
@@ -0,0 +1,22 @@
1
+ //#region src/node-relay.d.ts
2
+ interface NodeInspectionRelayOptions {
3
+ host?: string;
4
+ port?: number;
5
+ logger?: Pick<Console, 'info'>;
6
+ }
7
+ /** Local/self-hosted Node adapter for the room inspection relay. */
8
+ declare class NodeInspectionRelayServer {
9
+ private readonly options;
10
+ private server;
11
+ private webSocketServer;
12
+ private readonly rooms;
13
+ private readonly connections;
14
+ private readonly sockets;
15
+ constructor(options?: NodeInspectionRelayOptions);
16
+ get url(): string;
17
+ start(): Promise<void>;
18
+ stop(): Promise<void>;
19
+ }
20
+ declare function createNodeInspectionRelay(options?: NodeInspectionRelayOptions): NodeInspectionRelayServer;
21
+ //#endregion
22
+ export { NodeInspectionRelayOptions, NodeInspectionRelayServer, createNodeInspectionRelay };
@@ -0,0 +1,84 @@
1
+ import { getInspectionRoomId } from "./protocol.mjs";
2
+ import { createInspectionRelay } from "./relay.mjs";
3
+ import { createServer } from "node:http";
4
+ import { randomUUID } from "node:crypto";
5
+ import { WebSocket, WebSocketServer } from "ws";
6
+
7
+ //#region src/node-relay.ts
8
+ /** Local/self-hosted Node adapter for the room inspection relay. */
9
+ var NodeInspectionRelayServer = class {
10
+ server = null;
11
+ webSocketServer = null;
12
+ rooms = /* @__PURE__ */ new Map();
13
+ connections = /* @__PURE__ */ new Map();
14
+ sockets = /* @__PURE__ */ new Map();
15
+ constructor(options = {}) {
16
+ this.options = options;
17
+ }
18
+ get url() {
19
+ if (!this.server) throw new Error("Inspection relay is not running.");
20
+ const address = this.server.address();
21
+ if (!address || typeof address === "string") throw new Error("Inspection relay address is unavailable.");
22
+ return `ws://${this.options.host ?? "127.0.0.1"}:${address.port}`;
23
+ }
24
+ async start() {
25
+ if (this.server) return;
26
+ this.server = createServer();
27
+ this.webSocketServer = new WebSocketServer({ server: this.server });
28
+ this.webSocketServer.on("connection", (socket, request) => {
29
+ const roomId = getInspectionRoomId(new URL(request.url ?? "/", this.url));
30
+ if (!roomId) {
31
+ socket.close(1008, "Missing ?r=<roomId>");
32
+ return;
33
+ }
34
+ const relay = this.rooms.get(roomId) ?? createInspectionRelay();
35
+ this.rooms.set(roomId, relay);
36
+ const peerId = randomUUID();
37
+ this.connections.set(socket, {
38
+ peerId,
39
+ relay
40
+ });
41
+ this.sockets.set(peerId, socket);
42
+ socket.on("message", (raw) => {
43
+ for (const effect of relay.receive(peerId, raw.toString())) {
44
+ const target = this.sockets.get(effect.peerId);
45
+ if (target?.readyState === WebSocket.OPEN) target.send(JSON.stringify(effect.message));
46
+ }
47
+ });
48
+ socket.on("close", () => {
49
+ relay.disconnect(peerId);
50
+ this.connections.delete(socket);
51
+ this.sockets.delete(peerId);
52
+ });
53
+ });
54
+ await new Promise((resolve, reject) => {
55
+ this.server.once("error", reject);
56
+ this.server.listen(this.options.port ?? 4242, this.options.host ?? "127.0.0.1", () => {
57
+ this.server.off("error", reject);
58
+ resolve();
59
+ });
60
+ });
61
+ this.options.logger?.info(`Inspection relay listening on ${this.url}`);
62
+ }
63
+ async stop() {
64
+ for (const socket of this.connections.keys()) socket.close();
65
+ this.connections.clear();
66
+ this.sockets.clear();
67
+ for (const relay of this.rooms.values()) relay.clear();
68
+ this.rooms.clear();
69
+ if (this.webSocketServer) {
70
+ await new Promise((resolve) => this.webSocketServer.close(() => resolve()));
71
+ this.webSocketServer = null;
72
+ }
73
+ if (this.server) {
74
+ await new Promise((resolve, reject) => this.server.close((error) => error ? reject(error) : resolve()));
75
+ this.server = null;
76
+ }
77
+ }
78
+ };
79
+ function createNodeInspectionRelay(options) {
80
+ return new NodeInspectionRelayServer(options);
81
+ }
82
+
83
+ //#endregion
84
+ export { NodeInspectionRelayServer, createNodeInspectionRelay };