elasticdash-sdk 0.2.0
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/LICENSE +21 -0
- package/README.md +775 -0
- package/dist/browser-ui.d.ts +43 -0
- package/dist/browser-ui.d.ts.map +1 -0
- package/dist/browser-ui.js +246 -0
- package/dist/browser-ui.js.map +1 -0
- package/dist/capture/event.d.ts +33 -0
- package/dist/capture/event.d.ts.map +1 -0
- package/dist/capture/event.js +2 -0
- package/dist/capture/event.js.map +1 -0
- package/dist/capture/index.d.ts +4 -0
- package/dist/capture/index.d.ts.map +1 -0
- package/dist/capture/index.js +4 -0
- package/dist/capture/index.js.map +1 -0
- package/dist/capture/recorder.d.ts +24 -0
- package/dist/capture/recorder.d.ts.map +1 -0
- package/dist/capture/recorder.js +46 -0
- package/dist/capture/recorder.js.map +1 -0
- package/dist/capture/replay.d.ts +20 -0
- package/dist/capture/replay.d.ts.map +1 -0
- package/dist/capture/replay.js +47 -0
- package/dist/capture/replay.js.map +1 -0
- package/dist/ci/api-client.d.ts +38 -0
- package/dist/ci/api-client.d.ts.map +1 -0
- package/dist/ci/api-client.js +96 -0
- package/dist/ci/api-client.js.map +1 -0
- package/dist/ci/benchmark.d.ts +33 -0
- package/dist/ci/benchmark.d.ts.map +1 -0
- package/dist/ci/benchmark.js +213 -0
- package/dist/ci/benchmark.js.map +1 -0
- package/dist/ci/ed-runner.d.ts +48 -0
- package/dist/ci/ed-runner.d.ts.map +1 -0
- package/dist/ci/ed-runner.js +260 -0
- package/dist/ci/ed-runner.js.map +1 -0
- package/dist/ci/executor.d.ts +13 -0
- package/dist/ci/executor.d.ts.map +1 -0
- package/dist/ci/executor.js +542 -0
- package/dist/ci/executor.js.map +1 -0
- package/dist/ci/git-info.d.ts +17 -0
- package/dist/ci/git-info.d.ts.map +1 -0
- package/dist/ci/git-info.js +102 -0
- package/dist/ci/git-info.js.map +1 -0
- package/dist/ci/index.d.ts +6 -0
- package/dist/ci/index.d.ts.map +1 -0
- package/dist/ci/index.js +4 -0
- package/dist/ci/index.js.map +1 -0
- package/dist/ci/measurement.d.ts +9 -0
- package/dist/ci/measurement.d.ts.map +1 -0
- package/dist/ci/measurement.js +15 -0
- package/dist/ci/measurement.js.map +1 -0
- package/dist/ci/replay.d.ts +31 -0
- package/dist/ci/replay.d.ts.map +1 -0
- package/dist/ci/replay.js +96 -0
- package/dist/ci/replay.js.map +1 -0
- package/dist/ci/reporters/default.d.ts +8 -0
- package/dist/ci/reporters/default.d.ts.map +1 -0
- package/dist/ci/reporters/default.js +46 -0
- package/dist/ci/reporters/default.js.map +1 -0
- package/dist/ci/reporters/index.d.ts +8 -0
- package/dist/ci/reporters/index.d.ts.map +1 -0
- package/dist/ci/reporters/index.js +14 -0
- package/dist/ci/reporters/index.js.map +1 -0
- package/dist/ci/reporters/json.d.ts +8 -0
- package/dist/ci/reporters/json.d.ts.map +1 -0
- package/dist/ci/reporters/json.js +14 -0
- package/dist/ci/reporters/json.js.map +1 -0
- package/dist/ci/reporters/junit.d.ts +8 -0
- package/dist/ci/reporters/junit.d.ts.map +1 -0
- package/dist/ci/reporters/junit.js +48 -0
- package/dist/ci/reporters/junit.js.map +1 -0
- package/dist/ci/runner.d.ts +3 -0
- package/dist/ci/runner.d.ts.map +1 -0
- package/dist/ci/runner.js +187 -0
- package/dist/ci/runner.js.map +1 -0
- package/dist/ci/test-discovery.d.ts +5 -0
- package/dist/ci/test-discovery.d.ts.map +1 -0
- package/dist/ci/test-discovery.js +11 -0
- package/dist/ci/test-discovery.js.map +1 -0
- package/dist/ci/test-loader.d.ts +19 -0
- package/dist/ci/test-loader.d.ts.map +1 -0
- package/dist/ci/test-loader.js +149 -0
- package/dist/ci/test-loader.js.map +1 -0
- package/dist/ci/test-registry.d.ts +42 -0
- package/dist/ci/test-registry.d.ts.map +1 -0
- package/dist/ci/test-registry.js +18 -0
- package/dist/ci/test-registry.js.map +1 -0
- package/dist/ci/trace-schema.d.ts +30 -0
- package/dist/ci/trace-schema.d.ts.map +1 -0
- package/dist/ci/trace-schema.js +66 -0
- package/dist/ci/trace-schema.js.map +1 -0
- package/dist/ci/trace-writer.d.ts +16 -0
- package/dist/ci/trace-writer.d.ts.map +1 -0
- package/dist/ci/trace-writer.js +108 -0
- package/dist/ci/trace-writer.js.map +1 -0
- package/dist/ci/types.d.ts +108 -0
- package/dist/ci/types.d.ts.map +1 -0
- package/dist/ci/types.js +3 -0
- package/dist/ci/types.js.map +1 -0
- package/dist/ci/upload-client.d.ts +74 -0
- package/dist/ci/upload-client.d.ts.map +1 -0
- package/dist/ci/upload-client.js +195 -0
- package/dist/ci/upload-client.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +716 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/agent-state.d.ts +47 -0
- package/dist/core/agent-state.d.ts.map +1 -0
- package/dist/core/agent-state.js +137 -0
- package/dist/core/agent-state.js.map +1 -0
- package/dist/core/judge-utils.d.ts +22 -0
- package/dist/core/judge-utils.d.ts.map +1 -0
- package/dist/core/judge-utils.js +211 -0
- package/dist/core/judge-utils.js.map +1 -0
- package/dist/core/registry.d.ts +28 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +52 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/dashboard-server.d.ts +65 -0
- package/dist/dashboard-server.d.ts.map +1 -0
- package/dist/dashboard-server.js +3940 -0
- package/dist/dashboard-server.js.map +1 -0
- package/dist/execution/tool-runner.d.ts +26 -0
- package/dist/execution/tool-runner.d.ts.map +1 -0
- package/dist/execution/tool-runner.js +316 -0
- package/dist/execution/tool-runner.js.map +1 -0
- package/dist/html/dashboard.html +2218 -0
- package/dist/http.d.ts +14 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +13 -0
- package/dist/http.js.map +1 -0
- package/dist/index.cjs +8102 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +67 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors/ai-interceptor.d.ts +26 -0
- package/dist/interceptors/ai-interceptor.d.ts.map +1 -0
- package/dist/interceptors/ai-interceptor.js +756 -0
- package/dist/interceptors/ai-interceptor.js.map +1 -0
- package/dist/interceptors/db-auto.d.ts +8 -0
- package/dist/interceptors/db-auto.d.ts.map +1 -0
- package/dist/interceptors/db-auto.js +217 -0
- package/dist/interceptors/db-auto.js.map +1 -0
- package/dist/interceptors/db.d.ts +23 -0
- package/dist/interceptors/db.d.ts.map +1 -0
- package/dist/interceptors/db.js +137 -0
- package/dist/interceptors/db.js.map +1 -0
- package/dist/interceptors/http.d.ts +28 -0
- package/dist/interceptors/http.d.ts.map +1 -0
- package/dist/interceptors/http.js +356 -0
- package/dist/interceptors/http.js.map +1 -0
- package/dist/interceptors/side-effects.d.ts +7 -0
- package/dist/interceptors/side-effects.d.ts.map +1 -0
- package/dist/interceptors/side-effects.js +72 -0
- package/dist/interceptors/side-effects.js.map +1 -0
- package/dist/interceptors/telemetry-push.d.ts +142 -0
- package/dist/interceptors/telemetry-push.d.ts.map +1 -0
- package/dist/interceptors/telemetry-push.js +463 -0
- package/dist/interceptors/telemetry-push.js.map +1 -0
- package/dist/interceptors/tool.d.ts +2 -0
- package/dist/interceptors/tool.d.ts.map +1 -0
- package/dist/interceptors/tool.js +274 -0
- package/dist/interceptors/tool.js.map +1 -0
- package/dist/interceptors/workflow-ai.d.ts +5 -0
- package/dist/interceptors/workflow-ai.d.ts.map +1 -0
- package/dist/interceptors/workflow-ai.js +382 -0
- package/dist/interceptors/workflow-ai.js.map +1 -0
- package/dist/internals/conditional-recorder.d.ts +21 -0
- package/dist/internals/conditional-recorder.d.ts.map +1 -0
- package/dist/internals/conditional-recorder.js +54 -0
- package/dist/internals/conditional-recorder.js.map +1 -0
- package/dist/internals/mock-resolver.d.ts +146 -0
- package/dist/internals/mock-resolver.d.ts.map +1 -0
- package/dist/internals/mock-resolver.js +427 -0
- package/dist/internals/mock-resolver.js.map +1 -0
- package/dist/matchers/index.d.ts +96 -0
- package/dist/matchers/index.d.ts.map +1 -0
- package/dist/matchers/index.js +668 -0
- package/dist/matchers/index.js.map +1 -0
- package/dist/observability.d.ts +82 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +471 -0
- package/dist/observability.js.map +1 -0
- package/dist/portal-executor.d.ts +30 -0
- package/dist/portal-executor.d.ts.map +1 -0
- package/dist/portal-executor.js +324 -0
- package/dist/portal-executor.js.map +1 -0
- package/dist/portal-server.d.ts +3 -0
- package/dist/portal-server.d.ts.map +1 -0
- package/dist/portal-server.js +279 -0
- package/dist/portal-server.js.map +1 -0
- package/dist/proxy/llm-capture.d.ts +14 -0
- package/dist/proxy/llm-capture.d.ts.map +1 -0
- package/dist/proxy/llm-capture.js +264 -0
- package/dist/proxy/llm-capture.js.map +1 -0
- package/dist/reporter.d.ts +3 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +72 -0
- package/dist/reporter.js.map +1 -0
- package/dist/runWorkflowSubprocess.d.ts +14 -0
- package/dist/runWorkflowSubprocess.d.ts.map +1 -0
- package/dist/runWorkflowSubprocess.js +66 -0
- package/dist/runWorkflowSubprocess.js.map +1 -0
- package/dist/runner.d.ts +16 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +138 -0
- package/dist/runner.js.map +1 -0
- package/dist/socket-connector.d.ts +22 -0
- package/dist/socket-connector.d.ts.map +1 -0
- package/dist/socket-connector.js +104 -0
- package/dist/socket-connector.js.map +1 -0
- package/dist/telemetry-batcher.d.ts +56 -0
- package/dist/telemetry-batcher.d.ts.map +1 -0
- package/dist/telemetry-batcher.js +143 -0
- package/dist/telemetry-batcher.js.map +1 -0
- package/dist/test-setup.d.ts +12 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/test-setup.js +13 -0
- package/dist/test-setup.js.map +1 -0
- package/dist/tool-registry.d.ts +31 -0
- package/dist/tool-registry.d.ts.map +1 -0
- package/dist/tool-registry.js +73 -0
- package/dist/tool-registry.js.map +1 -0
- package/dist/tool-runner-worker.d.ts +2 -0
- package/dist/tool-runner-worker.d.ts.map +1 -0
- package/dist/tool-runner-worker.js +215 -0
- package/dist/tool-runner-worker.js.map +1 -0
- package/dist/trace-adapter/context.d.ts +72 -0
- package/dist/trace-adapter/context.d.ts.map +1 -0
- package/dist/trace-adapter/context.js +80 -0
- package/dist/trace-adapter/context.js.map +1 -0
- package/dist/tracing.d.ts +2 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +59 -0
- package/dist/tracing.js.map +1 -0
- package/dist/trigger-executor.d.ts +12 -0
- package/dist/trigger-executor.d.ts.map +1 -0
- package/dist/trigger-executor.js +130 -0
- package/dist/trigger-executor.js.map +1 -0
- package/dist/types/portal.d.ts +76 -0
- package/dist/types/portal.d.ts.map +1 -0
- package/dist/types/portal.js +2 -0
- package/dist/types/portal.js.map +1 -0
- package/dist/utils/debug.d.ts +3 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +8 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/license-error.d.ts +23 -0
- package/dist/utils/license-error.d.ts.map +1 -0
- package/dist/utils/license-error.js +42 -0
- package/dist/utils/license-error.js.map +1 -0
- package/dist/utils/redact.d.ts +7 -0
- package/dist/utils/redact.d.ts.map +1 -0
- package/dist/utils/redact.js +26 -0
- package/dist/utils/redact.js.map +1 -0
- package/dist/workflow-runner-worker.d.ts +2 -0
- package/dist/workflow-runner-worker.d.ts.map +1 -0
- package/dist/workflow-runner-worker.js +329 -0
- package/dist/workflow-runner-worker.js.map +1 -0
- package/dist/workflow-runner.d.ts +14 -0
- package/dist/workflow-runner.d.ts.map +1 -0
- package/dist/workflow-runner.js +34 -0
- package/dist/workflow-runner.js.map +1 -0
- package/docs/agent-coding-instructions.md +138 -0
- package/docs/agent-integration-guide.md +564 -0
- package/docs/agents.md +140 -0
- package/docs/dashboard.md +394 -0
- package/docs/deno.md +69 -0
- package/docs/instrumentation.md +424 -0
- package/docs/langfuse-trace-structure.md +145 -0
- package/docs/matchers.md +173 -0
- package/docs/observability_contract.md +192 -0
- package/docs/observability_mode.md +195 -0
- package/docs/quickstart.md +621 -0
- package/docs/security-compliance.md +566 -0
- package/docs/test-writing-guidelines.md +444 -0
- package/docs/tools.md +165 -0
- package/docs/workflow-modes.md +253 -0
- package/package.json +76 -0
- package/src/browser-ui.ts +281 -0
- package/src/capture/event.ts +30 -0
- package/src/capture/index.ts +3 -0
- package/src/capture/recorder.ts +62 -0
- package/src/capture/replay.ts +55 -0
- package/src/ci/api-client.ts +136 -0
- package/src/ci/benchmark.ts +257 -0
- package/src/ci/ed-runner.ts +351 -0
- package/src/ci/executor.ts +671 -0
- package/src/ci/git-info.ts +127 -0
- package/src/ci/index.ts +5 -0
- package/src/ci/measurement.ts +25 -0
- package/src/ci/replay.ts +127 -0
- package/src/ci/reporters/default.ts +50 -0
- package/src/ci/reporters/index.ts +21 -0
- package/src/ci/reporters/json.ts +18 -0
- package/src/ci/reporters/junit.ts +61 -0
- package/src/ci/runner.ts +208 -0
- package/src/ci/test-discovery.ts +16 -0
- package/src/ci/test-loader.ts +187 -0
- package/src/ci/test-registry.ts +62 -0
- package/src/ci/trace-schema.ts +96 -0
- package/src/ci/trace-writer.ts +107 -0
- package/src/ci/types.ts +115 -0
- package/src/ci/upload-client.ts +300 -0
- package/src/cli.ts +811 -0
- package/src/core/agent-state.ts +162 -0
- package/src/core/judge-utils.ts +232 -0
- package/src/core/registry.ts +92 -0
- package/src/dashboard-server.ts +2047 -0
- package/src/execution/tool-runner.ts +352 -0
- package/src/html/dashboard.html +2218 -0
- package/src/http.ts +13 -0
- package/src/index.ts +138 -0
- package/src/interceptors/ai-interceptor.ts +798 -0
- package/src/interceptors/db-auto.ts +243 -0
- package/src/interceptors/db.ts +156 -0
- package/src/interceptors/http.ts +393 -0
- package/src/interceptors/side-effects.ts +83 -0
- package/src/interceptors/telemetry-push.ts +537 -0
- package/src/interceptors/tool.ts +287 -0
- package/src/interceptors/workflow-ai.ts +419 -0
- package/src/internals/conditional-recorder.ts +63 -0
- package/src/internals/mock-resolver.ts +492 -0
- package/src/matchers/index.ts +824 -0
- package/src/observability.ts +501 -0
- package/src/portal-executor.ts +355 -0
- package/src/portal-server.ts +304 -0
- package/src/proxy/llm-capture.ts +301 -0
- package/src/reporter.ts +81 -0
- package/src/runWorkflowSubprocess.ts +74 -0
- package/src/runner.ts +178 -0
- package/src/socket-connector.ts +117 -0
- package/src/telemetry-batcher.ts +191 -0
- package/src/test-setup.ts +16 -0
- package/src/tool-registry.ts +94 -0
- package/src/tool-runner-worker.ts +244 -0
- package/src/trace-adapter/context.ts +156 -0
- package/src/tracing.ts +62 -0
- package/src/trigger-executor.ts +171 -0
- package/src/types/agent.d.ts +63 -0
- package/src/types/expect.d.ts +81 -0
- package/src/types/modules.d.ts +2 -0
- package/src/types/portal.ts +69 -0
- package/src/utils/debug.ts +8 -0
- package/src/utils/license-error.ts +43 -0
- package/src/utils/redact.ts +25 -0
- package/src/workflow-runner-worker.ts +386 -0
- package/src/workflow-runner.ts +58 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { getCaptureContext } from '../capture/recorder.js'
|
|
2
|
+
import { getCurrentTrace } from '../trace-adapter/context.js'
|
|
3
|
+
import { rawDateNow } from './side-effects.js'
|
|
4
|
+
import { getHttpRunContext, getHttpFrozenEvent, pushTelemetryEvent, tryAutoInitHttpContext, getObservabilityContext } from './telemetry-push.js'
|
|
5
|
+
|
|
6
|
+
type AnyFn = (...args: unknown[]) => unknown
|
|
7
|
+
|
|
8
|
+
interface MethodPatch {
|
|
9
|
+
proto: Record<string, unknown>
|
|
10
|
+
method: string
|
|
11
|
+
original: AnyFn
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const appliedPatches: MethodPatch[] = []
|
|
15
|
+
|
|
16
|
+
function toTraceArgs(input: unknown): Record<string, unknown> | undefined {
|
|
17
|
+
if (input && typeof input === 'object' && !Array.isArray(input)) {
|
|
18
|
+
return input as Record<string, unknown>
|
|
19
|
+
}
|
|
20
|
+
if (input === undefined) return undefined
|
|
21
|
+
return { value: input }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function wrapProtoMethod(proto: object, method: string, eventName: string): void {
|
|
25
|
+
const p = proto as Record<string, unknown>
|
|
26
|
+
if (typeof p[method] !== 'function') return
|
|
27
|
+
|
|
28
|
+
const original = p[method] as AnyFn
|
|
29
|
+
appliedPatches.push({ proto: p, method, original })
|
|
30
|
+
|
|
31
|
+
p[method] = function (this: unknown, ...args: unknown[]) {
|
|
32
|
+
// Skip callback-style calls to avoid breaking legacy APIs
|
|
33
|
+
if (args.length > 0 && typeof args[args.length - 1] === 'function') {
|
|
34
|
+
return original.apply(this, args)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const ctx = getCaptureContext()
|
|
38
|
+
const httpCtx = getHttpRunContext()
|
|
39
|
+
const obsCtx = getObservabilityContext()
|
|
40
|
+
const input = args.length === 1 ? args[0] : args
|
|
41
|
+
|
|
42
|
+
if (!ctx && !httpCtx && !obsCtx) return original.apply(this, args)
|
|
43
|
+
|
|
44
|
+
// Observability-only mode: record and push, no mocks/replay
|
|
45
|
+
if (!ctx && !httpCtx && obsCtx) {
|
|
46
|
+
const id = obsCtx.nextId()
|
|
47
|
+
const start = rawDateNow()
|
|
48
|
+
|
|
49
|
+
let result: unknown
|
|
50
|
+
try {
|
|
51
|
+
result = original.apply(this, args)
|
|
52
|
+
} catch (err) {
|
|
53
|
+
pushTelemetryEvent({ id, type: 'db', name: eventName, input, output: { error: String(err) }, timestamp: start, durationMs: rawDateNow() - start })
|
|
54
|
+
throw err
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (result != null && typeof (result as Promise<unknown>).then === 'function') {
|
|
58
|
+
return (result as Promise<unknown>)
|
|
59
|
+
.then((output: unknown) => {
|
|
60
|
+
pushTelemetryEvent({ id, type: 'db', name: eventName, input, output, timestamp: start, durationMs: rawDateNow() - start })
|
|
61
|
+
return output
|
|
62
|
+
})
|
|
63
|
+
.catch((err: unknown) => {
|
|
64
|
+
pushTelemetryEvent({ id, type: 'db', name: eventName, input, output: { error: String(err) }, timestamp: start, durationMs: rawDateNow() - start })
|
|
65
|
+
throw err
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
pushTelemetryEvent({ id, type: 'db', name: eventName, input, output: result, timestamp: start, durationMs: rawDateNow() - start })
|
|
70
|
+
return result
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// HTTP mode (no capture context): replay frozen events or execute live
|
|
74
|
+
if (!ctx && httpCtx) {
|
|
75
|
+
const id = httpCtx.nextId()
|
|
76
|
+
|
|
77
|
+
// Replay frozen step
|
|
78
|
+
const frozen = getHttpFrozenEvent(id)
|
|
79
|
+
if (frozen && frozen.type === 'db') {
|
|
80
|
+
pushTelemetryEvent(frozen)
|
|
81
|
+
return Promise.resolve(frozen.output)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Not frozen → execute live, push telemetry
|
|
85
|
+
const start = rawDateNow()
|
|
86
|
+
|
|
87
|
+
let result: unknown
|
|
88
|
+
try {
|
|
89
|
+
result = original.apply(this, args)
|
|
90
|
+
} catch (err) {
|
|
91
|
+
pushTelemetryEvent({ id, type: 'db', name: eventName, input, output: { error: String(err) }, timestamp: start, durationMs: rawDateNow() - start })
|
|
92
|
+
throw err
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (result != null && typeof (result as Promise<unknown>).then === 'function') {
|
|
96
|
+
return (result as Promise<unknown>)
|
|
97
|
+
.then((output: unknown) => {
|
|
98
|
+
pushTelemetryEvent({ id, type: 'db', name: eventName, input, output, timestamp: start, durationMs: rawDateNow() - start })
|
|
99
|
+
return output
|
|
100
|
+
})
|
|
101
|
+
.catch((err: unknown) => {
|
|
102
|
+
pushTelemetryEvent({ id, type: 'db', name: eventName, input, output: { error: String(err) }, timestamp: start, durationMs: rawDateNow() - start })
|
|
103
|
+
throw err
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
pushTelemetryEvent({ id, type: 'db', name: eventName, input, output: result, timestamp: start, durationMs: rawDateNow() - start })
|
|
108
|
+
return result
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Capture mode (enhanced with telemetry + TraceHandle)
|
|
112
|
+
const trace = getCurrentTrace()
|
|
113
|
+
const { recorder, replay } = ctx!
|
|
114
|
+
const id = recorder.nextId()
|
|
115
|
+
|
|
116
|
+
if (replay.shouldReplay(id)) {
|
|
117
|
+
const historicalEvent = replay.getRecordedEvent(id)
|
|
118
|
+
if (historicalEvent) recorder.record(historicalEvent)
|
|
119
|
+
if (httpCtx) pushTelemetryEvent(historicalEvent ?? { id, type: 'db', name: eventName, input, output: null, timestamp: rawDateNow(), durationMs: 0 })
|
|
120
|
+
const replayed = replay.getRecordedResult(id)
|
|
121
|
+
if (trace && typeof trace.recordToolCall === 'function') {
|
|
122
|
+
trace.recordToolCall({ name: eventName, args: toTraceArgs(input), result: replayed, workflowEventId: id })
|
|
123
|
+
}
|
|
124
|
+
return Promise.resolve(replayed)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const start = rawDateNow()
|
|
128
|
+
|
|
129
|
+
let result: unknown
|
|
130
|
+
try {
|
|
131
|
+
result = original.apply(this, args)
|
|
132
|
+
} catch (err) {
|
|
133
|
+
const durationMs = rawDateNow() - start
|
|
134
|
+
const event = { id, type: 'db' as const, name: eventName, input, output: { error: String(err) }, timestamp: start, durationMs }
|
|
135
|
+
recorder.record(event)
|
|
136
|
+
if (httpCtx) pushTelemetryEvent(event)
|
|
137
|
+
if (trace && typeof trace.recordToolCall === 'function') {
|
|
138
|
+
trace.recordToolCall({ name: eventName, args: toTraceArgs(input), result: { error: String(err) }, workflowEventId: id, durationMs })
|
|
139
|
+
}
|
|
140
|
+
throw err
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (result != null && typeof (result as Promise<unknown>).then === 'function') {
|
|
144
|
+
return (result as Promise<unknown>)
|
|
145
|
+
.then((output: unknown) => {
|
|
146
|
+
const durationMs = rawDateNow() - start
|
|
147
|
+
const event = { id, type: 'db' as const, name: eventName, input, output, timestamp: start, durationMs }
|
|
148
|
+
recorder.record(event)
|
|
149
|
+
if (httpCtx) pushTelemetryEvent(event)
|
|
150
|
+
if (trace && typeof trace.recordToolCall === 'function') {
|
|
151
|
+
trace.recordToolCall({ name: eventName, args: toTraceArgs(input), result: output, workflowEventId: id, durationMs })
|
|
152
|
+
}
|
|
153
|
+
return output
|
|
154
|
+
})
|
|
155
|
+
.catch((err: unknown) => {
|
|
156
|
+
const durationMs = rawDateNow() - start
|
|
157
|
+
const event = { id, type: 'db' as const, name: eventName, input, output: { error: String(err) }, timestamp: start, durationMs }
|
|
158
|
+
recorder.record(event)
|
|
159
|
+
if (httpCtx) pushTelemetryEvent(event)
|
|
160
|
+
if (trace && typeof trace.recordToolCall === 'function') {
|
|
161
|
+
trace.recordToolCall({ name: eventName, args: toTraceArgs(input), result: { error: String(err) }, workflowEventId: id, durationMs })
|
|
162
|
+
}
|
|
163
|
+
throw err
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Sync return (rare for DB calls)
|
|
168
|
+
const durationMs = rawDateNow() - start
|
|
169
|
+
const event = { id, type: 'db' as const, name: eventName, input, output: result, timestamp: start, durationMs }
|
|
170
|
+
recorder.record(event)
|
|
171
|
+
if (httpCtx) pushTelemetryEvent(event)
|
|
172
|
+
if (trace && typeof trace.recordToolCall === 'function') {
|
|
173
|
+
trace.recordToolCall({ name: eventName, args: toTraceArgs(input), result, workflowEventId: id, durationMs })
|
|
174
|
+
}
|
|
175
|
+
return result
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function tryPatchPg(): Promise<void> {
|
|
180
|
+
// @ts-ignore — optional peer dependency
|
|
181
|
+
const pgMod = await import('pg') as Record<string, unknown>
|
|
182
|
+
const pg = (pgMod.default as Record<string, unknown> | undefined) ?? pgMod
|
|
183
|
+
const Client = pg.Client as { prototype: object } | undefined
|
|
184
|
+
// Patch Client.prototype only — Pool.query delegates to Client internally
|
|
185
|
+
if (Client?.prototype) {
|
|
186
|
+
wrapProtoMethod(Client.prototype, 'query', 'pg.query')
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function tryPatchMysql2(): Promise<void> {
|
|
191
|
+
// @ts-ignore — optional peer dependency
|
|
192
|
+
const mod = await import('mysql2/promise') as Record<string, unknown>
|
|
193
|
+
const mysql2 = (mod.default as Record<string, unknown> | undefined) ?? mod
|
|
194
|
+
const Connection = mysql2.Connection as { prototype: object } | undefined
|
|
195
|
+
if (Connection?.prototype) {
|
|
196
|
+
wrapProtoMethod(Connection.prototype, 'query', 'mysql2.query')
|
|
197
|
+
wrapProtoMethod(Connection.prototype, 'execute', 'mysql2.execute')
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function tryPatchMongodb(): Promise<void> {
|
|
202
|
+
// @ts-ignore — optional peer dependency
|
|
203
|
+
const mongMod = await import('mongodb') as Record<string, unknown>
|
|
204
|
+
const Collection = (
|
|
205
|
+
mongMod.Collection ??
|
|
206
|
+
(mongMod.default as Record<string, unknown> | undefined)?.Collection
|
|
207
|
+
) as { prototype: object } | undefined
|
|
208
|
+
if (Collection?.prototype) {
|
|
209
|
+
for (const method of ['find', 'findOne', 'insertOne', 'updateOne', 'deleteOne', 'aggregate']) {
|
|
210
|
+
wrapProtoMethod(Collection.prototype, method, `mongodb.${method}`)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function tryPatchIoredis(): Promise<void> {
|
|
216
|
+
// @ts-ignore — optional peer dependency
|
|
217
|
+
const mod = await import('ioredis') as Record<string, unknown>
|
|
218
|
+
const Redis = (mod.default ?? mod) as { prototype: object } | undefined
|
|
219
|
+
if (Redis?.prototype) {
|
|
220
|
+
wrapProtoMethod(Redis.prototype, 'call', 'redis.call')
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Auto-instruments common DB driver prototypes. Safe to call when drivers are
|
|
226
|
+
* not installed — missing modules are silently skipped.
|
|
227
|
+
*/
|
|
228
|
+
export async function installDBAutoInterceptor(): Promise<void> {
|
|
229
|
+
await Promise.allSettled([
|
|
230
|
+
tryPatchPg(),
|
|
231
|
+
tryPatchMysql2(),
|
|
232
|
+
tryPatchMongodb(),
|
|
233
|
+
tryPatchIoredis(),
|
|
234
|
+
])
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Restores all patched DB driver prototypes to their originals. */
|
|
238
|
+
export function uninstallDBAutoInterceptor(): void {
|
|
239
|
+
for (const { proto, method, original } of appliedPatches) {
|
|
240
|
+
proto[method] = original
|
|
241
|
+
}
|
|
242
|
+
appliedPatches.length = 0
|
|
243
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { getCaptureContext } from '../capture/recorder.js'
|
|
2
|
+
import { getCurrentTrace } from '../trace-adapter/context.js'
|
|
3
|
+
import { rawDateNow } from './side-effects.js'
|
|
4
|
+
import { getHttpRunContext, getHttpFrozenEvent, pushTelemetryEvent, tryAutoInitHttpContext, getObservabilityContext } from './telemetry-push.js'
|
|
5
|
+
|
|
6
|
+
function toTraceArgs(input: unknown): Record<string, unknown> | undefined {
|
|
7
|
+
if (input && typeof input === 'object' && !Array.isArray(input)) {
|
|
8
|
+
return input as Record<string, unknown>
|
|
9
|
+
}
|
|
10
|
+
if (input === undefined) return undefined
|
|
11
|
+
return { value: input }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Wraps named methods on a DB client instance in-place so their calls are
|
|
16
|
+
* recorded as "db" events. Returns the same client object.
|
|
17
|
+
*
|
|
18
|
+
* Supports all three execution modes:
|
|
19
|
+
* - Capture mode: replay from TraceRecorder / ReplayController
|
|
20
|
+
* - HTTP mode: replay frozen events from dashboard, push telemetry
|
|
21
|
+
* - Observability mode: record and push telemetry
|
|
22
|
+
*
|
|
23
|
+
* @param client Any DB client (pg.Client, redis client, mongoose Model, etc.)
|
|
24
|
+
* @param methodNames Method names to wrap
|
|
25
|
+
* @param label Optional label prefix for event names (defaults to constructor name)
|
|
26
|
+
*/
|
|
27
|
+
export function wrapDB<T extends object>(
|
|
28
|
+
client: T,
|
|
29
|
+
methodNames: (keyof T & string)[],
|
|
30
|
+
label?: string,
|
|
31
|
+
): T {
|
|
32
|
+
const prefix = label ?? (client.constructor?.name ?? 'db')
|
|
33
|
+
|
|
34
|
+
for (const method of methodNames) {
|
|
35
|
+
const original = (client as Record<string, unknown>)[method]
|
|
36
|
+
if (typeof original !== 'function') continue
|
|
37
|
+
|
|
38
|
+
;(client as Record<string, unknown>)[method] = async (...args: unknown[]) => {
|
|
39
|
+
await tryAutoInitHttpContext()
|
|
40
|
+
const ctx = getCaptureContext()
|
|
41
|
+
const httpCtx = getHttpRunContext()
|
|
42
|
+
const obsCtx = getObservabilityContext()
|
|
43
|
+
const name = `${prefix}.${method}`
|
|
44
|
+
const input = args.length === 1 ? args[0] : args
|
|
45
|
+
|
|
46
|
+
if (!ctx && !httpCtx && !obsCtx) return (original as (...a: unknown[]) => unknown).apply(client, args)
|
|
47
|
+
|
|
48
|
+
// Observability-only mode: record and push, no mocks/replay
|
|
49
|
+
if (!ctx && !httpCtx && obsCtx) {
|
|
50
|
+
const id = obsCtx.nextId()
|
|
51
|
+
const start = rawDateNow()
|
|
52
|
+
try {
|
|
53
|
+
const output = await (original as (...a: unknown[]) => unknown).apply(client, args)
|
|
54
|
+
pushTelemetryEvent({ id, type: 'db', name, input, output, timestamp: start, durationMs: rawDateNow() - start })
|
|
55
|
+
return output
|
|
56
|
+
} catch (e) {
|
|
57
|
+
pushTelemetryEvent({ id, type: 'db', name, input, output: { error: String(e) }, timestamp: start, durationMs: rawDateNow() - start })
|
|
58
|
+
throw e
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// HTTP mode (no capture context): replay frozen events or execute live
|
|
63
|
+
if (!ctx && httpCtx) {
|
|
64
|
+
const id = httpCtx.nextId()
|
|
65
|
+
|
|
66
|
+
// Replay frozen step
|
|
67
|
+
const frozen = getHttpFrozenEvent(id)
|
|
68
|
+
if (frozen && frozen.type === 'db') {
|
|
69
|
+
pushTelemetryEvent(frozen)
|
|
70
|
+
return frozen.output
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Not frozen → execute live, push telemetry
|
|
74
|
+
const start = rawDateNow()
|
|
75
|
+
try {
|
|
76
|
+
const output = await (original as (...a: unknown[]) => unknown).apply(client, args)
|
|
77
|
+
pushTelemetryEvent({ id, type: 'db', name, input, output, timestamp: start, durationMs: rawDateNow() - start })
|
|
78
|
+
return output
|
|
79
|
+
} catch (e) {
|
|
80
|
+
pushTelemetryEvent({ id, type: 'db', name, input, output: { error: String(e) }, timestamp: start, durationMs: rawDateNow() - start })
|
|
81
|
+
throw e
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Capture mode
|
|
86
|
+
const trace = getCurrentTrace()
|
|
87
|
+
const { recorder, replay } = ctx!
|
|
88
|
+
const id = recorder.nextId()
|
|
89
|
+
|
|
90
|
+
if (replay.shouldReplay(id)) {
|
|
91
|
+
const historical = replay.getRecordedEvent(id)
|
|
92
|
+
if (historical) recorder.record(historical)
|
|
93
|
+
if (httpCtx) pushTelemetryEvent(historical ?? { id, type: 'db', name, input, output: null, timestamp: rawDateNow(), durationMs: 0 })
|
|
94
|
+
const replayed = replay.getRecordedResult(id)
|
|
95
|
+
if (trace && typeof trace.recordToolCall === 'function') {
|
|
96
|
+
trace.recordToolCall({ name, args: toTraceArgs(input), result: replayed, workflowEventId: id })
|
|
97
|
+
}
|
|
98
|
+
return replayed
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const start = rawDateNow()
|
|
102
|
+
try {
|
|
103
|
+
const output = await (original as (...a: unknown[]) => unknown).apply(client, args)
|
|
104
|
+
const durationMs = rawDateNow() - start
|
|
105
|
+
const event = { id, type: 'db' as const, name, input, output, timestamp: start, durationMs }
|
|
106
|
+
recorder.record(event)
|
|
107
|
+
if (httpCtx) pushTelemetryEvent(event)
|
|
108
|
+
if (trace && typeof trace.recordToolCall === 'function') {
|
|
109
|
+
trace.recordToolCall({ name, args: toTraceArgs(input), result: output, workflowEventId: id, durationMs })
|
|
110
|
+
}
|
|
111
|
+
return output
|
|
112
|
+
} catch (e) {
|
|
113
|
+
const durationMs = rawDateNow() - start
|
|
114
|
+
const event = { id, type: 'db' as const, name, input, output: { error: String(e) }, timestamp: start, durationMs }
|
|
115
|
+
recorder.record(event)
|
|
116
|
+
if (httpCtx) pushTelemetryEvent(event)
|
|
117
|
+
if (trace && typeof trace.recordToolCall === 'function') {
|
|
118
|
+
trace.recordToolCall({ name, args: toTraceArgs(input), result: { error: String(e) }, workflowEventId: id, durationMs })
|
|
119
|
+
}
|
|
120
|
+
throw e
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return client
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// --- Driver-specific convenience helpers ---
|
|
129
|
+
|
|
130
|
+
/** Wraps `query` on a pg.Client or pg.Pool instance. */
|
|
131
|
+
export function wrapPgClient<T extends object>(client: T): T {
|
|
132
|
+
return wrapDB(client, ['query'] as (keyof T & string)[], 'pg')
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Wraps `raw` on a knex instance. */
|
|
136
|
+
export function wrapKnex<T extends object>(knex: T): T {
|
|
137
|
+
return wrapDB(knex, ['raw'] as (keyof T & string)[], 'knex')
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Wraps common query methods on a MongoDB collection. */
|
|
141
|
+
export function wrapMongoCollection<T extends object>(collection: T): T {
|
|
142
|
+
return wrapDB(
|
|
143
|
+
collection,
|
|
144
|
+
['find', 'findOne', 'insertOne', 'updateOne', 'deleteOne'] as (keyof T & string)[],
|
|
145
|
+
'mongo',
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Wraps common methods on a Redis client. */
|
|
150
|
+
export function wrapRedisClient<T extends object>(client: T): T {
|
|
151
|
+
return wrapDB(
|
|
152
|
+
client,
|
|
153
|
+
['get', 'set', 'del', 'hget', 'hset'] as (keyof T & string)[],
|
|
154
|
+
'redis',
|
|
155
|
+
)
|
|
156
|
+
}
|