cojson 0.8.12 → 0.8.17
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/CHANGELOG.md +95 -83
- package/dist/native/PeerKnownStates.js +6 -1
- package/dist/native/PeerKnownStates.js.map +1 -1
- package/dist/native/PeerState.js +4 -3
- package/dist/native/PeerState.js.map +1 -1
- package/dist/native/PriorityBasedMessageQueue.js +1 -10
- package/dist/native/PriorityBasedMessageQueue.js.map +1 -1
- package/dist/native/SyncStateSubscriptionManager.js +70 -0
- package/dist/native/SyncStateSubscriptionManager.js.map +1 -0
- package/dist/native/base64url.js.map +1 -1
- package/dist/native/base64url.test.js +1 -1
- package/dist/native/base64url.test.js.map +1 -1
- package/dist/native/coValue.js.map +1 -1
- package/dist/native/coValueCore.js +141 -149
- package/dist/native/coValueCore.js.map +1 -1
- package/dist/native/coValueState.js.map +1 -1
- package/dist/native/coValues/account.js +6 -6
- package/dist/native/coValues/account.js.map +1 -1
- package/dist/native/coValues/coList.js +2 -3
- package/dist/native/coValues/coList.js.map +1 -1
- package/dist/native/coValues/coMap.js +1 -1
- package/dist/native/coValues/coMap.js.map +1 -1
- package/dist/native/coValues/coStream.js +3 -5
- package/dist/native/coValues/coStream.js.map +1 -1
- package/dist/native/coValues/group.js +11 -11
- package/dist/native/coValues/group.js.map +1 -1
- package/dist/native/coreToCoValue.js +2 -2
- package/dist/native/coreToCoValue.js.map +1 -1
- package/dist/native/crypto/PureJSCrypto.js +4 -4
- package/dist/native/crypto/PureJSCrypto.js.map +1 -1
- package/dist/native/crypto/crypto.js.map +1 -1
- package/dist/native/exports.js +12 -12
- package/dist/native/exports.js.map +1 -1
- package/dist/native/ids.js.map +1 -1
- package/dist/native/jsonStringify.js.map +1 -1
- package/dist/native/localNode.js +5 -7
- package/dist/native/localNode.js.map +1 -1
- package/dist/native/permissions.js +4 -7
- package/dist/native/permissions.js.map +1 -1
- package/dist/native/priority.js.map +1 -1
- package/dist/native/storage/FileSystem.js.map +1 -1
- package/dist/native/storage/chunksAndKnownStates.js +2 -4
- package/dist/native/storage/chunksAndKnownStates.js.map +1 -1
- package/dist/native/storage/index.js +6 -15
- package/dist/native/storage/index.js.map +1 -1
- package/dist/native/streamUtils.js.map +1 -1
- package/dist/native/sync.js +57 -7
- package/dist/native/sync.js.map +1 -1
- package/dist/native/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
- package/dist/native/typeUtils/expectGroup.js.map +1 -1
- package/dist/native/typeUtils/isAccountID.js.map +1 -1
- package/dist/native/typeUtils/isCoValue.js +1 -1
- package/dist/native/typeUtils/isCoValue.js.map +1 -1
- package/dist/web/PeerKnownStates.js +6 -1
- package/dist/web/PeerKnownStates.js.map +1 -1
- package/dist/web/PeerState.js +4 -3
- package/dist/web/PeerState.js.map +1 -1
- package/dist/web/PriorityBasedMessageQueue.js +1 -10
- package/dist/web/PriorityBasedMessageQueue.js.map +1 -1
- package/dist/web/SyncStateSubscriptionManager.js +70 -0
- package/dist/web/SyncStateSubscriptionManager.js.map +1 -0
- package/dist/web/base64url.js.map +1 -1
- package/dist/web/base64url.test.js +1 -1
- package/dist/web/base64url.test.js.map +1 -1
- package/dist/web/coValue.js.map +1 -1
- package/dist/web/coValueCore.js +141 -149
- package/dist/web/coValueCore.js.map +1 -1
- package/dist/web/coValueState.js.map +1 -1
- package/dist/web/coValues/account.js +6 -6
- package/dist/web/coValues/account.js.map +1 -1
- package/dist/web/coValues/coList.js +2 -3
- package/dist/web/coValues/coList.js.map +1 -1
- package/dist/web/coValues/coMap.js +1 -1
- package/dist/web/coValues/coMap.js.map +1 -1
- package/dist/web/coValues/coStream.js +3 -5
- package/dist/web/coValues/coStream.js.map +1 -1
- package/dist/web/coValues/group.js +11 -11
- package/dist/web/coValues/group.js.map +1 -1
- package/dist/web/coreToCoValue.js +2 -2
- package/dist/web/coreToCoValue.js.map +1 -1
- package/dist/web/crypto/PureJSCrypto.js +4 -4
- package/dist/web/crypto/PureJSCrypto.js.map +1 -1
- package/dist/web/crypto/WasmCrypto.js +5 -5
- package/dist/web/crypto/WasmCrypto.js.map +1 -1
- package/dist/web/crypto/crypto.js.map +1 -1
- package/dist/web/exports.js +12 -12
- package/dist/web/exports.js.map +1 -1
- package/dist/web/ids.js.map +1 -1
- package/dist/web/jsonStringify.js.map +1 -1
- package/dist/web/localNode.js +5 -7
- package/dist/web/localNode.js.map +1 -1
- package/dist/web/permissions.js +4 -7
- package/dist/web/permissions.js.map +1 -1
- package/dist/web/priority.js.map +1 -1
- package/dist/web/storage/FileSystem.js.map +1 -1
- package/dist/web/storage/chunksAndKnownStates.js +2 -4
- package/dist/web/storage/chunksAndKnownStates.js.map +1 -1
- package/dist/web/storage/index.js +6 -15
- package/dist/web/storage/index.js.map +1 -1
- package/dist/web/streamUtils.js.map +1 -1
- package/dist/web/sync.js +57 -7
- package/dist/web/sync.js.map +1 -1
- package/dist/web/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
- package/dist/web/typeUtils/expectGroup.js.map +1 -1
- package/dist/web/typeUtils/isAccountID.js.map +1 -1
- package/dist/web/typeUtils/isCoValue.js +1 -1
- package/dist/web/typeUtils/isCoValue.js.map +1 -1
- package/package.json +4 -14
- package/src/PeerKnownStates.ts +98 -90
- package/src/PeerState.ts +92 -73
- package/src/PriorityBasedMessageQueue.ts +42 -49
- package/src/SyncStateSubscriptionManager.ts +124 -0
- package/src/base64url.test.ts +24 -24
- package/src/base64url.ts +44 -45
- package/src/coValue.ts +45 -45
- package/src/coValueCore.ts +746 -785
- package/src/coValueState.ts +82 -72
- package/src/coValues/account.ts +143 -150
- package/src/coValues/coList.ts +520 -522
- package/src/coValues/coMap.ts +283 -285
- package/src/coValues/coStream.ts +320 -324
- package/src/coValues/group.ts +306 -305
- package/src/coreToCoValue.ts +28 -31
- package/src/crypto/PureJSCrypto.ts +188 -194
- package/src/crypto/WasmCrypto.ts +236 -254
- package/src/crypto/crypto.ts +302 -309
- package/src/exports.ts +116 -116
- package/src/ids.ts +9 -9
- package/src/jsonStringify.ts +46 -46
- package/src/jsonValue.ts +24 -10
- package/src/localNode.ts +635 -660
- package/src/media.ts +3 -3
- package/src/permissions.ts +272 -278
- package/src/priority.ts +21 -19
- package/src/storage/FileSystem.ts +91 -99
- package/src/storage/chunksAndKnownStates.ts +110 -115
- package/src/storage/index.ts +466 -497
- package/src/streamUtils.ts +60 -60
- package/src/sync.ts +656 -608
- package/src/tests/PeerKnownStates.test.ts +38 -34
- package/src/tests/PeerState.test.ts +101 -64
- package/src/tests/PriorityBasedMessageQueue.test.ts +91 -91
- package/src/tests/SyncStateSubscriptionManager.test.ts +232 -0
- package/src/tests/account.test.ts +59 -59
- package/src/tests/coList.test.ts +65 -65
- package/src/tests/coMap.test.ts +137 -137
- package/src/tests/coStream.test.ts +254 -257
- package/src/tests/coValueCore.test.ts +153 -156
- package/src/tests/crypto.test.ts +136 -144
- package/src/tests/cryptoImpl.test.ts +205 -197
- package/src/tests/group.test.ts +24 -24
- package/src/tests/permissions.test.ts +1306 -1371
- package/src/tests/priority.test.ts +65 -82
- package/src/tests/sync.test.ts +1573 -1263
- package/src/tests/testUtils.ts +85 -53
- package/src/typeUtils/accountOrAgentIDfromSessionID.ts +4 -4
- package/src/typeUtils/expectGroup.ts +9 -9
- package/src/typeUtils/isAccountID.ts +1 -1
- package/src/typeUtils/isCoValue.ts +9 -9
- package/tsconfig.json +4 -6
- package/tsconfig.native.json +9 -11
- package/tsconfig.web.json +4 -10
- package/.eslintrc.cjs +0 -25
- package/.prettierrc.js +0 -9
package/src/coValues/group.ts
CHANGED
|
@@ -1,30 +1,34 @@
|
|
|
1
|
+
import { base58 } from "@scure/base";
|
|
1
2
|
import { CoID } from "../coValue.js";
|
|
2
|
-
import {
|
|
3
|
-
import { RawCoList } from "./coList.js";
|
|
4
|
-
import { JsonObject } from "../jsonValue.js";
|
|
5
|
-
import { RawBinaryCoStream, RawCoStream } from "./coStream.js";
|
|
3
|
+
import { CoValueUniqueness } from "../coValueCore.js";
|
|
6
4
|
import { Encrypted, KeyID, KeySecret, Sealed } from "../crypto/crypto.js";
|
|
7
5
|
import { AgentID, isAgentID } from "../ids.js";
|
|
8
|
-
import {
|
|
6
|
+
import { JsonObject } from "../jsonValue.js";
|
|
9
7
|
import { Role } from "../permissions.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
8
|
+
import {
|
|
9
|
+
ControlledAccountOrAgent,
|
|
10
|
+
RawAccount,
|
|
11
|
+
RawAccountID,
|
|
12
|
+
} from "./account.js";
|
|
13
|
+
import { RawCoList } from "./coList.js";
|
|
14
|
+
import { RawCoMap } from "./coMap.js";
|
|
15
|
+
import { RawBinaryCoStream, RawCoStream } from "./coStream.js";
|
|
12
16
|
|
|
13
17
|
export const EVERYONE = "everyone" as const;
|
|
14
18
|
export type Everyone = "everyone";
|
|
15
19
|
|
|
16
20
|
export type GroupShape = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
profile: CoID<RawCoMap> | null;
|
|
22
|
+
root: CoID<RawCoMap> | null;
|
|
23
|
+
[key: RawAccountID | AgentID]: Role;
|
|
24
|
+
[EVERYONE]?: Role;
|
|
25
|
+
readKey?: KeyID;
|
|
26
|
+
[revelationFor: `${KeyID}_for_${RawAccountID | AgentID}`]: Sealed<KeySecret>;
|
|
27
|
+
[revelationFor: `${KeyID}_for_${Everyone}`]: KeySecret;
|
|
28
|
+
[oldKeyForNewKey: `${KeyID}_for_${KeyID}`]: Encrypted<
|
|
29
|
+
KeySecret,
|
|
30
|
+
{ encryptedID: KeyID; encryptingID: KeyID }
|
|
31
|
+
>;
|
|
28
32
|
};
|
|
29
33
|
|
|
30
34
|
/** A `Group` is a scope for permissions of its members (`"reader" | "writer" | "admin"`), applying to objects owned by that group.
|
|
@@ -49,313 +53,310 @@ export type GroupShape = {
|
|
|
49
53
|
* ```
|
|
50
54
|
* */
|
|
51
55
|
export class RawGroup<
|
|
52
|
-
|
|
56
|
+
Meta extends JsonObject | null = JsonObject | null,
|
|
53
57
|
> extends RawCoMap<GroupShape, Meta> {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Returns the current role of a given account.
|
|
60
|
+
*
|
|
61
|
+
* @category 1. Role reading
|
|
62
|
+
*/
|
|
63
|
+
roleOf(accountID: RawAccountID): Role | undefined {
|
|
64
|
+
return this.roleOfInternal(accountID);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** @internal */
|
|
68
|
+
roleOfInternal(accountID: RawAccountID | AgentID): Role | undefined {
|
|
69
|
+
return this.get(accountID);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Returns the role of the current account in the group.
|
|
74
|
+
*
|
|
75
|
+
* @category 1. Role reading
|
|
76
|
+
*/
|
|
77
|
+
myRole(): Role | undefined {
|
|
78
|
+
return this.roleOfInternal(this.core.node.account.id);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Directly grants a new member a role in the group. The current account must be an
|
|
83
|
+
* admin to be able to do so. Throws otherwise.
|
|
84
|
+
*
|
|
85
|
+
* @category 2. Role changing
|
|
86
|
+
*/
|
|
87
|
+
addMember(
|
|
88
|
+
account: RawAccount | ControlledAccountOrAgent | Everyone,
|
|
89
|
+
role: Role,
|
|
90
|
+
) {
|
|
91
|
+
this.addMemberInternal(account, role);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** @internal */
|
|
95
|
+
addMemberInternal(
|
|
96
|
+
account: RawAccount | ControlledAccountOrAgent | AgentID | Everyone,
|
|
97
|
+
role: Role,
|
|
98
|
+
) {
|
|
99
|
+
const currentReadKey = this.core.getCurrentReadKey();
|
|
100
|
+
|
|
101
|
+
if (!currentReadKey.secret) {
|
|
102
|
+
throw new Error("Can't add member without read key secret");
|
|
75
103
|
}
|
|
76
104
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
* @category 2. Role changing
|
|
82
|
-
*/
|
|
83
|
-
addMember(
|
|
84
|
-
account: RawAccount | ControlledAccountOrAgent | Everyone,
|
|
85
|
-
role: Role,
|
|
86
|
-
) {
|
|
87
|
-
this.addMemberInternal(account, role);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** @internal */
|
|
91
|
-
addMemberInternal(
|
|
92
|
-
account: RawAccount | ControlledAccountOrAgent | AgentID | Everyone,
|
|
93
|
-
role: Role,
|
|
94
|
-
) {
|
|
95
|
-
const currentReadKey = this.core.getCurrentReadKey();
|
|
96
|
-
|
|
97
|
-
if (!currentReadKey.secret) {
|
|
98
|
-
throw new Error("Can't add member without read key secret");
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (account === EVERYONE) {
|
|
102
|
-
if (!(role === "reader" || role === "writer")) {
|
|
103
|
-
throw new Error(
|
|
104
|
-
"Can't make everyone something other than reader or writer",
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
this.set(account, role, "trusting");
|
|
108
|
-
|
|
109
|
-
if (this.get(account) !== role) {
|
|
110
|
-
throw new Error("Failed to set role");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
this.set(
|
|
114
|
-
`${currentReadKey.id}_for_${EVERYONE}`,
|
|
115
|
-
currentReadKey.secret,
|
|
116
|
-
"trusting",
|
|
117
|
-
);
|
|
118
|
-
} else {
|
|
119
|
-
const memberKey =
|
|
120
|
-
typeof account === "string" ? account : account.id;
|
|
121
|
-
const agent =
|
|
122
|
-
typeof account === "string"
|
|
123
|
-
? account
|
|
124
|
-
: account
|
|
125
|
-
.currentAgentID()
|
|
126
|
-
._unsafeUnwrap({ withStackTrace: true });
|
|
127
|
-
this.set(memberKey, role, "trusting");
|
|
128
|
-
|
|
129
|
-
if (this.get(memberKey) !== role) {
|
|
130
|
-
throw new Error("Failed to set role");
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
this.set(
|
|
134
|
-
`${currentReadKey.id}_for_${memberKey}`,
|
|
135
|
-
this.core.crypto.seal({
|
|
136
|
-
message: currentReadKey.secret,
|
|
137
|
-
from: this.core.node.account.currentSealerSecret(),
|
|
138
|
-
to: this.core.crypto.getAgentSealerID(agent),
|
|
139
|
-
nOnceMaterial: {
|
|
140
|
-
in: this.id,
|
|
141
|
-
tx: this.core.nextTransactionID(),
|
|
142
|
-
},
|
|
143
|
-
}),
|
|
144
|
-
"trusting",
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/** @internal */
|
|
150
|
-
rotateReadKey() {
|
|
151
|
-
const currentlyPermittedReaders = this.keys().filter((key) => {
|
|
152
|
-
if (key.startsWith("co_") || isAgentID(key)) {
|
|
153
|
-
const role = this.get(key);
|
|
154
|
-
return (
|
|
155
|
-
role === "admin" || role === "writer" || role === "reader"
|
|
156
|
-
);
|
|
157
|
-
} else {
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
}) as (RawAccountID | AgentID)[];
|
|
161
|
-
|
|
162
|
-
const maybeCurrentReadKey = this.core.getCurrentReadKey();
|
|
163
|
-
|
|
164
|
-
if (!maybeCurrentReadKey.secret) {
|
|
165
|
-
throw new Error(
|
|
166
|
-
"Can't rotate read key secret we don't have access to",
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const currentReadKey = {
|
|
171
|
-
id: maybeCurrentReadKey.id,
|
|
172
|
-
secret: maybeCurrentReadKey.secret,
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
const newReadKey = this.core.crypto.newRandomKeySecret();
|
|
176
|
-
|
|
177
|
-
for (const readerID of currentlyPermittedReaders) {
|
|
178
|
-
const reader = this.core.node.resolveAccountAgent(
|
|
179
|
-
readerID,
|
|
180
|
-
"Expected to know currently permitted reader",
|
|
181
|
-
)._unsafeUnwrap({ withStackTrace: true });
|
|
182
|
-
|
|
183
|
-
this.set(
|
|
184
|
-
`${newReadKey.id}_for_${readerID}`,
|
|
185
|
-
this.core.crypto.seal({
|
|
186
|
-
message: newReadKey.secret,
|
|
187
|
-
from: this.core.node.account.currentSealerSecret(),
|
|
188
|
-
to: this.core.crypto.getAgentSealerID(reader),
|
|
189
|
-
nOnceMaterial: {
|
|
190
|
-
in: this.id,
|
|
191
|
-
tx: this.core.nextTransactionID(),
|
|
192
|
-
},
|
|
193
|
-
}),
|
|
194
|
-
"trusting",
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
this.set(
|
|
199
|
-
`${currentReadKey.id}_for_${newReadKey.id}`,
|
|
200
|
-
this.core.crypto.encryptKeySecret({
|
|
201
|
-
encrypting: newReadKey,
|
|
202
|
-
toEncrypt: currentReadKey,
|
|
203
|
-
}).encrypted,
|
|
204
|
-
"trusting",
|
|
105
|
+
if (account === EVERYONE) {
|
|
106
|
+
if (!(role === "reader" || role === "writer")) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
"Can't make everyone something other than reader or writer",
|
|
205
109
|
);
|
|
206
|
-
|
|
207
|
-
|
|
110
|
+
}
|
|
111
|
+
this.set(account, role, "trusting");
|
|
112
|
+
|
|
113
|
+
if (this.get(account) !== role) {
|
|
114
|
+
throw new Error("Failed to set role");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
this.set(
|
|
118
|
+
`${currentReadKey.id}_for_${EVERYONE}`,
|
|
119
|
+
currentReadKey.secret,
|
|
120
|
+
"trusting",
|
|
121
|
+
);
|
|
122
|
+
} else {
|
|
123
|
+
const memberKey = typeof account === "string" ? account : account.id;
|
|
124
|
+
const agent =
|
|
125
|
+
typeof account === "string"
|
|
126
|
+
? account
|
|
127
|
+
: account.currentAgentID()._unsafeUnwrap({ withStackTrace: true });
|
|
128
|
+
this.set(memberKey, role, "trusting");
|
|
129
|
+
|
|
130
|
+
if (this.get(memberKey) !== role) {
|
|
131
|
+
throw new Error("Failed to set role");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.set(
|
|
135
|
+
`${currentReadKey.id}_for_${memberKey}`,
|
|
136
|
+
this.core.crypto.seal({
|
|
137
|
+
message: currentReadKey.secret,
|
|
138
|
+
from: this.core.node.account.currentSealerSecret(),
|
|
139
|
+
to: this.core.crypto.getAgentSealerID(agent),
|
|
140
|
+
nOnceMaterial: {
|
|
141
|
+
in: this.id,
|
|
142
|
+
tx: this.core.nextTransactionID(),
|
|
143
|
+
},
|
|
144
|
+
}),
|
|
145
|
+
"trusting",
|
|
146
|
+
);
|
|
208
147
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** @internal */
|
|
151
|
+
rotateReadKey() {
|
|
152
|
+
const currentlyPermittedReaders = this.keys().filter((key) => {
|
|
153
|
+
if (key.startsWith("co_") || isAgentID(key)) {
|
|
154
|
+
const role = this.get(key);
|
|
155
|
+
return role === "admin" || role === "writer" || role === "reader";
|
|
156
|
+
} else {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}) as (RawAccountID | AgentID)[];
|
|
160
|
+
|
|
161
|
+
const maybeCurrentReadKey = this.core.getCurrentReadKey();
|
|
162
|
+
|
|
163
|
+
if (!maybeCurrentReadKey.secret) {
|
|
164
|
+
throw new Error("Can't rotate read key secret we don't have access to");
|
|
219
165
|
}
|
|
220
166
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
167
|
+
const currentReadKey = {
|
|
168
|
+
id: maybeCurrentReadKey.id,
|
|
169
|
+
secret: maybeCurrentReadKey.secret,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const newReadKey = this.core.crypto.newRandomKeySecret();
|
|
173
|
+
|
|
174
|
+
for (const readerID of currentlyPermittedReaders) {
|
|
175
|
+
const reader = this.core.node
|
|
176
|
+
.resolveAccountAgent(
|
|
177
|
+
readerID,
|
|
178
|
+
"Expected to know currently permitted reader",
|
|
179
|
+
)
|
|
180
|
+
._unsafeUnwrap({ withStackTrace: true });
|
|
181
|
+
|
|
182
|
+
this.set(
|
|
183
|
+
`${newReadKey.id}_for_${readerID}`,
|
|
184
|
+
this.core.crypto.seal({
|
|
185
|
+
message: newReadKey.secret,
|
|
186
|
+
from: this.core.node.account.currentSealerSecret(),
|
|
187
|
+
to: this.core.crypto.getAgentSealerID(reader),
|
|
188
|
+
nOnceMaterial: {
|
|
189
|
+
in: this.id,
|
|
190
|
+
tx: this.core.nextTransactionID(),
|
|
191
|
+
},
|
|
192
|
+
}),
|
|
193
|
+
"trusting",
|
|
194
|
+
);
|
|
228
195
|
}
|
|
229
196
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
197
|
+
this.set(
|
|
198
|
+
`${currentReadKey.id}_for_${newReadKey.id}`,
|
|
199
|
+
this.core.crypto.encryptKeySecret({
|
|
200
|
+
encrypting: newReadKey,
|
|
201
|
+
toEncrypt: currentReadKey,
|
|
202
|
+
}).encrypted,
|
|
203
|
+
"trusting",
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
this.set("readKey", newReadKey.id, "trusting");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Strips the specified member of all roles (preventing future writes in
|
|
211
|
+
* the group and owned values) and rotates the read encryption key for that group
|
|
212
|
+
* (preventing reads of new content in the group and owned values)
|
|
213
|
+
*
|
|
214
|
+
* @category 2. Role changing
|
|
215
|
+
*/
|
|
216
|
+
removeMember(account: RawAccount | ControlledAccountOrAgent | Everyone) {
|
|
217
|
+
this.removeMemberInternal(account);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** @internal */
|
|
221
|
+
removeMemberInternal(
|
|
222
|
+
account: RawAccount | ControlledAccountOrAgent | AgentID | Everyone,
|
|
223
|
+
) {
|
|
224
|
+
const memberKey = typeof account === "string" ? account : account.id;
|
|
225
|
+
this.set(memberKey, "revoked", "trusting");
|
|
226
|
+
this.rotateReadKey();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Creates an invite for new members to indirectly join the group,
|
|
231
|
+
* allowing them to grant themselves the specified role with the InviteSecret
|
|
232
|
+
* (a string starting with "inviteSecret_") - use `LocalNode.acceptInvite()` for this purpose.
|
|
233
|
+
*
|
|
234
|
+
* @category 2. Role changing
|
|
235
|
+
*/
|
|
236
|
+
createInvite(role: "reader" | "writer" | "admin"): InviteSecret {
|
|
237
|
+
const secretSeed = this.core.crypto.newRandomSecretSeed();
|
|
238
|
+
|
|
239
|
+
const inviteSecret = this.core.crypto.agentSecretFromSecretSeed(secretSeed);
|
|
240
|
+
const inviteID = this.core.crypto.getAgentID(inviteSecret);
|
|
241
|
+
|
|
242
|
+
this.addMemberInternal(inviteID, `${role}Invite` as Role);
|
|
243
|
+
|
|
244
|
+
return inviteSecretFromSecretSeed(secretSeed);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Creates a new `CoMap` within this group, with the specified specialized
|
|
249
|
+
* `CoMap` type `M` and optional static metadata.
|
|
250
|
+
*
|
|
251
|
+
* @category 3. Value creation
|
|
252
|
+
*/
|
|
253
|
+
createMap<M extends RawCoMap>(
|
|
254
|
+
init?: M["_shape"],
|
|
255
|
+
meta?: M["headerMeta"],
|
|
256
|
+
initPrivacy: "trusting" | "private" = "private",
|
|
257
|
+
uniqueness: CoValueUniqueness = this.core.crypto.createdNowUnique(),
|
|
258
|
+
): M {
|
|
259
|
+
const map = this.core.node
|
|
260
|
+
.createCoValue({
|
|
261
|
+
type: "comap",
|
|
262
|
+
ruleset: {
|
|
263
|
+
type: "ownedByGroup",
|
|
264
|
+
group: this.id,
|
|
265
|
+
},
|
|
266
|
+
meta: meta || null,
|
|
267
|
+
...uniqueness,
|
|
268
|
+
})
|
|
269
|
+
.getCurrentContent() as M;
|
|
270
|
+
|
|
271
|
+
if (init) {
|
|
272
|
+
for (const [key, value] of Object.entries(init)) {
|
|
273
|
+
map.set(key, value, initPrivacy);
|
|
274
|
+
}
|
|
280
275
|
}
|
|
281
276
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
277
|
+
return map;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Creates a new `CoList` within this group, with the specified specialized
|
|
282
|
+
* `CoList` type `L` and optional static metadata.
|
|
283
|
+
*
|
|
284
|
+
* @category 3. Value creation
|
|
285
|
+
*/
|
|
286
|
+
createList<L extends RawCoList>(
|
|
287
|
+
init?: L["_item"][],
|
|
288
|
+
meta?: L["headerMeta"],
|
|
289
|
+
initPrivacy: "trusting" | "private" = "private",
|
|
290
|
+
uniqueness: CoValueUniqueness = this.core.crypto.createdNowUnique(),
|
|
291
|
+
): L {
|
|
292
|
+
const list = this.core.node
|
|
293
|
+
.createCoValue({
|
|
294
|
+
type: "colist",
|
|
295
|
+
ruleset: {
|
|
296
|
+
type: "ownedByGroup",
|
|
297
|
+
group: this.id,
|
|
298
|
+
},
|
|
299
|
+
meta: meta || null,
|
|
300
|
+
...uniqueness,
|
|
301
|
+
})
|
|
302
|
+
.getCurrentContent() as L;
|
|
303
|
+
|
|
304
|
+
if (init) {
|
|
305
|
+
for (const item of init) {
|
|
306
|
+
list.append(item, undefined, initPrivacy);
|
|
307
|
+
}
|
|
313
308
|
}
|
|
314
309
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
310
|
+
return list;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** @category 3. Value creation */
|
|
314
|
+
createStream<C extends RawCoStream>(
|
|
315
|
+
meta?: C["headerMeta"],
|
|
316
|
+
uniqueness: CoValueUniqueness = this.core.crypto.createdNowUnique(),
|
|
317
|
+
): C {
|
|
318
|
+
return this.core.node
|
|
319
|
+
.createCoValue({
|
|
320
|
+
type: "costream",
|
|
321
|
+
ruleset: {
|
|
322
|
+
type: "ownedByGroup",
|
|
323
|
+
group: this.id,
|
|
324
|
+
},
|
|
325
|
+
meta: meta || null,
|
|
326
|
+
...uniqueness,
|
|
327
|
+
})
|
|
328
|
+
.getCurrentContent() as C;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** @category 3. Value creation */
|
|
332
|
+
createBinaryStream<C extends RawBinaryCoStream>(
|
|
333
|
+
meta: C["headerMeta"] = { type: "binary" },
|
|
334
|
+
uniqueness: CoValueUniqueness = this.core.crypto.createdNowUnique(),
|
|
335
|
+
): C {
|
|
336
|
+
return this.core.node
|
|
337
|
+
.createCoValue({
|
|
338
|
+
type: "costream",
|
|
339
|
+
ruleset: {
|
|
340
|
+
type: "ownedByGroup",
|
|
341
|
+
group: this.id,
|
|
342
|
+
},
|
|
343
|
+
meta: meta,
|
|
344
|
+
...uniqueness,
|
|
345
|
+
})
|
|
346
|
+
.getCurrentContent() as C;
|
|
347
|
+
}
|
|
347
348
|
}
|
|
348
349
|
|
|
349
350
|
export type InviteSecret = `inviteSecret_z${string}`;
|
|
350
351
|
|
|
351
352
|
function inviteSecretFromSecretSeed(secretSeed: Uint8Array): InviteSecret {
|
|
352
|
-
|
|
353
|
+
return `inviteSecret_z${base58.encode(secretSeed)}`;
|
|
353
354
|
}
|
|
354
355
|
|
|
355
356
|
export function secretSeedFromInviteSecret(inviteSecret: InviteSecret) {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
357
|
+
if (!inviteSecret.startsWith("inviteSecret_z")) {
|
|
358
|
+
throw new Error("Invalid invite secret");
|
|
359
|
+
}
|
|
359
360
|
|
|
360
|
-
|
|
361
|
+
return base58.decode(inviteSecret.slice("inviteSecret_z".length));
|
|
361
362
|
}
|