@qontinui/ui-bridge-native 0.4.0 → 0.6.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/core/index.js +38 -4
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +38 -4
- package/dist/core/index.mjs.map +1 -1
- package/dist/core/registry.d.ts +30 -1
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/types.d.ts +18 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/debug/index.js.map +1 -1
- package/dist/debug/index.mjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +232 -30
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +232 -30
- package/dist/index.mjs.map +1 -1
- package/dist/react/UIBridgeNativeProvider.d.ts +9 -2
- package/dist/react/UIBridgeNativeProvider.d.ts.map +1 -1
- package/dist/react/index.js +207 -30
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +207 -30
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/useUIElement.d.ts +73 -35
- package/dist/react/useUIElement.d.ts.map +1 -1
- package/dist/server/handlers.d.ts.map +1 -1
- package/dist/server/http-server.d.ts +8 -1
- package/dist/server/http-server.d.ts.map +1 -1
- package/dist/server/index.js +135 -4
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +135 -4
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/types.d.ts +13 -0
- package/dist/server/types.d.ts.map +1 -1
- package/package.json +3 -2
package/dist/server/index.mjs
CHANGED
|
@@ -139,6 +139,31 @@ var UI_BRIDGE_NATIVE_ROUTES = {
|
|
|
139
139
|
path: "/ui-bridge/ai/fill-form",
|
|
140
140
|
description: "Fill multiple input elements in a single call"
|
|
141
141
|
},
|
|
142
|
+
// AI helpers (NOT_SUPPORTED stubs) — these endpoints exist on the runner-side
|
|
143
|
+
// UI Bridge surface but have no analog on mobile React Native. The mobile
|
|
144
|
+
// bridge registers explicit stubs so callers get a structured NOT_SUPPORTED
|
|
145
|
+
// envelope instead of a confusing HTTP 404. The mobile bridge's snapshot +
|
|
146
|
+
// `/control/find` already cover what an operator would reach for these for.
|
|
147
|
+
AI_FORMS: {
|
|
148
|
+
method: "GET",
|
|
149
|
+
path: "/ui-bridge/ai/forms",
|
|
150
|
+
description: "Discover forms (runner-only; mobile returns NOT_SUPPORTED)"
|
|
151
|
+
},
|
|
152
|
+
AI_IDLE_STATUS: {
|
|
153
|
+
method: "GET",
|
|
154
|
+
path: "/ui-bridge/ai/idle-status",
|
|
155
|
+
description: "Get page-idle signal (runner-only; mobile returns NOT_SUPPORTED)"
|
|
156
|
+
},
|
|
157
|
+
AI_CHANGE_BUFFER_ENABLE: {
|
|
158
|
+
method: "POST",
|
|
159
|
+
path: "/ui-bridge/ai/change-buffer/enable",
|
|
160
|
+
description: "Enable DOM change buffer (runner-only; mobile returns NOT_SUPPORTED)"
|
|
161
|
+
},
|
|
162
|
+
AI_WAIT_FOR_ELEMENT: {
|
|
163
|
+
method: "POST",
|
|
164
|
+
path: "/ui-bridge/ai/wait-for-element",
|
|
165
|
+
description: "Wait for element predicate via HTTP (runner-only; mobile returns NOT_SUPPORTED \u2014 use WS waitForElement instead)"
|
|
166
|
+
},
|
|
142
167
|
// Test hooks — drive ModalDetector state from outside the React tree
|
|
143
168
|
// (gated by `features.testHooks`). Mirrors `pushModal` / `dismissModal`
|
|
144
169
|
// calls that components normally make in-process.
|
|
@@ -845,22 +870,25 @@ function createServerHandlers(registry, executor, config) {
|
|
|
845
870
|
getElements: async (ctx) => {
|
|
846
871
|
const visibleOnly = ctx.query?.visibleOnly === "true" || ctx.body?.visibleOnly === true;
|
|
847
872
|
const forRoute = ctx.query?.route || ctx.body?.route;
|
|
848
|
-
let allElements = visibleOnly ? registry.
|
|
873
|
+
let allElements = visibleOnly ? registry.getMountedVisibleElements() : registry.getAllElements();
|
|
849
874
|
if (forRoute) {
|
|
850
875
|
allElements = allElements.filter((e) => e.registrationRoute === forRoute);
|
|
851
876
|
}
|
|
852
877
|
const elements = allElements.map((e) => {
|
|
853
878
|
const handlers = extractHandlerNames(e.props);
|
|
879
|
+
const state = e.getState();
|
|
880
|
+
const visibility = !state.visible ? "hidden" : state.layout !== null ? "visible" : "likely-visible";
|
|
854
881
|
return {
|
|
855
882
|
id: e.id,
|
|
856
883
|
type: e.type,
|
|
857
884
|
label: e.label,
|
|
858
885
|
identifier: e.getIdentifier(),
|
|
859
|
-
state
|
|
886
|
+
state,
|
|
860
887
|
actions: e.actions,
|
|
861
888
|
customActions: e.customActions ? Object.keys(e.customActions) : void 0,
|
|
862
889
|
registeredHandlers: handlers.length > 0 ? handlers : void 0,
|
|
863
|
-
registrationRoute: e.registrationRoute
|
|
890
|
+
registrationRoute: e.registrationRoute,
|
|
891
|
+
visibility
|
|
864
892
|
};
|
|
865
893
|
});
|
|
866
894
|
return success2({ elements });
|
|
@@ -1115,6 +1143,45 @@ function createServerHandlers(registry, executor, config) {
|
|
|
1115
1143
|
"NOT_SUPPORTED"
|
|
1116
1144
|
);
|
|
1117
1145
|
},
|
|
1146
|
+
// Keep-awake (stub — apps should override via setKeepAwakeProvider, which
|
|
1147
|
+
// the provider wires when a keepAwakeProvider prop is supplied)
|
|
1148
|
+
keepAwake: async () => {
|
|
1149
|
+
return error2(
|
|
1150
|
+
"keep-awake not configured. Provide a keepAwakeProvider to UIBridgeNativeProvider.",
|
|
1151
|
+
"NOT_SUPPORTED"
|
|
1152
|
+
);
|
|
1153
|
+
},
|
|
1154
|
+
// AI helpers — runner-only endpoints that have no React Native analog.
|
|
1155
|
+
//
|
|
1156
|
+
// The mobile bridge mounts explicit NOT_SUPPORTED stubs at the runner's
|
|
1157
|
+
// cheatsheet paths so callers (and the /manual-test skill in particular)
|
|
1158
|
+
// get a structured envelope explaining the gap rather than a confusing
|
|
1159
|
+
// HTTP 404. Each error message names an in-tree mobile replacement so
|
|
1160
|
+
// operators aren't left guessing what to use instead.
|
|
1161
|
+
aiForms: async () => {
|
|
1162
|
+
return error2(
|
|
1163
|
+
'ai/forms (form discovery) is runner-only; mobile React Native has no DOM to walk. Use GET /ui-bridge/control/snapshot and filter for type === "input" instead.',
|
|
1164
|
+
"NOT_SUPPORTED"
|
|
1165
|
+
);
|
|
1166
|
+
},
|
|
1167
|
+
aiIdleStatus: async () => {
|
|
1168
|
+
return error2(
|
|
1169
|
+
'ai/idle-status (page-idle signal) is runner-only; mobile React Native has no DOM-mutation / network-idle signals. Use the registry event stream (WS subscribe to "element:registered"/"element:stateChanged") or poll /control/snapshot for stability.',
|
|
1170
|
+
"NOT_SUPPORTED"
|
|
1171
|
+
);
|
|
1172
|
+
},
|
|
1173
|
+
aiChangeBufferEnable: async () => {
|
|
1174
|
+
return error2(
|
|
1175
|
+
'ai/change-buffer (DOM mutation diff buffer) is runner-only; mobile React Native has no DOM. Use the WS event bridge (subscribe to "element:registered"/"element:unregistered"/"element:stateChanged") for change notifications.',
|
|
1176
|
+
"NOT_SUPPORTED"
|
|
1177
|
+
);
|
|
1178
|
+
},
|
|
1179
|
+
aiWaitForElement: async () => {
|
|
1180
|
+
return error2(
|
|
1181
|
+
'POST /ai/wait-for-element is runner-only on mobile. Use the WS "waitForElement" JSON-RPC method or "waitForCondition" instead \u2014 same semantics, no HTTP polling overhead.',
|
|
1182
|
+
"NOT_SUPPORTED"
|
|
1183
|
+
);
|
|
1184
|
+
},
|
|
1118
1185
|
// Meta / Introspection (stub — server constructor overrides with real route table)
|
|
1119
1186
|
getMethods: async () => {
|
|
1120
1187
|
return error2("Methods introspection not configured", "NOT_SUPPORTED");
|
|
@@ -1770,6 +1837,24 @@ var WS_ROUTES = [
|
|
|
1770
1837
|
{ pattern: "meta/methods", handler: "getMethods", httpMethods: ["GET"] },
|
|
1771
1838
|
// AI helpers — mobile parity with the runner's `/ui-bridge/ai/*` family.
|
|
1772
1839
|
{ pattern: "ai/fill-form", handler: "fillForm", httpMethods: ["POST"] },
|
|
1840
|
+
// AI helpers — NOT_SUPPORTED stubs. These endpoints exist on the runner-side
|
|
1841
|
+
// UI Bridge surface (and the /manual-test skill's cheatsheet documents them
|
|
1842
|
+
// as if they're universally available). Mobile React Native has no DOM, so
|
|
1843
|
+
// the mobile bridge mounts explicit stubs so callers get a structured
|
|
1844
|
+
// NOT_SUPPORTED envelope (with a hint pointing at the mobile-native
|
|
1845
|
+
// replacement) instead of a confusing HTTP 404.
|
|
1846
|
+
{ pattern: "ai/forms", handler: "aiForms", httpMethods: ["GET"] },
|
|
1847
|
+
{ pattern: "ai/idle-status", handler: "aiIdleStatus", httpMethods: ["GET"] },
|
|
1848
|
+
{
|
|
1849
|
+
pattern: "ai/change-buffer/enable",
|
|
1850
|
+
handler: "aiChangeBufferEnable",
|
|
1851
|
+
httpMethods: ["POST"]
|
|
1852
|
+
},
|
|
1853
|
+
{
|
|
1854
|
+
pattern: "ai/wait-for-element",
|
|
1855
|
+
handler: "aiWaitForElement",
|
|
1856
|
+
httpMethods: ["POST"]
|
|
1857
|
+
},
|
|
1773
1858
|
// Coord-based tap — synthesizes a press at the given screen coords by
|
|
1774
1859
|
// matching against registered elements' layout rects.
|
|
1775
1860
|
{ pattern: "control/tap", handler: "tapAt", httpMethods: ["POST"] },
|
|
@@ -1788,6 +1873,12 @@ var WS_ROUTES = [
|
|
|
1788
1873
|
httpMethods: ["POST"],
|
|
1789
1874
|
requiresTestHooks: true
|
|
1790
1875
|
},
|
|
1876
|
+
{
|
|
1877
|
+
pattern: "control/keep-awake",
|
|
1878
|
+
handler: "keepAwake",
|
|
1879
|
+
httpMethods: ["POST"],
|
|
1880
|
+
requiresTestHooks: true
|
|
1881
|
+
},
|
|
1791
1882
|
// Observability — last-N console errors + network requests. testHooks-gated
|
|
1792
1883
|
// because patching `console.error` / `fetch` is invasive in production.
|
|
1793
1884
|
{
|
|
@@ -1836,11 +1927,16 @@ var HANDLER_DESCRIPTIONS = {
|
|
|
1836
1927
|
diffBaseline: "Diff current UI against a saved baseline",
|
|
1837
1928
|
getMethods: "List every handler name (legacy introspection)",
|
|
1838
1929
|
fillForm: "Fill multiple inputs in one call: body { fields: [{elementId, value}] } \u2014 each field dispatches `type` + `clear:true`",
|
|
1930
|
+
aiForms: 'STUB \u2014 runner-only; mobile returns NOT_SUPPORTED. Use /control/snapshot + filter type==="input" instead.',
|
|
1931
|
+
aiIdleStatus: "STUB \u2014 runner-only; mobile returns NOT_SUPPORTED. Subscribe to registry events over WS for change signals.",
|
|
1932
|
+
aiChangeBufferEnable: "STUB \u2014 runner-only; mobile returns NOT_SUPPORTED. Use WS event subscriptions for change notifications.",
|
|
1933
|
+
aiWaitForElement: "STUB \u2014 runner-only; mobile returns NOT_SUPPORTED. Use the WS waitForElement / waitForCondition JSON-RPC methods.",
|
|
1839
1934
|
tapAt: "Synthesize a press at given screen coords by matching registered element layout rects: body { x, y, action? }",
|
|
1840
1935
|
getConsoleErrors: "TEST HOOK \u2014 last-N console.error/console.warn entries; ?since=<ms>&limit=<n>",
|
|
1841
1936
|
getNetworkRequests: "TEST HOOK \u2014 last-N fetch/XHR entries with status + duration; ?since=<ms>&limit=<n>",
|
|
1842
1937
|
pushModal: "TEST HOOK \u2014 push a modal onto the snapshot.modalStack via the registry ModalDetector",
|
|
1843
|
-
dismissModal: "TEST HOOK \u2014 dismiss a modal by id from the snapshot.modalStack via the registry ModalDetector"
|
|
1938
|
+
dismissModal: "TEST HOOK \u2014 dismiss a modal by id from the snapshot.modalStack via the registry ModalDetector",
|
|
1939
|
+
keepAwake: "TEST HOOK \u2014 keep the device screen awake via the injected keepAwakeProvider: body { enabled: boolean, durationMs?: number }"
|
|
1844
1940
|
};
|
|
1845
1941
|
var ACTION_REFERENCE = [
|
|
1846
1942
|
{ name: "press", aliases: ["click"], params: "PressActionParams (optional position)" },
|
|
@@ -2108,6 +2204,35 @@ var NativeUIBridgeServer = class {
|
|
|
2108
2204
|
}
|
|
2109
2205
|
};
|
|
2110
2206
|
}
|
|
2207
|
+
/**
|
|
2208
|
+
* Set a keep-awake provider for native screen-wake control.
|
|
2209
|
+
* This enables `control/keep-awake` and arms the auto-arm hook in
|
|
2210
|
+
* handleRequest (every request keeps the screen alive for 60s).
|
|
2211
|
+
*/
|
|
2212
|
+
setKeepAwakeProvider(provider) {
|
|
2213
|
+
this.keepAwakeProvider = provider;
|
|
2214
|
+
this.handlers.keepAwake = async (ctx) => {
|
|
2215
|
+
const { enabled, durationMs } = ctx.body ?? {};
|
|
2216
|
+
if (typeof enabled !== "boolean") {
|
|
2217
|
+
return {
|
|
2218
|
+
success: false,
|
|
2219
|
+
error: "enabled must be boolean",
|
|
2220
|
+
code: "INVALID_REQUEST",
|
|
2221
|
+
timestamp: Date.now()
|
|
2222
|
+
};
|
|
2223
|
+
}
|
|
2224
|
+
if (enabled) {
|
|
2225
|
+
provider.request("ui-bridge", typeof durationMs === "number" ? durationMs : void 0);
|
|
2226
|
+
} else {
|
|
2227
|
+
provider.release("ui-bridge");
|
|
2228
|
+
}
|
|
2229
|
+
return {
|
|
2230
|
+
success: true,
|
|
2231
|
+
data: { enabled, durationMs: typeof durationMs === "number" ? durationMs : null },
|
|
2232
|
+
timestamp: Date.now()
|
|
2233
|
+
};
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2111
2236
|
/**
|
|
2112
2237
|
* Start the HTTP server
|
|
2113
2238
|
*/
|
|
@@ -2700,6 +2825,12 @@ var NativeUIBridgeServer = class {
|
|
|
2700
2825
|
if (request.method === "OPTIONS") {
|
|
2701
2826
|
return { status: 204, headers, body: "" };
|
|
2702
2827
|
}
|
|
2828
|
+
if (this.config.testHooks === true && this.keepAwakeProvider) {
|
|
2829
|
+
try {
|
|
2830
|
+
this.keepAwakeProvider.request("auto-arm", 6e4);
|
|
2831
|
+
} catch {
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2703
2834
|
try {
|
|
2704
2835
|
const response = await this.routeRequest(request);
|
|
2705
2836
|
return {
|