cojson 0.2.3 → 0.3.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/dist/account.d.ts +1 -1
- package/dist/coValue.d.ts +5 -13
- package/dist/coValue.js +14 -7
- package/dist/coValue.js.map +1 -1
- package/dist/coValueCore.d.ts +6 -6
- package/dist/coValueCore.js +11 -14
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValues/coList.d.ts +99 -34
- package/dist/coValues/coList.js +162 -72
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +96 -31
- package/dist/coValues/coMap.js +157 -114
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.d.ts +67 -23
- package/dist/coValues/coStream.js +131 -59
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/crypto.d.ts +8 -3
- package/dist/crypto.js +6 -6
- package/dist/crypto.js.map +1 -1
- package/dist/group.d.ts +57 -23
- package/dist/group.js +75 -33
- package/dist/group.js.map +1 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/{node.d.ts → localNode.d.ts} +16 -8
- package/dist/{node.js → localNode.js} +48 -40
- package/dist/localNode.js.map +1 -0
- package/dist/permissions.js +6 -3
- package/dist/permissions.js.map +1 -1
- package/dist/queriedCoValues/queriedCoList.d.ts +66 -0
- package/dist/queriedCoValues/queriedCoList.js +120 -0
- package/dist/queriedCoValues/queriedCoList.js.map +1 -0
- package/dist/queriedCoValues/queriedCoMap.d.ts +47 -0
- package/dist/queriedCoValues/queriedCoMap.js +83 -0
- package/dist/queriedCoValues/queriedCoMap.js.map +1 -0
- package/dist/queriedCoValues/queriedCoStream.d.ts +40 -0
- package/dist/queriedCoValues/queriedCoStream.js +72 -0
- package/dist/queriedCoValues/queriedCoStream.js.map +1 -0
- package/dist/queries.d.ts +29 -112
- package/dist/queries.js +44 -227
- package/dist/queries.js.map +1 -1
- package/dist/sync.d.ts +1 -1
- package/dist/sync.js +1 -1
- package/dist/sync.js.map +1 -1
- package/dist/tests/testUtils.d.ts +1 -1
- package/dist/tests/testUtils.js +3 -3
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +2 -2
- package/src/account.ts +1 -1
- package/src/coValue.ts +25 -20
- package/src/coValueCore.ts +17 -21
- package/src/coValues/coList.ts +242 -128
- package/src/coValues/coMap.ts +293 -162
- package/src/coValues/coStream.ts +227 -94
- package/src/crypto.ts +37 -24
- package/src/group.ts +90 -63
- package/src/index.ts +35 -25
- package/src/{node.ts → localNode.ts} +64 -64
- package/src/permissions.ts +15 -18
- package/src/queriedCoValues/queriedCoList.ts +248 -0
- package/src/queriedCoValues/queriedCoMap.ts +180 -0
- package/src/queriedCoValues/queriedCoStream.ts +125 -0
- package/src/queries.ts +83 -460
- package/src/sync.ts +2 -2
- package/src/tests/account.test.ts +3 -6
- package/src/tests/coValue.test.ts +116 -110
- package/src/tests/coValueCore.test.ts +1 -1
- package/src/tests/crypto.test.ts +19 -21
- package/src/tests/permissions.test.ts +255 -242
- package/src/tests/queries.test.ts +57 -40
- package/src/tests/sync.test.ts +30 -30
- package/src/tests/testUtils.ts +3 -3
- package/dist/coValues/static.d.ts +0 -14
- package/dist/coValues/static.js +0 -20
- package/dist/coValues/static.js.map +0 -1
- package/dist/node.js.map +0 -1
- package/src/coValues/static.ts +0 -31
package/src/group.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CoID, CoValue, AnyCoValue } from "./coValue.js";
|
|
1
|
+
import { CoID, CoValue, AnyCoValue, AnyCoMap, AnyCoList } from "./coValue.js";
|
|
2
2
|
import { CoMap } from "./coValues/coMap.js";
|
|
3
3
|
import { JsonObject, JsonValue } from "./jsonValue.js";
|
|
4
4
|
import {
|
|
@@ -15,12 +15,11 @@ import {
|
|
|
15
15
|
agentSecretFromSecretSeed,
|
|
16
16
|
getAgentID,
|
|
17
17
|
} from "./crypto.js";
|
|
18
|
-
import { LocalNode } from "./
|
|
18
|
+
import { LocalNode } from "./localNode.js";
|
|
19
19
|
import { AgentID, SessionID, isAgentID } from "./ids.js";
|
|
20
20
|
import { AccountID, GeneralizedControlledAccount, Profile } from "./account.js";
|
|
21
21
|
import { Role } from "./permissions.js";
|
|
22
22
|
import { base58 } from "@scure/base";
|
|
23
|
-
import { CoList } from "./coValues/coList.js";
|
|
24
23
|
import {
|
|
25
24
|
BinaryCoStream,
|
|
26
25
|
BinaryCoStreamMeta,
|
|
@@ -28,9 +27,9 @@ import {
|
|
|
28
27
|
} from "./coValues/coStream.js";
|
|
29
28
|
|
|
30
29
|
export type GroupContent = {
|
|
31
|
-
profile
|
|
30
|
+
profile?: CoID<Profile> | null;
|
|
32
31
|
[key: AccountID | AgentID]: Role;
|
|
33
|
-
readKey
|
|
32
|
+
readKey?: KeyID;
|
|
34
33
|
[revelationFor: `${KeyID}_for_${AccountID | AgentID}`]: Sealed<KeySecret>;
|
|
35
34
|
[oldKeyForNewKey: `${KeyID}_for_${KeyID}`]: Encrypted<
|
|
36
35
|
KeySecret,
|
|
@@ -70,6 +69,7 @@ export function expectGroupContent(
|
|
|
70
69
|
* ```
|
|
71
70
|
* */
|
|
72
71
|
export class Group {
|
|
72
|
+
/** @category 4. Underlying CoMap */
|
|
73
73
|
underlyingMap: CoMap<GroupContent, JsonObject | null>;
|
|
74
74
|
/** @internal */
|
|
75
75
|
node: LocalNode;
|
|
@@ -83,12 +83,20 @@ export class Group {
|
|
|
83
83
|
this.node = node;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* Returns the `CoID` of the `Group`.
|
|
88
|
+
*
|
|
89
|
+
* @category 4. Underlying CoMap
|
|
90
|
+
*/
|
|
87
91
|
get id(): CoID<CoMap<GroupContent, JsonObject | null>> {
|
|
88
92
|
return this.underlyingMap.id;
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
/**
|
|
95
|
+
/**
|
|
96
|
+
* Returns the current role of a given account.
|
|
97
|
+
*
|
|
98
|
+
* @category 1. Role reading
|
|
99
|
+
*/
|
|
92
100
|
roleOf(accountID: AccountID): Role | undefined {
|
|
93
101
|
return this.roleOfInternal(accountID);
|
|
94
102
|
}
|
|
@@ -98,20 +106,28 @@ export class Group {
|
|
|
98
106
|
return this.underlyingMap.get(accountID);
|
|
99
107
|
}
|
|
100
108
|
|
|
101
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* Returns the role of the current account in the group.
|
|
111
|
+
*
|
|
112
|
+
* @category 1. Role reading
|
|
113
|
+
*/
|
|
102
114
|
myRole(): Role | undefined {
|
|
103
115
|
return this.roleOfInternal(this.node.account.id);
|
|
104
116
|
}
|
|
105
117
|
|
|
106
|
-
/**
|
|
107
|
-
*
|
|
118
|
+
/**
|
|
119
|
+
* Directly grants a new member a role in the group. The current account must be an
|
|
120
|
+
* admin to be able to do so. Throws otherwise.
|
|
121
|
+
*
|
|
122
|
+
* @category 2. Role changing
|
|
123
|
+
*/
|
|
108
124
|
addMember(accountID: AccountID, role: Role) {
|
|
109
125
|
this.addMemberInternal(accountID, role);
|
|
110
126
|
}
|
|
111
127
|
|
|
112
128
|
/** @internal */
|
|
113
129
|
addMemberInternal(accountID: AccountID | AgentID, role: Role) {
|
|
114
|
-
this.underlyingMap = this.underlyingMap.
|
|
130
|
+
this.underlyingMap = this.underlyingMap.mutate((map) => {
|
|
115
131
|
const currentReadKey = this.underlyingMap.core.getCurrentReadKey();
|
|
116
132
|
|
|
117
133
|
if (!currentReadKey.secret) {
|
|
@@ -131,15 +147,15 @@ export class Group {
|
|
|
131
147
|
|
|
132
148
|
map.set(
|
|
133
149
|
`${currentReadKey.id}_for_${accountID}`,
|
|
134
|
-
seal(
|
|
135
|
-
currentReadKey.secret,
|
|
136
|
-
this.underlyingMap.core.node.account.currentSealerSecret(),
|
|
137
|
-
getAgentSealerID(agent),
|
|
138
|
-
{
|
|
150
|
+
seal({
|
|
151
|
+
message: currentReadKey.secret,
|
|
152
|
+
from: this.underlyingMap.core.node.account.currentSealerSecret(),
|
|
153
|
+
to: getAgentSealerID(agent),
|
|
154
|
+
nOnceMaterial: {
|
|
139
155
|
in: this.underlyingMap.core.id,
|
|
140
156
|
tx: this.underlyingMap.core.nextTransactionID(),
|
|
141
|
-
}
|
|
142
|
-
),
|
|
157
|
+
},
|
|
158
|
+
}),
|
|
143
159
|
"trusting"
|
|
144
160
|
);
|
|
145
161
|
});
|
|
@@ -177,7 +193,7 @@ export class Group {
|
|
|
177
193
|
|
|
178
194
|
const newReadKey = newRandomKeySecret();
|
|
179
195
|
|
|
180
|
-
this.underlyingMap = this.underlyingMap.
|
|
196
|
+
this.underlyingMap = this.underlyingMap.mutate((map) => {
|
|
181
197
|
for (const readerID of currentlyPermittedReaders) {
|
|
182
198
|
const reader = this.node.resolveAccountAgent(
|
|
183
199
|
readerID,
|
|
@@ -186,15 +202,15 @@ export class Group {
|
|
|
186
202
|
|
|
187
203
|
map.set(
|
|
188
204
|
`${newReadKey.id}_for_${readerID}`,
|
|
189
|
-
seal(
|
|
190
|
-
newReadKey.secret,
|
|
191
|
-
this.underlyingMap.core.node.account.currentSealerSecret(),
|
|
192
|
-
getAgentSealerID(reader),
|
|
193
|
-
{
|
|
205
|
+
seal({
|
|
206
|
+
message: newReadKey.secret,
|
|
207
|
+
from: this.underlyingMap.core.node.account.currentSealerSecret(),
|
|
208
|
+
to: getAgentSealerID(reader),
|
|
209
|
+
nOnceMaterial: {
|
|
194
210
|
in: this.underlyingMap.core.id,
|
|
195
211
|
tx: this.underlyingMap.core.nextTransactionID(),
|
|
196
|
-
}
|
|
197
|
-
),
|
|
212
|
+
},
|
|
213
|
+
}),
|
|
198
214
|
"trusting"
|
|
199
215
|
);
|
|
200
216
|
}
|
|
@@ -212,23 +228,33 @@ export class Group {
|
|
|
212
228
|
});
|
|
213
229
|
}
|
|
214
230
|
|
|
215
|
-
/**
|
|
231
|
+
/**
|
|
232
|
+
* Strips the specified member of all roles (preventing future writes in
|
|
216
233
|
* the group and owned values) and rotates the read encryption key for that group
|
|
217
|
-
* (preventing reads of new content in the group and owned values)
|
|
234
|
+
* (preventing reads of new content in the group and owned values)
|
|
235
|
+
*
|
|
236
|
+
* @category 2. Role changing
|
|
237
|
+
*/
|
|
218
238
|
removeMember(accountID: AccountID) {
|
|
219
239
|
this.removeMemberInternal(accountID);
|
|
220
240
|
}
|
|
221
241
|
|
|
222
242
|
/** @internal */
|
|
223
243
|
removeMemberInternal(accountID: AccountID | AgentID) {
|
|
224
|
-
this.underlyingMap = this.underlyingMap.
|
|
244
|
+
this.underlyingMap = this.underlyingMap.mutate((map) => {
|
|
225
245
|
map.set(accountID, "revoked", "trusting");
|
|
226
246
|
});
|
|
227
247
|
|
|
228
248
|
this.rotateReadKey();
|
|
229
249
|
}
|
|
230
250
|
|
|
231
|
-
/**
|
|
251
|
+
/**
|
|
252
|
+
* Creates an invite for new members to indirectly join the group,
|
|
253
|
+
* allowing them to grant themselves the specified role with the InviteSecret
|
|
254
|
+
* (a string starting with "inviteSecret_") - use `LocalNode.acceptInvite()` for this purpose.
|
|
255
|
+
*
|
|
256
|
+
* @category 2. Role changing
|
|
257
|
+
*/
|
|
232
258
|
createInvite(role: "reader" | "writer" | "admin"): InviteSecret {
|
|
233
259
|
const secretSeed = newRandomSecretSeed();
|
|
234
260
|
|
|
@@ -240,22 +266,20 @@ export class Group {
|
|
|
240
266
|
return inviteSecretFromSecretSeed(secretSeed);
|
|
241
267
|
}
|
|
242
268
|
|
|
243
|
-
/**
|
|
244
|
-
*
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
: never,
|
|
258
|
-
meta?: M["meta"]
|
|
269
|
+
/**
|
|
270
|
+
* Creates a new `CoMap` within this group, with the specified specialized
|
|
271
|
+
* `CoMap` type `M` and optional static metadata.
|
|
272
|
+
*
|
|
273
|
+
* @category 3. Value creation
|
|
274
|
+
*/
|
|
275
|
+
createMap<M extends AnyCoMap>(
|
|
276
|
+
init?: {
|
|
277
|
+
[K in keyof M["_shape"]]: M["_shape"][K] extends AnyCoValue
|
|
278
|
+
? M["_shape"][K] | CoID<M["_shape"][K]>
|
|
279
|
+
: M["_shape"][K];
|
|
280
|
+
},
|
|
281
|
+
meta?: M["meta"],
|
|
282
|
+
initPrivacy: "trusting" | "private" = "trusting"
|
|
259
283
|
): M {
|
|
260
284
|
let map = this.node
|
|
261
285
|
.createCoValue({
|
|
@@ -270,23 +294,26 @@ export class Group {
|
|
|
270
294
|
.getCurrentContent() as M;
|
|
271
295
|
|
|
272
296
|
if (init) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
});
|
|
297
|
+
for (const [key, value] of Object.entries(init)) {
|
|
298
|
+
map = map.set(key, value, initPrivacy);
|
|
299
|
+
}
|
|
278
300
|
}
|
|
279
301
|
|
|
280
302
|
return map;
|
|
281
303
|
}
|
|
282
304
|
|
|
283
|
-
/**
|
|
284
|
-
* `CoList`
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
305
|
+
/**
|
|
306
|
+
* Creates a new `CoList` within this group, with the specified specialized
|
|
307
|
+
* `CoList` type `L` and optional static metadata.
|
|
308
|
+
*
|
|
309
|
+
* @category 3. Value creation
|
|
310
|
+
*/
|
|
311
|
+
createList<L extends AnyCoList>(
|
|
312
|
+
init?: (L["_item"] extends CoValue
|
|
313
|
+
? CoID<L["_item"]> | L["_item"]
|
|
314
|
+
: L["_item"])[],
|
|
315
|
+
meta?: L["meta"],
|
|
316
|
+
initPrivacy: "trusting" | "private" = "trusting"
|
|
290
317
|
): L {
|
|
291
318
|
let list = this.node
|
|
292
319
|
.createCoValue({
|
|
@@ -301,16 +328,15 @@ export class Group {
|
|
|
301
328
|
.getCurrentContent() as L;
|
|
302
329
|
|
|
303
330
|
if (init) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
});
|
|
331
|
+
for (const item of init) {
|
|
332
|
+
list = list.append(item, undefined, initPrivacy);
|
|
333
|
+
}
|
|
309
334
|
}
|
|
310
335
|
|
|
311
336
|
return list;
|
|
312
337
|
}
|
|
313
338
|
|
|
339
|
+
/** @category 3. Value creation */
|
|
314
340
|
createStream<C extends CoStream<JsonValue | CoValue, JsonObject | null>>(
|
|
315
341
|
meta?: C["meta"]
|
|
316
342
|
): C {
|
|
@@ -327,6 +353,7 @@ export class Group {
|
|
|
327
353
|
.getCurrentContent() as C;
|
|
328
354
|
}
|
|
329
355
|
|
|
356
|
+
/** @category 3. Value creation */
|
|
330
357
|
createBinaryStream<C extends BinaryCoStream<BinaryCoStreamMeta>>(
|
|
331
358
|
meta: C["meta"] = { type: "binary" }
|
|
332
359
|
): C {
|
package/src/index.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
CoValueCore,
|
|
3
|
+
newRandomSessionID,
|
|
4
|
+
MAX_RECOMMENDED_TX_SIZE,
|
|
5
|
+
} from "./coValueCore.js";
|
|
6
|
+
import { LocalNode } from "./localNode.js";
|
|
3
7
|
import type { CoValue } from "./coValue.js";
|
|
4
|
-
import { CoMap,
|
|
5
|
-
import { CoList,
|
|
8
|
+
import { CoMap, MutableCoMap } from "./coValues/coMap.js";
|
|
9
|
+
import { CoList, MutableCoList } from "./coValues/coList.js";
|
|
6
10
|
import {
|
|
7
11
|
CoStream,
|
|
8
|
-
|
|
12
|
+
MutableCoStream,
|
|
9
13
|
BinaryCoStream,
|
|
10
|
-
|
|
14
|
+
MutableBinaryCoStream,
|
|
11
15
|
} from "./coValues/coStream.js";
|
|
12
16
|
import {
|
|
13
17
|
agentSecretFromBytes,
|
|
@@ -18,7 +22,7 @@ import {
|
|
|
18
22
|
agentSecretFromSecretSeed,
|
|
19
23
|
secretSeedLength,
|
|
20
24
|
shortHashLength,
|
|
21
|
-
cryptoReady
|
|
25
|
+
cryptoReady,
|
|
22
26
|
} from "./crypto.js";
|
|
23
27
|
import { connectedPeers } from "./streamUtils.js";
|
|
24
28
|
import { AnonymousControlledAccount, ControlledAccount } from "./account.js";
|
|
@@ -30,7 +34,13 @@ import { parseJSON } from "./jsonStringify.js";
|
|
|
30
34
|
import type { SessionID, AgentID } from "./ids.js";
|
|
31
35
|
import type { CoID, AnyCoValue } from "./coValue.js";
|
|
32
36
|
import type { Queried } from "./queries.js";
|
|
33
|
-
import type {
|
|
37
|
+
import type { QueriedCoStream } from "./queriedCoValues/queriedCoStream.js";
|
|
38
|
+
import type { QueriedCoList } from "./queriedCoValues/queriedCoList.js";
|
|
39
|
+
import type { QueriedCoMap } from "./queriedCoValues/queriedCoMap.js";
|
|
40
|
+
import type {
|
|
41
|
+
BinaryStreamInfo,
|
|
42
|
+
BinaryCoStreamMeta,
|
|
43
|
+
} from "./coValues/coStream.js";
|
|
34
44
|
import type { JsonValue } from "./jsonValue.js";
|
|
35
45
|
import type { SyncMessage, Peer } from "./sync.js";
|
|
36
46
|
import type { AgentSecret } from "./crypto.js";
|
|
@@ -57,38 +67,39 @@ export const cojsonInternals = {
|
|
|
57
67
|
expectGroupContent,
|
|
58
68
|
base64URLtoBytes,
|
|
59
69
|
bytesToBase64url,
|
|
60
|
-
parseJSON
|
|
70
|
+
parseJSON,
|
|
61
71
|
};
|
|
62
72
|
|
|
63
73
|
export {
|
|
64
74
|
LocalNode,
|
|
65
75
|
Group,
|
|
66
76
|
CoMap,
|
|
67
|
-
|
|
77
|
+
MutableCoMap,
|
|
68
78
|
CoList,
|
|
69
|
-
|
|
79
|
+
MutableCoList,
|
|
70
80
|
CoStream,
|
|
71
|
-
|
|
81
|
+
MutableCoStream,
|
|
72
82
|
BinaryCoStream,
|
|
73
|
-
|
|
74
|
-
CoValueCore,
|
|
75
|
-
AnonymousControlledAccount,
|
|
76
|
-
ControlledAccount,
|
|
77
|
-
cryptoReady as cojsonReady,
|
|
78
|
-
MAX_RECOMMENDED_TX_SIZE
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
export type {
|
|
82
|
-
Value,
|
|
83
|
-
JsonValue,
|
|
83
|
+
MutableBinaryCoStream,
|
|
84
84
|
CoValue,
|
|
85
|
-
AnyCoValue,
|
|
86
85
|
CoID,
|
|
86
|
+
AnyCoValue,
|
|
87
87
|
Queried,
|
|
88
|
+
QueriedCoMap,
|
|
89
|
+
QueriedCoList,
|
|
90
|
+
QueriedCoStream,
|
|
88
91
|
AccountID,
|
|
89
92
|
Account,
|
|
90
93
|
Profile,
|
|
91
94
|
SessionID,
|
|
95
|
+
Media,
|
|
96
|
+
CoValueCore,
|
|
97
|
+
AnonymousControlledAccount,
|
|
98
|
+
ControlledAccount,
|
|
99
|
+
cryptoReady as cojsonReady,
|
|
100
|
+
MAX_RECOMMENDED_TX_SIZE,
|
|
101
|
+
Value,
|
|
102
|
+
JsonValue,
|
|
92
103
|
Peer,
|
|
93
104
|
BinaryStreamInfo,
|
|
94
105
|
BinaryCoStreamMeta,
|
|
@@ -96,7 +107,6 @@ export type {
|
|
|
96
107
|
AgentSecret,
|
|
97
108
|
InviteSecret,
|
|
98
109
|
SyncMessage,
|
|
99
|
-
Media
|
|
100
110
|
};
|
|
101
111
|
|
|
102
112
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
@@ -35,7 +35,6 @@ import {
|
|
|
35
35
|
AccountID,
|
|
36
36
|
Profile,
|
|
37
37
|
AccountContent,
|
|
38
|
-
Account,
|
|
39
38
|
} from "./account.js";
|
|
40
39
|
import { CoMap } from "./coValues/coMap.js";
|
|
41
40
|
import { CoValue } from "./index.js";
|
|
@@ -54,11 +53,14 @@ const { localNode } = useJazz();
|
|
|
54
53
|
export class LocalNode {
|
|
55
54
|
/** @internal */
|
|
56
55
|
coValues: { [key: RawCoID]: CoValueState } = {};
|
|
57
|
-
/** @
|
|
56
|
+
/** @category 3. Low-level */
|
|
58
57
|
account: GeneralizedControlledAccount;
|
|
58
|
+
/** @category 3. Low-level */
|
|
59
59
|
currentSessionID: SessionID;
|
|
60
|
-
|
|
60
|
+
/** @category 3. Low-level */
|
|
61
|
+
syncManager = new SyncManager(this);
|
|
61
62
|
|
|
63
|
+
/** @category 3. Low-level */
|
|
62
64
|
constructor(
|
|
63
65
|
account: GeneralizedControlledAccount,
|
|
64
66
|
currentSessionID: SessionID
|
|
@@ -67,6 +69,7 @@ export class LocalNode {
|
|
|
67
69
|
this.currentSessionID = currentSessionID;
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
/** @category 2. Node Creation */
|
|
70
73
|
static withNewlyCreatedAccount(
|
|
71
74
|
name: string,
|
|
72
75
|
initialAgentSecret = newRandomAgentSecret()
|
|
@@ -97,6 +100,7 @@ export class LocalNode {
|
|
|
97
100
|
};
|
|
98
101
|
}
|
|
99
102
|
|
|
103
|
+
/** @category 2. Node Creation */
|
|
100
104
|
static async withLoadedAccount(
|
|
101
105
|
accountID: AccountID,
|
|
102
106
|
accountSecret: AgentSecret,
|
|
@@ -111,7 +115,7 @@ export class LocalNode {
|
|
|
111
115
|
const accountPromise = loadingNode.load(accountID);
|
|
112
116
|
|
|
113
117
|
for (const peer of peersToLoadFrom) {
|
|
114
|
-
loadingNode.
|
|
118
|
+
loadingNode.syncManager.addPeer(peer);
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
const account = await accountPromise;
|
|
@@ -121,8 +125,8 @@ export class LocalNode {
|
|
|
121
125
|
new ControlledAccount(accountSecret, account, loadingNode),
|
|
122
126
|
sessionID
|
|
123
127
|
);
|
|
124
|
-
node.
|
|
125
|
-
node.
|
|
128
|
+
node.syncManager = loadingNode.syncManager;
|
|
129
|
+
node.syncManager.local = node;
|
|
126
130
|
|
|
127
131
|
return node;
|
|
128
132
|
}
|
|
@@ -132,7 +136,7 @@ export class LocalNode {
|
|
|
132
136
|
const coValue = new CoValueCore(header, this);
|
|
133
137
|
this.coValues[coValue.id] = { state: "loaded", coValue: coValue };
|
|
134
138
|
|
|
135
|
-
void this.
|
|
139
|
+
void this.syncManager.syncCoValue(coValue);
|
|
136
140
|
|
|
137
141
|
return coValue;
|
|
138
142
|
}
|
|
@@ -145,7 +149,7 @@ export class LocalNode {
|
|
|
145
149
|
|
|
146
150
|
this.coValues[id] = entry;
|
|
147
151
|
|
|
148
|
-
this.
|
|
152
|
+
this.syncManager.loadFromPeers(id);
|
|
149
153
|
}
|
|
150
154
|
if (entry.state === "loaded") {
|
|
151
155
|
return Promise.resolve(entry.coValue);
|
|
@@ -157,33 +161,42 @@ export class LocalNode {
|
|
|
157
161
|
* Loads a CoValue's content, syncing from peers as necessary and resolving the returned
|
|
158
162
|
* promise once a first version has been loaded. See `coValue.subscribe()` and `node.useTelepathicData()`
|
|
159
163
|
* for listening to subsequent updates to the CoValue.
|
|
164
|
+
*
|
|
165
|
+
* @category 3. Low-level
|
|
160
166
|
*/
|
|
161
167
|
async load<T extends CoValue>(id: CoID<T>): Promise<T> {
|
|
162
168
|
return (await this.loadCoValue(id)).getCurrentContent() as T;
|
|
163
169
|
}
|
|
164
170
|
|
|
165
|
-
|
|
171
|
+
/** @category 3. Low-level */
|
|
172
|
+
subscribe<T extends CoValue>(
|
|
173
|
+
id: CoID<T>,
|
|
174
|
+
callback: (update: T) => void
|
|
175
|
+
): () => void {
|
|
166
176
|
let stopped = false;
|
|
167
177
|
let unsubscribe!: () => void;
|
|
168
178
|
|
|
169
179
|
console.log("Subscribing to " + id);
|
|
170
180
|
|
|
171
|
-
this.load(id)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
this.load(id)
|
|
182
|
+
.then((coValue) => {
|
|
183
|
+
if (stopped) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
unsubscribe = coValue.subscribe(callback);
|
|
187
|
+
})
|
|
188
|
+
.catch((e) => {
|
|
189
|
+
console.error("Error subscribing to ", id, e);
|
|
190
|
+
});
|
|
179
191
|
|
|
180
192
|
return () => {
|
|
181
193
|
console.log("Unsubscribing from " + id);
|
|
182
194
|
stopped = true;
|
|
183
195
|
unsubscribe?.();
|
|
184
|
-
}
|
|
196
|
+
};
|
|
185
197
|
}
|
|
186
198
|
|
|
199
|
+
/** @category 1. High-level */
|
|
187
200
|
query<T extends CoValue>(
|
|
188
201
|
id: CoID<T>,
|
|
189
202
|
callback: (update: Queried<T> | undefined) => void
|
|
@@ -191,22 +204,7 @@ export class LocalNode {
|
|
|
191
204
|
return query(id, this, callback);
|
|
192
205
|
}
|
|
193
206
|
|
|
194
|
-
/**
|
|
195
|
-
* Loads a profile associated with an account. `Profile` is at least a `CoMap<{string: name}>`,
|
|
196
|
-
* but might contain other, app-specific properties.
|
|
197
|
-
*/
|
|
198
|
-
async loadProfile(id: AccountID): Promise<Profile> {
|
|
199
|
-
const account = await this.load<Account>(id);
|
|
200
|
-
const profileID = account.get("profile");
|
|
201
|
-
|
|
202
|
-
if (!profileID) {
|
|
203
|
-
throw new Error(`Account ${id} has no profile`);
|
|
204
|
-
}
|
|
205
|
-
return (
|
|
206
|
-
await this.loadCoValue(profileID)
|
|
207
|
-
).getCurrentContent() as Profile;
|
|
208
|
-
}
|
|
209
|
-
|
|
207
|
+
/** @category 1. High-level */
|
|
210
208
|
async acceptInvite<T extends CoValue>(
|
|
211
209
|
groupOrOwnedValueID: CoID<T>,
|
|
212
210
|
inviteSecret: InviteSecret
|
|
@@ -327,11 +325,12 @@ export class LocalNode {
|
|
|
327
325
|
name: string,
|
|
328
326
|
agentSecret = newRandomAgentSecret()
|
|
329
327
|
): ControlledAccount {
|
|
328
|
+
const accountAgentID = getAgentID(agentSecret);
|
|
330
329
|
const account = this.createCoValue(
|
|
331
330
|
accountHeaderForInitialAgentSecret(agentSecret)
|
|
332
331
|
).testWithDifferentAccount(
|
|
333
332
|
new AnonymousControlledAccount(agentSecret),
|
|
334
|
-
newRandomSessionID(
|
|
333
|
+
newRandomSessionID(accountAgentID)
|
|
335
334
|
);
|
|
336
335
|
|
|
337
336
|
const accountAsGroup = new Group(
|
|
@@ -339,22 +338,22 @@ export class LocalNode {
|
|
|
339
338
|
account.node
|
|
340
339
|
);
|
|
341
340
|
|
|
342
|
-
accountAsGroup.underlyingMap.
|
|
343
|
-
editable.set(
|
|
341
|
+
accountAsGroup.underlyingMap.mutate((editable) => {
|
|
342
|
+
editable.set(accountAgentID, "admin", "trusting");
|
|
344
343
|
|
|
345
344
|
const readKey = newRandomKeySecret();
|
|
346
345
|
|
|
347
346
|
editable.set(
|
|
348
|
-
`${readKey.id}_for_${
|
|
349
|
-
seal(
|
|
350
|
-
readKey.secret,
|
|
351
|
-
getAgentSealerSecret(agentSecret),
|
|
352
|
-
getAgentSealerID(
|
|
353
|
-
{
|
|
347
|
+
`${readKey.id}_for_${accountAgentID}`,
|
|
348
|
+
seal({
|
|
349
|
+
message: readKey.secret,
|
|
350
|
+
from: getAgentSealerSecret(agentSecret),
|
|
351
|
+
to: getAgentSealerID(accountAgentID),
|
|
352
|
+
nOnceMaterial: {
|
|
354
353
|
in: account.id,
|
|
355
354
|
tx: account.nextTransactionID(),
|
|
356
|
-
}
|
|
357
|
-
),
|
|
355
|
+
},
|
|
356
|
+
}),
|
|
358
357
|
"trusting"
|
|
359
358
|
);
|
|
360
359
|
|
|
@@ -367,17 +366,15 @@ export class LocalNode {
|
|
|
367
366
|
account.node
|
|
368
367
|
);
|
|
369
368
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
369
|
+
const profile = accountAsGroup.createMap<Profile>(
|
|
370
|
+
{ name },
|
|
371
|
+
{
|
|
372
|
+
type: "profile",
|
|
373
|
+
},
|
|
374
|
+
"trusting"
|
|
375
|
+
);
|
|
377
376
|
|
|
378
|
-
accountAsGroup.underlyingMap.
|
|
379
|
-
editable.set("profile", profile.id, "trusting");
|
|
380
|
-
});
|
|
377
|
+
accountAsGroup.underlyingMap.set("profile", profile.id, "trusting");
|
|
381
378
|
|
|
382
379
|
const accountOnThisNode = this.expectCoValueLoaded(account.id);
|
|
383
380
|
|
|
@@ -427,7 +424,10 @@ export class LocalNode {
|
|
|
427
424
|
).getCurrentAgentID();
|
|
428
425
|
}
|
|
429
426
|
|
|
430
|
-
/**
|
|
427
|
+
/**
|
|
428
|
+
* Creates a new group (with the current account as the group's first admin).
|
|
429
|
+
* @category 1. High-level
|
|
430
|
+
*/
|
|
431
431
|
createGroup(): Group {
|
|
432
432
|
const groupCoValue = this.createCoValue({
|
|
433
433
|
type: "comap",
|
|
@@ -438,22 +438,22 @@ export class LocalNode {
|
|
|
438
438
|
|
|
439
439
|
let groupContent = expectGroupContent(groupCoValue.getCurrentContent());
|
|
440
440
|
|
|
441
|
-
groupContent = groupContent.
|
|
441
|
+
groupContent = groupContent.mutate((editable) => {
|
|
442
442
|
editable.set(this.account.id, "admin", "trusting");
|
|
443
443
|
|
|
444
444
|
const readKey = newRandomKeySecret();
|
|
445
445
|
|
|
446
446
|
editable.set(
|
|
447
447
|
`${readKey.id}_for_${this.account.id}`,
|
|
448
|
-
seal(
|
|
449
|
-
readKey.secret,
|
|
450
|
-
this.account.currentSealerSecret(),
|
|
451
|
-
this.account.currentSealerID(),
|
|
452
|
-
{
|
|
448
|
+
seal({
|
|
449
|
+
message: readKey.secret,
|
|
450
|
+
from: this.account.currentSealerSecret(),
|
|
451
|
+
to: this.account.currentSealerID(),
|
|
452
|
+
nOnceMaterial: {
|
|
453
453
|
in: groupCoValue.id,
|
|
454
454
|
tx: groupCoValue.nextTransactionID(),
|
|
455
|
-
}
|
|
456
|
-
),
|
|
455
|
+
},
|
|
456
|
+
}),
|
|
457
457
|
"trusting"
|
|
458
458
|
);
|
|
459
459
|
|