cojson 0.8.11 → 0.8.16

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 (158) hide show
  1. package/CHANGELOG.md +94 -82
  2. package/dist/native/PeerKnownStates.js +1 -1
  3. package/dist/native/PeerKnownStates.js.map +1 -1
  4. package/dist/native/PeerState.js +4 -1
  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/base64url.js.map +1 -1
  9. package/dist/native/base64url.test.js +1 -1
  10. package/dist/native/base64url.test.js.map +1 -1
  11. package/dist/native/coValue.js.map +1 -1
  12. package/dist/native/coValueCore.js +141 -149
  13. package/dist/native/coValueCore.js.map +1 -1
  14. package/dist/native/coValueState.js.map +1 -1
  15. package/dist/native/coValues/account.js +6 -6
  16. package/dist/native/coValues/account.js.map +1 -1
  17. package/dist/native/coValues/coList.js +2 -3
  18. package/dist/native/coValues/coList.js.map +1 -1
  19. package/dist/native/coValues/coMap.js +1 -1
  20. package/dist/native/coValues/coMap.js.map +1 -1
  21. package/dist/native/coValues/coStream.js +3 -5
  22. package/dist/native/coValues/coStream.js.map +1 -1
  23. package/dist/native/coValues/group.js +11 -11
  24. package/dist/native/coValues/group.js.map +1 -1
  25. package/dist/native/coreToCoValue.js +2 -2
  26. package/dist/native/coreToCoValue.js.map +1 -1
  27. package/dist/native/crypto/PureJSCrypto.js +4 -4
  28. package/dist/native/crypto/PureJSCrypto.js.map +1 -1
  29. package/dist/native/crypto/crypto.js.map +1 -1
  30. package/dist/native/exports.js +12 -12
  31. package/dist/native/exports.js.map +1 -1
  32. package/dist/native/ids.js.map +1 -1
  33. package/dist/native/jsonStringify.js.map +1 -1
  34. package/dist/native/localNode.js +6 -8
  35. package/dist/native/localNode.js.map +1 -1
  36. package/dist/native/permissions.js +4 -7
  37. package/dist/native/permissions.js.map +1 -1
  38. package/dist/native/priority.js.map +1 -1
  39. package/dist/native/storage/FileSystem.js.map +1 -1
  40. package/dist/native/storage/chunksAndKnownStates.js +2 -4
  41. package/dist/native/storage/chunksAndKnownStates.js.map +1 -1
  42. package/dist/native/storage/index.js +7 -16
  43. package/dist/native/storage/index.js.map +1 -1
  44. package/dist/native/streamUtils.js.map +1 -1
  45. package/dist/native/sync.js +6 -8
  46. package/dist/native/sync.js.map +1 -1
  47. package/dist/native/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
  48. package/dist/native/typeUtils/expectGroup.js.map +1 -1
  49. package/dist/native/typeUtils/isAccountID.js.map +1 -1
  50. package/dist/native/typeUtils/isCoValue.js +1 -1
  51. package/dist/native/typeUtils/isCoValue.js.map +1 -1
  52. package/dist/web/PeerKnownStates.js +1 -1
  53. package/dist/web/PeerKnownStates.js.map +1 -1
  54. package/dist/web/PeerState.js +4 -1
  55. package/dist/web/PeerState.js.map +1 -1
  56. package/dist/web/PriorityBasedMessageQueue.js +1 -10
  57. package/dist/web/PriorityBasedMessageQueue.js.map +1 -1
  58. package/dist/web/base64url.js.map +1 -1
  59. package/dist/web/base64url.test.js +1 -1
  60. package/dist/web/base64url.test.js.map +1 -1
  61. package/dist/web/coValue.js.map +1 -1
  62. package/dist/web/coValueCore.js +141 -149
  63. package/dist/web/coValueCore.js.map +1 -1
  64. package/dist/web/coValueState.js.map +1 -1
  65. package/dist/web/coValues/account.js +6 -6
  66. package/dist/web/coValues/account.js.map +1 -1
  67. package/dist/web/coValues/coList.js +2 -3
  68. package/dist/web/coValues/coList.js.map +1 -1
  69. package/dist/web/coValues/coMap.js +1 -1
  70. package/dist/web/coValues/coMap.js.map +1 -1
  71. package/dist/web/coValues/coStream.js +3 -5
  72. package/dist/web/coValues/coStream.js.map +1 -1
  73. package/dist/web/coValues/group.js +11 -11
  74. package/dist/web/coValues/group.js.map +1 -1
  75. package/dist/web/coreToCoValue.js +2 -2
  76. package/dist/web/coreToCoValue.js.map +1 -1
  77. package/dist/web/crypto/PureJSCrypto.js +4 -4
  78. package/dist/web/crypto/PureJSCrypto.js.map +1 -1
  79. package/dist/web/crypto/WasmCrypto.js +5 -5
  80. package/dist/web/crypto/WasmCrypto.js.map +1 -1
  81. package/dist/web/crypto/crypto.js.map +1 -1
  82. package/dist/web/exports.js +12 -12
  83. package/dist/web/exports.js.map +1 -1
  84. package/dist/web/ids.js.map +1 -1
  85. package/dist/web/jsonStringify.js.map +1 -1
  86. package/dist/web/localNode.js +6 -8
  87. package/dist/web/localNode.js.map +1 -1
  88. package/dist/web/permissions.js +4 -7
  89. package/dist/web/permissions.js.map +1 -1
  90. package/dist/web/priority.js.map +1 -1
  91. package/dist/web/storage/FileSystem.js.map +1 -1
  92. package/dist/web/storage/chunksAndKnownStates.js +2 -4
  93. package/dist/web/storage/chunksAndKnownStates.js.map +1 -1
  94. package/dist/web/storage/index.js +7 -16
  95. package/dist/web/storage/index.js.map +1 -1
  96. package/dist/web/streamUtils.js.map +1 -1
  97. package/dist/web/sync.js +6 -8
  98. package/dist/web/sync.js.map +1 -1
  99. package/dist/web/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
  100. package/dist/web/typeUtils/expectGroup.js.map +1 -1
  101. package/dist/web/typeUtils/isAccountID.js.map +1 -1
  102. package/dist/web/typeUtils/isCoValue.js +1 -1
  103. package/dist/web/typeUtils/isCoValue.js.map +1 -1
  104. package/package.json +4 -14
  105. package/src/PeerKnownStates.ts +91 -89
  106. package/src/PeerState.ts +72 -69
  107. package/src/PriorityBasedMessageQueue.ts +42 -49
  108. package/src/base64url.test.ts +24 -24
  109. package/src/base64url.ts +44 -45
  110. package/src/coValue.ts +45 -45
  111. package/src/coValueCore.ts +746 -785
  112. package/src/coValueState.ts +82 -72
  113. package/src/coValues/account.ts +143 -150
  114. package/src/coValues/coList.ts +520 -522
  115. package/src/coValues/coMap.ts +283 -285
  116. package/src/coValues/coStream.ts +320 -324
  117. package/src/coValues/group.ts +306 -305
  118. package/src/coreToCoValue.ts +28 -31
  119. package/src/crypto/PureJSCrypto.ts +188 -194
  120. package/src/crypto/WasmCrypto.ts +236 -254
  121. package/src/crypto/crypto.ts +302 -309
  122. package/src/exports.ts +116 -116
  123. package/src/ids.ts +9 -9
  124. package/src/jsonStringify.ts +46 -46
  125. package/src/jsonValue.ts +24 -10
  126. package/src/localNode.ts +635 -660
  127. package/src/media.ts +3 -3
  128. package/src/permissions.ts +272 -278
  129. package/src/priority.ts +21 -19
  130. package/src/storage/FileSystem.ts +91 -99
  131. package/src/storage/chunksAndKnownStates.ts +110 -115
  132. package/src/storage/index.ts +466 -497
  133. package/src/streamUtils.ts +60 -60
  134. package/src/sync.ts +593 -615
  135. package/src/tests/PeerKnownStates.test.ts +38 -34
  136. package/src/tests/PeerState.test.ts +101 -64
  137. package/src/tests/PriorityBasedMessageQueue.test.ts +91 -91
  138. package/src/tests/account.test.ts +59 -59
  139. package/src/tests/coList.test.ts +65 -65
  140. package/src/tests/coMap.test.ts +137 -137
  141. package/src/tests/coStream.test.ts +254 -257
  142. package/src/tests/coValueCore.test.ts +153 -156
  143. package/src/tests/crypto.test.ts +136 -144
  144. package/src/tests/cryptoImpl.test.ts +205 -197
  145. package/src/tests/group.test.ts +24 -24
  146. package/src/tests/permissions.test.ts +1306 -1371
  147. package/src/tests/priority.test.ts +65 -82
  148. package/src/tests/sync.test.ts +1300 -1291
  149. package/src/tests/testUtils.ts +52 -53
  150. package/src/typeUtils/accountOrAgentIDfromSessionID.ts +4 -4
  151. package/src/typeUtils/expectGroup.ts +9 -9
  152. package/src/typeUtils/isAccountID.ts +1 -1
  153. package/src/typeUtils/isCoValue.ts +9 -9
  154. package/tsconfig.json +4 -6
  155. package/tsconfig.native.json +9 -11
  156. package/tsconfig.web.json +4 -10
  157. package/.eslintrc.cjs +0 -25
  158. 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
  }