@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.
- package/dist/data-diff.d.ts +11 -9
- package/dist/index.d.ts +0 -1
- package/dist/index.js +404 -1655
- package/dist/index.js.map +4 -4
- package/dist/mst/mst.d.ts +10 -6
- package/dist/readable-repo.d.ts +1 -1
- package/dist/repo.d.ts +1 -4
- package/dist/storage/index.d.ts +0 -1
- package/dist/storage/memory-blockstore.d.ts +7 -12
- package/dist/storage/types.d.ts +28 -0
- package/dist/sync/consumer.d.ts +13 -17
- package/dist/sync/provider.d.ts +1 -5
- package/dist/types.d.ts +228 -39
- package/dist/util.d.ts +3 -5
- package/package.json +2 -2
- package/src/data-diff.ts +46 -44
- package/src/index.ts +0 -1
- package/src/mst/diff.ts +14 -36
- package/src/mst/mst.ts +14 -4
- package/src/readable-repo.ts +3 -3
- package/src/repo.ts +49 -70
- package/src/storage/index.ts +0 -1
- package/src/storage/memory-blockstore.ts +18 -77
- package/src/storage/types.ts +29 -0
- package/src/sync/consumer.ts +170 -116
- package/src/sync/provider.ts +2 -40
- package/src/types.ts +49 -23
- package/src/util.ts +24 -79
- package/tests/_util.ts +38 -67
- package/tests/mst.test.ts +4 -1
- package/tests/{sync/narrow.test.ts → proofs.test.ts} +9 -20
- package/tests/repo.test.ts +5 -4
- package/tests/sync.test.ts +97 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/src/block-map.d.ts +0 -23
- package/dist/src/blockstore/index.d.ts +0 -2
- package/dist/src/blockstore/ipld-store.d.ts +0 -27
- package/dist/src/blockstore/memory-blockstore.d.ts +0 -13
- package/dist/src/blockstore/persistent-blockstore.d.ts +0 -12
- package/dist/src/cid-set.d.ts +0 -14
- package/dist/src/collection.d.ts +0 -22
- package/dist/src/data-diff.d.ts +0 -34
- package/dist/src/error.d.ts +0 -21
- package/dist/src/index.d.ts +0 -7
- package/dist/src/logger.d.ts +0 -2
- package/dist/src/mst/diff.d.ts +0 -33
- package/dist/src/mst/index.d.ts +0 -4
- package/dist/src/mst/mst.d.ts +0 -106
- package/dist/src/mst/util.d.ts +0 -9
- package/dist/src/mst/walker.d.ts +0 -22
- package/dist/src/parse.d.ts +0 -11
- package/dist/src/readable-repo.d.ts +0 -25
- package/dist/src/repo.d.ts +0 -39
- package/dist/src/storage/error.d.ts +0 -22
- package/dist/src/storage/index.d.ts +0 -1
- package/dist/src/storage/memory-blobstore.d.ts +0 -1
- package/dist/src/storage/memory-blockstore.d.ts +0 -28
- package/dist/src/storage/readable-blockstore.d.ts +0 -21
- package/dist/src/storage/repo-storage.d.ts +0 -18
- package/dist/src/storage/sync-storage.d.ts +0 -15
- package/dist/src/storage/types.d.ts +0 -12
- package/dist/src/storage/util.d.ts +0 -17
- package/dist/src/structure.d.ts +0 -39
- package/dist/src/sync/consumer.d.ts +0 -19
- package/dist/src/sync/index.d.ts +0 -2
- package/dist/src/sync/producer.d.ts +0 -13
- package/dist/src/sync/provider.d.ts +0 -11
- package/dist/src/sync.d.ts +0 -9
- package/dist/src/types.d.ts +0 -368
- package/dist/src/util.d.ts +0 -13
- package/dist/src/verify.d.ts +0 -5
- package/dist/storage/repo-storage.d.ts +0 -19
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- package/dist/verify.d.ts +0 -32
- package/src/storage/repo-storage.ts +0 -43
- package/src/verify.ts +0 -268
- package/tests/rebase.test.ts +0 -37
- package/tests/sync/checkout.test.ts +0 -75
- 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
|
-
|
|
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
|
-
|
|
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.
|
|
42
|
+
if (this.removedCids.has(cid)) {
|
|
43
|
+
this.removedCids.delete(cid)
|
|
44
|
+
} else {
|
|
45
|
+
this.newLeafCids.add(cid)
|
|
46
|
+
}
|
|
20
47
|
}
|
|
21
48
|
|
|
22
|
-
|
|
49
|
+
leafUpdate(key: string, prev: CID, cid: CID) {
|
|
50
|
+
if (prev.equals(cid)) return
|
|
23
51
|
this.updates[key] = { key, prev, cid }
|
|
24
|
-
this.
|
|
52
|
+
this.removedCids.add(prev)
|
|
53
|
+
this.newLeafCids.add(cid)
|
|
25
54
|
}
|
|
26
55
|
|
|
27
|
-
|
|
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
|
-
|
|
65
|
+
treeAdd(cid: CID, bytes: Uint8Array) {
|
|
32
66
|
if (this.removedCids.has(cid)) {
|
|
33
67
|
this.removedCids.delete(cid)
|
|
34
68
|
} else {
|
|
35
|
-
this.
|
|
69
|
+
this.newMstBlocks.set(cid, bytes)
|
|
36
70
|
}
|
|
37
71
|
}
|
|
38
72
|
|
|
39
|
-
|
|
40
|
-
if (this.
|
|
41
|
-
this.
|
|
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
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
52
|
+
diff.leafDelete(left.key, left.value)
|
|
67
53
|
await leftWalker.advance()
|
|
68
54
|
} else {
|
|
69
|
-
diff.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
115
|
-
diff.
|
|
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.
|
|
102
|
+
await diff.nodeAdd(right)
|
|
125
103
|
await rightWalker.stepInto()
|
|
126
104
|
continue
|
|
127
105
|
} else if (left.isTree() && right.isLeaf()) {
|
|
128
|
-
diff.
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
return
|
|
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
|
package/src/readable-repo.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CID } from 'multiformats/cid'
|
|
2
|
-
import {
|
|
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.
|
|
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
|
|
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:
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
66
|
+
cid: commitCid,
|
|
67
|
+
rev,
|
|
68
|
+
since: null,
|
|
66
69
|
prev: null,
|
|
67
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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:
|
|
154
|
-
|
|
155
|
-
|
|
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
|
|
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
|
-
|
|
169
|
+
cid: commitCid,
|
|
170
|
+
rev,
|
|
171
|
+
since: this.commit.rev,
|
|
163
172
|
prev: this.cid,
|
|
164
|
-
|
|
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.
|
|
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
|
package/src/storage/index.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { CID } from 'multiformats/cid'
|
|
2
|
-
import { CommitData
|
|
2
|
+
import { CommitData } from '../types'
|
|
3
3
|
import BlockMap from '../block-map'
|
|
4
|
-
import
|
|
5
|
-
import
|
|
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
|
|
7
|
+
export class MemoryBlockstore
|
|
8
|
+
extends ReadableBlockstore
|
|
9
|
+
implements RepoStorage
|
|
10
|
+
{
|
|
11
11
|
blocks: BlockMap
|
|
12
|
-
|
|
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
|
|
23
|
-
return this.
|
|
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
|
|
47
|
-
|
|
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.
|
|
58
|
-
|
|
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
|
-
|
|
56
|
+
commit.newBlocks.forEach((bytes, cid) => {
|
|
57
|
+
this.blocks.set(cid, bytes)
|
|
58
|
+
})
|
|
118
59
|
}
|
|
119
60
|
|
|
120
61
|
async sizeInBytes(): Promise<number> {
|
package/src/storage/types.ts
CHANGED
|
@@ -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>
|