@rasputin-ai/node 0.4.1 → 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 +17 -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 +439 -259
- 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,25 +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
|
-
measuredExecutions: 0,
|
|
925
|
-
totalInlineWallTimeMs: 0,
|
|
926
|
-
maxInlineWallTimeMs: 0
|
|
927
|
-
})
|
|
955
|
+
getStats: () => ({ ...emptyStats })
|
|
928
956
|
};
|
|
929
957
|
var mergeExecutionMetadata = (target, metadata) => {
|
|
930
958
|
if (metadata.kind) target.kind = metadata.kind;
|
|
@@ -947,10 +975,20 @@ var createExecutionRecorder = (options) => {
|
|
|
947
975
|
maxActiveMemoryBytes,
|
|
948
976
|
options.maxSerializedValueBytes ?? 8 * 1024
|
|
949
977
|
);
|
|
978
|
+
const serializer = createSafeSerializer(options);
|
|
950
979
|
const excludesSource = createSourceExclusionMatcher(options.excludeSources);
|
|
951
980
|
let executionCounter = 0;
|
|
981
|
+
let nextRuntimePlanId = 1;
|
|
982
|
+
const internedPlans = /* @__PURE__ */ new Map();
|
|
983
|
+
const preparedModules = /* @__PURE__ */ new WeakMap();
|
|
952
984
|
const errorExecutions = /* @__PURE__ */ new WeakMap();
|
|
953
985
|
const stats = {
|
|
986
|
+
functionCallsObserved: 0,
|
|
987
|
+
functionCallsCaptured: 0,
|
|
988
|
+
functionCallsExcluded: 0,
|
|
989
|
+
serializedValues: 0,
|
|
990
|
+
serializedValueNodes: 0,
|
|
991
|
+
serializedValueBytes: 0,
|
|
954
992
|
errorSnapshots: 0,
|
|
955
993
|
successfulExecutionsDiscarded: 0,
|
|
956
994
|
truncatedEvents: 0,
|
|
@@ -968,9 +1006,52 @@ var createExecutionRecorder = (options) => {
|
|
|
968
1006
|
stats.totalInlineWallTimeMs = Math.round((stats.totalInlineWallTimeMs + inlineWallTimeMs) * 1e4) / 1e4;
|
|
969
1007
|
stats.maxInlineWallTimeMs = Math.max(stats.maxInlineWallTimeMs, inlineWallTimeMs);
|
|
970
1008
|
};
|
|
971
|
-
const
|
|
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;
|
|
1045
|
+
};
|
|
972
1046
|
const stateFrom = (recording, endedAtNs = process.hrtime.bigint()) => {
|
|
973
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
|
+
}
|
|
974
1055
|
const events = recording.events.values().map((event) => event.type === "function_calls_suppressed" ? { ...event } : event);
|
|
975
1056
|
const referencedFunctionIds = new Set(
|
|
976
1057
|
events.flatMap(
|
|
@@ -1026,15 +1107,15 @@ var createExecutionRecorder = (options) => {
|
|
|
1026
1107
|
recording.events.append(event);
|
|
1027
1108
|
countDroppedEvents(recording, droppedBefore);
|
|
1028
1109
|
};
|
|
1029
|
-
const repeatedCallGroup = (recording,
|
|
1030
|
-
let byParent = recording.repeatedCalls.get(
|
|
1110
|
+
const repeatedCallGroup = (recording, runtimePlanId, parentCallId) => {
|
|
1111
|
+
let byParent = recording.repeatedCalls.get(runtimePlanId);
|
|
1031
1112
|
if (!byParent) {
|
|
1032
1113
|
byParent = /* @__PURE__ */ new Map();
|
|
1033
|
-
recording.repeatedCalls.set(
|
|
1114
|
+
recording.repeatedCalls.set(runtimePlanId, byParent);
|
|
1034
1115
|
}
|
|
1035
1116
|
let group = byParent.get(parentCallId);
|
|
1036
1117
|
if (!group) {
|
|
1037
|
-
group = { observedCalls: 0 };
|
|
1118
|
+
group = { observedCalls: 0, totalDurationNs: 0n };
|
|
1038
1119
|
byParent.set(parentCallId, group);
|
|
1039
1120
|
}
|
|
1040
1121
|
return group;
|
|
@@ -1052,14 +1133,14 @@ var createExecutionRecorder = (options) => {
|
|
|
1052
1133
|
return {
|
|
1053
1134
|
// Keep the bounded thrown value even after surrounding capture degrades. A partial
|
|
1054
1135
|
// snapshot without the exception itself would not be useful for investigation.
|
|
1055
|
-
value:
|
|
1136
|
+
value: serializeAndCount(error).value,
|
|
1056
1137
|
commit: () => {
|
|
1057
1138
|
if (key) recording.capturedErrors.set(key, callId);
|
|
1058
1139
|
}
|
|
1059
1140
|
};
|
|
1060
1141
|
};
|
|
1061
|
-
const captureCompletedThrow = (recording,
|
|
1062
|
-
if (!memoryBudget.reserveCall(recording.memory,
|
|
1142
|
+
const captureCompletedThrow = (recording, plan, args, parentCallId, startedAtNs, startedAtMs, error) => {
|
|
1143
|
+
if (!memoryBudget.reserveCall(recording.memory, plan.estimatedCallBytes)) {
|
|
1063
1144
|
rememberError(error, recording);
|
|
1064
1145
|
return;
|
|
1065
1146
|
}
|
|
@@ -1068,7 +1149,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1068
1149
|
const enter = {
|
|
1069
1150
|
type: "function_enter",
|
|
1070
1151
|
callId,
|
|
1071
|
-
functionId,
|
|
1152
|
+
functionId: plan.functionId,
|
|
1072
1153
|
timestampMs: startedAtMs,
|
|
1073
1154
|
args: Array.from(args, (argument) => captureValue(recording, argument)),
|
|
1074
1155
|
...parentCallId === void 0 ? {} : { parentCallId }
|
|
@@ -1082,13 +1163,17 @@ var createExecutionRecorder = (options) => {
|
|
|
1082
1163
|
};
|
|
1083
1164
|
const droppedBefore = recording.events.dropped;
|
|
1084
1165
|
const retained = recording.events.forceCompletedCall(enter, terminal);
|
|
1085
|
-
if (retained)
|
|
1166
|
+
if (retained) {
|
|
1167
|
+
stats.functionCallsCaptured++;
|
|
1168
|
+
capturedError.commit();
|
|
1169
|
+
}
|
|
1086
1170
|
countDroppedEvents(recording, droppedBefore);
|
|
1087
1171
|
rememberError(error, recording);
|
|
1088
1172
|
};
|
|
1089
|
-
const recordSuppressedCall = (recording, group, functionId, parentCallId, startedAtMs,
|
|
1173
|
+
const recordSuppressedCall = (recording, group, functionId, parentCallId, startedAtMs, startedAtNs, endedAtNs) => {
|
|
1090
1174
|
stats.repeatedCallsSuppressed++;
|
|
1091
|
-
|
|
1175
|
+
group.totalDurationNs += endedAtNs - startedAtNs;
|
|
1176
|
+
const endedAtMs = elapsedMs(recording.startedAtNs, endedAtNs);
|
|
1092
1177
|
if (!group.summary) {
|
|
1093
1178
|
group.summary = {
|
|
1094
1179
|
type: "function_calls_suppressed",
|
|
@@ -1096,7 +1181,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1096
1181
|
timestampMs: startedAtMs,
|
|
1097
1182
|
lastTimestampMs: endedAtMs,
|
|
1098
1183
|
suppressedCallCount: 1,
|
|
1099
|
-
totalDurationMs:
|
|
1184
|
+
totalDurationMs: 0,
|
|
1100
1185
|
...parentCallId === void 0 ? {} : { parentCallId }
|
|
1101
1186
|
};
|
|
1102
1187
|
appendStandaloneEvent(recording, group.summary);
|
|
@@ -1104,8 +1189,9 @@ var createExecutionRecorder = (options) => {
|
|
|
1104
1189
|
}
|
|
1105
1190
|
group.summary.lastTimestampMs = endedAtMs;
|
|
1106
1191
|
group.summary.suppressedCallCount++;
|
|
1107
|
-
|
|
1108
|
-
|
|
1192
|
+
if (!recording.events.hasSummary(group.summary)) {
|
|
1193
|
+
appendStandaloneEvent(recording, group.summary);
|
|
1194
|
+
}
|
|
1109
1195
|
};
|
|
1110
1196
|
const finish = (store, metadata) => {
|
|
1111
1197
|
const { recording } = store;
|
|
@@ -1202,48 +1288,90 @@ var createExecutionRecorder = (options) => {
|
|
|
1202
1288
|
return void 0;
|
|
1203
1289
|
}
|
|
1204
1290
|
};
|
|
1205
|
-
const
|
|
1291
|
+
const runPreparedFunction = (plan, args, callback) => {
|
|
1206
1292
|
const store = storage.getStore();
|
|
1207
1293
|
if (!store || store.recording.finished) return callback();
|
|
1208
|
-
|
|
1209
|
-
const functionId = definition.functionId;
|
|
1294
|
+
stats.functionCallsObserved++;
|
|
1210
1295
|
const recorderStartedAtNs = process.hrtime.bigint();
|
|
1211
|
-
if (store.suppressCapture ||
|
|
1296
|
+
if (store.suppressCapture || plan.excluded) {
|
|
1297
|
+
stats.functionCallsExcluded++;
|
|
1212
1298
|
return storage.run({ ...store, suppressCapture: true }, () => {
|
|
1213
1299
|
addRecorderTime(store.recording, process.hrtime.bigint() - recorderStartedAtNs);
|
|
1214
1300
|
return callback();
|
|
1215
1301
|
});
|
|
1216
1302
|
}
|
|
1217
|
-
store.recording.functions.set(functionId, definition);
|
|
1218
1303
|
const parentCallId = store.currentCallId;
|
|
1219
|
-
const group = repeatedCallGroup(store.recording,
|
|
1304
|
+
const group = repeatedCallGroup(store.recording, plan.runtimePlanId, parentCallId);
|
|
1220
1305
|
group.observedCalls++;
|
|
1306
|
+
if (group.observedCalls === 1) {
|
|
1307
|
+
store.recording.functions.set(plan.functionId, plan.definition);
|
|
1308
|
+
}
|
|
1221
1309
|
if (group.observedCalls > maxCapturedCallsPerFunction) {
|
|
1222
1310
|
const startedAtNs2 = process.hrtime.bigint();
|
|
1223
1311
|
const startedAtMs = elapsedMs(store.recording.startedAtNs, startedAtNs2);
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
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(
|
|
1238
1363
|
store.recording,
|
|
1239
|
-
|
|
1364
|
+
plan,
|
|
1240
1365
|
args,
|
|
1241
1366
|
parentCallId,
|
|
1242
1367
|
startedAtNs2,
|
|
1243
1368
|
startedAtMs,
|
|
1244
1369
|
error
|
|
1245
|
-
)
|
|
1246
|
-
|
|
1370
|
+
);
|
|
1371
|
+
addRecorderTime(store.recording, startedAtNs2 - recorderStartedAtNs);
|
|
1372
|
+
addRecorderTime(store.recording, process.hrtime.bigint() - callbackEndedAtNs);
|
|
1373
|
+
throw error;
|
|
1374
|
+
}
|
|
1247
1375
|
}
|
|
1248
1376
|
const startedAtNs = process.hrtime.bigint();
|
|
1249
1377
|
if (!store.recording.events.canStartCall()) {
|
|
@@ -1262,7 +1390,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1262
1390
|
captureSuccess,
|
|
1263
1391
|
(error) => captureCompletedThrow(
|
|
1264
1392
|
store.recording,
|
|
1265
|
-
|
|
1393
|
+
plan,
|
|
1266
1394
|
args,
|
|
1267
1395
|
parentCallId,
|
|
1268
1396
|
startedAtNs,
|
|
@@ -1272,7 +1400,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1272
1400
|
)
|
|
1273
1401
|
);
|
|
1274
1402
|
}
|
|
1275
|
-
if (!memoryBudget.reserveCall(store.recording.memory,
|
|
1403
|
+
if (!memoryBudget.reserveCall(store.recording.memory, plan.estimatedCallBytes)) {
|
|
1276
1404
|
return observeRecordedCallback(
|
|
1277
1405
|
store.recording,
|
|
1278
1406
|
recorderStartedAtNs,
|
|
@@ -1286,7 +1414,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1286
1414
|
const enter = {
|
|
1287
1415
|
type: "function_enter",
|
|
1288
1416
|
callId,
|
|
1289
|
-
functionId,
|
|
1417
|
+
functionId: plan.functionId,
|
|
1290
1418
|
timestampMs: elapsedMs(store.recording.startedAtNs),
|
|
1291
1419
|
args: Array.from(args, (argument) => captureValue(store.recording, argument)),
|
|
1292
1420
|
...parentCallId === void 0 ? {} : { parentCallId }
|
|
@@ -1307,6 +1435,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1307
1435
|
durationMs: elapsedMs(startedAtNs),
|
|
1308
1436
|
returnValue: captureValue(store.recording, value)
|
|
1309
1437
|
});
|
|
1438
|
+
stats.functionCallsCaptured++;
|
|
1310
1439
|
},
|
|
1311
1440
|
(error) => {
|
|
1312
1441
|
const capturedError = serializedError(store.recording, error, callId);
|
|
@@ -1317,16 +1446,23 @@ var createExecutionRecorder = (options) => {
|
|
|
1317
1446
|
durationMs: elapsedMs(startedAtNs),
|
|
1318
1447
|
error: capturedError.value
|
|
1319
1448
|
});
|
|
1449
|
+
stats.functionCallsCaptured++;
|
|
1320
1450
|
capturedError.commit();
|
|
1321
1451
|
rememberError(error, store.recording);
|
|
1322
1452
|
}
|
|
1323
1453
|
)
|
|
1324
1454
|
);
|
|
1325
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
|
+
};
|
|
1326
1461
|
function trace(input, fn) {
|
|
1327
1462
|
const definition = typeof input === "string" ? manualFunctionDefinition(input) : { ...input, functionId: runtimeFunctionId2(input) };
|
|
1463
|
+
const plan = preparePlan(definition);
|
|
1328
1464
|
return function traced(...args) {
|
|
1329
|
-
return
|
|
1465
|
+
return runPreparedFunction(plan, args, () => fn.apply(this, args));
|
|
1330
1466
|
};
|
|
1331
1467
|
}
|
|
1332
1468
|
return {
|
|
@@ -1334,11 +1470,51 @@ var createExecutionRecorder = (options) => {
|
|
|
1334
1470
|
run,
|
|
1335
1471
|
getErrorState,
|
|
1336
1472
|
runFunction,
|
|
1473
|
+
runFunctionPlans,
|
|
1474
|
+
runPreparedFunction,
|
|
1337
1475
|
trace,
|
|
1338
1476
|
getStats: () => ({ ...stats, ...memoryBudget.getStats() })
|
|
1339
1477
|
};
|
|
1340
1478
|
};
|
|
1341
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
|
+
|
|
1342
1518
|
// src/install-global-handlers.ts
|
|
1343
1519
|
var removeProcessListener = process.off.bind(process);
|
|
1344
1520
|
var installGlobalHandlers = (client, options = {}) => {
|
|
@@ -1388,6 +1564,7 @@ var installGlobalHandlers = (client, options = {}) => {
|
|
|
1388
1564
|
};
|
|
1389
1565
|
|
|
1390
1566
|
// src/rasputin-init.ts
|
|
1567
|
+
import { join } from "node:path";
|
|
1391
1568
|
import {
|
|
1392
1569
|
createClient,
|
|
1393
1570
|
isClientEnabled
|
|
@@ -1395,7 +1572,7 @@ import {
|
|
|
1395
1572
|
|
|
1396
1573
|
// src/sdk-meta.ts
|
|
1397
1574
|
var SDK_NAME = "@rasputin-ai/node";
|
|
1398
|
-
var SDK_VERSION = "0.
|
|
1575
|
+
var SDK_VERSION = "0.5.0-alpha.1";
|
|
1399
1576
|
|
|
1400
1577
|
// src/rasputin-init.ts
|
|
1401
1578
|
var withExecution = (client, execution, installRuntime, manifest) => {
|
|
@@ -1436,6 +1613,9 @@ var RasputinInit = (options) => {
|
|
|
1436
1613
|
...options.executionRecorder,
|
|
1437
1614
|
enabled: recorderEnabled
|
|
1438
1615
|
});
|
|
1616
|
+
loadInstrumentationManifestIfPresent(
|
|
1617
|
+
options.instrumentationManifestPath ?? process.env.RASPUTIN_INSTRUMENTATION_MANIFEST ?? join(process.cwd(), ".rasputin", "instrumentation-manifest.json")
|
|
1618
|
+
);
|
|
1439
1619
|
const wrappedClient = withExecution(client, execution, recorderEnabled, {
|
|
1440
1620
|
projectApiKey: options.projectApiKey,
|
|
1441
1621
|
release: options.release,
|