cojson 0.3.6 → 0.4.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/coValue.d.ts +7 -12
- package/dist/coValue.js.map +1 -1
- package/dist/coValueCore.d.ts +9 -4
- package/dist/coValueCore.js +69 -33
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValues/account.d.ts +62 -0
- package/dist/{account.js → coValues/account.js} +19 -11
- package/dist/coValues/account.js.map +1 -0
- package/dist/coValues/coList.d.ts +19 -19
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +31 -23
- package/dist/coValues/coMap.js +4 -6
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.d.ts +19 -19
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/{group.d.ts → coValues/group.d.ts} +27 -38
- package/dist/{group.js → coValues/group.js} +69 -73
- package/dist/coValues/group.js.map +1 -0
- package/dist/ids.d.ts +1 -1
- package/dist/index.d.ts +15 -11
- package/dist/index.js +10 -5
- package/dist/index.js.map +1 -1
- package/dist/localNode.d.ts +20 -7
- package/dist/localNode.js +74 -39
- package/dist/localNode.js.map +1 -1
- package/dist/media.d.ts +1 -1
- package/dist/permissions.d.ts +1 -1
- package/dist/permissions.js +43 -22
- package/dist/permissions.js.map +1 -1
- package/dist/queriedCoValues/queriedAccount.d.ts +13 -0
- package/dist/queriedCoValues/queriedAccount.js +24 -0
- package/dist/queriedCoValues/queriedAccount.js.map +1 -0
- package/dist/queriedCoValues/queriedCoList.d.ts +10 -10
- package/dist/queriedCoValues/queriedCoList.js +11 -15
- package/dist/queriedCoValues/queriedCoList.js.map +1 -1
- package/dist/queriedCoValues/queriedCoMap.d.ts +14 -21
- package/dist/queriedCoValues/queriedCoMap.js +27 -28
- package/dist/queriedCoValues/queriedCoMap.js.map +1 -1
- package/dist/queriedCoValues/queriedCoStream.d.ts +9 -9
- package/dist/queriedCoValues/queriedCoStream.js +44 -20
- package/dist/queriedCoValues/queriedCoStream.js.map +1 -1
- package/dist/queriedCoValues/queriedGroup.d.ts +29 -0
- package/dist/queriedCoValues/queriedGroup.js +54 -0
- package/dist/queriedCoValues/queriedGroup.js.map +1 -0
- package/dist/queries.d.ts +40 -9
- package/dist/queries.js +104 -39
- package/dist/queries.js.map +1 -1
- package/dist/sync.js +1 -0
- package/dist/sync.js.map +1 -1
- package/dist/tests/testUtils.d.ts +15 -7
- package/dist/tests/testUtils.js +16 -17
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +2 -2
- package/src/coValue.ts +12 -31
- package/src/coValueCore.ts +100 -40
- package/src/{account.ts → coValues/account.ts} +46 -27
- package/src/coValues/coList.ts +24 -28
- package/src/coValues/coMap.ts +42 -68
- package/src/coValues/coStream.ts +20 -26
- package/src/{group.ts → coValues/group.ts} +121 -141
- package/src/ids.ts +1 -1
- package/src/index.ts +25 -10
- package/src/localNode.ts +180 -77
- package/src/media.ts +1 -1
- package/src/permissions.ts +67 -36
- package/src/queriedCoValues/queriedAccount.ts +40 -0
- package/src/queriedCoValues/queriedCoList.ts +22 -30
- package/src/queriedCoValues/queriedCoMap.ts +60 -72
- package/src/queriedCoValues/queriedCoStream.ts +67 -37
- package/src/queriedCoValues/queriedGroup.ts +90 -0
- package/src/queries.ts +181 -60
- package/src/sync.ts +1 -0
- package/src/tests/account.test.ts +14 -9
- package/src/tests/coValueCore.test.ts +2 -2
- package/src/tests/permissions.test.ts +351 -242
- package/src/tests/queries.test.ts +98 -79
- package/src/tests/sync.test.ts +11 -11
- package/src/tests/testUtils.ts +16 -18
- package/dist/account.d.ts +0 -58
- package/dist/account.js.map +0 -1
- package/dist/group.js.map +0 -1
package/src/localNode.ts
CHANGED
|
@@ -17,16 +17,16 @@ import {
|
|
|
17
17
|
import {
|
|
18
18
|
InviteSecret,
|
|
19
19
|
Group,
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
GroupShape,
|
|
21
|
+
expectGroup,
|
|
22
22
|
secretSeedFromInviteSecret,
|
|
23
|
-
} from "./group.js";
|
|
23
|
+
} from "./coValues/group.js";
|
|
24
24
|
import { Peer, SyncManager } from "./sync.js";
|
|
25
25
|
import { AgentID, RawCoID, SessionID, isAgentID } from "./ids.js";
|
|
26
26
|
import { CoID } from "./coValue.js";
|
|
27
27
|
import { Queried, query } from "./queries.js";
|
|
28
28
|
import {
|
|
29
|
-
|
|
29
|
+
Account,
|
|
30
30
|
AccountMeta,
|
|
31
31
|
accountHeaderForInitialAgentSecret,
|
|
32
32
|
GeneralizedControlledAccount,
|
|
@@ -34,10 +34,12 @@ import {
|
|
|
34
34
|
AnonymousControlledAccount,
|
|
35
35
|
AccountID,
|
|
36
36
|
Profile,
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
isAccountID,
|
|
38
|
+
AccountMigration,
|
|
39
|
+
} from "./coValues/account.js";
|
|
39
40
|
import { CoMap } from "./coValues/coMap.js";
|
|
40
41
|
import { CoValue } from "./index.js";
|
|
42
|
+
import { QueriedAccount } from "./queriedCoValues/queriedAccount.js";
|
|
41
43
|
|
|
42
44
|
/** A `LocalNode` represents a local view of a set of loaded `CoValue`s, from the perspective of a particular account (or primitive cryptographic agent).
|
|
43
45
|
|
|
@@ -70,10 +72,19 @@ export class LocalNode {
|
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
/** @category 2. Node Creation */
|
|
73
|
-
static withNewlyCreatedAccount
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
static withNewlyCreatedAccount<
|
|
76
|
+
P extends Profile = Profile,
|
|
77
|
+
R extends CoMap = CoMap,
|
|
78
|
+
Meta extends AccountMeta = AccountMeta
|
|
79
|
+
>({
|
|
80
|
+
name,
|
|
81
|
+
migration,
|
|
82
|
+
initialAgentSecret = newRandomAgentSecret(),
|
|
83
|
+
}: {
|
|
84
|
+
name: string;
|
|
85
|
+
migration?: AccountMigration<P, R, Meta>;
|
|
86
|
+
initialAgentSecret?: AgentSecret;
|
|
87
|
+
}): {
|
|
77
88
|
node: LocalNode;
|
|
78
89
|
accountID: AccountID;
|
|
79
90
|
accountSecret: AgentSecret;
|
|
@@ -87,26 +98,52 @@ export class LocalNode {
|
|
|
87
98
|
|
|
88
99
|
const account = setupNode.createAccount(name, initialAgentSecret);
|
|
89
100
|
|
|
90
|
-
const nodeWithAccount = account.node.testWithDifferentAccount(
|
|
101
|
+
const nodeWithAccount = account.core.node.testWithDifferentAccount(
|
|
91
102
|
account,
|
|
92
103
|
newRandomSessionID(account.id)
|
|
93
104
|
);
|
|
94
105
|
|
|
106
|
+
const accountOnNodeWithAccount = nodeWithAccount.account as ControlledAccount<P, R, Meta>;
|
|
107
|
+
|
|
108
|
+
const profile = nodeWithAccount.expectProfileLoaded(
|
|
109
|
+
accountOnNodeWithAccount.id,
|
|
110
|
+
"After creating account"
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (migration) {
|
|
114
|
+
migration(accountOnNodeWithAccount, profile as P);
|
|
115
|
+
nodeWithAccount.account = new ControlledAccount(
|
|
116
|
+
accountOnNodeWithAccount.core,
|
|
117
|
+
accountOnNodeWithAccount.agentSecret
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
95
121
|
return {
|
|
96
122
|
node: nodeWithAccount,
|
|
97
|
-
accountID:
|
|
98
|
-
accountSecret:
|
|
123
|
+
accountID: accountOnNodeWithAccount.id,
|
|
124
|
+
accountSecret: accountOnNodeWithAccount.agentSecret,
|
|
99
125
|
sessionID: nodeWithAccount.currentSessionID,
|
|
100
126
|
};
|
|
101
127
|
}
|
|
102
128
|
|
|
103
129
|
/** @category 2. Node Creation */
|
|
104
|
-
static async withLoadedAccount
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
130
|
+
static async withLoadedAccount<
|
|
131
|
+
P extends Profile = Profile,
|
|
132
|
+
R extends CoMap = CoMap,
|
|
133
|
+
Meta extends AccountMeta = AccountMeta
|
|
134
|
+
>({
|
|
135
|
+
accountID,
|
|
136
|
+
accountSecret,
|
|
137
|
+
sessionID,
|
|
138
|
+
peersToLoadFrom,
|
|
139
|
+
migration,
|
|
140
|
+
}: {
|
|
141
|
+
accountID: AccountID;
|
|
142
|
+
accountSecret: AgentSecret;
|
|
143
|
+
sessionID: SessionID;
|
|
144
|
+
peersToLoadFrom: Peer[];
|
|
145
|
+
migration?: AccountMigration<P, R, Meta>;
|
|
146
|
+
}): Promise<LocalNode> {
|
|
110
147
|
const loadingNode = new LocalNode(
|
|
111
148
|
new AnonymousControlledAccount(accountSecret),
|
|
112
149
|
newRandomSessionID(accountID)
|
|
@@ -119,15 +156,38 @@ export class LocalNode {
|
|
|
119
156
|
}
|
|
120
157
|
|
|
121
158
|
const account = await accountPromise;
|
|
159
|
+
const controlledAccount = new ControlledAccount(
|
|
160
|
+
account.core,
|
|
161
|
+
accountSecret
|
|
162
|
+
);
|
|
122
163
|
|
|
123
164
|
// since this is all synchronous, we can just swap out nodes for the SyncManager
|
|
124
165
|
const node = loadingNode.testWithDifferentAccount(
|
|
125
|
-
|
|
166
|
+
controlledAccount,
|
|
126
167
|
sessionID
|
|
127
168
|
);
|
|
128
169
|
node.syncManager = loadingNode.syncManager;
|
|
129
170
|
node.syncManager.local = node;
|
|
130
171
|
|
|
172
|
+
controlledAccount.core.node = node;
|
|
173
|
+
|
|
174
|
+
const profileID = account.get("profile");
|
|
175
|
+
if (!profileID) {
|
|
176
|
+
throw new Error("Account has no profile");
|
|
177
|
+
}
|
|
178
|
+
const profile = await node.load(profileID);
|
|
179
|
+
|
|
180
|
+
if (migration) {
|
|
181
|
+
migration(
|
|
182
|
+
controlledAccount as ControlledAccount<P, R, Meta>,
|
|
183
|
+
profile as P
|
|
184
|
+
);
|
|
185
|
+
node.account = new ControlledAccount(
|
|
186
|
+
controlledAccount.core,
|
|
187
|
+
controlledAccount.agentSecret
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
131
191
|
return node;
|
|
132
192
|
}
|
|
133
193
|
|
|
@@ -197,14 +257,47 @@ export class LocalNode {
|
|
|
197
257
|
}
|
|
198
258
|
|
|
199
259
|
/** @category 1. High-level */
|
|
260
|
+
|
|
200
261
|
query<T extends CoValue>(
|
|
201
262
|
id: CoID<T>,
|
|
202
263
|
callback: (update: Queried<T> | undefined) => void
|
|
264
|
+
): () => void;
|
|
265
|
+
query<
|
|
266
|
+
P extends Profile = Profile,
|
|
267
|
+
R extends CoMap = CoMap,
|
|
268
|
+
Meta extends AccountMeta = AccountMeta
|
|
269
|
+
>(
|
|
270
|
+
id: "me",
|
|
271
|
+
callback: (
|
|
272
|
+
update: QueriedAccount<Account<P, R, Meta>> | undefined
|
|
273
|
+
) => void
|
|
274
|
+
): () => void;
|
|
275
|
+
query(
|
|
276
|
+
id: CoID<CoValue> | "me",
|
|
277
|
+
callback: (
|
|
278
|
+
update: Queried<CoValue> | QueriedAccount | undefined
|
|
279
|
+
) => void
|
|
280
|
+
): () => void;
|
|
281
|
+
query(
|
|
282
|
+
id: CoID<CoValue> | "me",
|
|
283
|
+
callback: (
|
|
284
|
+
// TODO: sort this out
|
|
285
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
286
|
+
update: any
|
|
287
|
+
) => void
|
|
203
288
|
): () => void {
|
|
204
|
-
|
|
289
|
+
if (id === "me") {
|
|
290
|
+
const meId = this.account.id;
|
|
291
|
+
if (!isAccountID(meId)) {
|
|
292
|
+
throw new Error("Can only query 'me' for accounts");
|
|
293
|
+
}
|
|
294
|
+
return query(meId, this, callback);
|
|
295
|
+
} else {
|
|
296
|
+
return query(id, this, callback);
|
|
297
|
+
}
|
|
205
298
|
}
|
|
206
299
|
|
|
207
|
-
/** @
|
|
300
|
+
/** @deprecated Use Account.acceptInvite instead */
|
|
208
301
|
async acceptInvite<T extends CoValue>(
|
|
209
302
|
groupOrOwnedValueID: CoID<T>,
|
|
210
303
|
inviteSecret: InviteSecret
|
|
@@ -213,16 +306,14 @@ export class LocalNode {
|
|
|
213
306
|
|
|
214
307
|
if (groupOrOwnedValue.core.header.ruleset.type === "ownedByGroup") {
|
|
215
308
|
return this.acceptInvite(
|
|
216
|
-
groupOrOwnedValue.core.header.ruleset.group as CoID<
|
|
217
|
-
CoMap<GroupContent>
|
|
218
|
-
>,
|
|
309
|
+
groupOrOwnedValue.core.header.ruleset.group as CoID<Group>,
|
|
219
310
|
inviteSecret
|
|
220
311
|
);
|
|
221
312
|
} else if (groupOrOwnedValue.core.header.ruleset.type !== "group") {
|
|
222
313
|
throw new Error("Can only accept invites to groups");
|
|
223
314
|
}
|
|
224
315
|
|
|
225
|
-
const group =
|
|
316
|
+
const group = expectGroup(groupOrOwnedValue);
|
|
226
317
|
|
|
227
318
|
const inviteAgentSecret = agentSecretFromSecretSeed(
|
|
228
319
|
secretSeedFromInviteSecret(inviteSecret)
|
|
@@ -230,8 +321,8 @@ export class LocalNode {
|
|
|
230
321
|
const inviteAgentID = getAgentID(inviteAgentSecret);
|
|
231
322
|
|
|
232
323
|
const inviteRole = await new Promise((resolve, reject) => {
|
|
233
|
-
group.
|
|
234
|
-
const role =
|
|
324
|
+
group.subscribe((groupUpdate) => {
|
|
325
|
+
const role = groupUpdate.get(inviteAgentID);
|
|
235
326
|
if (role) {
|
|
236
327
|
resolve(role);
|
|
237
328
|
}
|
|
@@ -246,7 +337,7 @@ export class LocalNode {
|
|
|
246
337
|
throw new Error("No invite found");
|
|
247
338
|
}
|
|
248
339
|
|
|
249
|
-
const existingRole = group.
|
|
340
|
+
const existingRole = group.get(this.account.id);
|
|
250
341
|
|
|
251
342
|
if (
|
|
252
343
|
existingRole === "admin" ||
|
|
@@ -260,9 +351,13 @@ export class LocalNode {
|
|
|
260
351
|
return;
|
|
261
352
|
}
|
|
262
353
|
|
|
263
|
-
const groupAsInvite =
|
|
264
|
-
|
|
265
|
-
|
|
354
|
+
const groupAsInvite = expectGroup(
|
|
355
|
+
group.core
|
|
356
|
+
.testWithDifferentAccount(
|
|
357
|
+
new AnonymousControlledAccount(inviteAgentSecret),
|
|
358
|
+
newRandomSessionID(inviteAgentID)
|
|
359
|
+
)
|
|
360
|
+
.getCurrentContent()
|
|
266
361
|
);
|
|
267
362
|
|
|
268
363
|
groupAsInvite.addMemberInternal(
|
|
@@ -274,12 +369,11 @@ export class LocalNode {
|
|
|
274
369
|
: "reader"
|
|
275
370
|
);
|
|
276
371
|
|
|
277
|
-
group.
|
|
278
|
-
|
|
279
|
-
group.underlyingMap.core._cachedContent = undefined;
|
|
372
|
+
group.core._sessions = groupAsInvite.core.sessions;
|
|
373
|
+
group.core._cachedContent = undefined;
|
|
280
374
|
|
|
281
|
-
for (const groupListener of group.
|
|
282
|
-
groupListener(group.
|
|
375
|
+
for (const groupListener of group.core.listeners) {
|
|
376
|
+
groupListener(group.core.getCurrentContent());
|
|
283
377
|
}
|
|
284
378
|
}
|
|
285
379
|
|
|
@@ -304,7 +398,7 @@ export class LocalNode {
|
|
|
304
398
|
/** @internal */
|
|
305
399
|
expectProfileLoaded(id: AccountID, expectation?: string): Profile {
|
|
306
400
|
const account = this.expectCoValueLoaded(id, expectation);
|
|
307
|
-
const profileID =
|
|
401
|
+
const profileID = expectGroup(account.getCurrentContent()).get(
|
|
308
402
|
"profile"
|
|
309
403
|
);
|
|
310
404
|
if (!profileID) {
|
|
@@ -326,47 +420,51 @@ export class LocalNode {
|
|
|
326
420
|
agentSecret = newRandomAgentSecret()
|
|
327
421
|
): ControlledAccount {
|
|
328
422
|
const accountAgentID = getAgentID(agentSecret);
|
|
329
|
-
|
|
330
|
-
accountHeaderForInitialAgentSecret(agentSecret)
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const accountAsGroup = new Group(
|
|
337
|
-
expectGroupContent(account.getCurrentContent()),
|
|
338
|
-
account.node
|
|
423
|
+
let account = expectGroup(
|
|
424
|
+
this.createCoValue(accountHeaderForInitialAgentSecret(agentSecret))
|
|
425
|
+
.testWithDifferentAccount(
|
|
426
|
+
new AnonymousControlledAccount(agentSecret),
|
|
427
|
+
newRandomSessionID(accountAgentID)
|
|
428
|
+
)
|
|
429
|
+
.getCurrentContent()
|
|
339
430
|
);
|
|
340
431
|
|
|
341
|
-
|
|
432
|
+
account = account.mutate((editable) => {
|
|
342
433
|
editable.set(accountAgentID, "admin", "trusting");
|
|
343
434
|
|
|
344
435
|
const readKey = newRandomKeySecret();
|
|
345
436
|
|
|
437
|
+
const sealed = seal({
|
|
438
|
+
message: readKey.secret,
|
|
439
|
+
from: getAgentSealerSecret(agentSecret),
|
|
440
|
+
to: getAgentSealerID(accountAgentID),
|
|
441
|
+
nOnceMaterial: {
|
|
442
|
+
in: account.id,
|
|
443
|
+
tx: account.core.nextTransactionID(),
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
console.log(
|
|
448
|
+
"Creating read key",
|
|
449
|
+
getAgentSealerSecret(agentSecret),
|
|
450
|
+
getAgentSealerID(accountAgentID),
|
|
451
|
+
account.id,
|
|
452
|
+
account.core.nextTransactionID(),
|
|
453
|
+
"in session",
|
|
454
|
+
account.core.node.currentSessionID,
|
|
455
|
+
"=",
|
|
456
|
+
sealed
|
|
457
|
+
);
|
|
346
458
|
editable.set(
|
|
347
459
|
`${readKey.id}_for_${accountAgentID}`,
|
|
348
|
-
|
|
349
|
-
message: readKey.secret,
|
|
350
|
-
from: getAgentSealerSecret(agentSecret),
|
|
351
|
-
to: getAgentSealerID(accountAgentID),
|
|
352
|
-
nOnceMaterial: {
|
|
353
|
-
in: account.id,
|
|
354
|
-
tx: account.nextTransactionID(),
|
|
355
|
-
},
|
|
356
|
-
}),
|
|
460
|
+
sealed,
|
|
357
461
|
"trusting"
|
|
358
462
|
);
|
|
359
463
|
|
|
360
464
|
editable.set("readKey", readKey.id, "trusting");
|
|
361
465
|
});
|
|
362
466
|
|
|
363
|
-
const
|
|
364
|
-
agentSecret,
|
|
365
|
-
account.getCurrentContent() as CoMap<AccountContent, AccountMeta>,
|
|
366
|
-
account.node
|
|
367
|
-
);
|
|
368
|
-
|
|
369
|
-
const profile = accountAsGroup.createMap<Profile>(
|
|
467
|
+
const profile = account.createMap<Profile>(
|
|
370
468
|
{ name },
|
|
371
469
|
{
|
|
372
470
|
type: "profile",
|
|
@@ -374,12 +472,12 @@ export class LocalNode {
|
|
|
374
472
|
"trusting"
|
|
375
473
|
);
|
|
376
474
|
|
|
377
|
-
|
|
475
|
+
account = account.set("profile", profile.id, "trusting");
|
|
378
476
|
|
|
379
477
|
const accountOnThisNode = this.expectCoValueLoaded(account.id);
|
|
380
478
|
|
|
381
479
|
accountOnThisNode._sessions = {
|
|
382
|
-
...
|
|
480
|
+
...account.core.sessions,
|
|
383
481
|
};
|
|
384
482
|
accountOnThisNode._cachedContent = undefined;
|
|
385
483
|
|
|
@@ -390,7 +488,7 @@ export class LocalNode {
|
|
|
390
488
|
};
|
|
391
489
|
profileOnThisNode._cachedContent = undefined;
|
|
392
490
|
|
|
393
|
-
return
|
|
491
|
+
return new ControlledAccount(accountOnThisNode, agentSecret);
|
|
394
492
|
}
|
|
395
493
|
|
|
396
494
|
/** @internal */
|
|
@@ -418,15 +516,11 @@ export class LocalNode {
|
|
|
418
516
|
);
|
|
419
517
|
}
|
|
420
518
|
|
|
421
|
-
return new
|
|
422
|
-
coValue.getCurrentContent() as CoMap<GroupContent, AccountMeta>,
|
|
423
|
-
this
|
|
424
|
-
).getCurrentAgentID();
|
|
519
|
+
return new Account(coValue).getCurrentAgentID();
|
|
425
520
|
}
|
|
426
521
|
|
|
427
522
|
/**
|
|
428
|
-
*
|
|
429
|
-
* @category 1. High-level
|
|
523
|
+
* @deprecated use Account.createGroup() instead
|
|
430
524
|
*/
|
|
431
525
|
createGroup(): Group {
|
|
432
526
|
const groupCoValue = this.createCoValue({
|
|
@@ -436,9 +530,9 @@ export class LocalNode {
|
|
|
436
530
|
...createdNowUnique(),
|
|
437
531
|
});
|
|
438
532
|
|
|
439
|
-
let
|
|
533
|
+
let group = expectGroup(groupCoValue.getCurrentContent());
|
|
440
534
|
|
|
441
|
-
|
|
535
|
+
group = group.mutate((editable) => {
|
|
442
536
|
editable.set(this.account.id, "admin", "trusting");
|
|
443
537
|
|
|
444
538
|
const readKey = newRandomKeySecret();
|
|
@@ -460,7 +554,7 @@ export class LocalNode {
|
|
|
460
554
|
editable.set("readKey", readKey.id, "trusting");
|
|
461
555
|
});
|
|
462
556
|
|
|
463
|
-
return
|
|
557
|
+
return group;
|
|
464
558
|
}
|
|
465
559
|
|
|
466
560
|
/** @internal */
|
|
@@ -505,6 +599,15 @@ export class LocalNode {
|
|
|
505
599
|
}
|
|
506
600
|
}
|
|
507
601
|
|
|
602
|
+
if (account instanceof ControlledAccount) {
|
|
603
|
+
// To make sure that when we edit the account, we're modifying the correct sessions
|
|
604
|
+
const accountInNode = new ControlledAccount(newNode.expectCoValueLoaded(account.id), account.agentSecret);
|
|
605
|
+
if (accountInNode.core.node !== newNode) {
|
|
606
|
+
throw new Error("Account's node is not the new node");
|
|
607
|
+
}
|
|
608
|
+
newNode.account = accountInNode;
|
|
609
|
+
}
|
|
610
|
+
|
|
508
611
|
return newNode;
|
|
509
612
|
}
|
|
510
613
|
}
|
package/src/media.ts
CHANGED
package/src/permissions.ts
CHANGED
|
@@ -5,13 +5,12 @@ import { KeyID } from "./crypto.js";
|
|
|
5
5
|
import {
|
|
6
6
|
CoValueCore,
|
|
7
7
|
Transaction,
|
|
8
|
-
TrustingTransaction,
|
|
9
8
|
accountOrAgentIDfromSessionID,
|
|
10
9
|
} from "./coValueCore.js";
|
|
11
10
|
import { AgentID, RawCoID, SessionID, TransactionID } from "./ids.js";
|
|
12
|
-
import { AccountID, Profile } from "./account.js";
|
|
11
|
+
import { Account, AccountID, Profile } from "./coValues/account.js";
|
|
13
12
|
import { parseJSON } from "./jsonStringify.js";
|
|
14
|
-
import {
|
|
13
|
+
import { EVERYONE, Everyone, expectGroup } from "./coValues/group.js";
|
|
15
14
|
|
|
16
15
|
export type PermissionsDef =
|
|
17
16
|
| { type: "group"; initialAdmin: AccountID | AgentID }
|
|
@@ -31,26 +30,21 @@ export function determineValidTransactions(
|
|
|
31
30
|
coValue: CoValueCore
|
|
32
31
|
): { txID: TransactionID; tx: Transaction }[] {
|
|
33
32
|
if (coValue.header.ruleset.type === "group") {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
sessionID: SessionID;
|
|
48
|
-
txIndex: number;
|
|
49
|
-
tx: TrustingTransaction;
|
|
50
|
-
}[];
|
|
51
|
-
});
|
|
33
|
+
const allTransactionsSorted = Object.entries(coValue.sessions).flatMap(
|
|
34
|
+
([sessionID, sessionLog]) => {
|
|
35
|
+
return sessionLog.transactions.map((tx, txIndex) => ({
|
|
36
|
+
sessionID,
|
|
37
|
+
txIndex,
|
|
38
|
+
tx,
|
|
39
|
+
})) as {
|
|
40
|
+
sessionID: SessionID;
|
|
41
|
+
txIndex: number;
|
|
42
|
+
tx: Transaction;
|
|
43
|
+
}[];
|
|
44
|
+
}
|
|
45
|
+
);
|
|
52
46
|
|
|
53
|
-
|
|
47
|
+
allTransactionsSorted.sort((a, b) => {
|
|
54
48
|
return a.tx.madeAt - b.tx.madeAt;
|
|
55
49
|
});
|
|
56
50
|
|
|
@@ -60,19 +54,33 @@ export function determineValidTransactions(
|
|
|
60
54
|
throw new Error("Group must have initialAdmin");
|
|
61
55
|
}
|
|
62
56
|
|
|
63
|
-
const memberState: {
|
|
57
|
+
const memberState: {
|
|
58
|
+
[agent: AccountID | AgentID]: Role;
|
|
59
|
+
[EVERYONE]?: Role;
|
|
60
|
+
} = {};
|
|
64
61
|
|
|
65
62
|
const validTransactions: { txID: TransactionID; tx: Transaction }[] =
|
|
66
63
|
[];
|
|
67
64
|
|
|
68
|
-
for (const {
|
|
69
|
-
sessionID,
|
|
70
|
-
txIndex,
|
|
71
|
-
tx,
|
|
72
|
-
} of allTrustingTransactionsSorted) {
|
|
65
|
+
for (const { sessionID, txIndex, tx } of allTransactionsSorted) {
|
|
73
66
|
// console.log("before", { memberState, validTransactions });
|
|
74
67
|
const transactor = accountOrAgentIDfromSessionID(sessionID);
|
|
75
68
|
|
|
69
|
+
if (tx.privacy === "private") {
|
|
70
|
+
if (memberState[transactor] === "admin") {
|
|
71
|
+
validTransactions.push({
|
|
72
|
+
txID: { sessionID, txIndex },
|
|
73
|
+
tx,
|
|
74
|
+
});
|
|
75
|
+
continue;
|
|
76
|
+
} else {
|
|
77
|
+
console.warn(
|
|
78
|
+
"Only admins can make private transactions in groups"
|
|
79
|
+
);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
76
84
|
let changes;
|
|
77
85
|
|
|
78
86
|
try {
|
|
@@ -93,7 +101,7 @@ export function determineValidTransactions(
|
|
|
93
101
|
}
|
|
94
102
|
|
|
95
103
|
const change = changes[0] as
|
|
96
|
-
| MapOpPayload<AccountID | AgentID, Role>
|
|
104
|
+
| MapOpPayload<AccountID | AgentID | Everyone, Role>
|
|
97
105
|
| MapOpPayload<"readKey", JsonValue>
|
|
98
106
|
| MapOpPayload<"profile", CoID<Profile>>;
|
|
99
107
|
if (changes.length !== 1) {
|
|
@@ -158,6 +166,20 @@ export function determineValidTransactions(
|
|
|
158
166
|
continue;
|
|
159
167
|
}
|
|
160
168
|
|
|
169
|
+
if (
|
|
170
|
+
affectedMember === EVERYONE &&
|
|
171
|
+
!(
|
|
172
|
+
change.value === "reader" ||
|
|
173
|
+
change.value === "writer" ||
|
|
174
|
+
change.value === "revoked"
|
|
175
|
+
)
|
|
176
|
+
) {
|
|
177
|
+
console.warn(
|
|
178
|
+
"Everyone can only be set to reader, writer or revoked"
|
|
179
|
+
);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
161
183
|
const isFirstSelfAppointment =
|
|
162
184
|
!memberState[transactor] &&
|
|
163
185
|
transactor === initialAdmin &&
|
|
@@ -206,7 +228,7 @@ export function determineValidTransactions(
|
|
|
206
228
|
|
|
207
229
|
return validTransactions;
|
|
208
230
|
} else if (coValue.header.ruleset.type === "ownedByGroup") {
|
|
209
|
-
const groupContent =
|
|
231
|
+
const groupContent = expectGroup(
|
|
210
232
|
coValue.node
|
|
211
233
|
.expectCoValueLoaded(
|
|
212
234
|
coValue.header.ruleset.group,
|
|
@@ -224,11 +246,18 @@ export function determineValidTransactions(
|
|
|
224
246
|
const transactor = accountOrAgentIDfromSessionID(
|
|
225
247
|
sessionID as SessionID
|
|
226
248
|
);
|
|
249
|
+
|
|
227
250
|
return sessionLog.transactions
|
|
228
251
|
.filter((tx) => {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
.
|
|
252
|
+
const groupAtTime = groupContent.atTime(tx.madeAt);
|
|
253
|
+
const effectiveTransactor =
|
|
254
|
+
transactor === groupContent.id &&
|
|
255
|
+
groupAtTime instanceof Account
|
|
256
|
+
? groupAtTime.getCurrentAgentID()
|
|
257
|
+
: transactor;
|
|
258
|
+
const transactorRoleAtTxTime =
|
|
259
|
+
groupAtTime.get(effectiveTransactor) ||
|
|
260
|
+
groupAtTime.get(EVERYONE);
|
|
232
261
|
|
|
233
262
|
return (
|
|
234
263
|
transactorRoleAtTxTime === "admin" ||
|
|
@@ -252,7 +281,8 @@ export function determineValidTransactions(
|
|
|
252
281
|
);
|
|
253
282
|
} else {
|
|
254
283
|
throw new Error(
|
|
255
|
-
"Unknown ruleset type " +
|
|
284
|
+
"Unknown ruleset type " +
|
|
285
|
+
(coValue.header.ruleset as { type: string }).type
|
|
256
286
|
);
|
|
257
287
|
}
|
|
258
288
|
}
|
|
@@ -267,7 +297,8 @@ export function isKeyForAccountField(
|
|
|
267
297
|
field: string
|
|
268
298
|
): field is `${KeyID}_for_${AccountID | AgentID}` {
|
|
269
299
|
return (
|
|
270
|
-
field.startsWith("key_") &&
|
|
271
|
-
|
|
300
|
+
(field.startsWith("key_") &&
|
|
301
|
+
(field.includes("_for_sealer") || field.includes("_for_co"))) ||
|
|
302
|
+
field.includes("_for_everyone")
|
|
272
303
|
);
|
|
273
304
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Account } from "../coValues/account.js";
|
|
2
|
+
import { CoID, CoValue, ControlledAccount, InviteSecret } from "../index.js";
|
|
3
|
+
import { QueryContext } from "../queries.js";
|
|
4
|
+
import { QueriedGroup } from "./queriedGroup.js";
|
|
5
|
+
|
|
6
|
+
export class QueriedAccount<A extends Account = Account> extends QueriedGroup<A> {
|
|
7
|
+
id!: CoID<A>;
|
|
8
|
+
isMe!: boolean;
|
|
9
|
+
|
|
10
|
+
constructor(account: A, queryContext: QueryContext) {
|
|
11
|
+
super(account, queryContext);
|
|
12
|
+
Object.defineProperties(this, {
|
|
13
|
+
id: { value: account.id, enumerable: false },
|
|
14
|
+
isMe: {
|
|
15
|
+
value: account.core.node.account.id === account.id,
|
|
16
|
+
enumerable: false,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
createGroup() {
|
|
22
|
+
if (!this.isMe)
|
|
23
|
+
throw new Error("Only the current user can create a group");
|
|
24
|
+
return (
|
|
25
|
+
this.group.core.node.account as ControlledAccount
|
|
26
|
+
).createGroup();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
acceptInvite<T extends CoValue>(
|
|
30
|
+
groupOrOwnedValueID: CoID<T>,
|
|
31
|
+
inviteSecret: InviteSecret
|
|
32
|
+
) {
|
|
33
|
+
if (!this.isMe)
|
|
34
|
+
throw new Error("Only the current user can accept an invite");
|
|
35
|
+
return (this.group.core.node.account as ControlledAccount).acceptInvite(
|
|
36
|
+
groupOrOwnedValueID,
|
|
37
|
+
inviteSecret
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|