@rasputin-ai/node 0.4.0 → 0.5.0-alpha.1
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.
- package/README.md +185 -159
- package/dist/auto-instrumentation/collect-instrumentation-delta.d.ts +2 -37
- package/dist/auto-instrumentation/collect-instrumentation-delta.d.ts.map +1 -1
- package/dist/auto-instrumentation/instrument-compiled-esm.d.ts +17 -0
- package/dist/auto-instrumentation/instrument-compiled-esm.d.ts.map +1 -0
- package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts +40 -0
- package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts.map +1 -0
- package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts +4 -1
- package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts.map +1 -1
- package/dist/auto-instrumentation/transform-source.d.ts +1 -1
- package/dist/auto-instrumentation/transform-source.d.ts.map +1 -1
- package/dist/execution-recorder/automatic-runtime.d.ts +12 -10
- package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
- package/dist/execution-recorder/call-aware-event-buffer.d.ts +2 -1
- package/dist/execution-recorder/call-aware-event-buffer.d.ts.map +1 -1
- package/dist/execution-recorder/execution-recorder-types.d.ts +20 -0
- package/dist/execution-recorder/execution-recorder-types.d.ts.map +1 -1
- package/dist/execution-recorder/execution-recorder.d.ts.map +1 -1
- package/dist/execution-recorder/function-plan.d.ts +26 -0
- package/dist/execution-recorder/function-plan.d.ts.map +1 -0
- package/dist/execution-recorder/recorder-memory-budget.d.ts +7 -1
- package/dist/execution-recorder/recorder-memory-budget.d.ts.map +1 -1
- package/dist/execution-recorder/safe-serialize.d.ts +7 -0
- package/dist/execution-recorder/safe-serialize.d.ts.map +1 -1
- package/dist/index.js +450 -257
- package/dist/instrument/build.d.ts +2 -0
- package/dist/instrument/build.d.ts.map +1 -0
- package/dist/instrument/build.js +707 -0
- package/dist/instrument/bun.js +41 -21
- package/dist/instrument/node.js +41 -21
- package/dist/rasputin-init.d.ts +5 -5
- package/dist/rasputin-init.d.ts.map +1 -1
- package/dist/sdk-meta.d.ts +1 -1
- package/dist/sdk-meta.d.ts.map +1 -1
- package/package.json +10 -2
package/dist/index.js
CHANGED
|
@@ -104,76 +104,38 @@ var runtimeLabel = () => {
|
|
|
104
104
|
|
|
105
105
|
// src/auto-instrumentation/instrumentation-manifest-registry.ts
|
|
106
106
|
import { createHash } from "node:crypto";
|
|
107
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
107
108
|
import {
|
|
108
109
|
INSTRUMENTATION_MANIFEST_SCHEMA_VERSION,
|
|
109
110
|
sourceLocatorFromFile as sourceLocatorFromFile2
|
|
110
111
|
} from "@rasputin-ai/core";
|
|
111
112
|
|
|
112
|
-
// src/execution-recorder/
|
|
113
|
+
// src/execution-recorder/function-plan.ts
|
|
113
114
|
import {
|
|
114
115
|
runtimeFunctionId,
|
|
115
116
|
sourceLocatorFromFile
|
|
116
117
|
} from "@rasputin-ai/core";
|
|
117
|
-
var
|
|
118
|
-
var
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
};
|
|
131
|
-
return { ...definition, functionId: runtimeFunctionId(definition) };
|
|
132
|
-
} catch {
|
|
133
|
-
return void 0;
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
var getRegistry = () => {
|
|
137
|
-
const existing = Reflect.get(globalThis, runtimeSymbol);
|
|
138
|
-
if (existing?.__rasputinRuntimeRegistry && Array.isArray(existing.runtimes)) {
|
|
139
|
-
return existing;
|
|
140
|
-
}
|
|
141
|
-
const runtimes = [];
|
|
142
|
-
const registry = {
|
|
143
|
-
__rasputinRuntimeRegistry: true,
|
|
144
|
-
runtimes,
|
|
145
|
-
run: (sourceToken, args, callback) => {
|
|
146
|
-
const runtime = runtimes.at(-1);
|
|
147
|
-
return runtime ? runtime.run(sourceToken, args, callback) : callback();
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
Reflect.set(globalThis, runtimeSymbol, registry);
|
|
151
|
-
return registry;
|
|
152
|
-
};
|
|
153
|
-
var installAutomaticExecutionRuntime = (execution) => {
|
|
154
|
-
const registry = getRegistry();
|
|
155
|
-
const definitions = /* @__PURE__ */ new Map();
|
|
156
|
-
const runtime = {
|
|
157
|
-
run: (sourceToken, args, callback) => {
|
|
158
|
-
let definition = definitions.get(sourceToken);
|
|
159
|
-
if (!definitions.has(sourceToken)) {
|
|
160
|
-
definition = decodeAutomaticFunctionSource(sourceToken);
|
|
161
|
-
definitions.set(sourceToken, definition);
|
|
162
|
-
}
|
|
163
|
-
return definition ? execution.runFunction(definition, args, callback) : callback();
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
registry.runtimes.push(runtime);
|
|
167
|
-
return () => {
|
|
168
|
-
const index = registry.runtimes.lastIndexOf(runtime);
|
|
169
|
-
if (index >= 0) registry.runtimes.splice(index, 1);
|
|
118
|
+
var EVENT_PAIR_BASE_ESTIMATED_BYTES = 512;
|
|
119
|
+
var definitionFromEncodedPlan = (plan) => {
|
|
120
|
+
const [functionId, name, packageName, packageRelativePath, line, column] = plan;
|
|
121
|
+
const source = packageRelativePath ? {
|
|
122
|
+
...packageName ? { packageName } : {},
|
|
123
|
+
packageRelativePath,
|
|
124
|
+
...line == null ? {} : { line },
|
|
125
|
+
...column == null ? {} : { column }
|
|
126
|
+
} : void 0;
|
|
127
|
+
return {
|
|
128
|
+
functionId,
|
|
129
|
+
name,
|
|
130
|
+
...source ? { source } : {}
|
|
170
131
|
};
|
|
171
132
|
};
|
|
133
|
+
var estimatedCallBytesFor = (functionId) => EVENT_PAIR_BASE_ESTIMATED_BYTES + Buffer.byteLength(functionId, "utf8") * 2;
|
|
172
134
|
|
|
173
135
|
// src/auto-instrumentation/instrumentation-manifest-registry.ts
|
|
174
136
|
var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v2";
|
|
175
137
|
var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
|
|
176
|
-
var
|
|
138
|
+
var getRegistry = () => {
|
|
177
139
|
const existing = Reflect.get(globalThis, registrySymbol);
|
|
178
140
|
if (existing?.__rasputinManifestRegistry && existing.files instanceof Map && typeof existing.generation === "number" && typeof existing.uploadedGeneration === "number") {
|
|
179
141
|
return existing;
|
|
@@ -192,20 +154,46 @@ var sourceAt = (filePath, line, column) => {
|
|
|
192
154
|
return source ? { ...source, line, column } : void 0;
|
|
193
155
|
};
|
|
194
156
|
var stableId = (kind, ...parts) => `${kind}_${createHash("sha256").update(parts.join("\0")).digest("base64url").slice(0, 24)}`;
|
|
157
|
+
var recordPrebuiltInstrumentationManifest = (manifest) => {
|
|
158
|
+
const registry = getRegistry();
|
|
159
|
+
registry.prebuilt = manifest;
|
|
160
|
+
registry.generation += 1;
|
|
161
|
+
registry.onDirty?.();
|
|
162
|
+
};
|
|
163
|
+
var loadInstrumentationManifestFile = (path) => {
|
|
164
|
+
try {
|
|
165
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
166
|
+
if (parsed.schema_version !== INSTRUMENTATION_MANIFEST_SCHEMA_VERSION) return false;
|
|
167
|
+
if (!Array.isArray(parsed.functions)) return false;
|
|
168
|
+
recordPrebuiltInstrumentationManifest(parsed);
|
|
169
|
+
return true;
|
|
170
|
+
} catch {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
var loadInstrumentationManifestIfPresent = (path) => {
|
|
175
|
+
if (!path || !existsSync(path)) return;
|
|
176
|
+
loadInstrumentationManifestFile(path);
|
|
177
|
+
};
|
|
195
178
|
var isInstrumentationManifestDirty = () => {
|
|
196
|
-
const registry =
|
|
197
|
-
return registry.generation !== registry.uploadedGeneration && registry.files.size > 0;
|
|
179
|
+
const registry = getRegistry();
|
|
180
|
+
return registry.generation !== registry.uploadedGeneration && (registry.files.size > 0 || registry.prebuilt !== void 0);
|
|
181
|
+
};
|
|
182
|
+
var instrumentationManifestGeneration = () => getRegistry().generation;
|
|
183
|
+
var instrumentationManifestModuleCount = () => {
|
|
184
|
+
const registry = getRegistry();
|
|
185
|
+
if (registry.files.size > 0) return registry.files.size;
|
|
186
|
+
if (!registry.prebuilt) return 0;
|
|
187
|
+
return new Set(registry.prebuilt.functions.map((fn) => fn.source.packageRelativePath)).size;
|
|
198
188
|
};
|
|
199
|
-
var instrumentationManifestGeneration = () => getRegistry2().generation;
|
|
200
|
-
var instrumentationManifestModuleCount = () => getRegistry2().files.size;
|
|
201
189
|
var snapshotInstrumentationManifest = (options = {}) => {
|
|
202
190
|
const functions = [];
|
|
203
191
|
const callSites = [];
|
|
204
192
|
const destructures = [];
|
|
205
|
-
for (const delta of
|
|
193
|
+
for (const delta of getRegistry().files.values()) {
|
|
206
194
|
for (const fn of delta.functions) {
|
|
207
|
-
const definition =
|
|
208
|
-
if (!definition
|
|
195
|
+
const definition = definitionFromEncodedPlan(fn.plan);
|
|
196
|
+
if (!definition.source) continue;
|
|
209
197
|
functions.push({
|
|
210
198
|
functionId: definition.functionId,
|
|
211
199
|
source: definition.source,
|
|
@@ -218,19 +206,18 @@ var snapshotInstrumentationManifest = (options = {}) => {
|
|
|
218
206
|
});
|
|
219
207
|
}
|
|
220
208
|
for (const site of delta.callSites) {
|
|
221
|
-
const caller = decodeAutomaticFunctionSource(site.callerSourceToken);
|
|
222
209
|
const source = sourceAt(site.filePath, site.line, site.column);
|
|
223
|
-
if (!
|
|
210
|
+
if (!source) continue;
|
|
224
211
|
callSites.push({
|
|
225
212
|
id: stableId(
|
|
226
213
|
"cs",
|
|
227
|
-
|
|
214
|
+
site.callerFunctionId,
|
|
228
215
|
source.packageName ?? "",
|
|
229
216
|
source.packageRelativePath,
|
|
230
217
|
site.line,
|
|
231
218
|
site.column
|
|
232
219
|
),
|
|
233
|
-
callerFunctionId:
|
|
220
|
+
callerFunctionId: site.callerFunctionId,
|
|
234
221
|
calleeName: site.calleeName,
|
|
235
222
|
calleeText: site.calleeText,
|
|
236
223
|
source,
|
|
@@ -243,11 +230,10 @@ var snapshotInstrumentationManifest = (options = {}) => {
|
|
|
243
230
|
});
|
|
244
231
|
}
|
|
245
232
|
for (const destructure of delta.destructures) {
|
|
246
|
-
const caller = decodeAutomaticFunctionSource(destructure.callerSourceToken);
|
|
247
233
|
const source = sourceAt(destructure.filePath, destructure.line, destructure.column);
|
|
248
|
-
if (!
|
|
234
|
+
if (!source) continue;
|
|
249
235
|
destructures.push({
|
|
250
|
-
callerFunctionId:
|
|
236
|
+
callerFunctionId: destructure.callerFunctionId,
|
|
251
237
|
sourceParam: destructure.sourceParam,
|
|
252
238
|
source,
|
|
253
239
|
line: destructure.line,
|
|
@@ -256,6 +242,18 @@ var snapshotInstrumentationManifest = (options = {}) => {
|
|
|
256
242
|
});
|
|
257
243
|
}
|
|
258
244
|
}
|
|
245
|
+
const registry = getRegistry();
|
|
246
|
+
if (registry.prebuilt && registry.files.size === 0) {
|
|
247
|
+
const prebuilt = registry.prebuilt;
|
|
248
|
+
if (!options.sourceRoot && !options.sourceRoots) return prebuilt;
|
|
249
|
+
return {
|
|
250
|
+
...prebuilt,
|
|
251
|
+
sourceRoots: {
|
|
252
|
+
...options.sourceRoot ? { default: options.sourceRoot } : {},
|
|
253
|
+
packages: { ...options.sourceRoots ?? {} }
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
259
257
|
if (functions.length === 0 && callSites.length === 0 && destructures.length === 0) {
|
|
260
258
|
return void 0;
|
|
261
259
|
}
|
|
@@ -274,11 +272,11 @@ var snapshotInstrumentationManifest = (options = {}) => {
|
|
|
274
272
|
};
|
|
275
273
|
};
|
|
276
274
|
var markInstrumentationManifestUploaded = (generation) => {
|
|
277
|
-
const registry =
|
|
275
|
+
const registry = getRegistry();
|
|
278
276
|
if (registry.generation === generation) registry.uploadedGeneration = generation;
|
|
279
277
|
};
|
|
280
278
|
var setInstrumentationManifestOnDirty = (onDirty) => {
|
|
281
|
-
|
|
279
|
+
getRegistry().onDirty = onDirty;
|
|
282
280
|
};
|
|
283
281
|
|
|
284
282
|
// src/auto-instrumentation/schedule-instrumentation-manifest-upload.ts
|
|
@@ -395,6 +393,7 @@ var CallAwareEventBuffer = class {
|
|
|
395
393
|
}
|
|
396
394
|
items = [];
|
|
397
395
|
openCallIds = /* @__PURE__ */ new Set();
|
|
396
|
+
retainedSummaries = /* @__PURE__ */ new WeakSet();
|
|
398
397
|
dropped = 0;
|
|
399
398
|
canStartCall() {
|
|
400
399
|
return this.items.length + this.openCallIds.size + 2 <= this.capacity;
|
|
@@ -417,8 +416,12 @@ var CallAwareEventBuffer = class {
|
|
|
417
416
|
return false;
|
|
418
417
|
}
|
|
419
418
|
this.items.push(event);
|
|
419
|
+
if (event.type === "function_calls_suppressed") this.retainedSummaries.add(event);
|
|
420
420
|
return true;
|
|
421
421
|
}
|
|
422
|
+
hasSummary(event) {
|
|
423
|
+
return this.retainedSummaries.has(event);
|
|
424
|
+
}
|
|
422
425
|
forceCompletedCall(enter, terminal) {
|
|
423
426
|
while (this.items.length + this.openCallIds.size + 2 > this.capacity) {
|
|
424
427
|
if (!this.evictCompletedLeafOrSummary()) {
|
|
@@ -432,14 +435,13 @@ var CallAwareEventBuffer = class {
|
|
|
432
435
|
values() {
|
|
433
436
|
return [...this.items];
|
|
434
437
|
}
|
|
435
|
-
contains(target) {
|
|
436
|
-
return this.items.includes(target);
|
|
437
|
-
}
|
|
438
438
|
evictCompletedLeafOrSummary() {
|
|
439
439
|
const summaryIndex = this.items.findIndex(
|
|
440
440
|
(event) => event.type === "function_calls_suppressed"
|
|
441
441
|
);
|
|
442
442
|
if (summaryIndex >= 0) {
|
|
443
|
+
const summary = this.items[summaryIndex];
|
|
444
|
+
if (summary) this.retainedSummaries.delete(summary);
|
|
443
445
|
this.items.splice(summaryIndex, 1);
|
|
444
446
|
this.dropped++;
|
|
445
447
|
return true;
|
|
@@ -469,9 +471,7 @@ var CallAwareEventBuffer = class {
|
|
|
469
471
|
};
|
|
470
472
|
|
|
471
473
|
// src/execution-recorder/recorder-memory-budget.ts
|
|
472
|
-
import { Buffer } from "node:buffer";
|
|
473
474
|
var EXECUTION_BASE_ESTIMATED_BYTES = 1024;
|
|
474
|
-
var EVENT_PAIR_BASE_ESTIMATED_BYTES = 512;
|
|
475
475
|
var SUMMARY_EVENT_BASE_ESTIMATED_BYTES = 256;
|
|
476
476
|
var VALUE_HEAP_ESTIMATE_MULTIPLIER = 2;
|
|
477
477
|
var MEMORY_BUDGET_VALUE_MARKER = {
|
|
@@ -525,12 +525,11 @@ var RecorderMemoryBudget = class {
|
|
|
525
525
|
}
|
|
526
526
|
return serialized.value;
|
|
527
527
|
}
|
|
528
|
-
reserveCall(allocation,
|
|
528
|
+
reserveCall(allocation, estimatedBytes) {
|
|
529
529
|
if (allocation.mode === "metadata-only") {
|
|
530
530
|
this.dropEvents(allocation, 2);
|
|
531
531
|
return false;
|
|
532
532
|
}
|
|
533
|
-
const estimatedBytes = EVENT_PAIR_BASE_ESTIMATED_BYTES + Buffer.byteLength(functionId, "utf8") * 2;
|
|
534
533
|
if (this.reserve(allocation, estimatedBytes)) return true;
|
|
535
534
|
this.markPressure(allocation, "metadata-only");
|
|
536
535
|
this.dropEvents(allocation, 2);
|
|
@@ -590,7 +589,7 @@ var RecorderMemoryBudget = class {
|
|
|
590
589
|
|
|
591
590
|
// src/execution-recorder/safe-serialize.ts
|
|
592
591
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
593
|
-
var DEFAULT_REDACT_KEYS =
|
|
592
|
+
var DEFAULT_REDACT_KEYS = [
|
|
594
593
|
"password",
|
|
595
594
|
"token",
|
|
596
595
|
"authorization",
|
|
@@ -598,7 +597,7 @@ var DEFAULT_REDACT_KEYS = /* @__PURE__ */ new Set([
|
|
|
598
597
|
"secret",
|
|
599
598
|
"apikey",
|
|
600
599
|
"creditcard"
|
|
601
|
-
]
|
|
600
|
+
];
|
|
602
601
|
var defaults = {
|
|
603
602
|
maxDepth: 3,
|
|
604
603
|
maxObjectKeys: 30,
|
|
@@ -615,135 +614,155 @@ var typeMarker = (type, value) => ({
|
|
|
615
614
|
__rasputin_type: type,
|
|
616
615
|
...value === void 0 ? {} : { value }
|
|
617
616
|
});
|
|
618
|
-
var
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
for (const [key, child] of Object.entries(item)) {
|
|
626
|
-
item[key] = redactKeys.has(key.toLowerCase()) ? "[RASPUTIN_REDACTED]" : redact(child, redactKeys, seen);
|
|
627
|
-
}
|
|
628
|
-
return item;
|
|
629
|
-
};
|
|
630
|
-
var errorValue = (error, depth, seen, limits) => ({
|
|
631
|
-
__rasputin_type: "Error",
|
|
632
|
-
name: error.name,
|
|
633
|
-
message: serializeInner(error.message, depth + 1, seen, limits),
|
|
634
|
-
...error.stack ? { stack: serializeInner(error.stack, depth + 1, seen, limits) } : {}
|
|
617
|
+
var REDACTED = "[RASPUTIN_REDACTED]";
|
|
618
|
+
var compileLimits = (options) => ({
|
|
619
|
+
maxDepth: options.maxDepth ?? defaults.maxDepth,
|
|
620
|
+
maxObjectKeys: options.maxObjectKeys ?? defaults.maxObjectKeys,
|
|
621
|
+
maxArrayElements: options.maxArrayElements ?? defaults.maxArrayElements,
|
|
622
|
+
maxStringLength: options.maxStringLength ?? defaults.maxStringLength,
|
|
623
|
+
maxSerializedValueBytes: options.maxSerializedValueBytes ?? defaults.maxSerializedValueBytes
|
|
635
624
|
});
|
|
636
|
-
var
|
|
637
|
-
if (
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
if (seen.has(value)) return typeMarker("circular");
|
|
652
|
-
seen.add(value);
|
|
653
|
-
try {
|
|
654
|
-
if (value instanceof Error) return errorValue(value, depth, seen, limits);
|
|
655
|
-
if (value instanceof Date) return typeMarker("Date", value.toISOString());
|
|
656
|
-
if (Buffer2.isBuffer(value)) {
|
|
657
|
-
return typeMarker("Buffer", {
|
|
658
|
-
byteLength: value.byteLength,
|
|
659
|
-
base64: value.subarray(0, limits.maxStringLength).toString("base64"),
|
|
660
|
-
...value.byteLength > limits.maxStringLength ? { __rasputin_truncated: true } : {}
|
|
625
|
+
var compileRedactKeys = (options) => {
|
|
626
|
+
if (!options.redactKeys?.length) return new Set(DEFAULT_REDACT_KEYS);
|
|
627
|
+
return /* @__PURE__ */ new Set([...DEFAULT_REDACT_KEYS, ...options.redactKeys.map((key) => key.toLowerCase())]);
|
|
628
|
+
};
|
|
629
|
+
var createSafeSerializer = (options = {}) => {
|
|
630
|
+
const limits = compileLimits(options);
|
|
631
|
+
const redactKeys = compileRedactKeys(options);
|
|
632
|
+
const serializeInner = (value, depth, seen, metrics) => {
|
|
633
|
+
metrics.visitedNodes++;
|
|
634
|
+
if (value === null || typeof value === "boolean" || typeof value === "number") return value;
|
|
635
|
+
if (typeof value === "string") {
|
|
636
|
+
if (value.length <= limits.maxStringLength) return value;
|
|
637
|
+
return truncation("max_string_length", {
|
|
638
|
+
__original_length: value.length,
|
|
639
|
+
value: value.slice(0, limits.maxStringLength)
|
|
661
640
|
});
|
|
662
641
|
}
|
|
663
|
-
if (value
|
|
664
|
-
if (value
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
642
|
+
if (typeof value === "undefined") return typeMarker("undefined");
|
|
643
|
+
if (typeof value === "bigint") return typeMarker("bigint", value.toString());
|
|
644
|
+
if (typeof value === "symbol") return typeMarker("symbol", String(value));
|
|
645
|
+
if (typeof value === "function") return typeMarker("function", value.name || "anonymous");
|
|
646
|
+
if (depth >= limits.maxDepth) return truncation("max_depth");
|
|
647
|
+
if (typeof value !== "object") return typeMarker(typeof value);
|
|
648
|
+
const tracking = seen ?? /* @__PURE__ */ new WeakSet();
|
|
649
|
+
if (tracking.has(value)) return typeMarker("circular");
|
|
650
|
+
tracking.add(value);
|
|
651
|
+
try {
|
|
652
|
+
if (value instanceof Error) {
|
|
653
|
+
return {
|
|
654
|
+
__rasputin_type: "Error",
|
|
655
|
+
name: value.name,
|
|
656
|
+
message: serializeInner(value.message, depth + 1, tracking, metrics),
|
|
657
|
+
...value.stack ? { stack: serializeInner(value.stack, depth + 1, tracking, metrics) } : {}
|
|
658
|
+
};
|
|
672
659
|
}
|
|
673
|
-
return
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
660
|
+
if (value instanceof Date) return typeMarker("Date", value.toISOString());
|
|
661
|
+
if (Buffer2.isBuffer(value)) {
|
|
662
|
+
return typeMarker("Buffer", {
|
|
663
|
+
byteLength: value.byteLength,
|
|
664
|
+
base64: value.subarray(0, limits.maxStringLength).toString("base64"),
|
|
665
|
+
...value.byteLength > limits.maxStringLength ? { __rasputin_truncated: true } : {}
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
if (value instanceof Promise) return typeMarker("Promise");
|
|
669
|
+
if (value instanceof Map) {
|
|
670
|
+
const entries = [];
|
|
671
|
+
for (const entry of value.entries()) {
|
|
672
|
+
if (entries.length >= limits.maxArrayElements) break;
|
|
673
|
+
entries.push([
|
|
674
|
+
serializeInner(entry[0], depth + 1, tracking, metrics),
|
|
675
|
+
serializeInner(entry[1], depth + 1, tracking, metrics)
|
|
676
|
+
]);
|
|
677
|
+
}
|
|
678
|
+
return {
|
|
679
|
+
__rasputin_type: "Map",
|
|
680
|
+
entries,
|
|
681
|
+
...value.size > limits.maxArrayElements ? truncation("max_array_elements", { __original_length: value.size }) : {}
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
if (value instanceof Set) {
|
|
685
|
+
const items = [];
|
|
686
|
+
for (const item of value.values()) {
|
|
687
|
+
if (items.length >= limits.maxArrayElements) break;
|
|
688
|
+
items.push(serializeInner(item, depth + 1, tracking, metrics));
|
|
689
|
+
}
|
|
690
|
+
return {
|
|
691
|
+
__rasputin_type: "Set",
|
|
692
|
+
values: items,
|
|
693
|
+
...value.size > limits.maxArrayElements ? truncation("max_array_elements", { __original_length: value.size }) : {}
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
if (Array.isArray(value)) {
|
|
697
|
+
const maxItems = value.length > limits.maxArrayElements ? limits.maxArrayElements - 1 : value.length;
|
|
698
|
+
const items = [];
|
|
699
|
+
for (let index = 0; index < maxItems; index++) {
|
|
700
|
+
items.push(serializeInner(value[index], depth + 1, tracking, metrics));
|
|
701
|
+
}
|
|
702
|
+
if (value.length > limits.maxArrayElements) {
|
|
703
|
+
items.push(truncation("max_array_elements", { __original_length: value.length }));
|
|
704
|
+
}
|
|
705
|
+
return items;
|
|
706
|
+
}
|
|
707
|
+
const prototype = Object.getPrototypeOf(value);
|
|
708
|
+
const serialized = {};
|
|
709
|
+
if (prototype && prototype !== Object.prototype) {
|
|
710
|
+
serialized.__rasputin_type = prototype.constructor?.name ?? "instance";
|
|
711
|
+
}
|
|
712
|
+
const keys = Object.keys(value);
|
|
713
|
+
const maxKeys = keys.length > limits.maxObjectKeys ? limits.maxObjectKeys - 1 : keys.length;
|
|
714
|
+
for (let index = 0; index < maxKeys; index++) {
|
|
715
|
+
const key = keys[index];
|
|
716
|
+
if (key === void 0) continue;
|
|
717
|
+
if (redactKeys.has(key.toLowerCase())) {
|
|
718
|
+
serialized[key] = REDACTED;
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
722
|
+
serialized[key] = descriptor && "value" in descriptor ? serializeInner(descriptor.value, depth + 1, tracking, metrics) : typeMarker("accessor");
|
|
723
|
+
}
|
|
724
|
+
if (keys.length > limits.maxObjectKeys) {
|
|
725
|
+
Object.assign(
|
|
726
|
+
serialized,
|
|
727
|
+
truncation("max_object_keys", { __original_length: keys.length })
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
return serialized;
|
|
731
|
+
} catch (error) {
|
|
732
|
+
return typeMarker("unserializable", error instanceof Error ? error.message : void 0);
|
|
733
|
+
} finally {
|
|
734
|
+
tracking.delete(value);
|
|
678
735
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
736
|
+
};
|
|
737
|
+
const serializeWithBytes = (value) => {
|
|
738
|
+
const metrics = { visitedNodes: 0 };
|
|
739
|
+
try {
|
|
740
|
+
const serialized = serializeInner(value, 0, void 0, metrics);
|
|
741
|
+
const bytes = Buffer2.byteLength(JSON.stringify(serialized), "utf8");
|
|
742
|
+
if (bytes <= limits.maxSerializedValueBytes) {
|
|
743
|
+
return { value: serialized, bytes, visitedNodes: metrics.visitedNodes };
|
|
684
744
|
}
|
|
745
|
+
const bounded = truncation("max_serialized_value_bytes", { __serialized_bytes: bytes });
|
|
685
746
|
return {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
747
|
+
value: bounded,
|
|
748
|
+
bytes: Buffer2.byteLength(JSON.stringify(bounded), "utf8"),
|
|
749
|
+
visitedNodes: metrics.visitedNodes
|
|
750
|
+
};
|
|
751
|
+
} catch {
|
|
752
|
+
const failed = typeMarker("unserializable");
|
|
753
|
+
return {
|
|
754
|
+
value: failed,
|
|
755
|
+
bytes: Buffer2.byteLength(JSON.stringify(failed), "utf8"),
|
|
756
|
+
visitedNodes: metrics.visitedNodes
|
|
689
757
|
};
|
|
690
758
|
}
|
|
691
|
-
if (Array.isArray(value)) {
|
|
692
|
-
const maxItems = value.length > limits.maxArrayElements ? limits.maxArrayElements - 1 : value.length;
|
|
693
|
-
const items = value.slice(0, maxItems).map((item) => serializeInner(item, depth + 1, seen, limits));
|
|
694
|
-
if (value.length > limits.maxArrayElements) {
|
|
695
|
-
items.push(truncation("max_array_elements", { __original_length: value.length }));
|
|
696
|
-
}
|
|
697
|
-
return items;
|
|
698
|
-
}
|
|
699
|
-
const prototype = Object.getPrototypeOf(value);
|
|
700
|
-
const serialized = {};
|
|
701
|
-
if (prototype && prototype !== Object.prototype) {
|
|
702
|
-
serialized.__rasputin_type = prototype.constructor?.name ?? "instance";
|
|
703
|
-
}
|
|
704
|
-
const keys = Object.keys(value);
|
|
705
|
-
const maxKeys = keys.length > limits.maxObjectKeys ? limits.maxObjectKeys - 1 : keys.length;
|
|
706
|
-
for (const key of keys.slice(0, maxKeys)) {
|
|
707
|
-
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
708
|
-
serialized[key] = descriptor && "value" in descriptor ? serializeInner(descriptor.value, depth + 1, seen, limits) : typeMarker("accessor");
|
|
709
|
-
}
|
|
710
|
-
if (keys.length > limits.maxObjectKeys) {
|
|
711
|
-
Object.assign(serialized, truncation("max_object_keys", { __original_length: keys.length }));
|
|
712
|
-
}
|
|
713
|
-
return serialized;
|
|
714
|
-
} catch (error) {
|
|
715
|
-
return typeMarker("unserializable", error instanceof Error ? error.message : void 0);
|
|
716
|
-
} finally {
|
|
717
|
-
seen.delete(value);
|
|
718
|
-
}
|
|
719
|
-
};
|
|
720
|
-
var safeSerializeWithBytes = (value, options = {}) => {
|
|
721
|
-
const limits = {
|
|
722
|
-
maxDepth: options.maxDepth ?? defaults.maxDepth,
|
|
723
|
-
maxObjectKeys: options.maxObjectKeys ?? defaults.maxObjectKeys,
|
|
724
|
-
maxArrayElements: options.maxArrayElements ?? defaults.maxArrayElements,
|
|
725
|
-
maxStringLength: options.maxStringLength ?? defaults.maxStringLength,
|
|
726
|
-
maxSerializedValueBytes: options.maxSerializedValueBytes ?? defaults.maxSerializedValueBytes
|
|
727
759
|
};
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
]);
|
|
733
|
-
const serialized = serializeInner(value, 0, /* @__PURE__ */ new WeakSet(), limits);
|
|
734
|
-
const redacted = redact(serialized, redactKeys, /* @__PURE__ */ new WeakSet());
|
|
735
|
-
const bytes = Buffer2.byteLength(JSON.stringify(redacted), "utf8");
|
|
736
|
-
const bounded = bytes <= limits.maxSerializedValueBytes ? redacted : truncation("max_serialized_value_bytes", { __serialized_bytes: bytes });
|
|
737
|
-
return {
|
|
738
|
-
value: bounded,
|
|
739
|
-
bytes: bounded === redacted ? bytes : Buffer2.byteLength(JSON.stringify(bounded), "utf8")
|
|
740
|
-
};
|
|
741
|
-
} catch {
|
|
742
|
-
const value2 = typeMarker("unserializable");
|
|
743
|
-
return { value: value2, bytes: Buffer2.byteLength(JSON.stringify(value2), "utf8") };
|
|
744
|
-
}
|
|
760
|
+
return {
|
|
761
|
+
serialize: (value) => serializeWithBytes(value).value,
|
|
762
|
+
serializeWithBytes
|
|
763
|
+
};
|
|
745
764
|
};
|
|
746
|
-
var
|
|
765
|
+
var defaultSerializer = createSafeSerializer();
|
|
747
766
|
|
|
748
767
|
// src/execution-recorder/source-exclusions.ts
|
|
749
768
|
var sourcePathFrom = (definition) => definition.source?.packageRelativePath.replaceAll("\\", "/") ?? definition.name;
|
|
@@ -896,6 +915,30 @@ var observeRecordedCallback = (recording, recorderStartedAtNs, callback, onRetur
|
|
|
896
915
|
recordExitWork(callbackEndedAtNs);
|
|
897
916
|
return result;
|
|
898
917
|
};
|
|
918
|
+
var emptyStats = {
|
|
919
|
+
functionCallsObserved: 0,
|
|
920
|
+
functionCallsCaptured: 0,
|
|
921
|
+
functionCallsExcluded: 0,
|
|
922
|
+
serializedValues: 0,
|
|
923
|
+
serializedValueNodes: 0,
|
|
924
|
+
serializedValueBytes: 0,
|
|
925
|
+
errorSnapshots: 0,
|
|
926
|
+
successfulExecutionsDiscarded: 0,
|
|
927
|
+
truncatedEvents: 0,
|
|
928
|
+
repeatedCallsSuppressed: 0,
|
|
929
|
+
activeExecutions: 0,
|
|
930
|
+
peakActiveExecutions: 0,
|
|
931
|
+
memoryPressureExecutions: 0,
|
|
932
|
+
memoryPressureDegradations: 0,
|
|
933
|
+
valuesDroppedByMemoryBudget: 0,
|
|
934
|
+
eventsDroppedByMemoryBudget: 0,
|
|
935
|
+
partialErrorSnapshots: 0,
|
|
936
|
+
measuredExecutions: 0,
|
|
937
|
+
totalInlineWallTimeMs: 0,
|
|
938
|
+
maxInlineWallTimeMs: 0,
|
|
939
|
+
activeEstimatedBytes: 0,
|
|
940
|
+
peakActiveEstimatedBytes: 0
|
|
941
|
+
};
|
|
899
942
|
var noOpExecution = {
|
|
900
943
|
createScope: () => ({
|
|
901
944
|
run: (callback) => callback(),
|
|
@@ -906,22 +949,10 @@ var noOpExecution = {
|
|
|
906
949
|
run: (_metadata, callback) => callback(),
|
|
907
950
|
getErrorState: () => void 0,
|
|
908
951
|
runFunction: (_functionId, _args, callback) => callback(),
|
|
952
|
+
runFunctionPlans: (_plans, _planIndex, _args, callback) => callback(),
|
|
953
|
+
runPreparedFunction: (_plan, _args, callback) => callback(),
|
|
909
954
|
trace: (_functionId, fn) => fn,
|
|
910
|
-
getStats: () => ({
|
|
911
|
-
errorSnapshots: 0,
|
|
912
|
-
successfulExecutionsDiscarded: 0,
|
|
913
|
-
truncatedEvents: 0,
|
|
914
|
-
repeatedCallsSuppressed: 0,
|
|
915
|
-
activeExecutions: 0,
|
|
916
|
-
peakActiveExecutions: 0,
|
|
917
|
-
activeEstimatedBytes: 0,
|
|
918
|
-
peakActiveEstimatedBytes: 0,
|
|
919
|
-
memoryPressureExecutions: 0,
|
|
920
|
-
memoryPressureDegradations: 0,
|
|
921
|
-
valuesDroppedByMemoryBudget: 0,
|
|
922
|
-
eventsDroppedByMemoryBudget: 0,
|
|
923
|
-
partialErrorSnapshots: 0
|
|
924
|
-
})
|
|
955
|
+
getStats: () => ({ ...emptyStats })
|
|
925
956
|
};
|
|
926
957
|
var mergeExecutionMetadata = (target, metadata) => {
|
|
927
958
|
if (metadata.kind) target.kind = metadata.kind;
|
|
@@ -944,21 +975,83 @@ var createExecutionRecorder = (options) => {
|
|
|
944
975
|
maxActiveMemoryBytes,
|
|
945
976
|
options.maxSerializedValueBytes ?? 8 * 1024
|
|
946
977
|
);
|
|
978
|
+
const serializer = createSafeSerializer(options);
|
|
947
979
|
const excludesSource = createSourceExclusionMatcher(options.excludeSources);
|
|
948
980
|
let executionCounter = 0;
|
|
981
|
+
let nextRuntimePlanId = 1;
|
|
982
|
+
const internedPlans = /* @__PURE__ */ new Map();
|
|
983
|
+
const preparedModules = /* @__PURE__ */ new WeakMap();
|
|
949
984
|
const errorExecutions = /* @__PURE__ */ new WeakMap();
|
|
950
985
|
const stats = {
|
|
986
|
+
functionCallsObserved: 0,
|
|
987
|
+
functionCallsCaptured: 0,
|
|
988
|
+
functionCallsExcluded: 0,
|
|
989
|
+
serializedValues: 0,
|
|
990
|
+
serializedValueNodes: 0,
|
|
991
|
+
serializedValueBytes: 0,
|
|
951
992
|
errorSnapshots: 0,
|
|
952
993
|
successfulExecutionsDiscarded: 0,
|
|
953
994
|
truncatedEvents: 0,
|
|
954
995
|
repeatedCallsSuppressed: 0,
|
|
955
996
|
activeExecutions: 0,
|
|
956
997
|
peakActiveExecutions: 0,
|
|
957
|
-
partialErrorSnapshots: 0
|
|
998
|
+
partialErrorSnapshots: 0,
|
|
999
|
+
measuredExecutions: 0,
|
|
1000
|
+
totalInlineWallTimeMs: 0,
|
|
1001
|
+
maxInlineWallTimeMs: 0
|
|
1002
|
+
};
|
|
1003
|
+
const recordInlineTiming = (recording) => {
|
|
1004
|
+
const inlineWallTimeMs = roundedMilliseconds(recording.inlineRecorderTimeNs);
|
|
1005
|
+
stats.measuredExecutions++;
|
|
1006
|
+
stats.totalInlineWallTimeMs = Math.round((stats.totalInlineWallTimeMs + inlineWallTimeMs) * 1e4) / 1e4;
|
|
1007
|
+
stats.maxInlineWallTimeMs = Math.max(stats.maxInlineWallTimeMs, inlineWallTimeMs);
|
|
1008
|
+
};
|
|
1009
|
+
const serializeAndCount = (value) => {
|
|
1010
|
+
const serialized = serializer.serializeWithBytes(value);
|
|
1011
|
+
stats.serializedValues++;
|
|
1012
|
+
stats.serializedValueNodes += serialized.visitedNodes;
|
|
1013
|
+
stats.serializedValueBytes += serialized.bytes;
|
|
1014
|
+
return serialized;
|
|
1015
|
+
};
|
|
1016
|
+
const captureValue = (recording, value) => memoryBudget.captureValue(recording.memory, () => serializeAndCount(value));
|
|
1017
|
+
const preparePlan = (definition) => {
|
|
1018
|
+
const existing = internedPlans.get(definition.functionId);
|
|
1019
|
+
if (existing) return existing;
|
|
1020
|
+
const plan = {
|
|
1021
|
+
runtimePlanId: nextRuntimePlanId++,
|
|
1022
|
+
definition,
|
|
1023
|
+
functionId: definition.functionId,
|
|
1024
|
+
excluded: excludesSource(definition),
|
|
1025
|
+
estimatedCallBytes: estimatedCallBytesFor(definition.functionId)
|
|
1026
|
+
};
|
|
1027
|
+
internedPlans.set(definition.functionId, plan);
|
|
1028
|
+
return plan;
|
|
1029
|
+
};
|
|
1030
|
+
const preparedPlansFor = (plans) => {
|
|
1031
|
+
const cached = preparedModules.get(plans);
|
|
1032
|
+
if (cached) return cached;
|
|
1033
|
+
const prepared = plans.map((encoded) => {
|
|
1034
|
+
const definition = definitionFromEncodedPlan(encoded);
|
|
1035
|
+
return {
|
|
1036
|
+
runtimePlanId: nextRuntimePlanId++,
|
|
1037
|
+
definition,
|
|
1038
|
+
functionId: definition.functionId,
|
|
1039
|
+
excluded: excludesSource(definition),
|
|
1040
|
+
estimatedCallBytes: estimatedCallBytesFor(definition.functionId)
|
|
1041
|
+
};
|
|
1042
|
+
});
|
|
1043
|
+
preparedModules.set(plans, prepared);
|
|
1044
|
+
return prepared;
|
|
958
1045
|
};
|
|
959
|
-
const captureValue = (recording, value) => memoryBudget.captureValue(recording.memory, () => safeSerializeWithBytes(value, options));
|
|
960
1046
|
const stateFrom = (recording, endedAtNs = process.hrtime.bigint()) => {
|
|
961
1047
|
const recorderStartedAtNs = process.hrtime.bigint();
|
|
1048
|
+
for (const byParent of recording.repeatedCalls.values()) {
|
|
1049
|
+
for (const group of byParent.values()) {
|
|
1050
|
+
if (group.summary) {
|
|
1051
|
+
group.summary.totalDurationMs = roundedMilliseconds(group.totalDurationNs);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
962
1055
|
const events = recording.events.values().map((event) => event.type === "function_calls_suppressed" ? { ...event } : event);
|
|
963
1056
|
const referencedFunctionIds = new Set(
|
|
964
1057
|
events.flatMap(
|
|
@@ -1014,15 +1107,15 @@ var createExecutionRecorder = (options) => {
|
|
|
1014
1107
|
recording.events.append(event);
|
|
1015
1108
|
countDroppedEvents(recording, droppedBefore);
|
|
1016
1109
|
};
|
|
1017
|
-
const repeatedCallGroup = (recording,
|
|
1018
|
-
let byParent = recording.repeatedCalls.get(
|
|
1110
|
+
const repeatedCallGroup = (recording, runtimePlanId, parentCallId) => {
|
|
1111
|
+
let byParent = recording.repeatedCalls.get(runtimePlanId);
|
|
1019
1112
|
if (!byParent) {
|
|
1020
1113
|
byParent = /* @__PURE__ */ new Map();
|
|
1021
|
-
recording.repeatedCalls.set(
|
|
1114
|
+
recording.repeatedCalls.set(runtimePlanId, byParent);
|
|
1022
1115
|
}
|
|
1023
1116
|
let group = byParent.get(parentCallId);
|
|
1024
1117
|
if (!group) {
|
|
1025
|
-
group = { observedCalls: 0 };
|
|
1118
|
+
group = { observedCalls: 0, totalDurationNs: 0n };
|
|
1026
1119
|
byParent.set(parentCallId, group);
|
|
1027
1120
|
}
|
|
1028
1121
|
return group;
|
|
@@ -1040,14 +1133,14 @@ var createExecutionRecorder = (options) => {
|
|
|
1040
1133
|
return {
|
|
1041
1134
|
// Keep the bounded thrown value even after surrounding capture degrades. A partial
|
|
1042
1135
|
// snapshot without the exception itself would not be useful for investigation.
|
|
1043
|
-
value:
|
|
1136
|
+
value: serializeAndCount(error).value,
|
|
1044
1137
|
commit: () => {
|
|
1045
1138
|
if (key) recording.capturedErrors.set(key, callId);
|
|
1046
1139
|
}
|
|
1047
1140
|
};
|
|
1048
1141
|
};
|
|
1049
|
-
const captureCompletedThrow = (recording,
|
|
1050
|
-
if (!memoryBudget.reserveCall(recording.memory,
|
|
1142
|
+
const captureCompletedThrow = (recording, plan, args, parentCallId, startedAtNs, startedAtMs, error) => {
|
|
1143
|
+
if (!memoryBudget.reserveCall(recording.memory, plan.estimatedCallBytes)) {
|
|
1051
1144
|
rememberError(error, recording);
|
|
1052
1145
|
return;
|
|
1053
1146
|
}
|
|
@@ -1056,7 +1149,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1056
1149
|
const enter = {
|
|
1057
1150
|
type: "function_enter",
|
|
1058
1151
|
callId,
|
|
1059
|
-
functionId,
|
|
1152
|
+
functionId: plan.functionId,
|
|
1060
1153
|
timestampMs: startedAtMs,
|
|
1061
1154
|
args: Array.from(args, (argument) => captureValue(recording, argument)),
|
|
1062
1155
|
...parentCallId === void 0 ? {} : { parentCallId }
|
|
@@ -1070,13 +1163,17 @@ var createExecutionRecorder = (options) => {
|
|
|
1070
1163
|
};
|
|
1071
1164
|
const droppedBefore = recording.events.dropped;
|
|
1072
1165
|
const retained = recording.events.forceCompletedCall(enter, terminal);
|
|
1073
|
-
if (retained)
|
|
1166
|
+
if (retained) {
|
|
1167
|
+
stats.functionCallsCaptured++;
|
|
1168
|
+
capturedError.commit();
|
|
1169
|
+
}
|
|
1074
1170
|
countDroppedEvents(recording, droppedBefore);
|
|
1075
1171
|
rememberError(error, recording);
|
|
1076
1172
|
};
|
|
1077
|
-
const recordSuppressedCall = (recording, group, functionId, parentCallId, startedAtMs,
|
|
1173
|
+
const recordSuppressedCall = (recording, group, functionId, parentCallId, startedAtMs, startedAtNs, endedAtNs) => {
|
|
1078
1174
|
stats.repeatedCallsSuppressed++;
|
|
1079
|
-
|
|
1175
|
+
group.totalDurationNs += endedAtNs - startedAtNs;
|
|
1176
|
+
const endedAtMs = elapsedMs(recording.startedAtNs, endedAtNs);
|
|
1080
1177
|
if (!group.summary) {
|
|
1081
1178
|
group.summary = {
|
|
1082
1179
|
type: "function_calls_suppressed",
|
|
@@ -1084,7 +1181,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1084
1181
|
timestampMs: startedAtMs,
|
|
1085
1182
|
lastTimestampMs: endedAtMs,
|
|
1086
1183
|
suppressedCallCount: 1,
|
|
1087
|
-
totalDurationMs:
|
|
1184
|
+
totalDurationMs: 0,
|
|
1088
1185
|
...parentCallId === void 0 ? {} : { parentCallId }
|
|
1089
1186
|
};
|
|
1090
1187
|
appendStandaloneEvent(recording, group.summary);
|
|
@@ -1092,8 +1189,9 @@ var createExecutionRecorder = (options) => {
|
|
|
1092
1189
|
}
|
|
1093
1190
|
group.summary.lastTimestampMs = endedAtMs;
|
|
1094
1191
|
group.summary.suppressedCallCount++;
|
|
1095
|
-
|
|
1096
|
-
|
|
1192
|
+
if (!recording.events.hasSummary(group.summary)) {
|
|
1193
|
+
appendStandaloneEvent(recording, group.summary);
|
|
1194
|
+
}
|
|
1097
1195
|
};
|
|
1098
1196
|
const finish = (store, metadata) => {
|
|
1099
1197
|
const { recording } = store;
|
|
@@ -1106,6 +1204,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1106
1204
|
if (!recording.hasError) stats.successfulExecutionsDiscarded++;
|
|
1107
1205
|
recording.finishedAtNs = process.hrtime.bigint();
|
|
1108
1206
|
addRecorderTime(recording, recording.finishedAtNs - recorderStartedAtNs);
|
|
1207
|
+
recordInlineTiming(recording);
|
|
1109
1208
|
};
|
|
1110
1209
|
const createScope = (metadata) => {
|
|
1111
1210
|
const recorderStartedAtNs = process.hrtime.bigint();
|
|
@@ -1189,48 +1288,90 @@ var createExecutionRecorder = (options) => {
|
|
|
1189
1288
|
return void 0;
|
|
1190
1289
|
}
|
|
1191
1290
|
};
|
|
1192
|
-
const
|
|
1291
|
+
const runPreparedFunction = (plan, args, callback) => {
|
|
1193
1292
|
const store = storage.getStore();
|
|
1194
1293
|
if (!store || store.recording.finished) return callback();
|
|
1195
|
-
|
|
1196
|
-
const functionId = definition.functionId;
|
|
1294
|
+
stats.functionCallsObserved++;
|
|
1197
1295
|
const recorderStartedAtNs = process.hrtime.bigint();
|
|
1198
|
-
if (store.suppressCapture ||
|
|
1296
|
+
if (store.suppressCapture || plan.excluded) {
|
|
1297
|
+
stats.functionCallsExcluded++;
|
|
1199
1298
|
return storage.run({ ...store, suppressCapture: true }, () => {
|
|
1200
1299
|
addRecorderTime(store.recording, process.hrtime.bigint() - recorderStartedAtNs);
|
|
1201
1300
|
return callback();
|
|
1202
1301
|
});
|
|
1203
1302
|
}
|
|
1204
|
-
store.recording.functions.set(functionId, definition);
|
|
1205
1303
|
const parentCallId = store.currentCallId;
|
|
1206
|
-
const group = repeatedCallGroup(store.recording,
|
|
1304
|
+
const group = repeatedCallGroup(store.recording, plan.runtimePlanId, parentCallId);
|
|
1207
1305
|
group.observedCalls++;
|
|
1306
|
+
if (group.observedCalls === 1) {
|
|
1307
|
+
store.recording.functions.set(plan.functionId, plan.definition);
|
|
1308
|
+
}
|
|
1208
1309
|
if (group.observedCalls > maxCapturedCallsPerFunction) {
|
|
1209
1310
|
const startedAtNs2 = process.hrtime.bigint();
|
|
1210
1311
|
const startedAtMs = elapsedMs(store.recording.startedAtNs, startedAtNs2);
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1312
|
+
try {
|
|
1313
|
+
const result = callback();
|
|
1314
|
+
if (isPromiseLike(result)) {
|
|
1315
|
+
addRecorderTime(store.recording, startedAtNs2 - recorderStartedAtNs);
|
|
1316
|
+
return result.then(
|
|
1317
|
+
(value) => {
|
|
1318
|
+
const callbackEndedAtNs2 = process.hrtime.bigint();
|
|
1319
|
+
recordSuppressedCall(
|
|
1320
|
+
store.recording,
|
|
1321
|
+
group,
|
|
1322
|
+
plan.functionId,
|
|
1323
|
+
parentCallId,
|
|
1324
|
+
startedAtMs,
|
|
1325
|
+
startedAtNs2,
|
|
1326
|
+
callbackEndedAtNs2
|
|
1327
|
+
);
|
|
1328
|
+
addRecorderTime(store.recording, process.hrtime.bigint() - callbackEndedAtNs2);
|
|
1329
|
+
return value;
|
|
1330
|
+
},
|
|
1331
|
+
(error) => {
|
|
1332
|
+
const callbackEndedAtNs2 = process.hrtime.bigint();
|
|
1333
|
+
captureCompletedThrow(
|
|
1334
|
+
store.recording,
|
|
1335
|
+
plan,
|
|
1336
|
+
args,
|
|
1337
|
+
parentCallId,
|
|
1338
|
+
startedAtNs2,
|
|
1339
|
+
startedAtMs,
|
|
1340
|
+
error
|
|
1341
|
+
);
|
|
1342
|
+
addRecorderTime(store.recording, process.hrtime.bigint() - callbackEndedAtNs2);
|
|
1343
|
+
throw error;
|
|
1344
|
+
}
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
const callbackEndedAtNs = process.hrtime.bigint();
|
|
1348
|
+
recordSuppressedCall(
|
|
1349
|
+
store.recording,
|
|
1350
|
+
group,
|
|
1351
|
+
plan.functionId,
|
|
1352
|
+
parentCallId,
|
|
1353
|
+
startedAtMs,
|
|
1354
|
+
startedAtNs2,
|
|
1355
|
+
callbackEndedAtNs
|
|
1356
|
+
);
|
|
1357
|
+
addRecorderTime(store.recording, startedAtNs2 - recorderStartedAtNs);
|
|
1358
|
+
addRecorderTime(store.recording, process.hrtime.bigint() - callbackEndedAtNs);
|
|
1359
|
+
return result;
|
|
1360
|
+
} catch (error) {
|
|
1361
|
+
const callbackEndedAtNs = process.hrtime.bigint();
|
|
1362
|
+
captureCompletedThrow(
|
|
1225
1363
|
store.recording,
|
|
1226
|
-
|
|
1364
|
+
plan,
|
|
1227
1365
|
args,
|
|
1228
1366
|
parentCallId,
|
|
1229
1367
|
startedAtNs2,
|
|
1230
1368
|
startedAtMs,
|
|
1231
1369
|
error
|
|
1232
|
-
)
|
|
1233
|
-
|
|
1370
|
+
);
|
|
1371
|
+
addRecorderTime(store.recording, startedAtNs2 - recorderStartedAtNs);
|
|
1372
|
+
addRecorderTime(store.recording, process.hrtime.bigint() - callbackEndedAtNs);
|
|
1373
|
+
throw error;
|
|
1374
|
+
}
|
|
1234
1375
|
}
|
|
1235
1376
|
const startedAtNs = process.hrtime.bigint();
|
|
1236
1377
|
if (!store.recording.events.canStartCall()) {
|
|
@@ -1249,7 +1390,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1249
1390
|
captureSuccess,
|
|
1250
1391
|
(error) => captureCompletedThrow(
|
|
1251
1392
|
store.recording,
|
|
1252
|
-
|
|
1393
|
+
plan,
|
|
1253
1394
|
args,
|
|
1254
1395
|
parentCallId,
|
|
1255
1396
|
startedAtNs,
|
|
@@ -1259,7 +1400,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1259
1400
|
)
|
|
1260
1401
|
);
|
|
1261
1402
|
}
|
|
1262
|
-
if (!memoryBudget.reserveCall(store.recording.memory,
|
|
1403
|
+
if (!memoryBudget.reserveCall(store.recording.memory, plan.estimatedCallBytes)) {
|
|
1263
1404
|
return observeRecordedCallback(
|
|
1264
1405
|
store.recording,
|
|
1265
1406
|
recorderStartedAtNs,
|
|
@@ -1273,7 +1414,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1273
1414
|
const enter = {
|
|
1274
1415
|
type: "function_enter",
|
|
1275
1416
|
callId,
|
|
1276
|
-
functionId,
|
|
1417
|
+
functionId: plan.functionId,
|
|
1277
1418
|
timestampMs: elapsedMs(store.recording.startedAtNs),
|
|
1278
1419
|
args: Array.from(args, (argument) => captureValue(store.recording, argument)),
|
|
1279
1420
|
...parentCallId === void 0 ? {} : { parentCallId }
|
|
@@ -1294,6 +1435,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1294
1435
|
durationMs: elapsedMs(startedAtNs),
|
|
1295
1436
|
returnValue: captureValue(store.recording, value)
|
|
1296
1437
|
});
|
|
1438
|
+
stats.functionCallsCaptured++;
|
|
1297
1439
|
},
|
|
1298
1440
|
(error) => {
|
|
1299
1441
|
const capturedError = serializedError(store.recording, error, callId);
|
|
@@ -1304,16 +1446,23 @@ var createExecutionRecorder = (options) => {
|
|
|
1304
1446
|
durationMs: elapsedMs(startedAtNs),
|
|
1305
1447
|
error: capturedError.value
|
|
1306
1448
|
});
|
|
1449
|
+
stats.functionCallsCaptured++;
|
|
1307
1450
|
capturedError.commit();
|
|
1308
1451
|
rememberError(error, store.recording);
|
|
1309
1452
|
}
|
|
1310
1453
|
)
|
|
1311
1454
|
);
|
|
1312
1455
|
};
|
|
1456
|
+
const runFunction = (input, args, callback) => runPreparedFunction(preparePlan(normalizeFunctionDefinition(input)), args, callback);
|
|
1457
|
+
const runFunctionPlans = (plans, planIndex, args, callback) => {
|
|
1458
|
+
const plan = preparedPlansFor(plans)[planIndex];
|
|
1459
|
+
return plan ? runPreparedFunction(plan, args, callback) : callback();
|
|
1460
|
+
};
|
|
1313
1461
|
function trace(input, fn) {
|
|
1314
1462
|
const definition = typeof input === "string" ? manualFunctionDefinition(input) : { ...input, functionId: runtimeFunctionId2(input) };
|
|
1463
|
+
const plan = preparePlan(definition);
|
|
1315
1464
|
return function traced(...args) {
|
|
1316
|
-
return
|
|
1465
|
+
return runPreparedFunction(plan, args, () => fn.apply(this, args));
|
|
1317
1466
|
};
|
|
1318
1467
|
}
|
|
1319
1468
|
return {
|
|
@@ -1321,11 +1470,51 @@ var createExecutionRecorder = (options) => {
|
|
|
1321
1470
|
run,
|
|
1322
1471
|
getErrorState,
|
|
1323
1472
|
runFunction,
|
|
1473
|
+
runFunctionPlans,
|
|
1474
|
+
runPreparedFunction,
|
|
1324
1475
|
trace,
|
|
1325
1476
|
getStats: () => ({ ...stats, ...memoryBudget.getStats() })
|
|
1326
1477
|
};
|
|
1327
1478
|
};
|
|
1328
1479
|
|
|
1480
|
+
// src/execution-recorder/automatic-runtime.ts
|
|
1481
|
+
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
|
|
1482
|
+
var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
|
|
1483
|
+
var createRegistry = () => {
|
|
1484
|
+
const runtimes = [];
|
|
1485
|
+
return {
|
|
1486
|
+
__rasputinRuntimeRegistry: true,
|
|
1487
|
+
runtimes,
|
|
1488
|
+
register(plans) {
|
|
1489
|
+
return (planIndex, args, callback) => {
|
|
1490
|
+
const runtime = runtimes.at(-1);
|
|
1491
|
+
return runtime ? runtime.run(plans, planIndex, args, callback) : callback();
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
};
|
|
1496
|
+
var getAutomaticRuntimeRegistry = () => {
|
|
1497
|
+
const existing = Reflect.get(globalThis, runtimeSymbol);
|
|
1498
|
+
if (existing && Array.isArray(existing.runtimes) && typeof existing.register === "function") {
|
|
1499
|
+
existing.__rasputinRuntimeRegistry = true;
|
|
1500
|
+
return existing;
|
|
1501
|
+
}
|
|
1502
|
+
const registry = createRegistry();
|
|
1503
|
+
Reflect.set(globalThis, runtimeSymbol, registry);
|
|
1504
|
+
return registry;
|
|
1505
|
+
};
|
|
1506
|
+
var installAutomaticExecutionRuntime = (execution) => {
|
|
1507
|
+
const registry = getAutomaticRuntimeRegistry();
|
|
1508
|
+
const runtime = {
|
|
1509
|
+
run: (plans, planIndex, args, callback) => execution.runFunctionPlans(plans, planIndex, args, callback)
|
|
1510
|
+
};
|
|
1511
|
+
registry.runtimes.push(runtime);
|
|
1512
|
+
return () => {
|
|
1513
|
+
const index = registry.runtimes.lastIndexOf(runtime);
|
|
1514
|
+
if (index >= 0) registry.runtimes.splice(index, 1);
|
|
1515
|
+
};
|
|
1516
|
+
};
|
|
1517
|
+
|
|
1329
1518
|
// src/install-global-handlers.ts
|
|
1330
1519
|
var removeProcessListener = process.off.bind(process);
|
|
1331
1520
|
var installGlobalHandlers = (client, options = {}) => {
|
|
@@ -1375,6 +1564,7 @@ var installGlobalHandlers = (client, options = {}) => {
|
|
|
1375
1564
|
};
|
|
1376
1565
|
|
|
1377
1566
|
// src/rasputin-init.ts
|
|
1567
|
+
import { join } from "node:path";
|
|
1378
1568
|
import {
|
|
1379
1569
|
createClient,
|
|
1380
1570
|
isClientEnabled
|
|
@@ -1382,7 +1572,7 @@ import {
|
|
|
1382
1572
|
|
|
1383
1573
|
// src/sdk-meta.ts
|
|
1384
1574
|
var SDK_NAME = "@rasputin-ai/node";
|
|
1385
|
-
var SDK_VERSION = "0.
|
|
1575
|
+
var SDK_VERSION = "0.5.0-alpha.1";
|
|
1386
1576
|
|
|
1387
1577
|
// src/rasputin-init.ts
|
|
1388
1578
|
var withExecution = (client, execution, installRuntime, manifest) => {
|
|
@@ -1423,6 +1613,9 @@ var RasputinInit = (options) => {
|
|
|
1423
1613
|
...options.executionRecorder,
|
|
1424
1614
|
enabled: recorderEnabled
|
|
1425
1615
|
});
|
|
1616
|
+
loadInstrumentationManifestIfPresent(
|
|
1617
|
+
options.instrumentationManifestPath ?? process.env.RASPUTIN_INSTRUMENTATION_MANIFEST ?? join(process.cwd(), ".rasputin", "instrumentation-manifest.json")
|
|
1618
|
+
);
|
|
1426
1619
|
const wrappedClient = withExecution(client, execution, recorderEnabled, {
|
|
1427
1620
|
projectApiKey: options.projectApiKey,
|
|
1428
1621
|
release: options.release,
|