cojson 0.2.1 → 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 (61) 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/sync.js +3 -1
  34. package/dist/sync.js.map +1 -1
  35. package/dist/{testUtils.d.ts → tests/testUtils.d.ts} +9 -9
  36. package/dist/{testUtils.js → tests/testUtils.js} +7 -5
  37. package/dist/tests/testUtils.js.map +1 -0
  38. package/package.json +2 -2
  39. package/src/account.ts +5 -5
  40. package/src/coValue.ts +54 -28
  41. package/src/coValueCore.ts +6 -6
  42. package/src/coValues/coList.ts +73 -37
  43. package/src/coValues/coMap.ts +134 -68
  44. package/src/coValues/coStream.ts +86 -55
  45. package/src/coValues/static.ts +5 -5
  46. package/src/group.ts +50 -11
  47. package/src/index.ts +10 -8
  48. package/src/media.ts +1 -2
  49. package/src/node.ts +70 -19
  50. package/src/queries.ts +519 -0
  51. package/src/sync.ts +10 -2
  52. package/src/{account.test.ts → tests/account.test.ts} +4 -4
  53. package/src/{coValue.test.ts → tests/coValue.test.ts} +5 -5
  54. package/src/{coValueCore.test.ts → tests/coValueCore.test.ts} +7 -7
  55. package/src/{crypto.test.ts → tests/crypto.test.ts} +3 -3
  56. package/src/{group.test.ts → tests/group.test.ts} +2 -2
  57. package/src/{permissions.test.ts → tests/permissions.test.ts} +5 -5
  58. package/src/tests/queries.test.ts +301 -0
  59. package/src/{sync.test.ts → tests/sync.test.ts} +10 -10
  60. package/src/{testUtils.ts → tests/testUtils.ts} +8 -6
  61. package/dist/testUtils.js.map +0 -1
package/src/coValue.ts CHANGED
@@ -1,17 +1,32 @@
1
1
  import { JsonObject, JsonValue } from "./jsonValue.js";
2
2
  import { RawCoID } from "./ids.js";
3
3
  import { CoMap } from "./coValues/coMap.js";
4
- import { BinaryCoStream, BinaryCoStreamMeta, CoStream } from "./coValues/coStream.js";
4
+ import {
5
+ BinaryCoStream,
6
+ BinaryCoStreamMeta,
7
+ CoStream,
8
+ } from "./coValues/coStream.js";
5
9
  import { Static } from "./coValues/static.js";
6
10
  import { CoList } from "./coValues/coList.js";
7
11
  import { CoValueCore } from "./coValueCore.js";
8
12
  import { Group } from "./group.js";
9
13
 
10
- export type CoID<T extends CoValueImpl> = RawCoID & {
14
+ export type CoID<T extends CoValue> = RawCoID & {
11
15
  readonly __type: T;
12
16
  };
13
17
 
14
- export interface ReadableCoValue extends CoValue {
18
+ export interface CoValue {
19
+ /** The `CoValue`'s (precisely typed) `CoID` */
20
+ id: CoID<this>;
21
+ core: CoValueCore;
22
+ /** Specifies which kind of `CoValue` this is */
23
+ type: string;
24
+ /** The `CoValue`'s (precisely typed) static metadata */
25
+ meta: JsonObject | null;
26
+ /** The `Group` this `CoValue` belongs to (determining permissions) */
27
+ group: Group;
28
+ /** Returns an immutable JSON presentation of this `CoValue` */
29
+ toJSON(): JsonValue;
15
30
  /** Lets you subscribe to future updates to this CoValue (whether made locally or by other users).
16
31
  *
17
32
  * Takes a listener function that will be called with the current state for each update.
@@ -19,42 +34,37 @@ export interface ReadableCoValue extends CoValue {
19
34
  * Returns an unsubscribe function.
20
35
  *
21
36
  * Used internally by `useTelepathicData()` for reactive updates on changes to a `CoValue`. */
22
- subscribe(listener: (coValue: CoValueImpl) => void): () => void;
37
+ subscribe(listener: (coValue: this) => void): () => void;
23
38
  /** Lets you apply edits to a `CoValue`, inside the changer callback, which receives a `WriteableCoValue`.
24
39
  *
25
40
  * A `WritableCoValue` has all the same methods as a `CoValue`, but all edits made to it (with its additional mutator methods)
26
41
  * are reflected in it immediately - so it behaves mutably, whereas a `CoValue` is always immutable
27
42
  * (you need to use `subscribe` to receive new versions of it). */
28
- edit?:
29
- | ((changer: (editable: WriteableCoValue) => void) => CoValueImpl)
30
- | undefined;
43
+ edit?: ((changer: (editable: CoValue) => void) => this) | undefined;
31
44
  }
32
45
 
33
- export interface CoValue {
34
- /** The `CoValue`'s (precisely typed) `CoID` */
35
- id: CoID<CoValueImpl>;
36
- core: CoValueCore;
37
- /** Specifies which kind of `CoValue` this is */
38
- type: CoValueImpl["type"];
39
- /** The `CoValue`'s (precisely typed) static metadata */
40
- meta: JsonObject | null;
41
- /** The `Group` this `CoValue` belongs to (determining permissions) */
42
- group: Group;
43
- /** Returns an immutable JSON presentation of this `CoValue` */
44
- toJSON(): JsonValue;
45
- }
46
+ export type AnyCoMap = CoMap<
47
+ { [key: string]: JsonValue | CoValue | undefined },
48
+ JsonObject | null
49
+ >;
46
50
 
47
- export interface WriteableCoValue extends CoValue {}
51
+ export type AnyCoList = CoList<JsonValue | CoValue, JsonObject | null>;
48
52
 
49
- export type CoValueImpl =
50
- | CoMap<{ [key: string]: JsonValue | undefined; }, JsonObject | null>
51
- | CoList<JsonValue, JsonObject | null>
52
- | CoStream<JsonValue, JsonObject | null>
53
- | BinaryCoStream<BinaryCoStreamMeta>
54
- | Static<JsonObject>;
53
+ export type AnyCoStream = CoStream<JsonValue | CoValue, JsonObject | null>;
54
+
55
+ export type AnyBinaryCoStream = BinaryCoStream<BinaryCoStreamMeta>;
56
+
57
+ export type AnyStatic = Static<JsonObject>;
58
+
59
+ export type AnyCoValue =
60
+ | AnyCoMap
61
+ | AnyCoList
62
+ | AnyCoStream
63
+ | AnyBinaryCoStream
64
+ | AnyStatic;
55
65
 
56
66
  export function expectMap(
57
- content: CoValueImpl
67
+ content: CoValue
58
68
  ): CoMap<{ [key: string]: string }, JsonObject | null> {
59
69
  if (content.type !== "comap") {
60
70
  throw new Error("Expected map");
@@ -62,3 +72,19 @@ export function expectMap(
62
72
 
63
73
  return content as CoMap<{ [key: string]: string }, JsonObject | null>;
64
74
  }
75
+
76
+ export function isCoValueImpl(
77
+ value: JsonValue | AnyCoValue | undefined
78
+ ): value is AnyCoValue {
79
+ return (
80
+ value instanceof CoMap ||
81
+ value instanceof CoList ||
82
+ value instanceof CoStream ||
83
+ value instanceof BinaryCoStream ||
84
+ value instanceof Static
85
+ );
86
+ }
87
+
88
+ export function isCoValue(value: JsonValue | CoValue | undefined) : value is CoValue {
89
+ return isCoValueImpl(value as AnyCoValue);
90
+ }
@@ -1,5 +1,5 @@
1
1
  import { randomBytes } from "@noble/hashes/utils";
2
- import { CoValueImpl } from "./coValue.js";
2
+ import { AnyCoValue } from "./coValue.js";
3
3
  import { Static } from "./coValues/static.js";
4
4
  import { BinaryCoStream, CoStream } from "./coValues/coStream.js";
5
5
  import { CoMap } from "./coValues/coMap.js";
@@ -38,7 +38,7 @@ import { Stringified, stableStringify } from "./jsonStringify.js";
38
38
  export const MAX_RECOMMENDED_TX_SIZE = 100 * 1024;
39
39
 
40
40
  export type CoValueHeader = {
41
- type: CoValueImpl["type"];
41
+ type: AnyCoValue["type"];
42
42
  ruleset: RulesetDef;
43
43
  meta: JsonObject | null;
44
44
  createdAt: `2${string}` | null;
@@ -99,8 +99,8 @@ export class CoValueCore {
99
99
  node: LocalNode;
100
100
  header: CoValueHeader;
101
101
  _sessions: { [key: SessionID]: SessionLog };
102
- _cachedContent?: CoValueImpl;
103
- listeners: Set<(content?: CoValueImpl) => void> = new Set();
102
+ _cachedContent?: AnyCoValue;
103
+ listeners: Set<(content?: AnyCoValue) => void> = new Set();
104
104
  _decryptionCache: {
105
105
  [key: Encrypted<JsonValue[], JsonValue>]:
106
106
  | Stringified<JsonValue[]>
@@ -376,7 +376,7 @@ export class CoValueCore {
376
376
  }
377
377
  }
378
378
 
379
- subscribe(listener: (content?: CoValueImpl) => void): () => void {
379
+ subscribe(listener: (content?: AnyCoValue) => void): () => void {
380
380
  this.listeners.add(listener);
381
381
  listener(this.getCurrentContent());
382
382
 
@@ -493,7 +493,7 @@ export class CoValueCore {
493
493
  return success;
494
494
  }
495
495
 
496
- getCurrentContent(): CoValueImpl {
496
+ getCurrentContent(): AnyCoValue {
497
497
  if (this._cachedContent) {
498
498
  return this._cachedContent;
499
499
  }
@@ -1,5 +1,5 @@
1
1
  import { JsonObject, JsonValue } from "../jsonValue.js";
2
- import { CoID, ReadableCoValue, WriteableCoValue } from "../coValue.js";
2
+ import { CoID, CoValue, isCoValue } from "../coValue.js";
3
3
  import { CoValueCore, accountOrAgentIDfromSessionID } from "../coValueCore.js";
4
4
  import { SessionID, TransactionID } from "../ids.js";
5
5
  import { Group } from "../group.js";
@@ -8,15 +8,15 @@ import { parseJSON } from "../jsonStringify.js";
8
8
 
9
9
  type OpID = TransactionID & { changeIdx: number };
10
10
 
11
- type InsertionOpPayload<T extends JsonValue> =
11
+ type InsertionOpPayload<T extends JsonValue | CoValue> =
12
12
  | {
13
13
  op: "pre";
14
- value: T;
14
+ value: T extends CoValue ? CoID<T> : Exclude<T, CoValue>;
15
15
  before: OpID | "end";
16
16
  }
17
17
  | {
18
18
  op: "app";
19
- value: T;
19
+ value: T extends CoValue ? CoID<T> : Exclude<T, CoValue>;
20
20
  after: OpID | "start";
21
21
  };
22
22
 
@@ -25,11 +25,11 @@ type DeletionOpPayload = {
25
25
  insertion: OpID;
26
26
  };
27
27
 
28
- export type ListOpPayload<T extends JsonValue> =
28
+ export type ListOpPayload<T extends JsonValue | CoValue> =
29
29
  | InsertionOpPayload<T>
30
30
  | DeletionOpPayload;
31
31
 
32
- type InsertionEntry<T extends JsonValue> = {
32
+ type InsertionEntry<T extends JsonValue | CoValue> = {
33
33
  madeAt: number;
34
34
  predecessors: OpID[];
35
35
  successors: OpID[];
@@ -40,10 +40,12 @@ type DeletionEntry = {
40
40
  deletionID: OpID;
41
41
  } & DeletionOpPayload;
42
42
 
43
- export class CoList<T extends JsonValue, Meta extends JsonObject | null = null>
44
- implements ReadableCoValue
43
+ export class CoList<
44
+ T extends JsonValue | CoValue,
45
+ Meta extends JsonObject | null = null
46
+ > implements CoValue
45
47
  {
46
- id: CoID<CoList<T, Meta>>;
48
+ id: CoID<this>;
47
49
  type = "colist" as const;
48
50
  core: CoValueCore;
49
51
  /** @internal */
@@ -69,7 +71,7 @@ export class CoList<T extends JsonValue, Meta extends JsonObject | null = null>
69
71
 
70
72
  /** @internal */
71
73
  constructor(core: CoValueCore) {
72
- this.id = core.id as CoID<CoList<T, Meta>>;
74
+ this.id = core.id as CoID<this>;
73
75
  this.core = core;
74
76
  this.afterStart = [];
75
77
  this.beforeEnd = [];
@@ -99,7 +101,9 @@ export class CoList<T extends JsonValue, Meta extends JsonObject | null = null>
99
101
  changes,
100
102
  madeAt,
101
103
  } of this.core.getValidSortedTransactions()) {
102
- for (const [changeIdx, changeUntyped] of parseJSON(changes).entries()) {
104
+ for (const [changeIdx, changeUntyped] of parseJSON(
105
+ changes
106
+ ).entries()) {
103
107
  const change = changeUntyped as ListOpPayload<T>;
104
108
 
105
109
  if (change.op === "pre" || change.op === "app") {
@@ -201,7 +205,9 @@ export class CoList<T extends JsonValue, Meta extends JsonObject | null = null>
201
205
  }
202
206
 
203
207
  /** Get the item currently at `idx`. */
204
- get(idx: number): T | undefined {
208
+ get(
209
+ idx: number
210
+ ): (T extends CoValue ? CoID<T> : Exclude<T, CoValue>) | undefined {
205
211
  const entry = this.entries()[idx];
206
212
  if (!entry) {
207
213
  return undefined;
@@ -210,12 +216,20 @@ export class CoList<T extends JsonValue, Meta extends JsonObject | null = null>
210
216
  }
211
217
 
212
218
  /** Returns the current items in the CoList as an array. */
213
- asArray(): T[] {
219
+ asArray(): (T extends CoValue ? CoID<T> : Exclude<T, CoValue>)[] {
214
220
  return this.entries().map((entry) => entry.value);
215
221
  }
216
222
 
217
- entries(): { value: T; madeAt: number; opID: OpID }[] {
218
- const arr: { value: T; madeAt: number; opID: OpID }[] = [];
223
+ entries(): {
224
+ value: T extends CoValue ? CoID<T> : Exclude<T, CoValue>;
225
+ madeAt: number;
226
+ opID: OpID;
227
+ }[] {
228
+ const arr: {
229
+ value: T extends CoValue ? CoID<T> : Exclude<T, CoValue>;
230
+ madeAt: number;
231
+ opID: OpID;
232
+ }[] = [];
219
233
  for (const opID of this.afterStart) {
220
234
  this.fillArrayFromOpID(opID, arr);
221
235
  }
@@ -228,7 +242,11 @@ export class CoList<T extends JsonValue, Meta extends JsonObject | null = null>
228
242
  /** @internal */
229
243
  private fillArrayFromOpID(
230
244
  opID: OpID,
231
- arr: { value: T; madeAt: number; opID: OpID }[]
245
+ arr: {
246
+ value: T extends CoValue ? CoID<T> : Exclude<T, CoValue>;
247
+ madeAt: number;
248
+ opID: OpID;
249
+ }[]
232
250
  ) {
233
251
  const entry =
234
252
  this.insertions[opID.sessionID]?.[opID.txIndex]?.[opID.changeIdx];
@@ -269,23 +287,42 @@ export class CoList<T extends JsonValue, Meta extends JsonObject | null = null>
269
287
  }
270
288
 
271
289
  /** Returns the current items in the CoList as an array. (alias of `asArray`) */
272
- toJSON(): T[] {
290
+ toJSON(): (T extends CoValue ? CoID<T> : Exclude<T, CoValue>)[] {
273
291
  return this.asArray();
274
292
  }
275
293
 
276
- map<U>(mapper: (value: T, idx: number) => U): U[] {
294
+ map<U>(
295
+ mapper: (
296
+ value: T extends CoValue ? CoID<T> : Exclude<T, CoValue>,
297
+ idx: number
298
+ ) => U
299
+ ): U[] {
277
300
  return this.entries().map((entry, idx) => mapper(entry.value, idx));
278
301
  }
279
302
 
280
- filter<U extends T>(predicate: (value: T, idx: number) => value is U): U[];
281
- filter(predicate: (value: T, idx: number) => boolean): T[] {
303
+ filter<U extends T extends CoValue ? CoID<T> : Exclude<T, CoValue>>(
304
+ predicate: (
305
+ value: T extends CoValue ? CoID<T> : Exclude<T, CoValue>,
306
+ idx: number
307
+ ) => value is U
308
+ ): U[];
309
+ filter(
310
+ predicate: (
311
+ value: T extends CoValue ? CoID<T> : Exclude<T, CoValue>,
312
+ idx: number
313
+ ) => boolean
314
+ ): (T extends CoValue ? CoID<T> : Exclude<T, CoValue>)[] {
282
315
  return this.entries()
283
316
  .filter((entry, idx) => predicate(entry.value, idx))
284
317
  .map((entry) => entry.value);
285
318
  }
286
319
 
287
320
  reduce<U>(
288
- reducer: (accumulator: U, value: T, idx: number) => U,
321
+ reducer: (
322
+ accumulator: U,
323
+ value: T extends CoValue ? CoID<T> : Exclude<T, CoValue>,
324
+ idx: number
325
+ ) => U,
289
326
  initialValue: U
290
327
  ): U {
291
328
  return this.entries().reduce(
@@ -294,32 +331,28 @@ export class CoList<T extends JsonValue, Meta extends JsonObject | null = null>
294
331
  );
295
332
  }
296
333
 
297
- subscribe(listener: (coMap: CoList<T, Meta>) => void): () => void {
334
+ subscribe(listener: (coList: this) => void): () => void {
298
335
  return this.core.subscribe((content) => {
299
- listener(content as CoList<T, Meta>);
336
+ listener(content as this);
300
337
  });
301
338
  }
302
339
 
303
- edit(
304
- changer: (editable: WriteableCoList<T, Meta>) => void
305
- ): CoList<T, Meta> {
340
+ edit(changer: (editable: WriteableCoList<T, Meta>) => void): this {
306
341
  const editable = new WriteableCoList<T, Meta>(this.core);
307
342
  changer(editable);
308
- return new CoList(this.core);
343
+ return new CoList(this.core) as this;
309
344
  }
310
345
  }
311
346
 
312
347
  export class WriteableCoList<
313
- T extends JsonValue,
348
+ T extends JsonValue | CoValue,
314
349
  Meta extends JsonObject | null = null
315
350
  >
316
351
  extends CoList<T, Meta>
317
- implements WriteableCoValue
352
+ implements CoValue
318
353
  {
319
354
  /** @internal */
320
- edit(
321
- _changer: (editable: WriteableCoList<T, Meta>) => void
322
- ): CoList<T, Meta> {
355
+ edit(_changer: (editable: WriteableCoList<T, Meta>) => void): this {
323
356
  throw new Error("Already editing.");
324
357
  }
325
358
 
@@ -330,7 +363,7 @@ export class WriteableCoList<
330
363
  * If `privacy` is `"trusting"`, both `value` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers. */
331
364
  append(
332
365
  after: number,
333
- value: T,
366
+ value: T extends CoValue ? T | CoID<T> : T,
334
367
  privacy: "private" | "trusting" = "private"
335
368
  ): void {
336
369
  const entries = this.entries();
@@ -351,7 +384,7 @@ export class WriteableCoList<
351
384
  [
352
385
  {
353
386
  op: "app",
354
- value,
387
+ value: isCoValue(value) ? value.id : value,
355
388
  after: opIDBefore,
356
389
  },
357
390
  ],
@@ -366,7 +399,10 @@ export class WriteableCoList<
366
399
  * If `privacy` is `"private"` **(default)**, both `value` is encrypted in the transaction, only readable by other members of the group this `CoList` belongs to. Not even sync servers can see the content in plaintext.
367
400
  *
368
401
  * If `privacy` is `"trusting"`, both `value` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers. */
369
- push(value: T, privacy: "private" | "trusting" = "private"): void {
402
+ push(
403
+ value: T extends CoValue ? T | CoID<T> : T,
404
+ privacy: "private" | "trusting" = "private"
405
+ ): void {
370
406
  // TODO: optimize
371
407
  const entries = this.entries();
372
408
  this.append(
@@ -385,7 +421,7 @@ export class WriteableCoList<
385
421
  */
386
422
  prepend(
387
423
  before: number,
388
- value: T,
424
+ value: T extends CoValue ? T | CoID<T> : T,
389
425
  privacy: "private" | "trusting" = "private"
390
426
  ): void {
391
427
  const entries = this.entries();
@@ -410,7 +446,7 @@ export class WriteableCoList<
410
446
  [
411
447
  {
412
448
  op: "pre",
413
- value,
449
+ value: isCoValue(value) ? value.id : value,
414
450
  before: opIDAfter,
415
451
  },
416
452
  ],