@descryy/runtime-adapter-rust 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.
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/rust-frame-parsing.d.ts +82 -0
- package/dist/rust-frame-parsing.d.ts.map +1 -0
- package/dist/rust-frame-parsing.js +146 -0
- package/dist/rust-frame-parsing.js.map +1 -0
- package/dist/rust-runtime-adapter.d.ts +29 -0
- package/dist/rust-runtime-adapter.d.ts.map +1 -0
- package/dist/rust-runtime-adapter.js +53 -0
- package/dist/rust-runtime-adapter.js.map +1 -0
- package/dist/rust-stack-trace-parser.d.ts +78 -0
- package/dist/rust-stack-trace-parser.d.ts.map +1 -0
- package/dist/rust-stack-trace-parser.js +176 -0
- package/dist/rust-stack-trace-parser.js.map +1 -0
- package/package.json +30 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { RawRustFrame, RustPanicHeader } from "./rust-frame-parsing.ts";
|
|
2
|
+
export { createRustLineClassifier, isRustBacktraceStartLine, isRustFrameLocationLine, isRustFrameSymbolLine, isRustHeaderLine, isRustNoteLine, parseRustFrameLocation, parseRustFrameSymbol, parseRustPanicHeader, } from "./rust-frame-parsing.ts";
|
|
3
|
+
export { createRustStackTraceParser } from "./rust-stack-trace-parser.ts";
|
|
4
|
+
export type { RustRuntimeAdapterOptions } from "./rust-runtime-adapter.ts";
|
|
5
|
+
export { createRustRuntimeAdapter, createRustSourceLocationResolver } from "./rust-runtime-adapter.ts";
|
|
6
|
+
//# 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,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC7E,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,YAAY,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,gCAAgC,EAAE,MAAM,2BAA2B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createRustLineClassifier, isRustBacktraceStartLine, isRustFrameLocationLine, isRustFrameSymbolLine, isRustHeaderLine, isRustNoteLine, parseRustFrameLocation, parseRustFrameSymbol, parseRustPanicHeader, } from "./rust-frame-parsing.js";
|
|
2
|
+
export { createRustStackTraceParser } from "./rust-stack-trace-parser.js";
|
|
3
|
+
export { createRustRuntimeAdapter, createRustSourceLocationResolver } from "./rust-runtime-adapter.js";
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE1E,OAAO,EAAE,wBAAwB,EAAE,gCAAgC,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rust panic and backtrace line shapes.
|
|
3
|
+
*
|
|
4
|
+
* **Verified against a real toolchain** (`rustc 1.91.1`), by compiling and
|
|
5
|
+
* running a fixture rather than transcribing the format:
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* thread 'main' (468745) panicked at thrower.rs:3:13:
|
|
9
|
+
* invoice 3 is corrupt
|
|
10
|
+
* stack backtrace:
|
|
11
|
+
* 0: __rustc::rust_begin_unwind
|
|
12
|
+
* at /rustc/ed61e7d.../library/std/src/panicking.rs:698:5
|
|
13
|
+
* 5: thrower::delete_invoice
|
|
14
|
+
* at ./thrower.rs:3:13
|
|
15
|
+
* 7: thrower::main
|
|
16
|
+
* at ./thrower.rs:11:20
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Rust is unlike every other runtime here in one important way: **the panic
|
|
20
|
+
* header itself carries a source location** — `panicked at thrower.rs:3:13`
|
|
21
|
+
* — with a line *and a column*. That matters because `RUST_BACKTRACE` is
|
|
22
|
+
* **off by default**, so in ordinary production output the header is the
|
|
23
|
+
* only location there is. A parser that required a backtrace would return
|
|
24
|
+
* nothing for the common case while the precise answer sat in line one.
|
|
25
|
+
*
|
|
26
|
+
* Two header shapes exist and both are supported. The modern one above was
|
|
27
|
+
* verified here. The pre-1.65 form put the message inside the header —
|
|
28
|
+
* `thread 'main' panicked at 'invoice 3 is corrupt', src/main.rs:3:13` —
|
|
29
|
+
* and is supported from its documented shape, **not** verified on this
|
|
30
|
+
* machine; it is still common in deployed toolchains, which is why it is
|
|
31
|
+
* handled rather than refused.
|
|
32
|
+
*
|
|
33
|
+
* Frames are two lines: ` N: symbol`, then a deeply-indented
|
|
34
|
+
* `at path:line:col`. The `at` line is **optional** — a build without debug
|
|
35
|
+
* info prints the symbol alone, which is what separates
|
|
36
|
+
* `debug-info-present` from `debug-info-stripped` here.
|
|
37
|
+
*/
|
|
38
|
+
export interface RawRustFrame {
|
|
39
|
+
readonly functionName: string;
|
|
40
|
+
/** Null when the frame printed no `at` line — a build with no debug info. Never fabricated. */
|
|
41
|
+
readonly file: string | null;
|
|
42
|
+
readonly line: number | null;
|
|
43
|
+
readonly column: number | null;
|
|
44
|
+
}
|
|
45
|
+
export interface RustPanicHeader {
|
|
46
|
+
readonly thread: string;
|
|
47
|
+
readonly file: string;
|
|
48
|
+
readonly line: number;
|
|
49
|
+
readonly column: number;
|
|
50
|
+
/** Present only in the legacy shape, where the message is inside the header line. */
|
|
51
|
+
readonly message: string | null;
|
|
52
|
+
}
|
|
53
|
+
export declare function parseRustPanicHeader(line: string): RustPanicHeader | null;
|
|
54
|
+
export declare function isRustHeaderLine(line: string): boolean;
|
|
55
|
+
export declare function isRustBacktraceStartLine(line: string): boolean;
|
|
56
|
+
export declare function isRustFrameSymbolLine(line: string): boolean;
|
|
57
|
+
export declare function isRustFrameLocationLine(line: string): boolean;
|
|
58
|
+
export declare function isRustNoteLine(line: string): boolean;
|
|
59
|
+
/** The symbol half of a frame; the location half is optional and supplied separately. */
|
|
60
|
+
export declare function parseRustFrameSymbol(line: string): string | null;
|
|
61
|
+
export declare function parseRustFrameLocation(line: string): {
|
|
62
|
+
file: string;
|
|
63
|
+
line: number;
|
|
64
|
+
column: number;
|
|
65
|
+
} | null;
|
|
66
|
+
/**
|
|
67
|
+
* A stateful `LineClassifier`, for RT-035's reason.
|
|
68
|
+
*
|
|
69
|
+
* Rust's specific hazard is ` 0: something` — a numbered list item is an
|
|
70
|
+
* ordinary thing to print, and a stateless rule accepting it would turn any
|
|
71
|
+
* enumerated log output into a stack-trace block. A numbered symbol line
|
|
72
|
+
* therefore only counts as block content **after `stack backtrace:`**.
|
|
73
|
+
*
|
|
74
|
+
* The panic message line, which follows the header and is arbitrary text,
|
|
75
|
+
* is accepted as block content only in the single position where Rust puts
|
|
76
|
+
* it: immediately after a modern header.
|
|
77
|
+
*/
|
|
78
|
+
export declare function createRustLineClassifier(): {
|
|
79
|
+
isHeader(line: string): boolean;
|
|
80
|
+
isFrame(line: string): boolean;
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=rust-frame-parsing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rust-frame-parsing.d.ts","sourceRoot":"","sources":["../src/rust-frame-parsing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,+FAA+F;IAC/F,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAeD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qFAAqF;IACrF,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAsBzE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQ1G;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,IAAI;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAChC,CA+BA"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rust panic and backtrace line shapes.
|
|
3
|
+
*
|
|
4
|
+
* **Verified against a real toolchain** (`rustc 1.91.1`), by compiling and
|
|
5
|
+
* running a fixture rather than transcribing the format:
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* thread 'main' (468745) panicked at thrower.rs:3:13:
|
|
9
|
+
* invoice 3 is corrupt
|
|
10
|
+
* stack backtrace:
|
|
11
|
+
* 0: __rustc::rust_begin_unwind
|
|
12
|
+
* at /rustc/ed61e7d.../library/std/src/panicking.rs:698:5
|
|
13
|
+
* 5: thrower::delete_invoice
|
|
14
|
+
* at ./thrower.rs:3:13
|
|
15
|
+
* 7: thrower::main
|
|
16
|
+
* at ./thrower.rs:11:20
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Rust is unlike every other runtime here in one important way: **the panic
|
|
20
|
+
* header itself carries a source location** — `panicked at thrower.rs:3:13`
|
|
21
|
+
* — with a line *and a column*. That matters because `RUST_BACKTRACE` is
|
|
22
|
+
* **off by default**, so in ordinary production output the header is the
|
|
23
|
+
* only location there is. A parser that required a backtrace would return
|
|
24
|
+
* nothing for the common case while the precise answer sat in line one.
|
|
25
|
+
*
|
|
26
|
+
* Two header shapes exist and both are supported. The modern one above was
|
|
27
|
+
* verified here. The pre-1.65 form put the message inside the header —
|
|
28
|
+
* `thread 'main' panicked at 'invoice 3 is corrupt', src/main.rs:3:13` —
|
|
29
|
+
* and is supported from its documented shape, **not** verified on this
|
|
30
|
+
* machine; it is still common in deployed toolchains, which is why it is
|
|
31
|
+
* handled rather than refused.
|
|
32
|
+
*
|
|
33
|
+
* Frames are two lines: ` N: symbol`, then a deeply-indented
|
|
34
|
+
* `at path:line:col`. The `at` line is **optional** — a build without debug
|
|
35
|
+
* info prints the symbol alone, which is what separates
|
|
36
|
+
* `debug-info-present` from `debug-info-stripped` here.
|
|
37
|
+
*/
|
|
38
|
+
/** Modern (verified): `thread 'main' (468745) panicked at thrower.rs:3:13:` — the thread id in parens is optional. */
|
|
39
|
+
const HEADER_MODERN = /^thread '(?<thread>[^']*)'(?:\s+\(\d+\))?\s+panicked at (?<file>.+?):(?<line>\d+):(?<col>\d+):\s*$/;
|
|
40
|
+
/** Legacy (documented, not verified here): `thread 'main' panicked at 'message', src/main.rs:3:13` */
|
|
41
|
+
const HEADER_LEGACY = /^thread '(?<thread>[^']*)'\s+panicked at '(?<message>.*)',\s+(?<file>.+?):(?<line>\d+):(?<col>\d+)\s*$/;
|
|
42
|
+
const BACKTRACE_START = /^\s*stack backtrace:\s*$/;
|
|
43
|
+
/** ` 0: __rustc::rust_begin_unwind` — the index is what makes this unambiguous. */
|
|
44
|
+
const FRAME_SYMBOL = /^\s*(?<index>\d+):\s+(?<symbol>\S.*?)\s*$/;
|
|
45
|
+
/** ` at /rustc/<hash>/library/std/src/panicking.rs:698:5` */
|
|
46
|
+
const FRAME_LOCATION = /^\s+at\s+(?<file>.+?):(?<line>\d+):(?<col>\d+)\s*$/;
|
|
47
|
+
/** `note: run with `RUST_BACKTRACE=1` ...` / `note: Some details are omitted...` */
|
|
48
|
+
const NOTE = /^note:\s/;
|
|
49
|
+
export function parseRustPanicHeader(line) {
|
|
50
|
+
const modern = HEADER_MODERN.exec(line);
|
|
51
|
+
if (modern?.groups) {
|
|
52
|
+
return {
|
|
53
|
+
thread: modern.groups["thread"] ?? "",
|
|
54
|
+
file: modern.groups["file"] ?? "",
|
|
55
|
+
line: Number(modern.groups["line"]),
|
|
56
|
+
column: Number(modern.groups["col"]),
|
|
57
|
+
message: null,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const legacy = HEADER_LEGACY.exec(line);
|
|
61
|
+
if (legacy?.groups) {
|
|
62
|
+
return {
|
|
63
|
+
thread: legacy.groups["thread"] ?? "",
|
|
64
|
+
file: legacy.groups["file"] ?? "",
|
|
65
|
+
line: Number(legacy.groups["line"]),
|
|
66
|
+
column: Number(legacy.groups["col"]),
|
|
67
|
+
message: legacy.groups["message"] ?? "",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
export function isRustHeaderLine(line) {
|
|
73
|
+
return parseRustPanicHeader(line) !== null;
|
|
74
|
+
}
|
|
75
|
+
export function isRustBacktraceStartLine(line) {
|
|
76
|
+
return BACKTRACE_START.test(line);
|
|
77
|
+
}
|
|
78
|
+
export function isRustFrameSymbolLine(line) {
|
|
79
|
+
return FRAME_SYMBOL.test(line);
|
|
80
|
+
}
|
|
81
|
+
export function isRustFrameLocationLine(line) {
|
|
82
|
+
return FRAME_LOCATION.test(line);
|
|
83
|
+
}
|
|
84
|
+
export function isRustNoteLine(line) {
|
|
85
|
+
return NOTE.test(line);
|
|
86
|
+
}
|
|
87
|
+
/** The symbol half of a frame; the location half is optional and supplied separately. */
|
|
88
|
+
export function parseRustFrameSymbol(line) {
|
|
89
|
+
const match = FRAME_SYMBOL.exec(line);
|
|
90
|
+
return match?.groups?.["symbol"] ?? null;
|
|
91
|
+
}
|
|
92
|
+
export function parseRustFrameLocation(line) {
|
|
93
|
+
const match = FRAME_LOCATION.exec(line);
|
|
94
|
+
if (!match?.groups)
|
|
95
|
+
return null;
|
|
96
|
+
return {
|
|
97
|
+
file: match.groups["file"] ?? "",
|
|
98
|
+
line: Number(match.groups["line"]),
|
|
99
|
+
column: Number(match.groups["col"]),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* A stateful `LineClassifier`, for RT-035's reason.
|
|
104
|
+
*
|
|
105
|
+
* Rust's specific hazard is ` 0: something` — a numbered list item is an
|
|
106
|
+
* ordinary thing to print, and a stateless rule accepting it would turn any
|
|
107
|
+
* enumerated log output into a stack-trace block. A numbered symbol line
|
|
108
|
+
* therefore only counts as block content **after `stack backtrace:`**.
|
|
109
|
+
*
|
|
110
|
+
* The panic message line, which follows the header and is arbitrary text,
|
|
111
|
+
* is accepted as block content only in the single position where Rust puts
|
|
112
|
+
* it: immediately after a modern header.
|
|
113
|
+
*/
|
|
114
|
+
export function createRustLineClassifier() {
|
|
115
|
+
let justSawHeader = false;
|
|
116
|
+
let inBacktrace = false;
|
|
117
|
+
return {
|
|
118
|
+
isHeader(line) {
|
|
119
|
+
if (isRustHeaderLine(line)) {
|
|
120
|
+
justSawHeader = true;
|
|
121
|
+
inBacktrace = false;
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
},
|
|
126
|
+
isFrame(line) {
|
|
127
|
+
if (justSawHeader) {
|
|
128
|
+
// The message line, or — for a legacy header, which carries its own
|
|
129
|
+
// message — whatever comes next. Either way exactly one line.
|
|
130
|
+
justSawHeader = false;
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
if (isRustBacktraceStartLine(line)) {
|
|
134
|
+
inBacktrace = true;
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
if (isRustNoteLine(line))
|
|
138
|
+
return true;
|
|
139
|
+
if (inBacktrace && (isRustFrameSymbolLine(line) || isRustFrameLocationLine(line)))
|
|
140
|
+
return true;
|
|
141
|
+
inBacktrace = false;
|
|
142
|
+
return false;
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=rust-frame-parsing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rust-frame-parsing.js","sourceRoot":"","sources":["../src/rust-frame-parsing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The concrete `RuntimeAdapter` for Rust.
|
|
3
|
+
*
|
|
4
|
+
* **Reliability is decided per frame, from whether the frame printed an
|
|
5
|
+
* `at` line at all.** A Rust backtrace in a debug build prints
|
|
6
|
+
* `symbol` followed by `at path:line:col`; a build without debug info
|
|
7
|
+
* prints the symbol alone. That is the same binary distinction the JVM
|
|
8
|
+
* makes between a class with and without a `LineNumberTable`, so it takes
|
|
9
|
+
* the same two values: `debug-info-present` when a location came back,
|
|
10
|
+
* `debug-info-stripped` when the symbol is all there is.
|
|
11
|
+
*
|
|
12
|
+
* **Not handled, and named rather than left to be discovered:**
|
|
13
|
+
* `-C split-debuginfo=packed` puts DWARF in a separate `.dwp` file, which
|
|
14
|
+
* *is* a side artifact and would deserve the `side-artifact-*` values. The
|
|
15
|
+
* default on Linux embeds it, that is what was verified here, and inferring
|
|
16
|
+
* which mode a binary was built in from its text output is not possible —
|
|
17
|
+
* so a split-debuginfo build is currently classified as though it were
|
|
18
|
+
* embedded. It reports `debug-info-stripped` for frames whose location is
|
|
19
|
+
* missing, which is true but understates why.
|
|
20
|
+
*/
|
|
21
|
+
import type { RuntimeAdapter } from "@descryy/runtime-backend-observation";
|
|
22
|
+
import type { SourceLocationResolver } from "@descryy/runtime-backend-observation";
|
|
23
|
+
export declare function createRustSourceLocationResolver(): SourceLocationResolver;
|
|
24
|
+
export interface RustRuntimeAdapterOptions {
|
|
25
|
+
/** Attributed on every emitted Evidence's `service` field (plan §16.7). Omit when not applicable. */
|
|
26
|
+
readonly service?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function createRustRuntimeAdapter(options?: RustRuntimeAdapterOptions): RuntimeAdapter;
|
|
29
|
+
//# sourceMappingURL=rust-runtime-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rust-runtime-adapter.d.ts","sourceRoot":"","sources":["../src/rust-runtime-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,sCAAsC,CAAC;AAGhG,OAAO,KAAK,EAAoB,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAKrG,wBAAgB,gCAAgC,IAAI,sBAAsB,CAezE;AAED,MAAM,WAAW,yBAAyB;IACxC,qGAAqG;IACrG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,yBAA8B,GAAG,cAAc,CAgBhG"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The concrete `RuntimeAdapter` for Rust.
|
|
3
|
+
*
|
|
4
|
+
* **Reliability is decided per frame, from whether the frame printed an
|
|
5
|
+
* `at` line at all.** A Rust backtrace in a debug build prints
|
|
6
|
+
* `symbol` followed by `at path:line:col`; a build without debug info
|
|
7
|
+
* prints the symbol alone. That is the same binary distinction the JVM
|
|
8
|
+
* makes between a class with and without a `LineNumberTable`, so it takes
|
|
9
|
+
* the same two values: `debug-info-present` when a location came back,
|
|
10
|
+
* `debug-info-stripped` when the symbol is all there is.
|
|
11
|
+
*
|
|
12
|
+
* **Not handled, and named rather than left to be discovered:**
|
|
13
|
+
* `-C split-debuginfo=packed` puts DWARF in a separate `.dwp` file, which
|
|
14
|
+
* *is* a side artifact and would deserve the `side-artifact-*` values. The
|
|
15
|
+
* default on Linux embeds it, that is what was verified here, and inferring
|
|
16
|
+
* which mode a binary was built in from its text output is not possible —
|
|
17
|
+
* so a split-debuginfo build is currently classified as though it were
|
|
18
|
+
* embedded. It reports `debug-info-stripped` for frames whose location is
|
|
19
|
+
* missing, which is true but understates why.
|
|
20
|
+
*/
|
|
21
|
+
import { createLogCollector } from "@descryy/runtime-backend-observation";
|
|
22
|
+
import { createRustLineClassifier } from "./rust-frame-parsing.js";
|
|
23
|
+
import { createRustStackTraceParser } from "./rust-stack-trace-parser.js";
|
|
24
|
+
export function createRustSourceLocationResolver() {
|
|
25
|
+
return {
|
|
26
|
+
resolve(frame) {
|
|
27
|
+
return Promise.resolve({
|
|
28
|
+
file: frame.file,
|
|
29
|
+
line: frame.line,
|
|
30
|
+
// Rust is the only runtime here besides V8 that prints a real
|
|
31
|
+
// column, and it prints one in the panic header too.
|
|
32
|
+
column: frame.column,
|
|
33
|
+
functionName: frame.functionName,
|
|
34
|
+
reliability: frame.line === null ? "debug-info-stripped" : "debug-info-present",
|
|
35
|
+
resolvedVia: null,
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function createRustRuntimeAdapter(options = {}) {
|
|
41
|
+
const sourceLocationResolver = createRustSourceLocationResolver();
|
|
42
|
+
const stackTraceParser = createRustStackTraceParser(sourceLocationResolver);
|
|
43
|
+
function createBackendCollectors(sources) {
|
|
44
|
+
return sources.map((source) => createLogCollector({
|
|
45
|
+
source,
|
|
46
|
+
classifier: createRustLineClassifier(),
|
|
47
|
+
stackTraceParser,
|
|
48
|
+
...(options.service !== undefined ? { service: options.service } : {}),
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
return { language: "rust", stackTraceParser, sourceLocationResolver, createBackendCollectors };
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=rust-runtime-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rust-runtime-adapter.js","sourceRoot":"","sources":["../src/rust-runtime-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAG1E,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE1E,MAAM,UAAU,gCAAgC;IAC9C,OAAO;QACL,OAAO,CAAC,KAAuB;YAC7B,OAAO,OAAO,CAAC,OAAO,CAAC;gBACrB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,8DAA8D;gBAC9D,qDAAqD;gBACrD,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,WAAW,EAAE,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,oBAAoB;gBAC/E,WAAW,EAAE,IAAI;aAClB,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAOD,MAAM,UAAU,wBAAwB,CAAC,UAAqC,EAAE;IAC9E,MAAM,sBAAsB,GAAG,gCAAgC,EAAE,CAAC;IAClE,MAAM,gBAAgB,GAAG,0BAA0B,CAAC,sBAAsB,CAAC,CAAC;IAE5E,SAAS,uBAAuB,CAAC,OAAuC;QACtE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAC5B,kBAAkB,CAAC;YACjB,MAAM;YACN,UAAU,EAAE,wBAAwB,EAAE;YACtC,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,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,CAAC;AACjG,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composes Rust's line recognition with a `SourceLocationResolver`.
|
|
3
|
+
*
|
|
4
|
+
* **A panic with no backtrace still yields a location, and that is the
|
|
5
|
+
* common case.** `RUST_BACKTRACE` is off by default, so ordinary production
|
|
6
|
+
* output is three lines: the header, the message, and a note suggesting you
|
|
7
|
+
* turn the backtrace on. The header carries `file:line:column` for the
|
|
8
|
+
* panic site itself — more precise than most runtimes manage *with* a
|
|
9
|
+
* backtrace. Returning null there would discard the best location Rust ever
|
|
10
|
+
* prints, so a header-only panic produces a single-frame trace whose
|
|
11
|
+
* `functionName` is null: the position is known, the function is not, and
|
|
12
|
+
* neither is invented.
|
|
13
|
+
*
|
|
14
|
+
* When a backtrace is present, its frames replace that single frame — they
|
|
15
|
+
* include the panic site and add the chain above it.
|
|
16
|
+
*
|
|
17
|
+
* **`std` and `core` frames are kept.** They are real frames and the top of
|
|
18
|
+
* every Rust backtrace is several of them (`rust_begin_unwind`,
|
|
19
|
+
* `panic_fmt`, `expect_failed`). Filtering them would be a readability
|
|
20
|
+
* judgment applied to evidence, and the frame list is not the place for it
|
|
21
|
+
* — a consumer that wants only application frames can filter on the paths,
|
|
22
|
+
* which are preserved exactly.
|
|
23
|
+
*
|
|
24
|
+
* **But the frames above the panic site are rooted out, and Rust itself
|
|
25
|
+
* says where that is.** Measured on a live threaded server (RT-121):
|
|
26
|
+
*
|
|
27
|
+
* ```
|
|
28
|
+
* thread '<unnamed>' (531538) panicked at server.rs:10:31:
|
|
29
|
+
* invoice has no customer
|
|
30
|
+
* stack backtrace:
|
|
31
|
+
* 0: __rustc::rust_begin_unwind
|
|
32
|
+
* …
|
|
33
|
+
* 4: core::option::Option<T>::expect
|
|
34
|
+
* 5: server::read_customer_name
|
|
35
|
+
* at ./server.rs:10:31 <- the header's own location
|
|
36
|
+
* 6: server::delete_invoice
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* `frames[0]` becomes `Evidence.sourceLocation` for every language
|
|
40
|
+
* (`collector.ts`), so untouched this reports
|
|
41
|
+
* `library/std/src/panicking.rs:698` — inside the Rust standard library —
|
|
42
|
+
* for every panic.
|
|
43
|
+
*
|
|
44
|
+
* **And the runtime turns the backtrace on itself.** `RUST_BACKTRACE` is
|
|
45
|
+
* off in ordinary Rust, but `withDefaultProcessEnv` sets it to `1` for
|
|
46
|
+
* every process the controller spawns — a deliberate, separately-reasoned
|
|
47
|
+
* decision (a panic with no backtrace is a collector-absence masquerading
|
|
48
|
+
* as application success). So the backtrace shape is not the unusual case
|
|
49
|
+
* here; inside Descry it is the *only* case, unless a caller opts out
|
|
50
|
+
* explicitly. Which means without this rooting, the more diagnostics the
|
|
51
|
+
* platform asks Rust for, the worse the location it reports: the header-only
|
|
52
|
+
* path below yields the exact panic site, and the enriched path yields
|
|
53
|
+
* `panicking.rs`. That was measured (RT-121), after being predicted
|
|
54
|
+
* backwards — this comment first claimed the header-only shape was the
|
|
55
|
+
* production default *for the managed path*, which the controller had
|
|
56
|
+
* already made false.
|
|
57
|
+
*
|
|
58
|
+
* This is **not** the std/core filter the paragraph above refuses, and
|
|
59
|
+
* since RT-083 it does not even look like one: **every frame stays in
|
|
60
|
+
* `frames`** and `primaryFrameIndex` points at the failure site. The
|
|
61
|
+
* decision above — *"std and core frames are kept; filtering them would be
|
|
62
|
+
* a readability judgment applied to evidence"* — is honoured rather than
|
|
63
|
+
* superseded. An earlier version of this parser sliced the list, and the
|
|
64
|
+
* contract gaining a field for the distinction is what made that
|
|
65
|
+
* unnecessary.
|
|
66
|
+
*
|
|
67
|
+
* The join is exact, on a position the runtime printed itself: the header's
|
|
68
|
+
* `file:line:column`, matched against a frame's own `at` location, column
|
|
69
|
+
* included. No name is inspected, no path is classified as "library" or
|
|
70
|
+
* "application", and a backtrace with no matching frame leaves the index at
|
|
71
|
+
* `0` rather than guessing — which is why `core::option::Option<T>::expect`
|
|
72
|
+
* being a `core` frame is irrelevant. It happens to sit above the declared
|
|
73
|
+
* panic site; a `core` frame *below* it would be pointed at just the same
|
|
74
|
+
* if that is where the header said the panic was.
|
|
75
|
+
*/
|
|
76
|
+
import type { SourceLocationResolver, StackTraceParser } from "@descryy/runtime-backend-observation";
|
|
77
|
+
export declare function createRustStackTraceParser(resolver: SourceLocationResolver): StackTraceParser;
|
|
78
|
+
//# sourceMappingURL=rust-stack-trace-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rust-stack-trace-parser.d.ts","sourceRoot":"","sources":["../src/rust-stack-trace-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AA+BrG,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,sBAAsB,GAAG,gBAAgB,CAgF7F"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composes Rust's line recognition with a `SourceLocationResolver`.
|
|
3
|
+
*
|
|
4
|
+
* **A panic with no backtrace still yields a location, and that is the
|
|
5
|
+
* common case.** `RUST_BACKTRACE` is off by default, so ordinary production
|
|
6
|
+
* output is three lines: the header, the message, and a note suggesting you
|
|
7
|
+
* turn the backtrace on. The header carries `file:line:column` for the
|
|
8
|
+
* panic site itself — more precise than most runtimes manage *with* a
|
|
9
|
+
* backtrace. Returning null there would discard the best location Rust ever
|
|
10
|
+
* prints, so a header-only panic produces a single-frame trace whose
|
|
11
|
+
* `functionName` is null: the position is known, the function is not, and
|
|
12
|
+
* neither is invented.
|
|
13
|
+
*
|
|
14
|
+
* When a backtrace is present, its frames replace that single frame — they
|
|
15
|
+
* include the panic site and add the chain above it.
|
|
16
|
+
*
|
|
17
|
+
* **`std` and `core` frames are kept.** They are real frames and the top of
|
|
18
|
+
* every Rust backtrace is several of them (`rust_begin_unwind`,
|
|
19
|
+
* `panic_fmt`, `expect_failed`). Filtering them would be a readability
|
|
20
|
+
* judgment applied to evidence, and the frame list is not the place for it
|
|
21
|
+
* — a consumer that wants only application frames can filter on the paths,
|
|
22
|
+
* which are preserved exactly.
|
|
23
|
+
*
|
|
24
|
+
* **But the frames above the panic site are rooted out, and Rust itself
|
|
25
|
+
* says where that is.** Measured on a live threaded server (RT-121):
|
|
26
|
+
*
|
|
27
|
+
* ```
|
|
28
|
+
* thread '<unnamed>' (531538) panicked at server.rs:10:31:
|
|
29
|
+
* invoice has no customer
|
|
30
|
+
* stack backtrace:
|
|
31
|
+
* 0: __rustc::rust_begin_unwind
|
|
32
|
+
* …
|
|
33
|
+
* 4: core::option::Option<T>::expect
|
|
34
|
+
* 5: server::read_customer_name
|
|
35
|
+
* at ./server.rs:10:31 <- the header's own location
|
|
36
|
+
* 6: server::delete_invoice
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* `frames[0]` becomes `Evidence.sourceLocation` for every language
|
|
40
|
+
* (`collector.ts`), so untouched this reports
|
|
41
|
+
* `library/std/src/panicking.rs:698` — inside the Rust standard library —
|
|
42
|
+
* for every panic.
|
|
43
|
+
*
|
|
44
|
+
* **And the runtime turns the backtrace on itself.** `RUST_BACKTRACE` is
|
|
45
|
+
* off in ordinary Rust, but `withDefaultProcessEnv` sets it to `1` for
|
|
46
|
+
* every process the controller spawns — a deliberate, separately-reasoned
|
|
47
|
+
* decision (a panic with no backtrace is a collector-absence masquerading
|
|
48
|
+
* as application success). So the backtrace shape is not the unusual case
|
|
49
|
+
* here; inside Descry it is the *only* case, unless a caller opts out
|
|
50
|
+
* explicitly. Which means without this rooting, the more diagnostics the
|
|
51
|
+
* platform asks Rust for, the worse the location it reports: the header-only
|
|
52
|
+
* path below yields the exact panic site, and the enriched path yields
|
|
53
|
+
* `panicking.rs`. That was measured (RT-121), after being predicted
|
|
54
|
+
* backwards — this comment first claimed the header-only shape was the
|
|
55
|
+
* production default *for the managed path*, which the controller had
|
|
56
|
+
* already made false.
|
|
57
|
+
*
|
|
58
|
+
* This is **not** the std/core filter the paragraph above refuses, and
|
|
59
|
+
* since RT-083 it does not even look like one: **every frame stays in
|
|
60
|
+
* `frames`** and `primaryFrameIndex` points at the failure site. The
|
|
61
|
+
* decision above — *"std and core frames are kept; filtering them would be
|
|
62
|
+
* a readability judgment applied to evidence"* — is honoured rather than
|
|
63
|
+
* superseded. An earlier version of this parser sliced the list, and the
|
|
64
|
+
* contract gaining a field for the distinction is what made that
|
|
65
|
+
* unnecessary.
|
|
66
|
+
*
|
|
67
|
+
* The join is exact, on a position the runtime printed itself: the header's
|
|
68
|
+
* `file:line:column`, matched against a frame's own `at` location, column
|
|
69
|
+
* included. No name is inspected, no path is classified as "library" or
|
|
70
|
+
* "application", and a backtrace with no matching frame leaves the index at
|
|
71
|
+
* `0` rather than guessing — which is why `core::option::Option<T>::expect`
|
|
72
|
+
* being a `core` frame is irrelevant. It happens to sit above the declared
|
|
73
|
+
* panic site; a `core` frame *below* it would be pointed at just the same
|
|
74
|
+
* if that is where the header said the panic was.
|
|
75
|
+
*/
|
|
76
|
+
import { isRustBacktraceStartLine, parseRustFrameLocation, parseRustFrameSymbol, parseRustPanicHeader, } from "./rust-frame-parsing.js";
|
|
77
|
+
/**
|
|
78
|
+
* Whether a backtrace frame's own `at` location is the position the panic
|
|
79
|
+
* header declared.
|
|
80
|
+
*
|
|
81
|
+
* Line and column must match exactly — a column is precise enough that two
|
|
82
|
+
* distinct frames sharing one is not a case worth hedging against. The file
|
|
83
|
+
* comparison is a suffix test in either direction because the two are
|
|
84
|
+
* printed by different code paths and disagree on prefix: the header says
|
|
85
|
+
* `server.rs:10:31` where the frame says `at ./server.rs:10:31` (measured),
|
|
86
|
+
* and under cargo both are relative to different roots. Requiring string
|
|
87
|
+
* equality would silently never match, which is the failure mode that reads
|
|
88
|
+
* as "this rule does nothing" while every test still passes.
|
|
89
|
+
*/
|
|
90
|
+
function isDeclaredPanicSite(header, location) {
|
|
91
|
+
if (location === null)
|
|
92
|
+
return false;
|
|
93
|
+
if (location.line !== header.line || location.column !== header.column)
|
|
94
|
+
return false;
|
|
95
|
+
const frameFile = location.file.replace(/^\.\//, "");
|
|
96
|
+
const headerFile = header.file.replace(/^\.\//, "");
|
|
97
|
+
return frameFile.endsWith(headerFile) || headerFile.endsWith(frameFile);
|
|
98
|
+
}
|
|
99
|
+
export function createRustStackTraceParser(resolver) {
|
|
100
|
+
return {
|
|
101
|
+
async parse(rawText) {
|
|
102
|
+
const lines = rawText.split(/\r\n|\r|\n/);
|
|
103
|
+
const header = lines.map(parseRustPanicHeader).find((h) => h !== null) ?? null;
|
|
104
|
+
const backtraceStart = lines.findIndex(isRustBacktraceStartLine);
|
|
105
|
+
if (header === null && backtraceStart === -1)
|
|
106
|
+
return null;
|
|
107
|
+
const frames = [];
|
|
108
|
+
/** Index of the frame at the header's own declared position, or -1 if the backtrace never names it. */
|
|
109
|
+
let declaredPanicSite = -1;
|
|
110
|
+
if (backtraceStart !== -1) {
|
|
111
|
+
for (let i = backtraceStart + 1; i < lines.length; i++) {
|
|
112
|
+
const symbolLine = lines[i];
|
|
113
|
+
if (symbolLine === undefined)
|
|
114
|
+
continue;
|
|
115
|
+
const symbol = parseRustFrameSymbol(symbolLine);
|
|
116
|
+
if (symbol === null)
|
|
117
|
+
continue;
|
|
118
|
+
// The `at` line is optional: a build without debug info prints
|
|
119
|
+
// the symbol alone. Looking ahead one line and accepting its
|
|
120
|
+
// absence is what makes debug-info-stripped observable rather
|
|
121
|
+
// than a fabricated location.
|
|
122
|
+
const next = lines[i + 1];
|
|
123
|
+
const location = next === undefined ? null : parseRustFrameLocation(next);
|
|
124
|
+
const resolved = await resolver.resolve({
|
|
125
|
+
file: location?.file ?? null,
|
|
126
|
+
line: location?.line ?? null,
|
|
127
|
+
column: location?.column ?? null,
|
|
128
|
+
functionName: symbol,
|
|
129
|
+
});
|
|
130
|
+
frames.push({ location: resolved, raw: location === null ? symbolLine : `${symbolLine}\n${next}` });
|
|
131
|
+
// First match only: a recursive panic site would repeat the
|
|
132
|
+
// position further down, and the outermost repetition is not
|
|
133
|
+
// where execution was.
|
|
134
|
+
if (declaredPanicSite === -1 && header !== null && isDeclaredPanicSite(header, location)) {
|
|
135
|
+
declaredPanicSite = frames.length - 1;
|
|
136
|
+
}
|
|
137
|
+
if (location !== null)
|
|
138
|
+
i++;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (frames.length === 0) {
|
|
142
|
+
if (header === null)
|
|
143
|
+
return null;
|
|
144
|
+
// Header-only panic: the position is real, the function is not
|
|
145
|
+
// known, and `functionName: null` says so rather than guessing.
|
|
146
|
+
const resolved = await resolver.resolve({
|
|
147
|
+
file: header.file,
|
|
148
|
+
line: header.line,
|
|
149
|
+
column: header.column,
|
|
150
|
+
functionName: null,
|
|
151
|
+
});
|
|
152
|
+
// One frame, and it IS the panic site — the header said so.
|
|
153
|
+
return { fidelity: "synchronous", frames: [{ location: resolved, raw: rawText.split(/\r\n|\r|\n/)[0] ?? "" }], primaryFrameIndex: 0 };
|
|
154
|
+
}
|
|
155
|
+
// Points at the position the header declared, when the backtrace
|
|
156
|
+
// names it. `declaredPanicSite === -1` means it did not — an inlined
|
|
157
|
+
// frame, a `RUST_BACKTRACE=full` layout, a header this parser read
|
|
158
|
+
// from a different panic — and the index stays `0`, because the
|
|
159
|
+
// alternative is choosing a site on no evidence.
|
|
160
|
+
const primaryFrameIndex = declaredPanicSite > 0 ? declaredPanicSite : 0;
|
|
161
|
+
// Rust prints most-recent-first (index 0 is the innermost frame),
|
|
162
|
+
// like V8 and the JVM and unlike CPython — read off the real
|
|
163
|
+
// backtrace, where `delete_invoice` precedes `handler` precedes
|
|
164
|
+
// `main`. No reversal.
|
|
165
|
+
//
|
|
166
|
+
// Fidelity is `synchronous`: a Rust backtrace walks the real thread
|
|
167
|
+
// stack. An async task's stack genuinely ends at the executor's poll
|
|
168
|
+
// rather than at the logical `.await`er, but nothing in the text says
|
|
169
|
+
// so — detecting it would need runtime task correlation, and claiming
|
|
170
|
+
// fragmentation without evidence is the guess this project refuses.
|
|
171
|
+
// A disclosed under-report, consistent with Kotlin's marker rule.
|
|
172
|
+
return { fidelity: "synchronous", frames, primaryFrameIndex };
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=rust-stack-trace-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rust-stack-trace-parser.js","sourceRoot":"","sources":["../src/rust-stack-trace-parser.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@descryy/runtime-adapter-rust",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Runtime adapter (rust).",
|
|
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": "*",
|
|
28
|
+
"@descryy/runtime-backend-observation": "*"
|
|
29
|
+
}
|
|
30
|
+
}
|