cojson 0.0.11 → 0.0.12

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 (75) hide show
  1. package/README.md +2 -2
  2. package/dist/account.d.ts +57 -0
  3. package/dist/account.js +76 -0
  4. package/dist/account.js.map +1 -0
  5. package/dist/account.test.d.ts +1 -0
  6. package/dist/account.test.js +40 -0
  7. package/dist/account.test.js.map +1 -0
  8. package/dist/coValue.d.ts +16 -35
  9. package/dist/coValue.js +49 -112
  10. package/dist/coValue.js.map +1 -1
  11. package/dist/coValue.test.js +16 -16
  12. package/dist/coValue.test.js.map +1 -1
  13. package/dist/contentType.d.ts +9 -9
  14. package/dist/contentType.js.map +1 -1
  15. package/dist/contentType.test.js +13 -17
  16. package/dist/contentType.test.js.map +1 -1
  17. package/dist/contentTypes/coList.d.ts +3 -3
  18. package/dist/contentTypes/coList.js.map +1 -1
  19. package/dist/contentTypes/coMap.d.ts +31 -21
  20. package/dist/contentTypes/coMap.js +28 -0
  21. package/dist/contentTypes/coMap.js.map +1 -1
  22. package/dist/contentTypes/coStream.d.ts +3 -3
  23. package/dist/contentTypes/coStream.js.map +1 -1
  24. package/dist/contentTypes/static.d.ts +4 -4
  25. package/dist/contentTypes/static.js.map +1 -1
  26. package/dist/crypto.d.ts +45 -39
  27. package/dist/crypto.js +68 -49
  28. package/dist/crypto.js.map +1 -1
  29. package/dist/crypto.test.js +45 -49
  30. package/dist/crypto.test.js.map +1 -1
  31. package/dist/ids.d.ts +5 -3
  32. package/dist/ids.js +3 -1
  33. package/dist/ids.js.map +1 -1
  34. package/dist/index.d.ts +12 -14
  35. package/dist/index.js +6 -8
  36. package/dist/index.js.map +1 -1
  37. package/dist/jsonValue.d.ts +2 -2
  38. package/dist/node.d.ts +25 -15
  39. package/dist/node.js +88 -33
  40. package/dist/node.js.map +1 -1
  41. package/dist/permissions.d.ts +27 -33
  42. package/dist/permissions.js +55 -47
  43. package/dist/permissions.js.map +1 -1
  44. package/dist/permissions.test.js +231 -314
  45. package/dist/permissions.test.js.map +1 -1
  46. package/dist/sync.d.ts +26 -28
  47. package/dist/sync.js +67 -63
  48. package/dist/sync.js.map +1 -1
  49. package/dist/sync.test.js +181 -298
  50. package/dist/sync.test.js.map +1 -1
  51. package/dist/testUtils.d.ts +37 -0
  52. package/dist/testUtils.js +157 -0
  53. package/dist/testUtils.js.map +1 -0
  54. package/package.json +1 -1
  55. package/src/account.test.ts +67 -0
  56. package/src/account.ts +152 -0
  57. package/src/coValue.test.ts +17 -31
  58. package/src/coValue.ts +93 -179
  59. package/src/contentType.test.ts +18 -45
  60. package/src/contentType.ts +15 -13
  61. package/src/contentTypes/coList.ts +4 -4
  62. package/src/contentTypes/coMap.ts +55 -29
  63. package/src/contentTypes/coStream.ts +4 -4
  64. package/src/contentTypes/static.ts +5 -5
  65. package/src/crypto.test.ts +53 -59
  66. package/src/crypto.ts +123 -95
  67. package/src/ids.ts +9 -3
  68. package/src/index.ts +14 -25
  69. package/src/jsonValue.ts +2 -2
  70. package/src/node.ts +189 -61
  71. package/src/permissions.test.ts +370 -404
  72. package/src/permissions.ts +126 -109
  73. package/src/sync.test.ts +258 -432
  74. package/src/sync.ts +95 -98
  75. package/src/testUtils.ts +229 -0
package/src/node.ts CHANGED
@@ -1,40 +1,94 @@
1
- import { createdNowUnique, newRandomKeySecret, seal } from './crypto.js';
2
1
  import {
3
- CoValue,
4
- AgentCredential,
5
- Agent,
6
- getAgent,
2
+ AgentSecret,
3
+ createdNowUnique,
7
4
  getAgentID,
8
- getAgentCoValueHeader,
9
- CoValueHeader,
10
- newRandomAgentCredential,
11
- } from './coValue.js';
12
- import { Team, expectTeamContent } from './permissions.js';
13
- import { SyncManager } from './sync.js';
14
- import { AgentID, RawCoValueID, SessionID } from './ids.js';
15
- import { CoValueID, ContentType } from './contentType.js';
5
+ getAgentSealerID,
6
+ getAgentSealerSecret,
7
+ newRandomAgentSecret,
8
+ newRandomKeySecret,
9
+ seal,
10
+ } from "./crypto.js";
11
+ import { CoValue, CoValueHeader, newRandomSessionID } from "./coValue.js";
12
+ import { Team, TeamContent, expectTeamContent } from "./permissions.js";
13
+ import { Peer, SyncManager } from "./sync.js";
14
+ import { AgentID, RawCoID, SessionID, isAgentID } from "./ids.js";
15
+ import { CoID, ContentType } from "./contentType.js";
16
+ import {
17
+ Account,
18
+ AccountMeta,
19
+ AccountIDOrAgentID,
20
+ accountHeaderForInitialAgentSecret,
21
+ GeneralizedControlledAccount,
22
+ ControlledAccount,
23
+ AnonymousControlledAccount,
24
+ AccountID,
25
+ Profile,
26
+ AccountContent,
27
+ ProfileContent,
28
+ ProfileMeta,
29
+ } from "./account.js";
30
+ import { CoMap } from "./index.js";
16
31
 
17
32
  export class LocalNode {
18
- coValues: { [key: RawCoValueID]: CoValueState } = {};
19
- agentCredential: AgentCredential;
20
- agentID: AgentID;
33
+ coValues: { [key: RawCoID]: CoValueState } = {};
34
+ account: GeneralizedControlledAccount;
21
35
  ownSessionID: SessionID;
22
36
  sync = new SyncManager(this);
23
37
 
24
- constructor(agentCredential: AgentCredential, ownSessionID: SessionID) {
25
- this.agentCredential = agentCredential;
26
- const agent = getAgent(agentCredential);
27
- const agentID = getAgentID(agent);
28
- this.agentID = agentID;
38
+ constructor(
39
+ account: GeneralizedControlledAccount,
40
+ ownSessionID: SessionID
41
+ ) {
42
+ this.account = account;
29
43
  this.ownSessionID = ownSessionID;
44
+ }
45
+
46
+ static withNewlyCreatedAccount(name: string): {
47
+ node: LocalNode;
48
+ accountID: AccountID;
49
+ accountSecret: AgentSecret;
50
+ sessionID: SessionID;
51
+ } {
52
+ const throwawayAgent = newRandomAgentSecret();
53
+ const setupNode = new LocalNode(
54
+ new AnonymousControlledAccount(throwawayAgent),
55
+ newRandomSessionID(getAgentID(throwawayAgent))
56
+ );
57
+
58
+ const account = setupNode.createAccount(name);
59
+
60
+ const nodeWithAccount = account.node.testWithDifferentAccount(
61
+ account,
62
+ newRandomSessionID(account.id)
63
+ );
30
64
 
31
- const agentCoValue = new CoValue(getAgentCoValueHeader(agent), this);
32
- this.coValues[agentCoValue.id] = {
33
- state: "loaded",
34
- coValue: agentCoValue,
65
+ return {
66
+ node: nodeWithAccount,
67
+ accountID: account.id,
68
+ accountSecret: account.agentSecret,
69
+ sessionID: nodeWithAccount.ownSessionID,
35
70
  };
36
71
  }
37
72
 
73
+ static async withLoadedAccount(accountID: AccountID, accountSecret: AgentSecret, sessionID: SessionID, peersToLoadFrom: Peer[]): Promise<LocalNode> {
74
+ const loadingNode = new LocalNode(new AnonymousControlledAccount(accountSecret), newRandomSessionID(accountID));
75
+
76
+ const accountPromise = loadingNode.load(accountID);
77
+
78
+ for (const peer of peersToLoadFrom) {
79
+ loadingNode.sync.addPeer(peer);
80
+ }
81
+
82
+ const account = await accountPromise;
83
+
84
+ // since this is all synchronous, we can just swap out nodes for the SyncManager
85
+ const node = loadingNode.testWithDifferentAccount(new ControlledAccount(accountSecret, account, loadingNode), sessionID);
86
+ node.sync = loadingNode.sync;
87
+ node.sync.local = node;
88
+
89
+ return node;
90
+ }
91
+
38
92
  createCoValue(header: CoValueHeader): CoValue {
39
93
  const coValue = new CoValue(header, this);
40
94
  this.coValues[coValue.id] = { state: "loaded", coValue: coValue };
@@ -44,7 +98,7 @@ export class LocalNode {
44
98
  return coValue;
45
99
  }
46
100
 
47
- loadCoValue(id: RawCoValueID): Promise<CoValue> {
101
+ loadCoValue(id: RawCoID): Promise<CoValue> {
48
102
  let entry = this.coValues[id];
49
103
  if (!entry) {
50
104
  entry = newLoadingState();
@@ -59,11 +113,21 @@ export class LocalNode {
59
113
  return entry.done;
60
114
  }
61
115
 
62
- async load<T extends ContentType>(id: CoValueID<T>): Promise<T> {
116
+ async load<T extends ContentType>(id: CoID<T>): Promise<T> {
63
117
  return (await this.loadCoValue(id)).getCurrentContent() as T;
64
118
  }
65
119
 
66
- expectCoValueLoaded(id: RawCoValueID, expectation?: string): CoValue {
120
+ async loadProfile(id: AccountID): Promise<Profile> {
121
+ const account = await this.load<CoMap<AccountContent>>(id);
122
+ const profileID = account.get("profile");
123
+
124
+ if (!profileID) {
125
+ throw new Error(`Account ${id} has no profile`);
126
+ }
127
+ return (await this.loadCoValue(profileID)).getCurrentContent() as Profile;
128
+ }
129
+
130
+ expectCoValueLoaded(id: RawCoID, expectation?: string): CoValue {
67
131
  const entry = this.coValues[id];
68
132
  if (!entry) {
69
133
  throw new Error(
@@ -80,75 +144,139 @@ export class LocalNode {
80
144
  return entry.coValue;
81
145
  }
82
146
 
83
- createAgent(publicNickname: string): AgentCredential {
84
- const agentCredential = newRandomAgentCredential(publicNickname);
147
+ expectProfileLoaded(id: AccountID, expectation?: string): Profile {
148
+ const account = this.expectCoValueLoaded(id, expectation);
149
+ const profileID = expectTeamContent(account.getCurrentContent()).get("profile");
150
+ if (!profileID) {
151
+ throw new Error(
152
+ `${
153
+ expectation ? expectation + ": " : ""
154
+ }Account ${id} has no profile`
155
+ );
156
+ }
157
+ return this.expectCoValueLoaded(profileID, expectation).getCurrentContent() as Profile;
158
+ }
85
159
 
86
- this.createCoValue(getAgentCoValueHeader(getAgent(agentCredential)));
160
+ createAccount(name: string): ControlledAccount {
161
+ const agentSecret = newRandomAgentSecret();
87
162
 
88
- return agentCredential;
89
- }
163
+ const account = this.createCoValue(
164
+ accountHeaderForInitialAgentSecret(agentSecret)
165
+ ).testWithDifferentAccount(
166
+ new AnonymousControlledAccount(agentSecret),
167
+ newRandomSessionID(getAgentID(agentSecret))
168
+ );
169
+
170
+ const accountAsTeam = new Team(expectTeamContent(account.getCurrentContent()), account.node);
171
+
172
+ accountAsTeam.teamMap.edit((editable) => {
173
+ editable.set(getAgentID(agentSecret), "admin", "trusting");
174
+
175
+ const readKey = newRandomKeySecret();
176
+
177
+ editable.set(
178
+ `${readKey.id}_for_${getAgentID(agentSecret)}`,
179
+ seal(
180
+ readKey.secret,
181
+ getAgentSealerSecret(agentSecret),
182
+ getAgentSealerID(getAgentID(agentSecret)),
183
+ {
184
+ in: account.id,
185
+ tx: account.nextTransactionID(),
186
+ }
187
+ ),
188
+ "trusting"
189
+ );
90
190
 
91
- expectAgentLoaded(id: AgentID, expectation?: string): Agent {
92
- const coValue = this.expectCoValueLoaded(
93
- id,
94
- expectation
191
+ editable.set("readKey", readKey.id, "trusting");
192
+ });
193
+
194
+ const controlledAccount = new ControlledAccount(
195
+ agentSecret,
196
+ account.getCurrentContent() as CoMap<AccountContent, AccountMeta>,
197
+ account.node
95
198
  );
96
199
 
97
- if (coValue.header.type !== "comap" || coValue.header.ruleset.type !== "agent") {
200
+ const profile = accountAsTeam.createMap<ProfileContent, ProfileMeta>({ type: "profile" });
201
+
202
+ profile.edit((editable) => {
203
+ editable.set("name", name, "trusting");
204
+ });
205
+
206
+ accountAsTeam.teamMap.edit((editable) => {
207
+ editable.set("profile", profile.id, "trusting");
208
+ });
209
+
210
+ return controlledAccount;
211
+ }
212
+
213
+ resolveAccountAgent(id: AccountIDOrAgentID, expectation?: string): AgentID {
214
+ if (isAgentID(id)) {
215
+ return id;
216
+ }
217
+
218
+ const coValue = this.expectCoValueLoaded(id, expectation);
219
+
220
+ if (
221
+ coValue.header.type !== "comap" ||
222
+ coValue.header.ruleset.type !== "team" ||
223
+ !coValue.header.meta ||
224
+ !("type" in coValue.header.meta) ||
225
+ coValue.header.meta.type !== "account"
226
+ ) {
98
227
  throw new Error(
99
228
  `${
100
229
  expectation ? expectation + ": " : ""
101
- }CoValue ${id} is not an agent`
230
+ }CoValue ${id} is not an account`
102
231
  );
103
232
  }
104
233
 
105
- return {
106
- recipientID: coValue.header.ruleset.initialRecipientID,
107
- signatoryID: coValue.header.ruleset.initialSignatoryID,
108
- publicNickname: coValue.header.publicNickname?.replace("agent-", ""),
109
- }
234
+ return new Account(
235
+ coValue.getCurrentContent() as CoMap<TeamContent, AccountMeta>,
236
+ this
237
+ ).getCurrentAgentID();
110
238
  }
111
239
 
112
240
  createTeam(): Team {
113
241
  const teamCoValue = this.createCoValue({
114
242
  type: "comap",
115
- ruleset: { type: "team", initialAdmin: this.agentID },
243
+ ruleset: { type: "team", initialAdmin: this.account.id },
116
244
  meta: null,
117
245
  ...createdNowUnique(),
118
- publicNickname: "team",
119
246
  });
120
247
 
121
248
  let teamContent = expectTeamContent(teamCoValue.getCurrentContent());
122
249
 
123
250
  teamContent = teamContent.edit((editable) => {
124
- editable.set(this.agentID, "admin", "trusting");
251
+ editable.set(this.account.id, "admin", "trusting");
125
252
 
126
253
  const readKey = newRandomKeySecret();
127
- const revelation = seal(
128
- readKey.secret,
129
- this.agentCredential.recipientSecret,
130
- new Set([getAgent(this.agentCredential).recipientID]),
131
- {
132
- in: teamCoValue.id,
133
- tx: teamCoValue.nextTransactionID(),
134
- }
135
- );
136
254
 
137
255
  editable.set(
138
- "readKey",
139
- { keyID: readKey.id, revelation },
256
+ `${readKey.id}_for_${this.account.id}`,
257
+ seal(
258
+ readKey.secret,
259
+ this.account.currentSealerSecret(),
260
+ this.account.currentSealerID(),
261
+ {
262
+ in: teamCoValue.id,
263
+ tx: teamCoValue.nextTransactionID(),
264
+ }
265
+ ),
140
266
  "trusting"
141
267
  );
268
+
269
+ editable.set("readKey", readKey.id, "trusting");
142
270
  });
143
271
 
144
272
  return new Team(teamContent, this);
145
273
  }
146
274
 
147
- testWithDifferentCredentials(
148
- agentCredential: AgentCredential,
275
+ testWithDifferentAccount(
276
+ account: GeneralizedControlledAccount,
149
277
  ownSessionID: SessionID
150
278
  ): LocalNode {
151
- const newNode = new LocalNode(agentCredential, ownSessionID);
279
+ const newNode = new LocalNode(account, ownSessionID);
152
280
 
153
281
  newNode.coValues = Object.fromEntries(
154
282
  Object.entries(this.coValues)