@rasputin-ai/node 0.4.1 → 0.5.0-alpha.2

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 (37) hide show
  1. package/README.md +188 -162
  2. package/dist/auto-instrumentation/collect-instrumentation-delta.d.ts +2 -37
  3. package/dist/auto-instrumentation/collect-instrumentation-delta.d.ts.map +1 -1
  4. package/dist/auto-instrumentation/instrument-compiled-esm.d.ts +17 -0
  5. package/dist/auto-instrumentation/instrument-compiled-esm.d.ts.map +1 -0
  6. package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts +40 -0
  7. package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts.map +1 -0
  8. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts +4 -1
  9. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts.map +1 -1
  10. package/dist/auto-instrumentation/transform-source.d.ts +1 -1
  11. package/dist/auto-instrumentation/transform-source.d.ts.map +1 -1
  12. package/dist/auto-instrumentation/warn-production-runtime-transform.d.ts +8 -0
  13. package/dist/auto-instrumentation/warn-production-runtime-transform.d.ts.map +1 -0
  14. package/dist/execution-recorder/automatic-runtime.d.ts +19 -10
  15. package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
  16. package/dist/execution-recorder/call-aware-event-buffer.d.ts +17 -3
  17. package/dist/execution-recorder/call-aware-event-buffer.d.ts.map +1 -1
  18. package/dist/execution-recorder/execution-recorder-types.d.ts +19 -0
  19. package/dist/execution-recorder/execution-recorder-types.d.ts.map +1 -1
  20. package/dist/execution-recorder/execution-recorder.d.ts.map +1 -1
  21. package/dist/execution-recorder/function-plan.d.ts +26 -0
  22. package/dist/execution-recorder/function-plan.d.ts.map +1 -0
  23. package/dist/execution-recorder/recorder-memory-budget.d.ts +7 -1
  24. package/dist/execution-recorder/recorder-memory-budget.d.ts.map +1 -1
  25. package/dist/execution-recorder/safe-serialize.d.ts +7 -0
  26. package/dist/execution-recorder/safe-serialize.d.ts.map +1 -1
  27. package/dist/index.js +546 -294
  28. package/dist/instrument/build.d.ts +2 -0
  29. package/dist/instrument/build.d.ts.map +1 -0
  30. package/dist/instrument/build.js +707 -0
  31. package/dist/instrument/bun.js +55 -21
  32. package/dist/instrument/node.js +55 -21
  33. package/dist/rasputin-init.d.ts +5 -5
  34. package/dist/rasputin-init.d.ts.map +1 -1
  35. package/dist/sdk-meta.d.ts +1 -1
  36. package/dist/sdk-meta.d.ts.map +1 -1
  37. package/package.json +10 -2
@@ -0,0 +1,707 @@
1
+ // src/auto-instrumentation/instrument-compiled-esm.ts
2
+ import { mkdirSync, readdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
3
+ import { dirname as dirname2, join as join2, relative as relative2, resolve as resolve5 } from "node:path";
4
+
5
+ // src/auto-instrumentation/instrumentation-manifest-registry.ts
6
+ import { createHash } from "node:crypto";
7
+ import { resolve } from "node:path";
8
+ import {
9
+ INSTRUMENTATION_MANIFEST_SCHEMA_VERSION,
10
+ sourceLocatorFromFile as sourceLocatorFromFile2
11
+ } from "@rasputin-ai/core";
12
+
13
+ // src/execution-recorder/function-plan.ts
14
+ import {
15
+ runtimeFunctionId,
16
+ sourceLocatorFromFile
17
+ } from "@rasputin-ai/core";
18
+ var encodeFunctionPlan = (source) => {
19
+ const locator = sourceLocatorFromFile(source.filePath);
20
+ const definition = {
21
+ name: source.name,
22
+ ...locator ? { source: { ...locator, line: source.line, column: source.column } } : {}
23
+ };
24
+ return [
25
+ runtimeFunctionId(definition),
26
+ source.name,
27
+ locator?.packageName ?? null,
28
+ locator?.packageRelativePath ?? null,
29
+ source.line,
30
+ source.column
31
+ ];
32
+ };
33
+ var definitionFromEncodedPlan = (plan) => {
34
+ const [functionId, name, packageName, packageRelativePath, line, column] = plan;
35
+ const source = packageRelativePath ? {
36
+ ...packageName ? { packageName } : {},
37
+ packageRelativePath,
38
+ ...line == null ? {} : { line },
39
+ ...column == null ? {} : { column }
40
+ } : void 0;
41
+ return {
42
+ functionId,
43
+ name,
44
+ ...source ? { source } : {}
45
+ };
46
+ };
47
+
48
+ // src/auto-instrumentation/instrumentation-manifest-registry.ts
49
+ var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v2";
50
+ var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
51
+ var getRegistry = () => {
52
+ const existing = Reflect.get(globalThis, registrySymbol);
53
+ if (existing?.__rasputinManifestRegistry && existing.files instanceof Map && typeof existing.generation === "number" && typeof existing.uploadedGeneration === "number") {
54
+ return existing;
55
+ }
56
+ const registry = {
57
+ __rasputinManifestRegistry: true,
58
+ files: /* @__PURE__ */ new Map(),
59
+ generation: 0,
60
+ uploadedGeneration: 0
61
+ };
62
+ Reflect.set(globalThis, registrySymbol, registry);
63
+ return registry;
64
+ };
65
+ var sourceAt = (filePath, line, column) => {
66
+ const source = sourceLocatorFromFile2(filePath);
67
+ return source ? { ...source, line, column } : void 0;
68
+ };
69
+ var stableId = (kind, ...parts) => `${kind}_${createHash("sha256").update(parts.join("\0")).digest("base64url").slice(0, 24)}`;
70
+ var recordInstrumentationManifestDelta = (filePath, delta) => {
71
+ const registry = getRegistry();
72
+ registry.files.set(resolve(filePath), delta);
73
+ registry.generation += 1;
74
+ registry.onDirty?.();
75
+ };
76
+ var snapshotInstrumentationManifest = (options = {}) => {
77
+ const functions = [];
78
+ const callSites = [];
79
+ const destructures = [];
80
+ for (const delta of getRegistry().files.values()) {
81
+ for (const fn of delta.functions) {
82
+ const definition = definitionFromEncodedPlan(fn.plan);
83
+ if (!definition.source) continue;
84
+ functions.push({
85
+ functionId: definition.functionId,
86
+ source: definition.source,
87
+ name: fn.name,
88
+ startLine: fn.startLine,
89
+ startColumn: fn.startColumn,
90
+ endLine: fn.endLine,
91
+ endColumn: fn.endColumn,
92
+ params: fn.params
93
+ });
94
+ }
95
+ for (const site of delta.callSites) {
96
+ const source = sourceAt(site.filePath, site.line, site.column);
97
+ if (!source) continue;
98
+ callSites.push({
99
+ id: stableId(
100
+ "cs",
101
+ site.callerFunctionId,
102
+ source.packageName ?? "",
103
+ source.packageRelativePath,
104
+ site.line,
105
+ site.column
106
+ ),
107
+ callerFunctionId: site.callerFunctionId,
108
+ calleeName: site.calleeName,
109
+ calleeText: site.calleeText,
110
+ source,
111
+ line: site.line,
112
+ column: site.column,
113
+ endLine: site.endLine,
114
+ endColumn: site.endColumn,
115
+ resultBinding: site.resultBinding,
116
+ argIdentifiers: site.argIdentifiers
117
+ });
118
+ }
119
+ for (const destructure of delta.destructures) {
120
+ const source = sourceAt(destructure.filePath, destructure.line, destructure.column);
121
+ if (!source) continue;
122
+ destructures.push({
123
+ callerFunctionId: destructure.callerFunctionId,
124
+ sourceParam: destructure.sourceParam,
125
+ source,
126
+ line: destructure.line,
127
+ column: destructure.column,
128
+ binding: destructure.binding
129
+ });
130
+ }
131
+ }
132
+ const registry = getRegistry();
133
+ if (registry.prebuilt && registry.files.size === 0) {
134
+ const prebuilt = registry.prebuilt;
135
+ if (!options.sourceRoot && !options.sourceRoots) return prebuilt;
136
+ return {
137
+ ...prebuilt,
138
+ sourceRoots: {
139
+ ...options.sourceRoot ? { default: options.sourceRoot } : {},
140
+ packages: { ...options.sourceRoots ?? {} }
141
+ }
142
+ };
143
+ }
144
+ if (functions.length === 0 && callSites.length === 0 && destructures.length === 0) {
145
+ return void 0;
146
+ }
147
+ const packages = { ...options.sourceRoots ?? {} };
148
+ return {
149
+ schema_version: INSTRUMENTATION_MANIFEST_SCHEMA_VERSION,
150
+ ...options.sourceRoot || Object.keys(packages).length > 0 ? {
151
+ sourceRoots: {
152
+ ...options.sourceRoot ? { default: options.sourceRoot } : {},
153
+ packages
154
+ }
155
+ } : {},
156
+ functions,
157
+ callSites,
158
+ destructures
159
+ };
160
+ };
161
+ var resetInstrumentationManifestRegistry = () => {
162
+ Reflect.deleteProperty(globalThis, registrySymbol);
163
+ };
164
+
165
+ // src/auto-instrumentation/source-classification.ts
166
+ import { isAbsolute, relative, resolve as resolve2, sep } from "node:path";
167
+ var sourceExtension = /\.[cm]?[jt]sx?$/i;
168
+ var alwaysExcludedDirectory = /(^|[\\/])(node_modules|coverage|generated|\.git|\.next|\.svelte-kit|\.output|\.vercel|\.rasputin|\.turbo|\.yarn|\.pnpm|\.bun)([\\/]|$)/;
169
+ var buildOutputDirectory = /(^|[\\/])(dist|build|out)([\\/]|$)/;
170
+ var matches = (pattern, value) => {
171
+ pattern.lastIndex = 0;
172
+ const result2 = pattern.test(value);
173
+ pattern.lastIndex = 0;
174
+ return result2;
175
+ };
176
+ var isWithinRoot = (filePath, root) => {
177
+ const fromRoot = relative(resolve2(root), filePath);
178
+ return fromRoot === "" || !fromRoot.startsWith(`..${sep}`) && fromRoot !== ".." && !isAbsolute(fromRoot);
179
+ };
180
+ var isApplicationSource = (filePath, options = {}) => {
181
+ const absolutePath = resolve2(filePath);
182
+ if (!sourceExtension.test(absolutePath) || alwaysExcludedDirectory.test(absolutePath))
183
+ return false;
184
+ if (options.includeBuildOutput !== true && buildOutputDirectory.test(absolutePath)) return false;
185
+ if (options.excludedRoots?.some((root) => isWithinRoot(absolutePath, root))) return false;
186
+ if (options.exclude && matches(options.exclude, absolutePath)) return false;
187
+ if (options.include && !matches(options.include, absolutePath)) return false;
188
+ if (options.rootDir) {
189
+ if (!isWithinRoot(absolutePath, options.rootDir)) return false;
190
+ }
191
+ return true;
192
+ };
193
+
194
+ // src/auto-instrumentation/source-map-locations.ts
195
+ import { readFileSync } from "node:fs";
196
+ import { dirname, isAbsolute as isAbsolute2, join, resolve as resolve3 } from "node:path";
197
+ import { fileURLToPath, pathToFileURL } from "node:url";
198
+ import { LEAST_UPPER_BOUND, originalPositionFor, TraceMap } from "@jridgewell/trace-mapping";
199
+ var sourceMapComment = /(?:\/\/[#@][ \t]*sourceMappingURL=([^\s'"]+)|\/\*[#@][ \t]*sourceMappingURL=([^\s*'"]+)[ \t]*\*\/)\s*$/;
200
+ var decodeDataUrl = (url) => {
201
+ const base64 = /^data:application\/json(?:;charset=[^;,]+)?;base64,(.+)$/i.exec(url);
202
+ if (base64?.[1]) return Buffer.from(base64[1], "base64").toString("utf8");
203
+ const raw = /^data:application\/json(?:;charset=[^;,]+)?,(.+)$/i.exec(url);
204
+ return raw?.[1] ? decodeURIComponent(raw[1]) : void 0;
205
+ };
206
+ var sourceMapFor = (generatedPath, source) => {
207
+ const match = sourceMapComment.exec(source);
208
+ const reference = match?.[1] ?? match?.[2];
209
+ if (reference?.startsWith("data:")) {
210
+ const payload = decodeDataUrl(reference);
211
+ return payload ? { payload, mapUrl: pathToFileURL(generatedPath).href } : void 0;
212
+ }
213
+ const mapPath = reference ? resolve3(dirname(generatedPath), reference) : `${generatedPath}.map`;
214
+ return {
215
+ payload: readFileSync(mapPath, "utf8"),
216
+ mapUrl: pathToFileURL(mapPath).href
217
+ };
218
+ };
219
+ var toFilePath = (source, generatedPath) => {
220
+ if (source.startsWith("file:")) return fileURLToPath(source);
221
+ return isAbsolute2(source) ? source : join(dirname(generatedPath), source);
222
+ };
223
+ var createSourceMapLocationMapper = (generatedPath, source) => {
224
+ try {
225
+ const sourceMap = sourceMapFor(generatedPath, source);
226
+ if (!sourceMap) return void 0;
227
+ const traceMap = new TraceMap(sourceMap.payload, sourceMap.mapUrl);
228
+ return (generated) => {
229
+ let original = originalPositionFor(traceMap, {
230
+ line: generated.line,
231
+ column: Math.max(0, generated.column - 1)
232
+ });
233
+ if (!original.source || original.line == null) {
234
+ original = originalPositionFor(traceMap, {
235
+ line: generated.line,
236
+ column: 0,
237
+ bias: LEAST_UPPER_BOUND
238
+ });
239
+ }
240
+ if (!original.source || original.line == null) return void 0;
241
+ return {
242
+ filePath: toFilePath(original.source, generatedPath),
243
+ line: original.line,
244
+ column: (original.column ?? 0) + 1
245
+ };
246
+ };
247
+ } catch {
248
+ return void 0;
249
+ }
250
+ };
251
+
252
+ // src/auto-instrumentation/transform-source.ts
253
+ import { resolve as resolve4 } from "node:path";
254
+ import ts2 from "typescript";
255
+
256
+ // src/execution-recorder/automatic-runtime.ts
257
+ var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
258
+ var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
259
+
260
+ // src/auto-instrumentation/collect-instrumentation-delta.ts
261
+ import ts from "typescript";
262
+ var paramBindingFromName = (name, sourceFile) => {
263
+ if (ts.isIdentifier(name)) return { kind: "identifier", name: name.text };
264
+ if (ts.isArrayBindingPattern(name)) {
265
+ const elements = name.elements.map(
266
+ (element) => {
267
+ if (ts.isOmittedExpression(element)) return { kind: "omitted" };
268
+ const binding = paramBindingFromName(element.name, sourceFile);
269
+ if (element.dotDotDotToken) return { kind: "rest", binding };
270
+ return binding;
271
+ }
272
+ );
273
+ return { kind: "array", elements };
274
+ }
275
+ const properties = name.elements.map((element) => {
276
+ const binding = paramBindingFromName(element.name, sourceFile);
277
+ const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
278
+ if (element.dotDotDotToken) {
279
+ const rest = { kind: "rest", binding };
280
+ return { key, binding: rest };
281
+ }
282
+ return { key, binding };
283
+ });
284
+ return { kind: "object", properties };
285
+ };
286
+ var collectFunctionParams = (node, sourceFile) => {
287
+ const params = [];
288
+ for (const parameter of node.parameters) {
289
+ if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
290
+ const binding = paramBindingFromName(parameter.name, sourceFile);
291
+ params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
292
+ }
293
+ return { params, paramNames: identifierNames(params) };
294
+ };
295
+ var identifierNames = (bindings) => {
296
+ const names = /* @__PURE__ */ new Set();
297
+ const walk = (node) => {
298
+ if (node.kind === "identifier") {
299
+ names.add(node.name);
300
+ return;
301
+ }
302
+ if (node.kind === "rest") {
303
+ walk(node.binding);
304
+ return;
305
+ }
306
+ if (node.kind === "object") {
307
+ for (const property of node.properties) walk(property.binding);
308
+ return;
309
+ }
310
+ if (node.kind === "array") {
311
+ for (const element of node.elements) {
312
+ if (element.kind !== "omitted") walk(element);
313
+ }
314
+ }
315
+ };
316
+ for (const item of bindings) walk(item);
317
+ return names;
318
+ };
319
+ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
320
+ const generated = sourceFile.getLineAndCharacterOfPosition(position);
321
+ const raw = {
322
+ filePath: absolutePath,
323
+ line: generated.line + 1,
324
+ column: generated.character + 1
325
+ };
326
+ return mapLocation ? mapLocation(raw) : raw;
327
+ };
328
+ var unwrapExpression = (expression) => {
329
+ let current = expression;
330
+ while (ts.isParenthesizedExpression(current) || ts.isNonNullExpression(current)) {
331
+ current = current.expression;
332
+ }
333
+ if (ts.isAwaitExpression(current)) return unwrapExpression(current.expression);
334
+ return current;
335
+ };
336
+ var calleeFromExpression = (expression, sourceFile) => {
337
+ const current = unwrapExpression(expression);
338
+ if (ts.isIdentifier(current)) {
339
+ return { calleeName: current.text, calleeText: current.text };
340
+ }
341
+ if (ts.isPropertyAccessExpression(current) && ts.isIdentifier(current.name)) {
342
+ return {
343
+ calleeName: current.name.text,
344
+ calleeText: current.getText(sourceFile).replace(/\s+/g, "")
345
+ };
346
+ }
347
+ return null;
348
+ };
349
+ var resultBindingForCall = (call) => {
350
+ let node = call.parent;
351
+ if (ts.isAwaitExpression(node)) node = node.parent;
352
+ if (!ts.isVariableDeclaration(node)) return null;
353
+ if (!ts.isIdentifier(node.name)) return null;
354
+ const initializer = node.initializer;
355
+ if (!initializer) return null;
356
+ const inner = unwrapExpression(initializer);
357
+ if (inner !== call) return null;
358
+ return node.name.text;
359
+ };
360
+ var argIdentifiersForCall = (call) => {
361
+ const identifiers = [];
362
+ call.arguments?.forEach((argument, index) => {
363
+ if (ts.isIdentifier(argument)) {
364
+ identifiers.push({ index, kind: "identifier", name: argument.text });
365
+ return;
366
+ }
367
+ if (!ts.isObjectLiteralExpression(argument)) return;
368
+ const properties = [];
369
+ for (const property of argument.properties) {
370
+ if (ts.isShorthandPropertyAssignment(property)) {
371
+ properties.push({ key: property.name.text, name: property.name.text });
372
+ continue;
373
+ }
374
+ if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && ts.isIdentifier(property.initializer)) {
375
+ properties.push({ key: property.name.text, name: property.initializer.text });
376
+ }
377
+ }
378
+ if (properties.length > 0) {
379
+ identifiers.push({ index, kind: "object", properties });
380
+ }
381
+ });
382
+ return identifiers;
383
+ };
384
+ var collectCallSite = (call, caller, context) => {
385
+ const callee = calleeFromExpression(call.expression, context.sourceFile);
386
+ if (!callee) return null;
387
+ const start = mappedLocation(
388
+ context.sourceFile,
389
+ call.getStart(context.sourceFile),
390
+ context.absolutePath,
391
+ context.mapLocation
392
+ );
393
+ const end = mappedLocation(
394
+ context.sourceFile,
395
+ call.end,
396
+ context.absolutePath,
397
+ context.mapLocation
398
+ );
399
+ if (!start || !end) return null;
400
+ return {
401
+ callerFunctionId: caller.functionId,
402
+ calleeName: callee.calleeName,
403
+ calleeText: callee.calleeText,
404
+ filePath: start.filePath,
405
+ line: start.line,
406
+ column: start.column,
407
+ endLine: end.line,
408
+ endColumn: end.column,
409
+ resultBinding: resultBindingForCall(call),
410
+ argIdentifiers: argIdentifiersForCall(call)
411
+ };
412
+ };
413
+ var collectParamDestructure = (declaration, caller, context) => {
414
+ if (!declaration.initializer || !ts.isIdentifier(declaration.initializer)) return null;
415
+ if (!ts.isObjectBindingPattern(declaration.name) && !ts.isArrayBindingPattern(declaration.name)) {
416
+ return null;
417
+ }
418
+ const sourceParam = declaration.initializer.text;
419
+ if (!caller.paramNames.has(sourceParam)) return null;
420
+ const start = mappedLocation(
421
+ context.sourceFile,
422
+ declaration.getStart(context.sourceFile),
423
+ context.absolutePath,
424
+ context.mapLocation
425
+ );
426
+ if (!start) return null;
427
+ return {
428
+ callerFunctionId: caller.functionId,
429
+ sourceParam,
430
+ filePath: start.filePath,
431
+ line: start.line,
432
+ column: start.column,
433
+ binding: paramBindingFromName(declaration.name, context.sourceFile)
434
+ };
435
+ };
436
+
437
+ // src/auto-instrumentation/transform-source.ts
438
+ var scriptKindFor = (filePath) => {
439
+ if (/\.tsx$/i.test(filePath)) return ts2.ScriptKind.TSX;
440
+ if (/\.jsx$/i.test(filePath)) return ts2.ScriptKind.JSX;
441
+ if (/\.[cm]?ts$/i.test(filePath)) return ts2.ScriptKind.TS;
442
+ return ts2.ScriptKind.JS;
443
+ };
444
+ var functionName = (node, sourceFile) => {
445
+ if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
446
+ const parent = node.parent;
447
+ if (ts2.isVariableDeclaration(parent) && ts2.isIdentifier(parent.name)) return parent.name.text;
448
+ if (ts2.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
449
+ if (ts2.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
450
+ const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
451
+ return `anonymous@${location.line + 1}:${location.character + 1}`;
452
+ };
453
+ var functionLocationPosition = (node, sourceFile) => {
454
+ if (node.name) return node.name.getStart(sourceFile);
455
+ const parent = node.parent;
456
+ if ((ts2.isVariableDeclaration(parent) || ts2.isPropertyAssignment(parent)) && parent.name) {
457
+ return parent.name.getStart(sourceFile);
458
+ }
459
+ return node.getStart(sourceFile);
460
+ };
461
+ var bindingValue = (name, sourceFile) => {
462
+ if (ts2.isIdentifier(name)) return name.text;
463
+ if (ts2.isArrayBindingPattern(name)) {
464
+ const values = name.elements.map((element) => {
465
+ if (ts2.isOmittedExpression(element)) return "";
466
+ const value = bindingValue(element.name, sourceFile);
467
+ return element.dotDotDotToken ? `...${value}` : value;
468
+ });
469
+ return `[${values.join(",")}]`;
470
+ }
471
+ const properties = name.elements.map((element) => {
472
+ const value = bindingValue(element.name, sourceFile);
473
+ if (element.dotDotDotToken) return `...${value}`;
474
+ if (element.propertyName) return `${element.propertyName.getText(sourceFile)}:${value}`;
475
+ return value;
476
+ });
477
+ return `{${properties.join(",")}}`;
478
+ };
479
+ var arrowArguments = (node) => {
480
+ const values = node.parameters.flatMap((parameter) => {
481
+ if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
482
+ const value = bindingValue(parameter.name, node.getSourceFile());
483
+ return [parameter.dotDotDotToken ? `...${value}` : value];
484
+ });
485
+ return `[${values.join(",")}]`;
486
+ };
487
+ var isSupportedFunction = (node) => {
488
+ if (!ts2.isFunctionDeclaration(node) && !ts2.isFunctionExpression(node) && !ts2.isArrowFunction(node) && !ts2.isMethodDeclaration(node) && !ts2.isGetAccessorDeclaration(node) && !ts2.isSetAccessorDeclaration(node)) {
489
+ return false;
490
+ }
491
+ if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
492
+ return true;
493
+ };
494
+ var uniqueName = (source, base) => {
495
+ let name = base;
496
+ while (source.includes(name)) name += "_";
497
+ return name;
498
+ };
499
+ var modulePreamble = (runName, plansName, plansJson) => `const ${plansName}=${plansJson};const ${runName}=((g,k)=>{const r=g[k]??(g[k]={runtimes:[],modules:[],register(p){const d={run:(i,a,c)=>c()};const m={plans:p,cell:d};this.modules.push(m);const n=this.runtimes.at(-1);if(n)n.bind(m);return(i,a,c)=>d.run(i,a,c)}});return r.register(${plansName})})(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));`;
500
+ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
501
+ const start = mappedLocation(
502
+ sourceFile,
503
+ functionLocationPosition(node, sourceFile),
504
+ absolutePath,
505
+ mapLocation
506
+ );
507
+ if (!start) return void 0;
508
+ const end = mappedLocation(sourceFile, node.end, absolutePath, mapLocation) ?? start;
509
+ const name = functionName(node, sourceFile).replace(/@\d+:\d+$/, "");
510
+ const { params, paramNames } = collectFunctionParams(node, sourceFile);
511
+ const plan = encodeFunctionPlan({
512
+ filePath: start.filePath,
513
+ name,
514
+ line: start.line,
515
+ column: start.column
516
+ });
517
+ return {
518
+ plan,
519
+ functionId: plan[0],
520
+ filePath: start.filePath,
521
+ name,
522
+ startLine: start.line,
523
+ startColumn: start.column,
524
+ endLine: end.line,
525
+ endColumn: end.column,
526
+ params,
527
+ paramNames
528
+ };
529
+ };
530
+ var transformSource = (source, options) => {
531
+ if (source.includes("@rasputin-ignore-file")) return void 0;
532
+ const sourceFile = ts2.createSourceFile(
533
+ options.filePath,
534
+ source,
535
+ ts2.ScriptTarget.Latest,
536
+ true,
537
+ scriptKindFor(options.filePath)
538
+ );
539
+ const runName = uniqueName(source, "__rasputinRun__");
540
+ const plansName = uniqueName(source, "__rasputinPlans__");
541
+ const absolutePath = resolve4(options.filePath);
542
+ const edits = [];
543
+ let order = 0;
544
+ const delta = {
545
+ functions: [],
546
+ callSites: [],
547
+ destructures: []
548
+ };
549
+ const collectContext = {
550
+ sourceFile,
551
+ mapLocation: options.mapLocation,
552
+ absolutePath
553
+ };
554
+ const visit = (node, caller) => {
555
+ if (isSupportedFunction(node)) {
556
+ const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
557
+ if (!collected) {
558
+ ts2.forEachChild(node, (child) => visit(child, caller));
559
+ return;
560
+ }
561
+ const planIndex = delta.functions.length;
562
+ delta.functions.push(collected);
563
+ const args2 = ts2.isArrowFunction(node) ? arrowArguments(node) : "arguments";
564
+ const asyncCallback = node.modifiers?.some(
565
+ (modifier) => modifier.kind === ts2.SyntaxKind.AsyncKeyword
566
+ ) ? "async " : "";
567
+ if (ts2.isBlock(node.body)) {
568
+ edits.push({
569
+ position: node.body.getStart(sourceFile) + 1,
570
+ text: `return ${runName}(${planIndex},${args2},${asyncCallback}()=>{`,
571
+ order: order++
572
+ });
573
+ edits.push({ position: node.body.end - 1, text: "});", order: order++ });
574
+ } else {
575
+ edits.push({
576
+ position: node.body.getStart(sourceFile),
577
+ text: `${runName}(${planIndex},${args2},${asyncCallback}()=>(`,
578
+ order: order++
579
+ });
580
+ edits.push({ position: node.body.end, text: "))", order: order++ });
581
+ }
582
+ ts2.forEachChild(node, (child) => visit(child, collected));
583
+ return;
584
+ }
585
+ if (caller && (ts2.isCallExpression(node) || ts2.isNewExpression(node))) {
586
+ const site = collectCallSite(node, caller, collectContext);
587
+ if (site) delta.callSites.push(site);
588
+ }
589
+ if (caller && ts2.isVariableDeclaration(node)) {
590
+ const destructure = collectParamDestructure(node, caller, collectContext);
591
+ if (destructure) delta.destructures.push(destructure);
592
+ }
593
+ ts2.forEachChild(node, (child) => visit(child, caller));
594
+ };
595
+ visit(sourceFile);
596
+ if (edits.length === 0) return void 0;
597
+ const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
598
+ edits.push({
599
+ position: shebangEnd,
600
+ text: modulePreamble(runName, plansName, JSON.stringify(delta.functions.map((fn) => fn.plan))),
601
+ order: -1
602
+ });
603
+ edits.sort((left, right) => right.position - left.position || right.order - left.order);
604
+ let transformed = source;
605
+ for (const edit of edits) {
606
+ transformed = `${transformed.slice(0, edit.position)}${edit.text}${transformed.slice(edit.position)}`;
607
+ }
608
+ return { code: transformed, delta };
609
+ };
610
+
611
+ // src/auto-instrumentation/instrument-compiled-esm.ts
612
+ var compiledJavaScript = /\.[cm]?js$/i;
613
+ var walkCompiledFiles = (directory) => {
614
+ const files = [];
615
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
616
+ if (entry.name === "node_modules" || entry.name === ".rasputin" || entry.name === ".git") {
617
+ continue;
618
+ }
619
+ const path = join2(directory, entry.name);
620
+ if (entry.isDirectory()) {
621
+ files.push(...walkCompiledFiles(path));
622
+ continue;
623
+ }
624
+ if (compiledJavaScript.test(entry.name)) files.push(path);
625
+ }
626
+ return files;
627
+ };
628
+ var writeText = (path, contents) => {
629
+ mkdirSync(dirname2(path), { recursive: true });
630
+ writeFileSync(path, contents);
631
+ };
632
+ var instrumentCompiledDirectory = (options) => {
633
+ const inputDir = resolve5(options.inputDir);
634
+ const outputDir = resolve5(options.outputDir ?? inputDir);
635
+ const manifestPath = resolve5(
636
+ options.manifestPath ?? join2(outputDir, ".rasputin", "instrumentation-manifest.json")
637
+ );
638
+ const transformedFiles = [];
639
+ resetInstrumentationManifestRegistry();
640
+ for (const filePath of walkCompiledFiles(inputDir)) {
641
+ if (!isApplicationSource(filePath, {
642
+ includeBuildOutput: true,
643
+ excludedRoots: options.excludedRoots
644
+ })) {
645
+ continue;
646
+ }
647
+ const source = readFileSync2(filePath, "utf8");
648
+ if (source.includes("__rasputinRun__") || source.includes("__rasputinPlans__")) continue;
649
+ const transformed = transformSource(source, {
650
+ filePath,
651
+ mapLocation: createSourceMapLocationMapper(filePath, source)
652
+ });
653
+ if (!transformed) continue;
654
+ const outputPath = join2(outputDir, relative2(inputDir, filePath));
655
+ writeText(outputPath, transformed.code);
656
+ recordInstrumentationManifestDelta(filePath, transformed.delta);
657
+ transformedFiles.push(outputPath);
658
+ }
659
+ const manifest = snapshotInstrumentationManifest({
660
+ sourceRoot: options.sourceRoot,
661
+ sourceRoots: options.sourceRoots
662
+ });
663
+ if (manifest) {
664
+ writeText(manifestPath, `${JSON.stringify(manifest, null, " ")}
665
+ `);
666
+ }
667
+ return { transformedFiles, manifest, manifestPath };
668
+ };
669
+
670
+ // src/auto-instrumentation/sdk-source-boundary.ts
671
+ import { dirname as dirname3, resolve as resolve6 } from "node:path";
672
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
673
+ var sdkPackageScopeRootFrom = (instrumentEntryUrl) => resolve6(dirname3(fileURLToPath2(instrumentEntryUrl)), "../../..");
674
+
675
+ // src/instrument/build.ts
676
+ var printUsage = () => {
677
+ console.error("Usage: rasputin-instrument <input-dir> [--out <output-dir>] [--manifest <path>]");
678
+ };
679
+ var parseArgs = (argv) => {
680
+ const inputDir = argv.find((argument) => !argument.startsWith("--"));
681
+ const option = (name) => {
682
+ const index = argv.indexOf(name);
683
+ return index >= 0 ? argv[index + 1] : void 0;
684
+ };
685
+ return {
686
+ inputDir,
687
+ outputDir: option("--out"),
688
+ manifestPath: option("--manifest")
689
+ };
690
+ };
691
+ var args = parseArgs(process.argv.slice(2));
692
+ if (!args.inputDir) {
693
+ printUsage();
694
+ process.exit(1);
695
+ }
696
+ var result = instrumentCompiledDirectory({
697
+ inputDir: args.inputDir,
698
+ outputDir: args.outputDir,
699
+ manifestPath: args.manifestPath,
700
+ excludedRoots: [sdkPackageScopeRootFrom(import.meta.url)]
701
+ });
702
+ console.info(
703
+ `[rasputin] instrumented ${result.transformedFiles.length} file${result.transformedFiles.length === 1 ? "" : "s"}`
704
+ );
705
+ if (result.manifest) {
706
+ console.info(`[rasputin] wrote ${result.manifestPath}`);
707
+ }