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