@statelyai/sdk 0.12.2 → 0.13.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/inspect.mjs CHANGED
@@ -1,306 +1,3 @@
1
- import { i as createPendingExportManager, n as createWebSocketTransport, r as createEventRegistry } from "./transport-D0nlFaLn.mjs";
1
+ import { n as createStatelyInspector, t as createInspector } from "./inspect-D9GyHTTW.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 };
@@ -29,6 +29,7 @@ interface EditorCapabilities {
29
29
  panels?: string[];
30
30
  commands?: string[];
31
31
  }
32
+ type ReadOnlyReason = "free-plan" | "access-unverified";
32
33
  type CapabilityDeniedCapability = "edit" | "export" | "ai" | "simulate" | "inspect" | "navigateHierarchy" | "panel" | "mode";
33
34
  interface CapabilityDeniedEvent {
34
35
  capability: CapabilityDeniedCapability;
@@ -178,6 +179,7 @@ interface MachineInitOptions {
178
179
  mode?: EmbedMode;
179
180
  theme?: "light" | "dark";
180
181
  readOnly?: boolean;
182
+ readOnlyReason?: ReadOnlyReason;
181
183
  depth?: number;
182
184
  capabilities?: EditorCapabilities;
183
185
  panels?: {
@@ -233,6 +235,7 @@ interface InitMessage {
233
235
  mode?: EmbedMode;
234
236
  theme?: "light" | "dark";
235
237
  readOnly?: boolean;
238
+ readOnlyReason?: ReadOnlyReason;
236
239
  depth?: number;
237
240
  capabilities?: EditorCapabilities;
238
241
  leftPanels?: string[];
@@ -335,8 +338,29 @@ interface ActorSystemEntry {
335
338
  /** Optional current snapshot (`{value, status, ...}`). */
336
339
  snapshot?: unknown;
337
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
+ }
338
361
  interface SystemInitMessage {
339
362
  type: "@statelyai.system.init";
363
+ protocolVersion?: number;
340
364
  /** All known actors at the moment the system is first streamed. */
341
365
  actors: ActorSystemEntry[];
342
366
  /** Actor id the viz should focus first. Defaults to the root. */
@@ -364,10 +388,18 @@ interface SystemActorRegisteredMessage {
364
388
  interface SystemActorSnapshotMessage {
365
389
  type: "@statelyai.system.actorSnapshot";
366
390
  actorId: string;
391
+ /** Snapshot before the event was processed. */
392
+ previousSnapshot?: unknown | null;
367
393
  snapshot: unknown;
368
394
  event?: unknown | null;
369
395
  /** The actorId of the actor that sent the triggering event, if known. */
370
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[];
371
403
  }
372
404
  /** An actor reached a final state / was stopped. */
373
405
  interface SystemActorStoppedMessage {
@@ -465,6 +497,7 @@ type ClientMessage = InitMessage | SetApiKeyMessage | ProjectInitMessage | Proje
465
497
  type VizMessage = ReadyMessage | LoadedMessage | ChangeMessage | SaveMessage | ProjectMachineSelectedMessage | RetrievedMessage | ErrorMessage | CapabilityDeniedMessage | UploadRequestMessage;
466
498
  interface RegisterMessage {
467
499
  type: "@statelyai.register";
500
+ protocolVersion?: number;
468
501
  role: "client" | "viz";
469
502
  sessionId: string;
470
503
  metadata?: {
@@ -474,10 +507,7 @@ interface RegisterMessage {
474
507
  }
475
508
  interface RegisteredMessage {
476
509
  type: "@statelyai.registered";
477
- sessionId: string;
478
- }
479
- interface RequestOpenMessage {
480
- type: "@statelyai.requestOpen";
510
+ protocolVersion?: number;
481
511
  sessionId: string;
482
512
  }
483
513
  /**
@@ -505,9 +535,11 @@ interface SessionAddedMessage {
505
535
  sessionId: string;
506
536
  }
507
537
  /** All session-management messages for the WS relay. */
508
- type SessionMessage = RegisterMessage | RegisteredMessage | RequestOpenMessage | AddSessionMessage | SessionAddedMessage;
538
+ type SessionMessage = RegisterMessage | RegisteredMessage | AddSessionMessage | SessionAddedMessage;
509
539
  /** Any valid protocol message. */
510
540
  type ProtocolMessage = ClientMessage | VizMessage | SessionMessage;
511
541
  declare const PREFIX = "@statelyai.";
542
+ /** Current WebSocket inspection protocol version. */
543
+ declare const INSPECTION_PROTOCOL_VERSION = 1;
512
544
  //#endregion
513
- 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, 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";