@qontinui/ui-bridge-native 0.3.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.
Files changed (39) hide show
  1. package/dist/{StateAnnotation-WmK1YjwP.d.ts → StateAnnotation--zm0Jfqj.d.ts} +4 -4
  2. package/dist/{StateAnnotation-D8reaRcS.d.mts → StateAnnotation-BgFoVWFy.d.mts} +4 -4
  3. package/dist/control/index.d.mts +4 -4
  4. package/dist/control/index.d.ts +4 -4
  5. package/dist/core/index.d.mts +3 -3
  6. package/dist/core/index.d.ts +3 -3
  7. package/dist/core/index.js +89 -6
  8. package/dist/core/index.js.map +1 -1
  9. package/dist/core/index.mjs +89 -6
  10. package/dist/core/index.mjs.map +1 -1
  11. package/dist/design/index.d.mts +1 -1
  12. package/dist/design/index.d.ts +1 -1
  13. package/dist/{http-server-D2U1k48d.d.mts → http-server--550eD1F.d.mts} +106 -4
  14. package/dist/{http-server-ck8clUsZ.d.ts → http-server-C9NaNBEP.d.ts} +106 -4
  15. package/dist/index.d.mts +5 -5
  16. package/dist/index.d.ts +5 -5
  17. package/dist/index.js +576 -49
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.mjs +576 -49
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/react/index.d.mts +5 -5
  22. package/dist/react/index.d.ts +5 -5
  23. package/dist/react/index.js +576 -49
  24. package/dist/react/index.js.map +1 -1
  25. package/dist/react/index.mjs +576 -49
  26. package/dist/react/index.mjs.map +1 -1
  27. package/dist/{registry-KdUW_koV.d.mts → registry-B5JadBN9.d.mts} +49 -2
  28. package/dist/{registry-BxXaxmsT.d.ts → registry-pAUpQq2O.d.ts} +49 -2
  29. package/dist/server/index.d.mts +65 -6
  30. package/dist/server/index.d.ts +65 -6
  31. package/dist/server/index.js +438 -2
  32. package/dist/server/index.js.map +1 -1
  33. package/dist/server/index.mjs +437 -3
  34. package/dist/server/index.mjs.map +1 -1
  35. package/dist/{types-DjB0zJJH.d.mts → types-B0XA4vOF.d.mts} +1 -1
  36. package/dist/{types-BwdPNpII.d.mts → types-CGVut7mc.d.mts} +43 -1
  37. package/dist/{types-BwdPNpII.d.ts → types-CGVut7mc.d.ts} +43 -1
  38. package/dist/{types-DCEv5qb6.d.ts → types-CjEKTDYT.d.ts} +1 -1
  39. package/package.json +1 -1
@@ -1023,7 +1023,8 @@ function createServerHandlers(registry, executor, config) {
1023
1023
  },
1024
1024
  getSnapshot: async (ctx) => {
1025
1025
  const visibleOnly = ctx.query?.visibleOnly === "true" || ctx.body?.visibleOnly === true;
1026
- const snapshot = registry.createSnapshot(void 0, { visibleOnly });
1026
+ const currentRouteOnly = ctx.query?.currentRouteOnly === "true" || ctx.body?.currentRouteOnly === true;
1027
+ const snapshot = registry.createSnapshot(void 0, { visibleOnly, currentRouteOnly });
1027
1028
  return success2(snapshot);
1028
1029
  },
1029
1030
  // Workflows
@@ -1190,6 +1191,134 @@ function createServerHandlers(registry, executor, config) {
1190
1191
  const failedCount = results.length - succeededCount;
1191
1192
  return success2({ results, succeededCount, failedCount });
1192
1193
  },
1194
+ // Coord-based tap
1195
+ //
1196
+ // Synthesizes a press at the given screen coordinates by scanning the
1197
+ // registered elements' layout rects for a containing match. We prefer the
1198
+ // *topmost* match (smallest area = innermost child) since native UIs
1199
+ // generally render parents before children, and the user's tap should
1200
+ // logically hit the leaf-most interactive element.
1201
+ //
1202
+ // Coords are interpreted in the absolute (screen) space published by
1203
+ // `measureInWindow`; we compare against `pageX/pageY` when available and
1204
+ // fall back to the layout's relative `x/y` so this works in test fixtures
1205
+ // that don't measure (the fallback only matches single-rooted screens but
1206
+ // is good enough for the unit suite).
1207
+ tapAt: async (ctx) => {
1208
+ const body = ctx.body;
1209
+ const x = body?.x;
1210
+ const y = body?.y;
1211
+ if (typeof x !== "number" || !Number.isFinite(x)) {
1212
+ return error2('"x" must be a finite number', "INVALID_REQUEST");
1213
+ }
1214
+ if (typeof y !== "number" || !Number.isFinite(y)) {
1215
+ return error2('"y" must be a finite number', "INVALID_REQUEST");
1216
+ }
1217
+ const action = body?.action ?? "press";
1218
+ const allowedActions = [
1219
+ "press",
1220
+ "longPress",
1221
+ "doubleTap"
1222
+ ];
1223
+ if (!allowedActions.includes(action)) {
1224
+ return error2(
1225
+ `"action" must be one of: ${allowedActions.join(", ")}`,
1226
+ "INVALID_REQUEST"
1227
+ );
1228
+ }
1229
+ const candidates = [];
1230
+ for (const el of registry.getAllElements()) {
1231
+ const state = el.getState();
1232
+ const layout = state?.layout;
1233
+ if (!layout) continue;
1234
+ if (typeof layout.width !== "number" || typeof layout.height !== "number" || layout.width <= 0 || layout.height <= 0) {
1235
+ continue;
1236
+ }
1237
+ const hasPage = typeof layout.pageX === "number" && typeof layout.pageY === "number" && Number.isFinite(layout.pageX) && Number.isFinite(layout.pageY);
1238
+ const left = hasPage ? layout.pageX : layout.x;
1239
+ const top = hasPage ? layout.pageY : layout.y;
1240
+ if (x >= left && x <= left + layout.width && y >= top && y <= top + layout.height) {
1241
+ candidates.push({
1242
+ elementId: el.id,
1243
+ layout,
1244
+ area: layout.width * layout.height
1245
+ });
1246
+ }
1247
+ }
1248
+ if (candidates.length === 0) {
1249
+ return error2(
1250
+ `No element registered at (${x}, ${y})`,
1251
+ "no_element_at_coords"
1252
+ );
1253
+ }
1254
+ candidates.sort((a, b) => a.area - b.area);
1255
+ const target = candidates[0];
1256
+ const response = await executor.executeAction(target.elementId, { action });
1257
+ if (!response.success) {
1258
+ return error2(response.error || "Action failed", "ACTION_FAILED");
1259
+ }
1260
+ return success2({
1261
+ elementId: target.elementId,
1262
+ action,
1263
+ layout: {
1264
+ x: target.layout.x,
1265
+ y: target.layout.y,
1266
+ width: target.layout.width,
1267
+ height: target.layout.height,
1268
+ pageX: target.layout.pageX,
1269
+ pageY: target.layout.pageY
1270
+ }
1271
+ });
1272
+ },
1273
+ // Observability — console error / network request ring buffers.
1274
+ //
1275
+ // The buffers are owned by NativeUIBridgeServer and patched onto the
1276
+ // globals on `start()` (gated behind `features.testHooks`). When no
1277
+ // buffer is provided (no testHooks, or a stripped-down server fixture),
1278
+ // these handlers gracefully return empty arrays so callers can probe
1279
+ // without 500ing.
1280
+ getConsoleErrors: async (ctx) => {
1281
+ const buf = config?.consoleErrorBuffer;
1282
+ const since = ctx.query?.since !== void 0 ? Number(ctx.query.since) : void 0;
1283
+ const limit = ctx.query?.limit !== void 0 ? Number(ctx.query.limit) : void 0;
1284
+ if (!buf) {
1285
+ return success2({
1286
+ entries: [],
1287
+ count: 0,
1288
+ bufferSize: 0
1289
+ });
1290
+ }
1291
+ const entries = buf.entries({
1292
+ since: typeof since === "number" && Number.isFinite(since) ? since : void 0,
1293
+ limit: typeof limit === "number" && Number.isFinite(limit) ? limit : void 0
1294
+ });
1295
+ return success2({
1296
+ entries,
1297
+ count: entries.length,
1298
+ bufferSize: buf.size()
1299
+ });
1300
+ },
1301
+ getNetworkRequests: async (ctx) => {
1302
+ const buf = config?.networkRequestBuffer;
1303
+ const since = ctx.query?.since !== void 0 ? Number(ctx.query.since) : void 0;
1304
+ const limit = ctx.query?.limit !== void 0 ? Number(ctx.query.limit) : void 0;
1305
+ if (!buf) {
1306
+ return success2({
1307
+ entries: [],
1308
+ count: 0,
1309
+ bufferSize: 0
1310
+ });
1311
+ }
1312
+ const entries = buf.entries({
1313
+ since: typeof since === "number" && Number.isFinite(since) ? since : void 0,
1314
+ limit: typeof limit === "number" && Number.isFinite(limit) ? limit : void 0
1315
+ });
1316
+ return success2({
1317
+ entries,
1318
+ count: entries.length,
1319
+ bufferSize: buf.size()
1320
+ });
1321
+ },
1193
1322
  // Test hooks — modal stack manipulation
1194
1323
  //
1195
1324
  // These two endpoints exist so external automation runners can drive
@@ -1291,6 +1420,266 @@ function createServerHandlers(registry, executor, config) {
1291
1420
  };
1292
1421
  }
1293
1422
 
1423
+ // src/server/observability.ts
1424
+ var RingBuffer = class {
1425
+ constructor(capacity) {
1426
+ this.capacity = capacity;
1427
+ this.items = [];
1428
+ }
1429
+ push(entry) {
1430
+ this.items.push(entry);
1431
+ if (this.items.length > this.capacity) {
1432
+ this.items.splice(0, this.items.length - this.capacity);
1433
+ }
1434
+ }
1435
+ /**
1436
+ * Return entries optionally filtered by `since` (timestamp ms) and capped
1437
+ * at `limit`. Newest-end is preserved when `limit` would otherwise truncate.
1438
+ */
1439
+ entries(opts = {}) {
1440
+ const { since, limit } = opts;
1441
+ let filtered = this.items;
1442
+ if (typeof since === "number") {
1443
+ filtered = filtered.filter((e) => e.timestamp >= since);
1444
+ }
1445
+ if (typeof limit === "number" && limit >= 0 && filtered.length > limit) {
1446
+ filtered = filtered.slice(filtered.length - limit);
1447
+ }
1448
+ return filtered.slice();
1449
+ }
1450
+ size() {
1451
+ return this.items.length;
1452
+ }
1453
+ clear() {
1454
+ this.items.length = 0;
1455
+ }
1456
+ };
1457
+ function formatConsoleArgs(args) {
1458
+ return args.map((a) => {
1459
+ if (a instanceof Error) return a.message;
1460
+ if (typeof a === "string") return a;
1461
+ try {
1462
+ return JSON.stringify(a);
1463
+ } catch {
1464
+ return String(a);
1465
+ }
1466
+ }).join(" ");
1467
+ }
1468
+ function extractStack(args) {
1469
+ for (const a of args) {
1470
+ if (a instanceof Error && a.stack) return a.stack;
1471
+ }
1472
+ return void 0;
1473
+ }
1474
+ var ConsoleErrorBuffer = class {
1475
+ constructor(capacity = 100) {
1476
+ this.installed = false;
1477
+ this.buffer = new RingBuffer(capacity);
1478
+ }
1479
+ install() {
1480
+ if (this.installed) return;
1481
+ if (typeof console === "undefined") return;
1482
+ this.originalError = console.error;
1483
+ this.originalWarn = console.warn;
1484
+ const buffer = this.buffer;
1485
+ const origError = this.originalError;
1486
+ const origWarn = this.originalWarn;
1487
+ console.error = (...args) => {
1488
+ try {
1489
+ buffer.push({
1490
+ timestamp: Date.now(),
1491
+ level: "error",
1492
+ message: formatConsoleArgs(args),
1493
+ stack: extractStack(args)
1494
+ });
1495
+ } catch {
1496
+ }
1497
+ origError.apply(console, args);
1498
+ };
1499
+ console.warn = (...args) => {
1500
+ try {
1501
+ buffer.push({
1502
+ timestamp: Date.now(),
1503
+ level: "warn",
1504
+ message: formatConsoleArgs(args),
1505
+ stack: extractStack(args)
1506
+ });
1507
+ } catch {
1508
+ }
1509
+ origWarn.apply(console, args);
1510
+ };
1511
+ this.installed = true;
1512
+ }
1513
+ uninstall() {
1514
+ if (!this.installed) return;
1515
+ if (typeof console === "undefined") return;
1516
+ if (this.originalError) console.error = this.originalError;
1517
+ if (this.originalWarn) console.warn = this.originalWarn;
1518
+ this.originalError = void 0;
1519
+ this.originalWarn = void 0;
1520
+ this.installed = false;
1521
+ }
1522
+ entries(opts = {}) {
1523
+ return this.buffer.entries(opts);
1524
+ }
1525
+ size() {
1526
+ return this.buffer.size();
1527
+ }
1528
+ clear() {
1529
+ this.buffer.clear();
1530
+ }
1531
+ isInstalled() {
1532
+ return this.installed;
1533
+ }
1534
+ };
1535
+ function extractUrl(input) {
1536
+ if (typeof input === "string") return input;
1537
+ if (input && typeof input === "object") {
1538
+ const obj = input;
1539
+ if (typeof obj.url === "string") return obj.url;
1540
+ try {
1541
+ return String(input);
1542
+ } catch {
1543
+ return "";
1544
+ }
1545
+ }
1546
+ return "";
1547
+ }
1548
+ function extractMethod(input, init) {
1549
+ if (init && typeof init.method === "string") return init.method.toUpperCase();
1550
+ if (input && typeof input === "object") {
1551
+ const obj = input;
1552
+ if (typeof obj.method === "string") return obj.method.toUpperCase();
1553
+ }
1554
+ return "GET";
1555
+ }
1556
+ var NetworkRequestBuffer = class {
1557
+ constructor(capacity = 100) {
1558
+ this.installed = false;
1559
+ this.buffer = new RingBuffer(capacity);
1560
+ }
1561
+ install() {
1562
+ if (this.installed) return;
1563
+ const g = globalThis;
1564
+ if (typeof g.fetch === "function") {
1565
+ this.originalFetch = g.fetch;
1566
+ const buffer = this.buffer;
1567
+ const original = this.originalFetch;
1568
+ const patched = async (input, init) => {
1569
+ const start = Date.now();
1570
+ const url = extractUrl(input);
1571
+ const method = extractMethod(input, init);
1572
+ try {
1573
+ const res = await original.call(globalThis, input, init);
1574
+ buffer.push({
1575
+ timestamp: start,
1576
+ method,
1577
+ url,
1578
+ status: res.status,
1579
+ durationMs: Date.now() - start,
1580
+ ok: res.ok
1581
+ });
1582
+ return res;
1583
+ } catch (err) {
1584
+ buffer.push({
1585
+ timestamp: start,
1586
+ method,
1587
+ url,
1588
+ status: 0,
1589
+ durationMs: Date.now() - start,
1590
+ ok: false,
1591
+ error: err instanceof Error ? err.message : String(err)
1592
+ });
1593
+ throw err;
1594
+ }
1595
+ };
1596
+ g.fetch = patched;
1597
+ }
1598
+ if (typeof g.XMLHttpRequest === "function") {
1599
+ const Xhr = g.XMLHttpRequest;
1600
+ this.originalXhrOpen = Xhr.prototype.open;
1601
+ this.originalXhrSend = Xhr.prototype.send;
1602
+ const buffer = this.buffer;
1603
+ const origOpen = this.originalXhrOpen;
1604
+ Xhr.prototype.open = function(method, url, ...rest) {
1605
+ this.__uiBridgeMethod = method.toUpperCase();
1606
+ this.__uiBridgeUrl = typeof url === "string" ? url : String(url);
1607
+ return origOpen.call(
1608
+ this,
1609
+ method,
1610
+ url,
1611
+ ...rest
1612
+ );
1613
+ };
1614
+ const origSend = this.originalXhrSend;
1615
+ Xhr.prototype.send = function(body) {
1616
+ const start = Date.now();
1617
+ const method = this.__uiBridgeMethod ?? "GET";
1618
+ const url = this.__uiBridgeUrl ?? "";
1619
+ const finalize = (status, ok, error3) => {
1620
+ buffer.push({
1621
+ timestamp: start,
1622
+ method,
1623
+ url,
1624
+ status,
1625
+ durationMs: Date.now() - start,
1626
+ ok,
1627
+ error: error3
1628
+ });
1629
+ };
1630
+ const onLoadEnd = () => {
1631
+ this.removeEventListener("loadend", onLoadEnd);
1632
+ this.removeEventListener("error", onError);
1633
+ finalize(this.status, this.status >= 200 && this.status < 300);
1634
+ };
1635
+ const onError = () => {
1636
+ this.removeEventListener("loadend", onLoadEnd);
1637
+ this.removeEventListener("error", onError);
1638
+ finalize(0, false, "XMLHttpRequest error");
1639
+ };
1640
+ this.addEventListener("loadend", onLoadEnd);
1641
+ this.addEventListener("error", onError);
1642
+ return origSend.call(
1643
+ this,
1644
+ body
1645
+ );
1646
+ };
1647
+ }
1648
+ this.installed = true;
1649
+ }
1650
+ uninstall() {
1651
+ if (!this.installed) return;
1652
+ const g = globalThis;
1653
+ if (this.originalFetch) {
1654
+ g.fetch = this.originalFetch;
1655
+ this.originalFetch = void 0;
1656
+ }
1657
+ if (typeof g.XMLHttpRequest === "function") {
1658
+ if (this.originalXhrOpen) {
1659
+ g.XMLHttpRequest.prototype.open = this.originalXhrOpen;
1660
+ }
1661
+ if (this.originalXhrSend) {
1662
+ g.XMLHttpRequest.prototype.send = this.originalXhrSend;
1663
+ }
1664
+ }
1665
+ this.originalXhrOpen = void 0;
1666
+ this.originalXhrSend = void 0;
1667
+ this.installed = false;
1668
+ }
1669
+ entries(opts = {}) {
1670
+ return this.buffer.entries(opts);
1671
+ }
1672
+ size() {
1673
+ return this.buffer.size();
1674
+ }
1675
+ clear() {
1676
+ this.buffer.clear();
1677
+ }
1678
+ isInstalled() {
1679
+ return this.installed;
1680
+ }
1681
+ };
1682
+
1294
1683
  // src/server/ws-types.ts
1295
1684
  function isBatchRequest(msg) {
1296
1685
  return typeof msg === "object" && msg !== null && "batch" in msg && Array.isArray(msg.batch);
@@ -1341,6 +1730,10 @@ var WS_ROUTES = [
1341
1730
  },
1342
1731
  // Discovery
1343
1732
  { pattern: "control/find", handler: "find", httpMethods: ["POST"] },
1733
+ // Runner-parity alias for `control/find`; the runner exposes its
1734
+ // AI-powered element search at `/ui-bridge/ai/find`, so mobile mounts the
1735
+ // same handler at both paths to keep agents portable across platforms.
1736
+ { pattern: "ai/find", handler: "find", httpMethods: ["POST"] },
1344
1737
  { pattern: "control/snapshot", handler: "getSnapshot", httpMethods: ["GET"] },
1345
1738
  { pattern: "control/discover", handler: "getSnapshot" },
1346
1739
  // Workflows
@@ -1379,6 +1772,9 @@ var WS_ROUTES = [
1379
1772
  { pattern: "meta/methods", handler: "getMethods", httpMethods: ["GET"] },
1380
1773
  // AI helpers — mobile parity with the runner's `/ui-bridge/ai/*` family.
1381
1774
  { pattern: "ai/fill-form", handler: "fillForm", httpMethods: ["POST"] },
1775
+ // Coord-based tap — synthesizes a press at the given screen coords by
1776
+ // matching against registered elements' layout rects.
1777
+ { pattern: "control/tap", handler: "tapAt", httpMethods: ["POST"] },
1382
1778
  // Test hooks — gated behind `features.testHooks`. Let external runners
1383
1779
  // drive the ModalDetector over HTTP so tests can flip `snapshot.modalStack`
1384
1780
  // without mounting React components.
@@ -1393,6 +1789,20 @@ var WS_ROUTES = [
1393
1789
  handler: "dismissModal",
1394
1790
  httpMethods: ["POST"],
1395
1791
  requiresTestHooks: true
1792
+ },
1793
+ // Observability — last-N console errors + network requests. testHooks-gated
1794
+ // because patching `console.error` / `fetch` is invasive in production.
1795
+ {
1796
+ pattern: "control/console-errors",
1797
+ handler: "getConsoleErrors",
1798
+ httpMethods: ["GET"],
1799
+ requiresTestHooks: true
1800
+ },
1801
+ {
1802
+ pattern: "sdk/network-requests",
1803
+ handler: "getNetworkRequests",
1804
+ httpMethods: ["GET"],
1805
+ requiresTestHooks: true
1396
1806
  }
1397
1807
  ];
1398
1808
  var HANDLER_DESCRIPTIONS = {
@@ -1428,6 +1838,9 @@ var HANDLER_DESCRIPTIONS = {
1428
1838
  diffBaseline: "Diff current UI against a saved baseline",
1429
1839
  getMethods: "List every handler name (legacy introspection)",
1430
1840
  fillForm: "Fill multiple inputs in one call: body { fields: [{elementId, value}] } \u2014 each field dispatches `type` + `clear:true`",
1841
+ tapAt: "Synthesize a press at given screen coords by matching registered element layout rects: body { x, y, action? }",
1842
+ getConsoleErrors: "TEST HOOK \u2014 last-N console.error/console.warn entries; ?since=<ms>&limit=<n>",
1843
+ getNetworkRequests: "TEST HOOK \u2014 last-N fetch/XHR entries with status + duration; ?since=<ms>&limit=<n>",
1431
1844
  pushModal: "TEST HOOK \u2014 push a modal onto the snapshot.modalStack via the registry ModalDetector",
1432
1845
  dismissModal: "TEST HOOK \u2014 dismiss a modal by id from the snapshot.modalStack via the registry ModalDetector"
1433
1846
  };
@@ -1496,8 +1909,15 @@ var NativeUIBridgeServer = class {
1496
1909
  cors: true,
1497
1910
  ...config
1498
1911
  };
1912
+ this.consoleErrorBuffer = new ConsoleErrorBuffer();
1913
+ this.networkRequestBuffer = new NetworkRequestBuffer();
1914
+ if (this.config.appInfo) {
1915
+ this.registry.setAppInfo(this.config.appInfo);
1916
+ }
1499
1917
  this.handlers = createServerHandlers(registry, executor, {
1500
- appInfo: this.config.appInfo
1918
+ appInfo: this.config.appInfo,
1919
+ consoleErrorBuffer: this.consoleErrorBuffer,
1920
+ networkRequestBuffer: this.networkRequestBuffer
1501
1921
  });
1502
1922
  this.handlers.getMethods = async () => {
1503
1923
  const testHooks = this.config.testHooks === true;
@@ -1625,8 +2045,16 @@ var NativeUIBridgeServer = class {
1625
2045
  /**
1626
2046
  * Set a route provider for reporting the current navigation route.
1627
2047
  * When set, `control/snapshot` responses include `currentRoute` and `segments`.
2048
+ *
2049
+ * Also forwards the provider into the registry so the *default*
2050
+ * `getSnapshot` handler (and any other call site of
2051
+ * `registry.createSnapshot`) reads the route too — without this the
2052
+ * cloud-relay/bare-server path returned `currentRoute: null` even when
2053
+ * a `RouteTracker` was wired, because that path never installed the
2054
+ * server-level handler override below.
1628
2055
  */
1629
2056
  setRouteProvider(provider) {
2057
+ this.registry.setRouteProvider(provider);
1630
2058
  const baseGetElements = this.handlers.getElements;
1631
2059
  this.handlers.getElements = async (ctx) => {
1632
2060
  const currentRouteOnly = ctx.query?.currentRouteOnly === "true" || ctx.body?.currentRouteOnly === true;
@@ -1695,6 +2123,10 @@ var NativeUIBridgeServer = class {
1695
2123
  console.warn("[ui-bridge-native] See documentation for supported adapters.");
1696
2124
  return;
1697
2125
  }
2126
+ if (this.config.testHooks === true) {
2127
+ this.consoleErrorBuffer.install();
2128
+ this.networkRequestBuffer.install();
2129
+ }
1698
2130
  await this.adapter.start(this.config.serverPort, this.handleRequest.bind(this));
1699
2131
  this.running = true;
1700
2132
  console.log(`[ui-bridge-native] HTTP server started on port ${this.config.serverPort}`);
@@ -1708,6 +2140,8 @@ var NativeUIBridgeServer = class {
1708
2140
  }
1709
2141
  await this.adapter.stop();
1710
2142
  this.running = false;
2143
+ this.consoleErrorBuffer.uninstall();
2144
+ this.networkRequestBuffer.uninstall();
1711
2145
  console.log("[ui-bridge-native] HTTP server stopped");
1712
2146
  }
1713
2147
  /**
@@ -3004,7 +3438,9 @@ var WebSocketConnection = class {
3004
3438
  }
3005
3439
  };
3006
3440
 
3441
+ exports.ConsoleErrorBuffer = ConsoleErrorBuffer;
3007
3442
  exports.NativeUIBridgeServer = NativeUIBridgeServer;
3443
+ exports.NetworkRequestBuffer = NetworkRequestBuffer;
3008
3444
  exports.OPCODE_BINARY = OPCODE_BINARY;
3009
3445
  exports.OPCODE_CLOSE = OPCODE_CLOSE;
3010
3446
  exports.OPCODE_CONTINUATION = OPCODE_CONTINUATION;