@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
package/dist/util.d.ts
CHANGED
|
@@ -1,13 +1,36 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { CID } from 'multiformats/cid';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
import { BlockWriter } from '@ipld/car/writer';
|
|
4
|
+
import { Block as CarBlock } from '@ipld/car/api';
|
|
5
|
+
import { LexValue, RepoRecord } from '@atproto/lexicon';
|
|
6
|
+
import DataDiff from './data-diff';
|
|
7
|
+
import { RepoStorage } from './storage';
|
|
8
|
+
import { Commit, RecordPath, RecordWriteDescript, UnsignedCommit, WriteLog } from './types';
|
|
9
|
+
import BlockMap from './block-map';
|
|
10
|
+
import { Keypair } from '@atproto/crypto';
|
|
11
|
+
import { Readable } from 'stream';
|
|
12
|
+
export declare function verifyIncomingCarBlocks(car: AsyncIterable<CarBlock>): AsyncIterable<CarBlock>;
|
|
13
|
+
export declare function writeCarStream(root: CID | null, fn: (car: BlockWriter) => Promise<void>): Readable;
|
|
14
|
+
export declare function writeCar(root: CID | null, fn: (car: BlockWriter) => Promise<void>): AsyncIterable<Uint8Array>;
|
|
15
|
+
export declare const blocksToCarStream: (root: CID | null, blocks: BlockMap) => AsyncIterable<Uint8Array>;
|
|
16
|
+
export declare const blocksToCarFile: (root: CID | null, blocks: BlockMap) => Promise<Uint8Array>;
|
|
17
|
+
export declare const readCar: (bytes: Uint8Array) => Promise<{
|
|
18
|
+
roots: CID[];
|
|
19
|
+
blocks: BlockMap;
|
|
20
|
+
}>;
|
|
21
|
+
export declare const readCarWithRoot: (bytes: Uint8Array) => Promise<{
|
|
22
|
+
root: CID;
|
|
23
|
+
blocks: BlockMap;
|
|
24
|
+
}>;
|
|
25
|
+
export declare const getWriteLog: (storage: RepoStorage, latest: CID, earliest: CID | null) => Promise<WriteLog>;
|
|
26
|
+
export declare const diffToWriteDescripts: (diff: DataDiff, blocks: BlockMap) => Promise<RecordWriteDescript[]>;
|
|
27
|
+
export declare const collapseWriteLog: (log: WriteLog) => RecordWriteDescript[];
|
|
28
|
+
export declare const collapseDiffs: (diffs: DataDiff[]) => DataDiff;
|
|
29
|
+
export declare const parseDataKey: (key: string) => RecordPath;
|
|
30
|
+
export declare const formatDataKey: (collection: string, rkey: string) => string;
|
|
31
|
+
export declare const metaEqual: (a: Commit, b: Commit) => boolean;
|
|
32
|
+
export declare const signCommit: (unsigned: UnsignedCommit, keypair: Keypair) => Promise<Commit>;
|
|
33
|
+
export declare const verifyCommitSig: (commit: Commit, didKey: string) => Promise<boolean>;
|
|
34
|
+
export declare const cborToLex: (val: Uint8Array) => LexValue;
|
|
35
|
+
export declare const cborToLexRecord: (val: Uint8Array) => RepoRecord;
|
|
36
|
+
export declare const cidForRecord: (val: LexValue) => Promise<CID>;
|
package/dist/verify.d.ts
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
import { CID } from 'multiformats/cid';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
2
|
+
import { ReadableBlockstore, RepoStorage } from './storage';
|
|
3
|
+
import DataDiff from './data-diff';
|
|
4
|
+
import ReadableRepo from './readable-repo';
|
|
5
|
+
import CidSet from './cid-set';
|
|
6
|
+
import { RecordClaim, RepoContents, RepoContentsWithCids } from './types';
|
|
7
|
+
export declare type VerifiedCheckout = {
|
|
8
|
+
contents: RepoContents;
|
|
9
|
+
newCids: CidSet;
|
|
10
|
+
};
|
|
11
|
+
export declare type VerifiedCheckoutWithCids = {
|
|
12
|
+
commit: CID;
|
|
13
|
+
contents: RepoContentsWithCids;
|
|
14
|
+
};
|
|
15
|
+
export declare const verifyCheckout: (storage: ReadableBlockstore, head: CID, did: string, signingKey: string) => Promise<VerifiedCheckout>;
|
|
16
|
+
export declare const verifyCheckoutWithCids: (storage: ReadableBlockstore, head: CID, did: string, signingKey: string) => Promise<VerifiedCheckoutWithCids>;
|
|
17
|
+
export declare type VerifiedUpdate = {
|
|
18
|
+
commit: CID;
|
|
19
|
+
prev: CID | null;
|
|
20
|
+
diff: DataDiff;
|
|
21
|
+
newCids: CidSet;
|
|
22
|
+
};
|
|
23
|
+
export declare const verifyFullHistory: (storage: RepoStorage, head: CID, did: string, signingKey: string) => Promise<VerifiedUpdate[]>;
|
|
24
|
+
export declare const verifyUpdates: (repo: ReadableRepo, updateStorage: RepoStorage, updateRoot: CID, did: string, signingKey: string) => Promise<VerifiedUpdate[]>;
|
|
25
|
+
export declare const verifyCommitPath: (baseRepo: ReadableRepo, storage: ReadableBlockstore, commitPath: CID[], did: string, signingKey: string) => Promise<VerifiedUpdate[]>;
|
|
26
|
+
export declare const verifyProofs: (proofs: Uint8Array, claims: RecordClaim[], did: string, didKey: string) => Promise<{
|
|
27
|
+
verified: RecordClaim[];
|
|
28
|
+
unverified: RecordClaim[];
|
|
29
|
+
}>;
|
|
30
|
+
export declare const verifyRecords: (proofs: Uint8Array, did: string, signingKey: string) => Promise<RecordClaim[]>;
|
|
31
|
+
export declare class RepoVerificationError extends Error {
|
|
32
|
+
}
|
package/jest.bench.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/repo",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/bluesky-social/atproto.git",
|
|
9
|
+
"directory": "packages/repo"
|
|
10
|
+
},
|
|
6
11
|
"scripts": {
|
|
7
12
|
"test": "jest",
|
|
8
13
|
"test:profile": "node --inspect ../../node_modules/.bin/jest",
|
|
9
14
|
"bench": "jest --config jest.bench.config.js",
|
|
10
|
-
"bench:profile": "node --inspect ../../node_modules/.bin/jest --config jest.bench.config.js",
|
|
11
|
-
"prettier": "prettier --check src/",
|
|
12
|
-
"prettier:fix": "prettier --write src/",
|
|
15
|
+
"bench:profile": "node --inspect-brk ../../node_modules/.bin/jest --config jest.bench.config.js",
|
|
16
|
+
"prettier": "prettier --check src/ tests/",
|
|
17
|
+
"prettier:fix": "prettier --write src/ tests/",
|
|
13
18
|
"lint": "eslint . --ext .ts,.tsx",
|
|
14
19
|
"lint:fix": "yarn lint --fix",
|
|
15
20
|
"verify": "run-p prettier lint",
|
|
@@ -22,13 +27,15 @@
|
|
|
22
27
|
"postpublish": "npm run update-main-to-src"
|
|
23
28
|
},
|
|
24
29
|
"dependencies": {
|
|
25
|
-
"@atproto/auth": "*",
|
|
26
30
|
"@atproto/common": "*",
|
|
31
|
+
"@atproto/crypto": "*",
|
|
32
|
+
"@atproto/identity": "*",
|
|
33
|
+
"@atproto/lexicon": "*",
|
|
27
34
|
"@atproto/nsid": "*",
|
|
28
35
|
"@ipld/car": "^3.2.3",
|
|
29
36
|
"@ipld/dag-cbor": "^7.0.0",
|
|
30
37
|
"multiformats": "^9.6.4",
|
|
31
38
|
"uint8arrays": "3.0.0",
|
|
32
|
-
"zod": "^3.
|
|
39
|
+
"zod": "^3.21.4"
|
|
33
40
|
}
|
|
34
41
|
}
|
package/src/block-map.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { lexToIpld, LexValue } from '@atproto/lexicon'
|
|
2
|
+
import { dataToCborBlock } from '@atproto/common'
|
|
3
|
+
import { CID } from 'multiformats/cid'
|
|
4
|
+
import * as uint8arrays from 'uint8arrays'
|
|
5
|
+
|
|
6
|
+
export class BlockMap {
|
|
7
|
+
private map: Map<string, Uint8Array> = new Map()
|
|
8
|
+
|
|
9
|
+
async add(value: LexValue): Promise<CID> {
|
|
10
|
+
const block = await dataToCborBlock(lexToIpld(value))
|
|
11
|
+
this.set(block.cid, block.bytes)
|
|
12
|
+
return block.cid
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
set(cid: CID, bytes: Uint8Array) {
|
|
16
|
+
this.map.set(cid.toString(), bytes)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get(cid: CID): Uint8Array | undefined {
|
|
20
|
+
return this.map.get(cid.toString())
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
delete(cid: CID) {
|
|
24
|
+
this.map.delete(cid.toString())
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getMany(cids: CID[]): { blocks: BlockMap; missing: CID[] } {
|
|
28
|
+
const missing: CID[] = []
|
|
29
|
+
const blocks = new BlockMap()
|
|
30
|
+
for (const cid of cids) {
|
|
31
|
+
const got = this.map.get(cid.toString())
|
|
32
|
+
if (got) {
|
|
33
|
+
blocks.set(cid, got)
|
|
34
|
+
} else {
|
|
35
|
+
missing.push(cid)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return { blocks, missing }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
has(cid: CID): boolean {
|
|
42
|
+
return this.map.has(cid.toString())
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
clear(): void {
|
|
46
|
+
this.map.clear()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
forEach(cb: (bytes: Uint8Array, cid: CID) => void): void {
|
|
50
|
+
this.map.forEach((val, key) => cb(val, CID.parse(key)))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
entries(): Entry[] {
|
|
54
|
+
const entries: Entry[] = []
|
|
55
|
+
this.forEach((bytes, cid) => {
|
|
56
|
+
entries.push({ cid, bytes })
|
|
57
|
+
})
|
|
58
|
+
return entries
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
cids(): CID[] {
|
|
62
|
+
return this.entries().map((e) => e.cid)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
addMap(toAdd: BlockMap) {
|
|
66
|
+
toAdd.forEach((bytes, cid) => {
|
|
67
|
+
this.set(cid, bytes)
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get size(): number {
|
|
72
|
+
return this.map.size
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
get byteSize(): number {
|
|
76
|
+
let size = 0
|
|
77
|
+
this.forEach((bytes) => {
|
|
78
|
+
size += bytes.length
|
|
79
|
+
})
|
|
80
|
+
return size
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
equals(other: BlockMap): boolean {
|
|
84
|
+
if (this.size !== other.size) {
|
|
85
|
+
return false
|
|
86
|
+
}
|
|
87
|
+
for (const entry of this.entries()) {
|
|
88
|
+
const otherBytes = other.get(entry.cid)
|
|
89
|
+
if (!otherBytes) return false
|
|
90
|
+
if (!uint8arrays.equals(entry.bytes, otherBytes)) {
|
|
91
|
+
return false
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return true
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
type Entry = {
|
|
99
|
+
cid: CID
|
|
100
|
+
bytes: Uint8Array
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export default BlockMap
|
package/src/cid-set.ts
CHANGED
package/src/data-diff.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { CID } from 'multiformats'
|
|
2
|
+
import CidSet from './cid-set'
|
|
3
|
+
import { MST, mstDiff } from './mst'
|
|
4
|
+
|
|
5
|
+
export class DataDiff {
|
|
6
|
+
adds: Record<string, DataAdd> = {}
|
|
7
|
+
updates: Record<string, DataUpdate> = {}
|
|
8
|
+
deletes: Record<string, DataDelete> = {}
|
|
9
|
+
|
|
10
|
+
newCids: CidSet = new CidSet()
|
|
11
|
+
removedCids: CidSet = new CidSet()
|
|
12
|
+
|
|
13
|
+
static async of(curr: MST, prev: MST | null): Promise<DataDiff> {
|
|
14
|
+
return mstDiff(curr, prev)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
recordAdd(key: string, cid: CID): void {
|
|
18
|
+
this.adds[key] = { key, cid }
|
|
19
|
+
this.newCids.add(cid)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
recordUpdate(key: string, prev: CID, cid: CID): void {
|
|
23
|
+
this.updates[key] = { key, prev, cid }
|
|
24
|
+
this.newCids.add(cid)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
recordDelete(key: string, cid: CID): void {
|
|
28
|
+
this.deletes[key] = { key, cid }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
recordNewCid(cid: CID): void {
|
|
32
|
+
if (this.removedCids.has(cid)) {
|
|
33
|
+
this.removedCids.delete(cid)
|
|
34
|
+
} else {
|
|
35
|
+
this.newCids.add(cid)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
recordRemovedCid(cid: CID): void {
|
|
40
|
+
if (this.newCids.has(cid)) {
|
|
41
|
+
this.newCids.delete(cid)
|
|
42
|
+
} else {
|
|
43
|
+
this.removedCids.add(cid)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
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
|
+
addList(): DataAdd[] {
|
|
76
|
+
return Object.values(this.adds)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
updateList(): DataUpdate[] {
|
|
80
|
+
return Object.values(this.updates)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
deleteList(): DataDelete[] {
|
|
84
|
+
return Object.values(this.deletes)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
newCidList(): CID[] {
|
|
88
|
+
return this.newCids.toList()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
updatedKeys(): string[] {
|
|
92
|
+
const keys = [
|
|
93
|
+
...Object.keys(this.adds),
|
|
94
|
+
...Object.keys(this.updates),
|
|
95
|
+
...Object.keys(this.deletes),
|
|
96
|
+
]
|
|
97
|
+
return [...new Set(keys)]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type DataAdd = {
|
|
102
|
+
key: string
|
|
103
|
+
cid: CID
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type DataUpdate = {
|
|
107
|
+
key: string
|
|
108
|
+
prev: CID
|
|
109
|
+
cid: CID
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type DataDelete = {
|
|
113
|
+
key: string
|
|
114
|
+
cid: CID
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
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,10 @@
|
|
|
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'
|
|
9
|
+
export * from './data-diff'
|
|
7
10
|
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
|
}
|