jazz-tools 0.7.1 → 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-test.log +33 -33
  4. package/CHANGELOG.md +177 -165
  5. package/LICENSE.txt +1 -1
  6. package/dist/coValues/account.js +41 -4
  7. package/dist/coValues/account.js.map +1 -1
  8. package/dist/coValues/coList.js +179 -8
  9. package/dist/coValues/coList.js.map +1 -1
  10. package/dist/coValues/coMap.js +141 -5
  11. package/dist/coValues/coMap.js.map +1 -1
  12. package/dist/coValues/coStream.js +49 -1
  13. package/dist/coValues/coStream.js.map +1 -1
  14. package/dist/coValues/group.js +26 -2
  15. package/dist/coValues/group.js.map +1 -1
  16. package/dist/coValues/interfaces.js +32 -48
  17. package/dist/coValues/interfaces.js.map +1 -1
  18. package/dist/implementation/schema.js.map +1 -1
  19. package/dist/implementation/subscriptionScope.js.map +1 -1
  20. package/dist/index.js +1 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/tests/deepLoading.test.js +2 -2
  23. package/dist/tests/deepLoading.test.js.map +1 -1
  24. package/dist/tests/groupsAndAccounts.test.js +3 -3
  25. package/dist/tests/groupsAndAccounts.test.js.map +1 -1
  26. package/package.json +1 -1
  27. package/src/coValues/account.ts +106 -13
  28. package/src/coValues/coList.ts +236 -23
  29. package/src/coValues/coMap.ts +194 -11
  30. package/src/coValues/coStream.ts +128 -11
  31. package/src/coValues/group.ts +78 -5
  32. package/src/coValues/interfaces.ts +141 -211
  33. package/src/implementation/schema.ts +7 -4
  34. package/src/implementation/subscriptionScope.ts +3 -3
  35. package/src/index.ts +7 -0
  36. package/src/tests/deepLoading.test.ts +6 -3
  37. package/src/tests/groupsAndAccounts.test.ts +4 -4
@@ -6,31 +6,69 @@ import type {
6
6
  SchemaFor,
7
7
  ID,
8
8
  RefEncoded,
9
- ClassOf,
10
9
  UnCo,
11
10
  CoValueClass,
11
+ DepthsIn,
12
+ DeeplyLoaded,
13
+ UnavailableError,
14
+ AccountCtx,
15
+ CoValueFromRaw,
12
16
  } from "../internal.js";
13
17
  import {
14
18
  Account,
15
- CoValueBase,
16
19
  Group,
17
20
  InitValues,
18
21
  ItemsSym,
19
22
  Ref,
20
23
  SchemaInit,
21
24
  co,
25
+ ensureCoValueLoaded,
22
26
  inspect,
23
27
  isRefEncoded,
28
+ loadCoValue,
29
+ loadCoValueEf,
24
30
  makeRefs,
31
+ subscribeToCoValue,
32
+ subscribeToCoValueEf,
33
+ subscribeToExistingCoValue,
25
34
  } from "../internal.js";
26
35
  import { encodeSync, decodeSync } from "@effect/schema/Schema";
27
-
28
- /** @category CoValues */
36
+ import { Effect, Stream } from "effect";
37
+
38
+ /**
39
+ * CoLists are collaborative versions of plain arrays.
40
+ *
41
+ * * @categoryDescription Content
42
+ * You can access items on a `CoList` as if they were normal items on a plain array, using `[]` notation, etc.
43
+ *
44
+ * Since `CoList` is a subclass of `Array`, you can use all the normal array methods like `push`, `pop`, `splice`, etc.
45
+ *
46
+ * ```ts
47
+ * colorList[0];
48
+ * colorList[3] = "yellow";
49
+ * colorList.push("Kawazaki Green");
50
+ * colorList.splice(1, 1);
51
+ * ```
52
+ *
53
+ * @category CoValues
54
+ */
29
55
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
- export class CoList<Item = any>
31
- extends Array<Item>
32
- implements CoValue<"CoList", RawCoList>
33
- {
56
+ export class CoList<Item = any> extends Array<Item> implements CoValue {
57
+ /**
58
+ * Declare a `CoList` by subclassing `CoList.Of(...)` and passing the item schema using `co`.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * class ColorList extends CoList.Of(
63
+ * co.string
64
+ * ) {}
65
+ * class AnimalList extends CoList.Of(
66
+ * co.ref(Animal)
67
+ * ) {}
68
+ * ```
69
+ *
70
+ * @category Declaration
71
+ */
34
72
  static Of<Item>(item: Item): typeof CoList<Item> {
35
73
  // TODO: cache superclass for item class
36
74
  return class CoListOf extends CoList<Item> {
@@ -38,36 +76,62 @@ export class CoList<Item = any>
38
76
  };
39
77
  }
40
78
 
41
- /** @deprecated Use UPPERCASE `CoList.Of` instead! */
79
+ /**
80
+ * @ignore
81
+ * @deprecated Use UPPERCASE `CoList.Of` instead! */
42
82
  static of(..._args: never): never {
43
83
  throw new Error("Can't use Array.of with CoLists");
44
84
  }
45
85
 
86
+ /**
87
+ * The ID of this `CoList`
88
+ * @category Content */
46
89
  id!: ID<this>;
90
+ /** @category Type Helpers */
47
91
  _type!: "CoList";
48
92
  static {
49
93
  this.prototype._type = "CoList";
50
94
  }
95
+ /** @category Internals */
51
96
  _raw!: RawCoList;
97
+ /** @category Internals */
52
98
  _instanceID!: string;
53
99
 
54
100
  /** @internal This is only a marker type and doesn't exist at runtime */
55
101
  [ItemsSym]!: Item;
102
+ /** @internal */
56
103
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
104
  static _schema: any;
105
+ /** @internal */
58
106
  get _schema(): {
59
107
  [ItemsSym]: SchemaFor<Item>;
60
108
  } {
61
109
  return (this.constructor as typeof CoList)._schema;
62
110
  }
63
111
 
112
+ /** @category Collaboration */
64
113
  get _owner(): Account | Group {
65
114
  return this._raw.group instanceof RawAccount
66
115
  ? Account.fromRaw(this._raw.group)
67
116
  : Group.fromRaw(this._raw.group);
68
117
  }
69
118
 
70
- /** @category Content */
119
+ /**
120
+ * If a `CoList`'s items are a `co.ref(...)`, you can use `coList._refs[i]` to access
121
+ * the `Ref` instead of the potentially loaded/null value.
122
+ *
123
+ * This allows you to always get the ID or load the value manually.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * animals._refs[0].id; // => ID<Animal>
128
+ * animals._refs[0].value;
129
+ * // => Animal | null
130
+ * const animal = await animals._refs[0].load();
131
+ * ```
132
+ *
133
+ * @category Content
134
+ **/
71
135
  get _refs(): {
72
136
  [idx: number]: Exclude<Item, null> extends CoValue
73
137
  ? Ref<UnCo<Exclude<Item, null>>>
@@ -145,17 +209,35 @@ export class CoList<Item = any>
145
209
  return new Proxy(this, CoListProxyHandler as ProxyHandler<this>);
146
210
  }
147
211
 
212
+ /**
213
+ * Create a new CoList with the given initial values and owner.
214
+ *
215
+ * The owner (a Group or Account) determines access rights to the CoMap.
216
+ *
217
+ * The CoList will immediately be persisted and synced to connected peers.
218
+ *
219
+ * @example
220
+ * ```ts
221
+ * const colours = ColorList.create(
222
+ * ["red", "green", "blue"],
223
+ * { owner: me }
224
+ * );
225
+ * const animals = AnimalList.create(
226
+ * [cat, dog, fish],
227
+ * { owner: me }
228
+ * );
229
+ * ```
230
+ *
231
+ * @category Creation
232
+ **/
148
233
  static create<L extends CoList>(
149
- this: ClassOf<L>,
234
+ this: CoValueClass<L>,
150
235
  items: UnCo<L[number]>[],
151
236
  options: { owner: Account | Group },
152
237
  ) {
153
238
  return new this({ init: items, owner: options.owner });
154
239
  }
155
240
 
156
- push(...items: Item[]): number;
157
- /** @private For exact type compatibility with Array superclass */
158
- push(...items: Item[]): number;
159
241
  push(...items: Item[]): number {
160
242
  for (const item of toRawItems(
161
243
  items as Item[],
@@ -167,9 +249,6 @@ export class CoList<Item = any>
167
249
  return this._raw.entries().length;
168
250
  }
169
251
 
170
- unshift(...items: Item[]): number;
171
- /** @private For exact type compatibility with Array superclass */
172
- unshift(...items: Item[]): number;
173
252
  unshift(...items: Item[]): number {
174
253
  for (const item of toRawItems(
175
254
  items as Item[],
@@ -240,18 +319,15 @@ export class CoList<Item = any>
240
319
  return this.toJSON();
241
320
  }
242
321
 
322
+ /** @category Internals */
243
323
  static fromRaw<V extends CoList>(
244
- this: ClassOf<V> & typeof CoList,
324
+ this: CoValueClass<V> & typeof CoList,
245
325
  raw: RawCoList,
246
326
  ) {
247
327
  return new this({ fromRaw: raw });
248
328
  }
249
329
 
250
- static load = CoValueBase.load as CoValueClass["load"];
251
- static loadEf = CoValueBase.loadEf as CoValueClass["loadEf"];
252
- static subscribe = CoValueBase.subscribe as CoValueClass["subscribe"];
253
- static subscribeEf = CoValueBase.subscribeEf as CoValueClass["subscribeEf"];
254
-
330
+ /** @internal */
255
331
  static schema<V extends CoList>(
256
332
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
257
333
  this: { new (...args: any): V } & typeof CoList,
@@ -260,6 +336,143 @@ export class CoList<Item = any>
260
336
  this._schema ||= {};
261
337
  Object.assign(this._schema, def);
262
338
  }
339
+
340
+ /**
341
+ * Load a `CoList` with a given ID, as a given account.
342
+ *
343
+ * `depth` specifies if item CoValue references should be loaded as well before resolving.
344
+ * The `DeeplyLoaded` return type guarantees that corresponding referenced CoValues are loaded to the specified depth.
345
+ *
346
+ * You can pass `[]` or for shallowly loading only this CoList, or `[itemDepth]` for recursively loading referenced CoValues.
347
+ *
348
+ * Check out the `load` methods on `CoMap`/`CoList`/`CoStream`/`Group`/`Account` to see which depth structures are valid to nest.
349
+ *
350
+ * @example
351
+ * ```ts
352
+ * const animalsWithVets =
353
+ * await ListOfAnimals.load(
354
+ * "co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
355
+ * me,
356
+ * [{ vet: {} }]
357
+ * );
358
+ * ```
359
+ *
360
+ * @category Subscription & Loading
361
+ */
362
+ static load<L extends CoList, Depth>(
363
+ this: CoValueClass<L>,
364
+ id: ID<L>,
365
+ as: Account,
366
+ depth: Depth & DepthsIn<L>,
367
+ ): Promise<DeeplyLoaded<L, Depth> | undefined> {
368
+ return loadCoValue(this, id, as, depth);
369
+ }
370
+
371
+ /**
372
+ * Effectful version of `CoList.load()`.
373
+ *
374
+ * Needs to be run inside an `AccountCtx` context.
375
+ *
376
+ * @category Subscription & Loading
377
+ */
378
+ static loadEf<L extends CoList, Depth>(
379
+ this: CoValueClass<L>,
380
+ id: ID<L>,
381
+ depth: Depth & DepthsIn<L>,
382
+ ): Effect.Effect<DeeplyLoaded<L, Depth>, UnavailableError, AccountCtx> {
383
+ return loadCoValueEf<L, Depth>(this, id, depth);
384
+ }
385
+
386
+ /**
387
+ * Load and subscribe to a `CoList` with a given ID, as a given account.
388
+ *
389
+ * Automatically also subscribes to updates to all referenced/nested CoValues as soon as they are accessed in the listener.
390
+ *
391
+ * `depth` specifies if item CoValue references should be loaded as well before calling `listener` for the first time.
392
+ * The `DeeplyLoaded` return type guarantees that corresponding referenced CoValues are loaded to the specified depth.
393
+ *
394
+ * You can pass `[]` or for shallowly loading only this CoList, or `[itemDepth]` for recursively loading referenced CoValues.
395
+ *
396
+ * Check out the `load` methods on `CoMap`/`CoList`/`CoStream`/`Group`/`Account` to see which depth structures are valid to nest.
397
+ *
398
+ * Returns an unsubscribe function that you should call when you no longer need updates.
399
+ *
400
+ * Also see the `useCoState` hook to reactively subscribe to a CoValue in a React component.
401
+ *
402
+ * @example
403
+ * ```ts
404
+ * const unsub = ListOfAnimals.subscribe(
405
+ * "co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
406
+ * me,
407
+ * { vet: {} },
408
+ * (animalsWithVets) => console.log(animalsWithVets)
409
+ * );
410
+ * ```
411
+ *
412
+ * @category Subscription & Loading
413
+ */
414
+ static subscribe<L extends CoList, Depth>(
415
+ this: CoValueClass<L>,
416
+ id: ID<L>,
417
+ as: Account,
418
+ depth: Depth & DepthsIn<L>,
419
+ listener: (value: DeeplyLoaded<L, Depth>) => void,
420
+ ): () => void {
421
+ return subscribeToCoValue<L, Depth>(this, id, as, depth, listener);
422
+ }
423
+
424
+ /**
425
+ * Effectful version of `CoList.subscribe()` that returns a stream of updates.
426
+ *
427
+ * Needs to be run inside an `AccountCtx` context.
428
+ *
429
+ * @category Subscription & Loading
430
+ */
431
+ static subscribeEf<L extends CoList, Depth>(
432
+ this: CoValueClass<L>,
433
+ id: ID<L>,
434
+ depth: Depth & DepthsIn<L>,
435
+ ): Stream.Stream<DeeplyLoaded<L, Depth>, UnavailableError, AccountCtx> {
436
+ return subscribeToCoValueEf<L, Depth>(this, id, depth);
437
+ }
438
+
439
+ /**
440
+ * Given an already loaded `CoList`, ensure that items are loaded to the specified depth.
441
+ *
442
+ * Works like `CoList.load()`, but you don't need to pass the ID or the account to load as again.
443
+ *
444
+ * @category Subscription & Loading
445
+ */
446
+ ensureLoaded<L extends CoList, Depth>(
447
+ this: L,
448
+ depth: Depth & DepthsIn<L>,
449
+ ): Promise<DeeplyLoaded<L, Depth> | undefined> {
450
+ return ensureCoValueLoaded(this, depth);
451
+ }
452
+
453
+ /**
454
+ * Given an already loaded `CoList`, subscribe to updates to the `CoList` and ensure that items are loaded to the specified depth.
455
+ *
456
+ * Works like `CoList.subscribe()`, but you don't need to pass the ID or the account to load as again.
457
+ *
458
+ * Returns an unsubscribe function that you should call when you no longer need updates.
459
+ *
460
+ * @category Subscription & Loading
461
+ **/
462
+ subscribe<L extends CoList, Depth>(
463
+ this: L,
464
+ depth: Depth & DepthsIn<L>,
465
+ listener: (value: DeeplyLoaded<L, Depth>) => void,
466
+ ): () => void {
467
+ return subscribeToExistingCoValue(this, depth, listener);
468
+ }
469
+
470
+ /** @category Type Helpers */
471
+ castAs<Cl extends CoValueClass & CoValueFromRaw<CoValue>>(
472
+ cl: Cl,
473
+ ): InstanceType<Cl> {
474
+ return cl.fromRaw(this._raw) as InstanceType<Cl>;
475
+ }
263
476
  }
264
477
 
265
478
  function toRawItems<Item>(items: Item[], itemDescriptor: Schema) {
@@ -9,7 +9,11 @@ import type {
9
9
  RefEncoded,
10
10
  IfCo,
11
11
  RefIfCoValue,
12
- ClassOf,
12
+ DepthsIn,
13
+ DeeplyLoaded,
14
+ UnavailableError,
15
+ AccountCtx,
16
+ CoValueClass,
13
17
  } from "../internal.js";
14
18
  import {
15
19
  Account,
@@ -22,7 +26,14 @@ import {
22
26
  ItemsSym,
23
27
  InitValues,
24
28
  isRefEncoded,
29
+ loadCoValue,
30
+ loadCoValueEf,
31
+ subscribeToCoValue,
32
+ subscribeToCoValueEf,
33
+ ensureCoValueLoaded,
34
+ subscribeToExistingCoValue,
25
35
  } from "../internal.js";
36
+ import { Effect, Stream } from "effect";
26
37
 
27
38
  type CoMapEdit<V> = {
28
39
  value?: V;
@@ -42,6 +53,8 @@ type InitValuesFor<C extends CoMap> = {
42
53
  * @categoryDescription Declaration
43
54
  * Declare your own CoMap schemas by subclassing `CoMap` and assigning field schemas with `co`.
44
55
  *
56
+ * Optional `co.ref(...)` fields must be marked with `{ optional: true }`.
57
+ *
45
58
  * ```ts
46
59
  * import { co, CoMap } from "jazz-tools";
47
60
  *
@@ -49,6 +62,7 @@ type InitValuesFor<C extends CoMap> = {
49
62
  * name = co.string;
50
63
  * age = co.number;
51
64
  * pet = co.ref(Animal);
65
+ * car = co.ref(Car, { optional: true });
52
66
  * }
53
67
  * ```
54
68
  *
@@ -66,7 +80,7 @@ type InitValuesFor<C extends CoMap> = {
66
80
  *
67
81
  * @category CoValues
68
82
  * */
69
- export class CoMap extends CoValueBase implements CoValue<"CoMap", RawCoMap> {
83
+ export class CoMap extends CoValueBase implements CoValue {
70
84
  /**
71
85
  * The ID of this `CoMap`
72
86
  * @category Content */
@@ -92,17 +106,19 @@ export class CoMap extends CoValueBase implements CoValue<"CoMap", RawCoMap> {
92
106
  /**
93
107
  * If property `prop` is a `co.ref(...)`, you can use `coMaps._refs.prop` to access
94
108
  * the `Ref` instead of the potentially loaded/null value.
109
+ *
95
110
  * This allows you to always get the ID or load the value manually.
96
111
  *
97
112
  * @example
98
113
  * ```ts
99
114
  * person._refs.pet.id; // => ID<Animal>
100
115
  * person._refs.pet.value;
101
- * // => Animal | undefined
116
+ * // => Animal | null
102
117
  * const pet = await person._refs.pet.load();
103
118
  * ```
104
119
  *
105
- * @category Content */
120
+ * @category Content
121
+ **/
106
122
  get _refs(): {
107
123
  [Key in CoKeys<this>]: IfCo<this[Key], RefIfCoValue<this[Key]>>;
108
124
  } {
@@ -161,10 +177,14 @@ export class CoMap extends CoValueBase implements CoValue<"CoMap", RawCoMap> {
161
177
  : undefined,
162
178
  by:
163
179
  rawEdit.by &&
164
- new Ref(rawEdit.by as ID<Account>, target._loadedAs, {
165
- ref: Account,
166
- optional: false,
167
- }).accessFrom(
180
+ new Ref<Account>(
181
+ rawEdit.by as ID<Account>,
182
+ target._loadedAs,
183
+ {
184
+ ref: Account,
185
+ optional: false,
186
+ },
187
+ ).accessFrom(
168
188
  target,
169
189
  "_edits." + key.toString() + ".by",
170
190
  ),
@@ -212,9 +232,26 @@ export class CoMap extends CoValueBase implements CoValue<"CoMap", RawCoMap> {
212
232
  return new Proxy(this, CoMapProxyHandler as ProxyHandler<this>);
213
233
  }
214
234
 
215
- /** @category Creation */
235
+ /**
236
+ * Create a new CoMap with the given initial values and owner.
237
+ *
238
+ * The owner (a Group or Account) determines access rights to the CoMap.
239
+ *
240
+ * The CoMap will immediately be persisted and synced to connected peers.
241
+ *
242
+ * @example
243
+ * ```ts
244
+ * const person = Person.create({
245
+ * name: "Alice",
246
+ * age: 42,
247
+ * pet: cat,
248
+ * }, { owner: friendGroup });
249
+ * ```
250
+ *
251
+ * @category Creation
252
+ **/
216
253
  static create<M extends CoMap>(
217
- this: ClassOf<M>,
254
+ this: CoValueClass<M>,
218
255
  init: Simplify<CoMapInit<M>>,
219
256
  options: { owner: Account | Group },
220
257
  ) {
@@ -286,7 +323,24 @@ export class CoMap extends CoValueBase implements CoValue<"CoMap", RawCoMap> {
286
323
  return rawOwner.createMap(rawInit);
287
324
  }
288
325
 
289
- /** @category Declaration */
326
+ /**
327
+ * Declare a Record-like CoMap schema, by extending `CoMap.Record(...)` and passing the value schema using `co`. Keys are always `string`.
328
+ *
329
+ * @example
330
+ * ```ts
331
+ * import { co, CoMap } from "jazz-tools";
332
+ *
333
+ * class ColorToFruitMap extends CoMap.Record(
334
+ * co.ref(Fruit)
335
+ * ) {}
336
+ *
337
+ * // assume we have map: ColorToFruitMap
338
+ * // and strawberry: Fruit
339
+ * map["red"] = strawberry;
340
+ * ```
341
+ *
342
+ * @category Declaration
343
+ */
290
344
  static Record<Value>(value: IfCo<Value, Value>) {
291
345
  // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
292
346
  class RecordLikeCoMap extends CoMap {
@@ -297,6 +351,135 @@ export class CoMap extends CoValueBase implements CoValue<"CoMap", RawCoMap> {
297
351
 
298
352
  return RecordLikeCoMap;
299
353
  }
354
+
355
+ /**
356
+ * Load a `CoMap` with a given ID, as a given account.
357
+ *
358
+ * `depth` specifies which (if any) fields that reference other CoValues to load as well before resolving.
359
+ * The `DeeplyLoaded` return type guarantees that corresponding referenced CoValues are loaded to the specified depth.
360
+ *
361
+ * You can pass `[]` or `{}` for shallowly loading only this CoMap, or `{ fieldA: depthA, fieldB: depthB }` for recursively loading referenced CoValues.
362
+ *
363
+ * Check out the `load` methods on `CoMap`/`CoList`/`CoStream`/`Group`/`Account` to see which depth structures are valid to nest.
364
+ *
365
+ * @example
366
+ * ```ts
367
+ * const person = await Person.load(
368
+ * "co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
369
+ * me,
370
+ * { pet: {} }
371
+ * );
372
+ * ```
373
+ *
374
+ * @category Subscription & Loading
375
+ */
376
+ static load<M extends CoMap, Depth>(
377
+ this: CoValueClass<M>,
378
+ id: ID<M>,
379
+ as: Account,
380
+ depth: Depth & DepthsIn<M>,
381
+ ): Promise<DeeplyLoaded<M, Depth> | undefined> {
382
+ return loadCoValue(this, id, as, depth);
383
+ }
384
+
385
+ /**
386
+ * Effectful version of `CoMap.load()`.
387
+ *
388
+ * Needs to be run inside an `AccountCtx` context.
389
+ *
390
+ * @category Subscription & Loading
391
+ */
392
+ static loadEf<M extends CoMap, Depth>(
393
+ this: CoValueClass<M>,
394
+ id: ID<M>,
395
+ depth: Depth & DepthsIn<M>,
396
+ ): Effect.Effect<DeeplyLoaded<M, Depth>, UnavailableError, AccountCtx> {
397
+ return loadCoValueEf<M, Depth>(this, id, depth);
398
+ }
399
+
400
+ /**
401
+ * Load and subscribe to a `CoMap` with a given ID, as a given account.
402
+ *
403
+ * Automatically also subscribes to updates to all referenced/nested CoValues as soon as they are accessed in the listener.
404
+ *
405
+ * `depth` specifies which (if any) fields that reference other CoValues to load as well before calling `listener` for the first time.
406
+ * The `DeeplyLoaded` return type guarantees that corresponding referenced CoValues are loaded to the specified depth.
407
+ *
408
+ * You can pass `[]` or `{}` for shallowly loading only this CoMap, or `{ fieldA: depthA, fieldB: depthB }` for recursively loading referenced CoValues.
409
+ *
410
+ * Check out the `load` methods on `CoMap`/`CoList`/`CoStream`/`Group`/`Account` to see which depth structures are valid to nest.
411
+ *
412
+ * Returns an unsubscribe function that you should call when you no longer need updates.
413
+ *
414
+ * Also see the `useCoState` hook to reactively subscribe to a CoValue in a React component.
415
+ *
416
+ * @example
417
+ * ```ts
418
+ * const unsub = Person.subscribe(
419
+ * "co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
420
+ * me,
421
+ * { pet: {} },
422
+ * (person) => console.log(person)
423
+ * );
424
+ * ```
425
+ *
426
+ * @category Subscription & Loading
427
+ */
428
+ static subscribe<M extends CoMap, Depth>(
429
+ this: CoValueClass<M>,
430
+ id: ID<M>,
431
+ as: Account,
432
+ depth: Depth & DepthsIn<M>,
433
+ listener: (value: DeeplyLoaded<M, Depth>) => void,
434
+ ): () => void {
435
+ return subscribeToCoValue<M, Depth>(this, id, as, depth, listener);
436
+ }
437
+
438
+ /**
439
+ * Effectful version of `CoMap.subscribe()` that returns a stream of updates.
440
+ *
441
+ * Needs to be run inside an `AccountCtx` context.
442
+ *
443
+ * @category Subscription & Loading
444
+ */
445
+ static subscribeEf<M extends CoMap, Depth>(
446
+ this: CoValueClass<M>,
447
+ id: ID<M>,
448
+ depth: Depth & DepthsIn<M>,
449
+ ): Stream.Stream<DeeplyLoaded<M, Depth>, UnavailableError, AccountCtx> {
450
+ return subscribeToCoValueEf<M, Depth>(this, id, depth);
451
+ }
452
+
453
+ /**
454
+ * Given an already loaded `CoMap`, ensure that the specified fields are loaded to the specified depth.
455
+ *
456
+ * Works like `CoMap.load()`, but you don't need to pass the ID or the account to load as again.
457
+ *
458
+ * @category Subscription & Loading
459
+ */
460
+ ensureLoaded<M extends CoMap, Depth>(
461
+ this: M,
462
+ depth: Depth & DepthsIn<M>,
463
+ ): Promise<DeeplyLoaded<M, Depth> | undefined> {
464
+ return ensureCoValueLoaded(this, depth);
465
+ }
466
+
467
+ /**
468
+ * Given an already loaded `CoMap`, subscribe to updates to the `CoMap` and ensure that the specified fields are loaded to the specified depth.
469
+ *
470
+ * Works like `CoMap.subscribe()`, but you don't need to pass the ID or the account to load as again.
471
+ *
472
+ * Returns an unsubscribe function that you should call when you no longer need updates.
473
+ *
474
+ * @category Subscription & Loading
475
+ **/
476
+ subscribe<M extends CoMap, Depth>(
477
+ this: M,
478
+ depth: Depth & DepthsIn<M>,
479
+ listener: (value: DeeplyLoaded<M, Depth>) => void,
480
+ ): () => void {
481
+ return subscribeToExistingCoValue(this, depth, listener);
482
+ }
300
483
  }
301
484
 
302
485
  export type CoKeys<Map extends object> = Exclude<