@ultimat3/storage 20.2.0 → 21.0.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/CLAUDE.md +12 -1
- package/package.json +2 -2
- package/src/upload-client.ts +19 -10
package/CLAUDE.md
CHANGED
|
@@ -273,5 +273,16 @@ Gotchas:
|
|
|
273
273
|
`sweepOrphans` cannot be tested against it with a frozen clock — `attachment.test.ts` uses a
|
|
274
274
|
stub driver with authored timestamps, and `driver-local.test.ts` proves the disk half.
|
|
275
275
|
- `upload-client.ts` defaults to `XMLHttpRequest`, not `fetch`: `fetch` reports no upload
|
|
276
|
-
progress in any shipping browser, and a bar that jumps 0 → 100 is a bar that is lying.
|
|
276
|
+
progress in any shipping browser, and a bar that jumps 0 → 100 is a bar that is lying. It is
|
|
277
|
+
**the declared XHR seam** of plan 101 (decision 13): the one `XMLHttpRequest` the framework
|
|
278
|
+
ships, and the one file slice 15's `browser-transport` guard exempts for it — the header says so.
|
|
279
|
+
- **`fetchSignedPut` goes through `@ultimat3/core`'s `clientTransport`** (`As of 2026-09-22`,
|
|
280
|
+
plan 101 slice 05): `rawBody` sends the `Blob` verbatim under the grant's content type with no
|
|
281
|
+
default header, and `decodeError` keeps a refusal `X_STORAGE_UPLOAD_FAILED` with the disk's
|
|
282
|
+
status. A request with no response at all is the transport's `X_CLIENT_TRANSPORT_FAILED` — it was a
|
|
283
|
+
bare `TypeError` before. This file calls no `fetch`; the XHR above is the one exemption.
|
|
284
|
+
- **Bundle, `bun build --target=browser --minify`, `import { uploadFile } from '@ultimat3/storage'`**
|
|
285
|
+
(`As of 2026-09-22`): **11,822 B** at 20.2.1, **17,767 B** on `clientTransport`. The +5.9 kB is
|
|
286
|
+
core's transport graph, paid only where the chunk did not already carry it — an island that
|
|
287
|
+
also calls an action or a query already does.
|
|
277
288
|
- `exactOptionalPropertyTypes` is on — declare optional fields as `x?: T | undefined`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/storage",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "21.0.0",
|
|
4
4
|
"description": "Named disks over Bun.file and Bun.s3: safe keys, signed URLs, sniffed uploads",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -31,6 +31,6 @@
|
|
|
31
31
|
"test": "bun test"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ultimat3/core": "
|
|
34
|
+
"@ultimat3/core": "21.0.0"
|
|
35
35
|
}
|
|
36
36
|
}
|
package/src/upload-client.ts
CHANGED
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
// bytes at it, hand back the storage key. Browser-safe by construction: no `Bun.*`, no `node:`,
|
|
3
3
|
// no driver import, so this file bundles into the client the way `@ultimat3/action`'s does.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
5
|
+
// THE DECLARED XHR SEAM (plan 101, decision 13): every other browser request goes through
|
|
6
|
+
// `@ultimat3/core`'s `clientTransport`, and `xhrSignedPut` below is the one `XMLHttpRequest` the
|
|
7
|
+
// framework ships, for exactly one reason — `fetch` reports no upload progress in any shipping
|
|
8
|
+
// browser, and a progress bar that jumps 0 -> 100 is a progress bar that is lying. This file is
|
|
9
|
+
// the one path the `browser-transport` guard exempts for it. Where XHR does not exist the
|
|
10
|
+
// fallback is `clientTransport` with a raw body — never a `fetch` call of this file's own.
|
|
11
|
+
|
|
12
|
+
import { clientTransport } from '@ultimat3/core';
|
|
9
13
|
import { tooLarge, uploadFailed } from './errors';
|
|
10
14
|
import type { UploadGrant, UploadRequest } from './grant';
|
|
11
15
|
|
|
@@ -109,18 +113,23 @@ export const xhrSignedPut: SignedPut = (input) =>
|
|
|
109
113
|
request.send(input.body);
|
|
110
114
|
});
|
|
111
115
|
|
|
112
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* No upload progress — `onProgress` fires once at 0 and once at 1, and says so by doing that.
|
|
118
|
+
* Through `clientTransport`, the one browser HTTP function: `rawBody` sends the bytes verbatim
|
|
119
|
+
* under the grant's content type and adds no header of its own (a signed request may cover them),
|
|
120
|
+
* and `decodeError` keeps a refusal this package's `X_STORAGE_UPLOAD_FAILED`, with the disk's
|
|
121
|
+
* status. A request that got no response at all is the transport's `X_CLIENT_TRANSPORT_FAILED`.
|
|
122
|
+
*/
|
|
113
123
|
export const fetchSignedPut: SignedPut = async (input) => {
|
|
114
124
|
input.onProgress?.(progressOf(0, input.body.size));
|
|
115
|
-
|
|
125
|
+
await clientTransport({
|
|
116
126
|
method: 'PUT',
|
|
127
|
+
url: input.url,
|
|
128
|
+
rawBody: input.body,
|
|
117
129
|
headers: { 'content-type': input.contentType },
|
|
118
|
-
|
|
130
|
+
decodeError: (status, text) => uploadFailed(pathOf(input.url), status, text),
|
|
119
131
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
120
132
|
});
|
|
121
|
-
if (!response.ok) {
|
|
122
|
-
throw uploadFailed(pathOf(input.url), response.status, await response.text());
|
|
123
|
-
}
|
|
124
133
|
input.onProgress?.(progressOf(input.body.size, input.body.size));
|
|
125
134
|
};
|
|
126
135
|
|