@uploadista/observability 0.0.11 → 0.0.13-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +233 -233
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +233 -233
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/src/flow/index.ts +7 -6
- package/src/flow/testing.ts +1 -1
- package/src/storage/index.ts +3 -2
- package/src/test-observability.ts +20 -17
- package/src/upload/index.ts +6 -5
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["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/layers.ts","../src/core/logging.ts","../src/core/metrics.ts","../src/core/testing.ts","../src/core/tracing.ts","../src/core/utilities.ts","../src/flow/metrics.ts","../src/flow/tracing.ts","../src/flow/layers.ts","../src/flow/errors.ts","../src/flow/testing.ts","../src/service/metrics.ts","../src/storage/s3.ts","../src/storage/azure.ts","../src/storage/gcs.ts","../src/storage/filesystem.ts","../src/upload/metrics.ts","../src/upload/tracing.ts","../src/upload/layers.ts","../src/upload/errors.ts","../src/upload/testing.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","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 { NodeSdk, WebSdk } from \"@effect/opentelemetry\";\nimport {\n BatchSpanProcessor,\n ConsoleSpanExporter,\n} from \"@opentelemetry/sdk-trace-base\";\nimport { Context, Effect, Layer } from \"effect\";\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","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/**\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 } 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","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 */\nexport const withNodeDuration = <A, E, R>(\n nodeId: string,\n nodeType: string,\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.nodeDurationHistogram, duration);\n yield* Metric.update(metrics.nodeLatencySummary, duration);\n return result;\n }).pipe(\n Effect.withSpan(`node-${nodeType}`, {\n attributes: {\n \"node.id\": nodeId,\n \"node.type\": 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, 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 } from \"effect\";\nimport { FlowObservability } from \"../core/layers.js\";\nimport type { FlowObservabilityService } 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 { 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// 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 { 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// 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// 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 { 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 } 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","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 { 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 { 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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["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/layers.ts","../src/core/logging.ts","../src/core/metrics.ts","../src/core/testing.ts","../src/core/tracing.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","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 { NodeSdk, WebSdk } from \"@effect/opentelemetry\";\nimport {\n BatchSpanProcessor,\n ConsoleSpanExporter,\n} from \"@opentelemetry/sdk-trace-base\";\nimport { Context, Effect, Layer } from \"effect\";\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","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/**\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 */\nexport const withNodeDuration = <A, E, R>(\n nodeId: string,\n nodeType: string,\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.nodeDurationHistogram, duration);\n yield* Metric.update(metrics.nodeLatencySummary, duration);\n return result;\n }).pipe(\n Effect.withSpan(`node-${nodeType}`, {\n attributes: {\n \"node.id\": nodeId,\n \"node.type\": 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","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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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uploadista/observability",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.13-beta.1",
|
|
5
5
|
"description": "Observability package for Uploadista",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Uploadista",
|
|
@@ -15,19 +15,19 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@effect/opentelemetry": "0.59.0",
|
|
18
|
-
"@opentelemetry/sdk-logs": "0.
|
|
18
|
+
"@opentelemetry/sdk-logs": "0.208.0",
|
|
19
19
|
"@opentelemetry/sdk-metrics": "2.2.0",
|
|
20
20
|
"@opentelemetry/sdk-trace-base": "2.2.0",
|
|
21
21
|
"@opentelemetry/sdk-trace-node": "2.2.0",
|
|
22
22
|
"@opentelemetry/sdk-trace-web": "2.2.0",
|
|
23
|
-
"effect": "3.19.
|
|
23
|
+
"effect": "3.19.2"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@cloudflare/workers-types": "4.
|
|
26
|
+
"@cloudflare/workers-types": "4.20251106.1",
|
|
27
27
|
"@types/node": "24.10.0",
|
|
28
28
|
"tsdown": "0.16.0",
|
|
29
29
|
"typescript": "5.9.3",
|
|
30
|
-
"@uploadista/typescript-config": "0.0.
|
|
30
|
+
"@uploadista/typescript-config": "0.0.13-beta.1"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsdown",
|
package/src/flow/index.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
// Flow observability exports
|
|
2
|
-
|
|
3
|
-
export * from "./
|
|
2
|
+
|
|
3
|
+
export * from "./errors.js";
|
|
4
4
|
export {
|
|
5
|
-
makeFlowObservabilityLive,
|
|
6
5
|
FlowObservabilityLive,
|
|
7
6
|
getFlowMetrics,
|
|
8
|
-
|
|
9
|
-
withNodeDuration,
|
|
7
|
+
makeFlowObservabilityLive,
|
|
10
8
|
trackActiveFlow,
|
|
11
9
|
trackActiveNode,
|
|
10
|
+
withFlowDuration,
|
|
11
|
+
withNodeDuration,
|
|
12
12
|
} from "./layers.js";
|
|
13
|
-
export * from "./
|
|
13
|
+
export * from "./metrics.js";
|
|
14
14
|
export {
|
|
15
15
|
makeTestFlowObservability as makeTestFlowObservabilityUtil,
|
|
16
16
|
runWithTestFlowObservability,
|
|
17
17
|
} from "./testing.js";
|
|
18
|
+
export * from "./tracing.js";
|
package/src/flow/testing.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, Layer } from "effect";
|
|
2
|
-
import { FlowObservability } from "../core/layers.js";
|
|
3
2
|
import type { FlowObservabilityService } from "../core/layers.js";
|
|
3
|
+
import { FlowObservability } from "../core/layers.js";
|
|
4
4
|
|
|
5
5
|
// ============================================================================
|
|
6
6
|
// Test Flow Observability Layers
|