@uploadista/core 0.0.2
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/.turbo/turbo-build.log +5 -0
- package/.turbo/turbo-check.log +231 -0
- package/.turbo/turbo-format.log +5 -0
- package/LICENSE +21 -0
- package/README.md +1120 -0
- package/dist/chunk-CUT6urMc.cjs +1 -0
- package/dist/debounce-C2SeqcxD.js +2 -0
- package/dist/debounce-C2SeqcxD.js.map +1 -0
- package/dist/debounce-LZK7yS7Z.cjs +1 -0
- package/dist/errors/index.cjs +1 -0
- package/dist/errors/index.d.cts +3 -0
- package/dist/errors/index.d.ts +3 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +2 -0
- package/dist/errors/uploadista-error.d.ts +209 -0
- package/dist/errors/uploadista-error.d.ts.map +1 -0
- package/dist/errors/uploadista-error.js +322 -0
- package/dist/errors-8i_aMxOE.js +1 -0
- package/dist/errors-CRm1FHHT.cjs +0 -0
- package/dist/flow/edge.d.ts +47 -0
- package/dist/flow/edge.d.ts.map +1 -0
- package/dist/flow/edge.js +40 -0
- package/dist/flow/event.d.ts +206 -0
- package/dist/flow/event.d.ts.map +1 -0
- package/dist/flow/event.js +53 -0
- package/dist/flow/flow-server.d.ts +223 -0
- package/dist/flow/flow-server.d.ts.map +1 -0
- package/dist/flow/flow-server.js +614 -0
- package/dist/flow/flow.d.ts +238 -0
- package/dist/flow/flow.d.ts.map +1 -0
- package/dist/flow/flow.js +629 -0
- package/dist/flow/index.cjs +1 -0
- package/dist/flow/index.d.cts +6 -0
- package/dist/flow/index.d.ts +24 -0
- package/dist/flow/index.d.ts.map +1 -0
- package/dist/flow/index.js +24 -0
- package/dist/flow/node.d.ts +136 -0
- package/dist/flow/node.d.ts.map +1 -0
- package/dist/flow/node.js +153 -0
- package/dist/flow/nodes/index.d.ts +8 -0
- package/dist/flow/nodes/index.d.ts.map +1 -0
- package/dist/flow/nodes/index.js +7 -0
- package/dist/flow/nodes/input-node.d.ts +78 -0
- package/dist/flow/nodes/input-node.d.ts.map +1 -0
- package/dist/flow/nodes/input-node.js +233 -0
- package/dist/flow/nodes/storage-node.d.ts +67 -0
- package/dist/flow/nodes/storage-node.d.ts.map +1 -0
- package/dist/flow/nodes/storage-node.js +94 -0
- package/dist/flow/nodes/streaming-input-node.d.ts +69 -0
- package/dist/flow/nodes/streaming-input-node.d.ts.map +1 -0
- package/dist/flow/nodes/streaming-input-node.js +156 -0
- package/dist/flow/nodes/transform-node.d.ts +85 -0
- package/dist/flow/nodes/transform-node.d.ts.map +1 -0
- package/dist/flow/nodes/transform-node.js +107 -0
- package/dist/flow/parallel-scheduler.d.ts +175 -0
- package/dist/flow/parallel-scheduler.d.ts.map +1 -0
- package/dist/flow/parallel-scheduler.js +193 -0
- package/dist/flow/plugins/credential-provider.d.ts +47 -0
- package/dist/flow/plugins/credential-provider.d.ts.map +1 -0
- package/dist/flow/plugins/credential-provider.js +24 -0
- package/dist/flow/plugins/image-ai-plugin.d.ts +61 -0
- package/dist/flow/plugins/image-ai-plugin.d.ts.map +1 -0
- package/dist/flow/plugins/image-ai-plugin.js +21 -0
- package/dist/flow/plugins/image-plugin.d.ts +52 -0
- package/dist/flow/plugins/image-plugin.d.ts.map +1 -0
- package/dist/flow/plugins/image-plugin.js +22 -0
- package/dist/flow/plugins/types/describe-image-node.d.ts +16 -0
- package/dist/flow/plugins/types/describe-image-node.d.ts.map +1 -0
- package/dist/flow/plugins/types/describe-image-node.js +9 -0
- package/dist/flow/plugins/types/index.d.ts +9 -0
- package/dist/flow/plugins/types/index.d.ts.map +1 -0
- package/dist/flow/plugins/types/index.js +8 -0
- package/dist/flow/plugins/types/optimize-node.d.ts +20 -0
- package/dist/flow/plugins/types/optimize-node.d.ts.map +1 -0
- package/dist/flow/plugins/types/optimize-node.js +11 -0
- package/dist/flow/plugins/types/remove-background-node.d.ts +16 -0
- package/dist/flow/plugins/types/remove-background-node.d.ts.map +1 -0
- package/dist/flow/plugins/types/remove-background-node.js +9 -0
- package/dist/flow/plugins/types/resize-node.d.ts +21 -0
- package/dist/flow/plugins/types/resize-node.d.ts.map +1 -0
- package/dist/flow/plugins/types/resize-node.js +16 -0
- package/dist/flow/plugins/zip-plugin.d.ts +62 -0
- package/dist/flow/plugins/zip-plugin.d.ts.map +1 -0
- package/dist/flow/plugins/zip-plugin.js +21 -0
- package/dist/flow/typed-flow.d.ts +90 -0
- package/dist/flow/typed-flow.d.ts.map +1 -0
- package/dist/flow/typed-flow.js +59 -0
- package/dist/flow/types/flow-file.d.ts +45 -0
- package/dist/flow/types/flow-file.d.ts.map +1 -0
- package/dist/flow/types/flow-file.js +27 -0
- package/dist/flow/types/flow-job.d.ts +118 -0
- package/dist/flow/types/flow-job.d.ts.map +1 -0
- package/dist/flow/types/flow-job.js +11 -0
- package/dist/flow/types/flow-types.d.ts +321 -0
- package/dist/flow/types/flow-types.d.ts.map +1 -0
- package/dist/flow/types/flow-types.js +52 -0
- package/dist/flow/types/index.d.ts +4 -0
- package/dist/flow/types/index.d.ts.map +1 -0
- package/dist/flow/types/index.js +3 -0
- package/dist/flow/types/run-args.d.ts +38 -0
- package/dist/flow/types/run-args.d.ts.map +1 -0
- package/dist/flow/types/run-args.js +30 -0
- package/dist/flow/types/type-validator.d.ts +26 -0
- package/dist/flow/types/type-validator.d.ts.map +1 -0
- package/dist/flow/types/type-validator.js +134 -0
- package/dist/flow/utils/resolve-upload-metadata.d.ts +11 -0
- package/dist/flow/utils/resolve-upload-metadata.d.ts.map +1 -0
- package/dist/flow/utils/resolve-upload-metadata.js +28 -0
- package/dist/flow-2zXnEiWL.cjs +1 -0
- package/dist/flow-CRaKy7Vj.js +2 -0
- package/dist/flow-CRaKy7Vj.js.map +1 -0
- package/dist/generate-id-Dm-Vboxq.d.ts +34 -0
- package/dist/generate-id-Dm-Vboxq.d.ts.map +1 -0
- package/dist/generate-id-LjJRLD6N.d.cts +34 -0
- package/dist/generate-id-LjJRLD6N.d.cts.map +1 -0
- package/dist/generate-id-xHp_Z7Cl.cjs +1 -0
- package/dist/generate-id-yohS1ZDk.js +2 -0
- package/dist/generate-id-yohS1ZDk.js.map +1 -0
- package/dist/index-BO8GZlbD.d.cts +1040 -0
- package/dist/index-BO8GZlbD.d.cts.map +1 -0
- package/dist/index-BoGG5KAY.d.ts +1 -0
- package/dist/index-BtBZHVmz.d.cts +1 -0
- package/dist/index-D-CoVpkZ.d.ts +1004 -0
- package/dist/index-D-CoVpkZ.d.ts.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/logger/logger.cjs +1 -0
- package/dist/logger/logger.d.cts +8 -0
- package/dist/logger/logger.d.cts.map +1 -0
- package/dist/logger/logger.d.ts +5 -0
- package/dist/logger/logger.d.ts.map +1 -0
- package/dist/logger/logger.js +10 -0
- package/dist/logger/logger.js.map +1 -0
- package/dist/semaphore-0ZwjVpyF.js +2 -0
- package/dist/semaphore-0ZwjVpyF.js.map +1 -0
- package/dist/semaphore-BHprIjFI.d.cts +37 -0
- package/dist/semaphore-BHprIjFI.d.cts.map +1 -0
- package/dist/semaphore-DThupBkc.d.ts +37 -0
- package/dist/semaphore-DThupBkc.d.ts.map +1 -0
- package/dist/semaphore-DVrONiAV.cjs +1 -0
- package/dist/stream-limiter-CoWKv39w.js +2 -0
- package/dist/stream-limiter-CoWKv39w.js.map +1 -0
- package/dist/stream-limiter-JgOwmkMa.cjs +1 -0
- package/dist/streams/multi-stream.cjs +1 -0
- package/dist/streams/multi-stream.d.cts +91 -0
- package/dist/streams/multi-stream.d.cts.map +1 -0
- package/dist/streams/multi-stream.d.ts +86 -0
- package/dist/streams/multi-stream.d.ts.map +1 -0
- package/dist/streams/multi-stream.js +149 -0
- package/dist/streams/multi-stream.js.map +1 -0
- package/dist/streams/stream-limiter.cjs +1 -0
- package/dist/streams/stream-limiter.d.cts +36 -0
- package/dist/streams/stream-limiter.d.cts.map +1 -0
- package/dist/streams/stream-limiter.d.ts +27 -0
- package/dist/streams/stream-limiter.d.ts.map +1 -0
- package/dist/streams/stream-limiter.js +49 -0
- package/dist/streams/stream-splitter.cjs +1 -0
- package/dist/streams/stream-splitter.d.cts +68 -0
- package/dist/streams/stream-splitter.d.cts.map +1 -0
- package/dist/streams/stream-splitter.d.ts +51 -0
- package/dist/streams/stream-splitter.d.ts.map +1 -0
- package/dist/streams/stream-splitter.js +175 -0
- package/dist/streams/stream-splitter.js.map +1 -0
- package/dist/types/data-store-registry.d.ts +13 -0
- package/dist/types/data-store-registry.d.ts.map +1 -0
- package/dist/types/data-store-registry.js +4 -0
- package/dist/types/data-store.d.ts +316 -0
- package/dist/types/data-store.d.ts.map +1 -0
- package/dist/types/data-store.js +157 -0
- package/dist/types/event-broadcaster.d.ts +28 -0
- package/dist/types/event-broadcaster.d.ts.map +1 -0
- package/dist/types/event-broadcaster.js +6 -0
- package/dist/types/event-emitter.d.ts +378 -0
- package/dist/types/event-emitter.d.ts.map +1 -0
- package/dist/types/event-emitter.js +223 -0
- package/dist/types/index.cjs +1 -0
- package/dist/types/index.d.cts +6 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +9 -0
- package/dist/types/input-file.d.ts +104 -0
- package/dist/types/input-file.d.ts.map +1 -0
- package/dist/types/input-file.js +27 -0
- package/dist/types/kv-store.d.ts +281 -0
- package/dist/types/kv-store.d.ts.map +1 -0
- package/dist/types/kv-store.js +234 -0
- package/dist/types/middleware.d.ts +17 -0
- package/dist/types/middleware.d.ts.map +1 -0
- package/dist/types/middleware.js +21 -0
- package/dist/types/upload-event.d.ts +105 -0
- package/dist/types/upload-event.d.ts.map +1 -0
- package/dist/types/upload-event.js +71 -0
- package/dist/types/upload-file.d.ts +136 -0
- package/dist/types/upload-file.d.ts.map +1 -0
- package/dist/types/upload-file.js +34 -0
- package/dist/types/websocket.d.ts +144 -0
- package/dist/types/websocket.d.ts.map +1 -0
- package/dist/types/websocket.js +40 -0
- package/dist/types-BT-cvi7T.cjs +1 -0
- package/dist/types-DhU2j-XF.js +2 -0
- package/dist/types-DhU2j-XF.js.map +1 -0
- package/dist/upload/convert-to-stream.d.ts +38 -0
- package/dist/upload/convert-to-stream.d.ts.map +1 -0
- package/dist/upload/convert-to-stream.js +43 -0
- package/dist/upload/convert-upload-to-flow-file.d.ts +14 -0
- package/dist/upload/convert-upload-to-flow-file.d.ts.map +1 -0
- package/dist/upload/convert-upload-to-flow-file.js +21 -0
- package/dist/upload/create-upload.d.ts +68 -0
- package/dist/upload/create-upload.d.ts.map +1 -0
- package/dist/upload/create-upload.js +157 -0
- package/dist/upload/index.cjs +1 -0
- package/dist/upload/index.d.cts +6 -0
- package/dist/upload/index.d.ts +4 -0
- package/dist/upload/index.d.ts.map +1 -0
- package/dist/upload/index.js +3 -0
- package/dist/upload/mime.d.ts +24 -0
- package/dist/upload/mime.d.ts.map +1 -0
- package/dist/upload/mime.js +351 -0
- package/dist/upload/upload-chunk.d.ts +58 -0
- package/dist/upload/upload-chunk.d.ts.map +1 -0
- package/dist/upload/upload-chunk.js +277 -0
- package/dist/upload/upload-server.d.ts +221 -0
- package/dist/upload/upload-server.d.ts.map +1 -0
- package/dist/upload/upload-server.js +181 -0
- package/dist/upload/upload-strategy-negotiator.d.ts +148 -0
- package/dist/upload/upload-strategy-negotiator.d.ts.map +1 -0
- package/dist/upload/upload-strategy-negotiator.js +217 -0
- package/dist/upload/upload-url.d.ts +68 -0
- package/dist/upload/upload-url.d.ts.map +1 -0
- package/dist/upload/upload-url.js +142 -0
- package/dist/upload/write-to-store.d.ts +77 -0
- package/dist/upload/write-to-store.d.ts.map +1 -0
- package/dist/upload/write-to-store.js +147 -0
- package/dist/upload-DLuICjpP.cjs +1 -0
- package/dist/upload-DaXO34dE.js +2 -0
- package/dist/upload-DaXO34dE.js.map +1 -0
- package/dist/uploadista-error-BB-Wdiz9.cjs +22 -0
- package/dist/uploadista-error-BVsVxqvz.js +23 -0
- package/dist/uploadista-error-BVsVxqvz.js.map +1 -0
- package/dist/uploadista-error-CwxYs4EB.d.ts +52 -0
- package/dist/uploadista-error-CwxYs4EB.d.ts.map +1 -0
- package/dist/uploadista-error-kKlhLRhY.d.cts +52 -0
- package/dist/uploadista-error-kKlhLRhY.d.cts.map +1 -0
- package/dist/utils/checksum.d.ts +22 -0
- package/dist/utils/checksum.d.ts.map +1 -0
- package/dist/utils/checksum.js +49 -0
- package/dist/utils/debounce.cjs +1 -0
- package/dist/utils/debounce.d.cts +38 -0
- package/dist/utils/debounce.d.cts.map +1 -0
- package/dist/utils/debounce.d.ts +36 -0
- package/dist/utils/debounce.d.ts.map +1 -0
- package/dist/utils/debounce.js +73 -0
- package/dist/utils/generate-id.cjs +1 -0
- package/dist/utils/generate-id.d.cts +2 -0
- package/dist/utils/generate-id.d.ts +32 -0
- package/dist/utils/generate-id.d.ts.map +1 -0
- package/dist/utils/generate-id.js +23 -0
- package/dist/utils/md5.cjs +1 -0
- package/dist/utils/md5.d.cts +73 -0
- package/dist/utils/md5.d.cts.map +1 -0
- package/dist/utils/md5.d.ts +71 -0
- package/dist/utils/md5.d.ts.map +1 -0
- package/dist/utils/md5.js +417 -0
- package/dist/utils/md5.js.map +1 -0
- package/dist/utils/once.cjs +1 -0
- package/dist/utils/once.d.cts +25 -0
- package/dist/utils/once.d.cts.map +1 -0
- package/dist/utils/once.d.ts +21 -0
- package/dist/utils/once.d.ts.map +1 -0
- package/dist/utils/once.js +54 -0
- package/dist/utils/once.js.map +1 -0
- package/dist/utils/semaphore.cjs +1 -0
- package/dist/utils/semaphore.d.cts +3 -0
- package/dist/utils/semaphore.d.ts +78 -0
- package/dist/utils/semaphore.d.ts.map +1 -0
- package/dist/utils/semaphore.js +134 -0
- package/dist/utils/throttle.cjs +1 -0
- package/dist/utils/throttle.d.cts +24 -0
- package/dist/utils/throttle.d.cts.map +1 -0
- package/dist/utils/throttle.d.ts +18 -0
- package/dist/utils/throttle.d.ts.map +1 -0
- package/dist/utils/throttle.js +20 -0
- package/dist/utils/throttle.js.map +1 -0
- package/docs/PARALLEL_EXECUTION.md +206 -0
- package/docs/PARALLEL_EXECUTION_QUICKSTART.md +142 -0
- package/docs/PARALLEL_EXECUTION_REFACTOR.md +184 -0
- package/package.json +80 -0
- package/src/errors/__tests__/uploadista-error.test.ts +251 -0
- package/src/errors/index.ts +2 -0
- package/src/errors/uploadista-error.ts +394 -0
- package/src/flow/README.md +352 -0
- package/src/flow/edge.test.ts +146 -0
- package/src/flow/edge.ts +60 -0
- package/src/flow/event.ts +229 -0
- package/src/flow/flow-server.ts +1089 -0
- package/src/flow/flow.ts +1050 -0
- package/src/flow/index.ts +28 -0
- package/src/flow/node.ts +249 -0
- package/src/flow/nodes/index.ts +8 -0
- package/src/flow/nodes/input-node.ts +296 -0
- package/src/flow/nodes/storage-node.ts +128 -0
- package/src/flow/nodes/transform-node.ts +154 -0
- package/src/flow/parallel-scheduler.ts +259 -0
- package/src/flow/plugins/credential-provider.ts +48 -0
- package/src/flow/plugins/image-ai-plugin.ts +66 -0
- package/src/flow/plugins/image-plugin.ts +60 -0
- package/src/flow/plugins/types/describe-image-node.ts +16 -0
- package/src/flow/plugins/types/index.ts +9 -0
- package/src/flow/plugins/types/optimize-node.ts +18 -0
- package/src/flow/plugins/types/remove-background-node.ts +18 -0
- package/src/flow/plugins/types/resize-node.ts +26 -0
- package/src/flow/plugins/zip-plugin.ts +69 -0
- package/src/flow/typed-flow.ts +279 -0
- package/src/flow/types/flow-file.ts +51 -0
- package/src/flow/types/flow-job.ts +138 -0
- package/src/flow/types/flow-types.ts +353 -0
- package/src/flow/types/index.ts +6 -0
- package/src/flow/types/run-args.ts +40 -0
- package/src/flow/types/type-validator.ts +204 -0
- package/src/flow/utils/resolve-upload-metadata.ts +48 -0
- package/src/index.ts +5 -0
- package/src/logger/logger.ts +14 -0
- package/src/streams/stream-limiter.test.ts +150 -0
- package/src/streams/stream-limiter.ts +75 -0
- package/src/types/data-store.ts +427 -0
- package/src/types/event-broadcaster.ts +39 -0
- package/src/types/event-emitter.ts +349 -0
- package/src/types/index.ts +9 -0
- package/src/types/input-file.ts +107 -0
- package/src/types/kv-store.ts +375 -0
- package/src/types/middleware.ts +54 -0
- package/src/types/upload-event.ts +75 -0
- package/src/types/upload-file.ts +139 -0
- package/src/types/websocket.ts +65 -0
- package/src/upload/convert-to-stream.ts +48 -0
- package/src/upload/create-upload.ts +214 -0
- package/src/upload/index.ts +3 -0
- package/src/upload/mime.ts +436 -0
- package/src/upload/upload-chunk.ts +364 -0
- package/src/upload/upload-server.ts +390 -0
- package/src/upload/upload-strategy-negotiator.ts +316 -0
- package/src/upload/upload-url.ts +173 -0
- package/src/upload/write-to-store.ts +211 -0
- package/src/utils/checksum.ts +61 -0
- package/src/utils/debounce.test.ts +126 -0
- package/src/utils/debounce.ts +89 -0
- package/src/utils/generate-id.ts +35 -0
- package/src/utils/md5.ts +475 -0
- package/src/utils/once.test.ts +83 -0
- package/src/utils/once.ts +63 -0
- package/src/utils/throttle.test.ts +101 -0
- package/src/utils/throttle.ts +29 -0
- package/tsconfig.json +20 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsdown.config.ts +25 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { Effect, Metric, MetricBoundaries } from "effect";
|
|
2
|
+
import { UploadistaError } from "../errors/uploadista-error";
|
|
3
|
+
import {
|
|
4
|
+
type DataStore,
|
|
5
|
+
type EventEmitter,
|
|
6
|
+
type KvStore,
|
|
7
|
+
type UploadEvent,
|
|
8
|
+
UploadEventType,
|
|
9
|
+
type UploadFile,
|
|
10
|
+
type UploadFileDataStoresShape,
|
|
11
|
+
} from "../types";
|
|
12
|
+
import { computeChecksum } from "../utils/checksum";
|
|
13
|
+
import { compareMimeTypes, detectMimeType } from "./mime";
|
|
14
|
+
import { writeToStore } from "./write-to-store";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Uploads a chunk of data for an existing upload.
|
|
18
|
+
*
|
|
19
|
+
* This function handles the core chunk upload logic including:
|
|
20
|
+
* - Retrieving upload metadata from KV store
|
|
21
|
+
* - Routing to appropriate data store based on storage ID
|
|
22
|
+
* - Writing chunk data to storage with progress tracking
|
|
23
|
+
* - Updating upload offset and metadata
|
|
24
|
+
* - Emitting progress events
|
|
25
|
+
* - Validating upload completion (checksum, MIME type)
|
|
26
|
+
*
|
|
27
|
+
* The function includes comprehensive observability with:
|
|
28
|
+
* - Effect tracing spans for performance monitoring
|
|
29
|
+
* - Metrics tracking for chunk size, throughput, and success rates
|
|
30
|
+
* - Structured logging for debugging and monitoring
|
|
31
|
+
* - Error handling with proper UploadistaError types
|
|
32
|
+
*
|
|
33
|
+
* @param uploadId - Unique identifier for the upload
|
|
34
|
+
* @param clientId - Client identifier (null for anonymous uploads)
|
|
35
|
+
* @param chunk - ReadableStream containing the chunk data to upload
|
|
36
|
+
* @param dataStoreService - Service for routing to appropriate data stores
|
|
37
|
+
* @param kvStore - KV store for upload metadata persistence
|
|
38
|
+
* @param eventEmitter - Event emitter for progress and validation events
|
|
39
|
+
* @returns Effect that yields the updated UploadFile with new offset
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* // Upload a chunk for an existing upload
|
|
44
|
+
* const uploadChunkEffect = uploadChunk(
|
|
45
|
+
* "upload-123",
|
|
46
|
+
* "client-456",
|
|
47
|
+
* chunkStream,
|
|
48
|
+
* {
|
|
49
|
+
* dataStoreService,
|
|
50
|
+
* kvStore,
|
|
51
|
+
* eventEmitter
|
|
52
|
+
* }
|
|
53
|
+
* );
|
|
54
|
+
*
|
|
55
|
+
* // Run with dependencies
|
|
56
|
+
* const result = await Effect.runPromise(
|
|
57
|
+
* uploadChunkEffect.pipe(
|
|
58
|
+
* Effect.provide(dataStoreLayer),
|
|
59
|
+
* Effect.provide(kvStoreLayer),
|
|
60
|
+
* Effect.provide(eventEmitterLayer)
|
|
61
|
+
* )
|
|
62
|
+
* );
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export const uploadChunk = (
|
|
66
|
+
uploadId: string,
|
|
67
|
+
clientId: string | null,
|
|
68
|
+
chunk: ReadableStream,
|
|
69
|
+
{
|
|
70
|
+
dataStoreService,
|
|
71
|
+
kvStore,
|
|
72
|
+
eventEmitter,
|
|
73
|
+
}: {
|
|
74
|
+
dataStoreService: UploadFileDataStoresShape;
|
|
75
|
+
kvStore: KvStore<UploadFile>;
|
|
76
|
+
eventEmitter: EventEmitter<UploadEvent>;
|
|
77
|
+
}
|
|
78
|
+
) =>
|
|
79
|
+
Effect.gen(function* () {
|
|
80
|
+
// Get file from KV store
|
|
81
|
+
const file = yield* kvStore.get(uploadId);
|
|
82
|
+
|
|
83
|
+
// Get datastore
|
|
84
|
+
const dataStore = yield* dataStoreService.getDataStore(
|
|
85
|
+
file.storage.id,
|
|
86
|
+
clientId
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Note: AbortController could be used for cancellation if needed
|
|
90
|
+
|
|
91
|
+
// Write to store using writeToStore Effect
|
|
92
|
+
const controller = new AbortController();
|
|
93
|
+
|
|
94
|
+
const chunkSize = yield* writeToStore({
|
|
95
|
+
dataStore,
|
|
96
|
+
data: chunk,
|
|
97
|
+
upload: file,
|
|
98
|
+
maxFileSize: 100_000_000,
|
|
99
|
+
controller,
|
|
100
|
+
uploadProgressInterval: 200,
|
|
101
|
+
eventEmitter,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
file.offset = chunkSize;
|
|
105
|
+
|
|
106
|
+
// Update KV store
|
|
107
|
+
yield* kvStore.set(uploadId, file);
|
|
108
|
+
|
|
109
|
+
// Emit progress event
|
|
110
|
+
yield* eventEmitter.emit(file.id, {
|
|
111
|
+
type: UploadEventType.UPLOAD_PROGRESS,
|
|
112
|
+
data: {
|
|
113
|
+
id: file.id,
|
|
114
|
+
progress: file.offset,
|
|
115
|
+
total: file.size ?? 0,
|
|
116
|
+
},
|
|
117
|
+
flow: file.flow,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Check if upload is complete and run validation
|
|
121
|
+
if (file.size && file.offset === file.size) {
|
|
122
|
+
yield* validateUpload({
|
|
123
|
+
file,
|
|
124
|
+
dataStore,
|
|
125
|
+
eventEmitter,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return file;
|
|
130
|
+
}).pipe(
|
|
131
|
+
// Add tracing span for chunk upload
|
|
132
|
+
Effect.withSpan("upload-chunk", {
|
|
133
|
+
attributes: {
|
|
134
|
+
"upload.id": uploadId,
|
|
135
|
+
"chunk.upload_id": uploadId,
|
|
136
|
+
},
|
|
137
|
+
}),
|
|
138
|
+
// Track chunk upload metrics
|
|
139
|
+
Effect.tap((file) =>
|
|
140
|
+
Effect.gen(function* () {
|
|
141
|
+
// Increment chunk uploaded counter
|
|
142
|
+
yield* Metric.increment(
|
|
143
|
+
Metric.counter("chunk_uploaded_total", {
|
|
144
|
+
description: "Total number of chunks uploaded",
|
|
145
|
+
})
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Record chunk size
|
|
149
|
+
const chunkSize = file.offset;
|
|
150
|
+
const chunkSizeHistogram = Metric.histogram(
|
|
151
|
+
"chunk_size_bytes",
|
|
152
|
+
MetricBoundaries.linear({
|
|
153
|
+
start: 262_144,
|
|
154
|
+
width: 262_144,
|
|
155
|
+
count: 20,
|
|
156
|
+
})
|
|
157
|
+
);
|
|
158
|
+
yield* Metric.update(chunkSizeHistogram, chunkSize);
|
|
159
|
+
|
|
160
|
+
// Update throughput gauge
|
|
161
|
+
if (file.size && file.size > 0) {
|
|
162
|
+
const throughput = chunkSize; // bytes processed
|
|
163
|
+
const throughputGauge = Metric.gauge(
|
|
164
|
+
"upload_throughput_bytes_per_second"
|
|
165
|
+
);
|
|
166
|
+
yield* Metric.set(throughputGauge, throughput);
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
),
|
|
170
|
+
// Add structured logging for chunk progress
|
|
171
|
+
Effect.tap((file) =>
|
|
172
|
+
Effect.logDebug("Chunk uploaded").pipe(
|
|
173
|
+
Effect.annotateLogs({
|
|
174
|
+
"upload.id": file.id,
|
|
175
|
+
"chunk.size": file.offset.toString(),
|
|
176
|
+
"chunk.progress":
|
|
177
|
+
file.size && file.size > 0
|
|
178
|
+
? ((file.offset / file.size) * 100).toFixed(2)
|
|
179
|
+
: "0",
|
|
180
|
+
"upload.total_size": file.size?.toString() ?? "0",
|
|
181
|
+
})
|
|
182
|
+
)
|
|
183
|
+
),
|
|
184
|
+
// Handle errors with logging
|
|
185
|
+
Effect.tapError((error) =>
|
|
186
|
+
Effect.logError("Chunk upload failed").pipe(
|
|
187
|
+
Effect.annotateLogs({
|
|
188
|
+
"upload.id": uploadId,
|
|
189
|
+
error: String(error),
|
|
190
|
+
})
|
|
191
|
+
)
|
|
192
|
+
)
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Validates an upload after completion.
|
|
197
|
+
*
|
|
198
|
+
* Performs comprehensive validation including:
|
|
199
|
+
* - Checksum validation (if provided) using the specified algorithm
|
|
200
|
+
* - MIME type validation (if required by data store capabilities)
|
|
201
|
+
* - File size validation against data store limits
|
|
202
|
+
*
|
|
203
|
+
* Validation results are emitted as events and failures result in:
|
|
204
|
+
* - Cleanup of uploaded data from storage
|
|
205
|
+
* - Removal of metadata from KV store
|
|
206
|
+
* - Appropriate error responses
|
|
207
|
+
*
|
|
208
|
+
* The function respects data store capabilities for validation limits
|
|
209
|
+
* and provides detailed error information for debugging.
|
|
210
|
+
*
|
|
211
|
+
* @param file - The upload file to validate
|
|
212
|
+
* @param dataStore - Data store containing the uploaded file
|
|
213
|
+
* @param eventEmitter - Event emitter for validation events
|
|
214
|
+
* @returns Effect that completes validation or fails with UploadistaError
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```typescript
|
|
218
|
+
* // Validate upload after completion
|
|
219
|
+
* const validationEffect = validateUpload({
|
|
220
|
+
* file: completedUpload,
|
|
221
|
+
* dataStore: s3DataStore,
|
|
222
|
+
* eventEmitter: progressEmitter
|
|
223
|
+
* });
|
|
224
|
+
*
|
|
225
|
+
* // Run validation
|
|
226
|
+
* await Effect.runPromise(validationEffect);
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
const validateUpload = ({
|
|
230
|
+
file,
|
|
231
|
+
dataStore,
|
|
232
|
+
eventEmitter,
|
|
233
|
+
}: {
|
|
234
|
+
file: UploadFile;
|
|
235
|
+
dataStore: DataStore<UploadFile>;
|
|
236
|
+
eventEmitter: EventEmitter<UploadEvent>;
|
|
237
|
+
}): Effect.Effect<void, UploadistaError, never> =>
|
|
238
|
+
Effect.gen(function* () {
|
|
239
|
+
const capabilities = dataStore.getCapabilities();
|
|
240
|
+
|
|
241
|
+
// Check if file exceeds max validation size
|
|
242
|
+
if (
|
|
243
|
+
capabilities.maxValidationSize &&
|
|
244
|
+
file.size &&
|
|
245
|
+
file.size > capabilities.maxValidationSize
|
|
246
|
+
) {
|
|
247
|
+
yield* eventEmitter.emit(file.id, {
|
|
248
|
+
type: UploadEventType.UPLOAD_VALIDATION_WARNING,
|
|
249
|
+
data: {
|
|
250
|
+
id: file.id,
|
|
251
|
+
message: `File size (${file.size} bytes) exceeds max validation size (${capabilities.maxValidationSize} bytes). Validation skipped.`,
|
|
252
|
+
},
|
|
253
|
+
flow: file.flow,
|
|
254
|
+
});
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Read file from datastore for validation
|
|
259
|
+
const fileBytes = yield* dataStore.read(file.id);
|
|
260
|
+
|
|
261
|
+
// Validate checksum if provided
|
|
262
|
+
if (file.checksum && file.checksumAlgorithm) {
|
|
263
|
+
const computedChecksum = yield* computeChecksum(
|
|
264
|
+
fileBytes,
|
|
265
|
+
file.checksumAlgorithm
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
if (computedChecksum !== file.checksum) {
|
|
269
|
+
// Emit validation failure event
|
|
270
|
+
yield* eventEmitter.emit(file.id, {
|
|
271
|
+
type: UploadEventType.UPLOAD_VALIDATION_FAILED,
|
|
272
|
+
data: {
|
|
273
|
+
id: file.id,
|
|
274
|
+
reason: "checksum_mismatch",
|
|
275
|
+
expected: file.checksum,
|
|
276
|
+
actual: computedChecksum,
|
|
277
|
+
},
|
|
278
|
+
flow: file.flow,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Clean up file and remove from KV store
|
|
282
|
+
yield* dataStore.remove(file.id);
|
|
283
|
+
|
|
284
|
+
// Fail with checksum mismatch error
|
|
285
|
+
return yield* UploadistaError.fromCode("CHECKSUM_MISMATCH", {
|
|
286
|
+
body: `Checksum validation failed. Expected: ${file.checksum}, Got: ${computedChecksum}`,
|
|
287
|
+
details: {
|
|
288
|
+
uploadId: file.id,
|
|
289
|
+
expected: file.checksum,
|
|
290
|
+
actual: computedChecksum,
|
|
291
|
+
algorithm: file.checksumAlgorithm,
|
|
292
|
+
},
|
|
293
|
+
}).toEffect();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Emit checksum validation success
|
|
297
|
+
yield* eventEmitter.emit(file.id, {
|
|
298
|
+
type: UploadEventType.UPLOAD_VALIDATION_SUCCESS,
|
|
299
|
+
data: {
|
|
300
|
+
id: file.id,
|
|
301
|
+
validationType: "checksum",
|
|
302
|
+
algorithm: file.checksumAlgorithm,
|
|
303
|
+
},
|
|
304
|
+
flow: file.flow,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Validate MIME type if required by capabilities
|
|
309
|
+
if (capabilities.requiresMimeTypeValidation) {
|
|
310
|
+
const detectedMimeType = detectMimeType(fileBytes);
|
|
311
|
+
const declaredMimeType = file.metadata?.type as string | undefined;
|
|
312
|
+
|
|
313
|
+
if (
|
|
314
|
+
declaredMimeType &&
|
|
315
|
+
!compareMimeTypes(declaredMimeType, detectedMimeType)
|
|
316
|
+
) {
|
|
317
|
+
// Emit validation failure event
|
|
318
|
+
yield* eventEmitter.emit(file.id, {
|
|
319
|
+
type: UploadEventType.UPLOAD_VALIDATION_FAILED,
|
|
320
|
+
data: {
|
|
321
|
+
id: file.id,
|
|
322
|
+
reason: "mimetype_mismatch",
|
|
323
|
+
expected: declaredMimeType,
|
|
324
|
+
actual: detectedMimeType,
|
|
325
|
+
},
|
|
326
|
+
flow: file.flow,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Clean up file and remove from KV store
|
|
330
|
+
yield* dataStore.remove(file.id);
|
|
331
|
+
|
|
332
|
+
// Fail with MIME type mismatch error
|
|
333
|
+
return yield* UploadistaError.fromCode("MIMETYPE_MISMATCH", {
|
|
334
|
+
body: `MIME type validation failed. Expected: ${declaredMimeType}, Detected: ${detectedMimeType}`,
|
|
335
|
+
details: {
|
|
336
|
+
uploadId: file.id,
|
|
337
|
+
expected: declaredMimeType,
|
|
338
|
+
actual: detectedMimeType,
|
|
339
|
+
},
|
|
340
|
+
}).toEffect();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Emit MIME type validation success
|
|
344
|
+
yield* eventEmitter.emit(file.id, {
|
|
345
|
+
type: UploadEventType.UPLOAD_VALIDATION_SUCCESS,
|
|
346
|
+
data: {
|
|
347
|
+
id: file.id,
|
|
348
|
+
validationType: "mimetype",
|
|
349
|
+
},
|
|
350
|
+
flow: file.flow,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}).pipe(
|
|
354
|
+
Effect.withSpan("validate-upload", {
|
|
355
|
+
attributes: {
|
|
356
|
+
"upload.id": file.id,
|
|
357
|
+
"validation.checksum_provided": file.checksum ? "true" : "false",
|
|
358
|
+
"validation.mime_required": dataStore.getCapabilities()
|
|
359
|
+
.requiresMimeTypeValidation
|
|
360
|
+
? "true"
|
|
361
|
+
: "false",
|
|
362
|
+
},
|
|
363
|
+
})
|
|
364
|
+
);
|