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

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 +42 -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 +767 -71
  26. package/dist/instrument/bun.js +748 -66
  27. package/dist/instrument/node.js +748 -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,655 @@ 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
+ if (nested.captured) {
435
+ const wrap = ctx.captureWraps.find(
436
+ (candidate) => candidate.expressionId === nested.expressionId
437
+ );
438
+ if (wrap) wrap.shorthandPropertyName = property.name.text;
439
+ }
440
+ properties.push({ key: property.name.text, value: nested.expressionId });
441
+ continue;
442
+ }
443
+ if (!ts.isPropertyAssignment(property)) {
444
+ return capture(ctx, node, "unsupported_expression");
445
+ }
446
+ if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
447
+ return capture(ctx, node, "unsupported_expression");
448
+ }
449
+ const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
450
+ if (key === void 0) return capture(ctx, node, "unsupported_expression");
451
+ properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
452
+ }
453
+ return { expressionId: ctx.ir.object(properties), shape: "object" };
454
+ }
455
+ if (ts.isPropertyAccessExpression(node)) {
456
+ if (node.questionDotToken) return capture(ctx, node, "unknown_property");
457
+ const object = analyzeExpression(ctx, node.expression);
458
+ if (object.shape === "object" && ts.isIdentifier(node.name)) {
459
+ return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
460
+ }
461
+ return capture(ctx, node, "unknown_property");
462
+ }
463
+ if (ts.isElementAccessExpression(node)) {
464
+ if (node.questionDotToken) return capture(ctx, node, "unknown_property");
465
+ const array = analyzeExpression(ctx, node.expression);
466
+ if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
467
+ return {
468
+ expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
469
+ shape: "unknown"
470
+ };
471
+ }
472
+ return capture(ctx, node, "unknown_property");
473
+ }
474
+ if (ts.isCallExpression(node)) {
475
+ if (node.questionDotToken || node.typeArguments?.length) {
476
+ return capture(ctx, node, "unsupported_expression");
477
+ }
478
+ if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
479
+ const calleeName = node.expression.text;
480
+ if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
481
+ const provenId = ctx.provenExact.get(calleeName);
482
+ ctx.callEdges.push({
483
+ callerFunctionId: ctx.functionId,
484
+ calleeName,
485
+ ...provenId ? { calleeFunctionId: provenId } : {}
486
+ });
487
+ if (!provenId) return capture(ctx, node, "opaque_call");
488
+ const args = node.arguments.map((argument) => {
489
+ if (ts.isSpreadElement(argument)) return void 0;
490
+ return analyzeExpression(ctx, argument).expressionId;
491
+ });
492
+ if (args.some((argument) => argument === void 0))
493
+ return capture(ctx, node, "unsupported_expression");
494
+ return {
495
+ expressionId: ctx.ir.pureCall(
496
+ provenId,
497
+ args.filter((id) => id !== void 0)
498
+ ),
499
+ shape: "unknown"
500
+ };
501
+ }
502
+ if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
503
+ return capture(
504
+ ctx,
505
+ node,
506
+ node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
507
+ );
508
+ }
509
+ if (ts.isConditionalExpression(node)) {
510
+ const test = analyzeExpression(ctx, node.condition);
511
+ const consequent = analyzeExpression(ctx, node.whenTrue);
512
+ const alternate = analyzeExpression(ctx, node.whenFalse);
513
+ return {
514
+ expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
515
+ shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
516
+ };
517
+ }
518
+ if (ts.isBinaryExpression(node)) {
519
+ if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
520
+ ctx.fallback ??= "mutable_state";
521
+ return capture(ctx, node, "mutable_object");
522
+ }
523
+ if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
524
+ const left2 = analyzeExpression(ctx, node.left);
525
+ const right2 = analyzeExpression(ctx, node.right);
526
+ const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
527
+ return {
528
+ expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
529
+ shape: "unknown"
530
+ };
531
+ }
532
+ const op = binaryOp(node.operatorToken.kind);
533
+ if (!op) return capture(ctx, node, "unsupported_expression");
534
+ if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
535
+ const left = analyzeExpression(ctx, node.left);
536
+ const right = analyzeExpression(ctx, node.right);
537
+ const numeric = isNumericBinaryOp(op);
538
+ const expressionId = ctx.ir.binary(
539
+ op,
540
+ left.expressionId,
541
+ right.expressionId,
542
+ numeric ? "numeric" : "primitive"
543
+ );
544
+ if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
545
+ ctx.hasCoercionGuard = true;
546
+ const fallbackRootId = ctx.nextRootId.value++;
547
+ ctx.guardWraps.push({
548
+ fallbackRootId,
549
+ start: node.getStart(ctx.sourceFile),
550
+ end: node.end,
551
+ op
552
+ });
553
+ }
554
+ return { expressionId, shape: "primitive" };
555
+ }
556
+ if (ts.isPrefixUnaryExpression(node)) {
557
+ const operand = analyzeExpression(ctx, node.operand);
558
+ if (node.operator === ts.SyntaxKind.ExclamationToken) {
559
+ return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
560
+ }
561
+ if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
562
+ if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
563
+ return {
564
+ expressionId: ctx.ir.unary(
565
+ node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
566
+ operand.expressionId
567
+ ),
568
+ shape: "primitive"
569
+ };
570
+ }
571
+ return capture(ctx, node, "unsupported_expression");
572
+ }
573
+ if (ts.isTypeOfExpression(node)) {
574
+ const operand = unwrapParens(node.expression);
575
+ if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
576
+ return capture(ctx, node, "external_state");
577
+ }
578
+ const inner = analyzeExpression(ctx, node.expression);
579
+ return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
580
+ }
581
+ return capture(ctx, node, "unsupported_expression");
582
+ };
583
+ var bindIdentifier = (ctx, name, result, declaration) => {
584
+ const current = ctx.scopes[ctx.scopes.length - 1];
585
+ current?.set(name.text, {
586
+ expressionId: result.expressionId,
587
+ shape: result.shape,
588
+ mutable: false
589
+ });
590
+ const position = loc(name, ctx.sourceFile);
591
+ const available = loc(declaration, ctx.sourceFile);
592
+ ctx.variables.push({
593
+ id: ctx.nextVariableId.value++,
594
+ name: name.text,
595
+ functionId: ctx.functionId,
596
+ scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
597
+ line: position.line,
598
+ column: position.column,
599
+ assignmentId: ctx.nextAssignmentId.value++,
600
+ expressionId: result.expressionId,
601
+ availableAtLine: available.line,
602
+ availableAtColumn: available.column
603
+ });
604
+ };
605
+ var analyzeStatement = (ctx, node) => {
606
+ if (ctx.fallback) return;
607
+ if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
608
+ if (ts.isBlock(node)) {
609
+ ctx.scopes.push(/* @__PURE__ */ new Map());
610
+ for (const statement of node.statements) analyzeStatement(ctx, statement);
611
+ ctx.scopes.pop();
612
+ return;
613
+ }
614
+ if (ts.isReturnStatement(node)) {
615
+ if (!node.expression) {
616
+ ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
617
+ return;
618
+ }
619
+ ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
620
+ return;
621
+ }
622
+ if (ts.isVariableStatement(node)) {
623
+ if (node.declarationList.flags & ts.NodeFlags.Using) {
624
+ fallback(ctx, "side_effect");
625
+ return;
626
+ }
627
+ for (const declaration of node.declarationList.declarations) {
628
+ if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
629
+ fallback(ctx, "unsupported_expression");
630
+ return;
631
+ }
632
+ const result = analyzeExpression(ctx, declaration.initializer);
633
+ bindIdentifier(ctx, declaration.name, result, declaration);
634
+ }
635
+ return;
636
+ }
637
+ if (ts.isExpressionStatement(node)) {
638
+ analyzeExpression(ctx, node.expression);
639
+ return;
640
+ }
641
+ if (ts.isThrowStatement(node)) {
642
+ ctx.hasThrow = true;
643
+ analyzeExpression(ctx, node.expression);
644
+ return;
645
+ }
646
+ if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
647
+ fallback(ctx, "unsupported_expression");
648
+ return;
649
+ }
650
+ if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
651
+ fallback(ctx, "has_loop");
652
+ return;
653
+ }
654
+ fallback(ctx, "unsupported_expression");
655
+ };
656
+ var bindParams = (ctx, node) => {
657
+ let index = 0;
658
+ for (const parameter of node.parameters) {
659
+ if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
660
+ if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
661
+ fallback(ctx, "unsupported_expression");
662
+ index++;
663
+ continue;
664
+ }
665
+ const expressionId = ctx.ir.param(index);
666
+ ctx.identifierParams.push(parameter.name.text);
667
+ bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
668
+ index++;
669
+ }
670
+ };
671
+ var analyzeOne = (node, functionId, name, sourceFile, provenExact) => {
672
+ const ctx = {
673
+ sourceFile,
674
+ functionId,
675
+ name,
676
+ ir: new DerivationIrBuilder(),
677
+ variables: [],
678
+ captureWraps: [],
679
+ guardWraps: [],
680
+ callEdges: [],
681
+ scopes: [/* @__PURE__ */ new Map()],
682
+ nextRootId: { value: 0 },
683
+ nextVariableId: { value: 0 },
684
+ nextAssignmentId: { value: 0 },
685
+ hasThrow: false,
686
+ hasCoercionGuard: false,
687
+ provenExact,
688
+ identifierParams: []
689
+ };
690
+ if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
691
+ ctx.fallback = "async";
692
+ } else {
693
+ bindParams(ctx, node);
694
+ if (ts.isBlock(node.body)) {
695
+ for (const statement of node.body.statements) analyzeStatement(ctx, statement);
696
+ } else {
697
+ ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
698
+ }
699
+ }
700
+ const plan = {
701
+ ...emptyPlan(functionId),
702
+ expressions: ctx.ir.expressions,
703
+ variables: ctx.variables,
704
+ captureBoundaries: ctx.captureWraps.map((wrap) => ({
705
+ id: wrap.rootId,
706
+ reason: wrap.reason,
707
+ expressionId: wrap.expressionId,
708
+ line: wrap.line,
709
+ column: wrap.column
710
+ })),
711
+ controlFacts: [],
712
+ ...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
713
+ ...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
714
+ };
715
+ return {
716
+ functionId,
717
+ name,
718
+ node,
719
+ plan,
720
+ captureWraps: ctx.fallback ? [] : ctx.captureWraps,
721
+ guardWraps: ctx.fallback ? [] : ctx.guardWraps,
722
+ callEdges: ctx.callEdges,
723
+ hasThrow: ctx.hasThrow,
724
+ hasCoercionGuard: ctx.hasCoercionGuard
725
+ };
726
+ };
727
+ var analyzeModuleFunctions = (sourceFile, targets) => {
728
+ const nameCounts = /* @__PURE__ */ new Map();
729
+ for (const target of targets) {
730
+ nameCounts.set(target.name, (nameCounts.get(target.name) ?? 0) + 1);
731
+ }
732
+ const firstPass = targets.map(
733
+ (target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
734
+ );
735
+ const graph = buildDependencyGraph(
736
+ firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
737
+ firstPass.flatMap((fn) => fn.callEdges)
738
+ );
739
+ const order = topologicalFunctionOrder(
740
+ firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
741
+ graph
742
+ );
743
+ const byId = new Map(targets.map((target) => [target.functionId, target]));
744
+ const provenExact = /* @__PURE__ */ new Map();
745
+ const analyzed = [];
746
+ for (const functionId of order) {
747
+ const target = byId.get(functionId);
748
+ if (!target) continue;
749
+ const result = analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact);
750
+ if (functionIsExact(result.plan) && nameCounts.get(result.name) === 1) {
751
+ provenExact.set(result.name, result.functionId);
752
+ }
753
+ analyzed.push(result);
754
+ }
755
+ const remaining = targets.filter(
756
+ (target) => !analyzed.some((fn) => fn.functionId === target.functionId)
757
+ );
758
+ for (const target of remaining) {
759
+ analyzed.push(analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact));
760
+ }
761
+ return analyzed;
762
+ };
763
+
764
+ // src/derivation/emit-capture-plan.ts
765
+ var captureWrapEdits = (wraps, capName) => {
766
+ const edits = [];
767
+ let order = 0;
768
+ for (const wrap of wraps) {
769
+ edits.push({
770
+ position: wrap.start,
771
+ text: `${wrap.shorthandPropertyName ? `${wrap.shorthandPropertyName}:` : ""}${capName}(${wrap.rootId},`,
772
+ order: order++
773
+ });
774
+ edits.push({
775
+ position: wrap.end,
776
+ text: ")",
777
+ order: order++
778
+ });
779
+ }
780
+ return edits;
781
+ };
147
782
 
148
783
  // src/execution-recorder/automatic-runtime.ts
149
784
  var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
150
785
  var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
151
786
 
152
787
  // src/auto-instrumentation/collect-instrumentation-delta.ts
153
- import ts from "typescript";
788
+ import ts2 from "typescript";
154
789
  var paramBindingFromName = (name, sourceFile) => {
155
- if (ts.isIdentifier(name)) return { kind: "identifier", name: name.text };
156
- if (ts.isArrayBindingPattern(name)) {
790
+ if (ts2.isIdentifier(name)) return { kind: "identifier", name: name.text };
791
+ if (ts2.isArrayBindingPattern(name)) {
157
792
  const elements = name.elements.map(
158
793
  (element) => {
159
- if (ts.isOmittedExpression(element)) return { kind: "omitted" };
794
+ if (ts2.isOmittedExpression(element)) return { kind: "omitted" };
160
795
  const binding = paramBindingFromName(element.name, sourceFile);
161
796
  if (element.dotDotDotToken) return { kind: "rest", binding };
162
797
  return binding;
@@ -166,7 +801,7 @@ var paramBindingFromName = (name, sourceFile) => {
166
801
  }
167
802
  const properties = name.elements.map((element) => {
168
803
  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);
804
+ const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts2.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
170
805
  if (element.dotDotDotToken) {
171
806
  const rest = { kind: "rest", binding };
172
807
  return { key, binding: rest };
@@ -178,7 +813,7 @@ var paramBindingFromName = (name, sourceFile) => {
178
813
  var collectFunctionParams = (node, sourceFile) => {
179
814
  const params = [];
180
815
  for (const parameter of node.parameters) {
181
- if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
816
+ if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
182
817
  const binding = paramBindingFromName(parameter.name, sourceFile);
183
818
  params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
184
819
  }
@@ -219,18 +854,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
219
854
  };
220
855
  var unwrapExpression = (expression) => {
221
856
  let current = expression;
222
- while (ts.isParenthesizedExpression(current) || ts.isNonNullExpression(current)) {
857
+ while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
223
858
  current = current.expression;
224
859
  }
225
- if (ts.isAwaitExpression(current)) return unwrapExpression(current.expression);
860
+ if (ts2.isAwaitExpression(current)) return unwrapExpression(current.expression);
226
861
  return current;
227
862
  };
228
863
  var calleeFromExpression = (expression, sourceFile) => {
229
864
  const current = unwrapExpression(expression);
230
- if (ts.isIdentifier(current)) {
865
+ if (ts2.isIdentifier(current)) {
231
866
  return { calleeName: current.text, calleeText: current.text };
232
867
  }
233
- if (ts.isPropertyAccessExpression(current) && ts.isIdentifier(current.name)) {
868
+ if (ts2.isPropertyAccessExpression(current) && ts2.isIdentifier(current.name)) {
234
869
  return {
235
870
  calleeName: current.name.text,
236
871
  calleeText: current.getText(sourceFile).replace(/\s+/g, "")
@@ -240,9 +875,9 @@ var calleeFromExpression = (expression, sourceFile) => {
240
875
  };
241
876
  var resultBindingForCall = (call) => {
242
877
  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;
878
+ if (ts2.isAwaitExpression(node)) node = node.parent;
879
+ if (!ts2.isVariableDeclaration(node)) return null;
880
+ if (!ts2.isIdentifier(node.name)) return null;
246
881
  const initializer = node.initializer;
247
882
  if (!initializer) return null;
248
883
  const inner = unwrapExpression(initializer);
@@ -252,18 +887,18 @@ var resultBindingForCall = (call) => {
252
887
  var argIdentifiersForCall = (call) => {
253
888
  const identifiers = [];
254
889
  call.arguments?.forEach((argument, index) => {
255
- if (ts.isIdentifier(argument)) {
890
+ if (ts2.isIdentifier(argument)) {
256
891
  identifiers.push({ index, kind: "identifier", name: argument.text });
257
892
  return;
258
893
  }
259
- if (!ts.isObjectLiteralExpression(argument)) return;
894
+ if (!ts2.isObjectLiteralExpression(argument)) return;
260
895
  const properties = [];
261
896
  for (const property of argument.properties) {
262
- if (ts.isShorthandPropertyAssignment(property)) {
897
+ if (ts2.isShorthandPropertyAssignment(property)) {
263
898
  properties.push({ key: property.name.text, name: property.name.text });
264
899
  continue;
265
900
  }
266
- if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && ts.isIdentifier(property.initializer)) {
901
+ if (ts2.isPropertyAssignment(property) && ts2.isIdentifier(property.name) && ts2.isIdentifier(property.initializer)) {
267
902
  properties.push({ key: property.name.text, name: property.initializer.text });
268
903
  }
269
904
  }
@@ -303,8 +938,8 @@ var collectCallSite = (call, caller, context) => {
303
938
  };
304
939
  };
305
940
  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)) {
941
+ if (!declaration.initializer || !ts2.isIdentifier(declaration.initializer)) return null;
942
+ if (!ts2.isObjectBindingPattern(declaration.name) && !ts2.isArrayBindingPattern(declaration.name)) {
308
943
  return null;
309
944
  }
310
945
  const sourceParam = declaration.initializer.text;
@@ -328,33 +963,33 @@ var collectParamDestructure = (declaration, caller, context) => {
328
963
 
329
964
  // src/auto-instrumentation/transform-source.ts
330
965
  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;
966
+ if (/\.tsx$/i.test(filePath)) return ts3.ScriptKind.TSX;
967
+ if (/\.jsx$/i.test(filePath)) return ts3.ScriptKind.JSX;
968
+ if (/\.[cm]?ts$/i.test(filePath)) return ts3.ScriptKind.TS;
969
+ return ts3.ScriptKind.JS;
335
970
  };
336
971
  var functionName = (node, sourceFile) => {
337
972
  if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
338
973
  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);
974
+ if (ts3.isVariableDeclaration(parent) && ts3.isIdentifier(parent.name)) return parent.name.text;
975
+ if (ts3.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
976
+ if (ts3.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
342
977
  const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
343
978
  return `anonymous@${location.line + 1}:${location.character + 1}`;
344
979
  };
345
980
  var functionLocationPosition = (node, sourceFile) => {
346
981
  if (node.name) return node.name.getStart(sourceFile);
347
982
  const parent = node.parent;
348
- if ((ts2.isVariableDeclaration(parent) || ts2.isPropertyAssignment(parent)) && parent.name) {
983
+ if ((ts3.isVariableDeclaration(parent) || ts3.isPropertyAssignment(parent)) && parent.name) {
349
984
  return parent.name.getStart(sourceFile);
350
985
  }
351
986
  return node.getStart(sourceFile);
352
987
  };
353
988
  var bindingValue = (name, sourceFile) => {
354
- if (ts2.isIdentifier(name)) return name.text;
355
- if (ts2.isArrayBindingPattern(name)) {
989
+ if (ts3.isIdentifier(name)) return name.text;
990
+ if (ts3.isArrayBindingPattern(name)) {
356
991
  const values = name.elements.map((element) => {
357
- if (ts2.isOmittedExpression(element)) return "";
992
+ if (ts3.isOmittedExpression(element)) return "";
358
993
  const value = bindingValue(element.name, sourceFile);
359
994
  return element.dotDotDotToken ? `...${value}` : value;
360
995
  });
@@ -370,14 +1005,14 @@ var bindingValue = (name, sourceFile) => {
370
1005
  };
371
1006
  var arrowArguments = (node) => {
372
1007
  const values = node.parameters.flatMap((parameter) => {
373
- if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
1008
+ if (ts3.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
374
1009
  const value = bindingValue(parameter.name, node.getSourceFile());
375
1010
  return [parameter.dotDotDotToken ? `...${value}` : value];
376
1011
  });
377
1012
  return `[${values.join(",")}]`;
378
1013
  };
379
1014
  var isSupportedFunction = (node) => {
380
- if (!ts2.isFunctionDeclaration(node) && !ts2.isFunctionExpression(node) && !ts2.isArrowFunction(node) && !ts2.isMethodDeclaration(node) && !ts2.isGetAccessorDeclaration(node) && !ts2.isSetAccessorDeclaration(node)) {
1015
+ if (!ts3.isFunctionDeclaration(node) && !ts3.isFunctionExpression(node) && !ts3.isArrowFunction(node) && !ts3.isMethodDeclaration(node) && !ts3.isGetAccessorDeclaration(node) && !ts3.isSetAccessorDeclaration(node)) {
381
1016
  return false;
382
1017
  }
383
1018
  if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
@@ -388,7 +1023,7 @@ var uniqueName = (source, base) => {
388
1023
  while (source.includes(name)) name += "_";
389
1024
  return name;
390
1025
  };
391
- var isDirectivePrologue = (statement) => ts2.isExpressionStatement(statement) && ts2.isStringLiteral(statement.expression);
1026
+ var isDirectivePrologue = (statement) => ts3.isExpressionStatement(statement) && ts3.isStringLiteral(statement.expression);
392
1027
  var insertPositionAfterPrologue = (source, sourceFile) => {
393
1028
  const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
394
1029
  let end = shebangEnd;
@@ -403,7 +1038,7 @@ var preambleInsert = (source, position, preamble) => {
403
1038
  const previous = source[position - 1];
404
1039
  return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
405
1040
  };
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)}));`;
1041
+ 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
1042
  var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
408
1043
  const start = mappedLocation(
409
1044
  sourceFile,
@@ -434,80 +1069,127 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
434
1069
  paramNames
435
1070
  };
436
1071
  };
1072
+ var canOmitCallValues = (analysis, analyzed) => {
1073
+ if (!functionIsExact(analysis.plan) || analysis.hasThrow || analysis.hasCoercionGuard) {
1074
+ return false;
1075
+ }
1076
+ const callers = analyzed.filter(
1077
+ (caller) => caller.callEdges.some((edge) => edge.calleeFunctionId === analysis.functionId)
1078
+ );
1079
+ if (callers.length === 0) return false;
1080
+ return callers.every((caller) => caller.plan.fallbackReason === void 0 && !caller.hasThrow);
1081
+ };
437
1082
  var transformSource = (source, options) => {
438
1083
  if (source.includes("@rasputin-ignore-file")) return void 0;
439
- const sourceFile = ts2.createSourceFile(
1084
+ const sourceFile = ts3.createSourceFile(
440
1085
  options.filePath,
441
1086
  source,
442
- ts2.ScriptTarget.Latest,
1087
+ ts3.ScriptTarget.Latest,
443
1088
  true,
444
1089
  scriptKindFor(options.filePath)
445
1090
  );
446
1091
  const runName = uniqueName(source, "__rasputinRun__");
1092
+ const capName = uniqueName(source, "__rasputinCap__");
447
1093
  const plansName = uniqueName(source, "__rasputinPlans__");
448
1094
  const absolutePath = resolve4(options.filePath);
1095
+ const derivationMode = options.derivationMode ?? "analysis";
449
1096
  const edits = [];
450
1097
  let order = 0;
451
1098
  const delta = {
452
1099
  functions: [],
453
1100
  callSites: [],
454
- destructures: []
1101
+ destructures: [],
1102
+ derivationPlans: []
455
1103
  };
456
1104
  const collectContext = {
457
1105
  sourceFile,
458
1106
  mapLocation: options.mapLocation,
459
1107
  absolutePath
460
1108
  };
1109
+ const pending = [];
461
1110
  const visit = (node, caller) => {
462
1111
  if (isSupportedFunction(node)) {
463
1112
  const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
464
1113
  if (!collected) {
465
- ts2.forEachChild(node, (child) => visit(child, caller));
1114
+ ts3.forEachChild(node, (child) => visit(child, caller));
466
1115
  return;
467
1116
  }
468
- const planIndex = delta.functions.length;
1117
+ pending.push({ node, collected });
469
1118
  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));
1119
+ ts3.forEachChild(node, (child) => visit(child, collected));
490
1120
  return;
491
1121
  }
492
- if (caller && (ts2.isCallExpression(node) || ts2.isNewExpression(node))) {
1122
+ if (caller && (ts3.isCallExpression(node) || ts3.isNewExpression(node))) {
493
1123
  const site = collectCallSite(node, caller, collectContext);
494
1124
  if (site) delta.callSites.push(site);
495
1125
  }
496
- if (caller && ts2.isVariableDeclaration(node)) {
1126
+ if (caller && ts3.isVariableDeclaration(node)) {
497
1127
  const destructure = collectParamDestructure(node, caller, collectContext);
498
1128
  if (destructure) delta.destructures.push(destructure);
499
1129
  }
500
- ts2.forEachChild(node, (child) => visit(child, caller));
1130
+ ts3.forEachChild(node, (child) => visit(child, caller));
501
1131
  };
502
1132
  visit(sourceFile);
503
- if (edits.length === 0) return void 0;
1133
+ if (pending.length === 0) return void 0;
1134
+ const analyzed = analyzeModuleFunctions(
1135
+ sourceFile,
1136
+ pending.map((entry) => ({
1137
+ functionId: entry.collected.functionId,
1138
+ name: entry.collected.name,
1139
+ node: entry.node
1140
+ }))
1141
+ );
1142
+ delta.derivationPlans = analyzed.map((fn) => fn.plan);
1143
+ const analyzedById = new Map(analyzed.map((fn) => [fn.functionId, fn]));
1144
+ for (const [planIndex, entry] of pending.entries()) {
1145
+ const analysis = analyzedById.get(entry.collected.functionId);
1146
+ const omitValues = derivationMode === "selective" && analysis && canOmitCallValues(analysis, analyzed);
1147
+ const valueFlag = omitValues ? ",1" : "";
1148
+ const args = ts3.isArrowFunction(entry.node) ? arrowArguments(entry.node) : "arguments";
1149
+ const asyncCallback = entry.node.modifiers?.some(
1150
+ (modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword
1151
+ ) ? "async " : "";
1152
+ if (ts3.isBlock(entry.node.body)) {
1153
+ edits.push({
1154
+ position: entry.node.body.getStart(sourceFile) + 1,
1155
+ text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
1156
+ order: order++
1157
+ });
1158
+ edits.push({
1159
+ position: entry.node.body.end - 1,
1160
+ text: `}${valueFlag});`,
1161
+ order: order++
1162
+ });
1163
+ } else {
1164
+ edits.push({
1165
+ position: entry.node.body.getStart(sourceFile),
1166
+ text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
1167
+ order: order++
1168
+ });
1169
+ edits.push({
1170
+ position: entry.node.body.end,
1171
+ text: `)${valueFlag})`,
1172
+ order: order++
1173
+ });
1174
+ }
1175
+ if (analysis) {
1176
+ for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
1177
+ edits.push({ ...edit, order: order++ });
1178
+ }
1179
+ }
1180
+ }
504
1181
  const insertAt = insertPositionAfterPrologue(source, sourceFile);
505
1182
  edits.push({
506
1183
  position: insertAt,
507
1184
  text: preambleInsert(
508
1185
  source,
509
1186
  insertAt,
510
- modulePreamble(runName, plansName, JSON.stringify(delta.functions.map((fn) => fn.plan)))
1187
+ modulePreamble(
1188
+ runName,
1189
+ capName,
1190
+ plansName,
1191
+ JSON.stringify(delta.functions.map((fn) => fn.plan))
1192
+ )
511
1193
  ),
512
1194
  order: -1
513
1195
  });