@rasputin-ai/node 0.5.0-alpha.6 → 0.5.0-alpha.7

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 (29) hide show
  1. package/dist/auto-instrumentation/instrument-compiled-esm.d.ts +2 -0
  2. package/dist/auto-instrumentation/instrument-compiled-esm.d.ts.map +1 -1
  3. package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts +2 -1
  4. package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts.map +1 -1
  5. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts +1 -1
  6. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts.map +1 -1
  7. package/dist/auto-instrumentation/transform-source.d.ts +6 -0
  8. package/dist/auto-instrumentation/transform-source.d.ts.map +1 -1
  9. package/dist/derivation/analyze-function.d.ts +40 -0
  10. package/dist/derivation/analyze-function.d.ts.map +1 -0
  11. package/dist/derivation/build-dependency-graph.d.ts +21 -0
  12. package/dist/derivation/build-dependency-graph.d.ts.map +1 -0
  13. package/dist/derivation/derivation-ir.d.ts +33 -0
  14. package/dist/derivation/derivation-ir.d.ts.map +1 -0
  15. package/dist/derivation/derivation-proof.d.ts +14 -0
  16. package/dist/derivation/derivation-proof.d.ts.map +1 -0
  17. package/dist/derivation/emit-capture-plan.d.ts +8 -0
  18. package/dist/derivation/emit-capture-plan.d.ts.map +1 -0
  19. package/dist/execution-recorder/automatic-runtime.d.ts +3 -1
  20. package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
  21. package/dist/execution-recorder/execution-recorder-types.d.ts +13 -2
  22. package/dist/execution-recorder/execution-recorder-types.d.ts.map +1 -1
  23. package/dist/execution-recorder/execution-recorder.d.ts.map +1 -1
  24. package/dist/index.js +169 -36
  25. package/dist/instrument/build.js +761 -71
  26. package/dist/instrument/bun.js +742 -66
  27. package/dist/instrument/node.js +742 -66
  28. package/dist/sdk-meta.d.ts +1 -1
  29. package/package.json +2 -2
@@ -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,649 @@ 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/derivation-proof.ts
270
+ var functionIsExact = (plan) => plan.fallbackReason === void 0 && plan.captureBoundaries.length === 0 && plan.controlFacts.length === 0;
271
+
272
+ // src/derivation/analyze-function.ts
273
+ var loc = (node, sourceFile) => {
274
+ const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
275
+ return { line: position.line + 1, column: position.character + 1 };
276
+ };
277
+ var lookup = (ctx, name) => {
278
+ for (let index = ctx.scopes.length - 1; index >= 0; index--) {
279
+ const binding = ctx.scopes[index]?.get(name);
280
+ if (binding) return binding;
281
+ }
282
+ return void 0;
283
+ };
284
+ var fallback = (ctx, reason) => {
285
+ ctx.fallback ??= reason;
286
+ return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "unknown" };
287
+ };
288
+ var capture = (ctx, node, reason) => {
289
+ const rootId = ctx.nextRootId.value++;
290
+ const position = loc(node, ctx.sourceFile);
291
+ const expressionId = ctx.ir.root(rootId);
292
+ ctx.captureWraps.push({
293
+ rootId,
294
+ reason,
295
+ start: node.getStart(ctx.sourceFile),
296
+ end: node.end,
297
+ line: position.line,
298
+ column: position.column,
299
+ expressionId
300
+ });
301
+ return { expressionId, shape: "unknown", captured: true };
302
+ };
303
+ var binaryOp = (kind) => {
304
+ switch (kind) {
305
+ case ts.SyntaxKind.PlusToken:
306
+ return "add";
307
+ case ts.SyntaxKind.MinusToken:
308
+ return "sub";
309
+ case ts.SyntaxKind.AsteriskToken:
310
+ return "mul";
311
+ case ts.SyntaxKind.SlashToken:
312
+ return "div";
313
+ case ts.SyntaxKind.PercentToken:
314
+ return "mod";
315
+ case ts.SyntaxKind.EqualsEqualsToken:
316
+ return "eq";
317
+ case ts.SyntaxKind.ExclamationEqualsToken:
318
+ return "neq";
319
+ case ts.SyntaxKind.EqualsEqualsEqualsToken:
320
+ return "seq";
321
+ case ts.SyntaxKind.ExclamationEqualsEqualsToken:
322
+ return "sneq";
323
+ case ts.SyntaxKind.LessThanToken:
324
+ return "lt";
325
+ case ts.SyntaxKind.LessThanEqualsToken:
326
+ return "lte";
327
+ case ts.SyntaxKind.GreaterThanToken:
328
+ return "gt";
329
+ case ts.SyntaxKind.GreaterThanEqualsToken:
330
+ return "gte";
331
+ default:
332
+ return void 0;
333
+ }
334
+ };
335
+ var isNumericBinaryOp = (op) => op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod";
336
+ var unwrapParens = (node) => {
337
+ while (ts.isParenthesizedExpression(node)) node = node.expression;
338
+ return node;
339
+ };
340
+ var numericLiteralValue = (node) => {
341
+ const value = Number(node.text);
342
+ return { type: "number", value };
343
+ };
344
+ var analyzeExpression = (ctx, node) => {
345
+ if (ctx.fallback) return fallback(ctx, ctx.fallback);
346
+ if (ts.isParenthesizedExpression(node)) return analyzeExpression(ctx, node.expression);
347
+ if (ts.isAsExpression(node) || ts.isNonNullExpression(node)) {
348
+ return analyzeExpression(ctx, node.expression);
349
+ }
350
+ if (node.kind === ts.SyntaxKind.TypeAssertionExpression) {
351
+ return analyzeExpression(ctx, node.expression);
352
+ }
353
+ if (ts.isSatisfiesExpression(node)) return analyzeExpression(ctx, node.expression);
354
+ if (node.kind === ts.SyntaxKind.NullKeyword) {
355
+ return { expressionId: ctx.ir.constValue({ type: "null" }), shape: "primitive" };
356
+ }
357
+ if (node.kind === ts.SyntaxKind.TrueKeyword) {
358
+ return {
359
+ expressionId: ctx.ir.constValue({ type: "boolean", value: true }),
360
+ shape: "primitive"
361
+ };
362
+ }
363
+ if (node.kind === ts.SyntaxKind.FalseKeyword) {
364
+ return {
365
+ expressionId: ctx.ir.constValue({ type: "boolean", value: false }),
366
+ shape: "primitive"
367
+ };
368
+ }
369
+ if (ts.isNumericLiteral(node)) {
370
+ return { expressionId: ctx.ir.constValue(numericLiteralValue(node)), shape: "primitive" };
371
+ }
372
+ if (ts.isPrefixUnaryExpression(node) && ts.isNumericLiteral(node.operand) && node.operator === ts.SyntaxKind.MinusToken) {
373
+ const value = -Number(node.operand.text);
374
+ return {
375
+ expressionId: ctx.ir.constValue(
376
+ Object.is(value, -0) ? { type: "number", value: 0, negativeZero: true } : { type: "number", value }
377
+ ),
378
+ shape: "primitive"
379
+ };
380
+ }
381
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
382
+ return {
383
+ expressionId: ctx.ir.constValue({ type: "string", value: node.text }),
384
+ shape: "primitive"
385
+ };
386
+ }
387
+ if (ts.isBigIntLiteral(node)) {
388
+ return {
389
+ expressionId: ctx.ir.constValue({ type: "bigint", value: node.text.replace(/n$/, "") }),
390
+ shape: "primitive"
391
+ };
392
+ }
393
+ if (ts.isIdentifier(node)) {
394
+ if (node.text === "undefined") {
395
+ return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "primitive" };
396
+ }
397
+ const binding = lookup(ctx, node.text);
398
+ if (!binding) return capture(ctx, node, "external_state");
399
+ if (binding.mutable) return capture(ctx, node, "mutable_object");
400
+ return {
401
+ expressionId: ctx.ir.alias(binding.expressionId),
402
+ shape: binding.shape
403
+ };
404
+ }
405
+ if (ts.isTemplateExpression(node)) {
406
+ const parts = [
407
+ { kind: "lit", value: node.head.text }
408
+ ];
409
+ for (const span of node.templateSpans) {
410
+ const nested = analyzeExpression(ctx, span.expression);
411
+ if (nested.shape !== "primitive" && !nested.captured) {
412
+ return capture(ctx, node, "unsupported_expression");
413
+ }
414
+ parts.push({ kind: "expr", expressionId: nested.expressionId });
415
+ parts.push({ kind: "lit", value: span.literal.text });
416
+ }
417
+ return { expressionId: ctx.ir.template(parts), shape: "primitive" };
418
+ }
419
+ if (ts.isArrayLiteralExpression(node)) {
420
+ if (node.elements.some((element) => ts.isSpreadElement(element))) {
421
+ return capture(ctx, node, "unsupported_expression");
422
+ }
423
+ const elements = node.elements.map((element) => {
424
+ if (ts.isOmittedExpression(element)) return { kind: "hole" };
425
+ return analyzeExpression(ctx, element).expressionId;
426
+ });
427
+ return { expressionId: ctx.ir.array(elements), shape: "array" };
428
+ }
429
+ if (ts.isObjectLiteralExpression(node)) {
430
+ const properties = [];
431
+ for (const property of node.properties) {
432
+ if (ts.isShorthandPropertyAssignment(property)) {
433
+ const nested = analyzeExpression(ctx, property.name);
434
+ properties.push({ key: property.name.text, value: nested.expressionId });
435
+ continue;
436
+ }
437
+ if (!ts.isPropertyAssignment(property)) {
438
+ return capture(ctx, node, "unsupported_expression");
439
+ }
440
+ if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
441
+ return capture(ctx, node, "unsupported_expression");
442
+ }
443
+ const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
444
+ if (key === void 0) return capture(ctx, node, "unsupported_expression");
445
+ properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
446
+ }
447
+ return { expressionId: ctx.ir.object(properties), shape: "object" };
448
+ }
449
+ if (ts.isPropertyAccessExpression(node)) {
450
+ if (node.questionDotToken) return capture(ctx, node, "unknown_property");
451
+ const object = analyzeExpression(ctx, node.expression);
452
+ if (object.shape === "object" && ts.isIdentifier(node.name)) {
453
+ return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
454
+ }
455
+ return capture(ctx, node, "unknown_property");
456
+ }
457
+ if (ts.isElementAccessExpression(node)) {
458
+ if (node.questionDotToken) return capture(ctx, node, "unknown_property");
459
+ const array = analyzeExpression(ctx, node.expression);
460
+ if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
461
+ return {
462
+ expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
463
+ shape: "unknown"
464
+ };
465
+ }
466
+ return capture(ctx, node, "unknown_property");
467
+ }
468
+ if (ts.isCallExpression(node)) {
469
+ if (node.questionDotToken || node.typeArguments?.length) {
470
+ return capture(ctx, node, "unsupported_expression");
471
+ }
472
+ if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
473
+ const calleeName = node.expression.text;
474
+ if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
475
+ const provenId = ctx.provenExact.get(calleeName);
476
+ ctx.callEdges.push({
477
+ callerFunctionId: ctx.functionId,
478
+ calleeName,
479
+ ...provenId ? { calleeFunctionId: provenId } : {}
480
+ });
481
+ if (!provenId) return capture(ctx, node, "opaque_call");
482
+ const args = node.arguments.map((argument) => {
483
+ if (ts.isSpreadElement(argument)) return void 0;
484
+ return analyzeExpression(ctx, argument).expressionId;
485
+ });
486
+ if (args.some((argument) => argument === void 0))
487
+ return capture(ctx, node, "unsupported_expression");
488
+ return {
489
+ expressionId: ctx.ir.pureCall(
490
+ provenId,
491
+ args.filter((id) => id !== void 0)
492
+ ),
493
+ shape: "unknown"
494
+ };
495
+ }
496
+ if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
497
+ return capture(
498
+ ctx,
499
+ node,
500
+ node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
501
+ );
502
+ }
503
+ if (ts.isConditionalExpression(node)) {
504
+ const test = analyzeExpression(ctx, node.condition);
505
+ const consequent = analyzeExpression(ctx, node.whenTrue);
506
+ const alternate = analyzeExpression(ctx, node.whenFalse);
507
+ return {
508
+ expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
509
+ shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
510
+ };
511
+ }
512
+ if (ts.isBinaryExpression(node)) {
513
+ if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
514
+ ctx.fallback ??= "mutable_state";
515
+ return capture(ctx, node, "mutable_object");
516
+ }
517
+ if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
518
+ const left2 = analyzeExpression(ctx, node.left);
519
+ const right2 = analyzeExpression(ctx, node.right);
520
+ const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
521
+ return {
522
+ expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
523
+ shape: "unknown"
524
+ };
525
+ }
526
+ const op = binaryOp(node.operatorToken.kind);
527
+ if (!op) return capture(ctx, node, "unsupported_expression");
528
+ if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
529
+ const left = analyzeExpression(ctx, node.left);
530
+ const right = analyzeExpression(ctx, node.right);
531
+ const numeric = isNumericBinaryOp(op);
532
+ const expressionId = ctx.ir.binary(
533
+ op,
534
+ left.expressionId,
535
+ right.expressionId,
536
+ numeric ? "numeric" : "primitive"
537
+ );
538
+ if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
539
+ ctx.hasCoercionGuard = true;
540
+ const fallbackRootId = ctx.nextRootId.value++;
541
+ ctx.guardWraps.push({
542
+ fallbackRootId,
543
+ start: node.getStart(ctx.sourceFile),
544
+ end: node.end,
545
+ op
546
+ });
547
+ }
548
+ return { expressionId, shape: "primitive" };
549
+ }
550
+ if (ts.isPrefixUnaryExpression(node)) {
551
+ const operand = analyzeExpression(ctx, node.operand);
552
+ if (node.operator === ts.SyntaxKind.ExclamationToken) {
553
+ return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
554
+ }
555
+ if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
556
+ if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
557
+ return {
558
+ expressionId: ctx.ir.unary(
559
+ node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
560
+ operand.expressionId
561
+ ),
562
+ shape: "primitive"
563
+ };
564
+ }
565
+ return capture(ctx, node, "unsupported_expression");
566
+ }
567
+ if (ts.isTypeOfExpression(node)) {
568
+ const operand = unwrapParens(node.expression);
569
+ if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
570
+ return capture(ctx, node, "external_state");
571
+ }
572
+ const inner = analyzeExpression(ctx, node.expression);
573
+ return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
574
+ }
575
+ return capture(ctx, node, "unsupported_expression");
576
+ };
577
+ var bindIdentifier = (ctx, name, result, declaration) => {
578
+ const current = ctx.scopes[ctx.scopes.length - 1];
579
+ current?.set(name.text, {
580
+ expressionId: result.expressionId,
581
+ shape: result.shape,
582
+ mutable: false
583
+ });
584
+ const position = loc(name, ctx.sourceFile);
585
+ const available = loc(declaration, ctx.sourceFile);
586
+ ctx.variables.push({
587
+ id: ctx.nextVariableId.value++,
588
+ name: name.text,
589
+ functionId: ctx.functionId,
590
+ scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
591
+ line: position.line,
592
+ column: position.column,
593
+ assignmentId: ctx.nextAssignmentId.value++,
594
+ expressionId: result.expressionId,
595
+ availableAtLine: available.line,
596
+ availableAtColumn: available.column
597
+ });
598
+ };
599
+ var analyzeStatement = (ctx, node) => {
600
+ if (ctx.fallback) return;
601
+ if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
602
+ if (ts.isBlock(node)) {
603
+ ctx.scopes.push(/* @__PURE__ */ new Map());
604
+ for (const statement of node.statements) analyzeStatement(ctx, statement);
605
+ ctx.scopes.pop();
606
+ return;
607
+ }
608
+ if (ts.isReturnStatement(node)) {
609
+ if (!node.expression) {
610
+ ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
611
+ return;
612
+ }
613
+ ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
614
+ return;
615
+ }
616
+ if (ts.isVariableStatement(node)) {
617
+ if (node.declarationList.flags & ts.NodeFlags.Using) {
618
+ fallback(ctx, "side_effect");
619
+ return;
620
+ }
621
+ for (const declaration of node.declarationList.declarations) {
622
+ if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
623
+ fallback(ctx, "unsupported_expression");
624
+ return;
625
+ }
626
+ const result = analyzeExpression(ctx, declaration.initializer);
627
+ bindIdentifier(ctx, declaration.name, result, declaration);
628
+ }
629
+ return;
630
+ }
631
+ if (ts.isExpressionStatement(node)) {
632
+ analyzeExpression(ctx, node.expression);
633
+ return;
634
+ }
635
+ if (ts.isThrowStatement(node)) {
636
+ ctx.hasThrow = true;
637
+ analyzeExpression(ctx, node.expression);
638
+ return;
639
+ }
640
+ if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
641
+ fallback(ctx, "unsupported_expression");
642
+ return;
643
+ }
644
+ if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
645
+ fallback(ctx, "has_loop");
646
+ return;
647
+ }
648
+ fallback(ctx, "unsupported_expression");
649
+ };
650
+ var bindParams = (ctx, node) => {
651
+ let index = 0;
652
+ for (const parameter of node.parameters) {
653
+ if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
654
+ if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
655
+ fallback(ctx, "unsupported_expression");
656
+ index++;
657
+ continue;
658
+ }
659
+ const expressionId = ctx.ir.param(index);
660
+ ctx.identifierParams.push(parameter.name.text);
661
+ bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
662
+ index++;
663
+ }
664
+ };
665
+ var analyzeOne = (node, functionId, name, sourceFile, provenExact) => {
666
+ const ctx = {
667
+ sourceFile,
668
+ functionId,
669
+ name,
670
+ ir: new DerivationIrBuilder(),
671
+ variables: [],
672
+ captureWraps: [],
673
+ guardWraps: [],
674
+ callEdges: [],
675
+ scopes: [/* @__PURE__ */ new Map()],
676
+ nextRootId: { value: 0 },
677
+ nextVariableId: { value: 0 },
678
+ nextAssignmentId: { value: 0 },
679
+ hasThrow: false,
680
+ hasCoercionGuard: false,
681
+ provenExact,
682
+ identifierParams: []
683
+ };
684
+ if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
685
+ ctx.fallback = "async";
686
+ } else {
687
+ bindParams(ctx, node);
688
+ if (ts.isBlock(node.body)) {
689
+ for (const statement of node.body.statements) analyzeStatement(ctx, statement);
690
+ } else {
691
+ ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
692
+ }
693
+ }
694
+ const plan = {
695
+ ...emptyPlan(functionId),
696
+ expressions: ctx.ir.expressions,
697
+ variables: ctx.variables,
698
+ captureBoundaries: ctx.captureWraps.map((wrap) => ({
699
+ id: wrap.rootId,
700
+ reason: wrap.reason,
701
+ expressionId: wrap.expressionId,
702
+ line: wrap.line,
703
+ column: wrap.column
704
+ })),
705
+ controlFacts: [],
706
+ ...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
707
+ ...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
708
+ };
709
+ return {
710
+ functionId,
711
+ name,
712
+ node,
713
+ plan,
714
+ captureWraps: ctx.fallback ? [] : ctx.captureWraps,
715
+ guardWraps: ctx.fallback ? [] : ctx.guardWraps,
716
+ callEdges: ctx.callEdges,
717
+ hasThrow: ctx.hasThrow,
718
+ hasCoercionGuard: ctx.hasCoercionGuard
719
+ };
720
+ };
721
+ var analyzeModuleFunctions = (sourceFile, targets) => {
722
+ const nameCounts = /* @__PURE__ */ new Map();
723
+ for (const target of targets) {
724
+ nameCounts.set(target.name, (nameCounts.get(target.name) ?? 0) + 1);
725
+ }
726
+ const firstPass = targets.map(
727
+ (target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
728
+ );
729
+ const graph = buildDependencyGraph(
730
+ firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
731
+ firstPass.flatMap((fn) => fn.callEdges)
732
+ );
733
+ const order = topologicalFunctionOrder(
734
+ firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
735
+ graph
736
+ );
737
+ const byId = new Map(targets.map((target) => [target.functionId, target]));
738
+ const provenExact = /* @__PURE__ */ new Map();
739
+ const analyzed = [];
740
+ for (const functionId of order) {
741
+ const target = byId.get(functionId);
742
+ if (!target) continue;
743
+ const result = analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact);
744
+ if (functionIsExact(result.plan) && nameCounts.get(result.name) === 1) {
745
+ provenExact.set(result.name, result.functionId);
746
+ }
747
+ analyzed.push(result);
748
+ }
749
+ const remaining = targets.filter(
750
+ (target) => !analyzed.some((fn) => fn.functionId === target.functionId)
751
+ );
752
+ for (const target of remaining) {
753
+ analyzed.push(analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact));
754
+ }
755
+ return analyzed;
756
+ };
757
+
758
+ // src/derivation/emit-capture-plan.ts
759
+ var captureWrapEdits = (wraps, capName) => {
760
+ const edits = [];
761
+ let order = 0;
762
+ for (const wrap of wraps) {
763
+ edits.push({
764
+ position: wrap.start,
765
+ text: `${capName}(${wrap.rootId},`,
766
+ order: order++
767
+ });
768
+ edits.push({
769
+ position: wrap.end,
770
+ text: ")",
771
+ order: order++
772
+ });
773
+ }
774
+ return edits;
775
+ };
147
776
 
148
777
  // src/execution-recorder/automatic-runtime.ts
149
778
  var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
150
779
  var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
151
780
 
152
781
  // src/auto-instrumentation/collect-instrumentation-delta.ts
153
- import ts from "typescript";
782
+ import ts2 from "typescript";
154
783
  var paramBindingFromName = (name, sourceFile) => {
155
- if (ts.isIdentifier(name)) return { kind: "identifier", name: name.text };
156
- if (ts.isArrayBindingPattern(name)) {
784
+ if (ts2.isIdentifier(name)) return { kind: "identifier", name: name.text };
785
+ if (ts2.isArrayBindingPattern(name)) {
157
786
  const elements = name.elements.map(
158
787
  (element) => {
159
- if (ts.isOmittedExpression(element)) return { kind: "omitted" };
788
+ if (ts2.isOmittedExpression(element)) return { kind: "omitted" };
160
789
  const binding = paramBindingFromName(element.name, sourceFile);
161
790
  if (element.dotDotDotToken) return { kind: "rest", binding };
162
791
  return binding;
@@ -166,7 +795,7 @@ var paramBindingFromName = (name, sourceFile) => {
166
795
  }
167
796
  const properties = name.elements.map((element) => {
168
797
  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);
798
+ const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts2.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
170
799
  if (element.dotDotDotToken) {
171
800
  const rest = { kind: "rest", binding };
172
801
  return { key, binding: rest };
@@ -178,7 +807,7 @@ var paramBindingFromName = (name, sourceFile) => {
178
807
  var collectFunctionParams = (node, sourceFile) => {
179
808
  const params = [];
180
809
  for (const parameter of node.parameters) {
181
- if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
810
+ if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
182
811
  const binding = paramBindingFromName(parameter.name, sourceFile);
183
812
  params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
184
813
  }
@@ -219,18 +848,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
219
848
  };
220
849
  var unwrapExpression = (expression) => {
221
850
  let current = expression;
222
- while (ts.isParenthesizedExpression(current) || ts.isNonNullExpression(current)) {
851
+ while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
223
852
  current = current.expression;
224
853
  }
225
- if (ts.isAwaitExpression(current)) return unwrapExpression(current.expression);
854
+ if (ts2.isAwaitExpression(current)) return unwrapExpression(current.expression);
226
855
  return current;
227
856
  };
228
857
  var calleeFromExpression = (expression, sourceFile) => {
229
858
  const current = unwrapExpression(expression);
230
- if (ts.isIdentifier(current)) {
859
+ if (ts2.isIdentifier(current)) {
231
860
  return { calleeName: current.text, calleeText: current.text };
232
861
  }
233
- if (ts.isPropertyAccessExpression(current) && ts.isIdentifier(current.name)) {
862
+ if (ts2.isPropertyAccessExpression(current) && ts2.isIdentifier(current.name)) {
234
863
  return {
235
864
  calleeName: current.name.text,
236
865
  calleeText: current.getText(sourceFile).replace(/\s+/g, "")
@@ -240,9 +869,9 @@ var calleeFromExpression = (expression, sourceFile) => {
240
869
  };
241
870
  var resultBindingForCall = (call) => {
242
871
  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;
872
+ if (ts2.isAwaitExpression(node)) node = node.parent;
873
+ if (!ts2.isVariableDeclaration(node)) return null;
874
+ if (!ts2.isIdentifier(node.name)) return null;
246
875
  const initializer = node.initializer;
247
876
  if (!initializer) return null;
248
877
  const inner = unwrapExpression(initializer);
@@ -252,18 +881,18 @@ var resultBindingForCall = (call) => {
252
881
  var argIdentifiersForCall = (call) => {
253
882
  const identifiers = [];
254
883
  call.arguments?.forEach((argument, index) => {
255
- if (ts.isIdentifier(argument)) {
884
+ if (ts2.isIdentifier(argument)) {
256
885
  identifiers.push({ index, kind: "identifier", name: argument.text });
257
886
  return;
258
887
  }
259
- if (!ts.isObjectLiteralExpression(argument)) return;
888
+ if (!ts2.isObjectLiteralExpression(argument)) return;
260
889
  const properties = [];
261
890
  for (const property of argument.properties) {
262
- if (ts.isShorthandPropertyAssignment(property)) {
891
+ if (ts2.isShorthandPropertyAssignment(property)) {
263
892
  properties.push({ key: property.name.text, name: property.name.text });
264
893
  continue;
265
894
  }
266
- if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && ts.isIdentifier(property.initializer)) {
895
+ if (ts2.isPropertyAssignment(property) && ts2.isIdentifier(property.name) && ts2.isIdentifier(property.initializer)) {
267
896
  properties.push({ key: property.name.text, name: property.initializer.text });
268
897
  }
269
898
  }
@@ -303,8 +932,8 @@ var collectCallSite = (call, caller, context) => {
303
932
  };
304
933
  };
305
934
  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)) {
935
+ if (!declaration.initializer || !ts2.isIdentifier(declaration.initializer)) return null;
936
+ if (!ts2.isObjectBindingPattern(declaration.name) && !ts2.isArrayBindingPattern(declaration.name)) {
308
937
  return null;
309
938
  }
310
939
  const sourceParam = declaration.initializer.text;
@@ -328,33 +957,33 @@ var collectParamDestructure = (declaration, caller, context) => {
328
957
 
329
958
  // src/auto-instrumentation/transform-source.ts
330
959
  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;
960
+ if (/\.tsx$/i.test(filePath)) return ts3.ScriptKind.TSX;
961
+ if (/\.jsx$/i.test(filePath)) return ts3.ScriptKind.JSX;
962
+ if (/\.[cm]?ts$/i.test(filePath)) return ts3.ScriptKind.TS;
963
+ return ts3.ScriptKind.JS;
335
964
  };
336
965
  var functionName = (node, sourceFile) => {
337
966
  if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
338
967
  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);
968
+ if (ts3.isVariableDeclaration(parent) && ts3.isIdentifier(parent.name)) return parent.name.text;
969
+ if (ts3.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
970
+ if (ts3.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
342
971
  const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
343
972
  return `anonymous@${location.line + 1}:${location.character + 1}`;
344
973
  };
345
974
  var functionLocationPosition = (node, sourceFile) => {
346
975
  if (node.name) return node.name.getStart(sourceFile);
347
976
  const parent = node.parent;
348
- if ((ts2.isVariableDeclaration(parent) || ts2.isPropertyAssignment(parent)) && parent.name) {
977
+ if ((ts3.isVariableDeclaration(parent) || ts3.isPropertyAssignment(parent)) && parent.name) {
349
978
  return parent.name.getStart(sourceFile);
350
979
  }
351
980
  return node.getStart(sourceFile);
352
981
  };
353
982
  var bindingValue = (name, sourceFile) => {
354
- if (ts2.isIdentifier(name)) return name.text;
355
- if (ts2.isArrayBindingPattern(name)) {
983
+ if (ts3.isIdentifier(name)) return name.text;
984
+ if (ts3.isArrayBindingPattern(name)) {
356
985
  const values = name.elements.map((element) => {
357
- if (ts2.isOmittedExpression(element)) return "";
986
+ if (ts3.isOmittedExpression(element)) return "";
358
987
  const value = bindingValue(element.name, sourceFile);
359
988
  return element.dotDotDotToken ? `...${value}` : value;
360
989
  });
@@ -370,14 +999,14 @@ var bindingValue = (name, sourceFile) => {
370
999
  };
371
1000
  var arrowArguments = (node) => {
372
1001
  const values = node.parameters.flatMap((parameter) => {
373
- if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
1002
+ if (ts3.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
374
1003
  const value = bindingValue(parameter.name, node.getSourceFile());
375
1004
  return [parameter.dotDotDotToken ? `...${value}` : value];
376
1005
  });
377
1006
  return `[${values.join(",")}]`;
378
1007
  };
379
1008
  var isSupportedFunction = (node) => {
380
- if (!ts2.isFunctionDeclaration(node) && !ts2.isFunctionExpression(node) && !ts2.isArrowFunction(node) && !ts2.isMethodDeclaration(node) && !ts2.isGetAccessorDeclaration(node) && !ts2.isSetAccessorDeclaration(node)) {
1009
+ if (!ts3.isFunctionDeclaration(node) && !ts3.isFunctionExpression(node) && !ts3.isArrowFunction(node) && !ts3.isMethodDeclaration(node) && !ts3.isGetAccessorDeclaration(node) && !ts3.isSetAccessorDeclaration(node)) {
381
1010
  return false;
382
1011
  }
383
1012
  if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
@@ -388,7 +1017,7 @@ var uniqueName = (source, base) => {
388
1017
  while (source.includes(name)) name += "_";
389
1018
  return name;
390
1019
  };
391
- var isDirectivePrologue = (statement) => ts2.isExpressionStatement(statement) && ts2.isStringLiteral(statement.expression);
1020
+ var isDirectivePrologue = (statement) => ts3.isExpressionStatement(statement) && ts3.isStringLiteral(statement.expression);
392
1021
  var insertPositionAfterPrologue = (source, sourceFile) => {
393
1022
  const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
394
1023
  let end = shebangEnd;
@@ -403,7 +1032,7 @@ var preambleInsert = (source, position, preamble) => {
403
1032
  const previous = source[position - 1];
404
1033
  return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
405
1034
  };
406
- var modulePreamble = (runName, plansName, plansJson) => `const ${plansName}=${plansJson};const ${runName}=((g,k)=>{const r=g[k]??(g[k]={runtimes:[],modules:[],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)=>d.run(i,a,c)}});return r.register(${plansName})})(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));`;
1035
+ 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)}));`;
407
1036
  var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
408
1037
  const start = mappedLocation(
409
1038
  sourceFile,
@@ -434,80 +1063,127 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
434
1063
  paramNames
435
1064
  };
436
1065
  };
1066
+ var canOmitCallValues = (analysis, analyzed) => {
1067
+ if (!functionIsExact(analysis.plan) || analysis.hasThrow || analysis.hasCoercionGuard) {
1068
+ return false;
1069
+ }
1070
+ const callers = analyzed.filter(
1071
+ (caller) => caller.callEdges.some((edge) => edge.calleeFunctionId === analysis.functionId)
1072
+ );
1073
+ if (callers.length === 0) return false;
1074
+ return callers.every((caller) => caller.plan.fallbackReason === void 0 && !caller.hasThrow);
1075
+ };
437
1076
  var transformSource = (source, options) => {
438
1077
  if (source.includes("@rasputin-ignore-file")) return void 0;
439
- const sourceFile = ts2.createSourceFile(
1078
+ const sourceFile = ts3.createSourceFile(
440
1079
  options.filePath,
441
1080
  source,
442
- ts2.ScriptTarget.Latest,
1081
+ ts3.ScriptTarget.Latest,
443
1082
  true,
444
1083
  scriptKindFor(options.filePath)
445
1084
  );
446
1085
  const runName = uniqueName(source, "__rasputinRun__");
1086
+ const capName = uniqueName(source, "__rasputinCap__");
447
1087
  const plansName = uniqueName(source, "__rasputinPlans__");
448
1088
  const absolutePath = resolve4(options.filePath);
1089
+ const derivationMode = options.derivationMode ?? "analysis";
449
1090
  const edits = [];
450
1091
  let order = 0;
451
1092
  const delta = {
452
1093
  functions: [],
453
1094
  callSites: [],
454
- destructures: []
1095
+ destructures: [],
1096
+ derivationPlans: []
455
1097
  };
456
1098
  const collectContext = {
457
1099
  sourceFile,
458
1100
  mapLocation: options.mapLocation,
459
1101
  absolutePath
460
1102
  };
1103
+ const pending = [];
461
1104
  const visit = (node, caller) => {
462
1105
  if (isSupportedFunction(node)) {
463
1106
  const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
464
1107
  if (!collected) {
465
- ts2.forEachChild(node, (child) => visit(child, caller));
1108
+ ts3.forEachChild(node, (child) => visit(child, caller));
466
1109
  return;
467
1110
  }
468
- const planIndex = delta.functions.length;
1111
+ pending.push({ node, collected });
469
1112
  delta.functions.push(collected);
470
- const args = ts2.isArrowFunction(node) ? arrowArguments(node) : "arguments";
471
- const asyncCallback = node.modifiers?.some(
472
- (modifier) => modifier.kind === ts2.SyntaxKind.AsyncKeyword
473
- ) ? "async " : "";
474
- if (ts2.isBlock(node.body)) {
475
- edits.push({
476
- position: node.body.getStart(sourceFile) + 1,
477
- text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
478
- order: order++
479
- });
480
- edits.push({ position: node.body.end - 1, text: "});", order: order++ });
481
- } else {
482
- edits.push({
483
- position: node.body.getStart(sourceFile),
484
- text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
485
- order: order++
486
- });
487
- edits.push({ position: node.body.end, text: "))", order: order++ });
488
- }
489
- ts2.forEachChild(node, (child) => visit(child, collected));
1113
+ ts3.forEachChild(node, (child) => visit(child, collected));
490
1114
  return;
491
1115
  }
492
- if (caller && (ts2.isCallExpression(node) || ts2.isNewExpression(node))) {
1116
+ if (caller && (ts3.isCallExpression(node) || ts3.isNewExpression(node))) {
493
1117
  const site = collectCallSite(node, caller, collectContext);
494
1118
  if (site) delta.callSites.push(site);
495
1119
  }
496
- if (caller && ts2.isVariableDeclaration(node)) {
1120
+ if (caller && ts3.isVariableDeclaration(node)) {
497
1121
  const destructure = collectParamDestructure(node, caller, collectContext);
498
1122
  if (destructure) delta.destructures.push(destructure);
499
1123
  }
500
- ts2.forEachChild(node, (child) => visit(child, caller));
1124
+ ts3.forEachChild(node, (child) => visit(child, caller));
501
1125
  };
502
1126
  visit(sourceFile);
503
- if (edits.length === 0) return void 0;
1127
+ if (pending.length === 0) return void 0;
1128
+ const analyzed = analyzeModuleFunctions(
1129
+ sourceFile,
1130
+ pending.map((entry) => ({
1131
+ functionId: entry.collected.functionId,
1132
+ name: entry.collected.name,
1133
+ node: entry.node
1134
+ }))
1135
+ );
1136
+ delta.derivationPlans = analyzed.map((fn) => fn.plan);
1137
+ const analyzedById = new Map(analyzed.map((fn) => [fn.functionId, fn]));
1138
+ for (const [planIndex, entry] of pending.entries()) {
1139
+ const analysis = analyzedById.get(entry.collected.functionId);
1140
+ const omitValues = derivationMode === "selective" && analysis && canOmitCallValues(analysis, analyzed);
1141
+ const valueFlag = omitValues ? ",1" : "";
1142
+ const args = ts3.isArrowFunction(entry.node) ? arrowArguments(entry.node) : "arguments";
1143
+ const asyncCallback = entry.node.modifiers?.some(
1144
+ (modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword
1145
+ ) ? "async " : "";
1146
+ if (ts3.isBlock(entry.node.body)) {
1147
+ edits.push({
1148
+ position: entry.node.body.getStart(sourceFile) + 1,
1149
+ text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
1150
+ order: order++
1151
+ });
1152
+ edits.push({
1153
+ position: entry.node.body.end - 1,
1154
+ text: `}${valueFlag});`,
1155
+ order: order++
1156
+ });
1157
+ } else {
1158
+ edits.push({
1159
+ position: entry.node.body.getStart(sourceFile),
1160
+ text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
1161
+ order: order++
1162
+ });
1163
+ edits.push({
1164
+ position: entry.node.body.end,
1165
+ text: `)${valueFlag})`,
1166
+ order: order++
1167
+ });
1168
+ }
1169
+ if (analysis) {
1170
+ for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
1171
+ edits.push({ ...edit, order: order++ });
1172
+ }
1173
+ }
1174
+ }
504
1175
  const insertAt = insertPositionAfterPrologue(source, sourceFile);
505
1176
  edits.push({
506
1177
  position: insertAt,
507
1178
  text: preambleInsert(
508
1179
  source,
509
1180
  insertAt,
510
- modulePreamble(runName, plansName, JSON.stringify(delta.functions.map((fn) => fn.plan)))
1181
+ modulePreamble(
1182
+ runName,
1183
+ capName,
1184
+ plansName,
1185
+ JSON.stringify(delta.functions.map((fn) => fn.plan))
1186
+ )
511
1187
  ),
512
1188
  order: -1
513
1189
  });