@uploadista/observability 0.0.20-beta.7 → 0.0.20-beta.9

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,"file":"index.mjs","names":["headers: Record<string, string>","signalEndpoint: string | undefined","OTLPMetricExporter","attrs: Record<string, string>","OTLPLogExporter","MeterProvider","PeriodicExportingMetricReader","LoggerProvider","BatchLogRecordProcessor","SeverityNumber","LoggerProvider","EffectLogger","attributes: Record<string, unknown>","MeterProvider","config: OtlpSdkConfig","captureTraceContextEffect: Effect.Effect<\n TraceContext | undefined\n>","service: StorageObservabilityService","service: UploadObservabilityService","service: FlowObservabilityService","FlowObservabilityDisabled","makeTestFlowObservability","service: FlowObservabilityService","NoOpMetricsServiceLive: Layer.Layer<MetricsService>","STORAGE_TYPE","STORAGE_TYPE","STORAGE_TYPE","UploadObservabilityDisabled"],"sources":["../src/core/errors.ts","../src/core/exporters.ts","../src/core/logs-sdk.ts","../src/core/metrics-sdk.ts","../src/core/tracing.ts","../src/core/full-observability.ts","../src/core/layers.ts","../src/core/logging.ts","../src/core/metrics.ts","../src/core/testing.ts","../src/core/utilities.ts","../src/flow/metrics.ts","../src/flow/errors.ts","../src/flow/layers.ts","../src/flow/testing.ts","../src/flow/tracing.ts","../src/service/metrics.ts","../src/storage/azure.ts","../src/storage/filesystem.ts","../src/storage/gcs.ts","../src/storage/s3.ts","../src/upload/errors.ts","../src/upload/metrics.ts","../src/upload/layers.ts","../src/upload/testing.ts","../src/upload/tracing.ts"],"sourcesContent":["import { Effect, Metric } from \"effect\";\nimport type { StorageMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Generic Storage Error Classification and Tracking\n// ============================================================================\n\nexport type StorageErrorCategory =\n | \"network_error\"\n | \"authentication_error\"\n | \"authorization_error\"\n | \"throttling_error\"\n | \"server_error\"\n | \"client_error\"\n | \"unknown_error\";\n\n// Generic error classifier - can be extended per storage type\nexport const classifyStorageError = (error: unknown): StorageErrorCategory => {\n if (!error || typeof error !== \"object\") return \"unknown_error\";\n\n const errorCode = \"code\" in error ? error.code : undefined;\n const errorName = \"name\" in error ? error.name : undefined;\n const errorMessage =\n error instanceof Error ? error.message.toLowerCase() : \"\";\n\n // Network errors (common across all storage types)\n if (\n errorCode === \"NetworkError\" ||\n errorCode === \"ECONNRESET\" ||\n errorCode === \"ENOTFOUND\" ||\n errorCode === \"ETIMEDOUT\" ||\n errorMessage.indexOf(\"network\") >= 0 ||\n errorMessage.indexOf(\"timeout\") >= 0\n ) {\n return \"network_error\";\n }\n\n // Authentication errors (common patterns)\n if (\n errorCode === \"InvalidAccessKeyId\" ||\n errorCode === \"SignatureDoesNotMatch\" ||\n errorCode === \"TokenRefreshRequired\" ||\n errorCode === \"AuthenticationFailed\" ||\n errorName === \"AuthenticationError\" ||\n errorMessage.indexOf(\"authentication\") >= 0 ||\n errorMessage.indexOf(\"unauthorized\") >= 0\n ) {\n return \"authentication_error\";\n }\n\n // Authorization errors\n if (\n errorCode === \"AccessDenied\" ||\n errorCode === \"AccountProblem\" ||\n errorCode === \"Forbidden\" ||\n errorName === \"AuthorizationError\" ||\n errorMessage.indexOf(\"forbidden\") >= 0 ||\n errorMessage.indexOf(\"permission\") >= 0\n ) {\n return \"authorization_error\";\n }\n\n // Throttling errors\n if (\n errorCode === \"SlowDown\" ||\n errorCode === \"RequestTimeTooSkewed\" ||\n errorCode === \"TooManyRequests\" ||\n errorName === \"ThrottlingError\" ||\n errorMessage.indexOf(\"throttl\") >= 0 ||\n errorMessage.indexOf(\"rate limit\") >= 0\n ) {\n return \"throttling_error\";\n }\n\n // Server errors\n if (\n errorCode === \"InternalError\" ||\n errorCode === \"ServiceUnavailable\" ||\n errorCode === \"InternalServerError\" ||\n errorName === \"ServerError\" ||\n errorMessage.indexOf(\"server error\") >= 0 ||\n errorMessage.indexOf(\"service unavailable\") >= 0\n ) {\n return \"server_error\";\n }\n\n // Client errors\n if (\n errorCode === \"InvalidRequest\" ||\n errorCode === \"MalformedXML\" ||\n errorCode === \"RequestEntityTooLarge\" ||\n errorCode === \"BadRequest\" ||\n errorName === \"ClientError\" ||\n errorMessage.indexOf(\"bad request\") >= 0 ||\n errorMessage.indexOf(\"invalid\") >= 0\n ) {\n return \"client_error\";\n }\n\n return \"unknown_error\";\n};\n\n// Storage-specific error classifier factory\nexport const createStorageErrorClassifier = (\n storageType: string,\n customErrorMapping?: (error: unknown) => StorageErrorCategory | null,\n) => {\n return (error: unknown): StorageErrorCategory => {\n // Try custom mapping first\n if (customErrorMapping) {\n const customResult = customErrorMapping(error);\n if (customResult !== null) return customResult;\n }\n\n // Fall back to generic classification\n return classifyStorageError(error);\n };\n};\n\n// Generic error tracking function\nexport const trackStorageError = (\n storageType: string,\n metrics: StorageMetrics,\n operation: string,\n error: unknown,\n context: Record<string, unknown> = {},\n errorClassifier = classifyStorageError,\n) =>\n Effect.gen(function* () {\n const errorCategory = errorClassifier(error);\n\n // Record error metrics\n const errorMetric = metrics.uploadErrorsTotal.pipe(\n Metric.tagged(\"operation\", operation),\n Metric.tagged(\"error_category\", errorCategory),\n );\n yield* errorMetric(Effect.succeed(1));\n\n // Create detailed error context\n const errorDetails = {\n storage_type: storageType,\n operation,\n error_category: errorCategory,\n error_type: typeof error,\n error_message: error instanceof Error ? error.message : String(error),\n error_code:\n error && typeof error === \"object\" && \"code\" in error\n ? error.code\n : undefined,\n error_name:\n error && typeof error === \"object\" && \"name\" in error\n ? error.name\n : undefined,\n ...context,\n };\n\n // Log structured error\n yield* Effect.logError(\n `${storageType.toUpperCase()} ${operation} failed`,\n ).pipe(Effect.annotateLogs(errorDetails));\n });\n\n// Factory for storage-specific error tracking\nexport const createStorageErrorTracker = (\n storageType: string,\n metrics: StorageMetrics,\n customErrorClassifier?: (error: unknown) => StorageErrorCategory | null,\n) => {\n const errorClassifier = createStorageErrorClassifier(\n storageType,\n customErrorClassifier,\n );\n\n return (\n operation: string,\n error: unknown,\n context: Record<string, unknown> = {},\n ) =>\n trackStorageError(\n storageType,\n metrics,\n operation,\n error,\n context,\n errorClassifier,\n );\n};\n","/**\n * OTLP Exporter Configuration for Uploadista SDK.\n *\n * This module provides factory functions for creating OpenTelemetry Protocol (OTLP)\n * exporters that send traces, metrics, and logs to observability backends like Grafana,\n * Jaeger, Datadog, and others.\n *\n * Configuration is done via standard OpenTelemetry environment variables:\n * - OTEL_EXPORTER_OTLP_ENDPOINT: Base endpoint URL (default: http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_HEADERS: Headers for authentication (format: key=value,key2=value2)\n * - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: Override endpoint for traces only\n * - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: Override endpoint for metrics only\n * - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: Override endpoint for logs only\n *\n * @module core/exporters\n */\n\nimport { OTLPLogExporter } from \"@opentelemetry/exporter-logs-otlp-http\";\nimport { OTLPMetricExporter } from \"@opentelemetry/exporter-metrics-otlp-http\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport {\n BatchLogRecordProcessor,\n LoggerProvider,\n} from \"@opentelemetry/sdk-logs\";\nimport {\n MeterProvider,\n PeriodicExportingMetricReader,\n} from \"@opentelemetry/sdk-metrics\";\n\n/**\n * Configuration options for OTLP exporters.\n */\nexport interface OtlpExporterConfig {\n /** Base endpoint URL. Defaults to OTEL_EXPORTER_OTLP_ENDPOINT or http://localhost:4318 */\n endpoint?: string;\n /** Headers to include in requests (for authentication). Defaults to OTEL_EXPORTER_OTLP_HEADERS */\n headers?: Record<string, string>;\n /** Request timeout in milliseconds. Defaults to 5000 */\n timeoutMillis?: number;\n}\n\n/**\n * Parses the OTEL_EXPORTER_OTLP_HEADERS environment variable.\n *\n * Format: key=value,key2=value2\n * Example: Authorization=Basic abc123,X-Custom-Header=value\n *\n * @returns Parsed headers as a Record, or undefined if not set\n */\nexport function parseOtlpHeaders(): Record<string, string> | undefined {\n const headersEnv =\n typeof process !== \"undefined\"\n ? process.env.OTEL_EXPORTER_OTLP_HEADERS\n : undefined;\n\n if (!headersEnv) {\n return undefined;\n }\n\n const headers: Record<string, string> = {};\n const pairs = headersEnv.split(\",\");\n\n for (const pair of pairs) {\n const [key, ...valueParts] = pair.split(\"=\");\n if (key && valueParts.length > 0) {\n headers[key.trim()] = valueParts.join(\"=\").trim();\n }\n }\n\n return Object.keys(headers).length > 0 ? headers : undefined;\n}\n\n/**\n * Gets the OTLP endpoint from environment variables with fallback.\n *\n * Checks in order:\n * 1. Provided endpoint parameter\n * 2. Signal-specific endpoint (OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, or OTEL_EXPORTER_OTLP_LOGS_ENDPOINT)\n * 3. Base endpoint (OTEL_EXPORTER_OTLP_ENDPOINT)\n * 4. Default: http://localhost:4318\n *\n * @param signal - The signal type ('traces', 'metrics', or 'logs')\n * @param configEndpoint - Optional endpoint from config\n * @returns The resolved endpoint URL\n */\nexport function getOtlpEndpoint(\n signal: \"traces\" | \"metrics\" | \"logs\",\n configEndpoint?: string,\n): string {\n if (configEndpoint) {\n return configEndpoint;\n }\n\n if (typeof process !== \"undefined\") {\n let signalEndpoint: string | undefined;\n switch (signal) {\n case \"traces\":\n signalEndpoint = process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;\n break;\n case \"metrics\":\n signalEndpoint = process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT;\n break;\n case \"logs\":\n signalEndpoint = process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT;\n break;\n }\n\n if (signalEndpoint) {\n return signalEndpoint;\n }\n\n if (process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {\n return process.env.OTEL_EXPORTER_OTLP_ENDPOINT;\n }\n }\n\n return \"http://localhost:4318\";\n}\n\n/**\n * Creates an OTLP trace exporter configured from environment variables.\n *\n * The exporter sends traces to an OTLP-compatible endpoint using HTTP/protobuf.\n *\n * @param config - Optional configuration overrides\n * @returns Configured OTLPTraceExporter instance\n *\n * @example\n * ```typescript\n * // Use environment variables\n * const exporter = createOtlpTraceExporter();\n *\n * // Override endpoint\n * const exporter = createOtlpTraceExporter({\n * endpoint: 'https://otlp.grafana.net'\n * });\n * ```\n */\nexport function createOtlpTraceExporter(\n config: OtlpExporterConfig = {},\n): OTLPTraceExporter {\n const endpoint = getOtlpEndpoint(\"traces\", config.endpoint);\n const headers = config.headers ?? parseOtlpHeaders();\n // Default to 30 seconds to accommodate cloud endpoints like Grafana Cloud\n const timeoutMillis = config.timeoutMillis ?? 30000;\n\n return new OTLPTraceExporter({\n url: `${endpoint}/v1/traces`,\n headers,\n timeoutMillis,\n });\n}\n\n/**\n * Creates an OTLP metric exporter configured from environment variables.\n *\n * The exporter sends metrics to an OTLP-compatible endpoint using HTTP/protobuf.\n *\n * @param config - Optional configuration overrides\n * @returns Configured OTLPMetricExporter instance\n *\n * @example\n * ```typescript\n * // Use environment variables\n * const exporter = createOtlpMetricExporter();\n *\n * // Override endpoint\n * const exporter = createOtlpMetricExporter({\n * endpoint: 'https://otlp.grafana.net'\n * });\n * ```\n */\nexport function createOtlpMetricExporter(\n config: OtlpExporterConfig = {},\n): OTLPMetricExporter {\n const endpoint = getOtlpEndpoint(\"metrics\", config.endpoint);\n const headers = config.headers ?? parseOtlpHeaders();\n // Default to 30 seconds to accommodate cloud endpoints like Grafana Cloud\n const timeoutMillis = config.timeoutMillis ?? 30000;\n\n return new OTLPMetricExporter({\n url: `${endpoint}/v1/metrics`,\n headers,\n timeoutMillis,\n });\n}\n\n/**\n * Checks if observability is enabled via environment variable.\n *\n * Reads UPLOADISTA_OBSERVABILITY_ENABLED environment variable.\n * Defaults to true if not set.\n *\n * @returns true if observability should be enabled\n */\nexport function isOtlpExportEnabled(): boolean {\n if (typeof process !== \"undefined\") {\n const enabled = process.env.UPLOADISTA_OBSERVABILITY_ENABLED;\n if (enabled !== undefined) {\n return enabled.toLowerCase() !== \"false\" && enabled !== \"0\";\n }\n }\n return true;\n}\n\n/**\n * Gets the service name from environment variables.\n *\n * Reads OTEL_SERVICE_NAME environment variable.\n * Defaults to \"uploadista\" if not set.\n *\n * @param defaultName - Default service name if not configured\n * @returns The service name to use\n */\nexport function getServiceName(defaultName = \"uploadista\"): string {\n if (typeof process !== \"undefined\" && process.env.OTEL_SERVICE_NAME) {\n return process.env.OTEL_SERVICE_NAME;\n }\n return defaultName;\n}\n\n/**\n * Parses resource attributes from OTEL_RESOURCE_ATTRIBUTES environment variable.\n *\n * Format: key=value,key2=value2\n * Example: tenant.id=abc123,deployment.environment=production\n *\n * @returns Parsed attributes as a Record, or empty object if not set\n */\nexport function parseResourceAttributes(): Record<string, string> {\n if (typeof process === \"undefined\") {\n return {};\n }\n\n const attrsEnv = process.env.OTEL_RESOURCE_ATTRIBUTES;\n if (!attrsEnv) {\n return {};\n }\n\n const attrs: Record<string, string> = {};\n const pairs = attrsEnv.split(\",\");\n\n for (const pair of pairs) {\n const [key, ...valueParts] = pair.split(\"=\");\n if (key && valueParts.length > 0) {\n attrs[key.trim()] = valueParts.join(\"=\").trim();\n }\n }\n\n return attrs;\n}\n\n// ============================================================================\n// Logs Exporter\n// ============================================================================\n\n/**\n * Creates an OTLP log exporter configured from environment variables.\n *\n * The exporter sends logs to an OTLP-compatible endpoint using HTTP/protobuf.\n *\n * @param config - Optional configuration overrides\n * @returns Configured OTLPLogExporter instance\n *\n * @example\n * ```typescript\n * // Use environment variables\n * const exporter = createOtlpLogExporter();\n *\n * // Override endpoint\n * const exporter = createOtlpLogExporter({\n * endpoint: 'https://otlp.grafana.net'\n * });\n * ```\n */\nexport function createOtlpLogExporter(\n config: OtlpExporterConfig = {},\n): OTLPLogExporter {\n const endpoint = getOtlpEndpoint(\"logs\", config.endpoint);\n const headers = config.headers ?? parseOtlpHeaders();\n // Default to 30 seconds to accommodate cloud endpoints like Grafana Cloud\n const timeoutMillis = config.timeoutMillis ?? 30000;\n\n return new OTLPLogExporter({\n url: `${endpoint}/v1/logs`,\n headers,\n timeoutMillis,\n });\n}\n\n// ============================================================================\n// Metrics SDK Configuration\n// ============================================================================\n\n/**\n * Configuration for metrics export.\n */\nexport interface MetricsSdkConfig extends OtlpExporterConfig {\n /** Service name for metrics. Defaults to OTEL_SERVICE_NAME or \"uploadista\" */\n serviceName?: string;\n /** Export interval in milliseconds. Defaults to OTEL_METRICS_EXPORT_INTERVAL or 60000 */\n exportIntervalMillis?: number;\n /** Export timeout in milliseconds. Defaults to 30000 */\n exportTimeoutMillis?: number;\n}\n\n/**\n * Gets the metrics export interval from environment or config.\n *\n * @param configInterval - Optional interval from config\n * @returns Export interval in milliseconds\n */\nexport function getMetricsExportInterval(configInterval?: number): number {\n if (configInterval !== undefined) {\n return configInterval;\n }\n\n if (\n typeof process !== \"undefined\" &&\n process.env.OTEL_METRICS_EXPORT_INTERVAL\n ) {\n const interval = Number.parseInt(\n process.env.OTEL_METRICS_EXPORT_INTERVAL,\n 10,\n );\n if (!Number.isNaN(interval) && interval > 0) {\n return interval;\n }\n }\n\n return 60000; // Default: 60 seconds\n}\n\n/**\n * Creates an OTLP MeterProvider with PeriodicExportingMetricReader.\n *\n * The MeterProvider is pre-configured with:\n * - OTLP HTTP exporter for metrics\n * - Periodic export based on OTEL_METRICS_EXPORT_INTERVAL (default: 60s)\n * - Graceful error handling (failures logged, not thrown)\n *\n * @param config - Optional configuration\n * @returns Configured MeterProvider instance\n *\n * @example\n * ```typescript\n * const meterProvider = createOtlpMeterProvider();\n * const meter = meterProvider.getMeter(\"uploadista\");\n * const counter = meter.createCounter(\"uploads_total\");\n * counter.add(1, { storage: \"s3\" });\n * ```\n */\nexport function createOtlpMeterProvider(\n config: MetricsSdkConfig = {},\n): MeterProvider {\n const exporter = createOtlpMetricExporter(config);\n const exportIntervalMillis = getMetricsExportInterval(\n config.exportIntervalMillis,\n );\n const exportTimeoutMillis = config.exportTimeoutMillis ?? 30000;\n\n const reader = new PeriodicExportingMetricReader({\n exporter,\n exportIntervalMillis,\n exportTimeoutMillis,\n });\n\n return new MeterProvider({\n readers: [reader],\n });\n}\n\n// ============================================================================\n// Logs SDK Configuration\n// ============================================================================\n\n/**\n * Configuration for logs export.\n */\nexport interface LogsSdkConfig extends OtlpExporterConfig {\n /** Service name for logs. Defaults to OTEL_SERVICE_NAME or \"uploadista\" */\n serviceName?: string;\n /** Maximum queue size for batch processor. Defaults to 512 */\n maxQueueSize?: number;\n /** Maximum export batch size. Defaults to 512 */\n maxExportBatchSize?: number;\n /** Schedule delay in milliseconds. Defaults to OTEL_LOGS_EXPORT_INTERVAL or 5000 */\n scheduledDelayMillis?: number;\n /** Export timeout in milliseconds. Defaults to 30000 */\n exportTimeoutMillis?: number;\n}\n\n/**\n * Gets the logs export interval from environment or config.\n *\n * @param configInterval - Optional interval from config\n * @returns Export interval in milliseconds\n */\nexport function getLogsExportInterval(configInterval?: number): number {\n if (configInterval !== undefined) {\n return configInterval;\n }\n\n if (typeof process !== \"undefined\" && process.env.OTEL_LOGS_EXPORT_INTERVAL) {\n const interval = Number.parseInt(process.env.OTEL_LOGS_EXPORT_INTERVAL, 10);\n if (!Number.isNaN(interval) && interval > 0) {\n return interval;\n }\n }\n\n return 5000; // Default: 5 seconds\n}\n\n/**\n * Creates an OTLP LoggerProvider with BatchLogRecordProcessor.\n *\n * The LoggerProvider is pre-configured with:\n * - OTLP HTTP exporter for logs\n * - Batch processing with configurable queue and batch sizes\n * - Graceful error handling (failures logged, not thrown)\n *\n * @param config - Optional configuration\n * @returns Configured LoggerProvider instance\n *\n * @example\n * ```typescript\n * const loggerProvider = createOtlpLoggerProvider();\n * const logger = loggerProvider.getLogger(\"uploadista\");\n * logger.emit({\n * severityNumber: SeverityNumber.INFO,\n * body: \"Upload completed\",\n * attributes: { uploadId: \"123\" },\n * });\n * ```\n */\nexport function createOtlpLoggerProvider(\n config: LogsSdkConfig = {},\n): LoggerProvider {\n const exporter = createOtlpLogExporter(config);\n const scheduledDelayMillis = getLogsExportInterval(\n config.scheduledDelayMillis,\n );\n\n const processor = new BatchLogRecordProcessor(exporter, {\n maxQueueSize: config.maxQueueSize ?? 512,\n maxExportBatchSize: config.maxExportBatchSize ?? 512,\n scheduledDelayMillis,\n exportTimeoutMillis: config.exportTimeoutMillis ?? 30000,\n });\n\n // Create provider with processor in constructor (SDK 0.208.0+ API)\n return new LoggerProvider({\n processors: [processor],\n });\n}\n\nexport { OTLPLogExporter } from \"@opentelemetry/exporter-logs-otlp-http\";\nexport { OTLPMetricExporter } from \"@opentelemetry/exporter-metrics-otlp-http\";\nexport {\n BatchLogRecordProcessor,\n LoggerProvider,\n} from \"@opentelemetry/sdk-logs\";\n// Re-export types for convenience\nexport {\n MeterProvider,\n PeriodicExportingMetricReader,\n} from \"@opentelemetry/sdk-metrics\";\n","/**\n * OTLP Logs SDK Layers for Uploadista SDK.\n *\n * This module provides Effect Layers that export logs to OTLP-compatible\n * backends like Grafana Loki, Elasticsearch, and others.\n *\n * Logs are automatically enriched with:\n * - trace_id and span_id for correlation with traces\n * - Service name and resource attributes\n * - Effect log annotations as attributes\n *\n * Configuration is done via standard OpenTelemetry environment variables:\n * - OTEL_EXPORTER_OTLP_ENDPOINT: Base endpoint URL (default: http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: Override endpoint for logs only\n * - OTEL_EXPORTER_OTLP_HEADERS: Headers for authentication\n * - OTEL_SERVICE_NAME: Service name (default: uploadista)\n * - OTEL_LOGS_EXPORT_INTERVAL: Export interval in ms (default: 5000)\n * - UPLOADISTA_OBSERVABILITY_ENABLED: Set to \"false\" to disable (default: true)\n *\n * @module core/logs-sdk\n */\n\nimport { trace } from \"@opentelemetry/api\";\nimport type { Logger } from \"@opentelemetry/api-logs\";\nimport { SeverityNumber } from \"@opentelemetry/api-logs\";\nimport type { LoggerProvider } from \"@opentelemetry/sdk-logs\";\nimport { Context, Effect, Logger as EffectLogger, Layer } from \"effect\";\nimport {\n createOtlpLoggerProvider,\n getServiceName,\n isOtlpExportEnabled,\n type LogsSdkConfig,\n} from \"./exporters.js\";\n\n// ============================================================================\n// Logs Service\n// ============================================================================\n\n/**\n * OpenTelemetry Logger service for emitting logs.\n */\nexport interface OtelLoggerService {\n /** The OpenTelemetry Logger instance */\n readonly logger: Logger;\n /** The LoggerProvider for shutdown handling */\n readonly provider: LoggerProvider;\n}\n\n/**\n * Effect Context tag for the OTEL Logger service.\n */\nexport class OtelLogger extends Context.Tag(\"OtelLogger\")<\n OtelLogger,\n OtelLoggerService\n>() {}\n\n// ============================================================================\n// Log Level Mapping\n// ============================================================================\n\n/**\n * Maps Effect log levels to OpenTelemetry SeverityNumber.\n *\n * | Effect Level | OTEL Severity |\n * |--------------|---------------|\n * | Debug | 5 (DEBUG) |\n * | Info | 9 (INFO) |\n * | Warning | 13 (WARN) |\n * | Error | 17 (ERROR) |\n * | Fatal | 21 (FATAL) |\n */\nexport function mapLogLevelToSeverity(level: string): SeverityNumber {\n switch (level.toLowerCase()) {\n case \"debug\":\n case \"trace\":\n return SeverityNumber.DEBUG;\n case \"info\":\n return SeverityNumber.INFO;\n case \"warning\":\n case \"warn\":\n return SeverityNumber.WARN;\n case \"error\":\n return SeverityNumber.ERROR;\n case \"fatal\":\n case \"critical\":\n return SeverityNumber.FATAL;\n default:\n return SeverityNumber.INFO;\n }\n}\n\n/**\n * Maps SeverityNumber to human-readable severity text.\n */\nexport function severityToText(severity: SeverityNumber): string {\n if (severity <= SeverityNumber.DEBUG) return \"DEBUG\";\n if (severity <= SeverityNumber.INFO) return \"INFO\";\n if (severity <= SeverityNumber.WARN) return \"WARN\";\n if (severity <= SeverityNumber.ERROR) return \"ERROR\";\n return \"FATAL\";\n}\n\n// ============================================================================\n// Trace Context Injection\n// ============================================================================\n\n/**\n * Gets the current trace context from OpenTelemetry.\n *\n * @returns Object with trace_id and span_id if active, empty object otherwise\n */\nexport function getTraceContext(): Record<string, string> {\n const span = trace.getActiveSpan();\n if (!span) {\n return {};\n }\n\n const ctx = span.spanContext();\n // Only include if valid trace ID\n if (ctx.traceId === \"00000000000000000000000000000000\") {\n return {};\n }\n\n return {\n trace_id: ctx.traceId,\n span_id: ctx.spanId,\n trace_flags: String(ctx.traceFlags),\n };\n}\n\n// ============================================================================\n// Logs SDK Layers\n// ============================================================================\n\n/**\n * Extended configuration for logs SDK.\n */\nexport interface LogsLayerConfig extends LogsSdkConfig {\n /** Minimum severity level to export. Logs below this level are filtered. */\n minSeverity?: SeverityNumber;\n}\n\n/**\n * Creates a Logs SDK layer with the given configuration.\n *\n * @param config - Logs SDK configuration\n * @returns Effect Layer providing OtelLogger service\n */\nfunction createLogsSdkLayer(config: LogsLayerConfig = {}) {\n return Layer.scoped(\n OtelLogger,\n Effect.gen(function* () {\n // Check if observability is disabled\n if (!isOtlpExportEnabled()) {\n // Return a no-op logger that doesn't export\n const { LoggerProvider } = yield* Effect.promise(\n () => import(\"@opentelemetry/sdk-logs\"),\n );\n const noopProvider = new LoggerProvider();\n return {\n logger: noopProvider.getLogger(getServiceName(\"uploadista\")),\n provider: noopProvider,\n };\n }\n\n // Create the OTLP LoggerProvider\n const provider = createOtlpLoggerProvider(config);\n const serviceName = config.serviceName ?? getServiceName(\"uploadista\");\n const logger = provider.getLogger(serviceName);\n\n // Register shutdown handler\n yield* Effect.addFinalizer(() =>\n Effect.promise(async () => {\n try {\n await provider.shutdown();\n } catch (error) {\n // Log but don't throw on shutdown errors\n console.warn(\"Error shutting down LoggerProvider:\", error);\n }\n }),\n );\n\n return { logger, provider };\n }),\n );\n}\n\n/**\n * Node.js OTLP Logs SDK Layer for production use.\n *\n * Exports logs to an OTLP-compatible endpoint with automatic trace correlation.\n *\n * @example\n * ```typescript\n * import { OtlpLogsNodeSdkLive, OtelLogger } from \"@uploadista/observability\";\n * import { Effect } from \"effect\";\n * import { SeverityNumber } from \"@opentelemetry/api-logs\";\n *\n * const program = Effect.gen(function* () {\n * const { logger } = yield* OtelLogger;\n * logger.emit({\n * severityNumber: SeverityNumber.INFO,\n * body: \"Upload completed\",\n * attributes: { uploadId: \"123\" },\n * });\n * }).pipe(Effect.provide(OtlpLogsNodeSdkLive));\n * ```\n */\nexport const OtlpLogsNodeSdkLive = createLogsSdkLayer();\n\n/**\n * Creates a customized OTLP Logs Node.js SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer\n *\n * @example\n * ```typescript\n * const customLogs = createOtlpLogsNodeSdkLayer({\n * serviceName: \"my-upload-service\",\n * minSeverity: SeverityNumber.WARN, // Only export WARN and above\n * });\n * ```\n */\nexport function createOtlpLogsNodeSdkLayer(config: LogsLayerConfig = {}) {\n return createLogsSdkLayer(config);\n}\n\n/**\n * Browser OTLP Logs SDK Layer for production use.\n *\n * Uses the same OTLP HTTP exporter, suitable for browser environments.\n * Note: Browser environments may have CORS restrictions.\n *\n * @example\n * ```typescript\n * import { OtlpLogsWebSdkLive } from \"@uploadista/observability\";\n *\n * const program = myEffect.pipe(Effect.provide(OtlpLogsWebSdkLive));\n * ```\n */\nexport const OtlpLogsWebSdkLive = createLogsSdkLayer();\n\n/**\n * Creates a customized OTLP Logs Web SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer for browser environments\n */\nexport function createOtlpLogsWebSdkLayer(config: LogsLayerConfig = {}) {\n return createLogsSdkLayer(config);\n}\n\n/**\n * Cloudflare Workers OTLP Logs SDK Layer for production use.\n *\n * Pre-configured with Workers-appropriate defaults.\n *\n * @example\n * ```typescript\n * import { OtlpLogsWorkersSdkLive } from \"@uploadista/observability\";\n *\n * export default {\n * async fetch(request, env) {\n * const program = handleRequest(request).pipe(\n * Effect.provide(OtlpLogsWorkersSdkLive)\n * );\n * return Effect.runPromise(program);\n * }\n * };\n * ```\n */\nexport const OtlpLogsWorkersSdkLive = createLogsSdkLayer({\n serviceName: getServiceName(\"uploadista-workers\"),\n});\n\n/**\n * Creates a customized OTLP Logs Workers SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer for Cloudflare Workers\n */\nexport function createOtlpLogsWorkersSdkLayer(config: LogsLayerConfig = {}) {\n return createLogsSdkLayer({\n serviceName: getServiceName(\"uploadista-workers\"),\n ...config,\n });\n}\n\n// ============================================================================\n// Effect Logger Integration\n// ============================================================================\n\n/**\n * Creates an Effect Logger that forwards logs to OTLP.\n *\n * This logger intercepts Effect.log calls and sends them to the OTLP endpoint\n * with automatic trace correlation and annotation support.\n *\n * @param minSeverity - Minimum severity to export (default: all)\n * @returns Effect Logger that exports to OTLP\n *\n * @example\n * ```typescript\n * import { createOtlpEffectLogger, OtlpLogsNodeSdkLive } from \"@uploadista/observability\";\n *\n * const program = Effect.gen(function* () {\n * yield* Effect.log(\"This will be exported to OTLP\");\n * yield* Effect.logError(\"Errors too!\");\n * }).pipe(\n * Effect.provide(OtlpLogsNodeSdkLive),\n * EffectLogger.withMinimumLogLevel(LogLevel.Debug),\n * );\n * ```\n */\nexport const createOtlpEffectLogger = (minSeverity?: SeverityNumber) =>\n EffectLogger.make<unknown, void>(({ logLevel, message, annotations }) => {\n // This is a synchronous logger - we'll emit to OTEL directly\n // In a real implementation, we'd need to access the OtelLogger from context\n // For now, we'll use the global approach\n\n const severity = mapLogLevelToSeverity(logLevel.label);\n\n // Filter by minimum severity if specified\n if (minSeverity !== undefined && severity < minSeverity) {\n return;\n }\n\n // Get trace context for correlation\n const traceContext = getTraceContext();\n\n // Convert annotations to attributes\n const attributes: Record<string, unknown> = {\n ...traceContext,\n };\n\n // Add annotations as attributes\n for (const [key, value] of annotations) {\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n attributes[key] = value;\n } else {\n attributes[key] = String(value);\n }\n }\n\n // Log to console as fallback (the actual OTLP export happens in the layer)\n // This ensures logs are not lost even if OTLP export fails\n const logFn =\n severity >= SeverityNumber.ERROR\n ? console.error\n : severity >= SeverityNumber.WARN\n ? console.warn\n : console.log;\n\n logFn(`[${logLevel.label}] ${String(message)}`, attributes);\n });\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Emits a log record to OTLP using the OtelLogger from context.\n *\n * @param level - Log level (debug, info, warn, error, fatal)\n * @param message - Log message\n * @param attributes - Optional log attributes\n * @returns Effect that emits the log\n *\n * @example\n * ```typescript\n * yield* emitLog(\"info\", \"Upload completed\", { uploadId: \"123\" });\n * ```\n */\nexport const emitLog = (\n level: \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\",\n message: string,\n attributes?: Record<string, string | number | boolean>,\n) =>\n Effect.gen(function* () {\n const { logger } = yield* OtelLogger;\n const severity = mapLogLevelToSeverity(level);\n const traceContext = getTraceContext();\n\n logger.emit({\n severityNumber: severity,\n severityText: severityToText(severity),\n body: message,\n attributes: {\n ...traceContext,\n ...attributes,\n },\n });\n });\n\n/**\n * Emits a debug log to OTLP.\n */\nexport const logDebug = (\n message: string,\n attributes?: Record<string, string | number | boolean>,\n) => emitLog(\"debug\", message, attributes);\n\n/**\n * Emits an info log to OTLP.\n */\nexport const logInfo = (\n message: string,\n attributes?: Record<string, string | number | boolean>,\n) => emitLog(\"info\", message, attributes);\n\n/**\n * Emits a warning log to OTLP.\n */\nexport const logWarn = (\n message: string,\n attributes?: Record<string, string | number | boolean>,\n) => emitLog(\"warn\", message, attributes);\n\n/**\n * Emits an error log to OTLP.\n */\nexport const logError = (\n message: string,\n attributes?: Record<string, string | number | boolean>,\n) => emitLog(\"error\", message, attributes);\n\n/**\n * Emits a fatal log to OTLP.\n */\nexport const logFatal = (\n message: string,\n attributes?: Record<string, string | number | boolean>,\n) => emitLog(\"fatal\", message, attributes);\n\n// Re-export SeverityNumber for convenience\nexport { SeverityNumber } from \"@opentelemetry/api-logs\";\n","/**\n * OTLP Metrics SDK Layers for Uploadista SDK.\n *\n * This module provides Effect Layers that export metrics to OTLP-compatible\n * backends like Grafana Mimir, Prometheus, Datadog, and others.\n *\n * Configuration is done via standard OpenTelemetry environment variables:\n * - OTEL_EXPORTER_OTLP_ENDPOINT: Base endpoint URL (default: http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: Override endpoint for metrics only\n * - OTEL_EXPORTER_OTLP_HEADERS: Headers for authentication\n * - OTEL_SERVICE_NAME: Service name (default: uploadista)\n * - OTEL_METRICS_EXPORT_INTERVAL: Export interval in ms (default: 60000)\n * - UPLOADISTA_OBSERVABILITY_ENABLED: Set to \"false\" to disable (default: true)\n *\n * @module core/metrics-sdk\n */\n\nimport type { Meter } from \"@opentelemetry/api\";\nimport type { MeterProvider } from \"@opentelemetry/sdk-metrics\";\nimport { Context, Effect, Layer } from \"effect\";\nimport {\n createOtlpMeterProvider,\n getServiceName,\n isOtlpExportEnabled,\n type MetricsSdkConfig,\n} from \"./exporters.js\";\n\n// ============================================================================\n// Metrics Service\n// ============================================================================\n\n/**\n * OpenTelemetry Meter service for recording metrics.\n */\nexport interface OtelMeterService {\n /** The OpenTelemetry Meter instance */\n readonly meter: Meter;\n /** The MeterProvider for shutdown handling */\n readonly provider: MeterProvider;\n}\n\n/**\n * Effect Context tag for the OTEL Meter service.\n */\nexport class OtelMeter extends Context.Tag(\"OtelMeter\")<\n OtelMeter,\n OtelMeterService\n>() {}\n\n// ============================================================================\n// Metrics SDK Layers\n// ============================================================================\n\n/**\n * Creates a Metrics SDK layer with the given configuration.\n *\n * @param config - Metrics SDK configuration\n * @returns Effect Layer providing OtelMeter service\n */\nfunction createMetricsSdkLayer(config: MetricsSdkConfig = {}) {\n return Layer.scoped(\n OtelMeter,\n Effect.gen(function* () {\n // Check if observability is disabled\n if (!isOtlpExportEnabled()) {\n // Return a no-op meter that doesn't export\n const { MeterProvider } = yield* Effect.promise(\n () => import(\"@opentelemetry/sdk-metrics\"),\n );\n const noopProvider = new MeterProvider();\n return {\n meter: noopProvider.getMeter(getServiceName(\"uploadista\")),\n provider: noopProvider,\n };\n }\n\n // Create the OTLP MeterProvider\n const provider = createOtlpMeterProvider(config);\n const serviceName = config.serviceName ?? getServiceName(\"uploadista\");\n const meter = provider.getMeter(serviceName);\n\n // Register shutdown handler\n yield* Effect.addFinalizer(() =>\n Effect.promise(async () => {\n try {\n await provider.shutdown();\n } catch (error) {\n // Log but don't throw on shutdown errors\n console.warn(\"Error shutting down MeterProvider:\", error);\n }\n }),\n );\n\n return { meter, provider };\n }),\n );\n}\n\n/**\n * Node.js OTLP Metrics SDK Layer for production use.\n *\n * Exports metrics to an OTLP-compatible endpoint configured via environment variables.\n *\n * @example\n * ```typescript\n * import { OtlpMetricsNodeSdkLive, OtelMeter } from \"@uploadista/observability\";\n * import { Effect } from \"effect\";\n *\n * const program = Effect.gen(function* () {\n * const { meter } = yield* OtelMeter;\n * const counter = meter.createCounter(\"uploads_total\");\n * counter.add(1, { storage: \"s3\" });\n * }).pipe(Effect.provide(OtlpMetricsNodeSdkLive));\n * ```\n */\nexport const OtlpMetricsNodeSdkLive = createMetricsSdkLayer();\n\n/**\n * Creates a customized OTLP Metrics Node.js SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer\n *\n * @example\n * ```typescript\n * const customMetrics = createOtlpMetricsNodeSdkLayer({\n * serviceName: \"my-upload-service\",\n * exportIntervalMillis: 30000, // Export every 30 seconds\n * });\n * ```\n */\nexport function createOtlpMetricsNodeSdkLayer(config: MetricsSdkConfig = {}) {\n return createMetricsSdkLayer(config);\n}\n\n/**\n * Browser OTLP Metrics SDK Layer for production use.\n *\n * Uses the same OTLP HTTP exporter, suitable for browser environments.\n * Note: Browser environments may have CORS restrictions.\n *\n * @example\n * ```typescript\n * import { OtlpMetricsWebSdkLive } from \"@uploadista/observability\";\n *\n * const program = myEffect.pipe(Effect.provide(OtlpMetricsWebSdkLive));\n * ```\n */\nexport const OtlpMetricsWebSdkLive = createMetricsSdkLayer();\n\n/**\n * Creates a customized OTLP Metrics Web SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer for browser environments\n */\nexport function createOtlpMetricsWebSdkLayer(config: MetricsSdkConfig = {}) {\n return createMetricsSdkLayer(config);\n}\n\n/**\n * Cloudflare Workers OTLP Metrics SDK Layer for production use.\n *\n * Pre-configured with Workers-appropriate defaults.\n *\n * @example\n * ```typescript\n * import { OtlpMetricsWorkersSdkLive } from \"@uploadista/observability\";\n *\n * export default {\n * async fetch(request, env) {\n * const program = handleRequest(request).pipe(\n * Effect.provide(OtlpMetricsWorkersSdkLive)\n * );\n * return Effect.runPromise(program);\n * }\n * };\n * ```\n */\nexport const OtlpMetricsWorkersSdkLive = createMetricsSdkLayer({\n serviceName: getServiceName(\"uploadista-workers\"),\n});\n\n/**\n * Creates a customized OTLP Metrics Workers SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer for Cloudflare Workers\n */\nexport function createOtlpMetricsWorkersSdkLayer(\n config: MetricsSdkConfig = {},\n) {\n return createMetricsSdkLayer({\n serviceName: getServiceName(\"uploadista-workers\"),\n ...config,\n });\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Records a counter metric using the OTEL Meter from context.\n *\n * @param name - Counter name\n * @param value - Value to add (default: 1)\n * @param attributes - Optional metric attributes\n * @returns Effect that records the counter\n *\n * @example\n * ```typescript\n * yield* recordCounter(\"uploads_total\", 1, { storage: \"s3\" });\n * ```\n */\nexport const recordCounter = (\n name: string,\n value = 1,\n attributes?: Record<string, string | number | boolean>,\n) =>\n Effect.gen(function* () {\n const { meter } = yield* OtelMeter;\n const counter = meter.createCounter(name);\n counter.add(value, attributes);\n });\n\n/**\n * Records a histogram metric using the OTEL Meter from context.\n *\n * @param name - Histogram name\n * @param value - Value to record\n * @param attributes - Optional metric attributes\n * @returns Effect that records the histogram\n *\n * @example\n * ```typescript\n * yield* recordHistogram(\"upload_duration_seconds\", 1.5, { storage: \"s3\" });\n * ```\n */\nexport const recordHistogram = (\n name: string,\n value: number,\n attributes?: Record<string, string | number | boolean>,\n) =>\n Effect.gen(function* () {\n const { meter } = yield* OtelMeter;\n const histogram = meter.createHistogram(name);\n histogram.record(value, attributes);\n });\n\n/**\n * Creates an observable gauge that reports the current value.\n *\n * @param name - Gauge name\n * @param callback - Function that returns the current value\n * @param attributes - Optional metric attributes\n * @returns Effect that registers the gauge\n *\n * @example\n * ```typescript\n * let activeUploads = 0;\n * yield* createGauge(\"active_uploads\", () => activeUploads);\n * ```\n */\nexport const createGauge = (\n name: string,\n callback: () => number,\n attributes?: Record<string, string | number | boolean>,\n) =>\n Effect.gen(function* () {\n const { meter } = yield* OtelMeter;\n meter\n .createObservableGauge(name, {\n description: name,\n })\n .addCallback((result) => {\n result.observe(callback(), attributes);\n });\n });\n","import { NodeSdk, WebSdk } from \"@effect/opentelemetry\";\nimport { trace } from \"@opentelemetry/api\";\nimport {\n BatchSpanProcessor,\n ConsoleSpanExporter,\n type SpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\nimport { Context, Effect, Layer, Option, Tracer } from \"effect\";\nimport {\n createOtlpTraceExporter,\n getServiceName,\n isOtlpExportEnabled,\n parseResourceAttributes,\n} from \"./exporters.js\";\nimport type { TraceContext } from \"./types.js\";\n\n// ============================================================================\n// Universal Tracing (Environment-agnostic)\n// ============================================================================\n\n// Generic service tag for tracing context\nexport const TracingService = Context.GenericTag<{ serviceName: string }>(\n \"TracingService\",\n);\n\n// Create a tracing layer using Effect's native tracing (works in all environments)\nexport const createTracingLayer = (options?: { serviceName?: string }) => {\n const serviceName = options?.serviceName ?? \"uploadista-storage\";\n\n // Return a layer that provides tracing service context\n return Layer.succeed(TracingService, { serviceName });\n};\n\n// Storage-specific tracing layers\nexport const createStorageTracingLayer = (storageType: string) =>\n createTracingLayer({\n serviceName: `uploadista-${storageType}-store`,\n });\n\n// Utility to add storage context to spans\nexport const withStorageSpan =\n <A, E, R>(\n operation: string,\n storageType: string,\n attributes?: Record<string, unknown>,\n ) =>\n (effect: Effect.Effect<A, E, R>) =>\n effect.pipe(\n Effect.withSpan(`${storageType}-${operation}`, {\n attributes: {\n \"storage.type\": storageType,\n operation: operation,\n ...attributes,\n },\n }),\n );\n\n// Set up tracing with the OpenTelemetry SDK\nexport const WebSdkLive = WebSdk.layer(() => ({\n resource: { serviceName: \"uploadista-storage\" },\n // Export span data to the console\n spanProcessor: new BatchSpanProcessor(new ConsoleSpanExporter()),\n}));\n\nexport const NodeSdkLive = NodeSdk.layer(() => ({\n resource: { serviceName: \"uploadista-storage\" },\n // Export span data to the console\n spanProcessor: new BatchSpanProcessor(new ConsoleSpanExporter()),\n}));\n\n// Cloudflare Workers SDK (uses WebSdk as base)\nexport const WorkersSdkLive = WebSdk.layer(() => ({\n resource: { serviceName: \"uploadista-storage-workers\" },\n // Export span data to the console in Workers environment\n spanProcessor: new BatchSpanProcessor(new ConsoleSpanExporter()),\n}));\n\n// ============================================================================\n// OTLP Export Layers (Production)\n// ============================================================================\n\n/**\n * Configuration options for OTLP SDK layers.\n */\nexport interface OtlpSdkConfig {\n /** Service name for traces. Defaults to OTEL_SERVICE_NAME or \"uploadista\" */\n serviceName?: string;\n /** Additional resource attributes to include in all spans */\n resourceAttributes?: Record<string, string>;\n /** Maximum queue size for batch processor. Defaults to 512 */\n maxQueueSize?: number;\n /** Maximum export batch size. Defaults to 512 */\n maxExportBatchSize?: number;\n /** Schedule delay in milliseconds. Defaults to 5000 */\n scheduledDelayMillis?: number;\n /** Export timeout in milliseconds. Defaults to 5000 */\n exportTimeoutMillis?: number;\n}\n\n/**\n * Creates a BatchSpanProcessor with OTLP exporter and graceful degradation.\n *\n * The processor is configured with:\n * - Configurable queue limits to prevent memory issues\n * - Export timeouts to prevent blocking\n * - Error handling that drops data rather than failing requests\n *\n * @param config - Optional configuration\n * @returns Configured BatchSpanProcessor\n */\nfunction createOtlpSpanProcessor(config: OtlpSdkConfig = {}): SpanProcessor {\n const exporter = createOtlpTraceExporter();\n\n return new BatchSpanProcessor(exporter, {\n maxQueueSize: config.maxQueueSize ?? 512,\n maxExportBatchSize: config.maxExportBatchSize ?? 512,\n scheduledDelayMillis: config.scheduledDelayMillis ?? 5000,\n // Default to 30 seconds to accommodate cloud endpoints like Grafana Cloud\n exportTimeoutMillis: config.exportTimeoutMillis ?? 30000,\n });\n}\n\n/**\n * Creates resource configuration from environment and config.\n */\nfunction createResourceConfig(config: OtlpSdkConfig = {}): {\n serviceName: string;\n [key: string]: string;\n} {\n const serviceName = config.serviceName ?? getServiceName(\"uploadista\");\n const envAttributes = parseResourceAttributes();\n const configAttributes = config.resourceAttributes ?? {};\n\n return {\n serviceName,\n ...envAttributes,\n ...configAttributes,\n };\n}\n\n/**\n * Node.js OTLP SDK Layer for production use.\n *\n * Exports traces to an OTLP-compatible endpoint configured via environment variables:\n * - OTEL_EXPORTER_OTLP_ENDPOINT: Base endpoint (default: http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_HEADERS: Authentication headers\n * - OTEL_SERVICE_NAME: Service name (default: uploadista)\n * - OTEL_RESOURCE_ATTRIBUTES: Additional resource attributes\n * - UPLOADISTA_OBSERVABILITY_ENABLED: Set to \"false\" to disable (default: true)\n *\n * @example\n * ```typescript\n * import { OtlpNodeSdkLive } from \"@uploadista/observability\";\n * import { Effect } from \"effect\";\n *\n * // With default environment configuration\n * const program = myEffect.pipe(Effect.provide(OtlpNodeSdkLive));\n *\n * // Run with:\n * // OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318\n * // OTEL_SERVICE_NAME=my-upload-service\n * ```\n */\nexport const OtlpNodeSdkLive = NodeSdk.layer(() => {\n // Check if observability is disabled\n if (!isOtlpExportEnabled()) {\n // Return no-op configuration (no span processor means no export)\n return {\n resource: createResourceConfig(),\n };\n }\n\n return {\n resource: createResourceConfig(),\n spanProcessor: createOtlpSpanProcessor(),\n };\n});\n\n/**\n * Creates a customized OTLP Node.js SDK Layer.\n *\n * Use this when you need to customize the SDK configuration beyond\n * what environment variables provide.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer\n *\n * @example\n * ```typescript\n * const customSdk = createOtlpNodeSdkLayer({\n * serviceName: \"my-custom-service\",\n * resourceAttributes: {\n * \"tenant.id\": \"abc123\",\n * \"deployment.environment\": \"production\"\n * },\n * maxQueueSize: 1024,\n * });\n *\n * const program = myEffect.pipe(Effect.provide(customSdk));\n * ```\n */\nexport function createOtlpNodeSdkLayer(config: OtlpSdkConfig = {}) {\n return NodeSdk.layer(() => {\n if (!isOtlpExportEnabled()) {\n return {\n resource: createResourceConfig(config),\n };\n }\n\n return {\n resource: createResourceConfig(config),\n spanProcessor: createOtlpSpanProcessor(config),\n };\n });\n}\n\n/**\n * Browser OTLP SDK Layer for production use.\n *\n * Similar to OtlpNodeSdkLive but uses fetch API for browser compatibility.\n * Note: Browser environments may have CORS restrictions.\n *\n * @example\n * ```typescript\n * import { OtlpWebSdkLive } from \"@uploadista/observability\";\n *\n * const program = myEffect.pipe(Effect.provide(OtlpWebSdkLive));\n * ```\n */\nexport const OtlpWebSdkLive = WebSdk.layer(() => {\n if (!isOtlpExportEnabled()) {\n return {\n resource: createResourceConfig(),\n };\n }\n\n return {\n resource: createResourceConfig(),\n spanProcessor: createOtlpSpanProcessor(),\n };\n});\n\n/**\n * Creates a customized OTLP Web SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer for browser environments\n */\nexport function createOtlpWebSdkLayer(config: OtlpSdkConfig = {}) {\n return WebSdk.layer(() => {\n if (!isOtlpExportEnabled()) {\n return {\n resource: createResourceConfig(config),\n };\n }\n\n return {\n resource: createResourceConfig(config),\n spanProcessor: createOtlpSpanProcessor(config),\n };\n });\n}\n\n/**\n * Cloudflare Workers OTLP SDK Layer for production use.\n *\n * Uses the Web SDK under the hood with fetch-based export.\n * Suitable for edge computing environments.\n *\n * @example\n * ```typescript\n * import { OtlpWorkersSdkLive } from \"@uploadista/observability\";\n *\n * export default {\n * async fetch(request, env) {\n * const program = handleRequest(request).pipe(\n * Effect.provide(OtlpWorkersSdkLive)\n * );\n * return Effect.runPromise(program);\n * }\n * };\n * ```\n */\nexport const OtlpWorkersSdkLive = WebSdk.layer(() => {\n const config: OtlpSdkConfig = {\n serviceName: getServiceName(\"uploadista-workers\"),\n };\n\n if (!isOtlpExportEnabled()) {\n return {\n resource: createResourceConfig(config),\n };\n }\n\n return {\n resource: createResourceConfig(config),\n spanProcessor: createOtlpSpanProcessor(config),\n };\n});\n\n/**\n * Creates a customized OTLP Workers SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer for Cloudflare Workers\n */\nexport function createOtlpWorkersSdkLayer(config: OtlpSdkConfig = {}) {\n return WebSdk.layer(() => {\n const effectiveConfig = {\n serviceName: getServiceName(\"uploadista-workers\"),\n ...config,\n };\n\n if (!isOtlpExportEnabled()) {\n return {\n resource: createResourceConfig(effectiveConfig),\n };\n }\n\n return {\n resource: createResourceConfig(effectiveConfig),\n spanProcessor: createOtlpSpanProcessor(effectiveConfig),\n };\n });\n}\n\n// ============================================================================\n// Distributed Tracing Context Utilities\n// ============================================================================\n\n/**\n * @deprecated Use `captureTraceContextEffect` instead. This synchronous function\n * uses OpenTelemetry's `trace.getActiveSpan()` which may not be synchronized\n * with Effect's span context when using @effect/opentelemetry.\n *\n * @returns TraceContext if there's an active OpenTelemetry span, undefined otherwise\n */\nexport function captureTraceContext(): TraceContext | undefined {\n const currentSpan = trace.getActiveSpan();\n if (!currentSpan) {\n return undefined;\n }\n\n const spanContext = currentSpan.spanContext();\n return {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n traceFlags: spanContext.traceFlags,\n };\n}\n\n/**\n * Captures the current Effect trace context for distributed tracing.\n *\n * Uses Effect's `currentSpan` to get the active span, which is more reliable\n * than OpenTelemetry's `trace.getActiveSpan()` when using @effect/opentelemetry\n * because Effect manages its own span context that may not be synchronized\n * with OpenTelemetry's global context.\n *\n * Use this to save the trace context (traceId, spanId, traceFlags) for later\n * use in distributed tracing. The captured context can be stored alongside\n * data (e.g., in KV store with upload metadata) and restored later using\n * `createExternalSpan` and passing it to `Effect.withSpan`'s `parent` option.\n *\n * @returns Effect yielding TraceContext if there's an active span, undefined otherwise\n *\n * @example\n * ```typescript\n * // Capture context during upload creation\n * const createUpload = Effect.gen(function* () {\n * const traceContext = yield* captureTraceContextEffect;\n *\n * const file: UploadFile = {\n * id: uploadId,\n * traceContext, // Store for later\n * // ...\n * };\n * yield* kvStore.set(uploadId, file);\n * }).pipe(Effect.withSpan(\"upload-create\", { ... }));\n * ```\n */\nexport const captureTraceContextEffect: Effect.Effect<\n TraceContext | undefined\n> = Effect.gen(function* () {\n const spanOption = yield* Effect.currentSpan.pipe(Effect.option);\n return Option.match(spanOption, {\n onNone: () => undefined,\n onSome: (span) => ({\n traceId: span.traceId,\n spanId: span.spanId,\n traceFlags: span.sampled ? 1 : 0,\n }),\n });\n});\n\n/**\n * Creates an ExternalSpan from a stored trace context.\n *\n * Use this to create a parent span reference that can be passed to\n * `Effect.withSpan`'s `parent` option for distributed tracing.\n *\n * **Important:** The parent must be passed directly to `Effect.withSpan`'s\n * options, not provided as a service afterward.\n *\n * @param traceContext - Previously captured trace context\n * @returns ExternalSpan that can be used as a parent in Effect.withSpan\n *\n * @example\n * ```typescript\n * // Create parent span from stored trace context\n * const parentSpan = file.traceContext\n * ? createExternalSpan(file.traceContext)\n * : undefined;\n *\n * // Pass parent directly to withSpan\n * const chunkEffect = Effect.gen(function* () {\n * // ... chunk upload logic\n * }).pipe(\n * Effect.withSpan(\"upload-chunk\", {\n * attributes: { ... },\n * parent: parentSpan, // Link to original trace\n * })\n * );\n * ```\n */\nexport function createExternalSpan(traceContext: TraceContext) {\n return Tracer.externalSpan({\n traceId: traceContext.traceId,\n spanId: traceContext.spanId,\n sampled: traceContext.traceFlags === 1,\n });\n}\n\n/**\n * @deprecated Use `createExternalSpan` instead and pass the result to\n * `Effect.withSpan`'s `parent` option directly. This function doesn't\n * work correctly because Effect.withSpan reads the parent at construction\n * time, not from the provided service.\n *\n * @example\n * ```typescript\n * // Instead of:\n * withParentContext(traceContext)(effect.pipe(Effect.withSpan(...)))\n *\n * // Do this:\n * const parent = createExternalSpan(traceContext);\n * effect.pipe(Effect.withSpan(\"name\", { parent }))\n * ```\n */\nexport function withParentContext(traceContext: TraceContext) {\n return <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> => {\n const externalSpan = Tracer.externalSpan({\n traceId: traceContext.traceId,\n spanId: traceContext.spanId,\n sampled: traceContext.traceFlags === 1,\n });\n\n return effect.pipe(Effect.provideService(Tracer.ParentSpan, externalSpan));\n };\n}\n\n/**\n * Checks if there's an active trace context.\n *\n * Useful for conditional logic based on whether tracing is active.\n *\n * @returns true if there's an active span with valid trace context\n *\n * @example\n * ```typescript\n * if (hasActiveTraceContext()) {\n * console.log(\"Tracing is active\");\n * }\n * ```\n */\nexport function hasActiveTraceContext(): boolean {\n const span = trace.getActiveSpan();\n if (!span) return false;\n\n const ctx = span.spanContext();\n // Check if the trace ID is valid (not all zeros)\n return ctx.traceId !== \"00000000000000000000000000000000\";\n}\n","/**\n * Full Observability SDK Layers for Uploadista SDK.\n *\n * This module provides combined Effect Layers that enable all three pillars\n * of observability (Traces, Metrics, Logs) with a single import.\n *\n * Configuration is done via standard OpenTelemetry environment variables:\n * - OTEL_EXPORTER_OTLP_ENDPOINT: Base endpoint URL (default: http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_HEADERS: Headers for authentication\n * - OTEL_SERVICE_NAME: Service name (default: uploadista)\n * - UPLOADISTA_OBSERVABILITY_ENABLED: Set to \"false\" to disable (default: true)\n *\n * @module core/full-observability\n */\n\nimport { Effect, Layer } from \"effect\";\nimport type { MetricsSdkConfig } from \"./exporters.js\";\nimport { getServiceName } from \"./exporters.js\";\nimport {\n createOtlpLogsNodeSdkLayer,\n createOtlpLogsWebSdkLayer,\n createOtlpLogsWorkersSdkLayer,\n type LogsLayerConfig,\n OtlpLogsNodeSdkLive,\n OtlpLogsWebSdkLive,\n OtlpLogsWorkersSdkLive,\n} from \"./logs-sdk.js\";\nimport {\n createOtlpMetricsNodeSdkLayer,\n createOtlpMetricsWebSdkLayer,\n createOtlpMetricsWorkersSdkLayer,\n OtlpMetricsNodeSdkLive,\n OtlpMetricsWebSdkLive,\n OtlpMetricsWorkersSdkLive,\n} from \"./metrics-sdk.js\";\nimport {\n createOtlpNodeSdkLayer,\n createOtlpWebSdkLayer,\n createOtlpWorkersSdkLayer,\n OtlpNodeSdkLive,\n type OtlpSdkConfig,\n OtlpWebSdkLive,\n OtlpWorkersSdkLive,\n} from \"./tracing.js\";\n\n// ============================================================================\n// Combined Configuration\n// ============================================================================\n\n/**\n * Configuration for full observability SDK.\n */\nexport interface FullObservabilityConfig {\n /** Service name for all telemetry. Defaults to OTEL_SERVICE_NAME or \"uploadista\" */\n serviceName?: string;\n /** Additional resource attributes */\n resourceAttributes?: Record<string, string>;\n /** Traces-specific configuration */\n traces?: Omit<OtlpSdkConfig, \"serviceName\" | \"resourceAttributes\">;\n /** Metrics-specific configuration */\n metrics?: Omit<MetricsSdkConfig, \"serviceName\">;\n /** Logs-specific configuration */\n logs?: Omit<LogsLayerConfig, \"serviceName\">;\n}\n\n// ============================================================================\n// Combined SDK Layers\n// ============================================================================\n\n/**\n * Node.js Full Observability SDK Layer.\n *\n * Combines traces, metrics, and logs export into a single layer.\n * Use this for easy setup of complete observability.\n *\n * @example\n * ```typescript\n * import { OtlpFullObservabilityNodeSdkLive } from \"@uploadista/observability\";\n * import { Effect } from \"effect\";\n *\n * const program = Effect.gen(function* () {\n * // All three pillars are now active:\n * // - Traces via Effect.withSpan\n * // - Metrics via OtelMeter\n * // - Logs via OtelLogger\n * yield* Effect.log(\"This goes to OTLP!\");\n * }).pipe(Effect.provide(OtlpFullObservabilityNodeSdkLive));\n * ```\n */\nexport const OtlpFullObservabilityNodeSdkLive = Layer.mergeAll(\n OtlpNodeSdkLive,\n OtlpMetricsNodeSdkLive,\n OtlpLogsNodeSdkLive,\n);\n\n/**\n * Creates a customized Full Observability Node.js SDK Layer.\n *\n * @param config - Configuration for all three pillars\n * @returns Combined Effect Layer\n *\n * @example\n * ```typescript\n * const customObservability = createOtlpFullObservabilityNodeSdkLayer({\n * serviceName: \"my-upload-service\",\n * traces: { maxQueueSize: 1024 },\n * metrics: { exportIntervalMillis: 30000 },\n * logs: { minSeverity: SeverityNumber.WARN },\n * });\n * ```\n */\nexport function createOtlpFullObservabilityNodeSdkLayer(\n config: FullObservabilityConfig = {},\n) {\n const serviceName = config.serviceName ?? getServiceName(\"uploadista\");\n const resourceAttributes = config.resourceAttributes;\n\n return Layer.mergeAll(\n createOtlpNodeSdkLayer({\n serviceName,\n resourceAttributes,\n ...config.traces,\n }),\n createOtlpMetricsNodeSdkLayer({\n serviceName,\n ...config.metrics,\n }),\n createOtlpLogsNodeSdkLayer({\n serviceName,\n ...config.logs,\n }),\n );\n}\n\n/**\n * Browser Full Observability SDK Layer.\n *\n * Combines traces, metrics, and logs export for browser environments.\n *\n * @example\n * ```typescript\n * import { OtlpFullObservabilityWebSdkLive } from \"@uploadista/observability\";\n *\n * const program = myEffect.pipe(Effect.provide(OtlpFullObservabilityWebSdkLive));\n * ```\n */\nexport const OtlpFullObservabilityWebSdkLive = Layer.mergeAll(\n OtlpWebSdkLive,\n OtlpMetricsWebSdkLive,\n OtlpLogsWebSdkLive,\n);\n\n/**\n * Creates a customized Full Observability Web SDK Layer.\n *\n * @param config - Configuration for all three pillars\n * @returns Combined Effect Layer for browser environments\n */\nexport function createOtlpFullObservabilityWebSdkLayer(\n config: FullObservabilityConfig = {},\n) {\n const serviceName = config.serviceName ?? getServiceName(\"uploadista\");\n const resourceAttributes = config.resourceAttributes;\n\n return Layer.mergeAll(\n createOtlpWebSdkLayer({\n serviceName,\n resourceAttributes,\n ...config.traces,\n }),\n createOtlpMetricsWebSdkLayer({\n serviceName,\n ...config.metrics,\n }),\n createOtlpLogsWebSdkLayer({\n serviceName,\n ...config.logs,\n }),\n );\n}\n\n/**\n * Cloudflare Workers Full Observability SDK Layer.\n *\n * Combines traces, metrics, and logs export for Workers environments.\n *\n * @example\n * ```typescript\n * import { OtlpFullObservabilityWorkersSdkLive } from \"@uploadista/observability\";\n *\n * export default {\n * async fetch(request, env) {\n * const program = handleRequest(request).pipe(\n * Effect.provide(OtlpFullObservabilityWorkersSdkLive)\n * );\n * return Effect.runPromise(program);\n * }\n * };\n * ```\n */\nexport const OtlpFullObservabilityWorkersSdkLive = Layer.mergeAll(\n OtlpWorkersSdkLive,\n OtlpMetricsWorkersSdkLive,\n OtlpLogsWorkersSdkLive,\n);\n\n/**\n * Creates a customized Full Observability Workers SDK Layer.\n *\n * @param config - Configuration for all three pillars\n * @returns Combined Effect Layer for Cloudflare Workers\n */\nexport function createOtlpFullObservabilityWorkersSdkLayer(\n config: FullObservabilityConfig = {},\n) {\n const serviceName =\n config.serviceName ?? getServiceName(\"uploadista-workers\");\n const resourceAttributes = config.resourceAttributes;\n\n return Layer.mergeAll(\n createOtlpWorkersSdkLayer({\n serviceName,\n resourceAttributes,\n ...config.traces,\n }),\n createOtlpMetricsWorkersSdkLayer({\n serviceName,\n ...config.metrics,\n }),\n createOtlpLogsWorkersSdkLayer({\n serviceName,\n ...config.logs,\n }),\n );\n}\n\n// ============================================================================\n// Auto-Detection Layer\n// ============================================================================\n\n/**\n * Runtime environment types.\n */\nexport type Environment = \"node\" | \"web\" | \"workers\";\n\n/**\n * Detects the current runtime environment.\n *\n * @returns The detected environment\n */\nexport function detectEnvironment(): Environment {\n // Check for Node.js\n if (typeof process !== \"undefined\" && process.versions?.node) {\n return \"node\";\n }\n\n // Check for browser (has navigator and window)\n if (typeof navigator !== \"undefined\" && typeof window !== \"undefined\") {\n return \"web\";\n }\n\n // Default to workers (Cloudflare Workers, Deno Deploy, etc.)\n return \"workers\";\n}\n\n/**\n * Auto-detecting Full Observability SDK Layer.\n *\n * Automatically selects the appropriate layer based on runtime environment.\n *\n * @example\n * ```typescript\n * import { OtlpAutoSdkLive } from \"@uploadista/observability\";\n *\n * // Works in Node.js, Browser, or Workers automatically\n * const program = myEffect.pipe(Effect.provide(OtlpAutoSdkLive));\n * ```\n */\nexport const OtlpAutoSdkLive = Layer.unwrapEffect(\n Effect.sync(() => {\n const env = detectEnvironment();\n switch (env) {\n case \"node\":\n return OtlpFullObservabilityNodeSdkLive;\n case \"web\":\n return OtlpFullObservabilityWebSdkLive;\n case \"workers\":\n return OtlpFullObservabilityWorkersSdkLive;\n }\n }),\n);\n\n/**\n * Creates an auto-detecting Full Observability SDK Layer with custom config.\n *\n * @param config - Configuration for all three pillars\n * @returns Auto-detecting combined Effect Layer\n */\nexport function createOtlpAutoSdkLayer(config: FullObservabilityConfig = {}) {\n return Layer.unwrapEffect(\n Effect.sync(() => {\n const env = detectEnvironment();\n switch (env) {\n case \"node\":\n return createOtlpFullObservabilityNodeSdkLayer(config);\n case \"web\":\n return createOtlpFullObservabilityWebSdkLayer(config);\n case \"workers\":\n return createOtlpFullObservabilityWorkersSdkLayer(config);\n }\n }),\n );\n}\n","import { Context, Effect, Layer, Option } from \"effect\";\nimport type { StorageMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Observability Layer Interfaces\n// ============================================================================\n\n/**\n * Core observability service providing tracing, metrics, and logging capabilities\n */\nexport interface ObservabilityService {\n readonly serviceName: string;\n readonly enabled: boolean;\n}\n\n/**\n * Observability service tag for Effect Context\n */\nexport class Observability extends Context.Tag(\"Observability\")<\n Observability,\n ObservabilityService\n>() {}\n\n/**\n * Storage observability service extending base observability with storage-specific metrics\n */\nexport interface StorageObservabilityService extends ObservabilityService {\n readonly storageType: string;\n readonly metrics: StorageMetrics;\n}\n\n/**\n * Storage observability service tag\n */\nexport class StorageObservability extends Context.Tag(\"StorageObservability\")<\n StorageObservability,\n StorageObservabilityService\n>() {}\n\n/**\n * Upload observability service for upload-specific operations\n */\nexport interface UploadObservabilityService extends ObservabilityService {\n readonly metrics: {\n uploadCreated: Effect.Effect<void>;\n uploadCompleted: Effect.Effect<void>;\n uploadFailed: Effect.Effect<void>;\n chunkUploaded: Effect.Effect<void>;\n };\n}\n\n/**\n * Upload observability service tag\n */\nexport class UploadObservability extends Context.Tag(\"UploadObservability\")<\n UploadObservability,\n UploadObservabilityService\n>() {}\n\n/**\n * Flow observability service for flow execution operations\n */\nexport interface FlowObservabilityService extends ObservabilityService {\n readonly metrics: {\n flowStarted: Effect.Effect<void>;\n flowCompleted: Effect.Effect<void>;\n flowFailed: Effect.Effect<void>;\n nodeExecuted: Effect.Effect<void>;\n };\n}\n\n/**\n * Flow observability service tag\n */\nexport class FlowObservability extends Context.Tag(\"FlowObservability\")<\n FlowObservability,\n FlowObservabilityService\n>() {}\n\n// ============================================================================\n// Layer Factories\n// ============================================================================\n\n/**\n * Create a base observability layer\n */\nexport const makeObservabilityLayer = (\n serviceName: string,\n enabled = true,\n): Layer.Layer<Observability> =>\n Layer.succeed(Observability, {\n serviceName,\n enabled,\n });\n\n/**\n * Create a storage observability layer\n */\nexport const makeStorageObservabilityLayer = (\n storageType: string,\n metrics: StorageMetrics,\n enabled = true,\n): Layer.Layer<StorageObservability> =>\n Layer.succeed(StorageObservability, {\n serviceName: `uploadista-${storageType}-store`,\n storageType,\n metrics,\n enabled,\n });\n\n/**\n * Create an upload observability layer\n */\nexport const makeUploadObservabilityLayer = (\n enabled = true,\n): Layer.Layer<UploadObservability> =>\n Layer.succeed(UploadObservability, {\n serviceName: \"uploadista-upload-server\",\n enabled,\n metrics: {\n uploadCreated: Effect.void,\n uploadCompleted: Effect.void,\n uploadFailed: Effect.void,\n chunkUploaded: Effect.void,\n },\n });\n\n/**\n * Create a flow observability layer\n */\nexport const makeFlowObservabilityLayer = (\n enabled = true,\n): Layer.Layer<FlowObservability> =>\n Layer.succeed(FlowObservability, {\n serviceName: \"uploadista-flow-engine\",\n enabled,\n metrics: {\n flowStarted: Effect.void,\n flowCompleted: Effect.void,\n flowFailed: Effect.void,\n nodeExecuted: Effect.void,\n },\n });\n\n// ============================================================================\n// No-op Layers (for testing and opt-out)\n// ============================================================================\n\n/**\n * No-op observability layer (disabled)\n */\nexport const ObservabilityDisabled = makeObservabilityLayer(\n \"uploadista-disabled\",\n false,\n);\n\n/**\n * No-op storage observability layer\n */\nexport const StorageObservabilityDisabled = (storageType: string) =>\n makeStorageObservabilityLayer(\n storageType,\n {} as StorageMetrics, // No-op metrics\n false,\n );\n\n/**\n * No-op upload observability layer\n */\nexport const UploadObservabilityDisabled = makeUploadObservabilityLayer(false);\n\n/**\n * No-op flow observability layer\n */\nexport const FlowObservabilityDisabled = makeFlowObservabilityLayer(false);\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Check if observability is enabled in the current context\n */\nexport const isObservabilityEnabled = Effect.gen(function* () {\n const observability = yield* Effect.serviceOption(Observability);\n return Option.match(observability, {\n onNone: () => false,\n onSome: (obs) => obs.enabled,\n });\n});\n\n/**\n * Execute an effect only if observability is enabled\n */\nexport const whenObservabilityEnabled = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<Option.Option<A>, E, R | Observability> =>\n Effect.gen(function* () {\n const enabled = yield* isObservabilityEnabled;\n if (enabled) {\n const result = yield* effect;\n return Option.some(result);\n }\n return Option.none();\n });\n","import { Effect } from \"effect\";\n\n// ============================================================================\n// Enhanced Logging Helpers (Storage-agnostic)\n// ============================================================================\n\nexport const logWithContext = (\n message: string,\n context: Record<string, unknown>,\n) => Effect.log(message).pipe(Effect.annotateLogs(context));\n\nexport const logUploadProgress = (\n storageType: string,\n uploadId: string,\n progress: {\n uploadedBytes: number;\n totalBytes: number;\n partNumber?: number;\n speed?: number;\n },\n) =>\n logWithContext(\"Upload progress\", {\n storage_type: storageType,\n upload_id: uploadId,\n uploaded_bytes: progress.uploadedBytes,\n total_bytes: progress.totalBytes,\n progress_percentage: Math.round(\n (progress.uploadedBytes / progress.totalBytes) * 100,\n ),\n ...(progress.partNumber && { part_number: progress.partNumber }),\n ...(progress.speed && { upload_speed_bps: progress.speed }),\n });\n\nexport const logStorageOperation = (\n storageType: string,\n operation: string,\n uploadId: string,\n metadata?: Record<string, unknown>,\n) =>\n logWithContext(`${storageType.toUpperCase()} ${operation}`, {\n storage_type: storageType,\n operation,\n upload_id: uploadId,\n ...metadata,\n });\n\nexport const logUploadCompletion = (\n storageType: string,\n uploadId: string,\n metrics: {\n fileSize: number;\n totalDurationMs: number;\n partsCount?: number;\n averagePartSize?: number;\n throughputBps?: number;\n retryCount?: number;\n },\n) => {\n const throughputMBps = metrics.throughputBps\n ? metrics.throughputBps / (1024 * 1024)\n : 0;\n\n return logWithContext(`${storageType.toUpperCase()} upload completed`, {\n storage_type: storageType,\n upload_id: uploadId,\n file_size_bytes: metrics.fileSize,\n file_size_mb: Math.round((metrics.fileSize / (1024 * 1024)) * 100) / 100,\n total_duration_ms: metrics.totalDurationMs,\n total_duration_seconds:\n Math.round((metrics.totalDurationMs / 1000) * 100) / 100,\n throughput_bps: metrics.throughputBps,\n throughput_mbps: Math.round(throughputMBps * 100) / 100,\n ...(metrics.partsCount && { parts_count: metrics.partsCount }),\n ...(metrics.averagePartSize && {\n average_part_size_bytes: metrics.averagePartSize,\n average_part_size_mb:\n Math.round((metrics.averagePartSize / (1024 * 1024)) * 100) / 100,\n }),\n ...(metrics.retryCount && { retry_count: metrics.retryCount }),\n });\n};\n","import { Metric, MetricBoundaries } from \"effect\";\n\n// ============================================================================\n// Core Storage Metrics (reusable across all storage types)\n// ============================================================================\n\n// Counter metrics\nexport const createUploadMetrics = (storageType: string) => ({\n uploadRequestsTotal: Metric.counter(`${storageType}_upload_requests_total`, {\n description: `Total number of upload requests for ${storageType}`,\n }),\n\n uploadPartsTotal: Metric.counter(`${storageType}_upload_parts_total`, {\n description: `Total number of individual parts uploaded for ${storageType}`,\n }),\n\n uploadSuccessTotal: Metric.counter(`${storageType}_upload_success_total`, {\n description: `Total number of successful uploads for ${storageType}`,\n }),\n\n uploadErrorsTotal: Metric.counter(`${storageType}_upload_errors_total`, {\n description: `Total number of upload errors for ${storageType}`,\n }),\n\n apiCallsTotal: Metric.counter(`${storageType}_api_calls_total`, {\n description: `Total number of API calls for ${storageType}`,\n }),\n});\n\n// Histogram metrics for timing and sizes (reusable)\nexport const createUploadHistograms = (storageType: string) => ({\n uploadDurationHistogram: Metric.histogram(\n `${storageType}_upload_duration_seconds`,\n MetricBoundaries.exponential({\n start: 0.01, // 10ms\n factor: 2,\n count: 20, // Up to ~10 seconds\n }),\n `Duration of upload operations in seconds for ${storageType}`,\n ),\n\n partUploadDurationHistogram: Metric.histogram(\n `${storageType}_part_upload_duration_seconds`,\n MetricBoundaries.exponential({\n start: 0.001, // 1ms\n factor: 2,\n count: 15, // Up to ~32 seconds\n }),\n `Duration of individual part uploads in seconds for ${storageType}`,\n ),\n\n fileSizeHistogram: Metric.histogram(\n `${storageType}_file_size_bytes`,\n MetricBoundaries.exponential({\n start: 1024, // 1KB\n factor: 2,\n count: 25, // Up to ~33GB\n }),\n `Size of uploaded files in bytes for ${storageType}`,\n ),\n\n partSizeHistogram: Metric.histogram(\n `${storageType}_part_size_bytes`,\n MetricBoundaries.linear({\n start: 5_242_880, // 5MB (minimum part size)\n width: 1_048_576, // 1MB increments\n count: 20, // Up to ~25MB\n }),\n `Size of upload parts in bytes for ${storageType}`,\n ),\n});\n\n// Gauge metrics for current state (reusable)\nexport const createUploadGauges = (storageType: string) => ({\n activeUploadsGauge: Metric.gauge(`${storageType}_active_uploads`, {\n description: `Number of currently active uploads for ${storageType}`,\n }),\n\n uploadThroughputGauge: Metric.gauge(\n `${storageType}_upload_throughput_bytes_per_second`,\n {\n description: `Current upload throughput in bytes per second for ${storageType}`,\n },\n ),\n});\n\n// Summary metrics for percentiles (reusable)\nexport const createUploadSummaries = (storageType: string) => ({\n uploadLatencySummary: Metric.summary({\n name: `${storageType}_upload_latency_seconds`,\n maxAge: \"10 minutes\",\n maxSize: 1000,\n error: 0.01,\n quantiles: [0.5, 0.9, 0.95, 0.99],\n description: `Upload latency percentiles for ${storageType}`,\n }),\n});\n\n// Combined metrics factory\nexport const createStorageMetrics = (storageType: string) => ({\n ...createUploadMetrics(storageType),\n ...createUploadHistograms(storageType),\n ...createUploadGauges(storageType),\n ...createUploadSummaries(storageType),\n});\n\n// Type for storage metrics\nexport type StorageMetrics = ReturnType<typeof createStorageMetrics>;\n","import { Effect, Layer, Metric } from \"effect\";\nimport type {\n FlowObservabilityService,\n StorageObservabilityService,\n UploadObservabilityService,\n} from \"./layers.js\";\nimport {\n FlowObservability,\n StorageObservability,\n UploadObservability,\n} from \"./layers.js\";\nimport { createStorageMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Test Observability Layers\n// ============================================================================\n\n/**\n * Mock storage observability for testing\n */\nexport const makeTestStorageObservability = (\n storageType: string,\n): Layer.Layer<StorageObservability> => {\n const metrics = createStorageMetrics(storageType);\n const service: StorageObservabilityService = {\n serviceName: `test-${storageType}-store`,\n storageType,\n metrics,\n enabled: true,\n };\n return Layer.succeed(StorageObservability, service);\n};\n\n/**\n * Mock upload observability for testing\n */\nexport const makeTestUploadObservability =\n (): Layer.Layer<UploadObservability> => {\n const service: UploadObservabilityService = {\n serviceName: \"test-upload-server\",\n enabled: true,\n metrics: {\n uploadCreated: Effect.void,\n uploadCompleted: Effect.void,\n uploadFailed: Effect.void,\n chunkUploaded: Effect.void,\n },\n };\n return Layer.succeed(UploadObservability, service);\n };\n\n/**\n * Mock flow observability for testing\n */\nexport const makeTestFlowObservability = (): Layer.Layer<FlowObservability> => {\n const service: FlowObservabilityService = {\n serviceName: \"test-flow-engine\",\n enabled: true,\n metrics: {\n flowStarted: Effect.void,\n flowCompleted: Effect.void,\n flowFailed: Effect.void,\n nodeExecuted: Effect.void,\n },\n };\n return Layer.succeed(FlowObservability, service);\n};\n\n// ============================================================================\n// Test Utilities\n// ============================================================================\n\n/**\n * Capture metrics snapshot from an effect for testing\n * Note: Metric snapshots are simplified - for full metric testing,\n * use Effect's built-in metric testing utilities\n */\nexport const captureMetrics = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n Effect.gen(function* () {\n const result = yield* effect;\n // Metrics are automatically captured by Effect runtime\n yield* Metric.snapshot;\n return result;\n });\n\n/**\n * Test helper to capture metrics around effect execution\n * This is a simplified version - for production testing,\n * use Effect's metric testing utilities\n */\nexport const withMetricTracking = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n Effect.gen(function* () {\n // Track metric before execution\n yield* Metric.snapshot;\n const result = yield* effect;\n // Track metric after execution\n yield* Metric.snapshot;\n return result;\n });\n\n/**\n * Test fixture for observability testing\n */\nexport interface ObservabilityTestFixture {\n readonly storageObservability: Layer.Layer<StorageObservability>;\n readonly uploadObservability: Layer.Layer<UploadObservability>;\n readonly flowObservability: Layer.Layer<FlowObservability>;\n}\n\n/**\n * Create a complete test fixture with all observability layers\n */\nexport const createTestFixture = (\n storageType = \"test-storage\",\n): ObservabilityTestFixture => ({\n storageObservability: makeTestStorageObservability(storageType),\n uploadObservability: makeTestUploadObservability(),\n flowObservability: makeTestFlowObservability(),\n});\n\n/**\n * Run an effect with test observability layers\n */\nexport const runWithTestObservability = <A, E>(\n effect: Effect.Effect<\n A,\n E,\n StorageObservability | UploadObservability | FlowObservability\n >,\n storageType = \"test-storage\",\n): Effect.Effect<A, E> => {\n const fixture = createTestFixture(storageType);\n return effect.pipe(\n Effect.provide(fixture.storageObservability),\n Effect.provide(fixture.uploadObservability),\n Effect.provide(fixture.flowObservability),\n );\n};\n","import { Effect, Metric } from \"effect\";\nimport type { StorageMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Storage Observability Utility Functions\n// ============================================================================\n\n// Generic upload metrics wrapper\nexport const withUploadMetrics = <A, E, R>(\n metrics: StorageMetrics,\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.tap(() =>\n metrics.uploadRequestsTotal.pipe(Metric.tagged(\"upload_id\", uploadId))(\n Effect.succeed(1),\n ),\n ),\n Effect.tapError(() =>\n metrics.uploadErrorsTotal.pipe(Metric.tagged(\"upload_id\", uploadId))(\n Effect.succeed(1),\n ),\n ),\n Effect.tap(() =>\n metrics.uploadSuccessTotal.pipe(Metric.tagged(\"upload_id\", uploadId))(\n Effect.succeed(1),\n ),\n ),\n );\n\n// Generic API call metrics wrapper\nexport const withApiMetrics = <A, E, R>(\n metrics: StorageMetrics,\n operation: string,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.tap(() =>\n metrics.apiCallsTotal.pipe(Metric.tagged(\"operation\", operation))(\n Effect.succeed(1),\n ),\n ),\n );\n\n// Generic timing metrics wrapper\nexport const withTimingMetrics = <A, E, R>(\n metric: Metric.Metric.Histogram<number>,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n Effect.gen(function* () {\n const startTime = yield* Effect.sync(() => Date.now());\n const result = yield* effect;\n const endTime = yield* Effect.sync(() => Date.now());\n const duration = (endTime - startTime) / 1000; // Convert to seconds\n\n yield* metric(Effect.succeed(duration));\n\n return result;\n });\n\n// File size tracking\nexport const trackFileSize = <A, E, R>(\n metrics: StorageMetrics,\n fileSize: number,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.tap(() => metrics.fileSizeHistogram(Effect.succeed(fileSize))),\n );\n\n// Part size tracking\nexport const trackPartSize = <A, E, R>(\n metrics: StorageMetrics,\n partSize: number,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.tap(() => metrics.partSizeHistogram(Effect.succeed(partSize))),\n );\n\n// Active uploads tracking\nexport const withActiveUploadTracking = <A, E, R>(\n metrics: StorageMetrics,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.tap(() => metrics.activeUploadsGauge(Effect.succeed(1))),\n Effect.ensuring(metrics.activeUploadsGauge(Effect.succeed(-1))),\n );\n\n// Throughput calculation and tracking\nexport const withThroughputTracking = <A, E, R>(\n metrics: StorageMetrics,\n bytes: number,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n Effect.gen(function* () {\n const startTime = yield* Effect.sync(() => Date.now());\n const result = yield* effect;\n const endTime = yield* Effect.sync(() => Date.now());\n const durationSeconds = (endTime - startTime) / 1000;\n const throughputBps = durationSeconds > 0 ? bytes / durationSeconds : 0;\n\n yield* metrics.uploadThroughputGauge(Effect.succeed(throughputBps));\n\n return result;\n });\n\n// Combined metrics wrapper for common upload operations\nexport const withStorageOperationMetrics = <A, E, R>(\n metrics: StorageMetrics,\n operation: string,\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n fileSize?: number,\n): Effect.Effect<A, E, R> => {\n let wrappedEffect = effect.pipe(\n (eff) => withApiMetrics(metrics, operation, eff),\n (eff) => withUploadMetrics(metrics, uploadId, eff),\n (eff) => withTimingMetrics(metrics.uploadDurationHistogram, eff),\n (eff) => withActiveUploadTracking(metrics, eff),\n );\n\n if (fileSize !== undefined) {\n wrappedEffect = wrappedEffect.pipe(\n (eff) => trackFileSize(metrics, fileSize, eff),\n (eff) => withThroughputTracking(metrics, fileSize, eff),\n );\n }\n\n return wrappedEffect;\n};\n","import { Metric, MetricBoundaries } from \"effect\";\n\n// ============================================================================\n// Flow Engine Metrics\n// ============================================================================\n\n/**\n * Flow engine metrics for tracking flow execution operations\n */\nexport const createFlowMetrics = () => ({\n // Counter metrics\n flowStartedTotal: Metric.counter(\"flow_started_total\", {\n description: \"Total number of flows started\",\n }),\n\n flowCompletedTotal: Metric.counter(\"flow_completed_total\", {\n description: \"Total number of flows completed successfully\",\n }),\n\n flowFailedTotal: Metric.counter(\"flow_failed_total\", {\n description: \"Total number of flows that failed\",\n }),\n\n flowPausedTotal: Metric.counter(\"flow_paused_total\", {\n description: \"Total number of flows that were paused\",\n }),\n\n flowResumedTotal: Metric.counter(\"flow_resumed_total\", {\n description: \"Total number of flows that were resumed\",\n }),\n\n nodeExecutedTotal: Metric.counter(\"node_executed_total\", {\n description: \"Total number of nodes executed\",\n }),\n\n nodeSuccessTotal: Metric.counter(\"node_success_total\", {\n description: \"Total number of nodes executed successfully\",\n }),\n\n nodeFailedTotal: Metric.counter(\"node_failed_total\", {\n description: \"Total number of nodes that failed\",\n }),\n\n nodeSkippedTotal: Metric.counter(\"node_skipped_total\", {\n description: \"Total number of nodes skipped (conditional)\",\n }),\n\n // Histogram metrics\n flowDurationHistogram: Metric.histogram(\n \"flow_duration_seconds\",\n MetricBoundaries.exponential({\n start: 0.1, // 100ms\n factor: 2,\n count: 20, // Up to ~100 seconds\n }),\n \"Duration of complete flow execution in seconds\",\n ),\n\n nodeDurationHistogram: Metric.histogram(\n \"node_duration_seconds\",\n MetricBoundaries.exponential({\n start: 0.01, // 10ms\n factor: 2,\n count: 18, // Up to ~26 seconds\n }),\n \"Duration of individual node execution in seconds\",\n ),\n\n flowNodeCountHistogram: Metric.histogram(\n \"flow_node_count\",\n MetricBoundaries.linear({\n start: 1,\n width: 5,\n count: 20, // Up to 100 nodes\n }),\n \"Number of nodes in a flow\",\n ),\n\n parallelNodesHistogram: Metric.histogram(\n \"parallel_nodes_count\",\n MetricBoundaries.linear({\n start: 1,\n width: 2,\n count: 15, // Up to 30 parallel nodes\n }),\n \"Number of nodes executed in parallel\",\n ),\n\n // Gauge metrics\n activeFlowsGauge: Metric.gauge(\"active_flows\", {\n description: \"Number of currently active flows\",\n }),\n\n activeNodesGauge: Metric.gauge(\"active_nodes\", {\n description: \"Number of currently executing nodes\",\n }),\n\n pausedFlowsGauge: Metric.gauge(\"paused_flows\", {\n description: \"Number of currently paused flows\",\n }),\n\n // Summary metrics for latency percentiles\n flowLatencySummary: Metric.summary({\n name: \"flow_latency_seconds\",\n maxAge: \"10 minutes\",\n maxSize: 1000,\n error: 0.01,\n quantiles: [0.5, 0.9, 0.95, 0.99],\n description: \"Flow execution latency percentiles\",\n }),\n\n nodeLatencySummary: Metric.summary({\n name: \"node_latency_seconds\",\n maxAge: \"10 minutes\",\n maxSize: 1000,\n error: 0.01,\n quantiles: [0.5, 0.9, 0.95, 0.99],\n description: \"Node execution latency percentiles\",\n }),\n\n // ============================================================================\n // Circuit Breaker Metrics\n // ============================================================================\n\n /** Total number of times circuit breakers opened */\n circuitBreakerOpenTotal: Metric.counter(\"circuit_breaker_open_total\", {\n description:\n \"Total number of times circuit breakers transitioned to open state\",\n }),\n\n /** Total number of times circuit breakers closed */\n circuitBreakerCloseTotal: Metric.counter(\"circuit_breaker_close_total\", {\n description:\n \"Total number of times circuit breakers transitioned to closed state\",\n }),\n\n /** Total number of requests rejected by open circuit breakers */\n circuitBreakerRejectedTotal: Metric.counter(\n \"circuit_breaker_rejected_total\",\n {\n description:\n \"Total number of requests rejected because circuit breaker is open\",\n },\n ),\n\n /** Total number of times circuit breakers transitioned to half-open */\n circuitBreakerHalfOpenTotal: Metric.counter(\n \"circuit_breaker_half_open_total\",\n {\n description:\n \"Total number of times circuit breakers transitioned to half-open state\",\n },\n ),\n\n /** Current state of circuit breakers (0=closed, 1=open, 2=half-open) */\n circuitBreakerStateGauge: Metric.gauge(\"circuit_breaker_state\", {\n description:\n \"Current circuit breaker state (0=closed, 1=open, 2=half-open)\",\n }),\n\n /** Number of failures in circuit breaker sliding window */\n circuitBreakerFailuresGauge: Metric.gauge(\"circuit_breaker_failures\", {\n description:\n \"Number of failures currently in the circuit breaker sliding window\",\n }),\n});\n\n/**\n * Type for flow metrics\n */\nexport type FlowMetrics = ReturnType<typeof createFlowMetrics>;\n\n/**\n * Default flow metrics instance\n */\nexport const flowMetrics = createFlowMetrics();\n","import { Effect, Metric } from \"effect\";\nimport { createFlowMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Flow Error Classification\n// ============================================================================\n\nexport type FlowErrorCategory =\n | \"flow_validation_error\"\n | \"node_execution_error\"\n | \"node_not_found_error\"\n | \"flow_timeout_error\"\n | \"flow_cancelled_error\"\n | \"unknown_flow_error\";\n\n/**\n * Classify flow execution errors\n */\nexport const classifyFlowError = (error: unknown): FlowErrorCategory => {\n if (!error || typeof error !== \"object\") return \"unknown_flow_error\";\n\n const errorCode = \"code\" in error ? error.code : undefined;\n if (!errorCode) return \"unknown_flow_error\";\n\n // Flow-specific error codes\n switch (errorCode) {\n case \"FLOW_VALIDATION_ERROR\":\n case \"FLOW_INVALID_INPUT\":\n case \"FLOW_INVALID_OUTPUT\":\n return \"flow_validation_error\";\n case \"FLOW_NODE_NOT_FOUND\":\n case \"FLOW_EDGE_INVALID\":\n return \"node_not_found_error\";\n case \"FLOW_NODE_EXECUTION_FAILED\":\n case \"FLOW_NODE_ERROR\":\n return \"node_execution_error\";\n case \"FLOW_TIMEOUT\":\n return \"flow_timeout_error\";\n case \"FLOW_CANCELLED\":\n case \"ABORTED\":\n return \"flow_cancelled_error\";\n default:\n return \"unknown_flow_error\";\n }\n};\n\n/**\n * Track flow errors with classification\n */\nexport const trackFlowError = <E>(\n error: E,\n): Effect.Effect<void, never, never> => {\n const metrics = createFlowMetrics();\n const category = classifyFlowError(error);\n\n return Effect.gen(function* () {\n // Increment total failed flows\n yield* Metric.increment(metrics.flowFailedTotal);\n\n // Log error with classification\n yield* Effect.logError(\"Flow execution failed\").pipe(\n Effect.annotateLogs({\n \"error.category\": category,\n \"error.message\": String(error),\n }),\n );\n });\n};\n\n/**\n * Track node errors with classification\n */\nexport const trackNodeError = <E>(\n nodeId: string,\n nodeType: string,\n error: E,\n): Effect.Effect<void, never, never> => {\n const metrics = createFlowMetrics();\n const category = classifyFlowError(error);\n\n return Effect.gen(function* () {\n // Increment node failed counter\n yield* Metric.increment(metrics.nodeFailedTotal);\n\n // Log error with node context\n yield* Effect.logError(\"Node execution failed\").pipe(\n Effect.annotateLogs({\n \"node.id\": nodeId,\n \"node.type\": nodeType,\n \"error.category\": category,\n \"error.message\": String(error),\n }),\n );\n });\n};\n","import { Effect, Layer, Metric } from \"effect\";\nimport {\n FlowObservability,\n makeFlowObservabilityLayer,\n} from \"../core/layers.js\";\nimport { createFlowMetrics, type FlowMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Flow Observability Layer Implementation\n// ============================================================================\n\n/**\n * Create a live flow observability layer with full metrics\n */\nexport const makeFlowObservabilityLive = (\n serviceName = \"uploadista-flow-engine\",\n): Layer.Layer<FlowObservability> => {\n const metrics = createFlowMetrics();\n\n return Layer.succeed(FlowObservability, {\n serviceName,\n enabled: true,\n metrics: {\n flowStarted: Metric.increment(metrics.flowStartedTotal),\n flowCompleted: Metric.increment(metrics.flowCompletedTotal),\n flowFailed: Metric.increment(metrics.flowFailedTotal),\n nodeExecuted: Metric.increment(metrics.nodeExecutedTotal),\n },\n });\n};\n\n/**\n * Default live flow observability layer\n */\nexport const FlowObservabilityLive = makeFlowObservabilityLive();\n\n/**\n * No-op flow observability layer (for testing or disabled observability)\n */\nexport const FlowObservabilityDisabled = makeFlowObservabilityLayer(false);\n\n/**\n * Helper to get flow metrics from context\n */\nexport const getFlowMetrics = Effect.gen(function* () {\n const obs = yield* FlowObservability;\n return obs.metrics;\n});\n\n/**\n * Helper to track flow duration\n */\nexport const withFlowDuration = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> => {\n const metrics = createFlowMetrics();\n return Effect.gen(function* () {\n const startTime = Date.now();\n const result = yield* effect;\n const duration = (Date.now() - startTime) / 1000; // Convert to seconds\n yield* Metric.update(metrics.flowDurationHistogram, duration);\n yield* Metric.update(metrics.flowLatencySummary, duration);\n return result;\n }).pipe(Effect.withSpan(\"flow-execution\"));\n};\n\n/**\n * Helper to track node duration\n * @param nodeId - Unique node identifier\n * @param nodeType - Generic node type (e.g., \"optimize\", \"resize\")\n * @param effect - The effect to track\n * @param nodeTypeId - Optional specific node type ID (e.g., \"optimize-image\", \"resize-video\")\n */\nexport const withNodeDuration = <A, E, R>(\n nodeId: string,\n nodeType: string,\n effect: Effect.Effect<A, E, R>,\n nodeTypeId?: string,\n): Effect.Effect<A, E, R> => {\n const metrics = createFlowMetrics();\n // Use nodeTypeId for span name if available, fallback to nodeType\n const spanName = nodeTypeId ?? nodeType;\n return Effect.gen(function* () {\n const startTime = Date.now();\n const result = yield* effect;\n const duration = (Date.now() - startTime) / 1000; // Convert to seconds\n yield* Metric.update(metrics.nodeDurationHistogram, duration);\n yield* Metric.update(metrics.nodeLatencySummary, duration);\n return result;\n }).pipe(\n Effect.withSpan(`node-${spanName}`, {\n attributes: {\n \"node.id\": nodeId,\n \"node.type\": nodeType,\n \"node.type_id\": nodeTypeId ?? nodeType,\n },\n }),\n );\n};\n\n/**\n * Helper to track active flows\n */\nexport const trackActiveFlow = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> => {\n const metrics = createFlowMetrics();\n return Effect.gen(function* () {\n // Increment active flows\n yield* Metric.increment(metrics.activeFlowsGauge);\n\n // Use acquireUseRelease for proper cleanup\n return yield* Effect.acquireUseRelease(\n Effect.void,\n () => effect,\n () => Metric.set(metrics.activeFlowsGauge, -1),\n );\n });\n};\n\n/**\n * Helper to track active nodes\n */\nexport const trackActiveNode = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> => {\n const metrics = createFlowMetrics();\n return Effect.gen(function* () {\n // Increment active nodes\n yield* Metric.increment(metrics.activeNodesGauge);\n\n // Use acquireUseRelease for proper cleanup\n return yield* Effect.acquireUseRelease(\n Effect.void,\n () => effect,\n () => Metric.set(metrics.activeNodesGauge, -1),\n );\n });\n};\n","import { Effect, Layer } from \"effect\";\nimport type { FlowObservabilityService } from \"../core/layers.js\";\nimport { FlowObservability } from \"../core/layers.js\";\n\n// ============================================================================\n// Test Flow Observability Layers\n// ============================================================================\n\n/**\n * Mock flow observability for testing\n */\nexport const makeTestFlowObservability = (): Layer.Layer<FlowObservability> => {\n const service: FlowObservabilityService = {\n serviceName: \"test-flow-engine\",\n enabled: true,\n metrics: {\n flowStarted: Effect.void,\n flowCompleted: Effect.void,\n flowFailed: Effect.void,\n nodeExecuted: Effect.void,\n },\n };\n return Layer.succeed(FlowObservability, service);\n};\n\n/**\n * Run an effect with test flow observability\n */\nexport const runWithTestFlowObservability = <A, E>(\n effect: Effect.Effect<A, E, FlowObservability>,\n): Effect.Effect<A, E> => {\n return effect.pipe(Effect.provide(makeTestFlowObservability()));\n};\n","import { Effect } from \"effect\";\n\n// ============================================================================\n// Flow Tracing Utilities\n// ============================================================================\n\n/**\n * Wrap an Effect with a flow operation span\n */\nexport const withFlowSpan =\n <A, E, R>(operation: string, attributes?: Record<string, unknown>) =>\n (effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.withSpan(`flow-${operation}`, {\n attributes: {\n \"flow.operation\": operation,\n ...attributes,\n },\n }),\n );\n\n/**\n * Add flow context to the current span\n */\nexport const withFlowContext = (context: {\n flowId?: string;\n flowName?: string;\n jobId?: string;\n nodeCount?: number;\n storageId?: string;\n}) =>\n Effect.annotateCurrentSpan({\n \"flow.id\": context.flowId ?? \"unknown\",\n \"flow.name\": context.flowName ?? \"unknown\",\n \"flow.job_id\": context.jobId ?? \"unknown\",\n \"flow.node_count\": context.nodeCount?.toString() ?? \"0\",\n \"flow.storage_id\": context.storageId ?? \"unknown\",\n });\n\n/**\n * Add node context to the current span\n */\nexport const withNodeContext = (context: {\n nodeId: string;\n nodeType: string;\n nodeName?: string;\n flowId?: string;\n jobId?: string;\n}) =>\n Effect.annotateCurrentSpan({\n \"node.id\": context.nodeId,\n \"node.type\": context.nodeType,\n \"node.name\": context.nodeName ?? \"unknown\",\n \"node.flow_id\": context.flowId ?? \"unknown\",\n \"node.job_id\": context.jobId ?? \"unknown\",\n });\n\n/**\n * Add execution state context to the current span\n */\nexport const withExecutionContext = (context: {\n executionOrder?: string[];\n currentIndex?: number;\n totalNodes?: number;\n parallelCount?: number;\n}) =>\n Effect.annotateCurrentSpan({\n \"execution.order\": context.executionOrder?.join(\",\") ?? \"\",\n \"execution.current_index\": context.currentIndex?.toString() ?? \"0\",\n \"execution.total_nodes\": context.totalNodes?.toString() ?? \"0\",\n \"execution.parallel_count\": context.parallelCount?.toString() ?? \"0\",\n });\n\n// ============================================================================\n// Plugin Operation Tracing\n// ============================================================================\n\n/**\n * Operation domains for plugin-level tracing\n */\nexport type OperationDomain =\n | \"image\"\n | \"video\"\n | \"document\"\n | \"ai\"\n | \"virus-scan\"\n | \"zip\";\n\n/**\n * Wrap an Effect with a plugin operation span\n *\n * @param domain - The operation domain (e.g., \"image\", \"video\", \"document\")\n * @param operation - The specific operation (e.g., \"optimize\", \"transcode\", \"extract-text\")\n * @param attributes - Optional span attributes with operation-specific details\n *\n * @example\n * ```typescript\n * // Image optimization span\n * withOperationSpan(\"image\", \"optimize\", {\n * \"image.format\": \"webp\",\n * \"image.quality\": 80,\n * })(imageService.optimize(inputBytes, params))\n *\n * // Video transcoding span\n * withOperationSpan(\"video\", \"transcode\", {\n * \"video.format\": \"mp4\",\n * \"video.codec\": \"h264\",\n * })(videoService.transcode(inputBytes, params))\n * ```\n */\nexport const withOperationSpan =\n <A, E, R>(\n domain: OperationDomain,\n operation: string,\n attributes?: Record<string, unknown>,\n ) =>\n (effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.withSpan(`${domain}-${operation}`, {\n attributes: {\n \"operation.domain\": domain,\n \"operation.name\": operation,\n ...attributes,\n },\n }),\n );\n\n/**\n * Add operation context to the current span\n */\nexport const withOperationContext = (context: {\n domain: OperationDomain;\n operation: string;\n inputSize?: number;\n outputSize?: number;\n}) =>\n Effect.annotateCurrentSpan({\n \"operation.domain\": context.domain,\n \"operation.name\": context.operation,\n \"operation.input_size\": context.inputSize?.toString() ?? \"unknown\",\n \"operation.output_size\": context.outputSize?.toString() ?? \"unknown\",\n });\n\n// ============================================================================\n// Circuit Breaker Tracing\n// ============================================================================\n\n/**\n * Circuit breaker state for tracing\n */\nexport type CircuitBreakerTracingState = \"closed\" | \"open\" | \"half-open\";\n\n/**\n * Wrap an Effect with a circuit breaker evaluation span\n */\nexport const withCircuitBreakerSpan =\n <A, E, R>(\n nodeType: string,\n state: CircuitBreakerTracingState,\n attributes?: Record<string, unknown>,\n ) =>\n (effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.withSpan(`circuit-breaker-${nodeType}`, {\n attributes: {\n \"circuit_breaker.node_type\": nodeType,\n \"circuit_breaker.state\": state,\n ...attributes,\n },\n }),\n );\n\n/**\n * Add circuit breaker context to the current span\n */\nexport const withCircuitBreakerContext = (context: {\n nodeType: string;\n state: CircuitBreakerTracingState;\n failureCount?: number;\n failureThreshold?: number;\n resetTimeout?: number;\n decision?: \"allowed\" | \"rejected\" | \"fallback\";\n}) =>\n Effect.annotateCurrentSpan({\n \"circuit_breaker.node_type\": context.nodeType,\n \"circuit_breaker.state\": context.state,\n \"circuit_breaker.failure_count\": context.failureCount?.toString() ?? \"0\",\n \"circuit_breaker.failure_threshold\":\n context.failureThreshold?.toString() ?? \"5\",\n \"circuit_breaker.reset_timeout\":\n context.resetTimeout?.toString() ?? \"30000\",\n \"circuit_breaker.decision\": context.decision ?? \"unknown\",\n });\n\n/**\n * Add a circuit breaker state change event to the current span\n */\nexport const annotateCircuitBreakerStateChange = (event: {\n nodeType: string;\n previousState: CircuitBreakerTracingState;\n newState: CircuitBreakerTracingState;\n failureCount?: number;\n timestamp?: number;\n}) =>\n Effect.annotateCurrentSpan({\n \"circuit_breaker.event\": \"state_change\",\n \"circuit_breaker.node_type\": event.nodeType,\n \"circuit_breaker.previous_state\": event.previousState,\n \"circuit_breaker.new_state\": event.newState,\n \"circuit_breaker.failure_count\": event.failureCount?.toString() ?? \"0\",\n \"circuit_breaker.timestamp\":\n event.timestamp?.toString() ?? Date.now().toString(),\n });\n","import { Context, Effect, Layer } from \"effect\";\n\n/**\n * Metrics Recording Service\n *\n * Provides access to metrics recording functionality throughout\n * the upload and flow processing pipeline. The service is provided\n * via Effect Layer and can be accessed using Effect.service().\n */\nexport class MetricsService extends Context.Tag(\"MetricsService\")<\n MetricsService,\n {\n /**\n * Record upload metrics for an organization\n */\n readonly recordUpload: (\n clientId: string,\n bytes: number,\n metadata?: Record<string, unknown>,\n ) => Effect.Effect<void, never>;\n }\n>() {}\n\n/**\n * No-op implementation of MetricsService that does nothing.\n * Used when metrics are disabled or database is not available.\n */\nexport const NoOpMetricsServiceLive: Layer.Layer<MetricsService> =\n Layer.succeed(MetricsService, {\n recordUpload: (_organizationId: string, _bytes: number) => Effect.void,\n });\n","import { type Effect, Layer } from \"effect\";\nimport {\n createStorageErrorTracker,\n type StorageErrorCategory,\n} from \"../core/errors.js\";\nimport {\n logStorageOperation,\n logUploadCompletion,\n logUploadProgress,\n logWithContext,\n} from \"../core/logging.js\";\nimport { createStorageMetrics, type StorageMetrics } from \"../core/metrics.js\";\nimport { createStorageTracingLayer, withStorageSpan } from \"../core/tracing.js\";\nimport {\n withApiMetrics,\n withStorageOperationMetrics,\n withTimingMetrics,\n withUploadMetrics,\n} from \"../core/utilities.js\";\n\n// ============================================================================\n// Azure Blob Storage-Specific Observability\n// ============================================================================\n\nconst STORAGE_TYPE = \"azure\";\n\n// Azure-specific metrics\nexport const azureMetrics = createStorageMetrics(STORAGE_TYPE);\n\n// Azure-specific tracing layer\nexport const AzureTracingLayer = createStorageTracingLayer(STORAGE_TYPE);\n\n// Azure-specific error classification\nconst classifyAzureError = (error: unknown): StorageErrorCategory | null => {\n if (!error || typeof error !== \"object\") return null;\n\n const errorCode =\n \"code\" in error\n ? error.code\n : \"statusCode\" in error\n ? error.statusCode\n : undefined;\n if (!errorCode) return null;\n\n // Azure-specific error codes\n switch (errorCode) {\n case \"BlobNotFound\":\n case \"ContainerNotFound\":\n case \"InvalidBlobOrBlock\":\n return \"client_error\";\n case \"ContainerAlreadyExists\":\n case \"BlobAlreadyExists\":\n return \"client_error\";\n case \"InvalidBlockId\":\n case \"InvalidBlockList\":\n case \"InvalidBlobType\":\n return \"client_error\";\n case \"RequestBodyTooLarge\":\n case \"InvalidHeaderValue\":\n return \"client_error\";\n case \"AuthenticationFailed\":\n case \"InvalidAuthenticationInfo\":\n return \"authentication_error\";\n case \"AccountIsDisabled\":\n return \"authorization_error\";\n case \"InsufficientAccountPermissions\":\n return \"authorization_error\";\n case \"OperationTimedOut\":\n case \"ServerBusy\":\n case \"InternalError\":\n return \"server_error\";\n default:\n // Check for HTTP status codes\n if (typeof errorCode === \"number\") {\n if (errorCode >= 500) return \"server_error\";\n if (errorCode === 429) return \"throttling_error\";\n if (errorCode === 403) return \"authorization_error\";\n if (errorCode === 401) return \"authentication_error\";\n if (errorCode >= 400) return \"client_error\";\n }\n return null; // Fall back to generic classification\n }\n};\n\n// Azure-specific error tracker\nexport const trackAzureError = createStorageErrorTracker(\n STORAGE_TYPE,\n azureMetrics,\n classifyAzureError,\n);\n\n// Azure-specific observability layer\nexport const AzureObservabilityLayer = Layer.mergeAll(\n AzureTracingLayer,\n // Metrics are automatically available through Effect\n);\n\n// ============================================================================\n// Azure Utility Functions\n// ============================================================================\n\nexport const withAzureUploadMetrics = <A, E, R>(\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n) => withUploadMetrics(azureMetrics, uploadId, effect);\n\nexport const withAzureApiMetrics = <A, E, R>(\n operation: string,\n effect: Effect.Effect<A, E, R>,\n) => withApiMetrics(azureMetrics, operation, effect);\n\nexport const withAzureTimingMetrics = withTimingMetrics;\n\nexport const withAzureOperationMetrics = <A, E, R>(\n operation: string,\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n fileSize?: number,\n) =>\n withStorageOperationMetrics(\n azureMetrics,\n operation,\n uploadId,\n effect,\n fileSize,\n );\n\n// Azure-specific span wrapper\nexport const withAzureSpan =\n <A, E, R>(operation: string, attributes?: Record<string, unknown>) =>\n (effect: Effect.Effect<A, E, R>) =>\n withStorageSpan(operation, STORAGE_TYPE, attributes)(effect);\n\n// Azure-specific logging functions\nexport const logAzureOperation = logStorageOperation.bind(null, STORAGE_TYPE);\nexport const logAzureUploadProgress = logUploadProgress.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logAzureUploadCompletion = logUploadCompletion.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logAzureContext = logWithContext;\n\n// Export metrics for external access\nexport const {\n uploadRequestsTotal: azureUploadRequestsTotal,\n uploadPartsTotal: azureUploadPartsTotal,\n uploadSuccessTotal: azureUploadSuccessTotal,\n uploadErrorsTotal: azureUploadErrorsTotal,\n apiCallsTotal: azureApiCallsTotal,\n uploadDurationHistogram: azureUploadDurationHistogram,\n partUploadDurationHistogram: azurePartUploadDurationHistogram,\n fileSizeHistogram: azureFileSizeHistogram,\n partSizeHistogram: azurePartSizeHistogram,\n activeUploadsGauge: azureActiveUploadsGauge,\n uploadThroughputGauge: azureUploadThroughputGauge,\n uploadLatencySummary: azureUploadLatencySummary,\n} = azureMetrics;\n\n// Type exports\nexport type AzureMetrics = StorageMetrics;\n","import { type Effect, Layer } from \"effect\";\nimport {\n createStorageErrorTracker,\n type StorageErrorCategory,\n} from \"../core/errors.js\";\nimport {\n logStorageOperation,\n logUploadCompletion,\n logUploadProgress,\n logWithContext,\n} from \"../core/logging.js\";\nimport { createStorageMetrics, type StorageMetrics } from \"../core/metrics.js\";\nimport { createStorageTracingLayer, withStorageSpan } from \"../core/tracing.js\";\nimport {\n withApiMetrics,\n withStorageOperationMetrics,\n withTimingMetrics,\n withUploadMetrics,\n} from \"../core/utilities.js\";\n\n// ============================================================================\n// Filesystem Storage-Specific Observability\n// ============================================================================\n\nconst STORAGE_TYPE = \"filesystem\";\n\n// Filesystem-specific metrics\nexport const filesystemMetrics = createStorageMetrics(STORAGE_TYPE);\n\n// Filesystem-specific tracing layer\nexport const FilesystemTracingLayer = createStorageTracingLayer(STORAGE_TYPE);\n\n// Filesystem-specific error classification\nconst classifyFilesystemError = (\n error: unknown,\n): StorageErrorCategory | null => {\n if (!error || typeof error !== \"object\") return null;\n\n const errorCode = \"code\" in error ? error.code : undefined;\n if (!errorCode) return null;\n\n // Node.js filesystem error codes\n switch (errorCode) {\n case \"ENOENT\": // File/directory not found\n case \"ENOTDIR\": // Not a directory\n return \"client_error\";\n case \"EEXIST\": // File/directory already exists\n return \"client_error\";\n case \"EISDIR\": // Is a directory\n return \"client_error\";\n case \"EINVAL\": // Invalid argument\n case \"ENAMETOOLONG\": // Filename too long\n return \"client_error\";\n case \"EACCES\": // Permission denied\n case \"EPERM\": // Operation not permitted\n return \"authorization_error\";\n case \"ENOSPC\": // No space left on device\n case \"EDQUOT\": // Disk quota exceeded\n return \"server_error\";\n case \"EIO\": // I/O error\n case \"EROFS\": // Read-only filesystem\n case \"EMFILE\": // Too many open files\n case \"ENFILE\": // File table overflow\n return \"server_error\";\n case \"EBUSY\": // Device or resource busy\n return \"throttling_error\";\n default:\n return null; // Fall back to generic classification\n }\n};\n\n// Filesystem-specific error tracker\nexport const trackFilesystemError = createStorageErrorTracker(\n STORAGE_TYPE,\n filesystemMetrics,\n classifyFilesystemError,\n);\n\n// Filesystem-specific observability layer\nexport const FilesystemObservabilityLayer = Layer.mergeAll(\n FilesystemTracingLayer,\n // Metrics are automatically available through Effect\n);\n\n// ============================================================================\n// Filesystem Utility Functions\n// ============================================================================\n\nexport const withFilesystemUploadMetrics = <A, E, R>(\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n) => withUploadMetrics(filesystemMetrics, uploadId, effect);\n\nexport const withFilesystemApiMetrics = <A, E, R>(\n operation: string,\n effect: Effect.Effect<A, E, R>,\n) => withApiMetrics(filesystemMetrics, operation, effect);\n\nexport const withFilesystemTimingMetrics = withTimingMetrics;\n\nexport const withFilesystemOperationMetrics = <A, E, R>(\n operation: string,\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n fileSize?: number,\n) =>\n withStorageOperationMetrics(\n filesystemMetrics,\n operation,\n uploadId,\n effect,\n fileSize,\n );\n\n// Filesystem-specific span wrapper\nexport const withFilesystemSpan =\n <A, E, R>(operation: string, attributes?: Record<string, unknown>) =>\n (effect: Effect.Effect<A, E, R>) =>\n withStorageSpan(operation, STORAGE_TYPE, attributes)(effect);\n\n// Filesystem-specific logging functions\nexport const logFilesystemOperation = logStorageOperation.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logFilesystemUploadProgress = logUploadProgress.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logFilesystemUploadCompletion = logUploadCompletion.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logFilesystemContext = logWithContext;\n\n// Export metrics for external access\nexport const {\n uploadRequestsTotal: filesystemUploadRequestsTotal,\n uploadPartsTotal: filesystemUploadPartsTotal,\n uploadSuccessTotal: filesystemUploadSuccessTotal,\n uploadErrorsTotal: filesystemUploadErrorsTotal,\n apiCallsTotal: filesystemApiCallsTotal,\n uploadDurationHistogram: filesystemUploadDurationHistogram,\n partUploadDurationHistogram: filesystemPartUploadDurationHistogram,\n fileSizeHistogram: filesystemFileSizeHistogram,\n partSizeHistogram: filesystemPartSizeHistogram,\n activeUploadsGauge: filesystemActiveUploadsGauge,\n uploadThroughputGauge: filesystemUploadThroughputGauge,\n uploadLatencySummary: filesystemUploadLatencySummary,\n} = filesystemMetrics;\n\n// Type exports\nexport type FilesystemMetrics = StorageMetrics;\n","import { type Effect, Layer } from \"effect\";\nimport {\n createStorageErrorTracker,\n type StorageErrorCategory,\n} from \"../core/errors.js\";\nimport {\n logStorageOperation,\n logUploadCompletion,\n logUploadProgress,\n logWithContext,\n} from \"../core/logging.js\";\nimport { createStorageMetrics, type StorageMetrics } from \"../core/metrics.js\";\nimport { createStorageTracingLayer, withStorageSpan } from \"../core/tracing.js\";\nimport {\n withApiMetrics,\n withStorageOperationMetrics,\n withTimingMetrics,\n withUploadMetrics,\n} from \"../core/utilities.js\";\n\n// ============================================================================\n// Google Cloud Storage-Specific Observability\n// ============================================================================\n\nconst STORAGE_TYPE = \"gcs\";\n\n// GCS-specific metrics\nexport const gcsMetrics = createStorageMetrics(STORAGE_TYPE);\n\n// GCS-specific tracing layer\nexport const GCSTracingLayer = createStorageTracingLayer(STORAGE_TYPE);\n\n// GCS-specific error classification\nconst classifyGCSError = (error: unknown): StorageErrorCategory | null => {\n if (!error || typeof error !== \"object\") return null;\n\n const errorCode =\n \"code\" in error ? error.code : \"status\" in error ? error.status : undefined;\n if (!errorCode) return null;\n\n // GCS-specific error codes\n switch (errorCode) {\n case \"NoSuchBucket\":\n case \"NoSuchKey\":\n case \"NoSuchUpload\":\n return \"client_error\";\n case \"BucketAlreadyOwnedByYou\":\n case \"BucketNotEmpty\":\n return \"client_error\";\n case \"InvalidBucketName\":\n case \"InvalidArgument\":\n case \"InvalidPart\":\n case \"InvalidPartOrder\":\n return \"client_error\";\n case \"EntityTooSmall\":\n case \"EntityTooLarge\":\n return \"client_error\";\n case \"MalformedPolicy\":\n return \"client_error\";\n case \"Unauthorized\":\n case \"AuthenticationRequired\":\n return \"authentication_error\";\n case \"Forbidden\":\n case \"AccessDenied\":\n return \"authorization_error\";\n case \"TooManyRequests\":\n case \"RateLimitExceeded\":\n return \"throttling_error\";\n case \"InternalError\":\n case \"ServiceUnavailable\":\n case \"BackendError\":\n return \"server_error\";\n default:\n // Check for HTTP status codes\n if (typeof errorCode === \"number\") {\n if (errorCode >= 500) return \"server_error\";\n if (errorCode === 429) return \"throttling_error\";\n if (errorCode === 403) return \"authorization_error\";\n if (errorCode === 401) return \"authentication_error\";\n if (errorCode >= 400) return \"client_error\";\n }\n return null; // Fall back to generic classification\n }\n};\n\n// GCS-specific error tracker\nexport const trackGCSError = createStorageErrorTracker(\n STORAGE_TYPE,\n gcsMetrics,\n classifyGCSError,\n);\n\n// GCS-specific observability layer\nexport const GCSObservabilityLayer = Layer.mergeAll(\n GCSTracingLayer,\n // Metrics are automatically available through Effect\n);\n\n// ============================================================================\n// GCS Utility Functions\n// ============================================================================\n\nexport const withGCSUploadMetrics = <A, E, R>(\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n) => withUploadMetrics(gcsMetrics, uploadId, effect);\n\nexport const withGCSApiMetrics = <A, E, R>(\n operation: string,\n effect: Effect.Effect<A, E, R>,\n) => withApiMetrics(gcsMetrics, operation, effect);\n\nexport const withGCSTimingMetrics = withTimingMetrics;\n\nexport const withGCSOperationMetrics = <A, E, R>(\n operation: string,\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n fileSize?: number,\n) =>\n withStorageOperationMetrics(\n gcsMetrics,\n operation,\n uploadId,\n effect,\n fileSize,\n );\n\n// GCS-specific span wrapper\nexport const withGCSSpan =\n <A, E, R>(operation: string, attributes?: Record<string, unknown>) =>\n (effect: Effect.Effect<A, E, R>) =>\n withStorageSpan(operation, STORAGE_TYPE, attributes)(effect);\n\n// GCS-specific logging functions\nexport const logGCSOperation = logStorageOperation.bind(null, STORAGE_TYPE);\nexport const logGCSUploadProgress = logUploadProgress.bind(null, STORAGE_TYPE);\nexport const logGCSUploadCompletion = logUploadCompletion.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logGCSContext = logWithContext;\n\n// Export metrics for external access\nexport const {\n uploadRequestsTotal: gcsUploadRequestsTotal,\n uploadPartsTotal: gcsUploadPartsTotal,\n uploadSuccessTotal: gcsUploadSuccessTotal,\n uploadErrorsTotal: gcsUploadErrorsTotal,\n apiCallsTotal: gcsApiCallsTotal,\n uploadDurationHistogram: gcsUploadDurationHistogram,\n partUploadDurationHistogram: gcsPartUploadDurationHistogram,\n fileSizeHistogram: gcsFileSizeHistogram,\n partSizeHistogram: gcsPartSizeHistogram,\n activeUploadsGauge: gcsActiveUploadsGauge,\n uploadThroughputGauge: gcsUploadThroughputGauge,\n uploadLatencySummary: gcsUploadLatencySummary,\n} = gcsMetrics;\n\n// Type exports\nexport type GCSMetrics = StorageMetrics;\n","import { type Effect, Layer } from \"effect\";\nimport {\n createStorageErrorTracker,\n type StorageErrorCategory,\n} from \"../core/errors.js\";\nimport {\n logStorageOperation,\n logUploadCompletion,\n logUploadProgress,\n logWithContext,\n} from \"../core/logging.js\";\nimport { createStorageMetrics, type StorageMetrics } from \"../core/metrics.js\";\nimport { createStorageTracingLayer, withStorageSpan } from \"../core/tracing.js\";\nimport {\n withApiMetrics,\n withStorageOperationMetrics,\n withTimingMetrics,\n withUploadMetrics,\n} from \"../core/utilities.js\";\n\n// ============================================================================\n// S3-Specific Observability\n// ============================================================================\n\nconst STORAGE_TYPE = \"s3\";\n\n// S3-specific metrics\nexport const s3Metrics = createStorageMetrics(STORAGE_TYPE);\n\n// S3-specific tracing layer\nexport const S3TracingLayer = createStorageTracingLayer(STORAGE_TYPE);\n\n// S3-specific error classification\nconst classifyS3Error = (error: unknown): StorageErrorCategory | null => {\n if (!error || typeof error !== \"object\") return null;\n\n const errorCode = \"code\" in error ? error.code : undefined;\n if (!errorCode) return null;\n\n // S3-specific error codes\n switch (errorCode) {\n case \"NoSuchKey\":\n case \"NoSuchBucket\":\n case \"NoSuchUpload\":\n return \"client_error\";\n case \"BucketAlreadyExists\":\n case \"BucketNotEmpty\":\n return \"client_error\";\n case \"InvalidBucketName\":\n case \"InvalidPart\":\n case \"InvalidPartOrder\":\n return \"client_error\";\n case \"EntityTooSmall\":\n case \"EntityTooLarge\":\n return \"client_error\";\n case \"ExpiredToken\":\n case \"TokenRefreshRequired\":\n return \"authentication_error\";\n case \"RequestTimeTooSkewed\":\n case \"SlowDown\":\n return \"throttling_error\";\n default:\n return null; // Fall back to generic classification\n }\n};\n\n// S3-specific error tracker\nexport const trackS3Error = createStorageErrorTracker(\n STORAGE_TYPE,\n s3Metrics,\n classifyS3Error,\n);\n\n// S3-specific observability layer\nexport const S3ObservabilityLayer = Layer.mergeAll(\n S3TracingLayer,\n // Metrics are automatically available through Effect\n);\n\n// ============================================================================\n// S3 Utility Functions\n// ============================================================================\n\nexport const withS3UploadMetrics = <A, E, R>(\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n) => withUploadMetrics(s3Metrics, uploadId, effect);\n\nexport const withS3ApiMetrics = <A, E, R>(\n operation: string,\n effect: Effect.Effect<A, E, R>,\n) => withApiMetrics(s3Metrics, operation, effect);\n\nexport const withS3TimingMetrics = withTimingMetrics;\n\nexport const withS3OperationMetrics = <A, E, R>(\n operation: string,\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n fileSize?: number,\n) =>\n withStorageOperationMetrics(s3Metrics, operation, uploadId, effect, fileSize);\n\n// S3-specific span wrapper\nexport const withS3Span =\n <A, E, R>(operation: string, attributes?: Record<string, unknown>) =>\n (effect: Effect.Effect<A, E, R>) =>\n withStorageSpan(operation, STORAGE_TYPE, attributes)(effect);\n\n// S3-specific logging functions\nexport const logS3Operation = logStorageOperation.bind(null, STORAGE_TYPE);\nexport const logS3UploadProgress = logUploadProgress.bind(null, STORAGE_TYPE);\nexport const logS3UploadCompletion = logUploadCompletion.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logS3Context = logWithContext;\n\n// Export metrics for external access\nexport const {\n uploadRequestsTotal: s3UploadRequestsTotal,\n uploadPartsTotal: s3UploadPartsTotal,\n uploadSuccessTotal: s3UploadSuccessTotal,\n uploadErrorsTotal: s3UploadErrorsTotal,\n apiCallsTotal: s3ApiCallsTotal,\n uploadDurationHistogram: s3UploadDurationHistogram,\n partUploadDurationHistogram: s3PartUploadDurationHistogram,\n fileSizeHistogram: s3FileSizeHistogram,\n partSizeHistogram: s3PartSizeHistogram,\n activeUploadsGauge: s3ActiveUploadsGauge,\n uploadThroughputGauge: s3UploadThroughputGauge,\n uploadLatencySummary: s3UploadLatencySummary,\n} = s3Metrics;\n\n// Type exports\nexport type S3Metrics = StorageMetrics;\n","import { Effect, Metric } from \"effect\";\nimport type { UploadServerMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Upload Error Classification and Tracking\n// ============================================================================\n\nexport type UploadErrorCategory =\n | \"network_error\"\n | \"authentication_error\"\n | \"authorization_error\"\n | \"validation_error\"\n | \"size_limit_error\"\n | \"storage_error\"\n | \"abort_error\"\n | \"unknown_error\";\n\n/**\n * Classify upload errors into standard categories\n */\nexport const classifyUploadError = (error: unknown): UploadErrorCategory => {\n if (!error || typeof error !== \"object\") return \"unknown_error\";\n\n const errorCode = \"code\" in error ? error.code : undefined;\n const errorName = \"name\" in error ? error.name : undefined;\n const errorMessage =\n error instanceof Error ? error.message.toLowerCase() : \"\";\n\n // Abort errors\n if (\n errorCode === \"ABORTED\" ||\n errorName === \"AbortError\" ||\n errorMessage.includes(\"abort\")\n ) {\n return \"abort_error\";\n }\n\n // Size limit errors\n if (\n errorCode === \"FILE_TOO_LARGE\" ||\n errorCode === \"LIMIT_FILE_SIZE\" ||\n errorCode === \"RequestEntityTooLarge\" ||\n errorMessage.includes(\"too large\") ||\n errorMessage.includes(\"size limit\") ||\n errorMessage.includes(\"max size\")\n ) {\n return \"size_limit_error\";\n }\n\n // Validation errors\n if (\n errorCode === \"INVALID_FILE\" ||\n errorCode === \"INVALID_METADATA\" ||\n errorCode === \"VALIDATION_ERROR\" ||\n errorMessage.includes(\"validation\") ||\n errorMessage.includes(\"invalid\")\n ) {\n return \"validation_error\";\n }\n\n // Network errors\n if (\n errorCode === \"NetworkError\" ||\n errorCode === \"ECONNRESET\" ||\n errorCode === \"ENOTFOUND\" ||\n errorCode === \"ETIMEDOUT\" ||\n errorMessage.includes(\"network\") ||\n errorMessage.includes(\"timeout\")\n ) {\n return \"network_error\";\n }\n\n // Authentication errors\n if (\n errorCode === \"UNAUTHORIZED\" ||\n errorCode === \"AuthenticationFailed\" ||\n errorName === \"AuthenticationError\" ||\n errorMessage.includes(\"authentication\") ||\n errorMessage.includes(\"unauthorized\")\n ) {\n return \"authentication_error\";\n }\n\n // Authorization errors\n if (\n errorCode === \"FORBIDDEN\" ||\n errorCode === \"AccessDenied\" ||\n errorName === \"AuthorizationError\" ||\n errorMessage.includes(\"forbidden\") ||\n errorMessage.includes(\"permission\")\n ) {\n return \"authorization_error\";\n }\n\n // Storage errors\n if (\n errorCode === \"FILE_WRITE_ERROR\" ||\n errorCode === \"STORAGE_ERROR\" ||\n errorMessage.includes(\"storage\") ||\n errorMessage.includes(\"write error\")\n ) {\n return \"storage_error\";\n }\n\n return \"unknown_error\";\n};\n\n/**\n * Track upload errors with metrics and structured logging\n */\nexport const trackUploadError = (\n metrics: UploadServerMetrics,\n operation: string,\n error: unknown,\n context: Record<string, unknown> = {},\n) =>\n Effect.gen(function* () {\n const errorCategory = classifyUploadError(error);\n\n // Record error metrics\n const errorMetric = metrics.uploadFailedTotal.pipe(\n Metric.tagged(\"operation\", operation),\n Metric.tagged(\"error_category\", errorCategory),\n );\n yield* errorMetric(Effect.succeed(1));\n\n // Create detailed error context\n const errorDetails = {\n operation,\n error_category: errorCategory,\n error_type: typeof error,\n error_message: error instanceof Error ? error.message : String(error),\n error_code:\n error && typeof error === \"object\" && \"code\" in error\n ? String(error.code)\n : undefined,\n error_name:\n error && typeof error === \"object\" && \"name\" in error\n ? String(error.name)\n : undefined,\n ...context,\n };\n\n // Log structured error\n yield* Effect.logError(`Upload ${operation} failed`).pipe(\n Effect.annotateLogs(errorDetails),\n );\n });\n\n/**\n * Create a custom error classifier for upload operations\n */\nexport const createUploadErrorClassifier = (\n customErrorMapping?: (error: unknown) => UploadErrorCategory | null,\n) => {\n return (error: unknown): UploadErrorCategory => {\n // Try custom mapping first\n if (customErrorMapping) {\n const customResult = customErrorMapping(error);\n if (customResult !== null) return customResult;\n }\n\n // Fall back to generic classification\n return classifyUploadError(error);\n };\n};\n","import { Metric, MetricBoundaries } from \"effect\";\n\n// ============================================================================\n// Upload Server Metrics\n// ============================================================================\n\n/**\n * Upload server metrics for tracking upload operations\n */\nexport const createUploadServerMetrics = () => ({\n // Counter metrics\n uploadCreatedTotal: Metric.counter(\"upload_created_total\", {\n description: \"Total number of uploads created\",\n }),\n\n uploadCompletedTotal: Metric.counter(\"upload_completed_total\", {\n description: \"Total number of uploads completed successfully\",\n }),\n\n uploadFailedTotal: Metric.counter(\"upload_failed_total\", {\n description: \"Total number of uploads that failed\",\n }),\n\n chunkUploadedTotal: Metric.counter(\"chunk_uploaded_total\", {\n description: \"Total number of chunks uploaded\",\n }),\n\n uploadFromUrlTotal: Metric.counter(\"upload_from_url_total\", {\n description: \"Total number of URL-based uploads\",\n }),\n\n uploadFromUrlSuccessTotal: Metric.counter(\"upload_from_url_success_total\", {\n description: \"Total number of successful URL-based uploads\",\n }),\n\n uploadFromUrlFailedTotal: Metric.counter(\"upload_from_url_failed_total\", {\n description: \"Total number of failed URL-based uploads\",\n }),\n\n // Histogram metrics\n uploadDurationHistogram: Metric.histogram(\n \"upload_duration_seconds\",\n MetricBoundaries.exponential({\n start: 0.01, // 10ms\n factor: 2,\n count: 20, // Up to ~10 seconds\n }),\n \"Duration of complete upload operations in seconds\",\n ),\n\n chunkUploadDurationHistogram: Metric.histogram(\n \"chunk_upload_duration_seconds\",\n MetricBoundaries.exponential({\n start: 0.001, // 1ms\n factor: 2,\n count: 15, // Up to ~32 seconds\n }),\n \"Duration of individual chunk uploads in seconds\",\n ),\n\n uploadFileSizeHistogram: Metric.histogram(\n \"upload_file_size_bytes\",\n MetricBoundaries.exponential({\n start: 1024, // 1KB\n factor: 2,\n count: 25, // Up to ~33GB\n }),\n \"Size of uploaded files in bytes\",\n ),\n\n chunkSizeHistogram: Metric.histogram(\n \"chunk_size_bytes\",\n MetricBoundaries.linear({\n start: 262_144, // 256KB\n width: 262_144, // 256KB increments\n count: 20, // Up to ~5MB\n }),\n \"Size of uploaded chunks in bytes\",\n ),\n\n // Gauge metrics\n activeUploadsGauge: Metric.gauge(\"active_uploads\", {\n description: \"Number of currently active uploads\",\n }),\n\n uploadThroughputGauge: Metric.gauge(\"upload_throughput_bytes_per_second\", {\n description: \"Current upload throughput in bytes per second\",\n }),\n\n // Summary metrics for latency percentiles\n uploadLatencySummary: Metric.summary({\n name: \"upload_latency_seconds\",\n maxAge: \"10 minutes\",\n maxSize: 1000,\n error: 0.01,\n quantiles: [0.5, 0.9, 0.95, 0.99],\n description: \"Upload operation latency percentiles\",\n }),\n\n chunkLatencySummary: Metric.summary({\n name: \"chunk_latency_seconds\",\n maxAge: \"10 minutes\",\n maxSize: 1000,\n error: 0.01,\n quantiles: [0.5, 0.9, 0.95, 0.99],\n description: \"Chunk upload latency percentiles\",\n }),\n});\n\n/**\n * Type for upload server metrics\n */\nexport type UploadServerMetrics = ReturnType<typeof createUploadServerMetrics>;\n\n/**\n * Default upload server metrics instance\n */\nexport const uploadServerMetrics = createUploadServerMetrics();\n","import { Effect, Layer, Metric } from \"effect\";\nimport {\n makeUploadObservabilityLayer,\n UploadObservability,\n} from \"../core/layers.js\";\nimport { createUploadServerMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Upload Observability Layer Implementation\n// ============================================================================\n\n/**\n * Create a live upload observability layer with full metrics\n */\nexport const makeUploadObservabilityLive = (\n serviceName = \"uploadista-upload-server\",\n): Layer.Layer<UploadObservability> => {\n const metrics = createUploadServerMetrics();\n\n return Layer.succeed(UploadObservability, {\n serviceName,\n enabled: true,\n metrics: {\n uploadCreated: Effect.succeed(metrics.uploadCreatedTotal).pipe(\n Effect.flatMap((metric) => Metric.increment(metric)),\n ),\n uploadCompleted: Effect.succeed(metrics.uploadCompletedTotal).pipe(\n Effect.flatMap((metric) => Metric.increment(metric)),\n ),\n uploadFailed: Effect.succeed(metrics.uploadFailedTotal).pipe(\n Effect.flatMap((metric) => Metric.increment(metric)),\n ),\n chunkUploaded: Effect.succeed(metrics.chunkUploadedTotal).pipe(\n Effect.flatMap((metric) => Metric.increment(metric)),\n ),\n },\n });\n};\n\n/**\n * Default live upload observability layer\n */\nexport const UploadObservabilityLive = makeUploadObservabilityLive();\n\n/**\n * No-op upload observability layer (for testing or disabled observability)\n */\nexport const UploadObservabilityDisabled = makeUploadObservabilityLayer(false);\n\n/**\n * Helper to get upload metrics from context\n */\nexport const getUploadMetrics = Effect.gen(function* () {\n const obs = yield* UploadObservability;\n return obs.metrics;\n});\n\n/**\n * Helper to track upload duration\n */\nexport const withUploadDuration = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R | UploadObservability> => {\n const metrics = createUploadServerMetrics();\n return Effect.gen(function* () {\n const startTime = Date.now();\n const result = yield* effect;\n const duration = (Date.now() - startTime) / 1000; // Convert to seconds\n yield* Metric.update(metrics.uploadDurationHistogram, duration);\n return result;\n }).pipe(Effect.withSpan(\"upload-operation\"));\n};\n\n/**\n * Helper to track chunk upload duration\n */\nexport const withChunkDuration = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> => {\n const metrics = createUploadServerMetrics();\n return Effect.gen(function* () {\n const startTime = Date.now();\n const result = yield* effect;\n const duration = (Date.now() - startTime) / 1000; // Convert to seconds\n yield* Metric.update(metrics.chunkUploadDurationHistogram, duration);\n return result;\n }).pipe(Effect.withSpan(\"chunk-upload\"));\n};\n","import { Layer } from \"effect\";\nimport { UploadObservability } from \"../core/layers.js\";\nimport { createUploadServerMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Upload Observability Testing Utilities\n// ============================================================================\n\n/**\n * Create a test upload observability layer that doesn't actually emit metrics\n * but validates that the observability system is wired correctly\n */\nexport const UploadObservabilityTest = Layer.succeed(UploadObservability, {\n serviceName: \"uploadista-upload-server-test\",\n enabled: true,\n metrics: {\n uploadCreated: () => Promise.resolve(),\n uploadCompleted: () => Promise.resolve(),\n uploadFailed: () => Promise.resolve(),\n chunkUploaded: () => Promise.resolve(),\n } as any,\n});\n\n/**\n * Get metrics for validation (useful for testing metric definitions)\n */\nexport const getTestMetrics = () => createUploadServerMetrics();\n\n/**\n * Validate that all required metrics exist\n */\nexport const validateMetricsExist = () => {\n const metrics = getTestMetrics();\n\n const requiredMetrics = [\n \"uploadCreatedTotal\",\n \"uploadCompletedTotal\",\n \"uploadFailedTotal\",\n \"chunkUploadedTotal\",\n \"uploadFromUrlTotal\",\n \"uploadFromUrlSuccessTotal\",\n \"uploadFromUrlFailedTotal\",\n \"uploadDurationHistogram\",\n \"chunkUploadDurationHistogram\",\n \"uploadFileSizeHistogram\",\n \"chunkSizeHistogram\",\n \"activeUploadsGauge\",\n \"uploadThroughputGauge\",\n \"uploadLatencySummary\",\n \"chunkLatencySummary\",\n ];\n\n const missingMetrics = requiredMetrics.filter((name) => !(name in metrics));\n\n if (missingMetrics.length > 0) {\n throw new Error(`Missing required metrics: ${missingMetrics.join(\", \")}`);\n }\n\n return true;\n};\n","import { Effect } from \"effect\";\n\n// ============================================================================\n// Upload Tracing Utilities\n// ============================================================================\n\n/**\n * Wrap an Effect with an upload operation span\n */\nexport const withUploadSpan =\n <A, E, R>(operation: string, attributes?: Record<string, unknown>) =>\n (effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.withSpan(`upload-${operation}`, {\n attributes: {\n \"upload.operation\": operation,\n ...attributes,\n },\n }),\n );\n\n/**\n * Add upload context to the current span\n */\nexport const withUploadContext = (context: {\n uploadId?: string;\n fileName?: string;\n fileSize?: number;\n storageId?: string;\n mimeType?: string;\n}) =>\n Effect.annotateCurrentSpan({\n \"upload.id\": context.uploadId ?? \"unknown\",\n \"upload.file_name\": context.fileName ?? \"unknown\",\n \"upload.file_size\": context.fileSize?.toString() ?? \"0\",\n \"upload.storage_id\": context.storageId ?? \"unknown\",\n \"upload.mime_type\": context.mimeType ?? \"unknown\",\n });\n\n/**\n * Add chunk context to the current span\n */\nexport const withChunkContext = (context: {\n uploadId: string;\n chunkSize: number;\n offset: number;\n totalSize?: number;\n}) =>\n Effect.annotateCurrentSpan({\n \"chunk.upload_id\": context.uploadId,\n \"chunk.size\": context.chunkSize.toString(),\n \"chunk.offset\": context.offset.toString(),\n \"chunk.total_size\": context.totalSize?.toString() ?? \"0\",\n \"chunk.progress\":\n context.totalSize && context.totalSize > 0\n ? ((context.offset / context.totalSize) * 100).toFixed(2)\n : \"0\",\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["headers: Record<string, string>","signalEndpoint: string | undefined","OTLPMetricExporter","attrs: Record<string, string>","OTLPLogExporter","MeterProvider","PeriodicExportingMetricReader","LoggerProvider","BatchLogRecordProcessor","SeverityNumber","LoggerProvider","EffectLogger","attributes: Record<string, unknown>","MeterProvider","config: OtlpSdkConfig","captureTraceContextEffect: Effect.Effect<\n TraceContext | undefined\n>","service: StorageObservabilityService","service: UploadObservabilityService","service: FlowObservabilityService","FlowObservabilityDisabled","makeTestFlowObservability","service: FlowObservabilityService","NoOpMetricsServiceLive: Layer.Layer<MetricsService>","STORAGE_TYPE","STORAGE_TYPE","STORAGE_TYPE","UploadObservabilityDisabled"],"sources":["../src/core/errors.ts","../src/core/exporters.ts","../src/core/logs-sdk.ts","../src/core/metrics-sdk.ts","../src/core/tracing.ts","../src/core/full-observability.ts","../src/core/layers.ts","../src/core/logging.ts","../src/core/metrics.ts","../src/core/testing.ts","../src/core/utilities.ts","../src/flow/metrics.ts","../src/flow/errors.ts","../src/flow/layers.ts","../src/flow/testing.ts","../src/flow/tracing.ts","../src/service/metrics.ts","../src/storage/azure.ts","../src/storage/filesystem.ts","../src/storage/gcs.ts","../src/storage/s3.ts","../src/upload/errors.ts","../src/upload/metrics.ts","../src/upload/layers.ts","../src/upload/testing.ts","../src/upload/tracing.ts"],"sourcesContent":["import { Effect, Metric } from \"effect\";\nimport type { StorageMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Generic Storage Error Classification and Tracking\n// ============================================================================\n\nexport type StorageErrorCategory =\n | \"network_error\"\n | \"authentication_error\"\n | \"authorization_error\"\n | \"throttling_error\"\n | \"server_error\"\n | \"client_error\"\n | \"unknown_error\";\n\n// Generic error classifier - can be extended per storage type\nexport const classifyStorageError = (error: unknown): StorageErrorCategory => {\n if (!error || typeof error !== \"object\") return \"unknown_error\";\n\n const errorCode = \"code\" in error ? error.code : undefined;\n const errorName = \"name\" in error ? error.name : undefined;\n const errorMessage =\n error instanceof Error ? error.message.toLowerCase() : \"\";\n\n // Network errors (common across all storage types)\n if (\n errorCode === \"NetworkError\" ||\n errorCode === \"ECONNRESET\" ||\n errorCode === \"ENOTFOUND\" ||\n errorCode === \"ETIMEDOUT\" ||\n errorMessage.indexOf(\"network\") >= 0 ||\n errorMessage.indexOf(\"timeout\") >= 0\n ) {\n return \"network_error\";\n }\n\n // Authentication errors (common patterns)\n if (\n errorCode === \"InvalidAccessKeyId\" ||\n errorCode === \"SignatureDoesNotMatch\" ||\n errorCode === \"TokenRefreshRequired\" ||\n errorCode === \"AuthenticationFailed\" ||\n errorName === \"AuthenticationError\" ||\n errorMessage.indexOf(\"authentication\") >= 0 ||\n errorMessage.indexOf(\"unauthorized\") >= 0\n ) {\n return \"authentication_error\";\n }\n\n // Authorization errors\n if (\n errorCode === \"AccessDenied\" ||\n errorCode === \"AccountProblem\" ||\n errorCode === \"Forbidden\" ||\n errorName === \"AuthorizationError\" ||\n errorMessage.indexOf(\"forbidden\") >= 0 ||\n errorMessage.indexOf(\"permission\") >= 0\n ) {\n return \"authorization_error\";\n }\n\n // Throttling errors\n if (\n errorCode === \"SlowDown\" ||\n errorCode === \"RequestTimeTooSkewed\" ||\n errorCode === \"TooManyRequests\" ||\n errorName === \"ThrottlingError\" ||\n errorMessage.indexOf(\"throttl\") >= 0 ||\n errorMessage.indexOf(\"rate limit\") >= 0\n ) {\n return \"throttling_error\";\n }\n\n // Server errors\n if (\n errorCode === \"InternalError\" ||\n errorCode === \"ServiceUnavailable\" ||\n errorCode === \"InternalServerError\" ||\n errorName === \"ServerError\" ||\n errorMessage.indexOf(\"server error\") >= 0 ||\n errorMessage.indexOf(\"service unavailable\") >= 0\n ) {\n return \"server_error\";\n }\n\n // Client errors\n if (\n errorCode === \"InvalidRequest\" ||\n errorCode === \"MalformedXML\" ||\n errorCode === \"RequestEntityTooLarge\" ||\n errorCode === \"BadRequest\" ||\n errorName === \"ClientError\" ||\n errorMessage.indexOf(\"bad request\") >= 0 ||\n errorMessage.indexOf(\"invalid\") >= 0\n ) {\n return \"client_error\";\n }\n\n return \"unknown_error\";\n};\n\n// Storage-specific error classifier factory\nexport const createStorageErrorClassifier = (\n storageType: string,\n customErrorMapping?: (error: unknown) => StorageErrorCategory | null,\n) => {\n return (error: unknown): StorageErrorCategory => {\n // Try custom mapping first\n if (customErrorMapping) {\n const customResult = customErrorMapping(error);\n if (customResult !== null) return customResult;\n }\n\n // Fall back to generic classification\n return classifyStorageError(error);\n };\n};\n\n// Generic error tracking function\nexport const trackStorageError = (\n storageType: string,\n metrics: StorageMetrics,\n operation: string,\n error: unknown,\n context: Record<string, unknown> = {},\n errorClassifier = classifyStorageError,\n) =>\n Effect.gen(function* () {\n const errorCategory = errorClassifier(error);\n\n // Record error metrics\n const errorMetric = metrics.uploadErrorsTotal.pipe(\n Metric.tagged(\"operation\", operation),\n Metric.tagged(\"error_category\", errorCategory),\n );\n yield* errorMetric(Effect.succeed(1));\n\n // Create detailed error context\n const errorDetails = {\n storage_type: storageType,\n operation,\n error_category: errorCategory,\n error_type: typeof error,\n error_message: error instanceof Error ? error.message : String(error),\n error_code:\n error && typeof error === \"object\" && \"code\" in error\n ? error.code\n : undefined,\n error_name:\n error && typeof error === \"object\" && \"name\" in error\n ? error.name\n : undefined,\n ...context,\n };\n\n // Log structured error\n yield* Effect.logError(\n `${storageType.toUpperCase()} ${operation} failed`,\n ).pipe(Effect.annotateLogs(errorDetails));\n });\n\n// Factory for storage-specific error tracking\nexport const createStorageErrorTracker = (\n storageType: string,\n metrics: StorageMetrics,\n customErrorClassifier?: (error: unknown) => StorageErrorCategory | null,\n) => {\n const errorClassifier = createStorageErrorClassifier(\n storageType,\n customErrorClassifier,\n );\n\n return (\n operation: string,\n error: unknown,\n context: Record<string, unknown> = {},\n ) =>\n trackStorageError(\n storageType,\n metrics,\n operation,\n error,\n context,\n errorClassifier,\n );\n};\n","/**\n * OTLP Exporter Configuration for Uploadista SDK.\n *\n * This module provides factory functions for creating OpenTelemetry Protocol (OTLP)\n * exporters that send traces, metrics, and logs to observability backends like Grafana,\n * Jaeger, Datadog, and others.\n *\n * Configuration is done via standard OpenTelemetry environment variables:\n * - OTEL_EXPORTER_OTLP_ENDPOINT: Base endpoint URL (default: http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_HEADERS: Headers for authentication (format: key=value,key2=value2)\n * - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: Override endpoint for traces only\n * - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: Override endpoint for metrics only\n * - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: Override endpoint for logs only\n *\n * @module core/exporters\n */\n\nimport { OTLPLogExporter } from \"@opentelemetry/exporter-logs-otlp-http\";\nimport { OTLPMetricExporter } from \"@opentelemetry/exporter-metrics-otlp-http\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport {\n BatchLogRecordProcessor,\n LoggerProvider,\n} from \"@opentelemetry/sdk-logs\";\nimport {\n MeterProvider,\n PeriodicExportingMetricReader,\n} from \"@opentelemetry/sdk-metrics\";\n\n/**\n * Configuration options for OTLP exporters.\n */\nexport interface OtlpExporterConfig {\n /** Base endpoint URL. Defaults to OTEL_EXPORTER_OTLP_ENDPOINT or http://localhost:4318 */\n endpoint?: string;\n /** Headers to include in requests (for authentication). Defaults to OTEL_EXPORTER_OTLP_HEADERS */\n headers?: Record<string, string>;\n /** Request timeout in milliseconds. Defaults to 5000 */\n timeoutMillis?: number;\n}\n\n/**\n * Parses the OTEL_EXPORTER_OTLP_HEADERS environment variable.\n *\n * Format: key=value,key2=value2\n * Example: Authorization=Basic abc123,X-Custom-Header=value\n *\n * @returns Parsed headers as a Record, or undefined if not set\n */\nexport function parseOtlpHeaders(): Record<string, string> | undefined {\n const headersEnv =\n typeof process !== \"undefined\"\n ? process.env.OTEL_EXPORTER_OTLP_HEADERS\n : undefined;\n\n if (!headersEnv) {\n return undefined;\n }\n\n const headers: Record<string, string> = {};\n const pairs = headersEnv.split(\",\");\n\n for (const pair of pairs) {\n const [key, ...valueParts] = pair.split(\"=\");\n if (key && valueParts.length > 0) {\n headers[key.trim()] = valueParts.join(\"=\").trim();\n }\n }\n\n return Object.keys(headers).length > 0 ? headers : undefined;\n}\n\n/**\n * Gets the OTLP endpoint from environment variables with fallback.\n *\n * Checks in order:\n * 1. Provided endpoint parameter\n * 2. Signal-specific endpoint (OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, or OTEL_EXPORTER_OTLP_LOGS_ENDPOINT)\n * 3. Base endpoint (OTEL_EXPORTER_OTLP_ENDPOINT)\n * 4. Default: http://localhost:4318\n *\n * @param signal - The signal type ('traces', 'metrics', or 'logs')\n * @param configEndpoint - Optional endpoint from config\n * @returns The resolved endpoint URL\n */\nexport function getOtlpEndpoint(\n signal: \"traces\" | \"metrics\" | \"logs\",\n configEndpoint?: string,\n): string {\n if (configEndpoint) {\n return configEndpoint;\n }\n\n if (typeof process !== \"undefined\") {\n let signalEndpoint: string | undefined;\n switch (signal) {\n case \"traces\":\n signalEndpoint = process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;\n break;\n case \"metrics\":\n signalEndpoint = process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT;\n break;\n case \"logs\":\n signalEndpoint = process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT;\n break;\n }\n\n if (signalEndpoint) {\n return signalEndpoint;\n }\n\n if (process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {\n return process.env.OTEL_EXPORTER_OTLP_ENDPOINT;\n }\n }\n\n return \"http://localhost:4318\";\n}\n\n/**\n * Creates an OTLP trace exporter configured from environment variables.\n *\n * The exporter sends traces to an OTLP-compatible endpoint using HTTP/protobuf.\n *\n * @param config - Optional configuration overrides\n * @returns Configured OTLPTraceExporter instance\n *\n * @example\n * ```typescript\n * // Use environment variables\n * const exporter = createOtlpTraceExporter();\n *\n * // Override endpoint\n * const exporter = createOtlpTraceExporter({\n * endpoint: 'https://otlp.grafana.net'\n * });\n * ```\n */\nexport function createOtlpTraceExporter(\n config: OtlpExporterConfig = {},\n): OTLPTraceExporter {\n const endpoint = getOtlpEndpoint(\"traces\", config.endpoint);\n const headers = config.headers ?? parseOtlpHeaders();\n // Default to 30 seconds to accommodate cloud endpoints like Grafana Cloud\n const timeoutMillis = config.timeoutMillis ?? 30000;\n\n return new OTLPTraceExporter({\n url: `${endpoint}/v1/traces`,\n headers,\n timeoutMillis,\n });\n}\n\n/**\n * Creates an OTLP metric exporter configured from environment variables.\n *\n * The exporter sends metrics to an OTLP-compatible endpoint using HTTP/protobuf.\n *\n * @param config - Optional configuration overrides\n * @returns Configured OTLPMetricExporter instance\n *\n * @example\n * ```typescript\n * // Use environment variables\n * const exporter = createOtlpMetricExporter();\n *\n * // Override endpoint\n * const exporter = createOtlpMetricExporter({\n * endpoint: 'https://otlp.grafana.net'\n * });\n * ```\n */\nexport function createOtlpMetricExporter(\n config: OtlpExporterConfig = {},\n): OTLPMetricExporter {\n const endpoint = getOtlpEndpoint(\"metrics\", config.endpoint);\n const headers = config.headers ?? parseOtlpHeaders();\n // Default to 30 seconds to accommodate cloud endpoints like Grafana Cloud\n const timeoutMillis = config.timeoutMillis ?? 30000;\n\n return new OTLPMetricExporter({\n url: `${endpoint}/v1/metrics`,\n headers,\n timeoutMillis,\n });\n}\n\n/**\n * Checks if observability is enabled via environment variable.\n *\n * Reads UPLOADISTA_OBSERVABILITY_ENABLED environment variable.\n * Defaults to true if not set.\n *\n * @returns true if observability should be enabled\n */\nexport function isOtlpExportEnabled(): boolean {\n if (typeof process !== \"undefined\") {\n const enabled = process.env.UPLOADISTA_OBSERVABILITY_ENABLED;\n if (enabled !== undefined) {\n return enabled.toLowerCase() !== \"false\" && enabled !== \"0\";\n }\n }\n return true;\n}\n\n/**\n * Gets the service name from environment variables.\n *\n * Reads OTEL_SERVICE_NAME environment variable.\n * Defaults to \"uploadista\" if not set.\n *\n * @param defaultName - Default service name if not configured\n * @returns The service name to use\n */\nexport function getServiceName(defaultName = \"uploadista\"): string {\n if (typeof process !== \"undefined\" && process.env.OTEL_SERVICE_NAME) {\n return process.env.OTEL_SERVICE_NAME;\n }\n return defaultName;\n}\n\n/**\n * Parses resource attributes from OTEL_RESOURCE_ATTRIBUTES environment variable.\n *\n * Format: key=value,key2=value2\n * Example: tenant.id=abc123,deployment.environment=production\n *\n * @returns Parsed attributes as a Record, or empty object if not set\n */\nexport function parseResourceAttributes(): Record<string, string> {\n if (typeof process === \"undefined\") {\n return {};\n }\n\n const attrsEnv = process.env.OTEL_RESOURCE_ATTRIBUTES;\n if (!attrsEnv) {\n return {};\n }\n\n const attrs: Record<string, string> = {};\n const pairs = attrsEnv.split(\",\");\n\n for (const pair of pairs) {\n const [key, ...valueParts] = pair.split(\"=\");\n if (key && valueParts.length > 0) {\n attrs[key.trim()] = valueParts.join(\"=\").trim();\n }\n }\n\n return attrs;\n}\n\n// ============================================================================\n// Logs Exporter\n// ============================================================================\n\n/**\n * Creates an OTLP log exporter configured from environment variables.\n *\n * The exporter sends logs to an OTLP-compatible endpoint using HTTP/protobuf.\n *\n * @param config - Optional configuration overrides\n * @returns Configured OTLPLogExporter instance\n *\n * @example\n * ```typescript\n * // Use environment variables\n * const exporter = createOtlpLogExporter();\n *\n * // Override endpoint\n * const exporter = createOtlpLogExporter({\n * endpoint: 'https://otlp.grafana.net'\n * });\n * ```\n */\nexport function createOtlpLogExporter(\n config: OtlpExporterConfig = {},\n): OTLPLogExporter {\n const endpoint = getOtlpEndpoint(\"logs\", config.endpoint);\n const headers = config.headers ?? parseOtlpHeaders();\n // Default to 30 seconds to accommodate cloud endpoints like Grafana Cloud\n const timeoutMillis = config.timeoutMillis ?? 30000;\n\n return new OTLPLogExporter({\n url: `${endpoint}/v1/logs`,\n headers,\n timeoutMillis,\n });\n}\n\n// ============================================================================\n// Metrics SDK Configuration\n// ============================================================================\n\n/**\n * Configuration for metrics export.\n */\nexport interface MetricsSdkConfig extends OtlpExporterConfig {\n /** Service name for metrics. Defaults to OTEL_SERVICE_NAME or \"uploadista\" */\n serviceName?: string;\n /** Export interval in milliseconds. Defaults to OTEL_METRICS_EXPORT_INTERVAL or 60000 */\n exportIntervalMillis?: number;\n /** Export timeout in milliseconds. Defaults to 30000 */\n exportTimeoutMillis?: number;\n}\n\n/**\n * Gets the metrics export interval from environment or config.\n *\n * @param configInterval - Optional interval from config\n * @returns Export interval in milliseconds\n */\nexport function getMetricsExportInterval(configInterval?: number): number {\n if (configInterval !== undefined) {\n return configInterval;\n }\n\n if (\n typeof process !== \"undefined\" &&\n process.env.OTEL_METRICS_EXPORT_INTERVAL\n ) {\n const interval = Number.parseInt(\n process.env.OTEL_METRICS_EXPORT_INTERVAL,\n 10,\n );\n if (!Number.isNaN(interval) && interval > 0) {\n return interval;\n }\n }\n\n return 60000; // Default: 60 seconds\n}\n\n/**\n * Creates an OTLP MeterProvider with PeriodicExportingMetricReader.\n *\n * The MeterProvider is pre-configured with:\n * - OTLP HTTP exporter for metrics\n * - Periodic export based on OTEL_METRICS_EXPORT_INTERVAL (default: 60s)\n * - Graceful error handling (failures logged, not thrown)\n *\n * @param config - Optional configuration\n * @returns Configured MeterProvider instance\n *\n * @example\n * ```typescript\n * const meterProvider = createOtlpMeterProvider();\n * const meter = meterProvider.getMeter(\"uploadista\");\n * const counter = meter.createCounter(\"uploads_total\");\n * counter.add(1, { storage: \"s3\" });\n * ```\n */\nexport function createOtlpMeterProvider(\n config: MetricsSdkConfig = {},\n): MeterProvider {\n const exporter = createOtlpMetricExporter(config);\n const exportIntervalMillis = getMetricsExportInterval(\n config.exportIntervalMillis,\n );\n const exportTimeoutMillis = config.exportTimeoutMillis ?? 30000;\n\n const reader = new PeriodicExportingMetricReader({\n exporter,\n exportIntervalMillis,\n exportTimeoutMillis,\n });\n\n return new MeterProvider({\n readers: [reader],\n });\n}\n\n// ============================================================================\n// Logs SDK Configuration\n// ============================================================================\n\n/**\n * Configuration for logs export.\n */\nexport interface LogsSdkConfig extends OtlpExporterConfig {\n /** Service name for logs. Defaults to OTEL_SERVICE_NAME or \"uploadista\" */\n serviceName?: string;\n /** Maximum queue size for batch processor. Defaults to 512 */\n maxQueueSize?: number;\n /** Maximum export batch size. Defaults to 512 */\n maxExportBatchSize?: number;\n /** Schedule delay in milliseconds. Defaults to OTEL_LOGS_EXPORT_INTERVAL or 5000 */\n scheduledDelayMillis?: number;\n /** Export timeout in milliseconds. Defaults to 30000 */\n exportTimeoutMillis?: number;\n}\n\n/**\n * Gets the logs export interval from environment or config.\n *\n * @param configInterval - Optional interval from config\n * @returns Export interval in milliseconds\n */\nexport function getLogsExportInterval(configInterval?: number): number {\n if (configInterval !== undefined) {\n return configInterval;\n }\n\n if (typeof process !== \"undefined\" && process.env.OTEL_LOGS_EXPORT_INTERVAL) {\n const interval = Number.parseInt(process.env.OTEL_LOGS_EXPORT_INTERVAL, 10);\n if (!Number.isNaN(interval) && interval > 0) {\n return interval;\n }\n }\n\n return 5000; // Default: 5 seconds\n}\n\n/**\n * Creates an OTLP LoggerProvider with BatchLogRecordProcessor.\n *\n * The LoggerProvider is pre-configured with:\n * - OTLP HTTP exporter for logs\n * - Batch processing with configurable queue and batch sizes\n * - Graceful error handling (failures logged, not thrown)\n *\n * @param config - Optional configuration\n * @returns Configured LoggerProvider instance\n *\n * @example\n * ```typescript\n * const loggerProvider = createOtlpLoggerProvider();\n * const logger = loggerProvider.getLogger(\"uploadista\");\n * logger.emit({\n * severityNumber: SeverityNumber.INFO,\n * body: \"Upload completed\",\n * attributes: { uploadId: \"123\" },\n * });\n * ```\n */\nexport function createOtlpLoggerProvider(\n config: LogsSdkConfig = {},\n): LoggerProvider {\n const exporter = createOtlpLogExporter(config);\n const scheduledDelayMillis = getLogsExportInterval(\n config.scheduledDelayMillis,\n );\n\n const processor = new BatchLogRecordProcessor(exporter, {\n maxQueueSize: config.maxQueueSize ?? 512,\n maxExportBatchSize: config.maxExportBatchSize ?? 512,\n scheduledDelayMillis,\n exportTimeoutMillis: config.exportTimeoutMillis ?? 30000,\n });\n\n // Create provider with processor in constructor (SDK 0.208.0+ API)\n return new LoggerProvider({\n processors: [processor],\n });\n}\n\nexport { OTLPLogExporter } from \"@opentelemetry/exporter-logs-otlp-http\";\nexport { OTLPMetricExporter } from \"@opentelemetry/exporter-metrics-otlp-http\";\nexport {\n BatchLogRecordProcessor,\n LoggerProvider,\n} from \"@opentelemetry/sdk-logs\";\n// Re-export types for convenience\nexport {\n MeterProvider,\n PeriodicExportingMetricReader,\n} from \"@opentelemetry/sdk-metrics\";\n","/**\n * OTLP Logs SDK Layers for Uploadista SDK.\n *\n * This module provides Effect Layers that export logs to OTLP-compatible\n * backends like Grafana Loki, Elasticsearch, and others.\n *\n * Logs are automatically enriched with:\n * - trace_id and span_id for correlation with traces\n * - Service name and resource attributes\n * - Effect log annotations as attributes\n *\n * Configuration is done via standard OpenTelemetry environment variables:\n * - OTEL_EXPORTER_OTLP_ENDPOINT: Base endpoint URL (default: http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: Override endpoint for logs only\n * - OTEL_EXPORTER_OTLP_HEADERS: Headers for authentication\n * - OTEL_SERVICE_NAME: Service name (default: uploadista)\n * - OTEL_LOGS_EXPORT_INTERVAL: Export interval in ms (default: 5000)\n * - UPLOADISTA_OBSERVABILITY_ENABLED: Set to \"false\" to disable (default: true)\n *\n * @module core/logs-sdk\n */\n\nimport { trace } from \"@opentelemetry/api\";\nimport type { Logger } from \"@opentelemetry/api-logs\";\nimport { SeverityNumber } from \"@opentelemetry/api-logs\";\nimport type { LoggerProvider } from \"@opentelemetry/sdk-logs\";\nimport { Context, Effect, Logger as EffectLogger, Layer } from \"effect\";\nimport {\n createOtlpLoggerProvider,\n getServiceName,\n isOtlpExportEnabled,\n type LogsSdkConfig,\n} from \"./exporters.js\";\n\n// ============================================================================\n// Logs Service\n// ============================================================================\n\n/**\n * OpenTelemetry Logger service for emitting logs.\n */\nexport interface OtelLoggerService {\n /** The OpenTelemetry Logger instance */\n readonly logger: Logger;\n /** The LoggerProvider for shutdown handling */\n readonly provider: LoggerProvider;\n}\n\n/**\n * Effect Context tag for the OTEL Logger service.\n */\nexport class OtelLogger extends Context.Tag(\"OtelLogger\")<\n OtelLogger,\n OtelLoggerService\n>() {}\n\n// ============================================================================\n// Log Level Mapping\n// ============================================================================\n\n/**\n * Maps Effect log levels to OpenTelemetry SeverityNumber.\n *\n * | Effect Level | OTEL Severity |\n * |--------------|---------------|\n * | Debug | 5 (DEBUG) |\n * | Info | 9 (INFO) |\n * | Warning | 13 (WARN) |\n * | Error | 17 (ERROR) |\n * | Fatal | 21 (FATAL) |\n */\nexport function mapLogLevelToSeverity(level: string): SeverityNumber {\n switch (level.toLowerCase()) {\n case \"debug\":\n case \"trace\":\n return SeverityNumber.DEBUG;\n case \"info\":\n return SeverityNumber.INFO;\n case \"warning\":\n case \"warn\":\n return SeverityNumber.WARN;\n case \"error\":\n return SeverityNumber.ERROR;\n case \"fatal\":\n case \"critical\":\n return SeverityNumber.FATAL;\n default:\n return SeverityNumber.INFO;\n }\n}\n\n/**\n * Maps SeverityNumber to human-readable severity text.\n */\nexport function severityToText(severity: SeverityNumber): string {\n if (severity <= SeverityNumber.DEBUG) return \"DEBUG\";\n if (severity <= SeverityNumber.INFO) return \"INFO\";\n if (severity <= SeverityNumber.WARN) return \"WARN\";\n if (severity <= SeverityNumber.ERROR) return \"ERROR\";\n return \"FATAL\";\n}\n\n// ============================================================================\n// Trace Context Injection\n// ============================================================================\n\n/**\n * Gets the current trace context from OpenTelemetry.\n *\n * @returns Object with trace_id and span_id if active, empty object otherwise\n */\nexport function getTraceContext(): Record<string, string> {\n const span = trace.getActiveSpan();\n if (!span) {\n return {};\n }\n\n const ctx = span.spanContext();\n // Only include if valid trace ID\n if (ctx.traceId === \"00000000000000000000000000000000\") {\n return {};\n }\n\n return {\n trace_id: ctx.traceId,\n span_id: ctx.spanId,\n trace_flags: String(ctx.traceFlags),\n };\n}\n\n// ============================================================================\n// Logs SDK Layers\n// ============================================================================\n\n/**\n * Extended configuration for logs SDK.\n */\nexport interface LogsLayerConfig extends LogsSdkConfig {\n /** Minimum severity level to export. Logs below this level are filtered. */\n minSeverity?: SeverityNumber;\n}\n\n/**\n * Creates a Logs SDK layer with the given configuration.\n *\n * @param config - Logs SDK configuration\n * @returns Effect Layer providing OtelLogger service\n */\nfunction createLogsSdkLayer(config: LogsLayerConfig = {}) {\n return Layer.scoped(\n OtelLogger,\n Effect.gen(function* () {\n // Check if observability is disabled\n if (!isOtlpExportEnabled()) {\n // Return a no-op logger that doesn't export\n const { LoggerProvider } = yield* Effect.promise(\n () => import(\"@opentelemetry/sdk-logs\"),\n );\n const noopProvider = new LoggerProvider();\n return {\n logger: noopProvider.getLogger(getServiceName(\"uploadista\")),\n provider: noopProvider,\n };\n }\n\n // Create the OTLP LoggerProvider\n const provider = createOtlpLoggerProvider(config);\n const serviceName = config.serviceName ?? getServiceName(\"uploadista\");\n const logger = provider.getLogger(serviceName);\n\n // Register shutdown handler\n yield* Effect.addFinalizer(() =>\n Effect.promise(async () => {\n try {\n await provider.shutdown();\n } catch (error) {\n // Log but don't throw on shutdown errors\n console.warn(\"Error shutting down LoggerProvider:\", error);\n }\n }),\n );\n\n return { logger, provider };\n }),\n );\n}\n\n/**\n * Node.js OTLP Logs SDK Layer for production use.\n *\n * Exports logs to an OTLP-compatible endpoint with automatic trace correlation.\n *\n * @example\n * ```typescript\n * import { OtlpLogsNodeSdkLive, OtelLogger } from \"@uploadista/observability\";\n * import { Effect } from \"effect\";\n * import { SeverityNumber } from \"@opentelemetry/api-logs\";\n *\n * const program = Effect.gen(function* () {\n * const { logger } = yield* OtelLogger;\n * logger.emit({\n * severityNumber: SeverityNumber.INFO,\n * body: \"Upload completed\",\n * attributes: { uploadId: \"123\" },\n * });\n * }).pipe(Effect.provide(OtlpLogsNodeSdkLive));\n * ```\n */\nexport const OtlpLogsNodeSdkLive = createLogsSdkLayer();\n\n/**\n * Creates a customized OTLP Logs Node.js SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer\n *\n * @example\n * ```typescript\n * const customLogs = createOtlpLogsNodeSdkLayer({\n * serviceName: \"my-upload-service\",\n * minSeverity: SeverityNumber.WARN, // Only export WARN and above\n * });\n * ```\n */\nexport function createOtlpLogsNodeSdkLayer(config: LogsLayerConfig = {}) {\n return createLogsSdkLayer(config);\n}\n\n/**\n * Browser OTLP Logs SDK Layer for production use.\n *\n * Uses the same OTLP HTTP exporter, suitable for browser environments.\n * Note: Browser environments may have CORS restrictions.\n *\n * @example\n * ```typescript\n * import { OtlpLogsWebSdkLive } from \"@uploadista/observability\";\n *\n * const program = myEffect.pipe(Effect.provide(OtlpLogsWebSdkLive));\n * ```\n */\nexport const OtlpLogsWebSdkLive = createLogsSdkLayer();\n\n/**\n * Creates a customized OTLP Logs Web SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer for browser environments\n */\nexport function createOtlpLogsWebSdkLayer(config: LogsLayerConfig = {}) {\n return createLogsSdkLayer(config);\n}\n\n/**\n * Cloudflare Workers OTLP Logs SDK Layer for production use.\n *\n * Pre-configured with Workers-appropriate defaults.\n *\n * @example\n * ```typescript\n * import { OtlpLogsWorkersSdkLive } from \"@uploadista/observability\";\n *\n * export default {\n * async fetch(request, env) {\n * const program = handleRequest(request).pipe(\n * Effect.provide(OtlpLogsWorkersSdkLive)\n * );\n * return Effect.runPromise(program);\n * }\n * };\n * ```\n */\nexport const OtlpLogsWorkersSdkLive = createLogsSdkLayer({\n serviceName: getServiceName(\"uploadista-workers\"),\n});\n\n/**\n * Creates a customized OTLP Logs Workers SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer for Cloudflare Workers\n */\nexport function createOtlpLogsWorkersSdkLayer(config: LogsLayerConfig = {}) {\n return createLogsSdkLayer({\n serviceName: getServiceName(\"uploadista-workers\"),\n ...config,\n });\n}\n\n// ============================================================================\n// Effect Logger Integration\n// ============================================================================\n\n/**\n * Creates an Effect Logger that forwards logs to OTLP.\n *\n * This logger intercepts Effect.log calls and sends them to the OTLP endpoint\n * with automatic trace correlation and annotation support.\n *\n * @param minSeverity - Minimum severity to export (default: all)\n * @returns Effect Logger that exports to OTLP\n *\n * @example\n * ```typescript\n * import { createOtlpEffectLogger, OtlpLogsNodeSdkLive } from \"@uploadista/observability\";\n *\n * const program = Effect.gen(function* () {\n * yield* Effect.log(\"This will be exported to OTLP\");\n * yield* Effect.logError(\"Errors too!\");\n * }).pipe(\n * Effect.provide(OtlpLogsNodeSdkLive),\n * EffectLogger.withMinimumLogLevel(LogLevel.Debug),\n * );\n * ```\n */\nexport const createOtlpEffectLogger = (minSeverity?: SeverityNumber) =>\n EffectLogger.make<unknown, void>(({ logLevel, message, annotations }) => {\n // This is a synchronous logger - we'll emit to OTEL directly\n // In a real implementation, we'd need to access the OtelLogger from context\n // For now, we'll use the global approach\n\n const severity = mapLogLevelToSeverity(logLevel.label);\n\n // Filter by minimum severity if specified\n if (minSeverity !== undefined && severity < minSeverity) {\n return;\n }\n\n // Get trace context for correlation\n const traceContext = getTraceContext();\n\n // Convert annotations to attributes\n const attributes: Record<string, unknown> = {\n ...traceContext,\n };\n\n // Add annotations as attributes\n for (const [key, value] of annotations) {\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n attributes[key] = value;\n } else {\n attributes[key] = String(value);\n }\n }\n\n // Log to console as fallback (the actual OTLP export happens in the layer)\n // This ensures logs are not lost even if OTLP export fails\n const logFn =\n severity >= SeverityNumber.ERROR\n ? console.error\n : severity >= SeverityNumber.WARN\n ? console.warn\n : console.log;\n\n logFn(`[${logLevel.label}] ${String(message)}`, attributes);\n });\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Emits a log record to OTLP using the OtelLogger from context.\n *\n * @param level - Log level (debug, info, warn, error, fatal)\n * @param message - Log message\n * @param attributes - Optional log attributes\n * @returns Effect that emits the log\n *\n * @example\n * ```typescript\n * yield* emitLog(\"info\", \"Upload completed\", { uploadId: \"123\" });\n * ```\n */\nexport const emitLog = (\n level: \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\",\n message: string,\n attributes?: Record<string, string | number | boolean>,\n) =>\n Effect.gen(function* () {\n const { logger } = yield* OtelLogger;\n const severity = mapLogLevelToSeverity(level);\n const traceContext = getTraceContext();\n\n logger.emit({\n severityNumber: severity,\n severityText: severityToText(severity),\n body: message,\n attributes: {\n ...traceContext,\n ...attributes,\n },\n });\n });\n\n/**\n * Emits a debug log to OTLP.\n */\nexport const logDebug = (\n message: string,\n attributes?: Record<string, string | number | boolean>,\n) => emitLog(\"debug\", message, attributes);\n\n/**\n * Emits an info log to OTLP.\n */\nexport const logInfo = (\n message: string,\n attributes?: Record<string, string | number | boolean>,\n) => emitLog(\"info\", message, attributes);\n\n/**\n * Emits a warning log to OTLP.\n */\nexport const logWarn = (\n message: string,\n attributes?: Record<string, string | number | boolean>,\n) => emitLog(\"warn\", message, attributes);\n\n/**\n * Emits an error log to OTLP.\n */\nexport const logError = (\n message: string,\n attributes?: Record<string, string | number | boolean>,\n) => emitLog(\"error\", message, attributes);\n\n/**\n * Emits a fatal log to OTLP.\n */\nexport const logFatal = (\n message: string,\n attributes?: Record<string, string | number | boolean>,\n) => emitLog(\"fatal\", message, attributes);\n\n// Re-export SeverityNumber for convenience\nexport { SeverityNumber } from \"@opentelemetry/api-logs\";\n","/**\n * OTLP Metrics SDK Layers for Uploadista SDK.\n *\n * This module provides Effect Layers that export metrics to OTLP-compatible\n * backends like Grafana Mimir, Prometheus, Datadog, and others.\n *\n * Configuration is done via standard OpenTelemetry environment variables:\n * - OTEL_EXPORTER_OTLP_ENDPOINT: Base endpoint URL (default: http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: Override endpoint for metrics only\n * - OTEL_EXPORTER_OTLP_HEADERS: Headers for authentication\n * - OTEL_SERVICE_NAME: Service name (default: uploadista)\n * - OTEL_METRICS_EXPORT_INTERVAL: Export interval in ms (default: 60000)\n * - UPLOADISTA_OBSERVABILITY_ENABLED: Set to \"false\" to disable (default: true)\n *\n * @module core/metrics-sdk\n */\n\nimport type { Meter } from \"@opentelemetry/api\";\nimport type { MeterProvider } from \"@opentelemetry/sdk-metrics\";\nimport { Context, Effect, Layer } from \"effect\";\nimport {\n createOtlpMeterProvider,\n getServiceName,\n isOtlpExportEnabled,\n type MetricsSdkConfig,\n} from \"./exporters.js\";\n\n// ============================================================================\n// Metrics Service\n// ============================================================================\n\n/**\n * OpenTelemetry Meter service for recording metrics.\n */\nexport interface OtelMeterService {\n /** The OpenTelemetry Meter instance */\n readonly meter: Meter;\n /** The MeterProvider for shutdown handling */\n readonly provider: MeterProvider;\n}\n\n/**\n * Effect Context tag for the OTEL Meter service.\n */\nexport class OtelMeter extends Context.Tag(\"OtelMeter\")<\n OtelMeter,\n OtelMeterService\n>() {}\n\n// ============================================================================\n// Metrics SDK Layers\n// ============================================================================\n\n/**\n * Creates a Metrics SDK layer with the given configuration.\n *\n * @param config - Metrics SDK configuration\n * @returns Effect Layer providing OtelMeter service\n */\nfunction createMetricsSdkLayer(config: MetricsSdkConfig = {}) {\n return Layer.scoped(\n OtelMeter,\n Effect.gen(function* () {\n // Check if observability is disabled\n if (!isOtlpExportEnabled()) {\n // Return a no-op meter that doesn't export\n const { MeterProvider } = yield* Effect.promise(\n () => import(\"@opentelemetry/sdk-metrics\"),\n );\n const noopProvider = new MeterProvider();\n return {\n meter: noopProvider.getMeter(getServiceName(\"uploadista\")),\n provider: noopProvider,\n };\n }\n\n // Create the OTLP MeterProvider\n const provider = createOtlpMeterProvider(config);\n const serviceName = config.serviceName ?? getServiceName(\"uploadista\");\n const meter = provider.getMeter(serviceName);\n\n // Register shutdown handler\n yield* Effect.addFinalizer(() =>\n Effect.promise(async () => {\n try {\n await provider.shutdown();\n } catch (error) {\n // Log but don't throw on shutdown errors\n console.warn(\"Error shutting down MeterProvider:\", error);\n }\n }),\n );\n\n return { meter, provider };\n }),\n );\n}\n\n/**\n * Node.js OTLP Metrics SDK Layer for production use.\n *\n * Exports metrics to an OTLP-compatible endpoint configured via environment variables.\n *\n * @example\n * ```typescript\n * import { OtlpMetricsNodeSdkLive, OtelMeter } from \"@uploadista/observability\";\n * import { Effect } from \"effect\";\n *\n * const program = Effect.gen(function* () {\n * const { meter } = yield* OtelMeter;\n * const counter = meter.createCounter(\"uploads_total\");\n * counter.add(1, { storage: \"s3\" });\n * }).pipe(Effect.provide(OtlpMetricsNodeSdkLive));\n * ```\n */\nexport const OtlpMetricsNodeSdkLive = createMetricsSdkLayer();\n\n/**\n * Creates a customized OTLP Metrics Node.js SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer\n *\n * @example\n * ```typescript\n * const customMetrics = createOtlpMetricsNodeSdkLayer({\n * serviceName: \"my-upload-service\",\n * exportIntervalMillis: 30000, // Export every 30 seconds\n * });\n * ```\n */\nexport function createOtlpMetricsNodeSdkLayer(config: MetricsSdkConfig = {}) {\n return createMetricsSdkLayer(config);\n}\n\n/**\n * Browser OTLP Metrics SDK Layer for production use.\n *\n * Uses the same OTLP HTTP exporter, suitable for browser environments.\n * Note: Browser environments may have CORS restrictions.\n *\n * @example\n * ```typescript\n * import { OtlpMetricsWebSdkLive } from \"@uploadista/observability\";\n *\n * const program = myEffect.pipe(Effect.provide(OtlpMetricsWebSdkLive));\n * ```\n */\nexport const OtlpMetricsWebSdkLive = createMetricsSdkLayer();\n\n/**\n * Creates a customized OTLP Metrics Web SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer for browser environments\n */\nexport function createOtlpMetricsWebSdkLayer(config: MetricsSdkConfig = {}) {\n return createMetricsSdkLayer(config);\n}\n\n/**\n * Cloudflare Workers OTLP Metrics SDK Layer for production use.\n *\n * Pre-configured with Workers-appropriate defaults.\n *\n * @example\n * ```typescript\n * import { OtlpMetricsWorkersSdkLive } from \"@uploadista/observability\";\n *\n * export default {\n * async fetch(request, env) {\n * const program = handleRequest(request).pipe(\n * Effect.provide(OtlpMetricsWorkersSdkLive)\n * );\n * return Effect.runPromise(program);\n * }\n * };\n * ```\n */\nexport const OtlpMetricsWorkersSdkLive = createMetricsSdkLayer({\n serviceName: getServiceName(\"uploadista-workers\"),\n});\n\n/**\n * Creates a customized OTLP Metrics Workers SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer for Cloudflare Workers\n */\nexport function createOtlpMetricsWorkersSdkLayer(\n config: MetricsSdkConfig = {},\n) {\n return createMetricsSdkLayer({\n serviceName: getServiceName(\"uploadista-workers\"),\n ...config,\n });\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Records a counter metric using the OTEL Meter from context.\n *\n * @param name - Counter name\n * @param value - Value to add (default: 1)\n * @param attributes - Optional metric attributes\n * @returns Effect that records the counter\n *\n * @example\n * ```typescript\n * yield* recordCounter(\"uploads_total\", 1, { storage: \"s3\" });\n * ```\n */\nexport const recordCounter = (\n name: string,\n value = 1,\n attributes?: Record<string, string | number | boolean>,\n) =>\n Effect.gen(function* () {\n const { meter } = yield* OtelMeter;\n const counter = meter.createCounter(name);\n counter.add(value, attributes);\n });\n\n/**\n * Records a histogram metric using the OTEL Meter from context.\n *\n * @param name - Histogram name\n * @param value - Value to record\n * @param attributes - Optional metric attributes\n * @returns Effect that records the histogram\n *\n * @example\n * ```typescript\n * yield* recordHistogram(\"upload_duration_seconds\", 1.5, { storage: \"s3\" });\n * ```\n */\nexport const recordHistogram = (\n name: string,\n value: number,\n attributes?: Record<string, string | number | boolean>,\n) =>\n Effect.gen(function* () {\n const { meter } = yield* OtelMeter;\n const histogram = meter.createHistogram(name);\n histogram.record(value, attributes);\n });\n\n/**\n * Creates an observable gauge that reports the current value.\n *\n * @param name - Gauge name\n * @param callback - Function that returns the current value\n * @param attributes - Optional metric attributes\n * @returns Effect that registers the gauge\n *\n * @example\n * ```typescript\n * let activeUploads = 0;\n * yield* createGauge(\"active_uploads\", () => activeUploads);\n * ```\n */\nexport const createGauge = (\n name: string,\n callback: () => number,\n attributes?: Record<string, string | number | boolean>,\n) =>\n Effect.gen(function* () {\n const { meter } = yield* OtelMeter;\n meter\n .createObservableGauge(name, {\n description: name,\n })\n .addCallback((result) => {\n result.observe(callback(), attributes);\n });\n });\n","import { NodeSdk, WebSdk } from \"@effect/opentelemetry\";\nimport { trace } from \"@opentelemetry/api\";\nimport {\n BatchSpanProcessor,\n ConsoleSpanExporter,\n type SpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\nimport { Context, Effect, Layer, Option, Tracer } from \"effect\";\nimport {\n createOtlpTraceExporter,\n getServiceName,\n isOtlpExportEnabled,\n parseResourceAttributes,\n} from \"./exporters.js\";\nimport type { TraceContext } from \"./types.js\";\n\n// ============================================================================\n// Universal Tracing (Environment-agnostic)\n// ============================================================================\n\n// Generic service tag for tracing context\nexport const TracingService = Context.GenericTag<{ serviceName: string }>(\n \"TracingService\",\n);\n\n// Create a tracing layer using Effect's native tracing (works in all environments)\nexport const createTracingLayer = (options?: { serviceName?: string }) => {\n const serviceName = options?.serviceName ?? \"uploadista-storage\";\n\n // Return a layer that provides tracing service context\n return Layer.succeed(TracingService, { serviceName });\n};\n\n// Storage-specific tracing layers\nexport const createStorageTracingLayer = (storageType: string) =>\n createTracingLayer({\n serviceName: `uploadista-${storageType}-store`,\n });\n\n// Utility to add storage context to spans\nexport const withStorageSpan =\n <A, E, R>(\n operation: string,\n storageType: string,\n attributes?: Record<string, unknown>,\n ) =>\n (effect: Effect.Effect<A, E, R>) =>\n effect.pipe(\n Effect.withSpan(`${storageType}-${operation}`, {\n attributes: {\n \"storage.type\": storageType,\n operation: operation,\n ...attributes,\n },\n }),\n );\n\n// Set up tracing with the OpenTelemetry SDK\nexport const WebSdkLive = WebSdk.layer(() => ({\n resource: { serviceName: \"uploadista-storage\" },\n // Export span data to the console\n spanProcessor: new BatchSpanProcessor(new ConsoleSpanExporter()),\n}));\n\nexport const NodeSdkLive = NodeSdk.layer(() => ({\n resource: { serviceName: \"uploadista-storage\" },\n // Export span data to the console\n spanProcessor: new BatchSpanProcessor(new ConsoleSpanExporter()),\n}));\n\n// Cloudflare Workers SDK (uses WebSdk as base)\nexport const WorkersSdkLive = WebSdk.layer(() => ({\n resource: { serviceName: \"uploadista-storage-workers\" },\n // Export span data to the console in Workers environment\n spanProcessor: new BatchSpanProcessor(new ConsoleSpanExporter()),\n}));\n\n// ============================================================================\n// OTLP Export Layers (Production)\n// ============================================================================\n\n/**\n * Configuration options for OTLP SDK layers.\n */\nexport interface OtlpSdkConfig {\n /** Service name for traces. Defaults to OTEL_SERVICE_NAME or \"uploadista\" */\n serviceName?: string;\n /** Additional resource attributes to include in all spans */\n resourceAttributes?: Record<string, string>;\n /** Maximum queue size for batch processor. Defaults to 512 */\n maxQueueSize?: number;\n /** Maximum export batch size. Defaults to 512 */\n maxExportBatchSize?: number;\n /** Schedule delay in milliseconds. Defaults to 5000 */\n scheduledDelayMillis?: number;\n /** Export timeout in milliseconds. Defaults to 5000 */\n exportTimeoutMillis?: number;\n}\n\n/**\n * Creates a BatchSpanProcessor with OTLP exporter and graceful degradation.\n *\n * The processor is configured with:\n * - Configurable queue limits to prevent memory issues\n * - Export timeouts to prevent blocking\n * - Error handling that drops data rather than failing requests\n *\n * @param config - Optional configuration\n * @returns Configured BatchSpanProcessor\n */\nfunction createOtlpSpanProcessor(config: OtlpSdkConfig = {}): SpanProcessor {\n const exporter = createOtlpTraceExporter();\n\n return new BatchSpanProcessor(exporter, {\n maxQueueSize: config.maxQueueSize ?? 512,\n maxExportBatchSize: config.maxExportBatchSize ?? 512,\n scheduledDelayMillis: config.scheduledDelayMillis ?? 5000,\n // Default to 30 seconds to accommodate cloud endpoints like Grafana Cloud\n exportTimeoutMillis: config.exportTimeoutMillis ?? 30000,\n });\n}\n\n/**\n * Creates resource configuration from environment and config.\n */\nfunction createResourceConfig(config: OtlpSdkConfig = {}): {\n serviceName: string;\n [key: string]: string;\n} {\n const serviceName = config.serviceName ?? getServiceName(\"uploadista\");\n const envAttributes = parseResourceAttributes();\n const configAttributes = config.resourceAttributes ?? {};\n\n return {\n serviceName,\n ...envAttributes,\n ...configAttributes,\n };\n}\n\n/**\n * Node.js OTLP SDK Layer for production use.\n *\n * Exports traces to an OTLP-compatible endpoint configured via environment variables:\n * - OTEL_EXPORTER_OTLP_ENDPOINT: Base endpoint (default: http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_HEADERS: Authentication headers\n * - OTEL_SERVICE_NAME: Service name (default: uploadista)\n * - OTEL_RESOURCE_ATTRIBUTES: Additional resource attributes\n * - UPLOADISTA_OBSERVABILITY_ENABLED: Set to \"false\" to disable (default: true)\n *\n * @example\n * ```typescript\n * import { OtlpNodeSdkLive } from \"@uploadista/observability\";\n * import { Effect } from \"effect\";\n *\n * // With default environment configuration\n * const program = myEffect.pipe(Effect.provide(OtlpNodeSdkLive));\n *\n * // Run with:\n * // OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318\n * // OTEL_SERVICE_NAME=my-upload-service\n * ```\n */\nexport const OtlpNodeSdkLive = NodeSdk.layer(() => {\n // Check if observability is disabled\n if (!isOtlpExportEnabled()) {\n // Return no-op configuration (no span processor means no export)\n return {\n resource: createResourceConfig(),\n };\n }\n\n return {\n resource: createResourceConfig(),\n spanProcessor: createOtlpSpanProcessor(),\n };\n});\n\n/**\n * Creates a customized OTLP Node.js SDK Layer.\n *\n * Use this when you need to customize the SDK configuration beyond\n * what environment variables provide.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer\n *\n * @example\n * ```typescript\n * const customSdk = createOtlpNodeSdkLayer({\n * serviceName: \"my-custom-service\",\n * resourceAttributes: {\n * \"tenant.id\": \"abc123\",\n * \"deployment.environment\": \"production\"\n * },\n * maxQueueSize: 1024,\n * });\n *\n * const program = myEffect.pipe(Effect.provide(customSdk));\n * ```\n */\nexport function createOtlpNodeSdkLayer(config: OtlpSdkConfig = {}) {\n return NodeSdk.layer(() => {\n if (!isOtlpExportEnabled()) {\n return {\n resource: createResourceConfig(config),\n };\n }\n\n return {\n resource: createResourceConfig(config),\n spanProcessor: createOtlpSpanProcessor(config),\n };\n });\n}\n\n/**\n * Browser OTLP SDK Layer for production use.\n *\n * Similar to OtlpNodeSdkLive but uses fetch API for browser compatibility.\n * Note: Browser environments may have CORS restrictions.\n *\n * @example\n * ```typescript\n * import { OtlpWebSdkLive } from \"@uploadista/observability\";\n *\n * const program = myEffect.pipe(Effect.provide(OtlpWebSdkLive));\n * ```\n */\nexport const OtlpWebSdkLive = WebSdk.layer(() => {\n if (!isOtlpExportEnabled()) {\n return {\n resource: createResourceConfig(),\n };\n }\n\n return {\n resource: createResourceConfig(),\n spanProcessor: createOtlpSpanProcessor(),\n };\n});\n\n/**\n * Creates a customized OTLP Web SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer for browser environments\n */\nexport function createOtlpWebSdkLayer(config: OtlpSdkConfig = {}) {\n return WebSdk.layer(() => {\n if (!isOtlpExportEnabled()) {\n return {\n resource: createResourceConfig(config),\n };\n }\n\n return {\n resource: createResourceConfig(config),\n spanProcessor: createOtlpSpanProcessor(config),\n };\n });\n}\n\n/**\n * Cloudflare Workers OTLP SDK Layer for production use.\n *\n * Uses the Web SDK under the hood with fetch-based export.\n * Suitable for edge computing environments.\n *\n * @example\n * ```typescript\n * import { OtlpWorkersSdkLive } from \"@uploadista/observability\";\n *\n * export default {\n * async fetch(request, env) {\n * const program = handleRequest(request).pipe(\n * Effect.provide(OtlpWorkersSdkLive)\n * );\n * return Effect.runPromise(program);\n * }\n * };\n * ```\n */\nexport const OtlpWorkersSdkLive = WebSdk.layer(() => {\n const config: OtlpSdkConfig = {\n serviceName: getServiceName(\"uploadista-workers\"),\n };\n\n if (!isOtlpExportEnabled()) {\n return {\n resource: createResourceConfig(config),\n };\n }\n\n return {\n resource: createResourceConfig(config),\n spanProcessor: createOtlpSpanProcessor(config),\n };\n});\n\n/**\n * Creates a customized OTLP Workers SDK Layer.\n *\n * @param config - Custom configuration options\n * @returns Configured Effect Layer for Cloudflare Workers\n */\nexport function createOtlpWorkersSdkLayer(config: OtlpSdkConfig = {}) {\n return WebSdk.layer(() => {\n const effectiveConfig = {\n serviceName: getServiceName(\"uploadista-workers\"),\n ...config,\n };\n\n if (!isOtlpExportEnabled()) {\n return {\n resource: createResourceConfig(effectiveConfig),\n };\n }\n\n return {\n resource: createResourceConfig(effectiveConfig),\n spanProcessor: createOtlpSpanProcessor(effectiveConfig),\n };\n });\n}\n\n// ============================================================================\n// Distributed Tracing Context Utilities\n// ============================================================================\n\n/**\n * @deprecated Use `captureTraceContextEffect` instead. This synchronous function\n * uses OpenTelemetry's `trace.getActiveSpan()` which may not be synchronized\n * with Effect's span context when using @effect/opentelemetry.\n *\n * @returns TraceContext if there's an active OpenTelemetry span, undefined otherwise\n */\nexport function captureTraceContext(): TraceContext | undefined {\n const currentSpan = trace.getActiveSpan();\n if (!currentSpan) {\n return undefined;\n }\n\n const spanContext = currentSpan.spanContext();\n return {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n traceFlags: spanContext.traceFlags,\n };\n}\n\n/**\n * Captures the current Effect trace context for distributed tracing.\n *\n * Uses Effect's `currentSpan` to get the active span, which is more reliable\n * than OpenTelemetry's `trace.getActiveSpan()` when using @effect/opentelemetry\n * because Effect manages its own span context that may not be synchronized\n * with OpenTelemetry's global context.\n *\n * Use this to save the trace context (traceId, spanId, traceFlags) for later\n * use in distributed tracing. The captured context can be stored alongside\n * data (e.g., in KV store with upload metadata) and restored later using\n * `createExternalSpan` and passing it to `Effect.withSpan`'s `parent` option.\n *\n * @returns Effect yielding TraceContext if there's an active span, undefined otherwise\n *\n * @example\n * ```typescript\n * // Capture context during upload creation\n * const createUpload = Effect.gen(function* () {\n * const traceContext = yield* captureTraceContextEffect;\n *\n * const file: UploadFile = {\n * id: uploadId,\n * traceContext, // Store for later\n * // ...\n * };\n * yield* kvStore.set(uploadId, file);\n * }).pipe(Effect.withSpan(\"upload-create\", { ... }));\n * ```\n */\nexport const captureTraceContextEffect: Effect.Effect<\n TraceContext | undefined\n> = Effect.gen(function* () {\n const spanOption = yield* Effect.currentSpan.pipe(Effect.option);\n return Option.match(spanOption, {\n onNone: () => undefined,\n onSome: (span) => ({\n traceId: span.traceId,\n spanId: span.spanId,\n traceFlags: span.sampled ? 1 : 0,\n }),\n });\n});\n\n/**\n * Creates an ExternalSpan from a stored trace context.\n *\n * Use this to create a parent span reference that can be passed to\n * `Effect.withSpan`'s `parent` option for distributed tracing.\n *\n * **Important:** The parent must be passed directly to `Effect.withSpan`'s\n * options, not provided as a service afterward.\n *\n * @param traceContext - Previously captured trace context\n * @returns ExternalSpan that can be used as a parent in Effect.withSpan\n *\n * @example\n * ```typescript\n * // Create parent span from stored trace context\n * const parentSpan = file.traceContext\n * ? createExternalSpan(file.traceContext)\n * : undefined;\n *\n * // Pass parent directly to withSpan\n * const chunkEffect = Effect.gen(function* () {\n * // ... chunk upload logic\n * }).pipe(\n * Effect.withSpan(\"upload-chunk\", {\n * attributes: { ... },\n * parent: parentSpan, // Link to original trace\n * })\n * );\n * ```\n */\nexport function createExternalSpan(traceContext: TraceContext) {\n return Tracer.externalSpan({\n traceId: traceContext.traceId,\n spanId: traceContext.spanId,\n sampled: traceContext.traceFlags === 1,\n });\n}\n\n/**\n * @deprecated Use `createExternalSpan` instead and pass the result to\n * `Effect.withSpan`'s `parent` option directly. This function doesn't\n * work correctly because Effect.withSpan reads the parent at construction\n * time, not from the provided service.\n *\n * @example\n * ```typescript\n * // Instead of:\n * withParentContext(traceContext)(effect.pipe(Effect.withSpan(...)))\n *\n * // Do this:\n * const parent = createExternalSpan(traceContext);\n * effect.pipe(Effect.withSpan(\"name\", { parent }))\n * ```\n */\nexport function withParentContext(traceContext: TraceContext) {\n return <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> => {\n const externalSpan = Tracer.externalSpan({\n traceId: traceContext.traceId,\n spanId: traceContext.spanId,\n sampled: traceContext.traceFlags === 1,\n });\n\n return effect.pipe(Effect.provideService(Tracer.ParentSpan, externalSpan));\n };\n}\n\n/**\n * Checks if there's an active trace context.\n *\n * Useful for conditional logic based on whether tracing is active.\n *\n * @returns true if there's an active span with valid trace context\n *\n * @example\n * ```typescript\n * if (hasActiveTraceContext()) {\n * console.log(\"Tracing is active\");\n * }\n * ```\n */\nexport function hasActiveTraceContext(): boolean {\n const span = trace.getActiveSpan();\n if (!span) return false;\n\n const ctx = span.spanContext();\n // Check if the trace ID is valid (not all zeros)\n return ctx.traceId !== \"00000000000000000000000000000000\";\n}\n","/**\n * Full Observability SDK Layers for Uploadista SDK.\n *\n * This module provides combined Effect Layers that enable all three pillars\n * of observability (Traces, Metrics, Logs) with a single import.\n *\n * Configuration is done via standard OpenTelemetry environment variables:\n * - OTEL_EXPORTER_OTLP_ENDPOINT: Base endpoint URL (default: http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_HEADERS: Headers for authentication\n * - OTEL_SERVICE_NAME: Service name (default: uploadista)\n * - UPLOADISTA_OBSERVABILITY_ENABLED: Set to \"false\" to disable (default: true)\n *\n * @module core/full-observability\n */\n\nimport { Effect, Layer } from \"effect\";\nimport type { MetricsSdkConfig } from \"./exporters.js\";\nimport { getServiceName } from \"./exporters.js\";\nimport {\n createOtlpLogsNodeSdkLayer,\n createOtlpLogsWebSdkLayer,\n createOtlpLogsWorkersSdkLayer,\n type LogsLayerConfig,\n OtlpLogsNodeSdkLive,\n OtlpLogsWebSdkLive,\n OtlpLogsWorkersSdkLive,\n} from \"./logs-sdk.js\";\nimport {\n createOtlpMetricsNodeSdkLayer,\n createOtlpMetricsWebSdkLayer,\n createOtlpMetricsWorkersSdkLayer,\n OtlpMetricsNodeSdkLive,\n OtlpMetricsWebSdkLive,\n OtlpMetricsWorkersSdkLive,\n} from \"./metrics-sdk.js\";\nimport {\n createOtlpNodeSdkLayer,\n createOtlpWebSdkLayer,\n createOtlpWorkersSdkLayer,\n OtlpNodeSdkLive,\n type OtlpSdkConfig,\n OtlpWebSdkLive,\n OtlpWorkersSdkLive,\n} from \"./tracing.js\";\n\n// ============================================================================\n// Combined Configuration\n// ============================================================================\n\n/**\n * Configuration for full observability SDK.\n */\nexport interface FullObservabilityConfig {\n /** Service name for all telemetry. Defaults to OTEL_SERVICE_NAME or \"uploadista\" */\n serviceName?: string;\n /** Additional resource attributes */\n resourceAttributes?: Record<string, string>;\n /** Traces-specific configuration */\n traces?: Omit<OtlpSdkConfig, \"serviceName\" | \"resourceAttributes\">;\n /** Metrics-specific configuration */\n metrics?: Omit<MetricsSdkConfig, \"serviceName\">;\n /** Logs-specific configuration */\n logs?: Omit<LogsLayerConfig, \"serviceName\">;\n}\n\n// ============================================================================\n// Combined SDK Layers\n// ============================================================================\n\n/**\n * Node.js Full Observability SDK Layer.\n *\n * Combines traces, metrics, and logs export into a single layer.\n * Use this for easy setup of complete observability.\n *\n * @example\n * ```typescript\n * import { OtlpFullObservabilityNodeSdkLive } from \"@uploadista/observability\";\n * import { Effect } from \"effect\";\n *\n * const program = Effect.gen(function* () {\n * // All three pillars are now active:\n * // - Traces via Effect.withSpan\n * // - Metrics via OtelMeter\n * // - Logs via OtelLogger\n * yield* Effect.log(\"This goes to OTLP!\");\n * }).pipe(Effect.provide(OtlpFullObservabilityNodeSdkLive));\n * ```\n */\nexport const OtlpFullObservabilityNodeSdkLive = Layer.mergeAll(\n OtlpNodeSdkLive,\n OtlpMetricsNodeSdkLive,\n OtlpLogsNodeSdkLive,\n);\n\n/**\n * Creates a customized Full Observability Node.js SDK Layer.\n *\n * @param config - Configuration for all three pillars\n * @returns Combined Effect Layer\n *\n * @example\n * ```typescript\n * const customObservability = createOtlpFullObservabilityNodeSdkLayer({\n * serviceName: \"my-upload-service\",\n * traces: { maxQueueSize: 1024 },\n * metrics: { exportIntervalMillis: 30000 },\n * logs: { minSeverity: SeverityNumber.WARN },\n * });\n * ```\n */\nexport function createOtlpFullObservabilityNodeSdkLayer(\n config: FullObservabilityConfig = {},\n) {\n const serviceName = config.serviceName ?? getServiceName(\"uploadista\");\n const resourceAttributes = config.resourceAttributes;\n\n return Layer.mergeAll(\n createOtlpNodeSdkLayer({\n serviceName,\n resourceAttributes,\n ...config.traces,\n }),\n createOtlpMetricsNodeSdkLayer({\n serviceName,\n ...config.metrics,\n }),\n createOtlpLogsNodeSdkLayer({\n serviceName,\n ...config.logs,\n }),\n );\n}\n\n/**\n * Browser Full Observability SDK Layer.\n *\n * Combines traces, metrics, and logs export for browser environments.\n *\n * @example\n * ```typescript\n * import { OtlpFullObservabilityWebSdkLive } from \"@uploadista/observability\";\n *\n * const program = myEffect.pipe(Effect.provide(OtlpFullObservabilityWebSdkLive));\n * ```\n */\nexport const OtlpFullObservabilityWebSdkLive = Layer.mergeAll(\n OtlpWebSdkLive,\n OtlpMetricsWebSdkLive,\n OtlpLogsWebSdkLive,\n);\n\n/**\n * Creates a customized Full Observability Web SDK Layer.\n *\n * @param config - Configuration for all three pillars\n * @returns Combined Effect Layer for browser environments\n */\nexport function createOtlpFullObservabilityWebSdkLayer(\n config: FullObservabilityConfig = {},\n) {\n const serviceName = config.serviceName ?? getServiceName(\"uploadista\");\n const resourceAttributes = config.resourceAttributes;\n\n return Layer.mergeAll(\n createOtlpWebSdkLayer({\n serviceName,\n resourceAttributes,\n ...config.traces,\n }),\n createOtlpMetricsWebSdkLayer({\n serviceName,\n ...config.metrics,\n }),\n createOtlpLogsWebSdkLayer({\n serviceName,\n ...config.logs,\n }),\n );\n}\n\n/**\n * Cloudflare Workers Full Observability SDK Layer.\n *\n * Combines traces, metrics, and logs export for Workers environments.\n *\n * @example\n * ```typescript\n * import { OtlpFullObservabilityWorkersSdkLive } from \"@uploadista/observability\";\n *\n * export default {\n * async fetch(request, env) {\n * const program = handleRequest(request).pipe(\n * Effect.provide(OtlpFullObservabilityWorkersSdkLive)\n * );\n * return Effect.runPromise(program);\n * }\n * };\n * ```\n */\nexport const OtlpFullObservabilityWorkersSdkLive = Layer.mergeAll(\n OtlpWorkersSdkLive,\n OtlpMetricsWorkersSdkLive,\n OtlpLogsWorkersSdkLive,\n);\n\n/**\n * Creates a customized Full Observability Workers SDK Layer.\n *\n * @param config - Configuration for all three pillars\n * @returns Combined Effect Layer for Cloudflare Workers\n */\nexport function createOtlpFullObservabilityWorkersSdkLayer(\n config: FullObservabilityConfig = {},\n) {\n const serviceName =\n config.serviceName ?? getServiceName(\"uploadista-workers\");\n const resourceAttributes = config.resourceAttributes;\n\n return Layer.mergeAll(\n createOtlpWorkersSdkLayer({\n serviceName,\n resourceAttributes,\n ...config.traces,\n }),\n createOtlpMetricsWorkersSdkLayer({\n serviceName,\n ...config.metrics,\n }),\n createOtlpLogsWorkersSdkLayer({\n serviceName,\n ...config.logs,\n }),\n );\n}\n\n// ============================================================================\n// Auto-Detection Layer\n// ============================================================================\n\n/**\n * Runtime environment types.\n */\nexport type Environment = \"node\" | \"web\" | \"workers\";\n\n/**\n * Detects the current runtime environment.\n *\n * @returns The detected environment\n */\nexport function detectEnvironment(): Environment {\n // Check for Node.js\n if (typeof process !== \"undefined\" && process.versions?.node) {\n return \"node\";\n }\n\n // Check for browser (has navigator and window)\n if (typeof navigator !== \"undefined\" && typeof window !== \"undefined\") {\n return \"web\";\n }\n\n // Default to workers (Cloudflare Workers, Deno Deploy, etc.)\n return \"workers\";\n}\n\n/**\n * Auto-detecting Full Observability SDK Layer.\n *\n * Automatically selects the appropriate layer based on runtime environment.\n *\n * @example\n * ```typescript\n * import { OtlpAutoSdkLive } from \"@uploadista/observability\";\n *\n * // Works in Node.js, Browser, or Workers automatically\n * const program = myEffect.pipe(Effect.provide(OtlpAutoSdkLive));\n * ```\n */\nexport const OtlpAutoSdkLive = Layer.unwrapEffect(\n Effect.sync(() => {\n const env = detectEnvironment();\n switch (env) {\n case \"node\":\n return OtlpFullObservabilityNodeSdkLive;\n case \"web\":\n return OtlpFullObservabilityWebSdkLive;\n case \"workers\":\n return OtlpFullObservabilityWorkersSdkLive;\n }\n }),\n);\n\n/**\n * Creates an auto-detecting Full Observability SDK Layer with custom config.\n *\n * @param config - Configuration for all three pillars\n * @returns Auto-detecting combined Effect Layer\n */\nexport function createOtlpAutoSdkLayer(config: FullObservabilityConfig = {}) {\n return Layer.unwrapEffect(\n Effect.sync(() => {\n const env = detectEnvironment();\n switch (env) {\n case \"node\":\n return createOtlpFullObservabilityNodeSdkLayer(config);\n case \"web\":\n return createOtlpFullObservabilityWebSdkLayer(config);\n case \"workers\":\n return createOtlpFullObservabilityWorkersSdkLayer(config);\n }\n }),\n );\n}\n","import { Context, Effect, Layer, Option } from \"effect\";\nimport type { StorageMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Observability Layer Interfaces\n// ============================================================================\n\n/**\n * Core observability service providing tracing, metrics, and logging capabilities\n */\nexport interface ObservabilityService {\n readonly serviceName: string;\n readonly enabled: boolean;\n}\n\n/**\n * Observability service tag for Effect Context\n */\nexport class Observability extends Context.Tag(\"Observability\")<\n Observability,\n ObservabilityService\n>() {}\n\n/**\n * Storage observability service extending base observability with storage-specific metrics\n */\nexport interface StorageObservabilityService extends ObservabilityService {\n readonly storageType: string;\n readonly metrics: StorageMetrics;\n}\n\n/**\n * Storage observability service tag\n */\nexport class StorageObservability extends Context.Tag(\"StorageObservability\")<\n StorageObservability,\n StorageObservabilityService\n>() {}\n\n/**\n * Upload observability service for upload-specific operations\n */\nexport interface UploadObservabilityService extends ObservabilityService {\n readonly metrics: {\n uploadCreated: Effect.Effect<void>;\n uploadCompleted: Effect.Effect<void>;\n uploadFailed: Effect.Effect<void>;\n chunkUploaded: Effect.Effect<void>;\n };\n}\n\n/**\n * Upload observability service tag\n */\nexport class UploadObservability extends Context.Tag(\"UploadObservability\")<\n UploadObservability,\n UploadObservabilityService\n>() {}\n\n/**\n * Flow observability service for flow execution operations\n */\nexport interface FlowObservabilityService extends ObservabilityService {\n readonly metrics: {\n flowStarted: Effect.Effect<void>;\n flowCompleted: Effect.Effect<void>;\n flowFailed: Effect.Effect<void>;\n nodeExecuted: Effect.Effect<void>;\n };\n}\n\n/**\n * Flow observability service tag\n */\nexport class FlowObservability extends Context.Tag(\"FlowObservability\")<\n FlowObservability,\n FlowObservabilityService\n>() {}\n\n// ============================================================================\n// Layer Factories\n// ============================================================================\n\n/**\n * Create a base observability layer\n */\nexport const makeObservabilityLayer = (\n serviceName: string,\n enabled = true,\n): Layer.Layer<Observability> =>\n Layer.succeed(Observability, {\n serviceName,\n enabled,\n });\n\n/**\n * Create a storage observability layer\n */\nexport const makeStorageObservabilityLayer = (\n storageType: string,\n metrics: StorageMetrics,\n enabled = true,\n): Layer.Layer<StorageObservability> =>\n Layer.succeed(StorageObservability, {\n serviceName: `uploadista-${storageType}-store`,\n storageType,\n metrics,\n enabled,\n });\n\n/**\n * Create an upload observability layer\n */\nexport const makeUploadObservabilityLayer = (\n enabled = true,\n): Layer.Layer<UploadObservability> =>\n Layer.succeed(UploadObservability, {\n serviceName: \"uploadista-upload-server\",\n enabled,\n metrics: {\n uploadCreated: Effect.void,\n uploadCompleted: Effect.void,\n uploadFailed: Effect.void,\n chunkUploaded: Effect.void,\n },\n });\n\n/**\n * Create a flow observability layer\n */\nexport const makeFlowObservabilityLayer = (\n enabled = true,\n): Layer.Layer<FlowObservability> =>\n Layer.succeed(FlowObservability, {\n serviceName: \"uploadista-flow-engine\",\n enabled,\n metrics: {\n flowStarted: Effect.void,\n flowCompleted: Effect.void,\n flowFailed: Effect.void,\n nodeExecuted: Effect.void,\n },\n });\n\n// ============================================================================\n// No-op Layers (for testing and opt-out)\n// ============================================================================\n\n/**\n * No-op observability layer (disabled)\n */\nexport const ObservabilityDisabled = makeObservabilityLayer(\n \"uploadista-disabled\",\n false,\n);\n\n/**\n * No-op storage observability layer\n */\nexport const StorageObservabilityDisabled = (storageType: string) =>\n makeStorageObservabilityLayer(\n storageType,\n {} as StorageMetrics, // No-op metrics\n false,\n );\n\n/**\n * No-op upload observability layer\n */\nexport const UploadObservabilityDisabled = makeUploadObservabilityLayer(false);\n\n/**\n * No-op flow observability layer\n */\nexport const FlowObservabilityDisabled = makeFlowObservabilityLayer(false);\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Check if observability is enabled in the current context\n */\nexport const isObservabilityEnabled = Effect.gen(function* () {\n const observability = yield* Effect.serviceOption(Observability);\n return Option.match(observability, {\n onNone: () => false,\n onSome: (obs) => obs.enabled,\n });\n});\n\n/**\n * Execute an effect only if observability is enabled\n */\nexport const whenObservabilityEnabled = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<Option.Option<A>, E, R | Observability> =>\n Effect.gen(function* () {\n const enabled = yield* isObservabilityEnabled;\n if (enabled) {\n const result = yield* effect;\n return Option.some(result);\n }\n return Option.none();\n });\n","import { Effect } from \"effect\";\n\n// ============================================================================\n// Enhanced Logging Helpers (Storage-agnostic)\n// ============================================================================\n\nexport const logWithContext = (\n message: string,\n context: Record<string, unknown>,\n) => Effect.log(message).pipe(Effect.annotateLogs(context));\n\nexport const logUploadProgress = (\n storageType: string,\n uploadId: string,\n progress: {\n uploadedBytes: number;\n totalBytes: number;\n partNumber?: number;\n speed?: number;\n },\n) =>\n logWithContext(\"Upload progress\", {\n storage_type: storageType,\n upload_id: uploadId,\n uploaded_bytes: progress.uploadedBytes,\n total_bytes: progress.totalBytes,\n progress_percentage: Math.round(\n (progress.uploadedBytes / progress.totalBytes) * 100,\n ),\n ...(progress.partNumber && { part_number: progress.partNumber }),\n ...(progress.speed && { upload_speed_bps: progress.speed }),\n });\n\nexport const logStorageOperation = (\n storageType: string,\n operation: string,\n uploadId: string,\n metadata?: Record<string, unknown>,\n) =>\n logWithContext(`${storageType.toUpperCase()} ${operation}`, {\n storage_type: storageType,\n operation,\n upload_id: uploadId,\n ...metadata,\n });\n\nexport const logUploadCompletion = (\n storageType: string,\n uploadId: string,\n metrics: {\n fileSize: number;\n totalDurationMs: number;\n partsCount?: number;\n averagePartSize?: number;\n throughputBps?: number;\n retryCount?: number;\n },\n) => {\n const throughputMBps = metrics.throughputBps\n ? metrics.throughputBps / (1024 * 1024)\n : 0;\n\n return logWithContext(`${storageType.toUpperCase()} upload completed`, {\n storage_type: storageType,\n upload_id: uploadId,\n file_size_bytes: metrics.fileSize,\n file_size_mb: Math.round((metrics.fileSize / (1024 * 1024)) * 100) / 100,\n total_duration_ms: metrics.totalDurationMs,\n total_duration_seconds:\n Math.round((metrics.totalDurationMs / 1000) * 100) / 100,\n throughput_bps: metrics.throughputBps,\n throughput_mbps: Math.round(throughputMBps * 100) / 100,\n ...(metrics.partsCount && { parts_count: metrics.partsCount }),\n ...(metrics.averagePartSize && {\n average_part_size_bytes: metrics.averagePartSize,\n average_part_size_mb:\n Math.round((metrics.averagePartSize / (1024 * 1024)) * 100) / 100,\n }),\n ...(metrics.retryCount && { retry_count: metrics.retryCount }),\n });\n};\n","import { Metric, MetricBoundaries } from \"effect\";\n\n// ============================================================================\n// Core Storage Metrics (reusable across all storage types)\n// ============================================================================\n\n// Counter metrics\nexport const createUploadMetrics = (storageType: string) => ({\n uploadRequestsTotal: Metric.counter(`${storageType}_upload_requests_total`, {\n description: `Total number of upload requests for ${storageType}`,\n }),\n\n uploadPartsTotal: Metric.counter(`${storageType}_upload_parts_total`, {\n description: `Total number of individual parts uploaded for ${storageType}`,\n }),\n\n uploadSuccessTotal: Metric.counter(`${storageType}_upload_success_total`, {\n description: `Total number of successful uploads for ${storageType}`,\n }),\n\n uploadErrorsTotal: Metric.counter(`${storageType}_upload_errors_total`, {\n description: `Total number of upload errors for ${storageType}`,\n }),\n\n apiCallsTotal: Metric.counter(`${storageType}_api_calls_total`, {\n description: `Total number of API calls for ${storageType}`,\n }),\n});\n\n// Histogram metrics for timing and sizes (reusable)\nexport const createUploadHistograms = (storageType: string) => ({\n uploadDurationHistogram: Metric.histogram(\n `${storageType}_upload_duration_seconds`,\n MetricBoundaries.exponential({\n start: 0.01, // 10ms\n factor: 2,\n count: 20, // Up to ~10 seconds\n }),\n `Duration of upload operations in seconds for ${storageType}`,\n ),\n\n partUploadDurationHistogram: Metric.histogram(\n `${storageType}_part_upload_duration_seconds`,\n MetricBoundaries.exponential({\n start: 0.001, // 1ms\n factor: 2,\n count: 15, // Up to ~32 seconds\n }),\n `Duration of individual part uploads in seconds for ${storageType}`,\n ),\n\n fileSizeHistogram: Metric.histogram(\n `${storageType}_file_size_bytes`,\n MetricBoundaries.exponential({\n start: 1024, // 1KB\n factor: 2,\n count: 25, // Up to ~33GB\n }),\n `Size of uploaded files in bytes for ${storageType}`,\n ),\n\n partSizeHistogram: Metric.histogram(\n `${storageType}_part_size_bytes`,\n MetricBoundaries.linear({\n start: 5_242_880, // 5MB (minimum part size)\n width: 1_048_576, // 1MB increments\n count: 20, // Up to ~25MB\n }),\n `Size of upload parts in bytes for ${storageType}`,\n ),\n});\n\n// Gauge metrics for current state (reusable)\nexport const createUploadGauges = (storageType: string) => ({\n activeUploadsGauge: Metric.gauge(`${storageType}_active_uploads`, {\n description: `Number of currently active uploads for ${storageType}`,\n }),\n\n uploadThroughputGauge: Metric.gauge(\n `${storageType}_upload_throughput_bytes_per_second`,\n {\n description: `Current upload throughput in bytes per second for ${storageType}`,\n },\n ),\n});\n\n// Summary metrics for percentiles (reusable)\nexport const createUploadSummaries = (storageType: string) => ({\n uploadLatencySummary: Metric.summary({\n name: `${storageType}_upload_latency_seconds`,\n maxAge: \"10 minutes\",\n maxSize: 1000,\n error: 0.01,\n quantiles: [0.5, 0.9, 0.95, 0.99],\n description: `Upload latency percentiles for ${storageType}`,\n }),\n});\n\n// Combined metrics factory\nexport const createStorageMetrics = (storageType: string) => ({\n ...createUploadMetrics(storageType),\n ...createUploadHistograms(storageType),\n ...createUploadGauges(storageType),\n ...createUploadSummaries(storageType),\n});\n\n// Type for storage metrics\nexport type StorageMetrics = ReturnType<typeof createStorageMetrics>;\n","import { Effect, Layer, Metric } from \"effect\";\nimport type {\n FlowObservabilityService,\n StorageObservabilityService,\n UploadObservabilityService,\n} from \"./layers.js\";\nimport {\n FlowObservability,\n StorageObservability,\n UploadObservability,\n} from \"./layers.js\";\nimport { createStorageMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Test Observability Layers\n// ============================================================================\n\n/**\n * Mock storage observability for testing\n */\nexport const makeTestStorageObservability = (\n storageType: string,\n): Layer.Layer<StorageObservability> => {\n const metrics = createStorageMetrics(storageType);\n const service: StorageObservabilityService = {\n serviceName: `test-${storageType}-store`,\n storageType,\n metrics,\n enabled: true,\n };\n return Layer.succeed(StorageObservability, service);\n};\n\n/**\n * Mock upload observability for testing\n */\nexport const makeTestUploadObservability =\n (): Layer.Layer<UploadObservability> => {\n const service: UploadObservabilityService = {\n serviceName: \"test-upload-server\",\n enabled: true,\n metrics: {\n uploadCreated: Effect.void,\n uploadCompleted: Effect.void,\n uploadFailed: Effect.void,\n chunkUploaded: Effect.void,\n },\n };\n return Layer.succeed(UploadObservability, service);\n };\n\n/**\n * Mock flow observability for testing\n */\nexport const makeTestFlowObservability = (): Layer.Layer<FlowObservability> => {\n const service: FlowObservabilityService = {\n serviceName: \"test-flow-engine\",\n enabled: true,\n metrics: {\n flowStarted: Effect.void,\n flowCompleted: Effect.void,\n flowFailed: Effect.void,\n nodeExecuted: Effect.void,\n },\n };\n return Layer.succeed(FlowObservability, service);\n};\n\n// ============================================================================\n// Test Utilities\n// ============================================================================\n\n/**\n * Capture metrics snapshot from an effect for testing\n * Note: Metric snapshots are simplified - for full metric testing,\n * use Effect's built-in metric testing utilities\n */\nexport const captureMetrics = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n Effect.gen(function* () {\n const result = yield* effect;\n // Metrics are automatically captured by Effect runtime\n yield* Metric.snapshot;\n return result;\n });\n\n/**\n * Test helper to capture metrics around effect execution\n * This is a simplified version - for production testing,\n * use Effect's metric testing utilities\n */\nexport const withMetricTracking = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n Effect.gen(function* () {\n // Track metric before execution\n yield* Metric.snapshot;\n const result = yield* effect;\n // Track metric after execution\n yield* Metric.snapshot;\n return result;\n });\n\n/**\n * Test fixture for observability testing\n */\nexport interface ObservabilityTestFixture {\n readonly storageObservability: Layer.Layer<StorageObservability>;\n readonly uploadObservability: Layer.Layer<UploadObservability>;\n readonly flowObservability: Layer.Layer<FlowObservability>;\n}\n\n/**\n * Create a complete test fixture with all observability layers\n */\nexport const createTestFixture = (\n storageType = \"test-storage\",\n): ObservabilityTestFixture => ({\n storageObservability: makeTestStorageObservability(storageType),\n uploadObservability: makeTestUploadObservability(),\n flowObservability: makeTestFlowObservability(),\n});\n\n/**\n * Run an effect with test observability layers\n */\nexport const runWithTestObservability = <A, E>(\n effect: Effect.Effect<\n A,\n E,\n StorageObservability | UploadObservability | FlowObservability\n >,\n storageType = \"test-storage\",\n): Effect.Effect<A, E> => {\n const fixture = createTestFixture(storageType);\n return effect.pipe(\n Effect.provide(fixture.storageObservability),\n Effect.provide(fixture.uploadObservability),\n Effect.provide(fixture.flowObservability),\n );\n};\n","import { Effect, Metric } from \"effect\";\nimport type { StorageMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Storage Observability Utility Functions\n// ============================================================================\n\n// Generic upload metrics wrapper\nexport const withUploadMetrics = <A, E, R>(\n metrics: StorageMetrics,\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.tap(() =>\n metrics.uploadRequestsTotal.pipe(Metric.tagged(\"upload_id\", uploadId))(\n Effect.succeed(1),\n ),\n ),\n Effect.tapError(() =>\n metrics.uploadErrorsTotal.pipe(Metric.tagged(\"upload_id\", uploadId))(\n Effect.succeed(1),\n ),\n ),\n Effect.tap(() =>\n metrics.uploadSuccessTotal.pipe(Metric.tagged(\"upload_id\", uploadId))(\n Effect.succeed(1),\n ),\n ),\n );\n\n// Generic API call metrics wrapper\nexport const withApiMetrics = <A, E, R>(\n metrics: StorageMetrics,\n operation: string,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.tap(() =>\n metrics.apiCallsTotal.pipe(Metric.tagged(\"operation\", operation))(\n Effect.succeed(1),\n ),\n ),\n );\n\n// Generic timing metrics wrapper\nexport const withTimingMetrics = <A, E, R>(\n metric: Metric.Metric.Histogram<number>,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n Effect.gen(function* () {\n const startTime = yield* Effect.sync(() => Date.now());\n const result = yield* effect;\n const endTime = yield* Effect.sync(() => Date.now());\n const duration = (endTime - startTime) / 1000; // Convert to seconds\n\n yield* metric(Effect.succeed(duration));\n\n return result;\n });\n\n// File size tracking\nexport const trackFileSize = <A, E, R>(\n metrics: StorageMetrics,\n fileSize: number,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.tap(() => metrics.fileSizeHistogram(Effect.succeed(fileSize))),\n );\n\n// Part size tracking\nexport const trackPartSize = <A, E, R>(\n metrics: StorageMetrics,\n partSize: number,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.tap(() => metrics.partSizeHistogram(Effect.succeed(partSize))),\n );\n\n// Active uploads tracking\nexport const withActiveUploadTracking = <A, E, R>(\n metrics: StorageMetrics,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.tap(() => metrics.activeUploadsGauge(Effect.succeed(1))),\n Effect.ensuring(metrics.activeUploadsGauge(Effect.succeed(-1))),\n );\n\n// Throughput calculation and tracking\nexport const withThroughputTracking = <A, E, R>(\n metrics: StorageMetrics,\n bytes: number,\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> =>\n Effect.gen(function* () {\n const startTime = yield* Effect.sync(() => Date.now());\n const result = yield* effect;\n const endTime = yield* Effect.sync(() => Date.now());\n const durationSeconds = (endTime - startTime) / 1000;\n const throughputBps = durationSeconds > 0 ? bytes / durationSeconds : 0;\n\n yield* metrics.uploadThroughputGauge(Effect.succeed(throughputBps));\n\n return result;\n });\n\n// Combined metrics wrapper for common upload operations\nexport const withStorageOperationMetrics = <A, E, R>(\n metrics: StorageMetrics,\n operation: string,\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n fileSize?: number,\n): Effect.Effect<A, E, R> => {\n let wrappedEffect = effect.pipe(\n (eff) => withApiMetrics(metrics, operation, eff),\n (eff) => withUploadMetrics(metrics, uploadId, eff),\n (eff) => withTimingMetrics(metrics.uploadDurationHistogram, eff),\n (eff) => withActiveUploadTracking(metrics, eff),\n );\n\n if (fileSize !== undefined) {\n wrappedEffect = wrappedEffect.pipe(\n (eff) => trackFileSize(metrics, fileSize, eff),\n (eff) => withThroughputTracking(metrics, fileSize, eff),\n );\n }\n\n return wrappedEffect;\n};\n","import { Metric, MetricBoundaries } from \"effect\";\n\n// ============================================================================\n// Flow Engine Metrics\n// ============================================================================\n\n/**\n * Flow engine metrics for tracking flow execution operations\n */\nexport const createFlowMetrics = () => ({\n // Counter metrics\n flowStartedTotal: Metric.counter(\"flow_started_total\", {\n description: \"Total number of flows started\",\n }),\n\n flowCompletedTotal: Metric.counter(\"flow_completed_total\", {\n description: \"Total number of flows completed successfully\",\n }),\n\n flowFailedTotal: Metric.counter(\"flow_failed_total\", {\n description: \"Total number of flows that failed\",\n }),\n\n flowPausedTotal: Metric.counter(\"flow_paused_total\", {\n description: \"Total number of flows that were paused\",\n }),\n\n flowResumedTotal: Metric.counter(\"flow_resumed_total\", {\n description: \"Total number of flows that were resumed\",\n }),\n\n nodeExecutedTotal: Metric.counter(\"node_executed_total\", {\n description: \"Total number of nodes executed\",\n }),\n\n nodeSuccessTotal: Metric.counter(\"node_success_total\", {\n description: \"Total number of nodes executed successfully\",\n }),\n\n nodeFailedTotal: Metric.counter(\"node_failed_total\", {\n description: \"Total number of nodes that failed\",\n }),\n\n nodeSkippedTotal: Metric.counter(\"node_skipped_total\", {\n description: \"Total number of nodes skipped (conditional)\",\n }),\n\n // Histogram metrics\n flowDurationHistogram: Metric.histogram(\n \"flow_duration_seconds\",\n MetricBoundaries.exponential({\n start: 0.1, // 100ms\n factor: 2,\n count: 20, // Up to ~100 seconds\n }),\n \"Duration of complete flow execution in seconds\",\n ),\n\n nodeDurationHistogram: Metric.histogram(\n \"node_duration_seconds\",\n MetricBoundaries.exponential({\n start: 0.01, // 10ms\n factor: 2,\n count: 18, // Up to ~26 seconds\n }),\n \"Duration of individual node execution in seconds\",\n ),\n\n flowNodeCountHistogram: Metric.histogram(\n \"flow_node_count\",\n MetricBoundaries.linear({\n start: 1,\n width: 5,\n count: 20, // Up to 100 nodes\n }),\n \"Number of nodes in a flow\",\n ),\n\n parallelNodesHistogram: Metric.histogram(\n \"parallel_nodes_count\",\n MetricBoundaries.linear({\n start: 1,\n width: 2,\n count: 15, // Up to 30 parallel nodes\n }),\n \"Number of nodes executed in parallel\",\n ),\n\n // Gauge metrics\n activeFlowsGauge: Metric.gauge(\"active_flows\", {\n description: \"Number of currently active flows\",\n }),\n\n activeNodesGauge: Metric.gauge(\"active_nodes\", {\n description: \"Number of currently executing nodes\",\n }),\n\n pausedFlowsGauge: Metric.gauge(\"paused_flows\", {\n description: \"Number of currently paused flows\",\n }),\n\n // Summary metrics for latency percentiles\n flowLatencySummary: Metric.summary({\n name: \"flow_latency_seconds\",\n maxAge: \"10 minutes\",\n maxSize: 1000,\n error: 0.01,\n quantiles: [0.5, 0.9, 0.95, 0.99],\n description: \"Flow execution latency percentiles\",\n }),\n\n nodeLatencySummary: Metric.summary({\n name: \"node_latency_seconds\",\n maxAge: \"10 minutes\",\n maxSize: 1000,\n error: 0.01,\n quantiles: [0.5, 0.9, 0.95, 0.99],\n description: \"Node execution latency percentiles\",\n }),\n\n // ============================================================================\n // Circuit Breaker Metrics\n // ============================================================================\n\n /** Total number of times circuit breakers opened */\n circuitBreakerOpenTotal: Metric.counter(\"circuit_breaker_open_total\", {\n description:\n \"Total number of times circuit breakers transitioned to open state\",\n }),\n\n /** Total number of times circuit breakers closed */\n circuitBreakerCloseTotal: Metric.counter(\"circuit_breaker_close_total\", {\n description:\n \"Total number of times circuit breakers transitioned to closed state\",\n }),\n\n /** Total number of requests rejected by open circuit breakers */\n circuitBreakerRejectedTotal: Metric.counter(\n \"circuit_breaker_rejected_total\",\n {\n description:\n \"Total number of requests rejected because circuit breaker is open\",\n },\n ),\n\n /** Total number of times circuit breakers transitioned to half-open */\n circuitBreakerHalfOpenTotal: Metric.counter(\n \"circuit_breaker_half_open_total\",\n {\n description:\n \"Total number of times circuit breakers transitioned to half-open state\",\n },\n ),\n\n /** Current state of circuit breakers (0=closed, 1=open, 2=half-open) */\n circuitBreakerStateGauge: Metric.gauge(\"circuit_breaker_state\", {\n description:\n \"Current circuit breaker state (0=closed, 1=open, 2=half-open)\",\n }),\n\n /** Number of failures in circuit breaker sliding window */\n circuitBreakerFailuresGauge: Metric.gauge(\"circuit_breaker_failures\", {\n description:\n \"Number of failures currently in the circuit breaker sliding window\",\n }),\n});\n\n/**\n * Type for flow metrics\n */\nexport type FlowMetrics = ReturnType<typeof createFlowMetrics>;\n\n/**\n * Default flow metrics instance\n */\nexport const flowMetrics = createFlowMetrics();\n","import { Effect, Metric } from \"effect\";\nimport { createFlowMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Flow Error Classification\n// ============================================================================\n\nexport type FlowErrorCategory =\n | \"flow_validation_error\"\n | \"node_execution_error\"\n | \"node_not_found_error\"\n | \"flow_timeout_error\"\n | \"flow_cancelled_error\"\n | \"unknown_flow_error\";\n\n/**\n * Classify flow execution errors\n */\nexport const classifyFlowError = (error: unknown): FlowErrorCategory => {\n if (!error || typeof error !== \"object\") return \"unknown_flow_error\";\n\n const errorCode = \"code\" in error ? error.code : undefined;\n if (!errorCode) return \"unknown_flow_error\";\n\n // Flow-specific error codes\n switch (errorCode) {\n case \"FLOW_VALIDATION_ERROR\":\n case \"FLOW_INVALID_INPUT\":\n case \"FLOW_INVALID_OUTPUT\":\n return \"flow_validation_error\";\n case \"FLOW_NODE_NOT_FOUND\":\n case \"FLOW_EDGE_INVALID\":\n return \"node_not_found_error\";\n case \"FLOW_NODE_EXECUTION_FAILED\":\n case \"FLOW_NODE_ERROR\":\n return \"node_execution_error\";\n case \"FLOW_TIMEOUT\":\n return \"flow_timeout_error\";\n case \"FLOW_CANCELLED\":\n case \"ABORTED\":\n return \"flow_cancelled_error\";\n default:\n return \"unknown_flow_error\";\n }\n};\n\n/**\n * Track flow errors with classification\n */\nexport const trackFlowError = <E>(\n error: E,\n): Effect.Effect<void, never, never> => {\n const metrics = createFlowMetrics();\n const category = classifyFlowError(error);\n\n return Effect.gen(function* () {\n // Increment total failed flows\n yield* Metric.increment(metrics.flowFailedTotal);\n\n // Log error with classification\n yield* Effect.logError(\"Flow execution failed\").pipe(\n Effect.annotateLogs({\n \"error.category\": category,\n \"error.message\": String(error),\n }),\n );\n });\n};\n\n/**\n * Track node errors with classification\n */\nexport const trackNodeError = <E>(\n nodeId: string,\n nodeType: string,\n error: E,\n): Effect.Effect<void, never, never> => {\n const metrics = createFlowMetrics();\n const category = classifyFlowError(error);\n\n return Effect.gen(function* () {\n // Increment node failed counter\n yield* Metric.increment(metrics.nodeFailedTotal);\n\n // Log error with node context\n yield* Effect.logError(\"Node execution failed\").pipe(\n Effect.annotateLogs({\n \"node.id\": nodeId,\n \"node.type\": nodeType,\n \"error.category\": category,\n \"error.message\": String(error),\n }),\n );\n });\n};\n","import { Effect, Layer, Metric } from \"effect\";\nimport {\n FlowObservability,\n makeFlowObservabilityLayer,\n} from \"../core/layers.js\";\nimport { createFlowMetrics, type FlowMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Flow Observability Layer Implementation\n// ============================================================================\n\n/**\n * Create a live flow observability layer with full metrics\n */\nexport const makeFlowObservabilityLive = (\n serviceName = \"uploadista-flow-engine\",\n): Layer.Layer<FlowObservability> => {\n const metrics = createFlowMetrics();\n\n return Layer.succeed(FlowObservability, {\n serviceName,\n enabled: true,\n metrics: {\n flowStarted: Metric.increment(metrics.flowStartedTotal),\n flowCompleted: Metric.increment(metrics.flowCompletedTotal),\n flowFailed: Metric.increment(metrics.flowFailedTotal),\n nodeExecuted: Metric.increment(metrics.nodeExecutedTotal),\n },\n });\n};\n\n/**\n * Default live flow observability layer\n */\nexport const FlowObservabilityLive = makeFlowObservabilityLive();\n\n/**\n * No-op flow observability layer (for testing or disabled observability)\n */\nexport const FlowObservabilityDisabled = makeFlowObservabilityLayer(false);\n\n/**\n * Helper to get flow metrics from context\n */\nexport const getFlowMetrics = Effect.gen(function* () {\n const obs = yield* FlowObservability;\n return obs.metrics;\n});\n\n/**\n * Helper to track flow duration\n */\nexport const withFlowDuration = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> => {\n const metrics = createFlowMetrics();\n return Effect.gen(function* () {\n const startTime = Date.now();\n const result = yield* effect;\n const duration = (Date.now() - startTime) / 1000; // Convert to seconds\n yield* Metric.update(metrics.flowDurationHistogram, duration);\n yield* Metric.update(metrics.flowLatencySummary, duration);\n return result;\n }).pipe(Effect.withSpan(\"flow-execution\"));\n};\n\n/**\n * Helper to track node duration\n * @param nodeId - Unique node identifier\n * @param nodeType - Generic node type (e.g., \"optimize\", \"resize\")\n * @param effect - The effect to track\n * @param nodeTypeId - Optional specific node type ID (e.g., \"optimize-image\", \"resize-video\")\n */\nexport const withNodeDuration = <A, E, R>(\n nodeId: string,\n nodeType: string,\n effect: Effect.Effect<A, E, R>,\n nodeTypeId?: string,\n): Effect.Effect<A, E, R> => {\n const metrics = createFlowMetrics();\n // Use nodeTypeId for span name if available, fallback to nodeType\n const spanName = nodeTypeId ?? nodeType;\n return Effect.gen(function* () {\n const startTime = Date.now();\n const result = yield* effect;\n const duration = (Date.now() - startTime) / 1000; // Convert to seconds\n yield* Metric.update(metrics.nodeDurationHistogram, duration);\n yield* Metric.update(metrics.nodeLatencySummary, duration);\n return result;\n }).pipe(\n Effect.withSpan(`node-${spanName}`, {\n attributes: {\n \"node.id\": nodeId,\n \"node.type\": nodeType,\n \"node.type_id\": nodeTypeId ?? nodeType,\n },\n }),\n );\n};\n\n/**\n * Helper to track active flows\n */\nexport const trackActiveFlow = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> => {\n const metrics = createFlowMetrics();\n return Effect.gen(function* () {\n // Increment active flows\n yield* Metric.increment(metrics.activeFlowsGauge);\n\n // Use acquireUseRelease for proper cleanup\n return yield* Effect.acquireUseRelease(\n Effect.void,\n () => effect,\n () => Metric.set(metrics.activeFlowsGauge, -1),\n );\n });\n};\n\n/**\n * Helper to track active nodes\n */\nexport const trackActiveNode = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> => {\n const metrics = createFlowMetrics();\n return Effect.gen(function* () {\n // Increment active nodes\n yield* Metric.increment(metrics.activeNodesGauge);\n\n // Use acquireUseRelease for proper cleanup\n return yield* Effect.acquireUseRelease(\n Effect.void,\n () => effect,\n () => Metric.set(metrics.activeNodesGauge, -1),\n );\n });\n};\n","import { Effect, Layer } from \"effect\";\nimport type { FlowObservabilityService } from \"../core/layers.js\";\nimport { FlowObservability } from \"../core/layers.js\";\n\n// ============================================================================\n// Test Flow Observability Layers\n// ============================================================================\n\n/**\n * Mock flow observability for testing\n */\nexport const makeTestFlowObservability = (): Layer.Layer<FlowObservability> => {\n const service: FlowObservabilityService = {\n serviceName: \"test-flow-engine\",\n enabled: true,\n metrics: {\n flowStarted: Effect.void,\n flowCompleted: Effect.void,\n flowFailed: Effect.void,\n nodeExecuted: Effect.void,\n },\n };\n return Layer.succeed(FlowObservability, service);\n};\n\n/**\n * Run an effect with test flow observability\n */\nexport const runWithTestFlowObservability = <A, E>(\n effect: Effect.Effect<A, E, FlowObservability>,\n): Effect.Effect<A, E> => {\n return effect.pipe(Effect.provide(makeTestFlowObservability()));\n};\n","import { Effect } from \"effect\";\n\n// ============================================================================\n// Flow Tracing Utilities\n// ============================================================================\n\n/**\n * Wrap an Effect with a flow operation span\n */\nexport const withFlowSpan =\n <A, E, R>(operation: string, attributes?: Record<string, unknown>) =>\n (effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.withSpan(`flow-${operation}`, {\n attributes: {\n \"flow.operation\": operation,\n ...attributes,\n },\n }),\n );\n\n/**\n * Add flow context to the current span\n */\nexport const withFlowContext = (context: {\n flowId?: string;\n flowName?: string;\n jobId?: string;\n nodeCount?: number;\n storageId?: string;\n}) =>\n Effect.annotateCurrentSpan({\n \"flow.id\": context.flowId ?? \"unknown\",\n \"flow.name\": context.flowName ?? \"unknown\",\n \"flow.job_id\": context.jobId ?? \"unknown\",\n \"flow.node_count\": context.nodeCount?.toString() ?? \"0\",\n \"flow.storage_id\": context.storageId ?? \"unknown\",\n });\n\n/**\n * Add node context to the current span\n */\nexport const withNodeContext = (context: {\n nodeId: string;\n nodeType: string;\n nodeName?: string;\n flowId?: string;\n jobId?: string;\n}) =>\n Effect.annotateCurrentSpan({\n \"node.id\": context.nodeId,\n \"node.type\": context.nodeType,\n \"node.name\": context.nodeName ?? \"unknown\",\n \"node.flow_id\": context.flowId ?? \"unknown\",\n \"node.job_id\": context.jobId ?? \"unknown\",\n });\n\n/**\n * Add execution state context to the current span\n */\nexport const withExecutionContext = (context: {\n executionOrder?: string[];\n currentIndex?: number;\n totalNodes?: number;\n parallelCount?: number;\n}) =>\n Effect.annotateCurrentSpan({\n \"execution.order\": context.executionOrder?.join(\",\") ?? \"\",\n \"execution.current_index\": context.currentIndex?.toString() ?? \"0\",\n \"execution.total_nodes\": context.totalNodes?.toString() ?? \"0\",\n \"execution.parallel_count\": context.parallelCount?.toString() ?? \"0\",\n });\n\n// ============================================================================\n// Plugin Operation Tracing\n// ============================================================================\n\n/**\n * Operation domains for plugin-level tracing\n */\nexport type OperationDomain =\n | \"image\"\n | \"video\"\n | \"document\"\n | \"ai\"\n | \"virus-scan\"\n | \"zip\";\n\n/**\n * Wrap an Effect with a plugin operation span\n *\n * @param domain - The operation domain (e.g., \"image\", \"video\", \"document\")\n * @param operation - The specific operation (e.g., \"optimize\", \"transcode\", \"extract-text\")\n * @param attributes - Optional span attributes with operation-specific details\n *\n * @example\n * ```typescript\n * // Image optimization span\n * withOperationSpan(\"image\", \"optimize\", {\n * \"image.format\": \"webp\",\n * \"image.quality\": 80,\n * })(imageService.optimize(inputBytes, params))\n *\n * // Video transcoding span\n * withOperationSpan(\"video\", \"transcode\", {\n * \"video.format\": \"mp4\",\n * \"video.codec\": \"h264\",\n * })(videoService.transcode(inputBytes, params))\n * ```\n */\nexport const withOperationSpan =\n <A, E, R>(\n domain: OperationDomain,\n operation: string,\n attributes?: Record<string, unknown>,\n ) =>\n (effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.withSpan(`${domain}-${operation}`, {\n attributes: {\n \"operation.domain\": domain,\n \"operation.name\": operation,\n ...attributes,\n },\n }),\n );\n\n/**\n * Add operation context to the current span\n */\nexport const withOperationContext = (context: {\n domain: OperationDomain;\n operation: string;\n inputSize?: number;\n outputSize?: number;\n}) =>\n Effect.annotateCurrentSpan({\n \"operation.domain\": context.domain,\n \"operation.name\": context.operation,\n \"operation.input_size\": context.inputSize?.toString() ?? \"unknown\",\n \"operation.output_size\": context.outputSize?.toString() ?? \"unknown\",\n });\n\n// ============================================================================\n// Circuit Breaker Tracing\n// ============================================================================\n\n/**\n * Circuit breaker state for tracing\n */\nexport type CircuitBreakerTracingState = \"closed\" | \"open\" | \"half-open\";\n\n/**\n * Wrap an Effect with a circuit breaker evaluation span\n */\nexport const withCircuitBreakerSpan =\n <A, E, R>(\n nodeType: string,\n state: CircuitBreakerTracingState,\n attributes?: Record<string, unknown>,\n ) =>\n (effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.withSpan(`circuit-breaker-${nodeType}`, {\n attributes: {\n \"circuit_breaker.node_type\": nodeType,\n \"circuit_breaker.state\": state,\n ...attributes,\n },\n }),\n );\n\n/**\n * Add circuit breaker context to the current span\n */\nexport const withCircuitBreakerContext = (context: {\n nodeType: string;\n state: CircuitBreakerTracingState;\n failureCount?: number;\n failureThreshold?: number;\n resetTimeout?: number;\n decision?: \"allowed\" | \"rejected\" | \"fallback\";\n}) =>\n Effect.annotateCurrentSpan({\n \"circuit_breaker.node_type\": context.nodeType,\n \"circuit_breaker.state\": context.state,\n \"circuit_breaker.failure_count\": context.failureCount?.toString() ?? \"0\",\n \"circuit_breaker.failure_threshold\":\n context.failureThreshold?.toString() ?? \"5\",\n \"circuit_breaker.reset_timeout\":\n context.resetTimeout?.toString() ?? \"30000\",\n \"circuit_breaker.decision\": context.decision ?? \"unknown\",\n });\n\n/**\n * Add a circuit breaker state change event to the current span\n */\nexport const annotateCircuitBreakerStateChange = (event: {\n nodeType: string;\n previousState: CircuitBreakerTracingState;\n newState: CircuitBreakerTracingState;\n failureCount?: number;\n timestamp?: number;\n}) =>\n Effect.annotateCurrentSpan({\n \"circuit_breaker.event\": \"state_change\",\n \"circuit_breaker.node_type\": event.nodeType,\n \"circuit_breaker.previous_state\": event.previousState,\n \"circuit_breaker.new_state\": event.newState,\n \"circuit_breaker.failure_count\": event.failureCount?.toString() ?? \"0\",\n \"circuit_breaker.timestamp\":\n event.timestamp?.toString() ?? Date.now().toString(),\n });\n","import { Context, Effect, Layer } from \"effect\";\n\n/**\n * Metrics Recording Service\n *\n * Provides access to metrics recording functionality throughout\n * the upload and flow processing pipeline. The service is provided\n * via Effect Layer and can be accessed using Effect.service().\n */\nexport class MetricsService extends Context.Tag(\"MetricsService\")<\n MetricsService,\n {\n /**\n * Record upload metrics for an organization\n */\n readonly recordUpload: (\n clientId: string,\n bytes: number,\n metadata?: Record<string, unknown>,\n ) => Effect.Effect<void, never>;\n }\n>() {}\n\n/**\n * No-op implementation of MetricsService that does nothing.\n * Used when metrics are disabled or database is not available.\n */\nexport const NoOpMetricsServiceLive: Layer.Layer<MetricsService> =\n Layer.succeed(MetricsService, {\n recordUpload: (_organizationId: string, _bytes: number) => Effect.void,\n });\n","import { type Effect, Layer } from \"effect\";\nimport {\n createStorageErrorTracker,\n type StorageErrorCategory,\n} from \"../core/errors.js\";\nimport {\n logStorageOperation,\n logUploadCompletion,\n logUploadProgress,\n logWithContext,\n} from \"../core/logging.js\";\nimport { createStorageMetrics, type StorageMetrics } from \"../core/metrics.js\";\nimport { createStorageTracingLayer, withStorageSpan } from \"../core/tracing.js\";\nimport {\n withApiMetrics,\n withStorageOperationMetrics,\n withTimingMetrics,\n withUploadMetrics,\n} from \"../core/utilities.js\";\n\n// ============================================================================\n// Azure Blob Storage-Specific Observability\n// ============================================================================\n\nconst STORAGE_TYPE = \"azure\";\n\n// Azure-specific metrics\nexport const azureMetrics = createStorageMetrics(STORAGE_TYPE);\n\n// Azure-specific tracing layer\nexport const AzureTracingLayer = createStorageTracingLayer(STORAGE_TYPE);\n\n// Azure-specific error classification\nconst classifyAzureError = (error: unknown): StorageErrorCategory | null => {\n if (!error || typeof error !== \"object\") return null;\n\n const errorCode =\n \"code\" in error\n ? error.code\n : \"statusCode\" in error\n ? error.statusCode\n : undefined;\n if (!errorCode) return null;\n\n // Azure-specific error codes\n switch (errorCode) {\n case \"BlobNotFound\":\n case \"ContainerNotFound\":\n case \"InvalidBlobOrBlock\":\n return \"client_error\";\n case \"ContainerAlreadyExists\":\n case \"BlobAlreadyExists\":\n return \"client_error\";\n case \"InvalidBlockId\":\n case \"InvalidBlockList\":\n case \"InvalidBlobType\":\n return \"client_error\";\n case \"RequestBodyTooLarge\":\n case \"InvalidHeaderValue\":\n return \"client_error\";\n case \"AuthenticationFailed\":\n case \"InvalidAuthenticationInfo\":\n return \"authentication_error\";\n case \"AccountIsDisabled\":\n return \"authorization_error\";\n case \"InsufficientAccountPermissions\":\n return \"authorization_error\";\n case \"OperationTimedOut\":\n case \"ServerBusy\":\n case \"InternalError\":\n return \"server_error\";\n default:\n // Check for HTTP status codes\n if (typeof errorCode === \"number\") {\n if (errorCode >= 500) return \"server_error\";\n if (errorCode === 429) return \"throttling_error\";\n if (errorCode === 403) return \"authorization_error\";\n if (errorCode === 401) return \"authentication_error\";\n if (errorCode >= 400) return \"client_error\";\n }\n return null; // Fall back to generic classification\n }\n};\n\n// Azure-specific error tracker\nexport const trackAzureError = createStorageErrorTracker(\n STORAGE_TYPE,\n azureMetrics,\n classifyAzureError,\n);\n\n// Azure-specific observability layer\nexport const AzureObservabilityLayer = Layer.mergeAll(\n AzureTracingLayer,\n // Metrics are automatically available through Effect\n);\n\n// ============================================================================\n// Azure Utility Functions\n// ============================================================================\n\nexport const withAzureUploadMetrics = <A, E, R>(\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n) => withUploadMetrics(azureMetrics, uploadId, effect);\n\nexport const withAzureApiMetrics = <A, E, R>(\n operation: string,\n effect: Effect.Effect<A, E, R>,\n) => withApiMetrics(azureMetrics, operation, effect);\n\nexport const withAzureTimingMetrics = withTimingMetrics;\n\nexport const withAzureOperationMetrics = <A, E, R>(\n operation: string,\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n fileSize?: number,\n) =>\n withStorageOperationMetrics(\n azureMetrics,\n operation,\n uploadId,\n effect,\n fileSize,\n );\n\n// Azure-specific span wrapper\nexport const withAzureSpan =\n <A, E, R>(operation: string, attributes?: Record<string, unknown>) =>\n (effect: Effect.Effect<A, E, R>) =>\n withStorageSpan(operation, STORAGE_TYPE, attributes)(effect);\n\n// Azure-specific logging functions\nexport const logAzureOperation = logStorageOperation.bind(null, STORAGE_TYPE);\nexport const logAzureUploadProgress = logUploadProgress.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logAzureUploadCompletion = logUploadCompletion.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logAzureContext = logWithContext;\n\n// Export metrics for external access\nexport const {\n uploadRequestsTotal: azureUploadRequestsTotal,\n uploadPartsTotal: azureUploadPartsTotal,\n uploadSuccessTotal: azureUploadSuccessTotal,\n uploadErrorsTotal: azureUploadErrorsTotal,\n apiCallsTotal: azureApiCallsTotal,\n uploadDurationHistogram: azureUploadDurationHistogram,\n partUploadDurationHistogram: azurePartUploadDurationHistogram,\n fileSizeHistogram: azureFileSizeHistogram,\n partSizeHistogram: azurePartSizeHistogram,\n activeUploadsGauge: azureActiveUploadsGauge,\n uploadThroughputGauge: azureUploadThroughputGauge,\n uploadLatencySummary: azureUploadLatencySummary,\n} = azureMetrics;\n\n// Type exports\nexport type AzureMetrics = StorageMetrics;\n","import { type Effect, Layer } from \"effect\";\nimport {\n createStorageErrorTracker,\n type StorageErrorCategory,\n} from \"../core/errors.js\";\nimport {\n logStorageOperation,\n logUploadCompletion,\n logUploadProgress,\n logWithContext,\n} from \"../core/logging.js\";\nimport { createStorageMetrics, type StorageMetrics } from \"../core/metrics.js\";\nimport { createStorageTracingLayer, withStorageSpan } from \"../core/tracing.js\";\nimport {\n withApiMetrics,\n withStorageOperationMetrics,\n withTimingMetrics,\n withUploadMetrics,\n} from \"../core/utilities.js\";\n\n// ============================================================================\n// Filesystem Storage-Specific Observability\n// ============================================================================\n\nconst STORAGE_TYPE = \"filesystem\";\n\n// Filesystem-specific metrics\nexport const filesystemMetrics = createStorageMetrics(STORAGE_TYPE);\n\n// Filesystem-specific tracing layer\nexport const FilesystemTracingLayer = createStorageTracingLayer(STORAGE_TYPE);\n\n// Filesystem-specific error classification\nconst classifyFilesystemError = (\n error: unknown,\n): StorageErrorCategory | null => {\n if (!error || typeof error !== \"object\") return null;\n\n const errorCode = \"code\" in error ? error.code : undefined;\n if (!errorCode) return null;\n\n // Node.js filesystem error codes\n switch (errorCode) {\n case \"ENOENT\": // File/directory not found\n case \"ENOTDIR\": // Not a directory\n return \"client_error\";\n case \"EEXIST\": // File/directory already exists\n return \"client_error\";\n case \"EISDIR\": // Is a directory\n return \"client_error\";\n case \"EINVAL\": // Invalid argument\n case \"ENAMETOOLONG\": // Filename too long\n return \"client_error\";\n case \"EACCES\": // Permission denied\n case \"EPERM\": // Operation not permitted\n return \"authorization_error\";\n case \"ENOSPC\": // No space left on device\n case \"EDQUOT\": // Disk quota exceeded\n return \"server_error\";\n case \"EIO\": // I/O error\n case \"EROFS\": // Read-only filesystem\n case \"EMFILE\": // Too many open files\n case \"ENFILE\": // File table overflow\n return \"server_error\";\n case \"EBUSY\": // Device or resource busy\n return \"throttling_error\";\n default:\n return null; // Fall back to generic classification\n }\n};\n\n// Filesystem-specific error tracker\nexport const trackFilesystemError = createStorageErrorTracker(\n STORAGE_TYPE,\n filesystemMetrics,\n classifyFilesystemError,\n);\n\n// Filesystem-specific observability layer\nexport const FilesystemObservabilityLayer = Layer.mergeAll(\n FilesystemTracingLayer,\n // Metrics are automatically available through Effect\n);\n\n// ============================================================================\n// Filesystem Utility Functions\n// ============================================================================\n\nexport const withFilesystemUploadMetrics = <A, E, R>(\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n) => withUploadMetrics(filesystemMetrics, uploadId, effect);\n\nexport const withFilesystemApiMetrics = <A, E, R>(\n operation: string,\n effect: Effect.Effect<A, E, R>,\n) => withApiMetrics(filesystemMetrics, operation, effect);\n\nexport const withFilesystemTimingMetrics = withTimingMetrics;\n\nexport const withFilesystemOperationMetrics = <A, E, R>(\n operation: string,\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n fileSize?: number,\n) =>\n withStorageOperationMetrics(\n filesystemMetrics,\n operation,\n uploadId,\n effect,\n fileSize,\n );\n\n// Filesystem-specific span wrapper\nexport const withFilesystemSpan =\n <A, E, R>(operation: string, attributes?: Record<string, unknown>) =>\n (effect: Effect.Effect<A, E, R>) =>\n withStorageSpan(operation, STORAGE_TYPE, attributes)(effect);\n\n// Filesystem-specific logging functions\nexport const logFilesystemOperation = logStorageOperation.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logFilesystemUploadProgress = logUploadProgress.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logFilesystemUploadCompletion = logUploadCompletion.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logFilesystemContext = logWithContext;\n\n// Export metrics for external access\nexport const {\n uploadRequestsTotal: filesystemUploadRequestsTotal,\n uploadPartsTotal: filesystemUploadPartsTotal,\n uploadSuccessTotal: filesystemUploadSuccessTotal,\n uploadErrorsTotal: filesystemUploadErrorsTotal,\n apiCallsTotal: filesystemApiCallsTotal,\n uploadDurationHistogram: filesystemUploadDurationHistogram,\n partUploadDurationHistogram: filesystemPartUploadDurationHistogram,\n fileSizeHistogram: filesystemFileSizeHistogram,\n partSizeHistogram: filesystemPartSizeHistogram,\n activeUploadsGauge: filesystemActiveUploadsGauge,\n uploadThroughputGauge: filesystemUploadThroughputGauge,\n uploadLatencySummary: filesystemUploadLatencySummary,\n} = filesystemMetrics;\n\n// Type exports\nexport type FilesystemMetrics = StorageMetrics;\n","import { type Effect, Layer } from \"effect\";\nimport {\n createStorageErrorTracker,\n type StorageErrorCategory,\n} from \"../core/errors.js\";\nimport {\n logStorageOperation,\n logUploadCompletion,\n logUploadProgress,\n logWithContext,\n} from \"../core/logging.js\";\nimport { createStorageMetrics, type StorageMetrics } from \"../core/metrics.js\";\nimport { createStorageTracingLayer, withStorageSpan } from \"../core/tracing.js\";\nimport {\n withApiMetrics,\n withStorageOperationMetrics,\n withTimingMetrics,\n withUploadMetrics,\n} from \"../core/utilities.js\";\n\n// ============================================================================\n// Google Cloud Storage-Specific Observability\n// ============================================================================\n\nconst STORAGE_TYPE = \"gcs\";\n\n// GCS-specific metrics\nexport const gcsMetrics = createStorageMetrics(STORAGE_TYPE);\n\n// GCS-specific tracing layer\nexport const GCSTracingLayer = createStorageTracingLayer(STORAGE_TYPE);\n\n// GCS-specific error classification\nconst classifyGCSError = (error: unknown): StorageErrorCategory | null => {\n if (!error || typeof error !== \"object\") return null;\n\n const errorCode =\n \"code\" in error ? error.code : \"status\" in error ? error.status : undefined;\n if (!errorCode) return null;\n\n // GCS-specific error codes\n switch (errorCode) {\n case \"NoSuchBucket\":\n case \"NoSuchKey\":\n case \"NoSuchUpload\":\n return \"client_error\";\n case \"BucketAlreadyOwnedByYou\":\n case \"BucketNotEmpty\":\n return \"client_error\";\n case \"InvalidBucketName\":\n case \"InvalidArgument\":\n case \"InvalidPart\":\n case \"InvalidPartOrder\":\n return \"client_error\";\n case \"EntityTooSmall\":\n case \"EntityTooLarge\":\n return \"client_error\";\n case \"MalformedPolicy\":\n return \"client_error\";\n case \"Unauthorized\":\n case \"AuthenticationRequired\":\n return \"authentication_error\";\n case \"Forbidden\":\n case \"AccessDenied\":\n return \"authorization_error\";\n case \"TooManyRequests\":\n case \"RateLimitExceeded\":\n return \"throttling_error\";\n case \"InternalError\":\n case \"ServiceUnavailable\":\n case \"BackendError\":\n return \"server_error\";\n default:\n // Check for HTTP status codes\n if (typeof errorCode === \"number\") {\n if (errorCode >= 500) return \"server_error\";\n if (errorCode === 429) return \"throttling_error\";\n if (errorCode === 403) return \"authorization_error\";\n if (errorCode === 401) return \"authentication_error\";\n if (errorCode >= 400) return \"client_error\";\n }\n return null; // Fall back to generic classification\n }\n};\n\n// GCS-specific error tracker\nexport const trackGCSError = createStorageErrorTracker(\n STORAGE_TYPE,\n gcsMetrics,\n classifyGCSError,\n);\n\n// GCS-specific observability layer\nexport const GCSObservabilityLayer = Layer.mergeAll(\n GCSTracingLayer,\n // Metrics are automatically available through Effect\n);\n\n// ============================================================================\n// GCS Utility Functions\n// ============================================================================\n\nexport const withGCSUploadMetrics = <A, E, R>(\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n) => withUploadMetrics(gcsMetrics, uploadId, effect);\n\nexport const withGCSApiMetrics = <A, E, R>(\n operation: string,\n effect: Effect.Effect<A, E, R>,\n) => withApiMetrics(gcsMetrics, operation, effect);\n\nexport const withGCSTimingMetrics = withTimingMetrics;\n\nexport const withGCSOperationMetrics = <A, E, R>(\n operation: string,\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n fileSize?: number,\n) =>\n withStorageOperationMetrics(\n gcsMetrics,\n operation,\n uploadId,\n effect,\n fileSize,\n );\n\n// GCS-specific span wrapper\nexport const withGCSSpan =\n <A, E, R>(operation: string, attributes?: Record<string, unknown>) =>\n (effect: Effect.Effect<A, E, R>) =>\n withStorageSpan(operation, STORAGE_TYPE, attributes)(effect);\n\n// GCS-specific logging functions\nexport const logGCSOperation = logStorageOperation.bind(null, STORAGE_TYPE);\nexport const logGCSUploadProgress = logUploadProgress.bind(null, STORAGE_TYPE);\nexport const logGCSUploadCompletion = logUploadCompletion.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logGCSContext = logWithContext;\n\n// Export metrics for external access\nexport const {\n uploadRequestsTotal: gcsUploadRequestsTotal,\n uploadPartsTotal: gcsUploadPartsTotal,\n uploadSuccessTotal: gcsUploadSuccessTotal,\n uploadErrorsTotal: gcsUploadErrorsTotal,\n apiCallsTotal: gcsApiCallsTotal,\n uploadDurationHistogram: gcsUploadDurationHistogram,\n partUploadDurationHistogram: gcsPartUploadDurationHistogram,\n fileSizeHistogram: gcsFileSizeHistogram,\n partSizeHistogram: gcsPartSizeHistogram,\n activeUploadsGauge: gcsActiveUploadsGauge,\n uploadThroughputGauge: gcsUploadThroughputGauge,\n uploadLatencySummary: gcsUploadLatencySummary,\n} = gcsMetrics;\n\n// Type exports\nexport type GCSMetrics = StorageMetrics;\n","import { type Effect, Layer } from \"effect\";\nimport {\n createStorageErrorTracker,\n type StorageErrorCategory,\n} from \"../core/errors.js\";\nimport {\n logStorageOperation,\n logUploadCompletion,\n logUploadProgress,\n logWithContext,\n} from \"../core/logging.js\";\nimport { createStorageMetrics, type StorageMetrics } from \"../core/metrics.js\";\nimport { createStorageTracingLayer, withStorageSpan } from \"../core/tracing.js\";\nimport {\n withApiMetrics,\n withStorageOperationMetrics,\n withTimingMetrics,\n withUploadMetrics,\n} from \"../core/utilities.js\";\n\n// ============================================================================\n// S3-Specific Observability\n// ============================================================================\n\nconst STORAGE_TYPE = \"s3\";\n\n// S3-specific metrics\nexport const s3Metrics = createStorageMetrics(STORAGE_TYPE);\n\n// S3-specific tracing layer\nexport const S3TracingLayer = createStorageTracingLayer(STORAGE_TYPE);\n\n// S3-specific error classification\nconst classifyS3Error = (error: unknown): StorageErrorCategory | null => {\n if (!error || typeof error !== \"object\") return null;\n\n const errorCode = \"code\" in error ? error.code : undefined;\n if (!errorCode) return null;\n\n // S3-specific error codes\n switch (errorCode) {\n case \"NoSuchKey\":\n case \"NoSuchBucket\":\n case \"NoSuchUpload\":\n return \"client_error\";\n case \"BucketAlreadyExists\":\n case \"BucketNotEmpty\":\n return \"client_error\";\n case \"InvalidBucketName\":\n case \"InvalidPart\":\n case \"InvalidPartOrder\":\n return \"client_error\";\n case \"EntityTooSmall\":\n case \"EntityTooLarge\":\n return \"client_error\";\n case \"ExpiredToken\":\n case \"TokenRefreshRequired\":\n return \"authentication_error\";\n case \"RequestTimeTooSkewed\":\n case \"SlowDown\":\n return \"throttling_error\";\n default:\n return null; // Fall back to generic classification\n }\n};\n\n// S3-specific error tracker\nexport const trackS3Error = createStorageErrorTracker(\n STORAGE_TYPE,\n s3Metrics,\n classifyS3Error,\n);\n\n// S3-specific observability layer\nexport const S3ObservabilityLayer = Layer.mergeAll(\n S3TracingLayer,\n // Metrics are automatically available through Effect\n);\n\n// ============================================================================\n// S3 Utility Functions\n// ============================================================================\n\nexport const withS3UploadMetrics = <A, E, R>(\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n) => withUploadMetrics(s3Metrics, uploadId, effect);\n\nexport const withS3ApiMetrics = <A, E, R>(\n operation: string,\n effect: Effect.Effect<A, E, R>,\n) => withApiMetrics(s3Metrics, operation, effect);\n\nexport const withS3TimingMetrics = withTimingMetrics;\n\nexport const withS3OperationMetrics = <A, E, R>(\n operation: string,\n uploadId: string,\n effect: Effect.Effect<A, E, R>,\n fileSize?: number,\n) =>\n withStorageOperationMetrics(s3Metrics, operation, uploadId, effect, fileSize);\n\n// S3-specific span wrapper\nexport const withS3Span =\n <A, E, R>(operation: string, attributes?: Record<string, unknown>) =>\n (effect: Effect.Effect<A, E, R>) =>\n withStorageSpan(operation, STORAGE_TYPE, attributes)(effect);\n\n// S3-specific logging functions\nexport const logS3Operation = logStorageOperation.bind(null, STORAGE_TYPE);\nexport const logS3UploadProgress = logUploadProgress.bind(null, STORAGE_TYPE);\nexport const logS3UploadCompletion = logUploadCompletion.bind(\n null,\n STORAGE_TYPE,\n);\nexport const logS3Context = logWithContext;\n\n// Export metrics for external access\nexport const {\n uploadRequestsTotal: s3UploadRequestsTotal,\n uploadPartsTotal: s3UploadPartsTotal,\n uploadSuccessTotal: s3UploadSuccessTotal,\n uploadErrorsTotal: s3UploadErrorsTotal,\n apiCallsTotal: s3ApiCallsTotal,\n uploadDurationHistogram: s3UploadDurationHistogram,\n partUploadDurationHistogram: s3PartUploadDurationHistogram,\n fileSizeHistogram: s3FileSizeHistogram,\n partSizeHistogram: s3PartSizeHistogram,\n activeUploadsGauge: s3ActiveUploadsGauge,\n uploadThroughputGauge: s3UploadThroughputGauge,\n uploadLatencySummary: s3UploadLatencySummary,\n} = s3Metrics;\n\n// Type exports\nexport type S3Metrics = StorageMetrics;\n","import { Effect, Metric } from \"effect\";\nimport type { UploadEngineMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Upload Error Classification and Tracking\n// ============================================================================\n\nexport type UploadErrorCategory =\n | \"network_error\"\n | \"authentication_error\"\n | \"authorization_error\"\n | \"validation_error\"\n | \"size_limit_error\"\n | \"storage_error\"\n | \"abort_error\"\n | \"unknown_error\";\n\n/**\n * Classify upload errors into standard categories\n */\nexport const classifyUploadError = (error: unknown): UploadErrorCategory => {\n if (!error || typeof error !== \"object\") return \"unknown_error\";\n\n const errorCode = \"code\" in error ? error.code : undefined;\n const errorName = \"name\" in error ? error.name : undefined;\n const errorMessage =\n error instanceof Error ? error.message.toLowerCase() : \"\";\n\n // Abort errors\n if (\n errorCode === \"ABORTED\" ||\n errorName === \"AbortError\" ||\n errorMessage.includes(\"abort\")\n ) {\n return \"abort_error\";\n }\n\n // Size limit errors\n if (\n errorCode === \"FILE_TOO_LARGE\" ||\n errorCode === \"LIMIT_FILE_SIZE\" ||\n errorCode === \"RequestEntityTooLarge\" ||\n errorMessage.includes(\"too large\") ||\n errorMessage.includes(\"size limit\") ||\n errorMessage.includes(\"max size\")\n ) {\n return \"size_limit_error\";\n }\n\n // Validation errors\n if (\n errorCode === \"INVALID_FILE\" ||\n errorCode === \"INVALID_METADATA\" ||\n errorCode === \"VALIDATION_ERROR\" ||\n errorMessage.includes(\"validation\") ||\n errorMessage.includes(\"invalid\")\n ) {\n return \"validation_error\";\n }\n\n // Network errors\n if (\n errorCode === \"NetworkError\" ||\n errorCode === \"ECONNRESET\" ||\n errorCode === \"ENOTFOUND\" ||\n errorCode === \"ETIMEDOUT\" ||\n errorMessage.includes(\"network\") ||\n errorMessage.includes(\"timeout\")\n ) {\n return \"network_error\";\n }\n\n // Authentication errors\n if (\n errorCode === \"UNAUTHORIZED\" ||\n errorCode === \"AuthenticationFailed\" ||\n errorName === \"AuthenticationError\" ||\n errorMessage.includes(\"authentication\") ||\n errorMessage.includes(\"unauthorized\")\n ) {\n return \"authentication_error\";\n }\n\n // Authorization errors\n if (\n errorCode === \"FORBIDDEN\" ||\n errorCode === \"AccessDenied\" ||\n errorName === \"AuthorizationError\" ||\n errorMessage.includes(\"forbidden\") ||\n errorMessage.includes(\"permission\")\n ) {\n return \"authorization_error\";\n }\n\n // Storage errors\n if (\n errorCode === \"FILE_WRITE_ERROR\" ||\n errorCode === \"STORAGE_ERROR\" ||\n errorMessage.includes(\"storage\") ||\n errorMessage.includes(\"write error\")\n ) {\n return \"storage_error\";\n }\n\n return \"unknown_error\";\n};\n\n/**\n * Track upload errors with metrics and structured logging\n */\nexport const trackUploadError = (\n metrics: UploadEngineMetrics,\n operation: string,\n error: unknown,\n context: Record<string, unknown> = {},\n) =>\n Effect.gen(function* () {\n const errorCategory = classifyUploadError(error);\n\n // Record error metrics\n const errorMetric = metrics.uploadFailedTotal.pipe(\n Metric.tagged(\"operation\", operation),\n Metric.tagged(\"error_category\", errorCategory),\n );\n yield* errorMetric(Effect.succeed(1));\n\n // Create detailed error context\n const errorDetails = {\n operation,\n error_category: errorCategory,\n error_type: typeof error,\n error_message: error instanceof Error ? error.message : String(error),\n error_code:\n error && typeof error === \"object\" && \"code\" in error\n ? String(error.code)\n : undefined,\n error_name:\n error && typeof error === \"object\" && \"name\" in error\n ? String(error.name)\n : undefined,\n ...context,\n };\n\n // Log structured error\n yield* Effect.logError(`Upload ${operation} failed`).pipe(\n Effect.annotateLogs(errorDetails),\n );\n });\n\n/**\n * Create a custom error classifier for upload operations\n */\nexport const createUploadErrorClassifier = (\n customErrorMapping?: (error: unknown) => UploadErrorCategory | null,\n) => {\n return (error: unknown): UploadErrorCategory => {\n // Try custom mapping first\n if (customErrorMapping) {\n const customResult = customErrorMapping(error);\n if (customResult !== null) return customResult;\n }\n\n // Fall back to generic classification\n return classifyUploadError(error);\n };\n};\n","import { Metric, MetricBoundaries } from \"effect\";\n\n// ============================================================================\n// Upload Server Metrics\n// ============================================================================\n\n/**\n * Upload server metrics for tracking upload operations\n */\nexport const createUploadEngineMetrics = () => ({\n // Counter metrics\n uploadCreatedTotal: Metric.counter(\"upload_created_total\", {\n description: \"Total number of uploads created\",\n }),\n\n uploadCompletedTotal: Metric.counter(\"upload_completed_total\", {\n description: \"Total number of uploads completed successfully\",\n }),\n\n uploadFailedTotal: Metric.counter(\"upload_failed_total\", {\n description: \"Total number of uploads that failed\",\n }),\n\n chunkUploadedTotal: Metric.counter(\"chunk_uploaded_total\", {\n description: \"Total number of chunks uploaded\",\n }),\n\n uploadFromUrlTotal: Metric.counter(\"upload_from_url_total\", {\n description: \"Total number of URL-based uploads\",\n }),\n\n uploadFromUrlSuccessTotal: Metric.counter(\"upload_from_url_success_total\", {\n description: \"Total number of successful URL-based uploads\",\n }),\n\n uploadFromUrlFailedTotal: Metric.counter(\"upload_from_url_failed_total\", {\n description: \"Total number of failed URL-based uploads\",\n }),\n\n // Histogram metrics\n uploadDurationHistogram: Metric.histogram(\n \"upload_duration_seconds\",\n MetricBoundaries.exponential({\n start: 0.01, // 10ms\n factor: 2,\n count: 20, // Up to ~10 seconds\n }),\n \"Duration of complete upload operations in seconds\",\n ),\n\n chunkUploadDurationHistogram: Metric.histogram(\n \"chunk_upload_duration_seconds\",\n MetricBoundaries.exponential({\n start: 0.001, // 1ms\n factor: 2,\n count: 15, // Up to ~32 seconds\n }),\n \"Duration of individual chunk uploads in seconds\",\n ),\n\n uploadFileSizeHistogram: Metric.histogram(\n \"upload_file_size_bytes\",\n MetricBoundaries.exponential({\n start: 1024, // 1KB\n factor: 2,\n count: 25, // Up to ~33GB\n }),\n \"Size of uploaded files in bytes\",\n ),\n\n chunkSizeHistogram: Metric.histogram(\n \"chunk_size_bytes\",\n MetricBoundaries.linear({\n start: 262_144, // 256KB\n width: 262_144, // 256KB increments\n count: 20, // Up to ~5MB\n }),\n \"Size of uploaded chunks in bytes\",\n ),\n\n // Gauge metrics\n activeUploadsGauge: Metric.gauge(\"active_uploads\", {\n description: \"Number of currently active uploads\",\n }),\n\n uploadThroughputGauge: Metric.gauge(\"upload_throughput_bytes_per_second\", {\n description: \"Current upload throughput in bytes per second\",\n }),\n\n // Summary metrics for latency percentiles\n uploadLatencySummary: Metric.summary({\n name: \"upload_latency_seconds\",\n maxAge: \"10 minutes\",\n maxSize: 1000,\n error: 0.01,\n quantiles: [0.5, 0.9, 0.95, 0.99],\n description: \"Upload operation latency percentiles\",\n }),\n\n chunkLatencySummary: Metric.summary({\n name: \"chunk_latency_seconds\",\n maxAge: \"10 minutes\",\n maxSize: 1000,\n error: 0.01,\n quantiles: [0.5, 0.9, 0.95, 0.99],\n description: \"Chunk upload latency percentiles\",\n }),\n});\n\n/**\n * Type for upload server metrics\n */\nexport type UploadEngineMetrics = ReturnType<typeof createUploadEngineMetrics>;\n\n/**\n * Default upload server metrics instance\n */\nexport const uploadEngineMetrics = createUploadEngineMetrics();\n","import { Effect, Layer, Metric } from \"effect\";\nimport {\n makeUploadObservabilityLayer,\n UploadObservability,\n} from \"../core/layers.js\";\nimport { createUploadEngineMetrics } from \"./metrics.js\";\n\n// ============================================================================\n// Upload Observability Layer Implementation\n// ============================================================================\n\n/**\n * Create a live upload observability layer with full metrics\n */\nexport const makeUploadObservabilityLive = (\n serviceName = \"uploadista-upload-server\",\n): Layer.Layer<UploadObservability> => {\n const metrics = createUploadEngineMetrics();\n\n return Layer.succeed(UploadObservability, {\n serviceName,\n enabled: true,\n metrics: {\n uploadCreated: Effect.succeed(metrics.uploadCreatedTotal).pipe(\n Effect.flatMap((metric) => Metric.increment(metric)),\n ),\n uploadCompleted: Effect.succeed(metrics.uploadCompletedTotal).pipe(\n Effect.flatMap((metric) => Metric.increment(metric)),\n ),\n uploadFailed: Effect.succeed(metrics.uploadFailedTotal).pipe(\n Effect.flatMap((metric) => Metric.increment(metric)),\n ),\n chunkUploaded: Effect.succeed(metrics.chunkUploadedTotal).pipe(\n Effect.flatMap((metric) => Metric.increment(metric)),\n ),\n },\n });\n};\n\n/**\n * Default live upload observability layer\n */\nexport const UploadObservabilityLive = makeUploadObservabilityLive();\n\n/**\n * No-op upload observability layer (for testing or disabled observability)\n */\nexport const UploadObservabilityDisabled = makeUploadObservabilityLayer(false);\n\n/**\n * Helper to get upload metrics from context\n */\nexport const getUploadMetrics = Effect.gen(function* () {\n const obs = yield* UploadObservability;\n return obs.metrics;\n});\n\n/**\n * Helper to track upload duration\n */\nexport const withUploadDuration = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R | UploadObservability> => {\n const metrics = createUploadEngineMetrics();\n return Effect.gen(function* () {\n const startTime = Date.now();\n const result = yield* effect;\n const duration = (Date.now() - startTime) / 1000; // Convert to seconds\n yield* Metric.update(metrics.uploadDurationHistogram, duration);\n return result;\n }).pipe(Effect.withSpan(\"upload-operation\"));\n};\n\n/**\n * Helper to track chunk upload duration\n */\nexport const withChunkDuration = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R> => {\n const metrics = createUploadEngineMetrics();\n return Effect.gen(function* () {\n const startTime = Date.now();\n const result = yield* effect;\n const duration = (Date.now() - startTime) / 1000; // Convert to seconds\n yield* Metric.update(metrics.chunkUploadDurationHistogram, duration);\n return result;\n }).pipe(Effect.withSpan(\"chunk-upload\"));\n};\n","import { Layer } from \"effect\";\nimport { UploadObservability } from \"../core/layers.js\";\nimport { createUploadEngineMetrics } from \"./metrics.js\";\n\n// ======================== ====================================================\n// Upload Observability Testing Utilities\n// ============================================================================\n\n/**\n * Create a test upload observability layer that doesn't actually emit metrics\n * but validates that the observability system is wired correctly\n */\nexport const UploadObservabilityTest = Layer.succeed(UploadObservability, {\n serviceName: \"uploadista-upload-server-test\",\n enabled: true,\n metrics: {\n uploadCreated: () => Promise.resolve(),\n uploadCompleted: () => Promise.resolve(),\n uploadFailed: () => Promise.resolve(),\n chunkUploaded: () => Promise.resolve(),\n } as any,\n});\n\n/**\n * Get metrics for validation (useful for testing metric definitions)\n */\nexport const getTestMetrics = () => createUploadEngineMetrics();\n\n/**\n * Validate that all required metrics exist\n */\nexport const validateMetricsExist = () => {\n const metrics = getTestMetrics();\n\n const requiredMetrics = [\n \"uploadCreatedTotal\",\n \"uploadCompletedTotal\",\n \"uploadFailedTotal\",\n \"chunkUploadedTotal\",\n \"uploadFromUrlTotal\",\n \"uploadFromUrlSuccessTotal\",\n \"uploadFromUrlFailedTotal\",\n \"uploadDurationHistogram\",\n \"chunkUploadDurationHistogram\",\n \"uploadFileSizeHistogram\",\n \"chunkSizeHistogram\",\n \"activeUploadsGauge\",\n \"uploadThroughputGauge\",\n \"uploadLatencySummary\",\n \"chunkLatencySummary\",\n ];\n\n const missingMetrics = requiredMetrics.filter((name) => !(name in metrics));\n\n if (missingMetrics.length > 0) {\n throw new Error(`Missing required metrics: ${missingMetrics.join(\", \")}`);\n }\n\n return true;\n};\n","import { Effect } from \"effect\";\n\n// ============================================================================\n// Upload Tracing Utilities\n// ============================================================================\n\n/**\n * Wrap an Effect with an upload operation span\n */\nexport const withUploadSpan =\n <A, E, R>(operation: string, attributes?: Record<string, unknown>) =>\n (effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>\n effect.pipe(\n Effect.withSpan(`upload-${operation}`, {\n attributes: {\n \"upload.operation\": operation,\n ...attributes,\n },\n }),\n );\n\n/**\n * Add upload context to the current span\n */\nexport const withUploadContext = (context: {\n uploadId?: string;\n fileName?: string;\n fileSize?: number;\n storageId?: string;\n mimeType?: string;\n}) =>\n Effect.annotateCurrentSpan({\n \"upload.id\": context.uploadId ?? \"unknown\",\n \"upload.file_name\": context.fileName ?? \"unknown\",\n \"upload.file_size\": context.fileSize?.toString() ?? \"0\",\n \"upload.storage_id\": context.storageId ?? \"unknown\",\n \"upload.mime_type\": context.mimeType ?? \"unknown\",\n });\n\n/**\n * Add chunk context to the current span\n */\nexport const withChunkContext = (context: {\n uploadId: string;\n chunkSize: number;\n offset: number;\n totalSize?: number;\n}) =>\n Effect.annotateCurrentSpan({\n \"chunk.upload_id\": context.uploadId,\n \"chunk.size\": context.chunkSize.toString(),\n \"chunk.offset\": context.offset.toString(),\n \"chunk.total_size\": context.totalSize?.toString() ?? \"0\",\n \"chunk.progress\":\n context.totalSize && context.totalSize > 0\n ? ((context.offset / context.totalSize) * 100).toFixed(2)\n : \"0\",\n });\n"],"mappings":"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"}