@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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +12 -1
  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 +58 -4
  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,80 @@
1
+ import { Effect, Option, Result, Schema } from "effect";
2
+ import { Base64, Hex } from "effect/encoding";
3
+ import { ClientToken } from "../contracts/blob-domain.js";
4
+ /*
5
+ * Client tokens, as the SDK's `generateClientTokenFromReadWriteToken` builds them:
6
+ * `vercel_blob_client_<storeId>_<base64("<hex HMAC-SHA256(payload)>.<payload>")>`, where `payload` is
7
+ * the base64 of the token's JSON and the HMAC is keyed by the store's read-write token. The JSON is
8
+ * serialized exactly as the SDK does (key order and all), so tokens are byte-identical.
9
+ */
10
+ /** Prefix of every client token. */
11
+ export const clientTokenPrefix = "vercel_blob_client_";
12
+ /** How long a client token generated without `validUntil` stays valid, as the SDK: 30 seconds. */
13
+ export const defaultClientTokenLifetimeMillis = 30_000;
14
+ /**
15
+ * The JSON a client token encodes, as the SDK serializes it: the fields in their order, with
16
+ * `allowOverwrite: true` appended when `ifMatch` is set and `allowOverwrite` is not, and
17
+ * `validUntil` defaulted to `defaultValidUntil` (kept in place when the fields have it).
18
+ *
19
+ * Callers reject `ifMatch` with `allowOverwrite: false` first; see {@link contradictsIfMatch}.
20
+ */
21
+ export const clientTokenPayloadJson = (fields, defaultValidUntil) => {
22
+ const args = { ...fields };
23
+ if (Boolean(args["ifMatch"]) && args["allowOverwrite"] === undefined) {
24
+ args["allowOverwrite"] = true;
25
+ }
26
+ return JSON.stringify({ ...args, validUntil: args["validUntil"] ?? defaultValidUntil });
27
+ };
28
+ /** The SDK's message for an `ifMatch` with `allowOverwrite: false`, which it refuses to sign. */
29
+ export const ifMatchContradictionMessage = "ifMatch and allowOverwrite: false are contradictory. ifMatch is used for conditional overwrites, which requires allowOverwrite to be true.";
30
+ /** Whether the fields set `ifMatch` together with `allowOverwrite: false`, which the SDK rejects. */
31
+ export const contradictsIfMatch = (fields) => Boolean(fields["ifMatch"]) && fields["allowOverwrite"] === false;
32
+ const textEncoder = new TextEncoder();
33
+ /** HMAC-SHA256 of `data` keyed by `secret` (both UTF-8), as lowercase hex. */
34
+ export const hmacSha256Hex = Effect.fnUntraced(function* (secret, data) {
35
+ const key = yield* Effect.promise(() => globalThis.crypto.subtle.importKey("raw", textEncoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]));
36
+ const signature = yield* Effect.promise(() => globalThis.crypto.subtle.sign("HMAC", key, textEncoder.encode(data)));
37
+ return Hex.encode(new Uint8Array(signature));
38
+ });
39
+ /**
40
+ * Sign a client token's JSON with the store's read-write token.
41
+ *
42
+ * @param payloadJson - The token's JSON, from {@link clientTokenPayloadJson}.
43
+ * @returns `vercel_blob_client_<storeId>_<base64("<hex HMAC>.<base64 payloadJson>")>`.
44
+ */
45
+ export const signClientToken = Effect.fnUntraced(function* (readWriteToken, storeId, payloadJson) {
46
+ const payload = Base64.encode(payloadJson);
47
+ const signature = yield* hmacSha256Hex(readWriteToken, payload);
48
+ return ClientToken.make(`${clientTokenPrefix}${storeId}_${Base64.encode(`${signature}.${payload}`)}`);
49
+ });
50
+ /** `onUploadCompleted` as a client token carries it. */
51
+ export const ClientTokenCallback = Schema.Struct({
52
+ callbackUrl: Schema.String,
53
+ tokenPayload: Schema.optionalKey(Schema.NullOr(Schema.String)),
54
+ });
55
+ /** The JSON a client token encodes, as the SDK's `DecodedClientTokenPayload`. */
56
+ export const ClientTokenPayload = Schema.Struct({
57
+ pathname: Schema.String,
58
+ /** Expiry in epoch milliseconds. */
59
+ validUntil: Schema.Number,
60
+ onUploadCompleted: Schema.optionalKey(ClientTokenCallback),
61
+ maximumSizeInBytes: Schema.optionalKey(Schema.Number),
62
+ allowedContentTypes: Schema.optionalKey(Schema.Array(Schema.String)),
63
+ addRandomSuffix: Schema.optionalKey(Schema.Boolean),
64
+ allowOverwrite: Schema.optionalKey(Schema.Boolean),
65
+ cacheControlMaxAge: Schema.optionalKey(Schema.Number),
66
+ ifMatch: Schema.optionalKey(Schema.String),
67
+ /** Set by upload routes, which copy the route's `tokenPayload` into the token as the SDK does. */
68
+ tokenPayload: Schema.optionalKey(Schema.NullOr(Schema.String)),
69
+ });
70
+ const decodePayload = Schema.decodeUnknownOption(Schema.fromJsonString(ClientTokenPayload));
71
+ const decodeBase64String = (input) => Result.getSuccess(Base64.decodeString(input));
72
+ /**
73
+ * Read the JSON a client token encodes without verifying its signature, as the SDK's
74
+ * `getPayloadFromClientToken`: the fifth `_`-separated segment is base64 of `<hmac>.<base64 JSON>`.
75
+ * Works in browsers.
76
+ *
77
+ * @returns None when the token does not have that shape or its JSON is not a client token payload.
78
+ */
79
+ export const getPayloadFromClientToken = (clientToken) => decodeBase64String(clientToken.split("_")[4] ?? "").pipe(Option.flatMap((secured) => Option.fromUndefinedOr(secured.split(".")[1])), Option.flatMap(decodeBase64String), Option.flatMap(decodePayload));
80
+ //# sourceMappingURL=client-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-token.js","sourceRoot":"","sources":["../../src/internal/client-token.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { Config, Effect, Option } from "effect";
2
+ /** A `ConfigError` with a message explaining what to fix. */
3
+ export declare const configError: (message: string) => Config.ConfigError;
4
+ /**
5
+ * A variable as the SDK's `readEnv` reads it: trimmed, and absent when blank. In browsers these
6
+ * are usually `NEXT_PUBLIC_*` values inlined by the bundler, or absent.
7
+ */
8
+ export declare const env: (name: string) => Config.Config<Option.Option<string>>;
9
+ /** The first of several variables that is set, such as a name and its `NEXT_PUBLIC_` twin. */
10
+ export declare const firstEnv: (...names: ReadonlyArray<string>) => Effect.Effect<Option.Option<string>, Config.ConfigError, never>;
11
+ /** Values that replace what {@link requestEnv} reads from the environment. */
12
+ export interface RequestEnvOptions {
13
+ readonly apiUrl?: URL | string | undefined;
14
+ readonly headers?: Readonly<Record<string, string>> | undefined;
15
+ readonly retries?: number | undefined;
16
+ }
17
+ /** How every Blob API request is addressed and retried, shared by the server and browser clients. */
18
+ export interface RequestEnv {
19
+ readonly apiUrl: URL;
20
+ readonly apiVersion: string;
21
+ readonly retries: number;
22
+ readonly sendContentLength: boolean;
23
+ readonly headers: Readonly<Record<string, string>>;
24
+ }
25
+ /**
26
+ * Reads the request settings the SDK's `requestApi` reads on every call: `VERCEL_BLOB_API_URL`,
27
+ * `VERCEL_BLOB_API_VERSION_OVERRIDE`, `VERCEL_BLOB_PROXY_THROUGH_ALTERNATIVE_API` (each also with a
28
+ * `NEXT_PUBLIC_` prefix), `VERCEL_BLOB_RETRIES` and `VERCEL_BLOB_USE_X_CONTENT_LENGTH`.
29
+ *
30
+ * Fails with a `ConfigError` when the API URL is not absolute or the retry count is not a
31
+ * non-negative integer.
32
+ */
33
+ export declare const requestEnv: (options: RequestEnvOptions) => Effect.Effect<RequestEnv, Config.ConfigError, never>;
34
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/internal/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAkB,MAAM,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAQxE,6DAA6D;AAC7D,eAAO,MAAM,WAAW,YAAa,MAAM,uBAC0B,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,GAAG,SAAU,MAAM,yCAQ7B,CAAC;AAEJ,8FAA8F;AAC9F,eAAO,MAAM,QAAQ,aAAc,aAAa,CAAC,MAAM,CAAC,oEAOpD,CAAC;AAQL,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;IAChE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,qGAAqG;AACrG,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACpD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,sFA6DrB,CAAC"}
@@ -0,0 +1,61 @@
1
+ import { Config, ConfigProvider, Effect, Option, Schema } from "effect";
2
+ import { defaultApiUrl } from "./urls.js";
3
+ const defaultApiVersion = "13";
4
+ const defaultRetries = 10;
5
+ /** A `ConfigError` with a message explaining what to fix. */
6
+ export const configError = (message) => new Config.ConfigError(new ConfigProvider.SourceError({ message }));
7
+ /**
8
+ * A variable as the SDK's `readEnv` reads it: trimmed, and absent when blank. In browsers these
9
+ * are usually `NEXT_PUBLIC_*` values inlined by the bundler, or absent.
10
+ */
11
+ export const env = (name) => Config.option(Config.String(name)).pipe(Config.map((value) => Option.filter(Option.map(value, (value) => value.trim()), (value) => value !== "")));
12
+ /** The first of several variables that is set, such as a name and its `NEXT_PUBLIC_` twin. */
13
+ export const firstEnv = (...names) => Effect.gen(function* () {
14
+ for (const name of names) {
15
+ const value = yield* env(name);
16
+ if (Option.isSome(value))
17
+ return value;
18
+ }
19
+ return Option.none();
20
+ });
21
+ const decodeUrl = Schema.decodeUnknownOption(Schema.URLFromString);
22
+ const decodeRetries = Schema.decodeUnknownOption(Schema.NumberFromString.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0)));
23
+ /**
24
+ * Reads the request settings the SDK's `requestApi` reads on every call: `VERCEL_BLOB_API_URL`,
25
+ * `VERCEL_BLOB_API_VERSION_OVERRIDE`, `VERCEL_BLOB_PROXY_THROUGH_ALTERNATIVE_API` (each also with a
26
+ * `NEXT_PUBLIC_` prefix), `VERCEL_BLOB_RETRIES` and `VERCEL_BLOB_USE_X_CONTENT_LENGTH`.
27
+ *
28
+ * Fails with a `ConfigError` when the API URL is not absolute or the retry count is not a
29
+ * non-negative integer.
30
+ */
31
+ export const requestEnv = Effect.fnUntraced(function* (options) {
32
+ const apiUrlInput = options.apiUrl ??
33
+ Option.getOrElse(yield* firstEnv("VERCEL_BLOB_API_URL", "NEXT_PUBLIC_VERCEL_BLOB_API_URL"), () => defaultApiUrl);
34
+ const apiUrl = yield* Option.match(decodeUrl(String(apiUrlInput)), {
35
+ onNone: () => Effect.fail(configError(`Invalid Blob API URL "${String(apiUrlInput)}": expected an absolute URL.`)),
36
+ onSome: Effect.succeed,
37
+ });
38
+ const apiVersion = Option.getOrElse(yield* firstEnv("VERCEL_BLOB_API_VERSION_OVERRIDE", "NEXT_PUBLIC_VERCEL_BLOB_API_VERSION_OVERRIDE"), () => defaultApiVersion);
39
+ const retries = options.retries ??
40
+ (yield* Option.match(yield* env("VERCEL_BLOB_RETRIES"), {
41
+ onNone: () => Effect.succeed(defaultRetries),
42
+ onSome: (value) => Option.match(decodeRetries(value), {
43
+ onNone: () => Effect.fail(configError(`Invalid VERCEL_BLOB_RETRIES "${value}": expected a non-negative integer.`)),
44
+ onSome: Effect.succeed,
45
+ }),
46
+ }));
47
+ if (!Number.isInteger(retries) || retries < 0) {
48
+ return yield* Effect.fail(configError(`Invalid retries ${retries}: expected a non-negative integer.`));
49
+ }
50
+ const sendContentLength = Option.contains(yield* env("VERCEL_BLOB_USE_X_CONTENT_LENGTH"), "1");
51
+ const proxyThroughAlternativeApi = yield* firstEnv("VERCEL_BLOB_PROXY_THROUGH_ALTERNATIVE_API", "NEXT_PUBLIC_VERCEL_BLOB_PROXY_THROUGH_ALTERNATIVE_API");
52
+ const headers = {
53
+ ...Option.match(proxyThroughAlternativeApi, {
54
+ onNone: () => ({}),
55
+ onSome: (value) => ({ "x-proxy-through-alternative-api": value }),
56
+ }),
57
+ ...options.headers,
58
+ };
59
+ return { apiUrl, apiVersion, retries, sendContentLength, headers };
60
+ });
61
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/internal/env.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ import { Duration, Effect } from "effect";
2
+ import type { BlobBody, OnUploadProgress, PutBlobResult } from "../contracts/blob-domain.ts";
3
+ import type { BlobApiError, BlobDomainErrorTag } from "./blob-errors.ts";
4
+ import type { BlobRequestAuth, BlobRequester } from "./blob-request.ts";
5
+ import type { WriteHeaderOptions, WriteOption } from "./write-headers.ts";
6
+ import { MultipartUpload, UploadedPart } from "../contracts/blob-domain.ts";
7
+ import { BlobUploadRejected } from "../contracts/blob-errors.ts";
8
+ /** Size of the parts an automatic multipart upload sends, as the SDK's `partSizeInBytes`. */
9
+ export declare const multipartPartSize: number;
10
+ /** Minimum time between progress events of an automatic multipart upload, as in the SDK. */
11
+ export declare const multipartProgressInterval: Duration.Duration;
12
+ /**
13
+ * How many parts an automatic multipart upload sends at once, as the SDK's `maxConcurrentUploads`:
14
+ * 6 in browsers (where `window` is defined), which allow about six connections per host, and 8
15
+ * elsewhere. Read on every call.
16
+ */
17
+ export declare const maxConcurrentPartUploads: () => number;
18
+ /**
19
+ * Which write options become headers at each step. Creation (and every request of an automatic
20
+ * upload, which the SDK sends with the `put` headers throughout) uses `create`; `uploadPart` and
21
+ * `completeMultipartUpload` rebuild their headers from the {@link MultipartUpload} handle with
22
+ * `part`.
23
+ */
24
+ export interface MultipartWriteOptions {
25
+ readonly create: ReadonlyArray<WriteOption>;
26
+ readonly part: ReadonlyArray<WriteOption>;
27
+ }
28
+ /** The SDK's server lists: every option at creation, all but `ifMatch` for parts and completion. */
29
+ export declare const serverMultipartWriteOptions: MultipartWriteOptions;
30
+ /** The SDK's client-token lists: only `contentType` (and `access`); the token fixes the rest. */
31
+ export declare const clientMultipartWriteOptions: MultipartWriteOptions;
32
+ /**
33
+ * Where and how multipart requests are sent. Build it once per layer for the server; the browser
34
+ * client builds one per call because its auth (client token or presigned payload) is per upload.
35
+ *
36
+ * @template EA - Failure of `auth`, such as `OidcTokenUnavailable`.
37
+ * @template RA - Requirements of `auth`.
38
+ * @template K - Domain errors the calling service's contract declares for writes.
39
+ */
40
+ export interface MultipartTarget<EA, RA, K extends BlobDomainErrorTag> {
41
+ readonly requester: BlobRequester;
42
+ /** Evaluated once per request, so refreshed OIDC tokens are picked up between parts. */
43
+ readonly auth: Effect.Effect<BlobRequestAuth, EA, RA>;
44
+ /**
45
+ * Prefix of the operation names in errors and spans: `BlobStore` or `BlobClient`. Requests are
46
+ * named `<service>.createMultipartUpload`, `<service>.uploadPart` and
47
+ * `<service>.completeMultipartUpload`.
48
+ */
49
+ readonly service: string;
50
+ readonly domainErrors: ReadonlyArray<K>;
51
+ readonly writeOptions: MultipartWriteOptions;
52
+ }
53
+ /** A part as the complete request sends it; `UploadedPart` and plain records both fit. */
54
+ export interface CompletedPart {
55
+ readonly etag: string;
56
+ readonly partNumber: number;
57
+ }
58
+ /**
59
+ * Start a multipart upload: `POST /mpu?pathname=…` with `x-mpu-action: create` and the
60
+ * `writeOptions.create` headers.
61
+ *
62
+ * @returns The handle: pathname, the store's key and upload id, `access` and every write option
63
+ * creation sent (cache lifetime in seconds), so parts and completion resend the same headers.
64
+ */
65
+ export declare const createMultipartUpload: <EA, RA, K extends BlobDomainErrorTag>(target: MultipartTarget<EA, RA, K>, pathname: string & import("effect/Brand").Brand<"BlobPathname">, options: WriteHeaderOptions) => Effect.Effect<MultipartUpload, EA | BlobApiError<K>, RA>;
66
+ /**
67
+ * Upload one part: `POST /mpu?pathname=…` with `x-mpu-action: upload`, `x-mpu-key`
68
+ * (URI-encoded), `x-mpu-upload-id`, `x-mpu-part-number` and the `writeOptions.part` headers
69
+ * rebuilt from the handle. The body is sent as given; a `Stream` body is not retried.
70
+ */
71
+ export declare const uploadPart: <EA, RA, K extends BlobDomainErrorTag, E = never, R = never>(target: MultipartTarget<EA, RA, K>, upload: MultipartUpload, partNumber: number & import("effect/Brand").Brand<"PartNumber">, body: BlobBody<E, R>) => Effect.Effect<UploadedPart, E | EA | BlobApiError<K>, R | RA>;
72
+ /**
73
+ * Assemble the parts into the blob: `POST /mpu?pathname=…` with `x-mpu-action: complete`, the
74
+ * `writeOptions.part` headers rebuilt from the handle, and the JSON array
75
+ * `[{"etag","partNumber"}, …]` in the order given.
76
+ */
77
+ export declare const completeMultipartUpload: <EA, RA, K extends BlobDomainErrorTag>(target: MultipartTarget<EA, RA, K>, upload: MultipartUpload, parts: ReadonlyArray<CompletedPart>) => Effect.Effect<PutBlobResult, EA | BlobApiError<K>, RA>;
78
+ /** Options of an automatic multipart upload. */
79
+ export type MultipartPutOptions = WriteHeaderOptions & {
80
+ /**
81
+ * Fail with `BlobUploadRejected` / `FileTooLarge` before any request when the body's known size
82
+ * is larger. Streams are not checked.
83
+ */
84
+ readonly maximumSizeInBytes?: number | undefined;
85
+ readonly onUploadProgress?: OnUploadProgress | undefined;
86
+ /** Parts sent at once; {@link maxConcurrentPartUploads} by default. */
87
+ readonly concurrency?: number | undefined;
88
+ };
89
+ /**
90
+ * Upload a body with a multipart upload, as the SDK's `uncontrolledMultipartUpload`: create it,
91
+ * send the body in {@link multipartPartSize} parts (the last one smaller) with bounded concurrency,
92
+ * then complete it with the parts in part-number order. Every request carries the
93
+ * `writeOptions.create` headers, as the SDK resends the `put` headers.
94
+ *
95
+ * - `maximumSizeInBytes` is checked against the known size before any request.
96
+ * - Memory stays bounded: a part's bytes are produced only when an upload slot is free, so at most
97
+ * `concurrency` parts (plus the parts cut from the stream chunk being read) are held at once.
98
+ * - The first failure, of a part or of the body stream, interrupts the parts in flight; the upload
99
+ * is then not completed.
100
+ * - An empty body uploads no part and completes with an empty part list, as the SDK does.
101
+ * - Progress: 0 % once the upload is created, then the bytes sent by all parts summed and
102
+ * throttled to one event per {@link multipartProgressInterval} (the SDK's numbers, rounded to two
103
+ * decimals). The event in which every byte has been acknowledged is held back, and
104
+ * `{ loaded, total: loaded, percentage: 100 }` is reported once the upload is completed instead.
105
+ *
106
+ * @returns The stored blob; fails with `BlobUploadRejected` for an oversized body, the requests'
107
+ * errors, or the body stream's error.
108
+ */
109
+ export declare const multipartPut: <EA, RA, K extends BlobDomainErrorTag, E = never, R = never>(target: MultipartTarget<EA, RA, K>, pathname: string & import("effect/Brand").Brand<"BlobPathname">, body: BlobBody<E, R>, options: MultipartPutOptions) => Effect.Effect<PutBlobResult, E | EA | BlobUploadRejected | BlobApiError<K>, R | RA>;
110
+ //# sourceMappingURL=multipart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multipart.d.ts","sourceRoot":"","sources":["../../src/internal/multipart.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAA2B,MAAM,QAAQ,CAAC;AAEnE,OAAO,KAAK,EACV,QAAQ,EAER,gBAAgB,EAChB,aAAa,EAEd,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE1E,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAgB,MAAM,6BAA6B,CAAC;AAW/E,6FAA6F;AAC7F,eAAO,MAAM,iBAAiB,QAAkB,CAAC;AAEjD,4FAA4F;AAC5F,eAAO,MAAM,yBAAyB,mBAAuB,CAAC;AAE9D;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,QAAO,MACgC,CAAC;AAE7E;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;CAC3C;AAED,oGAAoG;AACpG,eAAO,MAAM,2BAA2B,EAAE,qBAGzC,CAAC;AAEF,iGAAiG;AACjG,eAAO,MAAM,2BAA2B,EAAE,qBAGzC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,SAAS,kBAAkB;IACnE,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAElC,wFAAwF;IACxF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAEtD;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAExC,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC;CAC9C;AAED,0FAA0F;AAC1F,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAkID;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAChC,EAAE,EACF,EAAE,EACF,CAAC,SAAS,kBAAkB,+LAkB5B,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,UAAU,GACrB,EAAE,EACF,EAAE,EACF,CAAC,SAAS,kBAAkB,EAC5B,CAAC,UACD,CAAC,8NAgBD,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAAI,EAAE,EAAE,EAAE,EAAE,CAAC,SAAS,kBAAkB,UAClE,eAAe,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,UAC1B,eAAe,SAChB,aAAa,CAAC,aAAa,CAAC,2DASlC,CAAC;AAEJ,gDAAgD;AAChD,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,GAAG;IACrD;;;OAGG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEjD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAEzD,uEAAuE;IACvE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C,CAAC;AAoGF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,GACvB,EAAE,EACF,EAAE,EACF,CAAC,SAAS,kBAAkB,EAC5B,CAAC,UACD,CAAC,yPAyFD,CAAC"}
@@ -0,0 +1,290 @@
1
+ import { Duration, Effect, Stream, SynchronizedRef } from "effect";
2
+ import { MultipartUpload, UploadedPart } from "../contracts/blob-domain.js";
3
+ import { BlobUploadRejected, FileTooLarge } from "../contracts/blob-errors.js";
4
+ import { bodyLength, uploadProgressThrottle } from "./blob-request.js";
5
+ import { CreateMultipartUploadResponse, PutBlobResponse, UploadPartResponse } from "./wire.js";
6
+ import { allowedWriteOptions, cacheControlMaxAgeSeconds, writeHeaders } from "./write-headers.js";
7
+ /*
8
+ * Multipart uploads, shared by the server `BlobStore` and the browser `BlobClient`: the manual
9
+ * primitives (create, upload a part, complete) and the automatic upload behind `put({ multipart })`,
10
+ * ported from the SDK's `multipart/*.ts` and `uncontrolledMultipartUpload`.
11
+ */
12
+ /** Size of the parts an automatic multipart upload sends, as the SDK's `partSizeInBytes`. */
13
+ export const multipartPartSize = 8 * 1024 * 1024;
14
+ /** Minimum time between progress events of an automatic multipart upload, as in the SDK. */
15
+ export const multipartProgressInterval = Duration.millis(150);
16
+ /**
17
+ * How many parts an automatic multipart upload sends at once, as the SDK's `maxConcurrentUploads`:
18
+ * 6 in browsers (where `window` is defined), which allow about six connections per host, and 8
19
+ * elsewhere. Read on every call.
20
+ */
21
+ export const maxConcurrentPartUploads = () => globalThis.window === undefined ? 8 : 6;
22
+ /** The SDK's server lists: every option at creation, all but `ifMatch` for parts and completion. */
23
+ export const serverMultipartWriteOptions = {
24
+ create: allowedWriteOptions.put,
25
+ part: allowedWriteOptions.multipartPart,
26
+ };
27
+ /** The SDK's client-token lists: only `contentType` (and `access`); the token fixes the rest. */
28
+ export const clientMultipartWriteOptions = {
29
+ create: allowedWriteOptions.client,
30
+ part: allowedWriteOptions.client,
31
+ };
32
+ const mpuPath = (pathname) => `/mpu?${new URLSearchParams({ pathname }).toString()}`;
33
+ /** The handle's write options, filtered to what creation sent, with the cache lifetime in seconds. */
34
+ const handleWriteOptions = (allowed, options) => ({
35
+ access: options.access,
36
+ ...(allowed.includes("addRandomSuffix") && options.addRandomSuffix !== undefined
37
+ ? { addRandomSuffix: options.addRandomSuffix }
38
+ : {}),
39
+ ...(allowed.includes("allowOverwrite") && options.allowOverwrite !== undefined
40
+ ? { allowOverwrite: options.allowOverwrite }
41
+ : {}),
42
+ ...(allowed.includes("contentType") && options.contentType
43
+ ? { contentType: options.contentType }
44
+ : {}),
45
+ ...(allowed.includes("cacheControlMaxAge") && options.cacheControlMaxAge !== undefined
46
+ ? { cacheControlMaxAge: cacheControlMaxAgeSeconds(options.cacheControlMaxAge) }
47
+ : {}),
48
+ });
49
+ /** The write options a handle carries, back in the form `writeHeaders` reads. */
50
+ const uploadWriteOptions = (upload) => ({
51
+ access: upload.access,
52
+ addRandomSuffix: upload.addRandomSuffix,
53
+ allowOverwrite: upload.allowOverwrite,
54
+ contentType: upload.contentType,
55
+ cacheControlMaxAge: upload.cacheControlMaxAge === undefined
56
+ ? undefined
57
+ : Duration.seconds(upload.cacheControlMaxAge),
58
+ });
59
+ const createRequest = ({ target, pathname, headers, }) => target.requester.call({
60
+ operation: `${target.service}.createMultipartUpload`,
61
+ method: "POST",
62
+ path: mpuPath(pathname),
63
+ auth: target.auth,
64
+ response: CreateMultipartUploadResponse,
65
+ headers: { ...headers, "x-mpu-action": "create" },
66
+ locator: pathname,
67
+ domainErrors: target.domainErrors,
68
+ });
69
+ const uploadPartRequest = ({ target, pathname, headers }, part) => target.requester.call({
70
+ operation: `${target.service}.uploadPart`,
71
+ method: "POST",
72
+ path: mpuPath(pathname),
73
+ auth: target.auth,
74
+ response: UploadPartResponse,
75
+ headers: {
76
+ ...headers,
77
+ "x-mpu-action": "upload",
78
+ // The key can hold any UTF-8 character, and header values must be US-ASCII.
79
+ "x-mpu-key": encodeURIComponent(part.key),
80
+ "x-mpu-upload-id": part.uploadId,
81
+ "x-mpu-part-number": String(part.partNumber),
82
+ ...(part.contentType === undefined ? {} : { "content-type": part.contentType }),
83
+ },
84
+ body: part.body,
85
+ locator: pathname,
86
+ domainErrors: target.domainErrors,
87
+ onUploadProgress: part.onUploadProgress,
88
+ });
89
+ const completeRequest = ({ target, pathname, headers }, upload) => target.requester.call({
90
+ operation: `${target.service}.completeMultipartUpload`,
91
+ method: "POST",
92
+ path: mpuPath(pathname),
93
+ auth: target.auth,
94
+ response: PutBlobResponse,
95
+ headers: {
96
+ ...headers,
97
+ "x-mpu-action": "complete",
98
+ "x-mpu-upload-id": upload.uploadId,
99
+ "x-mpu-key": encodeURIComponent(upload.key),
100
+ },
101
+ // `content-type: application/json` comes with `json`; fields in the SDK's order.
102
+ json: upload.parts.map(({ etag, partNumber }) => upload.partNumberFirst === true ? { partNumber, etag } : { etag, partNumber }),
103
+ locator: pathname,
104
+ domainErrors: target.domainErrors,
105
+ });
106
+ /**
107
+ * Start a multipart upload: `POST /mpu?pathname=…` with `x-mpu-action: create` and the
108
+ * `writeOptions.create` headers.
109
+ *
110
+ * @returns The handle: pathname, the store's key and upload id, `access` and every write option
111
+ * creation sent (cache lifetime in seconds), so parts and completion resend the same headers.
112
+ */
113
+ export const createMultipartUpload = Effect.fnUntraced(function* (target, pathname, options) {
114
+ const allowed = target.writeOptions.create;
115
+ const { key, uploadId } = yield* createRequest({
116
+ target,
117
+ pathname,
118
+ headers: writeHeaders(allowed, options),
119
+ });
120
+ return new MultipartUpload({
121
+ pathname,
122
+ key,
123
+ uploadId,
124
+ ...handleWriteOptions(allowed, options),
125
+ });
126
+ });
127
+ /**
128
+ * Upload one part: `POST /mpu?pathname=…` with `x-mpu-action: upload`, `x-mpu-key`
129
+ * (URI-encoded), `x-mpu-upload-id`, `x-mpu-part-number` and the `writeOptions.part` headers
130
+ * rebuilt from the handle. The body is sent as given; a `Stream` body is not retried.
131
+ */
132
+ export const uploadPart = Effect.fnUntraced(function* (target, upload, partNumber, body) {
133
+ const { etag } = yield* uploadPartRequest({
134
+ target,
135
+ pathname: upload.pathname,
136
+ headers: writeHeaders(target.writeOptions.part, uploadWriteOptions(upload)),
137
+ }, { key: upload.key, uploadId: upload.uploadId, partNumber, body });
138
+ return new UploadedPart({ etag, partNumber });
139
+ });
140
+ /**
141
+ * Assemble the parts into the blob: `POST /mpu?pathname=…` with `x-mpu-action: complete`, the
142
+ * `writeOptions.part` headers rebuilt from the handle, and the JSON array
143
+ * `[{"etag","partNumber"}, …]` in the order given.
144
+ */
145
+ export const completeMultipartUpload = (target, upload, parts) => completeRequest({
146
+ target,
147
+ pathname: upload.pathname,
148
+ headers: writeHeaders(target.writeOptions.part, uploadWriteOptions(upload)),
149
+ }, { key: upload.key, uploadId: upload.uploadId, parts });
150
+ /**
151
+ * Regroup a byte stream into parts of exactly `size` bytes, the last one smaller, copying each
152
+ * byte once into its part (the SDK cuts parts the same way, because R2 refuses uneven parts).
153
+ */
154
+ const regroupParts = (size) => (stream) => Stream.mapAccum(stream, () => ({
155
+ part: undefined,
156
+ filled: 0,
157
+ }), (state, chunk) => {
158
+ let { part, filled } = state;
159
+ const full = [];
160
+ let position = 0;
161
+ while (position < chunk.length) {
162
+ part ??= new Uint8Array(size);
163
+ const count = Math.min(size - filled, chunk.length - position);
164
+ part.set(chunk.subarray(position, position + count), filled);
165
+ filled += count;
166
+ position += count;
167
+ if (filled === size) {
168
+ full.push(part);
169
+ part = undefined;
170
+ filled = 0;
171
+ }
172
+ }
173
+ return [{ part, filled }, full];
174
+ }, {
175
+ onHalt: ({ part, filled }) => part === undefined || filled === 0 ? [] : [part.slice(0, filled)],
176
+ });
177
+ const partRanges = (length, size) => Array.from({ length: Math.ceil(length / size) }, (_, index) => [
178
+ index * size,
179
+ Math.min(length, (index + 1) * size),
180
+ ]);
181
+ const textEncoder = new TextEncoder();
182
+ const isBlob = (body) => typeof globalThis.Blob !== "undefined" && body instanceof globalThis.Blob;
183
+ /**
184
+ * The body cut into parts, each one plain bytes so it can be retried and sent by browsers that
185
+ * cannot stream request bodies. In-memory bodies are cut into views without copying; a `Blob` is
186
+ * sliced and each slice read only when its upload starts; a stream is regrouped as it is read.
187
+ */
188
+ const bodyParts = (body, size) => {
189
+ if (typeof body === "string" || body instanceof Uint8Array) {
190
+ const bytes = typeof body === "string" ? textEncoder.encode(body) : body;
191
+ return Stream.fromIterable(partRanges(bytes.length, size).map(([start, end]) => Effect.succeed(bytes.subarray(start, end))));
192
+ }
193
+ if (isBlob(body)) {
194
+ // A Blob that cannot be read is a defect, as in `bodyStream`.
195
+ return Stream.fromIterable(partRanges(body.size, size).map(([start, end]) => Effect.map(Effect.promise(() => body.slice(start, end).arrayBuffer()), (buffer) => new Uint8Array(buffer))));
196
+ }
197
+ return body.pipe(regroupParts(size), Stream.map(Effect.succeed));
198
+ };
199
+ const sumLoaded = (loadedByPart) => {
200
+ let loaded = 0;
201
+ for (const partLoaded of loadedByPart.values())
202
+ loaded += partLoaded;
203
+ return loaded;
204
+ };
205
+ /** The SDK's aggregate event: bytes sent by every part, against the body's known size. */
206
+ const aggregateProgress = (loadedByPart, totalToLoad) => {
207
+ const loaded = sumLoaded(loadedByPart);
208
+ return {
209
+ loaded,
210
+ total: totalToLoad || loaded,
211
+ percentage: totalToLoad > 0 ? Number(((loaded / totalToLoad) * 100).toFixed(2)) : 0,
212
+ };
213
+ };
214
+ /**
215
+ * Upload a body with a multipart upload, as the SDK's `uncontrolledMultipartUpload`: create it,
216
+ * send the body in {@link multipartPartSize} parts (the last one smaller) with bounded concurrency,
217
+ * then complete it with the parts in part-number order. Every request carries the
218
+ * `writeOptions.create` headers, as the SDK resends the `put` headers.
219
+ *
220
+ * - `maximumSizeInBytes` is checked against the known size before any request.
221
+ * - Memory stays bounded: a part's bytes are produced only when an upload slot is free, so at most
222
+ * `concurrency` parts (plus the parts cut from the stream chunk being read) are held at once.
223
+ * - The first failure, of a part or of the body stream, interrupts the parts in flight; the upload
224
+ * is then not completed.
225
+ * - An empty body uploads no part and completes with an empty part list, as the SDK does.
226
+ * - Progress: 0 % once the upload is created, then the bytes sent by all parts summed and
227
+ * throttled to one event per {@link multipartProgressInterval} (the SDK's numbers, rounded to two
228
+ * decimals). The event in which every byte has been acknowledged is held back, and
229
+ * `{ loaded, total: loaded, percentage: 100 }` is reported once the upload is completed instead.
230
+ *
231
+ * @returns The stored blob; fails with `BlobUploadRejected` for an oversized body, the requests'
232
+ * errors, or the body stream's error.
233
+ */
234
+ export const multipartPut = Effect.fnUntraced(function* (target, pathname, body, options) {
235
+ const totalToLoad = bodyLength(body);
236
+ const maximum = options.maximumSizeInBytes;
237
+ if (maximum !== undefined && !Stream.isStream(body) && totalToLoad > maximum) {
238
+ return yield* new BlobUploadRejected({
239
+ reason: new FileTooLarge({
240
+ message: `Body size of ${totalToLoad} bytes exceeds the maximum allowed size of ${maximum} bytes`,
241
+ }),
242
+ });
243
+ }
244
+ const step = {
245
+ target,
246
+ pathname,
247
+ headers: writeHeaders(target.writeOptions.create, options),
248
+ };
249
+ const service = target.service;
250
+ const { key, uploadId } = yield* createRequest(step).pipe(Effect.withSpan(`${service}.createMultipartUpload`));
251
+ const onUploadProgress = options.onUploadProgress;
252
+ const loadedByPart = yield* SynchronizedRef.make(new Map());
253
+ const uploadParts = (report) => bodyParts(body, multipartPartSize).pipe(Stream.mapEffect((source, index) => Effect.gen(function* () {
254
+ const partNumber = index + 1;
255
+ const bytes = yield* source;
256
+ const { etag } = yield* uploadPartRequest(step, {
257
+ key,
258
+ uploadId,
259
+ partNumber,
260
+ body: bytes,
261
+ onUploadProgress: report?.(partNumber),
262
+ contentType: "application/octet-stream",
263
+ });
264
+ return { etag, partNumber };
265
+ }).pipe(Effect.withSpan(`${service}.uploadPart`, { attributes: { partNumber: index + 1 } })), { concurrency: options.concurrency ?? maxConcurrentPartUploads(), unordered: true }), Stream.runCollect);
266
+ const complete = (parts) => completeRequest(step, {
267
+ key,
268
+ uploadId,
269
+ parts: [...parts].sort((a, b) => a.partNumber - b.partNumber),
270
+ partNumberFirst: true,
271
+ }).pipe(Effect.withSpan(`${service}.completeMultipartUpload`));
272
+ if (onUploadProgress === undefined) {
273
+ return yield* Effect.flatMap(uploadParts(undefined), complete);
274
+ }
275
+ yield* onUploadProgress({ loaded: 0, total: totalToLoad, percentage: 0 });
276
+ const reporter = yield* uploadProgressThrottle(onUploadProgress, multipartProgressInterval);
277
+ // Updating the sums and offering the event under one lock keeps events in sending order. The SDK
278
+ // also reports the aggregate in which every byte is acknowledged; it is left for the final event.
279
+ const partProgress = (partNumber) => (progress) => SynchronizedRef.updateEffect(loadedByPart, (current) => {
280
+ const next = new Map(current).set(partNumber, progress.loaded);
281
+ const event = aggregateProgress(next, totalToLoad);
282
+ const acknowledged = totalToLoad > 0 && event.loaded >= totalToLoad;
283
+ return Effect.as(acknowledged ? Effect.void : reporter.report(event), next);
284
+ });
285
+ const blob = yield* uploadParts(partProgress).pipe(Effect.flatMap(complete), Effect.ensuring(reporter.stop));
286
+ const loaded = sumLoaded(yield* SynchronizedRef.get(loadedByPart));
287
+ yield* onUploadProgress({ loaded, total: loaded, percentage: 100 });
288
+ return blob;
289
+ });
290
+ //# sourceMappingURL=multipart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multipart.js","sourceRoot":"","sources":["../../src/internal/multipart.ts"],"names":[],"mappings":"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"}