@lunora/storage 1.0.0-alpha.1 → 1.0.0-alpha.11
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/LICENSE.md +6 -0
- package/__assets__/package-og.svg +1 -1
- package/dist/index.d.mts +163 -163
- package/dist/index.d.ts +163 -163
- package/dist/index.mjs +4 -4
- package/dist/packem_shared/{buildPresignedUrl-DzPwi1bY.mjs → buildPresignedUrl-B5f1wvz6.mjs} +2 -8
- package/dist/packem_shared/{buildSignedUrl-ZzB16yPl.mjs → buildSignedUrl-rt-6azia.mjs} +41 -24
- package/dist/packem_shared/{createBucketStorage-4Xk7-5CN.mjs → createBucketStorage-JYdrS4e5.mjs} +6 -4
- package/dist/packem_shared/createR2UploadStorage-BfLIpWdf.mjs +54 -0
- package/dist/packem_shared/{scopeKey-Bs_iJ1Mx.mjs → createStorage-Bu6GJB25.mjs} +48 -38
- package/dist/packem_shared/internal-Dqk0MrAj.mjs +17 -0
- package/dist/upload.d.mts +91 -0
- package/dist/upload.d.ts +91 -0
- package/dist/upload.mjs +1 -0
- package/package.json +11 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A single-range read against R2: an `{ offset, length }` window (at least one
|
|
3
|
-
* bound required, mirroring R2's own `R2Range`) or a `{ suffix }` tail. The
|
|
4
|
-
* subset of `R2Range` that {@link Storage.download} forwards so a caller can
|
|
5
|
-
* stream just the bytes it needs instead of the whole object.
|
|
6
|
-
*/
|
|
2
|
+
* A single-range read against R2: an `{ offset, length }` window (at least one
|
|
3
|
+
* bound required, mirroring R2's own `R2Range`) or a `{ suffix }` tail. The
|
|
4
|
+
* subset of `R2Range` that {@link Storage.download} forwards so a caller can
|
|
5
|
+
* stream just the bytes it needs instead of the whole object.
|
|
6
|
+
*/
|
|
7
7
|
type R2RangeLike = {
|
|
8
8
|
length: number;
|
|
9
9
|
offset?: number;
|
|
@@ -14,15 +14,15 @@ type R2RangeLike = {
|
|
|
14
14
|
suffix: number;
|
|
15
15
|
};
|
|
16
16
|
/**
|
|
17
|
-
* Minimal projection of `R2Bucket`. Declared structurally so unit tests can
|
|
18
|
-
* pass a plain object double; the real binding satisfies the same shape.
|
|
19
|
-
*/
|
|
17
|
+
* Minimal projection of `R2Bucket`. Declared structurally so unit tests can
|
|
18
|
+
* pass a plain object double; the real binding satisfies the same shape.
|
|
19
|
+
*/
|
|
20
20
|
interface R2BucketLike {
|
|
21
21
|
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
* Begin a multipart upload (R2 `createMultipartUpload`). Optional so existing
|
|
23
|
+
* test doubles still satisfy the type; {@link Storage.createMultipartUpload}
|
|
24
|
+
* throws a clear error when the binding lacks it.
|
|
25
|
+
*/
|
|
26
26
|
createMultipartUpload?: (key: string, options?: {
|
|
27
27
|
customMetadata?: Record<string, string>;
|
|
28
28
|
httpMetadata?: {
|
|
@@ -34,11 +34,11 @@ interface R2BucketLike {
|
|
|
34
34
|
range?: R2RangeLike;
|
|
35
35
|
}) => Promise<R2ObjectBodyLike | null>;
|
|
36
36
|
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
* Fetch an object's metadata without its body (R2 HEAD). Returns `null` when
|
|
38
|
+
* the object is absent. Declared optional so existing test doubles that only
|
|
39
|
+
* implement `get`/`put`/`list`/`delete` still satisfy the type; callers that
|
|
40
|
+
* need metadata fall back to a 0-length ranged `get()` when `head` is absent.
|
|
41
|
+
*/
|
|
42
42
|
head?: (key: string) => Promise<R2ObjectLike | null>;
|
|
43
43
|
list: (options?: {
|
|
44
44
|
cursor?: string;
|
|
@@ -65,11 +65,11 @@ interface R2UploadedPartLike {
|
|
|
65
65
|
partNumber: number;
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
|
-
* An in-progress multipart upload, mirroring R2's `R2MultipartUpload`. Each part
|
|
69
|
-
* (except the last) must be uniform in size. The object does not guarantee the
|
|
70
|
-
* underlying upload still exists — a parallel `complete`/`abort` can invalidate
|
|
71
|
-
* it — so wrap each call in error handling.
|
|
72
|
-
*/
|
|
68
|
+
* An in-progress multipart upload, mirroring R2's `R2MultipartUpload`. Each part
|
|
69
|
+
* (except the last) must be uniform in size. The object does not guarantee the
|
|
70
|
+
* underlying upload still exists — a parallel `complete`/`abort` can invalidate
|
|
71
|
+
* it — so wrap each call in error handling.
|
|
72
|
+
*/
|
|
73
73
|
interface R2MultipartUploadLike {
|
|
74
74
|
/** Abort the upload, discarding any uploaded parts. */
|
|
75
75
|
abort: () => Promise<void>;
|
|
@@ -84,44 +84,44 @@ interface R2MultipartUploadLike {
|
|
|
84
84
|
}
|
|
85
85
|
interface R2ObjectLike {
|
|
86
86
|
/**
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
* R2-computed checksums. The real binding exposes `sha256` as an
|
|
88
|
+
* `ArrayBuffer` (present only when R2 stored a SHA-256 for the object);
|
|
89
|
+
* declared optional so fakes and non-checksummed objects type-check.
|
|
90
|
+
*/
|
|
91
91
|
checksums?: {
|
|
92
92
|
sha256?: ArrayBuffer;
|
|
93
93
|
};
|
|
94
94
|
customMetadata?: Record<string, string>;
|
|
95
95
|
etag: string;
|
|
96
96
|
/**
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
* The quoted form of {@link R2ObjectLike.etag} (e.g. `"abc123"`), suitable
|
|
98
|
+
* for emitting directly as an HTTP `ETag` header. The real binding always
|
|
99
|
+
* provides it; declared optional so existing doubles that only set `etag`
|
|
100
|
+
* still type-check (callers fall back to quoting `etag`).
|
|
101
|
+
*/
|
|
102
102
|
httpEtag?: string;
|
|
103
103
|
httpMetadata?: {
|
|
104
104
|
contentType?: string;
|
|
105
105
|
};
|
|
106
106
|
key: string;
|
|
107
107
|
/**
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
* Hex-encoded SHA-256 of the object body, surfaced by `download()`/`list()`
|
|
109
|
+
* when R2 carries a checksum (derived from {@link R2ObjectLike.checksums}).
|
|
110
|
+
*/
|
|
111
111
|
sha256?: string;
|
|
112
112
|
/**
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
* Base64-encoded SHA-256 of the object body, surfaced alongside
|
|
114
|
+
* {@link R2ObjectLike.sha256} from the same checksum. Base64 is the encoding
|
|
115
|
+
* RFC 9530 digest headers (`Repr-Digest`/`Content-Digest`) require, so HTTP
|
|
116
|
+
* layers can emit a spec-compliant digest without re-deriving it.
|
|
117
|
+
*/
|
|
118
118
|
sha256Base64?: string;
|
|
119
119
|
size: number;
|
|
120
120
|
/**
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
* When the object was written. The real binding exposes this as a `Date`;
|
|
122
|
+
* declared optional so fakes that omit it still type-check.
|
|
123
|
+
* {@link Storage.getMetadata} normalises it to epoch ms.
|
|
124
|
+
*/
|
|
125
125
|
uploaded?: Date;
|
|
126
126
|
}
|
|
127
127
|
interface R2ObjectBodyLike extends R2ObjectLike {
|
|
@@ -130,11 +130,11 @@ interface R2ObjectBodyLike extends R2ObjectLike {
|
|
|
130
130
|
text: () => Promise<string>;
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
|
-
* R2 S3-API credentials for {@link Storage.getPresignedUrl}. These are an R2 API
|
|
134
|
-
* token's Access Key ID / Secret Access Key (NOT a Cloudflare API token), plus
|
|
135
|
-
* the account id and bucket name. Required only if you call `getPresignedUrl`;
|
|
136
|
-
* the worker-signed URL path (`getSignedUrl`) needs none of this.
|
|
137
|
-
*/
|
|
133
|
+
* R2 S3-API credentials for {@link Storage.getPresignedUrl}. These are an R2 API
|
|
134
|
+
* token's Access Key ID / Secret Access Key (NOT a Cloudflare API token), plus
|
|
135
|
+
* the account id and bucket name. Required only if you call `getPresignedUrl`;
|
|
136
|
+
* the worker-signed URL path (`getSignedUrl`) needs none of this.
|
|
137
|
+
*/
|
|
138
138
|
interface R2S3Credentials {
|
|
139
139
|
/** R2 S3 Access Key ID. */
|
|
140
140
|
accessKeyId: string;
|
|
@@ -159,10 +159,10 @@ interface LunoraStorageOptions {
|
|
|
159
159
|
/** Public base URL used by `getSignedUrl()`. Required for signed URLs. */
|
|
160
160
|
publicBaseUrl?: string;
|
|
161
161
|
/**
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
162
|
+
* R2 S3-API credentials enabling {@link Storage.getPresignedUrl} (native S3
|
|
163
|
+
* presigned URLs that hit R2 directly, bypassing the Worker). Omit to use
|
|
164
|
+
* only the worker-signed URL path.
|
|
165
|
+
*/
|
|
166
166
|
s3?: R2S3Credentials;
|
|
167
167
|
/** HMAC secret used by the worker-signed URL helper. Required for signed URLs. */
|
|
168
168
|
signingSecret?: string;
|
|
@@ -173,13 +173,13 @@ interface UploadOptions {
|
|
|
173
173
|
contentType?: string;
|
|
174
174
|
customMetadata?: Record<string, string>;
|
|
175
175
|
/**
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
176
|
+
* Maximum body size in bytes. For `ArrayBuffer`/`Blob` sources the length is
|
|
177
|
+
* known up front and rejected before the upload starts. For a
|
|
178
|
+
* `ReadableStream` the length isn't known synchronously, so the stream is
|
|
179
|
+
* piped through a byte counter that aborts the upload once the limit is
|
|
180
|
+
* exceeded — this also guards against R2 silently accepting/truncating an
|
|
181
|
+
* unbounded stream.
|
|
182
|
+
*/
|
|
183
183
|
maxSize?: number;
|
|
184
184
|
}
|
|
185
185
|
interface ListOptions {
|
|
@@ -191,20 +191,20 @@ interface ListOptions {
|
|
|
191
191
|
}
|
|
192
192
|
interface SignedUrlOptions {
|
|
193
193
|
/**
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
194
|
+
* Pin the `Content-Type` an uploader must send on a `method: "PUT"` URL.
|
|
195
|
+
* Baked into the HMAC canonical so the signature only authorizes a PUT with
|
|
196
|
+
* exactly this content-type; mirrored on the URL as `&ct=...`. Ignored for
|
|
197
|
+
* `GET` URLs (a download has no request body content-type to pin).
|
|
198
|
+
*/
|
|
199
199
|
contentType?: string;
|
|
200
200
|
expiresInSeconds?: number;
|
|
201
201
|
method?: "GET" | "PUT";
|
|
202
202
|
}
|
|
203
203
|
/**
|
|
204
|
-
* Per-object metadata returned by {@link Storage.getMetadata} — a flat,
|
|
205
|
-
* body-free projection of {@link R2ObjectLike}. Mirrors the shape Convex
|
|
206
|
-
* surfaces for `ctx.storage.getMetadata` / the `_storage` system table.
|
|
207
|
-
*/
|
|
204
|
+
* Per-object metadata returned by {@link Storage.getMetadata} — a flat,
|
|
205
|
+
* body-free projection of {@link R2ObjectLike}. Mirrors the shape Convex
|
|
206
|
+
* surfaces for `ctx.storage.getMetadata` / the `_storage` system table.
|
|
207
|
+
*/
|
|
208
208
|
interface ObjectMetadata {
|
|
209
209
|
/** The object's `Content-Type` (R2 `httpMetadata.contentType`), if recorded. */
|
|
210
210
|
contentType?: string;
|
|
@@ -221,49 +221,49 @@ interface ObjectMetadata {
|
|
|
221
221
|
}
|
|
222
222
|
interface Storage {
|
|
223
223
|
/**
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
224
|
+
* Begin a native R2 **multipart upload** for very large objects — upload
|
|
225
|
+
* parts (each uniform in size except the last), then `complete` with the
|
|
226
|
+
* returned parts (or `abort`). Wraps R2's `createMultipartUpload`; throws if
|
|
227
|
+
* the bound bucket doesn't support it. For ordinary uploads use
|
|
228
|
+
* {@link Storage.upload} / {@link Storage.store}.
|
|
229
|
+
*/
|
|
230
230
|
createMultipartUpload: (key: string, options?: {
|
|
231
231
|
contentType?: string;
|
|
232
232
|
customMetadata?: Record<string, string>;
|
|
233
233
|
}) => Promise<R2MultipartUploadLike>;
|
|
234
234
|
delete: (key: string) => Promise<void>;
|
|
235
235
|
/**
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
236
|
+
* Fetch a stored object's metadata + body. Pass `options.range` to stream
|
|
237
|
+
* only a byte window (R2 resolves the range server-side, so the unwanted
|
|
238
|
+
* bytes never reach the Worker) — `download(key)` reads the whole object.
|
|
239
|
+
*/
|
|
240
240
|
download: (key: string, options?: {
|
|
241
241
|
range?: R2RangeLike;
|
|
242
242
|
}) => Promise<R2ObjectBodyLike | null>;
|
|
243
243
|
/**
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
244
|
+
* Mint a short-lived signed `PUT` URL a client can upload directly to,
|
|
245
|
+
* optionally pinning the request `Content-Type`. Convex-compatible alias
|
|
246
|
+
* built on {@link Storage.getSignedUrl} with `method: "PUT"`.
|
|
247
|
+
*/
|
|
248
248
|
generateUploadUrl: (key: string, options?: {
|
|
249
249
|
contentType?: string;
|
|
250
250
|
expiresInSeconds?: number;
|
|
251
251
|
}) => Promise<string>;
|
|
252
252
|
/**
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
253
|
+
* Read a stored object's metadata (size, content-type, sha256, upload time,
|
|
254
|
+
* custom metadata) without fetching its body. Returns `null` when the object
|
|
255
|
+
* is absent. Backed by an R2 HEAD (`bucket.head`) when available, falling
|
|
256
|
+
* back to a 0-length ranged `get()` otherwise. Mirrors Convex's
|
|
257
|
+
* `ctx.storage.getMetadata`.
|
|
258
|
+
*/
|
|
259
259
|
getMetadata: (key: string) => Promise<ObjectMetadata | null>;
|
|
260
260
|
/**
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
261
|
+
* Mint a native S3 **presigned URL** (SigV4) that hits R2 directly, bypassing
|
|
262
|
+
* the Worker. Use for large downloads/uploads where you don't need per-request
|
|
263
|
+
* app gating and want the bytes off the Worker's CPU/bandwidth budget. Requires
|
|
264
|
+
* {@link LunoraStorageOptions.s3} credentials; throws if they're absent. For
|
|
265
|
+
* app-gated access (auth/policy/rate-limit) prefer {@link Storage.getSignedUrl}.
|
|
266
|
+
*/
|
|
267
267
|
getPresignedUrl: (key: string, options?: PresignedUrlOptions) => Promise<string>;
|
|
268
268
|
getSignedUrl: (key: string, options?: SignedUrlOptions) => Promise<string>;
|
|
269
269
|
getUrl: (key: string) => string;
|
|
@@ -273,16 +273,16 @@ interface Storage {
|
|
|
273
273
|
truncated?: boolean;
|
|
274
274
|
}>;
|
|
275
275
|
/**
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
276
|
+
* Resume an in-progress multipart upload by its `uploadId` (e.g. across
|
|
277
|
+
* requests). Wraps R2's `resumeMultipartUpload`; the id is not validated by
|
|
278
|
+
* R2, so a stale id surfaces as an error on the first `uploadPart`/`complete`.
|
|
279
|
+
*/
|
|
280
280
|
resumeMultipartUpload: (key: string, uploadId: string) => R2MultipartUploadLike;
|
|
281
281
|
/**
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
282
|
+
* Upload `body` to `key`, returning the stored key + etag. Convex-compatible
|
|
283
|
+
* alias for {@link Storage.upload} — it accepts the same {@link UploadOptions}
|
|
284
|
+
* so the `maxSize` / `allowedContentTypes` guards aren't lost behind the alias.
|
|
285
|
+
*/
|
|
286
286
|
store: (key: string, body: ReadableStream | ArrayBuffer | Blob, options?: UploadOptions) => Promise<{
|
|
287
287
|
etag: string;
|
|
288
288
|
httpEtag: string;
|
|
@@ -295,11 +295,11 @@ interface Storage {
|
|
|
295
295
|
}>;
|
|
296
296
|
}
|
|
297
297
|
/**
|
|
298
|
-
* A bucket-aware {@link Storage}: the methods target a default bucket, and
|
|
299
|
-
* `bucket(name)` selects a different named bucket (declared via
|
|
300
|
-
* `v.storage("name")`). `bucketName` is the bucket the current accessor targets
|
|
301
|
-
* — the storage-rules middleware reads it to scope `(bucket, operation)` rules.
|
|
302
|
-
*/
|
|
298
|
+
* A bucket-aware {@link Storage}: the methods target a default bucket, and
|
|
299
|
+
* `bucket(name)` selects a different named bucket (declared via
|
|
300
|
+
* `v.storage("name")`). `bucketName` is the bucket the current accessor targets
|
|
301
|
+
* — the storage-rules middleware reads it to scope `(bucket, operation)` rules.
|
|
302
|
+
*/
|
|
303
303
|
interface BucketStorage extends Storage {
|
|
304
304
|
/** Select a named bucket. Unknown names throw with the list of registered buckets. */
|
|
305
305
|
bucket: (name: string) => BucketStorage;
|
|
@@ -307,36 +307,36 @@ interface BucketStorage extends Storage {
|
|
|
307
307
|
readonly bucketName: string;
|
|
308
308
|
}
|
|
309
309
|
/**
|
|
310
|
-
* Compose several per-bucket {@link Storage} instances into one bucket-aware
|
|
311
|
-
* accessor. The bare methods (`download` / `store` / …) target the default
|
|
312
|
-
* bucket; `bucket(name)` switches to another. Each accessor is tagged with its
|
|
313
|
-
* `bucketName` so `storageRules(...)` can enforce per-bucket.
|
|
314
|
-
*
|
|
315
|
-
* ```ts
|
|
316
|
-
* storage: (env) => createBucketStorage({
|
|
317
|
-
* default: createStorage({ bucket: env.FILES }),
|
|
318
|
-
* avatars: createStorage({ bucket: env.AVATARS }),
|
|
319
|
-
* }),
|
|
320
|
-
* // → ctx.storage.download(key) // default bucket
|
|
321
|
-
* // → ctx.storage.bucket("avatars").store() // the avatars bucket
|
|
322
|
-
* ```
|
|
323
|
-
*
|
|
324
|
-
* The bare accessor is tagged `"default"` — the canonical name a
|
|
325
|
-
* `defineStorageRule({ bucket: "default" })` rule and the generated
|
|
326
|
-
* `StorageBucketName` union both use — unless `options.default` names another
|
|
327
|
-
* bucket (then the bare accessor takes that name). The binding it delegates to is
|
|
328
|
-
* `options.default`, else the `"default"` key when present, else the first
|
|
329
|
-
* registered bucket. Named buckets are reached with `bucket(name)`.
|
|
330
|
-
*/
|
|
310
|
+
* Compose several per-bucket {@link Storage} instances into one bucket-aware
|
|
311
|
+
* accessor. The bare methods (`download` / `store` / …) target the default
|
|
312
|
+
* bucket; `bucket(name)` switches to another. Each accessor is tagged with its
|
|
313
|
+
* `bucketName` so `storageRules(...)` can enforce per-bucket.
|
|
314
|
+
*
|
|
315
|
+
* ```ts
|
|
316
|
+
* storage: (env) => createBucketStorage({
|
|
317
|
+
* default: createStorage({ bucket: env.FILES }),
|
|
318
|
+
* avatars: createStorage({ bucket: env.AVATARS }),
|
|
319
|
+
* }),
|
|
320
|
+
* // → ctx.storage.download(key) // default bucket
|
|
321
|
+
* // → ctx.storage.bucket("avatars").store() // the avatars bucket
|
|
322
|
+
* ```
|
|
323
|
+
*
|
|
324
|
+
* The bare accessor is tagged `"default"` — the canonical name a
|
|
325
|
+
* `defineStorageRule({ bucket: "default" })` rule and the generated
|
|
326
|
+
* `StorageBucketName` union both use — unless `options.default` names another
|
|
327
|
+
* bucket (then the bare accessor takes that name). The binding it delegates to is
|
|
328
|
+
* `options.default`, else the `"default"` key when present, else the first
|
|
329
|
+
* registered bucket. Named buckets are reached with `bucket(name)`.
|
|
330
|
+
*/
|
|
331
331
|
declare const createBucketStorage: (buckets: Record<string, Storage>, options?: {
|
|
332
332
|
default?: string;
|
|
333
333
|
}) => BucketStorage;
|
|
334
334
|
/**
|
|
335
|
-
* Compose a per-tenant key from a scope prefix and a caller-supplied key.
|
|
336
|
-
* Both halves are validated — the prefix may not contain `..` or NUL either,
|
|
337
|
-
* and the resulting key must stay under R2's length ceiling. Recommended for
|
|
338
|
-
* any multi-tenant deployment so client-supplied keys can't address peer data.
|
|
339
|
-
*/
|
|
335
|
+
* Compose a per-tenant key from a scope prefix and a caller-supplied key.
|
|
336
|
+
* Both halves are validated — the prefix may not contain `..` or NUL either,
|
|
337
|
+
* and the resulting key must stay under R2's length ceiling. Recommended for
|
|
338
|
+
* any multi-tenant deployment so client-supplied keys can't address peer data.
|
|
339
|
+
*/
|
|
340
340
|
declare const scopeKey: (prefix: string, key: string) => string;
|
|
341
341
|
declare const createStorage: (options: LunoraStorageOptions) => Storage;
|
|
342
342
|
/** Parameters accepted by {@link buildPresignedUrl}. */
|
|
@@ -353,25 +353,25 @@ interface PresignedUrlParams {
|
|
|
353
353
|
now?: () => number;
|
|
354
354
|
}
|
|
355
355
|
/**
|
|
356
|
-
* Build a native S3 presigned URL for an R2 object using SigV4 query-string
|
|
357
|
-
* auth. The returned URL points at R2's S3 endpoint and carries the full
|
|
358
|
-
* signature, so it authorizes a single `GET`/`PUT` on `key` until it expires —
|
|
359
|
-
* no Worker round-trip.
|
|
360
|
-
*/
|
|
356
|
+
* Build a native S3 presigned URL for an R2 object using SigV4 query-string
|
|
357
|
+
* auth. The returned URL points at R2's S3 endpoint and carries the full
|
|
358
|
+
* signature, so it authorizes a single `GET`/`PUT` on `key` until it expires —
|
|
359
|
+
* no Worker round-trip.
|
|
360
|
+
*/
|
|
361
361
|
declare const buildPresignedUrl: (parameters: PresignedUrlParams) => Promise<string>;
|
|
362
362
|
/**
|
|
363
|
-
* Worker-signed URL: the `publicBaseUrl` joined to the object `key`, plus a query
|
|
364
|
-
* string carrying `exp` (unix seconds), `method` (`GET` or `PUT`) and `sig`
|
|
365
|
-
* (a base64url HMAC).
|
|
366
|
-
*
|
|
367
|
-
* The HMAC canonical includes the URL host so a signature minted for one bucket
|
|
368
|
-
* cannot be replayed against another host on the same signing secret. Even so,
|
|
369
|
-
* the signing secret MUST NOT be shared across buckets/tenants — host binding
|
|
370
|
-
* narrows replay surface but is not a substitute for per-tenant key isolation.
|
|
371
|
-
*
|
|
372
|
-
* The Worker handling `GET /storage/:key` should call {@link verifySignedUrl}
|
|
373
|
-
* to validate the signature + expiry before streaming the R2 body.
|
|
374
|
-
*/
|
|
363
|
+
* Worker-signed URL: the `publicBaseUrl` joined to the object `key`, plus a query
|
|
364
|
+
* string carrying `exp` (unix seconds), `method` (`GET` or `PUT`) and `sig`
|
|
365
|
+
* (a base64url HMAC).
|
|
366
|
+
*
|
|
367
|
+
* The HMAC canonical includes the URL host so a signature minted for one bucket
|
|
368
|
+
* cannot be replayed against another host on the same signing secret. Even so,
|
|
369
|
+
* the signing secret MUST NOT be shared across buckets/tenants — host binding
|
|
370
|
+
* narrows replay surface but is not a substitute for per-tenant key isolation.
|
|
371
|
+
*
|
|
372
|
+
* The Worker handling `GET /storage/:key` should call {@link verifySignedUrl}
|
|
373
|
+
* to validate the signature + expiry before streaming the R2 body.
|
|
374
|
+
*/
|
|
375
375
|
declare const buildSignedUrl: (args: SignedUrlOptions & {
|
|
376
376
|
baseUrl: string;
|
|
377
377
|
key: string;
|
|
@@ -383,23 +383,23 @@ interface VerifyResult {
|
|
|
383
383
|
key?: string;
|
|
384
384
|
method?: "GET" | "PUT";
|
|
385
385
|
/**
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
386
|
+
* Internal-only failure reason for server logs/diagnostics. **Do not echo
|
|
387
|
+
* to clients** — a precise reason ("expired" vs "bad_signature") is a
|
|
388
|
+
* signing oracle. Public responses should expose only `valid`.
|
|
389
|
+
*/
|
|
390
390
|
reason?: "bad_signature" | "expired" | "malformed";
|
|
391
391
|
valid: boolean;
|
|
392
392
|
}
|
|
393
393
|
/**
|
|
394
|
-
* Verify a {@link buildSignedUrl} output. By default the signature is
|
|
395
|
-
* canonicalized against the inbound `url.host`, which matches the build-side
|
|
396
|
-
* host whenever the URL being verified is the URL that was minted. In a
|
|
397
|
-
* topology where the host the Worker sees differs from the configured
|
|
398
|
-
* `publicBaseUrl` host (e.g. a CDN host vs a Worker route that rewrites
|
|
399
|
-
* `Host`), pass `expectedHost` (the `publicBaseUrl` host) so verification
|
|
400
|
-
* canonicalizes against the same host the signature was minted for instead of
|
|
401
|
-
* failing every request as `bad_signature`.
|
|
402
|
-
*/
|
|
394
|
+
* Verify a {@link buildSignedUrl} output. By default the signature is
|
|
395
|
+
* canonicalized against the inbound `url.host`, which matches the build-side
|
|
396
|
+
* host whenever the URL being verified is the URL that was minted. In a
|
|
397
|
+
* topology where the host the Worker sees differs from the configured
|
|
398
|
+
* `publicBaseUrl` host (e.g. a CDN host vs a Worker route that rewrites
|
|
399
|
+
* `Host`), pass `expectedHost` (the `publicBaseUrl` host) so verification
|
|
400
|
+
* canonicalizes against the same host the signature was minted for instead of
|
|
401
|
+
* failing every request as `bad_signature`.
|
|
402
|
+
*/
|
|
403
403
|
declare const verifySignedUrl: (input: string | URL, secret: string, options?: {
|
|
404
404
|
expectedHost?: string;
|
|
405
405
|
}) => Promise<VerifyResult>;
|