@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.
Files changed (41) hide show
  1. package/README.md +15 -15
  2. package/dist/auto-instrumentation/instrument-compiled-esm.d.ts +2 -0
  3. package/dist/auto-instrumentation/instrument-compiled-esm.d.ts.map +1 -1
  4. package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts +2 -1
  5. package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts.map +1 -1
  6. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts +1 -1
  7. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts.map +1 -1
  8. package/dist/auto-instrumentation/transform-source.d.ts +6 -0
  9. package/dist/auto-instrumentation/transform-source.d.ts.map +1 -1
  10. package/dist/auto-instrumentation/warn-production-runtime-transform.d.ts +8 -0
  11. package/dist/auto-instrumentation/warn-production-runtime-transform.d.ts.map +1 -0
  12. package/dist/derivation/analyze-function.d.ts +42 -0
  13. package/dist/derivation/analyze-function.d.ts.map +1 -0
  14. package/dist/derivation/build-dependency-graph.d.ts +21 -0
  15. package/dist/derivation/build-dependency-graph.d.ts.map +1 -0
  16. package/dist/derivation/derivation-ir.d.ts +33 -0
  17. package/dist/derivation/derivation-ir.d.ts.map +1 -0
  18. package/dist/derivation/derivation-proof.d.ts +14 -0
  19. package/dist/derivation/derivation-proof.d.ts.map +1 -0
  20. package/dist/derivation/emit-capture-plan.d.ts +8 -0
  21. package/dist/derivation/emit-capture-plan.d.ts.map +1 -0
  22. package/dist/execution-recorder/automatic-runtime.d.ts +12 -3
  23. package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
  24. package/dist/execution-recorder/execution-journal.d.ts +84 -0
  25. package/dist/execution-recorder/execution-journal.d.ts.map +1 -0
  26. package/dist/execution-recorder/execution-recorder-types.d.ts +23 -4
  27. package/dist/execution-recorder/execution-recorder-types.d.ts.map +1 -1
  28. package/dist/execution-recorder/execution-recorder.d.ts.map +1 -1
  29. package/dist/execution-recorder/recorder-memory-budget.d.ts +1 -0
  30. package/dist/execution-recorder/recorder-memory-budget.d.ts.map +1 -1
  31. package/dist/execution-recorder/safe-serialize.d.ts +1 -0
  32. package/dist/execution-recorder/safe-serialize.d.ts.map +1 -1
  33. package/dist/index.js +1181 -330
  34. package/dist/instrument/build.js +795 -72
  35. package/dist/instrument/bun.js +790 -67
  36. package/dist/instrument/node.js +790 -67
  37. package/dist/sdk-meta.d.ts +1 -1
  38. package/dist/sdk-meta.d.ts.map +1 -1
  39. package/package.json +2 -2
  40. package/dist/execution-recorder/call-aware-event-buffer.d.ts +0 -24
  41. package/dist/execution-recorder/call-aware-event-buffer.d.ts.map +0 -1
@@ -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.v2";
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 ts2 from "typescript";
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 ts from "typescript";
741
+ import ts2 from "typescript";
99
742
  var paramBindingFromName = (name, sourceFile) => {
100
- if (ts.isIdentifier(name)) return { kind: "identifier", name: name.text };
101
- if (ts.isArrayBindingPattern(name)) {
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 (ts.isOmittedExpression(element)) return { kind: "omitted" };
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, "") : ts.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
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 (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
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 (ts.isParenthesizedExpression(current) || ts.isNonNullExpression(current)) {
810
+ while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
168
811
  current = current.expression;
169
812
  }
170
- if (ts.isAwaitExpression(current)) return unwrapExpression(current.expression);
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 (ts.isIdentifier(current)) {
818
+ if (ts2.isIdentifier(current)) {
176
819
  return { calleeName: current.text, calleeText: current.text };
177
820
  }
178
- if (ts.isPropertyAccessExpression(current) && ts.isIdentifier(current.name)) {
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 (ts.isAwaitExpression(node)) node = node.parent;
189
- if (!ts.isVariableDeclaration(node)) return null;
190
- if (!ts.isIdentifier(node.name)) return null;
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 (ts.isIdentifier(argument)) {
843
+ if (ts2.isIdentifier(argument)) {
201
844
  identifiers.push({ index, kind: "identifier", name: argument.text });
202
845
  return;
203
846
  }
204
- if (!ts.isObjectLiteralExpression(argument)) return;
847
+ if (!ts2.isObjectLiteralExpression(argument)) return;
205
848
  const properties = [];
206
849
  for (const property of argument.properties) {
207
- if (ts.isShorthandPropertyAssignment(property)) {
850
+ if (ts2.isShorthandPropertyAssignment(property)) {
208
851
  properties.push({ key: property.name.text, name: property.name.text });
209
852
  continue;
210
853
  }
211
- if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && ts.isIdentifier(property.initializer)) {
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 || !ts.isIdentifier(declaration.initializer)) return null;
252
- if (!ts.isObjectBindingPattern(declaration.name) && !ts.isArrayBindingPattern(declaration.name)) {
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 ts2.ScriptKind.TSX;
277
- if (/\.jsx$/i.test(filePath)) return ts2.ScriptKind.JSX;
278
- if (/\.[cm]?ts$/i.test(filePath)) return ts2.ScriptKind.TS;
279
- return ts2.ScriptKind.JS;
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 (ts2.isVariableDeclaration(parent) && ts2.isIdentifier(parent.name)) return parent.name.text;
285
- if (ts2.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
286
- if (ts2.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
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 ((ts2.isVariableDeclaration(parent) || ts2.isPropertyAssignment(parent)) && parent.name) {
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 (ts2.isIdentifier(name)) return name.text;
300
- if (ts2.isArrayBindingPattern(name)) {
942
+ if (ts3.isIdentifier(name)) return name.text;
943
+ if (ts3.isArrayBindingPattern(name)) {
301
944
  const values = name.elements.map((element) => {
302
- if (ts2.isOmittedExpression(element)) return "";
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 (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
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 (!ts2.isFunctionDeclaration(node) && !ts2.isFunctionExpression(node) && !ts2.isArrowFunction(node) && !ts2.isMethodDeclaration(node) && !ts2.isGetAccessorDeclaration(node) && !ts2.isSetAccessorDeclaration(node)) {
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 modulePreamble = (runName, plansName, plansJson) => `const ${plansName}=${plansJson};const ${runName}=((g,k)=>{const r=g[k]??(g[k]={runtimes:[],register(p){return(i,a,c)=>{const n=this.runtimes.at(-1);return n?n.run(p,i,a,c):c()}}});return r.register(${plansName})})(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));`;
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 = ts2.createSourceFile(
1037
+ const sourceFile = ts3.createSourceFile(
370
1038
  options.filePath,
371
1039
  source,
372
- ts2.ScriptTarget.Latest,
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
- ts2.forEachChild(node, (child) => visit(child, caller));
1067
+ ts3.forEachChild(node, (child) => visit(child, caller));
396
1068
  return;
397
1069
  }
398
- const planIndex = delta.functions.length;
1070
+ pending.push({ node, collected });
399
1071
  delta.functions.push(collected);
400
- const args = ts2.isArrowFunction(node) ? arrowArguments(node) : "arguments";
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 && (ts2.isCallExpression(node) || ts2.isNewExpression(node))) {
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 && ts2.isVariableDeclaration(node)) {
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
- ts2.forEachChild(node, (child) => visit(child, caller));
1083
+ ts3.forEachChild(node, (child) => visit(child, caller));
431
1084
  };
432
1085
  visit(sourceFile);
433
- if (edits.length === 0) return void 0;
434
- const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
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: shebangEnd,
437
- text: modulePreamble(runName, plansName, JSON.stringify(delta.functions.map((fn) => fn.plan))),
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)]