@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/transports/cloudflare/binding.ts","../../../src/serialization/index.ts","../../../src/rpc/errors.ts","../../../src/rpc/transports/http.ts","../../../src/transports/cloudflare/queue.ts","../../../src/events/format.ts","../../../src/events/handler.ts","../../../src/transports/cloudflare/durable-object.ts"],"sourcesContent":["/**\n * @parsrun/service - Cloudflare Service Binding Transport\n * RPC transport using Cloudflare Workers Service Bindings\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { createLogger } from \"@parsrun/core\";\nimport type {\n RpcRequest,\n RpcResponse,\n RpcTransport,\n Fetcher,\n} from \"../../types.js\";\nimport { type Serializer, jsonSerializer } from \"../../serialization/index.js\";\nimport { TransportError, SerializationError } from \"../../rpc/errors.js\";\n\n// ============================================================================\n// SERVICE BINDING TRANSPORT\n// ============================================================================\n\nexport interface ServiceBindingTransportOptions {\n /** Service name */\n serviceName: string;\n /** Cloudflare service binding */\n binding: Fetcher;\n /** Serializer (default: JSON) */\n serializer?: Serializer;\n /** Logger */\n logger?: Logger;\n /** Timeout in ms */\n timeout?: number;\n}\n\n/**\n * RPC transport using Cloudflare Service Bindings\n *\n * Service bindings provide zero-latency, in-network communication\n * between Cloudflare Workers.\n */\nexport class ServiceBindingTransport implements RpcTransport {\n readonly name = \"service-binding\";\n private readonly serviceName: string;\n private readonly binding: Fetcher;\n private readonly serializer: Serializer;\n private readonly logger: Logger;\n\n constructor(options: ServiceBindingTransportOptions) {\n this.serviceName = options.serviceName;\n this.binding = options.binding;\n this.serializer = options.serializer ?? jsonSerializer;\n this.logger = options.logger ?? createLogger({ name: `binding:${options.serviceName}` });\n // Note: timeout option reserved for future AbortController support\n }\n\n async call<TInput, TOutput>(request: RpcRequest<TInput>): Promise<RpcResponse<TOutput>> {\n const startTime = Date.now();\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 // Build headers\n const headers: Record<string, string> = {\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 };\n\n if (request.version) {\n headers[\"X-Service-Version\"] = request.version;\n }\n\n if (request.traceContext) {\n headers[\"traceparent\"] = formatTraceparent(request.traceContext);\n if (request.traceContext.traceState) {\n headers[\"tracestate\"] = request.traceContext.traceState;\n }\n }\n\n if (request.metadata?.tenantId) {\n headers[\"X-Tenant-ID\"] = String(request.metadata.tenantId);\n }\n\n // Make request via service binding\n const response = await this.binding.fetch(\"http://internal/rpc\", {\n method: \"POST\",\n headers,\n body: typeof body === \"string\" ? body : body,\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 const duration = Date.now() - startTime;\n this.logger.debug(`RPC call completed`, {\n service: this.serviceName,\n method: request.method,\n durationMs: duration,\n success: responseData.success,\n });\n\n return responseData;\n } catch (error) {\n const duration = Date.now() - startTime;\n\n if (error instanceof SerializationError) {\n throw error;\n }\n\n this.logger.error(`RPC call failed`, error as Error, {\n service: this.serviceName,\n method: request.method,\n durationMs: duration,\n });\n\n throw new TransportError(\n `Service binding call failed: ${(error as Error).message}`,\n error as Error\n );\n }\n }\n\n async close(): Promise<void> {\n // Service bindings don't need cleanup\n }\n}\n\n/**\n * Create a service binding transport\n */\nexport function createServiceBindingTransport(\n options: ServiceBindingTransportOptions\n): ServiceBindingTransport {\n return new ServiceBindingTransport(options);\n}\n\n// ============================================================================\n// SERVICE BINDING HANDLER\n// ============================================================================\n\nimport type { RpcServer } from \"../../rpc/server.js\";\nimport { parseTraceparent } from \"../../rpc/transports/http.js\";\n\n/**\n * Create a request handler for service binding requests\n *\n * @example\n * ```typescript\n * // In your worker\n * export default {\n * fetch: createServiceBindingHandler(rpcServer),\n * };\n * ```\n */\nexport function createServiceBindingHandler(\n server: RpcServer,\n options?: {\n serializer?: Serializer;\n logger?: Logger;\n }\n): (request: Request) => Promise<Response> {\n const serializer = options?.serializer ?? jsonSerializer;\n const logger = options?.logger ?? createLogger({ name: \"binding-handler\" });\n\n return async (request: Request): Promise<Response> => {\n // Only handle POST to /rpc\n const url = new URL(request.url);\n if (request.method !== \"POST\" || url.pathname !== \"/rpc\") {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n try {\n // Parse request body\n const contentType = request.headers.get(\"Content-Type\") ?? \"\";\n let body: RpcRequest;\n\n if (contentType.includes(\"msgpack\")) {\n const buffer = await request.arrayBuffer();\n body = serializer.decode(buffer) as RpcRequest;\n } else {\n const text = await request.text();\n body = serializer.decode(text) as RpcRequest;\n }\n\n // Extract trace context\n const traceparent = request.headers.get(\"traceparent\");\n if (traceparent) {\n const parsedTrace = parseTraceparent(traceparent);\n if (parsedTrace) {\n body.traceContext = parsedTrace;\n const tracestate = request.headers.get(\"tracestate\");\n if (tracestate) {\n body.traceContext.traceState = tracestate;\n }\n }\n }\n\n // Extract tenant from header\n const tenantId = request.headers.get(\"X-Tenant-ID\");\n if (tenantId) {\n body.metadata = { ...body.metadata, tenantId };\n }\n\n // Handle request\n const response = await server.handle(body);\n\n // Serialize response\n const responseBody = serializer.encode(response);\n\n return new Response(\n typeof responseBody === \"string\" ? responseBody : responseBody,\n {\n status: response.success ? 200 : getHttpStatus(response.error?.code),\n headers: {\n \"Content-Type\": serializer.contentType,\n \"X-Request-ID\": body.id,\n },\n }\n );\n } catch (error) {\n logger.error(\"Handler error\", error as Error);\n\n return new Response(\n JSON.stringify({\n success: false,\n error: {\n code: \"INTERNAL_ERROR\",\n message: (error as Error).message,\n },\n }),\n {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n }\n );\n }\n };\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\nimport type { TraceContext } from \"../../types.js\";\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\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 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 - 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 - 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 - 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 - Cloudflare Queue Transport\n * Event transport using Cloudflare Queues\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 CompactEvent,\n} from \"../../types.js\";\nimport { toCompactEvent, fromCompactEvent } from \"../../events/format.js\";\nimport { EventHandlerRegistry } from \"../../events/handler.js\";\n\n// ============================================================================\n// CLOUDFLARE QUEUE TYPES\n// ============================================================================\n\n/**\n * Cloudflare Queue interface\n */\nexport interface CloudflareQueue {\n send(message: unknown, options?: { contentType?: string }): Promise<void>;\n sendBatch(\n messages: Array<{ body: unknown; contentType?: string }>\n ): Promise<void>;\n}\n\n/**\n * Queue message from Cloudflare\n */\nexport interface QueueMessage<T = unknown> {\n id: string;\n timestamp: Date;\n body: T;\n ack(): void;\n retry(): void;\n}\n\n/**\n * Queue batch from Cloudflare\n */\nexport interface QueueBatch<T = unknown> {\n queue: string;\n messages: QueueMessage<T>[];\n ackAll(): void;\n retryAll(): void;\n}\n\n// ============================================================================\n// CLOUDFLARE QUEUE TRANSPORT\n// ============================================================================\n\nexport interface CloudflareQueueTransportOptions {\n /** Cloudflare Queue binding */\n queue: CloudflareQueue;\n /** Queue name (for logging) */\n queueName?: string;\n /** Use compact event format */\n compact?: boolean;\n /** Logger */\n logger?: Logger;\n /** Batch size for sending */\n batchSize?: number;\n /** Flush interval in ms */\n flushInterval?: number;\n}\n\n/**\n * Event transport using Cloudflare Queues\n *\n * Events are sent to a Cloudflare Queue for async processing.\n * Handlers are registered to process events from the queue.\n */\nexport class CloudflareQueueTransport implements EventTransport {\n readonly name = \"cloudflare-queue\";\n private readonly queue: CloudflareQueue;\n private readonly queueName: string;\n private readonly compact: boolean;\n private readonly logger: Logger;\n private readonly batchSize: number;\n private readonly flushInterval: number;\n private readonly registry: EventHandlerRegistry;\n private readonly buffer: ParsEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: CloudflareQueueTransportOptions) {\n this.queue = options.queue;\n this.queueName = options.queueName ?? \"events\";\n this.compact = options.compact ?? true;\n this.logger = options.logger ?? createLogger({ name: `queue:${this.queueName}` });\n this.batchSize = options.batchSize ?? 100;\n this.flushInterval = options.flushInterval ?? 1000;\n this.registry = new EventHandlerRegistry({ logger: this.logger });\n\n // Start flush timer\n this.flushTimer = setInterval(() => this.flush(), this.flushInterval);\n }\n\n /**\n * Emit an event to the queue\n */\n async emit<T>(event: ParsEvent<T>): Promise<void> {\n this.buffer.push(event);\n\n if (this.buffer.length >= this.batchSize) {\n await this.flush();\n }\n }\n\n /**\n * Subscribe to events (for local handler registration)\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 * Flush buffered events to the queue\n */\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n\n const events = this.buffer.splice(0, this.batchSize);\n\n try {\n if (events.length === 1) {\n // Single event\n const body = this.compact\n ? toCompactEvent(events[0]!)\n : events[0];\n await this.queue.send(body);\n } else {\n // Batch send\n const messages = events.map((event) => ({\n body: this.compact ? toCompactEvent(event) : event,\n }));\n await this.queue.sendBatch(messages);\n }\n\n this.logger.debug(`Sent ${events.length} events to queue`, {\n queue: this.queueName,\n });\n } catch (error) {\n // Put events back in buffer for retry\n this.buffer.unshift(...events);\n this.logger.error(\"Failed to send events to queue\", error as Error);\n throw error;\n }\n }\n\n /**\n * Handle a queue message (called by queue consumer)\n */\n async handleMessage<T>(message: QueueMessage<T>): Promise<void> {\n try {\n const event = this.parseEvent(message.body);\n await this.registry.handle(event);\n message.ack();\n } catch (error) {\n this.logger.error(\"Failed to handle queue message\", error as Error, {\n messageId: message.id,\n });\n message.retry();\n }\n }\n\n /**\n * Handle a batch of queue messages\n */\n async handleBatch<T>(batch: QueueBatch<T>): Promise<void> {\n const results = await Promise.allSettled(\n batch.messages.map((msg) => this.handleMessage(msg))\n );\n\n const failures = results.filter((r) => r.status === \"rejected\");\n if (failures.length > 0) {\n this.logger.warn(`${failures.length}/${batch.messages.length} messages failed`);\n }\n }\n\n /**\n * Parse event from message body\n */\n private parseEvent(body: unknown): ParsEvent {\n if (this.compact && isCompactEvent(body)) {\n return fromCompactEvent(body as CompactEvent);\n }\n return body as ParsEvent;\n }\n\n /**\n * Close the transport\n */\n async close(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n await this.flush();\n this.registry.clear();\n }\n}\n\n/**\n * Create a Cloudflare Queue transport\n */\nexport function createCloudflareQueueTransport(\n options: CloudflareQueueTransportOptions\n): CloudflareQueueTransport {\n return new CloudflareQueueTransport(options);\n}\n\n// ============================================================================\n// QUEUE CONSUMER\n// ============================================================================\n\n/**\n * Create a queue consumer handler\n *\n * @example\n * ```typescript\n * // In your worker\n * const consumer = createQueueConsumer(registry);\n *\n * export default {\n * queue: consumer,\n * };\n * ```\n */\nexport function createQueueConsumer(\n registry: EventHandlerRegistry,\n options?: {\n compact?: boolean;\n logger?: Logger;\n }\n): (batch: QueueBatch) => Promise<void> {\n const compact = options?.compact ?? true;\n const logger = options?.logger ?? createLogger({ name: \"queue-consumer\" });\n\n return async (batch: QueueBatch): Promise<void> => {\n logger.info(`Processing batch of ${batch.messages.length} messages`, {\n queue: batch.queue,\n });\n\n for (const message of batch.messages) {\n try {\n let event: ParsEvent;\n\n if (compact && isCompactEvent(message.body)) {\n event = fromCompactEvent(message.body as CompactEvent);\n } else {\n event = message.body as ParsEvent;\n }\n\n await registry.handle(event);\n message.ack();\n } catch (error) {\n logger.error(\"Failed to process message\", error as Error, {\n messageId: message.id,\n });\n message.retry();\n }\n }\n };\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\nfunction isCompactEvent(body: unknown): boolean {\n if (!body || typeof body !== \"object\") return false;\n const obj = body as Record<string, unknown>;\n return (\n typeof obj[\"e\"] === \"string\" &&\n typeof obj[\"s\"] === \"string\" &&\n typeof obj[\"i\"] === \"string\" &&\n typeof obj[\"t\"] === \"number\"\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 - 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 - Durable Object Transport\n * RPC/Event transport using Cloudflare Durable Objects\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { createLogger } from \"@parsrun/core\";\nimport type {\n RpcRequest,\n RpcResponse,\n RpcTransport,\n ParsEvent,\n EventTransport,\n EventHandler,\n EventHandlerOptions,\n Unsubscribe,\n} from \"../../types.js\";\nimport { type Serializer, jsonSerializer } from \"../../serialization/index.js\";\nimport { TransportError } from \"../../rpc/errors.js\";\nimport { EventHandlerRegistry } from \"../../events/handler.js\";\n\n// ============================================================================\n// DURABLE OBJECT TYPES\n// ============================================================================\n\n/**\n * Durable Object namespace binding\n */\nexport interface DurableObjectNamespace {\n idFromName(name: string): DurableObjectId;\n idFromString(id: string): DurableObjectId;\n newUniqueId(): DurableObjectId;\n get(id: DurableObjectId): DurableObjectStub;\n}\n\n/**\n * Durable Object ID\n */\nexport interface DurableObjectId {\n toString(): string;\n}\n\n/**\n * Durable Object stub for making requests\n */\nexport interface DurableObjectStub {\n fetch(input: string | Request | URL, init?: RequestInit): Promise<Response>;\n}\n\n// ============================================================================\n// DURABLE OBJECT TRANSPORT\n// ============================================================================\n\nexport interface DurableObjectTransportOptions {\n /** Durable Object namespace */\n namespace: DurableObjectNamespace;\n /** Object ID or name resolver */\n objectId: string | ((request: RpcRequest) => string);\n /** Serializer (default: JSON) */\n serializer?: Serializer;\n /** Logger */\n logger?: Logger;\n}\n\n/**\n * RPC transport using Durable Objects\n *\n * Routes requests to specific Durable Object instances,\n * enabling stateful, single-threaded execution.\n */\nexport class DurableObjectTransport implements RpcTransport {\n readonly name = \"durable-object\";\n private readonly namespace: DurableObjectNamespace;\n private readonly objectIdResolver: (request: RpcRequest) => string;\n private readonly serializer: Serializer;\n private readonly logger: Logger;\n\n constructor(options: DurableObjectTransportOptions) {\n this.namespace = options.namespace;\n this.objectIdResolver =\n typeof options.objectId === \"function\"\n ? options.objectId\n : () => options.objectId as string;\n this.serializer = options.serializer ?? jsonSerializer;\n this.logger = options.logger ?? createLogger({ name: \"durable-object\" });\n }\n\n async call<TInput, TOutput>(request: RpcRequest<TInput>): Promise<RpcResponse<TOutput>> {\n try {\n // Resolve object ID\n const objectIdName = this.objectIdResolver(request);\n const id = this.namespace.idFromName(objectIdName);\n const stub = this.namespace.get(id);\n\n // Make request to Durable Object\n const body = this.serializer.encode(request);\n const response = await stub.fetch(\"http://internal/rpc\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": this.serializer.contentType,\n },\n body: typeof body === \"string\" ? body : body,\n });\n\n // Parse response\n const text = await response.text();\n return this.serializer.decode(text) as RpcResponse<TOutput>;\n } catch (error) {\n this.logger.error(\"Durable Object call failed\", error as Error);\n throw new TransportError(\n `Durable Object call failed: ${(error as Error).message}`,\n error as Error\n );\n }\n }\n\n async close(): Promise<void> {\n // No cleanup needed\n }\n}\n\n/**\n * Create a Durable Object transport\n */\nexport function createDurableObjectTransport(\n options: DurableObjectTransportOptions\n): DurableObjectTransport {\n return new DurableObjectTransport(options);\n}\n\n// ============================================================================\n// DURABLE OBJECT EVENT TRANSPORT\n// ============================================================================\n\nexport interface DurableObjectEventTransportOptions {\n /** Durable Object namespace */\n namespace: DurableObjectNamespace;\n /** Object ID resolver (e.g., by tenant ID) */\n objectIdResolver: (event: ParsEvent) => string;\n /** Logger */\n logger?: Logger;\n}\n\n/**\n * Event transport using Durable Objects\n *\n * Routes events to specific Durable Object instances,\n * useful for tenant-specific event processing.\n */\nexport class DurableObjectEventTransport implements EventTransport {\n readonly name = \"durable-object-events\";\n private readonly namespace: DurableObjectNamespace;\n private readonly objectIdResolver: (event: ParsEvent) => string;\n private readonly logger: Logger;\n private readonly registry: EventHandlerRegistry;\n\n constructor(options: DurableObjectEventTransportOptions) {\n this.namespace = options.namespace;\n this.objectIdResolver = options.objectIdResolver;\n this.logger = options.logger ?? createLogger({ name: \"do-events\" });\n this.registry = new EventHandlerRegistry({ logger: this.logger });\n }\n\n async emit<T>(event: ParsEvent<T>): Promise<void> {\n try {\n const objectIdName = this.objectIdResolver(event);\n const id = this.namespace.idFromName(objectIdName);\n const stub = this.namespace.get(id);\n\n await stub.fetch(\"http://internal/event\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(event),\n });\n } catch (error) {\n this.logger.error(\"Failed to emit event to Durable Object\", error as Error);\n throw error;\n }\n }\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 async close(): Promise<void> {\n this.registry.clear();\n }\n}\n\n// ============================================================================\n// DURABLE OBJECT BASE CLASS\n// ============================================================================\n\nimport type { RpcServer } from \"../../rpc/server.js\";\n\n/**\n * Base class for service Durable Objects\n *\n * @example\n * ```typescript\n * export class PaymentsDO extends ServiceDurableObject {\n * constructor(state: DurableObjectState, env: Env) {\n * super(state, env, createPaymentsServer());\n * }\n * }\n * ```\n */\nexport abstract class ServiceDurableObject {\n protected readonly state: DurableObjectState;\n protected readonly rpcServer: RpcServer;\n protected readonly eventRegistry: EventHandlerRegistry;\n protected readonly logger: Logger;\n\n constructor(\n state: DurableObjectState,\n _env: unknown,\n rpcServer: RpcServer\n ) {\n this.state = state;\n this.rpcServer = rpcServer;\n this.eventRegistry = new EventHandlerRegistry();\n this.logger = createLogger({ name: `do:${rpcServer.getDefinition().name}` });\n }\n\n async fetch(request: Request): Promise<Response> {\n const url = new URL(request.url);\n\n if (request.method === \"POST\" && url.pathname === \"/rpc\") {\n return this.handleRpc(request);\n }\n\n if (request.method === \"POST\" && url.pathname === \"/event\") {\n return this.handleEvent(request);\n }\n\n return new Response(\"Not Found\", { status: 404 });\n }\n\n private async handleRpc(request: Request): Promise<Response> {\n try {\n const body = await request.json() as RpcRequest;\n const response = await this.rpcServer.handle(body);\n\n return new Response(JSON.stringify(response), {\n status: response.success ? 200 : 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (error) {\n this.logger.error(\"RPC handler error\", error as Error);\n return new Response(\n JSON.stringify({\n success: false,\n error: { code: \"INTERNAL_ERROR\", message: (error as Error).message },\n }),\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n }\n\n private async handleEvent(request: Request): Promise<Response> {\n try {\n const event = await request.json() as ParsEvent;\n await this.eventRegistry.handle(event);\n return new Response(\"OK\", { status: 200 });\n } catch (error) {\n this.logger.error(\"Event handler error\", error as Error);\n return new Response(\"Error\", { status: 500 });\n }\n }\n\n /**\n * Register an event handler\n */\n protected on(\n eventType: string,\n handler: EventHandler,\n options?: EventHandlerOptions\n ): Unsubscribe {\n return this.eventRegistry.register(eventType, handler, options);\n }\n}\n\n/**\n * Durable Object state interface\n */\ninterface DurableObjectState {\n id: DurableObjectId;\n storage: DurableObjectStorage;\n blockConcurrencyWhile<T>(callback: () => Promise<T>): Promise<T>;\n}\n\ninterface DurableObjectStorage {\n get<T>(key: string): Promise<T | undefined>;\n get<T>(keys: string[]): Promise<Map<string, T>>;\n put<T>(key: string, value: T): Promise<void>;\n put<T>(entries: Record<string, T>): Promise<void>;\n delete(key: string): Promise<boolean>;\n delete(keys: string[]): Promise<number>;\n list<T>(options?: { prefix?: string; limit?: number }): Promise<Map<string, T>>;\n}\n"],"mappings":"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","names":["createLogger","createLogger","createLogger","createLogger","createLogger","createLogger"]}
1
+ {"version":3,"sources":["../../../src/transports/cloudflare/binding.ts","../../../src/serialization/index.ts","../../../src/rpc/errors.ts","../../../src/rpc/transports/http.ts","../../../src/transports/cloudflare/queue.ts","../../../src/events/format.ts","../../../src/events/handler.ts","../../../src/transports/cloudflare/durable-object.ts"],"sourcesContent":["/**\n * @parsrun/service - Cloudflare Service Binding Transport\n * RPC transport using Cloudflare Workers Service Bindings\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { createLogger } from \"@parsrun/core\";\nimport type {\n RpcRequest,\n RpcResponse,\n RpcTransport,\n Fetcher,\n} from \"../../types.js\";\nimport { type Serializer, jsonSerializer } from \"../../serialization/index.js\";\nimport { TransportError, SerializationError } from \"../../rpc/errors.js\";\n\n// ============================================================================\n// SERVICE BINDING TRANSPORT\n// ============================================================================\n\n/**\n * Options for creating a service binding transport.\n */\nexport interface ServiceBindingTransportOptions {\n /** Service name */\n serviceName: string;\n /** Cloudflare service binding */\n binding: Fetcher;\n /** Serializer (default: JSON) */\n serializer?: Serializer;\n /** Logger */\n logger?: Logger;\n /** Timeout in ms */\n timeout?: number;\n}\n\n/**\n * RPC transport using Cloudflare Service Bindings\n *\n * Service bindings provide zero-latency, in-network communication\n * between Cloudflare Workers.\n */\nexport class ServiceBindingTransport implements RpcTransport {\n readonly name = \"service-binding\";\n private readonly serviceName: string;\n private readonly binding: Fetcher;\n private readonly serializer: Serializer;\n private readonly logger: Logger;\n\n constructor(options: ServiceBindingTransportOptions) {\n this.serviceName = options.serviceName;\n this.binding = options.binding;\n this.serializer = options.serializer ?? jsonSerializer;\n this.logger = options.logger ?? createLogger({ name: `binding:${options.serviceName}` });\n // Note: timeout option reserved for future AbortController support\n }\n\n async call<TInput, TOutput>(request: RpcRequest<TInput>): Promise<RpcResponse<TOutput>> {\n const startTime = Date.now();\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 // Build headers\n const headers: Record<string, string> = {\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 };\n\n if (request.version) {\n headers[\"X-Service-Version\"] = request.version;\n }\n\n if (request.traceContext) {\n headers[\"traceparent\"] = formatTraceparent(request.traceContext);\n if (request.traceContext.traceState) {\n headers[\"tracestate\"] = request.traceContext.traceState;\n }\n }\n\n if (request.metadata?.tenantId) {\n headers[\"X-Tenant-ID\"] = String(request.metadata.tenantId);\n }\n\n // Make request via service binding\n const response = await this.binding.fetch(\"http://internal/rpc\", {\n method: \"POST\",\n headers,\n body: typeof body === \"string\" ? body : body,\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 const duration = Date.now() - startTime;\n this.logger.debug(`RPC call completed`, {\n service: this.serviceName,\n method: request.method,\n durationMs: duration,\n success: responseData.success,\n });\n\n return responseData;\n } catch (error) {\n const duration = Date.now() - startTime;\n\n if (error instanceof SerializationError) {\n throw error;\n }\n\n this.logger.error(`RPC call failed`, error as Error, {\n service: this.serviceName,\n method: request.method,\n durationMs: duration,\n });\n\n throw new TransportError(\n `Service binding call failed: ${(error as Error).message}`,\n error as Error\n );\n }\n }\n\n async close(): Promise<void> {\n // Service bindings don't need cleanup\n }\n}\n\n/**\n * Create a service binding transport for Cloudflare Workers.\n *\n * @param options - Transport configuration options\n * @returns A new service binding transport instance\n *\n * @example\n * ```typescript\n * const transport = createServiceBindingTransport({\n * serviceName: 'payments',\n * binding: env.PAYMENTS_SERVICE,\n * });\n * ```\n */\nexport function createServiceBindingTransport(\n options: ServiceBindingTransportOptions\n): ServiceBindingTransport {\n return new ServiceBindingTransport(options);\n}\n\n// ============================================================================\n// SERVICE BINDING HANDLER\n// ============================================================================\n\nimport type { RpcServer } from \"../../rpc/server.js\";\nimport { parseTraceparent } from \"../../rpc/transports/http.js\";\n\n/**\n * Create a request handler for service binding requests\n *\n * @example\n * ```typescript\n * // In your worker\n * export default {\n * fetch: createServiceBindingHandler(rpcServer),\n * };\n * ```\n */\nexport function createServiceBindingHandler(\n server: RpcServer,\n options?: {\n serializer?: Serializer;\n logger?: Logger;\n }\n): (request: Request) => Promise<Response> {\n const serializer = options?.serializer ?? jsonSerializer;\n const logger = options?.logger ?? createLogger({ name: \"binding-handler\" });\n\n return async (request: Request): Promise<Response> => {\n // Only handle POST to /rpc\n const url = new URL(request.url);\n if (request.method !== \"POST\" || url.pathname !== \"/rpc\") {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n try {\n // Parse request body\n const contentType = request.headers.get(\"Content-Type\") ?? \"\";\n let body: RpcRequest;\n\n if (contentType.includes(\"msgpack\")) {\n const buffer = await request.arrayBuffer();\n body = serializer.decode(buffer) as RpcRequest;\n } else {\n const text = await request.text();\n body = serializer.decode(text) as RpcRequest;\n }\n\n // Extract trace context\n const traceparent = request.headers.get(\"traceparent\");\n if (traceparent) {\n const parsedTrace = parseTraceparent(traceparent);\n if (parsedTrace) {\n body.traceContext = parsedTrace;\n const tracestate = request.headers.get(\"tracestate\");\n if (tracestate) {\n body.traceContext.traceState = tracestate;\n }\n }\n }\n\n // Extract tenant from header\n const tenantId = request.headers.get(\"X-Tenant-ID\");\n if (tenantId) {\n body.metadata = { ...body.metadata, tenantId };\n }\n\n // Handle request\n const response = await server.handle(body);\n\n // Serialize response\n const responseBody = serializer.encode(response);\n\n return new Response(\n typeof responseBody === \"string\" ? responseBody : responseBody,\n {\n status: response.success ? 200 : getHttpStatus(response.error?.code),\n headers: {\n \"Content-Type\": serializer.contentType,\n \"X-Request-ID\": body.id,\n },\n }\n );\n } catch (error) {\n logger.error(\"Handler error\", error as Error);\n\n return new Response(\n JSON.stringify({\n success: false,\n error: {\n code: \"INTERNAL_ERROR\",\n message: (error as Error).message,\n },\n }),\n {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n }\n );\n }\n };\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\nimport type { TraceContext } from \"../../types.js\";\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\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 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 - 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 - 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 - 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 - Cloudflare Queue Transport\n * Event transport using Cloudflare Queues\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 CompactEvent,\n} from \"../../types.js\";\nimport { toCompactEvent, fromCompactEvent } from \"../../events/format.js\";\nimport { EventHandlerRegistry } from \"../../events/handler.js\";\n\n// ============================================================================\n// CLOUDFLARE QUEUE TYPES\n// ============================================================================\n\n/**\n * Cloudflare Queue binding interface.\n * Represents a Cloudflare Queue for sending messages.\n */\nexport interface CloudflareQueue {\n /** Send a single message to the queue */\n send(message: unknown, options?: { contentType?: string }): Promise<void>;\n /** Send multiple messages to the queue in a batch */\n sendBatch(\n messages: Array<{ body: unknown; contentType?: string }>\n ): Promise<void>;\n}\n\n/**\n * Queue message received from Cloudflare.\n * Contains the message body and methods to acknowledge or retry.\n */\nexport interface QueueMessage<T = unknown> {\n /** Unique message identifier */\n id: string;\n /** Message timestamp */\n timestamp: Date;\n /** Message body */\n body: T;\n /** Acknowledge successful processing */\n ack(): void;\n /** Retry processing the message */\n retry(): void;\n}\n\n/**\n * Batch of queue messages from Cloudflare.\n * Received by queue consumers for batch processing.\n */\nexport interface QueueBatch<T = unknown> {\n /** Queue name */\n queue: string;\n /** Messages in the batch */\n messages: QueueMessage<T>[];\n /** Acknowledge all messages in the batch */\n ackAll(): void;\n /** Retry all messages in the batch */\n retryAll(): void;\n}\n\n// ============================================================================\n// CLOUDFLARE QUEUE TRANSPORT\n// ============================================================================\n\n/**\n * Options for creating a Cloudflare Queue transport.\n */\nexport interface CloudflareQueueTransportOptions {\n /** Cloudflare Queue binding */\n queue: CloudflareQueue;\n /** Queue name (for logging) */\n queueName?: string;\n /** Use compact event format */\n compact?: boolean;\n /** Logger */\n logger?: Logger;\n /** Batch size for sending */\n batchSize?: number;\n /** Flush interval in ms */\n flushInterval?: number;\n}\n\n/**\n * Event transport using Cloudflare Queues\n *\n * Events are sent to a Cloudflare Queue for async processing.\n * Handlers are registered to process events from the queue.\n */\nexport class CloudflareQueueTransport implements EventTransport {\n readonly name = \"cloudflare-queue\";\n private readonly queue: CloudflareQueue;\n private readonly queueName: string;\n private readonly compact: boolean;\n private readonly logger: Logger;\n private readonly batchSize: number;\n private readonly flushInterval: number;\n private readonly registry: EventHandlerRegistry;\n private readonly buffer: ParsEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: CloudflareQueueTransportOptions) {\n this.queue = options.queue;\n this.queueName = options.queueName ?? \"events\";\n this.compact = options.compact ?? true;\n this.logger = options.logger ?? createLogger({ name: `queue:${this.queueName}` });\n this.batchSize = options.batchSize ?? 100;\n this.flushInterval = options.flushInterval ?? 1000;\n this.registry = new EventHandlerRegistry({ logger: this.logger });\n\n // Start flush timer\n this.flushTimer = setInterval(() => this.flush(), this.flushInterval);\n }\n\n /**\n * Emit an event to the queue\n */\n async emit<T>(event: ParsEvent<T>): Promise<void> {\n this.buffer.push(event);\n\n if (this.buffer.length >= this.batchSize) {\n await this.flush();\n }\n }\n\n /**\n * Subscribe to events (for local handler registration)\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 * Flush buffered events to the queue\n */\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n\n const events = this.buffer.splice(0, this.batchSize);\n\n try {\n if (events.length === 1) {\n // Single event\n const body = this.compact\n ? toCompactEvent(events[0]!)\n : events[0];\n await this.queue.send(body);\n } else {\n // Batch send\n const messages = events.map((event) => ({\n body: this.compact ? toCompactEvent(event) : event,\n }));\n await this.queue.sendBatch(messages);\n }\n\n this.logger.debug(`Sent ${events.length} events to queue`, {\n queue: this.queueName,\n });\n } catch (error) {\n // Put events back in buffer for retry\n this.buffer.unshift(...events);\n this.logger.error(\"Failed to send events to queue\", error as Error);\n throw error;\n }\n }\n\n /**\n * Handle a queue message (called by queue consumer)\n */\n async handleMessage<T>(message: QueueMessage<T>): Promise<void> {\n try {\n const event = this.parseEvent(message.body);\n await this.registry.handle(event);\n message.ack();\n } catch (error) {\n this.logger.error(\"Failed to handle queue message\", error as Error, {\n messageId: message.id,\n });\n message.retry();\n }\n }\n\n /**\n * Handle a batch of queue messages\n */\n async handleBatch<T>(batch: QueueBatch<T>): Promise<void> {\n const results = await Promise.allSettled(\n batch.messages.map((msg) => this.handleMessage(msg))\n );\n\n const failures = results.filter((r) => r.status === \"rejected\");\n if (failures.length > 0) {\n this.logger.warn(`${failures.length}/${batch.messages.length} messages failed`);\n }\n }\n\n /**\n * Parse event from message body\n */\n private parseEvent(body: unknown): ParsEvent {\n if (this.compact && isCompactEvent(body)) {\n return fromCompactEvent(body as CompactEvent);\n }\n return body as ParsEvent;\n }\n\n /**\n * Close the transport\n */\n async close(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n await this.flush();\n this.registry.clear();\n }\n}\n\n/**\n * Create a Cloudflare Queue transport for event distribution.\n *\n * @param options - Transport configuration options\n * @returns A new Cloudflare Queue transport instance\n *\n * @example\n * ```typescript\n * const transport = createCloudflareQueueTransport({\n * queue: env.EVENTS_QUEUE,\n * queueName: 'events',\n * batchSize: 100,\n * });\n * ```\n */\nexport function createCloudflareQueueTransport(\n options: CloudflareQueueTransportOptions\n): CloudflareQueueTransport {\n return new CloudflareQueueTransport(options);\n}\n\n// ============================================================================\n// QUEUE CONSUMER\n// ============================================================================\n\n/**\n * Create a queue consumer handler for processing queue messages.\n * Use this to handle incoming events from a Cloudflare Queue.\n *\n * @param registry - Event handler registry for processing events\n * @param options - Consumer configuration options\n * @returns Queue batch handler function\n *\n * @example\n * ```typescript\n * // In your worker\n * const consumer = createQueueConsumer(registry);\n *\n * export default {\n * queue: consumer,\n * };\n * ```\n */\nexport function createQueueConsumer(\n registry: EventHandlerRegistry,\n options?: {\n compact?: boolean;\n logger?: Logger;\n }\n): (batch: QueueBatch) => Promise<void> {\n const compact = options?.compact ?? true;\n const logger = options?.logger ?? createLogger({ name: \"queue-consumer\" });\n\n return async (batch: QueueBatch): Promise<void> => {\n logger.info(`Processing batch of ${batch.messages.length} messages`, {\n queue: batch.queue,\n });\n\n for (const message of batch.messages) {\n try {\n let event: ParsEvent;\n\n if (compact && isCompactEvent(message.body)) {\n event = fromCompactEvent(message.body as CompactEvent);\n } else {\n event = message.body as ParsEvent;\n }\n\n await registry.handle(event);\n message.ack();\n } catch (error) {\n logger.error(\"Failed to process message\", error as Error, {\n messageId: message.id,\n });\n message.retry();\n }\n }\n };\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\nfunction isCompactEvent(body: unknown): boolean {\n if (!body || typeof body !== \"object\") return false;\n const obj = body as Record<string, unknown>;\n return (\n typeof obj[\"e\"] === \"string\" &&\n typeof obj[\"s\"] === \"string\" &&\n typeof obj[\"i\"] === \"string\" &&\n typeof obj[\"t\"] === \"number\"\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 - 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 - Durable Object Transport\n * RPC/Event transport using Cloudflare Durable Objects\n */\n\nimport type { Logger } from \"@parsrun/core\";\nimport { createLogger } from \"@parsrun/core\";\nimport type {\n RpcRequest,\n RpcResponse,\n RpcTransport,\n ParsEvent,\n EventTransport,\n EventHandler,\n EventHandlerOptions,\n Unsubscribe,\n} from \"../../types.js\";\nimport { type Serializer, jsonSerializer } from \"../../serialization/index.js\";\nimport { TransportError } from \"../../rpc/errors.js\";\nimport { EventHandlerRegistry } from \"../../events/handler.js\";\n\n// ============================================================================\n// DURABLE OBJECT TYPES\n// ============================================================================\n\n/**\n * Durable Object namespace binding\n */\nexport interface DurableObjectNamespace {\n idFromName(name: string): DurableObjectId;\n idFromString(id: string): DurableObjectId;\n newUniqueId(): DurableObjectId;\n get(id: DurableObjectId): DurableObjectStub;\n}\n\n/**\n * Durable Object ID\n */\nexport interface DurableObjectId {\n toString(): string;\n}\n\n/**\n * Durable Object stub for making requests\n */\nexport interface DurableObjectStub {\n fetch(input: string | Request | URL, init?: RequestInit): Promise<Response>;\n}\n\n// ============================================================================\n// DURABLE OBJECT TRANSPORT\n// ============================================================================\n\n/**\n * Options for creating a Durable Object transport.\n */\nexport interface DurableObjectTransportOptions {\n /** Durable Object namespace */\n namespace: DurableObjectNamespace;\n /** Object ID or name resolver */\n objectId: string | ((request: RpcRequest) => string);\n /** Serializer (default: JSON) */\n serializer?: Serializer;\n /** Logger */\n logger?: Logger;\n}\n\n/**\n * RPC transport using Durable Objects\n *\n * Routes requests to specific Durable Object instances,\n * enabling stateful, single-threaded execution.\n */\nexport class DurableObjectTransport implements RpcTransport {\n readonly name = \"durable-object\";\n private readonly namespace: DurableObjectNamespace;\n private readonly objectIdResolver: (request: RpcRequest) => string;\n private readonly serializer: Serializer;\n private readonly logger: Logger;\n\n constructor(options: DurableObjectTransportOptions) {\n this.namespace = options.namespace;\n this.objectIdResolver =\n typeof options.objectId === \"function\"\n ? options.objectId\n : () => options.objectId as string;\n this.serializer = options.serializer ?? jsonSerializer;\n this.logger = options.logger ?? createLogger({ name: \"durable-object\" });\n }\n\n async call<TInput, TOutput>(request: RpcRequest<TInput>): Promise<RpcResponse<TOutput>> {\n try {\n // Resolve object ID\n const objectIdName = this.objectIdResolver(request);\n const id = this.namespace.idFromName(objectIdName);\n const stub = this.namespace.get(id);\n\n // Make request to Durable Object\n const body = this.serializer.encode(request);\n const response = await stub.fetch(\"http://internal/rpc\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": this.serializer.contentType,\n },\n body: typeof body === \"string\" ? body : body,\n });\n\n // Parse response\n const text = await response.text();\n return this.serializer.decode(text) as RpcResponse<TOutput>;\n } catch (error) {\n this.logger.error(\"Durable Object call failed\", error as Error);\n throw new TransportError(\n `Durable Object call failed: ${(error as Error).message}`,\n error as Error\n );\n }\n }\n\n async close(): Promise<void> {\n // No cleanup needed\n }\n}\n\n/**\n * Create a Durable Object transport for stateful RPC.\n *\n * @param options - Transport configuration options\n * @returns A new Durable Object transport instance\n *\n * @example\n * ```typescript\n * const transport = createDurableObjectTransport({\n * namespace: env.PAYMENTS_DO,\n * objectId: (req) => req.metadata?.tenantId ?? 'default',\n * });\n * ```\n */\nexport function createDurableObjectTransport(\n options: DurableObjectTransportOptions\n): DurableObjectTransport {\n return new DurableObjectTransport(options);\n}\n\n// ============================================================================\n// DURABLE OBJECT EVENT TRANSPORT\n// ============================================================================\n\n/**\n * Options for creating a Durable Object event transport.\n */\nexport interface DurableObjectEventTransportOptions {\n /** Durable Object namespace */\n namespace: DurableObjectNamespace;\n /** Object ID resolver (e.g., by tenant ID) */\n objectIdResolver: (event: ParsEvent) => string;\n /** Logger */\n logger?: Logger;\n}\n\n/**\n * Event transport using Durable Objects\n *\n * Routes events to specific Durable Object instances,\n * useful for tenant-specific event processing.\n */\nexport class DurableObjectEventTransport implements EventTransport {\n readonly name = \"durable-object-events\";\n private readonly namespace: DurableObjectNamespace;\n private readonly objectIdResolver: (event: ParsEvent) => string;\n private readonly logger: Logger;\n private readonly registry: EventHandlerRegistry;\n\n constructor(options: DurableObjectEventTransportOptions) {\n this.namespace = options.namespace;\n this.objectIdResolver = options.objectIdResolver;\n this.logger = options.logger ?? createLogger({ name: \"do-events\" });\n this.registry = new EventHandlerRegistry({ logger: this.logger });\n }\n\n async emit<T>(event: ParsEvent<T>): Promise<void> {\n try {\n const objectIdName = this.objectIdResolver(event);\n const id = this.namespace.idFromName(objectIdName);\n const stub = this.namespace.get(id);\n\n await stub.fetch(\"http://internal/event\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(event),\n });\n } catch (error) {\n this.logger.error(\"Failed to emit event to Durable Object\", error as Error);\n throw error;\n }\n }\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 async close(): Promise<void> {\n this.registry.clear();\n }\n}\n\n// ============================================================================\n// DURABLE OBJECT BASE CLASS\n// ============================================================================\n\nimport type { RpcServer } from \"../../rpc/server.js\";\n\n/**\n * Base class for service Durable Objects\n *\n * @example\n * ```typescript\n * export class PaymentsDO extends ServiceDurableObject {\n * constructor(state: DurableObjectState, env: Env) {\n * super(state, env, createPaymentsServer());\n * }\n * }\n * ```\n */\nexport abstract class ServiceDurableObject {\n protected readonly state: DurableObjectState;\n protected readonly rpcServer: RpcServer;\n protected readonly eventRegistry: EventHandlerRegistry;\n protected readonly logger: Logger;\n\n constructor(\n state: DurableObjectState,\n _env: unknown,\n rpcServer: RpcServer\n ) {\n this.state = state;\n this.rpcServer = rpcServer;\n this.eventRegistry = new EventHandlerRegistry();\n this.logger = createLogger({ name: `do:${rpcServer.getDefinition().name}` });\n }\n\n async fetch(request: Request): Promise<Response> {\n const url = new URL(request.url);\n\n if (request.method === \"POST\" && url.pathname === \"/rpc\") {\n return this.handleRpc(request);\n }\n\n if (request.method === \"POST\" && url.pathname === \"/event\") {\n return this.handleEvent(request);\n }\n\n return new Response(\"Not Found\", { status: 404 });\n }\n\n private async handleRpc(request: Request): Promise<Response> {\n try {\n const body = await request.json() as RpcRequest;\n const response = await this.rpcServer.handle(body);\n\n return new Response(JSON.stringify(response), {\n status: response.success ? 200 : 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (error) {\n this.logger.error(\"RPC handler error\", error as Error);\n return new Response(\n JSON.stringify({\n success: false,\n error: { code: \"INTERNAL_ERROR\", message: (error as Error).message },\n }),\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n }\n\n private async handleEvent(request: Request): Promise<Response> {\n try {\n const event = await request.json() as ParsEvent;\n await this.eventRegistry.handle(event);\n return new Response(\"OK\", { status: 200 });\n } catch (error) {\n this.logger.error(\"Event handler error\", error as Error);\n return new Response(\"Error\", { status: 500 });\n }\n }\n\n /**\n * Register an event handler\n */\n protected on(\n eventType: string,\n handler: EventHandler,\n options?: EventHandlerOptions\n ): Unsubscribe {\n return this.eventRegistry.register(eventType, handler, options);\n }\n}\n\n/**\n * Durable Object state interface\n */\ninterface DurableObjectState {\n id: DurableObjectId;\n storage: DurableObjectStorage;\n blockConcurrencyWhile<T>(callback: () => Promise<T>): Promise<T>;\n}\n\ninterface DurableObjectStorage {\n get<T>(key: string): Promise<T | undefined>;\n get<T>(keys: string[]): Promise<Map<string, T>>;\n put<T>(key: string, value: T): Promise<void>;\n put<T>(entries: Record<string, T>): Promise<void>;\n delete(key: string): Promise<boolean>;\n delete(keys: string[]): Promise<number>;\n list<T>(options?: { prefix?: string; limit?: number }): Promise<Map<string, T>>;\n}\n"],"mappings":"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","names":["createLogger","createLogger","createLogger","createLogger","createLogger","createLogger"]}
@@ -281,12 +281,36 @@ interface Span {
281
281
  /** Span events */
282
282
  events: SpanEvent[];
283
283
  }
284
+ /**
285
+ * Kind of span indicating the relationship between the span and its parent.
286
+ * - "internal": Default type, represents internal operations
287
+ * - "server": Indicates the span covers server-side handling of a request
288
+ * - "client": Indicates the span covers client-side sending of a request
289
+ * - "producer": Indicates the span covers sending a message to a broker
290
+ * - "consumer": Indicates the span covers receiving a message from a broker
291
+ */
284
292
  type SpanKind = "internal" | "server" | "client" | "producer" | "consumer";
293
+ /**
294
+ * Status of a span indicating whether the operation succeeded or failed.
295
+ * - "unset": Default status, operation has not been marked
296
+ * - "ok": Operation completed successfully
297
+ * - "error": Operation failed
298
+ */
285
299
  type SpanStatus = "unset" | "ok" | "error";
300
+ /**
301
+ * Valid types for span attribute values.
302
+ * Supports primitive types and arrays of primitives.
303
+ */
286
304
  type SpanAttributeValue = string | number | boolean | string[] | number[] | boolean[];
305
+ /**
306
+ * An event recorded during a span's lifetime.
307
+ */
287
308
  interface SpanEvent {
309
+ /** Event name */
288
310
  name: string;
311
+ /** Event timestamp (unix ms) */
289
312
  time: number;
313
+ /** Optional event attributes */
290
314
  attributes?: Record<string, SpanAttributeValue>;
291
315
  }
292
316
  /**
@@ -334,16 +358,25 @@ interface ServiceConfig {
334
358
  /** Dead letter queue options */
335
359
  deadLetter?: DeadLetterConfig;
336
360
  }
361
+ /**
362
+ * Event format configuration options.
363
+ */
337
364
  interface EventFormatConfig {
338
365
  /** Use CloudEvents format (default: true) */
339
366
  format?: "cloudevents" | "compact";
340
367
  /** Use compact format for internal communication */
341
368
  internalCompact?: boolean;
342
369
  }
370
+ /**
371
+ * Serialization format configuration.
372
+ */
343
373
  interface SerializationConfig {
344
374
  /** Serialization format */
345
375
  format?: "json" | "msgpack";
346
376
  }
377
+ /**
378
+ * Distributed tracing configuration.
379
+ */
347
380
  interface TracingConfig {
348
381
  /** Enable tracing */
349
382
  enabled?: boolean;
@@ -358,12 +391,18 @@ interface TracingConfig {
358
391
  /** Service name for traces */
359
392
  serviceName?: string;
360
393
  }
394
+ /**
395
+ * API versioning configuration.
396
+ */
361
397
  interface VersioningConfig {
362
398
  /** Versioning strategy */
363
399
  strategy?: "header" | "url" | "none";
364
400
  /** Default version to use */
365
401
  defaultVersion?: string;
366
402
  }
403
+ /**
404
+ * Resilience patterns configuration for fault tolerance.
405
+ */
367
406
  interface ResilienceConfig {
368
407
  /** Circuit breaker config */
369
408
  circuitBreaker?: CircuitBreakerConfig;
@@ -374,6 +413,9 @@ interface ResilienceConfig {
374
413
  /** Retry config */
375
414
  retry?: RetryConfig;
376
415
  }
416
+ /**
417
+ * Circuit breaker configuration to prevent cascading failures.
418
+ */
377
419
  interface CircuitBreakerConfig {
378
420
  /** Enable circuit breaker */
379
421
  enabled?: boolean;
@@ -384,12 +426,18 @@ interface CircuitBreakerConfig {
384
426
  /** Number of successes in half-open to close circuit */
385
427
  successThreshold?: number;
386
428
  }
429
+ /**
430
+ * Bulkhead configuration to limit concurrent requests.
431
+ */
387
432
  interface BulkheadConfig {
388
433
  /** Maximum concurrent requests */
389
434
  maxConcurrent?: number;
390
435
  /** Maximum queue size */
391
436
  maxQueue?: number;
392
437
  }
438
+ /**
439
+ * Retry configuration for failed operations.
440
+ */
393
441
  interface RetryConfig {
394
442
  /** Number of retry attempts */
395
443
  attempts?: number;
@@ -400,6 +448,9 @@ interface RetryConfig {
400
448
  /** Maximum delay in ms */
401
449
  maxDelay?: number;
402
450
  }
451
+ /**
452
+ * Dead letter queue configuration for failed event handling.
453
+ */
403
454
  interface DeadLetterConfig {
404
455
  /** Enable DLQ */
405
456
  enabled?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parsrun/service",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "description": "Unified service layer for extracted microservices - RPC + Events",
5
5
  "keywords": [
6
6
  "pars",
@@ -63,8 +63,8 @@
63
63
  "dist"
64
64
  ],
65
65
  "dependencies": {
66
- "@parsrun/core": "0.1.29",
67
- "@parsrun/types": "0.1.29"
66
+ "@parsrun/core": "0.1.31",
67
+ "@parsrun/types": "0.1.31"
68
68
  },
69
69
  "devDependencies": {
70
70
  "tsup": "^8.3.5",