@browserbasehq/orca 3.2.0-preview.2 → 3.2.0-preview.4
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/dist/cjs/lib/v3/agent/AnthropicCUAClient.js +5 -5
- package/dist/cjs/lib/v3/agent/AnthropicCUAClient.js.map +1 -1
- package/dist/cjs/lib/v3/agent/GoogleCUAClient.js +5 -5
- package/dist/cjs/lib/v3/agent/GoogleCUAClient.js.map +1 -1
- package/dist/cjs/lib/v3/agent/OpenAICUAClient.js +5 -5
- package/dist/cjs/lib/v3/agent/OpenAICUAClient.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/act.js +1 -10
- package/dist/cjs/lib/v3/agent/tools/act.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/ariaTree.js +1 -12
- package/dist/cjs/lib/v3/agent/tools/ariaTree.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/braveSearch.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/browserbaseSearch.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/click.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/clickAndHold.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/dragAndDrop.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/extract.js +1 -10
- package/dist/cjs/lib/v3/agent/tools/extract.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/fillFormVision.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/fillform.js +1 -10
- package/dist/cjs/lib/v3/agent/tools/fillform.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/index.d.ts +2 -2
- package/dist/cjs/lib/v3/agent/tools/index.js +53 -5
- package/dist/cjs/lib/v3/agent/tools/index.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/keys.d.ts +1 -1
- package/dist/cjs/lib/v3/agent/tools/keys.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/type.js.map +1 -1
- package/dist/cjs/lib/v3/api.js +9 -2
- package/dist/cjs/lib/v3/api.js.map +1 -1
- package/dist/cjs/lib/v3/flowlogger/EventEmitter.d.ts +7 -0
- package/dist/cjs/lib/v3/flowlogger/EventEmitter.js +30 -0
- package/dist/cjs/lib/v3/flowlogger/EventEmitter.js.map +1 -0
- package/dist/cjs/lib/v3/flowlogger/EventSink.d.ts +44 -0
- package/dist/cjs/lib/v3/flowlogger/EventSink.js +217 -0
- package/dist/cjs/lib/v3/flowlogger/EventSink.js.map +1 -0
- package/dist/cjs/lib/v3/flowlogger/EventStore.d.ts +26 -0
- package/dist/cjs/lib/v3/flowlogger/EventStore.js +135 -0
- package/dist/cjs/lib/v3/flowlogger/EventStore.js.map +1 -0
- package/dist/{esm/lib/v3/flowLogger.d.ts → cjs/lib/v3/flowlogger/FlowLogger.d.ts} +32 -31
- package/dist/cjs/lib/v3/flowlogger/FlowLogger.js +591 -0
- package/dist/cjs/lib/v3/flowlogger/FlowLogger.js.map +1 -0
- package/dist/cjs/lib/v3/flowlogger/prettify.d.ts +6 -0
- package/dist/cjs/lib/v3/flowlogger/prettify.js +395 -0
- package/dist/cjs/lib/v3/flowlogger/prettify.js.map +1 -0
- package/dist/cjs/lib/v3/handlers/handlerUtils/actHandlerUtils.js +26 -28
- package/dist/cjs/lib/v3/handlers/handlerUtils/actHandlerUtils.js.map +1 -1
- package/dist/cjs/lib/v3/handlers/v3AgentHandler.js +2 -2
- package/dist/cjs/lib/v3/handlers/v3AgentHandler.js.map +1 -1
- package/dist/cjs/lib/v3/handlers/v3CuaAgentHandler.js +3 -5
- package/dist/cjs/lib/v3/handlers/v3CuaAgentHandler.js.map +1 -1
- package/dist/cjs/lib/v3/llm/aisdk.js +9 -9
- package/dist/cjs/lib/v3/llm/aisdk.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/options.d.ts +2 -0
- package/dist/cjs/lib/v3/types/public/options.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/cdp.d.ts +1 -1
- package/dist/cjs/lib/v3/understudy/cdp.js +83 -43
- package/dist/cjs/lib/v3/understudy/cdp.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/page.js +18 -23
- package/dist/cjs/lib/v3/understudy/page.js.map +1 -1
- package/dist/cjs/lib/v3/v3.d.ts +5 -5
- package/dist/cjs/lib/v3/v3.js +48 -46
- package/dist/cjs/lib/v3/v3.js.map +1 -1
- package/dist/cjs/tests/integration/flowLogger.spec.d.ts +1 -0
- package/dist/cjs/tests/integration/flowLogger.spec.js +714 -0
- package/dist/cjs/tests/integration/flowLogger.spec.js.map +1 -0
- package/dist/cjs/tests/integration/testUtils.d.ts +33 -0
- package/dist/cjs/tests/integration/testUtils.js +144 -0
- package/dist/cjs/tests/integration/testUtils.js.map +1 -1
- package/dist/cjs/tests/integration/timeouts.spec.js +112 -2
- package/dist/cjs/tests/integration/timeouts.spec.js.map +1 -1
- package/dist/cjs/tests/unit/flowlogger-capturing-cdp.test.d.ts +1 -0
- package/dist/cjs/tests/unit/flowlogger-capturing-cdp.test.js +95 -0
- package/dist/cjs/tests/unit/flowlogger-capturing-cdp.test.js.map +1 -0
- package/dist/cjs/tests/unit/flowlogger-capturing-llm.test.d.ts +1 -0
- package/dist/cjs/tests/unit/flowlogger-capturing-llm.test.js +43 -0
- package/dist/cjs/tests/unit/flowlogger-capturing-llm.test.js.map +1 -0
- package/dist/cjs/tests/unit/flowlogger-eventstore.test.d.ts +1 -0
- package/dist/cjs/tests/unit/flowlogger-eventstore.test.js +250 -0
- package/dist/cjs/tests/unit/flowlogger-eventstore.test.js.map +1 -0
- package/dist/esm/lib/v3/agent/AnthropicCUAClient.js +1 -1
- package/dist/esm/lib/v3/agent/AnthropicCUAClient.js.map +1 -1
- package/dist/esm/lib/v3/agent/GoogleCUAClient.js +1 -1
- package/dist/esm/lib/v3/agent/GoogleCUAClient.js.map +1 -1
- package/dist/esm/lib/v3/agent/OpenAICUAClient.js +1 -1
- package/dist/esm/lib/v3/agent/OpenAICUAClient.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/act.js +1 -10
- package/dist/esm/lib/v3/agent/tools/act.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/ariaTree.js +1 -12
- package/dist/esm/lib/v3/agent/tools/ariaTree.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/braveSearch.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/browserbaseSearch.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/click.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/clickAndHold.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/dragAndDrop.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/extract.js +1 -10
- package/dist/esm/lib/v3/agent/tools/extract.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/fillFormVision.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/fillform.js +1 -10
- package/dist/esm/lib/v3/agent/tools/fillform.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/index.d.ts +2 -2
- package/dist/esm/lib/v3/agent/tools/index.js +53 -5
- package/dist/esm/lib/v3/agent/tools/index.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/keys.d.ts +1 -1
- package/dist/esm/lib/v3/agent/tools/keys.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/type.js.map +1 -1
- package/dist/esm/lib/v3/api.js +9 -2
- package/dist/esm/lib/v3/api.js.map +1 -1
- package/dist/esm/lib/v3/flowlogger/EventEmitter.d.ts +7 -0
- package/dist/esm/lib/v3/flowlogger/EventEmitter.js +26 -0
- package/dist/esm/lib/v3/flowlogger/EventEmitter.js.map +1 -0
- package/dist/esm/lib/v3/flowlogger/EventSink.d.ts +44 -0
- package/dist/esm/lib/v3/flowlogger/EventSink.js +206 -0
- package/dist/esm/lib/v3/flowlogger/EventSink.js.map +1 -0
- package/dist/esm/lib/v3/flowlogger/EventStore.d.ts +26 -0
- package/dist/esm/lib/v3/flowlogger/EventStore.js +127 -0
- package/dist/esm/lib/v3/flowlogger/EventStore.js.map +1 -0
- package/dist/{cjs/lib/v3/flowLogger.d.ts → esm/lib/v3/flowlogger/FlowLogger.d.ts} +32 -31
- package/dist/esm/lib/v3/flowlogger/FlowLogger.js +583 -0
- package/dist/esm/lib/v3/flowlogger/FlowLogger.js.map +1 -0
- package/dist/esm/lib/v3/flowlogger/prettify.d.ts +6 -0
- package/dist/esm/lib/v3/flowlogger/prettify.js +389 -0
- package/dist/esm/lib/v3/flowlogger/prettify.js.map +1 -0
- package/dist/esm/lib/v3/handlers/handlerUtils/actHandlerUtils.js +25 -27
- package/dist/esm/lib/v3/handlers/handlerUtils/actHandlerUtils.js.map +1 -1
- package/dist/esm/lib/v3/handlers/v3AgentHandler.js +1 -1
- package/dist/esm/lib/v3/handlers/v3AgentHandler.js.map +1 -1
- package/dist/esm/lib/v3/handlers/v3CuaAgentHandler.js +2 -4
- package/dist/esm/lib/v3/handlers/v3CuaAgentHandler.js.map +1 -1
- package/dist/esm/lib/v3/llm/aisdk.js +1 -1
- package/dist/esm/lib/v3/llm/aisdk.js.map +1 -1
- package/dist/esm/lib/v3/types/public/options.d.ts +2 -0
- package/dist/esm/lib/v3/types/public/options.js.map +1 -1
- package/dist/esm/lib/v3/understudy/cdp.d.ts +1 -1
- package/dist/esm/lib/v3/understudy/cdp.js +78 -38
- package/dist/esm/lib/v3/understudy/cdp.js.map +1 -1
- package/dist/esm/lib/v3/understudy/page.js +13 -18
- package/dist/esm/lib/v3/understudy/page.js.map +1 -1
- package/dist/esm/lib/v3/v3.d.ts +5 -5
- package/dist/esm/lib/v3/v3.js +43 -41
- package/dist/esm/lib/v3/v3.js.map +1 -1
- package/dist/esm/tests/integration/flowLogger.spec.d.ts +1 -0
- package/dist/esm/tests/integration/flowLogger.spec.js +712 -0
- package/dist/esm/tests/integration/flowLogger.spec.js.map +1 -0
- package/dist/esm/tests/integration/testUtils.d.ts +33 -0
- package/dist/esm/tests/integration/testUtils.js +138 -0
- package/dist/esm/tests/integration/testUtils.js.map +1 -1
- package/dist/esm/tests/integration/timeouts.spec.js +112 -2
- package/dist/esm/tests/integration/timeouts.spec.js.map +1 -1
- package/dist/esm/tests/unit/flowlogger-capturing-cdp.test.d.ts +1 -0
- package/dist/esm/tests/unit/flowlogger-capturing-cdp.test.js +93 -0
- package/dist/esm/tests/unit/flowlogger-capturing-cdp.test.js.map +1 -0
- package/dist/esm/tests/unit/flowlogger-capturing-llm.test.d.ts +1 -0
- package/dist/esm/tests/unit/flowlogger-capturing-llm.test.js +41 -0
- package/dist/esm/tests/unit/flowlogger-capturing-llm.test.js.map +1 -0
- package/dist/esm/tests/unit/flowlogger-eventstore.test.d.ts +1 -0
- package/dist/esm/tests/unit/flowlogger-eventstore.test.js +248 -0
- package/dist/esm/tests/unit/flowlogger-eventstore.test.js.map +1 -0
- package/package.json +3 -1
- package/dist/cjs/lib/v3/eventStore.d.ts +0 -41
- package/dist/cjs/lib/v3/eventStore.js +0 -375
- package/dist/cjs/lib/v3/eventStore.js.map +0 -1
- package/dist/cjs/lib/v3/flowLogger.js +0 -470
- package/dist/cjs/lib/v3/flowLogger.js.map +0 -1
- package/dist/esm/lib/v3/eventStore.d.ts +0 -41
- package/dist/esm/lib/v3/eventStore.js +0 -363
- package/dist/esm/lib/v3/eventStore.js.map +0 -1
- package/dist/esm/lib/v3/flowLogger.js +0 -462
- package/dist/esm/lib/v3/flowLogger.js.map +0 -1
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { Writable } from "node:stream";
|
|
4
|
-
import pino from "pino";
|
|
5
|
-
const MAX_LINE_LENGTH = 160;
|
|
6
|
-
const CONFIG_DIR = process.env.BROWSERBASE_CONFIG_DIR || "";
|
|
7
|
-
// helper to take a list of events and compute aggregate metrics
|
|
8
|
-
export function aggregateFlowEventMetrics(events) {
|
|
9
|
-
return events.reduce((totals, event) => {
|
|
10
|
-
if (event.eventType === "LlmRequestEvent") {
|
|
11
|
-
totals.llmRequests += 1;
|
|
12
|
-
}
|
|
13
|
-
if (event.eventType === "LlmResponseEvent") {
|
|
14
|
-
const data = event.data;
|
|
15
|
-
totals.inputTokens += data?.inputTokens ?? 0;
|
|
16
|
-
totals.outputTokens += data?.outputTokens ?? 0;
|
|
17
|
-
}
|
|
18
|
-
if (event.eventType === "CdpCallEvent") {
|
|
19
|
-
totals.cdpEvents += 1;
|
|
20
|
-
}
|
|
21
|
-
return totals;
|
|
22
|
-
}, {
|
|
23
|
-
llmRequests: 0,
|
|
24
|
-
inputTokens: 0,
|
|
25
|
-
outputTokens: 0,
|
|
26
|
-
cdpEvents: 0,
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
function truncateCdpIds(value) {
|
|
30
|
-
return value.replace(/([iI]d:?"?)([0-9A-F]{32})(?="?[,})\s]|$)/g, (_, prefix, id) => `${prefix}${id.slice(0, 4)}…${id.slice(-4)}`);
|
|
31
|
-
}
|
|
32
|
-
function sanitizeSinkValue(value) {
|
|
33
|
-
if (typeof value === "string") {
|
|
34
|
-
return truncateCdpIds(value);
|
|
35
|
-
}
|
|
36
|
-
if (Array.isArray(value)) {
|
|
37
|
-
return value.map((entry) => sanitizeSinkValue(entry));
|
|
38
|
-
}
|
|
39
|
-
if (value && typeof value === "object") {
|
|
40
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
|
|
41
|
-
key,
|
|
42
|
-
sanitizeSinkValue(entry),
|
|
43
|
-
]));
|
|
44
|
-
}
|
|
45
|
-
return value;
|
|
46
|
-
}
|
|
47
|
-
function sanitizeEventForFileStore(event) {
|
|
48
|
-
if (!event.eventType.startsWith("Cdp")) {
|
|
49
|
-
return event;
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
...event,
|
|
53
|
-
data: sanitizeSinkValue(event.data),
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
function truncateLine(value, maxLen) {
|
|
57
|
-
const collapsed = value.replace(/\s+/g, " ");
|
|
58
|
-
if (collapsed.length <= maxLen) {
|
|
59
|
-
return collapsed;
|
|
60
|
-
}
|
|
61
|
-
const endLen = Math.floor(maxLen * 0.3);
|
|
62
|
-
const startLen = maxLen - endLen - 1;
|
|
63
|
-
return `${collapsed.slice(0, startLen)}…${collapsed.slice(-endLen)}`;
|
|
64
|
-
}
|
|
65
|
-
function formatValue(value) {
|
|
66
|
-
if (typeof value === "string")
|
|
67
|
-
return `'${value}'`;
|
|
68
|
-
if (value == null || typeof value !== "object")
|
|
69
|
-
return String(value);
|
|
70
|
-
try {
|
|
71
|
-
return JSON.stringify(value);
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
return "[unserializable]";
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
function formatArgs(args) {
|
|
78
|
-
if (args === undefined) {
|
|
79
|
-
return "";
|
|
80
|
-
}
|
|
81
|
-
return (Array.isArray(args) ? args : [args])
|
|
82
|
-
.filter((entry) => entry !== undefined)
|
|
83
|
-
.map(formatValue)
|
|
84
|
-
.filter((entry) => entry.length > 0)
|
|
85
|
-
.join(", ");
|
|
86
|
-
}
|
|
87
|
-
function shortId(id) {
|
|
88
|
-
return id ? id.slice(-4) : "-";
|
|
89
|
-
}
|
|
90
|
-
let nonce = 0;
|
|
91
|
-
function formatTimestamp() {
|
|
92
|
-
const date = new Date();
|
|
93
|
-
const pad = (value, width = 2) => String(value).padStart(width, "0");
|
|
94
|
-
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${pad(date.getMilliseconds(), 3)}${pad(nonce++ % 100)}`;
|
|
95
|
-
}
|
|
96
|
-
const SENSITIVE_KEYS = /apikey|api_key|key|secret|token|password|passwd|pwd|credential|auth/i;
|
|
97
|
-
function sanitizeOptions(options) {
|
|
98
|
-
const sanitize = (value) => {
|
|
99
|
-
if (typeof value !== "object" || value === null)
|
|
100
|
-
return value;
|
|
101
|
-
if (Array.isArray(value))
|
|
102
|
-
return value.map(sanitize);
|
|
103
|
-
const result = {};
|
|
104
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
105
|
-
result[key] = SENSITIVE_KEYS.test(key) ? "******" : sanitize(entry);
|
|
106
|
-
}
|
|
107
|
-
return result;
|
|
108
|
-
};
|
|
109
|
-
return sanitize({ ...options });
|
|
110
|
-
}
|
|
111
|
-
function removeQuotes(value) {
|
|
112
|
-
return value
|
|
113
|
-
.replace(/([^\\])["']/g, "$1")
|
|
114
|
-
.replace(/^["']|["']$/g, "")
|
|
115
|
-
.trim();
|
|
116
|
-
}
|
|
117
|
-
function formatEventTimestamp(value) {
|
|
118
|
-
const date = new Date(value);
|
|
119
|
-
if (Number.isNaN(date.getTime())) {
|
|
120
|
-
return formatTimestamp();
|
|
121
|
-
}
|
|
122
|
-
const pad = (entry, width = 2) => String(entry).padStart(width, "0");
|
|
123
|
-
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${pad(date.getMilliseconds(), 3)}${pad(nonce++ % 100)}`;
|
|
124
|
-
}
|
|
125
|
-
function prettifyEvent(event) {
|
|
126
|
-
const indent = " ".repeat(event.eventParentIds.length);
|
|
127
|
-
const tag = `[#${shortId(event.eventId)}]`;
|
|
128
|
-
const data = event.data;
|
|
129
|
-
const argsStr = data?.params
|
|
130
|
-
? formatArgs(data.params)
|
|
131
|
-
: formatArgs(event.data);
|
|
132
|
-
const durationSec = data?.durationMs
|
|
133
|
-
? (data.durationMs / 1000).toFixed(2)
|
|
134
|
-
: null;
|
|
135
|
-
const promptStr = data?.prompt ? ` ${String(data.prompt)}` : "";
|
|
136
|
-
const outputStr = data?.output ? ` ${String(data.output)}` : "";
|
|
137
|
-
const hasTokens = data?.inputTokens !== undefined || data?.outputTokens !== undefined;
|
|
138
|
-
const tokenStr = hasTokens
|
|
139
|
-
? ` ꜛ${data?.inputTokens ?? 0} ꜜ${data?.outputTokens ?? 0}`
|
|
140
|
-
: "";
|
|
141
|
-
const details = [
|
|
142
|
-
event.eventType,
|
|
143
|
-
argsStr ? `(${argsStr})` : "",
|
|
144
|
-
promptStr,
|
|
145
|
-
outputStr,
|
|
146
|
-
tokenStr,
|
|
147
|
-
durationSec ? ` ${durationSec}s` : "",
|
|
148
|
-
data?.msg ? ` ${data.msg}` : "",
|
|
149
|
-
data?.error ? ` ERROR ${data.error}` : "",
|
|
150
|
-
].join("");
|
|
151
|
-
if (!details) {
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
154
|
-
const fullLine = `${formatEventTimestamp(event.createdAt)} ${indent}${tag} ${details}`;
|
|
155
|
-
const cleaned = removeQuotes(fullLine);
|
|
156
|
-
return truncateLine(cleaned, MAX_LINE_LENGTH);
|
|
157
|
-
}
|
|
158
|
-
function isWritable(stream) {
|
|
159
|
-
return !!(stream && !stream.destroyed && stream.writable);
|
|
160
|
-
}
|
|
161
|
-
function createJsonlStream(ctx) {
|
|
162
|
-
return new Writable({
|
|
163
|
-
objectMode: true,
|
|
164
|
-
write(chunk, _, cb) {
|
|
165
|
-
if (ctx.initialized && isWritable(ctx.fileStreams.jsonl)) {
|
|
166
|
-
ctx.fileStreams.jsonl.write(chunk, cb);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
cb();
|
|
170
|
-
},
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
function createPrettyStream(ctx) {
|
|
174
|
-
return new Writable({
|
|
175
|
-
objectMode: true,
|
|
176
|
-
write(chunk, _, cb) {
|
|
177
|
-
const stream = ctx.fileStreams.pretty;
|
|
178
|
-
if (!ctx.initialized || !isWritable(stream)) {
|
|
179
|
-
cb();
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
try {
|
|
183
|
-
const event = JSON.parse(chunk);
|
|
184
|
-
const line = prettifyEvent(event);
|
|
185
|
-
if (line) {
|
|
186
|
-
stream.write(line + "\n", cb);
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
catch {
|
|
191
|
-
// fall through
|
|
192
|
-
}
|
|
193
|
-
cb();
|
|
194
|
-
},
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
export function getConfigDir() {
|
|
198
|
-
return CONFIG_DIR ? path.resolve(CONFIG_DIR) : "";
|
|
199
|
-
}
|
|
200
|
-
function matchesQuery(event, query) {
|
|
201
|
-
if (query.sessionId && event.sessionId !== query.sessionId)
|
|
202
|
-
return false;
|
|
203
|
-
if (query.eventId) {
|
|
204
|
-
const matchesEvent = event.eventId === query.eventId ||
|
|
205
|
-
event.eventParentIds.includes(query.eventId);
|
|
206
|
-
if (!matchesEvent) {
|
|
207
|
-
return false;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
if (query.eventType) {
|
|
211
|
-
const pattern = new RegExp(`^${query.eventType
|
|
212
|
-
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
213
|
-
.replace(/\\\*/g, ".*")}$`);
|
|
214
|
-
if (!pattern.test(event.eventType)) {
|
|
215
|
-
return false;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return true;
|
|
219
|
-
}
|
|
220
|
-
export class FileEventStore {
|
|
221
|
-
sessionContexts = new Map();
|
|
222
|
-
eventsBySession = new Map();
|
|
223
|
-
subscribers = new Set();
|
|
224
|
-
async initializeSession(sessionId, v3Options) {
|
|
225
|
-
const existing = this.sessionContexts.get(sessionId);
|
|
226
|
-
if (existing) {
|
|
227
|
-
await existing.initPromise;
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
const configDir = getConfigDir();
|
|
231
|
-
const sessionDir = configDir
|
|
232
|
-
? path.join(configDir, "sessions", sessionId)
|
|
233
|
-
: "";
|
|
234
|
-
const ctx = {
|
|
235
|
-
logger: null,
|
|
236
|
-
sessionId,
|
|
237
|
-
sessionDir,
|
|
238
|
-
configDir,
|
|
239
|
-
initPromise: Promise.resolve(),
|
|
240
|
-
initialized: false,
|
|
241
|
-
fileStreams: {
|
|
242
|
-
pretty: null,
|
|
243
|
-
jsonl: null,
|
|
244
|
-
},
|
|
245
|
-
};
|
|
246
|
-
ctx.initPromise = this.initSessionContext(ctx, v3Options);
|
|
247
|
-
this.sessionContexts.set(sessionId, ctx);
|
|
248
|
-
await ctx.initPromise;
|
|
249
|
-
}
|
|
250
|
-
async initSessionContext(ctx, v3Options) {
|
|
251
|
-
if (!ctx.configDir) {
|
|
252
|
-
ctx.initialized = true;
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
await fs.promises.mkdir(ctx.sessionDir, { recursive: true });
|
|
256
|
-
if (v3Options) {
|
|
257
|
-
const sessionJsonPath = path.join(ctx.sessionDir, "session.json");
|
|
258
|
-
await fs.promises.writeFile(sessionJsonPath, JSON.stringify(sanitizeOptions(v3Options), null, 2), "utf-8");
|
|
259
|
-
}
|
|
260
|
-
const latestLink = path.join(ctx.configDir, "sessions", "latest");
|
|
261
|
-
try {
|
|
262
|
-
try {
|
|
263
|
-
await fs.promises.unlink(latestLink);
|
|
264
|
-
}
|
|
265
|
-
catch {
|
|
266
|
-
// ignore missing link
|
|
267
|
-
}
|
|
268
|
-
await fs.promises.symlink(ctx.sessionId, latestLink, "dir");
|
|
269
|
-
}
|
|
270
|
-
catch {
|
|
271
|
-
// symlink best effort only
|
|
272
|
-
}
|
|
273
|
-
ctx.fileStreams.pretty = fs.createWriteStream(path.join(ctx.sessionDir, "session_events.log"), { flags: "a" });
|
|
274
|
-
ctx.fileStreams.jsonl = fs.createWriteStream(path.join(ctx.sessionDir, "session_events.jsonl"), { flags: "a" });
|
|
275
|
-
ctx.initialized = true;
|
|
276
|
-
ctx.logger = pino({ level: "info" }, pino.multistream([
|
|
277
|
-
{ stream: createJsonlStream(ctx) },
|
|
278
|
-
{ stream: createPrettyStream(ctx) },
|
|
279
|
-
]));
|
|
280
|
-
}
|
|
281
|
-
async appendEvent(event) {
|
|
282
|
-
const storedEvent = sanitizeEventForFileStore(event);
|
|
283
|
-
const existing = this.eventsBySession.get(storedEvent.sessionId) ?? [];
|
|
284
|
-
existing.push(storedEvent);
|
|
285
|
-
this.eventsBySession.set(storedEvent.sessionId, existing);
|
|
286
|
-
for (const subscriber of this.subscribers) {
|
|
287
|
-
if (matchesQuery(storedEvent, subscriber.query)) {
|
|
288
|
-
subscriber.listener(storedEvent);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
const ctx = this.sessionContexts.get(storedEvent.sessionId);
|
|
292
|
-
if (!ctx) {
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
await ctx.initPromise;
|
|
296
|
-
ctx.logger?.info(storedEvent);
|
|
297
|
-
}
|
|
298
|
-
attachBus(sessionId, bus) {
|
|
299
|
-
const emit = bus.emit.bind(bus);
|
|
300
|
-
bus.emit = ((eventName, ...args) => {
|
|
301
|
-
const [event] = args;
|
|
302
|
-
if (event.sessionId === sessionId) {
|
|
303
|
-
void this.appendEvent(event);
|
|
304
|
-
}
|
|
305
|
-
return emit(eventName, ...args);
|
|
306
|
-
});
|
|
307
|
-
return () => {
|
|
308
|
-
bus.emit = emit;
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
async listEvents(query) {
|
|
312
|
-
const sourceEvents = query.sessionId
|
|
313
|
-
? [...(this.eventsBySession.get(query.sessionId) ?? [])]
|
|
314
|
-
: [...this.eventsBySession.values()].flat();
|
|
315
|
-
const filtered = sourceEvents.filter((event) => matchesQuery(event, query));
|
|
316
|
-
filtered.sort((left, right) => {
|
|
317
|
-
const createdAtOrder = left.createdAt.localeCompare(right.createdAt);
|
|
318
|
-
if (createdAtOrder !== 0) {
|
|
319
|
-
return createdAtOrder;
|
|
320
|
-
}
|
|
321
|
-
return left.eventId.localeCompare(right.eventId);
|
|
322
|
-
});
|
|
323
|
-
if (!query.limit) {
|
|
324
|
-
return filtered;
|
|
325
|
-
}
|
|
326
|
-
return filtered.slice(0, query.limit);
|
|
327
|
-
}
|
|
328
|
-
subscribe(query, listener) {
|
|
329
|
-
const subscriber = { query, listener };
|
|
330
|
-
this.subscribers.add(subscriber);
|
|
331
|
-
return () => {
|
|
332
|
-
this.subscribers.delete(subscriber);
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
async destroy() {
|
|
336
|
-
this.subscribers.clear();
|
|
337
|
-
this.eventsBySession.clear();
|
|
338
|
-
await Promise.all([...this.sessionContexts.values()].flatMap((ctx) => Object.values(ctx.fileStreams)
|
|
339
|
-
.filter(Boolean)
|
|
340
|
-
.map((stream) => new Promise((resolve) => {
|
|
341
|
-
stream.end(resolve);
|
|
342
|
-
})))).catch(() => { });
|
|
343
|
-
this.sessionContexts.clear();
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
let eventStore = null;
|
|
347
|
-
export function setEventStore(store) {
|
|
348
|
-
eventStore = store;
|
|
349
|
-
}
|
|
350
|
-
export function getEventStore() {
|
|
351
|
-
if (!eventStore) {
|
|
352
|
-
eventStore = new FileEventStore();
|
|
353
|
-
}
|
|
354
|
-
return eventStore;
|
|
355
|
-
}
|
|
356
|
-
export async function destroyEventStore() {
|
|
357
|
-
if (!eventStore) {
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
await eventStore.destroy();
|
|
361
|
-
eventStore = null;
|
|
362
|
-
}
|
|
363
|
-
//# sourceMappingURL=eventStore.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"eventStore.js","sourceRoot":"","sources":["../../../../lib/v3/eventStore.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Writable } from \"node:stream\";\nimport type { EventEmitter } from \"node:events\";\nimport pino from \"pino\";\n\nimport type { V3Options } from \"./types/public/index.js\";\nimport { type FlowEvent } from \"./flowLogger.js\";\n\nconst MAX_LINE_LENGTH = 160;\nconst CONFIG_DIR = process.env.BROWSERBASE_CONFIG_DIR || \"\";\n\nexport interface FlowEventAggregateMetrics {\n llmRequests: number;\n inputTokens: number;\n outputTokens: number;\n cdpEvents: number;\n}\n\nexport interface EventStoreQuery {\n sessionId?: string;\n eventId?: string;\n eventType?: string;\n limit?: number;\n}\n\nexport type EventStoreListener = (event: FlowEvent) => void;\n\nexport interface EventStore {\n initializeSession(sessionId: string, v3Options?: V3Options): Promise<void>;\n appendEvent(event: FlowEvent): Promise<void>;\n attachBus(sessionId: string, bus: EventEmitter): () => void;\n listEvents(query: EventStoreQuery): Promise<FlowEvent[]>;\n subscribe(query: EventStoreQuery, listener: EventStoreListener): () => void;\n destroy(): Promise<void>;\n}\n\n// helper to take a list of events and compute aggregate metrics\nexport function aggregateFlowEventMetrics(\n events: FlowEvent[],\n): FlowEventAggregateMetrics {\n return events.reduce<FlowEventAggregateMetrics>(\n (totals, event) => {\n if (event.eventType === \"LlmRequestEvent\") {\n totals.llmRequests += 1;\n }\n\n if (event.eventType === \"LlmResponseEvent\") {\n const data = event.data as {\n inputTokens?: number;\n outputTokens?: number;\n };\n totals.inputTokens += data?.inputTokens ?? 0;\n totals.outputTokens += data?.outputTokens ?? 0;\n }\n\n if (event.eventType === \"CdpCallEvent\") {\n totals.cdpEvents += 1;\n }\n\n return totals;\n },\n {\n llmRequests: 0,\n inputTokens: 0,\n outputTokens: 0,\n cdpEvents: 0,\n },\n );\n}\n\ninterface SessionContext {\n logger: pino.Logger | null;\n sessionId: string;\n sessionDir: string;\n configDir: string;\n initPromise: Promise<void>;\n initialized: boolean;\n fileStreams: {\n pretty: fs.WriteStream | null;\n jsonl: fs.WriteStream | null;\n };\n}\n\ninterface EventSubscriber {\n listener: EventStoreListener;\n query: EventStoreQuery;\n}\n\nfunction truncateCdpIds(value: string): string {\n return value.replace(\n /([iI]d:?\"?)([0-9A-F]{32})(?=\"?[,})\\s]|$)/g,\n (_, prefix: string, id: string) =>\n `${prefix}${id.slice(0, 4)}…${id.slice(-4)}`,\n );\n}\n\nfunction sanitizeSinkValue(value: unknown): unknown {\n if (typeof value === \"string\") {\n return truncateCdpIds(value);\n }\n\n if (Array.isArray(value)) {\n return value.map((entry) => sanitizeSinkValue(entry));\n }\n\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value).map(([key, entry]) => [\n key,\n sanitizeSinkValue(entry),\n ]),\n );\n }\n\n return value;\n}\n\nfunction sanitizeEventForFileStore(event: FlowEvent): FlowEvent {\n if (!event.eventType.startsWith(\"Cdp\")) {\n return event;\n }\n\n return {\n ...event,\n data: sanitizeSinkValue(event.data) as Record<string, unknown>,\n };\n}\n\nfunction truncateLine(value: string, maxLen: number): string {\n const collapsed = value.replace(/\\s+/g, \" \");\n if (collapsed.length <= maxLen) {\n return collapsed;\n }\n\n const endLen = Math.floor(maxLen * 0.3);\n const startLen = maxLen - endLen - 1;\n return `${collapsed.slice(0, startLen)}…${collapsed.slice(-endLen)}`;\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") return `'${value}'`;\n if (value == null || typeof value !== \"object\") return String(value);\n\n try {\n return JSON.stringify(value);\n } catch {\n return \"[unserializable]\";\n }\n}\n\nfunction formatArgs(args?: unknown | unknown[]): string {\n if (args === undefined) {\n return \"\";\n }\n\n return (Array.isArray(args) ? args : [args])\n .filter((entry) => entry !== undefined)\n .map(formatValue)\n .filter((entry) => entry.length > 0)\n .join(\", \");\n}\n\nfunction shortId(id: string | null | undefined): string {\n return id ? id.slice(-4) : \"-\";\n}\n\nlet nonce = 0;\nfunction formatTimestamp(): string {\n const date = new Date();\n const pad = (value: number, width = 2) => String(value).padStart(width, \"0\");\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${pad(date.getMilliseconds(), 3)}${pad(nonce++ % 100)}`;\n}\n\nconst SENSITIVE_KEYS =\n /apikey|api_key|key|secret|token|password|passwd|pwd|credential|auth/i;\n\nfunction sanitizeOptions(options: V3Options): Record<string, unknown> {\n const sanitize = (value: unknown): unknown => {\n if (typeof value !== \"object\" || value === null) return value;\n if (Array.isArray(value)) return value.map(sanitize);\n\n const result: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value)) {\n result[key] = SENSITIVE_KEYS.test(key) ? \"******\" : sanitize(entry);\n }\n return result;\n };\n\n return sanitize({ ...options }) as Record<string, unknown>;\n}\n\nfunction removeQuotes(value: string): string {\n return value\n .replace(/([^\\\\])[\"']/g, \"$1\")\n .replace(/^[\"']|[\"']$/g, \"\")\n .trim();\n}\n\nfunction formatEventTimestamp(value: string): string {\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n return formatTimestamp();\n }\n\n const pad = (entry: number, width = 2) => String(entry).padStart(width, \"0\");\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${pad(date.getMilliseconds(), 3)}${pad(nonce++ % 100)}`;\n}\n\nfunction prettifyEvent(event: FlowEvent): string | null {\n const indent = \" \".repeat(event.eventParentIds.length);\n const tag = `[#${shortId(event.eventId)}]`;\n const data = event.data as {\n params?: unknown;\n prompt?: unknown;\n output?: unknown;\n durationMs?: number;\n inputTokens?: number;\n outputTokens?: number;\n error?: string;\n msg?: string;\n };\n const argsStr = data?.params\n ? formatArgs(data.params)\n : formatArgs(event.data);\n const durationSec = data?.durationMs\n ? (data.durationMs / 1000).toFixed(2)\n : null;\n const promptStr = data?.prompt ? ` ${String(data.prompt)}` : \"\";\n const outputStr = data?.output ? ` ${String(data.output)}` : \"\";\n const hasTokens =\n data?.inputTokens !== undefined || data?.outputTokens !== undefined;\n const tokenStr = hasTokens\n ? ` ꜛ${data?.inputTokens ?? 0} ꜜ${data?.outputTokens ?? 0}`\n : \"\";\n const details = [\n event.eventType,\n argsStr ? `(${argsStr})` : \"\",\n promptStr,\n outputStr,\n tokenStr,\n durationSec ? ` ${durationSec}s` : \"\",\n data?.msg ? ` ${data.msg}` : \"\",\n data?.error ? ` ERROR ${data.error}` : \"\",\n ].join(\"\");\n\n if (!details) {\n return null;\n }\n\n const fullLine = `${formatEventTimestamp(event.createdAt)} ${indent}${tag} ${details}`;\n const cleaned = removeQuotes(fullLine);\n return truncateLine(cleaned, MAX_LINE_LENGTH);\n}\n\nfunction isWritable(stream: fs.WriteStream | null): stream is fs.WriteStream {\n return !!(stream && !stream.destroyed && stream.writable);\n}\n\nfunction createJsonlStream(ctx: SessionContext): Writable {\n return new Writable({\n objectMode: true,\n write(chunk: string, _, cb) {\n if (ctx.initialized && isWritable(ctx.fileStreams.jsonl)) {\n ctx.fileStreams.jsonl.write(chunk, cb);\n return;\n }\n\n cb();\n },\n });\n}\n\nfunction createPrettyStream(ctx: SessionContext): Writable {\n return new Writable({\n objectMode: true,\n write(chunk: string, _, cb) {\n const stream = ctx.fileStreams.pretty;\n if (!ctx.initialized || !isWritable(stream)) {\n cb();\n return;\n }\n\n try {\n const event = JSON.parse(chunk) as FlowEvent;\n const line = prettifyEvent(event);\n if (line) {\n stream.write(line + \"\\n\", cb);\n return;\n }\n } catch {\n // fall through\n }\n\n cb();\n },\n });\n}\n\nexport function getConfigDir(): string {\n return CONFIG_DIR ? path.resolve(CONFIG_DIR) : \"\";\n}\n\nfunction matchesQuery(event: FlowEvent, query: EventStoreQuery): boolean {\n if (query.sessionId && event.sessionId !== query.sessionId) return false;\n if (query.eventId) {\n const matchesEvent =\n event.eventId === query.eventId ||\n event.eventParentIds.includes(query.eventId);\n if (!matchesEvent) {\n return false;\n }\n }\n if (query.eventType) {\n const pattern = new RegExp(\n `^${query.eventType\n .replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\\\\\*/g, \".*\")}$`,\n );\n if (!pattern.test(event.eventType)) {\n return false;\n }\n }\n return true;\n}\n\nexport class FileEventStore implements EventStore {\n private readonly sessionContexts = new Map<string, SessionContext>();\n private readonly eventsBySession = new Map<string, FlowEvent[]>();\n private readonly subscribers = new Set<EventSubscriber>();\n\n async initializeSession(\n sessionId: string,\n v3Options?: V3Options,\n ): Promise<void> {\n const existing = this.sessionContexts.get(sessionId);\n if (existing) {\n await existing.initPromise;\n return;\n }\n\n const configDir = getConfigDir();\n const sessionDir = configDir\n ? path.join(configDir, \"sessions\", sessionId)\n : \"\";\n\n const ctx: SessionContext = {\n logger: null,\n sessionId,\n sessionDir,\n configDir,\n initPromise: Promise.resolve(),\n initialized: false,\n fileStreams: {\n pretty: null,\n jsonl: null,\n },\n };\n\n ctx.initPromise = this.initSessionContext(ctx, v3Options);\n this.sessionContexts.set(sessionId, ctx);\n await ctx.initPromise;\n }\n\n private async initSessionContext(\n ctx: SessionContext,\n v3Options?: V3Options,\n ): Promise<void> {\n if (!ctx.configDir) {\n ctx.initialized = true;\n return;\n }\n\n await fs.promises.mkdir(ctx.sessionDir, { recursive: true });\n\n if (v3Options) {\n const sessionJsonPath = path.join(ctx.sessionDir, \"session.json\");\n await fs.promises.writeFile(\n sessionJsonPath,\n JSON.stringify(sanitizeOptions(v3Options), null, 2),\n \"utf-8\",\n );\n }\n\n const latestLink = path.join(ctx.configDir, \"sessions\", \"latest\");\n try {\n try {\n await fs.promises.unlink(latestLink);\n } catch {\n // ignore missing link\n }\n await fs.promises.symlink(ctx.sessionId, latestLink, \"dir\");\n } catch {\n // symlink best effort only\n }\n\n ctx.fileStreams.pretty = fs.createWriteStream(\n path.join(ctx.sessionDir, \"session_events.log\"),\n { flags: \"a\" },\n );\n ctx.fileStreams.jsonl = fs.createWriteStream(\n path.join(ctx.sessionDir, \"session_events.jsonl\"),\n { flags: \"a\" },\n );\n\n ctx.initialized = true;\n ctx.logger = pino(\n { level: \"info\" },\n pino.multistream([\n { stream: createJsonlStream(ctx) },\n { stream: createPrettyStream(ctx) },\n ]),\n );\n }\n\n async appendEvent(event: FlowEvent): Promise<void> {\n const storedEvent = sanitizeEventForFileStore(event);\n const existing = this.eventsBySession.get(storedEvent.sessionId) ?? [];\n existing.push(storedEvent);\n this.eventsBySession.set(storedEvent.sessionId, existing);\n\n for (const subscriber of this.subscribers) {\n if (matchesQuery(storedEvent, subscriber.query)) {\n subscriber.listener(storedEvent);\n }\n }\n\n const ctx = this.sessionContexts.get(storedEvent.sessionId);\n if (!ctx) {\n return;\n }\n\n await ctx.initPromise;\n ctx.logger?.info(storedEvent);\n }\n\n attachBus(sessionId: string, bus: EventEmitter): () => void {\n const emit = bus.emit.bind(bus);\n bus.emit = ((eventName: string | symbol, ...args: [FlowEvent]) => {\n const [event] = args;\n if (event.sessionId === sessionId) {\n void this.appendEvent(event);\n }\n return emit(eventName, ...args);\n }) as typeof bus.emit;\n\n return () => {\n bus.emit = emit;\n };\n }\n\n async listEvents(query: EventStoreQuery): Promise<FlowEvent[]> {\n const sourceEvents = query.sessionId\n ? [...(this.eventsBySession.get(query.sessionId) ?? [])]\n : [...this.eventsBySession.values()].flat();\n\n const filtered = sourceEvents.filter((event) => matchesQuery(event, query));\n filtered.sort((left, right) => {\n const createdAtOrder = left.createdAt.localeCompare(right.createdAt);\n if (createdAtOrder !== 0) {\n return createdAtOrder;\n }\n\n return left.eventId.localeCompare(right.eventId);\n });\n\n if (!query.limit) {\n return filtered;\n }\n\n return filtered.slice(0, query.limit);\n }\n\n subscribe(query: EventStoreQuery, listener: EventStoreListener): () => void {\n const subscriber: EventSubscriber = { query, listener };\n this.subscribers.add(subscriber);\n\n return () => {\n this.subscribers.delete(subscriber);\n };\n }\n\n async destroy(): Promise<void> {\n this.subscribers.clear();\n this.eventsBySession.clear();\n\n await Promise.all(\n [...this.sessionContexts.values()].flatMap((ctx) =>\n Object.values(ctx.fileStreams)\n .filter(Boolean)\n .map(\n (stream) =>\n new Promise<void>((resolve) => {\n stream!.end(resolve);\n }),\n ),\n ),\n ).catch(() => {});\n\n this.sessionContexts.clear();\n }\n}\n\nlet eventStore: EventStore | null = null;\n\nexport function setEventStore(store: EventStore): void {\n eventStore = store;\n}\n\nexport function getEventStore(): EventStore {\n if (!eventStore) {\n eventStore = new FileEventStore();\n }\n\n return eventStore;\n}\n\nexport async function destroyEventStore(): Promise<void> {\n if (!eventStore) {\n return;\n }\n\n await eventStore.destroy();\n eventStore = null;\n}\n"]}
|