autotel-cloudflare 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/agents.d.ts +626 -123
  2. package/dist/agents.d.ts.map +1 -1
  3. package/dist/agents.js +260 -171
  4. package/dist/agents.js.map +1 -1
  5. package/package.json +8 -4
  6. package/src/actors/alarms.ts +0 -225
  7. package/src/actors/index.ts +0 -36
  8. package/src/actors/instrument-actor.test.ts +0 -179
  9. package/src/actors/instrument-actor.ts +0 -574
  10. package/src/actors/sockets.ts +0 -217
  11. package/src/actors/storage.ts +0 -263
  12. package/src/actors/traced-handler.ts +0 -300
  13. package/src/actors/types.ts +0 -98
  14. package/src/actors.ts +0 -50
  15. package/src/agents/index.ts +0 -42
  16. package/src/agents/otel-observability.test.ts +0 -336
  17. package/src/agents/otel-observability.ts +0 -474
  18. package/src/agents/types.ts +0 -167
  19. package/src/agents.ts +0 -76
  20. package/src/bindings/ai.test.ts +0 -170
  21. package/src/bindings/ai.ts +0 -73
  22. package/src/bindings/analytics-engine.test.ts +0 -175
  23. package/src/bindings/analytics-engine.ts +0 -78
  24. package/src/bindings/bindings-cache.test.ts +0 -80
  25. package/src/bindings/bindings-detection.test.ts +0 -235
  26. package/src/bindings/bindings-this-binding.test.ts +0 -294
  27. package/src/bindings/bindings.ts +0 -720
  28. package/src/bindings/browser-rendering.test.ts +0 -160
  29. package/src/bindings/browser-rendering.ts +0 -70
  30. package/src/bindings/common.ts +0 -84
  31. package/src/bindings/hyperdrive.test.ts +0 -176
  32. package/src/bindings/hyperdrive.ts +0 -74
  33. package/src/bindings/images.test.ts +0 -262
  34. package/src/bindings/images.ts +0 -182
  35. package/src/bindings/index.ts +0 -20
  36. package/src/bindings/queue-producer.test.ts +0 -224
  37. package/src/bindings/queue-producer.ts +0 -105
  38. package/src/bindings/rate-limiter.test.ts +0 -140
  39. package/src/bindings/rate-limiter.ts +0 -69
  40. package/src/bindings/vectorize.test.ts +0 -362
  41. package/src/bindings/vectorize.ts +0 -86
  42. package/src/bindings.ts +0 -6
  43. package/src/events.ts +0 -6
  44. package/src/execution-logger.test.ts +0 -127
  45. package/src/execution-logger.ts +0 -112
  46. package/src/global/cache.test.ts +0 -292
  47. package/src/global/cache.ts +0 -164
  48. package/src/global/fetch.test.ts +0 -399
  49. package/src/global/fetch.ts +0 -136
  50. package/src/global/index.ts +0 -7
  51. package/src/handlers/durable-objects.test.ts +0 -524
  52. package/src/handlers/durable-objects.ts +0 -250
  53. package/src/handlers/index.ts +0 -6
  54. package/src/handlers/workflows.test.ts +0 -411
  55. package/src/handlers/workflows.ts +0 -345
  56. package/src/handlers.ts +0 -6
  57. package/src/index.ts +0 -91
  58. package/src/logger.ts +0 -15
  59. package/src/native/native-tracing.test.ts +0 -54
  60. package/src/native/native-tracing.ts +0 -83
  61. package/src/native.ts +0 -12
  62. package/src/parse-error.ts +0 -1
  63. package/src/sampling.ts +0 -6
  64. package/src/testing.ts +0 -6
  65. package/src/wrappers/cf-attributes.test.ts +0 -334
  66. package/src/wrappers/define-worker-fetch.test.ts +0 -102
  67. package/src/wrappers/define-worker-fetch.ts +0 -71
  68. package/src/wrappers/index.ts +0 -13
  69. package/src/wrappers/instrument.integration.test.ts +0 -578
  70. package/src/wrappers/instrument.ts +0 -806
  71. package/src/wrappers/native-instrument.test.ts +0 -153
  72. package/src/wrappers/wrap-do.ts +0 -34
  73. package/src/wrappers/wrap-module.ts +0 -37
@@ -1 +1 @@
1
- {"version":3,"file":"agents.js","names":[],"sources":["../src/agents/otel-observability.ts"],"sourcesContent":["/**\n * OpenTelemetry-based Observability implementation for Cloudflare Agents SDK\n *\n * Converts Agent events into OpenTelemetry spans for distributed tracing.\n *\n * @example\n * ```typescript\n * import { Agent } from 'agents'\n * import { createOtelObservability } from 'autotel-cloudflare/agents'\n *\n * class MyAgent extends Agent<Env> {\n * observability = createOtelObservability({\n * service: { name: 'my-agent' },\n * exporter: { url: env.OTLP_ENDPOINT }\n * })\n *\n * @callable()\n * async doSomething() {\n * // This RPC call will be automatically traced\n * return 'done'\n * }\n * }\n * ```\n */\n\nimport {\n trace,\n SpanStatusCode,\n SpanKind,\n type Span,\n type Attributes,\n} from '@opentelemetry/api';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport {\n createInitialiser,\n WorkerTracerProvider,\n WorkerTracer,\n type ResolvedEdgeConfig,\n} from 'autotel-edge';\nimport type {\n Observability,\n ObservabilityEvent,\n OtelObservabilityConfig,\n AgentInstrumentationOptions,\n} from './types';\n\n/**\n * Map of active spans keyed by event ID\n * Used to correlate start/end events\n */\nconst activeSpans = new Map<string, Span>();\n\n/**\n * Whether the provider has been initialized\n */\nlet providerInitialized = false;\n\n/**\n * Initialize the tracer provider for Agents\n */\nfunction initProvider(config: ResolvedEdgeConfig): void {\n if (providerInitialized) return;\n\n // Create resource with agent-specific attributes\n const resource = resourceFromAttributes({\n 'service.name': config.service.name,\n 'service.version': config.service.version,\n 'service.namespace': config.service.namespace,\n 'cloud.provider': 'cloudflare',\n 'cloud.platform': 'cloudflare.workers',\n 'telemetry.sdk.name': 'autotel-cloudflare',\n 'telemetry.sdk.language': 'js',\n 'agent.framework': 'cloudflare-agents',\n });\n\n // Create and register provider\n const provider = new WorkerTracerProvider(config.spanProcessors, resource);\n provider.register();\n\n // Set head sampler on tracer\n const tracer = trace.getTracer('autotel-cloudflare/agents') as WorkerTracer;\n tracer.setHeadSampler(config.sampling.headSampler);\n\n providerInitialized = true;\n}\n\n/**\n * Get default span name for an event\n */\nfunction getDefaultSpanName(event: ObservabilityEvent): string {\n switch (event.type) {\n case 'rpc': {\n return `agent.rpc ${event.payload.method}`;\n }\n case 'schedule:create': {\n return `agent.schedule.create ${event.payload.callback}`;\n }\n case 'schedule:execute': {\n return `agent.schedule.execute ${event.payload.callback}`;\n }\n case 'schedule:cancel': {\n return `agent.schedule.cancel ${event.payload.callback}`;\n }\n case 'connect': {\n return `agent.connect`;\n }\n case 'destroy': {\n return `agent.destroy`;\n }\n case 'state:update': {\n return `agent.state.update`;\n }\n case 'message:request': {\n return `agent.message.request`;\n }\n case 'message:response': {\n return `agent.message.response`;\n }\n case 'message:clear': {\n return `agent.message.clear`;\n }\n case 'mcp:client:preconnect': {\n return `mcp.preconnect ${event.payload.serverId}`;\n }\n case 'mcp:client:connect': {\n return `mcp.connect ${event.payload.url}`;\n }\n case 'mcp:client:authorize': {\n return `mcp.authorize ${event.payload.serverId}`;\n }\n case 'mcp:client:discover': {\n return `mcp.discover`;\n }\n default: {\n return `agent.${(event as ObservabilityEvent).type}`;\n }\n }\n}\n\n/**\n * Get default attributes for an event\n */\nfunction getDefaultAttributes(event: ObservabilityEvent): Attributes {\n const attrs: Attributes = {\n 'agent.event.type': event.type,\n 'agent.event.id': event.id,\n };\n\n // Add type-specific attributes\n switch (event.type) {\n case 'rpc': {\n attrs['agent.rpc.method'] = event.payload.method;\n if (event.payload.streaming !== undefined) {\n attrs['agent.rpc.streaming'] = event.payload.streaming;\n }\n break;\n }\n\n case 'schedule:create':\n case 'schedule:execute':\n case 'schedule:cancel': {\n attrs['agent.schedule.callback'] = event.payload.callback;\n attrs['agent.schedule.id'] = event.payload.id;\n break;\n }\n\n case 'connect': {\n attrs['agent.connection.id'] = event.payload.connectionId;\n break;\n }\n\n case 'mcp:client:preconnect': {\n attrs['agent.mcp.server_id'] = event.payload.serverId;\n break;\n }\n\n case 'mcp:client:connect': {\n attrs['agent.mcp.url'] = event.payload.url;\n attrs['agent.mcp.transport'] = event.payload.transport;\n attrs['agent.mcp.state'] = event.payload.state;\n if (event.payload.error) {\n attrs['agent.mcp.error'] = event.payload.error;\n }\n break;\n }\n\n case 'mcp:client:authorize': {\n attrs['agent.mcp.server_id'] = event.payload.serverId;\n attrs['agent.mcp.auth_url'] = event.payload.authUrl;\n if (event.payload.clientId) {\n attrs['agent.mcp.client_id'] = event.payload.clientId;\n }\n break;\n }\n }\n\n // Add any additional payload properties as attributes\n for (const [key, value] of Object.entries(event.payload)) {\n if (\n attrs[`agent.${key}`] === undefined &&\n (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean')\n ) {\n attrs[`agent.payload.${key}`] = value;\n }\n }\n\n return attrs;\n}\n\n/**\n * Determine span kind based on event type\n */\nfunction getSpanKind(event: ObservabilityEvent): SpanKind {\n switch (event.type) {\n case 'rpc': {\n return SpanKind.SERVER;\n }\n case 'connect': {\n return SpanKind.SERVER;\n }\n case 'mcp:client:connect':\n case 'mcp:client:preconnect':\n case 'mcp:client:authorize':\n case 'mcp:client:discover': {\n return SpanKind.CLIENT;\n }\n default: {\n return SpanKind.INTERNAL;\n }\n }\n}\n\n/**\n * Check if an event type should be traced based on options\n */\nfunction shouldTraceEvent(\n event: ObservabilityEvent,\n options: AgentInstrumentationOptions,\n): boolean {\n const defaults: AgentInstrumentationOptions = {\n traceRpc: true,\n traceSchedule: true,\n traceMcp: true,\n traceStateUpdates: false,\n traceMessages: true,\n traceLifecycle: true,\n };\n\n const opts = { ...defaults, ...options };\n\n switch (event.type) {\n case 'rpc': {\n return opts.traceRpc ?? true;\n }\n\n case 'schedule:create':\n case 'schedule:execute':\n case 'schedule:cancel': {\n return opts.traceSchedule ?? true;\n }\n\n case 'mcp:client:preconnect':\n case 'mcp:client:connect':\n case 'mcp:client:authorize':\n case 'mcp:client:discover': {\n return opts.traceMcp ?? true;\n }\n\n case 'state:update': {\n return opts.traceStateUpdates ?? false;\n }\n\n case 'message:request':\n case 'message:response':\n case 'message:clear': {\n return opts.traceMessages ?? true;\n }\n\n case 'connect':\n case 'destroy': {\n return opts.traceLifecycle ?? true;\n }\n\n default: {\n return true;\n }\n }\n}\n\n/**\n * Export spans asynchronously\n */\nasync function exportSpans(\n traceId: string,\n ctx?: DurableObjectState | ExecutionContext,\n): Promise<void> {\n const tracer = trace.getTracer('autotel-cloudflare/agents');\n if (tracer instanceof WorkerTracer) {\n try {\n // scheduler is only available on ExecutionContext, not DurableObjectState\n if (ctx && 'scheduler' in ctx) {\n const ctxWithScheduler = ctx as ExecutionContext & { scheduler?: { wait(ms: number): Promise<void> } };\n if (ctxWithScheduler.scheduler) {\n await ctxWithScheduler.scheduler.wait(1);\n }\n }\n await tracer.forceFlush(traceId);\n } catch (error) {\n console.error('[autotel-cloudflare/agents] Failed to export spans:', error);\n }\n }\n\n // If we have a DurableObject context, use waitUntil for export\n if (ctx && 'waitUntil' in ctx) {\n // Already exported above, but could defer more work here\n }\n}\n\n/**\n * OpenTelemetry-based Observability implementation\n *\n * Implements the Agents SDK Observability interface and converts\n * events into OpenTelemetry spans.\n */\nexport class OtelObservability implements Observability {\n private config: ResolvedEdgeConfig;\n private options: AgentInstrumentationOptions;\n private initialized = false;\n\n constructor(config: OtelObservabilityConfig) {\n // Use createInitialiser to resolve the config\n const initialiser = createInitialiser(config);\n this.config = initialiser({}, undefined);\n this.options = config.agents ?? {};\n }\n\n /**\n * Initialize the tracer provider (called lazily on first emit)\n */\n private initialize(): void {\n if (this.initialized) return;\n initProvider(this.config);\n this.initialized = true;\n }\n\n /**\n * Emit an observability event\n *\n * Converts the event to an OpenTelemetry span based on the event type.\n */\n emit(event: ObservabilityEvent, ctx?: DurableObjectState): void {\n // Initialize provider on first emit\n this.initialize();\n\n // Check if this event type should be traced\n if (!shouldTraceEvent(event, this.options)) {\n return;\n }\n\n const tracer = trace.getTracer('autotel-cloudflare/agents');\n\n // Get span name (custom or default)\n const spanName = this.options.spanNameFormatter\n ? this.options.spanNameFormatter(event)\n : getDefaultSpanName(event);\n\n // Get attributes (custom + default)\n const defaultAttrs = getDefaultAttributes(event);\n const customAttrs = this.options.attributeExtractor\n ? this.options.attributeExtractor(event)\n : {};\n const attributes = { ...defaultAttrs, ...customAttrs };\n\n // Determine span kind\n const kind = getSpanKind(event);\n\n // Create span with event timestamp\n const span = tracer.startSpan(spanName, {\n kind,\n attributes,\n startTime: event.timestamp,\n });\n\n // For short-lived events, end immediately\n // For events that have duration (like RPC), we would ideally track start/end\n // But the Agents SDK emits single events, so we create point-in-time spans\n span.setStatus({ code: SpanStatusCode.OK });\n span.end(event.timestamp + 1); // End 1ms after start\n\n // Store span for potential correlation\n activeSpans.set(event.id, span);\n\n // Schedule span export\n const traceId = span.spanContext().traceId;\n if (ctx && 'waitUntil' in ctx && typeof (ctx as any).waitUntil === 'function') {\n (ctx as any).waitUntil(exportSpans(traceId, ctx));\n } else {\n // In environments without waitUntil, export synchronously-ish\n void exportSpans(traceId, ctx);\n }\n }\n}\n\n/**\n * Create an OtelObservability instance\n *\n * @example\n * ```typescript\n * import { Agent } from 'agents'\n * import { createOtelObservability } from 'autotel-cloudflare/agents'\n *\n * class MyAgent extends Agent<Env> {\n * observability = createOtelObservability({\n * service: { name: 'my-agent' },\n * exporter: { url: env.OTLP_ENDPOINT }\n * })\n * }\n * ```\n */\nexport function createOtelObservability(config: OtelObservabilityConfig): OtelObservability {\n return new OtelObservability(config);\n}\n\n/**\n * Create an OtelObservability instance with environment-based config\n *\n * Use this when you need to access environment variables for configuration.\n *\n * @example\n * ```typescript\n * import { Agent } from 'agents'\n * import { createOtelObservabilityFromEnv } from 'autotel-cloudflare/agents'\n *\n * class MyAgent extends Agent<Env> {\n * observability?: OtelObservability\n *\n * constructor(state: DurableObjectState, env: Env) {\n * super(state, env)\n * this.observability = createOtelObservabilityFromEnv(env)\n * }\n * }\n * ```\n */\nexport function createOtelObservabilityFromEnv(\n env: Record<string, unknown>,\n options?: AgentInstrumentationOptions,\n): OtelObservability {\n // Extract standard OTLP env vars\n const endpoint = (env.OTEL_EXPORTER_OTLP_ENDPOINT as string) || undefined;\n const serviceName = (env.OTEL_SERVICE_NAME as string) || 'cloudflare-agent';\n\n // Parse headers if present\n let headers: Record<string, string> | undefined;\n const headersStr = env.OTEL_EXPORTER_OTLP_HEADERS as string;\n if (headersStr) {\n headers = {};\n for (const pair of headersStr.split(',')) {\n const [key, value] = pair.split('=');\n if (key && value) {\n headers[key.trim()] = value.trim();\n }\n }\n }\n\n // If no endpoint is configured, use a default localhost endpoint\n // In production, users should set OTEL_EXPORTER_OTLP_ENDPOINT\n const exporterUrl = endpoint || 'http://localhost:4318/v1/traces';\n\n return createOtelObservability({\n service: { name: serviceName },\n exporter: { url: exporterUrl, headers },\n agents: options,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agents.js","names":[],"sources":["../src/agents/otel-observability.ts","../src/agents/observability.ts"],"sourcesContent":["/**\n * OpenTelemetry-based Observability implementation for Cloudflare Agents SDK\n *\n * Converts Agent events into OpenTelemetry spans for distributed tracing.\n */\n\nimport {\n SpanKind,\n SpanStatusCode,\n trace,\n type Attributes,\n type Span,\n} from '@opentelemetry/api';\nimport {\n agentContextFromSpan,\n recordHumanApproval,\n} from 'autotel-genai/agent';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport {\n WorkerTracer,\n WorkerTracerProvider,\n createInitialiser,\n type ResolvedEdgeConfig,\n} from 'autotel-edge';\nimport type {\n AgentInstrumentationOptions,\n MCPObservabilityEvent,\n Observability,\n ObservabilityEvent,\n ObservabilityExecutionContext,\n OtelObservabilityConfig,\n} from './types';\n\nlet providerInitialized = false;\n\ntype EventCategory =\n | 'state'\n | 'rpc'\n | 'schedule'\n | 'queue'\n | 'submission'\n | 'message'\n | 'chat'\n | 'transcript'\n | 'fiber'\n | 'agentTool'\n | 'lifecycle'\n | 'workflow'\n | 'mcp'\n | 'email';\n\nfunction initProvider(config: ResolvedEdgeConfig): void {\n if (providerInitialized) return;\n\n const resource = resourceFromAttributes({\n 'service.name': config.service.name,\n 'service.version': config.service.version,\n 'service.namespace': config.service.namespace,\n 'cloud.provider': 'cloudflare',\n 'cloud.platform': 'cloudflare.workers',\n 'telemetry.sdk.name': 'autotel-cloudflare',\n 'telemetry.sdk.language': 'js',\n 'agent.framework': 'cloudflare-agents',\n });\n\n const provider = new WorkerTracerProvider(config.spanProcessors, resource);\n provider.register();\n\n const tracer = trace.getTracer('autotel-cloudflare/agents') as WorkerTracer;\n tracer.setHeadSampler(config.sampling.headSampler);\n\n providerInitialized = true;\n}\n\nfunction classifyEvent(type: ObservabilityEvent['type']): EventCategory {\n if (type.startsWith('mcp:')) return 'mcp';\n if (type.startsWith('workflow:')) return 'workflow';\n if (type.startsWith('fiber:')) return 'fiber';\n if (type.startsWith('agent_tool:')) return 'agentTool';\n if (type.startsWith('chat:transcript:')) return 'transcript';\n if (type.startsWith('chat:')) return 'chat';\n if (type.startsWith('submission:')) return 'submission';\n if (type.startsWith('queue:')) return 'queue';\n if (type.startsWith('schedule:')) return 'schedule';\n if (\n type.startsWith('message:') ||\n type.startsWith('tool:') ||\n type === 'rpc:error'\n ) {\n return 'message';\n }\n if (type === 'rpc') return 'rpc';\n if (type.startsWith('state:')) return 'state';\n if (type.startsWith('email:')) return 'email';\n if (type === 'connect' || type === 'disconnect' || type === 'destroy') {\n return 'lifecycle';\n }\n return 'message';\n}\n\nfunction shouldTraceEvent(\n event: ObservabilityEvent,\n options: AgentInstrumentationOptions,\n): boolean {\n const opts: Required<\n Pick<\n AgentInstrumentationOptions,\n | 'traceRpc'\n | 'traceSchedule'\n | 'traceQueue'\n | 'traceSubmissions'\n | 'traceMcp'\n | 'traceStateUpdates'\n | 'traceMessages'\n | 'traceLifecycle'\n | 'traceChat'\n | 'traceTranscripts'\n | 'traceFibers'\n | 'traceToolRecovery'\n | 'traceWorkflow'\n | 'traceEmail'\n >\n > = {\n traceRpc: true,\n traceSchedule: true,\n traceQueue: true,\n traceSubmissions: true,\n traceMcp: true,\n traceStateUpdates: false,\n traceMessages: true,\n traceLifecycle: true,\n traceChat: true,\n traceTranscripts: true,\n traceFibers: true,\n traceToolRecovery: true,\n traceWorkflow: true,\n traceEmail: true,\n };\n const merged = { ...opts, ...options };\n\n switch (classifyEvent(event.type)) {\n case 'rpc': {\n return merged.traceRpc;\n }\n case 'schedule': {\n return merged.traceSchedule;\n }\n case 'queue': {\n return merged.traceQueue;\n }\n case 'submission': {\n return merged.traceSubmissions;\n }\n case 'mcp': {\n return merged.traceMcp;\n }\n case 'state': {\n return merged.traceStateUpdates;\n }\n case 'message': {\n return merged.traceMessages;\n }\n case 'lifecycle': {\n return merged.traceLifecycle;\n }\n case 'chat': {\n return merged.traceChat;\n }\n case 'transcript': {\n return merged.traceTranscripts;\n }\n case 'fiber': {\n return merged.traceFibers;\n }\n case 'agentTool': {\n return merged.traceToolRecovery;\n }\n case 'workflow': {\n return merged.traceWorkflow;\n }\n case 'email': {\n return merged.traceEmail;\n }\n }\n}\n\nfunction formatTypeSegment(value: string): string {\n return value.replaceAll(/[:_]/g, '.');\n}\n\nfunction getPayloadName(\n payload: Record<string, unknown>,\n ...keys: string[]\n): string | undefined {\n for (const key of keys) {\n const value = payload[key];\n if (typeof value === 'string' && value.length > 0) {\n return value;\n }\n }\n return undefined;\n}\n\nfunction getDefaultSpanName(event: ObservabilityEvent): string {\n const payload = event.payload;\n\n switch (event.type) {\n case 'rpc':\n case 'rpc:error': {\n return `agent.rpc ${(payload as { method: string }).method}`;\n }\n case 'schedule:create':\n case 'schedule:execute':\n case 'schedule:cancel':\n case 'schedule:retry':\n case 'schedule:error':\n case 'schedule:duplicate_warning': {\n return `agent.${formatTypeSegment(event.type)} ${getPayloadName(payload, 'callback') ?? 'unknown'}`;\n }\n case 'queue:create':\n case 'queue:retry':\n case 'queue:error': {\n return `agent.${formatTypeSegment(event.type)} ${getPayloadName(payload, 'callback') ?? 'unknown'}`;\n }\n case 'submission:create':\n case 'submission:status':\n case 'submission:error': {\n return `agent.${formatTypeSegment(event.type)} ${getPayloadName(payload, 'submissionId') ?? 'unknown'}`;\n }\n case 'connect':\n case 'disconnect': {\n return `agent.${event.type} ${getPayloadName(payload, 'connectionId') ?? 'unknown'}`;\n }\n case 'mcp:client:preconnect': {\n return `mcp.client.preconnect ${getPayloadName(payload, 'serverId') ?? 'unknown'}`;\n }\n case 'mcp:client:authorize': {\n return `mcp.client.authorize ${getPayloadName(payload, 'serverId') ?? 'unknown'}`;\n }\n case 'mcp:client:connect':\n case 'mcp:client:discover':\n case 'mcp:client:close': {\n return `mcp.client.${formatTypeSegment(event.type).split('.').slice(-1)[0]} ${\n getPayloadName(payload, 'url', 'capability', 'state') ?? 'unknown'\n }`;\n }\n case 'workflow:start':\n case 'workflow:event':\n case 'workflow:approved':\n case 'workflow:rejected':\n case 'workflow:terminated':\n case 'workflow:paused':\n case 'workflow:resumed':\n case 'workflow:restarted': {\n return `agent.${formatTypeSegment(event.type)} ${\n getPayloadName(payload, 'workflowName', 'workflowId', 'eventType') ?? 'unknown'\n }`;\n }\n case 'email:receive':\n case 'email:reply':\n case 'email:send': {\n return `agent.${formatTypeSegment(event.type)} ${getPayloadName(payload, 'subject') ?? 'message'}`;\n }\n case 'destroy': {\n return 'agent.destroy';\n }\n default: {\n return `agent.${formatTypeSegment(event.type)}`;\n }\n }\n}\n\nfunction isPrimitiveAttributeValue(\n value: unknown,\n): value is string | number | boolean | string[] | number[] | boolean[] {\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n return true;\n }\n\n if (!Array.isArray(value)) {\n return false;\n }\n\n return value.every(\n (entry) =>\n typeof entry === 'string' ||\n typeof entry === 'number' ||\n typeof entry === 'boolean',\n );\n}\n\nfunction setIfPresent(\n attrs: Attributes,\n key: string,\n value: unknown,\n): void {\n if (value === undefined || value === null) return;\n if (isPrimitiveAttributeValue(value)) {\n attrs[key] = value;\n }\n}\n\nfunction getDefaultAttributes(event: ObservabilityEvent): Attributes {\n const attrs: Attributes = {\n 'agent.event.type': event.type,\n };\n\n setIfPresent(attrs, 'agent.event.id', event.id);\n setIfPresent(attrs, 'agent.display_message', event.displayMessage);\n setIfPresent(attrs, 'agent.class', event.agent);\n setIfPresent(attrs, 'agent.instance.name', event.name);\n setIfPresent(attrs, 'gen_ai.agent.name', event.agent);\n\n switch (event.type) {\n case 'rpc': {\n attrs['agent.rpc.method'] = event.payload.method;\n setIfPresent(attrs, 'agent.rpc.streaming', event.payload.streaming);\n break;\n }\n case 'rpc:error': {\n attrs['agent.rpc.method'] = event.payload.method;\n break;\n }\n case 'schedule:create':\n case 'schedule:execute':\n case 'schedule:cancel':\n case 'schedule:retry':\n case 'schedule:error':\n case 'queue:create':\n case 'queue:retry':\n case 'queue:error': {\n setIfPresent(attrs, 'agent.schedule.callback', event.payload.callback);\n setIfPresent(attrs, 'agent.schedule.id', event.payload.id);\n break;\n }\n case 'schedule:duplicate_warning': {\n setIfPresent(attrs, 'agent.schedule.callback', event.payload.callback);\n break;\n }\n case 'connect':\n case 'disconnect': {\n setIfPresent(attrs, 'agent.connection.id', event.payload.connectionId);\n break;\n }\n case 'mcp:client:preconnect': {\n setIfPresent(attrs, 'agent.mcp.server_id', event.payload.serverId);\n break;\n }\n case 'mcp:client:connect':\n case 'mcp:client:close': {\n setIfPresent(attrs, 'agent.mcp.url', event.payload.url);\n setIfPresent(attrs, 'agent.mcp.transport', event.payload.transport);\n setIfPresent(attrs, 'agent.mcp.state', event.payload.state);\n break;\n }\n case 'mcp:client:authorize': {\n setIfPresent(attrs, 'agent.mcp.server_id', event.payload.serverId);\n setIfPresent(attrs, 'agent.mcp.auth_url', event.payload.authUrl);\n setIfPresent(attrs, 'agent.mcp.client_id', event.payload.clientId);\n break;\n }\n case 'mcp:client:discover': {\n setIfPresent(attrs, 'agent.mcp.url', event.payload.url);\n setIfPresent(attrs, 'agent.mcp.state', event.payload.state);\n setIfPresent(attrs, 'agent.mcp.capability', event.payload.capability);\n break;\n }\n }\n\n for (const [key, value] of Object.entries(event.payload)) {\n setIfPresent(attrs, `agent.payload.${key}`, value);\n }\n\n return attrs;\n}\n\nfunction getSpanKind(event: ObservabilityEvent): SpanKind {\n switch (classifyEvent(event.type)) {\n case 'rpc':\n case 'lifecycle':\n case 'email': {\n return SpanKind.SERVER;\n }\n case 'mcp': {\n return SpanKind.CLIENT;\n }\n case 'schedule':\n case 'queue': {\n return SpanKind.CONSUMER;\n }\n default: {\n return SpanKind.INTERNAL;\n }\n }\n}\n\nfunction inferErrorMessage(event: ObservabilityEvent): string | undefined {\n const payload = event.payload as Record<string, unknown>;\n const direct = payload.error;\n if (typeof direct === 'string' && direct.length > 0) {\n return direct;\n }\n\n if (event.type === 'workflow:rejected') {\n const reason = payload.reason;\n if (typeof reason === 'string' && reason.length > 0) {\n return reason;\n }\n return 'workflow rejected';\n }\n\n if (event.type.endsWith(':failed') || event.type.endsWith(':error')) {\n return event.type;\n }\n\n return undefined;\n}\n\nfunction isErrorEvent(event: ObservabilityEvent): boolean {\n if (event.type === 'workflow:rejected') return true;\n if (event.type.endsWith(':error') || event.type.endsWith(':failed')) return true;\n\n if ('error' in event.payload) {\n return typeof (event.payload as Record<string, unknown>).error === 'string';\n }\n\n return false;\n}\n\nfunction applyEventStatus(span: Span, event: ObservabilityEvent): void {\n if (!isErrorEvent(event)) {\n span.setStatus({ code: SpanStatusCode.OK });\n return;\n }\n\n const message = inferErrorMessage(event);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message,\n });\n span.setAttribute('error', true);\n\n if (message) {\n span.setAttribute('exception.message', message);\n }\n}\n\nasync function exportSpans(\n traceId: string,\n ctx?: ObservabilityExecutionContext,\n): Promise<void> {\n const tracer = trace.getTracer('autotel-cloudflare/agents');\n if (tracer instanceof WorkerTracer) {\n try {\n const scheduler = ctx && 'scheduler' in ctx\n ? (ctx.scheduler as { wait?: (ms: number) => Promise<void> } | undefined)\n : undefined;\n if (scheduler?.wait) {\n await scheduler.wait(1);\n }\n await tracer.forceFlush(traceId);\n } catch (error) {\n console.error('[autotel-cloudflare/agents] Failed to export spans:', error);\n }\n }\n}\n\n/**\n * OpenTelemetry-based Observability implementation\n *\n * Implements the Agents SDK Observability interface and converts\n * events into OpenTelemetry spans.\n */\nexport class OtelObservability implements Observability {\n private readonly config: ResolvedEdgeConfig;\n private readonly options: AgentInstrumentationOptions;\n private initialized = false;\n\n constructor(config: OtelObservabilityConfig) {\n const initialiser = createInitialiser(config);\n this.config = initialiser({}, undefined);\n this.options = config.agents ?? {};\n }\n\n private initialize(): void {\n if (this.initialized) return;\n initProvider(this.config);\n this.initialized = true;\n }\n\n emit(event: ObservabilityEvent, ctx?: ObservabilityExecutionContext): void {\n this.initialize();\n\n if (!shouldTraceEvent(event, this.options)) {\n return;\n }\n\n const tracer = trace.getTracer('autotel-cloudflare/agents');\n const spanName = this.options.spanNameFormatter\n ? this.options.spanNameFormatter(event)\n : getDefaultSpanName(event);\n const defaultAttrs = getDefaultAttributes(event);\n const customAttrs = this.options.attributeExtractor\n ? this.options.attributeExtractor(event)\n : {};\n const span = tracer.startSpan(spanName, {\n kind: getSpanKind(event),\n attributes: { ...defaultAttrs, ...customAttrs },\n startTime: event.timestamp,\n });\n\n if (event.type === 'tool:approval') {\n recordHumanApproval({\n ctx: agentContextFromSpan(span),\n toolCallId: event.payload.toolCallId,\n approved: event.payload.approved,\n required: true,\n });\n }\n\n applyEventStatus(span, event);\n span.end(event.timestamp + 1);\n\n const traceId = span.spanContext().traceId;\n if (ctx && 'waitUntil' in ctx && typeof ctx.waitUntil === 'function') {\n ctx.waitUntil(exportSpans(traceId, ctx));\n return;\n }\n\n void exportSpans(traceId, ctx);\n }\n}\n\nexport function createOtelObservability(\n config: OtelObservabilityConfig,\n): OtelObservability {\n return new OtelObservability(config);\n}\n\nexport function createOtelObservabilityFromEnv(\n env: Record<string, unknown>,\n options?: AgentInstrumentationOptions,\n): OtelObservability {\n const endpoint = (env.OTEL_EXPORTER_OTLP_ENDPOINT as string) || undefined;\n const serviceName = (env.OTEL_SERVICE_NAME as string) || 'cloudflare-agent';\n\n let headers: Record<string, string> | undefined;\n const headersStr = env.OTEL_EXPORTER_OTLP_HEADERS as string;\n if (headersStr) {\n headers = {};\n for (const pair of headersStr.split(',')) {\n const [key, value] = pair.split('=');\n if (key && value) {\n headers[key.trim()] = value.trim();\n }\n }\n }\n\n return createOtelObservability({\n service: { name: serviceName },\n exporter: { url: endpoint || 'http://localhost:4318/v1/traces', headers },\n agents: options,\n });\n}\n\nexport type { MCPObservabilityEvent };\n","import type { AgentObservabilityEvent } from './agent';\nimport type { MCPObservabilityEvent } from './mcp';\n\nexport type ObservabilityEvent = AgentObservabilityEvent | MCPObservabilityEvent;\n\nexport interface Observability {\n emit(event: ObservabilityEvent, ctx?: DurableObjectState | ExecutionContext): void;\n}\n\ntype ChannelKey =\n | 'state'\n | 'rpc'\n | 'message'\n | 'chat'\n | 'transcript'\n | 'fiber'\n | 'agentTool'\n | 'schedule'\n | 'lifecycle'\n | 'workflow'\n | 'mcp'\n | 'email';\n\nexport type ChannelEventMap = {\n state: Extract<ObservabilityEvent, { type: `state:${string}` }>;\n rpc: Extract<ObservabilityEvent, { type: 'rpc' | `rpc:${string}` }>;\n message: Extract<\n ObservabilityEvent,\n { type: `message:${string}` | `tool:${string}` | `submission:${string}` }\n >;\n chat: Exclude<\n Extract<ObservabilityEvent, { type: `chat:${string}` }>,\n { type: `chat:transcript:${string}` }\n >;\n transcript: Extract<\n ObservabilityEvent,\n { type: `transcript:${string}` | `chat:transcript:${string}` }\n >;\n fiber: Extract<ObservabilityEvent, { type: `fiber:${string}` }>;\n agentTool: Extract<ObservabilityEvent, { type: `agent_tool:${string}` }>;\n schedule: Extract<\n ObservabilityEvent,\n { type: `schedule:${string}` | `queue:${string}` }\n >;\n lifecycle: Extract<\n ObservabilityEvent,\n { type: 'connect' | 'disconnect' | 'destroy' }\n >;\n workflow: Extract<ObservabilityEvent, { type: `workflow:${string}` }>;\n mcp: Extract<ObservabilityEvent, { type: `mcp:${string}` }>;\n email: Extract<ObservabilityEvent, { type: `email:${string}` }>;\n};\n\nexport interface ObservabilityChannel<TEvent extends ObservabilityEvent = ObservabilityEvent> {\n name: string;\n publish(event: TEvent): void;\n}\n\nconst listeners: {\n [K in ChannelKey]: Set<(event: ChannelEventMap[K]) => void>;\n} = {\n state: new Set(),\n rpc: new Set(),\n message: new Set(),\n chat: new Set(),\n transcript: new Set(),\n fiber: new Set(),\n agentTool: new Set(),\n schedule: new Set(),\n lifecycle: new Set(),\n workflow: new Set(),\n mcp: new Set(),\n email: new Set(),\n};\n\nfunction createChannel<K extends ChannelKey>(\n key: K,\n name: string,\n): ObservabilityChannel<ChannelEventMap[K]> {\n return {\n name,\n publish(event) {\n for (const callback of listeners[key]) {\n callback(event);\n }\n },\n };\n}\n\nexport const channels = {\n state: createChannel('state', 'agents:state'),\n rpc: createChannel('rpc', 'agents:rpc'),\n message: createChannel('message', 'agents:message'),\n chat: createChannel('chat', 'agents:chat'),\n transcript: createChannel('transcript', 'agents:transcript'),\n fiber: createChannel('fiber', 'agents:fiber'),\n agentTool: createChannel('agentTool', 'agents:agent_tool'),\n schedule: createChannel('schedule', 'agents:schedule'),\n lifecycle: createChannel('lifecycle', 'agents:lifecycle'),\n workflow: createChannel('workflow', 'agents:workflow'),\n mcp: createChannel('mcp', 'agents:mcp'),\n email: createChannel('email', 'agents:email'),\n} as const;\n\nfunction getChannel(type: string): ObservabilityChannel {\n if (type.startsWith('mcp:')) return channels.mcp;\n if (type.startsWith('workflow:')) return channels.workflow;\n if (type.startsWith('fiber:')) return channels.fiber;\n if (type.startsWith('transcript:') || type.startsWith('chat:transcript:')) {\n return channels.transcript;\n }\n if (type.startsWith('chat:')) return channels.chat;\n if (type.startsWith('agent_tool:')) return channels.agentTool;\n if (type.startsWith('schedule:') || type.startsWith('queue:')) {\n return channels.schedule;\n }\n if (\n type.startsWith('message:') ||\n type.startsWith('tool:') ||\n type.startsWith('submission:')\n ) {\n return channels.message;\n }\n if (type === 'rpc' || type.startsWith('rpc:')) return channels.rpc;\n if (type.startsWith('state:')) return channels.state;\n if (type.startsWith('email:')) return channels.email;\n return channels.lifecycle;\n}\n\nexport const genericObservability: Observability = {\n emit(event) {\n getChannel(event.type).publish(event);\n },\n};\n\nexport function subscribe<K extends keyof ChannelEventMap>(\n channelKey: K,\n callback: (event: ChannelEventMap[K]) => void,\n): () => void {\n const bucket = listeners[channelKey];\n bucket.add(callback);\n return () => {\n bucket.delete(callback);\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autotel-cloudflare",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "description": "The #1 OpenTelemetry package for Cloudflare Workers - complete bindings coverage, native CF OTel integration, advanced sampling",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -54,7 +54,6 @@
54
54
  },
55
55
  "files": [
56
56
  "dist",
57
- "src",
58
57
  "README.md",
59
58
  "skills"
60
59
  ],
@@ -81,11 +80,15 @@
81
80
  },
82
81
  "peerDependencies": {
83
82
  "@cloudflare/workers-types": "^4.20260607.1",
84
- "autotel-edge": "3.17.0"
83
+ "autotel-edge": "3.17.1",
84
+ "autotel-genai": "0.3.0"
85
85
  },
86
86
  "peerDependenciesMeta": {
87
87
  "autotel-edge": {
88
88
  "optional": false
89
+ },
90
+ "autotel-genai": {
91
+ "optional": true
89
92
  }
90
93
  },
91
94
  "devDependencies": {
@@ -97,7 +100,8 @@
97
100
  "typescript": "^6.0.3",
98
101
  "vitest": "^4.1.8",
99
102
  "vitest-mock-extended": "^4.0.0",
100
- "autotel-edge": "3.17.0"
103
+ "autotel-edge": "3.17.1",
104
+ "autotel-genai": "0.3.0"
101
105
  },
102
106
  "repository": {
103
107
  "type": "git",
@@ -1,225 +0,0 @@
1
- /**
2
- * Actor alarms instrumentation
3
- *
4
- * Traces operations on actor.alarms
5
- */
6
-
7
- import { trace, SpanStatusCode, SpanKind } from '@opentelemetry/api';
8
- import type { WorkerTracer } from 'autotel-edge';
9
- import { wrap } from '../bindings/common';
10
- import type { ActorLike } from './types';
11
-
12
- /**
13
- * Get the tracer instance
14
- */
15
- function getTracer(): WorkerTracer {
16
- return trace.getTracer('autotel-cloudflare-actors') as WorkerTracer;
17
- }
18
-
19
- /**
20
- * Instrument Actor alarms for tracing
21
- *
22
- * Captures:
23
- * - set: Schedule a single alarm
24
- * - setMultiple: Schedule multiple alarms
25
- * - cancel: Cancel an alarm
26
- * - cancelAll: Cancel all alarms
27
- */
28
- export function instrumentActorAlarms(
29
- alarms: unknown,
30
- actorInstance: ActorLike,
31
- actorClass: object,
32
- ): unknown {
33
- if (!alarms || typeof alarms !== 'object') {
34
- return alarms;
35
- }
36
-
37
- const actorClassName = (actorClass as { name?: string }).name || 'Actor';
38
- const actorName = actorInstance.name || actorClassName;
39
-
40
- const alarmsHandler: ProxyHandler<object> = {
41
- get(target, prop) {
42
- const value = Reflect.get(target, prop);
43
-
44
- // Instrument set method
45
- if (prop === 'set' && typeof value === 'function') {
46
- return async function instrumentedSet(
47
- this: unknown,
48
- ...args: unknown[]
49
- ): Promise<unknown> {
50
- const tracer = getTracer();
51
- const spanName = `Actor ${actorName}: alarms.set`;
52
-
53
- // Try to extract timing info from args
54
- const alarmAttributes: Record<string, string | number | boolean> = {
55
- 'actor.name': actorName,
56
- 'actor.class': actorClassName,
57
- 'alarm.operation': 'set',
58
- };
59
-
60
- // First arg might be timestamp, delay, or options
61
- if (args.length > 0) {
62
- const firstArg = args[0];
63
- if (typeof firstArg === 'number') {
64
- alarmAttributes['alarm.delay_ms'] = firstArg;
65
- } else if (firstArg instanceof Date) {
66
- alarmAttributes['alarm.scheduled_time'] = firstArg.toISOString();
67
- } else if (typeof firstArg === 'string') {
68
- // Might be a cron expression
69
- alarmAttributes['alarm.cron'] = firstArg;
70
- }
71
- }
72
-
73
- return tracer.startActiveSpan(
74
- spanName,
75
- {
76
- kind: SpanKind.CLIENT,
77
- attributes: alarmAttributes,
78
- },
79
- async (span) => {
80
- try {
81
- const result = await value.apply(target, args);
82
- span.setStatus({ code: SpanStatusCode.OK });
83
- return result;
84
- } catch (error) {
85
- span.recordException(error as Error);
86
- span.setStatus({
87
- code: SpanStatusCode.ERROR,
88
- message: error instanceof Error ? error.message : String(error),
89
- });
90
- throw error;
91
- } finally {
92
- span.end();
93
- }
94
- },
95
- );
96
- };
97
- }
98
-
99
- // Instrument setMultiple method
100
- if (prop === 'setMultiple' && typeof value === 'function') {
101
- return async function instrumentedSetMultiple(
102
- this: unknown,
103
- alarmDefs: unknown[],
104
- ): Promise<unknown> {
105
- const tracer = getTracer();
106
- const spanName = `Actor ${actorName}: alarms.setMultiple`;
107
-
108
- return tracer.startActiveSpan(
109
- spanName,
110
- {
111
- kind: SpanKind.CLIENT,
112
- attributes: {
113
- 'actor.name': actorName,
114
- 'actor.class': actorClassName,
115
- 'alarm.operation': 'setMultiple',
116
- 'alarm.count': Array.isArray(alarmDefs) ? alarmDefs.length : 0,
117
- },
118
- },
119
- async (span) => {
120
- try {
121
- const result = await value.call(target, alarmDefs);
122
- span.setStatus({ code: SpanStatusCode.OK });
123
- return result;
124
- } catch (error) {
125
- span.recordException(error as Error);
126
- span.setStatus({
127
- code: SpanStatusCode.ERROR,
128
- message: error instanceof Error ? error.message : String(error),
129
- });
130
- throw error;
131
- } finally {
132
- span.end();
133
- }
134
- },
135
- );
136
- };
137
- }
138
-
139
- // Instrument cancel method
140
- if (prop === 'cancel' && typeof value === 'function') {
141
- return async function instrumentedCancel(
142
- this: unknown,
143
- alarmId: string,
144
- ): Promise<unknown> {
145
- const tracer = getTracer();
146
- const spanName = `Actor ${actorName}: alarms.cancel`;
147
-
148
- return tracer.startActiveSpan(
149
- spanName,
150
- {
151
- kind: SpanKind.CLIENT,
152
- attributes: {
153
- 'actor.name': actorName,
154
- 'actor.class': actorClassName,
155
- 'alarm.operation': 'cancel',
156
- 'alarm.id': alarmId,
157
- },
158
- },
159
- async (span) => {
160
- try {
161
- const result = await value.call(target, alarmId);
162
- span.setStatus({ code: SpanStatusCode.OK });
163
- return result;
164
- } catch (error) {
165
- span.recordException(error as Error);
166
- span.setStatus({
167
- code: SpanStatusCode.ERROR,
168
- message: error instanceof Error ? error.message : String(error),
169
- });
170
- throw error;
171
- } finally {
172
- span.end();
173
- }
174
- },
175
- );
176
- };
177
- }
178
-
179
- // Instrument cancelAll method
180
- if (prop === 'cancelAll' && typeof value === 'function') {
181
- return async function instrumentedCancelAll(this: unknown): Promise<unknown> {
182
- const tracer = getTracer();
183
- const spanName = `Actor ${actorName}: alarms.cancelAll`;
184
-
185
- return tracer.startActiveSpan(
186
- spanName,
187
- {
188
- kind: SpanKind.CLIENT,
189
- attributes: {
190
- 'actor.name': actorName,
191
- 'actor.class': actorClassName,
192
- 'alarm.operation': 'cancelAll',
193
- },
194
- },
195
- async (span) => {
196
- try {
197
- const result = await value.call(target);
198
- span.setStatus({ code: SpanStatusCode.OK });
199
- return result;
200
- } catch (error) {
201
- span.recordException(error as Error);
202
- span.setStatus({
203
- code: SpanStatusCode.ERROR,
204
- message: error instanceof Error ? error.message : String(error),
205
- });
206
- throw error;
207
- } finally {
208
- span.end();
209
- }
210
- },
211
- );
212
- };
213
- }
214
-
215
- // Bind other methods to the target
216
- if (typeof value === 'function') {
217
- return value.bind(target);
218
- }
219
-
220
- return value;
221
- },
222
- };
223
-
224
- return wrap(alarms, alarmsHandler);
225
- }
@@ -1,36 +0,0 @@
1
- /**
2
- * @cloudflare/actors integration for autotel-cloudflare
3
- *
4
- * Provides deep instrumentation for the Cloudflare Actors framework,
5
- * tracing all lifecycle methods and providing Actor-specific semantic attributes.
6
- *
7
- * @example
8
- * ```typescript
9
- * import { Actor } from '@cloudflare/actors'
10
- * import { instrumentActor, tracedHandler } from 'autotel-cloudflare/actors'
11
- *
12
- * class MyActor extends Actor<Env> {
13
- * protected onInit() {
14
- * // Automatically traced with 'actor.lifecycle': 'init'
15
- * }
16
- *
17
- * protected onRequest(request: Request) {
18
- * // Automatically traced with full HTTP semantics + actor context
19
- * return new Response('Hello!')
20
- * }
21
- * }
22
- *
23
- * export { MyActor }
24
- * export default tracedHandler(MyActor, (env) => ({
25
- * service: { name: 'my-actor-service' },
26
- * exporter: { url: env.OTLP_ENDPOINT }
27
- * }))
28
- * ```
29
- */
30
-
31
- export { instrumentActor } from './instrument-actor';
32
- export { tracedHandler, wrapHandler } from './traced-handler';
33
- export { instrumentActorStorage } from './storage';
34
- export { instrumentActorAlarms } from './alarms';
35
- export { instrumentActorSockets } from './sockets';
36
- export type { ActorConfig, ActorInstrumentationOptions } from './types';
@@ -1,179 +0,0 @@
1
- /**
2
- * Tests for Actor instrumentation
3
- */
4
-
5
- import { describe, it, expect, vi, beforeEach } from 'vitest';
6
- import { instrumentActor } from './instrument-actor';
7
- import type { ActorLike } from './types';
8
-
9
- // Mock dependencies
10
- vi.mock('@opentelemetry/api', () => ({
11
- trace: {
12
- getTracer: () => ({
13
- startActiveSpan: vi.fn((name, options, contextOrCallback, maybeCallback) => {
14
- const callback = typeof contextOrCallback === 'function' ? contextOrCallback : maybeCallback;
15
- const mockSpan = {
16
- setAttributes: vi.fn(),
17
- setAttribute: vi.fn(),
18
- setStatus: vi.fn(),
19
- recordException: vi.fn(),
20
- end: vi.fn(),
21
- };
22
- return callback(mockSpan);
23
- }),
24
- }),
25
- },
26
- context: {
27
- active: () => ({}),
28
- with: (_ctx: unknown, fn: () => unknown) => fn(),
29
- },
30
- propagation: {
31
- extract: () => ({}),
32
- },
33
- SpanStatusCode: {
34
- OK: 'OK',
35
- ERROR: 'ERROR',
36
- },
37
- SpanKind: {
38
- SERVER: 'SERVER',
39
- INTERNAL: 'INTERNAL',
40
- CLIENT: 'CLIENT',
41
- PRODUCER: 'PRODUCER',
42
- },
43
- }));
44
-
45
- vi.mock('autotel-edge', () => ({
46
- createInitialiser: () => () => ({}),
47
- setConfig: () => ({}),
48
- WorkerTracer: class {},
49
- }));
50
-
51
- vi.mock('../bindings/common', () => ({
52
- wrap: <T>(target: T, handler: ProxyHandler<T>): T => new Proxy(target, handler) as T,
53
- }));
54
-
55
- // Mock Actor class for testing
56
- class MockActor implements ActorLike {
57
- name = 'test-actor';
58
- identifier = 'test-123';
59
- storage = {};
60
- alarms = {};
61
- sockets = {};
62
-
63
- async onInit(): Promise<void> {
64
- // Mock initialization
65
- }
66
-
67
- async onRequest(request: Request): Promise<Response> {
68
- return new Response('OK');
69
- }
70
-
71
- async onAlarm(): Promise<void> {
72
- // Mock alarm
73
- }
74
-
75
- onPersist(key: string, value: unknown): void {
76
- // Mock persist
77
- }
78
-
79
- async fetch(request: Request): Promise<Response> {
80
- return this.onRequest(request);
81
- }
82
-
83
- async alarm(): Promise<void> {
84
- return this.onAlarm();
85
- }
86
- }
87
-
88
- // Mock DurableObjectState
89
- const mockState = {
90
- id: {
91
- toString: () => 'do-id-123',
92
- name: 'test-do',
93
- },
94
- storage: {},
95
- } as unknown as DurableObjectState;
96
-
97
- describe('instrumentActor', () => {
98
- beforeEach(() => {
99
- vi.clearAllMocks();
100
- });
101
-
102
- it('should return an instrumented class constructor', () => {
103
- const InstrumentedActor = instrumentActor(
104
- MockActor as unknown as new (state: DurableObjectState, env: unknown) => ActorLike,
105
- { service: { name: 'test-service' } },
106
- );
107
-
108
- expect(typeof InstrumentedActor).toBe('function');
109
- });
110
-
111
- it('should create an instrumented instance', () => {
112
- const InstrumentedActor = instrumentActor(
113
- MockActor as unknown as new (state: DurableObjectState, env: unknown) => ActorLike,
114
- { service: { name: 'test-service' } },
115
- );
116
-
117
- const instance = new InstrumentedActor(mockState, {});
118
- expect(instance).toBeDefined();
119
- });
120
-
121
- it('should support config as a function', () => {
122
- const configFn = vi.fn(() => ({ service: { name: 'dynamic-service' } }));
123
-
124
- const InstrumentedActor = instrumentActor(
125
- MockActor as unknown as new (state: DurableObjectState, env: unknown) => ActorLike,
126
- configFn,
127
- );
128
-
129
- new InstrumentedActor(mockState, { API_KEY: 'test' });
130
-
131
- expect(configFn).toHaveBeenCalled();
132
- });
133
-
134
- it('should support actors-specific options', () => {
135
- const InstrumentedActor = instrumentActor(
136
- MockActor as unknown as new (state: DurableObjectState, env: unknown) => ActorLike,
137
- {
138
- service: { name: 'test-service' },
139
- actors: {
140
- instrumentStorage: false,
141
- capturePersistEvents: true,
142
- },
143
- },
144
- );
145
-
146
- const instance = new InstrumentedActor(mockState, {});
147
- expect(instance).toBeDefined();
148
- });
149
- });
150
-
151
- describe('ActorInstrumentationOptions', () => {
152
- it('should default instrumentStorage to true', () => {
153
- const InstrumentedActor = instrumentActor(
154
- MockActor as unknown as new (state: DurableObjectState, env: unknown) => ActorLike,
155
- { service: { name: 'test' } },
156
- );
157
-
158
- const instance = new InstrumentedActor(mockState, {});
159
- // Storage should be instrumented by default
160
- expect(instance.storage).toBeDefined();
161
- });
162
-
163
- it('should respect custom spanNameFormatter', () => {
164
- const formatter = vi.fn((actorName: string, lifecycle: string) => `Custom: ${actorName} - ${lifecycle}`);
165
-
166
- const InstrumentedActor = instrumentActor(
167
- MockActor as unknown as new (state: DurableObjectState, env: unknown) => ActorLike,
168
- {
169
- service: { name: 'test' },
170
- actors: {
171
- spanNameFormatter: formatter,
172
- },
173
- },
174
- );
175
-
176
- const instance = new InstrumentedActor(mockState, {});
177
- expect(instance).toBeDefined();
178
- });
179
- });