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