@rasputin-ai/node 0.5.0-alpha.12 → 0.5.0-alpha.14

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 (79) hide show
  1. package/README.md +60 -29
  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 +2597 -892
  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 -805
  61. package/dist/instrument/bun.js +1098 -778
  62. package/dist/instrument/node.js +1078 -780
  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/package.json +7 -2
  70. package/dist/derivation/analyze-function.d.ts +0 -42
  71. package/dist/derivation/analyze-function.d.ts.map +0 -1
  72. package/dist/derivation/build-dependency-graph.d.ts +0 -21
  73. package/dist/derivation/build-dependency-graph.d.ts.map +0 -1
  74. package/dist/derivation/derivation-ir.d.ts +0 -33
  75. package/dist/derivation/derivation-ir.d.ts.map +0 -1
  76. package/dist/derivation/derivation-proof.d.ts +0 -14
  77. package/dist/derivation/derivation-proof.d.ts.map +0 -1
  78. package/dist/derivation/emit-capture-plan.d.ts +0 -8
  79. 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,664 +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/analyze-function.ts
381
- var loc = (node, sourceFile) => {
382
- const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
383
- return { line: position.line + 1, column: position.character + 1 };
384
- };
385
- var lookup = (ctx, name) => {
386
- for (let index = ctx.scopes.length - 1; index >= 0; index--) {
387
- const binding = ctx.scopes[index]?.get(name);
388
- if (binding) return binding;
389
- }
390
- return void 0;
391
- };
392
- var fallback = (ctx, reason) => {
393
- ctx.fallback ??= reason;
394
- return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "unknown" };
395
- };
396
- var capture = (ctx, node, reason) => {
397
- const rootId = ctx.nextRootId.value++;
398
- const position = loc(node, ctx.sourceFile);
399
- const expressionId = ctx.ir.root(rootId);
400
- ctx.captureWraps.push({
401
- rootId,
402
- reason,
403
- start: node.getStart(ctx.sourceFile),
404
- end: node.end,
405
- line: position.line,
406
- column: position.column,
407
- expressionId
408
- });
409
- return { expressionId, shape: "unknown", captured: true };
410
- };
411
- var binaryOp = (kind) => {
412
- switch (kind) {
413
- case ts.SyntaxKind.PlusToken:
414
- return "add";
415
- case ts.SyntaxKind.MinusToken:
416
- return "sub";
417
- case ts.SyntaxKind.AsteriskToken:
418
- return "mul";
419
- case ts.SyntaxKind.SlashToken:
420
- return "div";
421
- case ts.SyntaxKind.PercentToken:
422
- return "mod";
423
- case ts.SyntaxKind.EqualsEqualsToken:
424
- return "eq";
425
- case ts.SyntaxKind.ExclamationEqualsToken:
426
- return "neq";
427
- case ts.SyntaxKind.EqualsEqualsEqualsToken:
428
- return "seq";
429
- case ts.SyntaxKind.ExclamationEqualsEqualsToken:
430
- return "sneq";
431
- case ts.SyntaxKind.LessThanToken:
432
- return "lt";
433
- case ts.SyntaxKind.LessThanEqualsToken:
434
- return "lte";
435
- case ts.SyntaxKind.GreaterThanToken:
436
- return "gt";
437
- case ts.SyntaxKind.GreaterThanEqualsToken:
438
- return "gte";
439
- default:
440
- return void 0;
441
- }
442
- };
443
- var isNumericBinaryOp = (op) => op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod";
444
- var unwrapParens = (node) => {
445
- while (ts.isParenthesizedExpression(node)) node = node.expression;
446
- return node;
447
- };
448
- var numericLiteralValue = (node) => {
449
- const value = Number(node.text);
450
- return { type: "number", value };
451
- };
452
- var analyzeExpression = (ctx, node) => {
453
- if (ctx.fallback) return fallback(ctx, ctx.fallback);
454
- if (ts.isParenthesizedExpression(node)) return analyzeExpression(ctx, node.expression);
455
- if (ts.isAsExpression(node) || ts.isNonNullExpression(node)) {
456
- return analyzeExpression(ctx, node.expression);
457
- }
458
- if (node.kind === ts.SyntaxKind.TypeAssertionExpression) {
459
- return analyzeExpression(ctx, node.expression);
460
- }
461
- if (ts.isSatisfiesExpression(node)) return analyzeExpression(ctx, node.expression);
462
- if (node.kind === ts.SyntaxKind.NullKeyword) {
463
- return { expressionId: ctx.ir.constValue({ type: "null" }), shape: "primitive" };
464
- }
465
- if (node.kind === ts.SyntaxKind.TrueKeyword) {
466
- return {
467
- expressionId: ctx.ir.constValue({ type: "boolean", value: true }),
468
- shape: "primitive"
469
- };
470
- }
471
- if (node.kind === ts.SyntaxKind.FalseKeyword) {
472
- return {
473
- expressionId: ctx.ir.constValue({ type: "boolean", value: false }),
474
- shape: "primitive"
475
- };
476
- }
477
- if (ts.isNumericLiteral(node)) {
478
- return { expressionId: ctx.ir.constValue(numericLiteralValue(node)), shape: "primitive" };
479
- }
480
- if (ts.isPrefixUnaryExpression(node) && ts.isNumericLiteral(node.operand) && node.operator === ts.SyntaxKind.MinusToken) {
481
- const value = -Number(node.operand.text);
482
- return {
483
- expressionId: ctx.ir.constValue(
484
- Object.is(value, -0) ? { type: "number", value: 0, negativeZero: true } : { type: "number", value }
485
- ),
486
- shape: "primitive"
487
- };
488
- }
489
- if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
490
- return {
491
- expressionId: ctx.ir.constValue({ type: "string", value: node.text }),
492
- shape: "primitive"
493
- };
494
- }
495
- if (ts.isBigIntLiteral(node)) {
496
- return {
497
- expressionId: ctx.ir.constValue({ type: "bigint", value: node.text.replace(/n$/, "") }),
498
- shape: "primitive"
499
- };
500
- }
501
- if (ts.isIdentifier(node)) {
502
- if (node.text === "undefined") {
503
- return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "primitive" };
504
- }
505
- const binding = lookup(ctx, node.text);
506
- if (!binding) return capture(ctx, node, "external_state");
507
- if (binding.mutable) return capture(ctx, node, "mutable_object");
508
- return {
509
- expressionId: ctx.ir.alias(binding.expressionId),
510
- shape: binding.shape
511
- };
512
- }
513
- if (ts.isTemplateExpression(node)) {
514
- const parts = [
515
- { kind: "lit", value: node.head.text }
516
- ];
517
- for (const span of node.templateSpans) {
518
- const nested = analyzeExpression(ctx, span.expression);
519
- if (nested.shape !== "primitive" && !nested.captured) {
520
- return capture(ctx, node, "unsupported_expression");
521
- }
522
- parts.push({ kind: "expr", expressionId: nested.expressionId });
523
- parts.push({ kind: "lit", value: span.literal.text });
524
- }
525
- return { expressionId: ctx.ir.template(parts), shape: "primitive" };
526
- }
527
- if (ts.isArrayLiteralExpression(node)) {
528
- if (node.elements.some((element) => ts.isSpreadElement(element))) {
529
- return capture(ctx, node, "unsupported_expression");
530
- }
531
- const elements = node.elements.map((element) => {
532
- if (ts.isOmittedExpression(element)) return { kind: "hole" };
533
- return analyzeExpression(ctx, element).expressionId;
534
- });
535
- return { expressionId: ctx.ir.array(elements), shape: "array" };
536
- }
537
- if (ts.isObjectLiteralExpression(node)) {
538
- const properties = [];
539
- for (const property of node.properties) {
540
- if (ts.isShorthandPropertyAssignment(property)) {
541
- const nested = analyzeExpression(ctx, property.name);
542
- if (nested.captured) {
543
- const wrap = ctx.captureWraps.find(
544
- (candidate) => candidate.expressionId === nested.expressionId
545
- );
546
- if (wrap) wrap.shorthandPropertyName = property.name.text;
547
- }
548
- properties.push({ key: property.name.text, value: nested.expressionId });
549
- continue;
550
- }
551
- if (!ts.isPropertyAssignment(property)) {
552
- return capture(ctx, node, "unsupported_expression");
553
- }
554
- if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
555
- return capture(ctx, node, "unsupported_expression");
556
- }
557
- const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
558
- if (key === void 0) return capture(ctx, node, "unsupported_expression");
559
- properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
560
- }
561
- return { expressionId: ctx.ir.object(properties), shape: "object" };
562
- }
563
- if (ts.isPropertyAccessExpression(node)) {
564
- if (node.questionDotToken) return capture(ctx, node, "unknown_property");
565
- const object = analyzeExpression(ctx, node.expression);
566
- if (object.shape === "object" && ts.isIdentifier(node.name)) {
567
- return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
568
- }
569
- return capture(ctx, node, "unknown_property");
570
- }
571
- if (ts.isElementAccessExpression(node)) {
572
- if (node.questionDotToken) return capture(ctx, node, "unknown_property");
573
- const array = analyzeExpression(ctx, node.expression);
574
- if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
575
- return {
576
- expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
577
- shape: "unknown"
578
- };
579
- }
580
- return capture(ctx, node, "unknown_property");
581
- }
582
- if (ts.isCallExpression(node)) {
583
- if (node.questionDotToken || node.typeArguments?.length) {
584
- return capture(ctx, node, "unsupported_expression");
585
- }
586
- if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
587
- const calleeName = node.expression.text;
588
- if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
589
- const provenId = ctx.moduleCallees.get(calleeName);
590
- ctx.callEdges.push({
591
- callerFunctionId: ctx.functionId,
592
- calleeName,
593
- ...provenId ? { calleeFunctionId: provenId } : {}
594
- });
595
- if (!provenId) return capture(ctx, node, "opaque_call");
596
- const args2 = node.arguments.map((argument) => {
597
- if (ts.isSpreadElement(argument)) return void 0;
598
- return analyzeExpression(ctx, argument).expressionId;
599
- });
600
- if (args2.some((argument) => argument === void 0))
601
- return capture(ctx, node, "unsupported_expression");
602
- return {
603
- expressionId: ctx.ir.pureCall(
604
- provenId,
605
- args2.filter((id) => id !== void 0)
606
- ),
607
- shape: "unknown"
608
- };
609
- }
610
- if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
611
- return capture(
612
- ctx,
613
- node,
614
- node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
615
- );
616
- }
617
- if (ts.isConditionalExpression(node)) {
618
- const test = analyzeExpression(ctx, node.condition);
619
- const consequent = analyzeExpression(ctx, node.whenTrue);
620
- const alternate = analyzeExpression(ctx, node.whenFalse);
621
- return {
622
- expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
623
- shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
624
- };
625
- }
626
- if (ts.isBinaryExpression(node)) {
627
- if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
628
- ctx.fallback ??= "mutable_state";
629
- return capture(ctx, node, "mutable_object");
630
- }
631
- if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
632
- const left2 = analyzeExpression(ctx, node.left);
633
- const right2 = analyzeExpression(ctx, node.right);
634
- const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
635
- return {
636
- expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
637
- shape: "unknown"
638
- };
639
- }
640
- const op = binaryOp(node.operatorToken.kind);
641
- if (!op) return capture(ctx, node, "unsupported_expression");
642
- if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
643
- const left = analyzeExpression(ctx, node.left);
644
- const right = analyzeExpression(ctx, node.right);
645
- const numeric = isNumericBinaryOp(op);
646
- const expressionId = ctx.ir.binary(
647
- op,
648
- left.expressionId,
649
- right.expressionId,
650
- numeric ? "numeric" : "primitive"
651
- );
652
- if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
653
- ctx.hasCoercionGuard = true;
654
- const fallbackRootId = ctx.nextRootId.value++;
655
- ctx.guardWraps.push({
656
- fallbackRootId,
657
- start: node.getStart(ctx.sourceFile),
658
- end: node.end,
659
- op
660
- });
661
- }
662
- return { expressionId, shape: "primitive" };
663
- }
664
- if (ts.isPrefixUnaryExpression(node)) {
665
- const operand = analyzeExpression(ctx, node.operand);
666
- if (node.operator === ts.SyntaxKind.ExclamationToken) {
667
- return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
668
- }
669
- if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
670
- if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
671
- return {
672
- expressionId: ctx.ir.unary(
673
- node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
674
- operand.expressionId
675
- ),
676
- shape: "primitive"
677
- };
678
- }
679
- return capture(ctx, node, "unsupported_expression");
680
- }
681
- if (ts.isTypeOfExpression(node)) {
682
- const operand = unwrapParens(node.expression);
683
- if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
684
- return capture(ctx, node, "external_state");
685
- }
686
- const inner = analyzeExpression(ctx, node.expression);
687
- return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
688
- }
689
- return capture(ctx, node, "unsupported_expression");
690
- };
691
- var bindIdentifier = (ctx, name, result2, declaration) => {
692
- const current = ctx.scopes[ctx.scopes.length - 1];
693
- current?.set(name.text, {
694
- expressionId: result2.expressionId,
695
- shape: result2.shape,
696
- mutable: false
697
- });
698
- const position = loc(name, ctx.sourceFile);
699
- const available = loc(declaration, ctx.sourceFile);
700
- ctx.variables.push({
701
- id: ctx.nextVariableId.value++,
702
- name: name.text,
703
- functionId: ctx.functionId,
704
- scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
705
- line: position.line,
706
- column: position.column,
707
- assignmentId: ctx.nextAssignmentId.value++,
708
- expressionId: result2.expressionId,
709
- availableAtLine: available.line,
710
- availableAtColumn: available.column
711
- });
712
- };
713
- var analyzeStatement = (ctx, node) => {
714
- if (ctx.fallback) return;
715
- if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
716
- if (ts.isBlock(node)) {
717
- ctx.scopes.push(/* @__PURE__ */ new Map());
718
- for (const statement of node.statements) analyzeStatement(ctx, statement);
719
- ctx.scopes.pop();
720
- return;
721
- }
722
- if (ts.isReturnStatement(node)) {
723
- if (!node.expression) {
724
- ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
725
- return;
726
- }
727
- ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
728
- return;
729
- }
730
- if (ts.isVariableStatement(node)) {
731
- if (node.declarationList.flags & ts.NodeFlags.Using) {
732
- fallback(ctx, "side_effect");
733
- return;
734
- }
735
- for (const declaration of node.declarationList.declarations) {
736
- if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
737
- fallback(ctx, "unsupported_expression");
738
- return;
739
- }
740
- const result2 = analyzeExpression(ctx, declaration.initializer);
741
- bindIdentifier(ctx, declaration.name, result2, declaration);
742
- }
743
- return;
744
- }
745
- if (ts.isExpressionStatement(node)) {
746
- analyzeExpression(ctx, node.expression);
747
- return;
748
- }
749
- if (ts.isThrowStatement(node)) {
750
- ctx.hasThrow = true;
751
- analyzeExpression(ctx, node.expression);
752
- return;
753
- }
754
- if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
755
- fallback(ctx, "unsupported_expression");
756
- return;
757
- }
758
- if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
759
- fallback(ctx, "has_loop");
760
- return;
761
- }
762
- fallback(ctx, "unsupported_expression");
763
- };
764
- var bindParams = (ctx, node) => {
765
- let index = 0;
766
- for (const parameter of node.parameters) {
767
- if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
768
- if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
769
- fallback(ctx, "unsupported_expression");
770
- index++;
771
- continue;
772
- }
773
- const expressionId = ctx.ir.param(index);
774
- ctx.identifierParams.push(parameter.name.text);
775
- bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
776
- index++;
777
- }
778
- };
779
- var analyzeOne = (node, functionId, name, sourceFile, moduleCallees) => {
780
- const ctx = {
781
- sourceFile,
782
- functionId,
783
- name,
784
- ir: new DerivationIrBuilder(),
785
- variables: [],
786
- captureWraps: [],
787
- guardWraps: [],
788
- callEdges: [],
789
- scopes: [/* @__PURE__ */ new Map()],
790
- nextRootId: { value: 0 },
791
- nextVariableId: { value: 0 },
792
- nextAssignmentId: { value: 0 },
793
- hasThrow: false,
794
- hasCoercionGuard: false,
795
- moduleCallees,
796
- identifierParams: []
797
- };
798
- if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
799
- ctx.fallback = "async";
800
- } else {
801
- bindParams(ctx, node);
802
- if (ts.isBlock(node.body)) {
803
- for (const statement of node.body.statements) analyzeStatement(ctx, statement);
804
- } else {
805
- ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
806
- }
807
- }
808
- const plan = {
809
- ...emptyPlan(functionId),
810
- expressions: ctx.ir.expressions,
811
- variables: ctx.variables,
812
- captureBoundaries: ctx.captureWraps.map((wrap) => ({
813
- id: wrap.rootId,
814
- reason: wrap.reason,
815
- expressionId: wrap.expressionId,
816
- line: wrap.line,
817
- column: wrap.column
818
- })),
819
- controlFacts: [],
820
- ...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
821
- ...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
822
- };
823
- return {
824
- functionId,
825
- name,
826
- node,
827
- plan,
828
- captureWraps: ctx.fallback ? [] : ctx.captureWraps,
829
- guardWraps: ctx.fallback ? [] : ctx.guardWraps,
830
- callEdges: ctx.callEdges,
831
- hasThrow: ctx.hasThrow,
832
- hasCoercionGuard: ctx.hasCoercionGuard
833
- };
834
- };
835
- var analyzeModuleFunctions = (sourceFile, targets) => {
836
- const nameCounts = /* @__PURE__ */ new Map();
837
- for (const target of targets) {
838
- nameCounts.set(target.name, (nameCounts.get(target.name) ?? 0) + 1);
839
- }
840
- const firstPass = targets.map(
841
- (target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
842
- );
843
- const graph = buildDependencyGraph(
844
- firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
845
- firstPass.flatMap((fn) => fn.callEdges)
846
- );
847
- const order = topologicalFunctionOrder(
848
- firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
849
- graph
850
- );
851
- const byId = new Map(targets.map((target) => [target.functionId, target]));
852
- const moduleCallees = /* @__PURE__ */ new Map();
853
- const analyzed = [];
854
- for (const functionId of order) {
855
- const target = byId.get(functionId);
856
- if (!target) continue;
857
- const result2 = analyzeOne(
858
- target.node,
859
- target.functionId,
860
- target.name,
861
- sourceFile,
862
- moduleCallees
863
- );
864
- if (nameCounts.get(result2.name) === 1) {
865
- moduleCallees.set(result2.name, result2.functionId);
866
- }
867
- analyzed.push(result2);
868
- }
869
- const remaining = targets.filter(
870
- (target) => !analyzed.some((fn) => fn.functionId === target.functionId)
871
- );
872
- for (const target of remaining) {
873
- analyzed.push(
874
- analyzeOne(target.node, target.functionId, target.name, sourceFile, moduleCallees)
875
- );
876
- }
877
- return analyzed;
878
- };
879
-
880
- // src/derivation/derivation-proof.ts
881
- var functionIsExact = (plan) => plan.fallbackReason === void 0 && plan.captureBoundaries.length === 0 && plan.controlFacts.length === 0;
882
-
883
- // src/derivation/emit-capture-plan.ts
884
- var captureWrapEdits = (wraps, capName) => {
885
- const edits = [];
886
- let order = 0;
887
- for (const wrap of wraps) {
888
- edits.push({
889
- position: wrap.start,
890
- text: `${wrap.shorthandPropertyName ? `${wrap.shorthandPropertyName}:` : ""}${capName}(${wrap.rootId},`,
891
- order: order++
892
- });
893
- edits.push({
894
- position: wrap.end,
895
- text: ")",
896
- order: order++
897
- });
898
- }
899
- return edits;
900
- };
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";
901
295
 
902
296
  // src/execution-recorder/automatic-runtime.ts
903
- var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
297
+ var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v3";
904
298
  var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
905
299
 
906
300
  // src/auto-instrumentation/collect-instrumentation-delta.ts
907
- import ts2 from "typescript";
301
+ import ts from "typescript";
908
302
  var paramBindingFromName = (name, sourceFile) => {
909
- if (ts2.isIdentifier(name)) return { kind: "identifier", name: name.text };
910
- if (ts2.isArrayBindingPattern(name)) {
303
+ if (ts.isIdentifier(name)) return { kind: "identifier", name: name.text };
304
+ if (ts.isArrayBindingPattern(name)) {
911
305
  const elements = name.elements.map(
912
306
  (element) => {
913
- if (ts2.isOmittedExpression(element)) return { kind: "omitted" };
307
+ if (ts.isOmittedExpression(element)) return { kind: "omitted" };
914
308
  const binding = paramBindingFromName(element.name, sourceFile);
915
309
  if (element.dotDotDotToken) return { kind: "rest", binding };
916
310
  return binding;
@@ -920,7 +314,7 @@ var paramBindingFromName = (name, sourceFile) => {
920
314
  }
921
315
  const properties = name.elements.map((element) => {
922
316
  const binding = paramBindingFromName(element.name, sourceFile);
923
- 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);
924
318
  if (element.dotDotDotToken) {
925
319
  const rest = { kind: "rest", binding };
926
320
  return { key, binding: rest };
@@ -932,7 +326,7 @@ var paramBindingFromName = (name, sourceFile) => {
932
326
  var collectFunctionParams = (node, sourceFile) => {
933
327
  const params = [];
934
328
  for (const parameter of node.parameters) {
935
- if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
329
+ if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
936
330
  const binding = paramBindingFromName(parameter.name, sourceFile);
937
331
  params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
938
332
  }
@@ -973,18 +367,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
973
367
  };
974
368
  var unwrapExpression = (expression) => {
975
369
  let current = expression;
976
- while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
370
+ while (ts.isParenthesizedExpression(current) || ts.isNonNullExpression(current)) {
977
371
  current = current.expression;
978
372
  }
979
- if (ts2.isAwaitExpression(current)) return unwrapExpression(current.expression);
373
+ if (ts.isAwaitExpression(current)) return unwrapExpression(current.expression);
980
374
  return current;
981
375
  };
982
376
  var calleeFromExpression = (expression, sourceFile) => {
983
377
  const current = unwrapExpression(expression);
984
- if (ts2.isIdentifier(current)) {
378
+ if (ts.isIdentifier(current)) {
985
379
  return { calleeName: current.text, calleeText: current.text };
986
380
  }
987
- if (ts2.isPropertyAccessExpression(current) && ts2.isIdentifier(current.name)) {
381
+ if (ts.isPropertyAccessExpression(current) && ts.isIdentifier(current.name)) {
988
382
  return {
989
383
  calleeName: current.name.text,
990
384
  calleeText: current.getText(sourceFile).replace(/\s+/g, "")
@@ -994,9 +388,9 @@ var calleeFromExpression = (expression, sourceFile) => {
994
388
  };
995
389
  var resultBindingForCall = (call) => {
996
390
  let node = call.parent;
997
- if (ts2.isAwaitExpression(node)) node = node.parent;
998
- if (!ts2.isVariableDeclaration(node)) return null;
999
- 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;
1000
394
  const initializer = node.initializer;
1001
395
  if (!initializer) return null;
1002
396
  const inner = unwrapExpression(initializer);
@@ -1006,18 +400,18 @@ var resultBindingForCall = (call) => {
1006
400
  var argIdentifiersForCall = (call) => {
1007
401
  const identifiers = [];
1008
402
  call.arguments?.forEach((argument, index) => {
1009
- if (ts2.isIdentifier(argument)) {
403
+ if (ts.isIdentifier(argument)) {
1010
404
  identifiers.push({ index, kind: "identifier", name: argument.text });
1011
405
  return;
1012
406
  }
1013
- if (!ts2.isObjectLiteralExpression(argument)) return;
407
+ if (!ts.isObjectLiteralExpression(argument)) return;
1014
408
  const properties = [];
1015
409
  for (const property of argument.properties) {
1016
- if (ts2.isShorthandPropertyAssignment(property)) {
410
+ if (ts.isShorthandPropertyAssignment(property)) {
1017
411
  properties.push({ key: property.name.text, name: property.name.text });
1018
412
  continue;
1019
413
  }
1020
- 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)) {
1021
415
  properties.push({ key: property.name.text, name: property.initializer.text });
1022
416
  }
1023
417
  }
@@ -1057,8 +451,8 @@ var collectCallSite = (call, caller, context) => {
1057
451
  };
1058
452
  };
1059
453
  var collectParamDestructure = (declaration, caller, context) => {
1060
- if (!declaration.initializer || !ts2.isIdentifier(declaration.initializer)) return null;
1061
- 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)) {
1062
456
  return null;
1063
457
  }
1064
458
  const sourceParam = declaration.initializer.text;
@@ -1080,58 +474,483 @@ var collectParamDestructure = (declaration, caller, context) => {
1080
474
  };
1081
475
  };
1082
476
 
1083
- // 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/;
1084
639
  var scriptKindFor = (filePath) => {
1085
- if (/\.tsx$/i.test(filePath)) return ts3.ScriptKind.TSX;
1086
- if (/\.jsx$/i.test(filePath)) return ts3.ScriptKind.JSX;
1087
- if (/\.[cm]?ts$/i.test(filePath)) return ts3.ScriptKind.TS;
1088
- 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;
1089
909
  };
1090
910
  var functionName = (node, sourceFile) => {
911
+ if (ts6.isConstructorDeclaration(node)) return "constructor";
1091
912
  if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
1092
913
  const parent = node.parent;
1093
- if (ts3.isVariableDeclaration(parent) && ts3.isIdentifier(parent.name)) return parent.name.text;
1094
- if (ts3.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
1095
- 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);
1096
917
  const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
1097
918
  return `anonymous@${location.line + 1}:${location.character + 1}`;
1098
919
  };
1099
920
  var functionLocationPosition = (node, sourceFile) => {
1100
921
  if (node.name) return node.name.getStart(sourceFile);
1101
922
  const parent = node.parent;
1102
- if ((ts3.isVariableDeclaration(parent) || ts3.isPropertyAssignment(parent)) && parent.name) {
923
+ if ((ts6.isVariableDeclaration(parent) || ts6.isPropertyAssignment(parent)) && parent.name) {
1103
924
  return parent.name.getStart(sourceFile);
1104
925
  }
1105
926
  return node.getStart(sourceFile);
1106
927
  };
1107
- var bindingValue = (name, sourceFile) => {
1108
- if (ts3.isIdentifier(name)) return name.text;
1109
- if (ts3.isArrayBindingPattern(name)) {
1110
- const values = name.elements.map((element) => {
1111
- if (ts3.isOmittedExpression(element)) return "";
1112
- const value = bindingValue(element.name, sourceFile);
1113
- return element.dotDotDotToken ? `...${value}` : value;
1114
- });
1115
- 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;
1116
933
  }
1117
- const properties = name.elements.map((element) => {
1118
- const value = bindingValue(element.name, sourceFile);
1119
- if (element.dotDotDotToken) return `...${value}`;
1120
- if (element.propertyName) return `${element.propertyName.getText(sourceFile)}:${value}`;
1121
- return value;
1122
- });
1123
- return `{${properties.join(",")}}`;
934
+ return end;
1124
935
  };
1125
- 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
+ }
1126
946
  const values = node.parameters.flatMap((parameter) => {
1127
- if (ts3.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
1128
- const value = bindingValue(parameter.name, node.getSourceFile());
1129
- 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];
1130
949
  });
1131
- return `[${values.join(",")}]`;
950
+ return { args: `[${values.join(",")}]`, omitValues: false };
1132
951
  };
1133
952
  var isSupportedFunction = (node) => {
1134
- 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)) {
1135
954
  return false;
1136
955
  }
1137
956
  if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
@@ -1142,7 +961,7 @@ var uniqueName = (source, base) => {
1142
961
  while (source.includes(name)) name += "_";
1143
962
  return name;
1144
963
  };
1145
- var isDirectivePrologue = (statement) => ts3.isExpressionStatement(statement) && ts3.isStringLiteral(statement.expression);
964
+ var isDirectivePrologue = (statement) => ts6.isExpressionStatement(statement) && ts6.isStringLiteral(statement.expression);
1146
965
  var insertPositionAfterPrologue = (source, sourceFile) => {
1147
966
  const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
1148
967
  let end = shebangEnd;
@@ -1157,7 +976,7 @@ var preambleInsert = (source, position, preamble) => {
1157
976
  const previous = source[position - 1];
1158
977
  return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
1159
978
  };
1160
- 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)}));`;
1161
980
  var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
1162
981
  const start = mappedLocation(
1163
982
  sourceFile,
@@ -1185,40 +1004,276 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
1185
1004
  endLine: end.line,
1186
1005
  endColumn: end.column,
1187
1006
  params,
1188
- paramNames
1007
+ paramNames,
1008
+ instrumented: true
1189
1009
  };
1190
1010
  };
1191
- var canOmitCallValues = (analysis, analyzed) => {
1192
- if (!functionIsExact(analysis.plan) || analysis.hasThrow || analysis.hasCoercionGuard) {
1193
- 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];
1194
1021
  }
1195
- const callers = analyzed.filter(
1196
- (caller) => caller.callEdges.some((edge) => edge.calleeFunctionId === analysis.functionId)
1197
- );
1198
- if (callers.length === 0) return false;
1199
- 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);
1200
1256
  };
1201
1257
  var transformSource = (source, options) => {
1202
1258
  if (source.includes("@rasputin-ignore-file")) return void 0;
1203
- const sourceFile = ts3.createSourceFile(
1259
+ if (sourceIsRasputinInstrumented(source)) return alreadyInstrumentedResult(source);
1260
+ const sourceFile = ts6.createSourceFile(
1204
1261
  options.filePath,
1205
1262
  source,
1206
- ts3.ScriptTarget.Latest,
1263
+ ts6.ScriptTarget.Latest,
1207
1264
  true,
1208
- scriptKindFor(options.filePath)
1265
+ scriptKindFor3(options.filePath)
1209
1266
  );
1210
1267
  const runName = uniqueName(source, "__rasputinRun__");
1211
- const capName = uniqueName(source, "__rasputinCap__");
1212
1268
  const plansName = uniqueName(source, "__rasputinPlans__");
1269
+ const handleName = uniqueName(source, "__rasputinH__");
1270
+ const errorName = uniqueName(source, "__rasputinE__");
1213
1271
  const absolutePath = resolve4(options.filePath);
1214
- const derivationMode = options.derivationMode ?? "analysis";
1215
1272
  const edits = [];
1216
- let order = 0;
1217
1273
  const delta = {
1218
1274
  functions: [],
1219
1275
  callSites: [],
1220
- destructures: [],
1221
- derivationPlans: []
1276
+ destructures: []
1222
1277
  };
1223
1278
  const collectContext = {
1224
1279
  sourceFile,
@@ -1226,75 +1281,134 @@ var transformSource = (source, options) => {
1226
1281
  absolutePath
1227
1282
  };
1228
1283
  const pending = [];
1229
- 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
+ }
1230
1331
  if (isSupportedFunction(node)) {
1231
1332
  const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
1232
1333
  if (!collected) {
1233
- ts3.forEachChild(node, (child) => visit(child, caller));
1334
+ ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
1234
1335
  return;
1235
1336
  }
1236
- 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
+ });
1237
1354
  delta.functions.push(collected);
1238
- ts3.forEachChild(node, (child) => visit(child, collected));
1355
+ ts6.forEachChild(node, (child) => visit(child, collected, depth + 1));
1239
1356
  return;
1240
1357
  }
1241
- if (caller && (ts3.isCallExpression(node) || ts3.isNewExpression(node))) {
1358
+ if (caller && (ts6.isCallExpression(node) || ts6.isNewExpression(node))) {
1242
1359
  const site = collectCallSite(node, caller, collectContext);
1243
1360
  if (site) delta.callSites.push(site);
1244
1361
  }
1245
- if (caller && ts3.isVariableDeclaration(node)) {
1362
+ if (caller && ts6.isVariableDeclaration(node)) {
1246
1363
  const destructure = collectParamDestructure(node, caller, collectContext);
1247
1364
  if (destructure) delta.destructures.push(destructure);
1248
1365
  }
1249
- ts3.forEachChild(node, (child) => visit(child, caller));
1366
+ ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
1250
1367
  };
1251
- visit(sourceFile);
1252
- if (pending.length === 0) return void 0;
1253
- const analyzed = analyzeModuleFunctions(
1254
- sourceFile,
1255
- pending.map((entry) => ({
1256
- functionId: entry.collected.functionId,
1257
- name: entry.collected.name,
1258
- node: entry.node
1259
- }))
1260
- );
1261
- delta.derivationPlans = analyzed.map((fn) => fn.plan);
1262
- 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
+ }
1263
1372
  for (const [planIndex, entry] of pending.entries()) {
1264
- const analysis = analyzedById.get(entry.collected.functionId);
1265
- const omitValues = derivationMode === "selective" && analysis && canOmitCallValues(analysis, analyzed);
1266
- const valueFlag = omitValues ? ",1" : "";
1267
- const args2 = ts3.isArrowFunction(entry.node) ? arrowArguments(entry.node) : "arguments";
1268
- const asyncCallback = entry.node.modifiers?.some(
1269
- (modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword
1270
- ) ? "async " : "";
1271
- if (ts3.isBlock(entry.node.body)) {
1272
- edits.push({
1273
- position: entry.node.body.getStart(sourceFile) + 1,
1274
- text: `return ${runName}(${planIndex},${args2},${asyncCallback}()=>{`,
1275
- order: order++
1276
- });
1277
- edits.push({
1278
- position: entry.node.body.end - 1,
1279
- text: `}${valueFlag});`,
1280
- order: order++
1281
- });
1282
- } else {
1283
- edits.push({
1284
- position: entry.node.body.getStart(sourceFile),
1285
- text: `${runName}(${planIndex},${args2},${asyncCallback}()=>(`,
1286
- order: order++
1287
- });
1288
- edits.push({
1289
- position: entry.node.body.end,
1290
- text: `)${valueFlag})`,
1291
- order: order++
1292
- });
1293
- }
1294
- if (analysis) {
1295
- for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
1296
- 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);
1297
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
+ );
1298
1412
  }
1299
1413
  }
1300
1414
  const insertAt = insertPositionAfterPrologue(source, sourceFile);
@@ -1305,19 +1419,49 @@ var transformSource = (source, options) => {
1305
1419
  insertAt,
1306
1420
  modulePreamble(
1307
1421
  runName,
1308
- capName,
1309
1422
  plansName,
1310
- JSON.stringify(delta.functions.map((fn) => fn.plan))
1423
+ JSON.stringify(pending.map((entry) => entry.collected.plan))
1311
1424
  )
1312
1425
  ),
1313
- order: -1
1426
+ deleteCount: 0,
1427
+ depth: -1,
1428
+ width: 0,
1429
+ kind: "replace"
1314
1430
  });
1315
- edits.sort((left, right) => right.position - left.position || right.order - left.order);
1431
+ edits.sort(compareEdits);
1316
1432
  let transformed = source;
1317
1433
  for (const edit of edits) {
1318
- 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();
1319
1463
  }
1320
- return { code: transformed, delta };
1464
+ return { code: attachInlineSourceMap(source, transformed, options.filePath, edits), delta };
1321
1465
  };
1322
1466
 
1323
1467
  // src/auto-instrumentation/instrument-compiled-esm.ts
@@ -1357,11 +1501,14 @@ var instrumentCompiledDirectory = (options) => {
1357
1501
  continue;
1358
1502
  }
1359
1503
  const source = readFileSync2(filePath, "utf8");
1360
- 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
+ }
1361
1509
  const transformed = transformSource(source, {
1362
1510
  filePath,
1363
- mapLocation: createSourceMapLocationMapper(filePath, source),
1364
- ...options.derivationMode ? { derivationMode: options.derivationMode } : {}
1511
+ mapLocation: createSourceMapLocationMapper(filePath, source)
1365
1512
  });
1366
1513
  if (!transformed) continue;
1367
1514
  const outputPath = join2(outputDir, relative2(inputDir, filePath));
@@ -1376,6 +1523,11 @@ var instrumentCompiledDirectory = (options) => {
1376
1523
  if (manifest) {
1377
1524
  writeText(manifestPath, `${JSON.stringify(manifest, null, " ")}
1378
1525
  `);
1526
+ writeText(
1527
+ `${manifestPath.replace(/\.json$/i, "")}.hash`,
1528
+ `${hashInstrumentationManifest(manifest)}
1529
+ `
1530
+ );
1379
1531
  }
1380
1532
  return { transformedFiles, manifest, manifestPath };
1381
1533
  };
@@ -1387,15 +1539,7 @@ var sdkPackageScopeRootFrom = (instrumentEntryUrl) => resolve6(dirname3(fileURLT
1387
1539
 
1388
1540
  // src/instrument/build.ts
1389
1541
  var printUsage = () => {
1390
- console.error(
1391
- "Usage: rasputin-instrument <input-dir> [--out <output-dir>] [--manifest <path>] [--derivation-mode analysis|selective]"
1392
- );
1393
- };
1394
- var parseDerivationMode = (value) => {
1395
- if (value === void 0) return void 0;
1396
- if (value === "analysis" || value === "selective") return value;
1397
- console.error(`[rasputin] unknown --derivation-mode ${value} (expected analysis or selective)`);
1398
- process.exit(1);
1542
+ console.error("Usage: rasputin-instrument <input-dir> [--out <output-dir>] [--manifest <path>]");
1399
1543
  };
1400
1544
  var parseArgs = (argv) => {
1401
1545
  const inputDir = argv.find((argument) => !argument.startsWith("--"));
@@ -1406,8 +1550,7 @@ var parseArgs = (argv) => {
1406
1550
  return {
1407
1551
  inputDir,
1408
1552
  outputDir: option("--out"),
1409
- manifestPath: option("--manifest"),
1410
- derivationMode: parseDerivationMode(option("--derivation-mode"))
1553
+ manifestPath: option("--manifest")
1411
1554
  };
1412
1555
  };
1413
1556
  var args = parseArgs(process.argv.slice(2));
@@ -1419,8 +1562,7 @@ var result = instrumentCompiledDirectory({
1419
1562
  inputDir: args.inputDir,
1420
1563
  outputDir: args.outputDir,
1421
1564
  manifestPath: args.manifestPath,
1422
- excludedRoots: [sdkPackageScopeRootFrom(import.meta.url)],
1423
- ...args.derivationMode ? { derivationMode: args.derivationMode } : {}
1565
+ excludedRoots: [sdkPackageScopeRootFrom(import.meta.url)]
1424
1566
  });
1425
1567
  console.info(
1426
1568
  `[rasputin] instrumented ${result.transformedFiles.length} file${result.transformedFiles.length === 1 ? "" : "s"}`