jazz-tools 0.8.31 → 0.8.34

Sign up to get free protection for your applications and to get access to all the features.
@@ -59,34 +59,92 @@ export type SingleCoFeedEntry<Item> = {
59
59
  /** @deprecated Use CoFeed instead */
60
60
  export { CoFeed as CoStream };
61
61
 
62
- /** @category CoValues */
63
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+ /**
63
+ * CoFeeds are collaborative logs of data.
64
+ *
65
+ * @categoryDescription Content
66
+ * They are similar to `CoList`s, but with a few key differences:
67
+ * - They are append-only
68
+ * - They consist of several internal append-only logs, one per account session (tab, device, app instance, etc.)
69
+ * - They expose those as a per-account aggregated view (default) or a precise per-session view
70
+ *
71
+ * ```ts
72
+ * favDog.push("Poodle");
73
+ * favDog.push("Schnowzer");
74
+ * ```
75
+ *
76
+ * @category CoValues
77
+ */
64
78
  export class CoFeed<Item = any> extends CoValueBase implements CoValue {
79
+ /**
80
+ * Declare a `CoFeed` by subclassing `CoFeed.Of(...)` and passing the item schema using a `co` primitive or a `co.ref`.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * class ColorFeed extends CoFeed.Of(co.string) {}
85
+ * class AnimalFeed extends CoFeed.Of(co.ref(Animal)) {}
86
+ * ```
87
+ *
88
+ * @category Declaration
89
+ */
65
90
  static Of<Item>(item: IfCo<Item, Item>): typeof CoFeed<Item> {
66
91
  return class CoFeedOf extends CoFeed<Item> {
67
92
  [co.items] = item;
68
93
  };
69
94
  }
70
95
 
96
+ /**
97
+ * The ID of this `CoFeed`
98
+ * @category Content */
71
99
  declare id: ID<this>;
100
+ /** @category Type Helpers */
72
101
  declare _type: "CoStream";
73
102
  static {
74
103
  this.prototype._type = "CoStream";
75
104
  }
105
+ /** @category Internals */
76
106
  declare _raw: RawCoStream;
77
107
 
78
108
  /** @internal This is only a marker type and doesn't exist at runtime */
79
109
  [ItemsSym]!: Item;
110
+ /** @internal */
80
111
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
112
  static _schema: any;
113
+ /** @internal */
82
114
  get _schema(): {
83
115
  [ItemsSym]: SchemaFor<Item>;
84
116
  } {
85
117
  return (this.constructor as typeof CoFeed)._schema;
86
118
  }
87
119
 
120
+ /**
121
+ * The per-account view of this `CoFeed`
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * // Access entries directly by account ID
126
+ * const aliceEntries = feed[aliceAccount.id];
127
+ * console.log(aliceEntries.value); // Latest value from Alice
128
+ *
129
+ * // Iterate through all accounts' entries
130
+ * for (const [accountId, entries] of Object.entries(feed)) {
131
+ * console.log(`Latest entry from ${accountId}:`, entries.value);
132
+ *
133
+ * // Access all entries from this account
134
+ * for (const entry of entries.all) {
135
+ * console.log(`Entry made at ${entry.madeAt}:`, entry.value);
136
+ * }
137
+ * }
138
+ * ```
139
+ *
140
+ * @category Content
141
+ */
88
142
  [key: ID<Account>]: CoFeedEntry<Item>;
89
143
 
144
+ /**
145
+ * The current account's view of this `CoFeed`
146
+ * @category Content
147
+ */
90
148
  get byMe(): CoFeedEntry<Item> | undefined {
91
149
  if (this._loadedAs._type === "Account") {
92
150
  return this[this._loadedAs.id];
@@ -94,9 +152,22 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
94
152
  return undefined;
95
153
  }
96
154
  }
155
+
156
+ /**
157
+ * The per-session view of this `CoFeed`
158
+ * @category Content
159
+ */
97
160
  perSession!: {
98
161
  [key: SessionID]: CoFeedEntry<Item>;
99
162
  };
163
+
164
+ /**
165
+ * The current session's view of this `CoFeed`
166
+ *
167
+ * This is a shortcut for `this.perSession` where the session ID is the current session ID.
168
+ *
169
+ * @category Content
170
+ */
100
171
  get inCurrentSession(): CoFeedEntry<Item> | undefined {
101
172
  if (this._loadedAs._type === "Account") {
102
173
  return this.perSession[this._loadedAs.sessionID!];
@@ -125,6 +196,10 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
125
196
  return new Proxy(this, CoStreamProxyHandler as ProxyHandler<this>);
126
197
  }
127
198
 
199
+ /**
200
+ * Create a new `CoFeed`
201
+ * @category Creation
202
+ */
128
203
  static create<S extends CoFeed>(
129
204
  this: CoValueClass<S>,
130
205
  init: S extends CoFeed<infer Item> ? UnCo<Item>[] : never,
@@ -147,6 +222,26 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
147
222
  return instance;
148
223
  }
149
224
 
225
+ /**
226
+ * Push items to this `CoFeed`
227
+ *
228
+ * Items are appended to the current session's log. Each session (tab, device, app instance)
229
+ * maintains its own append-only log, which is then aggregated into the per-account view.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * // Adds items to current session's log
234
+ * feed.push("item1", "item2");
235
+ *
236
+ * // View items from current session
237
+ * console.log(feed.inCurrentSession);
238
+ *
239
+ * // View aggregated items from all sessions for current account
240
+ * console.log(feed.byMe);
241
+ * ```
242
+ *
243
+ * @category Content
244
+ */
150
245
  push(...items: Item[]) {
151
246
  for (const item of items) {
152
247
  this.pushItem(item);
@@ -165,6 +260,10 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
165
260
  }
166
261
  }
167
262
 
263
+ /**
264
+ * Get a JSON representation of the `CoFeed`
265
+ * @category
266
+ */
168
267
  toJSON(): {
169
268
  id: string;
170
269
  _type: "CoStream";
@@ -197,6 +296,7 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
197
296
  };
198
297
  }
199
298
 
299
+ /** @internal */
200
300
  [inspect](): {
201
301
  id: string;
202
302
  _type: "CoStream";
@@ -206,6 +306,7 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
206
306
  return this.toJSON();
207
307
  }
208
308
 
309
+ /** @internal */
209
310
  static schema<V extends CoFeed>(
210
311
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
211
312
  this: { new (...args: any): V } & typeof CoFeed,
@@ -215,7 +316,10 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
215
316
  Object.assign(this._schema, def);
216
317
  }
217
318
 
218
- /** @category Subscription & Loading */
319
+ /**
320
+ * Load a `CoFeed`
321
+ * @category Subscription & Loading
322
+ */
219
323
  static load<S extends CoFeed, Depth>(
220
324
  this: CoValueClass<S>,
221
325
  id: ID<S>,
@@ -225,7 +329,10 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
225
329
  return loadCoValue(this, id, as, depth);
226
330
  }
227
331
 
228
- /** @category Subscription & Loading */
332
+ /**
333
+ * Subscribe to a `CoFeed`, when you have an ID but don't have a `CoFeed` instance yet
334
+ * @category Subscription & Loading
335
+ */
229
336
  static subscribe<S extends CoFeed, Depth>(
230
337
  this: CoValueClass<S>,
231
338
  id: ID<S>,
@@ -236,7 +343,13 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
236
343
  return subscribeToCoValue<S, Depth>(this, id, as, depth, listener);
237
344
  }
238
345
 
239
- /** @category Subscription & Loading */
346
+ /**
347
+ * Ensure a `CoFeed` is loaded to the specified depth
348
+ *
349
+ * @returns A new instance of the same CoFeed that's loaded to the specified depth,
350
+ * or undefined if it cannot be loaded that deeply
351
+ * @category Subscription & Loading
352
+ */
240
353
  ensureLoaded<S extends CoFeed, Depth>(
241
354
  this: S,
242
355
  depth: Depth & DepthsIn<S>,
@@ -244,7 +357,12 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
244
357
  return ensureCoValueLoaded(this, depth);
245
358
  }
246
359
 
247
- /** @category Subscription & Loading */
360
+ /**
361
+ * An instance method to subscribe to an existing `CoFeed`
362
+ *
363
+ * No need to provide an ID or Account since they're already part of the instance.
364
+ * @category Subscription & Loading
365
+ */
248
366
  subscribe<S extends CoFeed, Depth>(
249
367
  this: S,
250
368
  depth: Depth & DepthsIn<S>,
@@ -254,6 +372,10 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
254
372
  }
255
373
  }
256
374
 
375
+ /**
376
+ * Converts a raw stream entry into a formatted CoFeed entry with proper typing and accessors.
377
+ * @internal
378
+ */
257
379
  function entryFromRawEntry<Item>(
258
380
  accessFrom: CoValue,
259
381
  rawEntry: {
@@ -316,6 +438,10 @@ function entryFromRawEntry<Item>(
316
438
  };
317
439
  }
318
440
 
441
+ /**
442
+ * The proxy handler for `CoFeed` instances
443
+ * @internal
444
+ */
319
445
  export const CoStreamProxyHandler: ProxyHandler<CoFeed> = {
320
446
  get(target, key, receiver) {
321
447
  if (typeof key === "string" && key.startsWith("co_")) {
@@ -404,6 +530,10 @@ export const CoStreamProxyHandler: ProxyHandler<CoFeed> = {
404
530
  },
405
531
  };
406
532
 
533
+ /**
534
+ * The proxy handler for the per-session view of a `CoFeed`
535
+ * @internal
536
+ */
407
537
  const CoStreamPerSessionProxyHandler = (
408
538
  innerTarget: CoFeed,
409
539
  accessFrom: CoFeed,
@@ -469,13 +599,34 @@ const CoStreamPerSessionProxyHandler = (
469
599
  },
470
600
  });
471
601
 
472
- /** @deprecated Use CoFeed instead */
602
+ /** @deprecated Use FileStream instead */
473
603
  export { FileStream as BinaryCoStream };
474
604
 
475
- /** @category CoValues */
605
+ /**
606
+ * FileStreams are `CoFeed`s that contain binary data, collaborative versions of `Blob`s.
607
+ *
608
+ * @categoryDescription Declaration
609
+ * `FileStream` can be referenced in schemas.
610
+ *
611
+ * ```ts
612
+ * import { co, FileStream } from "jazz-tools";
613
+ *
614
+ * class MyCoMap extends CoMap {
615
+ * file = co.ref(FileStream);
616
+ * }
617
+ * ```
618
+ *
619
+ * @category CoValues
620
+ */
476
621
  export class FileStream extends CoValueBase implements CoValue {
622
+ /**
623
+ * The ID of this `FileStream`
624
+ * @category Content
625
+ */
477
626
  declare id: ID<this>;
627
+ /** @category Type Helpers */
478
628
  declare _type: "BinaryCoStream";
629
+ /** @internal */
479
630
  declare _raw: RawBinaryCoStream;
480
631
 
481
632
  constructor(
@@ -552,6 +703,11 @@ export class FileStream extends CoValueBase implements CoValue {
552
703
  return new Blob(chunks.chunks, { type: chunks.mimeType });
553
704
  }
554
705
 
706
+ /**
707
+ * Load a `FileStream` as a `Blob`
708
+ *
709
+ * @category Content
710
+ */
555
711
  static async loadAsBlob(
556
712
  id: ID<FileStream>,
557
713
  as: Account,
@@ -581,6 +737,17 @@ export class FileStream extends CoValueBase implements CoValue {
581
737
  });
582
738
  }
583
739
 
740
+ /**
741
+ * Create a `FileStream` from a `Blob` or `File`
742
+ *
743
+ * @example
744
+ * ```ts
745
+ * import { co, FileStream } from "jazz-tools";
746
+ *
747
+ * const fileStream = await FileStream.createFromBlob(file, {owner: group})
748
+ * ```
749
+ * @category Content
750
+ */
584
751
  static async createFromBlob(
585
752
  blob: Blob | File,
586
753
  options: {
@@ -626,6 +793,10 @@ export class FileStream extends CoValueBase implements CoValue {
626
793
  return stream;
627
794
  }
628
795
 
796
+ /**
797
+ * Get a JSON representation of the `FileStream`
798
+ * @category Content
799
+ */
629
800
  toJSON(): {
630
801
  id: string;
631
802
  _type: "BinaryCoStream";
@@ -642,11 +813,15 @@ export class FileStream extends CoValueBase implements CoValue {
642
813
  };
643
814
  }
644
815
 
816
+ /** @internal */
645
817
  [inspect]() {
646
818
  return this.toJSON();
647
819
  }
648
820
 
649
- /** @category Subscription & Loading */
821
+ /**
822
+ * Load a `FileStream`
823
+ * @category Subscription & Loading
824
+ */
650
825
  static load<B extends FileStream, Depth>(
651
826
  this: CoValueClass<B>,
652
827
  id: ID<B>,
@@ -656,7 +831,10 @@ export class FileStream extends CoValueBase implements CoValue {
656
831
  return loadCoValue(this, id, as, depth);
657
832
  }
658
833
 
659
- /** @category Subscription & Loading */
834
+ /**
835
+ * Subscribe to a `FileStream`, when you have an ID but don't have a `FileStream` instance yet
836
+ * @category Subscription & Loading
837
+ */
660
838
  static subscribe<B extends FileStream, Depth>(
661
839
  this: CoValueClass<B>,
662
840
  id: ID<B>,
@@ -667,7 +845,6 @@ export class FileStream extends CoValueBase implements CoValue {
667
845
  return subscribeToCoValue<B, Depth>(this, id, as, depth, listener);
668
846
  }
669
847
 
670
- /** @category Subscription & Loading */
671
848
  ensureLoaded<B extends FileStream, Depth>(
672
849
  this: B,
673
850
  depth: Depth & DepthsIn<B>,
@@ -675,7 +852,10 @@ export class FileStream extends CoValueBase implements CoValue {
675
852
  return ensureCoValueLoaded(this, depth);
676
853
  }
677
854
 
678
- /** @category Subscription & Loading */
855
+ /**
856
+ * An instance method to subscribe to an existing `FileStream`
857
+ * @category Subscription & Loading
858
+ */
679
859
  subscribe<B extends FileStream, Depth>(
680
860
  this: B,
681
861
  depth: Depth & DepthsIn<B>,
@@ -1,6 +1,9 @@
1
1
  import {
2
+ AgentID,
2
3
  type CoValueUniqueness,
4
+ CojsonInternalTypes,
3
5
  type JsonValue,
6
+ RawAccountID,
4
7
  type RawCoMap,
5
8
  cojsonInternals,
6
9
  } from "cojson";
@@ -41,6 +44,8 @@ type CoMapEdit<V> = {
41
44
  madeAt: Date;
42
45
  };
43
46
 
47
+ type LastAndAllCoMapEdits<V> = CoMapEdit<V> & { all: CoMapEdit<V>[] };
48
+
44
49
  export type Simplify<A> = {
45
50
  [K in keyof A]: A[K];
46
51
  } extends infer B
@@ -141,47 +146,82 @@ export class CoMap extends CoValueBase implements CoValue {
141
146
  ) as any;
142
147
  }
143
148
 
149
+ /** @internal */
150
+ private getEditFromRaw(
151
+ target: CoMap,
152
+ rawEdit: {
153
+ by: RawAccountID | AgentID;
154
+ tx: CojsonInternalTypes.TransactionID;
155
+ at: Date;
156
+ value?: JsonValue | undefined;
157
+ },
158
+ descriptor: Schema,
159
+ key: string,
160
+ ) {
161
+ return {
162
+ value:
163
+ descriptor === "json"
164
+ ? rawEdit.value
165
+ : "encoded" in descriptor
166
+ ? rawEdit.value === null || rawEdit.value === undefined
167
+ ? rawEdit.value
168
+ : descriptor.encoded.decode(rawEdit.value)
169
+ : new Ref(
170
+ rawEdit.value as ID<CoValue>,
171
+ target._loadedAs,
172
+ descriptor,
173
+ ).accessFrom(target, "_edits." + key + ".value"),
174
+ ref:
175
+ descriptor !== "json" && isRefEncoded(descriptor)
176
+ ? new Ref(rawEdit.value as ID<CoValue>, target._loadedAs, descriptor)
177
+ : undefined,
178
+ by:
179
+ rawEdit.by &&
180
+ new Ref<Account>(rawEdit.by as ID<Account>, target._loadedAs, {
181
+ ref: Account,
182
+ optional: false,
183
+ }).accessFrom(target, "_edits." + key + ".by"),
184
+ madeAt: rawEdit.at,
185
+ };
186
+ }
187
+
144
188
  /** @category Collaboration */
145
189
  get _edits() {
146
- return new Proxy(this, {
147
- get(target, key) {
148
- const rawEdit = target._raw.lastEditAt(key as string);
149
- if (!rawEdit) return undefined;
150
-
151
- const descriptor = target._schema[
152
- key as keyof typeof target._schema
153
- ] as Schema;
154
-
155
- return {
156
- value:
157
- descriptor === "json"
158
- ? rawEdit.value
159
- : "encoded" in descriptor
160
- ? descriptor.encoded.encode(rawEdit.value)
161
- : new Ref(
162
- rawEdit.value as ID<CoValue>,
163
- target._loadedAs,
164
- descriptor,
165
- ).accessFrom(target, "_edits." + key.toString() + ".value"),
166
- ref:
167
- descriptor !== "json" && isRefEncoded(descriptor)
168
- ? new Ref(
169
- rawEdit.value as ID<CoValue>,
170
- target._loadedAs,
171
- descriptor,
172
- )
173
- : undefined,
174
- by:
175
- rawEdit.by &&
176
- new Ref<Account>(rawEdit.by as ID<Account>, target._loadedAs, {
177
- ref: Account,
178
- optional: false,
179
- }).accessFrom(target, "_edits." + key.toString() + ".by"),
180
- madeAt: rawEdit.at,
181
- };
190
+ const map = this;
191
+ return new Proxy(
192
+ {},
193
+ {
194
+ get(_target, key) {
195
+ const rawEdit = map._raw.lastEditAt(key as string);
196
+ if (!rawEdit) return undefined;
197
+
198
+ const descriptor = map._schema[
199
+ key as keyof typeof map._schema
200
+ ] as Schema;
201
+
202
+ return {
203
+ ...map.getEditFromRaw(map, rawEdit, descriptor, key as string),
204
+ get all() {
205
+ return [...map._raw.editsAt(key as string)].map((rawEdit) =>
206
+ map.getEditFromRaw(map, rawEdit, descriptor, key as string),
207
+ );
208
+ },
209
+ };
210
+ },
211
+ ownKeys(_target) {
212
+ return map._raw.keys();
213
+ },
214
+ getOwnPropertyDescriptor(target, key) {
215
+ return {
216
+ value: Reflect.get(target, key),
217
+ writable: false,
218
+ enumerable: true,
219
+ configurable: true,
220
+ };
221
+ },
182
222
  },
183
- }) as {
184
- [Key in CoKeys<this>]: IfCo<this[Key], CoMapEdit<this[Key]>>;
223
+ ) as {
224
+ [Key in CoKeys<this>]: IfCo<this[Key], LastAndAllCoMapEdits<this[Key]>>;
185
225
  };
186
226
  }
187
227
 
@@ -251,6 +291,10 @@ export class CoMap extends CoValueBase implements CoValue {
251
291
  return instance;
252
292
  }
253
293
 
294
+ /**
295
+ * Return a JSON representation of the `CoMap`
296
+ * @category Content
297
+ */
254
298
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
255
299
  toJSON(_key?: string, seenAbove?: ID<CoValue>[]): any[] {
256
300
  const jsonedFields = this._raw.keys().map((key) => {
@@ -288,7 +332,10 @@ export class CoMap extends CoValueBase implements CoValue {
288
332
  return this.toJSON();
289
333
  }
290
334
 
291
- /** @internal */
335
+ /**
336
+ * Create a new `RawCoMap` from an initialization object
337
+ * @internal
338
+ */
292
339
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
293
340
  rawFromInit<Fields extends object = Record<string, any>>(
294
341
  init: Simplify<CoMapInit<Fields>> | undefined,
@@ -58,7 +58,18 @@ export function isCoValueClass<V extends CoValue>(
58
58
  return typeof value === "function" && value.fromRaw !== undefined;
59
59
  }
60
60
 
61
- /** @category CoValues */
61
+ /**
62
+ * IDs are unique identifiers for `CoValue`s.
63
+ * Can be used with a type argument to refer to a specific `CoValue` type.
64
+ *
65
+ * @example
66
+ *
67
+ * ```ts
68
+ * type AccountID = ID<Account>;
69
+ * ```
70
+ *
71
+ * @category CoValues
72
+ */
62
73
  export type ID<T> = CojsonInternalTypes.RawCoID & IDMarker<T>;
63
74
 
64
75
  type IDMarker<out T> = { __type(_: never): T };
@@ -9,10 +9,8 @@ import {
9
9
  * SchemaUnion allows you to create union types of CoValues that can be discriminated at runtime.
10
10
  *
11
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.
12
+ * Declare your union types by extending `SchemaUnion.Of(...)` and passing a discriminator function that determines which concrete type to use based on the raw data.
14
13
  *
15
- * @example
16
14
  * ```ts
17
15
  * import { SchemaUnion, CoMap } from "jazz-tools";
18
16
  *
@@ -40,28 +38,15 @@ import {
40
38
  * });
41
39
  * ```
42
40
  *
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
41
  * @category CoValues
58
42
  */
59
43
  export abstract class SchemaUnion extends CoValueBase implements CoValue {
60
44
  /**
61
45
  * Create a new union type from a discriminator function.
62
46
  *
63
- * The discriminator function receives the raw data and should return the appropriate
64
- * concrete class to use for that data.
47
+ * The discriminator function receives the raw data and should return the appropriate concrete class to use for that data.
48
+ *
49
+ * When loading a SchemaUnion, the correct subclass will be instantiated based on the discriminator.
65
50
  *
66
51
  * @param discriminator - Function that determines which concrete type to use
67
52
  * @returns A new class that can create/load instances of the union type
@@ -75,10 +60,19 @@ export abstract class SchemaUnion extends CoValueBase implements CoValue {
75
60
  * default: throw new Error("Unknown widget type");
76
61
  * }
77
62
  * });
63
+ *
64
+ * const widget = await loadCoValue(WidgetUnion, id, me, {});
65
+ *
66
+ * // You can narrow the returned instance to a subclass by using `instanceof`
67
+ * if (widget instanceof ButtonWidget) {
68
+ * console.log(widget.label);
69
+ * } else if (widget instanceof SliderWidget) {
70
+ * console.log(widget.min, widget.max);
71
+ * }
78
72
  * ```
79
73
  *
80
- * @category Creation
81
- */
74
+ * @category Declaration
75
+ **/
82
76
  static Of<V extends CoValue>(
83
77
  discriminator: (raw: V["_raw"]) => CoValueClass<V> & CoValueFromRaw<V>,
84
78
  ): CoValueClass<V> & typeof SchemaUnion {