@runtimescope/workers-sdk 0.9.1 → 0.9.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/dist/index.js +52 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/index.d.ts +0 -361
package/dist/index.js
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
3
7
|
|
|
4
8
|
// src/utils.ts
|
|
5
9
|
function generateId() {
|
|
6
|
-
|
|
10
|
+
try {
|
|
11
|
+
return crypto.randomUUID();
|
|
12
|
+
} catch {
|
|
13
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
14
|
+
const r = Math.random() * 16 | 0;
|
|
15
|
+
return (c === "x" ? r : r & 3 | 8).toString(16);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
7
18
|
}
|
|
8
19
|
function generateSessionId() {
|
|
9
|
-
|
|
20
|
+
try {
|
|
21
|
+
return `wk-${crypto.randomUUID().slice(0, 16)}`;
|
|
22
|
+
} catch {
|
|
23
|
+
return `wk-${generateId().replace(/-/g, "").slice(0, 16)}`;
|
|
24
|
+
}
|
|
10
25
|
}
|
|
11
26
|
function safeSerialize(value, maxDepth = 5) {
|
|
12
27
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
@@ -41,7 +56,7 @@ function safeSerialize(value, maxDepth = 5) {
|
|
|
41
56
|
}
|
|
42
57
|
|
|
43
58
|
// src/transport.ts
|
|
44
|
-
var SDK_VERSION = "0.9.
|
|
59
|
+
var SDK_VERSION = "0.9.2";
|
|
45
60
|
var DEFAULT_ENDPOINT = "http://localhost:9091/api/events";
|
|
46
61
|
var WorkersTransport = class {
|
|
47
62
|
buffer = [];
|
|
@@ -160,7 +175,8 @@ function interceptConsole(emit, options) {
|
|
|
160
175
|
level,
|
|
161
176
|
message,
|
|
162
177
|
args: args.map((a) => safeSerialize(a, 3)),
|
|
163
|
-
stackTrace: level === "error" || level === "trace" ? new Error().stack?.split("\n").slice(2).join("\n") : void 0
|
|
178
|
+
stackTrace: level === "error" || level === "trace" ? new Error().stack?.split("\n").slice(2).join("\n") : void 0,
|
|
179
|
+
source: "workers"
|
|
164
180
|
};
|
|
165
181
|
emit(event);
|
|
166
182
|
originals[level](...args);
|
|
@@ -181,12 +197,40 @@ function stringifyArg(arg) {
|
|
|
181
197
|
}
|
|
182
198
|
|
|
183
199
|
// src/handler.ts
|
|
184
|
-
var _contextStorage =
|
|
200
|
+
var _contextStorage = null;
|
|
201
|
+
try {
|
|
202
|
+
const { AsyncLocalStorage } = __require("async_hooks");
|
|
203
|
+
_contextStorage = new AsyncLocalStorage();
|
|
204
|
+
} catch {
|
|
205
|
+
let _currentContext;
|
|
206
|
+
_contextStorage = {
|
|
207
|
+
run(ctx, fn) {
|
|
208
|
+
const prev = _currentContext;
|
|
209
|
+
_currentContext = ctx;
|
|
210
|
+
try {
|
|
211
|
+
return fn();
|
|
212
|
+
} finally {
|
|
213
|
+
_currentContext = prev;
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
getStore() {
|
|
217
|
+
return _currentContext;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
185
221
|
function getActiveContext() {
|
|
186
|
-
return _contextStorage
|
|
222
|
+
return _contextStorage?.getStore() ?? null;
|
|
187
223
|
}
|
|
188
224
|
var DEFAULT_REDACT_HEADERS = ["authorization", "cookie", "set-cookie"];
|
|
189
225
|
function withRuntimeScope(handler, config) {
|
|
226
|
+
try {
|
|
227
|
+
return _withRuntimeScope(handler, config);
|
|
228
|
+
} catch (err) {
|
|
229
|
+
console.warn("[RuntimeScope] SDK init failed, running without instrumentation:", err.message);
|
|
230
|
+
return handler;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function _withRuntimeScope(handler, config) {
|
|
190
234
|
const transport = new WorkersTransport(config);
|
|
191
235
|
const sampler = config.sampleRate !== void 0 && config.sampleRate < 1 ? new Sampler({ sampleRate: config.sampleRate }) : null;
|
|
192
236
|
const redactHeaders = config.redactHeaders ?? DEFAULT_REDACT_HEADERS;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/handler.ts","../src/utils.ts","../src/transport.ts","../src/sampler.ts","../src/interceptors/console.ts","../src/bindings/d1.ts","../src/bindings/kv.ts","../src/bindings/r2.ts","../src/index.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type {\n WorkersConfig,\n WorkersRuntimeEvent,\n NetworkEvent,\n ConsoleEvent,\n UserContext,\n} from './types.js';\nimport { WorkersTransport } from './transport.js';\nimport { Sampler } from './sampler.js';\nimport { interceptConsole } from './interceptors/console.js';\nimport { generateId } from './utils.js';\n\n// ============================================================\n// withRuntimeScope — wraps a Workers fetch handler\n// Captures request/response metrics, errors, console output.\n// Flushes all events via ctx.waitUntil() at end of request.\n// ============================================================\n\n/** Internal context exposed to binding wrappers */\nexport interface RuntimeScopeContext {\n transport: WorkersTransport;\n sessionId: string;\n config: WorkersConfig;\n emit: (event: WorkersRuntimeEvent) => void;\n}\n\n// Per-request context — AsyncLocalStorage ensures isolation under concurrent requests.\n// Available in Workers runtime with nodejs_compat flag and in Node.js 16+.\nconst _contextStorage = new AsyncLocalStorage<RuntimeScopeContext>();\n\n/** Get the active RuntimeScope context (used by binding wrappers) */\nexport function getActiveContext(): RuntimeScopeContext | null {\n return _contextStorage.getStore() ?? null;\n}\n\nconst DEFAULT_REDACT_HEADERS = ['authorization', 'cookie', 'set-cookie'];\n\nexport interface WorkersFetchHandler {\n fetch(\n request: Request,\n env: unknown,\n ctx: ExecutionContext,\n ): Response | Promise<Response>;\n}\n\n/**\n * Wrap a Workers fetch handler to capture request/response metrics,\n * errors, and console output. Events are flushed via ctx.waitUntil().\n *\n * @example\n * ```ts\n * import { withRuntimeScope } from '@runtimescope/workers-sdk';\n *\n * export default withRuntimeScope({\n * async fetch(request, env, ctx) {\n * return new Response('Hello!');\n * },\n * }, { appName: 'my-worker' });\n * ```\n */\nexport function withRuntimeScope(\n handler: WorkersFetchHandler,\n config: WorkersConfig,\n): WorkersFetchHandler {\n const transport = new WorkersTransport(config);\n const sampler = config.sampleRate !== undefined && config.sampleRate < 1\n ? new Sampler({ sampleRate: config.sampleRate })\n : null;\n\n const redactHeaders = config.redactHeaders ?? DEFAULT_REDACT_HEADERS;\n const redactSet = new Set(redactHeaders.map((h) => h.toLowerCase()));\n\n function emit(event: WorkersRuntimeEvent): void {\n if (sampler && !sampler.shouldSample(event)) return;\n if (config.beforeSend) {\n const filtered = config.beforeSend(event);\n if (!filtered) return;\n transport.queue(filtered);\n } else {\n transport.queue(event);\n }\n }\n\n // Set up console interceptor (persistent across requests)\n let restoreConsole: (() => void) | null = null;\n if (config.captureConsole !== false) {\n restoreConsole = interceptConsole(emit, {\n sessionId: transport.sessionId,\n });\n }\n\n function extractHeaders(headers: Headers): Record<string, string> {\n if (!config.captureHeaders) return {};\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key] = redactSet.has(key.toLowerCase()) ? '[REDACTED]' : value;\n });\n return result;\n }\n\n return {\n async fetch(\n request: Request,\n env: unknown,\n ctx: ExecutionContext,\n ): Promise<Response> {\n const start = Date.now();\n const url = new URL(request.url);\n\n // Run handler inside AsyncLocalStorage so binding wrappers\n // get the correct per-request context even under concurrency\n const rsContext: RuntimeScopeContext = {\n transport,\n sessionId: transport.sessionId,\n config,\n emit,\n };\n\n return _contextStorage.run(rsContext, async () => {\n try {\n const response = await handler.fetch(request, env, ctx);\n const duration = Date.now() - start;\n\n const networkEvent: NetworkEvent = {\n eventId: generateId(),\n sessionId: transport.sessionId,\n timestamp: start,\n eventType: 'network',\n url: url.pathname + url.search,\n method: request.method,\n status: response.status,\n duration,\n requestHeaders: extractHeaders(request.headers),\n responseHeaders: extractHeaders(response.headers),\n requestBodySize: 0,\n responseBodySize: 0,\n ttfb: duration,\n source: 'workers',\n direction: 'incoming',\n cfProperties: extractCfProperties(request),\n };\n\n emit(networkEvent);\n ctx.waitUntil(transport.flush());\n return response;\n } catch (error) {\n const duration = Date.now() - start;\n\n // Capture the error as a console error event\n const errorEvent: ConsoleEvent = {\n eventId: generateId(),\n sessionId: transport.sessionId,\n timestamp: Date.now(),\n eventType: 'console',\n level: 'error',\n message: error instanceof Error ? error.message : String(error),\n args: [error instanceof Error ? { name: error.name, message: error.message, stack: error.stack } : String(error)],\n stackTrace: error instanceof Error ? error.stack : undefined,\n };\n\n emit(errorEvent);\n\n // Also capture the failed request\n const networkEvent: NetworkEvent = {\n eventId: generateId(),\n sessionId: transport.sessionId,\n timestamp: start,\n eventType: 'network',\n url: url.pathname + url.search,\n method: request.method,\n status: 500,\n duration,\n requestHeaders: extractHeaders(request.headers),\n responseHeaders: {},\n requestBodySize: 0,\n responseBodySize: 0,\n ttfb: duration,\n source: 'workers',\n direction: 'incoming',\n cfProperties: extractCfProperties(request),\n errorMessage: error instanceof Error ? error.message : String(error),\n };\n\n emit(networkEvent);\n ctx.waitUntil(transport.flush());\n throw error;\n }\n });\n },\n };\n}\n\nfunction extractCfProperties(request: Request): NetworkEvent['cfProperties'] | undefined {\n // request.cf is Cloudflare-specific — may not exist in non-CF environments\n const cf = (request as unknown as { cf?: Record<string, unknown> }).cf;\n if (!cf) return undefined;\n\n return {\n colo: cf.colo as string | undefined,\n country: cf.country as string | undefined,\n city: cf.city as string | undefined,\n region: cf.region as string | undefined,\n asn: cf.asn as number | undefined,\n httpProtocol: cf.httpProtocol as string | undefined,\n tlsVersion: cf.tlsVersion as string | undefined,\n };\n}\n","// ============================================================\n// Utility functions — no Node.js APIs, Workers-safe\n// ============================================================\n\n/** Generate a random event ID using Web Crypto API (available in Workers) */\nexport function generateId(): string {\n return crypto.randomUUID();\n}\n\n/** Generate a session ID with worker prefix */\nexport function generateSessionId(): string {\n return `wk-${crypto.randomUUID().slice(0, 16)}`;\n}\n\n/** Safely serialize a value, handling circular references, functions, symbols, and errors. */\nexport function safeSerialize(value: unknown, maxDepth = 5): unknown {\n const seen = new WeakSet();\n\n function walk(val: unknown, depth: number): unknown {\n if (depth > maxDepth) return '[max depth]';\n if (val === null || val === undefined) return val;\n if (typeof val === 'function') return `[Function: ${val.name || 'anonymous'}]`;\n if (typeof val === 'symbol') return val.toString();\n if (typeof val === 'bigint') return val.toString();\n if (typeof val !== 'object') return val;\n\n if (val instanceof Error) {\n return { name: val.name, message: val.message, stack: val.stack };\n }\n if (val instanceof Date) {\n return val.toISOString();\n }\n if (val instanceof RegExp) {\n return val.toString();\n }\n\n if (seen.has(val as object)) return '[Circular]';\n seen.add(val as object);\n\n if (Array.isArray(val)) {\n return val.map((v) => walk(v, depth + 1));\n }\n\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(val as Record<string, unknown>)) {\n result[key] = walk((val as Record<string, unknown>)[key], depth + 1);\n }\n return result;\n }\n\n return walk(value, 0);\n}\n","import type { WorkersRuntimeEvent, WorkersConfig, UserContext } from './types.js';\nimport { generateSessionId } from './utils.js';\n\n// ============================================================\n// Workers Transport\n// Flush events via ctx.waitUntil(fetch(...)) — no timers, no intervals.\n// Matches collector's POST /api/events endpoint.\n// ============================================================\n\nconst SDK_VERSION = '0.9.1';\nconst DEFAULT_ENDPOINT = 'http://localhost:9091/api/events';\n\nexport class WorkersTransport {\n private buffer: WorkersRuntimeEvent[] = [];\n private maxQueueSize: number;\n private url: string;\n private authToken: string | undefined;\n private appName: string;\n private projectId?: string;\n private sessionRegistered = false;\n private _droppedCount = 0;\n\n readonly sessionId: string;\n\n constructor(config: WorkersConfig) {\n this.sessionId = generateSessionId();\n this.url = config.httpEndpoint ?? DEFAULT_ENDPOINT;\n this.appName = config.appName;\n this.projectId = config.projectId;\n this.authToken = config.authToken;\n this.maxQueueSize = config.maxQueueSize ?? 500;\n }\n\n get droppedCount(): number {\n return this._droppedCount;\n }\n\n queue(event: WorkersRuntimeEvent): void {\n if (this.buffer.length >= this.maxQueueSize) {\n this.buffer.shift();\n this._droppedCount++;\n }\n this.buffer.push(event);\n }\n\n /**\n * Flush all buffered events to the collector.\n * Call via ctx.waitUntil(transport.flush()) at the end of each request.\n * Retries once on failure with a short delay.\n */\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n\n const events = this.buffer.splice(0);\n const payload: Record<string, unknown> = {\n sessionId: this.sessionId,\n events,\n };\n\n // Include appName/sdkVersion on first request to auto-register session\n if (!this.sessionRegistered) {\n payload.appName = this.appName;\n payload.sdkVersion = SDK_VERSION;\n if (this.projectId) payload.projectId = this.projectId;\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (this.authToken) {\n headers['Authorization'] = `Bearer ${this.authToken}`;\n }\n\n const body = JSON.stringify(payload);\n\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 5_000);\n\n const response = await fetch(this.url, {\n method: 'POST',\n headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n this.sessionRegistered = true;\n return;\n }\n\n // Server error (5xx) — retry once\n if (response.status >= 500 && attempt === 0) continue;\n // Client error (4xx) — don't retry\n return;\n } catch {\n // Network error or timeout — retry once\n if (attempt === 0) {\n await new Promise((r) => setTimeout(r, 200));\n continue;\n }\n // Second attempt failed — events are lost (acceptable for edge observability)\n }\n }\n }\n}\n","import type { WorkersRuntimeEvent } from './types.js';\n\n// ============================================================\n// Sampler — probabilistic + rate limiting\n// Adapted from server-sdk, no Node.js APIs.\n// ============================================================\n\nexport interface SamplerConfig {\n /** Probabilistic sample rate: 0.0–1.0 (default: 1.0 = keep all) */\n sampleRate?: number;\n}\n\nexport class Sampler {\n private _droppedCount = 0;\n\n constructor(private config: SamplerConfig) {}\n\n get droppedCount(): number {\n return this._droppedCount;\n }\n\n shouldSample(_event: WorkersRuntimeEvent): boolean {\n const rate = this.config.sampleRate;\n if (rate !== undefined && rate < 1) {\n if (Math.random() > rate) {\n this._droppedCount++;\n return false;\n }\n }\n return true;\n }\n}\n","import type { ConsoleEvent, ConsoleLevel } from '../types.js';\nimport { generateId, safeSerialize } from '../utils.js';\n\n// ============================================================\n// Console Interceptor — Workers-safe\n// Patches console.log/warn/error/info/debug/trace.\n// Returns a restore function for cleanup.\n// ============================================================\n\ntype EmitFn = (event: ConsoleEvent) => void;\n\nconst LEVELS: ConsoleLevel[] = ['log', 'warn', 'error', 'info', 'debug', 'trace'];\n\nexport interface ConsoleInterceptorOptions {\n levels?: ConsoleLevel[];\n sessionId: string;\n}\n\nexport function interceptConsole(\n emit: EmitFn,\n options: ConsoleInterceptorOptions\n): () => void {\n const levels = options.levels ?? LEVELS;\n const originals: Record<string, (...args: unknown[]) => void> = {};\n\n for (const level of levels) {\n originals[level] = console[level].bind(console);\n\n console[level] = (...args: unknown[]) => {\n const message = args\n .map((a) => (typeof a === 'string' ? a : stringifyArg(a)))\n .join(' ');\n\n const event: ConsoleEvent = {\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: Date.now(),\n eventType: 'console',\n level,\n message,\n args: args.map((a) => safeSerialize(a, 3)),\n stackTrace:\n level === 'error' || level === 'trace'\n ? new Error().stack?.split('\\n').slice(2).join('\\n')\n : undefined,\n };\n\n // emit() already applies beforeSend — don't double-filter\n emit(event);\n\n // Call original — MUST use saved reference\n originals[level](...args);\n };\n }\n\n return () => {\n for (const level of levels) {\n console[level] = originals[level];\n }\n };\n}\n\nfunction stringifyArg(arg: unknown): string {\n try {\n return JSON.stringify(arg);\n } catch {\n return String(arg);\n }\n}\n","import type {\n D1DatabaseBinding,\n D1PreparedStatementBinding,\n D1Result,\n D1ExecResult,\n DatabaseEvent,\n} from '../types.js';\nimport { generateId } from '../utils.js';\n\n// ============================================================\n// D1 Binding Wrapper\n// Wraps a D1Database to capture SQL queries, timing, and results.\n// Events emitted as 'database' type with source: 'd1'.\n// ============================================================\n\ntype EmitFn = (event: DatabaseEvent) => void;\n\ninterface D1InstrumentOptions {\n sessionId: string;\n}\n\n/**\n * Wrap a D1 database binding to capture queries.\n *\n * @example\n * ```ts\n * const db = instrumentD1(env.DB, transport.sessionId);\n * const results = await db.prepare('SELECT * FROM users').all();\n * ```\n */\nexport function instrumentD1(\n db: D1DatabaseBinding,\n emit: EmitFn,\n options: D1InstrumentOptions,\n): D1DatabaseBinding {\n return {\n prepare(query: string): D1PreparedStatementBinding {\n const stmt = db.prepare(query);\n return instrumentStatement(stmt, query, emit, options);\n },\n\n async batch<T = unknown>(statements: D1PreparedStatementBinding[]): Promise<D1Result<T>[]> {\n const start = Date.now();\n try {\n const results = await db.batch<T>(statements);\n const duration = Date.now() - start;\n emit({\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query: `BATCH (${statements.length} statements)`,\n normalizedQuery: 'BATCH',\n duration,\n tablesAccessed: [],\n operation: 'OTHER',\n source: 'd1',\n rowsReturned: results.reduce((sum, r) => sum + (r.results?.length ?? 0), 0),\n });\n return results;\n } catch (err) {\n const duration = Date.now() - start;\n emit({\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query: `BATCH (${statements.length} statements)`,\n normalizedQuery: 'BATCH',\n duration,\n tablesAccessed: [],\n operation: 'OTHER',\n source: 'd1',\n error: (err as Error).message,\n });\n throw err;\n }\n },\n\n async exec(query: string): Promise<D1ExecResult> {\n const start = Date.now();\n try {\n const result = await db.exec(query);\n emit({\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query,\n normalizedQuery: normalizeD1Query(query),\n duration: result.duration,\n tablesAccessed: extractTables(query),\n operation: parseOp(query),\n source: 'd1',\n rowsAffected: result.count,\n });\n return result;\n } catch (err) {\n const duration = Date.now() - start;\n emit({\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query,\n normalizedQuery: normalizeD1Query(query),\n duration,\n tablesAccessed: extractTables(query),\n operation: parseOp(query),\n source: 'd1',\n error: (err as Error).message,\n });\n throw err;\n }\n },\n\n dump(): Promise<ArrayBuffer> {\n return db.dump();\n },\n };\n}\n\nfunction instrumentStatement(\n stmt: D1PreparedStatementBinding,\n query: string,\n emit: EmitFn,\n options: D1InstrumentOptions,\n): D1PreparedStatementBinding {\n const op = parseOp(query);\n const tables = extractTables(query);\n const normalized = normalizeD1Query(query);\n\n function makeEvent(start: number, duration: number, extra: Partial<DatabaseEvent> = {}): DatabaseEvent {\n return {\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query,\n normalizedQuery: normalized,\n duration,\n tablesAccessed: tables,\n operation: op,\n source: 'd1',\n ...extra,\n };\n }\n\n async function wrapAsync<T>(fn: () => Promise<T>, start: number, getExtra?: (result: T) => Partial<DatabaseEvent>): Promise<T> {\n try {\n const result = await fn();\n const duration = Date.now() - start;\n emit(makeEvent(start, duration, getExtra?.(result)));\n return result;\n } catch (err) {\n const duration = Date.now() - start;\n emit(makeEvent(start, duration, { error: (err as Error).message }));\n throw err;\n }\n }\n\n return {\n bind(...values: unknown[]): D1PreparedStatementBinding {\n const bound = stmt.bind(...values);\n return instrumentStatement(bound, query, emit, options);\n },\n\n first<T = unknown>(colName?: string): Promise<T | null> {\n const start = Date.now();\n return wrapAsync(\n () => stmt.first<T>(colName),\n start,\n (result) => ({ rowsReturned: result !== null ? 1 : 0 }),\n );\n },\n\n run<T = unknown>(): Promise<D1Result<T>> {\n const start = Date.now();\n return wrapAsync(\n () => stmt.run<T>(),\n start,\n (result) => ({\n rowsAffected: result.meta?.changes,\n duration: result.meta?.duration ?? (Date.now() - start),\n }),\n );\n },\n\n all<T = unknown>(): Promise<D1Result<T>> {\n const start = Date.now();\n return wrapAsync(\n () => stmt.all<T>(),\n start,\n (result) => ({\n rowsReturned: result.results?.length ?? 0,\n duration: result.meta?.duration ?? (Date.now() - start),\n }),\n );\n },\n\n raw<T = unknown>(rawOptions?: { columnNames?: boolean }): Promise<T[]> {\n const start = Date.now();\n return wrapAsync(\n () => stmt.raw<T>(rawOptions),\n start,\n (result) => ({ rowsReturned: result.length }),\n );\n },\n };\n}\n\n// --- SQL Parsing Helpers (lightweight, no dependencies) ---\n\nfunction parseOp(query: string): DatabaseEvent['operation'] {\n const trimmed = query.trimStart().toUpperCase();\n if (trimmed.startsWith('SELECT')) return 'SELECT';\n if (trimmed.startsWith('INSERT')) return 'INSERT';\n if (trimmed.startsWith('UPDATE')) return 'UPDATE';\n if (trimmed.startsWith('DELETE')) return 'DELETE';\n return 'OTHER';\n}\n\nfunction extractTables(query: string): string[] {\n const tables: string[] = [];\n const fromMatch = query.match(/\\bFROM\\s+(\\w+)/i);\n if (fromMatch) tables.push(fromMatch[1]);\n const intoMatch = query.match(/\\bINTO\\s+(\\w+)/i);\n if (intoMatch) tables.push(intoMatch[1]);\n const updateMatch = query.match(/\\bUPDATE\\s+(\\w+)/i);\n if (updateMatch) tables.push(updateMatch[1]);\n const joinMatches = query.matchAll(/\\bJOIN\\s+(\\w+)/gi);\n for (const m of joinMatches) tables.push(m[1]);\n return [...new Set(tables)];\n}\n\nfunction normalizeD1Query(query: string): string {\n return query\n .replace(/'[^']*'/g, '?')\n .replace(/\\b\\d+\\b/g, '?')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n","import type { KVNamespaceBinding, DatabaseEvent } from '../types.js';\nimport { generateId } from '../utils.js';\n\n// ============================================================\n// KV Namespace Wrapper\n// Wraps a KVNamespace to capture get/put/delete/list operations.\n// Events emitted as 'database' type with source: 'kv'.\n// ============================================================\n\ntype EmitFn = (event: DatabaseEvent) => void;\n\ninterface KVInstrumentOptions {\n sessionId: string;\n}\n\n/**\n * Wrap a KV namespace binding to capture operations.\n *\n * @example\n * ```ts\n * const kv = instrumentKV(env.MY_KV, emit, { sessionId });\n * await kv.put('key', 'value');\n * ```\n */\nexport function instrumentKV(\n kv: KVNamespaceBinding,\n emit: EmitFn,\n options: KVInstrumentOptions,\n): KVNamespaceBinding {\n function sanitizeKey(key: string): string {\n // Truncate and escape quotes/newlines to prevent malformed query strings\n const safe = key.length > 200 ? key.slice(0, 200) + '…' : key;\n return safe.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n');\n }\n\n function emitOp(op: string, key: string, start: number, duration: number, extra: Partial<DatabaseEvent> = {}): void {\n emit({\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query: `KV.${op}(\"${sanitizeKey(key)}\")`,\n normalizedQuery: `KV.${op}(?)`,\n duration,\n tablesAccessed: [],\n operation: op === 'get' || op === 'getWithMetadata' || op === 'list' ? 'SELECT' : op === 'put' ? 'INSERT' : op === 'delete' ? 'DELETE' : 'OTHER',\n source: 'kv',\n ...extra,\n });\n }\n\n return {\n async get(key: string, kvOptions?: unknown): Promise<string | null> {\n const start = Date.now();\n try {\n const result = await kv.get(key, kvOptions);\n emitOp('get', key, start, Date.now() - start, { rowsReturned: result !== null ? 1 : 0 });\n return result;\n } catch (err) {\n emitOp('get', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async getWithMetadata<M = unknown>(key: string, kvOptions?: unknown): Promise<{ value: string | null; metadata: M | null }> {\n const start = Date.now();\n try {\n const result = await kv.getWithMetadata<M>(key, kvOptions);\n emitOp('getWithMetadata', key, start, Date.now() - start, { rowsReturned: result.value !== null ? 1 : 0 });\n return result;\n } catch (err) {\n emitOp('getWithMetadata', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async put(key: string, value: string | ReadableStream | ArrayBuffer, kvOptions?: unknown): Promise<void> {\n const start = Date.now();\n try {\n await kv.put(key, value, kvOptions);\n emitOp('put', key, start, Date.now() - start, { rowsAffected: 1 });\n } catch (err) {\n emitOp('put', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async delete(key: string): Promise<void> {\n const start = Date.now();\n try {\n await kv.delete(key);\n emitOp('delete', key, start, Date.now() - start, { rowsAffected: 1 });\n } catch (err) {\n emitOp('delete', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async list(kvOptions?: unknown): Promise<{ keys: { name: string }[]; list_complete: boolean; cursor?: string }> {\n const start = Date.now();\n try {\n const result = await kv.list(kvOptions);\n emitOp('list', '*', start, Date.now() - start, { rowsReturned: result.keys.length });\n return result;\n } catch (err) {\n emitOp('list', '*', start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n };\n}\n","import type { R2BucketBinding, R2Object, R2ObjectBody, R2Objects, DatabaseEvent } from '../types.js';\nimport { generateId } from '../utils.js';\n\n// ============================================================\n// R2 Bucket Wrapper\n// Wraps an R2Bucket to capture get/put/delete/list/head operations.\n// Events emitted as 'database' type with source: 'r2'.\n// ============================================================\n\ntype EmitFn = (event: DatabaseEvent) => void;\n\ninterface R2InstrumentOptions {\n sessionId: string;\n}\n\n/**\n * Wrap an R2 bucket binding to capture operations.\n *\n * @example\n * ```ts\n * const bucket = instrumentR2(env.MY_BUCKET, emit, { sessionId });\n * await bucket.put('file.txt', 'contents');\n * ```\n */\nexport function instrumentR2(\n bucket: R2BucketBinding,\n emit: EmitFn,\n options: R2InstrumentOptions,\n): R2BucketBinding {\n function sanitizeKey(key: string): string {\n const safe = key.length > 200 ? key.slice(0, 200) + '…' : key;\n return safe.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n');\n }\n\n function emitOp(op: string, key: string, start: number, duration: number, extra: Partial<DatabaseEvent> = {}): void {\n emit({\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query: `R2.${op}(\"${sanitizeKey(key)}\")`,\n normalizedQuery: `R2.${op}(?)`,\n duration,\n tablesAccessed: [],\n operation: op === 'get' || op === 'list' || op === 'head' ? 'SELECT' : op === 'put' ? 'INSERT' : op === 'delete' ? 'DELETE' : 'OTHER',\n source: 'r2',\n ...extra,\n });\n }\n\n return {\n async get(key: string, r2Options?: unknown): Promise<R2ObjectBody | null> {\n const start = Date.now();\n try {\n const result = await bucket.get(key, r2Options);\n emitOp('get', key, start, Date.now() - start, {\n rowsReturned: result !== null ? 1 : 0,\n label: result ? `${result.size} bytes` : undefined,\n });\n return result;\n } catch (err) {\n emitOp('get', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async put(\n key: string,\n value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob,\n r2Options?: unknown,\n ): Promise<R2Object | null> {\n const start = Date.now();\n try {\n const result = await bucket.put(key, value, r2Options);\n emitOp('put', key, start, Date.now() - start, {\n rowsAffected: 1,\n label: result ? `${result.size} bytes` : undefined,\n });\n return result;\n } catch (err) {\n emitOp('put', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async delete(keys: string | string[]): Promise<void> {\n const keyList = Array.isArray(keys) ? keys : [keys];\n const keyLabel = keyList.length === 1 ? keyList[0] : `${keyList.length} keys`;\n const start = Date.now();\n try {\n await bucket.delete(keys);\n emitOp('delete', keyLabel, start, Date.now() - start, { rowsAffected: keyList.length });\n } catch (err) {\n emitOp('delete', keyLabel, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async list(r2Options?: unknown): Promise<R2Objects> {\n const start = Date.now();\n try {\n const result = await bucket.list(r2Options);\n emitOp('list', '*', start, Date.now() - start, { rowsReturned: result.objects.length });\n return result;\n } catch (err) {\n emitOp('list', '*', start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async head(key: string): Promise<R2Object | null> {\n const start = Date.now();\n try {\n const result = await bucket.head(key);\n emitOp('head', key, start, Date.now() - start, {\n rowsReturned: result !== null ? 1 : 0,\n label: result ? `${result.size} bytes` : undefined,\n });\n return result;\n } catch (err) {\n emitOp('head', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n };\n}\n","// ============================================================\n// @runtimescope/workers-sdk\n// Zero-dependency SDK for Cloudflare Workers.\n// Captures requests, D1 queries, KV/R2 ops, console, errors.\n// ============================================================\n\nexport { withRuntimeScope } from './handler.js';\nexport { getActiveContext } from './handler.js';\nexport type { WorkersFetchHandler, RuntimeScopeContext } from './handler.js';\n\nexport { instrumentD1 } from './bindings/d1.js';\nexport { instrumentKV } from './bindings/kv.js';\nexport { instrumentR2 } from './bindings/r2.js';\n\nexport { WorkersTransport } from './transport.js';\nexport { Sampler } from './sampler.js';\nexport { generateId, generateSessionId } from './utils.js';\n\nexport type {\n WorkersConfig,\n WorkersRuntimeEvent,\n ConsoleEvent,\n DatabaseEvent,\n NetworkEvent,\n CustomEvent,\n UIInteractionEvent,\n UserContext,\n ConsoleLevel,\n DatabaseOperation,\n DatabaseSource,\n D1DatabaseBinding,\n D1PreparedStatementBinding,\n D1Result,\n KVNamespaceBinding,\n R2BucketBinding,\n R2Object,\n R2ObjectBody,\n R2Objects,\n} from './types.js';\n\n// ============================================================\n// Convenience: instrument bindings using the active request context\n// These are shortcuts that automatically use the current request's\n// transport and session ID — no manual wiring needed.\n// ============================================================\n\nimport { getActiveContext } from './handler.js';\nimport { instrumentD1 as _instrumentD1 } from './bindings/d1.js';\nimport { instrumentKV as _instrumentKV } from './bindings/kv.js';\nimport { instrumentR2 as _instrumentR2 } from './bindings/r2.js';\nimport { generateId } from './utils.js';\nimport type {\n D1DatabaseBinding,\n KVNamespaceBinding,\n R2BucketBinding,\n CustomEvent,\n UIInteractionEvent,\n} from './types.js';\n\n/**\n * Instrument a D1 database using the active request context.\n * Must be called inside a withRuntimeScope handler.\n *\n * @example\n * ```ts\n * import { withRuntimeScope, scopeD1 } from '@runtimescope/workers-sdk';\n *\n * export default withRuntimeScope({\n * async fetch(request, env, ctx) {\n * const db = scopeD1(env.DB);\n * const users = await db.prepare('SELECT * FROM users').all();\n * return Response.json(users);\n * },\n * }, { appName: 'my-worker' });\n * ```\n */\nexport function scopeD1(db: D1DatabaseBinding): D1DatabaseBinding {\n const ctx = getActiveContext();\n if (!ctx) return db; // No active context — return unwrapped (safe for non-instrumented calls)\n return _instrumentD1(db, ctx.emit, { sessionId: ctx.sessionId });\n}\n\n/** Instrument a KV namespace using the active request context. */\nexport function scopeKV(kv: KVNamespaceBinding): KVNamespaceBinding {\n const ctx = getActiveContext();\n if (!ctx) return kv;\n return _instrumentKV(kv, ctx.emit, { sessionId: ctx.sessionId });\n}\n\n/** Instrument an R2 bucket using the active request context. */\nexport function scopeR2(bucket: R2BucketBinding): R2BucketBinding {\n const ctx = getActiveContext();\n if (!ctx) return bucket;\n return _instrumentR2(bucket, ctx.emit, { sessionId: ctx.sessionId });\n}\n\n// ============================================================\n// Custom event tracking & breadcrumbs\n// Must be called inside a withRuntimeScope handler.\n// ============================================================\n\n/**\n * Track a custom business event (e.g., user signup, payment processed).\n * Must be called inside a withRuntimeScope handler.\n *\n * @example\n * ```ts\n * import { withRuntimeScope, track } from '@runtimescope/workers-sdk';\n *\n * export default withRuntimeScope({\n * async fetch(request, env, ctx) {\n * track('payment.processed', { amount: 99.99, currency: 'USD' });\n * return new Response('OK');\n * },\n * }, { appName: 'payments-worker' });\n * ```\n */\nexport function track(name: string, properties?: Record<string, unknown>): void {\n const ctx = getActiveContext();\n if (!ctx) return;\n const event: CustomEvent = {\n eventId: generateId(),\n sessionId: ctx.sessionId,\n timestamp: Date.now(),\n eventType: 'custom',\n name,\n ...(properties && { properties }),\n };\n ctx.emit(event);\n}\n\n/**\n * Add a breadcrumb to the current request's event trail.\n * Useful for marking key points in request processing.\n *\n * @example\n * ```ts\n * import { withRuntimeScope, addBreadcrumb } from '@runtimescope/workers-sdk';\n *\n * export default withRuntimeScope({\n * async fetch(request, env, ctx) {\n * addBreadcrumb('auth check passed', { userId: '123' });\n * // ... process request\n * addBreadcrumb('cache miss, fetching from origin');\n * return new Response('OK');\n * },\n * }, { appName: 'api-worker' });\n * ```\n */\nexport function addBreadcrumb(message: string, data?: Record<string, unknown>): void {\n const ctx = getActiveContext();\n if (!ctx) return;\n const event: UIInteractionEvent = {\n eventId: generateId(),\n sessionId: ctx.sessionId,\n timestamp: Date.now(),\n eventType: 'ui',\n action: 'breadcrumb',\n target: 'manual',\n text: message,\n ...(data && { data }),\n };\n ctx.emit(event);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts","../src/transport.ts","../src/sampler.ts","../src/interceptors/console.ts","../src/handler.ts","../src/bindings/d1.ts","../src/bindings/kv.ts","../src/bindings/r2.ts","../src/index.ts"],"sourcesContent":["// ============================================================\n// Utility functions — no Node.js APIs, Workers-safe\n// ============================================================\n\n/** Generate a random event ID. Uses Web Crypto API with fallback for environments where it's unavailable at init time. */\nexport function generateId(): string {\n try {\n return crypto.randomUUID();\n } catch {\n // Fallback: manual random hex (for environments where crypto.randomUUID isn't available at module eval time)\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);\n });\n }\n}\n\n/** Generate a session ID with worker prefix */\nexport function generateSessionId(): string {\n try {\n return `wk-${crypto.randomUUID().slice(0, 16)}`;\n } catch {\n return `wk-${generateId().replace(/-/g, '').slice(0, 16)}`;\n }\n}\n\n/** Safely serialize a value, handling circular references, functions, symbols, and errors. */\nexport function safeSerialize(value: unknown, maxDepth = 5): unknown {\n const seen = new WeakSet();\n\n function walk(val: unknown, depth: number): unknown {\n if (depth > maxDepth) return '[max depth]';\n if (val === null || val === undefined) return val;\n if (typeof val === 'function') return `[Function: ${val.name || 'anonymous'}]`;\n if (typeof val === 'symbol') return val.toString();\n if (typeof val === 'bigint') return val.toString();\n if (typeof val !== 'object') return val;\n\n if (val instanceof Error) {\n return { name: val.name, message: val.message, stack: val.stack };\n }\n if (val instanceof Date) {\n return val.toISOString();\n }\n if (val instanceof RegExp) {\n return val.toString();\n }\n\n if (seen.has(val as object)) return '[Circular]';\n seen.add(val as object);\n\n if (Array.isArray(val)) {\n return val.map((v) => walk(v, depth + 1));\n }\n\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(val as Record<string, unknown>)) {\n result[key] = walk((val as Record<string, unknown>)[key], depth + 1);\n }\n return result;\n }\n\n return walk(value, 0);\n}\n","import type { WorkersRuntimeEvent, WorkersConfig, UserContext } from './types.js';\nimport { generateSessionId } from './utils.js';\n\n// ============================================================\n// Workers Transport\n// Flush events via ctx.waitUntil(fetch(...)) — no timers, no intervals.\n// Matches collector's POST /api/events endpoint.\n// ============================================================\n\nconst SDK_VERSION = '0.9.2';\nconst DEFAULT_ENDPOINT = 'http://localhost:9091/api/events';\n\nexport class WorkersTransport {\n private buffer: WorkersRuntimeEvent[] = [];\n private maxQueueSize: number;\n private url: string;\n private authToken: string | undefined;\n private appName: string;\n private projectId?: string;\n private sessionRegistered = false;\n private _droppedCount = 0;\n\n readonly sessionId: string;\n\n constructor(config: WorkersConfig) {\n this.sessionId = generateSessionId();\n this.url = config.httpEndpoint ?? DEFAULT_ENDPOINT;\n this.appName = config.appName;\n this.projectId = config.projectId;\n this.authToken = config.authToken;\n this.maxQueueSize = config.maxQueueSize ?? 500;\n }\n\n get droppedCount(): number {\n return this._droppedCount;\n }\n\n queue(event: WorkersRuntimeEvent): void {\n if (this.buffer.length >= this.maxQueueSize) {\n this.buffer.shift();\n this._droppedCount++;\n }\n this.buffer.push(event);\n }\n\n /**\n * Flush all buffered events to the collector.\n * Call via ctx.waitUntil(transport.flush()) at the end of each request.\n * Retries once on failure with a short delay.\n */\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n\n const events = this.buffer.splice(0);\n const payload: Record<string, unknown> = {\n sessionId: this.sessionId,\n events,\n };\n\n // Include appName/sdkVersion on first request to auto-register session\n if (!this.sessionRegistered) {\n payload.appName = this.appName;\n payload.sdkVersion = SDK_VERSION;\n if (this.projectId) payload.projectId = this.projectId;\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (this.authToken) {\n headers['Authorization'] = `Bearer ${this.authToken}`;\n }\n\n const body = JSON.stringify(payload);\n\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 5_000);\n\n const response = await fetch(this.url, {\n method: 'POST',\n headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n this.sessionRegistered = true;\n return;\n }\n\n // Server error (5xx) — retry once\n if (response.status >= 500 && attempt === 0) continue;\n // Client error (4xx) — don't retry\n return;\n } catch {\n // Network error or timeout — retry once\n if (attempt === 0) {\n await new Promise((r) => setTimeout(r, 200));\n continue;\n }\n // Second attempt failed — events are lost (acceptable for edge observability)\n }\n }\n }\n}\n","import type { WorkersRuntimeEvent } from './types.js';\n\n// ============================================================\n// Sampler — probabilistic + rate limiting\n// Adapted from server-sdk, no Node.js APIs.\n// ============================================================\n\nexport interface SamplerConfig {\n /** Probabilistic sample rate: 0.0–1.0 (default: 1.0 = keep all) */\n sampleRate?: number;\n}\n\nexport class Sampler {\n private _droppedCount = 0;\n\n constructor(private config: SamplerConfig) {}\n\n get droppedCount(): number {\n return this._droppedCount;\n }\n\n shouldSample(_event: WorkersRuntimeEvent): boolean {\n const rate = this.config.sampleRate;\n if (rate !== undefined && rate < 1) {\n if (Math.random() > rate) {\n this._droppedCount++;\n return false;\n }\n }\n return true;\n }\n}\n","import type { ConsoleEvent, ConsoleLevel } from '../types.js';\nimport { generateId, safeSerialize } from '../utils.js';\n\n// ============================================================\n// Console Interceptor — Workers-safe\n// Patches console.log/warn/error/info/debug/trace.\n// Returns a restore function for cleanup.\n// ============================================================\n\ntype EmitFn = (event: ConsoleEvent) => void;\n\nconst LEVELS: ConsoleLevel[] = ['log', 'warn', 'error', 'info', 'debug', 'trace'];\n\nexport interface ConsoleInterceptorOptions {\n levels?: ConsoleLevel[];\n sessionId: string;\n}\n\nexport function interceptConsole(\n emit: EmitFn,\n options: ConsoleInterceptorOptions\n): () => void {\n const levels = options.levels ?? LEVELS;\n const originals: Record<string, (...args: unknown[]) => void> = {};\n\n for (const level of levels) {\n originals[level] = console[level].bind(console);\n\n console[level] = (...args: unknown[]) => {\n const message = args\n .map((a) => (typeof a === 'string' ? a : stringifyArg(a)))\n .join(' ');\n\n const event: ConsoleEvent = {\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: Date.now(),\n eventType: 'console',\n level,\n message,\n args: args.map((a) => safeSerialize(a, 3)),\n stackTrace:\n level === 'error' || level === 'trace'\n ? new Error().stack?.split('\\n').slice(2).join('\\n')\n : undefined,\n source: 'workers',\n };\n\n // emit() already applies beforeSend — don't double-filter\n emit(event);\n\n // Call original — MUST use saved reference\n originals[level](...args);\n };\n }\n\n return () => {\n for (const level of levels) {\n console[level] = originals[level];\n }\n };\n}\n\nfunction stringifyArg(arg: unknown): string {\n try {\n return JSON.stringify(arg);\n } catch {\n return String(arg);\n }\n}\n","import type {\n WorkersConfig,\n WorkersRuntimeEvent,\n NetworkEvent,\n ConsoleEvent,\n UserContext,\n} from './types.js';\nimport { WorkersTransport } from './transport.js';\nimport { Sampler } from './sampler.js';\nimport { interceptConsole } from './interceptors/console.js';\nimport { generateId } from './utils.js';\n\n// ============================================================\n// withRuntimeScope — wraps a Workers fetch handler\n// Captures request/response metrics, errors, console output.\n// Flushes all events via ctx.waitUntil() at end of request.\n// ============================================================\n\n/** Internal context exposed to binding wrappers */\nexport interface RuntimeScopeContext {\n transport: WorkersTransport;\n sessionId: string;\n config: WorkersConfig;\n emit: (event: WorkersRuntimeEvent) => void;\n}\n\n// Per-request context — AsyncLocalStorage for isolation under concurrent requests.\n// Lazy-loaded to avoid crashing in environments where node:async_hooks isn't available.\nlet _contextStorage: { run: <T>(ctx: RuntimeScopeContext, fn: () => T) => T; getStore: () => RuntimeScopeContext | undefined } | null = null;\n\ntry {\n const { AsyncLocalStorage } = require('node:async_hooks');\n _contextStorage = new AsyncLocalStorage<RuntimeScopeContext>();\n} catch {\n // Fallback: simple global context (safe for single-request-at-a-time dev mode)\n let _currentContext: RuntimeScopeContext | undefined;\n _contextStorage = {\n run<T>(ctx: RuntimeScopeContext, fn: () => T): T {\n const prev = _currentContext;\n _currentContext = ctx;\n try { return fn(); } finally { _currentContext = prev; }\n },\n getStore() { return _currentContext; },\n };\n}\n\n/** Get the active RuntimeScope context (used by binding wrappers) */\nexport function getActiveContext(): RuntimeScopeContext | null {\n return _contextStorage?.getStore() ?? null;\n}\n\nconst DEFAULT_REDACT_HEADERS = ['authorization', 'cookie', 'set-cookie'];\n\nexport interface WorkersFetchHandler {\n fetch(\n request: Request,\n env: unknown,\n ctx: ExecutionContext,\n ): Response | Promise<Response>;\n}\n\n/**\n * Wrap a Workers fetch handler to capture request/response metrics,\n * errors, and console output. Events are flushed via ctx.waitUntil().\n *\n * @example\n * ```ts\n * import { withRuntimeScope } from '@runtimescope/workers-sdk';\n *\n * export default withRuntimeScope({\n * async fetch(request, env, ctx) {\n * return new Response('Hello!');\n * },\n * }, { appName: 'my-worker' });\n * ```\n */\nexport function withRuntimeScope(\n handler: WorkersFetchHandler,\n config: WorkersConfig,\n): WorkersFetchHandler {\n // Never crash the app — if SDK init fails, pass through to the original handler\n try {\n return _withRuntimeScope(handler, config);\n } catch (err) {\n console.warn('[RuntimeScope] SDK init failed, running without instrumentation:', (err as Error).message);\n return handler;\n }\n}\n\nfunction _withRuntimeScope(\n handler: WorkersFetchHandler,\n config: WorkersConfig,\n): WorkersFetchHandler {\n const transport = new WorkersTransport(config);\n const sampler = config.sampleRate !== undefined && config.sampleRate < 1\n ? new Sampler({ sampleRate: config.sampleRate })\n : null;\n\n const redactHeaders = config.redactHeaders ?? DEFAULT_REDACT_HEADERS;\n const redactSet = new Set(redactHeaders.map((h) => h.toLowerCase()));\n\n function emit(event: WorkersRuntimeEvent): void {\n if (sampler && !sampler.shouldSample(event)) return;\n if (config.beforeSend) {\n const filtered = config.beforeSend(event);\n if (!filtered) return;\n transport.queue(filtered);\n } else {\n transport.queue(event);\n }\n }\n\n // Set up console interceptor (persistent across requests)\n let restoreConsole: (() => void) | null = null;\n if (config.captureConsole !== false) {\n restoreConsole = interceptConsole(emit, {\n sessionId: transport.sessionId,\n });\n }\n\n function extractHeaders(headers: Headers): Record<string, string> {\n if (!config.captureHeaders) return {};\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key] = redactSet.has(key.toLowerCase()) ? '[REDACTED]' : value;\n });\n return result;\n }\n\n return {\n async fetch(\n request: Request,\n env: unknown,\n ctx: ExecutionContext,\n ): Promise<Response> {\n const start = Date.now();\n const url = new URL(request.url);\n\n // Run handler inside AsyncLocalStorage so binding wrappers\n // get the correct per-request context even under concurrency\n const rsContext: RuntimeScopeContext = {\n transport,\n sessionId: transport.sessionId,\n config,\n emit,\n };\n\n return _contextStorage.run(rsContext, async () => {\n try {\n const response = await handler.fetch(request, env, ctx);\n const duration = Date.now() - start;\n\n const networkEvent: NetworkEvent = {\n eventId: generateId(),\n sessionId: transport.sessionId,\n timestamp: start,\n eventType: 'network',\n url: url.pathname + url.search,\n method: request.method,\n status: response.status,\n duration,\n requestHeaders: extractHeaders(request.headers),\n responseHeaders: extractHeaders(response.headers),\n requestBodySize: 0,\n responseBodySize: 0,\n ttfb: duration,\n source: 'workers',\n direction: 'incoming',\n cfProperties: extractCfProperties(request),\n };\n\n emit(networkEvent);\n ctx.waitUntil(transport.flush());\n return response;\n } catch (error) {\n const duration = Date.now() - start;\n\n // Capture the error as a console error event\n const errorEvent: ConsoleEvent = {\n eventId: generateId(),\n sessionId: transport.sessionId,\n timestamp: Date.now(),\n eventType: 'console',\n level: 'error',\n message: error instanceof Error ? error.message : String(error),\n args: [error instanceof Error ? { name: error.name, message: error.message, stack: error.stack } : String(error)],\n stackTrace: error instanceof Error ? error.stack : undefined,\n };\n\n emit(errorEvent);\n\n // Also capture the failed request\n const networkEvent: NetworkEvent = {\n eventId: generateId(),\n sessionId: transport.sessionId,\n timestamp: start,\n eventType: 'network',\n url: url.pathname + url.search,\n method: request.method,\n status: 500,\n duration,\n requestHeaders: extractHeaders(request.headers),\n responseHeaders: {},\n requestBodySize: 0,\n responseBodySize: 0,\n ttfb: duration,\n source: 'workers',\n direction: 'incoming',\n cfProperties: extractCfProperties(request),\n errorMessage: error instanceof Error ? error.message : String(error),\n };\n\n emit(networkEvent);\n ctx.waitUntil(transport.flush());\n throw error;\n }\n });\n },\n };\n}\n\nfunction extractCfProperties(request: Request): NetworkEvent['cfProperties'] | undefined {\n // request.cf is Cloudflare-specific — may not exist in non-CF environments\n const cf = (request as unknown as { cf?: Record<string, unknown> }).cf;\n if (!cf) return undefined;\n\n return {\n colo: cf.colo as string | undefined,\n country: cf.country as string | undefined,\n city: cf.city as string | undefined,\n region: cf.region as string | undefined,\n asn: cf.asn as number | undefined,\n httpProtocol: cf.httpProtocol as string | undefined,\n tlsVersion: cf.tlsVersion as string | undefined,\n };\n}\n","import type {\n D1DatabaseBinding,\n D1PreparedStatementBinding,\n D1Result,\n D1ExecResult,\n DatabaseEvent,\n} from '../types.js';\nimport { generateId } from '../utils.js';\n\n// ============================================================\n// D1 Binding Wrapper\n// Wraps a D1Database to capture SQL queries, timing, and results.\n// Events emitted as 'database' type with source: 'd1'.\n// ============================================================\n\ntype EmitFn = (event: DatabaseEvent) => void;\n\ninterface D1InstrumentOptions {\n sessionId: string;\n}\n\n/**\n * Wrap a D1 database binding to capture queries.\n *\n * @example\n * ```ts\n * const db = instrumentD1(env.DB, transport.sessionId);\n * const results = await db.prepare('SELECT * FROM users').all();\n * ```\n */\nexport function instrumentD1(\n db: D1DatabaseBinding,\n emit: EmitFn,\n options: D1InstrumentOptions,\n): D1DatabaseBinding {\n return {\n prepare(query: string): D1PreparedStatementBinding {\n const stmt = db.prepare(query);\n return instrumentStatement(stmt, query, emit, options);\n },\n\n async batch<T = unknown>(statements: D1PreparedStatementBinding[]): Promise<D1Result<T>[]> {\n const start = Date.now();\n try {\n const results = await db.batch<T>(statements);\n const duration = Date.now() - start;\n emit({\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query: `BATCH (${statements.length} statements)`,\n normalizedQuery: 'BATCH',\n duration,\n tablesAccessed: [],\n operation: 'OTHER',\n source: 'd1',\n rowsReturned: results.reduce((sum, r) => sum + (r.results?.length ?? 0), 0),\n });\n return results;\n } catch (err) {\n const duration = Date.now() - start;\n emit({\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query: `BATCH (${statements.length} statements)`,\n normalizedQuery: 'BATCH',\n duration,\n tablesAccessed: [],\n operation: 'OTHER',\n source: 'd1',\n error: (err as Error).message,\n });\n throw err;\n }\n },\n\n async exec(query: string): Promise<D1ExecResult> {\n const start = Date.now();\n try {\n const result = await db.exec(query);\n emit({\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query,\n normalizedQuery: normalizeD1Query(query),\n duration: result.duration,\n tablesAccessed: extractTables(query),\n operation: parseOp(query),\n source: 'd1',\n rowsAffected: result.count,\n });\n return result;\n } catch (err) {\n const duration = Date.now() - start;\n emit({\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query,\n normalizedQuery: normalizeD1Query(query),\n duration,\n tablesAccessed: extractTables(query),\n operation: parseOp(query),\n source: 'd1',\n error: (err as Error).message,\n });\n throw err;\n }\n },\n\n dump(): Promise<ArrayBuffer> {\n return db.dump();\n },\n };\n}\n\nfunction instrumentStatement(\n stmt: D1PreparedStatementBinding,\n query: string,\n emit: EmitFn,\n options: D1InstrumentOptions,\n): D1PreparedStatementBinding {\n const op = parseOp(query);\n const tables = extractTables(query);\n const normalized = normalizeD1Query(query);\n\n function makeEvent(start: number, duration: number, extra: Partial<DatabaseEvent> = {}): DatabaseEvent {\n return {\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query,\n normalizedQuery: normalized,\n duration,\n tablesAccessed: tables,\n operation: op,\n source: 'd1',\n ...extra,\n };\n }\n\n async function wrapAsync<T>(fn: () => Promise<T>, start: number, getExtra?: (result: T) => Partial<DatabaseEvent>): Promise<T> {\n try {\n const result = await fn();\n const duration = Date.now() - start;\n emit(makeEvent(start, duration, getExtra?.(result)));\n return result;\n } catch (err) {\n const duration = Date.now() - start;\n emit(makeEvent(start, duration, { error: (err as Error).message }));\n throw err;\n }\n }\n\n return {\n bind(...values: unknown[]): D1PreparedStatementBinding {\n const bound = stmt.bind(...values);\n return instrumentStatement(bound, query, emit, options);\n },\n\n first<T = unknown>(colName?: string): Promise<T | null> {\n const start = Date.now();\n return wrapAsync(\n () => stmt.first<T>(colName),\n start,\n (result) => ({ rowsReturned: result !== null ? 1 : 0 }),\n );\n },\n\n run<T = unknown>(): Promise<D1Result<T>> {\n const start = Date.now();\n return wrapAsync(\n () => stmt.run<T>(),\n start,\n (result) => ({\n rowsAffected: result.meta?.changes,\n duration: result.meta?.duration ?? (Date.now() - start),\n }),\n );\n },\n\n all<T = unknown>(): Promise<D1Result<T>> {\n const start = Date.now();\n return wrapAsync(\n () => stmt.all<T>(),\n start,\n (result) => ({\n rowsReturned: result.results?.length ?? 0,\n duration: result.meta?.duration ?? (Date.now() - start),\n }),\n );\n },\n\n raw<T = unknown>(rawOptions?: { columnNames?: boolean }): Promise<T[]> {\n const start = Date.now();\n return wrapAsync(\n () => stmt.raw<T>(rawOptions),\n start,\n (result) => ({ rowsReturned: result.length }),\n );\n },\n };\n}\n\n// --- SQL Parsing Helpers (lightweight, no dependencies) ---\n\nfunction parseOp(query: string): DatabaseEvent['operation'] {\n const trimmed = query.trimStart().toUpperCase();\n if (trimmed.startsWith('SELECT')) return 'SELECT';\n if (trimmed.startsWith('INSERT')) return 'INSERT';\n if (trimmed.startsWith('UPDATE')) return 'UPDATE';\n if (trimmed.startsWith('DELETE')) return 'DELETE';\n return 'OTHER';\n}\n\nfunction extractTables(query: string): string[] {\n const tables: string[] = [];\n const fromMatch = query.match(/\\bFROM\\s+(\\w+)/i);\n if (fromMatch) tables.push(fromMatch[1]);\n const intoMatch = query.match(/\\bINTO\\s+(\\w+)/i);\n if (intoMatch) tables.push(intoMatch[1]);\n const updateMatch = query.match(/\\bUPDATE\\s+(\\w+)/i);\n if (updateMatch) tables.push(updateMatch[1]);\n const joinMatches = query.matchAll(/\\bJOIN\\s+(\\w+)/gi);\n for (const m of joinMatches) tables.push(m[1]);\n return [...new Set(tables)];\n}\n\nfunction normalizeD1Query(query: string): string {\n return query\n .replace(/'[^']*'/g, '?')\n .replace(/\\b\\d+\\b/g, '?')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n","import type { KVNamespaceBinding, DatabaseEvent } from '../types.js';\nimport { generateId } from '../utils.js';\n\n// ============================================================\n// KV Namespace Wrapper\n// Wraps a KVNamespace to capture get/put/delete/list operations.\n// Events emitted as 'database' type with source: 'kv'.\n// ============================================================\n\ntype EmitFn = (event: DatabaseEvent) => void;\n\ninterface KVInstrumentOptions {\n sessionId: string;\n}\n\n/**\n * Wrap a KV namespace binding to capture operations.\n *\n * @example\n * ```ts\n * const kv = instrumentKV(env.MY_KV, emit, { sessionId });\n * await kv.put('key', 'value');\n * ```\n */\nexport function instrumentKV(\n kv: KVNamespaceBinding,\n emit: EmitFn,\n options: KVInstrumentOptions,\n): KVNamespaceBinding {\n function sanitizeKey(key: string): string {\n // Truncate and escape quotes/newlines to prevent malformed query strings\n const safe = key.length > 200 ? key.slice(0, 200) + '…' : key;\n return safe.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n');\n }\n\n function emitOp(op: string, key: string, start: number, duration: number, extra: Partial<DatabaseEvent> = {}): void {\n emit({\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query: `KV.${op}(\"${sanitizeKey(key)}\")`,\n normalizedQuery: `KV.${op}(?)`,\n duration,\n tablesAccessed: [],\n operation: op === 'get' || op === 'getWithMetadata' || op === 'list' ? 'SELECT' : op === 'put' ? 'INSERT' : op === 'delete' ? 'DELETE' : 'OTHER',\n source: 'kv',\n ...extra,\n });\n }\n\n return {\n async get(key: string, kvOptions?: unknown): Promise<string | null> {\n const start = Date.now();\n try {\n const result = await kv.get(key, kvOptions);\n emitOp('get', key, start, Date.now() - start, { rowsReturned: result !== null ? 1 : 0 });\n return result;\n } catch (err) {\n emitOp('get', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async getWithMetadata<M = unknown>(key: string, kvOptions?: unknown): Promise<{ value: string | null; metadata: M | null }> {\n const start = Date.now();\n try {\n const result = await kv.getWithMetadata<M>(key, kvOptions);\n emitOp('getWithMetadata', key, start, Date.now() - start, { rowsReturned: result.value !== null ? 1 : 0 });\n return result;\n } catch (err) {\n emitOp('getWithMetadata', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async put(key: string, value: string | ReadableStream | ArrayBuffer, kvOptions?: unknown): Promise<void> {\n const start = Date.now();\n try {\n await kv.put(key, value, kvOptions);\n emitOp('put', key, start, Date.now() - start, { rowsAffected: 1 });\n } catch (err) {\n emitOp('put', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async delete(key: string): Promise<void> {\n const start = Date.now();\n try {\n await kv.delete(key);\n emitOp('delete', key, start, Date.now() - start, { rowsAffected: 1 });\n } catch (err) {\n emitOp('delete', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async list(kvOptions?: unknown): Promise<{ keys: { name: string }[]; list_complete: boolean; cursor?: string }> {\n const start = Date.now();\n try {\n const result = await kv.list(kvOptions);\n emitOp('list', '*', start, Date.now() - start, { rowsReturned: result.keys.length });\n return result;\n } catch (err) {\n emitOp('list', '*', start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n };\n}\n","import type { R2BucketBinding, R2Object, R2ObjectBody, R2Objects, DatabaseEvent } from '../types.js';\nimport { generateId } from '../utils.js';\n\n// ============================================================\n// R2 Bucket Wrapper\n// Wraps an R2Bucket to capture get/put/delete/list/head operations.\n// Events emitted as 'database' type with source: 'r2'.\n// ============================================================\n\ntype EmitFn = (event: DatabaseEvent) => void;\n\ninterface R2InstrumentOptions {\n sessionId: string;\n}\n\n/**\n * Wrap an R2 bucket binding to capture operations.\n *\n * @example\n * ```ts\n * const bucket = instrumentR2(env.MY_BUCKET, emit, { sessionId });\n * await bucket.put('file.txt', 'contents');\n * ```\n */\nexport function instrumentR2(\n bucket: R2BucketBinding,\n emit: EmitFn,\n options: R2InstrumentOptions,\n): R2BucketBinding {\n function sanitizeKey(key: string): string {\n const safe = key.length > 200 ? key.slice(0, 200) + '…' : key;\n return safe.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n');\n }\n\n function emitOp(op: string, key: string, start: number, duration: number, extra: Partial<DatabaseEvent> = {}): void {\n emit({\n eventId: generateId(),\n sessionId: options.sessionId,\n timestamp: start,\n eventType: 'database',\n query: `R2.${op}(\"${sanitizeKey(key)}\")`,\n normalizedQuery: `R2.${op}(?)`,\n duration,\n tablesAccessed: [],\n operation: op === 'get' || op === 'list' || op === 'head' ? 'SELECT' : op === 'put' ? 'INSERT' : op === 'delete' ? 'DELETE' : 'OTHER',\n source: 'r2',\n ...extra,\n });\n }\n\n return {\n async get(key: string, r2Options?: unknown): Promise<R2ObjectBody | null> {\n const start = Date.now();\n try {\n const result = await bucket.get(key, r2Options);\n emitOp('get', key, start, Date.now() - start, {\n rowsReturned: result !== null ? 1 : 0,\n label: result ? `${result.size} bytes` : undefined,\n });\n return result;\n } catch (err) {\n emitOp('get', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async put(\n key: string,\n value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob,\n r2Options?: unknown,\n ): Promise<R2Object | null> {\n const start = Date.now();\n try {\n const result = await bucket.put(key, value, r2Options);\n emitOp('put', key, start, Date.now() - start, {\n rowsAffected: 1,\n label: result ? `${result.size} bytes` : undefined,\n });\n return result;\n } catch (err) {\n emitOp('put', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async delete(keys: string | string[]): Promise<void> {\n const keyList = Array.isArray(keys) ? keys : [keys];\n const keyLabel = keyList.length === 1 ? keyList[0] : `${keyList.length} keys`;\n const start = Date.now();\n try {\n await bucket.delete(keys);\n emitOp('delete', keyLabel, start, Date.now() - start, { rowsAffected: keyList.length });\n } catch (err) {\n emitOp('delete', keyLabel, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async list(r2Options?: unknown): Promise<R2Objects> {\n const start = Date.now();\n try {\n const result = await bucket.list(r2Options);\n emitOp('list', '*', start, Date.now() - start, { rowsReturned: result.objects.length });\n return result;\n } catch (err) {\n emitOp('list', '*', start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n\n async head(key: string): Promise<R2Object | null> {\n const start = Date.now();\n try {\n const result = await bucket.head(key);\n emitOp('head', key, start, Date.now() - start, {\n rowsReturned: result !== null ? 1 : 0,\n label: result ? `${result.size} bytes` : undefined,\n });\n return result;\n } catch (err) {\n emitOp('head', key, start, Date.now() - start, { error: (err as Error).message });\n throw err;\n }\n },\n };\n}\n","// ============================================================\n// @runtimescope/workers-sdk\n// Zero-dependency SDK for Cloudflare Workers.\n// Captures requests, D1 queries, KV/R2 ops, console, errors.\n// ============================================================\n\nexport { withRuntimeScope } from './handler.js';\nexport { getActiveContext } from './handler.js';\nexport type { WorkersFetchHandler, RuntimeScopeContext } from './handler.js';\n\nexport { instrumentD1 } from './bindings/d1.js';\nexport { instrumentKV } from './bindings/kv.js';\nexport { instrumentR2 } from './bindings/r2.js';\n\nexport { WorkersTransport } from './transport.js';\nexport { Sampler } from './sampler.js';\nexport { generateId, generateSessionId } from './utils.js';\n\nexport type {\n WorkersConfig,\n WorkersRuntimeEvent,\n ConsoleEvent,\n DatabaseEvent,\n NetworkEvent,\n CustomEvent,\n UIInteractionEvent,\n UserContext,\n ConsoleLevel,\n DatabaseOperation,\n DatabaseSource,\n D1DatabaseBinding,\n D1PreparedStatementBinding,\n D1Result,\n KVNamespaceBinding,\n R2BucketBinding,\n R2Object,\n R2ObjectBody,\n R2Objects,\n} from './types.js';\n\n// ============================================================\n// Convenience: instrument bindings using the active request context\n// These are shortcuts that automatically use the current request's\n// transport and session ID — no manual wiring needed.\n// ============================================================\n\nimport { getActiveContext } from './handler.js';\nimport { instrumentD1 as _instrumentD1 } from './bindings/d1.js';\nimport { instrumentKV as _instrumentKV } from './bindings/kv.js';\nimport { instrumentR2 as _instrumentR2 } from './bindings/r2.js';\nimport { generateId } from './utils.js';\nimport type {\n D1DatabaseBinding,\n KVNamespaceBinding,\n R2BucketBinding,\n CustomEvent,\n UIInteractionEvent,\n} from './types.js';\n\n/**\n * Instrument a D1 database using the active request context.\n * Must be called inside a withRuntimeScope handler.\n *\n * @example\n * ```ts\n * import { withRuntimeScope, scopeD1 } from '@runtimescope/workers-sdk';\n *\n * export default withRuntimeScope({\n * async fetch(request, env, ctx) {\n * const db = scopeD1(env.DB);\n * const users = await db.prepare('SELECT * FROM users').all();\n * return Response.json(users);\n * },\n * }, { appName: 'my-worker' });\n * ```\n */\nexport function scopeD1<T extends D1DatabaseBinding>(db: T): T {\n const ctx = getActiveContext();\n if (!ctx) return db; // No active context — return unwrapped (safe for non-instrumented calls)\n return _instrumentD1(db, ctx.emit, { sessionId: ctx.sessionId }) as T;\n}\n\n/** Instrument a KV namespace using the active request context. */\nexport function scopeKV<T extends KVNamespaceBinding>(kv: T): T {\n const ctx = getActiveContext();\n if (!ctx) return kv;\n return _instrumentKV(kv, ctx.emit, { sessionId: ctx.sessionId }) as T;\n}\n\n/** Instrument an R2 bucket using the active request context. */\nexport function scopeR2<T extends R2BucketBinding>(bucket: T): T {\n const ctx = getActiveContext();\n if (!ctx) return bucket;\n return _instrumentR2(bucket, ctx.emit, { sessionId: ctx.sessionId }) as T;\n}\n\n// ============================================================\n// Custom event tracking & breadcrumbs\n// Must be called inside a withRuntimeScope handler.\n// ============================================================\n\n/**\n * Track a custom business event (e.g., user signup, payment processed).\n * Must be called inside a withRuntimeScope handler.\n *\n * @example\n * ```ts\n * import { withRuntimeScope, track } from '@runtimescope/workers-sdk';\n *\n * export default withRuntimeScope({\n * async fetch(request, env, ctx) {\n * track('payment.processed', { amount: 99.99, currency: 'USD' });\n * return new Response('OK');\n * },\n * }, { appName: 'payments-worker' });\n * ```\n */\nexport function track(name: string, properties?: Record<string, unknown>): void {\n const ctx = getActiveContext();\n if (!ctx) return;\n const event: CustomEvent = {\n eventId: generateId(),\n sessionId: ctx.sessionId,\n timestamp: Date.now(),\n eventType: 'custom',\n name,\n ...(properties && { properties }),\n };\n ctx.emit(event);\n}\n\n/**\n * Add a breadcrumb to the current request's event trail.\n * Useful for marking key points in request processing.\n *\n * @example\n * ```ts\n * import { withRuntimeScope, addBreadcrumb } from '@runtimescope/workers-sdk';\n *\n * export default withRuntimeScope({\n * async fetch(request, env, ctx) {\n * addBreadcrumb('auth check passed', { userId: '123' });\n * // ... process request\n * addBreadcrumb('cache miss, fetching from origin');\n * return new Response('OK');\n * },\n * }, { appName: 'api-worker' });\n * ```\n */\nexport function addBreadcrumb(message: string, data?: Record<string, unknown>): void {\n const ctx = getActiveContext();\n if (!ctx) return;\n const event: UIInteractionEvent = {\n eventId: generateId(),\n sessionId: ctx.sessionId,\n timestamp: Date.now(),\n eventType: 'ui',\n action: 'breadcrumb',\n target: 'manual',\n text: message,\n ...(data && { data }),\n };\n ctx.emit(event);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
package/dist/index.d.ts
DELETED
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
type ConsoleLevel = 'log' | 'warn' | 'error' | 'info' | 'debug' | 'trace';
|
|
2
|
-
interface ConsoleEvent {
|
|
3
|
-
eventId: string;
|
|
4
|
-
sessionId: string;
|
|
5
|
-
timestamp: number;
|
|
6
|
-
eventType: 'console';
|
|
7
|
-
level: ConsoleLevel;
|
|
8
|
-
message: string;
|
|
9
|
-
args: unknown[];
|
|
10
|
-
stackTrace?: string;
|
|
11
|
-
sourceFile?: string;
|
|
12
|
-
}
|
|
13
|
-
type DatabaseOperation = 'SELECT' | 'INSERT' | 'UPDATE' | 'DELETE' | 'OTHER';
|
|
14
|
-
type DatabaseSource = 'd1' | 'kv' | 'r2' | 'generic';
|
|
15
|
-
interface DatabaseEvent {
|
|
16
|
-
eventId: string;
|
|
17
|
-
sessionId: string;
|
|
18
|
-
timestamp: number;
|
|
19
|
-
eventType: 'database';
|
|
20
|
-
query: string;
|
|
21
|
-
normalizedQuery: string;
|
|
22
|
-
duration: number;
|
|
23
|
-
rowsReturned?: number;
|
|
24
|
-
rowsAffected?: number;
|
|
25
|
-
tablesAccessed: string[];
|
|
26
|
-
operation: DatabaseOperation;
|
|
27
|
-
source: DatabaseSource;
|
|
28
|
-
error?: string;
|
|
29
|
-
label?: string;
|
|
30
|
-
}
|
|
31
|
-
interface NetworkEvent {
|
|
32
|
-
eventId: string;
|
|
33
|
-
sessionId: string;
|
|
34
|
-
timestamp: number;
|
|
35
|
-
eventType: 'network';
|
|
36
|
-
url: string;
|
|
37
|
-
method: string;
|
|
38
|
-
status: number;
|
|
39
|
-
duration: number;
|
|
40
|
-
requestHeaders: Record<string, string>;
|
|
41
|
-
responseHeaders: Record<string, string>;
|
|
42
|
-
requestBodySize: number;
|
|
43
|
-
responseBodySize: number;
|
|
44
|
-
ttfb: number;
|
|
45
|
-
source: 'workers';
|
|
46
|
-
direction: 'incoming';
|
|
47
|
-
/** Cloudflare-specific properties from request.cf */
|
|
48
|
-
cfProperties?: {
|
|
49
|
-
colo?: string;
|
|
50
|
-
country?: string;
|
|
51
|
-
city?: string;
|
|
52
|
-
region?: string;
|
|
53
|
-
asn?: number;
|
|
54
|
-
httpProtocol?: string;
|
|
55
|
-
tlsVersion?: string;
|
|
56
|
-
};
|
|
57
|
-
errorMessage?: string;
|
|
58
|
-
}
|
|
59
|
-
interface CustomEvent {
|
|
60
|
-
eventId: string;
|
|
61
|
-
sessionId: string;
|
|
62
|
-
timestamp: number;
|
|
63
|
-
eventType: 'custom';
|
|
64
|
-
name: string;
|
|
65
|
-
properties?: Record<string, unknown>;
|
|
66
|
-
}
|
|
67
|
-
type UIInteractionAction = 'breadcrumb';
|
|
68
|
-
interface UIInteractionEvent {
|
|
69
|
-
eventId: string;
|
|
70
|
-
sessionId: string;
|
|
71
|
-
timestamp: number;
|
|
72
|
-
eventType: 'ui';
|
|
73
|
-
action: UIInteractionAction;
|
|
74
|
-
target: string;
|
|
75
|
-
text?: string;
|
|
76
|
-
data?: Record<string, unknown>;
|
|
77
|
-
}
|
|
78
|
-
type WorkersRuntimeEvent = ConsoleEvent | DatabaseEvent | NetworkEvent | CustomEvent | UIInteractionEvent;
|
|
79
|
-
interface WorkersConfig {
|
|
80
|
-
/** App name — identifies this worker in session info */
|
|
81
|
-
appName: string;
|
|
82
|
-
/** Project ID — links this worker to a PM project */
|
|
83
|
-
projectId?: string;
|
|
84
|
-
/** HTTP endpoint for collector (default: http://localhost:9091/api/events) */
|
|
85
|
-
httpEndpoint?: string;
|
|
86
|
-
/** Auth token for authenticated collectors */
|
|
87
|
-
authToken?: string;
|
|
88
|
-
/** Probabilistic sample rate: 0.0–1.0 (default: 1.0 = keep all) */
|
|
89
|
-
sampleRate?: number;
|
|
90
|
-
/** Max events queued per request before dropping (default: 500) */
|
|
91
|
-
maxQueueSize?: number;
|
|
92
|
-
/** Capture console.log/warn/error (default: true) */
|
|
93
|
-
captureConsole?: boolean;
|
|
94
|
-
/** Include request/response headers in network events (default: false) */
|
|
95
|
-
captureHeaders?: boolean;
|
|
96
|
-
/** Headers to redact from network events (default: ['authorization', 'cookie', 'set-cookie']) */
|
|
97
|
-
redactHeaders?: string[];
|
|
98
|
-
/** Filter/modify events before sending. Return null to drop. */
|
|
99
|
-
beforeSend?: (event: WorkersRuntimeEvent) => WorkersRuntimeEvent | null;
|
|
100
|
-
/** User context attached to all events */
|
|
101
|
-
user?: UserContext | null;
|
|
102
|
-
}
|
|
103
|
-
interface UserContext {
|
|
104
|
-
id?: string;
|
|
105
|
-
email?: string;
|
|
106
|
-
name?: string;
|
|
107
|
-
[key: string]: unknown;
|
|
108
|
-
}
|
|
109
|
-
/** Minimal D1Database interface — matches Cloudflare's D1Database */
|
|
110
|
-
interface D1DatabaseBinding {
|
|
111
|
-
prepare(query: string): D1PreparedStatementBinding;
|
|
112
|
-
batch<T = unknown>(statements: D1PreparedStatementBinding[]): Promise<D1Result<T>[]>;
|
|
113
|
-
exec(query: string): Promise<D1ExecResult>;
|
|
114
|
-
dump(): Promise<ArrayBuffer>;
|
|
115
|
-
}
|
|
116
|
-
interface D1PreparedStatementBinding {
|
|
117
|
-
bind(...values: unknown[]): D1PreparedStatementBinding;
|
|
118
|
-
first<T = unknown>(colName?: string): Promise<T | null>;
|
|
119
|
-
run<T = unknown>(): Promise<D1Result<T>>;
|
|
120
|
-
all<T = unknown>(): Promise<D1Result<T>>;
|
|
121
|
-
raw<T = unknown>(options?: {
|
|
122
|
-
columnNames?: boolean;
|
|
123
|
-
}): Promise<T[]>;
|
|
124
|
-
}
|
|
125
|
-
interface D1Result<T = unknown> {
|
|
126
|
-
results?: T[];
|
|
127
|
-
success: boolean;
|
|
128
|
-
meta: {
|
|
129
|
-
duration: number;
|
|
130
|
-
rows_read: number;
|
|
131
|
-
rows_written: number;
|
|
132
|
-
last_row_id: number;
|
|
133
|
-
changed_db: boolean;
|
|
134
|
-
size_after: number;
|
|
135
|
-
changes: number;
|
|
136
|
-
};
|
|
137
|
-
error?: string;
|
|
138
|
-
}
|
|
139
|
-
interface D1ExecResult {
|
|
140
|
-
count: number;
|
|
141
|
-
duration: number;
|
|
142
|
-
}
|
|
143
|
-
/** Minimal KVNamespace interface — matches Cloudflare's KVNamespace */
|
|
144
|
-
interface KVNamespaceBinding {
|
|
145
|
-
get(key: string, options?: unknown): Promise<string | null>;
|
|
146
|
-
getWithMetadata<M = unknown>(key: string, options?: unknown): Promise<{
|
|
147
|
-
value: string | null;
|
|
148
|
-
metadata: M | null;
|
|
149
|
-
}>;
|
|
150
|
-
put(key: string, value: string | ReadableStream | ArrayBuffer, options?: unknown): Promise<void>;
|
|
151
|
-
delete(key: string): Promise<void>;
|
|
152
|
-
list(options?: unknown): Promise<{
|
|
153
|
-
keys: {
|
|
154
|
-
name: string;
|
|
155
|
-
}[];
|
|
156
|
-
list_complete: boolean;
|
|
157
|
-
cursor?: string;
|
|
158
|
-
}>;
|
|
159
|
-
}
|
|
160
|
-
/** Minimal R2Bucket interface — matches Cloudflare's R2Bucket */
|
|
161
|
-
interface R2BucketBinding {
|
|
162
|
-
get(key: string, options?: unknown): Promise<R2ObjectBody | null>;
|
|
163
|
-
put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: unknown): Promise<R2Object | null>;
|
|
164
|
-
delete(keys: string | string[]): Promise<void>;
|
|
165
|
-
list(options?: unknown): Promise<R2Objects>;
|
|
166
|
-
head(key: string): Promise<R2Object | null>;
|
|
167
|
-
}
|
|
168
|
-
interface R2Object {
|
|
169
|
-
key: string;
|
|
170
|
-
size: number;
|
|
171
|
-
etag: string;
|
|
172
|
-
httpEtag: string;
|
|
173
|
-
uploaded: Date;
|
|
174
|
-
}
|
|
175
|
-
interface R2ObjectBody extends R2Object {
|
|
176
|
-
body: ReadableStream;
|
|
177
|
-
bodyUsed: boolean;
|
|
178
|
-
arrayBuffer(): Promise<ArrayBuffer>;
|
|
179
|
-
text(): Promise<string>;
|
|
180
|
-
json<T>(): Promise<T>;
|
|
181
|
-
blob(): Promise<Blob>;
|
|
182
|
-
}
|
|
183
|
-
interface R2Objects {
|
|
184
|
-
objects: R2Object[];
|
|
185
|
-
truncated: boolean;
|
|
186
|
-
cursor?: string;
|
|
187
|
-
delimitedPrefixes: string[];
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
declare class WorkersTransport {
|
|
191
|
-
private buffer;
|
|
192
|
-
private maxQueueSize;
|
|
193
|
-
private url;
|
|
194
|
-
private authToken;
|
|
195
|
-
private appName;
|
|
196
|
-
private projectId?;
|
|
197
|
-
private sessionRegistered;
|
|
198
|
-
private _droppedCount;
|
|
199
|
-
readonly sessionId: string;
|
|
200
|
-
constructor(config: WorkersConfig);
|
|
201
|
-
get droppedCount(): number;
|
|
202
|
-
queue(event: WorkersRuntimeEvent): void;
|
|
203
|
-
/**
|
|
204
|
-
* Flush all buffered events to the collector.
|
|
205
|
-
* Call via ctx.waitUntil(transport.flush()) at the end of each request.
|
|
206
|
-
* Retries once on failure with a short delay.
|
|
207
|
-
*/
|
|
208
|
-
flush(): Promise<void>;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/** Internal context exposed to binding wrappers */
|
|
212
|
-
interface RuntimeScopeContext {
|
|
213
|
-
transport: WorkersTransport;
|
|
214
|
-
sessionId: string;
|
|
215
|
-
config: WorkersConfig;
|
|
216
|
-
emit: (event: WorkersRuntimeEvent) => void;
|
|
217
|
-
}
|
|
218
|
-
/** Get the active RuntimeScope context (used by binding wrappers) */
|
|
219
|
-
declare function getActiveContext(): RuntimeScopeContext | null;
|
|
220
|
-
interface WorkersFetchHandler {
|
|
221
|
-
fetch(request: Request, env: unknown, ctx: ExecutionContext): Response | Promise<Response>;
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Wrap a Workers fetch handler to capture request/response metrics,
|
|
225
|
-
* errors, and console output. Events are flushed via ctx.waitUntil().
|
|
226
|
-
*
|
|
227
|
-
* @example
|
|
228
|
-
* ```ts
|
|
229
|
-
* import { withRuntimeScope } from '@runtimescope/workers-sdk';
|
|
230
|
-
*
|
|
231
|
-
* export default withRuntimeScope({
|
|
232
|
-
* async fetch(request, env, ctx) {
|
|
233
|
-
* return new Response('Hello!');
|
|
234
|
-
* },
|
|
235
|
-
* }, { appName: 'my-worker' });
|
|
236
|
-
* ```
|
|
237
|
-
*/
|
|
238
|
-
declare function withRuntimeScope(handler: WorkersFetchHandler, config: WorkersConfig): WorkersFetchHandler;
|
|
239
|
-
|
|
240
|
-
type EmitFn$2 = (event: DatabaseEvent) => void;
|
|
241
|
-
interface D1InstrumentOptions {
|
|
242
|
-
sessionId: string;
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Wrap a D1 database binding to capture queries.
|
|
246
|
-
*
|
|
247
|
-
* @example
|
|
248
|
-
* ```ts
|
|
249
|
-
* const db = instrumentD1(env.DB, transport.sessionId);
|
|
250
|
-
* const results = await db.prepare('SELECT * FROM users').all();
|
|
251
|
-
* ```
|
|
252
|
-
*/
|
|
253
|
-
declare function instrumentD1(db: D1DatabaseBinding, emit: EmitFn$2, options: D1InstrumentOptions): D1DatabaseBinding;
|
|
254
|
-
|
|
255
|
-
type EmitFn$1 = (event: DatabaseEvent) => void;
|
|
256
|
-
interface KVInstrumentOptions {
|
|
257
|
-
sessionId: string;
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Wrap a KV namespace binding to capture operations.
|
|
261
|
-
*
|
|
262
|
-
* @example
|
|
263
|
-
* ```ts
|
|
264
|
-
* const kv = instrumentKV(env.MY_KV, emit, { sessionId });
|
|
265
|
-
* await kv.put('key', 'value');
|
|
266
|
-
* ```
|
|
267
|
-
*/
|
|
268
|
-
declare function instrumentKV(kv: KVNamespaceBinding, emit: EmitFn$1, options: KVInstrumentOptions): KVNamespaceBinding;
|
|
269
|
-
|
|
270
|
-
type EmitFn = (event: DatabaseEvent) => void;
|
|
271
|
-
interface R2InstrumentOptions {
|
|
272
|
-
sessionId: string;
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Wrap an R2 bucket binding to capture operations.
|
|
276
|
-
*
|
|
277
|
-
* @example
|
|
278
|
-
* ```ts
|
|
279
|
-
* const bucket = instrumentR2(env.MY_BUCKET, emit, { sessionId });
|
|
280
|
-
* await bucket.put('file.txt', 'contents');
|
|
281
|
-
* ```
|
|
282
|
-
*/
|
|
283
|
-
declare function instrumentR2(bucket: R2BucketBinding, emit: EmitFn, options: R2InstrumentOptions): R2BucketBinding;
|
|
284
|
-
|
|
285
|
-
interface SamplerConfig {
|
|
286
|
-
/** Probabilistic sample rate: 0.0–1.0 (default: 1.0 = keep all) */
|
|
287
|
-
sampleRate?: number;
|
|
288
|
-
}
|
|
289
|
-
declare class Sampler {
|
|
290
|
-
private config;
|
|
291
|
-
private _droppedCount;
|
|
292
|
-
constructor(config: SamplerConfig);
|
|
293
|
-
get droppedCount(): number;
|
|
294
|
-
shouldSample(_event: WorkersRuntimeEvent): boolean;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/** Generate a random event ID using Web Crypto API (available in Workers) */
|
|
298
|
-
declare function generateId(): string;
|
|
299
|
-
/** Generate a session ID with worker prefix */
|
|
300
|
-
declare function generateSessionId(): string;
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Instrument a D1 database using the active request context.
|
|
304
|
-
* Must be called inside a withRuntimeScope handler.
|
|
305
|
-
*
|
|
306
|
-
* @example
|
|
307
|
-
* ```ts
|
|
308
|
-
* import { withRuntimeScope, scopeD1 } from '@runtimescope/workers-sdk';
|
|
309
|
-
*
|
|
310
|
-
* export default withRuntimeScope({
|
|
311
|
-
* async fetch(request, env, ctx) {
|
|
312
|
-
* const db = scopeD1(env.DB);
|
|
313
|
-
* const users = await db.prepare('SELECT * FROM users').all();
|
|
314
|
-
* return Response.json(users);
|
|
315
|
-
* },
|
|
316
|
-
* }, { appName: 'my-worker' });
|
|
317
|
-
* ```
|
|
318
|
-
*/
|
|
319
|
-
declare function scopeD1(db: D1DatabaseBinding): D1DatabaseBinding;
|
|
320
|
-
/** Instrument a KV namespace using the active request context. */
|
|
321
|
-
declare function scopeKV(kv: KVNamespaceBinding): KVNamespaceBinding;
|
|
322
|
-
/** Instrument an R2 bucket using the active request context. */
|
|
323
|
-
declare function scopeR2(bucket: R2BucketBinding): R2BucketBinding;
|
|
324
|
-
/**
|
|
325
|
-
* Track a custom business event (e.g., user signup, payment processed).
|
|
326
|
-
* Must be called inside a withRuntimeScope handler.
|
|
327
|
-
*
|
|
328
|
-
* @example
|
|
329
|
-
* ```ts
|
|
330
|
-
* import { withRuntimeScope, track } from '@runtimescope/workers-sdk';
|
|
331
|
-
*
|
|
332
|
-
* export default withRuntimeScope({
|
|
333
|
-
* async fetch(request, env, ctx) {
|
|
334
|
-
* track('payment.processed', { amount: 99.99, currency: 'USD' });
|
|
335
|
-
* return new Response('OK');
|
|
336
|
-
* },
|
|
337
|
-
* }, { appName: 'payments-worker' });
|
|
338
|
-
* ```
|
|
339
|
-
*/
|
|
340
|
-
declare function track(name: string, properties?: Record<string, unknown>): void;
|
|
341
|
-
/**
|
|
342
|
-
* Add a breadcrumb to the current request's event trail.
|
|
343
|
-
* Useful for marking key points in request processing.
|
|
344
|
-
*
|
|
345
|
-
* @example
|
|
346
|
-
* ```ts
|
|
347
|
-
* import { withRuntimeScope, addBreadcrumb } from '@runtimescope/workers-sdk';
|
|
348
|
-
*
|
|
349
|
-
* export default withRuntimeScope({
|
|
350
|
-
* async fetch(request, env, ctx) {
|
|
351
|
-
* addBreadcrumb('auth check passed', { userId: '123' });
|
|
352
|
-
* // ... process request
|
|
353
|
-
* addBreadcrumb('cache miss, fetching from origin');
|
|
354
|
-
* return new Response('OK');
|
|
355
|
-
* },
|
|
356
|
-
* }, { appName: 'api-worker' });
|
|
357
|
-
* ```
|
|
358
|
-
*/
|
|
359
|
-
declare function addBreadcrumb(message: string, data?: Record<string, unknown>): void;
|
|
360
|
-
|
|
361
|
-
export { type ConsoleEvent, type ConsoleLevel, type CustomEvent, type D1DatabaseBinding, type D1PreparedStatementBinding, type D1Result, type DatabaseEvent, type DatabaseOperation, type DatabaseSource, type KVNamespaceBinding, type NetworkEvent, type R2BucketBinding, type R2Object, type R2ObjectBody, type R2Objects, type RuntimeScopeContext, Sampler, type UIInteractionEvent, type UserContext, type WorkersConfig, type WorkersFetchHandler, type WorkersRuntimeEvent, WorkersTransport, addBreadcrumb, generateId, generateSessionId, getActiveContext, instrumentD1, instrumentKV, instrumentR2, scopeD1, scopeKV, scopeR2, track, withRuntimeScope };
|