@panorama-industries/effect-vercel-blob 0.0.0 → 0.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/LICENSE +21 -0
- package/README.md +12 -1
- package/dist/client.d.ts +8 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +8 -0
- package/dist/client.js.map +1 -0
- package/dist/contracts/blob-client.d.ts +67 -0
- package/dist/contracts/blob-client.d.ts.map +1 -0
- package/dist/contracts/blob-client.js +11 -0
- package/dist/contracts/blob-client.js.map +1 -0
- package/dist/contracts/blob-config.d.ts +114 -0
- package/dist/contracts/blob-config.d.ts.map +1 -0
- package/dist/contracts/blob-config.js +7 -0
- package/dist/contracts/blob-config.js.map +1 -0
- package/dist/contracts/blob-domain.d.ts +336 -0
- package/dist/contracts/blob-domain.d.ts.map +1 -0
- package/dist/contracts/blob-domain.js +152 -0
- package/dist/contracts/blob-domain.js.map +1 -0
- package/dist/contracts/blob-errors.d.ts +125 -0
- package/dist/contracts/blob-errors.d.ts.map +1 -0
- package/dist/contracts/blob-errors.js +114 -0
- package/dist/contracts/blob-errors.js.map +1 -0
- package/dist/contracts/blob-signed-urls.d.ts +83 -0
- package/dist/contracts/blob-signed-urls.d.ts.map +1 -0
- package/dist/contracts/blob-signed-urls.js +10 -0
- package/dist/contracts/blob-signed-urls.js.map +1 -0
- package/dist/contracts/blob-store.d.ts +153 -0
- package/dist/contracts/blob-store.d.ts.map +1 -0
- package/dist/contracts/blob-store.js +15 -0
- package/dist/contracts/blob-store.js.map +1 -0
- package/dist/contracts/blob-uploads.d.ts +101 -0
- package/dist/contracts/blob-uploads.d.ts.map +1 -0
- package/dist/contracts/blob-uploads.js +17 -0
- package/dist/contracts/blob-uploads.js.map +1 -0
- package/dist/implementation/blob-client-live.d.ts +37 -0
- package/dist/implementation/blob-client-live.d.ts.map +1 -0
- package/dist/implementation/blob-client-live.js +213 -0
- package/dist/implementation/blob-client-live.js.map +1 -0
- package/dist/implementation/blob-config-live.d.ts +49 -0
- package/dist/implementation/blob-config-live.d.ts.map +1 -0
- package/dist/implementation/blob-config-live.js +82 -0
- package/dist/implementation/blob-config-live.js.map +1 -0
- package/dist/implementation/blob-live.d.ts +16 -0
- package/dist/implementation/blob-live.d.ts.map +1 -0
- package/dist/implementation/blob-live.js +16 -0
- package/dist/implementation/blob-live.js.map +1 -0
- package/dist/implementation/blob-signed-urls-live.d.ts +12 -0
- package/dist/implementation/blob-signed-urls-live.d.ts.map +1 -0
- package/dist/implementation/blob-signed-urls-live.js +92 -0
- package/dist/implementation/blob-signed-urls-live.js.map +1 -0
- package/dist/implementation/blob-store-live.d.ts +18 -0
- package/dist/implementation/blob-store-live.d.ts.map +1 -0
- package/dist/implementation/blob-store-live.js +364 -0
- package/dist/implementation/blob-store-live.js.map +1 -0
- package/dist/implementation/blob-uploads-live.d.ts +11 -0
- package/dist/implementation/blob-uploads-live.d.ts.map +1 -0
- package/dist/implementation/blob-uploads-live.js +218 -0
- package/dist/implementation/blob-uploads-live.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/blob-errors.d.ts +79 -0
- package/dist/internal/blob-errors.d.ts.map +1 -0
- package/dist/internal/blob-errors.js +196 -0
- package/dist/internal/blob-errors.js.map +1 -0
- package/dist/internal/blob-request.d.ts +255 -0
- package/dist/internal/blob-request.d.ts.map +1 -0
- package/dist/internal/blob-request.js +291 -0
- package/dist/internal/blob-request.js.map +1 -0
- package/dist/internal/callback.d.ts +70 -0
- package/dist/internal/callback.d.ts.map +1 -0
- package/dist/internal/callback.js +141 -0
- package/dist/internal/callback.js.map +1 -0
- package/dist/internal/client-token.d.ts +65 -0
- package/dist/internal/client-token.d.ts.map +1 -0
- package/dist/internal/client-token.js +80 -0
- package/dist/internal/client-token.js.map +1 -0
- package/dist/internal/env.d.ts +34 -0
- package/dist/internal/env.d.ts.map +1 -0
- package/dist/internal/env.js +61 -0
- package/dist/internal/env.js.map +1 -0
- package/dist/internal/multipart.d.ts +110 -0
- package/dist/internal/multipart.d.ts.map +1 -0
- package/dist/internal/multipart.js +290 -0
- package/dist/internal/multipart.js.map +1 -0
- package/dist/internal/presign.d.ts +123 -0
- package/dist/internal/presign.d.ts.map +1 -0
- package/dist/internal/presign.js +345 -0
- package/dist/internal/presign.js.map +1 -0
- package/dist/internal/store-id.d.ts +38 -0
- package/dist/internal/store-id.d.ts.map +1 -0
- package/dist/internal/store-id.js +54 -0
- package/dist/internal/store-id.js.map +1 -0
- package/dist/internal/urls.d.ts +26 -0
- package/dist/internal/urls.d.ts.map +1 -0
- package/dist/internal/urls.js +33 -0
- package/dist/internal/urls.js.map +1 -0
- package/dist/internal/wire.d.ts +55 -0
- package/dist/internal/wire.d.ts.map +1 -0
- package/dist/internal/wire.js +64 -0
- package/dist/internal/wire.js.map +1 -0
- package/dist/internal/write-headers.d.ts +56 -0
- package/dist/internal/write-headers.d.ts.map +1 -0
- package/dist/internal/write-headers.js +82 -0
- package/dist/internal/write-headers.js.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +2 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/test-blob-store.d.ts +134 -0
- package/dist/testing/test-blob-store.d.ts.map +1 -0
- package/dist/testing/test-blob-store.js +718 -0
- package/dist/testing/test-blob-store.js.map +1 -0
- package/package.json +58 -4
- package/src/client.ts +11 -0
- package/src/contracts/blob-client.ts +121 -0
- package/src/contracts/blob-config.ts +64 -0
- package/src/contracts/blob-domain.ts +304 -0
- package/src/contracts/blob-errors.ts +161 -0
- package/src/contracts/blob-signed-urls.ts +112 -0
- package/src/contracts/blob-store.ts +258 -0
- package/src/contracts/blob-uploads.ts +151 -0
- package/src/implementation/blob-client-live.ts +387 -0
- package/src/implementation/blob-config-live.ts +145 -0
- package/src/implementation/blob-live.ts +30 -0
- package/src/implementation/blob-signed-urls-live.ts +121 -0
- package/src/implementation/blob-store-live.ts +528 -0
- package/src/implementation/blob-uploads-live.ts +326 -0
- package/src/index.ts +18 -0
- package/src/internal/blob-errors.ts +325 -0
- package/src/internal/blob-request.ts +617 -0
- package/src/internal/callback.ts +200 -0
- package/src/internal/client-token.ts +132 -0
- package/src/internal/env.ts +128 -0
- package/src/internal/multipart.ts +526 -0
- package/src/internal/presign.ts +477 -0
- package/src/internal/store-id.ts +69 -0
- package/src/internal/urls.ts +42 -0
- package/src/internal/wire.ts +80 -0
- package/src/internal/write-headers.ts +118 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/test-blob-store.ts +1136 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Panorama Industries
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
1
|
# @panorama-industries/effect-vercel-blob
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Effect-native client for [Vercel Blob](https://vercel.com/docs/vercel-blob): uploads, downloads,
|
|
4
|
+
listing, multipart, signed URLs and browser uploads. Part of
|
|
5
|
+
[effect-vercel](https://github.com/panorama-industries/effect-vercel).
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @panorama-industries/effect-vercel-blob effect
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- `effect` `^4.0.6` (peer dependency)
|
|
14
|
+
- ESM only
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./contracts/blob-client.ts";
|
|
2
|
+
export * from "./contracts/blob-domain.ts";
|
|
3
|
+
export * from "./contracts/blob-errors.ts";
|
|
4
|
+
export { storeIdFromDelegationToken } from "./internal/store-id.ts";
|
|
5
|
+
export { downloadUrl } from "./internal/urls.ts";
|
|
6
|
+
export { type BlobClientOptions, BlobClientLive, blobClientLayer, } from "./implementation/blob-client-live.ts";
|
|
7
|
+
export { ClientTokenPayload, getPayloadFromClientToken } from "./internal/client-token.ts";
|
|
8
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EACL,KAAK,iBAAiB,EACtB,cAAc,EACd,eAAe,GAChB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./contracts/blob-client.js";
|
|
2
|
+
export * from "./contracts/blob-domain.js";
|
|
3
|
+
export * from "./contracts/blob-errors.js";
|
|
4
|
+
export { storeIdFromDelegationToken } from "./internal/store-id.js";
|
|
5
|
+
export { downloadUrl } from "./internal/urls.js";
|
|
6
|
+
export { BlobClientLive, blobClientLayer, } from "./implementation/blob-client-live.js";
|
|
7
|
+
export { ClientTokenPayload, getPayloadFromClientToken } from "./internal/client-token.js";
|
|
8
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAEL,cAAc,EACd,eAAe,GAChB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { VercelApiError } from "@panorama-industries/effect-vercel-core";
|
|
2
|
+
import type { Array, Effect } from "effect";
|
|
3
|
+
import { Context } from "effect";
|
|
4
|
+
import type { BlobAccess, BlobBody, BlobPathname, ClientToken, MultipartUpload, OnUploadProgress, PartNumber, PutBlobResult, UploadedPart } from "./blob-domain.ts";
|
|
5
|
+
import type { BlobClientTokenNotAllowed, BlobPreconditionFailed, BlobStoreUnavailable, BlobUploadAuthorizationFailed, BlobUploadRejected } from "./blob-errors.ts";
|
|
6
|
+
/** Failures of uploading from a browser with a client token or presigned URL. */
|
|
7
|
+
export type BlobClientError = VercelApiError | BlobStoreUnavailable | BlobUploadRejected | BlobPreconditionFailed | BlobClientTokenNotAllowed;
|
|
8
|
+
/**
|
|
9
|
+
* Options a browser may set on an upload. Overwriting, random suffixes and cache lifetime are
|
|
10
|
+
* decided by the server when it authorizes the upload.
|
|
11
|
+
*/
|
|
12
|
+
export interface ClientUploadOptions {
|
|
13
|
+
/** Must match the store's access mode. */
|
|
14
|
+
readonly access: BlobAccess;
|
|
15
|
+
readonly contentType?: string | undefined;
|
|
16
|
+
/** Upload in 8 MiB parts, six at a time; use it for large files. */
|
|
17
|
+
readonly multipart?: boolean | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Byte-level progress where `fetch` can stream request bodies (Chromium); elsewhere 0 % before
|
|
20
|
+
* the upload, one event per finished part, and 100 % at the end.
|
|
21
|
+
*/
|
|
22
|
+
readonly onUploadProgress?: OnUploadProgress | undefined;
|
|
23
|
+
}
|
|
24
|
+
/** How a browser asks the application's upload route to authorize an upload. */
|
|
25
|
+
export interface UploadRouteOptions {
|
|
26
|
+
/** The upload route, absolute or relative to the page. */
|
|
27
|
+
readonly handleUploadUrl: string;
|
|
28
|
+
/** Sent to the route's `onBeforeGenerateToken` or `getSignedToken`; untrusted there. */
|
|
29
|
+
readonly clientPayload?: string | undefined;
|
|
30
|
+
/** Extra headers for the request to the upload route, such as an anti-CSRF token. */
|
|
31
|
+
readonly headers?: Readonly<Record<string, string>> | undefined;
|
|
32
|
+
}
|
|
33
|
+
declare const BlobClient_base: Context.ServiceClass<BlobClient, "@effect-vercel/blob/BlobClient", {
|
|
34
|
+
/** Get a client token from the upload route, then upload with it. */
|
|
35
|
+
readonly upload: <E = never, R = never>(pathname: BlobPathname, body: BlobBody<E, R>, options: ClientUploadOptions & UploadRouteOptions) => Effect.Effect<PutBlobResult, BlobClientError | BlobUploadAuthorizationFailed | E, R>;
|
|
36
|
+
/** Get a presigned URL from the upload route, then upload with it; sends no credentials. */
|
|
37
|
+
readonly uploadPresigned: <E = never, R = never>(pathname: BlobPathname, body: BlobBody<E, R>, options: ClientUploadOptions & UploadRouteOptions) => Effect.Effect<PutBlobResult, BlobClientError | BlobUploadAuthorizationFailed | E, R>;
|
|
38
|
+
/** Upload with a client token the application obtained itself. */
|
|
39
|
+
readonly put: <E = never, R = never>(pathname: BlobPathname, body: BlobBody<E, R>, options: ClientUploadOptions & {
|
|
40
|
+
readonly token: ClientToken;
|
|
41
|
+
}) => Effect.Effect<PutBlobResult, BlobClientError | E, R>;
|
|
42
|
+
/** Start a multipart upload with a client token issued for multipart uploads. */
|
|
43
|
+
readonly createMultipartUpload: (pathname: BlobPathname, options: {
|
|
44
|
+
readonly token: ClientToken;
|
|
45
|
+
readonly access: BlobAccess;
|
|
46
|
+
readonly contentType?: string | undefined;
|
|
47
|
+
}) => Effect.Effect<MultipartUpload, BlobClientError>;
|
|
48
|
+
/** Upload one part of a multipart upload. */
|
|
49
|
+
readonly uploadPart: <E = never, R = never>(upload: MultipartUpload, partNumber: PartNumber, body: BlobBody<E, R>, options: {
|
|
50
|
+
readonly token: ClientToken;
|
|
51
|
+
}) => Effect.Effect<UploadedPart, BlobClientError | E, R>;
|
|
52
|
+
/** Assemble the uploaded parts into the blob. */
|
|
53
|
+
readonly completeMultipartUpload: (upload: MultipartUpload, parts: Array.NonEmptyReadonlyArray<UploadedPart>, options: {
|
|
54
|
+
readonly token: ClientToken;
|
|
55
|
+
}) => Effect.Effect<PutBlobResult, BlobClientError>;
|
|
56
|
+
}>;
|
|
57
|
+
/**
|
|
58
|
+
* Browser uploads straight to Vercel Blob, bypassing the 4.5 MB function body limit. The
|
|
59
|
+
* application's upload route (see `BlobUploads`) authorizes each upload.
|
|
60
|
+
*
|
|
61
|
+
* Import from `@panorama-industries/effect-vercel-blob/client`. `BlobClientLive` needs only an
|
|
62
|
+
* `HttpClient`, such as `FetchHttpClient.layer`.
|
|
63
|
+
*/
|
|
64
|
+
export declare class BlobClient extends BlobClient_base {
|
|
65
|
+
}
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=blob-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob-client.d.ts","sourceRoot":"","sources":["../../src/contracts/blob-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Browser uploads straight to Vercel Blob, bypassing the 4.5 MB function body limit. The
|
|
4
|
+
* application's upload route (see `BlobUploads`) authorizes each upload.
|
|
5
|
+
*
|
|
6
|
+
* Import from `@panorama-industries/effect-vercel-blob/client`. `BlobClientLive` needs only an
|
|
7
|
+
* `HttpClient`, such as `FetchHttpClient.layer`.
|
|
8
|
+
*/
|
|
9
|
+
export class BlobClient extends Context.Service()("@effect-vercel/blob/BlobClient") {
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=blob-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob-client.js","sourceRoot":"","sources":["../../src/contracts/blob-client.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AA6DjC;;;;;;GAMG;AACH,MAAM,OAAO,UAAW,SAAQ,OAAO,CAAC,OAAO,EAiD5C,CAAC,gCAAgC,CAAC;CAAG"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { Option, Redacted } from "effect";
|
|
2
|
+
import { Context, Data } from "effect";
|
|
3
|
+
import type { BlobAccess, StoreId } from "./blob-domain.ts";
|
|
4
|
+
/** How server-side calls authenticate to the Blob API. */
|
|
5
|
+
export type BlobAuth = Data.TaggedEnum<{
|
|
6
|
+
/**
|
|
7
|
+
* Use the Vercel OIDC token of the current request with the store's id. When no OIDC token is
|
|
8
|
+
* available and `BlobConfig.readWriteToken` is set, a request falls back to that token, as the
|
|
9
|
+
* SDK does.
|
|
10
|
+
*/
|
|
11
|
+
Oidc: {
|
|
12
|
+
readonly storeId: StoreId;
|
|
13
|
+
};
|
|
14
|
+
/** Use a store's read-write token (`vercel_blob_rw_<storeId>_…`). */
|
|
15
|
+
ReadWrite: {
|
|
16
|
+
readonly token: Redacted.Redacted<string>;
|
|
17
|
+
readonly storeId: StoreId;
|
|
18
|
+
};
|
|
19
|
+
}>;
|
|
20
|
+
/** Constructors and matchers for {@link BlobAuth}. */
|
|
21
|
+
export declare const BlobAuth: {
|
|
22
|
+
readonly $is: <Tag extends "Oidc" | "ReadWrite">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
23
|
+
readonly _tag: "Oidc";
|
|
24
|
+
readonly storeId: StoreId;
|
|
25
|
+
}, {
|
|
26
|
+
readonly _tag: Tag;
|
|
27
|
+
}> | Extract<{
|
|
28
|
+
readonly _tag: "ReadWrite";
|
|
29
|
+
readonly token: Redacted.Redacted<string>;
|
|
30
|
+
readonly storeId: StoreId;
|
|
31
|
+
}, {
|
|
32
|
+
readonly _tag: Tag;
|
|
33
|
+
}>;
|
|
34
|
+
readonly $match: {
|
|
35
|
+
<Cases extends { readonly [Tag in "Oidc" | "ReadWrite"]: (args: Extract<{
|
|
36
|
+
readonly _tag: "Oidc";
|
|
37
|
+
readonly storeId: StoreId;
|
|
38
|
+
} | {
|
|
39
|
+
readonly _tag: "ReadWrite";
|
|
40
|
+
readonly token: Redacted.Redacted<string>;
|
|
41
|
+
readonly storeId: StoreId;
|
|
42
|
+
}, {
|
|
43
|
+
readonly _tag: Tag;
|
|
44
|
+
}>) => any; }>(cases: Cases): (value: {
|
|
45
|
+
readonly _tag: "Oidc";
|
|
46
|
+
readonly storeId: StoreId;
|
|
47
|
+
} | {
|
|
48
|
+
readonly _tag: "ReadWrite";
|
|
49
|
+
readonly token: Redacted.Redacted<string>;
|
|
50
|
+
readonly storeId: StoreId;
|
|
51
|
+
}) => import("effect/Unify").Unify<ReturnType<Cases["Oidc" | "ReadWrite"]>>;
|
|
52
|
+
<Cases extends { readonly [Tag in "Oidc" | "ReadWrite"]: (args: Extract<{
|
|
53
|
+
readonly _tag: "Oidc";
|
|
54
|
+
readonly storeId: StoreId;
|
|
55
|
+
} | {
|
|
56
|
+
readonly _tag: "ReadWrite";
|
|
57
|
+
readonly token: Redacted.Redacted<string>;
|
|
58
|
+
readonly storeId: StoreId;
|
|
59
|
+
}, {
|
|
60
|
+
readonly _tag: Tag;
|
|
61
|
+
}>) => any; }>(value: {
|
|
62
|
+
readonly _tag: "Oidc";
|
|
63
|
+
readonly storeId: StoreId;
|
|
64
|
+
} | {
|
|
65
|
+
readonly _tag: "ReadWrite";
|
|
66
|
+
readonly token: Redacted.Redacted<string>;
|
|
67
|
+
readonly storeId: StoreId;
|
|
68
|
+
}, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Oidc" | "ReadWrite"]>>;
|
|
69
|
+
};
|
|
70
|
+
readonly Oidc: Data.TaggedEnum.ConstructorFrom<{
|
|
71
|
+
readonly _tag: "Oidc";
|
|
72
|
+
readonly storeId: StoreId;
|
|
73
|
+
}, "_tag">;
|
|
74
|
+
readonly ReadWrite: Data.TaggedEnum.ConstructorFrom<{
|
|
75
|
+
readonly _tag: "ReadWrite";
|
|
76
|
+
readonly token: Redacted.Redacted<string>;
|
|
77
|
+
readonly storeId: StoreId;
|
|
78
|
+
}, "_tag">;
|
|
79
|
+
};
|
|
80
|
+
declare const BlobConfig_base: Context.ServiceClass<BlobConfig, "@effect-vercel/blob/BlobConfig", {
|
|
81
|
+
/** Blob API base URL, `https://vercel.com/api/blob` unless overridden for an emulator. */
|
|
82
|
+
readonly apiUrl: URL;
|
|
83
|
+
readonly auth: BlobAuth;
|
|
84
|
+
/**
|
|
85
|
+
* The store's read-write token, when one is configured. Besides the OIDC fallback, it signs
|
|
86
|
+
* client tokens and verifies upload-completed callbacks, which OIDC cannot do.
|
|
87
|
+
*/
|
|
88
|
+
readonly readWriteToken: Option.Option<Redacted.Redacted<string>>;
|
|
89
|
+
/** Sent as `x-api-version`; selects the server's response format. */
|
|
90
|
+
readonly apiVersion: string;
|
|
91
|
+
/** Retries after the first attempt for network failures and retryable server errors. */
|
|
92
|
+
readonly retries: number;
|
|
93
|
+
/** Send `x-content-length` with every body, which local development servers need for streams. */
|
|
94
|
+
readonly sendContentLength: boolean;
|
|
95
|
+
/** Extra headers sent with every API request. */
|
|
96
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
97
|
+
/**
|
|
98
|
+
* Origin and path under which a store serves its blobs; `get` appends the pathname. Defaults to
|
|
99
|
+
* `https://<storeId>.<access>.blob.vercel-storage.com/`.
|
|
100
|
+
*/
|
|
101
|
+
readonly blobBaseUrl: (storeId: StoreId, access: BlobAccess) => URL;
|
|
102
|
+
/** Ed25519 public key (PEM) that verifies presigned-upload callbacks. */
|
|
103
|
+
readonly webhookPublicKey: Option.Option<string>;
|
|
104
|
+
/**
|
|
105
|
+
* Origin that upload-completed callbacks are sent to, such as a tunnel in local development
|
|
106
|
+
* (`VERCEL_BLOB_CALLBACK_URL`); the upload route's path is appended.
|
|
107
|
+
*/
|
|
108
|
+
readonly callbackBaseUrl: Option.Option<string>;
|
|
109
|
+
}>;
|
|
110
|
+
/** Where and as whom the server-side Blob client talks to Vercel Blob. */
|
|
111
|
+
export declare class BlobConfig extends BlobConfig_base {
|
|
112
|
+
}
|
|
113
|
+
export {};
|
|
114
|
+
//# sourceMappingURL=blob-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob-config.d.ts","sourceRoot":"","sources":["../../src/contracts/blob-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE5D,0DAA0D;AAC1D,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC;IACrC;;;;OAIG;IACH,IAAI,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IACpC,qEAAqE;IACrE,SAAS,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;CACrF,CAAC,CAAC;AAEH,sDAAsD;AACtD,eAAO,MAAM,QAAQ;;;0BANO,OAAO;;;;;wBAEJ,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;0BAAoB,OAAO;;;;;;;8BAFvD,OAAO;;;4BAEJ,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;8BAAoB,OAAO;;;;;;;;;;;;;8BAFvD,OAAO;;;4BAEJ,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;8BAAoB,OAAO;;;;;;;;;;;;;;0BAFvD,OAAO;;;;wBAEJ,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;0BAAoB,OAAO;;CAIhC,CAAC;;IAMhD,0FAA0F;qBACzE,GAAG;mBAEL,QAAQ;IAEvB;;;OAGG;6BACsB,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEjE,qEAAqE;yBAChD,MAAM;IAE3B,wFAAwF;sBACtE,MAAM;IAExB,iGAAiG;gCACrE,OAAO;IAEnC,iDAAiD;sBAC/B,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAElD;;;OAGG;0BACmB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,KAAK,GAAG;IAEnE,yEAAyE;+BAC9C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;IAEhD;;;OAGG;8BACuB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;;AAxCnD,0EAA0E;AAC1E,qBAAa,UAAW,SAAQ,eAyCK;CAAG"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Context, Data } from "effect";
|
|
2
|
+
/** Constructors and matchers for {@link BlobAuth}. */
|
|
3
|
+
export const BlobAuth = Data.taggedEnum();
|
|
4
|
+
/** Where and as whom the server-side Blob client talks to Vercel Blob. */
|
|
5
|
+
export class BlobConfig extends Context.Service()("@effect-vercel/blob/BlobConfig") {
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=blob-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob-config.js","sourceRoot":"","sources":["../../src/contracts/blob-config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAgBvC,sDAAsD;AACtD,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAY,CAAC;AAEpD,0EAA0E;AAC1E,MAAM,OAAO,UAAW,SAAQ,OAAO,CAAC,OAAO,EAyC5C,CAAC,gCAAgC,CAAC;CAAG"}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import type { VercelApiError } from "@panorama-industries/effect-vercel-core";
|
|
2
|
+
import type { DateTime, Duration, Effect, Stream } from "effect";
|
|
3
|
+
import { Data, Schema } from "effect";
|
|
4
|
+
/** Whether a blob is readable by anyone with its URL (`public`) or only with credentials. */
|
|
5
|
+
export declare const BlobAccess: Schema.Literals<readonly ["public", "private"]>;
|
|
6
|
+
/** Whether a blob is readable by anyone with its URL (`public`) or only with credentials. */
|
|
7
|
+
export type BlobAccess = typeof BlobAccess.Type;
|
|
8
|
+
/**
|
|
9
|
+
* Identifier of a Blob store without its `store_` prefix, as sent in `x-vercel-blob-store-id` and
|
|
10
|
+
* used in blob host names. Case-sensitive.
|
|
11
|
+
*/
|
|
12
|
+
export declare const StoreId: Schema.brand<Schema.String, "StoreId">;
|
|
13
|
+
/** Identifier of a Blob store without its `store_` prefix. */
|
|
14
|
+
export type StoreId = typeof StoreId.Type;
|
|
15
|
+
/** Longest pathname a write accepts: the provider limit minus room for Vercel's suffixes. */
|
|
16
|
+
export declare const maximumPathnameLength = 950;
|
|
17
|
+
/**
|
|
18
|
+
* Where a write stores a blob inside its store, such as `avatars/user-1.png`: non-empty, at most
|
|
19
|
+
* {@link maximumPathnameLength} characters, and without `//`.
|
|
20
|
+
*
|
|
21
|
+
* Reads and deletes take any pathname or blob URL as a plain string, because stored pathnames may
|
|
22
|
+
* be longer once a random suffix is added.
|
|
23
|
+
*/
|
|
24
|
+
export declare const BlobPathname: Schema.brand<Schema.String, "BlobPathname">;
|
|
25
|
+
/** Where a write stores a blob inside its store. */
|
|
26
|
+
export type BlobPathname = typeof BlobPathname.Type;
|
|
27
|
+
/** A blob to read or delete: its pathname in the store, or its full URL. */
|
|
28
|
+
export type BlobLocator = string | URL;
|
|
29
|
+
/** Version tag of a blob's content, used for conditional writes, deletes and reads. */
|
|
30
|
+
export declare const ETag: Schema.brand<Schema.NonEmptyString, "ETag">;
|
|
31
|
+
/** Version tag of a blob's content. */
|
|
32
|
+
export type ETag = typeof ETag.Type;
|
|
33
|
+
/** Position of a part in a multipart upload: an integer from 1 to 10000. */
|
|
34
|
+
export declare const PartNumber: Schema.brand<Schema.Int, "PartNumber">;
|
|
35
|
+
/** Position of a part in a multipart upload: an integer from 1 to 10000. */
|
|
36
|
+
export type PartNumber = typeof PartNumber.Type;
|
|
37
|
+
/**
|
|
38
|
+
* Content to upload. A stream is sent as it is produced; its failures and requirements become the
|
|
39
|
+
* operation's. Convert a Node `Readable` with `NodeStream.fromReadable` and a web `ReadableStream`
|
|
40
|
+
* with `Stream.fromReadableStream`.
|
|
41
|
+
*/
|
|
42
|
+
export type BlobBody<E = never, R = never> = string | Uint8Array | globalThis.Blob | Stream.Stream<Uint8Array, E, R>;
|
|
43
|
+
/** Bytes sent so far during an upload. */
|
|
44
|
+
export interface UploadProgress {
|
|
45
|
+
readonly loaded: number;
|
|
46
|
+
/** Bytes to send in total; equals `loaded` when the size of a streamed body is unknown. */
|
|
47
|
+
readonly total: number;
|
|
48
|
+
/** Completion from 0 to 100 with two decimals; 100 is reported only once the upload finished. */
|
|
49
|
+
readonly percentage: number;
|
|
50
|
+
}
|
|
51
|
+
/** Callback for upload progress events, throttled like `@vercel/blob`. */
|
|
52
|
+
export type OnUploadProgress = (progress: UploadProgress) => Effect.Effect<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Whether a write may replace an existing blob. Either set `allowOverwrite`, or make the write
|
|
55
|
+
* conditional with `ifMatch`, which implies overwriting.
|
|
56
|
+
*/
|
|
57
|
+
export type OverwriteOptions = {
|
|
58
|
+
/** Replace a blob already stored at the pathname instead of failing. Defaults to false. */
|
|
59
|
+
readonly allowOverwrite?: boolean | undefined;
|
|
60
|
+
readonly ifMatch?: undefined;
|
|
61
|
+
} | {
|
|
62
|
+
readonly allowOverwrite?: true | undefined;
|
|
63
|
+
/** Write only if the current blob has this ETag; otherwise fail with `BlobPreconditionFailed`. */
|
|
64
|
+
readonly ifMatch: ETag;
|
|
65
|
+
};
|
|
66
|
+
/** Options shared by every operation that creates a blob. */
|
|
67
|
+
export type CreateBlobOptions = OverwriteOptions & {
|
|
68
|
+
readonly access: BlobAccess;
|
|
69
|
+
/** Append a random suffix to the pathname so it never collides. */
|
|
70
|
+
readonly addRandomSuffix?: boolean | undefined;
|
|
71
|
+
/** Media type served with the blob; inferred from the pathname extension by default. */
|
|
72
|
+
readonly contentType?: string | undefined;
|
|
73
|
+
/** How long browsers and the CDN cache the blob: at least one minute, one month by default. */
|
|
74
|
+
readonly cacheControlMaxAge?: Duration.Input | undefined;
|
|
75
|
+
};
|
|
76
|
+
declare const PutBlobResult_base: Schema.Class<PutBlobResult, Schema.Struct<{
|
|
77
|
+
readonly url: Schema.URL;
|
|
78
|
+
/** The URL with `?download=1`, which makes browsers download instead of display. */
|
|
79
|
+
readonly downloadUrl: Schema.URL;
|
|
80
|
+
readonly pathname: Schema.String;
|
|
81
|
+
readonly contentType: Schema.String;
|
|
82
|
+
readonly contentDisposition: Schema.String;
|
|
83
|
+
readonly etag: Schema.brand<Schema.NonEmptyString, "ETag">;
|
|
84
|
+
}>, {}>;
|
|
85
|
+
/** A stored blob as returned by writes. */
|
|
86
|
+
export declare class PutBlobResult extends PutBlobResult_base {
|
|
87
|
+
}
|
|
88
|
+
declare const BlobMetadata_base: Schema.Class<BlobMetadata, Schema.Struct<{
|
|
89
|
+
readonly url: Schema.URL;
|
|
90
|
+
readonly downloadUrl: Schema.URL;
|
|
91
|
+
readonly pathname: Schema.String;
|
|
92
|
+
/** Size in bytes. */
|
|
93
|
+
readonly size: Schema.Int;
|
|
94
|
+
readonly uploadedAt: Schema.DateTimeUtc;
|
|
95
|
+
readonly contentType: Schema.String;
|
|
96
|
+
readonly contentDisposition: Schema.String;
|
|
97
|
+
readonly cacheControl: Schema.String;
|
|
98
|
+
readonly etag: Schema.brand<Schema.NonEmptyString, "ETag">;
|
|
99
|
+
}>, {}>;
|
|
100
|
+
/** Everything known about a stored blob. */
|
|
101
|
+
export declare class BlobMetadata extends BlobMetadata_base {
|
|
102
|
+
}
|
|
103
|
+
declare const ListedBlob_base: Schema.Class<ListedBlob, Schema.Struct<{
|
|
104
|
+
readonly url: Schema.URL;
|
|
105
|
+
readonly downloadUrl: Schema.URL;
|
|
106
|
+
readonly pathname: Schema.String;
|
|
107
|
+
readonly size: Schema.Int;
|
|
108
|
+
readonly uploadedAt: Schema.DateTimeUtc;
|
|
109
|
+
readonly etag: Schema.brand<Schema.NonEmptyString, "ETag">;
|
|
110
|
+
}>, {}>;
|
|
111
|
+
/** One entry of a listing. */
|
|
112
|
+
export declare class ListedBlob extends ListedBlob_base {
|
|
113
|
+
}
|
|
114
|
+
declare const ListPage_base: Schema.Class<ListPage, Schema.Struct<{
|
|
115
|
+
readonly blobs: Schema.$Array<typeof ListedBlob>;
|
|
116
|
+
/** Fully qualified folder pathnames directly under the prefix; empty unless `mode` is `folded`. */
|
|
117
|
+
readonly folders: Schema.$Array<Schema.String>;
|
|
118
|
+
/** Pass to the next `list` call to continue; none on the last page. */
|
|
119
|
+
readonly cursor: Schema.Option<Schema.String>;
|
|
120
|
+
readonly hasMore: Schema.Boolean;
|
|
121
|
+
}>, {}>;
|
|
122
|
+
/** One page of a listing, in lexicographic pathname order. */
|
|
123
|
+
export declare class ListPage extends ListPage_base {
|
|
124
|
+
}
|
|
125
|
+
declare const CreateFolderResult_base: Schema.Class<CreateFolderResult, Schema.Struct<{
|
|
126
|
+
readonly url: Schema.URL;
|
|
127
|
+
/** The folder pathname, ending in `/`. */
|
|
128
|
+
readonly pathname: Schema.String;
|
|
129
|
+
}>, {}>;
|
|
130
|
+
/** A folder created with `createFolder`. */
|
|
131
|
+
export declare class CreateFolderResult extends CreateFolderResult_base {
|
|
132
|
+
}
|
|
133
|
+
declare const FetchedBlob_base: Schema.Class<FetchedBlob, Schema.Struct<{
|
|
134
|
+
readonly url: Schema.URL;
|
|
135
|
+
readonly downloadUrl: Schema.URL;
|
|
136
|
+
readonly pathname: Schema.String;
|
|
137
|
+
readonly contentDisposition: Schema.String;
|
|
138
|
+
readonly cacheControl: Schema.String;
|
|
139
|
+
/** `Last-Modified`, or the time of the read when the response had none. */
|
|
140
|
+
readonly uploadedAt: Schema.DateTimeUtc;
|
|
141
|
+
readonly etag: Schema.String;
|
|
142
|
+
}>, {}>;
|
|
143
|
+
/** Metadata of a blob read with `get`, from its response headers. */
|
|
144
|
+
export declare class FetchedBlob extends FetchedBlob_base {
|
|
145
|
+
}
|
|
146
|
+
/** Outcome of `get`. */
|
|
147
|
+
export type GetBlobResult = Data.TaggedEnum<{
|
|
148
|
+
/** The blob's content (HTTP 200). */
|
|
149
|
+
Found: {
|
|
150
|
+
readonly blob: FetchedBlob;
|
|
151
|
+
readonly contentType: string;
|
|
152
|
+
/** `Content-Length`, or 0 when the response had none. */
|
|
153
|
+
readonly size: number;
|
|
154
|
+
/** Response headers, for anything not in `blob`. */
|
|
155
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
156
|
+
/** The content, read as it arrives. Read it at most once. */
|
|
157
|
+
readonly body: Stream.Stream<Uint8Array, VercelApiError>;
|
|
158
|
+
};
|
|
159
|
+
/** The blob still has the ETag given as `ifNoneMatch` (HTTP 304). */
|
|
160
|
+
NotModified: {
|
|
161
|
+
readonly blob: FetchedBlob;
|
|
162
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
163
|
+
};
|
|
164
|
+
}>;
|
|
165
|
+
/** Constructors and matchers for {@link GetBlobResult}. */
|
|
166
|
+
export declare const GetBlobResult: {
|
|
167
|
+
readonly $is: <Tag extends "Found" | "NotModified">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
168
|
+
readonly _tag: "Found";
|
|
169
|
+
readonly blob: FetchedBlob;
|
|
170
|
+
readonly contentType: string;
|
|
171
|
+
readonly size: number;
|
|
172
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
173
|
+
readonly body: Stream.Stream<Uint8Array, VercelApiError>;
|
|
174
|
+
}, {
|
|
175
|
+
readonly _tag: Tag;
|
|
176
|
+
}> | Extract<{
|
|
177
|
+
readonly _tag: "NotModified";
|
|
178
|
+
readonly blob: FetchedBlob;
|
|
179
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
180
|
+
}, {
|
|
181
|
+
readonly _tag: Tag;
|
|
182
|
+
}>;
|
|
183
|
+
readonly $match: {
|
|
184
|
+
<Cases extends { readonly [Tag in "Found" | "NotModified"]: (args: Extract<{
|
|
185
|
+
readonly _tag: "Found";
|
|
186
|
+
readonly blob: FetchedBlob;
|
|
187
|
+
readonly contentType: string;
|
|
188
|
+
readonly size: number;
|
|
189
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
190
|
+
readonly body: Stream.Stream<Uint8Array, VercelApiError>;
|
|
191
|
+
} | {
|
|
192
|
+
readonly _tag: "NotModified";
|
|
193
|
+
readonly blob: FetchedBlob;
|
|
194
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
195
|
+
}, {
|
|
196
|
+
readonly _tag: Tag;
|
|
197
|
+
}>) => any; }>(cases: Cases): (value: {
|
|
198
|
+
readonly _tag: "Found";
|
|
199
|
+
readonly blob: FetchedBlob;
|
|
200
|
+
readonly contentType: string;
|
|
201
|
+
readonly size: number;
|
|
202
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
203
|
+
readonly body: Stream.Stream<Uint8Array, VercelApiError>;
|
|
204
|
+
} | {
|
|
205
|
+
readonly _tag: "NotModified";
|
|
206
|
+
readonly blob: FetchedBlob;
|
|
207
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
208
|
+
}) => import("effect/Unify").Unify<ReturnType<Cases["Found" | "NotModified"]>>;
|
|
209
|
+
<Cases extends { readonly [Tag in "Found" | "NotModified"]: (args: Extract<{
|
|
210
|
+
readonly _tag: "Found";
|
|
211
|
+
readonly blob: FetchedBlob;
|
|
212
|
+
readonly contentType: string;
|
|
213
|
+
readonly size: number;
|
|
214
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
215
|
+
readonly body: Stream.Stream<Uint8Array, VercelApiError>;
|
|
216
|
+
} | {
|
|
217
|
+
readonly _tag: "NotModified";
|
|
218
|
+
readonly blob: FetchedBlob;
|
|
219
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
220
|
+
}, {
|
|
221
|
+
readonly _tag: Tag;
|
|
222
|
+
}>) => any; }>(value: {
|
|
223
|
+
readonly _tag: "Found";
|
|
224
|
+
readonly blob: FetchedBlob;
|
|
225
|
+
readonly contentType: string;
|
|
226
|
+
readonly size: number;
|
|
227
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
228
|
+
readonly body: Stream.Stream<Uint8Array, VercelApiError>;
|
|
229
|
+
} | {
|
|
230
|
+
readonly _tag: "NotModified";
|
|
231
|
+
readonly blob: FetchedBlob;
|
|
232
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
233
|
+
}, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Found" | "NotModified"]>>;
|
|
234
|
+
};
|
|
235
|
+
readonly Found: Data.TaggedEnum.ConstructorFrom<{
|
|
236
|
+
readonly _tag: "Found";
|
|
237
|
+
readonly blob: FetchedBlob;
|
|
238
|
+
readonly contentType: string;
|
|
239
|
+
readonly size: number;
|
|
240
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
241
|
+
readonly body: Stream.Stream<Uint8Array, VercelApiError>;
|
|
242
|
+
}, "_tag">;
|
|
243
|
+
readonly NotModified: Data.TaggedEnum.ConstructorFrom<{
|
|
244
|
+
readonly _tag: "NotModified";
|
|
245
|
+
readonly blob: FetchedBlob;
|
|
246
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
247
|
+
}, "_tag">;
|
|
248
|
+
};
|
|
249
|
+
declare const MultipartUpload_base: Schema.Class<MultipartUpload, Schema.Struct<{
|
|
250
|
+
readonly pathname: Schema.brand<Schema.String, "BlobPathname">;
|
|
251
|
+
/** Identifies the blob object being assembled. */
|
|
252
|
+
readonly key: Schema.String;
|
|
253
|
+
readonly uploadId: Schema.String;
|
|
254
|
+
readonly access: Schema.Literals<readonly ["public", "private"]>;
|
|
255
|
+
readonly addRandomSuffix: Schema.optionalKey<Schema.Boolean>;
|
|
256
|
+
readonly allowOverwrite: Schema.optionalKey<Schema.Boolean>;
|
|
257
|
+
readonly contentType: Schema.optionalKey<Schema.String>;
|
|
258
|
+
/** In seconds, as sent on the wire. */
|
|
259
|
+
readonly cacheControlMaxAge: Schema.optionalKey<Schema.Int>;
|
|
260
|
+
}>, {}>;
|
|
261
|
+
/**
|
|
262
|
+
* A multipart upload in progress. Every write option is kept so parts and completion send the same
|
|
263
|
+
* headers as creation; the value can be serialized and resumed by another process.
|
|
264
|
+
*/
|
|
265
|
+
export declare class MultipartUpload extends MultipartUpload_base {
|
|
266
|
+
}
|
|
267
|
+
declare const UploadedPart_base: Schema.Class<UploadedPart, Schema.Struct<{
|
|
268
|
+
readonly etag: Schema.String;
|
|
269
|
+
readonly partNumber: Schema.brand<Schema.Int, "PartNumber">;
|
|
270
|
+
}>, {}>;
|
|
271
|
+
/** A part uploaded to a multipart upload, needed to complete it. */
|
|
272
|
+
export declare class UploadedPart extends UploadedPart_base {
|
|
273
|
+
}
|
|
274
|
+
/** Operations a signed token can delegate. */
|
|
275
|
+
export declare const DelegationOperation: Schema.Literals<readonly ["get", "head", "put", "delete"]>;
|
|
276
|
+
/** Operations a signed token can delegate. */
|
|
277
|
+
export type DelegationOperation = typeof DelegationOperation.Type;
|
|
278
|
+
declare const PresignedUrlPayload_base: Schema.Class<PresignedUrlPayload, Schema.Struct<{
|
|
279
|
+
/** Public part of the signed token; the store id is read from it. */
|
|
280
|
+
readonly delegationToken: Schema.String;
|
|
281
|
+
/** HMAC-SHA256 (base64url) of the request's canonical string, keyed by the client signing token. */
|
|
282
|
+
readonly signature: Schema.String;
|
|
283
|
+
/** Signed constraints, sent as query parameters. */
|
|
284
|
+
readonly params: Schema.$Record<Schema.String, Schema.String>;
|
|
285
|
+
}>, {}>;
|
|
286
|
+
/** Query parameters that authorize one presigned request instead of a bearer token. */
|
|
287
|
+
export declare class PresignedUrlPayload extends PresignedUrlPayload_base {
|
|
288
|
+
}
|
|
289
|
+
declare const SignedToken_base: Schema.Class<SignedToken, Schema.Struct<{
|
|
290
|
+
/** Public; embedded in every presigned URL. */
|
|
291
|
+
readonly delegationToken: Schema.String;
|
|
292
|
+
/** Secret HMAC key for presigning. Never send it to clients. */
|
|
293
|
+
readonly clientSigningToken: Schema.Redacted<Schema.String>;
|
|
294
|
+
readonly validUntil: Schema.DateTimeUtc;
|
|
295
|
+
}>, {}>;
|
|
296
|
+
/** A short-lived token, issued by the Blob API, from which presigned URLs are signed locally. */
|
|
297
|
+
export declare class SignedToken extends SignedToken_base {
|
|
298
|
+
}
|
|
299
|
+
/** A token that lets a browser upload one pathname directly (`vercel_blob_client_…`). */
|
|
300
|
+
export declare const ClientToken: Schema.brand<Schema.String, "ClientToken">;
|
|
301
|
+
/** A token that lets a browser upload one pathname directly. */
|
|
302
|
+
export type ClientToken = typeof ClientToken.Type;
|
|
303
|
+
/**
|
|
304
|
+
* Constraints a server places on a browser upload when it issues a client token. As for writes,
|
|
305
|
+
* `ifMatch` implies `allowOverwrite`.
|
|
306
|
+
*/
|
|
307
|
+
export type ClientTokenConstraints = OverwriteOptions & {
|
|
308
|
+
/** Largest accepted file, at most 5 TB. */
|
|
309
|
+
readonly maximumSizeInBytes?: number | undefined;
|
|
310
|
+
/** Accepted media types; wildcards such as `image/*` are allowed. */
|
|
311
|
+
readonly allowedContentTypes?: ReadonlyArray<string> | undefined;
|
|
312
|
+
/** Expiry; defaults depend on the caller (one hour for upload routes). */
|
|
313
|
+
readonly validUntil?: DateTime.Utc | undefined;
|
|
314
|
+
readonly addRandomSuffix?: boolean | undefined;
|
|
315
|
+
/** In seconds. */
|
|
316
|
+
readonly cacheControlMaxAge?: number | undefined;
|
|
317
|
+
};
|
|
318
|
+
declare const UploadCompleted_base: Schema.Class<UploadCompleted, Schema.Struct<{
|
|
319
|
+
readonly blob: typeof PutBlobResult;
|
|
320
|
+
/** Data the server attached when it authorized the upload. */
|
|
321
|
+
readonly tokenPayload: Schema.Option<Schema.String>;
|
|
322
|
+
}>, {}>;
|
|
323
|
+
/** The blob store's notification that a browser upload finished. */
|
|
324
|
+
export declare class UploadCompleted extends UploadCompleted_base {
|
|
325
|
+
}
|
|
326
|
+
declare const UploadRequest_base: Schema.Class<UploadRequest, Schema.Struct<{
|
|
327
|
+
readonly pathname: Schema.String;
|
|
328
|
+
/** Data the browser sent along, such as a form id; untrusted. */
|
|
329
|
+
readonly clientPayload: Schema.Option<Schema.String>;
|
|
330
|
+
readonly multipart: Schema.Boolean;
|
|
331
|
+
}>, {}>;
|
|
332
|
+
/** What a browser asks for when it starts an upload through an upload route. */
|
|
333
|
+
export declare class UploadRequest extends UploadRequest_base {
|
|
334
|
+
}
|
|
335
|
+
export {};
|
|
336
|
+
//# sourceMappingURL=blob-domain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob-domain.d.ts","sourceRoot":"","sources":["../../src/contracts/blob-domain.ts"],"names":[],"mappings":"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"}
|