@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/src/types.ts
CHANGED
|
@@ -1,101 +1,138 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
|
-
import {
|
|
3
|
-
import { def as common } from '@atproto/common'
|
|
2
|
+
import { schema as common, def as commonDef } from '@atproto/common'
|
|
4
3
|
import { CID } from 'multiformats'
|
|
5
|
-
import
|
|
4
|
+
import BlockMap from './block-map'
|
|
5
|
+
import { RepoRecord } from '@atproto/lexicon'
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// Repo nodes
|
|
8
|
+
// ---------------
|
|
9
|
+
|
|
10
|
+
const unsignedCommit = z.object({
|
|
8
11
|
did: z.string(),
|
|
9
12
|
version: z.number(),
|
|
10
|
-
datastore: z.string(),
|
|
11
|
-
})
|
|
12
|
-
export type RepoMeta = z.infer<typeof repoMeta>
|
|
13
|
-
|
|
14
|
-
const repoRoot = z.object({
|
|
15
|
-
meta: common.cid,
|
|
16
13
|
prev: common.cid.nullable(),
|
|
17
|
-
auth_token: common.cid.nullable(),
|
|
18
14
|
data: common.cid,
|
|
19
15
|
})
|
|
20
|
-
export type
|
|
16
|
+
export type UnsignedCommit = z.infer<typeof unsignedCommit> & { sig?: never }
|
|
21
17
|
|
|
22
18
|
const commit = z.object({
|
|
23
|
-
|
|
19
|
+
did: z.string(),
|
|
20
|
+
version: z.number(),
|
|
21
|
+
prev: common.cid.nullable(),
|
|
22
|
+
data: common.cid,
|
|
24
23
|
sig: common.bytes,
|
|
25
24
|
})
|
|
26
25
|
export type Commit = z.infer<typeof commit>
|
|
27
26
|
|
|
28
|
-
export const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
cid: common.cid,
|
|
33
|
-
})
|
|
34
|
-
export type CidCreateOp = z.infer<typeof cidCreateOp>
|
|
27
|
+
export const schema = {
|
|
28
|
+
...common,
|
|
29
|
+
commit,
|
|
30
|
+
}
|
|
35
31
|
|
|
36
|
-
export const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
32
|
+
export const def = {
|
|
33
|
+
...commonDef,
|
|
34
|
+
commit: {
|
|
35
|
+
name: 'commit',
|
|
36
|
+
schema: schema.commit,
|
|
37
|
+
},
|
|
38
|
+
}
|
|
43
39
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
collection: z.string(),
|
|
47
|
-
rkey: z.string(),
|
|
48
|
-
})
|
|
49
|
-
export type DeleteOp = z.infer<typeof deleteOp>
|
|
40
|
+
// Repo Operations
|
|
41
|
+
// ---------------
|
|
50
42
|
|
|
51
|
-
export
|
|
52
|
-
|
|
43
|
+
export enum WriteOpAction {
|
|
44
|
+
Create = 'create',
|
|
45
|
+
Update = 'update',
|
|
46
|
+
Delete = 'delete',
|
|
47
|
+
}
|
|
53
48
|
|
|
54
|
-
export
|
|
55
|
-
action:
|
|
56
|
-
collection:
|
|
57
|
-
rkey:
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
export type RecordCreateOp = z.infer<typeof recordCreateOp>
|
|
49
|
+
export type RecordCreateOp = {
|
|
50
|
+
action: WriteOpAction.Create
|
|
51
|
+
collection: string
|
|
52
|
+
rkey: string
|
|
53
|
+
record: RepoRecord
|
|
54
|
+
}
|
|
61
55
|
|
|
62
|
-
export
|
|
63
|
-
action:
|
|
64
|
-
collection:
|
|
65
|
-
rkey:
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
export type RecordUpdateOp = z.infer<typeof recordUpdateOp>
|
|
56
|
+
export type RecordUpdateOp = {
|
|
57
|
+
action: WriteOpAction.Update
|
|
58
|
+
collection: string
|
|
59
|
+
rkey: string
|
|
60
|
+
record: RepoRecord
|
|
61
|
+
}
|
|
69
62
|
|
|
70
|
-
export
|
|
71
|
-
|
|
63
|
+
export type RecordDeleteOp = {
|
|
64
|
+
action: WriteOpAction.Delete
|
|
65
|
+
collection: string
|
|
66
|
+
rkey: string
|
|
67
|
+
}
|
|
72
68
|
|
|
73
|
-
export
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
commit,
|
|
78
|
-
cidWriteOp,
|
|
79
|
-
recordWriteOp,
|
|
69
|
+
export type RecordWriteOp = RecordCreateOp | RecordUpdateOp | RecordDeleteOp
|
|
70
|
+
|
|
71
|
+
export type RecordCreateDescript = RecordCreateOp & {
|
|
72
|
+
cid: CID
|
|
80
73
|
}
|
|
81
74
|
|
|
82
|
-
export
|
|
83
|
-
|
|
75
|
+
export type RecordUpdateDescript = RecordUpdateOp & {
|
|
76
|
+
prev: CID
|
|
77
|
+
cid: CID
|
|
84
78
|
}
|
|
85
79
|
|
|
86
|
-
export type
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
export type RecordDeleteDescript = RecordDeleteOp & {
|
|
81
|
+
cid: CID
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type RecordWriteDescript =
|
|
85
|
+
| RecordCreateDescript
|
|
86
|
+
| RecordUpdateDescript
|
|
87
|
+
| RecordDeleteDescript
|
|
88
|
+
|
|
89
|
+
export type WriteLog = RecordWriteDescript[][]
|
|
90
|
+
|
|
91
|
+
// Updates/Commits
|
|
92
|
+
// ---------------
|
|
93
|
+
|
|
94
|
+
export type CommitBlockData = {
|
|
95
|
+
commit: CID
|
|
96
|
+
blocks: BlockMap
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type CommitData = CommitBlockData & {
|
|
100
|
+
prev: CID | null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type RebaseData = {
|
|
104
|
+
commit: CID
|
|
105
|
+
rebased: CID
|
|
106
|
+
blocks: BlockMap
|
|
107
|
+
preservedCids: CID[]
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export type CommitCidData = {
|
|
111
|
+
commit: CID
|
|
112
|
+
prev: CID | null
|
|
113
|
+
cids: CID[]
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export type RepoUpdate = CommitData & {
|
|
117
|
+
ops: RecordWriteOp[]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export type CollectionContents = Record<string, RepoRecord>
|
|
121
|
+
export type RepoContents = Record<string, CollectionContents>
|
|
122
|
+
|
|
123
|
+
export type RepoRecordWithCid = { cid: CID; value: RepoRecord }
|
|
124
|
+
export type CollectionContentsWithCids = Record<string, RepoRecordWithCid>
|
|
125
|
+
export type RepoContentsWithCids = Record<string, CollectionContentsWithCids>
|
|
126
|
+
|
|
127
|
+
export type DatastoreContents = Record<string, CID>
|
|
128
|
+
|
|
129
|
+
export type RecordPath = {
|
|
130
|
+
collection: string
|
|
131
|
+
rkey: string
|
|
89
132
|
}
|
|
90
133
|
|
|
91
|
-
export
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
get(key: string): Promise<CID | null>
|
|
96
|
-
list(count: number, after?: string, before?: string): Promise<DataValue[]>
|
|
97
|
-
listWithPrefix(prefix: string, count?: number): Promise<DataValue[]>
|
|
98
|
-
diff(other: DataStore): Promise<DataDiff>
|
|
99
|
-
stage(): Promise<CID>
|
|
100
|
-
writeToCarStream(car: BlockWriter): Promise<void>
|
|
134
|
+
export type RecordClaim = {
|
|
135
|
+
collection: string
|
|
136
|
+
rkey: string
|
|
137
|
+
record: RepoRecord | null
|
|
101
138
|
}
|
package/src/util.ts
CHANGED
|
@@ -1,88 +1,290 @@
|
|
|
1
1
|
import { CID } from 'multiformats/cid'
|
|
2
|
-
import * as
|
|
2
|
+
import * as cbor from '@ipld/dag-cbor'
|
|
3
|
+
import { CarReader } from '@ipld/car/reader'
|
|
4
|
+
import { BlockWriter, CarWriter } from '@ipld/car/writer'
|
|
5
|
+
import { Block as CarBlock } from '@ipld/car/api'
|
|
6
|
+
import {
|
|
7
|
+
streamToBuffer,
|
|
8
|
+
verifyCidForBytes,
|
|
9
|
+
cborDecode,
|
|
10
|
+
check,
|
|
11
|
+
schema,
|
|
12
|
+
cidForCbor,
|
|
13
|
+
byteIterableToStream,
|
|
14
|
+
} from '@atproto/common'
|
|
15
|
+
import { ipldToLex, lexToIpld, LexValue, RepoRecord } from '@atproto/lexicon'
|
|
16
|
+
|
|
17
|
+
import * as crypto from '@atproto/crypto'
|
|
3
18
|
import Repo from './repo'
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
19
|
+
import { MST } from './mst'
|
|
20
|
+
import DataDiff from './data-diff'
|
|
21
|
+
import { RepoStorage } from './storage'
|
|
22
|
+
import {
|
|
23
|
+
Commit,
|
|
24
|
+
RecordCreateDescript,
|
|
25
|
+
RecordDeleteDescript,
|
|
26
|
+
RecordPath,
|
|
27
|
+
RecordUpdateDescript,
|
|
28
|
+
RecordWriteDescript,
|
|
29
|
+
UnsignedCommit,
|
|
30
|
+
WriteLog,
|
|
31
|
+
WriteOpAction,
|
|
32
|
+
} from './types'
|
|
33
|
+
import BlockMap from './block-map'
|
|
34
|
+
import { MissingBlocksError } from './error'
|
|
35
|
+
import * as parse from './parse'
|
|
36
|
+
import { Keypair } from '@atproto/crypto'
|
|
37
|
+
import { Readable } from 'stream'
|
|
38
|
+
|
|
39
|
+
export async function* verifyIncomingCarBlocks(
|
|
40
|
+
car: AsyncIterable<CarBlock>,
|
|
41
|
+
): AsyncIterable<CarBlock> {
|
|
42
|
+
for await (const block of car) {
|
|
43
|
+
await verifyCidForBytes(block.cid, block.bytes)
|
|
44
|
+
yield block
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// we have to turn the car writer output into a stream in order to properly handle errors
|
|
49
|
+
export function writeCarStream(
|
|
50
|
+
root: CID | null,
|
|
51
|
+
fn: (car: BlockWriter) => Promise<void>,
|
|
52
|
+
): Readable {
|
|
53
|
+
const { writer, out } =
|
|
54
|
+
root !== null ? CarWriter.create(root) : CarWriter.create()
|
|
55
|
+
|
|
56
|
+
const stream = byteIterableToStream(out)
|
|
57
|
+
fn(writer)
|
|
58
|
+
.catch((err) => {
|
|
59
|
+
stream.destroy(err)
|
|
60
|
+
})
|
|
61
|
+
.finally(() => writer.close())
|
|
62
|
+
return stream
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function* writeCar(
|
|
66
|
+
root: CID | null,
|
|
67
|
+
fn: (car: BlockWriter) => Promise<void>,
|
|
68
|
+
): AsyncIterable<Uint8Array> {
|
|
69
|
+
const stream = writeCarStream(root, fn)
|
|
70
|
+
for await (const chunk of stream) {
|
|
71
|
+
yield chunk
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const blocksToCarStream = (
|
|
76
|
+
root: CID | null,
|
|
77
|
+
blocks: BlockMap,
|
|
78
|
+
): AsyncIterable<Uint8Array> => {
|
|
79
|
+
return writeCar(root, async (writer) => {
|
|
80
|
+
for (const entry of blocks.entries()) {
|
|
81
|
+
await writer.put(entry)
|
|
36
82
|
}
|
|
37
|
-
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const blocksToCarFile = (
|
|
87
|
+
root: CID | null,
|
|
88
|
+
blocks: BlockMap,
|
|
89
|
+
): Promise<Uint8Array> => {
|
|
90
|
+
const carStream = blocksToCarStream(root, blocks)
|
|
91
|
+
return streamToBuffer(carStream)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const readCar = async (
|
|
95
|
+
bytes: Uint8Array,
|
|
96
|
+
): Promise<{ roots: CID[]; blocks: BlockMap }> => {
|
|
97
|
+
const car = await CarReader.fromBytes(bytes)
|
|
98
|
+
const roots = await car.getRoots()
|
|
99
|
+
const blocks = new BlockMap()
|
|
100
|
+
for await (const block of verifyIncomingCarBlocks(car.blocks())) {
|
|
101
|
+
await blocks.set(block.cid, block.bytes)
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
roots,
|
|
105
|
+
blocks,
|
|
38
106
|
}
|
|
39
|
-
return null
|
|
40
107
|
}
|
|
41
108
|
|
|
42
|
-
export const
|
|
43
|
-
|
|
44
|
-
|
|
109
|
+
export const readCarWithRoot = async (
|
|
110
|
+
bytes: Uint8Array,
|
|
111
|
+
): Promise<{ root: CID; blocks: BlockMap }> => {
|
|
112
|
+
const { roots, blocks } = await readCar(bytes)
|
|
113
|
+
if (roots.length !== 1) {
|
|
114
|
+
throw new Error(`Expected one root, got ${roots.length}`)
|
|
115
|
+
}
|
|
116
|
+
const root = roots[0]
|
|
117
|
+
return {
|
|
118
|
+
root,
|
|
119
|
+
blocks,
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const getWriteLog = async (
|
|
124
|
+
storage: RepoStorage,
|
|
45
125
|
latest: CID,
|
|
46
|
-
|
|
47
|
-
|
|
126
|
+
earliest: CID | null,
|
|
127
|
+
): Promise<WriteLog> => {
|
|
128
|
+
const commits = await storage.getCommitPath(latest, earliest)
|
|
48
129
|
if (!commits) throw new Error('Could not find shared history')
|
|
49
|
-
const heads = await Promise.all(commits.map((c) => Repo.load(
|
|
130
|
+
const heads = await Promise.all(commits.map((c) => Repo.load(storage, c)))
|
|
50
131
|
// Turn commit path into list of diffs
|
|
51
|
-
let prev
|
|
132
|
+
let prev = await MST.create(storage) // Empty
|
|
52
133
|
const msts = heads.map((h) => h.data)
|
|
53
134
|
const diffs: DataDiff[] = []
|
|
54
135
|
for (const mst of msts) {
|
|
55
|
-
diffs.push(await
|
|
136
|
+
diffs.push(await DataDiff.of(mst, prev))
|
|
56
137
|
prev = mst
|
|
57
138
|
}
|
|
139
|
+
const fullDiff = collapseDiffs(diffs)
|
|
140
|
+
const diffBlocks = await storage.getBlocks(fullDiff.newCidList())
|
|
141
|
+
if (diffBlocks.missing.length > 0) {
|
|
142
|
+
throw new MissingBlocksError('write op log', diffBlocks.missing)
|
|
143
|
+
}
|
|
58
144
|
// Map MST diffs to write ops
|
|
59
|
-
return Promise.all(
|
|
145
|
+
return Promise.all(
|
|
146
|
+
diffs.map((diff) => diffToWriteDescripts(diff, diffBlocks.blocks)),
|
|
147
|
+
)
|
|
60
148
|
}
|
|
61
149
|
|
|
62
|
-
export const
|
|
63
|
-
blockstore: IpldStore,
|
|
150
|
+
export const diffToWriteDescripts = (
|
|
64
151
|
diff: DataDiff,
|
|
65
|
-
|
|
152
|
+
blocks: BlockMap,
|
|
153
|
+
): Promise<RecordWriteDescript[]> => {
|
|
66
154
|
return Promise.all([
|
|
67
155
|
...diff.addList().map(async (add) => {
|
|
68
|
-
const { collection, rkey } =
|
|
69
|
-
const value = await
|
|
70
|
-
return {
|
|
156
|
+
const { collection, rkey } = parseDataKey(add.key)
|
|
157
|
+
const value = await parse.getAndParseRecord(blocks, add.cid)
|
|
158
|
+
return {
|
|
159
|
+
action: WriteOpAction.Create,
|
|
160
|
+
collection,
|
|
161
|
+
rkey,
|
|
162
|
+
cid: add.cid,
|
|
163
|
+
record: value.record,
|
|
164
|
+
} as RecordCreateDescript
|
|
71
165
|
}),
|
|
72
166
|
...diff.updateList().map(async (upd) => {
|
|
73
|
-
const { collection, rkey } =
|
|
74
|
-
const value = await
|
|
75
|
-
return {
|
|
167
|
+
const { collection, rkey } = parseDataKey(upd.key)
|
|
168
|
+
const value = await parse.getAndParseRecord(blocks, upd.cid)
|
|
169
|
+
return {
|
|
170
|
+
action: WriteOpAction.Update,
|
|
171
|
+
collection,
|
|
172
|
+
rkey,
|
|
173
|
+
cid: upd.cid,
|
|
174
|
+
prev: upd.prev,
|
|
175
|
+
record: value.record,
|
|
176
|
+
} as RecordUpdateDescript
|
|
76
177
|
}),
|
|
77
178
|
...diff.deleteList().map((del) => {
|
|
78
|
-
const { collection, rkey } =
|
|
79
|
-
return {
|
|
179
|
+
const { collection, rkey } = parseDataKey(del.key)
|
|
180
|
+
return {
|
|
181
|
+
action: WriteOpAction.Delete,
|
|
182
|
+
collection,
|
|
183
|
+
rkey,
|
|
184
|
+
cid: del.cid,
|
|
185
|
+
} as RecordDeleteDescript
|
|
80
186
|
}),
|
|
81
187
|
])
|
|
82
188
|
}
|
|
83
189
|
|
|
84
|
-
export const
|
|
190
|
+
export const collapseWriteLog = (log: WriteLog): RecordWriteDescript[] => {
|
|
191
|
+
const creates: Record<string, RecordCreateDescript> = {}
|
|
192
|
+
const updates: Record<string, RecordUpdateDescript> = {}
|
|
193
|
+
const deletes: Record<string, RecordDeleteDescript> = {}
|
|
194
|
+
for (const commit of log) {
|
|
195
|
+
for (const op of commit) {
|
|
196
|
+
const key = op.collection + '/' + op.rkey
|
|
197
|
+
if (op.action === WriteOpAction.Create) {
|
|
198
|
+
const del = deletes[key]
|
|
199
|
+
if (del) {
|
|
200
|
+
if (del.cid !== op.cid) {
|
|
201
|
+
updates[key] = {
|
|
202
|
+
...op,
|
|
203
|
+
action: WriteOpAction.Update,
|
|
204
|
+
prev: del.cid,
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
delete deletes[key]
|
|
208
|
+
} else {
|
|
209
|
+
creates[key] = op
|
|
210
|
+
}
|
|
211
|
+
} else if (op.action === WriteOpAction.Update) {
|
|
212
|
+
updates[key] = op
|
|
213
|
+
delete creates[key]
|
|
214
|
+
delete deletes[key]
|
|
215
|
+
} else if (op.action === WriteOpAction.Delete) {
|
|
216
|
+
if (creates[key]) {
|
|
217
|
+
delete creates[key]
|
|
218
|
+
} else {
|
|
219
|
+
delete updates[key]
|
|
220
|
+
deletes[key] = op
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
throw new Error(`unknown action: ${op}`)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return [
|
|
228
|
+
...Object.values(creates),
|
|
229
|
+
...Object.values(updates),
|
|
230
|
+
...Object.values(deletes),
|
|
231
|
+
]
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export const collapseDiffs = (diffs: DataDiff[]): DataDiff => {
|
|
235
|
+
return diffs.reduce((acc, cur) => {
|
|
236
|
+
acc.addDiff(cur)
|
|
237
|
+
return acc
|
|
238
|
+
}, new DataDiff())
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export const parseDataKey = (key: string): RecordPath => {
|
|
85
242
|
const parts = key.split('/')
|
|
86
243
|
if (parts.length !== 2) throw new Error(`Invalid record key: ${key}`)
|
|
87
244
|
return { collection: parts[0], rkey: parts[1] }
|
|
88
245
|
}
|
|
246
|
+
|
|
247
|
+
export const formatDataKey = (collection: string, rkey: string): string => {
|
|
248
|
+
return collection + '/' + rkey
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export const metaEqual = (a: Commit, b: Commit): boolean => {
|
|
252
|
+
return a.did === b.did && a.version === b.version
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export const signCommit = async (
|
|
256
|
+
unsigned: UnsignedCommit,
|
|
257
|
+
keypair: Keypair,
|
|
258
|
+
): Promise<Commit> => {
|
|
259
|
+
const encoded = cbor.encode(unsigned)
|
|
260
|
+
const sig = await keypair.sign(encoded)
|
|
261
|
+
return {
|
|
262
|
+
...unsigned,
|
|
263
|
+
sig,
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export const verifyCommitSig = async (
|
|
268
|
+
commit: Commit,
|
|
269
|
+
didKey: string,
|
|
270
|
+
): Promise<boolean> => {
|
|
271
|
+
const { sig, ...rest } = commit
|
|
272
|
+
const encoded = cbor.encode(rest)
|
|
273
|
+
return crypto.verifySignature(didKey, encoded, sig)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export const cborToLex = (val: Uint8Array): LexValue => {
|
|
277
|
+
return ipldToLex(cborDecode(val))
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export const cborToLexRecord = (val: Uint8Array): RepoRecord => {
|
|
281
|
+
const parsed = cborToLex(val)
|
|
282
|
+
if (!check.is(parsed, schema.map)) {
|
|
283
|
+
throw new Error('lexicon records be a json object')
|
|
284
|
+
}
|
|
285
|
+
return parsed
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export const cidForRecord = async (val: LexValue) => {
|
|
289
|
+
return cidForCbor(lexToIpld(val))
|
|
290
|
+
}
|