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

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 (80) hide show
  1. package/README.md +219 -188
  2. package/dist/auto-instrumentation/bun-plugin.d.ts.map +1 -1
  3. package/dist/auto-instrumentation/format-configuration-report.d.ts +4 -0
  4. package/dist/auto-instrumentation/format-configuration-report.d.ts.map +1 -1
  5. package/dist/auto-instrumentation/instrument-compiled-esm.d.ts +1 -3
  6. package/dist/auto-instrumentation/instrument-compiled-esm.d.ts.map +1 -1
  7. package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts +10 -2
  8. package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts.map +1 -1
  9. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts +6 -0
  10. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts.map +1 -1
  11. package/dist/auto-instrumentation/node-hooks.d.ts.map +1 -1
  12. package/dist/auto-instrumentation/parameter-initializer-may-run-code.d.ts +3 -0
  13. package/dist/auto-instrumentation/parameter-initializer-may-run-code.d.ts.map +1 -0
  14. package/dist/auto-instrumentation/schedule-instrumentation-manifest-upload.d.ts +1 -1
  15. package/dist/auto-instrumentation/schedule-instrumentation-manifest-upload.d.ts.map +1 -1
  16. package/dist/auto-instrumentation/source-map-locations.d.ts +1 -0
  17. package/dist/auto-instrumentation/source-map-locations.d.ts.map +1 -1
  18. package/dist/auto-instrumentation/suspension-whitelist.d.ts +33 -0
  19. package/dist/auto-instrumentation/suspension-whitelist.d.ts.map +1 -0
  20. package/dist/auto-instrumentation/transform-disk-cache.d.ts +7 -0
  21. package/dist/auto-instrumentation/transform-disk-cache.d.ts.map +1 -0
  22. package/dist/auto-instrumentation/transform-source.d.ts +9 -6
  23. package/dist/auto-instrumentation/transform-source.d.ts.map +1 -1
  24. package/dist/auto-instrumentation/verify-instrumentation-isomorphism.d.ts +2 -0
  25. package/dist/auto-instrumentation/verify-instrumentation-isomorphism.d.ts.map +1 -0
  26. package/dist/auto-instrumentation/verify-transformed-source.d.ts +2 -0
  27. package/dist/auto-instrumentation/verify-transformed-source.d.ts.map +1 -0
  28. package/dist/execution-recorder/automatic-runtime.d.ts +17 -7
  29. package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
  30. package/dist/execution-recorder/encode-frozen.d.ts +16 -0
  31. package/dist/execution-recorder/encode-frozen.d.ts.map +1 -0
  32. package/dist/execution-recorder/execution-journal.d.ts.map +1 -1
  33. package/dist/execution-recorder/execution-recorder-types.d.ts +124 -36
  34. package/dist/execution-recorder/execution-recorder-types.d.ts.map +1 -1
  35. package/dist/execution-recorder/execution-recorder.d.ts +2 -2
  36. package/dist/execution-recorder/execution-recorder.d.ts.map +1 -1
  37. package/dist/execution-recorder/function-plan.d.ts +8 -1
  38. package/dist/execution-recorder/function-plan.d.ts.map +1 -1
  39. package/dist/execution-recorder/index.d.ts +1 -1
  40. package/dist/execution-recorder/index.d.ts.map +1 -1
  41. package/dist/execution-recorder/recorder-memory-budget.d.ts +1 -0
  42. package/dist/execution-recorder/recorder-memory-budget.d.ts.map +1 -1
  43. package/dist/execution-recorder/redact-keys.d.ts +4 -0
  44. package/dist/execution-recorder/redact-keys.d.ts.map +1 -0
  45. package/dist/execution-recorder/run-exactly-once.d.ts +7 -0
  46. package/dist/execution-recorder/run-exactly-once.d.ts.map +1 -0
  47. package/dist/execution-recorder/runtime-state-size-budget.d.ts +14 -0
  48. package/dist/execution-recorder/runtime-state-size-budget.d.ts.map +1 -0
  49. package/dist/execution-recorder/safe-serialize.d.ts +9 -3
  50. package/dist/execution-recorder/safe-serialize.d.ts.map +1 -1
  51. package/dist/execution-recorder/value-freeze.d.ts +43 -0
  52. package/dist/execution-recorder/value-freeze.d.ts.map +1 -0
  53. package/dist/execution-recorder/warn-missing-async-context-frame.d.ts +10 -0
  54. package/dist/execution-recorder/warn-missing-async-context-frame.d.ts.map +1 -0
  55. package/dist/index.d.ts +1 -6
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +2637 -894
  58. package/dist/install-global-handlers.d.ts +29 -8
  59. package/dist/install-global-handlers.d.ts.map +1 -1
  60. package/dist/instrument/build.js +947 -797
  61. package/dist/instrument/bun.js +1098 -770
  62. package/dist/instrument/node.js +1078 -772
  63. package/dist/internal.d.ts +7 -0
  64. package/dist/internal.d.ts.map +1 -0
  65. package/dist/internal.js +4072 -0
  66. package/dist/rasputin-init.d.ts +25 -5
  67. package/dist/rasputin-init.d.ts.map +1 -1
  68. package/dist/sdk-meta.d.ts +1 -1
  69. package/dist/sdk-meta.d.ts.map +1 -1
  70. package/package.json +8 -3
  71. package/dist/derivation/analyze-function.d.ts +0 -42
  72. package/dist/derivation/analyze-function.d.ts.map +0 -1
  73. package/dist/derivation/build-dependency-graph.d.ts +0 -21
  74. package/dist/derivation/build-dependency-graph.d.ts.map +0 -1
  75. package/dist/derivation/derivation-ir.d.ts +0 -33
  76. package/dist/derivation/derivation-ir.d.ts.map +0 -1
  77. package/dist/derivation/derivation-proof.d.ts +0 -14
  78. package/dist/derivation/derivation-proof.d.ts.map +0 -1
  79. package/dist/derivation/emit-capture-plan.d.ts +0 -8
  80. package/dist/derivation/emit-capture-plan.d.ts.map +0 -1
@@ -1,6 +1,9 @@
1
1
  // src/auto-instrumentation/instrument-compiled-esm.ts
2
2
  import { mkdirSync, readdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
3
3
  import { dirname as dirname2, join as join2, relative as relative2, resolve as resolve5 } from "node:path";
4
+ import {
5
+ hashInstrumentationManifest
6
+ } from "@rasputin-ai/core";
4
7
 
5
8
  // src/auto-instrumentation/instrumentation-manifest-registry.ts
6
9
  import { createHash } from "node:crypto";
@@ -15,6 +18,17 @@ import {
15
18
  runtimeFunctionId,
16
19
  sourceLocatorFromFile
17
20
  } from "@rasputin-ai/core";
21
+ var encodedPlanHasPreBodyParameterEvaluation = (plan) => plan[7] === 1;
22
+ var withPreBodyParameterEvaluation = (plan) => [
23
+ plan[0],
24
+ plan[1],
25
+ plan[2],
26
+ plan[3],
27
+ plan[4],
28
+ plan[5],
29
+ plan[6],
30
+ 1
31
+ ];
18
32
  var encodeFunctionPlan = (source) => {
19
33
  const locator = sourceLocatorFromFile(source.filePath);
20
34
  const definition = {
@@ -41,7 +55,8 @@ var definitionFromEncodedPlan = (plan) => {
41
55
  return {
42
56
  functionId,
43
57
  name,
44
- ...source ? { source } : {}
58
+ ...source ? { source } : {},
59
+ ...encodedPlanHasPreBodyParameterEvaluation(plan) ? { parentAttribution: "pre_body_parameter_evaluation" } : {}
45
60
  };
46
61
  };
47
62
 
@@ -51,13 +66,16 @@ var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
51
66
  var getRegistry = () => {
52
67
  const existing = Reflect.get(globalThis, registrySymbol);
53
68
  if (existing?.__rasputinManifestRegistry && existing.files instanceof Map && typeof existing.generation === "number" && typeof existing.uploadedGeneration === "number") {
54
- return existing;
69
+ const registry2 = existing;
70
+ if (typeof registry2.alreadyInstrumented !== "number") registry2.alreadyInstrumented = 0;
71
+ return registry2;
55
72
  }
56
73
  const registry = {
57
74
  __rasputinManifestRegistry: true,
58
75
  files: /* @__PURE__ */ new Map(),
59
76
  generation: 0,
60
- uploadedGeneration: 0
77
+ uploadedGeneration: 0,
78
+ alreadyInstrumented: 0
61
79
  };
62
80
  Reflect.set(globalThis, registrySymbol, registry);
63
81
  return registry;
@@ -69,16 +87,36 @@ var sourceAt = (filePath, line, column) => {
69
87
  var stableId = (kind, ...parts) => `${kind}_${createHash("sha256").update(parts.join("\0")).digest("base64url").slice(0, 24)}`;
70
88
  var recordInstrumentationManifestDelta = (filePath, delta) => {
71
89
  const registry = getRegistry();
90
+ if (delta.alreadyInstrumented) {
91
+ registry.alreadyInstrumented += 1;
92
+ return;
93
+ }
72
94
  registry.files.set(resolve(filePath), delta);
73
95
  registry.generation += 1;
74
96
  registry.onDirty?.();
75
97
  };
98
+ var snapshotOptionsKey = (options) => JSON.stringify({
99
+ sourceRoot: options.sourceRoot ?? "",
100
+ sourceRoots: options.sourceRoots ?? {}
101
+ });
76
102
  var snapshotInstrumentationManifest = (options = {}) => {
103
+ const registry = getRegistry();
104
+ const optionsKey = snapshotOptionsKey(options);
105
+ if (registry.snapshotCache && registry.snapshotCache.generation === registry.generation && registry.snapshotCache.optionsKey === optionsKey) {
106
+ return registry.snapshotCache.manifest;
107
+ }
77
108
  const functions = [];
78
109
  const callSites = [];
79
110
  const destructures = [];
80
- const derivationPlans = [];
81
- for (const delta of getRegistry().files.values()) {
111
+ const remember = (manifest) => {
112
+ registry.snapshotCache = {
113
+ generation: registry.generation,
114
+ optionsKey,
115
+ manifest
116
+ };
117
+ return manifest;
118
+ };
119
+ for (const delta of registry.files.values()) {
82
120
  for (const fn of delta.functions) {
83
121
  const definition = definitionFromEncodedPlan(fn.plan);
84
122
  if (!definition.source) continue;
@@ -90,7 +128,8 @@ var snapshotInstrumentationManifest = (options = {}) => {
90
128
  startColumn: fn.startColumn,
91
129
  endLine: fn.endLine,
92
130
  endColumn: fn.endColumn,
93
- params: fn.params
131
+ params: fn.params,
132
+ ...fn.instrumented === false ? { instrumented: false, skipReason: fn.skipReason } : {}
94
133
  });
95
134
  }
96
135
  for (const site of delta.callSites) {
@@ -129,25 +168,23 @@ var snapshotInstrumentationManifest = (options = {}) => {
129
168
  binding: destructure.binding
130
169
  });
131
170
  }
132
- derivationPlans.push(...delta.derivationPlans ?? []);
133
171
  }
134
- const registry = getRegistry();
135
172
  if (registry.prebuilt && registry.files.size === 0) {
136
173
  const prebuilt = registry.prebuilt;
137
- if (!options.sourceRoot && !options.sourceRoots) return prebuilt;
138
- return {
174
+ if (!options.sourceRoot && !options.sourceRoots) return remember(prebuilt);
175
+ return remember({
139
176
  ...prebuilt,
140
177
  sourceRoots: {
141
178
  ...options.sourceRoot ? { default: options.sourceRoot } : {},
142
179
  packages: { ...options.sourceRoots ?? {} }
143
180
  }
144
- };
181
+ });
145
182
  }
146
183
  if (functions.length === 0 && callSites.length === 0 && destructures.length === 0) {
147
- return void 0;
184
+ return remember(void 0);
148
185
  }
149
186
  const packages = { ...options.sourceRoots ?? {} };
150
- return {
187
+ return remember({
151
188
  schema_version: INSTRUMENTATION_MANIFEST_SCHEMA_VERSION,
152
189
  ...options.sourceRoot || Object.keys(packages).length > 0 ? {
153
190
  sourceRoots: {
@@ -157,9 +194,8 @@ var snapshotInstrumentationManifest = (options = {}) => {
157
194
  } : {},
158
195
  functions,
159
196
  callSites,
160
- destructures,
161
- derivationPlans
162
- };
197
+ destructures
198
+ });
163
199
  };
164
200
  var resetInstrumentationManifestRegistry = () => {
165
201
  Reflect.deleteProperty(globalThis, registrySymbol);
@@ -253,656 +289,22 @@ var createSourceMapLocationMapper = (generatedPath, source) => {
253
289
  };
254
290
 
255
291
  // src/auto-instrumentation/transform-source.ts
256
- import { resolve as resolve4 } from "node:path";
257
- import ts3 from "typescript";
258
-
259
- // src/derivation/analyze-function.ts
260
- import ts from "typescript";
261
-
262
- // src/derivation/build-dependency-graph.ts
263
- var buildDependencyGraph = (functions, edges) => {
264
- const names = new Set(functions.map((fn) => fn.name));
265
- const localEdges = edges.filter((edge) => names.has(edge.calleeName));
266
- const calleesByCaller = /* @__PURE__ */ new Map();
267
- for (const edge of localEdges) {
268
- const existing = calleesByCaller.get(edge.callerFunctionId) ?? [];
269
- existing.push(edge.calleeName);
270
- calleesByCaller.set(edge.callerFunctionId, existing);
271
- }
272
- return {
273
- functions: functions.map((fn) => fn.functionId),
274
- edges: localEdges,
275
- calleesByCaller
276
- };
277
- };
278
- var topologicalFunctionOrder = (functions, graph) => {
279
- const idByName = new Map(functions.map((fn) => [fn.name, fn.functionId]));
280
- const remainingCallees = new Map(
281
- functions.map((fn) => {
282
- const unique = new Set(
283
- (graph.calleesByCaller.get(fn.functionId) ?? []).map((name) => idByName.get(name)).filter((id) => Boolean(id) && id !== fn.functionId)
284
- );
285
- return [fn.functionId, unique.size];
286
- })
287
- );
288
- const callersOf = /* @__PURE__ */ new Map();
289
- for (const fn of functions) {
290
- for (const calleeName of graph.calleesByCaller.get(fn.functionId) ?? []) {
291
- const calleeId = idByName.get(calleeName);
292
- if (!calleeId || calleeId === fn.functionId) continue;
293
- const callers = callersOf.get(calleeId) ?? [];
294
- callers.push(fn.functionId);
295
- callersOf.set(calleeId, callers);
296
- }
297
- }
298
- const queue = functions.filter((fn) => (remainingCallees.get(fn.functionId) ?? 0) === 0).map((fn) => fn.functionId);
299
- const ordered = [];
300
- const queued = new Set(queue);
301
- while (queue.length > 0) {
302
- const current = queue.shift();
303
- if (current === void 0) break;
304
- ordered.push(current);
305
- for (const caller of callersOf.get(current) ?? []) {
306
- const next = (remainingCallees.get(caller) ?? 0) - 1;
307
- remainingCallees.set(caller, next);
308
- if (next === 0 && !queued.has(caller)) {
309
- queued.add(caller);
310
- queue.push(caller);
311
- }
312
- }
313
- }
314
- for (const fn of functions) {
315
- if (!queued.has(fn.functionId)) ordered.push(fn.functionId);
316
- }
317
- return ordered;
318
- };
319
-
320
- // src/derivation/derivation-ir.ts
321
- var DerivationIrBuilder = class {
322
- expressions = [];
323
- nextId = 0;
324
- constValue(value) {
325
- return this.add({ id: this.nextId++, kind: "const", value });
326
- }
327
- param(index) {
328
- return this.add({ id: this.nextId++, kind: "param", index });
329
- }
330
- root(rootId) {
331
- return this.add({ id: this.nextId++, kind: "root", rootId });
332
- }
333
- alias(from) {
334
- return this.add({ id: this.nextId++, kind: "alias", from });
335
- }
336
- binary(op, left, right, guard) {
337
- return this.add({ id: this.nextId++, kind: "binary", op, left, right, guard });
338
- }
339
- unary(op, operand) {
340
- return this.add({ id: this.nextId++, kind: "unary", op, operand });
341
- }
342
- logical(op, left, right) {
343
- return this.add({ id: this.nextId++, kind: "logical", op, left, right });
344
- }
345
- cond(test, consequent, alternate) {
346
- return this.add({ id: this.nextId++, kind: "cond", test, consequent, alternate });
347
- }
348
- object(properties) {
349
- return this.add({ id: this.nextId++, kind: "object", properties });
350
- }
351
- array(elements) {
352
- return this.add({ id: this.nextId++, kind: "array", elements });
353
- }
354
- prop(object, key) {
355
- return this.add({ id: this.nextId++, kind: "prop", object, key });
356
- }
357
- index(array, index) {
358
- return this.add({ id: this.nextId++, kind: "index", array, index });
359
- }
360
- pureCall(calleeFunctionId, args2) {
361
- return this.add({ id: this.nextId++, kind: "pure_call", calleeFunctionId, args: args2 });
362
- }
363
- template(parts) {
364
- return this.add({ id: this.nextId++, kind: "template", parts });
365
- }
366
- add(expression) {
367
- this.expressions.push(expression);
368
- return expression.id;
369
- }
370
- };
371
- var emptyPlan = (functionId) => ({
372
- planId: `dp_${functionId}`,
373
- functionId,
374
- expressions: [],
375
- variables: [],
376
- captureBoundaries: [],
377
- controlFacts: []
378
- });
379
-
380
- // src/derivation/derivation-proof.ts
381
- var functionIsExact = (plan) => plan.fallbackReason === void 0 && plan.captureBoundaries.length === 0 && plan.controlFacts.length === 0;
382
-
383
- // src/derivation/analyze-function.ts
384
- var loc = (node, sourceFile) => {
385
- const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
386
- return { line: position.line + 1, column: position.character + 1 };
387
- };
388
- var lookup = (ctx, name) => {
389
- for (let index = ctx.scopes.length - 1; index >= 0; index--) {
390
- const binding = ctx.scopes[index]?.get(name);
391
- if (binding) return binding;
392
- }
393
- return void 0;
394
- };
395
- var fallback = (ctx, reason) => {
396
- ctx.fallback ??= reason;
397
- return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "unknown" };
398
- };
399
- var capture = (ctx, node, reason) => {
400
- const rootId = ctx.nextRootId.value++;
401
- const position = loc(node, ctx.sourceFile);
402
- const expressionId = ctx.ir.root(rootId);
403
- ctx.captureWraps.push({
404
- rootId,
405
- reason,
406
- start: node.getStart(ctx.sourceFile),
407
- end: node.end,
408
- line: position.line,
409
- column: position.column,
410
- expressionId
411
- });
412
- return { expressionId, shape: "unknown", captured: true };
413
- };
414
- var binaryOp = (kind) => {
415
- switch (kind) {
416
- case ts.SyntaxKind.PlusToken:
417
- return "add";
418
- case ts.SyntaxKind.MinusToken:
419
- return "sub";
420
- case ts.SyntaxKind.AsteriskToken:
421
- return "mul";
422
- case ts.SyntaxKind.SlashToken:
423
- return "div";
424
- case ts.SyntaxKind.PercentToken:
425
- return "mod";
426
- case ts.SyntaxKind.EqualsEqualsToken:
427
- return "eq";
428
- case ts.SyntaxKind.ExclamationEqualsToken:
429
- return "neq";
430
- case ts.SyntaxKind.EqualsEqualsEqualsToken:
431
- return "seq";
432
- case ts.SyntaxKind.ExclamationEqualsEqualsToken:
433
- return "sneq";
434
- case ts.SyntaxKind.LessThanToken:
435
- return "lt";
436
- case ts.SyntaxKind.LessThanEqualsToken:
437
- return "lte";
438
- case ts.SyntaxKind.GreaterThanToken:
439
- return "gt";
440
- case ts.SyntaxKind.GreaterThanEqualsToken:
441
- return "gte";
442
- default:
443
- return void 0;
444
- }
445
- };
446
- var isNumericBinaryOp = (op) => op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod";
447
- var unwrapParens = (node) => {
448
- while (ts.isParenthesizedExpression(node)) node = node.expression;
449
- return node;
450
- };
451
- var numericLiteralValue = (node) => {
452
- const value = Number(node.text);
453
- return { type: "number", value };
454
- };
455
- var analyzeExpression = (ctx, node) => {
456
- if (ctx.fallback) return fallback(ctx, ctx.fallback);
457
- if (ts.isParenthesizedExpression(node)) return analyzeExpression(ctx, node.expression);
458
- if (ts.isAsExpression(node) || ts.isNonNullExpression(node)) {
459
- return analyzeExpression(ctx, node.expression);
460
- }
461
- if (node.kind === ts.SyntaxKind.TypeAssertionExpression) {
462
- return analyzeExpression(ctx, node.expression);
463
- }
464
- if (ts.isSatisfiesExpression(node)) return analyzeExpression(ctx, node.expression);
465
- if (node.kind === ts.SyntaxKind.NullKeyword) {
466
- return { expressionId: ctx.ir.constValue({ type: "null" }), shape: "primitive" };
467
- }
468
- if (node.kind === ts.SyntaxKind.TrueKeyword) {
469
- return {
470
- expressionId: ctx.ir.constValue({ type: "boolean", value: true }),
471
- shape: "primitive"
472
- };
473
- }
474
- if (node.kind === ts.SyntaxKind.FalseKeyword) {
475
- return {
476
- expressionId: ctx.ir.constValue({ type: "boolean", value: false }),
477
- shape: "primitive"
478
- };
479
- }
480
- if (ts.isNumericLiteral(node)) {
481
- return { expressionId: ctx.ir.constValue(numericLiteralValue(node)), shape: "primitive" };
482
- }
483
- if (ts.isPrefixUnaryExpression(node) && ts.isNumericLiteral(node.operand) && node.operator === ts.SyntaxKind.MinusToken) {
484
- const value = -Number(node.operand.text);
485
- return {
486
- expressionId: ctx.ir.constValue(
487
- Object.is(value, -0) ? { type: "number", value: 0, negativeZero: true } : { type: "number", value }
488
- ),
489
- shape: "primitive"
490
- };
491
- }
492
- if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
493
- return {
494
- expressionId: ctx.ir.constValue({ type: "string", value: node.text }),
495
- shape: "primitive"
496
- };
497
- }
498
- if (ts.isBigIntLiteral(node)) {
499
- return {
500
- expressionId: ctx.ir.constValue({ type: "bigint", value: node.text.replace(/n$/, "") }),
501
- shape: "primitive"
502
- };
503
- }
504
- if (ts.isIdentifier(node)) {
505
- if (node.text === "undefined") {
506
- return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "primitive" };
507
- }
508
- const binding = lookup(ctx, node.text);
509
- if (!binding) return capture(ctx, node, "external_state");
510
- if (binding.mutable) return capture(ctx, node, "mutable_object");
511
- return {
512
- expressionId: ctx.ir.alias(binding.expressionId),
513
- shape: binding.shape
514
- };
515
- }
516
- if (ts.isTemplateExpression(node)) {
517
- const parts = [
518
- { kind: "lit", value: node.head.text }
519
- ];
520
- for (const span of node.templateSpans) {
521
- const nested = analyzeExpression(ctx, span.expression);
522
- if (nested.shape !== "primitive" && !nested.captured) {
523
- return capture(ctx, node, "unsupported_expression");
524
- }
525
- parts.push({ kind: "expr", expressionId: nested.expressionId });
526
- parts.push({ kind: "lit", value: span.literal.text });
527
- }
528
- return { expressionId: ctx.ir.template(parts), shape: "primitive" };
529
- }
530
- if (ts.isArrayLiteralExpression(node)) {
531
- if (node.elements.some((element) => ts.isSpreadElement(element))) {
532
- return capture(ctx, node, "unsupported_expression");
533
- }
534
- const elements = node.elements.map((element) => {
535
- if (ts.isOmittedExpression(element)) return { kind: "hole" };
536
- return analyzeExpression(ctx, element).expressionId;
537
- });
538
- return { expressionId: ctx.ir.array(elements), shape: "array" };
539
- }
540
- if (ts.isObjectLiteralExpression(node)) {
541
- const properties = [];
542
- for (const property of node.properties) {
543
- if (ts.isShorthandPropertyAssignment(property)) {
544
- const nested = analyzeExpression(ctx, property.name);
545
- if (nested.captured) {
546
- const wrap = ctx.captureWraps.find(
547
- (candidate) => candidate.expressionId === nested.expressionId
548
- );
549
- if (wrap) wrap.shorthandPropertyName = property.name.text;
550
- }
551
- properties.push({ key: property.name.text, value: nested.expressionId });
552
- continue;
553
- }
554
- if (!ts.isPropertyAssignment(property)) {
555
- return capture(ctx, node, "unsupported_expression");
556
- }
557
- if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
558
- return capture(ctx, node, "unsupported_expression");
559
- }
560
- const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
561
- if (key === void 0) return capture(ctx, node, "unsupported_expression");
562
- properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
563
- }
564
- return { expressionId: ctx.ir.object(properties), shape: "object" };
565
- }
566
- if (ts.isPropertyAccessExpression(node)) {
567
- if (node.questionDotToken) return capture(ctx, node, "unknown_property");
568
- const object = analyzeExpression(ctx, node.expression);
569
- if (object.shape === "object" && ts.isIdentifier(node.name)) {
570
- return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
571
- }
572
- return capture(ctx, node, "unknown_property");
573
- }
574
- if (ts.isElementAccessExpression(node)) {
575
- if (node.questionDotToken) return capture(ctx, node, "unknown_property");
576
- const array = analyzeExpression(ctx, node.expression);
577
- if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
578
- return {
579
- expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
580
- shape: "unknown"
581
- };
582
- }
583
- return capture(ctx, node, "unknown_property");
584
- }
585
- if (ts.isCallExpression(node)) {
586
- if (node.questionDotToken || node.typeArguments?.length) {
587
- return capture(ctx, node, "unsupported_expression");
588
- }
589
- if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
590
- const calleeName = node.expression.text;
591
- if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
592
- const provenId = ctx.provenExact.get(calleeName);
593
- ctx.callEdges.push({
594
- callerFunctionId: ctx.functionId,
595
- calleeName,
596
- ...provenId ? { calleeFunctionId: provenId } : {}
597
- });
598
- if (!provenId) return capture(ctx, node, "opaque_call");
599
- const args2 = node.arguments.map((argument) => {
600
- if (ts.isSpreadElement(argument)) return void 0;
601
- return analyzeExpression(ctx, argument).expressionId;
602
- });
603
- if (args2.some((argument) => argument === void 0))
604
- return capture(ctx, node, "unsupported_expression");
605
- return {
606
- expressionId: ctx.ir.pureCall(
607
- provenId,
608
- args2.filter((id) => id !== void 0)
609
- ),
610
- shape: "unknown"
611
- };
612
- }
613
- if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
614
- return capture(
615
- ctx,
616
- node,
617
- node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
618
- );
619
- }
620
- if (ts.isConditionalExpression(node)) {
621
- const test = analyzeExpression(ctx, node.condition);
622
- const consequent = analyzeExpression(ctx, node.whenTrue);
623
- const alternate = analyzeExpression(ctx, node.whenFalse);
624
- return {
625
- expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
626
- shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
627
- };
628
- }
629
- if (ts.isBinaryExpression(node)) {
630
- if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
631
- ctx.fallback ??= "mutable_state";
632
- return capture(ctx, node, "mutable_object");
633
- }
634
- if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
635
- const left2 = analyzeExpression(ctx, node.left);
636
- const right2 = analyzeExpression(ctx, node.right);
637
- const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
638
- return {
639
- expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
640
- shape: "unknown"
641
- };
642
- }
643
- const op = binaryOp(node.operatorToken.kind);
644
- if (!op) return capture(ctx, node, "unsupported_expression");
645
- if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
646
- const left = analyzeExpression(ctx, node.left);
647
- const right = analyzeExpression(ctx, node.right);
648
- const numeric = isNumericBinaryOp(op);
649
- const expressionId = ctx.ir.binary(
650
- op,
651
- left.expressionId,
652
- right.expressionId,
653
- numeric ? "numeric" : "primitive"
654
- );
655
- if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
656
- ctx.hasCoercionGuard = true;
657
- const fallbackRootId = ctx.nextRootId.value++;
658
- ctx.guardWraps.push({
659
- fallbackRootId,
660
- start: node.getStart(ctx.sourceFile),
661
- end: node.end,
662
- op
663
- });
664
- }
665
- return { expressionId, shape: "primitive" };
666
- }
667
- if (ts.isPrefixUnaryExpression(node)) {
668
- const operand = analyzeExpression(ctx, node.operand);
669
- if (node.operator === ts.SyntaxKind.ExclamationToken) {
670
- return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
671
- }
672
- if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
673
- if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
674
- return {
675
- expressionId: ctx.ir.unary(
676
- node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
677
- operand.expressionId
678
- ),
679
- shape: "primitive"
680
- };
681
- }
682
- return capture(ctx, node, "unsupported_expression");
683
- }
684
- if (ts.isTypeOfExpression(node)) {
685
- const operand = unwrapParens(node.expression);
686
- if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
687
- return capture(ctx, node, "external_state");
688
- }
689
- const inner = analyzeExpression(ctx, node.expression);
690
- return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
691
- }
692
- return capture(ctx, node, "unsupported_expression");
693
- };
694
- var bindIdentifier = (ctx, name, result2, declaration) => {
695
- const current = ctx.scopes[ctx.scopes.length - 1];
696
- current?.set(name.text, {
697
- expressionId: result2.expressionId,
698
- shape: result2.shape,
699
- mutable: false
700
- });
701
- const position = loc(name, ctx.sourceFile);
702
- const available = loc(declaration, ctx.sourceFile);
703
- ctx.variables.push({
704
- id: ctx.nextVariableId.value++,
705
- name: name.text,
706
- functionId: ctx.functionId,
707
- scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
708
- line: position.line,
709
- column: position.column,
710
- assignmentId: ctx.nextAssignmentId.value++,
711
- expressionId: result2.expressionId,
712
- availableAtLine: available.line,
713
- availableAtColumn: available.column
714
- });
715
- };
716
- var analyzeStatement = (ctx, node) => {
717
- if (ctx.fallback) return;
718
- if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
719
- if (ts.isBlock(node)) {
720
- ctx.scopes.push(/* @__PURE__ */ new Map());
721
- for (const statement of node.statements) analyzeStatement(ctx, statement);
722
- ctx.scopes.pop();
723
- return;
724
- }
725
- if (ts.isReturnStatement(node)) {
726
- if (!node.expression) {
727
- ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
728
- return;
729
- }
730
- ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
731
- return;
732
- }
733
- if (ts.isVariableStatement(node)) {
734
- if (node.declarationList.flags & ts.NodeFlags.Using) {
735
- fallback(ctx, "side_effect");
736
- return;
737
- }
738
- for (const declaration of node.declarationList.declarations) {
739
- if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
740
- fallback(ctx, "unsupported_expression");
741
- return;
742
- }
743
- const result2 = analyzeExpression(ctx, declaration.initializer);
744
- bindIdentifier(ctx, declaration.name, result2, declaration);
745
- }
746
- return;
747
- }
748
- if (ts.isExpressionStatement(node)) {
749
- analyzeExpression(ctx, node.expression);
750
- return;
751
- }
752
- if (ts.isThrowStatement(node)) {
753
- ctx.hasThrow = true;
754
- analyzeExpression(ctx, node.expression);
755
- return;
756
- }
757
- if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
758
- fallback(ctx, "unsupported_expression");
759
- return;
760
- }
761
- if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
762
- fallback(ctx, "has_loop");
763
- return;
764
- }
765
- fallback(ctx, "unsupported_expression");
766
- };
767
- var bindParams = (ctx, node) => {
768
- let index = 0;
769
- for (const parameter of node.parameters) {
770
- if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
771
- if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
772
- fallback(ctx, "unsupported_expression");
773
- index++;
774
- continue;
775
- }
776
- const expressionId = ctx.ir.param(index);
777
- ctx.identifierParams.push(parameter.name.text);
778
- bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
779
- index++;
780
- }
781
- };
782
- var analyzeOne = (node, functionId, name, sourceFile, provenExact) => {
783
- const ctx = {
784
- sourceFile,
785
- functionId,
786
- name,
787
- ir: new DerivationIrBuilder(),
788
- variables: [],
789
- captureWraps: [],
790
- guardWraps: [],
791
- callEdges: [],
792
- scopes: [/* @__PURE__ */ new Map()],
793
- nextRootId: { value: 0 },
794
- nextVariableId: { value: 0 },
795
- nextAssignmentId: { value: 0 },
796
- hasThrow: false,
797
- hasCoercionGuard: false,
798
- provenExact,
799
- identifierParams: []
800
- };
801
- if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
802
- ctx.fallback = "async";
803
- } else {
804
- bindParams(ctx, node);
805
- if (ts.isBlock(node.body)) {
806
- for (const statement of node.body.statements) analyzeStatement(ctx, statement);
807
- } else {
808
- ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
809
- }
810
- }
811
- const plan = {
812
- ...emptyPlan(functionId),
813
- expressions: ctx.ir.expressions,
814
- variables: ctx.variables,
815
- captureBoundaries: ctx.captureWraps.map((wrap) => ({
816
- id: wrap.rootId,
817
- reason: wrap.reason,
818
- expressionId: wrap.expressionId,
819
- line: wrap.line,
820
- column: wrap.column
821
- })),
822
- controlFacts: [],
823
- ...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
824
- ...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
825
- };
826
- return {
827
- functionId,
828
- name,
829
- node,
830
- plan,
831
- captureWraps: ctx.fallback ? [] : ctx.captureWraps,
832
- guardWraps: ctx.fallback ? [] : ctx.guardWraps,
833
- callEdges: ctx.callEdges,
834
- hasThrow: ctx.hasThrow,
835
- hasCoercionGuard: ctx.hasCoercionGuard
836
- };
837
- };
838
- var analyzeModuleFunctions = (sourceFile, targets) => {
839
- const nameCounts = /* @__PURE__ */ new Map();
840
- for (const target of targets) {
841
- nameCounts.set(target.name, (nameCounts.get(target.name) ?? 0) + 1);
842
- }
843
- const firstPass = targets.map(
844
- (target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
845
- );
846
- const graph = buildDependencyGraph(
847
- firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
848
- firstPass.flatMap((fn) => fn.callEdges)
849
- );
850
- const order = topologicalFunctionOrder(
851
- firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
852
- graph
853
- );
854
- const byId = new Map(targets.map((target) => [target.functionId, target]));
855
- const provenExact = /* @__PURE__ */ new Map();
856
- const analyzed = [];
857
- for (const functionId of order) {
858
- const target = byId.get(functionId);
859
- if (!target) continue;
860
- const result2 = analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact);
861
- if (functionIsExact(result2.plan) && nameCounts.get(result2.name) === 1) {
862
- provenExact.set(result2.name, result2.functionId);
863
- }
864
- analyzed.push(result2);
865
- }
866
- const remaining = targets.filter(
867
- (target) => !analyzed.some((fn) => fn.functionId === target.functionId)
868
- );
869
- for (const target of remaining) {
870
- analyzed.push(analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact));
871
- }
872
- return analyzed;
873
- };
874
-
875
- // src/derivation/emit-capture-plan.ts
876
- var captureWrapEdits = (wraps, capName) => {
877
- const edits = [];
878
- let order = 0;
879
- for (const wrap of wraps) {
880
- edits.push({
881
- position: wrap.start,
882
- text: `${wrap.shorthandPropertyName ? `${wrap.shorthandPropertyName}:` : ""}${capName}(${wrap.rootId},`,
883
- order: order++
884
- });
885
- edits.push({
886
- position: wrap.end,
887
- text: ")",
888
- order: order++
889
- });
890
- }
891
- return edits;
892
- };
292
+ import { basename, resolve as resolve4 } from "node:path";
293
+ import { originalPositionFor as originalPositionFor2, TraceMap as TraceMap2 } from "@jridgewell/trace-mapping";
294
+ import ts6 from "typescript";
893
295
 
894
296
  // src/execution-recorder/automatic-runtime.ts
895
- var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
297
+ var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v3";
896
298
  var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
897
299
 
898
300
  // src/auto-instrumentation/collect-instrumentation-delta.ts
899
- import ts2 from "typescript";
301
+ import ts from "typescript";
900
302
  var paramBindingFromName = (name, sourceFile) => {
901
- if (ts2.isIdentifier(name)) return { kind: "identifier", name: name.text };
902
- if (ts2.isArrayBindingPattern(name)) {
303
+ if (ts.isIdentifier(name)) return { kind: "identifier", name: name.text };
304
+ if (ts.isArrayBindingPattern(name)) {
903
305
  const elements = name.elements.map(
904
306
  (element) => {
905
- if (ts2.isOmittedExpression(element)) return { kind: "omitted" };
307
+ if (ts.isOmittedExpression(element)) return { kind: "omitted" };
906
308
  const binding = paramBindingFromName(element.name, sourceFile);
907
309
  if (element.dotDotDotToken) return { kind: "rest", binding };
908
310
  return binding;
@@ -912,7 +314,7 @@ var paramBindingFromName = (name, sourceFile) => {
912
314
  }
913
315
  const properties = name.elements.map((element) => {
914
316
  const binding = paramBindingFromName(element.name, sourceFile);
915
- const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts2.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
317
+ const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
916
318
  if (element.dotDotDotToken) {
917
319
  const rest = { kind: "rest", binding };
918
320
  return { key, binding: rest };
@@ -924,7 +326,7 @@ var paramBindingFromName = (name, sourceFile) => {
924
326
  var collectFunctionParams = (node, sourceFile) => {
925
327
  const params = [];
926
328
  for (const parameter of node.parameters) {
927
- if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
329
+ if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
928
330
  const binding = paramBindingFromName(parameter.name, sourceFile);
929
331
  params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
930
332
  }
@@ -965,18 +367,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
965
367
  };
966
368
  var unwrapExpression = (expression) => {
967
369
  let current = expression;
968
- while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
370
+ while (ts.isParenthesizedExpression(current) || ts.isNonNullExpression(current)) {
969
371
  current = current.expression;
970
372
  }
971
- if (ts2.isAwaitExpression(current)) return unwrapExpression(current.expression);
373
+ if (ts.isAwaitExpression(current)) return unwrapExpression(current.expression);
972
374
  return current;
973
375
  };
974
376
  var calleeFromExpression = (expression, sourceFile) => {
975
377
  const current = unwrapExpression(expression);
976
- if (ts2.isIdentifier(current)) {
378
+ if (ts.isIdentifier(current)) {
977
379
  return { calleeName: current.text, calleeText: current.text };
978
380
  }
979
- if (ts2.isPropertyAccessExpression(current) && ts2.isIdentifier(current.name)) {
381
+ if (ts.isPropertyAccessExpression(current) && ts.isIdentifier(current.name)) {
980
382
  return {
981
383
  calleeName: current.name.text,
982
384
  calleeText: current.getText(sourceFile).replace(/\s+/g, "")
@@ -986,9 +388,9 @@ var calleeFromExpression = (expression, sourceFile) => {
986
388
  };
987
389
  var resultBindingForCall = (call) => {
988
390
  let node = call.parent;
989
- if (ts2.isAwaitExpression(node)) node = node.parent;
990
- if (!ts2.isVariableDeclaration(node)) return null;
991
- if (!ts2.isIdentifier(node.name)) return null;
391
+ if (ts.isAwaitExpression(node)) node = node.parent;
392
+ if (!ts.isVariableDeclaration(node)) return null;
393
+ if (!ts.isIdentifier(node.name)) return null;
992
394
  const initializer = node.initializer;
993
395
  if (!initializer) return null;
994
396
  const inner = unwrapExpression(initializer);
@@ -998,18 +400,18 @@ var resultBindingForCall = (call) => {
998
400
  var argIdentifiersForCall = (call) => {
999
401
  const identifiers = [];
1000
402
  call.arguments?.forEach((argument, index) => {
1001
- if (ts2.isIdentifier(argument)) {
403
+ if (ts.isIdentifier(argument)) {
1002
404
  identifiers.push({ index, kind: "identifier", name: argument.text });
1003
405
  return;
1004
406
  }
1005
- if (!ts2.isObjectLiteralExpression(argument)) return;
407
+ if (!ts.isObjectLiteralExpression(argument)) return;
1006
408
  const properties = [];
1007
409
  for (const property of argument.properties) {
1008
- if (ts2.isShorthandPropertyAssignment(property)) {
410
+ if (ts.isShorthandPropertyAssignment(property)) {
1009
411
  properties.push({ key: property.name.text, name: property.name.text });
1010
412
  continue;
1011
413
  }
1012
- if (ts2.isPropertyAssignment(property) && ts2.isIdentifier(property.name) && ts2.isIdentifier(property.initializer)) {
414
+ if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && ts.isIdentifier(property.initializer)) {
1013
415
  properties.push({ key: property.name.text, name: property.initializer.text });
1014
416
  }
1015
417
  }
@@ -1049,8 +451,8 @@ var collectCallSite = (call, caller, context) => {
1049
451
  };
1050
452
  };
1051
453
  var collectParamDestructure = (declaration, caller, context) => {
1052
- if (!declaration.initializer || !ts2.isIdentifier(declaration.initializer)) return null;
1053
- if (!ts2.isObjectBindingPattern(declaration.name) && !ts2.isArrayBindingPattern(declaration.name)) {
454
+ if (!declaration.initializer || !ts.isIdentifier(declaration.initializer)) return null;
455
+ if (!ts.isObjectBindingPattern(declaration.name) && !ts.isArrayBindingPattern(declaration.name)) {
1054
456
  return null;
1055
457
  }
1056
458
  const sourceParam = declaration.initializer.text;
@@ -1072,58 +474,483 @@ var collectParamDestructure = (declaration, caller, context) => {
1072
474
  };
1073
475
  };
1074
476
 
1075
- // src/auto-instrumentation/transform-source.ts
477
+ // src/auto-instrumentation/parameter-initializer-may-run-code.ts
478
+ import ts2 from "typescript";
479
+ var unwrap = (expression) => {
480
+ let current = expression;
481
+ while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
482
+ current = current.expression;
483
+ }
484
+ return current;
485
+ };
486
+ var isSafeLiteralInitializer = (expression) => {
487
+ const current = unwrap(expression);
488
+ if (ts2.isNumericLiteral(current) || ts2.isStringLiteral(current) || ts2.isNoSubstitutionTemplateLiteral(current) || ts2.isBigIntLiteral(current)) {
489
+ return true;
490
+ }
491
+ if (current.kind === ts2.SyntaxKind.TrueKeyword || current.kind === ts2.SyntaxKind.FalseKeyword || current.kind === ts2.SyntaxKind.NullKeyword) {
492
+ return true;
493
+ }
494
+ if (ts2.isIdentifier(current) && current.text === "undefined") return true;
495
+ if (ts2.isPrefixUnaryExpression(current) && (current.operator === ts2.SyntaxKind.PlusToken || current.operator === ts2.SyntaxKind.MinusToken)) {
496
+ return ts2.isNumericLiteral(unwrap(current.operand));
497
+ }
498
+ return false;
499
+ };
500
+ var functionHasPreBodyParameterEvaluation = (node) => {
501
+ for (const parameter of node.parameters) {
502
+ if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
503
+ if (!parameter.initializer) continue;
504
+ if (!isSafeLiteralInitializer(parameter.initializer)) return true;
505
+ }
506
+ return false;
507
+ };
508
+
509
+ // src/auto-instrumentation/suspension-whitelist.ts
510
+ import ts3 from "typescript";
511
+ var nodeFlags = ts3.NodeFlags;
512
+ var AWAIT_USING_FLAG = nodeFlags.AwaitUsing ?? 0;
513
+ var USING_FLAG = nodeFlags.Using ?? 0;
514
+ var KNOWN_SUSPENSION_KIND_NAMES = [
515
+ "AwaitExpression",
516
+ "AwaitKeyword",
517
+ "YieldExpression",
518
+ "YieldKeyword",
519
+ "UsingKeyword"
520
+ ];
521
+ var isFunctionLike = (node) => ts3.isFunctionLike(node);
522
+ var isAsyncFunctionLike = (node) => (ts3.getCombinedModifierFlags(node) & ts3.ModifierFlags.Async) !== 0;
523
+ var isAsyncGeneratorLike = (node) => isFunctionLike(node) && isAsyncFunctionLike(node) && "asteriskToken" in node && Boolean(node.asteriskToken);
524
+ var isSyncGeneratorLike = (node) => isFunctionLike(node) && "asteriskToken" in node && Boolean(node.asteriskToken) && !isAsyncFunctionLike(node);
525
+ var visitOwnBody = (node, visitNode) => {
526
+ if (!node.body) return;
527
+ const visit = (candidate) => {
528
+ if (candidate !== node && isFunctionLike(candidate)) {
529
+ visitNode(candidate);
530
+ return;
531
+ }
532
+ if (visitNode(candidate)) return;
533
+ ts3.forEachChild(candidate, visit);
534
+ };
535
+ visit(node.body);
536
+ };
537
+ var nestedFunctionSpans = (node, sourceFile) => {
538
+ const spans = [];
539
+ visitOwnBody(node, (candidate) => {
540
+ if (candidate !== node && isFunctionLike(candidate)) {
541
+ spans.push([candidate.getStart(sourceFile), candidate.end]);
542
+ }
543
+ return void 0;
544
+ });
545
+ return spans.sort((left, right) => left[0] - right[0]);
546
+ };
547
+ var slicesOutside = (start, end, spans) => {
548
+ const slices = [];
549
+ let cursor = start;
550
+ for (const [from, to] of spans) {
551
+ if (to <= cursor) continue;
552
+ if (from >= end) break;
553
+ if (from > cursor) slices.push([cursor, Math.min(from, end)]);
554
+ cursor = Math.max(cursor, to);
555
+ }
556
+ if (cursor < end) slices.push([cursor, end]);
557
+ return slices;
558
+ };
559
+ var sliceHasAwaitKeyword = (source, start, end) => {
560
+ if (end <= start) return false;
561
+ const scanner = ts3.createScanner(
562
+ ts3.ScriptTarget.Latest,
563
+ true,
564
+ ts3.LanguageVariant.Standard,
565
+ source.slice(start, end)
566
+ );
567
+ while (true) {
568
+ const token = scanner.scan();
569
+ if (token === ts3.SyntaxKind.EndOfFileToken) return false;
570
+ if (token === ts3.SyntaxKind.AwaitKeyword) return true;
571
+ }
572
+ };
573
+ var ownBodyHasAwaitKeyword = (node, sourceFile) => {
574
+ if (!node.body) return false;
575
+ const spans = nestedFunctionSpans(node, sourceFile);
576
+ for (const [start, end] of slicesOutside(node.body.getStart(sourceFile), node.body.end, spans)) {
577
+ if (sliceHasAwaitKeyword(sourceFile.text, start, end)) return true;
578
+ }
579
+ return false;
580
+ };
581
+ var isAwaitUsingList = (node) => {
582
+ const text = node.getText().trimStart();
583
+ if (text.startsWith("await using")) return true;
584
+ if (AWAIT_USING_FLAG === 0 || USING_FLAG === 0) return false;
585
+ return (node.flags & AWAIT_USING_FLAG) === AWAIT_USING_FLAG && (node.flags & USING_FLAG) !== 0;
586
+ };
587
+ var unknownSuspensionKind = (node) => {
588
+ const name = ts3.SyntaxKind[node.kind];
589
+ if (typeof name !== "string" || !/Await|Yield|Using/.test(name)) return false;
590
+ return !KNOWN_SUSPENSION_KIND_NAMES.includes(name);
591
+ };
592
+ var ownSuspensionFacts = (node, sourceFile) => {
593
+ const facts = {
594
+ awaitExpressions: false,
595
+ forAwait: false,
596
+ awaitUsing: false,
597
+ unknown: false,
598
+ awaitKeyword: ownBodyHasAwaitKeyword(node, sourceFile)
599
+ };
600
+ visitOwnBody(node, (candidate) => {
601
+ if (ts3.isAwaitExpression(candidate)) facts.awaitExpressions = true;
602
+ if (ts3.isForOfStatement(candidate) && candidate.awaitModifier) facts.forAwait = true;
603
+ if (ts3.isVariableDeclarationList(candidate) && isAwaitUsingList(candidate)) {
604
+ facts.awaitUsing = true;
605
+ }
606
+ if (unknownSuspensionKind(candidate)) facts.unknown = true;
607
+ return void 0;
608
+ });
609
+ return facts;
610
+ };
611
+ var bodyHasDirectEval = (node) => {
612
+ let found = false;
613
+ visitOwnBody(node, (candidate) => {
614
+ if (ts3.isCallExpression(candidate) && ts3.isIdentifier(candidate.expression) && candidate.expression.text === "eval") {
615
+ found = true;
616
+ }
617
+ return found;
618
+ });
619
+ return found;
620
+ };
621
+ var decideAsyncInstrumentation = (node, sourceFile) => {
622
+ if (isAsyncGeneratorLike(node)) return { action: "skip", reason: "unsupported_async_generator" };
623
+ if (!isAsyncFunctionLike(node)) return { action: "inline" };
624
+ if (bodyHasDirectEval(node)) return { action: "skip", reason: "direct_eval" };
625
+ const facts = ownSuspensionFacts(node, sourceFile);
626
+ if (facts.unknown) return { action: "skip", reason: "unknown_suspension" };
627
+ if (facts.forAwait) return { action: "skip", reason: "unsupported_for_await" };
628
+ if (facts.awaitUsing) return { action: "skip", reason: "await_using" };
629
+ if (facts.awaitKeyword && !facts.awaitExpressions) {
630
+ return { action: "skip", reason: "unknown_suspension" };
631
+ }
632
+ if (facts.awaitExpressions) return { action: "suspend_resume" };
633
+ return { action: "inline" };
634
+ };
635
+
636
+ // src/auto-instrumentation/verify-instrumentation-isomorphism.ts
637
+ import ts4 from "typescript";
638
+ var RASPUTIN_IDENT = /^__rasputin/;
1076
639
  var scriptKindFor = (filePath) => {
1077
- if (/\.tsx$/i.test(filePath)) return ts3.ScriptKind.TSX;
1078
- if (/\.jsx$/i.test(filePath)) return ts3.ScriptKind.JSX;
1079
- if (/\.[cm]?ts$/i.test(filePath)) return ts3.ScriptKind.TS;
1080
- return ts3.ScriptKind.JS;
640
+ if (/\.tsx$/i.test(filePath)) return ts4.ScriptKind.TSX;
641
+ if (/\.jsx$/i.test(filePath)) return ts4.ScriptKind.JSX;
642
+ if (/\.[cm]?ts$/i.test(filePath)) return ts4.ScriptKind.TS;
643
+ return ts4.ScriptKind.JS;
644
+ };
645
+ var parse = (filePath, source) => ts4.createSourceFile(filePath, source, ts4.ScriptTarget.Latest, true, scriptKindFor(filePath));
646
+ var isRasputinName = (name) => RASPUTIN_IDENT.test(name);
647
+ var identText = (node) => {
648
+ if (ts4.isIdentifier(node)) return node.text;
649
+ if (ts4.isPrivateIdentifier(node)) return node.text;
650
+ return void 0;
651
+ };
652
+ var calleeName = (expression) => {
653
+ if (ts4.isIdentifier(expression)) return expression.text;
654
+ if (ts4.isPropertyAccessExpression(expression)) {
655
+ const object = identText(expression.expression);
656
+ const property = identText(expression.name);
657
+ if (object && isRasputinName(object) && property) return `${object}.${property}`;
658
+ }
659
+ return void 0;
660
+ };
661
+ var isRasputinCall = (node, methods) => {
662
+ if (!ts4.isCallExpression(node)) return false;
663
+ const name = calleeName(node.expression);
664
+ if (!name) return false;
665
+ if (isRasputinName(name)) return !methods || methods.has(name.split(".")[1] ?? "");
666
+ return false;
667
+ };
668
+ var unwrapCall = (node) => {
669
+ let current = node;
670
+ while (isRasputinCall(current) && current.arguments.length === 1 && current.arguments[0]) {
671
+ current = current.arguments[0];
672
+ if (ts4.isParenthesizedExpression(current)) current = current.expression;
673
+ }
674
+ if (ts4.isParenthesizedExpression(current)) return current.expression;
675
+ return current;
676
+ };
677
+ var isRasputinStatement = (node) => {
678
+ if (ts4.isEmptyStatement(node)) return true;
679
+ if (ts4.isVariableStatement(node)) {
680
+ return node.declarationList.declarations.every((declaration) => {
681
+ const name = identText(declaration.name);
682
+ return Boolean(name && isRasputinName(name));
683
+ });
684
+ }
685
+ if (ts4.isExpressionStatement(node)) {
686
+ if (!isRasputinCall(node.expression)) return false;
687
+ const unwrapped = unwrapCall(node.expression);
688
+ return unwrapped === node.expression;
689
+ }
690
+ return false;
691
+ };
692
+ var isRasputinTryWrapper = (node) => {
693
+ const catchClause = node.catchClause;
694
+ if (!catchClause || !node.finallyBlock) return false;
695
+ const catchCalls = catchClause.block.statements.filter(
696
+ (statement) => !isRasputinStatement(statement)
697
+ );
698
+ const leftover = catchCalls[0];
699
+ const leftoverThrow = catchCalls.length === 1 && leftover !== void 0 && ts4.isThrowStatement(leftover) && ts4.isIdentifier(leftover.expression) && isRasputinName(leftover.expression.text);
700
+ if (!leftoverThrow && catchCalls.length > 0) return false;
701
+ return node.finallyBlock.statements.every((statement) => isRasputinStatement(statement));
702
+ };
703
+ var flattenStatements = (statements) => {
704
+ const flattened = [];
705
+ for (const statement of statements) {
706
+ if (isRasputinStatement(statement)) continue;
707
+ if (ts4.isTryStatement(statement) && isRasputinTryWrapper(statement)) {
708
+ flattened.push(...flattenStatements(statement.tryBlock.statements));
709
+ continue;
710
+ }
711
+ flattened.push(statement);
712
+ }
713
+ return flattened;
714
+ };
715
+ var extra = (node) => {
716
+ if (ts4.isIdentifier(node) || ts4.isPrivateIdentifier(node)) {
717
+ return isRasputinName(node.text) ? "" : node.text;
718
+ }
719
+ if (ts4.isStringLiteral(node) || ts4.isNumericLiteral(node) || ts4.isNoSubstitutionTemplateLiteral(node)) {
720
+ return node.text;
721
+ }
722
+ if (ts4.isToken(node) && !ts4.isIdentifier(node)) return ts4.SyntaxKind[node.kind] ?? "";
723
+ return "";
724
+ };
725
+ var signatureOf = (node) => {
726
+ const unwrapped = unwrapCall(node);
727
+ if (unwrapped !== node) return signatureOf(unwrapped);
728
+ if (ts4.isEmptyStatement(unwrapped) || isRasputinStatement(unwrapped)) return "";
729
+ if (ts4.isTryStatement(unwrapped) && isRasputinTryWrapper(unwrapped)) {
730
+ return flattenStatements(unwrapped.tryBlock.statements).map(signatureOf).filter(Boolean).join("");
731
+ }
732
+ if (ts4.isBlock(unwrapped)) {
733
+ const statements = flattenStatements(unwrapped.statements);
734
+ return `Block[${statements.map(signatureOf).filter(Boolean).join(",")}]`;
735
+ }
736
+ if (ts4.isCatchClause(unwrapped)) {
737
+ const statements = flattenStatements(unwrapped.block.statements);
738
+ const variable = unwrapped.variableDeclaration ? signatureOf(unwrapped.variableDeclaration) : "";
739
+ return `Catch${variable}[${statements.map(signatureOf).filter(Boolean).join(",")}]`;
740
+ }
741
+ if (ts4.isArrowFunction(unwrapped) && ts4.isBlock(unwrapped.body)) {
742
+ const statements = flattenStatements(unwrapped.body.statements);
743
+ const only = statements[0];
744
+ if (statements.length === 1 && only && ts4.isReturnStatement(only) && only.expression) {
745
+ const expression = only.expression;
746
+ const parts2 = [];
747
+ ts4.forEachChild(unwrapped, (child) => {
748
+ if (child === unwrapped.body) {
749
+ const part2 = signatureOf(expression);
750
+ if (part2) parts2.push(part2);
751
+ return;
752
+ }
753
+ const part = signatureOf(child);
754
+ if (part) parts2.push(part);
755
+ });
756
+ return `ArrowFunction[${parts2.join(",")}]`;
757
+ }
758
+ }
759
+ const parts = [];
760
+ ts4.forEachChild(unwrapped, (child) => {
761
+ const part = signatureOf(child);
762
+ if (part) parts.push(part);
763
+ });
764
+ const kind = ts4.SyntaxKind[unwrapped.kind] ?? "Node";
765
+ const label = extra(unwrapped);
766
+ return `${kind}${label}[${parts.join(",")}]`;
767
+ };
768
+ var instrumentationTreesAreIsomorphic = (filePath, original, transformed) => {
769
+ return signatureOf(parse(filePath, original)) === signatureOf(parse(filePath, transformed));
770
+ };
771
+
772
+ // src/auto-instrumentation/verify-transformed-source.ts
773
+ import ts5 from "typescript";
774
+ var transpileForVerification = (filePath, code) => ts5.transpileModule(code, {
775
+ compilerOptions: {
776
+ // ES2020 downlevel keeps native @decorators out of `new Function`.
777
+ target: ts5.ScriptTarget.ES2020,
778
+ module: ts5.ModuleKind.ESNext,
779
+ allowJs: true,
780
+ jsx: ts5.JsxEmit.React,
781
+ experimentalDecorators: true
782
+ },
783
+ reportDiagnostics: true,
784
+ fileName: filePath
785
+ });
786
+ var hasScanOrParseError = (diagnostics) => Boolean(
787
+ diagnostics?.some(
788
+ (diagnostic) => diagnostic.category === ts5.DiagnosticCategory.Error && diagnostic.code < 2e3
789
+ )
790
+ );
791
+ var scriptBodyForEngine = (emitted) => (
792
+ // File loaders strip a leading shebang; `new Function` does not.
793
+ emitted.replace(/^#![^\n]*\n/, "").replaceAll("import.meta", "({})")
794
+ );
795
+ var looksTyped = (filePath) => /\.[cm]?tsx?$/i.test(filePath);
796
+ var scriptKindFor2 = (filePath) => {
797
+ if (/\.tsx$/i.test(filePath)) return ts5.ScriptKind.TSX;
798
+ if (/\.jsx$/i.test(filePath)) return ts5.ScriptKind.JSX;
799
+ if (/\.[cm]?ts$/i.test(filePath)) return ts5.ScriptKind.TS;
800
+ return ts5.ScriptKind.JS;
801
+ };
802
+ var isIterationStatement = (node) => ts5.isForStatement(node) || ts5.isForOfStatement(node) || ts5.isForInStatement(node) || ts5.isWhileStatement(node) || ts5.isDoStatement(node);
803
+ var unwrapLabeled = (node) => {
804
+ let current = node;
805
+ while (ts5.isLabeledStatement(current)) current = current.statement;
806
+ return current;
807
+ };
808
+ var hasInvalidLabelJump = (sourceFile) => {
809
+ const visit = (node, labels) => {
810
+ if (ts5.isFunctionLike(node)) {
811
+ let nestedInvalid = false;
812
+ ts5.forEachChild(node, (child) => {
813
+ if (visit(child, /* @__PURE__ */ new Map())) nestedInvalid = true;
814
+ });
815
+ return nestedInvalid;
816
+ }
817
+ if (ts5.isLabeledStatement(node)) {
818
+ const next = new Map(labels);
819
+ next.set(node.label.text, isIterationStatement(unwrapLabeled(node.statement)));
820
+ return visit(node.statement, next);
821
+ }
822
+ if (ts5.isContinueStatement(node) && node.label) {
823
+ if (labels.get(node.label.text) !== true) return true;
824
+ }
825
+ if (ts5.isBreakStatement(node) && node.label && !labels.has(node.label.text)) {
826
+ return true;
827
+ }
828
+ let invalid = false;
829
+ ts5.forEachChild(node, (child) => {
830
+ if (visit(child, labels)) invalid = true;
831
+ });
832
+ return invalid;
833
+ };
834
+ return visit(sourceFile, /* @__PURE__ */ new Map());
835
+ };
836
+ var importSpecifier = `('[^']+'|"[^"]+")`;
837
+ var importAttributes = String.raw`(?:\s+with\s*\{[^}]*\})?`;
838
+ var stripModuleSyntax = (body) => body.replace(/\bexport\s+default\b/g, "var __rasputinVerifyDefault =").replace(new RegExp(String.raw`\bexport\s*\*[\s\S]*?from\s+${importSpecifier}`, "g"), "").replace(
839
+ new RegExp(
840
+ String.raw`\bexport\s*\{[^}]*\}\s*from\s+${importSpecifier}${importAttributes}`,
841
+ "g"
842
+ ),
843
+ ""
844
+ ).replace(/\bexport\s*\{[^}]*\}/g, "").replace(/\bexport\s+/g, "").replace(
845
+ new RegExp(
846
+ String.raw`\bimport(?:\s+type)?\s+[\s\S]*?from\s+${importSpecifier}${importAttributes}`,
847
+ "g"
848
+ ),
849
+ ""
850
+ ).replace(/\bimport\s+type\s+[\s\S]*?(?:;|\n)/g, "").replace(new RegExp(String.raw`\bimport\s+${importSpecifier}${importAttributes}`, "g"), "");
851
+ var engineAccepts = (body) => {
852
+ for (const candidate of [body, stripModuleSyntax(body)]) {
853
+ try {
854
+ new Function(candidate);
855
+ return true;
856
+ } catch {
857
+ try {
858
+ new Function(`return (async()=>{${candidate}
859
+ })`);
860
+ return true;
861
+ } catch {
862
+ }
863
+ }
864
+ }
865
+ return false;
866
+ };
867
+ var GLUED_INSTRUMENTATION = /\b(?:return|await|throw|typeof|void|yield|new|delete|in|instanceof|of|from|as|case|else)__rasputin/;
868
+ var UNTERMINATED_BARE_LEAVE = /\breturn\s+__rasputin\w*\.leave\(\)(?!\s*;|\()/;
869
+ var transformedSourceIsInvalid = (filePath, code) => {
870
+ if (GLUED_INSTRUMENTATION.test(code)) return true;
871
+ if (UNTERMINATED_BARE_LEAVE.test(code)) return true;
872
+ const sourceFile = ts5.createSourceFile(
873
+ filePath,
874
+ code,
875
+ ts5.ScriptTarget.Latest,
876
+ true,
877
+ scriptKindFor2(filePath)
878
+ );
879
+ if (hasInvalidLabelJump(sourceFile)) return true;
880
+ const { diagnostics, outputText } = transpileForVerification(filePath, code);
881
+ if (hasScanOrParseError(diagnostics)) return true;
882
+ if (looksTyped(filePath)) return !engineAccepts(scriptBodyForEngine(outputText));
883
+ if (engineAccepts(scriptBodyForEngine(code))) return false;
884
+ return !engineAccepts(scriptBodyForEngine(outputText));
885
+ };
886
+
887
+ // src/auto-instrumentation/transform-source.ts
888
+ var isIdentContinue = (code) => code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 36 || code === 95;
889
+ var isIdentStart = (code) => code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 36 || code === 95;
890
+ var separateFromPreviousToken = (source, position, text) => {
891
+ if (position <= 0 || text.length === 0) return text;
892
+ if (isIdentContinue(source.charCodeAt(position - 1)) && isIdentStart(text.charCodeAt(0))) {
893
+ return ` ${text}`;
894
+ }
895
+ return text;
896
+ };
897
+ var RASPUTIN_INSTRUMENTATION_MARKER = "rasputin.instrumented.v3";
898
+ var RASPUTIN_INSTRUMENTATION_PREAMBLE = `/*${RASPUTIN_INSTRUMENTATION_MARKER}*/`;
899
+ var sourceIsRasputinInstrumented = (source) => source.includes(RASPUTIN_INSTRUMENTATION_PREAMBLE);
900
+ var alreadyInstrumentedResult = (source) => ({
901
+ code: source,
902
+ delta: { functions: [], callSites: [], destructures: [], alreadyInstrumented: true }
903
+ });
904
+ var scriptKindFor3 = (filePath) => {
905
+ if (/\.tsx$/i.test(filePath)) return ts6.ScriptKind.TSX;
906
+ if (/\.jsx$/i.test(filePath)) return ts6.ScriptKind.JSX;
907
+ if (/\.[cm]?ts$/i.test(filePath)) return ts6.ScriptKind.TS;
908
+ return ts6.ScriptKind.JS;
1081
909
  };
1082
910
  var functionName = (node, sourceFile) => {
911
+ if (ts6.isConstructorDeclaration(node)) return "constructor";
1083
912
  if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
1084
913
  const parent = node.parent;
1085
- if (ts3.isVariableDeclaration(parent) && ts3.isIdentifier(parent.name)) return parent.name.text;
1086
- if (ts3.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
1087
- if (ts3.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
914
+ if (ts6.isVariableDeclaration(parent) && ts6.isIdentifier(parent.name)) return parent.name.text;
915
+ if (ts6.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
916
+ if (ts6.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
1088
917
  const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
1089
918
  return `anonymous@${location.line + 1}:${location.character + 1}`;
1090
919
  };
1091
920
  var functionLocationPosition = (node, sourceFile) => {
1092
921
  if (node.name) return node.name.getStart(sourceFile);
1093
922
  const parent = node.parent;
1094
- if ((ts3.isVariableDeclaration(parent) || ts3.isPropertyAssignment(parent)) && parent.name) {
923
+ if ((ts6.isVariableDeclaration(parent) || ts6.isPropertyAssignment(parent)) && parent.name) {
1095
924
  return parent.name.getStart(sourceFile);
1096
925
  }
1097
926
  return node.getStart(sourceFile);
1098
927
  };
1099
- var bindingValue = (name, sourceFile) => {
1100
- if (ts3.isIdentifier(name)) return name.text;
1101
- if (ts3.isArrayBindingPattern(name)) {
1102
- const values = name.elements.map((element) => {
1103
- if (ts3.isOmittedExpression(element)) return "";
1104
- const value = bindingValue(element.name, sourceFile);
1105
- return element.dotDotDotToken ? `...${value}` : value;
1106
- });
1107
- return `[${values.join(",")}]`;
928
+ var functionBodyPrologueEnd = (body, sourceFile) => {
929
+ let end = body.getStart(sourceFile) + 1;
930
+ for (const statement of body.statements) {
931
+ if (!isDirectivePrologue(statement)) break;
932
+ end = statement.end;
1108
933
  }
1109
- const properties = name.elements.map((element) => {
1110
- const value = bindingValue(element.name, sourceFile);
1111
- if (element.dotDotDotToken) return `...${value}`;
1112
- if (element.propertyName) return `${element.propertyName.getText(sourceFile)}:${value}`;
1113
- return value;
1114
- });
1115
- return `{${properties.join(",")}}`;
934
+ return end;
1116
935
  };
1117
- var arrowArguments = (node) => {
936
+ var arrowArgsCapture = (node) => {
937
+ if (!node.parameters.every((parameter) => ts6.isIdentifier(parameter.name))) {
938
+ const count = node.parameters.filter(
939
+ (parameter) => !(ts6.isIdentifier(parameter.name) && parameter.name.text === "this")
940
+ ).length;
941
+ return {
942
+ args: `[${Array.from({ length: count }, () => "void 0").join(",")}]`,
943
+ omitValues: true
944
+ };
945
+ }
1118
946
  const values = node.parameters.flatMap((parameter) => {
1119
- if (ts3.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
1120
- const value = bindingValue(parameter.name, node.getSourceFile());
1121
- return [parameter.dotDotDotToken ? `...${value}` : value];
947
+ if (!ts6.isIdentifier(parameter.name) || parameter.name.text === "this") return [];
948
+ return [parameter.dotDotDotToken ? `...${parameter.name.text}` : parameter.name.text];
1122
949
  });
1123
- return `[${values.join(",")}]`;
950
+ return { args: `[${values.join(",")}]`, omitValues: false };
1124
951
  };
1125
952
  var isSupportedFunction = (node) => {
1126
- if (!ts3.isFunctionDeclaration(node) && !ts3.isFunctionExpression(node) && !ts3.isArrowFunction(node) && !ts3.isMethodDeclaration(node) && !ts3.isGetAccessorDeclaration(node) && !ts3.isSetAccessorDeclaration(node)) {
953
+ if (!ts6.isFunctionDeclaration(node) && !ts6.isFunctionExpression(node) && !ts6.isArrowFunction(node) && !ts6.isMethodDeclaration(node) && !ts6.isGetAccessorDeclaration(node) && !ts6.isSetAccessorDeclaration(node)) {
1127
954
  return false;
1128
955
  }
1129
956
  if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
@@ -1134,7 +961,7 @@ var uniqueName = (source, base) => {
1134
961
  while (source.includes(name)) name += "_";
1135
962
  return name;
1136
963
  };
1137
- var isDirectivePrologue = (statement) => ts3.isExpressionStatement(statement) && ts3.isStringLiteral(statement.expression);
964
+ var isDirectivePrologue = (statement) => ts6.isExpressionStatement(statement) && ts6.isStringLiteral(statement.expression);
1138
965
  var insertPositionAfterPrologue = (source, sourceFile) => {
1139
966
  const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
1140
967
  let end = shebangEnd;
@@ -1149,7 +976,7 @@ var preambleInsert = (source, position, preamble) => {
1149
976
  const previous = source[position - 1];
1150
977
  return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
1151
978
  };
1152
- 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)}));`;
979
+ var modulePreamble = (runName, plansName, plansJson) => `${RASPUTIN_INSTRUMENTATION_PREAMBLE}const ${plansName}=${plansJson};const ${runName}=((g,k)=>{const r=g[k]??(g[k]={runtimes:[],modules:[],register(p){const d={run:(i,a,c)=>c(),enter(){return{leave(v){return v},fail(){},end(){},suspend(v){return v},resume(v){return v},run(c){return c()}}}};const m={plans:p,cell:d};this.modules.push(m);const n=this.runtimes.at(-1);if(n)n.bind(m);const f=(i,a,c,l)=>d.run(i,a,c,l);f.enter=(i,a,l)=>d.enter(i,a,l);return f}});return r.register(${plansName})})(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));`;
1153
980
  var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
1154
981
  const start = mappedLocation(
1155
982
  sourceFile,
@@ -1177,40 +1004,276 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
1177
1004
  endLine: end.line,
1178
1005
  endColumn: end.column,
1179
1006
  params,
1180
- paramNames
1007
+ paramNames,
1008
+ instrumented: true
1181
1009
  };
1182
1010
  };
1183
- var canOmitCallValues = (analysis, analyzed) => {
1184
- if (!functionIsExact(analysis.plan) || analysis.hasThrow || analysis.hasCoercionGuard) {
1185
- return false;
1011
+ var wrap = (edits, start, end, open, close, depth) => {
1012
+ const width = Math.max(0, end - start);
1013
+ edits.push({ position: start, text: open, deleteCount: 0, depth, width, kind: "open" });
1014
+ edits.push({ position: end, text: close, deleteCount: 0, depth, width, kind: "close" });
1015
+ };
1016
+ var compareEdits = (left, right) => {
1017
+ if (left.position !== right.position) return right.position - left.position;
1018
+ if (left.kind !== right.kind) {
1019
+ const rank = { replace: 0, close: 1, open: 2 };
1020
+ return rank[left.kind] - rank[right.kind];
1186
1021
  }
1187
- const callers = analyzed.filter(
1188
- (caller) => caller.callEdges.some((edge) => edge.calleeFunctionId === analysis.functionId)
1189
- );
1190
- if (callers.length === 0) return false;
1191
- return callers.every((caller) => caller.plan.fallbackReason === void 0 && !caller.hasThrow);
1022
+ if (left.kind === "close") {
1023
+ if (left.width !== right.width) return right.width - left.width;
1024
+ return left.depth - right.depth;
1025
+ }
1026
+ if (left.kind === "open") {
1027
+ if (left.width !== right.width) return left.width - right.width;
1028
+ return right.depth - left.depth;
1029
+ }
1030
+ return 0;
1031
+ };
1032
+ var VLQ_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1033
+ var encodeVlq = (value) => {
1034
+ let vlq = value < 0 ? -value << 1 | 1 : value << 1;
1035
+ let encoded = "";
1036
+ do {
1037
+ let digit = vlq & 31;
1038
+ vlq >>>= 5;
1039
+ if (vlq > 0) digit |= 32;
1040
+ encoded += VLQ_ALPHABET[digit];
1041
+ } while (vlq > 0);
1042
+ return encoded;
1043
+ };
1044
+ var SOURCEMAP_COMMENT = /(?:\/\/[#@][ \t]*sourceMappingURL=([^\s'"]+)|\/\*[#@][ \t]*sourceMappingURL=([^\s*'"]+)[ \t]*\*\/)\s*$/;
1045
+ var decodeInlineSourceMap = (url) => {
1046
+ const base64 = /^data:application\/json(?:;charset=[^;,]+)?;base64,(.+)$/i.exec(url);
1047
+ if (base64?.[1]) {
1048
+ try {
1049
+ return Buffer.from(base64[1], "base64").toString("utf8");
1050
+ } catch {
1051
+ return void 0;
1052
+ }
1053
+ }
1054
+ const raw = /^data:application\/json(?:;charset=[^;,]+)?,(.+)$/i.exec(url);
1055
+ if (!raw?.[1]) return void 0;
1056
+ try {
1057
+ return decodeURIComponent(raw[1]);
1058
+ } catch {
1059
+ return void 0;
1060
+ }
1061
+ };
1062
+ var upstreamTraceMap = (original) => {
1063
+ const match = SOURCEMAP_COMMENT.exec(original);
1064
+ const url = match?.[1] ?? match?.[2];
1065
+ if (!url) return void 0;
1066
+ const payload = url.startsWith("data:") ? decodeInlineSourceMap(url) : void 0;
1067
+ if (!payload) return void 0;
1068
+ try {
1069
+ return new TraceMap2(payload);
1070
+ } catch {
1071
+ return void 0;
1072
+ }
1073
+ };
1074
+ var lineStartsOf = (text) => {
1075
+ const starts = [0];
1076
+ for (let index = 0; index < text.length; index++) {
1077
+ if (text[index] === "\n") starts.push(index + 1);
1078
+ }
1079
+ return starts;
1080
+ };
1081
+ var positionAt = (starts, offset) => {
1082
+ let low = 0;
1083
+ let high = starts.length - 1;
1084
+ while (low < high) {
1085
+ const mid = low + high + 1 >> 1;
1086
+ if ((starts[mid] ?? 0) <= offset) low = mid;
1087
+ else high = mid - 1;
1088
+ }
1089
+ return { line: low, column: offset - (starts[low] ?? 0) };
1090
+ };
1091
+ var sortedEdits = (edits) => [...edits].sort(compareEdits);
1092
+ var originalOffsetToGenerated = (originalOffset, edits) => {
1093
+ let generated = originalOffset;
1094
+ for (const edit of edits) {
1095
+ const delta = edit.text.length - edit.deleteCount;
1096
+ if (originalOffset >= edit.position + edit.deleteCount) generated += delta;
1097
+ else if (originalOffset >= edit.position) generated = edit.position + edit.text.length;
1098
+ }
1099
+ return generated;
1100
+ };
1101
+ var mappingOffsets = (original, edits) => {
1102
+ const offsets = /* @__PURE__ */ new Set([0, original.length]);
1103
+ for (const start of lineStartsOf(original)) offsets.add(start);
1104
+ for (const edit of edits) {
1105
+ offsets.add(edit.position);
1106
+ offsets.add(edit.position + edit.deleteCount);
1107
+ }
1108
+ return [...offsets].filter((offset) => offset >= 0 && offset <= original.length).sort((left, right) => left - right);
1109
+ };
1110
+ var encodeColumnMappings = (original, generated, file, edits) => {
1111
+ const upstream = upstreamTraceMap(original);
1112
+ const sources = (upstream?.sources.filter((source) => Boolean(source)) ?? [file]).filter((source, index, all) => all.indexOf(source) === index);
1113
+ if (sources.length === 0) sources.push(file);
1114
+ const applied = sortedEdits(edits);
1115
+ const originalStarts = lineStartsOf(original);
1116
+ const generatedStarts = lineStartsOf(generated);
1117
+ const pointsByLine = Array.from({ length: generatedStarts.length }, () => []);
1118
+ const seen = /* @__PURE__ */ new Set();
1119
+ for (const originalOffset of mappingOffsets(original, edits)) {
1120
+ const generatedOffset = Math.min(
1121
+ originalOffsetToGenerated(originalOffset, applied),
1122
+ generated.length
1123
+ );
1124
+ const originalPosition = positionAt(originalStarts, originalOffset);
1125
+ const generatedPosition = positionAt(generatedStarts, generatedOffset);
1126
+ let sourceIndex = 0;
1127
+ let mappedLine = originalPosition.line;
1128
+ let mappedColumn = originalPosition.column;
1129
+ if (upstream) {
1130
+ const mapped = originalPositionFor2(upstream, {
1131
+ line: originalPosition.line + 1,
1132
+ column: originalPosition.column
1133
+ });
1134
+ if (mapped.source && mapped.line != null && mapped.column != null) {
1135
+ const index = sources.indexOf(mapped.source);
1136
+ sourceIndex = index >= 0 ? index : 0;
1137
+ mappedLine = mapped.line - 1;
1138
+ mappedColumn = mapped.column;
1139
+ }
1140
+ }
1141
+ const key = `${generatedPosition.line}:${generatedPosition.column}`;
1142
+ if (seen.has(key)) continue;
1143
+ seen.add(key);
1144
+ pointsByLine[generatedPosition.line]?.push({
1145
+ generatedColumn: generatedPosition.column,
1146
+ sourceIndex,
1147
+ originalLine: mappedLine,
1148
+ originalColumn: mappedColumn
1149
+ });
1150
+ }
1151
+ const lines = pointsByLine.map((segments) => {
1152
+ segments.sort((left, right) => left.generatedColumn - right.generatedColumn);
1153
+ let lastGeneratedColumn = 0;
1154
+ let lastSourceIndex = 0;
1155
+ let lastOriginalLine = 0;
1156
+ let lastOriginalColumn = 0;
1157
+ return segments.map((point) => {
1158
+ const encoded = `${encodeVlq(point.generatedColumn - lastGeneratedColumn)}${encodeVlq(point.sourceIndex - lastSourceIndex)}${encodeVlq(point.originalLine - lastOriginalLine)}${encodeVlq(point.originalColumn - lastOriginalColumn)}`;
1159
+ lastGeneratedColumn = point.generatedColumn;
1160
+ lastSourceIndex = point.sourceIndex;
1161
+ lastOriginalLine = point.originalLine;
1162
+ lastOriginalColumn = point.originalColumn;
1163
+ return encoded;
1164
+ }).join(",");
1165
+ });
1166
+ return { sources, mappings: lines.join(";") };
1167
+ };
1168
+ var attachInlineSourceMap = (original, generated, filePath, edits) => {
1169
+ const file = basename(filePath);
1170
+ const withoutUpstream = generated.replace(SOURCEMAP_COMMENT, "");
1171
+ const { sources, mappings } = encodeColumnMappings(original, withoutUpstream, file, edits);
1172
+ const comment = `/*# sourceMappingURL=data:application/json;base64,${Buffer.from(
1173
+ JSON.stringify({ version: 3, file, sources, mappings })
1174
+ ).toString("base64")} */`;
1175
+ return withoutUpstream.endsWith("\n") ? `${withoutUpstream.slice(0, -1)}${comment}
1176
+ ` : `${withoutUpstream}${comment}`;
1177
+ };
1178
+ var insertAfterSuperOrPrologue = (body, sourceFile) => {
1179
+ const end = functionBodyPrologueEnd(body, sourceFile);
1180
+ for (const statement of body.statements) {
1181
+ if (isDirectivePrologue(statement)) continue;
1182
+ if (ts6.isExpressionStatement(statement) && ts6.isCallExpression(statement.expression) && statement.expression.expression.kind === ts6.SyntaxKind.SuperKeyword) {
1183
+ return statement.end;
1184
+ }
1185
+ break;
1186
+ }
1187
+ return end;
1188
+ };
1189
+ var failEpilogue = (handleName, errorName, args2, restoreOnCatch) => `}catch(${errorName}){${restoreOnCatch ? `${handleName}.resume();` : ""}${handleName}.fail(${errorName},${args2});throw ${errorName};}finally{${handleName}.end();}`;
1190
+ var walkOwnBody = (node, visitNode) => {
1191
+ if (!node.body) return;
1192
+ const visit = (candidate) => {
1193
+ if (candidate !== node && ts6.isFunctionLike(candidate)) return;
1194
+ visitNode(candidate);
1195
+ ts6.forEachChild(candidate, visit);
1196
+ };
1197
+ visit(node.body);
1198
+ };
1199
+ var collectAwaitWraps = (node, sourceFile, handleName, edits, depth) => {
1200
+ walkOwnBody(node, (candidate) => {
1201
+ if (!ts6.isAwaitExpression(candidate)) return;
1202
+ wrap(
1203
+ edits,
1204
+ candidate.expression.getStart(sourceFile),
1205
+ candidate.expression.end,
1206
+ `${handleName}.suspend(`,
1207
+ ")",
1208
+ depth + 3
1209
+ );
1210
+ wrap(
1211
+ edits,
1212
+ candidate.getStart(sourceFile),
1213
+ candidate.end,
1214
+ `${handleName}.resume(`,
1215
+ ")",
1216
+ depth + 2
1217
+ );
1218
+ });
1219
+ };
1220
+ var collectCatchFinallyResumes = (node, sourceFile, handleName, edits, depth) => {
1221
+ walkOwnBody(node, (candidate) => {
1222
+ if (!ts6.isTryStatement(candidate)) return;
1223
+ const blocks = [candidate.catchClause?.block, candidate.finallyBlock];
1224
+ for (const block of blocks) {
1225
+ if (!block) continue;
1226
+ edits.push({
1227
+ position: block.getStart(sourceFile) + 1,
1228
+ text: `${handleName}.resume();`,
1229
+ deleteCount: 0,
1230
+ depth,
1231
+ width: 0,
1232
+ kind: "replace"
1233
+ });
1234
+ }
1235
+ });
1236
+ };
1237
+ var collectReturnWraps = (node, sourceFile, handleName, edits, depth) => {
1238
+ if (!node.body || !ts6.isBlock(node.body)) return;
1239
+ const visit = (candidate, at) => {
1240
+ if (candidate !== node && ts6.isFunctionLike(candidate)) return;
1241
+ if (ts6.isReturnStatement(candidate) && candidate.expression) {
1242
+ const expression = candidate.expression;
1243
+ wrap(
1244
+ edits,
1245
+ expression.getStart(sourceFile),
1246
+ expression.end,
1247
+ `(${handleName}.leave((`,
1248
+ ")))",
1249
+ at
1250
+ );
1251
+ return;
1252
+ }
1253
+ ts6.forEachChild(candidate, (child) => visit(child, at + 1));
1254
+ };
1255
+ visit(node.body, depth + 1);
1192
1256
  };
1193
1257
  var transformSource = (source, options) => {
1194
1258
  if (source.includes("@rasputin-ignore-file")) return void 0;
1195
- const sourceFile = ts3.createSourceFile(
1259
+ if (sourceIsRasputinInstrumented(source)) return alreadyInstrumentedResult(source);
1260
+ const sourceFile = ts6.createSourceFile(
1196
1261
  options.filePath,
1197
1262
  source,
1198
- ts3.ScriptTarget.Latest,
1263
+ ts6.ScriptTarget.Latest,
1199
1264
  true,
1200
- scriptKindFor(options.filePath)
1265
+ scriptKindFor3(options.filePath)
1201
1266
  );
1202
1267
  const runName = uniqueName(source, "__rasputinRun__");
1203
- const capName = uniqueName(source, "__rasputinCap__");
1204
1268
  const plansName = uniqueName(source, "__rasputinPlans__");
1269
+ const handleName = uniqueName(source, "__rasputinH__");
1270
+ const errorName = uniqueName(source, "__rasputinE__");
1205
1271
  const absolutePath = resolve4(options.filePath);
1206
- const derivationMode = options.derivationMode ?? "analysis";
1207
1272
  const edits = [];
1208
- let order = 0;
1209
1273
  const delta = {
1210
1274
  functions: [],
1211
1275
  callSites: [],
1212
- destructures: [],
1213
- derivationPlans: []
1276
+ destructures: []
1214
1277
  };
1215
1278
  const collectContext = {
1216
1279
  sourceFile,
@@ -1218,75 +1281,134 @@ var transformSource = (source, options) => {
1218
1281
  absolutePath
1219
1282
  };
1220
1283
  const pending = [];
1221
- const visit = (node, caller) => {
1284
+ const skipFunction = (collected, reason) => {
1285
+ collected.instrumented = false;
1286
+ collected.skipReason = reason;
1287
+ delta.functions.push(collected);
1288
+ if (process.env.RASPUTIN_DEBUG === "1") {
1289
+ console.info(
1290
+ `[rasputin] skipped instrumentation for ${collected.name} in ${options.filePath}: ${reason}`
1291
+ );
1292
+ }
1293
+ };
1294
+ const visit = (node, caller, depth) => {
1295
+ if (isAsyncGeneratorLike(node) && node.body) {
1296
+ const collected = collectWrappedFunction(
1297
+ node,
1298
+ sourceFile,
1299
+ absolutePath,
1300
+ options.mapLocation
1301
+ );
1302
+ if (collected) {
1303
+ collected.asyncFunction = true;
1304
+ skipFunction(collected, "unsupported_async_generator");
1305
+ }
1306
+ ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
1307
+ return;
1308
+ }
1309
+ if (isSyncGeneratorLike(node) && node.body) {
1310
+ const collected = collectWrappedFunction(
1311
+ node,
1312
+ sourceFile,
1313
+ absolutePath,
1314
+ options.mapLocation
1315
+ );
1316
+ if (collected) skipFunction(collected, "unsupported_sync_generator");
1317
+ ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
1318
+ return;
1319
+ }
1320
+ if (ts6.isConstructorDeclaration(node) && node.body) {
1321
+ const collected = collectWrappedFunction(
1322
+ node,
1323
+ sourceFile,
1324
+ absolutePath,
1325
+ options.mapLocation
1326
+ );
1327
+ if (collected) skipFunction(collected, "unsupported_constructor");
1328
+ ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
1329
+ return;
1330
+ }
1222
1331
  if (isSupportedFunction(node)) {
1223
1332
  const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
1224
1333
  if (!collected) {
1225
- ts3.forEachChild(node, (child) => visit(child, caller));
1334
+ ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
1226
1335
  return;
1227
1336
  }
1228
- pending.push({ node, collected });
1337
+ const asyncFunction = isAsyncFunctionLike(node);
1338
+ collected.asyncFunction = asyncFunction;
1339
+ const decision = asyncFunction ? decideAsyncInstrumentation(node, sourceFile) : { action: "inline" };
1340
+ if (decision.action === "skip") {
1341
+ skipFunction(collected, decision.reason);
1342
+ ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
1343
+ return;
1344
+ }
1345
+ if (functionHasPreBodyParameterEvaluation(node)) {
1346
+ collected.plan = withPreBodyParameterEvaluation(collected.plan);
1347
+ }
1348
+ pending.push({
1349
+ node,
1350
+ collected,
1351
+ depth,
1352
+ suspendResume: decision.action === "suspend_resume"
1353
+ });
1229
1354
  delta.functions.push(collected);
1230
- ts3.forEachChild(node, (child) => visit(child, collected));
1355
+ ts6.forEachChild(node, (child) => visit(child, collected, depth + 1));
1231
1356
  return;
1232
1357
  }
1233
- if (caller && (ts3.isCallExpression(node) || ts3.isNewExpression(node))) {
1358
+ if (caller && (ts6.isCallExpression(node) || ts6.isNewExpression(node))) {
1234
1359
  const site = collectCallSite(node, caller, collectContext);
1235
1360
  if (site) delta.callSites.push(site);
1236
1361
  }
1237
- if (caller && ts3.isVariableDeclaration(node)) {
1362
+ if (caller && ts6.isVariableDeclaration(node)) {
1238
1363
  const destructure = collectParamDestructure(node, caller, collectContext);
1239
1364
  if (destructure) delta.destructures.push(destructure);
1240
1365
  }
1241
- ts3.forEachChild(node, (child) => visit(child, caller));
1366
+ ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
1242
1367
  };
1243
- visit(sourceFile);
1244
- if (pending.length === 0) return void 0;
1245
- const analyzed = analyzeModuleFunctions(
1246
- sourceFile,
1247
- pending.map((entry) => ({
1248
- functionId: entry.collected.functionId,
1249
- name: entry.collected.name,
1250
- node: entry.node
1251
- }))
1252
- );
1253
- delta.derivationPlans = analyzed.map((fn) => fn.plan);
1254
- const analyzedById = new Map(analyzed.map((fn) => [fn.functionId, fn]));
1368
+ visit(sourceFile, void 0, 0);
1369
+ if (pending.length === 0) {
1370
+ return delta.functions.length > 0 ? { code: source, delta } : void 0;
1371
+ }
1255
1372
  for (const [planIndex, entry] of pending.entries()) {
1256
- const analysis = analyzedById.get(entry.collected.functionId);
1257
- const omitValues = derivationMode === "selective" && analysis && canOmitCallValues(analysis, analyzed);
1258
- const valueFlag = omitValues ? ",1" : "";
1259
- const args2 = ts3.isArrowFunction(entry.node) ? arrowArguments(entry.node) : "arguments";
1260
- const asyncCallback = entry.node.modifiers?.some(
1261
- (modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword
1262
- ) ? "async " : "";
1263
- if (ts3.isBlock(entry.node.body)) {
1264
- edits.push({
1265
- position: entry.node.body.getStart(sourceFile) + 1,
1266
- text: `return ${runName}(${planIndex},${args2},${asyncCallback}()=>{`,
1267
- order: order++
1268
- });
1269
- edits.push({
1270
- position: entry.node.body.end - 1,
1271
- text: `}${valueFlag});`,
1272
- order: order++
1273
- });
1274
- } else {
1275
- edits.push({
1276
- position: entry.node.body.getStart(sourceFile),
1277
- text: `${runName}(${planIndex},${args2},${asyncCallback}()=>(`,
1278
- order: order++
1279
- });
1280
- edits.push({
1281
- position: entry.node.body.end,
1282
- text: `)${valueFlag})`,
1283
- order: order++
1284
- });
1285
- }
1286
- if (analysis) {
1287
- for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
1288
- edits.push({ ...edit, order: order++ });
1373
+ const captured = ts6.isArrowFunction(entry.node) ? arrowArgsCapture(entry.node) : { args: "arguments", omitValues: false };
1374
+ const omitArg = captured.omitValues ? ",true" : "";
1375
+ const enterCall = `${runName}.enter(${planIndex},${captured.args}${omitArg})`;
1376
+ const epilogue = failEpilogue(handleName, errorName, captured.args, entry.suspendResume);
1377
+ if (ts6.isBlock(entry.node.body)) {
1378
+ const prologueAt = insertAfterSuperOrPrologue(entry.node.body, sourceFile);
1379
+ const closeAt = entry.node.body.end - 1;
1380
+ wrap(
1381
+ edits,
1382
+ prologueAt,
1383
+ closeAt,
1384
+ `;const ${handleName}=${enterCall};try{`,
1385
+ epilogue,
1386
+ entry.depth
1387
+ );
1388
+ collectReturnWraps(entry.node, sourceFile, handleName, edits, entry.depth);
1389
+ if (entry.suspendResume) {
1390
+ collectAwaitWraps(entry.node, sourceFile, handleName, edits, entry.depth);
1391
+ collectCatchFinallyResumes(entry.node, sourceFile, handleName, edits, entry.depth);
1289
1392
  }
1393
+ } else if (entry.suspendResume) {
1394
+ wrap(
1395
+ edits,
1396
+ entry.node.body.getStart(sourceFile),
1397
+ entry.node.body.end,
1398
+ `{;const ${handleName}=${enterCall};try{return (${handleName}.leave((`,
1399
+ `)));${epilogue}}`,
1400
+ entry.depth
1401
+ );
1402
+ collectAwaitWraps(entry.node, sourceFile, handleName, edits, entry.depth);
1403
+ } else {
1404
+ wrap(
1405
+ edits,
1406
+ entry.node.body.getStart(sourceFile),
1407
+ entry.node.body.end,
1408
+ `{;const ${handleName}=${enterCall};try{return (${handleName}.leave((`,
1409
+ `)));${epilogue}}`,
1410
+ entry.depth
1411
+ );
1290
1412
  }
1291
1413
  }
1292
1414
  const insertAt = insertPositionAfterPrologue(source, sourceFile);
@@ -1297,19 +1419,49 @@ var transformSource = (source, options) => {
1297
1419
  insertAt,
1298
1420
  modulePreamble(
1299
1421
  runName,
1300
- capName,
1301
1422
  plansName,
1302
- JSON.stringify(delta.functions.map((fn) => fn.plan))
1423
+ JSON.stringify(pending.map((entry) => entry.collected.plan))
1303
1424
  )
1304
1425
  ),
1305
- order: -1
1426
+ deleteCount: 0,
1427
+ depth: -1,
1428
+ width: 0,
1429
+ kind: "replace"
1306
1430
  });
1307
- edits.sort((left, right) => right.position - left.position || right.order - left.order);
1431
+ edits.sort(compareEdits);
1308
1432
  let transformed = source;
1309
1433
  for (const edit of edits) {
1310
- transformed = `${transformed.slice(0, edit.position)}${edit.text}${transformed.slice(edit.position)}`;
1434
+ const text = separateFromPreviousToken(transformed, edit.position, edit.text);
1435
+ transformed = `${transformed.slice(0, edit.position)}${text}${transformed.slice(edit.position + edit.deleteCount)}`;
1436
+ }
1437
+ const verify = options.verifyTransform ?? true;
1438
+ const refuseTransformed = () => {
1439
+ for (const entry of pending) {
1440
+ entry.collected.instrumented = false;
1441
+ entry.collected.skipReason = "verification_failed";
1442
+ }
1443
+ if (process.env.RASPUTIN_DEBUG === "1") {
1444
+ console.info(
1445
+ `[rasputin] skipped instrumentation for ${options.filePath}: verification_failed`
1446
+ );
1447
+ }
1448
+ return { code: source, delta };
1449
+ };
1450
+ if (verify && transformedSourceIsInvalid(options.filePath, transformed)) {
1451
+ if (transformedSourceIsInvalid(options.filePath, source)) {
1452
+ if (process.env.RASPUTIN_DEBUG === "1") {
1453
+ console.info(
1454
+ `[rasputin] skipped instrumentation for ${options.filePath}: transformed source failed verification`
1455
+ );
1456
+ }
1457
+ return void 0;
1458
+ }
1459
+ return refuseTransformed();
1460
+ }
1461
+ if (verify && !instrumentationTreesAreIsomorphic(options.filePath, source, transformed)) {
1462
+ return refuseTransformed();
1311
1463
  }
1312
- return { code: transformed, delta };
1464
+ return { code: attachInlineSourceMap(source, transformed, options.filePath, edits), delta };
1313
1465
  };
1314
1466
 
1315
1467
  // src/auto-instrumentation/instrument-compiled-esm.ts
@@ -1349,11 +1501,14 @@ var instrumentCompiledDirectory = (options) => {
1349
1501
  continue;
1350
1502
  }
1351
1503
  const source = readFileSync2(filePath, "utf8");
1352
- if (source.includes("__rasputinRun__") || source.includes("__rasputinPlans__")) continue;
1504
+ if (sourceIsRasputinInstrumented(source)) {
1505
+ const outputPath2 = join2(outputDir, relative2(inputDir, filePath));
1506
+ if (outputPath2 !== filePath) writeText(outputPath2, source);
1507
+ continue;
1508
+ }
1353
1509
  const transformed = transformSource(source, {
1354
1510
  filePath,
1355
- mapLocation: createSourceMapLocationMapper(filePath, source),
1356
- ...options.derivationMode ? { derivationMode: options.derivationMode } : {}
1511
+ mapLocation: createSourceMapLocationMapper(filePath, source)
1357
1512
  });
1358
1513
  if (!transformed) continue;
1359
1514
  const outputPath = join2(outputDir, relative2(inputDir, filePath));
@@ -1368,6 +1523,11 @@ var instrumentCompiledDirectory = (options) => {
1368
1523
  if (manifest) {
1369
1524
  writeText(manifestPath, `${JSON.stringify(manifest, null, " ")}
1370
1525
  `);
1526
+ writeText(
1527
+ `${manifestPath.replace(/\.json$/i, "")}.hash`,
1528
+ `${hashInstrumentationManifest(manifest)}
1529
+ `
1530
+ );
1371
1531
  }
1372
1532
  return { transformedFiles, manifest, manifestPath };
1373
1533
  };
@@ -1379,15 +1539,7 @@ var sdkPackageScopeRootFrom = (instrumentEntryUrl) => resolve6(dirname3(fileURLT
1379
1539
 
1380
1540
  // src/instrument/build.ts
1381
1541
  var printUsage = () => {
1382
- console.error(
1383
- "Usage: rasputin-instrument <input-dir> [--out <output-dir>] [--manifest <path>] [--derivation-mode analysis|selective]"
1384
- );
1385
- };
1386
- var parseDerivationMode = (value) => {
1387
- if (value === void 0) return void 0;
1388
- if (value === "analysis" || value === "selective") return value;
1389
- console.error(`[rasputin] unknown --derivation-mode ${value} (expected analysis or selective)`);
1390
- process.exit(1);
1542
+ console.error("Usage: rasputin-instrument <input-dir> [--out <output-dir>] [--manifest <path>]");
1391
1543
  };
1392
1544
  var parseArgs = (argv) => {
1393
1545
  const inputDir = argv.find((argument) => !argument.startsWith("--"));
@@ -1398,8 +1550,7 @@ var parseArgs = (argv) => {
1398
1550
  return {
1399
1551
  inputDir,
1400
1552
  outputDir: option("--out"),
1401
- manifestPath: option("--manifest"),
1402
- derivationMode: parseDerivationMode(option("--derivation-mode"))
1553
+ manifestPath: option("--manifest")
1403
1554
  };
1404
1555
  };
1405
1556
  var args = parseArgs(process.argv.slice(2));
@@ -1411,8 +1562,7 @@ var result = instrumentCompiledDirectory({
1411
1562
  inputDir: args.inputDir,
1412
1563
  outputDir: args.outputDir,
1413
1564
  manifestPath: args.manifestPath,
1414
- excludedRoots: [sdkPackageScopeRootFrom(import.meta.url)],
1415
- ...args.derivationMode ? { derivationMode: args.derivationMode } : {}
1565
+ excludedRoots: [sdkPackageScopeRootFrom(import.meta.url)]
1416
1566
  });
1417
1567
  console.info(
1418
1568
  `[rasputin] instrumented ${result.transformedFiles.length} file${result.transformedFiles.length === 1 ? "" : "s"}`