@statelyai/sdk 0.12.3 → 0.13.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.
package/dist/inspect.mjs CHANGED
@@ -1,306 +1,3 @@
1
- import { i as createPendingExportManager, n as createWebSocketTransport, r as createEventRegistry } from "./transport-DysvAtl5.mjs";
1
+ import { n as createStatelyInspector, t as createInspector } from "./inspect-pcNCbL7j.mjs";
2
2
 
3
- //#region src/inspect.ts
4
- const defaultSerializeSnapshot = (snapshot) => ({
5
- value: snapshot?.value,
6
- status: snapshot?.status
7
- });
8
- const defaultExtractMachineConfig = (actor) => actor?.logic?.config ?? null;
9
- let nextManualId = 0;
10
- function generateSessionId() {
11
- return `manual-${Date.now()}-${nextManualId++}`;
12
- }
13
- function createStatelyInspector(options = {}) {
14
- const { url = "ws://localhost:4242", autoOpen = true, name, serializeSnapshot = defaultSerializeSnapshot, extractMachineConfig = defaultExtractMachineConfig, selectedActorId, panels, theme, readOnly, depth, capabilities, transport: injectedTransport } = options;
15
- const sessionId = options.sessionId ?? generateSessionId();
16
- const displayName = name ?? "system:manual";
17
- let destroyed = false;
18
- let initSent = false;
19
- const pendingMessages = [];
20
- const events = createEventRegistry();
21
- const localListeners = {};
22
- const transport = injectedTransport ?? createWebSocketTransport({
23
- url,
24
- role: "client",
25
- sessionId,
26
- metadata: { name: displayName }
27
- });
28
- const ownsTransport = !injectedTransport;
29
- const exportManager = createPendingExportManager((message) => send(message));
30
- function send(msg) {
31
- if (!transport.ready) {
32
- pendingMessages.push(msg);
33
- return;
34
- }
35
- transport.send(msg);
36
- }
37
- transport.onReady(() => {
38
- pendingMessages.length = 0;
39
- initSent = false;
40
- sendInit();
41
- if (autoOpen) transport.send({
42
- type: "@statelyai.requestOpen",
43
- sessionId
44
- });
45
- });
46
- transport.onMessage((msg) => {
47
- if (destroyed) return;
48
- switch (msg.type) {
49
- case "@statelyai.retrieved":
50
- exportManager.resolve(msg.requestId, msg.data);
51
- break;
52
- case "@statelyai.error":
53
- exportManager.reject(new Error(msg.message), msg.requestId);
54
- emitLocal("error", {
55
- code: msg.code,
56
- message: msg.message
57
- });
58
- events.emit("error", {
59
- code: msg.code,
60
- message: msg.message
61
- });
62
- break;
63
- default: break;
64
- }
65
- });
66
- function emitLocal(event, data) {
67
- const set = localListeners[event];
68
- if (!set) return;
69
- for (const handler of set) handler(data);
70
- }
71
- const actors = /* @__PURE__ */ new Map();
72
- /** Tracks which actors we've already emitted local 'register' events for. */
73
- const locallyRegistered = /* @__PURE__ */ new Set();
74
- /**
75
- * Maps target actorId → source actorId for the most recent `@xstate.event`.
76
- * Read + cleared by `handleSnapshot` so the source info propagates into the
77
- * `actorSnapshot` message. xstate emits `@xstate.event` immediately before
78
- * `@xstate.snapshot` for the same actor.
79
- */
80
- const pendingEventSource = /* @__PURE__ */ new Map();
81
- function emitRegisterOnce(entry) {
82
- if (locallyRegistered.has(entry.actorId)) return;
83
- locallyRegistered.add(entry.actorId);
84
- emitLocal("register", entry);
85
- }
86
- function snapshotValue(actorRef) {
87
- try {
88
- return serializeSnapshot(actorRef.getSnapshot());
89
- } catch {
90
- return null;
91
- }
92
- }
93
- function toEntry(actorRef, rootActorId) {
94
- return {
95
- actorId: actorRef.sessionId,
96
- parentActorId: actorRef._parent?.sessionId ?? null,
97
- rootId: rootActorId,
98
- id: actorRef.id,
99
- systemId: actorRef.systemId,
100
- snapshot: snapshotValue(actorRef),
101
- machineConfig: extractMachineConfig(actorRef)
102
- };
103
- }
104
- function toSystemEntry(entry) {
105
- return {
106
- actorId: entry.actorId,
107
- parentActorId: entry.parentActorId,
108
- id: entry.id,
109
- machine: entry.machineConfig,
110
- snapshot: entry.snapshot ?? void 0
111
- };
112
- }
113
- /**
114
- * Walk the actor tree and populate the local `actors` map. Idempotent —
115
- * existing entries stay in place but have their snapshot refreshed.
116
- */
117
- function backfillActorTree(actorRef, rootActorId) {
118
- const existing = actors.get(actorRef.sessionId);
119
- const entry = existing ?? toEntry(actorRef, rootActorId);
120
- if (existing) existing.snapshot = snapshotValue(actorRef);
121
- else actors.set(entry.actorId, entry);
122
- emitRegisterOnce(entry);
123
- const children = actorRef.getSnapshot()?.children ?? {};
124
- for (const child of Object.values(children)) if (child) backfillActorTree(child, rootActorId);
125
- }
126
- const inspectedRoots = /* @__PURE__ */ new Map();
127
- function sendInit() {
128
- for (const [rootActorId, { actor }] of inspectedRoots) backfillActorTree(actor, rootActorId);
129
- send({
130
- type: "@statelyai.system.init",
131
- mode: "inspecting",
132
- actors: Array.from(actors.values()).map(toSystemEntry),
133
- selectedActorId: selectedActorId ?? inspectedRoots.keys().next().value ?? sessionId,
134
- ...theme && { theme },
135
- ...readOnly !== void 0 && { readOnly },
136
- ...depth !== void 0 && { depth },
137
- ...capabilities !== void 0 && { capabilities },
138
- ...panels?.leftPanels && { leftPanels: panels.leftPanels },
139
- ...panels?.rightPanels && { rightPanels: panels.rightPanels },
140
- ...panels?.activePanels && { activePanels: panels.activePanels }
141
- });
142
- initSent = true;
143
- }
144
- function handleActor(actorRef, rootActorId) {
145
- if (actors.has(actorRef.sessionId)) return;
146
- const entry = toEntry(actorRef, rootActorId);
147
- actors.set(entry.actorId, entry);
148
- if (!initSent) return;
149
- send({
150
- type: "@statelyai.system.actorRegistered",
151
- actorId: entry.actorId,
152
- parentActorId: entry.parentActorId,
153
- id: entry.id,
154
- machine: entry.machineConfig,
155
- snapshot: entry.snapshot ?? void 0
156
- });
157
- emitRegisterOnce(entry);
158
- }
159
- function handleSnapshot(actorRef, snapshot, triggeringEvent, rootActorId) {
160
- if (!actors.has(actorRef.sessionId)) handleActor(actorRef, rootActorId);
161
- const entry = actors.get(actorRef.sessionId);
162
- if (!entry) return;
163
- const serialized = serializeSnapshot(snapshot);
164
- entry.snapshot = serialized;
165
- const sourceActorId = pendingEventSource.get(entry.actorId) ?? null;
166
- pendingEventSource.delete(entry.actorId);
167
- if (initSent) send({
168
- type: "@statelyai.system.actorSnapshot",
169
- actorId: entry.actorId,
170
- snapshot: serialized,
171
- event: triggeringEvent ?? null,
172
- ...sourceActorId ? { sourceActorId } : {}
173
- });
174
- emitLocal("snapshot", entry);
175
- const status = snapshot?.status;
176
- if (status === "done" || status === "stopped" || status === "error") {
177
- actors.delete(entry.actorId);
178
- pendingEventSource.delete(entry.actorId);
179
- if (initSent) send({
180
- type: "@statelyai.system.actorStopped",
181
- actorId: entry.actorId
182
- });
183
- emitLocal("stopped", { actorId: entry.actorId });
184
- }
185
- }
186
- function inspectActor(actor) {
187
- const root = actor;
188
- const rootActorId = root.sessionId;
189
- const existing = inspectedRoots.get(rootActorId);
190
- if (existing) return existing.subscription;
191
- const subscription = root.system.inspect((inspectionEvent) => {
192
- if (destroyed) return;
193
- switch (inspectionEvent.type) {
194
- case "@xstate.actor":
195
- handleActor(inspectionEvent.actorRef, rootActorId);
196
- break;
197
- case "@xstate.event":
198
- if (inspectionEvent.sourceRef) pendingEventSource.set(inspectionEvent.actorRef.sessionId, inspectionEvent.sourceRef.sessionId);
199
- break;
200
- case "@xstate.snapshot":
201
- handleSnapshot(inspectionEvent.actorRef, inspectionEvent.snapshot, inspectionEvent.event, rootActorId);
202
- break;
203
- default: break;
204
- }
205
- });
206
- inspectedRoots.set(rootActorId, {
207
- actor: root,
208
- subscription
209
- });
210
- backfillActorTree(root, rootActorId);
211
- if (initSent) sendInit();
212
- return { unsubscribe() {
213
- subscription.unsubscribe();
214
- inspectedRoots.delete(rootActorId);
215
- } };
216
- }
217
- function manualRegister(id, opts) {
218
- if (actors.has(id)) return;
219
- const entry = {
220
- actorId: id,
221
- parentActorId: opts?.parent ?? null,
222
- rootId: sessionId,
223
- id,
224
- systemId: opts?.systemId,
225
- snapshot: opts?.snapshot ?? null,
226
- machineConfig: opts?.machine ?? null
227
- };
228
- actors.set(id, entry);
229
- if (initSent) send({
230
- type: "@statelyai.system.actorRegistered",
231
- actorId: id,
232
- parentActorId: entry.parentActorId,
233
- id,
234
- machine: entry.machineConfig,
235
- snapshot: entry.snapshot ?? void 0
236
- });
237
- emitRegisterOnce(entry);
238
- }
239
- function manualSnapshot(actorId, snapshot, event, sourceActorId) {
240
- if (!actors.has(actorId)) manualRegister(actorId);
241
- const entry = actors.get(actorId);
242
- entry.snapshot = snapshot;
243
- if (initSent) send({
244
- type: "@statelyai.system.actorSnapshot",
245
- actorId,
246
- snapshot,
247
- event: event ?? null,
248
- ...sourceActorId ? { sourceActorId } : {}
249
- });
250
- emitLocal("snapshot", entry);
251
- }
252
- function manualEvent(actorId, event, eventOpts) {
253
- const eventObj = typeof event === "string" ? { type: event } : { ...event };
254
- manualSnapshot(actorId, actors.get(actorId)?.snapshot ?? null, eventObj, eventOpts?.source ?? null);
255
- }
256
- function manualStop(actorId) {
257
- if (!actors.has(actorId)) return;
258
- actors.delete(actorId);
259
- if (initSent) send({
260
- type: "@statelyai.system.actorStopped",
261
- actorId
262
- });
263
- emitLocal("stopped", { actorId });
264
- }
265
- return {
266
- actors,
267
- get sessionId() {
268
- return sessionId;
269
- },
270
- inspect: inspectActor,
271
- on(event, handler) {
272
- if (!localListeners[event]) localListeners[event] = /* @__PURE__ */ new Set();
273
- localListeners[event].add(handler);
274
- return () => {
275
- localListeners[event].delete(handler);
276
- };
277
- },
278
- off(event, handler) {
279
- localListeners[event]?.delete(handler);
280
- },
281
- export(format, callOptions) {
282
- return exportManager.start(format, callOptions, "Inspector is destroyed", () => destroyed);
283
- },
284
- actor: manualRegister,
285
- snapshot: manualSnapshot,
286
- event: manualEvent,
287
- stop: manualStop,
288
- destroy() {
289
- if (destroyed) return;
290
- destroyed = true;
291
- try {
292
- for (const { subscription } of inspectedRoots.values()) subscription.unsubscribe();
293
- } catch {}
294
- if (ownsTransport) transport.destroy();
295
- exportManager.clear("Inspector destroyed");
296
- events.clear();
297
- actors.clear();
298
- inspectedRoots.clear();
299
- pendingMessages.length = 0;
300
- for (const set of Object.values(localListeners)) set?.clear();
301
- }
302
- };
303
- }
304
-
305
- //#endregion
306
- export { createStatelyInspector };
3
+ export { createInspector, createStatelyInspector };
@@ -1,2 +1,2 @@
1
- import { a as EditorStateType, n as CanvasColor, o as GraphPatch, s as ImplementationSourceType, t as ActionLocation } from "./index-BRW2DecD.mjs";
1
+ import { a as EditorStateType, n as CanvasColor, o as GraphPatch, s as ImplementationSourceType, t as ActionLocation } from "./index-DVjNSTuQ.mjs";
2
2
  export { type ActionLocation, type CanvasColor, type EditorStateType, type GraphPatch, type ImplementationSourceType };
@@ -338,8 +338,29 @@ interface ActorSystemEntry {
338
338
  /** Optional current snapshot (`{value, status, ...}`). */
339
339
  snapshot?: unknown;
340
340
  }
341
+ /** JSON-safe record of an action executed during an inspected transition. */
342
+ interface InspectionAction {
343
+ type: string;
344
+ params?: unknown;
345
+ }
346
+ /** JSON-safe identity of a state transition taken during a macrostep. */
347
+ interface InspectionTakenTransition {
348
+ sourceStateId: string | null;
349
+ targetStateIds: string[];
350
+ eventType?: string;
351
+ reenter?: boolean;
352
+ }
353
+ /** JSON-safe record of an event sent or scheduled during a transition. */
354
+ interface InspectionSentEvent {
355
+ targetActorId?: string;
356
+ targetId: string;
357
+ event: unknown;
358
+ delay?: number;
359
+ id?: string;
360
+ }
341
361
  interface SystemInitMessage {
342
362
  type: "@statelyai.system.init";
363
+ protocolVersion?: number;
343
364
  /** All known actors at the moment the system is first streamed. */
344
365
  actors: ActorSystemEntry[];
345
366
  /** Actor id the viz should focus first. Defaults to the root. */
@@ -367,10 +388,18 @@ interface SystemActorRegisteredMessage {
367
388
  interface SystemActorSnapshotMessage {
368
389
  type: "@statelyai.system.actorSnapshot";
369
390
  actorId: string;
391
+ /** Snapshot before the event was processed. */
392
+ previousSnapshot?: unknown | null;
370
393
  snapshot: unknown;
371
394
  event?: unknown | null;
372
395
  /** The actorId of the actor that sent the triggering event, if known. */
373
396
  sourceActorId?: string | null;
397
+ /** Actions executed while processing the event. */
398
+ actions?: InspectionAction[];
399
+ /** State transitions taken across every microstep. */
400
+ transitions?: InspectionTakenTransition[];
401
+ /** Events sent or scheduled while processing the event. */
402
+ sent?: InspectionSentEvent[];
374
403
  }
375
404
  /** An actor reached a final state / was stopped. */
376
405
  interface SystemActorStoppedMessage {
@@ -468,6 +497,7 @@ type ClientMessage = InitMessage | SetApiKeyMessage | ProjectInitMessage | Proje
468
497
  type VizMessage = ReadyMessage | LoadedMessage | ChangeMessage | SaveMessage | ProjectMachineSelectedMessage | RetrievedMessage | ErrorMessage | CapabilityDeniedMessage | UploadRequestMessage;
469
498
  interface RegisterMessage {
470
499
  type: "@statelyai.register";
500
+ protocolVersion?: number;
471
501
  role: "client" | "viz";
472
502
  sessionId: string;
473
503
  metadata?: {
@@ -477,10 +507,7 @@ interface RegisterMessage {
477
507
  }
478
508
  interface RegisteredMessage {
479
509
  type: "@statelyai.registered";
480
- sessionId: string;
481
- }
482
- interface RequestOpenMessage {
483
- type: "@statelyai.requestOpen";
510
+ protocolVersion?: number;
484
511
  sessionId: string;
485
512
  }
486
513
  /**
@@ -508,9 +535,11 @@ interface SessionAddedMessage {
508
535
  sessionId: string;
509
536
  }
510
537
  /** All session-management messages for the WS relay. */
511
- type SessionMessage = RegisterMessage | RegisteredMessage | RequestOpenMessage | AddSessionMessage | SessionAddedMessage;
538
+ type SessionMessage = RegisterMessage | RegisteredMessage | AddSessionMessage | SessionAddedMessage;
512
539
  /** Any valid protocol message. */
513
540
  type ProtocolMessage = ClientMessage | VizMessage | SessionMessage;
514
541
  declare const PREFIX = "@statelyai.";
542
+ /** Current WebSocket inspection protocol version. */
543
+ declare const INSPECTION_PROTOCOL_VERSION = 1;
515
544
  //#endregion
516
- export { ActorSystemEntry, AddSessionMessage, CapabilityDeniedCapability, CapabilityDeniedEvent, CapabilityDeniedMessage, ChangeMessage, ClientMessage, CommentsConfig, EditorCapabilities, EmbedEventHandler, EmbedEventMap, EmbedEventName, EmbedMode, ErrorMessage, ExportCallOptions, ExportFormat, ExportFormatMap, InitMessage, InitOptions, InspectSnapshotMessage, LoadedMessage, MachineInitOptions, MachineSourceLocations, PREFIX, ProjectEmbedMachine, ProjectInitMessage, ProjectInitOptions, ProjectMachineSelectedMessage, ProjectSelectMachineMessage, ProjectUpdateMessage, ProtocolMessage, ReadOnlyReason, ReadyMessage, RegisterMessage, RegisteredMessage, RequestOpenMessage, RetrieveMessage, RetrievedMessage, SaveMessage, SessionAddedMessage, SessionMessage, SetApiKeyMessage, SetModeMessage, SetSettingsMessage, SetThemeMessage, SourceLocation, SourceRange, StateSourceLocation, SystemActorRegisteredMessage, SystemActorSnapshotMessage, SystemActorStoppedMessage, SystemInitMessage, ToastMessage, UpdateMessage, UploadCapabilitiesMessage, UploadFileInfo, UploadRequestMessage, UploadResponseMessage, UploadResult, ValidationIssue, VizMessage };
545
+ export { ActorSystemEntry, AddSessionMessage, CapabilityDeniedCapability, CapabilityDeniedEvent, CapabilityDeniedMessage, ChangeMessage, ClientMessage, CommentsConfig, EditorCapabilities, EmbedEventHandler, EmbedEventMap, EmbedEventName, EmbedMode, ErrorMessage, ExportCallOptions, ExportFormat, ExportFormatMap, INSPECTION_PROTOCOL_VERSION, InitMessage, InitOptions, InspectSnapshotMessage, InspectionAction, InspectionSentEvent, InspectionTakenTransition, LoadedMessage, MachineInitOptions, MachineSourceLocations, PREFIX, ProjectEmbedMachine, ProjectInitMessage, ProjectInitOptions, ProjectMachineSelectedMessage, ProjectSelectMachineMessage, ProjectUpdateMessage, ProtocolMessage, ReadOnlyReason, ReadyMessage, RegisterMessage, RegisteredMessage, RetrieveMessage, RetrievedMessage, SaveMessage, SessionAddedMessage, SessionMessage, SetApiKeyMessage, SetModeMessage, SetSettingsMessage, SetThemeMessage, SourceLocation, SourceRange, StateSourceLocation, SystemActorRegisteredMessage, SystemActorSnapshotMessage, SystemActorStoppedMessage, SystemInitMessage, ToastMessage, UpdateMessage, UploadCapabilitiesMessage, UploadFileInfo, UploadRequestMessage, UploadResponseMessage, UploadResult, ValidationIssue, VizMessage };
package/dist/protocol.mjs CHANGED
@@ -1,5 +1,7 @@
1
1
  //#region src/protocol.ts
2
2
  const PREFIX = "@statelyai.";
3
+ /** Current WebSocket inspection protocol version. */
4
+ const INSPECTION_PROTOCOL_VERSION = 1;
3
5
 
4
6
  //#endregion
5
- export { PREFIX };
7
+ export { INSPECTION_PROTOCOL_VERSION, PREFIX };
@@ -0,0 +1,43 @@
1
+ import { ProtocolMessage } from "./protocol.mjs";
2
+
3
+ //#region src/relay.d.ts
4
+ type InspectionRelayRole = 'client' | 'viz';
5
+ interface InspectionRelayPeer {
6
+ id: string;
7
+ role: InspectionRelayRole;
8
+ primarySessionId: string;
9
+ sessionIds: string[];
10
+ }
11
+ interface InspectionRelaySnapshot {
12
+ sessions: Record<string, ProtocolMessage[]>;
13
+ }
14
+ type InspectionRelayEffect = {
15
+ type: 'send';
16
+ peerId: string;
17
+ message: ProtocolMessage;
18
+ };
19
+ interface InspectionRelay {
20
+ /** Restore a live peer after process/DO hibernation. */
21
+ connect(peer: InspectionRelayPeer): void;
22
+ disconnect(peerId: string): void;
23
+ clear(): void;
24
+ peer(peerId: string): InspectionRelayPeer | undefined;
25
+ receive(peerId: string, raw: unknown): InspectionRelayEffect[];
26
+ snapshot(): InspectionRelaySnapshot;
27
+ }
28
+ interface CreateInspectionRelayOptions {
29
+ snapshot?: InspectionRelaySnapshot;
30
+ maxReplayMessages?: number;
31
+ /** Maximum sessions retained for late-join replay. Default 100. */
32
+ maxReplaySessions?: number;
33
+ }
34
+ /**
35
+ * Runtime-neutral WebSocket inspection room.
36
+ *
37
+ * It owns protocol registration, routing, multiplexed session membership, and
38
+ * bounded late-join replay. Node and Durable Object adapters only translate
39
+ * `send` effects to their native WebSocket APIs and persist `snapshot()`.
40
+ */
41
+ declare function createInspectionRelay(options?: CreateInspectionRelayOptions): InspectionRelay;
42
+ //#endregion
43
+ export { CreateInspectionRelayOptions, InspectionRelay, InspectionRelayEffect, InspectionRelayPeer, InspectionRelayRole, InspectionRelaySnapshot, createInspectionRelay };
package/dist/relay.mjs ADDED
@@ -0,0 +1,153 @@
1
+ import { INSPECTION_PROTOCOL_VERSION, PREFIX } from "./protocol.mjs";
2
+
3
+ //#region src/relay.ts
4
+ function parseMessage(raw) {
5
+ if (raw instanceof ArrayBuffer) raw = new TextDecoder().decode(raw);
6
+ if (typeof raw === "string") try {
7
+ raw = JSON.parse(raw);
8
+ } catch {
9
+ return null;
10
+ }
11
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
12
+ const message = raw;
13
+ return typeof message.type === "string" && message.type.startsWith(PREFIX) ? message : null;
14
+ }
15
+ function errorMessage(code, message) {
16
+ return {
17
+ type: "@statelyai.error",
18
+ code,
19
+ message
20
+ };
21
+ }
22
+ function compactReplay(replay, message, max) {
23
+ const record = message;
24
+ const type = record.type;
25
+ if (type === "@statelyai.system.init") for (let index = replay.length - 1; index >= 0; index--) {
26
+ const replayType = replay[index].type;
27
+ if (replayType === "@statelyai.system.init" || replayType.startsWith("@statelyai.system.actor")) replay.splice(index, 1);
28
+ }
29
+ else if (type === "@statelyai.init") {
30
+ const index = replay.findIndex((entry) => entry.type === "@statelyai.init");
31
+ if (index >= 0) replay.splice(index, 1);
32
+ } else if (type === "@statelyai.system.actorSnapshot" || type === "@statelyai.inspectSnapshot") {
33
+ const actorId = record.actorId;
34
+ const index = replay.findIndex((entry) => {
35
+ const candidate = entry;
36
+ return candidate.type === type && candidate.actorId === actorId;
37
+ });
38
+ if (index >= 0) replay.splice(index, 1);
39
+ }
40
+ replay.push(message);
41
+ if (replay.length > max) replay.splice(0, replay.length - max);
42
+ }
43
+ /**
44
+ * Runtime-neutral WebSocket inspection room.
45
+ *
46
+ * It owns protocol registration, routing, multiplexed session membership, and
47
+ * bounded late-join replay. Node and Durable Object adapters only translate
48
+ * `send` effects to their native WebSocket APIs and persist `snapshot()`.
49
+ */
50
+ function createInspectionRelay(options = {}) {
51
+ const maxReplayMessages = options.maxReplayMessages ?? 200;
52
+ const maxReplaySessions = Math.max(1, options.maxReplaySessions ?? 100);
53
+ const peers = /* @__PURE__ */ new Map();
54
+ const sessions = new Map(Object.entries(options.snapshot?.sessions ?? {}).slice(-maxReplaySessions).map(([id, replay]) => [id, [...replay]]));
55
+ const send = (peerId, message) => ({
56
+ type: "send",
57
+ peerId,
58
+ message
59
+ });
60
+ function replayTo(peerId, sessionId) {
61
+ return (sessions.get(sessionId) ?? []).map((message) => send(peerId, message));
62
+ }
63
+ function oppositePeers(sender, sessionId) {
64
+ return Array.from(peers.values()).filter((peer) => peer.id !== sender.id && peer.role !== sender.role && peer.sessionIds.includes(sessionId));
65
+ }
66
+ function retainReplay(sessionId, replay) {
67
+ sessions.delete(sessionId);
68
+ sessions.set(sessionId, replay);
69
+ while (sessions.size > maxReplaySessions) {
70
+ const oldestSessionId = sessions.keys().next().value;
71
+ if (oldestSessionId === void 0) break;
72
+ sessions.delete(oldestSessionId);
73
+ }
74
+ }
75
+ return {
76
+ connect(peer) {
77
+ peers.set(peer.id, {
78
+ ...peer,
79
+ sessionIds: Array.from(new Set([peer.primarySessionId, ...peer.sessionIds]))
80
+ });
81
+ },
82
+ disconnect(peerId) {
83
+ peers.delete(peerId);
84
+ },
85
+ clear() {
86
+ peers.clear();
87
+ sessions.clear();
88
+ },
89
+ peer(peerId) {
90
+ const peer = peers.get(peerId);
91
+ return peer ? {
92
+ ...peer,
93
+ sessionIds: [...peer.sessionIds]
94
+ } : void 0;
95
+ },
96
+ receive(peerId, raw) {
97
+ const message = parseMessage(raw);
98
+ if (!message) return [];
99
+ if (message.type === "@statelyai.register") {
100
+ const role = message.role;
101
+ const sessionId = message.sessionId;
102
+ const protocolVersion = message.protocolVersion;
103
+ if (role !== "client" && role !== "viz" || typeof sessionId !== "string" || sessionId.length === 0) return [send(peerId, errorMessage("invalid_registration", "Invalid relay registration"))];
104
+ if (typeof protocolVersion === "number" && protocolVersion > INSPECTION_PROTOCOL_VERSION) return [send(peerId, errorMessage("unsupported_protocol", `Protocol ${protocolVersion} is not supported`))];
105
+ const peer = {
106
+ id: peerId,
107
+ role,
108
+ primarySessionId: sessionId,
109
+ sessionIds: [sessionId]
110
+ };
111
+ peers.set(peerId, peer);
112
+ const effects = [send(peerId, {
113
+ type: "@statelyai.registered",
114
+ protocolVersion: INSPECTION_PROTOCOL_VERSION,
115
+ sessionId
116
+ })];
117
+ if (role === "viz") effects.push(...replayTo(peerId, sessionId));
118
+ return effects;
119
+ }
120
+ const sender = peers.get(peerId);
121
+ if (!sender) return [send(peerId, errorMessage("not_registered", "Register before sending messages"))];
122
+ if (message.type === "@statelyai.addSession") {
123
+ const sessionId = message.sessionId;
124
+ if (typeof sessionId !== "string" || sessionId.length === 0) return [send(peerId, errorMessage("invalid_session", "Invalid session id"))];
125
+ if (!sender.sessionIds.includes(sessionId)) sender.sessionIds.push(sessionId);
126
+ const effects = [send(peerId, {
127
+ type: "@statelyai.sessionAdded",
128
+ sessionId
129
+ })];
130
+ if (sender.role === "viz") effects.push(...replayTo(peerId, sessionId));
131
+ return effects;
132
+ }
133
+ const sessionId = (typeof message.sessionId === "string" ? message.sessionId : void 0) ?? sender.primarySessionId;
134
+ if (!sender.sessionIds.includes(sessionId)) return [send(peerId, errorMessage("unknown_session", "Peer is not registered for session"))];
135
+ const stamped = {
136
+ ...message,
137
+ sessionId
138
+ };
139
+ if (sender.role === "client") {
140
+ const replay = sessions.get(sessionId) ?? [];
141
+ compactReplay(replay, stamped, maxReplayMessages);
142
+ retainReplay(sessionId, replay);
143
+ }
144
+ return oppositePeers(sender, sessionId).map((peer) => send(peer.id, stamped));
145
+ },
146
+ snapshot() {
147
+ return { sessions: Object.fromEntries(Array.from(sessions, ([sessionId, replay]) => [sessionId, [...replay]])) };
148
+ }
149
+ };
150
+ }
151
+
152
+ //#endregion
153
+ export { createInspectionRelay };
package/dist/studio.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as StatelyCredential, t as StatelyAuthMode } from "./auth-DO1-eQcU.mjs";
1
+ import { n as StatelyCredential, t as StatelyAuthMode } from "./auth-DmPZiERd.mjs";
2
2
 
3
3
  //#region src/studio.d.ts
4
4
  interface StudioClientOptions {
package/dist/sync.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { b as DigraphConfig, i as StatelyGraph } from "./graph-Dq5ycdSq.mjs";
1
+ import { b as DigraphConfig, i as StatelyGraph } from "./graph-R0sbxnZ7.mjs";
2
2
  import { CreateProjectInput, ProjectData, StudioClient, StudioMachineRecord, XStateVersion } from "./studio.mjs";
3
3
  import { GraphDiff } from "@statelyai/graph";
4
4
 
package/dist/sync.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createStatelyClient } from "./studio.mjs";
2
- import { d as xstateSourceToConfig, f as findStatelyPragmaAttachments, i as detectXStateSourceProfile, m as upsertStatelyPragma, p as getStatelyPragma, s as graphToXStateTS, t as INLINE_EXPRESSION_DIRECTIVE_FIELDS, u as serializeJS } from "./dist-C-AdA8Iw.mjs";
2
+ import { d as xstateSourceToConfig, f as findStatelyPragmaAttachments, i as detectXStateSourceProfile, m as upsertStatelyPragma, p as getStatelyPragma, s as graphToXStateTS, t as INLINE_EXPRESSION_DIRECTIVE_FIELDS, u as serializeJS } from "./dist-CHGcvOdQ.mjs";
3
3
  import { fromStudioMachine, toStudioMachine } from "./graph.mjs";
4
4
  import * as ts$1 from "typescript";
5
5
  import ts from "typescript";