@parsrun/service 0.1.29 → 0.1.31

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/config.ts","../src/rpc/client.ts","../src/rpc/errors.ts","../src/resilience/circuit-breaker.ts","../src/resilience/bulkhead.ts","../src/resilience/retry.ts","../src/resilience/timeout.ts","../src/rpc/transports/embedded.ts","../src/serialization/index.ts","../src/rpc/transports/http.ts","../src/events/transports/memory.ts","../src/events/handler.ts","../src/events/format.ts","../src/tracing/tracer.ts","../src/tracing/exporters.ts"],"sourcesContent":["/**\n * @parsrun/service - Service Client\n * High-level client API for consuming services\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { generateId } from \"@parsrun/core\";\nimport type {\n ServiceDefinition,\n ServiceClient,\n ServiceClientOptions,\n ServiceConfig,\n RpcTransport,\n EventTransport,\n EventHandler,\n EventHandlerOptions,\n Unsubscribe,\n ParsEvent,\n QueryInput,\n QueryOutput,\n MutationInput,\n MutationOutput,\n EventData,\n} from \"./types.js\";\nimport { mergeConfig } from \"./config.js\";\nimport { RpcClient } from \"./rpc/client.js\";\nimport { getEmbeddedRegistry } from \"./rpc/transports/embedded.js\";\nimport { createHttpTransport } from \"./rpc/transports/http.js\";\nimport { createMemoryEventTransport, getGlobalEventBus } from \"./events/transports/memory.js\";\nimport { Tracer, getGlobalTracer } from \"./tracing/tracer.js\";\n\n// ============================================================================\n// SERVICE CLIENT IMPLEMENTATION\n// ============================================================================\n\n/**\n * Full service client with RPC and Events\n */\nclass ServiceClientImpl<TDef extends ServiceDefinition> implements ServiceClient<TDef> {\n readonly name: string;\n private readonly rpcClient: RpcClient;\n private readonly eventTransport: EventTransport;\n private readonly config: Required<ServiceConfig>;\n private readonly tracer: Tracer | null;\n\n constructor(\n definition: TDef,\n rpcTransport: RpcTransport,\n eventTransport: EventTransport,\n config: ServiceConfig,\n _logger?: Logger\n ) {\n this.name = definition.name;\n this.config = mergeConfig(config);\n this.tracer = getGlobalTracer();\n\n this.rpcClient = new RpcClient({\n service: definition.name,\n transport: rpcTransport,\n config: this.config,\n });\n\n this.eventTransport = eventTransport;\n }\n\n /**\n * Execute a query\n */\n async query<K extends keyof TDef[\"queries\"]>(\n method: K,\n input: QueryInput<TDef[\"queries\"], K>\n ): Promise<QueryOutput<TDef[\"queries\"], K>> {\n const methodName = String(method);\n const traceContext = this.tracer?.currentContext();\n\n // Trace if available\n if (this.tracer && traceContext) {\n return this.tracer.trace(\n `rpc.${this.name}.${methodName}`,\n async () => {\n return this.rpcClient.query(methodName, input, {\n traceContext,\n }) as Promise<QueryOutput<TDef[\"queries\"], K>>;\n },\n { kind: \"client\" }\n ) as Promise<QueryOutput<TDef[\"queries\"], K>>;\n }\n\n return this.rpcClient.query(methodName, input) as Promise<QueryOutput<TDef[\"queries\"], K>>;\n }\n\n /**\n * Execute a mutation\n */\n async mutate<K extends keyof TDef[\"mutations\"]>(\n method: K,\n input: MutationInput<TDef[\"mutations\"], K>\n ): Promise<MutationOutput<TDef[\"mutations\"], K>> {\n const methodName = String(method);\n const traceContext = this.tracer?.currentContext();\n\n // Trace if available\n if (this.tracer && traceContext) {\n return this.tracer.trace(\n `rpc.${this.name}.${methodName}`,\n async () => {\n return this.rpcClient.mutate(methodName, input, {\n traceContext,\n }) as Promise<MutationOutput<TDef[\"mutations\"], K>>;\n },\n { kind: \"client\" }\n ) as Promise<MutationOutput<TDef[\"mutations\"], K>>;\n }\n\n return this.rpcClient.mutate(methodName, input) as Promise<MutationOutput<TDef[\"mutations\"], K>>;\n }\n\n /**\n * Emit an event\n */\n async emit<K extends keyof NonNullable<TDef[\"events\"]>[\"emits\"]>(\n eventType: K,\n data: EventData<NonNullable<TDef[\"events\"]>[\"emits\"], K>\n ): Promise<void> {\n const type = String(eventType);\n const traceContext = this.tracer?.currentContext();\n\n const event: ParsEvent = {\n specversion: \"1.0\",\n type,\n source: this.name,\n id: generateId(),\n time: new Date().toISOString(),\n data,\n };\n\n // Add trace context if available\n if (traceContext) {\n event.parstracecontext = `00-${traceContext.traceId}-${traceContext.spanId}-01`;\n }\n\n await this.eventTransport.emit(event);\n }\n\n /**\n * Subscribe to events\n */\n on<T = unknown>(\n eventType: string,\n handler: EventHandler<T>,\n options?: EventHandlerOptions\n ): Unsubscribe {\n return this.eventTransport.subscribe(eventType, handler as EventHandler, options);\n }\n\n /**\n * Get circuit breaker state\n */\n getCircuitState(): \"closed\" | \"open\" | \"half-open\" | null {\n return this.rpcClient.getCircuitState();\n }\n\n /**\n * Close the client\n */\n async close(): Promise<void> {\n await this.rpcClient.close();\n await this.eventTransport.close?.();\n }\n}\n\n// ============================================================================\n// USE SERVICE FACTORY\n// ============================================================================\n\n/**\n * Create a service client\n *\n * @example\n * ```typescript\n * // Embedded mode (same process)\n * const payments = useService('payments');\n *\n * // HTTP mode (remote service)\n * const payments = useService('payments', {\n * mode: 'http',\n * baseUrl: 'https://payments.example.com',\n * });\n *\n * // Cloudflare binding mode\n * const payments = useService('payments', {\n * mode: 'binding',\n * binding: env.PAYMENTS,\n * });\n * ```\n */\nexport function useService<TDef extends ServiceDefinition = ServiceDefinition>(\n serviceName: string,\n options: ServiceClientOptions = {}\n): ServiceClient<TDef> {\n const mode = options.mode ?? \"embedded\";\n const config = options.config ?? {};\n\n let rpcTransport: RpcTransport;\n let eventTransport: EventTransport;\n\n switch (mode) {\n case \"embedded\": {\n // Use embedded registry\n const registry = getEmbeddedRegistry();\n if (!registry.has(serviceName)) {\n throw new Error(\n `Service not found in embedded registry: ${serviceName}. ` +\n `Make sure the service is registered before using it.`\n );\n }\n rpcTransport = registry.createTransport(serviceName);\n\n // Use global event bus\n const eventBus = getGlobalEventBus();\n const services = eventBus.getServices();\n if (services.includes(serviceName)) {\n // Get existing transport from bus\n eventTransport = createMemoryEventTransport();\n } else {\n eventTransport = createMemoryEventTransport();\n }\n break;\n }\n\n case \"http\": {\n if (!options.baseUrl) {\n throw new Error(\"baseUrl is required for HTTP mode\");\n }\n rpcTransport = createHttpTransport({\n baseUrl: options.baseUrl,\n });\n // Events over HTTP would need webhook or polling - use memory for now\n eventTransport = createMemoryEventTransport();\n break;\n }\n\n case \"binding\": {\n if (!options.binding) {\n throw new Error(\"binding is required for binding mode\");\n }\n // Cloudflare service binding transport\n rpcTransport = createBindingTransport(serviceName, options.binding);\n eventTransport = createMemoryEventTransport();\n break;\n }\n\n default:\n throw new Error(`Unknown service client mode: ${mode}`);\n }\n\n // Use custom transports if provided\n if (options.rpcTransport) {\n rpcTransport = options.rpcTransport;\n }\n if (options.eventTransport) {\n eventTransport = options.eventTransport;\n }\n\n // Create a minimal definition for the client\n const definition: ServiceDefinition = {\n name: serviceName,\n version: \"1.x\",\n };\n\n return new ServiceClientImpl<TDef>(\n definition as TDef,\n rpcTransport,\n eventTransport,\n config\n );\n}\n\n/**\n * Create a typed service client from a definition\n */\nexport function useTypedService<TDef extends ServiceDefinition>(\n definition: TDef,\n options: ServiceClientOptions = {}\n): ServiceClient<TDef> {\n return useService<TDef>(definition.name, options);\n}\n\n// ============================================================================\n// BINDING TRANSPORT HELPER\n// ============================================================================\n\nimport type { Fetcher, RpcRequest, RpcResponse } from \"./types.js\";\n\n/**\n * Create RPC transport for Cloudflare service binding\n */\nfunction createBindingTransport(_serviceName: string, binding: Fetcher): RpcTransport {\n return {\n name: \"binding\",\n async call<TInput, TOutput>(request: RpcRequest<TInput>) {\n const response = await binding.fetch(\"http://internal/rpc\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Request-ID\": request.id,\n \"X-Service\": request.service,\n \"X-Method\": request.method,\n },\n body: JSON.stringify(request),\n });\n\n return response.json() as Promise<RpcResponse<TOutput>>;\n },\n async close() {\n // No cleanup needed for bindings\n },\n };\n}\n\n// ============================================================================\n// SERVICE REGISTRY CLIENT\n// ============================================================================\n\n/**\n * Service registry for managing multiple service clients\n */\nexport class ServiceRegistry {\n private readonly clients: Map<string, ServiceClient> = new Map();\n private readonly config: ServiceConfig;\n\n constructor(config?: ServiceConfig) {\n this.config = config ?? {};\n }\n\n /**\n * Get or create a service client\n */\n get<TDef extends ServiceDefinition = ServiceDefinition>(\n serviceName: string,\n options?: ServiceClientOptions\n ): ServiceClient<TDef> {\n let client = this.clients.get(serviceName);\n if (!client) {\n client = useService<TDef>(serviceName, {\n ...options,\n config: { ...this.config, ...options?.config },\n });\n this.clients.set(serviceName, client);\n }\n return client as ServiceClient<TDef>;\n }\n\n /**\n * Close all clients\n */\n async closeAll(): Promise<void> {\n const closePromises = Array.from(this.clients.values()).map((client) => {\n if (\"close\" in client && typeof client.close === \"function\") {\n return (client as { close: () => Promise<void> }).close();\n }\n return Promise.resolve();\n });\n await Promise.all(closePromises);\n this.clients.clear();\n }\n}\n\n/**\n * Create a service registry\n */\nexport function createServiceRegistry(config?: ServiceConfig): ServiceRegistry {\n return new ServiceRegistry(config);\n}\n","/**\n * @parsrun/service - Configuration\n * Default configuration and config utilities\n */\n\nimport type {\n ServiceConfig,\n EventFormatConfig,\n SerializationConfig,\n TracingConfig,\n VersioningConfig,\n ResilienceConfig,\n DeadLetterConfig,\n} from \"./types.js\";\n\n// ============================================================================\n// DEFAULT CONFIGURATIONS\n// ============================================================================\n\nexport const DEFAULT_EVENT_CONFIG: Required<EventFormatConfig> = {\n format: \"cloudevents\",\n internalCompact: true,\n};\n\nexport const DEFAULT_SERIALIZATION_CONFIG: Required<SerializationConfig> = {\n format: \"json\",\n};\n\nexport const DEFAULT_TRACING_CONFIG: Required<TracingConfig> = {\n enabled: true,\n sampler: { ratio: 0.1 },\n exporter: \"console\",\n endpoint: \"\",\n serviceName: \"pars-service\",\n};\n\nexport const DEFAULT_VERSIONING_CONFIG: Required<VersioningConfig> = {\n strategy: \"header\",\n defaultVersion: \"1.x\",\n};\n\nexport const DEFAULT_RESILIENCE_CONFIG: Required<ResilienceConfig> = {\n circuitBreaker: {\n enabled: true,\n failureThreshold: 5,\n resetTimeout: 30_000,\n successThreshold: 2,\n },\n bulkhead: {\n maxConcurrent: 100,\n maxQueue: 50,\n },\n timeout: 5_000,\n retry: {\n attempts: 3,\n backoff: \"exponential\",\n initialDelay: 100,\n maxDelay: 10_000,\n },\n};\n\nexport const DEFAULT_DEAD_LETTER_CONFIG: Required<DeadLetterConfig> = {\n enabled: true,\n retention: \"30d\",\n onFail: \"alert\",\n alertThreshold: 10,\n};\n\nexport const DEFAULT_SERVICE_CONFIG: Required<ServiceConfig> = {\n events: DEFAULT_EVENT_CONFIG,\n serialization: DEFAULT_SERIALIZATION_CONFIG,\n tracing: DEFAULT_TRACING_CONFIG,\n versioning: DEFAULT_VERSIONING_CONFIG,\n resilience: DEFAULT_RESILIENCE_CONFIG,\n deadLetter: DEFAULT_DEAD_LETTER_CONFIG,\n};\n\n// ============================================================================\n// CONFIG UTILITIES\n// ============================================================================\n\n/**\n * Merge user config with defaults\n */\nexport function mergeConfig(userConfig?: Partial<ServiceConfig>): Required<ServiceConfig> {\n if (!userConfig) {\n return { ...DEFAULT_SERVICE_CONFIG };\n }\n\n return {\n events: {\n ...DEFAULT_EVENT_CONFIG,\n ...userConfig.events,\n },\n serialization: {\n ...DEFAULT_SERIALIZATION_CONFIG,\n ...userConfig.serialization,\n },\n tracing: {\n ...DEFAULT_TRACING_CONFIG,\n ...userConfig.tracing,\n },\n versioning: {\n ...DEFAULT_VERSIONING_CONFIG,\n ...userConfig.versioning,\n },\n resilience: {\n ...DEFAULT_RESILIENCE_CONFIG,\n ...userConfig.resilience,\n circuitBreaker: {\n ...DEFAULT_RESILIENCE_CONFIG.circuitBreaker,\n ...userConfig.resilience?.circuitBreaker,\n },\n bulkhead: {\n ...DEFAULT_RESILIENCE_CONFIG.bulkhead,\n ...userConfig.resilience?.bulkhead,\n },\n retry: {\n ...DEFAULT_RESILIENCE_CONFIG.retry,\n ...userConfig.resilience?.retry,\n },\n },\n deadLetter: {\n ...DEFAULT_DEAD_LETTER_CONFIG,\n ...userConfig.deadLetter,\n },\n };\n}\n\n/**\n * Create config for development\n */\nexport function createDevConfig(overrides?: Partial<ServiceConfig>): Required<ServiceConfig> {\n return mergeConfig({\n tracing: {\n enabled: true,\n sampler: \"always\",\n exporter: \"console\",\n },\n resilience: {\n circuitBreaker: { enabled: false },\n timeout: 30_000,\n },\n ...overrides,\n });\n}\n\n/**\n * Create config for production\n */\nexport function createProdConfig(overrides?: Partial<ServiceConfig>): Required<ServiceConfig> {\n return mergeConfig({\n tracing: {\n enabled: true,\n sampler: { ratio: 0.1 },\n exporter: \"otlp\",\n },\n resilience: {\n circuitBreaker: { enabled: true },\n timeout: 5_000,\n },\n ...overrides,\n });\n}\n\n/**\n * Validate config\n */\nexport function validateConfig(config: ServiceConfig): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Validate tracing\n if (config.tracing?.sampler && typeof config.tracing.sampler === \"object\") {\n const ratio = config.tracing.sampler.ratio;\n if (ratio < 0 || ratio > 1) {\n errors.push(\"tracing.sampler.ratio must be between 0 and 1\");\n }\n }\n\n // Validate resilience\n if (config.resilience?.timeout !== undefined && config.resilience.timeout < 0) {\n errors.push(\"resilience.timeout must be non-negative\");\n }\n\n if (config.resilience?.circuitBreaker?.failureThreshold !== undefined) {\n if (config.resilience.circuitBreaker.failureThreshold < 1) {\n errors.push(\"resilience.circuitBreaker.failureThreshold must be at least 1\");\n }\n }\n\n if (config.resilience?.bulkhead?.maxConcurrent !== undefined) {\n if (config.resilience.bulkhead.maxConcurrent < 1) {\n errors.push(\"resilience.bulkhead.maxConcurrent must be at least 1\");\n }\n }\n\n if (config.resilience?.retry?.attempts !== undefined) {\n if (config.resilience.retry.attempts < 0) {\n errors.push(\"resilience.retry.attempts must be non-negative\");\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n","/**\n * @parsrun/service - RPC Client\n * Client for making RPC calls to services\n */\n\nimport { generateId } from \"@parsrun/core\";\nimport type {\n RpcRequest,\n RpcTransport,\n RpcMetadata,\n TraceContext,\n ServiceConfig,\n} from \"../types.js\";\nimport { mergeConfig } from \"../config.js\";\nimport { TimeoutError, toRpcError } from \"./errors.js\";\nimport {\n CircuitBreaker,\n Bulkhead,\n withRetry,\n withTimeout,\n} from \"../resilience/index.js\";\n\n// ============================================================================\n// RPC CLIENT\n// ============================================================================\n\nexport interface RpcClientOptions {\n /** Service name */\n service: string;\n /** RPC transport */\n transport: RpcTransport;\n /** Service configuration */\n config?: ServiceConfig;\n /** Default metadata for all requests */\n defaultMetadata?: RpcMetadata;\n}\n\n/**\n * RPC Client for making service calls\n */\nexport class RpcClient {\n private readonly service: string;\n private readonly transport: RpcTransport;\n private readonly config: Required<ServiceConfig>;\n private readonly defaultMetadata: RpcMetadata;\n private readonly circuitBreaker: CircuitBreaker | null;\n private readonly bulkhead: Bulkhead | null;\n\n constructor(options: RpcClientOptions) {\n this.service = options.service;\n this.transport = options.transport;\n this.config = mergeConfig(options.config);\n this.defaultMetadata = options.defaultMetadata ?? {};\n\n // Initialize circuit breaker\n const cbConfig = this.config.resilience?.circuitBreaker;\n if (\n cbConfig &&\n cbConfig.enabled &&\n cbConfig.failureThreshold !== undefined &&\n cbConfig.resetTimeout !== undefined &&\n cbConfig.successThreshold !== undefined\n ) {\n this.circuitBreaker = new CircuitBreaker({\n failureThreshold: cbConfig.failureThreshold,\n resetTimeout: cbConfig.resetTimeout,\n successThreshold: cbConfig.successThreshold,\n });\n } else {\n this.circuitBreaker = null;\n }\n\n // Initialize bulkhead\n const bhConfig = this.config.resilience?.bulkhead;\n if (bhConfig && bhConfig.maxConcurrent !== undefined && bhConfig.maxQueue !== undefined) {\n this.bulkhead = new Bulkhead({\n maxConcurrent: bhConfig.maxConcurrent,\n maxQueue: bhConfig.maxQueue,\n });\n } else {\n this.bulkhead = null;\n }\n }\n\n /**\n * Execute a query\n */\n async query<TInput, TOutput>(\n method: string,\n input: TInput,\n options?: CallOptions\n ): Promise<TOutput> {\n return this.call<TInput, TOutput>(\"query\", method, input, options);\n }\n\n /**\n * Execute a mutation\n */\n async mutate<TInput, TOutput>(\n method: string,\n input: TInput,\n options?: CallOptions\n ): Promise<TOutput> {\n return this.call<TInput, TOutput>(\"mutation\", method, input, options);\n }\n\n /**\n * Internal call implementation\n */\n private async call<TInput, TOutput>(\n type: \"query\" | \"mutation\",\n method: string,\n input: TInput,\n options?: CallOptions\n ): Promise<TOutput> {\n const request: RpcRequest<TInput> = {\n id: generateId(),\n service: this.service,\n method,\n type,\n input,\n metadata: {\n ...this.defaultMetadata,\n ...options?.metadata,\n },\n };\n\n const version = options?.version ?? this.config.versioning.defaultVersion;\n if (version) {\n request.version = version;\n }\n if (options?.traceContext) {\n request.traceContext = options.traceContext;\n }\n\n const timeout = options?.timeout ?? this.config.resilience.timeout ?? 30_000;\n const retryConfig = options?.retry ?? this.config.resilience.retry;\n\n // Build the execution chain\n let execute = async (): Promise<TOutput> => {\n const response = await this.transport.call<TInput, TOutput>(request);\n\n if (!response.success) {\n const error = toRpcError(\n new Error(response.error?.message ?? \"Unknown error\")\n );\n throw error;\n }\n\n return response.output as TOutput;\n };\n\n // Wrap with timeout\n execute = withTimeout(execute, timeout, () => {\n throw new TimeoutError(this.service, method, timeout);\n });\n\n // Wrap with retry\n const attempts = retryConfig?.attempts ?? 0;\n if (attempts > 0) {\n execute = withRetry(execute, {\n attempts,\n backoff: retryConfig?.backoff ?? \"exponential\",\n initialDelay: retryConfig?.initialDelay ?? 100,\n maxDelay: retryConfig?.maxDelay ?? 5000,\n shouldRetry: (error) => {\n if (error instanceof Error && \"retryable\" in error) {\n return (error as { retryable: boolean }).retryable;\n }\n return false;\n },\n });\n }\n\n // Wrap with circuit breaker\n if (this.circuitBreaker) {\n const cb = this.circuitBreaker;\n const originalExecute = execute;\n execute = async () => {\n return cb.execute(originalExecute);\n };\n }\n\n // Wrap with bulkhead\n if (this.bulkhead) {\n const bh = this.bulkhead;\n const originalExecute = execute;\n execute = async () => {\n return bh.execute(originalExecute);\n };\n }\n\n return execute();\n }\n\n /**\n * Get circuit breaker state\n */\n getCircuitState(): \"closed\" | \"open\" | \"half-open\" | null {\n return this.circuitBreaker?.state ?? null;\n }\n\n /**\n * Get bulkhead stats\n */\n getBulkheadStats(): { concurrent: number; queued: number } | null {\n if (!this.bulkhead) return null;\n return {\n concurrent: this.bulkhead.concurrent,\n queued: this.bulkhead.queued,\n };\n }\n\n /**\n * Close the client and release resources\n */\n async close(): Promise<void> {\n await this.transport.close?.();\n }\n}\n\n/**\n * Call options\n */\nexport interface CallOptions {\n /** Timeout in ms */\n timeout?: number;\n /** Version requirement */\n version?: string;\n /** Trace context */\n traceContext?: TraceContext;\n /** Request metadata */\n metadata?: RpcMetadata;\n /** Retry configuration override */\n retry?: {\n attempts?: number;\n backoff?: \"linear\" | \"exponential\";\n initialDelay?: number;\n maxDelay?: number;\n };\n}\n\n/**\n * Create an RPC client\n */\nexport function createRpcClient(options: RpcClientOptions): RpcClient {\n return new RpcClient(options);\n}\n","/**\n * @parsrun/service - RPC Errors\n */\n\nimport { ParsError } from \"@parsrun/core\";\n\n/**\n * Base RPC error\n */\nexport class RpcError extends ParsError {\n public readonly retryable: boolean;\n public readonly retryAfter?: number;\n\n constructor(\n message: string,\n code: string,\n statusCode: number = 500,\n options?: {\n retryable?: boolean;\n retryAfter?: number;\n details?: Record<string, unknown>;\n }\n ) {\n super(message, code, statusCode, options?.details);\n this.name = \"RpcError\";\n this.retryable = options?.retryable ?? false;\n if (options?.retryAfter !== undefined) {\n this.retryAfter = options.retryAfter;\n }\n }\n}\n\n/**\n * Service not found error\n */\nexport class ServiceNotFoundError extends RpcError {\n constructor(serviceName: string) {\n super(`Service not found: ${serviceName}`, \"SERVICE_NOT_FOUND\", 404, {\n retryable: false,\n details: { service: serviceName },\n });\n this.name = \"ServiceNotFoundError\";\n }\n}\n\n/**\n * Method not found error\n */\nexport class MethodNotFoundError extends RpcError {\n constructor(serviceName: string, methodName: string) {\n super(\n `Method not found: ${serviceName}.${methodName}`,\n \"METHOD_NOT_FOUND\",\n 404,\n {\n retryable: false,\n details: { service: serviceName, method: methodName },\n }\n );\n this.name = \"MethodNotFoundError\";\n }\n}\n\n/**\n * Version mismatch error\n */\nexport class VersionMismatchError extends RpcError {\n constructor(serviceName: string, requested: string, available: string) {\n super(\n `Version mismatch for ${serviceName}: requested ${requested}, available ${available}`,\n \"VERSION_MISMATCH\",\n 400,\n {\n retryable: false,\n details: { service: serviceName, requested, available },\n }\n );\n this.name = \"VersionMismatchError\";\n }\n}\n\n/**\n * Timeout error\n */\nexport class TimeoutError extends RpcError {\n constructor(serviceName: string, methodName: string, timeoutMs: number) {\n super(\n `Request to ${serviceName}.${methodName} timed out after ${timeoutMs}ms`,\n \"TIMEOUT\",\n 504,\n {\n retryable: true,\n details: { service: serviceName, method: methodName, timeout: timeoutMs },\n }\n );\n this.name = \"TimeoutError\";\n }\n}\n\n/**\n * Circuit breaker open error\n */\nexport class CircuitOpenError extends RpcError {\n constructor(serviceName: string, resetAfterMs: number) {\n super(\n `Circuit breaker open for ${serviceName}`,\n \"CIRCUIT_OPEN\",\n 503,\n {\n retryable: true,\n retryAfter: Math.ceil(resetAfterMs / 1000),\n details: { service: serviceName, resetAfterMs },\n }\n );\n this.name = \"CircuitOpenError\";\n }\n}\n\n/**\n * Bulkhead rejected error\n */\nexport class BulkheadRejectedError extends RpcError {\n constructor(serviceName: string) {\n super(\n `Request rejected by bulkhead for ${serviceName}: too many concurrent requests`,\n \"BULKHEAD_REJECTED\",\n 503,\n {\n retryable: true,\n retryAfter: 1,\n details: { service: serviceName },\n }\n );\n this.name = \"BulkheadRejectedError\";\n }\n}\n\n/**\n * Transport error\n */\nexport class TransportError extends RpcError {\n constructor(message: string, cause?: Error) {\n const options: { retryable: boolean; details?: Record<string, unknown> } = {\n retryable: true,\n };\n if (cause) {\n options.details = { cause: cause.message };\n }\n super(message, \"TRANSPORT_ERROR\", 502, options);\n this.name = \"TransportError\";\n }\n}\n\n/**\n * Serialization error\n */\nexport class SerializationError extends RpcError {\n constructor(message: string, cause?: Error) {\n const options: { retryable: boolean; details?: Record<string, unknown> } = {\n retryable: false,\n };\n if (cause) {\n options.details = { cause: cause.message };\n }\n super(message, \"SERIALIZATION_ERROR\", 400, options);\n this.name = \"SerializationError\";\n }\n}\n\n/**\n * Convert unknown error to RpcError\n */\nexport function toRpcError(error: unknown): RpcError {\n if (error instanceof RpcError) {\n return error;\n }\n\n if (error instanceof Error) {\n return new RpcError(error.message, \"INTERNAL_ERROR\", 500, {\n retryable: false,\n details: { originalError: error.name },\n });\n }\n\n return new RpcError(String(error), \"UNKNOWN_ERROR\", 500, {\n retryable: false,\n });\n}\n","/**\n * @parsrun/service - Circuit Breaker\n * Prevents cascading failures by failing fast when a service is unhealthy\n */\n\nimport { CircuitOpenError } from \"../rpc/errors.js\";\n\n// ============================================================================\n// CIRCUIT BREAKER\n// ============================================================================\n\nexport interface CircuitBreakerOptions {\n /** Number of failures before opening circuit */\n failureThreshold: number;\n /** Time to wait before half-open state (ms) */\n resetTimeout: number;\n /** Number of successes in half-open to close circuit */\n successThreshold: number;\n /** Optional callback on state change */\n onStateChange?: (from: CircuitState, to: CircuitState) => void;\n}\n\nexport type CircuitState = \"closed\" | \"open\" | \"half-open\";\n\n/**\n * Circuit Breaker implementation\n *\n * States:\n * - CLOSED: Normal operation, requests pass through\n * - OPEN: Failing fast, requests are rejected immediately\n * - HALF-OPEN: Testing if service recovered, limited requests allowed\n */\nexport class CircuitBreaker {\n private _state: CircuitState = \"closed\";\n private failures = 0;\n private successes = 0;\n private lastFailureTime = 0;\n private readonly options: CircuitBreakerOptions;\n\n constructor(options: CircuitBreakerOptions) {\n this.options = options;\n }\n\n /**\n * Get current state\n */\n get state(): CircuitState {\n // Check if we should transition from open to half-open\n if (this._state === \"open\") {\n const timeSinceFailure = Date.now() - this.lastFailureTime;\n if (timeSinceFailure >= this.options.resetTimeout) {\n this.transitionTo(\"half-open\");\n }\n }\n return this._state;\n }\n\n /**\n * Execute a function with circuit breaker protection\n */\n async execute<T>(fn: () => Promise<T>): Promise<T> {\n // Check state (this may transition from open to half-open)\n const currentState = this.state;\n\n if (currentState === \"open\") {\n const resetAfter = this.options.resetTimeout - (Date.now() - this.lastFailureTime);\n throw new CircuitOpenError(\"service\", Math.max(0, resetAfter));\n }\n\n try {\n const result = await fn();\n this.onSuccess();\n return result;\n } catch (error) {\n this.onFailure();\n throw error;\n }\n }\n\n /**\n * Record a successful call\n */\n private onSuccess(): void {\n if (this._state === \"half-open\") {\n this.successes++;\n if (this.successes >= this.options.successThreshold) {\n this.transitionTo(\"closed\");\n }\n } else if (this._state === \"closed\") {\n // Reset failure count on success\n this.failures = 0;\n }\n }\n\n /**\n * Record a failed call\n */\n private onFailure(): void {\n this.lastFailureTime = Date.now();\n\n if (this._state === \"half-open\") {\n // Any failure in half-open goes back to open\n this.transitionTo(\"open\");\n } else if (this._state === \"closed\") {\n this.failures++;\n if (this.failures >= this.options.failureThreshold) {\n this.transitionTo(\"open\");\n }\n }\n }\n\n /**\n * Transition to a new state\n */\n private transitionTo(newState: CircuitState): void {\n const oldState = this._state;\n this._state = newState;\n\n // Reset counters on state change\n if (newState === \"closed\") {\n this.failures = 0;\n this.successes = 0;\n } else if (newState === \"half-open\") {\n this.successes = 0;\n }\n\n this.options.onStateChange?.(oldState, newState);\n }\n\n /**\n * Manually reset the circuit breaker\n */\n reset(): void {\n this.transitionTo(\"closed\");\n }\n\n /**\n * Get circuit breaker statistics\n */\n getStats(): {\n state: CircuitState;\n failures: number;\n successes: number;\n lastFailureTime: number;\n } {\n return {\n state: this.state,\n failures: this.failures,\n successes: this.successes,\n lastFailureTime: this.lastFailureTime,\n };\n }\n}\n","/**\n * @parsrun/service - Bulkhead\n * Limits concurrent requests to prevent resource exhaustion\n */\n\nimport { BulkheadRejectedError } from \"../rpc/errors.js\";\n\n// ============================================================================\n// BULKHEAD\n// ============================================================================\n\nexport interface BulkheadOptions {\n /** Maximum concurrent requests */\n maxConcurrent: number;\n /** Maximum queue size (0 = no queue) */\n maxQueue: number;\n /** Optional callback when request is rejected */\n onRejected?: () => void;\n}\n\ninterface QueuedRequest<T> {\n fn: () => Promise<T>;\n resolve: (value: T) => void;\n reject: (error: Error) => void;\n}\n\n/**\n * Bulkhead implementation\n *\n * Limits the number of concurrent requests to protect resources.\n * Excess requests can be queued up to maxQueue limit.\n */\nexport class Bulkhead {\n private _concurrent = 0;\n private readonly queue: QueuedRequest<unknown>[] = [];\n private readonly options: BulkheadOptions;\n\n constructor(options: BulkheadOptions) {\n this.options = options;\n }\n\n /**\n * Get current concurrent count\n */\n get concurrent(): number {\n return this._concurrent;\n }\n\n /**\n * Get current queue size\n */\n get queued(): number {\n return this.queue.length;\n }\n\n /**\n * Check if bulkhead is full\n */\n get isFull(): boolean {\n return (\n this._concurrent >= this.options.maxConcurrent &&\n this.queue.length >= this.options.maxQueue\n );\n }\n\n /**\n * Execute a function with bulkhead protection\n */\n async execute<T>(fn: () => Promise<T>): Promise<T> {\n // Check if we can execute immediately\n if (this._concurrent < this.options.maxConcurrent) {\n return this.doExecute(fn);\n }\n\n // Check if we can queue\n if (this.queue.length < this.options.maxQueue) {\n return this.enqueue(fn);\n }\n\n // Reject\n this.options.onRejected?.();\n throw new BulkheadRejectedError(\"service\");\n }\n\n /**\n * Execute immediately\n */\n private async doExecute<T>(fn: () => Promise<T>): Promise<T> {\n this._concurrent++;\n try {\n return await fn();\n } finally {\n this._concurrent--;\n this.processQueue();\n }\n }\n\n /**\n * Add to queue\n */\n private enqueue<T>(fn: () => Promise<T>): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n this.queue.push({\n fn,\n resolve: resolve as (value: unknown) => void,\n reject,\n });\n });\n }\n\n /**\n * Process queued requests\n */\n private processQueue(): void {\n if (this.queue.length === 0) return;\n if (this._concurrent >= this.options.maxConcurrent) return;\n\n const queued = this.queue.shift();\n if (!queued) return;\n\n this.doExecute(queued.fn)\n .then(queued.resolve)\n .catch(queued.reject);\n }\n\n /**\n * Get bulkhead statistics\n */\n getStats(): {\n concurrent: number;\n queued: number;\n maxConcurrent: number;\n maxQueue: number;\n } {\n return {\n concurrent: this._concurrent,\n queued: this.queue.length,\n maxConcurrent: this.options.maxConcurrent,\n maxQueue: this.options.maxQueue,\n };\n }\n\n /**\n * Clear the queue (reject all pending)\n */\n clearQueue(): void {\n const error = new BulkheadRejectedError(\"service\");\n while (this.queue.length > 0) {\n const queued = this.queue.shift();\n queued?.reject(error);\n }\n }\n}\n","/**\n * @parsrun/service - Retry\n * Retry failed operations with backoff\n */\n\n// ============================================================================\n// RETRY\n// ============================================================================\n\nexport interface RetryOptions {\n /** Number of retry attempts (not including initial attempt) */\n attempts: number;\n /** Backoff strategy */\n backoff: \"linear\" | \"exponential\";\n /** Initial delay in ms */\n initialDelay: number;\n /** Maximum delay in ms */\n maxDelay: number;\n /** Jitter factor (0-1) to add randomness */\n jitter?: number;\n /** Should retry predicate */\n shouldRetry?: (error: unknown, attempt: number) => boolean;\n /** Callback on retry */\n onRetry?: (error: unknown, attempt: number, delay: number) => void;\n}\n\n/**\n * Default retry predicate - retry on retryable errors\n */\nconst defaultShouldRetry = (error: unknown): boolean => {\n if (error && typeof error === \"object\" && \"retryable\" in error) {\n return (error as { retryable: boolean }).retryable;\n }\n return false;\n};\n\n/**\n * Calculate delay for retry attempt\n */\nfunction calculateDelay(\n attempt: number,\n options: RetryOptions\n): number {\n let delay: number;\n\n if (options.backoff === \"exponential\") {\n // Exponential backoff: initialDelay * 2^attempt\n delay = options.initialDelay * Math.pow(2, attempt);\n } else {\n // Linear backoff: initialDelay * (attempt + 1)\n delay = options.initialDelay * (attempt + 1);\n }\n\n // Apply max delay\n delay = Math.min(delay, options.maxDelay);\n\n // Apply jitter\n if (options.jitter && options.jitter > 0) {\n const jitterRange = delay * options.jitter;\n delay = delay - jitterRange / 2 + Math.random() * jitterRange;\n }\n\n return Math.round(delay);\n}\n\n/**\n * Sleep for a given duration\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Wrap a function with retry logic\n */\nexport function withRetry<T>(\n fn: () => Promise<T>,\n options: RetryOptions\n): () => Promise<T> {\n const shouldRetry = options.shouldRetry ?? defaultShouldRetry;\n\n return async (): Promise<T> => {\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= options.attempts; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n\n // Check if we should retry\n if (attempt >= options.attempts || !shouldRetry(error, attempt)) {\n throw error;\n }\n\n // Calculate delay\n const delay = calculateDelay(attempt, options);\n\n // Callback\n options.onRetry?.(error, attempt + 1, delay);\n\n // Wait before retry\n await sleep(delay);\n }\n }\n\n throw lastError;\n };\n}\n\n/**\n * Execute a function with retry\n */\nexport async function executeWithRetry<T>(\n fn: () => Promise<T>,\n options: RetryOptions\n): Promise<T> {\n return withRetry(fn, options)();\n}\n\n/**\n * Create a retry wrapper with preset options\n */\nexport function createRetryWrapper(\n defaultOptions: Partial<RetryOptions>\n): <T>(fn: () => Promise<T>, options?: Partial<RetryOptions>) => Promise<T> {\n const defaults: RetryOptions = {\n attempts: defaultOptions.attempts ?? 3,\n backoff: defaultOptions.backoff ?? \"exponential\",\n initialDelay: defaultOptions.initialDelay ?? 100,\n maxDelay: defaultOptions.maxDelay ?? 10_000,\n jitter: defaultOptions.jitter ?? 0.1,\n };\n\n if (defaultOptions.shouldRetry) {\n defaults.shouldRetry = defaultOptions.shouldRetry;\n }\n if (defaultOptions.onRetry) {\n defaults.onRetry = defaultOptions.onRetry;\n }\n\n return async <T>(\n fn: () => Promise<T>,\n options?: Partial<RetryOptions>\n ): Promise<T> => {\n return executeWithRetry(fn, { ...defaults, ...options });\n };\n}\n","/**\n * @parsrun/service - Timeout\n * Timeout wrapper for async operations\n */\n\n// ============================================================================\n// TIMEOUT\n// ============================================================================\n\n/**\n * Timeout error\n */\nexport class TimeoutExceededError extends Error {\n readonly timeout: number;\n\n constructor(timeout: number) {\n super(`Operation timed out after ${timeout}ms`);\n this.name = \"TimeoutExceededError\";\n this.timeout = timeout;\n }\n}\n\n/**\n * Wrap a function with timeout\n */\nexport function withTimeout<T>(\n fn: () => Promise<T>,\n timeoutMs: number,\n onTimeout?: () => void | never\n): () => Promise<T> {\n return async (): Promise<T> => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n if (onTimeout) {\n try {\n onTimeout();\n } catch (error) {\n reject(error);\n return;\n }\n }\n reject(new TimeoutExceededError(timeoutMs));\n }, timeoutMs);\n });\n\n try {\n return await Promise.race([fn(), timeoutPromise]);\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n };\n}\n\n/**\n * Execute a function with timeout\n */\nexport async function executeWithTimeout<T>(\n fn: () => Promise<T>,\n timeoutMs: number,\n onTimeout?: () => void | never\n): Promise<T> {\n return withTimeout(fn, timeoutMs, onTimeout)();\n}\n\n/**\n * Create a timeout wrapper with preset duration\n */\nexport function createTimeoutWrapper(\n defaultTimeoutMs: number\n): <T>(fn: () => Promise<T>, timeoutMs?: number) => Promise<T> {\n return async <T>(fn: () => Promise<T>, timeoutMs?: number): Promise<T> => {\n return executeWithTimeout(fn, timeoutMs ?? defaultTimeoutMs);\n };\n}\n\n/**\n * Race multiple promises with a timeout\n */\nexport async function raceWithTimeout<T>(\n promises: Promise<T>[],\n timeoutMs: number\n): Promise<T> {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new TimeoutExceededError(timeoutMs));\n }, timeoutMs);\n });\n\n try {\n return await Promise.race([...promises, timeoutPromise]);\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n}\n\n/**\n * Execute with deadline (absolute time)\n */\nexport async function executeWithDeadline<T>(\n fn: () => Promise<T>,\n deadline: Date\n): Promise<T> {\n const now = Date.now();\n const deadlineMs = deadline.getTime();\n const timeoutMs = Math.max(0, deadlineMs - now);\n\n if (timeoutMs === 0) {\n throw new TimeoutExceededError(0);\n }\n\n return executeWithTimeout(fn, timeoutMs);\n}\n","/**\n * @parsrun/service - Embedded RPC Transport\n * Direct function call transport for monolithic/embedded mode\n */\n\nimport type { RpcRequest, RpcResponse, RpcTransport } from \"../../types.js\";\nimport type { RpcServer } from \"../server.js\";\n\n// ============================================================================\n// EMBEDDED TRANSPORT\n// ============================================================================\n\n/**\n * Embedded transport that calls handlers directly\n * Used when services are in the same process\n */\nexport class EmbeddedTransport implements RpcTransport {\n readonly name = \"embedded\";\n private readonly server: RpcServer;\n\n constructor(server: RpcServer) {\n this.server = server;\n }\n\n async call<TInput, TOutput>(request: RpcRequest<TInput>): Promise<RpcResponse<TOutput>> {\n // Direct call to server - no serialization needed\n return this.server.handle<TInput, TOutput>(request);\n }\n\n async close(): Promise<void> {\n // No cleanup needed for embedded transport\n }\n}\n\n/**\n * Create an embedded transport\n */\nexport function createEmbeddedTransport(server: RpcServer): EmbeddedTransport {\n return new EmbeddedTransport(server);\n}\n\n// ============================================================================\n// EMBEDDED TRANSPORT REGISTRY\n// ============================================================================\n\n/**\n * Registry for embedded services\n * Allows services to find each other in embedded mode\n */\nexport class EmbeddedRegistry {\n private static instance: EmbeddedRegistry | null = null;\n private readonly servers: Map<string, RpcServer> = new Map();\n\n private constructor() {}\n\n static getInstance(): EmbeddedRegistry {\n if (!EmbeddedRegistry.instance) {\n EmbeddedRegistry.instance = new EmbeddedRegistry();\n }\n return EmbeddedRegistry.instance;\n }\n\n /**\n * Register a service\n */\n register(name: string, server: RpcServer): void {\n if (this.servers.has(name)) {\n throw new Error(`Service already registered: ${name}`);\n }\n this.servers.set(name, server);\n }\n\n /**\n * Unregister a service\n */\n unregister(name: string): boolean {\n return this.servers.delete(name);\n }\n\n /**\n * Get a service by name\n */\n get(name: string): RpcServer | undefined {\n return this.servers.get(name);\n }\n\n /**\n * Check if a service is registered\n */\n has(name: string): boolean {\n return this.servers.has(name);\n }\n\n /**\n * Get all registered service names\n */\n getServiceNames(): string[] {\n return Array.from(this.servers.keys());\n }\n\n /**\n * Create a transport for a registered service\n */\n createTransport(name: string): EmbeddedTransport {\n const server = this.servers.get(name);\n if (!server) {\n throw new Error(`Service not found: ${name}`);\n }\n return new EmbeddedTransport(server);\n }\n\n /**\n * Clear all registered services\n */\n clear(): void {\n this.servers.clear();\n }\n\n /**\n * Reset the singleton instance (for testing)\n */\n static reset(): void {\n EmbeddedRegistry.instance = null;\n }\n}\n\n/**\n * Get the global embedded registry\n */\nexport function getEmbeddedRegistry(): EmbeddedRegistry {\n return EmbeddedRegistry.getInstance();\n}\n","/**\n * @parsrun/service - Serialization\n * JSON and MessagePack serializers\n */\n\n// ============================================================================\n// SERIALIZER INTERFACE\n// ============================================================================\n\n/**\n * Serializer interface for encoding/decoding data\n */\nexport interface Serializer {\n /** Encode data to string or buffer */\n encode(data: unknown): string | ArrayBuffer;\n /** Decode string or buffer to data */\n decode(raw: string | ArrayBuffer): unknown;\n /** Content type for HTTP headers */\n contentType: string;\n}\n\n// ============================================================================\n// JSON SERIALIZER\n// ============================================================================\n\n/**\n * JSON serializer (default)\n */\nexport const jsonSerializer: Serializer = {\n encode(data: unknown): string {\n return JSON.stringify(data);\n },\n\n decode(raw: string | ArrayBuffer): unknown {\n if (raw instanceof ArrayBuffer) {\n const decoder = new TextDecoder();\n return JSON.parse(decoder.decode(raw));\n }\n return JSON.parse(raw);\n },\n\n contentType: \"application/json\",\n};\n\n// ============================================================================\n// MESSAGEPACK SERIALIZER (Lightweight implementation)\n// ============================================================================\n\n/**\n * Lightweight MessagePack encoder\n * Supports: null, boolean, number, string, array, object\n */\nfunction msgpackEncode(value: unknown): Uint8Array {\n const parts: Uint8Array[] = [];\n\n function encode(val: unknown): void {\n if (val === null || val === undefined) {\n parts.push(new Uint8Array([0xc0])); // nil\n return;\n }\n\n if (typeof val === \"boolean\") {\n parts.push(new Uint8Array([val ? 0xc3 : 0xc2]));\n return;\n }\n\n if (typeof val === \"number\") {\n if (Number.isInteger(val)) {\n if (val >= 0 && val <= 127) {\n // positive fixint\n parts.push(new Uint8Array([val]));\n } else if (val < 0 && val >= -32) {\n // negative fixint\n parts.push(new Uint8Array([val & 0xff]));\n } else if (val >= 0 && val <= 0xff) {\n // uint8\n parts.push(new Uint8Array([0xcc, val]));\n } else if (val >= 0 && val <= 0xffff) {\n // uint16\n parts.push(new Uint8Array([0xcd, (val >> 8) & 0xff, val & 0xff]));\n } else if (val >= 0 && val <= 0xffffffff) {\n // uint32\n parts.push(\n new Uint8Array([\n 0xce,\n (val >> 24) & 0xff,\n (val >> 16) & 0xff,\n (val >> 8) & 0xff,\n val & 0xff,\n ])\n );\n } else if (val >= -128 && val <= 127) {\n // int8\n parts.push(new Uint8Array([0xd0, val & 0xff]));\n } else if (val >= -32768 && val <= 32767) {\n // int16\n parts.push(new Uint8Array([0xd1, (val >> 8) & 0xff, val & 0xff]));\n } else if (val >= -2147483648 && val <= 2147483647) {\n // int32\n parts.push(\n new Uint8Array([\n 0xd2,\n (val >> 24) & 0xff,\n (val >> 16) & 0xff,\n (val >> 8) & 0xff,\n val & 0xff,\n ])\n );\n } else {\n // Fall back to float64 for large integers\n const buffer = new ArrayBuffer(9);\n const view = new DataView(buffer);\n view.setUint8(0, 0xcb);\n view.setFloat64(1, val, false);\n parts.push(new Uint8Array(buffer));\n }\n } else {\n // float64\n const buffer = new ArrayBuffer(9);\n const view = new DataView(buffer);\n view.setUint8(0, 0xcb);\n view.setFloat64(1, val, false);\n parts.push(new Uint8Array(buffer));\n }\n return;\n }\n\n if (typeof val === \"string\") {\n const encoded = new TextEncoder().encode(val);\n const len = encoded.length;\n\n if (len <= 31) {\n // fixstr\n parts.push(new Uint8Array([0xa0 | len]));\n } else if (len <= 0xff) {\n // str8\n parts.push(new Uint8Array([0xd9, len]));\n } else if (len <= 0xffff) {\n // str16\n parts.push(new Uint8Array([0xda, (len >> 8) & 0xff, len & 0xff]));\n } else {\n // str32\n parts.push(\n new Uint8Array([\n 0xdb,\n (len >> 24) & 0xff,\n (len >> 16) & 0xff,\n (len >> 8) & 0xff,\n len & 0xff,\n ])\n );\n }\n parts.push(encoded);\n return;\n }\n\n if (Array.isArray(val)) {\n const len = val.length;\n\n if (len <= 15) {\n // fixarray\n parts.push(new Uint8Array([0x90 | len]));\n } else if (len <= 0xffff) {\n // array16\n parts.push(new Uint8Array([0xdc, (len >> 8) & 0xff, len & 0xff]));\n } else {\n // array32\n parts.push(\n new Uint8Array([\n 0xdd,\n (len >> 24) & 0xff,\n (len >> 16) & 0xff,\n (len >> 8) & 0xff,\n len & 0xff,\n ])\n );\n }\n\n for (const item of val) {\n encode(item);\n }\n return;\n }\n\n if (typeof val === \"object\") {\n const keys = Object.keys(val as object);\n const len = keys.length;\n\n if (len <= 15) {\n // fixmap\n parts.push(new Uint8Array([0x80 | len]));\n } else if (len <= 0xffff) {\n // map16\n parts.push(new Uint8Array([0xde, (len >> 8) & 0xff, len & 0xff]));\n } else {\n // map32\n parts.push(\n new Uint8Array([\n 0xdf,\n (len >> 24) & 0xff,\n (len >> 16) & 0xff,\n (len >> 8) & 0xff,\n len & 0xff,\n ])\n );\n }\n\n for (const key of keys) {\n encode(key);\n encode((val as Record<string, unknown>)[key]);\n }\n return;\n }\n\n // Unsupported type - encode as null\n parts.push(new Uint8Array([0xc0]));\n }\n\n encode(value);\n\n // Merge all parts\n const totalLength = parts.reduce((sum, p) => sum + p.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const part of parts) {\n result.set(part, offset);\n offset += part.length;\n }\n\n return result;\n}\n\n/**\n * Lightweight MessagePack decoder\n */\nfunction msgpackDecode(buffer: Uint8Array): unknown {\n let offset = 0;\n\n function decode(): unknown {\n if (offset >= buffer.length) {\n throw new Error(\"Unexpected end of buffer\");\n }\n\n const byte = buffer[offset++]!;\n\n // Positive fixint (0x00 - 0x7f)\n if (byte <= 0x7f) {\n return byte;\n }\n\n // Negative fixint (0xe0 - 0xff)\n if (byte >= 0xe0) {\n return byte - 256;\n }\n\n // Fixmap (0x80 - 0x8f)\n if (byte >= 0x80 && byte <= 0x8f) {\n const len = byte - 0x80;\n const result: Record<string, unknown> = {};\n for (let i = 0; i < len; i++) {\n const key = decode() as string;\n result[key] = decode();\n }\n return result;\n }\n\n // Fixarray (0x90 - 0x9f)\n if (byte >= 0x90 && byte <= 0x9f) {\n const len = byte - 0x90;\n const result: unknown[] = [];\n for (let i = 0; i < len; i++) {\n result.push(decode());\n }\n return result;\n }\n\n // Fixstr (0xa0 - 0xbf)\n if (byte >= 0xa0 && byte <= 0xbf) {\n const len = byte - 0xa0;\n const str = new TextDecoder().decode(buffer.subarray(offset, offset + len));\n offset += len;\n return str;\n }\n\n switch (byte) {\n case 0xc0: // nil\n return null;\n case 0xc2: // false\n return false;\n case 0xc3: // true\n return true;\n\n case 0xcc: // uint8\n return buffer[offset++];\n case 0xcd: // uint16\n return (buffer[offset++]! << 8) | buffer[offset++]!;\n case 0xce: // uint32\n return (\n ((buffer[offset++]! << 24) >>> 0) +\n (buffer[offset++]! << 16) +\n (buffer[offset++]! << 8) +\n buffer[offset++]!\n );\n\n case 0xd0: // int8\n {\n const val = buffer[offset++]!;\n return val > 127 ? val - 256 : val;\n }\n case 0xd1: // int16\n {\n const val = (buffer[offset++]! << 8) | buffer[offset++]!;\n return val > 32767 ? val - 65536 : val;\n }\n case 0xd2: // int32\n {\n const val =\n (buffer[offset++]! << 24) |\n (buffer[offset++]! << 16) |\n (buffer[offset++]! << 8) |\n buffer[offset++]!;\n return val;\n }\n\n case 0xcb: // float64\n {\n const view = new DataView(buffer.buffer, buffer.byteOffset + offset, 8);\n offset += 8;\n return view.getFloat64(0, false);\n }\n\n case 0xd9: // str8\n {\n const len = buffer[offset++]!;\n const str = new TextDecoder().decode(buffer.subarray(offset, offset + len));\n offset += len;\n return str;\n }\n case 0xda: // str16\n {\n const len = (buffer[offset++]! << 8) | buffer[offset++]!;\n const str = new TextDecoder().decode(buffer.subarray(offset, offset + len));\n offset += len;\n return str;\n }\n case 0xdb: // str32\n {\n const len =\n (buffer[offset++]! << 24) |\n (buffer[offset++]! << 16) |\n (buffer[offset++]! << 8) |\n buffer[offset++]!;\n const str = new TextDecoder().decode(buffer.subarray(offset, offset + len));\n offset += len;\n return str;\n }\n\n case 0xdc: // array16\n {\n const len = (buffer[offset++]! << 8) | buffer[offset++]!;\n const result: unknown[] = [];\n for (let i = 0; i < len; i++) {\n result.push(decode());\n }\n return result;\n }\n case 0xdd: // array32\n {\n const len =\n (buffer[offset++]! << 24) |\n (buffer[offset++]! << 16) |\n (buffer[offset++]! << 8) |\n buffer[offset++]!;\n const result: unknown[] = [];\n for (let i = 0; i < len; i++) {\n result.push(decode());\n }\n return result;\n }\n\n case 0xde: // map16\n {\n const len = (buffer[offset++]! << 8) | buffer[offset++]!;\n const result: Record<string, unknown> = {};\n for (let i = 0; i < len; i++) {\n const key = decode() as string;\n result[key] = decode();\n }\n return result;\n }\n case 0xdf: // map32\n {\n const len =\n (buffer[offset++]! << 24) |\n (buffer[offset++]! << 16) |\n (buffer[offset++]! << 8) |\n buffer[offset++]!;\n const result: Record<string, unknown> = {};\n for (let i = 0; i < len; i++) {\n const key = decode() as string;\n result[key] = decode();\n }\n return result;\n }\n\n default:\n throw new Error(`Unknown MessagePack type: 0x${byte.toString(16)}`);\n }\n }\n\n return decode();\n}\n\n/**\n * MessagePack serializer\n */\nexport const msgpackSerializer: Serializer = {\n encode(data: unknown): ArrayBuffer {\n const encoded = msgpackEncode(data);\n // Create a new ArrayBuffer with the exact bytes from the Uint8Array\n const buffer = new ArrayBuffer(encoded.byteLength);\n new Uint8Array(buffer).set(encoded);\n return buffer;\n },\n\n decode(raw: string | ArrayBuffer): unknown {\n if (typeof raw === \"string\") {\n // If string is passed, assume it's base64 encoded\n const binary = atob(raw);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return msgpackDecode(bytes);\n }\n return msgpackDecode(new Uint8Array(raw));\n },\n\n contentType: \"application/msgpack\",\n};\n\n// ============================================================================\n// SERIALIZER FACTORY\n// ============================================================================\n\n/**\n * Get serializer by format name\n */\nexport function getSerializer(format: \"json\" | \"msgpack\"): Serializer {\n switch (format) {\n case \"json\":\n return jsonSerializer;\n case \"msgpack\":\n return msgpackSerializer;\n default:\n return jsonSerializer;\n }\n}\n\n/**\n * Create a custom serializer\n */\nexport function createSerializer(options: {\n encode: (data: unknown) => string | ArrayBuffer;\n decode: (raw: string | ArrayBuffer) => unknown;\n contentType: string;\n}): Serializer {\n return options;\n}\n","/**\n * @parsrun/service - HTTP RPC Transport\n * HTTP-based transport for distributed services\n */\n\nimport type { RpcRequest, RpcResponse, RpcTransport } from \"../../types.js\";\nimport { type Serializer, jsonSerializer } from \"../../serialization/index.js\";\nimport { TransportError, SerializationError } from \"../errors.js\";\n\n// ============================================================================\n// HTTP TRANSPORT\n// ============================================================================\n\nexport interface HttpTransportOptions {\n /** Base URL of the service */\n baseUrl: string;\n /** Custom serializer (default: JSON) */\n serializer?: Serializer;\n /** Custom headers */\n headers?: Record<string, string>;\n /** Fetch function (for testing or custom implementations) */\n fetch?: typeof globalThis.fetch;\n /** Request timeout in ms */\n timeout?: number;\n}\n\n/**\n * HTTP transport for RPC calls\n */\nexport class HttpTransport implements RpcTransport {\n readonly name = \"http\";\n private readonly baseUrl: string;\n private readonly serializer: Serializer;\n private readonly headers: Record<string, string>;\n private readonly fetchFn: typeof globalThis.fetch;\n private readonly timeout: number;\n\n constructor(options: HttpTransportOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\"); // Remove trailing slash\n this.serializer = options.serializer ?? jsonSerializer;\n this.headers = options.headers ?? {};\n this.fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);\n this.timeout = options.timeout ?? 30_000;\n }\n\n async call<TInput, TOutput>(request: RpcRequest<TInput>): Promise<RpcResponse<TOutput>> {\n const url = `${this.baseUrl}/rpc`;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n // Serialize request\n let body: string | ArrayBuffer;\n try {\n body = this.serializer.encode(request);\n } catch (error) {\n throw new SerializationError(\n \"Failed to serialize request\",\n error instanceof Error ? error : undefined\n );\n }\n\n // Make HTTP request\n const response = await this.fetchFn(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": this.serializer.contentType,\n Accept: this.serializer.contentType,\n \"X-Request-ID\": request.id,\n \"X-Service\": request.service,\n \"X-Method\": request.method,\n \"X-Method-Type\": request.type,\n ...(request.version ? { \"X-Service-Version\": request.version } : {}),\n ...(request.traceContext\n ? {\n traceparent: formatTraceparent(request.traceContext),\n ...(request.traceContext.traceState\n ? { tracestate: request.traceContext.traceState }\n : {}),\n }\n : {}),\n ...this.headers,\n },\n body: body instanceof ArrayBuffer ? body : body,\n signal: controller.signal,\n });\n\n // Parse response\n let responseData: RpcResponse<TOutput>;\n try {\n const contentType = response.headers.get(\"Content-Type\") ?? \"\";\n if (contentType.includes(\"msgpack\")) {\n const buffer = await response.arrayBuffer();\n responseData = this.serializer.decode(buffer) as RpcResponse<TOutput>;\n } else {\n const text = await response.text();\n responseData = this.serializer.decode(text) as RpcResponse<TOutput>;\n }\n } catch (error) {\n throw new SerializationError(\n \"Failed to deserialize response\",\n error instanceof Error ? error : undefined\n );\n }\n\n return responseData;\n } catch (error) {\n if (error instanceof SerializationError) {\n throw error;\n }\n\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new TransportError(`Request timeout after ${this.timeout}ms`);\n }\n throw new TransportError(`HTTP request failed: ${error.message}`, error);\n }\n\n throw new TransportError(\"Unknown transport error\");\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async close(): Promise<void> {\n // No persistent connection to close\n }\n}\n\n/**\n * Create an HTTP transport\n */\nexport function createHttpTransport(options: HttpTransportOptions): HttpTransport {\n return new HttpTransport(options);\n}\n\n// ============================================================================\n// TRACE CONTEXT HELPERS\n// ============================================================================\n\nimport type { TraceContext } from \"../../types.js\";\n\n/**\n * Format trace context as W3C traceparent header\n */\nfunction formatTraceparent(ctx: TraceContext): string {\n const flags = ctx.traceFlags.toString(16).padStart(2, \"0\");\n return `00-${ctx.traceId}-${ctx.spanId}-${flags}`;\n}\n\n/**\n * Parse W3C traceparent header\n */\nexport function parseTraceparent(header: string): TraceContext | null {\n const parts = header.split(\"-\");\n if (parts.length !== 4) {\n return null;\n }\n\n const [version, traceId, spanId, flags] = parts;\n if (version !== \"00\" || !traceId || !spanId || !flags) {\n return null;\n }\n\n if (traceId.length !== 32 || spanId.length !== 16 || flags.length !== 2) {\n return null;\n }\n\n return {\n traceId,\n spanId,\n traceFlags: parseInt(flags, 16),\n };\n}\n\n// ============================================================================\n// HTTP SERVER ADAPTER\n// ============================================================================\n\nimport type { RpcServer } from \"../server.js\";\n\n/**\n * Create HTTP request handler for RPC server\n * Can be used with Hono, Express, or any HTTP framework\n */\nexport function createHttpHandler(server: RpcServer) {\n return async (request: Request): Promise<Response> => {\n try {\n // Parse request body\n const contentType = request.headers.get(\"Content-Type\") ?? \"application/json\";\n let body: unknown;\n\n if (contentType.includes(\"msgpack\")) {\n const buffer = await request.arrayBuffer();\n // For msgpack, we'd need to decode - using JSON for now\n body = JSON.parse(new TextDecoder().decode(buffer));\n } else {\n body = await request.json();\n }\n\n const rpcRequest = body as RpcRequest;\n\n // Parse trace context\n const traceparent = request.headers.get(\"traceparent\");\n if (traceparent) {\n const traceContext = parseTraceparent(traceparent);\n if (traceContext) {\n const tracestate = request.headers.get(\"tracestate\");\n if (tracestate) {\n traceContext.traceState = tracestate;\n }\n rpcRequest.traceContext = traceContext;\n }\n }\n\n // Handle request\n const response = await server.handle(rpcRequest);\n\n // Return response\n return new Response(JSON.stringify(response), {\n status: response.success ? 200 : getHttpStatus(response.error?.code),\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Request-ID\": rpcRequest.id,\n },\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n return new Response(\n JSON.stringify({\n success: false,\n error: {\n code: \"INTERNAL_ERROR\",\n message,\n },\n }),\n {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n }\n );\n }\n };\n}\n\n/**\n * Map error code to HTTP status\n */\nfunction getHttpStatus(code?: string): number {\n switch (code) {\n case \"METHOD_NOT_FOUND\":\n case \"SERVICE_NOT_FOUND\":\n return 404;\n case \"VERSION_MISMATCH\":\n case \"VALIDATION_ERROR\":\n case \"SERIALIZATION_ERROR\":\n return 400;\n case \"UNAUTHORIZED\":\n return 401;\n case \"FORBIDDEN\":\n return 403;\n case \"TIMEOUT\":\n return 504;\n case \"CIRCUIT_OPEN\":\n case \"BULKHEAD_REJECTED\":\n return 503;\n default:\n return 500;\n }\n}\n","/**\n * @parsrun/service - Memory Event Transport\n * In-memory event transport for embedded mode and testing\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { createLogger } from \"@parsrun/core\";\nimport type {\n ParsEvent,\n EventTransport,\n EventHandler,\n EventHandlerOptions,\n Unsubscribe,\n} from \"../../types.js\";\nimport { EventHandlerRegistry, type EventHandlerRegistryOptions } from \"../handler.js\";\n\n// ============================================================================\n// MEMORY EVENT TRANSPORT\n// ============================================================================\n\nexport interface MemoryEventTransportOptions {\n /** Logger */\n logger?: Logger;\n /** Process events synchronously (default: false) */\n sync?: boolean;\n /** Default handler options */\n defaultHandlerOptions?: EventHandlerOptions;\n}\n\n/**\n * In-memory event transport\n * Events are processed immediately without persistence\n */\nexport class MemoryEventTransport implements EventTransport {\n readonly name = \"memory\";\n private readonly registry: EventHandlerRegistry;\n private readonly logger: Logger;\n private readonly sync: boolean;\n private readonly pendingEvents: ParsEvent[] = [];\n private processing = false;\n\n constructor(options: MemoryEventTransportOptions = {}) {\n this.logger = options.logger ?? createLogger({ name: \"memory-transport\" });\n this.sync = options.sync ?? false;\n\n const registryOptions: EventHandlerRegistryOptions = {\n logger: this.logger,\n };\n if (options.defaultHandlerOptions) {\n registryOptions.defaultOptions = options.defaultHandlerOptions;\n }\n this.registry = new EventHandlerRegistry(registryOptions);\n }\n\n /**\n * Emit an event\n */\n async emit<T>(event: ParsEvent<T>): Promise<void> {\n this.logger.debug(`Event emitted: ${event.type}`, {\n eventId: event.id,\n tenantId: event.parstenantid,\n });\n\n if (this.sync) {\n // Process synchronously\n await this.registry.handle(event);\n } else {\n // Queue for async processing\n this.pendingEvents.push(event);\n this.processQueue();\n }\n }\n\n /**\n * Subscribe to events\n */\n subscribe(\n eventType: string,\n handler: EventHandler,\n options?: EventHandlerOptions\n ): Unsubscribe {\n return this.registry.register(eventType, handler, options);\n }\n\n /**\n * Process pending events asynchronously\n */\n private async processQueue(): Promise<void> {\n if (this.processing) return;\n this.processing = true;\n\n try {\n while (this.pendingEvents.length > 0) {\n const event = this.pendingEvents.shift();\n if (event) {\n await this.registry.handle(event);\n }\n }\n } finally {\n this.processing = false;\n }\n }\n\n /**\n * Wait for all pending events to be processed\n */\n async flush(): Promise<void> {\n while (this.pendingEvents.length > 0 || this.processing) {\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n }\n\n /**\n * Get pending event count\n */\n get pendingCount(): number {\n return this.pendingEvents.length;\n }\n\n /**\n * Get registered patterns\n */\n getPatterns(): string[] {\n return this.registry.getPatterns();\n }\n\n /**\n * Clear all subscriptions\n */\n clear(): void {\n this.registry.clear();\n this.pendingEvents.length = 0;\n }\n\n /**\n * Close the transport\n */\n async close(): Promise<void> {\n await this.flush();\n this.clear();\n }\n}\n\n/**\n * Create a memory event transport\n */\nexport function createMemoryEventTransport(\n options?: MemoryEventTransportOptions\n): MemoryEventTransport {\n return new MemoryEventTransport(options);\n}\n\n// ============================================================================\n// GLOBAL EVENT BUS (for embedded multi-service)\n// ============================================================================\n\n/**\n * Global event bus for communication between embedded services\n */\nexport class GlobalEventBus {\n private static instance: GlobalEventBus | null = null;\n private readonly transports: Map<string, MemoryEventTransport> = new Map();\n private readonly logger: Logger;\n\n private constructor() {\n this.logger = createLogger({ name: \"global-event-bus\" });\n }\n\n static getInstance(): GlobalEventBus {\n if (!GlobalEventBus.instance) {\n GlobalEventBus.instance = new GlobalEventBus();\n }\n return GlobalEventBus.instance;\n }\n\n /**\n * Register a service's event transport\n */\n register(serviceName: string, transport: MemoryEventTransport): void {\n if (this.transports.has(serviceName)) {\n throw new Error(`Service already registered: ${serviceName}`);\n }\n this.transports.set(serviceName, transport);\n this.logger.debug(`Service registered: ${serviceName}`);\n }\n\n /**\n * Unregister a service\n */\n unregister(serviceName: string): boolean {\n const deleted = this.transports.delete(serviceName);\n if (deleted) {\n this.logger.debug(`Service unregistered: ${serviceName}`);\n }\n return deleted;\n }\n\n /**\n * Broadcast an event to all services (except source)\n */\n async broadcast(event: ParsEvent, excludeSource?: string): Promise<void> {\n const promises: Promise<void>[] = [];\n\n for (const [name, transport] of this.transports) {\n if (name !== excludeSource) {\n promises.push(transport.emit(event));\n }\n }\n\n await Promise.allSettled(promises);\n }\n\n /**\n * Send an event to a specific service\n */\n async send(serviceName: string, event: ParsEvent): Promise<void> {\n const transport = this.transports.get(serviceName);\n if (!transport) {\n this.logger.warn(`Target service not found: ${serviceName}`, {\n eventId: event.id,\n });\n return;\n }\n\n await transport.emit(event);\n }\n\n /**\n * Get all registered service names\n */\n getServices(): string[] {\n return Array.from(this.transports.keys());\n }\n\n /**\n * Clear all registrations\n */\n clear(): void {\n this.transports.clear();\n }\n\n /**\n * Reset singleton (for testing)\n */\n static reset(): void {\n GlobalEventBus.instance = null;\n }\n}\n\n/**\n * Get the global event bus\n */\nexport function getGlobalEventBus(): GlobalEventBus {\n return GlobalEventBus.getInstance();\n}\n","/**\n * @parsrun/service - Event Handler\n * Event handler registration and execution\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { createLogger } from \"@parsrun/core\";\nimport type {\n ParsEvent,\n EventHandler,\n EventHandlerContext,\n EventHandlerOptions,\n Unsubscribe,\n} from \"../types.js\";\nimport { matchEventType } from \"./format.js\";\nimport type { DeadLetterQueue } from \"./dead-letter.js\";\n\n// ============================================================================\n// EVENT HANDLER REGISTRY\n// ============================================================================\n\n/**\n * Resolved handler options with required fields\n */\nexport interface ResolvedHandlerOptions {\n retries: number;\n backoff: \"linear\" | \"exponential\";\n maxDelay: number;\n onExhausted: \"alert\" | \"log\" | \"discard\";\n deadLetter?: string;\n}\n\nexport interface HandlerRegistration {\n /** Event type pattern (supports wildcards) */\n pattern: string;\n /** Handler function */\n handler: EventHandler;\n /** Handler options */\n options: ResolvedHandlerOptions;\n}\n\nexport interface EventHandlerRegistryOptions {\n /** Logger */\n logger?: Logger;\n /** Dead letter queue */\n deadLetterQueue?: DeadLetterQueue;\n /** Default handler options */\n defaultOptions?: Partial<EventHandlerOptions>;\n}\n\n/**\n * Registry for event handlers\n */\nexport class EventHandlerRegistry {\n private readonly handlers: Map<string, HandlerRegistration[]> = new Map();\n private readonly logger: Logger;\n private readonly deadLetterQueue?: DeadLetterQueue;\n private readonly defaultOptions: ResolvedHandlerOptions;\n\n constructor(options: EventHandlerRegistryOptions = {}) {\n this.logger = options.logger ?? createLogger({ name: \"event-handler\" });\n if (options.deadLetterQueue) {\n this.deadLetterQueue = options.deadLetterQueue;\n }\n const defaultOpts: ResolvedHandlerOptions = {\n retries: options.defaultOptions?.retries ?? 3,\n backoff: options.defaultOptions?.backoff ?? \"exponential\",\n maxDelay: options.defaultOptions?.maxDelay ?? 30_000,\n onExhausted: options.defaultOptions?.onExhausted ?? \"log\",\n };\n if (options.defaultOptions?.deadLetter) {\n defaultOpts.deadLetter = options.defaultOptions.deadLetter;\n }\n this.defaultOptions = defaultOpts;\n }\n\n /**\n * Register an event handler\n */\n register(\n pattern: string,\n handler: EventHandler,\n options?: EventHandlerOptions\n ): Unsubscribe {\n const registration: HandlerRegistration = {\n pattern,\n handler,\n options: {\n ...this.defaultOptions,\n ...options,\n },\n };\n\n const handlers = this.handlers.get(pattern) ?? [];\n handlers.push(registration);\n this.handlers.set(pattern, handlers);\n\n this.logger.debug(`Handler registered for pattern: ${pattern}`);\n\n // Return unsubscribe function\n return () => {\n const currentHandlers = this.handlers.get(pattern);\n if (currentHandlers) {\n const index = currentHandlers.indexOf(registration);\n if (index !== -1) {\n currentHandlers.splice(index, 1);\n if (currentHandlers.length === 0) {\n this.handlers.delete(pattern);\n }\n this.logger.debug(`Handler unregistered for pattern: ${pattern}`);\n }\n }\n };\n }\n\n /**\n * Handle an event\n */\n async handle(event: ParsEvent): Promise<void> {\n const matchingHandlers = this.getMatchingHandlers(event.type);\n\n if (matchingHandlers.length === 0) {\n this.logger.debug(`No handlers for event type: ${event.type}`, {\n eventId: event.id,\n });\n return;\n }\n\n this.logger.debug(`Handling event: ${event.type}`, {\n eventId: event.id,\n handlerCount: matchingHandlers.length,\n });\n\n // Execute handlers in parallel\n const results = await Promise.allSettled(\n matchingHandlers.map((reg) => this.executeHandler(event, reg))\n );\n\n // Log failures\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n if (result?.status === \"rejected\") {\n this.logger.error(\n `Handler failed for ${event.type}`,\n result.reason as Error,\n { eventId: event.id, pattern: matchingHandlers[i]?.pattern }\n );\n }\n }\n }\n\n /**\n * Execute a single handler with retry logic\n */\n private async executeHandler(\n event: ParsEvent,\n registration: HandlerRegistration\n ): Promise<void> {\n const { handler, options } = registration;\n const maxAttempts = options.retries + 1;\n let lastError: Error | undefined;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const context: EventHandlerContext = {\n logger: this.logger.child({\n eventId: event.id,\n pattern: registration.pattern,\n attempt,\n }),\n attempt,\n maxAttempts,\n isRetry: attempt > 1,\n };\n\n // Add trace context if available\n if (event.parstracecontext) {\n const traceCtx = parseTraceContext(event.parstracecontext);\n if (traceCtx) {\n context.traceContext = traceCtx;\n }\n }\n\n await handler(event, context);\n return; // Success\n } catch (error) {\n lastError = error as Error;\n\n if (attempt < maxAttempts) {\n const delay = this.calculateBackoff(attempt, options);\n this.logger.warn(\n `Handler failed, retrying in ${delay}ms`,\n { eventId: event.id, attempt, maxAttempts }\n );\n await sleep(delay);\n }\n }\n }\n\n // All retries exhausted\n await this.handleExhausted(event, registration, lastError!);\n }\n\n /**\n * Calculate backoff delay\n */\n private calculateBackoff(\n attempt: number,\n options: ResolvedHandlerOptions\n ): number {\n const baseDelay = 100;\n\n if (options.backoff === \"exponential\") {\n return Math.min(baseDelay * Math.pow(2, attempt - 1), options.maxDelay);\n }\n\n // Linear\n return Math.min(baseDelay * attempt, options.maxDelay);\n }\n\n /**\n * Handle exhausted retries\n */\n private async handleExhausted(\n event: ParsEvent,\n registration: HandlerRegistration,\n error: Error\n ): Promise<void> {\n const { options } = registration;\n\n // Send to dead letter queue\n if (options.deadLetter && this.deadLetterQueue) {\n await this.deadLetterQueue.add({\n event,\n error: error.message,\n pattern: registration.pattern,\n attempts: options.retries + 1,\n });\n }\n\n // Handle based on onExhausted option\n switch (options.onExhausted) {\n case \"alert\":\n this.logger.error(\n `[ALERT] Event handler exhausted all retries`,\n error,\n {\n eventId: event.id,\n eventType: event.type,\n pattern: registration.pattern,\n }\n );\n break;\n case \"discard\":\n this.logger.debug(`Event discarded after exhausted retries`, {\n eventId: event.id,\n });\n break;\n case \"log\":\n default:\n this.logger.warn(`Event handler exhausted all retries`, {\n eventId: event.id,\n error: error.message,\n });\n }\n }\n\n /**\n * Get handlers matching an event type\n */\n private getMatchingHandlers(eventType: string): HandlerRegistration[] {\n const matching: HandlerRegistration[] = [];\n\n for (const [pattern, handlers] of this.handlers) {\n if (matchEventType(eventType, pattern)) {\n matching.push(...handlers);\n }\n }\n\n return matching;\n }\n\n /**\n * Get all registered patterns\n */\n getPatterns(): string[] {\n return Array.from(this.handlers.keys());\n }\n\n /**\n * Check if a pattern has handlers\n */\n hasHandlers(pattern: string): boolean {\n return this.handlers.has(pattern);\n }\n\n /**\n * Clear all handlers\n */\n clear(): void {\n this.handlers.clear();\n }\n}\n\n/**\n * Create an event handler registry\n */\nexport function createEventHandlerRegistry(\n options?: EventHandlerRegistryOptions\n): EventHandlerRegistry {\n return new EventHandlerRegistry(options);\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction parseTraceContext(traceparent: string): {\n traceId: string;\n spanId: string;\n traceFlags: number;\n} | undefined {\n const parts = traceparent.split(\"-\");\n if (parts.length !== 4) return undefined;\n\n const [, traceId, spanId, flags] = parts;\n if (!traceId || !spanId || !flags) return undefined;\n\n return {\n traceId,\n spanId,\n traceFlags: parseInt(flags, 16),\n };\n}\n","/**\n * @parsrun/service - Event Format\n * CloudEvents and compact event format utilities\n */\n\nimport { generateId } from \"@parsrun/core\";\nimport type { ParsEvent, CompactEvent, TraceContext } from \"../types.js\";\n\n// ============================================================================\n// EVENT CREATION\n// ============================================================================\n\nexport interface CreateEventOptions<T = unknown> {\n /** Event type (e.g., \"subscription.created\") */\n type: string;\n /** Source service */\n source: string;\n /** Event data */\n data: T;\n /** Optional event ID (auto-generated if not provided) */\n id?: string;\n /** Optional subject */\n subject?: string;\n /** Tenant ID */\n tenantId?: string;\n /** Request ID for correlation */\n requestId?: string;\n /** Trace context */\n traceContext?: TraceContext;\n /** Delivery guarantee */\n delivery?: \"at-most-once\" | \"at-least-once\";\n}\n\n/**\n * Create a CloudEvents-compatible event\n */\nexport function createEvent<T = unknown>(options: CreateEventOptions<T>): ParsEvent<T> {\n const event: ParsEvent<T> = {\n specversion: \"1.0\",\n type: options.type,\n source: options.source,\n id: options.id ?? generateId(),\n time: new Date().toISOString(),\n datacontenttype: \"application/json\",\n data: options.data,\n };\n\n if (options.subject) event.subject = options.subject;\n if (options.tenantId) event.parstenantid = options.tenantId;\n if (options.requestId) event.parsrequestid = options.requestId;\n if (options.traceContext) event.parstracecontext = formatTraceContext(options.traceContext);\n if (options.delivery) event.parsdelivery = options.delivery;\n\n return event;\n}\n\n// ============================================================================\n// FORMAT CONVERSION\n// ============================================================================\n\n/**\n * Convert to full CloudEvents format\n */\nexport function toCloudEvent<T>(event: ParsEvent<T>): ParsEvent<T> {\n return { ...event };\n}\n\n/**\n * Convert to compact internal format\n */\nexport function toCompactEvent<T>(event: ParsEvent<T>): CompactEvent<T> {\n const compact: CompactEvent<T> = {\n e: event.type,\n s: event.source,\n i: event.id,\n t: new Date(event.time).getTime(),\n d: event.data,\n };\n\n if (event.parstracecontext) compact.ctx = event.parstracecontext;\n if (event.parstenantid) compact.tid = event.parstenantid;\n\n return compact;\n}\n\n/**\n * Convert from compact format to CloudEvents\n */\nexport function fromCompactEvent<T>(compact: CompactEvent<T>, source?: string): ParsEvent<T> {\n const event: ParsEvent<T> = {\n specversion: \"1.0\",\n type: compact.e,\n source: source ?? compact.s,\n id: compact.i,\n time: new Date(compact.t).toISOString(),\n datacontenttype: \"application/json\",\n data: compact.d,\n };\n\n if (compact.ctx) event.parstracecontext = compact.ctx;\n if (compact.tid) event.parstenantid = compact.tid;\n\n return event;\n}\n\n// ============================================================================\n// EVENT TYPE UTILITIES\n// ============================================================================\n\n/**\n * Format full event type with source prefix\n *\n * @example\n * formatEventType('payments', 'subscription.created')\n * // Returns: 'com.pars.payments.subscription.created'\n */\nexport function formatEventType(source: string, type: string): string {\n return `com.pars.${source}.${type}`;\n}\n\n/**\n * Parse event type to extract source and type\n *\n * @example\n * parseEventType('com.pars.payments.subscription.created')\n * // Returns: { source: 'payments', type: 'subscription.created' }\n */\nexport function parseEventType(fullType: string): { source: string; type: string } | null {\n const prefix = \"com.pars.\";\n if (!fullType.startsWith(prefix)) {\n // Try to parse as simple type (source.type)\n const parts = fullType.split(\".\");\n if (parts.length >= 2) {\n const [source, ...rest] = parts;\n return { source: source!, type: rest.join(\".\") };\n }\n return null;\n }\n\n const withoutPrefix = fullType.slice(prefix.length);\n const dotIndex = withoutPrefix.indexOf(\".\");\n if (dotIndex === -1) {\n return null;\n }\n\n return {\n source: withoutPrefix.slice(0, dotIndex),\n type: withoutPrefix.slice(dotIndex + 1),\n };\n}\n\n/**\n * Check if event type matches a pattern\n * Supports wildcards: * matches one segment, ** matches multiple segments\n *\n * @example\n * matchEventType('subscription.created', 'subscription.*') // true\n * matchEventType('payment.invoice.paid', 'payment.**') // true\n * matchEventType('subscription.created', 'payment.*') // false\n */\nexport function matchEventType(type: string, pattern: string): boolean {\n if (pattern === \"*\" || pattern === \"**\") {\n return true;\n }\n\n const typeParts = type.split(\".\");\n const patternParts = pattern.split(\".\");\n\n let ti = 0;\n let pi = 0;\n\n while (ti < typeParts.length && pi < patternParts.length) {\n const pp = patternParts[pi];\n\n if (pp === \"**\") {\n // ** matches rest of type\n if (pi === patternParts.length - 1) {\n return true;\n }\n // Try to match remaining pattern\n for (let i = ti; i <= typeParts.length; i++) {\n const remaining = typeParts.slice(i).join(\".\");\n const remainingPattern = patternParts.slice(pi + 1).join(\".\");\n if (matchEventType(remaining, remainingPattern)) {\n return true;\n }\n }\n return false;\n }\n\n if (pp === \"*\") {\n // * matches single segment\n ti++;\n pi++;\n continue;\n }\n\n if (pp !== typeParts[ti]) {\n return false;\n }\n\n ti++;\n pi++;\n }\n\n return ti === typeParts.length && pi === patternParts.length;\n}\n\n// ============================================================================\n// TRACE CONTEXT HELPERS\n// ============================================================================\n\n/**\n * Format trace context to W3C traceparent string\n */\nfunction formatTraceContext(ctx: TraceContext): string {\n const flags = ctx.traceFlags.toString(16).padStart(2, \"0\");\n return `00-${ctx.traceId}-${ctx.spanId}-${flags}`;\n}\n\n/**\n * Parse W3C traceparent string to trace context\n */\nexport function parseTraceContext(traceparent: string): TraceContext | null {\n const parts = traceparent.split(\"-\");\n if (parts.length !== 4) {\n return null;\n }\n\n const [version, traceId, spanId, flags] = parts;\n if (version !== \"00\" || !traceId || !spanId || !flags) {\n return null;\n }\n\n return {\n traceId,\n spanId,\n traceFlags: parseInt(flags, 16),\n };\n}\n\n// ============================================================================\n// VALIDATION\n// ============================================================================\n\n/**\n * Validate CloudEvents structure\n */\nexport function validateEvent(event: unknown): event is ParsEvent {\n if (!event || typeof event !== \"object\") {\n return false;\n }\n\n const e = event as Record<string, unknown>;\n\n // Required fields\n if (e[\"specversion\"] !== \"1.0\") return false;\n if (typeof e[\"type\"] !== \"string\" || (e[\"type\"] as string).length === 0) return false;\n if (typeof e[\"source\"] !== \"string\" || (e[\"source\"] as string).length === 0) return false;\n if (typeof e[\"id\"] !== \"string\" || (e[\"id\"] as string).length === 0) return false;\n if (typeof e[\"time\"] !== \"string\") return false;\n\n return true;\n}\n\n/**\n * Validate compact event structure\n */\nexport function validateCompactEvent(event: unknown): event is CompactEvent {\n if (!event || typeof event !== \"object\") {\n return false;\n }\n\n const e = event as Record<string, unknown>;\n\n if (typeof e[\"e\"] !== \"string\" || (e[\"e\"] as string).length === 0) return false;\n if (typeof e[\"s\"] !== \"string\" || (e[\"s\"] as string).length === 0) return false;\n if (typeof e[\"i\"] !== \"string\" || (e[\"i\"] as string).length === 0) return false;\n if (typeof e[\"t\"] !== \"number\") return false;\n\n return true;\n}\n","/**\n * @parsrun/service - Tracer\n * High-level tracing API\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { createLogger } from \"@parsrun/core\";\nimport type { Span, TraceContext, SpanKind, SpanAttributeValue } from \"../types.js\";\nimport type { TracingConfig } from \"../types.js\";\nimport {\n TraceContextManager,\n shouldSample,\n type Sampler,\n} from \"./context.js\";\nimport { SpanManager, SpanAttributes } from \"./spans.js\";\nimport type { SpanExporter } from \"./exporters.js\";\nimport { ConsoleExporter } from \"./exporters.js\";\n\n// ============================================================================\n// TRACER\n// ============================================================================\n\nexport interface TracerOptions {\n /** Service name */\n serviceName: string;\n /** Service version */\n serviceVersion?: string;\n /** Tracing config */\n config?: TracingConfig;\n /** Span exporter */\n exporter?: SpanExporter;\n /** Logger */\n logger?: Logger;\n}\n\n/**\n * Main tracer class for distributed tracing\n */\nexport class Tracer {\n private readonly serviceName: string;\n private readonly serviceVersion: string;\n private readonly sampler: Sampler;\n private readonly exporter: SpanExporter;\n private readonly logger: Logger;\n private readonly contextManager: TraceContextManager;\n private readonly spanManager: SpanManager;\n private readonly enabled: boolean;\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: TracerOptions) {\n this.serviceName = options.serviceName;\n this.serviceVersion = options.serviceVersion ?? \"1.0.0\";\n this.enabled = options.config?.enabled ?? true;\n this.sampler = options.config?.sampler ?? { ratio: 0.1 };\n this.exporter = options.exporter ?? new ConsoleExporter();\n this.logger = options.logger ?? createLogger({ name: `tracer:${options.serviceName}` });\n this.contextManager = new TraceContextManager();\n this.spanManager = new SpanManager();\n\n // Auto-flush spans periodically\n if (this.enabled) {\n this.flushTimer = setInterval(() => this.flush(), 5000);\n }\n }\n\n /**\n * Start a new span\n */\n startSpan(\n name: string,\n options?: {\n kind?: SpanKind;\n parent?: TraceContext;\n attributes?: Record<string, SpanAttributeValue>;\n }\n ): Span | null {\n if (!this.enabled) return null;\n\n // Get parent from options or current context\n const parent = options?.parent ?? this.contextManager.current();\n\n // Check sampling\n if (!parent && !shouldSample(this.sampler)) {\n return null;\n }\n\n const spanOptions: Parameters<typeof this.spanManager.startSpan>[0] = {\n name,\n kind: options?.kind ?? \"internal\",\n attributes: {\n [SpanAttributes.SERVICE_NAME]: this.serviceName,\n [SpanAttributes.SERVICE_VERSION]: this.serviceVersion,\n ...options?.attributes,\n },\n };\n\n if (parent) {\n spanOptions.parent = parent;\n }\n\n const span = this.spanManager.startSpan(spanOptions);\n\n return span;\n }\n\n /**\n * End a span\n */\n endSpan(span: Span | null, error?: Error): void {\n if (!span) return;\n\n if (error) {\n this.spanManager.recordException(span, error);\n }\n\n this.spanManager.endSpan(span, error ? \"error\" : \"ok\");\n }\n\n /**\n * Run a function with automatic span creation\n */\n async trace<T>(\n name: string,\n fn: (span: Span | null) => Promise<T>,\n options?: {\n kind?: SpanKind;\n attributes?: Record<string, SpanAttributeValue>;\n }\n ): Promise<T> {\n const span = this.startSpan(name, options);\n\n if (!span) {\n return fn(null);\n }\n\n try {\n const result = await this.contextManager.run(span.traceContext, () => fn(span));\n this.endSpan(span);\n return result;\n } catch (error) {\n this.endSpan(span, error as Error);\n throw error;\n }\n }\n\n /**\n * Add attribute to current span\n */\n setAttribute(key: string, value: SpanAttributeValue): void {\n const ctx = this.contextManager.current();\n if (!ctx) return;\n\n const span = this.spanManager.getSpan(ctx.spanId);\n if (span) {\n this.spanManager.setAttribute(span, key, value);\n }\n }\n\n /**\n * Add event to current span\n */\n addEvent(name: string, attributes?: Record<string, SpanAttributeValue>): void {\n const ctx = this.contextManager.current();\n if (!ctx) return;\n\n const span = this.spanManager.getSpan(ctx.spanId);\n if (span) {\n this.spanManager.addEvent(span, name, attributes);\n }\n }\n\n /**\n * Get current trace context\n */\n currentContext(): TraceContext | undefined {\n return this.contextManager.current();\n }\n\n /**\n * Get context manager for advanced use\n */\n getContextManager(): TraceContextManager {\n return this.contextManager;\n }\n\n /**\n * Extract trace context from incoming request\n */\n extract(headers: Headers | Record<string, string>): TraceContext | undefined {\n return this.contextManager.fromHeaders(headers);\n }\n\n /**\n * Inject trace context into outgoing request headers\n */\n inject(ctx?: TraceContext): Record<string, string> {\n const context = ctx ?? this.contextManager.current();\n if (!context) return {};\n return this.contextManager.toHeaders(context);\n }\n\n /**\n * Flush completed spans to exporter\n */\n async flush(): Promise<void> {\n const spans = this.spanManager.flush();\n if (spans.length > 0) {\n try {\n await this.exporter.export(spans);\n } catch (error) {\n this.logger.error(\"Failed to export spans\", error as Error);\n }\n }\n }\n\n /**\n * Shutdown tracer\n */\n async shutdown(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n await this.flush();\n await this.exporter.shutdown();\n this.spanManager.clear();\n this.contextManager.clear();\n }\n}\n\n/**\n * Create a tracer\n */\nexport function createTracer(options: TracerOptions): Tracer {\n return new Tracer(options);\n}\n\n// ============================================================================\n// GLOBAL TRACER\n// ============================================================================\n\nlet globalTracer: Tracer | null = null;\n\n/**\n * Get the global tracer instance\n */\nexport function getGlobalTracer(): Tracer | null {\n return globalTracer;\n}\n\n/**\n * Set the global tracer instance\n */\nexport function setGlobalTracer(tracer: Tracer): void {\n globalTracer = tracer;\n}\n\n/**\n * Reset the global tracer (for testing)\n */\nexport function resetGlobalTracer(): void {\n globalTracer = null;\n}\n\n// ============================================================================\n// MIDDLEWARE HELPERS\n// ============================================================================\n\n/**\n * Create HTTP server tracing middleware\n */\nexport function createTracingMiddleware(tracer: Tracer) {\n return async (\n request: Request,\n next: () => Promise<Response>\n ): Promise<Response> => {\n // Extract trace context from incoming request\n const parentCtx = tracer.extract(request.headers);\n\n // Start server span\n const url = new URL(request.url);\n const spanOpts: {\n kind: SpanKind;\n parent?: TraceContext;\n attributes: Record<string, SpanAttributeValue>;\n } = {\n kind: \"server\",\n attributes: {\n [SpanAttributes.HTTP_METHOD]: request.method,\n [SpanAttributes.HTTP_URL]: request.url,\n },\n };\n if (parentCtx) {\n spanOpts.parent = parentCtx;\n }\n const span = tracer.startSpan(`${request.method} ${url.pathname}`, spanOpts);\n\n if (!span) {\n return next();\n }\n\n try {\n const response = await tracer.getContextManager().run(span.traceContext, next);\n\n tracer.endSpan(span);\n span.attributes[SpanAttributes.HTTP_STATUS_CODE] = response.status;\n\n return response;\n } catch (error) {\n tracer.endSpan(span, error as Error);\n throw error;\n }\n };\n}\n\n/**\n * Create RPC tracing helpers\n */\nexport function createRpcTracing(tracer: Tracer) {\n return {\n /**\n * Trace an outgoing RPC call\n */\n async traceCall<T>(\n service: string,\n method: string,\n fn: () => Promise<T>\n ): Promise<T> {\n return tracer.trace(\n `rpc.${service}.${method}`,\n fn,\n {\n kind: \"client\",\n attributes: {\n [SpanAttributes.RPC_SYSTEM]: \"pars\",\n [SpanAttributes.RPC_SERVICE]: service,\n [SpanAttributes.RPC_METHOD]: method,\n },\n }\n );\n },\n\n /**\n * Trace an incoming RPC request\n */\n async traceHandler<T>(\n service: string,\n method: string,\n fn: () => Promise<T>,\n parentCtx?: TraceContext\n ): Promise<T> {\n const handlerOpts: {\n kind: SpanKind;\n parent?: TraceContext;\n attributes: Record<string, SpanAttributeValue>;\n } = {\n kind: \"server\",\n attributes: {\n [SpanAttributes.RPC_SYSTEM]: \"pars\",\n [SpanAttributes.RPC_SERVICE]: service,\n [SpanAttributes.RPC_METHOD]: method,\n },\n };\n if (parentCtx) {\n handlerOpts.parent = parentCtx;\n }\n const span = tracer.startSpan(`rpc.${service}.${method}`, handlerOpts);\n\n if (!span) {\n return fn();\n }\n\n try {\n const result = await tracer.getContextManager().run(span.traceContext, fn);\n tracer.endSpan(span);\n return result;\n } catch (error) {\n tracer.endSpan(span, error as Error);\n throw error;\n }\n },\n };\n}\n","/**\n * @parsrun/service - Trace Exporters\n * Console and OTLP exporters for spans\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { createLogger } from \"@parsrun/core\";\nimport type { Span } from \"../types.js\";\nimport { spanToLogObject, getSpanDuration } from \"./spans.js\";\n\n// ============================================================================\n// EXPORTER INTERFACE\n// ============================================================================\n\nexport interface SpanExporter {\n /** Exporter name */\n readonly name: string;\n /** Export spans */\n export(spans: Span[]): Promise<void>;\n /** Shutdown exporter */\n shutdown(): Promise<void>;\n}\n\nexport interface ExporterOptions {\n /** Logger */\n logger?: Logger;\n}\n\n// ============================================================================\n// CONSOLE EXPORTER\n// ============================================================================\n\nexport interface ConsoleExporterOptions extends ExporterOptions {\n /** Pretty print (default: true in dev) */\n pretty?: boolean;\n /** Include attributes (default: true) */\n includeAttributes?: boolean;\n /** Include events (default: true) */\n includeEvents?: boolean;\n}\n\n/**\n * Console exporter for development/debugging\n */\nexport class ConsoleExporter implements SpanExporter {\n readonly name = \"console\";\n private readonly logger: Logger;\n private readonly pretty: boolean;\n private readonly includeAttributes: boolean;\n private readonly includeEvents: boolean;\n\n constructor(options: ConsoleExporterOptions = {}) {\n this.logger = options.logger ?? createLogger({ name: \"trace-exporter\" });\n this.pretty = options.pretty ?? true;\n this.includeAttributes = options.includeAttributes ?? true;\n this.includeEvents = options.includeEvents ?? true;\n }\n\n async export(spans: Span[]): Promise<void> {\n for (const span of spans) {\n const duration = getSpanDuration(span);\n const status = span.status === \"error\" ? \"ERROR\" : span.status === \"ok\" ? \"OK\" : \"UNSET\";\n\n if (this.pretty) {\n const indent = span.parentSpanId ? \" └─\" : \"──\";\n const statusIcon = span.status === \"error\" ? \"✗\" : span.status === \"ok\" ? \"✓\" : \"○\";\n const durationStr = duration !== undefined ? `${duration}ms` : \"?ms\";\n\n console.log(\n `${indent} ${statusIcon} [${span.kind}] ${span.name} (${durationStr}) trace=${span.traceContext.traceId.slice(0, 8)}`\n );\n\n if (this.includeAttributes && Object.keys(span.attributes).length > 0) {\n console.log(` attributes:`, span.attributes);\n }\n\n if (this.includeEvents && span.events.length > 0) {\n for (const event of span.events) {\n console.log(` event: ${event.name}`, event.attributes ?? \"\");\n }\n }\n } else {\n const logObj = spanToLogObject(span);\n this.logger.info(`Span: ${span.name}`, {\n ...logObj,\n status,\n durationMs: duration,\n });\n }\n }\n }\n\n async shutdown(): Promise<void> {\n // No cleanup needed\n }\n}\n\n/**\n * Create a console exporter\n */\nexport function createConsoleExporter(options?: ConsoleExporterOptions): ConsoleExporter {\n return new ConsoleExporter(options);\n}\n\n// ============================================================================\n// OTLP EXPORTER\n// ============================================================================\n\nexport interface OtlpExporterOptions extends ExporterOptions {\n /** OTLP endpoint URL */\n endpoint: string;\n /** Service name */\n serviceName: string;\n /** Service version */\n serviceVersion?: string;\n /** Custom headers */\n headers?: Record<string, string>;\n /** Timeout in ms */\n timeout?: number;\n /** Batch size */\n batchSize?: number;\n /** Flush interval in ms */\n flushInterval?: number;\n}\n\n/**\n * OTLP exporter for production tracing\n */\nexport class OtlpExporter implements SpanExporter {\n readonly name = \"otlp\";\n private readonly endpoint: string;\n private readonly serviceName: string;\n private readonly serviceVersion: string;\n private readonly headers: Record<string, string>;\n private readonly timeout: number;\n private readonly batchSize: number;\n private readonly flushInterval: number;\n private readonly logger: Logger;\n private readonly buffer: Span[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: OtlpExporterOptions) {\n this.endpoint = options.endpoint.replace(/\\/$/, \"\");\n this.serviceName = options.serviceName;\n this.serviceVersion = options.serviceVersion ?? \"1.0.0\";\n this.headers = options.headers ?? {};\n this.timeout = options.timeout ?? 10_000;\n this.batchSize = options.batchSize ?? 100;\n this.flushInterval = options.flushInterval ?? 5_000;\n this.logger = options.logger ?? createLogger({ name: \"otlp-exporter\" });\n\n // Start flush timer\n this.flushTimer = setInterval(() => this.flush(), this.flushInterval);\n }\n\n async export(spans: Span[]): Promise<void> {\n this.buffer.push(...spans);\n\n if (this.buffer.length >= this.batchSize) {\n await this.flush();\n }\n }\n\n private async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n\n const spansToExport = this.buffer.splice(0, this.batchSize);\n\n try {\n const payload = this.buildOtlpPayload(spansToExport);\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(`${this.endpoint}/v1/traces`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...this.headers,\n },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`OTLP export failed: ${response.status} ${response.statusText}`);\n }\n\n this.logger.debug(`Exported ${spansToExport.length} spans to OTLP`);\n } finally {\n clearTimeout(timeoutId);\n }\n } catch (error) {\n this.logger.error(`Failed to export spans to OTLP`, error as Error);\n // Put spans back in buffer for retry\n this.buffer.unshift(...spansToExport);\n }\n }\n\n private buildOtlpPayload(spans: Span[]): OtlpTracePayload {\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: \"service.name\", value: { stringValue: this.serviceName } },\n { key: \"service.version\", value: { stringValue: this.serviceVersion } },\n ],\n },\n scopeSpans: [\n {\n scope: {\n name: \"@parsrun/service\",\n version: \"0.1.0\",\n },\n spans: spans.map((span) => this.convertSpan(span)),\n },\n ],\n },\n ],\n };\n }\n\n private convertSpan(span: Span): OtlpSpan {\n const otlpSpan: OtlpSpan = {\n traceId: span.traceContext.traceId,\n spanId: span.traceContext.spanId,\n name: span.name,\n kind: this.convertSpanKind(span.kind),\n startTimeUnixNano: String(span.startTime * 1_000_000),\n attributes: Object.entries(span.attributes).map(([key, value]) => ({\n key,\n value: this.convertAttributeValue(value),\n })),\n events: span.events.map((event) => ({\n name: event.name,\n timeUnixNano: String(event.time * 1_000_000),\n attributes: event.attributes\n ? Object.entries(event.attributes).map(([key, value]) => ({\n key,\n value: this.convertAttributeValue(value),\n }))\n : [],\n })),\n status: {\n code: span.status === \"error\" ? 2 : span.status === \"ok\" ? 1 : 0,\n },\n };\n\n if (span.parentSpanId) {\n otlpSpan.parentSpanId = span.parentSpanId;\n }\n if (span.endTime) {\n otlpSpan.endTimeUnixNano = String(span.endTime * 1_000_000);\n }\n\n return otlpSpan;\n }\n\n private convertSpanKind(kind: string): number {\n switch (kind) {\n case \"internal\":\n return 1;\n case \"server\":\n return 2;\n case \"client\":\n return 3;\n case \"producer\":\n return 4;\n case \"consumer\":\n return 5;\n default:\n return 0;\n }\n }\n\n private convertAttributeValue(value: unknown): OtlpAttributeValue {\n if (typeof value === \"string\") {\n return { stringValue: value };\n }\n if (typeof value === \"number\") {\n if (Number.isInteger(value)) {\n return { intValue: String(value) };\n }\n return { doubleValue: value };\n }\n if (typeof value === \"boolean\") {\n return { boolValue: value };\n }\n if (Array.isArray(value)) {\n return {\n arrayValue: {\n values: value.map((v) => this.convertAttributeValue(v)),\n },\n };\n }\n return { stringValue: String(value) };\n }\n\n async shutdown(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n await this.flush();\n }\n}\n\n/**\n * Create an OTLP exporter\n */\nexport function createOtlpExporter(options: OtlpExporterOptions): OtlpExporter {\n return new OtlpExporter(options);\n}\n\n// ============================================================================\n// OTLP TYPES\n// ============================================================================\n\ninterface OtlpTracePayload {\n resourceSpans: Array<{\n resource: {\n attributes: OtlpAttribute[];\n };\n scopeSpans: Array<{\n scope: {\n name: string;\n version: string;\n };\n spans: OtlpSpan[];\n }>;\n }>;\n}\n\ninterface OtlpSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n name: string;\n kind: number;\n startTimeUnixNano: string;\n endTimeUnixNano?: string;\n attributes: OtlpAttribute[];\n events: Array<{\n name: string;\n timeUnixNano: string;\n attributes: OtlpAttribute[];\n }>;\n status: {\n code: number;\n };\n}\n\ninterface OtlpAttribute {\n key: string;\n value: OtlpAttributeValue;\n}\n\ninterface OtlpAttributeValue {\n stringValue?: string;\n intValue?: string;\n doubleValue?: number;\n boolValue?: boolean;\n arrayValue?: {\n values: OtlpAttributeValue[];\n };\n}\n"],"mappings":"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","names":["generateId","createLogger","generateId","sleep","sleep","createLogger","createLogger","createLogger","generateId"]}
1
+ {"version":3,"sources":["../src/client.ts","../src/config.ts","../src/rpc/client.ts","../src/rpc/errors.ts","../src/resilience/circuit-breaker.ts","../src/resilience/bulkhead.ts","../src/resilience/retry.ts","../src/resilience/timeout.ts","../src/rpc/transports/embedded.ts","../src/serialization/index.ts","../src/rpc/transports/http.ts","../src/events/transports/memory.ts","../src/events/handler.ts","../src/events/format.ts","../src/tracing/tracer.ts","../src/tracing/exporters.ts"],"sourcesContent":["/**\n * @parsrun/service - Service Client\n * High-level client API for consuming services\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { generateId } from \"@parsrun/core\";\nimport type {\n ServiceDefinition,\n ServiceClient,\n ServiceClientOptions,\n ServiceConfig,\n RpcTransport,\n EventTransport,\n EventHandler,\n EventHandlerOptions,\n Unsubscribe,\n ParsEvent,\n QueryInput,\n QueryOutput,\n MutationInput,\n MutationOutput,\n EventData,\n} from \"./types.js\";\nimport { mergeConfig } from \"./config.js\";\nimport { RpcClient } from \"./rpc/client.js\";\nimport { getEmbeddedRegistry } from \"./rpc/transports/embedded.js\";\nimport { createHttpTransport } from \"./rpc/transports/http.js\";\nimport { createMemoryEventTransport, getGlobalEventBus } from \"./events/transports/memory.js\";\nimport { Tracer, getGlobalTracer } from \"./tracing/tracer.js\";\n\n// ============================================================================\n// SERVICE CLIENT IMPLEMENTATION\n// ============================================================================\n\n/**\n * Full service client with RPC and Events\n */\nclass ServiceClientImpl<TDef extends ServiceDefinition> implements ServiceClient<TDef> {\n readonly name: string;\n private readonly rpcClient: RpcClient;\n private readonly eventTransport: EventTransport;\n private readonly config: Required<ServiceConfig>;\n private readonly tracer: Tracer | null;\n\n constructor(\n definition: TDef,\n rpcTransport: RpcTransport,\n eventTransport: EventTransport,\n config: ServiceConfig,\n _logger?: Logger\n ) {\n this.name = definition.name;\n this.config = mergeConfig(config);\n this.tracer = getGlobalTracer();\n\n this.rpcClient = new RpcClient({\n service: definition.name,\n transport: rpcTransport,\n config: this.config,\n });\n\n this.eventTransport = eventTransport;\n }\n\n /**\n * Execute a query\n */\n async query<K extends keyof TDef[\"queries\"]>(\n method: K,\n input: QueryInput<TDef[\"queries\"], K>\n ): Promise<QueryOutput<TDef[\"queries\"], K>> {\n const methodName = String(method);\n const traceContext = this.tracer?.currentContext();\n\n // Trace if available\n if (this.tracer && traceContext) {\n return this.tracer.trace(\n `rpc.${this.name}.${methodName}`,\n async () => {\n return this.rpcClient.query(methodName, input, {\n traceContext,\n }) as Promise<QueryOutput<TDef[\"queries\"], K>>;\n },\n { kind: \"client\" }\n ) as Promise<QueryOutput<TDef[\"queries\"], K>>;\n }\n\n return this.rpcClient.query(methodName, input) as Promise<QueryOutput<TDef[\"queries\"], K>>;\n }\n\n /**\n * Execute a mutation\n */\n async mutate<K extends keyof TDef[\"mutations\"]>(\n method: K,\n input: MutationInput<TDef[\"mutations\"], K>\n ): Promise<MutationOutput<TDef[\"mutations\"], K>> {\n const methodName = String(method);\n const traceContext = this.tracer?.currentContext();\n\n // Trace if available\n if (this.tracer && traceContext) {\n return this.tracer.trace(\n `rpc.${this.name}.${methodName}`,\n async () => {\n return this.rpcClient.mutate(methodName, input, {\n traceContext,\n }) as Promise<MutationOutput<TDef[\"mutations\"], K>>;\n },\n { kind: \"client\" }\n ) as Promise<MutationOutput<TDef[\"mutations\"], K>>;\n }\n\n return this.rpcClient.mutate(methodName, input) as Promise<MutationOutput<TDef[\"mutations\"], K>>;\n }\n\n /**\n * Emit an event\n */\n async emit<K extends keyof NonNullable<TDef[\"events\"]>[\"emits\"]>(\n eventType: K,\n data: EventData<NonNullable<TDef[\"events\"]>[\"emits\"], K>\n ): Promise<void> {\n const type = String(eventType);\n const traceContext = this.tracer?.currentContext();\n\n const event: ParsEvent = {\n specversion: \"1.0\",\n type,\n source: this.name,\n id: generateId(),\n time: new Date().toISOString(),\n data,\n };\n\n // Add trace context if available\n if (traceContext) {\n event.parstracecontext = `00-${traceContext.traceId}-${traceContext.spanId}-01`;\n }\n\n await this.eventTransport.emit(event);\n }\n\n /**\n * Subscribe to events\n */\n on<T = unknown>(\n eventType: string,\n handler: EventHandler<T>,\n options?: EventHandlerOptions\n ): Unsubscribe {\n return this.eventTransport.subscribe(eventType, handler as EventHandler, options);\n }\n\n /**\n * Get circuit breaker state\n */\n getCircuitState(): \"closed\" | \"open\" | \"half-open\" | null {\n return this.rpcClient.getCircuitState();\n }\n\n /**\n * Close the client\n */\n async close(): Promise<void> {\n await this.rpcClient.close();\n await this.eventTransport.close?.();\n }\n}\n\n// ============================================================================\n// USE SERVICE FACTORY\n// ============================================================================\n\n/**\n * Create a service client\n *\n * @example\n * ```typescript\n * // Embedded mode (same process)\n * const payments = useService('payments');\n *\n * // HTTP mode (remote service)\n * const payments = useService('payments', {\n * mode: 'http',\n * baseUrl: 'https://payments.example.com',\n * });\n *\n * // Cloudflare binding mode\n * const payments = useService('payments', {\n * mode: 'binding',\n * binding: env.PAYMENTS,\n * });\n * ```\n */\nexport function useService<TDef extends ServiceDefinition = ServiceDefinition>(\n serviceName: string,\n options: ServiceClientOptions = {}\n): ServiceClient<TDef> {\n const mode = options.mode ?? \"embedded\";\n const config = options.config ?? {};\n\n let rpcTransport: RpcTransport;\n let eventTransport: EventTransport;\n\n switch (mode) {\n case \"embedded\": {\n // Use embedded registry\n const registry = getEmbeddedRegistry();\n if (!registry.has(serviceName)) {\n throw new Error(\n `Service not found in embedded registry: ${serviceName}. ` +\n `Make sure the service is registered before using it.`\n );\n }\n rpcTransport = registry.createTransport(serviceName);\n\n // Use global event bus\n const eventBus = getGlobalEventBus();\n const services = eventBus.getServices();\n if (services.includes(serviceName)) {\n // Get existing transport from bus\n eventTransport = createMemoryEventTransport();\n } else {\n eventTransport = createMemoryEventTransport();\n }\n break;\n }\n\n case \"http\": {\n if (!options.baseUrl) {\n throw new Error(\"baseUrl is required for HTTP mode\");\n }\n rpcTransport = createHttpTransport({\n baseUrl: options.baseUrl,\n });\n // Events over HTTP would need webhook or polling - use memory for now\n eventTransport = createMemoryEventTransport();\n break;\n }\n\n case \"binding\": {\n if (!options.binding) {\n throw new Error(\"binding is required for binding mode\");\n }\n // Cloudflare service binding transport\n rpcTransport = createBindingTransport(serviceName, options.binding);\n eventTransport = createMemoryEventTransport();\n break;\n }\n\n default:\n throw new Error(`Unknown service client mode: ${mode}`);\n }\n\n // Use custom transports if provided\n if (options.rpcTransport) {\n rpcTransport = options.rpcTransport;\n }\n if (options.eventTransport) {\n eventTransport = options.eventTransport;\n }\n\n // Create a minimal definition for the client\n const definition: ServiceDefinition = {\n name: serviceName,\n version: \"1.x\",\n };\n\n return new ServiceClientImpl<TDef>(\n definition as TDef,\n rpcTransport,\n eventTransport,\n config\n );\n}\n\n/**\n * Create a typed service client from a service definition.\n * Provides full type safety for queries, mutations, and events.\n *\n * @param definition - The service definition\n * @param options - Client configuration options\n * @returns A typed service client\n *\n * @example\n * ```typescript\n * const payments = useTypedService(paymentsServiceDefinition, {\n * mode: 'http',\n * baseUrl: 'https://payments.example.com',\n * });\n * // Full type safety for all operations\n * const sub = await payments.query('getSubscription', { subscriptionId: '123' });\n * ```\n */\nexport function useTypedService<TDef extends ServiceDefinition>(\n definition: TDef,\n options: ServiceClientOptions = {}\n): ServiceClient<TDef> {\n return useService<TDef>(definition.name, options);\n}\n\n// ============================================================================\n// BINDING TRANSPORT HELPER\n// ============================================================================\n\nimport type { Fetcher, RpcRequest, RpcResponse } from \"./types.js\";\n\n/**\n * Create RPC transport for Cloudflare service binding\n */\nfunction createBindingTransport(_serviceName: string, binding: Fetcher): RpcTransport {\n return {\n name: \"binding\",\n async call<TInput, TOutput>(request: RpcRequest<TInput>) {\n const response = await binding.fetch(\"http://internal/rpc\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Request-ID\": request.id,\n \"X-Service\": request.service,\n \"X-Method\": request.method,\n },\n body: JSON.stringify(request),\n });\n\n return response.json() as Promise<RpcResponse<TOutput>>;\n },\n async close() {\n // No cleanup needed for bindings\n },\n };\n}\n\n// ============================================================================\n// SERVICE REGISTRY CLIENT\n// ============================================================================\n\n/**\n * Service registry for managing multiple service clients\n */\nexport class ServiceRegistry {\n private readonly clients: Map<string, ServiceClient> = new Map();\n private readonly config: ServiceConfig;\n\n constructor(config?: ServiceConfig) {\n this.config = config ?? {};\n }\n\n /**\n * Get or create a service client\n */\n get<TDef extends ServiceDefinition = ServiceDefinition>(\n serviceName: string,\n options?: ServiceClientOptions\n ): ServiceClient<TDef> {\n let client = this.clients.get(serviceName);\n if (!client) {\n client = useService<TDef>(serviceName, {\n ...options,\n config: { ...this.config, ...options?.config },\n });\n this.clients.set(serviceName, client);\n }\n return client as ServiceClient<TDef>;\n }\n\n /**\n * Close all clients\n */\n async closeAll(): Promise<void> {\n const closePromises = Array.from(this.clients.values()).map((client) => {\n if (\"close\" in client && typeof client.close === \"function\") {\n return (client as { close: () => Promise<void> }).close();\n }\n return Promise.resolve();\n });\n await Promise.all(closePromises);\n this.clients.clear();\n }\n}\n\n/**\n * Create a service registry for managing multiple service clients.\n *\n * @param config - Optional shared configuration for all clients\n * @returns A new service registry instance\n *\n * @example\n * ```typescript\n * const registry = createServiceRegistry({ tracing: { enabled: true } });\n * const payments = registry.get('payments');\n * const users = registry.get('users');\n * // Later, clean up all connections\n * await registry.closeAll();\n * ```\n */\nexport function createServiceRegistry(config?: ServiceConfig): ServiceRegistry {\n return new ServiceRegistry(config);\n}\n","/**\n * @parsrun/service - Configuration\n * Default configuration and config utilities\n */\n\nimport type {\n ServiceConfig,\n EventFormatConfig,\n SerializationConfig,\n TracingConfig,\n VersioningConfig,\n ResilienceConfig,\n DeadLetterConfig,\n} from \"./types.js\";\n\n// ============================================================================\n// DEFAULT CONFIGURATIONS\n// ============================================================================\n\n/**\n * Default event format configuration.\n * Uses CloudEvents format with compact internal communication.\n */\nexport const DEFAULT_EVENT_CONFIG: Required<EventFormatConfig> = {\n format: \"cloudevents\",\n internalCompact: true,\n};\n\n/**\n * Default serialization configuration.\n * Uses JSON format for data encoding.\n */\nexport const DEFAULT_SERIALIZATION_CONFIG: Required<SerializationConfig> = {\n format: \"json\",\n};\n\n/**\n * Default tracing configuration.\n * Enables tracing with 10% sampling ratio and console exporter.\n */\nexport const DEFAULT_TRACING_CONFIG: Required<TracingConfig> = {\n enabled: true,\n sampler: { ratio: 0.1 },\n exporter: \"console\",\n endpoint: \"\",\n serviceName: \"pars-service\",\n};\n\n/**\n * Default versioning configuration.\n * Uses header-based versioning with \"1.x\" as the default version.\n */\nexport const DEFAULT_VERSIONING_CONFIG: Required<VersioningConfig> = {\n strategy: \"header\",\n defaultVersion: \"1.x\",\n};\n\n/**\n * Default resilience configuration.\n * Configures circuit breaker, bulkhead, timeout, and retry settings.\n */\nexport const DEFAULT_RESILIENCE_CONFIG: Required<ResilienceConfig> = {\n circuitBreaker: {\n enabled: true,\n failureThreshold: 5,\n resetTimeout: 30_000,\n successThreshold: 2,\n },\n bulkhead: {\n maxConcurrent: 100,\n maxQueue: 50,\n },\n timeout: 5_000,\n retry: {\n attempts: 3,\n backoff: \"exponential\",\n initialDelay: 100,\n maxDelay: 10_000,\n },\n};\n\n/**\n * Default dead letter queue configuration.\n * Enables DLQ with 30-day retention and alerting at 10 messages.\n */\nexport const DEFAULT_DEAD_LETTER_CONFIG: Required<DeadLetterConfig> = {\n enabled: true,\n retention: \"30d\",\n onFail: \"alert\",\n alertThreshold: 10,\n};\n\n/**\n * Default complete service configuration.\n * Combines all default sub-configurations.\n */\nexport const DEFAULT_SERVICE_CONFIG: Required<ServiceConfig> = {\n events: DEFAULT_EVENT_CONFIG,\n serialization: DEFAULT_SERIALIZATION_CONFIG,\n tracing: DEFAULT_TRACING_CONFIG,\n versioning: DEFAULT_VERSIONING_CONFIG,\n resilience: DEFAULT_RESILIENCE_CONFIG,\n deadLetter: DEFAULT_DEAD_LETTER_CONFIG,\n};\n\n// ============================================================================\n// CONFIG UTILITIES\n// ============================================================================\n\n/**\n * Merge user config with defaults.\n * Deep merges the user configuration with default values.\n *\n * @param userConfig - Optional partial service configuration\n * @returns Complete service configuration with all required fields\n */\nexport function mergeConfig(userConfig?: Partial<ServiceConfig>): Required<ServiceConfig> {\n if (!userConfig) {\n return { ...DEFAULT_SERVICE_CONFIG };\n }\n\n return {\n events: {\n ...DEFAULT_EVENT_CONFIG,\n ...userConfig.events,\n },\n serialization: {\n ...DEFAULT_SERIALIZATION_CONFIG,\n ...userConfig.serialization,\n },\n tracing: {\n ...DEFAULT_TRACING_CONFIG,\n ...userConfig.tracing,\n },\n versioning: {\n ...DEFAULT_VERSIONING_CONFIG,\n ...userConfig.versioning,\n },\n resilience: {\n ...DEFAULT_RESILIENCE_CONFIG,\n ...userConfig.resilience,\n circuitBreaker: {\n ...DEFAULT_RESILIENCE_CONFIG.circuitBreaker,\n ...userConfig.resilience?.circuitBreaker,\n },\n bulkhead: {\n ...DEFAULT_RESILIENCE_CONFIG.bulkhead,\n ...userConfig.resilience?.bulkhead,\n },\n retry: {\n ...DEFAULT_RESILIENCE_CONFIG.retry,\n ...userConfig.resilience?.retry,\n },\n },\n deadLetter: {\n ...DEFAULT_DEAD_LETTER_CONFIG,\n ...userConfig.deadLetter,\n },\n };\n}\n\n/**\n * Create configuration optimized for development.\n * Enables full tracing, disables circuit breaker, and uses longer timeouts.\n *\n * @param overrides - Optional configuration overrides\n * @returns Complete service configuration for development\n */\nexport function createDevConfig(overrides?: Partial<ServiceConfig>): Required<ServiceConfig> {\n return mergeConfig({\n tracing: {\n enabled: true,\n sampler: \"always\",\n exporter: \"console\",\n },\n resilience: {\n circuitBreaker: { enabled: false },\n timeout: 30_000,\n },\n ...overrides,\n });\n}\n\n/**\n * Create configuration optimized for production.\n * Uses 10% sampling ratio, OTLP exporter, and enables circuit breaker.\n *\n * @param overrides - Optional configuration overrides\n * @returns Complete service configuration for production\n */\nexport function createProdConfig(overrides?: Partial<ServiceConfig>): Required<ServiceConfig> {\n return mergeConfig({\n tracing: {\n enabled: true,\n sampler: { ratio: 0.1 },\n exporter: \"otlp\",\n },\n resilience: {\n circuitBreaker: { enabled: true },\n timeout: 5_000,\n },\n ...overrides,\n });\n}\n\n/**\n * Validate service configuration.\n * Checks for valid ranges and values in the configuration.\n *\n * @param config - Service configuration to validate\n * @returns Object containing validation result and any error messages\n */\nexport function validateConfig(config: ServiceConfig): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Validate tracing\n if (config.tracing?.sampler && typeof config.tracing.sampler === \"object\") {\n const ratio = config.tracing.sampler.ratio;\n if (ratio < 0 || ratio > 1) {\n errors.push(\"tracing.sampler.ratio must be between 0 and 1\");\n }\n }\n\n // Validate resilience\n if (config.resilience?.timeout !== undefined && config.resilience.timeout < 0) {\n errors.push(\"resilience.timeout must be non-negative\");\n }\n\n if (config.resilience?.circuitBreaker?.failureThreshold !== undefined) {\n if (config.resilience.circuitBreaker.failureThreshold < 1) {\n errors.push(\"resilience.circuitBreaker.failureThreshold must be at least 1\");\n }\n }\n\n if (config.resilience?.bulkhead?.maxConcurrent !== undefined) {\n if (config.resilience.bulkhead.maxConcurrent < 1) {\n errors.push(\"resilience.bulkhead.maxConcurrent must be at least 1\");\n }\n }\n\n if (config.resilience?.retry?.attempts !== undefined) {\n if (config.resilience.retry.attempts < 0) {\n errors.push(\"resilience.retry.attempts must be non-negative\");\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n","/**\n * @parsrun/service - RPC Client\n * Client for making RPC calls to services\n */\n\nimport { generateId } from \"@parsrun/core\";\nimport type {\n RpcRequest,\n RpcTransport,\n RpcMetadata,\n TraceContext,\n ServiceConfig,\n} from \"../types.js\";\nimport { mergeConfig } from \"../config.js\";\nimport { TimeoutError, toRpcError } from \"./errors.js\";\nimport {\n CircuitBreaker,\n Bulkhead,\n withRetry,\n withTimeout,\n} from \"../resilience/index.js\";\n\n// ============================================================================\n// RPC CLIENT\n// ============================================================================\n\n/**\n * Options for creating an RPC client.\n */\nexport interface RpcClientOptions {\n /** Service name */\n service: string;\n /** RPC transport */\n transport: RpcTransport;\n /** Service configuration */\n config?: ServiceConfig;\n /** Default metadata for all requests */\n defaultMetadata?: RpcMetadata;\n}\n\n/**\n * RPC Client for making service calls\n */\nexport class RpcClient {\n private readonly service: string;\n private readonly transport: RpcTransport;\n private readonly config: Required<ServiceConfig>;\n private readonly defaultMetadata: RpcMetadata;\n private readonly circuitBreaker: CircuitBreaker | null;\n private readonly bulkhead: Bulkhead | null;\n\n constructor(options: RpcClientOptions) {\n this.service = options.service;\n this.transport = options.transport;\n this.config = mergeConfig(options.config);\n this.defaultMetadata = options.defaultMetadata ?? {};\n\n // Initialize circuit breaker\n const cbConfig = this.config.resilience?.circuitBreaker;\n if (\n cbConfig &&\n cbConfig.enabled &&\n cbConfig.failureThreshold !== undefined &&\n cbConfig.resetTimeout !== undefined &&\n cbConfig.successThreshold !== undefined\n ) {\n this.circuitBreaker = new CircuitBreaker({\n failureThreshold: cbConfig.failureThreshold,\n resetTimeout: cbConfig.resetTimeout,\n successThreshold: cbConfig.successThreshold,\n });\n } else {\n this.circuitBreaker = null;\n }\n\n // Initialize bulkhead\n const bhConfig = this.config.resilience?.bulkhead;\n if (bhConfig && bhConfig.maxConcurrent !== undefined && bhConfig.maxQueue !== undefined) {\n this.bulkhead = new Bulkhead({\n maxConcurrent: bhConfig.maxConcurrent,\n maxQueue: bhConfig.maxQueue,\n });\n } else {\n this.bulkhead = null;\n }\n }\n\n /**\n * Execute a query\n */\n async query<TInput, TOutput>(\n method: string,\n input: TInput,\n options?: CallOptions\n ): Promise<TOutput> {\n return this.call<TInput, TOutput>(\"query\", method, input, options);\n }\n\n /**\n * Execute a mutation\n */\n async mutate<TInput, TOutput>(\n method: string,\n input: TInput,\n options?: CallOptions\n ): Promise<TOutput> {\n return this.call<TInput, TOutput>(\"mutation\", method, input, options);\n }\n\n /**\n * Internal call implementation\n */\n private async call<TInput, TOutput>(\n type: \"query\" | \"mutation\",\n method: string,\n input: TInput,\n options?: CallOptions\n ): Promise<TOutput> {\n const request: RpcRequest<TInput> = {\n id: generateId(),\n service: this.service,\n method,\n type,\n input,\n metadata: {\n ...this.defaultMetadata,\n ...options?.metadata,\n },\n };\n\n const version = options?.version ?? this.config.versioning.defaultVersion;\n if (version) {\n request.version = version;\n }\n if (options?.traceContext) {\n request.traceContext = options.traceContext;\n }\n\n const timeout = options?.timeout ?? this.config.resilience.timeout ?? 30_000;\n const retryConfig = options?.retry ?? this.config.resilience.retry;\n\n // Build the execution chain\n let execute = async (): Promise<TOutput> => {\n const response = await this.transport.call<TInput, TOutput>(request);\n\n if (!response.success) {\n const error = toRpcError(\n new Error(response.error?.message ?? \"Unknown error\")\n );\n throw error;\n }\n\n return response.output as TOutput;\n };\n\n // Wrap with timeout\n execute = withTimeout(execute, timeout, () => {\n throw new TimeoutError(this.service, method, timeout);\n });\n\n // Wrap with retry\n const attempts = retryConfig?.attempts ?? 0;\n if (attempts > 0) {\n execute = withRetry(execute, {\n attempts,\n backoff: retryConfig?.backoff ?? \"exponential\",\n initialDelay: retryConfig?.initialDelay ?? 100,\n maxDelay: retryConfig?.maxDelay ?? 5000,\n shouldRetry: (error) => {\n if (error instanceof Error && \"retryable\" in error) {\n return (error as { retryable: boolean }).retryable;\n }\n return false;\n },\n });\n }\n\n // Wrap with circuit breaker\n if (this.circuitBreaker) {\n const cb = this.circuitBreaker;\n const originalExecute = execute;\n execute = async () => {\n return cb.execute(originalExecute);\n };\n }\n\n // Wrap with bulkhead\n if (this.bulkhead) {\n const bh = this.bulkhead;\n const originalExecute = execute;\n execute = async () => {\n return bh.execute(originalExecute);\n };\n }\n\n return execute();\n }\n\n /**\n * Get circuit breaker state\n */\n getCircuitState(): \"closed\" | \"open\" | \"half-open\" | null {\n return this.circuitBreaker?.state ?? null;\n }\n\n /**\n * Get bulkhead stats\n */\n getBulkheadStats(): { concurrent: number; queued: number } | null {\n if (!this.bulkhead) return null;\n return {\n concurrent: this.bulkhead.concurrent,\n queued: this.bulkhead.queued,\n };\n }\n\n /**\n * Close the client and release resources\n */\n async close(): Promise<void> {\n await this.transport.close?.();\n }\n}\n\n/**\n * Options for individual RPC calls.\n * Allows overriding default client settings per-call.\n */\nexport interface CallOptions {\n /** Timeout in ms */\n timeout?: number;\n /** Version requirement */\n version?: string;\n /** Trace context */\n traceContext?: TraceContext;\n /** Request metadata */\n metadata?: RpcMetadata;\n /** Retry configuration override */\n retry?: {\n attempts?: number;\n backoff?: \"linear\" | \"exponential\";\n initialDelay?: number;\n maxDelay?: number;\n };\n}\n\n/**\n * Create an RPC client for making service calls.\n *\n * @param options - Client configuration options\n * @returns A new RPC client instance\n *\n * @example\n * ```typescript\n * const client = createRpcClient({\n * service: 'payments',\n * transport: httpTransport,\n * config: { resilience: { timeout: 5000 } },\n * });\n * const result = await client.query('getSubscription', { id: '123' });\n * ```\n */\nexport function createRpcClient(options: RpcClientOptions): RpcClient {\n return new RpcClient(options);\n}\n","/**\n * @parsrun/service - RPC Errors\n */\n\nimport { ParsError } from \"@parsrun/core\";\n\n/**\n * Base RPC error\n */\nexport class RpcError extends ParsError {\n public readonly retryable: boolean;\n public readonly retryAfter?: number;\n\n constructor(\n message: string,\n code: string,\n statusCode: number = 500,\n options?: {\n retryable?: boolean;\n retryAfter?: number;\n details?: Record<string, unknown>;\n }\n ) {\n super(message, code, statusCode, options?.details);\n this.name = \"RpcError\";\n this.retryable = options?.retryable ?? false;\n if (options?.retryAfter !== undefined) {\n this.retryAfter = options.retryAfter;\n }\n }\n}\n\n/**\n * Service not found error\n */\nexport class ServiceNotFoundError extends RpcError {\n constructor(serviceName: string) {\n super(`Service not found: ${serviceName}`, \"SERVICE_NOT_FOUND\", 404, {\n retryable: false,\n details: { service: serviceName },\n });\n this.name = \"ServiceNotFoundError\";\n }\n}\n\n/**\n * Method not found error\n */\nexport class MethodNotFoundError extends RpcError {\n constructor(serviceName: string, methodName: string) {\n super(\n `Method not found: ${serviceName}.${methodName}`,\n \"METHOD_NOT_FOUND\",\n 404,\n {\n retryable: false,\n details: { service: serviceName, method: methodName },\n }\n );\n this.name = \"MethodNotFoundError\";\n }\n}\n\n/**\n * Version mismatch error\n */\nexport class VersionMismatchError extends RpcError {\n constructor(serviceName: string, requested: string, available: string) {\n super(\n `Version mismatch for ${serviceName}: requested ${requested}, available ${available}`,\n \"VERSION_MISMATCH\",\n 400,\n {\n retryable: false,\n details: { service: serviceName, requested, available },\n }\n );\n this.name = \"VersionMismatchError\";\n }\n}\n\n/**\n * Timeout error\n */\nexport class TimeoutError extends RpcError {\n constructor(serviceName: string, methodName: string, timeoutMs: number) {\n super(\n `Request to ${serviceName}.${methodName} timed out after ${timeoutMs}ms`,\n \"TIMEOUT\",\n 504,\n {\n retryable: true,\n details: { service: serviceName, method: methodName, timeout: timeoutMs },\n }\n );\n this.name = \"TimeoutError\";\n }\n}\n\n/**\n * Circuit breaker open error\n */\nexport class CircuitOpenError extends RpcError {\n constructor(serviceName: string, resetAfterMs: number) {\n super(\n `Circuit breaker open for ${serviceName}`,\n \"CIRCUIT_OPEN\",\n 503,\n {\n retryable: true,\n retryAfter: Math.ceil(resetAfterMs / 1000),\n details: { service: serviceName, resetAfterMs },\n }\n );\n this.name = \"CircuitOpenError\";\n }\n}\n\n/**\n * Bulkhead rejected error\n */\nexport class BulkheadRejectedError extends RpcError {\n constructor(serviceName: string) {\n super(\n `Request rejected by bulkhead for ${serviceName}: too many concurrent requests`,\n \"BULKHEAD_REJECTED\",\n 503,\n {\n retryable: true,\n retryAfter: 1,\n details: { service: serviceName },\n }\n );\n this.name = \"BulkheadRejectedError\";\n }\n}\n\n/**\n * Transport error\n */\nexport class TransportError extends RpcError {\n constructor(message: string, cause?: Error) {\n const options: { retryable: boolean; details?: Record<string, unknown> } = {\n retryable: true,\n };\n if (cause) {\n options.details = { cause: cause.message };\n }\n super(message, \"TRANSPORT_ERROR\", 502, options);\n this.name = \"TransportError\";\n }\n}\n\n/**\n * Serialization error\n */\nexport class SerializationError extends RpcError {\n constructor(message: string, cause?: Error) {\n const options: { retryable: boolean; details?: Record<string, unknown> } = {\n retryable: false,\n };\n if (cause) {\n options.details = { cause: cause.message };\n }\n super(message, \"SERIALIZATION_ERROR\", 400, options);\n this.name = \"SerializationError\";\n }\n}\n\n/**\n * Convert unknown error to RpcError\n */\nexport function toRpcError(error: unknown): RpcError {\n if (error instanceof RpcError) {\n return error;\n }\n\n if (error instanceof Error) {\n return new RpcError(error.message, \"INTERNAL_ERROR\", 500, {\n retryable: false,\n details: { originalError: error.name },\n });\n }\n\n return new RpcError(String(error), \"UNKNOWN_ERROR\", 500, {\n retryable: false,\n });\n}\n","/**\n * @parsrun/service - Circuit Breaker\n * Prevents cascading failures by failing fast when a service is unhealthy\n */\n\nimport { CircuitOpenError } from \"../rpc/errors.js\";\n\n// ============================================================================\n// CIRCUIT BREAKER\n// ============================================================================\n\n/**\n * Options for configuring a circuit breaker.\n */\nexport interface CircuitBreakerOptions {\n /** Number of failures before opening circuit */\n failureThreshold: number;\n /** Time to wait before half-open state (ms) */\n resetTimeout: number;\n /** Number of successes in half-open to close circuit */\n successThreshold: number;\n /** Optional callback on state change */\n onStateChange?: (from: CircuitState, to: CircuitState) => void;\n}\n\n/**\n * Circuit breaker state.\n * - \"closed\": Normal operation, requests pass through\n * - \"open\": Failing fast, requests are rejected immediately\n * - \"half-open\": Testing if service recovered, limited requests allowed\n */\nexport type CircuitState = \"closed\" | \"open\" | \"half-open\";\n\n/**\n * Circuit Breaker implementation\n *\n * States:\n * - CLOSED: Normal operation, requests pass through\n * - OPEN: Failing fast, requests are rejected immediately\n * - HALF-OPEN: Testing if service recovered, limited requests allowed\n */\nexport class CircuitBreaker {\n private _state: CircuitState = \"closed\";\n private failures = 0;\n private successes = 0;\n private lastFailureTime = 0;\n private readonly options: CircuitBreakerOptions;\n\n constructor(options: CircuitBreakerOptions) {\n this.options = options;\n }\n\n /**\n * Get current state\n */\n get state(): CircuitState {\n // Check if we should transition from open to half-open\n if (this._state === \"open\") {\n const timeSinceFailure = Date.now() - this.lastFailureTime;\n if (timeSinceFailure >= this.options.resetTimeout) {\n this.transitionTo(\"half-open\");\n }\n }\n return this._state;\n }\n\n /**\n * Execute a function with circuit breaker protection\n */\n async execute<T>(fn: () => Promise<T>): Promise<T> {\n // Check state (this may transition from open to half-open)\n const currentState = this.state;\n\n if (currentState === \"open\") {\n const resetAfter = this.options.resetTimeout - (Date.now() - this.lastFailureTime);\n throw new CircuitOpenError(\"service\", Math.max(0, resetAfter));\n }\n\n try {\n const result = await fn();\n this.onSuccess();\n return result;\n } catch (error) {\n this.onFailure();\n throw error;\n }\n }\n\n /**\n * Record a successful call\n */\n private onSuccess(): void {\n if (this._state === \"half-open\") {\n this.successes++;\n if (this.successes >= this.options.successThreshold) {\n this.transitionTo(\"closed\");\n }\n } else if (this._state === \"closed\") {\n // Reset failure count on success\n this.failures = 0;\n }\n }\n\n /**\n * Record a failed call\n */\n private onFailure(): void {\n this.lastFailureTime = Date.now();\n\n if (this._state === \"half-open\") {\n // Any failure in half-open goes back to open\n this.transitionTo(\"open\");\n } else if (this._state === \"closed\") {\n this.failures++;\n if (this.failures >= this.options.failureThreshold) {\n this.transitionTo(\"open\");\n }\n }\n }\n\n /**\n * Transition to a new state\n */\n private transitionTo(newState: CircuitState): void {\n const oldState = this._state;\n this._state = newState;\n\n // Reset counters on state change\n if (newState === \"closed\") {\n this.failures = 0;\n this.successes = 0;\n } else if (newState === \"half-open\") {\n this.successes = 0;\n }\n\n this.options.onStateChange?.(oldState, newState);\n }\n\n /**\n * Manually reset the circuit breaker\n */\n reset(): void {\n this.transitionTo(\"closed\");\n }\n\n /**\n * Get circuit breaker statistics\n */\n getStats(): {\n state: CircuitState;\n failures: number;\n successes: number;\n lastFailureTime: number;\n } {\n return {\n state: this.state,\n failures: this.failures,\n successes: this.successes,\n lastFailureTime: this.lastFailureTime,\n };\n }\n}\n","/**\n * @parsrun/service - Bulkhead\n * Limits concurrent requests to prevent resource exhaustion\n */\n\nimport { BulkheadRejectedError } from \"../rpc/errors.js\";\n\n// ============================================================================\n// BULKHEAD\n// ============================================================================\n\n/**\n * Options for configuring a bulkhead.\n */\nexport interface BulkheadOptions {\n /** Maximum concurrent requests */\n maxConcurrent: number;\n /** Maximum queue size (0 = no queue) */\n maxQueue: number;\n /** Optional callback when request is rejected */\n onRejected?: () => void;\n}\n\ninterface QueuedRequest<T> {\n fn: () => Promise<T>;\n resolve: (value: T) => void;\n reject: (error: Error) => void;\n}\n\n/**\n * Bulkhead implementation\n *\n * Limits the number of concurrent requests to protect resources.\n * Excess requests can be queued up to maxQueue limit.\n */\nexport class Bulkhead {\n private _concurrent = 0;\n private readonly queue: QueuedRequest<unknown>[] = [];\n private readonly options: BulkheadOptions;\n\n constructor(options: BulkheadOptions) {\n this.options = options;\n }\n\n /**\n * Get current concurrent count\n */\n get concurrent(): number {\n return this._concurrent;\n }\n\n /**\n * Get current queue size\n */\n get queued(): number {\n return this.queue.length;\n }\n\n /**\n * Check if bulkhead is full\n */\n get isFull(): boolean {\n return (\n this._concurrent >= this.options.maxConcurrent &&\n this.queue.length >= this.options.maxQueue\n );\n }\n\n /**\n * Execute a function with bulkhead protection\n */\n async execute<T>(fn: () => Promise<T>): Promise<T> {\n // Check if we can execute immediately\n if (this._concurrent < this.options.maxConcurrent) {\n return this.doExecute(fn);\n }\n\n // Check if we can queue\n if (this.queue.length < this.options.maxQueue) {\n return this.enqueue(fn);\n }\n\n // Reject\n this.options.onRejected?.();\n throw new BulkheadRejectedError(\"service\");\n }\n\n /**\n * Execute immediately\n */\n private async doExecute<T>(fn: () => Promise<T>): Promise<T> {\n this._concurrent++;\n try {\n return await fn();\n } finally {\n this._concurrent--;\n this.processQueue();\n }\n }\n\n /**\n * Add to queue\n */\n private enqueue<T>(fn: () => Promise<T>): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n this.queue.push({\n fn,\n resolve: resolve as (value: unknown) => void,\n reject,\n });\n });\n }\n\n /**\n * Process queued requests\n */\n private processQueue(): void {\n if (this.queue.length === 0) return;\n if (this._concurrent >= this.options.maxConcurrent) return;\n\n const queued = this.queue.shift();\n if (!queued) return;\n\n this.doExecute(queued.fn)\n .then(queued.resolve)\n .catch(queued.reject);\n }\n\n /**\n * Get bulkhead statistics\n */\n getStats(): {\n concurrent: number;\n queued: number;\n maxConcurrent: number;\n maxQueue: number;\n } {\n return {\n concurrent: this._concurrent,\n queued: this.queue.length,\n maxConcurrent: this.options.maxConcurrent,\n maxQueue: this.options.maxQueue,\n };\n }\n\n /**\n * Clear the queue (reject all pending)\n */\n clearQueue(): void {\n const error = new BulkheadRejectedError(\"service\");\n while (this.queue.length > 0) {\n const queued = this.queue.shift();\n queued?.reject(error);\n }\n }\n}\n","/**\n * @parsrun/service - Retry\n * Retry failed operations with backoff\n */\n\n// ============================================================================\n// RETRY\n// ============================================================================\n\n/**\n * Options for configuring retry behavior.\n */\nexport interface RetryOptions {\n /** Number of retry attempts (not including initial attempt) */\n attempts: number;\n /** Backoff strategy */\n backoff: \"linear\" | \"exponential\";\n /** Initial delay in ms */\n initialDelay: number;\n /** Maximum delay in ms */\n maxDelay: number;\n /** Jitter factor (0-1) to add randomness */\n jitter?: number;\n /** Should retry predicate */\n shouldRetry?: (error: unknown, attempt: number) => boolean;\n /** Callback on retry */\n onRetry?: (error: unknown, attempt: number, delay: number) => void;\n}\n\n/**\n * Default retry predicate - retry on retryable errors\n */\nconst defaultShouldRetry = (error: unknown): boolean => {\n if (error && typeof error === \"object\" && \"retryable\" in error) {\n return (error as { retryable: boolean }).retryable;\n }\n return false;\n};\n\n/**\n * Calculate delay for retry attempt\n */\nfunction calculateDelay(\n attempt: number,\n options: RetryOptions\n): number {\n let delay: number;\n\n if (options.backoff === \"exponential\") {\n // Exponential backoff: initialDelay * 2^attempt\n delay = options.initialDelay * Math.pow(2, attempt);\n } else {\n // Linear backoff: initialDelay * (attempt + 1)\n delay = options.initialDelay * (attempt + 1);\n }\n\n // Apply max delay\n delay = Math.min(delay, options.maxDelay);\n\n // Apply jitter\n if (options.jitter && options.jitter > 0) {\n const jitterRange = delay * options.jitter;\n delay = delay - jitterRange / 2 + Math.random() * jitterRange;\n }\n\n return Math.round(delay);\n}\n\n/**\n * Sleep for a given duration\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Wrap a function with retry logic.\n * Returns a new function that will retry on failure.\n *\n * @param fn - The function to wrap\n * @param options - Retry configuration\n * @returns A wrapped function with retry behavior\n *\n * @example\n * ```typescript\n * const fetchWithRetry = withRetry(\n * () => fetch('/api/data'),\n * { attempts: 3, backoff: 'exponential', initialDelay: 100, maxDelay: 5000 }\n * );\n * const response = await fetchWithRetry();\n * ```\n */\nexport function withRetry<T>(\n fn: () => Promise<T>,\n options: RetryOptions\n): () => Promise<T> {\n const shouldRetry = options.shouldRetry ?? defaultShouldRetry;\n\n return async (): Promise<T> => {\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= options.attempts; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n\n // Check if we should retry\n if (attempt >= options.attempts || !shouldRetry(error, attempt)) {\n throw error;\n }\n\n // Calculate delay\n const delay = calculateDelay(attempt, options);\n\n // Callback\n options.onRetry?.(error, attempt + 1, delay);\n\n // Wait before retry\n await sleep(delay);\n }\n }\n\n throw lastError;\n };\n}\n\n/**\n * Execute a function with retry logic immediately.\n *\n * @param fn - The function to execute\n * @param options - Retry configuration\n * @returns Promise resolving to the function result\n */\nexport async function executeWithRetry<T>(\n fn: () => Promise<T>,\n options: RetryOptions\n): Promise<T> {\n return withRetry(fn, options)();\n}\n\n/**\n * Create a reusable retry wrapper with preset default options.\n *\n * @param defaultOptions - Default retry configuration\n * @returns A function that executes with retry using the defaults\n *\n * @example\n * ```typescript\n * const retry = createRetryWrapper({ attempts: 3, backoff: 'exponential' });\n * const result = await retry(() => fetchData());\n * ```\n */\nexport function createRetryWrapper(\n defaultOptions: Partial<RetryOptions>\n): <T>(fn: () => Promise<T>, options?: Partial<RetryOptions>) => Promise<T> {\n const defaults: RetryOptions = {\n attempts: defaultOptions.attempts ?? 3,\n backoff: defaultOptions.backoff ?? \"exponential\",\n initialDelay: defaultOptions.initialDelay ?? 100,\n maxDelay: defaultOptions.maxDelay ?? 10_000,\n jitter: defaultOptions.jitter ?? 0.1,\n };\n\n if (defaultOptions.shouldRetry) {\n defaults.shouldRetry = defaultOptions.shouldRetry;\n }\n if (defaultOptions.onRetry) {\n defaults.onRetry = defaultOptions.onRetry;\n }\n\n return async <T>(\n fn: () => Promise<T>,\n options?: Partial<RetryOptions>\n ): Promise<T> => {\n return executeWithRetry(fn, { ...defaults, ...options });\n };\n}\n","/**\n * @parsrun/service - Timeout\n * Timeout wrapper for async operations\n */\n\n// ============================================================================\n// TIMEOUT\n// ============================================================================\n\n/**\n * Error thrown when an operation exceeds its timeout.\n */\nexport class TimeoutExceededError extends Error {\n /** The timeout value in milliseconds that was exceeded */\n readonly timeout: number;\n\n constructor(timeout: number) {\n super(`Operation timed out after ${timeout}ms`);\n this.name = \"TimeoutExceededError\";\n this.timeout = timeout;\n }\n}\n\n/**\n * Wrap a function with a timeout.\n * Returns a new function that will reject if the timeout is exceeded.\n *\n * @param fn - The function to wrap\n * @param timeoutMs - Timeout in milliseconds\n * @param onTimeout - Optional callback or throw function when timeout occurs\n * @returns A wrapped function with timeout behavior\n */\nexport function withTimeout<T>(\n fn: () => Promise<T>,\n timeoutMs: number,\n onTimeout?: () => void | never\n): () => Promise<T> {\n return async (): Promise<T> => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n if (onTimeout) {\n try {\n onTimeout();\n } catch (error) {\n reject(error);\n return;\n }\n }\n reject(new TimeoutExceededError(timeoutMs));\n }, timeoutMs);\n });\n\n try {\n return await Promise.race([fn(), timeoutPromise]);\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n };\n}\n\n/**\n * Execute a function with timeout immediately.\n *\n * @param fn - The function to execute\n * @param timeoutMs - Timeout in milliseconds\n * @param onTimeout - Optional callback or throw function when timeout occurs\n * @returns Promise resolving to the function result or rejecting on timeout\n */\nexport async function executeWithTimeout<T>(\n fn: () => Promise<T>,\n timeoutMs: number,\n onTimeout?: () => void | never\n): Promise<T> {\n return withTimeout(fn, timeoutMs, onTimeout)();\n}\n\n/**\n * Create a reusable timeout wrapper with preset duration.\n *\n * @param defaultTimeoutMs - Default timeout in milliseconds\n * @returns A function that executes with timeout\n */\nexport function createTimeoutWrapper(\n defaultTimeoutMs: number\n): <T>(fn: () => Promise<T>, timeoutMs?: number) => Promise<T> {\n return async <T>(fn: () => Promise<T>, timeoutMs?: number): Promise<T> => {\n return executeWithTimeout(fn, timeoutMs ?? defaultTimeoutMs);\n };\n}\n\n/**\n * Race multiple promises with a timeout.\n * Returns the first promise to resolve, or rejects on timeout.\n *\n * @param promises - Array of promises to race\n * @param timeoutMs - Timeout in milliseconds\n * @returns Promise resolving to the first result\n */\nexport async function raceWithTimeout<T>(\n promises: Promise<T>[],\n timeoutMs: number\n): Promise<T> {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new TimeoutExceededError(timeoutMs));\n }, timeoutMs);\n });\n\n try {\n return await Promise.race([...promises, timeoutPromise]);\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n}\n\n/**\n * Execute a function with an absolute deadline.\n * Converts the deadline to a relative timeout.\n *\n * @param fn - The function to execute\n * @param deadline - Absolute deadline as a Date\n * @returns Promise resolving to the function result\n */\nexport async function executeWithDeadline<T>(\n fn: () => Promise<T>,\n deadline: Date\n): Promise<T> {\n const now = Date.now();\n const deadlineMs = deadline.getTime();\n const timeoutMs = Math.max(0, deadlineMs - now);\n\n if (timeoutMs === 0) {\n throw new TimeoutExceededError(0);\n }\n\n return executeWithTimeout(fn, timeoutMs);\n}\n","/**\n * @parsrun/service - Embedded RPC Transport\n * Direct function call transport for monolithic/embedded mode\n */\n\nimport type { RpcRequest, RpcResponse, RpcTransport } from \"../../types.js\";\nimport type { RpcServer } from \"../server.js\";\n\n// ============================================================================\n// EMBEDDED TRANSPORT\n// ============================================================================\n\n/**\n * Embedded transport that calls handlers directly\n * Used when services are in the same process\n */\nexport class EmbeddedTransport implements RpcTransport {\n readonly name = \"embedded\";\n private readonly server: RpcServer;\n\n constructor(server: RpcServer) {\n this.server = server;\n }\n\n async call<TInput, TOutput>(request: RpcRequest<TInput>): Promise<RpcResponse<TOutput>> {\n // Direct call to server - no serialization needed\n return this.server.handle<TInput, TOutput>(request);\n }\n\n async close(): Promise<void> {\n // No cleanup needed for embedded transport\n }\n}\n\n/**\n * Create an embedded transport for direct function calls.\n * Used when the service is in the same process.\n *\n * @param server - The RPC server to call directly\n * @returns A new embedded transport instance\n *\n * @example\n * ```typescript\n * const transport = createEmbeddedTransport(rpcServer);\n * const client = createRpcClient({ service: 'payments', transport });\n * ```\n */\nexport function createEmbeddedTransport(server: RpcServer): EmbeddedTransport {\n return new EmbeddedTransport(server);\n}\n\n// ============================================================================\n// EMBEDDED TRANSPORT REGISTRY\n// ============================================================================\n\n/**\n * Registry for embedded services\n * Allows services to find each other in embedded mode\n */\nexport class EmbeddedRegistry {\n private static instance: EmbeddedRegistry | null = null;\n private readonly servers: Map<string, RpcServer> = new Map();\n\n private constructor() {}\n\n static getInstance(): EmbeddedRegistry {\n if (!EmbeddedRegistry.instance) {\n EmbeddedRegistry.instance = new EmbeddedRegistry();\n }\n return EmbeddedRegistry.instance;\n }\n\n /**\n * Register a service\n */\n register(name: string, server: RpcServer): void {\n if (this.servers.has(name)) {\n throw new Error(`Service already registered: ${name}`);\n }\n this.servers.set(name, server);\n }\n\n /**\n * Unregister a service\n */\n unregister(name: string): boolean {\n return this.servers.delete(name);\n }\n\n /**\n * Get a service by name\n */\n get(name: string): RpcServer | undefined {\n return this.servers.get(name);\n }\n\n /**\n * Check if a service is registered\n */\n has(name: string): boolean {\n return this.servers.has(name);\n }\n\n /**\n * Get all registered service names\n */\n getServiceNames(): string[] {\n return Array.from(this.servers.keys());\n }\n\n /**\n * Create a transport for a registered service\n */\n createTransport(name: string): EmbeddedTransport {\n const server = this.servers.get(name);\n if (!server) {\n throw new Error(`Service not found: ${name}`);\n }\n return new EmbeddedTransport(server);\n }\n\n /**\n * Clear all registered services\n */\n clear(): void {\n this.servers.clear();\n }\n\n /**\n * Reset the singleton instance (for testing)\n */\n static reset(): void {\n EmbeddedRegistry.instance = null;\n }\n}\n\n/**\n * Get the global embedded registry singleton.\n * Provides access to the shared registry for service discovery.\n *\n * @returns The global embedded registry instance\n *\n * @example\n * ```typescript\n * const registry = getEmbeddedRegistry();\n * registry.register('payments', paymentsServer);\n * const transport = registry.createTransport('payments');\n * ```\n */\nexport function getEmbeddedRegistry(): EmbeddedRegistry {\n return EmbeddedRegistry.getInstance();\n}\n","/**\n * @parsrun/service - Serialization\n * JSON and MessagePack serializers\n */\n\n// ============================================================================\n// SERIALIZER INTERFACE\n// ============================================================================\n\n/**\n * Serializer interface for encoding/decoding data\n */\nexport interface Serializer {\n /** Encode data to string or buffer */\n encode(data: unknown): string | ArrayBuffer;\n /** Decode string or buffer to data */\n decode(raw: string | ArrayBuffer): unknown;\n /** Content type for HTTP headers */\n contentType: string;\n}\n\n// ============================================================================\n// JSON SERIALIZER\n// ============================================================================\n\n/**\n * JSON serializer (default)\n */\nexport const jsonSerializer: Serializer = {\n encode(data: unknown): string {\n return JSON.stringify(data);\n },\n\n decode(raw: string | ArrayBuffer): unknown {\n if (raw instanceof ArrayBuffer) {\n const decoder = new TextDecoder();\n return JSON.parse(decoder.decode(raw));\n }\n return JSON.parse(raw);\n },\n\n contentType: \"application/json\",\n};\n\n// ============================================================================\n// MESSAGEPACK SERIALIZER (Lightweight implementation)\n// ============================================================================\n\n/**\n * Lightweight MessagePack encoder\n * Supports: null, boolean, number, string, array, object\n */\nfunction msgpackEncode(value: unknown): Uint8Array {\n const parts: Uint8Array[] = [];\n\n function encode(val: unknown): void {\n if (val === null || val === undefined) {\n parts.push(new Uint8Array([0xc0])); // nil\n return;\n }\n\n if (typeof val === \"boolean\") {\n parts.push(new Uint8Array([val ? 0xc3 : 0xc2]));\n return;\n }\n\n if (typeof val === \"number\") {\n if (Number.isInteger(val)) {\n if (val >= 0 && val <= 127) {\n // positive fixint\n parts.push(new Uint8Array([val]));\n } else if (val < 0 && val >= -32) {\n // negative fixint\n parts.push(new Uint8Array([val & 0xff]));\n } else if (val >= 0 && val <= 0xff) {\n // uint8\n parts.push(new Uint8Array([0xcc, val]));\n } else if (val >= 0 && val <= 0xffff) {\n // uint16\n parts.push(new Uint8Array([0xcd, (val >> 8) & 0xff, val & 0xff]));\n } else if (val >= 0 && val <= 0xffffffff) {\n // uint32\n parts.push(\n new Uint8Array([\n 0xce,\n (val >> 24) & 0xff,\n (val >> 16) & 0xff,\n (val >> 8) & 0xff,\n val & 0xff,\n ])\n );\n } else if (val >= -128 && val <= 127) {\n // int8\n parts.push(new Uint8Array([0xd0, val & 0xff]));\n } else if (val >= -32768 && val <= 32767) {\n // int16\n parts.push(new Uint8Array([0xd1, (val >> 8) & 0xff, val & 0xff]));\n } else if (val >= -2147483648 && val <= 2147483647) {\n // int32\n parts.push(\n new Uint8Array([\n 0xd2,\n (val >> 24) & 0xff,\n (val >> 16) & 0xff,\n (val >> 8) & 0xff,\n val & 0xff,\n ])\n );\n } else {\n // Fall back to float64 for large integers\n const buffer = new ArrayBuffer(9);\n const view = new DataView(buffer);\n view.setUint8(0, 0xcb);\n view.setFloat64(1, val, false);\n parts.push(new Uint8Array(buffer));\n }\n } else {\n // float64\n const buffer = new ArrayBuffer(9);\n const view = new DataView(buffer);\n view.setUint8(0, 0xcb);\n view.setFloat64(1, val, false);\n parts.push(new Uint8Array(buffer));\n }\n return;\n }\n\n if (typeof val === \"string\") {\n const encoded = new TextEncoder().encode(val);\n const len = encoded.length;\n\n if (len <= 31) {\n // fixstr\n parts.push(new Uint8Array([0xa0 | len]));\n } else if (len <= 0xff) {\n // str8\n parts.push(new Uint8Array([0xd9, len]));\n } else if (len <= 0xffff) {\n // str16\n parts.push(new Uint8Array([0xda, (len >> 8) & 0xff, len & 0xff]));\n } else {\n // str32\n parts.push(\n new Uint8Array([\n 0xdb,\n (len >> 24) & 0xff,\n (len >> 16) & 0xff,\n (len >> 8) & 0xff,\n len & 0xff,\n ])\n );\n }\n parts.push(encoded);\n return;\n }\n\n if (Array.isArray(val)) {\n const len = val.length;\n\n if (len <= 15) {\n // fixarray\n parts.push(new Uint8Array([0x90 | len]));\n } else if (len <= 0xffff) {\n // array16\n parts.push(new Uint8Array([0xdc, (len >> 8) & 0xff, len & 0xff]));\n } else {\n // array32\n parts.push(\n new Uint8Array([\n 0xdd,\n (len >> 24) & 0xff,\n (len >> 16) & 0xff,\n (len >> 8) & 0xff,\n len & 0xff,\n ])\n );\n }\n\n for (const item of val) {\n encode(item);\n }\n return;\n }\n\n if (typeof val === \"object\") {\n const keys = Object.keys(val as object);\n const len = keys.length;\n\n if (len <= 15) {\n // fixmap\n parts.push(new Uint8Array([0x80 | len]));\n } else if (len <= 0xffff) {\n // map16\n parts.push(new Uint8Array([0xde, (len >> 8) & 0xff, len & 0xff]));\n } else {\n // map32\n parts.push(\n new Uint8Array([\n 0xdf,\n (len >> 24) & 0xff,\n (len >> 16) & 0xff,\n (len >> 8) & 0xff,\n len & 0xff,\n ])\n );\n }\n\n for (const key of keys) {\n encode(key);\n encode((val as Record<string, unknown>)[key]);\n }\n return;\n }\n\n // Unsupported type - encode as null\n parts.push(new Uint8Array([0xc0]));\n }\n\n encode(value);\n\n // Merge all parts\n const totalLength = parts.reduce((sum, p) => sum + p.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const part of parts) {\n result.set(part, offset);\n offset += part.length;\n }\n\n return result;\n}\n\n/**\n * Lightweight MessagePack decoder\n */\nfunction msgpackDecode(buffer: Uint8Array): unknown {\n let offset = 0;\n\n function decode(): unknown {\n if (offset >= buffer.length) {\n throw new Error(\"Unexpected end of buffer\");\n }\n\n const byte = buffer[offset++]!;\n\n // Positive fixint (0x00 - 0x7f)\n if (byte <= 0x7f) {\n return byte;\n }\n\n // Negative fixint (0xe0 - 0xff)\n if (byte >= 0xe0) {\n return byte - 256;\n }\n\n // Fixmap (0x80 - 0x8f)\n if (byte >= 0x80 && byte <= 0x8f) {\n const len = byte - 0x80;\n const result: Record<string, unknown> = {};\n for (let i = 0; i < len; i++) {\n const key = decode() as string;\n result[key] = decode();\n }\n return result;\n }\n\n // Fixarray (0x90 - 0x9f)\n if (byte >= 0x90 && byte <= 0x9f) {\n const len = byte - 0x90;\n const result: unknown[] = [];\n for (let i = 0; i < len; i++) {\n result.push(decode());\n }\n return result;\n }\n\n // Fixstr (0xa0 - 0xbf)\n if (byte >= 0xa0 && byte <= 0xbf) {\n const len = byte - 0xa0;\n const str = new TextDecoder().decode(buffer.subarray(offset, offset + len));\n offset += len;\n return str;\n }\n\n switch (byte) {\n case 0xc0: // nil\n return null;\n case 0xc2: // false\n return false;\n case 0xc3: // true\n return true;\n\n case 0xcc: // uint8\n return buffer[offset++];\n case 0xcd: // uint16\n return (buffer[offset++]! << 8) | buffer[offset++]!;\n case 0xce: // uint32\n return (\n ((buffer[offset++]! << 24) >>> 0) +\n (buffer[offset++]! << 16) +\n (buffer[offset++]! << 8) +\n buffer[offset++]!\n );\n\n case 0xd0: // int8\n {\n const val = buffer[offset++]!;\n return val > 127 ? val - 256 : val;\n }\n case 0xd1: // int16\n {\n const val = (buffer[offset++]! << 8) | buffer[offset++]!;\n return val > 32767 ? val - 65536 : val;\n }\n case 0xd2: // int32\n {\n const val =\n (buffer[offset++]! << 24) |\n (buffer[offset++]! << 16) |\n (buffer[offset++]! << 8) |\n buffer[offset++]!;\n return val;\n }\n\n case 0xcb: // float64\n {\n const view = new DataView(buffer.buffer, buffer.byteOffset + offset, 8);\n offset += 8;\n return view.getFloat64(0, false);\n }\n\n case 0xd9: // str8\n {\n const len = buffer[offset++]!;\n const str = new TextDecoder().decode(buffer.subarray(offset, offset + len));\n offset += len;\n return str;\n }\n case 0xda: // str16\n {\n const len = (buffer[offset++]! << 8) | buffer[offset++]!;\n const str = new TextDecoder().decode(buffer.subarray(offset, offset + len));\n offset += len;\n return str;\n }\n case 0xdb: // str32\n {\n const len =\n (buffer[offset++]! << 24) |\n (buffer[offset++]! << 16) |\n (buffer[offset++]! << 8) |\n buffer[offset++]!;\n const str = new TextDecoder().decode(buffer.subarray(offset, offset + len));\n offset += len;\n return str;\n }\n\n case 0xdc: // array16\n {\n const len = (buffer[offset++]! << 8) | buffer[offset++]!;\n const result: unknown[] = [];\n for (let i = 0; i < len; i++) {\n result.push(decode());\n }\n return result;\n }\n case 0xdd: // array32\n {\n const len =\n (buffer[offset++]! << 24) |\n (buffer[offset++]! << 16) |\n (buffer[offset++]! << 8) |\n buffer[offset++]!;\n const result: unknown[] = [];\n for (let i = 0; i < len; i++) {\n result.push(decode());\n }\n return result;\n }\n\n case 0xde: // map16\n {\n const len = (buffer[offset++]! << 8) | buffer[offset++]!;\n const result: Record<string, unknown> = {};\n for (let i = 0; i < len; i++) {\n const key = decode() as string;\n result[key] = decode();\n }\n return result;\n }\n case 0xdf: // map32\n {\n const len =\n (buffer[offset++]! << 24) |\n (buffer[offset++]! << 16) |\n (buffer[offset++]! << 8) |\n buffer[offset++]!;\n const result: Record<string, unknown> = {};\n for (let i = 0; i < len; i++) {\n const key = decode() as string;\n result[key] = decode();\n }\n return result;\n }\n\n default:\n throw new Error(`Unknown MessagePack type: 0x${byte.toString(16)}`);\n }\n }\n\n return decode();\n}\n\n/**\n * MessagePack serializer\n */\nexport const msgpackSerializer: Serializer = {\n encode(data: unknown): ArrayBuffer {\n const encoded = msgpackEncode(data);\n // Create a new ArrayBuffer with the exact bytes from the Uint8Array\n const buffer = new ArrayBuffer(encoded.byteLength);\n new Uint8Array(buffer).set(encoded);\n return buffer;\n },\n\n decode(raw: string | ArrayBuffer): unknown {\n if (typeof raw === \"string\") {\n // If string is passed, assume it's base64 encoded\n const binary = atob(raw);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return msgpackDecode(bytes);\n }\n return msgpackDecode(new Uint8Array(raw));\n },\n\n contentType: \"application/msgpack\",\n};\n\n// ============================================================================\n// SERIALIZER FACTORY\n// ============================================================================\n\n/**\n * Get a serializer by format name.\n *\n * @param format - The serialization format (\"json\" or \"msgpack\")\n * @returns The corresponding serializer instance\n *\n * @example\n * ```typescript\n * const serializer = getSerializer('json');\n * const encoded = serializer.encode({ foo: 'bar' });\n * const decoded = serializer.decode(encoded);\n * ```\n */\nexport function getSerializer(format: \"json\" | \"msgpack\"): Serializer {\n switch (format) {\n case \"json\":\n return jsonSerializer;\n case \"msgpack\":\n return msgpackSerializer;\n default:\n return jsonSerializer;\n }\n}\n\n/**\n * Create a custom serializer with provided encode/decode functions.\n *\n * @param options - Serializer configuration\n * @param options.encode - Function to encode data to string or ArrayBuffer\n * @param options.decode - Function to decode string or ArrayBuffer to data\n * @param options.contentType - HTTP content type for the serialization format\n * @returns A custom serializer instance\n *\n * @example\n * ```typescript\n * const customSerializer = createSerializer({\n * encode: (data) => btoa(JSON.stringify(data)),\n * decode: (raw) => JSON.parse(atob(raw as string)),\n * contentType: 'application/x-custom',\n * });\n * ```\n */\nexport function createSerializer(options: {\n encode: (data: unknown) => string | ArrayBuffer;\n decode: (raw: string | ArrayBuffer) => unknown;\n contentType: string;\n}): Serializer {\n return options;\n}\n","/**\n * @parsrun/service - HTTP RPC Transport\n * HTTP-based transport for distributed services\n */\n\nimport type { RpcRequest, RpcResponse, RpcTransport } from \"../../types.js\";\nimport { type Serializer, jsonSerializer } from \"../../serialization/index.js\";\nimport { TransportError, SerializationError } from \"../errors.js\";\n\n// ============================================================================\n// HTTP TRANSPORT\n// ============================================================================\n\n/**\n * Options for creating an HTTP transport.\n */\nexport interface HttpTransportOptions {\n /** Base URL of the service */\n baseUrl: string;\n /** Custom serializer (default: JSON) */\n serializer?: Serializer;\n /** Custom headers */\n headers?: Record<string, string>;\n /** Fetch function (for testing or custom implementations) */\n fetch?: typeof globalThis.fetch;\n /** Request timeout in ms */\n timeout?: number;\n}\n\n/**\n * HTTP transport for RPC calls over HTTP/HTTPS.\n * Makes POST requests to /rpc endpoint with serialized request body.\n */\nexport class HttpTransport implements RpcTransport {\n readonly name = \"http\";\n private readonly baseUrl: string;\n private readonly serializer: Serializer;\n private readonly headers: Record<string, string>;\n private readonly fetchFn: typeof globalThis.fetch;\n private readonly timeout: number;\n\n constructor(options: HttpTransportOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\"); // Remove trailing slash\n this.serializer = options.serializer ?? jsonSerializer;\n this.headers = options.headers ?? {};\n this.fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);\n this.timeout = options.timeout ?? 30_000;\n }\n\n async call<TInput, TOutput>(request: RpcRequest<TInput>): Promise<RpcResponse<TOutput>> {\n const url = `${this.baseUrl}/rpc`;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n // Serialize request\n let body: string | ArrayBuffer;\n try {\n body = this.serializer.encode(request);\n } catch (error) {\n throw new SerializationError(\n \"Failed to serialize request\",\n error instanceof Error ? error : undefined\n );\n }\n\n // Make HTTP request\n const response = await this.fetchFn(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": this.serializer.contentType,\n Accept: this.serializer.contentType,\n \"X-Request-ID\": request.id,\n \"X-Service\": request.service,\n \"X-Method\": request.method,\n \"X-Method-Type\": request.type,\n ...(request.version ? { \"X-Service-Version\": request.version } : {}),\n ...(request.traceContext\n ? {\n traceparent: formatTraceparent(request.traceContext),\n ...(request.traceContext.traceState\n ? { tracestate: request.traceContext.traceState }\n : {}),\n }\n : {}),\n ...this.headers,\n },\n body: body instanceof ArrayBuffer ? body : body,\n signal: controller.signal,\n });\n\n // Parse response\n let responseData: RpcResponse<TOutput>;\n try {\n const contentType = response.headers.get(\"Content-Type\") ?? \"\";\n if (contentType.includes(\"msgpack\")) {\n const buffer = await response.arrayBuffer();\n responseData = this.serializer.decode(buffer) as RpcResponse<TOutput>;\n } else {\n const text = await response.text();\n responseData = this.serializer.decode(text) as RpcResponse<TOutput>;\n }\n } catch (error) {\n throw new SerializationError(\n \"Failed to deserialize response\",\n error instanceof Error ? error : undefined\n );\n }\n\n return responseData;\n } catch (error) {\n if (error instanceof SerializationError) {\n throw error;\n }\n\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new TransportError(`Request timeout after ${this.timeout}ms`);\n }\n throw new TransportError(`HTTP request failed: ${error.message}`, error);\n }\n\n throw new TransportError(\"Unknown transport error\");\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async close(): Promise<void> {\n // No persistent connection to close\n }\n}\n\n/**\n * Create an HTTP transport for RPC calls.\n *\n * @param options - Transport configuration options\n * @returns A new HTTP transport instance\n *\n * @example\n * ```typescript\n * const transport = createHttpTransport({\n * baseUrl: 'https://api.example.com',\n * timeout: 5000,\n * });\n * ```\n */\nexport function createHttpTransport(options: HttpTransportOptions): HttpTransport {\n return new HttpTransport(options);\n}\n\n// ============================================================================\n// TRACE CONTEXT HELPERS\n// ============================================================================\n\nimport type { TraceContext } from \"../../types.js\";\n\n/**\n * Format trace context as W3C traceparent header\n */\nfunction formatTraceparent(ctx: TraceContext): string {\n const flags = ctx.traceFlags.toString(16).padStart(2, \"0\");\n return `00-${ctx.traceId}-${ctx.spanId}-${flags}`;\n}\n\n/**\n * Parse W3C traceparent header to extract trace context.\n *\n * @param header - The traceparent header value\n * @returns Parsed trace context or null if invalid\n *\n * @example\n * ```typescript\n * const ctx = parseTraceparent('00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01');\n * // { traceId: '0af7651916cd43dd8448eb211c80319c', spanId: 'b7ad6b7169203331', traceFlags: 1 }\n * ```\n */\nexport function parseTraceparent(header: string): TraceContext | null {\n const parts = header.split(\"-\");\n if (parts.length !== 4) {\n return null;\n }\n\n const [version, traceId, spanId, flags] = parts;\n if (version !== \"00\" || !traceId || !spanId || !flags) {\n return null;\n }\n\n if (traceId.length !== 32 || spanId.length !== 16 || flags.length !== 2) {\n return null;\n }\n\n return {\n traceId,\n spanId,\n traceFlags: parseInt(flags, 16),\n };\n}\n\n// ============================================================================\n// HTTP SERVER ADAPTER\n// ============================================================================\n\nimport type { RpcServer } from \"../server.js\";\n\n/**\n * Create HTTP request handler for RPC server.\n * Can be used with Hono, Express, or any HTTP framework.\n *\n * @param server - The RPC server to handle requests\n * @returns Request handler function for HTTP frameworks\n *\n * @example\n * ```typescript\n * const handler = createHttpHandler(rpcServer);\n * // With Hono\n * app.post('/rpc', (c) => handler(c.req.raw));\n * ```\n */\nexport function createHttpHandler(server: RpcServer) {\n return async (request: Request): Promise<Response> => {\n try {\n // Parse request body\n const contentType = request.headers.get(\"Content-Type\") ?? \"application/json\";\n let body: unknown;\n\n if (contentType.includes(\"msgpack\")) {\n const buffer = await request.arrayBuffer();\n // For msgpack, we'd need to decode - using JSON for now\n body = JSON.parse(new TextDecoder().decode(buffer));\n } else {\n body = await request.json();\n }\n\n const rpcRequest = body as RpcRequest;\n\n // Parse trace context\n const traceparent = request.headers.get(\"traceparent\");\n if (traceparent) {\n const traceContext = parseTraceparent(traceparent);\n if (traceContext) {\n const tracestate = request.headers.get(\"tracestate\");\n if (tracestate) {\n traceContext.traceState = tracestate;\n }\n rpcRequest.traceContext = traceContext;\n }\n }\n\n // Handle request\n const response = await server.handle(rpcRequest);\n\n // Return response\n return new Response(JSON.stringify(response), {\n status: response.success ? 200 : getHttpStatus(response.error?.code),\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Request-ID\": rpcRequest.id,\n },\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n return new Response(\n JSON.stringify({\n success: false,\n error: {\n code: \"INTERNAL_ERROR\",\n message,\n },\n }),\n {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n }\n );\n }\n };\n}\n\n/**\n * Map error code to HTTP status\n */\nfunction getHttpStatus(code?: string): number {\n switch (code) {\n case \"METHOD_NOT_FOUND\":\n case \"SERVICE_NOT_FOUND\":\n return 404;\n case \"VERSION_MISMATCH\":\n case \"VALIDATION_ERROR\":\n case \"SERIALIZATION_ERROR\":\n return 400;\n case \"UNAUTHORIZED\":\n return 401;\n case \"FORBIDDEN\":\n return 403;\n case \"TIMEOUT\":\n return 504;\n case \"CIRCUIT_OPEN\":\n case \"BULKHEAD_REJECTED\":\n return 503;\n default:\n return 500;\n }\n}\n","/**\n * @parsrun/service - Memory Event Transport\n * In-memory event transport for embedded mode and testing\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { createLogger } from \"@parsrun/core\";\nimport type {\n ParsEvent,\n EventTransport,\n EventHandler,\n EventHandlerOptions,\n Unsubscribe,\n} from \"../../types.js\";\nimport { EventHandlerRegistry, type EventHandlerRegistryOptions } from \"../handler.js\";\n\n// ============================================================================\n// MEMORY EVENT TRANSPORT\n// ============================================================================\n\n/**\n * Options for creating a memory event transport.\n */\nexport interface MemoryEventTransportOptions {\n /** Logger */\n logger?: Logger;\n /** Process events synchronously (default: false) */\n sync?: boolean;\n /** Default handler options */\n defaultHandlerOptions?: EventHandlerOptions;\n}\n\n/**\n * In-memory event transport\n * Events are processed immediately without persistence\n */\nexport class MemoryEventTransport implements EventTransport {\n readonly name = \"memory\";\n private readonly registry: EventHandlerRegistry;\n private readonly logger: Logger;\n private readonly sync: boolean;\n private readonly pendingEvents: ParsEvent[] = [];\n private processing = false;\n\n constructor(options: MemoryEventTransportOptions = {}) {\n this.logger = options.logger ?? createLogger({ name: \"memory-transport\" });\n this.sync = options.sync ?? false;\n\n const registryOptions: EventHandlerRegistryOptions = {\n logger: this.logger,\n };\n if (options.defaultHandlerOptions) {\n registryOptions.defaultOptions = options.defaultHandlerOptions;\n }\n this.registry = new EventHandlerRegistry(registryOptions);\n }\n\n /**\n * Emit an event\n */\n async emit<T>(event: ParsEvent<T>): Promise<void> {\n this.logger.debug(`Event emitted: ${event.type}`, {\n eventId: event.id,\n tenantId: event.parstenantid,\n });\n\n if (this.sync) {\n // Process synchronously\n await this.registry.handle(event);\n } else {\n // Queue for async processing\n this.pendingEvents.push(event);\n this.processQueue();\n }\n }\n\n /**\n * Subscribe to events\n */\n subscribe(\n eventType: string,\n handler: EventHandler,\n options?: EventHandlerOptions\n ): Unsubscribe {\n return this.registry.register(eventType, handler, options);\n }\n\n /**\n * Process pending events asynchronously\n */\n private async processQueue(): Promise<void> {\n if (this.processing) return;\n this.processing = true;\n\n try {\n while (this.pendingEvents.length > 0) {\n const event = this.pendingEvents.shift();\n if (event) {\n await this.registry.handle(event);\n }\n }\n } finally {\n this.processing = false;\n }\n }\n\n /**\n * Wait for all pending events to be processed\n */\n async flush(): Promise<void> {\n while (this.pendingEvents.length > 0 || this.processing) {\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n }\n\n /**\n * Get pending event count\n */\n get pendingCount(): number {\n return this.pendingEvents.length;\n }\n\n /**\n * Get registered patterns\n */\n getPatterns(): string[] {\n return this.registry.getPatterns();\n }\n\n /**\n * Clear all subscriptions\n */\n clear(): void {\n this.registry.clear();\n this.pendingEvents.length = 0;\n }\n\n /**\n * Close the transport\n */\n async close(): Promise<void> {\n await this.flush();\n this.clear();\n }\n}\n\n/**\n * Create a memory event transport for in-process event handling.\n * Suitable for testing and embedded mode.\n *\n * @param options - Transport configuration options\n * @returns A new memory event transport instance\n *\n * @example\n * ```typescript\n * const transport = createMemoryEventTransport({ sync: true });\n * transport.subscribe('user.created', async (event) => {\n * console.log('User created:', event.data);\n * });\n * await transport.emit({ type: 'user.created', data: { id: '123' } });\n * ```\n */\nexport function createMemoryEventTransport(\n options?: MemoryEventTransportOptions\n): MemoryEventTransport {\n return new MemoryEventTransport(options);\n}\n\n// ============================================================================\n// GLOBAL EVENT BUS (for embedded multi-service)\n// ============================================================================\n\n/**\n * Global event bus for communication between embedded services\n */\nexport class GlobalEventBus {\n private static instance: GlobalEventBus | null = null;\n private readonly transports: Map<string, MemoryEventTransport> = new Map();\n private readonly logger: Logger;\n\n private constructor() {\n this.logger = createLogger({ name: \"global-event-bus\" });\n }\n\n static getInstance(): GlobalEventBus {\n if (!GlobalEventBus.instance) {\n GlobalEventBus.instance = new GlobalEventBus();\n }\n return GlobalEventBus.instance;\n }\n\n /**\n * Register a service's event transport\n */\n register(serviceName: string, transport: MemoryEventTransport): void {\n if (this.transports.has(serviceName)) {\n throw new Error(`Service already registered: ${serviceName}`);\n }\n this.transports.set(serviceName, transport);\n this.logger.debug(`Service registered: ${serviceName}`);\n }\n\n /**\n * Unregister a service\n */\n unregister(serviceName: string): boolean {\n const deleted = this.transports.delete(serviceName);\n if (deleted) {\n this.logger.debug(`Service unregistered: ${serviceName}`);\n }\n return deleted;\n }\n\n /**\n * Broadcast an event to all services (except source)\n */\n async broadcast(event: ParsEvent, excludeSource?: string): Promise<void> {\n const promises: Promise<void>[] = [];\n\n for (const [name, transport] of this.transports) {\n if (name !== excludeSource) {\n promises.push(transport.emit(event));\n }\n }\n\n await Promise.allSettled(promises);\n }\n\n /**\n * Send an event to a specific service\n */\n async send(serviceName: string, event: ParsEvent): Promise<void> {\n const transport = this.transports.get(serviceName);\n if (!transport) {\n this.logger.warn(`Target service not found: ${serviceName}`, {\n eventId: event.id,\n });\n return;\n }\n\n await transport.emit(event);\n }\n\n /**\n * Get all registered service names\n */\n getServices(): string[] {\n return Array.from(this.transports.keys());\n }\n\n /**\n * Clear all registrations\n */\n clear(): void {\n this.transports.clear();\n }\n\n /**\n * Reset singleton (for testing)\n */\n static reset(): void {\n GlobalEventBus.instance = null;\n }\n}\n\n/**\n * Get the global event bus singleton.\n * Provides a shared event bus for communication between embedded services.\n *\n * @returns The global event bus instance\n *\n * @example\n * ```typescript\n * const bus = getGlobalEventBus();\n * bus.register('payments', paymentsTransport);\n * await bus.broadcast(event, 'payments'); // Exclude payments\n * ```\n */\nexport function getGlobalEventBus(): GlobalEventBus {\n return GlobalEventBus.getInstance();\n}\n","/**\n * @parsrun/service - Event Handler\n * Event handler registration and execution\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { createLogger } from \"@parsrun/core\";\nimport type {\n ParsEvent,\n EventHandler,\n EventHandlerContext,\n EventHandlerOptions,\n Unsubscribe,\n} from \"../types.js\";\nimport { matchEventType } from \"./format.js\";\nimport type { DeadLetterQueue } from \"./dead-letter.js\";\n\n// ============================================================================\n// EVENT HANDLER REGISTRY\n// ============================================================================\n\n/**\n * Resolved handler options with required fields\n */\nexport interface ResolvedHandlerOptions {\n retries: number;\n backoff: \"linear\" | \"exponential\";\n maxDelay: number;\n onExhausted: \"alert\" | \"log\" | \"discard\";\n deadLetter?: string;\n}\n\n/**\n * Registration entry for an event handler.\n */\nexport interface HandlerRegistration {\n /** Event type pattern (supports wildcards) */\n pattern: string;\n /** Handler function */\n handler: EventHandler;\n /** Handler options */\n options: ResolvedHandlerOptions;\n}\n\n/**\n * Options for creating an event handler registry.\n */\nexport interface EventHandlerRegistryOptions {\n /** Logger */\n logger?: Logger;\n /** Dead letter queue */\n deadLetterQueue?: DeadLetterQueue;\n /** Default handler options */\n defaultOptions?: Partial<EventHandlerOptions>;\n}\n\n/**\n * Registry for event handlers\n */\nexport class EventHandlerRegistry {\n private readonly handlers: Map<string, HandlerRegistration[]> = new Map();\n private readonly logger: Logger;\n private readonly deadLetterQueue?: DeadLetterQueue;\n private readonly defaultOptions: ResolvedHandlerOptions;\n\n constructor(options: EventHandlerRegistryOptions = {}) {\n this.logger = options.logger ?? createLogger({ name: \"event-handler\" });\n if (options.deadLetterQueue) {\n this.deadLetterQueue = options.deadLetterQueue;\n }\n const defaultOpts: ResolvedHandlerOptions = {\n retries: options.defaultOptions?.retries ?? 3,\n backoff: options.defaultOptions?.backoff ?? \"exponential\",\n maxDelay: options.defaultOptions?.maxDelay ?? 30_000,\n onExhausted: options.defaultOptions?.onExhausted ?? \"log\",\n };\n if (options.defaultOptions?.deadLetter) {\n defaultOpts.deadLetter = options.defaultOptions.deadLetter;\n }\n this.defaultOptions = defaultOpts;\n }\n\n /**\n * Register an event handler\n */\n register(\n pattern: string,\n handler: EventHandler,\n options?: EventHandlerOptions\n ): Unsubscribe {\n const registration: HandlerRegistration = {\n pattern,\n handler,\n options: {\n ...this.defaultOptions,\n ...options,\n },\n };\n\n const handlers = this.handlers.get(pattern) ?? [];\n handlers.push(registration);\n this.handlers.set(pattern, handlers);\n\n this.logger.debug(`Handler registered for pattern: ${pattern}`);\n\n // Return unsubscribe function\n return () => {\n const currentHandlers = this.handlers.get(pattern);\n if (currentHandlers) {\n const index = currentHandlers.indexOf(registration);\n if (index !== -1) {\n currentHandlers.splice(index, 1);\n if (currentHandlers.length === 0) {\n this.handlers.delete(pattern);\n }\n this.logger.debug(`Handler unregistered for pattern: ${pattern}`);\n }\n }\n };\n }\n\n /**\n * Handle an event\n */\n async handle(event: ParsEvent): Promise<void> {\n const matchingHandlers = this.getMatchingHandlers(event.type);\n\n if (matchingHandlers.length === 0) {\n this.logger.debug(`No handlers for event type: ${event.type}`, {\n eventId: event.id,\n });\n return;\n }\n\n this.logger.debug(`Handling event: ${event.type}`, {\n eventId: event.id,\n handlerCount: matchingHandlers.length,\n });\n\n // Execute handlers in parallel\n const results = await Promise.allSettled(\n matchingHandlers.map((reg) => this.executeHandler(event, reg))\n );\n\n // Log failures\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n if (result?.status === \"rejected\") {\n this.logger.error(\n `Handler failed for ${event.type}`,\n result.reason as Error,\n { eventId: event.id, pattern: matchingHandlers[i]?.pattern }\n );\n }\n }\n }\n\n /**\n * Execute a single handler with retry logic\n */\n private async executeHandler(\n event: ParsEvent,\n registration: HandlerRegistration\n ): Promise<void> {\n const { handler, options } = registration;\n const maxAttempts = options.retries + 1;\n let lastError: Error | undefined;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const context: EventHandlerContext = {\n logger: this.logger.child({\n eventId: event.id,\n pattern: registration.pattern,\n attempt,\n }),\n attempt,\n maxAttempts,\n isRetry: attempt > 1,\n };\n\n // Add trace context if available\n if (event.parstracecontext) {\n const traceCtx = parseTraceContext(event.parstracecontext);\n if (traceCtx) {\n context.traceContext = traceCtx;\n }\n }\n\n await handler(event, context);\n return; // Success\n } catch (error) {\n lastError = error as Error;\n\n if (attempt < maxAttempts) {\n const delay = this.calculateBackoff(attempt, options);\n this.logger.warn(\n `Handler failed, retrying in ${delay}ms`,\n { eventId: event.id, attempt, maxAttempts }\n );\n await sleep(delay);\n }\n }\n }\n\n // All retries exhausted\n await this.handleExhausted(event, registration, lastError!);\n }\n\n /**\n * Calculate backoff delay\n */\n private calculateBackoff(\n attempt: number,\n options: ResolvedHandlerOptions\n ): number {\n const baseDelay = 100;\n\n if (options.backoff === \"exponential\") {\n return Math.min(baseDelay * Math.pow(2, attempt - 1), options.maxDelay);\n }\n\n // Linear\n return Math.min(baseDelay * attempt, options.maxDelay);\n }\n\n /**\n * Handle exhausted retries\n */\n private async handleExhausted(\n event: ParsEvent,\n registration: HandlerRegistration,\n error: Error\n ): Promise<void> {\n const { options } = registration;\n\n // Send to dead letter queue\n if (options.deadLetter && this.deadLetterQueue) {\n await this.deadLetterQueue.add({\n event,\n error: error.message,\n pattern: registration.pattern,\n attempts: options.retries + 1,\n });\n }\n\n // Handle based on onExhausted option\n switch (options.onExhausted) {\n case \"alert\":\n this.logger.error(\n `[ALERT] Event handler exhausted all retries`,\n error,\n {\n eventId: event.id,\n eventType: event.type,\n pattern: registration.pattern,\n }\n );\n break;\n case \"discard\":\n this.logger.debug(`Event discarded after exhausted retries`, {\n eventId: event.id,\n });\n break;\n case \"log\":\n default:\n this.logger.warn(`Event handler exhausted all retries`, {\n eventId: event.id,\n error: error.message,\n });\n }\n }\n\n /**\n * Get handlers matching an event type\n */\n private getMatchingHandlers(eventType: string): HandlerRegistration[] {\n const matching: HandlerRegistration[] = [];\n\n for (const [pattern, handlers] of this.handlers) {\n if (matchEventType(eventType, pattern)) {\n matching.push(...handlers);\n }\n }\n\n return matching;\n }\n\n /**\n * Get all registered patterns\n */\n getPatterns(): string[] {\n return Array.from(this.handlers.keys());\n }\n\n /**\n * Check if a pattern has handlers\n */\n hasHandlers(pattern: string): boolean {\n return this.handlers.has(pattern);\n }\n\n /**\n * Clear all handlers\n */\n clear(): void {\n this.handlers.clear();\n }\n}\n\n/**\n * Create an event handler registry for managing event subscriptions.\n *\n * @param options - Registry configuration options\n * @returns A new event handler registry instance\n *\n * @example\n * ```typescript\n * const registry = createEventHandlerRegistry({\n * deadLetterQueue: dlq,\n * defaultOptions: { retries: 3 },\n * });\n * registry.register('subscription.*', async (event, ctx) => {\n * console.log('Received event:', event.type);\n * });\n * ```\n */\nexport function createEventHandlerRegistry(\n options?: EventHandlerRegistryOptions\n): EventHandlerRegistry {\n return new EventHandlerRegistry(options);\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction parseTraceContext(traceparent: string): {\n traceId: string;\n spanId: string;\n traceFlags: number;\n} | undefined {\n const parts = traceparent.split(\"-\");\n if (parts.length !== 4) return undefined;\n\n const [, traceId, spanId, flags] = parts;\n if (!traceId || !spanId || !flags) return undefined;\n\n return {\n traceId,\n spanId,\n traceFlags: parseInt(flags, 16),\n };\n}\n","/**\n * @parsrun/service - Event Format\n * CloudEvents and compact event format utilities\n */\n\nimport { generateId } from \"@parsrun/core\";\nimport type { ParsEvent, CompactEvent, TraceContext } from \"../types.js\";\n\n// ============================================================================\n// EVENT CREATION\n// ============================================================================\n\n/**\n * Options for creating a CloudEvents-compatible event.\n */\nexport interface CreateEventOptions<T = unknown> {\n /** Event type (e.g., \"subscription.created\") */\n type: string;\n /** Source service */\n source: string;\n /** Event data */\n data: T;\n /** Optional event ID (auto-generated if not provided) */\n id?: string;\n /** Optional subject */\n subject?: string;\n /** Tenant ID */\n tenantId?: string;\n /** Request ID for correlation */\n requestId?: string;\n /** Trace context */\n traceContext?: TraceContext;\n /** Delivery guarantee */\n delivery?: \"at-most-once\" | \"at-least-once\";\n}\n\n/**\n * Create a CloudEvents-compatible event.\n *\n * @param options - Event creation options\n * @returns A new ParsEvent conforming to CloudEvents spec\n */\nexport function createEvent<T = unknown>(options: CreateEventOptions<T>): ParsEvent<T> {\n const event: ParsEvent<T> = {\n specversion: \"1.0\",\n type: options.type,\n source: options.source,\n id: options.id ?? generateId(),\n time: new Date().toISOString(),\n datacontenttype: \"application/json\",\n data: options.data,\n };\n\n if (options.subject) event.subject = options.subject;\n if (options.tenantId) event.parstenantid = options.tenantId;\n if (options.requestId) event.parsrequestid = options.requestId;\n if (options.traceContext) event.parstracecontext = formatTraceContext(options.traceContext);\n if (options.delivery) event.parsdelivery = options.delivery;\n\n return event;\n}\n\n// ============================================================================\n// FORMAT CONVERSION\n// ============================================================================\n\n/**\n * Convert to full CloudEvents format (creates a copy).\n *\n * @param event - The event to convert\n * @returns A copy of the event in CloudEvents format\n */\nexport function toCloudEvent<T>(event: ParsEvent<T>): ParsEvent<T> {\n return { ...event };\n}\n\n/**\n * Convert to compact internal format for efficient transport.\n *\n * @param event - The CloudEvents event to convert\n * @returns A compact representation of the event\n */\nexport function toCompactEvent<T>(event: ParsEvent<T>): CompactEvent<T> {\n const compact: CompactEvent<T> = {\n e: event.type,\n s: event.source,\n i: event.id,\n t: new Date(event.time).getTime(),\n d: event.data,\n };\n\n if (event.parstracecontext) compact.ctx = event.parstracecontext;\n if (event.parstenantid) compact.tid = event.parstenantid;\n\n return compact;\n}\n\n/**\n * Convert from compact format to CloudEvents format.\n *\n * @param compact - The compact event to convert\n * @param source - Optional source override\n * @returns A full CloudEvents event\n */\nexport function fromCompactEvent<T>(compact: CompactEvent<T>, source?: string): ParsEvent<T> {\n const event: ParsEvent<T> = {\n specversion: \"1.0\",\n type: compact.e,\n source: source ?? compact.s,\n id: compact.i,\n time: new Date(compact.t).toISOString(),\n datacontenttype: \"application/json\",\n data: compact.d,\n };\n\n if (compact.ctx) event.parstracecontext = compact.ctx;\n if (compact.tid) event.parstenantid = compact.tid;\n\n return event;\n}\n\n// ============================================================================\n// EVENT TYPE UTILITIES\n// ============================================================================\n\n/**\n * Format full event type with source prefix.\n *\n * @param source - The source service name\n * @param type - The event type\n * @returns Fully qualified event type\n *\n * @example\n * ```typescript\n * formatEventType('payments', 'subscription.created')\n * // Returns: 'com.pars.payments.subscription.created'\n * ```\n */\nexport function formatEventType(source: string, type: string): string {\n return `com.pars.${source}.${type}`;\n}\n\n/**\n * Parse event type to extract source and type.\n *\n * @param fullType - The fully qualified event type\n * @returns Parsed source and type, or null if invalid\n *\n * @example\n * ```typescript\n * parseEventType('com.pars.payments.subscription.created')\n * // Returns: { source: 'payments', type: 'subscription.created' }\n * ```\n */\nexport function parseEventType(fullType: string): { source: string; type: string } | null {\n const prefix = \"com.pars.\";\n if (!fullType.startsWith(prefix)) {\n // Try to parse as simple type (source.type)\n const parts = fullType.split(\".\");\n if (parts.length >= 2) {\n const [source, ...rest] = parts;\n return { source: source!, type: rest.join(\".\") };\n }\n return null;\n }\n\n const withoutPrefix = fullType.slice(prefix.length);\n const dotIndex = withoutPrefix.indexOf(\".\");\n if (dotIndex === -1) {\n return null;\n }\n\n return {\n source: withoutPrefix.slice(0, dotIndex),\n type: withoutPrefix.slice(dotIndex + 1),\n };\n}\n\n/**\n * Check if event type matches a pattern.\n * Supports wildcards: * matches one segment, ** matches multiple segments.\n *\n * @param type - The event type to check\n * @param pattern - The pattern to match against\n * @returns True if the type matches the pattern\n *\n * @example\n * ```typescript\n * matchEventType('subscription.created', 'subscription.*') // true\n * matchEventType('payment.invoice.paid', 'payment.**') // true\n * matchEventType('subscription.created', 'payment.*') // false\n * ```\n */\nexport function matchEventType(type: string, pattern: string): boolean {\n if (pattern === \"*\" || pattern === \"**\") {\n return true;\n }\n\n const typeParts = type.split(\".\");\n const patternParts = pattern.split(\".\");\n\n let ti = 0;\n let pi = 0;\n\n while (ti < typeParts.length && pi < patternParts.length) {\n const pp = patternParts[pi];\n\n if (pp === \"**\") {\n // ** matches rest of type\n if (pi === patternParts.length - 1) {\n return true;\n }\n // Try to match remaining pattern\n for (let i = ti; i <= typeParts.length; i++) {\n const remaining = typeParts.slice(i).join(\".\");\n const remainingPattern = patternParts.slice(pi + 1).join(\".\");\n if (matchEventType(remaining, remainingPattern)) {\n return true;\n }\n }\n return false;\n }\n\n if (pp === \"*\") {\n // * matches single segment\n ti++;\n pi++;\n continue;\n }\n\n if (pp !== typeParts[ti]) {\n return false;\n }\n\n ti++;\n pi++;\n }\n\n return ti === typeParts.length && pi === patternParts.length;\n}\n\n// ============================================================================\n// TRACE CONTEXT HELPERS\n// ============================================================================\n\n/**\n * Format trace context to W3C traceparent string\n */\nfunction formatTraceContext(ctx: TraceContext): string {\n const flags = ctx.traceFlags.toString(16).padStart(2, \"0\");\n return `00-${ctx.traceId}-${ctx.spanId}-${flags}`;\n}\n\n/**\n * Parse W3C traceparent string to trace context.\n *\n * @param traceparent - The traceparent header value\n * @returns Parsed trace context, or null if invalid\n */\nexport function parseTraceContext(traceparent: string): TraceContext | null {\n const parts = traceparent.split(\"-\");\n if (parts.length !== 4) {\n return null;\n }\n\n const [version, traceId, spanId, flags] = parts;\n if (version !== \"00\" || !traceId || !spanId || !flags) {\n return null;\n }\n\n return {\n traceId,\n spanId,\n traceFlags: parseInt(flags, 16),\n };\n}\n\n// ============================================================================\n// VALIDATION\n// ============================================================================\n\n/**\n * Validate that an object conforms to CloudEvents structure.\n *\n * @param event - The object to validate\n * @returns True if the object is a valid ParsEvent\n */\nexport function validateEvent(event: unknown): event is ParsEvent {\n if (!event || typeof event !== \"object\") {\n return false;\n }\n\n const e = event as Record<string, unknown>;\n\n // Required fields\n if (e[\"specversion\"] !== \"1.0\") return false;\n if (typeof e[\"type\"] !== \"string\" || (e[\"type\"] as string).length === 0) return false;\n if (typeof e[\"source\"] !== \"string\" || (e[\"source\"] as string).length === 0) return false;\n if (typeof e[\"id\"] !== \"string\" || (e[\"id\"] as string).length === 0) return false;\n if (typeof e[\"time\"] !== \"string\") return false;\n\n return true;\n}\n\n/**\n * Validate that an object conforms to compact event structure.\n *\n * @param event - The object to validate\n * @returns True if the object is a valid CompactEvent\n */\nexport function validateCompactEvent(event: unknown): event is CompactEvent {\n if (!event || typeof event !== \"object\") {\n return false;\n }\n\n const e = event as Record<string, unknown>;\n\n if (typeof e[\"e\"] !== \"string\" || (e[\"e\"] as string).length === 0) return false;\n if (typeof e[\"s\"] !== \"string\" || (e[\"s\"] as string).length === 0) return false;\n if (typeof e[\"i\"] !== \"string\" || (e[\"i\"] as string).length === 0) return false;\n if (typeof e[\"t\"] !== \"number\") return false;\n\n return true;\n}\n","/**\n * @parsrun/service - Tracer\n * High-level tracing API\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { createLogger } from \"@parsrun/core\";\nimport type { Span, TraceContext, SpanKind, SpanAttributeValue } from \"../types.js\";\nimport type { TracingConfig } from \"../types.js\";\nimport {\n TraceContextManager,\n shouldSample,\n type Sampler,\n} from \"./context.js\";\nimport { SpanManager, SpanAttributes } from \"./spans.js\";\nimport type { SpanExporter } from \"./exporters.js\";\nimport { ConsoleExporter } from \"./exporters.js\";\n\n// ============================================================================\n// TRACER\n// ============================================================================\n\n/**\n * Options for creating a tracer.\n */\nexport interface TracerOptions {\n /** Service name */\n serviceName: string;\n /** Service version */\n serviceVersion?: string;\n /** Tracing config */\n config?: TracingConfig;\n /** Span exporter */\n exporter?: SpanExporter;\n /** Logger */\n logger?: Logger;\n}\n\n/**\n * Main tracer class for distributed tracing\n */\nexport class Tracer {\n private readonly serviceName: string;\n private readonly serviceVersion: string;\n private readonly sampler: Sampler;\n private readonly exporter: SpanExporter;\n private readonly logger: Logger;\n private readonly contextManager: TraceContextManager;\n private readonly spanManager: SpanManager;\n private readonly enabled: boolean;\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: TracerOptions) {\n this.serviceName = options.serviceName;\n this.serviceVersion = options.serviceVersion ?? \"1.0.0\";\n this.enabled = options.config?.enabled ?? true;\n this.sampler = options.config?.sampler ?? { ratio: 0.1 };\n this.exporter = options.exporter ?? new ConsoleExporter();\n this.logger = options.logger ?? createLogger({ name: `tracer:${options.serviceName}` });\n this.contextManager = new TraceContextManager();\n this.spanManager = new SpanManager();\n\n // Auto-flush spans periodically\n if (this.enabled) {\n this.flushTimer = setInterval(() => this.flush(), 5000);\n }\n }\n\n /**\n * Start a new span\n */\n startSpan(\n name: string,\n options?: {\n kind?: SpanKind;\n parent?: TraceContext;\n attributes?: Record<string, SpanAttributeValue>;\n }\n ): Span | null {\n if (!this.enabled) return null;\n\n // Get parent from options or current context\n const parent = options?.parent ?? this.contextManager.current();\n\n // Check sampling\n if (!parent && !shouldSample(this.sampler)) {\n return null;\n }\n\n const spanOptions: Parameters<typeof this.spanManager.startSpan>[0] = {\n name,\n kind: options?.kind ?? \"internal\",\n attributes: {\n [SpanAttributes.SERVICE_NAME]: this.serviceName,\n [SpanAttributes.SERVICE_VERSION]: this.serviceVersion,\n ...options?.attributes,\n },\n };\n\n if (parent) {\n spanOptions.parent = parent;\n }\n\n const span = this.spanManager.startSpan(spanOptions);\n\n return span;\n }\n\n /**\n * End a span\n */\n endSpan(span: Span | null, error?: Error): void {\n if (!span) return;\n\n if (error) {\n this.spanManager.recordException(span, error);\n }\n\n this.spanManager.endSpan(span, error ? \"error\" : \"ok\");\n }\n\n /**\n * Run a function with automatic span creation\n */\n async trace<T>(\n name: string,\n fn: (span: Span | null) => Promise<T>,\n options?: {\n kind?: SpanKind;\n attributes?: Record<string, SpanAttributeValue>;\n }\n ): Promise<T> {\n const span = this.startSpan(name, options);\n\n if (!span) {\n return fn(null);\n }\n\n try {\n const result = await this.contextManager.run(span.traceContext, () => fn(span));\n this.endSpan(span);\n return result;\n } catch (error) {\n this.endSpan(span, error as Error);\n throw error;\n }\n }\n\n /**\n * Add attribute to current span\n */\n setAttribute(key: string, value: SpanAttributeValue): void {\n const ctx = this.contextManager.current();\n if (!ctx) return;\n\n const span = this.spanManager.getSpan(ctx.spanId);\n if (span) {\n this.spanManager.setAttribute(span, key, value);\n }\n }\n\n /**\n * Add event to current span\n */\n addEvent(name: string, attributes?: Record<string, SpanAttributeValue>): void {\n const ctx = this.contextManager.current();\n if (!ctx) return;\n\n const span = this.spanManager.getSpan(ctx.spanId);\n if (span) {\n this.spanManager.addEvent(span, name, attributes);\n }\n }\n\n /**\n * Get current trace context\n */\n currentContext(): TraceContext | undefined {\n return this.contextManager.current();\n }\n\n /**\n * Get context manager for advanced use\n */\n getContextManager(): TraceContextManager {\n return this.contextManager;\n }\n\n /**\n * Extract trace context from incoming request\n */\n extract(headers: Headers | Record<string, string>): TraceContext | undefined {\n return this.contextManager.fromHeaders(headers);\n }\n\n /**\n * Inject trace context into outgoing request headers\n */\n inject(ctx?: TraceContext): Record<string, string> {\n const context = ctx ?? this.contextManager.current();\n if (!context) return {};\n return this.contextManager.toHeaders(context);\n }\n\n /**\n * Flush completed spans to exporter\n */\n async flush(): Promise<void> {\n const spans = this.spanManager.flush();\n if (spans.length > 0) {\n try {\n await this.exporter.export(spans);\n } catch (error) {\n this.logger.error(\"Failed to export spans\", error as Error);\n }\n }\n }\n\n /**\n * Shutdown tracer\n */\n async shutdown(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n await this.flush();\n await this.exporter.shutdown();\n this.spanManager.clear();\n this.contextManager.clear();\n }\n}\n\n/**\n * Create a tracer for distributed tracing.\n *\n * @param options - Tracer configuration options\n * @returns A new tracer instance\n *\n * @example\n * ```typescript\n * const tracer = createTracer({\n * serviceName: 'payments',\n * config: { sampler: { ratio: 0.1 } },\n * });\n * await tracer.trace('processPayment', async (span) => {\n * // Processing logic\n * });\n * ```\n */\nexport function createTracer(options: TracerOptions): Tracer {\n return new Tracer(options);\n}\n\n// ============================================================================\n// GLOBAL TRACER\n// ============================================================================\n\nlet globalTracer: Tracer | null = null;\n\n/**\n * Get the global tracer instance\n */\nexport function getGlobalTracer(): Tracer | null {\n return globalTracer;\n}\n\n/**\n * Set the global tracer instance\n */\nexport function setGlobalTracer(tracer: Tracer): void {\n globalTracer = tracer;\n}\n\n/**\n * Reset the global tracer (for testing)\n */\nexport function resetGlobalTracer(): void {\n globalTracer = null;\n}\n\n// ============================================================================\n// MIDDLEWARE HELPERS\n// ============================================================================\n\n/**\n * Create HTTP server tracing middleware.\n * Automatically creates spans for incoming HTTP requests.\n *\n * @param tracer - The tracer instance to use\n * @returns Middleware function for HTTP frameworks\n */\nexport function createTracingMiddleware(tracer: Tracer) {\n return async (\n request: Request,\n next: () => Promise<Response>\n ): Promise<Response> => {\n // Extract trace context from incoming request\n const parentCtx = tracer.extract(request.headers);\n\n // Start server span\n const url = new URL(request.url);\n const spanOpts: {\n kind: SpanKind;\n parent?: TraceContext;\n attributes: Record<string, SpanAttributeValue>;\n } = {\n kind: \"server\",\n attributes: {\n [SpanAttributes.HTTP_METHOD]: request.method,\n [SpanAttributes.HTTP_URL]: request.url,\n },\n };\n if (parentCtx) {\n spanOpts.parent = parentCtx;\n }\n const span = tracer.startSpan(`${request.method} ${url.pathname}`, spanOpts);\n\n if (!span) {\n return next();\n }\n\n try {\n const response = await tracer.getContextManager().run(span.traceContext, next);\n\n tracer.endSpan(span);\n span.attributes[SpanAttributes.HTTP_STATUS_CODE] = response.status;\n\n return response;\n } catch (error) {\n tracer.endSpan(span, error as Error);\n throw error;\n }\n };\n}\n\n/**\n * Create RPC tracing helpers for instrumenting RPC calls.\n * Provides methods to trace outgoing calls and incoming handlers.\n *\n * @param tracer - The tracer instance to use\n * @returns Object with traceCall and traceHandler methods\n */\nexport function createRpcTracing(tracer: Tracer) {\n return {\n /**\n * Trace an outgoing RPC call\n */\n async traceCall<T>(\n service: string,\n method: string,\n fn: () => Promise<T>\n ): Promise<T> {\n return tracer.trace(\n `rpc.${service}.${method}`,\n fn,\n {\n kind: \"client\",\n attributes: {\n [SpanAttributes.RPC_SYSTEM]: \"pars\",\n [SpanAttributes.RPC_SERVICE]: service,\n [SpanAttributes.RPC_METHOD]: method,\n },\n }\n );\n },\n\n /**\n * Trace an incoming RPC request\n */\n async traceHandler<T>(\n service: string,\n method: string,\n fn: () => Promise<T>,\n parentCtx?: TraceContext\n ): Promise<T> {\n const handlerOpts: {\n kind: SpanKind;\n parent?: TraceContext;\n attributes: Record<string, SpanAttributeValue>;\n } = {\n kind: \"server\",\n attributes: {\n [SpanAttributes.RPC_SYSTEM]: \"pars\",\n [SpanAttributes.RPC_SERVICE]: service,\n [SpanAttributes.RPC_METHOD]: method,\n },\n };\n if (parentCtx) {\n handlerOpts.parent = parentCtx;\n }\n const span = tracer.startSpan(`rpc.${service}.${method}`, handlerOpts);\n\n if (!span) {\n return fn();\n }\n\n try {\n const result = await tracer.getContextManager().run(span.traceContext, fn);\n tracer.endSpan(span);\n return result;\n } catch (error) {\n tracer.endSpan(span, error as Error);\n throw error;\n }\n },\n };\n}\n","/**\n * @parsrun/service - Trace Exporters\n * Console and OTLP exporters for spans\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { createLogger } from \"@parsrun/core\";\nimport type { Span } from \"../types.js\";\nimport { spanToLogObject, getSpanDuration } from \"./spans.js\";\n\n// ============================================================================\n// EXPORTER INTERFACE\n// ============================================================================\n\n/**\n * Interface for span exporters.\n * Exporters receive completed spans and send them to a backend.\n */\nexport interface SpanExporter {\n /** Exporter name */\n readonly name: string;\n /** Export spans to the backend */\n export(spans: Span[]): Promise<void>;\n /** Shutdown exporter and flush pending spans */\n shutdown(): Promise<void>;\n}\n\n/**\n * Base options for span exporters.\n */\nexport interface ExporterOptions {\n /** Logger */\n logger?: Logger;\n}\n\n// ============================================================================\n// CONSOLE EXPORTER\n// ============================================================================\n\n/**\n * Options for the console exporter.\n */\nexport interface ConsoleExporterOptions extends ExporterOptions {\n /** Pretty print (default: true in dev) */\n pretty?: boolean;\n /** Include attributes (default: true) */\n includeAttributes?: boolean;\n /** Include events (default: true) */\n includeEvents?: boolean;\n}\n\n/**\n * Console exporter for development and debugging.\n * Outputs spans to the console with optional pretty formatting.\n */\nexport class ConsoleExporter implements SpanExporter {\n readonly name = \"console\";\n private readonly logger: Logger;\n private readonly pretty: boolean;\n private readonly includeAttributes: boolean;\n private readonly includeEvents: boolean;\n\n constructor(options: ConsoleExporterOptions = {}) {\n this.logger = options.logger ?? createLogger({ name: \"trace-exporter\" });\n this.pretty = options.pretty ?? true;\n this.includeAttributes = options.includeAttributes ?? true;\n this.includeEvents = options.includeEvents ?? true;\n }\n\n async export(spans: Span[]): Promise<void> {\n for (const span of spans) {\n const duration = getSpanDuration(span);\n const status = span.status === \"error\" ? \"ERROR\" : span.status === \"ok\" ? \"OK\" : \"UNSET\";\n\n if (this.pretty) {\n const indent = span.parentSpanId ? \" └─\" : \"──\";\n const statusIcon = span.status === \"error\" ? \"✗\" : span.status === \"ok\" ? \"✓\" : \"○\";\n const durationStr = duration !== undefined ? `${duration}ms` : \"?ms\";\n\n console.log(\n `${indent} ${statusIcon} [${span.kind}] ${span.name} (${durationStr}) trace=${span.traceContext.traceId.slice(0, 8)}`\n );\n\n if (this.includeAttributes && Object.keys(span.attributes).length > 0) {\n console.log(` attributes:`, span.attributes);\n }\n\n if (this.includeEvents && span.events.length > 0) {\n for (const event of span.events) {\n console.log(` event: ${event.name}`, event.attributes ?? \"\");\n }\n }\n } else {\n const logObj = spanToLogObject(span);\n this.logger.info(`Span: ${span.name}`, {\n ...logObj,\n status,\n durationMs: duration,\n });\n }\n }\n }\n\n async shutdown(): Promise<void> {\n // No cleanup needed\n }\n}\n\n/**\n * Create a console exporter for local development.\n *\n * @param options - Exporter configuration options\n * @returns A new console exporter instance\n */\nexport function createConsoleExporter(options?: ConsoleExporterOptions): ConsoleExporter {\n return new ConsoleExporter(options);\n}\n\n// ============================================================================\n// OTLP EXPORTER\n// ============================================================================\n\n/**\n * Options for the OTLP exporter.\n */\nexport interface OtlpExporterOptions extends ExporterOptions {\n /** OTLP endpoint URL */\n endpoint: string;\n /** Service name */\n serviceName: string;\n /** Service version */\n serviceVersion?: string;\n /** Custom headers */\n headers?: Record<string, string>;\n /** Timeout in ms */\n timeout?: number;\n /** Batch size */\n batchSize?: number;\n /** Flush interval in ms */\n flushInterval?: number;\n}\n\n/**\n * OTLP exporter for production tracing.\n * Sends spans to an OpenTelemetry-compatible backend via HTTP.\n */\nexport class OtlpExporter implements SpanExporter {\n readonly name = \"otlp\";\n private readonly endpoint: string;\n private readonly serviceName: string;\n private readonly serviceVersion: string;\n private readonly headers: Record<string, string>;\n private readonly timeout: number;\n private readonly batchSize: number;\n private readonly flushInterval: number;\n private readonly logger: Logger;\n private readonly buffer: Span[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: OtlpExporterOptions) {\n this.endpoint = options.endpoint.replace(/\\/$/, \"\");\n this.serviceName = options.serviceName;\n this.serviceVersion = options.serviceVersion ?? \"1.0.0\";\n this.headers = options.headers ?? {};\n this.timeout = options.timeout ?? 10_000;\n this.batchSize = options.batchSize ?? 100;\n this.flushInterval = options.flushInterval ?? 5_000;\n this.logger = options.logger ?? createLogger({ name: \"otlp-exporter\" });\n\n // Start flush timer\n this.flushTimer = setInterval(() => this.flush(), this.flushInterval);\n }\n\n async export(spans: Span[]): Promise<void> {\n this.buffer.push(...spans);\n\n if (this.buffer.length >= this.batchSize) {\n await this.flush();\n }\n }\n\n private async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n\n const spansToExport = this.buffer.splice(0, this.batchSize);\n\n try {\n const payload = this.buildOtlpPayload(spansToExport);\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(`${this.endpoint}/v1/traces`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...this.headers,\n },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`OTLP export failed: ${response.status} ${response.statusText}`);\n }\n\n this.logger.debug(`Exported ${spansToExport.length} spans to OTLP`);\n } finally {\n clearTimeout(timeoutId);\n }\n } catch (error) {\n this.logger.error(`Failed to export spans to OTLP`, error as Error);\n // Put spans back in buffer for retry\n this.buffer.unshift(...spansToExport);\n }\n }\n\n private buildOtlpPayload(spans: Span[]): OtlpTracePayload {\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: \"service.name\", value: { stringValue: this.serviceName } },\n { key: \"service.version\", value: { stringValue: this.serviceVersion } },\n ],\n },\n scopeSpans: [\n {\n scope: {\n name: \"@parsrun/service\",\n version: \"0.1.0\",\n },\n spans: spans.map((span) => this.convertSpan(span)),\n },\n ],\n },\n ],\n };\n }\n\n private convertSpan(span: Span): OtlpSpan {\n const otlpSpan: OtlpSpan = {\n traceId: span.traceContext.traceId,\n spanId: span.traceContext.spanId,\n name: span.name,\n kind: this.convertSpanKind(span.kind),\n startTimeUnixNano: String(span.startTime * 1_000_000),\n attributes: Object.entries(span.attributes).map(([key, value]) => ({\n key,\n value: this.convertAttributeValue(value),\n })),\n events: span.events.map((event) => ({\n name: event.name,\n timeUnixNano: String(event.time * 1_000_000),\n attributes: event.attributes\n ? Object.entries(event.attributes).map(([key, value]) => ({\n key,\n value: this.convertAttributeValue(value),\n }))\n : [],\n })),\n status: {\n code: span.status === \"error\" ? 2 : span.status === \"ok\" ? 1 : 0,\n },\n };\n\n if (span.parentSpanId) {\n otlpSpan.parentSpanId = span.parentSpanId;\n }\n if (span.endTime) {\n otlpSpan.endTimeUnixNano = String(span.endTime * 1_000_000);\n }\n\n return otlpSpan;\n }\n\n private convertSpanKind(kind: string): number {\n switch (kind) {\n case \"internal\":\n return 1;\n case \"server\":\n return 2;\n case \"client\":\n return 3;\n case \"producer\":\n return 4;\n case \"consumer\":\n return 5;\n default:\n return 0;\n }\n }\n\n private convertAttributeValue(value: unknown): OtlpAttributeValue {\n if (typeof value === \"string\") {\n return { stringValue: value };\n }\n if (typeof value === \"number\") {\n if (Number.isInteger(value)) {\n return { intValue: String(value) };\n }\n return { doubleValue: value };\n }\n if (typeof value === \"boolean\") {\n return { boolValue: value };\n }\n if (Array.isArray(value)) {\n return {\n arrayValue: {\n values: value.map((v) => this.convertAttributeValue(v)),\n },\n };\n }\n return { stringValue: String(value) };\n }\n\n async shutdown(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n await this.flush();\n }\n}\n\n/**\n * Create an OTLP exporter for production tracing.\n *\n * @param options - Exporter configuration options\n * @returns A new OTLP exporter instance\n *\n * @example\n * ```typescript\n * const exporter = createOtlpExporter({\n * endpoint: 'https://otel-collector.example.com:4318',\n * serviceName: 'payments',\n * });\n * ```\n */\nexport function createOtlpExporter(options: OtlpExporterOptions): OtlpExporter {\n return new OtlpExporter(options);\n}\n\n// ============================================================================\n// OTLP TYPES\n// ============================================================================\n\ninterface OtlpTracePayload {\n resourceSpans: Array<{\n resource: {\n attributes: OtlpAttribute[];\n };\n scopeSpans: Array<{\n scope: {\n name: string;\n version: string;\n };\n spans: OtlpSpan[];\n }>;\n }>;\n}\n\ninterface OtlpSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n name: string;\n kind: number;\n startTimeUnixNano: string;\n endTimeUnixNano?: string;\n attributes: OtlpAttribute[];\n events: Array<{\n name: string;\n timeUnixNano: string;\n attributes: OtlpAttribute[];\n }>;\n status: {\n code: number;\n };\n}\n\ninterface OtlpAttribute {\n key: string;\n value: OtlpAttributeValue;\n}\n\ninterface OtlpAttributeValue {\n stringValue?: string;\n intValue?: string;\n doubleValue?: number;\n boolValue?: boolean;\n arrayValue?: {\n values: OtlpAttributeValue[];\n };\n}\n"],"mappings":"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","names":["generateId","createLogger","generateId","sleep","sleep","createLogger","createLogger","createLogger","generateId"]}