@rasputin-ai/node 0.5.0-alpha.1 → 0.5.0-alpha.10
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 +15 -15
- package/dist/auto-instrumentation/instrument-compiled-esm.d.ts +2 -0
- package/dist/auto-instrumentation/instrument-compiled-esm.d.ts.map +1 -1
- package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts +2 -1
- package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts.map +1 -1
- package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts +1 -1
- package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts.map +1 -1
- package/dist/auto-instrumentation/transform-source.d.ts +6 -0
- package/dist/auto-instrumentation/transform-source.d.ts.map +1 -1
- package/dist/auto-instrumentation/warn-production-runtime-transform.d.ts +8 -0
- package/dist/auto-instrumentation/warn-production-runtime-transform.d.ts.map +1 -0
- package/dist/derivation/analyze-function.d.ts +42 -0
- package/dist/derivation/analyze-function.d.ts.map +1 -0
- package/dist/derivation/build-dependency-graph.d.ts +21 -0
- package/dist/derivation/build-dependency-graph.d.ts.map +1 -0
- package/dist/derivation/derivation-ir.d.ts +33 -0
- package/dist/derivation/derivation-ir.d.ts.map +1 -0
- package/dist/derivation/derivation-proof.d.ts +14 -0
- package/dist/derivation/derivation-proof.d.ts.map +1 -0
- package/dist/derivation/emit-capture-plan.d.ts +8 -0
- package/dist/derivation/emit-capture-plan.d.ts.map +1 -0
- package/dist/execution-recorder/automatic-runtime.d.ts +12 -3
- package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
- package/dist/execution-recorder/execution-journal.d.ts +84 -0
- package/dist/execution-recorder/execution-journal.d.ts.map +1 -0
- package/dist/execution-recorder/execution-recorder-types.d.ts +21 -2
- package/dist/execution-recorder/execution-recorder-types.d.ts.map +1 -1
- package/dist/execution-recorder/execution-recorder.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/safe-serialize.d.ts +1 -0
- package/dist/execution-recorder/safe-serialize.d.ts.map +1 -1
- package/dist/index.js +1162 -328
- package/dist/instrument/build.js +795 -72
- package/dist/instrument/bun.js +790 -67
- package/dist/instrument/node.js +790 -67
- package/dist/sdk-meta.d.ts +1 -1
- package/dist/sdk-meta.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/execution-recorder/call-aware-event-buffer.d.ts +0 -24
- package/dist/execution-recorder/call-aware-event-buffer.d.ts.map +0 -1
package/dist/instrument/bun.js
CHANGED
|
@@ -34,7 +34,7 @@ var encodeFunctionPlan = (source) => {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
// src/auto-instrumentation/instrumentation-manifest-registry.ts
|
|
37
|
-
var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.
|
|
37
|
+
var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v3";
|
|
38
38
|
var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
|
|
39
39
|
var getRegistry = () => {
|
|
40
40
|
const existing = Reflect.get(globalThis, registrySymbol);
|
|
@@ -88,20 +88,663 @@ var isApplicationSource = (filePath, options = {}) => {
|
|
|
88
88
|
|
|
89
89
|
// src/auto-instrumentation/transform-source.ts
|
|
90
90
|
import { resolve as resolve3 } from "node:path";
|
|
91
|
-
import
|
|
91
|
+
import ts3 from "typescript";
|
|
92
|
+
|
|
93
|
+
// src/derivation/analyze-function.ts
|
|
94
|
+
import ts from "typescript";
|
|
95
|
+
|
|
96
|
+
// src/derivation/build-dependency-graph.ts
|
|
97
|
+
var buildDependencyGraph = (functions, edges) => {
|
|
98
|
+
const names = new Set(functions.map((fn) => fn.name));
|
|
99
|
+
const localEdges = edges.filter((edge) => names.has(edge.calleeName));
|
|
100
|
+
const calleesByCaller = /* @__PURE__ */ new Map();
|
|
101
|
+
for (const edge of localEdges) {
|
|
102
|
+
const existing = calleesByCaller.get(edge.callerFunctionId) ?? [];
|
|
103
|
+
existing.push(edge.calleeName);
|
|
104
|
+
calleesByCaller.set(edge.callerFunctionId, existing);
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
functions: functions.map((fn) => fn.functionId),
|
|
108
|
+
edges: localEdges,
|
|
109
|
+
calleesByCaller
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
var topologicalFunctionOrder = (functions, graph) => {
|
|
113
|
+
const idByName = new Map(functions.map((fn) => [fn.name, fn.functionId]));
|
|
114
|
+
const remainingCallees = new Map(
|
|
115
|
+
functions.map((fn) => {
|
|
116
|
+
const unique = new Set(
|
|
117
|
+
(graph.calleesByCaller.get(fn.functionId) ?? []).map((name) => idByName.get(name)).filter((id) => Boolean(id) && id !== fn.functionId)
|
|
118
|
+
);
|
|
119
|
+
return [fn.functionId, unique.size];
|
|
120
|
+
})
|
|
121
|
+
);
|
|
122
|
+
const callersOf = /* @__PURE__ */ new Map();
|
|
123
|
+
for (const fn of functions) {
|
|
124
|
+
for (const calleeName of graph.calleesByCaller.get(fn.functionId) ?? []) {
|
|
125
|
+
const calleeId = idByName.get(calleeName);
|
|
126
|
+
if (!calleeId || calleeId === fn.functionId) continue;
|
|
127
|
+
const callers = callersOf.get(calleeId) ?? [];
|
|
128
|
+
callers.push(fn.functionId);
|
|
129
|
+
callersOf.set(calleeId, callers);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const queue = functions.filter((fn) => (remainingCallees.get(fn.functionId) ?? 0) === 0).map((fn) => fn.functionId);
|
|
133
|
+
const ordered = [];
|
|
134
|
+
const queued = new Set(queue);
|
|
135
|
+
while (queue.length > 0) {
|
|
136
|
+
const current = queue.shift();
|
|
137
|
+
if (current === void 0) break;
|
|
138
|
+
ordered.push(current);
|
|
139
|
+
for (const caller of callersOf.get(current) ?? []) {
|
|
140
|
+
const next = (remainingCallees.get(caller) ?? 0) - 1;
|
|
141
|
+
remainingCallees.set(caller, next);
|
|
142
|
+
if (next === 0 && !queued.has(caller)) {
|
|
143
|
+
queued.add(caller);
|
|
144
|
+
queue.push(caller);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
for (const fn of functions) {
|
|
149
|
+
if (!queued.has(fn.functionId)) ordered.push(fn.functionId);
|
|
150
|
+
}
|
|
151
|
+
return ordered;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// src/derivation/derivation-ir.ts
|
|
155
|
+
var DerivationIrBuilder = class {
|
|
156
|
+
expressions = [];
|
|
157
|
+
nextId = 0;
|
|
158
|
+
constValue(value) {
|
|
159
|
+
return this.add({ id: this.nextId++, kind: "const", value });
|
|
160
|
+
}
|
|
161
|
+
param(index) {
|
|
162
|
+
return this.add({ id: this.nextId++, kind: "param", index });
|
|
163
|
+
}
|
|
164
|
+
root(rootId) {
|
|
165
|
+
return this.add({ id: this.nextId++, kind: "root", rootId });
|
|
166
|
+
}
|
|
167
|
+
alias(from) {
|
|
168
|
+
return this.add({ id: this.nextId++, kind: "alias", from });
|
|
169
|
+
}
|
|
170
|
+
binary(op, left, right, guard) {
|
|
171
|
+
return this.add({ id: this.nextId++, kind: "binary", op, left, right, guard });
|
|
172
|
+
}
|
|
173
|
+
unary(op, operand) {
|
|
174
|
+
return this.add({ id: this.nextId++, kind: "unary", op, operand });
|
|
175
|
+
}
|
|
176
|
+
logical(op, left, right) {
|
|
177
|
+
return this.add({ id: this.nextId++, kind: "logical", op, left, right });
|
|
178
|
+
}
|
|
179
|
+
cond(test, consequent, alternate) {
|
|
180
|
+
return this.add({ id: this.nextId++, kind: "cond", test, consequent, alternate });
|
|
181
|
+
}
|
|
182
|
+
object(properties) {
|
|
183
|
+
return this.add({ id: this.nextId++, kind: "object", properties });
|
|
184
|
+
}
|
|
185
|
+
array(elements) {
|
|
186
|
+
return this.add({ id: this.nextId++, kind: "array", elements });
|
|
187
|
+
}
|
|
188
|
+
prop(object, key) {
|
|
189
|
+
return this.add({ id: this.nextId++, kind: "prop", object, key });
|
|
190
|
+
}
|
|
191
|
+
index(array, index) {
|
|
192
|
+
return this.add({ id: this.nextId++, kind: "index", array, index });
|
|
193
|
+
}
|
|
194
|
+
pureCall(calleeFunctionId, args) {
|
|
195
|
+
return this.add({ id: this.nextId++, kind: "pure_call", calleeFunctionId, args });
|
|
196
|
+
}
|
|
197
|
+
template(parts) {
|
|
198
|
+
return this.add({ id: this.nextId++, kind: "template", parts });
|
|
199
|
+
}
|
|
200
|
+
add(expression) {
|
|
201
|
+
this.expressions.push(expression);
|
|
202
|
+
return expression.id;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
var emptyPlan = (functionId) => ({
|
|
206
|
+
planId: `dp_${functionId}`,
|
|
207
|
+
functionId,
|
|
208
|
+
expressions: [],
|
|
209
|
+
variables: [],
|
|
210
|
+
captureBoundaries: [],
|
|
211
|
+
controlFacts: []
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// src/derivation/analyze-function.ts
|
|
215
|
+
var loc = (node, sourceFile) => {
|
|
216
|
+
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
217
|
+
return { line: position.line + 1, column: position.character + 1 };
|
|
218
|
+
};
|
|
219
|
+
var lookup = (ctx, name) => {
|
|
220
|
+
for (let index = ctx.scopes.length - 1; index >= 0; index--) {
|
|
221
|
+
const binding = ctx.scopes[index]?.get(name);
|
|
222
|
+
if (binding) return binding;
|
|
223
|
+
}
|
|
224
|
+
return void 0;
|
|
225
|
+
};
|
|
226
|
+
var fallback = (ctx, reason) => {
|
|
227
|
+
ctx.fallback ??= reason;
|
|
228
|
+
return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "unknown" };
|
|
229
|
+
};
|
|
230
|
+
var capture = (ctx, node, reason) => {
|
|
231
|
+
const rootId = ctx.nextRootId.value++;
|
|
232
|
+
const position = loc(node, ctx.sourceFile);
|
|
233
|
+
const expressionId = ctx.ir.root(rootId);
|
|
234
|
+
ctx.captureWraps.push({
|
|
235
|
+
rootId,
|
|
236
|
+
reason,
|
|
237
|
+
start: node.getStart(ctx.sourceFile),
|
|
238
|
+
end: node.end,
|
|
239
|
+
line: position.line,
|
|
240
|
+
column: position.column,
|
|
241
|
+
expressionId
|
|
242
|
+
});
|
|
243
|
+
return { expressionId, shape: "unknown", captured: true };
|
|
244
|
+
};
|
|
245
|
+
var binaryOp = (kind) => {
|
|
246
|
+
switch (kind) {
|
|
247
|
+
case ts.SyntaxKind.PlusToken:
|
|
248
|
+
return "add";
|
|
249
|
+
case ts.SyntaxKind.MinusToken:
|
|
250
|
+
return "sub";
|
|
251
|
+
case ts.SyntaxKind.AsteriskToken:
|
|
252
|
+
return "mul";
|
|
253
|
+
case ts.SyntaxKind.SlashToken:
|
|
254
|
+
return "div";
|
|
255
|
+
case ts.SyntaxKind.PercentToken:
|
|
256
|
+
return "mod";
|
|
257
|
+
case ts.SyntaxKind.EqualsEqualsToken:
|
|
258
|
+
return "eq";
|
|
259
|
+
case ts.SyntaxKind.ExclamationEqualsToken:
|
|
260
|
+
return "neq";
|
|
261
|
+
case ts.SyntaxKind.EqualsEqualsEqualsToken:
|
|
262
|
+
return "seq";
|
|
263
|
+
case ts.SyntaxKind.ExclamationEqualsEqualsToken:
|
|
264
|
+
return "sneq";
|
|
265
|
+
case ts.SyntaxKind.LessThanToken:
|
|
266
|
+
return "lt";
|
|
267
|
+
case ts.SyntaxKind.LessThanEqualsToken:
|
|
268
|
+
return "lte";
|
|
269
|
+
case ts.SyntaxKind.GreaterThanToken:
|
|
270
|
+
return "gt";
|
|
271
|
+
case ts.SyntaxKind.GreaterThanEqualsToken:
|
|
272
|
+
return "gte";
|
|
273
|
+
default:
|
|
274
|
+
return void 0;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
var isNumericBinaryOp = (op) => op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod";
|
|
278
|
+
var unwrapParens = (node) => {
|
|
279
|
+
while (ts.isParenthesizedExpression(node)) node = node.expression;
|
|
280
|
+
return node;
|
|
281
|
+
};
|
|
282
|
+
var numericLiteralValue = (node) => {
|
|
283
|
+
const value = Number(node.text);
|
|
284
|
+
return { type: "number", value };
|
|
285
|
+
};
|
|
286
|
+
var analyzeExpression = (ctx, node) => {
|
|
287
|
+
if (ctx.fallback) return fallback(ctx, ctx.fallback);
|
|
288
|
+
if (ts.isParenthesizedExpression(node)) return analyzeExpression(ctx, node.expression);
|
|
289
|
+
if (ts.isAsExpression(node) || ts.isNonNullExpression(node)) {
|
|
290
|
+
return analyzeExpression(ctx, node.expression);
|
|
291
|
+
}
|
|
292
|
+
if (node.kind === ts.SyntaxKind.TypeAssertionExpression) {
|
|
293
|
+
return analyzeExpression(ctx, node.expression);
|
|
294
|
+
}
|
|
295
|
+
if (ts.isSatisfiesExpression(node)) return analyzeExpression(ctx, node.expression);
|
|
296
|
+
if (node.kind === ts.SyntaxKind.NullKeyword) {
|
|
297
|
+
return { expressionId: ctx.ir.constValue({ type: "null" }), shape: "primitive" };
|
|
298
|
+
}
|
|
299
|
+
if (node.kind === ts.SyntaxKind.TrueKeyword) {
|
|
300
|
+
return {
|
|
301
|
+
expressionId: ctx.ir.constValue({ type: "boolean", value: true }),
|
|
302
|
+
shape: "primitive"
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
if (node.kind === ts.SyntaxKind.FalseKeyword) {
|
|
306
|
+
return {
|
|
307
|
+
expressionId: ctx.ir.constValue({ type: "boolean", value: false }),
|
|
308
|
+
shape: "primitive"
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
if (ts.isNumericLiteral(node)) {
|
|
312
|
+
return { expressionId: ctx.ir.constValue(numericLiteralValue(node)), shape: "primitive" };
|
|
313
|
+
}
|
|
314
|
+
if (ts.isPrefixUnaryExpression(node) && ts.isNumericLiteral(node.operand) && node.operator === ts.SyntaxKind.MinusToken) {
|
|
315
|
+
const value = -Number(node.operand.text);
|
|
316
|
+
return {
|
|
317
|
+
expressionId: ctx.ir.constValue(
|
|
318
|
+
Object.is(value, -0) ? { type: "number", value: 0, negativeZero: true } : { type: "number", value }
|
|
319
|
+
),
|
|
320
|
+
shape: "primitive"
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
324
|
+
return {
|
|
325
|
+
expressionId: ctx.ir.constValue({ type: "string", value: node.text }),
|
|
326
|
+
shape: "primitive"
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
if (ts.isBigIntLiteral(node)) {
|
|
330
|
+
return {
|
|
331
|
+
expressionId: ctx.ir.constValue({ type: "bigint", value: node.text.replace(/n$/, "") }),
|
|
332
|
+
shape: "primitive"
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
if (ts.isIdentifier(node)) {
|
|
336
|
+
if (node.text === "undefined") {
|
|
337
|
+
return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "primitive" };
|
|
338
|
+
}
|
|
339
|
+
const binding = lookup(ctx, node.text);
|
|
340
|
+
if (!binding) return capture(ctx, node, "external_state");
|
|
341
|
+
if (binding.mutable) return capture(ctx, node, "mutable_object");
|
|
342
|
+
return {
|
|
343
|
+
expressionId: ctx.ir.alias(binding.expressionId),
|
|
344
|
+
shape: binding.shape
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
if (ts.isTemplateExpression(node)) {
|
|
348
|
+
const parts = [
|
|
349
|
+
{ kind: "lit", value: node.head.text }
|
|
350
|
+
];
|
|
351
|
+
for (const span of node.templateSpans) {
|
|
352
|
+
const nested = analyzeExpression(ctx, span.expression);
|
|
353
|
+
if (nested.shape !== "primitive" && !nested.captured) {
|
|
354
|
+
return capture(ctx, node, "unsupported_expression");
|
|
355
|
+
}
|
|
356
|
+
parts.push({ kind: "expr", expressionId: nested.expressionId });
|
|
357
|
+
parts.push({ kind: "lit", value: span.literal.text });
|
|
358
|
+
}
|
|
359
|
+
return { expressionId: ctx.ir.template(parts), shape: "primitive" };
|
|
360
|
+
}
|
|
361
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
362
|
+
if (node.elements.some((element) => ts.isSpreadElement(element))) {
|
|
363
|
+
return capture(ctx, node, "unsupported_expression");
|
|
364
|
+
}
|
|
365
|
+
const elements = node.elements.map((element) => {
|
|
366
|
+
if (ts.isOmittedExpression(element)) return { kind: "hole" };
|
|
367
|
+
return analyzeExpression(ctx, element).expressionId;
|
|
368
|
+
});
|
|
369
|
+
return { expressionId: ctx.ir.array(elements), shape: "array" };
|
|
370
|
+
}
|
|
371
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
372
|
+
const properties = [];
|
|
373
|
+
for (const property of node.properties) {
|
|
374
|
+
if (ts.isShorthandPropertyAssignment(property)) {
|
|
375
|
+
const nested = analyzeExpression(ctx, property.name);
|
|
376
|
+
if (nested.captured) {
|
|
377
|
+
const wrap = ctx.captureWraps.find(
|
|
378
|
+
(candidate) => candidate.expressionId === nested.expressionId
|
|
379
|
+
);
|
|
380
|
+
if (wrap) wrap.shorthandPropertyName = property.name.text;
|
|
381
|
+
}
|
|
382
|
+
properties.push({ key: property.name.text, value: nested.expressionId });
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
if (!ts.isPropertyAssignment(property)) {
|
|
386
|
+
return capture(ctx, node, "unsupported_expression");
|
|
387
|
+
}
|
|
388
|
+
if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
|
|
389
|
+
return capture(ctx, node, "unsupported_expression");
|
|
390
|
+
}
|
|
391
|
+
const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
|
|
392
|
+
if (key === void 0) return capture(ctx, node, "unsupported_expression");
|
|
393
|
+
properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
|
|
394
|
+
}
|
|
395
|
+
return { expressionId: ctx.ir.object(properties), shape: "object" };
|
|
396
|
+
}
|
|
397
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
398
|
+
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
399
|
+
const object = analyzeExpression(ctx, node.expression);
|
|
400
|
+
if (object.shape === "object" && ts.isIdentifier(node.name)) {
|
|
401
|
+
return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
|
|
402
|
+
}
|
|
403
|
+
return capture(ctx, node, "unknown_property");
|
|
404
|
+
}
|
|
405
|
+
if (ts.isElementAccessExpression(node)) {
|
|
406
|
+
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
407
|
+
const array = analyzeExpression(ctx, node.expression);
|
|
408
|
+
if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
|
|
409
|
+
return {
|
|
410
|
+
expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
|
|
411
|
+
shape: "unknown"
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
return capture(ctx, node, "unknown_property");
|
|
415
|
+
}
|
|
416
|
+
if (ts.isCallExpression(node)) {
|
|
417
|
+
if (node.questionDotToken || node.typeArguments?.length) {
|
|
418
|
+
return capture(ctx, node, "unsupported_expression");
|
|
419
|
+
}
|
|
420
|
+
if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
|
|
421
|
+
const calleeName = node.expression.text;
|
|
422
|
+
if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
|
|
423
|
+
const provenId = ctx.moduleCallees.get(calleeName);
|
|
424
|
+
ctx.callEdges.push({
|
|
425
|
+
callerFunctionId: ctx.functionId,
|
|
426
|
+
calleeName,
|
|
427
|
+
...provenId ? { calleeFunctionId: provenId } : {}
|
|
428
|
+
});
|
|
429
|
+
if (!provenId) return capture(ctx, node, "opaque_call");
|
|
430
|
+
const args = node.arguments.map((argument) => {
|
|
431
|
+
if (ts.isSpreadElement(argument)) return void 0;
|
|
432
|
+
return analyzeExpression(ctx, argument).expressionId;
|
|
433
|
+
});
|
|
434
|
+
if (args.some((argument) => argument === void 0))
|
|
435
|
+
return capture(ctx, node, "unsupported_expression");
|
|
436
|
+
return {
|
|
437
|
+
expressionId: ctx.ir.pureCall(
|
|
438
|
+
provenId,
|
|
439
|
+
args.filter((id) => id !== void 0)
|
|
440
|
+
),
|
|
441
|
+
shape: "unknown"
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
|
|
445
|
+
return capture(
|
|
446
|
+
ctx,
|
|
447
|
+
node,
|
|
448
|
+
node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
if (ts.isConditionalExpression(node)) {
|
|
452
|
+
const test = analyzeExpression(ctx, node.condition);
|
|
453
|
+
const consequent = analyzeExpression(ctx, node.whenTrue);
|
|
454
|
+
const alternate = analyzeExpression(ctx, node.whenFalse);
|
|
455
|
+
return {
|
|
456
|
+
expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
|
|
457
|
+
shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
if (ts.isBinaryExpression(node)) {
|
|
461
|
+
if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
462
|
+
ctx.fallback ??= "mutable_state";
|
|
463
|
+
return capture(ctx, node, "mutable_object");
|
|
464
|
+
}
|
|
465
|
+
if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
466
|
+
const left2 = analyzeExpression(ctx, node.left);
|
|
467
|
+
const right2 = analyzeExpression(ctx, node.right);
|
|
468
|
+
const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
|
|
469
|
+
return {
|
|
470
|
+
expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
|
|
471
|
+
shape: "unknown"
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
const op = binaryOp(node.operatorToken.kind);
|
|
475
|
+
if (!op) return capture(ctx, node, "unsupported_expression");
|
|
476
|
+
if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
|
|
477
|
+
const left = analyzeExpression(ctx, node.left);
|
|
478
|
+
const right = analyzeExpression(ctx, node.right);
|
|
479
|
+
const numeric = isNumericBinaryOp(op);
|
|
480
|
+
const expressionId = ctx.ir.binary(
|
|
481
|
+
op,
|
|
482
|
+
left.expressionId,
|
|
483
|
+
right.expressionId,
|
|
484
|
+
numeric ? "numeric" : "primitive"
|
|
485
|
+
);
|
|
486
|
+
if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
|
|
487
|
+
ctx.hasCoercionGuard = true;
|
|
488
|
+
const fallbackRootId = ctx.nextRootId.value++;
|
|
489
|
+
ctx.guardWraps.push({
|
|
490
|
+
fallbackRootId,
|
|
491
|
+
start: node.getStart(ctx.sourceFile),
|
|
492
|
+
end: node.end,
|
|
493
|
+
op
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
return { expressionId, shape: "primitive" };
|
|
497
|
+
}
|
|
498
|
+
if (ts.isPrefixUnaryExpression(node)) {
|
|
499
|
+
const operand = analyzeExpression(ctx, node.operand);
|
|
500
|
+
if (node.operator === ts.SyntaxKind.ExclamationToken) {
|
|
501
|
+
return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
|
|
502
|
+
}
|
|
503
|
+
if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
|
|
504
|
+
if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
|
|
505
|
+
return {
|
|
506
|
+
expressionId: ctx.ir.unary(
|
|
507
|
+
node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
|
|
508
|
+
operand.expressionId
|
|
509
|
+
),
|
|
510
|
+
shape: "primitive"
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
return capture(ctx, node, "unsupported_expression");
|
|
514
|
+
}
|
|
515
|
+
if (ts.isTypeOfExpression(node)) {
|
|
516
|
+
const operand = unwrapParens(node.expression);
|
|
517
|
+
if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
|
|
518
|
+
return capture(ctx, node, "external_state");
|
|
519
|
+
}
|
|
520
|
+
const inner = analyzeExpression(ctx, node.expression);
|
|
521
|
+
return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
|
|
522
|
+
}
|
|
523
|
+
return capture(ctx, node, "unsupported_expression");
|
|
524
|
+
};
|
|
525
|
+
var bindIdentifier = (ctx, name, result, declaration) => {
|
|
526
|
+
const current = ctx.scopes[ctx.scopes.length - 1];
|
|
527
|
+
current?.set(name.text, {
|
|
528
|
+
expressionId: result.expressionId,
|
|
529
|
+
shape: result.shape,
|
|
530
|
+
mutable: false
|
|
531
|
+
});
|
|
532
|
+
const position = loc(name, ctx.sourceFile);
|
|
533
|
+
const available = loc(declaration, ctx.sourceFile);
|
|
534
|
+
ctx.variables.push({
|
|
535
|
+
id: ctx.nextVariableId.value++,
|
|
536
|
+
name: name.text,
|
|
537
|
+
functionId: ctx.functionId,
|
|
538
|
+
scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
|
|
539
|
+
line: position.line,
|
|
540
|
+
column: position.column,
|
|
541
|
+
assignmentId: ctx.nextAssignmentId.value++,
|
|
542
|
+
expressionId: result.expressionId,
|
|
543
|
+
availableAtLine: available.line,
|
|
544
|
+
availableAtColumn: available.column
|
|
545
|
+
});
|
|
546
|
+
};
|
|
547
|
+
var analyzeStatement = (ctx, node) => {
|
|
548
|
+
if (ctx.fallback) return;
|
|
549
|
+
if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
|
|
550
|
+
if (ts.isBlock(node)) {
|
|
551
|
+
ctx.scopes.push(/* @__PURE__ */ new Map());
|
|
552
|
+
for (const statement of node.statements) analyzeStatement(ctx, statement);
|
|
553
|
+
ctx.scopes.pop();
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
if (ts.isReturnStatement(node)) {
|
|
557
|
+
if (!node.expression) {
|
|
558
|
+
ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
if (ts.isVariableStatement(node)) {
|
|
565
|
+
if (node.declarationList.flags & ts.NodeFlags.Using) {
|
|
566
|
+
fallback(ctx, "side_effect");
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
for (const declaration of node.declarationList.declarations) {
|
|
570
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
|
|
571
|
+
fallback(ctx, "unsupported_expression");
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
const result = analyzeExpression(ctx, declaration.initializer);
|
|
575
|
+
bindIdentifier(ctx, declaration.name, result, declaration);
|
|
576
|
+
}
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (ts.isExpressionStatement(node)) {
|
|
580
|
+
analyzeExpression(ctx, node.expression);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
if (ts.isThrowStatement(node)) {
|
|
584
|
+
ctx.hasThrow = true;
|
|
585
|
+
analyzeExpression(ctx, node.expression);
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
|
|
589
|
+
fallback(ctx, "unsupported_expression");
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
|
|
593
|
+
fallback(ctx, "has_loop");
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
fallback(ctx, "unsupported_expression");
|
|
597
|
+
};
|
|
598
|
+
var bindParams = (ctx, node) => {
|
|
599
|
+
let index = 0;
|
|
600
|
+
for (const parameter of node.parameters) {
|
|
601
|
+
if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
602
|
+
if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
|
|
603
|
+
fallback(ctx, "unsupported_expression");
|
|
604
|
+
index++;
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
const expressionId = ctx.ir.param(index);
|
|
608
|
+
ctx.identifierParams.push(parameter.name.text);
|
|
609
|
+
bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
|
|
610
|
+
index++;
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
var analyzeOne = (node, functionId, name, sourceFile, moduleCallees) => {
|
|
614
|
+
const ctx = {
|
|
615
|
+
sourceFile,
|
|
616
|
+
functionId,
|
|
617
|
+
name,
|
|
618
|
+
ir: new DerivationIrBuilder(),
|
|
619
|
+
variables: [],
|
|
620
|
+
captureWraps: [],
|
|
621
|
+
guardWraps: [],
|
|
622
|
+
callEdges: [],
|
|
623
|
+
scopes: [/* @__PURE__ */ new Map()],
|
|
624
|
+
nextRootId: { value: 0 },
|
|
625
|
+
nextVariableId: { value: 0 },
|
|
626
|
+
nextAssignmentId: { value: 0 },
|
|
627
|
+
hasThrow: false,
|
|
628
|
+
hasCoercionGuard: false,
|
|
629
|
+
moduleCallees,
|
|
630
|
+
identifierParams: []
|
|
631
|
+
};
|
|
632
|
+
if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
|
|
633
|
+
ctx.fallback = "async";
|
|
634
|
+
} else {
|
|
635
|
+
bindParams(ctx, node);
|
|
636
|
+
if (ts.isBlock(node.body)) {
|
|
637
|
+
for (const statement of node.body.statements) analyzeStatement(ctx, statement);
|
|
638
|
+
} else {
|
|
639
|
+
ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
const plan = {
|
|
643
|
+
...emptyPlan(functionId),
|
|
644
|
+
expressions: ctx.ir.expressions,
|
|
645
|
+
variables: ctx.variables,
|
|
646
|
+
captureBoundaries: ctx.captureWraps.map((wrap) => ({
|
|
647
|
+
id: wrap.rootId,
|
|
648
|
+
reason: wrap.reason,
|
|
649
|
+
expressionId: wrap.expressionId,
|
|
650
|
+
line: wrap.line,
|
|
651
|
+
column: wrap.column
|
|
652
|
+
})),
|
|
653
|
+
controlFacts: [],
|
|
654
|
+
...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
|
|
655
|
+
...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
|
|
656
|
+
};
|
|
657
|
+
return {
|
|
658
|
+
functionId,
|
|
659
|
+
name,
|
|
660
|
+
node,
|
|
661
|
+
plan,
|
|
662
|
+
captureWraps: ctx.fallback ? [] : ctx.captureWraps,
|
|
663
|
+
guardWraps: ctx.fallback ? [] : ctx.guardWraps,
|
|
664
|
+
callEdges: ctx.callEdges,
|
|
665
|
+
hasThrow: ctx.hasThrow,
|
|
666
|
+
hasCoercionGuard: ctx.hasCoercionGuard
|
|
667
|
+
};
|
|
668
|
+
};
|
|
669
|
+
var analyzeModuleFunctions = (sourceFile, targets) => {
|
|
670
|
+
const nameCounts = /* @__PURE__ */ new Map();
|
|
671
|
+
for (const target of targets) {
|
|
672
|
+
nameCounts.set(target.name, (nameCounts.get(target.name) ?? 0) + 1);
|
|
673
|
+
}
|
|
674
|
+
const firstPass = targets.map(
|
|
675
|
+
(target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
|
|
676
|
+
);
|
|
677
|
+
const graph = buildDependencyGraph(
|
|
678
|
+
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
679
|
+
firstPass.flatMap((fn) => fn.callEdges)
|
|
680
|
+
);
|
|
681
|
+
const order = topologicalFunctionOrder(
|
|
682
|
+
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
683
|
+
graph
|
|
684
|
+
);
|
|
685
|
+
const byId = new Map(targets.map((target) => [target.functionId, target]));
|
|
686
|
+
const moduleCallees = /* @__PURE__ */ new Map();
|
|
687
|
+
const analyzed = [];
|
|
688
|
+
for (const functionId of order) {
|
|
689
|
+
const target = byId.get(functionId);
|
|
690
|
+
if (!target) continue;
|
|
691
|
+
const result = analyzeOne(
|
|
692
|
+
target.node,
|
|
693
|
+
target.functionId,
|
|
694
|
+
target.name,
|
|
695
|
+
sourceFile,
|
|
696
|
+
moduleCallees
|
|
697
|
+
);
|
|
698
|
+
if (nameCounts.get(result.name) === 1) {
|
|
699
|
+
moduleCallees.set(result.name, result.functionId);
|
|
700
|
+
}
|
|
701
|
+
analyzed.push(result);
|
|
702
|
+
}
|
|
703
|
+
const remaining = targets.filter(
|
|
704
|
+
(target) => !analyzed.some((fn) => fn.functionId === target.functionId)
|
|
705
|
+
);
|
|
706
|
+
for (const target of remaining) {
|
|
707
|
+
analyzed.push(
|
|
708
|
+
analyzeOne(target.node, target.functionId, target.name, sourceFile, moduleCallees)
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
return analyzed;
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
// src/derivation/derivation-proof.ts
|
|
715
|
+
var functionIsExact = (plan) => plan.fallbackReason === void 0 && plan.captureBoundaries.length === 0 && plan.controlFacts.length === 0;
|
|
716
|
+
|
|
717
|
+
// src/derivation/emit-capture-plan.ts
|
|
718
|
+
var captureWrapEdits = (wraps, capName) => {
|
|
719
|
+
const edits = [];
|
|
720
|
+
let order = 0;
|
|
721
|
+
for (const wrap of wraps) {
|
|
722
|
+
edits.push({
|
|
723
|
+
position: wrap.start,
|
|
724
|
+
text: `${wrap.shorthandPropertyName ? `${wrap.shorthandPropertyName}:` : ""}${capName}(${wrap.rootId},`,
|
|
725
|
+
order: order++
|
|
726
|
+
});
|
|
727
|
+
edits.push({
|
|
728
|
+
position: wrap.end,
|
|
729
|
+
text: ")",
|
|
730
|
+
order: order++
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
return edits;
|
|
734
|
+
};
|
|
92
735
|
|
|
93
736
|
// src/execution-recorder/automatic-runtime.ts
|
|
94
737
|
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
|
|
95
738
|
var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
|
|
96
739
|
|
|
97
740
|
// src/auto-instrumentation/collect-instrumentation-delta.ts
|
|
98
|
-
import
|
|
741
|
+
import ts2 from "typescript";
|
|
99
742
|
var paramBindingFromName = (name, sourceFile) => {
|
|
100
|
-
if (
|
|
101
|
-
if (
|
|
743
|
+
if (ts2.isIdentifier(name)) return { kind: "identifier", name: name.text };
|
|
744
|
+
if (ts2.isArrayBindingPattern(name)) {
|
|
102
745
|
const elements = name.elements.map(
|
|
103
746
|
(element) => {
|
|
104
|
-
if (
|
|
747
|
+
if (ts2.isOmittedExpression(element)) return { kind: "omitted" };
|
|
105
748
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
106
749
|
if (element.dotDotDotToken) return { kind: "rest", binding };
|
|
107
750
|
return binding;
|
|
@@ -111,7 +754,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
111
754
|
}
|
|
112
755
|
const properties = name.elements.map((element) => {
|
|
113
756
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
114
|
-
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") :
|
|
757
|
+
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts2.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
|
|
115
758
|
if (element.dotDotDotToken) {
|
|
116
759
|
const rest = { kind: "rest", binding };
|
|
117
760
|
return { key, binding: rest };
|
|
@@ -123,7 +766,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
123
766
|
var collectFunctionParams = (node, sourceFile) => {
|
|
124
767
|
const params = [];
|
|
125
768
|
for (const parameter of node.parameters) {
|
|
126
|
-
if (
|
|
769
|
+
if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
127
770
|
const binding = paramBindingFromName(parameter.name, sourceFile);
|
|
128
771
|
params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
|
|
129
772
|
}
|
|
@@ -164,18 +807,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
|
|
|
164
807
|
};
|
|
165
808
|
var unwrapExpression = (expression) => {
|
|
166
809
|
let current = expression;
|
|
167
|
-
while (
|
|
810
|
+
while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
|
|
168
811
|
current = current.expression;
|
|
169
812
|
}
|
|
170
|
-
if (
|
|
813
|
+
if (ts2.isAwaitExpression(current)) return unwrapExpression(current.expression);
|
|
171
814
|
return current;
|
|
172
815
|
};
|
|
173
816
|
var calleeFromExpression = (expression, sourceFile) => {
|
|
174
817
|
const current = unwrapExpression(expression);
|
|
175
|
-
if (
|
|
818
|
+
if (ts2.isIdentifier(current)) {
|
|
176
819
|
return { calleeName: current.text, calleeText: current.text };
|
|
177
820
|
}
|
|
178
|
-
if (
|
|
821
|
+
if (ts2.isPropertyAccessExpression(current) && ts2.isIdentifier(current.name)) {
|
|
179
822
|
return {
|
|
180
823
|
calleeName: current.name.text,
|
|
181
824
|
calleeText: current.getText(sourceFile).replace(/\s+/g, "")
|
|
@@ -185,9 +828,9 @@ var calleeFromExpression = (expression, sourceFile) => {
|
|
|
185
828
|
};
|
|
186
829
|
var resultBindingForCall = (call) => {
|
|
187
830
|
let node = call.parent;
|
|
188
|
-
if (
|
|
189
|
-
if (!
|
|
190
|
-
if (!
|
|
831
|
+
if (ts2.isAwaitExpression(node)) node = node.parent;
|
|
832
|
+
if (!ts2.isVariableDeclaration(node)) return null;
|
|
833
|
+
if (!ts2.isIdentifier(node.name)) return null;
|
|
191
834
|
const initializer = node.initializer;
|
|
192
835
|
if (!initializer) return null;
|
|
193
836
|
const inner = unwrapExpression(initializer);
|
|
@@ -197,18 +840,18 @@ var resultBindingForCall = (call) => {
|
|
|
197
840
|
var argIdentifiersForCall = (call) => {
|
|
198
841
|
const identifiers = [];
|
|
199
842
|
call.arguments?.forEach((argument, index) => {
|
|
200
|
-
if (
|
|
843
|
+
if (ts2.isIdentifier(argument)) {
|
|
201
844
|
identifiers.push({ index, kind: "identifier", name: argument.text });
|
|
202
845
|
return;
|
|
203
846
|
}
|
|
204
|
-
if (!
|
|
847
|
+
if (!ts2.isObjectLiteralExpression(argument)) return;
|
|
205
848
|
const properties = [];
|
|
206
849
|
for (const property of argument.properties) {
|
|
207
|
-
if (
|
|
850
|
+
if (ts2.isShorthandPropertyAssignment(property)) {
|
|
208
851
|
properties.push({ key: property.name.text, name: property.name.text });
|
|
209
852
|
continue;
|
|
210
853
|
}
|
|
211
|
-
if (
|
|
854
|
+
if (ts2.isPropertyAssignment(property) && ts2.isIdentifier(property.name) && ts2.isIdentifier(property.initializer)) {
|
|
212
855
|
properties.push({ key: property.name.text, name: property.initializer.text });
|
|
213
856
|
}
|
|
214
857
|
}
|
|
@@ -248,8 +891,8 @@ var collectCallSite = (call, caller, context) => {
|
|
|
248
891
|
};
|
|
249
892
|
};
|
|
250
893
|
var collectParamDestructure = (declaration, caller, context) => {
|
|
251
|
-
if (!declaration.initializer || !
|
|
252
|
-
if (!
|
|
894
|
+
if (!declaration.initializer || !ts2.isIdentifier(declaration.initializer)) return null;
|
|
895
|
+
if (!ts2.isObjectBindingPattern(declaration.name) && !ts2.isArrayBindingPattern(declaration.name)) {
|
|
253
896
|
return null;
|
|
254
897
|
}
|
|
255
898
|
const sourceParam = declaration.initializer.text;
|
|
@@ -273,33 +916,33 @@ var collectParamDestructure = (declaration, caller, context) => {
|
|
|
273
916
|
|
|
274
917
|
// src/auto-instrumentation/transform-source.ts
|
|
275
918
|
var scriptKindFor = (filePath) => {
|
|
276
|
-
if (/\.tsx$/i.test(filePath)) return
|
|
277
|
-
if (/\.jsx$/i.test(filePath)) return
|
|
278
|
-
if (/\.[cm]?ts$/i.test(filePath)) return
|
|
279
|
-
return
|
|
919
|
+
if (/\.tsx$/i.test(filePath)) return ts3.ScriptKind.TSX;
|
|
920
|
+
if (/\.jsx$/i.test(filePath)) return ts3.ScriptKind.JSX;
|
|
921
|
+
if (/\.[cm]?ts$/i.test(filePath)) return ts3.ScriptKind.TS;
|
|
922
|
+
return ts3.ScriptKind.JS;
|
|
280
923
|
};
|
|
281
924
|
var functionName = (node, sourceFile) => {
|
|
282
925
|
if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
|
|
283
926
|
const parent = node.parent;
|
|
284
|
-
if (
|
|
285
|
-
if (
|
|
286
|
-
if (
|
|
927
|
+
if (ts3.isVariableDeclaration(parent) && ts3.isIdentifier(parent.name)) return parent.name.text;
|
|
928
|
+
if (ts3.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
|
|
929
|
+
if (ts3.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
|
|
287
930
|
const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
288
931
|
return `anonymous@${location.line + 1}:${location.character + 1}`;
|
|
289
932
|
};
|
|
290
933
|
var functionLocationPosition = (node, sourceFile) => {
|
|
291
934
|
if (node.name) return node.name.getStart(sourceFile);
|
|
292
935
|
const parent = node.parent;
|
|
293
|
-
if ((
|
|
936
|
+
if ((ts3.isVariableDeclaration(parent) || ts3.isPropertyAssignment(parent)) && parent.name) {
|
|
294
937
|
return parent.name.getStart(sourceFile);
|
|
295
938
|
}
|
|
296
939
|
return node.getStart(sourceFile);
|
|
297
940
|
};
|
|
298
941
|
var bindingValue = (name, sourceFile) => {
|
|
299
|
-
if (
|
|
300
|
-
if (
|
|
942
|
+
if (ts3.isIdentifier(name)) return name.text;
|
|
943
|
+
if (ts3.isArrayBindingPattern(name)) {
|
|
301
944
|
const values = name.elements.map((element) => {
|
|
302
|
-
if (
|
|
945
|
+
if (ts3.isOmittedExpression(element)) return "";
|
|
303
946
|
const value = bindingValue(element.name, sourceFile);
|
|
304
947
|
return element.dotDotDotToken ? `...${value}` : value;
|
|
305
948
|
});
|
|
@@ -315,14 +958,14 @@ var bindingValue = (name, sourceFile) => {
|
|
|
315
958
|
};
|
|
316
959
|
var arrowArguments = (node) => {
|
|
317
960
|
const values = node.parameters.flatMap((parameter) => {
|
|
318
|
-
if (
|
|
961
|
+
if (ts3.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
|
|
319
962
|
const value = bindingValue(parameter.name, node.getSourceFile());
|
|
320
963
|
return [parameter.dotDotDotToken ? `...${value}` : value];
|
|
321
964
|
});
|
|
322
965
|
return `[${values.join(",")}]`;
|
|
323
966
|
};
|
|
324
967
|
var isSupportedFunction = (node) => {
|
|
325
|
-
if (!
|
|
968
|
+
if (!ts3.isFunctionDeclaration(node) && !ts3.isFunctionExpression(node) && !ts3.isArrowFunction(node) && !ts3.isMethodDeclaration(node) && !ts3.isGetAccessorDeclaration(node) && !ts3.isSetAccessorDeclaration(node)) {
|
|
326
969
|
return false;
|
|
327
970
|
}
|
|
328
971
|
if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
|
|
@@ -333,7 +976,22 @@ var uniqueName = (source, base) => {
|
|
|
333
976
|
while (source.includes(name)) name += "_";
|
|
334
977
|
return name;
|
|
335
978
|
};
|
|
336
|
-
var
|
|
979
|
+
var isDirectivePrologue = (statement) => ts3.isExpressionStatement(statement) && ts3.isStringLiteral(statement.expression);
|
|
980
|
+
var insertPositionAfterPrologue = (source, sourceFile) => {
|
|
981
|
+
const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
|
|
982
|
+
let end = shebangEnd;
|
|
983
|
+
for (const statement of sourceFile.statements) {
|
|
984
|
+
if (!isDirectivePrologue(statement)) break;
|
|
985
|
+
end = statement.end;
|
|
986
|
+
}
|
|
987
|
+
return end;
|
|
988
|
+
};
|
|
989
|
+
var preambleInsert = (source, position, preamble) => {
|
|
990
|
+
if (position <= 0) return preamble;
|
|
991
|
+
const previous = source[position - 1];
|
|
992
|
+
return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
|
|
993
|
+
};
|
|
994
|
+
var modulePreamble = (runName, capName, plansName, plansJson) => `const ${plansName}=${plansJson};const ${runName}=((g,k)=>{const r=g[k]??(g[k]={runtimes:[],modules:[],captureRoot(i,v){return v},register(p){const d={run:(i,a,c)=>c()};const m={plans:p,cell:d};this.modules.push(m);const n=this.runtimes.at(-1);if(n)n.bind(m);return(i,a,c,l)=>d.run(i,a,c,l)}});return r.register(${plansName})})(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));const ${capName}=((g,k)=>{const r=g[k];return(i,v)=>typeof r?.captureRoot==='function'?r.captureRoot(i,v):v})(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));`;
|
|
337
995
|
var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
338
996
|
const start = mappedLocation(
|
|
339
997
|
sourceFile,
|
|
@@ -364,77 +1022,128 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
|
364
1022
|
paramNames
|
|
365
1023
|
};
|
|
366
1024
|
};
|
|
1025
|
+
var canOmitCallValues = (analysis, analyzed) => {
|
|
1026
|
+
if (!functionIsExact(analysis.plan) || analysis.hasThrow || analysis.hasCoercionGuard) {
|
|
1027
|
+
return false;
|
|
1028
|
+
}
|
|
1029
|
+
const callers = analyzed.filter(
|
|
1030
|
+
(caller) => caller.callEdges.some((edge) => edge.calleeFunctionId === analysis.functionId)
|
|
1031
|
+
);
|
|
1032
|
+
if (callers.length === 0) return false;
|
|
1033
|
+
return callers.every((caller) => caller.plan.fallbackReason === void 0 && !caller.hasThrow);
|
|
1034
|
+
};
|
|
367
1035
|
var transformSource = (source, options) => {
|
|
368
1036
|
if (source.includes("@rasputin-ignore-file")) return void 0;
|
|
369
|
-
const sourceFile =
|
|
1037
|
+
const sourceFile = ts3.createSourceFile(
|
|
370
1038
|
options.filePath,
|
|
371
1039
|
source,
|
|
372
|
-
|
|
1040
|
+
ts3.ScriptTarget.Latest,
|
|
373
1041
|
true,
|
|
374
1042
|
scriptKindFor(options.filePath)
|
|
375
1043
|
);
|
|
376
1044
|
const runName = uniqueName(source, "__rasputinRun__");
|
|
1045
|
+
const capName = uniqueName(source, "__rasputinCap__");
|
|
377
1046
|
const plansName = uniqueName(source, "__rasputinPlans__");
|
|
378
1047
|
const absolutePath = resolve3(options.filePath);
|
|
1048
|
+
const derivationMode = options.derivationMode ?? "analysis";
|
|
379
1049
|
const edits = [];
|
|
380
1050
|
let order = 0;
|
|
381
1051
|
const delta = {
|
|
382
1052
|
functions: [],
|
|
383
1053
|
callSites: [],
|
|
384
|
-
destructures: []
|
|
1054
|
+
destructures: [],
|
|
1055
|
+
derivationPlans: []
|
|
385
1056
|
};
|
|
386
1057
|
const collectContext = {
|
|
387
1058
|
sourceFile,
|
|
388
1059
|
mapLocation: options.mapLocation,
|
|
389
1060
|
absolutePath
|
|
390
1061
|
};
|
|
1062
|
+
const pending = [];
|
|
391
1063
|
const visit = (node, caller) => {
|
|
392
1064
|
if (isSupportedFunction(node)) {
|
|
393
1065
|
const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
|
|
394
1066
|
if (!collected) {
|
|
395
|
-
|
|
1067
|
+
ts3.forEachChild(node, (child) => visit(child, caller));
|
|
396
1068
|
return;
|
|
397
1069
|
}
|
|
398
|
-
|
|
1070
|
+
pending.push({ node, collected });
|
|
399
1071
|
delta.functions.push(collected);
|
|
400
|
-
|
|
401
|
-
const asyncCallback = node.modifiers?.some(
|
|
402
|
-
(modifier) => modifier.kind === ts2.SyntaxKind.AsyncKeyword
|
|
403
|
-
) ? "async " : "";
|
|
404
|
-
if (ts2.isBlock(node.body)) {
|
|
405
|
-
edits.push({
|
|
406
|
-
position: node.body.getStart(sourceFile) + 1,
|
|
407
|
-
text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
|
|
408
|
-
order: order++
|
|
409
|
-
});
|
|
410
|
-
edits.push({ position: node.body.end - 1, text: "});", order: order++ });
|
|
411
|
-
} else {
|
|
412
|
-
edits.push({
|
|
413
|
-
position: node.body.getStart(sourceFile),
|
|
414
|
-
text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
|
|
415
|
-
order: order++
|
|
416
|
-
});
|
|
417
|
-
edits.push({ position: node.body.end, text: "))", order: order++ });
|
|
418
|
-
}
|
|
419
|
-
ts2.forEachChild(node, (child) => visit(child, collected));
|
|
1072
|
+
ts3.forEachChild(node, (child) => visit(child, collected));
|
|
420
1073
|
return;
|
|
421
1074
|
}
|
|
422
|
-
if (caller && (
|
|
1075
|
+
if (caller && (ts3.isCallExpression(node) || ts3.isNewExpression(node))) {
|
|
423
1076
|
const site = collectCallSite(node, caller, collectContext);
|
|
424
1077
|
if (site) delta.callSites.push(site);
|
|
425
1078
|
}
|
|
426
|
-
if (caller &&
|
|
1079
|
+
if (caller && ts3.isVariableDeclaration(node)) {
|
|
427
1080
|
const destructure = collectParamDestructure(node, caller, collectContext);
|
|
428
1081
|
if (destructure) delta.destructures.push(destructure);
|
|
429
1082
|
}
|
|
430
|
-
|
|
1083
|
+
ts3.forEachChild(node, (child) => visit(child, caller));
|
|
431
1084
|
};
|
|
432
1085
|
visit(sourceFile);
|
|
433
|
-
if (
|
|
434
|
-
const
|
|
1086
|
+
if (pending.length === 0) return void 0;
|
|
1087
|
+
const analyzed = analyzeModuleFunctions(
|
|
1088
|
+
sourceFile,
|
|
1089
|
+
pending.map((entry) => ({
|
|
1090
|
+
functionId: entry.collected.functionId,
|
|
1091
|
+
name: entry.collected.name,
|
|
1092
|
+
node: entry.node
|
|
1093
|
+
}))
|
|
1094
|
+
);
|
|
1095
|
+
delta.derivationPlans = analyzed.map((fn) => fn.plan);
|
|
1096
|
+
const analyzedById = new Map(analyzed.map((fn) => [fn.functionId, fn]));
|
|
1097
|
+
for (const [planIndex, entry] of pending.entries()) {
|
|
1098
|
+
const analysis = analyzedById.get(entry.collected.functionId);
|
|
1099
|
+
const omitValues = derivationMode === "selective" && analysis && canOmitCallValues(analysis, analyzed);
|
|
1100
|
+
const valueFlag = omitValues ? ",1" : "";
|
|
1101
|
+
const args = ts3.isArrowFunction(entry.node) ? arrowArguments(entry.node) : "arguments";
|
|
1102
|
+
const asyncCallback = entry.node.modifiers?.some(
|
|
1103
|
+
(modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword
|
|
1104
|
+
) ? "async " : "";
|
|
1105
|
+
if (ts3.isBlock(entry.node.body)) {
|
|
1106
|
+
edits.push({
|
|
1107
|
+
position: entry.node.body.getStart(sourceFile) + 1,
|
|
1108
|
+
text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
|
|
1109
|
+
order: order++
|
|
1110
|
+
});
|
|
1111
|
+
edits.push({
|
|
1112
|
+
position: entry.node.body.end - 1,
|
|
1113
|
+
text: `}${valueFlag});`,
|
|
1114
|
+
order: order++
|
|
1115
|
+
});
|
|
1116
|
+
} else {
|
|
1117
|
+
edits.push({
|
|
1118
|
+
position: entry.node.body.getStart(sourceFile),
|
|
1119
|
+
text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
|
|
1120
|
+
order: order++
|
|
1121
|
+
});
|
|
1122
|
+
edits.push({
|
|
1123
|
+
position: entry.node.body.end,
|
|
1124
|
+
text: `)${valueFlag})`,
|
|
1125
|
+
order: order++
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
if (analysis) {
|
|
1129
|
+
for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
|
|
1130
|
+
edits.push({ ...edit, order: order++ });
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
const insertAt = insertPositionAfterPrologue(source, sourceFile);
|
|
435
1135
|
edits.push({
|
|
436
|
-
position:
|
|
437
|
-
text:
|
|
1136
|
+
position: insertAt,
|
|
1137
|
+
text: preambleInsert(
|
|
1138
|
+
source,
|
|
1139
|
+
insertAt,
|
|
1140
|
+
modulePreamble(
|
|
1141
|
+
runName,
|
|
1142
|
+
capName,
|
|
1143
|
+
plansName,
|
|
1144
|
+
JSON.stringify(delta.functions.map((fn) => fn.plan))
|
|
1145
|
+
)
|
|
1146
|
+
),
|
|
438
1147
|
order: -1
|
|
439
1148
|
});
|
|
440
1149
|
edits.sort((left, right) => right.position - left.position || right.order - left.order);
|
|
@@ -501,7 +1210,21 @@ import { dirname, resolve as resolve5 } from "node:path";
|
|
|
501
1210
|
import { fileURLToPath } from "node:url";
|
|
502
1211
|
var sdkPackageScopeRootFrom = (instrumentEntryUrl) => resolve5(dirname(fileURLToPath(instrumentEntryUrl)), "../../..");
|
|
503
1212
|
|
|
1213
|
+
// src/auto-instrumentation/warn-production-runtime-transform.ts
|
|
1214
|
+
var RUNTIME_TRANSFORM_WARNING_SUPPRESS_ENV = "RASPUTIN_SUPPRESS_RUNTIME_TRANSFORM_WARNING";
|
|
1215
|
+
var warnedSymbol = Symbol.for("rasputin.runtimeTransformWarning");
|
|
1216
|
+
var warnIfProductionRuntimeTransform = () => {
|
|
1217
|
+
if (process.env.NODE_ENV !== "production") return;
|
|
1218
|
+
if (process.env[RUNTIME_TRANSFORM_WARNING_SUPPRESS_ENV] === "1") return;
|
|
1219
|
+
if (Reflect.get(globalThis, warnedSymbol)) return;
|
|
1220
|
+
Reflect.set(globalThis, warnedSymbol, true);
|
|
1221
|
+
console.warn(
|
|
1222
|
+
"[rasputin] Production is using load-time source transformation (`--import` / `--preload`). Prefer build-time `rasputin-instrument` so the process does not load the TypeScript compiler. Set RASPUTIN_SUPPRESS_RUNTIME_TRANSFORM_WARNING=1 to hide this warning."
|
|
1223
|
+
);
|
|
1224
|
+
};
|
|
1225
|
+
|
|
504
1226
|
// src/instrument/bun.ts
|
|
1227
|
+
warnIfProductionRuntimeTransform();
|
|
505
1228
|
plugin(
|
|
506
1229
|
createRasputinBunPlugin({
|
|
507
1230
|
excludedRoots: [sdkPackageScopeRootFrom(import.meta.url)]
|