@rasputin-ai/node 0.5.0-alpha.12 → 0.5.0-alpha.14
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/README.md +60 -29
- package/dist/auto-instrumentation/bun-plugin.d.ts.map +1 -1
- package/dist/auto-instrumentation/format-configuration-report.d.ts +4 -0
- package/dist/auto-instrumentation/format-configuration-report.d.ts.map +1 -1
- package/dist/auto-instrumentation/instrument-compiled-esm.d.ts +1 -3
- package/dist/auto-instrumentation/instrument-compiled-esm.d.ts.map +1 -1
- package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts +10 -2
- package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts.map +1 -1
- package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts +6 -0
- package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts.map +1 -1
- package/dist/auto-instrumentation/node-hooks.d.ts.map +1 -1
- package/dist/auto-instrumentation/parameter-initializer-may-run-code.d.ts +3 -0
- package/dist/auto-instrumentation/parameter-initializer-may-run-code.d.ts.map +1 -0
- package/dist/auto-instrumentation/schedule-instrumentation-manifest-upload.d.ts +1 -1
- package/dist/auto-instrumentation/schedule-instrumentation-manifest-upload.d.ts.map +1 -1
- package/dist/auto-instrumentation/source-map-locations.d.ts +1 -0
- package/dist/auto-instrumentation/source-map-locations.d.ts.map +1 -1
- package/dist/auto-instrumentation/suspension-whitelist.d.ts +33 -0
- package/dist/auto-instrumentation/suspension-whitelist.d.ts.map +1 -0
- package/dist/auto-instrumentation/transform-disk-cache.d.ts +7 -0
- package/dist/auto-instrumentation/transform-disk-cache.d.ts.map +1 -0
- package/dist/auto-instrumentation/transform-source.d.ts +9 -6
- package/dist/auto-instrumentation/transform-source.d.ts.map +1 -1
- package/dist/auto-instrumentation/verify-instrumentation-isomorphism.d.ts +2 -0
- package/dist/auto-instrumentation/verify-instrumentation-isomorphism.d.ts.map +1 -0
- package/dist/auto-instrumentation/verify-transformed-source.d.ts +2 -0
- package/dist/auto-instrumentation/verify-transformed-source.d.ts.map +1 -0
- package/dist/execution-recorder/automatic-runtime.d.ts +17 -7
- package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
- package/dist/execution-recorder/encode-frozen.d.ts +16 -0
- package/dist/execution-recorder/encode-frozen.d.ts.map +1 -0
- package/dist/execution-recorder/execution-journal.d.ts.map +1 -1
- package/dist/execution-recorder/execution-recorder-types.d.ts +124 -36
- package/dist/execution-recorder/execution-recorder-types.d.ts.map +1 -1
- package/dist/execution-recorder/execution-recorder.d.ts +2 -2
- package/dist/execution-recorder/execution-recorder.d.ts.map +1 -1
- package/dist/execution-recorder/function-plan.d.ts +8 -1
- package/dist/execution-recorder/function-plan.d.ts.map +1 -1
- package/dist/execution-recorder/index.d.ts +1 -1
- package/dist/execution-recorder/index.d.ts.map +1 -1
- package/dist/execution-recorder/recorder-memory-budget.d.ts +1 -0
- package/dist/execution-recorder/recorder-memory-budget.d.ts.map +1 -1
- package/dist/execution-recorder/redact-keys.d.ts +4 -0
- package/dist/execution-recorder/redact-keys.d.ts.map +1 -0
- package/dist/execution-recorder/run-exactly-once.d.ts +7 -0
- package/dist/execution-recorder/run-exactly-once.d.ts.map +1 -0
- package/dist/execution-recorder/runtime-state-size-budget.d.ts +14 -0
- package/dist/execution-recorder/runtime-state-size-budget.d.ts.map +1 -0
- package/dist/execution-recorder/safe-serialize.d.ts +9 -3
- package/dist/execution-recorder/safe-serialize.d.ts.map +1 -1
- package/dist/execution-recorder/value-freeze.d.ts +43 -0
- package/dist/execution-recorder/value-freeze.d.ts.map +1 -0
- package/dist/execution-recorder/warn-missing-async-context-frame.d.ts +10 -0
- package/dist/execution-recorder/warn-missing-async-context-frame.d.ts.map +1 -0
- package/dist/index.d.ts +1 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2597 -892
- package/dist/install-global-handlers.d.ts +29 -8
- package/dist/install-global-handlers.d.ts.map +1 -1
- package/dist/instrument/build.js +947 -805
- package/dist/instrument/bun.js +1098 -778
- package/dist/instrument/node.js +1078 -780
- package/dist/internal.d.ts +7 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +4072 -0
- package/dist/rasputin-init.d.ts +25 -5
- package/dist/rasputin-init.d.ts.map +1 -1
- package/dist/sdk-meta.d.ts +1 -1
- package/package.json +7 -2
- package/dist/derivation/analyze-function.d.ts +0 -42
- package/dist/derivation/analyze-function.d.ts.map +0 -1
- package/dist/derivation/build-dependency-graph.d.ts +0 -21
- package/dist/derivation/build-dependency-graph.d.ts.map +0 -1
- package/dist/derivation/derivation-ir.d.ts +0 -33
- package/dist/derivation/derivation-ir.d.ts.map +0 -1
- package/dist/derivation/derivation-proof.d.ts +0 -14
- package/dist/derivation/derivation-proof.d.ts.map +0 -1
- package/dist/derivation/emit-capture-plan.d.ts +0 -8
- package/dist/derivation/emit-capture-plan.d.ts.map +0 -1
package/dist/instrument/bun.js
CHANGED
|
@@ -3,7 +3,7 @@ import { plugin } from "bun";
|
|
|
3
3
|
|
|
4
4
|
// src/auto-instrumentation/bun-plugin.ts
|
|
5
5
|
import { readFile } from "node:fs/promises";
|
|
6
|
-
import { resolve as
|
|
6
|
+
import { resolve as resolve5 } from "node:path";
|
|
7
7
|
|
|
8
8
|
// src/auto-instrumentation/instrumentation-manifest-registry.ts
|
|
9
9
|
import { resolve } from "node:path";
|
|
@@ -17,6 +17,16 @@ import {
|
|
|
17
17
|
runtimeFunctionId,
|
|
18
18
|
sourceLocatorFromFile
|
|
19
19
|
} from "@rasputin-ai/core";
|
|
20
|
+
var withPreBodyParameterEvaluation = (plan) => [
|
|
21
|
+
plan[0],
|
|
22
|
+
plan[1],
|
|
23
|
+
plan[2],
|
|
24
|
+
plan[3],
|
|
25
|
+
plan[4],
|
|
26
|
+
plan[5],
|
|
27
|
+
plan[6],
|
|
28
|
+
1
|
|
29
|
+
];
|
|
20
30
|
var encodeFunctionPlan = (source) => {
|
|
21
31
|
const locator = sourceLocatorFromFile(source.filePath);
|
|
22
32
|
const definition = {
|
|
@@ -39,19 +49,26 @@ var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
|
|
|
39
49
|
var getRegistry = () => {
|
|
40
50
|
const existing = Reflect.get(globalThis, registrySymbol);
|
|
41
51
|
if (existing?.__rasputinManifestRegistry && existing.files instanceof Map && typeof existing.generation === "number" && typeof existing.uploadedGeneration === "number") {
|
|
42
|
-
|
|
52
|
+
const registry2 = existing;
|
|
53
|
+
if (typeof registry2.alreadyInstrumented !== "number") registry2.alreadyInstrumented = 0;
|
|
54
|
+
return registry2;
|
|
43
55
|
}
|
|
44
56
|
const registry = {
|
|
45
57
|
__rasputinManifestRegistry: true,
|
|
46
58
|
files: /* @__PURE__ */ new Map(),
|
|
47
59
|
generation: 0,
|
|
48
|
-
uploadedGeneration: 0
|
|
60
|
+
uploadedGeneration: 0,
|
|
61
|
+
alreadyInstrumented: 0
|
|
49
62
|
};
|
|
50
63
|
Reflect.set(globalThis, registrySymbol, registry);
|
|
51
64
|
return registry;
|
|
52
65
|
};
|
|
53
66
|
var recordInstrumentationManifestDelta = (filePath, delta) => {
|
|
54
67
|
const registry = getRegistry();
|
|
68
|
+
if (delta.alreadyInstrumented) {
|
|
69
|
+
registry.alreadyInstrumented += 1;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
55
72
|
registry.files.set(resolve(filePath), delta);
|
|
56
73
|
registry.generation += 1;
|
|
57
74
|
registry.onDirty?.();
|
|
@@ -86,665 +103,74 @@ var isApplicationSource = (filePath, options = {}) => {
|
|
|
86
103
|
return true;
|
|
87
104
|
};
|
|
88
105
|
|
|
89
|
-
// src/auto-instrumentation/transform-
|
|
90
|
-
import {
|
|
91
|
-
import
|
|
106
|
+
// src/auto-instrumentation/transform-disk-cache.ts
|
|
107
|
+
import { createHash } from "node:crypto";
|
|
108
|
+
import {
|
|
109
|
+
chmodSync,
|
|
110
|
+
lstatSync,
|
|
111
|
+
mkdirSync,
|
|
112
|
+
readdirSync,
|
|
113
|
+
readFileSync as readFileSync2,
|
|
114
|
+
statSync,
|
|
115
|
+
unlinkSync,
|
|
116
|
+
writeFileSync
|
|
117
|
+
} from "node:fs";
|
|
118
|
+
import { homedir } from "node:os";
|
|
119
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
92
120
|
|
|
93
|
-
// src/
|
|
94
|
-
|
|
121
|
+
// src/sdk-meta.ts
|
|
122
|
+
var SDK_VERSION = "0.5.0-alpha.14";
|
|
95
123
|
|
|
96
|
-
// src/
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return
|
|
107
|
-
functions: functions.map((fn) => fn.functionId),
|
|
108
|
-
edges: localEdges,
|
|
109
|
-
calleesByCaller
|
|
110
|
-
};
|
|
111
|
-
};
|
|
112
|
-
var topologicalFunctionOrder = (functions, graph) => {
|
|
113
|
-
const idByName = new Map(functions.map((fn) => [fn.name, fn.functionId]));
|
|
114
|
-
const remainingCallees = new Map(
|
|
115
|
-
functions.map((fn) => {
|
|
116
|
-
const unique = new Set(
|
|
117
|
-
(graph.calleesByCaller.get(fn.functionId) ?? []).map((name) => idByName.get(name)).filter((id) => Boolean(id) && id !== fn.functionId)
|
|
118
|
-
);
|
|
119
|
-
return [fn.functionId, unique.size];
|
|
120
|
-
})
|
|
121
|
-
);
|
|
122
|
-
const callersOf = /* @__PURE__ */ new Map();
|
|
123
|
-
for (const fn of functions) {
|
|
124
|
-
for (const calleeName of graph.calleesByCaller.get(fn.functionId) ?? []) {
|
|
125
|
-
const calleeId = idByName.get(calleeName);
|
|
126
|
-
if (!calleeId || calleeId === fn.functionId) continue;
|
|
127
|
-
const callers = callersOf.get(calleeId) ?? [];
|
|
128
|
-
callers.push(fn.functionId);
|
|
129
|
-
callersOf.set(calleeId, callers);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
const queue = functions.filter((fn) => (remainingCallees.get(fn.functionId) ?? 0) === 0).map((fn) => fn.functionId);
|
|
133
|
-
const ordered = [];
|
|
134
|
-
const queued = new Set(queue);
|
|
135
|
-
while (queue.length > 0) {
|
|
136
|
-
const current = queue.shift();
|
|
137
|
-
if (current === void 0) break;
|
|
138
|
-
ordered.push(current);
|
|
139
|
-
for (const caller of callersOf.get(current) ?? []) {
|
|
140
|
-
const next = (remainingCallees.get(caller) ?? 0) - 1;
|
|
141
|
-
remainingCallees.set(caller, next);
|
|
142
|
-
if (next === 0 && !queued.has(caller)) {
|
|
143
|
-
queued.add(caller);
|
|
144
|
-
queue.push(caller);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
for (const fn of functions) {
|
|
149
|
-
if (!queued.has(fn.functionId)) ordered.push(fn.functionId);
|
|
150
|
-
}
|
|
151
|
-
return ordered;
|
|
124
|
+
// src/auto-instrumentation/source-map-locations.ts
|
|
125
|
+
import { readFileSync } from "node:fs";
|
|
126
|
+
import { dirname, isAbsolute as isAbsolute2, join, resolve as resolve3 } from "node:path";
|
|
127
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
128
|
+
import { LEAST_UPPER_BOUND, originalPositionFor, TraceMap } from "@jridgewell/trace-mapping";
|
|
129
|
+
var sourceMapComment = /(?:\/\/[#@][ \t]*sourceMappingURL=([^\s'"]+)|\/\*[#@][ \t]*sourceMappingURL=([^\s*'"]+)[ \t]*\*\/)\s*$/;
|
|
130
|
+
var decodeDataUrl = (url) => {
|
|
131
|
+
const base64 = /^data:application\/json(?:;charset=[^;,]+)?;base64,(.+)$/i.exec(url);
|
|
132
|
+
if (base64?.[1]) return Buffer.from(base64[1], "base64").toString("utf8");
|
|
133
|
+
const raw = /^data:application\/json(?:;charset=[^;,]+)?,(.+)$/i.exec(url);
|
|
134
|
+
return raw?.[1] ? decodeURIComponent(raw[1]) : void 0;
|
|
152
135
|
};
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
param(index) {
|
|
162
|
-
return this.add({ id: this.nextId++, kind: "param", index });
|
|
163
|
-
}
|
|
164
|
-
root(rootId) {
|
|
165
|
-
return this.add({ id: this.nextId++, kind: "root", rootId });
|
|
166
|
-
}
|
|
167
|
-
alias(from) {
|
|
168
|
-
return this.add({ id: this.nextId++, kind: "alias", from });
|
|
169
|
-
}
|
|
170
|
-
binary(op, left, right, guard) {
|
|
171
|
-
return this.add({ id: this.nextId++, kind: "binary", op, left, right, guard });
|
|
172
|
-
}
|
|
173
|
-
unary(op, operand) {
|
|
174
|
-
return this.add({ id: this.nextId++, kind: "unary", op, operand });
|
|
175
|
-
}
|
|
176
|
-
logical(op, left, right) {
|
|
177
|
-
return this.add({ id: this.nextId++, kind: "logical", op, left, right });
|
|
178
|
-
}
|
|
179
|
-
cond(test, consequent, alternate) {
|
|
180
|
-
return this.add({ id: this.nextId++, kind: "cond", test, consequent, alternate });
|
|
181
|
-
}
|
|
182
|
-
object(properties) {
|
|
183
|
-
return this.add({ id: this.nextId++, kind: "object", properties });
|
|
184
|
-
}
|
|
185
|
-
array(elements) {
|
|
186
|
-
return this.add({ id: this.nextId++, kind: "array", elements });
|
|
187
|
-
}
|
|
188
|
-
prop(object, key) {
|
|
189
|
-
return this.add({ id: this.nextId++, kind: "prop", object, key });
|
|
190
|
-
}
|
|
191
|
-
index(array, index) {
|
|
192
|
-
return this.add({ id: this.nextId++, kind: "index", array, index });
|
|
193
|
-
}
|
|
194
|
-
pureCall(calleeFunctionId, args) {
|
|
195
|
-
return this.add({ id: this.nextId++, kind: "pure_call", calleeFunctionId, args });
|
|
196
|
-
}
|
|
197
|
-
template(parts) {
|
|
198
|
-
return this.add({ id: this.nextId++, kind: "template", parts });
|
|
199
|
-
}
|
|
200
|
-
add(expression) {
|
|
201
|
-
this.expressions.push(expression);
|
|
202
|
-
return expression.id;
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
var emptyPlan = (functionId) => ({
|
|
206
|
-
planId: `dp_${functionId}`,
|
|
207
|
-
functionId,
|
|
208
|
-
expressions: [],
|
|
209
|
-
variables: [],
|
|
210
|
-
captureBoundaries: [],
|
|
211
|
-
controlFacts: []
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
// src/derivation/analyze-function.ts
|
|
215
|
-
var loc = (node, sourceFile) => {
|
|
216
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
217
|
-
return { line: position.line + 1, column: position.character + 1 };
|
|
218
|
-
};
|
|
219
|
-
var lookup = (ctx, name) => {
|
|
220
|
-
for (let index = ctx.scopes.length - 1; index >= 0; index--) {
|
|
221
|
-
const binding = ctx.scopes[index]?.get(name);
|
|
222
|
-
if (binding) return binding;
|
|
223
|
-
}
|
|
224
|
-
return void 0;
|
|
225
|
-
};
|
|
226
|
-
var fallback = (ctx, reason) => {
|
|
227
|
-
ctx.fallback ??= reason;
|
|
228
|
-
return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "unknown" };
|
|
229
|
-
};
|
|
230
|
-
var capture = (ctx, node, reason) => {
|
|
231
|
-
const rootId = ctx.nextRootId.value++;
|
|
232
|
-
const position = loc(node, ctx.sourceFile);
|
|
233
|
-
const expressionId = ctx.ir.root(rootId);
|
|
234
|
-
ctx.captureWraps.push({
|
|
235
|
-
rootId,
|
|
236
|
-
reason,
|
|
237
|
-
start: node.getStart(ctx.sourceFile),
|
|
238
|
-
end: node.end,
|
|
239
|
-
line: position.line,
|
|
240
|
-
column: position.column,
|
|
241
|
-
expressionId
|
|
242
|
-
});
|
|
243
|
-
return { expressionId, shape: "unknown", captured: true };
|
|
244
|
-
};
|
|
245
|
-
var binaryOp = (kind) => {
|
|
246
|
-
switch (kind) {
|
|
247
|
-
case ts.SyntaxKind.PlusToken:
|
|
248
|
-
return "add";
|
|
249
|
-
case ts.SyntaxKind.MinusToken:
|
|
250
|
-
return "sub";
|
|
251
|
-
case ts.SyntaxKind.AsteriskToken:
|
|
252
|
-
return "mul";
|
|
253
|
-
case ts.SyntaxKind.SlashToken:
|
|
254
|
-
return "div";
|
|
255
|
-
case ts.SyntaxKind.PercentToken:
|
|
256
|
-
return "mod";
|
|
257
|
-
case ts.SyntaxKind.EqualsEqualsToken:
|
|
258
|
-
return "eq";
|
|
259
|
-
case ts.SyntaxKind.ExclamationEqualsToken:
|
|
260
|
-
return "neq";
|
|
261
|
-
case ts.SyntaxKind.EqualsEqualsEqualsToken:
|
|
262
|
-
return "seq";
|
|
263
|
-
case ts.SyntaxKind.ExclamationEqualsEqualsToken:
|
|
264
|
-
return "sneq";
|
|
265
|
-
case ts.SyntaxKind.LessThanToken:
|
|
266
|
-
return "lt";
|
|
267
|
-
case ts.SyntaxKind.LessThanEqualsToken:
|
|
268
|
-
return "lte";
|
|
269
|
-
case ts.SyntaxKind.GreaterThanToken:
|
|
270
|
-
return "gt";
|
|
271
|
-
case ts.SyntaxKind.GreaterThanEqualsToken:
|
|
272
|
-
return "gte";
|
|
273
|
-
default:
|
|
274
|
-
return void 0;
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
var isNumericBinaryOp = (op) => op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod";
|
|
278
|
-
var unwrapParens = (node) => {
|
|
279
|
-
while (ts.isParenthesizedExpression(node)) node = node.expression;
|
|
280
|
-
return node;
|
|
281
|
-
};
|
|
282
|
-
var numericLiteralValue = (node) => {
|
|
283
|
-
const value = Number(node.text);
|
|
284
|
-
return { type: "number", value };
|
|
285
|
-
};
|
|
286
|
-
var analyzeExpression = (ctx, node) => {
|
|
287
|
-
if (ctx.fallback) return fallback(ctx, ctx.fallback);
|
|
288
|
-
if (ts.isParenthesizedExpression(node)) return analyzeExpression(ctx, node.expression);
|
|
289
|
-
if (ts.isAsExpression(node) || ts.isNonNullExpression(node)) {
|
|
290
|
-
return analyzeExpression(ctx, node.expression);
|
|
291
|
-
}
|
|
292
|
-
if (node.kind === ts.SyntaxKind.TypeAssertionExpression) {
|
|
293
|
-
return analyzeExpression(ctx, node.expression);
|
|
294
|
-
}
|
|
295
|
-
if (ts.isSatisfiesExpression(node)) return analyzeExpression(ctx, node.expression);
|
|
296
|
-
if (node.kind === ts.SyntaxKind.NullKeyword) {
|
|
297
|
-
return { expressionId: ctx.ir.constValue({ type: "null" }), shape: "primitive" };
|
|
298
|
-
}
|
|
299
|
-
if (node.kind === ts.SyntaxKind.TrueKeyword) {
|
|
300
|
-
return {
|
|
301
|
-
expressionId: ctx.ir.constValue({ type: "boolean", value: true }),
|
|
302
|
-
shape: "primitive"
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
if (node.kind === ts.SyntaxKind.FalseKeyword) {
|
|
306
|
-
return {
|
|
307
|
-
expressionId: ctx.ir.constValue({ type: "boolean", value: false }),
|
|
308
|
-
shape: "primitive"
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
if (ts.isNumericLiteral(node)) {
|
|
312
|
-
return { expressionId: ctx.ir.constValue(numericLiteralValue(node)), shape: "primitive" };
|
|
313
|
-
}
|
|
314
|
-
if (ts.isPrefixUnaryExpression(node) && ts.isNumericLiteral(node.operand) && node.operator === ts.SyntaxKind.MinusToken) {
|
|
315
|
-
const value = -Number(node.operand.text);
|
|
316
|
-
return {
|
|
317
|
-
expressionId: ctx.ir.constValue(
|
|
318
|
-
Object.is(value, -0) ? { type: "number", value: 0, negativeZero: true } : { type: "number", value }
|
|
319
|
-
),
|
|
320
|
-
shape: "primitive"
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
324
|
-
return {
|
|
325
|
-
expressionId: ctx.ir.constValue({ type: "string", value: node.text }),
|
|
326
|
-
shape: "primitive"
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
if (ts.isBigIntLiteral(node)) {
|
|
330
|
-
return {
|
|
331
|
-
expressionId: ctx.ir.constValue({ type: "bigint", value: node.text.replace(/n$/, "") }),
|
|
332
|
-
shape: "primitive"
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
if (ts.isIdentifier(node)) {
|
|
336
|
-
if (node.text === "undefined") {
|
|
337
|
-
return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "primitive" };
|
|
338
|
-
}
|
|
339
|
-
const binding = lookup(ctx, node.text);
|
|
340
|
-
if (!binding) return capture(ctx, node, "external_state");
|
|
341
|
-
if (binding.mutable) return capture(ctx, node, "mutable_object");
|
|
342
|
-
return {
|
|
343
|
-
expressionId: ctx.ir.alias(binding.expressionId),
|
|
344
|
-
shape: binding.shape
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
if (ts.isTemplateExpression(node)) {
|
|
348
|
-
const parts = [
|
|
349
|
-
{ kind: "lit", value: node.head.text }
|
|
350
|
-
];
|
|
351
|
-
for (const span of node.templateSpans) {
|
|
352
|
-
const nested = analyzeExpression(ctx, span.expression);
|
|
353
|
-
if (nested.shape !== "primitive" && !nested.captured) {
|
|
354
|
-
return capture(ctx, node, "unsupported_expression");
|
|
355
|
-
}
|
|
356
|
-
parts.push({ kind: "expr", expressionId: nested.expressionId });
|
|
357
|
-
parts.push({ kind: "lit", value: span.literal.text });
|
|
358
|
-
}
|
|
359
|
-
return { expressionId: ctx.ir.template(parts), shape: "primitive" };
|
|
360
|
-
}
|
|
361
|
-
if (ts.isArrayLiteralExpression(node)) {
|
|
362
|
-
if (node.elements.some((element) => ts.isSpreadElement(element))) {
|
|
363
|
-
return capture(ctx, node, "unsupported_expression");
|
|
364
|
-
}
|
|
365
|
-
const elements = node.elements.map((element) => {
|
|
366
|
-
if (ts.isOmittedExpression(element)) return { kind: "hole" };
|
|
367
|
-
return analyzeExpression(ctx, element).expressionId;
|
|
368
|
-
});
|
|
369
|
-
return { expressionId: ctx.ir.array(elements), shape: "array" };
|
|
370
|
-
}
|
|
371
|
-
if (ts.isObjectLiteralExpression(node)) {
|
|
372
|
-
const properties = [];
|
|
373
|
-
for (const property of node.properties) {
|
|
374
|
-
if (ts.isShorthandPropertyAssignment(property)) {
|
|
375
|
-
const nested = analyzeExpression(ctx, property.name);
|
|
376
|
-
if (nested.captured) {
|
|
377
|
-
const wrap = ctx.captureWraps.find(
|
|
378
|
-
(candidate) => candidate.expressionId === nested.expressionId
|
|
379
|
-
);
|
|
380
|
-
if (wrap) wrap.shorthandPropertyName = property.name.text;
|
|
381
|
-
}
|
|
382
|
-
properties.push({ key: property.name.text, value: nested.expressionId });
|
|
383
|
-
continue;
|
|
384
|
-
}
|
|
385
|
-
if (!ts.isPropertyAssignment(property)) {
|
|
386
|
-
return capture(ctx, node, "unsupported_expression");
|
|
387
|
-
}
|
|
388
|
-
if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
|
|
389
|
-
return capture(ctx, node, "unsupported_expression");
|
|
390
|
-
}
|
|
391
|
-
const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
|
|
392
|
-
if (key === void 0) return capture(ctx, node, "unsupported_expression");
|
|
393
|
-
properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
|
|
394
|
-
}
|
|
395
|
-
return { expressionId: ctx.ir.object(properties), shape: "object" };
|
|
396
|
-
}
|
|
397
|
-
if (ts.isPropertyAccessExpression(node)) {
|
|
398
|
-
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
399
|
-
const object = analyzeExpression(ctx, node.expression);
|
|
400
|
-
if (object.shape === "object" && ts.isIdentifier(node.name)) {
|
|
401
|
-
return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
|
|
402
|
-
}
|
|
403
|
-
return capture(ctx, node, "unknown_property");
|
|
404
|
-
}
|
|
405
|
-
if (ts.isElementAccessExpression(node)) {
|
|
406
|
-
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
407
|
-
const array = analyzeExpression(ctx, node.expression);
|
|
408
|
-
if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
|
|
409
|
-
return {
|
|
410
|
-
expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
|
|
411
|
-
shape: "unknown"
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
return capture(ctx, node, "unknown_property");
|
|
415
|
-
}
|
|
416
|
-
if (ts.isCallExpression(node)) {
|
|
417
|
-
if (node.questionDotToken || node.typeArguments?.length) {
|
|
418
|
-
return capture(ctx, node, "unsupported_expression");
|
|
419
|
-
}
|
|
420
|
-
if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
|
|
421
|
-
const calleeName = node.expression.text;
|
|
422
|
-
if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
|
|
423
|
-
const provenId = ctx.moduleCallees.get(calleeName);
|
|
424
|
-
ctx.callEdges.push({
|
|
425
|
-
callerFunctionId: ctx.functionId,
|
|
426
|
-
calleeName,
|
|
427
|
-
...provenId ? { calleeFunctionId: provenId } : {}
|
|
428
|
-
});
|
|
429
|
-
if (!provenId) return capture(ctx, node, "opaque_call");
|
|
430
|
-
const args = node.arguments.map((argument) => {
|
|
431
|
-
if (ts.isSpreadElement(argument)) return void 0;
|
|
432
|
-
return analyzeExpression(ctx, argument).expressionId;
|
|
433
|
-
});
|
|
434
|
-
if (args.some((argument) => argument === void 0))
|
|
435
|
-
return capture(ctx, node, "unsupported_expression");
|
|
436
|
-
return {
|
|
437
|
-
expressionId: ctx.ir.pureCall(
|
|
438
|
-
provenId,
|
|
439
|
-
args.filter((id) => id !== void 0)
|
|
440
|
-
),
|
|
441
|
-
shape: "unknown"
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
|
|
445
|
-
return capture(
|
|
446
|
-
ctx,
|
|
447
|
-
node,
|
|
448
|
-
node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
|
|
449
|
-
);
|
|
450
|
-
}
|
|
451
|
-
if (ts.isConditionalExpression(node)) {
|
|
452
|
-
const test = analyzeExpression(ctx, node.condition);
|
|
453
|
-
const consequent = analyzeExpression(ctx, node.whenTrue);
|
|
454
|
-
const alternate = analyzeExpression(ctx, node.whenFalse);
|
|
455
|
-
return {
|
|
456
|
-
expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
|
|
457
|
-
shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
if (ts.isBinaryExpression(node)) {
|
|
461
|
-
if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
462
|
-
ctx.fallback ??= "mutable_state";
|
|
463
|
-
return capture(ctx, node, "mutable_object");
|
|
464
|
-
}
|
|
465
|
-
if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
466
|
-
const left2 = analyzeExpression(ctx, node.left);
|
|
467
|
-
const right2 = analyzeExpression(ctx, node.right);
|
|
468
|
-
const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
|
|
469
|
-
return {
|
|
470
|
-
expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
|
|
471
|
-
shape: "unknown"
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
const op = binaryOp(node.operatorToken.kind);
|
|
475
|
-
if (!op) return capture(ctx, node, "unsupported_expression");
|
|
476
|
-
if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
|
|
477
|
-
const left = analyzeExpression(ctx, node.left);
|
|
478
|
-
const right = analyzeExpression(ctx, node.right);
|
|
479
|
-
const numeric = isNumericBinaryOp(op);
|
|
480
|
-
const expressionId = ctx.ir.binary(
|
|
481
|
-
op,
|
|
482
|
-
left.expressionId,
|
|
483
|
-
right.expressionId,
|
|
484
|
-
numeric ? "numeric" : "primitive"
|
|
485
|
-
);
|
|
486
|
-
if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
|
|
487
|
-
ctx.hasCoercionGuard = true;
|
|
488
|
-
const fallbackRootId = ctx.nextRootId.value++;
|
|
489
|
-
ctx.guardWraps.push({
|
|
490
|
-
fallbackRootId,
|
|
491
|
-
start: node.getStart(ctx.sourceFile),
|
|
492
|
-
end: node.end,
|
|
493
|
-
op
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
return { expressionId, shape: "primitive" };
|
|
497
|
-
}
|
|
498
|
-
if (ts.isPrefixUnaryExpression(node)) {
|
|
499
|
-
const operand = analyzeExpression(ctx, node.operand);
|
|
500
|
-
if (node.operator === ts.SyntaxKind.ExclamationToken) {
|
|
501
|
-
return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
|
|
502
|
-
}
|
|
503
|
-
if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
|
|
504
|
-
if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
|
|
505
|
-
return {
|
|
506
|
-
expressionId: ctx.ir.unary(
|
|
507
|
-
node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
|
|
508
|
-
operand.expressionId
|
|
509
|
-
),
|
|
510
|
-
shape: "primitive"
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
return capture(ctx, node, "unsupported_expression");
|
|
514
|
-
}
|
|
515
|
-
if (ts.isTypeOfExpression(node)) {
|
|
516
|
-
const operand = unwrapParens(node.expression);
|
|
517
|
-
if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
|
|
518
|
-
return capture(ctx, node, "external_state");
|
|
519
|
-
}
|
|
520
|
-
const inner = analyzeExpression(ctx, node.expression);
|
|
521
|
-
return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
|
|
522
|
-
}
|
|
523
|
-
return capture(ctx, node, "unsupported_expression");
|
|
524
|
-
};
|
|
525
|
-
var bindIdentifier = (ctx, name, result, declaration) => {
|
|
526
|
-
const current = ctx.scopes[ctx.scopes.length - 1];
|
|
527
|
-
current?.set(name.text, {
|
|
528
|
-
expressionId: result.expressionId,
|
|
529
|
-
shape: result.shape,
|
|
530
|
-
mutable: false
|
|
531
|
-
});
|
|
532
|
-
const position = loc(name, ctx.sourceFile);
|
|
533
|
-
const available = loc(declaration, ctx.sourceFile);
|
|
534
|
-
ctx.variables.push({
|
|
535
|
-
id: ctx.nextVariableId.value++,
|
|
536
|
-
name: name.text,
|
|
537
|
-
functionId: ctx.functionId,
|
|
538
|
-
scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
|
|
539
|
-
line: position.line,
|
|
540
|
-
column: position.column,
|
|
541
|
-
assignmentId: ctx.nextAssignmentId.value++,
|
|
542
|
-
expressionId: result.expressionId,
|
|
543
|
-
availableAtLine: available.line,
|
|
544
|
-
availableAtColumn: available.column
|
|
545
|
-
});
|
|
546
|
-
};
|
|
547
|
-
var analyzeStatement = (ctx, node) => {
|
|
548
|
-
if (ctx.fallback) return;
|
|
549
|
-
if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
|
|
550
|
-
if (ts.isBlock(node)) {
|
|
551
|
-
ctx.scopes.push(/* @__PURE__ */ new Map());
|
|
552
|
-
for (const statement of node.statements) analyzeStatement(ctx, statement);
|
|
553
|
-
ctx.scopes.pop();
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
if (ts.isReturnStatement(node)) {
|
|
557
|
-
if (!node.expression) {
|
|
558
|
-
ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
|
|
559
|
-
return;
|
|
560
|
-
}
|
|
561
|
-
ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
if (ts.isVariableStatement(node)) {
|
|
565
|
-
if (node.declarationList.flags & ts.NodeFlags.Using) {
|
|
566
|
-
fallback(ctx, "side_effect");
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
for (const declaration of node.declarationList.declarations) {
|
|
570
|
-
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
|
|
571
|
-
fallback(ctx, "unsupported_expression");
|
|
572
|
-
return;
|
|
573
|
-
}
|
|
574
|
-
const result = analyzeExpression(ctx, declaration.initializer);
|
|
575
|
-
bindIdentifier(ctx, declaration.name, result, declaration);
|
|
576
|
-
}
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
|
-
if (ts.isExpressionStatement(node)) {
|
|
580
|
-
analyzeExpression(ctx, node.expression);
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
if (ts.isThrowStatement(node)) {
|
|
584
|
-
ctx.hasThrow = true;
|
|
585
|
-
analyzeExpression(ctx, node.expression);
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
588
|
-
if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
|
|
589
|
-
fallback(ctx, "unsupported_expression");
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
|
|
593
|
-
fallback(ctx, "has_loop");
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
fallback(ctx, "unsupported_expression");
|
|
597
|
-
};
|
|
598
|
-
var bindParams = (ctx, node) => {
|
|
599
|
-
let index = 0;
|
|
600
|
-
for (const parameter of node.parameters) {
|
|
601
|
-
if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
602
|
-
if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
|
|
603
|
-
fallback(ctx, "unsupported_expression");
|
|
604
|
-
index++;
|
|
605
|
-
continue;
|
|
606
|
-
}
|
|
607
|
-
const expressionId = ctx.ir.param(index);
|
|
608
|
-
ctx.identifierParams.push(parameter.name.text);
|
|
609
|
-
bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
|
|
610
|
-
index++;
|
|
611
|
-
}
|
|
612
|
-
};
|
|
613
|
-
var analyzeOne = (node, functionId, name, sourceFile, moduleCallees) => {
|
|
614
|
-
const ctx = {
|
|
615
|
-
sourceFile,
|
|
616
|
-
functionId,
|
|
617
|
-
name,
|
|
618
|
-
ir: new DerivationIrBuilder(),
|
|
619
|
-
variables: [],
|
|
620
|
-
captureWraps: [],
|
|
621
|
-
guardWraps: [],
|
|
622
|
-
callEdges: [],
|
|
623
|
-
scopes: [/* @__PURE__ */ new Map()],
|
|
624
|
-
nextRootId: { value: 0 },
|
|
625
|
-
nextVariableId: { value: 0 },
|
|
626
|
-
nextAssignmentId: { value: 0 },
|
|
627
|
-
hasThrow: false,
|
|
628
|
-
hasCoercionGuard: false,
|
|
629
|
-
moduleCallees,
|
|
630
|
-
identifierParams: []
|
|
631
|
-
};
|
|
632
|
-
if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
|
|
633
|
-
ctx.fallback = "async";
|
|
634
|
-
} else {
|
|
635
|
-
bindParams(ctx, node);
|
|
636
|
-
if (ts.isBlock(node.body)) {
|
|
637
|
-
for (const statement of node.body.statements) analyzeStatement(ctx, statement);
|
|
638
|
-
} else {
|
|
639
|
-
ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
const plan = {
|
|
643
|
-
...emptyPlan(functionId),
|
|
644
|
-
expressions: ctx.ir.expressions,
|
|
645
|
-
variables: ctx.variables,
|
|
646
|
-
captureBoundaries: ctx.captureWraps.map((wrap) => ({
|
|
647
|
-
id: wrap.rootId,
|
|
648
|
-
reason: wrap.reason,
|
|
649
|
-
expressionId: wrap.expressionId,
|
|
650
|
-
line: wrap.line,
|
|
651
|
-
column: wrap.column
|
|
652
|
-
})),
|
|
653
|
-
controlFacts: [],
|
|
654
|
-
...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
|
|
655
|
-
...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
|
|
656
|
-
};
|
|
136
|
+
var sourceMapFor = (generatedPath, source) => {
|
|
137
|
+
const match = sourceMapComment.exec(source);
|
|
138
|
+
const reference = match?.[1] ?? match?.[2];
|
|
139
|
+
if (reference?.startsWith("data:")) {
|
|
140
|
+
const payload = decodeDataUrl(reference);
|
|
141
|
+
return payload ? { payload, mapUrl: pathToFileURL(generatedPath).href } : void 0;
|
|
142
|
+
}
|
|
143
|
+
const mapPath = reference ? resolve3(dirname(generatedPath), reference) : `${generatedPath}.map`;
|
|
657
144
|
return {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
node,
|
|
661
|
-
plan,
|
|
662
|
-
captureWraps: ctx.fallback ? [] : ctx.captureWraps,
|
|
663
|
-
guardWraps: ctx.fallback ? [] : ctx.guardWraps,
|
|
664
|
-
callEdges: ctx.callEdges,
|
|
665
|
-
hasThrow: ctx.hasThrow,
|
|
666
|
-
hasCoercionGuard: ctx.hasCoercionGuard
|
|
145
|
+
payload: readFileSync(mapPath, "utf8"),
|
|
146
|
+
mapUrl: pathToFileURL(mapPath).href
|
|
667
147
|
};
|
|
668
148
|
};
|
|
669
|
-
var
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
const firstPass = targets.map(
|
|
675
|
-
(target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
|
|
676
|
-
);
|
|
677
|
-
const graph = buildDependencyGraph(
|
|
678
|
-
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
679
|
-
firstPass.flatMap((fn) => fn.callEdges)
|
|
680
|
-
);
|
|
681
|
-
const order = topologicalFunctionOrder(
|
|
682
|
-
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
683
|
-
graph
|
|
684
|
-
);
|
|
685
|
-
const byId = new Map(targets.map((target) => [target.functionId, target]));
|
|
686
|
-
const moduleCallees = /* @__PURE__ */ new Map();
|
|
687
|
-
const analyzed = [];
|
|
688
|
-
for (const functionId of order) {
|
|
689
|
-
const target = byId.get(functionId);
|
|
690
|
-
if (!target) continue;
|
|
691
|
-
const result = analyzeOne(
|
|
692
|
-
target.node,
|
|
693
|
-
target.functionId,
|
|
694
|
-
target.name,
|
|
695
|
-
sourceFile,
|
|
696
|
-
moduleCallees
|
|
697
|
-
);
|
|
698
|
-
if (nameCounts.get(result.name) === 1) {
|
|
699
|
-
moduleCallees.set(result.name, result.functionId);
|
|
700
|
-
}
|
|
701
|
-
analyzed.push(result);
|
|
702
|
-
}
|
|
703
|
-
const remaining = targets.filter(
|
|
704
|
-
(target) => !analyzed.some((fn) => fn.functionId === target.functionId)
|
|
705
|
-
);
|
|
706
|
-
for (const target of remaining) {
|
|
707
|
-
analyzed.push(
|
|
708
|
-
analyzeOne(target.node, target.functionId, target.name, sourceFile, moduleCallees)
|
|
709
|
-
);
|
|
149
|
+
var sourceMapPayloadForCache = (generatedPath, source) => {
|
|
150
|
+
try {
|
|
151
|
+
return sourceMapFor(generatedPath, source)?.payload ?? "";
|
|
152
|
+
} catch {
|
|
153
|
+
return "";
|
|
710
154
|
}
|
|
711
|
-
return analyzed;
|
|
712
155
|
};
|
|
713
156
|
|
|
714
|
-
// src/
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
var captureWrapEdits = (wraps, capName) => {
|
|
719
|
-
const edits = [];
|
|
720
|
-
let order = 0;
|
|
721
|
-
for (const wrap of wraps) {
|
|
722
|
-
edits.push({
|
|
723
|
-
position: wrap.start,
|
|
724
|
-
text: `${wrap.shorthandPropertyName ? `${wrap.shorthandPropertyName}:` : ""}${capName}(${wrap.rootId},`,
|
|
725
|
-
order: order++
|
|
726
|
-
});
|
|
727
|
-
edits.push({
|
|
728
|
-
position: wrap.end,
|
|
729
|
-
text: ")",
|
|
730
|
-
order: order++
|
|
731
|
-
});
|
|
732
|
-
}
|
|
733
|
-
return edits;
|
|
734
|
-
};
|
|
157
|
+
// src/auto-instrumentation/transform-source.ts
|
|
158
|
+
import { basename, resolve as resolve4 } from "node:path";
|
|
159
|
+
import { originalPositionFor as originalPositionFor2, TraceMap as TraceMap2 } from "@jridgewell/trace-mapping";
|
|
160
|
+
import ts6 from "typescript";
|
|
735
161
|
|
|
736
162
|
// src/execution-recorder/automatic-runtime.ts
|
|
737
|
-
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.
|
|
163
|
+
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v3";
|
|
738
164
|
var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
|
|
739
165
|
|
|
740
166
|
// src/auto-instrumentation/collect-instrumentation-delta.ts
|
|
741
|
-
import
|
|
167
|
+
import ts from "typescript";
|
|
742
168
|
var paramBindingFromName = (name, sourceFile) => {
|
|
743
|
-
if (
|
|
744
|
-
if (
|
|
169
|
+
if (ts.isIdentifier(name)) return { kind: "identifier", name: name.text };
|
|
170
|
+
if (ts.isArrayBindingPattern(name)) {
|
|
745
171
|
const elements = name.elements.map(
|
|
746
172
|
(element) => {
|
|
747
|
-
if (
|
|
173
|
+
if (ts.isOmittedExpression(element)) return { kind: "omitted" };
|
|
748
174
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
749
175
|
if (element.dotDotDotToken) return { kind: "rest", binding };
|
|
750
176
|
return binding;
|
|
@@ -754,7 +180,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
754
180
|
}
|
|
755
181
|
const properties = name.elements.map((element) => {
|
|
756
182
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
757
|
-
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") :
|
|
183
|
+
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
|
|
758
184
|
if (element.dotDotDotToken) {
|
|
759
185
|
const rest = { kind: "rest", binding };
|
|
760
186
|
return { key, binding: rest };
|
|
@@ -766,7 +192,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
766
192
|
var collectFunctionParams = (node, sourceFile) => {
|
|
767
193
|
const params = [];
|
|
768
194
|
for (const parameter of node.parameters) {
|
|
769
|
-
if (
|
|
195
|
+
if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
770
196
|
const binding = paramBindingFromName(parameter.name, sourceFile);
|
|
771
197
|
params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
|
|
772
198
|
}
|
|
@@ -807,18 +233,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
|
|
|
807
233
|
};
|
|
808
234
|
var unwrapExpression = (expression) => {
|
|
809
235
|
let current = expression;
|
|
810
|
-
while (
|
|
236
|
+
while (ts.isParenthesizedExpression(current) || ts.isNonNullExpression(current)) {
|
|
811
237
|
current = current.expression;
|
|
812
238
|
}
|
|
813
|
-
if (
|
|
239
|
+
if (ts.isAwaitExpression(current)) return unwrapExpression(current.expression);
|
|
814
240
|
return current;
|
|
815
241
|
};
|
|
816
242
|
var calleeFromExpression = (expression, sourceFile) => {
|
|
817
243
|
const current = unwrapExpression(expression);
|
|
818
|
-
if (
|
|
244
|
+
if (ts.isIdentifier(current)) {
|
|
819
245
|
return { calleeName: current.text, calleeText: current.text };
|
|
820
246
|
}
|
|
821
|
-
if (
|
|
247
|
+
if (ts.isPropertyAccessExpression(current) && ts.isIdentifier(current.name)) {
|
|
822
248
|
return {
|
|
823
249
|
calleeName: current.name.text,
|
|
824
250
|
calleeText: current.getText(sourceFile).replace(/\s+/g, "")
|
|
@@ -828,9 +254,9 @@ var calleeFromExpression = (expression, sourceFile) => {
|
|
|
828
254
|
};
|
|
829
255
|
var resultBindingForCall = (call) => {
|
|
830
256
|
let node = call.parent;
|
|
831
|
-
if (
|
|
832
|
-
if (!
|
|
833
|
-
if (!
|
|
257
|
+
if (ts.isAwaitExpression(node)) node = node.parent;
|
|
258
|
+
if (!ts.isVariableDeclaration(node)) return null;
|
|
259
|
+
if (!ts.isIdentifier(node.name)) return null;
|
|
834
260
|
const initializer = node.initializer;
|
|
835
261
|
if (!initializer) return null;
|
|
836
262
|
const inner = unwrapExpression(initializer);
|
|
@@ -840,18 +266,18 @@ var resultBindingForCall = (call) => {
|
|
|
840
266
|
var argIdentifiersForCall = (call) => {
|
|
841
267
|
const identifiers = [];
|
|
842
268
|
call.arguments?.forEach((argument, index) => {
|
|
843
|
-
if (
|
|
269
|
+
if (ts.isIdentifier(argument)) {
|
|
844
270
|
identifiers.push({ index, kind: "identifier", name: argument.text });
|
|
845
271
|
return;
|
|
846
272
|
}
|
|
847
|
-
if (!
|
|
273
|
+
if (!ts.isObjectLiteralExpression(argument)) return;
|
|
848
274
|
const properties = [];
|
|
849
275
|
for (const property of argument.properties) {
|
|
850
|
-
if (
|
|
276
|
+
if (ts.isShorthandPropertyAssignment(property)) {
|
|
851
277
|
properties.push({ key: property.name.text, name: property.name.text });
|
|
852
278
|
continue;
|
|
853
279
|
}
|
|
854
|
-
if (
|
|
280
|
+
if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && ts.isIdentifier(property.initializer)) {
|
|
855
281
|
properties.push({ key: property.name.text, name: property.initializer.text });
|
|
856
282
|
}
|
|
857
283
|
}
|
|
@@ -891,8 +317,8 @@ var collectCallSite = (call, caller, context) => {
|
|
|
891
317
|
};
|
|
892
318
|
};
|
|
893
319
|
var collectParamDestructure = (declaration, caller, context) => {
|
|
894
|
-
if (!declaration.initializer || !
|
|
895
|
-
if (!
|
|
320
|
+
if (!declaration.initializer || !ts.isIdentifier(declaration.initializer)) return null;
|
|
321
|
+
if (!ts.isObjectBindingPattern(declaration.name) && !ts.isArrayBindingPattern(declaration.name)) {
|
|
896
322
|
return null;
|
|
897
323
|
}
|
|
898
324
|
const sourceParam = declaration.initializer.text;
|
|
@@ -914,58 +340,483 @@ var collectParamDestructure = (declaration, caller, context) => {
|
|
|
914
340
|
};
|
|
915
341
|
};
|
|
916
342
|
|
|
917
|
-
// src/auto-instrumentation/
|
|
343
|
+
// src/auto-instrumentation/parameter-initializer-may-run-code.ts
|
|
344
|
+
import ts2 from "typescript";
|
|
345
|
+
var unwrap = (expression) => {
|
|
346
|
+
let current = expression;
|
|
347
|
+
while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
|
|
348
|
+
current = current.expression;
|
|
349
|
+
}
|
|
350
|
+
return current;
|
|
351
|
+
};
|
|
352
|
+
var isSafeLiteralInitializer = (expression) => {
|
|
353
|
+
const current = unwrap(expression);
|
|
354
|
+
if (ts2.isNumericLiteral(current) || ts2.isStringLiteral(current) || ts2.isNoSubstitutionTemplateLiteral(current) || ts2.isBigIntLiteral(current)) {
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
if (current.kind === ts2.SyntaxKind.TrueKeyword || current.kind === ts2.SyntaxKind.FalseKeyword || current.kind === ts2.SyntaxKind.NullKeyword) {
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
if (ts2.isIdentifier(current) && current.text === "undefined") return true;
|
|
361
|
+
if (ts2.isPrefixUnaryExpression(current) && (current.operator === ts2.SyntaxKind.PlusToken || current.operator === ts2.SyntaxKind.MinusToken)) {
|
|
362
|
+
return ts2.isNumericLiteral(unwrap(current.operand));
|
|
363
|
+
}
|
|
364
|
+
return false;
|
|
365
|
+
};
|
|
366
|
+
var functionHasPreBodyParameterEvaluation = (node) => {
|
|
367
|
+
for (const parameter of node.parameters) {
|
|
368
|
+
if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
369
|
+
if (!parameter.initializer) continue;
|
|
370
|
+
if (!isSafeLiteralInitializer(parameter.initializer)) return true;
|
|
371
|
+
}
|
|
372
|
+
return false;
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// src/auto-instrumentation/suspension-whitelist.ts
|
|
376
|
+
import ts3 from "typescript";
|
|
377
|
+
var nodeFlags = ts3.NodeFlags;
|
|
378
|
+
var AWAIT_USING_FLAG = nodeFlags.AwaitUsing ?? 0;
|
|
379
|
+
var USING_FLAG = nodeFlags.Using ?? 0;
|
|
380
|
+
var KNOWN_SUSPENSION_KIND_NAMES = [
|
|
381
|
+
"AwaitExpression",
|
|
382
|
+
"AwaitKeyword",
|
|
383
|
+
"YieldExpression",
|
|
384
|
+
"YieldKeyword",
|
|
385
|
+
"UsingKeyword"
|
|
386
|
+
];
|
|
387
|
+
var isFunctionLike = (node) => ts3.isFunctionLike(node);
|
|
388
|
+
var isAsyncFunctionLike = (node) => (ts3.getCombinedModifierFlags(node) & ts3.ModifierFlags.Async) !== 0;
|
|
389
|
+
var isAsyncGeneratorLike = (node) => isFunctionLike(node) && isAsyncFunctionLike(node) && "asteriskToken" in node && Boolean(node.asteriskToken);
|
|
390
|
+
var isSyncGeneratorLike = (node) => isFunctionLike(node) && "asteriskToken" in node && Boolean(node.asteriskToken) && !isAsyncFunctionLike(node);
|
|
391
|
+
var visitOwnBody = (node, visitNode) => {
|
|
392
|
+
if (!node.body) return;
|
|
393
|
+
const visit = (candidate) => {
|
|
394
|
+
if (candidate !== node && isFunctionLike(candidate)) {
|
|
395
|
+
visitNode(candidate);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (visitNode(candidate)) return;
|
|
399
|
+
ts3.forEachChild(candidate, visit);
|
|
400
|
+
};
|
|
401
|
+
visit(node.body);
|
|
402
|
+
};
|
|
403
|
+
var nestedFunctionSpans = (node, sourceFile) => {
|
|
404
|
+
const spans = [];
|
|
405
|
+
visitOwnBody(node, (candidate) => {
|
|
406
|
+
if (candidate !== node && isFunctionLike(candidate)) {
|
|
407
|
+
spans.push([candidate.getStart(sourceFile), candidate.end]);
|
|
408
|
+
}
|
|
409
|
+
return void 0;
|
|
410
|
+
});
|
|
411
|
+
return spans.sort((left, right) => left[0] - right[0]);
|
|
412
|
+
};
|
|
413
|
+
var slicesOutside = (start, end, spans) => {
|
|
414
|
+
const slices = [];
|
|
415
|
+
let cursor = start;
|
|
416
|
+
for (const [from, to] of spans) {
|
|
417
|
+
if (to <= cursor) continue;
|
|
418
|
+
if (from >= end) break;
|
|
419
|
+
if (from > cursor) slices.push([cursor, Math.min(from, end)]);
|
|
420
|
+
cursor = Math.max(cursor, to);
|
|
421
|
+
}
|
|
422
|
+
if (cursor < end) slices.push([cursor, end]);
|
|
423
|
+
return slices;
|
|
424
|
+
};
|
|
425
|
+
var sliceHasAwaitKeyword = (source, start, end) => {
|
|
426
|
+
if (end <= start) return false;
|
|
427
|
+
const scanner = ts3.createScanner(
|
|
428
|
+
ts3.ScriptTarget.Latest,
|
|
429
|
+
true,
|
|
430
|
+
ts3.LanguageVariant.Standard,
|
|
431
|
+
source.slice(start, end)
|
|
432
|
+
);
|
|
433
|
+
while (true) {
|
|
434
|
+
const token = scanner.scan();
|
|
435
|
+
if (token === ts3.SyntaxKind.EndOfFileToken) return false;
|
|
436
|
+
if (token === ts3.SyntaxKind.AwaitKeyword) return true;
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
var ownBodyHasAwaitKeyword = (node, sourceFile) => {
|
|
440
|
+
if (!node.body) return false;
|
|
441
|
+
const spans = nestedFunctionSpans(node, sourceFile);
|
|
442
|
+
for (const [start, end] of slicesOutside(node.body.getStart(sourceFile), node.body.end, spans)) {
|
|
443
|
+
if (sliceHasAwaitKeyword(sourceFile.text, start, end)) return true;
|
|
444
|
+
}
|
|
445
|
+
return false;
|
|
446
|
+
};
|
|
447
|
+
var isAwaitUsingList = (node) => {
|
|
448
|
+
const text = node.getText().trimStart();
|
|
449
|
+
if (text.startsWith("await using")) return true;
|
|
450
|
+
if (AWAIT_USING_FLAG === 0 || USING_FLAG === 0) return false;
|
|
451
|
+
return (node.flags & AWAIT_USING_FLAG) === AWAIT_USING_FLAG && (node.flags & USING_FLAG) !== 0;
|
|
452
|
+
};
|
|
453
|
+
var unknownSuspensionKind = (node) => {
|
|
454
|
+
const name = ts3.SyntaxKind[node.kind];
|
|
455
|
+
if (typeof name !== "string" || !/Await|Yield|Using/.test(name)) return false;
|
|
456
|
+
return !KNOWN_SUSPENSION_KIND_NAMES.includes(name);
|
|
457
|
+
};
|
|
458
|
+
var ownSuspensionFacts = (node, sourceFile) => {
|
|
459
|
+
const facts = {
|
|
460
|
+
awaitExpressions: false,
|
|
461
|
+
forAwait: false,
|
|
462
|
+
awaitUsing: false,
|
|
463
|
+
unknown: false,
|
|
464
|
+
awaitKeyword: ownBodyHasAwaitKeyword(node, sourceFile)
|
|
465
|
+
};
|
|
466
|
+
visitOwnBody(node, (candidate) => {
|
|
467
|
+
if (ts3.isAwaitExpression(candidate)) facts.awaitExpressions = true;
|
|
468
|
+
if (ts3.isForOfStatement(candidate) && candidate.awaitModifier) facts.forAwait = true;
|
|
469
|
+
if (ts3.isVariableDeclarationList(candidate) && isAwaitUsingList(candidate)) {
|
|
470
|
+
facts.awaitUsing = true;
|
|
471
|
+
}
|
|
472
|
+
if (unknownSuspensionKind(candidate)) facts.unknown = true;
|
|
473
|
+
return void 0;
|
|
474
|
+
});
|
|
475
|
+
return facts;
|
|
476
|
+
};
|
|
477
|
+
var bodyHasDirectEval = (node) => {
|
|
478
|
+
let found = false;
|
|
479
|
+
visitOwnBody(node, (candidate) => {
|
|
480
|
+
if (ts3.isCallExpression(candidate) && ts3.isIdentifier(candidate.expression) && candidate.expression.text === "eval") {
|
|
481
|
+
found = true;
|
|
482
|
+
}
|
|
483
|
+
return found;
|
|
484
|
+
});
|
|
485
|
+
return found;
|
|
486
|
+
};
|
|
487
|
+
var decideAsyncInstrumentation = (node, sourceFile) => {
|
|
488
|
+
if (isAsyncGeneratorLike(node)) return { action: "skip", reason: "unsupported_async_generator" };
|
|
489
|
+
if (!isAsyncFunctionLike(node)) return { action: "inline" };
|
|
490
|
+
if (bodyHasDirectEval(node)) return { action: "skip", reason: "direct_eval" };
|
|
491
|
+
const facts = ownSuspensionFacts(node, sourceFile);
|
|
492
|
+
if (facts.unknown) return { action: "skip", reason: "unknown_suspension" };
|
|
493
|
+
if (facts.forAwait) return { action: "skip", reason: "unsupported_for_await" };
|
|
494
|
+
if (facts.awaitUsing) return { action: "skip", reason: "await_using" };
|
|
495
|
+
if (facts.awaitKeyword && !facts.awaitExpressions) {
|
|
496
|
+
return { action: "skip", reason: "unknown_suspension" };
|
|
497
|
+
}
|
|
498
|
+
if (facts.awaitExpressions) return { action: "suspend_resume" };
|
|
499
|
+
return { action: "inline" };
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
// src/auto-instrumentation/verify-instrumentation-isomorphism.ts
|
|
503
|
+
import ts4 from "typescript";
|
|
504
|
+
var RASPUTIN_IDENT = /^__rasputin/;
|
|
918
505
|
var scriptKindFor = (filePath) => {
|
|
919
|
-
if (/\.tsx$/i.test(filePath)) return
|
|
920
|
-
if (/\.jsx$/i.test(filePath)) return
|
|
921
|
-
if (/\.[cm]?ts$/i.test(filePath)) return
|
|
922
|
-
return
|
|
506
|
+
if (/\.tsx$/i.test(filePath)) return ts4.ScriptKind.TSX;
|
|
507
|
+
if (/\.jsx$/i.test(filePath)) return ts4.ScriptKind.JSX;
|
|
508
|
+
if (/\.[cm]?ts$/i.test(filePath)) return ts4.ScriptKind.TS;
|
|
509
|
+
return ts4.ScriptKind.JS;
|
|
510
|
+
};
|
|
511
|
+
var parse = (filePath, source) => ts4.createSourceFile(filePath, source, ts4.ScriptTarget.Latest, true, scriptKindFor(filePath));
|
|
512
|
+
var isRasputinName = (name) => RASPUTIN_IDENT.test(name);
|
|
513
|
+
var identText = (node) => {
|
|
514
|
+
if (ts4.isIdentifier(node)) return node.text;
|
|
515
|
+
if (ts4.isPrivateIdentifier(node)) return node.text;
|
|
516
|
+
return void 0;
|
|
517
|
+
};
|
|
518
|
+
var calleeName = (expression) => {
|
|
519
|
+
if (ts4.isIdentifier(expression)) return expression.text;
|
|
520
|
+
if (ts4.isPropertyAccessExpression(expression)) {
|
|
521
|
+
const object = identText(expression.expression);
|
|
522
|
+
const property = identText(expression.name);
|
|
523
|
+
if (object && isRasputinName(object) && property) return `${object}.${property}`;
|
|
524
|
+
}
|
|
525
|
+
return void 0;
|
|
526
|
+
};
|
|
527
|
+
var isRasputinCall = (node, methods) => {
|
|
528
|
+
if (!ts4.isCallExpression(node)) return false;
|
|
529
|
+
const name = calleeName(node.expression);
|
|
530
|
+
if (!name) return false;
|
|
531
|
+
if (isRasputinName(name)) return !methods || methods.has(name.split(".")[1] ?? "");
|
|
532
|
+
return false;
|
|
533
|
+
};
|
|
534
|
+
var unwrapCall = (node) => {
|
|
535
|
+
let current = node;
|
|
536
|
+
while (isRasputinCall(current) && current.arguments.length === 1 && current.arguments[0]) {
|
|
537
|
+
current = current.arguments[0];
|
|
538
|
+
if (ts4.isParenthesizedExpression(current)) current = current.expression;
|
|
539
|
+
}
|
|
540
|
+
if (ts4.isParenthesizedExpression(current)) return current.expression;
|
|
541
|
+
return current;
|
|
542
|
+
};
|
|
543
|
+
var isRasputinStatement = (node) => {
|
|
544
|
+
if (ts4.isEmptyStatement(node)) return true;
|
|
545
|
+
if (ts4.isVariableStatement(node)) {
|
|
546
|
+
return node.declarationList.declarations.every((declaration) => {
|
|
547
|
+
const name = identText(declaration.name);
|
|
548
|
+
return Boolean(name && isRasputinName(name));
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
if (ts4.isExpressionStatement(node)) {
|
|
552
|
+
if (!isRasputinCall(node.expression)) return false;
|
|
553
|
+
const unwrapped = unwrapCall(node.expression);
|
|
554
|
+
return unwrapped === node.expression;
|
|
555
|
+
}
|
|
556
|
+
return false;
|
|
557
|
+
};
|
|
558
|
+
var isRasputinTryWrapper = (node) => {
|
|
559
|
+
const catchClause = node.catchClause;
|
|
560
|
+
if (!catchClause || !node.finallyBlock) return false;
|
|
561
|
+
const catchCalls = catchClause.block.statements.filter(
|
|
562
|
+
(statement) => !isRasputinStatement(statement)
|
|
563
|
+
);
|
|
564
|
+
const leftover = catchCalls[0];
|
|
565
|
+
const leftoverThrow = catchCalls.length === 1 && leftover !== void 0 && ts4.isThrowStatement(leftover) && ts4.isIdentifier(leftover.expression) && isRasputinName(leftover.expression.text);
|
|
566
|
+
if (!leftoverThrow && catchCalls.length > 0) return false;
|
|
567
|
+
return node.finallyBlock.statements.every((statement) => isRasputinStatement(statement));
|
|
568
|
+
};
|
|
569
|
+
var flattenStatements = (statements) => {
|
|
570
|
+
const flattened = [];
|
|
571
|
+
for (const statement of statements) {
|
|
572
|
+
if (isRasputinStatement(statement)) continue;
|
|
573
|
+
if (ts4.isTryStatement(statement) && isRasputinTryWrapper(statement)) {
|
|
574
|
+
flattened.push(...flattenStatements(statement.tryBlock.statements));
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
flattened.push(statement);
|
|
578
|
+
}
|
|
579
|
+
return flattened;
|
|
580
|
+
};
|
|
581
|
+
var extra = (node) => {
|
|
582
|
+
if (ts4.isIdentifier(node) || ts4.isPrivateIdentifier(node)) {
|
|
583
|
+
return isRasputinName(node.text) ? "" : node.text;
|
|
584
|
+
}
|
|
585
|
+
if (ts4.isStringLiteral(node) || ts4.isNumericLiteral(node) || ts4.isNoSubstitutionTemplateLiteral(node)) {
|
|
586
|
+
return node.text;
|
|
587
|
+
}
|
|
588
|
+
if (ts4.isToken(node) && !ts4.isIdentifier(node)) return ts4.SyntaxKind[node.kind] ?? "";
|
|
589
|
+
return "";
|
|
590
|
+
};
|
|
591
|
+
var signatureOf = (node) => {
|
|
592
|
+
const unwrapped = unwrapCall(node);
|
|
593
|
+
if (unwrapped !== node) return signatureOf(unwrapped);
|
|
594
|
+
if (ts4.isEmptyStatement(unwrapped) || isRasputinStatement(unwrapped)) return "";
|
|
595
|
+
if (ts4.isTryStatement(unwrapped) && isRasputinTryWrapper(unwrapped)) {
|
|
596
|
+
return flattenStatements(unwrapped.tryBlock.statements).map(signatureOf).filter(Boolean).join("");
|
|
597
|
+
}
|
|
598
|
+
if (ts4.isBlock(unwrapped)) {
|
|
599
|
+
const statements = flattenStatements(unwrapped.statements);
|
|
600
|
+
return `Block[${statements.map(signatureOf).filter(Boolean).join(",")}]`;
|
|
601
|
+
}
|
|
602
|
+
if (ts4.isCatchClause(unwrapped)) {
|
|
603
|
+
const statements = flattenStatements(unwrapped.block.statements);
|
|
604
|
+
const variable = unwrapped.variableDeclaration ? signatureOf(unwrapped.variableDeclaration) : "";
|
|
605
|
+
return `Catch${variable}[${statements.map(signatureOf).filter(Boolean).join(",")}]`;
|
|
606
|
+
}
|
|
607
|
+
if (ts4.isArrowFunction(unwrapped) && ts4.isBlock(unwrapped.body)) {
|
|
608
|
+
const statements = flattenStatements(unwrapped.body.statements);
|
|
609
|
+
const only = statements[0];
|
|
610
|
+
if (statements.length === 1 && only && ts4.isReturnStatement(only) && only.expression) {
|
|
611
|
+
const expression = only.expression;
|
|
612
|
+
const parts2 = [];
|
|
613
|
+
ts4.forEachChild(unwrapped, (child) => {
|
|
614
|
+
if (child === unwrapped.body) {
|
|
615
|
+
const part2 = signatureOf(expression);
|
|
616
|
+
if (part2) parts2.push(part2);
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
const part = signatureOf(child);
|
|
620
|
+
if (part) parts2.push(part);
|
|
621
|
+
});
|
|
622
|
+
return `ArrowFunction[${parts2.join(",")}]`;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const parts = [];
|
|
626
|
+
ts4.forEachChild(unwrapped, (child) => {
|
|
627
|
+
const part = signatureOf(child);
|
|
628
|
+
if (part) parts.push(part);
|
|
629
|
+
});
|
|
630
|
+
const kind = ts4.SyntaxKind[unwrapped.kind] ?? "Node";
|
|
631
|
+
const label = extra(unwrapped);
|
|
632
|
+
return `${kind}${label}[${parts.join(",")}]`;
|
|
633
|
+
};
|
|
634
|
+
var instrumentationTreesAreIsomorphic = (filePath, original, transformed) => {
|
|
635
|
+
return signatureOf(parse(filePath, original)) === signatureOf(parse(filePath, transformed));
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
// src/auto-instrumentation/verify-transformed-source.ts
|
|
639
|
+
import ts5 from "typescript";
|
|
640
|
+
var transpileForVerification = (filePath, code) => ts5.transpileModule(code, {
|
|
641
|
+
compilerOptions: {
|
|
642
|
+
// ES2020 downlevel keeps native @decorators out of `new Function`.
|
|
643
|
+
target: ts5.ScriptTarget.ES2020,
|
|
644
|
+
module: ts5.ModuleKind.ESNext,
|
|
645
|
+
allowJs: true,
|
|
646
|
+
jsx: ts5.JsxEmit.React,
|
|
647
|
+
experimentalDecorators: true
|
|
648
|
+
},
|
|
649
|
+
reportDiagnostics: true,
|
|
650
|
+
fileName: filePath
|
|
651
|
+
});
|
|
652
|
+
var hasScanOrParseError = (diagnostics) => Boolean(
|
|
653
|
+
diagnostics?.some(
|
|
654
|
+
(diagnostic) => diagnostic.category === ts5.DiagnosticCategory.Error && diagnostic.code < 2e3
|
|
655
|
+
)
|
|
656
|
+
);
|
|
657
|
+
var scriptBodyForEngine = (emitted) => (
|
|
658
|
+
// File loaders strip a leading shebang; `new Function` does not.
|
|
659
|
+
emitted.replace(/^#![^\n]*\n/, "").replaceAll("import.meta", "({})")
|
|
660
|
+
);
|
|
661
|
+
var looksTyped = (filePath) => /\.[cm]?tsx?$/i.test(filePath);
|
|
662
|
+
var scriptKindFor2 = (filePath) => {
|
|
663
|
+
if (/\.tsx$/i.test(filePath)) return ts5.ScriptKind.TSX;
|
|
664
|
+
if (/\.jsx$/i.test(filePath)) return ts5.ScriptKind.JSX;
|
|
665
|
+
if (/\.[cm]?ts$/i.test(filePath)) return ts5.ScriptKind.TS;
|
|
666
|
+
return ts5.ScriptKind.JS;
|
|
667
|
+
};
|
|
668
|
+
var isIterationStatement = (node) => ts5.isForStatement(node) || ts5.isForOfStatement(node) || ts5.isForInStatement(node) || ts5.isWhileStatement(node) || ts5.isDoStatement(node);
|
|
669
|
+
var unwrapLabeled = (node) => {
|
|
670
|
+
let current = node;
|
|
671
|
+
while (ts5.isLabeledStatement(current)) current = current.statement;
|
|
672
|
+
return current;
|
|
673
|
+
};
|
|
674
|
+
var hasInvalidLabelJump = (sourceFile) => {
|
|
675
|
+
const visit = (node, labels) => {
|
|
676
|
+
if (ts5.isFunctionLike(node)) {
|
|
677
|
+
let nestedInvalid = false;
|
|
678
|
+
ts5.forEachChild(node, (child) => {
|
|
679
|
+
if (visit(child, /* @__PURE__ */ new Map())) nestedInvalid = true;
|
|
680
|
+
});
|
|
681
|
+
return nestedInvalid;
|
|
682
|
+
}
|
|
683
|
+
if (ts5.isLabeledStatement(node)) {
|
|
684
|
+
const next = new Map(labels);
|
|
685
|
+
next.set(node.label.text, isIterationStatement(unwrapLabeled(node.statement)));
|
|
686
|
+
return visit(node.statement, next);
|
|
687
|
+
}
|
|
688
|
+
if (ts5.isContinueStatement(node) && node.label) {
|
|
689
|
+
if (labels.get(node.label.text) !== true) return true;
|
|
690
|
+
}
|
|
691
|
+
if (ts5.isBreakStatement(node) && node.label && !labels.has(node.label.text)) {
|
|
692
|
+
return true;
|
|
693
|
+
}
|
|
694
|
+
let invalid = false;
|
|
695
|
+
ts5.forEachChild(node, (child) => {
|
|
696
|
+
if (visit(child, labels)) invalid = true;
|
|
697
|
+
});
|
|
698
|
+
return invalid;
|
|
699
|
+
};
|
|
700
|
+
return visit(sourceFile, /* @__PURE__ */ new Map());
|
|
701
|
+
};
|
|
702
|
+
var importSpecifier = `('[^']+'|"[^"]+")`;
|
|
703
|
+
var importAttributes = String.raw`(?:\s+with\s*\{[^}]*\})?`;
|
|
704
|
+
var stripModuleSyntax = (body) => body.replace(/\bexport\s+default\b/g, "var __rasputinVerifyDefault =").replace(new RegExp(String.raw`\bexport\s*\*[\s\S]*?from\s+${importSpecifier}`, "g"), "").replace(
|
|
705
|
+
new RegExp(
|
|
706
|
+
String.raw`\bexport\s*\{[^}]*\}\s*from\s+${importSpecifier}${importAttributes}`,
|
|
707
|
+
"g"
|
|
708
|
+
),
|
|
709
|
+
""
|
|
710
|
+
).replace(/\bexport\s*\{[^}]*\}/g, "").replace(/\bexport\s+/g, "").replace(
|
|
711
|
+
new RegExp(
|
|
712
|
+
String.raw`\bimport(?:\s+type)?\s+[\s\S]*?from\s+${importSpecifier}${importAttributes}`,
|
|
713
|
+
"g"
|
|
714
|
+
),
|
|
715
|
+
""
|
|
716
|
+
).replace(/\bimport\s+type\s+[\s\S]*?(?:;|\n)/g, "").replace(new RegExp(String.raw`\bimport\s+${importSpecifier}${importAttributes}`, "g"), "");
|
|
717
|
+
var engineAccepts = (body) => {
|
|
718
|
+
for (const candidate of [body, stripModuleSyntax(body)]) {
|
|
719
|
+
try {
|
|
720
|
+
new Function(candidate);
|
|
721
|
+
return true;
|
|
722
|
+
} catch {
|
|
723
|
+
try {
|
|
724
|
+
new Function(`return (async()=>{${candidate}
|
|
725
|
+
})`);
|
|
726
|
+
return true;
|
|
727
|
+
} catch {
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return false;
|
|
732
|
+
};
|
|
733
|
+
var GLUED_INSTRUMENTATION = /\b(?:return|await|throw|typeof|void|yield|new|delete|in|instanceof|of|from|as|case|else)__rasputin/;
|
|
734
|
+
var UNTERMINATED_BARE_LEAVE = /\breturn\s+__rasputin\w*\.leave\(\)(?!\s*;|\()/;
|
|
735
|
+
var transformedSourceIsInvalid = (filePath, code) => {
|
|
736
|
+
if (GLUED_INSTRUMENTATION.test(code)) return true;
|
|
737
|
+
if (UNTERMINATED_BARE_LEAVE.test(code)) return true;
|
|
738
|
+
const sourceFile = ts5.createSourceFile(
|
|
739
|
+
filePath,
|
|
740
|
+
code,
|
|
741
|
+
ts5.ScriptTarget.Latest,
|
|
742
|
+
true,
|
|
743
|
+
scriptKindFor2(filePath)
|
|
744
|
+
);
|
|
745
|
+
if (hasInvalidLabelJump(sourceFile)) return true;
|
|
746
|
+
const { diagnostics, outputText } = transpileForVerification(filePath, code);
|
|
747
|
+
if (hasScanOrParseError(diagnostics)) return true;
|
|
748
|
+
if (looksTyped(filePath)) return !engineAccepts(scriptBodyForEngine(outputText));
|
|
749
|
+
if (engineAccepts(scriptBodyForEngine(code))) return false;
|
|
750
|
+
return !engineAccepts(scriptBodyForEngine(outputText));
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
// src/auto-instrumentation/transform-source.ts
|
|
754
|
+
var isIdentContinue = (code) => code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 36 || code === 95;
|
|
755
|
+
var isIdentStart = (code) => code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 36 || code === 95;
|
|
756
|
+
var separateFromPreviousToken = (source, position, text) => {
|
|
757
|
+
if (position <= 0 || text.length === 0) return text;
|
|
758
|
+
if (isIdentContinue(source.charCodeAt(position - 1)) && isIdentStart(text.charCodeAt(0))) {
|
|
759
|
+
return ` ${text}`;
|
|
760
|
+
}
|
|
761
|
+
return text;
|
|
762
|
+
};
|
|
763
|
+
var RASPUTIN_INSTRUMENTATION_MARKER = "rasputin.instrumented.v3";
|
|
764
|
+
var RASPUTIN_INSTRUMENTATION_PREAMBLE = `/*${RASPUTIN_INSTRUMENTATION_MARKER}*/`;
|
|
765
|
+
var sourceIsRasputinInstrumented = (source) => source.includes(RASPUTIN_INSTRUMENTATION_PREAMBLE);
|
|
766
|
+
var alreadyInstrumentedResult = (source) => ({
|
|
767
|
+
code: source,
|
|
768
|
+
delta: { functions: [], callSites: [], destructures: [], alreadyInstrumented: true }
|
|
769
|
+
});
|
|
770
|
+
var scriptKindFor3 = (filePath) => {
|
|
771
|
+
if (/\.tsx$/i.test(filePath)) return ts6.ScriptKind.TSX;
|
|
772
|
+
if (/\.jsx$/i.test(filePath)) return ts6.ScriptKind.JSX;
|
|
773
|
+
if (/\.[cm]?ts$/i.test(filePath)) return ts6.ScriptKind.TS;
|
|
774
|
+
return ts6.ScriptKind.JS;
|
|
923
775
|
};
|
|
924
776
|
var functionName = (node, sourceFile) => {
|
|
777
|
+
if (ts6.isConstructorDeclaration(node)) return "constructor";
|
|
925
778
|
if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
|
|
926
779
|
const parent = node.parent;
|
|
927
|
-
if (
|
|
928
|
-
if (
|
|
929
|
-
if (
|
|
780
|
+
if (ts6.isVariableDeclaration(parent) && ts6.isIdentifier(parent.name)) return parent.name.text;
|
|
781
|
+
if (ts6.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
|
|
782
|
+
if (ts6.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
|
|
930
783
|
const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
931
784
|
return `anonymous@${location.line + 1}:${location.character + 1}`;
|
|
932
785
|
};
|
|
933
786
|
var functionLocationPosition = (node, sourceFile) => {
|
|
934
787
|
if (node.name) return node.name.getStart(sourceFile);
|
|
935
788
|
const parent = node.parent;
|
|
936
|
-
if ((
|
|
789
|
+
if ((ts6.isVariableDeclaration(parent) || ts6.isPropertyAssignment(parent)) && parent.name) {
|
|
937
790
|
return parent.name.getStart(sourceFile);
|
|
938
791
|
}
|
|
939
792
|
return node.getStart(sourceFile);
|
|
940
793
|
};
|
|
941
|
-
var
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
const value = bindingValue(element.name, sourceFile);
|
|
947
|
-
return element.dotDotDotToken ? `...${value}` : value;
|
|
948
|
-
});
|
|
949
|
-
return `[${values.join(",")}]`;
|
|
794
|
+
var functionBodyPrologueEnd = (body, sourceFile) => {
|
|
795
|
+
let end = body.getStart(sourceFile) + 1;
|
|
796
|
+
for (const statement of body.statements) {
|
|
797
|
+
if (!isDirectivePrologue(statement)) break;
|
|
798
|
+
end = statement.end;
|
|
950
799
|
}
|
|
951
|
-
|
|
952
|
-
const value = bindingValue(element.name, sourceFile);
|
|
953
|
-
if (element.dotDotDotToken) return `...${value}`;
|
|
954
|
-
if (element.propertyName) return `${element.propertyName.getText(sourceFile)}:${value}`;
|
|
955
|
-
return value;
|
|
956
|
-
});
|
|
957
|
-
return `{${properties.join(",")}}`;
|
|
800
|
+
return end;
|
|
958
801
|
};
|
|
959
|
-
var
|
|
802
|
+
var arrowArgsCapture = (node) => {
|
|
803
|
+
if (!node.parameters.every((parameter) => ts6.isIdentifier(parameter.name))) {
|
|
804
|
+
const count = node.parameters.filter(
|
|
805
|
+
(parameter) => !(ts6.isIdentifier(parameter.name) && parameter.name.text === "this")
|
|
806
|
+
).length;
|
|
807
|
+
return {
|
|
808
|
+
args: `[${Array.from({ length: count }, () => "void 0").join(",")}]`,
|
|
809
|
+
omitValues: true
|
|
810
|
+
};
|
|
811
|
+
}
|
|
960
812
|
const values = node.parameters.flatMap((parameter) => {
|
|
961
|
-
if (
|
|
962
|
-
|
|
963
|
-
return [parameter.dotDotDotToken ? `...${value}` : value];
|
|
813
|
+
if (!ts6.isIdentifier(parameter.name) || parameter.name.text === "this") return [];
|
|
814
|
+
return [parameter.dotDotDotToken ? `...${parameter.name.text}` : parameter.name.text];
|
|
964
815
|
});
|
|
965
|
-
return `[${values.join(",")}]
|
|
816
|
+
return { args: `[${values.join(",")}]`, omitValues: false };
|
|
966
817
|
};
|
|
967
818
|
var isSupportedFunction = (node) => {
|
|
968
|
-
if (!
|
|
819
|
+
if (!ts6.isFunctionDeclaration(node) && !ts6.isFunctionExpression(node) && !ts6.isArrowFunction(node) && !ts6.isMethodDeclaration(node) && !ts6.isGetAccessorDeclaration(node) && !ts6.isSetAccessorDeclaration(node)) {
|
|
969
820
|
return false;
|
|
970
821
|
}
|
|
971
822
|
if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
|
|
@@ -976,7 +827,7 @@ var uniqueName = (source, base) => {
|
|
|
976
827
|
while (source.includes(name)) name += "_";
|
|
977
828
|
return name;
|
|
978
829
|
};
|
|
979
|
-
var isDirectivePrologue = (statement) =>
|
|
830
|
+
var isDirectivePrologue = (statement) => ts6.isExpressionStatement(statement) && ts6.isStringLiteral(statement.expression);
|
|
980
831
|
var insertPositionAfterPrologue = (source, sourceFile) => {
|
|
981
832
|
const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
|
|
982
833
|
let end = shebangEnd;
|
|
@@ -991,7 +842,7 @@ var preambleInsert = (source, position, preamble) => {
|
|
|
991
842
|
const previous = source[position - 1];
|
|
992
843
|
return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
|
|
993
844
|
};
|
|
994
|
-
var modulePreamble = (runName,
|
|
845
|
+
var modulePreamble = (runName, plansName, plansJson) => `${RASPUTIN_INSTRUMENTATION_PREAMBLE}const ${plansName}=${plansJson};const ${runName}=((g,k)=>{const r=g[k]??(g[k]={runtimes:[],modules:[],register(p){const d={run:(i,a,c)=>c(),enter(){return{leave(v){return v},fail(){},end(){},suspend(v){return v},resume(v){return v},run(c){return c()}}}};const m={plans:p,cell:d};this.modules.push(m);const n=this.runtimes.at(-1);if(n)n.bind(m);const f=(i,a,c,l)=>d.run(i,a,c,l);f.enter=(i,a,l)=>d.enter(i,a,l);return f}});return r.register(${plansName})})(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));`;
|
|
995
846
|
var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
996
847
|
const start = mappedLocation(
|
|
997
848
|
sourceFile,
|
|
@@ -1019,40 +870,276 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
|
1019
870
|
endLine: end.line,
|
|
1020
871
|
endColumn: end.column,
|
|
1021
872
|
params,
|
|
1022
|
-
paramNames
|
|
873
|
+
paramNames,
|
|
874
|
+
instrumented: true
|
|
1023
875
|
};
|
|
1024
876
|
};
|
|
1025
|
-
var
|
|
1026
|
-
|
|
1027
|
-
|
|
877
|
+
var wrap = (edits, start, end, open, close, depth) => {
|
|
878
|
+
const width = Math.max(0, end - start);
|
|
879
|
+
edits.push({ position: start, text: open, deleteCount: 0, depth, width, kind: "open" });
|
|
880
|
+
edits.push({ position: end, text: close, deleteCount: 0, depth, width, kind: "close" });
|
|
881
|
+
};
|
|
882
|
+
var compareEdits = (left, right) => {
|
|
883
|
+
if (left.position !== right.position) return right.position - left.position;
|
|
884
|
+
if (left.kind !== right.kind) {
|
|
885
|
+
const rank = { replace: 0, close: 1, open: 2 };
|
|
886
|
+
return rank[left.kind] - rank[right.kind];
|
|
1028
887
|
}
|
|
1029
|
-
|
|
1030
|
-
(
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
888
|
+
if (left.kind === "close") {
|
|
889
|
+
if (left.width !== right.width) return right.width - left.width;
|
|
890
|
+
return left.depth - right.depth;
|
|
891
|
+
}
|
|
892
|
+
if (left.kind === "open") {
|
|
893
|
+
if (left.width !== right.width) return left.width - right.width;
|
|
894
|
+
return right.depth - left.depth;
|
|
895
|
+
}
|
|
896
|
+
return 0;
|
|
897
|
+
};
|
|
898
|
+
var VLQ_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
899
|
+
var encodeVlq = (value) => {
|
|
900
|
+
let vlq = value < 0 ? -value << 1 | 1 : value << 1;
|
|
901
|
+
let encoded = "";
|
|
902
|
+
do {
|
|
903
|
+
let digit = vlq & 31;
|
|
904
|
+
vlq >>>= 5;
|
|
905
|
+
if (vlq > 0) digit |= 32;
|
|
906
|
+
encoded += VLQ_ALPHABET[digit];
|
|
907
|
+
} while (vlq > 0);
|
|
908
|
+
return encoded;
|
|
909
|
+
};
|
|
910
|
+
var SOURCEMAP_COMMENT = /(?:\/\/[#@][ \t]*sourceMappingURL=([^\s'"]+)|\/\*[#@][ \t]*sourceMappingURL=([^\s*'"]+)[ \t]*\*\/)\s*$/;
|
|
911
|
+
var decodeInlineSourceMap = (url) => {
|
|
912
|
+
const base64 = /^data:application\/json(?:;charset=[^;,]+)?;base64,(.+)$/i.exec(url);
|
|
913
|
+
if (base64?.[1]) {
|
|
914
|
+
try {
|
|
915
|
+
return Buffer.from(base64[1], "base64").toString("utf8");
|
|
916
|
+
} catch {
|
|
917
|
+
return void 0;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
const raw = /^data:application\/json(?:;charset=[^;,]+)?,(.+)$/i.exec(url);
|
|
921
|
+
if (!raw?.[1]) return void 0;
|
|
922
|
+
try {
|
|
923
|
+
return decodeURIComponent(raw[1]);
|
|
924
|
+
} catch {
|
|
925
|
+
return void 0;
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
var upstreamTraceMap = (original) => {
|
|
929
|
+
const match = SOURCEMAP_COMMENT.exec(original);
|
|
930
|
+
const url = match?.[1] ?? match?.[2];
|
|
931
|
+
if (!url) return void 0;
|
|
932
|
+
const payload = url.startsWith("data:") ? decodeInlineSourceMap(url) : void 0;
|
|
933
|
+
if (!payload) return void 0;
|
|
934
|
+
try {
|
|
935
|
+
return new TraceMap2(payload);
|
|
936
|
+
} catch {
|
|
937
|
+
return void 0;
|
|
938
|
+
}
|
|
939
|
+
};
|
|
940
|
+
var lineStartsOf = (text) => {
|
|
941
|
+
const starts = [0];
|
|
942
|
+
for (let index = 0; index < text.length; index++) {
|
|
943
|
+
if (text[index] === "\n") starts.push(index + 1);
|
|
944
|
+
}
|
|
945
|
+
return starts;
|
|
946
|
+
};
|
|
947
|
+
var positionAt = (starts, offset) => {
|
|
948
|
+
let low = 0;
|
|
949
|
+
let high = starts.length - 1;
|
|
950
|
+
while (low < high) {
|
|
951
|
+
const mid = low + high + 1 >> 1;
|
|
952
|
+
if ((starts[mid] ?? 0) <= offset) low = mid;
|
|
953
|
+
else high = mid - 1;
|
|
954
|
+
}
|
|
955
|
+
return { line: low, column: offset - (starts[low] ?? 0) };
|
|
956
|
+
};
|
|
957
|
+
var sortedEdits = (edits) => [...edits].sort(compareEdits);
|
|
958
|
+
var originalOffsetToGenerated = (originalOffset, edits) => {
|
|
959
|
+
let generated = originalOffset;
|
|
960
|
+
for (const edit of edits) {
|
|
961
|
+
const delta = edit.text.length - edit.deleteCount;
|
|
962
|
+
if (originalOffset >= edit.position + edit.deleteCount) generated += delta;
|
|
963
|
+
else if (originalOffset >= edit.position) generated = edit.position + edit.text.length;
|
|
964
|
+
}
|
|
965
|
+
return generated;
|
|
966
|
+
};
|
|
967
|
+
var mappingOffsets = (original, edits) => {
|
|
968
|
+
const offsets = /* @__PURE__ */ new Set([0, original.length]);
|
|
969
|
+
for (const start of lineStartsOf(original)) offsets.add(start);
|
|
970
|
+
for (const edit of edits) {
|
|
971
|
+
offsets.add(edit.position);
|
|
972
|
+
offsets.add(edit.position + edit.deleteCount);
|
|
973
|
+
}
|
|
974
|
+
return [...offsets].filter((offset) => offset >= 0 && offset <= original.length).sort((left, right) => left - right);
|
|
975
|
+
};
|
|
976
|
+
var encodeColumnMappings = (original, generated, file, edits) => {
|
|
977
|
+
const upstream = upstreamTraceMap(original);
|
|
978
|
+
const sources = (upstream?.sources.filter((source) => Boolean(source)) ?? [file]).filter((source, index, all) => all.indexOf(source) === index);
|
|
979
|
+
if (sources.length === 0) sources.push(file);
|
|
980
|
+
const applied = sortedEdits(edits);
|
|
981
|
+
const originalStarts = lineStartsOf(original);
|
|
982
|
+
const generatedStarts = lineStartsOf(generated);
|
|
983
|
+
const pointsByLine = Array.from({ length: generatedStarts.length }, () => []);
|
|
984
|
+
const seen = /* @__PURE__ */ new Set();
|
|
985
|
+
for (const originalOffset of mappingOffsets(original, edits)) {
|
|
986
|
+
const generatedOffset = Math.min(
|
|
987
|
+
originalOffsetToGenerated(originalOffset, applied),
|
|
988
|
+
generated.length
|
|
989
|
+
);
|
|
990
|
+
const originalPosition = positionAt(originalStarts, originalOffset);
|
|
991
|
+
const generatedPosition = positionAt(generatedStarts, generatedOffset);
|
|
992
|
+
let sourceIndex = 0;
|
|
993
|
+
let mappedLine = originalPosition.line;
|
|
994
|
+
let mappedColumn = originalPosition.column;
|
|
995
|
+
if (upstream) {
|
|
996
|
+
const mapped = originalPositionFor2(upstream, {
|
|
997
|
+
line: originalPosition.line + 1,
|
|
998
|
+
column: originalPosition.column
|
|
999
|
+
});
|
|
1000
|
+
if (mapped.source && mapped.line != null && mapped.column != null) {
|
|
1001
|
+
const index = sources.indexOf(mapped.source);
|
|
1002
|
+
sourceIndex = index >= 0 ? index : 0;
|
|
1003
|
+
mappedLine = mapped.line - 1;
|
|
1004
|
+
mappedColumn = mapped.column;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
const key = `${generatedPosition.line}:${generatedPosition.column}`;
|
|
1008
|
+
if (seen.has(key)) continue;
|
|
1009
|
+
seen.add(key);
|
|
1010
|
+
pointsByLine[generatedPosition.line]?.push({
|
|
1011
|
+
generatedColumn: generatedPosition.column,
|
|
1012
|
+
sourceIndex,
|
|
1013
|
+
originalLine: mappedLine,
|
|
1014
|
+
originalColumn: mappedColumn
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
const lines = pointsByLine.map((segments) => {
|
|
1018
|
+
segments.sort((left, right) => left.generatedColumn - right.generatedColumn);
|
|
1019
|
+
let lastGeneratedColumn = 0;
|
|
1020
|
+
let lastSourceIndex = 0;
|
|
1021
|
+
let lastOriginalLine = 0;
|
|
1022
|
+
let lastOriginalColumn = 0;
|
|
1023
|
+
return segments.map((point) => {
|
|
1024
|
+
const encoded = `${encodeVlq(point.generatedColumn - lastGeneratedColumn)}${encodeVlq(point.sourceIndex - lastSourceIndex)}${encodeVlq(point.originalLine - lastOriginalLine)}${encodeVlq(point.originalColumn - lastOriginalColumn)}`;
|
|
1025
|
+
lastGeneratedColumn = point.generatedColumn;
|
|
1026
|
+
lastSourceIndex = point.sourceIndex;
|
|
1027
|
+
lastOriginalLine = point.originalLine;
|
|
1028
|
+
lastOriginalColumn = point.originalColumn;
|
|
1029
|
+
return encoded;
|
|
1030
|
+
}).join(",");
|
|
1031
|
+
});
|
|
1032
|
+
return { sources, mappings: lines.join(";") };
|
|
1033
|
+
};
|
|
1034
|
+
var attachInlineSourceMap = (original, generated, filePath, edits) => {
|
|
1035
|
+
const file = basename(filePath);
|
|
1036
|
+
const withoutUpstream = generated.replace(SOURCEMAP_COMMENT, "");
|
|
1037
|
+
const { sources, mappings } = encodeColumnMappings(original, withoutUpstream, file, edits);
|
|
1038
|
+
const comment = `/*# sourceMappingURL=data:application/json;base64,${Buffer.from(
|
|
1039
|
+
JSON.stringify({ version: 3, file, sources, mappings })
|
|
1040
|
+
).toString("base64")} */`;
|
|
1041
|
+
return withoutUpstream.endsWith("\n") ? `${withoutUpstream.slice(0, -1)}${comment}
|
|
1042
|
+
` : `${withoutUpstream}${comment}`;
|
|
1043
|
+
};
|
|
1044
|
+
var insertAfterSuperOrPrologue = (body, sourceFile) => {
|
|
1045
|
+
const end = functionBodyPrologueEnd(body, sourceFile);
|
|
1046
|
+
for (const statement of body.statements) {
|
|
1047
|
+
if (isDirectivePrologue(statement)) continue;
|
|
1048
|
+
if (ts6.isExpressionStatement(statement) && ts6.isCallExpression(statement.expression) && statement.expression.expression.kind === ts6.SyntaxKind.SuperKeyword) {
|
|
1049
|
+
return statement.end;
|
|
1050
|
+
}
|
|
1051
|
+
break;
|
|
1052
|
+
}
|
|
1053
|
+
return end;
|
|
1054
|
+
};
|
|
1055
|
+
var failEpilogue = (handleName, errorName, args, restoreOnCatch) => `}catch(${errorName}){${restoreOnCatch ? `${handleName}.resume();` : ""}${handleName}.fail(${errorName},${args});throw ${errorName};}finally{${handleName}.end();}`;
|
|
1056
|
+
var walkOwnBody = (node, visitNode) => {
|
|
1057
|
+
if (!node.body) return;
|
|
1058
|
+
const visit = (candidate) => {
|
|
1059
|
+
if (candidate !== node && ts6.isFunctionLike(candidate)) return;
|
|
1060
|
+
visitNode(candidate);
|
|
1061
|
+
ts6.forEachChild(candidate, visit);
|
|
1062
|
+
};
|
|
1063
|
+
visit(node.body);
|
|
1064
|
+
};
|
|
1065
|
+
var collectAwaitWraps = (node, sourceFile, handleName, edits, depth) => {
|
|
1066
|
+
walkOwnBody(node, (candidate) => {
|
|
1067
|
+
if (!ts6.isAwaitExpression(candidate)) return;
|
|
1068
|
+
wrap(
|
|
1069
|
+
edits,
|
|
1070
|
+
candidate.expression.getStart(sourceFile),
|
|
1071
|
+
candidate.expression.end,
|
|
1072
|
+
`${handleName}.suspend(`,
|
|
1073
|
+
")",
|
|
1074
|
+
depth + 3
|
|
1075
|
+
);
|
|
1076
|
+
wrap(
|
|
1077
|
+
edits,
|
|
1078
|
+
candidate.getStart(sourceFile),
|
|
1079
|
+
candidate.end,
|
|
1080
|
+
`${handleName}.resume(`,
|
|
1081
|
+
")",
|
|
1082
|
+
depth + 2
|
|
1083
|
+
);
|
|
1084
|
+
});
|
|
1085
|
+
};
|
|
1086
|
+
var collectCatchFinallyResumes = (node, sourceFile, handleName, edits, depth) => {
|
|
1087
|
+
walkOwnBody(node, (candidate) => {
|
|
1088
|
+
if (!ts6.isTryStatement(candidate)) return;
|
|
1089
|
+
const blocks = [candidate.catchClause?.block, candidate.finallyBlock];
|
|
1090
|
+
for (const block of blocks) {
|
|
1091
|
+
if (!block) continue;
|
|
1092
|
+
edits.push({
|
|
1093
|
+
position: block.getStart(sourceFile) + 1,
|
|
1094
|
+
text: `${handleName}.resume();`,
|
|
1095
|
+
deleteCount: 0,
|
|
1096
|
+
depth,
|
|
1097
|
+
width: 0,
|
|
1098
|
+
kind: "replace"
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
1102
|
+
};
|
|
1103
|
+
var collectReturnWraps = (node, sourceFile, handleName, edits, depth) => {
|
|
1104
|
+
if (!node.body || !ts6.isBlock(node.body)) return;
|
|
1105
|
+
const visit = (candidate, at) => {
|
|
1106
|
+
if (candidate !== node && ts6.isFunctionLike(candidate)) return;
|
|
1107
|
+
if (ts6.isReturnStatement(candidate) && candidate.expression) {
|
|
1108
|
+
const expression = candidate.expression;
|
|
1109
|
+
wrap(
|
|
1110
|
+
edits,
|
|
1111
|
+
expression.getStart(sourceFile),
|
|
1112
|
+
expression.end,
|
|
1113
|
+
`(${handleName}.leave((`,
|
|
1114
|
+
")))",
|
|
1115
|
+
at
|
|
1116
|
+
);
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
ts6.forEachChild(candidate, (child) => visit(child, at + 1));
|
|
1120
|
+
};
|
|
1121
|
+
visit(node.body, depth + 1);
|
|
1034
1122
|
};
|
|
1035
1123
|
var transformSource = (source, options) => {
|
|
1036
1124
|
if (source.includes("@rasputin-ignore-file")) return void 0;
|
|
1037
|
-
|
|
1125
|
+
if (sourceIsRasputinInstrumented(source)) return alreadyInstrumentedResult(source);
|
|
1126
|
+
const sourceFile = ts6.createSourceFile(
|
|
1038
1127
|
options.filePath,
|
|
1039
1128
|
source,
|
|
1040
|
-
|
|
1129
|
+
ts6.ScriptTarget.Latest,
|
|
1041
1130
|
true,
|
|
1042
|
-
|
|
1131
|
+
scriptKindFor3(options.filePath)
|
|
1043
1132
|
);
|
|
1044
1133
|
const runName = uniqueName(source, "__rasputinRun__");
|
|
1045
|
-
const capName = uniqueName(source, "__rasputinCap__");
|
|
1046
1134
|
const plansName = uniqueName(source, "__rasputinPlans__");
|
|
1047
|
-
const
|
|
1048
|
-
const
|
|
1135
|
+
const handleName = uniqueName(source, "__rasputinH__");
|
|
1136
|
+
const errorName = uniqueName(source, "__rasputinE__");
|
|
1137
|
+
const absolutePath = resolve4(options.filePath);
|
|
1049
1138
|
const edits = [];
|
|
1050
|
-
let order = 0;
|
|
1051
1139
|
const delta = {
|
|
1052
1140
|
functions: [],
|
|
1053
1141
|
callSites: [],
|
|
1054
|
-
destructures: []
|
|
1055
|
-
derivationPlans: []
|
|
1142
|
+
destructures: []
|
|
1056
1143
|
};
|
|
1057
1144
|
const collectContext = {
|
|
1058
1145
|
sourceFile,
|
|
@@ -1060,75 +1147,134 @@ var transformSource = (source, options) => {
|
|
|
1060
1147
|
absolutePath
|
|
1061
1148
|
};
|
|
1062
1149
|
const pending = [];
|
|
1063
|
-
const
|
|
1150
|
+
const skipFunction = (collected, reason) => {
|
|
1151
|
+
collected.instrumented = false;
|
|
1152
|
+
collected.skipReason = reason;
|
|
1153
|
+
delta.functions.push(collected);
|
|
1154
|
+
if (process.env.RASPUTIN_DEBUG === "1") {
|
|
1155
|
+
console.info(
|
|
1156
|
+
`[rasputin] skipped instrumentation for ${collected.name} in ${options.filePath}: ${reason}`
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
const visit = (node, caller, depth) => {
|
|
1161
|
+
if (isAsyncGeneratorLike(node) && node.body) {
|
|
1162
|
+
const collected = collectWrappedFunction(
|
|
1163
|
+
node,
|
|
1164
|
+
sourceFile,
|
|
1165
|
+
absolutePath,
|
|
1166
|
+
options.mapLocation
|
|
1167
|
+
);
|
|
1168
|
+
if (collected) {
|
|
1169
|
+
collected.asyncFunction = true;
|
|
1170
|
+
skipFunction(collected, "unsupported_async_generator");
|
|
1171
|
+
}
|
|
1172
|
+
ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
if (isSyncGeneratorLike(node) && node.body) {
|
|
1176
|
+
const collected = collectWrappedFunction(
|
|
1177
|
+
node,
|
|
1178
|
+
sourceFile,
|
|
1179
|
+
absolutePath,
|
|
1180
|
+
options.mapLocation
|
|
1181
|
+
);
|
|
1182
|
+
if (collected) skipFunction(collected, "unsupported_sync_generator");
|
|
1183
|
+
ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
if (ts6.isConstructorDeclaration(node) && node.body) {
|
|
1187
|
+
const collected = collectWrappedFunction(
|
|
1188
|
+
node,
|
|
1189
|
+
sourceFile,
|
|
1190
|
+
absolutePath,
|
|
1191
|
+
options.mapLocation
|
|
1192
|
+
);
|
|
1193
|
+
if (collected) skipFunction(collected, "unsupported_constructor");
|
|
1194
|
+
ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1064
1197
|
if (isSupportedFunction(node)) {
|
|
1065
1198
|
const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
|
|
1066
1199
|
if (!collected) {
|
|
1067
|
-
|
|
1200
|
+
ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
const asyncFunction = isAsyncFunctionLike(node);
|
|
1204
|
+
collected.asyncFunction = asyncFunction;
|
|
1205
|
+
const decision = asyncFunction ? decideAsyncInstrumentation(node, sourceFile) : { action: "inline" };
|
|
1206
|
+
if (decision.action === "skip") {
|
|
1207
|
+
skipFunction(collected, decision.reason);
|
|
1208
|
+
ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
|
|
1068
1209
|
return;
|
|
1069
1210
|
}
|
|
1070
|
-
|
|
1211
|
+
if (functionHasPreBodyParameterEvaluation(node)) {
|
|
1212
|
+
collected.plan = withPreBodyParameterEvaluation(collected.plan);
|
|
1213
|
+
}
|
|
1214
|
+
pending.push({
|
|
1215
|
+
node,
|
|
1216
|
+
collected,
|
|
1217
|
+
depth,
|
|
1218
|
+
suspendResume: decision.action === "suspend_resume"
|
|
1219
|
+
});
|
|
1071
1220
|
delta.functions.push(collected);
|
|
1072
|
-
|
|
1221
|
+
ts6.forEachChild(node, (child) => visit(child, collected, depth + 1));
|
|
1073
1222
|
return;
|
|
1074
1223
|
}
|
|
1075
|
-
if (caller && (
|
|
1224
|
+
if (caller && (ts6.isCallExpression(node) || ts6.isNewExpression(node))) {
|
|
1076
1225
|
const site = collectCallSite(node, caller, collectContext);
|
|
1077
1226
|
if (site) delta.callSites.push(site);
|
|
1078
1227
|
}
|
|
1079
|
-
if (caller &&
|
|
1228
|
+
if (caller && ts6.isVariableDeclaration(node)) {
|
|
1080
1229
|
const destructure = collectParamDestructure(node, caller, collectContext);
|
|
1081
1230
|
if (destructure) delta.destructures.push(destructure);
|
|
1082
1231
|
}
|
|
1083
|
-
|
|
1232
|
+
ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
|
|
1084
1233
|
};
|
|
1085
|
-
visit(sourceFile);
|
|
1086
|
-
if (pending.length === 0)
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
pending.map((entry) => ({
|
|
1090
|
-
functionId: entry.collected.functionId,
|
|
1091
|
-
name: entry.collected.name,
|
|
1092
|
-
node: entry.node
|
|
1093
|
-
}))
|
|
1094
|
-
);
|
|
1095
|
-
delta.derivationPlans = analyzed.map((fn) => fn.plan);
|
|
1096
|
-
const analyzedById = new Map(analyzed.map((fn) => [fn.functionId, fn]));
|
|
1234
|
+
visit(sourceFile, void 0, 0);
|
|
1235
|
+
if (pending.length === 0) {
|
|
1236
|
+
return delta.functions.length > 0 ? { code: source, delta } : void 0;
|
|
1237
|
+
}
|
|
1097
1238
|
for (const [planIndex, entry] of pending.entries()) {
|
|
1098
|
-
const
|
|
1099
|
-
const
|
|
1100
|
-
const
|
|
1101
|
-
const
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
edits.push({
|
|
1118
|
-
position: entry.node.body.getStart(sourceFile),
|
|
1119
|
-
text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
|
|
1120
|
-
order: order++
|
|
1121
|
-
});
|
|
1122
|
-
edits.push({
|
|
1123
|
-
position: entry.node.body.end,
|
|
1124
|
-
text: `)${valueFlag})`,
|
|
1125
|
-
order: order++
|
|
1126
|
-
});
|
|
1127
|
-
}
|
|
1128
|
-
if (analysis) {
|
|
1129
|
-
for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
|
|
1130
|
-
edits.push({ ...edit, order: order++ });
|
|
1239
|
+
const captured = ts6.isArrowFunction(entry.node) ? arrowArgsCapture(entry.node) : { args: "arguments", omitValues: false };
|
|
1240
|
+
const omitArg = captured.omitValues ? ",true" : "";
|
|
1241
|
+
const enterCall = `${runName}.enter(${planIndex},${captured.args}${omitArg})`;
|
|
1242
|
+
const epilogue = failEpilogue(handleName, errorName, captured.args, entry.suspendResume);
|
|
1243
|
+
if (ts6.isBlock(entry.node.body)) {
|
|
1244
|
+
const prologueAt = insertAfterSuperOrPrologue(entry.node.body, sourceFile);
|
|
1245
|
+
const closeAt = entry.node.body.end - 1;
|
|
1246
|
+
wrap(
|
|
1247
|
+
edits,
|
|
1248
|
+
prologueAt,
|
|
1249
|
+
closeAt,
|
|
1250
|
+
`;const ${handleName}=${enterCall};try{`,
|
|
1251
|
+
epilogue,
|
|
1252
|
+
entry.depth
|
|
1253
|
+
);
|
|
1254
|
+
collectReturnWraps(entry.node, sourceFile, handleName, edits, entry.depth);
|
|
1255
|
+
if (entry.suspendResume) {
|
|
1256
|
+
collectAwaitWraps(entry.node, sourceFile, handleName, edits, entry.depth);
|
|
1257
|
+
collectCatchFinallyResumes(entry.node, sourceFile, handleName, edits, entry.depth);
|
|
1131
1258
|
}
|
|
1259
|
+
} else if (entry.suspendResume) {
|
|
1260
|
+
wrap(
|
|
1261
|
+
edits,
|
|
1262
|
+
entry.node.body.getStart(sourceFile),
|
|
1263
|
+
entry.node.body.end,
|
|
1264
|
+
`{;const ${handleName}=${enterCall};try{return (${handleName}.leave((`,
|
|
1265
|
+
`)));${epilogue}}`,
|
|
1266
|
+
entry.depth
|
|
1267
|
+
);
|
|
1268
|
+
collectAwaitWraps(entry.node, sourceFile, handleName, edits, entry.depth);
|
|
1269
|
+
} else {
|
|
1270
|
+
wrap(
|
|
1271
|
+
edits,
|
|
1272
|
+
entry.node.body.getStart(sourceFile),
|
|
1273
|
+
entry.node.body.end,
|
|
1274
|
+
`{;const ${handleName}=${enterCall};try{return (${handleName}.leave((`,
|
|
1275
|
+
`)));${epilogue}}`,
|
|
1276
|
+
entry.depth
|
|
1277
|
+
);
|
|
1132
1278
|
}
|
|
1133
1279
|
}
|
|
1134
1280
|
const insertAt = insertPositionAfterPrologue(source, sourceFile);
|
|
@@ -1139,19 +1285,189 @@ var transformSource = (source, options) => {
|
|
|
1139
1285
|
insertAt,
|
|
1140
1286
|
modulePreamble(
|
|
1141
1287
|
runName,
|
|
1142
|
-
capName,
|
|
1143
1288
|
plansName,
|
|
1144
|
-
JSON.stringify(
|
|
1289
|
+
JSON.stringify(pending.map((entry) => entry.collected.plan))
|
|
1145
1290
|
)
|
|
1146
1291
|
),
|
|
1147
|
-
|
|
1292
|
+
deleteCount: 0,
|
|
1293
|
+
depth: -1,
|
|
1294
|
+
width: 0,
|
|
1295
|
+
kind: "replace"
|
|
1148
1296
|
});
|
|
1149
|
-
edits.sort(
|
|
1297
|
+
edits.sort(compareEdits);
|
|
1150
1298
|
let transformed = source;
|
|
1151
1299
|
for (const edit of edits) {
|
|
1152
|
-
|
|
1300
|
+
const text = separateFromPreviousToken(transformed, edit.position, edit.text);
|
|
1301
|
+
transformed = `${transformed.slice(0, edit.position)}${text}${transformed.slice(edit.position + edit.deleteCount)}`;
|
|
1302
|
+
}
|
|
1303
|
+
const verify = options.verifyTransform ?? true;
|
|
1304
|
+
const refuseTransformed = () => {
|
|
1305
|
+
for (const entry of pending) {
|
|
1306
|
+
entry.collected.instrumented = false;
|
|
1307
|
+
entry.collected.skipReason = "verification_failed";
|
|
1308
|
+
}
|
|
1309
|
+
if (process.env.RASPUTIN_DEBUG === "1") {
|
|
1310
|
+
console.info(
|
|
1311
|
+
`[rasputin] skipped instrumentation for ${options.filePath}: verification_failed`
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
return { code: source, delta };
|
|
1315
|
+
};
|
|
1316
|
+
if (verify && transformedSourceIsInvalid(options.filePath, transformed)) {
|
|
1317
|
+
if (transformedSourceIsInvalid(options.filePath, source)) {
|
|
1318
|
+
if (process.env.RASPUTIN_DEBUG === "1") {
|
|
1319
|
+
console.info(
|
|
1320
|
+
`[rasputin] skipped instrumentation for ${options.filePath}: transformed source failed verification`
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
return void 0;
|
|
1324
|
+
}
|
|
1325
|
+
return refuseTransformed();
|
|
1326
|
+
}
|
|
1327
|
+
if (verify && !instrumentationTreesAreIsomorphic(options.filePath, source, transformed)) {
|
|
1328
|
+
return refuseTransformed();
|
|
1329
|
+
}
|
|
1330
|
+
return { code: attachInlineSourceMap(source, transformed, options.filePath, edits), delta };
|
|
1331
|
+
};
|
|
1332
|
+
|
|
1333
|
+
// src/auto-instrumentation/transform-disk-cache.ts
|
|
1334
|
+
var projectCacheDir = () => {
|
|
1335
|
+
let directory = process.cwd();
|
|
1336
|
+
for (let depth = 0; depth < 10; depth++) {
|
|
1337
|
+
const modules = join2(directory, "node_modules");
|
|
1338
|
+
try {
|
|
1339
|
+
if (statSync(modules).isDirectory()) {
|
|
1340
|
+
return join2(modules, ".cache", "rasputin-transform", SDK_VERSION);
|
|
1341
|
+
}
|
|
1342
|
+
} catch {
|
|
1343
|
+
}
|
|
1344
|
+
const parent = dirname2(directory);
|
|
1345
|
+
if (parent === directory) break;
|
|
1346
|
+
directory = parent;
|
|
1347
|
+
}
|
|
1348
|
+
return void 0;
|
|
1349
|
+
};
|
|
1350
|
+
var resolveTransformCacheDir = () => process.env.RASPUTIN_TRANSFORM_CACHE_DIR ?? projectCacheDir() ?? join2(homedir(), ".cache", "rasputin", "transform", SDK_VERSION);
|
|
1351
|
+
var defaultCacheLimit = () => {
|
|
1352
|
+
const parsed = Number(process.env.RASPUTIN_TRANSFORM_CACHE_MAX);
|
|
1353
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : 256;
|
|
1354
|
+
};
|
|
1355
|
+
var cacheEnabled = () => process.env.RASPUTIN_TRANSFORM_CACHE !== "0";
|
|
1356
|
+
var directoryIsWorldWritable = (directory) => {
|
|
1357
|
+
try {
|
|
1358
|
+
const stats = lstatSync(directory);
|
|
1359
|
+
return !stats.isDirectory() || (stats.mode & 2) !== 0;
|
|
1360
|
+
} catch {
|
|
1361
|
+
return true;
|
|
1362
|
+
}
|
|
1363
|
+
};
|
|
1364
|
+
var directoryOwnerTrusted = (directory) => {
|
|
1365
|
+
if (process.platform === "win32") return true;
|
|
1366
|
+
try {
|
|
1367
|
+
const stats = lstatSync(directory);
|
|
1368
|
+
const uid = process.getuid?.();
|
|
1369
|
+
if (uid === void 0) return true;
|
|
1370
|
+
return stats.uid === uid;
|
|
1371
|
+
} catch {
|
|
1372
|
+
return false;
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
var ensureTrustedCacheDir = (directory, explicit) => {
|
|
1376
|
+
try {
|
|
1377
|
+
mkdirSync(directory, { recursive: true, mode: 448 });
|
|
1378
|
+
try {
|
|
1379
|
+
chmodSync(directory, 448);
|
|
1380
|
+
} catch {
|
|
1381
|
+
}
|
|
1382
|
+
const stats = lstatSync(directory);
|
|
1383
|
+
if (!stats.isDirectory()) return false;
|
|
1384
|
+
if (explicit) return true;
|
|
1385
|
+
if (process.platform === "win32") return true;
|
|
1386
|
+
return !directoryIsWorldWritable(directory) && directoryOwnerTrusted(directory);
|
|
1387
|
+
} catch {
|
|
1388
|
+
return false;
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
var cacheKey = (source, filePath, verifyTransform) => createHash("sha256").update(SDK_VERSION).update("\0").update(filePath).update("\0").update(source).update("\0").update(sourceMapPayloadForCache(filePath, source)).update("\0").update(verifyTransform ? "1" : "0").digest("hex");
|
|
1392
|
+
var revive = (cached) => ({
|
|
1393
|
+
code: cached.code,
|
|
1394
|
+
delta: {
|
|
1395
|
+
functions: cached.delta.functions.map((fn) => ({
|
|
1396
|
+
...fn,
|
|
1397
|
+
instrumented: fn.instrumented !== false,
|
|
1398
|
+
paramNames: new Set(fn.paramNames)
|
|
1399
|
+
})),
|
|
1400
|
+
callSites: cached.delta.callSites,
|
|
1401
|
+
destructures: cached.delta.destructures
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
1404
|
+
var persistable = (result) => ({
|
|
1405
|
+
code: result.code,
|
|
1406
|
+
delta: {
|
|
1407
|
+
functions: result.delta.functions.map((fn) => ({
|
|
1408
|
+
...fn,
|
|
1409
|
+
paramNames: [...fn.paramNames]
|
|
1410
|
+
})),
|
|
1411
|
+
callSites: result.delta.callSites,
|
|
1412
|
+
destructures: result.delta.destructures
|
|
1413
|
+
}
|
|
1414
|
+
});
|
|
1415
|
+
var evictOldest = (directory, keep) => {
|
|
1416
|
+
const entries = readdirSync(directory).map((name) => {
|
|
1417
|
+
const path = join2(directory, name);
|
|
1418
|
+
try {
|
|
1419
|
+
return { path, mtime: statSync(path).mtimeMs };
|
|
1420
|
+
} catch {
|
|
1421
|
+
return void 0;
|
|
1422
|
+
}
|
|
1423
|
+
}).filter((entry) => entry !== void 0).sort((left, right) => left.mtime - right.mtime);
|
|
1424
|
+
while (entries.length > keep) {
|
|
1425
|
+
const oldest = entries.shift();
|
|
1426
|
+
if (!oldest) break;
|
|
1427
|
+
try {
|
|
1428
|
+
unlinkSync(oldest.path);
|
|
1429
|
+
} catch {
|
|
1430
|
+
}
|
|
1153
1431
|
}
|
|
1154
|
-
|
|
1432
|
+
};
|
|
1433
|
+
var readCache = (directory, key) => {
|
|
1434
|
+
try {
|
|
1435
|
+
const parsed = JSON.parse(
|
|
1436
|
+
readFileSync2(join2(directory, `${key}.json`), "utf8")
|
|
1437
|
+
);
|
|
1438
|
+
if (typeof parsed.code !== "string" || !parsed.delta) return void 0;
|
|
1439
|
+
return revive(parsed);
|
|
1440
|
+
} catch {
|
|
1441
|
+
return void 0;
|
|
1442
|
+
}
|
|
1443
|
+
};
|
|
1444
|
+
var writeCache = (directory, key, result, limit) => {
|
|
1445
|
+
try {
|
|
1446
|
+
const path = join2(directory, `${key}.json`);
|
|
1447
|
+
writeFileSync(path, JSON.stringify(persistable(result)), { mode: 384 });
|
|
1448
|
+
try {
|
|
1449
|
+
chmodSync(path, 384);
|
|
1450
|
+
} catch {
|
|
1451
|
+
}
|
|
1452
|
+
evictOldest(directory, limit);
|
|
1453
|
+
} catch {
|
|
1454
|
+
}
|
|
1455
|
+
};
|
|
1456
|
+
var transformSourceCached = (source, options, cache = {}) => {
|
|
1457
|
+
const transformOptions = {
|
|
1458
|
+
...options,
|
|
1459
|
+
verifyTransform: options.verifyTransform ?? process.env.RASPUTIN_VERIFY_TRANSFORM !== "0"
|
|
1460
|
+
};
|
|
1461
|
+
if (!cacheEnabled()) return transformSource(source, transformOptions);
|
|
1462
|
+
const explicit = cache.directory !== void 0;
|
|
1463
|
+
const directory = cache.directory ?? resolveTransformCacheDir();
|
|
1464
|
+
if (!ensureTrustedCacheDir(directory, explicit)) return transformSource(source, transformOptions);
|
|
1465
|
+
const key = cacheKey(source, options.filePath, transformOptions.verifyTransform === true);
|
|
1466
|
+
const hit = readCache(directory, key);
|
|
1467
|
+
if (hit) return hit;
|
|
1468
|
+
const result = transformSource(source, transformOptions);
|
|
1469
|
+
if (result) writeCache(directory, key, result, cache.limit ?? defaultCacheLimit());
|
|
1470
|
+
return result;
|
|
1155
1471
|
};
|
|
1156
1472
|
|
|
1157
1473
|
// src/auto-instrumentation/bun-plugin.ts
|
|
@@ -1164,13 +1480,13 @@ var loaderFor = (filePath) => {
|
|
|
1164
1480
|
var loadedSourceFilter = /^(?!.*[\\/](?:node_modules|dist|build|out|coverage|generated|\.git|\.next|\.svelte-kit|\.output|\.vercel|\.rasputin|\.turbo|\.yarn|\.pnpm|\.bun)[\\/]).*\.[cm]?[jt]sx?$/i;
|
|
1165
1481
|
var isLoadedApplicationSource = (filePath, options = {}) => isApplicationSource(filePath, { ...options, includeBuildOutput: false });
|
|
1166
1482
|
var loadSourceForInstrumentation = async (filePath, options = {}) => {
|
|
1167
|
-
const absolutePath =
|
|
1483
|
+
const absolutePath = resolve5(filePath);
|
|
1168
1484
|
const source = await readFile(absolutePath, "utf8");
|
|
1169
1485
|
if (!isLoadedApplicationSource(absolutePath, options)) {
|
|
1170
1486
|
return { contents: source, loader: loaderFor(absolutePath), transformed: false };
|
|
1171
1487
|
}
|
|
1172
1488
|
try {
|
|
1173
|
-
const transformed =
|
|
1489
|
+
const transformed = transformSourceCached(source, { filePath: absolutePath });
|
|
1174
1490
|
if (transformed) {
|
|
1175
1491
|
recordInstrumentationManifestDelta(absolutePath, transformed.delta);
|
|
1176
1492
|
}
|
|
@@ -1179,7 +1495,11 @@ var loadSourceForInstrumentation = async (filePath, options = {}) => {
|
|
|
1179
1495
|
loader: loaderFor(absolutePath),
|
|
1180
1496
|
transformed: transformed !== void 0
|
|
1181
1497
|
};
|
|
1182
|
-
} catch {
|
|
1498
|
+
} catch (error) {
|
|
1499
|
+
if (options.debug ?? process.env.RASPUTIN_DEBUG === "1") {
|
|
1500
|
+
const detail = error instanceof Error ? error.message : "unknown error";
|
|
1501
|
+
console.info(`[rasputin] skipped instrumentation for ${absolutePath}: ${detail}`);
|
|
1502
|
+
}
|
|
1183
1503
|
return { contents: source, loader: loaderFor(absolutePath), transformed: false };
|
|
1184
1504
|
}
|
|
1185
1505
|
};
|
|
@@ -1190,11 +1510,11 @@ var createRasputinBunPlugin = (options = {}) => {
|
|
|
1190
1510
|
setup(build) {
|
|
1191
1511
|
if (debug) {
|
|
1192
1512
|
console.info(
|
|
1193
|
-
options.rootDir ? `[rasputin] automatic instrumentation active (source root: ${
|
|
1513
|
+
options.rootDir ? `[rasputin] automatic instrumentation active (source root: ${resolve5(options.rootDir)})` : "[rasputin] automatic instrumentation active (loaded local source)"
|
|
1194
1514
|
);
|
|
1195
1515
|
}
|
|
1196
1516
|
build.onLoad({ filter: loadedSourceFilter }, async ({ path }) => {
|
|
1197
|
-
const absolutePath =
|
|
1517
|
+
const absolutePath = resolve5(path);
|
|
1198
1518
|
const result = await loadSourceForInstrumentation(absolutePath, options);
|
|
1199
1519
|
if (debug && result.transformed) {
|
|
1200
1520
|
console.info(`[rasputin] instrumented ${absolutePath}`);
|
|
@@ -1206,9 +1526,9 @@ var createRasputinBunPlugin = (options = {}) => {
|
|
|
1206
1526
|
};
|
|
1207
1527
|
|
|
1208
1528
|
// src/auto-instrumentation/sdk-source-boundary.ts
|
|
1209
|
-
import { dirname, resolve as
|
|
1210
|
-
import { fileURLToPath } from "node:url";
|
|
1211
|
-
var sdkPackageScopeRootFrom = (instrumentEntryUrl) =>
|
|
1529
|
+
import { dirname as dirname3, resolve as resolve6 } from "node:path";
|
|
1530
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1531
|
+
var sdkPackageScopeRootFrom = (instrumentEntryUrl) => resolve6(dirname3(fileURLToPath2(instrumentEntryUrl)), "../../..");
|
|
1212
1532
|
|
|
1213
1533
|
// src/auto-instrumentation/warn-production-runtime-transform.ts
|
|
1214
1534
|
var RUNTIME_TRANSFORM_WARNING_SUPPRESS_ENV = "RASPUTIN_SUPPRESS_RUNTIME_TRANSFORM_WARNING";
|
|
@@ -1219,7 +1539,7 @@ var warnIfProductionRuntimeTransform = () => {
|
|
|
1219
1539
|
if (Reflect.get(globalThis, warnedSymbol)) return;
|
|
1220
1540
|
Reflect.set(globalThis, warnedSymbol, true);
|
|
1221
1541
|
console.warn(
|
|
1222
|
-
"[rasputin]
|
|
1542
|
+
"[rasputin] This production process is wrapping source as it loads (`--import` / `--preload`). Instrument compiled output at build time with `rasputin-instrument` instead \u2014 wrapping at startup is slower and uses more memory. Set RASPUTIN_SUPPRESS_RUNTIME_TRANSFORM_WARNING=1 to hide this warning."
|
|
1223
1543
|
);
|
|
1224
1544
|
};
|
|
1225
1545
|
|