@bjornpagen/bumbledb-log 0.17.0 → 0.19.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.
package/src/errors.ts CHANGED
@@ -4,10 +4,15 @@
4
4
  * causes carried as data properties read back with the `*Of` accessors —
5
5
  * never by message-string matching. There is deliberately no
6
6
  * `ErrAlreadyApplied`: the state it would name is absorbed by idempotent
7
- * replay (L10) and never surfaces.
7
+ * replay (L10) and never surfaces. ManifestMissing, Ambiguous, OverWidth,
8
+ * Exhausted, and SlotRetired are named sums of their own: a replica
9
+ * without a manifest, an unproved conditional write, a draw past the
10
+ * lease width or the u64 ceiling, and a below-floor create.
8
11
  */
9
12
 
10
13
  import * as errors from "@superbuilders/errors"
14
+ import type { Braid } from "#descriptor.ts"
15
+ import type { Generation, StoreKey } from "#keys.ts"
11
16
 
12
17
  /**
13
18
  * Typed refusal of a protocol object before any apply: batch shape,
@@ -35,6 +40,25 @@ const ErrChainMismatch = errors.new("bumbledb-log chainMismatch: the batch viola
35
40
  /** Bounded live-tip losses exhausted — an operational signal, not an outcome arm. */
36
41
  const ErrContention = errors.new("bumbledb-log contention: consecutive live-tip losses exhausted the bound")
37
42
 
43
+ /** A replica (or any read-role handle) found no manifest; only the writer births a store. */
44
+ export const ErrManifestMissing = errors.new("bumbledb-log manifestMissing: the store has no manifest")
45
+
46
+ /**
47
+ * A conditional write the transport cannot prove (S3 409, timeout, a
48
+ * retried PUT). The machine GET-verifies; this identity is the unproved
49
+ * arm, never a proved Exists or Moved.
50
+ */
51
+ const ErrAmbiguous = errors.new("bumbledb-log ambiguous: the conditional write is unproved")
52
+
53
+ /** A single id draw larger than one lease width; the width is the one block size. */
54
+ const ErrOverWidth = errors.new("bumbledb-log overWidth: the draw exceeds the lease width")
55
+
56
+ /** The next lease would leave u64 — the id space is spent. */
57
+ const ErrExhausted = errors.new("bumbledb-log exhausted: the id space is spent")
58
+
59
+ /** A put_create at a slot below the published checkpoint vector. */
60
+ const ErrSlotRetired = errors.new("bumbledb-log slotRetired: the slot is retired")
61
+
38
62
  /** The vendor channel: I/O and store infrastructure failures. */
39
63
  const ErrStore = errors.new("bumbledb-log store failure")
40
64
 
@@ -54,26 +78,28 @@ type RefusalCause =
54
78
  | { readonly kind: "UnknownOpKind"; readonly op: number; readonly opKind: number }
55
79
  | { readonly kind: "UnknownRelation"; readonly op: number; readonly relation: number }
56
80
  | { readonly kind: "ClosedRelation"; readonly op: number; readonly relation: number }
57
- | { readonly kind: "OpRelationOutsideBraid"; readonly op: number; readonly relation: number; readonly braid: string }
81
+ | { readonly kind: "OpRelationOutsideBraid"; readonly op: number; readonly relation: number; readonly braid: Braid }
58
82
  | { readonly kind: "TagMismatch"; readonly relation: string; readonly row: number; readonly field: string }
59
83
  | { readonly kind: "BoolByte"; readonly relation: string; readonly row: number; readonly field: string }
60
84
  | { readonly kind: "InvalidUtf8"; readonly relation: string; readonly row: number; readonly field: string }
61
85
  | { readonly kind: "EmptyInterval"; readonly relation: string; readonly row: number; readonly field: string }
62
86
  | { readonly kind: "IntervalOverflow"; readonly relation: string; readonly row: number; readonly field: string }
63
87
  | { readonly kind: "TrailingBytes"; readonly bytes: number }
64
- | { readonly kind: "ManifestShape" }
88
+ | { readonly kind: "DigestWidth" }
89
+ | { readonly kind: "Arity"; readonly op: number; readonly relation: string; readonly row: number }
90
+ | { readonly kind: "Malformed"; readonly at: number }
91
+ | { readonly kind: "Overflow" }
65
92
  | { readonly kind: "ManifestVersion"; readonly version: number }
66
- | { readonly kind: "CheckpointShape" }
67
93
  | { readonly kind: "CheckpointBraids"; readonly carried: readonly string[]; readonly derived: readonly string[] }
68
94
  | { readonly kind: "CheckpointDigest"; readonly expected: string; readonly computed: string }
69
- | { readonly kind: "NoOpSlot"; readonly braid: string; readonly slot: bigint; readonly writer: bigint }
95
+ | { readonly kind: "NoOpSlot"; readonly braid: Braid; readonly slot: Generation; readonly writer: bigint }
70
96
 
71
97
  type ChainCause = "prev" | "slot" | "timestamp"
72
98
 
73
99
  interface ChainMismatchData {
74
100
  readonly cause: ChainCause
75
- readonly braid: string
76
- readonly slot: bigint
101
+ readonly braid: Braid
102
+ readonly slot: Generation
77
103
  readonly writer: bigint
78
104
  }
79
105
 
@@ -83,16 +109,46 @@ type ContentionCause =
83
109
  readonly statement: string
84
110
  readonly determinants: ReadonlyArray<Readonly<Record<string, unknown>>>
85
111
  }
86
- | { readonly kind: "slot-race"; readonly tip: bigint }
112
+ | { readonly kind: "slot-race"; readonly tip: Generation }
87
113
 
88
114
  interface ContentionData {
89
- readonly braid: string
115
+ readonly braid: Braid
90
116
  readonly cause: ContentionCause
91
117
  }
92
118
 
119
+ type AmbiguousVerb = "create" | "swap"
120
+
121
+ interface AmbiguousData {
122
+ readonly verb: AmbiguousVerb
123
+ readonly key: StoreKey
124
+ }
125
+
126
+ interface OverWidthData {
127
+ readonly requested: bigint
128
+ }
129
+
130
+ interface ExhaustedData {
131
+ readonly relation: number
132
+ readonly field: number
133
+ }
134
+
135
+ interface SlotRetiredData {
136
+ readonly braid: Braid
137
+ readonly slot: Generation
138
+ }
139
+
140
+ /** The id-lease algebra's refusal arms: OverWidth | Exhausted. */
141
+ type LeaseRefusal =
142
+ | { readonly kind: "OverWidth"; readonly requested: bigint }
143
+ | { readonly kind: "Exhausted"; readonly relation: number; readonly field: number }
144
+
93
145
  const refusalData = new WeakMap<Error, RefusalCause>()
94
146
  const chainData = new WeakMap<Error, ChainMismatchData>()
95
147
  const contentionData = new WeakMap<Error, ContentionData>()
148
+ const ambiguousData = new WeakMap<Error, AmbiguousData>()
149
+ const overWidthData = new WeakMap<Error, OverWidthData>()
150
+ const exhaustedData = new WeakMap<Error, ExhaustedData>()
151
+ const slotRetiredData = new WeakMap<Error, SlotRetiredData>()
96
152
 
97
153
  function refuse(cause: RefusalCause, detail: string): never {
98
154
  const error = errors.wrap(ErrRefused, detail)
@@ -112,6 +168,38 @@ function throwContention(data: ContentionData, detail: string): never {
112
168
  throw error
113
169
  }
114
170
 
171
+ function refuseManifestMissing(detail: string): never {
172
+ throw errors.wrap(ErrManifestMissing, detail)
173
+ }
174
+
175
+ function isManifestMissing(error: unknown): boolean {
176
+ return error instanceof Error && errors.is(error, ErrManifestMissing)
177
+ }
178
+
179
+ function throwAmbiguous(data: AmbiguousData, detail: string): never {
180
+ const error = errors.wrap(ErrAmbiguous, detail)
181
+ ambiguousData.set(error, data)
182
+ throw error
183
+ }
184
+
185
+ function refuseOverWidth(data: OverWidthData, detail: string): never {
186
+ const error = errors.wrap(ErrOverWidth, detail)
187
+ overWidthData.set(error, data)
188
+ throw error
189
+ }
190
+
191
+ function refuseExhausted(data: ExhaustedData, detail: string): never {
192
+ const error = errors.wrap(ErrExhausted, detail)
193
+ exhaustedData.set(error, data)
194
+ throw error
195
+ }
196
+
197
+ function refuseSlotRetired(data: SlotRetiredData, detail: string): never {
198
+ const error = errors.wrap(ErrSlotRetired, detail)
199
+ slotRetiredData.set(error, data)
200
+ throw error
201
+ }
202
+
115
203
  function refusalOf(error: Error): RefusalCause | undefined {
116
204
  return refusalData.get(error)
117
205
  }
@@ -124,6 +212,22 @@ function contentionOf(error: Error): ContentionData | undefined {
124
212
  return contentionData.get(error)
125
213
  }
126
214
 
215
+ function ambiguousOf(error: Error): AmbiguousData | undefined {
216
+ return ambiguousData.get(error)
217
+ }
218
+
219
+ function overWidthOf(error: Error): OverWidthData | undefined {
220
+ return overWidthData.get(error)
221
+ }
222
+
223
+ function exhaustedOf(error: Error): ExhaustedData | undefined {
224
+ return exhaustedData.get(error)
225
+ }
226
+
227
+ function slotRetiredOf(error: Error): SlotRetiredData | undefined {
228
+ return slotRetiredData.get(error)
229
+ }
230
+
127
231
  /** Every store failure wraps the exported sentinel itself, so
128
232
  * `errors.is(e, ErrStore)` matches by identity; the vendor error's
129
233
  * message rides the detail verbatim. */
@@ -131,20 +235,46 @@ function wrapStore(inner: Error, detail: string): Error {
131
235
  return errors.wrap(ErrStore, `${detail}: ${inner.message}`)
132
236
  }
133
237
 
134
- export type { ChainCause, ChainMismatchData, ContentionCause, ContentionData, RefusalCause }
238
+ export type {
239
+ AmbiguousData,
240
+ AmbiguousVerb,
241
+ ChainCause,
242
+ ChainMismatchData,
243
+ ContentionCause,
244
+ ContentionData,
245
+ ExhaustedData,
246
+ LeaseRefusal,
247
+ OverWidthData,
248
+ RefusalCause,
249
+ SlotRetiredData
250
+ }
135
251
  export {
252
+ ambiguousOf,
136
253
  chainMismatchOf,
137
254
  contentionOf,
255
+ ErrAmbiguous,
138
256
  ErrChainMismatch,
139
257
  ErrContention,
258
+ ErrExhausted,
140
259
  ErrGapDetected,
260
+ ErrOverWidth,
141
261
  ErrRefused,
142
262
  ErrReplayDiverged,
263
+ ErrSlotRetired,
143
264
  ErrSpanningCommit,
144
265
  ErrStore,
266
+ exhaustedOf,
267
+ isManifestMissing,
268
+ overWidthOf,
145
269
  refusalOf,
146
270
  refuse,
147
271
  refuseChain,
272
+ refuseExhausted,
273
+ refuseManifestMissing,
274
+ refuseOverWidth,
275
+ refuseSlotRetired,
276
+ slotRetiredOf,
277
+ throwAmbiguous,
148
278
  throwContention,
149
279
  wrapStore
150
280
  }
package/src/index.ts CHANGED
@@ -3,16 +3,16 @@
3
3
  * bumbledb, a thin peer of the engine SDK. Public surface: the pure
4
4
  * protocol pair (`encodeBatch`/`decodeBatch`, `braidsOf`) mirrored
5
5
  * byte-exactly against the Rust driver, the five-verb object store
6
- * with its tier-1 `fsStore`, and `openReplica`/`openWriter`/
6
+ * with its tier-1 `fsStore`, in-process `memStore`, and the `s3Store` AWS S3 client, and `openReplica`/`openWriter`/
7
7
  * `openTenants` composed from the engine SDK's own verbs — the replica
8
8
  * hands out the SDK's `Db`, and no engine surface is duplicated.
9
9
  */
10
10
 
11
11
  export type { Braid } from "#braids.ts"
12
- export { braidsOf, serialAtStatementsOf } from "#braids.ts"
13
- export type { BatchHeader, BatchOp, ChainPosition, DecodedBatch } from "#codec.ts"
12
+ export { braid, braidsOf, serialAtStatementsOf } from "#braids.ts"
13
+ export type { BatchHeader, ChainEntry, DecodedBatch, EncodeHeader, Op } from "#codec.ts"
14
14
  export { decodeBatch, encodeBatch, verifyChain } from "#codec.ts"
15
- export type { LogDescriptor, LogTheory, SerialStatement } from "#descriptor.ts"
15
+ export type { Descriptor, SerialStatement, Theory } from "#descriptor.ts"
16
16
  export { descriptorOf } from "#descriptor.ts"
17
17
  export type { ChainCause, ChainMismatchData, ContentionCause, ContentionData, RefusalCause } from "#errors.ts"
18
18
  export {
@@ -21,18 +21,33 @@ export {
21
21
  ErrChainMismatch,
22
22
  ErrContention,
23
23
  ErrGapDetected,
24
+ ErrManifestMissing,
24
25
  ErrRefused,
25
26
  ErrReplayDiverged,
26
27
  ErrSpanningCommit,
27
28
  ErrStore,
29
+ isManifestMissing,
28
30
  refusalOf
29
31
  } from "#errors.ts"
32
+ export type { Generation, StoreKey } from "#keys.ts"
33
+ export {
34
+ CKPT_SCRATCH_LEASE,
35
+ encodeCkptScratch,
36
+ generation,
37
+ LEASE_NAMESPACE,
38
+ parseCkptScratch,
39
+ scratchCkptDigest,
40
+ scratchCkptName,
41
+ storeKey
42
+ } from "#keys.ts"
30
43
  export type { OpenReplicaOptions, Replica } from "#replica.ts"
31
44
  export { openReplica } from "#replica.ts"
32
- export type { Create, Fetched, ObjectStore, Poll, Swap } from "#store.ts"
33
- export { fsStore } from "#store.ts"
45
+ export type { Create, Etag, Fetched, ObjectStore, Poll, S3Config, S3Credentials, Swap } from "#store.ts"
46
+ export { etag, fsStore, memStore, s3Store } from "#store.ts"
34
47
  export type { OpenTenantsOptions, Tenants } from "#tenants.ts"
35
48
  export { openTenants } from "#tenants.ts"
36
- export type { LogInterval, LogValue } from "#value.ts"
37
- export type { BraidOutcome, Commit, CommitSplit, Durability, LogBatch, Writer } from "#writer.ts"
49
+ export type { Interval, Value } from "#value.ts"
50
+ export type { CheckpointOrder, VectorParse } from "#vector.ts"
51
+ export { Overflow, Vector } from "#vector.ts"
52
+ export type { Batch, BraidOutcome, Commit, CommitSplit, Durability, Writer } from "#writer.ts"
38
53
  export { openWriter } from "#writer.ts"
package/src/keys.ts CHANGED
@@ -1,34 +1,231 @@
1
1
  /**
2
- * The key layout (10): generation numbers zero-padded lowercase hex,
3
- * 16 chars; a prefix is a store; a tenant is a prefix under `t/`.
2
+ * The key layout: generation numbers zero-padded lowercase hex, 16
3
+ * chars; a prefix is a store; a tenant is a prefix under `t/`. A
4
+ * StoreKey is parsed here, once — the verbs take the proof and never
5
+ * re-check. An empty prefix joins as the rest alone, so a leading
6
+ * slash is unrepresentable. Temps and leases live under a reserved
7
+ * tilde-family first segment no StoreKey can spell — ASCII `~` and
8
+ * its lookalikes — so those namespaces stay disjoint from every
9
+ * honest key. Format characters, line and paragraph separators, and
10
+ * space separators cannot hide a reserved prefix or a `.lock` suffix.
4
11
  */
5
12
 
6
- function hex16(g: bigint): string {
13
+ import * as errors from "@superbuilders/errors"
14
+ import { regex } from "arkregex"
15
+ import type { Digest32 } from "#bytes.ts"
16
+ import { digest32, hex32, U64_MAX } from "#bytes.ts"
17
+ import type { Braid } from "#descriptor.ts"
18
+
19
+ /** A segment wearing this suffix — after format characters are stripped — is not a key. */
20
+ const LOCK_SUFFIX = ".lock"
21
+
22
+ /** Reserved first-segment names no StoreKey can spell. Temps and leases live here. */
23
+ const TEMP_NAMESPACE = "~tmp"
24
+ const LEASE_NAMESPACE = "~lease"
25
+
26
+ /** ASCII tilde and lookalikes that can spell a reserved first segment. */
27
+ const TILDE_FAMILY = new Set([
28
+ "~",
29
+ "\u02DC",
30
+ "\u02F7",
31
+ "\u1FC0",
32
+ "\u2053",
33
+ "\u223C",
34
+ "\u223D",
35
+ "\u223F",
36
+ "\u2E1E",
37
+ "\u2E1F",
38
+ "\u301C",
39
+ "\u3030",
40
+ "\uFE4B",
41
+ "\uFE4F",
42
+ "\uFF5E"
43
+ ])
44
+
45
+ const FORMAT_OR_SEPARATOR = regex("[\\p{Cc}\\p{Cf}\\p{Zl}\\p{Zp}\\p{Zs}]", "u")
46
+ const FORMAT_CHARS = regex("\\p{Cf}", "gu")
47
+ const SLASH_TRIM = regex("^/+|/+$", "g")
48
+
49
+ declare const storeKeyBrand: unique symbol
50
+ type StoreKey = string & { readonly [storeKeyBrand]: typeof storeKeyBrand }
51
+
52
+ declare const generationBrand: unique symbol
53
+ type Generation = bigint & { readonly [generationBrand]: typeof generationBrand }
54
+
55
+ function firstCodePoint(seg: string): string | undefined {
56
+ for (const ch of seg) {
57
+ return ch
58
+ }
59
+ return undefined
60
+ }
61
+
62
+ function tildeFamilyPrefix(seg: string): boolean {
63
+ const first = firstCodePoint(seg)
64
+ if (first === undefined) {
65
+ return false
66
+ }
67
+ return TILDE_FAMILY.has(first) || first.normalize("NFKC") === "~"
68
+ }
69
+
70
+ function stripFormat(seg: string): string {
71
+ return seg.replace(FORMAT_CHARS, "")
72
+ }
73
+
74
+ /** One path segment of a key or a tenant id: the same grammar. */
75
+ function segmentOk(seg: string): boolean {
76
+ if (seg.length === 0 || seg.includes("/") || seg === "." || seg === "..") {
77
+ return false
78
+ }
79
+ if (FORMAT_OR_SEPARATOR.test(seg)) {
80
+ return false
81
+ }
82
+ const stripped = stripFormat(seg)
83
+ if (stripped.length === 0 || stripped === "." || stripped === "..") {
84
+ return false
85
+ }
86
+ return !tildeFamilyPrefix(stripped) && !stripped.endsWith(LOCK_SUFFIX)
87
+ }
88
+
89
+ function storeKey(raw: string): StoreKey {
90
+ const wellFormed = raw.length > 0 && !raw.startsWith("/") && !raw.endsWith("/") && raw.split("/").every(segmentOk)
91
+ if (!wellFormed) {
92
+ throw errors.new(`store key is not a slash path: ${raw}`)
93
+ }
94
+ return raw as StoreKey
95
+ }
96
+
97
+ /** Empty, or a StoreKey spelling (the same segment grammar, no leading or trailing slash). */
98
+ function parsePrefix(raw: string): string {
99
+ if (raw.length === 0) {
100
+ return ""
101
+ }
102
+ return storeKey(raw.replace(SLASH_TRIM, ""))
103
+ }
104
+
105
+ function isReservedName(name: string): boolean {
106
+ return tildeFamilyPrefix(stripFormat(name))
107
+ }
108
+
109
+ function reservedTemp(pid: number, seq: number): string {
110
+ return `${TEMP_NAMESPACE}/${String(pid)}.${String(seq)}`
111
+ }
112
+
113
+ function reservedLease(key: string, token: bigint): string {
114
+ return `${LEASE_NAMESPACE}/${key}/${String(token)}`
115
+ }
116
+
117
+ /** A slash path whose first segment is `~tmp` or `~lease`. Not a StoreKey. */
118
+ function reservedName(raw: string): string {
119
+ const segs = raw.split("/")
120
+ if (segs.length === 0 || segs.some((seg) => seg.length === 0 || seg === "." || seg === "..")) {
121
+ throw errors.new(`reserved name is not a slash path: ${raw}`)
122
+ }
123
+ const first = segs[0]
124
+ if (first !== TEMP_NAMESPACE && first !== LEASE_NAMESPACE) {
125
+ throw errors.new(`reserved name is not under ${TEMP_NAMESPACE} or ${LEASE_NAMESPACE}: ${raw}`)
126
+ }
127
+ return raw
128
+ }
129
+
130
+ /** Known scratch-lease document under `~lease`. One path, no LIST. */
131
+ const CKPT_SCRATCH_LEASE = "ckpt-scratch"
132
+
133
+ /** The reserved scratch name: `~lease/ckpt-scratch`. */
134
+ function scratchCkptName(): string {
135
+ return reservedName(`${LEASE_NAMESPACE}/${CKPT_SCRATCH_LEASE}`)
136
+ }
137
+
138
+ const SCRATCH_VERSION = 3
139
+
140
+ /** The scratch-lease body: version byte 3, then the 32-byte digest. */
141
+ function encodeCkptScratch(digest: Digest32): Uint8Array {
142
+ const out = new Uint8Array(33)
143
+ out[0] = SCRATCH_VERSION
144
+ out.set(digest, 1)
145
+ return out
146
+ }
147
+
148
+ /** The digest a scratch-lease body names, or null. */
149
+ function scratchCkptDigest(bytes: Uint8Array): Digest32 | null {
150
+ if (bytes.length !== 33) {
151
+ return null
152
+ }
153
+ const version = bytes[0]
154
+ if (version !== SCRATCH_VERSION) {
155
+ return null
156
+ }
157
+ return digest32(bytes.subarray(1))
158
+ }
159
+
160
+ function parseCkptScratch(bytes: Uint8Array): Digest32 | null {
161
+ return scratchCkptDigest(bytes)
162
+ }
163
+
164
+ function generation(raw: bigint): Generation {
165
+ if (raw < 0n || raw > U64_MAX) {
166
+ throw errors.new(`generation is a u64: ${raw}`)
167
+ }
168
+ return raw as Generation
169
+ }
170
+
171
+ function hex16(g: Generation): string {
7
172
  return g.toString(16).padStart(16, "0")
8
173
  }
9
174
 
10
- function manifestKey(prefix: string): string {
11
- return `${prefix}/manifest.json`
175
+ function assemble(prefix: string, rest: string): StoreKey {
176
+ return storeKey(prefix.length === 0 ? rest : `${prefix}/${rest}`)
12
177
  }
13
178
 
14
- function logKey(prefix: string, braid: string, g: bigint): string {
15
- return `${prefix}/log/${braid}/${hex16(g)}`
179
+ function manifestKey(prefix: string): StoreKey {
180
+ return assemble(prefix, "manifest")
16
181
  }
17
182
 
18
- function checkpointMdbKey(prefix: string, digest: string): string {
19
- return `${prefix}/ckpt/${digest}.mdb`
183
+ function logKey(prefix: string, braid: Braid, g: Generation): StoreKey {
184
+ return assemble(prefix, `log/${braid}/${hex16(g)}`)
20
185
  }
21
186
 
22
- function checkpointJsonKey(prefix: string, digest: string): string {
23
- return `${prefix}/ckpt/${digest}.json`
187
+ function checkpointMdbKey(prefix: string, digest: Digest32): StoreKey {
188
+ return assemble(prefix, `ckpt/${hex32(digest)}.mdb`)
24
189
  }
25
190
 
26
- function idsKey(prefix: string, relation: number, field: number): string {
27
- return `${prefix}/ids/${relation.toString(16).padStart(8, "0")}/${field.toString(16).padStart(4, "0")}`
191
+ function ckptDocKey(prefix: string, digest: Digest32): StoreKey {
192
+ return assemble(prefix, `ckpt/${hex32(digest)}`)
193
+ }
194
+
195
+ function idsKey(prefix: string, relation: number, field: number): StoreKey {
196
+ return assemble(prefix, `ids/${relation.toString(16).padStart(8, "0")}/${field.toString(16).padStart(4, "0")}`)
28
197
  }
29
198
 
30
199
  function tenantPrefix(root: string, tenant: string): string {
31
- return `${root}/t/${tenant}`
200
+ if (!segmentOk(tenant)) {
201
+ throw errors.new(`tenant id is not a single path segment: ${tenant}`)
202
+ }
203
+ return assemble(root, `t/${tenant}`)
32
204
  }
33
205
 
34
- export { checkpointJsonKey, checkpointMdbKey, hex16, idsKey, logKey, manifestKey, tenantPrefix }
206
+ export type { Generation, StoreKey }
207
+ export {
208
+ CKPT_SCRATCH_LEASE,
209
+ checkpointMdbKey,
210
+ ckptDocKey,
211
+ encodeCkptScratch,
212
+ generation,
213
+ hex16,
214
+ idsKey,
215
+ isReservedName,
216
+ LEASE_NAMESPACE,
217
+ LOCK_SUFFIX,
218
+ logKey,
219
+ manifestKey,
220
+ parseCkptScratch,
221
+ parsePrefix,
222
+ reservedLease,
223
+ reservedName,
224
+ reservedTemp,
225
+ scratchCkptDigest,
226
+ scratchCkptName,
227
+ segmentOk,
228
+ storeKey,
229
+ TEMP_NAMESPACE,
230
+ tenantPrefix
231
+ }