jazz-tools 0.8.21 → 0.8.27
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +18 -0
- package/dist/native/coValues/{coStream.js → coFeed.js} +9 -5
- package/dist/native/coValues/coFeed.js.map +1 -0
- package/dist/native/coValues/coList.js +2 -2
- package/dist/native/coValues/coMap.js +2 -2
- package/dist/native/coValues/deepLoading.js.map +1 -1
- package/dist/native/coValues/extensions/imageDef.js +2 -2
- package/dist/native/coValues/extensions/imageDef.js.map +1 -1
- package/dist/native/coValues/schemaUnion.js +95 -0
- package/dist/native/coValues/schemaUnion.js.map +1 -0
- package/dist/native/exports.js +1 -1
- package/dist/native/exports.js.map +1 -1
- package/dist/native/internal.js +2 -1
- package/dist/native/internal.js.map +1 -1
- package/dist/web/coValues/{coStream.js → coFeed.js} +9 -5
- package/dist/web/coValues/coFeed.js.map +1 -0
- package/dist/web/coValues/coList.js +2 -2
- package/dist/web/coValues/coMap.js +2 -2
- package/dist/web/coValues/deepLoading.js.map +1 -1
- package/dist/web/coValues/extensions/imageDef.js +2 -2
- package/dist/web/coValues/extensions/imageDef.js.map +1 -1
- package/dist/web/coValues/schemaUnion.js +95 -0
- package/dist/web/coValues/schemaUnion.js.map +1 -0
- package/dist/web/exports.js +1 -1
- package/dist/web/exports.js.map +1 -1
- package/dist/web/internal.js +2 -1
- package/dist/web/internal.js.map +1 -1
- package/package.json +2 -2
- package/src/coValues/{coStream.ts → coFeed.ts} +50 -38
- package/src/coValues/coList.ts +2 -2
- package/src/coValues/coMap.ts +2 -2
- package/src/coValues/deepLoading.ts +6 -6
- package/src/coValues/extensions/imageDef.ts +4 -9
- package/src/coValues/schemaUnion.ts +108 -0
- package/src/exports.ts +3 -0
- package/src/internal.ts +2 -1
- package/src/tests/{coStream.test.ts → coFeed.test.ts} +21 -30
- package/src/tests/deepLoading.test.ts +2 -2
- package/src/tests/schemaUnion.test.ts +118 -0
- package/src/tests/subscribe.test.ts +4 -4
- package/dist/native/coValues/coStream.js.map +0 -1
- package/dist/web/coValues/coStream.js.map +0 -1
@@ -38,11 +38,17 @@ import {
|
|
38
38
|
subscribeToExistingCoValue,
|
39
39
|
} from "../internal.js";
|
40
40
|
|
41
|
-
|
42
|
-
|
41
|
+
/** @deprecated Use CoFeedEntry instead */
|
42
|
+
export type CoStreamEntry<Item> = CoFeedEntry<Item>;
|
43
|
+
|
44
|
+
export type CoFeedEntry<Item> = SingleCoFeedEntry<Item> & {
|
45
|
+
all: IterableIterator<SingleCoFeedEntry<Item>>;
|
43
46
|
};
|
44
47
|
|
45
|
-
|
48
|
+
/** @deprecated Use SingleCoFeedEntry instead */
|
49
|
+
export type SingleCoStreamEntry<Item> = SingleCoFeedEntry<Item>;
|
50
|
+
|
51
|
+
export type SingleCoFeedEntry<Item> = {
|
46
52
|
value: NonNullable<Item> extends CoValue ? NonNullable<Item> | null : Item;
|
47
53
|
ref: NonNullable<Item> extends CoValue ? Ref<NonNullable<Item>> : never;
|
48
54
|
by?: Account | null;
|
@@ -50,11 +56,14 @@ export type SingleCoStreamEntry<Item> = {
|
|
50
56
|
tx: CojsonInternalTypes.TransactionID;
|
51
57
|
};
|
52
58
|
|
59
|
+
/** @deprecated Use CoFeed instead */
|
60
|
+
export { CoFeed as CoStream };
|
61
|
+
|
53
62
|
/** @category CoValues */
|
54
63
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
55
|
-
export class
|
56
|
-
static Of<Item>(item: IfCo<Item, Item>): typeof
|
57
|
-
return class
|
64
|
+
export class CoFeed<Item = any> extends CoValueBase implements CoValue {
|
65
|
+
static Of<Item>(item: IfCo<Item, Item>): typeof CoFeed<Item> {
|
66
|
+
return class CoFeedOf extends CoFeed<Item> {
|
58
67
|
[co.items] = item;
|
59
68
|
};
|
60
69
|
}
|
@@ -73,12 +82,12 @@ export class CoStream<Item = any> extends CoValueBase implements CoValue {
|
|
73
82
|
get _schema(): {
|
74
83
|
[ItemsSym]: SchemaFor<Item>;
|
75
84
|
} {
|
76
|
-
return (this.constructor as typeof
|
85
|
+
return (this.constructor as typeof CoFeed)._schema;
|
77
86
|
}
|
78
87
|
|
79
|
-
[key: ID<Account>]:
|
88
|
+
[key: ID<Account>]: CoFeedEntry<Item>;
|
80
89
|
|
81
|
-
get byMe():
|
90
|
+
get byMe(): CoFeedEntry<Item> | undefined {
|
82
91
|
if (this._loadedAs._type === "Account") {
|
83
92
|
return this[this._loadedAs.id];
|
84
93
|
} else {
|
@@ -86,9 +95,9 @@ export class CoStream<Item = any> extends CoValueBase implements CoValue {
|
|
86
95
|
}
|
87
96
|
}
|
88
97
|
perSession!: {
|
89
|
-
[key: SessionID]:
|
98
|
+
[key: SessionID]: CoFeedEntry<Item>;
|
90
99
|
};
|
91
|
-
get inCurrentSession():
|
100
|
+
get inCurrentSession(): CoFeedEntry<Item> | undefined {
|
92
101
|
if (this._loadedAs._type === "Account") {
|
93
102
|
return this.perSession[this._loadedAs.sessionID!];
|
94
103
|
} else {
|
@@ -116,9 +125,9 @@ export class CoStream<Item = any> extends CoValueBase implements CoValue {
|
|
116
125
|
return new Proxy(this, CoStreamProxyHandler as ProxyHandler<this>);
|
117
126
|
}
|
118
127
|
|
119
|
-
static create<S extends
|
128
|
+
static create<S extends CoFeed>(
|
120
129
|
this: CoValueClass<S>,
|
121
|
-
init: S extends
|
130
|
+
init: S extends CoFeed<infer Item> ? UnCo<Item>[] : never,
|
122
131
|
options: { owner: Account | Group },
|
123
132
|
) {
|
124
133
|
const instance = new this({ init, owner: options.owner });
|
@@ -197,9 +206,9 @@ export class CoStream<Item = any> extends CoValueBase implements CoValue {
|
|
197
206
|
return this.toJSON();
|
198
207
|
}
|
199
208
|
|
200
|
-
static schema<V extends
|
209
|
+
static schema<V extends CoFeed>(
|
201
210
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
202
|
-
this: { new (...args: any): V } & typeof
|
211
|
+
this: { new (...args: any): V } & typeof CoFeed,
|
203
212
|
def: { [ItemsSym]: V["_schema"][ItemsSym] },
|
204
213
|
) {
|
205
214
|
this._schema ||= {};
|
@@ -207,7 +216,7 @@ export class CoStream<Item = any> extends CoValueBase implements CoValue {
|
|
207
216
|
}
|
208
217
|
|
209
218
|
/** @category Subscription & Loading */
|
210
|
-
static load<S extends
|
219
|
+
static load<S extends CoFeed, Depth>(
|
211
220
|
this: CoValueClass<S>,
|
212
221
|
id: ID<S>,
|
213
222
|
as: Account,
|
@@ -217,7 +226,7 @@ export class CoStream<Item = any> extends CoValueBase implements CoValue {
|
|
217
226
|
}
|
218
227
|
|
219
228
|
/** @category Subscription & Loading */
|
220
|
-
static subscribe<S extends
|
229
|
+
static subscribe<S extends CoFeed, Depth>(
|
221
230
|
this: CoValueClass<S>,
|
222
231
|
id: ID<S>,
|
223
232
|
as: Account,
|
@@ -228,7 +237,7 @@ export class CoStream<Item = any> extends CoValueBase implements CoValue {
|
|
228
237
|
}
|
229
238
|
|
230
239
|
/** @category Subscription & Loading */
|
231
|
-
ensureLoaded<S extends
|
240
|
+
ensureLoaded<S extends CoFeed, Depth>(
|
232
241
|
this: S,
|
233
242
|
depth: Depth & DepthsIn<S>,
|
234
243
|
): Promise<DeeplyLoaded<S, Depth> | undefined> {
|
@@ -236,7 +245,7 @@ export class CoStream<Item = any> extends CoValueBase implements CoValue {
|
|
236
245
|
}
|
237
246
|
|
238
247
|
/** @category Subscription & Loading */
|
239
|
-
subscribe<S extends
|
248
|
+
subscribe<S extends CoFeed, Depth>(
|
240
249
|
this: S,
|
241
250
|
depth: Depth & DepthsIn<S>,
|
242
251
|
listener: (value: DeeplyLoaded<S, Depth>) => void,
|
@@ -256,7 +265,7 @@ function entryFromRawEntry<Item>(
|
|
256
265
|
loadedAs: Account | AnonymousJazzAgent,
|
257
266
|
accountID: ID<Account> | undefined,
|
258
267
|
itemField: Schema,
|
259
|
-
): Omit<
|
268
|
+
): Omit<CoFeedEntry<Item>, "all"> {
|
260
269
|
return {
|
261
270
|
get value(): NonNullable<Item> extends CoValue
|
262
271
|
? (CoValue & Item) | null
|
@@ -307,7 +316,7 @@ function entryFromRawEntry<Item>(
|
|
307
316
|
};
|
308
317
|
}
|
309
318
|
|
310
|
-
export const CoStreamProxyHandler: ProxyHandler<
|
319
|
+
export const CoStreamProxyHandler: ProxyHandler<CoFeed> = {
|
311
320
|
get(target, key, receiver) {
|
312
321
|
if (typeof key === "string" && key.startsWith("co_")) {
|
313
322
|
const rawEntry = target._raw.lastItemBy(key as RawAccountID);
|
@@ -337,7 +346,7 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
|
|
337
346
|
);
|
338
347
|
}
|
339
348
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
340
|
-
})() satisfies IterableIterator<
|
349
|
+
})() satisfies IterableIterator<SingleCoFeedEntry<any>>;
|
341
350
|
},
|
342
351
|
});
|
343
352
|
|
@@ -350,8 +359,8 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
|
|
350
359
|
},
|
351
360
|
set(target, key, value, receiver) {
|
352
361
|
if (key === ItemsSym && typeof value === "object" && SchemaInit in value) {
|
353
|
-
(target.constructor as typeof
|
354
|
-
(target.constructor as typeof
|
362
|
+
(target.constructor as typeof CoFeed)._schema ||= {};
|
363
|
+
(target.constructor as typeof CoFeed)._schema[ItemsSym] =
|
355
364
|
value[SchemaInit];
|
356
365
|
return true;
|
357
366
|
} else {
|
@@ -365,8 +374,8 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
|
|
365
374
|
typeof descriptor.value === "object" &&
|
366
375
|
SchemaInit in descriptor.value
|
367
376
|
) {
|
368
|
-
(target.constructor as typeof
|
369
|
-
(target.constructor as typeof
|
377
|
+
(target.constructor as typeof CoFeed)._schema ||= {};
|
378
|
+
(target.constructor as typeof CoFeed)._schema[ItemsSym] =
|
370
379
|
descriptor.value[SchemaInit];
|
371
380
|
return true;
|
372
381
|
} else {
|
@@ -396,8 +405,8 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
|
|
396
405
|
};
|
397
406
|
|
398
407
|
const CoStreamPerSessionProxyHandler = (
|
399
|
-
innerTarget:
|
400
|
-
accessFrom:
|
408
|
+
innerTarget: CoFeed,
|
409
|
+
accessFrom: CoFeed,
|
401
410
|
): ProxyHandler<Record<string, never>> => ({
|
402
411
|
get(_target, key, receiver) {
|
403
412
|
if (typeof key === "string" && key.includes("session")) {
|
@@ -435,7 +444,7 @@ const CoStreamPerSessionProxyHandler = (
|
|
435
444
|
);
|
436
445
|
}
|
437
446
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
438
|
-
})() satisfies IterableIterator<
|
447
|
+
})() satisfies IterableIterator<SingleCoFeedEntry<any>>;
|
439
448
|
},
|
440
449
|
});
|
441
450
|
|
@@ -460,8 +469,11 @@ const CoStreamPerSessionProxyHandler = (
|
|
460
469
|
},
|
461
470
|
});
|
462
471
|
|
472
|
+
/** @deprecated Use CoFeed instead */
|
473
|
+
export { FileStream as BinaryCoStream };
|
474
|
+
|
463
475
|
/** @category CoValues */
|
464
|
-
export class
|
476
|
+
export class FileStream extends CoValueBase implements CoValue {
|
465
477
|
declare id: ID<this>;
|
466
478
|
declare _type: "BinaryCoStream";
|
467
479
|
declare _raw: RawBinaryCoStream;
|
@@ -496,7 +508,7 @@ export class BinaryCoStream extends CoValueBase implements CoValue {
|
|
496
508
|
});
|
497
509
|
}
|
498
510
|
|
499
|
-
static create<S extends
|
511
|
+
static create<S extends FileStream>(
|
500
512
|
this: CoValueClass<S>,
|
501
513
|
options: { owner: Account | Group },
|
502
514
|
) {
|
@@ -541,7 +553,7 @@ export class BinaryCoStream extends CoValueBase implements CoValue {
|
|
541
553
|
}
|
542
554
|
|
543
555
|
static async loadAsBlob(
|
544
|
-
id: ID<
|
556
|
+
id: ID<FileStream>,
|
545
557
|
as: Account,
|
546
558
|
options?: {
|
547
559
|
allowUnfinished?: boolean;
|
@@ -554,7 +566,7 @@ export class BinaryCoStream extends CoValueBase implements CoValue {
|
|
554
566
|
* stream isn't complete wait for the stream download before progressing
|
555
567
|
*/
|
556
568
|
if (!options?.allowUnfinished && !stream?.isBinaryStreamEnded()) {
|
557
|
-
stream = await new Promise<
|
569
|
+
stream = await new Promise<FileStream>((resolve) => {
|
558
570
|
const unsubscribe = subscribeToCoValue(this, id, as, [], (value) => {
|
559
571
|
if (value.isBinaryStreamEnded()) {
|
560
572
|
unsubscribe();
|
@@ -575,7 +587,7 @@ export class BinaryCoStream extends CoValueBase implements CoValue {
|
|
575
587
|
owner: Group | Account;
|
576
588
|
onProgress?: (progress: number) => void;
|
577
589
|
},
|
578
|
-
): Promise<
|
590
|
+
): Promise<FileStream> {
|
579
591
|
const stream = this.create({ owner: options.owner });
|
580
592
|
|
581
593
|
const start = Date.now();
|
@@ -635,7 +647,7 @@ export class BinaryCoStream extends CoValueBase implements CoValue {
|
|
635
647
|
}
|
636
648
|
|
637
649
|
/** @category Subscription & Loading */
|
638
|
-
static load<B extends
|
650
|
+
static load<B extends FileStream, Depth>(
|
639
651
|
this: CoValueClass<B>,
|
640
652
|
id: ID<B>,
|
641
653
|
as: Account,
|
@@ -645,7 +657,7 @@ export class BinaryCoStream extends CoValueBase implements CoValue {
|
|
645
657
|
}
|
646
658
|
|
647
659
|
/** @category Subscription & Loading */
|
648
|
-
static subscribe<B extends
|
660
|
+
static subscribe<B extends FileStream, Depth>(
|
649
661
|
this: CoValueClass<B>,
|
650
662
|
id: ID<B>,
|
651
663
|
as: Account,
|
@@ -656,7 +668,7 @@ export class BinaryCoStream extends CoValueBase implements CoValue {
|
|
656
668
|
}
|
657
669
|
|
658
670
|
/** @category Subscription & Loading */
|
659
|
-
ensureLoaded<B extends
|
671
|
+
ensureLoaded<B extends FileStream, Depth>(
|
660
672
|
this: B,
|
661
673
|
depth: Depth & DepthsIn<B>,
|
662
674
|
): Promise<DeeplyLoaded<B, Depth> | undefined> {
|
@@ -664,7 +676,7 @@ export class BinaryCoStream extends CoValueBase implements CoValue {
|
|
664
676
|
}
|
665
677
|
|
666
678
|
/** @category Subscription & Loading */
|
667
|
-
subscribe<B extends
|
679
|
+
subscribe<B extends FileStream, Depth>(
|
668
680
|
this: B,
|
669
681
|
depth: Depth & DepthsIn<B>,
|
670
682
|
listener: (value: DeeplyLoaded<B, Depth>) => void,
|
package/src/coValues/coList.ts
CHANGED
@@ -339,7 +339,7 @@ export class CoList<Item = any> extends Array<Item> implements CoValue {
|
|
339
339
|
*
|
340
340
|
* You can pass `[]` or for shallowly loading only this CoList, or `[itemDepth]` for recursively loading referenced CoValues.
|
341
341
|
*
|
342
|
-
* Check out the `load` methods on `CoMap`/`CoList`/`
|
342
|
+
* Check out the `load` methods on `CoMap`/`CoList`/`CoFeed`/`Group`/`Account` to see which depth structures are valid to nest.
|
343
343
|
*
|
344
344
|
* @example
|
345
345
|
* ```ts
|
@@ -372,7 +372,7 @@ export class CoList<Item = any> extends Array<Item> implements CoValue {
|
|
372
372
|
*
|
373
373
|
* You can pass `[]` or for shallowly loading only this CoList, or `[itemDepth]` for recursively loading referenced CoValues.
|
374
374
|
*
|
375
|
-
* Check out the `load` methods on `CoMap`/`CoList`/`
|
375
|
+
* Check out the `load` methods on `CoMap`/`CoList`/`CoFeed`/`Group`/`Account` to see which depth structures are valid to nest.
|
376
376
|
*
|
377
377
|
* Returns an unsubscribe function that you should call when you no longer need updates.
|
378
378
|
*
|
package/src/coValues/coMap.ts
CHANGED
@@ -366,7 +366,7 @@ export class CoMap extends CoValueBase implements CoValue {
|
|
366
366
|
*
|
367
367
|
* You can pass `[]` or `{}` for shallowly loading only this CoMap, or `{ fieldA: depthA, fieldB: depthB }` for recursively loading referenced CoValues.
|
368
368
|
*
|
369
|
-
* Check out the `load` methods on `CoMap`/`CoList`/`
|
369
|
+
* Check out the `load` methods on `CoMap`/`CoList`/`CoFeed`/`Group`/`Account` to see which depth structures are valid to nest.
|
370
370
|
*
|
371
371
|
* @example
|
372
372
|
* ```ts
|
@@ -398,7 +398,7 @@ export class CoMap extends CoValueBase implements CoValue {
|
|
398
398
|
*
|
399
399
|
* You can pass `[]` or `{}` for shallowly loading only this CoMap, or `{ fieldA: depthA, fieldB: depthB }` for recursively loading referenced CoValues.
|
400
400
|
*
|
401
|
-
* Check out the `load` methods on `CoMap`/`CoList`/`
|
401
|
+
* Check out the `load` methods on `CoMap`/`CoList`/`CoFeed`/`Group`/`Account` to see which depth structures are valid to nest.
|
402
402
|
*
|
403
403
|
* Returns an unsubscribe function that you should call when you no longer need updates.
|
404
404
|
*
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import { SessionID } from "cojson";
|
2
2
|
import {
|
3
3
|
Account,
|
4
|
+
CoFeed,
|
5
|
+
CoFeedEntry,
|
4
6
|
CoList,
|
5
|
-
CoStream,
|
6
|
-
CoStreamEntry,
|
7
7
|
ItemsSym,
|
8
8
|
Ref,
|
9
9
|
RefEncoded,
|
@@ -66,10 +66,10 @@ export function fulfillsDepth(depth: any, value: CoValue): boolean {
|
|
66
66
|
return true;
|
67
67
|
} else {
|
68
68
|
const itemDepth = depth[0];
|
69
|
-
return Object.values((value as
|
69
|
+
return Object.values((value as CoFeed).perSession).every((entry) =>
|
70
70
|
entry.ref
|
71
71
|
? entry.value && fulfillsDepth(itemDepth, entry.value)
|
72
|
-
: ((value as
|
72
|
+
: ((value as CoFeed)._schema[ItemsSym] as RefEncoded<CoValue>)
|
73
73
|
.optional,
|
74
74
|
);
|
75
75
|
}
|
@@ -121,7 +121,7 @@ export type DepthsIn<
|
|
121
121
|
| never[]
|
122
122
|
: V extends {
|
123
123
|
_type: "CoStream";
|
124
|
-
byMe:
|
124
|
+
byMe: CoFeedEntry<infer Item> | undefined;
|
125
125
|
}
|
126
126
|
?
|
127
127
|
| [
|
@@ -192,7 +192,7 @@ export type DeeplyLoaded<
|
|
192
192
|
: [V] extends [
|
193
193
|
{
|
194
194
|
_type: "CoStream";
|
195
|
-
byMe:
|
195
|
+
byMe: CoFeedEntry<infer Item> | undefined;
|
196
196
|
},
|
197
197
|
]
|
198
198
|
? Depth extends never[]
|
@@ -1,21 +1,16 @@
|
|
1
|
-
import {
|
2
|
-
BinaryCoStream,
|
3
|
-
CoMap,
|
4
|
-
co,
|
5
|
-
subscriptionsScopes,
|
6
|
-
} from "../../internal.js";
|
1
|
+
import { CoMap, FileStream, co, subscriptionsScopes } from "../../internal.js";
|
7
2
|
|
8
3
|
/** @category Media */
|
9
4
|
export class ImageDefinition extends CoMap {
|
10
5
|
originalSize = co.json<[number, number]>();
|
11
6
|
placeholderDataURL? = co.string;
|
12
7
|
|
13
|
-
[co.items] = co.ref(
|
14
|
-
[res: `${number}x${number}`]: co<
|
8
|
+
[co.items] = co.ref(FileStream);
|
9
|
+
[res: `${number}x${number}`]: co<FileStream | null>;
|
15
10
|
|
16
11
|
highestResAvailable(options?: {
|
17
12
|
maxWidth?: number;
|
18
|
-
}): { res: `${number}x${number}`; stream:
|
13
|
+
}): { res: `${number}x${number}`; stream: FileStream } | undefined {
|
19
14
|
if (!subscriptionsScopes.get(this)) {
|
20
15
|
console.warn(
|
21
16
|
"highestResAvailable() only makes sense when used within a subscription.",
|
@@ -0,0 +1,108 @@
|
|
1
|
+
import {
|
2
|
+
CoValue,
|
3
|
+
CoValueBase,
|
4
|
+
CoValueClass,
|
5
|
+
CoValueFromRaw,
|
6
|
+
} from "../internal.js";
|
7
|
+
|
8
|
+
/**
|
9
|
+
* SchemaUnion allows you to create union types of CoValues that can be discriminated at runtime.
|
10
|
+
*
|
11
|
+
* @categoryDescription Declaration
|
12
|
+
* Declare your union types by extending `SchemaUnion.Of(...)` and passing a discriminator function
|
13
|
+
* that determines which concrete type to use based on the raw data.
|
14
|
+
*
|
15
|
+
* @example
|
16
|
+
* ```ts
|
17
|
+
* import { SchemaUnion, CoMap } from "jazz-tools";
|
18
|
+
*
|
19
|
+
* class BaseWidget extends CoMap {
|
20
|
+
* type = co.string;
|
21
|
+
* }
|
22
|
+
*
|
23
|
+
* class ButtonWidget extends BaseWidget {
|
24
|
+
* type = co.literal("button");
|
25
|
+
* label = co.string;
|
26
|
+
* }
|
27
|
+
*
|
28
|
+
* class SliderWidget extends BaseWidget {
|
29
|
+
* type = co.literal("slider");
|
30
|
+
* min = co.number;
|
31
|
+
* max = co.number;
|
32
|
+
* }
|
33
|
+
*
|
34
|
+
* const WidgetUnion = SchemaUnion.Of<BaseWidget>((raw) => {
|
35
|
+
* switch (raw.get("type")) {
|
36
|
+
* case "button": return ButtonWidget;
|
37
|
+
* case "slider": return SliderWidget;
|
38
|
+
* default: throw new Error("Unknown widget type");
|
39
|
+
* }
|
40
|
+
* });
|
41
|
+
* ```
|
42
|
+
*
|
43
|
+
* @categoryDescription Loading
|
44
|
+
* When loading a SchemaUnion, the correct subclass will be instantiated based on the discriminator.
|
45
|
+
* You can narrow the returned instance to a subclass by using `instanceof` like so:
|
46
|
+
*
|
47
|
+
* @example
|
48
|
+
* ```ts
|
49
|
+
* const widget = await loadCoValue(WidgetUnion, id, me, {});
|
50
|
+
* if (widget instanceof ButtonWidget) {
|
51
|
+
* console.log(widget.label);
|
52
|
+
* } else if (widget instanceof SliderWidget) {
|
53
|
+
* console.log(widget.min, widget.max);
|
54
|
+
* }
|
55
|
+
* ```
|
56
|
+
*
|
57
|
+
* @category CoValues
|
58
|
+
*/
|
59
|
+
export abstract class SchemaUnion extends CoValueBase implements CoValue {
|
60
|
+
/**
|
61
|
+
* Create a new union type from a discriminator function.
|
62
|
+
*
|
63
|
+
* The discriminator function receives the raw data and should return the appropriate
|
64
|
+
* concrete class to use for that data.
|
65
|
+
*
|
66
|
+
* @param discriminator - Function that determines which concrete type to use
|
67
|
+
* @returns A new class that can create/load instances of the union type
|
68
|
+
*
|
69
|
+
* @example
|
70
|
+
* ```ts
|
71
|
+
* const WidgetUnion = SchemaUnion.Of<BaseWidget>((raw) => {
|
72
|
+
* switch (raw.get("type")) {
|
73
|
+
* case "button": return ButtonWidget;
|
74
|
+
* case "slider": return SliderWidget;
|
75
|
+
* default: throw new Error("Unknown widget type");
|
76
|
+
* }
|
77
|
+
* });
|
78
|
+
* ```
|
79
|
+
*
|
80
|
+
* @category Creation
|
81
|
+
*/
|
82
|
+
static Of<V extends CoValue>(
|
83
|
+
discriminator: (raw: V["_raw"]) => CoValueClass<V> & CoValueFromRaw<V>,
|
84
|
+
): CoValueClass<V> & typeof SchemaUnion {
|
85
|
+
return class SchemaUnionClass extends SchemaUnion {
|
86
|
+
static override fromRaw<T extends CoValue>(
|
87
|
+
this: CoValueClass<T> & CoValueFromRaw<T>,
|
88
|
+
raw: T["_raw"],
|
89
|
+
): T {
|
90
|
+
const ResolvedClass = discriminator(
|
91
|
+
raw as V["_raw"],
|
92
|
+
) as unknown as CoValueClass<T> & CoValueFromRaw<T>;
|
93
|
+
return ResolvedClass.fromRaw(raw);
|
94
|
+
}
|
95
|
+
} as unknown as CoValueClass<V> & typeof SchemaUnion;
|
96
|
+
}
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Create an instance from raw data. This is called internally and should not be used directly.
|
100
|
+
* Use {@link SchemaUnion.Of} to create a union type instead.
|
101
|
+
*
|
102
|
+
* @internal
|
103
|
+
*/
|
104
|
+
// @ts-ignore
|
105
|
+
static fromRaw<V extends CoValue>(this: CoValueClass<V>, raw: V["_raw"]): V {
|
106
|
+
throw new Error("Not implemented");
|
107
|
+
}
|
108
|
+
}
|
package/src/exports.ts
CHANGED
@@ -14,15 +14,18 @@ export { Encoders, co } from "./internal.js";
|
|
14
14
|
|
15
15
|
export {
|
16
16
|
Account,
|
17
|
+
FileStream,
|
17
18
|
BinaryCoStream,
|
18
19
|
CoList,
|
19
20
|
CoMap,
|
21
|
+
CoFeed,
|
20
22
|
CoStream,
|
21
23
|
CoValueBase,
|
22
24
|
Group,
|
23
25
|
ImageDefinition,
|
24
26
|
Profile,
|
25
27
|
isControlledAccount,
|
28
|
+
SchemaUnion,
|
26
29
|
type AccountClass,
|
27
30
|
type CoMapInit,
|
28
31
|
type CoValueClass,
|
package/src/internal.ts
CHANGED
@@ -5,7 +5,8 @@ export * from "./coValues/interfaces.js";
|
|
5
5
|
export * from "./coValues/coMap.js";
|
6
6
|
export * from "./coValues/account.js";
|
7
7
|
export * from "./coValues/coList.js";
|
8
|
-
export * from "./coValues/
|
8
|
+
export * from "./coValues/coFeed.js";
|
9
|
+
export * from "./coValues/schemaUnion.js";
|
9
10
|
export * from "./coValues/group.js";
|
10
11
|
|
11
12
|
export * from "./implementation/errors.js";
|
@@ -2,8 +2,8 @@ import { connectedPeers } from "cojson/src/streamUtils.ts";
|
|
2
2
|
import { describe, expect, test } from "vitest";
|
3
3
|
import {
|
4
4
|
Account,
|
5
|
-
|
6
|
-
|
5
|
+
CoFeed,
|
6
|
+
FileStream,
|
7
7
|
ID,
|
8
8
|
WasmCrypto,
|
9
9
|
co,
|
@@ -16,7 +16,7 @@ import { randomSessionProvider } from "../internal.js";
|
|
16
16
|
|
17
17
|
const Crypto = await WasmCrypto.create();
|
18
18
|
|
19
|
-
describe("Simple
|
19
|
+
describe("Simple CoFeed operations", async () => {
|
20
20
|
const me = await Account.create({
|
21
21
|
creationProps: { name: "Hermes Puggington" },
|
22
22
|
crypto: Crypto,
|
@@ -24,7 +24,7 @@ describe("Simple CoStream operations", async () => {
|
|
24
24
|
if (!isControlledAccount(me)) {
|
25
25
|
throw "me is not a controlled account";
|
26
26
|
}
|
27
|
-
class TestStream extends
|
27
|
+
class TestStream extends CoFeed.Of(co.string) {}
|
28
28
|
|
29
29
|
const stream = TestStream.create(["milk"], { owner: me });
|
30
30
|
|
@@ -46,16 +46,16 @@ describe("Simple CoStream operations", async () => {
|
|
46
46
|
});
|
47
47
|
});
|
48
48
|
|
49
|
-
describe("
|
50
|
-
class TwiceNestedStream extends
|
49
|
+
describe("CoFeed resolution", async () => {
|
50
|
+
class TwiceNestedStream extends CoFeed.Of(co.string) {
|
51
51
|
fancyValueOf(account: ID<Account>) {
|
52
52
|
return "Sir " + this[account]?.value;
|
53
53
|
}
|
54
54
|
}
|
55
55
|
|
56
|
-
class NestedStream extends
|
56
|
+
class NestedStream extends CoFeed.Of(co.ref(TwiceNestedStream)) {}
|
57
57
|
|
58
|
-
class TestStream extends
|
58
|
+
class TestStream extends CoFeed.Of(co.ref(NestedStream)) {}
|
59
59
|
|
60
60
|
const initNodeAndStream = async () => {
|
61
61
|
const me = await Account.create({
|
@@ -242,13 +242,13 @@ describe("CoStream resolution", async () => {
|
|
242
242
|
});
|
243
243
|
});
|
244
244
|
|
245
|
-
describe("Simple
|
245
|
+
describe("Simple FileStream operations", async () => {
|
246
246
|
const me = await Account.create({
|
247
247
|
creationProps: { name: "Hermes Puggington" },
|
248
248
|
crypto: Crypto,
|
249
249
|
});
|
250
250
|
|
251
|
-
const stream =
|
251
|
+
const stream = FileStream.create({ owner: me });
|
252
252
|
|
253
253
|
test("Construction", () => {
|
254
254
|
expect(stream.getChunks()).toBe(undefined);
|
@@ -270,14 +270,14 @@ describe("Simple BinaryCoStream operations", async () => {
|
|
270
270
|
});
|
271
271
|
});
|
272
272
|
|
273
|
-
describe("
|
273
|
+
describe("FileStream loading & Subscription", async () => {
|
274
274
|
const initNodeAndStream = async () => {
|
275
275
|
const me = await Account.create({
|
276
276
|
creationProps: { name: "Hermes Puggington" },
|
277
277
|
crypto: Crypto,
|
278
278
|
});
|
279
279
|
|
280
|
-
const stream =
|
280
|
+
const stream = FileStream.create({ owner: me });
|
281
281
|
|
282
282
|
stream.start({ mimeType: "text/plain" });
|
283
283
|
stream.push(new Uint8Array([1, 2, 3]));
|
@@ -316,11 +316,7 @@ describe("BinaryCoStream loading & Subscription", async () => {
|
|
316
316
|
crypto: Crypto,
|
317
317
|
});
|
318
318
|
|
319
|
-
const loadedStream = await
|
320
|
-
stream.id,
|
321
|
-
meOnSecondPeer,
|
322
|
-
[],
|
323
|
-
);
|
319
|
+
const loadedStream = await FileStream.load(stream.id, meOnSecondPeer, []);
|
324
320
|
|
325
321
|
expect(loadedStream?.getChunks()).toEqual({
|
326
322
|
mimeType: "text/plain",
|
@@ -331,7 +327,7 @@ describe("BinaryCoStream loading & Subscription", async () => {
|
|
331
327
|
|
332
328
|
test("Subscription", async () => {
|
333
329
|
const { me } = await initNodeAndStream();
|
334
|
-
const stream =
|
330
|
+
const stream = FileStream.create({ owner: me });
|
335
331
|
|
336
332
|
const [initialAsPeer, secondAsPeer] = connectedPeers("initial", "second", {
|
337
333
|
peer1role: "server",
|
@@ -353,14 +349,9 @@ describe("BinaryCoStream loading & Subscription", async () => {
|
|
353
349
|
|
354
350
|
const queue = new cojsonInternals.Channel();
|
355
351
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
[],
|
360
|
-
(subscribedStream) => {
|
361
|
-
void queue.push(subscribedStream);
|
362
|
-
},
|
363
|
-
);
|
352
|
+
FileStream.subscribe(stream.id, meOnSecondPeer, [], (subscribedStream) => {
|
353
|
+
void queue.push(subscribedStream);
|
354
|
+
});
|
364
355
|
|
365
356
|
const update1 = (await queue.next()).value;
|
366
357
|
expect(update1.getChunks()).toBe(undefined);
|
@@ -411,14 +402,14 @@ describe("BinaryCoStream loading & Subscription", async () => {
|
|
411
402
|
});
|
412
403
|
});
|
413
404
|
|
414
|
-
describe("
|
405
|
+
describe("FileStream.loadAsBlob", async () => {
|
415
406
|
async function setup() {
|
416
407
|
const me = await Account.create({
|
417
408
|
creationProps: { name: "Hermes Puggington" },
|
418
409
|
crypto: Crypto,
|
419
410
|
});
|
420
411
|
|
421
|
-
const stream =
|
412
|
+
const stream = FileStream.create({ owner: me });
|
422
413
|
|
423
414
|
stream.start({ mimeType: "text/plain" });
|
424
415
|
|
@@ -429,7 +420,7 @@ describe("BinaryCoStream.loadAsBlob", async () => {
|
|
429
420
|
const { stream, me } = await setup();
|
430
421
|
stream.push(new Uint8Array([1]));
|
431
422
|
|
432
|
-
const promise =
|
423
|
+
const promise = FileStream.loadAsBlob(stream.id, me);
|
433
424
|
|
434
425
|
await stream.ensureLoaded([]);
|
435
426
|
|
@@ -447,7 +438,7 @@ describe("BinaryCoStream.loadAsBlob", async () => {
|
|
447
438
|
const { stream, me } = await setup();
|
448
439
|
stream.push(new Uint8Array([1]));
|
449
440
|
|
450
|
-
const promise =
|
441
|
+
const promise = FileStream.loadAsBlob(stream.id, me, {
|
451
442
|
allowUnfinished: true,
|
452
443
|
});
|
453
444
|
|