@descryy/runtime-browser 0.2.0 → 0.4.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 (37) hide show
  1. package/LICENSE +6 -0
  2. package/dist/browser-action-collector.d.ts.map +1 -1
  3. package/dist/browser-action-collector.js +22 -2
  4. package/dist/browser-action-collector.js.map +1 -1
  5. package/dist/browser-console-collector.d.ts +10 -18
  6. package/dist/browser-console-collector.d.ts.map +1 -1
  7. package/dist/browser-console-collector.js +24 -47
  8. package/dist/browser-console-collector.js.map +1 -1
  9. package/dist/browser-network-collector.d.ts +17 -0
  10. package/dist/browser-network-collector.d.ts.map +1 -1
  11. package/dist/browser-network-collector.js +70 -1
  12. package/dist/browser-network-collector.js.map +1 -1
  13. package/dist/browser-session.d.ts +46 -78
  14. package/dist/browser-session.d.ts.map +1 -1
  15. package/dist/browser-session.js +21 -41
  16. package/dist/browser-session.js.map +1 -1
  17. package/dist/collector-version.d.ts +3 -6
  18. package/dist/collector-version.d.ts.map +1 -1
  19. package/dist/collector-version.js +3 -6
  20. package/dist/collector-version.js.map +1 -1
  21. package/dist/dom-evidence.d.ts +3 -6
  22. package/dist/dom-evidence.d.ts.map +1 -1
  23. package/dist/dom-evidence.js +3 -6
  24. package/dist/dom-evidence.js.map +1 -1
  25. package/dist/frontend-initiator-capture.d.ts +29 -64
  26. package/dist/frontend-initiator-capture.d.ts.map +1 -1
  27. package/dist/frontend-initiator-capture.js +31 -67
  28. package/dist/frontend-initiator-capture.js.map +1 -1
  29. package/dist/network-correlation.d.ts +4 -7
  30. package/dist/network-correlation.d.ts.map +1 -1
  31. package/dist/network-correlation.js +4 -7
  32. package/dist/network-correlation.js.map +1 -1
  33. package/dist/script-url-mapping.d.ts +11 -22
  34. package/dist/script-url-mapping.d.ts.map +1 -1
  35. package/dist/script-url-mapping.js +18 -47
  36. package/dist/script-url-mapping.js.map +1 -1
  37. package/package.json +9 -4
@@ -1,49 +1,30 @@
1
1
  /**
2
- * Fetch-initiator capture — the page-side half of resolving *which*
3
- * frontend function issued an observed request, not merely which functions
4
- * the graph says are capable of it (`resolveFrontendCallers`'s own
5
- * structural limit, `@descryy/runtime-graph-correlator`).
2
+ * Fetch-initiator capture: page-side half of resolving which frontend
3
+ * function issued an observed request (structural capability alone is
4
+ * `resolveFrontendCallers`'s job, in `@descryy/runtime-graph-correlator`).
6
5
  *
7
- * **This is a write, not a read**, in the sense
8
- * `PROPOSAL-code-injection-into-observed-processes.md` asks every injection
9
- * mechanism to state for itself: the wrapped `fetch()` adds a real header
10
- * (`x-descry-initiator-id`) to a real outgoing request. That line does not
11
- * run when Descry is not attached — a same-origin request can pick up a
12
- * fresh CORS preflight it would not otherwise have had. That side effect is
13
- * the one `PROPOSAL-observed-frontend-caller-needs-initiator-capture.md`
14
- * asked a human to sign off on before this was built; nothing here reopens
15
- * that call.
6
+ * This is a write, not a read: the wrapped fetch() adds a real header
7
+ * (`x-descry-initiator-id`) to a real outgoing request, which can trigger a
8
+ * fresh CORS preflight on a same-origin request. Signed off in
9
+ * `PROPOSAL-observed-frontend-caller-needs-initiator-capture.md`.
16
10
  *
17
- * The call-site stack itself never travels over the wire that carries the
18
- * application's own traffic — only a short, opaque per-call id does, as the
19
- * header value. The stack text instead travels over Playwright's own CDP
20
- * binding channel (`page.exposeFunction`), straight to `take()` below,
21
- * keyed by that same id.
11
+ * The call-site stack never travels with application traffic -- only an
12
+ * opaque per-call id does, as the header value. The stack text travels
13
+ * over Playwright's CDP binding (`page.exposeFunction`) to `take()` below.
22
14
  *
23
- * **Why the real `fetch()` is held until the exposed call returns.** Firing
24
- * the buffering call and the real request concurrently would make
25
- * correctness depend on which of two independent async channels — the CDP
26
- * binding call, and Chromium's own network stack — happens to land at this
27
- * process first. That is exactly the kind of timing assumption §27's
28
- * "similarity must never be treated as causality" rules out. Awaiting the
29
- * binding call before dispatching `fetch` makes the ordering true by
30
- * construction instead: the real request cannot be sent until this module
31
- * has already buffered the stack it will be looked up by.
15
+ * The real fetch() is held until the exposed call returns, so ordering is
16
+ * true by construction rather than a race between the CDP binding and
17
+ * Chromium's network stack (§27: never treat timing as causality).
32
18
  *
33
- * Chromium only — `page.addInitScript` + `page.exposeFunction` is the
34
- * mechanism, and no non-CDP-based engine here exposes an equivalent pair.
35
- * `fetch()` only, not `XMLHttpRequest`. Both boundaries are the scope
36
- * `PROPOSAL-observed-frontend-caller-needs-initiator-capture.md` drew, not
37
- * an oversight of this module.
19
+ * Chromium only (`page.addInitScript`/`page.exposeFunction`), fetch() only
20
+ * (not XMLHttpRequest) -- scope drawn by the proposal above, not an
21
+ * oversight.
38
22
  */
39
23
  export const INITIATOR_HEADER_NAME = "x-descry-initiator-id";
40
24
  /**
41
- * Bound on stacks buffered but never claimed. Without it, every request
42
- * whose id is never looked up — the header stripped in transit, a page that
43
- * never triggers a matching `onRequest` at all — would grow this map for
44
- * the entire life of the page. FIFO eviction via `Map`'s own insertion
45
- * order; a stack this old was never going to be claimed by the time
46
- * something newer needs the room.
25
+ * Cap on stacks buffered but never claimed (stripped header, no matching
26
+ * request) -- otherwise this map grows for the page's whole life. FIFO
27
+ * eviction via Map's own insertion order.
47
28
  */
48
29
  const MAX_BUFFERED_STACKS = 2000;
49
30
  const CAPTURE_BINDING_NAME = "__descryCaptureInitiatorStack";
@@ -70,16 +51,10 @@ export function createFrontendInitiatorCapture() {
70
51
  };
71
52
  }
72
53
  /**
73
- * The injected wrapper, as a plain string — `page.addInitScript`'s
74
- * string form, so this never needs its own transpilation step and runs
75
- * exactly as written in the page's own JS engine, not this process's.
76
- *
77
- * Every call site outcome degrades to "no id header" or "no functionName"
78
- * rather than throwing: `bind(...)` is wrapped in its own `.catch`, and a
79
- * `fetch()` whose `input` is neither a string, `URL`, nor `Request` still
80
- * reaches `originalFetch` unmodified so the native `TypeError` a caller
81
- * would have gotten anyway is the one they still get — this wrapper adds a
82
- * header, it does not change what counts as a valid `fetch()` call.
54
+ * Injected wrapper as a plain string (addInitScript's string form; runs
55
+ * unmodified in the page's own JS engine). Failures degrade silently:
56
+ * bind() is wrapped in .catch, and a non-string/URL/Request fetch input
57
+ * still reaches originalFetch unmodified, so callers get their normal error.
83
58
  */
84
59
  function initScriptSource() {
85
60
  return `(() => {
@@ -110,27 +85,16 @@ function initScriptSource() {
110
85
  }
111
86
  const FRAME_LINE = /^\s*at\s/;
112
87
  /**
113
- * Strips `descryFetch`'s own frame from a raw V8 stack, so the remaining
114
- * text's first frame is the real call site rather than this module's own
115
- * wrapper.
88
+ * Strips descryFetch's own frame from a raw V8 stack so the first
89
+ * remaining frame is the real call site.
116
90
  *
117
- * **Why this can be a fixed "drop the first frame line" rule rather than a
118
- * search for a named function.** `new Error().stack` is captured
119
- * synchronously, as the wrapper's first statement — before the `await`-
120
- * shaped `.then(dispatch)` chain below it ever runs. V8's stack is built
121
- * from the synchronous call chain live at capture time, so the innermost
122
- * frame is deterministically `descryFetch` itself, every time, regardless
123
- * of what called it. Stripping by *position* (first frame line) rather than
124
- * by *name* also survives minification of this wrapper's own name, which a
125
- * name-based strip would not.
91
+ * Works as a fixed "drop first line" rule rather than a name search:
92
+ * Error().stack is captured synchronously as the wrapper's first statement,
93
+ * so the innermost frame is always descryFetch regardless of caller --
94
+ * stripping by position also survives minification of the wrapper's name.
126
95
  *
127
- * Applied to raw text, before parsing — same reasoning
128
- * `rewriteScriptUrls`'s own doc gives for rewriting before rather than
129
- * after: `createV8StackTraceParser` already treats frame 0 of whatever it
130
- * receives as `primaryFrameIndex`, so removing the wrapper's line here
131
- * means the parser's existing, already-tested convention does the right
132
- * thing unmodified, instead of teaching every consumer a second index to
133
- * skip.
96
+ * Applied to raw text before parsing, so createV8StackTraceParser's
97
+ * existing primaryFrameIndex convention (frame 0) just works.
134
98
  */
135
99
  export function stripInitiatorWrapperFrame(rawStackText) {
136
100
  const lines = rawStackText.split(/\r\n|\r|\n/);
@@ -1 +1 @@
1
- {"version":3,"file":"frontend-initiator-capture.js","sourceRoot":"","sources":["../src/frontend-initiator-capture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAIH,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAuB,CAAC;AAE7D;;;;;;;GAOG;AACH,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAEjC,MAAM,oBAAoB,GAAG,+BAA+B,CAAC;AAwB7D,MAAM,UAAU,8BAA8B;IAC5C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE3C,SAAS,MAAM,CAAC,EAAU,EAAE,SAAiB;QAC3C,IAAI,QAAQ,CAAC,IAAI,IAAI,mBAAmB,EAAE,CAAC;YACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YAC5C,IAAI,MAAM,KAAK,SAAS;gBAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpD,CAAC;QACD,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,IAAU;YACtB,MAAM,IAAI,CAAC,cAAc,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;YACxD,MAAM,IAAI,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,IAAI,CAAC,EAAU;YACb,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACpB,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,gBAAgB;IACvB,OAAO;;;0BAGiB,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;;;;;;;;;;;sBAaxC,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC;;;;;;;;QAQnD,CAAC;AACT,CAAC;AAED,MAAM,UAAU,GAAG,UAAU,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,0BAA0B,CAAC,YAAoB;IAC7D,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACpE,IAAI,UAAU,KAAK,CAAC,CAAC;QAAE,OAAO,YAAY,CAAC;IAC3C,OAAO,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpF,CAAC"}
1
+ {"version":3,"file":"frontend-initiator-capture.js","sourceRoot":"","sources":["../src/frontend-initiator-capture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAuB,CAAC;AAE7D;;;;GAIG;AACH,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAEjC,MAAM,oBAAoB,GAAG,+BAA+B,CAAC;AAgB7D,MAAM,UAAU,8BAA8B;IAC5C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE3C,SAAS,MAAM,CAAC,EAAU,EAAE,SAAiB;QAC3C,IAAI,QAAQ,CAAC,IAAI,IAAI,mBAAmB,EAAE,CAAC;YACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YAC5C,IAAI,MAAM,KAAK,SAAS;gBAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpD,CAAC;QACD,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,IAAU;YACtB,MAAM,IAAI,CAAC,cAAc,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;YACxD,MAAM,IAAI,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,IAAI,CAAC,EAAU;YACb,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACpB,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,gBAAgB;IACvB,OAAO;;;0BAGiB,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;;;;;;;;;;;sBAaxC,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC;;;;;;;;QAQnD,CAAC;AACT,CAAC;AAED,MAAM,UAAU,GAAG,UAAU,CAAC;AAE9B;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,0BAA0B,CAAC,YAAoB;IAC7D,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACpE,IAAI,UAAU,KAAK,CAAC,CAAC;QAAE,OAAO,YAAY,CAAC;IAC3C,OAAO,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpF,CAAC"}
@@ -1,11 +1,8 @@
1
1
  /**
2
- * Correlation-id extraction from real HTTP headers (structured access, not
3
- * text regex) -- covers the top three levels of plan §10's six-level
4
- * preference order: trace ID, request ID, W3C trace context. Direct header
5
- * access makes the W3C case cheap here in a way it wasn't for
6
- * `backend-observation`'s `extractCorrelationIds`, which only ever sees
7
- * unstructured log text and explicitly cuts W3C parsing as a documented
8
- * V1 gap -- this collector doesn't need that same cut.
2
+ * Correlation-id extraction from HTTP headers -- top three of §10's six
3
+ * levels (trace ID, request ID, W3C trace context). Structured header
4
+ * access makes W3C parsing cheap here, unlike `backend-observation`'s
5
+ * `extractCorrelationIds` (log-text only, documents W3C as a V1 gap).
9
6
  */
10
7
  export interface HeaderCorrelationIds {
11
8
  readonly traceId: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"network-correlation.d.ts","sourceRoot":"","sources":["../src/network-correlation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAUD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,oBAAoB,CAS3G"}
1
+ {"version":3,"file":"network-correlation.d.ts","sourceRoot":"","sources":["../src/network-correlation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAUD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,oBAAoB,CAS3G"}
@@ -1,11 +1,8 @@
1
1
  /**
2
- * Correlation-id extraction from real HTTP headers (structured access, not
3
- * text regex) -- covers the top three levels of plan §10's six-level
4
- * preference order: trace ID, request ID, W3C trace context. Direct header
5
- * access makes the W3C case cheap here in a way it wasn't for
6
- * `backend-observation`'s `extractCorrelationIds`, which only ever sees
7
- * unstructured log text and explicitly cuts W3C parsing as a documented
8
- * V1 gap -- this collector doesn't need that same cut.
2
+ * Correlation-id extraction from HTTP headers -- top three of §10's six
3
+ * levels (trace ID, request ID, W3C trace context). Structured header
4
+ * access makes W3C parsing cheap here, unlike `backend-observation`'s
5
+ * `extractCorrelationIds` (log-text only, documents W3C as a V1 gap).
9
6
  */
10
7
  // W3C Trace Context: "00-<32 hex trace-id>-<16 hex parent-id>-<2 hex flags>".
11
8
  const TRACEPARENT_PATTERN = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/i;
@@ -1 +1 @@
1
- {"version":3,"file":"network-correlation.js","sourceRoot":"","sources":["../src/network-correlation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,8EAA8E;AAC9E,MAAM,mBAAmB,GAAG,8DAA8D,CAAC;AAO3F,SAAS,WAAW,CAAC,OAAyC,EAAE,KAAwB;IACtF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IAC5D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,OAAyC;IACnF,MAAM,eAAe,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC;IACzE,MAAM,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAE3G,OAAO;QACL,OAAO,EAAE,eAAe,IAAI,UAAU;QACtC,SAAS,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;KAChE,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"network-correlation.js","sourceRoot":"","sources":["../src/network-correlation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,8EAA8E;AAC9E,MAAM,mBAAmB,GAAG,8DAA8D,CAAC;AAO3F,SAAS,WAAW,CAAC,OAAyC,EAAE,KAAwB;IACtF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IAC5D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,OAAyC;IACnF,MAAM,eAAe,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC;IACzE,MAAM,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAE3G,OAAO;QACL,OAAO,EAAE,eAAe,IAAI,UAAU;QACtC,SAAS,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;KAChE,CAAC;AACJ,CAAC"}
@@ -1,29 +1,18 @@
1
1
  /**
2
- * RT-023's collector half: rewriting a browser script URL to the on-disk
3
- * file it was served from, so a page-error stack can be parsed into real
4
- * `SourceLocation`s instead of preserved as text.
2
+ * RT-023 collector half: rewrites a script URL to its on-disk file so a
3
+ * page-error stack parses into real `SourceLocation`s instead of text.
5
4
  *
6
- * **The rewrite happens on the stack text, before parsing, deliberately.**
7
- * A `StackTraceParser` and its `SourceLocationResolver` already know how to
8
- * turn `at f (/abs/path.js:12:5)` into a resolved location — including
9
- * source-map handling and staleness detection, which took real work to get
10
- * right. Rewriting the URL first lets all of that apply unchanged, where
11
- * post-processing parsed frames would mean reimplementing it here and
12
- * keeping two copies honest.
5
+ * Rewrite happens on the stack text before parsing, so the existing
6
+ * `StackTraceParser`/`SourceLocationResolver` (source maps, staleness)
7
+ * apply unchanged rather than being reimplemented here.
13
8
  *
14
- * **What this can and cannot claim.** `CollectorContext.resolveSourceRoot`
15
- * answers "which service's declared root does this origin belong to", and
16
- * `ConfiguredServiceRoot.declaredCwd` is named for what it is: configured,
17
- * not verified. So a successful rewrite proves the origin maps to a
18
- * directory — **not** that the file the browser executed came from the
19
- * checkout in it. The downstream resolver's own existence check is the real
20
- * refusal point, and an unmapped or non-existent path degrades to the URL
21
- * itself rather than to a fabricated file. That is RT-031's class again:
22
- * a match on an observable proxy is not proof of the underlying identity.
9
+ * A successful rewrite proves the origin maps to a directory, not that the
10
+ * executed file came from that checkout (`declaredCwd` is configured, not
11
+ * verified) -- the downstream resolver's existence check is the real
12
+ * refusal point. RT-031's class: a match on a proxy isn't proof of identity.
23
13
  *
24
- * Nothing is rewritten when the resolver refuses, when it is absent, or
25
- * when the URL is not one this execution spawned — a CDN script, a browser
26
- * extension, `about:blank`. Those are routine, not failures.
14
+ * Nothing is rewritten when the resolver refuses, is absent, or the URL
15
+ * wasn't spawned by this execution (CDN script, extension, `about:blank`).
27
16
  */
28
17
  import type { ServiceRootLookup } from "@descryy/runtime-contracts";
29
18
  export type SourceRootResolver = (origin: string) => ServiceRootLookup;
@@ -1 +1 @@
1
- {"version":3,"file":"script-url-mapping.d.ts","sourceRoot":"","sources":["../src/script-url-mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEpE,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,iBAAiB,CAAC;AAKvE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4GAA4G;IAC5G,QAAQ,CAAC,QAAQ,EAAE,SAAS;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;CACxH;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,kBAAkB,GAAG,SAAS,GAAG,gBAAgB,CAoExH"}
1
+ {"version":3,"file":"script-url-mapping.d.ts","sourceRoot":"","sources":["../src/script-url-mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEpE,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,iBAAiB,CAAC;AAKvE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4GAA4G;IAC5G,QAAQ,CAAC,QAAQ,EAAE,SAAS;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;CACxH;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,kBAAkB,GAAG,SAAS,GAAG,gBAAgB,CAkDxH"}
@@ -1,29 +1,18 @@
1
1
  /**
2
- * RT-023's collector half: rewriting a browser script URL to the on-disk
3
- * file it was served from, so a page-error stack can be parsed into real
4
- * `SourceLocation`s instead of preserved as text.
2
+ * RT-023 collector half: rewrites a script URL to its on-disk file so a
3
+ * page-error stack parses into real `SourceLocation`s instead of text.
5
4
  *
6
- * **The rewrite happens on the stack text, before parsing, deliberately.**
7
- * A `StackTraceParser` and its `SourceLocationResolver` already know how to
8
- * turn `at f (/abs/path.js:12:5)` into a resolved location — including
9
- * source-map handling and staleness detection, which took real work to get
10
- * right. Rewriting the URL first lets all of that apply unchanged, where
11
- * post-processing parsed frames would mean reimplementing it here and
12
- * keeping two copies honest.
5
+ * Rewrite happens on the stack text before parsing, so the existing
6
+ * `StackTraceParser`/`SourceLocationResolver` (source maps, staleness)
7
+ * apply unchanged rather than being reimplemented here.
13
8
  *
14
- * **What this can and cannot claim.** `CollectorContext.resolveSourceRoot`
15
- * answers "which service's declared root does this origin belong to", and
16
- * `ConfiguredServiceRoot.declaredCwd` is named for what it is: configured,
17
- * not verified. So a successful rewrite proves the origin maps to a
18
- * directory — **not** that the file the browser executed came from the
19
- * checkout in it. The downstream resolver's own existence check is the real
20
- * refusal point, and an unmapped or non-existent path degrades to the URL
21
- * itself rather than to a fabricated file. That is RT-031's class again:
22
- * a match on an observable proxy is not proof of the underlying identity.
9
+ * A successful rewrite proves the origin maps to a directory, not that the
10
+ * executed file came from that checkout (`declaredCwd` is configured, not
11
+ * verified) -- the downstream resolver's existence check is the real
12
+ * refusal point. RT-031's class: a match on a proxy isn't proof of identity.
23
13
  *
24
- * Nothing is rewritten when the resolver refuses, when it is absent, or
25
- * when the URL is not one this execution spawned — a CDN script, a browser
26
- * extension, `about:blank`. Those are routine, not failures.
14
+ * Nothing is rewritten when the resolver refuses, is absent, or the URL
15
+ * wasn't spawned by this execution (CDN script, extension, `about:blank`).
27
16
  */
28
17
  import { statSync } from "node:fs";
29
18
  import { join } from "node:path";
@@ -35,9 +24,7 @@ export function rewriteScriptUrls(stackText, resolveSourceRoot) {
35
24
  }
36
25
  const outcomes = [];
37
26
  const text = stackText.replace(SCRIPT_URL, (match) => {
38
- // Strip the stack's own `:line:column` suffix before treating the rest
39
- // as a URL — `new URL()` would otherwise parse `:12` as part of the
40
- // path and produce a file that never existed.
27
+ // strip the stack's ":line:column" suffix first -- new URL() would parse ":12" as part of the path.
41
28
  const suffix = /:(\d+):(\d+)$/.exec(match);
42
29
  const urlText = suffix === null ? match : match.slice(0, match.length - suffix[0].length);
43
30
  let url;
@@ -53,24 +40,12 @@ export function rewriteScriptUrls(stackText, resolveSourceRoot) {
53
40
  outcomes.push({ url: match, mapped: null, reason: lookup.message });
54
41
  return match;
55
42
  }
56
- // `url.pathname` is always absolute; `join` would otherwise treat a
57
- // leading slash as a fresh root and discard the service's own cwd.
43
+ // url.pathname is always absolute; join would otherwise treat the leading slash as a fresh root and drop declaredCwd.
58
44
  const candidate = join(lookup.root.declaredCwd, url.pathname.replace(/^\/+/, ""));
59
- // **`isFile()`, not `existsSync` — and the difference is not pedantry.**
60
- // An inline `<script>` on a page served at `/` produces frames V8 names
61
- // after the *document* URL, whose pathname is `/` and nothing else. That
62
- // joins to `declaredCwd` itself: a real directory, so `existsSync` said
63
- // yes and this function rewrote a stack frame to a directory no browser
64
- // ever executed. The downstream `SourceLocationResolver` then reached
65
- // `readFileSync(directory)`, threw `EISDIR`, and the whole
66
- // `NETWORK_REQUEST` row was lost with it — an observation discarded to
67
- // protect a nullable field. `statSync` answers the question that was
68
- // always being asked (is there a FILE here?) rather than the weaker one
69
- // that happened to be cheap.
70
- //
71
- // `statSync` throws on a missing path where `existsSync` returned false,
72
- // so the absence case is caught rather than propagated: both branches end
73
- // in the same refusal, with the reason naming which one it was.
45
+ // isFile(), not existsSync: an inline <script> on a page served at "/" resolves to declaredCwd
46
+ // itself -- a real directory, so existsSync said yes and readFileSync(directory) threw EISDIR,
47
+ // dropping the whole NETWORK_REQUEST row. statSync also catches the missing-path case, so both
48
+ // branches end in the same refusal, with the reason naming which one it was.
74
49
  let isFile;
75
50
  try {
76
51
  isFile = statSync(candidate).isFile();
@@ -80,11 +55,7 @@ export function rewriteScriptUrls(stackText, resolveSourceRoot) {
80
55
  return match;
81
56
  }
82
57
  if (!isFile) {
83
- // The origin maps to a root and the pathname points at something that
84
- // is not a file in it — a directory (the `/` case above), a socket, a
85
- // device. Reported rather than rewritten: a path that is not a source
86
- // file is worse than the URL, which at least says truthfully where the
87
- // browser got it.
58
+ // not a file (dir/socket/device) -- reported rather than rewritten, since that's worse than the honest URL.
88
59
  outcomes.push({ url: match, mapped: null, reason: `resolved to "${candidate}", which is not a file on disk` });
89
60
  return match;
90
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"script-url-mapping.js","sourceRoot":"","sources":["../src/script-url-mapping.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"script-url-mapping.js","sourceRoot":"","sources":["../src/script-url-mapping.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@descryy/runtime-browser",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Browser runtime: launch, isolated context, navigation, click/type/wait, DOM inspection, screenshots, console/page-error/network capture, all through the Collector contract (plan \u00a77/\u00a78). See documents/decisions-inbox/PROPOSAL-B-playwright-for-browser-runtime.md for the Playwright-vs-CDP call.",
6
6
  "license": "UNLICENSED",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/descryhq-wq/descry-runtime.git",
10
+ "directory": "packages/browser"
11
+ },
7
12
  "engines": {
8
13
  "node": ">=22.5"
9
14
  },
@@ -24,10 +29,10 @@
24
29
  "build": "tsc -b"
25
30
  },
26
31
  "dependencies": {
27
- "@descryy/runtime-contracts": "0.2.0",
28
- "@descryy/runtime-backend-observation": "0.2.0",
32
+ "@descryy/runtime-contracts": "0.3.0",
33
+ "@descryy/runtime-backend-observation": "0.3.0",
29
34
  "playwright-core": "^1.62.1",
30
- "@descryy/runtime-adapter-typescript": "0.2.0"
35
+ "@descryy/runtime-adapter-typescript": "0.3.0"
31
36
  },
32
37
  "devDependencies": {
33
38
  "@descryy/runtime-controller": "*",