jazz-tools 0.8.19 → 0.8.23

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 (67) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/native/coValues/account.js +15 -17
  3. package/dist/native/coValues/account.js.map +1 -1
  4. package/dist/native/coValues/{coStream.js → coFeed.js} +21 -14
  5. package/dist/native/coValues/coFeed.js.map +1 -0
  6. package/dist/native/coValues/coList.js +16 -13
  7. package/dist/native/coValues/coList.js.map +1 -1
  8. package/dist/native/coValues/coMap.js +14 -10
  9. package/dist/native/coValues/coMap.js.map +1 -1
  10. package/dist/native/coValues/deepLoading.js.map +1 -1
  11. package/dist/native/coValues/extensions/imageDef.js +9 -4
  12. package/dist/native/coValues/extensions/imageDef.js.map +1 -1
  13. package/dist/native/coValues/group.js +23 -20
  14. package/dist/native/coValues/group.js.map +1 -1
  15. package/dist/native/coValues/schemaUnion.js +93 -0
  16. package/dist/native/coValues/schemaUnion.js.map +1 -0
  17. package/dist/native/exports.js +1 -1
  18. package/dist/native/exports.js.map +1 -1
  19. package/dist/native/implementation/createContext.js +1 -2
  20. package/dist/native/implementation/createContext.js.map +1 -1
  21. package/dist/native/implementation/refs.js +0 -3
  22. package/dist/native/implementation/refs.js.map +1 -1
  23. package/dist/native/implementation/subscriptionScope.js +5 -8
  24. package/dist/native/implementation/subscriptionScope.js.map +1 -1
  25. package/dist/native/internal.js +2 -1
  26. package/dist/native/internal.js.map +1 -1
  27. package/dist/web/coValues/account.js +15 -17
  28. package/dist/web/coValues/account.js.map +1 -1
  29. package/dist/web/coValues/{coStream.js → coFeed.js} +21 -14
  30. package/dist/web/coValues/coFeed.js.map +1 -0
  31. package/dist/web/coValues/coList.js +16 -13
  32. package/dist/web/coValues/coList.js.map +1 -1
  33. package/dist/web/coValues/coMap.js +14 -10
  34. package/dist/web/coValues/coMap.js.map +1 -1
  35. package/dist/web/coValues/deepLoading.js.map +1 -1
  36. package/dist/web/coValues/extensions/imageDef.js +9 -4
  37. package/dist/web/coValues/extensions/imageDef.js.map +1 -1
  38. package/dist/web/coValues/group.js +23 -20
  39. package/dist/web/coValues/group.js.map +1 -1
  40. package/dist/web/coValues/schemaUnion.js +93 -0
  41. package/dist/web/coValues/schemaUnion.js.map +1 -0
  42. package/dist/web/exports.js +1 -1
  43. package/dist/web/exports.js.map +1 -1
  44. package/dist/web/implementation/createContext.js +1 -2
  45. package/dist/web/implementation/createContext.js.map +1 -1
  46. package/dist/web/implementation/refs.js +0 -3
  47. package/dist/web/implementation/refs.js.map +1 -1
  48. package/dist/web/implementation/subscriptionScope.js +5 -8
  49. package/dist/web/implementation/subscriptionScope.js.map +1 -1
  50. package/dist/web/internal.js +2 -1
  51. package/dist/web/internal.js.map +1 -1
  52. package/package.json +2 -2
  53. package/src/coValues/{coStream.ts → coFeed.ts} +50 -38
  54. package/src/coValues/coList.ts +2 -2
  55. package/src/coValues/coMap.ts +2 -2
  56. package/src/coValues/deepLoading.ts +6 -6
  57. package/src/coValues/extensions/imageDef.ts +4 -9
  58. package/src/coValues/schemaUnion.ts +106 -0
  59. package/src/exports.ts +3 -0
  60. package/src/internal.ts +2 -1
  61. package/src/tests/{coStream.test.ts → coFeed.test.ts} +21 -30
  62. package/src/tests/deepLoading.test.ts +2 -2
  63. package/src/tests/schemaUnion.test.ts +118 -0
  64. package/src/tests/subscribe.test.ts +4 -4
  65. package/tsconfig.json +1 -1
  66. package/dist/native/coValues/coStream.js.map +0 -1
  67. 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
- export type CoStreamEntry<Item> = SingleCoStreamEntry<Item> & {
42
- all: IterableIterator<SingleCoStreamEntry<Item>>;
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
- export type SingleCoStreamEntry<Item> = {
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 CoStream<Item = any> extends CoValueBase implements CoValue {
56
- static Of<Item>(item: IfCo<Item, Item>): typeof CoStream<Item> {
57
- return class CoStreamOf extends CoStream<Item> {
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 CoStream)._schema;
85
+ return (this.constructor as typeof CoFeed)._schema;
77
86
  }
78
87
 
79
- [key: ID<Account>]: CoStreamEntry<Item>;
88
+ [key: ID<Account>]: CoFeedEntry<Item>;
80
89
 
81
- get byMe(): CoStreamEntry<Item> | undefined {
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]: CoStreamEntry<Item>;
98
+ [key: SessionID]: CoFeedEntry<Item>;
90
99
  };
91
- get inCurrentSession(): CoStreamEntry<Item> | undefined {
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 CoStream>(
128
+ static create<S extends CoFeed>(
120
129
  this: CoValueClass<S>,
121
- init: S extends CoStream<infer Item> ? UnCo<Item>[] : never,
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 CoStream>(
209
+ static schema<V extends CoFeed>(
201
210
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
202
- this: { new (...args: any): V } & typeof CoStream,
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 CoStream, Depth>(
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 CoStream, Depth>(
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 CoStream, Depth>(
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 CoStream, Depth>(
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<CoStreamEntry<Item>, "all"> {
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<CoStream> = {
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<SingleCoStreamEntry<any>>;
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 CoStream)._schema ||= {};
354
- (target.constructor as typeof CoStream)._schema[ItemsSym] =
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 CoStream)._schema ||= {};
369
- (target.constructor as typeof CoStream)._schema[ItemsSym] =
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: CoStream,
400
- accessFrom: CoStream,
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<SingleCoStreamEntry<any>>;
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 BinaryCoStream extends CoValueBase implements CoValue {
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 BinaryCoStream>(
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<BinaryCoStream>,
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<BinaryCoStream>((resolve) => {
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<BinaryCoStream> {
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 BinaryCoStream, Depth>(
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 BinaryCoStream, Depth>(
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 BinaryCoStream, Depth>(
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 BinaryCoStream, Depth>(
679
+ subscribe<B extends FileStream, Depth>(
668
680
  this: B,
669
681
  depth: Depth & DepthsIn<B>,
670
682
  listener: (value: DeeplyLoaded<B, Depth>) => void,
@@ -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`/`CoStream`/`Group`/`Account` to see which depth structures are valid to nest.
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`/`CoStream`/`Group`/`Account` to see which depth structures are valid to nest.
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
  *
@@ -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`/`CoStream`/`Group`/`Account` to see which depth structures are valid to nest.
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`/`CoStream`/`Group`/`Account` to see which depth structures are valid to nest.
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 CoStream).perSession).every((entry) =>
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 CoStream)._schema[ItemsSym] as RefEncoded<CoValue>)
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: CoStreamEntry<infer Item> | undefined;
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: CoStreamEntry<infer Item> | undefined;
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(BinaryCoStream);
14
- [res: `${number}x${number}`]: co<BinaryCoStream | null>;
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: BinaryCoStream } | undefined {
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,106 @@
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
+ * ```ts
16
+ * import { SchemaUnion, CoMap } from "jazz-tools";
17
+ *
18
+ * class BaseWidget extends CoMap {
19
+ * type = co.string;
20
+ * }
21
+ *
22
+ * class ButtonWidget extends BaseWidget {
23
+ * type = co.literal("button");
24
+ * label = co.string;
25
+ * }
26
+ *
27
+ * class SliderWidget extends BaseWidget {
28
+ * type = co.literal("slider");
29
+ * min = co.number;
30
+ * max = co.number;
31
+ * }
32
+ *
33
+ * const WidgetUnion = SchemaUnion.Of<BaseWidget>((raw) => {
34
+ * switch (raw.get("type")) {
35
+ * case "button": return ButtonWidget;
36
+ * case "slider": return SliderWidget;
37
+ * default: throw new Error("Unknown widget type");
38
+ * }
39
+ * });
40
+ * ```
41
+ *
42
+ * @categoryDescription Loading
43
+ * When loading a SchemaUnion, the correct subclass will be instantiated based on the discriminator.
44
+ * You can narrow the returned instance to a subclass by using `instanceof` like so:
45
+ *
46
+ * ```ts
47
+ * const widget = await WidgetUnion.load(id, me, {});
48
+ * if (widget instanceof ButtonWidget) {
49
+ * console.log(widget.label);
50
+ * } else if (widget instanceof SliderWidget) {
51
+ * console.log(widget.min, widget.max);
52
+ * }
53
+ * ```
54
+ *
55
+ * @category CoValues
56
+ */
57
+ export abstract class SchemaUnion extends CoValueBase implements CoValue {
58
+ /**
59
+ * Create a new union type from a discriminator function.
60
+ *
61
+ * The discriminator function receives the raw data and should return the appropriate
62
+ * concrete class to use for that data.
63
+ *
64
+ * @param discriminator - Function that determines which concrete type to use
65
+ * @returns A new class that can create/load instances of the union type
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * const WidgetUnion = SchemaUnion.Of<BaseWidget>((raw) => {
70
+ * switch (raw.get("type")) {
71
+ * case "button": return ButtonWidget;
72
+ * case "slider": return SliderWidget;
73
+ * default: throw new Error("Unknown widget type");
74
+ * }
75
+ * });
76
+ * ```
77
+ *
78
+ * @category Creation
79
+ */
80
+ static Of<V extends CoValue>(
81
+ discriminator: (raw: V["_raw"]) => CoValueClass<V> & CoValueFromRaw<V>,
82
+ ): CoValueClass<V> & typeof SchemaUnion {
83
+ return class SchemaUnionClass extends SchemaUnion {
84
+ static override fromRaw<T extends CoValue>(
85
+ this: CoValueClass<T> & CoValueFromRaw<T>,
86
+ raw: T["_raw"],
87
+ ): T {
88
+ const ResolvedClass = discriminator(
89
+ raw as V["_raw"],
90
+ ) as unknown as CoValueClass<T> & CoValueFromRaw<T>;
91
+ return ResolvedClass.fromRaw(raw);
92
+ }
93
+ } as unknown as CoValueClass<V> & typeof SchemaUnion;
94
+ }
95
+
96
+ /**
97
+ * Create an instance from raw data. This is called internally and should not be used directly.
98
+ * Use {@link SchemaUnion.Of} to create a union type instead.
99
+ *
100
+ * @internal
101
+ */
102
+ // @ts-ignore
103
+ static fromRaw<V extends CoValue>(this: CoValueClass<V>, raw: V["_raw"]): V {
104
+ throw new Error("Not implemented");
105
+ }
106
+ }
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/coStream.js";
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
- BinaryCoStream,
6
- CoStream,
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 CoStream operations", async () => {
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 CoStream.Of(co.string) {}
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("CoStream resolution", async () => {
50
- class TwiceNestedStream extends CoStream.Of(co.string) {
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 CoStream.Of(co.ref(TwiceNestedStream)) {}
56
+ class NestedStream extends CoFeed.Of(co.ref(TwiceNestedStream)) {}
57
57
 
58
- class TestStream extends CoStream.Of(co.ref(NestedStream)) {}
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 BinaryCoStream operations", async () => {
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 = BinaryCoStream.create({ owner: me });
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("BinaryCoStream loading & Subscription", async () => {
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 = BinaryCoStream.create({ owner: me });
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 BinaryCoStream.load(
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 = BinaryCoStream.create({ owner: me });
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
- BinaryCoStream.subscribe(
357
- stream.id,
358
- meOnSecondPeer,
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("BinaryCoStream.loadAsBlob", async () => {
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 = BinaryCoStream.create({ owner: me });
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 = BinaryCoStream.loadAsBlob(stream.id, me);
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 = BinaryCoStream.loadAsBlob(stream.id, me, {
441
+ const promise = FileStream.loadAsBlob(stream.id, me, {
451
442
  allowUnfinished: true,
452
443
  });
453
444