@vforsh/argus 0.1.20 → 0.1.21

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/argus.js CHANGED
@@ -5,15 +5,29 @@ var __getProtoOf = Object.getPrototypeOf;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ function __accessProp(key) {
9
+ return this[key];
10
+ }
11
+ var __toESMCache_node;
12
+ var __toESMCache_esm;
8
13
  var __toESM = (mod, isNodeMode, target) => {
14
+ var canCache = mod != null && typeof mod === "object";
15
+ if (canCache) {
16
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
+ var cached = cache.get(mod);
18
+ if (cached)
19
+ return cached;
20
+ }
9
21
  target = mod != null ? __create(__getProtoOf(mod)) : {};
10
22
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
23
  for (let key of __getOwnPropNames(mod))
12
24
  if (!__hasOwnProp.call(to, key))
13
25
  __defProp(to, key, {
14
- get: () => mod[key],
26
+ get: __accessProp.bind(mod, key),
15
27
  enumerable: true
16
28
  });
29
+ if (canCache)
30
+ cache.set(mod, to);
17
31
  return to;
18
32
  };
19
33
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
@@ -2130,7 +2144,7 @@ var {
2130
2144
  // package.json
2131
2145
  var package_default = {
2132
2146
  name: "@vforsh/argus",
2133
- version: "0.1.20",
2147
+ version: "0.1.21",
2134
2148
  repository: {
2135
2149
  type: "git",
2136
2150
  url: "git+https://github.com/vforsh/argus.git",
@@ -6838,6 +6852,9 @@ function validateAddRequest(body) {
6838
6852
  if (payload.times !== undefined && (!Number.isInteger(payload.times) || payload.times < 1)) {
6839
6853
  return "times must be an integer >= 1";
6840
6854
  }
6855
+ if (payload.scope !== undefined && payload.scope !== "page" && payload.scope !== "selected") {
6856
+ return "scope must be one of: page, selected";
6857
+ }
6841
6858
  return null;
6842
6859
  }
6843
6860
  var validateMatch = (match) => {
@@ -9358,10 +9375,12 @@ var route29 = defineJsonRoute({
9358
9375
  });
9359
9376
 
9360
9377
  // ../argus-watcher/dist/cdp/keyboard.js
9361
- var buildKeyMap = () => {
9362
- const map = new Map;
9378
+ var buildKeyMaps = () => {
9379
+ const byKey = new Map;
9380
+ const byCode = new Map;
9363
9381
  const put = (lookup, def) => {
9364
- map.set(lookup.toLowerCase(), def);
9382
+ byKey.set(lookup.toLowerCase(), def);
9383
+ byCode.set(def.code.toLowerCase(), def);
9365
9384
  };
9366
9385
  for (let i = 0;i < 26; i++) {
9367
9386
  const lower = String.fromCharCode(97 + i);
@@ -9396,11 +9415,14 @@ var buildKeyMap = () => {
9396
9415
  const name = `F${i}`;
9397
9416
  put(name, { key: name, code: name, keyCode: 111 + i });
9398
9417
  }
9399
- return map;
9418
+ return { byKey, byCode };
9400
9419
  };
9401
- var KEY_MAP = buildKeyMap();
9420
+ var KEY_MAPS = buildKeyMaps();
9402
9421
  var resolveKeyDefinition = (key) => {
9403
- return KEY_MAP.get(key.toLowerCase());
9422
+ return KEY_MAPS.byKey.get(key.toLowerCase());
9423
+ };
9424
+ var resolveCodeDefinition = (code) => {
9425
+ return KEY_MAPS.byCode.get(code.toLowerCase());
9404
9426
  };
9405
9427
  var MODIFIER_BITS = {
9406
9428
  alt: 1,
@@ -9430,8 +9452,61 @@ var parseModifiers = (input) => {
9430
9452
  }
9431
9453
  return mask;
9432
9454
  };
9455
+ var resolveKeyboardEvent = (options) => {
9456
+ const modifiers = options.modifiers ?? 0;
9457
+ const keyInput = normalizeOptionalString(options.key);
9458
+ const codeInput = normalizeOptionalString(options.code);
9459
+ if (!keyInput && !codeInput) {
9460
+ throw new Error("key or code is required");
9461
+ }
9462
+ const keyDef = keyInput ? resolveKeyDefinition(keyInput) : undefined;
9463
+ const codeDef = codeInput ? resolveCodeDefinition(codeInput) : undefined;
9464
+ const baseDef = codeDef ?? keyDef;
9465
+ if (!baseDef) {
9466
+ if (codeInput && !keyInput) {
9467
+ throw new Error(`Unknown code: "${codeInput}"`);
9468
+ }
9469
+ throw new Error(`Unknown key: "${keyInput}"`);
9470
+ }
9471
+ const key = resolveEventKey(keyInput, baseDef, modifiers);
9472
+ const text = resolveEventText(key, baseDef);
9473
+ const event = {
9474
+ key,
9475
+ code: codeInput ?? baseDef.code,
9476
+ keyCode: baseDef.keyCode,
9477
+ modifiers,
9478
+ altKey: (modifiers & MODIFIER_BITS.alt) !== 0,
9479
+ ctrlKey: (modifiers & MODIFIER_BITS.ctrl) !== 0,
9480
+ metaKey: (modifiers & MODIFIER_BITS.meta) !== 0,
9481
+ shiftKey: (modifiers & MODIFIER_BITS.shift) !== 0
9482
+ };
9483
+ if (text !== undefined) {
9484
+ event.text = text;
9485
+ }
9486
+ return event;
9487
+ };
9488
+ var normalizeOptionalString = (value) => {
9489
+ const normalized = value?.trim();
9490
+ return normalized ? normalized : undefined;
9491
+ };
9492
+ var resolveEventKey = (keyInput, def, modifiers) => {
9493
+ const semanticKey = keyInput ?? def.key;
9494
+ if (isSingleLetter(semanticKey)) {
9495
+ return isShift(modifiers) ? semanticKey.toUpperCase() : semanticKey.toLowerCase();
9496
+ }
9497
+ return semanticKey;
9498
+ };
9499
+ var resolveEventText = (key, def) => {
9500
+ if (key.length === 1) {
9501
+ return key;
9502
+ }
9503
+ return def.text;
9504
+ };
9505
+ var isSingleLetter = (value) => /^[a-z]$/i.test(value);
9506
+ var isShift = (modifiers) => (modifiers & MODIFIER_BITS.shift) !== 0;
9433
9507
  var dispatchKeydown = async (session, options) => {
9434
- const { key, selector, modifiers = 0 } = options;
9508
+ const { selector } = options;
9509
+ const event = resolveKeyboardEvent(options);
9435
9510
  let focused = false;
9436
9511
  if (selector) {
9437
9512
  const { allNodeIds, nodeIds } = await resolveDomSelectorMatches(session, selector, false);
@@ -9447,31 +9522,23 @@ var dispatchKeydown = async (session, options) => {
9447
9522
  await session.sendAndWait("DOM.focus", { nodeId });
9448
9523
  focused = true;
9449
9524
  }
9450
- const def = resolveKeyDefinition(key);
9451
- if (!def) {
9452
- throw new Error(`Unknown key: "${key}"`);
9453
- }
9454
- let text = def.text;
9455
- if (text && text.length === 1 && (modifiers & 8) !== 0) {
9456
- text = text.toUpperCase();
9457
- }
9458
- const isPrintable = text != null && text !== "\r";
9525
+ const isPrintable = event.text != null && event.text !== "\r";
9459
9526
  const baseParams = {
9460
- code: def.code,
9461
- key: def.key,
9462
- windowsVirtualKeyCode: def.keyCode,
9463
- nativeVirtualKeyCode: def.keyCode,
9464
- modifiers
9527
+ code: event.code,
9528
+ key: event.key,
9529
+ windowsVirtualKeyCode: event.keyCode,
9530
+ nativeVirtualKeyCode: event.keyCode,
9531
+ modifiers: event.modifiers
9465
9532
  };
9466
9533
  if (isPrintable) {
9467
- await session.sendAndWait("Input.dispatchKeyEvent", { ...baseParams, type: "keyDown", text });
9468
- await session.sendAndWait("Input.dispatchKeyEvent", { ...baseParams, type: "char", text });
9534
+ await session.sendAndWait("Input.dispatchKeyEvent", { ...baseParams, type: "keyDown", text: event.text });
9535
+ await session.sendAndWait("Input.dispatchKeyEvent", { ...baseParams, type: "char", text: event.text });
9469
9536
  await session.sendAndWait("Input.dispatchKeyEvent", { ...baseParams, type: "keyUp" });
9470
9537
  } else {
9471
- await session.sendAndWait("Input.dispatchKeyEvent", { ...baseParams, type: "rawKeyDown", text: def.text });
9538
+ await session.sendAndWait("Input.dispatchKeyEvent", { ...baseParams, type: "rawKeyDown", text: event.text });
9472
9539
  await session.sendAndWait("Input.dispatchKeyEvent", { ...baseParams, type: "keyUp" });
9473
9540
  }
9474
- return { key: def.key, modifiers, focused };
9541
+ return { key: event.key, code: event.code, modifiers: event.modifiers, focused, event };
9475
9542
  };
9476
9543
 
9477
9544
  // ../argus-watcher/dist/http/routes/postDomKeydown.js
@@ -9481,12 +9548,23 @@ var route30 = defineJsonRoute({
9481
9548
  parseBody: true,
9482
9549
  endpoint: "dom/keydown",
9483
9550
  validate: (payload) => {
9484
- if (!payload.key || typeof payload.key !== "string") {
9485
- return "key is required";
9551
+ const hasKey = typeof payload.key === "string" && payload.key.trim() !== "";
9552
+ const hasCode = typeof payload.code === "string" && payload.code.trim() !== "";
9553
+ if (payload.key != null && !hasKey) {
9554
+ return "key must be a non-empty string";
9555
+ }
9556
+ if (payload.code != null && !hasCode) {
9557
+ return "code must be a non-empty string";
9558
+ }
9559
+ if (!hasKey && !hasCode) {
9560
+ return "key or code is required";
9486
9561
  }
9487
9562
  if (payload.selector != null && (typeof payload.selector !== "string" || payload.selector.trim() === "")) {
9488
9563
  return "selector must be a non-empty string";
9489
9564
  }
9565
+ if (payload.modifiers != null && typeof payload.modifiers !== "string") {
9566
+ return "modifiers must be a string";
9567
+ }
9490
9568
  try {
9491
9569
  parseModifiers(payload.modifiers);
9492
9570
  } catch (error) {
@@ -9497,10 +9575,18 @@ var route30 = defineJsonRoute({
9497
9575
  handle: async ({ ctx, body: payload }) => {
9498
9576
  const result = await dispatchKeydown(ctx.cdpSession, {
9499
9577
  key: payload.key,
9578
+ code: payload.code,
9500
9579
  selector: payload.selector,
9501
9580
  modifiers: parseModifiers(payload.modifiers)
9502
9581
  });
9503
- return { ok: true, key: result.key, modifiers: result.modifiers, focused: result.focused };
9582
+ return {
9583
+ ok: true,
9584
+ key: result.key,
9585
+ code: result.code,
9586
+ modifiers: result.modifiers,
9587
+ focused: result.focused,
9588
+ event: result.event
9589
+ };
9504
9590
  }
9505
9591
  });
9506
9592
 
@@ -11905,73 +11991,169 @@ var createThrottleController = () => {
11905
11991
  return { getStatus, setDesired, clearDesired, onAttach };
11906
11992
  };
11907
11993
 
11908
- // ../argus-watcher/dist/net/NetMockController.js
11909
- var createNetMockController = () => {
11910
- let session = null;
11911
- let bound = false;
11912
- let enabled = false;
11913
- let lastError = null;
11914
- let nextRuleId = 1;
11915
- const rules = [];
11916
- const hasActiveRules = () => rules.some(isRuleActive);
11917
- const recordError = (error) => {
11918
- lastError = { message: error instanceof Error ? error.message : String(error) };
11919
- const code = error?.code;
11920
- if (typeof code === "string") {
11921
- lastError.code = code;
11994
+ // ../argus-watcher/dist/net/NetMockInterception.js
11995
+ var PAGE_TARGET = { key: "page", sessionId: null };
11996
+
11997
+ class NetMockInterception {
11998
+ onPaused;
11999
+ onError;
12000
+ binding = null;
12001
+ bound = false;
12002
+ enabledTargets = new Map;
12003
+ reconcileTail = Promise.resolve();
12004
+ constructor(onPaused, onError) {
12005
+ this.onPaused = onPaused;
12006
+ this.onError = onError;
12007
+ }
12008
+ bind(binding) {
12009
+ this.binding = binding;
12010
+ if (this.bound) {
12011
+ return;
11922
12012
  }
11923
- };
11924
- const enableInterception = async () => {
11925
- if (!session || !session.isAttached()) {
11926
- enabled = false;
11927
- return false;
12013
+ this.bound = true;
12014
+ binding.pageSession.onEvent("Fetch.requestPaused", (params, meta) => {
12015
+ const request = parsePausedRequest(params, meta);
12016
+ if (request) {
12017
+ this.onPaused(request);
12018
+ }
12019
+ });
12020
+ }
12021
+ get enabled() {
12022
+ return this.enabledTargets.size > 0;
12023
+ }
12024
+ matchesScope(scope, request) {
12025
+ if (scope === "page") {
12026
+ return request.sessionId === null;
11928
12027
  }
11929
- try {
11930
- await session.sendAndWait("Fetch.enable", { patterns: [{ urlPattern: "*", requestStage: "Request" }] });
11931
- enabled = true;
11932
- return true;
11933
- } catch (error) {
11934
- enabled = false;
11935
- recordError(error);
12028
+ const selected = this.binding?.getSelectedTarget() ?? null;
12029
+ if (selected?.sessionId) {
12030
+ return request.sessionId === selected.sessionId;
12031
+ }
12032
+ if (request.sessionId !== null) {
11936
12033
  return false;
11937
12034
  }
11938
- };
11939
- const disableInterception = async () => {
11940
- if (!enabled) {
12035
+ const selectedFrameId = selected?.frameId ?? null;
12036
+ const selectedIsChildFrame = selectedFrameId !== null && selectedFrameId !== selected?.topFrameId;
12037
+ return !selectedIsChildFrame || request.frameId === selectedFrameId;
12038
+ }
12039
+ async sendRequestCommand(request, method, params) {
12040
+ await this.sendCommand({ key: request.sessionId ? `session:${request.sessionId}` : "page", sessionId: request.sessionId }, method, params);
12041
+ }
12042
+ reconcile(scopes, reset = false) {
12043
+ const requestedScopes = new Set(scopes);
12044
+ const operation = this.reconcileTail.then(() => this.applyReconcile(requestedScopes, reset));
12045
+ this.reconcileTail = operation.then(() => {
11941
12046
  return;
11942
- }
11943
- enabled = false;
11944
- if (!session || !session.isAttached()) {
12047
+ }, () => {
11945
12048
  return;
12049
+ });
12050
+ return operation;
12051
+ }
12052
+ async applyReconcile(scopes, reset) {
12053
+ if (reset) {
12054
+ this.enabledTargets.clear();
12055
+ }
12056
+ const session = this.binding?.pageSession;
12057
+ if (!session?.isAttached()) {
12058
+ this.enabledTargets.clear();
12059
+ return false;
12060
+ }
12061
+ const desiredTargets = this.getDesiredTargets(scopes);
12062
+ for (const [key, target] of this.enabledTargets) {
12063
+ if (!desiredTargets.has(key)) {
12064
+ await this.sendCommand(target, "Fetch.disable");
12065
+ this.enabledTargets.delete(key);
12066
+ }
12067
+ }
12068
+ for (const [key, target] of desiredTargets) {
12069
+ if (this.enabledTargets.has(key)) {
12070
+ continue;
12071
+ }
12072
+ const enabled = await this.sendCommand(target, "Fetch.enable", { patterns: [{ urlPattern: "*", requestStage: "Request" }] });
12073
+ if (enabled) {
12074
+ this.enabledTargets.set(key, target);
12075
+ }
12076
+ }
12077
+ return this.enabled;
12078
+ }
12079
+ onDetach() {
12080
+ this.enabledTargets.clear();
12081
+ }
12082
+ getDesiredTargets(scopes) {
12083
+ const targets = new Map;
12084
+ if (scopes.has("page")) {
12085
+ targets.set(PAGE_TARGET.key, PAGE_TARGET);
12086
+ }
12087
+ if (!scopes.has("selected")) {
12088
+ return targets;
12089
+ }
12090
+ const sessionId = this.binding?.getSelectedTarget()?.sessionId ?? null;
12091
+ const selectedTarget = sessionId ? { key: `session:${sessionId}`, sessionId } : PAGE_TARGET;
12092
+ targets.set(selectedTarget.key, selectedTarget);
12093
+ return targets;
12094
+ }
12095
+ async sendCommand(target, method, params = {}) {
12096
+ const session = this.binding?.pageSession;
12097
+ if (!session?.isAttached()) {
12098
+ return false;
11946
12099
  }
11947
12100
  try {
11948
- await session.sendAndWait("Fetch.disable");
12101
+ await session.sendAndWait(method, params, target.sessionId ? { sessionId: target.sessionId } : undefined);
12102
+ return true;
11949
12103
  } catch (error) {
11950
- recordError(error);
12104
+ if (!isBenignInterceptionError(error)) {
12105
+ this.onError(error);
12106
+ }
12107
+ return false;
11951
12108
  }
12109
+ }
12110
+ }
12111
+ var parsePausedRequest = (params, meta) => {
12112
+ const paused = params;
12113
+ const requestId = paused?.requestId;
12114
+ if (typeof requestId !== "string" || requestId === "") {
12115
+ return null;
12116
+ }
12117
+ return {
12118
+ requestId,
12119
+ url: paused.request?.url ?? "",
12120
+ method: paused.request?.method ?? "GET",
12121
+ resourceType: paused.resourceType ?? "",
12122
+ frameId: paused.frameId ?? null,
12123
+ sessionId: meta.sessionId ?? null,
12124
+ headers: paused.request?.headers ?? {}
11952
12125
  };
11953
- const bind = (nextSession) => {
11954
- session = nextSession;
11955
- if (bound) {
11956
- return;
12126
+ };
12127
+ var isBenignInterceptionError = (error) => {
12128
+ const code = error?.code;
12129
+ if (code === "cdp_not_attached") {
12130
+ return true;
12131
+ }
12132
+ const message = error instanceof Error ? error.message : String(error);
12133
+ return message.includes("Invalid InterceptionId") || message.includes("Inspected target navigated or closed") || message.includes("Session with given id not found") || message.includes("No target with given id");
12134
+ };
12135
+
12136
+ // ../argus-watcher/dist/net/NetMockController.js
12137
+ var createNetMockController = () => {
12138
+ let lastError = null;
12139
+ let nextRuleId = 1;
12140
+ const rules = [];
12141
+ const recordError = (error) => {
12142
+ lastError = { message: error instanceof Error ? error.message : String(error) };
12143
+ const code = error?.code;
12144
+ if (typeof code === "string") {
12145
+ lastError.code = code;
11957
12146
  }
11958
- bound = true;
11959
- nextSession.onEvent("Fetch.requestPaused", (params) => {
11960
- handleRequestPaused(params);
11961
- });
11962
12147
  };
11963
- const handleRequestPaused = async (params) => {
11964
- const paused = params;
11965
- const requestId = paused?.requestId;
11966
- if (typeof requestId !== "string" || requestId === "") {
11967
- return;
11968
- }
11969
- const url = paused.request?.url ?? "";
11970
- const method = paused.request?.method ?? "GET";
11971
- const resourceType = paused.resourceType ?? "";
11972
- const rule = rules.find((candidate) => ruleMatches(candidate, url, method, resourceType));
12148
+ const interception = new NetMockInterception((request) => void handleRequestPaused(request), recordError);
12149
+ const bind = (binding) => {
12150
+ interception.bind(binding);
12151
+ interception.reconcile(getActiveScopes(rules));
12152
+ };
12153
+ const handleRequestPaused = async (paused) => {
12154
+ const rule = rules.find((candidate) => interception.matchesScope(candidate.scope ?? "page", paused) && ruleMatches(candidate, paused.url, paused.method, paused.resourceType));
11973
12155
  if (!rule) {
11974
- await sendAction("Fetch.continueRequest", { requestId });
12156
+ await sendAction(paused, "Fetch.continueRequest", { requestId: paused.requestId });
11975
12157
  return;
11976
12158
  }
11977
12159
  rule.hits += 1;
@@ -11980,53 +12162,41 @@ var createNetMockController = () => {
11980
12162
  }
11981
12163
  const action = rule.action;
11982
12164
  if (action.kind === "block") {
11983
- await sendAction("Fetch.failRequest", { requestId, errorReason: "BlockedByClient" });
12165
+ await sendAction(paused, "Fetch.failRequest", { requestId: paused.requestId, errorReason: "BlockedByClient" });
11984
12166
  return;
11985
12167
  }
11986
12168
  if (action.kind === "fail") {
11987
- await sendAction("Fetch.failRequest", { requestId, errorReason: action.reason });
12169
+ await sendAction(paused, "Fetch.failRequest", { requestId: paused.requestId, errorReason: action.reason });
11988
12170
  return;
11989
12171
  }
11990
12172
  if (action.kind === "fulfill") {
11991
- const fulfillParams = { requestId, responseCode: action.status };
12173
+ const fulfillParams = { requestId: paused.requestId, responseCode: action.status };
11992
12174
  if (action.headers && action.headers.length > 0) {
11993
12175
  fulfillParams.responseHeaders = action.headers;
11994
12176
  }
11995
12177
  if (action.bodyBase64 != null) {
11996
12178
  fulfillParams.body = action.bodyBase64;
11997
12179
  }
11998
- await sendAction("Fetch.fulfillRequest", fulfillParams);
12180
+ await sendAction(paused, "Fetch.fulfillRequest", fulfillParams);
11999
12181
  return;
12000
12182
  }
12001
- const continueParams = { requestId };
12183
+ const continueParams = { requestId: paused.requestId };
12002
12184
  if (action.rewriteHost) {
12003
- const rewritten = rewriteUrlHost(url, action.rewriteHost);
12185
+ const rewritten = rewriteUrlHost(paused.url, action.rewriteHost);
12004
12186
  if (rewritten) {
12005
12187
  continueParams.url = rewritten;
12006
12188
  }
12007
12189
  }
12008
12190
  if (action.setHeaders && action.setHeaders.length > 0) {
12009
- continueParams.headers = mergeHeaders(paused.request?.headers ?? {}, action.setHeaders);
12010
- }
12011
- await sendAction("Fetch.continueRequest", continueParams);
12012
- };
12013
- const sendAction = async (cdpMethod, params) => {
12014
- if (!session || !session.isAttached()) {
12015
- return;
12016
- }
12017
- try {
12018
- await session.sendAndWait(cdpMethod, params);
12019
- } catch (error) {
12020
- if (isBenignInterceptionError(error)) {
12021
- return;
12022
- }
12023
- recordError(error);
12191
+ continueParams.headers = mergeHeaders(paused.headers, action.setHeaders);
12024
12192
  }
12193
+ await sendAction(paused, "Fetch.continueRequest", continueParams);
12025
12194
  };
12195
+ const sendAction = async (request, cdpMethod, params) => interception.sendRequestCommand(request, cdpMethod, params);
12026
12196
  const getStatus = (ctx) => ({
12027
12197
  ok: true,
12028
12198
  attached: ctx.attached,
12029
- enabled,
12199
+ enabled: interception.enabled,
12030
12200
  rules: rules.map(toPublicRule),
12031
12201
  lastError
12032
12202
  });
@@ -12034,6 +12204,7 @@ var createNetMockController = () => {
12034
12204
  lastError = null;
12035
12205
  const rule = {
12036
12206
  id: nextRuleId++,
12207
+ scope: input.scope ?? "page",
12037
12208
  match: input.match,
12038
12209
  action: input.action,
12039
12210
  delayMs: input.delayMs,
@@ -12046,7 +12217,7 @@ var createNetMockController = () => {
12046
12217
  if (!attached) {
12047
12218
  return { ok: true, attached: false, enabled: false, rule: toPublicRule(rule) };
12048
12219
  }
12049
- const applied = enabled || await enableInterception();
12220
+ const applied = await interception.reconcile(getActiveScopes(rules));
12050
12221
  return {
12051
12222
  ok: true,
12052
12223
  attached: true,
@@ -12062,32 +12233,33 @@ var createNetMockController = () => {
12062
12233
  rules.splice(index, 1);
12063
12234
  }
12064
12235
  if (rules.length === 0) {
12065
- await disableInterception();
12236
+ await interception.reconcile(new Set);
12237
+ } else {
12238
+ await interception.reconcile(getActiveScopes(rules));
12066
12239
  }
12067
- return { ok: true, removed, enabled };
12240
+ return { ok: true, removed, enabled: interception.enabled };
12068
12241
  };
12069
12242
  const clearRules = async () => {
12070
12243
  const removed = rules.length;
12071
12244
  rules.length = 0;
12072
- await disableInterception();
12073
- return { ok: true, removed, enabled };
12245
+ await interception.reconcile(new Set);
12246
+ return { ok: true, removed, enabled: interception.enabled };
12074
12247
  };
12075
- const onAttach = async (nextSession) => {
12076
- session = nextSession;
12077
- enabled = false;
12078
- if (!hasActiveRules()) {
12079
- return;
12080
- }
12081
- const applied = await enableInterception();
12248
+ const onAttach = async () => {
12249
+ const applied = await interception.reconcile(getActiveScopes(rules), true);
12082
12250
  if (!applied && lastError) {
12083
12251
  console.warn(`[NetMock] Failed to re-enable interception on attach: ${lastError.message}`);
12084
12252
  }
12085
12253
  };
12254
+ const onTargetChanged = async () => {
12255
+ await interception.reconcile(getActiveScopes(rules));
12256
+ };
12086
12257
  const onDetach = () => {
12087
- enabled = false;
12258
+ interception.onDetach();
12088
12259
  };
12089
- return { bind, getStatus, addRule, removeRule, clearRules, onAttach, onDetach };
12260
+ return { bind, getStatus, addRule, removeRule, clearRules, onAttach, onTargetChanged, onDetach };
12090
12261
  };
12262
+ var getActiveScopes = (rules) => new Set(rules.filter(isRuleActive).map((rule) => rule.scope ?? "page"));
12091
12263
  var isRuleActive = (rule) => rule.times == null || rule.hits < rule.times;
12092
12264
  var ruleMatches = (rule, url, method, resourceType) => {
12093
12265
  if (!isRuleActive(rule)) {
@@ -12131,16 +12303,9 @@ var mergeHeaders = (original, overrides) => {
12131
12303
  }
12132
12304
  return [...merged.values()];
12133
12305
  };
12134
- var isBenignInterceptionError = (error) => {
12135
- const code = error?.code;
12136
- if (code === "cdp_not_attached") {
12137
- return true;
12138
- }
12139
- const message = error instanceof Error ? error.message : String(error);
12140
- return message.includes("Invalid InterceptionId") || message.includes("Inspected target navigated or closed");
12141
- };
12142
12306
  var toPublicRule = (rule) => ({
12143
12307
  id: rule.id,
12308
+ scope: rule.scope ?? "page",
12144
12309
  match: rule.match,
12145
12310
  action: rule.action,
12146
12311
  delayMs: rule.delayMs,
@@ -17053,7 +17218,7 @@ var createWatcherHandle = async (options, watcherId) => {
17053
17218
  await emulationController.onAttach(session);
17054
17219
  await throttleController.onAttach(session);
17055
17220
  await visibilityController.onAttach(sourceHandle.pageSession ?? session);
17056
- await netMockController.onAttach(sourceHandle.pageSession ?? session);
17221
+ await netMockController.onAttach();
17057
17222
  await networkCapture?.onAttached();
17058
17223
  onIndicatorAttach(session, target);
17059
17224
  await maybeInjectOnAttach(session, target);
@@ -17070,6 +17235,7 @@ var createWatcherHandle = async (options, watcherId) => {
17070
17235
  },
17071
17236
  reason: null
17072
17237
  });
17238
+ netMockController.onTargetChanged();
17073
17239
  onIndicatorAttach(session, target);
17074
17240
  };
17075
17241
  const handleSourceDetach = (reason) => {
@@ -17097,7 +17263,18 @@ var createWatcherHandle = async (options, watcherId) => {
17097
17263
  indicatorController?.setRecording(recording);
17098
17264
  }
17099
17265
  });
17100
- netMockController.bind(sourceHandle.pageSession ?? sourceHandle.session);
17266
+ netMockController.bind({
17267
+ pageSession: sourceHandle.pageSession ?? sourceHandle.session,
17268
+ getSelectedTarget: () => {
17269
+ const context = sourceHandle.getNetFilterContext?.() ?? null;
17270
+ const frameId = context?.selectedFrameId ?? null;
17271
+ return {
17272
+ frameId,
17273
+ topFrameId: context?.topFrameId ?? null,
17274
+ sessionId: frameId ? sourceHandle.getFrameSessionId?.(frameId) ?? null : null
17275
+ };
17276
+ }
17277
+ });
17101
17278
  const dialogSession = sourceHandle.pageSession ?? sourceHandle.session;
17102
17279
  dialogSession.onEvent("Page.javascriptDialogOpening", (params) => {
17103
17280
  const dialog = parseDialogStatus(params);
@@ -22101,6 +22278,10 @@ var buildAddRequest = async (options, output) => {
22101
22278
  if (!action) {
22102
22279
  return null;
22103
22280
  }
22281
+ const scope = resolveMockScope(options, output);
22282
+ if (!scope) {
22283
+ return null;
22284
+ }
22104
22285
  const match = { url: urlPattern };
22105
22286
  if (options.method) {
22106
22287
  match.method = options.method;
@@ -22108,7 +22289,21 @@ var buildAddRequest = async (options, output) => {
22108
22289
  if (options.resourceType) {
22109
22290
  match.resourceType = options.resourceType;
22110
22291
  }
22111
- return { match, action, delayMs, times };
22292
+ return { scope, match, action, delayMs, times };
22293
+ };
22294
+ var resolveMockScope = (options, output) => {
22295
+ if (options.scope && options.frame) {
22296
+ output.writeWarn("Cannot combine --scope and --frame. Use one or the other.");
22297
+ process.exitCode = 2;
22298
+ return null;
22299
+ }
22300
+ const value = (options.scope ?? options.frame ?? "page").trim().toLowerCase();
22301
+ if (value === "page" || value === "selected") {
22302
+ return value;
22303
+ }
22304
+ output.writeWarn(`Invalid mock scope: ${value}. Expected page or selected.`);
22305
+ process.exitCode = 2;
22306
+ return null;
22112
22307
  };
22113
22308
  var buildAction = async (options, responseHeaders, requestHeaders, delayMs, output) => {
22114
22309
  const invalid = (message) => {
@@ -22296,6 +22491,9 @@ var describeRule = (rule) => {
22296
22491
  if (rule.times != null) {
22297
22492
  extras.push(`times ${Math.min(rule.hits, rule.times)}/${rule.times}`);
22298
22493
  }
22494
+ if (rule.scope === "selected") {
22495
+ extras.push("scope selected");
22496
+ }
22299
22497
  extras.push(`hits ${rule.hits}`);
22300
22498
  return `#${rule.id} ${match} → ${describeAction(rule.action)} [${extras.join(", ")}]`;
22301
22499
  };
@@ -22415,6 +22613,8 @@ var netMockCommand = {
22415
22613
  { flags: "--url <pattern>", description: "URL wildcard pattern; substring match when it contains no *" },
22416
22614
  { flags: "--method <method>", description: "Only match this HTTP method" },
22417
22615
  { flags: "--resource-type <type>", description: "Only match this CDP resource type (Fetch, XHR, Document, ...)" },
22616
+ { flags: "--scope <scope>", description: "Intercept the top-level page or currently selected target (page|selected)" },
22617
+ { flags: "--frame <frame>", description: "Alias for --scope; accepts page or selected" },
22418
22618
  { flags: "--block", description: "Abort matching requests as BlockedByClient" },
22419
22619
  { flags: "--fail <reason>", description: "Abort with a network error (TimedOut, ConnectionRefused, ...)" },
22420
22620
  { flags: "--status <code>", description: "Stub a response with this HTTP status (default 200 when a body is given)" },
@@ -22441,12 +22641,13 @@ var netMockCommand = {
22441
22641
  'argus net mock add app --url "*/analytics/*" --block',
22442
22642
  'argus net mock add app --url "*/api/save" --fail ConnectionRefused --times 1',
22443
22643
  'argus net mock add app --url "*/api/config" --status 200 --body-file ./fixtures/config.json',
22644
+ 'argus net mock add extension --scope selected --url "*/api/config" --status 200 --body-file ./fixtures/config.json',
22444
22645
  `argus net mock add app --url "*/game/init" --status 500 --body '{"error":"maintenance"}'`,
22445
22646
  'argus net mock add app --url "*/api/*" --delay 2s --method POST',
22446
22647
  'argus net mock add app --url "cdn.prod.com" --rewrite-host localhost:3000'
22447
22648
  ],
22448
22649
  action: async (id, options) => {
22449
- await runNetMockAdd(id, options);
22650
+ await runNetMockAdd(id, resolveNetMockAddOptions(options));
22450
22651
  }
22451
22652
  },
22452
22653
  {
@@ -22457,7 +22658,7 @@ var netMockCommand = {
22457
22658
  options: [jsonOption],
22458
22659
  examples: ["argus net mock ls app", "argus net mock ls app --json"],
22459
22660
  action: async (id, options) => {
22460
- await runNetMockList(id, options);
22661
+ await runNetMockList(id, resolveCommandOptions(options));
22461
22662
  }
22462
22663
  },
22463
22664
  {
@@ -22471,7 +22672,7 @@ var netMockCommand = {
22471
22672
  options: [jsonOption],
22472
22673
  examples: ["argus net mock rm 2 app", "argus net mock rm 2 app --json"],
22473
22674
  action: async (rule, id, options) => {
22474
- await runNetMockRemove(id, rule, options);
22675
+ await runNetMockRemove(id, rule, resolveCommandOptions(options));
22475
22676
  }
22476
22677
  },
22477
22678
  {
@@ -22481,7 +22682,7 @@ var netMockCommand = {
22481
22682
  options: [jsonOption],
22482
22683
  examples: ["argus net mock clear app", "argus net mock clear app --json"],
22483
22684
  action: async (id, options) => {
22484
- await runNetMockClear(id, options);
22685
+ await runNetMockClear(id, resolveCommandOptions(options));
22485
22686
  }
22486
22687
  }
22487
22688
  ]
@@ -22704,8 +22905,8 @@ Examples:
22704
22905
  ]
22705
22906
  }
22706
22907
  ];
22707
- var resolveCommandOptions = (value) => {
22708
- const fallback = parseNetArgv(process.argv.slice(2));
22908
+ var resolveCommandOptions = (value, argv = process.argv.slice(2)) => {
22909
+ const fallback = parseNetArgv(argv);
22709
22910
  if (value && typeof value === "object" && "opts" in value && typeof value.opts === "function") {
22710
22911
  return {
22711
22912
  ...value.opts(),
@@ -22717,6 +22918,16 @@ var resolveCommandOptions = (value) => {
22717
22918
  ...fallback
22718
22919
  };
22719
22920
  };
22921
+ var resolveNetMockAddOptions = (value, argv = process.argv.slice(2)) => {
22922
+ const resolved = resolveCommandOptions(value, argv);
22923
+ for (const key of ["method", "status", "resourceType"]) {
22924
+ const option = resolved[key];
22925
+ if (Array.isArray(option)) {
22926
+ resolved[key] = option.at(-1);
22927
+ }
22928
+ }
22929
+ return resolved;
22930
+ };
22720
22931
  var parseNetArgv = (argv) => {
22721
22932
  const parsed = {};
22722
22933
  const readValue = (flag) => {
@@ -47417,10 +47628,34 @@ var domCommands = [
47417
47628
  ];
47418
47629
 
47419
47630
  // dist/commands/domKeydown.js
47631
+ var normalizeOptionalString2 = (value) => {
47632
+ const normalized = value?.trim();
47633
+ return normalized ? normalized : undefined;
47634
+ };
47635
+ var mergeModifierOptions = (options) => {
47636
+ const modifiers = new Set;
47637
+ for (const part of (options.modifiers ?? "").split(",")) {
47638
+ const name = part.trim().toLowerCase();
47639
+ if (name) {
47640
+ modifiers.add(name);
47641
+ }
47642
+ }
47643
+ if (options.shift)
47644
+ modifiers.add("shift");
47645
+ if (options.ctrl)
47646
+ modifiers.add("ctrl");
47647
+ if (options.alt)
47648
+ modifiers.add("alt");
47649
+ if (options.meta || options.cmd)
47650
+ modifiers.add("meta");
47651
+ return modifiers.size > 0 ? Array.from(modifiers).join(",") : undefined;
47652
+ };
47420
47653
  var runDomKeydown = defineWatcherCommand({
47421
47654
  build: (_args, options, output) => {
47422
- if (!options.key || options.key.trim() === "") {
47423
- output.writeWarn("--key is required");
47655
+ const key = normalizeOptionalString2(options.key);
47656
+ const code = normalizeOptionalString2(options.code);
47657
+ if (!key && !code) {
47658
+ output.writeWarn("--key or --code is required");
47424
47659
  process.exitCode = 2;
47425
47660
  return null;
47426
47661
  }
@@ -47428,15 +47663,20 @@ var runDomKeydown = defineWatcherCommand({
47428
47663
  path: "/dom/keydown",
47429
47664
  method: "POST",
47430
47665
  body: {
47431
- key: options.key,
47666
+ key,
47667
+ code,
47432
47668
  selector: options.selector,
47433
- modifiers: options.modifiers
47669
+ modifiers: mergeModifierOptions(options)
47434
47670
  },
47435
47671
  timeoutMs: 30000
47436
47672
  };
47437
47673
  },
47438
- formatHuman: (response, { output }) => {
47439
- output.writeHuman(`Dispatched keydown: ${response.key}`);
47674
+ formatHuman: (response, { options, output }) => {
47675
+ if (options.printEvent) {
47676
+ output.writeHuman(`Dispatched keydown event: ${JSON.stringify(response.event)}`);
47677
+ return;
47678
+ }
47679
+ output.writeHuman(`Dispatched keydown: ${response.key} (code=${response.code})`);
47440
47680
  }
47441
47681
  });
47442
47682
 
@@ -47446,13 +47686,26 @@ var keydownCommand = {
47446
47686
  description: "Dispatch a keyboard event to the connected page",
47447
47687
  arguments: [{ flags: "[id]", description: "Watcher id to query" }],
47448
47688
  options: [
47449
- { flags: "--key <name>", description: "Key name (e.g. Enter, a, ArrowUp)", required: true },
47689
+ { flags: "--key <name>", description: "KeyboardEvent.key value (e.g. Enter, a, ArrowUp)" },
47690
+ { flags: "--code <code>", description: "KeyboardEvent.code value (e.g. KeyG, Digit1)" },
47450
47691
  { flags: "--selector <css>", description: "Focus element before dispatching" },
47451
47692
  { flags: "--testid <id>", description: `Shorthand for --selector "[data-testid='<id>']"` },
47452
47693
  { flags: "--modifiers <list>", description: "Comma-separated modifiers: shift,ctrl,alt,meta" },
47694
+ { flags: "--shift", description: "Shortcut for --modifiers shift" },
47695
+ { flags: "--ctrl", description: "Shortcut for --modifiers ctrl" },
47696
+ { flags: "--alt", description: "Shortcut for --modifiers alt" },
47697
+ { flags: "--meta", description: "Shortcut for --modifiers meta" },
47698
+ { flags: "--cmd", description: "Alias for --meta" },
47699
+ { flags: "--print-event", description: "Print resolved key/code/modifier event details" },
47453
47700
  { flags: "--json", description: "Output JSON for automation" }
47454
47701
  ],
47455
- examples: ["argus keydown app --key Enter", 'argus keydown app --key a --selector "#input"', "argus keydown app --key a --modifiers shift,ctrl"],
47702
+ examples: [
47703
+ "argus keydown app --key Enter",
47704
+ "argus keydown app --key G",
47705
+ "argus keydown app --code KeyG",
47706
+ 'argus keydown app --key a --selector "#input"',
47707
+ "argus keydown app --key a --shift --ctrl"
47708
+ ],
47456
47709
  action: async (id, options) => {
47457
47710
  if (!resolveTestId(options))
47458
47711
  return;
@@ -48715,7 +48968,7 @@ var extractPlugin = (mod) => {
48715
48968
  return null;
48716
48969
  return plugin;
48717
48970
  };
48718
- var normalizeOptionalString = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
48971
+ var normalizeOptionalString3 = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
48719
48972
  var normalizeCommands = (value) => {
48720
48973
  if (!Array.isArray(value))
48721
48974
  return [];
@@ -48737,11 +48990,11 @@ var createLoadedEntry = (entry, plugin, url) => ({
48737
48990
  alias: entry.alias,
48738
48991
  status: "loaded",
48739
48992
  name: plugin.name,
48740
- version: normalizeOptionalString(plugin.version),
48741
- description: normalizeOptionalString(plugin.description),
48993
+ version: normalizeOptionalString3(plugin.version),
48994
+ description: normalizeOptionalString3(plugin.description),
48742
48995
  commands: normalizeCommands(plugin.commands),
48743
- homepage: normalizeOptionalString(plugin.homepage),
48744
- minArgusVersion: normalizeOptionalString(plugin.minArgusVersion),
48996
+ homepage: normalizeOptionalString3(plugin.homepage),
48997
+ minArgusVersion: normalizeOptionalString3(plugin.minArgusVersion),
48745
48998
  url
48746
48999
  });
48747
49000
  var registerPlugins = async (program2, argv = process.argv.slice(2)) => {