@atproto/repo 0.0.1 → 0.1.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/bench/mst.bench.ts +7 -4
- package/bench/repo.bench.ts +25 -16
- package/dist/block-map.d.ts +25 -0
- package/dist/data-diff.d.ts +36 -0
- package/dist/error.d.ts +20 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +11605 -10399
- package/dist/index.js.map +4 -4
- package/dist/mst/diff.d.ts +4 -33
- package/dist/mst/mst.d.ts +68 -25
- package/dist/mst/util.d.ts +13 -5
- package/dist/parse.d.ts +16 -0
- package/dist/readable-repo.d.ts +22 -0
- package/dist/repo.d.ts +14 -30
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/memory-blockstore.d.ts +28 -0
- package/dist/storage/readable-blockstore.d.ts +24 -0
- package/dist/storage/repo-storage.d.ts +18 -0
- package/dist/storage/sync-storage.d.ts +15 -0
- package/dist/storage/types.d.ts +3 -0
- package/dist/sync/consumer.d.ts +18 -0
- package/dist/sync/index.d.ts +2 -0
- package/dist/sync/provider.d.ts +9 -0
- package/dist/types.d.ts +124 -317
- package/dist/util.d.ts +31 -12
- package/dist/verify.d.ts +26 -4
- package/package.json +4 -2
- package/src/block-map.ts +95 -0
- package/src/cid-set.ts +1 -2
- package/src/data-diff.ts +121 -0
- package/src/error.ts +31 -0
- package/src/index.ts +3 -1
- package/src/mst/diff.ts +120 -90
- package/src/mst/mst.ts +185 -184
- package/src/mst/util.ts +54 -31
- package/src/parse.ts +44 -0
- package/src/readable-repo.ts +75 -0
- package/src/repo.ts +119 -249
- package/src/storage/index.ts +4 -0
- package/src/storage/memory-blockstore.ts +114 -0
- package/src/storage/readable-blockstore.ts +56 -0
- package/src/storage/repo-storage.ts +42 -0
- package/src/storage/sync-storage.ts +35 -0
- package/src/storage/types.ts +3 -0
- package/src/sync/consumer.ts +137 -0
- package/src/sync/index.ts +2 -0
- package/src/sync/provider.ts +91 -0
- package/src/types.ts +101 -62
- package/src/util.ts +237 -56
- package/src/verify.ts +207 -42
- package/tests/_util.ts +132 -97
- package/tests/mst.test.ts +269 -122
- package/tests/repo.test.ts +48 -50
- package/tests/sync/checkout.test.ts +57 -0
- package/tests/sync/diff.test.ts +87 -0
- package/tests/sync/narrow.test.ts +145 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.json +2 -1
- package/src/blockstore/index.ts +0 -2
- package/src/blockstore/ipld-store.ts +0 -103
- package/src/blockstore/memory-blockstore.ts +0 -49
- package/src/sync.ts +0 -38
- package/tests/sync.test.ts +0 -129
package/src/data-diff.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { CID } from 'multiformats'
|
|
2
|
+
import CidSet from './cid-set'
|
|
3
|
+
import { MST, mstDiff } from './mst'
|
|
4
|
+
import { DataStore } from './types'
|
|
5
|
+
|
|
6
|
+
export class DataDiff {
|
|
7
|
+
adds: Record<string, DataAdd> = {}
|
|
8
|
+
updates: Record<string, DataUpdate> = {}
|
|
9
|
+
deletes: Record<string, DataDelete> = {}
|
|
10
|
+
|
|
11
|
+
newCids: CidSet = new CidSet()
|
|
12
|
+
removedCids: CidSet = new CidSet()
|
|
13
|
+
|
|
14
|
+
static async of(curr: DataStore, prev: DataStore | null): Promise<DataDiff> {
|
|
15
|
+
if (curr instanceof MST && (prev === null || prev instanceof MST)) {
|
|
16
|
+
return mstDiff(curr, prev)
|
|
17
|
+
}
|
|
18
|
+
throw new Error('Unsupported DataStore type for diff')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
recordAdd(key: string, cid: CID): void {
|
|
22
|
+
this.adds[key] = { key, cid }
|
|
23
|
+
this.newCids.add(cid)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
recordUpdate(key: string, prev: CID, cid: CID): void {
|
|
27
|
+
this.updates[key] = { key, prev, cid }
|
|
28
|
+
this.newCids.add(cid)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
recordDelete(key: string, cid: CID): void {
|
|
32
|
+
this.deletes[key] = { key, cid }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
recordNewCid(cid: CID): void {
|
|
36
|
+
if (this.removedCids.has(cid)) {
|
|
37
|
+
this.removedCids.delete(cid)
|
|
38
|
+
} else {
|
|
39
|
+
this.newCids.add(cid)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
recordRemovedCid(cid: CID): void {
|
|
44
|
+
if (this.newCids.has(cid)) {
|
|
45
|
+
this.newCids.delete(cid)
|
|
46
|
+
} else {
|
|
47
|
+
this.removedCids.add(cid)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
addDiff(diff: DataDiff) {
|
|
52
|
+
for (const add of diff.addList()) {
|
|
53
|
+
if (this.deletes[add.key]) {
|
|
54
|
+
const del = this.deletes[add.key]
|
|
55
|
+
if (del.cid !== add.cid) {
|
|
56
|
+
this.recordUpdate(add.key, del.cid, add.cid)
|
|
57
|
+
}
|
|
58
|
+
delete this.deletes[add.key]
|
|
59
|
+
} else {
|
|
60
|
+
this.recordAdd(add.key, add.cid)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (const update of diff.updateList()) {
|
|
64
|
+
this.recordUpdate(update.key, update.prev, update.cid)
|
|
65
|
+
delete this.adds[update.key]
|
|
66
|
+
delete this.deletes[update.key]
|
|
67
|
+
}
|
|
68
|
+
for (const del of diff.deleteList()) {
|
|
69
|
+
if (this.adds[del.key]) {
|
|
70
|
+
delete this.adds[del.key]
|
|
71
|
+
} else {
|
|
72
|
+
delete this.updates[del.key]
|
|
73
|
+
this.recordDelete(del.key, del.cid)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
this.newCids.addSet(diff.newCids)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
addList(): DataAdd[] {
|
|
80
|
+
return Object.values(this.adds)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
updateList(): DataUpdate[] {
|
|
84
|
+
return Object.values(this.updates)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
deleteList(): DataDelete[] {
|
|
88
|
+
return Object.values(this.deletes)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
newCidList(): CID[] {
|
|
92
|
+
return this.newCids.toList()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
updatedKeys(): string[] {
|
|
96
|
+
const keys = [
|
|
97
|
+
...Object.keys(this.adds),
|
|
98
|
+
...Object.keys(this.updates),
|
|
99
|
+
...Object.keys(this.deletes),
|
|
100
|
+
]
|
|
101
|
+
return [...new Set(keys)]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type DataAdd = {
|
|
106
|
+
key: string
|
|
107
|
+
cid: CID
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export type DataUpdate = {
|
|
111
|
+
key: string
|
|
112
|
+
prev: CID
|
|
113
|
+
cid: CID
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export type DataDelete = {
|
|
117
|
+
key: string
|
|
118
|
+
cid: CID
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export default DataDiff
|
package/src/error.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { CID } from 'multiformats/cid'
|
|
2
|
+
|
|
3
|
+
export class MissingBlockError extends Error {
|
|
4
|
+
constructor(public cid: CID, def?: string) {
|
|
5
|
+
let msg = `block not found: ${cid.toString()}`
|
|
6
|
+
if (def) {
|
|
7
|
+
msg += `, expected type: ${def}`
|
|
8
|
+
}
|
|
9
|
+
super(msg)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class MissingBlocksError extends Error {
|
|
14
|
+
constructor(public context: string, public cids: CID[]) {
|
|
15
|
+
const cidStr = cids.map((c) => c.toString())
|
|
16
|
+
super(`missing ${context} blocks: ${cidStr}`)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class MissingCommitBlocksError extends Error {
|
|
21
|
+
constructor(public commit: CID, public cids: CID[]) {
|
|
22
|
+
const cidStr = cids.map((c) => c.toString())
|
|
23
|
+
super(`missing blocks for commit ${commit.toString()}: ${cidStr}`)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class UnexpectedObjectError extends Error {
|
|
28
|
+
constructor(public cid: CID, public def: string) {
|
|
29
|
+
super(`unexpected object at ${cid.toString()}, expected: ${def}`)
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './block-map'
|
|
2
|
+
export * from './cid-set'
|
|
2
3
|
export * from './repo'
|
|
3
4
|
export * from './mst'
|
|
4
5
|
export * from './storage'
|
|
6
|
+
export * from './sync'
|
|
5
7
|
export * from './types'
|
|
6
8
|
export * from './verify'
|
|
7
9
|
export * from './util'
|
package/src/mst/diff.ts
CHANGED
|
@@ -1,106 +1,136 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
recordAdd(key: string, cid: CID): void {
|
|
14
|
-
this.adds[key] = { key, cid }
|
|
15
|
-
this.newCids.add(cid)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
recordUpdate(key: string, prev: CID, cid: CID): void {
|
|
19
|
-
this.updates[key] = { key, prev, cid }
|
|
20
|
-
this.newCids.add(cid)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
recordDelete(key: string, cid: CID): void {
|
|
24
|
-
this.deletes[key] = { key, cid }
|
|
1
|
+
import { DataDiff } from '../data-diff'
|
|
2
|
+
import MST from './mst'
|
|
3
|
+
import MstWalker from './walker'
|
|
4
|
+
|
|
5
|
+
export const nullDiff = async (tree: MST): Promise<DataDiff> => {
|
|
6
|
+
const diff = new DataDiff()
|
|
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
|
+
}
|
|
25
13
|
}
|
|
14
|
+
return diff
|
|
15
|
+
}
|
|
26
16
|
|
|
27
|
-
|
|
28
|
-
|
|
17
|
+
export const mstDiff = async (
|
|
18
|
+
curr: MST,
|
|
19
|
+
prev: MST | null,
|
|
20
|
+
): Promise<DataDiff> => {
|
|
21
|
+
await curr.getPointer()
|
|
22
|
+
if (prev === null) {
|
|
23
|
+
return nullDiff(curr)
|
|
29
24
|
}
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
await prev.getPointer()
|
|
27
|
+
const diff = new DataDiff()
|
|
28
|
+
|
|
29
|
+
const leftWalker = new MstWalker(prev)
|
|
30
|
+
const rightWalker = new MstWalker(curr)
|
|
31
|
+
while (!leftWalker.status.done || !rightWalker.status.done) {
|
|
32
|
+
// if one walker is finished, continue walking the other & logging all nodes
|
|
33
|
+
if (leftWalker.status.done && !rightWalker.status.done) {
|
|
34
|
+
const node = rightWalker.status.curr
|
|
35
|
+
if (node.isLeaf()) {
|
|
36
|
+
diff.recordAdd(node.key, node.value)
|
|
39
37
|
} else {
|
|
40
|
-
|
|
38
|
+
diff.recordNewCid(node.pointer)
|
|
41
39
|
}
|
|
40
|
+
await rightWalker.advance()
|
|
41
|
+
continue
|
|
42
|
+
} 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
|
+
}
|
|
49
|
+
await leftWalker.advance()
|
|
50
|
+
continue
|
|
42
51
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
if (leftWalker.status.done || rightWalker.status.done) break
|
|
53
|
+
const left = leftWalker.status.curr
|
|
54
|
+
const right = rightWalker.status.curr
|
|
55
|
+
if (left === null || right === null) break
|
|
56
|
+
|
|
57
|
+
// if both pointers are leaves, record an update & advance both or record the lowest key and advance that pointer
|
|
58
|
+
if (left.isLeaf() && right.isLeaf()) {
|
|
59
|
+
if (left.key === right.key) {
|
|
60
|
+
if (!left.value.equals(right.value)) {
|
|
61
|
+
diff.recordUpdate(left.key, left.value, right.value)
|
|
62
|
+
}
|
|
63
|
+
await leftWalker.advance()
|
|
64
|
+
await rightWalker.advance()
|
|
65
|
+
} else if (left.key < right.key) {
|
|
66
|
+
diff.recordDelete(left.key, left.value)
|
|
67
|
+
await leftWalker.advance()
|
|
51
68
|
} else {
|
|
52
|
-
|
|
53
|
-
|
|
69
|
+
diff.recordAdd(right.key, right.value)
|
|
70
|
+
await rightWalker.advance()
|
|
54
71
|
}
|
|
72
|
+
continue
|
|
55
73
|
}
|
|
56
|
-
this.newCids.addSet(diff.newCids)
|
|
57
|
-
}
|
|
58
74
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
// next, ensure that we're on the same layer
|
|
76
|
+
// if one walker is at a higher layer than the other, we need to do one of two things
|
|
77
|
+
// if the higher walker is pointed at a tree, step into that tree to try to catch up with the lower
|
|
78
|
+
// if the higher walker is pointed at a leaf, then advance the lower walker to try to catch up the higher
|
|
79
|
+
if (leftWalker.layer() > rightWalker.layer()) {
|
|
80
|
+
if (left.isLeaf()) {
|
|
81
|
+
if (right.isLeaf()) {
|
|
82
|
+
diff.recordAdd(right.key, right.value)
|
|
83
|
+
} else {
|
|
84
|
+
diff.recordNewCid(right.pointer)
|
|
85
|
+
}
|
|
86
|
+
await rightWalker.advance()
|
|
87
|
+
} else {
|
|
88
|
+
diff.recordRemovedCid(left.pointer)
|
|
89
|
+
await leftWalker.stepInto()
|
|
90
|
+
}
|
|
91
|
+
continue
|
|
92
|
+
} else if (leftWalker.layer() < rightWalker.layer()) {
|
|
93
|
+
if (right.isLeaf()) {
|
|
94
|
+
if (left.isLeaf()) {
|
|
95
|
+
diff.recordDelete(left.key, left.value)
|
|
96
|
+
} else {
|
|
97
|
+
diff.recordRemovedCid(left.pointer)
|
|
98
|
+
}
|
|
99
|
+
await leftWalker.advance()
|
|
100
|
+
} else {
|
|
101
|
+
diff.recordNewCid(right.pointer)
|
|
102
|
+
await rightWalker.stepInto()
|
|
103
|
+
}
|
|
104
|
+
continue
|
|
105
|
+
}
|
|
70
106
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
107
|
+
// if we're on the same level, and both pointers are trees, do a comparison
|
|
108
|
+
// if they're the same, step over. if they're different, step in to find the subdiff
|
|
109
|
+
if (left.isTree() && right.isTree()) {
|
|
110
|
+
if (left.pointer.equals(right.pointer)) {
|
|
111
|
+
await leftWalker.stepOver()
|
|
112
|
+
await rightWalker.stepOver()
|
|
113
|
+
} else {
|
|
114
|
+
diff.recordNewCid(right.pointer)
|
|
115
|
+
diff.recordRemovedCid(left.pointer)
|
|
116
|
+
await leftWalker.stepInto()
|
|
117
|
+
await rightWalker.stepInto()
|
|
118
|
+
}
|
|
119
|
+
continue
|
|
120
|
+
}
|
|
74
121
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
122
|
+
// finally, if one pointer is a tree and the other is a leaf, simply step into the tree
|
|
123
|
+
if (left.isLeaf() && right.isTree()) {
|
|
124
|
+
diff.recordNewCid(right.pointer)
|
|
125
|
+
await rightWalker.stepInto()
|
|
126
|
+
continue
|
|
127
|
+
} else if (left.isTree() && right.isLeaf()) {
|
|
128
|
+
diff.recordRemovedCid(left.pointer)
|
|
129
|
+
await leftWalker.stepInto()
|
|
130
|
+
continue
|
|
131
|
+
}
|
|
83
132
|
|
|
84
|
-
|
|
85
|
-
return this.updatedKeys().map((key) => {
|
|
86
|
-
const { collection, rkey } = parseRecordKey(key)
|
|
87
|
-
return auth.writeCap(rootDid, collection, rkey)
|
|
88
|
-
})
|
|
133
|
+
throw new Error('Unidentifiable case in diff walk')
|
|
89
134
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
export type DataAdd = {
|
|
93
|
-
key: string
|
|
94
|
-
cid: CID
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export type DataUpdate = {
|
|
98
|
-
key: string
|
|
99
|
-
prev: CID
|
|
100
|
-
cid: CID
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export type DataDelete = {
|
|
104
|
-
key: string
|
|
105
|
-
cid: CID
|
|
135
|
+
return diff
|
|
106
136
|
}
|