cojson 0.2.2 → 0.2.3

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 (58) hide show
  1. package/.eslintrc.cjs +1 -0
  2. package/dist/account.d.ts +7 -7
  3. package/dist/account.js +2 -2
  4. package/dist/account.js.map +1 -1
  5. package/dist/coValue.d.ts +26 -23
  6. package/dist/coValue.js +14 -0
  7. package/dist/coValue.js.map +1 -1
  8. package/dist/coValueCore.d.ts +6 -6
  9. package/dist/coValueCore.js.map +1 -1
  10. package/dist/coValues/coList.d.ts +22 -22
  11. package/dist/coValues/coList.js +3 -2
  12. package/dist/coValues/coList.js.map +1 -1
  13. package/dist/coValues/coMap.d.ts +29 -35
  14. package/dist/coValues/coMap.js +17 -8
  15. package/dist/coValues/coMap.js.map +1 -1
  16. package/dist/coValues/coStream.d.ts +28 -27
  17. package/dist/coValues/coStream.js +15 -6
  18. package/dist/coValues/coStream.js.map +1 -1
  19. package/dist/coValues/static.d.ts +4 -4
  20. package/dist/coValues/static.js.map +1 -1
  21. package/dist/group.d.ts +8 -6
  22. package/dist/group.js +20 -4
  23. package/dist/group.js.map +1 -1
  24. package/dist/index.d.ts +7 -6
  25. package/dist/index.js.map +1 -1
  26. package/dist/media.d.ts +1 -2
  27. package/dist/node.d.ts +7 -3
  28. package/dist/node.js +37 -7
  29. package/dist/node.js.map +1 -1
  30. package/dist/queries.d.ts +114 -0
  31. package/dist/queries.js +260 -0
  32. package/dist/queries.js.map +1 -0
  33. package/dist/{testUtils.d.ts → tests/testUtils.d.ts} +9 -9
  34. package/dist/{testUtils.js → tests/testUtils.js} +7 -5
  35. package/dist/tests/testUtils.js.map +1 -0
  36. package/package.json +2 -2
  37. package/src/account.ts +5 -5
  38. package/src/coValue.ts +54 -28
  39. package/src/coValueCore.ts +6 -6
  40. package/src/coValues/coList.ts +73 -37
  41. package/src/coValues/coMap.ts +134 -68
  42. package/src/coValues/coStream.ts +86 -55
  43. package/src/coValues/static.ts +5 -5
  44. package/src/group.ts +50 -11
  45. package/src/index.ts +10 -8
  46. package/src/media.ts +1 -2
  47. package/src/node.ts +70 -19
  48. package/src/queries.ts +519 -0
  49. package/src/{account.test.ts → tests/account.test.ts} +4 -4
  50. package/src/{coValue.test.ts → tests/coValue.test.ts} +5 -5
  51. package/src/{coValueCore.test.ts → tests/coValueCore.test.ts} +7 -7
  52. package/src/{crypto.test.ts → tests/crypto.test.ts} +3 -3
  53. package/src/{group.test.ts → tests/group.test.ts} +2 -2
  54. package/src/{permissions.test.ts → tests/permissions.test.ts} +5 -5
  55. package/src/tests/queries.test.ts +301 -0
  56. package/src/{sync.test.ts → tests/sync.test.ts} +10 -10
  57. package/src/{testUtils.ts → tests/testUtils.ts} +8 -6
  58. package/dist/testUtils.js.map +0 -1
package/src/group.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CoID, CoValueImpl } from "./coValue.js";
1
+ import { CoID, CoValue, AnyCoValue } from "./coValue.js";
2
2
  import { CoMap } from "./coValues/coMap.js";
3
3
  import { JsonObject, JsonValue } from "./jsonValue.js";
4
4
  import {
@@ -21,7 +21,11 @@ import { AccountID, GeneralizedControlledAccount, Profile } from "./account.js";
21
21
  import { Role } from "./permissions.js";
22
22
  import { base58 } from "@scure/base";
23
23
  import { CoList } from "./coValues/coList.js";
24
- import { BinaryCoStream, BinaryCoStreamMeta, CoStream } from "./coValues/coStream.js";
24
+ import {
25
+ BinaryCoStream,
26
+ BinaryCoStreamMeta,
27
+ CoStream,
28
+ } from "./coValues/coStream.js";
25
29
 
26
30
  export type GroupContent = {
27
31
  profile: CoID<Profile> | null;
@@ -35,7 +39,7 @@ export type GroupContent = {
35
39
  };
36
40
 
37
41
  export function expectGroupContent(
38
- content: CoValueImpl
42
+ content: CoValue
39
43
  ): CoMap<GroupContent, JsonObject | null> {
40
44
  if (content.type !== "comap") {
41
45
  throw new Error("Expected map");
@@ -238,10 +242,22 @@ export class Group {
238
242
 
239
243
  /** Creates a new `CoMap` within this group, with the specified specialized
240
244
  * `CoMap` type `M` and optional static metadata. */
241
- createMap<M extends CoMap<{ [key: string]: JsonValue | undefined; }, JsonObject | null>>(
245
+ createMap<
246
+ M extends CoMap<
247
+ { [key: string]: JsonValue | AnyCoValue | undefined },
248
+ JsonObject | null
249
+ >
250
+ >(
251
+ init?: M extends CoMap<infer M, infer _Meta>
252
+ ? {
253
+ [K in keyof M]: M[K] extends AnyCoValue
254
+ ? M[K] | CoID<M[K]>
255
+ : M[K];
256
+ }
257
+ : never,
242
258
  meta?: M["meta"]
243
259
  ): M {
244
- return this.node
260
+ let map = this.node
245
261
  .createCoValue({
246
262
  type: "comap",
247
263
  ruleset: {
@@ -252,14 +268,27 @@ export class Group {
252
268
  ...createdNowUnique(),
253
269
  })
254
270
  .getCurrentContent() as M;
271
+
272
+ if (init) {
273
+ map = map.edit((editable) => {
274
+ for (const [key, value] of Object.entries(init)) {
275
+ editable.set(key, value);
276
+ }
277
+ });
278
+ }
279
+
280
+ return map;
255
281
  }
256
282
 
257
283
  /** Creates a new `CoList` within this group, with the specified specialized
258
284
  * `CoList` type `L` and optional static metadata. */
259
- createList<L extends CoList<JsonValue, JsonObject | null>>(
285
+ createList<L extends CoList<JsonValue | CoValue, JsonObject | null>>(
286
+ init?: L extends CoList<infer I, infer _Meta>
287
+ ? (I extends CoValue ? CoID<I> | I : I)[]
288
+ : never,
260
289
  meta?: L["meta"]
261
290
  ): L {
262
- return this.node
291
+ let list = this.node
263
292
  .createCoValue({
264
293
  type: "colist",
265
294
  ruleset: {
@@ -270,9 +299,19 @@ export class Group {
270
299
  ...createdNowUnique(),
271
300
  })
272
301
  .getCurrentContent() as L;
302
+
303
+ if (init) {
304
+ list = list.edit((editable) => {
305
+ for (const item of init) {
306
+ editable.push(item);
307
+ }
308
+ });
309
+ }
310
+
311
+ return list;
273
312
  }
274
313
 
275
- createStream<C extends CoStream<JsonValue, JsonObject | null>>(
314
+ createStream<C extends CoStream<JsonValue | CoValue, JsonObject | null>>(
276
315
  meta?: C["meta"]
277
316
  ): C {
278
317
  return this.node
@@ -288,9 +327,9 @@ export class Group {
288
327
  .getCurrentContent() as C;
289
328
  }
290
329
 
291
- createBinaryStream<
292
- C extends BinaryCoStream<BinaryCoStreamMeta>
293
- >(meta: C["meta"] = { type: "binary" }): C {
330
+ createBinaryStream<C extends BinaryCoStream<BinaryCoStreamMeta>>(
331
+ meta: C["meta"] = { type: "binary" }
332
+ ): C {
294
333
  return this.node
295
334
  .createCoValue({
296
335
  type: "costream",
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { CoValueCore, newRandomSessionID, MAX_RECOMMENDED_TX_SIZE } from "./coValueCore.js";
2
2
  import { LocalNode } from "./node.js";
3
- import type { CoValue, ReadableCoValue } from "./coValue.js";
3
+ import type { CoValue } from "./coValue.js";
4
4
  import { CoMap, WriteableCoMap } from "./coValues/coMap.js";
5
5
  import { CoList, WriteableCoList } from "./coValues/coList.js";
6
6
  import {
@@ -28,16 +28,17 @@ import { base64URLtoBytes, bytesToBase64url } from "./base64url.js";
28
28
  import { parseJSON } from "./jsonStringify.js";
29
29
 
30
30
  import type { SessionID, AgentID } from "./ids.js";
31
- import type { CoID, CoValueImpl } from "./coValue.js";
32
- import type { BinaryChunkInfo, BinaryCoStreamMeta } from "./coValues/coStream.js";
31
+ import type { CoID, AnyCoValue } from "./coValue.js";
32
+ import type { Queried } from "./queries.js";
33
+ import type { BinaryStreamInfo, BinaryCoStreamMeta } from "./coValues/coStream.js";
33
34
  import type { JsonValue } from "./jsonValue.js";
34
35
  import type { SyncMessage, Peer } from "./sync.js";
35
36
  import type { AgentSecret } from "./crypto.js";
36
- import type { AccountID, Profile } from "./account.js";
37
+ import type { AccountID, Account, Profile } from "./account.js";
37
38
  import type { InviteSecret } from "./group.js";
38
39
  import type * as Media from "./media.js";
39
40
 
40
- type Value = JsonValue | CoValueImpl;
41
+ type Value = JsonValue | AnyCoValue;
41
42
 
42
43
  /** @hidden */
43
44
  export const cojsonInternals = {
@@ -81,14 +82,15 @@ export type {
81
82
  Value,
82
83
  JsonValue,
83
84
  CoValue,
84
- ReadableCoValue,
85
- CoValueImpl,
85
+ AnyCoValue,
86
86
  CoID,
87
+ Queried,
87
88
  AccountID,
89
+ Account,
88
90
  Profile,
89
91
  SessionID,
90
92
  Peer,
91
- BinaryChunkInfo,
93
+ BinaryStreamInfo,
92
94
  BinaryCoStreamMeta,
93
95
  AgentID,
94
96
  AgentSecret,
package/src/media.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  import { CoMap } from './coValues/coMap.js'
2
- import { CoID } from './coValue.js'
3
2
  import { BinaryCoStream } from './coValues/coStream.js'
4
3
 
5
4
  export type ImageDefinition = CoMap<{
6
5
  originalSize: [number, number];
7
6
  placeholderDataURL?: string;
8
- [res: `${number}x${number}`]: CoID<BinaryCoStream>;
7
+ [res: `${number}x${number}`]: BinaryCoStream;
9
8
  }>;
package/src/node.ts CHANGED
@@ -9,7 +9,11 @@ import {
9
9
  newRandomKeySecret,
10
10
  seal,
11
11
  } from "./crypto.js";
12
- import { CoValueCore, CoValueHeader, newRandomSessionID } from "./coValueCore.js";
12
+ import {
13
+ CoValueCore,
14
+ CoValueHeader,
15
+ newRandomSessionID,
16
+ } from "./coValueCore.js";
13
17
  import {
14
18
  InviteSecret,
15
19
  Group,
@@ -19,9 +23,10 @@ import {
19
23
  } from "./group.js";
20
24
  import { Peer, SyncManager } from "./sync.js";
21
25
  import { AgentID, RawCoID, SessionID, isAgentID } from "./ids.js";
22
- import { CoID, CoValueImpl } from "./coValue.js";
26
+ import { CoID } from "./coValue.js";
27
+ import { Queried, query } from "./queries.js";
23
28
  import {
24
- Account,
29
+ AccountGroup,
25
30
  AccountMeta,
26
31
  accountHeaderForInitialAgentSecret,
27
32
  GeneralizedControlledAccount,
@@ -30,9 +35,10 @@ import {
30
35
  AccountID,
31
36
  Profile,
32
37
  AccountContent,
33
- AccountMap,
38
+ Account,
34
39
  } from "./account.js";
35
40
  import { CoMap } from "./coValues/coMap.js";
41
+ import { CoValue } from "./index.js";
36
42
 
37
43
  /** A `LocalNode` represents a local view of a set of loaded `CoValue`s, from the perspective of a particular account (or primitive cryptographic agent).
38
44
 
@@ -152,16 +158,45 @@ export class LocalNode {
152
158
  * promise once a first version has been loaded. See `coValue.subscribe()` and `node.useTelepathicData()`
153
159
  * for listening to subsequent updates to the CoValue.
154
160
  */
155
- async load<T extends CoValueImpl>(id: CoID<T>): Promise<T> {
161
+ async load<T extends CoValue>(id: CoID<T>): Promise<T> {
156
162
  return (await this.loadCoValue(id)).getCurrentContent() as T;
157
163
  }
158
164
 
165
+ subscribe<T extends CoValue>(id: CoID<T>, callback: (update: T) => void): () => void {
166
+ let stopped = false;
167
+ let unsubscribe!: () => void;
168
+
169
+ console.log("Subscribing to " + id);
170
+
171
+ this.load(id).then((coValue) => {
172
+ if (stopped) {
173
+ return;
174
+ }
175
+ unsubscribe = coValue.subscribe(callback);
176
+ }).catch((e) => {
177
+ console.error("Error subscribing to ", id, e);
178
+ });
179
+
180
+ return () => {
181
+ console.log("Unsubscribing from " + id);
182
+ stopped = true;
183
+ unsubscribe?.();
184
+ }
185
+ }
186
+
187
+ query<T extends CoValue>(
188
+ id: CoID<T>,
189
+ callback: (update: Queried<T> | undefined) => void
190
+ ): () => void {
191
+ return query(id, this, callback);
192
+ }
193
+
159
194
  /**
160
195
  * Loads a profile associated with an account. `Profile` is at least a `CoMap<{string: name}>`,
161
196
  * but might contain other, app-specific properties.
162
197
  */
163
198
  async loadProfile(id: AccountID): Promise<Profile> {
164
- const account = await this.load<AccountMap>(id);
199
+ const account = await this.load<Account>(id);
165
200
  const profileID = account.get("profile");
166
201
 
167
202
  if (!profileID) {
@@ -172,7 +207,7 @@ export class LocalNode {
172
207
  ).getCurrentContent() as Profile;
173
208
  }
174
209
 
175
- async acceptInvite<T extends CoValueImpl>(
210
+ async acceptInvite<T extends CoValue>(
176
211
  groupOrOwnedValueID: CoID<T>,
177
212
  inviteSecret: InviteSecret
178
213
  ): Promise<void> {
@@ -204,10 +239,7 @@ export class LocalNode {
204
239
  }
205
240
  });
206
241
  setTimeout(
207
- () =>
208
- reject(
209
- new Error("Couldn't find invite before timeout")
210
- ),
242
+ () => reject(new Error("Couldn't find invite before timeout")),
211
243
  2000
212
244
  );
213
245
  });
@@ -224,7 +256,9 @@ export class LocalNode {
224
256
  (existingRole === "writer" && inviteRole === "reader") ||
225
257
  (existingRole === "reader" && inviteRole === "readerInvite")
226
258
  ) {
227
- console.debug("Not accepting invite that would replace or downgrade role");
259
+ console.debug(
260
+ "Not accepting invite that would replace or downgrade role"
261
+ );
228
262
  return;
229
263
  }
230
264
 
@@ -242,7 +276,8 @@ export class LocalNode {
242
276
  : "reader"
243
277
  );
244
278
 
245
- group.underlyingMap.core._sessions = groupAsInvite.underlyingMap.core.sessions;
279
+ group.underlyingMap.core._sessions =
280
+ groupAsInvite.underlyingMap.core.sessions;
246
281
  group.underlyingMap.core._cachedContent = undefined;
247
282
 
248
283
  for (const groupListener of group.underlyingMap.core.listeners) {
@@ -332,11 +367,11 @@ export class LocalNode {
332
367
  account.node
333
368
  );
334
369
 
335
- const profile = accountAsGroup.createMap<Profile>({
370
+ let profile = accountAsGroup.createMap<Profile>(undefined, {
336
371
  type: "profile",
337
372
  });
338
373
 
339
- profile.edit((editable) => {
374
+ profile = profile.edit((editable) => {
340
375
  editable.set("name", name, "trusting");
341
376
  });
342
377
 
@@ -346,14 +381,26 @@ export class LocalNode {
346
381
 
347
382
  const accountOnThisNode = this.expectCoValueLoaded(account.id);
348
383
 
349
- accountOnThisNode._sessions = {...accountAsGroup.underlyingMap.core.sessions};
384
+ accountOnThisNode._sessions = {
385
+ ...accountAsGroup.underlyingMap.core.sessions,
386
+ };
350
387
  accountOnThisNode._cachedContent = undefined;
351
388
 
389
+ const profileOnThisNode = this.createCoValue(profile.core.header);
390
+
391
+ profileOnThisNode._sessions = {
392
+ ...profile.core.sessions,
393
+ };
394
+ profileOnThisNode._cachedContent = undefined;
395
+
352
396
  return controlledAccount;
353
397
  }
354
398
 
355
399
  /** @internal */
356
- resolveAccountAgent(id: AccountID | AgentID, expectation?: string): AgentID {
400
+ resolveAccountAgent(
401
+ id: AccountID | AgentID,
402
+ expectation?: string
403
+ ): AgentID {
357
404
  if (isAgentID(id)) {
358
405
  return id;
359
406
  }
@@ -374,7 +421,7 @@ export class LocalNode {
374
421
  );
375
422
  }
376
423
 
377
- return new Account(
424
+ return new AccountGroup(
378
425
  coValue.getCurrentContent() as CoMap<GroupContent, AccountMeta>,
379
426
  this
380
427
  ).getCurrentAgentID();
@@ -443,7 +490,11 @@ export class LocalNode {
443
490
  continue;
444
491
  }
445
492
 
446
- const newCoValue = new CoValueCore(entry.coValue.header, newNode, {...entry.coValue.sessions});
493
+ const newCoValue = new CoValueCore(
494
+ entry.coValue.header,
495
+ newNode,
496
+ { ...entry.coValue.sessions }
497
+ );
447
498
 
448
499
  newNode.coValues[coValueID as RawCoID] = {
449
500
  state: "loaded",