@qontinui/ui-bridge-native 0.6.8 → 0.6.9

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/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":
@@ -1040,6 +1040,8 @@ var DefaultNativeActionExecutor = class {
1040
1040
  return this.performDoubleTap(props);
1041
1041
  case "type":
1042
1042
  return this.performType(element, props, params);
1043
+ case "setValue":
1044
+ return this.performSetValue(element, props, params);
1043
1045
  case "clear":
1044
1046
  return this.performClear(element, props);
1045
1047
  case "focus":
@@ -1132,6 +1134,38 @@ var DefaultNativeActionExecutor = class {
1132
1134
  this.registry.updateElementState(element.id, { value: params.text });
1133
1135
  }
1134
1136
  }
1137
+ /**
1138
+ * Perform setValue action.
1139
+ *
1140
+ * Web/runner UI Bridge parity: `setValue` replaces the input's value
1141
+ * atomically in a single call (no per-character keystroke simulation),
1142
+ * mirroring the web bridge's `setValue` semantics. On native there is no
1143
+ * DOM value to assign, so we drive the controlled-input handler
1144
+ * (`onChangeText`, falling back to `onChange`) with the full string. The
1145
+ * registry's `state.value` is then synced so a subsequent GET on the
1146
+ * element reflects the new value.
1147
+ *
1148
+ * Unlike `type`, this does not append to existing content and ignores
1149
+ * `delay` — it is the deterministic "set the field to exactly this" action.
1150
+ */
1151
+ async performSetValue(element, props, params) {
1152
+ const next = typeof params?.text === "string" ? params.text : typeof params?.value === "string" ? params.value : void 0;
1153
+ if (typeof next !== "string") {
1154
+ throw new Error('setValue action requires a string "text" (or "value") parameter');
1155
+ }
1156
+ if (typeof props.onChangeText === "function") {
1157
+ props.onChangeText(next);
1158
+ } else if (typeof props.onChange === "function") {
1159
+ props.onChange({
1160
+ nativeEvent: { text: next }
1161
+ });
1162
+ } else {
1163
+ throw new Error("No text change handler found on element");
1164
+ }
1165
+ if (element) {
1166
+ this.registry.updateElementState(element.id, { value: next });
1167
+ }
1168
+ }
1135
1169
  /**
1136
1170
  * Perform clear action
1137
1171
  */
@@ -2897,7 +2931,8 @@ function createServerHandlers(registry, executor, config) {
2897
2931
  return success2({
2898
2932
  entries: [],
2899
2933
  count: 0,
2900
- bufferSize: 0
2934
+ bufferSize: 0,
2935
+ installed: false
2901
2936
  });
2902
2937
  }
2903
2938
  const entries = buf.entries({
@@ -2907,7 +2942,8 @@ function createServerHandlers(registry, executor, config) {
2907
2942
  return success2({
2908
2943
  entries,
2909
2944
  count: entries.length,
2910
- bufferSize: buf.size()
2945
+ bufferSize: buf.size(),
2946
+ installed: buf.isInstalled()
2911
2947
  });
2912
2948
  },
2913
2949
  getNetworkRequests: async (ctx) => {
@@ -2918,7 +2954,8 @@ function createServerHandlers(registry, executor, config) {
2918
2954
  return success2({
2919
2955
  entries: [],
2920
2956
  count: 0,
2921
- bufferSize: 0
2957
+ bufferSize: 0,
2958
+ installed: false
2922
2959
  });
2923
2960
  }
2924
2961
  const entries = buf.entries({
@@ -2928,7 +2965,8 @@ function createServerHandlers(registry, executor, config) {
2928
2965
  return success2({
2929
2966
  entries,
2930
2967
  count: entries.length,
2931
- bufferSize: buf.size()
2968
+ bufferSize: buf.size(),
2969
+ installed: buf.isInstalled()
2932
2970
  });
2933
2971
  },
2934
2972
  // Test hooks — modal stack manipulation
@@ -3347,7 +3385,10 @@ var WS_ROUTES = [
3347
3385
  // same handler at both paths to keep agents portable across platforms.
3348
3386
  { pattern: "ai/find", handler: "find", httpMethods: ["POST"] },
3349
3387
  { pattern: "control/snapshot", handler: "getSnapshot", httpMethods: ["GET"] },
3350
- { pattern: "control/discover", handler: "getSnapshot" },
3388
+ // Discovery alias — web/runner parity with `POST /control/discover` (a
3389
+ // legacy alias for snapshot/find). Mounted on HTTP (GET+POST) so mobile
3390
+ // doesn't 404 where web returns 200; delegates to the snapshot handler.
3391
+ { pattern: "control/discover", handler: "getSnapshot", httpMethods: ["GET", "POST"] },
3351
3392
  // Page health — accepts both GET and POST so the canonical skill invocation
3352
3393
  // (`POST .../control/page-health` with an empty body) and ad-hoc curls both
3353
3394
  // work without a body-shape gotcha.
@@ -3441,19 +3482,26 @@ var WS_ROUTES = [
3441
3482
  httpMethods: ["POST"],
3442
3483
  requiresTestHooks: true
3443
3484
  },
3444
- // Observability — last-N console errors + network requests. testHooks-gated
3445
- // because patching `console.error` / `fetch` is invasive in production.
3485
+ // Observability — last-N console errors + network requests.
3486
+ //
3487
+ // Always mounted (not testHooks-gated) so they return a schema-valid 200
3488
+ // on every build instead of a 404 that agents can't distinguish from a
3489
+ // missing route. The INVASIVE part — monkey-patching `console.error` /
3490
+ // `fetch` / `XMLHttpRequest` — stays gated behind `testHooks` (see
3491
+ // NativeUIBridgeServer.start → buffer.install()). When the patches aren't
3492
+ // installed the handlers report `installed: false` + empty entries, so
3493
+ // production builds expose the contract without observing real users'
3494
+ // console/network traffic. Set `features.testHooks` (typically on
3495
+ // `__DEV__`) to populate the buffers with live data.
3446
3496
  {
3447
3497
  pattern: "control/console-errors",
3448
3498
  handler: "getConsoleErrors",
3449
- httpMethods: ["GET"],
3450
- requiresTestHooks: true
3499
+ httpMethods: ["GET"]
3451
3500
  },
3452
3501
  {
3453
3502
  pattern: "sdk/network-requests",
3454
3503
  handler: "getNetworkRequests",
3455
- httpMethods: ["GET"],
3456
- requiresTestHooks: true
3504
+ httpMethods: ["GET"]
3457
3505
  }
3458
3506
  ];
3459
3507
  var HANDLER_DESCRIPTIONS = {
@@ -3500,8 +3548,8 @@ var HANDLER_DESCRIPTIONS = {
3500
3548
  typeInto: "App-agnostic: type into the input matching a label: body { label, text, clear?, visibleOnly? }. (selector form is web-only)",
3501
3549
  readValue: "STUB \u2014 selector-based; NOT_SUPPORTED on native (no DOM). Use /control/element/:id/state to read a value.",
3502
3550
  findByText: "App-agnostic: list elements matching free-text (no press): body { text, visibleOnly? }",
3503
- getConsoleErrors: "TEST HOOK \u2014 last-N console.error/console.warn entries; ?since=<ms>&limit=<n>",
3504
- getNetworkRequests: "TEST HOOK \u2014 last-N fetch/XHR entries with status + duration; ?since=<ms>&limit=<n>",
3551
+ 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.",
3552
+ 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.",
3505
3553
  pushModal: "TEST HOOK \u2014 push a modal onto the snapshot.modalStack via the registry ModalDetector",
3506
3554
  dismissModal: "TEST HOOK \u2014 dismiss a modal by id from the snapshot.modalStack via the registry ModalDetector",
3507
3555
  keepAwake: "TEST HOOK \u2014 keep the device screen awake via the injected keepAwakeProvider: body { enabled: boolean, durationMs?: number }"
@@ -5672,7 +5720,8 @@ function useUIElement(options) {
5672
5720
  parentPath,
5673
5721
  style,
5674
5722
  stateStyles: stateStylesProp,
5675
- handlers: handlersProp
5723
+ handlers: handlersProp,
5724
+ value: valueProp
5676
5725
  } = options;
5677
5726
  const treePath = parentPath ? `${parentPath}/${id}` : id;
5678
5727
  const bridgeProps = useMemo(
@@ -5698,9 +5747,12 @@ function useUIElement(options) {
5698
5747
  });
5699
5748
  registeredRef.current = true;
5700
5749
  registeredIdRef.current = id;
5750
+ if (typeof valueProp === "string") {
5751
+ bridge.registry.updateElementState(id, { value: valueProp });
5752
+ }
5701
5753
  setRegistered(true);
5702
5754
  scheduleDeadButtonWarning(bridge, id, type);
5703
- }, [bridge, id, type, label, actions, customActions, treePath, style, stateStylesProp]);
5755
+ }, [bridge, id, type, label, actions, customActions, treePath, style, stateStylesProp, valueProp]);
5704
5756
  const unregister = useCallback(() => {
5705
5757
  if (!bridge || !registeredRef.current) return;
5706
5758
  bridge.registry.unregisterElement(registeredIdRef.current ?? id);
@@ -5830,6 +5882,13 @@ function useUIElement(options) {
5830
5882
  }
5831
5883
  };
5832
5884
  }, [bridge, registered, id, handlersProp]);
5885
+ useEffect(() => {
5886
+ if (!bridge || !registered) return;
5887
+ if (typeof valueProp !== "string") return;
5888
+ const current = bridge.registry.getElement(id)?.getState().value;
5889
+ if (current === valueProp) return;
5890
+ bridge.registry.updateElementState(id, { value: valueProp });
5891
+ }, [bridge, registered, id, valueProp]);
5833
5892
  useCallback(
5834
5893
  (props) => {
5835
5894
  propsRef.current = { ...propsRef.current, ...props };
@@ -6221,6 +6280,11 @@ var UI_BRIDGE_NATIVE_ROUTES = {
6221
6280
  path: "/ui-bridge/control/snapshot",
6222
6281
  description: "Get full bridge snapshot"
6223
6282
  },
6283
+ DISCOVER: {
6284
+ method: "GET",
6285
+ path: "/ui-bridge/control/discover",
6286
+ description: "Discover elements (alias for snapshot; web/runner parity)"
6287
+ },
6224
6288
  PAGE_HEALTH: {
6225
6289
  method: "POST",
6226
6290
  path: "/ui-bridge/control/page-health",
@@ -6348,6 +6412,18 @@ var UI_BRIDGE_NATIVE_ROUTES = {
6348
6412
  path: "/ui-bridge/control/modal/dismiss/:id",
6349
6413
  description: "Dismiss a modal by id (testHooks)"
6350
6414
  },
6415
+ // Observability — last-N ring buffers (always mounted; `installed:false`
6416
+ // when the console/network patches are off, i.e. no testHooks).
6417
+ CONSOLE_ERRORS: {
6418
+ method: "GET",
6419
+ path: "/ui-bridge/control/console-errors",
6420
+ description: "Last-N captured console.error/console.warn entries"
6421
+ },
6422
+ NETWORK_REQUESTS: {
6423
+ method: "GET",
6424
+ path: "/ui-bridge/sdk/network-requests",
6425
+ description: "Last-N captured fetch/XHR requests with status + duration"
6426
+ },
6351
6427
  // Health
6352
6428
  HEALTH: {
6353
6429
  method: "GET",