@depup/miniflare 4.20260317.0-depup.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 (91) hide show
  1. package/README.md +31 -0
  2. package/bootstrap.js +11 -0
  3. package/changes.json +10 -0
  4. package/dist/local-explorer-ui/assets/Breadcrumbs-7hjI0sYt.js +1 -0
  5. package/dist/local-explorer-ui/assets/TableSelect-_bi6l6Iv.js +25 -0
  6. package/dist/local-explorer-ui/assets/_className-gSkjniQn.js +1 -0
  7. package/dist/local-explorer-ui/assets/_databaseId-DWcfgBis.js +1 -0
  8. package/dist/local-explorer-ui/assets/_namespaceId-8ViM8VDL.js +1 -0
  9. package/dist/local-explorer-ui/assets/_objectId-X6x-JUph.js +1 -0
  10. package/dist/local-explorer-ui/assets/dropdown-DFeFcKfn-BCd_NRZS.js +1 -0
  11. package/dist/local-explorer-ui/assets/index-BFNDwiew.js +42 -0
  12. package/dist/local-explorer-ui/assets/index-CLSFsgi0.js +1 -0
  13. package/dist/local-explorer-ui/assets/index-KG4JeHCX.js +1 -0
  14. package/dist/local-explorer-ui/assets/index-mgoUmSld.css +1 -0
  15. package/dist/local-explorer-ui/assets/table-BUmvaBj8-v-EIZgOz.js +1 -0
  16. package/dist/local-explorer-ui/favicon.svg +3 -0
  17. package/dist/local-explorer-ui/index.html +14 -0
  18. package/dist/src/index.d.ts +8703 -0
  19. package/dist/src/index.js +83666 -0
  20. package/dist/src/index.js.map +6 -0
  21. package/dist/src/shared/dev-registry.worker.js +69801 -0
  22. package/dist/src/shared/dev-registry.worker.js.map +6 -0
  23. package/dist/src/workers/analytics-engine/analytics-engine.worker.js +15 -0
  24. package/dist/src/workers/analytics-engine/analytics-engine.worker.js.map +6 -0
  25. package/dist/src/workers/assets/assets-kv.worker.js +28 -0
  26. package/dist/src/workers/assets/assets-kv.worker.js.map +6 -0
  27. package/dist/src/workers/assets/assets.worker.js +9014 -0
  28. package/dist/src/workers/assets/assets.worker.js.map +6 -0
  29. package/dist/src/workers/assets/router.worker.js +9625 -0
  30. package/dist/src/workers/assets/router.worker.js.map +6 -0
  31. package/dist/src/workers/assets/rpc-proxy.worker.js +29 -0
  32. package/dist/src/workers/assets/rpc-proxy.worker.js.map +6 -0
  33. package/dist/src/workers/browser-rendering/binding.worker.js +129 -0
  34. package/dist/src/workers/browser-rendering/binding.worker.js.map +6 -0
  35. package/dist/src/workers/cache/cache-entry-noop.worker.js +19 -0
  36. package/dist/src/workers/cache/cache-entry-noop.worker.js.map +6 -0
  37. package/dist/src/workers/cache/cache-entry.worker.js +28 -0
  38. package/dist/src/workers/cache/cache-entry.worker.js.map +6 -0
  39. package/dist/src/workers/cache/cache.worker.js +653 -0
  40. package/dist/src/workers/cache/cache.worker.js.map +6 -0
  41. package/dist/src/workers/core/do-wrapper.worker.js +43 -0
  42. package/dist/src/workers/core/do-wrapper.worker.js.map +6 -0
  43. package/dist/src/workers/core/entry.worker.js +4633 -0
  44. package/dist/src/workers/core/entry.worker.js.map +6 -0
  45. package/dist/src/workers/core/strip-cf-connecting-ip.worker.js +11 -0
  46. package/dist/src/workers/core/strip-cf-connecting-ip.worker.js.map +6 -0
  47. package/dist/src/workers/d1/database.worker.js +219 -0
  48. package/dist/src/workers/d1/database.worker.js.map +6 -0
  49. package/dist/src/workers/dispatch-namespace/dispatch-namespace-proxy.worker.js +2271 -0
  50. package/dist/src/workers/dispatch-namespace/dispatch-namespace-proxy.worker.js.map +6 -0
  51. package/dist/src/workers/dispatch-namespace/dispatch-namespace.worker.js +12 -0
  52. package/dist/src/workers/dispatch-namespace/dispatch-namespace.worker.js.map +6 -0
  53. package/dist/src/workers/email/email.worker.js +23 -0
  54. package/dist/src/workers/email/email.worker.js.map +6 -0
  55. package/dist/src/workers/email/send_email.worker.js +3294 -0
  56. package/dist/src/workers/email/send_email.worker.js.map +6 -0
  57. package/dist/src/workers/hello-world/binding.worker.js +19 -0
  58. package/dist/src/workers/hello-world/binding.worker.js.map +6 -0
  59. package/dist/src/workers/hello-world/object.worker.js +14 -0
  60. package/dist/src/workers/hello-world/object.worker.js.map +6 -0
  61. package/dist/src/workers/images/images.worker.js +155 -0
  62. package/dist/src/workers/images/images.worker.js.map +6 -0
  63. package/dist/src/workers/kv/namespace.worker.js +322 -0
  64. package/dist/src/workers/kv/namespace.worker.js.map +6 -0
  65. package/dist/src/workers/kv/sites.worker.js +146 -0
  66. package/dist/src/workers/kv/sites.worker.js.map +6 -0
  67. package/dist/src/workers/local-explorer/explorer.worker.js +5245 -0
  68. package/dist/src/workers/local-explorer/explorer.worker.js.map +6 -0
  69. package/dist/src/workers/pipelines/pipeline.worker.js +10 -0
  70. package/dist/src/workers/pipelines/pipeline.worker.js.map +6 -0
  71. package/dist/src/workers/queues/broker.worker.js +289 -0
  72. package/dist/src/workers/queues/broker.worker.js.map +6 -0
  73. package/dist/src/workers/r2/bucket.worker.js +1134 -0
  74. package/dist/src/workers/r2/bucket.worker.js.map +6 -0
  75. package/dist/src/workers/ratelimit/ratelimit.worker.js +54 -0
  76. package/dist/src/workers/ratelimit/ratelimit.worker.js.map +6 -0
  77. package/dist/src/workers/secrets-store/secret.worker.js +65 -0
  78. package/dist/src/workers/secrets-store/secret.worker.js.map +6 -0
  79. package/dist/src/workers/shared/index.worker.js +693 -0
  80. package/dist/src/workers/shared/index.worker.js.map +6 -0
  81. package/dist/src/workers/shared/object-entry.worker.js +21 -0
  82. package/dist/src/workers/shared/object-entry.worker.js.map +6 -0
  83. package/dist/src/workers/shared/remote-proxy-client.worker.js +2271 -0
  84. package/dist/src/workers/shared/remote-proxy-client.worker.js.map +6 -0
  85. package/dist/src/workers/shared/zod.worker.js +2954 -0
  86. package/dist/src/workers/shared/zod.worker.js.map +6 -0
  87. package/dist/src/workers/workflows/binding.worker.js +2422 -0
  88. package/dist/src/workers/workflows/binding.worker.js.map +6 -0
  89. package/dist/src/workers/workflows/wrapped-binding.worker.js +71 -0
  90. package/dist/src/workers/workflows/wrapped-binding.worker.js.map +6 -0
  91. package/package.json +139 -0
@@ -0,0 +1,1134 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ for (var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target, i = decorators.length - 1, decorator; i >= 0; i--)
5
+ (decorator = decorators[i]) && (result = (kind ? decorator(target, key, result) : decorator(result)) || result);
6
+ return kind && result && __defProp(target, key, result), result;
7
+ };
8
+
9
+ // src/workers/r2/bucket.worker.ts
10
+ import assert2 from "node:assert";
11
+ import { Buffer as Buffer3 } from "node:buffer";
12
+ import { createHash } from "node:crypto";
13
+ import {
14
+ all,
15
+ base64Decode,
16
+ base64Encode,
17
+ DeferredPromise,
18
+ GET,
19
+ get,
20
+ maybeApply,
21
+ MiniflareDurableObject,
22
+ PUT,
23
+ readPrefix,
24
+ WaitGroup
25
+ } from "miniflare:shared";
26
+
27
+ // src/workers/r2/constants.ts
28
+ var R2Limits = {
29
+ MAX_LIST_KEYS: 1e3,
30
+ MAX_KEY_SIZE: 1024,
31
+ // https://developers.cloudflare.com/r2/platform/limits/
32
+ MAX_VALUE_SIZE: 5363466240,
33
+ // 5 GiB - 5 MiB
34
+ MAX_METADATA_SIZE: 2048,
35
+ // 2048 B
36
+ MIN_MULTIPART_PART_SIZE: 5242880,
37
+ MIN_MULTIPART_PART_SIZE_TEST: 50
38
+ }, R2Headers = {
39
+ ERROR: "cf-r2-error",
40
+ REQUEST: "cf-r2-request",
41
+ METADATA_SIZE: "cf-r2-metadata-size"
42
+ };
43
+
44
+ // src/workers/r2/errors.worker.ts
45
+ import { HttpError } from "miniflare:shared";
46
+ var R2ErrorCode = {
47
+ INTERNAL_ERROR: 10001,
48
+ NO_SUCH_OBJECT_KEY: 10007,
49
+ ENTITY_TOO_LARGE: 100100,
50
+ ENTITY_TOO_SMALL: 10011,
51
+ METADATA_TOO_LARGE: 10012,
52
+ INVALID_OBJECT_NAME: 10020,
53
+ INVALID_MAX_KEYS: 10022,
54
+ NO_SUCH_UPLOAD: 10024,
55
+ INVALID_PART: 10025,
56
+ INVALID_ARGUMENT: 10029,
57
+ PRECONDITION_FAILED: 10031,
58
+ BAD_DIGEST: 10037,
59
+ INVALID_RANGE: 10039,
60
+ BAD_UPLOAD: 10048
61
+ }, R2Error = class extends HttpError {
62
+ constructor(code, message, v4Code) {
63
+ super(code, message);
64
+ this.v4Code = v4Code;
65
+ }
66
+ object;
67
+ toResponse() {
68
+ if (this.object !== void 0) {
69
+ let { metadataSize, value } = this.object.encode();
70
+ return new Response(value, {
71
+ status: this.code,
72
+ headers: {
73
+ [R2Headers.METADATA_SIZE]: `${metadataSize}`,
74
+ "Content-Type": "application/json",
75
+ [R2Headers.ERROR]: JSON.stringify({
76
+ message: this.message,
77
+ version: 1,
78
+ // Note the lowercase 'c', which the runtime expects
79
+ v4code: this.v4Code
80
+ })
81
+ }
82
+ });
83
+ }
84
+ return new Response(null, {
85
+ status: this.code,
86
+ headers: {
87
+ [R2Headers.ERROR]: JSON.stringify({
88
+ message: this.message,
89
+ version: 1,
90
+ // Note the lowercase 'c', which the runtime expects
91
+ v4code: this.v4Code
92
+ })
93
+ }
94
+ });
95
+ }
96
+ context(info) {
97
+ return this.message += ` (${info})`, this;
98
+ }
99
+ attach(object) {
100
+ return this.object = object, this;
101
+ }
102
+ }, InvalidMetadata = class extends R2Error {
103
+ constructor() {
104
+ super(400, "Metadata missing or invalid", R2ErrorCode.INVALID_ARGUMENT);
105
+ }
106
+ }, InternalError = class extends R2Error {
107
+ constructor() {
108
+ super(
109
+ 500,
110
+ "We encountered an internal error. Please try again.",
111
+ R2ErrorCode.INTERNAL_ERROR
112
+ );
113
+ }
114
+ }, NoSuchKey = class extends R2Error {
115
+ constructor() {
116
+ super(
117
+ 404,
118
+ "The specified key does not exist.",
119
+ R2ErrorCode.NO_SUCH_OBJECT_KEY
120
+ );
121
+ }
122
+ }, EntityTooLarge = class extends R2Error {
123
+ constructor() {
124
+ super(
125
+ 400,
126
+ "Your proposed upload exceeds the maximum allowed object size.",
127
+ R2ErrorCode.ENTITY_TOO_LARGE
128
+ );
129
+ }
130
+ }, EntityTooSmall = class extends R2Error {
131
+ constructor() {
132
+ super(
133
+ 400,
134
+ "Your proposed upload is smaller than the minimum allowed object size.",
135
+ R2ErrorCode.ENTITY_TOO_SMALL
136
+ );
137
+ }
138
+ }, MetadataTooLarge = class extends R2Error {
139
+ constructor() {
140
+ super(
141
+ 400,
142
+ "Your metadata headers exceed the maximum allowed metadata size.",
143
+ R2ErrorCode.METADATA_TOO_LARGE
144
+ );
145
+ }
146
+ }, BadDigest = class extends R2Error {
147
+ constructor(algorithm, provided, calculated) {
148
+ super(
149
+ 400,
150
+ [
151
+ `The ${algorithm} checksum you specified did not match what we received.`,
152
+ `You provided a ${algorithm} checksum with value: ${provided.toString(
153
+ "hex"
154
+ )}`,
155
+ `Actual ${algorithm} was: ${calculated.toString("hex")}`
156
+ ].join(`
157
+ `),
158
+ R2ErrorCode.BAD_DIGEST
159
+ );
160
+ }
161
+ }, InvalidObjectName = class extends R2Error {
162
+ constructor() {
163
+ super(
164
+ 400,
165
+ "The specified object name is not valid.",
166
+ R2ErrorCode.INVALID_OBJECT_NAME
167
+ );
168
+ }
169
+ }, InvalidMaxKeys = class extends R2Error {
170
+ constructor() {
171
+ super(
172
+ 400,
173
+ "MaxKeys params must be positive integer <= 1000.",
174
+ R2ErrorCode.INVALID_MAX_KEYS
175
+ );
176
+ }
177
+ }, NoSuchUpload = class extends R2Error {
178
+ constructor() {
179
+ super(
180
+ 400,
181
+ "The specified multipart upload does not exist.",
182
+ R2ErrorCode.NO_SUCH_UPLOAD
183
+ );
184
+ }
185
+ }, InvalidPart = class extends R2Error {
186
+ constructor() {
187
+ super(
188
+ 400,
189
+ "One or more of the specified parts could not be found.",
190
+ R2ErrorCode.INVALID_PART
191
+ );
192
+ }
193
+ }, PreconditionFailed = class extends R2Error {
194
+ constructor() {
195
+ super(
196
+ 412,
197
+ "At least one of the pre-conditions you specified did not hold.",
198
+ R2ErrorCode.PRECONDITION_FAILED
199
+ );
200
+ }
201
+ }, InvalidRange = class extends R2Error {
202
+ constructor() {
203
+ super(
204
+ 416,
205
+ "The requested range is not satisfiable",
206
+ R2ErrorCode.INVALID_RANGE
207
+ );
208
+ }
209
+ }, BadUpload = class extends R2Error {
210
+ constructor() {
211
+ super(
212
+ 500,
213
+ "There was a problem with the multipart upload.",
214
+ R2ErrorCode.BAD_UPLOAD
215
+ );
216
+ }
217
+ };
218
+
219
+ // src/workers/r2/r2Object.worker.ts
220
+ import { HEX_REGEXP } from "miniflare:zod";
221
+ var InternalR2Object = class {
222
+ key;
223
+ version;
224
+ size;
225
+ etag;
226
+ uploaded;
227
+ httpMetadata;
228
+ customMetadata;
229
+ range;
230
+ checksums;
231
+ constructor(row, range) {
232
+ this.key = row.key, this.version = row.version, this.size = row.size, this.etag = row.etag, this.uploaded = row.uploaded, this.httpMetadata = JSON.parse(row.http_metadata), this.customMetadata = JSON.parse(row.custom_metadata), this.range = range;
233
+ let checksums = JSON.parse(row.checksums);
234
+ this.etag.length === 32 && HEX_REGEXP.test(this.etag) && (checksums.md5 = row.etag), this.checksums = checksums;
235
+ }
236
+ // Format for return to the Workers Runtime
237
+ #rawProperties() {
238
+ return {
239
+ name: this.key,
240
+ version: this.version,
241
+ size: this.size,
242
+ etag: this.etag,
243
+ uploaded: this.uploaded,
244
+ httpFields: this.httpMetadata,
245
+ customFields: Object.entries(this.customMetadata).map(([k, v]) => ({
246
+ k,
247
+ v
248
+ })),
249
+ range: this.range,
250
+ checksums: {
251
+ 0: this.checksums.md5,
252
+ 1: this.checksums.sha1,
253
+ 2: this.checksums.sha256,
254
+ 3: this.checksums.sha384,
255
+ 4: this.checksums.sha512
256
+ }
257
+ };
258
+ }
259
+ encode() {
260
+ let json = JSON.stringify(this.#rawProperties()), blob = new Blob([json]);
261
+ return { metadataSize: blob.size, value: blob.stream(), size: blob.size };
262
+ }
263
+ static encodeMultiple(objects) {
264
+ let json = JSON.stringify({
265
+ ...objects,
266
+ objects: objects.objects.map((o) => o.#rawProperties())
267
+ }), blob = new Blob([json]);
268
+ return { metadataSize: blob.size, value: blob.stream(), size: blob.size };
269
+ }
270
+ }, InternalR2ObjectBody = class extends InternalR2Object {
271
+ constructor(metadata, body, range) {
272
+ super(metadata, range);
273
+ this.body = body;
274
+ }
275
+ encode() {
276
+ let { metadataSize, value: metadata } = super.encode(), size = this.range?.length ?? this.size, identity2 = new FixedLengthStream(size + metadataSize);
277
+ return metadata.pipeTo(identity2.writable, { preventClose: !0 }).then(() => this.body.pipeTo(identity2.writable)), {
278
+ metadataSize,
279
+ value: identity2.readable,
280
+ size
281
+ };
282
+ }
283
+ };
284
+
285
+ // src/workers/r2/schemas.worker.ts
286
+ import { Base64DataSchema, HexDataSchema, z } from "miniflare:zod";
287
+ var MultipartUploadState = {
288
+ IN_PROGRESS: 0,
289
+ COMPLETED: 1,
290
+ ABORTED: 2
291
+ }, SQL_SCHEMA = `
292
+ CREATE TABLE IF NOT EXISTS _mf_objects (
293
+ key TEXT PRIMARY KEY,
294
+ blob_id TEXT,
295
+ version TEXT NOT NULL,
296
+ size INTEGER NOT NULL,
297
+ etag TEXT NOT NULL,
298
+ uploaded INTEGER NOT NULL,
299
+ checksums TEXT NOT NULL,
300
+ http_metadata TEXT NOT NULL,
301
+ custom_metadata TEXT NOT NULL
302
+ );
303
+ CREATE TABLE IF NOT EXISTS _mf_multipart_uploads (
304
+ upload_id TEXT PRIMARY KEY,
305
+ key TEXT NOT NULL,
306
+ http_metadata TEXT NOT NULL,
307
+ custom_metadata TEXT NOT NULL,
308
+ state TINYINT DEFAULT 0 NOT NULL
309
+ );
310
+ CREATE TABLE IF NOT EXISTS _mf_multipart_parts (
311
+ upload_id TEXT NOT NULL REFERENCES _mf_multipart_uploads(upload_id),
312
+ part_number INTEGER NOT NULL,
313
+ blob_id TEXT NOT NULL,
314
+ size INTEGER NOT NULL,
315
+ etag TEXT NOT NULL,
316
+ checksum_md5 TEXT NOT NULL,
317
+ object_key TEXT REFERENCES _mf_objects(key) DEFERRABLE INITIALLY DEFERRED,
318
+ PRIMARY KEY (upload_id, part_number)
319
+ );
320
+ `, DateSchema = z.coerce.number().transform((value) => new Date(value)), RecordSchema = z.object({
321
+ k: z.string(),
322
+ v: z.string()
323
+ }).array().transform(
324
+ (entries) => Object.fromEntries(entries.map(({ k, v }) => [k, v]))
325
+ ), R2RangeSchema = z.object({
326
+ offset: z.coerce.number().optional(),
327
+ length: z.coerce.number().optional(),
328
+ suffix: z.coerce.number().optional()
329
+ }), R2EtagSchema = z.discriminatedUnion("type", [
330
+ z.object({ type: z.literal("strong"), value: z.string() }),
331
+ z.object({ type: z.literal("weak"), value: z.string() }),
332
+ z.object({ type: z.literal("wildcard") })
333
+ ]), R2EtagMatchSchema = R2EtagSchema.array().min(1).optional(), R2ConditionalSchema = z.object({
334
+ // Performs the operation if the object's ETag matches the given string
335
+ etagMatches: R2EtagMatchSchema,
336
+ // "If-Match"
337
+ // Performs the operation if the object's ETag does NOT match the given string
338
+ etagDoesNotMatch: R2EtagMatchSchema,
339
+ // "If-None-Match"
340
+ // Performs the operation if the object was uploaded BEFORE the given date
341
+ uploadedBefore: DateSchema.optional(),
342
+ // "If-Unmodified-Since"
343
+ // Performs the operation if the object was uploaded AFTER the given date
344
+ uploadedAfter: DateSchema.optional(),
345
+ // "If-Modified-Since"
346
+ // Truncates dates to seconds before performing comparisons
347
+ secondsGranularity: z.oboolean()
348
+ }), R2ChecksumsSchema = z.object({
349
+ 0: HexDataSchema.optional(),
350
+ 1: HexDataSchema.optional(),
351
+ 2: HexDataSchema.optional(),
352
+ 3: HexDataSchema.optional(),
353
+ 4: HexDataSchema.optional()
354
+ }).transform((checksums) => ({
355
+ md5: checksums[0],
356
+ sha1: checksums[1],
357
+ sha256: checksums[2],
358
+ sha384: checksums[3],
359
+ sha512: checksums[4]
360
+ })), R2PublishedPartSchema = z.object({
361
+ etag: z.string(),
362
+ part: z.number()
363
+ }), R2HttpFieldsSchema = z.object({
364
+ contentType: z.ostring(),
365
+ contentLanguage: z.ostring(),
366
+ contentDisposition: z.ostring(),
367
+ contentEncoding: z.ostring(),
368
+ cacheControl: z.ostring(),
369
+ cacheExpiry: z.coerce.number().optional()
370
+ }), R2HeadRequestSchema = z.object({
371
+ method: z.literal("head"),
372
+ object: z.string()
373
+ }), R2GetRequestSchema = z.object({
374
+ method: z.literal("get"),
375
+ object: z.string(),
376
+ // Specifies that only a specific length (from an optional offset) or suffix
377
+ // of bytes from the object should be returned. Refer to
378
+ // https://developers.cloudflare.com/r2/runtime-apis/#ranged-reads.
379
+ range: R2RangeSchema.optional(),
380
+ rangeHeader: z.ostring(),
381
+ // Specifies that the object should only be returned given satisfaction of
382
+ // certain conditions in the R2Conditional. Refer to R2Conditional above.
383
+ onlyIf: R2ConditionalSchema.optional()
384
+ }), R2PutRequestSchema = z.object({
385
+ method: z.literal("put"),
386
+ object: z.string(),
387
+ customFields: RecordSchema.optional(),
388
+ // (renamed in transform)
389
+ httpFields: R2HttpFieldsSchema.optional(),
390
+ // (renamed in transform)
391
+ onlyIf: R2ConditionalSchema.optional(),
392
+ md5: Base64DataSchema.optional(),
393
+ // (intentionally base64, not hex)
394
+ sha1: HexDataSchema.optional(),
395
+ sha256: HexDataSchema.optional(),
396
+ sha384: HexDataSchema.optional(),
397
+ sha512: HexDataSchema.optional()
398
+ }).transform((value) => ({
399
+ method: value.method,
400
+ object: value.object,
401
+ customMetadata: value.customFields,
402
+ httpMetadata: value.httpFields,
403
+ onlyIf: value.onlyIf,
404
+ md5: value.md5,
405
+ sha1: value.sha1,
406
+ sha256: value.sha256,
407
+ sha384: value.sha384,
408
+ sha512: value.sha512
409
+ })), R2CreateMultipartUploadRequestSchema = z.object({
410
+ method: z.literal("createMultipartUpload"),
411
+ object: z.string(),
412
+ customFields: RecordSchema.optional(),
413
+ // (renamed in transform)
414
+ httpFields: R2HttpFieldsSchema.optional()
415
+ // (renamed in transform)
416
+ }).transform((value) => ({
417
+ method: value.method,
418
+ object: value.object,
419
+ customMetadata: value.customFields,
420
+ httpMetadata: value.httpFields
421
+ })), R2UploadPartRequestSchema = z.object({
422
+ method: z.literal("uploadPart"),
423
+ object: z.string(),
424
+ uploadId: z.string(),
425
+ partNumber: z.number()
426
+ }), R2CompleteMultipartUploadRequestSchema = z.object({
427
+ method: z.literal("completeMultipartUpload"),
428
+ object: z.string(),
429
+ uploadId: z.string(),
430
+ parts: R2PublishedPartSchema.array()
431
+ }), R2AbortMultipartUploadRequestSchema = z.object({
432
+ method: z.literal("abortMultipartUpload"),
433
+ object: z.string(),
434
+ uploadId: z.string()
435
+ }), R2ListRequestSchema = z.object({
436
+ method: z.literal("list"),
437
+ limit: z.onumber(),
438
+ prefix: z.ostring(),
439
+ cursor: z.ostring(),
440
+ delimiter: z.ostring(),
441
+ startAfter: z.ostring(),
442
+ include: z.union([z.literal(0), z.literal(1)]).transform((value) => value === 0 ? "httpMetadata" : "customMetadata").array().optional()
443
+ }), R2DeleteRequestSchema = z.intersection(
444
+ z.object({ method: z.literal("delete") }),
445
+ z.union([
446
+ z.object({ object: z.string() }),
447
+ z.object({ objects: z.string().array() })
448
+ ])
449
+ ), R2BindingRequestSchema = z.union([
450
+ R2HeadRequestSchema,
451
+ R2GetRequestSchema,
452
+ R2PutRequestSchema,
453
+ R2CreateMultipartUploadRequestSchema,
454
+ R2UploadPartRequestSchema,
455
+ R2CompleteMultipartUploadRequestSchema,
456
+ R2AbortMultipartUploadRequestSchema,
457
+ R2ListRequestSchema,
458
+ R2DeleteRequestSchema
459
+ ]);
460
+
461
+ // src/workers/r2/validator.worker.ts
462
+ import assert from "node:assert";
463
+ import { Buffer as Buffer2 } from "node:buffer";
464
+ import { parseRanges } from "miniflare:shared";
465
+ function identity(ms) {
466
+ return ms;
467
+ }
468
+ function truncateToSeconds(ms) {
469
+ return Math.floor(ms / 1e3) * 1e3;
470
+ }
471
+ function includesEtag(conditions, etag, comparison) {
472
+ for (let condition of conditions)
473
+ if (condition.type === "wildcard" || condition.value === etag && (condition.type === "strong" || comparison === "weak"))
474
+ return !0;
475
+ return !1;
476
+ }
477
+ function _testR2Conditional(cond, metadata) {
478
+ if (metadata === void 0) {
479
+ let ifMatch2 = cond.etagMatches === void 0, ifModifiedSince2 = cond.uploadedAfter === void 0;
480
+ return ifMatch2 && ifModifiedSince2;
481
+ }
482
+ let { etag, uploaded: lastModifiedRaw } = metadata, ifMatch = cond.etagMatches === void 0 || includesEtag(cond.etagMatches, etag, "strong"), ifNoneMatch = cond.etagDoesNotMatch === void 0 || !includesEtag(cond.etagDoesNotMatch, etag, "weak"), maybeTruncate = cond.secondsGranularity ? truncateToSeconds : identity, lastModified = maybeTruncate(lastModifiedRaw), ifModifiedSince = cond.uploadedAfter === void 0 || maybeTruncate(cond.uploadedAfter.getTime()) < lastModified || cond.etagDoesNotMatch !== void 0 && ifNoneMatch, ifUnmodifiedSince = cond.uploadedBefore === void 0 || lastModified < maybeTruncate(cond.uploadedBefore.getTime()) || cond.etagMatches !== void 0 && ifMatch;
483
+ return ifMatch && ifNoneMatch && ifModifiedSince && ifUnmodifiedSince;
484
+ }
485
+ var R2_HASH_ALGORITHMS = [
486
+ { name: "MD5", field: "md5" },
487
+ { name: "SHA-1", field: "sha1" },
488
+ { name: "SHA-256", field: "sha256" },
489
+ { name: "SHA-384", field: "sha384" },
490
+ { name: "SHA-512", field: "sha512" }
491
+ ];
492
+ function serialisedLength(x) {
493
+ for (let i = 0; i < x.length; i++)
494
+ if (x.charCodeAt(i) >= 256) return x.length * 2;
495
+ return x.length;
496
+ }
497
+ var Validator = class {
498
+ hash(digests, hashes) {
499
+ let checksums = {};
500
+ for (let { name, field } of R2_HASH_ALGORITHMS) {
501
+ let providedHash = hashes[field];
502
+ if (providedHash !== void 0) {
503
+ let computedHash = digests.get(name);
504
+ if (assert(computedHash !== void 0), !providedHash.equals(computedHash))
505
+ throw new BadDigest(name, providedHash, computedHash);
506
+ checksums[field] = computedHash.toString("hex");
507
+ }
508
+ }
509
+ return checksums;
510
+ }
511
+ condition(meta, onlyIf) {
512
+ if (onlyIf !== void 0 && !_testR2Conditional(onlyIf, meta))
513
+ throw new PreconditionFailed();
514
+ return this;
515
+ }
516
+ range(options, size) {
517
+ if (options.rangeHeader !== void 0) {
518
+ let ranges = parseRanges(options.rangeHeader, size);
519
+ if (ranges?.length === 1) return ranges[0];
520
+ } else if (options.range !== void 0) {
521
+ let { offset, length, suffix } = options.range;
522
+ if (suffix !== void 0) {
523
+ if (suffix <= 0) throw new InvalidRange();
524
+ suffix > size && (suffix = size), offset = size - suffix, length = suffix;
525
+ }
526
+ if (offset === void 0 && (offset = 0), length === void 0 && (length = size - offset), offset < 0 || offset > size || length <= 0) throw new InvalidRange();
527
+ return offset + length > size && (length = size - offset), { start: offset, end: offset + length - 1 };
528
+ }
529
+ }
530
+ size(size) {
531
+ if (size > R2Limits.MAX_VALUE_SIZE)
532
+ throw new EntityTooLarge();
533
+ return this;
534
+ }
535
+ metadataSize(customMetadata) {
536
+ if (customMetadata === void 0) return this;
537
+ let metadataLength = 0;
538
+ for (let [key, value] of Object.entries(customMetadata))
539
+ metadataLength += serialisedLength(key) + serialisedLength(value);
540
+ if (metadataLength > R2Limits.MAX_METADATA_SIZE)
541
+ throw new MetadataTooLarge();
542
+ return this;
543
+ }
544
+ key(key) {
545
+ if (Buffer2.byteLength(key) > R2Limits.MAX_KEY_SIZE)
546
+ throw new InvalidObjectName();
547
+ return this;
548
+ }
549
+ limit(limit) {
550
+ if (limit !== void 0 && (limit < 1 || limit > R2Limits.MAX_LIST_KEYS))
551
+ throw new InvalidMaxKeys();
552
+ return this;
553
+ }
554
+ };
555
+
556
+ // src/workers/r2/bucket.worker.ts
557
+ var DigestingStream = class extends TransformStream {
558
+ digests;
559
+ constructor(algorithms) {
560
+ let digests = new DeferredPromise(), hashes = algorithms.map((alg) => {
561
+ let stream = new crypto.DigestStream(alg), writer = stream.getWriter();
562
+ return { stream, writer };
563
+ });
564
+ super({
565
+ async transform(chunk, controller) {
566
+ for (let hash of hashes) await hash.writer.write(chunk);
567
+ controller.enqueue(chunk);
568
+ },
569
+ async flush() {
570
+ let result = /* @__PURE__ */ new Map();
571
+ for (let i = 0; i < hashes.length; i++)
572
+ await hashes[i].writer.close(), result.set(algorithms[i], Buffer3.from(await hashes[i].stream.digest));
573
+ digests.resolve(result);
574
+ }
575
+ }), this.digests = digests;
576
+ }
577
+ }, validate = new Validator(), decoder = new TextDecoder();
578
+ function generateVersion() {
579
+ return Buffer3.from(crypto.getRandomValues(new Uint8Array(16))).toString(
580
+ "hex"
581
+ );
582
+ }
583
+ function generateId() {
584
+ return Buffer3.from(crypto.getRandomValues(new Uint8Array(128))).toString(
585
+ "base64url"
586
+ );
587
+ }
588
+ function generateMultipartEtag(md5Hexes) {
589
+ let hash = createHash("md5");
590
+ for (let md5Hex of md5Hexes) hash.update(md5Hex, "hex");
591
+ return `${hash.digest("hex")}-${md5Hexes.length}`;
592
+ }
593
+ function rangeOverlaps(a, b) {
594
+ return a.start <= b.end && b.start <= a.end;
595
+ }
596
+ async function decodeMetadata(req) {
597
+ let metadataSize = parseInt(req.headers.get(R2Headers.METADATA_SIZE));
598
+ if (Number.isNaN(metadataSize)) throw new InvalidMetadata();
599
+ assert2(req.body !== null);
600
+ let body = req.body, [metadataBuffer, value] = await readPrefix(body, metadataSize), metadataJson = decoder.decode(metadataBuffer);
601
+ return { metadata: R2BindingRequestSchema.parse(JSON.parse(metadataJson)), metadataSize, value };
602
+ }
603
+ function decodeHeaderMetadata(req) {
604
+ let header = req.headers.get(R2Headers.REQUEST);
605
+ if (header === null) throw new InvalidMetadata();
606
+ return R2BindingRequestSchema.parse(JSON.parse(header));
607
+ }
608
+ function encodeResult(result) {
609
+ let encoded;
610
+ return result instanceof InternalR2Object ? encoded = result.encode() : encoded = InternalR2Object.encodeMultiple(result), new Response(encoded.value, {
611
+ headers: {
612
+ [R2Headers.METADATA_SIZE]: `${encoded.metadataSize}`,
613
+ "Content-Type": "application/json",
614
+ "Content-Length": `${encoded.size}`
615
+ }
616
+ });
617
+ }
618
+ function encodeJSONResult(result) {
619
+ let encoded = JSON.stringify(result);
620
+ return new Response(encoded, {
621
+ headers: {
622
+ [R2Headers.METADATA_SIZE]: `${Buffer3.byteLength(encoded)}`,
623
+ "Content-Type": "application/json"
624
+ }
625
+ });
626
+ }
627
+ function sqlStmts(db) {
628
+ let stmtGetPreviousByKey = db.stmt("SELECT blob_id, etag, uploaded FROM _mf_objects WHERE key = :key"), stmtGetByKey = db.stmt(`
629
+ SELECT key, blob_id, version, size, etag, uploaded, checksums, http_metadata, custom_metadata
630
+ FROM _mf_objects WHERE key = :key
631
+ `), stmtPut = db.stmt(`
632
+ INSERT OR REPLACE INTO _mf_objects (key, blob_id, version, size, etag, uploaded, checksums, http_metadata, custom_metadata)
633
+ VALUES (:key, :blob_id, :version, :size, :etag, :uploaded, :checksums, :http_metadata, :custom_metadata)
634
+ `), stmtDelete = db.stmt("DELETE FROM _mf_objects WHERE key = :key RETURNING blob_id");
635
+ function stmtListWithoutDelimiter(...extraColumns) {
636
+ let columns = [
637
+ "key",
638
+ "version",
639
+ "size",
640
+ "etag",
641
+ "uploaded",
642
+ "checksums",
643
+ ...extraColumns
644
+ ];
645
+ return db.stmt(`
646
+ SELECT ${columns.join(", ")}
647
+ FROM _mf_objects
648
+ WHERE substr(key, 1, length(:prefix)) = :prefix
649
+ AND (:start_after IS NULL OR key > :start_after)
650
+ ORDER BY key LIMIT :limit
651
+ `);
652
+ }
653
+ let stmtGetUploadState = db.stmt(
654
+ // For checking current upload state
655
+ "SELECT state FROM _mf_multipart_uploads WHERE upload_id = :upload_id AND key = :key"
656
+ ), stmtGetUploadMetadata = db.stmt(
657
+ // For checking current upload state, and getting metadata for completion
658
+ "SELECT http_metadata, custom_metadata, state FROM _mf_multipart_uploads WHERE upload_id = :upload_id AND key = :key"
659
+ ), stmtUpdateUploadState = db.stmt(
660
+ // For completing/aborting uploads
661
+ "UPDATE _mf_multipart_uploads SET state = :state WHERE upload_id = :upload_id"
662
+ ), stmtGetPreviousPartByNumber = db.stmt(
663
+ // For getting part number's previous blob ID to garbage collect
664
+ "SELECT blob_id FROM _mf_multipart_parts WHERE upload_id = :upload_id AND part_number = :part_number"
665
+ ), stmtPutPart = db.stmt(
666
+ // For recording metadata when uploading parts
667
+ `INSERT OR REPLACE INTO _mf_multipart_parts (upload_id, part_number, blob_id, size, etag, checksum_md5)
668
+ VALUES (:upload_id, :part_number, :blob_id, :size, :etag, :checksum_md5)`
669
+ ), stmtLinkPart = db.stmt(
670
+ // For linking parts with an object when completing uploads
671
+ `UPDATE _mf_multipart_parts SET object_key = :object_key
672
+ WHERE upload_id = :upload_id AND part_number = :part_number`
673
+ ), stmtDeletePartsByUploadId = db.stmt(
674
+ // For deleting parts when aborting uploads
675
+ "DELETE FROM _mf_multipart_parts WHERE upload_id = :upload_id RETURNING blob_id"
676
+ ), stmtDeleteUnlinkedPartsByUploadId = db.stmt(
677
+ // For deleting unused parts when completing uploads
678
+ "DELETE FROM _mf_multipart_parts WHERE upload_id = :upload_id AND object_key IS NULL RETURNING blob_id"
679
+ ), stmtDeletePartsByKey = db.stmt(
680
+ // For deleting dangling parts when overwriting an existing key
681
+ "DELETE FROM _mf_multipart_parts WHERE object_key = :object_key RETURNING blob_id"
682
+ ), stmtListPartsByUploadId = db.stmt(
683
+ // For getting part metadata when completing uploads
684
+ `SELECT upload_id, part_number, blob_id, size, etag, checksum_md5, object_key
685
+ FROM _mf_multipart_parts WHERE upload_id = :upload_id`
686
+ ), stmtListPartsByKey = db.stmt(
687
+ // For getting part metadata when getting values, size included for range
688
+ // requests, so we only need to read blobs containing the required data
689
+ "SELECT blob_id, size FROM _mf_multipart_parts WHERE object_key = :object_key ORDER BY part_number"
690
+ );
691
+ return {
692
+ getByKey: stmtGetByKey,
693
+ getPartsByKey: db.txn((key) => {
694
+ let row = get(stmtGetByKey({ key }));
695
+ if (row !== void 0)
696
+ if (row.blob_id === null) {
697
+ let partsRows = all(stmtListPartsByKey({ object_key: key }));
698
+ return { row, parts: partsRows };
699
+ } else
700
+ return { row };
701
+ }),
702
+ put: db.txn((newRow, onlyIf) => {
703
+ let key = newRow.key, row = get(stmtGetPreviousByKey({ key }));
704
+ onlyIf !== void 0 && validate.condition(row, onlyIf), stmtPut(newRow);
705
+ let maybeOldBlobId = row?.blob_id;
706
+ return maybeOldBlobId === void 0 ? [] : maybeOldBlobId === null ? all(stmtDeletePartsByKey({ object_key: key })).map(({ blob_id }) => blob_id) : [maybeOldBlobId];
707
+ }),
708
+ deleteByKeys: db.txn((keys) => {
709
+ let oldBlobIds = [];
710
+ for (let key of keys) {
711
+ let maybeOldBlobId = get(stmtDelete({ key }))?.blob_id;
712
+ if (maybeOldBlobId === null) {
713
+ let partRows = stmtDeletePartsByKey({ object_key: key });
714
+ for (let partRow of partRows) oldBlobIds.push(partRow.blob_id);
715
+ } else maybeOldBlobId !== void 0 && oldBlobIds.push(maybeOldBlobId);
716
+ }
717
+ return oldBlobIds;
718
+ }),
719
+ listWithoutDelimiter: stmtListWithoutDelimiter(),
720
+ listHttpMetadataWithoutDelimiter: stmtListWithoutDelimiter("http_metadata"),
721
+ listCustomMetadataWithoutDelimiter: stmtListWithoutDelimiter("custom_metadata"),
722
+ listHttpCustomMetadataWithoutDelimiter: stmtListWithoutDelimiter(
723
+ "http_metadata",
724
+ "custom_metadata"
725
+ ),
726
+ listMetadata: db.stmt(`
727
+ SELECT
728
+ -- When grouping by a delimited prefix, this will give us the last key with that prefix.
729
+ -- NOTE: we'll use this for the next cursor. If we didn't return the last key, the next page may return the
730
+ -- same delimited prefix. Essentially, we're skipping over all keys with this group's delimited prefix.
731
+ -- When grouping by a key, this will just give us the key.
732
+ max(key) AS last_key,
733
+ iif(
734
+ -- Try get 1-indexed position \`i\` of :delimiter in rest of key after :prefix...
735
+ instr(substr(key, length(:prefix) + 1), :delimiter),
736
+ -- ...if found, we have a delimited prefix of the :prefix followed by the rest of key up to and including the :delimiter
737
+ 'dlp:' || substr(key, 1, length(:prefix) + instr(substr(key, length(:prefix) + 1), :delimiter) + length(:delimiter) - 1),
738
+ -- ...otherwise, we just have a regular key
739
+ 'key:' || key
740
+ ) AS delimited_prefix_or_key,
741
+ -- NOTE: we'll ignore metadata for delimited prefix rows, so it doesn't matter which keys' we return
742
+ version, size, etag, uploaded, checksums, http_metadata, custom_metadata
743
+ FROM _mf_objects
744
+ WHERE substr(key, 1, length(:prefix)) = :prefix
745
+ AND (:start_after IS NULL OR key > :start_after)
746
+ GROUP BY delimited_prefix_or_key -- Group keys with same delimited prefix into a row, leaving others in their own rows
747
+ ORDER BY last_key LIMIT :limit;
748
+ `),
749
+ createMultipartUpload: db.stmt(`
750
+ INSERT INTO _mf_multipart_uploads (upload_id, key, http_metadata, custom_metadata)
751
+ VALUES (:upload_id, :key, :http_metadata, :custom_metadata)
752
+ `),
753
+ putPart: db.txn(
754
+ (key, newRow) => {
755
+ if (get(
756
+ stmtGetUploadState({
757
+ key,
758
+ upload_id: newRow.upload_id
759
+ })
760
+ )?.state !== MultipartUploadState.IN_PROGRESS)
761
+ throw new NoSuchUpload();
762
+ let partRow = get(
763
+ stmtGetPreviousPartByNumber({
764
+ upload_id: newRow.upload_id,
765
+ part_number: newRow.part_number
766
+ })
767
+ );
768
+ return stmtPutPart(newRow), partRow?.blob_id;
769
+ }
770
+ ),
771
+ completeMultipartUpload: db.txn(
772
+ (key, upload_id, selectedParts, minPartSize) => {
773
+ let uploadRow = get(stmtGetUploadMetadata({ key, upload_id }));
774
+ if (uploadRow === void 0)
775
+ throw new InternalError();
776
+ if (uploadRow.state > MultipartUploadState.IN_PROGRESS)
777
+ throw new NoSuchUpload();
778
+ let partNumberSet = /* @__PURE__ */ new Set();
779
+ for (let { part } of selectedParts) {
780
+ if (partNumberSet.has(part)) throw new InternalError();
781
+ partNumberSet.add(part);
782
+ }
783
+ let uploadedPartRows = stmtListPartsByUploadId({ upload_id }), uploadedParts = /* @__PURE__ */ new Map();
784
+ for (let row of uploadedPartRows)
785
+ uploadedParts.set(row.part_number, row);
786
+ let parts = selectedParts.map((selectedPart) => {
787
+ let uploadedPart = uploadedParts.get(selectedPart.part);
788
+ if (uploadedPart?.etag !== selectedPart.etag)
789
+ throw new InvalidPart();
790
+ return uploadedPart;
791
+ });
792
+ for (let part of parts.slice(0, -1))
793
+ if (part.size < minPartSize)
794
+ throw new EntityTooSmall();
795
+ parts.sort((a, b) => a.part_number - b.part_number);
796
+ let partSize;
797
+ for (let part of parts.slice(0, -1))
798
+ if (partSize ??= part.size, part.size < minPartSize || part.size !== partSize)
799
+ throw new BadUpload();
800
+ if (partSize !== void 0 && parts[parts.length - 1].size > partSize)
801
+ throw new BadUpload();
802
+ let oldBlobIds = [], maybeOldBlobId = get(stmtGetPreviousByKey({ key }))?.blob_id;
803
+ if (maybeOldBlobId === null) {
804
+ let partRows2 = stmtDeletePartsByKey({ object_key: key });
805
+ for (let partRow of partRows2) oldBlobIds.push(partRow.blob_id);
806
+ } else maybeOldBlobId !== void 0 && oldBlobIds.push(maybeOldBlobId);
807
+ let totalSize = parts.reduce((acc, { size }) => acc + size, 0), etag = generateMultipartEtag(
808
+ parts.map(({ checksum_md5 }) => checksum_md5)
809
+ ), newRow = {
810
+ key,
811
+ blob_id: null,
812
+ version: generateVersion(),
813
+ size: totalSize,
814
+ etag,
815
+ uploaded: Date.now(),
816
+ checksums: "{}",
817
+ http_metadata: uploadRow.http_metadata,
818
+ custom_metadata: uploadRow.custom_metadata
819
+ };
820
+ stmtPut(newRow);
821
+ for (let part of parts)
822
+ stmtLinkPart({
823
+ upload_id,
824
+ part_number: part.part_number,
825
+ object_key: key
826
+ });
827
+ let partRows = stmtDeleteUnlinkedPartsByUploadId({ upload_id });
828
+ for (let partRow of partRows) oldBlobIds.push(partRow.blob_id);
829
+ return stmtUpdateUploadState({
830
+ upload_id,
831
+ state: MultipartUploadState.COMPLETED
832
+ }), { newRow, oldBlobIds };
833
+ }
834
+ ),
835
+ abortMultipartUpload: db.txn((key, upload_id) => {
836
+ let uploadRow = get(stmtGetUploadState({ key, upload_id }));
837
+ if (uploadRow === void 0)
838
+ throw new InternalError();
839
+ if (uploadRow.state > MultipartUploadState.IN_PROGRESS)
840
+ return [];
841
+ let oldBlobIds = all(stmtDeletePartsByUploadId({ upload_id })).map(({ blob_id }) => blob_id);
842
+ return stmtUpdateUploadState({
843
+ upload_id,
844
+ state: MultipartUploadState.ABORTED
845
+ }), oldBlobIds;
846
+ })
847
+ };
848
+ }
849
+ var R2BucketObject = class extends MiniflareDurableObject {
850
+ #stmts;
851
+ // Multipart uploads are stored as multiple blobs. Therefore, when reading a
852
+ // multipart upload, we'll be reading multiple blobs. When an object is
853
+ // deleted, all its blobs are deleted in the background.
854
+ //
855
+ // Normally for single part objects, this is fine, since we'd open a handle to
856
+ // a single blob, which we'd have until we closed it, at which point the blob
857
+ // may be deleted. With multipart, we don't want to open handles for all blobs
858
+ // as we could hit open file descriptor limits. Similarly, we don't want to
859
+ // read all blobs first, as we'd have to buffer them.
860
+ //
861
+ // Instead, we set up in-process locking on blobs needed for multipart reads.
862
+ // When we start a multipart read, we acquire all the blobs we need, then
863
+ // release them as we've streamed each part. Multiple multipart reads may be
864
+ // in-progress at any given time, so we use a wait group.
865
+ //
866
+ // This assumes we only ever have a single Miniflare instance operating on a
867
+ // blob store, which is always true for in-memory stores, and usually true for
868
+ // on-disk ones. If we really wanted to do this properly, we could store the
869
+ // bookkeeping for the wait group in SQLite, but then we'd have to implement
870
+ // some inter-process signalling/subscription system.
871
+ #inUseBlobs = /* @__PURE__ */ new Map();
872
+ constructor(state, env) {
873
+ super(state, env), this.db.exec("PRAGMA case_sensitive_like = TRUE"), this.db.exec(SQL_SCHEMA), this.#stmts = sqlStmts(this.db);
874
+ }
875
+ #acquireBlob(blobId) {
876
+ let waitGroup = this.#inUseBlobs.get(blobId);
877
+ waitGroup === void 0 ? (waitGroup = new WaitGroup(), this.#inUseBlobs.set(blobId, waitGroup), waitGroup.add(), waitGroup.wait().then(() => this.#inUseBlobs.delete(blobId))) : waitGroup.add();
878
+ }
879
+ #releaseBlob(blobId) {
880
+ this.#inUseBlobs.get(blobId)?.done();
881
+ }
882
+ #backgroundDelete(blobId) {
883
+ this.timers.queueMicrotask(async () => (await this.#inUseBlobs.get(blobId)?.wait(), this.blob.delete(blobId).catch((e) => {
884
+ console.error("R2BucketObject##backgroundDelete():", e);
885
+ })));
886
+ }
887
+ #assembleMultipartValue(parts, queryRange) {
888
+ let requiredParts = [], start = 0;
889
+ for (let part of parts) {
890
+ let partRange = { start, end: start + part.size - 1 };
891
+ if (rangeOverlaps(partRange, queryRange)) {
892
+ let range = {
893
+ start: Math.max(partRange.start, queryRange.start) - partRange.start,
894
+ end: Math.min(partRange.end, queryRange.end) - partRange.start
895
+ };
896
+ this.#acquireBlob(part.blob_id), requiredParts.push({ blobId: part.blob_id, range });
897
+ }
898
+ start = partRange.end + 1;
899
+ }
900
+ let identity2 = new TransformStream();
901
+ return (async () => {
902
+ let i = 0;
903
+ try {
904
+ for (; i < requiredParts.length; i++) {
905
+ let { blobId, range } = requiredParts[i], value = await this.blob.get(blobId, range), msg = `Expected to find blob "${blobId}" for multipart value`;
906
+ assert2(value !== null, msg), await value.pipeTo(identity2.writable, { preventClose: !0 }), this.#releaseBlob(blobId);
907
+ }
908
+ await identity2.writable.close();
909
+ } catch (e) {
910
+ await identity2.writable.abort(e);
911
+ } finally {
912
+ for (; i < requiredParts.length; i++)
913
+ this.#releaseBlob(requiredParts[i].blobId);
914
+ }
915
+ })(), identity2.readable;
916
+ }
917
+ async #head(key) {
918
+ validate.key(key);
919
+ let row = get(this.#stmts.getByKey({ key }));
920
+ if (row === void 0) throw new NoSuchKey();
921
+ let range = { offset: 0, length: row.size };
922
+ return new InternalR2Object(row, range);
923
+ }
924
+ async #get(key, opts) {
925
+ validate.key(key);
926
+ let result = this.#stmts.getPartsByKey(key);
927
+ if (result === void 0) throw new NoSuchKey();
928
+ let { row, parts } = result, defaultR2Range = { offset: 0, length: row.size };
929
+ try {
930
+ validate.condition(row, opts.onlyIf);
931
+ } catch (e) {
932
+ throw e instanceof PreconditionFailed && e.attach(new InternalR2Object(row, defaultR2Range)), e;
933
+ }
934
+ let range = validate.range(opts, row.size), r2Range;
935
+ if (range === void 0)
936
+ r2Range = defaultR2Range;
937
+ else {
938
+ let start = range.start, end = Math.min(range.end, row.size);
939
+ r2Range = { offset: start, length: end - start + 1 };
940
+ }
941
+ let value;
942
+ if (row.blob_id === null) {
943
+ assert2(parts !== void 0);
944
+ let defaultRange = { start: 0, end: row.size - 1 };
945
+ value = this.#assembleMultipartValue(parts, range ?? defaultRange);
946
+ } else if (value = await this.blob.get(row.blob_id, range), value === null) throw new NoSuchKey();
947
+ return new InternalR2ObjectBody(row, value, r2Range);
948
+ }
949
+ async #put(key, value, valueSize, opts) {
950
+ let algorithms = [];
951
+ for (let { name, field } of R2_HASH_ALGORITHMS)
952
+ (field === "md5" || opts[field] !== void 0) && algorithms.push(name);
953
+ let digesting = new DigestingStream(algorithms), blobId = await this.blob.put(value.pipeThrough(digesting)), digests = await digesting.digests, md5Digest = digests.get("MD5");
954
+ assert2(md5Digest !== void 0);
955
+ let md5DigestHex = md5Digest.toString("hex"), checksums = validate.key(key).size(valueSize).metadataSize(opts.customMetadata).hash(digests, opts), row = {
956
+ key,
957
+ blob_id: blobId,
958
+ version: generateVersion(),
959
+ size: valueSize,
960
+ etag: md5DigestHex,
961
+ uploaded: Date.now(),
962
+ checksums: JSON.stringify(checksums),
963
+ http_metadata: JSON.stringify(opts.httpMetadata ?? {}),
964
+ custom_metadata: JSON.stringify(opts.customMetadata ?? {})
965
+ }, oldBlobIds;
966
+ try {
967
+ oldBlobIds = this.#stmts.put(row, opts.onlyIf);
968
+ } catch (e) {
969
+ throw this.#backgroundDelete(blobId), e;
970
+ }
971
+ if (oldBlobIds !== void 0)
972
+ for (let blobId2 of oldBlobIds) this.#backgroundDelete(blobId2);
973
+ return new InternalR2Object(row);
974
+ }
975
+ #delete(keys) {
976
+ Array.isArray(keys) || (keys = [keys]);
977
+ for (let key of keys) validate.key(key);
978
+ let oldBlobIds = this.#stmts.deleteByKeys(keys);
979
+ for (let blobId of oldBlobIds) this.#backgroundDelete(blobId);
980
+ }
981
+ #listWithoutDelimiterQuery(excludeHttp, excludeCustom) {
982
+ return excludeHttp && excludeCustom ? this.#stmts.listWithoutDelimiter : excludeHttp ? this.#stmts.listCustomMetadataWithoutDelimiter : excludeCustom ? this.#stmts.listHttpMetadataWithoutDelimiter : this.#stmts.listHttpCustomMetadataWithoutDelimiter;
983
+ }
984
+ async #list(opts) {
985
+ let prefix = opts.prefix ?? "", limit = opts.limit ?? R2Limits.MAX_LIST_KEYS;
986
+ validate.limit(limit);
987
+ let include = opts.include ?? [];
988
+ include.length > 0 && (limit = Math.min(limit, 100));
989
+ let excludeHttp = !include.includes("httpMetadata"), excludeCustom = !include.includes("customMetadata"), rowObject = (row) => ((row.http_metadata === void 0 || excludeHttp) && (row.http_metadata = "{}"), (row.custom_metadata === void 0 || excludeCustom) && (row.custom_metadata = "{}"), new InternalR2Object(row)), startAfter = opts.startAfter;
990
+ if (opts.cursor !== void 0) {
991
+ let cursorStartAfter = base64Decode(opts.cursor);
992
+ (startAfter === void 0 || cursorStartAfter > startAfter) && (startAfter = cursorStartAfter);
993
+ }
994
+ let delimiter = opts.delimiter;
995
+ delimiter === "" && (delimiter = void 0);
996
+ let params = {
997
+ prefix,
998
+ start_after: startAfter ?? null,
999
+ // Increase the queried limit by 1, if we return this many results, we
1000
+ // know there are more rows. We'll truncate to the original limit before
1001
+ // returning results.
1002
+ limit: limit + 1
1003
+ }, objects, delimitedPrefixes = [], nextCursorStartAfter;
1004
+ if (delimiter !== void 0) {
1005
+ let rows = all(this.#stmts.listMetadata({ ...params, delimiter })), hasMoreRows = rows.length === limit + 1;
1006
+ rows.splice(limit, 1), objects = [];
1007
+ for (let row of rows)
1008
+ row.delimited_prefix_or_key.startsWith("dlp:") ? delimitedPrefixes.push(row.delimited_prefix_or_key.substring(4)) : objects.push(rowObject({ ...row, key: row.last_key }));
1009
+ hasMoreRows && (nextCursorStartAfter = rows[limit - 1].last_key);
1010
+ } else {
1011
+ let query = this.#listWithoutDelimiterQuery(excludeHttp, excludeCustom), rows = all(query(params)), hasMoreRows = rows.length === limit + 1;
1012
+ rows.splice(limit, 1), objects = rows.map(rowObject), hasMoreRows && (nextCursorStartAfter = rows[limit - 1].key);
1013
+ }
1014
+ let nextCursor = maybeApply(base64Encode, nextCursorStartAfter);
1015
+ return {
1016
+ objects,
1017
+ truncated: nextCursor !== void 0,
1018
+ cursor: nextCursor,
1019
+ delimitedPrefixes
1020
+ };
1021
+ }
1022
+ async #createMultipartUpload(key, opts) {
1023
+ validate.key(key);
1024
+ let uploadId = generateId();
1025
+ return this.#stmts.createMultipartUpload({
1026
+ key,
1027
+ upload_id: uploadId,
1028
+ http_metadata: JSON.stringify(opts.httpMetadata ?? {}),
1029
+ custom_metadata: JSON.stringify(opts.customMetadata ?? {})
1030
+ }), { uploadId };
1031
+ }
1032
+ async #uploadPart(key, uploadId, partNumber, value, valueSize) {
1033
+ validate.key(key);
1034
+ let digesting = new DigestingStream(["MD5"]), blobId = await this.blob.put(value.pipeThrough(digesting)), md5Digest = (await digesting.digests).get("MD5");
1035
+ assert2(md5Digest !== void 0);
1036
+ let etag = generateId(), maybeOldBlobId;
1037
+ try {
1038
+ maybeOldBlobId = this.#stmts.putPart(key, {
1039
+ upload_id: uploadId,
1040
+ part_number: partNumber,
1041
+ blob_id: blobId,
1042
+ size: valueSize,
1043
+ etag,
1044
+ checksum_md5: md5Digest.toString("hex")
1045
+ });
1046
+ } catch (e) {
1047
+ throw this.#backgroundDelete(blobId), e;
1048
+ }
1049
+ return maybeOldBlobId !== void 0 && this.#backgroundDelete(maybeOldBlobId), { etag };
1050
+ }
1051
+ async #completeMultipartUpload(key, uploadId, parts) {
1052
+ validate.key(key);
1053
+ let minPartSize = this.beingTested ? R2Limits.MIN_MULTIPART_PART_SIZE_TEST : R2Limits.MIN_MULTIPART_PART_SIZE, { newRow, oldBlobIds } = this.#stmts.completeMultipartUpload(
1054
+ key,
1055
+ uploadId,
1056
+ parts,
1057
+ minPartSize
1058
+ );
1059
+ for (let blobId of oldBlobIds) this.#backgroundDelete(blobId);
1060
+ return new InternalR2Object(newRow);
1061
+ }
1062
+ async #abortMultipartUpload(key, uploadId) {
1063
+ validate.key(key);
1064
+ let oldBlobIds = this.#stmts.abortMultipartUpload(key, uploadId);
1065
+ for (let blobId of oldBlobIds) this.#backgroundDelete(blobId);
1066
+ }
1067
+ get = async (req) => {
1068
+ let metadata = decodeHeaderMetadata(req), result;
1069
+ if (metadata.method === "head")
1070
+ result = await this.#head(metadata.object);
1071
+ else if (metadata.method === "get")
1072
+ result = await this.#get(metadata.object, metadata);
1073
+ else if (metadata.method === "list")
1074
+ result = await this.#list(metadata);
1075
+ else
1076
+ throw new InternalError();
1077
+ return encodeResult(result);
1078
+ };
1079
+ put = async (req) => {
1080
+ let { metadata, metadataSize, value } = await decodeMetadata(req);
1081
+ if (metadata.method === "delete")
1082
+ return await this.#delete(
1083
+ "object" in metadata ? metadata.object : metadata.objects
1084
+ ), new Response();
1085
+ if (metadata.method === "put") {
1086
+ let contentLength = parseInt(req.headers.get("Content-Length"));
1087
+ assert2(!isNaN(contentLength));
1088
+ let valueSize = contentLength - metadataSize, result = await this.#put(
1089
+ metadata.object,
1090
+ value,
1091
+ valueSize,
1092
+ metadata
1093
+ );
1094
+ return encodeResult(result);
1095
+ } else if (metadata.method === "createMultipartUpload") {
1096
+ let result = await this.#createMultipartUpload(
1097
+ metadata.object,
1098
+ metadata
1099
+ );
1100
+ return encodeJSONResult(result);
1101
+ } else if (metadata.method === "uploadPart") {
1102
+ let contentLength = parseInt(req.headers.get("Content-Length"));
1103
+ assert2(!isNaN(contentLength));
1104
+ let valueSize = contentLength - metadataSize, result = await this.#uploadPart(
1105
+ metadata.object,
1106
+ metadata.uploadId,
1107
+ metadata.partNumber,
1108
+ value,
1109
+ valueSize
1110
+ );
1111
+ return encodeJSONResult(result);
1112
+ } else if (metadata.method === "completeMultipartUpload") {
1113
+ let result = await this.#completeMultipartUpload(
1114
+ metadata.object,
1115
+ metadata.uploadId,
1116
+ metadata.parts
1117
+ );
1118
+ return encodeResult(result);
1119
+ } else {
1120
+ if (metadata.method === "abortMultipartUpload")
1121
+ return await this.#abortMultipartUpload(metadata.object, metadata.uploadId), new Response();
1122
+ throw new InternalError();
1123
+ }
1124
+ };
1125
+ };
1126
+ __decorateClass([
1127
+ GET("/")
1128
+ ], R2BucketObject.prototype, "get", 2), __decorateClass([
1129
+ PUT("/")
1130
+ ], R2BucketObject.prototype, "put", 2);
1131
+ export {
1132
+ R2BucketObject
1133
+ };
1134
+ //# sourceMappingURL=bucket.worker.js.map