@rasputin-ai/node 0.5.0-alpha.1 → 0.5.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +21 -2
  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 +1162 -328
  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
@@ -46,7 +46,7 @@ var definitionFromEncodedPlan = (plan) => {
46
46
  };
47
47
 
48
48
  // src/auto-instrumentation/instrumentation-manifest-registry.ts
49
- var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v2";
49
+ var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v3";
50
50
  var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
51
51
  var getRegistry = () => {
52
52
  const existing = Reflect.get(globalThis, registrySymbol);
@@ -77,6 +77,7 @@ var snapshotInstrumentationManifest = (options = {}) => {
77
77
  const functions = [];
78
78
  const callSites = [];
79
79
  const destructures = [];
80
+ const derivationPlans = [];
80
81
  for (const delta of getRegistry().files.values()) {
81
82
  for (const fn of delta.functions) {
82
83
  const definition = definitionFromEncodedPlan(fn.plan);
@@ -128,6 +129,7 @@ var snapshotInstrumentationManifest = (options = {}) => {
128
129
  binding: destructure.binding
129
130
  });
130
131
  }
132
+ derivationPlans.push(...delta.derivationPlans ?? []);
131
133
  }
132
134
  const registry = getRegistry();
133
135
  if (registry.prebuilt && registry.files.size === 0) {
@@ -155,7 +157,8 @@ var snapshotInstrumentationManifest = (options = {}) => {
155
157
  } : {},
156
158
  functions,
157
159
  callSites,
158
- destructures
160
+ destructures,
161
+ derivationPlans
159
162
  };
160
163
  };
161
164
  var resetInstrumentationManifestRegistry = () => {
@@ -251,20 +254,663 @@ var createSourceMapLocationMapper = (generatedPath, source) => {
251
254
 
252
255
  // src/auto-instrumentation/transform-source.ts
253
256
  import { resolve as resolve4 } from "node:path";
254
- import ts2 from "typescript";
257
+ import ts3 from "typescript";
258
+
259
+ // src/derivation/analyze-function.ts
260
+ import ts from "typescript";
261
+
262
+ // src/derivation/build-dependency-graph.ts
263
+ var buildDependencyGraph = (functions, edges) => {
264
+ const names = new Set(functions.map((fn) => fn.name));
265
+ const localEdges = edges.filter((edge) => names.has(edge.calleeName));
266
+ const calleesByCaller = /* @__PURE__ */ new Map();
267
+ for (const edge of localEdges) {
268
+ const existing = calleesByCaller.get(edge.callerFunctionId) ?? [];
269
+ existing.push(edge.calleeName);
270
+ calleesByCaller.set(edge.callerFunctionId, existing);
271
+ }
272
+ return {
273
+ functions: functions.map((fn) => fn.functionId),
274
+ edges: localEdges,
275
+ calleesByCaller
276
+ };
277
+ };
278
+ var topologicalFunctionOrder = (functions, graph) => {
279
+ const idByName = new Map(functions.map((fn) => [fn.name, fn.functionId]));
280
+ const remainingCallees = new Map(
281
+ functions.map((fn) => {
282
+ const unique = new Set(
283
+ (graph.calleesByCaller.get(fn.functionId) ?? []).map((name) => idByName.get(name)).filter((id) => Boolean(id) && id !== fn.functionId)
284
+ );
285
+ return [fn.functionId, unique.size];
286
+ })
287
+ );
288
+ const callersOf = /* @__PURE__ */ new Map();
289
+ for (const fn of functions) {
290
+ for (const calleeName of graph.calleesByCaller.get(fn.functionId) ?? []) {
291
+ const calleeId = idByName.get(calleeName);
292
+ if (!calleeId || calleeId === fn.functionId) continue;
293
+ const callers = callersOf.get(calleeId) ?? [];
294
+ callers.push(fn.functionId);
295
+ callersOf.set(calleeId, callers);
296
+ }
297
+ }
298
+ const queue = functions.filter((fn) => (remainingCallees.get(fn.functionId) ?? 0) === 0).map((fn) => fn.functionId);
299
+ const ordered = [];
300
+ const queued = new Set(queue);
301
+ while (queue.length > 0) {
302
+ const current = queue.shift();
303
+ if (current === void 0) break;
304
+ ordered.push(current);
305
+ for (const caller of callersOf.get(current) ?? []) {
306
+ const next = (remainingCallees.get(caller) ?? 0) - 1;
307
+ remainingCallees.set(caller, next);
308
+ if (next === 0 && !queued.has(caller)) {
309
+ queued.add(caller);
310
+ queue.push(caller);
311
+ }
312
+ }
313
+ }
314
+ for (const fn of functions) {
315
+ if (!queued.has(fn.functionId)) ordered.push(fn.functionId);
316
+ }
317
+ return ordered;
318
+ };
319
+
320
+ // src/derivation/derivation-ir.ts
321
+ var DerivationIrBuilder = class {
322
+ expressions = [];
323
+ nextId = 0;
324
+ constValue(value) {
325
+ return this.add({ id: this.nextId++, kind: "const", value });
326
+ }
327
+ param(index) {
328
+ return this.add({ id: this.nextId++, kind: "param", index });
329
+ }
330
+ root(rootId) {
331
+ return this.add({ id: this.nextId++, kind: "root", rootId });
332
+ }
333
+ alias(from) {
334
+ return this.add({ id: this.nextId++, kind: "alias", from });
335
+ }
336
+ binary(op, left, right, guard) {
337
+ return this.add({ id: this.nextId++, kind: "binary", op, left, right, guard });
338
+ }
339
+ unary(op, operand) {
340
+ return this.add({ id: this.nextId++, kind: "unary", op, operand });
341
+ }
342
+ logical(op, left, right) {
343
+ return this.add({ id: this.nextId++, kind: "logical", op, left, right });
344
+ }
345
+ cond(test, consequent, alternate) {
346
+ return this.add({ id: this.nextId++, kind: "cond", test, consequent, alternate });
347
+ }
348
+ object(properties) {
349
+ return this.add({ id: this.nextId++, kind: "object", properties });
350
+ }
351
+ array(elements) {
352
+ return this.add({ id: this.nextId++, kind: "array", elements });
353
+ }
354
+ prop(object, key) {
355
+ return this.add({ id: this.nextId++, kind: "prop", object, key });
356
+ }
357
+ index(array, index) {
358
+ return this.add({ id: this.nextId++, kind: "index", array, index });
359
+ }
360
+ pureCall(calleeFunctionId, args2) {
361
+ return this.add({ id: this.nextId++, kind: "pure_call", calleeFunctionId, args: args2 });
362
+ }
363
+ template(parts) {
364
+ return this.add({ id: this.nextId++, kind: "template", parts });
365
+ }
366
+ add(expression) {
367
+ this.expressions.push(expression);
368
+ return expression.id;
369
+ }
370
+ };
371
+ var emptyPlan = (functionId) => ({
372
+ planId: `dp_${functionId}`,
373
+ functionId,
374
+ expressions: [],
375
+ variables: [],
376
+ captureBoundaries: [],
377
+ controlFacts: []
378
+ });
379
+
380
+ // src/derivation/analyze-function.ts
381
+ var loc = (node, sourceFile) => {
382
+ const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
383
+ return { line: position.line + 1, column: position.character + 1 };
384
+ };
385
+ var lookup = (ctx, name) => {
386
+ for (let index = ctx.scopes.length - 1; index >= 0; index--) {
387
+ const binding = ctx.scopes[index]?.get(name);
388
+ if (binding) return binding;
389
+ }
390
+ return void 0;
391
+ };
392
+ var fallback = (ctx, reason) => {
393
+ ctx.fallback ??= reason;
394
+ return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "unknown" };
395
+ };
396
+ var capture = (ctx, node, reason) => {
397
+ const rootId = ctx.nextRootId.value++;
398
+ const position = loc(node, ctx.sourceFile);
399
+ const expressionId = ctx.ir.root(rootId);
400
+ ctx.captureWraps.push({
401
+ rootId,
402
+ reason,
403
+ start: node.getStart(ctx.sourceFile),
404
+ end: node.end,
405
+ line: position.line,
406
+ column: position.column,
407
+ expressionId
408
+ });
409
+ return { expressionId, shape: "unknown", captured: true };
410
+ };
411
+ var binaryOp = (kind) => {
412
+ switch (kind) {
413
+ case ts.SyntaxKind.PlusToken:
414
+ return "add";
415
+ case ts.SyntaxKind.MinusToken:
416
+ return "sub";
417
+ case ts.SyntaxKind.AsteriskToken:
418
+ return "mul";
419
+ case ts.SyntaxKind.SlashToken:
420
+ return "div";
421
+ case ts.SyntaxKind.PercentToken:
422
+ return "mod";
423
+ case ts.SyntaxKind.EqualsEqualsToken:
424
+ return "eq";
425
+ case ts.SyntaxKind.ExclamationEqualsToken:
426
+ return "neq";
427
+ case ts.SyntaxKind.EqualsEqualsEqualsToken:
428
+ return "seq";
429
+ case ts.SyntaxKind.ExclamationEqualsEqualsToken:
430
+ return "sneq";
431
+ case ts.SyntaxKind.LessThanToken:
432
+ return "lt";
433
+ case ts.SyntaxKind.LessThanEqualsToken:
434
+ return "lte";
435
+ case ts.SyntaxKind.GreaterThanToken:
436
+ return "gt";
437
+ case ts.SyntaxKind.GreaterThanEqualsToken:
438
+ return "gte";
439
+ default:
440
+ return void 0;
441
+ }
442
+ };
443
+ var isNumericBinaryOp = (op) => op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod";
444
+ var unwrapParens = (node) => {
445
+ while (ts.isParenthesizedExpression(node)) node = node.expression;
446
+ return node;
447
+ };
448
+ var numericLiteralValue = (node) => {
449
+ const value = Number(node.text);
450
+ return { type: "number", value };
451
+ };
452
+ var analyzeExpression = (ctx, node) => {
453
+ if (ctx.fallback) return fallback(ctx, ctx.fallback);
454
+ if (ts.isParenthesizedExpression(node)) return analyzeExpression(ctx, node.expression);
455
+ if (ts.isAsExpression(node) || ts.isNonNullExpression(node)) {
456
+ return analyzeExpression(ctx, node.expression);
457
+ }
458
+ if (node.kind === ts.SyntaxKind.TypeAssertionExpression) {
459
+ return analyzeExpression(ctx, node.expression);
460
+ }
461
+ if (ts.isSatisfiesExpression(node)) return analyzeExpression(ctx, node.expression);
462
+ if (node.kind === ts.SyntaxKind.NullKeyword) {
463
+ return { expressionId: ctx.ir.constValue({ type: "null" }), shape: "primitive" };
464
+ }
465
+ if (node.kind === ts.SyntaxKind.TrueKeyword) {
466
+ return {
467
+ expressionId: ctx.ir.constValue({ type: "boolean", value: true }),
468
+ shape: "primitive"
469
+ };
470
+ }
471
+ if (node.kind === ts.SyntaxKind.FalseKeyword) {
472
+ return {
473
+ expressionId: ctx.ir.constValue({ type: "boolean", value: false }),
474
+ shape: "primitive"
475
+ };
476
+ }
477
+ if (ts.isNumericLiteral(node)) {
478
+ return { expressionId: ctx.ir.constValue(numericLiteralValue(node)), shape: "primitive" };
479
+ }
480
+ if (ts.isPrefixUnaryExpression(node) && ts.isNumericLiteral(node.operand) && node.operator === ts.SyntaxKind.MinusToken) {
481
+ const value = -Number(node.operand.text);
482
+ return {
483
+ expressionId: ctx.ir.constValue(
484
+ Object.is(value, -0) ? { type: "number", value: 0, negativeZero: true } : { type: "number", value }
485
+ ),
486
+ shape: "primitive"
487
+ };
488
+ }
489
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
490
+ return {
491
+ expressionId: ctx.ir.constValue({ type: "string", value: node.text }),
492
+ shape: "primitive"
493
+ };
494
+ }
495
+ if (ts.isBigIntLiteral(node)) {
496
+ return {
497
+ expressionId: ctx.ir.constValue({ type: "bigint", value: node.text.replace(/n$/, "") }),
498
+ shape: "primitive"
499
+ };
500
+ }
501
+ if (ts.isIdentifier(node)) {
502
+ if (node.text === "undefined") {
503
+ return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "primitive" };
504
+ }
505
+ const binding = lookup(ctx, node.text);
506
+ if (!binding) return capture(ctx, node, "external_state");
507
+ if (binding.mutable) return capture(ctx, node, "mutable_object");
508
+ return {
509
+ expressionId: ctx.ir.alias(binding.expressionId),
510
+ shape: binding.shape
511
+ };
512
+ }
513
+ if (ts.isTemplateExpression(node)) {
514
+ const parts = [
515
+ { kind: "lit", value: node.head.text }
516
+ ];
517
+ for (const span of node.templateSpans) {
518
+ const nested = analyzeExpression(ctx, span.expression);
519
+ if (nested.shape !== "primitive" && !nested.captured) {
520
+ return capture(ctx, node, "unsupported_expression");
521
+ }
522
+ parts.push({ kind: "expr", expressionId: nested.expressionId });
523
+ parts.push({ kind: "lit", value: span.literal.text });
524
+ }
525
+ return { expressionId: ctx.ir.template(parts), shape: "primitive" };
526
+ }
527
+ if (ts.isArrayLiteralExpression(node)) {
528
+ if (node.elements.some((element) => ts.isSpreadElement(element))) {
529
+ return capture(ctx, node, "unsupported_expression");
530
+ }
531
+ const elements = node.elements.map((element) => {
532
+ if (ts.isOmittedExpression(element)) return { kind: "hole" };
533
+ return analyzeExpression(ctx, element).expressionId;
534
+ });
535
+ return { expressionId: ctx.ir.array(elements), shape: "array" };
536
+ }
537
+ if (ts.isObjectLiteralExpression(node)) {
538
+ const properties = [];
539
+ for (const property of node.properties) {
540
+ if (ts.isShorthandPropertyAssignment(property)) {
541
+ const nested = analyzeExpression(ctx, property.name);
542
+ if (nested.captured) {
543
+ const wrap = ctx.captureWraps.find(
544
+ (candidate) => candidate.expressionId === nested.expressionId
545
+ );
546
+ if (wrap) wrap.shorthandPropertyName = property.name.text;
547
+ }
548
+ properties.push({ key: property.name.text, value: nested.expressionId });
549
+ continue;
550
+ }
551
+ if (!ts.isPropertyAssignment(property)) {
552
+ return capture(ctx, node, "unsupported_expression");
553
+ }
554
+ if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
555
+ return capture(ctx, node, "unsupported_expression");
556
+ }
557
+ const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
558
+ if (key === void 0) return capture(ctx, node, "unsupported_expression");
559
+ properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
560
+ }
561
+ return { expressionId: ctx.ir.object(properties), shape: "object" };
562
+ }
563
+ if (ts.isPropertyAccessExpression(node)) {
564
+ if (node.questionDotToken) return capture(ctx, node, "unknown_property");
565
+ const object = analyzeExpression(ctx, node.expression);
566
+ if (object.shape === "object" && ts.isIdentifier(node.name)) {
567
+ return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
568
+ }
569
+ return capture(ctx, node, "unknown_property");
570
+ }
571
+ if (ts.isElementAccessExpression(node)) {
572
+ if (node.questionDotToken) return capture(ctx, node, "unknown_property");
573
+ const array = analyzeExpression(ctx, node.expression);
574
+ if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
575
+ return {
576
+ expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
577
+ shape: "unknown"
578
+ };
579
+ }
580
+ return capture(ctx, node, "unknown_property");
581
+ }
582
+ if (ts.isCallExpression(node)) {
583
+ if (node.questionDotToken || node.typeArguments?.length) {
584
+ return capture(ctx, node, "unsupported_expression");
585
+ }
586
+ if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
587
+ const calleeName = node.expression.text;
588
+ if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
589
+ const provenId = ctx.moduleCallees.get(calleeName);
590
+ ctx.callEdges.push({
591
+ callerFunctionId: ctx.functionId,
592
+ calleeName,
593
+ ...provenId ? { calleeFunctionId: provenId } : {}
594
+ });
595
+ if (!provenId) return capture(ctx, node, "opaque_call");
596
+ const args2 = node.arguments.map((argument) => {
597
+ if (ts.isSpreadElement(argument)) return void 0;
598
+ return analyzeExpression(ctx, argument).expressionId;
599
+ });
600
+ if (args2.some((argument) => argument === void 0))
601
+ return capture(ctx, node, "unsupported_expression");
602
+ return {
603
+ expressionId: ctx.ir.pureCall(
604
+ provenId,
605
+ args2.filter((id) => id !== void 0)
606
+ ),
607
+ shape: "unknown"
608
+ };
609
+ }
610
+ if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
611
+ return capture(
612
+ ctx,
613
+ node,
614
+ node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
615
+ );
616
+ }
617
+ if (ts.isConditionalExpression(node)) {
618
+ const test = analyzeExpression(ctx, node.condition);
619
+ const consequent = analyzeExpression(ctx, node.whenTrue);
620
+ const alternate = analyzeExpression(ctx, node.whenFalse);
621
+ return {
622
+ expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
623
+ shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
624
+ };
625
+ }
626
+ if (ts.isBinaryExpression(node)) {
627
+ if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
628
+ ctx.fallback ??= "mutable_state";
629
+ return capture(ctx, node, "mutable_object");
630
+ }
631
+ if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
632
+ const left2 = analyzeExpression(ctx, node.left);
633
+ const right2 = analyzeExpression(ctx, node.right);
634
+ const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
635
+ return {
636
+ expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
637
+ shape: "unknown"
638
+ };
639
+ }
640
+ const op = binaryOp(node.operatorToken.kind);
641
+ if (!op) return capture(ctx, node, "unsupported_expression");
642
+ if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
643
+ const left = analyzeExpression(ctx, node.left);
644
+ const right = analyzeExpression(ctx, node.right);
645
+ const numeric = isNumericBinaryOp(op);
646
+ const expressionId = ctx.ir.binary(
647
+ op,
648
+ left.expressionId,
649
+ right.expressionId,
650
+ numeric ? "numeric" : "primitive"
651
+ );
652
+ if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
653
+ ctx.hasCoercionGuard = true;
654
+ const fallbackRootId = ctx.nextRootId.value++;
655
+ ctx.guardWraps.push({
656
+ fallbackRootId,
657
+ start: node.getStart(ctx.sourceFile),
658
+ end: node.end,
659
+ op
660
+ });
661
+ }
662
+ return { expressionId, shape: "primitive" };
663
+ }
664
+ if (ts.isPrefixUnaryExpression(node)) {
665
+ const operand = analyzeExpression(ctx, node.operand);
666
+ if (node.operator === ts.SyntaxKind.ExclamationToken) {
667
+ return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
668
+ }
669
+ if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
670
+ if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
671
+ return {
672
+ expressionId: ctx.ir.unary(
673
+ node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
674
+ operand.expressionId
675
+ ),
676
+ shape: "primitive"
677
+ };
678
+ }
679
+ return capture(ctx, node, "unsupported_expression");
680
+ }
681
+ if (ts.isTypeOfExpression(node)) {
682
+ const operand = unwrapParens(node.expression);
683
+ if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
684
+ return capture(ctx, node, "external_state");
685
+ }
686
+ const inner = analyzeExpression(ctx, node.expression);
687
+ return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
688
+ }
689
+ return capture(ctx, node, "unsupported_expression");
690
+ };
691
+ var bindIdentifier = (ctx, name, result2, declaration) => {
692
+ const current = ctx.scopes[ctx.scopes.length - 1];
693
+ current?.set(name.text, {
694
+ expressionId: result2.expressionId,
695
+ shape: result2.shape,
696
+ mutable: false
697
+ });
698
+ const position = loc(name, ctx.sourceFile);
699
+ const available = loc(declaration, ctx.sourceFile);
700
+ ctx.variables.push({
701
+ id: ctx.nextVariableId.value++,
702
+ name: name.text,
703
+ functionId: ctx.functionId,
704
+ scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
705
+ line: position.line,
706
+ column: position.column,
707
+ assignmentId: ctx.nextAssignmentId.value++,
708
+ expressionId: result2.expressionId,
709
+ availableAtLine: available.line,
710
+ availableAtColumn: available.column
711
+ });
712
+ };
713
+ var analyzeStatement = (ctx, node) => {
714
+ if (ctx.fallback) return;
715
+ if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
716
+ if (ts.isBlock(node)) {
717
+ ctx.scopes.push(/* @__PURE__ */ new Map());
718
+ for (const statement of node.statements) analyzeStatement(ctx, statement);
719
+ ctx.scopes.pop();
720
+ return;
721
+ }
722
+ if (ts.isReturnStatement(node)) {
723
+ if (!node.expression) {
724
+ ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
725
+ return;
726
+ }
727
+ ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
728
+ return;
729
+ }
730
+ if (ts.isVariableStatement(node)) {
731
+ if (node.declarationList.flags & ts.NodeFlags.Using) {
732
+ fallback(ctx, "side_effect");
733
+ return;
734
+ }
735
+ for (const declaration of node.declarationList.declarations) {
736
+ if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
737
+ fallback(ctx, "unsupported_expression");
738
+ return;
739
+ }
740
+ const result2 = analyzeExpression(ctx, declaration.initializer);
741
+ bindIdentifier(ctx, declaration.name, result2, declaration);
742
+ }
743
+ return;
744
+ }
745
+ if (ts.isExpressionStatement(node)) {
746
+ analyzeExpression(ctx, node.expression);
747
+ return;
748
+ }
749
+ if (ts.isThrowStatement(node)) {
750
+ ctx.hasThrow = true;
751
+ analyzeExpression(ctx, node.expression);
752
+ return;
753
+ }
754
+ if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
755
+ fallback(ctx, "unsupported_expression");
756
+ return;
757
+ }
758
+ if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
759
+ fallback(ctx, "has_loop");
760
+ return;
761
+ }
762
+ fallback(ctx, "unsupported_expression");
763
+ };
764
+ var bindParams = (ctx, node) => {
765
+ let index = 0;
766
+ for (const parameter of node.parameters) {
767
+ if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
768
+ if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
769
+ fallback(ctx, "unsupported_expression");
770
+ index++;
771
+ continue;
772
+ }
773
+ const expressionId = ctx.ir.param(index);
774
+ ctx.identifierParams.push(parameter.name.text);
775
+ bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
776
+ index++;
777
+ }
778
+ };
779
+ var analyzeOne = (node, functionId, name, sourceFile, moduleCallees) => {
780
+ const ctx = {
781
+ sourceFile,
782
+ functionId,
783
+ name,
784
+ ir: new DerivationIrBuilder(),
785
+ variables: [],
786
+ captureWraps: [],
787
+ guardWraps: [],
788
+ callEdges: [],
789
+ scopes: [/* @__PURE__ */ new Map()],
790
+ nextRootId: { value: 0 },
791
+ nextVariableId: { value: 0 },
792
+ nextAssignmentId: { value: 0 },
793
+ hasThrow: false,
794
+ hasCoercionGuard: false,
795
+ moduleCallees,
796
+ identifierParams: []
797
+ };
798
+ if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
799
+ ctx.fallback = "async";
800
+ } else {
801
+ bindParams(ctx, node);
802
+ if (ts.isBlock(node.body)) {
803
+ for (const statement of node.body.statements) analyzeStatement(ctx, statement);
804
+ } else {
805
+ ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
806
+ }
807
+ }
808
+ const plan = {
809
+ ...emptyPlan(functionId),
810
+ expressions: ctx.ir.expressions,
811
+ variables: ctx.variables,
812
+ captureBoundaries: ctx.captureWraps.map((wrap) => ({
813
+ id: wrap.rootId,
814
+ reason: wrap.reason,
815
+ expressionId: wrap.expressionId,
816
+ line: wrap.line,
817
+ column: wrap.column
818
+ })),
819
+ controlFacts: [],
820
+ ...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
821
+ ...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
822
+ };
823
+ return {
824
+ functionId,
825
+ name,
826
+ node,
827
+ plan,
828
+ captureWraps: ctx.fallback ? [] : ctx.captureWraps,
829
+ guardWraps: ctx.fallback ? [] : ctx.guardWraps,
830
+ callEdges: ctx.callEdges,
831
+ hasThrow: ctx.hasThrow,
832
+ hasCoercionGuard: ctx.hasCoercionGuard
833
+ };
834
+ };
835
+ var analyzeModuleFunctions = (sourceFile, targets) => {
836
+ const nameCounts = /* @__PURE__ */ new Map();
837
+ for (const target of targets) {
838
+ nameCounts.set(target.name, (nameCounts.get(target.name) ?? 0) + 1);
839
+ }
840
+ const firstPass = targets.map(
841
+ (target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
842
+ );
843
+ const graph = buildDependencyGraph(
844
+ firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
845
+ firstPass.flatMap((fn) => fn.callEdges)
846
+ );
847
+ const order = topologicalFunctionOrder(
848
+ firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
849
+ graph
850
+ );
851
+ const byId = new Map(targets.map((target) => [target.functionId, target]));
852
+ const moduleCallees = /* @__PURE__ */ new Map();
853
+ const analyzed = [];
854
+ for (const functionId of order) {
855
+ const target = byId.get(functionId);
856
+ if (!target) continue;
857
+ const result2 = analyzeOne(
858
+ target.node,
859
+ target.functionId,
860
+ target.name,
861
+ sourceFile,
862
+ moduleCallees
863
+ );
864
+ if (nameCounts.get(result2.name) === 1) {
865
+ moduleCallees.set(result2.name, result2.functionId);
866
+ }
867
+ analyzed.push(result2);
868
+ }
869
+ const remaining = targets.filter(
870
+ (target) => !analyzed.some((fn) => fn.functionId === target.functionId)
871
+ );
872
+ for (const target of remaining) {
873
+ analyzed.push(
874
+ analyzeOne(target.node, target.functionId, target.name, sourceFile, moduleCallees)
875
+ );
876
+ }
877
+ return analyzed;
878
+ };
879
+
880
+ // src/derivation/derivation-proof.ts
881
+ var functionIsExact = (plan) => plan.fallbackReason === void 0 && plan.captureBoundaries.length === 0 && plan.controlFacts.length === 0;
882
+
883
+ // src/derivation/emit-capture-plan.ts
884
+ var captureWrapEdits = (wraps, capName) => {
885
+ const edits = [];
886
+ let order = 0;
887
+ for (const wrap of wraps) {
888
+ edits.push({
889
+ position: wrap.start,
890
+ text: `${wrap.shorthandPropertyName ? `${wrap.shorthandPropertyName}:` : ""}${capName}(${wrap.rootId},`,
891
+ order: order++
892
+ });
893
+ edits.push({
894
+ position: wrap.end,
895
+ text: ")",
896
+ order: order++
897
+ });
898
+ }
899
+ return edits;
900
+ };
255
901
 
256
902
  // src/execution-recorder/automatic-runtime.ts
257
903
  var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
258
904
  var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
259
905
 
260
906
  // src/auto-instrumentation/collect-instrumentation-delta.ts
261
- import ts from "typescript";
907
+ import ts2 from "typescript";
262
908
  var paramBindingFromName = (name, sourceFile) => {
263
- if (ts.isIdentifier(name)) return { kind: "identifier", name: name.text };
264
- if (ts.isArrayBindingPattern(name)) {
909
+ if (ts2.isIdentifier(name)) return { kind: "identifier", name: name.text };
910
+ if (ts2.isArrayBindingPattern(name)) {
265
911
  const elements = name.elements.map(
266
912
  (element) => {
267
- if (ts.isOmittedExpression(element)) return { kind: "omitted" };
913
+ if (ts2.isOmittedExpression(element)) return { kind: "omitted" };
268
914
  const binding = paramBindingFromName(element.name, sourceFile);
269
915
  if (element.dotDotDotToken) return { kind: "rest", binding };
270
916
  return binding;
@@ -274,7 +920,7 @@ var paramBindingFromName = (name, sourceFile) => {
274
920
  }
275
921
  const properties = name.elements.map((element) => {
276
922
  const binding = paramBindingFromName(element.name, sourceFile);
277
- const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
923
+ const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts2.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
278
924
  if (element.dotDotDotToken) {
279
925
  const rest = { kind: "rest", binding };
280
926
  return { key, binding: rest };
@@ -286,7 +932,7 @@ var paramBindingFromName = (name, sourceFile) => {
286
932
  var collectFunctionParams = (node, sourceFile) => {
287
933
  const params = [];
288
934
  for (const parameter of node.parameters) {
289
- if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
935
+ if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
290
936
  const binding = paramBindingFromName(parameter.name, sourceFile);
291
937
  params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
292
938
  }
@@ -327,18 +973,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
327
973
  };
328
974
  var unwrapExpression = (expression) => {
329
975
  let current = expression;
330
- while (ts.isParenthesizedExpression(current) || ts.isNonNullExpression(current)) {
976
+ while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
331
977
  current = current.expression;
332
978
  }
333
- if (ts.isAwaitExpression(current)) return unwrapExpression(current.expression);
979
+ if (ts2.isAwaitExpression(current)) return unwrapExpression(current.expression);
334
980
  return current;
335
981
  };
336
982
  var calleeFromExpression = (expression, sourceFile) => {
337
983
  const current = unwrapExpression(expression);
338
- if (ts.isIdentifier(current)) {
984
+ if (ts2.isIdentifier(current)) {
339
985
  return { calleeName: current.text, calleeText: current.text };
340
986
  }
341
- if (ts.isPropertyAccessExpression(current) && ts.isIdentifier(current.name)) {
987
+ if (ts2.isPropertyAccessExpression(current) && ts2.isIdentifier(current.name)) {
342
988
  return {
343
989
  calleeName: current.name.text,
344
990
  calleeText: current.getText(sourceFile).replace(/\s+/g, "")
@@ -348,9 +994,9 @@ var calleeFromExpression = (expression, sourceFile) => {
348
994
  };
349
995
  var resultBindingForCall = (call) => {
350
996
  let node = call.parent;
351
- if (ts.isAwaitExpression(node)) node = node.parent;
352
- if (!ts.isVariableDeclaration(node)) return null;
353
- if (!ts.isIdentifier(node.name)) return null;
997
+ if (ts2.isAwaitExpression(node)) node = node.parent;
998
+ if (!ts2.isVariableDeclaration(node)) return null;
999
+ if (!ts2.isIdentifier(node.name)) return null;
354
1000
  const initializer = node.initializer;
355
1001
  if (!initializer) return null;
356
1002
  const inner = unwrapExpression(initializer);
@@ -360,18 +1006,18 @@ var resultBindingForCall = (call) => {
360
1006
  var argIdentifiersForCall = (call) => {
361
1007
  const identifiers = [];
362
1008
  call.arguments?.forEach((argument, index) => {
363
- if (ts.isIdentifier(argument)) {
1009
+ if (ts2.isIdentifier(argument)) {
364
1010
  identifiers.push({ index, kind: "identifier", name: argument.text });
365
1011
  return;
366
1012
  }
367
- if (!ts.isObjectLiteralExpression(argument)) return;
1013
+ if (!ts2.isObjectLiteralExpression(argument)) return;
368
1014
  const properties = [];
369
1015
  for (const property of argument.properties) {
370
- if (ts.isShorthandPropertyAssignment(property)) {
1016
+ if (ts2.isShorthandPropertyAssignment(property)) {
371
1017
  properties.push({ key: property.name.text, name: property.name.text });
372
1018
  continue;
373
1019
  }
374
- if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && ts.isIdentifier(property.initializer)) {
1020
+ if (ts2.isPropertyAssignment(property) && ts2.isIdentifier(property.name) && ts2.isIdentifier(property.initializer)) {
375
1021
  properties.push({ key: property.name.text, name: property.initializer.text });
376
1022
  }
377
1023
  }
@@ -411,8 +1057,8 @@ var collectCallSite = (call, caller, context) => {
411
1057
  };
412
1058
  };
413
1059
  var collectParamDestructure = (declaration, caller, context) => {
414
- if (!declaration.initializer || !ts.isIdentifier(declaration.initializer)) return null;
415
- if (!ts.isObjectBindingPattern(declaration.name) && !ts.isArrayBindingPattern(declaration.name)) {
1060
+ if (!declaration.initializer || !ts2.isIdentifier(declaration.initializer)) return null;
1061
+ if (!ts2.isObjectBindingPattern(declaration.name) && !ts2.isArrayBindingPattern(declaration.name)) {
416
1062
  return null;
417
1063
  }
418
1064
  const sourceParam = declaration.initializer.text;
@@ -436,33 +1082,33 @@ var collectParamDestructure = (declaration, caller, context) => {
436
1082
 
437
1083
  // src/auto-instrumentation/transform-source.ts
438
1084
  var scriptKindFor = (filePath) => {
439
- if (/\.tsx$/i.test(filePath)) return ts2.ScriptKind.TSX;
440
- if (/\.jsx$/i.test(filePath)) return ts2.ScriptKind.JSX;
441
- if (/\.[cm]?ts$/i.test(filePath)) return ts2.ScriptKind.TS;
442
- return ts2.ScriptKind.JS;
1085
+ if (/\.tsx$/i.test(filePath)) return ts3.ScriptKind.TSX;
1086
+ if (/\.jsx$/i.test(filePath)) return ts3.ScriptKind.JSX;
1087
+ if (/\.[cm]?ts$/i.test(filePath)) return ts3.ScriptKind.TS;
1088
+ return ts3.ScriptKind.JS;
443
1089
  };
444
1090
  var functionName = (node, sourceFile) => {
445
1091
  if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
446
1092
  const parent = node.parent;
447
- if (ts2.isVariableDeclaration(parent) && ts2.isIdentifier(parent.name)) return parent.name.text;
448
- if (ts2.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
449
- if (ts2.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
1093
+ if (ts3.isVariableDeclaration(parent) && ts3.isIdentifier(parent.name)) return parent.name.text;
1094
+ if (ts3.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
1095
+ if (ts3.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
450
1096
  const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
451
1097
  return `anonymous@${location.line + 1}:${location.character + 1}`;
452
1098
  };
453
1099
  var functionLocationPosition = (node, sourceFile) => {
454
1100
  if (node.name) return node.name.getStart(sourceFile);
455
1101
  const parent = node.parent;
456
- if ((ts2.isVariableDeclaration(parent) || ts2.isPropertyAssignment(parent)) && parent.name) {
1102
+ if ((ts3.isVariableDeclaration(parent) || ts3.isPropertyAssignment(parent)) && parent.name) {
457
1103
  return parent.name.getStart(sourceFile);
458
1104
  }
459
1105
  return node.getStart(sourceFile);
460
1106
  };
461
1107
  var bindingValue = (name, sourceFile) => {
462
- if (ts2.isIdentifier(name)) return name.text;
463
- if (ts2.isArrayBindingPattern(name)) {
1108
+ if (ts3.isIdentifier(name)) return name.text;
1109
+ if (ts3.isArrayBindingPattern(name)) {
464
1110
  const values = name.elements.map((element) => {
465
- if (ts2.isOmittedExpression(element)) return "";
1111
+ if (ts3.isOmittedExpression(element)) return "";
466
1112
  const value = bindingValue(element.name, sourceFile);
467
1113
  return element.dotDotDotToken ? `...${value}` : value;
468
1114
  });
@@ -478,14 +1124,14 @@ var bindingValue = (name, sourceFile) => {
478
1124
  };
479
1125
  var arrowArguments = (node) => {
480
1126
  const values = node.parameters.flatMap((parameter) => {
481
- if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
1127
+ if (ts3.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
482
1128
  const value = bindingValue(parameter.name, node.getSourceFile());
483
1129
  return [parameter.dotDotDotToken ? `...${value}` : value];
484
1130
  });
485
1131
  return `[${values.join(",")}]`;
486
1132
  };
487
1133
  var isSupportedFunction = (node) => {
488
- if (!ts2.isFunctionDeclaration(node) && !ts2.isFunctionExpression(node) && !ts2.isArrowFunction(node) && !ts2.isMethodDeclaration(node) && !ts2.isGetAccessorDeclaration(node) && !ts2.isSetAccessorDeclaration(node)) {
1134
+ if (!ts3.isFunctionDeclaration(node) && !ts3.isFunctionExpression(node) && !ts3.isArrowFunction(node) && !ts3.isMethodDeclaration(node) && !ts3.isGetAccessorDeclaration(node) && !ts3.isSetAccessorDeclaration(node)) {
489
1135
  return false;
490
1136
  }
491
1137
  if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
@@ -496,7 +1142,22 @@ var uniqueName = (source, base) => {
496
1142
  while (source.includes(name)) name += "_";
497
1143
  return name;
498
1144
  };
499
- 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)}));`;
1145
+ var isDirectivePrologue = (statement) => ts3.isExpressionStatement(statement) && ts3.isStringLiteral(statement.expression);
1146
+ var insertPositionAfterPrologue = (source, sourceFile) => {
1147
+ const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
1148
+ let end = shebangEnd;
1149
+ for (const statement of sourceFile.statements) {
1150
+ if (!isDirectivePrologue(statement)) break;
1151
+ end = statement.end;
1152
+ }
1153
+ return end;
1154
+ };
1155
+ var preambleInsert = (source, position, preamble) => {
1156
+ if (position <= 0) return preamble;
1157
+ const previous = source[position - 1];
1158
+ return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
1159
+ };
1160
+ 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)}));`;
500
1161
  var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
501
1162
  const start = mappedLocation(
502
1163
  sourceFile,
@@ -527,77 +1188,128 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
527
1188
  paramNames
528
1189
  };
529
1190
  };
1191
+ var canOmitCallValues = (analysis, analyzed) => {
1192
+ if (!functionIsExact(analysis.plan) || analysis.hasThrow || analysis.hasCoercionGuard) {
1193
+ return false;
1194
+ }
1195
+ const callers = analyzed.filter(
1196
+ (caller) => caller.callEdges.some((edge) => edge.calleeFunctionId === analysis.functionId)
1197
+ );
1198
+ if (callers.length === 0) return false;
1199
+ return callers.every((caller) => caller.plan.fallbackReason === void 0 && !caller.hasThrow);
1200
+ };
530
1201
  var transformSource = (source, options) => {
531
1202
  if (source.includes("@rasputin-ignore-file")) return void 0;
532
- const sourceFile = ts2.createSourceFile(
1203
+ const sourceFile = ts3.createSourceFile(
533
1204
  options.filePath,
534
1205
  source,
535
- ts2.ScriptTarget.Latest,
1206
+ ts3.ScriptTarget.Latest,
536
1207
  true,
537
1208
  scriptKindFor(options.filePath)
538
1209
  );
539
1210
  const runName = uniqueName(source, "__rasputinRun__");
1211
+ const capName = uniqueName(source, "__rasputinCap__");
540
1212
  const plansName = uniqueName(source, "__rasputinPlans__");
541
1213
  const absolutePath = resolve4(options.filePath);
1214
+ const derivationMode = options.derivationMode ?? "analysis";
542
1215
  const edits = [];
543
1216
  let order = 0;
544
1217
  const delta = {
545
1218
  functions: [],
546
1219
  callSites: [],
547
- destructures: []
1220
+ destructures: [],
1221
+ derivationPlans: []
548
1222
  };
549
1223
  const collectContext = {
550
1224
  sourceFile,
551
1225
  mapLocation: options.mapLocation,
552
1226
  absolutePath
553
1227
  };
1228
+ const pending = [];
554
1229
  const visit = (node, caller) => {
555
1230
  if (isSupportedFunction(node)) {
556
1231
  const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
557
1232
  if (!collected) {
558
- ts2.forEachChild(node, (child) => visit(child, caller));
1233
+ ts3.forEachChild(node, (child) => visit(child, caller));
559
1234
  return;
560
1235
  }
561
- const planIndex = delta.functions.length;
1236
+ pending.push({ node, collected });
562
1237
  delta.functions.push(collected);
563
- const args2 = ts2.isArrowFunction(node) ? arrowArguments(node) : "arguments";
564
- const asyncCallback = node.modifiers?.some(
565
- (modifier) => modifier.kind === ts2.SyntaxKind.AsyncKeyword
566
- ) ? "async " : "";
567
- if (ts2.isBlock(node.body)) {
568
- edits.push({
569
- position: node.body.getStart(sourceFile) + 1,
570
- text: `return ${runName}(${planIndex},${args2},${asyncCallback}()=>{`,
571
- order: order++
572
- });
573
- edits.push({ position: node.body.end - 1, text: "});", order: order++ });
574
- } else {
575
- edits.push({
576
- position: node.body.getStart(sourceFile),
577
- text: `${runName}(${planIndex},${args2},${asyncCallback}()=>(`,
578
- order: order++
579
- });
580
- edits.push({ position: node.body.end, text: "))", order: order++ });
581
- }
582
- ts2.forEachChild(node, (child) => visit(child, collected));
1238
+ ts3.forEachChild(node, (child) => visit(child, collected));
583
1239
  return;
584
1240
  }
585
- if (caller && (ts2.isCallExpression(node) || ts2.isNewExpression(node))) {
1241
+ if (caller && (ts3.isCallExpression(node) || ts3.isNewExpression(node))) {
586
1242
  const site = collectCallSite(node, caller, collectContext);
587
1243
  if (site) delta.callSites.push(site);
588
1244
  }
589
- if (caller && ts2.isVariableDeclaration(node)) {
1245
+ if (caller && ts3.isVariableDeclaration(node)) {
590
1246
  const destructure = collectParamDestructure(node, caller, collectContext);
591
1247
  if (destructure) delta.destructures.push(destructure);
592
1248
  }
593
- ts2.forEachChild(node, (child) => visit(child, caller));
1249
+ ts3.forEachChild(node, (child) => visit(child, caller));
594
1250
  };
595
1251
  visit(sourceFile);
596
- if (edits.length === 0) return void 0;
597
- const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
1252
+ if (pending.length === 0) return void 0;
1253
+ const analyzed = analyzeModuleFunctions(
1254
+ sourceFile,
1255
+ pending.map((entry) => ({
1256
+ functionId: entry.collected.functionId,
1257
+ name: entry.collected.name,
1258
+ node: entry.node
1259
+ }))
1260
+ );
1261
+ delta.derivationPlans = analyzed.map((fn) => fn.plan);
1262
+ const analyzedById = new Map(analyzed.map((fn) => [fn.functionId, fn]));
1263
+ for (const [planIndex, entry] of pending.entries()) {
1264
+ const analysis = analyzedById.get(entry.collected.functionId);
1265
+ const omitValues = derivationMode === "selective" && analysis && canOmitCallValues(analysis, analyzed);
1266
+ const valueFlag = omitValues ? ",1" : "";
1267
+ const args2 = ts3.isArrowFunction(entry.node) ? arrowArguments(entry.node) : "arguments";
1268
+ const asyncCallback = entry.node.modifiers?.some(
1269
+ (modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword
1270
+ ) ? "async " : "";
1271
+ if (ts3.isBlock(entry.node.body)) {
1272
+ edits.push({
1273
+ position: entry.node.body.getStart(sourceFile) + 1,
1274
+ text: `return ${runName}(${planIndex},${args2},${asyncCallback}()=>{`,
1275
+ order: order++
1276
+ });
1277
+ edits.push({
1278
+ position: entry.node.body.end - 1,
1279
+ text: `}${valueFlag});`,
1280
+ order: order++
1281
+ });
1282
+ } else {
1283
+ edits.push({
1284
+ position: entry.node.body.getStart(sourceFile),
1285
+ text: `${runName}(${planIndex},${args2},${asyncCallback}()=>(`,
1286
+ order: order++
1287
+ });
1288
+ edits.push({
1289
+ position: entry.node.body.end,
1290
+ text: `)${valueFlag})`,
1291
+ order: order++
1292
+ });
1293
+ }
1294
+ if (analysis) {
1295
+ for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
1296
+ edits.push({ ...edit, order: order++ });
1297
+ }
1298
+ }
1299
+ }
1300
+ const insertAt = insertPositionAfterPrologue(source, sourceFile);
598
1301
  edits.push({
599
- position: shebangEnd,
600
- text: modulePreamble(runName, plansName, JSON.stringify(delta.functions.map((fn) => fn.plan))),
1302
+ position: insertAt,
1303
+ text: preambleInsert(
1304
+ source,
1305
+ insertAt,
1306
+ modulePreamble(
1307
+ runName,
1308
+ capName,
1309
+ plansName,
1310
+ JSON.stringify(delta.functions.map((fn) => fn.plan))
1311
+ )
1312
+ ),
601
1313
  order: -1
602
1314
  });
603
1315
  edits.sort((left, right) => right.position - left.position || right.order - left.order);
@@ -648,7 +1360,8 @@ var instrumentCompiledDirectory = (options) => {
648
1360
  if (source.includes("__rasputinRun__") || source.includes("__rasputinPlans__")) continue;
649
1361
  const transformed = transformSource(source, {
650
1362
  filePath,
651
- mapLocation: createSourceMapLocationMapper(filePath, source)
1363
+ mapLocation: createSourceMapLocationMapper(filePath, source),
1364
+ ...options.derivationMode ? { derivationMode: options.derivationMode } : {}
652
1365
  });
653
1366
  if (!transformed) continue;
654
1367
  const outputPath = join2(outputDir, relative2(inputDir, filePath));
@@ -674,7 +1387,15 @@ var sdkPackageScopeRootFrom = (instrumentEntryUrl) => resolve6(dirname3(fileURLT
674
1387
 
675
1388
  // src/instrument/build.ts
676
1389
  var printUsage = () => {
677
- console.error("Usage: rasputin-instrument <input-dir> [--out <output-dir>] [--manifest <path>]");
1390
+ console.error(
1391
+ "Usage: rasputin-instrument <input-dir> [--out <output-dir>] [--manifest <path>] [--derivation-mode analysis|selective]"
1392
+ );
1393
+ };
1394
+ var parseDerivationMode = (value) => {
1395
+ if (value === void 0) return void 0;
1396
+ if (value === "analysis" || value === "selective") return value;
1397
+ console.error(`[rasputin] unknown --derivation-mode ${value} (expected analysis or selective)`);
1398
+ process.exit(1);
678
1399
  };
679
1400
  var parseArgs = (argv) => {
680
1401
  const inputDir = argv.find((argument) => !argument.startsWith("--"));
@@ -685,7 +1406,8 @@ var parseArgs = (argv) => {
685
1406
  return {
686
1407
  inputDir,
687
1408
  outputDir: option("--out"),
688
- manifestPath: option("--manifest")
1409
+ manifestPath: option("--manifest"),
1410
+ derivationMode: parseDerivationMode(option("--derivation-mode"))
689
1411
  };
690
1412
  };
691
1413
  var args = parseArgs(process.argv.slice(2));
@@ -697,7 +1419,8 @@ var result = instrumentCompiledDirectory({
697
1419
  inputDir: args.inputDir,
698
1420
  outputDir: args.outputDir,
699
1421
  manifestPath: args.manifestPath,
700
- excludedRoots: [sdkPackageScopeRootFrom(import.meta.url)]
1422
+ excludedRoots: [sdkPackageScopeRootFrom(import.meta.url)],
1423
+ ...args.derivationMode ? { derivationMode: args.derivationMode } : {}
701
1424
  });
702
1425
  console.info(
703
1426
  `[rasputin] instrumented ${result.transformedFiles.length} file${result.transformedFiles.length === 1 ? "" : "s"}`