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.
- package/.turbo/turbo-build.log +2 -2
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +33 -33
- package/CHANGELOG.md +177 -165
- package/LICENSE.txt +1 -1
- package/dist/coValues/account.js +41 -4
- package/dist/coValues/account.js.map +1 -1
- package/dist/coValues/coList.js +179 -8
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.js +141 -5
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.js +49 -1
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/coValues/group.js +26 -2
- package/dist/coValues/group.js.map +1 -1
- package/dist/coValues/interfaces.js +32 -48
- package/dist/coValues/interfaces.js.map +1 -1
- package/dist/implementation/schema.js.map +1 -1
- package/dist/implementation/subscriptionScope.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/tests/deepLoading.test.js +2 -2
- package/dist/tests/deepLoading.test.js.map +1 -1
- package/dist/tests/groupsAndAccounts.test.js +3 -3
- package/dist/tests/groupsAndAccounts.test.js.map +1 -1
- package/package.json +1 -1
- package/src/coValues/account.ts +106 -13
- package/src/coValues/coList.ts +236 -23
- package/src/coValues/coMap.ts +194 -11
- package/src/coValues/coStream.ts +128 -11
- package/src/coValues/group.ts +78 -5
- package/src/coValues/interfaces.ts +141 -211
- package/src/implementation/schema.ts +7 -4
- package/src/implementation/subscriptionScope.ts +3 -3
- package/src/index.ts +7 -0
- package/src/tests/deepLoading.test.ts +6 -3
- package/src/tests/groupsAndAccounts.test.ts +4 -4
package/src/coValues/coList.ts
CHANGED
@@ -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
|
-
|
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
|
-
|
32
|
-
|
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
|
-
/**
|
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
|
-
/**
|
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:
|
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:
|
324
|
+
this: CoValueClass<V> & typeof CoList,
|
245
325
|
raw: RawCoList,
|
246
326
|
) {
|
247
327
|
return new this({ fromRaw: raw });
|
248
328
|
}
|
249
329
|
|
250
|
-
|
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) {
|
package/src/coValues/coMap.ts
CHANGED
@@ -9,7 +9,11 @@ import type {
|
|
9
9
|
RefEncoded,
|
10
10
|
IfCo,
|
11
11
|
RefIfCoValue,
|
12
|
-
|
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
|
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 |
|
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
|
165
|
-
|
166
|
-
|
167
|
-
|
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
|
-
/**
|
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:
|
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
|
-
/**
|
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<
|