@atproto/repo 0.2.0 → 0.3.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 (79) hide show
  1. package/dist/data-diff.d.ts +11 -9
  2. package/dist/index.d.ts +0 -1
  3. package/dist/index.js +404 -1655
  4. package/dist/index.js.map +4 -4
  5. package/dist/mst/mst.d.ts +10 -6
  6. package/dist/readable-repo.d.ts +1 -1
  7. package/dist/repo.d.ts +1 -4
  8. package/dist/storage/index.d.ts +0 -1
  9. package/dist/storage/memory-blockstore.d.ts +7 -12
  10. package/dist/storage/types.d.ts +28 -0
  11. package/dist/sync/consumer.d.ts +13 -17
  12. package/dist/sync/provider.d.ts +1 -5
  13. package/dist/types.d.ts +228 -39
  14. package/dist/util.d.ts +3 -5
  15. package/package.json +2 -2
  16. package/src/data-diff.ts +46 -44
  17. package/src/index.ts +0 -1
  18. package/src/mst/diff.ts +14 -36
  19. package/src/mst/mst.ts +14 -4
  20. package/src/readable-repo.ts +3 -3
  21. package/src/repo.ts +49 -70
  22. package/src/storage/index.ts +0 -1
  23. package/src/storage/memory-blockstore.ts +18 -77
  24. package/src/storage/types.ts +29 -0
  25. package/src/sync/consumer.ts +170 -116
  26. package/src/sync/provider.ts +2 -40
  27. package/src/types.ts +49 -23
  28. package/src/util.ts +24 -79
  29. package/tests/_util.ts +38 -67
  30. package/tests/mst.test.ts +4 -1
  31. package/tests/{sync/narrow.test.ts → proofs.test.ts} +9 -20
  32. package/tests/repo.test.ts +5 -4
  33. package/tests/sync.test.ts +97 -0
  34. package/tsconfig.build.tsbuildinfo +1 -1
  35. package/dist/src/block-map.d.ts +0 -23
  36. package/dist/src/blockstore/index.d.ts +0 -2
  37. package/dist/src/blockstore/ipld-store.d.ts +0 -27
  38. package/dist/src/blockstore/memory-blockstore.d.ts +0 -13
  39. package/dist/src/blockstore/persistent-blockstore.d.ts +0 -12
  40. package/dist/src/cid-set.d.ts +0 -14
  41. package/dist/src/collection.d.ts +0 -22
  42. package/dist/src/data-diff.d.ts +0 -34
  43. package/dist/src/error.d.ts +0 -21
  44. package/dist/src/index.d.ts +0 -7
  45. package/dist/src/logger.d.ts +0 -2
  46. package/dist/src/mst/diff.d.ts +0 -33
  47. package/dist/src/mst/index.d.ts +0 -4
  48. package/dist/src/mst/mst.d.ts +0 -106
  49. package/dist/src/mst/util.d.ts +0 -9
  50. package/dist/src/mst/walker.d.ts +0 -22
  51. package/dist/src/parse.d.ts +0 -11
  52. package/dist/src/readable-repo.d.ts +0 -25
  53. package/dist/src/repo.d.ts +0 -39
  54. package/dist/src/storage/error.d.ts +0 -22
  55. package/dist/src/storage/index.d.ts +0 -1
  56. package/dist/src/storage/memory-blobstore.d.ts +0 -1
  57. package/dist/src/storage/memory-blockstore.d.ts +0 -28
  58. package/dist/src/storage/readable-blockstore.d.ts +0 -21
  59. package/dist/src/storage/repo-storage.d.ts +0 -18
  60. package/dist/src/storage/sync-storage.d.ts +0 -15
  61. package/dist/src/storage/types.d.ts +0 -12
  62. package/dist/src/storage/util.d.ts +0 -17
  63. package/dist/src/structure.d.ts +0 -39
  64. package/dist/src/sync/consumer.d.ts +0 -19
  65. package/dist/src/sync/index.d.ts +0 -2
  66. package/dist/src/sync/producer.d.ts +0 -13
  67. package/dist/src/sync/provider.d.ts +0 -11
  68. package/dist/src/sync.d.ts +0 -9
  69. package/dist/src/types.d.ts +0 -368
  70. package/dist/src/util.d.ts +0 -13
  71. package/dist/src/verify.d.ts +0 -5
  72. package/dist/storage/repo-storage.d.ts +0 -19
  73. package/dist/tsconfig.build.tsbuildinfo +0 -1
  74. package/dist/verify.d.ts +0 -32
  75. package/src/storage/repo-storage.ts +0 -43
  76. package/src/verify.ts +0 -268
  77. package/tests/rebase.test.ts +0 -37
  78. package/tests/sync/checkout.test.ts +0 -75
  79. package/tests/sync/diff.test.ts +0 -92
package/src/data-diff.ts CHANGED
@@ -1,77 +1,83 @@
1
1
  import { CID } from 'multiformats'
2
2
  import CidSet from './cid-set'
3
- import { MST, mstDiff } from './mst'
3
+ import { MST, NodeEntry, mstDiff } from './mst'
4
+ import BlockMap from './block-map'
4
5
 
5
6
  export class DataDiff {
6
7
  adds: Record<string, DataAdd> = {}
7
8
  updates: Record<string, DataUpdate> = {}
8
9
  deletes: Record<string, DataDelete> = {}
9
10
 
10
- newCids: CidSet = new CidSet()
11
+ newMstBlocks: BlockMap = new BlockMap()
12
+ newLeafCids: CidSet = new CidSet()
11
13
  removedCids: CidSet = new CidSet()
12
14
 
13
15
  static async of(curr: MST, prev: MST | null): Promise<DataDiff> {
14
16
  return mstDiff(curr, prev)
15
17
  }
16
18
 
17
- recordAdd(key: string, cid: CID): void {
19
+ async nodeAdd(node: NodeEntry) {
20
+ if (node.isLeaf()) {
21
+ this.leafAdd(node.key, node.value)
22
+ } else {
23
+ const data = await node.serialize()
24
+ this.treeAdd(data.cid, data.bytes)
25
+ }
26
+ }
27
+
28
+ async nodeDelete(node: NodeEntry) {
29
+ if (node.isLeaf()) {
30
+ const key = node.key
31
+ const cid = node.value
32
+ this.deletes[key] = { key, cid }
33
+ this.removedCids.add(cid)
34
+ } else {
35
+ const cid = await node.getPointer()
36
+ this.treeDelete(cid)
37
+ }
38
+ }
39
+
40
+ leafAdd(key: string, cid: CID) {
18
41
  this.adds[key] = { key, cid }
19
- this.newCids.add(cid)
42
+ if (this.removedCids.has(cid)) {
43
+ this.removedCids.delete(cid)
44
+ } else {
45
+ this.newLeafCids.add(cid)
46
+ }
20
47
  }
21
48
 
22
- recordUpdate(key: string, prev: CID, cid: CID): void {
49
+ leafUpdate(key: string, prev: CID, cid: CID) {
50
+ if (prev.equals(cid)) return
23
51
  this.updates[key] = { key, prev, cid }
24
- this.newCids.add(cid)
52
+ this.removedCids.add(prev)
53
+ this.newLeafCids.add(cid)
25
54
  }
26
55
 
27
- recordDelete(key: string, cid: CID): void {
56
+ leafDelete(key: string, cid: CID) {
28
57
  this.deletes[key] = { key, cid }
58
+ if (this.newLeafCids.has(cid)) {
59
+ this.newLeafCids.delete(cid)
60
+ } else {
61
+ this.removedCids.add(cid)
62
+ }
29
63
  }
30
64
 
31
- recordNewCid(cid: CID): void {
65
+ treeAdd(cid: CID, bytes: Uint8Array) {
32
66
  if (this.removedCids.has(cid)) {
33
67
  this.removedCids.delete(cid)
34
68
  } else {
35
- this.newCids.add(cid)
69
+ this.newMstBlocks.set(cid, bytes)
36
70
  }
37
71
  }
38
72
 
39
- recordRemovedCid(cid: CID): void {
40
- if (this.newCids.has(cid)) {
41
- this.newCids.delete(cid)
73
+ treeDelete(cid: CID) {
74
+ if (this.newMstBlocks.has(cid)) {
75
+ this.newMstBlocks.delete(cid)
42
76
  } else {
43
77
  this.removedCids.add(cid)
44
78
  }
45
79
  }
46
80
 
47
- addDiff(diff: DataDiff) {
48
- for (const add of diff.addList()) {
49
- if (this.deletes[add.key]) {
50
- const del = this.deletes[add.key]
51
- if (del.cid !== add.cid) {
52
- this.recordUpdate(add.key, del.cid, add.cid)
53
- }
54
- delete this.deletes[add.key]
55
- } else {
56
- this.recordAdd(add.key, add.cid)
57
- }
58
- }
59
- for (const update of diff.updateList()) {
60
- this.recordUpdate(update.key, update.prev, update.cid)
61
- delete this.adds[update.key]
62
- delete this.deletes[update.key]
63
- }
64
- for (const del of diff.deleteList()) {
65
- if (this.adds[del.key]) {
66
- delete this.adds[del.key]
67
- } else {
68
- delete this.updates[del.key]
69
- this.recordDelete(del.key, del.cid)
70
- }
71
- }
72
- this.newCids.addSet(diff.newCids)
73
- }
74
-
75
81
  addList(): DataAdd[] {
76
82
  return Object.values(this.adds)
77
83
  }
@@ -84,10 +90,6 @@ export class DataDiff {
84
90
  return Object.values(this.deletes)
85
91
  }
86
92
 
87
- newCidList(): CID[] {
88
- return this.newCids.toList()
89
- }
90
-
91
93
  updatedKeys(): string[] {
92
94
  const keys = [
93
95
  ...Object.keys(this.adds),
package/src/index.ts CHANGED
@@ -5,6 +5,5 @@ export * from './mst'
5
5
  export * from './storage'
6
6
  export * from './sync'
7
7
  export * from './types'
8
- export * from './verify'
9
8
  export * from './data-diff'
10
9
  export * from './util'
package/src/mst/diff.ts CHANGED
@@ -5,11 +5,7 @@ import MstWalker from './walker'
5
5
  export const nullDiff = async (tree: MST): Promise<DataDiff> => {
6
6
  const diff = new DataDiff()
7
7
  for await (const entry of tree.walk()) {
8
- if (entry.isLeaf()) {
9
- diff.recordAdd(entry.key, entry.value)
10
- } else {
11
- diff.recordNewCid(entry.pointer)
12
- }
8
+ await diff.nodeAdd(entry)
13
9
  }
14
10
  return diff
15
11
  }
@@ -31,21 +27,11 @@ export const mstDiff = async (
31
27
  while (!leftWalker.status.done || !rightWalker.status.done) {
32
28
  // if one walker is finished, continue walking the other & logging all nodes
33
29
  if (leftWalker.status.done && !rightWalker.status.done) {
34
- const node = rightWalker.status.curr
35
- if (node.isLeaf()) {
36
- diff.recordAdd(node.key, node.value)
37
- } else {
38
- diff.recordNewCid(node.pointer)
39
- }
30
+ await diff.nodeAdd(rightWalker.status.curr)
40
31
  await rightWalker.advance()
41
32
  continue
42
33
  } else if (!leftWalker.status.done && rightWalker.status.done) {
43
- const node = leftWalker.status.curr
44
- if (node.isLeaf()) {
45
- diff.recordDelete(node.key, node.value)
46
- } else {
47
- diff.recordRemovedCid(node.pointer)
48
- }
34
+ await diff.nodeDelete(leftWalker.status.curr)
49
35
  await leftWalker.advance()
50
36
  continue
51
37
  }
@@ -58,15 +44,15 @@ export const mstDiff = async (
58
44
  if (left.isLeaf() && right.isLeaf()) {
59
45
  if (left.key === right.key) {
60
46
  if (!left.value.equals(right.value)) {
61
- diff.recordUpdate(left.key, left.value, right.value)
47
+ diff.leafUpdate(left.key, left.value, right.value)
62
48
  }
63
49
  await leftWalker.advance()
64
50
  await rightWalker.advance()
65
51
  } else if (left.key < right.key) {
66
- diff.recordDelete(left.key, left.value)
52
+ diff.leafDelete(left.key, left.value)
67
53
  await leftWalker.advance()
68
54
  } else {
69
- diff.recordAdd(right.key, right.value)
55
+ diff.leafAdd(right.key, right.value)
70
56
  await rightWalker.advance()
71
57
  }
72
58
  continue
@@ -78,27 +64,19 @@ export const mstDiff = async (
78
64
  // if the higher walker is pointed at a leaf, then advance the lower walker to try to catch up the higher
79
65
  if (leftWalker.layer() > rightWalker.layer()) {
80
66
  if (left.isLeaf()) {
81
- if (right.isLeaf()) {
82
- diff.recordAdd(right.key, right.value)
83
- } else {
84
- diff.recordNewCid(right.pointer)
85
- }
67
+ await diff.nodeAdd(right)
86
68
  await rightWalker.advance()
87
69
  } else {
88
- diff.recordRemovedCid(left.pointer)
70
+ await diff.nodeDelete(left)
89
71
  await leftWalker.stepInto()
90
72
  }
91
73
  continue
92
74
  } else if (leftWalker.layer() < rightWalker.layer()) {
93
75
  if (right.isLeaf()) {
94
- if (left.isLeaf()) {
95
- diff.recordDelete(left.key, left.value)
96
- } else {
97
- diff.recordRemovedCid(left.pointer)
98
- }
76
+ await diff.nodeDelete(left)
99
77
  await leftWalker.advance()
100
78
  } else {
101
- diff.recordNewCid(right.pointer)
79
+ await diff.nodeAdd(right)
102
80
  await rightWalker.stepInto()
103
81
  }
104
82
  continue
@@ -111,8 +89,8 @@ export const mstDiff = async (
111
89
  await leftWalker.stepOver()
112
90
  await rightWalker.stepOver()
113
91
  } else {
114
- diff.recordNewCid(right.pointer)
115
- diff.recordRemovedCid(left.pointer)
92
+ await diff.nodeAdd(right)
93
+ await diff.nodeDelete(left)
116
94
  await leftWalker.stepInto()
117
95
  await rightWalker.stepInto()
118
96
  }
@@ -121,11 +99,11 @@ export const mstDiff = async (
121
99
 
122
100
  // finally, if one pointer is a tree and the other is a leaf, simply step into the tree
123
101
  if (left.isLeaf() && right.isTree()) {
124
- diff.recordNewCid(right.pointer)
102
+ await diff.nodeAdd(right)
125
103
  await rightWalker.stepInto()
126
104
  continue
127
105
  } else if (left.isTree() && right.isLeaf()) {
128
- diff.recordRemovedCid(left.pointer)
106
+ await diff.nodeDelete(left)
129
107
  await leftWalker.stepInto()
130
108
  continue
131
109
  }
package/src/mst/mst.ts CHANGED
@@ -2,7 +2,7 @@ import z from 'zod'
2
2
  import { CID } from 'multiformats'
3
3
 
4
4
  import { ReadableBlockstore } from '../storage'
5
- import { schema as common, cidForCbor } from '@atproto/common'
5
+ import { schema as common, cidForCbor, dataToCborBlock } from '@atproto/common'
6
6
  import { BlockWriter } from '@ipld/car/api'
7
7
  import * as util from './util'
8
8
  import BlockMap from '../block-map'
@@ -153,6 +153,13 @@ export class MST {
153
153
  // Instead we keep track of whether the pointer is outdated and only (recursively) calculate when needed
154
154
  async getPointer(): Promise<CID> {
155
155
  if (!this.outdatedPointer) return this.pointer
156
+ const { cid } = await this.serialize()
157
+ this.pointer = cid
158
+ this.outdatedPointer = false
159
+ return this.pointer
160
+ }
161
+
162
+ async serialize(): Promise<{ cid: CID; bytes: Uint8Array }> {
156
163
  let entries = await this.getEntries()
157
164
  const outdated = entries.filter(
158
165
  (e) => e.isTree() && e.outdatedPointer,
@@ -161,9 +168,12 @@ export class MST {
161
168
  await Promise.all(outdated.map((e) => e.getPointer()))
162
169
  entries = await this.getEntries()
163
170
  }
164
- this.pointer = await util.cidForEntries(entries)
165
- this.outdatedPointer = false
166
- return this.pointer
171
+ const data = util.serializeNodeData(entries)
172
+ const block = await dataToCborBlock(data)
173
+ return {
174
+ cid: block.cid,
175
+ bytes: block.bytes,
176
+ }
167
177
  }
168
178
 
169
179
  // In most cases, we get the layer of a node from a hint on creation
@@ -1,5 +1,5 @@
1
1
  import { CID } from 'multiformats/cid'
2
- import { Commit, def, RepoContents } from './types'
2
+ import { def, RepoContents, Commit } from './types'
3
3
  import { ReadableBlockstore } from './storage'
4
4
  import { MST } from './mst'
5
5
  import log from './logger'
@@ -28,13 +28,13 @@ export class ReadableRepo {
28
28
  }
29
29
 
30
30
  static async load(storage: ReadableBlockstore, commitCid: CID) {
31
- const commit = await storage.readObj(commitCid, def.commit)
31
+ const commit = await storage.readObj(commitCid, def.versionedCommit)
32
32
  const data = await MST.load(storage, commit.data)
33
33
  log.info({ did: commit.did }, 'loaded repo for')
34
34
  return new ReadableRepo({
35
35
  storage,
36
36
  data,
37
- commit,
37
+ commit: util.ensureV3Commit(commit),
38
38
  cid: commitCid,
39
39
  })
40
40
  }
package/src/repo.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import { CID } from 'multiformats/cid'
2
+ import { TID } from '@atproto/common'
2
3
  import * as crypto from '@atproto/crypto'
3
4
  import {
4
5
  Commit,
6
+ CommitData,
5
7
  def,
6
8
  RecordCreateOp,
7
9
  RecordWriteOp,
8
- CommitData,
9
10
  WriteOpAction,
10
- RebaseData,
11
11
  } from './types'
12
12
  import { RepoStorage } from './storage'
13
13
  import { MST } from './mst'
@@ -46,25 +46,29 @@ export class Repo extends ReadableRepo {
46
46
  const dataKey = util.formatDataKey(record.collection, record.rkey)
47
47
  data = await data.add(dataKey, cid)
48
48
  }
49
+ const dataCid = await data.getPointer()
50
+ const diff = await DataDiff.of(data, null)
51
+ newBlocks.addMap(diff.newMstBlocks)
49
52
 
50
- const unstoredData = await data.getUnstoredBlocks()
51
- newBlocks.addMap(unstoredData.blocks)
52
-
53
+ const rev = TID.nextStr()
53
54
  const commit = await util.signCommit(
54
55
  {
55
56
  did,
56
- version: 2,
57
- prev: null,
58
- data: unstoredData.root,
57
+ version: 3,
58
+ rev,
59
+ prev: null, // added for backwards compatibility with v2
60
+ data: dataCid,
59
61
  },
60
62
  keypair,
61
63
  )
62
64
  const commitCid = await newBlocks.add(commit)
63
-
64
65
  return {
65
- commit: commitCid,
66
+ cid: commitCid,
67
+ rev,
68
+ since: null,
66
69
  prev: null,
67
- blocks: newBlocks,
70
+ newBlocks,
71
+ removedCids: diff.removedCids,
68
72
  }
69
73
  }
70
74
 
@@ -73,7 +77,7 @@ export class Repo extends ReadableRepo {
73
77
  commit: CommitData,
74
78
  ): Promise<Repo> {
75
79
  await storage.applyCommit(commit)
76
- return Repo.load(storage, commit.commit)
80
+ return Repo.load(storage, commit.cid)
77
81
  }
78
82
 
79
83
  static async create(
@@ -92,17 +96,17 @@ export class Repo extends ReadableRepo {
92
96
  }
93
97
 
94
98
  static async load(storage: RepoStorage, cid?: CID) {
95
- const commitCid = cid || (await storage.getHead())
99
+ const commitCid = cid || (await storage.getRoot())
96
100
  if (!commitCid) {
97
101
  throw new Error('No cid provided and none in storage')
98
102
  }
99
- const commit = await storage.readObj(commitCid, def.commit)
103
+ const commit = await storage.readObj(commitCid, def.versionedCommit)
100
104
  const data = await MST.load(storage, commit.data)
101
105
  log.info({ did: commit.did }, 'loaded repo for')
102
106
  return new Repo({
103
107
  storage,
104
108
  data,
105
- commit,
109
+ commit: util.ensureV3Commit(commit),
106
110
  cid: commitCid,
107
111
  })
108
112
  }
@@ -112,16 +116,16 @@ export class Repo extends ReadableRepo {
112
116
  keypair: crypto.Keypair,
113
117
  ): Promise<CommitData> {
114
118
  const writes = Array.isArray(toWrite) ? toWrite : [toWrite]
115
- const commitBlocks = new BlockMap()
119
+ const leaves = new BlockMap()
116
120
 
117
121
  let data = this.data
118
122
  for (const write of writes) {
119
123
  if (write.action === WriteOpAction.Create) {
120
- const cid = await commitBlocks.add(write.record)
124
+ const cid = await leaves.add(write.record)
121
125
  const dataKey = write.collection + '/' + write.rkey
122
126
  data = await data.add(dataKey, cid)
123
127
  } else if (write.action === WriteOpAction.Update) {
124
- const cid = await commitBlocks.add(write.record)
128
+ const cid = await leaves.add(write.record)
125
129
  const dataKey = write.collection + '/' + write.rkey
126
130
  data = await data.update(dataKey, cid)
127
131
  } else if (write.action === WriteOpAction.Delete) {
@@ -130,44 +134,50 @@ export class Repo extends ReadableRepo {
130
134
  }
131
135
  }
132
136
 
133
- const unstoredData = await data.getUnstoredBlocks()
134
- commitBlocks.addMap(unstoredData.blocks)
135
-
136
- // ensure we're not missing any blocks that were removed and then readded in this commit
137
+ const dataCid = await data.getPointer()
137
138
  const diff = await DataDiff.of(data, this.data)
138
- const found = commitBlocks.getMany(diff.newCidList())
139
- if (found.missing.length > 0) {
140
- const fromStorage = await this.storage.getBlocks(found.missing)
141
- if (fromStorage.missing.length > 0) {
142
- // this shouldn't ever happen
143
- throw new Error(
144
- 'Could not find block for commit in Datastore or storage',
145
- )
146
- }
147
- commitBlocks.addMap(fromStorage.blocks)
139
+ const newBlocks = diff.newMstBlocks
140
+ const removedCids = diff.removedCids
141
+
142
+ const addedLeaves = leaves.getMany(diff.newLeafCids.toList())
143
+ if (addedLeaves.missing.length > 0) {
144
+ throw new Error(`Missing leaf blocks: ${addedLeaves.missing}`)
148
145
  }
146
+ newBlocks.addMap(addedLeaves.blocks)
149
147
 
148
+ const rev = TID.nextStr(this.commit.rev)
150
149
  const commit = await util.signCommit(
151
150
  {
152
151
  did: this.did,
153
- version: 2,
154
- prev: this.cid,
155
- data: unstoredData.root,
152
+ version: 3,
153
+ rev,
154
+ prev: null, // added for backwards compatibility with v2
155
+ data: dataCid,
156
156
  },
157
157
  keypair,
158
158
  )
159
- const commitCid = await commitBlocks.add(commit)
159
+ const commitCid = await newBlocks.add(commit)
160
+
161
+ // ensure the commit cid actually changed
162
+ if (commitCid.equals(this.cid)) {
163
+ newBlocks.delete(commitCid)
164
+ } else {
165
+ removedCids.add(this.cid)
166
+ }
160
167
 
161
168
  return {
162
- commit: commitCid,
169
+ cid: commitCid,
170
+ rev,
171
+ since: this.commit.rev,
163
172
  prev: this.cid,
164
- blocks: commitBlocks,
173
+ newBlocks,
174
+ removedCids,
165
175
  }
166
176
  }
167
177
 
168
178
  async applyCommit(commitData: CommitData): Promise<Repo> {
169
179
  await this.storage.applyCommit(commitData)
170
- return Repo.load(this.storage, commitData.commit)
180
+ return Repo.load(this.storage, commitData.cid)
171
181
  }
172
182
 
173
183
  async applyWrites(
@@ -177,37 +187,6 @@ export class Repo extends ReadableRepo {
177
187
  const commit = await this.formatCommit(toWrite, keypair)
178
188
  return this.applyCommit(commit)
179
189
  }
180
-
181
- async formatRebase(keypair: crypto.Keypair): Promise<RebaseData> {
182
- const preservedCids = await this.data.allCids()
183
- const blocks = new BlockMap()
184
- const commit = await util.signCommit(
185
- {
186
- did: this.did,
187
- version: 2,
188
- prev: null,
189
- data: this.commit.data,
190
- },
191
- keypair,
192
- )
193
- const commitCid = await blocks.add(commit)
194
- return {
195
- commit: commitCid,
196
- rebased: this.cid,
197
- blocks,
198
- preservedCids: preservedCids.toList(),
199
- }
200
- }
201
-
202
- async applyRebase(rebase: RebaseData): Promise<Repo> {
203
- await this.storage.applyRebase(rebase)
204
- return Repo.load(this.storage, rebase.commit)
205
- }
206
-
207
- async rebase(keypair: crypto.Keypair): Promise<Repo> {
208
- const rebaseData = await this.formatRebase(keypair)
209
- return this.applyRebase(rebaseData)
210
- }
211
190
  }
212
191
 
213
192
  export default Repo
@@ -1,5 +1,4 @@
1
1
  export * from './readable-blockstore'
2
- export * from './repo-storage'
3
2
  export * from './memory-blockstore'
4
3
  export * from './sync-storage'
5
4
  export * from './types'
@@ -1,15 +1,15 @@
1
1
  import { CID } from 'multiformats/cid'
2
- import { CommitData, def, RebaseData } from '../types'
2
+ import { CommitData } from '../types'
3
3
  import BlockMap from '../block-map'
4
- import { MST } from '../mst'
5
- import DataDiff from '../data-diff'
6
- import { MissingCommitBlocksError } from '../error'
7
- import RepoStorage from './repo-storage'
8
- import CidSet from '../cid-set'
4
+ import ReadableBlockstore from './readable-blockstore'
5
+ import { RepoStorage } from './types'
9
6
 
10
- export class MemoryBlockstore extends RepoStorage {
7
+ export class MemoryBlockstore
8
+ extends ReadableBlockstore
9
+ implements RepoStorage
10
+ {
11
11
  blocks: BlockMap
12
- head: CID | null = null
12
+ root: CID | null = null
13
13
 
14
14
  constructor(blocks?: BlockMap) {
15
15
  super()
@@ -19,8 +19,8 @@ export class MemoryBlockstore extends RepoStorage {
19
19
  }
20
20
  }
21
21
 
22
- async getHead(): Promise<CID | null> {
23
- return this.head
22
+ async getRoot(): Promise<CID | null> {
23
+ return this.root
24
24
  }
25
25
 
26
26
  async getBytes(cid: CID): Promise<Uint8Array | null> {
@@ -43,78 +43,19 @@ export class MemoryBlockstore extends RepoStorage {
43
43
  this.blocks.addMap(blocks)
44
44
  }
45
45
 
46
- async indexCommits(commits: CommitData[]): Promise<void> {
47
- commits.forEach((commit) => {
48
- this.blocks.addMap(commit.blocks)
49
- })
50
- }
51
-
52
- async updateHead(cid: CID, _prev: CID | null): Promise<void> {
53
- this.head = cid
46
+ async updateRoot(cid: CID): Promise<void> {
47
+ this.root = cid
54
48
  }
55
49
 
56
50
  async applyCommit(commit: CommitData): Promise<void> {
57
- this.blocks.addMap(commit.blocks)
58
- this.head = commit.commit
59
- }
60
-
61
- async getCommitPath(
62
- latest: CID,
63
- earliest: CID | null,
64
- ): Promise<CID[] | null> {
65
- let curr: CID | null = latest
66
- const path: CID[] = []
67
- while (curr !== null) {
68
- path.push(curr)
69
- const commit = await this.readObj(curr, def.commit)
70
- if (!earliest && commit.prev === null) {
71
- return path.reverse()
72
- } else if (earliest && commit.prev.equals(earliest)) {
73
- return path.reverse()
74
- }
75
- curr = commit.prev
76
- }
77
- return null
78
- }
79
-
80
- async getBlocksForCommits(
81
- commits: CID[],
82
- ): Promise<{ [commit: string]: BlockMap }> {
83
- const commitData: { [commit: string]: BlockMap } = {}
84
- let prevData: MST | null = null
85
- for (const commitCid of commits) {
86
- const commit = await this.readObj(commitCid, def.commit)
87
- const data = await MST.load(this, commit.data)
88
- const diff = await DataDiff.of(data, prevData)
89
- const { blocks, missing } = await this.getBlocks([
90
- commitCid,
91
- ...diff.newCidList(),
92
- ])
93
- if (missing.length > 0) {
94
- throw new MissingCommitBlocksError(commitCid, missing)
95
- }
96
- commitData[commitCid.toString()] = blocks
97
- prevData = data
98
- }
99
- return commitData
100
- }
101
-
102
- async applyRebase(rebase: RebaseData) {
103
- this.putMany(rebase.blocks)
104
- const allCids = new CidSet([
105
- ...rebase.preservedCids,
106
- ...rebase.blocks.cids(),
107
- ])
108
- const toDelete: CID[] = []
109
- this.blocks.forEach((_bytes, cid) => {
110
- if (!allCids.has(cid)) {
111
- toDelete.push(cid)
112
- }
113
- })
114
- for (const cid of toDelete) {
51
+ this.root = commit.cid
52
+ const rmCids = commit.removedCids.toList()
53
+ for (const cid of rmCids) {
115
54
  this.blocks.delete(cid)
116
55
  }
117
- await this.updateHead(rebase.commit, null)
56
+ commit.newBlocks.forEach((bytes, cid) => {
57
+ this.blocks.set(cid, bytes)
58
+ })
118
59
  }
119
60
 
120
61
  async sizeInBytes(): Promise<number> {
@@ -1,5 +1,34 @@
1
1
  import stream from 'stream'
2
2
  import { CID } from 'multiformats/cid'
3
+ import { RepoRecord } from '@atproto/lexicon'
4
+ import { check } from '@atproto/common'
5
+ import BlockMap from '../block-map'
6
+ import { CommitData } from '../types'
7
+
8
+ export interface RepoStorage {
9
+ // Writable
10
+ getRoot(): Promise<CID | null>
11
+ putBlock(cid: CID, block: Uint8Array, rev: string): Promise<void>
12
+ putMany(blocks: BlockMap, rev: string): Promise<void>
13
+ updateRoot(cid: CID): Promise<void>
14
+ applyCommit(commit: CommitData)
15
+
16
+ // Readable
17
+ getBytes(cid: CID): Promise<Uint8Array | null>
18
+ has(cid: CID): Promise<boolean>
19
+ getBlocks(cids: CID[]): Promise<{ blocks: BlockMap; missing: CID[] }>
20
+ attemptRead<T>(
21
+ cid: CID,
22
+ def: check.Def<T>,
23
+ ): Promise<{ obj: T; bytes: Uint8Array } | null>
24
+ readObjAndBytes<T>(
25
+ cid: CID,
26
+ def: check.Def<T>,
27
+ ): Promise<{ obj: T; bytes: Uint8Array }>
28
+ readObj<T>(cid: CID, def: check.Def<T>): Promise<T>
29
+ attemptReadRecord(cid: CID): Promise<RepoRecord | null>
30
+ readRecord(cid: CID): Promise<RepoRecord>
31
+ }
3
32
 
4
33
  export interface BlobStore {
5
34
  putTemp(bytes: Uint8Array | stream.Readable): Promise<string>