@qontinui/ui-bridge-native 0.6.8 → 0.6.10
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/control/action-executor.d.ts +15 -0
- package/dist/control/action-executor.d.ts.map +1 -1
- package/dist/control/index.js +36 -1
- package/dist/control/index.js.map +1 -1
- package/dist/control/index.mjs +36 -1
- package/dist/control/index.mjs.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +1 -1
- package/dist/core/index.mjs.map +1 -1
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +153 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +152 -27
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.js +94 -26
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +94 -26
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/useUIElement.d.ts +10 -0
- package/dist/react/useUIElement.d.ts.map +1 -1
- package/dist/server/bind-retry.d.ts +64 -0
- package/dist/server/bind-retry.d.ts.map +1 -0
- package/dist/server/handlers.d.ts.map +1 -1
- package/dist/server/http-server.d.ts.map +1 -1
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +103 -22
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +102 -23
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/types.d.ts +14 -0
- package/dist/server/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/react/index.mjs
CHANGED
|
@@ -26,7 +26,7 @@ function inferActions(type) {
|
|
|
26
26
|
case "pressable":
|
|
27
27
|
return [...baseActions, "click", "press", "longPress", "doubleTap"];
|
|
28
28
|
case "input":
|
|
29
|
-
return [...baseActions, "click", "press", "type", "clear"];
|
|
29
|
+
return [...baseActions, "click", "press", "type", "setValue", "clear"];
|
|
30
30
|
case "text":
|
|
31
31
|
return [...baseActions, "click", "press", "longPress"];
|
|
32
32
|
case "view":
|
|
@@ -933,9 +933,10 @@ var DefaultNativeActionExecutor = class {
|
|
|
933
933
|
requestId: request.requestId,
|
|
934
934
|
durationMs: successTime - startTime
|
|
935
935
|
});
|
|
936
|
+
const fresh = this.registry.getElement(registered.id) ?? registered;
|
|
936
937
|
return {
|
|
937
938
|
success: true,
|
|
938
|
-
elementState:
|
|
939
|
+
elementState: fresh.getState(),
|
|
939
940
|
result,
|
|
940
941
|
durationMs: successTime - startTime,
|
|
941
942
|
timestamp: successTime,
|
|
@@ -988,6 +989,8 @@ var DefaultNativeActionExecutor = class {
|
|
|
988
989
|
return this.performDoubleTap(props);
|
|
989
990
|
case "type":
|
|
990
991
|
return this.performType(element, props, params);
|
|
992
|
+
case "setValue":
|
|
993
|
+
return this.performSetValue(element, props, params);
|
|
991
994
|
case "clear":
|
|
992
995
|
return this.performClear(element, props);
|
|
993
996
|
case "focus":
|
|
@@ -1080,6 +1083,38 @@ var DefaultNativeActionExecutor = class {
|
|
|
1080
1083
|
this.registry.updateElementState(element.id, { value: params.text });
|
|
1081
1084
|
}
|
|
1082
1085
|
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Perform setValue action.
|
|
1088
|
+
*
|
|
1089
|
+
* Web/runner UI Bridge parity: `setValue` replaces the input's value
|
|
1090
|
+
* atomically in a single call (no per-character keystroke simulation),
|
|
1091
|
+
* mirroring the web bridge's `setValue` semantics. On native there is no
|
|
1092
|
+
* DOM value to assign, so we drive the controlled-input handler
|
|
1093
|
+
* (`onChangeText`, falling back to `onChange`) with the full string. The
|
|
1094
|
+
* registry's `state.value` is then synced so a subsequent GET on the
|
|
1095
|
+
* element reflects the new value.
|
|
1096
|
+
*
|
|
1097
|
+
* Unlike `type`, this does not append to existing content and ignores
|
|
1098
|
+
* `delay` — it is the deterministic "set the field to exactly this" action.
|
|
1099
|
+
*/
|
|
1100
|
+
async performSetValue(element, props, params) {
|
|
1101
|
+
const next = typeof params?.text === "string" ? params.text : typeof params?.value === "string" ? params.value : void 0;
|
|
1102
|
+
if (typeof next !== "string") {
|
|
1103
|
+
throw new Error('setValue action requires a string "text" (or "value") parameter');
|
|
1104
|
+
}
|
|
1105
|
+
if (typeof props.onChangeText === "function") {
|
|
1106
|
+
props.onChangeText(next);
|
|
1107
|
+
} else if (typeof props.onChange === "function") {
|
|
1108
|
+
props.onChange({
|
|
1109
|
+
nativeEvent: { text: next }
|
|
1110
|
+
});
|
|
1111
|
+
} else {
|
|
1112
|
+
throw new Error("No text change handler found on element");
|
|
1113
|
+
}
|
|
1114
|
+
if (element) {
|
|
1115
|
+
this.registry.updateElementState(element.id, { value: next });
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1083
1118
|
/**
|
|
1084
1119
|
* Perform clear action
|
|
1085
1120
|
*/
|
|
@@ -2072,6 +2107,13 @@ function matchElementsByText(registry, text, opts) {
|
|
|
2072
2107
|
scored.sort((a, b) => a.rank - b.rank);
|
|
2073
2108
|
return scored.map((s) => s.element);
|
|
2074
2109
|
}
|
|
2110
|
+
function resolveElementState(element) {
|
|
2111
|
+
const state = element.getState();
|
|
2112
|
+
if (element.type === "input" && state.value === void 0 && typeof element.props?.value === "string") {
|
|
2113
|
+
return { ...state, value: element.props.value };
|
|
2114
|
+
}
|
|
2115
|
+
return state;
|
|
2116
|
+
}
|
|
2075
2117
|
async function executeWorkflowStep(step, registry, executor) {
|
|
2076
2118
|
const startTime = Date.now();
|
|
2077
2119
|
try {
|
|
@@ -2308,7 +2350,7 @@ function createServerHandlers(registry, executor, config) {
|
|
|
2308
2350
|
type: element.type,
|
|
2309
2351
|
label: element.label,
|
|
2310
2352
|
identifier: element.getIdentifier(),
|
|
2311
|
-
state: element
|
|
2353
|
+
state: resolveElementState(element),
|
|
2312
2354
|
actions: element.actions,
|
|
2313
2355
|
customActions: element.customActions ? Object.keys(element.customActions) : void 0,
|
|
2314
2356
|
registeredHandlers: handlers.length > 0 ? handlers : void 0,
|
|
@@ -2322,18 +2364,19 @@ function createServerHandlers(registry, executor, config) {
|
|
|
2322
2364
|
if (!element) {
|
|
2323
2365
|
return error2(`Element not found: ${id}`, "ELEMENT_NOT_FOUND");
|
|
2324
2366
|
}
|
|
2325
|
-
return success2({ state: element
|
|
2367
|
+
return success2({ state: resolveElementState(element) });
|
|
2326
2368
|
},
|
|
2327
2369
|
executeAction: async (ctx) => {
|
|
2328
|
-
const
|
|
2329
|
-
const
|
|
2330
|
-
|
|
2370
|
+
const rawBody = ctx.body ?? {};
|
|
2371
|
+
const envelope = rawBody.request && typeof rawBody.request === "object" ? rawBody.request : rawBody;
|
|
2372
|
+
const id = ctx.params.id ?? rawBody.id ?? "";
|
|
2373
|
+
if (!envelope?.action) {
|
|
2331
2374
|
return error2("Action is required", "INVALID_REQUEST");
|
|
2332
2375
|
}
|
|
2333
2376
|
const response = await executor.executeAction(id, {
|
|
2334
|
-
action:
|
|
2335
|
-
params:
|
|
2336
|
-
waitOptions:
|
|
2377
|
+
action: envelope.action,
|
|
2378
|
+
params: envelope.params,
|
|
2379
|
+
waitOptions: envelope.waitOptions
|
|
2337
2380
|
});
|
|
2338
2381
|
if (!response.success) {
|
|
2339
2382
|
return error2(response.error || "Action failed", "ACTION_FAILED");
|
|
@@ -2845,7 +2888,8 @@ function createServerHandlers(registry, executor, config) {
|
|
|
2845
2888
|
return success2({
|
|
2846
2889
|
entries: [],
|
|
2847
2890
|
count: 0,
|
|
2848
|
-
bufferSize: 0
|
|
2891
|
+
bufferSize: 0,
|
|
2892
|
+
installed: false
|
|
2849
2893
|
});
|
|
2850
2894
|
}
|
|
2851
2895
|
const entries = buf.entries({
|
|
@@ -2855,7 +2899,8 @@ function createServerHandlers(registry, executor, config) {
|
|
|
2855
2899
|
return success2({
|
|
2856
2900
|
entries,
|
|
2857
2901
|
count: entries.length,
|
|
2858
|
-
bufferSize: buf.size()
|
|
2902
|
+
bufferSize: buf.size(),
|
|
2903
|
+
installed: buf.isInstalled()
|
|
2859
2904
|
});
|
|
2860
2905
|
},
|
|
2861
2906
|
getNetworkRequests: async (ctx) => {
|
|
@@ -2866,7 +2911,8 @@ function createServerHandlers(registry, executor, config) {
|
|
|
2866
2911
|
return success2({
|
|
2867
2912
|
entries: [],
|
|
2868
2913
|
count: 0,
|
|
2869
|
-
bufferSize: 0
|
|
2914
|
+
bufferSize: 0,
|
|
2915
|
+
installed: false
|
|
2870
2916
|
});
|
|
2871
2917
|
}
|
|
2872
2918
|
const entries = buf.entries({
|
|
@@ -2876,7 +2922,8 @@ function createServerHandlers(registry, executor, config) {
|
|
|
2876
2922
|
return success2({
|
|
2877
2923
|
entries,
|
|
2878
2924
|
count: entries.length,
|
|
2879
|
-
bufferSize: buf.size()
|
|
2925
|
+
bufferSize: buf.size(),
|
|
2926
|
+
installed: buf.isInstalled()
|
|
2880
2927
|
});
|
|
2881
2928
|
},
|
|
2882
2929
|
// Test hooks — modal stack manipulation
|
|
@@ -2968,7 +3015,7 @@ function createServerHandlers(registry, executor, config) {
|
|
|
2968
3015
|
};
|
|
2969
3016
|
if (config?.appInfo) {
|
|
2970
3017
|
response.uiBridge = {
|
|
2971
|
-
version: "0.
|
|
3018
|
+
version: "0.6.10",
|
|
2972
3019
|
...config.appInfo,
|
|
2973
3020
|
capabilities: ["elements", "components", "actions", "design"],
|
|
2974
3021
|
elementCount: stats.elements,
|
|
@@ -3295,7 +3342,10 @@ var WS_ROUTES = [
|
|
|
3295
3342
|
// same handler at both paths to keep agents portable across platforms.
|
|
3296
3343
|
{ pattern: "ai/find", handler: "find", httpMethods: ["POST"] },
|
|
3297
3344
|
{ pattern: "control/snapshot", handler: "getSnapshot", httpMethods: ["GET"] },
|
|
3298
|
-
|
|
3345
|
+
// Discovery alias — web/runner parity with `POST /control/discover` (a
|
|
3346
|
+
// legacy alias for snapshot/find). Mounted on HTTP (GET+POST) so mobile
|
|
3347
|
+
// doesn't 404 where web returns 200; delegates to the snapshot handler.
|
|
3348
|
+
{ pattern: "control/discover", handler: "getSnapshot", httpMethods: ["GET", "POST"] },
|
|
3299
3349
|
// Page health — accepts both GET and POST so the canonical skill invocation
|
|
3300
3350
|
// (`POST .../control/page-health` with an empty body) and ad-hoc curls both
|
|
3301
3351
|
// work without a body-shape gotcha.
|
|
@@ -3389,19 +3439,26 @@ var WS_ROUTES = [
|
|
|
3389
3439
|
httpMethods: ["POST"],
|
|
3390
3440
|
requiresTestHooks: true
|
|
3391
3441
|
},
|
|
3392
|
-
// Observability — last-N console errors + network requests.
|
|
3393
|
-
//
|
|
3442
|
+
// Observability — last-N console errors + network requests.
|
|
3443
|
+
//
|
|
3444
|
+
// Always mounted (not testHooks-gated) so they return a schema-valid 200
|
|
3445
|
+
// on every build instead of a 404 that agents can't distinguish from a
|
|
3446
|
+
// missing route. The INVASIVE part — monkey-patching `console.error` /
|
|
3447
|
+
// `fetch` / `XMLHttpRequest` — stays gated behind `testHooks` (see
|
|
3448
|
+
// NativeUIBridgeServer.start → buffer.install()). When the patches aren't
|
|
3449
|
+
// installed the handlers report `installed: false` + empty entries, so
|
|
3450
|
+
// production builds expose the contract without observing real users'
|
|
3451
|
+
// console/network traffic. Set `features.testHooks` (typically on
|
|
3452
|
+
// `__DEV__`) to populate the buffers with live data.
|
|
3394
3453
|
{
|
|
3395
3454
|
pattern: "control/console-errors",
|
|
3396
3455
|
handler: "getConsoleErrors",
|
|
3397
|
-
httpMethods: ["GET"]
|
|
3398
|
-
requiresTestHooks: true
|
|
3456
|
+
httpMethods: ["GET"]
|
|
3399
3457
|
},
|
|
3400
3458
|
{
|
|
3401
3459
|
pattern: "sdk/network-requests",
|
|
3402
3460
|
handler: "getNetworkRequests",
|
|
3403
|
-
httpMethods: ["GET"]
|
|
3404
|
-
requiresTestHooks: true
|
|
3461
|
+
httpMethods: ["GET"]
|
|
3405
3462
|
}
|
|
3406
3463
|
];
|
|
3407
3464
|
var HANDLER_DESCRIPTIONS = {
|
|
@@ -3448,8 +3505,8 @@ var HANDLER_DESCRIPTIONS = {
|
|
|
3448
3505
|
typeInto: "App-agnostic: type into the input matching a label: body { label, text, clear?, visibleOnly? }. (selector form is web-only)",
|
|
3449
3506
|
readValue: "STUB \u2014 selector-based; NOT_SUPPORTED on native (no DOM). Use /control/element/:id/state to read a value.",
|
|
3450
3507
|
findByText: "App-agnostic: list elements matching free-text (no press): body { text, visibleOnly? }",
|
|
3451
|
-
getConsoleErrors: "
|
|
3452
|
-
getNetworkRequests: "
|
|
3508
|
+
getConsoleErrors: "Last-N console.error/console.warn entries; ?since=<ms>&limit=<n>. `installed:false` when capture is off (no testHooks) \u2014 entries empty but schema-valid.",
|
|
3509
|
+
getNetworkRequests: "Last-N fetch/XHR entries with status + duration; ?since=<ms>&limit=<n>. `installed:false` when capture is off (no testHooks) \u2014 entries empty but schema-valid.",
|
|
3453
3510
|
pushModal: "TEST HOOK \u2014 push a modal onto the snapshot.modalStack via the registry ModalDetector",
|
|
3454
3511
|
dismissModal: "TEST HOOK \u2014 dismiss a modal by id from the snapshot.modalStack via the registry ModalDetector",
|
|
3455
3512
|
keepAwake: "TEST HOOK \u2014 keep the device screen awake via the injected keepAwakeProvider: body { enabled: boolean, durationMs?: number }"
|
|
@@ -5620,7 +5677,8 @@ function useUIElement(options) {
|
|
|
5620
5677
|
parentPath,
|
|
5621
5678
|
style,
|
|
5622
5679
|
stateStyles: stateStylesProp,
|
|
5623
|
-
handlers: handlersProp
|
|
5680
|
+
handlers: handlersProp,
|
|
5681
|
+
value: valueProp
|
|
5624
5682
|
} = options;
|
|
5625
5683
|
const treePath = parentPath ? `${parentPath}/${id}` : id;
|
|
5626
5684
|
const bridgeProps = useMemo(
|
|
@@ -5646,9 +5704,12 @@ function useUIElement(options) {
|
|
|
5646
5704
|
});
|
|
5647
5705
|
registeredRef.current = true;
|
|
5648
5706
|
registeredIdRef.current = id;
|
|
5707
|
+
if (typeof valueProp === "string") {
|
|
5708
|
+
bridge.registry.updateElementState(id, { value: valueProp });
|
|
5709
|
+
}
|
|
5649
5710
|
setRegistered(true);
|
|
5650
5711
|
scheduleDeadButtonWarning(bridge, id, type);
|
|
5651
|
-
}, [bridge, id, type, label, actions, customActions, treePath, style, stateStylesProp]);
|
|
5712
|
+
}, [bridge, id, type, label, actions, customActions, treePath, style, stateStylesProp, valueProp]);
|
|
5652
5713
|
const unregister = useCallback(() => {
|
|
5653
5714
|
if (!bridge || !registeredRef.current) return;
|
|
5654
5715
|
bridge.registry.unregisterElement(registeredIdRef.current ?? id);
|
|
@@ -5778,6 +5839,13 @@ function useUIElement(options) {
|
|
|
5778
5839
|
}
|
|
5779
5840
|
};
|
|
5780
5841
|
}, [bridge, registered, id, handlersProp]);
|
|
5842
|
+
useEffect(() => {
|
|
5843
|
+
if (!bridge || !registered) return;
|
|
5844
|
+
if (typeof valueProp !== "string") return;
|
|
5845
|
+
const current = bridge.registry.getElement(id)?.getState().value;
|
|
5846
|
+
if (current === valueProp) return;
|
|
5847
|
+
bridge.registry.updateElementState(id, { value: valueProp });
|
|
5848
|
+
}, [bridge, registered, id, valueProp]);
|
|
5781
5849
|
useCallback(
|
|
5782
5850
|
(props) => {
|
|
5783
5851
|
propsRef.current = { ...propsRef.current, ...props };
|