jazz-tools 0.8.19 → 0.8.23

Sign up to get free protection for your applications and to get access to all the features.
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