@qontinui/ui-bridge 0.2.0 → 0.3.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/ai/index.d.mts +4 -4
- package/dist/ai/index.d.ts +4 -4
- package/dist/babel-plugin/index.js +515 -0
- package/dist/babel-plugin/index.js.map +1 -0
- package/dist/babel-plugin/index.mjs +499 -0
- package/dist/babel-plugin/index.mjs.map +1 -0
- package/dist/control/index.d.mts +5 -5
- package/dist/control/index.d.ts +5 -5
- package/dist/core/index.d.mts +115 -44
- package/dist/core/index.d.ts +115 -44
- package/dist/core/index.js +0 -1560
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +1 -1549
- package/dist/core/index.mjs.map +1 -1
- package/dist/debug/index.d.mts +3 -3
- package/dist/debug/index.d.ts +3 -3
- package/dist/index.d.mts +7 -8
- package/dist/index.d.ts +7 -8
- package/dist/index.js +859 -873
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +860 -862
- package/dist/index.mjs.map +1 -1
- package/dist/{metrics-C9XRi_mL.d.ts → metrics-BfiT_rhZ.d.ts} +2 -2
- package/dist/{metrics-NC3csD0R.d.mts → metrics-DTA2bwG7.d.mts} +2 -2
- package/dist/native/control/index.js +453 -0
- package/dist/native/control/index.js.map +1 -0
- package/dist/native/control/index.mjs +450 -0
- package/dist/native/control/index.mjs.map +1 -0
- package/dist/native/core/index.js +486 -0
- package/dist/native/core/index.js.map +1 -0
- package/dist/native/core/index.mjs +475 -0
- package/dist/native/core/index.mjs.map +1 -0
- package/dist/native/debug/index.js +451 -0
- package/dist/native/debug/index.js.map +1 -0
- package/dist/native/debug/index.mjs +449 -0
- package/dist/native/debug/index.mjs.map +1 -0
- package/dist/native/index.js +2274 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native/index.mjs +2246 -0
- package/dist/native/index.mjs.map +1 -0
- package/dist/native/react/index.js +1401 -0
- package/dist/native/react/index.js.map +1 -0
- package/dist/native/react/index.mjs +1389 -0
- package/dist/native/react/index.mjs.map +1 -0
- package/dist/native/server/index.js +415 -0
- package/dist/native/server/index.js.map +1 -0
- package/dist/native/server/index.mjs +410 -0
- package/dist/native/server/index.mjs.map +1 -0
- package/dist/react/index.d.mts +20 -7
- package/dist/react/index.d.ts +20 -7
- package/dist/react/index.js +42 -4
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +42 -4
- package/dist/react/index.mjs.map +1 -1
- package/dist/{registry-CIEDjbQ9.d.ts → registry-BKLEm-yk.d.ts} +9 -15
- package/dist/{registry-SsSDq46X.d.mts → registry-BmZgyCz8.d.mts} +9 -15
- package/dist/render-log/index.d.mts +1 -1
- package/dist/render-log/index.d.ts +1 -1
- package/dist/server/express.d.mts +36 -0
- package/dist/server/express.d.ts +36 -0
- package/dist/server/express.js +196 -0
- package/dist/server/express.js.map +1 -0
- package/dist/server/express.mjs +192 -0
- package/dist/server/express.mjs.map +1 -0
- package/dist/server/handlers.d.mts +93 -0
- package/dist/server/handlers.d.ts +93 -0
- package/dist/server/handlers.js +4278 -0
- package/dist/server/handlers.js.map +1 -0
- package/dist/server/handlers.mjs +4275 -0
- package/dist/server/handlers.mjs.map +1 -0
- package/dist/server/index.d.mts +10 -0
- package/dist/server/index.d.ts +10 -0
- package/dist/server/index.js +5352 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +5337 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/server/nextjs.d.mts +126 -0
- package/dist/server/nextjs.d.ts +126 -0
- package/dist/server/nextjs.js +287 -0
- package/dist/server/nextjs.js.map +1 -0
- package/dist/server/nextjs.mjs +282 -0
- package/dist/server/nextjs.mjs.map +1 -0
- package/dist/server/standalone.d.mts +6 -0
- package/dist/server/standalone.d.ts +6 -0
- package/dist/server/standalone.js +719 -0
- package/dist/server/standalone.js.map +1 -0
- package/dist/server/standalone.mjs +715 -0
- package/dist/server/standalone.mjs.map +1 -0
- package/dist/standalone-BURj8J3G.d.ts +212 -0
- package/dist/standalone-Dwmel29d.d.mts +212 -0
- package/dist/swc-plugin/index.d.mts +79 -0
- package/dist/swc-plugin/index.d.ts +79 -0
- package/dist/swc-plugin/index.js +15 -0
- package/dist/swc-plugin/index.js.map +1 -0
- package/dist/swc-plugin/index.mjs +9 -0
- package/dist/swc-plugin/index.mjs.map +1 -0
- package/dist/{types-CFT3Dnx4.d.mts → types-B5Q0GVo0.d.mts} +115 -3
- package/dist/{types-Dr6tH-bm.d.mts → types-B7J7noLK.d.mts} +1 -1
- package/dist/{types-oCTrRxSw.d.ts → types-BkNRILUa.d.ts} +1 -1
- package/dist/types-CEQLnFMv.d.mts +156 -0
- package/dist/types-CHnlwiTK.d.ts +156 -0
- package/dist/{types-BvCfFuEV.d.ts → types-DfPqwU-i.d.ts} +115 -3
- package/dist/{types-CPMbN_Iw.d.mts → types-jKVgTI6_.d.mts} +356 -160
- package/dist/{types-CPMbN_Iw.d.ts → types-jKVgTI6_.d.ts} +356 -160
- package/package.json +106 -3
- package/swc-plugin-wasm/ui_bridge_swc_plugin.wasm +0 -0
- package/dist/websocket-client-CX4QJesI.d.ts +0 -124
- package/dist/websocket-client-C_Na0OSp.d.mts +0 -124
package/dist/index.js
CHANGED
|
@@ -3,8 +3,442 @@
|
|
|
3
3
|
var react = require('react');
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
5
|
|
|
6
|
+
// src/core/websocket-client.ts
|
|
7
|
+
function generateId() {
|
|
8
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
9
|
+
}
|
|
10
|
+
var UIBridgeWSClient = class {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.ws = null;
|
|
13
|
+
this.state = "disconnected";
|
|
14
|
+
this.clientId = null;
|
|
15
|
+
this.reconnectAttempts = 0;
|
|
16
|
+
this.reconnectTimer = null;
|
|
17
|
+
this.pingTimer = null;
|
|
18
|
+
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
19
|
+
// Event listeners
|
|
20
|
+
this.connectionListeners = /* @__PURE__ */ new Set();
|
|
21
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
22
|
+
this.errorListeners = /* @__PURE__ */ new Set();
|
|
23
|
+
// Current subscriptions
|
|
24
|
+
this.subscriptions = {};
|
|
25
|
+
this.config = {
|
|
26
|
+
url: config.url,
|
|
27
|
+
autoReconnect: config.autoReconnect ?? true,
|
|
28
|
+
reconnectDelay: config.reconnectDelay ?? 1e3,
|
|
29
|
+
maxReconnectAttempts: config.maxReconnectAttempts ?? 10,
|
|
30
|
+
pingInterval: config.pingInterval ?? 3e4,
|
|
31
|
+
connectionTimeout: config.connectionTimeout ?? 1e4
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get current connection state
|
|
36
|
+
*/
|
|
37
|
+
get connectionState() {
|
|
38
|
+
return this.state;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get assigned client ID
|
|
42
|
+
*/
|
|
43
|
+
get id() {
|
|
44
|
+
return this.clientId;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Connect to the WebSocket server
|
|
48
|
+
*/
|
|
49
|
+
connect() {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
if (this.ws && this.state === "connected") {
|
|
52
|
+
resolve();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.setState("connecting");
|
|
56
|
+
try {
|
|
57
|
+
this.ws = new WebSocket(this.config.url);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
this.setState("disconnected");
|
|
60
|
+
reject(error);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const connectionTimeout = setTimeout(() => {
|
|
64
|
+
if (this.state === "connecting") {
|
|
65
|
+
this.ws?.close();
|
|
66
|
+
this.setState("disconnected");
|
|
67
|
+
reject(new Error("Connection timeout"));
|
|
68
|
+
}
|
|
69
|
+
}, this.config.connectionTimeout);
|
|
70
|
+
this.ws.onopen = () => {
|
|
71
|
+
clearTimeout(connectionTimeout);
|
|
72
|
+
};
|
|
73
|
+
this.ws.onmessage = (event) => {
|
|
74
|
+
try {
|
|
75
|
+
const message = JSON.parse(event.data);
|
|
76
|
+
this.handleMessage(message);
|
|
77
|
+
if (message.type === "welcome") {
|
|
78
|
+
clearTimeout(connectionTimeout);
|
|
79
|
+
this.reconnectAttempts = 0;
|
|
80
|
+
this.setState("connected");
|
|
81
|
+
this.startPingInterval();
|
|
82
|
+
if (this.subscriptions.events?.length || this.subscriptions.elementIds?.length || this.subscriptions.componentIds?.length) {
|
|
83
|
+
this.subscribe(this.subscriptions);
|
|
84
|
+
}
|
|
85
|
+
resolve();
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error("Failed to parse WebSocket message:", error);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
this.ws.onerror = (_event) => {
|
|
92
|
+
clearTimeout(connectionTimeout);
|
|
93
|
+
const error = new Error("WebSocket error");
|
|
94
|
+
this.notifyError(error);
|
|
95
|
+
if (this.state === "connecting") {
|
|
96
|
+
reject(error);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
this.ws.onclose = () => {
|
|
100
|
+
clearTimeout(connectionTimeout);
|
|
101
|
+
this.stopPingInterval();
|
|
102
|
+
this.clientId = null;
|
|
103
|
+
const wasConnected = this.state === "connected";
|
|
104
|
+
this.setState("disconnected");
|
|
105
|
+
for (const [_id, pending] of this.pendingRequests) {
|
|
106
|
+
clearTimeout(pending.timeout);
|
|
107
|
+
pending.reject(new Error("Connection closed"));
|
|
108
|
+
}
|
|
109
|
+
this.pendingRequests.clear();
|
|
110
|
+
if (wasConnected && this.config.autoReconnect && (this.config.maxReconnectAttempts === 0 || this.reconnectAttempts < this.config.maxReconnectAttempts)) {
|
|
111
|
+
this.scheduleReconnect();
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Disconnect from the server
|
|
118
|
+
*/
|
|
119
|
+
disconnect() {
|
|
120
|
+
if (this.reconnectTimer) {
|
|
121
|
+
clearTimeout(this.reconnectTimer);
|
|
122
|
+
this.reconnectTimer = null;
|
|
123
|
+
}
|
|
124
|
+
this.stopPingInterval();
|
|
125
|
+
if (this.ws) {
|
|
126
|
+
this.ws.close();
|
|
127
|
+
this.ws = null;
|
|
128
|
+
}
|
|
129
|
+
this.setState("disconnected");
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Subscribe to events
|
|
133
|
+
*/
|
|
134
|
+
async subscribe(options) {
|
|
135
|
+
this.subscriptions = { ...this.subscriptions, ...options };
|
|
136
|
+
const response = await this.sendRequest({
|
|
137
|
+
id: generateId(),
|
|
138
|
+
type: "subscribe",
|
|
139
|
+
timestamp: Date.now(),
|
|
140
|
+
payload: options
|
|
141
|
+
});
|
|
142
|
+
return response.events;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Unsubscribe from events
|
|
146
|
+
*/
|
|
147
|
+
async unsubscribe(events) {
|
|
148
|
+
if (events) {
|
|
149
|
+
this.subscriptions.events = this.subscriptions.events?.filter((e) => !events.includes(e));
|
|
150
|
+
} else {
|
|
151
|
+
this.subscriptions = {};
|
|
152
|
+
}
|
|
153
|
+
const response = await this.sendRequest({
|
|
154
|
+
id: generateId(),
|
|
155
|
+
type: "unsubscribe",
|
|
156
|
+
timestamp: Date.now(),
|
|
157
|
+
payload: { events }
|
|
158
|
+
});
|
|
159
|
+
return response.events;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Find elements
|
|
163
|
+
*/
|
|
164
|
+
async find(options) {
|
|
165
|
+
const response = await this.sendRequest({
|
|
166
|
+
id: generateId(),
|
|
167
|
+
type: "find",
|
|
168
|
+
timestamp: Date.now(),
|
|
169
|
+
payload: options
|
|
170
|
+
});
|
|
171
|
+
return response.elements;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Discover elements
|
|
175
|
+
* @deprecated Use find() instead
|
|
176
|
+
*/
|
|
177
|
+
async discover(options) {
|
|
178
|
+
return this.find(options);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Get element details
|
|
182
|
+
*/
|
|
183
|
+
async getElement(elementId, includeState = true) {
|
|
184
|
+
const response = await this.sendRequest({
|
|
185
|
+
id: generateId(),
|
|
186
|
+
type: "getElement",
|
|
187
|
+
timestamp: Date.now(),
|
|
188
|
+
payload: { elementId, includeState }
|
|
189
|
+
});
|
|
190
|
+
return response.element;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get full snapshot
|
|
194
|
+
*/
|
|
195
|
+
async getSnapshot() {
|
|
196
|
+
const response = await this.sendRequest({
|
|
197
|
+
id: generateId(),
|
|
198
|
+
type: "getSnapshot",
|
|
199
|
+
timestamp: Date.now()
|
|
200
|
+
});
|
|
201
|
+
return response;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Execute action on an element
|
|
205
|
+
*/
|
|
206
|
+
async executeAction(elementId, action) {
|
|
207
|
+
const response = await this.sendRequest({
|
|
208
|
+
id: generateId(),
|
|
209
|
+
type: "executeAction",
|
|
210
|
+
timestamp: Date.now(),
|
|
211
|
+
payload: { elementId, action }
|
|
212
|
+
});
|
|
213
|
+
return response;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Execute component action
|
|
217
|
+
*/
|
|
218
|
+
async executeComponentAction(componentId, action, params) {
|
|
219
|
+
const response = await this.sendRequest({
|
|
220
|
+
id: generateId(),
|
|
221
|
+
type: "executeComponentAction",
|
|
222
|
+
timestamp: Date.now(),
|
|
223
|
+
payload: { componentId, action, params }
|
|
224
|
+
});
|
|
225
|
+
return response;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Execute workflow with optional progress streaming
|
|
229
|
+
*/
|
|
230
|
+
async executeWorkflow(workflowId, params, onProgress) {
|
|
231
|
+
const id = generateId();
|
|
232
|
+
const progressHandler = onProgress ? (message) => {
|
|
233
|
+
if (message.type === "workflowProgress" && message.requestId === id) {
|
|
234
|
+
onProgress({
|
|
235
|
+
currentStep: message.payload.currentStep,
|
|
236
|
+
totalSteps: message.payload.totalSteps,
|
|
237
|
+
step: {
|
|
238
|
+
id: message.payload.step.id,
|
|
239
|
+
status: message.payload.step.status
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
} : void 0;
|
|
244
|
+
const response = await this.sendRequest(
|
|
245
|
+
{
|
|
246
|
+
id,
|
|
247
|
+
type: "executeWorkflow",
|
|
248
|
+
timestamp: Date.now(),
|
|
249
|
+
payload: { workflowId, params, streamProgress: !!onProgress }
|
|
250
|
+
},
|
|
251
|
+
progressHandler
|
|
252
|
+
);
|
|
253
|
+
return response;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Add connection state listener
|
|
257
|
+
*/
|
|
258
|
+
onConnectionChange(listener) {
|
|
259
|
+
this.connectionListeners.add(listener);
|
|
260
|
+
return () => this.connectionListeners.delete(listener);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Add event listener
|
|
264
|
+
*/
|
|
265
|
+
onEvent(eventType, listener) {
|
|
266
|
+
if (!this.eventListeners.has(eventType)) {
|
|
267
|
+
this.eventListeners.set(eventType, /* @__PURE__ */ new Set());
|
|
268
|
+
}
|
|
269
|
+
this.eventListeners.get(eventType).add(listener);
|
|
270
|
+
return () => this.eventListeners.get(eventType)?.delete(listener);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Add error listener
|
|
274
|
+
*/
|
|
275
|
+
onError(listener) {
|
|
276
|
+
this.errorListeners.add(listener);
|
|
277
|
+
return () => this.errorListeners.delete(listener);
|
|
278
|
+
}
|
|
279
|
+
// Private methods
|
|
280
|
+
setState(state) {
|
|
281
|
+
this.state = state;
|
|
282
|
+
for (const listener of this.connectionListeners) {
|
|
283
|
+
try {
|
|
284
|
+
listener(state);
|
|
285
|
+
} catch (error) {
|
|
286
|
+
console.error("Connection listener error:", error);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
handleMessage(message) {
|
|
291
|
+
switch (message.type) {
|
|
292
|
+
case "welcome":
|
|
293
|
+
this.clientId = message.payload.clientId;
|
|
294
|
+
break;
|
|
295
|
+
case "pong":
|
|
296
|
+
break;
|
|
297
|
+
case "subscribed":
|
|
298
|
+
case "unsubscribed":
|
|
299
|
+
break;
|
|
300
|
+
case "event":
|
|
301
|
+
this.notifyEvent(message.payload);
|
|
302
|
+
break;
|
|
303
|
+
case "response":
|
|
304
|
+
this.handleResponse(message);
|
|
305
|
+
break;
|
|
306
|
+
case "error":
|
|
307
|
+
if (message.requestId) {
|
|
308
|
+
this.handleResponse({
|
|
309
|
+
...message,
|
|
310
|
+
type: "response",
|
|
311
|
+
requestId: message.requestId,
|
|
312
|
+
payload: {
|
|
313
|
+
success: false,
|
|
314
|
+
error: message.payload.message
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
} else {
|
|
318
|
+
this.notifyError(new Error(message.payload.message));
|
|
319
|
+
}
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
handleResponse(message) {
|
|
324
|
+
const pending = this.pendingRequests.get(message.requestId);
|
|
325
|
+
if (!pending) return;
|
|
326
|
+
clearTimeout(pending.timeout);
|
|
327
|
+
this.pendingRequests.delete(message.requestId);
|
|
328
|
+
if (message.type === "response") {
|
|
329
|
+
if (message.payload.success) {
|
|
330
|
+
pending.resolve(message.payload.data);
|
|
331
|
+
} else {
|
|
332
|
+
pending.reject(new Error(message.payload.error || "Request failed"));
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
notifyEvent(event) {
|
|
337
|
+
const typeListeners = this.eventListeners.get(event.type);
|
|
338
|
+
if (typeListeners) {
|
|
339
|
+
for (const listener of typeListeners) {
|
|
340
|
+
try {
|
|
341
|
+
listener(event);
|
|
342
|
+
} catch (error) {
|
|
343
|
+
console.error("Event listener error:", error);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const wildcardListeners = this.eventListeners.get("*");
|
|
348
|
+
if (wildcardListeners) {
|
|
349
|
+
for (const listener of wildcardListeners) {
|
|
350
|
+
try {
|
|
351
|
+
listener(event);
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.error("Event listener error:", error);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
notifyError(error) {
|
|
359
|
+
for (const listener of this.errorListeners) {
|
|
360
|
+
try {
|
|
361
|
+
listener(error);
|
|
362
|
+
} catch (e) {
|
|
363
|
+
console.error("Error listener error:", e);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
sendRequest(message, progressHandler) {
|
|
368
|
+
return new Promise((resolve, reject) => {
|
|
369
|
+
if (!this.ws || this.state !== "connected") {
|
|
370
|
+
reject(new Error("Not connected"));
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const timeout = setTimeout(() => {
|
|
374
|
+
this.pendingRequests.delete(message.id);
|
|
375
|
+
reject(new Error("Request timeout"));
|
|
376
|
+
}, 3e4);
|
|
377
|
+
this.pendingRequests.set(message.id, {
|
|
378
|
+
resolve,
|
|
379
|
+
reject,
|
|
380
|
+
timeout
|
|
381
|
+
});
|
|
382
|
+
if (progressHandler && this.ws) {
|
|
383
|
+
const originalHandler = this.ws.onmessage;
|
|
384
|
+
const wsRef = this.ws;
|
|
385
|
+
const wrappedHandler = (event) => {
|
|
386
|
+
try {
|
|
387
|
+
const msg = JSON.parse(event.data);
|
|
388
|
+
if (msg.type === "workflowProgress") {
|
|
389
|
+
progressHandler(msg);
|
|
390
|
+
}
|
|
391
|
+
} catch {
|
|
392
|
+
}
|
|
393
|
+
if (originalHandler) {
|
|
394
|
+
originalHandler.call(wsRef, event);
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
this.ws.onmessage = wrappedHandler;
|
|
398
|
+
}
|
|
399
|
+
this.ws.send(JSON.stringify(message));
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
scheduleReconnect() {
|
|
403
|
+
if (this.reconnectTimer) return;
|
|
404
|
+
this.setState("reconnecting");
|
|
405
|
+
this.reconnectAttempts++;
|
|
406
|
+
const delay = Math.min(
|
|
407
|
+
this.config.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1),
|
|
408
|
+
3e4
|
|
409
|
+
);
|
|
410
|
+
this.reconnectTimer = setTimeout(() => {
|
|
411
|
+
this.reconnectTimer = null;
|
|
412
|
+
this.connect().catch(() => {
|
|
413
|
+
});
|
|
414
|
+
}, delay);
|
|
415
|
+
}
|
|
416
|
+
startPingInterval() {
|
|
417
|
+
if (this.config.pingInterval <= 0) return;
|
|
418
|
+
this.pingTimer = setInterval(() => {
|
|
419
|
+
if (this.ws && this.state === "connected") {
|
|
420
|
+
this.ws.send(
|
|
421
|
+
JSON.stringify({
|
|
422
|
+
id: generateId(),
|
|
423
|
+
type: "ping",
|
|
424
|
+
timestamp: Date.now()
|
|
425
|
+
})
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
}, this.config.pingInterval);
|
|
429
|
+
}
|
|
430
|
+
stopPingInterval() {
|
|
431
|
+
if (this.pingTimer) {
|
|
432
|
+
clearInterval(this.pingTimer);
|
|
433
|
+
this.pingTimer = null;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
function createWSClient(config) {
|
|
438
|
+
return new UIBridgeWSClient(config);
|
|
439
|
+
}
|
|
440
|
+
|
|
6
441
|
// src/core/element-identifier.ts
|
|
7
|
-
var ID_ATTRIBUTES = ["data-ui-id", "data-testid", "data-awas-element", "id"];
|
|
8
442
|
function generateXPath(element) {
|
|
9
443
|
if (element.id) {
|
|
10
444
|
return `//*[@id="${element.id}"]`;
|
|
@@ -188,39 +622,6 @@ function findElementByIdentifier(identifier, root = document) {
|
|
|
188
622
|
}
|
|
189
623
|
return null;
|
|
190
624
|
}
|
|
191
|
-
function findAllElementsByIdentifier(pattern, root = document) {
|
|
192
|
-
const results = [];
|
|
193
|
-
try {
|
|
194
|
-
const elements = root.querySelectorAll(pattern);
|
|
195
|
-
results.push(...Array.from(elements));
|
|
196
|
-
if (results.length > 0) return results;
|
|
197
|
-
} catch {
|
|
198
|
-
}
|
|
199
|
-
const partials = [
|
|
200
|
-
`[data-ui-id*="${pattern}"]`,
|
|
201
|
-
`[data-testid*="${pattern}"]`,
|
|
202
|
-
`[data-awas-element*="${pattern}"]`,
|
|
203
|
-
`[id*="${pattern}"]`
|
|
204
|
-
];
|
|
205
|
-
for (const selector of partials) {
|
|
206
|
-
try {
|
|
207
|
-
const elements = root.querySelectorAll(selector);
|
|
208
|
-
for (const el of elements) {
|
|
209
|
-
if (!results.includes(el)) {
|
|
210
|
-
results.push(el);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
} catch {
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
return results;
|
|
217
|
-
}
|
|
218
|
-
function elementMatchesIdentifier(element, identifier) {
|
|
219
|
-
if (typeof identifier === "string") {
|
|
220
|
-
return element.getAttribute("data-ui-id") === identifier || element.getAttribute("data-testid") === identifier || element.getAttribute("data-awas-element") === identifier || element.id === identifier || element.matches(identifier);
|
|
221
|
-
}
|
|
222
|
-
return identifier.uiId && element.getAttribute("data-ui-id") === identifier.uiId || identifier.testId && element.getAttribute("data-testid") === identifier.testId || identifier.awasId && element.getAttribute("data-awas-element") === identifier.awasId || identifier.htmlId && element.id === identifier.htmlId || false;
|
|
223
|
-
}
|
|
224
625
|
|
|
225
626
|
// src/ai/fuzzy-matcher.ts
|
|
226
627
|
var DEFAULT_FUZZY_CONFIG = {
|
|
@@ -1260,937 +1661,512 @@ var UIBridgeRegistry = class {
|
|
|
1260
1661
|
}
|
|
1261
1662
|
/**
|
|
1262
1663
|
* Register a component
|
|
1263
|
-
*/
|
|
1264
|
-
registerComponent(id, options) {
|
|
1265
|
-
const registered = {
|
|
1266
|
-
id,
|
|
1267
|
-
name: options.name,
|
|
1268
|
-
description: options.description,
|
|
1269
|
-
actions: options.actions?.map((a) => ({
|
|
1270
|
-
id: a.id,
|
|
1271
|
-
label: a.label,
|
|
1272
|
-
description: a.description,
|
|
1273
|
-
handler: a.handler
|
|
1274
|
-
})) ?? [],
|
|
1275
|
-
elementIds: options.elementIds,
|
|
1276
|
-
registeredAt: Date.now(),
|
|
1277
|
-
mounted: true
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
* Unregister a component
|
|
1285
|
-
*/
|
|
1286
|
-
unregisterComponent(id) {
|
|
1287
|
-
const component = this.components.get(id);
|
|
1288
|
-
if (component) {
|
|
1289
|
-
component.mounted = false;
|
|
1290
|
-
this.components.delete(id);
|
|
1291
|
-
this.emit("component:unregistered", { id });
|
|
1292
|
-
return true;
|
|
1293
|
-
}
|
|
1294
|
-
return false;
|
|
1295
|
-
}
|
|
1296
|
-
/**
|
|
1297
|
-
* Get a registered component
|
|
1298
|
-
*/
|
|
1299
|
-
getComponent(id) {
|
|
1300
|
-
return this.components.get(id);
|
|
1301
|
-
}
|
|
1302
|
-
/**
|
|
1303
|
-
* Get all registered components
|
|
1304
|
-
*/
|
|
1305
|
-
getAllComponents() {
|
|
1306
|
-
return Array.from(this.components.values());
|
|
1307
|
-
}
|
|
1308
|
-
/**
|
|
1309
|
-
* Register a workflow
|
|
1310
|
-
*/
|
|
1311
|
-
registerWorkflow(workflow) {
|
|
1312
|
-
this.workflows.set(workflow.id, workflow);
|
|
1313
|
-
return workflow;
|
|
1314
|
-
}
|
|
1315
|
-
/**
|
|
1316
|
-
* Unregister a workflow
|
|
1317
|
-
*/
|
|
1318
|
-
unregisterWorkflow(id) {
|
|
1319
|
-
return this.workflows.delete(id);
|
|
1320
|
-
}
|
|
1321
|
-
/**
|
|
1322
|
-
* Get a workflow
|
|
1323
|
-
*/
|
|
1324
|
-
getWorkflow(id) {
|
|
1325
|
-
return this.workflows.get(id);
|
|
1326
|
-
}
|
|
1327
|
-
/**
|
|
1328
|
-
* Get all workflows
|
|
1329
|
-
*/
|
|
1330
|
-
getAllWorkflows() {
|
|
1331
|
-
return Array.from(this.workflows.values());
|
|
1332
|
-
}
|
|
1333
|
-
// ==========================================================================
|
|
1334
|
-
// State Management
|
|
1335
|
-
// ==========================================================================
|
|
1336
|
-
/**
|
|
1337
|
-
* Register a state
|
|
1338
|
-
*/
|
|
1339
|
-
registerState(state) {
|
|
1340
|
-
this.states.set(state.id, state);
|
|
1341
|
-
this.emit("element:registered", { id: state.id, type: "state", name: state.name });
|
|
1342
|
-
return state;
|
|
1343
|
-
}
|
|
1344
|
-
/**
|
|
1345
|
-
* Unregister a state
|
|
1346
|
-
*/
|
|
1347
|
-
unregisterState(id) {
|
|
1348
|
-
const state = this.states.get(id);
|
|
1349
|
-
if (state) {
|
|
1350
|
-
this.activeStates.delete(id);
|
|
1351
|
-
this.states.delete(id);
|
|
1352
|
-
this.emit("element:unregistered", { id, type: "state" });
|
|
1353
|
-
return true;
|
|
1354
|
-
}
|
|
1355
|
-
return false;
|
|
1356
|
-
}
|
|
1357
|
-
/**
|
|
1358
|
-
* Get a registered state
|
|
1359
|
-
*/
|
|
1360
|
-
getState(id) {
|
|
1361
|
-
return this.states.get(id);
|
|
1362
|
-
}
|
|
1363
|
-
/**
|
|
1364
|
-
* Get all registered states
|
|
1365
|
-
*/
|
|
1366
|
-
getAllStates() {
|
|
1367
|
-
return Array.from(this.states.values());
|
|
1368
|
-
}
|
|
1369
|
-
/**
|
|
1370
|
-
* Register a state group
|
|
1371
|
-
*/
|
|
1372
|
-
registerStateGroup(group) {
|
|
1373
|
-
this.stateGroups.set(group.id, group);
|
|
1374
|
-
return group;
|
|
1375
|
-
}
|
|
1376
|
-
/**
|
|
1377
|
-
* Unregister a state group
|
|
1378
|
-
*/
|
|
1379
|
-
unregisterStateGroup(id) {
|
|
1380
|
-
return this.stateGroups.delete(id);
|
|
1381
|
-
}
|
|
1382
|
-
/**
|
|
1383
|
-
* Get a state group
|
|
1384
|
-
*/
|
|
1385
|
-
getStateGroup(id) {
|
|
1386
|
-
return this.stateGroups.get(id);
|
|
1387
|
-
}
|
|
1388
|
-
/**
|
|
1389
|
-
* Get all state groups
|
|
1390
|
-
*/
|
|
1391
|
-
getAllStateGroups() {
|
|
1392
|
-
return Array.from(this.stateGroups.values());
|
|
1393
|
-
}
|
|
1394
|
-
/**
|
|
1395
|
-
* Register a transition
|
|
1396
|
-
*/
|
|
1397
|
-
registerTransition(transition) {
|
|
1398
|
-
this.transitions.set(transition.id, transition);
|
|
1399
|
-
return transition;
|
|
1400
|
-
}
|
|
1401
|
-
/**
|
|
1402
|
-
* Unregister a transition
|
|
1403
|
-
*/
|
|
1404
|
-
unregisterTransition(id) {
|
|
1405
|
-
return this.transitions.delete(id);
|
|
1406
|
-
}
|
|
1407
|
-
/**
|
|
1408
|
-
* Get a transition
|
|
1409
|
-
*/
|
|
1410
|
-
getTransition(id) {
|
|
1411
|
-
return this.transitions.get(id);
|
|
1412
|
-
}
|
|
1413
|
-
/**
|
|
1414
|
-
* Get all transitions
|
|
1415
|
-
*/
|
|
1416
|
-
getAllTransitions() {
|
|
1417
|
-
return Array.from(this.transitions.values());
|
|
1418
|
-
}
|
|
1419
|
-
/**
|
|
1420
|
-
* Get currently active states
|
|
1421
|
-
*/
|
|
1422
|
-
getActiveStates() {
|
|
1423
|
-
return Array.from(this.activeStates);
|
|
1424
|
-
}
|
|
1425
|
-
/**
|
|
1426
|
-
* Check if a state is active
|
|
1427
|
-
*/
|
|
1428
|
-
isStateActive(id) {
|
|
1429
|
-
return this.activeStates.has(id);
|
|
1430
|
-
}
|
|
1431
|
-
/**
|
|
1432
|
-
* Activate a state
|
|
1433
|
-
*/
|
|
1434
|
-
activateState(id) {
|
|
1435
|
-
const state = this.states.get(id);
|
|
1436
|
-
if (!state) {
|
|
1437
|
-
return false;
|
|
1438
|
-
}
|
|
1439
|
-
for (const activeId of this.activeStates) {
|
|
1440
|
-
const activeState = this.states.get(activeId);
|
|
1441
|
-
if (activeState?.blocking && activeState.id !== id) {
|
|
1442
|
-
return false;
|
|
1443
|
-
}
|
|
1444
|
-
if (activeState?.blocks?.includes(id)) {
|
|
1445
|
-
return false;
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
const wasActive = this.activeStates.has(id);
|
|
1449
|
-
this.activeStates.add(id);
|
|
1450
|
-
if (!wasActive) {
|
|
1451
|
-
this.emit("element:stateChanged", {
|
|
1452
|
-
stateId: id,
|
|
1453
|
-
active: true,
|
|
1454
|
-
activeStates: this.getActiveStates()
|
|
1455
|
-
});
|
|
1456
|
-
}
|
|
1457
|
-
return true;
|
|
1664
|
+
*/
|
|
1665
|
+
registerComponent(id, options) {
|
|
1666
|
+
const registered = {
|
|
1667
|
+
id,
|
|
1668
|
+
name: options.name,
|
|
1669
|
+
description: options.description,
|
|
1670
|
+
actions: options.actions?.map((a) => ({
|
|
1671
|
+
id: a.id,
|
|
1672
|
+
label: a.label,
|
|
1673
|
+
description: a.description,
|
|
1674
|
+
handler: a.handler
|
|
1675
|
+
})) ?? [],
|
|
1676
|
+
elementIds: options.elementIds,
|
|
1677
|
+
registeredAt: Date.now(),
|
|
1678
|
+
mounted: true,
|
|
1679
|
+
getState: options.getState,
|
|
1680
|
+
getComputed: options.getComputed
|
|
1681
|
+
};
|
|
1682
|
+
this.components.set(id, registered);
|
|
1683
|
+
this.emit("component:registered", { id, name: options.name });
|
|
1684
|
+
return registered;
|
|
1458
1685
|
}
|
|
1459
1686
|
/**
|
|
1460
|
-
*
|
|
1687
|
+
* Unregister a component
|
|
1461
1688
|
*/
|
|
1462
|
-
|
|
1463
|
-
const
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
this.
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
activeStates: this.getActiveStates()
|
|
1470
|
-
});
|
|
1689
|
+
unregisterComponent(id) {
|
|
1690
|
+
const component = this.components.get(id);
|
|
1691
|
+
if (component) {
|
|
1692
|
+
component.mounted = false;
|
|
1693
|
+
this.components.delete(id);
|
|
1694
|
+
this.emit("component:unregistered", { id });
|
|
1695
|
+
return true;
|
|
1471
1696
|
}
|
|
1472
|
-
return
|
|
1697
|
+
return false;
|
|
1473
1698
|
}
|
|
1474
1699
|
/**
|
|
1475
|
-
*
|
|
1700
|
+
* Get a registered component
|
|
1476
1701
|
*/
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
for (const id of ids) {
|
|
1480
|
-
if (this.activateState(id)) {
|
|
1481
|
-
activated.push(id);
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
return activated;
|
|
1702
|
+
getComponent(id) {
|
|
1703
|
+
return this.components.get(id);
|
|
1485
1704
|
}
|
|
1486
1705
|
/**
|
|
1487
|
-
*
|
|
1706
|
+
* Get all registered components
|
|
1488
1707
|
*/
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
for (const id of ids) {
|
|
1492
|
-
if (this.deactivateState(id)) {
|
|
1493
|
-
deactivated.push(id);
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
return deactivated;
|
|
1708
|
+
getAllComponents() {
|
|
1709
|
+
return Array.from(this.components.values());
|
|
1497
1710
|
}
|
|
1498
1711
|
/**
|
|
1499
|
-
*
|
|
1712
|
+
* Get the current state and computed properties of a component
|
|
1500
1713
|
*/
|
|
1501
|
-
|
|
1502
|
-
const
|
|
1503
|
-
if (!
|
|
1504
|
-
|
|
1714
|
+
getComponentState(id) {
|
|
1715
|
+
const component = this.components.get(id);
|
|
1716
|
+
if (!component || !component.mounted) {
|
|
1717
|
+
return null;
|
|
1718
|
+
}
|
|
1719
|
+
return {
|
|
1720
|
+
state: component.getState?.() ?? {},
|
|
1721
|
+
computed: component.getComputed?.() ?? {},
|
|
1722
|
+
timestamp: Date.now()
|
|
1723
|
+
};
|
|
1505
1724
|
}
|
|
1506
1725
|
/**
|
|
1507
|
-
*
|
|
1726
|
+
* Register a workflow
|
|
1508
1727
|
*/
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
return this.deactivateStates(group.states);
|
|
1728
|
+
registerWorkflow(workflow) {
|
|
1729
|
+
this.workflows.set(workflow.id, workflow);
|
|
1730
|
+
return workflow;
|
|
1513
1731
|
}
|
|
1514
1732
|
/**
|
|
1515
|
-
*
|
|
1733
|
+
* Unregister a workflow
|
|
1516
1734
|
*/
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
if (!transition) return false;
|
|
1520
|
-
return transition.fromStates.some((stateId) => this.activeStates.has(stateId));
|
|
1735
|
+
unregisterWorkflow(id) {
|
|
1736
|
+
return this.workflows.delete(id);
|
|
1521
1737
|
}
|
|
1522
1738
|
/**
|
|
1523
|
-
*
|
|
1739
|
+
* Get a workflow
|
|
1524
1740
|
*/
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
const transition = this.transitions.get(transitionId);
|
|
1528
|
-
if (!transition) {
|
|
1529
|
-
return {
|
|
1530
|
-
success: false,
|
|
1531
|
-
activatedStates: [],
|
|
1532
|
-
deactivatedStates: [],
|
|
1533
|
-
error: `Transition not found: ${transitionId}`,
|
|
1534
|
-
durationMs: performance.now() - startTime
|
|
1535
|
-
};
|
|
1536
|
-
}
|
|
1537
|
-
if (!this.canExecuteTransition(transitionId)) {
|
|
1538
|
-
return {
|
|
1539
|
-
success: false,
|
|
1540
|
-
activatedStates: [],
|
|
1541
|
-
deactivatedStates: [],
|
|
1542
|
-
error: "Precondition not met: none of the fromStates are active",
|
|
1543
|
-
failedPhase: "precondition",
|
|
1544
|
-
durationMs: performance.now() - startTime
|
|
1545
|
-
};
|
|
1546
|
-
}
|
|
1547
|
-
try {
|
|
1548
|
-
const deactivated = this.deactivateStates(transition.exitStates);
|
|
1549
|
-
if (transition.exitGroups) {
|
|
1550
|
-
for (const groupId of transition.exitGroups) {
|
|
1551
|
-
deactivated.push(...this.deactivateStateGroup(groupId));
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
const activated = this.activateStates(transition.activateStates);
|
|
1555
|
-
if (transition.activateGroups) {
|
|
1556
|
-
for (const groupId of transition.activateGroups) {
|
|
1557
|
-
activated.push(...this.activateStateGroup(groupId));
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
return {
|
|
1561
|
-
success: true,
|
|
1562
|
-
activatedStates: activated,
|
|
1563
|
-
deactivatedStates: deactivated,
|
|
1564
|
-
durationMs: performance.now() - startTime
|
|
1565
|
-
};
|
|
1566
|
-
} catch (error) {
|
|
1567
|
-
return {
|
|
1568
|
-
success: false,
|
|
1569
|
-
activatedStates: [],
|
|
1570
|
-
deactivatedStates: [],
|
|
1571
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1572
|
-
failedPhase: "execution",
|
|
1573
|
-
durationMs: performance.now() - startTime
|
|
1574
|
-
};
|
|
1575
|
-
}
|
|
1741
|
+
getWorkflow(id) {
|
|
1742
|
+
return this.workflows.get(id);
|
|
1576
1743
|
}
|
|
1577
1744
|
/**
|
|
1578
|
-
*
|
|
1579
|
-
*
|
|
1580
|
-
* Uses a simple BFS algorithm for pathfinding.
|
|
1581
|
-
* For more advanced pathfinding (Dijkstra, A*), use the Python state manager service.
|
|
1745
|
+
* Get all workflows
|
|
1582
1746
|
*/
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
return {
|
|
1586
|
-
found: true,
|
|
1587
|
-
transitions: [],
|
|
1588
|
-
totalCost: 0,
|
|
1589
|
-
targetStates,
|
|
1590
|
-
estimatedSteps: 0
|
|
1591
|
-
};
|
|
1592
|
-
}
|
|
1593
|
-
const queue = [
|
|
1594
|
-
{ activeStates: new Set(this.activeStates), path: [], cost: 0 }
|
|
1595
|
-
];
|
|
1596
|
-
const visited = /* @__PURE__ */ new Set();
|
|
1597
|
-
while (queue.length > 0) {
|
|
1598
|
-
const current = queue.shift();
|
|
1599
|
-
const stateKey = Array.from(current.activeStates).sort().join(",");
|
|
1600
|
-
if (visited.has(stateKey)) continue;
|
|
1601
|
-
visited.add(stateKey);
|
|
1602
|
-
if (targetStates.every((t) => current.activeStates.has(t))) {
|
|
1603
|
-
return {
|
|
1604
|
-
found: true,
|
|
1605
|
-
transitions: current.path,
|
|
1606
|
-
totalCost: current.cost,
|
|
1607
|
-
targetStates,
|
|
1608
|
-
estimatedSteps: current.path.length
|
|
1609
|
-
};
|
|
1610
|
-
}
|
|
1611
|
-
for (const transition of this.transitions.values()) {
|
|
1612
|
-
const canExecute = transition.fromStates.some((s) => current.activeStates.has(s));
|
|
1613
|
-
if (!canExecute) continue;
|
|
1614
|
-
const newActive = new Set(current.activeStates);
|
|
1615
|
-
for (const s of transition.exitStates) newActive.delete(s);
|
|
1616
|
-
for (const s of transition.activateStates) newActive.add(s);
|
|
1617
|
-
const newCost = current.cost + (transition.pathCost ?? 1);
|
|
1618
|
-
queue.push({
|
|
1619
|
-
activeStates: newActive,
|
|
1620
|
-
path: [...current.path, transition.id],
|
|
1621
|
-
cost: newCost
|
|
1622
|
-
});
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
return {
|
|
1626
|
-
found: false,
|
|
1627
|
-
transitions: [],
|
|
1628
|
-
totalCost: 0,
|
|
1629
|
-
targetStates,
|
|
1630
|
-
estimatedSteps: 0
|
|
1631
|
-
};
|
|
1747
|
+
getAllWorkflows() {
|
|
1748
|
+
return Array.from(this.workflows.values());
|
|
1632
1749
|
}
|
|
1750
|
+
// ==========================================================================
|
|
1751
|
+
// State Management
|
|
1752
|
+
// ==========================================================================
|
|
1633
1753
|
/**
|
|
1634
|
-
*
|
|
1754
|
+
* Register a state
|
|
1635
1755
|
*/
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
return {
|
|
1641
|
-
success: false,
|
|
1642
|
-
path,
|
|
1643
|
-
executedTransitions: [],
|
|
1644
|
-
finalActiveStates: this.getActiveStates(),
|
|
1645
|
-
error: `No path found to target states: ${targetStates.join(", ")}`,
|
|
1646
|
-
durationMs: performance.now() - startTime
|
|
1647
|
-
};
|
|
1648
|
-
}
|
|
1649
|
-
const executedTransitions = [];
|
|
1650
|
-
for (const transitionId of path.transitions) {
|
|
1651
|
-
const result = await this.executeTransition(transitionId);
|
|
1652
|
-
if (!result.success) {
|
|
1653
|
-
return {
|
|
1654
|
-
success: false,
|
|
1655
|
-
path,
|
|
1656
|
-
executedTransitions,
|
|
1657
|
-
finalActiveStates: this.getActiveStates(),
|
|
1658
|
-
error: result.error,
|
|
1659
|
-
durationMs: performance.now() - startTime
|
|
1660
|
-
};
|
|
1661
|
-
}
|
|
1662
|
-
executedTransitions.push(transitionId);
|
|
1663
|
-
}
|
|
1664
|
-
return {
|
|
1665
|
-
success: true,
|
|
1666
|
-
path,
|
|
1667
|
-
executedTransitions,
|
|
1668
|
-
finalActiveStates: this.getActiveStates(),
|
|
1669
|
-
durationMs: performance.now() - startTime
|
|
1670
|
-
};
|
|
1756
|
+
registerState(state) {
|
|
1757
|
+
this.states.set(state.id, state);
|
|
1758
|
+
this.emit("element:registered", { id: state.id, type: "state", name: state.name });
|
|
1759
|
+
return state;
|
|
1671
1760
|
}
|
|
1672
1761
|
/**
|
|
1673
|
-
*
|
|
1762
|
+
* Unregister a state
|
|
1674
1763
|
*/
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
}
|
|
1764
|
+
unregisterState(id) {
|
|
1765
|
+
const state = this.states.get(id);
|
|
1766
|
+
if (state) {
|
|
1767
|
+
this.activeStates.delete(id);
|
|
1768
|
+
this.states.delete(id);
|
|
1769
|
+
this.emit("element:unregistered", { id, type: "state" });
|
|
1770
|
+
return true;
|
|
1771
|
+
}
|
|
1772
|
+
return false;
|
|
1683
1773
|
}
|
|
1684
1774
|
/**
|
|
1685
|
-
*
|
|
1775
|
+
* Get a registered state
|
|
1686
1776
|
*/
|
|
1687
|
-
|
|
1688
|
-
return
|
|
1689
|
-
timestamp: Date.now(),
|
|
1690
|
-
elements: this.getAllElements().map((el) => ({
|
|
1691
|
-
id: el.id,
|
|
1692
|
-
type: el.type,
|
|
1693
|
-
label: el.label,
|
|
1694
|
-
identifier: el.getIdentifier(),
|
|
1695
|
-
state: el.getState(),
|
|
1696
|
-
actions: el.actions,
|
|
1697
|
-
customActions: el.customActions ? Object.keys(el.customActions) : void 0
|
|
1698
|
-
})),
|
|
1699
|
-
components: this.getAllComponents().map((comp) => ({
|
|
1700
|
-
id: comp.id,
|
|
1701
|
-
name: comp.name,
|
|
1702
|
-
description: comp.description,
|
|
1703
|
-
actions: comp.actions.map((a) => a.id),
|
|
1704
|
-
elementIds: comp.elementIds
|
|
1705
|
-
})),
|
|
1706
|
-
workflows: this.getAllWorkflows().map((wf) => ({
|
|
1707
|
-
id: wf.id,
|
|
1708
|
-
name: wf.name,
|
|
1709
|
-
description: wf.description,
|
|
1710
|
-
stepCount: wf.steps.length
|
|
1711
|
-
}))
|
|
1712
|
-
};
|
|
1777
|
+
getState(id) {
|
|
1778
|
+
return this.states.get(id);
|
|
1713
1779
|
}
|
|
1714
1780
|
/**
|
|
1715
|
-
*
|
|
1781
|
+
* Get all registered states
|
|
1716
1782
|
*/
|
|
1717
|
-
|
|
1718
|
-
this.
|
|
1719
|
-
this.components.clear();
|
|
1720
|
-
this.workflows.clear();
|
|
1721
|
-
this.eventListeners.clear();
|
|
1722
|
-
this.states.clear();
|
|
1723
|
-
this.stateGroups.clear();
|
|
1724
|
-
this.transitions.clear();
|
|
1725
|
-
this.activeStates.clear();
|
|
1783
|
+
getAllStates() {
|
|
1784
|
+
return Array.from(this.states.values());
|
|
1726
1785
|
}
|
|
1727
1786
|
/**
|
|
1728
|
-
*
|
|
1787
|
+
* Register a state group
|
|
1729
1788
|
*/
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
return {
|
|
1734
|
-
elementCount: elements.length,
|
|
1735
|
-
componentCount: components.length,
|
|
1736
|
-
workflowCount: this.workflows.size,
|
|
1737
|
-
mountedElementCount: elements.filter((e) => e.mounted).length,
|
|
1738
|
-
mountedComponentCount: components.filter((c) => c.mounted).length,
|
|
1739
|
-
stateCount: this.states.size,
|
|
1740
|
-
stateGroupCount: this.stateGroups.size,
|
|
1741
|
-
transitionCount: this.transitions.size,
|
|
1742
|
-
activeStateCount: this.activeStates.size
|
|
1743
|
-
};
|
|
1744
|
-
}
|
|
1745
|
-
};
|
|
1746
|
-
var globalRegistry = null;
|
|
1747
|
-
function getGlobalRegistry() {
|
|
1748
|
-
if (!globalRegistry) {
|
|
1749
|
-
globalRegistry = new UIBridgeRegistry();
|
|
1750
|
-
}
|
|
1751
|
-
return globalRegistry;
|
|
1752
|
-
}
|
|
1753
|
-
function setGlobalRegistry(registry) {
|
|
1754
|
-
globalRegistry = registry;
|
|
1755
|
-
}
|
|
1756
|
-
function resetGlobalRegistry() {
|
|
1757
|
-
globalRegistry?.clear();
|
|
1758
|
-
globalRegistry = null;
|
|
1759
|
-
}
|
|
1760
|
-
|
|
1761
|
-
// src/core/websocket-client.ts
|
|
1762
|
-
function generateId() {
|
|
1763
|
-
return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
1764
|
-
}
|
|
1765
|
-
var UIBridgeWSClient = class {
|
|
1766
|
-
constructor(config) {
|
|
1767
|
-
this.ws = null;
|
|
1768
|
-
this.state = "disconnected";
|
|
1769
|
-
this.clientId = null;
|
|
1770
|
-
this.reconnectAttempts = 0;
|
|
1771
|
-
this.reconnectTimer = null;
|
|
1772
|
-
this.pingTimer = null;
|
|
1773
|
-
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
1774
|
-
// Event listeners
|
|
1775
|
-
this.connectionListeners = /* @__PURE__ */ new Set();
|
|
1776
|
-
this.eventListeners = /* @__PURE__ */ new Map();
|
|
1777
|
-
this.errorListeners = /* @__PURE__ */ new Set();
|
|
1778
|
-
// Current subscriptions
|
|
1779
|
-
this.subscriptions = {};
|
|
1780
|
-
this.config = {
|
|
1781
|
-
url: config.url,
|
|
1782
|
-
autoReconnect: config.autoReconnect ?? true,
|
|
1783
|
-
reconnectDelay: config.reconnectDelay ?? 1e3,
|
|
1784
|
-
maxReconnectAttempts: config.maxReconnectAttempts ?? 10,
|
|
1785
|
-
pingInterval: config.pingInterval ?? 3e4,
|
|
1786
|
-
connectionTimeout: config.connectionTimeout ?? 1e4
|
|
1787
|
-
};
|
|
1789
|
+
registerStateGroup(group) {
|
|
1790
|
+
this.stateGroups.set(group.id, group);
|
|
1791
|
+
return group;
|
|
1788
1792
|
}
|
|
1789
1793
|
/**
|
|
1790
|
-
*
|
|
1794
|
+
* Unregister a state group
|
|
1791
1795
|
*/
|
|
1792
|
-
|
|
1793
|
-
return this.
|
|
1796
|
+
unregisterStateGroup(id) {
|
|
1797
|
+
return this.stateGroups.delete(id);
|
|
1794
1798
|
}
|
|
1795
1799
|
/**
|
|
1796
|
-
* Get
|
|
1800
|
+
* Get a state group
|
|
1797
1801
|
*/
|
|
1798
|
-
|
|
1799
|
-
return this.
|
|
1802
|
+
getStateGroup(id) {
|
|
1803
|
+
return this.stateGroups.get(id);
|
|
1800
1804
|
}
|
|
1801
1805
|
/**
|
|
1802
|
-
*
|
|
1806
|
+
* Get all state groups
|
|
1803
1807
|
*/
|
|
1804
|
-
|
|
1805
|
-
return
|
|
1806
|
-
if (this.ws && this.state === "connected") {
|
|
1807
|
-
resolve();
|
|
1808
|
-
return;
|
|
1809
|
-
}
|
|
1810
|
-
this.setState("connecting");
|
|
1811
|
-
try {
|
|
1812
|
-
this.ws = new WebSocket(this.config.url);
|
|
1813
|
-
} catch (error) {
|
|
1814
|
-
this.setState("disconnected");
|
|
1815
|
-
reject(error);
|
|
1816
|
-
return;
|
|
1817
|
-
}
|
|
1818
|
-
const connectionTimeout = setTimeout(() => {
|
|
1819
|
-
if (this.state === "connecting") {
|
|
1820
|
-
this.ws?.close();
|
|
1821
|
-
this.setState("disconnected");
|
|
1822
|
-
reject(new Error("Connection timeout"));
|
|
1823
|
-
}
|
|
1824
|
-
}, this.config.connectionTimeout);
|
|
1825
|
-
this.ws.onopen = () => {
|
|
1826
|
-
clearTimeout(connectionTimeout);
|
|
1827
|
-
};
|
|
1828
|
-
this.ws.onmessage = (event) => {
|
|
1829
|
-
try {
|
|
1830
|
-
const message = JSON.parse(event.data);
|
|
1831
|
-
this.handleMessage(message);
|
|
1832
|
-
if (message.type === "welcome") {
|
|
1833
|
-
clearTimeout(connectionTimeout);
|
|
1834
|
-
this.reconnectAttempts = 0;
|
|
1835
|
-
this.setState("connected");
|
|
1836
|
-
this.startPingInterval();
|
|
1837
|
-
if (this.subscriptions.events?.length || this.subscriptions.elementIds?.length || this.subscriptions.componentIds?.length) {
|
|
1838
|
-
this.subscribe(this.subscriptions);
|
|
1839
|
-
}
|
|
1840
|
-
resolve();
|
|
1841
|
-
}
|
|
1842
|
-
} catch (error) {
|
|
1843
|
-
console.error("Failed to parse WebSocket message:", error);
|
|
1844
|
-
}
|
|
1845
|
-
};
|
|
1846
|
-
this.ws.onerror = (_event) => {
|
|
1847
|
-
clearTimeout(connectionTimeout);
|
|
1848
|
-
const error = new Error("WebSocket error");
|
|
1849
|
-
this.notifyError(error);
|
|
1850
|
-
if (this.state === "connecting") {
|
|
1851
|
-
reject(error);
|
|
1852
|
-
}
|
|
1853
|
-
};
|
|
1854
|
-
this.ws.onclose = () => {
|
|
1855
|
-
clearTimeout(connectionTimeout);
|
|
1856
|
-
this.stopPingInterval();
|
|
1857
|
-
this.clientId = null;
|
|
1858
|
-
const wasConnected = this.state === "connected";
|
|
1859
|
-
this.setState("disconnected");
|
|
1860
|
-
for (const [_id, pending] of this.pendingRequests) {
|
|
1861
|
-
clearTimeout(pending.timeout);
|
|
1862
|
-
pending.reject(new Error("Connection closed"));
|
|
1863
|
-
}
|
|
1864
|
-
this.pendingRequests.clear();
|
|
1865
|
-
if (wasConnected && this.config.autoReconnect && (this.config.maxReconnectAttempts === 0 || this.reconnectAttempts < this.config.maxReconnectAttempts)) {
|
|
1866
|
-
this.scheduleReconnect();
|
|
1867
|
-
}
|
|
1868
|
-
};
|
|
1869
|
-
});
|
|
1808
|
+
getAllStateGroups() {
|
|
1809
|
+
return Array.from(this.stateGroups.values());
|
|
1870
1810
|
}
|
|
1871
1811
|
/**
|
|
1872
|
-
*
|
|
1812
|
+
* Register a transition
|
|
1873
1813
|
*/
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
this.reconnectTimer = null;
|
|
1878
|
-
}
|
|
1879
|
-
this.stopPingInterval();
|
|
1880
|
-
if (this.ws) {
|
|
1881
|
-
this.ws.close();
|
|
1882
|
-
this.ws = null;
|
|
1883
|
-
}
|
|
1884
|
-
this.setState("disconnected");
|
|
1814
|
+
registerTransition(transition) {
|
|
1815
|
+
this.transitions.set(transition.id, transition);
|
|
1816
|
+
return transition;
|
|
1885
1817
|
}
|
|
1886
1818
|
/**
|
|
1887
|
-
*
|
|
1819
|
+
* Unregister a transition
|
|
1888
1820
|
*/
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
const response = await this.sendRequest({
|
|
1892
|
-
id: generateId(),
|
|
1893
|
-
type: "subscribe",
|
|
1894
|
-
timestamp: Date.now(),
|
|
1895
|
-
payload: options
|
|
1896
|
-
});
|
|
1897
|
-
return response.events;
|
|
1821
|
+
unregisterTransition(id) {
|
|
1822
|
+
return this.transitions.delete(id);
|
|
1898
1823
|
}
|
|
1899
1824
|
/**
|
|
1900
|
-
*
|
|
1825
|
+
* Get a transition
|
|
1901
1826
|
*/
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
this.subscriptions.events = this.subscriptions.events?.filter((e) => !events.includes(e));
|
|
1905
|
-
} else {
|
|
1906
|
-
this.subscriptions = {};
|
|
1907
|
-
}
|
|
1908
|
-
const response = await this.sendRequest({
|
|
1909
|
-
id: generateId(),
|
|
1910
|
-
type: "unsubscribe",
|
|
1911
|
-
timestamp: Date.now(),
|
|
1912
|
-
payload: { events }
|
|
1913
|
-
});
|
|
1914
|
-
return response.events;
|
|
1827
|
+
getTransition(id) {
|
|
1828
|
+
return this.transitions.get(id);
|
|
1915
1829
|
}
|
|
1916
1830
|
/**
|
|
1917
|
-
*
|
|
1831
|
+
* Get all transitions
|
|
1918
1832
|
*/
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
id: generateId(),
|
|
1922
|
-
type: "find",
|
|
1923
|
-
timestamp: Date.now(),
|
|
1924
|
-
payload: options
|
|
1925
|
-
});
|
|
1926
|
-
return response.elements;
|
|
1833
|
+
getAllTransitions() {
|
|
1834
|
+
return Array.from(this.transitions.values());
|
|
1927
1835
|
}
|
|
1928
1836
|
/**
|
|
1929
|
-
*
|
|
1930
|
-
* @deprecated Use find() instead
|
|
1837
|
+
* Get currently active states
|
|
1931
1838
|
*/
|
|
1932
|
-
|
|
1933
|
-
return this.
|
|
1839
|
+
getActiveStates() {
|
|
1840
|
+
return Array.from(this.activeStates);
|
|
1934
1841
|
}
|
|
1935
1842
|
/**
|
|
1936
|
-
*
|
|
1843
|
+
* Check if a state is active
|
|
1937
1844
|
*/
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
id: generateId(),
|
|
1941
|
-
type: "getElement",
|
|
1942
|
-
timestamp: Date.now(),
|
|
1943
|
-
payload: { elementId, includeState }
|
|
1944
|
-
});
|
|
1945
|
-
return response.element;
|
|
1845
|
+
isStateActive(id) {
|
|
1846
|
+
return this.activeStates.has(id);
|
|
1946
1847
|
}
|
|
1947
1848
|
/**
|
|
1948
|
-
*
|
|
1849
|
+
* Activate a state
|
|
1949
1850
|
*/
|
|
1950
|
-
|
|
1951
|
-
const
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1851
|
+
activateState(id) {
|
|
1852
|
+
const state = this.states.get(id);
|
|
1853
|
+
if (!state) {
|
|
1854
|
+
return false;
|
|
1855
|
+
}
|
|
1856
|
+
for (const activeId of this.activeStates) {
|
|
1857
|
+
const activeState = this.states.get(activeId);
|
|
1858
|
+
if (activeState?.blocking && activeState.id !== id) {
|
|
1859
|
+
return false;
|
|
1860
|
+
}
|
|
1861
|
+
if (activeState?.blocks?.includes(id)) {
|
|
1862
|
+
return false;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
const wasActive = this.activeStates.has(id);
|
|
1866
|
+
this.activeStates.add(id);
|
|
1867
|
+
if (!wasActive) {
|
|
1868
|
+
this.emit("element:stateChanged", {
|
|
1869
|
+
stateId: id,
|
|
1870
|
+
active: true,
|
|
1871
|
+
activeStates: this.getActiveStates()
|
|
1872
|
+
});
|
|
1873
|
+
}
|
|
1874
|
+
return true;
|
|
1957
1875
|
}
|
|
1958
1876
|
/**
|
|
1959
|
-
*
|
|
1877
|
+
* Deactivate a state
|
|
1960
1878
|
*/
|
|
1961
|
-
|
|
1962
|
-
const
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1879
|
+
deactivateState(id) {
|
|
1880
|
+
const wasActive = this.activeStates.has(id);
|
|
1881
|
+
this.activeStates.delete(id);
|
|
1882
|
+
if (wasActive) {
|
|
1883
|
+
this.emit("element:stateChanged", {
|
|
1884
|
+
stateId: id,
|
|
1885
|
+
active: false,
|
|
1886
|
+
activeStates: this.getActiveStates()
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
return wasActive;
|
|
1969
1890
|
}
|
|
1970
1891
|
/**
|
|
1971
|
-
*
|
|
1892
|
+
* Activate multiple states
|
|
1972
1893
|
*/
|
|
1973
|
-
|
|
1974
|
-
const
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
}
|
|
1980
|
-
return
|
|
1894
|
+
activateStates(ids) {
|
|
1895
|
+
const activated = [];
|
|
1896
|
+
for (const id of ids) {
|
|
1897
|
+
if (this.activateState(id)) {
|
|
1898
|
+
activated.push(id);
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
return activated;
|
|
1981
1902
|
}
|
|
1982
1903
|
/**
|
|
1983
|
-
*
|
|
1904
|
+
* Deactivate multiple states
|
|
1984
1905
|
*/
|
|
1985
|
-
|
|
1986
|
-
const
|
|
1987
|
-
const
|
|
1988
|
-
if (
|
|
1989
|
-
|
|
1990
|
-
currentStep: message.payload.currentStep,
|
|
1991
|
-
totalSteps: message.payload.totalSteps,
|
|
1992
|
-
step: {
|
|
1993
|
-
id: message.payload.step.id,
|
|
1994
|
-
status: message.payload.step.status
|
|
1995
|
-
}
|
|
1996
|
-
});
|
|
1906
|
+
deactivateStates(ids) {
|
|
1907
|
+
const deactivated = [];
|
|
1908
|
+
for (const id of ids) {
|
|
1909
|
+
if (this.deactivateState(id)) {
|
|
1910
|
+
deactivated.push(id);
|
|
1997
1911
|
}
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
{
|
|
2001
|
-
id,
|
|
2002
|
-
type: "executeWorkflow",
|
|
2003
|
-
timestamp: Date.now(),
|
|
2004
|
-
payload: { workflowId, params, streamProgress: !!onProgress }
|
|
2005
|
-
},
|
|
2006
|
-
progressHandler
|
|
2007
|
-
);
|
|
2008
|
-
return response;
|
|
1912
|
+
}
|
|
1913
|
+
return deactivated;
|
|
2009
1914
|
}
|
|
2010
1915
|
/**
|
|
2011
|
-
*
|
|
1916
|
+
* Activate a state group (all states in the group)
|
|
2012
1917
|
*/
|
|
2013
|
-
|
|
2014
|
-
this.
|
|
2015
|
-
|
|
1918
|
+
activateStateGroup(groupId) {
|
|
1919
|
+
const group = this.stateGroups.get(groupId);
|
|
1920
|
+
if (!group) return [];
|
|
1921
|
+
return this.activateStates(group.states);
|
|
2016
1922
|
}
|
|
2017
1923
|
/**
|
|
2018
|
-
*
|
|
1924
|
+
* Deactivate a state group (all states in the group)
|
|
2019
1925
|
*/
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
this.eventListeners.get(eventType).add(listener);
|
|
2025
|
-
return () => this.eventListeners.get(eventType)?.delete(listener);
|
|
1926
|
+
deactivateStateGroup(groupId) {
|
|
1927
|
+
const group = this.stateGroups.get(groupId);
|
|
1928
|
+
if (!group) return [];
|
|
1929
|
+
return this.deactivateStates(group.states);
|
|
2026
1930
|
}
|
|
2027
1931
|
/**
|
|
2028
|
-
*
|
|
1932
|
+
* Check if a transition can be executed from current state
|
|
2029
1933
|
*/
|
|
2030
|
-
|
|
2031
|
-
this.
|
|
2032
|
-
|
|
1934
|
+
canExecuteTransition(transitionId) {
|
|
1935
|
+
const transition = this.transitions.get(transitionId);
|
|
1936
|
+
if (!transition) return false;
|
|
1937
|
+
return transition.fromStates.some((stateId) => this.activeStates.has(stateId));
|
|
2033
1938
|
}
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
1939
|
+
/**
|
|
1940
|
+
* Execute a transition
|
|
1941
|
+
*/
|
|
1942
|
+
async executeTransition(transitionId) {
|
|
1943
|
+
const startTime = performance.now();
|
|
1944
|
+
const transition = this.transitions.get(transitionId);
|
|
1945
|
+
if (!transition) {
|
|
1946
|
+
return {
|
|
1947
|
+
success: false,
|
|
1948
|
+
activatedStates: [],
|
|
1949
|
+
deactivatedStates: [],
|
|
1950
|
+
error: `Transition not found: ${transitionId}`,
|
|
1951
|
+
durationMs: performance.now() - startTime
|
|
1952
|
+
};
|
|
2043
1953
|
}
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
this.handleResponse(message);
|
|
2060
|
-
break;
|
|
2061
|
-
case "error":
|
|
2062
|
-
if (message.requestId) {
|
|
2063
|
-
this.handleResponse({
|
|
2064
|
-
...message,
|
|
2065
|
-
type: "response",
|
|
2066
|
-
requestId: message.requestId,
|
|
2067
|
-
payload: {
|
|
2068
|
-
success: false,
|
|
2069
|
-
error: message.payload.message
|
|
2070
|
-
}
|
|
2071
|
-
});
|
|
2072
|
-
} else {
|
|
2073
|
-
this.notifyError(new Error(message.payload.message));
|
|
1954
|
+
if (!this.canExecuteTransition(transitionId)) {
|
|
1955
|
+
return {
|
|
1956
|
+
success: false,
|
|
1957
|
+
activatedStates: [],
|
|
1958
|
+
deactivatedStates: [],
|
|
1959
|
+
error: "Precondition not met: none of the fromStates are active",
|
|
1960
|
+
failedPhase: "precondition",
|
|
1961
|
+
durationMs: performance.now() - startTime
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1964
|
+
try {
|
|
1965
|
+
const deactivated = this.deactivateStates(transition.exitStates);
|
|
1966
|
+
if (transition.exitGroups) {
|
|
1967
|
+
for (const groupId of transition.exitGroups) {
|
|
1968
|
+
deactivated.push(...this.deactivateStateGroup(groupId));
|
|
2074
1969
|
}
|
|
2075
|
-
|
|
1970
|
+
}
|
|
1971
|
+
const activated = this.activateStates(transition.activateStates);
|
|
1972
|
+
if (transition.activateGroups) {
|
|
1973
|
+
for (const groupId of transition.activateGroups) {
|
|
1974
|
+
activated.push(...this.activateStateGroup(groupId));
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
return {
|
|
1978
|
+
success: true,
|
|
1979
|
+
activatedStates: activated,
|
|
1980
|
+
deactivatedStates: deactivated,
|
|
1981
|
+
durationMs: performance.now() - startTime
|
|
1982
|
+
};
|
|
1983
|
+
} catch (error) {
|
|
1984
|
+
return {
|
|
1985
|
+
success: false,
|
|
1986
|
+
activatedStates: [],
|
|
1987
|
+
deactivatedStates: [],
|
|
1988
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1989
|
+
failedPhase: "execution",
|
|
1990
|
+
durationMs: performance.now() - startTime
|
|
1991
|
+
};
|
|
2076
1992
|
}
|
|
2077
1993
|
}
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
1994
|
+
/**
|
|
1995
|
+
* Find a path from current state to target states
|
|
1996
|
+
*
|
|
1997
|
+
* Uses a simple BFS algorithm for pathfinding.
|
|
1998
|
+
* For more advanced pathfinding (Dijkstra, A*), use the Python state manager service.
|
|
1999
|
+
*/
|
|
2000
|
+
findPath(targetStates) {
|
|
2001
|
+
if (targetStates.every((t) => this.activeStates.has(t))) {
|
|
2002
|
+
return {
|
|
2003
|
+
found: true,
|
|
2004
|
+
transitions: [],
|
|
2005
|
+
totalCost: 0,
|
|
2006
|
+
targetStates,
|
|
2007
|
+
estimatedSteps: 0
|
|
2008
|
+
};
|
|
2089
2009
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2010
|
+
const queue = [
|
|
2011
|
+
{ activeStates: new Set(this.activeStates), path: [], cost: 0 }
|
|
2012
|
+
];
|
|
2013
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2014
|
+
while (queue.length > 0) {
|
|
2015
|
+
const current = queue.shift();
|
|
2016
|
+
const stateKey = Array.from(current.activeStates).sort().join(",");
|
|
2017
|
+
if (visited.has(stateKey)) continue;
|
|
2018
|
+
visited.add(stateKey);
|
|
2019
|
+
if (targetStates.every((t) => current.activeStates.has(t))) {
|
|
2020
|
+
return {
|
|
2021
|
+
found: true,
|
|
2022
|
+
transitions: current.path,
|
|
2023
|
+
totalCost: current.cost,
|
|
2024
|
+
targetStates,
|
|
2025
|
+
estimatedSteps: current.path.length
|
|
2026
|
+
};
|
|
2100
2027
|
}
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2028
|
+
for (const transition of this.transitions.values()) {
|
|
2029
|
+
const canExecute = transition.fromStates.some((s) => current.activeStates.has(s));
|
|
2030
|
+
if (!canExecute) continue;
|
|
2031
|
+
const newActive = new Set(current.activeStates);
|
|
2032
|
+
for (const s of transition.exitStates) newActive.delete(s);
|
|
2033
|
+
for (const s of transition.activateStates) newActive.add(s);
|
|
2034
|
+
const newCost = current.cost + (transition.pathCost ?? 1);
|
|
2035
|
+
queue.push({
|
|
2036
|
+
activeStates: newActive,
|
|
2037
|
+
path: [...current.path, transition.id],
|
|
2038
|
+
cost: newCost
|
|
2039
|
+
});
|
|
2110
2040
|
}
|
|
2111
2041
|
}
|
|
2042
|
+
return {
|
|
2043
|
+
found: false,
|
|
2044
|
+
transitions: [],
|
|
2045
|
+
totalCost: 0,
|
|
2046
|
+
targetStates,
|
|
2047
|
+
estimatedSteps: 0
|
|
2048
|
+
};
|
|
2112
2049
|
}
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2050
|
+
/**
|
|
2051
|
+
* Navigate to target states using pathfinding
|
|
2052
|
+
*/
|
|
2053
|
+
async navigateTo(targetStates) {
|
|
2054
|
+
const startTime = performance.now();
|
|
2055
|
+
const path = this.findPath(targetStates);
|
|
2056
|
+
if (!path.found) {
|
|
2057
|
+
return {
|
|
2058
|
+
success: false,
|
|
2059
|
+
path,
|
|
2060
|
+
executedTransitions: [],
|
|
2061
|
+
finalActiveStates: this.getActiveStates(),
|
|
2062
|
+
error: `No path found to target states: ${targetStates.join(", ")}`,
|
|
2063
|
+
durationMs: performance.now() - startTime
|
|
2064
|
+
};
|
|
2120
2065
|
}
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
if (!
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
this.pendingRequests.set(message.id, {
|
|
2133
|
-
resolve,
|
|
2134
|
-
reject,
|
|
2135
|
-
timeout
|
|
2136
|
-
});
|
|
2137
|
-
if (progressHandler && this.ws) {
|
|
2138
|
-
const originalHandler = this.ws.onmessage;
|
|
2139
|
-
const wsRef = this.ws;
|
|
2140
|
-
const wrappedHandler = (event) => {
|
|
2141
|
-
try {
|
|
2142
|
-
const msg = JSON.parse(event.data);
|
|
2143
|
-
if (msg.type === "workflowProgress") {
|
|
2144
|
-
progressHandler(msg);
|
|
2145
|
-
}
|
|
2146
|
-
} catch {
|
|
2147
|
-
}
|
|
2148
|
-
if (originalHandler) {
|
|
2149
|
-
originalHandler.call(wsRef, event);
|
|
2150
|
-
}
|
|
2066
|
+
const executedTransitions = [];
|
|
2067
|
+
for (const transitionId of path.transitions) {
|
|
2068
|
+
const result = await this.executeTransition(transitionId);
|
|
2069
|
+
if (!result.success) {
|
|
2070
|
+
return {
|
|
2071
|
+
success: false,
|
|
2072
|
+
path,
|
|
2073
|
+
executedTransitions,
|
|
2074
|
+
finalActiveStates: this.getActiveStates(),
|
|
2075
|
+
error: result.error,
|
|
2076
|
+
durationMs: performance.now() - startTime
|
|
2151
2077
|
};
|
|
2152
|
-
this.ws.onmessage = wrappedHandler;
|
|
2153
2078
|
}
|
|
2154
|
-
|
|
2155
|
-
}
|
|
2079
|
+
executedTransitions.push(transitionId);
|
|
2080
|
+
}
|
|
2081
|
+
return {
|
|
2082
|
+
success: true,
|
|
2083
|
+
path,
|
|
2084
|
+
executedTransitions,
|
|
2085
|
+
finalActiveStates: this.getActiveStates(),
|
|
2086
|
+
durationMs: performance.now() - startTime
|
|
2087
|
+
};
|
|
2156
2088
|
}
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
this.
|
|
2167
|
-
|
|
2168
|
-
});
|
|
2169
|
-
}, delay);
|
|
2089
|
+
/**
|
|
2090
|
+
* Create a state snapshot
|
|
2091
|
+
*/
|
|
2092
|
+
createStateSnapshot() {
|
|
2093
|
+
return {
|
|
2094
|
+
timestamp: Date.now(),
|
|
2095
|
+
activeStates: this.getActiveStates(),
|
|
2096
|
+
states: this.getAllStates(),
|
|
2097
|
+
groups: this.getAllStateGroups(),
|
|
2098
|
+
transitions: this.getAllTransitions()
|
|
2099
|
+
};
|
|
2170
2100
|
}
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
)
|
|
2182
|
-
|
|
2183
|
-
|
|
2101
|
+
/**
|
|
2102
|
+
* Create a snapshot of the current state
|
|
2103
|
+
*/
|
|
2104
|
+
createSnapshot() {
|
|
2105
|
+
return {
|
|
2106
|
+
timestamp: Date.now(),
|
|
2107
|
+
elements: this.getAllElements().map((el) => ({
|
|
2108
|
+
id: el.id,
|
|
2109
|
+
type: el.type,
|
|
2110
|
+
label: el.label,
|
|
2111
|
+
identifier: el.getIdentifier(),
|
|
2112
|
+
state: el.getState(),
|
|
2113
|
+
actions: el.actions,
|
|
2114
|
+
customActions: el.customActions ? Object.keys(el.customActions) : void 0
|
|
2115
|
+
})),
|
|
2116
|
+
components: this.getAllComponents().map((comp) => ({
|
|
2117
|
+
id: comp.id,
|
|
2118
|
+
name: comp.name,
|
|
2119
|
+
description: comp.description,
|
|
2120
|
+
actions: comp.actions.map((a) => a.id),
|
|
2121
|
+
elementIds: comp.elementIds
|
|
2122
|
+
})),
|
|
2123
|
+
workflows: this.getAllWorkflows().map((wf) => ({
|
|
2124
|
+
id: wf.id,
|
|
2125
|
+
name: wf.name,
|
|
2126
|
+
description: wf.description,
|
|
2127
|
+
stepCount: wf.steps.length
|
|
2128
|
+
}))
|
|
2129
|
+
};
|
|
2184
2130
|
}
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2131
|
+
/**
|
|
2132
|
+
* Clear all registrations
|
|
2133
|
+
*/
|
|
2134
|
+
clear() {
|
|
2135
|
+
this.elements.clear();
|
|
2136
|
+
this.components.clear();
|
|
2137
|
+
this.workflows.clear();
|
|
2138
|
+
this.eventListeners.clear();
|
|
2139
|
+
this.states.clear();
|
|
2140
|
+
this.stateGroups.clear();
|
|
2141
|
+
this.transitions.clear();
|
|
2142
|
+
this.activeStates.clear();
|
|
2143
|
+
}
|
|
2144
|
+
/**
|
|
2145
|
+
* Get registry statistics
|
|
2146
|
+
*/
|
|
2147
|
+
getStats() {
|
|
2148
|
+
const elements = this.getAllElements();
|
|
2149
|
+
const components = this.getAllComponents();
|
|
2150
|
+
return {
|
|
2151
|
+
elementCount: elements.length,
|
|
2152
|
+
componentCount: components.length,
|
|
2153
|
+
workflowCount: this.workflows.size,
|
|
2154
|
+
mountedElementCount: elements.filter((e) => e.mounted).length,
|
|
2155
|
+
mountedComponentCount: components.filter((c) => c.mounted).length,
|
|
2156
|
+
stateCount: this.states.size,
|
|
2157
|
+
stateGroupCount: this.stateGroups.size,
|
|
2158
|
+
transitionCount: this.transitions.size,
|
|
2159
|
+
activeStateCount: this.activeStates.size
|
|
2160
|
+
};
|
|
2190
2161
|
}
|
|
2191
2162
|
};
|
|
2192
|
-
|
|
2193
|
-
|
|
2163
|
+
var globalRegistry = null;
|
|
2164
|
+
function setGlobalRegistry(registry) {
|
|
2165
|
+
globalRegistry = registry;
|
|
2166
|
+
}
|
|
2167
|
+
function resetGlobalRegistry() {
|
|
2168
|
+
globalRegistry?.clear();
|
|
2169
|
+
globalRegistry = null;
|
|
2194
2170
|
}
|
|
2195
2171
|
|
|
2196
2172
|
// src/control/action-executor.ts
|
|
@@ -4095,11 +4071,31 @@ function useUIComponent(options) {
|
|
|
4095
4071
|
const registeredRef = react.useRef(false);
|
|
4096
4072
|
const actionsRef = react.useRef(options.actions || []);
|
|
4097
4073
|
const elementIdsRef = react.useRef(options.elementIds || []);
|
|
4074
|
+
const stateRef = react.useRef(options.state);
|
|
4075
|
+
const computedRef = react.useRef(options.computed);
|
|
4098
4076
|
const { id, name, description, autoRegister = true } = options;
|
|
4099
4077
|
react.useEffect(() => {
|
|
4100
4078
|
actionsRef.current = options.actions || [];
|
|
4101
4079
|
elementIdsRef.current = options.elementIds || [];
|
|
4102
|
-
|
|
4080
|
+
stateRef.current = options.state;
|
|
4081
|
+
computedRef.current = options.computed;
|
|
4082
|
+
}, [options.actions, options.elementIds, options.state, options.computed]);
|
|
4083
|
+
const createGetComputed = react.useCallback(() => {
|
|
4084
|
+
return () => {
|
|
4085
|
+
const computed = computedRef.current;
|
|
4086
|
+
if (!computed) return {};
|
|
4087
|
+
const result = {};
|
|
4088
|
+
for (const [key, def] of Object.entries(computed)) {
|
|
4089
|
+
try {
|
|
4090
|
+
const getter = typeof def === "function" ? def : def.getter;
|
|
4091
|
+
result[key] = getter();
|
|
4092
|
+
} catch {
|
|
4093
|
+
result[key] = void 0;
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
return result;
|
|
4097
|
+
};
|
|
4098
|
+
}, []);
|
|
4103
4099
|
const register = react.useCallback(() => {
|
|
4104
4100
|
if (!bridge || registeredRef.current) return;
|
|
4105
4101
|
bridge.registry.registerComponent(id, {
|
|
@@ -4111,10 +4107,12 @@ function useUIComponent(options) {
|
|
|
4111
4107
|
description: a.description,
|
|
4112
4108
|
handler: a.handler
|
|
4113
4109
|
})),
|
|
4114
|
-
elementIds: elementIdsRef.current
|
|
4110
|
+
elementIds: elementIdsRef.current,
|
|
4111
|
+
getState: stateRef.current,
|
|
4112
|
+
getComputed: createGetComputed()
|
|
4115
4113
|
});
|
|
4116
4114
|
registeredRef.current = true;
|
|
4117
|
-
}, [bridge, id, name, description]);
|
|
4115
|
+
}, [bridge, id, name, description, createGetComputed]);
|
|
4118
4116
|
const unregister = react.useCallback(() => {
|
|
4119
4117
|
if (!bridge || !registeredRef.current) return;
|
|
4120
4118
|
bridge.registry.unregisterComponent(id);
|
|
@@ -8718,7 +8716,6 @@ exports.DOMChangeObserver = DOMChangeObserver;
|
|
|
8718
8716
|
exports.DefaultActionExecutor = DefaultActionExecutor;
|
|
8719
8717
|
exports.DefaultWorkflowEngine = DefaultWorkflowEngine;
|
|
8720
8718
|
exports.ErrorCodes = ErrorCodes;
|
|
8721
|
-
exports.ID_ATTRIBUTES = ID_ATTRIBUTES;
|
|
8722
8719
|
exports.InMemoryRenderLogStorage = InMemoryRenderLogStorage;
|
|
8723
8720
|
exports.InfoPanel = InfoPanel;
|
|
8724
8721
|
exports.Inspector = Inspector;
|
|
@@ -8730,7 +8727,6 @@ exports.SearchEngine = SearchEngine;
|
|
|
8730
8727
|
exports.SemanticDiffManager = SemanticDiffManager;
|
|
8731
8728
|
exports.SemanticSnapshotManager = SemanticSnapshotManager;
|
|
8732
8729
|
exports.UIBridgeProvider = UIBridgeProvider;
|
|
8733
|
-
exports.UIBridgeRegistry = UIBridgeRegistry;
|
|
8734
8730
|
exports.UIBridgeWSClient = UIBridgeWSClient;
|
|
8735
8731
|
exports.areSynonyms = areSynonyms;
|
|
8736
8732
|
exports.captureDOMSnapshot = captureDOMSnapshot;
|
|
@@ -8739,7 +8735,6 @@ exports.computeDiff = computeDiff;
|
|
|
8739
8735
|
exports.createActionExecutor = createActionExecutor;
|
|
8740
8736
|
exports.createAssertionExecutor = createAssertionExecutor;
|
|
8741
8737
|
exports.createDiffManager = createDiffManager;
|
|
8742
|
-
exports.createElementIdentifier = createElementIdentifier;
|
|
8743
8738
|
exports.createErrorContext = createErrorContext;
|
|
8744
8739
|
exports.createMetricsCollector = createMetricsCollector;
|
|
8745
8740
|
exports.createNLActionExecutor = createNLActionExecutor;
|
|
@@ -8751,19 +8746,15 @@ exports.createWSClient = createWSClient;
|
|
|
8751
8746
|
exports.createWorkflowEngine = createWorkflowEngine;
|
|
8752
8747
|
exports.describeAction = describeAction;
|
|
8753
8748
|
exports.describeDiff = describeDiff;
|
|
8754
|
-
exports.elementMatchesIdentifier = elementMatchesIdentifier;
|
|
8755
8749
|
exports.extractModifiers = extractModifiers;
|
|
8756
|
-
exports.findAllElementsByIdentifier = findAllElementsByIdentifier;
|
|
8757
8750
|
exports.findAllMatches = findAllMatches;
|
|
8758
8751
|
exports.findBestMatch = findBestMatch;
|
|
8759
|
-
exports.findElementByIdentifier = findElementByIdentifier;
|
|
8760
8752
|
exports.formatDuration = formatDuration;
|
|
8761
8753
|
exports.formatErrorContext = formatErrorContext;
|
|
8762
8754
|
exports.formatPercentage = formatPercentage;
|
|
8763
8755
|
exports.fuzzyContains = fuzzyContains;
|
|
8764
8756
|
exports.fuzzyMatch = fuzzyMatch;
|
|
8765
8757
|
exports.generateAliases = generateAliases;
|
|
8766
|
-
exports.generateCSSSelector = generateCSSSelector;
|
|
8767
8758
|
exports.generateDescription = generateDescription;
|
|
8768
8759
|
exports.generateDiffSummary = generateDiffSummary;
|
|
8769
8760
|
exports.generateElementDescription = generateElementDescription;
|
|
@@ -8772,10 +8763,7 @@ exports.generatePageSummary = generatePageSummary;
|
|
|
8772
8763
|
exports.generatePurpose = generatePurpose;
|
|
8773
8764
|
exports.generateSnapshotSummary = generateSnapshotSummary;
|
|
8774
8765
|
exports.generateSuggestedActions = generateSuggestedActions;
|
|
8775
|
-
exports.generateXPath = generateXPath;
|
|
8776
|
-
exports.getBestIdentifier = getBestIdentifier;
|
|
8777
8766
|
exports.getBestRecoverySuggestion = getBestRecoverySuggestion;
|
|
8778
|
-
exports.getGlobalRegistry = getGlobalRegistry;
|
|
8779
8767
|
exports.getSynonyms = getSynonyms;
|
|
8780
8768
|
exports.hasSignificantChanges = hasSignificantChanges;
|
|
8781
8769
|
exports.inferPageType = inferPageType;
|
|
@@ -8788,8 +8776,6 @@ exports.ngramSimilarity = ngramSimilarity;
|
|
|
8788
8776
|
exports.normalizeString = normalizeString;
|
|
8789
8777
|
exports.parseNLInstruction = parseNLInstruction;
|
|
8790
8778
|
exports.parseNLInstructions = parseNLInstructions;
|
|
8791
|
-
exports.resetGlobalRegistry = resetGlobalRegistry;
|
|
8792
|
-
exports.setGlobalRegistry = setGlobalRegistry;
|
|
8793
8779
|
exports.splitCompoundInstruction = splitCompoundInstruction;
|
|
8794
8780
|
exports.tokenSimilarity = tokenSimilarity;
|
|
8795
8781
|
exports.tokenize = tokenize;
|