@browserbridge/bbx 1.7.4 → 1.7.5

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.
@@ -6,12 +6,14 @@ import {
6
6
  DEFAULT_A11Y_MAX_DEPTH,
7
7
  DEFAULT_A11Y_MAX_NODES,
8
8
  DEFAULT_CONSOLE_LIMIT,
9
+ DEFAULT_DEVICE_SCALE_FACTOR,
9
10
  DEFAULT_EVAL_TIMEOUT_MS,
10
11
  DEFAULT_LOG_TAIL_LIMIT,
11
12
  DEFAULT_MAX_DEPTH,
12
13
  DEFAULT_MAX_HTML_LENGTH,
13
14
  DEFAULT_MAX_NODES,
14
15
  DEFAULT_NAV_TIMEOUT_MS,
16
+ DEFAULT_NETWORK_INTERCEPT_ACTION,
15
17
  DEFAULT_NETWORK_LIMIT,
16
18
  DEFAULT_PAGE_TEXT_BUDGET,
17
19
  DEFAULT_TEXT_BUDGET,
@@ -42,6 +44,7 @@ import { BRIDGE_METHODS, METHOD_SET, createBridgeMethodGroups } from './registry
42
44
  /** @typedef {import('./types.js').InputActionParams} InputActionParams */
43
45
  /** @typedef {import('./types.js').LogTailParams} LogTailParams */
44
46
  /** @typedef {import('./types.js').NavigationActionParams} NavigationActionParams */
47
+ /** @typedef {import('./types.js').NetworkInterceptAddParams} NetworkInterceptAddParams */
45
48
  /** @typedef {import('./types.js').NetworkParams} NetworkParams */
46
49
  /** @typedef {import('./types.js').NormalizedAccessibilityTreeParams} NormalizedAccessibilityTreeParams */
47
50
  /** @typedef {import('./types.js').NormalizedCheckedAction} NormalizedCheckedAction */
@@ -58,6 +61,7 @@ import { BRIDGE_METHODS, METHOD_SET, createBridgeMethodGroups } from './registry
58
61
  /** @typedef {import('./types.js').NormalizedInputAction} NormalizedInputAction */
59
62
  /** @typedef {import('./types.js').NormalizedLogTailParams} NormalizedLogTailParams */
60
63
  /** @typedef {import('./types.js').NormalizedNavigationAction} NormalizedNavigationAction */
64
+ /** @typedef {import('./types.js').NormalizedNetworkInterceptAddParams} NormalizedNetworkInterceptAddParams */
61
65
  /** @typedef {import('./types.js').NormalizedNetworkParams} NormalizedNetworkParams */
62
66
  /** @typedef {import('./types.js').NormalizedPageTextParams} NormalizedPageTextParams */
63
67
  /** @typedef {import('./types.js').NormalizedPatchOperation} NormalizedPatchOperation */
@@ -151,6 +155,30 @@ function clampInt(value, min, max, fallback) {
151
155
  return Math.min(Math.max(integer, min), max);
152
156
  }
153
157
 
158
+ /**
159
+ * Clamp a finite number without discarding its fractional component.
160
+ *
161
+ * @param {unknown} value
162
+ * @param {number} min
163
+ * @param {number} max
164
+ * @param {number} fallback
165
+ * @returns {number}
166
+ */
167
+ function clampNumber(value, min, max, fallback) {
168
+ const numeric = typeof value === 'number' && Number.isFinite(value) ? value : fallback;
169
+ return Math.min(Math.max(numeric, min), max);
170
+ }
171
+
172
+ /**
173
+ * @param {unknown} value
174
+ * @returns {value is Record<string, unknown>}
175
+ */
176
+ function isPlainRecord(value) {
177
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
178
+ const prototype = Object.getPrototypeOf(value);
179
+ return prototype === Object.prototype || prototype === null;
180
+ }
181
+
154
182
  /** @type {ReadonlyArray<BridgeMethod>} */
155
183
  export const METHODS = BRIDGE_METHODS;
156
184
 
@@ -336,6 +364,8 @@ function normalizeRequestParams(method, params) {
336
364
  return normalizePageTextParams(params);
337
365
  case 'page.get_network':
338
366
  return normalizeNetworkParams(params);
367
+ case 'network.intercept.add':
368
+ return normalizeNetworkInterceptAddParams(params);
339
369
  case 'navigation.navigate':
340
370
  case 'navigation.reload':
341
371
  case 'navigation.go_back':
@@ -643,7 +673,7 @@ export function normalizeNavigationAction(params = {}) {
643
673
  return {
644
674
  url,
645
675
  waitForLoad: params.waitForLoad !== false,
646
- timeoutMs: clampInt(params.timeoutMs, 500, 120_000, 15_000),
676
+ timeoutMs: clampInt(params.timeoutMs, 500, 120_000, DEFAULT_NAV_TIMEOUT_MS),
647
677
  };
648
678
  }
649
679
 
@@ -682,11 +712,17 @@ export function normalizeEvaluateParams(params = {}) {
682
712
  `Expression too large (${expression.length} chars). Maximum is 100,000.`
683
713
  );
684
714
  }
715
+ if (params.returnByValue === false) {
716
+ throw new BridgeError(
717
+ ERROR_CODES.INVALID_REQUEST,
718
+ 'returnByValue=false is not supported because remote CDP objects cannot be consumed.'
719
+ );
720
+ }
685
721
  return {
686
722
  expression,
687
723
  awaitPromise: Boolean(params.awaitPromise),
688
- timeoutMs: clampInt(params.timeoutMs, 100, 30_000, 5_000),
689
- returnByValue: params.returnByValue !== false,
724
+ timeoutMs: clampInt(params.timeoutMs, 100, 30_000, DEFAULT_EVAL_TIMEOUT_MS),
725
+ returnByValue: true,
690
726
  };
691
727
  }
692
728
 
@@ -829,7 +865,7 @@ export function normalizeStorageParams(params = {}) {
829
865
  export function normalizeWaitForLoadStateParams(params = {}) {
830
866
  return {
831
867
  waitForLoad: params.waitForLoad !== false,
832
- timeoutMs: clampInt(params.timeoutMs, 500, 120_000, 15_000),
868
+ timeoutMs: clampInt(params.timeoutMs, 500, 120_000, DEFAULT_NAV_TIMEOUT_MS),
833
869
  };
834
870
  }
835
871
 
@@ -898,6 +934,71 @@ export function normalizeNetworkParams(params = {}) {
898
934
  };
899
935
  }
900
936
 
937
+ /**
938
+ * @param {NetworkInterceptAddParams} [params={}]
939
+ * @returns {NormalizedNetworkInterceptAddParams}
940
+ */
941
+ export function normalizeNetworkInterceptAddParams(params = {}) {
942
+ if (typeof params.urlPattern !== 'string' || !params.urlPattern) {
943
+ throw new BridgeError(ERROR_CODES.INVALID_REQUEST, 'urlPattern is required.');
944
+ }
945
+
946
+ const action = params.action ?? DEFAULT_NETWORK_INTERCEPT_ACTION;
947
+ if (action !== 'fulfill' && action !== 'continue' && action !== 'block') {
948
+ throw new BridgeError(
949
+ ERROR_CODES.INVALID_REQUEST,
950
+ 'action must be one of fulfill, continue, or block.'
951
+ );
952
+ }
953
+ if (
954
+ params.statusCode !== undefined &&
955
+ (!Number.isInteger(params.statusCode) || params.statusCode < 100 || params.statusCode > 599)
956
+ ) {
957
+ throw new BridgeError(
958
+ ERROR_CODES.INVALID_REQUEST,
959
+ 'statusCode must be an integer between 100 and 599.'
960
+ );
961
+ }
962
+ if (params.body !== undefined && typeof params.body !== 'string') {
963
+ throw new BridgeError(ERROR_CODES.INVALID_REQUEST, 'body must be a string.');
964
+ }
965
+
966
+ /** @type {Record<string, string> | undefined} */
967
+ let headers;
968
+ if (params.headers !== undefined) {
969
+ if (!isPlainRecord(params.headers)) {
970
+ throw new BridgeError(
971
+ ERROR_CODES.INVALID_REQUEST,
972
+ 'headers must be an object containing string values.'
973
+ );
974
+ }
975
+ headers = {};
976
+ for (const [name, value] of Object.entries(params.headers)) {
977
+ if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) {
978
+ throw new BridgeError(
979
+ ERROR_CODES.INVALID_REQUEST,
980
+ `Invalid header name: ${name || '(empty)'}.`
981
+ );
982
+ }
983
+ if (typeof value !== 'string' || /[\r\n]/.test(value)) {
984
+ throw new BridgeError(
985
+ ERROR_CODES.INVALID_REQUEST,
986
+ `Header ${name} must have a string value without newlines.`
987
+ );
988
+ }
989
+ headers[name] = value;
990
+ }
991
+ }
992
+
993
+ return {
994
+ urlPattern: params.urlPattern,
995
+ action,
996
+ ...(params.statusCode === undefined ? {} : { statusCode: params.statusCode }),
997
+ ...(params.body === undefined ? {} : { body: params.body }),
998
+ ...(headers === undefined ? {} : { headers }),
999
+ };
1000
+ }
1001
+
901
1002
  /**
902
1003
  * @param {PageTextParams} [params={}]
903
1004
  * @returns {NormalizedPageTextParams}
@@ -926,7 +1027,7 @@ export function normalizeViewportResizeParams(params = {}) {
926
1027
  return {
927
1028
  width: clampInt(params.width, 320, 7680, DEFAULT_VIEWPORT_WIDTH),
928
1029
  height: clampInt(params.height, 200, 4320, DEFAULT_VIEWPORT_HEIGHT),
929
- deviceScaleFactor: clampInt(params.deviceScaleFactor, 0, 4, 0),
1030
+ deviceScaleFactor: clampNumber(params.deviceScaleFactor, 0, 4, DEFAULT_DEVICE_SCALE_FACTOR),
930
1031
  reset: Boolean(params.reset),
931
1032
  };
932
1033
  }
@@ -408,7 +408,7 @@ export interface NormalizedEvaluateParams extends BridgeParams {
408
408
  expression: string;
409
409
  awaitPromise: boolean;
410
410
  timeoutMs: number;
411
- returnByValue: boolean;
411
+ returnByValue: true;
412
412
  }
413
413
 
414
414
  export interface ConsoleParams {
@@ -565,6 +565,24 @@ export interface NormalizedNetworkParams extends BridgeParams {
565
565
  urlPattern: string | null;
566
566
  }
567
567
 
568
+ export type NetworkInterceptAction = 'fulfill' | 'continue' | 'block';
569
+
570
+ export interface NetworkInterceptAddParams {
571
+ urlPattern?: string;
572
+ action?: NetworkInterceptAction;
573
+ statusCode?: number;
574
+ body?: string;
575
+ headers?: Record<string, string>;
576
+ }
577
+
578
+ export interface NormalizedNetworkInterceptAddParams extends BridgeParams {
579
+ urlPattern: string;
580
+ action: NetworkInterceptAction;
581
+ statusCode?: number;
582
+ body?: string;
583
+ headers?: Record<string, string>;
584
+ }
585
+
568
586
  export interface PageTextParams {
569
587
  textBudget?: number;
570
588
  }
@@ -21,7 +21,7 @@ For open-ended investigation, start with structured reads (`page.get_state`, `do
21
21
  bbx status # daemon + extension health
22
22
  bbx doctor # install/access readiness
23
23
  bbx access-request # ask user to enable access for the focused window
24
- bbx restart # start/restart the local daemon non-interactively
24
+ bbx restart # start/restart the daemon and running MCP servers
25
25
  bbx call <method> '{...}' # any RPC method (raw output)
26
26
  bbx <method> '{...}' # direct alias for an exact bridge method such as page.get_state
27
27
  bbx call --tab 123 <method> '{...}' # explicit tab override