@descryy/mcp 0.6.0 → 0.7.1
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/action-handshake.d.ts +8 -75
- package/dist/action-handshake.d.ts.map +1 -1
- package/dist/action-handshake.js +9 -79
- package/dist/action-handshake.js.map +1 -1
- package/dist/bin/descry-mcp.d.ts +4 -15
- package/dist/bin/descry-mcp.d.ts.map +1 -1
- package/dist/bin/descry-mcp.js +12 -42
- package/dist/bin/descry-mcp.js.map +1 -1
- package/dist/browser/driver.d.ts +121 -224
- package/dist/browser/driver.d.ts.map +1 -1
- package/dist/browser/driver.js +9 -55
- package/dist/browser/driver.js.map +1 -1
- package/dist/browser/evidence.d.ts +10 -43
- package/dist/browser/evidence.d.ts.map +1 -1
- package/dist/browser/evidence.js +20 -54
- package/dist/browser/evidence.js.map +1 -1
- package/dist/browser/fake-driver.d.ts +12 -19
- package/dist/browser/fake-driver.d.ts.map +1 -1
- package/dist/browser/fake-driver.js +31 -26
- package/dist/browser/fake-driver.js.map +1 -1
- package/dist/browser/graph-write.d.ts +4 -45
- package/dist/browser/graph-write.d.ts.map +1 -1
- package/dist/browser/graph-write.js +8 -53
- package/dist/browser/graph-write.js.map +1 -1
- package/dist/browser/playwright-driver.d.ts +7 -91
- package/dist/browser/playwright-driver.d.ts.map +1 -1
- package/dist/browser/playwright-driver.js +59 -197
- package/dist/browser/playwright-driver.js.map +1 -1
- package/dist/browser/provider.d.ts +5 -34
- package/dist/browser/provider.d.ts.map +1 -1
- package/dist/browser/provider.js +4 -24
- package/dist/browser/provider.js.map +1 -1
- package/dist/browser/registry.d.ts +26 -106
- package/dist/browser/registry.d.ts.map +1 -1
- package/dist/browser/registry.js +20 -77
- package/dist/browser/registry.js.map +1 -1
- package/dist/browser/scenario-provenance.d.ts +23 -0
- package/dist/browser/scenario-provenance.d.ts.map +1 -0
- package/dist/browser/scenario-provenance.js +59 -0
- package/dist/browser/scenario-provenance.js.map +1 -0
- package/dist/browser/scenario-resolve.d.ts +10 -44
- package/dist/browser/scenario-resolve.d.ts.map +1 -1
- package/dist/browser/scenario-resolve.js +10 -41
- package/dist/browser/scenario-resolve.js.map +1 -1
- package/dist/browser/scenario-runner.d.ts +12 -70
- package/dist/browser/scenario-runner.d.ts.map +1 -1
- package/dist/browser/scenario-runner.js +30 -90
- package/dist/browser/scenario-runner.js.map +1 -1
- package/dist/browser/stack-parser.d.ts +4 -28
- package/dist/browser/stack-parser.d.ts.map +1 -1
- package/dist/browser/stack-parser.js +12 -39
- package/dist/browser/stack-parser.js.map +1 -1
- package/dist/browser/tool-support.d.ts +20 -67
- package/dist/browser/tool-support.d.ts.map +1 -1
- package/dist/browser/tool-support.js +21 -65
- package/dist/browser/tool-support.js.map +1 -1
- package/dist/browser/url-scheme.d.ts +14 -0
- package/dist/browser/url-scheme.d.ts.map +1 -0
- package/dist/browser/url-scheme.js +38 -0
- package/dist/browser/url-scheme.js.map +1 -0
- package/dist/cancellation.d.ts +11 -46
- package/dist/cancellation.d.ts.map +1 -1
- package/dist/cancellation.js +11 -46
- package/dist/cancellation.js.map +1 -1
- package/dist/capped.d.ts +9 -33
- package/dist/capped.d.ts.map +1 -1
- package/dist/capped.js +7 -27
- package/dist/capped.js.map +1 -1
- package/dist/disclosure-ledger.d.ts +6 -30
- package/dist/disclosure-ledger.d.ts.map +1 -1
- package/dist/disclosure-ledger.js +4 -26
- package/dist/disclosure-ledger.js.map +1 -1
- package/dist/index.d.ts +10 -26
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -17
- package/dist/index.js.map +1 -1
- package/dist/module-trust.d.ts +24 -0
- package/dist/module-trust.d.ts.map +1 -0
- package/dist/module-trust.js +60 -0
- package/dist/module-trust.js.map +1 -0
- package/dist/path-confinement.d.ts +31 -0
- package/dist/path-confinement.d.ts.map +1 -0
- package/dist/path-confinement.js +44 -0
- package/dist/path-confinement.js.map +1 -0
- package/dist/protocol.d.ts +10 -53
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +14 -60
- package/dist/protocol.js.map +1 -1
- package/dist/registry.d.ts +37 -58
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +88 -96
- package/dist/registry.js.map +1 -1
- package/dist/render.d.ts +89 -13
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +111 -16
- package/dist/render.js.map +1 -1
- package/dist/runtime-registry.d.ts +7 -66
- package/dist/runtime-registry.d.ts.map +1 -1
- package/dist/runtime-registry.js +39 -114
- package/dist/runtime-registry.js.map +1 -1
- package/dist/scenarios/index.d.ts +1 -1
- package/dist/scenarios/index.d.ts.map +1 -1
- package/dist/scenarios/index.js +1 -1
- package/dist/scenarios/index.js.map +1 -1
- package/dist/scenarios/parse.d.ts +4 -18
- package/dist/scenarios/parse.d.ts.map +1 -1
- package/dist/scenarios/parse.js +14 -34
- package/dist/scenarios/parse.js.map +1 -1
- package/dist/scenarios/scenario.d.ts +18 -74
- package/dist/scenarios/scenario.d.ts.map +1 -1
- package/dist/scenarios/scenario.js +7 -34
- package/dist/scenarios/scenario.js.map +1 -1
- package/dist/scenarios/storage.d.ts +11 -41
- package/dist/scenarios/storage.d.ts.map +1 -1
- package/dist/scenarios/storage.js +57 -47
- package/dist/scenarios/storage.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +69 -19
- package/dist/server.js.map +1 -1
- package/dist/session.d.ts +90 -259
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +136 -250
- package/dist/session.js.map +1 -1
- package/dist/tools/analyze.d.ts +32 -118
- package/dist/tools/analyze.d.ts.map +1 -1
- package/dist/tools/analyze.js +65 -186
- package/dist/tools/analyze.js.map +1 -1
- package/dist/tools/browser-click.d.ts +3 -21
- package/dist/tools/browser-click.d.ts.map +1 -1
- package/dist/tools/browser-click.js +10 -31
- package/dist/tools/browser-click.js.map +1 -1
- package/dist/tools/browser-close-session.d.ts +4 -13
- package/dist/tools/browser-close-session.d.ts.map +1 -1
- package/dist/tools/browser-close-session.js +4 -13
- package/dist/tools/browser-close-session.js.map +1 -1
- package/dist/tools/browser-fill.d.ts +5 -36
- package/dist/tools/browser-fill.d.ts.map +1 -1
- package/dist/tools/browser-fill.js +9 -44
- package/dist/tools/browser-fill.js.map +1 -1
- package/dist/tools/browser-navigate.d.ts +6 -27
- package/dist/tools/browser-navigate.d.ts.map +1 -1
- package/dist/tools/browser-navigate.js +6 -23
- package/dist/tools/browser-navigate.js.map +1 -1
- package/dist/tools/browser-run-scenario.d.ts +5 -31
- package/dist/tools/browser-run-scenario.d.ts.map +1 -1
- package/dist/tools/browser-run-scenario.js +10 -46
- package/dist/tools/browser-run-scenario.js.map +1 -1
- package/dist/tools/browser-save-scenario.d.ts +3 -28
- package/dist/tools/browser-save-scenario.d.ts.map +1 -1
- package/dist/tools/browser-save-scenario.js +47 -84
- package/dist/tools/browser-save-scenario.js.map +1 -1
- package/dist/tools/browser-snapshot.d.ts +6 -39
- package/dist/tools/browser-snapshot.d.ts.map +1 -1
- package/dist/tools/browser-snapshot.js +4 -31
- package/dist/tools/browser-snapshot.js.map +1 -1
- package/dist/tools/browser-start-session.d.ts +4 -23
- package/dist/tools/browser-start-session.d.ts.map +1 -1
- package/dist/tools/browser-start-session.js +145 -56
- package/dist/tools/browser-start-session.js.map +1 -1
- package/dist/tools/browser-type.d.ts +5 -35
- package/dist/tools/browser-type.d.ts.map +1 -1
- package/dist/tools/browser-type.js +10 -45
- package/dist/tools/browser-type.js.map +1 -1
- package/dist/tools/contracts.d.ts +9 -86
- package/dist/tools/contracts.d.ts.map +1 -1
- package/dist/tools/contracts.js +24 -80
- package/dist/tools/contracts.js.map +1 -1
- package/dist/tools/cross-pr.d.ts +42 -30
- package/dist/tools/cross-pr.d.ts.map +1 -1
- package/dist/tools/cross-pr.js +81 -62
- package/dist/tools/cross-pr.js.map +1 -1
- package/dist/tools/git-diff.d.ts +18 -2
- package/dist/tools/git-diff.d.ts.map +1 -1
- package/dist/tools/git-diff.js +118 -26
- package/dist/tools/git-diff.js.map +1 -1
- package/dist/tools/git-history.d.ts +5 -16
- package/dist/tools/git-history.d.ts.map +1 -1
- package/dist/tools/git-history.js +3 -10
- package/dist/tools/git-history.js.map +1 -1
- package/dist/tools/history.d.ts +4 -33
- package/dist/tools/history.d.ts.map +1 -1
- package/dist/tools/history.js +6 -31
- package/dist/tools/history.js.map +1 -1
- package/dist/tools/impact.d.ts +7 -51
- package/dist/tools/impact.d.ts.map +1 -1
- package/dist/tools/impact.js +15 -67
- package/dist/tools/impact.js.map +1 -1
- package/dist/tools/index.d.ts +3 -8
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +3 -8
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/kit.d.ts +66 -113
- package/dist/tools/kit.d.ts.map +1 -1
- package/dist/tools/kit.js +60 -28
- package/dist/tools/kit.js.map +1 -1
- package/dist/tools/link-workspace.d.ts +7 -45
- package/dist/tools/link-workspace.d.ts.map +1 -1
- package/dist/tools/link-workspace.js +10 -48
- package/dist/tools/link-workspace.js.map +1 -1
- package/dist/tools/lookup.d.ts +4 -14
- package/dist/tools/lookup.d.ts.map +1 -1
- package/dist/tools/lookup.js +4 -14
- package/dist/tools/lookup.js.map +1 -1
- package/dist/tools/mark-incident.d.ts +7 -54
- package/dist/tools/mark-incident.d.ts.map +1 -1
- package/dist/tools/mark-incident.js +15 -68
- package/dist/tools/mark-incident.js.map +1 -1
- package/dist/tools/observe-runtime.d.ts +27 -210
- package/dist/tools/observe-runtime.d.ts.map +1 -1
- package/dist/tools/observe-runtime.js +273 -434
- package/dist/tools/observe-runtime.js.map +1 -1
- package/dist/tools/observe-tests.d.ts +9 -100
- package/dist/tools/observe-tests.d.ts.map +1 -1
- package/dist/tools/observe-tests.js +74 -152
- package/dist/tools/observe-tests.js.map +1 -1
- package/dist/tools/pr-analysis.d.ts +18 -2
- package/dist/tools/pr-analysis.d.ts.map +1 -1
- package/dist/tools/pr-analysis.js +73 -23
- package/dist/tools/pr-analysis.js.map +1 -1
- package/dist/tools/pre-push.d.ts +74 -16
- package/dist/tools/pre-push.d.ts.map +1 -1
- package/dist/tools/pre-push.js +152 -51
- package/dist/tools/pre-push.js.map +1 -1
- package/dist/tools/propagation.d.ts +11 -53
- package/dist/tools/propagation.d.ts.map +1 -1
- package/dist/tools/propagation.js +17 -58
- package/dist/tools/propagation.js.map +1 -1
- package/dist/tools/questions.d.ts +13 -63
- package/dist/tools/questions.d.ts.map +1 -1
- package/dist/tools/questions.js +33 -105
- package/dist/tools/questions.js.map +1 -1
- package/dist/tools/refusal-fetch.d.ts +4 -40
- package/dist/tools/refusal-fetch.d.ts.map +1 -1
- package/dist/tools/refusal-fetch.js +4 -40
- package/dist/tools/refusal-fetch.js.map +1 -1
- package/dist/tools/runtime-incident.d.ts +4 -63
- package/dist/tools/runtime-incident.d.ts.map +1 -1
- package/dist/tools/runtime-incident.js +10 -87
- package/dist/tools/runtime-incident.js.map +1 -1
- package/dist/tools/scope.d.ts +7 -25
- package/dist/tools/scope.d.ts.map +1 -1
- package/dist/tools/scope.js +10 -29
- package/dist/tools/scope.js.map +1 -1
- package/dist/tools/similar-incidents.d.ts +11 -86
- package/dist/tools/similar-incidents.d.ts.map +1 -1
- package/dist/tools/similar-incidents.js +7 -71
- package/dist/tools/similar-incidents.js.map +1 -1
- package/dist/tools/validate.d.ts +29 -70
- package/dist/tools/validate.d.ts.map +1 -1
- package/dist/tools/validate.js +141 -85
- package/dist/tools/validate.js.map +1 -1
- package/dist/tools/verification-status.d.ts +9 -64
- package/dist/tools/verification-status.d.ts.map +1 -1
- package/dist/tools/verification-status.js +9 -62
- package/dist/tools/verification-status.js.map +1 -1
- package/dist/tools/verify-claim.d.ts +5 -52
- package/dist/tools/verify-claim.d.ts.map +1 -1
- package/dist/tools/verify-claim.js +6 -56
- package/dist/tools/verify-claim.js.map +1 -1
- package/dist/transport.d.ts +15 -52
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +16 -60
- package/dist/transport.js.map +1 -1
- package/package.json +40 -22
|
@@ -1,99 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The real `BrowserDriver`, over `@descryy/runtime-browser`.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* `@descryy/runtime-browser` is now a declared dependency of `@descryy/mcp`
|
|
7
|
-
* (`mcp-browser-tools.md` §3.1's publish landed) — so the reason this module
|
|
8
|
-
* used to give for a call-time specifier import, "not on the registry yet,"
|
|
9
|
-
* no longer holds. It stays dynamic anyway, for a reason that outgrew that
|
|
10
|
-
* one: `loadRuntimeModule` (`PlaywrightDriverOptions`, below) is a real
|
|
11
|
-
* injection seam that `browser-action-settle.test.ts` uses today to drive
|
|
12
|
-
* this file's settle logic against a fake module shaped like this one,
|
|
13
|
-
* deterministically, with no real browser involved. A static
|
|
14
|
-
* `import … from "@descryy/runtime-browser"` at the top of this file would
|
|
15
|
-
* remove that seam along with the indirection, and nothing here needs
|
|
16
|
-
* removing it — the package being on the registry made the *dependency*
|
|
17
|
-
* declarable, not the import path mandatory. Absence (package missing,
|
|
18
|
-
* import throws, or Chromium missing) is still a **named refusal with a
|
|
19
|
-
* remedy**, exactly as before — see `provider.ts`.
|
|
20
|
-
*
|
|
21
|
-
* ## What this adds over the runtime package, and what it deliberately does not
|
|
22
|
-
*
|
|
23
|
-
* It adds exactly one thing the runtime has no API for: **element refs.** The
|
|
24
|
-
* port's contract is that an action names an element the agent has seen, and
|
|
25
|
-
* `BrowserActionCollector` acts on raw selectors. So this module enumerates
|
|
26
|
-
* the page's interactive elements, holds a Playwright `ElementHandle` per ref,
|
|
27
|
-
* and drops the whole map on navigation. Handles rather than injected `data-`
|
|
28
|
-
* attributes on purpose: stamping the DOM would make the act of observing
|
|
29
|
-
* change the page being observed, and a detached element then fails naturally
|
|
30
|
-
* instead of resolving to whatever took its place.
|
|
31
|
-
*
|
|
32
|
-
* Everything else is composition. Launch, click, type, fill, console capture,
|
|
33
|
-
* network capture, the `fetch()` initiator stack and its source-map rewrite
|
|
34
|
-
* are all the runtime package's, called rather than reimplemented.
|
|
35
|
-
*
|
|
36
|
-
* ## Rule 1
|
|
37
|
-
*
|
|
38
|
-
* No language is named here. The stack-trace parser that turns a raw browser
|
|
39
|
-
* stack into resolved frames is handed in by the caller as a module specifier
|
|
40
|
-
* — the same mechanism `runtime-registry.ts` uses for runtime adapters, for
|
|
41
|
-
* the same reason. Without one, requests are still observed and no caller
|
|
42
|
-
* edge is written; the reply says so.
|
|
2
|
+
* The real `BrowserDriver`, over `@descryy/runtime-browser`. Import stays
|
|
3
|
+
* dynamic — `loadRuntimeModule` is a test injection seam (fake module, no
|
|
4
|
+
* browser needed). Adds only element refs over the runtime; stack-trace parsing is caller-supplied (rule 1: no language named here).
|
|
43
5
|
*/
|
|
44
6
|
import type { StackTrace, ServiceRootLookup } from "@descryy/runtime-contracts";
|
|
45
7
|
import type { StackTraceParser } from "@descryy/runtime-backend-observation";
|
|
46
8
|
import type { BrowserDriver, BrowserObservation, ObservedAction, UnsettledAction } from "./driver.ts";
|
|
47
9
|
export interface PlaywrightDriverOptions {
|
|
48
|
-
/**
|
|
49
|
-
* Turns a raw browser stack into resolved frames. Supplied by the caller;
|
|
50
|
-
* this package cannot construct one without naming a language.
|
|
51
|
-
*
|
|
52
|
-
* Absent, the initiator capture is not installed at all — the runtime's own
|
|
53
|
-
* two-halves-or-neither rule — so no request carries a call site and no R4
|
|
54
|
-
* caller edge can be written.
|
|
55
|
-
*/
|
|
10
|
+
/** Turns a raw stack into resolved frames; caller-supplied (rule 1: no language named here). Absent, initiator capture isn't installed and no R4 caller edge is written. */
|
|
56
11
|
readonly stackParser?: StackTraceParser;
|
|
57
12
|
/** Maps a page origin to the on-disk root that served it. Absent means script URLs stay unresolved. */
|
|
58
13
|
readonly resolveSourceRoot?: SourceRootResolver;
|
|
59
14
|
/** Which service the evidence is attributed to. */
|
|
60
15
|
readonly service?: string;
|
|
61
|
-
/**
|
|
62
|
-
* How the runtime browser module is loaded. Defaults to importing
|
|
63
|
-
* `@descryy/runtime-browser` by specifier.
|
|
64
|
-
*
|
|
65
|
-
* It exists because the settle logic below is timing-dependent and this
|
|
66
|
-
* build has no browser backend to run it against — the conformance test
|
|
67
|
-
* that would exercise it skips on every machine without
|
|
68
|
-
* `@descryy/runtime-browser` and Chromium. A driver-level fake page is the
|
|
69
|
-
* only way that logic is executable at all here — and a sleep that dropped
|
|
70
|
-
* evidence shipped in this file with no executable test over it at all,
|
|
71
|
-
* which is the argument for the seam rather than against it.
|
|
72
|
-
*
|
|
73
|
-
* Keep it. Ruled on when it was introduced: exporting `openSession` instead
|
|
74
|
-
* would widen the public surface to an internal lifecycle, where this
|
|
75
|
-
* narrows to one injection point with a stated purpose.
|
|
76
|
-
*/
|
|
16
|
+
/** Loads the runtime browser module; overridable because the settle logic below is timing-dependent and this build has no browser backend to test it against otherwise. Keep narrow — do not export `openSession` instead. */
|
|
77
17
|
readonly loadRuntimeModule?: () => Promise<unknown>;
|
|
78
18
|
}
|
|
79
19
|
type SourceRootResolver = (origin: string) => ServiceRootLookup;
|
|
80
20
|
export declare class BrowserBackendMissingError extends Error {
|
|
81
21
|
constructor(cause: unknown);
|
|
82
22
|
}
|
|
83
|
-
/**
|
|
84
|
-
* An `Evidence`-shaped record, as the collectors emit it.
|
|
85
|
-
*
|
|
86
|
-
* Typed structurally rather than imported wholesale because only four fields
|
|
87
|
-
* are read: everything else the collectors attach travels to nobody here.
|
|
88
|
-
*/
|
|
89
|
-
/**
|
|
90
|
-
* Exported for `browser-request-outcome-mapping.test.ts`, which drives
|
|
91
|
-
* `collect()` directly with synthetic evidence records — the real
|
|
92
|
-
* evidence-to-`ObservedRequest` mapping, not the fake driver's fixtures. That
|
|
93
|
-
* test is the regression test for B3: the fake driver lets a fixture declare
|
|
94
|
-
* `outcome` directly, which let this mapping function ship never populating
|
|
95
|
-
* the field at all while every test still passed.
|
|
96
|
-
*/
|
|
23
|
+
/** `Evidence`-shaped record (typed structurally; only 4 fields are read). Exported for `browser-request-outcome-mapping.test.ts` — regression test for B3, where `outcome` shipped never populated despite passing tests. */
|
|
97
24
|
export interface EmittedEvidence {
|
|
98
25
|
readonly eventType: string;
|
|
99
26
|
readonly payload: Record<string, unknown>;
|
|
@@ -102,18 +29,7 @@ export interface EmittedEvidence {
|
|
|
102
29
|
readonly timestamp: string;
|
|
103
30
|
}
|
|
104
31
|
export declare function createPlaywrightBrowserDriver(options?: PlaywrightDriverOptions): Promise<BrowserDriver>;
|
|
105
|
-
/**
|
|
106
|
-
* Turn what the collectors emitted into the port's vocabulary.
|
|
107
|
-
*
|
|
108
|
-
* The request/response join is on `requestId`, because a `NETWORK_REQUEST`
|
|
109
|
-
* carries the method, the URL and the call-site stack while only the matching
|
|
110
|
-
* `NETWORK_RESPONSE` knows the status. A request with no response is kept with
|
|
111
|
-
* `status: null` — "never answered" is a different fact from a 500, and the
|
|
112
|
-
* port says so.
|
|
113
|
-
*
|
|
114
|
-
* Exported for `browser-request-outcome-mapping.test.ts` — see
|
|
115
|
-
* `EmittedEvidence`'s doc comment for why.
|
|
116
|
-
*/
|
|
32
|
+
/** Turns collector evidence into the port's vocabulary. Joins request/response on `requestId`, keeping `status: null` (not guessed) when unanswered. Exported for `browser-request-outcome-mapping.test.ts`. */
|
|
117
33
|
export declare function collect(emitted: readonly EmittedEvidence[], actions: readonly ObservedAction[], unsettled: readonly UnsettledAction[]): BrowserObservation;
|
|
118
34
|
export {};
|
|
119
35
|
//# sourceMappingURL=playwright-driver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-driver.d.ts","sourceRoot":"","sources":["../../src/browser/playwright-driver.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"playwright-driver.d.ts","sourceRoot":"","sources":["../../src/browser/playwright-driver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAChF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAgB7E,OAAO,KAAK,EACV,aAAa,EAGb,kBAAkB,EAElB,cAAc,EAQd,eAAe,EAChB,MAAM,aAAa,CAAC;AAuBrB,MAAM,WAAW,uBAAuB;IACtC,4KAA4K;IAC5K,QAAQ,CAAC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IACxC,uGAAuG;IACvG,QAAQ,CAAC,iBAAiB,CAAC,EAAE,kBAAkB,CAAC;IAChD,mDAAmD;IACnD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,8NAA8N;IAC9N,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CACrD;AAED,KAAK,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,iBAAiB,CAAC;AAgDhE,qBAAa,0BAA2B,SAAQ,KAAK;gBACvC,KAAK,EAAE,OAAO;CAO3B;AAED,6NAA6N;AAC7N,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,6BAA6B,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,aAAa,CAAC,CAmBjH;AAgWD,gNAAgN;AAChN,wBAAgB,OAAO,CACrB,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,SAAS,EAAE,SAAS,eAAe,EAAE,GACpC,kBAAkB,CAgFpB"}
|
|
@@ -1,45 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The real `BrowserDriver`, over `@descryy/runtime-browser`.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* `@descryy/runtime-browser` is now a declared dependency of `@descryy/mcp`
|
|
7
|
-
* (`mcp-browser-tools.md` §3.1's publish landed) — so the reason this module
|
|
8
|
-
* used to give for a call-time specifier import, "not on the registry yet,"
|
|
9
|
-
* no longer holds. It stays dynamic anyway, for a reason that outgrew that
|
|
10
|
-
* one: `loadRuntimeModule` (`PlaywrightDriverOptions`, below) is a real
|
|
11
|
-
* injection seam that `browser-action-settle.test.ts` uses today to drive
|
|
12
|
-
* this file's settle logic against a fake module shaped like this one,
|
|
13
|
-
* deterministically, with no real browser involved. A static
|
|
14
|
-
* `import … from "@descryy/runtime-browser"` at the top of this file would
|
|
15
|
-
* remove that seam along with the indirection, and nothing here needs
|
|
16
|
-
* removing it — the package being on the registry made the *dependency*
|
|
17
|
-
* declarable, not the import path mandatory. Absence (package missing,
|
|
18
|
-
* import throws, or Chromium missing) is still a **named refusal with a
|
|
19
|
-
* remedy**, exactly as before — see `provider.ts`.
|
|
20
|
-
*
|
|
21
|
-
* ## What this adds over the runtime package, and what it deliberately does not
|
|
22
|
-
*
|
|
23
|
-
* It adds exactly one thing the runtime has no API for: **element refs.** The
|
|
24
|
-
* port's contract is that an action names an element the agent has seen, and
|
|
25
|
-
* `BrowserActionCollector` acts on raw selectors. So this module enumerates
|
|
26
|
-
* the page's interactive elements, holds a Playwright `ElementHandle` per ref,
|
|
27
|
-
* and drops the whole map on navigation. Handles rather than injected `data-`
|
|
28
|
-
* attributes on purpose: stamping the DOM would make the act of observing
|
|
29
|
-
* change the page being observed, and a detached element then fails naturally
|
|
30
|
-
* instead of resolving to whatever took its place.
|
|
31
|
-
*
|
|
32
|
-
* Everything else is composition. Launch, click, type, fill, console capture,
|
|
33
|
-
* network capture, the `fetch()` initiator stack and its source-map rewrite
|
|
34
|
-
* are all the runtime package's, called rather than reimplemented.
|
|
35
|
-
*
|
|
36
|
-
* ## Rule 1
|
|
37
|
-
*
|
|
38
|
-
* No language is named here. The stack-trace parser that turns a raw browser
|
|
39
|
-
* stack into resolved frames is handed in by the caller as a module specifier
|
|
40
|
-
* — the same mechanism `runtime-registry.ts` uses for runtime adapters, for
|
|
41
|
-
* the same reason. Without one, requests are still observed and no caller
|
|
42
|
-
* edge is written; the reply says so.
|
|
2
|
+
* The real `BrowserDriver`, over `@descryy/runtime-browser`. Import stays
|
|
3
|
+
* dynamic — `loadRuntimeModule` is a test injection seam (fake module, no
|
|
4
|
+
* browser needed). Adds only element refs over the runtime; stack-trace parsing is caller-supplied (rule 1: no language named here).
|
|
43
5
|
*/
|
|
44
6
|
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
45
7
|
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
@@ -53,40 +15,15 @@ import { BrowserSessionCrashedError, UnknownElementRefError } from "./driver.js"
|
|
|
53
15
|
/** The module the driver composes. Named once, imported at call time. */
|
|
54
16
|
const RUNTIME_BROWSER_MODULE = "@descryy/runtime-browser";
|
|
55
17
|
const defaultRuntimeModuleLoader = () => import(__rewriteRelativeImportExtension(RUNTIME_BROWSER_MODULE));
|
|
56
|
-
/**
|
|
57
|
-
* How long an action waits for the page to *start* doing something.
|
|
58
|
-
*
|
|
59
|
-
* This is the one half of the wait that cannot be made conditional: there is
|
|
60
|
-
* no signal for "this click will never issue a request", and proving that
|
|
61
|
-
* negative is proving a negative. It is the old fixed 300ms, kept at its old
|
|
62
|
-
* value so an action that causes nothing observes exactly what it observed
|
|
63
|
-
* before — and it is now a *ceiling* rather than a duration, since any
|
|
64
|
-
* request that starts inside it moves the wait onto the settle condition
|
|
65
|
-
* below.
|
|
66
|
-
*/
|
|
18
|
+
/** Ceiling on waiting for network activity to *start* — no signal proves "never will". Kept at the old fixed 300ms so a no-op action observes the same as before. */
|
|
67
19
|
const ACTIVITY_GRACE_MS = 300;
|
|
68
|
-
/**
|
|
69
|
-
* After the last request lifecycle event, how long of nothing counts as
|
|
70
|
-
* settled. Short on purpose: this fires only once activity has been seen, so
|
|
71
|
-
* it measures a gap between requests, not a guess about whether any will come.
|
|
72
|
-
*/
|
|
20
|
+
/** How long after the last request event counts as settled; fires only once activity is seen, so it measures a gap, not a guess about more coming. */
|
|
73
21
|
const NETWORK_QUIET_MS = 120;
|
|
74
|
-
/**
|
|
75
|
-
* The ceiling on the whole wait. A page that never stops requesting must not
|
|
76
|
-
* hold a tool call open forever; reaching this is recorded on the observation
|
|
77
|
-
* and disclosed, never swallowed.
|
|
78
|
-
*/
|
|
22
|
+
/** Ceiling on the whole wait; reaching it is recorded on the observation and disclosed, never swallowed. */
|
|
79
23
|
const SETTLE_TIMEOUT_MS = 5_000;
|
|
80
24
|
/** How often the settle loop re-checks. Small enough not to add latency of its own. */
|
|
81
25
|
const POLL_INTERVAL_MS = 10;
|
|
82
|
-
/**
|
|
83
|
-
* Elements an agent can act on.
|
|
84
|
-
*
|
|
85
|
-
* Deliberately narrow. A snapshot of every node on the page would be
|
|
86
|
-
* enormous, mostly unactionable, and would push the reply budget into
|
|
87
|
-
* dropping the elements that matter. `[role]` is included so an application
|
|
88
|
-
* that builds its own controls is not invisible.
|
|
89
|
-
*/
|
|
26
|
+
/** Elements an agent can act on — deliberately narrow; a full-page snapshot would blow the reply budget. `[role]` included for custom controls. */
|
|
90
27
|
const INTERACTIVE = "a, button, input, select, textarea, [role], [onclick], summary";
|
|
91
28
|
export class BrowserBackendMissingError extends Error {
|
|
92
29
|
constructor(cause) {
|
|
@@ -119,9 +56,11 @@ async function openSession(runtime, session, launchOptions, options) {
|
|
|
119
56
|
const emitted = [];
|
|
120
57
|
const actions = [];
|
|
121
58
|
const unsettled = [];
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
59
|
+
// Never cleared, unlike `actions` — the session's whole-lifetime provenance record for
|
|
60
|
+
// `browser_save_scenario`, not per-drain evidence. Goes out of reach the moment this closure
|
|
61
|
+
// does (session closed/reaped) — there is no other path back to it.
|
|
62
|
+
const performed = [];
|
|
63
|
+
// inFlight/lastEventAt track request activity: counted, not slept through.
|
|
125
64
|
let inFlight = 0;
|
|
126
65
|
let lastEventAt = 0;
|
|
127
66
|
session.page.on("request", () => {
|
|
@@ -160,12 +99,9 @@ async function openSession(runtime, session, launchOptions, options) {
|
|
|
160
99
|
...(options.service === undefined ? {} : { service: options.service }),
|
|
161
100
|
...(options.stackParser === undefined ? {} : { stackTraceParser: options.stackParser }),
|
|
162
101
|
});
|
|
163
|
-
// Started before
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
// started after navigating would observe a page it could never have
|
|
167
|
-
// instrumented, and would report an honest-looking empty stack for every
|
|
168
|
-
// request.
|
|
102
|
+
// Started before first navigation: Playwright's init-script contract means
|
|
103
|
+
// a collector started later misses instrumentation, reporting an
|
|
104
|
+
// honest-looking but empty stack.
|
|
169
105
|
await actionCollector.start(context);
|
|
170
106
|
await consoleCollector.start(context);
|
|
171
107
|
await networkCollector.start(context);
|
|
@@ -183,26 +119,10 @@ async function openSession(runtime, session, launchOptions, options) {
|
|
|
183
119
|
function recordAction(action, ref, mode) {
|
|
184
120
|
actions.push({ action, ref, mode, url: session.page.url(), at: new Date().toISOString() });
|
|
185
121
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
* Two conditions, because there are two different questions:
|
|
191
|
-
*
|
|
192
|
-
* - **Did anything start?** There is no signal for "nothing will", so this
|
|
193
|
-
* half is a bounded wait — `ACTIVITY_GRACE_MS`, the old fixed sleep's own
|
|
194
|
-
* value, so an action that causes nothing observes exactly what it did
|
|
195
|
-
* before. It is a ceiling now rather than a duration: the moment a
|
|
196
|
-
* request starts, the wait moves onto the second condition.
|
|
197
|
-
* - **Has it finished?** Nothing in flight, and nothing new for
|
|
198
|
-
* `NETWORK_QUIET_MS`. This is the half the fixed sleep got wrong: a
|
|
199
|
-
* request that took longer than 300ms was not waited for and its
|
|
200
|
-
* evidence was silently absent from the reply.
|
|
201
|
-
*
|
|
202
|
-
* Whichever condition is running, the whole wait is capped at
|
|
203
|
-
* `SETTLE_TIMEOUT_MS` — and reaching that cap is recorded on the
|
|
204
|
-
* observation, never swallowed.
|
|
205
|
-
*/
|
|
122
|
+
function recordPerformed(action, target, url, text) {
|
|
123
|
+
performed.push({ action, target, url, text, at: new Date().toISOString() });
|
|
124
|
+
}
|
|
125
|
+
/** Waits for the action's network effect (started within `ACTIVITY_GRACE_MS`, then quiet for `NETWORK_QUIET_MS`), capped at `SETTLE_TIMEOUT_MS` — reaching the cap is recorded, never swallowed. */
|
|
206
126
|
async function settleAfterAction(action, ref) {
|
|
207
127
|
const startedAt = Date.now();
|
|
208
128
|
lastEventAt = startedAt;
|
|
@@ -241,6 +161,7 @@ async function openSession(runtime, session, launchOptions, options) {
|
|
|
241
161
|
await dropRefs();
|
|
242
162
|
await actionCollector.navigate(url);
|
|
243
163
|
recordAction("navigate", null, null);
|
|
164
|
+
recordPerformed("navigate", null, url, null);
|
|
244
165
|
},
|
|
245
166
|
async snapshot() {
|
|
246
167
|
assertUsable();
|
|
@@ -266,25 +187,18 @@ async function openSession(runtime, session, launchOptions, options) {
|
|
|
266
187
|
: tag === "input" && type === "checkbox"
|
|
267
188
|
? "checkbox"
|
|
268
189
|
: tag);
|
|
269
|
-
// A pragmatic accessible
|
|
270
|
-
// the label a person would read, in the order a person would find
|
|
271
|
-
// it. Named as an approximation rather than presented as the
|
|
272
|
-
// accessibility tree's own answer.
|
|
190
|
+
// A pragmatic accessible-name approximation, not a full AccName implementation.
|
|
273
191
|
const name = el.getAttribute("aria-label") ??
|
|
274
192
|
el.innerText?.trim() ??
|
|
275
193
|
el.textContent?.trim() ??
|
|
276
194
|
el.getAttribute("placeholder") ??
|
|
277
195
|
el.getAttribute("name") ??
|
|
278
196
|
"";
|
|
279
|
-
//
|
|
280
|
-
//
|
|
281
|
-
//
|
|
282
|
-
//
|
|
283
|
-
//
|
|
284
|
-
// An id that looks generated rather than authored: a hex run
|
|
285
|
-
// (a hashed/uuid-derived id), React's `:r0:`-style useId
|
|
286
|
-
// output, or blank. Rejected because "stable across a reload"
|
|
287
|
-
// is exactly the property a generated id does not have.
|
|
197
|
+
// Computed in this same round trip so role/name/selector read off
|
|
198
|
+
// one live DOM state; runs inside the page (serialized by
|
|
199
|
+
// Playwright), so it must not close over the outer module.
|
|
200
|
+
// Rejects ids that look generated (hex run, React's `:r0:` useId
|
|
201
|
+
// style, or blank) — "stable across reload" is what a generated id lacks.
|
|
288
202
|
const GENERATED_ID = /[0-9a-f]{8}|:r[0-9a-z]+:|^\s*$/i;
|
|
289
203
|
const FORM_CONTROLS = ["input", "select", "textarea", "button"];
|
|
290
204
|
function escapeAttrValue(value) {
|
|
@@ -295,9 +209,7 @@ async function openSession(runtime, session, launchOptions, options) {
|
|
|
295
209
|
return el.ownerDocument.querySelectorAll(candidate).length === 1;
|
|
296
210
|
}
|
|
297
211
|
catch {
|
|
298
|
-
//
|
|
299
|
-
// but not impossible) is treated as "no candidate" rather
|
|
300
|
-
// than thrown — the caller falls through to the next tier.
|
|
212
|
+
// Invalid selector (rare) is treated as "no candidate", not thrown.
|
|
301
213
|
return false;
|
|
302
214
|
}
|
|
303
215
|
}
|
|
@@ -344,12 +256,9 @@ async function openSession(runtime, session, launchOptions, options) {
|
|
|
344
256
|
return null;
|
|
345
257
|
}
|
|
346
258
|
function computeSelector(target) {
|
|
347
|
-
// Priority order: first candidate
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
// addressable either, and the next tier gets its turn rather
|
|
351
|
-
// than giving up at the first attribute that happens to be
|
|
352
|
-
// present.
|
|
259
|
+
// Priority order: first unique candidate wins. Each tier is checked
|
|
260
|
+
// independently — a shared data-testid isn't addressable either,
|
|
261
|
+
// so the next tier gets a turn instead of stopping there.
|
|
353
262
|
const testId = target.getAttribute("data-testid");
|
|
354
263
|
if (testId !== null && testId.trim() !== "") {
|
|
355
264
|
const candidate = `[data-testid="${escapeAttrValue(testId)}"]`;
|
|
@@ -405,16 +314,16 @@ async function openSession(runtime, session, launchOptions, options) {
|
|
|
405
314
|
},
|
|
406
315
|
async click(ref) {
|
|
407
316
|
assertUsable();
|
|
408
|
-
|
|
317
|
+
const { element, handle } = resolve(ref);
|
|
318
|
+
await handle.click();
|
|
409
319
|
recordAction("click", ref, null);
|
|
410
|
-
|
|
411
|
-
//
|
|
412
|
-
// caused — which is the request this whole feature exists to witness.
|
|
320
|
+
recordPerformed("click", { role: element.role, name: element.name, selector: element.selector }, null, null);
|
|
321
|
+
// requestfinished fires after the page's handler resolves; draining immediately would miss the very request being watched for.
|
|
413
322
|
await settleAfterAction("click", ref);
|
|
414
323
|
},
|
|
415
324
|
async type(ref, text) {
|
|
416
325
|
assertUsable();
|
|
417
|
-
const { handle } = resolve(ref);
|
|
326
|
+
const { element, handle } = resolve(ref);
|
|
418
327
|
if (handle.pressSequentially !== undefined)
|
|
419
328
|
await handle.pressSequentially(text);
|
|
420
329
|
else if (handle.type !== undefined)
|
|
@@ -422,12 +331,15 @@ async function openSession(runtime, session, launchOptions, options) {
|
|
|
422
331
|
else
|
|
423
332
|
throw new Error("this Playwright build exposes no keystroke-level typing method");
|
|
424
333
|
recordAction("type", ref, "append");
|
|
334
|
+
recordPerformed("type", { role: element.role, name: element.name, selector: element.selector }, null, text);
|
|
425
335
|
await settleAfterAction("type", ref);
|
|
426
336
|
},
|
|
427
337
|
async fill(ref, text) {
|
|
428
338
|
assertUsable();
|
|
429
|
-
|
|
339
|
+
const { element, handle } = resolve(ref);
|
|
340
|
+
await handle.fill(text);
|
|
430
341
|
recordAction("fill", ref, "replace");
|
|
342
|
+
recordPerformed("fill", { role: element.role, name: element.name, selector: element.selector }, null, text);
|
|
431
343
|
await settleAfterAction("fill", ref);
|
|
432
344
|
},
|
|
433
345
|
async drainEvidence() {
|
|
@@ -437,13 +349,15 @@ async function openSession(runtime, session, launchOptions, options) {
|
|
|
437
349
|
unsettled.length = 0;
|
|
438
350
|
return observation;
|
|
439
351
|
},
|
|
352
|
+
performedSteps() {
|
|
353
|
+
return [...performed];
|
|
354
|
+
},
|
|
440
355
|
async close() {
|
|
441
356
|
if (finalObservation !== null)
|
|
442
357
|
return finalObservation;
|
|
443
358
|
closed = true;
|
|
444
|
-
//
|
|
445
|
-
//
|
|
446
|
-
// observation first would drop exactly that.
|
|
359
|
+
// Stopped before observing: a stop flushes what each collector was
|
|
360
|
+
// still holding, which observing first would drop.
|
|
447
361
|
await actionCollector.stop().catch(() => { });
|
|
448
362
|
await consoleCollector.stop().catch(() => { });
|
|
449
363
|
await networkCollector.stop().catch(() => { });
|
|
@@ -458,33 +372,16 @@ async function openSession(runtime, session, launchOptions, options) {
|
|
|
458
372
|
},
|
|
459
373
|
};
|
|
460
374
|
}
|
|
461
|
-
/**
|
|
462
|
-
* Turn what the collectors emitted into the port's vocabulary.
|
|
463
|
-
*
|
|
464
|
-
* The request/response join is on `requestId`, because a `NETWORK_REQUEST`
|
|
465
|
-
* carries the method, the URL and the call-site stack while only the matching
|
|
466
|
-
* `NETWORK_RESPONSE` knows the status. A request with no response is kept with
|
|
467
|
-
* `status: null` — "never answered" is a different fact from a 500, and the
|
|
468
|
-
* port says so.
|
|
469
|
-
*
|
|
470
|
-
* Exported for `browser-request-outcome-mapping.test.ts` — see
|
|
471
|
-
* `EmittedEvidence`'s doc comment for why.
|
|
472
|
-
*/
|
|
375
|
+
/** Turns collector evidence into the port's vocabulary. Joins request/response on `requestId`, keeping `status: null` (not guessed) when unanswered. Exported for `browser-request-outcome-mapping.test.ts`. */
|
|
473
376
|
export function collect(emitted, actions, unsettled) {
|
|
474
|
-
// Two joins,
|
|
475
|
-
//
|
|
476
|
-
//
|
|
477
|
-
// an id-only join looked correct while silently reporting `status: null`
|
|
478
|
-
// for every request the page issued without one — which is most of them.
|
|
377
|
+
// Two joins, by priority: `requestId` is a correlation header the app must
|
|
378
|
+
// itself propagate, not a transport id — an id-only join silently reported
|
|
379
|
+
// `status: null` for every request without one, i.e. most of them.
|
|
479
380
|
const statusByRequestId = new Map();
|
|
480
381
|
const statusByUrl = new Map();
|
|
481
|
-
// B3:
|
|
482
|
-
//
|
|
483
|
-
//
|
|
484
|
-
// WebSocket `socketerror`); it never emits `"pending"` itself — a request
|
|
485
|
-
// still open when the drain runs simply has no terminal event at all,
|
|
486
|
-
// which is why `pairedRequestIds`/`pairedUrls` below track "a terminal
|
|
487
|
-
// event existed" separately from "and it named a recognisable outcome".
|
|
382
|
+
// B3: same two joins, for `outcome`. `descry-runtime` stamps "answered" or
|
|
383
|
+
// "unanswered", never "pending" itself — a still-open request at drain time
|
|
384
|
+
// has no terminal event, which pairedRequestIds/pairedUrls track separately.
|
|
488
385
|
const outcomeByRequestId = new Map();
|
|
489
386
|
const outcomeByUrl = new Map();
|
|
490
387
|
const pairedRequestIds = new Set();
|
|
@@ -502,9 +399,7 @@ export function collect(emitted, actions, unsettled) {
|
|
|
502
399
|
if (typeof status === "number") {
|
|
503
400
|
if (evidence.requestId !== null)
|
|
504
401
|
statusByRequestId.set(evidence.requestId, status);
|
|
505
|
-
// Last response wins:
|
|
506
|
-
// the most recent outcome, which is the one a caller reading this
|
|
507
|
-
// reply is asking about.
|
|
402
|
+
// Last response wins: repeat requests report the most recent outcome.
|
|
508
403
|
if (typeof url === "string")
|
|
509
404
|
statusByUrl.set(url, status);
|
|
510
405
|
}
|
|
@@ -528,12 +423,9 @@ export function collect(emitted, actions, unsettled) {
|
|
|
528
423
|
const requestId = evidence.requestId;
|
|
529
424
|
const pairedOutcome = (requestId === null ? undefined : outcomeByRequestId.get(requestId)) ?? outcomeByUrl.get(url);
|
|
530
425
|
const wasPaired = (requestId !== null && pairedRequestIds.has(requestId)) || pairedUrls.has(url);
|
|
531
|
-
//
|
|
532
|
-
//
|
|
533
|
-
//
|
|
534
|
-
// recognisable outcome (an older `descry-runtime` build) leaves the
|
|
535
|
-
// field undefined rather than guessing — `isFailedRequest`'s documented
|
|
536
|
-
// fallback is what handles that case, not this function.
|
|
426
|
+
// Paired outcome wins; no terminal event means still in-flight
|
|
427
|
+
// ("pending"); an unrecognised outcome (older runtime build) leaves the
|
|
428
|
+
// field undefined — `isFailedRequest`'s fallback handles that, not this.
|
|
537
429
|
const outcome = pairedOutcome ?? (wasPaired ? undefined : "pending");
|
|
538
430
|
requests.push({
|
|
539
431
|
method,
|
|
@@ -547,9 +439,7 @@ export function collect(emitted, actions, unsettled) {
|
|
|
547
439
|
});
|
|
548
440
|
continue;
|
|
549
441
|
}
|
|
550
|
-
//
|
|
551
|
-
// something went wrong"; a caller triaging them does not need to know
|
|
552
|
-
// which listener fired.
|
|
442
|
+
// Uncaught page error and console error both mean "something went wrong"; callers don't need to know which listener fired.
|
|
553
443
|
if (evidence.eventType === "EXCEPTION" || evidence.eventType === "CONSOLE_MESSAGE") {
|
|
554
444
|
const level = evidence.payload["level"] ?? evidence.payload["type"];
|
|
555
445
|
if (evidence.eventType === "CONSOLE_MESSAGE" && level !== "error")
|
|
@@ -565,30 +455,14 @@ export function collect(emitted, actions, unsettled) {
|
|
|
565
455
|
}
|
|
566
456
|
return { actions: [...actions], consoleErrors, requests, unsettled: [...unsettled] };
|
|
567
457
|
}
|
|
568
|
-
/**
|
|
569
|
-
* Reads a terminal event's `outcome` off an untyped payload.
|
|
570
|
-
*
|
|
571
|
-
* Validated rather than cast, exactly like `scriptUrlOutcomesOf` below: this
|
|
572
|
-
* crosses a package boundary — the field is `@descryy/runtime-browser`'s to
|
|
573
|
-
* set, and this build does not depend on that package's types — so an
|
|
574
|
-
* absent or unrecognised value must degrade to "no outcome reported", never
|
|
575
|
-
* be cast through as one of the three known literals it might not actually
|
|
576
|
-
* be.
|
|
577
|
-
*/
|
|
458
|
+
/** Reads `outcome` off an untyped cross-package payload; validated not cast, since this build doesn't depend on `@descryy/runtime-browser`'s types — unrecognised values degrade to undefined, never a guessed literal. */
|
|
578
459
|
function requestOutcomeOf(payload) {
|
|
579
460
|
const raw = payload["outcome"];
|
|
580
461
|
if (raw === "answered" || raw === "unanswered" || raw === "pending")
|
|
581
462
|
return raw;
|
|
582
463
|
return undefined;
|
|
583
464
|
}
|
|
584
|
-
/**
|
|
585
|
-
* Reads the collector's `scriptUrlMapping` off an untyped payload.
|
|
586
|
-
*
|
|
587
|
-
* Validated rather than cast. This crosses a package boundary — the shape is
|
|
588
|
-
* `@descryy/runtime-browser`'s and this build does not even depend on it — so
|
|
589
|
-
* a malformed or absent field must degrade to "no mapping was reported",
|
|
590
|
-
* never to a half-populated outcome that reads like a real one.
|
|
591
|
-
*/
|
|
465
|
+
/** Reads `scriptUrlMapping` off an untyped cross-package payload; validated not cast, so a malformed/absent field degrades to "no mapping reported" rather than a half-populated result. */
|
|
592
466
|
function scriptUrlOutcomesOf(payload) {
|
|
593
467
|
const raw = payload["scriptUrlMapping"];
|
|
594
468
|
if (!Array.isArray(raw))
|
|
@@ -611,19 +485,7 @@ function scriptUrlOutcomesOf(payload) {
|
|
|
611
485
|
}
|
|
612
486
|
return outcomes;
|
|
613
487
|
}
|
|
614
|
-
/**
|
|
615
|
-
* A short, readable rendering of a parsed stack.
|
|
616
|
-
*
|
|
617
|
-
* `StackTrace` carries frames, not the original text — the raw string is
|
|
618
|
-
* consumed by the parser and deliberately not kept, since a parsed frame with
|
|
619
|
-
* a resolved on-disk path is strictly more useful than the `http://` line it
|
|
620
|
-
* came from. So the port's `stackText` is rendered from the frames rather
|
|
621
|
-
* than passed through, and it is for a human reading the reply: the R4 write
|
|
622
|
-
* uses `stackTrace`, never this.
|
|
623
|
-
*
|
|
624
|
-
* The primary frame is marked, because it is the one a developer should be
|
|
625
|
-
* sent to and it is not always the innermost.
|
|
626
|
-
*/
|
|
488
|
+
/** Human-readable rendering of parsed stack frames, for the reply; the R4 write uses `stackTrace` itself, never this. Primary frame marked since it isn't always the innermost. */
|
|
627
489
|
function renderStack(stack) {
|
|
628
490
|
if (stack === null || stack.frames.length === 0)
|
|
629
491
|
return null;
|