@gentleduck/upload 0.2.5 → 0.3.0
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/README.md +297 -0
- package/dist/core/index.d.ts +2 -3
- package/dist/core/index.js +1 -1
- package/dist/core-BVgloTiP.js +10 -0
- package/dist/index-DCqyBVcx.d.ts +1441 -0
- package/dist/index-DsZHPmJm.d.ts +225 -0
- package/dist/index-KROXtLQh.d.ts +281 -0
- package/dist/index.d.ts +4 -5
- package/dist/index.js +1 -1
- package/dist/react/index.d.ts +2 -90
- package/dist/react/index.js +1 -1
- package/dist/react-euPrDv24.js +1 -0
- package/dist/strategies/index.d.ts +2 -56
- package/dist/strategies/index.js +1 -1
- package/dist/strategies-CUF0s68j.js +1 -0
- package/package.json +3 -2
- package/src/__tests__/emitter.test.ts +85 -0
- package/src/__tests__/fingerprint.test.ts +49 -0
- package/src/__tests__/guards.test.ts +40 -0
- package/src/__tests__/id.test.ts +67 -0
- package/src/__tests__/mime-sniff.test.ts +142 -0
- package/src/__tests__/persistence.test.ts +191 -0
- package/src/__tests__/sanitize-filename.test.ts +109 -0
- package/src/__tests__/sleep.test.ts +34 -0
- package/src/__tests__/static-analysis.test.ts +145 -0
- package/src/__tests__/store-libs.test.ts +192 -0
- package/src/__tests__/transport-fetch.test.ts +108 -0
- package/src/__tests__/transport-libs.test.ts +42 -0
- package/src/__tests__/transport.test.ts +182 -0
- package/src/core/__tests__/errors.test.ts +152 -0
- package/src/core/client.ts +3 -0
- package/src/core/contracts/contracts.types.ts +373 -0
- package/src/core/contracts/index.ts +3 -8
- package/src/core/contracts/transport/index.ts +3 -2
- package/src/core/contracts/transport/transport.fetch.ts +203 -0
- package/src/core/contracts/transport/transport.libs.ts +13 -39
- package/src/core/contracts/transport/transport.ts +96 -90
- package/src/core/contracts/transport/transport.types.ts +71 -35
- package/src/core/engine/__tests__/config-nan-bypass.test.ts +40 -0
- package/src/core/engine/engine.libs.ts +35 -0
- package/src/core/engine/engine.types.ts +490 -0
- package/src/core/engine/index.ts +3 -8
- package/src/core/engine/reducer.ts +93 -53
- package/src/core/engine/store/handlers/add-file.ts +43 -48
- package/src/core/engine/store/handlers/cancel.ts +13 -8
- package/src/core/engine/store/handlers/clean-up.ts +11 -8
- package/src/core/engine/store/handlers/create-intent.ts +82 -27
- package/src/core/engine/store/handlers/finalize.ts +30 -13
- package/src/core/engine/store/handlers/index.ts +7 -7
- package/src/core/engine/store/handlers/pause.ts +8 -6
- package/src/core/engine/store/handlers/run-upload.ts +54 -39
- package/src/core/engine/store/index.ts +27 -21
- package/src/core/engine/store/store.dispatch.ts +10 -8
- package/src/core/engine/store/store.libs.ts +197 -94
- package/src/core/engine/store/store.runtime.ts +52 -53
- package/src/core/engine/store/store.schedule.ts +27 -21
- package/src/core/engine/store/store.types.ts +177 -227
- package/src/core/engine/validation/__tests__/intent.test.ts +70 -0
- package/src/core/engine/validation/file.ts +8 -4
- package/src/core/engine/validation/index.ts +1 -0
- package/src/core/engine/validation/intent.ts +49 -27
- package/src/core/engine/validation/list.ts +14 -15
- package/src/core/engine/validation/mime.ts +39 -0
- package/src/core/errors.ts +271 -0
- package/src/core/index.ts +28 -5
- package/src/core/persistence/__tests__/deserialize-nan-bypass.test.ts +100 -0
- package/src/core/persistence/__tests__/persistence.test.ts +85 -0
- package/src/core/persistence/adapters.indexeddb.ts +9 -3
- package/src/core/persistence/adapters.local.ts +9 -3
- package/src/core/persistence/adapters.memory.ts +35 -13
- package/src/core/persistence/index.ts +5 -5
- package/src/core/persistence/persistence.ts +73 -60
- package/src/core/persistence/persistence.types.ts +140 -60
- package/src/core/utils/__tests__/guards.test.ts +93 -0
- package/src/core/utils/__tests__/url-safety.test.ts +112 -0
- package/src/core/utils/async.ts +7 -2
- package/src/core/utils/constants.ts +3 -9
- package/src/core/utils/emitter/emitter.ts +126 -0
- package/src/core/utils/emitter/emitter.types.ts +33 -16
- package/src/core/utils/emitter/index.ts +2 -74
- package/src/core/utils/fingerprint.ts +16 -5
- package/src/core/utils/guards.ts +91 -1
- package/src/core/utils/hash/__tests__/sha256.test.ts +61 -0
- package/src/core/utils/hash/index.ts +126 -0
- package/src/core/utils/hash/sha256.ts +179 -0
- package/src/core/utils/id.ts +33 -1
- package/src/core/utils/index.ts +15 -6
- package/src/core/utils/mime-sniff.ts +119 -0
- package/src/core/utils/sanitize-filename.ts +85 -0
- package/src/core/utils/url-safety.ts +156 -0
- package/src/react/__tests__/use-dropzone.test.ts +64 -0
- package/src/react/index.ts +5 -2
- package/src/react/upload-provider.tsx +59 -95
- package/src/react/uploader.types.ts +80 -0
- package/src/react/use-dropzone.tsx +226 -0
- package/src/react/use-uploader.tsx +97 -93
- package/src/strategies/_shared/__tests__/retry.test.ts +120 -0
- package/src/strategies/_shared/retry.ts +79 -0
- package/src/strategies/index.ts +9 -15
- package/src/strategies/multipart/__tests__/multipart.test.ts +229 -0
- package/src/strategies/multipart/index.ts +211 -116
- package/src/strategies/post/__tests__/post-url-safety.test.ts +119 -0
- package/src/strategies/post/index.ts +113 -71
- package/src/strategies/put/__tests__/put.test.ts +132 -0
- package/src/strategies/put/index.ts +139 -0
- package/src/strategies/registry/index.ts +12 -9
- package/src/strategies/tus/__tests__/tus.test.ts +159 -0
- package/src/strategies/tus/index.ts +211 -0
- package/dist/async-DxRWu4le.js +0 -1
- package/dist/core-BNxznRif.js +0 -2
- package/dist/index-BXg9WIce.d.ts +0 -987
- package/dist/strategy.types-gg9ZStRe.d.ts +0 -225
- package/src/core/client/config.types.ts +0 -77
- package/src/core/client/index.ts +0 -4
- package/src/core/client/plugins.types.ts +0 -46
- package/src/core/client/retry.types.ts +0 -6
- package/src/core/client/uploadClient.ts +0 -19
- package/src/core/contracts/backendApi.ts +0 -62
- package/src/core/contracts/cursor.types.ts +0 -23
- package/src/core/contracts/errors.types.ts +0 -67
- package/src/core/contracts/fingerprint.types.ts +0 -18
- package/src/core/contracts/intent.types.ts +0 -39
- package/src/core/contracts/result.types.ts +0 -11
- package/src/core/contracts/strategy.types.ts +0 -45
- package/src/core/engine/commands.types.ts +0 -17
- package/src/core/engine/event-map.types.ts +0 -45
- package/src/core/engine/internal-events.types.ts +0 -222
- package/src/core/engine/outcome.types.ts +0 -9
- package/src/core/engine/progress.types.ts +0 -15
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
import type { Contracts } from '../contracts/contracts.types'
|
|
2
|
+
import type { UploadError } from '../errors'
|
|
3
|
+
import type { Store } from './store'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Engine-level type surface containing runtime state models, commands, progress,
|
|
7
|
+
* and configuration options.
|
|
8
|
+
* @author wildduck2 <https://github.com/wildduck2>
|
|
9
|
+
*/
|
|
10
|
+
export namespace Engine {
|
|
11
|
+
/**
|
|
12
|
+
* Return value from a retry policy determining if and when a task should be re-scheduled.
|
|
13
|
+
*/
|
|
14
|
+
export type RetryDecision =
|
|
15
|
+
| {
|
|
16
|
+
/** The task has failed terminal or cannot recover. */
|
|
17
|
+
retryable: false
|
|
18
|
+
}
|
|
19
|
+
| {
|
|
20
|
+
/** The task is recoverable and should retry. */
|
|
21
|
+
retryable: true
|
|
22
|
+
/** Delay duration in milliseconds before invoking the next attempt. */
|
|
23
|
+
delayMs: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Global upload engine configuration.
|
|
28
|
+
*
|
|
29
|
+
* Coordinates concurrency limits, retry strategies, constraints, and validation.
|
|
30
|
+
*
|
|
31
|
+
* @typeParam P - Union of allowed `purpose` strings for your app (e.g. `"avatar" | "attachment"`).
|
|
32
|
+
*/
|
|
33
|
+
export type Config<P extends string> = {
|
|
34
|
+
/**
|
|
35
|
+
* Maximum number of parallel uploads active at any time.
|
|
36
|
+
* Queued items wait until active uploads complete or fail.
|
|
37
|
+
*/
|
|
38
|
+
maxConcurrentUploads: number
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Auto-start uploads for specific purposes.
|
|
42
|
+
* - Array form: only files with these purposes automatically begin uploading on registration.
|
|
43
|
+
* - Function form: dynamic callback determining whether to auto-start.
|
|
44
|
+
*/
|
|
45
|
+
autoStart?: (readonly P[] | ((purpose: P) => boolean)) | undefined
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Throttle delay in milliseconds for progress events.
|
|
49
|
+
* Limits rendering updates and state changes under busy progress flows.
|
|
50
|
+
*/
|
|
51
|
+
progressThrottleMs: number
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Validation rules keyed by purpose.
|
|
55
|
+
*/
|
|
56
|
+
validation: Partial<Record<P, Contracts.ValidationRules>>
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Maximum number of retry attempts permitted for upload/intent actions.
|
|
60
|
+
*/
|
|
61
|
+
maxAttempts: number
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Custom retry policy handler.
|
|
65
|
+
* Overrides default exponential backoff calculation.
|
|
66
|
+
*/
|
|
67
|
+
retryPolicy?:
|
|
68
|
+
| ((ctx: {
|
|
69
|
+
/** The active execution phase. */
|
|
70
|
+
phase: 'intent' | 'upload' | 'complete'
|
|
71
|
+
/** Current attempt number (starting at 1). */
|
|
72
|
+
attempt: number
|
|
73
|
+
/** The normalized error structure. */
|
|
74
|
+
error: UploadError
|
|
75
|
+
}) => RetryDecision)
|
|
76
|
+
| undefined
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Maximum number of items to keep in memory/state.
|
|
80
|
+
* When exceeded, oldest completed/canceled items are evicted automatically.
|
|
81
|
+
*
|
|
82
|
+
* Set to `undefined` for no limit (not recommended for long-running single-page apps).
|
|
83
|
+
*/
|
|
84
|
+
maxItems: number
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Duration in milliseconds after which completed items are evicted from the store.
|
|
88
|
+
* Set to `undefined` to preserve completed items indefinitely.
|
|
89
|
+
*/
|
|
90
|
+
completedItemTTL?: number | undefined
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Set to `true` to validate files by sniffing magic bytes/mime signature
|
|
94
|
+
* and rejecting files whose suffix does not match structural magic numbers.
|
|
95
|
+
*/
|
|
96
|
+
strictMimeMatch?: boolean | undefined
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* File-size boundary in bytes between the two checksum paths. At or below
|
|
100
|
+
* it, files hash inline with native Web Crypto (fastest). Above it, files
|
|
101
|
+
* hash incrementally in bounded slices — off the main thread via a Web
|
|
102
|
+
* Worker when available — so large files no longer freeze the UI and are
|
|
103
|
+
* still deduplicated. Set to `null` or `undefined` for the default (64 MiB).
|
|
104
|
+
*/
|
|
105
|
+
checksumMaxSize?: number | null | undefined
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Upload progress snapshot.
|
|
110
|
+
*/
|
|
111
|
+
export type Progress = {
|
|
112
|
+
/** Bytes uploaded so far. */
|
|
113
|
+
uploadedBytes: number
|
|
114
|
+
/** Total bytes to upload. */
|
|
115
|
+
totalBytes: number
|
|
116
|
+
/** Percent complete (0..100). */
|
|
117
|
+
pct: number
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Describes how a `completed` upload item resolved.
|
|
122
|
+
* - `'upload'`: standard byte transfer.
|
|
123
|
+
* - `'dedupe'`: server matched existing file checksum, skipping byte transfer.
|
|
124
|
+
*/
|
|
125
|
+
export type CompletionKind = 'upload' | 'dedupe'
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Terminal outcome returned by `waitFor`.
|
|
129
|
+
*
|
|
130
|
+
* @template R Result type carried on successful completion.
|
|
131
|
+
*/
|
|
132
|
+
export type Outcome<R extends Contracts.Result.Base> =
|
|
133
|
+
| {
|
|
134
|
+
localId: string
|
|
135
|
+
status: 'completed'
|
|
136
|
+
completedBy: CompletionKind
|
|
137
|
+
result: R
|
|
138
|
+
}
|
|
139
|
+
| {
|
|
140
|
+
localId: string
|
|
141
|
+
status: 'error'
|
|
142
|
+
error: UploadError
|
|
143
|
+
}
|
|
144
|
+
| {
|
|
145
|
+
localId: string
|
|
146
|
+
status: 'canceled'
|
|
147
|
+
}
|
|
148
|
+
| {
|
|
149
|
+
localId: string
|
|
150
|
+
status: 'missing'
|
|
151
|
+
/**
|
|
152
|
+
* Why the id is missing.
|
|
153
|
+
* - `'removed'`: explicit user removal.
|
|
154
|
+
* - `'evicted'`: automatically removed (maxItems/TTL rules).
|
|
155
|
+
* - `'never-existed'`: id was never registered.
|
|
156
|
+
* - `'destroyed'`: store was destroyed.
|
|
157
|
+
*/
|
|
158
|
+
reason: 'removed' | 'evicted' | 'never-existed' | 'destroyed'
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Public commands dispatched to the upload store.
|
|
163
|
+
*
|
|
164
|
+
* @template P Purpose discriminator string.
|
|
165
|
+
*/
|
|
166
|
+
export type Command<P extends string> =
|
|
167
|
+
| {
|
|
168
|
+
/** Register files into the upload store for a specific purpose. */
|
|
169
|
+
type: 'addFiles'
|
|
170
|
+
files: File[]
|
|
171
|
+
purpose: P
|
|
172
|
+
/** Optional caller metadata forwarded to every backend API call for this file. */
|
|
173
|
+
meta?: Record<string, unknown> | undefined
|
|
174
|
+
}
|
|
175
|
+
| {
|
|
176
|
+
/** Start uploading a specific file. */
|
|
177
|
+
type: 'start'
|
|
178
|
+
localId: string
|
|
179
|
+
}
|
|
180
|
+
| {
|
|
181
|
+
/** Start all queued files, optionally filtering by purpose. */
|
|
182
|
+
type: 'startAll'
|
|
183
|
+
purpose?: P
|
|
184
|
+
}
|
|
185
|
+
| {
|
|
186
|
+
/** Pause an active upload. */
|
|
187
|
+
type: 'pause'
|
|
188
|
+
localId: string
|
|
189
|
+
}
|
|
190
|
+
| {
|
|
191
|
+
/** Pause all active uploads, optionally filtering by purpose. */
|
|
192
|
+
type: 'pauseAll'
|
|
193
|
+
purpose?: P
|
|
194
|
+
}
|
|
195
|
+
| {
|
|
196
|
+
/** Resume a paused upload. */
|
|
197
|
+
type: 'resume'
|
|
198
|
+
localId: string
|
|
199
|
+
}
|
|
200
|
+
| {
|
|
201
|
+
/** Cancel a pending or active upload. */
|
|
202
|
+
type: 'cancel'
|
|
203
|
+
localId: string
|
|
204
|
+
}
|
|
205
|
+
| {
|
|
206
|
+
/** Cancel all pending and active uploads, optionally filtering by purpose. */
|
|
207
|
+
type: 'cancelAll'
|
|
208
|
+
purpose?: P
|
|
209
|
+
}
|
|
210
|
+
| {
|
|
211
|
+
/** Retry a failed upload item. */
|
|
212
|
+
type: 'retry'
|
|
213
|
+
localId: string
|
|
214
|
+
}
|
|
215
|
+
| {
|
|
216
|
+
/** Re-bind a new browser `File` object to a resumed/paused item missing a file reference. */
|
|
217
|
+
type: 'rebind'
|
|
218
|
+
localId: string
|
|
219
|
+
file: File
|
|
220
|
+
}
|
|
221
|
+
| {
|
|
222
|
+
/** Remove an item from the store state entirely. */
|
|
223
|
+
type: 'remove'
|
|
224
|
+
localId: string
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Const mapping of upload stages.
|
|
229
|
+
*/
|
|
230
|
+
export const Phases = {
|
|
231
|
+
/** Initial validation checks are running. */
|
|
232
|
+
validating: 'validating',
|
|
233
|
+
/** Fetching signed URLs and strategies from backend. */
|
|
234
|
+
creating_intent: 'creating_intent',
|
|
235
|
+
/** Ready to start uploading bytes. */
|
|
236
|
+
ready: 'ready',
|
|
237
|
+
/** Waiting in queue (concurrency cap reached). */
|
|
238
|
+
queued: 'queued',
|
|
239
|
+
/** Actively transferring bytes. */
|
|
240
|
+
uploading: 'uploading',
|
|
241
|
+
/** Upload paused; resumable state preserved. */
|
|
242
|
+
paused: 'paused',
|
|
243
|
+
/** Triggering backend finalization. */
|
|
244
|
+
completing: 'completing',
|
|
245
|
+
/** File is fully completed and registered on backend. */
|
|
246
|
+
completed: 'completed',
|
|
247
|
+
/** Execution hit a terminal error or maximum retry cap. */
|
|
248
|
+
error: 'error',
|
|
249
|
+
/** Upload was canceled by the user. */
|
|
250
|
+
canceled: 'canceled',
|
|
251
|
+
} as const
|
|
252
|
+
|
|
253
|
+
/** Every upload phase; the `phase` discriminator of {@link Item}. */
|
|
254
|
+
export type Phase = keyof typeof Phases
|
|
255
|
+
|
|
256
|
+
interface BaseItem<P extends string> {
|
|
257
|
+
/** Unique client-side identifier for this file item. */
|
|
258
|
+
localId: string
|
|
259
|
+
/** Target upload purpose. */
|
|
260
|
+
purpose: P
|
|
261
|
+
/** Metadata properties identifying the file. */
|
|
262
|
+
fingerprint: Contracts.FingerprintFile
|
|
263
|
+
/** Epoch timestamp in ms when the item was registered. */
|
|
264
|
+
createdAt: number
|
|
265
|
+
attempt?: number | undefined
|
|
266
|
+
file?: File | undefined
|
|
267
|
+
/** Full phase history. Each entry records when a phase was entered/left and any stripped error. */
|
|
268
|
+
steps: Contracts.UploadStep[]
|
|
269
|
+
/** Arbitrary caller-supplied metadata from the addFiles command. Never persisted. */
|
|
270
|
+
meta: Record<string, unknown>
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Representational union of upload items in their respective lifecycle phases.
|
|
275
|
+
*/
|
|
276
|
+
export type Item<
|
|
277
|
+
M extends Contracts.Intent.Map,
|
|
278
|
+
C extends Contracts.Cursor.Map<M>,
|
|
279
|
+
P extends string,
|
|
280
|
+
R extends Contracts.Result.Base = Contracts.Result.Base,
|
|
281
|
+
> = BaseItem<P> &
|
|
282
|
+
(
|
|
283
|
+
| { phase: 'validating'; file: File }
|
|
284
|
+
| { phase: 'creating_intent'; file: File; attempt: number }
|
|
285
|
+
| {
|
|
286
|
+
phase: 'ready'
|
|
287
|
+
file: File
|
|
288
|
+
intent: Contracts.Intent.Any<M>
|
|
289
|
+
cursor?: Contracts.Cursor.Any<C> | undefined
|
|
290
|
+
progress?: Progress | undefined
|
|
291
|
+
}
|
|
292
|
+
| {
|
|
293
|
+
phase: 'queued'
|
|
294
|
+
file: File
|
|
295
|
+
intent: Contracts.Intent.Any<M>
|
|
296
|
+
requestedAt: number
|
|
297
|
+
cursor?: Contracts.Cursor.Any<C> | undefined
|
|
298
|
+
progress?: Progress | undefined
|
|
299
|
+
}
|
|
300
|
+
| {
|
|
301
|
+
phase: 'uploading'
|
|
302
|
+
file: File
|
|
303
|
+
intent: Contracts.Intent.Any<M>
|
|
304
|
+
startedAt: number
|
|
305
|
+
progress: Progress
|
|
306
|
+
cursor?: Contracts.Cursor.Any<C> | undefined
|
|
307
|
+
}
|
|
308
|
+
| {
|
|
309
|
+
phase: 'paused'
|
|
310
|
+
intent: Contracts.Intent.Any<M>
|
|
311
|
+
cursor: Contracts.Cursor.Any<C>
|
|
312
|
+
progress: Progress
|
|
313
|
+
pausedAt: number
|
|
314
|
+
file?: File | undefined
|
|
315
|
+
}
|
|
316
|
+
| { phase: 'completing'; file: File; intent: Contracts.Intent.Any<M>; progress: Progress; completingAt: number }
|
|
317
|
+
| {
|
|
318
|
+
phase: 'completed'
|
|
319
|
+
completedBy: CompletionKind
|
|
320
|
+
result: R
|
|
321
|
+
completedAt: number
|
|
322
|
+
intent?: Contracts.Intent.Any<M> | undefined
|
|
323
|
+
file?: File | undefined
|
|
324
|
+
}
|
|
325
|
+
| {
|
|
326
|
+
phase: 'error'
|
|
327
|
+
error: UploadError
|
|
328
|
+
retryable: boolean
|
|
329
|
+
attempt: number
|
|
330
|
+
failedAt: number
|
|
331
|
+
file?: File | undefined
|
|
332
|
+
intent?: Contracts.Intent.Any<M> | undefined
|
|
333
|
+
cursor?: Contracts.Cursor.Any<C> | undefined
|
|
334
|
+
progress?: Progress | undefined
|
|
335
|
+
}
|
|
336
|
+
| {
|
|
337
|
+
phase: 'canceled'
|
|
338
|
+
canceledAt: number
|
|
339
|
+
file?: File | undefined
|
|
340
|
+
intent?: Contracts.Intent.Any<M> | undefined
|
|
341
|
+
cursor?: Contracts.Cursor.Any<C> | undefined
|
|
342
|
+
progress?: Progress | undefined
|
|
343
|
+
attempt?: number | undefined
|
|
344
|
+
}
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Immutable state shape of the upload engine store.
|
|
349
|
+
*/
|
|
350
|
+
export type State<
|
|
351
|
+
M extends Contracts.Intent.Map,
|
|
352
|
+
C extends Contracts.Cursor.Map<M>,
|
|
353
|
+
P extends string,
|
|
354
|
+
R extends Contracts.Result.Base = Contracts.Result.Base,
|
|
355
|
+
> = {
|
|
356
|
+
/** Map of active and historical upload items. */
|
|
357
|
+
items: Map<string, Item<M, C, P, R>>
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Interface for registering lifecycle plugins.
|
|
362
|
+
*/
|
|
363
|
+
export type Plugin<
|
|
364
|
+
M extends Contracts.Intent.Map,
|
|
365
|
+
C extends Contracts.Cursor.Map<M>,
|
|
366
|
+
P extends string,
|
|
367
|
+
R extends Contracts.Result.Base = Contracts.Result.Base,
|
|
368
|
+
> = {
|
|
369
|
+
/** Name of the plugin (for diagnostics). */
|
|
370
|
+
name: string
|
|
371
|
+
/** Sets up subscription listener binds on the store core proxy. */
|
|
372
|
+
setup(store: Pick<Store.UploadStore<M, C, P, R>, 'on' | 'off' | 'dispatch' | 'getSnapshot'>): void
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Events emitted by effects and consumed by the reducer to transition item states.
|
|
377
|
+
*/
|
|
378
|
+
export type Event<
|
|
379
|
+
M extends Contracts.Intent.Map,
|
|
380
|
+
C extends Contracts.Cursor.Map<M>,
|
|
381
|
+
P extends string,
|
|
382
|
+
R extends Contracts.Result.Base = Contracts.Result.Base,
|
|
383
|
+
> =
|
|
384
|
+
| {
|
|
385
|
+
type: 'files.added'
|
|
386
|
+
items: Array<{
|
|
387
|
+
localId: string
|
|
388
|
+
purpose: P
|
|
389
|
+
file: File
|
|
390
|
+
fingerprint: Contracts.FingerprintFile
|
|
391
|
+
createdAt: number
|
|
392
|
+
meta: Record<string, unknown>
|
|
393
|
+
}>
|
|
394
|
+
}
|
|
395
|
+
| { type: 'fingerprint.updated'; localId: string; fingerprint: Contracts.FingerprintFile }
|
|
396
|
+
| { type: 'validation.ok'; localId: string }
|
|
397
|
+
| { type: 'validation.failed'; localId: string; reason: Contracts.Validation.Rejection }
|
|
398
|
+
| { type: 'intent.ok'; localId: string; intent: Contracts.Intent.Any<M> }
|
|
399
|
+
| { type: 'intent.failed'; localId: string; error: UploadError; retryable: boolean }
|
|
400
|
+
| { type: 'upload.begin'; localId: string; startedAt: number }
|
|
401
|
+
| { type: 'upload.progress'; localId: string; uploadedBytes: number; totalBytes: number }
|
|
402
|
+
| { type: 'cursor.updated'; localId: string; cursor: Contracts.Cursor.Any<C> }
|
|
403
|
+
| { type: 'upload.ok'; localId: string }
|
|
404
|
+
| { type: 'upload.failed'; localId: string; error: UploadError; retryable: boolean }
|
|
405
|
+
| { type: 'dedupe.ok'; localId: string; result: R }
|
|
406
|
+
| { type: 'complete.ok'; localId: string; result: R }
|
|
407
|
+
| { type: 'complete.failed'; localId: string; error: UploadError; retryable: boolean }
|
|
408
|
+
| { type: 'paused'; localId: string; cursor: Contracts.Cursor.Any<C>; pausedAt: number }
|
|
409
|
+
| { type: 'canceled'; localId: string; canceledAt: number }
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Public event registry mapping event names to payloads emitted via `store.on()`.
|
|
413
|
+
*/
|
|
414
|
+
export type EventMap<
|
|
415
|
+
M extends Contracts.Intent.Map,
|
|
416
|
+
C extends Contracts.Cursor.Map<M>,
|
|
417
|
+
P extends string,
|
|
418
|
+
R extends Contracts.Result.Base = Contracts.Result.Base,
|
|
419
|
+
> = {
|
|
420
|
+
/** Emitted when a file is validated and successfully registered in state. */
|
|
421
|
+
'file.added': { localId: string; purpose: P; file: File; fingerprint: Contracts.FingerprintFile }
|
|
422
|
+
/** Emitted when a file fails client-side validation rules. */
|
|
423
|
+
'file.rejected': { file: File; reason: Contracts.Validation.Rejection }
|
|
424
|
+
|
|
425
|
+
/** Emitted when validation succeeds. */
|
|
426
|
+
'validation.ok': { localId: string }
|
|
427
|
+
/** Emitted when validation fails. */
|
|
428
|
+
'validation.failed': { localId: string; reason: Contracts.Validation.Rejection }
|
|
429
|
+
|
|
430
|
+
/** Emitted when intent lookup/creation is requested. */
|
|
431
|
+
'intent.creating': { localId: string }
|
|
432
|
+
/** Emitted when upload intent is successfully created. */
|
|
433
|
+
'intent.created': { localId: string; intent: M[keyof M] }
|
|
434
|
+
/** Emitted when intent creation fails. */
|
|
435
|
+
'intent.failed': { localId: string; error: UploadError; retryable: boolean }
|
|
436
|
+
|
|
437
|
+
/** Emitted when an item is queued waiting for concurrency slots. */
|
|
438
|
+
'upload.queued': { localId: string }
|
|
439
|
+
/** Emitted when a paused upload is resumed. */
|
|
440
|
+
'upload.resumed': { localId: string }
|
|
441
|
+
/** Emitted when data transfer starts. */
|
|
442
|
+
'upload.started': { localId: string }
|
|
443
|
+
/** Periodic progress event. */
|
|
444
|
+
'upload.progress': { localId: string; pct: number; uploadedBytes: number; totalBytes: number }
|
|
445
|
+
/** Emitted when strategy cursor state is updated. */
|
|
446
|
+
'upload.cursor': { localId: string; cursor: Contracts.Cursor.Any<C> }
|
|
447
|
+
/** Emitted when upload is paused. */
|
|
448
|
+
'upload.paused': { localId: string; cursor: Contracts.Cursor.Any<C> }
|
|
449
|
+
/** Emitted when upload is canceled. */
|
|
450
|
+
'upload.canceled': { localId: string }
|
|
451
|
+
|
|
452
|
+
/** Emitted when final completing steps (like completeMultipart or final verification) begin. */
|
|
453
|
+
'upload.completing': { localId: string }
|
|
454
|
+
/** Emitted when a file is completely uploaded and finalized. */
|
|
455
|
+
'upload.completed': { localId: string; result: R; completedBy: CompletionKind }
|
|
456
|
+
|
|
457
|
+
/** Emitted when transfer hits a retryable or terminal error. */
|
|
458
|
+
'upload.error': { localId: string; error: UploadError; retryable: boolean }
|
|
459
|
+
|
|
460
|
+
/** Emitted when an item is removed from the store state. */
|
|
461
|
+
'upload.removed': { localId: string; reason: 'user' | 'cleanup' }
|
|
462
|
+
|
|
463
|
+
/** Emitted when a file rebind operation succeeds. */
|
|
464
|
+
'rebind.ok': { localId: string }
|
|
465
|
+
/** Emitted when a file rebind fails. */
|
|
466
|
+
'rebind.failed': { localId: string; reason: RebindReason }
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Rebind failure classifications.
|
|
471
|
+
*/
|
|
472
|
+
export type RebindReason =
|
|
473
|
+
| { code: 'no_item' }
|
|
474
|
+
| { code: 'wrong_phase'; phase: string }
|
|
475
|
+
| { code: 'already_bound' }
|
|
476
|
+
| { code: 'fingerprint_mismatch'; expected: Contracts.FingerprintFile; got: Contracts.FingerprintFile }
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Diagnostic hooks for observing engine updates.
|
|
480
|
+
*/
|
|
481
|
+
export type Hooks<
|
|
482
|
+
M extends Contracts.Intent.Map,
|
|
483
|
+
C extends Contracts.Cursor.Map<M>,
|
|
484
|
+
P extends string,
|
|
485
|
+
R extends Contracts.Result.Base = Contracts.Result.Base,
|
|
486
|
+
> = {
|
|
487
|
+
/** Callback invoked on every state-reducing internal event. */
|
|
488
|
+
onInternalEvent?: (event: Event<M, C, P, R>, state: State<M, C, P, R>) => void
|
|
489
|
+
}
|
|
490
|
+
}
|
package/src/core/engine/index.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export * from './outcome.types'
|
|
5
|
-
export * from './progress.types'
|
|
6
|
-
export * from './reducer'
|
|
7
|
-
export * from './store'
|
|
8
|
-
export * from './validation'
|
|
1
|
+
export { Engine } from './engine.types'
|
|
2
|
+
export type { Store } from './store'
|
|
3
|
+
export { createUploadStore } from './store'
|