@tranquilload/core 0.1.0 → 0.1.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/.turbo/turbo-build.log +68 -68
- package/CHANGELOG.md +67 -0
- package/dist/errors.cjs +3 -1
- package/dist/errors.d.cts +2 -2
- package/dist/errors.d.mts +2 -2
- package/dist/errors.mjs +2 -2
- package/dist/index-BaeUV_fj.d.cts +139 -0
- package/dist/index-BaeUV_fj.d.cts.map +1 -0
- package/dist/index-bpWq6tje.d.mts +139 -0
- package/dist/index-bpWq6tje.d.mts.map +1 -0
- package/dist/multipart.cjs +80 -12
- package/dist/multipart.cjs.map +1 -1
- package/dist/multipart.d.cts +2 -2
- package/dist/multipart.d.mts +2 -2
- package/dist/multipart.mjs +80 -12
- package/dist/multipart.mjs.map +1 -1
- package/dist/{normalize-callback-BNBZZ1jT.cjs → normalize-callback-BdLtk9jb.cjs} +2 -2
- package/dist/{normalize-callback-BNBZZ1jT.cjs.map → normalize-callback-BdLtk9jb.cjs.map} +1 -1
- package/dist/{normalize-callback-DQ6C4gaV.mjs → normalize-callback-tcZ_nyq5.mjs} +2 -2
- package/dist/{normalize-callback-DQ6C4gaV.mjs.map → normalize-callback-tcZ_nyq5.mjs.map} +1 -1
- package/dist/oneshot.cjs +2 -2
- package/dist/oneshot.d.cts +2 -2
- package/dist/oneshot.d.mts +2 -2
- package/dist/oneshot.mjs +2 -2
- package/dist/progress.d.cts +2 -2
- package/dist/progress.d.mts +2 -2
- package/dist/{upload-error-BUexBh08.cjs → upload-error-BG1dOOl3.cjs} +26 -1
- package/dist/upload-error-BG1dOOl3.cjs.map +1 -0
- package/dist/{upload-error-B2ISUc_k.d.cts → upload-error-DTYVNlaJ.d.cts} +19 -3
- package/dist/{upload-error-B2ISUc_k.d.cts.map → upload-error-DTYVNlaJ.d.cts.map} +1 -1
- package/dist/{upload-error-zDvpxT9X.mjs → upload-error-Dbz_9j81.mjs} +21 -2
- package/dist/upload-error-Dbz_9j81.mjs.map +1 -0
- package/dist/{upload-error-jol-eoDW.d.mts → upload-error-jBco270d.d.mts} +19 -3
- package/dist/{upload-error-jol-eoDW.d.mts.map → upload-error-jBco270d.d.mts.map} +1 -1
- package/dist/{upload-event-C9TOVp5l.d.mts → upload-event-BT_nXgM9.d.cts} +7 -1
- package/dist/upload-event-BT_nXgM9.d.cts.map +1 -0
- package/dist/{upload-event-D77olieX.d.cts → upload-event-DOGbegxa.d.mts} +7 -1
- package/dist/upload-event-DOGbegxa.d.mts.map +1 -0
- package/package.json +1 -1
- package/src/errors/index.ts +2 -0
- package/src/errors/upload-error.test.ts +35 -0
- package/src/errors/upload-error.ts +27 -0
- package/src/multipart/index.test.ts +164 -1
- package/src/multipart/index.ts +96 -5
- package/src/multipart/upload-stream.test.ts +201 -2
- package/src/multipart/upload-stream.ts +160 -16
- package/src/progress/upload-event.ts +6 -0
- package/dist/index-Ch8xM6Xt.d.cts +0 -60
- package/dist/index-Ch8xM6Xt.d.cts.map +0 -1
- package/dist/index-DBGtgXEd.d.mts +0 -60
- package/dist/index-DBGtgXEd.d.mts.map +0 -1
- package/dist/upload-error-BUexBh08.cjs.map +0 -1
- package/dist/upload-error-zDvpxT9X.mjs.map +0 -1
- package/dist/upload-event-C9TOVp5l.d.mts.map +0 -1
- package/dist/upload-event-D77olieX.d.cts.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Cause, Effect, Exit, Option, Ref, Schedule, Stream } from "effect"
|
|
2
2
|
import type { UploadError } from "../errors/upload-error.js"
|
|
3
|
-
import { CircuitOpenError, CompleteUploadError, InitiateUploadError, MaxRetriesExceededError, PartUploadError, ReconcileError } from "../errors/upload-error.js"
|
|
3
|
+
import { CircuitOpenError, CompleteUploadError, InitiateUploadError, MaxRetriesExceededError, PartUploadError, ReconcileError, ResumeMismatchError } from "../errors/upload-error.js"
|
|
4
4
|
import type { CircuitOpen, PartCompleted, ProgressTick, UploadCompleted, UploadEvent, UploadInitiated } from "../progress/upload-event.js"
|
|
5
5
|
import { LoggerService } from "../services/logger-service.js"
|
|
6
6
|
import { fromAbortSignal } from "../utils/abort-interop.js"
|
|
@@ -13,6 +13,35 @@ export interface CompletedPart {
|
|
|
13
13
|
readonly etag: string
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Opaque resume metadata returned by `uploadMultipart` and persisted by the
|
|
18
|
+
* caller (typically `JSON.stringify` → localStorage). Pass it back as
|
|
19
|
+
* `resumeFrom` on the next session.
|
|
20
|
+
*
|
|
21
|
+
* The lib validates `version`, `chunkSize`, `pipelineIdentity`, and the content
|
|
22
|
+
* digest before any byte is uploaded. A mismatch fails the upload with a typed
|
|
23
|
+
* `ResumeMismatchError` — preventing the silent-corruption classes documented
|
|
24
|
+
* in the v0.2.x release notes.
|
|
25
|
+
*
|
|
26
|
+
* **Schema versioning.** The `version: 1` literal is a tripwire for schema
|
|
27
|
+
* evolution: future v2 schemas widen this union, and a persisted v1 state
|
|
28
|
+
* passed to a future v2 lib will fail with `ResumeMismatchError("version_mismatch")`
|
|
29
|
+
* rather than silently misinterpreting old fields.
|
|
30
|
+
*/
|
|
31
|
+
export interface ResumeState {
|
|
32
|
+
readonly version: 1
|
|
33
|
+
readonly uploadId: string
|
|
34
|
+
readonly chunkSize: number
|
|
35
|
+
readonly pipelineIdentity?: string
|
|
36
|
+
readonly contentDigest?: string
|
|
37
|
+
/**
|
|
38
|
+
* True if the original session captured a digest. Detects persistence layers
|
|
39
|
+
* that drop the `contentDigest` field (which would otherwise silently bypass
|
|
40
|
+
* content-mismatch validation).
|
|
41
|
+
*/
|
|
42
|
+
readonly contentDigestCaptured: boolean
|
|
43
|
+
}
|
|
44
|
+
|
|
16
45
|
export interface UploadMultipartOptions {
|
|
17
46
|
readonly stream: ReadableStream<Uint8Array>
|
|
18
47
|
readonly chunkSize: number
|
|
@@ -32,6 +61,59 @@ export interface UploadMultipartOptions {
|
|
|
32
61
|
| ReadonlyArray<CompletedPart>
|
|
33
62
|
| Promise<ReadonlyArray<CompletedPart>>
|
|
34
63
|
| Effect.Effect<ReadonlyArray<CompletedPart>, UploadError>
|
|
64
|
+
/**
|
|
65
|
+
* Resume metadata persisted from a previous session. When set, the lib skips
|
|
66
|
+
* the `initiate` callback (the `uploadId` is read from `resumeFrom`) and
|
|
67
|
+
* validates `version`, `chunkSize`, `pipelineIdentity`, and `contentDigest`
|
|
68
|
+
* before any byte is uploaded. A mismatch fails the upload with
|
|
69
|
+
* `ResumeMismatchError`.
|
|
70
|
+
*
|
|
71
|
+
* Synchronous (pre-flight) validation happens at `uploadMultipart()` call
|
|
72
|
+
* time — `TypeError` for an empty `uploadId`, `ResumeMismatchError` for the
|
|
73
|
+
* rest. The asynchronous content-digest *value* match is verified inside
|
|
74
|
+
* the Effect once the upload stream is consumed.
|
|
75
|
+
*/
|
|
76
|
+
readonly resumeFrom?: ResumeState
|
|
77
|
+
/**
|
|
78
|
+
* Called once on fresh initiate to capture a digest of the source content.
|
|
79
|
+
* On a subsequent resume session, called again and compared to
|
|
80
|
+
* `resumeFrom.contentDigest`; a mismatch fails the upload with
|
|
81
|
+
* `ResumeMismatchError("content_mismatch")` before any byte is uploaded.
|
|
82
|
+
*
|
|
83
|
+
* **MUST be lightweight and stable across sessions.** Suggested patterns:
|
|
84
|
+
* - Browser `File`: `` `${name}|${size}|${lastModified}` ``
|
|
85
|
+
* - Node `Readable` from a file: `` `${path}|${stat.size}|${stat.mtimeMs}` ``
|
|
86
|
+
* - Synchronous strings; avoid full-file crypto hashes on the synchronous path.
|
|
87
|
+
*
|
|
88
|
+
* **MUST NOT consume bytes from the source stream** (passed in
|
|
89
|
+
* `options.stream`). The lib calls `getContentDigest` before any chunk is
|
|
90
|
+
* pulled from the source; consuming from the source here will produce a
|
|
91
|
+
* zero-byte upload because no bytes remain for `chunkStream`.
|
|
92
|
+
*/
|
|
93
|
+
readonly getContentDigest?: () =>
|
|
94
|
+
| string
|
|
95
|
+
| Promise<string>
|
|
96
|
+
| Effect.Effect<string, UploadError>
|
|
97
|
+
/**
|
|
98
|
+
* An opaque, stable identifier for the upstream pipeline composition.
|
|
99
|
+
* Captured in `ResumeState` and validated strict-equality on resume.
|
|
100
|
+
* **You own keeping this stable** — if you configure `compress("deflate-raw")`
|
|
101
|
+
* in session A, you must pass the same `pipelineIdentity` on resume.
|
|
102
|
+
*
|
|
103
|
+
* **Strict equality limitation:** a pipeline that is logically identical but
|
|
104
|
+
* produces different identifier strings (e.g. tag bumps, version-stamped
|
|
105
|
+
* strings) triggers `ResumeMismatchError("pipeline_mismatch")`. Pick a stable
|
|
106
|
+
* string (e.g. `"deflate-raw-v1"`) and only change it when the pipeline's
|
|
107
|
+
* *byte-level output* changes.
|
|
108
|
+
*
|
|
109
|
+
* **Compression non-determinism caveat:** even with identical
|
|
110
|
+
* `pipelineIdentity`, a non-deterministic pipeline (e.g. gzip with `mtime`
|
|
111
|
+
* headers, encryption with random salt) produces different bytes per run.
|
|
112
|
+
* Resume against the same uploaded parts only works if the pipeline is
|
|
113
|
+
* byte-deterministic. Verify your pipeline's determinism before relying on
|
|
114
|
+
* this.
|
|
115
|
+
*/
|
|
116
|
+
readonly pipelineIdentity?: string
|
|
35
117
|
readonly maxConcurrency?: number
|
|
36
118
|
readonly signal?: AbortSignal
|
|
37
119
|
readonly retrySchedule?: Schedule.Schedule<unknown, PartUploadError>
|
|
@@ -55,11 +137,43 @@ export const uploadMultipartEffect = (
|
|
|
55
137
|
completeUpload,
|
|
56
138
|
initiate,
|
|
57
139
|
reconcileCompletedParts,
|
|
140
|
+
resumeFrom,
|
|
141
|
+
getContentDigest,
|
|
142
|
+
pipelineIdentity,
|
|
58
143
|
maxConcurrency = DEFAULT_MAX_CONCURRENCY,
|
|
59
144
|
signal,
|
|
60
145
|
retrySchedule = DEFAULT_RETRY_SCHEDULE,
|
|
61
146
|
} = options
|
|
62
147
|
|
|
148
|
+
if (!Number.isFinite(chunkSize) || chunkSize <= 0) {
|
|
149
|
+
throw new TypeError(
|
|
150
|
+
`uploadMultipart: chunkSize must be a positive finite number, got ${chunkSize}`
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (resumeFrom !== undefined) {
|
|
155
|
+
if (typeof resumeFrom.uploadId !== "string" || resumeFrom.uploadId === "") {
|
|
156
|
+
throw new TypeError(
|
|
157
|
+
"uploadMultipart: ResumeState.uploadId must be a non-empty string"
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
if (resumeFrom.version !== 1) {
|
|
161
|
+
throw new ResumeMismatchError("version_mismatch")
|
|
162
|
+
}
|
|
163
|
+
if (resumeFrom.chunkSize !== chunkSize) {
|
|
164
|
+
throw new ResumeMismatchError("chunksize_mismatch")
|
|
165
|
+
}
|
|
166
|
+
if (resumeFrom.pipelineIdentity !== pipelineIdentity) {
|
|
167
|
+
throw new ResumeMismatchError("pipeline_mismatch")
|
|
168
|
+
}
|
|
169
|
+
if (
|
|
170
|
+
resumeFrom.contentDigestCaptured === true &&
|
|
171
|
+
resumeFrom.contentDigest === undefined
|
|
172
|
+
) {
|
|
173
|
+
throw new ResumeMismatchError("content_mismatch")
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
63
177
|
return Stream.unwrap(
|
|
64
178
|
Effect.gen(function* () {
|
|
65
179
|
const logger = yield* LoggerService
|
|
@@ -67,6 +181,7 @@ export const uploadMultipartEffect = (
|
|
|
67
181
|
const refParts = yield* Ref.make<CompletedPart[]>([])
|
|
68
182
|
const refBytesUploaded = yield* Ref.make(0)
|
|
69
183
|
const refUploadId = yield* Ref.make("")
|
|
184
|
+
const refDigest = yield* Ref.make<Option.Option<string>>(Option.none())
|
|
70
185
|
const breaker = options.circuitBreaker
|
|
71
186
|
? yield* makeCircuitBreaker(options.circuitBreaker)
|
|
72
187
|
: null
|
|
@@ -79,22 +194,51 @@ export const uploadMultipartEffect = (
|
|
|
79
194
|
)
|
|
80
195
|
: new Map()
|
|
81
196
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
197
|
+
const runFreshInit: Effect.Effect<UploadInitiated, UploadError> = Effect.gen(
|
|
198
|
+
function* () {
|
|
199
|
+
const { uploadId } = yield* normalizeCallback(initiate!).pipe(
|
|
200
|
+
Effect.mapError((cause): UploadError => new InitiateUploadError(cause))
|
|
201
|
+
)
|
|
202
|
+
yield* Ref.set(refUploadId, uploadId)
|
|
203
|
+
if (getContentDigest !== undefined) {
|
|
204
|
+
const digest = yield* normalizeCallback(getContentDigest).pipe(
|
|
205
|
+
Effect.mapError((cause): UploadError => new InitiateUploadError(cause))
|
|
206
|
+
)
|
|
207
|
+
yield* Ref.set(refDigest, Option.some(digest))
|
|
208
|
+
}
|
|
209
|
+
const capturedDigest = yield* Ref.get(refDigest)
|
|
210
|
+
return {
|
|
211
|
+
_tag: "UploadInitiated" as const,
|
|
212
|
+
uploadId,
|
|
213
|
+
contentDigest: Option.getOrUndefined(capturedDigest),
|
|
214
|
+
timestamp: Date.now(),
|
|
215
|
+
} satisfies UploadInitiated
|
|
216
|
+
}
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
const runResumeSetup: Effect.Effect<void, UploadError> = Effect.gen(function* () {
|
|
220
|
+
// `resumeFrom` is non-undefined here (checked by setupStream selector below).
|
|
221
|
+
const rf = resumeFrom!
|
|
222
|
+
if (rf.contentDigest !== undefined && getContentDigest !== undefined) {
|
|
223
|
+
const digest = yield* normalizeCallback(getContentDigest).pipe(
|
|
224
|
+
Effect.mapError(
|
|
225
|
+
(cause): UploadError => new ResumeMismatchError("content_mismatch", cause)
|
|
95
226
|
)
|
|
96
227
|
)
|
|
97
|
-
|
|
228
|
+
if (digest !== rf.contentDigest) {
|
|
229
|
+
return yield* Effect.fail(new ResumeMismatchError("content_mismatch"))
|
|
230
|
+
}
|
|
231
|
+
yield* Ref.set(refDigest, Option.some(digest))
|
|
232
|
+
}
|
|
233
|
+
yield* Ref.set(refUploadId, rf.uploadId)
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
const setupStream: Stream.Stream<UploadEvent, UploadError, never> =
|
|
237
|
+
resumeFrom !== undefined
|
|
238
|
+
? Stream.fromEffect(runResumeSetup).pipe(Stream.drain)
|
|
239
|
+
: initiate !== undefined
|
|
240
|
+
? Stream.fromEffect(runFreshInit)
|
|
241
|
+
: Stream.empty
|
|
98
242
|
|
|
99
243
|
const makeUploadOne = (
|
|
100
244
|
partNumber: number,
|
|
@@ -240,7 +384,7 @@ export const uploadMultipartEffect = (
|
|
|
240
384
|
}
|
|
241
385
|
)
|
|
242
386
|
|
|
243
|
-
return Stream.concat(
|
|
387
|
+
return Stream.concat(setupStream, partsStream.pipe(Stream.concat(Stream.fromEffect(finalEffect))))
|
|
244
388
|
})
|
|
245
389
|
)
|
|
246
390
|
}
|
|
@@ -3,6 +3,12 @@ import { Option } from "effect"
|
|
|
3
3
|
export interface UploadInitiated {
|
|
4
4
|
readonly _tag: "UploadInitiated"
|
|
5
5
|
readonly uploadId: string
|
|
6
|
+
/**
|
|
7
|
+
* Populated when `getContentDigest` was provided on the fresh-init path.
|
|
8
|
+
* Carried on the event so the public wrapper can build a complete `ResumeState`
|
|
9
|
+
* without needing access to the internal digest Ref.
|
|
10
|
+
*/
|
|
11
|
+
readonly contentDigest?: string
|
|
6
12
|
readonly timestamp: number
|
|
7
13
|
}
|
|
8
14
|
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { a as PartUploadError, c as UploadError } from "./upload-error-B2ISUc_k.cjs";
|
|
2
|
-
import { n as LoggerService } from "./logger-service-CbN12RhO.cjs";
|
|
3
|
-
import { a as UploadEvent, i as UploadCompleted } from "./upload-event-D77olieX.cjs";
|
|
4
|
-
import { t as Transform } from "./middleware-CYcctmlY.cjs";
|
|
5
|
-
import { Effect, Option, Schedule, Stream } from "effect";
|
|
6
|
-
|
|
7
|
-
//#region src/multipart/circuit-breaker.d.ts
|
|
8
|
-
interface CircuitBreakerConfig {
|
|
9
|
-
readonly threshold: number;
|
|
10
|
-
readonly cooldown: number;
|
|
11
|
-
}
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/multipart/upload-stream.d.ts
|
|
14
|
-
interface CompletedPart {
|
|
15
|
-
readonly partNumber: number;
|
|
16
|
-
readonly etag: string;
|
|
17
|
-
}
|
|
18
|
-
interface UploadMultipartOptions {
|
|
19
|
-
readonly stream: ReadableStream<Uint8Array>;
|
|
20
|
-
readonly chunkSize: number;
|
|
21
|
-
readonly uploadPart: (partNumber: number, chunk: Uint8Array) => string | Promise<string> | Effect.Effect<string, UploadError>;
|
|
22
|
-
readonly completeUpload: (uploadId: string, parts: ReadonlyArray<CompletedPart>) => void | Promise<void> | Effect.Effect<void, UploadError>;
|
|
23
|
-
readonly initiate?: () => {
|
|
24
|
-
uploadId: string;
|
|
25
|
-
} | Promise<{
|
|
26
|
-
uploadId: string;
|
|
27
|
-
}> | Effect.Effect<{
|
|
28
|
-
uploadId: string;
|
|
29
|
-
}, UploadError>;
|
|
30
|
-
readonly reconcileCompletedParts?: () => ReadonlyArray<CompletedPart> | Promise<ReadonlyArray<CompletedPart>> | Effect.Effect<ReadonlyArray<CompletedPart>, UploadError>;
|
|
31
|
-
readonly maxConcurrency?: number;
|
|
32
|
-
readonly signal?: AbortSignal;
|
|
33
|
-
readonly retrySchedule?: Schedule.Schedule<unknown, PartUploadError>;
|
|
34
|
-
readonly circuitBreaker?: CircuitBreakerConfig;
|
|
35
|
-
}
|
|
36
|
-
//#endregion
|
|
37
|
-
//#region src/multipart/index.d.ts
|
|
38
|
-
type UploadResult = UploadCompleted;
|
|
39
|
-
interface Progress {
|
|
40
|
-
readonly bytesUploaded: number;
|
|
41
|
-
readonly totalBytes: Option.Option<number>;
|
|
42
|
-
}
|
|
43
|
-
interface MultipartPublicOptions extends UploadMultipartOptions {
|
|
44
|
-
readonly totalBytes?: number;
|
|
45
|
-
readonly pipeline?: Transform | Effect.Effect<Transform, unknown, unknown>;
|
|
46
|
-
}
|
|
47
|
-
declare const uploadMultipart: {
|
|
48
|
-
(options: MultipartPublicOptions): {
|
|
49
|
-
events: ReadableStream<UploadEvent>;
|
|
50
|
-
result: Promise<UploadResult>;
|
|
51
|
-
getProgress: (() => Promise<Progress>) & {
|
|
52
|
-
effect: Effect.Effect<Progress>;
|
|
53
|
-
};
|
|
54
|
-
uploadId: Promise<string>;
|
|
55
|
-
};
|
|
56
|
-
effect: (options: UploadMultipartOptions) => Stream.Stream<UploadEvent, UploadError, LoggerService>;
|
|
57
|
-
};
|
|
58
|
-
//#endregion
|
|
59
|
-
export { CompletedPart as a, uploadMultipart as i, Progress as n, UploadMultipartOptions as o, UploadResult as r, MultipartPublicOptions as t };
|
|
60
|
-
//# sourceMappingURL=index-Ch8xM6Xt.d.cts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-Ch8xM6Xt.d.cts","names":[],"sources":["../src/multipart/circuit-breaker.ts","../src/multipart/upload-stream.ts","../src/multipart/index.ts"],"mappings":";;;;;;;UAIiB,oBAAA;EAAA,SACN,SAAA;EAAA,SACA,QAAA;AAAA;;;UCIM,aAAA;EAAA,SACN,UAAA;EAAA,SACA,IAAA;AAAA;AAAA,UAGM,sBAAA;EAAA,SACN,MAAA,EAAQ,cAAA,CAAe,UAAA;EAAA,SACvB,SAAA;EAAA,SACA,UAAA,GACP,UAAA,UACA,KAAA,EAAO,UAAA,cACK,OAAA,WAAkB,MAAA,CAAO,MAAA,SAAe,WAAA;EAAA,SAC7C,cAAA,GACP,QAAA,UACA,KAAA,EAAO,aAAA,CAAc,aAAA,aACX,OAAA,SAAgB,MAAA,CAAO,MAAA,OAAa,WAAA;EAAA,SACvC,QAAA;IACH,QAAA;EAAA,IACF,OAAA;IAAU,QAAA;EAAA,KACV,MAAA,CAAO,MAAA;IAAS,QAAA;EAAA,GAAoB,WAAA;EAAA,SAC/B,uBAAA,SACL,aAAA,CAAc,aAAA,IACd,OAAA,CAAQ,aAAA,CAAc,aAAA,KACtB,MAAA,CAAO,MAAA,CAAO,aAAA,CAAc,aAAA,GAAgB,WAAA;EAAA,SACvC,cAAA;EAAA,SACA,MAAA,GAAS,WAAA;EAAA,SACT,aAAA,GAAgB,QAAA,CAAS,QAAA,UAAkB,eAAA;EAAA,SAC3C,cAAA,GAAiB,oBAAA;AAAA;;;KC9BhB,YAAA,GAAe,eAAA;AAAA,UAGV,QAAA;EAAA,SACN,aAAA;EAAA,SACA,UAAA,EAAY,MAAA,CAAO,MAAA;AAAA;AAAA,UAGb,sBAAA,SAA+B,sBAAA;EAAA,SACrC,UAAA;EAAA,SACA,QAAA,GAAW,SAAA,GAAY,MAAA,CAAO,MAAA,CAAO,SAAA;AAAA;AAAA,cAGnC,eAAA;EAAA,UACF,sBAAA;IAET,MAAA,EAAQ,cAAA,CAAe,WAAA;IACvB,MAAA,EAAQ,OAAA,CAAQ,YAAA;IAChB,WAAA,SAAoB,OAAA,CAAQ,QAAA;MAAe,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,QAAA;IAAA;IACjE,QAAA,EAAU,OAAA;EAAA"}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { a as PartUploadError, c as UploadError } from "./upload-error-jol-eoDW.mjs";
|
|
2
|
-
import { n as LoggerService } from "./logger-service-BF2pZOHN.mjs";
|
|
3
|
-
import { a as UploadEvent, i as UploadCompleted } from "./upload-event-C9TOVp5l.mjs";
|
|
4
|
-
import { t as Transform } from "./middleware-CAI0cnW2.mjs";
|
|
5
|
-
import { Effect, Option, Schedule, Stream } from "effect";
|
|
6
|
-
|
|
7
|
-
//#region src/multipart/circuit-breaker.d.ts
|
|
8
|
-
interface CircuitBreakerConfig {
|
|
9
|
-
readonly threshold: number;
|
|
10
|
-
readonly cooldown: number;
|
|
11
|
-
}
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/multipart/upload-stream.d.ts
|
|
14
|
-
interface CompletedPart {
|
|
15
|
-
readonly partNumber: number;
|
|
16
|
-
readonly etag: string;
|
|
17
|
-
}
|
|
18
|
-
interface UploadMultipartOptions {
|
|
19
|
-
readonly stream: ReadableStream<Uint8Array>;
|
|
20
|
-
readonly chunkSize: number;
|
|
21
|
-
readonly uploadPart: (partNumber: number, chunk: Uint8Array) => string | Promise<string> | Effect.Effect<string, UploadError>;
|
|
22
|
-
readonly completeUpload: (uploadId: string, parts: ReadonlyArray<CompletedPart>) => void | Promise<void> | Effect.Effect<void, UploadError>;
|
|
23
|
-
readonly initiate?: () => {
|
|
24
|
-
uploadId: string;
|
|
25
|
-
} | Promise<{
|
|
26
|
-
uploadId: string;
|
|
27
|
-
}> | Effect.Effect<{
|
|
28
|
-
uploadId: string;
|
|
29
|
-
}, UploadError>;
|
|
30
|
-
readonly reconcileCompletedParts?: () => ReadonlyArray<CompletedPart> | Promise<ReadonlyArray<CompletedPart>> | Effect.Effect<ReadonlyArray<CompletedPart>, UploadError>;
|
|
31
|
-
readonly maxConcurrency?: number;
|
|
32
|
-
readonly signal?: AbortSignal;
|
|
33
|
-
readonly retrySchedule?: Schedule.Schedule<unknown, PartUploadError>;
|
|
34
|
-
readonly circuitBreaker?: CircuitBreakerConfig;
|
|
35
|
-
}
|
|
36
|
-
//#endregion
|
|
37
|
-
//#region src/multipart/index.d.ts
|
|
38
|
-
type UploadResult = UploadCompleted;
|
|
39
|
-
interface Progress {
|
|
40
|
-
readonly bytesUploaded: number;
|
|
41
|
-
readonly totalBytes: Option.Option<number>;
|
|
42
|
-
}
|
|
43
|
-
interface MultipartPublicOptions extends UploadMultipartOptions {
|
|
44
|
-
readonly totalBytes?: number;
|
|
45
|
-
readonly pipeline?: Transform | Effect.Effect<Transform, unknown, unknown>;
|
|
46
|
-
}
|
|
47
|
-
declare const uploadMultipart: {
|
|
48
|
-
(options: MultipartPublicOptions): {
|
|
49
|
-
events: ReadableStream<UploadEvent>;
|
|
50
|
-
result: Promise<UploadResult>;
|
|
51
|
-
getProgress: (() => Promise<Progress>) & {
|
|
52
|
-
effect: Effect.Effect<Progress>;
|
|
53
|
-
};
|
|
54
|
-
uploadId: Promise<string>;
|
|
55
|
-
};
|
|
56
|
-
effect: (options: UploadMultipartOptions) => Stream.Stream<UploadEvent, UploadError, LoggerService>;
|
|
57
|
-
};
|
|
58
|
-
//#endregion
|
|
59
|
-
export { CompletedPart as a, uploadMultipart as i, Progress as n, UploadMultipartOptions as o, UploadResult as r, MultipartPublicOptions as t };
|
|
60
|
-
//# sourceMappingURL=index-DBGtgXEd.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-DBGtgXEd.d.mts","names":[],"sources":["../src/multipart/circuit-breaker.ts","../src/multipart/upload-stream.ts","../src/multipart/index.ts"],"mappings":";;;;;;;UAIiB,oBAAA;EAAA,SACN,SAAA;EAAA,SACA,QAAA;AAAA;;;UCIM,aAAA;EAAA,SACN,UAAA;EAAA,SACA,IAAA;AAAA;AAAA,UAGM,sBAAA;EAAA,SACN,MAAA,EAAQ,cAAA,CAAe,UAAA;EAAA,SACvB,SAAA;EAAA,SACA,UAAA,GACP,UAAA,UACA,KAAA,EAAO,UAAA,cACK,OAAA,WAAkB,MAAA,CAAO,MAAA,SAAe,WAAA;EAAA,SAC7C,cAAA,GACP,QAAA,UACA,KAAA,EAAO,aAAA,CAAc,aAAA,aACX,OAAA,SAAgB,MAAA,CAAO,MAAA,OAAa,WAAA;EAAA,SACvC,QAAA;IACH,QAAA;EAAA,IACF,OAAA;IAAU,QAAA;EAAA,KACV,MAAA,CAAO,MAAA;IAAS,QAAA;EAAA,GAAoB,WAAA;EAAA,SAC/B,uBAAA,SACL,aAAA,CAAc,aAAA,IACd,OAAA,CAAQ,aAAA,CAAc,aAAA,KACtB,MAAA,CAAO,MAAA,CAAO,aAAA,CAAc,aAAA,GAAgB,WAAA;EAAA,SACvC,cAAA;EAAA,SACA,MAAA,GAAS,WAAA;EAAA,SACT,aAAA,GAAgB,QAAA,CAAS,QAAA,UAAkB,eAAA;EAAA,SAC3C,cAAA,GAAiB,oBAAA;AAAA;;;KC9BhB,YAAA,GAAe,eAAA;AAAA,UAGV,QAAA;EAAA,SACN,aAAA;EAAA,SACA,UAAA,EAAY,MAAA,CAAO,MAAA;AAAA;AAAA,UAGb,sBAAA,SAA+B,sBAAA;EAAA,SACrC,UAAA;EAAA,SACA,QAAA,GAAW,SAAA,GAAY,MAAA,CAAO,MAAA,CAAO,SAAA;AAAA;AAAA,cAGnC,eAAA;EAAA,UACF,sBAAA;IAET,MAAA,EAAQ,cAAA,CAAe,WAAA;IACvB,MAAA,EAAQ,OAAA,CAAQ,YAAA;IAChB,WAAA,SAAoB,OAAA,CAAQ,QAAA;MAAe,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,QAAA;IAAA;IACjE,QAAA,EAAU,OAAA;EAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"upload-error-BUexBh08.cjs","names":[],"sources":["../src/errors/upload-error.ts"],"sourcesContent":["export class PartUploadError extends Error {\n readonly _tag = \"PartUploadError\" as const\n\n constructor(\n readonly partNumber: number,\n readonly attempt: number,\n override readonly cause: unknown\n ) {\n super(`Part ${partNumber} failed on attempt ${attempt}`)\n this.name = \"PartUploadError\"\n }\n}\n\nexport class MaxRetriesExceededError extends Error {\n readonly _tag = \"MaxRetriesExceededError\" as const\n\n constructor(\n readonly partNumber: number,\n readonly totalAttempts: number,\n override readonly cause: unknown\n ) {\n super(`Part ${partNumber} failed after ${totalAttempts} attempts`)\n this.name = \"MaxRetriesExceededError\"\n }\n}\n\nexport class PresignedUrlError extends Error {\n readonly _tag = \"PresignedUrlError\" as const\n\n constructor(override readonly cause: unknown) {\n super(\"Failed to obtain pre-signed URL\")\n this.name = \"PresignedUrlError\"\n }\n}\n\nexport class InitiateUploadError extends Error {\n readonly _tag = \"InitiateUploadError\" as const\n\n constructor(override readonly cause: unknown) {\n super(\"Failed to initiate multipart upload\")\n this.name = \"InitiateUploadError\"\n }\n}\n\nexport class ReconcileError extends Error {\n readonly _tag = \"ReconcileError\" as const\n\n constructor(override readonly cause: unknown) {\n super(\"Failed to reconcile completed parts\")\n this.name = \"ReconcileError\"\n }\n}\n\nexport class CompleteUploadError extends Error {\n readonly _tag = \"CompleteUploadError\" as const\n\n constructor(override readonly cause: unknown) {\n super(\"Failed to complete multipart upload\")\n this.name = \"CompleteUploadError\"\n }\n}\n\nexport class AbortError extends Error {\n readonly _tag = \"AbortError\" as const\n\n constructor() {\n super(\"Upload aborted\")\n this.name = \"AbortError\"\n }\n}\n\nexport class CircuitOpenError extends Error {\n readonly _tag = \"CircuitOpenError\" as const\n\n constructor(readonly failedParts: number) {\n super(`Circuit breaker opened after ${failedParts} consecutive part failures`)\n this.name = \"CircuitOpenError\"\n }\n}\n\nexport type UploadError =\n | PartUploadError\n | MaxRetriesExceededError\n | PresignedUrlError\n | InitiateUploadError\n | ReconcileError\n | CompleteUploadError\n | AbortError\n | CircuitOpenError\n"],"mappings":";AAAA,IAAa,kBAAb,cAAqC,MAAM;CACzC,OAAgB;CAEhB,YACE,YACA,SACA,OACA;AACA,QAAM,QAAQ,WAAW,qBAAqB,UAAU;AAJ/C,OAAA,aAAA;AACA,OAAA,UAAA;AACS,OAAA,QAAA;AAGlB,OAAK,OAAO;;;AAIhB,IAAa,0BAAb,cAA6C,MAAM;CACjD,OAAgB;CAEhB,YACE,YACA,eACA,OACA;AACA,QAAM,QAAQ,WAAW,gBAAgB,cAAc,WAAW;AAJzD,OAAA,aAAA;AACA,OAAA,gBAAA;AACS,OAAA,QAAA;AAGlB,OAAK,OAAO;;;AAIhB,IAAa,oBAAb,cAAuC,MAAM;CAC3C,OAAgB;CAEhB,YAAY,OAAkC;AAC5C,QAAM,kCAAkC;AADZ,OAAA,QAAA;AAE5B,OAAK,OAAO;;;AAIhB,IAAa,sBAAb,cAAyC,MAAM;CAC7C,OAAgB;CAEhB,YAAY,OAAkC;AAC5C,QAAM,sCAAsC;AADhB,OAAA,QAAA;AAE5B,OAAK,OAAO;;;AAIhB,IAAa,iBAAb,cAAoC,MAAM;CACxC,OAAgB;CAEhB,YAAY,OAAkC;AAC5C,QAAM,sCAAsC;AADhB,OAAA,QAAA;AAE5B,OAAK,OAAO;;;AAIhB,IAAa,sBAAb,cAAyC,MAAM;CAC7C,OAAgB;CAEhB,YAAY,OAAkC;AAC5C,QAAM,sCAAsC;AADhB,OAAA,QAAA;AAE5B,OAAK,OAAO;;;AAIhB,IAAa,aAAb,cAAgC,MAAM;CACpC,OAAgB;CAEhB,cAAc;AACZ,QAAM,iBAAiB;AACvB,OAAK,OAAO;;;AAIhB,IAAa,mBAAb,cAAsC,MAAM;CAC1C,OAAgB;CAEhB,YAAY,aAA8B;AACxC,QAAM,gCAAgC,YAAY,4BAA4B;AAD3D,OAAA,cAAA;AAEnB,OAAK,OAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"upload-error-zDvpxT9X.mjs","names":[],"sources":["../src/errors/upload-error.ts"],"sourcesContent":["export class PartUploadError extends Error {\n readonly _tag = \"PartUploadError\" as const\n\n constructor(\n readonly partNumber: number,\n readonly attempt: number,\n override readonly cause: unknown\n ) {\n super(`Part ${partNumber} failed on attempt ${attempt}`)\n this.name = \"PartUploadError\"\n }\n}\n\nexport class MaxRetriesExceededError extends Error {\n readonly _tag = \"MaxRetriesExceededError\" as const\n\n constructor(\n readonly partNumber: number,\n readonly totalAttempts: number,\n override readonly cause: unknown\n ) {\n super(`Part ${partNumber} failed after ${totalAttempts} attempts`)\n this.name = \"MaxRetriesExceededError\"\n }\n}\n\nexport class PresignedUrlError extends Error {\n readonly _tag = \"PresignedUrlError\" as const\n\n constructor(override readonly cause: unknown) {\n super(\"Failed to obtain pre-signed URL\")\n this.name = \"PresignedUrlError\"\n }\n}\n\nexport class InitiateUploadError extends Error {\n readonly _tag = \"InitiateUploadError\" as const\n\n constructor(override readonly cause: unknown) {\n super(\"Failed to initiate multipart upload\")\n this.name = \"InitiateUploadError\"\n }\n}\n\nexport class ReconcileError extends Error {\n readonly _tag = \"ReconcileError\" as const\n\n constructor(override readonly cause: unknown) {\n super(\"Failed to reconcile completed parts\")\n this.name = \"ReconcileError\"\n }\n}\n\nexport class CompleteUploadError extends Error {\n readonly _tag = \"CompleteUploadError\" as const\n\n constructor(override readonly cause: unknown) {\n super(\"Failed to complete multipart upload\")\n this.name = \"CompleteUploadError\"\n }\n}\n\nexport class AbortError extends Error {\n readonly _tag = \"AbortError\" as const\n\n constructor() {\n super(\"Upload aborted\")\n this.name = \"AbortError\"\n }\n}\n\nexport class CircuitOpenError extends Error {\n readonly _tag = \"CircuitOpenError\" as const\n\n constructor(readonly failedParts: number) {\n super(`Circuit breaker opened after ${failedParts} consecutive part failures`)\n this.name = \"CircuitOpenError\"\n }\n}\n\nexport type UploadError =\n | PartUploadError\n | MaxRetriesExceededError\n | PresignedUrlError\n | InitiateUploadError\n | ReconcileError\n | CompleteUploadError\n | AbortError\n | CircuitOpenError\n"],"mappings":";AAAA,IAAa,kBAAb,cAAqC,MAAM;CACzC,OAAgB;CAEhB,YACE,YACA,SACA,OACA;AACA,QAAM,QAAQ,WAAW,qBAAqB,UAAU;AAJ/C,OAAA,aAAA;AACA,OAAA,UAAA;AACS,OAAA,QAAA;AAGlB,OAAK,OAAO;;;AAIhB,IAAa,0BAAb,cAA6C,MAAM;CACjD,OAAgB;CAEhB,YACE,YACA,eACA,OACA;AACA,QAAM,QAAQ,WAAW,gBAAgB,cAAc,WAAW;AAJzD,OAAA,aAAA;AACA,OAAA,gBAAA;AACS,OAAA,QAAA;AAGlB,OAAK,OAAO;;;AAIhB,IAAa,oBAAb,cAAuC,MAAM;CAC3C,OAAgB;CAEhB,YAAY,OAAkC;AAC5C,QAAM,kCAAkC;AADZ,OAAA,QAAA;AAE5B,OAAK,OAAO;;;AAIhB,IAAa,sBAAb,cAAyC,MAAM;CAC7C,OAAgB;CAEhB,YAAY,OAAkC;AAC5C,QAAM,sCAAsC;AADhB,OAAA,QAAA;AAE5B,OAAK,OAAO;;;AAIhB,IAAa,iBAAb,cAAoC,MAAM;CACxC,OAAgB;CAEhB,YAAY,OAAkC;AAC5C,QAAM,sCAAsC;AADhB,OAAA,QAAA;AAE5B,OAAK,OAAO;;;AAIhB,IAAa,sBAAb,cAAyC,MAAM;CAC7C,OAAgB;CAEhB,YAAY,OAAkC;AAC5C,QAAM,sCAAsC;AADhB,OAAA,QAAA;AAE5B,OAAK,OAAO;;;AAIhB,IAAa,aAAb,cAAgC,MAAM;CACpC,OAAgB;CAEhB,cAAc;AACZ,QAAM,iBAAiB;AACvB,OAAK,OAAO;;;AAIhB,IAAa,mBAAb,cAAsC,MAAM;CAC1C,OAAgB;CAEhB,YAAY,aAA8B;AACxC,QAAM,gCAAgC,YAAY,4BAA4B;AAD3D,OAAA,cAAA;AAEnB,OAAK,OAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"upload-event-C9TOVp5l.d.mts","names":[],"sources":["../src/progress/upload-event.ts"],"mappings":";;;UAEiB,eAAA;EAAA,SACN,IAAA;EAAA,SACA,QAAA;EAAA,SACA,SAAA;AAAA;AAAA,UAGM,eAAA;EAAA,SACN,IAAA;EAAA,SACA,QAAA;EAAA,SACA,UAAA;EAAA,SACA,SAAA;AAAA;AAAA,UAGM,aAAA;EAAA,SACN,IAAA;EAAA,SACA,UAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;EAAA,SACA,SAAA;AAAA;AAAA,UAGM,WAAA;EAAA,SACN,IAAA;EAAA,SACA,WAAA;EAAA,SACA,SAAA;AAAA;AAAA,UAGM,YAAA;EAAA,SACN,IAAA;EAAA,SACA,aAAA;EAAA,SACA,UAAA,EAAY,MAAA,CAAO,MAAA;EAAA,SACnB,SAAA;AAAA;AAAA,KAGC,WAAA,GAAc,eAAA,GAAkB,eAAA,GAAkB,aAAA,GAAgB,YAAA,GAAe,WAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"upload-event-D77olieX.d.cts","names":[],"sources":["../src/progress/upload-event.ts"],"mappings":";;;UAEiB,eAAA;EAAA,SACN,IAAA;EAAA,SACA,QAAA;EAAA,SACA,SAAA;AAAA;AAAA,UAGM,eAAA;EAAA,SACN,IAAA;EAAA,SACA,QAAA;EAAA,SACA,UAAA;EAAA,SACA,SAAA;AAAA;AAAA,UAGM,aAAA;EAAA,SACN,IAAA;EAAA,SACA,UAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;EAAA,SACA,SAAA;AAAA;AAAA,UAGM,WAAA;EAAA,SACN,IAAA;EAAA,SACA,WAAA;EAAA,SACA,SAAA;AAAA;AAAA,UAGM,YAAA;EAAA,SACN,IAAA;EAAA,SACA,aAAA;EAAA,SACA,UAAA,EAAY,MAAA,CAAO,MAAA;EAAA,SACnB,SAAA;AAAA;AAAA,KAGC,WAAA,GAAc,eAAA,GAAkB,eAAA,GAAkB,aAAA,GAAgB,YAAA,GAAe,WAAA"}
|