@panorama-industries/effect-vercel-blob 0.0.0-stage → 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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +13 -2
  3. package/dist/client.d.ts +8 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +8 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/contracts/blob-client.d.ts +67 -0
  8. package/dist/contracts/blob-client.d.ts.map +1 -0
  9. package/dist/contracts/blob-client.js +11 -0
  10. package/dist/contracts/blob-client.js.map +1 -0
  11. package/dist/contracts/blob-config.d.ts +114 -0
  12. package/dist/contracts/blob-config.d.ts.map +1 -0
  13. package/dist/contracts/blob-config.js +7 -0
  14. package/dist/contracts/blob-config.js.map +1 -0
  15. package/dist/contracts/blob-domain.d.ts +336 -0
  16. package/dist/contracts/blob-domain.d.ts.map +1 -0
  17. package/dist/contracts/blob-domain.js +152 -0
  18. package/dist/contracts/blob-domain.js.map +1 -0
  19. package/dist/contracts/blob-errors.d.ts +125 -0
  20. package/dist/contracts/blob-errors.d.ts.map +1 -0
  21. package/dist/contracts/blob-errors.js +114 -0
  22. package/dist/contracts/blob-errors.js.map +1 -0
  23. package/dist/contracts/blob-signed-urls.d.ts +83 -0
  24. package/dist/contracts/blob-signed-urls.d.ts.map +1 -0
  25. package/dist/contracts/blob-signed-urls.js +10 -0
  26. package/dist/contracts/blob-signed-urls.js.map +1 -0
  27. package/dist/contracts/blob-store.d.ts +153 -0
  28. package/dist/contracts/blob-store.d.ts.map +1 -0
  29. package/dist/contracts/blob-store.js +15 -0
  30. package/dist/contracts/blob-store.js.map +1 -0
  31. package/dist/contracts/blob-uploads.d.ts +101 -0
  32. package/dist/contracts/blob-uploads.d.ts.map +1 -0
  33. package/dist/contracts/blob-uploads.js +17 -0
  34. package/dist/contracts/blob-uploads.js.map +1 -0
  35. package/dist/implementation/blob-client-live.d.ts +37 -0
  36. package/dist/implementation/blob-client-live.d.ts.map +1 -0
  37. package/dist/implementation/blob-client-live.js +213 -0
  38. package/dist/implementation/blob-client-live.js.map +1 -0
  39. package/dist/implementation/blob-config-live.d.ts +49 -0
  40. package/dist/implementation/blob-config-live.d.ts.map +1 -0
  41. package/dist/implementation/blob-config-live.js +82 -0
  42. package/dist/implementation/blob-config-live.js.map +1 -0
  43. package/dist/implementation/blob-live.d.ts +16 -0
  44. package/dist/implementation/blob-live.d.ts.map +1 -0
  45. package/dist/implementation/blob-live.js +16 -0
  46. package/dist/implementation/blob-live.js.map +1 -0
  47. package/dist/implementation/blob-signed-urls-live.d.ts +12 -0
  48. package/dist/implementation/blob-signed-urls-live.d.ts.map +1 -0
  49. package/dist/implementation/blob-signed-urls-live.js +92 -0
  50. package/dist/implementation/blob-signed-urls-live.js.map +1 -0
  51. package/dist/implementation/blob-store-live.d.ts +18 -0
  52. package/dist/implementation/blob-store-live.d.ts.map +1 -0
  53. package/dist/implementation/blob-store-live.js +364 -0
  54. package/dist/implementation/blob-store-live.js.map +1 -0
  55. package/dist/implementation/blob-uploads-live.d.ts +11 -0
  56. package/dist/implementation/blob-uploads-live.d.ts.map +1 -0
  57. package/dist/implementation/blob-uploads-live.js +218 -0
  58. package/dist/implementation/blob-uploads-live.js.map +1 -0
  59. package/dist/index.d.ts +15 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +15 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/internal/blob-errors.d.ts +79 -0
  64. package/dist/internal/blob-errors.d.ts.map +1 -0
  65. package/dist/internal/blob-errors.js +196 -0
  66. package/dist/internal/blob-errors.js.map +1 -0
  67. package/dist/internal/blob-request.d.ts +255 -0
  68. package/dist/internal/blob-request.d.ts.map +1 -0
  69. package/dist/internal/blob-request.js +291 -0
  70. package/dist/internal/blob-request.js.map +1 -0
  71. package/dist/internal/callback.d.ts +70 -0
  72. package/dist/internal/callback.d.ts.map +1 -0
  73. package/dist/internal/callback.js +141 -0
  74. package/dist/internal/callback.js.map +1 -0
  75. package/dist/internal/client-token.d.ts +65 -0
  76. package/dist/internal/client-token.d.ts.map +1 -0
  77. package/dist/internal/client-token.js +80 -0
  78. package/dist/internal/client-token.js.map +1 -0
  79. package/dist/internal/env.d.ts +34 -0
  80. package/dist/internal/env.d.ts.map +1 -0
  81. package/dist/internal/env.js +61 -0
  82. package/dist/internal/env.js.map +1 -0
  83. package/dist/internal/multipart.d.ts +110 -0
  84. package/dist/internal/multipart.d.ts.map +1 -0
  85. package/dist/internal/multipart.js +290 -0
  86. package/dist/internal/multipart.js.map +1 -0
  87. package/dist/internal/presign.d.ts +123 -0
  88. package/dist/internal/presign.d.ts.map +1 -0
  89. package/dist/internal/presign.js +345 -0
  90. package/dist/internal/presign.js.map +1 -0
  91. package/dist/internal/store-id.d.ts +38 -0
  92. package/dist/internal/store-id.d.ts.map +1 -0
  93. package/dist/internal/store-id.js +54 -0
  94. package/dist/internal/store-id.js.map +1 -0
  95. package/dist/internal/urls.d.ts +26 -0
  96. package/dist/internal/urls.d.ts.map +1 -0
  97. package/dist/internal/urls.js +33 -0
  98. package/dist/internal/urls.js.map +1 -0
  99. package/dist/internal/wire.d.ts +55 -0
  100. package/dist/internal/wire.d.ts.map +1 -0
  101. package/dist/internal/wire.js +64 -0
  102. package/dist/internal/wire.js.map +1 -0
  103. package/dist/internal/write-headers.d.ts +56 -0
  104. package/dist/internal/write-headers.d.ts.map +1 -0
  105. package/dist/internal/write-headers.js +82 -0
  106. package/dist/internal/write-headers.js.map +1 -0
  107. package/dist/testing/index.d.ts +2 -0
  108. package/dist/testing/index.d.ts.map +1 -0
  109. package/dist/testing/index.js +2 -0
  110. package/dist/testing/index.js.map +1 -0
  111. package/dist/testing/test-blob-store.d.ts +134 -0
  112. package/dist/testing/test-blob-store.d.ts.map +1 -0
  113. package/dist/testing/test-blob-store.js +718 -0
  114. package/dist/testing/test-blob-store.js.map +1 -0
  115. package/package.json +65 -3
  116. package/src/client.ts +11 -0
  117. package/src/contracts/blob-client.ts +121 -0
  118. package/src/contracts/blob-config.ts +64 -0
  119. package/src/contracts/blob-domain.ts +304 -0
  120. package/src/contracts/blob-errors.ts +161 -0
  121. package/src/contracts/blob-signed-urls.ts +112 -0
  122. package/src/contracts/blob-store.ts +258 -0
  123. package/src/contracts/blob-uploads.ts +151 -0
  124. package/src/implementation/blob-client-live.ts +387 -0
  125. package/src/implementation/blob-config-live.ts +145 -0
  126. package/src/implementation/blob-live.ts +30 -0
  127. package/src/implementation/blob-signed-urls-live.ts +121 -0
  128. package/src/implementation/blob-store-live.ts +528 -0
  129. package/src/implementation/blob-uploads-live.ts +326 -0
  130. package/src/index.ts +18 -0
  131. package/src/internal/blob-errors.ts +325 -0
  132. package/src/internal/blob-request.ts +617 -0
  133. package/src/internal/callback.ts +200 -0
  134. package/src/internal/client-token.ts +132 -0
  135. package/src/internal/env.ts +128 -0
  136. package/src/internal/multipart.ts +526 -0
  137. package/src/internal/presign.ts +477 -0
  138. package/src/internal/store-id.ts +69 -0
  139. package/src/internal/urls.ts +42 -0
  140. package/src/internal/wire.ts +80 -0
  141. package/src/internal/write-headers.ts +118 -0
  142. package/src/testing/index.ts +1 -0
  143. package/src/testing/test-blob-store.ts +1136 -0
@@ -0,0 +1,33 @@
1
+ /** The production Blob API, used unless `VERCEL_BLOB_API_URL` or an option overrides it. */
2
+ export const defaultApiUrl = "https://vercel.com/api/blob";
3
+ /** Where a store serves its blobs: `https://<storeId>.<access>.blob.vercel-storage.com/`. */
4
+ export const defaultBlobBaseUrl = (storeId, access) => new URL(`https://${storeId}.${access}.blob.vercel-storage.com/`);
5
+ /**
6
+ * The URL of the blob at `pathname` under a store's base URL (`BlobConfig.blobBaseUrl`), as the
7
+ * SDK's `constructBlobUrl`: the pathname is appended as is, so URL syntax in it (`?`, `#`) keeps its
8
+ * URL meaning exactly as with the SDK.
9
+ */
10
+ export const blobUrl = (baseUrl, pathname) => {
11
+ const base = baseUrl.href.endsWith("/") ? baseUrl.href : `${baseUrl.href}/`;
12
+ return new URL(`${base}${pathname}`);
13
+ };
14
+ /** True for strings the SDK treats as URLs rather than pathnames (`http://` or `https://`). */
15
+ export const isUrl = (urlOrPathname) => urlOrPathname.startsWith("http://") || urlOrPathname.startsWith("https://");
16
+ /**
17
+ * Whether `get` may send credentials to `url`: hosts under `.blob.vercel-storage.com`, and, when
18
+ * the API URL is overridden (an emulator or a test server), that API URL's origin.
19
+ *
20
+ * @param apiUrl - The configured API URL; the production default allows no extra origin.
21
+ */
22
+ export const isAllowedBlobUrl = (url, apiUrl) => url.hostname.endsWith(".blob.vercel-storage.com") ||
23
+ (apiUrl.href !== new URL(defaultApiUrl).href && apiUrl.origin === url.origin);
24
+ /**
25
+ * The URL that makes browsers download a blob instead of displaying it: the blob URL with
26
+ * `download=1` in its query.
27
+ */
28
+ export const downloadUrl = (url) => {
29
+ const result = new URL(url);
30
+ result.searchParams.set("download", "1");
31
+ return result;
32
+ };
33
+ //# sourceMappingURL=urls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"urls.js","sourceRoot":"","sources":["../../src/internal/urls.ts"],"names":[],"mappings":"AAEA,4FAA4F;AAC5F,MAAM,CAAC,MAAM,aAAa,GAAG,6BAA6B,CAAC;AAE3D,6FAA6F;AAC7F,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,OAAgB,EAAE,MAAkB,EAAO,EAAE,CAC9E,IAAI,GAAG,CAAC,WAAW,OAAO,IAAI,MAAM,2BAA2B,CAAC,CAAC;AAEnE;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,OAAY,EAAE,QAAgB,EAAO,EAAE;IAC7D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,GAAG,CAAC;IAC5E,OAAO,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,QAAQ,EAAE,CAAC,CAAC;AACvC,CAAC,CAAC;AAEF,+FAA+F;AAC/F,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,aAAqB,EAAW,EAAE,CACtD,aAAa,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,aAAa,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAQ,EAAE,MAAW,EAAW,EAAE,CACjE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,0BAA0B,CAAC;IACjD,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC;AAEhF;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,GAAiB,EAAO,EAAE;IACpD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACzC,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { Schema } from "effect";
2
+ import { BlobMetadata, CreateFolderResult, ListPage, PutBlobResult } from "../contracts/blob-domain.ts";
3
+ /** `PUT /`, `PUT ?pathname&fromUrl` (copy), rename, put-image and multipart `complete`. */
4
+ export declare const PutBlobResponse: Schema.compose<typeof PutBlobResult, Schema.Struct<{
5
+ readonly url: Schema.URLFromString;
6
+ readonly downloadUrl: Schema.URLFromString;
7
+ readonly pathname: Schema.String;
8
+ readonly contentType: Schema.String;
9
+ readonly contentDisposition: Schema.String;
10
+ readonly etag: Schema.String;
11
+ }>>;
12
+ /** `PUT /?pathname=<folder>/`: only `url` and `pathname` are kept, as the SDK does. */
13
+ export declare const CreateFolderResponse: Schema.compose<typeof CreateFolderResult, Schema.Struct<{
14
+ readonly url: Schema.URLFromString;
15
+ readonly pathname: Schema.String;
16
+ }>>;
17
+ /** `GET ?url=…` (head). `uploadedAt` is an ISO 8601 string. */
18
+ export declare const HeadBlobResponse: Schema.compose<typeof BlobMetadata, Schema.Struct<{
19
+ readonly url: Schema.URLFromString;
20
+ readonly downloadUrl: Schema.URLFromString;
21
+ readonly pathname: Schema.String;
22
+ readonly size: Schema.Int;
23
+ readonly uploadedAt: Schema.DateTimeUtcFromString;
24
+ readonly contentType: Schema.String;
25
+ readonly contentDisposition: Schema.String;
26
+ readonly cacheControl: Schema.String;
27
+ readonly etag: Schema.String;
28
+ }>>;
29
+ /**
30
+ * `GET ?limit&prefix&cursor&mode` (list). `folders` is only sent for `mode=folded`; it decodes to
31
+ * an empty array otherwise.
32
+ */
33
+ export declare const ListResponse: Schema.decodeTo<typeof ListPage, Schema.Struct<{
34
+ readonly blobs: Schema.$Array<Schema.Struct<{
35
+ readonly url: Schema.URLFromString;
36
+ readonly downloadUrl: Schema.URLFromString;
37
+ readonly pathname: Schema.String;
38
+ readonly size: Schema.Int;
39
+ readonly uploadedAt: Schema.DateTimeUtcFromString;
40
+ readonly etag: Schema.String;
41
+ }>>;
42
+ readonly folders: Schema.optionalKey<Schema.$Array<Schema.String>>;
43
+ readonly cursor: Schema.OptionFromOptionalNullOr<Schema.String>;
44
+ readonly hasMore: Schema.Boolean;
45
+ }>, never, never>;
46
+ /** `POST /mpu` with `x-mpu-action: create`. */
47
+ export declare const CreateMultipartUploadResponse: Schema.Struct<{
48
+ readonly uploadId: Schema.String;
49
+ readonly key: Schema.String;
50
+ }>;
51
+ /** `POST /mpu` with `x-mpu-action: upload`. */
52
+ export declare const UploadPartResponse: Schema.Struct<{
53
+ readonly etag: Schema.String;
54
+ }>;
55
+ //# sourceMappingURL=wire.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../src/internal/wire.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAE9C,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACd,MAAM,6BAA6B,CAAC;AAOrC,2FAA2F;AAC3F,eAAO,MAAM,eAAe;;;;;;;GAOW,CAAC;AAExC,uFAAuF;AACvF,eAAO,MAAM,oBAAoB;;;GAGW,CAAC;AAE7C,+DAA+D;AAC/D,eAAO,MAAM,gBAAgB;;;;;;;;;;GAUS,CAAC;AAkBvC;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;iBAKxB,CAAC;AAEF,+CAA+C;AAC/C,eAAO,MAAM,6BAA6B;;;EAGxC,CAAC;AAEH,+CAA+C;AAC/C,eAAO,MAAM,kBAAkB;;EAE7B,CAAC"}
@@ -0,0 +1,64 @@
1
+ import { Schema, SchemaGetter } from "effect";
2
+ import { BlobMetadata, CreateFolderResult, ListPage, PutBlobResult, } from "../contracts/blob-domain.js";
3
+ /*
4
+ * JSON bodies of Blob API responses, decoded into the contract classes. Unknown fields are ignored
5
+ * so newer API versions keep decoding. Pass these as `response` to `BlobRequester.call`.
6
+ */
7
+ /** `PUT /`, `PUT ?pathname&fromUrl` (copy), rename, put-image and multipart `complete`. */
8
+ export const PutBlobResponse = Schema.Struct({
9
+ url: Schema.URLFromString,
10
+ downloadUrl: Schema.URLFromString,
11
+ pathname: Schema.String,
12
+ contentType: Schema.String,
13
+ contentDisposition: Schema.String,
14
+ etag: Schema.String,
15
+ }).pipe(Schema.decodeTo(PutBlobResult));
16
+ /** `PUT /?pathname=<folder>/`: only `url` and `pathname` are kept, as the SDK does. */
17
+ export const CreateFolderResponse = Schema.Struct({
18
+ url: Schema.URLFromString,
19
+ pathname: Schema.String,
20
+ }).pipe(Schema.decodeTo(CreateFolderResult));
21
+ /** `GET ?url=…` (head). `uploadedAt` is an ISO 8601 string. */
22
+ export const HeadBlobResponse = Schema.Struct({
23
+ url: Schema.URLFromString,
24
+ downloadUrl: Schema.URLFromString,
25
+ pathname: Schema.String,
26
+ size: Schema.Int,
27
+ uploadedAt: Schema.DateTimeUtcFromString,
28
+ contentType: Schema.String,
29
+ contentDisposition: Schema.String,
30
+ cacheControl: Schema.String,
31
+ etag: Schema.String,
32
+ }).pipe(Schema.decodeTo(BlobMetadata));
33
+ const ListedBlobWire = Schema.Struct({
34
+ url: Schema.URLFromString,
35
+ downloadUrl: Schema.URLFromString,
36
+ pathname: Schema.String,
37
+ size: Schema.Int,
38
+ uploadedAt: Schema.DateTimeUtcFromString,
39
+ etag: Schema.String,
40
+ });
41
+ const ListWire = Schema.Struct({
42
+ blobs: Schema.Array(ListedBlobWire),
43
+ folders: Schema.optionalKey(Schema.Array(Schema.String)),
44
+ cursor: Schema.OptionFromOptionalNullOr(Schema.String),
45
+ hasMore: Schema.Boolean,
46
+ });
47
+ /**
48
+ * `GET ?limit&prefix&cursor&mode` (list). `folders` is only sent for `mode=folded`; it decodes to
49
+ * an empty array otherwise.
50
+ */
51
+ export const ListResponse = ListWire.pipe(Schema.decodeTo(ListPage, {
52
+ decode: SchemaGetter.transform(({ folders, ...page }) => ({ ...page, folders: folders ?? [] })),
53
+ encode: SchemaGetter.transform((page) => page),
54
+ }));
55
+ /** `POST /mpu` with `x-mpu-action: create`. */
56
+ export const CreateMultipartUploadResponse = Schema.Struct({
57
+ uploadId: Schema.String,
58
+ key: Schema.String,
59
+ });
60
+ /** `POST /mpu` with `x-mpu-action: upload`. */
61
+ export const UploadPartResponse = Schema.Struct({
62
+ etag: Schema.String,
63
+ });
64
+ //# sourceMappingURL=wire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.js","sourceRoot":"","sources":["../../src/internal/wire.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { Duration } from "effect";
2
+ import type { BlobAccess } from "../contracts/blob-domain.ts";
3
+ /** Header carrying each write option, as the SDK's `putOptionHeaderMap`. */
4
+ export declare const writeOptionHeaders: {
5
+ readonly cacheControlMaxAge: "x-cache-control-max-age";
6
+ readonly addRandomSuffix: "x-add-random-suffix";
7
+ readonly allowOverwrite: "x-allow-overwrite";
8
+ readonly contentType: "x-content-type";
9
+ readonly access: "x-vercel-blob-access";
10
+ readonly ifMatch: "x-if-match";
11
+ };
12
+ /** A write option an endpoint may accept; `access` is always sent and needs no listing. */
13
+ export type WriteOption = Exclude<keyof typeof writeOptionHeaders, "access">;
14
+ /**
15
+ * Write options as any write operation receives them. `CreateBlobOptions`, `PutOptions` and the
16
+ * client upload options are all assignable to it.
17
+ */
18
+ export interface WriteHeaderOptions {
19
+ readonly access: BlobAccess;
20
+ readonly addRandomSuffix?: boolean | undefined;
21
+ readonly allowOverwrite?: boolean | undefined;
22
+ readonly contentType?: string | undefined;
23
+ readonly cacheControlMaxAge?: Duration.Input | undefined;
24
+ readonly ifMatch?: string | undefined;
25
+ }
26
+ /** The `allowedOptions` each SDK write method passes to `createPutHeaders`. */
27
+ export declare const allowedWriteOptions: {
28
+ /** `put` (single and multipart), `createMultipartUpload`. */
29
+ readonly put: readonly ["cacheControlMaxAge", "addRandomSuffix", "allowOverwrite", "contentType", "ifMatch"];
30
+ /** `uploadPart` and `completeMultipartUpload`: no `ifMatch`. */
31
+ readonly multipartPart: readonly ["cacheControlMaxAge", "addRandomSuffix", "allowOverwrite", "contentType"];
32
+ /** `putImage`: the optimizer decides the media type. */
33
+ readonly putImage: readonly ["cacheControlMaxAge", "addRandomSuffix", "allowOverwrite", "ifMatch"];
34
+ /** Every browser (client token) write: the token fixes the rest. */
35
+ readonly client: readonly ["contentType"];
36
+ };
37
+ /**
38
+ * The cache lifetime in whole seconds, as sent in `x-cache-control-max-age`. Note that a bare
39
+ * number is a `Duration.Input` in milliseconds.
40
+ */
41
+ export declare const cacheControlMaxAgeSeconds: (maxAge: Duration.Input) => number;
42
+ /**
43
+ * Port of the SDK's `createPutHeaders`: the headers for the write options that `allowedOptions`
44
+ * lists, plus `x-vercel-blob-access` always.
45
+ *
46
+ * Booleans are sent as `1`/`0`. `ifMatch` (when allowed and non-empty) implies
47
+ * `x-allow-overwrite: 1` unless `allowOverwrite` is set explicitly; the option types already rule
48
+ * out `ifMatch` with `allowOverwrite: false`. An empty `contentType` is not sent.
49
+ */
50
+ export declare const writeHeaders: (allowedOptions: ReadonlyArray<WriteOption>, options: WriteHeaderOptions) => Record<string, string>;
51
+ /**
52
+ * The write headers of `copy` and `rename`, which the SDK builds by hand: every option is sent when
53
+ * set, and `ifMatch` (checked against the source) does not imply `x-allow-overwrite`.
54
+ */
55
+ export declare const copyWriteHeaders: (options: WriteHeaderOptions) => Record<string, string>;
56
+ //# sourceMappingURL=write-headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-headers.d.ts","sourceRoot":"","sources":["../../src/internal/write-headers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB;aAC7B,kBAAkB,EAAE,yBAAyB;aAC7C,eAAe,EAAE,qBAAqB;aACtC,cAAc,EAAE,mBAAmB;aACnC,WAAW,EAAE,gBAAgB;aAC7B,MAAM,EAAE,sBAAsB;aAC9B,OAAO,EAAE,YAAY;CACb,CAAC;AAEX,2FAA2F;AAC3F,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,OAAO,kBAAkB,EAAE,QAAQ,CAAC,CAAC;AAE7E;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,kBAAkB,CAAC,EAAE,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,+EAA+E;AAC/E,eAAO,MAAM,mBAAmB;IAC9B,6DAA6D;;IAE7D,gEAAgE;;IAEhE,wDAAwD;;IAExD,oEAAoE;;CAEP,CAAC;AAEhE;;;GAGG;AACH,eAAO,MAAM,yBAAyB,WAAY,QAAQ,CAAC,KAAK,KAAG,MAC3B,CAAC;AAEzC;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,mBACP,aAAa,CAAC,WAAW,CAAC,WACjC,kBAAkB,KAC1B,MAAM,CAAC,MAAM,EAAE,MAAM,CA8BvB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,YAAa,kBAAkB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAqBnF,CAAC"}
@@ -0,0 +1,82 @@
1
+ import { Duration } from "effect";
2
+ /** Header carrying each write option, as the SDK's `putOptionHeaderMap`. */
3
+ export const writeOptionHeaders = {
4
+ cacheControlMaxAge: "x-cache-control-max-age",
5
+ addRandomSuffix: "x-add-random-suffix",
6
+ allowOverwrite: "x-allow-overwrite",
7
+ contentType: "x-content-type",
8
+ access: "x-vercel-blob-access",
9
+ ifMatch: "x-if-match",
10
+ };
11
+ /** The `allowedOptions` each SDK write method passes to `createPutHeaders`. */
12
+ export const allowedWriteOptions = {
13
+ /** `put` (single and multipart), `createMultipartUpload`. */
14
+ put: ["cacheControlMaxAge", "addRandomSuffix", "allowOverwrite", "contentType", "ifMatch"],
15
+ /** `uploadPart` and `completeMultipartUpload`: no `ifMatch`. */
16
+ multipartPart: ["cacheControlMaxAge", "addRandomSuffix", "allowOverwrite", "contentType"],
17
+ /** `putImage`: the optimizer decides the media type. */
18
+ putImage: ["cacheControlMaxAge", "addRandomSuffix", "allowOverwrite", "ifMatch"],
19
+ /** Every browser (client token) write: the token fixes the rest. */
20
+ client: ["contentType"],
21
+ };
22
+ /**
23
+ * The cache lifetime in whole seconds, as sent in `x-cache-control-max-age`. Note that a bare
24
+ * number is a `Duration.Input` in milliseconds.
25
+ */
26
+ export const cacheControlMaxAgeSeconds = (maxAge) => Math.floor(Duration.toSeconds(maxAge));
27
+ /**
28
+ * Port of the SDK's `createPutHeaders`: the headers for the write options that `allowedOptions`
29
+ * lists, plus `x-vercel-blob-access` always.
30
+ *
31
+ * Booleans are sent as `1`/`0`. `ifMatch` (when allowed and non-empty) implies
32
+ * `x-allow-overwrite: 1` unless `allowOverwrite` is set explicitly; the option types already rule
33
+ * out `ifMatch` with `allowOverwrite: false`. An empty `contentType` is not sent.
34
+ */
35
+ export const writeHeaders = (allowedOptions, options) => {
36
+ const headers = {};
37
+ headers[writeOptionHeaders.access] = options.access;
38
+ if (allowedOptions.includes("contentType") && options.contentType) {
39
+ headers[writeOptionHeaders.contentType] = options.contentType;
40
+ }
41
+ if (allowedOptions.includes("addRandomSuffix") && options.addRandomSuffix !== undefined) {
42
+ headers[writeOptionHeaders.addRandomSuffix] = options.addRandomSuffix ? "1" : "0";
43
+ }
44
+ if (allowedOptions.includes("ifMatch") && options.ifMatch) {
45
+ headers[writeOptionHeaders.ifMatch] = options.ifMatch;
46
+ if (allowedOptions.includes("allowOverwrite") && options.allowOverwrite === undefined) {
47
+ headers[writeOptionHeaders.allowOverwrite] = "1";
48
+ }
49
+ }
50
+ if (allowedOptions.includes("allowOverwrite") && options.allowOverwrite !== undefined) {
51
+ headers[writeOptionHeaders.allowOverwrite] = options.allowOverwrite ? "1" : "0";
52
+ }
53
+ if (allowedOptions.includes("cacheControlMaxAge") && options.cacheControlMaxAge !== undefined) {
54
+ headers[writeOptionHeaders.cacheControlMaxAge] = String(cacheControlMaxAgeSeconds(options.cacheControlMaxAge));
55
+ }
56
+ return headers;
57
+ };
58
+ /**
59
+ * The write headers of `copy` and `rename`, which the SDK builds by hand: every option is sent when
60
+ * set, and `ifMatch` (checked against the source) does not imply `x-allow-overwrite`.
61
+ */
62
+ export const copyWriteHeaders = (options) => {
63
+ const headers = {};
64
+ headers[writeOptionHeaders.access] = options.access;
65
+ if (options.addRandomSuffix !== undefined) {
66
+ headers[writeOptionHeaders.addRandomSuffix] = options.addRandomSuffix ? "1" : "0";
67
+ }
68
+ if (options.allowOverwrite !== undefined) {
69
+ headers[writeOptionHeaders.allowOverwrite] = options.allowOverwrite ? "1" : "0";
70
+ }
71
+ if (options.contentType) {
72
+ headers[writeOptionHeaders.contentType] = options.contentType;
73
+ }
74
+ if (options.cacheControlMaxAge !== undefined) {
75
+ headers[writeOptionHeaders.cacheControlMaxAge] = String(cacheControlMaxAgeSeconds(options.cacheControlMaxAge));
76
+ }
77
+ if (options.ifMatch) {
78
+ headers[writeOptionHeaders.ifMatch] = options.ifMatch;
79
+ }
80
+ return headers;
81
+ };
82
+ //# sourceMappingURL=write-headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-headers.js","sourceRoot":"","sources":["../../src/internal/write-headers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from "./test-blob-store.ts";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from "./test-blob-store.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,134 @@
1
+ import { Context, DateTime, Duration, Effect, Layer, Option } from "effect";
2
+ import type { BlobAccess } from "../contracts/blob-domain.ts";
3
+ import type { BlobRequestError, BlobWriteError } from "../contracts/blob-store.ts";
4
+ import { ETag, StoreId } from "../contracts/blob-domain.ts";
5
+ import { BlobNotFound, BlobPreconditionFailed } from "../contracts/blob-errors.ts";
6
+ import { BlobStore } from "../contracts/blob-store.ts";
7
+ /** A `BlobStore` operation, as recorded by {@link TestBlobStore}. */
8
+ export type TestBlobStoreOperation = keyof TestBlobStoreOperationError;
9
+ /** Errors each operation can be scripted to fail with. `listAll` fails through `list`. */
10
+ export interface TestBlobStoreOperationError {
11
+ readonly put: BlobWriteError;
12
+ readonly putImage: BlobWriteError;
13
+ readonly head: BlobRequestError | BlobNotFound;
14
+ readonly get: BlobRequestError | BlobNotFound;
15
+ readonly list: BlobRequestError;
16
+ readonly del: BlobRequestError | BlobPreconditionFailed;
17
+ readonly delMany: BlobRequestError;
18
+ readonly copy: BlobWriteError | BlobNotFound;
19
+ readonly rename: BlobWriteError | BlobNotFound;
20
+ readonly createFolder: BlobWriteError;
21
+ readonly createMultipartUpload: BlobWriteError;
22
+ readonly uploadPart: BlobWriteError;
23
+ readonly completeMultipartUpload: BlobWriteError;
24
+ }
25
+ /** One call made to the test `BlobStore`, in call order. */
26
+ export interface TestBlobStoreCall {
27
+ readonly operation: TestBlobStoreOperation;
28
+ /** When the call was made, in epoch milliseconds. */
29
+ readonly at: number;
30
+ /** The pathname written: the requested one, before any random suffix. */
31
+ readonly pathname?: string;
32
+ /** The blobs read, deleted, copied or renamed, as given. */
33
+ readonly locators?: ReadonlyArray<string>;
34
+ readonly partNumber?: number;
35
+ readonly prefix?: string;
36
+ readonly cursor?: string;
37
+ }
38
+ /** A blob held by the in-memory store, with everything the store reports about it. */
39
+ export interface TestBlob {
40
+ /** Final pathname, including any random suffix. */
41
+ readonly pathname: string;
42
+ readonly access: BlobAccess;
43
+ readonly url: URL;
44
+ readonly downloadUrl: URL;
45
+ readonly size: number;
46
+ readonly uploadedAt: DateTime.Utc;
47
+ readonly contentType: string;
48
+ readonly contentDisposition: string;
49
+ /** The `Cache-Control` value served, such as `public, max-age=2592000`. */
50
+ readonly cacheControl: string;
51
+ /** Cache lifetime in seconds. */
52
+ readonly cacheControlMaxAge: number;
53
+ readonly etag: ETag;
54
+ /** A copy of the content. */
55
+ readonly body: Uint8Array;
56
+ }
57
+ /** How {@link TestBlobStore} `seed` stores a blob. */
58
+ export interface TestBlobSeedOptions {
59
+ /** The store's access by default. */
60
+ readonly access?: BlobAccess | undefined;
61
+ /** Inferred from the pathname extension by default, like a write. */
62
+ readonly contentType?: string | undefined;
63
+ /** One month by default. */
64
+ readonly cacheControlMaxAge?: Duration.Input | undefined;
65
+ }
66
+ /** Options of {@link TestBlobStore.layerWith}. */
67
+ export interface TestBlobStoreOptions {
68
+ /** Store id used in blob URLs; `teststore` by default. */
69
+ readonly storeId?: StoreId | undefined;
70
+ /**
71
+ * The access every blob of the store has, `public` by default. Like a real store, writes with
72
+ * the other access fail.
73
+ */
74
+ readonly access?: BlobAccess | undefined;
75
+ }
76
+ declare const TestBlobStore_base: Context.ServiceClass<TestBlobStore, "@effect-vercel/blob/TestBlobStore", {
77
+ /** Every `BlobStore` call so far, in order, including calls that failed. */
78
+ readonly calls: Effect.Effect<ReadonlyArray<TestBlobStoreCall>>;
79
+ /** Every stored blob, in pathname order. */
80
+ readonly blobs: Effect.Effect<ReadonlyArray<TestBlob>>;
81
+ /** The blob at a final pathname (including any random suffix). */
82
+ readonly blob: (pathname: string) => Effect.Effect<Option.Option<TestBlob>>;
83
+ /**
84
+ * Store a blob directly, replacing any blob at `pathname`, without recording a call or
85
+ * consuming a scripted failure.
86
+ */
87
+ readonly seed: (pathname: string, body: string | Uint8Array, options?: TestBlobSeedOptions) => Effect.Effect<TestBlob>;
88
+ /** Make the next call to `operation` fail with `error` instead of running. */
89
+ readonly failNext: <Op extends TestBlobStoreOperation>(operation: Op, error: TestBlobStoreOperationError[Op]) => Effect.Effect<void>;
90
+ /**
91
+ * Content the store fetches for a `putImage` source URL outside the store. Blob URLs of this
92
+ * store resolve to the stored blob; any other unserved URL fails with `ApiBadRequest`.
93
+ */
94
+ readonly serveUrl: (url: string | URL, body: string | Uint8Array, contentType?: string) => Effect.Effect<void>;
95
+ }>;
96
+ /**
97
+ * Inspects and scripts the in-memory store behind {@link TestBlobStore.layer}.
98
+ *
99
+ * Time follows `Clock` and random suffixes follow `Random`, so `TestClock` and `Random.withSeed`
100
+ * make results deterministic.
101
+ */
102
+ export declare class TestBlobStore extends TestBlobStore_base {
103
+ /**
104
+ * An in-memory `BlobStore` with the store's write, read, listing and multipart semantics, plus
105
+ * the {@link TestBlobStore} controls. Each build of the layer holds its own empty store.
106
+ *
107
+ * Behavior checked against real stores by `test/live` (`pnpm test:live`):
108
+ *
109
+ * - The store has one access, `public` unless set with {@link TestBlobStore.layerWith}; writes
110
+ * with the other access fail with `VercelApiError` / `ApiBadRequest` (`bad_request`).
111
+ * - Writes refuse an existing pathname with `ApiBadRequest` unless `allowOverwrite` is set,
112
+ * except `copy`, which always replaces. `ifMatch` replaces only a blob with that ETag and
113
+ * otherwise fails with `BlobPreconditionFailed`, also when nothing is stored there. Copying or
114
+ * renaming a missing blob fails with `BlobNotFound`.
115
+ * - Every write gets a new ETag and `uploadedAt` from `Clock`. `addRandomSuffix` inserts 30
116
+ * characters from `Random` before the extension.
117
+ * - Media types come from the option, then common extensions, then the request: text for a
118
+ * string body, the type of a typed `Blob`, `application/octet-stream` otherwise. Images,
119
+ * audio, video, `text/plain`, `text/xml`, `application/json` and `application/pdf` are served
120
+ * `inline`, anything else as `attachment`, with a percent-encoded filename. Cache-Control is
121
+ * `public, max-age=<seconds>` for every blob, one month by default.
122
+ * - URLs are `https://<storeId>.<access>.blob.vercel-storage.com/<pathname>`. A URL locator
123
+ * addresses a blob only with this store's host and the blob's access; `get` with a pathname
124
+ * finds it only under the given `access`.
125
+ * - `list` orders by pathname, with opaque cursors. `putImage` stores the source unchanged.
126
+ * Multipart uploads assemble their parts in part-number order; part sizes are not checked.
127
+ * - Progress reports 0 % before the body is read and 100 % once stored.
128
+ */
129
+ static readonly layer: Layer.Layer<BlobStore | TestBlobStore>;
130
+ /** {@link TestBlobStore.layer} with a chosen store id or access. */
131
+ static layerWith(options: TestBlobStoreOptions): Layer.Layer<BlobStore | TestBlobStore>;
132
+ }
133
+ export {};
134
+ //# sourceMappingURL=test-blob-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-blob-store.d.ts","sourceRoot":"","sources":["../../src/testing/test-blob-store.ts"],"names":[],"mappings":"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"}