@midsummer-ai/sdk 0.0.0-stage → 0.1.0-next.1723

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 (79) hide show
  1. package/LICENSE +177 -0
  2. package/README.md +4 -2
  3. package/agents/contract.d.mts +9 -0
  4. package/agents/contract.mjs +221 -0
  5. package/agents/index.d.mts +128 -0
  6. package/agents/index.mjs +203 -0
  7. package/agents/stand-in.d.mts +35 -0
  8. package/agents/stand-in.mjs +149 -0
  9. package/ai/contract.d.mts +10 -0
  10. package/ai/contract.mjs +77 -0
  11. package/ai/index.d.mts +94 -0
  12. package/ai/index.mjs +193 -0
  13. package/ai/stand-in.d.mts +7 -0
  14. package/ai/stand-in.mjs +41 -0
  15. package/build-SQ44jwSa.d.mts +41 -0
  16. package/client.d.mts +45 -0
  17. package/client.mjs +106 -0
  18. package/connection-BHjDaLYA.mjs +2009 -0
  19. package/contract.d.mts +10 -0
  20. package/contract.mjs +469 -0
  21. package/database/build.d.mts +2 -0
  22. package/database/build.mjs +579 -0
  23. package/database/client.d.mts +60 -0
  24. package/database/client.mjs +133 -0
  25. package/database/contract.d.mts +11 -0
  26. package/database/contract.mjs +164 -0
  27. package/database/errors.d.mts +23 -0
  28. package/database/errors.mjs +13 -0
  29. package/database/server.d.mts +3 -0
  30. package/database/server.mjs +320 -0
  31. package/database/stand-in.d.mts +99 -0
  32. package/database/stand-in.mjs +430 -0
  33. package/email/client.d.mts +34 -0
  34. package/email/client.mjs +131 -0
  35. package/email/contract.d.mts +11 -0
  36. package/email/contract.mjs +99 -0
  37. package/email/index.d.mts +74 -0
  38. package/email/index.mjs +59 -0
  39. package/email/smtp.d.mts +22 -0
  40. package/email/smtp.mjs +296 -0
  41. package/email/stand-in.d.mts +13 -0
  42. package/email/stand-in.mjs +51 -0
  43. package/index-DdoK0y7D.d.mts +192 -0
  44. package/index-sgdyoA2P.d.mts +212 -0
  45. package/index.d.mts +2 -0
  46. package/index.mjs +137 -0
  47. package/names-DfCBKupJ.mjs +115 -0
  48. package/package.json +152 -4
  49. package/predict-Drfm_8pO.mjs +179 -0
  50. package/queues/contract.d.mts +13 -0
  51. package/queues/contract.mjs +278 -0
  52. package/queues/index.d.mts +184 -0
  53. package/queues/index.mjs +126 -0
  54. package/queues/stand-in.d.mts +7 -0
  55. package/queues/stand-in.mjs +110 -0
  56. package/queues/wire.d.mts +9 -0
  57. package/queues/wire.mjs +199 -0
  58. package/s3.d.mts +43 -0
  59. package/s3.mjs +1003 -0
  60. package/schema-BF-Y1EVu.mjs +142 -0
  61. package/schema-DS8x1Dn8.mjs +115 -0
  62. package/schema-f7E9uzUn.d.mts +74 -0
  63. package/server-C7O7CICU.d.mts +182 -0
  64. package/socket.d.mts +67 -0
  65. package/socket.mjs +263 -0
  66. package/stand-in.d.mts +7 -0
  67. package/stand-in.mjs +185 -0
  68. package/store-BA8K-bAR.mjs +237 -0
  69. package/tabs-DEeMee7M.mjs +457 -0
  70. package/vector/contract.d.mts +10 -0
  71. package/vector/contract.mjs +635 -0
  72. package/vector/index.d.mts +2 -0
  73. package/vector/index.mjs +108 -0
  74. package/vector/stand-in.d.mts +11 -0
  75. package/vector/stand-in.mjs +169 -0
  76. package/vector/wire.d.mts +25 -0
  77. package/vector/wire.mjs +175 -0
  78. package/wire.d.mts +54 -0
  79. package/wire.mjs +731 -0
package/contract.d.mts ADDED
@@ -0,0 +1,10 @@
1
+ import { p as Storage } from "./index-sgdyoA2P.mjs";
2
+ //#region src/contract.d.ts
3
+ interface ContractCase {
4
+ /** A line of the specification. */
5
+ readonly name: string;
6
+ run(storage: Storage): Promise<void>;
7
+ }
8
+ declare const storageContract: ReadonlyArray<ContractCase>;
9
+ //#endregion
10
+ export { ContractCase, storageContract };
package/contract.mjs ADDED
@@ -0,0 +1,469 @@
1
+ import { h as StorageError } from "./names-DfCBKupJ.mjs";
2
+ import "./index.mjs";
3
+ //#region src/contract.ts
4
+ /**
5
+ * Storage's contract: what every engine promises, as cases any test runner can run. The Stand-in
6
+ * runs them in this package; the real Capability runs the same cases against its own engine,
7
+ * through the wire. A behaviour that is not a case here is not promised.
8
+ *
9
+ * for (const c of storageContract) test(c.name, () => c.run(storage(makeEngine())))
10
+ *
11
+ * Each case gets a Storage with nothing in it and throws when the engine breaks the promise.
12
+ * Expected values are written out, never computed with the SDK's own code.
13
+ */
14
+ const HELLO = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824";
15
+ const HELLO_WORLD = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9";
16
+ const storageContract = [
17
+ {
18
+ name: "an Object put is read back byte for byte, with its size, content type, metadata and SHA-256 etag",
19
+ async run(storage) {
20
+ const bucket = storage.bucket("avatars");
21
+ const put = await bucket.put("ada.txt", "hello", {
22
+ contentType: "text/plain",
23
+ metadata: { owner: "ada" }
24
+ });
25
+ equal(pick(put), {
26
+ key: "ada.txt",
27
+ size: 5,
28
+ etag: HELLO,
29
+ contentType: "text/plain",
30
+ metadata: { owner: "ada" }
31
+ }, "the put's answer");
32
+ const read = await bucket.get("ada.txt");
33
+ equal(read && text(read.body), "hello", "the bytes read");
34
+ equal(read && pick(read), pick(put), "the read's description");
35
+ equal(await bucket.head("ada.txt"), put, "the head");
36
+ }
37
+ },
38
+ {
39
+ name: "a key that holds nothing reads and heads as null, and deleting it is not an error",
40
+ async run(storage) {
41
+ const bucket = storage.bucket("avatars");
42
+ equal(await bucket.get("nobody.png"), null, "the read");
43
+ equal(await bucket.head("nobody.png"), null, "the head");
44
+ await bucket.delete("nobody.png");
45
+ await bucket.put("ada.txt", "hello");
46
+ await bucket.delete("ada.txt");
47
+ equal(await bucket.get("ada.txt"), null, "the read after its delete");
48
+ }
49
+ },
50
+ {
51
+ name: "a put replaces the Object and the next read sees it, whatever the caller does to its buffer afterwards",
52
+ async run(storage) {
53
+ const bucket = storage.bucket("avatars");
54
+ await bucket.put("ada.txt", "hello");
55
+ const bytes = new TextEncoder().encode("hello world");
56
+ const put = await bucket.put("ada.txt", bytes);
57
+ bytes.fill(0);
58
+ const read = await bucket.get("ada.txt");
59
+ equal(read && text(read.body), "hello world", "the bytes read");
60
+ equal(put.etag, HELLO_WORLD, "the new etag");
61
+ equal(put.contentType, "application/octet-stream", "the default content type");
62
+ }
63
+ },
64
+ {
65
+ name: "a conditional put refuses to overwrite: ifNoneMatch when the key holds something, ifMatch when the etag differs",
66
+ async run(storage) {
67
+ const bucket = storage.bucket("avatars");
68
+ await bucket.put("ada.txt", "hello", { ifNoneMatch: "*" });
69
+ await fails(bucket.put("ada.txt", "again", { ifNoneMatch: "*" }), "conflict", "ifNoneMatch on a key that holds something");
70
+ await fails(bucket.put("ada.txt", "again", { ifMatch: HELLO_WORLD }), "conflict", "ifMatch with another etag");
71
+ await fails(bucket.put("grace.txt", "hello", { ifMatch: HELLO }), "conflict", "ifMatch on a key that holds nothing");
72
+ await bucket.put("ada.txt", "hello world", { ifMatch: HELLO });
73
+ const read = await bucket.get("ada.txt");
74
+ equal(read && text(read.body), "hello world", "the bytes after the conditional put that held");
75
+ }
76
+ },
77
+ {
78
+ name: "a range read returns exactly those bytes, cut at the end, and a range that starts past the end is invalid",
79
+ async run(storage) {
80
+ const bucket = storage.bucket("videos");
81
+ await bucket.put("clip.bin", "0123456789");
82
+ for (const [options, bytes, range] of [
83
+ [
84
+ { range: {
85
+ offset: 2,
86
+ length: 3
87
+ } },
88
+ "234",
89
+ {
90
+ offset: 2,
91
+ length: 3
92
+ }
93
+ ],
94
+ [
95
+ { range: { offset: 7 } },
96
+ "789",
97
+ {
98
+ offset: 7,
99
+ length: 3
100
+ }
101
+ ],
102
+ [
103
+ { range: {
104
+ offset: 8,
105
+ length: 100
106
+ } },
107
+ "89",
108
+ {
109
+ offset: 8,
110
+ length: 2
111
+ }
112
+ ],
113
+ [
114
+ { range: { suffix: 3 } },
115
+ "789",
116
+ {
117
+ offset: 7,
118
+ length: 3
119
+ }
120
+ ],
121
+ [
122
+ { range: { suffix: 100 } },
123
+ "0123456789",
124
+ {
125
+ offset: 0,
126
+ length: 10
127
+ }
128
+ ]
129
+ ]) {
130
+ const read = await bucket.get("clip.bin", options);
131
+ equal(read && [
132
+ text(read.body),
133
+ read.range,
134
+ read.size
135
+ ], [
136
+ bytes,
137
+ range,
138
+ 10
139
+ ], `the read of ${JSON.stringify(options)}`);
140
+ }
141
+ await fails(bucket.get("clip.bin", { range: { offset: 10 } }), "invalid", "a range that starts at the end");
142
+ equal(await bucket.get("missing.bin", { range: {
143
+ offset: 0,
144
+ length: 1
145
+ } }), null, "a range of a key that holds nothing");
146
+ }
147
+ },
148
+ {
149
+ name: "a listing is in UTF-8 byte order under its prefix, folds keys at the delimiter into prefixes, and pages with after",
150
+ async run(storage) {
151
+ const bucket = storage.bucket("photos");
152
+ for (const key of [
153
+ "b.txt",
154
+ "photos/dog.png",
155
+ "a.txt",
156
+ "photos/2026/2.png",
157
+ "Z.txt",
158
+ "photos/2026/1.png",
159
+ "photos/cat.png",
160
+ "😀",
161
+ "�"
162
+ ]) await bucket.put(key, "x");
163
+ const keys = (listing) => [
164
+ listing.objects.map((o) => o.key),
165
+ listing.prefixes,
166
+ listing.next ?? null
167
+ ];
168
+ equal(keys(await bucket.list()), [
169
+ [
170
+ "Z.txt",
171
+ "a.txt",
172
+ "b.txt",
173
+ "photos/2026/1.png",
174
+ "photos/2026/2.png",
175
+ "photos/cat.png",
176
+ "photos/dog.png",
177
+ "�",
178
+ "😀"
179
+ ],
180
+ [],
181
+ null
182
+ ], "the whole listing");
183
+ equal(keys(await bucket.list({
184
+ prefix: "photos/",
185
+ delimiter: "/"
186
+ })), [
187
+ ["photos/cat.png", "photos/dog.png"],
188
+ ["photos/2026/"],
189
+ null
190
+ ], "one level of photos/");
191
+ equal(keys(await bucket.list({
192
+ prefix: "photos/",
193
+ delimiter: "/",
194
+ limit: 2
195
+ })), [
196
+ ["photos/cat.png"],
197
+ ["photos/2026/"],
198
+ "photos/cat.png"
199
+ ], "the first page of two");
200
+ equal(keys(await bucket.list({
201
+ prefix: "photos/",
202
+ delimiter: "/",
203
+ limit: 2,
204
+ after: "photos/cat.png"
205
+ })), [
206
+ ["photos/dog.png"],
207
+ [],
208
+ null
209
+ ], "the second page");
210
+ equal(keys(await bucket.list({
211
+ prefix: "photos/",
212
+ delimiter: "/",
213
+ after: "photos/2026/"
214
+ })), [
215
+ ["photos/cat.png", "photos/dog.png"],
216
+ [],
217
+ null
218
+ ], "after a folded prefix, none of its keys");
219
+ equal(keys(await bucket.list({ limit: 9 })), [
220
+ (await bucket.list()).objects.map((o) => o.key),
221
+ [],
222
+ null
223
+ ], "a page that ends exactly at the last key has no next");
224
+ }
225
+ },
226
+ {
227
+ name: "a copy puts the same content under a second key, with its content type and metadata",
228
+ async run(storage) {
229
+ const bucket = storage.bucket("avatars");
230
+ await bucket.put("ada.txt", "hello", {
231
+ contentType: "text/plain",
232
+ metadata: { owner: "ada" }
233
+ });
234
+ equal(pick(await bucket.copy("ada.txt", "ada-copy.txt")), {
235
+ key: "ada-copy.txt",
236
+ size: 5,
237
+ etag: HELLO,
238
+ contentType: "text/plain",
239
+ metadata: { owner: "ada" }
240
+ }, "the copy");
241
+ const read = await bucket.get("ada-copy.txt");
242
+ equal(read && text(read.body), "hello", "the copy's bytes");
243
+ await fails(bucket.copy("nobody.txt", "x.txt"), "not_found", "a copy from a key that holds nothing");
244
+ await fails(bucket.copy("ada.txt", "ada-copy.txt", { ifNoneMatch: "*" }), "conflict", "a conditional copy onto a key that holds something");
245
+ }
246
+ },
247
+ {
248
+ name: "Buckets are separate: what one holds is not in another",
249
+ async run(storage) {
250
+ await storage.bucket("avatars").put("ada.txt", "hello");
251
+ equal(await storage.bucket("invoices").get("ada.txt"), null, "the read in another Bucket");
252
+ equal((await storage.bucket("invoices").list()).objects.length, 0, "the other Bucket's listing");
253
+ }
254
+ },
255
+ {
256
+ name: "names, ranges and limits are checked: Bucket names, empty keys, keys over 1,024 UTF-8 bytes, metadata names",
257
+ async run(storage) {
258
+ for (const name of [
259
+ "Avatars",
260
+ "ab",
261
+ "-avatars",
262
+ "avatars_2",
263
+ "a".repeat(64)
264
+ ]) await fails(Promise.resolve().then(() => storage.bucket(name)), "invalid", `the Bucket name "${name}"`);
265
+ const bucket = storage.bucket("avatars");
266
+ await fails(bucket.put("", "x"), "invalid", "an empty key");
267
+ await fails(bucket.put("é".repeat(513), "x"), "invalid", "a key of 1,026 bytes");
268
+ await bucket.put("é".repeat(512), "x");
269
+ await fails(bucket.put("ada.txt", "x", { metadata: { Owner: "ada" } }), "invalid", "a metadata name with a capital");
270
+ await fails(bucket.get("ada.txt", { range: { offset: -1 } }), "invalid", "a negative range offset");
271
+ await fails(bucket.list({ limit: 0 }), "invalid", "a listing limit of 0");
272
+ }
273
+ },
274
+ {
275
+ name: "an upload in parts becomes one Object of their bytes in part-number order when it completes, and until then the key holds what it held",
276
+ async run(storage) {
277
+ const bucket = storage.bucket("media");
278
+ await bucket.put("film.mp4", "old");
279
+ const { uploadId } = await bucket.createUpload("film.mp4", {
280
+ contentType: "video/mp4",
281
+ metadata: { owner: "ada" }
282
+ });
283
+ const second = await bucket.uploadPart("film.mp4", uploadId, 2, "hello");
284
+ const first = await bucket.uploadPart("film.mp4", uploadId, 1, new Uint8Array(FIVE_MIB).fill(97));
285
+ equal([
286
+ second.partNumber,
287
+ second.etag,
288
+ second.size
289
+ ], [
290
+ 2,
291
+ HELLO,
292
+ 5
293
+ ], "the second part, sent first");
294
+ equal(first.size, FIVE_MIB, "the first part's size");
295
+ const before = await bucket.get("film.mp4");
296
+ equal(before && text(before.body), "old", "the key before the upload completes");
297
+ const done = await bucket.completeUpload("film.mp4", uploadId, [first, second]);
298
+ equal([
299
+ done.size,
300
+ done.contentType,
301
+ done.metadata
302
+ ], [
303
+ FIVE_MIB + 5,
304
+ "video/mp4",
305
+ { owner: "ada" }
306
+ ], "the completed Object");
307
+ const read = await bucket.get("film.mp4");
308
+ equal(read && [
309
+ read.body.length,
310
+ read.body[0],
311
+ read.body[FIVE_MIB - 1],
312
+ text(read.body.subarray(FIVE_MIB))
313
+ ], [
314
+ FIVE_MIB + 5,
315
+ 97,
316
+ 97,
317
+ "hello"
318
+ ], "the joined bytes");
319
+ const whole = new Uint8Array(FIVE_MIB + 5).fill(97);
320
+ whole.set(new TextEncoder().encode("hello"), FIVE_MIB);
321
+ equal(done.etag, (await bucket.put("same.bin", whole)).etag, "the etag, the same as one put of the same bytes");
322
+ await fails(bucket.uploadPart("film.mp4", uploadId, 3, "more"), "not_found", "a part sent after completing", "upload");
323
+ }
324
+ },
325
+ {
326
+ name: "a part sent again replaces it, and completing checks each part's etag, their order, the 5 MiB floor and that parts but the last are one size, leaving the upload open when it refuses",
327
+ async run(storage) {
328
+ const bucket = storage.bucket("media");
329
+ const { uploadId } = await bucket.createUpload("film.mp4");
330
+ const stale = await bucket.uploadPart("film.mp4", uploadId, 1, "small");
331
+ const first = await bucket.uploadPart("film.mp4", uploadId, 1, new Uint8Array(FIVE_MIB));
332
+ const second = await bucket.uploadPart("film.mp4", uploadId, 2, "hello");
333
+ await fails(bucket.uploadPart("film.mp4", uploadId, 10001, "x"), "invalid", "part number 10,001");
334
+ await fails(bucket.completeUpload("film.mp4", uploadId, [stale, second]), "invalid", "a part whose etag was replaced", "part");
335
+ await fails(bucket.completeUpload("film.mp4", uploadId, [second, first]), "invalid", "parts out of order", "part-order");
336
+ await fails(bucket.completeUpload("film.mp4", uploadId, [first, {
337
+ partNumber: 3,
338
+ etag: HELLO
339
+ }]), "invalid", "a part never sent", "part");
340
+ const small = await bucket.createUpload("small.bin");
341
+ const a = await bucket.uploadPart("small.bin", small.uploadId, 1, "hello");
342
+ const b = await bucket.uploadPart("small.bin", small.uploadId, 2, "hello");
343
+ await fails(bucket.completeUpload("small.bin", small.uploadId, [a, b]), "invalid", "a first part under 5 MiB", "part-too-small");
344
+ const uneven = await bucket.createUpload("uneven.bin");
345
+ const big = await bucket.uploadPart("uneven.bin", uneven.uploadId, 1, new Uint8Array(FIVE_MIB + 1));
346
+ const less = await bucket.uploadPart("uneven.bin", uneven.uploadId, 2, new Uint8Array(FIVE_MIB));
347
+ const last = await bucket.uploadPart("uneven.bin", uneven.uploadId, 3, "hello");
348
+ await fails(bucket.completeUpload("uneven.bin", uneven.uploadId, [
349
+ big,
350
+ less,
351
+ last
352
+ ]), "invalid", "a middle part smaller than the first", "part");
353
+ const growing = await bucket.createUpload("growing.bin");
354
+ const one = await bucket.uploadPart("growing.bin", growing.uploadId, 1, new Uint8Array(FIVE_MIB));
355
+ const two = await bucket.uploadPart("growing.bin", growing.uploadId, 2, new Uint8Array(FIVE_MIB + 1));
356
+ await fails(bucket.completeUpload("growing.bin", growing.uploadId, [one, two]), "invalid", "a last part larger than the first", "part");
357
+ equal((await bucket.completeUpload("film.mp4", uploadId, [first, second])).size, FIVE_MIB + 5, "the upload completed after its refusals");
358
+ }
359
+ },
360
+ {
361
+ name: "an Object larger than one call carries is put, read whole and read by any range, with the etag one put of its bytes has",
362
+ async run(storage) {
363
+ const bucket = storage.bucket("media");
364
+ const body = pattern(LARGE);
365
+ const put = await bucket.put("big.bin", body);
366
+ equal([put.size, put.etag], [LARGE, await hex(body)], "its size, and the hash of its bytes as its etag");
367
+ const whole = await bucket.get("big.bin");
368
+ equal([
369
+ whole?.size,
370
+ whole?.range,
371
+ await hex(whole.body)
372
+ ], [
373
+ LARGE,
374
+ void 0,
375
+ put.etag
376
+ ], "read whole, every byte");
377
+ const ranges = [
378
+ {
379
+ offset: 5242887,
380
+ length: 31457280
381
+ },
382
+ { suffix: 20971523 },
383
+ { offset: LARGE - 1048576 }
384
+ ];
385
+ for (const range of ranges) {
386
+ const read = await bucket.get("big.bin", { range });
387
+ const offset = "suffix" in range ? LARGE - range.suffix : range.offset;
388
+ const length = "suffix" in range ? range.suffix : range.length ?? LARGE - offset;
389
+ equal(read?.range, {
390
+ offset,
391
+ length
392
+ }, `the range ${JSON.stringify(range)}`);
393
+ equal(await hex(read.body), await hex(body.subarray(offset, offset + length)), `the bytes of ${JSON.stringify(range)}`);
394
+ }
395
+ }
396
+ },
397
+ {
398
+ name: "an aborted upload takes no more parts and cannot complete, and an unknown one is not found",
399
+ async run(storage) {
400
+ const bucket = storage.bucket("media");
401
+ await bucket.put("film.mp4", "old");
402
+ const { uploadId } = await bucket.createUpload("film.mp4");
403
+ const part = await bucket.uploadPart("film.mp4", uploadId, 1, "hello");
404
+ await bucket.abortUpload("film.mp4", uploadId);
405
+ await fails(bucket.uploadPart("film.mp4", uploadId, 2, "x"), "not_found", "a part after aborting", "upload");
406
+ await fails(bucket.completeUpload("film.mp4", uploadId, [part]), "not_found", "completing after aborting", "upload");
407
+ await fails(bucket.abortUpload("film.mp4", uploadId), "not_found", "aborting twice", "upload");
408
+ await fails(bucket.uploadPart("film.mp4", "no-such-upload", 1, "x"), "not_found", "a part of an unknown upload", "upload");
409
+ const read = await bucket.get("film.mp4");
410
+ equal(read && text(read.body), "old", "the key after the abort");
411
+ }
412
+ },
413
+ {
414
+ name: "an upload made with ifNoneMatch refuses to complete over a key that holds something",
415
+ async run(storage) {
416
+ const bucket = storage.bucket("media");
417
+ const { uploadId } = await bucket.createUpload("film.mp4", { ifNoneMatch: "*" });
418
+ const part = await bucket.uploadPart("film.mp4", uploadId, 1, "hello");
419
+ await bucket.put("film.mp4", "someone else's");
420
+ await fails(bucket.completeUpload("film.mp4", uploadId, [part]), "conflict", "completing over a key that now holds something");
421
+ }
422
+ }
423
+ ];
424
+ function pick(o) {
425
+ return {
426
+ key: o.key,
427
+ size: o.size,
428
+ etag: o.etag,
429
+ contentType: o.contentType,
430
+ metadata: o.metadata
431
+ };
432
+ }
433
+ const text = (bytes) => new TextDecoder().decode(bytes);
434
+ function equal(actual, expected, what) {
435
+ const a = JSON.stringify(actual), e = JSON.stringify(expected);
436
+ if (a !== e) throw new Error(`${what}: expected ${e}, got ${a}`);
437
+ }
438
+ async function fails(call, code, what, reason) {
439
+ const expected = `a StorageError "${code}"${reason ? ` for ${reason}` : ""}`;
440
+ try {
441
+ await call;
442
+ } catch (error) {
443
+ if (error instanceof StorageError && error.code === code && (reason === void 0 || error.reason === reason)) return;
444
+ throw new Error(`${what}: expected ${expected}, got ${error instanceof StorageError ? `"${error.code}" (${error.reason ?? "no reason"}): ${error.message}` : String(error)}`);
445
+ }
446
+ throw new Error(`${what}: expected ${expected}, but it succeeded`);
447
+ }
448
+ const FIVE_MIB = 5242880;
449
+ const LARGE = 41943040;
450
+ /** `size` bytes that differ along their length, so a byte read from the wrong place shows. */
451
+ function pattern(size) {
452
+ const bytes = new Uint8Array(size);
453
+ let state = 2463534242;
454
+ for (let i = 0; i < size; i += 4) {
455
+ state ^= state << 13;
456
+ state ^= state >>> 17;
457
+ state ^= state << 5;
458
+ bytes[i] = state & 255;
459
+ bytes[i + 1] = state >>> 8 & 255;
460
+ bytes[i + 2] = state >>> 16 & 255;
461
+ bytes[i + 3] = state >>> 24 & 255;
462
+ }
463
+ return bytes;
464
+ }
465
+ async function hex(bytes) {
466
+ return [...new Uint8Array(await crypto.subtle.digest("SHA-256", bytes))].map((b) => b.toString(16).padStart(2, "0")).join("");
467
+ }
468
+ //#endregion
469
+ export { storageContract };
@@ -0,0 +1,2 @@
1
+ import { a as isSource, i as buildDatabase, n as BuiltFunction, o as typeOf, r as Refused, t as Built } from "../build-SQ44jwSa.mjs";
2
+ export { Built, BuiltFunction, Refused, buildDatabase, isSource, typeOf };