@jobshimo/browser-link 0.34.1 → 0.36.0

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 (38) hide show
  1. package/dist/agent-instructions/content.js +19 -6
  2. package/dist/agent-instructions/content.js.map +1 -1
  3. package/dist/bridge/events.d.ts +1 -1
  4. package/dist/bridge/events.js +8 -1
  5. package/dist/bridge/events.js.map +1 -1
  6. package/dist/bridge/ws-bridge.d.ts +17 -0
  7. package/dist/bridge/ws-bridge.js +34 -0
  8. package/dist/bridge/ws-bridge.js.map +1 -1
  9. package/dist/cdp/inpage/builders.d.ts +70 -3
  10. package/dist/cdp/inpage/builders.js +197 -36
  11. package/dist/cdp/inpage/builders.js.map +1 -1
  12. package/dist/cdp/inpage/observer.d.ts +77 -0
  13. package/dist/cdp/inpage/observer.js +509 -0
  14. package/dist/cdp/inpage/observer.js.map +1 -0
  15. package/dist/cdp/observer-host.d.ts +137 -0
  16. package/dist/cdp/observer-host.js +188 -0
  17. package/dist/cdp/observer-host.js.map +1 -0
  18. package/dist/cdp/transport.js +161 -25
  19. package/dist/cdp/transport.js.map +1 -1
  20. package/dist/extension/background.js +183 -33
  21. package/dist/extension/background.js.map +1 -1
  22. package/dist/extension/inpage/builders.d.ts +70 -3
  23. package/dist/extension/inpage/builders.js +196 -35
  24. package/dist/extension/inpage/builders.js.map +1 -1
  25. package/dist/extension/inpage/observer.d.ts +88 -0
  26. package/dist/extension/inpage/observer.js +520 -0
  27. package/dist/extension/inpage/observer.js.map +1 -0
  28. package/dist/extension/manifest.json +1 -1
  29. package/dist/extension/observer-host.d.ts +84 -0
  30. package/dist/extension/observer-host.js +172 -0
  31. package/dist/extension/observer-host.js.map +1 -0
  32. package/dist/tools/browser-definitions.js +16 -6
  33. package/dist/tools/browser-definitions.js.map +1 -1
  34. package/dist/tools/browser-dispatch.js +14 -2
  35. package/dist/tools/browser-dispatch.js.map +1 -1
  36. package/dist/tools/server-instructions.js +19 -5
  37. package/dist/tools/server-instructions.js.map +1 -1
  38. package/package.json +1 -1
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * DELIBERATELY A SUBSET: only the builders `cdp/transport.ts` needs for
7
7
  * cdp-direct's v1 supported tool set (snapshot, find, click, type, press,
8
- * state, plus the shared settle wait) are copied. `buildDragProbeJs` /
8
+ * state, wait_for, plus the shared settle wait) are copied. `buildDragProbeJs` /
9
9
  * `DragProbeOpts` are NOT copied — `browser.drag` is explicitly out of
10
10
  * v1 scope for cdp-direct (see the README's cdp-direct tool-support table
11
11
  * and `cdp/transport.ts`'s `CDP_TOOL_SUPPORT`). If drag is ever added to
@@ -22,6 +22,52 @@
22
22
  */
23
23
  import { DEEP_QUERY_JS } from './deep-query.js';
24
24
  import { DOM_HELPERS_JS } from './dom-helpers.js';
25
+ import { OBSERVER_GLOBAL } from './observer.js';
26
+ /**
27
+ * In-page access to the page observer's element identity (spec v4 §5.6),
28
+ * injected verbatim into `buildSnapshotJs`, `buildFindJs` and `buildStateJs`.
29
+ * The observer global is looked up ONCE per evaluation and used only when it
30
+ * is present, not torn down, and exposes `refFor`:
31
+ * - `refOf(el)` returns the observer's `e<N>` ref for `el`, or `null`;
32
+ * - `withDocId(result)` adds `doc_id` to `result` and returns it;
33
+ * - `snapshotStore()` returns the observer when it keeps snapshot records
34
+ * (`recordSnapshot` / `getSnapshot`, used by `snapshot({ since })`), else
35
+ * `null`.
36
+ * Without the observer (injection failed, pre-observer document) all are
37
+ * no-ops, so every result is byte-identical to the pre-ref output.
38
+ */
39
+ export const PAGE_OBSERVER_JS = `
40
+ const pageObserver = (() => {
41
+ try {
42
+ const g = window[${JSON.stringify(OBSERVER_GLOBAL)}];
43
+ if (g && !g.dead && typeof g.refFor === 'function') return g;
44
+ } catch (e) {}
45
+ return null;
46
+ })();
47
+ function refOf(el) {
48
+ if (!pageObserver) return null;
49
+ try {
50
+ const ref = pageObserver.refFor(el);
51
+ return typeof ref === 'string' ? ref : null;
52
+ } catch (e) {
53
+ return null;
54
+ }
55
+ }
56
+ function withDocId(result) {
57
+ if (pageObserver && typeof pageObserver.doc_id === 'string') result.doc_id = pageObserver.doc_id;
58
+ return result;
59
+ }
60
+ function snapshotStore() {
61
+ if (
62
+ pageObserver &&
63
+ typeof pageObserver.recordSnapshot === 'function' &&
64
+ typeof pageObserver.getSnapshot === 'function'
65
+ ) {
66
+ return pageObserver;
67
+ }
68
+ return null;
69
+ }
70
+ `;
25
71
  export function buildSnapshotJs(opts = {}) {
26
72
  const optsJson = JSON.stringify({
27
73
  withinSelector: typeof opts.within_selector === 'string' ? opts.within_selector : null,
@@ -30,23 +76,25 @@ export function buildSnapshotJs(opts = {}) {
30
76
  maxInteractive: typeof opts.max_interactive === 'number' && opts.max_interactive > 0
31
77
  ? Math.min(opts.max_interactive, 500)
32
78
  : 120,
79
+ since: typeof opts.since === 'string' ? opts.since : null,
33
80
  });
34
81
  return `
35
82
  (() => {
36
83
  ${DEEP_QUERY_JS}
37
84
  ${DOM_HELPERS_JS}
85
+ ${PAGE_OBSERVER_JS}
38
86
  const opts = ${optsJson};
39
87
  let root = document;
40
88
  let notice = '';
41
89
  if (opts.withinSelector) {
42
90
  const sub = deepQueryFirst(opts.withinSelector);
43
91
  if (!sub) {
44
- return {
92
+ return withDocId({
45
93
  title: document.title,
46
94
  url: location.href,
47
95
  interactive: [],
48
96
  notice: 'within_selector did not match any element',
49
- };
97
+ });
50
98
  }
51
99
  root = sub;
52
100
  }
@@ -89,6 +137,8 @@ export function buildSnapshotJs(opts = {}) {
89
137
  const role = el.getAttribute('role') || tag;
90
138
  const selInfo = genSelectorInfo(el);
91
139
  const entry = { tag, role, selector: selInfo.selector };
140
+ const ref = refOf(el);
141
+ if (ref) entry.ref = ref;
92
142
  if (selInfo.ambiguous) entry.ambiguous = true;
93
143
  const txt = shortText(el);
94
144
  if (txt) entry.text = txt;
@@ -108,26 +158,105 @@ export function buildSnapshotJs(opts = {}) {
108
158
  if (frame) entry.frame = frame;
109
159
  interactive.push(entry);
110
160
  });
111
- const result = {
112
- title: document.title,
113
- url: location.href,
114
- interactive: interactive.slice(0, opts.maxInteractive),
115
- };
161
+ let headings = null;
162
+ let visibleText = '';
116
163
  if (!opts.onlyInteractive) {
117
- const headings = [];
164
+ const found = [];
118
165
  deepQueryAll('h1, h2, h3', root).forEach((h) => {
119
166
  if (!isVisible(h)) return;
120
167
  if (inExcludedLandmark(h)) return;
121
168
  const t = shortText(h);
122
- if (t) headings.push({ level: h.tagName, text: t });
169
+ if (t) found.push({ level: h.tagName, text: t });
123
170
  });
124
- result.headings = headings.slice(0, 30);
171
+ headings = found.slice(0, 30);
125
172
  const textRoot = (root === document) ? (document.body || document) : root;
126
- const visibleText = (textRoot && textRoot.innerText) ? textRoot.innerText.slice(0, 4000) : '';
173
+ visibleText = (textRoot && textRoot.innerText) ? textRoot.innerText.slice(0, 4000) : '';
174
+ }
175
+ let snapshotId = null;
176
+ if (withDocId({}).doc_id && typeof pageObserver.nextSnapshotId === 'function') {
177
+ const issued = pageObserver.nextSnapshotId();
178
+ if (typeof issued === 'string') snapshotId = issued;
179
+ }
180
+ function fullResult() {
181
+ const result = {
182
+ title: document.title,
183
+ url: location.href,
184
+ interactive: interactive.slice(0, opts.maxInteractive),
185
+ };
186
+ if (headings) result.headings = headings;
127
187
  if (visibleText) result.text = visibleText;
188
+ if (notice) result.notice = notice;
189
+ withDocId(result);
190
+ if (snapshotId) result.snapshot_id = snapshotId;
191
+ return result;
128
192
  }
129
- if (notice) result.notice = notice;
130
- return result;
193
+ // Record every snapshot (with or without since) so a later call can diff
194
+ // against it. Entries are keyed by ref; the hash is the entry serialised
195
+ // without its ref, in emission order. Capped at the first 500 entries.
196
+ const store = snapshotId ? snapshotStore() : null;
197
+ // A snapshot_id is \`<doc_id>:s<N>\`: a since whose prefix (before the LAST
198
+ // ':') is not this document's doc_id, or that has no ':', came from another
199
+ // document and is never looked up.
200
+ const sinceCut = opts.since === null ? -1 : opts.since.lastIndexOf(':');
201
+ const sinceSameDoc =
202
+ sinceCut >= 0 && withDocId({}).doc_id === opts.since.slice(0, sinceCut);
203
+ const previous = store && sinceSameDoc ? store.getSnapshot(opts.since) : null;
204
+ const current = new Map();
205
+ const headingsHash = headings ? JSON.stringify(headings) : null;
206
+ const textHash = opts.onlyInteractive ? null : visibleText;
207
+ if (store) {
208
+ for (const entry of interactive) {
209
+ if (current.size >= 500) break;
210
+ if (typeof entry.ref !== 'string') continue;
211
+ const copy = {};
212
+ for (const key of Object.keys(entry)) if (key !== 'ref') copy[key] = entry[key];
213
+ current.set(entry.ref, { hash: JSON.stringify(copy), entry });
214
+ }
215
+ store.recordSnapshot(snapshotId, { entries: current, headings_hash: headingsHash, text_hash: textHash });
216
+ }
217
+ if (opts.since === null) return fullResult();
218
+ if (!store) {
219
+ const result = fullResult();
220
+ result.since = opts.since;
221
+ result.since_status = 'observer-unavailable';
222
+ return result;
223
+ }
224
+ if (!previous || !previous.entries || typeof previous.entries.get !== 'function') {
225
+ const result = fullResult();
226
+ result.since = opts.since;
227
+ result.since_status = sinceSameDoc ? 'evicted' : 'doc-changed';
228
+ return result;
229
+ }
230
+ const added = [];
231
+ const changed = [];
232
+ const removed = [];
233
+ current.forEach((item, ref) => {
234
+ const old = previous.entries.get(ref);
235
+ if (!old) added.push(item.entry);
236
+ else if (old.hash !== item.hash) changed.push(item.entry);
237
+ });
238
+ previous.entries.forEach((item, ref) => {
239
+ if (current.has(ref) || removed.length >= 500) return;
240
+ const gone = { ref, tag: item.entry.tag };
241
+ if (item.entry.text) gone.text = item.entry.text;
242
+ removed.push(gone);
243
+ });
244
+ // max_interactive applies after diffing (added first, then changed), so
245
+ // a window shift never masquerades as change.
246
+ const listed = added.concat(changed);
247
+ const kept = listed.slice(0, opts.maxInteractive);
248
+ const diff = withDocId({ title: document.title, url: location.href });
249
+ diff.snapshot_id = snapshotId;
250
+ diff.since = opts.since;
251
+ diff.added = kept.slice(0, Math.min(added.length, kept.length));
252
+ diff.removed = removed;
253
+ diff.changed = kept.slice(diff.added.length);
254
+ if (listed.length > kept.length) diff.overflow = listed.length - kept.length;
255
+ if (!opts.onlyInteractive) {
256
+ if (headingsHash !== previous.headings_hash) diff.headings = headings;
257
+ if (textHash !== previous.text_hash) diff.text = visibleText;
258
+ }
259
+ return diff;
131
260
  })()
132
261
  `;
133
262
  }
@@ -143,6 +272,7 @@ export function buildFindJs(opts) {
143
272
  (() => {
144
273
  ${DEEP_QUERY_JS}
145
274
  ${DOM_HELPERS_JS}
275
+ ${PAGE_OBSERVER_JS}
146
276
  const opts = ${optsJson};
147
277
  const needle = opts.text.toLowerCase();
148
278
  const needleTokens = needle.split(/[^a-z0-9]+/).filter(Boolean);
@@ -203,6 +333,8 @@ export function buildFindJs(opts) {
203
333
  }
204
334
  function toNearMiss(el) {
205
335
  const entry = { text: snippetOf(el), selector: genSelector(el) };
336
+ const ref = refOf(el);
337
+ if (ref) entry.ref = ref;
206
338
  const roleAttr = el.getAttribute('role');
207
339
  if (roleAttr) entry.role = roleAttr;
208
340
  return entry;
@@ -235,44 +367,46 @@ export function buildFindJs(opts) {
235
367
  ' but none with role "' + opts.role + '"' +
236
368
  (closest ? ' — closest: ' + describeForError(closest) + ' "' + snippetOf(closest) + '"' : '');
237
369
  result.near_misses = ranked.map(toNearMiss);
238
- return result;
370
+ return withDocId(result);
239
371
  }
240
372
  const ranked = rankCandidates(broadAll);
241
373
  if (ranked.length > 0) result.near_misses = ranked.map(toNearMiss);
242
- return result;
374
+ return withDocId(result);
243
375
  }
244
376
  const ranked = rankCandidates(all);
245
377
  if (ranked.length > 0) result.near_misses = ranked.map(toNearMiss);
246
- return result;
378
+ return withDocId(result);
247
379
  }
248
380
  if (matches.length > 1) {
249
- return {
381
+ return withDocId({
250
382
  matched: false,
251
383
  reason: 'multiple-matches',
252
- candidates: matches.slice(0, opts.candidateLimit).map((el) => ({
253
- selector: genSelector(el),
254
- text: shortText(el),
255
- tag: el.tagName.toLowerCase(),
256
- })),
257
- };
384
+ candidates: matches.slice(0, opts.candidateLimit).map((el) => {
385
+ const candidate = { selector: genSelector(el) };
386
+ const ref = refOf(el);
387
+ if (ref) candidate.ref = ref;
388
+ candidate.text = shortText(el);
389
+ candidate.tag = el.tagName.toLowerCase();
390
+ return candidate;
391
+ }),
392
+ });
258
393
  }
259
394
  const el = matches[0];
260
395
  const center = viewportCenterOf(el);
261
396
  const selInfo = genSelectorInfo(el);
262
- const result = {
263
- matched: true,
264
- selector: selInfo.selector,
265
- coords: {
266
- x: Math.round(center.x),
267
- y: Math.round(center.y),
268
- },
269
- tag: el.tagName.toLowerCase(),
270
- text: shortText(el),
397
+ const result = { matched: true, selector: selInfo.selector };
398
+ const ref = refOf(el);
399
+ if (ref) result.ref = ref;
400
+ result.coords = {
401
+ x: Math.round(center.x),
402
+ y: Math.round(center.y),
271
403
  };
404
+ result.tag = el.tagName.toLowerCase();
405
+ result.text = shortText(el);
272
406
  if (selInfo.ambiguous) result.ambiguous = true;
273
407
  const frame = frameSelectorFor(el);
274
408
  if (frame) result.frame = frame;
275
- return result;
409
+ return withDocId(result);
276
410
  })()
277
411
  `;
278
412
  }
@@ -337,6 +471,26 @@ export function buildFocusJs(opts) {
337
471
  return true;
338
472
  })()`;
339
473
  }
474
+ export function buildWaitForSelectorJs(selector, condition) {
475
+ const optsJson = JSON.stringify({ selector, condition });
476
+ return `
477
+ (() => {
478
+ ${DEEP_QUERY_JS}
479
+ const opts = ${optsJson};
480
+ document.querySelector(opts.selector);
481
+ const el = deepQueryFirst(opts.selector);
482
+ if (opts.condition === 'attached') return Boolean(el);
483
+ if (opts.condition === 'detached') return !el;
484
+ const wantHidden = opts.condition === 'hidden';
485
+ if (!el) return wantHidden;
486
+ const view = (el.ownerDocument && el.ownerDocument.defaultView) || window;
487
+ const s = view.getComputedStyle(el);
488
+ if (s.display === 'none' || s.visibility === 'hidden' || s.opacity === '0') return wantHidden;
489
+ const r = el.getBoundingClientRect();
490
+ const shown = r.width > 0 && r.height > 0;
491
+ return wantHidden ? !shown : shown;
492
+ })()`;
493
+ }
340
494
  export function buildElementRectJs(opts) {
341
495
  const optsJson = JSON.stringify({ selector: opts.selector });
342
496
  return `
@@ -549,12 +703,16 @@ export function buildExpectProbeJs(opts) {
549
703
  * `focused` beyond `<body>`, no `dialogs` when none are open, and no
550
704
  * `scroll` when the page is at its default (0,0) position — token-lean by
551
705
  * construction, matching the omit-falsy convention the other builders use.
706
+ *
707
+ * With the page observer installed (see `PAGE_OBSERVER_JS`), `focused`
708
+ * carries `ref` right after `selector` and the result carries `doc_id`.
552
709
  */
553
710
  export function buildStateJs() {
554
711
  return `
555
712
  (() => {
556
713
  ${DEEP_QUERY_JS}
557
714
  ${DOM_HELPERS_JS}
715
+ ${PAGE_OBSERVER_JS}
558
716
  function deepActiveElement() {
559
717
  let el = document.activeElement;
560
718
  let guard = 0;
@@ -615,7 +773,10 @@ export function buildStateJs() {
615
773
  const active = deepActiveElement();
616
774
  if (active && active !== document.body && active.tagName) {
617
775
  const selInfo = genSelectorInfo(active);
618
- const focused = { selector: selInfo.selector, tag: active.tagName.toLowerCase() };
776
+ const focused = { selector: selInfo.selector };
777
+ const ref = refOf(active);
778
+ if (ref) focused.ref = ref;
779
+ focused.tag = active.tagName.toLowerCase();
619
780
  if (selInfo.ambiguous) focused.ambiguous = true;
620
781
  result.focused = focused;
621
782
  }
@@ -635,7 +796,7 @@ export function buildStateJs() {
635
796
  const scrollY = Math.round(window.scrollY);
636
797
  if (scrollX !== 0 || scrollY !== 0) result.scroll = { x: scrollX, y: scrollY };
637
798
 
638
- return result;
799
+ return withDocId(result);
639
800
  })()
640
801
  `;
641
802
  }
@@ -1 +1 @@
1
- {"version":3,"file":"builders.js","sourceRoot":"","sources":["../../../src/cdp/inpage/builders.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"builders.js","sourceRoot":"","sources":["../../../src/cdp/inpage/builders.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Independent copy of `packages/extension/src/inpage/observer.ts` — see
3
+ * `./deep-query.ts`'s header comment for why this is a verbatim duplicate
4
+ * rather than a cross-package import.
5
+ *
6
+ * The page observer: a persistent, coalescing MutationObserver that runs in
7
+ * the page's MAIN world (spec v4 §5.1–§5.3). cdp-direct installs it with the
8
+ * same `Runtime.addBinding` + `Page.addScriptToEvaluateOnNewDocument` pair as
9
+ * the extension. Everything below this header must stay byte-identical to the
10
+ * extension original — `cdp/drift.test.ts` enforces it and
11
+ * `cdp/builders-parity.test.ts` checks the built output.
12
+ */
13
+ /** Bump whenever the in-page protocol (global shape, messages, methods) changes. */
14
+ export declare const OBSERVER_VERSION = 2;
15
+ /** Name of the `Runtime.addBinding` function the observer pushes messages through. */
16
+ export declare const OBSERVER_BINDING = "__blPageObserverPush";
17
+ /** Well-known global that holds the installed observer instance. */
18
+ export declare const OBSERVER_GLOBAL = "__blPageObserver";
19
+ export interface ObserverInstallOpts {
20
+ /** Binding name to push through; defaults to `OBSERVER_BINDING`. */
21
+ binding?: string;
22
+ /** Instance version; defaults to `OBSERVER_VERSION`. */
23
+ version?: number;
24
+ /** Max target descriptors per summary (default 20, clamped to 1..100). */
25
+ descriptor_cap?: number;
26
+ /** Summaries kept for mark/changes read-back (default 64, clamped to 1..1024). */
27
+ ring_size?: number;
28
+ }
29
+ /**
30
+ * Build the observer install source. Valid both as a
31
+ * `Page.addScriptToEvaluateOnNewDocument` source and as a one-off
32
+ * `Runtime.evaluate` expression, which evaluates to
33
+ * `{ ok: true, installed, version, doc_id, coverage }`.
34
+ *
35
+ * The instance also owns element identity (spec v4 §5.6): `refFor(el)` issues
36
+ * `e<N>` (monotonic per document instance, from `e1`) the first time an
37
+ * element is seen and reuses it while the node lives; `resolveRef(ref)`
38
+ * returns the element while it is connected, else `null`. Refs are only valid
39
+ * for the `doc_id` that issued them. Neither method throws. `nextSnapshotId()`
40
+ * issues `<doc_id>:s<N>` (N monotonic per document instance, from 1) for each
41
+ * `snapshot` result and returns `null` once the instance is torn down; the
42
+ * `doc_id` prefix lets a stale id from another document be rejected.
43
+ * `recordSnapshot(id, record)` keeps the last 4 snapshot records per document
44
+ * instance (oldest evicted first) for `snapshot({ since })`; `getSnapshot(id)`
45
+ * returns the record or `null` when unknown or evicted. The record is opaque
46
+ * here. Both are inert after teardown, which clears the store.
47
+ */
48
+ export declare function buildObserverInstallJs(opts?: ObserverInstallOpts): string;
49
+ /**
50
+ * Build the action-mark expression. Forces a flush (including records still
51
+ * queued in the observers) so the mark is exact, then baselines URL and focus.
52
+ * Evaluates to `{ ok: true, doc_id, mark, url, has_focus_baseline: true }` or
53
+ * `{ ok: false, reason: 'observer-unavailable' }`.
54
+ */
55
+ export declare function buildObserverMarkJs(): string;
56
+ /**
57
+ * Build the "changes since mark" expression. Forces a flush, then aggregates
58
+ * the ring summaries with `seq > mark`. `docId`, when given, must match the
59
+ * current document. Evaluates to the aggregated changes (see
60
+ * `SettleChanges` in `@browser-link/shared`) plus `ok`, `doc_id`, `since`,
61
+ * and `url_changed` / `focus_moved` only when they differ from the mark's
62
+ * baseline; or `{ ok: false, reason: 'observer-unavailable' | 'doc-changed' }`.
63
+ */
64
+ export declare function buildObserverChangesJs(mark: number, docId?: string): string;
65
+ /**
66
+ * Build the ref-resolve expression, for tests and debugging. Evaluates to
67
+ * `{ ok: true, found: true, tag }` when `ref` names an element of the current
68
+ * document that is still connected, `{ ok: true, found: false }` when it is
69
+ * unknown or stale, or `{ ok: false, reason: 'observer-unavailable' }`.
70
+ */
71
+ export declare function buildObserverRefResolveJs(ref: string): string;
72
+ /**
73
+ * Build the teardown expression: disconnects every observer, restores the
74
+ * `attachShadow` and `history` patches when they are still the observer's,
75
+ * and removes the global. Idempotent; evaluates to `{ ok: true, removed }`.
76
+ */
77
+ export declare function buildObserverTeardownJs(): string;