@statelyai/sdk 0.18.0 → 0.20.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/README.md +33 -2
- package/dist/auth.d.mts +1 -1
- package/dist/editor-sync-types-D8_EZm0A.d.mts +94 -0
- package/dist/editorHostProtocol-D_h4y3rX.mjs +5 -0
- package/dist/editorHostSession-CZHd22pZ.mjs +478 -0
- package/dist/editorHostSession-jnC5mQDN.d.mts +184 -0
- package/dist/editorHostSession.d.mts +2 -0
- package/dist/editorHostSession.mjs +4 -0
- package/dist/embed.d.mts +1 -1
- package/dist/embed.mjs +31 -3
- package/dist/{graph-DuOeLGvh.d.mts → graph-DxeqWHC3.d.mts} +1 -1
- package/dist/graph.d.mts +1 -1
- package/dist/index.d.mts +7 -6
- package/dist/index.mjs +7 -5
- package/dist/{inspect-BZFzyeew.d.mts → inspect-CGJR4iZK.d.mts} +13 -3
- package/dist/{inspect-llcqYpZ1.mjs → inspect-CWwLGwLq.mjs} +58 -31
- package/dist/inspect.d.mts +1 -1
- package/dist/inspect.mjs +2 -2
- package/dist/patchTypes.d.mts +1 -1
- package/dist/protocol.d.mts +44 -50
- package/dist/protocol.mjs +2 -2
- package/dist/{studio-txdyJHD7.mjs → schemas-C2raUv-n.mjs} +1 -396
- package/dist/{statelyPragma-Bm9O4XV6.mjs → statelyPragma-GN-bfuC_.mjs} +1 -1
- package/dist/{studio-Bk_u41gu.d.mts → studio-DLduJRSp.d.mts} +2 -2
- package/dist/studio-EKd7cQN7.mjs +397 -0
- package/dist/studio.d.mts +1 -1
- package/dist/studio.mjs +1 -1
- package/dist/sync.d.mts +2 -2
- package/dist/sync.mjs +2 -2
- package/dist/{transport-CyCrdKyz.mjs → version-TY9EZF0F.mjs} +14 -4
- package/package.json +6 -2
- /package/dist/{auth-2Thaw42j.d.mts → auth-B0ZTKri2.d.mts} +0 -0
- /package/dist/{index-ag6K2iAS.d.mts → index-CKFbG_Ix.d.mts} +0 -0
package/README.md
CHANGED
|
@@ -74,6 +74,17 @@ embed.on('capabilityDenied', (event) => {
|
|
|
74
74
|
Use `access-unverified` when reopening or signing in, rather than upgrading, is
|
|
75
75
|
the appropriate recovery action.
|
|
76
76
|
|
|
77
|
+
### Editor hosts
|
|
78
|
+
|
|
79
|
+
<!-- shared editor host session and protocol from src/editorHostSession.ts and src/editorHostProtocol.ts -->
|
|
80
|
+
|
|
81
|
+
CLI and editor-extension hosts use `EditorHostSession` from
|
|
82
|
+
`@statelyai/sdk/editor-host` for initialization, saves, source refreshes,
|
|
83
|
+
clipboard requests, and editor protocol negotiation. Use
|
|
84
|
+
`createRemoteEditorSyncBackend` for two-way validation of the versioned Editor
|
|
85
|
+
Sync HTTP contract.
|
|
86
|
+
Hosts keep ownership of files, credentials, UI, and transport adapters.
|
|
87
|
+
|
|
77
88
|
Key capabilities:
|
|
78
89
|
|
|
79
90
|
- **Mount or attach** to any container element or existing iframe
|
|
@@ -186,13 +197,33 @@ const inspector = createInspector({
|
|
|
186
197
|
});
|
|
187
198
|
```
|
|
188
199
|
|
|
200
|
+
For persisted actors, `resolveSessionId` can keep one producer-local identity
|
|
201
|
+
across sequential restore/resume incarnations. Generate a new id for a new
|
|
202
|
+
logical invocation, persist it beside the actor snapshot, and reuse it only
|
|
203
|
+
after the previous incarnation has stopped. Create a new inspector for the
|
|
204
|
+
restored actor system, reusing the same room and producer identities:
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
const inspector = createInspector({
|
|
208
|
+
roomId: persistedInspectionRoomId,
|
|
209
|
+
producerId: persistedInspectionProducerId,
|
|
210
|
+
resolveSessionId: (actor, defaultSessionId) =>
|
|
211
|
+
actor.logic === machine ? persistedInspectionId : defaultSessionId,
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
A resume must reuse the same room id, producer id, and resolved session id. Keep
|
|
216
|
+
the room id private because it is the room's bearer capability. A new logical
|
|
217
|
+
invocation keeps the producer id but receives a new resolved session id.
|
|
218
|
+
|
|
189
219
|
Stopped actors remain in `inspector.actors` and in reconnect checkpoints, so
|
|
190
220
|
completed request-scoped runs stay available for inspection.
|
|
191
221
|
|
|
192
222
|
XState v5 and v6 inspection events normalize to independent actor, event,
|
|
193
223
|
snapshot, and stop observations before crossing the WebSocket protocol.
|
|
194
|
-
`actorId` is system-local
|
|
195
|
-
`producerId + sessionId` as the global
|
|
224
|
+
`actorId` is system-local. `sessionId` defaults to XState's runtime value and
|
|
225
|
+
may be producer-resolved. A room uses `producerId + sessionId` as the global
|
|
226
|
+
actor identity.
|
|
196
227
|
Observations captured before the first transport registration replay in order
|
|
197
228
|
after the initial actor-tree baseline. Reconnects use the latest checkpoint.
|
|
198
229
|
In Node, the inspector opens in the default browser automatically. The SDK
|
package/dist/auth.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as getCredentialToken, n as StatelyCredential, r as createAuthorizationHeader, t as StatelyAuthMode } from "./auth-
|
|
1
|
+
import { i as getCredentialToken, n as StatelyCredential, r as createAuthorizationHeader, t as StatelyAuthMode } from "./auth-B0ZTKri2.mjs";
|
|
2
2
|
export { StatelyAuthMode, StatelyCredential, createAuthorizationHeader, getCredentialToken };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
//#region ../api-contract/dist/editor-sync-types.d.mts
|
|
2
|
+
//#region src/editor-sync-types.d.ts
|
|
3
|
+
interface EditorCapabilities {
|
|
4
|
+
edit?: boolean;
|
|
5
|
+
export?: boolean;
|
|
6
|
+
ai?: boolean;
|
|
7
|
+
simulate?: boolean;
|
|
8
|
+
inspect?: boolean;
|
|
9
|
+
navigateHierarchy?: boolean;
|
|
10
|
+
maxDepth?: number;
|
|
11
|
+
panels?: string[];
|
|
12
|
+
commands?: string[];
|
|
13
|
+
}
|
|
14
|
+
interface EditorSyncAccess {
|
|
15
|
+
level: 'full' | 'restricted';
|
|
16
|
+
readOnly: boolean;
|
|
17
|
+
readOnlyReason?: 'free-plan' | 'access-unverified';
|
|
18
|
+
capabilities: EditorCapabilities;
|
|
19
|
+
}
|
|
20
|
+
interface EditorSyncSourceRange {
|
|
21
|
+
startLine: number;
|
|
22
|
+
startChar: number;
|
|
23
|
+
endLine: number;
|
|
24
|
+
endChar: number;
|
|
25
|
+
}
|
|
26
|
+
interface EditorSyncSourceLocation {
|
|
27
|
+
uri: string;
|
|
28
|
+
range: EditorSyncSourceRange;
|
|
29
|
+
}
|
|
30
|
+
interface EditorSyncStateSourceLocation extends EditorSyncSourceLocation {
|
|
31
|
+
path: string[];
|
|
32
|
+
kind: 'inline' | 'objectReference' | 'createStateConfig';
|
|
33
|
+
symbol?: string;
|
|
34
|
+
keyRange?: EditorSyncSourceRange;
|
|
35
|
+
referenceRange?: EditorSyncSourceRange;
|
|
36
|
+
keySource?: {
|
|
37
|
+
kind: 'const' | 'enumMember' | 'computedLiteral';
|
|
38
|
+
symbol?: string;
|
|
39
|
+
valueRange?: EditorSyncSourceRange;
|
|
40
|
+
machineLocal?: boolean;
|
|
41
|
+
};
|
|
42
|
+
keyReplacementText?: string;
|
|
43
|
+
}
|
|
44
|
+
interface MachineSourceLocations {
|
|
45
|
+
version: 1;
|
|
46
|
+
profile?: 'xstate-v5' | 'xstate-v6-alpha';
|
|
47
|
+
root?: EditorSyncSourceLocation;
|
|
48
|
+
statesObject?: EditorSyncStateSourceLocation;
|
|
49
|
+
states: EditorSyncStateSourceLocation[];
|
|
50
|
+
staticValues?: Array<EditorSyncSourceLocation & {
|
|
51
|
+
value: string;
|
|
52
|
+
}>;
|
|
53
|
+
transitionFunctions?: Array<EditorSyncSourceLocation & {
|
|
54
|
+
symbol: string;
|
|
55
|
+
sourcePaths: string[][];
|
|
56
|
+
}>;
|
|
57
|
+
}
|
|
58
|
+
interface EditorSyncSourceDocument {
|
|
59
|
+
uri: string;
|
|
60
|
+
fileName: string;
|
|
61
|
+
text: string;
|
|
62
|
+
}
|
|
63
|
+
interface EditorSyncDocumentContext {
|
|
64
|
+
rootUri: string;
|
|
65
|
+
rootFileName: string;
|
|
66
|
+
rootDir: string;
|
|
67
|
+
documents: EditorSyncSourceDocument[];
|
|
68
|
+
}
|
|
69
|
+
type EditorSyncParseRequest = EditorSyncDocumentContext;
|
|
70
|
+
interface EditorSyncParseResponse {
|
|
71
|
+
machine: unknown;
|
|
72
|
+
format: string;
|
|
73
|
+
sourceLocations?: MachineSourceLocations;
|
|
74
|
+
access?: EditorSyncAccess;
|
|
75
|
+
}
|
|
76
|
+
interface EditorSyncApplyRequest extends EditorSyncDocumentContext {
|
|
77
|
+
machineConfig?: unknown;
|
|
78
|
+
patches?: unknown[];
|
|
79
|
+
prevGraph?: unknown;
|
|
80
|
+
nextGraph?: unknown;
|
|
81
|
+
sourceLocations?: MachineSourceLocations;
|
|
82
|
+
serialized?: Record<string, string>;
|
|
83
|
+
preserveStatelyMetadata?: boolean;
|
|
84
|
+
}
|
|
85
|
+
interface EditorSyncTextReplacement {
|
|
86
|
+
uri?: string;
|
|
87
|
+
range: EditorSyncSourceRange;
|
|
88
|
+
newText: string;
|
|
89
|
+
}
|
|
90
|
+
interface EditorSyncApplyResponse {
|
|
91
|
+
replacements: EditorSyncTextReplacement[];
|
|
92
|
+
} //#endregion
|
|
93
|
+
//#endregion
|
|
94
|
+
export { EditorSyncParseRequest as a, EditorSyncSourceLocation as c, EditorSyncTextReplacement as d, MachineSourceLocations as f, EditorSyncApplyResponse as i, EditorSyncSourceRange as l, EditorSyncAccess as n, EditorSyncParseResponse as o, EditorSyncApplyRequest as r, EditorSyncSourceDocument as s, EditorCapabilities as t, EditorSyncStateSourceLocation as u };
|
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import { t as EDITOR_PROTOCOL_VERSION } from "./editorHostProtocol-D_h4y3rX.mjs";
|
|
2
|
+
import { a as literal, c as record, d as unknown, l as string, n as array, o as number, r as boolean, s as object, t as _enum } from "./schemas-C2raUv-n.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../api-contract/dist/editor-sync.mjs
|
|
5
|
+
const EDITOR_SYNC_PROTOCOL_VERSION = 1;
|
|
6
|
+
const EDITOR_SYNC_PROTOCOL_HEADER = "X-Stately-Editor-Sync-Version";
|
|
7
|
+
const editorCapabilitiesSchema = object({
|
|
8
|
+
edit: boolean().optional(),
|
|
9
|
+
export: boolean().optional(),
|
|
10
|
+
ai: boolean().optional(),
|
|
11
|
+
simulate: boolean().optional(),
|
|
12
|
+
inspect: boolean().optional(),
|
|
13
|
+
navigateHierarchy: boolean().optional(),
|
|
14
|
+
maxDepth: number().optional(),
|
|
15
|
+
panels: array(string()).optional(),
|
|
16
|
+
commands: array(string()).optional()
|
|
17
|
+
}).passthrough();
|
|
18
|
+
const editorSyncAccessSchema = object({
|
|
19
|
+
level: _enum(["full", "restricted"]),
|
|
20
|
+
readOnly: boolean(),
|
|
21
|
+
readOnlyReason: _enum(["free-plan", "access-unverified"]).optional(),
|
|
22
|
+
capabilities: editorCapabilitiesSchema
|
|
23
|
+
}).passthrough();
|
|
24
|
+
const editorSyncSourceRangeSchema = object({
|
|
25
|
+
startLine: number(),
|
|
26
|
+
startChar: number(),
|
|
27
|
+
endLine: number(),
|
|
28
|
+
endChar: number()
|
|
29
|
+
});
|
|
30
|
+
const editorSyncSourceLocationSchema = object({
|
|
31
|
+
uri: string(),
|
|
32
|
+
range: editorSyncSourceRangeSchema
|
|
33
|
+
});
|
|
34
|
+
const editorSyncStateSourceLocationSchema = editorSyncSourceLocationSchema.extend({
|
|
35
|
+
path: array(string()),
|
|
36
|
+
kind: _enum([
|
|
37
|
+
"inline",
|
|
38
|
+
"objectReference",
|
|
39
|
+
"createStateConfig"
|
|
40
|
+
]),
|
|
41
|
+
symbol: string().optional(),
|
|
42
|
+
keyRange: editorSyncSourceRangeSchema.optional(),
|
|
43
|
+
referenceRange: editorSyncSourceRangeSchema.optional(),
|
|
44
|
+
keySource: object({
|
|
45
|
+
kind: _enum([
|
|
46
|
+
"const",
|
|
47
|
+
"enumMember",
|
|
48
|
+
"computedLiteral"
|
|
49
|
+
]),
|
|
50
|
+
symbol: string().optional(),
|
|
51
|
+
valueRange: editorSyncSourceRangeSchema.optional(),
|
|
52
|
+
machineLocal: boolean().optional()
|
|
53
|
+
}).optional(),
|
|
54
|
+
keyReplacementText: string().optional()
|
|
55
|
+
});
|
|
56
|
+
const machineSourceLocationsSchema = object({
|
|
57
|
+
version: literal(1),
|
|
58
|
+
profile: _enum(["xstate-v5", "xstate-v6-alpha"]).optional(),
|
|
59
|
+
root: editorSyncSourceLocationSchema.optional(),
|
|
60
|
+
statesObject: editorSyncStateSourceLocationSchema.optional(),
|
|
61
|
+
states: array(editorSyncStateSourceLocationSchema),
|
|
62
|
+
staticValues: array(editorSyncSourceLocationSchema.extend({ value: string() })).optional(),
|
|
63
|
+
transitionFunctions: array(editorSyncSourceLocationSchema.extend({
|
|
64
|
+
symbol: string(),
|
|
65
|
+
sourcePaths: array(array(string()))
|
|
66
|
+
})).optional()
|
|
67
|
+
}).passthrough();
|
|
68
|
+
const editorSyncSourceDocumentSchema = object({
|
|
69
|
+
uri: string(),
|
|
70
|
+
fileName: string(),
|
|
71
|
+
text: string()
|
|
72
|
+
});
|
|
73
|
+
const editorSyncDocumentContextSchema = object({
|
|
74
|
+
rootUri: string(),
|
|
75
|
+
rootFileName: string(),
|
|
76
|
+
rootDir: string(),
|
|
77
|
+
documents: array(editorSyncSourceDocumentSchema)
|
|
78
|
+
});
|
|
79
|
+
const editorSyncParseResponseSchema = object({
|
|
80
|
+
machine: unknown(),
|
|
81
|
+
format: string(),
|
|
82
|
+
sourceLocations: machineSourceLocationsSchema.optional(),
|
|
83
|
+
access: editorSyncAccessSchema.optional()
|
|
84
|
+
}).passthrough();
|
|
85
|
+
const editorSyncApplyRequestSchema = editorSyncDocumentContextSchema.extend({
|
|
86
|
+
machineConfig: unknown().optional(),
|
|
87
|
+
patches: array(unknown()).optional(),
|
|
88
|
+
prevGraph: unknown().optional(),
|
|
89
|
+
nextGraph: unknown().optional(),
|
|
90
|
+
sourceLocations: machineSourceLocationsSchema.optional(),
|
|
91
|
+
serialized: record(string(), string()).optional(),
|
|
92
|
+
preserveStatelyMetadata: boolean().optional()
|
|
93
|
+
}).passthrough();
|
|
94
|
+
const editorSyncTextReplacementSchema = object({
|
|
95
|
+
uri: string().optional(),
|
|
96
|
+
range: editorSyncSourceRangeSchema,
|
|
97
|
+
newText: string()
|
|
98
|
+
});
|
|
99
|
+
const editorSyncApplyResponseSchema = object({ replacements: array(editorSyncTextReplacementSchema) });
|
|
100
|
+
const editorSyncAccessResponseSchema = object({ access: editorSyncAccessSchema });
|
|
101
|
+
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/editorHostSession.ts
|
|
104
|
+
function createRemoteEditorSyncBackend(options) {
|
|
105
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
106
|
+
async function post(pathname, body) {
|
|
107
|
+
const url = new URL(pathname, `${options.editorUrl.replace(/\/+$/, "")}/`);
|
|
108
|
+
const headers = {
|
|
109
|
+
"Content-Type": "application/json",
|
|
110
|
+
[EDITOR_SYNC_PROTOCOL_HEADER]: String(EDITOR_SYNC_PROTOCOL_VERSION)
|
|
111
|
+
};
|
|
112
|
+
if (options.apiKey) headers.Authorization = `Bearer ${options.apiKey}`;
|
|
113
|
+
const response = await fetchImpl(url, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers,
|
|
116
|
+
body: JSON.stringify(body)
|
|
117
|
+
});
|
|
118
|
+
assertEditorSyncResponseProtocol(response);
|
|
119
|
+
const data = await response.json().catch(() => null);
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
const message = data && typeof data === "object" && "error" in data && typeof data.error === "string" ? data.error : `HTTP ${response.status}`;
|
|
122
|
+
throw new Error(message);
|
|
123
|
+
}
|
|
124
|
+
return data;
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
async parse(request) {
|
|
128
|
+
return editorSyncParseResponseSchema.parse(await post("/api/editor-sync/parse", request));
|
|
129
|
+
},
|
|
130
|
+
async apply(request) {
|
|
131
|
+
return editorSyncApplyResponseSchema.parse(await post("/api/editor-sync/apply", request));
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
async function fetchEditorSyncAccess(options) {
|
|
136
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
137
|
+
const url = new URL("/api/editor-sync/access", `${options.editorUrl.replace(/\/+$/, "")}/`);
|
|
138
|
+
const headers = { [EDITOR_SYNC_PROTOCOL_HEADER]: String(EDITOR_SYNC_PROTOCOL_VERSION) };
|
|
139
|
+
if (options.apiKey) headers.Authorization = `Bearer ${options.apiKey}`;
|
|
140
|
+
const response = await fetchImpl(url, {
|
|
141
|
+
method: "GET",
|
|
142
|
+
headers
|
|
143
|
+
});
|
|
144
|
+
assertEditorSyncResponseProtocol(response);
|
|
145
|
+
const data = await response.json().catch(() => null);
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
const message = data && typeof data === "object" && "error" in data && typeof data.error === "string" ? data.error : `HTTP ${response.status}`;
|
|
148
|
+
throw new Error(message);
|
|
149
|
+
}
|
|
150
|
+
return editorSyncAccessResponseSchema.parse(data).access;
|
|
151
|
+
}
|
|
152
|
+
function assertEditorSyncResponseProtocol(response) {
|
|
153
|
+
const selectedVersion = response.headers.get(EDITOR_SYNC_PROTOCOL_HEADER);
|
|
154
|
+
if (selectedVersion === null || selectedVersion === String(EDITOR_SYNC_PROTOCOL_VERSION)) return;
|
|
155
|
+
throw new Error(`Editor Sync protocol ${selectedVersion} is unsupported; this client supports ${EDITOR_SYNC_PROTOCOL_VERSION}.`);
|
|
156
|
+
}
|
|
157
|
+
var EditorHostSession = class {
|
|
158
|
+
editorProtocolState = "awaiting";
|
|
159
|
+
currentGraph;
|
|
160
|
+
lastPersistedGraph;
|
|
161
|
+
currentFormat;
|
|
162
|
+
currentSourceLocations;
|
|
163
|
+
access;
|
|
164
|
+
watchedSourceUris = /* @__PURE__ */ new Set();
|
|
165
|
+
selfEditCount = 0;
|
|
166
|
+
watcher;
|
|
167
|
+
retrieveRequestCount = 0;
|
|
168
|
+
pendingRetrievals = /* @__PURE__ */ new Map();
|
|
169
|
+
constructor(options) {
|
|
170
|
+
this.options = options;
|
|
171
|
+
this.watcher = options.source.watchSavedDocuments?.((uri) => this.handleSavedDocument(uri));
|
|
172
|
+
}
|
|
173
|
+
dispose() {
|
|
174
|
+
this.watcher?.dispose();
|
|
175
|
+
for (const pending of this.pendingRetrievals.values()) pending.reject(/* @__PURE__ */ new Error("Editor session disposed before export completed."));
|
|
176
|
+
this.pendingRetrievals.clear();
|
|
177
|
+
}
|
|
178
|
+
notifySavedDocument(uri = this.options.source.rootUri) {
|
|
179
|
+
return this.handleSavedDocument(uri);
|
|
180
|
+
}
|
|
181
|
+
async receive(message) {
|
|
182
|
+
if (message.type !== "@statelyai.ready" && this.editorProtocolState !== "compatible") return;
|
|
183
|
+
switch (message.type) {
|
|
184
|
+
case "@statelyai.ready":
|
|
185
|
+
if (this.editorProtocolState === "incompatible") break;
|
|
186
|
+
if (message.protocolVersion !== void 0 && message.protocolVersion !== EDITOR_PROTOCOL_VERSION) {
|
|
187
|
+
this.editorProtocolState = "incompatible";
|
|
188
|
+
this.options.send({
|
|
189
|
+
type: "@statelyai.error",
|
|
190
|
+
code: "unsupported_protocol",
|
|
191
|
+
message: `Editor protocol ${message.protocolVersion} is unsupported; this client supports ${EDITOR_PROTOCOL_VERSION}.`
|
|
192
|
+
});
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
this.editorProtocolState = "compatible";
|
|
196
|
+
try {
|
|
197
|
+
if (message.protocolVersion !== void 0) this.options.send({
|
|
198
|
+
type: "@statelyai.handshake",
|
|
199
|
+
protocolVersion: EDITOR_PROTOCOL_VERSION,
|
|
200
|
+
client: this.options.client
|
|
201
|
+
});
|
|
202
|
+
await this.initialize();
|
|
203
|
+
} catch (error) {
|
|
204
|
+
const message = formatError(error, "Failed to initialize the visual editor.");
|
|
205
|
+
this.options.send({
|
|
206
|
+
type: "@statelyai.error",
|
|
207
|
+
code: "initialization_failed",
|
|
208
|
+
message
|
|
209
|
+
});
|
|
210
|
+
this.options.showError?.(message);
|
|
211
|
+
}
|
|
212
|
+
break;
|
|
213
|
+
case "@statelyai.loaded":
|
|
214
|
+
this.currentGraph = message.graph;
|
|
215
|
+
this.lastPersistedGraph = message.graph;
|
|
216
|
+
this.currentSourceLocations = mergeSourceLocations(this.currentSourceLocations, message.sourceLocations);
|
|
217
|
+
break;
|
|
218
|
+
case "@statelyai.change":
|
|
219
|
+
this.currentGraph = message.graph;
|
|
220
|
+
this.currentSourceLocations = mergeSourceLocations(this.currentSourceLocations, message.sourceLocations);
|
|
221
|
+
break;
|
|
222
|
+
case "@statelyai.save":
|
|
223
|
+
try {
|
|
224
|
+
await this.save(message);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
const text = formatError(error, "Failed to apply Viz changes.");
|
|
227
|
+
this.options.send({
|
|
228
|
+
type: "@statelyai.toast",
|
|
229
|
+
message: text,
|
|
230
|
+
toastType: "error"
|
|
231
|
+
});
|
|
232
|
+
this.options.showError?.(text);
|
|
233
|
+
}
|
|
234
|
+
break;
|
|
235
|
+
case "@statelyai.retrieved":
|
|
236
|
+
this.resolveRetrieved(message.requestId, message.data);
|
|
237
|
+
break;
|
|
238
|
+
case "@statelyai.error":
|
|
239
|
+
if (message.requestId) {
|
|
240
|
+
const pending = this.pendingRetrievals.get(message.requestId);
|
|
241
|
+
if (pending) {
|
|
242
|
+
this.pendingRetrievals.delete(message.requestId);
|
|
243
|
+
pending.reject(new Error(message.message));
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
this.options.showError?.(message.message);
|
|
248
|
+
break;
|
|
249
|
+
case "@statelyai.clipboardRead": {
|
|
250
|
+
let text = "";
|
|
251
|
+
try {
|
|
252
|
+
text = await this.options.readClipboard?.() ?? "";
|
|
253
|
+
} catch {}
|
|
254
|
+
this.options.send({
|
|
255
|
+
type: "@statelyai.clipboardReadResponse",
|
|
256
|
+
requestId: message.requestId,
|
|
257
|
+
text
|
|
258
|
+
});
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
case "@statelyai.clipboardWrite": {
|
|
262
|
+
let ok = false;
|
|
263
|
+
try {
|
|
264
|
+
await this.options.writeClipboard?.(message.text);
|
|
265
|
+
ok = Boolean(this.options.writeClipboard);
|
|
266
|
+
} catch {}
|
|
267
|
+
this.options.send({
|
|
268
|
+
type: "@statelyai.clipboardWriteResponse",
|
|
269
|
+
requestId: message.requestId,
|
|
270
|
+
ok
|
|
271
|
+
});
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
async initialize() {
|
|
277
|
+
const documents = await this.options.source.collectDocuments();
|
|
278
|
+
const update = await this.options.sync.parse({
|
|
279
|
+
rootUri: this.options.source.rootUri,
|
|
280
|
+
rootFileName: this.options.source.rootFileName,
|
|
281
|
+
rootDir: this.options.source.rootDir,
|
|
282
|
+
documents
|
|
283
|
+
});
|
|
284
|
+
this.currentFormat = update.format;
|
|
285
|
+
this.currentSourceLocations = update.sourceLocations;
|
|
286
|
+
this.access = update.access ?? this.options.access;
|
|
287
|
+
this.updateWatchedSourceUris(update.sourceLocations);
|
|
288
|
+
if (this.options.apiKey) this.options.send({
|
|
289
|
+
type: "@statelyai.setApiKey",
|
|
290
|
+
apiKey: this.options.apiKey
|
|
291
|
+
});
|
|
292
|
+
this.options.send({
|
|
293
|
+
type: "@statelyai.init",
|
|
294
|
+
machine: update.machine,
|
|
295
|
+
format: update.format,
|
|
296
|
+
sourceLocations: update.sourceLocations,
|
|
297
|
+
mode: this.access?.readOnly ? "viewing" : "editing",
|
|
298
|
+
readOnly: this.access?.readOnly,
|
|
299
|
+
readOnlyReason: this.access?.readOnlyReason,
|
|
300
|
+
capabilities: this.access?.capabilities,
|
|
301
|
+
theme: this.options.getTheme?.() ?? "light",
|
|
302
|
+
unsavedIndicator: this.options.init?.unsavedIndicator ?? { enabled: true },
|
|
303
|
+
settings: this.options.init?.settings,
|
|
304
|
+
leftPanels: this.options.init?.leftPanels ?? [],
|
|
305
|
+
rightPanels: this.options.init?.rightPanels ?? [],
|
|
306
|
+
activePanels: this.options.init?.activePanels ?? []
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
async save(message) {
|
|
310
|
+
this.currentSourceLocations = mergeSourceLocations(this.currentSourceLocations, message.sourceLocations);
|
|
311
|
+
this.updateWatchedSourceUris(this.currentSourceLocations);
|
|
312
|
+
if (this.access?.readOnly) {
|
|
313
|
+
const text = "Your Stately plan allows read-only editor sync.";
|
|
314
|
+
this.options.send({
|
|
315
|
+
type: "@statelyai.toast",
|
|
316
|
+
message: text,
|
|
317
|
+
toastType: "error"
|
|
318
|
+
});
|
|
319
|
+
this.options.showError?.(text);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const validationErrors = (message.validations ?? []).filter((validation) => validation.level === "error");
|
|
323
|
+
if (validationErrors.length > 0) {
|
|
324
|
+
const text = formatValidationErrorMessage(validationErrors);
|
|
325
|
+
this.options.send({
|
|
326
|
+
type: "@statelyai.toast",
|
|
327
|
+
message: text,
|
|
328
|
+
toastType: "error"
|
|
329
|
+
});
|
|
330
|
+
this.options.showError?.(text);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const previousGraph = this.lastPersistedGraph ?? this.currentGraph;
|
|
334
|
+
if (!message.patches?.length && graphsEqual(previousGraph, message.graph)) {
|
|
335
|
+
this.currentGraph = message.graph;
|
|
336
|
+
this.lastPersistedGraph = message.graph;
|
|
337
|
+
this.options.send({
|
|
338
|
+
type: "@statelyai.saved",
|
|
339
|
+
graph: message.graph
|
|
340
|
+
});
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const documents = await this.options.source.collectDocuments(sourceLocationUris(this.currentSourceLocations));
|
|
344
|
+
const serialized = await this.getSerializedPayload();
|
|
345
|
+
const response = await this.options.sync.apply({
|
|
346
|
+
rootUri: this.options.source.rootUri,
|
|
347
|
+
rootFileName: this.options.source.rootFileName,
|
|
348
|
+
rootDir: this.options.source.rootDir,
|
|
349
|
+
documents,
|
|
350
|
+
machineConfig: message.machineConfig,
|
|
351
|
+
patches: message.patches,
|
|
352
|
+
prevGraph: previousGraph,
|
|
353
|
+
nextGraph: message.graph,
|
|
354
|
+
sourceLocations: this.currentSourceLocations,
|
|
355
|
+
serialized,
|
|
356
|
+
preserveStatelyMetadata: this.options.source.getPreserveStatelyMetadata?.(this.options.source.rootUri) ?? true
|
|
357
|
+
});
|
|
358
|
+
if (response.replacements.length === 0) {
|
|
359
|
+
const text = "Unable to apply Viz changes to the source file automatically.";
|
|
360
|
+
this.options.send({
|
|
361
|
+
type: "@statelyai.toast",
|
|
362
|
+
message: text,
|
|
363
|
+
toastType: "error"
|
|
364
|
+
});
|
|
365
|
+
this.options.showError?.(text);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
this.selfEditCount++;
|
|
369
|
+
try {
|
|
370
|
+
const touchedUris = await this.options.source.applyReplacements(response.replacements, this.options.source.rootUri);
|
|
371
|
+
await this.options.source.saveDocuments?.(touchedUris);
|
|
372
|
+
} finally {
|
|
373
|
+
this.selfEditCount--;
|
|
374
|
+
}
|
|
375
|
+
this.currentGraph = message.graph;
|
|
376
|
+
this.lastPersistedGraph = message.graph;
|
|
377
|
+
this.options.send({
|
|
378
|
+
type: "@statelyai.saved",
|
|
379
|
+
graph: message.graph
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
async handleSavedDocument(uri) {
|
|
383
|
+
if (this.editorProtocolState !== "compatible") return;
|
|
384
|
+
if (uri !== this.options.source.rootUri && !this.watchedSourceUris.has(uri) || this.selfEditCount > 0) return;
|
|
385
|
+
try {
|
|
386
|
+
const documents = await this.options.source.collectDocuments(sourceLocationUris(this.currentSourceLocations));
|
|
387
|
+
const update = await this.options.sync.parse({
|
|
388
|
+
rootUri: this.options.source.rootUri,
|
|
389
|
+
rootFileName: this.options.source.rootFileName,
|
|
390
|
+
rootDir: this.options.source.rootDir,
|
|
391
|
+
documents
|
|
392
|
+
});
|
|
393
|
+
this.currentFormat = update.format;
|
|
394
|
+
this.currentSourceLocations = update.sourceLocations;
|
|
395
|
+
this.access = update.access ?? this.access;
|
|
396
|
+
this.updateWatchedSourceUris(update.sourceLocations);
|
|
397
|
+
this.options.send({
|
|
398
|
+
type: "@statelyai.update",
|
|
399
|
+
machine: update.machine,
|
|
400
|
+
format: update.format,
|
|
401
|
+
sourceLocations: update.sourceLocations
|
|
402
|
+
});
|
|
403
|
+
} catch (error) {
|
|
404
|
+
this.options.showError?.(formatError(error, "Failed to read the saved document."));
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
updateWatchedSourceUris(sourceLocations) {
|
|
408
|
+
this.watchedSourceUris.clear();
|
|
409
|
+
for (const uri of stateSourceLocationUris(sourceLocations)) if (uri !== this.options.source.rootUri) this.watchedSourceUris.add(uri);
|
|
410
|
+
}
|
|
411
|
+
async getSerializedPayload() {
|
|
412
|
+
const format = this.currentFormat;
|
|
413
|
+
if (!format || format === "xstate") return void 0;
|
|
414
|
+
try {
|
|
415
|
+
return { [format]: await this.requestSerialized(format) };
|
|
416
|
+
} catch {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
requestSerialized(format) {
|
|
421
|
+
const requestId = `retrieve-${++this.retrieveRequestCount}`;
|
|
422
|
+
return new Promise((resolve, reject) => {
|
|
423
|
+
this.pendingRetrievals.set(requestId, {
|
|
424
|
+
resolve,
|
|
425
|
+
reject
|
|
426
|
+
});
|
|
427
|
+
this.options.send({
|
|
428
|
+
type: "@statelyai.retrieve",
|
|
429
|
+
requestId,
|
|
430
|
+
format
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
resolveRetrieved(requestId, data) {
|
|
435
|
+
const pending = this.pendingRetrievals.get(requestId);
|
|
436
|
+
if (!pending) return;
|
|
437
|
+
this.pendingRetrievals.delete(requestId);
|
|
438
|
+
pending.resolve(typeof data === "string" ? data : JSON.stringify(data, null, 2));
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
function graphsEqual(left, right) {
|
|
442
|
+
if (left === void 0 || right === void 0) return false;
|
|
443
|
+
try {
|
|
444
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
445
|
+
} catch {
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function mergeSourceLocations(previous, next) {
|
|
450
|
+
if (!next) return previous;
|
|
451
|
+
if (!next.profile && previous?.profile) return {
|
|
452
|
+
...next,
|
|
453
|
+
profile: previous.profile
|
|
454
|
+
};
|
|
455
|
+
return next;
|
|
456
|
+
}
|
|
457
|
+
function formatError(error, fallback) {
|
|
458
|
+
return error instanceof Error ? error.message : fallback;
|
|
459
|
+
}
|
|
460
|
+
function formatValidationErrorMessage(errors) {
|
|
461
|
+
const first = errors[0];
|
|
462
|
+
if (errors.length === 1 && first) return `Cannot save Viz changes: ${first.message}`;
|
|
463
|
+
return `Cannot save Viz changes: ${errors.length} validation errors. First: ${first?.message ?? "Unknown validation error"}`;
|
|
464
|
+
}
|
|
465
|
+
function sourceLocationUris(sourceLocations) {
|
|
466
|
+
return [...new Set([
|
|
467
|
+
sourceLocations?.root?.uri,
|
|
468
|
+
...(sourceLocations?.states ?? []).map((location) => location.uri),
|
|
469
|
+
...(sourceLocations?.staticValues ?? []).map((location) => location.uri),
|
|
470
|
+
...(sourceLocations?.transitionFunctions ?? []).map((location) => location.uri)
|
|
471
|
+
].filter((uri) => Boolean(uri)))];
|
|
472
|
+
}
|
|
473
|
+
function stateSourceLocationUris(sourceLocations) {
|
|
474
|
+
return [...new Set((sourceLocations?.states ?? []).map((location) => location.uri).filter(Boolean))];
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
//#endregion
|
|
478
|
+
export { createRemoteEditorSyncBackend as n, fetchEditorSyncAccess as r, EditorHostSession as t };
|