generated from fission-codes/project-template
-
Notifications
You must be signed in to change notification settings - Fork 24
/
PrivateHistory.ts
144 lines (122 loc) · 3.83 KB
/
PrivateHistory.ts
1
2
3
4
5
6
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import * as Uint8arrays from "uint8arrays"
import * as Crypto from "../../components/crypto/implementation.js"
import * as Depot from "../../components/depot/implementation.js"
import * as Protocol from "../protocol/index.js"
import MMPT from "../protocol/private/mmpt.js"
import { BareNameFilter } from "../protocol/private/namefilter.js"
import { DecryptedNode, Revision } from "../protocol/private/types.js"
import { Maybe, decodeCID } from "../../common/index.js"
import { Metadata } from "../metadata.js"
export interface Node {
fromInfo: (mmpt: MMPT, key: Uint8Array, info: DecryptedNode) => Promise<Node>
header: {
bareNameFilter: BareNameFilter
metadata: Metadata
revision: number
}
key: Uint8Array
mmpt: MMPT
}
export default class PrivateHistory {
readonly node: Node
crypto: Crypto.Implementation
depot: Depot.Implementation
constructor(crypto: Crypto.Implementation, depot: Depot.Implementation, node: Node) {
this.crypto = crypto
this.depot = depot
this.node = node
}
/**
* Go back one or more versions.
*
* @param delta Optional negative number to specify how far to go back
*/
async back(delta = -1): Promise<Maybe<Node>> {
const n = Math.min(delta, -1)
const revision = this.node.header?.revision
return (revision && await this._getRevision(revision + n)) || null
}
// async forward(delta: number = 1): Promise<Maybe<Node>> {
// const n = Math.max(delta, 1)
// const revision = this.node.header?.revision
// return (revision && await this._getRevision(revision + n)) || null
// }
/**
* Get a version before a given timestamp.
*
* @param timestamp Unix timestamp in seconds
*/
async prior(timestamp: number): Promise<Maybe<Node>> {
if (this.node.header.metadata.unixMeta.mtime < timestamp) {
return this.node
} else {
return this._prior(this.node.header.revision - 1, timestamp)
}
}
/**
* List earlier versions along with the timestamp they were created.
*/
async list(amount = 5): Promise<Array<{ delta: number; timestamp: number }>> {
const max = this.node.header.revision
return Promise.all(
Array.from({ length: amount }, (_, i) => {
const n = i + 1
return this._getRevisionInfoFromNumber(max - n).then(info => ({
revisionInfo: info,
delta: -n
}))
})
).then(
list => list.filter(a => !!a.revisionInfo) as Array<{ revisionInfo: DecryptedNode; delta: number }>
).then(
list => list.map(a => {
const mtime = a.revisionInfo.metadata.unixMeta.mtime
return { delta: a.delta, timestamp: mtime }
})
)
}
/**
* @internal
*/
async _getRevision(revision: number): Promise<Maybe<Node>> {
const info = await this._getRevisionInfoFromNumber(revision)
return info && await this.node.fromInfo(
this.node.mmpt,
this.node.key,
info
)
}
/**
* @internal
*/
_getRevisionInfo(revision: Revision): Promise<DecryptedNode> {
return Protocol.priv.readNode(
this.depot,
this.crypto,
decodeCID(revision.cid),
this.node.key
)
}
/**
* @internal
*/
async _getRevisionInfoFromNumber(revision: number): Promise<Maybe<DecryptedNode>> {
const { mmpt } = this.node
const { bareNameFilter } = this.node.header
const key = this.node.key
const r = await Protocol.priv.getRevision(this.crypto, mmpt, bareNameFilter, key, revision)
return r && this._getRevisionInfo(r)
}
/**
* @internal
*/
async _prior(revision: number, timestamp: number): Promise<Maybe<Node>> {
const info = await this._getRevisionInfoFromNumber(revision)
if (!info?.revision) return null
if (info.metadata.unixMeta.mtime < timestamp) {
return this._getRevision(info.revision)
} else {
return this._prior(info.revision - 1, timestamp)
}
}
}