@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,375 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { UploadistaError } from "../errors";
|
|
3
|
+
import type { FlowJob } from "../flow";
|
|
4
|
+
import type { UploadFile } from "./upload-file";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Base key-value store interface for raw string storage.
|
|
8
|
+
*
|
|
9
|
+
* This is the low-level interface that storage adapters implement.
|
|
10
|
+
* It stores raw string values without type safety or serialization.
|
|
11
|
+
*
|
|
12
|
+
* @property get - Retrieves a value by key, returns null if not found
|
|
13
|
+
* @property set - Stores a value with the given key
|
|
14
|
+
* @property delete - Removes a value by key
|
|
15
|
+
* @property list - Optional operation to list all keys with a given prefix
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // Implement a BaseKvStore with Redis
|
|
20
|
+
* const redisKvStore: BaseKvStore = {
|
|
21
|
+
* get: (key) => Effect.tryPromise({
|
|
22
|
+
* try: () => redis.get(key),
|
|
23
|
+
* catch: (error) => UploadistaError.fromCode("UNKNOWN_ERROR", { cause: error })
|
|
24
|
+
* }),
|
|
25
|
+
*
|
|
26
|
+
* set: (key, value) => Effect.tryPromise({
|
|
27
|
+
* try: () => redis.set(key, value),
|
|
28
|
+
* catch: (error) => UploadistaError.fromCode("UNKNOWN_ERROR", { cause: error })
|
|
29
|
+
* }),
|
|
30
|
+
*
|
|
31
|
+
* delete: (key) => Effect.tryPromise({
|
|
32
|
+
* try: () => redis.del(key),
|
|
33
|
+
* catch: (error) => UploadistaError.fromCode("UNKNOWN_ERROR", { cause: error })
|
|
34
|
+
* }),
|
|
35
|
+
*
|
|
36
|
+
* list: (prefix) => Effect.tryPromise({
|
|
37
|
+
* try: () => redis.keys(`${prefix}*`),
|
|
38
|
+
* catch: (error) => UploadistaError.fromCode("UNKNOWN_ERROR", { cause: error })
|
|
39
|
+
* })
|
|
40
|
+
* };
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export interface BaseKvStore {
|
|
44
|
+
readonly get: (key: string) => Effect.Effect<string | null, UploadistaError>;
|
|
45
|
+
readonly set: (
|
|
46
|
+
key: string,
|
|
47
|
+
value: string,
|
|
48
|
+
) => Effect.Effect<void, UploadistaError>;
|
|
49
|
+
readonly delete: (key: string) => Effect.Effect<void, UploadistaError>;
|
|
50
|
+
readonly list?: (
|
|
51
|
+
keyPrefix: string,
|
|
52
|
+
) => Effect.Effect<Array<string>, UploadistaError>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Type-safe key-value store interface with automatic serialization.
|
|
57
|
+
*
|
|
58
|
+
* This wraps a BaseKvStore and handles JSON serialization/deserialization
|
|
59
|
+
* for a specific data type, providing type safety and eliminating the need
|
|
60
|
+
* for manual JSON.stringify/parse calls.
|
|
61
|
+
*
|
|
62
|
+
* @template TData - The type of data stored in this KV store
|
|
63
|
+
*
|
|
64
|
+
* @property get - Retrieves and deserializes a value, fails if not found
|
|
65
|
+
* @property set - Serializes and stores a value
|
|
66
|
+
* @property delete - Removes a value by key
|
|
67
|
+
* @property list - Optional operation to list all keys (without prefix)
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* // Use a typed KV store
|
|
72
|
+
* const uploadStore: KvStore<UploadFile> = new TypedKvStore(
|
|
73
|
+
* baseStore,
|
|
74
|
+
* "uploads:",
|
|
75
|
+
* jsonSerializer.serialize,
|
|
76
|
+
* jsonSerializer.deserialize
|
|
77
|
+
* );
|
|
78
|
+
*
|
|
79
|
+
* // Store and retrieve typed data
|
|
80
|
+
* const program = Effect.gen(function* () {
|
|
81
|
+
* const file: UploadFile = {
|
|
82
|
+
* id: "file123",
|
|
83
|
+
* offset: 0,
|
|
84
|
+
* storage: { id: "s3", type: "s3" }
|
|
85
|
+
* };
|
|
86
|
+
*
|
|
87
|
+
* // Automatic serialization
|
|
88
|
+
* yield* uploadStore.set("file123", file);
|
|
89
|
+
*
|
|
90
|
+
* // Automatic deserialization with type safety
|
|
91
|
+
* const retrieved = yield* uploadStore.get("file123");
|
|
92
|
+
* console.log(retrieved.offset); // TypeScript knows this is a number
|
|
93
|
+
* });
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export type KvStore<TData> = {
|
|
97
|
+
readonly get: (key: string) => Effect.Effect<TData, UploadistaError>;
|
|
98
|
+
readonly set: (
|
|
99
|
+
key: string,
|
|
100
|
+
value: TData,
|
|
101
|
+
) => Effect.Effect<void, UploadistaError>;
|
|
102
|
+
readonly delete: (key: string) => Effect.Effect<void, UploadistaError>;
|
|
103
|
+
readonly list?: () => Effect.Effect<Array<string>, UploadistaError>;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Typed wrapper class that adds serialization to a BaseKvStore.
|
|
108
|
+
*
|
|
109
|
+
* This class implements the KvStore interface by wrapping a BaseKvStore
|
|
110
|
+
* and handling serialization/deserialization for a specific type. It also
|
|
111
|
+
* adds a key prefix to isolate different data types in the same store.
|
|
112
|
+
*
|
|
113
|
+
* @template TData - The type of data to store
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* // Create a typed store for UploadFile
|
|
118
|
+
* const uploadFileStore = new TypedKvStore<UploadFile>(
|
|
119
|
+
* baseKvStore,
|
|
120
|
+
* "uploadista:upload-file:", // All keys will be prefixed
|
|
121
|
+
* (data) => JSON.stringify(data),
|
|
122
|
+
* (str) => JSON.parse(str) as UploadFile
|
|
123
|
+
* );
|
|
124
|
+
*
|
|
125
|
+
* // Use the store
|
|
126
|
+
* const effect = Effect.gen(function* () {
|
|
127
|
+
* const file: UploadFile = { ... };
|
|
128
|
+
* yield* uploadFileStore.set("abc123", file);
|
|
129
|
+
* // Internally stores at key "uploadista:upload-file:abc123"
|
|
130
|
+
*
|
|
131
|
+
* const retrieved = yield* uploadFileStore.get("abc123");
|
|
132
|
+
* return retrieved;
|
|
133
|
+
* });
|
|
134
|
+
*
|
|
135
|
+
* // Custom serialization for binary data
|
|
136
|
+
* const binaryStore = new TypedKvStore<Uint8Array>(
|
|
137
|
+
* baseKvStore,
|
|
138
|
+
* "binary:",
|
|
139
|
+
* (data) => btoa(String.fromCharCode(...data)), // Base64 encode
|
|
140
|
+
* (str) => Uint8Array.from(atob(str), c => c.charCodeAt(0)) // Base64 decode
|
|
141
|
+
* );
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export class TypedKvStore<TData> implements KvStore<TData> {
|
|
145
|
+
constructor(
|
|
146
|
+
private baseStore: BaseKvStore,
|
|
147
|
+
private keyPrefix: string,
|
|
148
|
+
private serialize: (data: TData) => string,
|
|
149
|
+
private deserialize: (str: string) => TData,
|
|
150
|
+
) {}
|
|
151
|
+
|
|
152
|
+
get = (key: string): Effect.Effect<TData, UploadistaError> =>
|
|
153
|
+
this.baseStore.get(this.keyPrefix + key).pipe(
|
|
154
|
+
Effect.flatMap((value) => {
|
|
155
|
+
if (value === null) {
|
|
156
|
+
return Effect.fail(
|
|
157
|
+
UploadistaError.fromCode("FILE_NOT_FOUND", {
|
|
158
|
+
cause: `Key "${key}" not found`,
|
|
159
|
+
}),
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
return Effect.succeed(this.deserialize(value));
|
|
164
|
+
} catch (error) {
|
|
165
|
+
return Effect.fail(
|
|
166
|
+
new UploadistaError({
|
|
167
|
+
code: "VALIDATION_ERROR",
|
|
168
|
+
status: 400,
|
|
169
|
+
body: `Failed to deserialize value for key "${key}"`,
|
|
170
|
+
cause: error,
|
|
171
|
+
}),
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
}),
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
set = (key: string, value: TData): Effect.Effect<void, UploadistaError> => {
|
|
178
|
+
try {
|
|
179
|
+
const serialized = this.serialize(value);
|
|
180
|
+
return this.baseStore.set(this.keyPrefix + key, serialized);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
return Effect.fail(
|
|
183
|
+
new UploadistaError({
|
|
184
|
+
code: "VALIDATION_ERROR",
|
|
185
|
+
status: 400,
|
|
186
|
+
body: `Failed to serialize value for key "${key}"`,
|
|
187
|
+
cause: error,
|
|
188
|
+
}),
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
delete = (key: string): Effect.Effect<void, UploadistaError> =>
|
|
194
|
+
this.baseStore.delete(this.keyPrefix + key);
|
|
195
|
+
|
|
196
|
+
list = (): Effect.Effect<Array<string>, UploadistaError> => {
|
|
197
|
+
if (this.baseStore.list) {
|
|
198
|
+
return this.baseStore.list(this.keyPrefix);
|
|
199
|
+
}
|
|
200
|
+
return Effect.fail(
|
|
201
|
+
new UploadistaError({
|
|
202
|
+
code: "UNKNOWN_ERROR",
|
|
203
|
+
status: 501,
|
|
204
|
+
body: "List operation not supported by this store",
|
|
205
|
+
}),
|
|
206
|
+
);
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Default JSON serialization helpers.
|
|
212
|
+
*
|
|
213
|
+
* These functions provide standard JSON serialization for use with TypedKvStore.
|
|
214
|
+
* They work with any JSON-serializable type.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```typescript
|
|
218
|
+
* const store = new TypedKvStore<MyType>(
|
|
219
|
+
* baseStore,
|
|
220
|
+
* "mydata:",
|
|
221
|
+
* jsonSerializer.serialize,
|
|
222
|
+
* jsonSerializer.deserialize
|
|
223
|
+
* );
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
export const jsonSerializer = {
|
|
227
|
+
serialize: <T>(data: T): string => JSON.stringify(data),
|
|
228
|
+
deserialize: <T>(str: string): T => JSON.parse(str),
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Effect-TS context tag for the base untyped KV store.
|
|
233
|
+
*
|
|
234
|
+
* This is the low-level store that storage adapter implementations provide.
|
|
235
|
+
* Most application code should use typed stores like UploadFileKVStore instead.
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```typescript
|
|
239
|
+
* // Provide a base store implementation
|
|
240
|
+
* const baseStoreLayer = Layer.succeed(BaseKvStoreService, redisKvStore);
|
|
241
|
+
*
|
|
242
|
+
* // Use in an Effect
|
|
243
|
+
* const effect = Effect.gen(function* () {
|
|
244
|
+
* const baseStore = yield* BaseKvStoreService;
|
|
245
|
+
* yield* baseStore.set("raw-key", "raw-value");
|
|
246
|
+
* });
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
export class BaseKvStoreService extends Context.Tag("BaseKvStore")<
|
|
250
|
+
BaseKvStoreService,
|
|
251
|
+
BaseKvStore
|
|
252
|
+
>() {}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Effect-TS context tag for the UploadFile typed KV store.
|
|
256
|
+
*
|
|
257
|
+
* This provides type-safe storage for UploadFile metadata. It's the primary
|
|
258
|
+
* way to store and retrieve upload metadata in the system.
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```typescript
|
|
262
|
+
* const uploadEffect = Effect.gen(function* () {
|
|
263
|
+
* const kvStore = yield* UploadFileKVStore;
|
|
264
|
+
*
|
|
265
|
+
* // Store upload metadata
|
|
266
|
+
* const file: UploadFile = {
|
|
267
|
+
* id: "upload123",
|
|
268
|
+
* offset: 0,
|
|
269
|
+
* storage: { id: "s3", type: "s3" }
|
|
270
|
+
* };
|
|
271
|
+
* yield* kvStore.set("upload123", file);
|
|
272
|
+
*
|
|
273
|
+
* // Retrieve with type safety
|
|
274
|
+
* const retrieved = yield* kvStore.get("upload123");
|
|
275
|
+
* return retrieved;
|
|
276
|
+
* });
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
export class UploadFileKVStore extends Context.Tag("UploadFileKVStore")<
|
|
280
|
+
UploadFileKVStore,
|
|
281
|
+
KvStore<UploadFile>
|
|
282
|
+
>() {}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Effect Layer that creates the UploadFileKVStore from a BaseKvStore.
|
|
286
|
+
*
|
|
287
|
+
* This layer automatically wires up JSON serialization for UploadFile objects
|
|
288
|
+
* with the "uploadista:upload-file:" key prefix.
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* ```typescript
|
|
292
|
+
* const program = Effect.gen(function* () {
|
|
293
|
+
* const kvStore = yield* UploadFileKVStore;
|
|
294
|
+
* // Use the store...
|
|
295
|
+
* }).pipe(
|
|
296
|
+
* Effect.provide(uploadFileKvStore),
|
|
297
|
+
* Effect.provide(baseStoreLayer)
|
|
298
|
+
* );
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
export const uploadFileKvStore = Layer.effect(
|
|
302
|
+
UploadFileKVStore,
|
|
303
|
+
Effect.gen(function* () {
|
|
304
|
+
const baseStore = yield* BaseKvStoreService;
|
|
305
|
+
return new TypedKvStore<UploadFile>(
|
|
306
|
+
baseStore,
|
|
307
|
+
"uploadista:upload-file:",
|
|
308
|
+
jsonSerializer.serialize,
|
|
309
|
+
jsonSerializer.deserialize,
|
|
310
|
+
);
|
|
311
|
+
}),
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Effect-TS context tag for the FlowJob typed KV store.
|
|
316
|
+
*
|
|
317
|
+
* This provides type-safe storage for FlowJob metadata, tracking the
|
|
318
|
+
* execution state of flow processing jobs.
|
|
319
|
+
*
|
|
320
|
+
* @example
|
|
321
|
+
* ```typescript
|
|
322
|
+
* const flowEffect = Effect.gen(function* () {
|
|
323
|
+
* const jobStore = yield* FlowJobKVStore;
|
|
324
|
+
*
|
|
325
|
+
* // Store job state
|
|
326
|
+
* const job: FlowJob = {
|
|
327
|
+
* id: "job123",
|
|
328
|
+
* flowId: "flow_resize",
|
|
329
|
+
* status: "running",
|
|
330
|
+
* tasks: [],
|
|
331
|
+
* createdAt: new Date(),
|
|
332
|
+
* updatedAt: new Date()
|
|
333
|
+
* };
|
|
334
|
+
* yield* jobStore.set("job123", job);
|
|
335
|
+
*
|
|
336
|
+
* // Retrieve and check status
|
|
337
|
+
* const retrieved = yield* jobStore.get("job123");
|
|
338
|
+
* return retrieved.status;
|
|
339
|
+
* });
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
export class FlowJobKVStore extends Context.Tag("FlowJobKVStore")<
|
|
343
|
+
FlowJobKVStore,
|
|
344
|
+
KvStore<FlowJob>
|
|
345
|
+
>() {}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Effect Layer that creates the FlowJobKVStore from a BaseKvStore.
|
|
349
|
+
*
|
|
350
|
+
* This layer automatically wires up JSON serialization for FlowJob objects
|
|
351
|
+
* with the "uploadista:flow-job:" key prefix.
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* ```typescript
|
|
355
|
+
* const program = Effect.gen(function* () {
|
|
356
|
+
* const jobStore = yield* FlowJobKVStore;
|
|
357
|
+
* // Use the store...
|
|
358
|
+
* }).pipe(
|
|
359
|
+
* Effect.provide(flowJobKvStore),
|
|
360
|
+
* Effect.provide(baseStoreLayer)
|
|
361
|
+
* );
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
export const flowJobKvStore = Layer.effect(
|
|
365
|
+
FlowJobKVStore,
|
|
366
|
+
Effect.gen(function* () {
|
|
367
|
+
const baseStore = yield* BaseKvStoreService;
|
|
368
|
+
return new TypedKvStore<FlowJob>(
|
|
369
|
+
baseStore,
|
|
370
|
+
"uploadista:flow-job:",
|
|
371
|
+
jsonSerializer.serialize,
|
|
372
|
+
jsonSerializer.deserialize,
|
|
373
|
+
);
|
|
374
|
+
}),
|
|
375
|
+
);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import type { UploadistaError } from "../errors";
|
|
3
|
+
|
|
4
|
+
export type MiddlewareContext = {
|
|
5
|
+
request: Request;
|
|
6
|
+
uploadId?: string;
|
|
7
|
+
metadata?: Record<string, string>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type MiddlewareNext = () => Promise<Response>;
|
|
11
|
+
|
|
12
|
+
export type Middleware = (
|
|
13
|
+
context: MiddlewareContext,
|
|
14
|
+
next: MiddlewareNext,
|
|
15
|
+
) => Promise<Response>;
|
|
16
|
+
|
|
17
|
+
// Effect-based Middleware service
|
|
18
|
+
export class MiddlewareService extends Context.Tag("MiddlewareService")<
|
|
19
|
+
MiddlewareService,
|
|
20
|
+
{
|
|
21
|
+
readonly execute: (
|
|
22
|
+
middlewares: Middleware[],
|
|
23
|
+
context: MiddlewareContext,
|
|
24
|
+
handler: MiddlewareNext,
|
|
25
|
+
) => Effect.Effect<Response, UploadistaError>;
|
|
26
|
+
}
|
|
27
|
+
>() {}
|
|
28
|
+
|
|
29
|
+
export const MiddlewareServiceLive = Layer.succeed(
|
|
30
|
+
MiddlewareService,
|
|
31
|
+
MiddlewareService.of({
|
|
32
|
+
execute: (middlewares, context, handler) =>
|
|
33
|
+
Effect.gen(function* () {
|
|
34
|
+
if (middlewares.length === 0) {
|
|
35
|
+
return yield* Effect.tryPromise({
|
|
36
|
+
try: () => handler(),
|
|
37
|
+
catch: (error) => error as UploadistaError,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const chain = middlewares.reduceRight(
|
|
42
|
+
(next: MiddlewareNext, middleware: Middleware) => {
|
|
43
|
+
return () => middleware(context, next);
|
|
44
|
+
},
|
|
45
|
+
handler,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return yield* Effect.tryPromise({
|
|
49
|
+
try: () => chain(),
|
|
50
|
+
catch: (error) => error as UploadistaError,
|
|
51
|
+
});
|
|
52
|
+
}),
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { uploadFileSchema } from "./upload-file";
|
|
3
|
+
|
|
4
|
+
export enum UploadEventType {
|
|
5
|
+
UPLOAD_STARTED = "upload-started",
|
|
6
|
+
UPLOAD_PROGRESS = "upload-progress",
|
|
7
|
+
UPLOAD_COMPLETE = "upload-complete",
|
|
8
|
+
UPLOAD_FAILED = "upload-failed",
|
|
9
|
+
UPLOAD_VALIDATION_SUCCESS = "upload-validation-success",
|
|
10
|
+
UPLOAD_VALIDATION_FAILED = "upload-validation-failed",
|
|
11
|
+
UPLOAD_VALIDATION_WARNING = "upload-validation-warning",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const flowContextSchema = z.object({
|
|
15
|
+
flowId: z.string(),
|
|
16
|
+
nodeId: z.string(),
|
|
17
|
+
jobId: z.string(),
|
|
18
|
+
}).optional();
|
|
19
|
+
|
|
20
|
+
export const uploadEventSchema = z.union([
|
|
21
|
+
z.object({
|
|
22
|
+
type: z.union([
|
|
23
|
+
z.literal(UploadEventType.UPLOAD_STARTED),
|
|
24
|
+
z.literal(UploadEventType.UPLOAD_COMPLETE),
|
|
25
|
+
]),
|
|
26
|
+
data: uploadFileSchema,
|
|
27
|
+
flow: flowContextSchema,
|
|
28
|
+
}),
|
|
29
|
+
z.object({
|
|
30
|
+
type: z.literal(UploadEventType.UPLOAD_PROGRESS),
|
|
31
|
+
data: z.object({
|
|
32
|
+
id: z.string(),
|
|
33
|
+
progress: z.number(),
|
|
34
|
+
total: z.number(),
|
|
35
|
+
}),
|
|
36
|
+
flow: flowContextSchema,
|
|
37
|
+
}),
|
|
38
|
+
z.object({
|
|
39
|
+
type: z.literal(UploadEventType.UPLOAD_FAILED),
|
|
40
|
+
data: z.object({
|
|
41
|
+
id: z.string(),
|
|
42
|
+
error: z.string(),
|
|
43
|
+
}),
|
|
44
|
+
flow: flowContextSchema,
|
|
45
|
+
}),
|
|
46
|
+
z.object({
|
|
47
|
+
type: z.literal(UploadEventType.UPLOAD_VALIDATION_SUCCESS),
|
|
48
|
+
data: z.object({
|
|
49
|
+
id: z.string(),
|
|
50
|
+
validationType: z.enum(["checksum", "mimetype"]),
|
|
51
|
+
algorithm: z.string().optional(),
|
|
52
|
+
}),
|
|
53
|
+
flow: flowContextSchema,
|
|
54
|
+
}),
|
|
55
|
+
z.object({
|
|
56
|
+
type: z.literal(UploadEventType.UPLOAD_VALIDATION_FAILED),
|
|
57
|
+
data: z.object({
|
|
58
|
+
id: z.string(),
|
|
59
|
+
reason: z.string(),
|
|
60
|
+
expected: z.string(),
|
|
61
|
+
actual: z.string(),
|
|
62
|
+
}),
|
|
63
|
+
flow: flowContextSchema,
|
|
64
|
+
}),
|
|
65
|
+
z.object({
|
|
66
|
+
type: z.literal(UploadEventType.UPLOAD_VALIDATION_WARNING),
|
|
67
|
+
data: z.object({
|
|
68
|
+
id: z.string(),
|
|
69
|
+
message: z.string(),
|
|
70
|
+
}),
|
|
71
|
+
flow: flowContextSchema,
|
|
72
|
+
}),
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
export type UploadEvent = z.infer<typeof uploadEventSchema>;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Zod schema for validating UploadFile objects.
|
|
5
|
+
*
|
|
6
|
+
* This schema defines the structure and validation rules for upload file metadata.
|
|
7
|
+
* Use this schema to parse and validate UploadFile data from external sources.
|
|
8
|
+
*
|
|
9
|
+
* @see {@link UploadFile} for the TypeScript type
|
|
10
|
+
*/
|
|
11
|
+
export const uploadFileSchema = z.object({
|
|
12
|
+
id: z.string(),
|
|
13
|
+
size: z.number().optional(),
|
|
14
|
+
offset: z.number(),
|
|
15
|
+
metadata: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
|
|
16
|
+
creationDate: z.string().optional(),
|
|
17
|
+
url: z.string().optional(),
|
|
18
|
+
sizeIsDeferred: z.boolean().optional(),
|
|
19
|
+
checksum: z.string().optional(),
|
|
20
|
+
checksumAlgorithm: z.string().optional(),
|
|
21
|
+
storage: z.object({
|
|
22
|
+
id: z.string(),
|
|
23
|
+
type: z.string(),
|
|
24
|
+
path: z.string().optional(),
|
|
25
|
+
uploadId: z.string().optional(),
|
|
26
|
+
bucket: z.string().optional(),
|
|
27
|
+
}),
|
|
28
|
+
flow: z
|
|
29
|
+
.object({
|
|
30
|
+
flowId: z.string(),
|
|
31
|
+
nodeId: z.string(),
|
|
32
|
+
jobId: z.string(),
|
|
33
|
+
})
|
|
34
|
+
.optional(),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Represents an uploaded file with its metadata and storage information.
|
|
39
|
+
*
|
|
40
|
+
* This is the core data structure that tracks file uploads throughout their lifecycle.
|
|
41
|
+
* It contains all metadata needed to resume uploads, track progress, and locate files
|
|
42
|
+
* in storage backends.
|
|
43
|
+
*
|
|
44
|
+
* @property id - Unique identifier for this upload
|
|
45
|
+
* @property offset - Current byte offset (how many bytes have been uploaded)
|
|
46
|
+
* @property storage - Storage backend information
|
|
47
|
+
* @property storage.id - Storage backend identifier (e.g., "s3-production")
|
|
48
|
+
* @property storage.type - Storage backend type (e.g., "s3", "azure", "gcs")
|
|
49
|
+
* @property storage.path - Optional path prefix within the storage backend
|
|
50
|
+
* @property storage.uploadId - Optional backend-specific upload ID (e.g., S3 multipart upload ID)
|
|
51
|
+
* @property storage.bucket - Optional bucket or container name
|
|
52
|
+
* @property flow - Optional flow processing information (when file is part of a flow)
|
|
53
|
+
* @property flow.flowId - ID of the flow processing this file
|
|
54
|
+
* @property flow.nodeId - ID of the flow node that created this file
|
|
55
|
+
* @property flow.jobId - ID of the flow job execution
|
|
56
|
+
* @property size - Total file size in bytes (undefined if deferred)
|
|
57
|
+
* @property metadata - Custom key-value metadata attached to the file
|
|
58
|
+
* @property creationDate - ISO 8601 timestamp when upload was created
|
|
59
|
+
* @property url - Optional public URL to access the file
|
|
60
|
+
* @property sizeIsDeferred - True if file size is not known at upload start
|
|
61
|
+
* @property checksum - Optional file checksum/hash value
|
|
62
|
+
* @property checksumAlgorithm - Algorithm used for checksum (e.g., "md5", "sha256")
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* // Create an UploadFile for a new upload
|
|
67
|
+
* const uploadFile: UploadFile = {
|
|
68
|
+
* id: "upload_abc123",
|
|
69
|
+
* offset: 0,
|
|
70
|
+
* size: 1024000,
|
|
71
|
+
* storage: {
|
|
72
|
+
* id: "s3-production",
|
|
73
|
+
* type: "s3",
|
|
74
|
+
* bucket: "my-uploads",
|
|
75
|
+
* path: "files/"
|
|
76
|
+
* },
|
|
77
|
+
* metadata: {
|
|
78
|
+
* fileName: "image.jpg",
|
|
79
|
+
* contentType: "image/jpeg",
|
|
80
|
+
* userId: "user_123"
|
|
81
|
+
* },
|
|
82
|
+
* creationDate: new Date().toISOString(),
|
|
83
|
+
* checksum: "5d41402abc4b2a76b9719d911017c592",
|
|
84
|
+
* checksumAlgorithm: "md5"
|
|
85
|
+
* };
|
|
86
|
+
*
|
|
87
|
+
* // UploadFile with flow processing
|
|
88
|
+
* const flowFile: UploadFile = {
|
|
89
|
+
* id: "upload_xyz789",
|
|
90
|
+
* offset: 0,
|
|
91
|
+
* size: 2048000,
|
|
92
|
+
* storage: {
|
|
93
|
+
* id: "s3-temp",
|
|
94
|
+
* type: "s3",
|
|
95
|
+
* bucket: "temp-processing"
|
|
96
|
+
* },
|
|
97
|
+
* flow: {
|
|
98
|
+
* flowId: "flow_resize_optimize",
|
|
99
|
+
* nodeId: "input_1",
|
|
100
|
+
* jobId: "job_456"
|
|
101
|
+
* }
|
|
102
|
+
* };
|
|
103
|
+
*
|
|
104
|
+
* // Resume an interrupted upload
|
|
105
|
+
* const resumingFile: UploadFile = {
|
|
106
|
+
* id: "upload_resume",
|
|
107
|
+
* offset: 524288, // Already uploaded 512KB
|
|
108
|
+
* size: 1024000,
|
|
109
|
+
* storage: {
|
|
110
|
+
* id: "s3-production",
|
|
111
|
+
* type: "s3",
|
|
112
|
+
* uploadId: "multipart_xyz" // S3 multipart upload ID
|
|
113
|
+
* }
|
|
114
|
+
* };
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export type UploadFile = {
|
|
118
|
+
id: string;
|
|
119
|
+
offset: number;
|
|
120
|
+
storage: {
|
|
121
|
+
id: string;
|
|
122
|
+
type: string;
|
|
123
|
+
path?: string | undefined;
|
|
124
|
+
uploadId?: string | undefined;
|
|
125
|
+
bucket?: string | undefined;
|
|
126
|
+
};
|
|
127
|
+
flow?: {
|
|
128
|
+
flowId: string;
|
|
129
|
+
nodeId: string;
|
|
130
|
+
jobId: string;
|
|
131
|
+
};
|
|
132
|
+
size?: number | undefined;
|
|
133
|
+
metadata?: Record<string, string | number | boolean> | undefined;
|
|
134
|
+
creationDate?: string | undefined;
|
|
135
|
+
url?: string | undefined;
|
|
136
|
+
sizeIsDeferred?: boolean | undefined;
|
|
137
|
+
checksum?: string | undefined;
|
|
138
|
+
checksumAlgorithm?: string | undefined;
|
|
139
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
import { uploadEventSchema } from "./upload-event";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Platform-agnostic WebSocket connection interface
|
|
6
|
+
*/
|
|
7
|
+
export interface WebSocketConnection {
|
|
8
|
+
send(data: string): void;
|
|
9
|
+
close(code?: number, reason?: string): void;
|
|
10
|
+
readonly readyState: number;
|
|
11
|
+
readonly id: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* WebSocket message that can be sent/received
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export const webSocketMessageSchema = z.union([
|
|
19
|
+
z.object({
|
|
20
|
+
type: z.literal("upload_event"),
|
|
21
|
+
payload: uploadEventSchema,
|
|
22
|
+
timestamp: z.string().optional(),
|
|
23
|
+
}),
|
|
24
|
+
z.object({
|
|
25
|
+
type: z.literal("flow_event"),
|
|
26
|
+
payload: z.any(), // FlowEvent doesn't have a zod schema, using z.any() for now
|
|
27
|
+
timestamp: z.string().optional(),
|
|
28
|
+
}),
|
|
29
|
+
z.object({
|
|
30
|
+
type: z.literal("subscribed"),
|
|
31
|
+
payload: z.object({ eventKey: z.string() }),
|
|
32
|
+
timestamp: z.string().optional(),
|
|
33
|
+
}),
|
|
34
|
+
z.object({
|
|
35
|
+
type: z.literal("error"),
|
|
36
|
+
message: z.string().optional(),
|
|
37
|
+
}),
|
|
38
|
+
z.object({
|
|
39
|
+
type: z.literal("pong"),
|
|
40
|
+
timestamp: z.string().optional(),
|
|
41
|
+
}),
|
|
42
|
+
z.object({
|
|
43
|
+
type: z.literal("ping"),
|
|
44
|
+
timestamp: z.string().optional(),
|
|
45
|
+
}),
|
|
46
|
+
z.object({
|
|
47
|
+
type: z.literal("connection"),
|
|
48
|
+
message: z.string().optional(),
|
|
49
|
+
uploadId: z.string().optional(),
|
|
50
|
+
timestamp: z.string().optional(),
|
|
51
|
+
}),
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
export type WebSocketMessage<TEvent = unknown> =
|
|
55
|
+
| z.infer<typeof webSocketMessageSchema>
|
|
56
|
+
| {
|
|
57
|
+
type: "upload_event";
|
|
58
|
+
payload: TEvent;
|
|
59
|
+
timestamp?: string;
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
type: "flow_event";
|
|
63
|
+
payload: TEvent;
|
|
64
|
+
timestamp?: string;
|
|
65
|
+
};
|