@xstate-devtools/adapter 0.1.2 → 0.1.4
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/README.md +49 -0
- package/dist/chunk-3GM2SFT4.js +680 -0
- package/dist/chunk-3GM2SFT4.js.map +1 -0
- package/dist/chunk-MHHRMHW5.js +62 -0
- package/dist/chunk-MHHRMHW5.js.map +1 -0
- package/dist/index.cjs +753 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +790 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +12 -0
- package/dist/react.d.ts +12 -0
- package/dist/react.js +43 -0
- package/dist/react.js.map +1 -0
- package/dist/server.cjs +936 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +29 -0
- package/dist/server.d.ts +29 -0
- package/dist/server.js +235 -0
- package/dist/server.js.map +1 -0
- package/package.json +46 -6
- package/src/core.test.ts +0 -287
- package/src/core.ts +0 -667
- package/src/index.ts +0 -73
- package/src/logging.test.ts +0 -39
- package/src/logging.ts +0 -40
- package/src/react.tsx +0 -50
- package/src/sanitize.test.ts +0 -68
- package/src/sanitize.ts +0 -70
- package/src/serialize.test.ts +0 -170
- package/src/serialize.ts +0 -148
- package/src/server.ts +0 -272
- package/tsconfig.json +0 -14
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
createAdapter: () => createAdapter
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/logging.ts
|
|
28
|
+
function hasProcessEnv() {
|
|
29
|
+
return typeof process !== "undefined" && typeof process.env !== "undefined";
|
|
30
|
+
}
|
|
31
|
+
function isLoggingEnabled() {
|
|
32
|
+
if (globalThis.__XSTATE_DEVTOOLS_LOGGING__ === true) return true;
|
|
33
|
+
if (!hasProcessEnv()) return false;
|
|
34
|
+
const value = process.env.XSTATE_DEVTOOLS_LOGGING;
|
|
35
|
+
return value === "1" || value === "true";
|
|
36
|
+
}
|
|
37
|
+
function log(level, scope, message, details) {
|
|
38
|
+
if (!isLoggingEnabled()) return;
|
|
39
|
+
if (details === void 0) {
|
|
40
|
+
console[level](`[xstate-devtools:${scope}] ${message}`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
console[level](`[xstate-devtools:${scope}] ${message}`, details);
|
|
44
|
+
}
|
|
45
|
+
function debugLog(scope, message, details) {
|
|
46
|
+
log("debug", scope, message, details);
|
|
47
|
+
}
|
|
48
|
+
function infoLog(scope, message, details) {
|
|
49
|
+
log("info", scope, message, details);
|
|
50
|
+
}
|
|
51
|
+
function warnLog(scope, message, details) {
|
|
52
|
+
log("warn", scope, message, details);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/sanitize.ts
|
|
56
|
+
var MAX_DEPTH = 10;
|
|
57
|
+
var MAX_STRING_LENGTH = 500;
|
|
58
|
+
var MAX_ARRAY_LENGTH = 100;
|
|
59
|
+
function sanitizeValue(value, depth, seen) {
|
|
60
|
+
if (depth > MAX_DEPTH) return "[MaxDepth]";
|
|
61
|
+
if (value === null || value === void 0) return value;
|
|
62
|
+
if (typeof value === "boolean" || typeof value === "number") return value;
|
|
63
|
+
if (typeof value === "string") {
|
|
64
|
+
return value.length > MAX_STRING_LENGTH ? `${value.slice(0, MAX_STRING_LENGTH)}\u2026` : value;
|
|
65
|
+
}
|
|
66
|
+
if (typeof value === "function") return `[Function: ${value.name || "(anonymous)"}]`;
|
|
67
|
+
if (typeof value === "symbol") return `[Symbol: ${value.description ?? ""}]`;
|
|
68
|
+
if (typeof value === "bigint") return `[BigInt: ${value}]`;
|
|
69
|
+
if (value instanceof Error) return { __type: "Error", name: value.name, message: value.message };
|
|
70
|
+
if (value instanceof Date) return { __type: "Date", iso: value.toISOString() };
|
|
71
|
+
if (value instanceof RegExp) return { __type: "RegExp", source: value.source, flags: value.flags };
|
|
72
|
+
if (typeof value === "object") {
|
|
73
|
+
if (seen.has(value)) return "[Circular]";
|
|
74
|
+
seen.add(value);
|
|
75
|
+
}
|
|
76
|
+
if (value instanceof Map) {
|
|
77
|
+
const entries = [];
|
|
78
|
+
for (const [k, v] of value) {
|
|
79
|
+
if (entries.length >= MAX_ARRAY_LENGTH) break;
|
|
80
|
+
entries.push([sanitizeValue(k, depth + 1, seen), sanitizeValue(v, depth + 1, seen)]);
|
|
81
|
+
}
|
|
82
|
+
return { __type: "Map", entries };
|
|
83
|
+
}
|
|
84
|
+
if (value instanceof Set) {
|
|
85
|
+
const values = [];
|
|
86
|
+
for (const v of value) {
|
|
87
|
+
if (values.length >= MAX_ARRAY_LENGTH) break;
|
|
88
|
+
values.push(sanitizeValue(v, depth + 1, seen));
|
|
89
|
+
}
|
|
90
|
+
return { __type: "Set", values };
|
|
91
|
+
}
|
|
92
|
+
if (value instanceof Promise) return "[Promise]";
|
|
93
|
+
if (value instanceof WeakMap || value instanceof WeakSet) return "[WeakCollection]";
|
|
94
|
+
if (ArrayBuffer.isView(value)) return `[TypedArray: ${value.constructor.name}]`;
|
|
95
|
+
if (typeof Node !== "undefined" && value instanceof Node) {
|
|
96
|
+
return `[DOMNode: ${value.tagName ?? value.nodeName}]`;
|
|
97
|
+
}
|
|
98
|
+
if (Array.isArray(value)) {
|
|
99
|
+
const sliced = value.slice(0, MAX_ARRAY_LENGTH);
|
|
100
|
+
const result = sliced.map((v) => sanitizeValue(v, depth + 1, seen));
|
|
101
|
+
if (value.length > MAX_ARRAY_LENGTH) result.push(`[\u2026${value.length - MAX_ARRAY_LENGTH} more]`);
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
if (typeof value === "object") {
|
|
105
|
+
const result = {};
|
|
106
|
+
let count = 0;
|
|
107
|
+
for (const [k, v] of Object.entries(value)) {
|
|
108
|
+
if (count++ >= MAX_ARRAY_LENGTH) {
|
|
109
|
+
result["\u2026"] = "[truncated]";
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
result[k] = sanitizeValue(v, depth + 1, seen);
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
return String(value);
|
|
117
|
+
}
|
|
118
|
+
function sanitize(value, depth = 0) {
|
|
119
|
+
return sanitizeValue(value, depth, /* @__PURE__ */ new WeakSet());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/serialize.ts
|
|
123
|
+
var MAX_SERIALIZED_NODES = 500;
|
|
124
|
+
var MAX_TRANSITIONS_PER_NODE = 100;
|
|
125
|
+
var MAX_CHILD_STATES = 100;
|
|
126
|
+
var MAX_ACTIONS_PER_TRANSITION = 20;
|
|
127
|
+
var MAX_ENTRY_EXIT_ACTIONS = 20;
|
|
128
|
+
var MAX_INVOKES_PER_NODE = 20;
|
|
129
|
+
function serializeGuard(guard) {
|
|
130
|
+
if (!guard) return void 0;
|
|
131
|
+
if (typeof guard === "string") return guard;
|
|
132
|
+
if (typeof guard === "function") return guard.name || "(inline)";
|
|
133
|
+
if (typeof guard === "object" && guard !== null) {
|
|
134
|
+
const g = guard;
|
|
135
|
+
return g.type ?? g.name ?? "(inline)";
|
|
136
|
+
}
|
|
137
|
+
return "(inline)";
|
|
138
|
+
}
|
|
139
|
+
function serializeAction(action) {
|
|
140
|
+
if (typeof action === "string") return action;
|
|
141
|
+
if (typeof action === "function") return action.name || "(anonymous)";
|
|
142
|
+
if (typeof action === "object" && action !== null) {
|
|
143
|
+
const a = action;
|
|
144
|
+
return a.type ?? a.name ?? String(action);
|
|
145
|
+
}
|
|
146
|
+
return String(action);
|
|
147
|
+
}
|
|
148
|
+
function serializeTransitionList(transitions) {
|
|
149
|
+
return transitions.slice(0, MAX_TRANSITIONS_PER_NODE).map((t) => ({
|
|
150
|
+
eventType: t.eventType ?? "",
|
|
151
|
+
targets: (t.target ?? []).map((n) => n?.id ?? String(n)).filter(Boolean),
|
|
152
|
+
guard: serializeGuard(t.guard),
|
|
153
|
+
actions: (t.actions ?? []).slice(0, MAX_ACTIONS_PER_TRANSITION).map(serializeAction).filter(Boolean)
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
function serializeInvokes(node) {
|
|
157
|
+
return node.invoke.slice(0, MAX_INVOKES_PER_NODE).map((inv) => ({
|
|
158
|
+
id: inv.id ?? "(unknown)",
|
|
159
|
+
src: typeof inv.src === "string" ? inv.src : inv.src?.id ?? inv.src?.name ?? "(inline)"
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
function serializeNode(node, state) {
|
|
163
|
+
if (!node || typeof node !== "object") {
|
|
164
|
+
return {
|
|
165
|
+
id: "(unknown)",
|
|
166
|
+
key: "(unknown)",
|
|
167
|
+
type: "atomic",
|
|
168
|
+
states: {},
|
|
169
|
+
on: [],
|
|
170
|
+
always: [],
|
|
171
|
+
entry: [],
|
|
172
|
+
exit: [],
|
|
173
|
+
invoke: []
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
if (state.count >= MAX_SERIALIZED_NODES) {
|
|
177
|
+
return {
|
|
178
|
+
id: node.id ?? "(truncated)",
|
|
179
|
+
key: node.key ?? "(truncated)",
|
|
180
|
+
type: node.type ?? "atomic",
|
|
181
|
+
states: {},
|
|
182
|
+
on: [],
|
|
183
|
+
always: [],
|
|
184
|
+
entry: [],
|
|
185
|
+
exit: [],
|
|
186
|
+
invoke: []
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (state.seen.has(node)) {
|
|
190
|
+
return {
|
|
191
|
+
id: node.id ?? "(circular)",
|
|
192
|
+
key: node.key ?? "(circular)",
|
|
193
|
+
type: node.type ?? "atomic",
|
|
194
|
+
states: {},
|
|
195
|
+
on: [],
|
|
196
|
+
always: [],
|
|
197
|
+
entry: [],
|
|
198
|
+
exit: [],
|
|
199
|
+
invoke: []
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
state.seen.add(node);
|
|
203
|
+
state.count += 1;
|
|
204
|
+
const allTransitions = [];
|
|
205
|
+
if (node.transitions instanceof Map) {
|
|
206
|
+
for (const [, tList] of node.transitions) {
|
|
207
|
+
if (allTransitions.length >= MAX_TRANSITIONS_PER_NODE) break;
|
|
208
|
+
allTransitions.push(...serializeTransitionList(tList));
|
|
209
|
+
if (allTransitions.length >= MAX_TRANSITIONS_PER_NODE) {
|
|
210
|
+
allTransitions.length = MAX_TRANSITIONS_PER_NODE;
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const always = Array.isArray(node.always) ? serializeTransitionList(node.always) : [];
|
|
216
|
+
const childEntries = Object.entries(node.states ?? {}).slice(0, MAX_CHILD_STATES);
|
|
217
|
+
return {
|
|
218
|
+
id: node.id,
|
|
219
|
+
key: node.key,
|
|
220
|
+
type: node.type,
|
|
221
|
+
initial: node.initial?.target?.[0]?.key,
|
|
222
|
+
states: Object.fromEntries(childEntries.map(([k, v]) => [k, serializeNode(v, state)])),
|
|
223
|
+
on: allTransitions,
|
|
224
|
+
always,
|
|
225
|
+
entry: (node.entry ?? []).slice(0, MAX_ENTRY_EXIT_ACTIONS).map(serializeAction).filter(Boolean),
|
|
226
|
+
exit: (node.exit ?? []).slice(0, MAX_ENTRY_EXIT_ACTIONS).map(serializeAction).filter(Boolean),
|
|
227
|
+
invoke: serializeInvokes(node),
|
|
228
|
+
sourceLocation: node.config?.__xstateDevtoolsSource ?? void 0,
|
|
229
|
+
description: node.config?.description ?? void 0
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function serializeMachine(machine, sourceLocation) {
|
|
233
|
+
return {
|
|
234
|
+
id: machine.id,
|
|
235
|
+
root: serializeNode(machine.root, { seen: /* @__PURE__ */ new WeakSet(), count: 0 }),
|
|
236
|
+
sourceLocation
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/core.ts
|
|
241
|
+
function summarizeMessage(message) {
|
|
242
|
+
const summary = { type: message.type };
|
|
243
|
+
if ("sessionId" in message) summary.sessionId = message.sessionId;
|
|
244
|
+
if ("stateNodeId" in message) summary.stateNodeId = message.stateNodeId;
|
|
245
|
+
if ("parentSessionId" in message && message.parentSessionId) {
|
|
246
|
+
summary.parentSessionId = message.parentSessionId;
|
|
247
|
+
}
|
|
248
|
+
if ("globalSeq" in message) summary.globalSeq = message.globalSeq;
|
|
249
|
+
if ("timestamp" in message) summary.timestamp = message.timestamp;
|
|
250
|
+
if ("event" in message && message.event && typeof message.event === "object" && "type" in message.event) {
|
|
251
|
+
summary.eventType = message.event.type;
|
|
252
|
+
}
|
|
253
|
+
return summary;
|
|
254
|
+
}
|
|
255
|
+
function summarizeInspectionEvent(event) {
|
|
256
|
+
return {
|
|
257
|
+
type: event?.type,
|
|
258
|
+
sessionId: event?.actorRef?.sessionId,
|
|
259
|
+
eventType: event?.type === "@xstate.event" && event?.event && typeof event.event === "object" ? event.event.type : void 0
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function debugLog2(source, message, details) {
|
|
263
|
+
debugLog(`${source}:adapter`, message, details);
|
|
264
|
+
}
|
|
265
|
+
function infoLog2(source, message, details) {
|
|
266
|
+
infoLog(`${source}:adapter`, message, details);
|
|
267
|
+
}
|
|
268
|
+
function warnLog2(source, message, details) {
|
|
269
|
+
warnLog(`${source}:adapter`, message, details);
|
|
270
|
+
}
|
|
271
|
+
function isLibraryStackFrame(line) {
|
|
272
|
+
const normalized = line.replace(/\\/g, "/").toLowerCase();
|
|
273
|
+
return normalized.includes("/node_modules/xstate/") || normalized.includes("/node_modules/@xstate/") || normalized.includes("/@xstate-devtools/adapter/") || normalized.includes("/packages/adapter/");
|
|
274
|
+
}
|
|
275
|
+
function normalizeStackFrame(line) {
|
|
276
|
+
return line.trim().replace(/^at\s+/, "");
|
|
277
|
+
}
|
|
278
|
+
function extractStackFrameLocation(frame) {
|
|
279
|
+
return frame.match(/\((.*)\)$/)?.[1] ?? frame;
|
|
280
|
+
}
|
|
281
|
+
function isAnonymousOrEvalLocation(location) {
|
|
282
|
+
const normalized = location.trim().toLowerCase().replace(/^[./\\]+/, "");
|
|
283
|
+
return normalized === "<anonymous>" || normalized === "anonymous" || normalized === "(anonymous)" || normalized === "eval" || normalized === "<eval>" || normalized === "[native code]";
|
|
284
|
+
}
|
|
285
|
+
function hasFilesystemBackedPath(location) {
|
|
286
|
+
const trimmed = location.trim();
|
|
287
|
+
if (!trimmed) return false;
|
|
288
|
+
const match = trimmed.match(/^(.*?)(?::\d+)?(?::\d+)?$/);
|
|
289
|
+
const rawPath = (match?.[1] ?? trimmed).trim();
|
|
290
|
+
if (!rawPath || isAnonymousOrEvalLocation(rawPath)) return false;
|
|
291
|
+
if (/^[a-zA-Z]:[\\/]/.test(rawPath)) return true;
|
|
292
|
+
if (rawPath.startsWith("/") || rawPath.startsWith("./") || rawPath.startsWith("../")) return true;
|
|
293
|
+
if (/^[a-zA-Z][a-zA-Z\d+.-]*:\/\//.test(rawPath)) {
|
|
294
|
+
try {
|
|
295
|
+
const url = new URL(rawPath);
|
|
296
|
+
if (url.protocol === "file:") return true;
|
|
297
|
+
return url.pathname.startsWith("/@fs/");
|
|
298
|
+
} catch {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
function remapWebUrlLocationToFsPath(location, source, options) {
|
|
305
|
+
if (source !== "web" || !options?.webSourceRoot) return location;
|
|
306
|
+
const match = location.trim().match(/^(.*?)(?::(\d+))?(?::(\d+))?$/);
|
|
307
|
+
if (!match) return location;
|
|
308
|
+
const [, rawPath, line, column] = match;
|
|
309
|
+
if (!rawPath || !/^https?:\/\//.test(rawPath)) return location;
|
|
310
|
+
try {
|
|
311
|
+
const url = new URL(rawPath);
|
|
312
|
+
const pathname = decodeURIComponent(url.pathname);
|
|
313
|
+
if (!pathname.startsWith("/app/")) return location;
|
|
314
|
+
const root = options.webSourceRoot.replace(/\/+$/, "");
|
|
315
|
+
const filePath = `${root}${pathname}`;
|
|
316
|
+
const suffix = line ? `:${line}${column ? `:${column}` : ""}` : "";
|
|
317
|
+
return `${filePath}${suffix}`;
|
|
318
|
+
} catch {
|
|
319
|
+
return location;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function getSourceLocationFromStack(stack, source = "web", options) {
|
|
323
|
+
const lines = stack?.split("\n") ?? [];
|
|
324
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
325
|
+
if (i <= 2) continue;
|
|
326
|
+
const line = lines[i];
|
|
327
|
+
if (isLibraryStackFrame(line)) continue;
|
|
328
|
+
const normalizedFrame = normalizeStackFrame(line);
|
|
329
|
+
const location = remapWebUrlLocationToFsPath(
|
|
330
|
+
extractStackFrameLocation(normalizedFrame),
|
|
331
|
+
source,
|
|
332
|
+
options
|
|
333
|
+
);
|
|
334
|
+
if (!hasFilesystemBackedPath(location)) continue;
|
|
335
|
+
const wrapped = normalizedFrame.match(/\((.*)\)$/);
|
|
336
|
+
if (wrapped) {
|
|
337
|
+
return normalizedFrame.replace(wrapped[1], location);
|
|
338
|
+
}
|
|
339
|
+
return location;
|
|
340
|
+
}
|
|
341
|
+
return void 0;
|
|
342
|
+
}
|
|
343
|
+
function getSourceLocation(source, options) {
|
|
344
|
+
try {
|
|
345
|
+
const oldLimit = Error.stackTraceLimit;
|
|
346
|
+
Error.stackTraceLimit = 50;
|
|
347
|
+
const stack = new Error().stack;
|
|
348
|
+
Error.stackTraceLimit = oldLimit;
|
|
349
|
+
return getSourceLocationFromStack(stack, source, options);
|
|
350
|
+
} catch {
|
|
351
|
+
return void 0;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
function serializeSnapshot(snapshot) {
|
|
355
|
+
return {
|
|
356
|
+
value: snapshot?.value ?? null,
|
|
357
|
+
context: sanitize(snapshot?.context),
|
|
358
|
+
status: snapshot?.status ?? "active",
|
|
359
|
+
error: snapshot?.error ? sanitize(snapshot.error) : void 0
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function safeSerializeSnapshot(actorRef) {
|
|
363
|
+
try {
|
|
364
|
+
return serializeSnapshot(actorRef.getSnapshot());
|
|
365
|
+
} catch {
|
|
366
|
+
return { value: null, context: void 0, status: "active" };
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function getActorDisplayName(actorRef) {
|
|
370
|
+
const actor = actorRef;
|
|
371
|
+
const src = actor.src ?? actor.logic?.src;
|
|
372
|
+
if (typeof src === "string" && src.length > 0) return src;
|
|
373
|
+
if (typeof actor.logic?.id === "string" && actor.logic.id.length > 0) return actor.logic.id;
|
|
374
|
+
if (typeof actor.logic?.name === "string" && actor.logic.name.length > 0) return actor.logic.name;
|
|
375
|
+
if (src && typeof src === "object") {
|
|
376
|
+
const namedSrc = src;
|
|
377
|
+
if (typeof namedSrc.id === "string" && namedSrc.id.length > 0) return namedSrc.id;
|
|
378
|
+
if (typeof namedSrc.name === "string" && namedSrc.name.length > 0) return namedSrc.name;
|
|
379
|
+
}
|
|
380
|
+
return void 0;
|
|
381
|
+
}
|
|
382
|
+
function getNodeInitialChild(node) {
|
|
383
|
+
if (!node.states) return null;
|
|
384
|
+
if (typeof node.initial === "string") {
|
|
385
|
+
return node.states[node.initial] ?? null;
|
|
386
|
+
}
|
|
387
|
+
const target = Array.isArray(node.initial?.target) ? node.initial.target[0] : null;
|
|
388
|
+
return target ?? null;
|
|
389
|
+
}
|
|
390
|
+
function encodeChildValue(child, childValue) {
|
|
391
|
+
if (child.type === "atomic" || child.type === "final" || child.type === "history") {
|
|
392
|
+
return child.key;
|
|
393
|
+
}
|
|
394
|
+
return { [child.key]: childValue };
|
|
395
|
+
}
|
|
396
|
+
function getDefaultStateValue(node) {
|
|
397
|
+
if (node.type === "parallel") {
|
|
398
|
+
const value = {};
|
|
399
|
+
for (const child of Object.values(node.states ?? {})) {
|
|
400
|
+
value[child.key] = getDefaultSelectionValue(child);
|
|
401
|
+
}
|
|
402
|
+
return value;
|
|
403
|
+
}
|
|
404
|
+
const initialChild = getNodeInitialChild(node);
|
|
405
|
+
if (!initialChild) return {};
|
|
406
|
+
return encodeChildValue(initialChild, getDefaultStateValue(initialChild));
|
|
407
|
+
}
|
|
408
|
+
function getDefaultSelectionValue(node) {
|
|
409
|
+
if (node.type === "atomic" || node.type === "final" || node.type === "history") {
|
|
410
|
+
return node.key;
|
|
411
|
+
}
|
|
412
|
+
return getDefaultStateValue(node);
|
|
413
|
+
}
|
|
414
|
+
function getExistingChildValue(value, childKey) {
|
|
415
|
+
if (!value || typeof value !== "object") return void 0;
|
|
416
|
+
return value[childKey];
|
|
417
|
+
}
|
|
418
|
+
function getPathToRoot(target, root) {
|
|
419
|
+
const path = [];
|
|
420
|
+
let current = target;
|
|
421
|
+
while (current) {
|
|
422
|
+
path.unshift(current);
|
|
423
|
+
if (current.id === root.id) return path;
|
|
424
|
+
current = current.parent;
|
|
425
|
+
}
|
|
426
|
+
throw new Error(`State node '${target.id}' is not part of machine '${root.id}'`);
|
|
427
|
+
}
|
|
428
|
+
function buildTargetStateValue(node, path, currentValue) {
|
|
429
|
+
const [, ...restPath] = path;
|
|
430
|
+
if (restPath.length === 0) {
|
|
431
|
+
if (node.type === "parallel") {
|
|
432
|
+
const next = {};
|
|
433
|
+
for (const child2 of Object.values(node.states ?? {})) {
|
|
434
|
+
next[child2.key] = getExistingChildValue(currentValue, child2.key) ?? getDefaultSelectionValue(child2);
|
|
435
|
+
}
|
|
436
|
+
return next;
|
|
437
|
+
}
|
|
438
|
+
if (node.type === "compound") {
|
|
439
|
+
return getDefaultStateValue(node);
|
|
440
|
+
}
|
|
441
|
+
return node.key;
|
|
442
|
+
}
|
|
443
|
+
const child = restPath[0];
|
|
444
|
+
if (node.type === "parallel") {
|
|
445
|
+
const next = {};
|
|
446
|
+
for (const sibling of Object.values(node.states ?? {})) {
|
|
447
|
+
if (sibling.key === child.key) {
|
|
448
|
+
next[sibling.key] = buildTargetStateValue(
|
|
449
|
+
sibling,
|
|
450
|
+
restPath,
|
|
451
|
+
getExistingChildValue(currentValue, sibling.key)
|
|
452
|
+
);
|
|
453
|
+
} else {
|
|
454
|
+
next[sibling.key] = getExistingChildValue(currentValue, sibling.key) ?? getDefaultSelectionValue(sibling);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return next;
|
|
458
|
+
}
|
|
459
|
+
const childValue = buildTargetStateValue(
|
|
460
|
+
child,
|
|
461
|
+
restPath,
|
|
462
|
+
getExistingChildValue(currentValue, child.key)
|
|
463
|
+
);
|
|
464
|
+
return encodeChildValue(child, childValue);
|
|
465
|
+
}
|
|
466
|
+
function setActiveState(actorRef, stateNodeId) {
|
|
467
|
+
const mutableActorRef = actorRef;
|
|
468
|
+
const machine = mutableActorRef.logic;
|
|
469
|
+
if (!machine?.getStateNodeById || !machine.resolveState || typeof mutableActorRef.update !== "function") {
|
|
470
|
+
throw new Error("Actor does not expose machine state mutation internals");
|
|
471
|
+
}
|
|
472
|
+
const currentSnapshot = actorRef.getSnapshot();
|
|
473
|
+
const targetNode = machine.getStateNodeById(stateNodeId);
|
|
474
|
+
const path = getPathToRoot(targetNode, machine.root);
|
|
475
|
+
const targetValue = buildTargetStateValue(machine.root, path, currentSnapshot?.value);
|
|
476
|
+
const nextSnapshot = machine.resolveState({
|
|
477
|
+
value: targetValue,
|
|
478
|
+
context: currentSnapshot?.context,
|
|
479
|
+
status: currentSnapshot?.status,
|
|
480
|
+
output: currentSnapshot?.output,
|
|
481
|
+
error: currentSnapshot?.error,
|
|
482
|
+
historyValue: currentSnapshot?.historyValue
|
|
483
|
+
});
|
|
484
|
+
mutableActorRef.update(nextSnapshot, {
|
|
485
|
+
type: "xstate.devtools.set-active-state",
|
|
486
|
+
stateNodeId
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
var SEQ_KEY = "__xstate_devtools_global_seq__";
|
|
490
|
+
function nextSeq() {
|
|
491
|
+
const g = globalThis;
|
|
492
|
+
const cur = g[SEQ_KEY] ?? 0;
|
|
493
|
+
const next = cur + 1;
|
|
494
|
+
g[SEQ_KEY] = next;
|
|
495
|
+
return next;
|
|
496
|
+
}
|
|
497
|
+
function createInspector(transport, source, options = {}) {
|
|
498
|
+
const actorRefs = /* @__PURE__ */ new Map();
|
|
499
|
+
const actorMachines = /* @__PURE__ */ new Map();
|
|
500
|
+
const prefix = `${source}:`;
|
|
501
|
+
const tag = (sessionId) => prefix + sessionId;
|
|
502
|
+
const tagOptional = (id) => id ? prefix + id : void 0;
|
|
503
|
+
const stripIfMine = (id) => id.startsWith(prefix) ? id.slice(prefix.length) : null;
|
|
504
|
+
function checkAndNotifyStop(actorRef) {
|
|
505
|
+
let snap;
|
|
506
|
+
try {
|
|
507
|
+
snap = actorRef.getSnapshot();
|
|
508
|
+
} catch {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
if (snap?.status !== "active") {
|
|
512
|
+
const message = {
|
|
513
|
+
type: "XSTATE_ACTOR_STOPPED",
|
|
514
|
+
sessionId: tag(actorRef.sessionId)
|
|
515
|
+
};
|
|
516
|
+
debugLog2(source, "actor stopped; notifying transport", summarizeMessage(message));
|
|
517
|
+
transport.send(message);
|
|
518
|
+
actorRefs.delete(actorRef.sessionId);
|
|
519
|
+
actorMachines.delete(actorRef.sessionId);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
const unsubscribe = transport.subscribe((message) => {
|
|
523
|
+
debugLog2(source, "received message from transport", summarizeMessage(message));
|
|
524
|
+
if (message.type === "XSTATE_PANEL_CONNECTED") {
|
|
525
|
+
infoLog2(source, "panel connected; resyncing actors", { actorCount: actorRefs.size });
|
|
526
|
+
actorRefs.forEach((actorRef, sessionId) => {
|
|
527
|
+
const machine = actorMachines.get(sessionId) ?? null;
|
|
528
|
+
const resyncMessage = {
|
|
529
|
+
type: "XSTATE_ACTOR_REGISTERED",
|
|
530
|
+
sessionId: tag(sessionId),
|
|
531
|
+
parentSessionId: tagOptional(actorRef._parent?.sessionId),
|
|
532
|
+
displayName: getActorDisplayName(actorRef),
|
|
533
|
+
machine,
|
|
534
|
+
snapshot: safeSerializeSnapshot(actorRef),
|
|
535
|
+
globalSeq: nextSeq(),
|
|
536
|
+
timestamp: Date.now()
|
|
537
|
+
};
|
|
538
|
+
debugLog2(source, "resyncing actor", summarizeMessage(resyncMessage));
|
|
539
|
+
transport.send(resyncMessage);
|
|
540
|
+
});
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (message.type === "XSTATE_DISPATCH") {
|
|
544
|
+
const local = stripIfMine(message.sessionId);
|
|
545
|
+
if (local === null) {
|
|
546
|
+
debugLog2(source, "ignoring dispatch for different source", summarizeMessage(message));
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const ref = actorRefs.get(local);
|
|
550
|
+
if (ref) {
|
|
551
|
+
try {
|
|
552
|
+
debugLog2(source, "dispatching event to actor", {
|
|
553
|
+
sessionId: local,
|
|
554
|
+
eventType: message.event && typeof message.event === "object" && "type" in message.event ? message.event.type : void 0
|
|
555
|
+
});
|
|
556
|
+
ref.send(message.event);
|
|
557
|
+
} catch (e) {
|
|
558
|
+
warnLog2(source, "dispatch error", { error: e, sessionId: local });
|
|
559
|
+
}
|
|
560
|
+
} else {
|
|
561
|
+
warnLog2(source, "received dispatch for unknown actor", {
|
|
562
|
+
sessionId: local,
|
|
563
|
+
knownActors: actorRefs.size
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
if (message.type === "XSTATE_SET_ACTIVE_STATE") {
|
|
569
|
+
const local = stripIfMine(message.sessionId);
|
|
570
|
+
if (local === null) {
|
|
571
|
+
debugLog2(
|
|
572
|
+
source,
|
|
573
|
+
"ignoring state activation for different source",
|
|
574
|
+
summarizeMessage(message)
|
|
575
|
+
);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
const ref = actorRefs.get(local);
|
|
579
|
+
if (!ref) {
|
|
580
|
+
warnLog2(source, "received state activation for unknown actor", {
|
|
581
|
+
sessionId: local,
|
|
582
|
+
knownActors: actorRefs.size
|
|
583
|
+
});
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
debugLog2(source, "setting active state on actor", summarizeMessage(message));
|
|
588
|
+
setActiveState(ref, message.stateNodeId);
|
|
589
|
+
const snapshotMessage = {
|
|
590
|
+
type: "XSTATE_SNAPSHOT",
|
|
591
|
+
sessionId: tag(local),
|
|
592
|
+
snapshot: safeSerializeSnapshot(ref),
|
|
593
|
+
timestamp: Date.now(),
|
|
594
|
+
globalSeq: nextSeq()
|
|
595
|
+
};
|
|
596
|
+
debugLog2(
|
|
597
|
+
source,
|
|
598
|
+
"sending snapshot after state activation",
|
|
599
|
+
summarizeMessage(snapshotMessage)
|
|
600
|
+
);
|
|
601
|
+
transport.send(snapshotMessage);
|
|
602
|
+
} catch (error) {
|
|
603
|
+
warnLog2(source, "failed to set active state", {
|
|
604
|
+
error,
|
|
605
|
+
sessionId: local,
|
|
606
|
+
stateNodeId: message.stateNodeId
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
transport.send({ type: "XSTATE_ADAPTER_READY" });
|
|
612
|
+
infoLog2(source, "inspector created");
|
|
613
|
+
const inspect = (inspectionEvent) => {
|
|
614
|
+
debugLog2(source, "inspect callback invoked", summarizeInspectionEvent(inspectionEvent));
|
|
615
|
+
if (inspectionEvent.type === "@xstate.actor") {
|
|
616
|
+
const actorRef = inspectionEvent.actorRef;
|
|
617
|
+
const sessionId = actorRef.sessionId;
|
|
618
|
+
const actorLogic = actorRef.logic;
|
|
619
|
+
const machine = actorLogic?.root ? serializeMachine(
|
|
620
|
+
actorLogic,
|
|
621
|
+
actorLogic.config?.__xstateDevtoolsSource ?? getSourceLocation(source, options)
|
|
622
|
+
) : null;
|
|
623
|
+
actorRefs.set(sessionId, actorRef);
|
|
624
|
+
actorMachines.set(sessionId, machine);
|
|
625
|
+
const notifyStop = () => {
|
|
626
|
+
if (actorRefs.has(sessionId)) {
|
|
627
|
+
actorRefs.delete(sessionId);
|
|
628
|
+
actorMachines.delete(sessionId);
|
|
629
|
+
const stopMsg = {
|
|
630
|
+
type: "XSTATE_ACTOR_STOPPED",
|
|
631
|
+
sessionId: tag(sessionId)
|
|
632
|
+
};
|
|
633
|
+
debugLog2(source, "actor stopped; notifying transport", summarizeMessage(stopMsg));
|
|
634
|
+
transport.send(stopMsg);
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
try {
|
|
638
|
+
actorRef.subscribe({ complete: notifyStop, error: notifyStop });
|
|
639
|
+
} catch {
|
|
640
|
+
}
|
|
641
|
+
const message = {
|
|
642
|
+
type: "XSTATE_ACTOR_REGISTERED",
|
|
643
|
+
sessionId: tag(sessionId),
|
|
644
|
+
parentSessionId: tagOptional(actorRef._parent?.sessionId),
|
|
645
|
+
displayName: getActorDisplayName(actorRef),
|
|
646
|
+
machine,
|
|
647
|
+
snapshot: safeSerializeSnapshot(actorRef),
|
|
648
|
+
globalSeq: nextSeq(),
|
|
649
|
+
timestamp: Date.now()
|
|
650
|
+
};
|
|
651
|
+
infoLog2(source, "registering actor with transport", {
|
|
652
|
+
message: summarizeMessage(message),
|
|
653
|
+
actorCount: actorRefs.size,
|
|
654
|
+
hasMachine: machine !== null
|
|
655
|
+
});
|
|
656
|
+
transport.send(message);
|
|
657
|
+
} else if (inspectionEvent.type === "@xstate.snapshot") {
|
|
658
|
+
const message = {
|
|
659
|
+
type: "XSTATE_SNAPSHOT",
|
|
660
|
+
sessionId: tag(inspectionEvent.actorRef.sessionId),
|
|
661
|
+
snapshot: serializeSnapshot(inspectionEvent.snapshot),
|
|
662
|
+
timestamp: Date.now(),
|
|
663
|
+
globalSeq: nextSeq()
|
|
664
|
+
};
|
|
665
|
+
debugLog2(source, "sending snapshot to transport", summarizeMessage(message));
|
|
666
|
+
transport.send(message);
|
|
667
|
+
checkAndNotifyStop(inspectionEvent.actorRef);
|
|
668
|
+
} else if (inspectionEvent.type === "@xstate.event") {
|
|
669
|
+
const message = {
|
|
670
|
+
type: "XSTATE_EVENT",
|
|
671
|
+
sessionId: tag(inspectionEvent.actorRef.sessionId),
|
|
672
|
+
// sanitize() returns unknown; the inspected event keeps its { type, ... }
|
|
673
|
+
// shape, so it is a SerializedEvent after deep-sanitizing.
|
|
674
|
+
event: sanitize(inspectionEvent.event),
|
|
675
|
+
snapshotAfter: safeSerializeSnapshot(inspectionEvent.actorRef),
|
|
676
|
+
timestamp: Date.now(),
|
|
677
|
+
globalSeq: nextSeq()
|
|
678
|
+
};
|
|
679
|
+
debugLog2(source, "sending event to transport", summarizeMessage(message));
|
|
680
|
+
transport.send(message);
|
|
681
|
+
checkAndNotifyStop(inspectionEvent.actorRef);
|
|
682
|
+
} else {
|
|
683
|
+
debugLog2(
|
|
684
|
+
source,
|
|
685
|
+
"ignoring unsupported inspection event",
|
|
686
|
+
summarizeInspectionEvent(inspectionEvent)
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
function dispose() {
|
|
691
|
+
infoLog2(source, "disposing inspector", { actorCount: actorRefs.size });
|
|
692
|
+
unsubscribe();
|
|
693
|
+
actorRefs.clear();
|
|
694
|
+
actorMachines.clear();
|
|
695
|
+
}
|
|
696
|
+
return { inspect, dispose };
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// src/index.ts
|
|
700
|
+
function createAdapter(options = {}) {
|
|
701
|
+
if (typeof window === "undefined") {
|
|
702
|
+
infoLog("web:adapter", "createAdapter called without window; returning no-op adapter");
|
|
703
|
+
return { inspect: () => {
|
|
704
|
+
}, dispose: () => {
|
|
705
|
+
} };
|
|
706
|
+
}
|
|
707
|
+
infoLog("web:adapter", "creating browser adapter", {
|
|
708
|
+
hookInstalled: Boolean(window.__XSTATE_DEVTOOLS__)
|
|
709
|
+
});
|
|
710
|
+
let warnedMissingHook = false;
|
|
711
|
+
const transport = {
|
|
712
|
+
send(message) {
|
|
713
|
+
const payload = { ...message, __xstateDevtools: true };
|
|
714
|
+
debugLog("web:adapter", "sending message via page hook", {
|
|
715
|
+
type: message.type,
|
|
716
|
+
sessionId: "sessionId" in message ? message.sessionId : void 0
|
|
717
|
+
});
|
|
718
|
+
if (window.__XSTATE_DEVTOOLS__) {
|
|
719
|
+
window.__XSTATE_DEVTOOLS__.send(payload);
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
if (!warnedMissingHook) {
|
|
723
|
+
warnedMissingHook = true;
|
|
724
|
+
warnLog("web:adapter", "page hook missing; using direct window.postMessage fallback");
|
|
725
|
+
}
|
|
726
|
+
window.postMessage(payload, "*");
|
|
727
|
+
},
|
|
728
|
+
subscribe(handler) {
|
|
729
|
+
infoLog("web:adapter", "subscribing to window messages");
|
|
730
|
+
const onMessage = (evt) => {
|
|
731
|
+
if (evt.source !== window) return;
|
|
732
|
+
const data = evt.data;
|
|
733
|
+
if (!data?.__xstateDevtools) return;
|
|
734
|
+
debugLog("web:adapter", "received message from window bridge", {
|
|
735
|
+
type: data.type,
|
|
736
|
+
sessionId: "sessionId" in data ? data.sessionId : void 0
|
|
737
|
+
});
|
|
738
|
+
handler(data);
|
|
739
|
+
};
|
|
740
|
+
window.addEventListener("message", onMessage);
|
|
741
|
+
return () => {
|
|
742
|
+
infoLog("web:adapter", "unsubscribing from window messages");
|
|
743
|
+
window.removeEventListener("message", onMessage);
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
return createInspector(transport, "web", options);
|
|
748
|
+
}
|
|
749
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
750
|
+
0 && (module.exports = {
|
|
751
|
+
createAdapter
|
|
752
|
+
});
|
|
753
|
+
//# sourceMappingURL=index.cjs.map
|