@rasputin-ai/node 0.5.0-alpha.8 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +219 -188
- 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 +2637 -894
- 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 -797
- package/dist/instrument/bun.js +1098 -770
- package/dist/instrument/node.js +1078 -772
- 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/dist/sdk-meta.d.ts.map +1 -1
- package/package.json +8 -3
- 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,657 +103,74 @@ var isApplicationSource = (filePath, options = {}) => {
|
|
|
86
103
|
return true;
|
|
87
104
|
};
|
|
88
105
|
|
|
89
|
-
// src/auto-instrumentation/transform-
|
|
90
|
-
import {
|
|
91
|
-
import
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
existing.push(edge.calleeName);
|
|
104
|
-
calleesByCaller.set(edge.callerFunctionId, existing);
|
|
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;
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
// src/derivation/derivation-ir.ts
|
|
155
|
-
var DerivationIrBuilder = class {
|
|
156
|
-
expressions = [];
|
|
157
|
-
nextId = 0;
|
|
158
|
-
constValue(value) {
|
|
159
|
-
return this.add({ id: this.nextId++, kind: "const", value });
|
|
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
|
-
});
|
|
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";
|
|
213
120
|
|
|
214
|
-
// src/
|
|
215
|
-
var
|
|
121
|
+
// src/sdk-meta.ts
|
|
122
|
+
var SDK_VERSION = "0.5.0";
|
|
216
123
|
|
|
217
|
-
// src/
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
};
|
|
222
|
-
var
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return void 0;
|
|
228
|
-
};
|
|
229
|
-
var fallback = (ctx, reason) => {
|
|
230
|
-
ctx.fallback ??= reason;
|
|
231
|
-
return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "unknown" };
|
|
232
|
-
};
|
|
233
|
-
var capture = (ctx, node, reason) => {
|
|
234
|
-
const rootId = ctx.nextRootId.value++;
|
|
235
|
-
const position = loc(node, ctx.sourceFile);
|
|
236
|
-
const expressionId = ctx.ir.root(rootId);
|
|
237
|
-
ctx.captureWraps.push({
|
|
238
|
-
rootId,
|
|
239
|
-
reason,
|
|
240
|
-
start: node.getStart(ctx.sourceFile),
|
|
241
|
-
end: node.end,
|
|
242
|
-
line: position.line,
|
|
243
|
-
column: position.column,
|
|
244
|
-
expressionId
|
|
245
|
-
});
|
|
246
|
-
return { expressionId, shape: "unknown", captured: true };
|
|
247
|
-
};
|
|
248
|
-
var binaryOp = (kind) => {
|
|
249
|
-
switch (kind) {
|
|
250
|
-
case ts.SyntaxKind.PlusToken:
|
|
251
|
-
return "add";
|
|
252
|
-
case ts.SyntaxKind.MinusToken:
|
|
253
|
-
return "sub";
|
|
254
|
-
case ts.SyntaxKind.AsteriskToken:
|
|
255
|
-
return "mul";
|
|
256
|
-
case ts.SyntaxKind.SlashToken:
|
|
257
|
-
return "div";
|
|
258
|
-
case ts.SyntaxKind.PercentToken:
|
|
259
|
-
return "mod";
|
|
260
|
-
case ts.SyntaxKind.EqualsEqualsToken:
|
|
261
|
-
return "eq";
|
|
262
|
-
case ts.SyntaxKind.ExclamationEqualsToken:
|
|
263
|
-
return "neq";
|
|
264
|
-
case ts.SyntaxKind.EqualsEqualsEqualsToken:
|
|
265
|
-
return "seq";
|
|
266
|
-
case ts.SyntaxKind.ExclamationEqualsEqualsToken:
|
|
267
|
-
return "sneq";
|
|
268
|
-
case ts.SyntaxKind.LessThanToken:
|
|
269
|
-
return "lt";
|
|
270
|
-
case ts.SyntaxKind.LessThanEqualsToken:
|
|
271
|
-
return "lte";
|
|
272
|
-
case ts.SyntaxKind.GreaterThanToken:
|
|
273
|
-
return "gt";
|
|
274
|
-
case ts.SyntaxKind.GreaterThanEqualsToken:
|
|
275
|
-
return "gte";
|
|
276
|
-
default:
|
|
277
|
-
return void 0;
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
var isNumericBinaryOp = (op) => op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod";
|
|
281
|
-
var unwrapParens = (node) => {
|
|
282
|
-
while (ts.isParenthesizedExpression(node)) node = node.expression;
|
|
283
|
-
return node;
|
|
284
|
-
};
|
|
285
|
-
var numericLiteralValue = (node) => {
|
|
286
|
-
const value = Number(node.text);
|
|
287
|
-
return { type: "number", value };
|
|
288
|
-
};
|
|
289
|
-
var analyzeExpression = (ctx, node) => {
|
|
290
|
-
if (ctx.fallback) return fallback(ctx, ctx.fallback);
|
|
291
|
-
if (ts.isParenthesizedExpression(node)) return analyzeExpression(ctx, node.expression);
|
|
292
|
-
if (ts.isAsExpression(node) || ts.isNonNullExpression(node)) {
|
|
293
|
-
return analyzeExpression(ctx, node.expression);
|
|
294
|
-
}
|
|
295
|
-
if (node.kind === ts.SyntaxKind.TypeAssertionExpression) {
|
|
296
|
-
return analyzeExpression(ctx, node.expression);
|
|
297
|
-
}
|
|
298
|
-
if (ts.isSatisfiesExpression(node)) return analyzeExpression(ctx, node.expression);
|
|
299
|
-
if (node.kind === ts.SyntaxKind.NullKeyword) {
|
|
300
|
-
return { expressionId: ctx.ir.constValue({ type: "null" }), shape: "primitive" };
|
|
301
|
-
}
|
|
302
|
-
if (node.kind === ts.SyntaxKind.TrueKeyword) {
|
|
303
|
-
return {
|
|
304
|
-
expressionId: ctx.ir.constValue({ type: "boolean", value: true }),
|
|
305
|
-
shape: "primitive"
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
if (node.kind === ts.SyntaxKind.FalseKeyword) {
|
|
309
|
-
return {
|
|
310
|
-
expressionId: ctx.ir.constValue({ type: "boolean", value: false }),
|
|
311
|
-
shape: "primitive"
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
if (ts.isNumericLiteral(node)) {
|
|
315
|
-
return { expressionId: ctx.ir.constValue(numericLiteralValue(node)), shape: "primitive" };
|
|
316
|
-
}
|
|
317
|
-
if (ts.isPrefixUnaryExpression(node) && ts.isNumericLiteral(node.operand) && node.operator === ts.SyntaxKind.MinusToken) {
|
|
318
|
-
const value = -Number(node.operand.text);
|
|
319
|
-
return {
|
|
320
|
-
expressionId: ctx.ir.constValue(
|
|
321
|
-
Object.is(value, -0) ? { type: "number", value: 0, negativeZero: true } : { type: "number", value }
|
|
322
|
-
),
|
|
323
|
-
shape: "primitive"
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
327
|
-
return {
|
|
328
|
-
expressionId: ctx.ir.constValue({ type: "string", value: node.text }),
|
|
329
|
-
shape: "primitive"
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
if (ts.isBigIntLiteral(node)) {
|
|
333
|
-
return {
|
|
334
|
-
expressionId: ctx.ir.constValue({ type: "bigint", value: node.text.replace(/n$/, "") }),
|
|
335
|
-
shape: "primitive"
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
if (ts.isIdentifier(node)) {
|
|
339
|
-
if (node.text === "undefined") {
|
|
340
|
-
return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "primitive" };
|
|
341
|
-
}
|
|
342
|
-
const binding = lookup(ctx, node.text);
|
|
343
|
-
if (!binding) return capture(ctx, node, "external_state");
|
|
344
|
-
if (binding.mutable) return capture(ctx, node, "mutable_object");
|
|
345
|
-
return {
|
|
346
|
-
expressionId: ctx.ir.alias(binding.expressionId),
|
|
347
|
-
shape: binding.shape
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
if (ts.isTemplateExpression(node)) {
|
|
351
|
-
const parts = [
|
|
352
|
-
{ kind: "lit", value: node.head.text }
|
|
353
|
-
];
|
|
354
|
-
for (const span of node.templateSpans) {
|
|
355
|
-
const nested = analyzeExpression(ctx, span.expression);
|
|
356
|
-
if (nested.shape !== "primitive" && !nested.captured) {
|
|
357
|
-
return capture(ctx, node, "unsupported_expression");
|
|
358
|
-
}
|
|
359
|
-
parts.push({ kind: "expr", expressionId: nested.expressionId });
|
|
360
|
-
parts.push({ kind: "lit", value: span.literal.text });
|
|
361
|
-
}
|
|
362
|
-
return { expressionId: ctx.ir.template(parts), shape: "primitive" };
|
|
363
|
-
}
|
|
364
|
-
if (ts.isArrayLiteralExpression(node)) {
|
|
365
|
-
if (node.elements.some((element) => ts.isSpreadElement(element))) {
|
|
366
|
-
return capture(ctx, node, "unsupported_expression");
|
|
367
|
-
}
|
|
368
|
-
const elements = node.elements.map((element) => {
|
|
369
|
-
if (ts.isOmittedExpression(element)) return { kind: "hole" };
|
|
370
|
-
return analyzeExpression(ctx, element).expressionId;
|
|
371
|
-
});
|
|
372
|
-
return { expressionId: ctx.ir.array(elements), shape: "array" };
|
|
373
|
-
}
|
|
374
|
-
if (ts.isObjectLiteralExpression(node)) {
|
|
375
|
-
const properties = [];
|
|
376
|
-
for (const property of node.properties) {
|
|
377
|
-
if (ts.isShorthandPropertyAssignment(property)) {
|
|
378
|
-
const nested = analyzeExpression(ctx, property.name);
|
|
379
|
-
if (nested.captured) {
|
|
380
|
-
const wrap = ctx.captureWraps.find(
|
|
381
|
-
(candidate) => candidate.expressionId === nested.expressionId
|
|
382
|
-
);
|
|
383
|
-
if (wrap) wrap.shorthandPropertyName = property.name.text;
|
|
384
|
-
}
|
|
385
|
-
properties.push({ key: property.name.text, value: nested.expressionId });
|
|
386
|
-
continue;
|
|
387
|
-
}
|
|
388
|
-
if (!ts.isPropertyAssignment(property)) {
|
|
389
|
-
return capture(ctx, node, "unsupported_expression");
|
|
390
|
-
}
|
|
391
|
-
if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
|
|
392
|
-
return capture(ctx, node, "unsupported_expression");
|
|
393
|
-
}
|
|
394
|
-
const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
|
|
395
|
-
if (key === void 0) return capture(ctx, node, "unsupported_expression");
|
|
396
|
-
properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
|
|
397
|
-
}
|
|
398
|
-
return { expressionId: ctx.ir.object(properties), shape: "object" };
|
|
399
|
-
}
|
|
400
|
-
if (ts.isPropertyAccessExpression(node)) {
|
|
401
|
-
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
402
|
-
const object = analyzeExpression(ctx, node.expression);
|
|
403
|
-
if (object.shape === "object" && ts.isIdentifier(node.name)) {
|
|
404
|
-
return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
|
|
405
|
-
}
|
|
406
|
-
return capture(ctx, node, "unknown_property");
|
|
407
|
-
}
|
|
408
|
-
if (ts.isElementAccessExpression(node)) {
|
|
409
|
-
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
410
|
-
const array = analyzeExpression(ctx, node.expression);
|
|
411
|
-
if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
|
|
412
|
-
return {
|
|
413
|
-
expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
|
|
414
|
-
shape: "unknown"
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
return capture(ctx, node, "unknown_property");
|
|
418
|
-
}
|
|
419
|
-
if (ts.isCallExpression(node)) {
|
|
420
|
-
if (node.questionDotToken || node.typeArguments?.length) {
|
|
421
|
-
return capture(ctx, node, "unsupported_expression");
|
|
422
|
-
}
|
|
423
|
-
if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
|
|
424
|
-
const calleeName = node.expression.text;
|
|
425
|
-
if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
|
|
426
|
-
const provenId = ctx.provenExact.get(calleeName);
|
|
427
|
-
ctx.callEdges.push({
|
|
428
|
-
callerFunctionId: ctx.functionId,
|
|
429
|
-
calleeName,
|
|
430
|
-
...provenId ? { calleeFunctionId: provenId } : {}
|
|
431
|
-
});
|
|
432
|
-
if (!provenId) return capture(ctx, node, "opaque_call");
|
|
433
|
-
const args = node.arguments.map((argument) => {
|
|
434
|
-
if (ts.isSpreadElement(argument)) return void 0;
|
|
435
|
-
return analyzeExpression(ctx, argument).expressionId;
|
|
436
|
-
});
|
|
437
|
-
if (args.some((argument) => argument === void 0))
|
|
438
|
-
return capture(ctx, node, "unsupported_expression");
|
|
439
|
-
return {
|
|
440
|
-
expressionId: ctx.ir.pureCall(
|
|
441
|
-
provenId,
|
|
442
|
-
args.filter((id) => id !== void 0)
|
|
443
|
-
),
|
|
444
|
-
shape: "unknown"
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
|
|
448
|
-
return capture(
|
|
449
|
-
ctx,
|
|
450
|
-
node,
|
|
451
|
-
node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
|
|
452
|
-
);
|
|
453
|
-
}
|
|
454
|
-
if (ts.isConditionalExpression(node)) {
|
|
455
|
-
const test = analyzeExpression(ctx, node.condition);
|
|
456
|
-
const consequent = analyzeExpression(ctx, node.whenTrue);
|
|
457
|
-
const alternate = analyzeExpression(ctx, node.whenFalse);
|
|
458
|
-
return {
|
|
459
|
-
expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
|
|
460
|
-
shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
if (ts.isBinaryExpression(node)) {
|
|
464
|
-
if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
465
|
-
ctx.fallback ??= "mutable_state";
|
|
466
|
-
return capture(ctx, node, "mutable_object");
|
|
467
|
-
}
|
|
468
|
-
if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
469
|
-
const left2 = analyzeExpression(ctx, node.left);
|
|
470
|
-
const right2 = analyzeExpression(ctx, node.right);
|
|
471
|
-
const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
|
|
472
|
-
return {
|
|
473
|
-
expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
|
|
474
|
-
shape: "unknown"
|
|
475
|
-
};
|
|
476
|
-
}
|
|
477
|
-
const op = binaryOp(node.operatorToken.kind);
|
|
478
|
-
if (!op) return capture(ctx, node, "unsupported_expression");
|
|
479
|
-
if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
|
|
480
|
-
const left = analyzeExpression(ctx, node.left);
|
|
481
|
-
const right = analyzeExpression(ctx, node.right);
|
|
482
|
-
const numeric = isNumericBinaryOp(op);
|
|
483
|
-
const expressionId = ctx.ir.binary(
|
|
484
|
-
op,
|
|
485
|
-
left.expressionId,
|
|
486
|
-
right.expressionId,
|
|
487
|
-
numeric ? "numeric" : "primitive"
|
|
488
|
-
);
|
|
489
|
-
if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
|
|
490
|
-
ctx.hasCoercionGuard = true;
|
|
491
|
-
const fallbackRootId = ctx.nextRootId.value++;
|
|
492
|
-
ctx.guardWraps.push({
|
|
493
|
-
fallbackRootId,
|
|
494
|
-
start: node.getStart(ctx.sourceFile),
|
|
495
|
-
end: node.end,
|
|
496
|
-
op
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
return { expressionId, shape: "primitive" };
|
|
500
|
-
}
|
|
501
|
-
if (ts.isPrefixUnaryExpression(node)) {
|
|
502
|
-
const operand = analyzeExpression(ctx, node.operand);
|
|
503
|
-
if (node.operator === ts.SyntaxKind.ExclamationToken) {
|
|
504
|
-
return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
|
|
505
|
-
}
|
|
506
|
-
if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
|
|
507
|
-
if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
|
|
508
|
-
return {
|
|
509
|
-
expressionId: ctx.ir.unary(
|
|
510
|
-
node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
|
|
511
|
-
operand.expressionId
|
|
512
|
-
),
|
|
513
|
-
shape: "primitive"
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
return capture(ctx, node, "unsupported_expression");
|
|
517
|
-
}
|
|
518
|
-
if (ts.isTypeOfExpression(node)) {
|
|
519
|
-
const operand = unwrapParens(node.expression);
|
|
520
|
-
if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
|
|
521
|
-
return capture(ctx, node, "external_state");
|
|
522
|
-
}
|
|
523
|
-
const inner = analyzeExpression(ctx, node.expression);
|
|
524
|
-
return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
|
|
525
|
-
}
|
|
526
|
-
return capture(ctx, node, "unsupported_expression");
|
|
527
|
-
};
|
|
528
|
-
var bindIdentifier = (ctx, name, result, declaration) => {
|
|
529
|
-
const current = ctx.scopes[ctx.scopes.length - 1];
|
|
530
|
-
current?.set(name.text, {
|
|
531
|
-
expressionId: result.expressionId,
|
|
532
|
-
shape: result.shape,
|
|
533
|
-
mutable: false
|
|
534
|
-
});
|
|
535
|
-
const position = loc(name, ctx.sourceFile);
|
|
536
|
-
const available = loc(declaration, ctx.sourceFile);
|
|
537
|
-
ctx.variables.push({
|
|
538
|
-
id: ctx.nextVariableId.value++,
|
|
539
|
-
name: name.text,
|
|
540
|
-
functionId: ctx.functionId,
|
|
541
|
-
scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
|
|
542
|
-
line: position.line,
|
|
543
|
-
column: position.column,
|
|
544
|
-
assignmentId: ctx.nextAssignmentId.value++,
|
|
545
|
-
expressionId: result.expressionId,
|
|
546
|
-
availableAtLine: available.line,
|
|
547
|
-
availableAtColumn: available.column
|
|
548
|
-
});
|
|
549
|
-
};
|
|
550
|
-
var analyzeStatement = (ctx, node) => {
|
|
551
|
-
if (ctx.fallback) return;
|
|
552
|
-
if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
|
|
553
|
-
if (ts.isBlock(node)) {
|
|
554
|
-
ctx.scopes.push(/* @__PURE__ */ new Map());
|
|
555
|
-
for (const statement of node.statements) analyzeStatement(ctx, statement);
|
|
556
|
-
ctx.scopes.pop();
|
|
557
|
-
return;
|
|
558
|
-
}
|
|
559
|
-
if (ts.isReturnStatement(node)) {
|
|
560
|
-
if (!node.expression) {
|
|
561
|
-
ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
if (ts.isVariableStatement(node)) {
|
|
568
|
-
if (node.declarationList.flags & ts.NodeFlags.Using) {
|
|
569
|
-
fallback(ctx, "side_effect");
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
for (const declaration of node.declarationList.declarations) {
|
|
573
|
-
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
|
|
574
|
-
fallback(ctx, "unsupported_expression");
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
const result = analyzeExpression(ctx, declaration.initializer);
|
|
578
|
-
bindIdentifier(ctx, declaration.name, result, declaration);
|
|
579
|
-
}
|
|
580
|
-
return;
|
|
581
|
-
}
|
|
582
|
-
if (ts.isExpressionStatement(node)) {
|
|
583
|
-
analyzeExpression(ctx, node.expression);
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
|
-
if (ts.isThrowStatement(node)) {
|
|
587
|
-
ctx.hasThrow = true;
|
|
588
|
-
analyzeExpression(ctx, node.expression);
|
|
589
|
-
return;
|
|
590
|
-
}
|
|
591
|
-
if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
|
|
592
|
-
fallback(ctx, "unsupported_expression");
|
|
593
|
-
return;
|
|
594
|
-
}
|
|
595
|
-
if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
|
|
596
|
-
fallback(ctx, "has_loop");
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
fallback(ctx, "unsupported_expression");
|
|
600
|
-
};
|
|
601
|
-
var bindParams = (ctx, node) => {
|
|
602
|
-
let index = 0;
|
|
603
|
-
for (const parameter of node.parameters) {
|
|
604
|
-
if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
605
|
-
if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
|
|
606
|
-
fallback(ctx, "unsupported_expression");
|
|
607
|
-
index++;
|
|
608
|
-
continue;
|
|
609
|
-
}
|
|
610
|
-
const expressionId = ctx.ir.param(index);
|
|
611
|
-
ctx.identifierParams.push(parameter.name.text);
|
|
612
|
-
bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
|
|
613
|
-
index++;
|
|
614
|
-
}
|
|
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;
|
|
615
135
|
};
|
|
616
|
-
var
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
guardWraps: [],
|
|
625
|
-
callEdges: [],
|
|
626
|
-
scopes: [/* @__PURE__ */ new Map()],
|
|
627
|
-
nextRootId: { value: 0 },
|
|
628
|
-
nextVariableId: { value: 0 },
|
|
629
|
-
nextAssignmentId: { value: 0 },
|
|
630
|
-
hasThrow: false,
|
|
631
|
-
hasCoercionGuard: false,
|
|
632
|
-
provenExact,
|
|
633
|
-
identifierParams: []
|
|
634
|
-
};
|
|
635
|
-
if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
|
|
636
|
-
ctx.fallback = "async";
|
|
637
|
-
} else {
|
|
638
|
-
bindParams(ctx, node);
|
|
639
|
-
if (ts.isBlock(node.body)) {
|
|
640
|
-
for (const statement of node.body.statements) analyzeStatement(ctx, statement);
|
|
641
|
-
} else {
|
|
642
|
-
ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
const plan = {
|
|
646
|
-
...emptyPlan(functionId),
|
|
647
|
-
expressions: ctx.ir.expressions,
|
|
648
|
-
variables: ctx.variables,
|
|
649
|
-
captureBoundaries: ctx.captureWraps.map((wrap) => ({
|
|
650
|
-
id: wrap.rootId,
|
|
651
|
-
reason: wrap.reason,
|
|
652
|
-
expressionId: wrap.expressionId,
|
|
653
|
-
line: wrap.line,
|
|
654
|
-
column: wrap.column
|
|
655
|
-
})),
|
|
656
|
-
controlFacts: [],
|
|
657
|
-
...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
|
|
658
|
-
...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
|
|
659
|
-
};
|
|
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`;
|
|
660
144
|
return {
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
node,
|
|
664
|
-
plan,
|
|
665
|
-
captureWraps: ctx.fallback ? [] : ctx.captureWraps,
|
|
666
|
-
guardWraps: ctx.fallback ? [] : ctx.guardWraps,
|
|
667
|
-
callEdges: ctx.callEdges,
|
|
668
|
-
hasThrow: ctx.hasThrow,
|
|
669
|
-
hasCoercionGuard: ctx.hasCoercionGuard
|
|
145
|
+
payload: readFileSync(mapPath, "utf8"),
|
|
146
|
+
mapUrl: pathToFileURL(mapPath).href
|
|
670
147
|
};
|
|
671
148
|
};
|
|
672
|
-
var
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
const firstPass = targets.map(
|
|
678
|
-
(target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
|
|
679
|
-
);
|
|
680
|
-
const graph = buildDependencyGraph(
|
|
681
|
-
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
682
|
-
firstPass.flatMap((fn) => fn.callEdges)
|
|
683
|
-
);
|
|
684
|
-
const order = topologicalFunctionOrder(
|
|
685
|
-
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
686
|
-
graph
|
|
687
|
-
);
|
|
688
|
-
const byId = new Map(targets.map((target) => [target.functionId, target]));
|
|
689
|
-
const provenExact = /* @__PURE__ */ new Map();
|
|
690
|
-
const analyzed = [];
|
|
691
|
-
for (const functionId of order) {
|
|
692
|
-
const target = byId.get(functionId);
|
|
693
|
-
if (!target) continue;
|
|
694
|
-
const result = analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact);
|
|
695
|
-
if (functionIsExact(result.plan) && nameCounts.get(result.name) === 1) {
|
|
696
|
-
provenExact.set(result.name, result.functionId);
|
|
697
|
-
}
|
|
698
|
-
analyzed.push(result);
|
|
699
|
-
}
|
|
700
|
-
const remaining = targets.filter(
|
|
701
|
-
(target) => !analyzed.some((fn) => fn.functionId === target.functionId)
|
|
702
|
-
);
|
|
703
|
-
for (const target of remaining) {
|
|
704
|
-
analyzed.push(analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact));
|
|
149
|
+
var sourceMapPayloadForCache = (generatedPath, source) => {
|
|
150
|
+
try {
|
|
151
|
+
return sourceMapFor(generatedPath, source)?.payload ?? "";
|
|
152
|
+
} catch {
|
|
153
|
+
return "";
|
|
705
154
|
}
|
|
706
|
-
return analyzed;
|
|
707
155
|
};
|
|
708
156
|
|
|
709
|
-
// src/
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
for (const wrap of wraps) {
|
|
714
|
-
edits.push({
|
|
715
|
-
position: wrap.start,
|
|
716
|
-
text: `${wrap.shorthandPropertyName ? `${wrap.shorthandPropertyName}:` : ""}${capName}(${wrap.rootId},`,
|
|
717
|
-
order: order++
|
|
718
|
-
});
|
|
719
|
-
edits.push({
|
|
720
|
-
position: wrap.end,
|
|
721
|
-
text: ")",
|
|
722
|
-
order: order++
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
return edits;
|
|
726
|
-
};
|
|
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";
|
|
727
161
|
|
|
728
162
|
// src/execution-recorder/automatic-runtime.ts
|
|
729
|
-
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.
|
|
163
|
+
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v3";
|
|
730
164
|
var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
|
|
731
165
|
|
|
732
166
|
// src/auto-instrumentation/collect-instrumentation-delta.ts
|
|
733
|
-
import
|
|
167
|
+
import ts from "typescript";
|
|
734
168
|
var paramBindingFromName = (name, sourceFile) => {
|
|
735
|
-
if (
|
|
736
|
-
if (
|
|
169
|
+
if (ts.isIdentifier(name)) return { kind: "identifier", name: name.text };
|
|
170
|
+
if (ts.isArrayBindingPattern(name)) {
|
|
737
171
|
const elements = name.elements.map(
|
|
738
172
|
(element) => {
|
|
739
|
-
if (
|
|
173
|
+
if (ts.isOmittedExpression(element)) return { kind: "omitted" };
|
|
740
174
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
741
175
|
if (element.dotDotDotToken) return { kind: "rest", binding };
|
|
742
176
|
return binding;
|
|
@@ -746,7 +180,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
746
180
|
}
|
|
747
181
|
const properties = name.elements.map((element) => {
|
|
748
182
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
749
|
-
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);
|
|
750
184
|
if (element.dotDotDotToken) {
|
|
751
185
|
const rest = { kind: "rest", binding };
|
|
752
186
|
return { key, binding: rest };
|
|
@@ -758,7 +192,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
758
192
|
var collectFunctionParams = (node, sourceFile) => {
|
|
759
193
|
const params = [];
|
|
760
194
|
for (const parameter of node.parameters) {
|
|
761
|
-
if (
|
|
195
|
+
if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
762
196
|
const binding = paramBindingFromName(parameter.name, sourceFile);
|
|
763
197
|
params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
|
|
764
198
|
}
|
|
@@ -799,18 +233,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
|
|
|
799
233
|
};
|
|
800
234
|
var unwrapExpression = (expression) => {
|
|
801
235
|
let current = expression;
|
|
802
|
-
while (
|
|
236
|
+
while (ts.isParenthesizedExpression(current) || ts.isNonNullExpression(current)) {
|
|
803
237
|
current = current.expression;
|
|
804
238
|
}
|
|
805
|
-
if (
|
|
239
|
+
if (ts.isAwaitExpression(current)) return unwrapExpression(current.expression);
|
|
806
240
|
return current;
|
|
807
241
|
};
|
|
808
242
|
var calleeFromExpression = (expression, sourceFile) => {
|
|
809
243
|
const current = unwrapExpression(expression);
|
|
810
|
-
if (
|
|
244
|
+
if (ts.isIdentifier(current)) {
|
|
811
245
|
return { calleeName: current.text, calleeText: current.text };
|
|
812
246
|
}
|
|
813
|
-
if (
|
|
247
|
+
if (ts.isPropertyAccessExpression(current) && ts.isIdentifier(current.name)) {
|
|
814
248
|
return {
|
|
815
249
|
calleeName: current.name.text,
|
|
816
250
|
calleeText: current.getText(sourceFile).replace(/\s+/g, "")
|
|
@@ -820,9 +254,9 @@ var calleeFromExpression = (expression, sourceFile) => {
|
|
|
820
254
|
};
|
|
821
255
|
var resultBindingForCall = (call) => {
|
|
822
256
|
let node = call.parent;
|
|
823
|
-
if (
|
|
824
|
-
if (!
|
|
825
|
-
if (!
|
|
257
|
+
if (ts.isAwaitExpression(node)) node = node.parent;
|
|
258
|
+
if (!ts.isVariableDeclaration(node)) return null;
|
|
259
|
+
if (!ts.isIdentifier(node.name)) return null;
|
|
826
260
|
const initializer = node.initializer;
|
|
827
261
|
if (!initializer) return null;
|
|
828
262
|
const inner = unwrapExpression(initializer);
|
|
@@ -832,18 +266,18 @@ var resultBindingForCall = (call) => {
|
|
|
832
266
|
var argIdentifiersForCall = (call) => {
|
|
833
267
|
const identifiers = [];
|
|
834
268
|
call.arguments?.forEach((argument, index) => {
|
|
835
|
-
if (
|
|
269
|
+
if (ts.isIdentifier(argument)) {
|
|
836
270
|
identifiers.push({ index, kind: "identifier", name: argument.text });
|
|
837
271
|
return;
|
|
838
272
|
}
|
|
839
|
-
if (!
|
|
273
|
+
if (!ts.isObjectLiteralExpression(argument)) return;
|
|
840
274
|
const properties = [];
|
|
841
275
|
for (const property of argument.properties) {
|
|
842
|
-
if (
|
|
276
|
+
if (ts.isShorthandPropertyAssignment(property)) {
|
|
843
277
|
properties.push({ key: property.name.text, name: property.name.text });
|
|
844
278
|
continue;
|
|
845
279
|
}
|
|
846
|
-
if (
|
|
280
|
+
if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && ts.isIdentifier(property.initializer)) {
|
|
847
281
|
properties.push({ key: property.name.text, name: property.initializer.text });
|
|
848
282
|
}
|
|
849
283
|
}
|
|
@@ -883,8 +317,8 @@ var collectCallSite = (call, caller, context) => {
|
|
|
883
317
|
};
|
|
884
318
|
};
|
|
885
319
|
var collectParamDestructure = (declaration, caller, context) => {
|
|
886
|
-
if (!declaration.initializer || !
|
|
887
|
-
if (!
|
|
320
|
+
if (!declaration.initializer || !ts.isIdentifier(declaration.initializer)) return null;
|
|
321
|
+
if (!ts.isObjectBindingPattern(declaration.name) && !ts.isArrayBindingPattern(declaration.name)) {
|
|
888
322
|
return null;
|
|
889
323
|
}
|
|
890
324
|
const sourceParam = declaration.initializer.text;
|
|
@@ -906,58 +340,483 @@ var collectParamDestructure = (declaration, caller, context) => {
|
|
|
906
340
|
};
|
|
907
341
|
};
|
|
908
342
|
|
|
909
|
-
// 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/;
|
|
910
505
|
var scriptKindFor = (filePath) => {
|
|
911
|
-
if (/\.tsx$/i.test(filePath)) return
|
|
912
|
-
if (/\.jsx$/i.test(filePath)) return
|
|
913
|
-
if (/\.[cm]?ts$/i.test(filePath)) return
|
|
914
|
-
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;
|
|
915
775
|
};
|
|
916
776
|
var functionName = (node, sourceFile) => {
|
|
777
|
+
if (ts6.isConstructorDeclaration(node)) return "constructor";
|
|
917
778
|
if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
|
|
918
779
|
const parent = node.parent;
|
|
919
|
-
if (
|
|
920
|
-
if (
|
|
921
|
-
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);
|
|
922
783
|
const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
923
784
|
return `anonymous@${location.line + 1}:${location.character + 1}`;
|
|
924
785
|
};
|
|
925
786
|
var functionLocationPosition = (node, sourceFile) => {
|
|
926
787
|
if (node.name) return node.name.getStart(sourceFile);
|
|
927
788
|
const parent = node.parent;
|
|
928
|
-
if ((
|
|
789
|
+
if ((ts6.isVariableDeclaration(parent) || ts6.isPropertyAssignment(parent)) && parent.name) {
|
|
929
790
|
return parent.name.getStart(sourceFile);
|
|
930
791
|
}
|
|
931
792
|
return node.getStart(sourceFile);
|
|
932
793
|
};
|
|
933
|
-
var
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
const value = bindingValue(element.name, sourceFile);
|
|
939
|
-
return element.dotDotDotToken ? `...${value}` : value;
|
|
940
|
-
});
|
|
941
|
-
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;
|
|
942
799
|
}
|
|
943
|
-
|
|
944
|
-
const value = bindingValue(element.name, sourceFile);
|
|
945
|
-
if (element.dotDotDotToken) return `...${value}`;
|
|
946
|
-
if (element.propertyName) return `${element.propertyName.getText(sourceFile)}:${value}`;
|
|
947
|
-
return value;
|
|
948
|
-
});
|
|
949
|
-
return `{${properties.join(",")}}`;
|
|
800
|
+
return end;
|
|
950
801
|
};
|
|
951
|
-
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
|
+
}
|
|
952
812
|
const values = node.parameters.flatMap((parameter) => {
|
|
953
|
-
if (
|
|
954
|
-
|
|
955
|
-
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];
|
|
956
815
|
});
|
|
957
|
-
return `[${values.join(",")}]
|
|
816
|
+
return { args: `[${values.join(",")}]`, omitValues: false };
|
|
958
817
|
};
|
|
959
818
|
var isSupportedFunction = (node) => {
|
|
960
|
-
if (!
|
|
819
|
+
if (!ts6.isFunctionDeclaration(node) && !ts6.isFunctionExpression(node) && !ts6.isArrowFunction(node) && !ts6.isMethodDeclaration(node) && !ts6.isGetAccessorDeclaration(node) && !ts6.isSetAccessorDeclaration(node)) {
|
|
961
820
|
return false;
|
|
962
821
|
}
|
|
963
822
|
if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
|
|
@@ -968,7 +827,7 @@ var uniqueName = (source, base) => {
|
|
|
968
827
|
while (source.includes(name)) name += "_";
|
|
969
828
|
return name;
|
|
970
829
|
};
|
|
971
|
-
var isDirectivePrologue = (statement) =>
|
|
830
|
+
var isDirectivePrologue = (statement) => ts6.isExpressionStatement(statement) && ts6.isStringLiteral(statement.expression);
|
|
972
831
|
var insertPositionAfterPrologue = (source, sourceFile) => {
|
|
973
832
|
const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
|
|
974
833
|
let end = shebangEnd;
|
|
@@ -983,7 +842,7 @@ var preambleInsert = (source, position, preamble) => {
|
|
|
983
842
|
const previous = source[position - 1];
|
|
984
843
|
return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
|
|
985
844
|
};
|
|
986
|
-
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)}));`;
|
|
987
846
|
var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
988
847
|
const start = mappedLocation(
|
|
989
848
|
sourceFile,
|
|
@@ -1011,40 +870,276 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
|
1011
870
|
endLine: end.line,
|
|
1012
871
|
endColumn: end.column,
|
|
1013
872
|
params,
|
|
1014
|
-
paramNames
|
|
873
|
+
paramNames,
|
|
874
|
+
instrumented: true
|
|
1015
875
|
};
|
|
1016
876
|
};
|
|
1017
|
-
var
|
|
1018
|
-
|
|
1019
|
-
|
|
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];
|
|
1020
887
|
}
|
|
1021
|
-
|
|
1022
|
-
(
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
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);
|
|
1026
1122
|
};
|
|
1027
1123
|
var transformSource = (source, options) => {
|
|
1028
1124
|
if (source.includes("@rasputin-ignore-file")) return void 0;
|
|
1029
|
-
|
|
1125
|
+
if (sourceIsRasputinInstrumented(source)) return alreadyInstrumentedResult(source);
|
|
1126
|
+
const sourceFile = ts6.createSourceFile(
|
|
1030
1127
|
options.filePath,
|
|
1031
1128
|
source,
|
|
1032
|
-
|
|
1129
|
+
ts6.ScriptTarget.Latest,
|
|
1033
1130
|
true,
|
|
1034
|
-
|
|
1131
|
+
scriptKindFor3(options.filePath)
|
|
1035
1132
|
);
|
|
1036
1133
|
const runName = uniqueName(source, "__rasputinRun__");
|
|
1037
|
-
const capName = uniqueName(source, "__rasputinCap__");
|
|
1038
1134
|
const plansName = uniqueName(source, "__rasputinPlans__");
|
|
1039
|
-
const
|
|
1040
|
-
const
|
|
1135
|
+
const handleName = uniqueName(source, "__rasputinH__");
|
|
1136
|
+
const errorName = uniqueName(source, "__rasputinE__");
|
|
1137
|
+
const absolutePath = resolve4(options.filePath);
|
|
1041
1138
|
const edits = [];
|
|
1042
|
-
let order = 0;
|
|
1043
1139
|
const delta = {
|
|
1044
1140
|
functions: [],
|
|
1045
1141
|
callSites: [],
|
|
1046
|
-
destructures: []
|
|
1047
|
-
derivationPlans: []
|
|
1142
|
+
destructures: []
|
|
1048
1143
|
};
|
|
1049
1144
|
const collectContext = {
|
|
1050
1145
|
sourceFile,
|
|
@@ -1052,75 +1147,134 @@ var transformSource = (source, options) => {
|
|
|
1052
1147
|
absolutePath
|
|
1053
1148
|
};
|
|
1054
1149
|
const pending = [];
|
|
1055
|
-
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
|
+
}
|
|
1056
1197
|
if (isSupportedFunction(node)) {
|
|
1057
1198
|
const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
|
|
1058
1199
|
if (!collected) {
|
|
1059
|
-
|
|
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));
|
|
1060
1209
|
return;
|
|
1061
1210
|
}
|
|
1062
|
-
|
|
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
|
+
});
|
|
1063
1220
|
delta.functions.push(collected);
|
|
1064
|
-
|
|
1221
|
+
ts6.forEachChild(node, (child) => visit(child, collected, depth + 1));
|
|
1065
1222
|
return;
|
|
1066
1223
|
}
|
|
1067
|
-
if (caller && (
|
|
1224
|
+
if (caller && (ts6.isCallExpression(node) || ts6.isNewExpression(node))) {
|
|
1068
1225
|
const site = collectCallSite(node, caller, collectContext);
|
|
1069
1226
|
if (site) delta.callSites.push(site);
|
|
1070
1227
|
}
|
|
1071
|
-
if (caller &&
|
|
1228
|
+
if (caller && ts6.isVariableDeclaration(node)) {
|
|
1072
1229
|
const destructure = collectParamDestructure(node, caller, collectContext);
|
|
1073
1230
|
if (destructure) delta.destructures.push(destructure);
|
|
1074
1231
|
}
|
|
1075
|
-
|
|
1232
|
+
ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
|
|
1076
1233
|
};
|
|
1077
|
-
visit(sourceFile);
|
|
1078
|
-
if (pending.length === 0)
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
pending.map((entry) => ({
|
|
1082
|
-
functionId: entry.collected.functionId,
|
|
1083
|
-
name: entry.collected.name,
|
|
1084
|
-
node: entry.node
|
|
1085
|
-
}))
|
|
1086
|
-
);
|
|
1087
|
-
delta.derivationPlans = analyzed.map((fn) => fn.plan);
|
|
1088
|
-
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
|
+
}
|
|
1089
1238
|
for (const [planIndex, entry] of pending.entries()) {
|
|
1090
|
-
const
|
|
1091
|
-
const
|
|
1092
|
-
const
|
|
1093
|
-
const
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
edits.push({
|
|
1110
|
-
position: entry.node.body.getStart(sourceFile),
|
|
1111
|
-
text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
|
|
1112
|
-
order: order++
|
|
1113
|
-
});
|
|
1114
|
-
edits.push({
|
|
1115
|
-
position: entry.node.body.end,
|
|
1116
|
-
text: `)${valueFlag})`,
|
|
1117
|
-
order: order++
|
|
1118
|
-
});
|
|
1119
|
-
}
|
|
1120
|
-
if (analysis) {
|
|
1121
|
-
for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
|
|
1122
|
-
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);
|
|
1123
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
|
+
);
|
|
1124
1278
|
}
|
|
1125
1279
|
}
|
|
1126
1280
|
const insertAt = insertPositionAfterPrologue(source, sourceFile);
|
|
@@ -1131,19 +1285,189 @@ var transformSource = (source, options) => {
|
|
|
1131
1285
|
insertAt,
|
|
1132
1286
|
modulePreamble(
|
|
1133
1287
|
runName,
|
|
1134
|
-
capName,
|
|
1135
1288
|
plansName,
|
|
1136
|
-
JSON.stringify(
|
|
1289
|
+
JSON.stringify(pending.map((entry) => entry.collected.plan))
|
|
1137
1290
|
)
|
|
1138
1291
|
),
|
|
1139
|
-
|
|
1292
|
+
deleteCount: 0,
|
|
1293
|
+
depth: -1,
|
|
1294
|
+
width: 0,
|
|
1295
|
+
kind: "replace"
|
|
1140
1296
|
});
|
|
1141
|
-
edits.sort(
|
|
1297
|
+
edits.sort(compareEdits);
|
|
1142
1298
|
let transformed = source;
|
|
1143
1299
|
for (const edit of edits) {
|
|
1144
|
-
|
|
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();
|
|
1145
1326
|
}
|
|
1146
|
-
|
|
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
|
+
}
|
|
1431
|
+
}
|
|
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;
|
|
1147
1471
|
};
|
|
1148
1472
|
|
|
1149
1473
|
// src/auto-instrumentation/bun-plugin.ts
|
|
@@ -1156,13 +1480,13 @@ var loaderFor = (filePath) => {
|
|
|
1156
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;
|
|
1157
1481
|
var isLoadedApplicationSource = (filePath, options = {}) => isApplicationSource(filePath, { ...options, includeBuildOutput: false });
|
|
1158
1482
|
var loadSourceForInstrumentation = async (filePath, options = {}) => {
|
|
1159
|
-
const absolutePath =
|
|
1483
|
+
const absolutePath = resolve5(filePath);
|
|
1160
1484
|
const source = await readFile(absolutePath, "utf8");
|
|
1161
1485
|
if (!isLoadedApplicationSource(absolutePath, options)) {
|
|
1162
1486
|
return { contents: source, loader: loaderFor(absolutePath), transformed: false };
|
|
1163
1487
|
}
|
|
1164
1488
|
try {
|
|
1165
|
-
const transformed =
|
|
1489
|
+
const transformed = transformSourceCached(source, { filePath: absolutePath });
|
|
1166
1490
|
if (transformed) {
|
|
1167
1491
|
recordInstrumentationManifestDelta(absolutePath, transformed.delta);
|
|
1168
1492
|
}
|
|
@@ -1171,7 +1495,11 @@ var loadSourceForInstrumentation = async (filePath, options = {}) => {
|
|
|
1171
1495
|
loader: loaderFor(absolutePath),
|
|
1172
1496
|
transformed: transformed !== void 0
|
|
1173
1497
|
};
|
|
1174
|
-
} 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
|
+
}
|
|
1175
1503
|
return { contents: source, loader: loaderFor(absolutePath), transformed: false };
|
|
1176
1504
|
}
|
|
1177
1505
|
};
|
|
@@ -1182,11 +1510,11 @@ var createRasputinBunPlugin = (options = {}) => {
|
|
|
1182
1510
|
setup(build) {
|
|
1183
1511
|
if (debug) {
|
|
1184
1512
|
console.info(
|
|
1185
|
-
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)"
|
|
1186
1514
|
);
|
|
1187
1515
|
}
|
|
1188
1516
|
build.onLoad({ filter: loadedSourceFilter }, async ({ path }) => {
|
|
1189
|
-
const absolutePath =
|
|
1517
|
+
const absolutePath = resolve5(path);
|
|
1190
1518
|
const result = await loadSourceForInstrumentation(absolutePath, options);
|
|
1191
1519
|
if (debug && result.transformed) {
|
|
1192
1520
|
console.info(`[rasputin] instrumented ${absolutePath}`);
|
|
@@ -1198,9 +1526,9 @@ var createRasputinBunPlugin = (options = {}) => {
|
|
|
1198
1526
|
};
|
|
1199
1527
|
|
|
1200
1528
|
// src/auto-instrumentation/sdk-source-boundary.ts
|
|
1201
|
-
import { dirname, resolve as
|
|
1202
|
-
import { fileURLToPath } from "node:url";
|
|
1203
|
-
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)), "../../..");
|
|
1204
1532
|
|
|
1205
1533
|
// src/auto-instrumentation/warn-production-runtime-transform.ts
|
|
1206
1534
|
var RUNTIME_TRANSFORM_WARNING_SUPPRESS_ENV = "RASPUTIN_SUPPRESS_RUNTIME_TRANSFORM_WARNING";
|
|
@@ -1211,7 +1539,7 @@ var warnIfProductionRuntimeTransform = () => {
|
|
|
1211
1539
|
if (Reflect.get(globalThis, warnedSymbol)) return;
|
|
1212
1540
|
Reflect.set(globalThis, warnedSymbol, true);
|
|
1213
1541
|
console.warn(
|
|
1214
|
-
"[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."
|
|
1215
1543
|
);
|
|
1216
1544
|
};
|
|
1217
1545
|
|