@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
@@ -31,7 +31,7 @@ var encodeFunctionPlan = (source) => {
31
31
  };
32
32
 
33
33
  // src/auto-instrumentation/instrumentation-manifest-registry.ts
34
- var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v2";
34
+ var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v3";
35
35
  var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
36
36
  var getRegistry = () => {
37
37
  const existing = Reflect.get(globalThis, registrySymbol);
@@ -143,20 +143,663 @@ var createSourceMapLocationMapper = (generatedPath, source) => {
143
143
 
144
144
  // src/auto-instrumentation/transform-source.ts
145
145
  import { resolve as resolve4 } from "node:path";
146
- import ts2 from "typescript";
146
+ import ts3 from "typescript";
147
+
148
+ // src/derivation/analyze-function.ts
149
+ import ts from "typescript";
150
+
151
+ // src/derivation/build-dependency-graph.ts
152
+ var buildDependencyGraph = (functions, edges) => {
153
+ const names = new Set(functions.map((fn) => fn.name));
154
+ const localEdges = edges.filter((edge) => names.has(edge.calleeName));
155
+ const calleesByCaller = /* @__PURE__ */ new Map();
156
+ for (const edge of localEdges) {
157
+ const existing = calleesByCaller.get(edge.callerFunctionId) ?? [];
158
+ existing.push(edge.calleeName);
159
+ calleesByCaller.set(edge.callerFunctionId, existing);
160
+ }
161
+ return {
162
+ functions: functions.map((fn) => fn.functionId),
163
+ edges: localEdges,
164
+ calleesByCaller
165
+ };
166
+ };
167
+ var topologicalFunctionOrder = (functions, graph) => {
168
+ const idByName = new Map(functions.map((fn) => [fn.name, fn.functionId]));
169
+ const remainingCallees = new Map(
170
+ functions.map((fn) => {
171
+ const unique = new Set(
172
+ (graph.calleesByCaller.get(fn.functionId) ?? []).map((name) => idByName.get(name)).filter((id) => Boolean(id) && id !== fn.functionId)
173
+ );
174
+ return [fn.functionId, unique.size];
175
+ })
176
+ );
177
+ const callersOf = /* @__PURE__ */ new Map();
178
+ for (const fn of functions) {
179
+ for (const calleeName of graph.calleesByCaller.get(fn.functionId) ?? []) {
180
+ const calleeId = idByName.get(calleeName);
181
+ if (!calleeId || calleeId === fn.functionId) continue;
182
+ const callers = callersOf.get(calleeId) ?? [];
183
+ callers.push(fn.functionId);
184
+ callersOf.set(calleeId, callers);
185
+ }
186
+ }
187
+ const queue = functions.filter((fn) => (remainingCallees.get(fn.functionId) ?? 0) === 0).map((fn) => fn.functionId);
188
+ const ordered = [];
189
+ const queued = new Set(queue);
190
+ while (queue.length > 0) {
191
+ const current = queue.shift();
192
+ if (current === void 0) break;
193
+ ordered.push(current);
194
+ for (const caller of callersOf.get(current) ?? []) {
195
+ const next = (remainingCallees.get(caller) ?? 0) - 1;
196
+ remainingCallees.set(caller, next);
197
+ if (next === 0 && !queued.has(caller)) {
198
+ queued.add(caller);
199
+ queue.push(caller);
200
+ }
201
+ }
202
+ }
203
+ for (const fn of functions) {
204
+ if (!queued.has(fn.functionId)) ordered.push(fn.functionId);
205
+ }
206
+ return ordered;
207
+ };
208
+
209
+ // src/derivation/derivation-ir.ts
210
+ var DerivationIrBuilder = class {
211
+ expressions = [];
212
+ nextId = 0;
213
+ constValue(value) {
214
+ return this.add({ id: this.nextId++, kind: "const", value });
215
+ }
216
+ param(index) {
217
+ return this.add({ id: this.nextId++, kind: "param", index });
218
+ }
219
+ root(rootId) {
220
+ return this.add({ id: this.nextId++, kind: "root", rootId });
221
+ }
222
+ alias(from) {
223
+ return this.add({ id: this.nextId++, kind: "alias", from });
224
+ }
225
+ binary(op, left, right, guard) {
226
+ return this.add({ id: this.nextId++, kind: "binary", op, left, right, guard });
227
+ }
228
+ unary(op, operand) {
229
+ return this.add({ id: this.nextId++, kind: "unary", op, operand });
230
+ }
231
+ logical(op, left, right) {
232
+ return this.add({ id: this.nextId++, kind: "logical", op, left, right });
233
+ }
234
+ cond(test, consequent, alternate) {
235
+ return this.add({ id: this.nextId++, kind: "cond", test, consequent, alternate });
236
+ }
237
+ object(properties) {
238
+ return this.add({ id: this.nextId++, kind: "object", properties });
239
+ }
240
+ array(elements) {
241
+ return this.add({ id: this.nextId++, kind: "array", elements });
242
+ }
243
+ prop(object, key) {
244
+ return this.add({ id: this.nextId++, kind: "prop", object, key });
245
+ }
246
+ index(array, index) {
247
+ return this.add({ id: this.nextId++, kind: "index", array, index });
248
+ }
249
+ pureCall(calleeFunctionId, args) {
250
+ return this.add({ id: this.nextId++, kind: "pure_call", calleeFunctionId, args });
251
+ }
252
+ template(parts) {
253
+ return this.add({ id: this.nextId++, kind: "template", parts });
254
+ }
255
+ add(expression) {
256
+ this.expressions.push(expression);
257
+ return expression.id;
258
+ }
259
+ };
260
+ var emptyPlan = (functionId) => ({
261
+ planId: `dp_${functionId}`,
262
+ functionId,
263
+ expressions: [],
264
+ variables: [],
265
+ captureBoundaries: [],
266
+ controlFacts: []
267
+ });
268
+
269
+ // src/derivation/analyze-function.ts
270
+ var loc = (node, sourceFile) => {
271
+ const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
272
+ return { line: position.line + 1, column: position.character + 1 };
273
+ };
274
+ var lookup = (ctx, name) => {
275
+ for (let index = ctx.scopes.length - 1; index >= 0; index--) {
276
+ const binding = ctx.scopes[index]?.get(name);
277
+ if (binding) return binding;
278
+ }
279
+ return void 0;
280
+ };
281
+ var fallback = (ctx, reason) => {
282
+ ctx.fallback ??= reason;
283
+ return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "unknown" };
284
+ };
285
+ var capture = (ctx, node, reason) => {
286
+ const rootId = ctx.nextRootId.value++;
287
+ const position = loc(node, ctx.sourceFile);
288
+ const expressionId = ctx.ir.root(rootId);
289
+ ctx.captureWraps.push({
290
+ rootId,
291
+ reason,
292
+ start: node.getStart(ctx.sourceFile),
293
+ end: node.end,
294
+ line: position.line,
295
+ column: position.column,
296
+ expressionId
297
+ });
298
+ return { expressionId, shape: "unknown", captured: true };
299
+ };
300
+ var binaryOp = (kind) => {
301
+ switch (kind) {
302
+ case ts.SyntaxKind.PlusToken:
303
+ return "add";
304
+ case ts.SyntaxKind.MinusToken:
305
+ return "sub";
306
+ case ts.SyntaxKind.AsteriskToken:
307
+ return "mul";
308
+ case ts.SyntaxKind.SlashToken:
309
+ return "div";
310
+ case ts.SyntaxKind.PercentToken:
311
+ return "mod";
312
+ case ts.SyntaxKind.EqualsEqualsToken:
313
+ return "eq";
314
+ case ts.SyntaxKind.ExclamationEqualsToken:
315
+ return "neq";
316
+ case ts.SyntaxKind.EqualsEqualsEqualsToken:
317
+ return "seq";
318
+ case ts.SyntaxKind.ExclamationEqualsEqualsToken:
319
+ return "sneq";
320
+ case ts.SyntaxKind.LessThanToken:
321
+ return "lt";
322
+ case ts.SyntaxKind.LessThanEqualsToken:
323
+ return "lte";
324
+ case ts.SyntaxKind.GreaterThanToken:
325
+ return "gt";
326
+ case ts.SyntaxKind.GreaterThanEqualsToken:
327
+ return "gte";
328
+ default:
329
+ return void 0;
330
+ }
331
+ };
332
+ var isNumericBinaryOp = (op) => op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod";
333
+ var unwrapParens = (node) => {
334
+ while (ts.isParenthesizedExpression(node)) node = node.expression;
335
+ return node;
336
+ };
337
+ var numericLiteralValue = (node) => {
338
+ const value = Number(node.text);
339
+ return { type: "number", value };
340
+ };
341
+ var analyzeExpression = (ctx, node) => {
342
+ if (ctx.fallback) return fallback(ctx, ctx.fallback);
343
+ if (ts.isParenthesizedExpression(node)) return analyzeExpression(ctx, node.expression);
344
+ if (ts.isAsExpression(node) || ts.isNonNullExpression(node)) {
345
+ return analyzeExpression(ctx, node.expression);
346
+ }
347
+ if (node.kind === ts.SyntaxKind.TypeAssertionExpression) {
348
+ return analyzeExpression(ctx, node.expression);
349
+ }
350
+ if (ts.isSatisfiesExpression(node)) return analyzeExpression(ctx, node.expression);
351
+ if (node.kind === ts.SyntaxKind.NullKeyword) {
352
+ return { expressionId: ctx.ir.constValue({ type: "null" }), shape: "primitive" };
353
+ }
354
+ if (node.kind === ts.SyntaxKind.TrueKeyword) {
355
+ return {
356
+ expressionId: ctx.ir.constValue({ type: "boolean", value: true }),
357
+ shape: "primitive"
358
+ };
359
+ }
360
+ if (node.kind === ts.SyntaxKind.FalseKeyword) {
361
+ return {
362
+ expressionId: ctx.ir.constValue({ type: "boolean", value: false }),
363
+ shape: "primitive"
364
+ };
365
+ }
366
+ if (ts.isNumericLiteral(node)) {
367
+ return { expressionId: ctx.ir.constValue(numericLiteralValue(node)), shape: "primitive" };
368
+ }
369
+ if (ts.isPrefixUnaryExpression(node) && ts.isNumericLiteral(node.operand) && node.operator === ts.SyntaxKind.MinusToken) {
370
+ const value = -Number(node.operand.text);
371
+ return {
372
+ expressionId: ctx.ir.constValue(
373
+ Object.is(value, -0) ? { type: "number", value: 0, negativeZero: true } : { type: "number", value }
374
+ ),
375
+ shape: "primitive"
376
+ };
377
+ }
378
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
379
+ return {
380
+ expressionId: ctx.ir.constValue({ type: "string", value: node.text }),
381
+ shape: "primitive"
382
+ };
383
+ }
384
+ if (ts.isBigIntLiteral(node)) {
385
+ return {
386
+ expressionId: ctx.ir.constValue({ type: "bigint", value: node.text.replace(/n$/, "") }),
387
+ shape: "primitive"
388
+ };
389
+ }
390
+ if (ts.isIdentifier(node)) {
391
+ if (node.text === "undefined") {
392
+ return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "primitive" };
393
+ }
394
+ const binding = lookup(ctx, node.text);
395
+ if (!binding) return capture(ctx, node, "external_state");
396
+ if (binding.mutable) return capture(ctx, node, "mutable_object");
397
+ return {
398
+ expressionId: ctx.ir.alias(binding.expressionId),
399
+ shape: binding.shape
400
+ };
401
+ }
402
+ if (ts.isTemplateExpression(node)) {
403
+ const parts = [
404
+ { kind: "lit", value: node.head.text }
405
+ ];
406
+ for (const span of node.templateSpans) {
407
+ const nested = analyzeExpression(ctx, span.expression);
408
+ if (nested.shape !== "primitive" && !nested.captured) {
409
+ return capture(ctx, node, "unsupported_expression");
410
+ }
411
+ parts.push({ kind: "expr", expressionId: nested.expressionId });
412
+ parts.push({ kind: "lit", value: span.literal.text });
413
+ }
414
+ return { expressionId: ctx.ir.template(parts), shape: "primitive" };
415
+ }
416
+ if (ts.isArrayLiteralExpression(node)) {
417
+ if (node.elements.some((element) => ts.isSpreadElement(element))) {
418
+ return capture(ctx, node, "unsupported_expression");
419
+ }
420
+ const elements = node.elements.map((element) => {
421
+ if (ts.isOmittedExpression(element)) return { kind: "hole" };
422
+ return analyzeExpression(ctx, element).expressionId;
423
+ });
424
+ return { expressionId: ctx.ir.array(elements), shape: "array" };
425
+ }
426
+ if (ts.isObjectLiteralExpression(node)) {
427
+ const properties = [];
428
+ for (const property of node.properties) {
429
+ if (ts.isShorthandPropertyAssignment(property)) {
430
+ const nested = analyzeExpression(ctx, property.name);
431
+ if (nested.captured) {
432
+ const wrap = ctx.captureWraps.find(
433
+ (candidate) => candidate.expressionId === nested.expressionId
434
+ );
435
+ if (wrap) wrap.shorthandPropertyName = property.name.text;
436
+ }
437
+ properties.push({ key: property.name.text, value: nested.expressionId });
438
+ continue;
439
+ }
440
+ if (!ts.isPropertyAssignment(property)) {
441
+ return capture(ctx, node, "unsupported_expression");
442
+ }
443
+ if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
444
+ return capture(ctx, node, "unsupported_expression");
445
+ }
446
+ const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
447
+ if (key === void 0) return capture(ctx, node, "unsupported_expression");
448
+ properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
449
+ }
450
+ return { expressionId: ctx.ir.object(properties), shape: "object" };
451
+ }
452
+ if (ts.isPropertyAccessExpression(node)) {
453
+ if (node.questionDotToken) return capture(ctx, node, "unknown_property");
454
+ const object = analyzeExpression(ctx, node.expression);
455
+ if (object.shape === "object" && ts.isIdentifier(node.name)) {
456
+ return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
457
+ }
458
+ return capture(ctx, node, "unknown_property");
459
+ }
460
+ if (ts.isElementAccessExpression(node)) {
461
+ if (node.questionDotToken) return capture(ctx, node, "unknown_property");
462
+ const array = analyzeExpression(ctx, node.expression);
463
+ if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
464
+ return {
465
+ expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
466
+ shape: "unknown"
467
+ };
468
+ }
469
+ return capture(ctx, node, "unknown_property");
470
+ }
471
+ if (ts.isCallExpression(node)) {
472
+ if (node.questionDotToken || node.typeArguments?.length) {
473
+ return capture(ctx, node, "unsupported_expression");
474
+ }
475
+ if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
476
+ const calleeName = node.expression.text;
477
+ if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
478
+ const provenId = ctx.moduleCallees.get(calleeName);
479
+ ctx.callEdges.push({
480
+ callerFunctionId: ctx.functionId,
481
+ calleeName,
482
+ ...provenId ? { calleeFunctionId: provenId } : {}
483
+ });
484
+ if (!provenId) return capture(ctx, node, "opaque_call");
485
+ const args = node.arguments.map((argument) => {
486
+ if (ts.isSpreadElement(argument)) return void 0;
487
+ return analyzeExpression(ctx, argument).expressionId;
488
+ });
489
+ if (args.some((argument) => argument === void 0))
490
+ return capture(ctx, node, "unsupported_expression");
491
+ return {
492
+ expressionId: ctx.ir.pureCall(
493
+ provenId,
494
+ args.filter((id) => id !== void 0)
495
+ ),
496
+ shape: "unknown"
497
+ };
498
+ }
499
+ if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
500
+ return capture(
501
+ ctx,
502
+ node,
503
+ node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
504
+ );
505
+ }
506
+ if (ts.isConditionalExpression(node)) {
507
+ const test = analyzeExpression(ctx, node.condition);
508
+ const consequent = analyzeExpression(ctx, node.whenTrue);
509
+ const alternate = analyzeExpression(ctx, node.whenFalse);
510
+ return {
511
+ expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
512
+ shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
513
+ };
514
+ }
515
+ if (ts.isBinaryExpression(node)) {
516
+ if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
517
+ ctx.fallback ??= "mutable_state";
518
+ return capture(ctx, node, "mutable_object");
519
+ }
520
+ if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
521
+ const left2 = analyzeExpression(ctx, node.left);
522
+ const right2 = analyzeExpression(ctx, node.right);
523
+ const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
524
+ return {
525
+ expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
526
+ shape: "unknown"
527
+ };
528
+ }
529
+ const op = binaryOp(node.operatorToken.kind);
530
+ if (!op) return capture(ctx, node, "unsupported_expression");
531
+ if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
532
+ const left = analyzeExpression(ctx, node.left);
533
+ const right = analyzeExpression(ctx, node.right);
534
+ const numeric = isNumericBinaryOp(op);
535
+ const expressionId = ctx.ir.binary(
536
+ op,
537
+ left.expressionId,
538
+ right.expressionId,
539
+ numeric ? "numeric" : "primitive"
540
+ );
541
+ if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
542
+ ctx.hasCoercionGuard = true;
543
+ const fallbackRootId = ctx.nextRootId.value++;
544
+ ctx.guardWraps.push({
545
+ fallbackRootId,
546
+ start: node.getStart(ctx.sourceFile),
547
+ end: node.end,
548
+ op
549
+ });
550
+ }
551
+ return { expressionId, shape: "primitive" };
552
+ }
553
+ if (ts.isPrefixUnaryExpression(node)) {
554
+ const operand = analyzeExpression(ctx, node.operand);
555
+ if (node.operator === ts.SyntaxKind.ExclamationToken) {
556
+ return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
557
+ }
558
+ if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
559
+ if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
560
+ return {
561
+ expressionId: ctx.ir.unary(
562
+ node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
563
+ operand.expressionId
564
+ ),
565
+ shape: "primitive"
566
+ };
567
+ }
568
+ return capture(ctx, node, "unsupported_expression");
569
+ }
570
+ if (ts.isTypeOfExpression(node)) {
571
+ const operand = unwrapParens(node.expression);
572
+ if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
573
+ return capture(ctx, node, "external_state");
574
+ }
575
+ const inner = analyzeExpression(ctx, node.expression);
576
+ return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
577
+ }
578
+ return capture(ctx, node, "unsupported_expression");
579
+ };
580
+ var bindIdentifier = (ctx, name, result, declaration) => {
581
+ const current = ctx.scopes[ctx.scopes.length - 1];
582
+ current?.set(name.text, {
583
+ expressionId: result.expressionId,
584
+ shape: result.shape,
585
+ mutable: false
586
+ });
587
+ const position = loc(name, ctx.sourceFile);
588
+ const available = loc(declaration, ctx.sourceFile);
589
+ ctx.variables.push({
590
+ id: ctx.nextVariableId.value++,
591
+ name: name.text,
592
+ functionId: ctx.functionId,
593
+ scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
594
+ line: position.line,
595
+ column: position.column,
596
+ assignmentId: ctx.nextAssignmentId.value++,
597
+ expressionId: result.expressionId,
598
+ availableAtLine: available.line,
599
+ availableAtColumn: available.column
600
+ });
601
+ };
602
+ var analyzeStatement = (ctx, node) => {
603
+ if (ctx.fallback) return;
604
+ if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
605
+ if (ts.isBlock(node)) {
606
+ ctx.scopes.push(/* @__PURE__ */ new Map());
607
+ for (const statement of node.statements) analyzeStatement(ctx, statement);
608
+ ctx.scopes.pop();
609
+ return;
610
+ }
611
+ if (ts.isReturnStatement(node)) {
612
+ if (!node.expression) {
613
+ ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
614
+ return;
615
+ }
616
+ ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
617
+ return;
618
+ }
619
+ if (ts.isVariableStatement(node)) {
620
+ if (node.declarationList.flags & ts.NodeFlags.Using) {
621
+ fallback(ctx, "side_effect");
622
+ return;
623
+ }
624
+ for (const declaration of node.declarationList.declarations) {
625
+ if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
626
+ fallback(ctx, "unsupported_expression");
627
+ return;
628
+ }
629
+ const result = analyzeExpression(ctx, declaration.initializer);
630
+ bindIdentifier(ctx, declaration.name, result, declaration);
631
+ }
632
+ return;
633
+ }
634
+ if (ts.isExpressionStatement(node)) {
635
+ analyzeExpression(ctx, node.expression);
636
+ return;
637
+ }
638
+ if (ts.isThrowStatement(node)) {
639
+ ctx.hasThrow = true;
640
+ analyzeExpression(ctx, node.expression);
641
+ return;
642
+ }
643
+ if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
644
+ fallback(ctx, "unsupported_expression");
645
+ return;
646
+ }
647
+ if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
648
+ fallback(ctx, "has_loop");
649
+ return;
650
+ }
651
+ fallback(ctx, "unsupported_expression");
652
+ };
653
+ var bindParams = (ctx, node) => {
654
+ let index = 0;
655
+ for (const parameter of node.parameters) {
656
+ if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
657
+ if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
658
+ fallback(ctx, "unsupported_expression");
659
+ index++;
660
+ continue;
661
+ }
662
+ const expressionId = ctx.ir.param(index);
663
+ ctx.identifierParams.push(parameter.name.text);
664
+ bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
665
+ index++;
666
+ }
667
+ };
668
+ var analyzeOne = (node, functionId, name, sourceFile, moduleCallees) => {
669
+ const ctx = {
670
+ sourceFile,
671
+ functionId,
672
+ name,
673
+ ir: new DerivationIrBuilder(),
674
+ variables: [],
675
+ captureWraps: [],
676
+ guardWraps: [],
677
+ callEdges: [],
678
+ scopes: [/* @__PURE__ */ new Map()],
679
+ nextRootId: { value: 0 },
680
+ nextVariableId: { value: 0 },
681
+ nextAssignmentId: { value: 0 },
682
+ hasThrow: false,
683
+ hasCoercionGuard: false,
684
+ moduleCallees,
685
+ identifierParams: []
686
+ };
687
+ if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
688
+ ctx.fallback = "async";
689
+ } else {
690
+ bindParams(ctx, node);
691
+ if (ts.isBlock(node.body)) {
692
+ for (const statement of node.body.statements) analyzeStatement(ctx, statement);
693
+ } else {
694
+ ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
695
+ }
696
+ }
697
+ const plan = {
698
+ ...emptyPlan(functionId),
699
+ expressions: ctx.ir.expressions,
700
+ variables: ctx.variables,
701
+ captureBoundaries: ctx.captureWraps.map((wrap) => ({
702
+ id: wrap.rootId,
703
+ reason: wrap.reason,
704
+ expressionId: wrap.expressionId,
705
+ line: wrap.line,
706
+ column: wrap.column
707
+ })),
708
+ controlFacts: [],
709
+ ...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
710
+ ...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
711
+ };
712
+ return {
713
+ functionId,
714
+ name,
715
+ node,
716
+ plan,
717
+ captureWraps: ctx.fallback ? [] : ctx.captureWraps,
718
+ guardWraps: ctx.fallback ? [] : ctx.guardWraps,
719
+ callEdges: ctx.callEdges,
720
+ hasThrow: ctx.hasThrow,
721
+ hasCoercionGuard: ctx.hasCoercionGuard
722
+ };
723
+ };
724
+ var analyzeModuleFunctions = (sourceFile, targets) => {
725
+ const nameCounts = /* @__PURE__ */ new Map();
726
+ for (const target of targets) {
727
+ nameCounts.set(target.name, (nameCounts.get(target.name) ?? 0) + 1);
728
+ }
729
+ const firstPass = targets.map(
730
+ (target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
731
+ );
732
+ const graph = buildDependencyGraph(
733
+ firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
734
+ firstPass.flatMap((fn) => fn.callEdges)
735
+ );
736
+ const order = topologicalFunctionOrder(
737
+ firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
738
+ graph
739
+ );
740
+ const byId = new Map(targets.map((target) => [target.functionId, target]));
741
+ const moduleCallees = /* @__PURE__ */ new Map();
742
+ const analyzed = [];
743
+ for (const functionId of order) {
744
+ const target = byId.get(functionId);
745
+ if (!target) continue;
746
+ const result = analyzeOne(
747
+ target.node,
748
+ target.functionId,
749
+ target.name,
750
+ sourceFile,
751
+ moduleCallees
752
+ );
753
+ if (nameCounts.get(result.name) === 1) {
754
+ moduleCallees.set(result.name, result.functionId);
755
+ }
756
+ analyzed.push(result);
757
+ }
758
+ const remaining = targets.filter(
759
+ (target) => !analyzed.some((fn) => fn.functionId === target.functionId)
760
+ );
761
+ for (const target of remaining) {
762
+ analyzed.push(
763
+ analyzeOne(target.node, target.functionId, target.name, sourceFile, moduleCallees)
764
+ );
765
+ }
766
+ return analyzed;
767
+ };
768
+
769
+ // src/derivation/derivation-proof.ts
770
+ var functionIsExact = (plan) => plan.fallbackReason === void 0 && plan.captureBoundaries.length === 0 && plan.controlFacts.length === 0;
771
+
772
+ // src/derivation/emit-capture-plan.ts
773
+ var captureWrapEdits = (wraps, capName) => {
774
+ const edits = [];
775
+ let order = 0;
776
+ for (const wrap of wraps) {
777
+ edits.push({
778
+ position: wrap.start,
779
+ text: `${wrap.shorthandPropertyName ? `${wrap.shorthandPropertyName}:` : ""}${capName}(${wrap.rootId},`,
780
+ order: order++
781
+ });
782
+ edits.push({
783
+ position: wrap.end,
784
+ text: ")",
785
+ order: order++
786
+ });
787
+ }
788
+ return edits;
789
+ };
147
790
 
148
791
  // src/execution-recorder/automatic-runtime.ts
149
792
  var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
150
793
  var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
151
794
 
152
795
  // src/auto-instrumentation/collect-instrumentation-delta.ts
153
- import ts from "typescript";
796
+ import ts2 from "typescript";
154
797
  var paramBindingFromName = (name, sourceFile) => {
155
- if (ts.isIdentifier(name)) return { kind: "identifier", name: name.text };
156
- if (ts.isArrayBindingPattern(name)) {
798
+ if (ts2.isIdentifier(name)) return { kind: "identifier", name: name.text };
799
+ if (ts2.isArrayBindingPattern(name)) {
157
800
  const elements = name.elements.map(
158
801
  (element) => {
159
- if (ts.isOmittedExpression(element)) return { kind: "omitted" };
802
+ if (ts2.isOmittedExpression(element)) return { kind: "omitted" };
160
803
  const binding = paramBindingFromName(element.name, sourceFile);
161
804
  if (element.dotDotDotToken) return { kind: "rest", binding };
162
805
  return binding;
@@ -166,7 +809,7 @@ var paramBindingFromName = (name, sourceFile) => {
166
809
  }
167
810
  const properties = name.elements.map((element) => {
168
811
  const binding = paramBindingFromName(element.name, sourceFile);
169
- const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
812
+ const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts2.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
170
813
  if (element.dotDotDotToken) {
171
814
  const rest = { kind: "rest", binding };
172
815
  return { key, binding: rest };
@@ -178,7 +821,7 @@ var paramBindingFromName = (name, sourceFile) => {
178
821
  var collectFunctionParams = (node, sourceFile) => {
179
822
  const params = [];
180
823
  for (const parameter of node.parameters) {
181
- if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
824
+ if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
182
825
  const binding = paramBindingFromName(parameter.name, sourceFile);
183
826
  params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
184
827
  }
@@ -219,18 +862,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
219
862
  };
220
863
  var unwrapExpression = (expression) => {
221
864
  let current = expression;
222
- while (ts.isParenthesizedExpression(current) || ts.isNonNullExpression(current)) {
865
+ while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
223
866
  current = current.expression;
224
867
  }
225
- if (ts.isAwaitExpression(current)) return unwrapExpression(current.expression);
868
+ if (ts2.isAwaitExpression(current)) return unwrapExpression(current.expression);
226
869
  return current;
227
870
  };
228
871
  var calleeFromExpression = (expression, sourceFile) => {
229
872
  const current = unwrapExpression(expression);
230
- if (ts.isIdentifier(current)) {
873
+ if (ts2.isIdentifier(current)) {
231
874
  return { calleeName: current.text, calleeText: current.text };
232
875
  }
233
- if (ts.isPropertyAccessExpression(current) && ts.isIdentifier(current.name)) {
876
+ if (ts2.isPropertyAccessExpression(current) && ts2.isIdentifier(current.name)) {
234
877
  return {
235
878
  calleeName: current.name.text,
236
879
  calleeText: current.getText(sourceFile).replace(/\s+/g, "")
@@ -240,9 +883,9 @@ var calleeFromExpression = (expression, sourceFile) => {
240
883
  };
241
884
  var resultBindingForCall = (call) => {
242
885
  let node = call.parent;
243
- if (ts.isAwaitExpression(node)) node = node.parent;
244
- if (!ts.isVariableDeclaration(node)) return null;
245
- if (!ts.isIdentifier(node.name)) return null;
886
+ if (ts2.isAwaitExpression(node)) node = node.parent;
887
+ if (!ts2.isVariableDeclaration(node)) return null;
888
+ if (!ts2.isIdentifier(node.name)) return null;
246
889
  const initializer = node.initializer;
247
890
  if (!initializer) return null;
248
891
  const inner = unwrapExpression(initializer);
@@ -252,18 +895,18 @@ var resultBindingForCall = (call) => {
252
895
  var argIdentifiersForCall = (call) => {
253
896
  const identifiers = [];
254
897
  call.arguments?.forEach((argument, index) => {
255
- if (ts.isIdentifier(argument)) {
898
+ if (ts2.isIdentifier(argument)) {
256
899
  identifiers.push({ index, kind: "identifier", name: argument.text });
257
900
  return;
258
901
  }
259
- if (!ts.isObjectLiteralExpression(argument)) return;
902
+ if (!ts2.isObjectLiteralExpression(argument)) return;
260
903
  const properties = [];
261
904
  for (const property of argument.properties) {
262
- if (ts.isShorthandPropertyAssignment(property)) {
905
+ if (ts2.isShorthandPropertyAssignment(property)) {
263
906
  properties.push({ key: property.name.text, name: property.name.text });
264
907
  continue;
265
908
  }
266
- if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && ts.isIdentifier(property.initializer)) {
909
+ if (ts2.isPropertyAssignment(property) && ts2.isIdentifier(property.name) && ts2.isIdentifier(property.initializer)) {
267
910
  properties.push({ key: property.name.text, name: property.initializer.text });
268
911
  }
269
912
  }
@@ -303,8 +946,8 @@ var collectCallSite = (call, caller, context) => {
303
946
  };
304
947
  };
305
948
  var collectParamDestructure = (declaration, caller, context) => {
306
- if (!declaration.initializer || !ts.isIdentifier(declaration.initializer)) return null;
307
- if (!ts.isObjectBindingPattern(declaration.name) && !ts.isArrayBindingPattern(declaration.name)) {
949
+ if (!declaration.initializer || !ts2.isIdentifier(declaration.initializer)) return null;
950
+ if (!ts2.isObjectBindingPattern(declaration.name) && !ts2.isArrayBindingPattern(declaration.name)) {
308
951
  return null;
309
952
  }
310
953
  const sourceParam = declaration.initializer.text;
@@ -328,33 +971,33 @@ var collectParamDestructure = (declaration, caller, context) => {
328
971
 
329
972
  // src/auto-instrumentation/transform-source.ts
330
973
  var scriptKindFor = (filePath) => {
331
- if (/\.tsx$/i.test(filePath)) return ts2.ScriptKind.TSX;
332
- if (/\.jsx$/i.test(filePath)) return ts2.ScriptKind.JSX;
333
- if (/\.[cm]?ts$/i.test(filePath)) return ts2.ScriptKind.TS;
334
- return ts2.ScriptKind.JS;
974
+ if (/\.tsx$/i.test(filePath)) return ts3.ScriptKind.TSX;
975
+ if (/\.jsx$/i.test(filePath)) return ts3.ScriptKind.JSX;
976
+ if (/\.[cm]?ts$/i.test(filePath)) return ts3.ScriptKind.TS;
977
+ return ts3.ScriptKind.JS;
335
978
  };
336
979
  var functionName = (node, sourceFile) => {
337
980
  if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
338
981
  const parent = node.parent;
339
- if (ts2.isVariableDeclaration(parent) && ts2.isIdentifier(parent.name)) return parent.name.text;
340
- if (ts2.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
341
- if (ts2.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
982
+ if (ts3.isVariableDeclaration(parent) && ts3.isIdentifier(parent.name)) return parent.name.text;
983
+ if (ts3.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
984
+ if (ts3.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
342
985
  const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
343
986
  return `anonymous@${location.line + 1}:${location.character + 1}`;
344
987
  };
345
988
  var functionLocationPosition = (node, sourceFile) => {
346
989
  if (node.name) return node.name.getStart(sourceFile);
347
990
  const parent = node.parent;
348
- if ((ts2.isVariableDeclaration(parent) || ts2.isPropertyAssignment(parent)) && parent.name) {
991
+ if ((ts3.isVariableDeclaration(parent) || ts3.isPropertyAssignment(parent)) && parent.name) {
349
992
  return parent.name.getStart(sourceFile);
350
993
  }
351
994
  return node.getStart(sourceFile);
352
995
  };
353
996
  var bindingValue = (name, sourceFile) => {
354
- if (ts2.isIdentifier(name)) return name.text;
355
- if (ts2.isArrayBindingPattern(name)) {
997
+ if (ts3.isIdentifier(name)) return name.text;
998
+ if (ts3.isArrayBindingPattern(name)) {
356
999
  const values = name.elements.map((element) => {
357
- if (ts2.isOmittedExpression(element)) return "";
1000
+ if (ts3.isOmittedExpression(element)) return "";
358
1001
  const value = bindingValue(element.name, sourceFile);
359
1002
  return element.dotDotDotToken ? `...${value}` : value;
360
1003
  });
@@ -370,14 +1013,14 @@ var bindingValue = (name, sourceFile) => {
370
1013
  };
371
1014
  var arrowArguments = (node) => {
372
1015
  const values = node.parameters.flatMap((parameter) => {
373
- if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
1016
+ if (ts3.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
374
1017
  const value = bindingValue(parameter.name, node.getSourceFile());
375
1018
  return [parameter.dotDotDotToken ? `...${value}` : value];
376
1019
  });
377
1020
  return `[${values.join(",")}]`;
378
1021
  };
379
1022
  var isSupportedFunction = (node) => {
380
- if (!ts2.isFunctionDeclaration(node) && !ts2.isFunctionExpression(node) && !ts2.isArrowFunction(node) && !ts2.isMethodDeclaration(node) && !ts2.isGetAccessorDeclaration(node) && !ts2.isSetAccessorDeclaration(node)) {
1023
+ if (!ts3.isFunctionDeclaration(node) && !ts3.isFunctionExpression(node) && !ts3.isArrowFunction(node) && !ts3.isMethodDeclaration(node) && !ts3.isGetAccessorDeclaration(node) && !ts3.isSetAccessorDeclaration(node)) {
381
1024
  return false;
382
1025
  }
383
1026
  if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
@@ -388,7 +1031,22 @@ var uniqueName = (source, base) => {
388
1031
  while (source.includes(name)) name += "_";
389
1032
  return name;
390
1033
  };
391
- var 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)}));`;
1034
+ var isDirectivePrologue = (statement) => ts3.isExpressionStatement(statement) && ts3.isStringLiteral(statement.expression);
1035
+ var insertPositionAfterPrologue = (source, sourceFile) => {
1036
+ const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
1037
+ let end = shebangEnd;
1038
+ for (const statement of sourceFile.statements) {
1039
+ if (!isDirectivePrologue(statement)) break;
1040
+ end = statement.end;
1041
+ }
1042
+ return end;
1043
+ };
1044
+ var preambleInsert = (source, position, preamble) => {
1045
+ if (position <= 0) return preamble;
1046
+ const previous = source[position - 1];
1047
+ return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
1048
+ };
1049
+ var modulePreamble = (runName, capName, plansName, plansJson) => `const ${plansName}=${plansJson};const ${runName}=((g,k)=>{const r=g[k]??(g[k]={runtimes:[],modules:[],captureRoot(i,v){return v},register(p){const d={run:(i,a,c)=>c()};const m={plans:p,cell:d};this.modules.push(m);const n=this.runtimes.at(-1);if(n)n.bind(m);return(i,a,c,l)=>d.run(i,a,c,l)}});return r.register(${plansName})})(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));const ${capName}=((g,k)=>{const r=g[k];return(i,v)=>typeof r?.captureRoot==='function'?r.captureRoot(i,v):v})(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));`;
392
1050
  var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
393
1051
  const start = mappedLocation(
394
1052
  sourceFile,
@@ -419,77 +1077,128 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
419
1077
  paramNames
420
1078
  };
421
1079
  };
1080
+ var canOmitCallValues = (analysis, analyzed) => {
1081
+ if (!functionIsExact(analysis.plan) || analysis.hasThrow || analysis.hasCoercionGuard) {
1082
+ return false;
1083
+ }
1084
+ const callers = analyzed.filter(
1085
+ (caller) => caller.callEdges.some((edge) => edge.calleeFunctionId === analysis.functionId)
1086
+ );
1087
+ if (callers.length === 0) return false;
1088
+ return callers.every((caller) => caller.plan.fallbackReason === void 0 && !caller.hasThrow);
1089
+ };
422
1090
  var transformSource = (source, options) => {
423
1091
  if (source.includes("@rasputin-ignore-file")) return void 0;
424
- const sourceFile = ts2.createSourceFile(
1092
+ const sourceFile = ts3.createSourceFile(
425
1093
  options.filePath,
426
1094
  source,
427
- ts2.ScriptTarget.Latest,
1095
+ ts3.ScriptTarget.Latest,
428
1096
  true,
429
1097
  scriptKindFor(options.filePath)
430
1098
  );
431
1099
  const runName = uniqueName(source, "__rasputinRun__");
1100
+ const capName = uniqueName(source, "__rasputinCap__");
432
1101
  const plansName = uniqueName(source, "__rasputinPlans__");
433
1102
  const absolutePath = resolve4(options.filePath);
1103
+ const derivationMode = options.derivationMode ?? "analysis";
434
1104
  const edits = [];
435
1105
  let order = 0;
436
1106
  const delta = {
437
1107
  functions: [],
438
1108
  callSites: [],
439
- destructures: []
1109
+ destructures: [],
1110
+ derivationPlans: []
440
1111
  };
441
1112
  const collectContext = {
442
1113
  sourceFile,
443
1114
  mapLocation: options.mapLocation,
444
1115
  absolutePath
445
1116
  };
1117
+ const pending = [];
446
1118
  const visit = (node, caller) => {
447
1119
  if (isSupportedFunction(node)) {
448
1120
  const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
449
1121
  if (!collected) {
450
- ts2.forEachChild(node, (child) => visit(child, caller));
1122
+ ts3.forEachChild(node, (child) => visit(child, caller));
451
1123
  return;
452
1124
  }
453
- const planIndex = delta.functions.length;
1125
+ pending.push({ node, collected });
454
1126
  delta.functions.push(collected);
455
- const args = ts2.isArrowFunction(node) ? arrowArguments(node) : "arguments";
456
- const asyncCallback = node.modifiers?.some(
457
- (modifier) => modifier.kind === ts2.SyntaxKind.AsyncKeyword
458
- ) ? "async " : "";
459
- if (ts2.isBlock(node.body)) {
460
- edits.push({
461
- position: node.body.getStart(sourceFile) + 1,
462
- text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
463
- order: order++
464
- });
465
- edits.push({ position: node.body.end - 1, text: "});", order: order++ });
466
- } else {
467
- edits.push({
468
- position: node.body.getStart(sourceFile),
469
- text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
470
- order: order++
471
- });
472
- edits.push({ position: node.body.end, text: "))", order: order++ });
473
- }
474
- ts2.forEachChild(node, (child) => visit(child, collected));
1127
+ ts3.forEachChild(node, (child) => visit(child, collected));
475
1128
  return;
476
1129
  }
477
- if (caller && (ts2.isCallExpression(node) || ts2.isNewExpression(node))) {
1130
+ if (caller && (ts3.isCallExpression(node) || ts3.isNewExpression(node))) {
478
1131
  const site = collectCallSite(node, caller, collectContext);
479
1132
  if (site) delta.callSites.push(site);
480
1133
  }
481
- if (caller && ts2.isVariableDeclaration(node)) {
1134
+ if (caller && ts3.isVariableDeclaration(node)) {
482
1135
  const destructure = collectParamDestructure(node, caller, collectContext);
483
1136
  if (destructure) delta.destructures.push(destructure);
484
1137
  }
485
- ts2.forEachChild(node, (child) => visit(child, caller));
1138
+ ts3.forEachChild(node, (child) => visit(child, caller));
486
1139
  };
487
1140
  visit(sourceFile);
488
- if (edits.length === 0) return void 0;
489
- const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
1141
+ if (pending.length === 0) return void 0;
1142
+ const analyzed = analyzeModuleFunctions(
1143
+ sourceFile,
1144
+ pending.map((entry) => ({
1145
+ functionId: entry.collected.functionId,
1146
+ name: entry.collected.name,
1147
+ node: entry.node
1148
+ }))
1149
+ );
1150
+ delta.derivationPlans = analyzed.map((fn) => fn.plan);
1151
+ const analyzedById = new Map(analyzed.map((fn) => [fn.functionId, fn]));
1152
+ for (const [planIndex, entry] of pending.entries()) {
1153
+ const analysis = analyzedById.get(entry.collected.functionId);
1154
+ const omitValues = derivationMode === "selective" && analysis && canOmitCallValues(analysis, analyzed);
1155
+ const valueFlag = omitValues ? ",1" : "";
1156
+ const args = ts3.isArrowFunction(entry.node) ? arrowArguments(entry.node) : "arguments";
1157
+ const asyncCallback = entry.node.modifiers?.some(
1158
+ (modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword
1159
+ ) ? "async " : "";
1160
+ if (ts3.isBlock(entry.node.body)) {
1161
+ edits.push({
1162
+ position: entry.node.body.getStart(sourceFile) + 1,
1163
+ text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
1164
+ order: order++
1165
+ });
1166
+ edits.push({
1167
+ position: entry.node.body.end - 1,
1168
+ text: `}${valueFlag});`,
1169
+ order: order++
1170
+ });
1171
+ } else {
1172
+ edits.push({
1173
+ position: entry.node.body.getStart(sourceFile),
1174
+ text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
1175
+ order: order++
1176
+ });
1177
+ edits.push({
1178
+ position: entry.node.body.end,
1179
+ text: `)${valueFlag})`,
1180
+ order: order++
1181
+ });
1182
+ }
1183
+ if (analysis) {
1184
+ for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
1185
+ edits.push({ ...edit, order: order++ });
1186
+ }
1187
+ }
1188
+ }
1189
+ const insertAt = insertPositionAfterPrologue(source, sourceFile);
490
1190
  edits.push({
491
- position: shebangEnd,
492
- text: modulePreamble(runName, plansName, JSON.stringify(delta.functions.map((fn) => fn.plan))),
1191
+ position: insertAt,
1192
+ text: preambleInsert(
1193
+ source,
1194
+ insertAt,
1195
+ modulePreamble(
1196
+ runName,
1197
+ capName,
1198
+ plansName,
1199
+ JSON.stringify(delta.functions.map((fn) => fn.plan))
1200
+ )
1201
+ ),
493
1202
  order: -1
494
1203
  });
495
1204
  edits.sort((left, right) => right.position - left.position || right.order - left.order);
@@ -558,7 +1267,21 @@ import { dirname as dirname2, resolve as resolve5 } from "node:path";
558
1267
  import { fileURLToPath as fileURLToPath3 } from "node:url";
559
1268
  var sdkPackageScopeRootFrom = (instrumentEntryUrl) => resolve5(dirname2(fileURLToPath3(instrumentEntryUrl)), "../../..");
560
1269
 
1270
+ // src/auto-instrumentation/warn-production-runtime-transform.ts
1271
+ var RUNTIME_TRANSFORM_WARNING_SUPPRESS_ENV = "RASPUTIN_SUPPRESS_RUNTIME_TRANSFORM_WARNING";
1272
+ var warnedSymbol = Symbol.for("rasputin.runtimeTransformWarning");
1273
+ var warnIfProductionRuntimeTransform = () => {
1274
+ if (process.env.NODE_ENV !== "production") return;
1275
+ if (process.env[RUNTIME_TRANSFORM_WARNING_SUPPRESS_ENV] === "1") return;
1276
+ if (Reflect.get(globalThis, warnedSymbol)) return;
1277
+ Reflect.set(globalThis, warnedSymbol, true);
1278
+ console.warn(
1279
+ "[rasputin] Production is using load-time source transformation (`--import` / `--preload`). Prefer build-time `rasputin-instrument` so the process does not load the TypeScript compiler. Set RASPUTIN_SUPPRESS_RUNTIME_TRANSFORM_WARNING=1 to hide this warning."
1280
+ );
1281
+ };
1282
+
561
1283
  // src/instrument/node.ts
1284
+ warnIfProductionRuntimeTransform();
562
1285
  registerRasputinNodeHooks({
563
1286
  excludedRoots: [sdkPackageScopeRootFrom(import.meta.url)]
564
1287
  });