@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
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AnyCursor,
|
|
3
|
-
CursorMap,
|
|
4
|
-
FileFingerprint,
|
|
5
|
-
IntentMap,
|
|
6
|
-
RejectReason,
|
|
7
|
-
UploadError,
|
|
8
|
-
UploadResultBase,
|
|
9
|
-
} from '../contracts'
|
|
10
|
-
import type { UploadCompletionKind } from './outcome.types'
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Public event map emitted to consumers.
|
|
14
|
-
*
|
|
15
|
-
* Consumers subscribe via {@link TypedEmitter.on}.
|
|
16
|
-
*/
|
|
17
|
-
export type UploadEventMap<
|
|
18
|
-
M extends IntentMap,
|
|
19
|
-
C extends CursorMap<M>,
|
|
20
|
-
P extends string,
|
|
21
|
-
R extends UploadResultBase = UploadResultBase,
|
|
22
|
-
> = {
|
|
23
|
-
'file.added': { localId: string; purpose: P; file: File; fingerprint: FileFingerprint }
|
|
24
|
-
'file.rejected': { file: File; reason: RejectReason }
|
|
25
|
-
|
|
26
|
-
'validation.ok': { localId: string }
|
|
27
|
-
'validation.failed': { localId: string; reason: RejectReason }
|
|
28
|
-
|
|
29
|
-
'intent.creating': { localId: string }
|
|
30
|
-
'intent.created': { localId: string; intent: M[keyof M] }
|
|
31
|
-
'intent.failed': { localId: string; error: UploadError; retryable: boolean }
|
|
32
|
-
|
|
33
|
-
'upload.queued': { localId: string }
|
|
34
|
-
'upload.resumed': { localId: string }
|
|
35
|
-
'upload.started': { localId: string }
|
|
36
|
-
'upload.progress': { localId: string; pct: number; uploadedBytes: number; totalBytes: number }
|
|
37
|
-
'upload.cursor': { localId: string; cursor: AnyCursor<C> }
|
|
38
|
-
'upload.paused': { localId: string; cursor: AnyCursor<C> }
|
|
39
|
-
'upload.canceled': { localId: string }
|
|
40
|
-
|
|
41
|
-
'upload.completing': { localId: string }
|
|
42
|
-
'upload.completed': { localId: string; result: R; completedBy: UploadCompletionKind }
|
|
43
|
-
|
|
44
|
-
'upload.error': { localId: string; error: UploadError; retryable: boolean }
|
|
45
|
-
}
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AnyCursor,
|
|
3
|
-
AnyIntent,
|
|
4
|
-
CursorMap,
|
|
5
|
-
FileFingerprint,
|
|
6
|
-
IntentMap,
|
|
7
|
-
RejectReason,
|
|
8
|
-
UploadError,
|
|
9
|
-
UploadResultBase,
|
|
10
|
-
} from '../contracts'
|
|
11
|
-
import type { UploadCompletionKind } from './outcome.types'
|
|
12
|
-
import type { UploadProgress } from './progress.types'
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Internal events emitted by effects and consumed by the reducer.
|
|
16
|
-
*
|
|
17
|
-
* These are not the same as public events. They are designed for correctness and state transitions.
|
|
18
|
-
*
|
|
19
|
-
* @typeParam M - Intent map
|
|
20
|
-
* @typeParam C - Cursor map
|
|
21
|
-
*/
|
|
22
|
-
export type InternalEvent<
|
|
23
|
-
M extends IntentMap,
|
|
24
|
-
C extends CursorMap<M>,
|
|
25
|
-
P extends string,
|
|
26
|
-
R extends UploadResultBase = UploadResultBase,
|
|
27
|
-
> =
|
|
28
|
-
| {
|
|
29
|
-
type: 'files.added'
|
|
30
|
-
items: Array<{ localId: string; purpose: P; file: File; fingerprint: FileFingerprint; createdAt: number }>
|
|
31
|
-
}
|
|
32
|
-
| { type: 'fingerprint.updated'; localId: string; fingerprint: FileFingerprint }
|
|
33
|
-
| { type: 'validation.ok'; localId: string }
|
|
34
|
-
| { type: 'validation.failed'; localId: string; reason: RejectReason }
|
|
35
|
-
| { type: 'intent.ok'; localId: string; intent: AnyIntent<M> }
|
|
36
|
-
| { type: 'intent.failed'; localId: string; error: UploadError; retryable: boolean }
|
|
37
|
-
| { type: 'upload.begin'; localId: string; startedAt: number }
|
|
38
|
-
| { type: 'upload.progress'; localId: string; uploadedBytes: number; totalBytes: number }
|
|
39
|
-
| { type: 'cursor.updated'; localId: string; cursor: AnyCursor<C> }
|
|
40
|
-
| { type: 'upload.ok'; localId: string }
|
|
41
|
-
| { type: 'upload.failed'; localId: string; error: UploadError; retryable: boolean }
|
|
42
|
-
| { type: 'dedupe.ok'; localId: string; result: R }
|
|
43
|
-
| { type: 'complete.ok'; localId: string; result: R }
|
|
44
|
-
| { type: 'complete.failed'; localId: string; error: UploadError; retryable: boolean }
|
|
45
|
-
/** Internal completion for pause requests. */
|
|
46
|
-
| { type: 'paused'; localId: string; cursor: AnyCursor<C>; pausedAt: number }
|
|
47
|
-
/** Internal completion for cancel requests. */
|
|
48
|
-
| { type: 'canceled'; localId: string; canceledAt: number }
|
|
49
|
-
|
|
50
|
-
// ============================================================================
|
|
51
|
-
// UPLOAD ITEM (STATE MACHINE)
|
|
52
|
-
// ============================================================================
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* All possible upload phases.
|
|
56
|
-
*
|
|
57
|
-
* Useful for constraints and UI filtering.
|
|
58
|
-
*/
|
|
59
|
-
export type UploadPhase =
|
|
60
|
-
| 'validating'
|
|
61
|
-
| 'creating_intent'
|
|
62
|
-
| 'ready'
|
|
63
|
-
| 'queued'
|
|
64
|
-
| 'uploading'
|
|
65
|
-
| 'paused'
|
|
66
|
-
| 'completing'
|
|
67
|
-
| 'completed'
|
|
68
|
-
| 'error'
|
|
69
|
-
| 'canceled'
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* State of a single upload item.
|
|
73
|
-
*
|
|
74
|
-
* This is a discriminated union on `phase`.
|
|
75
|
-
*
|
|
76
|
-
* @typeParam M - Intent map
|
|
77
|
-
* @typeParam C - Cursor map
|
|
78
|
-
* @typeParam P - Purpose union
|
|
79
|
-
*/
|
|
80
|
-
export type UploadItem<
|
|
81
|
-
M extends IntentMap,
|
|
82
|
-
C extends CursorMap<M>,
|
|
83
|
-
P extends string,
|
|
84
|
-
R extends UploadResultBase = UploadResultBase,
|
|
85
|
-
> =
|
|
86
|
-
| {
|
|
87
|
-
/** Validating file against rules. */
|
|
88
|
-
phase: 'validating'
|
|
89
|
-
localId: string
|
|
90
|
-
purpose: P
|
|
91
|
-
fingerprint: FileFingerprint
|
|
92
|
-
file: File
|
|
93
|
-
createdAt: number
|
|
94
|
-
}
|
|
95
|
-
| {
|
|
96
|
-
/** Requesting upload intent from backend. */
|
|
97
|
-
phase: 'creating_intent'
|
|
98
|
-
localId: string
|
|
99
|
-
purpose: P
|
|
100
|
-
fingerprint: FileFingerprint
|
|
101
|
-
file: File
|
|
102
|
-
/** Current attempt number for this phase. */
|
|
103
|
-
attempt: number
|
|
104
|
-
createdAt: number
|
|
105
|
-
}
|
|
106
|
-
| {
|
|
107
|
-
/** Intent received, ready to start upload. */
|
|
108
|
-
phase: 'ready'
|
|
109
|
-
localId: string
|
|
110
|
-
purpose: P
|
|
111
|
-
fingerprint: FileFingerprint
|
|
112
|
-
file: File
|
|
113
|
-
intent: AnyIntent<M>
|
|
114
|
-
createdAt: number
|
|
115
|
-
cursor?: AnyCursor<C>
|
|
116
|
-
progress?: UploadProgress
|
|
117
|
-
attempt?: number
|
|
118
|
-
}
|
|
119
|
-
| {
|
|
120
|
-
/** Queued for upload (waiting for slot). */
|
|
121
|
-
phase: 'queued'
|
|
122
|
-
localId: string
|
|
123
|
-
purpose: P
|
|
124
|
-
fingerprint: FileFingerprint
|
|
125
|
-
file: File
|
|
126
|
-
intent: AnyIntent<M>
|
|
127
|
-
/** Timestamp when this item requested a concurrency slot. */
|
|
128
|
-
requestedAt: number
|
|
129
|
-
createdAt: number
|
|
130
|
-
cursor?: AnyCursor<C>
|
|
131
|
-
progress?: UploadProgress
|
|
132
|
-
attempt?: number
|
|
133
|
-
}
|
|
134
|
-
| {
|
|
135
|
-
/** Actively uploading bytes. */
|
|
136
|
-
phase: 'uploading'
|
|
137
|
-
localId: string
|
|
138
|
-
purpose: P
|
|
139
|
-
fingerprint: FileFingerprint
|
|
140
|
-
file: File
|
|
141
|
-
intent: AnyIntent<M>
|
|
142
|
-
startedAt: number
|
|
143
|
-
progress: UploadProgress
|
|
144
|
-
createdAt: number
|
|
145
|
-
cursor?: AnyCursor<C>
|
|
146
|
-
attempt?: number
|
|
147
|
-
}
|
|
148
|
-
| {
|
|
149
|
-
/** Upload paused (resumable). */
|
|
150
|
-
phase: 'paused'
|
|
151
|
-
localId: string
|
|
152
|
-
purpose: P
|
|
153
|
-
fingerprint: FileFingerprint
|
|
154
|
-
intent: AnyIntent<M>
|
|
155
|
-
cursor: AnyCursor<C>
|
|
156
|
-
progress: UploadProgress
|
|
157
|
-
pausedAt: number
|
|
158
|
-
createdAt: number
|
|
159
|
-
/**
|
|
160
|
-
* File is optional because a paused item can be restored from persistence without a File.
|
|
161
|
-
* Use `rebind` to attach the File again before resuming (when required).
|
|
162
|
-
*/
|
|
163
|
-
file?: File
|
|
164
|
-
attempt?: number
|
|
165
|
-
}
|
|
166
|
-
| {
|
|
167
|
-
/** Bytes sent, finalizing with backend. */
|
|
168
|
-
phase: 'completing'
|
|
169
|
-
localId: string
|
|
170
|
-
purpose: P
|
|
171
|
-
fingerprint: FileFingerprint
|
|
172
|
-
file: File
|
|
173
|
-
intent: AnyIntent<M>
|
|
174
|
-
progress: UploadProgress
|
|
175
|
-
completingAt: number
|
|
176
|
-
createdAt: number
|
|
177
|
-
attempt?: number
|
|
178
|
-
}
|
|
179
|
-
| {
|
|
180
|
-
/** Upload completed successfully. */
|
|
181
|
-
phase: 'completed'
|
|
182
|
-
localId: string
|
|
183
|
-
file: File
|
|
184
|
-
purpose: P
|
|
185
|
-
fingerprint: FileFingerprint
|
|
186
|
-
intent?: AnyIntent<M>
|
|
187
|
-
completedBy: UploadCompletionKind
|
|
188
|
-
result: R
|
|
189
|
-
completedAt: number
|
|
190
|
-
createdAt: number
|
|
191
|
-
attempt?: number
|
|
192
|
-
}
|
|
193
|
-
| {
|
|
194
|
-
/** Upload failed (retryable or final). */
|
|
195
|
-
phase: 'error'
|
|
196
|
-
localId: string
|
|
197
|
-
purpose: P
|
|
198
|
-
fingerprint: FileFingerprint
|
|
199
|
-
error: UploadError
|
|
200
|
-
retryable: boolean
|
|
201
|
-
attempt: number
|
|
202
|
-
failedAt: number
|
|
203
|
-
createdAt: number
|
|
204
|
-
file?: File
|
|
205
|
-
intent?: AnyIntent<M>
|
|
206
|
-
cursor?: AnyCursor<C>
|
|
207
|
-
progress?: UploadProgress
|
|
208
|
-
}
|
|
209
|
-
| {
|
|
210
|
-
/** Upload canceled by user/system. */
|
|
211
|
-
phase: 'canceled'
|
|
212
|
-
localId: string
|
|
213
|
-
purpose: P
|
|
214
|
-
fingerprint: FileFingerprint
|
|
215
|
-
canceledAt: number
|
|
216
|
-
createdAt: number
|
|
217
|
-
file?: File
|
|
218
|
-
intent?: AnyIntent<M>
|
|
219
|
-
cursor?: AnyCursor<C>
|
|
220
|
-
progress?: UploadProgress
|
|
221
|
-
attempt?: number
|
|
222
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { UploadError, UploadResultBase } from '../contracts'
|
|
2
|
-
|
|
3
|
-
export type UploadCompletionKind = 'upload' | 'dedupe'
|
|
4
|
-
|
|
5
|
-
export type UploadOutcome<R extends UploadResultBase> =
|
|
6
|
-
| { localId: string; status: 'completed'; completedBy: UploadCompletionKind; result: R }
|
|
7
|
-
| { localId: string; status: 'error'; error: UploadError }
|
|
8
|
-
| { localId: string; status: 'canceled' }
|
|
9
|
-
| { localId: string; status: 'missing' }
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Upload progress snapshot.
|
|
3
|
-
*
|
|
4
|
-
* Invariants:
|
|
5
|
-
* - `totalBytes` should be `file.size` when known.
|
|
6
|
-
* - `pct` should be clamped to 0..100.
|
|
7
|
-
*/
|
|
8
|
-
export type UploadProgress = {
|
|
9
|
-
/** Bytes uploaded so far. */
|
|
10
|
-
uploadedBytes: number
|
|
11
|
-
/** Total bytes to upload. */
|
|
12
|
-
totalBytes: number
|
|
13
|
-
/** Percent complete (0..100). */
|
|
14
|
-
pct: number
|
|
15
|
-
}
|