@qontinui/ui-bridge 0.2.0 → 0.3.1
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 +312 -155
- package/dist/ai/index.d.ts +312 -155
- package/dist/ai/index.js +2363 -67
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/index.mjs +2328 -68
- package/dist/ai/index.mjs.map +1 -1
- package/dist/annotations/index.d.mts +218 -0
- package/dist/annotations/index.d.ts +218 -0
- package/dist/annotations/index.js +246 -0
- package/dist/annotations/index.js.map +1 -0
- package/dist/annotations/index.mjs +241 -0
- package/dist/annotations/index.mjs.map +1 -0
- package/dist/assertions-BSR3afVr.d.ts +161 -0
- package/dist/assertions-CTw1hfOx.d.mts +161 -0
- package/dist/babel-plugin/index.js +504 -0
- package/dist/babel-plugin/index.js.map +1 -0
- package/dist/babel-plugin/index.mjs +488 -0
- package/dist/babel-plugin/index.mjs.map +1 -0
- package/dist/browser-capture-Bms60T6f.d.mts +47 -0
- package/dist/browser-capture-CsTU29mb.d.ts +47 -0
- package/dist/control/index.d.mts +26 -7
- package/dist/control/index.d.ts +26 -7
- package/dist/control/index.js +276 -48
- package/dist/control/index.js.map +1 -1
- package/dist/control/index.mjs +276 -48
- package/dist/control/index.mjs.map +1 -1
- 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 +5 -3
- package/dist/debug/index.d.ts +5 -3
- package/dist/debug/index.js +925 -1
- package/dist/debug/index.js.map +1 -1
- package/dist/debug/index.mjs +924 -2
- package/dist/debug/index.mjs.map +1 -1
- package/dist/index.d.mts +13 -9
- package/dist/index.d.ts +13 -9
- package/dist/index.js +8310 -3777
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8246 -3766
- package/dist/index.mjs.map +1 -1
- package/dist/{metrics-NC3csD0R.d.mts → metrics-DuA2qIIz.d.mts} +2 -2
- package/dist/{metrics-C9XRi_mL.d.ts → metrics-KFAAKNEB.d.ts} +2 -2
- package/dist/native/control/index.js +448 -0
- package/dist/native/control/index.js.map +1 -0
- package/dist/native/control/index.mjs +445 -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 +408 -0
- package/dist/native/debug/index.js.map +1 -0
- package/dist/native/debug/index.mjs +406 -0
- package/dist/native/debug/index.mjs.map +1 -0
- package/dist/native/index.js +2232 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native/index.mjs +2204 -0
- package/dist/native/index.mjs.map +1 -0
- package/dist/native/react/index.js +1377 -0
- package/dist/native/react/index.js.map +1 -0
- package/dist/native/react/index.mjs +1365 -0
- package/dist/native/react/index.mjs.map +1 -0
- package/dist/native/server/index.js +440 -0
- package/dist/native/server/index.js.map +1 -0
- package/dist/native/server/index.mjs +435 -0
- package/dist/native/server/index.mjs.map +1 -0
- package/dist/react/index.d.mts +121 -9
- package/dist/react/index.d.ts +121 -9
- package/dist/react/index.js +2239 -91
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +2239 -92
- package/dist/react/index.mjs.map +1 -1
- package/dist/{registry-CIEDjbQ9.d.ts → registry-C6dDtn1v.d.ts} +34 -15
- package/dist/{registry-SsSDq46X.d.mts → registry-POtcxnal.d.mts} +34 -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 +37 -0
- package/dist/server/express.d.ts +37 -0
- package/dist/server/express.js +298 -0
- package/dist/server/express.js.map +1 -0
- package/dist/server/express.mjs +294 -0
- package/dist/server/express.mjs.map +1 -0
- package/dist/server/handlers.d.mts +124 -0
- package/dist/server/handlers.d.ts +124 -0
- package/dist/server/handlers.js +7183 -0
- package/dist/server/handlers.js.map +1 -0
- package/dist/server/handlers.mjs +7180 -0
- package/dist/server/handlers.mjs.map +1 -0
- package/dist/server/index.d.mts +12 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.js +8384 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +8369 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/server/nextjs.d.mts +128 -0
- package/dist/server/nextjs.d.ts +128 -0
- package/dist/server/nextjs.js +390 -0
- package/dist/server/nextjs.js.map +1 -0
- package/dist/server/nextjs.mjs +385 -0
- package/dist/server/nextjs.mjs.map +1 -0
- package/dist/server/standalone.d.mts +7 -0
- package/dist/server/standalone.d.ts +7 -0
- package/dist/server/standalone.js +845 -0
- package/dist/server/standalone.js.map +1 -0
- package/dist/server/standalone.mjs +841 -0
- package/dist/server/standalone.mjs.map +1 -0
- package/dist/specs/index.d.mts +365 -0
- package/dist/specs/index.d.ts +365 -0
- package/dist/specs/index.js +2809 -0
- package/dist/specs/index.js.map +1 -0
- package/dist/specs/index.mjs +2786 -0
- package/dist/specs/index.mjs.map +1 -0
- package/dist/standalone-B6GLIEmR.d.ts +216 -0
- package/dist/standalone-CjdYqj3P.d.mts +216 -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-B2EfvEaq.d.ts +236 -0
- package/dist/{types-Dr6tH-bm.d.mts → types-C7gVYRnF.d.ts} +72 -2
- package/dist/{types-oCTrRxSw.d.ts → types-CJGrBEhC.d.mts} +72 -2
- package/dist/types-CebMQj76.d.ts +1275 -0
- package/dist/types-D_ypYl3T.d.mts +1275 -0
- package/dist/types-UBtp7R0u.d.mts +132 -0
- package/dist/types-UBtp7R0u.d.ts +132 -0
- package/dist/types-gO696T_t.d.mts +236 -0
- package/dist/{types-CPMbN_Iw.d.mts → types-suaYwWWg.d.mts} +519 -152
- package/dist/{types-CPMbN_Iw.d.ts → types-suaYwWWg.d.ts} +519 -152
- package/package.json +123 -4
- package/swc-plugin-wasm/ui_bridge_swc_plugin.wasm +0 -0
- package/dist/types-BvCfFuEV.d.ts +0 -534
- package/dist/types-CFT3Dnx4.d.mts +0 -534
- package/dist/websocket-client-CX4QJesI.d.ts +0 -124
- package/dist/websocket-client-C_Na0OSp.d.mts +0 -124
|
@@ -0,0 +1,2232 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
var reactNative = require('react-native');
|
|
6
|
+
|
|
7
|
+
// src/native/core/registry.ts
|
|
8
|
+
function inferActions(type) {
|
|
9
|
+
const baseActions = ["focus", "blur"];
|
|
10
|
+
switch (type) {
|
|
11
|
+
case "button":
|
|
12
|
+
case "touchable":
|
|
13
|
+
case "pressable":
|
|
14
|
+
return [...baseActions, "press", "longPress", "doubleTap"];
|
|
15
|
+
case "input":
|
|
16
|
+
return [...baseActions, "press", "type", "clear"];
|
|
17
|
+
case "text":
|
|
18
|
+
return [...baseActions, "press", "longPress"];
|
|
19
|
+
case "view":
|
|
20
|
+
return [...baseActions, "press"];
|
|
21
|
+
case "scroll":
|
|
22
|
+
return [...baseActions, "scroll", "swipe"];
|
|
23
|
+
case "list":
|
|
24
|
+
return [...baseActions, "scroll", "swipe"];
|
|
25
|
+
case "listItem":
|
|
26
|
+
return [...baseActions, "press", "longPress", "swipe"];
|
|
27
|
+
case "switch":
|
|
28
|
+
case "checkbox":
|
|
29
|
+
return [...baseActions, "press", "toggle"];
|
|
30
|
+
case "radio":
|
|
31
|
+
return [...baseActions, "press"];
|
|
32
|
+
case "image":
|
|
33
|
+
return [...baseActions, "press", "longPress"];
|
|
34
|
+
case "modal":
|
|
35
|
+
return ["focus", "blur"];
|
|
36
|
+
case "custom":
|
|
37
|
+
default:
|
|
38
|
+
return [...baseActions, "press"];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
var NativeUIBridgeRegistry = class {
|
|
42
|
+
constructor(config = {}) {
|
|
43
|
+
this.elements = /* @__PURE__ */ new Map();
|
|
44
|
+
this.components = /* @__PURE__ */ new Map();
|
|
45
|
+
this.workflows = /* @__PURE__ */ new Map();
|
|
46
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
47
|
+
this.config = config;
|
|
48
|
+
}
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// Element Management
|
|
51
|
+
// ============================================================================
|
|
52
|
+
/**
|
|
53
|
+
* Register a native element
|
|
54
|
+
*/
|
|
55
|
+
registerElement(id, ref, options = {}) {
|
|
56
|
+
const {
|
|
57
|
+
type = "custom",
|
|
58
|
+
label,
|
|
59
|
+
actions = inferActions(type),
|
|
60
|
+
customActions,
|
|
61
|
+
props,
|
|
62
|
+
treePath = id,
|
|
63
|
+
testId,
|
|
64
|
+
accessibilityLabel
|
|
65
|
+
} = options;
|
|
66
|
+
const getState = () => {
|
|
67
|
+
const element = ref.current;
|
|
68
|
+
if (!element) {
|
|
69
|
+
return {
|
|
70
|
+
mounted: false,
|
|
71
|
+
visible: false,
|
|
72
|
+
enabled: false,
|
|
73
|
+
focused: false,
|
|
74
|
+
layout: null
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const stored = this.elements.get(id);
|
|
78
|
+
if (stored && stored.getState !== getState) {
|
|
79
|
+
return stored.getState();
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
mounted: true,
|
|
83
|
+
visible: true,
|
|
84
|
+
enabled: true,
|
|
85
|
+
focused: false,
|
|
86
|
+
layout: null
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
const getIdentifier = () => ({
|
|
90
|
+
uiId: id,
|
|
91
|
+
testId: testId || id,
|
|
92
|
+
accessibilityLabel,
|
|
93
|
+
treePath
|
|
94
|
+
});
|
|
95
|
+
const registered = {
|
|
96
|
+
id,
|
|
97
|
+
ref,
|
|
98
|
+
type,
|
|
99
|
+
label,
|
|
100
|
+
actions,
|
|
101
|
+
customActions,
|
|
102
|
+
props,
|
|
103
|
+
getState,
|
|
104
|
+
getIdentifier,
|
|
105
|
+
registeredAt: Date.now(),
|
|
106
|
+
mounted: true
|
|
107
|
+
};
|
|
108
|
+
this.elements.set(id, registered);
|
|
109
|
+
this.emit("element:registered", { id, type, label });
|
|
110
|
+
if (this.config.verbose) {
|
|
111
|
+
console.log(`[ui-bridge-native] Registered element: ${id} (${type})`);
|
|
112
|
+
}
|
|
113
|
+
return registered;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Unregister an element
|
|
117
|
+
*/
|
|
118
|
+
unregisterElement(id) {
|
|
119
|
+
const element = this.elements.get(id);
|
|
120
|
+
if (element) {
|
|
121
|
+
this.elements.delete(id);
|
|
122
|
+
this.emit("element:unregistered", { id });
|
|
123
|
+
if (this.config.verbose) {
|
|
124
|
+
console.log(`[ui-bridge-native] Unregistered element: ${id}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Get a registered element
|
|
130
|
+
*/
|
|
131
|
+
getElement(id) {
|
|
132
|
+
return this.elements.get(id);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get all registered elements
|
|
136
|
+
*/
|
|
137
|
+
getAllElements() {
|
|
138
|
+
return Array.from(this.elements.values());
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Update element state
|
|
142
|
+
*/
|
|
143
|
+
updateElementState(id, state) {
|
|
144
|
+
const element = this.elements.get(id);
|
|
145
|
+
if (element) {
|
|
146
|
+
const currentState = element.getState();
|
|
147
|
+
const newState = { ...currentState, ...state };
|
|
148
|
+
const updated = {
|
|
149
|
+
...element,
|
|
150
|
+
getState: () => newState
|
|
151
|
+
};
|
|
152
|
+
this.elements.set(id, updated);
|
|
153
|
+
this.emit("element:stateChanged", { id, state: newState });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Update element props (for action execution)
|
|
158
|
+
*/
|
|
159
|
+
updateElementProps(id, props) {
|
|
160
|
+
const element = this.elements.get(id);
|
|
161
|
+
if (element) {
|
|
162
|
+
const updated = {
|
|
163
|
+
...element,
|
|
164
|
+
props: { ...element.props, ...props }
|
|
165
|
+
};
|
|
166
|
+
this.elements.set(id, updated);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Find element by testID
|
|
171
|
+
*/
|
|
172
|
+
findByTestId(testId) {
|
|
173
|
+
for (const element of this.elements.values()) {
|
|
174
|
+
const identifier = element.getIdentifier();
|
|
175
|
+
if (identifier.testId === testId) {
|
|
176
|
+
return element;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return void 0;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Find elements by type
|
|
183
|
+
*/
|
|
184
|
+
findByType(type) {
|
|
185
|
+
return Array.from(this.elements.values()).filter((e) => e.type === type);
|
|
186
|
+
}
|
|
187
|
+
// ============================================================================
|
|
188
|
+
// Component Management
|
|
189
|
+
// ============================================================================
|
|
190
|
+
/**
|
|
191
|
+
* Register a component
|
|
192
|
+
*/
|
|
193
|
+
registerComponent(id, options) {
|
|
194
|
+
const { name, description, actions = [], elementIds } = options;
|
|
195
|
+
const registered = {
|
|
196
|
+
id,
|
|
197
|
+
name,
|
|
198
|
+
description,
|
|
199
|
+
actions: actions.map((a) => ({
|
|
200
|
+
id: a.id,
|
|
201
|
+
label: a.label,
|
|
202
|
+
description: a.description,
|
|
203
|
+
handler: a.handler
|
|
204
|
+
})),
|
|
205
|
+
elementIds,
|
|
206
|
+
registeredAt: Date.now(),
|
|
207
|
+
mounted: true
|
|
208
|
+
};
|
|
209
|
+
this.components.set(id, registered);
|
|
210
|
+
this.emit("component:registered", { id, name });
|
|
211
|
+
if (this.config.verbose) {
|
|
212
|
+
console.log(`[ui-bridge-native] Registered component: ${id} (${name})`);
|
|
213
|
+
}
|
|
214
|
+
return registered;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Unregister a component
|
|
218
|
+
*/
|
|
219
|
+
unregisterComponent(id) {
|
|
220
|
+
const component = this.components.get(id);
|
|
221
|
+
if (component) {
|
|
222
|
+
this.components.delete(id);
|
|
223
|
+
this.emit("component:unregistered", { id });
|
|
224
|
+
if (this.config.verbose) {
|
|
225
|
+
console.log(`[ui-bridge-native] Unregistered component: ${id}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get a registered component
|
|
231
|
+
*/
|
|
232
|
+
getComponent(id) {
|
|
233
|
+
return this.components.get(id);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Get all registered components
|
|
237
|
+
*/
|
|
238
|
+
getAllComponents() {
|
|
239
|
+
return Array.from(this.components.values());
|
|
240
|
+
}
|
|
241
|
+
// ============================================================================
|
|
242
|
+
// Workflow Management
|
|
243
|
+
// ============================================================================
|
|
244
|
+
/**
|
|
245
|
+
* Register a workflow
|
|
246
|
+
*/
|
|
247
|
+
registerWorkflow(workflow) {
|
|
248
|
+
this.workflows.set(workflow.id, workflow);
|
|
249
|
+
if (this.config.verbose) {
|
|
250
|
+
console.log(`[ui-bridge-native] Registered workflow: ${workflow.id}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Unregister a workflow
|
|
255
|
+
*/
|
|
256
|
+
unregisterWorkflow(id) {
|
|
257
|
+
this.workflows.delete(id);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Get a workflow
|
|
261
|
+
*/
|
|
262
|
+
getWorkflow(id) {
|
|
263
|
+
return this.workflows.get(id);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Get all workflows
|
|
267
|
+
*/
|
|
268
|
+
getAllWorkflows() {
|
|
269
|
+
return Array.from(this.workflows.values());
|
|
270
|
+
}
|
|
271
|
+
// ============================================================================
|
|
272
|
+
// Event System
|
|
273
|
+
// ============================================================================
|
|
274
|
+
/**
|
|
275
|
+
* Subscribe to events
|
|
276
|
+
*/
|
|
277
|
+
on(type, listener) {
|
|
278
|
+
if (!this.eventListeners.has(type)) {
|
|
279
|
+
this.eventListeners.set(type, /* @__PURE__ */ new Set());
|
|
280
|
+
}
|
|
281
|
+
this.eventListeners.get(type).add(listener);
|
|
282
|
+
return () => this.off(type, listener);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Unsubscribe from events
|
|
286
|
+
*/
|
|
287
|
+
off(type, listener) {
|
|
288
|
+
this.eventListeners.get(type)?.delete(listener);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Emit an event
|
|
292
|
+
*/
|
|
293
|
+
emit(type, data) {
|
|
294
|
+
const event = {
|
|
295
|
+
type,
|
|
296
|
+
timestamp: Date.now(),
|
|
297
|
+
data
|
|
298
|
+
};
|
|
299
|
+
const listeners = this.eventListeners.get(type);
|
|
300
|
+
if (listeners) {
|
|
301
|
+
for (const listener of listeners) {
|
|
302
|
+
try {
|
|
303
|
+
listener(event);
|
|
304
|
+
} catch (error2) {
|
|
305
|
+
console.error(`[ui-bridge-native] Event listener error:`, error2);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (this.config.onEvent) {
|
|
310
|
+
try {
|
|
311
|
+
this.config.onEvent(event);
|
|
312
|
+
} catch (error2) {
|
|
313
|
+
console.error(`[ui-bridge-native] Global event handler error:`, error2);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// ============================================================================
|
|
318
|
+
// Snapshots
|
|
319
|
+
// ============================================================================
|
|
320
|
+
/**
|
|
321
|
+
* Create a snapshot of the current state
|
|
322
|
+
*/
|
|
323
|
+
createSnapshot() {
|
|
324
|
+
return {
|
|
325
|
+
timestamp: Date.now(),
|
|
326
|
+
elements: this.getAllElements().map((e) => ({
|
|
327
|
+
id: e.id,
|
|
328
|
+
type: e.type,
|
|
329
|
+
label: e.label,
|
|
330
|
+
identifier: e.getIdentifier(),
|
|
331
|
+
state: e.getState(),
|
|
332
|
+
actions: e.actions,
|
|
333
|
+
customActions: e.customActions ? Object.keys(e.customActions) : void 0
|
|
334
|
+
})),
|
|
335
|
+
components: this.getAllComponents().map((c) => ({
|
|
336
|
+
id: c.id,
|
|
337
|
+
name: c.name,
|
|
338
|
+
description: c.description,
|
|
339
|
+
actions: c.actions.map((a) => a.id),
|
|
340
|
+
elementIds: c.elementIds
|
|
341
|
+
})),
|
|
342
|
+
workflows: this.getAllWorkflows().map((w) => ({
|
|
343
|
+
id: w.id,
|
|
344
|
+
name: w.name,
|
|
345
|
+
description: w.description,
|
|
346
|
+
stepCount: w.steps.length
|
|
347
|
+
}))
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Get registry statistics
|
|
352
|
+
*/
|
|
353
|
+
getStats() {
|
|
354
|
+
return {
|
|
355
|
+
elements: this.elements.size,
|
|
356
|
+
components: this.components.size,
|
|
357
|
+
workflows: this.workflows.size
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Clear all registrations
|
|
362
|
+
*/
|
|
363
|
+
clear() {
|
|
364
|
+
this.elements.clear();
|
|
365
|
+
this.components.clear();
|
|
366
|
+
this.workflows.clear();
|
|
367
|
+
if (this.config.verbose) {
|
|
368
|
+
console.log(`[ui-bridge-native] Registry cleared`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
var globalRegistry = null;
|
|
373
|
+
function setGlobalRegistry(registry) {
|
|
374
|
+
globalRegistry = registry;
|
|
375
|
+
}
|
|
376
|
+
function getGlobalRegistry() {
|
|
377
|
+
return globalRegistry;
|
|
378
|
+
}
|
|
379
|
+
function resetGlobalRegistry() {
|
|
380
|
+
globalRegistry?.clear();
|
|
381
|
+
globalRegistry = null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/native/core/element-identifier.ts
|
|
385
|
+
function createNativeElementIdentifier(id, options = {}) {
|
|
386
|
+
return {
|
|
387
|
+
uiId: id,
|
|
388
|
+
testId: options.testId || id,
|
|
389
|
+
accessibilityLabel: options.accessibilityLabel,
|
|
390
|
+
accessibilityHint: options.accessibilityHint,
|
|
391
|
+
treePath: options.treePath || id
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function findElementByIdentifier(identifier) {
|
|
395
|
+
const registry = getGlobalRegistry();
|
|
396
|
+
if (!registry) return null;
|
|
397
|
+
if (typeof identifier === "string") {
|
|
398
|
+
const byId = registry.getElement(identifier);
|
|
399
|
+
if (byId) return byId;
|
|
400
|
+
const byTestId = registry.findByTestId(identifier);
|
|
401
|
+
if (byTestId) return byTestId;
|
|
402
|
+
return findByPattern(registry, identifier);
|
|
403
|
+
}
|
|
404
|
+
if (identifier.uiId) {
|
|
405
|
+
const byId = registry.getElement(identifier.uiId);
|
|
406
|
+
if (byId) return byId;
|
|
407
|
+
}
|
|
408
|
+
if (identifier.testId) {
|
|
409
|
+
const byTestId = registry.findByTestId(identifier.testId);
|
|
410
|
+
if (byTestId) return byTestId;
|
|
411
|
+
}
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
function findByPattern(registry, pattern) {
|
|
415
|
+
if (!registry) return null;
|
|
416
|
+
const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
417
|
+
const regex = new RegExp(`^${regexPattern}$`, "i");
|
|
418
|
+
for (const element of registry.getAllElements()) {
|
|
419
|
+
const identifier = element.getIdentifier();
|
|
420
|
+
if (identifier.testId && regex.test(identifier.testId)) {
|
|
421
|
+
return element;
|
|
422
|
+
}
|
|
423
|
+
if (identifier.uiId && regex.test(identifier.uiId)) {
|
|
424
|
+
return element;
|
|
425
|
+
}
|
|
426
|
+
if (identifier.treePath && regex.test(identifier.treePath)) {
|
|
427
|
+
return element;
|
|
428
|
+
}
|
|
429
|
+
if (identifier.accessibilityLabel && regex.test(identifier.accessibilityLabel)) {
|
|
430
|
+
return element;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
function findAllByPattern(pattern) {
|
|
436
|
+
const registry = getGlobalRegistry();
|
|
437
|
+
if (!registry) return [];
|
|
438
|
+
const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
439
|
+
const regex = new RegExp(`^${regexPattern}$`, "i");
|
|
440
|
+
const results = [];
|
|
441
|
+
for (const element of registry.getAllElements()) {
|
|
442
|
+
const identifier = element.getIdentifier();
|
|
443
|
+
if (identifier.testId && regex.test(identifier.testId) || identifier.uiId && regex.test(identifier.uiId) || identifier.treePath && regex.test(identifier.treePath) || identifier.accessibilityLabel && regex.test(identifier.accessibilityLabel)) {
|
|
444
|
+
results.push(element);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return results;
|
|
448
|
+
}
|
|
449
|
+
function buildTreePath(componentPath, elementIndex) {
|
|
450
|
+
let path = componentPath.join("/");
|
|
451
|
+
if (elementIndex !== void 0) {
|
|
452
|
+
path += `[${elementIndex}]`;
|
|
453
|
+
}
|
|
454
|
+
return path;
|
|
455
|
+
}
|
|
456
|
+
function parseTreePath(treePath) {
|
|
457
|
+
const indexMatch = treePath.match(/\[(\d+)\]$/);
|
|
458
|
+
const index = indexMatch ? parseInt(indexMatch[1], 10) : void 0;
|
|
459
|
+
const pathWithoutIndex = treePath.replace(/\[\d+\]$/, "");
|
|
460
|
+
const components = pathWithoutIndex.split("/").filter(Boolean);
|
|
461
|
+
return { components, index };
|
|
462
|
+
}
|
|
463
|
+
function matchesIdentifier(identifier, criteria) {
|
|
464
|
+
if (criteria.uiId && identifier.uiId !== criteria.uiId) {
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
if (criteria.testId && identifier.testId !== criteria.testId) {
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
if (criteria.accessibilityLabel && identifier.accessibilityLabel !== criteria.accessibilityLabel) {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
if (criteria.treePath && identifier.treePath !== criteria.treePath) {
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// src/native/control/action-executor.ts
|
|
480
|
+
var DEFAULT_WAIT_OPTIONS = {
|
|
481
|
+
visible: true,
|
|
482
|
+
enabled: true,
|
|
483
|
+
focused: false,
|
|
484
|
+
state: {},
|
|
485
|
+
timeout: 1e4,
|
|
486
|
+
interval: 100
|
|
487
|
+
};
|
|
488
|
+
function sleep(ms) {
|
|
489
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
490
|
+
}
|
|
491
|
+
var DefaultNativeActionExecutor = class {
|
|
492
|
+
constructor(registry) {
|
|
493
|
+
this.registry = registry;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Execute an action on an element
|
|
497
|
+
*/
|
|
498
|
+
async executeAction(elementId, request) {
|
|
499
|
+
const startTime = Date.now();
|
|
500
|
+
let waitDurationMs = 0;
|
|
501
|
+
try {
|
|
502
|
+
let registered = this.registry.getElement(elementId);
|
|
503
|
+
if (!registered) {
|
|
504
|
+
registered = findElementByIdentifier(elementId) ?? void 0;
|
|
505
|
+
}
|
|
506
|
+
if (!registered) {
|
|
507
|
+
return {
|
|
508
|
+
success: false,
|
|
509
|
+
error: `Element not found: ${elementId}`,
|
|
510
|
+
durationMs: Date.now() - startTime,
|
|
511
|
+
timestamp: Date.now(),
|
|
512
|
+
requestId: request.requestId
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
if (request.waitOptions) {
|
|
516
|
+
const waitResult = await this.waitForElementInternal(registered.id, request.waitOptions);
|
|
517
|
+
waitDurationMs = waitResult.waitedMs;
|
|
518
|
+
if (!waitResult.met) {
|
|
519
|
+
return {
|
|
520
|
+
success: false,
|
|
521
|
+
error: waitResult.error || "Wait condition not met",
|
|
522
|
+
durationMs: Date.now() - startTime,
|
|
523
|
+
timestamp: Date.now(),
|
|
524
|
+
requestId: request.requestId,
|
|
525
|
+
waitDurationMs
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
const result = await this.performAction(registered, request.action, request.params);
|
|
530
|
+
return {
|
|
531
|
+
success: true,
|
|
532
|
+
elementState: registered.getState(),
|
|
533
|
+
result,
|
|
534
|
+
durationMs: Date.now() - startTime,
|
|
535
|
+
timestamp: Date.now(),
|
|
536
|
+
requestId: request.requestId,
|
|
537
|
+
waitDurationMs
|
|
538
|
+
};
|
|
539
|
+
} catch (error2) {
|
|
540
|
+
return {
|
|
541
|
+
success: false,
|
|
542
|
+
error: error2 instanceof Error ? error2.message : String(error2),
|
|
543
|
+
stack: error2 instanceof Error ? error2.stack : void 0,
|
|
544
|
+
durationMs: Date.now() - startTime,
|
|
545
|
+
timestamp: Date.now(),
|
|
546
|
+
requestId: request.requestId,
|
|
547
|
+
waitDurationMs
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Perform an action on an element
|
|
553
|
+
*/
|
|
554
|
+
async performAction(element, action, params) {
|
|
555
|
+
if (!element) {
|
|
556
|
+
throw new Error("Element not found");
|
|
557
|
+
}
|
|
558
|
+
const props = element.props || {};
|
|
559
|
+
if (element.customActions && action in element.customActions) {
|
|
560
|
+
return element.customActions[action].handler(params);
|
|
561
|
+
}
|
|
562
|
+
switch (action) {
|
|
563
|
+
case "press":
|
|
564
|
+
return this.performPress(props, params);
|
|
565
|
+
case "longPress":
|
|
566
|
+
return this.performLongPress(props, params);
|
|
567
|
+
case "doubleTap":
|
|
568
|
+
return this.performDoubleTap(props);
|
|
569
|
+
case "type":
|
|
570
|
+
return this.performType(element, props, params);
|
|
571
|
+
case "clear":
|
|
572
|
+
return this.performClear(element, props);
|
|
573
|
+
case "focus":
|
|
574
|
+
return this.performFocus(element);
|
|
575
|
+
case "blur":
|
|
576
|
+
return this.performBlur(element);
|
|
577
|
+
case "scroll":
|
|
578
|
+
return this.performScroll(props, params);
|
|
579
|
+
case "swipe":
|
|
580
|
+
return this.performSwipe(props, params);
|
|
581
|
+
case "toggle":
|
|
582
|
+
return this.performToggle(props);
|
|
583
|
+
default:
|
|
584
|
+
throw new Error(`Unknown action: ${action}`);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Perform press action
|
|
589
|
+
*/
|
|
590
|
+
async performPress(props, params) {
|
|
591
|
+
const handlers = ["onPress", "onPressIn", "onResponderRelease"];
|
|
592
|
+
for (const handler of handlers) {
|
|
593
|
+
if (typeof props[handler] === "function") {
|
|
594
|
+
const event = this.createPressEvent(params);
|
|
595
|
+
props[handler](event);
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
throw new Error("No press handler found on element");
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Perform long press action
|
|
603
|
+
*/
|
|
604
|
+
async performLongPress(props, params) {
|
|
605
|
+
if (typeof props.onLongPress === "function") {
|
|
606
|
+
const event = this.createPressEvent(params);
|
|
607
|
+
props.onLongPress(event);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
throw new Error("No long press handler found on element");
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Perform double tap action
|
|
614
|
+
*/
|
|
615
|
+
async performDoubleTap(props) {
|
|
616
|
+
if (typeof props.onDoubleTap === "function") {
|
|
617
|
+
props.onDoubleTap();
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
if (typeof props.onPress === "function") {
|
|
621
|
+
const event = this.createPressEvent();
|
|
622
|
+
props.onPress(event);
|
|
623
|
+
await sleep(50);
|
|
624
|
+
props.onPress(event);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
throw new Error("No press handler found for double tap");
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Perform type action
|
|
631
|
+
*/
|
|
632
|
+
async performType(element, props, params) {
|
|
633
|
+
if (!params?.text) {
|
|
634
|
+
throw new Error("Type action requires text parameter");
|
|
635
|
+
}
|
|
636
|
+
if (params.clearFirst) {
|
|
637
|
+
await this.performClear(element, props);
|
|
638
|
+
}
|
|
639
|
+
if (params.delay && params.delay > 0) {
|
|
640
|
+
const currentValue = element?.getState().value || "";
|
|
641
|
+
for (const char of params.text) {
|
|
642
|
+
const newValue = currentValue + char;
|
|
643
|
+
if (typeof props.onChangeText === "function") {
|
|
644
|
+
props.onChangeText(newValue);
|
|
645
|
+
}
|
|
646
|
+
await sleep(params.delay);
|
|
647
|
+
}
|
|
648
|
+
} else {
|
|
649
|
+
if (typeof props.onChangeText === "function") {
|
|
650
|
+
props.onChangeText(params.text);
|
|
651
|
+
} else if (typeof props.onChange === "function") {
|
|
652
|
+
props.onChange({
|
|
653
|
+
nativeEvent: { text: params.text }
|
|
654
|
+
});
|
|
655
|
+
} else {
|
|
656
|
+
throw new Error("No text change handler found on element");
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
if (element) {
|
|
660
|
+
this.registry.updateElementState(element.id, { value: params.text });
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Perform clear action
|
|
665
|
+
*/
|
|
666
|
+
async performClear(element, props) {
|
|
667
|
+
if (typeof props.onChangeText === "function") {
|
|
668
|
+
props.onChangeText("");
|
|
669
|
+
} else if (typeof props.onChange === "function") {
|
|
670
|
+
props.onChange({
|
|
671
|
+
nativeEvent: { text: "" }
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
if (element) {
|
|
675
|
+
this.registry.updateElementState(element.id, { value: "" });
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Perform focus action
|
|
680
|
+
*/
|
|
681
|
+
async performFocus(element) {
|
|
682
|
+
if (element?.ref.current && "focus" in element.ref.current) {
|
|
683
|
+
element.ref.current.focus();
|
|
684
|
+
}
|
|
685
|
+
if (element) {
|
|
686
|
+
this.registry.updateElementState(element.id, { focused: true });
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Perform blur action
|
|
691
|
+
*/
|
|
692
|
+
async performBlur(element) {
|
|
693
|
+
if (element?.ref.current && "blur" in element.ref.current) {
|
|
694
|
+
element.ref.current.blur();
|
|
695
|
+
}
|
|
696
|
+
if (element) {
|
|
697
|
+
this.registry.updateElementState(element.id, { focused: false });
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Perform scroll action
|
|
702
|
+
*/
|
|
703
|
+
async performScroll(props, params) {
|
|
704
|
+
if (typeof props.onScroll === "function") {
|
|
705
|
+
const event = {
|
|
706
|
+
nativeEvent: {
|
|
707
|
+
contentOffset: params?.offset || { x: 0, y: 0 }
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
props.onScroll(event);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Perform swipe action
|
|
715
|
+
*/
|
|
716
|
+
async performSwipe(props, params) {
|
|
717
|
+
if (!params?.direction) {
|
|
718
|
+
throw new Error("Swipe action requires direction parameter");
|
|
719
|
+
}
|
|
720
|
+
const handlerMap = {
|
|
721
|
+
left: "onSwipeLeft",
|
|
722
|
+
right: "onSwipeRight",
|
|
723
|
+
up: "onSwipeUp",
|
|
724
|
+
down: "onSwipeDown"
|
|
725
|
+
};
|
|
726
|
+
const handler = handlerMap[params.direction];
|
|
727
|
+
if (handler && typeof props[handler] === "function") {
|
|
728
|
+
props[handler]();
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
if (typeof props.onSwipe === "function") {
|
|
732
|
+
props.onSwipe(params.direction);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Perform toggle action
|
|
737
|
+
*/
|
|
738
|
+
async performToggle(props) {
|
|
739
|
+
if (typeof props.onValueChange === "function") {
|
|
740
|
+
const currentValue = props.value;
|
|
741
|
+
props.onValueChange(!currentValue);
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
if (typeof props.onPress === "function") {
|
|
745
|
+
props.onPress();
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
throw new Error("No toggle handler found on element");
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Create a synthetic press event
|
|
752
|
+
*/
|
|
753
|
+
createPressEvent(params) {
|
|
754
|
+
return {
|
|
755
|
+
nativeEvent: {
|
|
756
|
+
locationX: params?.position?.x ?? 0,
|
|
757
|
+
locationY: params?.position?.y ?? 0,
|
|
758
|
+
timestamp: Date.now()
|
|
759
|
+
},
|
|
760
|
+
persist: () => {
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Execute a component action
|
|
766
|
+
*/
|
|
767
|
+
async executeComponentAction(componentId, request) {
|
|
768
|
+
const startTime = Date.now();
|
|
769
|
+
try {
|
|
770
|
+
const component = this.registry.getComponent(componentId);
|
|
771
|
+
if (!component) {
|
|
772
|
+
return {
|
|
773
|
+
success: false,
|
|
774
|
+
error: `Component not found: ${componentId}`,
|
|
775
|
+
durationMs: Date.now() - startTime,
|
|
776
|
+
timestamp: Date.now(),
|
|
777
|
+
requestId: request.requestId
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
const action = component.actions.find((a) => a.id === request.action);
|
|
781
|
+
if (!action) {
|
|
782
|
+
return {
|
|
783
|
+
success: false,
|
|
784
|
+
error: `Action not found: ${request.action}`,
|
|
785
|
+
durationMs: Date.now() - startTime,
|
|
786
|
+
timestamp: Date.now(),
|
|
787
|
+
requestId: request.requestId
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
const result = await action.handler(request.params);
|
|
791
|
+
return {
|
|
792
|
+
success: true,
|
|
793
|
+
result,
|
|
794
|
+
durationMs: Date.now() - startTime,
|
|
795
|
+
timestamp: Date.now(),
|
|
796
|
+
requestId: request.requestId
|
|
797
|
+
};
|
|
798
|
+
} catch (error2) {
|
|
799
|
+
return {
|
|
800
|
+
success: false,
|
|
801
|
+
error: error2 instanceof Error ? error2.message : String(error2),
|
|
802
|
+
stack: error2 instanceof Error ? error2.stack : void 0,
|
|
803
|
+
durationMs: Date.now() - startTime,
|
|
804
|
+
timestamp: Date.now(),
|
|
805
|
+
requestId: request.requestId
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Find elements
|
|
811
|
+
*/
|
|
812
|
+
async find(request) {
|
|
813
|
+
const startTime = Date.now();
|
|
814
|
+
const allElements = this.registry.getAllElements();
|
|
815
|
+
let filtered = allElements;
|
|
816
|
+
if (request.types && request.types.length > 0) {
|
|
817
|
+
filtered = filtered.filter((e) => request.types.includes(e.type));
|
|
818
|
+
}
|
|
819
|
+
if (request.testIdPattern) {
|
|
820
|
+
const regex = new RegExp(request.testIdPattern.replace(/\*/g, ".*").replace(/\?/g, "."));
|
|
821
|
+
filtered = filtered.filter((e) => {
|
|
822
|
+
const identifier = e.getIdentifier();
|
|
823
|
+
return identifier.testId && regex.test(identifier.testId);
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
if (request.accessibilityLabelPattern) {
|
|
827
|
+
const regex = new RegExp(
|
|
828
|
+
request.accessibilityLabelPattern.replace(/\*/g, ".*").replace(/\?/g, ".")
|
|
829
|
+
);
|
|
830
|
+
filtered = filtered.filter((e) => {
|
|
831
|
+
const identifier = e.getIdentifier();
|
|
832
|
+
return identifier.accessibilityLabel && regex.test(identifier.accessibilityLabel);
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
if (request.visibleOnly) {
|
|
836
|
+
filtered = filtered.filter((e) => e.getState().visible);
|
|
837
|
+
}
|
|
838
|
+
if (request.limit && request.limit > 0) {
|
|
839
|
+
filtered = filtered.slice(0, request.limit);
|
|
840
|
+
}
|
|
841
|
+
const elements = filtered.map((e) => ({
|
|
842
|
+
id: e.id,
|
|
843
|
+
type: e.type,
|
|
844
|
+
identifier: e.getIdentifier(),
|
|
845
|
+
state: e.getState(),
|
|
846
|
+
actions: e.actions,
|
|
847
|
+
label: e.label
|
|
848
|
+
}));
|
|
849
|
+
return {
|
|
850
|
+
elements,
|
|
851
|
+
total: elements.length,
|
|
852
|
+
durationMs: Date.now() - startTime,
|
|
853
|
+
timestamp: Date.now()
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Wait for element conditions
|
|
858
|
+
*/
|
|
859
|
+
async waitForElement(elementId, options) {
|
|
860
|
+
return this.waitForElementInternal(elementId, options);
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Internal wait implementation
|
|
864
|
+
*/
|
|
865
|
+
async waitForElementInternal(elementId, options) {
|
|
866
|
+
const opts = { ...DEFAULT_WAIT_OPTIONS, ...options };
|
|
867
|
+
const startTime = Date.now();
|
|
868
|
+
while (Date.now() - startTime < opts.timeout) {
|
|
869
|
+
const element = this.registry.getElement(elementId);
|
|
870
|
+
if (!element) {
|
|
871
|
+
await sleep(opts.interval);
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
const state = element.getState();
|
|
875
|
+
let conditionsMet = true;
|
|
876
|
+
if (opts.visible && !state.visible) {
|
|
877
|
+
conditionsMet = false;
|
|
878
|
+
}
|
|
879
|
+
if (opts.enabled && !state.enabled) {
|
|
880
|
+
conditionsMet = false;
|
|
881
|
+
}
|
|
882
|
+
if (opts.focused && !state.focused) {
|
|
883
|
+
conditionsMet = false;
|
|
884
|
+
}
|
|
885
|
+
if (opts.state && Object.keys(opts.state).length > 0) {
|
|
886
|
+
const stateRecord = state;
|
|
887
|
+
for (const [key, value] of Object.entries(opts.state)) {
|
|
888
|
+
if (stateRecord[key] !== value) {
|
|
889
|
+
conditionsMet = false;
|
|
890
|
+
break;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
if (conditionsMet) {
|
|
895
|
+
return {
|
|
896
|
+
met: true,
|
|
897
|
+
waitedMs: Date.now() - startTime,
|
|
898
|
+
state
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
await sleep(opts.interval);
|
|
902
|
+
}
|
|
903
|
+
return {
|
|
904
|
+
met: false,
|
|
905
|
+
waitedMs: Date.now() - startTime,
|
|
906
|
+
error: `Timeout waiting for conditions on element: ${elementId}`
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
function createNativeActionExecutor(registry) {
|
|
911
|
+
return new DefaultNativeActionExecutor(registry);
|
|
912
|
+
}
|
|
913
|
+
var UIBridgeNativeContext = react.createContext(null);
|
|
914
|
+
function UIBridgeNativeProvider({
|
|
915
|
+
children,
|
|
916
|
+
features = {},
|
|
917
|
+
config = {},
|
|
918
|
+
onEvent
|
|
919
|
+
}) {
|
|
920
|
+
const registryRef = react.useRef(null);
|
|
921
|
+
const executorRef = react.useRef(null);
|
|
922
|
+
const [serverRunning, setServerRunning] = react.useState(false);
|
|
923
|
+
if (!registryRef.current) {
|
|
924
|
+
registryRef.current = new NativeUIBridgeRegistry({
|
|
925
|
+
verbose: config.verbose,
|
|
926
|
+
onEvent
|
|
927
|
+
});
|
|
928
|
+
setGlobalRegistry(registryRef.current);
|
|
929
|
+
}
|
|
930
|
+
const registry = registryRef.current;
|
|
931
|
+
if (!executorRef.current) {
|
|
932
|
+
executorRef.current = createNativeActionExecutor(registry);
|
|
933
|
+
}
|
|
934
|
+
const executor = executorRef.current;
|
|
935
|
+
const startServer = react.useCallback(async () => {
|
|
936
|
+
if (!features.server) {
|
|
937
|
+
console.warn("[ui-bridge-native] Server feature not enabled");
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
console.log(`[ui-bridge-native] Would start HTTP server on port ${config.serverPort || 9876}`);
|
|
941
|
+
setServerRunning(true);
|
|
942
|
+
}, [features.server, config.serverPort]);
|
|
943
|
+
const stopServer = react.useCallback(() => {
|
|
944
|
+
console.log("[ui-bridge-native] Would stop HTTP server");
|
|
945
|
+
setServerRunning(false);
|
|
946
|
+
}, []);
|
|
947
|
+
react.useEffect(() => {
|
|
948
|
+
if (features.server) {
|
|
949
|
+
startServer();
|
|
950
|
+
return () => stopServer();
|
|
951
|
+
}
|
|
952
|
+
}, [features.server, startServer, stopServer]);
|
|
953
|
+
react.useEffect(() => {
|
|
954
|
+
return () => {
|
|
955
|
+
stopServer();
|
|
956
|
+
resetGlobalRegistry();
|
|
957
|
+
};
|
|
958
|
+
}, [stopServer]);
|
|
959
|
+
const getElements = react.useCallback(() => registry.getAllElements(), [registry]);
|
|
960
|
+
const getComponents = react.useCallback(() => registry.getAllComponents(), [registry]);
|
|
961
|
+
const createSnapshot = react.useCallback(() => registry.createSnapshot(), [registry]);
|
|
962
|
+
const on = react.useCallback(
|
|
963
|
+
(type, listener) => registry.on(type, listener),
|
|
964
|
+
[registry]
|
|
965
|
+
);
|
|
966
|
+
const off = react.useCallback(
|
|
967
|
+
(type, listener) => registry.off(type, listener),
|
|
968
|
+
[registry]
|
|
969
|
+
);
|
|
970
|
+
const contextValue = react.useMemo(
|
|
971
|
+
() => ({
|
|
972
|
+
features,
|
|
973
|
+
config,
|
|
974
|
+
registry,
|
|
975
|
+
executor,
|
|
976
|
+
getElements,
|
|
977
|
+
getComponents,
|
|
978
|
+
createSnapshot,
|
|
979
|
+
on,
|
|
980
|
+
off,
|
|
981
|
+
initialized: true,
|
|
982
|
+
serverRunning,
|
|
983
|
+
startServer,
|
|
984
|
+
stopServer
|
|
985
|
+
}),
|
|
986
|
+
[
|
|
987
|
+
features,
|
|
988
|
+
config,
|
|
989
|
+
registry,
|
|
990
|
+
executor,
|
|
991
|
+
getElements,
|
|
992
|
+
getComponents,
|
|
993
|
+
createSnapshot,
|
|
994
|
+
on,
|
|
995
|
+
off,
|
|
996
|
+
serverRunning,
|
|
997
|
+
startServer,
|
|
998
|
+
stopServer
|
|
999
|
+
]
|
|
1000
|
+
);
|
|
1001
|
+
return /* @__PURE__ */ jsxRuntime.jsx(UIBridgeNativeContext.Provider, { value: contextValue, children });
|
|
1002
|
+
}
|
|
1003
|
+
function useUIBridgeNative() {
|
|
1004
|
+
const context = react.useContext(UIBridgeNativeContext);
|
|
1005
|
+
if (!context) {
|
|
1006
|
+
throw new Error("useUIBridgeNative must be used within a UIBridgeNativeProvider");
|
|
1007
|
+
}
|
|
1008
|
+
return context;
|
|
1009
|
+
}
|
|
1010
|
+
function useUIBridgeNativeOptional() {
|
|
1011
|
+
return react.useContext(UIBridgeNativeContext);
|
|
1012
|
+
}
|
|
1013
|
+
var useUIBridgeNativeRequired = useUIBridgeNative;
|
|
1014
|
+
function useUIElement(options) {
|
|
1015
|
+
const bridge = useUIBridgeNativeOptional();
|
|
1016
|
+
const ref = react.useRef(null);
|
|
1017
|
+
const [registered, setRegistered] = react.useState(false);
|
|
1018
|
+
const [_layout, setLayout] = react.useState(null);
|
|
1019
|
+
const propsRef = react.useRef({});
|
|
1020
|
+
const {
|
|
1021
|
+
id,
|
|
1022
|
+
type = "custom",
|
|
1023
|
+
label,
|
|
1024
|
+
actions,
|
|
1025
|
+
customActions,
|
|
1026
|
+
autoRegister = true,
|
|
1027
|
+
onStateChange,
|
|
1028
|
+
parentPath
|
|
1029
|
+
} = options;
|
|
1030
|
+
const treePath = parentPath ? `${parentPath}/${id}` : id;
|
|
1031
|
+
const bridgeProps = react.useMemo(
|
|
1032
|
+
() => ({
|
|
1033
|
+
testID: id,
|
|
1034
|
+
accessibilityLabel: label
|
|
1035
|
+
}),
|
|
1036
|
+
[id, label]
|
|
1037
|
+
);
|
|
1038
|
+
const register = react.useCallback(() => {
|
|
1039
|
+
if (!bridge || registered) return;
|
|
1040
|
+
bridge.registry.registerElement(id, ref, {
|
|
1041
|
+
type,
|
|
1042
|
+
label,
|
|
1043
|
+
actions,
|
|
1044
|
+
customActions,
|
|
1045
|
+
treePath,
|
|
1046
|
+
testId: id,
|
|
1047
|
+
accessibilityLabel: label
|
|
1048
|
+
});
|
|
1049
|
+
setRegistered(true);
|
|
1050
|
+
}, [bridge, registered, id, type, label, actions, customActions, treePath]);
|
|
1051
|
+
const unregister = react.useCallback(() => {
|
|
1052
|
+
if (!bridge || !registered) return;
|
|
1053
|
+
bridge.registry.unregisterElement(id);
|
|
1054
|
+
setRegistered(false);
|
|
1055
|
+
}, [bridge, registered, id]);
|
|
1056
|
+
const onLayout = react.useCallback(
|
|
1057
|
+
(event) => {
|
|
1058
|
+
const { x, y, width, height } = event.nativeEvent.layout;
|
|
1059
|
+
if (ref.current && "measureInWindow" in ref.current) {
|
|
1060
|
+
ref.current.measureInWindow((pageX, pageY) => {
|
|
1061
|
+
const newLayout = {
|
|
1062
|
+
x,
|
|
1063
|
+
y,
|
|
1064
|
+
width,
|
|
1065
|
+
height,
|
|
1066
|
+
pageX,
|
|
1067
|
+
pageY
|
|
1068
|
+
};
|
|
1069
|
+
setLayout(newLayout);
|
|
1070
|
+
if (bridge && registered) {
|
|
1071
|
+
const newState = {
|
|
1072
|
+
mounted: true,
|
|
1073
|
+
visible: width > 0 && height > 0,
|
|
1074
|
+
enabled: true,
|
|
1075
|
+
focused: false,
|
|
1076
|
+
layout: newLayout
|
|
1077
|
+
};
|
|
1078
|
+
bridge.registry.updateElementState(id, newState);
|
|
1079
|
+
onStateChange?.(newState);
|
|
1080
|
+
}
|
|
1081
|
+
});
|
|
1082
|
+
} else {
|
|
1083
|
+
const newLayout = {
|
|
1084
|
+
x,
|
|
1085
|
+
y,
|
|
1086
|
+
width,
|
|
1087
|
+
height,
|
|
1088
|
+
pageX: x,
|
|
1089
|
+
pageY: y
|
|
1090
|
+
};
|
|
1091
|
+
setLayout(newLayout);
|
|
1092
|
+
if (bridge && registered) {
|
|
1093
|
+
const newState = {
|
|
1094
|
+
mounted: true,
|
|
1095
|
+
visible: width > 0 && height > 0,
|
|
1096
|
+
enabled: true,
|
|
1097
|
+
focused: false,
|
|
1098
|
+
layout: newLayout
|
|
1099
|
+
};
|
|
1100
|
+
bridge.registry.updateElementState(id, newState);
|
|
1101
|
+
onStateChange?.(newState);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
},
|
|
1105
|
+
[bridge, registered, id, onStateChange]
|
|
1106
|
+
);
|
|
1107
|
+
react.useEffect(() => {
|
|
1108
|
+
if (autoRegister) {
|
|
1109
|
+
register();
|
|
1110
|
+
}
|
|
1111
|
+
return () => {
|
|
1112
|
+
unregister();
|
|
1113
|
+
};
|
|
1114
|
+
}, [autoRegister, register, unregister]);
|
|
1115
|
+
react.useCallback(
|
|
1116
|
+
(props) => {
|
|
1117
|
+
propsRef.current = { ...propsRef.current, ...props };
|
|
1118
|
+
if (bridge && registered) {
|
|
1119
|
+
bridge.registry.updateElementProps(id, props);
|
|
1120
|
+
}
|
|
1121
|
+
},
|
|
1122
|
+
[bridge, registered, id]
|
|
1123
|
+
);
|
|
1124
|
+
const getState = react.useCallback(() => {
|
|
1125
|
+
if (!bridge) return null;
|
|
1126
|
+
const element = bridge.registry.getElement(id);
|
|
1127
|
+
return element?.getState() || null;
|
|
1128
|
+
}, [bridge, id]);
|
|
1129
|
+
const getIdentifier = react.useCallback(() => {
|
|
1130
|
+
if (!bridge) return null;
|
|
1131
|
+
const element = bridge.registry.getElement(id);
|
|
1132
|
+
return element?.getIdentifier() || null;
|
|
1133
|
+
}, [bridge, id]);
|
|
1134
|
+
const trigger = react.useCallback(
|
|
1135
|
+
async (action, params) => {
|
|
1136
|
+
if (!bridge) {
|
|
1137
|
+
throw new Error("UI Bridge Native not available");
|
|
1138
|
+
}
|
|
1139
|
+
const response = await bridge.executor.executeAction(id, {
|
|
1140
|
+
action,
|
|
1141
|
+
params
|
|
1142
|
+
});
|
|
1143
|
+
if (!response.success) {
|
|
1144
|
+
throw new Error(response.error || "Action failed");
|
|
1145
|
+
}
|
|
1146
|
+
},
|
|
1147
|
+
[bridge, id]
|
|
1148
|
+
);
|
|
1149
|
+
const registeredElement = react.useMemo(() => {
|
|
1150
|
+
if (!bridge) return null;
|
|
1151
|
+
return bridge.registry.getElement(id) || null;
|
|
1152
|
+
}, [bridge, id, registered]);
|
|
1153
|
+
return {
|
|
1154
|
+
ref,
|
|
1155
|
+
onLayout,
|
|
1156
|
+
bridgeProps,
|
|
1157
|
+
registered,
|
|
1158
|
+
getState,
|
|
1159
|
+
getIdentifier,
|
|
1160
|
+
trigger,
|
|
1161
|
+
register,
|
|
1162
|
+
unregister,
|
|
1163
|
+
registeredElement
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
function useUIElementWithProps(options) {
|
|
1167
|
+
const elementReturn = useUIElement(options);
|
|
1168
|
+
const bridge = useUIBridgeNativeOptional();
|
|
1169
|
+
const captureProps = react.useCallback(
|
|
1170
|
+
(props) => {
|
|
1171
|
+
if (bridge && elementReturn.registered) {
|
|
1172
|
+
bridge.registry.updateElementProps(options.id, props);
|
|
1173
|
+
}
|
|
1174
|
+
},
|
|
1175
|
+
[bridge, elementReturn.registered, options.id]
|
|
1176
|
+
);
|
|
1177
|
+
return {
|
|
1178
|
+
...elementReturn,
|
|
1179
|
+
captureProps
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
function useUIComponent(options) {
|
|
1183
|
+
const bridge = useUIBridgeNativeOptional();
|
|
1184
|
+
const registeredRef = react.useRef(false);
|
|
1185
|
+
const actionsRef = react.useRef(options.actions || []);
|
|
1186
|
+
const elementIdsRef = react.useRef(options.elementIds || []);
|
|
1187
|
+
const { id, name, description, autoRegister = true } = options;
|
|
1188
|
+
react.useEffect(() => {
|
|
1189
|
+
actionsRef.current = options.actions || [];
|
|
1190
|
+
elementIdsRef.current = options.elementIds || [];
|
|
1191
|
+
}, [options.actions, options.elementIds]);
|
|
1192
|
+
const register = react.useCallback(() => {
|
|
1193
|
+
if (!bridge || registeredRef.current) return;
|
|
1194
|
+
bridge.registry.registerComponent(id, {
|
|
1195
|
+
name,
|
|
1196
|
+
description,
|
|
1197
|
+
actions: actionsRef.current.map((a) => ({
|
|
1198
|
+
id: a.id,
|
|
1199
|
+
label: a.label,
|
|
1200
|
+
description: a.description,
|
|
1201
|
+
handler: a.handler
|
|
1202
|
+
})),
|
|
1203
|
+
elementIds: elementIdsRef.current
|
|
1204
|
+
});
|
|
1205
|
+
registeredRef.current = true;
|
|
1206
|
+
}, [bridge, id, name, description]);
|
|
1207
|
+
const unregister = react.useCallback(() => {
|
|
1208
|
+
if (!bridge || !registeredRef.current) return;
|
|
1209
|
+
bridge.registry.unregisterComponent(id);
|
|
1210
|
+
registeredRef.current = false;
|
|
1211
|
+
}, [bridge, id]);
|
|
1212
|
+
const executeAction = react.useCallback(
|
|
1213
|
+
async (actionId, params) => {
|
|
1214
|
+
if (!bridge) {
|
|
1215
|
+
throw new Error("UI Bridge Native not available");
|
|
1216
|
+
}
|
|
1217
|
+
const response = await bridge.executor.executeComponentAction(id, {
|
|
1218
|
+
action: actionId,
|
|
1219
|
+
params
|
|
1220
|
+
});
|
|
1221
|
+
if (!response.success) {
|
|
1222
|
+
throw new Error(response.error || "Action failed");
|
|
1223
|
+
}
|
|
1224
|
+
return response.result;
|
|
1225
|
+
},
|
|
1226
|
+
[bridge, id]
|
|
1227
|
+
);
|
|
1228
|
+
const updateActions = react.useCallback(
|
|
1229
|
+
(actions) => {
|
|
1230
|
+
actionsRef.current = actions;
|
|
1231
|
+
if (registeredRef.current && bridge) {
|
|
1232
|
+
bridge.registry.unregisterComponent(id);
|
|
1233
|
+
registeredRef.current = false;
|
|
1234
|
+
register();
|
|
1235
|
+
}
|
|
1236
|
+
},
|
|
1237
|
+
[bridge, id, register]
|
|
1238
|
+
);
|
|
1239
|
+
const addElement = react.useCallback((elementId) => {
|
|
1240
|
+
if (!elementIdsRef.current.includes(elementId)) {
|
|
1241
|
+
elementIdsRef.current = [...elementIdsRef.current, elementId];
|
|
1242
|
+
}
|
|
1243
|
+
}, []);
|
|
1244
|
+
const removeElement = react.useCallback((elementId) => {
|
|
1245
|
+
elementIdsRef.current = elementIdsRef.current.filter((eid) => eid !== elementId);
|
|
1246
|
+
}, []);
|
|
1247
|
+
react.useEffect(() => {
|
|
1248
|
+
if (autoRegister) {
|
|
1249
|
+
register();
|
|
1250
|
+
}
|
|
1251
|
+
return () => {
|
|
1252
|
+
unregister();
|
|
1253
|
+
};
|
|
1254
|
+
}, [autoRegister, register, unregister]);
|
|
1255
|
+
const registeredComponent = react.useMemo(() => {
|
|
1256
|
+
if (!bridge) return null;
|
|
1257
|
+
return bridge.registry.getComponent(id) || null;
|
|
1258
|
+
}, [bridge, id, registeredRef.current]);
|
|
1259
|
+
return {
|
|
1260
|
+
registered: registeredRef.current,
|
|
1261
|
+
executeAction,
|
|
1262
|
+
register,
|
|
1263
|
+
unregister,
|
|
1264
|
+
updateActions,
|
|
1265
|
+
addElement,
|
|
1266
|
+
removeElement,
|
|
1267
|
+
registeredComponent
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
function useUIComponentAction(handler, deps) {
|
|
1271
|
+
return react.useCallback(handler, deps);
|
|
1272
|
+
}
|
|
1273
|
+
function useUIBridge() {
|
|
1274
|
+
const bridge = useUIBridgeNativeOptional();
|
|
1275
|
+
const available = bridge !== null;
|
|
1276
|
+
const initialized = bridge?.initialized ?? false;
|
|
1277
|
+
const elements = react.useMemo(() => bridge ? bridge.getElements() : [], [bridge]);
|
|
1278
|
+
const components = react.useMemo(() => bridge ? bridge.getComponents() : [], [bridge]);
|
|
1279
|
+
const workflows = react.useMemo(() => bridge ? bridge.registry.getAllWorkflows() : [], [bridge]);
|
|
1280
|
+
const createSnapshot = react.useCallback(() => {
|
|
1281
|
+
if (!bridge) {
|
|
1282
|
+
return {
|
|
1283
|
+
timestamp: Date.now(),
|
|
1284
|
+
elements: [],
|
|
1285
|
+
components: [],
|
|
1286
|
+
workflows: []
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
return bridge.createSnapshot();
|
|
1290
|
+
}, [bridge]);
|
|
1291
|
+
const executeAction = react.useCallback(
|
|
1292
|
+
async (elementId, request) => {
|
|
1293
|
+
if (!bridge) {
|
|
1294
|
+
return {
|
|
1295
|
+
success: false,
|
|
1296
|
+
error: "UI Bridge not available",
|
|
1297
|
+
durationMs: 0,
|
|
1298
|
+
timestamp: Date.now()
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
return bridge.executor.executeAction(elementId, request);
|
|
1302
|
+
},
|
|
1303
|
+
[bridge]
|
|
1304
|
+
);
|
|
1305
|
+
const executeComponentAction = react.useCallback(
|
|
1306
|
+
async (componentId, request) => {
|
|
1307
|
+
if (!bridge) {
|
|
1308
|
+
return {
|
|
1309
|
+
success: false,
|
|
1310
|
+
error: "UI Bridge not available",
|
|
1311
|
+
durationMs: 0,
|
|
1312
|
+
timestamp: Date.now()
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
return bridge.executor.executeComponentAction(componentId, request);
|
|
1316
|
+
},
|
|
1317
|
+
[bridge]
|
|
1318
|
+
);
|
|
1319
|
+
const find = react.useCallback(
|
|
1320
|
+
async (request) => {
|
|
1321
|
+
if (!bridge) {
|
|
1322
|
+
return {
|
|
1323
|
+
elements: [],
|
|
1324
|
+
total: 0,
|
|
1325
|
+
durationMs: 0,
|
|
1326
|
+
timestamp: Date.now()
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
return bridge.executor.find(request || {});
|
|
1330
|
+
},
|
|
1331
|
+
[bridge]
|
|
1332
|
+
);
|
|
1333
|
+
const getElement = react.useCallback((id) => bridge?.registry.getElement(id), [bridge]);
|
|
1334
|
+
const getComponent = react.useCallback((id) => bridge?.registry.getComponent(id), [bridge]);
|
|
1335
|
+
const getElementState = react.useCallback(
|
|
1336
|
+
(id) => {
|
|
1337
|
+
const element = bridge?.registry.getElement(id);
|
|
1338
|
+
return element?.getState() ?? null;
|
|
1339
|
+
},
|
|
1340
|
+
[bridge]
|
|
1341
|
+
);
|
|
1342
|
+
const registerWorkflow = react.useCallback(
|
|
1343
|
+
(workflow) => {
|
|
1344
|
+
bridge?.registry.registerWorkflow(workflow);
|
|
1345
|
+
},
|
|
1346
|
+
[bridge]
|
|
1347
|
+
);
|
|
1348
|
+
const unregisterWorkflow = react.useCallback(
|
|
1349
|
+
(id) => {
|
|
1350
|
+
bridge?.registry.unregisterWorkflow(id);
|
|
1351
|
+
},
|
|
1352
|
+
[bridge]
|
|
1353
|
+
);
|
|
1354
|
+
return {
|
|
1355
|
+
available,
|
|
1356
|
+
initialized,
|
|
1357
|
+
elements,
|
|
1358
|
+
components,
|
|
1359
|
+
workflows,
|
|
1360
|
+
createSnapshot,
|
|
1361
|
+
executeAction,
|
|
1362
|
+
executeComponentAction,
|
|
1363
|
+
find,
|
|
1364
|
+
getElement,
|
|
1365
|
+
getComponent,
|
|
1366
|
+
getElementState,
|
|
1367
|
+
registerWorkflow,
|
|
1368
|
+
unregisterWorkflow
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
function useUIBridgeRequired() {
|
|
1372
|
+
const result = useUIBridge();
|
|
1373
|
+
if (!result.available) {
|
|
1374
|
+
throw new Error("useUIBridgeRequired must be used within a UIBridgeNativeProvider");
|
|
1375
|
+
}
|
|
1376
|
+
return result;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
// src/native/server/types.ts
|
|
1380
|
+
var UI_BRIDGE_NATIVE_ROUTES = {
|
|
1381
|
+
// Control - Elements
|
|
1382
|
+
GET_ELEMENTS: {
|
|
1383
|
+
method: "GET",
|
|
1384
|
+
path: "/ui-bridge/control/elements",
|
|
1385
|
+
description: "List all registered elements"
|
|
1386
|
+
},
|
|
1387
|
+
GET_ELEMENT: {
|
|
1388
|
+
method: "GET",
|
|
1389
|
+
path: "/ui-bridge/control/element/:id",
|
|
1390
|
+
description: "Get element details"
|
|
1391
|
+
},
|
|
1392
|
+
GET_ELEMENT_STATE: {
|
|
1393
|
+
method: "GET",
|
|
1394
|
+
path: "/ui-bridge/control/element/:id/state",
|
|
1395
|
+
description: "Get element state"
|
|
1396
|
+
},
|
|
1397
|
+
EXECUTE_ACTION: {
|
|
1398
|
+
method: "POST",
|
|
1399
|
+
path: "/ui-bridge/control/element/:id/action",
|
|
1400
|
+
description: "Execute action on element"
|
|
1401
|
+
},
|
|
1402
|
+
// Control - Components
|
|
1403
|
+
GET_COMPONENTS: {
|
|
1404
|
+
method: "GET",
|
|
1405
|
+
path: "/ui-bridge/control/components",
|
|
1406
|
+
description: "List all registered components"
|
|
1407
|
+
},
|
|
1408
|
+
GET_COMPONENT: {
|
|
1409
|
+
method: "GET",
|
|
1410
|
+
path: "/ui-bridge/control/component/:id",
|
|
1411
|
+
description: "Get component details"
|
|
1412
|
+
},
|
|
1413
|
+
EXECUTE_COMPONENT_ACTION: {
|
|
1414
|
+
method: "POST",
|
|
1415
|
+
path: "/ui-bridge/control/component/:id/action/:actionId",
|
|
1416
|
+
description: "Execute component action"
|
|
1417
|
+
},
|
|
1418
|
+
// Discovery
|
|
1419
|
+
FIND: {
|
|
1420
|
+
method: "POST",
|
|
1421
|
+
path: "/ui-bridge/control/find",
|
|
1422
|
+
description: "Find elements matching criteria"
|
|
1423
|
+
},
|
|
1424
|
+
GET_SNAPSHOT: {
|
|
1425
|
+
method: "GET",
|
|
1426
|
+
path: "/ui-bridge/control/snapshot",
|
|
1427
|
+
description: "Get full bridge snapshot"
|
|
1428
|
+
},
|
|
1429
|
+
// Workflows
|
|
1430
|
+
GET_WORKFLOWS: {
|
|
1431
|
+
method: "GET",
|
|
1432
|
+
path: "/ui-bridge/control/workflows",
|
|
1433
|
+
description: "List all workflows"
|
|
1434
|
+
},
|
|
1435
|
+
RUN_WORKFLOW: {
|
|
1436
|
+
method: "POST",
|
|
1437
|
+
path: "/ui-bridge/control/workflow/:id/run",
|
|
1438
|
+
description: "Run a workflow"
|
|
1439
|
+
},
|
|
1440
|
+
// Page Navigation
|
|
1441
|
+
PAGE_REFRESH: {
|
|
1442
|
+
method: "POST",
|
|
1443
|
+
path: "/ui-bridge/control/page/refresh",
|
|
1444
|
+
description: "Refresh the current page"
|
|
1445
|
+
},
|
|
1446
|
+
PAGE_NAVIGATE: {
|
|
1447
|
+
method: "POST",
|
|
1448
|
+
path: "/ui-bridge/control/page/navigate",
|
|
1449
|
+
description: "Navigate to a URL"
|
|
1450
|
+
},
|
|
1451
|
+
PAGE_GO_BACK: {
|
|
1452
|
+
method: "POST",
|
|
1453
|
+
path: "/ui-bridge/control/page/back",
|
|
1454
|
+
description: "Go back in navigation history"
|
|
1455
|
+
},
|
|
1456
|
+
PAGE_GO_FORWARD: {
|
|
1457
|
+
method: "POST",
|
|
1458
|
+
path: "/ui-bridge/control/page/forward",
|
|
1459
|
+
description: "Go forward in navigation history"
|
|
1460
|
+
},
|
|
1461
|
+
// Health
|
|
1462
|
+
HEALTH: {
|
|
1463
|
+
method: "GET",
|
|
1464
|
+
path: "/ui-bridge/health",
|
|
1465
|
+
description: "Health check"
|
|
1466
|
+
}
|
|
1467
|
+
};
|
|
1468
|
+
|
|
1469
|
+
// src/native/server/handlers.ts
|
|
1470
|
+
function success(data) {
|
|
1471
|
+
return {
|
|
1472
|
+
success: true,
|
|
1473
|
+
data,
|
|
1474
|
+
timestamp: Date.now()
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
function error(message, code) {
|
|
1478
|
+
return {
|
|
1479
|
+
success: false,
|
|
1480
|
+
error: message,
|
|
1481
|
+
code,
|
|
1482
|
+
timestamp: Date.now()
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
function createServerHandlers(registry, executor) {
|
|
1486
|
+
return {
|
|
1487
|
+
// Elements
|
|
1488
|
+
getElements: async () => {
|
|
1489
|
+
const elements = registry.getAllElements().map((e) => ({
|
|
1490
|
+
id: e.id,
|
|
1491
|
+
type: e.type,
|
|
1492
|
+
label: e.label,
|
|
1493
|
+
identifier: e.getIdentifier(),
|
|
1494
|
+
state: e.getState(),
|
|
1495
|
+
actions: e.actions,
|
|
1496
|
+
customActions: e.customActions ? Object.keys(e.customActions) : void 0
|
|
1497
|
+
}));
|
|
1498
|
+
return success({ elements });
|
|
1499
|
+
},
|
|
1500
|
+
getElement: async (ctx) => {
|
|
1501
|
+
const { id } = ctx.params;
|
|
1502
|
+
const element = registry.getElement(id);
|
|
1503
|
+
if (!element) {
|
|
1504
|
+
return error(`Element not found: ${id}`, "ELEMENT_NOT_FOUND");
|
|
1505
|
+
}
|
|
1506
|
+
return success({
|
|
1507
|
+
element: {
|
|
1508
|
+
id: element.id,
|
|
1509
|
+
type: element.type,
|
|
1510
|
+
label: element.label,
|
|
1511
|
+
identifier: element.getIdentifier(),
|
|
1512
|
+
state: element.getState(),
|
|
1513
|
+
actions: element.actions,
|
|
1514
|
+
customActions: element.customActions ? Object.keys(element.customActions) : void 0
|
|
1515
|
+
}
|
|
1516
|
+
});
|
|
1517
|
+
},
|
|
1518
|
+
getElementState: async (ctx) => {
|
|
1519
|
+
const { id } = ctx.params;
|
|
1520
|
+
const element = registry.getElement(id);
|
|
1521
|
+
if (!element) {
|
|
1522
|
+
return error(`Element not found: ${id}`, "ELEMENT_NOT_FOUND");
|
|
1523
|
+
}
|
|
1524
|
+
return success({ state: element.getState() });
|
|
1525
|
+
},
|
|
1526
|
+
executeAction: async (ctx) => {
|
|
1527
|
+
const { id } = ctx.params;
|
|
1528
|
+
const body = ctx.body;
|
|
1529
|
+
if (!body?.action) {
|
|
1530
|
+
return error("Action is required", "INVALID_REQUEST");
|
|
1531
|
+
}
|
|
1532
|
+
const response = await executor.executeAction(id, {
|
|
1533
|
+
action: body.action,
|
|
1534
|
+
params: body.params,
|
|
1535
|
+
waitOptions: body.waitOptions
|
|
1536
|
+
});
|
|
1537
|
+
if (!response.success) {
|
|
1538
|
+
return error(response.error || "Action failed", "ACTION_FAILED");
|
|
1539
|
+
}
|
|
1540
|
+
return success(response);
|
|
1541
|
+
},
|
|
1542
|
+
// Components
|
|
1543
|
+
getComponents: async () => {
|
|
1544
|
+
const components = registry.getAllComponents().map((c) => ({
|
|
1545
|
+
id: c.id,
|
|
1546
|
+
name: c.name,
|
|
1547
|
+
description: c.description,
|
|
1548
|
+
actions: c.actions.map((a) => ({ id: a.id, label: a.label })),
|
|
1549
|
+
elementIds: c.elementIds
|
|
1550
|
+
}));
|
|
1551
|
+
return success({ components });
|
|
1552
|
+
},
|
|
1553
|
+
getComponent: async (ctx) => {
|
|
1554
|
+
const { id } = ctx.params;
|
|
1555
|
+
const component = registry.getComponent(id);
|
|
1556
|
+
if (!component) {
|
|
1557
|
+
return error(`Component not found: ${id}`, "COMPONENT_NOT_FOUND");
|
|
1558
|
+
}
|
|
1559
|
+
return success({
|
|
1560
|
+
component: {
|
|
1561
|
+
id: component.id,
|
|
1562
|
+
name: component.name,
|
|
1563
|
+
description: component.description,
|
|
1564
|
+
actions: component.actions.map((a) => ({
|
|
1565
|
+
id: a.id,
|
|
1566
|
+
label: a.label,
|
|
1567
|
+
description: a.description
|
|
1568
|
+
})),
|
|
1569
|
+
elementIds: component.elementIds
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
},
|
|
1573
|
+
executeComponentAction: async (ctx) => {
|
|
1574
|
+
const { id, actionId } = ctx.params;
|
|
1575
|
+
const body = ctx.body;
|
|
1576
|
+
const response = await executor.executeComponentAction(id, {
|
|
1577
|
+
action: actionId,
|
|
1578
|
+
params: body?.params
|
|
1579
|
+
});
|
|
1580
|
+
if (!response.success) {
|
|
1581
|
+
return error(response.error || "Action failed", "ACTION_FAILED");
|
|
1582
|
+
}
|
|
1583
|
+
return success(response);
|
|
1584
|
+
},
|
|
1585
|
+
// Discovery
|
|
1586
|
+
find: async (ctx) => {
|
|
1587
|
+
const body = ctx.body;
|
|
1588
|
+
const response = await executor.find({
|
|
1589
|
+
types: body?.types,
|
|
1590
|
+
testIdPattern: body?.testIdPattern,
|
|
1591
|
+
accessibilityLabelPattern: body?.accessibilityLabelPattern,
|
|
1592
|
+
visibleOnly: body?.visibleOnly,
|
|
1593
|
+
limit: body?.limit
|
|
1594
|
+
});
|
|
1595
|
+
return success(response);
|
|
1596
|
+
},
|
|
1597
|
+
getSnapshot: async () => {
|
|
1598
|
+
const snapshot = registry.createSnapshot();
|
|
1599
|
+
return success(snapshot);
|
|
1600
|
+
},
|
|
1601
|
+
// Workflows
|
|
1602
|
+
getWorkflows: async () => {
|
|
1603
|
+
const workflows = registry.getAllWorkflows().map((w) => ({
|
|
1604
|
+
id: w.id,
|
|
1605
|
+
name: w.name,
|
|
1606
|
+
description: w.description,
|
|
1607
|
+
stepCount: w.steps.length
|
|
1608
|
+
}));
|
|
1609
|
+
return success({ workflows });
|
|
1610
|
+
},
|
|
1611
|
+
runWorkflow: async (ctx) => {
|
|
1612
|
+
const { id } = ctx.params;
|
|
1613
|
+
const workflow = registry.getWorkflow(id);
|
|
1614
|
+
if (!workflow) {
|
|
1615
|
+
return error(`Workflow not found: ${id}`, "WORKFLOW_NOT_FOUND");
|
|
1616
|
+
}
|
|
1617
|
+
return success({
|
|
1618
|
+
runId: `run-${Date.now()}`,
|
|
1619
|
+
status: "pending"
|
|
1620
|
+
});
|
|
1621
|
+
},
|
|
1622
|
+
// Page Navigation (stubs — React Native apps should override with their navigation provider)
|
|
1623
|
+
pageRefresh: async () => {
|
|
1624
|
+
return error("Page refresh not supported on native platform", "NOT_SUPPORTED");
|
|
1625
|
+
},
|
|
1626
|
+
pageNavigate: async () => {
|
|
1627
|
+
return error("Page navigation not supported on native platform", "NOT_SUPPORTED");
|
|
1628
|
+
},
|
|
1629
|
+
pageGoBack: async () => {
|
|
1630
|
+
return error("Page go back not supported on native platform", "NOT_SUPPORTED");
|
|
1631
|
+
},
|
|
1632
|
+
pageGoForward: async () => {
|
|
1633
|
+
return error("Page go forward not supported on native platform", "NOT_SUPPORTED");
|
|
1634
|
+
},
|
|
1635
|
+
// Health
|
|
1636
|
+
health: async () => {
|
|
1637
|
+
const stats = registry.getStats();
|
|
1638
|
+
return success({
|
|
1639
|
+
status: "healthy",
|
|
1640
|
+
timestamp: Date.now(),
|
|
1641
|
+
...stats
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// src/native/server/http-server.ts
|
|
1648
|
+
var NativeUIBridgeServer = class {
|
|
1649
|
+
constructor(registry, executor, config = {}) {
|
|
1650
|
+
this.registry = registry;
|
|
1651
|
+
this.executor = executor;
|
|
1652
|
+
this.running = false;
|
|
1653
|
+
this.config = {
|
|
1654
|
+
serverPort: 9876,
|
|
1655
|
+
cors: true,
|
|
1656
|
+
...config
|
|
1657
|
+
};
|
|
1658
|
+
this.handlers = createServerHandlers(registry, executor);
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Set the server adapter
|
|
1662
|
+
*/
|
|
1663
|
+
setAdapter(adapter) {
|
|
1664
|
+
this.adapter = adapter;
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Start the HTTP server
|
|
1668
|
+
*/
|
|
1669
|
+
async start() {
|
|
1670
|
+
if (this.running) {
|
|
1671
|
+
console.warn("[ui-bridge-native] Server already running");
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
if (!this.adapter) {
|
|
1675
|
+
console.warn("[ui-bridge-native] No server adapter configured. Call setAdapter() first.");
|
|
1676
|
+
console.warn("[ui-bridge-native] See documentation for supported adapters.");
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1679
|
+
await this.adapter.start(this.config.serverPort, this.handleRequest.bind(this));
|
|
1680
|
+
this.running = true;
|
|
1681
|
+
console.log(`[ui-bridge-native] HTTP server started on port ${this.config.serverPort}`);
|
|
1682
|
+
}
|
|
1683
|
+
/**
|
|
1684
|
+
* Stop the HTTP server
|
|
1685
|
+
*/
|
|
1686
|
+
async stop() {
|
|
1687
|
+
if (!this.running || !this.adapter) {
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1690
|
+
await this.adapter.stop();
|
|
1691
|
+
this.running = false;
|
|
1692
|
+
console.log("[ui-bridge-native] HTTP server stopped");
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Check if server is running
|
|
1696
|
+
*/
|
|
1697
|
+
isRunning() {
|
|
1698
|
+
return this.running;
|
|
1699
|
+
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Handle incoming HTTP request
|
|
1702
|
+
*/
|
|
1703
|
+
async handleRequest(request) {
|
|
1704
|
+
const headers = {
|
|
1705
|
+
"Content-Type": "application/json"
|
|
1706
|
+
};
|
|
1707
|
+
if (this.config.cors) {
|
|
1708
|
+
headers["Access-Control-Allow-Origin"] = this.config.allowedOrigins ? this.config.allowedOrigins.join(",") : "*";
|
|
1709
|
+
headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS";
|
|
1710
|
+
headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization";
|
|
1711
|
+
}
|
|
1712
|
+
if (request.method === "OPTIONS") {
|
|
1713
|
+
return { status: 204, headers, body: "" };
|
|
1714
|
+
}
|
|
1715
|
+
try {
|
|
1716
|
+
const response = await this.routeRequest(request);
|
|
1717
|
+
return {
|
|
1718
|
+
status: response.success ? 200 : 400,
|
|
1719
|
+
headers,
|
|
1720
|
+
body: JSON.stringify(response)
|
|
1721
|
+
};
|
|
1722
|
+
} catch (error2) {
|
|
1723
|
+
const errorResponse = {
|
|
1724
|
+
success: false,
|
|
1725
|
+
error: error2 instanceof Error ? error2.message : "Internal server error",
|
|
1726
|
+
code: "INTERNAL_ERROR",
|
|
1727
|
+
timestamp: Date.now()
|
|
1728
|
+
};
|
|
1729
|
+
return {
|
|
1730
|
+
status: 500,
|
|
1731
|
+
headers,
|
|
1732
|
+
body: JSON.stringify(errorResponse)
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
/**
|
|
1737
|
+
* Route request to appropriate handler
|
|
1738
|
+
*/
|
|
1739
|
+
async routeRequest(request) {
|
|
1740
|
+
const { method, path, query, body } = request;
|
|
1741
|
+
const parsePath = (pattern, actual) => {
|
|
1742
|
+
const patternParts = pattern.split("/");
|
|
1743
|
+
const actualParts = actual.split("/");
|
|
1744
|
+
if (patternParts.length !== actualParts.length) {
|
|
1745
|
+
return null;
|
|
1746
|
+
}
|
|
1747
|
+
const params2 = {};
|
|
1748
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
1749
|
+
if (patternParts[i].startsWith(":")) {
|
|
1750
|
+
params2[patternParts[i].slice(1)] = actualParts[i];
|
|
1751
|
+
} else if (patternParts[i] !== actualParts[i]) {
|
|
1752
|
+
return null;
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
return params2;
|
|
1756
|
+
};
|
|
1757
|
+
if (method === "GET" && path === "/ui-bridge/health") {
|
|
1758
|
+
return this.handlers.health({ params: {}, query, body });
|
|
1759
|
+
}
|
|
1760
|
+
if (method === "GET" && path === "/ui-bridge/control/elements") {
|
|
1761
|
+
return this.handlers.getElements({ params: {}, query, body });
|
|
1762
|
+
}
|
|
1763
|
+
let params = parsePath("/ui-bridge/control/element/:id", path);
|
|
1764
|
+
if (method === "GET" && params) {
|
|
1765
|
+
return this.handlers.getElement({ params, query, body });
|
|
1766
|
+
}
|
|
1767
|
+
params = parsePath("/ui-bridge/control/element/:id/state", path);
|
|
1768
|
+
if (method === "GET" && params) {
|
|
1769
|
+
return this.handlers.getElementState({ params, query, body });
|
|
1770
|
+
}
|
|
1771
|
+
params = parsePath("/ui-bridge/control/element/:id/action", path);
|
|
1772
|
+
if (method === "POST" && params) {
|
|
1773
|
+
return this.handlers.executeAction({ params, query, body });
|
|
1774
|
+
}
|
|
1775
|
+
if (method === "GET" && path === "/ui-bridge/control/components") {
|
|
1776
|
+
return this.handlers.getComponents({ params: {}, query, body });
|
|
1777
|
+
}
|
|
1778
|
+
params = parsePath("/ui-bridge/control/component/:id", path);
|
|
1779
|
+
if (method === "GET" && params) {
|
|
1780
|
+
return this.handlers.getComponent({ params, query, body });
|
|
1781
|
+
}
|
|
1782
|
+
params = parsePath("/ui-bridge/control/component/:id/action/:actionId", path);
|
|
1783
|
+
if (method === "POST" && params) {
|
|
1784
|
+
return this.handlers.executeComponentAction({ params, query, body });
|
|
1785
|
+
}
|
|
1786
|
+
if (method === "POST" && path === "/ui-bridge/control/find") {
|
|
1787
|
+
return this.handlers.find({ params: {}, query, body });
|
|
1788
|
+
}
|
|
1789
|
+
if (method === "GET" && path === "/ui-bridge/control/snapshot") {
|
|
1790
|
+
return this.handlers.getSnapshot({ params: {}, query, body });
|
|
1791
|
+
}
|
|
1792
|
+
if (method === "GET" && path === "/ui-bridge/control/workflows") {
|
|
1793
|
+
return this.handlers.getWorkflows({ params: {}, query, body });
|
|
1794
|
+
}
|
|
1795
|
+
params = parsePath("/ui-bridge/control/workflow/:id/run", path);
|
|
1796
|
+
if (method === "POST" && params) {
|
|
1797
|
+
return this.handlers.runWorkflow({ params, query, body });
|
|
1798
|
+
}
|
|
1799
|
+
return {
|
|
1800
|
+
success: false,
|
|
1801
|
+
error: `Route not found: ${method} ${path}`,
|
|
1802
|
+
code: "NOT_FOUND",
|
|
1803
|
+
timestamp: Date.now()
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
};
|
|
1807
|
+
function createNativeServer(registry, executor, config) {
|
|
1808
|
+
return new NativeUIBridgeServer(registry, executor, config);
|
|
1809
|
+
}
|
|
1810
|
+
function ElementCard({
|
|
1811
|
+
element,
|
|
1812
|
+
onPress
|
|
1813
|
+
}) {
|
|
1814
|
+
const state = element.getState();
|
|
1815
|
+
const identifier = element.getIdentifier();
|
|
1816
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.TouchableOpacity, { style: styles.elementCard, onPress: () => onPress(element), children: [
|
|
1817
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.elementHeader, children: [
|
|
1818
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.elementId, children: element.id }),
|
|
1819
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.elementType, children: element.type })
|
|
1820
|
+
] }),
|
|
1821
|
+
element.label && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.elementLabel, children: element.label }),
|
|
1822
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.stateRow, children: [
|
|
1823
|
+
/* @__PURE__ */ jsxRuntime.jsx(StateIndicator, { label: "Mounted", value: state.mounted }),
|
|
1824
|
+
/* @__PURE__ */ jsxRuntime.jsx(StateIndicator, { label: "Visible", value: state.visible }),
|
|
1825
|
+
/* @__PURE__ */ jsxRuntime.jsx(StateIndicator, { label: "Enabled", value: state.enabled })
|
|
1826
|
+
] }),
|
|
1827
|
+
identifier.testId && /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: styles.testId, children: [
|
|
1828
|
+
"testID: ",
|
|
1829
|
+
identifier.testId
|
|
1830
|
+
] })
|
|
1831
|
+
] });
|
|
1832
|
+
}
|
|
1833
|
+
function StateIndicator({ label, value }) {
|
|
1834
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.stateIndicator, children: [
|
|
1835
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.stateDot, { backgroundColor: value ? "#4CAF50" : "#F44336" }] }),
|
|
1836
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.stateLabel, children: label })
|
|
1837
|
+
] });
|
|
1838
|
+
}
|
|
1839
|
+
function ElementDetail({
|
|
1840
|
+
element,
|
|
1841
|
+
onClose
|
|
1842
|
+
}) {
|
|
1843
|
+
const state = element.getState();
|
|
1844
|
+
const identifier = element.getIdentifier();
|
|
1845
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.detailContainer, children: [
|
|
1846
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.detailHeader, children: [
|
|
1847
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.detailTitle, children: element.id }),
|
|
1848
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.closeButton, children: "Close" }) })
|
|
1849
|
+
] }),
|
|
1850
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.ScrollView, { style: styles.detailContent, children: [
|
|
1851
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.sectionTitle, children: "Type" }),
|
|
1852
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.sectionValue, children: element.type }),
|
|
1853
|
+
element.label && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1854
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.sectionTitle, children: "Label" }),
|
|
1855
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.sectionValue, children: element.label })
|
|
1856
|
+
] }),
|
|
1857
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.sectionTitle, children: "State" }),
|
|
1858
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.stateSection, children: [
|
|
1859
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: styles.stateText, children: [
|
|
1860
|
+
"Mounted: ",
|
|
1861
|
+
String(state.mounted)
|
|
1862
|
+
] }),
|
|
1863
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: styles.stateText, children: [
|
|
1864
|
+
"Visible: ",
|
|
1865
|
+
String(state.visible)
|
|
1866
|
+
] }),
|
|
1867
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: styles.stateText, children: [
|
|
1868
|
+
"Enabled: ",
|
|
1869
|
+
String(state.enabled)
|
|
1870
|
+
] }),
|
|
1871
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: styles.stateText, children: [
|
|
1872
|
+
"Focused: ",
|
|
1873
|
+
String(state.focused)
|
|
1874
|
+
] }),
|
|
1875
|
+
state.value !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: styles.stateText, children: [
|
|
1876
|
+
"Value: ",
|
|
1877
|
+
state.value
|
|
1878
|
+
] })
|
|
1879
|
+
] }),
|
|
1880
|
+
state.layout && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1881
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.sectionTitle, children: "Layout" }),
|
|
1882
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.stateSection, children: [
|
|
1883
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: styles.stateText, children: [
|
|
1884
|
+
"Position: (",
|
|
1885
|
+
state.layout.x,
|
|
1886
|
+
", ",
|
|
1887
|
+
state.layout.y,
|
|
1888
|
+
")"
|
|
1889
|
+
] }),
|
|
1890
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: styles.stateText, children: [
|
|
1891
|
+
"Size: ",
|
|
1892
|
+
state.layout.width,
|
|
1893
|
+
" x ",
|
|
1894
|
+
state.layout.height
|
|
1895
|
+
] }),
|
|
1896
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: styles.stateText, children: [
|
|
1897
|
+
"Page: (",
|
|
1898
|
+
state.layout.pageX,
|
|
1899
|
+
", ",
|
|
1900
|
+
state.layout.pageY,
|
|
1901
|
+
")"
|
|
1902
|
+
] })
|
|
1903
|
+
] })
|
|
1904
|
+
] }),
|
|
1905
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.sectionTitle, children: "Identifier" }),
|
|
1906
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.stateSection, children: [
|
|
1907
|
+
identifier.uiId && /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: styles.stateText, children: [
|
|
1908
|
+
"uiId: ",
|
|
1909
|
+
identifier.uiId
|
|
1910
|
+
] }),
|
|
1911
|
+
identifier.testId && /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: styles.stateText, children: [
|
|
1912
|
+
"testId: ",
|
|
1913
|
+
identifier.testId
|
|
1914
|
+
] }),
|
|
1915
|
+
identifier.accessibilityLabel && /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: styles.stateText, children: [
|
|
1916
|
+
"a11yLabel: ",
|
|
1917
|
+
identifier.accessibilityLabel
|
|
1918
|
+
] }),
|
|
1919
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: styles.stateText, children: [
|
|
1920
|
+
"treePath: ",
|
|
1921
|
+
identifier.treePath
|
|
1922
|
+
] })
|
|
1923
|
+
] }),
|
|
1924
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.sectionTitle, children: "Actions" }),
|
|
1925
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.actionsSection, children: element.actions.map((action) => /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.actionBadge, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.actionText, children: action }) }, action)) }),
|
|
1926
|
+
element.customActions && Object.keys(element.customActions).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1927
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.sectionTitle, children: "Custom Actions" }),
|
|
1928
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.actionsSection, children: Object.keys(element.customActions).map((action) => /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.actionBadge, styles.customActionBadge], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.actionText, children: action }) }, action)) })
|
|
1929
|
+
] })
|
|
1930
|
+
] })
|
|
1931
|
+
] });
|
|
1932
|
+
}
|
|
1933
|
+
function UIBridgeInspector({
|
|
1934
|
+
visible = false,
|
|
1935
|
+
onClose,
|
|
1936
|
+
togglePosition = "bottom-right"
|
|
1937
|
+
}) {
|
|
1938
|
+
const bridge = useUIBridgeNativeOptional();
|
|
1939
|
+
const [showInspector, setShowInspector] = react.useState(visible);
|
|
1940
|
+
const [selectedElement, setSelectedElement] = react.useState(null);
|
|
1941
|
+
const elements = react.useMemo(() => bridge ? bridge.getElements() : [], [bridge, showInspector]);
|
|
1942
|
+
const components = react.useMemo(() => bridge ? bridge.getComponents() : [], [bridge, showInspector]);
|
|
1943
|
+
const handleToggle = react.useCallback(() => {
|
|
1944
|
+
setShowInspector((prev) => !prev);
|
|
1945
|
+
}, []);
|
|
1946
|
+
const handleClose = react.useCallback(() => {
|
|
1947
|
+
setShowInspector(false);
|
|
1948
|
+
onClose?.();
|
|
1949
|
+
}, [onClose]);
|
|
1950
|
+
const handleSelectElement = react.useCallback((element) => {
|
|
1951
|
+
setSelectedElement(element);
|
|
1952
|
+
}, []);
|
|
1953
|
+
const handleCloseDetail = react.useCallback(() => {
|
|
1954
|
+
setSelectedElement(null);
|
|
1955
|
+
}, []);
|
|
1956
|
+
const toggleStyle = react.useMemo(() => {
|
|
1957
|
+
switch (togglePosition) {
|
|
1958
|
+
case "top-left":
|
|
1959
|
+
return { top: 50, left: 10 };
|
|
1960
|
+
case "top-right":
|
|
1961
|
+
return { top: 50, right: 10 };
|
|
1962
|
+
case "bottom-left":
|
|
1963
|
+
return { bottom: 50, left: 10 };
|
|
1964
|
+
case "bottom-right":
|
|
1965
|
+
default:
|
|
1966
|
+
return { bottom: 50, right: 10 };
|
|
1967
|
+
}
|
|
1968
|
+
}, [togglePosition]);
|
|
1969
|
+
if (!bridge) {
|
|
1970
|
+
return null;
|
|
1971
|
+
}
|
|
1972
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1973
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { style: [styles.toggleButton, toggleStyle], onPress: handleToggle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.toggleText, children: "UI" }) }),
|
|
1974
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1975
|
+
reactNative.Modal,
|
|
1976
|
+
{
|
|
1977
|
+
visible: showInspector,
|
|
1978
|
+
animationType: "slide",
|
|
1979
|
+
transparent: true,
|
|
1980
|
+
onRequestClose: handleClose,
|
|
1981
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.modalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.inspectorContainer, children: [
|
|
1982
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.header, children: [
|
|
1983
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.headerTitle, children: "UI Bridge Inspector" }),
|
|
1984
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: handleClose, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.closeButton, children: "X" }) })
|
|
1985
|
+
] }),
|
|
1986
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.statsRow, children: [
|
|
1987
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.stat, children: [
|
|
1988
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.statValue, children: elements.length }),
|
|
1989
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.statLabel, children: "Elements" })
|
|
1990
|
+
] }),
|
|
1991
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.stat, children: [
|
|
1992
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.statValue, children: components.length }),
|
|
1993
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.statLabel, children: "Components" })
|
|
1994
|
+
] }),
|
|
1995
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.stat, children: [
|
|
1996
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.statValue, children: bridge.serverRunning ? "ON" : "OFF" }),
|
|
1997
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.statLabel, children: "Server" })
|
|
1998
|
+
] })
|
|
1999
|
+
] }),
|
|
2000
|
+
selectedElement ? /* @__PURE__ */ jsxRuntime.jsx(ElementDetail, { element: selectedElement, onClose: handleCloseDetail }) : /* @__PURE__ */ jsxRuntime.jsxs(reactNative.ScrollView, { style: styles.elementList, children: [
|
|
2001
|
+
elements.map((element) => /* @__PURE__ */ jsxRuntime.jsx(ElementCard, { element, onPress: handleSelectElement }, element.id)),
|
|
2002
|
+
elements.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.emptyText, children: "No elements registered yet" })
|
|
2003
|
+
] })
|
|
2004
|
+
] }) })
|
|
2005
|
+
}
|
|
2006
|
+
)
|
|
2007
|
+
] });
|
|
2008
|
+
}
|
|
2009
|
+
var { height: screenHeight } = reactNative.Dimensions.get("window");
|
|
2010
|
+
var styles = reactNative.StyleSheet.create({
|
|
2011
|
+
toggleButton: {
|
|
2012
|
+
position: "absolute",
|
|
2013
|
+
width: 44,
|
|
2014
|
+
height: 44,
|
|
2015
|
+
borderRadius: 22,
|
|
2016
|
+
backgroundColor: "#2196F3",
|
|
2017
|
+
justifyContent: "center",
|
|
2018
|
+
alignItems: "center",
|
|
2019
|
+
shadowColor: "#000",
|
|
2020
|
+
shadowOffset: { width: 0, height: 2 },
|
|
2021
|
+
shadowOpacity: 0.25,
|
|
2022
|
+
shadowRadius: 4,
|
|
2023
|
+
elevation: 5,
|
|
2024
|
+
zIndex: 1e3
|
|
2025
|
+
},
|
|
2026
|
+
toggleText: {
|
|
2027
|
+
color: "#fff",
|
|
2028
|
+
fontWeight: "bold",
|
|
2029
|
+
fontSize: 14
|
|
2030
|
+
},
|
|
2031
|
+
modalContainer: {
|
|
2032
|
+
flex: 1,
|
|
2033
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
2034
|
+
justifyContent: "flex-end"
|
|
2035
|
+
},
|
|
2036
|
+
inspectorContainer: {
|
|
2037
|
+
backgroundColor: "#1e1e1e",
|
|
2038
|
+
borderTopLeftRadius: 16,
|
|
2039
|
+
borderTopRightRadius: 16,
|
|
2040
|
+
maxHeight: screenHeight * 0.8
|
|
2041
|
+
},
|
|
2042
|
+
header: {
|
|
2043
|
+
flexDirection: "row",
|
|
2044
|
+
justifyContent: "space-between",
|
|
2045
|
+
alignItems: "center",
|
|
2046
|
+
padding: 16,
|
|
2047
|
+
borderBottomWidth: 1,
|
|
2048
|
+
borderBottomColor: "#333"
|
|
2049
|
+
},
|
|
2050
|
+
headerTitle: {
|
|
2051
|
+
color: "#fff",
|
|
2052
|
+
fontSize: 18,
|
|
2053
|
+
fontWeight: "bold"
|
|
2054
|
+
},
|
|
2055
|
+
closeButton: {
|
|
2056
|
+
color: "#2196F3",
|
|
2057
|
+
fontSize: 16,
|
|
2058
|
+
fontWeight: "bold"
|
|
2059
|
+
},
|
|
2060
|
+
statsRow: {
|
|
2061
|
+
flexDirection: "row",
|
|
2062
|
+
padding: 12,
|
|
2063
|
+
borderBottomWidth: 1,
|
|
2064
|
+
borderBottomColor: "#333"
|
|
2065
|
+
},
|
|
2066
|
+
stat: {
|
|
2067
|
+
flex: 1,
|
|
2068
|
+
alignItems: "center"
|
|
2069
|
+
},
|
|
2070
|
+
statValue: {
|
|
2071
|
+
color: "#fff",
|
|
2072
|
+
fontSize: 20,
|
|
2073
|
+
fontWeight: "bold"
|
|
2074
|
+
},
|
|
2075
|
+
statLabel: {
|
|
2076
|
+
color: "#888",
|
|
2077
|
+
fontSize: 12,
|
|
2078
|
+
marginTop: 2
|
|
2079
|
+
},
|
|
2080
|
+
elementList: {
|
|
2081
|
+
padding: 12
|
|
2082
|
+
},
|
|
2083
|
+
elementCard: {
|
|
2084
|
+
backgroundColor: "#2d2d2d",
|
|
2085
|
+
borderRadius: 8,
|
|
2086
|
+
padding: 12,
|
|
2087
|
+
marginBottom: 8
|
|
2088
|
+
},
|
|
2089
|
+
elementHeader: {
|
|
2090
|
+
flexDirection: "row",
|
|
2091
|
+
justifyContent: "space-between",
|
|
2092
|
+
alignItems: "center"
|
|
2093
|
+
},
|
|
2094
|
+
elementId: {
|
|
2095
|
+
color: "#fff",
|
|
2096
|
+
fontSize: 14,
|
|
2097
|
+
fontWeight: "bold"
|
|
2098
|
+
},
|
|
2099
|
+
elementType: {
|
|
2100
|
+
color: "#888",
|
|
2101
|
+
fontSize: 12,
|
|
2102
|
+
backgroundColor: "#444",
|
|
2103
|
+
paddingHorizontal: 8,
|
|
2104
|
+
paddingVertical: 2,
|
|
2105
|
+
borderRadius: 4
|
|
2106
|
+
},
|
|
2107
|
+
elementLabel: {
|
|
2108
|
+
color: "#aaa",
|
|
2109
|
+
fontSize: 12,
|
|
2110
|
+
marginTop: 4
|
|
2111
|
+
},
|
|
2112
|
+
stateRow: {
|
|
2113
|
+
flexDirection: "row",
|
|
2114
|
+
marginTop: 8
|
|
2115
|
+
},
|
|
2116
|
+
stateIndicator: {
|
|
2117
|
+
flexDirection: "row",
|
|
2118
|
+
alignItems: "center",
|
|
2119
|
+
marginRight: 12
|
|
2120
|
+
},
|
|
2121
|
+
stateDot: {
|
|
2122
|
+
width: 8,
|
|
2123
|
+
height: 8,
|
|
2124
|
+
borderRadius: 4,
|
|
2125
|
+
marginRight: 4
|
|
2126
|
+
},
|
|
2127
|
+
stateLabel: {
|
|
2128
|
+
color: "#888",
|
|
2129
|
+
fontSize: 10
|
|
2130
|
+
},
|
|
2131
|
+
testId: {
|
|
2132
|
+
color: "#666",
|
|
2133
|
+
fontSize: 10,
|
|
2134
|
+
marginTop: 4,
|
|
2135
|
+
fontFamily: "monospace"
|
|
2136
|
+
},
|
|
2137
|
+
emptyText: {
|
|
2138
|
+
color: "#888",
|
|
2139
|
+
textAlign: "center",
|
|
2140
|
+
marginTop: 20
|
|
2141
|
+
},
|
|
2142
|
+
detailContainer: {
|
|
2143
|
+
flex: 1
|
|
2144
|
+
},
|
|
2145
|
+
detailHeader: {
|
|
2146
|
+
flexDirection: "row",
|
|
2147
|
+
justifyContent: "space-between",
|
|
2148
|
+
alignItems: "center",
|
|
2149
|
+
padding: 12,
|
|
2150
|
+
borderBottomWidth: 1,
|
|
2151
|
+
borderBottomColor: "#333"
|
|
2152
|
+
},
|
|
2153
|
+
detailTitle: {
|
|
2154
|
+
color: "#fff",
|
|
2155
|
+
fontSize: 16,
|
|
2156
|
+
fontWeight: "bold"
|
|
2157
|
+
},
|
|
2158
|
+
detailContent: {
|
|
2159
|
+
padding: 12
|
|
2160
|
+
},
|
|
2161
|
+
sectionTitle: {
|
|
2162
|
+
color: "#888",
|
|
2163
|
+
fontSize: 12,
|
|
2164
|
+
marginTop: 12,
|
|
2165
|
+
marginBottom: 4,
|
|
2166
|
+
textTransform: "uppercase"
|
|
2167
|
+
},
|
|
2168
|
+
sectionValue: {
|
|
2169
|
+
color: "#fff",
|
|
2170
|
+
fontSize: 14
|
|
2171
|
+
},
|
|
2172
|
+
stateSection: {
|
|
2173
|
+
backgroundColor: "#2d2d2d",
|
|
2174
|
+
borderRadius: 8,
|
|
2175
|
+
padding: 8
|
|
2176
|
+
},
|
|
2177
|
+
stateText: {
|
|
2178
|
+
color: "#ddd",
|
|
2179
|
+
fontSize: 12,
|
|
2180
|
+
fontFamily: "monospace",
|
|
2181
|
+
marginBottom: 2
|
|
2182
|
+
},
|
|
2183
|
+
actionsSection: {
|
|
2184
|
+
flexDirection: "row",
|
|
2185
|
+
flexWrap: "wrap"
|
|
2186
|
+
},
|
|
2187
|
+
actionBadge: {
|
|
2188
|
+
backgroundColor: "#2196F3",
|
|
2189
|
+
paddingHorizontal: 8,
|
|
2190
|
+
paddingVertical: 4,
|
|
2191
|
+
borderRadius: 4,
|
|
2192
|
+
marginRight: 6,
|
|
2193
|
+
marginBottom: 6
|
|
2194
|
+
},
|
|
2195
|
+
customActionBadge: {
|
|
2196
|
+
backgroundColor: "#9C27B0"
|
|
2197
|
+
},
|
|
2198
|
+
actionText: {
|
|
2199
|
+
color: "#fff",
|
|
2200
|
+
fontSize: 12
|
|
2201
|
+
}
|
|
2202
|
+
});
|
|
2203
|
+
|
|
2204
|
+
exports.DefaultNativeActionExecutor = DefaultNativeActionExecutor;
|
|
2205
|
+
exports.NativeUIBridgeRegistry = NativeUIBridgeRegistry;
|
|
2206
|
+
exports.NativeUIBridgeServer = NativeUIBridgeServer;
|
|
2207
|
+
exports.UIBridgeInspector = UIBridgeInspector;
|
|
2208
|
+
exports.UIBridgeNativeProvider = UIBridgeNativeProvider;
|
|
2209
|
+
exports.UI_BRIDGE_NATIVE_ROUTES = UI_BRIDGE_NATIVE_ROUTES;
|
|
2210
|
+
exports.buildTreePath = buildTreePath;
|
|
2211
|
+
exports.createNativeActionExecutor = createNativeActionExecutor;
|
|
2212
|
+
exports.createNativeElementIdentifier = createNativeElementIdentifier;
|
|
2213
|
+
exports.createNativeServer = createNativeServer;
|
|
2214
|
+
exports.createServerHandlers = createServerHandlers;
|
|
2215
|
+
exports.findAllByPattern = findAllByPattern;
|
|
2216
|
+
exports.findElementByIdentifier = findElementByIdentifier;
|
|
2217
|
+
exports.getGlobalRegistry = getGlobalRegistry;
|
|
2218
|
+
exports.matchesIdentifier = matchesIdentifier;
|
|
2219
|
+
exports.parseTreePath = parseTreePath;
|
|
2220
|
+
exports.resetGlobalRegistry = resetGlobalRegistry;
|
|
2221
|
+
exports.setGlobalRegistry = setGlobalRegistry;
|
|
2222
|
+
exports.useUIBridge = useUIBridge;
|
|
2223
|
+
exports.useUIBridgeNative = useUIBridgeNative;
|
|
2224
|
+
exports.useUIBridgeNativeOptional = useUIBridgeNativeOptional;
|
|
2225
|
+
exports.useUIBridgeNativeRequired = useUIBridgeNativeRequired;
|
|
2226
|
+
exports.useUIBridgeRequired = useUIBridgeRequired;
|
|
2227
|
+
exports.useUIComponent = useUIComponent;
|
|
2228
|
+
exports.useUIComponentAction = useUIComponentAction;
|
|
2229
|
+
exports.useUIElement = useUIElement;
|
|
2230
|
+
exports.useUIElementWithProps = useUIElementWithProps;
|
|
2231
|
+
//# sourceMappingURL=index.js.map
|
|
2232
|
+
//# sourceMappingURL=index.js.map
|