@rasputin-ai/node 0.4.1 → 0.5.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +188 -162
- 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/auto-instrumentation/warn-production-runtime-transform.d.ts +8 -0
- package/dist/auto-instrumentation/warn-production-runtime-transform.d.ts.map +1 -0
- package/dist/execution-recorder/automatic-runtime.d.ts +19 -10
- package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
- package/dist/execution-recorder/call-aware-event-buffer.d.ts +17 -3
- package/dist/execution-recorder/call-aware-event-buffer.d.ts.map +1 -1
- package/dist/execution-recorder/execution-recorder-types.d.ts +19 -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 +546 -294
- 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 +55 -21
- package/dist/instrument/node.js +55 -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
|
|
@@ -389,6 +387,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
389
387
|
import { runtimeFunctionId as runtimeFunctionId2 } from "@rasputin-ai/core";
|
|
390
388
|
|
|
391
389
|
// src/execution-recorder/call-aware-event-buffer.ts
|
|
390
|
+
var isSuppressionPlaceholder = (item) => "kind" in item && item.kind === "suppressed";
|
|
392
391
|
var CallAwareEventBuffer = class {
|
|
393
392
|
constructor(capacity) {
|
|
394
393
|
this.capacity = capacity;
|
|
@@ -411,12 +410,14 @@ var CallAwareEventBuffer = class {
|
|
|
411
410
|
dropCall() {
|
|
412
411
|
this.dropped += 2;
|
|
413
412
|
}
|
|
414
|
-
|
|
413
|
+
appendSuppressed(group) {
|
|
415
414
|
if (this.items.length + this.openCallIds.size + 1 > this.capacity) {
|
|
416
415
|
this.dropped++;
|
|
417
416
|
return false;
|
|
418
417
|
}
|
|
419
|
-
|
|
418
|
+
const placeholder = { kind: "suppressed", group };
|
|
419
|
+
this.items.push(placeholder);
|
|
420
|
+
group.retainedSlot = placeholder;
|
|
420
421
|
return true;
|
|
421
422
|
}
|
|
422
423
|
forceCompletedCall(enter, terminal) {
|
|
@@ -432,31 +433,27 @@ var CallAwareEventBuffer = class {
|
|
|
432
433
|
values() {
|
|
433
434
|
return [...this.items];
|
|
434
435
|
}
|
|
435
|
-
contains(target) {
|
|
436
|
-
return this.items.includes(target);
|
|
437
|
-
}
|
|
438
436
|
evictCompletedLeafOrSummary() {
|
|
439
|
-
const summaryIndex = this.items.findIndex(
|
|
440
|
-
(event) => event.type === "function_calls_suppressed"
|
|
441
|
-
);
|
|
437
|
+
const summaryIndex = this.items.findIndex((item) => isSuppressionPlaceholder(item));
|
|
442
438
|
if (summaryIndex >= 0) {
|
|
439
|
+
const summary = this.items[summaryIndex];
|
|
440
|
+
if (summary && isSuppressionPlaceholder(summary)) summary.group.retainedSlot = void 0;
|
|
443
441
|
this.items.splice(summaryIndex, 1);
|
|
444
442
|
this.dropped++;
|
|
445
443
|
return true;
|
|
446
444
|
}
|
|
447
445
|
const parentCallIds = /* @__PURE__ */ new Set();
|
|
448
|
-
for (const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}
|
|
446
|
+
for (const item of this.items) {
|
|
447
|
+
const parentCallId = isSuppressionPlaceholder(item) ? item.group.parentCallId : item.type === "function_enter" ? item.parentCallId : void 0;
|
|
448
|
+
if (parentCallId !== void 0) parentCallIds.add(parentCallId);
|
|
452
449
|
}
|
|
453
450
|
for (let index = 0; index < this.items.length; index++) {
|
|
454
451
|
const event = this.items[index];
|
|
455
|
-
if (event
|
|
452
|
+
if (!event || isSuppressionPlaceholder(event) || event.type !== "function_enter" || this.openCallIds.has(event.callId) || parentCallIds.has(event.callId)) {
|
|
456
453
|
continue;
|
|
457
454
|
}
|
|
458
455
|
const terminalIndex = this.items.findIndex(
|
|
459
|
-
(candidate) => (candidate.type === "function_exit" || candidate.type === "function_throw") && candidate.callId === event.callId
|
|
456
|
+
(candidate) => !isSuppressionPlaceholder(candidate) && (candidate.type === "function_exit" || candidate.type === "function_throw") && candidate.callId === event.callId
|
|
460
457
|
);
|
|
461
458
|
if (terminalIndex < 0) continue;
|
|
462
459
|
this.items.splice(Math.max(index, terminalIndex), 1);
|
|
@@ -469,9 +466,7 @@ var CallAwareEventBuffer = class {
|
|
|
469
466
|
};
|
|
470
467
|
|
|
471
468
|
// src/execution-recorder/recorder-memory-budget.ts
|
|
472
|
-
import { Buffer } from "node:buffer";
|
|
473
469
|
var EXECUTION_BASE_ESTIMATED_BYTES = 1024;
|
|
474
|
-
var EVENT_PAIR_BASE_ESTIMATED_BYTES = 512;
|
|
475
470
|
var SUMMARY_EVENT_BASE_ESTIMATED_BYTES = 256;
|
|
476
471
|
var VALUE_HEAP_ESTIMATE_MULTIPLIER = 2;
|
|
477
472
|
var MEMORY_BUDGET_VALUE_MARKER = {
|
|
@@ -525,12 +520,11 @@ var RecorderMemoryBudget = class {
|
|
|
525
520
|
}
|
|
526
521
|
return serialized.value;
|
|
527
522
|
}
|
|
528
|
-
reserveCall(allocation,
|
|
523
|
+
reserveCall(allocation, estimatedBytes) {
|
|
529
524
|
if (allocation.mode === "metadata-only") {
|
|
530
525
|
this.dropEvents(allocation, 2);
|
|
531
526
|
return false;
|
|
532
527
|
}
|
|
533
|
-
const estimatedBytes = EVENT_PAIR_BASE_ESTIMATED_BYTES + Buffer.byteLength(functionId, "utf8") * 2;
|
|
534
528
|
if (this.reserve(allocation, estimatedBytes)) return true;
|
|
535
529
|
this.markPressure(allocation, "metadata-only");
|
|
536
530
|
this.dropEvents(allocation, 2);
|
|
@@ -590,7 +584,7 @@ var RecorderMemoryBudget = class {
|
|
|
590
584
|
|
|
591
585
|
// src/execution-recorder/safe-serialize.ts
|
|
592
586
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
593
|
-
var DEFAULT_REDACT_KEYS =
|
|
587
|
+
var DEFAULT_REDACT_KEYS = [
|
|
594
588
|
"password",
|
|
595
589
|
"token",
|
|
596
590
|
"authorization",
|
|
@@ -598,7 +592,7 @@ var DEFAULT_REDACT_KEYS = /* @__PURE__ */ new Set([
|
|
|
598
592
|
"secret",
|
|
599
593
|
"apikey",
|
|
600
594
|
"creditcard"
|
|
601
|
-
]
|
|
595
|
+
];
|
|
602
596
|
var defaults = {
|
|
603
597
|
maxDepth: 3,
|
|
604
598
|
maxObjectKeys: 30,
|
|
@@ -606,6 +600,10 @@ var defaults = {
|
|
|
606
600
|
maxStringLength: 500,
|
|
607
601
|
maxSerializedValueBytes: 8 * 1024
|
|
608
602
|
};
|
|
603
|
+
var TYPE_MARKER_OPEN = '{"__rasputin_type":"';
|
|
604
|
+
var TYPE_MARKER_VALUE_MID = '","value":"';
|
|
605
|
+
var TYPE_MARKER_CLOSE = '"}';
|
|
606
|
+
var UNDEFINED_MARKER_BYTES = TYPE_MARKER_OPEN.length + "undefined".length + TYPE_MARKER_CLOSE.length;
|
|
609
607
|
var truncation = (reason, extra = {}) => ({
|
|
610
608
|
__rasputin_truncated: true,
|
|
611
609
|
__reason: reason,
|
|
@@ -615,135 +613,224 @@ var typeMarker = (type, value) => ({
|
|
|
615
613
|
__rasputin_type: type,
|
|
616
614
|
...value === void 0 ? {} : { value }
|
|
617
615
|
});
|
|
618
|
-
var
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
616
|
+
var REDACTED = "[RASPUTIN_REDACTED]";
|
|
617
|
+
var compileLimits = (options) => ({
|
|
618
|
+
maxDepth: options.maxDepth ?? defaults.maxDepth,
|
|
619
|
+
maxObjectKeys: options.maxObjectKeys ?? defaults.maxObjectKeys,
|
|
620
|
+
maxArrayElements: options.maxArrayElements ?? defaults.maxArrayElements,
|
|
621
|
+
maxStringLength: options.maxStringLength ?? defaults.maxStringLength,
|
|
622
|
+
maxSerializedValueBytes: options.maxSerializedValueBytes ?? defaults.maxSerializedValueBytes
|
|
623
|
+
});
|
|
624
|
+
var compileRedactKeys = (options) => {
|
|
625
|
+
if (!options.redactKeys?.length) return new Set(DEFAULT_REDACT_KEYS);
|
|
626
|
+
return /* @__PURE__ */ new Set([...DEFAULT_REDACT_KEYS, ...options.redactKeys.map((key) => key.toLowerCase())]);
|
|
627
|
+
};
|
|
628
|
+
var isJsonSafeString = (value) => {
|
|
629
|
+
for (let index = 0; index < value.length; index++) {
|
|
630
|
+
const code = value.charCodeAt(index);
|
|
631
|
+
if (code < 32 || code === 34 || code === 92) return false;
|
|
632
|
+
if (code >= 55296 && code <= 56319) {
|
|
633
|
+
const next = value.charCodeAt(index + 1);
|
|
634
|
+
if (index + 1 >= value.length || next < 56320 || next > 57343) return false;
|
|
635
|
+
index++;
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
if (code >= 56320 && code <= 57343) return false;
|
|
627
639
|
}
|
|
628
|
-
return
|
|
640
|
+
return true;
|
|
629
641
|
};
|
|
630
|
-
var
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
|
|
642
|
+
var typeMarkerWithValueBytes = (type, jsonSafeValue) => TYPE_MARKER_OPEN.length + type.length + TYPE_MARKER_VALUE_MID.length + Buffer2.byteLength(jsonSafeValue, "utf8") + TYPE_MARKER_CLOSE.length;
|
|
643
|
+
var primitiveFastPath = (value, maxStringLength) => {
|
|
644
|
+
if (value === null) return { value: null, bytes: 4, visitedNodes: 1 };
|
|
645
|
+
if (typeof value === "boolean") return { value, bytes: value ? 4 : 5, visitedNodes: 1 };
|
|
646
|
+
if (typeof value === "number") {
|
|
647
|
+
if (!Number.isFinite(value)) return { value, bytes: 4, visitedNodes: 1 };
|
|
648
|
+
if (Object.is(value, -0)) return { value, bytes: 1, visitedNodes: 1 };
|
|
649
|
+
return { value, bytes: String(value).length, visitedNodes: 1 };
|
|
650
|
+
}
|
|
638
651
|
if (typeof value === "string") {
|
|
639
|
-
if (value.length
|
|
640
|
-
return
|
|
641
|
-
__original_length: value.length,
|
|
642
|
-
value: value.slice(0, limits.maxStringLength)
|
|
643
|
-
});
|
|
652
|
+
if (value.length > maxStringLength || !isJsonSafeString(value)) return void 0;
|
|
653
|
+
return { value, bytes: 2 + Buffer2.byteLength(value, "utf8"), visitedNodes: 1 };
|
|
644
654
|
}
|
|
645
|
-
if (typeof value === "undefined")
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
655
|
+
if (typeof value === "undefined") {
|
|
656
|
+
return {
|
|
657
|
+
value: { __rasputin_type: "undefined" },
|
|
658
|
+
bytes: UNDEFINED_MARKER_BYTES,
|
|
659
|
+
visitedNodes: 1
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
if (typeof value === "bigint") {
|
|
663
|
+
const digits = value.toString();
|
|
664
|
+
return {
|
|
665
|
+
value: { __rasputin_type: "bigint", value: digits },
|
|
666
|
+
bytes: typeMarkerWithValueBytes("bigint", digits),
|
|
667
|
+
visitedNodes: 1
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
if (typeof value === "symbol") {
|
|
671
|
+
const text = String(value);
|
|
672
|
+
if (!isJsonSafeString(text)) return void 0;
|
|
673
|
+
return {
|
|
674
|
+
value: { __rasputin_type: "symbol", value: text },
|
|
675
|
+
bytes: typeMarkerWithValueBytes("symbol", text),
|
|
676
|
+
visitedNodes: 1
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
if (typeof value === "function") {
|
|
680
|
+
const name = value.name || "anonymous";
|
|
681
|
+
if (!isJsonSafeString(name)) return void 0;
|
|
682
|
+
return {
|
|
683
|
+
value: { __rasputin_type: "function", value: name },
|
|
684
|
+
bytes: typeMarkerWithValueBytes("function", name),
|
|
685
|
+
visitedNodes: 1
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
return void 0;
|
|
689
|
+
};
|
|
690
|
+
var createSafeSerializer = (options = {}) => {
|
|
691
|
+
const limits = compileLimits(options);
|
|
692
|
+
const redactKeys = compileRedactKeys(options);
|
|
693
|
+
const serializeInner = (value, depth, seen, metrics) => {
|
|
694
|
+
metrics.visitedNodes++;
|
|
695
|
+
if (value === null || typeof value === "boolean" || typeof value === "number") return value;
|
|
696
|
+
if (typeof value === "string") {
|
|
697
|
+
if (value.length <= limits.maxStringLength) return value;
|
|
698
|
+
return truncation("max_string_length", {
|
|
699
|
+
__original_length: value.length,
|
|
700
|
+
value: value.slice(0, limits.maxStringLength)
|
|
661
701
|
});
|
|
662
702
|
}
|
|
663
|
-
if (value
|
|
664
|
-
if (value
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
703
|
+
if (typeof value === "undefined") return typeMarker("undefined");
|
|
704
|
+
if (typeof value === "bigint") return typeMarker("bigint", value.toString());
|
|
705
|
+
if (typeof value === "symbol") return typeMarker("symbol", String(value));
|
|
706
|
+
if (typeof value === "function") return typeMarker("function", value.name || "anonymous");
|
|
707
|
+
if (depth >= limits.maxDepth) return truncation("max_depth");
|
|
708
|
+
if (typeof value !== "object") return typeMarker(typeof value);
|
|
709
|
+
const tracking = seen ?? /* @__PURE__ */ new WeakSet();
|
|
710
|
+
if (tracking.has(value)) return typeMarker("circular");
|
|
711
|
+
tracking.add(value);
|
|
712
|
+
try {
|
|
713
|
+
if (value instanceof Error) {
|
|
714
|
+
return {
|
|
715
|
+
__rasputin_type: "Error",
|
|
716
|
+
name: value.name,
|
|
717
|
+
message: serializeInner(value.message, depth + 1, tracking, metrics),
|
|
718
|
+
...value.stack ? { stack: serializeInner(value.stack, depth + 1, tracking, metrics) } : {}
|
|
719
|
+
};
|
|
672
720
|
}
|
|
673
|
-
return
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
const items = [];
|
|
681
|
-
for (const item of value.values()) {
|
|
682
|
-
if (items.length >= limits.maxArrayElements) break;
|
|
683
|
-
items.push(serializeInner(item, depth + 1, seen, limits));
|
|
721
|
+
if (value instanceof Date) return typeMarker("Date", value.toISOString());
|
|
722
|
+
if (Buffer2.isBuffer(value)) {
|
|
723
|
+
return typeMarker("Buffer", {
|
|
724
|
+
byteLength: value.byteLength,
|
|
725
|
+
base64: value.subarray(0, limits.maxStringLength).toString("base64"),
|
|
726
|
+
...value.byteLength > limits.maxStringLength ? { __rasputin_truncated: true } : {}
|
|
727
|
+
});
|
|
684
728
|
}
|
|
685
|
-
return
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
729
|
+
if (value instanceof Promise) return typeMarker("Promise");
|
|
730
|
+
if (value instanceof Map) {
|
|
731
|
+
const entries = [];
|
|
732
|
+
for (const entry of value.entries()) {
|
|
733
|
+
if (entries.length >= limits.maxArrayElements) break;
|
|
734
|
+
entries.push([
|
|
735
|
+
serializeInner(entry[0], depth + 1, tracking, metrics),
|
|
736
|
+
serializeInner(entry[1], depth + 1, tracking, metrics)
|
|
737
|
+
]);
|
|
738
|
+
}
|
|
739
|
+
return {
|
|
740
|
+
__rasputin_type: "Map",
|
|
741
|
+
entries,
|
|
742
|
+
...value.size > limits.maxArrayElements ? truncation("max_array_elements", { __original_length: value.size }) : {}
|
|
743
|
+
};
|
|
696
744
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
745
|
+
if (value instanceof Set) {
|
|
746
|
+
const items = [];
|
|
747
|
+
for (const item of value.values()) {
|
|
748
|
+
if (items.length >= limits.maxArrayElements) break;
|
|
749
|
+
items.push(serializeInner(item, depth + 1, tracking, metrics));
|
|
750
|
+
}
|
|
751
|
+
return {
|
|
752
|
+
__rasputin_type: "Set",
|
|
753
|
+
values: items,
|
|
754
|
+
...value.size > limits.maxArrayElements ? truncation("max_array_elements", { __original_length: value.size }) : {}
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
if (Array.isArray(value)) {
|
|
758
|
+
const maxItems = value.length > limits.maxArrayElements ? limits.maxArrayElements - 1 : value.length;
|
|
759
|
+
const items = [];
|
|
760
|
+
for (let index = 0; index < maxItems; index++) {
|
|
761
|
+
items.push(serializeInner(value[index], depth + 1, tracking, metrics));
|
|
762
|
+
}
|
|
763
|
+
if (value.length > limits.maxArrayElements) {
|
|
764
|
+
items.push(truncation("max_array_elements", { __original_length: value.length }));
|
|
765
|
+
}
|
|
766
|
+
return items;
|
|
767
|
+
}
|
|
768
|
+
const prototype = Object.getPrototypeOf(value);
|
|
769
|
+
const serialized = {};
|
|
770
|
+
if (prototype && prototype !== Object.prototype) {
|
|
771
|
+
serialized.__rasputin_type = prototype.constructor?.name ?? "instance";
|
|
772
|
+
}
|
|
773
|
+
const keys = Object.keys(value);
|
|
774
|
+
const maxKeys = keys.length > limits.maxObjectKeys ? limits.maxObjectKeys - 1 : keys.length;
|
|
775
|
+
for (let index = 0; index < maxKeys; index++) {
|
|
776
|
+
const key = keys[index];
|
|
777
|
+
if (key === void 0) continue;
|
|
778
|
+
if (redactKeys.has(key.toLowerCase())) {
|
|
779
|
+
serialized[key] = REDACTED;
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
783
|
+
serialized[key] = descriptor && "value" in descriptor ? serializeInner(descriptor.value, depth + 1, tracking, metrics) : typeMarker("accessor");
|
|
784
|
+
}
|
|
785
|
+
if (keys.length > limits.maxObjectKeys) {
|
|
786
|
+
Object.assign(
|
|
787
|
+
serialized,
|
|
788
|
+
truncation("max_object_keys", { __original_length: keys.length })
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
return serialized;
|
|
792
|
+
} catch (error) {
|
|
793
|
+
return typeMarker("unserializable", error instanceof Error ? error.message : void 0);
|
|
794
|
+
} finally {
|
|
795
|
+
tracking.delete(value);
|
|
712
796
|
}
|
|
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
797
|
};
|
|
728
|
-
|
|
729
|
-
const
|
|
730
|
-
...DEFAULT_REDACT_KEYS,
|
|
731
|
-
...(options.redactKeys ?? []).map((key) => key.toLowerCase())
|
|
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 });
|
|
798
|
+
const boundedOversize = (bytes, visitedNodes) => {
|
|
799
|
+
const bounded = truncation("max_serialized_value_bytes", { __serialized_bytes: bytes });
|
|
737
800
|
return {
|
|
738
801
|
value: bounded,
|
|
739
|
-
bytes:
|
|
802
|
+
bytes: Buffer2.byteLength(JSON.stringify(bounded), "utf8"),
|
|
803
|
+
visitedNodes
|
|
740
804
|
};
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
805
|
+
};
|
|
806
|
+
const serializeWithBytes = (value) => {
|
|
807
|
+
const fast = primitiveFastPath(value, limits.maxStringLength);
|
|
808
|
+
if (fast) {
|
|
809
|
+
return fast.bytes <= limits.maxSerializedValueBytes ? fast : boundedOversize(fast.bytes, fast.visitedNodes);
|
|
810
|
+
}
|
|
811
|
+
const metrics = { visitedNodes: 0 };
|
|
812
|
+
try {
|
|
813
|
+
const serialized = serializeInner(value, 0, void 0, metrics);
|
|
814
|
+
const bytes = Buffer2.byteLength(JSON.stringify(serialized), "utf8");
|
|
815
|
+
if (bytes <= limits.maxSerializedValueBytes) {
|
|
816
|
+
return { value: serialized, bytes, visitedNodes: metrics.visitedNodes };
|
|
817
|
+
}
|
|
818
|
+
return boundedOversize(bytes, metrics.visitedNodes);
|
|
819
|
+
} catch {
|
|
820
|
+
const failed = typeMarker("unserializable");
|
|
821
|
+
return {
|
|
822
|
+
value: failed,
|
|
823
|
+
bytes: Buffer2.byteLength(JSON.stringify(failed), "utf8"),
|
|
824
|
+
visitedNodes: metrics.visitedNodes
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
return {
|
|
829
|
+
serialize: (value) => serializeWithBytes(value).value,
|
|
830
|
+
serializeWithBytes
|
|
831
|
+
};
|
|
745
832
|
};
|
|
746
|
-
var
|
|
833
|
+
var defaultSerializer = createSafeSerializer();
|
|
747
834
|
|
|
748
835
|
// src/execution-recorder/source-exclusions.ts
|
|
749
836
|
var sourcePathFrom = (definition) => definition.source?.packageRelativePath.replaceAll("\\", "/") ?? definition.name;
|
|
@@ -896,6 +983,30 @@ var observeRecordedCallback = (recording, recorderStartedAtNs, callback, onRetur
|
|
|
896
983
|
recordExitWork(callbackEndedAtNs);
|
|
897
984
|
return result;
|
|
898
985
|
};
|
|
986
|
+
var emptyStats = {
|
|
987
|
+
functionCallsObserved: 0,
|
|
988
|
+
functionCallsCaptured: 0,
|
|
989
|
+
functionCallsExcluded: 0,
|
|
990
|
+
serializedValues: 0,
|
|
991
|
+
serializedValueNodes: 0,
|
|
992
|
+
serializedValueBytes: 0,
|
|
993
|
+
errorSnapshots: 0,
|
|
994
|
+
successfulExecutionsDiscarded: 0,
|
|
995
|
+
truncatedEvents: 0,
|
|
996
|
+
repeatedCallsSuppressed: 0,
|
|
997
|
+
activeExecutions: 0,
|
|
998
|
+
peakActiveExecutions: 0,
|
|
999
|
+
memoryPressureExecutions: 0,
|
|
1000
|
+
memoryPressureDegradations: 0,
|
|
1001
|
+
valuesDroppedByMemoryBudget: 0,
|
|
1002
|
+
eventsDroppedByMemoryBudget: 0,
|
|
1003
|
+
partialErrorSnapshots: 0,
|
|
1004
|
+
measuredExecutions: 0,
|
|
1005
|
+
totalInlineWallTimeMs: 0,
|
|
1006
|
+
maxInlineWallTimeMs: 0,
|
|
1007
|
+
activeEstimatedBytes: 0,
|
|
1008
|
+
peakActiveEstimatedBytes: 0
|
|
1009
|
+
};
|
|
899
1010
|
var noOpExecution = {
|
|
900
1011
|
createScope: () => ({
|
|
901
1012
|
run: (callback) => callback(),
|
|
@@ -906,25 +1017,11 @@ var noOpExecution = {
|
|
|
906
1017
|
run: (_metadata, callback) => callback(),
|
|
907
1018
|
getErrorState: () => void 0,
|
|
908
1019
|
runFunction: (_functionId, _args, callback) => callback(),
|
|
1020
|
+
runFunctionPlans: (_plans, _planIndex, _args, callback) => callback(),
|
|
1021
|
+
prepareFunctionPlans: () => [],
|
|
1022
|
+
runPreparedFunction: (_plan, _args, callback) => callback(),
|
|
909
1023
|
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
|
-
})
|
|
1024
|
+
getStats: () => ({ ...emptyStats })
|
|
928
1025
|
};
|
|
929
1026
|
var mergeExecutionMetadata = (target, metadata) => {
|
|
930
1027
|
if (metadata.kind) target.kind = metadata.kind;
|
|
@@ -947,10 +1044,20 @@ var createExecutionRecorder = (options) => {
|
|
|
947
1044
|
maxActiveMemoryBytes,
|
|
948
1045
|
options.maxSerializedValueBytes ?? 8 * 1024
|
|
949
1046
|
);
|
|
1047
|
+
const serializer = createSafeSerializer(options);
|
|
950
1048
|
const excludesSource = createSourceExclusionMatcher(options.excludeSources);
|
|
951
1049
|
let executionCounter = 0;
|
|
1050
|
+
let nextRuntimePlanId = 1;
|
|
1051
|
+
const internedPlans = /* @__PURE__ */ new Map();
|
|
1052
|
+
const preparedModules = /* @__PURE__ */ new WeakMap();
|
|
952
1053
|
const errorExecutions = /* @__PURE__ */ new WeakMap();
|
|
953
1054
|
const stats = {
|
|
1055
|
+
functionCallsObserved: 0,
|
|
1056
|
+
functionCallsCaptured: 0,
|
|
1057
|
+
functionCallsExcluded: 0,
|
|
1058
|
+
serializedValues: 0,
|
|
1059
|
+
serializedValueNodes: 0,
|
|
1060
|
+
serializedValueBytes: 0,
|
|
954
1061
|
errorSnapshots: 0,
|
|
955
1062
|
successfulExecutionsDiscarded: 0,
|
|
956
1063
|
truncatedEvents: 0,
|
|
@@ -968,10 +1075,57 @@ var createExecutionRecorder = (options) => {
|
|
|
968
1075
|
stats.totalInlineWallTimeMs = Math.round((stats.totalInlineWallTimeMs + inlineWallTimeMs) * 1e4) / 1e4;
|
|
969
1076
|
stats.maxInlineWallTimeMs = Math.max(stats.maxInlineWallTimeMs, inlineWallTimeMs);
|
|
970
1077
|
};
|
|
971
|
-
const
|
|
1078
|
+
const serializeAndCount = (value) => {
|
|
1079
|
+
const serialized = serializer.serializeWithBytes(value);
|
|
1080
|
+
stats.serializedValues++;
|
|
1081
|
+
stats.serializedValueNodes += serialized.visitedNodes;
|
|
1082
|
+
stats.serializedValueBytes += serialized.bytes;
|
|
1083
|
+
return serialized;
|
|
1084
|
+
};
|
|
1085
|
+
const captureValue = (recording, value) => memoryBudget.captureValue(recording.memory, () => serializeAndCount(value));
|
|
1086
|
+
const preparePlan = (definition) => {
|
|
1087
|
+
const existing = internedPlans.get(definition.functionId);
|
|
1088
|
+
if (existing) return existing;
|
|
1089
|
+
const plan = {
|
|
1090
|
+
runtimePlanId: nextRuntimePlanId++,
|
|
1091
|
+
definition,
|
|
1092
|
+
functionId: definition.functionId,
|
|
1093
|
+
excluded: excludesSource(definition),
|
|
1094
|
+
estimatedCallBytes: estimatedCallBytesFor(definition.functionId)
|
|
1095
|
+
};
|
|
1096
|
+
internedPlans.set(definition.functionId, plan);
|
|
1097
|
+
return plan;
|
|
1098
|
+
};
|
|
1099
|
+
const preparedPlansFor = (plans) => {
|
|
1100
|
+
const cached = preparedModules.get(plans);
|
|
1101
|
+
if (cached) return cached;
|
|
1102
|
+
const prepared = plans.map((encoded) => {
|
|
1103
|
+
const definition = definitionFromEncodedPlan(encoded);
|
|
1104
|
+
return {
|
|
1105
|
+
runtimePlanId: nextRuntimePlanId++,
|
|
1106
|
+
definition,
|
|
1107
|
+
functionId: definition.functionId,
|
|
1108
|
+
excluded: excludesSource(definition),
|
|
1109
|
+
estimatedCallBytes: estimatedCallBytesFor(definition.functionId)
|
|
1110
|
+
};
|
|
1111
|
+
});
|
|
1112
|
+
preparedModules.set(plans, prepared);
|
|
1113
|
+
return prepared;
|
|
1114
|
+
};
|
|
1115
|
+
const snapshotSuppressed = (group, startedAtNs) => ({
|
|
1116
|
+
type: "function_calls_suppressed",
|
|
1117
|
+
functionId: group.functionId,
|
|
1118
|
+
timestampMs: elapsedMs(startedAtNs, group.firstTimestampNs),
|
|
1119
|
+
lastTimestampMs: elapsedMs(startedAtNs, group.lastTimestampNs),
|
|
1120
|
+
suppressedCallCount: group.count,
|
|
1121
|
+
totalDurationMs: roundedMilliseconds(group.totalDurationNs),
|
|
1122
|
+
...group.parentCallId === void 0 ? {} : { parentCallId: group.parentCallId }
|
|
1123
|
+
});
|
|
972
1124
|
const stateFrom = (recording, endedAtNs = process.hrtime.bigint()) => {
|
|
973
1125
|
const recorderStartedAtNs = process.hrtime.bigint();
|
|
974
|
-
const events = recording.events.values().map(
|
|
1126
|
+
const events = recording.events.values().map(
|
|
1127
|
+
(item) => isSuppressionPlaceholder(item) ? snapshotSuppressed(item.group, recording.startedAtNs) : item
|
|
1128
|
+
);
|
|
975
1129
|
const referencedFunctionIds = new Set(
|
|
976
1130
|
events.flatMap(
|
|
977
1131
|
(event) => event.type === "function_enter" || event.type === "function_calls_suppressed" ? [event.functionId] : []
|
|
@@ -1020,18 +1174,25 @@ var createExecutionRecorder = (options) => {
|
|
|
1020
1174
|
const countDroppedEvents = (recording, droppedBefore) => {
|
|
1021
1175
|
stats.truncatedEvents += recording.events.dropped - droppedBefore;
|
|
1022
1176
|
};
|
|
1023
|
-
const
|
|
1177
|
+
const appendSuppressedPlaceholder = (recording, group) => {
|
|
1024
1178
|
if (!memoryBudget.reserveStandaloneEvent(recording.memory)) return;
|
|
1025
1179
|
const droppedBefore = recording.events.dropped;
|
|
1026
|
-
recording.events.
|
|
1180
|
+
recording.events.appendSuppressed(group);
|
|
1027
1181
|
countDroppedEvents(recording, droppedBefore);
|
|
1028
1182
|
};
|
|
1029
|
-
const repeatedCallGroup = (recording,
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1183
|
+
const repeatedCallGroup = (recording, runtimePlanId, parentCallId) => {
|
|
1184
|
+
const slots = recording.repeatedCalls;
|
|
1185
|
+
let slot = slots[runtimePlanId];
|
|
1186
|
+
if (!slot) {
|
|
1187
|
+
slot = {};
|
|
1188
|
+
slots[runtimePlanId] = slot;
|
|
1189
|
+
}
|
|
1190
|
+
if (parentCallId === void 0) {
|
|
1191
|
+
if (!slot.root) slot.root = { observedCalls: 0 };
|
|
1192
|
+
return slot.root;
|
|
1034
1193
|
}
|
|
1194
|
+
if (!slot.byParent) slot.byParent = /* @__PURE__ */ new Map();
|
|
1195
|
+
const byParent = slot.byParent;
|
|
1035
1196
|
let group = byParent.get(parentCallId);
|
|
1036
1197
|
if (!group) {
|
|
1037
1198
|
group = { observedCalls: 0 };
|
|
@@ -1052,14 +1213,14 @@ var createExecutionRecorder = (options) => {
|
|
|
1052
1213
|
return {
|
|
1053
1214
|
// Keep the bounded thrown value even after surrounding capture degrades. A partial
|
|
1054
1215
|
// snapshot without the exception itself would not be useful for investigation.
|
|
1055
|
-
value:
|
|
1216
|
+
value: serializeAndCount(error).value,
|
|
1056
1217
|
commit: () => {
|
|
1057
1218
|
if (key) recording.capturedErrors.set(key, callId);
|
|
1058
1219
|
}
|
|
1059
1220
|
};
|
|
1060
1221
|
};
|
|
1061
|
-
const captureCompletedThrow = (recording,
|
|
1062
|
-
if (!memoryBudget.reserveCall(recording.memory,
|
|
1222
|
+
const captureCompletedThrow = (recording, plan, args, parentCallId, startedAtNs, error) => {
|
|
1223
|
+
if (!memoryBudget.reserveCall(recording.memory, plan.estimatedCallBytes)) {
|
|
1063
1224
|
rememberError(error, recording);
|
|
1064
1225
|
return;
|
|
1065
1226
|
}
|
|
@@ -1068,8 +1229,8 @@ var createExecutionRecorder = (options) => {
|
|
|
1068
1229
|
const enter = {
|
|
1069
1230
|
type: "function_enter",
|
|
1070
1231
|
callId,
|
|
1071
|
-
functionId,
|
|
1072
|
-
timestampMs:
|
|
1232
|
+
functionId: plan.functionId,
|
|
1233
|
+
timestampMs: elapsedMs(recording.startedAtNs, startedAtNs),
|
|
1073
1234
|
args: Array.from(args, (argument) => captureValue(recording, argument)),
|
|
1074
1235
|
...parentCallId === void 0 ? {} : { parentCallId }
|
|
1075
1236
|
};
|
|
@@ -1082,30 +1243,35 @@ var createExecutionRecorder = (options) => {
|
|
|
1082
1243
|
};
|
|
1083
1244
|
const droppedBefore = recording.events.dropped;
|
|
1084
1245
|
const retained = recording.events.forceCompletedCall(enter, terminal);
|
|
1085
|
-
if (retained)
|
|
1246
|
+
if (retained) {
|
|
1247
|
+
stats.functionCallsCaptured++;
|
|
1248
|
+
capturedError.commit();
|
|
1249
|
+
}
|
|
1086
1250
|
countDroppedEvents(recording, droppedBefore);
|
|
1087
1251
|
rememberError(error, recording);
|
|
1088
1252
|
};
|
|
1089
|
-
const recordSuppressedCall = (recording, group, functionId, parentCallId,
|
|
1253
|
+
const recordSuppressedCall = (recording, group, functionId, parentCallId, startedAtNs, endedAtNs) => {
|
|
1090
1254
|
stats.repeatedCallsSuppressed++;
|
|
1091
|
-
const
|
|
1092
|
-
if (!group.
|
|
1093
|
-
group.
|
|
1094
|
-
|
|
1255
|
+
const durationNs = endedAtNs - startedAtNs;
|
|
1256
|
+
if (!group.suppressed) {
|
|
1257
|
+
group.suppressed = {
|
|
1258
|
+
count: 1,
|
|
1259
|
+
firstTimestampNs: startedAtNs,
|
|
1260
|
+
lastTimestampNs: endedAtNs,
|
|
1261
|
+
totalDurationNs: durationNs,
|
|
1095
1262
|
functionId,
|
|
1096
|
-
timestampMs: startedAtMs,
|
|
1097
|
-
lastTimestampMs: endedAtMs,
|
|
1098
|
-
suppressedCallCount: 1,
|
|
1099
|
-
totalDurationMs: durationMs,
|
|
1100
1263
|
...parentCallId === void 0 ? {} : { parentCallId }
|
|
1101
1264
|
};
|
|
1102
|
-
|
|
1265
|
+
appendSuppressedPlaceholder(recording, group.suppressed);
|
|
1103
1266
|
return;
|
|
1104
1267
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1268
|
+
const suppressed = group.suppressed;
|
|
1269
|
+
suppressed.count++;
|
|
1270
|
+
suppressed.lastTimestampNs = endedAtNs;
|
|
1271
|
+
suppressed.totalDurationNs += durationNs;
|
|
1272
|
+
if (!suppressed.retainedSlot) {
|
|
1273
|
+
appendSuppressedPlaceholder(recording, suppressed);
|
|
1274
|
+
}
|
|
1109
1275
|
};
|
|
1110
1276
|
const finish = (store, metadata) => {
|
|
1111
1277
|
const { recording } = store;
|
|
@@ -1134,7 +1300,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1134
1300
|
},
|
|
1135
1301
|
events: new CallAwareEventBuffer(maxEventsPerExecution),
|
|
1136
1302
|
functions: /* @__PURE__ */ new Map(),
|
|
1137
|
-
repeatedCalls:
|
|
1303
|
+
repeatedCalls: [],
|
|
1138
1304
|
capturedErrors: /* @__PURE__ */ new WeakMap(),
|
|
1139
1305
|
nextCallId: 0,
|
|
1140
1306
|
finished: false,
|
|
@@ -1202,52 +1368,74 @@ var createExecutionRecorder = (options) => {
|
|
|
1202
1368
|
return void 0;
|
|
1203
1369
|
}
|
|
1204
1370
|
};
|
|
1205
|
-
const
|
|
1371
|
+
const runPreparedFunction = (plan, args, callback) => {
|
|
1206
1372
|
const store = storage.getStore();
|
|
1207
1373
|
if (!store || store.recording.finished) return callback();
|
|
1208
|
-
|
|
1209
|
-
const functionId = definition.functionId;
|
|
1374
|
+
stats.functionCallsObserved++;
|
|
1210
1375
|
const recorderStartedAtNs = process.hrtime.bigint();
|
|
1211
|
-
if (store.suppressCapture ||
|
|
1376
|
+
if (store.suppressCapture || plan.excluded) {
|
|
1377
|
+
stats.functionCallsExcluded++;
|
|
1212
1378
|
return storage.run({ ...store, suppressCapture: true }, () => {
|
|
1213
1379
|
addRecorderTime(store.recording, process.hrtime.bigint() - recorderStartedAtNs);
|
|
1214
1380
|
return callback();
|
|
1215
1381
|
});
|
|
1216
1382
|
}
|
|
1217
|
-
store.recording.functions.set(functionId, definition);
|
|
1218
1383
|
const parentCallId = store.currentCallId;
|
|
1219
|
-
const group = repeatedCallGroup(store.recording,
|
|
1384
|
+
const group = repeatedCallGroup(store.recording, plan.runtimePlanId, parentCallId);
|
|
1220
1385
|
group.observedCalls++;
|
|
1386
|
+
if (group.observedCalls === 1) {
|
|
1387
|
+
store.recording.functions.set(plan.functionId, plan.definition);
|
|
1388
|
+
}
|
|
1221
1389
|
if (group.observedCalls > maxCapturedCallsPerFunction) {
|
|
1222
1390
|
const startedAtNs2 = process.hrtime.bigint();
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1391
|
+
try {
|
|
1392
|
+
const result = callback();
|
|
1393
|
+
if (isPromiseLike(result)) {
|
|
1394
|
+
addRecorderTime(store.recording, startedAtNs2 - recorderStartedAtNs);
|
|
1395
|
+
return result.then(
|
|
1396
|
+
(value) => {
|
|
1397
|
+
const callbackEndedAtNs2 = process.hrtime.bigint();
|
|
1398
|
+
recordSuppressedCall(
|
|
1399
|
+
store.recording,
|
|
1400
|
+
group,
|
|
1401
|
+
plan.functionId,
|
|
1402
|
+
parentCallId,
|
|
1403
|
+
startedAtNs2,
|
|
1404
|
+
callbackEndedAtNs2
|
|
1405
|
+
);
|
|
1406
|
+
addRecorderTime(store.recording, process.hrtime.bigint() - callbackEndedAtNs2);
|
|
1407
|
+
return value;
|
|
1408
|
+
},
|
|
1409
|
+
(error) => {
|
|
1410
|
+
const callbackEndedAtNs2 = process.hrtime.bigint();
|
|
1411
|
+
captureCompletedThrow(store.recording, plan, args, parentCallId, startedAtNs2, error);
|
|
1412
|
+
addRecorderTime(store.recording, process.hrtime.bigint() - callbackEndedAtNs2);
|
|
1413
|
+
throw error;
|
|
1414
|
+
}
|
|
1415
|
+
);
|
|
1416
|
+
}
|
|
1417
|
+
const callbackEndedAtNs = process.hrtime.bigint();
|
|
1418
|
+
recordSuppressedCall(
|
|
1238
1419
|
store.recording,
|
|
1239
|
-
|
|
1240
|
-
|
|
1420
|
+
group,
|
|
1421
|
+
plan.functionId,
|
|
1241
1422
|
parentCallId,
|
|
1242
1423
|
startedAtNs2,
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
)
|
|
1246
|
-
|
|
1424
|
+
callbackEndedAtNs
|
|
1425
|
+
);
|
|
1426
|
+
addRecorderTime(store.recording, startedAtNs2 - recorderStartedAtNs);
|
|
1427
|
+
addRecorderTime(store.recording, process.hrtime.bigint() - callbackEndedAtNs);
|
|
1428
|
+
return result;
|
|
1429
|
+
} catch (error) {
|
|
1430
|
+
const callbackEndedAtNs = process.hrtime.bigint();
|
|
1431
|
+
captureCompletedThrow(store.recording, plan, args, parentCallId, startedAtNs2, error);
|
|
1432
|
+
addRecorderTime(store.recording, startedAtNs2 - recorderStartedAtNs);
|
|
1433
|
+
addRecorderTime(store.recording, process.hrtime.bigint() - callbackEndedAtNs);
|
|
1434
|
+
throw error;
|
|
1435
|
+
}
|
|
1247
1436
|
}
|
|
1248
1437
|
const startedAtNs = process.hrtime.bigint();
|
|
1249
1438
|
if (!store.recording.events.canStartCall()) {
|
|
1250
|
-
const startedAtMs = elapsedMs(store.recording.startedAtNs, startedAtNs);
|
|
1251
1439
|
const captureSuccess = () => {
|
|
1252
1440
|
const droppedBefore = store.recording.events.dropped;
|
|
1253
1441
|
store.recording.events.dropCall();
|
|
@@ -1260,19 +1448,11 @@ var createExecutionRecorder = (options) => {
|
|
|
1260
1448
|
recorderStartedAtNs,
|
|
1261
1449
|
callback,
|
|
1262
1450
|
captureSuccess,
|
|
1263
|
-
(error) => captureCompletedThrow(
|
|
1264
|
-
store.recording,
|
|
1265
|
-
functionId,
|
|
1266
|
-
args,
|
|
1267
|
-
parentCallId,
|
|
1268
|
-
startedAtNs,
|
|
1269
|
-
startedAtMs,
|
|
1270
|
-
error
|
|
1271
|
-
)
|
|
1451
|
+
(error) => captureCompletedThrow(store.recording, plan, args, parentCallId, startedAtNs, error)
|
|
1272
1452
|
)
|
|
1273
1453
|
);
|
|
1274
1454
|
}
|
|
1275
|
-
if (!memoryBudget.reserveCall(store.recording.memory,
|
|
1455
|
+
if (!memoryBudget.reserveCall(store.recording.memory, plan.estimatedCallBytes)) {
|
|
1276
1456
|
return observeRecordedCallback(
|
|
1277
1457
|
store.recording,
|
|
1278
1458
|
recorderStartedAtNs,
|
|
@@ -1286,7 +1466,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1286
1466
|
const enter = {
|
|
1287
1467
|
type: "function_enter",
|
|
1288
1468
|
callId,
|
|
1289
|
-
functionId,
|
|
1469
|
+
functionId: plan.functionId,
|
|
1290
1470
|
timestampMs: elapsedMs(store.recording.startedAtNs),
|
|
1291
1471
|
args: Array.from(args, (argument) => captureValue(store.recording, argument)),
|
|
1292
1472
|
...parentCallId === void 0 ? {} : { parentCallId }
|
|
@@ -1307,6 +1487,7 @@ var createExecutionRecorder = (options) => {
|
|
|
1307
1487
|
durationMs: elapsedMs(startedAtNs),
|
|
1308
1488
|
returnValue: captureValue(store.recording, value)
|
|
1309
1489
|
});
|
|
1490
|
+
stats.functionCallsCaptured++;
|
|
1310
1491
|
},
|
|
1311
1492
|
(error) => {
|
|
1312
1493
|
const capturedError = serializedError(store.recording, error, callId);
|
|
@@ -1317,16 +1498,24 @@ var createExecutionRecorder = (options) => {
|
|
|
1317
1498
|
durationMs: elapsedMs(startedAtNs),
|
|
1318
1499
|
error: capturedError.value
|
|
1319
1500
|
});
|
|
1501
|
+
stats.functionCallsCaptured++;
|
|
1320
1502
|
capturedError.commit();
|
|
1321
1503
|
rememberError(error, store.recording);
|
|
1322
1504
|
}
|
|
1323
1505
|
)
|
|
1324
1506
|
);
|
|
1325
1507
|
};
|
|
1508
|
+
const runFunction = (input, args, callback) => runPreparedFunction(preparePlan(normalizeFunctionDefinition(input)), args, callback);
|
|
1509
|
+
const runFunctionPlans = (plans, planIndex, args, callback) => {
|
|
1510
|
+
const plan = preparedPlansFor(plans)[planIndex];
|
|
1511
|
+
return plan ? runPreparedFunction(plan, args, callback) : callback();
|
|
1512
|
+
};
|
|
1513
|
+
const prepareFunctionPlans = (plans) => preparedPlansFor(plans);
|
|
1326
1514
|
function trace(input, fn) {
|
|
1327
1515
|
const definition = typeof input === "string" ? manualFunctionDefinition(input) : { ...input, functionId: runtimeFunctionId2(input) };
|
|
1516
|
+
const plan = preparePlan(definition);
|
|
1328
1517
|
return function traced(...args) {
|
|
1329
|
-
return
|
|
1518
|
+
return runPreparedFunction(plan, args, () => fn.apply(this, args));
|
|
1330
1519
|
};
|
|
1331
1520
|
}
|
|
1332
1521
|
return {
|
|
@@ -1334,11 +1523,70 @@ var createExecutionRecorder = (options) => {
|
|
|
1334
1523
|
run,
|
|
1335
1524
|
getErrorState,
|
|
1336
1525
|
runFunction,
|
|
1526
|
+
runFunctionPlans,
|
|
1527
|
+
prepareFunctionPlans,
|
|
1528
|
+
runPreparedFunction,
|
|
1337
1529
|
trace,
|
|
1338
1530
|
getStats: () => ({ ...stats, ...memoryBudget.getStats() })
|
|
1339
1531
|
};
|
|
1340
1532
|
};
|
|
1341
1533
|
|
|
1534
|
+
// src/execution-recorder/automatic-runtime.ts
|
|
1535
|
+
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
|
|
1536
|
+
var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
|
|
1537
|
+
var passthrough = (_planIndex, _args, callback) => callback();
|
|
1538
|
+
var bindPassthrough = (module) => {
|
|
1539
|
+
module.cell.run = passthrough;
|
|
1540
|
+
};
|
|
1541
|
+
var isRegistry = (value) => Array.isArray(Reflect.get(value, "runtimes")) && Array.isArray(Reflect.get(value, "modules")) && typeof Reflect.get(value, "register") === "function";
|
|
1542
|
+
var registerModule = (registry, plans) => {
|
|
1543
|
+
const cell = { run: passthrough };
|
|
1544
|
+
const module = { plans, cell };
|
|
1545
|
+
registry.modules.push(module);
|
|
1546
|
+
registry.runtimes.at(-1)?.bind(module);
|
|
1547
|
+
return (planIndex, args, callback) => cell.run(planIndex, args, callback);
|
|
1548
|
+
};
|
|
1549
|
+
var createRegistry = () => {
|
|
1550
|
+
const registry = {
|
|
1551
|
+
runtimes: [],
|
|
1552
|
+
modules: [],
|
|
1553
|
+
register(plans) {
|
|
1554
|
+
return registerModule(registry, plans);
|
|
1555
|
+
}
|
|
1556
|
+
};
|
|
1557
|
+
return registry;
|
|
1558
|
+
};
|
|
1559
|
+
var getAutomaticRuntimeRegistry = () => {
|
|
1560
|
+
const existing = Reflect.get(globalThis, runtimeSymbol);
|
|
1561
|
+
if (existing && typeof existing === "object" && isRegistry(existing)) return existing;
|
|
1562
|
+
const registry = createRegistry();
|
|
1563
|
+
Reflect.set(globalThis, runtimeSymbol, registry);
|
|
1564
|
+
return registry;
|
|
1565
|
+
};
|
|
1566
|
+
var installAutomaticExecutionRuntime = (execution) => {
|
|
1567
|
+
const registry = getAutomaticRuntimeRegistry();
|
|
1568
|
+
const runtime = {
|
|
1569
|
+
bind(module) {
|
|
1570
|
+
const prepared = execution.prepareFunctionPlans(module.plans);
|
|
1571
|
+
module.cell.run = (planIndex, args, callback) => {
|
|
1572
|
+
const plan = prepared[planIndex];
|
|
1573
|
+
return plan ? execution.runPreparedFunction(plan, args, callback) : callback();
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
registry.runtimes.push(runtime);
|
|
1578
|
+
for (const module of registry.modules) runtime.bind(module);
|
|
1579
|
+
return () => {
|
|
1580
|
+
const index = registry.runtimes.lastIndexOf(runtime);
|
|
1581
|
+
if (index >= 0) registry.runtimes.splice(index, 1);
|
|
1582
|
+
const current = registry.runtimes.at(-1);
|
|
1583
|
+
for (const module of registry.modules) {
|
|
1584
|
+
if (current) current.bind(module);
|
|
1585
|
+
else bindPassthrough(module);
|
|
1586
|
+
}
|
|
1587
|
+
};
|
|
1588
|
+
};
|
|
1589
|
+
|
|
1342
1590
|
// src/install-global-handlers.ts
|
|
1343
1591
|
var removeProcessListener = process.off.bind(process);
|
|
1344
1592
|
var installGlobalHandlers = (client, options = {}) => {
|
|
@@ -1388,6 +1636,7 @@ var installGlobalHandlers = (client, options = {}) => {
|
|
|
1388
1636
|
};
|
|
1389
1637
|
|
|
1390
1638
|
// src/rasputin-init.ts
|
|
1639
|
+
import { join } from "node:path";
|
|
1391
1640
|
import {
|
|
1392
1641
|
createClient,
|
|
1393
1642
|
isClientEnabled
|
|
@@ -1395,7 +1644,7 @@ import {
|
|
|
1395
1644
|
|
|
1396
1645
|
// src/sdk-meta.ts
|
|
1397
1646
|
var SDK_NAME = "@rasputin-ai/node";
|
|
1398
|
-
var SDK_VERSION = "0.
|
|
1647
|
+
var SDK_VERSION = "0.5.0-alpha.2";
|
|
1399
1648
|
|
|
1400
1649
|
// src/rasputin-init.ts
|
|
1401
1650
|
var withExecution = (client, execution, installRuntime, manifest) => {
|
|
@@ -1436,6 +1685,9 @@ var RasputinInit = (options) => {
|
|
|
1436
1685
|
...options.executionRecorder,
|
|
1437
1686
|
enabled: recorderEnabled
|
|
1438
1687
|
});
|
|
1688
|
+
loadInstrumentationManifestIfPresent(
|
|
1689
|
+
options.instrumentationManifestPath ?? process.env.RASPUTIN_INSTRUMENTATION_MANIFEST ?? join(process.cwd(), ".rasputin", "instrumentation-manifest.json")
|
|
1690
|
+
);
|
|
1439
1691
|
const wrappedClient = withExecution(client, execution, recorderEnabled, {
|
|
1440
1692
|
projectApiKey: options.projectApiKey,
|
|
1441
1693
|
release: options.release,
|