@rasputin-ai/node 0.4.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 (49) hide show
  1. package/README.md +188 -162
  2. package/dist/auto-instrumentation/collect-instrumentation-delta.d.ts +2 -37
  3. package/dist/auto-instrumentation/collect-instrumentation-delta.d.ts.map +1 -1
  4. package/dist/auto-instrumentation/instrument-compiled-esm.d.ts +19 -0
  5. package/dist/auto-instrumentation/instrument-compiled-esm.d.ts.map +1 -0
  6. package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts +41 -0
  7. package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts.map +1 -0
  8. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts +5 -2
  9. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts.map +1 -1
  10. package/dist/auto-instrumentation/transform-source.d.ts +7 -1
  11. package/dist/auto-instrumentation/transform-source.d.ts.map +1 -1
  12. package/dist/auto-instrumentation/warn-production-runtime-transform.d.ts +8 -0
  13. package/dist/auto-instrumentation/warn-production-runtime-transform.d.ts.map +1 -0
  14. package/dist/derivation/analyze-function.d.ts +42 -0
  15. package/dist/derivation/analyze-function.d.ts.map +1 -0
  16. package/dist/derivation/build-dependency-graph.d.ts +21 -0
  17. package/dist/derivation/build-dependency-graph.d.ts.map +1 -0
  18. package/dist/derivation/derivation-ir.d.ts +33 -0
  19. package/dist/derivation/derivation-ir.d.ts.map +1 -0
  20. package/dist/derivation/derivation-proof.d.ts +14 -0
  21. package/dist/derivation/derivation-proof.d.ts.map +1 -0
  22. package/dist/derivation/emit-capture-plan.d.ts +8 -0
  23. package/dist/derivation/emit-capture-plan.d.ts.map +1 -0
  24. package/dist/execution-recorder/automatic-runtime.d.ts +21 -10
  25. package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
  26. package/dist/execution-recorder/execution-journal.d.ts +84 -0
  27. package/dist/execution-recorder/execution-journal.d.ts.map +1 -0
  28. package/dist/execution-recorder/execution-recorder-types.d.ts +36 -0
  29. package/dist/execution-recorder/execution-recorder-types.d.ts.map +1 -1
  30. package/dist/execution-recorder/execution-recorder.d.ts.map +1 -1
  31. package/dist/execution-recorder/function-plan.d.ts +26 -0
  32. package/dist/execution-recorder/function-plan.d.ts.map +1 -0
  33. package/dist/execution-recorder/recorder-memory-budget.d.ts +8 -1
  34. package/dist/execution-recorder/recorder-memory-budget.d.ts.map +1 -1
  35. package/dist/execution-recorder/safe-serialize.d.ts +8 -0
  36. package/dist/execution-recorder/safe-serialize.d.ts.map +1 -1
  37. package/dist/index.js +1432 -418
  38. package/dist/instrument/build.d.ts +2 -0
  39. package/dist/instrument/build.d.ts.map +1 -0
  40. package/dist/instrument/build.js +1430 -0
  41. package/dist/instrument/bun.js +825 -82
  42. package/dist/instrument/node.js +825 -82
  43. package/dist/rasputin-init.d.ts +5 -5
  44. package/dist/rasputin-init.d.ts.map +1 -1
  45. package/dist/sdk-meta.d.ts +1 -1
  46. package/dist/sdk-meta.d.ts.map +1 -1
  47. package/package.json +10 -2
  48. package/dist/execution-recorder/call-aware-event-buffer.d.ts +0 -23
  49. package/dist/execution-recorder/call-aware-event-buffer.d.ts.map +0 -1
@@ -9,25 +9,29 @@ import {
9
9
  sourceLocatorFromFile as sourceLocatorFromFile2
10
10
  } from "@rasputin-ai/core";
11
11
 
12
- // src/execution-recorder/automatic-runtime.ts
12
+ // src/execution-recorder/function-plan.ts
13
13
  import {
14
14
  runtimeFunctionId,
15
15
  sourceLocatorFromFile
16
16
  } from "@rasputin-ai/core";
17
- var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
18
- var AUTOMATIC_SOURCE_PREFIX = "rasputin-source-v2:";
19
- var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
20
- var encodeAutomaticFunctionSource = (source) => {
17
+ var encodeFunctionPlan = (source) => {
21
18
  const locator = sourceLocatorFromFile(source.filePath);
22
- const value = {
19
+ const definition = {
23
20
  name: source.name,
24
21
  ...locator ? { source: { ...locator, line: source.line, column: source.column } } : {}
25
22
  };
26
- return `${AUTOMATIC_SOURCE_PREFIX}${JSON.stringify(value)}`;
23
+ return [
24
+ runtimeFunctionId(definition),
25
+ source.name,
26
+ locator?.packageName ?? null,
27
+ locator?.packageRelativePath ?? null,
28
+ source.line,
29
+ source.column
30
+ ];
27
31
  };
28
32
 
29
33
  // src/auto-instrumentation/instrumentation-manifest-registry.ts
30
- var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v2";
34
+ var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v3";
31
35
  var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
32
36
  var getRegistry = () => {
33
37
  const existing = Reflect.get(globalThis, registrySymbol);
@@ -139,16 +143,663 @@ var createSourceMapLocationMapper = (generatedPath, source) => {
139
143
 
140
144
  // src/auto-instrumentation/transform-source.ts
141
145
  import { resolve as resolve4 } from "node:path";
142
- import ts2 from "typescript";
146
+ import ts3 from "typescript";
143
147
 
144
- // src/auto-instrumentation/collect-instrumentation-delta.ts
148
+ // src/derivation/analyze-function.ts
145
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
+ };
790
+
791
+ // src/execution-recorder/automatic-runtime.ts
792
+ var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
793
+ var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
794
+
795
+ // src/auto-instrumentation/collect-instrumentation-delta.ts
796
+ import ts2 from "typescript";
146
797
  var paramBindingFromName = (name, sourceFile) => {
147
- if (ts.isIdentifier(name)) return { kind: "identifier", name: name.text };
148
- if (ts.isArrayBindingPattern(name)) {
798
+ if (ts2.isIdentifier(name)) return { kind: "identifier", name: name.text };
799
+ if (ts2.isArrayBindingPattern(name)) {
149
800
  const elements = name.elements.map(
150
801
  (element) => {
151
- if (ts.isOmittedExpression(element)) return { kind: "omitted" };
802
+ if (ts2.isOmittedExpression(element)) return { kind: "omitted" };
152
803
  const binding = paramBindingFromName(element.name, sourceFile);
153
804
  if (element.dotDotDotToken) return { kind: "rest", binding };
154
805
  return binding;
@@ -158,7 +809,7 @@ var paramBindingFromName = (name, sourceFile) => {
158
809
  }
159
810
  const properties = name.elements.map((element) => {
160
811
  const binding = paramBindingFromName(element.name, sourceFile);
161
- 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);
162
813
  if (element.dotDotDotToken) {
163
814
  const rest = { kind: "rest", binding };
164
815
  return { key, binding: rest };
@@ -170,7 +821,7 @@ var paramBindingFromName = (name, sourceFile) => {
170
821
  var collectFunctionParams = (node, sourceFile) => {
171
822
  const params = [];
172
823
  for (const parameter of node.parameters) {
173
- if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
824
+ if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
174
825
  const binding = paramBindingFromName(parameter.name, sourceFile);
175
826
  params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
176
827
  }
@@ -211,18 +862,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
211
862
  };
212
863
  var unwrapExpression = (expression) => {
213
864
  let current = expression;
214
- while (ts.isParenthesizedExpression(current) || ts.isNonNullExpression(current)) {
865
+ while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
215
866
  current = current.expression;
216
867
  }
217
- if (ts.isAwaitExpression(current)) return unwrapExpression(current.expression);
868
+ if (ts2.isAwaitExpression(current)) return unwrapExpression(current.expression);
218
869
  return current;
219
870
  };
220
871
  var calleeFromExpression = (expression, sourceFile) => {
221
872
  const current = unwrapExpression(expression);
222
- if (ts.isIdentifier(current)) {
873
+ if (ts2.isIdentifier(current)) {
223
874
  return { calleeName: current.text, calleeText: current.text };
224
875
  }
225
- if (ts.isPropertyAccessExpression(current) && ts.isIdentifier(current.name)) {
876
+ if (ts2.isPropertyAccessExpression(current) && ts2.isIdentifier(current.name)) {
226
877
  return {
227
878
  calleeName: current.name.text,
228
879
  calleeText: current.getText(sourceFile).replace(/\s+/g, "")
@@ -232,9 +883,9 @@ var calleeFromExpression = (expression, sourceFile) => {
232
883
  };
233
884
  var resultBindingForCall = (call) => {
234
885
  let node = call.parent;
235
- if (ts.isAwaitExpression(node)) node = node.parent;
236
- if (!ts.isVariableDeclaration(node)) return null;
237
- 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;
238
889
  const initializer = node.initializer;
239
890
  if (!initializer) return null;
240
891
  const inner = unwrapExpression(initializer);
@@ -244,18 +895,18 @@ var resultBindingForCall = (call) => {
244
895
  var argIdentifiersForCall = (call) => {
245
896
  const identifiers = [];
246
897
  call.arguments?.forEach((argument, index) => {
247
- if (ts.isIdentifier(argument)) {
898
+ if (ts2.isIdentifier(argument)) {
248
899
  identifiers.push({ index, kind: "identifier", name: argument.text });
249
900
  return;
250
901
  }
251
- if (!ts.isObjectLiteralExpression(argument)) return;
902
+ if (!ts2.isObjectLiteralExpression(argument)) return;
252
903
  const properties = [];
253
904
  for (const property of argument.properties) {
254
- if (ts.isShorthandPropertyAssignment(property)) {
905
+ if (ts2.isShorthandPropertyAssignment(property)) {
255
906
  properties.push({ key: property.name.text, name: property.name.text });
256
907
  continue;
257
908
  }
258
- 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)) {
259
910
  properties.push({ key: property.name.text, name: property.initializer.text });
260
911
  }
261
912
  }
@@ -282,7 +933,7 @@ var collectCallSite = (call, caller, context) => {
282
933
  );
283
934
  if (!start || !end) return null;
284
935
  return {
285
- callerSourceToken: caller.sourceToken,
936
+ callerFunctionId: caller.functionId,
286
937
  calleeName: callee.calleeName,
287
938
  calleeText: callee.calleeText,
288
939
  filePath: start.filePath,
@@ -295,8 +946,8 @@ var collectCallSite = (call, caller, context) => {
295
946
  };
296
947
  };
297
948
  var collectParamDestructure = (declaration, caller, context) => {
298
- if (!declaration.initializer || !ts.isIdentifier(declaration.initializer)) return null;
299
- 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)) {
300
951
  return null;
301
952
  }
302
953
  const sourceParam = declaration.initializer.text;
@@ -309,7 +960,7 @@ var collectParamDestructure = (declaration, caller, context) => {
309
960
  );
310
961
  if (!start) return null;
311
962
  return {
312
- callerSourceToken: caller.sourceToken,
963
+ callerFunctionId: caller.functionId,
313
964
  sourceParam,
314
965
  filePath: start.filePath,
315
966
  line: start.line,
@@ -320,33 +971,33 @@ var collectParamDestructure = (declaration, caller, context) => {
320
971
 
321
972
  // src/auto-instrumentation/transform-source.ts
322
973
  var scriptKindFor = (filePath) => {
323
- if (/\.tsx$/i.test(filePath)) return ts2.ScriptKind.TSX;
324
- if (/\.jsx$/i.test(filePath)) return ts2.ScriptKind.JSX;
325
- if (/\.[cm]?ts$/i.test(filePath)) return ts2.ScriptKind.TS;
326
- 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;
327
978
  };
328
979
  var functionName = (node, sourceFile) => {
329
980
  if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
330
981
  const parent = node.parent;
331
- if (ts2.isVariableDeclaration(parent) && ts2.isIdentifier(parent.name)) return parent.name.text;
332
- if (ts2.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
333
- 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);
334
985
  const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
335
986
  return `anonymous@${location.line + 1}:${location.character + 1}`;
336
987
  };
337
988
  var functionLocationPosition = (node, sourceFile) => {
338
989
  if (node.name) return node.name.getStart(sourceFile);
339
990
  const parent = node.parent;
340
- if ((ts2.isVariableDeclaration(parent) || ts2.isPropertyAssignment(parent)) && parent.name) {
991
+ if ((ts3.isVariableDeclaration(parent) || ts3.isPropertyAssignment(parent)) && parent.name) {
341
992
  return parent.name.getStart(sourceFile);
342
993
  }
343
994
  return node.getStart(sourceFile);
344
995
  };
345
996
  var bindingValue = (name, sourceFile) => {
346
- if (ts2.isIdentifier(name)) return name.text;
347
- if (ts2.isArrayBindingPattern(name)) {
997
+ if (ts3.isIdentifier(name)) return name.text;
998
+ if (ts3.isArrayBindingPattern(name)) {
348
999
  const values = name.elements.map((element) => {
349
- if (ts2.isOmittedExpression(element)) return "";
1000
+ if (ts3.isOmittedExpression(element)) return "";
350
1001
  const value = bindingValue(element.name, sourceFile);
351
1002
  return element.dotDotDotToken ? `...${value}` : value;
352
1003
  });
@@ -362,20 +1013,40 @@ var bindingValue = (name, sourceFile) => {
362
1013
  };
363
1014
  var arrowArguments = (node) => {
364
1015
  const values = node.parameters.flatMap((parameter) => {
365
- if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
1016
+ if (ts3.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
366
1017
  const value = bindingValue(parameter.name, node.getSourceFile());
367
1018
  return [parameter.dotDotDotToken ? `...${value}` : value];
368
1019
  });
369
1020
  return `[${values.join(",")}]`;
370
1021
  };
371
1022
  var isSupportedFunction = (node) => {
372
- 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)) {
373
1024
  return false;
374
1025
  }
375
1026
  if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
376
1027
  return true;
377
1028
  };
378
- var helperDeclaration = (helperName) => `const ${helperName}=(functionId,args,callback)=>{const runtime=Reflect.get(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));return runtime?runtime.run(functionId,args,callback):callback()};`;
1029
+ var uniqueName = (source, base) => {
1030
+ let name = base;
1031
+ while (source.includes(name)) name += "_";
1032
+ return name;
1033
+ };
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)}));`;
379
1050
  var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
380
1051
  const start = mappedLocation(
381
1052
  sourceFile,
@@ -387,13 +1058,15 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
387
1058
  const end = mappedLocation(sourceFile, node.end, absolutePath, mapLocation) ?? start;
388
1059
  const name = functionName(node, sourceFile).replace(/@\d+:\d+$/, "");
389
1060
  const { params, paramNames } = collectFunctionParams(node, sourceFile);
1061
+ const plan = encodeFunctionPlan({
1062
+ filePath: start.filePath,
1063
+ name,
1064
+ line: start.line,
1065
+ column: start.column
1066
+ });
390
1067
  return {
391
- sourceToken: encodeAutomaticFunctionSource({
392
- filePath: start.filePath,
393
- name,
394
- line: start.line,
395
- column: start.column
396
- }),
1068
+ plan,
1069
+ functionId: plan[0],
397
1070
  filePath: start.filePath,
398
1071
  name,
399
1072
  startLine: start.line,
@@ -404,74 +1077,130 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
404
1077
  paramNames
405
1078
  };
406
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
+ };
407
1090
  var transformSource = (source, options) => {
408
1091
  if (source.includes("@rasputin-ignore-file")) return void 0;
409
- const sourceFile = ts2.createSourceFile(
1092
+ const sourceFile = ts3.createSourceFile(
410
1093
  options.filePath,
411
1094
  source,
412
- ts2.ScriptTarget.Latest,
1095
+ ts3.ScriptTarget.Latest,
413
1096
  true,
414
1097
  scriptKindFor(options.filePath)
415
1098
  );
416
- let helperName = "__rasputinAutoRun__";
417
- while (source.includes(helperName)) helperName += "_";
1099
+ const runName = uniqueName(source, "__rasputinRun__");
1100
+ const capName = uniqueName(source, "__rasputinCap__");
1101
+ const plansName = uniqueName(source, "__rasputinPlans__");
418
1102
  const absolutePath = resolve4(options.filePath);
1103
+ const derivationMode = options.derivationMode ?? "analysis";
419
1104
  const edits = [];
420
1105
  let order = 0;
421
1106
  const delta = {
422
1107
  functions: [],
423
1108
  callSites: [],
424
- destructures: []
1109
+ destructures: [],
1110
+ derivationPlans: []
425
1111
  };
426
1112
  const collectContext = {
427
1113
  sourceFile,
428
1114
  mapLocation: options.mapLocation,
429
1115
  absolutePath
430
1116
  };
1117
+ const pending = [];
431
1118
  const visit = (node, caller) => {
432
1119
  if (isSupportedFunction(node)) {
433
1120
  const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
434
1121
  if (!collected) {
435
- ts2.forEachChild(node, (child) => visit(child, caller));
1122
+ ts3.forEachChild(node, (child) => visit(child, caller));
436
1123
  return;
437
1124
  }
1125
+ pending.push({ node, collected });
438
1126
  delta.functions.push(collected);
439
- const args = ts2.isArrowFunction(node) ? arrowArguments(node) : "arguments";
440
- const asyncCallback = node.modifiers?.some(
441
- (modifier) => modifier.kind === ts2.SyntaxKind.AsyncKeyword
442
- ) ? "async " : "";
443
- if (ts2.isBlock(node.body)) {
444
- edits.push({
445
- position: node.body.getStart(sourceFile) + 1,
446
- text: `return ${helperName}(${JSON.stringify(collected.sourceToken)},${args},${asyncCallback}()=>{`,
447
- order: order++
448
- });
449
- edits.push({ position: node.body.end - 1, text: "});", order: order++ });
450
- } else {
451
- edits.push({
452
- position: node.body.getStart(sourceFile),
453
- text: `${helperName}(${JSON.stringify(collected.sourceToken)},${args},${asyncCallback}()=>(`,
454
- order: order++
455
- });
456
- edits.push({ position: node.body.end, text: "))", order: order++ });
457
- }
458
- ts2.forEachChild(node, (child) => visit(child, collected));
1127
+ ts3.forEachChild(node, (child) => visit(child, collected));
459
1128
  return;
460
1129
  }
461
- if (caller && (ts2.isCallExpression(node) || ts2.isNewExpression(node))) {
1130
+ if (caller && (ts3.isCallExpression(node) || ts3.isNewExpression(node))) {
462
1131
  const site = collectCallSite(node, caller, collectContext);
463
1132
  if (site) delta.callSites.push(site);
464
1133
  }
465
- if (caller && ts2.isVariableDeclaration(node)) {
1134
+ if (caller && ts3.isVariableDeclaration(node)) {
466
1135
  const destructure = collectParamDestructure(node, caller, collectContext);
467
1136
  if (destructure) delta.destructures.push(destructure);
468
1137
  }
469
- ts2.forEachChild(node, (child) => visit(child, caller));
1138
+ ts3.forEachChild(node, (child) => visit(child, caller));
470
1139
  };
471
1140
  visit(sourceFile);
472
- if (edits.length === 0) return void 0;
473
- const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
474
- edits.push({ position: shebangEnd, text: helperDeclaration(helperName), order: -1 });
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);
1190
+ edits.push({
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
+ ),
1202
+ order: -1
1203
+ });
475
1204
  edits.sort((left, right) => right.position - left.position || right.order - left.order);
476
1205
  let transformed = source;
477
1206
  for (const edit of edits) {
@@ -538,7 +1267,21 @@ import { dirname as dirname2, resolve as resolve5 } from "node:path";
538
1267
  import { fileURLToPath as fileURLToPath3 } from "node:url";
539
1268
  var sdkPackageScopeRootFrom = (instrumentEntryUrl) => resolve5(dirname2(fileURLToPath3(instrumentEntryUrl)), "../../..");
540
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
+
541
1283
  // src/instrument/node.ts
1284
+ warnIfProductionRuntimeTransform();
542
1285
  registerRasputinNodeHooks({
543
1286
  excludedRoots: [sdkPackageScopeRootFrom(import.meta.url)]
544
1287
  });