@atproto/repo 0.0.1 → 0.2.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 +27 -0
- package/dist/data-diff.d.ts +36 -0
- package/dist/error.d.ts +20 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +22870 -12456
- package/dist/index.js.map +4 -4
- package/dist/mst/diff.d.ts +4 -33
- package/dist/mst/mst.d.ts +73 -31
- package/dist/mst/util.d.ts +13 -5
- package/dist/parse.d.ts +16 -0
- package/dist/readable-repo.d.ts +23 -0
- package/dist/repo.d.ts +19 -31
- package/dist/src/block-map.d.ts +23 -0
- package/dist/src/blockstore/persistent-blockstore.d.ts +12 -0
- package/dist/src/cid-set.d.ts +14 -0
- package/dist/src/collection.d.ts +22 -0
- package/dist/src/data-diff.d.ts +34 -0
- package/dist/src/error.d.ts +21 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/logger.d.ts +2 -0
- package/dist/src/mst/diff.d.ts +33 -0
- package/dist/src/mst/index.d.ts +4 -0
- package/dist/src/mst/mst.d.ts +106 -0
- package/dist/src/mst/util.d.ts +9 -0
- package/dist/src/mst/walker.d.ts +22 -0
- package/dist/src/parse.d.ts +11 -0
- package/dist/src/readable-repo.d.ts +25 -0
- package/dist/src/repo.d.ts +39 -0
- package/dist/src/storage/error.d.ts +22 -0
- package/dist/src/storage/index.d.ts +1 -0
- package/dist/src/storage/memory-blobstore.d.ts +1 -0
- package/dist/src/storage/memory-blockstore.d.ts +28 -0
- package/dist/src/storage/readable-blockstore.d.ts +21 -0
- package/dist/src/storage/repo-storage.d.ts +18 -0
- package/dist/src/storage/sync-storage.d.ts +15 -0
- package/dist/src/storage/types.d.ts +12 -0
- package/dist/src/storage/util.d.ts +17 -0
- package/dist/src/structure.d.ts +39 -0
- package/dist/src/sync/consumer.d.ts +19 -0
- package/dist/src/sync/index.d.ts +2 -0
- package/dist/src/sync/producer.d.ts +13 -0
- package/dist/src/sync/provider.d.ts +11 -0
- package/dist/src/types.d.ts +368 -0
- package/dist/src/util.d.ts +13 -0
- package/dist/src/verify.d.ts +5 -0
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/memory-blockstore.d.ts +29 -0
- package/dist/storage/readable-blockstore.d.ts +24 -0
- package/dist/storage/repo-storage.d.ts +19 -0
- package/dist/storage/sync-storage.d.ts +15 -0
- package/dist/storage/types.d.ts +4 -0
- package/dist/sync/consumer.d.ts +19 -0
- package/dist/sync/index.d.ts +2 -0
- package/dist/sync/provider.d.ts +9 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/types.d.ts +137 -331
- package/dist/util.d.ts +35 -12
- package/dist/verify.d.ts +31 -4
- package/jest.bench.config.js +2 -1
- package/package.json +13 -6
- package/src/block-map.ts +103 -0
- package/src/cid-set.ts +1 -2
- package/src/data-diff.ts +117 -0
- package/src/error.ts +31 -0
- package/src/index.ts +4 -1
- package/src/mst/diff.ts +120 -90
- package/src/mst/mst.ts +179 -187
- package/src/mst/util.ts +54 -31
- package/src/parse.ts +44 -0
- package/src/readable-repo.ts +75 -0
- package/src/repo.ts +145 -244
- package/src/storage/index.ts +4 -0
- package/src/storage/memory-blockstore.ts +133 -0
- package/src/storage/readable-blockstore.ts +56 -0
- package/src/storage/repo-storage.ts +43 -0
- package/src/storage/sync-storage.ts +35 -0
- package/src/storage/types.ts +4 -0
- package/src/sync/consumer.ts +140 -0
- package/src/sync/index.ts +2 -0
- package/src/sync/provider.ts +91 -0
- package/src/types.ts +110 -73
- package/src/util.ts +258 -56
- package/src/verify.ts +248 -42
- package/tests/_util.ts +132 -97
- package/tests/mst.test.ts +269 -122
- package/tests/rebase.test.ts +37 -0
- package/tests/repo.test.ts +48 -50
- package/tests/sync/checkout.test.ts +75 -0
- package/tests/sync/diff.test.ts +92 -0
- package/tests/sync/narrow.test.ts +149 -0
- package/tests/util.test.ts +21 -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/dist/{blockstore → src/blockstore}/index.d.ts +0 -0
- /package/dist/{blockstore → src/blockstore}/ipld-store.d.ts +0 -0
- /package/dist/{blockstore → src/blockstore}/memory-blockstore.d.ts +0 -0
- /package/dist/{sync.d.ts → src/sync.d.ts} +0 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { CID } from 'multiformats/cid'
|
|
2
|
+
import { CommitData, def, RebaseData } from '../types'
|
|
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'
|
|
9
|
+
|
|
10
|
+
export class MemoryBlockstore extends RepoStorage {
|
|
11
|
+
blocks: BlockMap
|
|
12
|
+
head: CID | null = null
|
|
13
|
+
|
|
14
|
+
constructor(blocks?: BlockMap) {
|
|
15
|
+
super()
|
|
16
|
+
this.blocks = new BlockMap()
|
|
17
|
+
if (blocks) {
|
|
18
|
+
this.blocks.addMap(blocks)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async getHead(): Promise<CID | null> {
|
|
23
|
+
return this.head
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async getBytes(cid: CID): Promise<Uint8Array | null> {
|
|
27
|
+
return this.blocks.get(cid) || null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async has(cid: CID): Promise<boolean> {
|
|
31
|
+
return this.blocks.has(cid)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async getBlocks(cids: CID[]): Promise<{ blocks: BlockMap; missing: CID[] }> {
|
|
35
|
+
return this.blocks.getMany(cids)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async putBlock(cid: CID, block: Uint8Array): Promise<void> {
|
|
39
|
+
this.blocks.set(cid, block)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async putMany(blocks: BlockMap): Promise<void> {
|
|
43
|
+
this.blocks.addMap(blocks)
|
|
44
|
+
}
|
|
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
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
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) {
|
|
115
|
+
this.blocks.delete(cid)
|
|
116
|
+
}
|
|
117
|
+
await this.updateHead(rebase.commit, null)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async sizeInBytes(): Promise<number> {
|
|
121
|
+
let total = 0
|
|
122
|
+
this.blocks.forEach((bytes) => {
|
|
123
|
+
total += bytes.byteLength
|
|
124
|
+
})
|
|
125
|
+
return total
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async destroy(): Promise<void> {
|
|
129
|
+
this.blocks.clear()
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default MemoryBlockstore
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { check } from '@atproto/common'
|
|
2
|
+
import { RepoRecord } from '@atproto/lexicon'
|
|
3
|
+
import { CID } from 'multiformats/cid'
|
|
4
|
+
import BlockMap from '../block-map'
|
|
5
|
+
import { MissingBlockError } from '../error'
|
|
6
|
+
import * as parse from '../parse'
|
|
7
|
+
import { cborToLexRecord } from '../util'
|
|
8
|
+
|
|
9
|
+
export abstract class ReadableBlockstore {
|
|
10
|
+
abstract getBytes(cid: CID): Promise<Uint8Array | null>
|
|
11
|
+
abstract has(cid: CID): Promise<boolean>
|
|
12
|
+
abstract getBlocks(cids: CID[]): Promise<{ blocks: BlockMap; missing: CID[] }>
|
|
13
|
+
|
|
14
|
+
async attemptRead<T>(
|
|
15
|
+
cid: CID,
|
|
16
|
+
def: check.Def<T>,
|
|
17
|
+
): Promise<{ obj: T; bytes: Uint8Array } | null> {
|
|
18
|
+
const bytes = await this.getBytes(cid)
|
|
19
|
+
if (!bytes) return null
|
|
20
|
+
return parse.parseObjByDef(bytes, cid, def)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async readObjAndBytes<T>(
|
|
24
|
+
cid: CID,
|
|
25
|
+
def: check.Def<T>,
|
|
26
|
+
): Promise<{ obj: T; bytes: Uint8Array }> {
|
|
27
|
+
const read = await this.attemptRead(cid, def)
|
|
28
|
+
if (!read) {
|
|
29
|
+
throw new MissingBlockError(cid, def.name)
|
|
30
|
+
}
|
|
31
|
+
return read
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async readObj<T>(cid: CID, def: check.Def<T>): Promise<T> {
|
|
35
|
+
const obj = await this.readObjAndBytes(cid, def)
|
|
36
|
+
return obj.obj
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async attemptReadRecord(cid: CID): Promise<RepoRecord | null> {
|
|
40
|
+
try {
|
|
41
|
+
return await this.readRecord(cid)
|
|
42
|
+
} catch {
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async readRecord(cid: CID): Promise<RepoRecord> {
|
|
48
|
+
const bytes = await this.getBytes(cid)
|
|
49
|
+
if (!bytes) {
|
|
50
|
+
throw new MissingBlockError(cid)
|
|
51
|
+
}
|
|
52
|
+
return cborToLexRecord(bytes)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default ReadableBlockstore
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { CID } from 'multiformats/cid'
|
|
2
|
+
import BlockMap from '../block-map'
|
|
3
|
+
import { CommitBlockData, CommitData, RebaseData } from '../types'
|
|
4
|
+
import ReadableBlockstore from './readable-blockstore'
|
|
5
|
+
|
|
6
|
+
export abstract class RepoStorage extends ReadableBlockstore {
|
|
7
|
+
abstract getHead(forUpdate?: boolean): Promise<CID | null>
|
|
8
|
+
abstract getCommitPath(
|
|
9
|
+
latest: CID,
|
|
10
|
+
earliest: CID | null,
|
|
11
|
+
): Promise<CID[] | null>
|
|
12
|
+
abstract getBlocksForCommits(
|
|
13
|
+
commits: CID[],
|
|
14
|
+
): Promise<{ [commit: string]: BlockMap }>
|
|
15
|
+
|
|
16
|
+
abstract putBlock(cid: CID, block: Uint8Array): Promise<void>
|
|
17
|
+
abstract putMany(blocks: BlockMap): Promise<void>
|
|
18
|
+
abstract updateHead(cid: CID, prev: CID | null): Promise<void>
|
|
19
|
+
abstract indexCommits(commit: CommitData[]): Promise<void>
|
|
20
|
+
abstract applyRebase(rebase: RebaseData): Promise<void>
|
|
21
|
+
|
|
22
|
+
async applyCommit(commit: CommitData): Promise<void> {
|
|
23
|
+
await Promise.all([
|
|
24
|
+
this.indexCommits([commit]),
|
|
25
|
+
this.updateHead(commit.commit, commit.prev),
|
|
26
|
+
])
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async getCommits(
|
|
30
|
+
latest: CID,
|
|
31
|
+
earliest: CID | null,
|
|
32
|
+
): Promise<CommitBlockData[] | null> {
|
|
33
|
+
const commitPath = await this.getCommitPath(latest, earliest)
|
|
34
|
+
if (!commitPath) return null
|
|
35
|
+
const blocksByCommit = await this.getBlocksForCommits(commitPath)
|
|
36
|
+
return commitPath.map((commit) => ({
|
|
37
|
+
commit,
|
|
38
|
+
blocks: blocksByCommit[commit.toString()] || new BlockMap(),
|
|
39
|
+
}))
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default RepoStorage
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CID } from 'multiformats/cid'
|
|
2
|
+
import BlockMap from '../block-map'
|
|
3
|
+
import ReadableBlockstore from './readable-blockstore'
|
|
4
|
+
|
|
5
|
+
export class SyncStorage extends ReadableBlockstore {
|
|
6
|
+
constructor(
|
|
7
|
+
public staged: ReadableBlockstore,
|
|
8
|
+
public saved: ReadableBlockstore,
|
|
9
|
+
) {
|
|
10
|
+
super()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async getBytes(cid: CID): Promise<Uint8Array | null> {
|
|
14
|
+
const got = await this.staged.getBytes(cid)
|
|
15
|
+
if (got) return got
|
|
16
|
+
return this.saved.getBytes(cid)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async getBlocks(cids: CID[]): Promise<{ blocks: BlockMap; missing: CID[] }> {
|
|
20
|
+
const fromStaged = await this.staged.getBlocks(cids)
|
|
21
|
+
const fromSaved = await this.saved.getBlocks(fromStaged.missing)
|
|
22
|
+
const blocks = fromStaged.blocks
|
|
23
|
+
blocks.addMap(fromSaved.blocks)
|
|
24
|
+
return {
|
|
25
|
+
blocks,
|
|
26
|
+
missing: fromSaved.missing,
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async has(cid: CID): Promise<boolean> {
|
|
31
|
+
return (await this.staged.has(cid)) || (await this.saved.has(cid))
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default SyncStorage
|
package/src/storage/types.ts
CHANGED
|
@@ -5,8 +5,12 @@ export interface BlobStore {
|
|
|
5
5
|
putTemp(bytes: Uint8Array | stream.Readable): Promise<string>
|
|
6
6
|
makePermanent(key: string, cid: CID): Promise<void>
|
|
7
7
|
putPermanent(cid: CID, bytes: Uint8Array | stream.Readable): Promise<void>
|
|
8
|
+
quarantine(cid: CID): Promise<void>
|
|
9
|
+
unquarantine(cid: CID): Promise<void>
|
|
8
10
|
getBytes(cid: CID): Promise<Uint8Array>
|
|
9
11
|
getStream(cid: CID): Promise<stream.Readable>
|
|
12
|
+
hasStored(cid: CID): Promise<boolean>
|
|
13
|
+
delete(cid: CID): Promise<void>
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
export class BlobNotFoundError extends Error {}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { CID } from 'multiformats/cid'
|
|
2
|
+
import { MemoryBlockstore, RepoStorage } from '../storage'
|
|
3
|
+
import Repo from '../repo'
|
|
4
|
+
import * as verify from '../verify'
|
|
5
|
+
import * as util from '../util'
|
|
6
|
+
import { CommitData, RepoContents, WriteLog } from '../types'
|
|
7
|
+
import CidSet from '../cid-set'
|
|
8
|
+
import { MissingBlocksError } from '../error'
|
|
9
|
+
|
|
10
|
+
// Checkouts
|
|
11
|
+
// -------------
|
|
12
|
+
|
|
13
|
+
export const loadCheckout = async (
|
|
14
|
+
storage: RepoStorage,
|
|
15
|
+
repoCar: Uint8Array,
|
|
16
|
+
did: string,
|
|
17
|
+
signingKey: string,
|
|
18
|
+
): Promise<{ root: CID; contents: RepoContents }> => {
|
|
19
|
+
const { root, blocks } = await util.readCarWithRoot(repoCar)
|
|
20
|
+
const updateStorage = new MemoryBlockstore(blocks)
|
|
21
|
+
const checkout = await verify.verifyCheckout(
|
|
22
|
+
updateStorage,
|
|
23
|
+
root,
|
|
24
|
+
did,
|
|
25
|
+
signingKey,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const checkoutBlocks = await updateStorage.getBlocks(
|
|
29
|
+
checkout.newCids.toList(),
|
|
30
|
+
)
|
|
31
|
+
if (checkoutBlocks.missing.length > 0) {
|
|
32
|
+
throw new MissingBlocksError('sync', checkoutBlocks.missing)
|
|
33
|
+
}
|
|
34
|
+
await Promise.all([
|
|
35
|
+
storage.putMany(checkoutBlocks.blocks),
|
|
36
|
+
storage.updateHead(root, null),
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
root,
|
|
41
|
+
contents: checkout.contents,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Diffs
|
|
46
|
+
// -------------
|
|
47
|
+
|
|
48
|
+
export const loadFullRepo = async (
|
|
49
|
+
storage: RepoStorage,
|
|
50
|
+
repoCar: Uint8Array,
|
|
51
|
+
did: string,
|
|
52
|
+
signingKey: string,
|
|
53
|
+
): Promise<{ root: CID; writeLog: WriteLog; repo: Repo }> => {
|
|
54
|
+
const { root, blocks } = await util.readCarWithRoot(repoCar)
|
|
55
|
+
const updateStorage = new MemoryBlockstore(blocks)
|
|
56
|
+
const updates = await verify.verifyFullHistory(
|
|
57
|
+
updateStorage,
|
|
58
|
+
root,
|
|
59
|
+
did,
|
|
60
|
+
signingKey,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
const [writeLog] = await Promise.all([
|
|
64
|
+
persistUpdates(storage, updateStorage, updates),
|
|
65
|
+
storage.updateHead(root, null),
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
const repo = await Repo.load(storage, root)
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
root,
|
|
72
|
+
writeLog,
|
|
73
|
+
repo,
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const loadDiff = async (
|
|
78
|
+
repo: Repo,
|
|
79
|
+
diffCar: Uint8Array,
|
|
80
|
+
did: string,
|
|
81
|
+
signingKey: string,
|
|
82
|
+
): Promise<{ root: CID; writeLog: WriteLog }> => {
|
|
83
|
+
const { root, blocks } = await util.readCarWithRoot(diffCar)
|
|
84
|
+
const updateStorage = new MemoryBlockstore(blocks)
|
|
85
|
+
const updates = await verify.verifyUpdates(
|
|
86
|
+
repo,
|
|
87
|
+
updateStorage,
|
|
88
|
+
root,
|
|
89
|
+
did,
|
|
90
|
+
signingKey,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
const [writeLog] = await Promise.all([
|
|
94
|
+
persistUpdates(repo.storage, updateStorage, updates),
|
|
95
|
+
repo.storage.updateHead(root, repo.cid),
|
|
96
|
+
])
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
root,
|
|
100
|
+
writeLog,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Helpers
|
|
105
|
+
// -------------
|
|
106
|
+
|
|
107
|
+
export const persistUpdates = async (
|
|
108
|
+
storage: RepoStorage,
|
|
109
|
+
updateStorage: RepoStorage,
|
|
110
|
+
updates: verify.VerifiedUpdate[],
|
|
111
|
+
): Promise<WriteLog> => {
|
|
112
|
+
const newCids = new CidSet()
|
|
113
|
+
for (const update of updates) {
|
|
114
|
+
newCids.addSet(update.newCids)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const diffBlocks = await updateStorage.getBlocks(newCids.toList())
|
|
118
|
+
if (diffBlocks.missing.length > 0) {
|
|
119
|
+
throw new MissingBlocksError('sync', diffBlocks.missing)
|
|
120
|
+
}
|
|
121
|
+
const commits: CommitData[] = updates.map((update) => {
|
|
122
|
+
const forCommit = diffBlocks.blocks.getMany(update.newCids.toList())
|
|
123
|
+
if (forCommit.missing.length > 0) {
|
|
124
|
+
throw new MissingBlocksError('sync', forCommit.missing)
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
commit: update.commit,
|
|
128
|
+
prev: update.prev,
|
|
129
|
+
blocks: forCommit.blocks,
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
await storage.indexCommits(commits)
|
|
134
|
+
|
|
135
|
+
return Promise.all(
|
|
136
|
+
updates.map((upd) =>
|
|
137
|
+
util.diffToWriteDescripts(upd.diff, diffBlocks.blocks),
|
|
138
|
+
),
|
|
139
|
+
)
|
|
140
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { def, RecordPath } from '../types'
|
|
2
|
+
import { BlockWriter } from '@ipld/car/writer'
|
|
3
|
+
import { CID } from 'multiformats/cid'
|
|
4
|
+
import CidSet from '../cid-set'
|
|
5
|
+
import { MissingBlocksError } from '../error'
|
|
6
|
+
import { RepoStorage } from '../storage'
|
|
7
|
+
import * as util from '../util'
|
|
8
|
+
import { MST } from '../mst'
|
|
9
|
+
|
|
10
|
+
// Checkouts
|
|
11
|
+
// -------------
|
|
12
|
+
|
|
13
|
+
export const getCheckout = (
|
|
14
|
+
storage: RepoStorage,
|
|
15
|
+
commitCid: CID,
|
|
16
|
+
): AsyncIterable<Uint8Array> => {
|
|
17
|
+
return util.writeCar(commitCid, async (car: BlockWriter) => {
|
|
18
|
+
const commit = await storage.readObjAndBytes(commitCid, def.commit)
|
|
19
|
+
await car.put({ cid: commitCid, bytes: commit.bytes })
|
|
20
|
+
const mst = MST.load(storage, commit.obj.data)
|
|
21
|
+
await mst.writeToCarStream(car)
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Commits
|
|
26
|
+
// -------------
|
|
27
|
+
|
|
28
|
+
export const getCommits = (
|
|
29
|
+
storage: RepoStorage,
|
|
30
|
+
latest: CID,
|
|
31
|
+
earliest: CID | null,
|
|
32
|
+
): AsyncIterable<Uint8Array> => {
|
|
33
|
+
return util.writeCar(latest, (car: BlockWriter) => {
|
|
34
|
+
return writeCommitsToCarStream(storage, car, latest, earliest)
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const getFullRepo = (
|
|
39
|
+
storage: RepoStorage,
|
|
40
|
+
cid: CID,
|
|
41
|
+
): AsyncIterable<Uint8Array> => {
|
|
42
|
+
return getCommits(storage, cid, null)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const writeCommitsToCarStream = async (
|
|
46
|
+
storage: RepoStorage,
|
|
47
|
+
car: BlockWriter,
|
|
48
|
+
latest: CID,
|
|
49
|
+
earliest: CID | null,
|
|
50
|
+
): Promise<void> => {
|
|
51
|
+
const commits = await storage.getCommits(latest, earliest)
|
|
52
|
+
if (commits === null) {
|
|
53
|
+
throw new Error('Could not find shared history')
|
|
54
|
+
}
|
|
55
|
+
if (commits.length === 0) return
|
|
56
|
+
for (const commit of commits) {
|
|
57
|
+
for (const entry of commit.blocks.entries()) {
|
|
58
|
+
await car.put(entry)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Narrow slices
|
|
64
|
+
// -------------
|
|
65
|
+
|
|
66
|
+
export const getRecords = (
|
|
67
|
+
storage: RepoStorage,
|
|
68
|
+
commitCid: CID,
|
|
69
|
+
paths: RecordPath[],
|
|
70
|
+
): AsyncIterable<Uint8Array> => {
|
|
71
|
+
return util.writeCar(commitCid, async (car: BlockWriter) => {
|
|
72
|
+
const commit = await storage.readObjAndBytes(commitCid, def.commit)
|
|
73
|
+
await car.put({ cid: commitCid, bytes: commit.bytes })
|
|
74
|
+
const mst = MST.load(storage, commit.obj.data)
|
|
75
|
+
const cidsForPaths = await Promise.all(
|
|
76
|
+
paths.map((p) =>
|
|
77
|
+
mst.cidsForPath(util.formatDataKey(p.collection, p.rkey)),
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
const allCids = cidsForPaths.reduce((acc, cur) => {
|
|
81
|
+
return acc.addSet(new CidSet(cur))
|
|
82
|
+
}, new CidSet())
|
|
83
|
+
const found = await storage.getBlocks(allCids.toList())
|
|
84
|
+
if (found.missing.length > 0) {
|
|
85
|
+
throw new MissingBlocksError('writeRecordsToCarStream', found.missing)
|
|
86
|
+
}
|
|
87
|
+
for (const block of found.blocks.entries()) {
|
|
88
|
+
await car.put(block)
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
}
|