@rasputin-ai/node 0.5.0-alpha.1 → 0.5.0-alpha.11
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 +23 -4
- 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 +1181 -330
- 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/node.js
CHANGED
|
@@ -31,7 +31,7 @@ var encodeFunctionPlan = (source) => {
|
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
// src/auto-instrumentation/instrumentation-manifest-registry.ts
|
|
34
|
-
var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.
|
|
34
|
+
var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v3";
|
|
35
35
|
var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
|
|
36
36
|
var getRegistry = () => {
|
|
37
37
|
const existing = Reflect.get(globalThis, registrySymbol);
|
|
@@ -143,20 +143,663 @@ var createSourceMapLocationMapper = (generatedPath, source) => {
|
|
|
143
143
|
|
|
144
144
|
// src/auto-instrumentation/transform-source.ts
|
|
145
145
|
import { resolve as resolve4 } from "node:path";
|
|
146
|
-
import
|
|
146
|
+
import ts3 from "typescript";
|
|
147
|
+
|
|
148
|
+
// src/derivation/analyze-function.ts
|
|
149
|
+
import ts from "typescript";
|
|
150
|
+
|
|
151
|
+
// src/derivation/build-dependency-graph.ts
|
|
152
|
+
var buildDependencyGraph = (functions, edges) => {
|
|
153
|
+
const names = new Set(functions.map((fn) => fn.name));
|
|
154
|
+
const localEdges = edges.filter((edge) => names.has(edge.calleeName));
|
|
155
|
+
const calleesByCaller = /* @__PURE__ */ new Map();
|
|
156
|
+
for (const edge of localEdges) {
|
|
157
|
+
const existing = calleesByCaller.get(edge.callerFunctionId) ?? [];
|
|
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
|
+
};
|
|
768
|
+
|
|
769
|
+
// src/derivation/derivation-proof.ts
|
|
770
|
+
var functionIsExact = (plan) => plan.fallbackReason === void 0 && plan.captureBoundaries.length === 0 && plan.controlFacts.length === 0;
|
|
771
|
+
|
|
772
|
+
// src/derivation/emit-capture-plan.ts
|
|
773
|
+
var captureWrapEdits = (wraps, capName) => {
|
|
774
|
+
const edits = [];
|
|
775
|
+
let order = 0;
|
|
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
|
+
};
|
|
147
790
|
|
|
148
791
|
// src/execution-recorder/automatic-runtime.ts
|
|
149
792
|
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
|
|
150
793
|
var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
|
|
151
794
|
|
|
152
795
|
// src/auto-instrumentation/collect-instrumentation-delta.ts
|
|
153
|
-
import
|
|
796
|
+
import ts2 from "typescript";
|
|
154
797
|
var paramBindingFromName = (name, sourceFile) => {
|
|
155
|
-
if (
|
|
156
|
-
if (
|
|
798
|
+
if (ts2.isIdentifier(name)) return { kind: "identifier", name: name.text };
|
|
799
|
+
if (ts2.isArrayBindingPattern(name)) {
|
|
157
800
|
const elements = name.elements.map(
|
|
158
801
|
(element) => {
|
|
159
|
-
if (
|
|
802
|
+
if (ts2.isOmittedExpression(element)) return { kind: "omitted" };
|
|
160
803
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
161
804
|
if (element.dotDotDotToken) return { kind: "rest", binding };
|
|
162
805
|
return binding;
|
|
@@ -166,7 +809,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
166
809
|
}
|
|
167
810
|
const properties = name.elements.map((element) => {
|
|
168
811
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
169
|
-
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") :
|
|
812
|
+
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts2.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
|
|
170
813
|
if (element.dotDotDotToken) {
|
|
171
814
|
const rest = { kind: "rest", binding };
|
|
172
815
|
return { key, binding: rest };
|
|
@@ -178,7 +821,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
178
821
|
var collectFunctionParams = (node, sourceFile) => {
|
|
179
822
|
const params = [];
|
|
180
823
|
for (const parameter of node.parameters) {
|
|
181
|
-
if (
|
|
824
|
+
if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
182
825
|
const binding = paramBindingFromName(parameter.name, sourceFile);
|
|
183
826
|
params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
|
|
184
827
|
}
|
|
@@ -219,18 +862,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
|
|
|
219
862
|
};
|
|
220
863
|
var unwrapExpression = (expression) => {
|
|
221
864
|
let current = expression;
|
|
222
|
-
while (
|
|
865
|
+
while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
|
|
223
866
|
current = current.expression;
|
|
224
867
|
}
|
|
225
|
-
if (
|
|
868
|
+
if (ts2.isAwaitExpression(current)) return unwrapExpression(current.expression);
|
|
226
869
|
return current;
|
|
227
870
|
};
|
|
228
871
|
var calleeFromExpression = (expression, sourceFile) => {
|
|
229
872
|
const current = unwrapExpression(expression);
|
|
230
|
-
if (
|
|
873
|
+
if (ts2.isIdentifier(current)) {
|
|
231
874
|
return { calleeName: current.text, calleeText: current.text };
|
|
232
875
|
}
|
|
233
|
-
if (
|
|
876
|
+
if (ts2.isPropertyAccessExpression(current) && ts2.isIdentifier(current.name)) {
|
|
234
877
|
return {
|
|
235
878
|
calleeName: current.name.text,
|
|
236
879
|
calleeText: current.getText(sourceFile).replace(/\s+/g, "")
|
|
@@ -240,9 +883,9 @@ var calleeFromExpression = (expression, sourceFile) => {
|
|
|
240
883
|
};
|
|
241
884
|
var resultBindingForCall = (call) => {
|
|
242
885
|
let node = call.parent;
|
|
243
|
-
if (
|
|
244
|
-
if (!
|
|
245
|
-
if (!
|
|
886
|
+
if (ts2.isAwaitExpression(node)) node = node.parent;
|
|
887
|
+
if (!ts2.isVariableDeclaration(node)) return null;
|
|
888
|
+
if (!ts2.isIdentifier(node.name)) return null;
|
|
246
889
|
const initializer = node.initializer;
|
|
247
890
|
if (!initializer) return null;
|
|
248
891
|
const inner = unwrapExpression(initializer);
|
|
@@ -252,18 +895,18 @@ var resultBindingForCall = (call) => {
|
|
|
252
895
|
var argIdentifiersForCall = (call) => {
|
|
253
896
|
const identifiers = [];
|
|
254
897
|
call.arguments?.forEach((argument, index) => {
|
|
255
|
-
if (
|
|
898
|
+
if (ts2.isIdentifier(argument)) {
|
|
256
899
|
identifiers.push({ index, kind: "identifier", name: argument.text });
|
|
257
900
|
return;
|
|
258
901
|
}
|
|
259
|
-
if (!
|
|
902
|
+
if (!ts2.isObjectLiteralExpression(argument)) return;
|
|
260
903
|
const properties = [];
|
|
261
904
|
for (const property of argument.properties) {
|
|
262
|
-
if (
|
|
905
|
+
if (ts2.isShorthandPropertyAssignment(property)) {
|
|
263
906
|
properties.push({ key: property.name.text, name: property.name.text });
|
|
264
907
|
continue;
|
|
265
908
|
}
|
|
266
|
-
if (
|
|
909
|
+
if (ts2.isPropertyAssignment(property) && ts2.isIdentifier(property.name) && ts2.isIdentifier(property.initializer)) {
|
|
267
910
|
properties.push({ key: property.name.text, name: property.initializer.text });
|
|
268
911
|
}
|
|
269
912
|
}
|
|
@@ -303,8 +946,8 @@ var collectCallSite = (call, caller, context) => {
|
|
|
303
946
|
};
|
|
304
947
|
};
|
|
305
948
|
var collectParamDestructure = (declaration, caller, context) => {
|
|
306
|
-
if (!declaration.initializer || !
|
|
307
|
-
if (!
|
|
949
|
+
if (!declaration.initializer || !ts2.isIdentifier(declaration.initializer)) return null;
|
|
950
|
+
if (!ts2.isObjectBindingPattern(declaration.name) && !ts2.isArrayBindingPattern(declaration.name)) {
|
|
308
951
|
return null;
|
|
309
952
|
}
|
|
310
953
|
const sourceParam = declaration.initializer.text;
|
|
@@ -328,33 +971,33 @@ var collectParamDestructure = (declaration, caller, context) => {
|
|
|
328
971
|
|
|
329
972
|
// src/auto-instrumentation/transform-source.ts
|
|
330
973
|
var scriptKindFor = (filePath) => {
|
|
331
|
-
if (/\.tsx$/i.test(filePath)) return
|
|
332
|
-
if (/\.jsx$/i.test(filePath)) return
|
|
333
|
-
if (/\.[cm]?ts$/i.test(filePath)) return
|
|
334
|
-
return
|
|
974
|
+
if (/\.tsx$/i.test(filePath)) return ts3.ScriptKind.TSX;
|
|
975
|
+
if (/\.jsx$/i.test(filePath)) return ts3.ScriptKind.JSX;
|
|
976
|
+
if (/\.[cm]?ts$/i.test(filePath)) return ts3.ScriptKind.TS;
|
|
977
|
+
return ts3.ScriptKind.JS;
|
|
335
978
|
};
|
|
336
979
|
var functionName = (node, sourceFile) => {
|
|
337
980
|
if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
|
|
338
981
|
const parent = node.parent;
|
|
339
|
-
if (
|
|
340
|
-
if (
|
|
341
|
-
if (
|
|
982
|
+
if (ts3.isVariableDeclaration(parent) && ts3.isIdentifier(parent.name)) return parent.name.text;
|
|
983
|
+
if (ts3.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
|
|
984
|
+
if (ts3.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
|
|
342
985
|
const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
343
986
|
return `anonymous@${location.line + 1}:${location.character + 1}`;
|
|
344
987
|
};
|
|
345
988
|
var functionLocationPosition = (node, sourceFile) => {
|
|
346
989
|
if (node.name) return node.name.getStart(sourceFile);
|
|
347
990
|
const parent = node.parent;
|
|
348
|
-
if ((
|
|
991
|
+
if ((ts3.isVariableDeclaration(parent) || ts3.isPropertyAssignment(parent)) && parent.name) {
|
|
349
992
|
return parent.name.getStart(sourceFile);
|
|
350
993
|
}
|
|
351
994
|
return node.getStart(sourceFile);
|
|
352
995
|
};
|
|
353
996
|
var bindingValue = (name, sourceFile) => {
|
|
354
|
-
if (
|
|
355
|
-
if (
|
|
997
|
+
if (ts3.isIdentifier(name)) return name.text;
|
|
998
|
+
if (ts3.isArrayBindingPattern(name)) {
|
|
356
999
|
const values = name.elements.map((element) => {
|
|
357
|
-
if (
|
|
1000
|
+
if (ts3.isOmittedExpression(element)) return "";
|
|
358
1001
|
const value = bindingValue(element.name, sourceFile);
|
|
359
1002
|
return element.dotDotDotToken ? `...${value}` : value;
|
|
360
1003
|
});
|
|
@@ -370,14 +1013,14 @@ var bindingValue = (name, sourceFile) => {
|
|
|
370
1013
|
};
|
|
371
1014
|
var arrowArguments = (node) => {
|
|
372
1015
|
const values = node.parameters.flatMap((parameter) => {
|
|
373
|
-
if (
|
|
1016
|
+
if (ts3.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
|
|
374
1017
|
const value = bindingValue(parameter.name, node.getSourceFile());
|
|
375
1018
|
return [parameter.dotDotDotToken ? `...${value}` : value];
|
|
376
1019
|
});
|
|
377
1020
|
return `[${values.join(",")}]`;
|
|
378
1021
|
};
|
|
379
1022
|
var isSupportedFunction = (node) => {
|
|
380
|
-
if (!
|
|
1023
|
+
if (!ts3.isFunctionDeclaration(node) && !ts3.isFunctionExpression(node) && !ts3.isArrowFunction(node) && !ts3.isMethodDeclaration(node) && !ts3.isGetAccessorDeclaration(node) && !ts3.isSetAccessorDeclaration(node)) {
|
|
381
1024
|
return false;
|
|
382
1025
|
}
|
|
383
1026
|
if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
|
|
@@ -388,7 +1031,22 @@ var uniqueName = (source, base) => {
|
|
|
388
1031
|
while (source.includes(name)) name += "_";
|
|
389
1032
|
return name;
|
|
390
1033
|
};
|
|
391
|
-
var
|
|
1034
|
+
var isDirectivePrologue = (statement) => ts3.isExpressionStatement(statement) && ts3.isStringLiteral(statement.expression);
|
|
1035
|
+
var insertPositionAfterPrologue = (source, sourceFile) => {
|
|
1036
|
+
const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
|
|
1037
|
+
let end = shebangEnd;
|
|
1038
|
+
for (const statement of sourceFile.statements) {
|
|
1039
|
+
if (!isDirectivePrologue(statement)) break;
|
|
1040
|
+
end = statement.end;
|
|
1041
|
+
}
|
|
1042
|
+
return end;
|
|
1043
|
+
};
|
|
1044
|
+
var preambleInsert = (source, position, preamble) => {
|
|
1045
|
+
if (position <= 0) return preamble;
|
|
1046
|
+
const previous = source[position - 1];
|
|
1047
|
+
return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
|
|
1048
|
+
};
|
|
1049
|
+
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)}));`;
|
|
392
1050
|
var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
393
1051
|
const start = mappedLocation(
|
|
394
1052
|
sourceFile,
|
|
@@ -419,77 +1077,128 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
|
419
1077
|
paramNames
|
|
420
1078
|
};
|
|
421
1079
|
};
|
|
1080
|
+
var canOmitCallValues = (analysis, analyzed) => {
|
|
1081
|
+
if (!functionIsExact(analysis.plan) || analysis.hasThrow || analysis.hasCoercionGuard) {
|
|
1082
|
+
return false;
|
|
1083
|
+
}
|
|
1084
|
+
const callers = analyzed.filter(
|
|
1085
|
+
(caller) => caller.callEdges.some((edge) => edge.calleeFunctionId === analysis.functionId)
|
|
1086
|
+
);
|
|
1087
|
+
if (callers.length === 0) return false;
|
|
1088
|
+
return callers.every((caller) => caller.plan.fallbackReason === void 0 && !caller.hasThrow);
|
|
1089
|
+
};
|
|
422
1090
|
var transformSource = (source, options) => {
|
|
423
1091
|
if (source.includes("@rasputin-ignore-file")) return void 0;
|
|
424
|
-
const sourceFile =
|
|
1092
|
+
const sourceFile = ts3.createSourceFile(
|
|
425
1093
|
options.filePath,
|
|
426
1094
|
source,
|
|
427
|
-
|
|
1095
|
+
ts3.ScriptTarget.Latest,
|
|
428
1096
|
true,
|
|
429
1097
|
scriptKindFor(options.filePath)
|
|
430
1098
|
);
|
|
431
1099
|
const runName = uniqueName(source, "__rasputinRun__");
|
|
1100
|
+
const capName = uniqueName(source, "__rasputinCap__");
|
|
432
1101
|
const plansName = uniqueName(source, "__rasputinPlans__");
|
|
433
1102
|
const absolutePath = resolve4(options.filePath);
|
|
1103
|
+
const derivationMode = options.derivationMode ?? "analysis";
|
|
434
1104
|
const edits = [];
|
|
435
1105
|
let order = 0;
|
|
436
1106
|
const delta = {
|
|
437
1107
|
functions: [],
|
|
438
1108
|
callSites: [],
|
|
439
|
-
destructures: []
|
|
1109
|
+
destructures: [],
|
|
1110
|
+
derivationPlans: []
|
|
440
1111
|
};
|
|
441
1112
|
const collectContext = {
|
|
442
1113
|
sourceFile,
|
|
443
1114
|
mapLocation: options.mapLocation,
|
|
444
1115
|
absolutePath
|
|
445
1116
|
};
|
|
1117
|
+
const pending = [];
|
|
446
1118
|
const visit = (node, caller) => {
|
|
447
1119
|
if (isSupportedFunction(node)) {
|
|
448
1120
|
const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
|
|
449
1121
|
if (!collected) {
|
|
450
|
-
|
|
1122
|
+
ts3.forEachChild(node, (child) => visit(child, caller));
|
|
451
1123
|
return;
|
|
452
1124
|
}
|
|
453
|
-
|
|
1125
|
+
pending.push({ node, collected });
|
|
454
1126
|
delta.functions.push(collected);
|
|
455
|
-
|
|
456
|
-
const asyncCallback = node.modifiers?.some(
|
|
457
|
-
(modifier) => modifier.kind === ts2.SyntaxKind.AsyncKeyword
|
|
458
|
-
) ? "async " : "";
|
|
459
|
-
if (ts2.isBlock(node.body)) {
|
|
460
|
-
edits.push({
|
|
461
|
-
position: node.body.getStart(sourceFile) + 1,
|
|
462
|
-
text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
|
|
463
|
-
order: order++
|
|
464
|
-
});
|
|
465
|
-
edits.push({ position: node.body.end - 1, text: "});", order: order++ });
|
|
466
|
-
} else {
|
|
467
|
-
edits.push({
|
|
468
|
-
position: node.body.getStart(sourceFile),
|
|
469
|
-
text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
|
|
470
|
-
order: order++
|
|
471
|
-
});
|
|
472
|
-
edits.push({ position: node.body.end, text: "))", order: order++ });
|
|
473
|
-
}
|
|
474
|
-
ts2.forEachChild(node, (child) => visit(child, collected));
|
|
1127
|
+
ts3.forEachChild(node, (child) => visit(child, collected));
|
|
475
1128
|
return;
|
|
476
1129
|
}
|
|
477
|
-
if (caller && (
|
|
1130
|
+
if (caller && (ts3.isCallExpression(node) || ts3.isNewExpression(node))) {
|
|
478
1131
|
const site = collectCallSite(node, caller, collectContext);
|
|
479
1132
|
if (site) delta.callSites.push(site);
|
|
480
1133
|
}
|
|
481
|
-
if (caller &&
|
|
1134
|
+
if (caller && ts3.isVariableDeclaration(node)) {
|
|
482
1135
|
const destructure = collectParamDestructure(node, caller, collectContext);
|
|
483
1136
|
if (destructure) delta.destructures.push(destructure);
|
|
484
1137
|
}
|
|
485
|
-
|
|
1138
|
+
ts3.forEachChild(node, (child) => visit(child, caller));
|
|
486
1139
|
};
|
|
487
1140
|
visit(sourceFile);
|
|
488
|
-
if (
|
|
489
|
-
const
|
|
1141
|
+
if (pending.length === 0) return void 0;
|
|
1142
|
+
const analyzed = analyzeModuleFunctions(
|
|
1143
|
+
sourceFile,
|
|
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]));
|
|
1152
|
+
for (const [planIndex, entry] of pending.entries()) {
|
|
1153
|
+
const analysis = analyzedById.get(entry.collected.functionId);
|
|
1154
|
+
const omitValues = derivationMode === "selective" && analysis && canOmitCallValues(analysis, analyzed);
|
|
1155
|
+
const valueFlag = omitValues ? ",1" : "";
|
|
1156
|
+
const args = ts3.isArrowFunction(entry.node) ? arrowArguments(entry.node) : "arguments";
|
|
1157
|
+
const asyncCallback = entry.node.modifiers?.some(
|
|
1158
|
+
(modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword
|
|
1159
|
+
) ? "async " : "";
|
|
1160
|
+
if (ts3.isBlock(entry.node.body)) {
|
|
1161
|
+
edits.push({
|
|
1162
|
+
position: entry.node.body.getStart(sourceFile) + 1,
|
|
1163
|
+
text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
|
|
1164
|
+
order: order++
|
|
1165
|
+
});
|
|
1166
|
+
edits.push({
|
|
1167
|
+
position: entry.node.body.end - 1,
|
|
1168
|
+
text: `}${valueFlag});`,
|
|
1169
|
+
order: order++
|
|
1170
|
+
});
|
|
1171
|
+
} else {
|
|
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++ });
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
const insertAt = insertPositionAfterPrologue(source, sourceFile);
|
|
490
1190
|
edits.push({
|
|
491
|
-
position:
|
|
492
|
-
text:
|
|
1191
|
+
position: insertAt,
|
|
1192
|
+
text: preambleInsert(
|
|
1193
|
+
source,
|
|
1194
|
+
insertAt,
|
|
1195
|
+
modulePreamble(
|
|
1196
|
+
runName,
|
|
1197
|
+
capName,
|
|
1198
|
+
plansName,
|
|
1199
|
+
JSON.stringify(delta.functions.map((fn) => fn.plan))
|
|
1200
|
+
)
|
|
1201
|
+
),
|
|
493
1202
|
order: -1
|
|
494
1203
|
});
|
|
495
1204
|
edits.sort((left, right) => right.position - left.position || right.order - left.order);
|
|
@@ -558,7 +1267,21 @@ import { dirname as dirname2, resolve as resolve5 } from "node:path";
|
|
|
558
1267
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
559
1268
|
var sdkPackageScopeRootFrom = (instrumentEntryUrl) => resolve5(dirname2(fileURLToPath3(instrumentEntryUrl)), "../../..");
|
|
560
1269
|
|
|
1270
|
+
// src/auto-instrumentation/warn-production-runtime-transform.ts
|
|
1271
|
+
var RUNTIME_TRANSFORM_WARNING_SUPPRESS_ENV = "RASPUTIN_SUPPRESS_RUNTIME_TRANSFORM_WARNING";
|
|
1272
|
+
var warnedSymbol = Symbol.for("rasputin.runtimeTransformWarning");
|
|
1273
|
+
var warnIfProductionRuntimeTransform = () => {
|
|
1274
|
+
if (process.env.NODE_ENV !== "production") return;
|
|
1275
|
+
if (process.env[RUNTIME_TRANSFORM_WARNING_SUPPRESS_ENV] === "1") return;
|
|
1276
|
+
if (Reflect.get(globalThis, warnedSymbol)) return;
|
|
1277
|
+
Reflect.set(globalThis, warnedSymbol, true);
|
|
1278
|
+
console.warn(
|
|
1279
|
+
"[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."
|
|
1280
|
+
);
|
|
1281
|
+
};
|
|
1282
|
+
|
|
561
1283
|
// src/instrument/node.ts
|
|
1284
|
+
warnIfProductionRuntimeTransform();
|
|
562
1285
|
registerRasputinNodeHooks({
|
|
563
1286
|
excludedRoots: [sdkPackageScopeRootFrom(import.meta.url)]
|
|
564
1287
|
});
|