@ultimat3/storage 22.1.0 → 22.2.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 +7 -0
- package/package.json +2 -2
- package/src/accept.ts +13 -0
- package/src/index.ts +2 -2
- package/src/storage.ts +10 -0
package/README.md
CHANGED
|
@@ -179,6 +179,13 @@ and hands over, hashed again here and refused on any disagreement.
|
|
|
179
179
|
Neither owns a `Request`, a `Response` or a status number — mounting is the host's job, and
|
|
180
180
|
`@ultimat3/http` is the only layer that turns an `X_*` code into a status.
|
|
181
181
|
|
|
182
|
+
**Step 3 ships.** `As of 2026-09-24` `@ultimat3/cli` mounts `PUT /_storage/:disk/*key` beside the
|
|
183
|
+
`GET` in `x dev` and `runRole` (#523), so an app writes steps 1 and 2 only. The route calls
|
|
184
|
+
`signedUploadConstraints({ url, disk, orgId })` first. That is the verified PUT constraints with
|
|
185
|
+
no bytes, and the signed `maxBytes` caps the body read. Then it calls `acceptSignedUpload` with a
|
|
186
|
+
policy built from the grant's own signed type and ceiling. The routes serve `definedStorage()`,
|
|
187
|
+
the process's one registry, which is the app's `defineStorage` when it declared one (#524).
|
|
188
|
+
|
|
182
189
|
## Attachments and orphans
|
|
183
190
|
|
|
184
191
|
An upload happens **before** the row it belongs to exists, so it lands at
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/storage",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.2.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": "22.
|
|
34
|
+
"@ultimat3/core": "22.2.0"
|
|
35
35
|
}
|
|
36
36
|
}
|
package/src/accept.ts
CHANGED
|
@@ -123,6 +123,19 @@ async function constraintsFor(
|
|
|
123
123
|
return constraints;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
/**
|
|
127
|
+
* The verified half of a PUT, without the bytes: the signature, the expiry, the method and the
|
|
128
|
+
* tenant, answered as the constraints the grant signed. A route reads this BEFORE the body so the
|
|
129
|
+
* signed `maxBytes` — not an HTTP-wide default, and never an unverified query parameter — is the
|
|
130
|
+
* cap on how much of the body it is willing to hold. `acceptSignedUpload` asks again; an HMAC is
|
|
131
|
+
* cheap and a second statement of the accept rules would not be.
|
|
132
|
+
*/
|
|
133
|
+
export async function signedUploadConstraints(
|
|
134
|
+
input: SignedRequestInput,
|
|
135
|
+
): Promise<SignedUrlConstraints> {
|
|
136
|
+
return constraintsFor(input, 'PUT');
|
|
137
|
+
}
|
|
138
|
+
|
|
126
139
|
/**
|
|
127
140
|
* Take a presigned PUT and write it, or refuse. Four gates the client cannot move because all
|
|
128
141
|
* four are inside the signature or inside the bytes: the signature itself, the expiry, the byte
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// every consumer imports from here, so this list is the package's contract.
|
|
3
3
|
|
|
4
4
|
export type { AcceptSignedUploadInput, SignedRequestInput } from './accept';
|
|
5
|
-
export { acceptSignedUpload, readSignedObject } from './accept';
|
|
5
|
+
export { acceptSignedUpload, readSignedObject, signedUploadConstraints } from './accept';
|
|
6
6
|
export type {
|
|
7
7
|
AttachmentTarget,
|
|
8
8
|
PromoteAttachmentInput,
|
|
@@ -154,7 +154,7 @@ export {
|
|
|
154
154
|
verifySignedUrl,
|
|
155
155
|
} from './signed-url';
|
|
156
156
|
export type { Storage, StorageConfig } from './storage';
|
|
157
|
-
export { defineStorage, disk, resetStorage, storage } from './storage';
|
|
157
|
+
export { definedStorage, defineStorage, disk, resetStorage, storage } from './storage';
|
|
158
158
|
export type { UploadCandidate, UploadPolicy, UploadPolicyInit, ValidatedUpload } from './upload';
|
|
159
159
|
export {
|
|
160
160
|
contentTypeMatches,
|
package/src/storage.ts
CHANGED
|
@@ -88,6 +88,16 @@ export function storage(): Storage {
|
|
|
88
88
|
return current;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* The configured storage, or `undefined` before any `defineStorage` — the question a host asks
|
|
93
|
+
* when it must serve whatever the app installed and fall back to its own disk otherwise. The LAST
|
|
94
|
+
* call wins, by `defineStorage`'s own contract: there is one registry, so the app's declaration
|
|
95
|
+
* replaces a host's default rather than sitting beside it.
|
|
96
|
+
*/
|
|
97
|
+
export function definedStorage(): Storage | undefined {
|
|
98
|
+
return current;
|
|
99
|
+
}
|
|
100
|
+
|
|
91
101
|
/** Shorthand for the common call. `disk()` alone resolves the default disk. */
|
|
92
102
|
export function disk(name?: string): StorageDriver {
|
|
93
103
|
return storage().disk(name);
|