@bjornpagen/bumbledb-log 0.17.0 → 0.18.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
@@ -8,6 +8,8 @@
8
8
  */
9
9
 
10
10
  import * as errors from "@superbuilders/errors"
11
+ import type { Braid } from "#descriptor.ts"
12
+ import type { Generation } from "#keys.ts"
11
13
 
12
14
  /**
13
15
  * Typed refusal of a protocol object before any apply: batch shape,
@@ -54,7 +56,7 @@ type RefusalCause =
54
56
  | { readonly kind: "UnknownOpKind"; readonly op: number; readonly opKind: number }
55
57
  | { readonly kind: "UnknownRelation"; readonly op: number; readonly relation: number }
56
58
  | { readonly kind: "ClosedRelation"; readonly op: number; readonly relation: number }
57
- | { readonly kind: "OpRelationOutsideBraid"; readonly op: number; readonly relation: number; readonly braid: string }
59
+ | { readonly kind: "OpRelationOutsideBraid"; readonly op: number; readonly relation: number; readonly braid: Braid }
58
60
  | { readonly kind: "TagMismatch"; readonly relation: string; readonly row: number; readonly field: string }
59
61
  | { readonly kind: "BoolByte"; readonly relation: string; readonly row: number; readonly field: string }
60
62
  | { readonly kind: "InvalidUtf8"; readonly relation: string; readonly row: number; readonly field: string }
@@ -66,14 +68,14 @@ type RefusalCause =
66
68
  | { readonly kind: "CheckpointShape" }
67
69
  | { readonly kind: "CheckpointBraids"; readonly carried: readonly string[]; readonly derived: readonly string[] }
68
70
  | { readonly kind: "CheckpointDigest"; readonly expected: string; readonly computed: string }
69
- | { readonly kind: "NoOpSlot"; readonly braid: string; readonly slot: bigint; readonly writer: bigint }
71
+ | { readonly kind: "NoOpSlot"; readonly braid: Braid; readonly slot: Generation; readonly writer: bigint }
70
72
 
71
73
  type ChainCause = "prev" | "slot" | "timestamp"
72
74
 
73
75
  interface ChainMismatchData {
74
76
  readonly cause: ChainCause
75
- readonly braid: string
76
- readonly slot: bigint
77
+ readonly braid: Braid
78
+ readonly slot: Generation
77
79
  readonly writer: bigint
78
80
  }
79
81
 
@@ -83,10 +85,10 @@ type ContentionCause =
83
85
  readonly statement: string
84
86
  readonly determinants: ReadonlyArray<Readonly<Record<string, unknown>>>
85
87
  }
86
- | { readonly kind: "slot-race"; readonly tip: bigint }
88
+ | { readonly kind: "slot-race"; readonly tip: Generation }
87
89
 
88
90
  interface ContentionData {
89
- readonly braid: string
91
+ readonly braid: Braid
90
92
  readonly cause: ContentionCause
91
93
  }
92
94
 
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, 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 {
@@ -27,12 +27,14 @@ export {
27
27
  ErrStore,
28
28
  refusalOf
29
29
  } from "#errors.ts"
30
+ export type { Generation, StoreKey } from "#keys.ts"
31
+ export { generation, storeKey } from "#keys.ts"
30
32
  export type { OpenReplicaOptions, Replica } from "#replica.ts"
31
33
  export { openReplica } from "#replica.ts"
32
- export type { Create, Fetched, ObjectStore, Poll, Swap } from "#store.ts"
33
- export { fsStore } from "#store.ts"
34
+ export type { Create, Etag, Fetched, ObjectStore, Poll, S3Config, S3Credentials, Swap } from "#store.ts"
35
+ export { etag, fsStore, memStore, s3Store } from "#store.ts"
34
36
  export type { OpenTenantsOptions, Tenants } from "#tenants.ts"
35
37
  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"
38
+ export type { Interval, Value } from "#value.ts"
39
+ export type { Batch, BraidOutcome, Commit, CommitSplit, Durability, Writer } from "#writer.ts"
38
40
  export { openWriter } from "#writer.ts"
package/src/keys.ts CHANGED
@@ -1,34 +1,88 @@
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.
4
7
  */
5
8
 
6
- function hex16(g: bigint): string {
9
+ import * as errors from "@superbuilders/errors"
10
+ import { U64_MAX } from "#bytes.ts"
11
+ import type { Braid } from "#descriptor.ts"
12
+
13
+ /** Suffix of the per-key pid-lockfile. A segment wearing it cannot be a key. */
14
+ const LOCK_SUFFIX = ".lock"
15
+
16
+ declare const storeKeyBrand: unique symbol
17
+ type StoreKey = string & { readonly [storeKeyBrand]: typeof storeKeyBrand }
18
+
19
+ declare const generationBrand: unique symbol
20
+ type Generation = bigint & { readonly [generationBrand]: typeof generationBrand }
21
+
22
+ function storeKey(raw: string): StoreKey {
23
+ if (raw.length === 0 || raw.startsWith("/") || raw.endsWith("/")) {
24
+ throw errors.new(`store key is not a slash path: ${raw}`)
25
+ }
26
+ for (const segment of raw.split("/")) {
27
+ if (segment.length === 0 || segment === "." || segment === "..") {
28
+ throw errors.new(`store key segment is illegal: ${raw}`)
29
+ }
30
+ if (segment.endsWith(LOCK_SUFFIX)) {
31
+ throw errors.new(`store key collides with the lockfile suffix: ${raw}`)
32
+ }
33
+ }
34
+ return raw as StoreKey
35
+ }
36
+
37
+ function generation(raw: bigint): Generation {
38
+ if (raw < 0n || raw > U64_MAX) {
39
+ throw errors.new(`generation is a u64: ${raw}`)
40
+ }
41
+ return raw as Generation
42
+ }
43
+
44
+ function hex16(g: Generation): string {
7
45
  return g.toString(16).padStart(16, "0")
8
46
  }
9
47
 
10
- function manifestKey(prefix: string): string {
11
- return `${prefix}/manifest.json`
48
+ function assemble(prefix: string, rest: string): StoreKey {
49
+ return storeKey(prefix.length === 0 ? rest : `${prefix}/${rest}`)
12
50
  }
13
51
 
14
- function logKey(prefix: string, braid: string, g: bigint): string {
15
- return `${prefix}/log/${braid}/${hex16(g)}`
52
+ function manifestKey(prefix: string): StoreKey {
53
+ return assemble(prefix, "manifest.json")
16
54
  }
17
55
 
18
- function checkpointMdbKey(prefix: string, digest: string): string {
19
- return `${prefix}/ckpt/${digest}.mdb`
56
+ function logKey(prefix: string, braid: Braid, g: Generation): StoreKey {
57
+ return assemble(prefix, `log/${braid}/${hex16(g)}`)
20
58
  }
21
59
 
22
- function checkpointJsonKey(prefix: string, digest: string): string {
23
- return `${prefix}/ckpt/${digest}.json`
60
+ function checkpointMdbKey(prefix: string, digest: string): StoreKey {
61
+ return assemble(prefix, `ckpt/${digest}.mdb`)
24
62
  }
25
63
 
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")}`
64
+ function checkpointJsonKey(prefix: string, digest: string): StoreKey {
65
+ return assemble(prefix, `ckpt/${digest}.json`)
66
+ }
67
+
68
+ function idsKey(prefix: string, relation: number, field: number): StoreKey {
69
+ return assemble(prefix, `ids/${relation.toString(16).padStart(8, "0")}/${field.toString(16).padStart(4, "0")}`)
28
70
  }
29
71
 
30
72
  function tenantPrefix(root: string, tenant: string): string {
31
- return `${root}/t/${tenant}`
73
+ return assemble(root, `t/${tenant}`)
32
74
  }
33
75
 
34
- export { checkpointJsonKey, checkpointMdbKey, hex16, idsKey, logKey, manifestKey, tenantPrefix }
76
+ export type { Generation, StoreKey }
77
+ export {
78
+ checkpointJsonKey,
79
+ checkpointMdbKey,
80
+ generation,
81
+ hex16,
82
+ idsKey,
83
+ LOCK_SUFFIX,
84
+ logKey,
85
+ manifestKey,
86
+ storeKey,
87
+ tenantPrefix
88
+ }
package/src/manifest.ts CHANGED
@@ -8,7 +8,11 @@
8
8
 
9
9
  import * as errors from "@superbuilders/errors"
10
10
  import { utf8Encoder, utf8StrictDecoder } from "#bytes.ts"
11
+ import type { Braid } from "#descriptor.ts"
12
+ import { braid } from "#descriptor.ts"
11
13
  import { refuse } from "#errors.ts"
14
+ import type { Generation } from "#keys.ts"
15
+ import { generation } from "#keys.ts"
12
16
 
13
17
  interface Manifest {
14
18
  readonly fingerprint: string
@@ -59,13 +63,13 @@ function parseManifest(bytes: Uint8Array): Manifest {
59
63
  }
60
64
 
61
65
  interface CheckpointHead {
62
- readonly g: bigint
66
+ readonly g: Generation
63
67
  readonly hash: string
64
68
  readonly ts: bigint
65
69
  }
66
70
 
67
71
  interface CheckpointFacts {
68
- readonly braids: ReadonlyMap<string, CheckpointHead>
72
+ readonly braids: ReadonlyMap<Braid, CheckpointHead>
69
73
  readonly catalog: string
70
74
  readonly writer: bigint
71
75
  readonly prev: string | null
@@ -83,19 +87,19 @@ function parseCheckpoint(bytes: Uint8Array): CheckpointFacts {
83
87
  if (typeof rawBraids !== "object" || rawBraids === null) {
84
88
  refuse({ kind: "CheckpointShape" }, "checkpoint json carries no braids map")
85
89
  }
86
- const braids = new Map<string, CheckpointHead>()
87
- for (const [braid, head] of Object.entries(rawBraids as Record<string, unknown>)) {
90
+ const braids = new Map<Braid, CheckpointHead>()
91
+ for (const [name, head] of Object.entries(rawBraids as Record<string, unknown>)) {
88
92
  if (typeof head !== "object" || head === null) {
89
- refuse({ kind: "CheckpointShape" }, `checkpoint braid ${braid} head is not an object`)
93
+ refuse({ kind: "CheckpointShape" }, `checkpoint braid ${name} head is not an object`)
90
94
  }
91
95
  const headRecord = head as Record<string, unknown>
92
96
  const g = headRecord.g
93
97
  const hash = headRecord.hash
94
98
  const ts = headRecord.ts
95
99
  if (typeof g !== "number" || typeof ts !== "number" || typeof hash !== "string" || !HEX64.test(hash)) {
96
- refuse({ kind: "CheckpointShape" }, `checkpoint braid ${braid} head is malformed`)
100
+ refuse({ kind: "CheckpointShape" }, `checkpoint braid ${name} head is malformed`)
97
101
  }
98
- braids.set(braid, { g: BigInt(g), hash, ts: BigInt(ts) })
102
+ braids.set(braid(name), { g: generation(BigInt(g)), hash, ts: BigInt(ts) })
99
103
  }
100
104
  const catalog = record.catalog
101
105
  const writer = record.writer
package/src/replica.ts CHANGED
@@ -14,18 +14,19 @@ import type { Db, Fact, MemberRelation, Schema, SchemaRelations, WriteOutcome }
14
14
  import { internalBlake3, Db as SdkDb } from "@bjornpagen/bumbledb"
15
15
  import * as errors from "@superbuilders/errors"
16
16
  import { bytesEqual, toHex } from "#bytes.ts"
17
- import type { ChainEntry, PendingBatch } from "#chain.ts"
17
+ import type { ChainEntry, Pending } from "#chain.ts"
18
18
  import { readSidecar, writeSidecar } from "#chain.ts"
19
- import type { BatchOp } from "#codec.ts"
19
+ import type { Op } from "#codec.ts"
20
20
  import { decodeBatch, encodeBatch, verifyChain } from "#codec.ts"
21
- import type { LogDescriptor, RelationInfo } from "#descriptor.ts"
21
+ import type { Braid, Descriptor, RelationInfo } from "#descriptor.ts"
22
22
  import { descriptorOf } from "#descriptor.ts"
23
23
  import { ErrGapDetected, ErrReplayDiverged, refuse } from "#errors.ts"
24
- import { checkpointJsonKey, checkpointMdbKey, logKey, manifestKey } from "#keys.ts"
24
+ import type { Generation } from "#keys.ts"
25
+ import { checkpointJsonKey, checkpointMdbKey, generation, logKey, manifestKey } from "#keys.ts"
25
26
  import type { CheckpointFacts } from "#manifest.ts"
26
27
  import { parseCheckpoint, parseManifest, renderManifest } from "#manifest.ts"
27
- import type { ObjectStore } from "#store.ts"
28
- import type { LogValue } from "#value.ts"
28
+ import type { Etag, ObjectStore } from "#store.ts"
29
+ import type { Value } from "#value.ts"
29
30
 
30
31
  const ZERO_HASH = "0".repeat(64)
31
32
 
@@ -45,9 +46,9 @@ interface OpenReplicaOptions<Rels extends SchemaRelations> {
45
46
 
46
47
  interface Replica<Rels extends SchemaRelations> extends AsyncDisposable {
47
48
  readonly db: Db<Rels>
48
- readonly vector: ReadonlyMap<string, bigint>
49
- refresh(braid?: string): Promise<ReadonlyMap<string, bigint>>
50
- waitFor(vector: ReadonlyMap<string, bigint>): Promise<void>
49
+ readonly vector: ReadonlyMap<Braid, Generation>
50
+ refresh(braid?: Braid): Promise<ReadonlyMap<Braid, Generation>>
51
+ waitFor(vector: ReadonlyMap<Braid, Generation>): Promise<void>
51
52
  }
52
53
 
53
54
  interface Core<Rels extends SchemaRelations> {
@@ -55,13 +56,13 @@ interface Core<Rels extends SchemaRelations> {
55
56
  readonly prefix: string
56
57
  readonly dir: string
57
58
  readonly theory: Schema<Rels>
58
- readonly descriptor: LogDescriptor
59
+ readonly descriptor: Descriptor
59
60
  db: Db<Rels>
60
- chain: Map<string, ChainEntry>
61
- pending: PendingBatch | null
62
- pendingOps: readonly BatchOp[] | null
61
+ chain: Map<Braid, ChainEntry>
62
+ pending: Pending | null
63
+ pendingOps: readonly Op[] | null
63
64
  pendingApplied: boolean
64
- manifestEtag: string | null
65
+ manifestEtag: Etag | null
65
66
  checkpoint: CheckpointFacts | null
66
67
  checkpointDigest: string | null
67
68
  passes: number
@@ -117,15 +118,15 @@ function storePath<Rels extends SchemaRelations>(core: Core<Rels>): string {
117
118
  return path.join(core.dir, core.storeName)
118
119
  }
119
120
 
120
- function zeroChain(descriptor: LogDescriptor): Map<string, ChainEntry> {
121
- const chain = new Map<string, ChainEntry>()
122
- for (const braid of descriptor.braidMembers.keys()) {
123
- chain.set(braid, { g: 0n, prev: ZERO_HASH, ts: 0n })
121
+ function zeroChain(descriptor: Descriptor): Map<Braid, ChainEntry> {
122
+ const chain = new Map<Braid, ChainEntry>()
123
+ for (const id of descriptor.braidMembers.keys()) {
124
+ chain.set(id, { g: generation(0n), prev: ZERO_HASH, ts: 0n })
124
125
  }
125
126
  return chain
126
127
  }
127
128
 
128
- function chainEntry<Rels extends SchemaRelations>(core: Core<Rels>, braid: string): ChainEntry {
129
+ function chainEntry<Rels extends SchemaRelations>(core: Core<Rels>, braid: Braid): ChainEntry {
129
130
  const entry = core.chain.get(braid)
130
131
  if (entry === undefined) {
131
132
  throw errors.new(`braid ${braid} is not derived from this theory`)
@@ -166,7 +167,7 @@ async function clearPending<Rels extends SchemaRelations>(core: Core<Rels>): Pro
166
167
  function factOf<Rels extends SchemaRelations>(
167
168
  core: Core<Rels>,
168
169
  relation: RelationInfo,
169
- row: readonly LogValue[]
170
+ row: readonly Value[]
170
171
  ): Record<string, unknown> {
171
172
  const fact: Record<string, unknown> = {}
172
173
  relation.fields.forEach(function liftCell(field, ordinal) {
@@ -191,7 +192,7 @@ function factOf<Rels extends SchemaRelations>(
191
192
  }
192
193
 
193
194
  /** One `db.write` applying ops in listed order, rows in listed order. */
194
- function applyOps<Rels extends SchemaRelations>(core: Core<Rels>, ops: readonly BatchOp[]): WriteOutcome<Rels, number> {
195
+ function applyOps<Rels extends SchemaRelations>(core: Core<Rels>, ops: readonly Op[]): WriteOutcome<Rels, number> {
195
196
  return core.db.write(function applyBatch(tx) {
196
197
  for (const op of ops) {
197
198
  const info = core.descriptor.relationByName.get(op.relation)
@@ -225,8 +226,8 @@ type SlotApply = { readonly tag: "applied"; readonly generation: bigint } | { re
225
226
  */
226
227
  async function applySlot<Rels extends SchemaRelations>(
227
228
  core: Core<Rels>,
228
- braid: string,
229
- slot: bigint,
229
+ braid: Braid,
230
+ slot: Generation,
230
231
  bytes: Uint8Array,
231
232
  phase: ApplyPhase
232
233
  ): Promise<SlotApply> {
@@ -256,7 +257,7 @@ async function applySlot<Rels extends SchemaRelations>(
256
257
  * The gc floor rule (10): below the current checkpoint's vector a 404 is
257
258
  * a collected hole, at or above it the honest tip.
258
259
  */
259
- function holeAt<Rels extends SchemaRelations>(core: Core<Rels>, braid: string, next: bigint): boolean {
260
+ function holeAt<Rels extends SchemaRelations>(core: Core<Rels>, braid: Braid, next: Generation): boolean {
260
261
  if (core.checkpoint === null) {
261
262
  return false
262
263
  }
@@ -269,11 +270,11 @@ function holeAt<Rels extends SchemaRelations>(core: Core<Rels>, braid: string, n
269
270
 
270
271
  async function catchUpBraid<Rels extends SchemaRelations>(
271
272
  core: Core<Rels>,
272
- braid: string,
273
+ braid: Braid,
273
274
  phase: ApplyPhase
274
275
  ): Promise<"tip" | "discard"> {
275
276
  for (;;) {
276
- const next = chainEntry(core, braid).g + 1n
277
+ const next = generation(chainEntry(core, braid).g + 1n)
277
278
  const fetched = await core.store.get(logKey(core.prefix, braid, next))
278
279
  if (fetched === null) {
279
280
  if (holeAt(core, braid, next)) {
@@ -322,7 +323,7 @@ function wholenessHolds<Rels extends SchemaRelations>(core: Core<Rels>): boolean
322
323
  async function adoptManifest<Rels extends SchemaRelations>(
323
324
  core: Core<Rels>,
324
325
  bytes: Uint8Array,
325
- etag: string
326
+ etag: Etag
326
327
  ): Promise<void> {
327
328
  const manifest = parseManifest(bytes)
328
329
  if (manifest.fingerprint !== core.descriptor.fingerprint) {
@@ -502,7 +503,7 @@ async function resolvePendingAtOpen<Rels extends SchemaRelations>(core: Core<Rel
502
503
  return
503
504
  }
504
505
  const decoded = errors.trySync(function decodePending() {
505
- return decodeBatch(core.descriptor, (core.pending as PendingBatch).bytes)
506
+ return decodeBatch(core.descriptor, (core.pending as Pending).bytes)
506
507
  })
507
508
  if (decoded.error) {
508
509
  await clearPending(core)
@@ -529,10 +530,7 @@ async function resolvePendingAtOpen<Rels extends SchemaRelations>(core: Core<Rel
529
530
  * absorbs it (L10's idempotent replay), and everything else takes the
530
531
  * one loss path's re-judgment at the tip.
531
532
  */
532
- async function resolveColdPending<Rels extends SchemaRelations>(
533
- core: Core<Rels>,
534
- pending: PendingBatch
535
- ): Promise<void> {
533
+ async function resolveColdPending<Rels extends SchemaRelations>(core: Core<Rels>, pending: Pending): Promise<void> {
536
534
  const decoded = errors.trySync(function decodePending() {
537
535
  return decodeBatch(core.descriptor, pending.bytes)
538
536
  })
@@ -583,7 +581,7 @@ async function openCore<Rels extends SchemaRelations>(options: OpenReplicaOption
583
581
  const existing = await newestStoreDir(core.dir)
584
582
  const sidecar = await readSidecar(sidecarPath(core))
585
583
  let opened = false
586
- let coldPending: PendingBatch | null = null
584
+ let coldPending: Pending | null = null
587
585
  if (existing !== null) {
588
586
  core.storeName = existing
589
587
  const openedDb = await errors.try(SdkDb.open(storePath(core), options.theory))
@@ -592,7 +590,7 @@ async function openCore<Rels extends SchemaRelations>(options: OpenReplicaOption
592
590
  core.chain = new Map(sidecar.chain)
593
591
  for (const braid of descriptor.braidMembers.keys()) {
594
592
  if (!core.chain.has(braid)) {
595
- core.chain.set(braid, { g: 0n, prev: ZERO_HASH, ts: 0n })
593
+ core.chain.set(braid, { g: generation(0n), prev: ZERO_HASH, ts: 0n })
596
594
  }
597
595
  }
598
596
  core.pending = sidecar.pending
@@ -645,7 +643,7 @@ async function repairDiscard<Rels extends SchemaRelations>(core: Core<Rels>): Pr
645
643
  */
646
644
  async function readdressPending<Rels extends SchemaRelations>(
647
645
  core: Core<Rels>,
648
- ops: readonly BatchOp[],
646
+ ops: readonly Op[],
649
647
  writerId: bigint
650
648
  ): Promise<void> {
651
649
  const first = ops[0]
@@ -666,14 +664,14 @@ async function readdressPending<Rels extends SchemaRelations>(
666
664
  {
667
665
  fingerprint: core.descriptor.fingerprint,
668
666
  braid,
669
- braidGen: entry.g + 1n,
667
+ braidGen: generation(entry.g + 1n),
670
668
  prev: entry.prev,
671
669
  writer: writerId,
672
670
  timestamp
673
671
  },
674
672
  ops
675
673
  )
676
- core.pending = { braid, gen: entry.g + 1n, bytes }
674
+ core.pending = { braid, gen: generation(entry.g + 1n), bytes }
677
675
  core.pendingOps = ops
678
676
  core.pendingApplied = true
679
677
  await persistSidecar(core)
@@ -683,15 +681,15 @@ function maxBigint(a: bigint, b: bigint): bigint {
683
681
  return a > b ? a : b
684
682
  }
685
683
 
686
- function vectorOf<Rels extends SchemaRelations>(core: Core<Rels>): ReadonlyMap<string, bigint> {
687
- const vector = new Map<string, bigint>()
688
- for (const [braid, entry] of core.chain) {
689
- vector.set(braid, entry.g)
684
+ function vectorOf<Rels extends SchemaRelations>(core: Core<Rels>): ReadonlyMap<Braid, Generation> {
685
+ const vector = new Map<Braid, Generation>()
686
+ for (const [id, entry] of core.chain) {
687
+ vector.set(id, entry.g)
690
688
  }
691
689
  return vector
692
690
  }
693
691
 
694
- function dominates(have: ReadonlyMap<string, bigint>, want: ReadonlyMap<string, bigint>): boolean {
692
+ function dominates(have: ReadonlyMap<Braid, Generation>, want: ReadonlyMap<Braid, Generation>): boolean {
695
693
  for (const [braid, generation] of want) {
696
694
  if ((have.get(braid) ?? -1n) < generation) {
697
695
  return false
@@ -700,7 +698,7 @@ function dominates(have: ReadonlyMap<string, bigint>, want: ReadonlyMap<string,
700
698
  return true
701
699
  }
702
700
 
703
- async function refreshPass<Rels extends SchemaRelations>(core: Core<Rels>, braid?: string): Promise<void> {
701
+ async function refreshPass<Rels extends SchemaRelations>(core: Core<Rels>, braid?: Braid): Promise<void> {
704
702
  core.passes += 1
705
703
  if (core.passes % HEARTBEAT_PASSES === 0) {
706
704
  await refreshManifest(core, false)
@@ -729,7 +727,7 @@ async function openReplica<Rels extends SchemaRelations>(options: OpenReplicaOpt
729
727
  get vector() {
730
728
  return vectorOf(core)
731
729
  },
732
- async refresh(braid?: string) {
730
+ async refresh(braid?: Braid) {
733
731
  return withGate(core, async function refreshBody() {
734
732
  if (core.closed) {
735
733
  throw errors.new("replica is disposed")