@descryy/runtime-adapter-typescript 0.2.0 → 0.3.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/LICENSE +6 -0
- package/dist/node-source-location-resolver.d.ts +15 -22
- package/dist/node-source-location-resolver.d.ts.map +1 -1
- package/dist/node-source-location-resolver.js +23 -40
- package/dist/node-source-location-resolver.js.map +1 -1
- package/dist/typescript-runtime-adapter.d.ts +4 -5
- package/dist/typescript-runtime-adapter.d.ts.map +1 -1
- package/dist/typescript-runtime-adapter.js +11 -19
- package/dist/typescript-runtime-adapter.js.map +1 -1
- package/dist/v8-frame-parsing.d.ts +18 -53
- package/dist/v8-frame-parsing.d.ts.map +1 -1
- package/dist/v8-frame-parsing.js +24 -70
- package/dist/v8-frame-parsing.js.map +1 -1
- package/dist/v8-stack-trace-parser.d.ts +10 -15
- package/dist/v8-stack-trace-parser.d.ts.map +1 -1
- package/dist/v8-stack-trace-parser.js +22 -46
- package/dist/v8-stack-trace-parser.js.map +1 -1
- package/package.json +9 -4
package/LICENSE
ADDED
|
@@ -1,28 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Node/TypeScript `SourceLocationResolver` — resolves a V8-captured
|
|
3
|
-
* compiled-code location back to authored source through a
|
|
4
|
-
*
|
|
5
|
-
* (RT-003) honestly rather than defaulting.
|
|
3
|
+
* compiled-code location back to authored source through a
|
|
4
|
+
* `//# sourceMappingURL` map, when one exists, and classifies
|
|
5
|
+
* `SourceLocation.reliability` (RT-003) honestly rather than defaulting.
|
|
6
6
|
*
|
|
7
|
-
* - No `sourceMappingURL` reference
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* -
|
|
13
|
-
*
|
|
14
|
-
* -
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* -
|
|
18
|
-
*
|
|
19
|
-
* → `side-artifact-stale`. This is the one value nothing else in this
|
|
20
|
-
* build exercises without this check actually running — comparing
|
|
21
|
-
* `sourcesContent` against disk rather than punting on staleness
|
|
22
|
-
* detection entirely.
|
|
23
|
-
* - A location with no file on disk at all (`node:events`, a stdlib
|
|
24
|
-
* internal) → `self-contained`. There was never an artifact to lose;
|
|
25
|
-
* the captured location is the accurate answer.
|
|
7
|
+
* - No `sourceMappingURL` reference → `self-contained` (the compiled
|
|
8
|
+
* location is itself accurate; NOT `side-artifact-missing`, which means
|
|
9
|
+
* "expected a map and couldn't get it").
|
|
10
|
+
* - Reference exists but the map can't be found/parsed, or has no entry
|
|
11
|
+
* for this line:column → `side-artifact-missing`.
|
|
12
|
+
* - Reference exists, position resolves, and `sourcesContent` is absent or
|
|
13
|
+
* matches on-disk → `side-artifact-resolved`.
|
|
14
|
+
* - Reference exists, position resolves, `sourcesContent` present AND
|
|
15
|
+
* on-disk content differs → `side-artifact-stale` (the one value that
|
|
16
|
+
* requires comparing `sourcesContent` against disk rather than punting).
|
|
17
|
+
* - No file on disk at all (`node:events`, a stdlib internal) →
|
|
18
|
+
* `self-contained` — no artifact was ever there to lose.
|
|
26
19
|
*/
|
|
27
20
|
import type { SourceLocationResolver } from "@descryy/runtime-backend-observation";
|
|
28
21
|
export declare function createNodeSourceLocationResolver(): SourceLocationResolver;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-source-location-resolver.d.ts","sourceRoot":"","sources":["../src/node-source-location-resolver.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"node-source-location-resolver.d.ts","sourceRoot":"","sources":["../src/node-source-location-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,KAAK,EAAoB,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAmIrG,wBAAgB,gCAAgC,IAAI,sBAAsB,CAMzE"}
|
|
@@ -1,28 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Node/TypeScript `SourceLocationResolver` — resolves a V8-captured
|
|
3
|
-
* compiled-code location back to authored source through a
|
|
4
|
-
*
|
|
5
|
-
* (RT-003) honestly rather than defaulting.
|
|
3
|
+
* compiled-code location back to authored source through a
|
|
4
|
+
* `//# sourceMappingURL` map, when one exists, and classifies
|
|
5
|
+
* `SourceLocation.reliability` (RT-003) honestly rather than defaulting.
|
|
6
6
|
*
|
|
7
|
-
* - No `sourceMappingURL` reference
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* -
|
|
13
|
-
*
|
|
14
|
-
* -
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* -
|
|
18
|
-
*
|
|
19
|
-
* → `side-artifact-stale`. This is the one value nothing else in this
|
|
20
|
-
* build exercises without this check actually running — comparing
|
|
21
|
-
* `sourcesContent` against disk rather than punting on staleness
|
|
22
|
-
* detection entirely.
|
|
23
|
-
* - A location with no file on disk at all (`node:events`, a stdlib
|
|
24
|
-
* internal) → `self-contained`. There was never an artifact to lose;
|
|
25
|
-
* the captured location is the accurate answer.
|
|
7
|
+
* - No `sourceMappingURL` reference → `self-contained` (the compiled
|
|
8
|
+
* location is itself accurate; NOT `side-artifact-missing`, which means
|
|
9
|
+
* "expected a map and couldn't get it").
|
|
10
|
+
* - Reference exists but the map can't be found/parsed, or has no entry
|
|
11
|
+
* for this line:column → `side-artifact-missing`.
|
|
12
|
+
* - Reference exists, position resolves, and `sourcesContent` is absent or
|
|
13
|
+
* matches on-disk → `side-artifact-resolved`.
|
|
14
|
+
* - Reference exists, position resolves, `sourcesContent` present AND
|
|
15
|
+
* on-disk content differs → `side-artifact-stale` (the one value that
|
|
16
|
+
* requires comparing `sourcesContent` against disk rather than punting).
|
|
17
|
+
* - No file on disk at all (`node:events`, a stdlib internal) →
|
|
18
|
+
* `self-contained` — no artifact was ever there to lose.
|
|
26
19
|
*/
|
|
27
20
|
import { existsSync, readFileSync } from "node:fs";
|
|
28
21
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -89,17 +82,15 @@ function sideArtifactMissing(frame, file, mappingUrl) {
|
|
|
89
82
|
};
|
|
90
83
|
}
|
|
91
84
|
function resolveSync(frame) {
|
|
92
|
-
// V8 always names a file
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
// moment some other caller reuses this resolver.
|
|
85
|
+
// unreachable from the V8 parser (V8 always names a file); the type
|
|
86
|
+
// admits null because a JVM frame can omit it, and a reused resolver
|
|
87
|
+
// shouldn't crash on that.
|
|
96
88
|
if (frame.line === null || frame.file === null) {
|
|
97
89
|
return selfContained(frame, frame.file);
|
|
98
90
|
}
|
|
99
91
|
const file = normalizeCompiledFile(frame.file);
|
|
100
92
|
if (!existsSync(file)) {
|
|
101
|
-
// e.g. "node:events" --
|
|
102
|
-
// Nothing was ever there to lose; the captured location is accurate.
|
|
93
|
+
// e.g. "node:events" -- stdlib internal, no file on disk, nothing to lose
|
|
103
94
|
return selfContained(frame, file);
|
|
104
95
|
}
|
|
105
96
|
const compiledText = readFileSync(file, "utf8");
|
|
@@ -108,18 +99,14 @@ function resolveSync(frame) {
|
|
|
108
99
|
return selfContained(frame, file);
|
|
109
100
|
}
|
|
110
101
|
if (frame.column === null) {
|
|
111
|
-
//
|
|
112
|
-
// column — trying anyway would silently substitute a guessed column
|
|
113
|
-
// and report it as resolved. Report what was actually captured
|
|
114
|
-
// instead of a fabricated mapping.
|
|
102
|
+
// no column -> can't walk the map to a specific position without guessing one
|
|
115
103
|
return selfContained(frame, file);
|
|
116
104
|
}
|
|
117
105
|
const loaded = loadTraceMap(file, mappingUrl);
|
|
118
106
|
if (loaded === undefined) {
|
|
119
107
|
return sideArtifactMissing(frame, file, mappingUrl);
|
|
120
108
|
}
|
|
121
|
-
//
|
|
122
|
-
// Column is 1-based in V8 stacks but 0-based in trace-mapping's needle.
|
|
109
|
+
// line is 1-based (matches originalPositionFor); column is 1-based in V8 stacks but 0-based in trace-mapping's needle
|
|
123
110
|
const original = originalPositionFor(loaded.map, { line: frame.line, column: Math.max(0, frame.column - 1) });
|
|
124
111
|
if (original.source === null || original.line === null || original.column === null) {
|
|
125
112
|
return sideArtifactMissing(frame, file, mappingUrl);
|
|
@@ -135,15 +122,11 @@ function resolveSync(frame) {
|
|
|
135
122
|
reliability = "side-artifact-stale";
|
|
136
123
|
}
|
|
137
124
|
}
|
|
138
|
-
// embeddedContent
|
|
139
|
-
// compare against, so freshness is simply unverifiable — resolved stays
|
|
140
|
-
// the honest answer rather than downgrading on the absence of a check.
|
|
125
|
+
// embeddedContent null or on-disk unreadable: nothing to compare, freshness unverifiable, "resolved" stays honest
|
|
141
126
|
return {
|
|
142
127
|
file: onDiskPath,
|
|
143
128
|
line: original.line,
|
|
144
|
-
// trace-mapping
|
|
145
|
-
// 1-based to match how every other frame in this system is stated.
|
|
146
|
-
column: original.column + 1,
|
|
129
|
+
column: original.column + 1, // trace-mapping is 0-based; SourceLocation is 1-based like every other frame here
|
|
147
130
|
functionName: original.name ?? frame.functionName,
|
|
148
131
|
reliability,
|
|
149
132
|
resolvedVia: mappingUrl,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-source-location-resolver.js","sourceRoot":"","sources":["../src/node-source-location-resolver.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"node-source-location-resolver.js","sourceRoot":"","sources":["../src/node-source-location-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* neutral seam `@descryy/runtime-backend-observation` defines.
|
|
2
|
+
* Concrete `RuntimeAdapter` for TypeScript/Node. Wires this package's V8
|
|
3
|
+
* stack-trace parser, Node source-map resolver, and V8 line classifier
|
|
4
|
+
* into the language-neutral seam `@descryy/runtime-backend-observation`
|
|
5
|
+
* defines.
|
|
7
6
|
*/
|
|
8
7
|
import type { RuntimeAdapter } from "@descryy/runtime-backend-observation";
|
|
9
8
|
export interface TypeScriptRuntimeAdapterOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typescript-runtime-adapter.d.ts","sourceRoot":"","sources":["../src/typescript-runtime-adapter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"typescript-runtime-adapter.d.ts","sourceRoot":"","sources":["../src/typescript-runtime-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA2C,MAAM,sCAAsC,CAAC;AAYpH,MAAM,WAAW,+BAA+B;IAC9C,qGAAqG;IACrG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,8BAA8B,CAAC,OAAO,GAAE,+BAAoC,GAAG,cAAc,CA6B5G"}
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* neutral seam `@descryy/runtime-backend-observation` defines.
|
|
2
|
+
* Concrete `RuntimeAdapter` for TypeScript/Node. Wires this package's V8
|
|
3
|
+
* stack-trace parser, Node source-map resolver, and V8 line classifier
|
|
4
|
+
* into the language-neutral seam `@descryy/runtime-backend-observation`
|
|
5
|
+
* defines.
|
|
7
6
|
*/
|
|
8
7
|
import { createLogCollector } from "@descryy/runtime-backend-observation";
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// instrument this process instead, which is the defect
|
|
13
|
-
// `scripts/package-index-side-effects.test.mjs` now measures for.
|
|
8
|
+
// path constant only: fetch-instrumentation.ts replaces globalThis.fetch and is
|
|
9
|
+
// loaded via `node --import` INSIDE the observed process; importing it here
|
|
10
|
+
// would instrument this process instead (scripts/package-index-side-effects.test.mjs)
|
|
14
11
|
import { FETCH_PRELOAD_MODULE_PATH } from "@descryy/runtime-external-service-observation";
|
|
15
12
|
import { createV8LineClassifier } from "./v8-frame-parsing.js";
|
|
16
13
|
import { createNodeSourceLocationResolver } from "./node-source-location-resolver.js";
|
|
@@ -21,19 +18,14 @@ export function createTypeScriptRuntimeAdapter(options = {}) {
|
|
|
21
18
|
function createBackendCollectors(sources) {
|
|
22
19
|
return sources.map((source) => createLogCollector({
|
|
23
20
|
source,
|
|
24
|
-
//
|
|
25
|
-
// and a shared instance would let one process's trace arm another's.
|
|
26
|
-
classifier: createV8LineClassifier(),
|
|
21
|
+
classifier: createV8LineClassifier(), // one per source: stateful, a shared instance would let one trace arm another's
|
|
27
22
|
stackTraceParser,
|
|
28
23
|
...(options.service !== undefined ? { service: options.service } : {}),
|
|
29
24
|
}));
|
|
30
25
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
* unpatched `fetch` reference. It must sit between the interpreter and the
|
|
35
|
-
* script: `node --import <preload> server.mjs`.
|
|
36
|
-
*/
|
|
26
|
+
// `--import` loads the preload module before the entry script so the
|
|
27
|
+
// patch installs before any application code captures an unpatched
|
|
28
|
+
// `fetch` reference: `node --import <preload> server.mjs`.
|
|
37
29
|
function outboundHttpLaunch() {
|
|
38
30
|
return { interpreterArgs: ["--import", FETCH_PRELOAD_MODULE_PATH] };
|
|
39
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typescript-runtime-adapter.js","sourceRoot":"","sources":["../src/typescript-runtime-adapter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"typescript-runtime-adapter.js","sourceRoot":"","sources":["../src/typescript-runtime-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,gFAAgF;AAChF,4EAA4E;AAC5E,sFAAsF;AACtF,OAAO,EAAE,yBAAyB,EAAE,MAAM,+CAA+C,CAAC;AAE1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAOtE,MAAM,UAAU,8BAA8B,CAAC,UAA2C,EAAE;IAC1F,MAAM,sBAAsB,GAAG,gCAAgC,EAAE,CAAC;IAClE,MAAM,gBAAgB,GAAG,wBAAwB,CAAC,sBAAsB,CAAC,CAAC;IAE1E,SAAS,uBAAuB,CAAC,OAAuC;QACtE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAC5B,kBAAkB,CAAC;YACjB,MAAM;YACN,UAAU,EAAE,sBAAsB,EAAE,EAAE,gFAAgF;YACtH,gBAAgB;YAChB,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvE,CAAC,CACH,CAAC;IACJ,CAAC;IAED,qEAAqE;IACrE,mEAAmE;IACnE,2DAA2D;IAC3D,SAAS,kBAAkB;QACzB,OAAO,EAAE,eAAe,EAAE,CAAC,UAAU,EAAE,yBAAyB,CAAC,EAAE,CAAC;IACtE,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,YAAY;QACtB,gBAAgB;QAChB,sBAAsB;QACtB,uBAAuB;QACvB,kBAAkB;KACnB,CAAC;AACJ,CAAC"}
|
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* V8's `Error.stack` line shapes — the `LineClassifier` for
|
|
3
|
-
* `ExceptionBlockDetector` plus the raw-text-to-frame-parts extraction
|
|
4
|
-
*
|
|
3
|
+
* `ExceptionBlockDetector` plus the raw-text-to-frame-parts extraction
|
|
4
|
+
* feeding `SourceLocationResolver`.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* application context, which a start-anchored pattern would have missed
|
|
12
|
-
* entirely, misclassifying a genuine exception as an unattributed
|
|
13
|
-
* stack-trace block. The block-detector's own "header immediately
|
|
14
|
-
* followed by a frame line" rule keeps this safe from false positives: a
|
|
15
|
-
* log line that merely *mentions* an Error-shaped word with nothing
|
|
16
|
-
* frame-shaped after it never becomes an EXCEPTION.
|
|
6
|
+
* Header pattern is deliberately NOT anchored to line start: measured real
|
|
7
|
+
* output puts the error type mid-line behind a custom log prefix (e.g.
|
|
8
|
+
* `"[broken-invoices] DELETE /api/invoices/3 failed: TypeError: ..."`).
|
|
9
|
+
* Safe from false positives since the block-detector requires a header
|
|
10
|
+
* immediately followed by a frame line.
|
|
17
11
|
*/
|
|
18
12
|
export interface RawStackFrame {
|
|
19
13
|
readonly functionName: string | null;
|
|
20
|
-
/** Unparsed location text — `file:///abs/path.js:37:44`, `node:events:519:28`, or `<anonymous>` (no line:col, dropped by the caller rather than fabricated). */
|
|
21
14
|
readonly location: string;
|
|
22
15
|
}
|
|
23
16
|
export interface ParsedLocation {
|
|
@@ -27,49 +20,21 @@ export interface ParsedLocation {
|
|
|
27
20
|
}
|
|
28
21
|
export declare function isV8HeaderLine(line: string): boolean;
|
|
29
22
|
export declare function isV8FrameLine(line: string): boolean;
|
|
30
|
-
/**
|
|
31
|
-
* The `at fn (loc)` form specifically — a shape no other runtime in this
|
|
32
|
-
* repo produces, and therefore the anchor `createV8StackTraceParser` uses
|
|
33
|
-
* to decide that text is V8's at all. See its header for why a bare frame
|
|
34
|
-
* alone is not enough to make that call.
|
|
35
|
-
*/
|
|
23
|
+
/** `at fn (loc)` specifically — no other runtime here produces this shape, so `createV8StackTraceParser` anchors on it to decide text is V8's at all. */
|
|
36
24
|
export declare function isV8NamedFrameLine(line: string): boolean;
|
|
37
25
|
export declare function parseV8Frame(line: string): RawStackFrame | null;
|
|
38
|
-
/** `null` for a frame with no parseable line:column (e.g. `<anonymous>`) — never fabricated. */
|
|
39
26
|
export declare function parseLocation(location: string): ParsedLocation | null;
|
|
40
27
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* single parenthesis-free token, which is also precisely the shape of a
|
|
52
|
-
* bare V8 frame. RT-039 found this at the *parser* layer and fixed it there
|
|
53
|
-
* by requiring the whole text to contain a V8 header or a named `at fn
|
|
54
|
-
* (loc)` frame — and then reported that the matrix confirmed the classifier
|
|
55
|
-
* layer too. It had not: the classifier half of that matrix was inspecting
|
|
56
|
-
* an empty array (see `false-accept-matrix.test.ts`). With the matrix
|
|
57
|
-
* actually running, the V8 classifier opens **nine** blocks on one Rust
|
|
58
|
-
* backtrace.
|
|
59
|
-
*
|
|
60
|
-
* The rule here is the parser's anchor applied line by line, which is what
|
|
61
|
-
* a classifier can do that a set of predicates cannot: a **named** frame is
|
|
62
|
-
* always block content and arms the classifier; a **bare** frame is block
|
|
63
|
-
* content only once armed. A header arms it too.
|
|
64
|
-
*
|
|
65
|
-
* **The cost is the same one RT-039 already took and is restated rather
|
|
66
|
-
* than rediscovered:** a V8 trace consisting solely of bare frames does not
|
|
67
|
-
* open a block. Measured against four real V8 traces in RT-039, a bare
|
|
68
|
-
* frame occurs only where V8 has no function name, and Node's own internal
|
|
69
|
-
* frames — which follow every such throw — are all named, so the shape is
|
|
70
|
-
* rare. A disclosed recall loss, taken in the direction this project always
|
|
71
|
-
* takes it, over nine fabricated frames pointing into another language's
|
|
72
|
-
* standard library.
|
|
28
|
+
* Stateful `LineClassifier` (RT-035, RT-047). `FRAME_BARE` also matches a
|
|
29
|
+
* Rust backtrace's location lines (same `at` + one token shape as a bare
|
|
30
|
+
* V8 frame); RT-039 fixed the parser layer but the classifier wasn't
|
|
31
|
+
* covered by that matrix, and once it was, the V8 classifier opened nine
|
|
32
|
+
* blocks on one Rust backtrace. Fix: a named frame always arms and counts;
|
|
33
|
+
* a bare frame counts only once armed (a header arms it too). Cost: a
|
|
34
|
+
* trace of solely bare frames doesn't open a block — rare (measured
|
|
35
|
+
* against four real V8 traces: bare frames occur only with no function
|
|
36
|
+
* name, and Node's internal frames that follow are all named), a
|
|
37
|
+
* disclosed recall loss over nine fabricated foreign-stdlib frames.
|
|
73
38
|
*/
|
|
74
39
|
export declare function createV8LineClassifier(): {
|
|
75
40
|
isHeader(line: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v8-frame-parsing.d.ts","sourceRoot":"","sources":["../src/v8-frame-parsing.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"v8-frame-parsing.d.ts","sourceRoot":"","sources":["../src/v8-frame-parsing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAYD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,yJAAyJ;AACzJ,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAU/D;AAGD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAQrE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAChC,CAoBA"}
|
package/dist/v8-frame-parsing.js
CHANGED
|
@@ -1,40 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* V8's `Error.stack` line shapes — the `LineClassifier` for
|
|
3
|
-
* `ExceptionBlockDetector` plus the raw-text-to-frame-parts extraction
|
|
4
|
-
*
|
|
3
|
+
* `ExceptionBlockDetector` plus the raw-text-to-frame-parts extraction
|
|
4
|
+
* feeding `SourceLocationResolver`.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* application context, which a start-anchored pattern would have missed
|
|
12
|
-
* entirely, misclassifying a genuine exception as an unattributed
|
|
13
|
-
* stack-trace block. The block-detector's own "header immediately
|
|
14
|
-
* followed by a frame line" rule keeps this safe from false positives: a
|
|
15
|
-
* log line that merely *mentions* an Error-shaped word with nothing
|
|
16
|
-
* frame-shaped after it never becomes an EXCEPTION.
|
|
6
|
+
* Header pattern is deliberately NOT anchored to line start: measured real
|
|
7
|
+
* output puts the error type mid-line behind a custom log prefix (e.g.
|
|
8
|
+
* `"[broken-invoices] DELETE /api/invoices/3 failed: TypeError: ..."`).
|
|
9
|
+
* Safe from false positives since the block-detector requires a header
|
|
10
|
+
* immediately followed by a frame line.
|
|
17
11
|
*/
|
|
18
12
|
const HEADER_PATTERN = /\b(?<type>(?:[A-Z][\w$]*)?Error)(?::\s*(?<message>.*))?$/;
|
|
19
13
|
const FRAME_WITH_PARENS = /^\s*at\s+(?:new\s+)?(?<fn>.+?)\s+\((?<loc>[^()]+)\)\s*$/;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* cross-language control is what found this.** Without the exclusion this
|
|
26
|
-
* pattern matched `at com.example.Thrower.handler(Thrower.java:12)` — a JVM
|
|
27
|
-
* frame is `at` followed by a single non-whitespace run, exactly like a
|
|
28
|
-
* bare V8 frame. A real bare V8 frame never contains parentheses: once
|
|
29
|
-
* there is a function name, V8 emits the `fn (loc)` form instead. So the
|
|
30
|
-
* exclusion costs no genuine V8 frame and removes the whole false-accept.
|
|
31
|
-
*
|
|
32
|
-
* The parser was already safe here — `parseLocation` requires a trailing
|
|
33
|
-
* `:line:column` and refuses the JVM shape — but the *classifier* is what
|
|
34
|
-
* `ExceptionBlockDetector` uses to decide whether a line belongs to a
|
|
35
|
-
* block at all, so accepting a foreign runtime's frames there is enough for
|
|
36
|
-
* a mis-wired adapter to half-work rather than fail.
|
|
37
|
-
*/
|
|
14
|
+
// location-only frame: `at file:///app/x.js:1:1`, `at <anonymous>`. Parens excluded
|
|
15
|
+
// deliberately (found via the JVM adapter's cross-language control): without it this
|
|
16
|
+
// also matched a JVM frame (`at com.example.Thrower.handler(Thrower.java:12)`), since
|
|
17
|
+
// a real bare V8 frame never has parens -- once there's a name V8 emits `fn (loc)`.
|
|
18
|
+
// Needed at the classifier level too, not just the parser.
|
|
38
19
|
const FRAME_BARE = /^\s*at\s+(?<loc>[^\s()]+)\s*$/;
|
|
39
20
|
const LOCATION_WITH_LINE_COL = /^(?<file>.+):(?<line>\d+):(?<col>\d+)$/;
|
|
40
21
|
export function isV8HeaderLine(line) {
|
|
@@ -43,12 +24,7 @@ export function isV8HeaderLine(line) {
|
|
|
43
24
|
export function isV8FrameLine(line) {
|
|
44
25
|
return FRAME_WITH_PARENS.test(line) || FRAME_BARE.test(line);
|
|
45
26
|
}
|
|
46
|
-
/**
|
|
47
|
-
* The `at fn (loc)` form specifically — a shape no other runtime in this
|
|
48
|
-
* repo produces, and therefore the anchor `createV8StackTraceParser` uses
|
|
49
|
-
* to decide that text is V8's at all. See its header for why a bare frame
|
|
50
|
-
* alone is not enough to make that call.
|
|
51
|
-
*/
|
|
27
|
+
/** `at fn (loc)` specifically — no other runtime here produces this shape, so `createV8StackTraceParser` anchors on it to decide text is V8's at all. */
|
|
52
28
|
export function isV8NamedFrameLine(line) {
|
|
53
29
|
return FRAME_WITH_PARENS.test(line);
|
|
54
30
|
}
|
|
@@ -63,7 +39,7 @@ export function parseV8Frame(line) {
|
|
|
63
39
|
}
|
|
64
40
|
return null;
|
|
65
41
|
}
|
|
66
|
-
|
|
42
|
+
// null for a frame with no parseable line:column (e.g. `<anonymous>`) -- never fabricated
|
|
67
43
|
export function parseLocation(location) {
|
|
68
44
|
const match = LOCATION_WITH_LINE_COL.exec(location);
|
|
69
45
|
if (!match?.groups)
|
|
@@ -75,38 +51,16 @@ export function parseLocation(location) {
|
|
|
75
51
|
};
|
|
76
52
|
}
|
|
77
53
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* single parenthesis-free token, which is also precisely the shape of a
|
|
89
|
-
* bare V8 frame. RT-039 found this at the *parser* layer and fixed it there
|
|
90
|
-
* by requiring the whole text to contain a V8 header or a named `at fn
|
|
91
|
-
* (loc)` frame — and then reported that the matrix confirmed the classifier
|
|
92
|
-
* layer too. It had not: the classifier half of that matrix was inspecting
|
|
93
|
-
* an empty array (see `false-accept-matrix.test.ts`). With the matrix
|
|
94
|
-
* actually running, the V8 classifier opens **nine** blocks on one Rust
|
|
95
|
-
* backtrace.
|
|
96
|
-
*
|
|
97
|
-
* The rule here is the parser's anchor applied line by line, which is what
|
|
98
|
-
* a classifier can do that a set of predicates cannot: a **named** frame is
|
|
99
|
-
* always block content and arms the classifier; a **bare** frame is block
|
|
100
|
-
* content only once armed. A header arms it too.
|
|
101
|
-
*
|
|
102
|
-
* **The cost is the same one RT-039 already took and is restated rather
|
|
103
|
-
* than rediscovered:** a V8 trace consisting solely of bare frames does not
|
|
104
|
-
* open a block. Measured against four real V8 traces in RT-039, a bare
|
|
105
|
-
* frame occurs only where V8 has no function name, and Node's own internal
|
|
106
|
-
* frames — which follow every such throw — are all named, so the shape is
|
|
107
|
-
* rare. A disclosed recall loss, taken in the direction this project always
|
|
108
|
-
* takes it, over nine fabricated frames pointing into another language's
|
|
109
|
-
* standard library.
|
|
54
|
+
* Stateful `LineClassifier` (RT-035, RT-047). `FRAME_BARE` also matches a
|
|
55
|
+
* Rust backtrace's location lines (same `at` + one token shape as a bare
|
|
56
|
+
* V8 frame); RT-039 fixed the parser layer but the classifier wasn't
|
|
57
|
+
* covered by that matrix, and once it was, the V8 classifier opened nine
|
|
58
|
+
* blocks on one Rust backtrace. Fix: a named frame always arms and counts;
|
|
59
|
+
* a bare frame counts only once armed (a header arms it too). Cost: a
|
|
60
|
+
* trace of solely bare frames doesn't open a block — rare (measured
|
|
61
|
+
* against four real V8 traces: bare frames occur only with no function
|
|
62
|
+
* name, and Node's internal frames that follow are all named), a
|
|
63
|
+
* disclosed recall loss over nine fabricated foreign-stdlib frames.
|
|
110
64
|
*/
|
|
111
65
|
export function createV8LineClassifier() {
|
|
112
66
|
let armed = false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v8-frame-parsing.js","sourceRoot":"","sources":["../src/v8-frame-parsing.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"v8-frame-parsing.js","sourceRoot":"","sources":["../src/v8-frame-parsing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAaH,MAAM,cAAc,GAAG,0DAA0D,CAAC;AAClF,MAAM,iBAAiB,GAAG,yDAAyD,CAAC;AACpF,oFAAoF;AACpF,qFAAqF;AACrF,sFAAsF;AACtF,oFAAoF;AACpF,2DAA2D;AAC3D,MAAM,UAAU,GAAG,+BAA+B,CAAC;AACnD,MAAM,sBAAsB,GAAG,wCAAwC,CAAC;AAExE,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED,yJAAyJ;AACzJ,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,UAAU,EAAE,MAAM,EAAE,CAAC;QACvB,OAAO,EAAE,YAAY,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;IACrG,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;IACpE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,MAAM,KAAK,GAAG,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpD,IAAI,CAAC,KAAK,EAAE,MAAM;QAAE,OAAO,IAAI,CAAC;IAChC,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QAChC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;KACpC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB;IAIpC,IAAI,KAAK,GAAG,KAAK,CAAC;IAElB,OAAO;QACL,QAAQ,CAAC,IAAY;YACnB,IAAI,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzB,KAAK,GAAG,IAAI,CAAC;gBACb,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,CAAC,IAAY;YAClB,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7B,KAAK,GAAG,IAAI,CAAC;gBACb,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,KAAK,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Composes the pure V8 line-recognition functions (`v8-frame-parsing.ts`)
|
|
3
|
-
* with a `SourceLocationResolver` to implement the
|
|
4
|
-
*
|
|
3
|
+
* with a `SourceLocationResolver` to implement the async `StackTraceParser`
|
|
4
|
+
* contract.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* what V8 stacks can ever contain, not evidence that what IS shown is
|
|
15
|
-
* fragmented. Detecting a genuinely lost logical caller would need
|
|
16
|
-
* async_hooks-based correlation across reentry points, not text parsing —
|
|
17
|
-
* out of scope here, and reporting "concurrency-fragmented" without that
|
|
18
|
-
* evidence would be a guess this parser has no basis for.
|
|
6
|
+
* Fidelity is always "synchronous". V8's zero-cost async stack traces mean
|
|
7
|
+
* every frame here is genuine — V8 doesn't fabricate frames. A single
|
|
8
|
+
* capture can't show who scheduled a callback across an event-loop reentry
|
|
9
|
+
* point; the stack correctly ends at that boundary (`node:events`,
|
|
10
|
+
* `node:timers`, the module loader). That's a hard limit of what V8 stacks
|
|
11
|
+
* contain, not evidence of fragmentation — detecting a lost logical caller
|
|
12
|
+
* would need async_hooks correlation, out of scope here, so reporting
|
|
13
|
+
* "concurrency-fragmented" without that evidence would be an unfounded guess.
|
|
19
14
|
*/
|
|
20
15
|
import type { SourceLocationResolver, StackTraceParser } from "@descryy/runtime-backend-observation";
|
|
21
16
|
export declare function createV8StackTraceParser(resolver: SourceLocationResolver): StackTraceParser;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v8-stack-trace-parser.d.ts","sourceRoot":"","sources":["../src/v8-stack-trace-parser.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"v8-stack-trace-parser.d.ts","sourceRoot":"","sources":["../src/v8-stack-trace-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAIrG,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,gBAAgB,CA+C3F"}
|
|
@@ -1,46 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Composes the pure V8 line-recognition functions (`v8-frame-parsing.ts`)
|
|
3
|
-
* with a `SourceLocationResolver` to implement the
|
|
4
|
-
*
|
|
3
|
+
* with a `SourceLocationResolver` to implement the async `StackTraceParser`
|
|
4
|
+
* contract.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* what V8 stacks can ever contain, not evidence that what IS shown is
|
|
15
|
-
* fragmented. Detecting a genuinely lost logical caller would need
|
|
16
|
-
* async_hooks-based correlation across reentry points, not text parsing —
|
|
17
|
-
* out of scope here, and reporting "concurrency-fragmented" without that
|
|
18
|
-
* evidence would be a guess this parser has no basis for.
|
|
6
|
+
* Fidelity is always "synchronous". V8's zero-cost async stack traces mean
|
|
7
|
+
* every frame here is genuine — V8 doesn't fabricate frames. A single
|
|
8
|
+
* capture can't show who scheduled a callback across an event-loop reentry
|
|
9
|
+
* point; the stack correctly ends at that boundary (`node:events`,
|
|
10
|
+
* `node:timers`, the module loader). That's a hard limit of what V8 stacks
|
|
11
|
+
* contain, not evidence of fragmentation — detecting a lost logical caller
|
|
12
|
+
* would need async_hooks correlation, out of scope here, so reporting
|
|
13
|
+
* "concurrency-fragmented" without that evidence would be an unfounded guess.
|
|
19
14
|
*/
|
|
20
15
|
import { parseV8Frame, parseLocation, isV8HeaderLine, isV8NamedFrameLine } from "./v8-frame-parsing.js";
|
|
21
16
|
export function createV8StackTraceParser(resolver) {
|
|
22
17
|
return {
|
|
23
18
|
async parse(rawText) {
|
|
24
19
|
const lines = rawText.split(/\r\n|\r|\n/);
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
// (an ESM top-level throw), and Node's own internal frames — which
|
|
35
|
-
// follow every such throw — are all named. Verified against real
|
|
36
|
-
// output for ESM top-level, CJS top-level, an arrow in `Array.map`,
|
|
37
|
-
// and an unhandled rejection.
|
|
38
|
-
//
|
|
39
|
-
// The cost, stated: a trace consisting solely of bare frames is
|
|
40
|
-
// refused rather than parsed. That is a disclosed recall loss in a
|
|
41
|
-
// rare shape, taken in the direction this project always takes it —
|
|
42
|
-
// refusing beats fabricating, and the alternative here was nine
|
|
43
|
-
// fabricated frames pointing into another language's standard library.
|
|
20
|
+
// anchor check: a bare V8 frame (`at <location>`) has the same shape as
|
|
21
|
+
// a Rust backtrace's location lines, so without an anchor this parser
|
|
22
|
+
// accepted a whole Rust backtrace as nine wrong frames. `at fn (loc)`
|
|
23
|
+
// is the anchor because no other runtime here emits it, and a real V8
|
|
24
|
+
// trace effectively always contains one (bare frames appear only where
|
|
25
|
+
// V8 has no function name; Node's own internal frames that follow are
|
|
26
|
+
// all named — verified against ESM/CJS top-level, an Array.map arrow,
|
|
27
|
+
// and an unhandled rejection). Cost: a trace of solely bare frames is
|
|
28
|
+
// refused rather than fabricated.
|
|
44
29
|
if (!lines.some(isV8HeaderLine) && !lines.some(isV8NamedFrameLine)) {
|
|
45
30
|
return null;
|
|
46
31
|
}
|
|
@@ -51,11 +36,7 @@ export function createV8StackTraceParser(resolver) {
|
|
|
51
36
|
continue; // a header line, blank line, or anything not frame-shaped
|
|
52
37
|
const parsedLocation = parseLocation(rawFrame.location);
|
|
53
38
|
if (parsedLocation === null) {
|
|
54
|
-
//
|
|
55
|
-
// source location at all (e.g. "at Array.map (<anonymous>)").
|
|
56
|
-
// There is no line to report and none to fabricate -- omitted
|
|
57
|
-
// rather than guessed, same precision-over-recall choice this
|
|
58
|
-
// package's block detector already makes for ambiguous headers.
|
|
39
|
+
// rare but real: native built-in call with no JS location (e.g. "at Array.map (<anonymous>)") -- omitted, not guessed
|
|
59
40
|
continue;
|
|
60
41
|
}
|
|
61
42
|
const location = await resolver.resolve({
|
|
@@ -67,15 +48,10 @@ export function createV8StackTraceParser(resolver) {
|
|
|
67
48
|
frames.push({ location, raw: line });
|
|
68
49
|
}
|
|
69
50
|
if (frames.length === 0) {
|
|
70
|
-
//
|
|
71
|
-
// V8's format, per this interface's own contract: null, never an
|
|
72
|
-
// empty-but-structured StackTrace standing in for "couldn't parse."
|
|
51
|
+
// nothing frame-shaped: null per contract, never an empty-but-structured StackTrace standing in for "couldn't parse"
|
|
73
52
|
return null;
|
|
74
53
|
}
|
|
75
|
-
//
|
|
76
|
-
// answer rather than inheriting one (RT-083). 0 is today's behaviour
|
|
77
|
-
// and is stated, not defaulted; the real value here is where the
|
|
78
|
-
// runtime declares its own boundary — V8 puts no frames of its own above the application's. Lane C.
|
|
54
|
+
// primaryFrameIndex is 0, stated not defaulted: V8 puts no frames of its own above the application's (RT-083)
|
|
79
55
|
return { fidelity: "synchronous", frames, primaryFrameIndex: 0 };
|
|
80
56
|
},
|
|
81
57
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v8-stack-trace-parser.js","sourceRoot":"","sources":["../src/v8-stack-trace-parser.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"v8-stack-trace-parser.js","sourceRoot":"","sources":["../src/v8-stack-trace-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAExG,MAAM,UAAU,wBAAwB,CAAC,QAAgC;IACvE,OAAO;QACL,KAAK,CAAC,KAAK,CAAC,OAAe;YACzB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAE1C,wEAAwE;YACxE,sEAAsE;YACtE,sEAAsE;YACtE,sEAAsE;YACtE,uEAAuE;YACvE,sEAAsE;YACtE,sEAAsE;YACtE,sEAAsE;YACtE,kCAAkC;YAClC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBACnE,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,MAAM,GAAiB,EAAE,CAAC;YAChC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;gBACpC,IAAI,QAAQ,KAAK,IAAI;oBAAE,SAAS,CAAC,0DAA0D;gBAE3F,MAAM,cAAc,GAAG,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBACxD,IAAI,cAAc,KAAK,IAAI,EAAE,CAAC;oBAC5B,sHAAsH;oBACtH,SAAS;gBACX,CAAC;gBAED,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC;oBACtC,IAAI,EAAE,cAAc,CAAC,IAAI;oBACzB,IAAI,EAAE,cAAc,CAAC,IAAI;oBACzB,MAAM,EAAE,cAAc,CAAC,MAAM;oBAC7B,YAAY,EAAE,QAAQ,CAAC,YAAY;iBACpC,CAAC,CAAC;gBACH,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACvC,CAAC;YAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,qHAAqH;gBACrH,OAAO,IAAI,CAAC;YACd,CAAC;YAED,8GAA8G;YAC9G,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;QACnE,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@descryy/runtime-adapter-typescript",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "TypeScript/Node runtime adapter: V8 stack-trace parsing, Node source-map resolution, the concrete RuntimeAdapter for TS/Node.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/descryhq-wq/descry-runtime.git",
|
|
10
|
+
"directory": "packages/adapter-typescript"
|
|
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.
|
|
28
|
-
"@descryy/runtime-backend-observation": "0.
|
|
32
|
+
"@descryy/runtime-contracts": "0.3.1",
|
|
33
|
+
"@descryy/runtime-backend-observation": "0.3.1",
|
|
29
34
|
"@jridgewell/trace-mapping": "^0.3.31",
|
|
30
|
-
"@descryy/runtime-external-service-observation": "0.
|
|
35
|
+
"@descryy/runtime-external-service-observation": "0.3.1"
|
|
31
36
|
},
|
|
32
37
|
"devDependencies": {
|
|
33
38
|
"@descryy/runtime-evidence-store": "*"
|