@descryy/runtime-adapter-typescript 0.0.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.
@@ -0,0 +1,7 @@
1
+ export type { RawStackFrame, ParsedLocation } from "./v8-frame-parsing.ts";
2
+ export { isV8HeaderLine, isV8FrameLine, isV8NamedFrameLine, parseV8Frame, parseLocation, createV8LineClassifier } from "./v8-frame-parsing.ts";
3
+ export { createNodeSourceLocationResolver } from "./node-source-location-resolver.ts";
4
+ export { createV8StackTraceParser } from "./v8-stack-trace-parser.ts";
5
+ export type { TypeScriptRuntimeAdapterOptions } from "./typescript-runtime-adapter.ts";
6
+ export { createTypeScriptRuntimeAdapter } from "./typescript-runtime-adapter.ts";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,YAAY,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/I,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { isV8HeaderLine, isV8FrameLine, isV8NamedFrameLine, parseV8Frame, parseLocation, createV8LineClassifier } from "./v8-frame-parsing.js";
2
+ export { createNodeSourceLocationResolver } from "./node-source-location-resolver.js";
3
+ export { createV8StackTraceParser } from "./v8-stack-trace-parser.js";
4
+ export { createTypeScriptRuntimeAdapter } from "./typescript-runtime-adapter.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,YAAY,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/I,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Node/TypeScript `SourceLocationResolver` — resolves a V8-captured
3
+ * compiled-code location back to authored source through a `//# sourceMappingURL`
4
+ * source map, when one exists, and classifies `SourceLocation.reliability`
5
+ * (RT-003) honestly rather than defaulting.
6
+ *
7
+ * - No `sourceMappingURL` reference at all → `self-contained`. Nothing was
8
+ * lost — the V8-captured compiled location IS the accurate answer, same
9
+ * category as a Python traceback's self-captured frame. This is NOT
10
+ * `side-artifact-missing`, which means "we expected a map and couldn't
11
+ * get it."
12
+ * - A reference exists but the map file can't be found or parsed, or has
13
+ * no entry for this exact line:column → `side-artifact-missing`.
14
+ * - A reference exists, the map resolves this position, and either
15
+ * `sourcesContent` is absent for that source or the on-disk file matches
16
+ * it → `side-artifact-resolved`.
17
+ * - A reference exists, the map resolves this position, `sourcesContent`
18
+ * is present for that source, AND the on-disk file's content differs
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.
26
+ */
27
+ import type { SourceLocationResolver } from "@descryy/runtime-backend-observation";
28
+ export declare function createNodeSourceLocationResolver(): SourceLocationResolver;
29
+ //# sourceMappingURL=node-source-location-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-source-location-resolver.d.ts","sourceRoot":"","sources":["../src/node-source-location-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAQH,OAAO,KAAK,EAAoB,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AA6IrG,wBAAgB,gCAAgC,IAAI,sBAAsB,CAMzE"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Node/TypeScript `SourceLocationResolver` — resolves a V8-captured
3
+ * compiled-code location back to authored source through a `//# sourceMappingURL`
4
+ * source map, when one exists, and classifies `SourceLocation.reliability`
5
+ * (RT-003) honestly rather than defaulting.
6
+ *
7
+ * - No `sourceMappingURL` reference at all → `self-contained`. Nothing was
8
+ * lost — the V8-captured compiled location IS the accurate answer, same
9
+ * category as a Python traceback's self-captured frame. This is NOT
10
+ * `side-artifact-missing`, which means "we expected a map and couldn't
11
+ * get it."
12
+ * - A reference exists but the map file can't be found or parsed, or has
13
+ * no entry for this exact line:column → `side-artifact-missing`.
14
+ * - A reference exists, the map resolves this position, and either
15
+ * `sourcesContent` is absent for that source or the on-disk file matches
16
+ * it → `side-artifact-resolved`.
17
+ * - A reference exists, the map resolves this position, `sourcesContent`
18
+ * is present for that source, AND the on-disk file's content differs
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.
26
+ */
27
+ import { existsSync, readFileSync } from "node:fs";
28
+ import { fileURLToPath, pathToFileURL } from "node:url";
29
+ import { dirname, resolve as resolvePath, isAbsolute } from "node:path";
30
+ import { TraceMap, originalPositionFor, sourceContentFor } from "@jridgewell/trace-mapping";
31
+ const SOURCE_MAPPING_URL_PATTERN = /\/\/[#@]\s*sourceMappingURL=(\S+)/g;
32
+ function normalizeCompiledFile(rawFile) {
33
+ return rawFile.startsWith("file://") ? fileURLToPath(rawFile) : rawFile;
34
+ }
35
+ /** Last match wins, per convention — and a large bundle could contain earlier false-positive-shaped text. */
36
+ function findSourceMappingUrl(compiledText) {
37
+ let last;
38
+ for (const match of compiledText.matchAll(SOURCE_MAPPING_URL_PATTERN)) {
39
+ last = match[1];
40
+ }
41
+ return last;
42
+ }
43
+ function loadTraceMap(compiledFilePath, mappingUrl) {
44
+ if (mappingUrl.startsWith("data:")) {
45
+ const marker = "base64,";
46
+ const idx = mappingUrl.indexOf(marker);
47
+ if (idx === -1)
48
+ return undefined;
49
+ try {
50
+ const json = Buffer.from(mappingUrl.slice(idx + marker.length), "base64").toString("utf8");
51
+ return { map: new TraceMap(json, pathToFileURL(compiledFilePath).href) };
52
+ }
53
+ catch {
54
+ return undefined;
55
+ }
56
+ }
57
+ const mapPath = isAbsolute(mappingUrl) ? mappingUrl : resolvePath(dirname(compiledFilePath), mappingUrl);
58
+ if (!existsSync(mapPath))
59
+ return undefined;
60
+ try {
61
+ const json = readFileSync(mapPath, "utf8");
62
+ return { map: new TraceMap(json, pathToFileURL(mapPath).href) };
63
+ }
64
+ catch {
65
+ return undefined;
66
+ }
67
+ }
68
+ function normalizeLineEndings(text) {
69
+ return text.replace(/\r\n/g, "\n");
70
+ }
71
+ function selfContained(frame, file) {
72
+ return {
73
+ file,
74
+ line: frame.line,
75
+ column: frame.column,
76
+ functionName: frame.functionName,
77
+ reliability: "self-contained",
78
+ resolvedVia: null,
79
+ };
80
+ }
81
+ function sideArtifactMissing(frame, file, mappingUrl) {
82
+ return {
83
+ file,
84
+ line: frame.line,
85
+ column: frame.column,
86
+ functionName: frame.functionName,
87
+ reliability: "side-artifact-missing",
88
+ resolvedVia: mappingUrl,
89
+ };
90
+ }
91
+ function resolveSync(frame) {
92
+ // V8 always names a file, so this is unreachable from the V8 parser --
93
+ // the type admits null because a JVM frame can omit it. Handled rather
94
+ // than asserted away, since an assertion here would become a crash the
95
+ // moment some other caller reuses this resolver.
96
+ if (frame.line === null || frame.file === null) {
97
+ return selfContained(frame, frame.file);
98
+ }
99
+ const file = normalizeCompiledFile(frame.file);
100
+ if (!existsSync(file)) {
101
+ // e.g. "node:events" -- a stdlib internal with no file on disk at all.
102
+ // Nothing was ever there to lose; the captured location is accurate.
103
+ return selfContained(frame, file);
104
+ }
105
+ const compiledText = readFileSync(file, "utf8");
106
+ const mappingUrl = findSourceMappingUrl(compiledText);
107
+ if (mappingUrl === undefined) {
108
+ return selfContained(frame, file);
109
+ }
110
+ if (frame.column === null) {
111
+ // A source map can't be walked to a specific position without a
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.
115
+ return selfContained(frame, file);
116
+ }
117
+ const loaded = loadTraceMap(file, mappingUrl);
118
+ if (loaded === undefined) {
119
+ return sideArtifactMissing(frame, file, mappingUrl);
120
+ }
121
+ // Stack frame line is 1-based, matching originalPositionFor's expectation.
122
+ // Column is 1-based in V8 stacks but 0-based in trace-mapping's needle.
123
+ const original = originalPositionFor(loaded.map, { line: frame.line, column: Math.max(0, frame.column - 1) });
124
+ if (original.source === null || original.line === null || original.column === null) {
125
+ return sideArtifactMissing(frame, file, mappingUrl);
126
+ }
127
+ const sourceIndex = loaded.map.sources.indexOf(original.source);
128
+ const embeddedContent = sourceContentFor(loaded.map, original.source);
129
+ const resolvedSourcePath = sourceIndex >= 0 ? (loaded.map.resolvedSources[sourceIndex] ?? original.source) : original.source;
130
+ const onDiskPath = resolvedSourcePath.startsWith("file://") ? fileURLToPath(resolvedSourcePath) : resolvedSourcePath;
131
+ let reliability = "side-artifact-resolved";
132
+ if (embeddedContent !== null && existsSync(onDiskPath)) {
133
+ const onDisk = readFileSync(onDiskPath, "utf8");
134
+ if (normalizeLineEndings(onDisk) !== normalizeLineEndings(embeddedContent)) {
135
+ reliability = "side-artifact-stale";
136
+ }
137
+ }
138
+ // embeddedContent === null, or the on-disk file is unreadable: nothing to
139
+ // compare against, so freshness is simply unverifiable — resolved stays
140
+ // the honest answer rather than downgrading on the absence of a check.
141
+ return {
142
+ file: onDiskPath,
143
+ line: original.line,
144
+ // trace-mapping's column is 0-based; SourceLocation.column is reported
145
+ // 1-based to match how every other frame in this system is stated.
146
+ column: original.column + 1,
147
+ functionName: original.name ?? frame.functionName,
148
+ reliability,
149
+ resolvedVia: mappingUrl,
150
+ };
151
+ }
152
+ export function createNodeSourceLocationResolver() {
153
+ return {
154
+ resolve(frame) {
155
+ return Promise.resolve(resolveSync(frame));
156
+ },
157
+ };
158
+ }
159
+ //# sourceMappingURL=node-source-location-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-source-location-resolver.js","sourceRoot":"","sources":["../src/node-source-location-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The concrete `RuntimeAdapter` for TypeScript/Node — V1's only language
3
+ * (per the explicit vertical-slice scope limit: no second language until
4
+ * this one closes end to end). Wires this package's V8 stack-trace parser,
5
+ * Node source-map resolver, and V8 line classifier into the language-
6
+ * neutral seam `@descryy/runtime-backend-observation` defines.
7
+ */
8
+ import type { RuntimeAdapter } from "@descryy/runtime-backend-observation";
9
+ export interface TypeScriptRuntimeAdapterOptions {
10
+ /** Attributed on every emitted Evidence's `service` field (plan §16.7). Omit when not applicable. */
11
+ readonly service?: string;
12
+ }
13
+ export declare function createTypeScriptRuntimeAdapter(options?: TypeScriptRuntimeAdapterOptions): RuntimeAdapter;
14
+ //# sourceMappingURL=typescript-runtime-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript-runtime-adapter.d.ts","sourceRoot":"","sources":["../src/typescript-runtime-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,sCAAsC,CAAC;AAQhG,MAAM,WAAW,+BAA+B;IAC9C,qGAAqG;IACrG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,8BAA8B,CAAC,OAAO,GAAE,+BAAoC,GAAG,cAAc,CAuB5G"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The concrete `RuntimeAdapter` for TypeScript/Node — V1's only language
3
+ * (per the explicit vertical-slice scope limit: no second language until
4
+ * this one closes end to end). Wires this package's V8 stack-trace parser,
5
+ * Node source-map resolver, and V8 line classifier into the language-
6
+ * neutral seam `@descryy/runtime-backend-observation` defines.
7
+ */
8
+ import { createLogCollector } from "@descryy/runtime-backend-observation";
9
+ import { createV8LineClassifier } from "./v8-frame-parsing.js";
10
+ import { createNodeSourceLocationResolver } from "./node-source-location-resolver.js";
11
+ import { createV8StackTraceParser } from "./v8-stack-trace-parser.js";
12
+ export function createTypeScriptRuntimeAdapter(options = {}) {
13
+ const sourceLocationResolver = createNodeSourceLocationResolver();
14
+ const stackTraceParser = createV8StackTraceParser(sourceLocationResolver);
15
+ function createBackendCollectors(sources) {
16
+ return sources.map((source) => createLogCollector({
17
+ source,
18
+ // One classifier per source, never one shared: it is stateful now,
19
+ // and a shared instance would let one process's trace arm another's.
20
+ classifier: createV8LineClassifier(),
21
+ stackTraceParser,
22
+ ...(options.service !== undefined ? { service: options.service } : {}),
23
+ }));
24
+ }
25
+ return {
26
+ language: "typescript",
27
+ stackTraceParser,
28
+ sourceLocationResolver,
29
+ createBackendCollectors,
30
+ };
31
+ }
32
+ //# sourceMappingURL=typescript-runtime-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript-runtime-adapter.js","sourceRoot":"","sources":["../src/typescript-runtime-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAE1E,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,mEAAmE;YACnE,qEAAqE;YACrE,UAAU,EAAE,sBAAsB,EAAE;YACpC,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,OAAO;QACL,QAAQ,EAAE,YAAY;QACtB,gBAAgB;QAChB,sBAAsB;QACtB,uBAAuB;KACxB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * V8's `Error.stack` line shapes — the `LineClassifier` for
3
+ * `ExceptionBlockDetector` plus the raw-text-to-frame-parts extraction that
4
+ * feeds `SourceLocationResolver`.
5
+ *
6
+ * The header pattern is deliberately NOT anchored to the start of the
7
+ * line. Measured against `broken-invoices-backend`'s own real output
8
+ * (`console.error` with a custom prefix): `"[broken-invoices] DELETE
9
+ * /api/invoices/3 failed: TypeError: Cannot read properties of null
10
+ * (reading 'toUpperCase')"` — the error type sits mid-line behind
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.
17
+ */
18
+ export interface RawStackFrame {
19
+ 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
+ readonly location: string;
22
+ }
23
+ export interface ParsedLocation {
24
+ readonly file: string;
25
+ readonly line: number;
26
+ readonly column: number;
27
+ }
28
+ export declare function isV8HeaderLine(line: string): boolean;
29
+ 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
+ */
36
+ export declare function isV8NamedFrameLine(line: string): boolean;
37
+ export declare function parseV8Frame(line: string): RawStackFrame | null;
38
+ /** `null` for a frame with no parseable line:column (e.g. `<anonymous>`) — never fabricated. */
39
+ export declare function parseLocation(location: string): ParsedLocation | null;
40
+ /**
41
+ * A **stateful** `LineClassifier`, for RT-035's reason and RT-047's
42
+ * evidence.
43
+ *
44
+ * V8 was the last adapter still handing `ExceptionBlockDetector` a bare
45
+ * pair of stateless predicates — `{ isHeader: isV8HeaderLine, isFrame:
46
+ * isV8FrameLine }` — and it was wrong in exactly the way every other
47
+ * language's had already been found to be.
48
+ *
49
+ * **`FRAME_BARE` matches a Rust backtrace's location lines.** ` at
50
+ * /rustc/<hash>/library/std/src/panicking.rs:698:5` is `at` followed by a
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.
73
+ */
74
+ export declare function createV8LineClassifier(): {
75
+ isHeader(line: string): boolean;
76
+ isFrame(line: string): boolean;
77
+ };
78
+ //# sourceMappingURL=v8-frame-parsing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v8-frame-parsing.d.ts","sourceRoot":"","sources":["../src/v8-frame-parsing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,gKAAgK;IAChK,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;AAyBD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAU/D;AAED,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAQrE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAChC,CAoBA"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * V8's `Error.stack` line shapes — the `LineClassifier` for
3
+ * `ExceptionBlockDetector` plus the raw-text-to-frame-parts extraction that
4
+ * feeds `SourceLocationResolver`.
5
+ *
6
+ * The header pattern is deliberately NOT anchored to the start of the
7
+ * line. Measured against `broken-invoices-backend`'s own real output
8
+ * (`console.error` with a custom prefix): `"[broken-invoices] DELETE
9
+ * /api/invoices/3 failed: TypeError: Cannot read properties of null
10
+ * (reading 'toUpperCase')"` — the error type sits mid-line behind
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.
17
+ */
18
+ const HEADER_PATTERN = /\b(?<type>(?:[A-Z][\w$]*)?Error)(?::\s*(?<message>.*))?$/;
19
+ const FRAME_WITH_PARENS = /^\s*at\s+(?:new\s+)?(?<fn>.+?)\s+\((?<loc>[^()]+)\)\s*$/;
20
+ /**
21
+ * A location-only frame: `at file:///app/x.js:1:1`, `at <anonymous>`,
22
+ * `at node:internal/main/run_main_module:23:47`.
23
+ *
24
+ * **Parentheses are excluded deliberately, and the JVM adapter's
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
+ */
38
+ const FRAME_BARE = /^\s*at\s+(?<loc>[^\s()]+)\s*$/;
39
+ const LOCATION_WITH_LINE_COL = /^(?<file>.+):(?<line>\d+):(?<col>\d+)$/;
40
+ export function isV8HeaderLine(line) {
41
+ return HEADER_PATTERN.test(line);
42
+ }
43
+ export function isV8FrameLine(line) {
44
+ return FRAME_WITH_PARENS.test(line) || FRAME_BARE.test(line);
45
+ }
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
+ */
52
+ export function isV8NamedFrameLine(line) {
53
+ return FRAME_WITH_PARENS.test(line);
54
+ }
55
+ export function parseV8Frame(line) {
56
+ const withParens = FRAME_WITH_PARENS.exec(line);
57
+ if (withParens?.groups) {
58
+ return { functionName: withParens.groups["fn"] ?? null, location: withParens.groups["loc"] ?? "" };
59
+ }
60
+ const bare = FRAME_BARE.exec(line);
61
+ if (bare?.groups) {
62
+ return { functionName: null, location: bare.groups["loc"] ?? "" };
63
+ }
64
+ return null;
65
+ }
66
+ /** `null` for a frame with no parseable line:column (e.g. `<anonymous>`) — never fabricated. */
67
+ export function parseLocation(location) {
68
+ const match = LOCATION_WITH_LINE_COL.exec(location);
69
+ if (!match?.groups)
70
+ return null;
71
+ return {
72
+ file: match.groups["file"] ?? "",
73
+ line: Number(match.groups["line"]),
74
+ column: Number(match.groups["col"]),
75
+ };
76
+ }
77
+ /**
78
+ * A **stateful** `LineClassifier`, for RT-035's reason and RT-047's
79
+ * evidence.
80
+ *
81
+ * V8 was the last adapter still handing `ExceptionBlockDetector` a bare
82
+ * pair of stateless predicates — `{ isHeader: isV8HeaderLine, isFrame:
83
+ * isV8FrameLine }` — and it was wrong in exactly the way every other
84
+ * language's had already been found to be.
85
+ *
86
+ * **`FRAME_BARE` matches a Rust backtrace's location lines.** ` at
87
+ * /rustc/<hash>/library/std/src/panicking.rs:698:5` is `at` followed by a
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.
110
+ */
111
+ export function createV8LineClassifier() {
112
+ let armed = false;
113
+ return {
114
+ isHeader(line) {
115
+ if (isV8HeaderLine(line)) {
116
+ armed = true;
117
+ return true;
118
+ }
119
+ return false;
120
+ },
121
+ isFrame(line) {
122
+ if (isV8NamedFrameLine(line)) {
123
+ armed = true;
124
+ return true;
125
+ }
126
+ return armed && FRAME_BARE.test(line);
127
+ },
128
+ };
129
+ }
130
+ //# sourceMappingURL=v8-frame-parsing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v8-frame-parsing.js","sourceRoot":"","sources":["../src/v8-frame-parsing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAcH,MAAM,cAAc,GAAG,0DAA0D,CAAC;AAClF,MAAM,iBAAiB,GAAG,yDAAyD,CAAC;AACpF;;;;;;;;;;;;;;;;;GAiBG;AACH,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;;;;;GAKG;AACH,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,gGAAgG;AAChG,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Composes the pure V8 line-recognition functions (`v8-frame-parsing.ts`)
3
+ * with a `SourceLocationResolver` to implement the full async
4
+ * `StackTraceParser` contract.
5
+ *
6
+ * fidelity is always reported "synchronous". V8's zero-cost async stack
7
+ * traces mean every frame this parser sees is a genuine, unbroken link in
8
+ * the chain — V8 does not fabricate frames. What a single stack capture
9
+ * can never show is who scheduled a callback across a real event-loop
10
+ * reentry point (a timer, an emitted event, a fresh microtask): the stack
11
+ * simply, correctly, ends at that boundary (`node:events`, `node:timers`,
12
+ * the module loader — see the real broken-invoices-backend and thrower.ts
13
+ * fixtures, both of which bottom out this way). That is a hard limit of
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.
19
+ */
20
+ import type { SourceLocationResolver, StackTraceParser } from "@descryy/runtime-backend-observation";
21
+ export declare function createV8StackTraceParser(resolver: SourceLocationResolver): StackTraceParser;
22
+ //# sourceMappingURL=v8-stack-trace-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v8-stack-trace-parser.d.ts","sourceRoot":"","sources":["../src/v8-stack-trace-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAIrG,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,gBAAgB,CAkE3F"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Composes the pure V8 line-recognition functions (`v8-frame-parsing.ts`)
3
+ * with a `SourceLocationResolver` to implement the full async
4
+ * `StackTraceParser` contract.
5
+ *
6
+ * fidelity is always reported "synchronous". V8's zero-cost async stack
7
+ * traces mean every frame this parser sees is a genuine, unbroken link in
8
+ * the chain — V8 does not fabricate frames. What a single stack capture
9
+ * can never show is who scheduled a callback across a real event-loop
10
+ * reentry point (a timer, an emitted event, a fresh microtask): the stack
11
+ * simply, correctly, ends at that boundary (`node:events`, `node:timers`,
12
+ * the module loader — see the real broken-invoices-backend and thrower.ts
13
+ * fixtures, both of which bottom out this way). That is a hard limit of
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.
19
+ */
20
+ import { parseV8Frame, parseLocation, isV8HeaderLine, isV8NamedFrameLine } from "./v8-frame-parsing.js";
21
+ export function createV8StackTraceParser(resolver) {
22
+ return {
23
+ async parse(rawText) {
24
+ const lines = rawText.split(/\r\n|\r|\n/);
25
+ // **Anchor check — the all-pairs matrix is what forced this.** A bare
26
+ // V8 frame is `at <location>`, and a Rust backtrace's location lines
27
+ // are `at /rustc/.../panicking.rs:698:5`. Those are the same shape,
28
+ // and without an anchor this parser accepted a whole Rust backtrace
29
+ // and produced nine confident, well-formed, wrong frames.
30
+ //
31
+ // The `at fn (loc)` form is the anchor because no other runtime here
32
+ // emits it, and measurement says a real V8 trace effectively always
33
+ // contains one: bare frames appear only where V8 has no function name
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.
44
+ if (!lines.some(isV8HeaderLine) && !lines.some(isV8NamedFrameLine)) {
45
+ return null;
46
+ }
47
+ const frames = [];
48
+ for (const line of lines) {
49
+ const rawFrame = parseV8Frame(line);
50
+ if (rawFrame === null)
51
+ continue; // a header line, blank line, or anything not frame-shaped
52
+ const parsedLocation = parseLocation(rawFrame.location);
53
+ if (parsedLocation === null) {
54
+ // A real, if rare, V8 shape: a native built-in call with no JS
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.
59
+ continue;
60
+ }
61
+ const location = await resolver.resolve({
62
+ file: parsedLocation.file,
63
+ line: parsedLocation.line,
64
+ column: parsedLocation.column,
65
+ functionName: rawFrame.functionName,
66
+ });
67
+ frames.push({ location, raw: line });
68
+ }
69
+ if (frames.length === 0) {
70
+ // Nothing frame-shaped in this text at all -- not recognizable as
71
+ // V8's format, per this interface's own contract: null, never an
72
+ // empty-but-structured StackTrace standing in for "couldn't parse."
73
+ return null;
74
+ }
75
+ // `primaryFrameIndex` is required so that every adapter states an
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.
79
+ return { fidelity: "synchronous", frames, primaryFrameIndex: 0 };
80
+ },
81
+ };
82
+ }
83
+ //# sourceMappingURL=v8-stack-trace-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v8-stack-trace-parser.js","sourceRoot":"","sources":["../src/v8-stack-trace-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;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,sEAAsE;YACtE,qEAAqE;YACrE,oEAAoE;YACpE,oEAAoE;YACpE,0DAA0D;YAC1D,EAAE;YACF,qEAAqE;YACrE,oEAAoE;YACpE,sEAAsE;YACtE,mEAAmE;YACnE,iEAAiE;YACjE,oEAAoE;YACpE,8BAA8B;YAC9B,EAAE;YACF,gEAAgE;YAChE,mEAAmE;YACnE,oEAAoE;YACpE,gEAAgE;YAChE,uEAAuE;YACvE,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,+DAA+D;oBAC/D,8DAA8D;oBAC9D,8DAA8D;oBAC9D,8DAA8D;oBAC9D,gEAAgE;oBAChE,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,kEAAkE;gBAClE,iEAAiE;gBACjE,oEAAoE;gBACpE,OAAO,IAAI,CAAC;YACd,CAAC;YAEK,kEAAkE;YACxE,qEAAqE;YACrE,iEAAiE;YACjE,oGAAoG;YACpG,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;QACnE,CAAC;KACF,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@descryy/runtime-adapter-typescript",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "description": "TypeScript/Node runtime adapter: V8 stack-trace parsing, Node source-map resolution, the concrete RuntimeAdapter for TS/Node.",
6
+ "license": "UNLICENSED",
7
+ "engines": {
8
+ "node": ">=22.5"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "publishConfig": {
20
+ "registry": "https://registry.npmjs.org",
21
+ "access": "public"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc -b"
25
+ },
26
+ "dependencies": {
27
+ "@descryy/runtime-contracts": "0.0.0",
28
+ "@descryy/runtime-backend-observation": "0.0.0",
29
+ "@jridgewell/trace-mapping": "^0.3.31"
30
+ },
31
+ "devDependencies": {
32
+ "@descryy/runtime-evidence-store": "*"
33
+ }
34
+ }