@jobshimo/browser-link 0.35.0 → 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.
@@ -170,6 +170,16 @@ function body() {
170
170
  ' Filters are applied IN-page so the dropped material never travels back.',
171
171
  ' Pass `exclude:["nav","footer"]` to skip repeated landmarks; pass',
172
172
  ' `within_selector` when only one region matters.',
173
+ '- Looking at the same view again → take ONE full `browser.snapshot` per',
174
+ ' view, keep its `snapshot_id`, and pass it as `since` for every later',
175
+ ' look: `browser.snapshot({ since })` returns only `added` / `removed` /',
176
+ ' `changed` entries instead of the whole page. On `since_status:',
177
+ ' "doc-changed"` (or `evicted`, `observer-unavailable`) you already got',
178
+ ' the full snapshot back: keep its new `snapshot_id`. A changed',
179
+ ' neighbour can be listed because its selector changed — use the current',
180
+ ' one. `ref` (`e<N>`) on entries is an element handle valid only for the',
181
+ ' `doc_id` that issued it; for now it is informational, not an action',
182
+ ' target.',
173
183
  '- Reading N values → ONE `browser.evaluate` returning an object:',
174
184
  ' `(() => ({ a: document.querySelector("#a").value, b: ... }))()`.',
175
185
  ' Never N separate evaluates for N reads.',
@@ -1 +1 @@
1
- {"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/agent-instructions/content.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/agent-instructions/content.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,17 @@
1
+ /**
2
+ * In-page access to the page observer's element identity (spec v4 §5.6),
3
+ * injected verbatim into `buildSnapshotJs`, `buildFindJs` and `buildStateJs`.
4
+ * The observer global is looked up ONCE per evaluation and used only when it
5
+ * is present, not torn down, and exposes `refFor`:
6
+ * - `refOf(el)` returns the observer's `e<N>` ref for `el`, or `null`;
7
+ * - `withDocId(result)` adds `doc_id` to `result` and returns it;
8
+ * - `snapshotStore()` returns the observer when it keeps snapshot records
9
+ * (`recordSnapshot` / `getSnapshot`, used by `snapshot({ since })`), else
10
+ * `null`.
11
+ * Without the observer (injection failed, pre-observer document) all are
12
+ * no-ops, so every result is byte-identical to the pre-ref output.
13
+ */
14
+ export declare const PAGE_OBSERVER_JS: string;
1
15
  /**
2
16
  * Build the snapshot expression. Filters are applied INSIDE the page so the
3
17
  * server never receives the dropped material at all — that's where the token
@@ -17,18 +31,50 @@
17
31
  * a computed `pointer-events: none` element can never receive a real click
18
32
  * (see the helper's comment in `dom-helpers.ts`). Headings and the text
19
33
  * dump keep plain `isVisible`, so such elements still contribute text.
34
+ *
35
+ * With the page observer installed (see `PAGE_OBSERVER_JS`), each
36
+ * interactive entry carries `ref` right after `selector`, and the result
37
+ * carries `doc_id` and `snapshot_id` (`<doc_id>:s<N>`, one per call). Without
38
+ * it the output is unchanged.
39
+ *
40
+ * Every snapshot taken with the observer is recorded by it (the last 4 per
41
+ * document; the first 500 entries keyed by ref, each with a hash = the entry
42
+ * serialised without `ref`). `since: <snapshot_id>` then answers with a diff
43
+ * against that record instead of the full list:
44
+ * { title, url, doc_id, snapshot_id, since, added, removed, changed,
45
+ * overflow?, headings?, text? }
46
+ * `added` are entries whose ref is new, `removed` are `{ ref, tag, text? }`
47
+ * of refs gone (capped at 500), `changed` are current entries whose hash
48
+ * differs. `max_interactive` applies AFTER diffing to `added` then `changed`;
49
+ * `overflow` counts what it dropped. `headings` / `text` appear only when
50
+ * `only_interactive` is false and they differ from the recorded ones.
51
+ * When the diff cannot be made the result is the full snapshot plus `since`
52
+ * and `since_status`:
53
+ * - `observer-unavailable`: no observer (no `doc_id` / `snapshot_id`);
54
+ * - `doc-changed`: `snapshot_id` is `<doc_id>:s<N>`, and the part of
55
+ * `since` before its LAST `:` is not the current `doc_id` (or `since` has
56
+ * no `:`), so it came from another document; the store is not consulted;
57
+ * - `evicted`: the prefix is the current `doc_id` but the id is not among
58
+ * the last 4 records (evicted, or never issued).
59
+ * A `within_selector` miss ignores `since` and keeps the miss shape (plus
60
+ * `doc_id`).
20
61
  */
21
62
  export interface SnapshotOpts {
22
63
  within_selector?: string;
23
64
  only_interactive?: boolean;
24
65
  exclude?: string[];
25
66
  max_interactive?: number;
67
+ /** `snapshot_id` of an earlier snapshot of this document to diff against. */
68
+ since?: string;
26
69
  }
27
70
  export declare function buildSnapshotJs(opts?: SnapshotOpts): string;
28
71
  /** Build the find-by-text expression. Returns one of:
29
- * { matched: true, selector, coords:{x,y}, tag, text, frame?, ambiguous? }
30
- * { matched: false, reason: 'not-found', error?, near_misses?: [{text,selector,role?}] }
31
- * { matched: false, reason: 'multiple-matches', candidates: [{selector,text,tag}] }
72
+ * { matched: true, selector, ref?, coords:{x,y}, tag, text, frame?, ambiguous?, doc_id? }
73
+ * { matched: false, reason: 'not-found', error?, near_misses?: [{text,selector,ref?,role?}], doc_id? }
74
+ * { matched: false, reason: 'multiple-matches', candidates: [{selector,ref?,text,tag}], doc_id? }
75
+ *
76
+ * `ref` and `doc_id` are present only when the page observer is installed
77
+ * (see `PAGE_OBSERVER_JS`).
32
78
  *
33
79
  * Role-aware: when `role` is provided, only elements whose explicit ARIA
34
80
  * role or implicit role match are considered. When omitted, the search
@@ -233,5 +279,8 @@ export declare function buildExpectProbeJs(opts: ExpectProbeOpts): string;
233
279
  * `focused` beyond `<body>`, no `dialogs` when none are open, and no
234
280
  * `scroll` when the page is at its default (0,0) position — token-lean by
235
281
  * construction, matching the omit-falsy convention the other builders use.
282
+ *
283
+ * With the page observer installed (see `PAGE_OBSERVER_JS`), `focused`
284
+ * carries `ref` right after `selector` and the result carries `doc_id`.
236
285
  */
237
286
  export declare function buildStateJs(): string;
@@ -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
  }
@@ -569,12 +703,16 @@ export function buildExpectProbeJs(opts) {
569
703
  * `focused` beyond `<body>`, no `dialogs` when none are open, and no
570
704
  * `scroll` when the page is at its default (0,0) position — token-lean by
571
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`.
572
709
  */
573
710
  export function buildStateJs() {
574
711
  return `
575
712
  (() => {
576
713
  ${DEEP_QUERY_JS}
577
714
  ${DOM_HELPERS_JS}
715
+ ${PAGE_OBSERVER_JS}
578
716
  function deepActiveElement() {
579
717
  let el = document.activeElement;
580
718
  let guard = 0;
@@ -635,7 +773,10 @@ export function buildStateJs() {
635
773
  const active = deepActiveElement();
636
774
  if (active && active !== document.body && active.tagName) {
637
775
  const selInfo = genSelectorInfo(active);
638
- 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();
639
780
  if (selInfo.ambiguous) focused.ambiguous = true;
640
781
  result.focused = focused;
641
782
  }
@@ -655,7 +796,7 @@ export function buildStateJs() {
655
796
  const scrollY = Math.round(window.scrollY);
656
797
  if (scrollX !== 0 || scrollY !== 0) result.scroll = { x: scrollX, y: scrollY };
657
798
 
658
- return result;
799
+ return withDocId(result);
659
800
  })()
660
801
  `;
661
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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"}
@@ -11,7 +11,7 @@
11
11
  * `cdp/builders-parity.test.ts` checks the built output.
12
12
  */
13
13
  /** Bump whenever the in-page protocol (global shape, messages, methods) changes. */
14
- export declare const OBSERVER_VERSION = 1;
14
+ export declare const OBSERVER_VERSION = 2;
15
15
  /** Name of the `Runtime.addBinding` function the observer pushes messages through. */
16
16
  export declare const OBSERVER_BINDING = "__blPageObserverPush";
17
17
  /** Well-known global that holds the installed observer instance. */
@@ -31,6 +31,19 @@ export interface ObserverInstallOpts {
31
31
  * `Page.addScriptToEvaluateOnNewDocument` source and as a one-off
32
32
  * `Runtime.evaluate` expression, which evaluates to
33
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.
34
47
  */
35
48
  export declare function buildObserverInstallJs(opts?: ObserverInstallOpts): string;
36
49
  /**
@@ -49,6 +62,13 @@ export declare function buildObserverMarkJs(): string;
49
62
  * baseline; or `{ ok: false, reason: 'observer-unavailable' | 'doc-changed' }`.
50
63
  */
51
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;
52
72
  /**
53
73
  * Build the teardown expression: disconnects every observer, restores the
54
74
  * `attachShadow` and `history` patches when they are still the observer's,
@@ -11,7 +11,7 @@
11
11
  * `cdp/builders-parity.test.ts` checks the built output.
12
12
  */
13
13
  /** Bump whenever the in-page protocol (global shape, messages, methods) changes. */
14
- export const OBSERVER_VERSION = 1;
14
+ export const OBSERVER_VERSION = 2;
15
15
  /** Name of the `Runtime.addBinding` function the observer pushes messages through. */
16
16
  export const OBSERVER_BINDING = '__blPageObserverPush';
17
17
  /** Well-known global that holds the installed observer instance. */
@@ -30,6 +30,19 @@ function clampInt(value, fallback, min, max) {
30
30
  * `Page.addScriptToEvaluateOnNewDocument` source and as a one-off
31
31
  * `Runtime.evaluate` expression, which evaluates to
32
32
  * `{ ok: true, installed, version, doc_id, coverage }`.
33
+ *
34
+ * The instance also owns element identity (spec v4 §5.6): `refFor(el)` issues
35
+ * `e<N>` (monotonic per document instance, from `e1`) the first time an
36
+ * element is seen and reuses it while the node lives; `resolveRef(ref)`
37
+ * returns the element while it is connected, else `null`. Refs are only valid
38
+ * for the `doc_id` that issued them. Neither method throws. `nextSnapshotId()`
39
+ * issues `<doc_id>:s<N>` (N monotonic per document instance, from 1) for each
40
+ * `snapshot` result and returns `null` once the instance is torn down; the
41
+ * `doc_id` prefix lets a stale id from another document be rejected.
42
+ * `recordSnapshot(id, record)` keeps the last 4 snapshot records per document
43
+ * instance (oldest evicted first) for `snapshot({ since })`; `getSnapshot(id)`
44
+ * returns the record or `null` when unknown or evicted. The record is opaque
45
+ * here. Both are inert after teardown, which clears the store.
33
46
  */
34
47
  export function buildObserverInstallJs(opts = {}) {
35
48
  const optsJson = JSON.stringify({
@@ -71,6 +84,18 @@ export function buildObserverInstallJs(opts = {}) {
71
84
  };
72
85
  const observers = [];
73
86
  const observed = new WeakSet();
87
+ let refs = new WeakMap();
88
+ const refNodes = new Map();
89
+ let refSeq = 0;
90
+ let snapshotSeq = 0;
91
+ // Last SNAPSHOT_KEEP snapshot records (opaque to the observer; the snapshot
92
+ // builder owns their shape), keyed by snapshot_id in insertion order.
93
+ const SNAPSHOT_KEEP = 4;
94
+ const snapshots = new Map();
95
+ // WeakRef keeps the registry from pinning removed nodes; the fallback only
96
+ // matters on engines without it (Chrome has it since 84).
97
+ const holdWeakly =
98
+ typeof WeakRef === 'function' ? (el) => new WeakRef(el) : (el) => ({ deref: () => el });
74
99
  const round = (n) => Math.round(n * 10) / 10;
75
100
 
76
101
  function push(message) {
@@ -257,6 +282,53 @@ export function buildObserverInstallJs(opts = {}) {
257
282
 
258
283
  g.flush = flush;
259
284
 
285
+ g.refFor = function (el) {
286
+ try {
287
+ if (g.dead || !el || el.nodeType !== 1) return null;
288
+ let ref = refs.get(el);
289
+ if (!ref) {
290
+ ref = 'e' + ++refSeq;
291
+ refs.set(el, ref);
292
+ }
293
+ // Same node, same ref: re-register one pruned while it was detached.
294
+ if (!refNodes.has(ref)) refNodes.set(ref, holdWeakly(el));
295
+ return ref;
296
+ } catch (e) {
297
+ return null;
298
+ }
299
+ };
300
+
301
+ g.resolveRef = function (ref) {
302
+ try {
303
+ if (typeof ref !== 'string') return null;
304
+ const held = refNodes.get(ref);
305
+ if (!held) return null;
306
+ const el = held.deref();
307
+ if (el && el.isConnected) return el;
308
+ refNodes.delete(ref);
309
+ return null;
310
+ } catch (e) {
311
+ return null;
312
+ }
313
+ };
314
+
315
+ g.nextSnapshotId = function () {
316
+ if (g.dead) return null;
317
+ return g.doc_id + ':s' + ++snapshotSeq;
318
+ };
319
+ g.recordSnapshot = function (id, record) {
320
+ if (g.dead || typeof id !== 'string') return;
321
+ snapshots.delete(id);
322
+ snapshots.set(id, record);
323
+ while (snapshots.size > SNAPSHOT_KEEP) snapshots.delete(snapshots.keys().next().value);
324
+ };
325
+
326
+ g.getSnapshot = function (id) {
327
+ if (g.dead || typeof id !== 'string') return null;
328
+ const record = snapshots.get(id);
329
+ return record === undefined ? null : record;
330
+ };
331
+
260
332
  g.mark = function () {
261
333
  flush();
262
334
  const mark = g.seq;
@@ -337,6 +409,10 @@ export function buildObserverInstallJs(opts = {}) {
337
409
  window.removeEventListener('popstate', onNav);
338
410
  window.removeEventListener('hashchange', onNav);
339
411
  g.baselines.clear();
412
+ refs = new WeakMap();
413
+ refNodes.clear();
414
+ snapshotSeq = 0;
415
+ snapshots.clear();
340
416
  if (window[opts.key] === g) {
341
417
  try { delete window[opts.key]; } catch (e) {}
342
418
  }
@@ -379,9 +455,37 @@ export function buildObserverMarkJs() {
379
455
  */
380
456
  export function buildObserverChangesJs(mark, docId) {
381
457
  const markJson = JSON.stringify(clampInt(mark, 0, 0, Number.MAX_SAFE_INTEGER));
382
- const docJson = typeof docId === 'string' && docId.length > 0 ? JSON.stringify(docId) : 'null';
458
+ const docJson = typeof docId === 'string' && docId.length > 0 ? jsStringLiteral(docId) : 'null';
383
459
  return callInstance(`g.changes(${markJson}, ${docJson})`);
384
460
  }
461
+ /**
462
+ * A string literal safe to embed in JS source. `JSON.stringify` alone leaves
463
+ * U+2028 / U+2029 (line terminators in older JS grammars) and `<` (which can
464
+ * close a surrounding `<script>`) unescaped; this escapes them as `\uXXXX`,
465
+ * which parses back to the same string.
466
+ */
467
+ function jsStringLiteral(value) {
468
+ // The two separators are written as char codes so this source file never
469
+ // carries them raw; the replacement is the six-character escape sequence.
470
+ const lineSeparator = String.fromCharCode(0x2028);
471
+ const paragraphSeparator = String.fromCharCode(0x2029);
472
+ return JSON.stringify(value)
473
+ .split(lineSeparator)
474
+ .join('\\u2028')
475
+ .split(paragraphSeparator)
476
+ .join('\\u2029')
477
+ .replace(/</g, '\\u003c');
478
+ }
479
+ /**
480
+ * Build the ref-resolve expression, for tests and debugging. Evaluates to
481
+ * `{ ok: true, found: true, tag }` when `ref` names an element of the current
482
+ * document that is still connected, `{ ok: true, found: false }` when it is
483
+ * unknown or stale, or `{ ok: false, reason: 'observer-unavailable' }`.
484
+ */
485
+ export function buildObserverRefResolveJs(ref) {
486
+ const refJson = jsStringLiteral(typeof ref === 'string' ? ref : '');
487
+ return callInstance(`((el) => el ? { ok: true, found: true, tag: el.localName || String(el.nodeName).toLowerCase() } : { ok: true, found: false })(g.resolveRef(${refJson}))`);
488
+ }
385
489
  /**
386
490
  * Build the teardown expression: disconnects every observer, restores the
387
491
  * `attachShadow` and `history` patches when they are still the observer's,