cojson 0.0.10 → 0.0.12
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/README.md +2 -2
- package/dist/account.d.ts +57 -0
- package/dist/account.js +76 -0
- package/dist/account.js.map +1 -0
- package/dist/account.test.d.ts +1 -0
- package/dist/account.test.js +40 -0
- package/dist/account.test.js.map +1 -0
- package/dist/coValue.d.ts +16 -35
- package/dist/coValue.js +49 -112
- package/dist/coValue.js.map +1 -1
- package/dist/coValue.test.js +16 -16
- package/dist/coValue.test.js.map +1 -1
- package/dist/contentType.d.ts +9 -9
- package/dist/contentType.js.map +1 -1
- package/dist/contentType.test.js +13 -17
- package/dist/contentType.test.js.map +1 -1
- package/dist/contentTypes/coList.d.ts +3 -3
- package/dist/contentTypes/coList.js.map +1 -1
- package/dist/contentTypes/coMap.d.ts +31 -21
- package/dist/contentTypes/coMap.js +28 -0
- package/dist/contentTypes/coMap.js.map +1 -1
- package/dist/contentTypes/coStream.d.ts +3 -3
- package/dist/contentTypes/coStream.js.map +1 -1
- package/dist/contentTypes/static.d.ts +4 -4
- package/dist/contentTypes/static.js.map +1 -1
- package/dist/crypto.d.ts +45 -39
- package/dist/crypto.js +68 -49
- package/dist/crypto.js.map +1 -1
- package/dist/crypto.test.js +45 -49
- package/dist/crypto.test.js.map +1 -1
- package/dist/ids.d.ts +5 -3
- package/dist/ids.js +3 -1
- package/dist/ids.js.map +1 -1
- package/dist/index.d.ts +12 -14
- package/dist/index.js +6 -8
- package/dist/index.js.map +1 -1
- package/dist/jsonValue.d.ts +2 -2
- package/dist/node.d.ts +25 -15
- package/dist/node.js +88 -33
- package/dist/node.js.map +1 -1
- package/dist/permissions.d.ts +27 -33
- package/dist/permissions.js +55 -47
- package/dist/permissions.js.map +1 -1
- package/dist/permissions.test.js +231 -314
- package/dist/permissions.test.js.map +1 -1
- package/dist/sync.d.ts +26 -28
- package/dist/sync.js +81 -67
- package/dist/sync.js.map +1 -1
- package/dist/sync.test.js +194 -293
- package/dist/sync.test.js.map +1 -1
- package/dist/testUtils.d.ts +37 -0
- package/dist/testUtils.js +157 -0
- package/dist/testUtils.js.map +1 -0
- package/package.json +1 -1
- package/src/account.test.ts +67 -0
- package/src/account.ts +152 -0
- package/src/coValue.test.ts +17 -31
- package/src/coValue.ts +93 -179
- package/src/contentType.test.ts +18 -45
- package/src/contentType.ts +15 -13
- package/src/contentTypes/coList.ts +4 -4
- package/src/contentTypes/coMap.ts +55 -29
- package/src/contentTypes/coStream.ts +4 -4
- package/src/contentTypes/static.ts +5 -5
- package/src/crypto.test.ts +53 -59
- package/src/crypto.ts +123 -95
- package/src/ids.ts +9 -3
- package/src/index.ts +14 -25
- package/src/jsonValue.ts +2 -2
- package/src/node.ts +189 -61
- package/src/permissions.test.ts +370 -404
- package/src/permissions.ts +126 -109
- package/src/sync.test.ts +284 -426
- package/src/sync.ts +115 -105
- package/src/testUtils.ts +229 -0
package/src/node.ts
CHANGED
|
@@ -1,40 +1,94 @@
|
|
|
1
|
-
import { createdNowUnique, newRandomKeySecret, seal } from './crypto.js';
|
|
2
1
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Agent,
|
|
6
|
-
getAgent,
|
|
2
|
+
AgentSecret,
|
|
3
|
+
createdNowUnique,
|
|
7
4
|
getAgentID,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
5
|
+
getAgentSealerID,
|
|
6
|
+
getAgentSealerSecret,
|
|
7
|
+
newRandomAgentSecret,
|
|
8
|
+
newRandomKeySecret,
|
|
9
|
+
seal,
|
|
10
|
+
} from "./crypto.js";
|
|
11
|
+
import { CoValue, CoValueHeader, newRandomSessionID } from "./coValue.js";
|
|
12
|
+
import { Team, TeamContent, expectTeamContent } from "./permissions.js";
|
|
13
|
+
import { Peer, SyncManager } from "./sync.js";
|
|
14
|
+
import { AgentID, RawCoID, SessionID, isAgentID } from "./ids.js";
|
|
15
|
+
import { CoID, ContentType } from "./contentType.js";
|
|
16
|
+
import {
|
|
17
|
+
Account,
|
|
18
|
+
AccountMeta,
|
|
19
|
+
AccountIDOrAgentID,
|
|
20
|
+
accountHeaderForInitialAgentSecret,
|
|
21
|
+
GeneralizedControlledAccount,
|
|
22
|
+
ControlledAccount,
|
|
23
|
+
AnonymousControlledAccount,
|
|
24
|
+
AccountID,
|
|
25
|
+
Profile,
|
|
26
|
+
AccountContent,
|
|
27
|
+
ProfileContent,
|
|
28
|
+
ProfileMeta,
|
|
29
|
+
} from "./account.js";
|
|
30
|
+
import { CoMap } from "./index.js";
|
|
16
31
|
|
|
17
32
|
export class LocalNode {
|
|
18
|
-
coValues: { [key:
|
|
19
|
-
|
|
20
|
-
agentID: AgentID;
|
|
33
|
+
coValues: { [key: RawCoID]: CoValueState } = {};
|
|
34
|
+
account: GeneralizedControlledAccount;
|
|
21
35
|
ownSessionID: SessionID;
|
|
22
36
|
sync = new SyncManager(this);
|
|
23
37
|
|
|
24
|
-
constructor(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
this.
|
|
38
|
+
constructor(
|
|
39
|
+
account: GeneralizedControlledAccount,
|
|
40
|
+
ownSessionID: SessionID
|
|
41
|
+
) {
|
|
42
|
+
this.account = account;
|
|
29
43
|
this.ownSessionID = ownSessionID;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static withNewlyCreatedAccount(name: string): {
|
|
47
|
+
node: LocalNode;
|
|
48
|
+
accountID: AccountID;
|
|
49
|
+
accountSecret: AgentSecret;
|
|
50
|
+
sessionID: SessionID;
|
|
51
|
+
} {
|
|
52
|
+
const throwawayAgent = newRandomAgentSecret();
|
|
53
|
+
const setupNode = new LocalNode(
|
|
54
|
+
new AnonymousControlledAccount(throwawayAgent),
|
|
55
|
+
newRandomSessionID(getAgentID(throwawayAgent))
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const account = setupNode.createAccount(name);
|
|
59
|
+
|
|
60
|
+
const nodeWithAccount = account.node.testWithDifferentAccount(
|
|
61
|
+
account,
|
|
62
|
+
newRandomSessionID(account.id)
|
|
63
|
+
);
|
|
30
64
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
65
|
+
return {
|
|
66
|
+
node: nodeWithAccount,
|
|
67
|
+
accountID: account.id,
|
|
68
|
+
accountSecret: account.agentSecret,
|
|
69
|
+
sessionID: nodeWithAccount.ownSessionID,
|
|
35
70
|
};
|
|
36
71
|
}
|
|
37
72
|
|
|
73
|
+
static async withLoadedAccount(accountID: AccountID, accountSecret: AgentSecret, sessionID: SessionID, peersToLoadFrom: Peer[]): Promise<LocalNode> {
|
|
74
|
+
const loadingNode = new LocalNode(new AnonymousControlledAccount(accountSecret), newRandomSessionID(accountID));
|
|
75
|
+
|
|
76
|
+
const accountPromise = loadingNode.load(accountID);
|
|
77
|
+
|
|
78
|
+
for (const peer of peersToLoadFrom) {
|
|
79
|
+
loadingNode.sync.addPeer(peer);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const account = await accountPromise;
|
|
83
|
+
|
|
84
|
+
// since this is all synchronous, we can just swap out nodes for the SyncManager
|
|
85
|
+
const node = loadingNode.testWithDifferentAccount(new ControlledAccount(accountSecret, account, loadingNode), sessionID);
|
|
86
|
+
node.sync = loadingNode.sync;
|
|
87
|
+
node.sync.local = node;
|
|
88
|
+
|
|
89
|
+
return node;
|
|
90
|
+
}
|
|
91
|
+
|
|
38
92
|
createCoValue(header: CoValueHeader): CoValue {
|
|
39
93
|
const coValue = new CoValue(header, this);
|
|
40
94
|
this.coValues[coValue.id] = { state: "loaded", coValue: coValue };
|
|
@@ -44,7 +98,7 @@ export class LocalNode {
|
|
|
44
98
|
return coValue;
|
|
45
99
|
}
|
|
46
100
|
|
|
47
|
-
loadCoValue(id:
|
|
101
|
+
loadCoValue(id: RawCoID): Promise<CoValue> {
|
|
48
102
|
let entry = this.coValues[id];
|
|
49
103
|
if (!entry) {
|
|
50
104
|
entry = newLoadingState();
|
|
@@ -59,11 +113,21 @@ export class LocalNode {
|
|
|
59
113
|
return entry.done;
|
|
60
114
|
}
|
|
61
115
|
|
|
62
|
-
async load<T extends ContentType>(id:
|
|
116
|
+
async load<T extends ContentType>(id: CoID<T>): Promise<T> {
|
|
63
117
|
return (await this.loadCoValue(id)).getCurrentContent() as T;
|
|
64
118
|
}
|
|
65
119
|
|
|
66
|
-
|
|
120
|
+
async loadProfile(id: AccountID): Promise<Profile> {
|
|
121
|
+
const account = await this.load<CoMap<AccountContent>>(id);
|
|
122
|
+
const profileID = account.get("profile");
|
|
123
|
+
|
|
124
|
+
if (!profileID) {
|
|
125
|
+
throw new Error(`Account ${id} has no profile`);
|
|
126
|
+
}
|
|
127
|
+
return (await this.loadCoValue(profileID)).getCurrentContent() as Profile;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
expectCoValueLoaded(id: RawCoID, expectation?: string): CoValue {
|
|
67
131
|
const entry = this.coValues[id];
|
|
68
132
|
if (!entry) {
|
|
69
133
|
throw new Error(
|
|
@@ -80,75 +144,139 @@ export class LocalNode {
|
|
|
80
144
|
return entry.coValue;
|
|
81
145
|
}
|
|
82
146
|
|
|
83
|
-
|
|
84
|
-
const
|
|
147
|
+
expectProfileLoaded(id: AccountID, expectation?: string): Profile {
|
|
148
|
+
const account = this.expectCoValueLoaded(id, expectation);
|
|
149
|
+
const profileID = expectTeamContent(account.getCurrentContent()).get("profile");
|
|
150
|
+
if (!profileID) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
`${
|
|
153
|
+
expectation ? expectation + ": " : ""
|
|
154
|
+
}Account ${id} has no profile`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
return this.expectCoValueLoaded(profileID, expectation).getCurrentContent() as Profile;
|
|
158
|
+
}
|
|
85
159
|
|
|
86
|
-
|
|
160
|
+
createAccount(name: string): ControlledAccount {
|
|
161
|
+
const agentSecret = newRandomAgentSecret();
|
|
87
162
|
|
|
88
|
-
|
|
89
|
-
|
|
163
|
+
const account = this.createCoValue(
|
|
164
|
+
accountHeaderForInitialAgentSecret(agentSecret)
|
|
165
|
+
).testWithDifferentAccount(
|
|
166
|
+
new AnonymousControlledAccount(agentSecret),
|
|
167
|
+
newRandomSessionID(getAgentID(agentSecret))
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const accountAsTeam = new Team(expectTeamContent(account.getCurrentContent()), account.node);
|
|
171
|
+
|
|
172
|
+
accountAsTeam.teamMap.edit((editable) => {
|
|
173
|
+
editable.set(getAgentID(agentSecret), "admin", "trusting");
|
|
174
|
+
|
|
175
|
+
const readKey = newRandomKeySecret();
|
|
176
|
+
|
|
177
|
+
editable.set(
|
|
178
|
+
`${readKey.id}_for_${getAgentID(agentSecret)}`,
|
|
179
|
+
seal(
|
|
180
|
+
readKey.secret,
|
|
181
|
+
getAgentSealerSecret(agentSecret),
|
|
182
|
+
getAgentSealerID(getAgentID(agentSecret)),
|
|
183
|
+
{
|
|
184
|
+
in: account.id,
|
|
185
|
+
tx: account.nextTransactionID(),
|
|
186
|
+
}
|
|
187
|
+
),
|
|
188
|
+
"trusting"
|
|
189
|
+
);
|
|
90
190
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
191
|
+
editable.set("readKey", readKey.id, "trusting");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const controlledAccount = new ControlledAccount(
|
|
195
|
+
agentSecret,
|
|
196
|
+
account.getCurrentContent() as CoMap<AccountContent, AccountMeta>,
|
|
197
|
+
account.node
|
|
95
198
|
);
|
|
96
199
|
|
|
97
|
-
|
|
200
|
+
const profile = accountAsTeam.createMap<ProfileContent, ProfileMeta>({ type: "profile" });
|
|
201
|
+
|
|
202
|
+
profile.edit((editable) => {
|
|
203
|
+
editable.set("name", name, "trusting");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
accountAsTeam.teamMap.edit((editable) => {
|
|
207
|
+
editable.set("profile", profile.id, "trusting");
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
return controlledAccount;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
resolveAccountAgent(id: AccountIDOrAgentID, expectation?: string): AgentID {
|
|
214
|
+
if (isAgentID(id)) {
|
|
215
|
+
return id;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const coValue = this.expectCoValueLoaded(id, expectation);
|
|
219
|
+
|
|
220
|
+
if (
|
|
221
|
+
coValue.header.type !== "comap" ||
|
|
222
|
+
coValue.header.ruleset.type !== "team" ||
|
|
223
|
+
!coValue.header.meta ||
|
|
224
|
+
!("type" in coValue.header.meta) ||
|
|
225
|
+
coValue.header.meta.type !== "account"
|
|
226
|
+
) {
|
|
98
227
|
throw new Error(
|
|
99
228
|
`${
|
|
100
229
|
expectation ? expectation + ": " : ""
|
|
101
|
-
}CoValue ${id} is not an
|
|
230
|
+
}CoValue ${id} is not an account`
|
|
102
231
|
);
|
|
103
232
|
}
|
|
104
233
|
|
|
105
|
-
return
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
234
|
+
return new Account(
|
|
235
|
+
coValue.getCurrentContent() as CoMap<TeamContent, AccountMeta>,
|
|
236
|
+
this
|
|
237
|
+
).getCurrentAgentID();
|
|
110
238
|
}
|
|
111
239
|
|
|
112
240
|
createTeam(): Team {
|
|
113
241
|
const teamCoValue = this.createCoValue({
|
|
114
242
|
type: "comap",
|
|
115
|
-
ruleset: { type: "team", initialAdmin: this.
|
|
243
|
+
ruleset: { type: "team", initialAdmin: this.account.id },
|
|
116
244
|
meta: null,
|
|
117
245
|
...createdNowUnique(),
|
|
118
|
-
publicNickname: "team",
|
|
119
246
|
});
|
|
120
247
|
|
|
121
248
|
let teamContent = expectTeamContent(teamCoValue.getCurrentContent());
|
|
122
249
|
|
|
123
250
|
teamContent = teamContent.edit((editable) => {
|
|
124
|
-
editable.set(this.
|
|
251
|
+
editable.set(this.account.id, "admin", "trusting");
|
|
125
252
|
|
|
126
253
|
const readKey = newRandomKeySecret();
|
|
127
|
-
const revelation = seal(
|
|
128
|
-
readKey.secret,
|
|
129
|
-
this.agentCredential.recipientSecret,
|
|
130
|
-
new Set([getAgent(this.agentCredential).recipientID]),
|
|
131
|
-
{
|
|
132
|
-
in: teamCoValue.id,
|
|
133
|
-
tx: teamCoValue.nextTransactionID(),
|
|
134
|
-
}
|
|
135
|
-
);
|
|
136
254
|
|
|
137
255
|
editable.set(
|
|
138
|
-
|
|
139
|
-
|
|
256
|
+
`${readKey.id}_for_${this.account.id}`,
|
|
257
|
+
seal(
|
|
258
|
+
readKey.secret,
|
|
259
|
+
this.account.currentSealerSecret(),
|
|
260
|
+
this.account.currentSealerID(),
|
|
261
|
+
{
|
|
262
|
+
in: teamCoValue.id,
|
|
263
|
+
tx: teamCoValue.nextTransactionID(),
|
|
264
|
+
}
|
|
265
|
+
),
|
|
140
266
|
"trusting"
|
|
141
267
|
);
|
|
268
|
+
|
|
269
|
+
editable.set("readKey", readKey.id, "trusting");
|
|
142
270
|
});
|
|
143
271
|
|
|
144
272
|
return new Team(teamContent, this);
|
|
145
273
|
}
|
|
146
274
|
|
|
147
|
-
|
|
148
|
-
|
|
275
|
+
testWithDifferentAccount(
|
|
276
|
+
account: GeneralizedControlledAccount,
|
|
149
277
|
ownSessionID: SessionID
|
|
150
278
|
): LocalNode {
|
|
151
|
-
const newNode = new LocalNode(
|
|
279
|
+
const newNode = new LocalNode(account, ownSessionID);
|
|
152
280
|
|
|
153
281
|
newNode.coValues = Object.fromEntries(
|
|
154
282
|
Object.entries(this.coValues)
|