@bjornpagen/bumbledb-log 0.20.0 → 0.20.2

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 (76) hide show
  1. package/dist/braids.d.ts +28 -0
  2. package/dist/braids.d.ts.map +1 -0
  3. package/{src/braids.ts → dist/braids.js} +22 -29
  4. package/dist/braids.js.map +1 -0
  5. package/dist/bytes.d.ts +26 -0
  6. package/dist/bytes.d.ts.map +1 -0
  7. package/dist/bytes.js +86 -0
  8. package/dist/bytes.js.map +1 -0
  9. package/dist/chain.d.ts +56 -0
  10. package/dist/chain.d.ts.map +1 -0
  11. package/dist/chain.js +137 -0
  12. package/dist/chain.js.map +1 -0
  13. package/dist/codec.d.ts +71 -0
  14. package/dist/codec.d.ts.map +1 -0
  15. package/dist/codec.js +179 -0
  16. package/dist/codec.js.map +1 -0
  17. package/dist/descriptor.d.ts +61 -0
  18. package/dist/descriptor.d.ts.map +1 -0
  19. package/dist/descriptor.js +160 -0
  20. package/dist/descriptor.js.map +1 -0
  21. package/dist/errors.d.ts +202 -0
  22. package/dist/errors.d.ts.map +1 -0
  23. package/dist/errors.js +122 -0
  24. package/dist/errors.js.map +1 -0
  25. package/dist/index.d.ts +34 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +24 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/keys.d.ts +52 -0
  30. package/dist/keys.d.ts.map +1 -0
  31. package/dist/keys.js +166 -0
  32. package/dist/keys.js.map +1 -0
  33. package/dist/manifest.d.ts +42 -0
  34. package/dist/manifest.d.ts.map +1 -0
  35. package/dist/manifest.js +116 -0
  36. package/dist/manifest.js.map +1 -0
  37. package/dist/replica.d.ts +154 -0
  38. package/dist/replica.d.ts.map +1 -0
  39. package/dist/replica.js +820 -0
  40. package/dist/replica.js.map +1 -0
  41. package/dist/store-s3.d.ts +31 -0
  42. package/dist/store-s3.d.ts.map +1 -0
  43. package/dist/store-s3.js +299 -0
  44. package/dist/store-s3.js.map +1 -0
  45. package/dist/store.d.ts +129 -0
  46. package/dist/store.d.ts.map +1 -0
  47. package/dist/store.js +577 -0
  48. package/dist/store.js.map +1 -0
  49. package/dist/tenants.d.ts +56 -0
  50. package/dist/tenants.d.ts.map +1 -0
  51. package/dist/tenants.js +333 -0
  52. package/dist/tenants.js.map +1 -0
  53. package/dist/tilde-family.json +21 -0
  54. package/dist/vector.d.ts +31 -0
  55. package/dist/vector.d.ts.map +1 -0
  56. package/dist/vector.js +87 -0
  57. package/dist/vector.js.map +1 -0
  58. package/dist/writer.d.ts +106 -0
  59. package/dist/writer.d.ts.map +1 -0
  60. package/dist/writer.js +677 -0
  61. package/dist/writer.js.map +1 -0
  62. package/package.json +12 -7
  63. package/src/bytes.ts +0 -115
  64. package/src/chain.ts +0 -181
  65. package/src/codec.ts +0 -252
  66. package/src/descriptor.ts +0 -240
  67. package/src/errors.ts +0 -309
  68. package/src/index.ts +0 -57
  69. package/src/keys.ts +0 -218
  70. package/src/manifest.ts +0 -161
  71. package/src/replica.ts +0 -1061
  72. package/src/store-s3.ts +0 -377
  73. package/src/store.ts +0 -727
  74. package/src/tenants.ts +0 -414
  75. package/src/vector.ts +0 -103
  76. package/src/writer.ts +0 -977
package/src/store-s3.ts DELETED
@@ -1,377 +0,0 @@
1
- /**
2
- * `s3Store`: the five verbs over one S3-compatible target. The official
3
- * `@aws-sdk/client-s3` client signs and talks; this module maps HTTP
4
- * outcomes onto the sums. One storage class for every key. The vendor
5
- * ETag header is the opaque token, carried verbatim. R2 rides region
6
- * `auto` and a required endpoint. A 409, a timeout, or any other
7
- * unproved conditional-write result is Ambiguous; the GET-verify law
8
- * resolves it. Credentials are consulted per request. Conditional
9
- * writes are not retried by the client: a re-sent PUT is an unproved
10
- * outcome. Body-stream failures wrap ErrStore.
11
- */
12
-
13
- import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3"
14
- import * as errors from "@superbuilders/errors"
15
- import { bytesEqual } from "#bytes.ts"
16
- import { wrapStore } from "#errors.ts"
17
- import type { StoreKey } from "#keys.ts"
18
- import { parsePrefix } from "#keys.ts"
19
- import type { Create, Etag, Fetched, ObjectStore, Swap } from "#store.ts"
20
-
21
- interface StaticKeys {
22
- readonly accessKeyId: string
23
- readonly secretAccessKey: string
24
- readonly sessionToken?: string
25
- }
26
-
27
- /** Static keys, or a caller-owned refresh the store invokes before each signed request. */
28
- type S3Credentials = StaticKeys | (() => StaticKeys | Promise<StaticKeys>)
29
-
30
- interface S3Config {
31
- readonly endpoint?: string
32
- readonly region: string
33
- readonly bucket: string
34
- readonly credentials: S3Credentials
35
- readonly prefix?: string
36
- }
37
-
38
- const READ_ATTEMPTS = 6
39
- const READ_BASE_MS = 50
40
- const READ_CAP_MS = 2_000
41
-
42
- function asEtag(raw: string): Etag {
43
- return raw as Etag
44
- }
45
-
46
- function joinPrefix(prefix: string, key: string): string {
47
- return prefix.length === 0 ? key : `${prefix}/${key}`
48
- }
49
-
50
- async function resolveKeys(credentials: S3Credentials): Promise<StaticKeys> {
51
- if (typeof credentials === "function") {
52
- return await credentials()
53
- }
54
- return credentials
55
- }
56
-
57
- function httpStatus(error: unknown): number | undefined {
58
- if (error === null || typeof error !== "object" || !("$metadata" in error)) {
59
- return undefined
60
- }
61
- const metadata = error.$metadata
62
- if (metadata === null || typeof metadata !== "object" || !("httpStatusCode" in metadata)) {
63
- return undefined
64
- }
65
- const status = metadata.httpStatusCode
66
- return typeof status === "number" ? status : undefined
67
- }
68
-
69
- function asError(error: unknown): Error {
70
- if (error instanceof Error) {
71
- return error
72
- }
73
- return errors.new(String(error))
74
- }
75
-
76
- function etagOf(raw: string | undefined, op: string, key: StoreKey): Etag {
77
- if (raw === undefined || raw.length === 0) {
78
- throw wrapStore(errors.new("vendor omitted ETag"), `${op} ${key}`)
79
- }
80
- return asEtag(raw)
81
- }
82
-
83
- function isTransientName(error: unknown): boolean {
84
- if (error === null || typeof error !== "object" || !("name" in error)) {
85
- return true
86
- }
87
- const name = error.name
88
- return (
89
- name === "TimeoutError" ||
90
- name === "RequestTimeout" ||
91
- name === "TimeoutErrorException" ||
92
- name === "NetworkingError" ||
93
- name === "Conflict" ||
94
- name === "ConflictException" ||
95
- name === undefined
96
- )
97
- }
98
-
99
- /** 409 Conflict is Ambiguous, never a proved Exists or Moved. */
100
- function isUnprovedWrite(error: unknown): boolean {
101
- const status = httpStatus(error)
102
- if (status === 409) {
103
- return true
104
- }
105
- if (status !== undefined && status >= 500 && status < 600) {
106
- return true
107
- }
108
- if (status !== undefined) {
109
- return false
110
- }
111
- return isTransientName(error)
112
- }
113
-
114
- function isRetryableRead(error: unknown): boolean {
115
- const status = httpStatus(error)
116
- if (status !== undefined) {
117
- return status >= 500 && status < 600
118
- }
119
- return isTransientName(error)
120
- }
121
-
122
- async function sleep(ms: number): Promise<void> {
123
- await new Promise(function later(resolve) {
124
- setTimeout(resolve, ms)
125
- })
126
- }
127
-
128
- function jitteredMs(ceiling: number): number {
129
- return Math.floor(Math.random() * (ceiling + 1))
130
- }
131
-
132
- async function bodyBytes(body: unknown, op: string, key: StoreKey): Promise<Uint8Array> {
133
- const ran = await errors.try(
134
- (async function consume() {
135
- if (body === null || typeof body !== "object") {
136
- throw errors.new("vendor omitted body stream")
137
- }
138
- const stream = body as { transformToByteArray?: () => Promise<Uint8Array> }
139
- if (typeof stream.transformToByteArray !== "function") {
140
- throw errors.new("vendor omitted body stream")
141
- }
142
- return await stream.transformToByteArray()
143
- })()
144
- )
145
- if (ran.error) {
146
- throw wrapStore(ran.error, `${op} ${key}`)
147
- }
148
- return ran.data
149
- }
150
-
151
- function s3Store(config: S3Config): ObjectStore {
152
- if (config.region === "auto" && config.endpoint === undefined) {
153
- throw errors.new("region auto needs an endpoint")
154
- }
155
- const prefix = parsePrefix(config.prefix ?? "")
156
- const client = new S3Client({
157
- region: config.region,
158
- maxAttempts: 1,
159
- credentials: async function credentials() {
160
- const keys = await resolveKeys(config.credentials)
161
- return {
162
- accessKeyId: keys.accessKeyId,
163
- secretAccessKey: keys.secretAccessKey,
164
- ...(keys.sessionToken === undefined ? {} : { sessionToken: keys.sessionToken })
165
- }
166
- },
167
- requestChecksumCalculation: "WHEN_REQUIRED",
168
- responseChecksumValidation: "WHEN_REQUIRED",
169
- ...(config.endpoint === undefined ? {} : { endpoint: config.endpoint, forcePathStyle: true })
170
- })
171
-
172
- function objectKey(key: StoreKey): string {
173
- return joinPrefix(prefix, key)
174
- }
175
-
176
- type Load =
177
- | { readonly tag: "hit"; readonly fetched: Fetched }
178
- | { readonly tag: "miss" }
179
- | { readonly tag: "retry"; readonly error: Error }
180
- | { readonly tag: "fail"; readonly error: Error }
181
-
182
- async function load(key: StoreKey): Promise<Load> {
183
- const ran = await errors.try(
184
- client.send(
185
- new GetObjectCommand({
186
- Bucket: config.bucket,
187
- Key: objectKey(key)
188
- })
189
- )
190
- )
191
- if (ran.error) {
192
- if (httpStatus(ran.error) === 404) {
193
- return { tag: "miss" }
194
- }
195
- const error = wrapStore(asError(ran.error), `get ${key}`)
196
- return isRetryableRead(ran.error) ? { tag: "retry", error } : { tag: "fail", error }
197
- }
198
- if (ran.data.Body === undefined) {
199
- return { tag: "fail", error: wrapStore(errors.new("vendor omitted body"), `get ${key}`) }
200
- }
201
- const body = await errors.try(bodyBytes(ran.data.Body, "get", key))
202
- if (body.error) {
203
- return { tag: "retry", error: body.error }
204
- }
205
- const tag = await errors.try(
206
- (async function etag() {
207
- return etagOf(ran.data.ETag, "get", key)
208
- })()
209
- )
210
- if (tag.error) {
211
- return { tag: "fail", error: tag.error }
212
- }
213
- return { tag: "hit", fetched: { bytes: body.data, etag: tag.data } }
214
- }
215
-
216
- async function getOnce(key: StoreKey): Promise<Fetched | null> {
217
- const step = await load(key)
218
- if (step.tag === "hit") {
219
- return step.fetched
220
- }
221
- if (step.tag === "miss") {
222
- return null
223
- }
224
- throw step.error
225
- }
226
-
227
- async function retryGet(key: StoreKey): Promise<Fetched | null> {
228
- let attempt = 0
229
- for (;;) {
230
- const step = await load(key)
231
- if (step.tag === "hit") {
232
- return step.fetched
233
- }
234
- if (step.tag === "miss") {
235
- return null
236
- }
237
- if (step.tag === "fail") {
238
- throw step.error
239
- }
240
- attempt += 1
241
- if (attempt === READ_ATTEMPTS) {
242
- throw step.error
243
- }
244
- const ceiling = Math.min(READ_CAP_MS, READ_BASE_MS * 2 ** (attempt - 1))
245
- await sleep(jitteredMs(ceiling))
246
- }
247
- }
248
-
249
- async function proveCreate(key: StoreKey, attempted: Uint8Array): Promise<Create> {
250
- const fetched = await retryGet(key)
251
- if (fetched === null) {
252
- return { tag: "ambiguous" }
253
- }
254
- if (bytesEqual(fetched.bytes, attempted)) {
255
- return { tag: "created", etag: fetched.etag }
256
- }
257
- return { tag: "exists" }
258
- }
259
-
260
- async function proveSwap(key: StoreKey, attempted: Uint8Array): Promise<Swap> {
261
- const fetched = await retryGet(key)
262
- if (fetched === null) {
263
- return { tag: "moved" }
264
- }
265
- if (bytesEqual(fetched.bytes, attempted)) {
266
- return { tag: "swapped", etag: fetched.etag }
267
- }
268
- return { tag: "moved" }
269
- }
270
-
271
- return {
272
- async get(key) {
273
- return await getOnce(key)
274
- },
275
-
276
- async getIfChanged(key, etag) {
277
- const ran = await errors.try(
278
- client.send(
279
- new GetObjectCommand({
280
- Bucket: config.bucket,
281
- Key: objectKey(key),
282
- IfNoneMatch: etag
283
- })
284
- )
285
- )
286
- if (ran.error) {
287
- if (httpStatus(ran.error) === 304) {
288
- return { tag: "unchanged" }
289
- }
290
- throw wrapStore(asError(ran.error), `getIfChanged ${key}`)
291
- }
292
- if (ran.data.Body === undefined) {
293
- throw wrapStore(errors.new("vendor omitted body"), `getIfChanged ${key}`)
294
- }
295
- return {
296
- tag: "changed",
297
- fetched: {
298
- bytes: await bodyBytes(ran.data.Body, "getIfChanged", key),
299
- etag: etagOf(ran.data.ETag, "getIfChanged", key)
300
- }
301
- }
302
- },
303
-
304
- async putCreate(key, bytes) {
305
- const ran = await errors.try(
306
- client.send(
307
- new PutObjectCommand({
308
- Bucket: config.bucket,
309
- Key: objectKey(key),
310
- Body: bytes,
311
- IfNoneMatch: "*"
312
- })
313
- )
314
- )
315
- if (ran.error) {
316
- if (httpStatus(ran.error) === 412) {
317
- return { tag: "exists" }
318
- }
319
- if (isUnprovedWrite(ran.error)) {
320
- return await proveCreate(key, bytes)
321
- }
322
- throw wrapStore(asError(ran.error), `putCreate ${key}`)
323
- }
324
- if (ran.data.ETag === undefined || ran.data.ETag.length === 0) {
325
- return await proveCreate(key, bytes)
326
- }
327
- return { tag: "created", etag: asEtag(ran.data.ETag) }
328
- },
329
-
330
- async putSwap(key, bytes, etag) {
331
- const ran = await errors.try(
332
- client.send(
333
- new PutObjectCommand({
334
- Bucket: config.bucket,
335
- Key: objectKey(key),
336
- Body: bytes,
337
- IfMatch: etag
338
- })
339
- )
340
- )
341
- if (ran.error) {
342
- const status = httpStatus(ran.error)
343
- if (status === 412 || status === 404) {
344
- return { tag: "moved" }
345
- }
346
- if (isUnprovedWrite(ran.error)) {
347
- return await proveSwap(key, bytes)
348
- }
349
- throw wrapStore(asError(ran.error), `putSwap ${key}`)
350
- }
351
- if (ran.data.ETag === undefined || ran.data.ETag.length === 0) {
352
- return await proveSwap(key, bytes)
353
- }
354
- return { tag: "swapped", etag: asEtag(ran.data.ETag) }
355
- },
356
-
357
- async delete(key) {
358
- const ran = await errors.try(
359
- client.send(
360
- new DeleteObjectCommand({
361
- Bucket: config.bucket,
362
- Key: objectKey(key)
363
- })
364
- )
365
- )
366
- if (ran.error) {
367
- if (httpStatus(ran.error) === 404) {
368
- return
369
- }
370
- throw wrapStore(asError(ran.error), `delete ${key}`)
371
- }
372
- }
373
- }
374
- }
375
-
376
- export type { S3Config, S3Credentials }
377
- export { joinPrefix, s3Store }