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