@jobshimo/browser-link 0.34.1 → 0.35.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 (36) hide show
  1. package/dist/agent-instructions/content.js +9 -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 +18 -0
  10. package/dist/cdp/inpage/builders.js +21 -1
  11. package/dist/cdp/inpage/builders.js.map +1 -1
  12. package/dist/cdp/inpage/observer.d.ts +57 -0
  13. package/dist/cdp/inpage/observer.js +405 -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 +160 -25
  19. package/dist/cdp/transport.js.map +1 -1
  20. package/dist/extension/background.js +182 -33
  21. package/dist/extension/background.js.map +1 -1
  22. package/dist/extension/inpage/builders.d.ts +18 -0
  23. package/dist/extension/inpage/builders.js +20 -0
  24. package/dist/extension/inpage/builders.js.map +1 -1
  25. package/dist/extension/inpage/observer.d.ts +68 -0
  26. package/dist/extension/inpage/observer.js +416 -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 +3 -3
  33. package/dist/tools/browser-definitions.js.map +1 -1
  34. package/dist/tools/server-instructions.js +9 -5
  35. package/dist/tools/server-instructions.js.map +1 -1
  36. package/package.json +1 -1
@@ -0,0 +1,405 @@
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 const OBSERVER_VERSION = 1;
15
+ /** Name of the `Runtime.addBinding` function the observer pushes messages through. */
16
+ export const OBSERVER_BINDING = '__blPageObserverPush';
17
+ /** Well-known global that holds the installed observer instance. */
18
+ export const OBSERVER_GLOBAL = '__blPageObserver';
19
+ const DEFAULT_DESCRIPTOR_CAP = 20;
20
+ const MAX_DESCRIPTOR_CAP = 100;
21
+ const DEFAULT_RING_SIZE = 64;
22
+ const MAX_RING_SIZE = 1024;
23
+ function clampInt(value, fallback, min, max) {
24
+ if (typeof value !== 'number' || !Number.isFinite(value))
25
+ return fallback;
26
+ return Math.min(max, Math.max(min, Math.floor(value)));
27
+ }
28
+ /**
29
+ * Build the observer install source. Valid both as a
30
+ * `Page.addScriptToEvaluateOnNewDocument` source and as a one-off
31
+ * `Runtime.evaluate` expression, which evaluates to
32
+ * `{ ok: true, installed, version, doc_id, coverage }`.
33
+ */
34
+ export function buildObserverInstallJs(opts = {}) {
35
+ const optsJson = JSON.stringify({
36
+ key: OBSERVER_GLOBAL,
37
+ binding: typeof opts.binding === 'string' && opts.binding.length > 0 ? opts.binding : OBSERVER_BINDING,
38
+ version: clampInt(opts.version, OBSERVER_VERSION, 0, Number.MAX_SAFE_INTEGER),
39
+ cap: clampInt(opts.descriptor_cap, DEFAULT_DESCRIPTOR_CAP, 1, MAX_DESCRIPTOR_CAP),
40
+ ringSize: clampInt(opts.ring_size, DEFAULT_RING_SIZE, 1, MAX_RING_SIZE),
41
+ });
42
+ return `
43
+ (() => {
44
+ const opts = ${optsJson};
45
+ const existing = window[opts.key];
46
+ if (existing && typeof existing.version === 'number' && existing.version >= opts.version) {
47
+ return { ok: true, installed: false, version: existing.version, doc_id: existing.doc_id, coverage: existing.coverage };
48
+ }
49
+ if (existing && typeof existing.teardown === 'function') {
50
+ try { existing.teardown(); } catch (e) {}
51
+ }
52
+ let isFrame = true;
53
+ try { isFrame = window !== window.top; } catch (e) {}
54
+ const zeroKinds = () => ({ childList: 0, attributes: 0, characterData: 0 });
55
+ const g = {
56
+ version: opts.version,
57
+ doc_id: Math.random().toString(36).slice(2, 10) + Date.now().toString(36),
58
+ seq: 0,
59
+ ring: [],
60
+ pending: new Map(),
61
+ pendingRecords: 0,
62
+ pendingKinds: zeroKinds(),
63
+ pendingRoots: { document: false, shadow: false },
64
+ t0: 0,
65
+ timer: null,
66
+ rescan: null,
67
+ roots: 0,
68
+ coverage: 'full',
69
+ baselines: new Map(),
70
+ dead: false,
71
+ };
72
+ const observers = [];
73
+ const observed = new WeakSet();
74
+ const round = (n) => Math.round(n * 10) / 10;
75
+
76
+ function push(message) {
77
+ const fn = window[opts.binding];
78
+ if (typeof fn !== 'function') return;
79
+ try { fn(JSON.stringify(message)); } catch (e) {}
80
+ }
81
+
82
+ function fold(records, kind) {
83
+ for (const rec of records) {
84
+ if (g.pendingRecords === 0) g.t0 = performance.now();
85
+ g.pendingRecords++;
86
+ g.pendingKinds[rec.type]++;
87
+ g.pendingRoots[kind] = true;
88
+ const target = rec.type === 'characterData' ? rec.target.parentNode : rec.target;
89
+ if (!target) continue;
90
+ g.pending.set(target, (g.pending.get(target) || 0) + 1);
91
+ }
92
+ }
93
+
94
+ function drain() {
95
+ for (const o of observers) {
96
+ const recs = o.mo.takeRecords();
97
+ if (recs.length) fold(recs, o.kind);
98
+ }
99
+ }
100
+
101
+ function segment(el) {
102
+ const tag = el.localName || String(el.nodeName).toLowerCase();
103
+ return el.id ? tag + '#' + el.id : tag;
104
+ }
105
+
106
+ function pathOf(node) {
107
+ const parts = [];
108
+ let n = node;
109
+ let segments = 0;
110
+ while (n && segments < 6) {
111
+ if (n.nodeType === 1) {
112
+ if (n === document.documentElement) break;
113
+ parts.unshift(segment(n));
114
+ segments++;
115
+ n = n.parentNode;
116
+ } else if (n.nodeType === 11 && n.host) {
117
+ parts.unshift('#shadow');
118
+ n = n.host;
119
+ } else {
120
+ break;
121
+ }
122
+ }
123
+ return parts.join('>');
124
+ }
125
+
126
+ function describe(node) {
127
+ if (node.nodeType === 11) {
128
+ return { tag: '#shadow-root', path: node.host ? pathOf(node.host) + '>#shadow' : '#shadow' };
129
+ }
130
+ if (node.nodeType === 9) return { tag: '#document', path: '#document' };
131
+ const d = { tag: node.localName || String(node.nodeName).toLowerCase() };
132
+ if (typeof node.id === 'string' && node.id) d.id = node.id;
133
+ const role = typeof node.getAttribute === 'function' ? node.getAttribute('role') : null;
134
+ if (role) d.role = role;
135
+ d.path = pathOf(node);
136
+ return d;
137
+ }
138
+
139
+ function flush() {
140
+ if (g.timer !== null) {
141
+ clearTimeout(g.timer);
142
+ g.timer = null;
143
+ }
144
+ drain();
145
+ if (g.pendingRecords === 0) return null;
146
+ const entries = Array.from(g.pending.entries()).sort((a, b) => b[1] - a[1]);
147
+ const top = entries.slice(0, opts.cap);
148
+ const summary = {
149
+ type: 'summary',
150
+ doc_id: g.doc_id,
151
+ seq: ++g.seq,
152
+ t0: round(g.t0),
153
+ t1: round(performance.now()),
154
+ records: g.pendingRecords,
155
+ targets: entries.length,
156
+ kinds: g.pendingKinds,
157
+ roots: { document: g.pendingRoots.document, shadow: g.pendingRoots.shadow, frame: isFrame },
158
+ descriptors: top.map((e) => describe(e[0])),
159
+ overflow: entries.length - top.length,
160
+ };
161
+ g.ring.push({ summary: summary, counts: top.map((e) => e[1]) });
162
+ while (g.ring.length > opts.ringSize) g.ring.shift();
163
+ g.pending = new Map();
164
+ g.pendingRecords = 0;
165
+ g.pendingKinds = zeroKinds();
166
+ g.pendingRoots = { document: false, shadow: false };
167
+ push(summary);
168
+ return summary;
169
+ }
170
+
171
+ function onTimer() {
172
+ g.timer = null;
173
+ try { flush(); } catch (e) {}
174
+ }
175
+
176
+ function onRecords(kind) {
177
+ return (records) => {
178
+ if (g.dead) return;
179
+ try {
180
+ fold(records, kind);
181
+ if (g.pendingRecords > 0 && g.timer === null) g.timer = setTimeout(onTimer, 16);
182
+ } catch (e) {}
183
+ };
184
+ }
185
+
186
+ function observe(root, kind) {
187
+ if (!root || observed.has(root)) return;
188
+ observed.add(root);
189
+ try {
190
+ const mo = new MutationObserver(onRecords(kind));
191
+ mo.observe(root, { subtree: true, childList: true, attributes: true, characterData: true });
192
+ observers.push({ mo: mo, kind: kind });
193
+ g.roots++;
194
+ } catch (e) {}
195
+ }
196
+
197
+ function walk(root) {
198
+ let all;
199
+ try { all = root.querySelectorAll('*'); } catch (e) { return; }
200
+ for (const el of all) {
201
+ const sr = el.shadowRoot;
202
+ if (sr) {
203
+ observe(sr, 'shadow');
204
+ walk(sr);
205
+ }
206
+ }
207
+ }
208
+
209
+ observe(document, 'document');
210
+ walk(document);
211
+
212
+ const proto = Element.prototype;
213
+ const originalAttach = proto.attachShadow;
214
+ let patchedAttach = null;
215
+ if (typeof originalAttach === 'function') {
216
+ try {
217
+ const candidate = function attachShadow() {
218
+ const root = originalAttach.apply(this, arguments);
219
+ if (!g.dead) {
220
+ try { observe(root, 'shadow'); } catch (e) {}
221
+ }
222
+ return root;
223
+ };
224
+ proto.attachShadow = candidate;
225
+ if (proto.attachShadow !== candidate) throw new Error('attachShadow not patchable');
226
+ patchedAttach = candidate;
227
+ } catch (e) {
228
+ g.coverage = 'partial';
229
+ g.rescan = setInterval(() => {
230
+ try { walk(document); } catch (err) {}
231
+ }, 2000);
232
+ }
233
+ }
234
+
235
+ function onNav() {
236
+ if (g.dead) return;
237
+ push({ type: 'nav', doc_id: g.doc_id, url: location.href });
238
+ }
239
+ const hist = window.history;
240
+ const historyPatches = [];
241
+ for (const name of ['pushState', 'replaceState']) {
242
+ const original = hist[name];
243
+ if (typeof original !== 'function') continue;
244
+ const hadOwn = Object.prototype.hasOwnProperty.call(hist, name);
245
+ const wrapped = function () {
246
+ const result = original.apply(this, arguments);
247
+ try { onNav(); } catch (e) {}
248
+ return result;
249
+ };
250
+ try {
251
+ hist[name] = wrapped;
252
+ historyPatches.push({ name: name, original: original, wrapped: wrapped, hadOwn: hadOwn });
253
+ } catch (e) {}
254
+ }
255
+ window.addEventListener('popstate', onNav);
256
+ window.addEventListener('hashchange', onNav);
257
+
258
+ g.flush = flush;
259
+
260
+ g.mark = function () {
261
+ flush();
262
+ const mark = g.seq;
263
+ g.baselines.delete(mark);
264
+ g.baselines.set(mark, { url: location.href, active: document.activeElement });
265
+ while (g.baselines.size > 16) g.baselines.delete(g.baselines.keys().next().value);
266
+ return { ok: true, doc_id: g.doc_id, mark: mark, url: location.href, has_focus_baseline: true };
267
+ };
268
+
269
+ g.changes = function (mark, docId) {
270
+ if (docId && docId !== g.doc_id) return { ok: false, reason: 'doc-changed' };
271
+ const base = g.baselines.get(mark);
272
+ if (!base) return { ok: false, reason: 'doc-changed' };
273
+ flush();
274
+ const items = g.ring.filter((it) => it.summary.seq > mark);
275
+ const kinds = zeroKinds();
276
+ const roots = { document: false, shadow: false, frame: isFrame };
277
+ const merged = new Map();
278
+ let records = 0;
279
+ let targets = 0;
280
+ let overflow = 0;
281
+ for (const it of items) {
282
+ const s = it.summary;
283
+ records += s.records;
284
+ targets += s.targets;
285
+ overflow += s.overflow;
286
+ kinds.childList += s.kinds.childList;
287
+ kinds.attributes += s.kinds.attributes;
288
+ kinds.characterData += s.kinds.characterData;
289
+ roots.document = roots.document || s.roots.document;
290
+ roots.shadow = roots.shadow || s.roots.shadow;
291
+ s.descriptors.forEach((d, i) => {
292
+ const key = d.tag + '\\u0000' + (d.id || '') + '\\u0000' + d.path;
293
+ const m = merged.get(key);
294
+ if (m) m.n += it.counts[i];
295
+ else merged.set(key, { d: d, n: it.counts[i] });
296
+ });
297
+ }
298
+ const sorted = Array.from(merged.values()).sort((a, b) => b.n - a.n);
299
+ const descriptors = sorted.slice(0, opts.cap).map((m) => m.d);
300
+ overflow += sorted.length - descriptors.length;
301
+ const out = {
302
+ ok: true,
303
+ doc_id: g.doc_id,
304
+ since: mark,
305
+ records: records,
306
+ targets: targets,
307
+ kinds: kinds,
308
+ roots: roots,
309
+ descriptors: descriptors,
310
+ overflow: overflow,
311
+ };
312
+ if (g.seq > mark && (items.length === 0 || items[0].summary.seq !== mark + 1)) out.truncated = true;
313
+ if (location.href !== base.url) out.url_changed = location.href;
314
+ if (document.activeElement !== base.active) out.focus_moved = true;
315
+ return out;
316
+ };
317
+
318
+ g.teardown = function () {
319
+ if (g.dead) return;
320
+ g.dead = true;
321
+ if (g.timer !== null) clearTimeout(g.timer);
322
+ if (g.rescan !== null) clearInterval(g.rescan);
323
+ for (const o of observers) {
324
+ try { o.mo.disconnect(); } catch (e) {}
325
+ }
326
+ observers.length = 0;
327
+ if (patchedAttach && proto.attachShadow === patchedAttach) {
328
+ try { proto.attachShadow = originalAttach; } catch (e) {}
329
+ }
330
+ for (const p of historyPatches) {
331
+ if (hist[p.name] !== p.wrapped) continue;
332
+ try {
333
+ if (p.hadOwn) hist[p.name] = p.original;
334
+ else delete hist[p.name];
335
+ } catch (e) {}
336
+ }
337
+ window.removeEventListener('popstate', onNav);
338
+ window.removeEventListener('hashchange', onNav);
339
+ g.baselines.clear();
340
+ if (window[opts.key] === g) {
341
+ try { delete window[opts.key]; } catch (e) {}
342
+ }
343
+ };
344
+
345
+ Object.defineProperty(window, opts.key, { value: g, configurable: true, writable: true, enumerable: false });
346
+ push({ type: 'doc_start', doc_id: g.doc_id, url: location.href, version: g.version, coverage: g.coverage });
347
+ return { ok: true, installed: true, version: g.version, doc_id: g.doc_id, coverage: g.coverage };
348
+ })()`;
349
+ }
350
+ const UNAVAILABLE = "{ ok: false, reason: 'observer-unavailable' }";
351
+ function callInstance(call) {
352
+ return `
353
+ (() => {
354
+ const g = window[${JSON.stringify(OBSERVER_GLOBAL)}];
355
+ if (!g || g.dead || typeof g.doc_id !== 'string') return ${UNAVAILABLE};
356
+ try {
357
+ return ${call};
358
+ } catch (e) {
359
+ return ${UNAVAILABLE};
360
+ }
361
+ })()`;
362
+ }
363
+ /**
364
+ * Build the action-mark expression. Forces a flush (including records still
365
+ * queued in the observers) so the mark is exact, then baselines URL and focus.
366
+ * Evaluates to `{ ok: true, doc_id, mark, url, has_focus_baseline: true }` or
367
+ * `{ ok: false, reason: 'observer-unavailable' }`.
368
+ */
369
+ export function buildObserverMarkJs() {
370
+ return callInstance('g.mark()');
371
+ }
372
+ /**
373
+ * Build the "changes since mark" expression. Forces a flush, then aggregates
374
+ * the ring summaries with `seq > mark`. `docId`, when given, must match the
375
+ * current document. Evaluates to the aggregated changes (see
376
+ * `SettleChanges` in `@browser-link/shared`) plus `ok`, `doc_id`, `since`,
377
+ * and `url_changed` / `focus_moved` only when they differ from the mark's
378
+ * baseline; or `{ ok: false, reason: 'observer-unavailable' | 'doc-changed' }`.
379
+ */
380
+ export function buildObserverChangesJs(mark, docId) {
381
+ 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';
383
+ return callInstance(`g.changes(${markJson}, ${docJson})`);
384
+ }
385
+ /**
386
+ * Build the teardown expression: disconnects every observer, restores the
387
+ * `attachShadow` and `history` patches when they are still the observer's,
388
+ * and removes the global. Idempotent; evaluates to `{ ok: true, removed }`.
389
+ */
390
+ export function buildObserverTeardownJs() {
391
+ return `
392
+ (() => {
393
+ const key = ${JSON.stringify(OBSERVER_GLOBAL)};
394
+ const g = window[key];
395
+ if (!g) return { ok: true, removed: false };
396
+ if (typeof g.teardown === 'function') {
397
+ try { g.teardown(); } catch (e) {}
398
+ }
399
+ if (window[key] === g) {
400
+ try { delete window[key]; } catch (e) {}
401
+ }
402
+ return { ok: true, removed: true };
403
+ })()`;
404
+ }
405
+ //# sourceMappingURL=observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.js","sourceRoot":"","sources":["../../../src/cdp/inpage/observer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ /** Per-kind MutationRecord counts reported by the page observer. */
2
+ interface PageObserverKinds {
3
+ childList: number;
4
+ attributes: number;
5
+ characterData: number;
6
+ }
7
+ /** Which kinds of root the counted records came from. `frame` is true when
8
+ * the observer runs in a child frame (`window !== window.top`). */
9
+ interface PageObserverRoots {
10
+ document: boolean;
11
+ shadow: boolean;
12
+ frame: boolean;
13
+ }
14
+ /** One mutated target, as described by the page observer. `path` is a short
15
+ * structural path such as `body>div#app>ul` (at most six element segments,
16
+ * no indexes), with a `#shadow` segment at each shadow-root boundary. A
17
+ * shadow root that was itself the mutation target is reported with tag
18
+ * `#shadow-root`. */
19
+ interface PageObserverDescriptor {
20
+ tag: string;
21
+ id?: string;
22
+ role?: string;
23
+ path: string;
24
+ }
25
+ /** One coalesced flush of the page observer: every MutationRecord seen
26
+ * since the previous flush, folded per target element. */
27
+ interface PageObserverSummary {
28
+ type: 'summary';
29
+ doc_id: string;
30
+ /** Monotonic per document, starting at 1. */
31
+ seq: number;
32
+ /** `performance.now()` at the first record and at the flush. */
33
+ t0: number;
34
+ t1: number;
35
+ records: number;
36
+ /** Distinct target elements in this flush. */
37
+ targets: number;
38
+ kinds: PageObserverKinds;
39
+ roots: PageObserverRoots;
40
+ /** Up to the descriptor cap, ordered by record count (highest first). */
41
+ descriptors: PageObserverDescriptor[];
42
+ /** Targets beyond the descriptor cap. */
43
+ overflow: number;
44
+ }
45
+ /** Pushed once when the page observer installs in a document. */
46
+ interface PageObserverDocStart {
47
+ type: 'doc_start';
48
+ doc_id: string;
49
+ url: string;
50
+ version: number;
51
+ /** `partial` when `attachShadow` could not be patched and the observer
52
+ * falls back to a periodic rescan for open shadow roots. */
53
+ coverage: 'full' | 'partial';
54
+ }
55
+ /** Pushed on `pushState`, `replaceState`, `popstate` and `hashchange`. */
56
+ interface PageObserverNav {
57
+ type: 'nav';
58
+ doc_id: string;
59
+ url: string;
60
+ }
61
+ /** Every message the page observer pushes through its binding (JSON). */
62
+ type PageObserverMessage = PageObserverSummary | PageObserverDocStart | PageObserverNav;
63
+ /** Summaries kept in the host mirror (the page keeps its own, larger ring). */
64
+ export declare const OBSERVER_RECENT_CAP = 32;
65
+ /** Per-tab host state for the page observer of the tab's top document. */
66
+ export interface ObserverMirror {
67
+ /** True once the install commands succeeded on this tab. */
68
+ installed: boolean;
69
+ doc_id?: string;
70
+ url?: string;
71
+ coverage?: 'full' | 'partial';
72
+ last_seq: number;
73
+ recent: PageObserverSummary[];
74
+ }
75
+ export type ObserverMessageOutcome = {
76
+ kind: 'doc_start';
77
+ url: string;
78
+ } | {
79
+ kind: 'nav';
80
+ url: string;
81
+ } | {
82
+ kind: 'summary';
83
+ } | {
84
+ kind: 'ignored';
85
+ };
86
+ /** Reason attached as `settle.observer` when `changes` could not be read. */
87
+ export type ObserverChangesFailure = 'observer-unavailable' | 'doc-changed' | 'changes-error';
88
+ /** The settle result as the action callers carry it (`settleSafely`). */
89
+ export type SettleResultLike = Record<string, unknown>;
90
+ export declare function createObserverMirror(): ObserverMirror;
91
+ /**
92
+ * Parse one binding payload. The binding is a page-visible global, so the
93
+ * payload is untrusted: anything that is not valid JSON in one of the three
94
+ * observer message shapes yields `null`.
95
+ */
96
+ export declare function parseObserverMessage(payload: string): PageObserverMessage | null;
97
+ /**
98
+ * Fold one observer message into the mirror. `doc_start` and `nav` are
99
+ * assumed to come from the top document — the caller confirms that first
100
+ * with `TOP_DOCUMENT_PROBE_JS` / `isTopDocumentMessage`, because the install
101
+ * script also runs in same-process child frames. Summaries from a child
102
+ * frame (`roots.frame`) or from a document other than the mirrored one are
103
+ * ignored. A `nav` that does not change the URL (e.g. a state-only
104
+ * `replaceState`) is ignored so it does not emit a navigation event.
105
+ */
106
+ export declare function applyObserverMessage(mirror: ObserverMirror, message: PageObserverMessage): ObserverMessageOutcome;
107
+ /**
108
+ * Expression evaluated in the tab's top document (default `Runtime.evaluate`
109
+ * context) to tell whether a `doc_start` / `nav` came from it: evaluates to
110
+ * `{ doc_id }` of the observer installed there, or `{ doc_id: null }`.
111
+ */
112
+ export declare const TOP_DOCUMENT_PROBE_JS: string;
113
+ /** True when the top-document probe reports the message's `doc_id`. */
114
+ export declare function isTopDocumentMessage(probe: unknown, docId: string): boolean;
115
+ /** The part of an action mark (`buildObserverMarkJs`) the changes read needs. */
116
+ export interface ObserverMark {
117
+ mark: number;
118
+ doc_id: string;
119
+ }
120
+ /**
121
+ * Validate the result of the mark evaluation. `null` means there is no
122
+ * usable mark (observer missing, `ok: false`, or a malformed value); the
123
+ * caller then skips the changes read and reports the failure instead.
124
+ */
125
+ export declare function readObserverMark(value: unknown): ObserverMark | null;
126
+ /**
127
+ * Fold the observer's "changes since mark" into a settle result (spec v4
128
+ * §5.4). With valid changes: attach `changes` (the `SettleChanges` shape),
129
+ * recompute `mutation_count` from the observer's records, and replace
130
+ * `url_changed` / `focus_moved` with the values baselined at the action
131
+ * mark (present only when they changed). Otherwise keep every settle field
132
+ * as `settleSafely` produced it and add `changes: null` plus
133
+ * `observer: <reason>`. A `null` settle (settle disabled) stays `null`.
134
+ * Returns a new object; never throws.
135
+ */
136
+ export declare function mergeSettleChanges(settle: SettleResultLike | null, changes: unknown): SettleResultLike | null;
137
+ export {};