jazz-tools 0.7.0-alpha.9 → 0.7.3

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 (73) hide show
  1. package/.eslintrc.cjs +3 -10
  2. package/.prettierrc.js +9 -0
  3. package/.turbo/turbo-build.log +14 -15
  4. package/.turbo/turbo-lint.log +4 -0
  5. package/.turbo/turbo-test.log +140 -0
  6. package/CHANGELOG.md +331 -27
  7. package/LICENSE.txt +1 -1
  8. package/README.md +10 -2
  9. package/dist/coValues/account.js +86 -41
  10. package/dist/coValues/account.js.map +1 -1
  11. package/dist/coValues/coList.js +75 -48
  12. package/dist/coValues/coList.js.map +1 -1
  13. package/dist/coValues/coMap.js +167 -44
  14. package/dist/coValues/coMap.js.map +1 -1
  15. package/dist/coValues/coStream.js +192 -35
  16. package/dist/coValues/coStream.js.map +1 -1
  17. package/dist/coValues/deepLoading.js +60 -0
  18. package/dist/coValues/deepLoading.js.map +1 -0
  19. package/dist/coValues/extensions/imageDef.js +10 -7
  20. package/dist/coValues/extensions/imageDef.js.map +1 -1
  21. package/dist/coValues/group.js +73 -13
  22. package/dist/coValues/group.js.map +1 -1
  23. package/dist/coValues/interfaces.js +58 -35
  24. package/dist/coValues/interfaces.js.map +1 -1
  25. package/dist/implementation/devtoolsFormatters.js +114 -0
  26. package/dist/implementation/devtoolsFormatters.js.map +1 -0
  27. package/dist/implementation/refs.js +58 -18
  28. package/dist/implementation/refs.js.map +1 -1
  29. package/dist/implementation/schema.js +58 -0
  30. package/dist/implementation/schema.js.map +1 -0
  31. package/dist/implementation/subscriptionScope.js +19 -1
  32. package/dist/implementation/subscriptionScope.js.map +1 -1
  33. package/dist/implementation/symbols.js +5 -0
  34. package/dist/implementation/symbols.js.map +1 -0
  35. package/dist/index.js +4 -5
  36. package/dist/index.js.map +1 -1
  37. package/dist/internal.js +5 -2
  38. package/dist/internal.js.map +1 -1
  39. package/dist/tests/coList.test.js +51 -48
  40. package/dist/tests/coList.test.js.map +1 -1
  41. package/dist/tests/coMap.test.js +131 -74
  42. package/dist/tests/coMap.test.js.map +1 -1
  43. package/dist/tests/coStream.test.js +56 -41
  44. package/dist/tests/coStream.test.js.map +1 -1
  45. package/dist/tests/deepLoading.test.js +188 -0
  46. package/dist/tests/deepLoading.test.js.map +1 -0
  47. package/dist/tests/groupsAndAccounts.test.js +83 -0
  48. package/dist/tests/groupsAndAccounts.test.js.map +1 -0
  49. package/package.json +17 -9
  50. package/src/coValues/account.ts +186 -128
  51. package/src/coValues/coList.ts +156 -107
  52. package/src/coValues/coMap.ts +272 -148
  53. package/src/coValues/coStream.ts +388 -87
  54. package/src/coValues/deepLoading.ts +229 -0
  55. package/src/coValues/extensions/imageDef.ts +17 -13
  56. package/src/coValues/group.ts +166 -59
  57. package/src/coValues/interfaces.ts +189 -160
  58. package/src/implementation/devtoolsFormatters.ts +110 -0
  59. package/src/implementation/inspect.ts +1 -1
  60. package/src/implementation/refs.ts +80 -28
  61. package/src/implementation/schema.ts +141 -0
  62. package/src/implementation/subscriptionScope.ts +48 -12
  63. package/src/implementation/symbols.ts +11 -0
  64. package/src/index.ts +19 -8
  65. package/src/internal.ts +7 -3
  66. package/src/tests/coList.test.ts +77 -62
  67. package/src/tests/coMap.test.ts +201 -114
  68. package/src/tests/coStream.test.ts +113 -84
  69. package/src/tests/deepLoading.test.ts +304 -0
  70. package/src/tests/groupsAndAccounts.test.ts +91 -0
  71. package/dist/implementation/encoding.js +0 -26
  72. package/dist/implementation/encoding.js.map +0 -1
  73. package/src/implementation/encoding.ts +0 -105
@@ -8,16 +8,20 @@ import type {
8
8
  RawCoStream,
9
9
  SessionID,
10
10
  } from "cojson";
11
- import { cojsonInternals } from "cojson";
11
+ import { MAX_RECOMMENDED_TX_SIZE, cojsonInternals } from "cojson";
12
12
  import type {
13
13
  CoValue,
14
- ValidItem,
15
- Encoding,
16
- EncodingFor,
14
+ Schema,
15
+ SchemaFor,
17
16
  Group,
18
17
  ID,
19
- Me,
20
- IsVal,
18
+ IfCo,
19
+ UnCo,
20
+ AccountCtx,
21
+ CoValueClass,
22
+ DeeplyLoaded,
23
+ DepthsIn,
24
+ UnavailableError,
21
25
  } from "../internal.js";
22
26
  import {
23
27
  ItemsSym,
@@ -25,46 +29,56 @@ import {
25
29
  CoValueBase,
26
30
  Ref,
27
31
  inspect,
28
- val,
32
+ co,
29
33
  InitValues,
30
34
  SchemaInit,
35
+ isRefEncoded,
36
+ loadCoValue,
37
+ loadCoValueEf,
38
+ subscribeToCoValue,
39
+ subscribeToCoValueEf,
40
+ ensureCoValueLoaded,
41
+ subscribeToExistingCoValue,
31
42
  } from "../internal.js";
32
- import { Schema } from "@effect/schema";
43
+ import { encodeSync, decodeSync } from "@effect/schema/Schema";
44
+ import { Effect, Stream } from "effect";
33
45
 
34
- export type CoStreamEntry<Item> = {
46
+ export type CoStreamEntry<Item> = SingleCoStreamEntry<Item> & {
47
+ all: IterableIterator<SingleCoStreamEntry<Item>>;
48
+ };
49
+
50
+ export type SingleCoStreamEntry<Item> = {
35
51
  value: NonNullable<Item> extends CoValue ? NonNullable<Item> | null : Item;
36
- ref?: NonNullable<Item> extends CoValue ? Ref<NonNullable<Item>> : never;
37
- by?: Account;
52
+ ref: NonNullable<Item> extends CoValue ? Ref<NonNullable<Item>> : never;
53
+ by?: Account | null;
38
54
  madeAt: Date;
39
55
  tx: CojsonInternalTypes.TransactionID;
40
56
  };
41
57
 
42
- export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
43
- extends CoValueBase
44
- implements CoValue<"CoStream", RawCoStream>
45
- {
46
- static Of<Item extends ValidItem<Item, "CoStream"> = any>(
47
- item: IsVal<Item, Item>
48
- ): typeof CoStream<Item> {
58
+ /** @category CoValues */
59
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
+ export class CoStream<Item = any> extends CoValueBase implements CoValue {
61
+ static Of<Item>(item: IfCo<Item, Item>): typeof CoStream<Item> {
49
62
  return class CoStreamOf extends CoStream<Item> {
50
- [val.items] = item;
63
+ [co.items] = item;
51
64
  };
52
65
  }
53
66
 
54
- id!: ID<this>;
55
- _type!: "CoStream";
67
+ declare id: ID<this>;
68
+ declare _type: "CoStream";
56
69
  static {
57
70
  this.prototype._type = "CoStream";
58
71
  }
59
- _raw!: RawCoStream;
72
+ declare _raw: RawCoStream;
60
73
 
61
74
  /** @internal This is only a marker type and doesn't exist at runtime */
62
75
  [ItemsSym]!: Item;
63
- static _encoding: any;
64
- get _encoding(): {
65
- [ItemsSym]: EncodingFor<Item>;
76
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
+ static _schema: any;
78
+ get _schema(): {
79
+ [ItemsSym]: SchemaFor<Item>;
66
80
  } {
67
- return (this.constructor as typeof CoStream)._encoding;
81
+ return (this.constructor as typeof CoStream)._schema;
68
82
  }
69
83
 
70
84
  [key: ID<Account>]: CoStreamEntry<Item>;
@@ -76,19 +90,16 @@ export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
76
90
  [key: SessionID]: CoStreamEntry<Item>;
77
91
  };
78
92
  get inCurrentSession(): CoStreamEntry<Item> | undefined {
79
- return this.perSession[this._loadedAs.sessionID];
93
+ return this.perSession[this._loadedAs.sessionID!];
80
94
  }
81
95
 
82
- [InitValues]?: {
83
- init?: Item[];
84
- owner: Account | Group;
85
- };
96
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
97
+ [InitValues]?: any;
86
98
 
87
- constructor(_init: undefined, options: { fromRaw: RawCoStream });
88
- constructor(init: Item[], options: { owner: Account | Group });
89
99
  constructor(
90
- init: Item[] | undefined,
91
- options: { owner: Account | Group } | { fromRaw: RawCoStream }
100
+ options:
101
+ | { init: Item[]; owner: Account | Group }
102
+ | { fromRaw: RawCoStream },
92
103
  ) {
93
104
  super();
94
105
 
@@ -102,7 +113,7 @@ export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
102
113
  });
103
114
  } else {
104
115
  this[InitValues] = {
105
- init,
116
+ init: options.init,
106
117
  owner: options.owner,
107
118
  };
108
119
  }
@@ -110,6 +121,14 @@ export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
110
121
  return new Proxy(this, CoStreamProxyHandler as ProxyHandler<this>);
111
122
  }
112
123
 
124
+ static create<S extends CoStream>(
125
+ this: CoValueClass<S>,
126
+ init: S extends CoStream<infer Item> ? UnCo<Item>[] : never,
127
+ options: { owner: Account | Group },
128
+ ) {
129
+ return new this({ init, owner: options.owner });
130
+ }
131
+
113
132
  push(...items: Item[]) {
114
133
  for (const item of items) {
115
134
  this.pushItem(item);
@@ -117,24 +136,24 @@ export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
117
136
  }
118
137
 
119
138
  private pushItem(item: Item) {
120
- const itemDescriptor = this._encoding[ItemsSym] as Encoding;
139
+ const itemDescriptor = this._schema[ItemsSym] as Schema;
121
140
 
122
141
  if (itemDescriptor === "json") {
123
142
  this._raw.push(item as JsonValue);
124
143
  } else if ("encoded" in itemDescriptor) {
125
- this._raw.push(Schema.encodeSync(itemDescriptor.encoded)(item));
126
- } else if ("ref" in itemDescriptor) {
144
+ this._raw.push(encodeSync(itemDescriptor.encoded)(item));
145
+ } else if (isRefEncoded(itemDescriptor)) {
127
146
  this._raw.push((item as unknown as CoValue).id);
128
147
  }
129
148
  }
130
149
 
131
150
  toJSON() {
132
- const itemDescriptor = this._encoding[ItemsSym] as Encoding;
151
+ const itemDescriptor = this._schema[ItemsSym] as Schema;
133
152
  const mapper =
134
153
  itemDescriptor === "json"
135
154
  ? (v: unknown) => v
136
155
  : "encoded" in itemDescriptor
137
- ? Schema.encodeSync(itemDescriptor.encoded)
156
+ ? encodeSync(itemDescriptor.encoded)
138
157
  : (v: unknown) => v && (v as CoValue).id;
139
158
 
140
159
  return {
@@ -144,13 +163,13 @@ export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
144
163
  Object.entries(this).map(([account, entry]) => [
145
164
  account,
146
165
  mapper(entry.value),
147
- ])
166
+ ]),
148
167
  ),
149
168
  in: Object.fromEntries(
150
169
  Object.entries(this.perSession).map(([session, entry]) => [
151
170
  session,
152
171
  mapper(entry.value),
153
- ])
172
+ ]),
154
173
  ),
155
174
  };
156
175
  }
@@ -159,12 +178,69 @@ export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
159
178
  return this.toJSON();
160
179
  }
161
180
 
162
- static encoding<V extends CoStream>(
181
+ static schema<V extends CoStream>(
182
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
163
183
  this: { new (...args: any): V } & typeof CoStream,
164
- def: { [ItemsSym]: V["_encoding"][ItemsSym] }
184
+ def: { [ItemsSym]: V["_schema"][ItemsSym] },
165
185
  ) {
166
- this._encoding ||= {};
167
- Object.assign(this._encoding, def);
186
+ this._schema ||= {};
187
+ Object.assign(this._schema, def);
188
+ }
189
+
190
+ /** @category Subscription & Loading */
191
+ static load<S extends CoStream, Depth>(
192
+ this: CoValueClass<S>,
193
+ id: ID<S>,
194
+ as: Account,
195
+ depth: Depth & DepthsIn<S>,
196
+ ): Promise<DeeplyLoaded<S, Depth> | undefined> {
197
+ return loadCoValue(this, id, as, depth);
198
+ }
199
+
200
+ /** @category Subscription & Loading */
201
+ static loadEf<S extends CoStream, Depth>(
202
+ this: CoValueClass<S>,
203
+ id: ID<S>,
204
+ depth: Depth & DepthsIn<S>,
205
+ ): Effect.Effect<DeeplyLoaded<S, Depth>, UnavailableError, AccountCtx> {
206
+ return loadCoValueEf<S, Depth>(this, id, depth);
207
+ }
208
+
209
+ /** @category Subscription & Loading */
210
+ static subscribe<S extends CoStream, Depth>(
211
+ this: CoValueClass<S>,
212
+ id: ID<S>,
213
+ as: Account,
214
+ depth: Depth & DepthsIn<S>,
215
+ listener: (value: DeeplyLoaded<S, Depth>) => void,
216
+ ): () => void {
217
+ return subscribeToCoValue<S, Depth>(this, id, as, depth, listener);
218
+ }
219
+
220
+ /** @category Subscription & Loading */
221
+ static subscribeEf<S extends CoStream, Depth>(
222
+ this: CoValueClass<S>,
223
+ id: ID<S>,
224
+ depth: Depth & DepthsIn<S>,
225
+ ): Stream.Stream<DeeplyLoaded<S, Depth>, UnavailableError, AccountCtx> {
226
+ return subscribeToCoValueEf<S, Depth>(this, id, depth);
227
+ }
228
+
229
+ /** @category Subscription & Loading */
230
+ ensureLoaded<S extends CoStream, Depth>(
231
+ this: S,
232
+ depth: Depth & DepthsIn<S>,
233
+ ): Promise<DeeplyLoaded<S, Depth> | undefined> {
234
+ return ensureCoValueLoaded(this, depth);
235
+ }
236
+
237
+ /** @category Subscription & Loading */
238
+ subscribe<S extends CoStream, Depth>(
239
+ this: S,
240
+ depth: Depth & DepthsIn<S>,
241
+ listener: (value: DeeplyLoaded<S, Depth>) => void,
242
+ ): () => void {
243
+ return subscribeToExistingCoValue(this, depth, listener);
168
244
  }
169
245
  }
170
246
 
@@ -176,38 +252,64 @@ function entryFromRawEntry<Item>(
176
252
  at: Date;
177
253
  value: JsonValue;
178
254
  },
179
- loadedAs: Account & Me,
255
+ loadedAs: Account,
180
256
  accountID: ID<Account> | undefined,
181
- itemField: Encoding
182
- ) {
257
+ itemField: Schema,
258
+ ): Omit<CoStreamEntry<Item>, "all"> {
183
259
  return {
184
- get value(): Item | undefined {
260
+ get value(): NonNullable<Item> extends CoValue
261
+ ? (CoValue & Item) | null
262
+ : Item {
185
263
  if (itemField === "json") {
186
- return rawEntry.value as Item;
264
+ return rawEntry.value as NonNullable<Item> extends CoValue
265
+ ? (CoValue & Item) | null
266
+ : Item;
187
267
  } else if ("encoded" in itemField) {
188
- return Schema.decodeSync(itemField.encoded)(rawEntry.value);
189
- } else if ("ref" in itemField) {
190
- return this.ref?.accessFrom(accessFrom) as Item;
268
+ return decodeSync(itemField.encoded)(rawEntry.value);
269
+ } else if (isRefEncoded(itemField)) {
270
+ return this.ref?.accessFrom(
271
+ accessFrom,
272
+ rawEntry.by +
273
+ rawEntry.tx.sessionID +
274
+ rawEntry.tx.txIndex +
275
+ ".value",
276
+ ) as NonNullable<Item> extends CoValue
277
+ ? (CoValue & Item) | null
278
+ : Item;
279
+ } else {
280
+ throw new Error("Invalid item field schema");
191
281
  }
192
282
  },
193
- get ref() {
194
- if (itemField !== "json" && "ref" in itemField) {
283
+ get ref(): NonNullable<Item> extends CoValue
284
+ ? Ref<NonNullable<Item>>
285
+ : never {
286
+ if (itemField !== "json" && isRefEncoded(itemField)) {
195
287
  const rawId = rawEntry.value;
196
288
  return new Ref(
197
289
  rawId as unknown as ID<CoValue>,
198
290
  loadedAs,
199
- itemField
200
- );
291
+ itemField,
292
+ ) as NonNullable<Item> extends CoValue
293
+ ? Ref<NonNullable<Item>>
294
+ : never;
295
+ } else {
296
+ return undefined as never;
201
297
  }
202
298
  },
203
299
  get by() {
204
300
  return (
205
301
  accountID &&
206
- new Ref(
302
+ new Ref<Account>(
207
303
  accountID as unknown as ID<Account>,
208
304
  loadedAs,
209
- {ref: () => Account}
210
- )?.accessFrom(accessFrom)
305
+ { ref: Account, optional: false },
306
+ )?.accessFrom(
307
+ accessFrom,
308
+ rawEntry.by +
309
+ rawEntry.tx.sessionID +
310
+ rawEntry.tx.txIndex +
311
+ ".by",
312
+ )
211
313
  );
212
314
  },
213
315
  madeAt: rawEntry.at,
@@ -242,15 +344,40 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
242
344
  const rawEntry = target._raw.lastItemBy(key as AccountID);
243
345
 
244
346
  if (!rawEntry) return;
245
- return entryFromRawEntry(
347
+ const entry = entryFromRawEntry(
246
348
  receiver,
247
349
  rawEntry,
248
350
  target._loadedAs,
249
351
  key as unknown as ID<Account>,
250
- target._encoding[ItemsSym]
352
+ target._schema[ItemsSym],
251
353
  );
354
+
355
+ Object.defineProperty(entry, "all", {
356
+ get: () => {
357
+ const allRawEntries = target._raw.itemsBy(key as AccountID);
358
+ return (function* () {
359
+ while (true) {
360
+ const rawEntry = allRawEntries.next();
361
+ if (rawEntry.done) return;
362
+ yield entryFromRawEntry(
363
+ receiver,
364
+ rawEntry.value,
365
+ target._loadedAs,
366
+ key as unknown as ID<Account>,
367
+ target._schema[ItemsSym],
368
+ );
369
+ }
370
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
371
+ })() satisfies IterableIterator<SingleCoStreamEntry<any>>;
372
+ },
373
+ });
374
+
375
+ return entry;
252
376
  } else if (key === "perSession") {
253
- return new Proxy(receiver, CoStreamPerSessionProxyHandler);
377
+ return new Proxy(
378
+ {},
379
+ CoStreamPerSessionProxyHandler(target, receiver),
380
+ );
254
381
  } else {
255
382
  return Reflect.get(target, key, receiver);
256
383
  }
@@ -261,8 +388,8 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
261
388
  typeof value === "object" &&
262
389
  SchemaInit in value
263
390
  ) {
264
- (target.constructor as typeof CoStream)._encoding ||= {};
265
- (target.constructor as typeof CoStream)._encoding[ItemsSym] =
391
+ (target.constructor as typeof CoStream)._schema ||= {};
392
+ (target.constructor as typeof CoStream)._schema[ItemsSym] =
266
393
  value[SchemaInit];
267
394
  init(target);
268
395
  return true;
@@ -277,8 +404,8 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
277
404
  typeof descriptor.value === "object" &&
278
405
  SchemaInit in descriptor.value
279
406
  ) {
280
- (target.constructor as typeof CoStream)._encoding ||= {};
281
- (target.constructor as typeof CoStream)._encoding[ItemsSym] =
407
+ (target.constructor as typeof CoStream)._schema ||= {};
408
+ (target.constructor as typeof CoStream)._schema[ItemsSym] =
282
409
  descriptor.value[SchemaInit];
283
410
  init(target);
284
411
  return true;
@@ -308,46 +435,85 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
308
435
  },
309
436
  };
310
437
 
311
- const CoStreamPerSessionProxyHandler: ProxyHandler<CoStream> = {
312
- get(target, key, receiver) {
438
+ const CoStreamPerSessionProxyHandler = (
439
+ innerTarget: CoStream,
440
+ accessFrom: CoStream,
441
+ ): ProxyHandler<Record<string, never>> => ({
442
+ get(_target, key, receiver) {
313
443
  if (typeof key === "string" && key.includes("session")) {
314
444
  const sessionID = key as SessionID;
315
- const rawEntry = target._raw.lastItemIn(sessionID);
445
+ const rawEntry = innerTarget._raw.lastItemIn(sessionID);
316
446
 
317
447
  if (!rawEntry) return;
318
448
  const by = cojsonInternals.accountOrAgentIDfromSessionID(sessionID);
319
- return entryFromRawEntry(
320
- target,
449
+
450
+ const entry = entryFromRawEntry(
451
+ accessFrom,
321
452
  rawEntry,
322
- target._loadedAs,
453
+ innerTarget._loadedAs,
323
454
  cojsonInternals.isAccountID(by)
324
455
  ? (by as unknown as ID<Account>)
325
456
  : undefined,
326
- target._encoding[ItemsSym]
457
+ innerTarget._schema[ItemsSym],
327
458
  );
459
+
460
+ Object.defineProperty(entry, "all", {
461
+ get: () => {
462
+ const allRawEntries = innerTarget._raw.itemsIn(sessionID);
463
+ return (function* () {
464
+ while (true) {
465
+ const rawEntry = allRawEntries.next();
466
+ if (rawEntry.done) return;
467
+ yield entryFromRawEntry(
468
+ accessFrom,
469
+ rawEntry.value,
470
+ innerTarget._loadedAs,
471
+ cojsonInternals.isAccountID(by)
472
+ ? (by as unknown as ID<Account>)
473
+ : undefined,
474
+ innerTarget._schema[ItemsSym],
475
+ );
476
+ }
477
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
478
+ })() satisfies IterableIterator<SingleCoStreamEntry<any>>;
479
+ },
480
+ });
481
+
482
+ return entry;
328
483
  } else {
329
- return Reflect.get(target, key, receiver);
484
+ return Reflect.get(innerTarget, key, receiver);
330
485
  }
331
486
  },
332
- };
487
+ ownKeys() {
488
+ return innerTarget._raw.sessions();
489
+ },
490
+ getOwnPropertyDescriptor(target, key) {
491
+ if (typeof key === "string" && key.startsWith("co_")) {
492
+ return {
493
+ configurable: true,
494
+ enumerable: true,
495
+ writable: false,
496
+ };
497
+ } else {
498
+ return Reflect.getOwnPropertyDescriptor(target, key);
499
+ }
500
+ },
501
+ });
333
502
 
334
- export class BinaryCoStream
335
- extends CoValueBase
336
- implements CoValue<"BinaryCoStream", RawBinaryCoStream>
337
- {
338
- id!: ID<this>;
339
- _type!: "BinaryCoStream";
340
- _raw!: RawBinaryCoStream;
503
+ /** @category CoValues */
504
+ export class BinaryCoStream extends CoValueBase implements CoValue {
505
+ declare id: ID<this>;
506
+ declare _type: "BinaryCoStream";
507
+ declare _raw: RawBinaryCoStream;
341
508
 
342
509
  constructor(
343
- init: [] | undefined,
344
510
  options:
345
511
  | {
346
512
  owner: Account | Group;
347
513
  }
348
514
  | {
349
515
  fromRaw: RawBinaryCoStream;
350
- }
516
+ },
351
517
  ) {
352
518
  super();
353
519
 
@@ -365,10 +531,18 @@ export class BinaryCoStream
365
531
  value: raw.id,
366
532
  enumerable: false,
367
533
  },
534
+ _type: { value: "BinaryCoStream", enumerable: false },
368
535
  _raw: { value: raw, enumerable: false },
369
536
  });
370
537
  }
371
538
 
539
+ static create<S extends BinaryCoStream>(
540
+ this: CoValueClass<S>,
541
+ options: { owner: Account | Group },
542
+ ) {
543
+ return new this(options);
544
+ }
545
+
372
546
  getChunks(options?: {
373
547
  allowUnfinished?: boolean;
374
548
  }):
@@ -389,6 +563,77 @@ export class BinaryCoStream
389
563
  this._raw.endBinaryStream();
390
564
  }
391
565
 
566
+ toBlob(options?: { allowUnfinished?: boolean }): Blob | undefined {
567
+ const chunks = this.getChunks({
568
+ allowUnfinished: options?.allowUnfinished,
569
+ });
570
+
571
+ if (!chunks) {
572
+ return undefined;
573
+ }
574
+
575
+ return new Blob(chunks.chunks, { type: chunks.mimeType });
576
+ }
577
+
578
+ static async loadAsBlob(
579
+ id: ID<BinaryCoStream>,
580
+ as: Account,
581
+ options?: {
582
+ allowUnfinished?: boolean;
583
+ },
584
+ ): Promise<Blob | undefined> {
585
+ const stream = await this.load(id, as, []);
586
+
587
+ return stream?.toBlob({
588
+ allowUnfinished: options?.allowUnfinished,
589
+ });
590
+ }
591
+
592
+ static async createFromBlob(
593
+ blob: Blob | File,
594
+ options: {
595
+ owner: Group | Account;
596
+ onProgress?: (progress: number) => void;
597
+ },
598
+ ): Promise<BinaryCoStream> {
599
+ const stream = this.create({ owner: options.owner });
600
+
601
+ const start = Date.now();
602
+
603
+ const data = new Uint8Array(await blob.arrayBuffer());
604
+ stream.start({
605
+ mimeType: blob.type,
606
+ totalSizeBytes: blob.size,
607
+ fileName: blob instanceof File ? blob.name : undefined,
608
+ });
609
+ const chunkSize = MAX_RECOMMENDED_TX_SIZE;
610
+
611
+ let lastProgressUpdate = Date.now();
612
+
613
+ for (let idx = 0; idx < data.length; idx += chunkSize) {
614
+ stream.push(data.slice(idx, idx + chunkSize));
615
+
616
+ if (Date.now() - lastProgressUpdate > 100) {
617
+ options.onProgress?.(idx / data.length);
618
+ lastProgressUpdate = Date.now();
619
+ }
620
+
621
+ await new Promise((resolve) => setTimeout(resolve, 0));
622
+ }
623
+ stream.end();
624
+ const end = Date.now();
625
+
626
+ console.debug(
627
+ "Finished creating binary stream in",
628
+ (end - start) / 1000,
629
+ "s - Throughput in MB/s",
630
+ (1000 * (blob.size / (end - start))) / (1024 * 1024),
631
+ );
632
+ options.onProgress?.(1);
633
+
634
+ return stream;
635
+ }
636
+
392
637
  toJSON() {
393
638
  return {
394
639
  id: this.id,
@@ -400,4 +645,60 @@ export class BinaryCoStream
400
645
  [inspect]() {
401
646
  return this.toJSON();
402
647
  }
648
+
649
+ /** @category Subscription & Loading */
650
+ static load<B extends BinaryCoStream, Depth>(
651
+ this: CoValueClass<B>,
652
+ id: ID<B>,
653
+ as: Account,
654
+ depth: Depth & DepthsIn<B>,
655
+ ): Promise<DeeplyLoaded<B, Depth> | undefined> {
656
+ return loadCoValue(this, id, as, depth);
657
+ }
658
+
659
+ /** @category Subscription & Loading */
660
+ static loadEf<B extends BinaryCoStream, Depth>(
661
+ this: CoValueClass<B>,
662
+ id: ID<B>,
663
+ depth: Depth & DepthsIn<B>,
664
+ ): Effect.Effect<DeeplyLoaded<B, Depth>, UnavailableError, AccountCtx> {
665
+ return loadCoValueEf<B, Depth>(this, id, depth);
666
+ }
667
+
668
+ /** @category Subscription & Loading */
669
+ static subscribe<B extends BinaryCoStream, Depth>(
670
+ this: CoValueClass<B>,
671
+ id: ID<B>,
672
+ as: Account,
673
+ depth: Depth & DepthsIn<B>,
674
+ listener: (value: DeeplyLoaded<B, Depth>) => void,
675
+ ): () => void {
676
+ return subscribeToCoValue<B, Depth>(this, id, as, depth, listener);
677
+ }
678
+
679
+ /** @category Subscription & Loading */
680
+ static subscribeEf<B extends BinaryCoStream, Depth>(
681
+ this: CoValueClass<B>,
682
+ id: ID<B>,
683
+ depth: Depth & DepthsIn<B>,
684
+ ): Stream.Stream<DeeplyLoaded<B, Depth>, UnavailableError, AccountCtx> {
685
+ return subscribeToCoValueEf<B, Depth>(this, id, depth);
686
+ }
687
+
688
+ /** @category Subscription & Loading */
689
+ ensureLoaded<B extends BinaryCoStream, Depth>(
690
+ this: B,
691
+ depth: Depth & DepthsIn<B>,
692
+ ): Promise<DeeplyLoaded<B, Depth> | undefined> {
693
+ return ensureCoValueLoaded(this, depth);
694
+ }
695
+
696
+ /** @category Subscription & Loading */
697
+ subscribe<B extends BinaryCoStream, Depth>(
698
+ this: B,
699
+ depth: Depth & DepthsIn<B>,
700
+ listener: (value: DeeplyLoaded<B, Depth>) => void,
701
+ ): () => void {
702
+ return subscribeToExistingCoValue(this, depth, listener);
703
+ }
403
704
  }