jazz-tools 0.7.0-alpha.8 → 0.7.1

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 (72) hide show
  1. package/.eslintrc.cjs +3 -10
  2. package/.prettierrc.js +9 -0
  3. package/.turbo/turbo-build.log +3 -19
  4. package/.turbo/turbo-lint.log +4 -0
  5. package/.turbo/turbo-test.log +140 -0
  6. package/CHANGELOG.md +304 -0
  7. package/README.md +10 -2
  8. package/dist/coValues/account.js +59 -41
  9. package/dist/coValues/account.js.map +1 -1
  10. package/dist/coValues/coList.js +49 -46
  11. package/dist/coValues/coList.js.map +1 -1
  12. package/dist/coValues/coMap.js +143 -44
  13. package/dist/coValues/coMap.js.map +1 -1
  14. package/dist/coValues/coStream.js +144 -35
  15. package/dist/coValues/coStream.js.map +1 -1
  16. package/dist/coValues/deepLoading.js +60 -0
  17. package/dist/coValues/deepLoading.js.map +1 -0
  18. package/dist/coValues/extensions/imageDef.js +10 -7
  19. package/dist/coValues/extensions/imageDef.js.map +1 -1
  20. package/dist/coValues/group.js +49 -13
  21. package/dist/coValues/group.js.map +1 -1
  22. package/dist/coValues/interfaces.js +70 -31
  23. package/dist/coValues/interfaces.js.map +1 -1
  24. package/dist/implementation/devtoolsFormatters.js +114 -0
  25. package/dist/implementation/devtoolsFormatters.js.map +1 -0
  26. package/dist/implementation/refs.js +58 -18
  27. package/dist/implementation/refs.js.map +1 -1
  28. package/dist/implementation/schema.js +58 -0
  29. package/dist/implementation/schema.js.map +1 -0
  30. package/dist/implementation/subscriptionScope.js +19 -1
  31. package/dist/implementation/subscriptionScope.js.map +1 -1
  32. package/dist/implementation/symbols.js +5 -0
  33. package/dist/implementation/symbols.js.map +1 -0
  34. package/dist/index.js +3 -5
  35. package/dist/index.js.map +1 -1
  36. package/dist/internal.js +5 -2
  37. package/dist/internal.js.map +1 -1
  38. package/dist/tests/coList.test.js +51 -48
  39. package/dist/tests/coList.test.js.map +1 -1
  40. package/dist/tests/coMap.test.js +131 -73
  41. package/dist/tests/coMap.test.js.map +1 -1
  42. package/dist/tests/coStream.test.js +56 -41
  43. package/dist/tests/coStream.test.js.map +1 -1
  44. package/dist/tests/deepLoading.test.js +188 -0
  45. package/dist/tests/deepLoading.test.js.map +1 -0
  46. package/dist/tests/groupsAndAccounts.test.js +83 -0
  47. package/dist/tests/groupsAndAccounts.test.js.map +1 -0
  48. package/package.json +17 -9
  49. package/src/coValues/account.ts +113 -125
  50. package/src/coValues/coList.ts +87 -103
  51. package/src/coValues/coMap.ts +200 -147
  52. package/src/coValues/coStream.ts +264 -80
  53. package/src/coValues/deepLoading.ts +229 -0
  54. package/src/coValues/extensions/imageDef.ts +17 -13
  55. package/src/coValues/group.ts +92 -58
  56. package/src/coValues/interfaces.ts +215 -115
  57. package/src/implementation/devtoolsFormatters.ts +110 -0
  58. package/src/implementation/inspect.ts +1 -1
  59. package/src/implementation/refs.ts +80 -28
  60. package/src/implementation/schema.ts +138 -0
  61. package/src/implementation/subscriptionScope.ts +48 -12
  62. package/src/implementation/symbols.ts +11 -0
  63. package/src/index.ts +12 -8
  64. package/src/internal.ts +7 -3
  65. package/src/tests/coList.test.ts +77 -62
  66. package/src/tests/coMap.test.ts +201 -113
  67. package/src/tests/coStream.test.ts +113 -84
  68. package/src/tests/deepLoading.test.ts +301 -0
  69. package/src/tests/groupsAndAccounts.test.ts +91 -0
  70. package/dist/implementation/encoding.js +0 -26
  71. package/dist/implementation/encoding.js.map +0 -1
  72. package/src/implementation/encoding.ts +0 -105
@@ -8,16 +8,16 @@ 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
+ ClassOf,
20
+ UnCo,
21
21
  } from "../internal.js";
22
22
  import {
23
23
  ItemsSym,
@@ -25,46 +25,52 @@ import {
25
25
  CoValueBase,
26
26
  Ref,
27
27
  inspect,
28
- val,
28
+ co,
29
29
  InitValues,
30
30
  SchemaInit,
31
+ isRefEncoded,
31
32
  } from "../internal.js";
32
- import { Schema } from "@effect/schema";
33
+ import { encodeSync, decodeSync } from "@effect/schema/Schema";
33
34
 
34
- export type CoStreamEntry<Item> = {
35
+ export type CoStreamEntry<Item> = SingleCoStreamEntry<Item> & {
36
+ all: IterableIterator<SingleCoStreamEntry<Item>>;
37
+ };
38
+
39
+ export type SingleCoStreamEntry<Item> = {
35
40
  value: NonNullable<Item> extends CoValue ? NonNullable<Item> | null : Item;
36
- ref?: NonNullable<Item> extends CoValue ? Ref<NonNullable<Item>> : never;
37
- by?: Account;
41
+ ref: NonNullable<Item> extends CoValue ? Ref<NonNullable<Item>> : never;
42
+ by?: Account | null;
38
43
  madeAt: Date;
39
44
  tx: CojsonInternalTypes.TransactionID;
40
45
  };
41
46
 
42
- export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
47
+ /** @category CoValues */
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
+ export class CoStream<Item = any>
43
50
  extends CoValueBase
44
51
  implements CoValue<"CoStream", RawCoStream>
45
52
  {
46
- static Of<Item extends ValidItem<Item, "CoStream"> = any>(
47
- item: IsVal<Item, Item>
48
- ): typeof CoStream<Item> {
53
+ static Of<Item>(item: IfCo<Item, Item>): typeof CoStream<Item> {
49
54
  return class CoStreamOf extends CoStream<Item> {
50
- [val.items] = item;
55
+ [co.items] = item;
51
56
  };
52
57
  }
53
58
 
54
- id!: ID<this>;
55
- _type!: "CoStream";
59
+ declare id: ID<this>;
60
+ declare _type: "CoStream";
56
61
  static {
57
62
  this.prototype._type = "CoStream";
58
63
  }
59
- _raw!: RawCoStream;
64
+ declare _raw: RawCoStream;
60
65
 
61
66
  /** @internal This is only a marker type and doesn't exist at runtime */
62
67
  [ItemsSym]!: Item;
63
- static _encoding: any;
64
- get _encoding(): {
65
- [ItemsSym]: EncodingFor<Item>;
68
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
69
+ static _schema: any;
70
+ get _schema(): {
71
+ [ItemsSym]: SchemaFor<Item>;
66
72
  } {
67
- return (this.constructor as typeof CoStream)._encoding;
73
+ return (this.constructor as typeof CoStream)._schema;
68
74
  }
69
75
 
70
76
  [key: ID<Account>]: CoStreamEntry<Item>;
@@ -76,19 +82,16 @@ export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
76
82
  [key: SessionID]: CoStreamEntry<Item>;
77
83
  };
78
84
  get inCurrentSession(): CoStreamEntry<Item> | undefined {
79
- return this.perSession[this._loadedAs.sessionID];
85
+ return this.perSession[this._loadedAs.sessionID!];
80
86
  }
81
87
 
82
- [InitValues]?: {
83
- init?: Item[];
84
- owner: Account | Group;
85
- };
88
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
+ [InitValues]?: any;
86
90
 
87
- constructor(_init: undefined, options: { fromRaw: RawCoStream });
88
- constructor(init: Item[], options: { owner: Account | Group });
89
91
  constructor(
90
- init: Item[] | undefined,
91
- options: { owner: Account | Group } | { fromRaw: RawCoStream }
92
+ options:
93
+ | { init: Item[]; owner: Account | Group }
94
+ | { fromRaw: RawCoStream },
92
95
  ) {
93
96
  super();
94
97
 
@@ -102,7 +105,7 @@ export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
102
105
  });
103
106
  } else {
104
107
  this[InitValues] = {
105
- init,
108
+ init: options.init,
106
109
  owner: options.owner,
107
110
  };
108
111
  }
@@ -110,6 +113,14 @@ export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
110
113
  return new Proxy(this, CoStreamProxyHandler as ProxyHandler<this>);
111
114
  }
112
115
 
116
+ static create<S extends CoStream>(
117
+ this: ClassOf<S>,
118
+ init: S extends CoStream<infer Item> ? UnCo<Item>[] : never,
119
+ options: { owner: Account | Group },
120
+ ) {
121
+ return new this({ init, owner: options.owner });
122
+ }
123
+
113
124
  push(...items: Item[]) {
114
125
  for (const item of items) {
115
126
  this.pushItem(item);
@@ -117,24 +128,24 @@ export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
117
128
  }
118
129
 
119
130
  private pushItem(item: Item) {
120
- const itemDescriptor = this._encoding[ItemsSym] as Encoding;
131
+ const itemDescriptor = this._schema[ItemsSym] as Schema;
121
132
 
122
133
  if (itemDescriptor === "json") {
123
134
  this._raw.push(item as JsonValue);
124
135
  } else if ("encoded" in itemDescriptor) {
125
- this._raw.push(Schema.encodeSync(itemDescriptor.encoded)(item));
126
- } else if ("ref" in itemDescriptor) {
136
+ this._raw.push(encodeSync(itemDescriptor.encoded)(item));
137
+ } else if (isRefEncoded(itemDescriptor)) {
127
138
  this._raw.push((item as unknown as CoValue).id);
128
139
  }
129
140
  }
130
141
 
131
142
  toJSON() {
132
- const itemDescriptor = this._encoding[ItemsSym] as Encoding;
143
+ const itemDescriptor = this._schema[ItemsSym] as Schema;
133
144
  const mapper =
134
145
  itemDescriptor === "json"
135
146
  ? (v: unknown) => v
136
147
  : "encoded" in itemDescriptor
137
- ? Schema.encodeSync(itemDescriptor.encoded)
148
+ ? encodeSync(itemDescriptor.encoded)
138
149
  : (v: unknown) => v && (v as CoValue).id;
139
150
 
140
151
  return {
@@ -144,13 +155,13 @@ export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
144
155
  Object.entries(this).map(([account, entry]) => [
145
156
  account,
146
157
  mapper(entry.value),
147
- ])
158
+ ]),
148
159
  ),
149
160
  in: Object.fromEntries(
150
161
  Object.entries(this.perSession).map(([session, entry]) => [
151
162
  session,
152
163
  mapper(entry.value),
153
- ])
164
+ ]),
154
165
  ),
155
166
  };
156
167
  }
@@ -159,12 +170,13 @@ export class CoStream<Item extends ValidItem<Item, "CoStream"> = any>
159
170
  return this.toJSON();
160
171
  }
161
172
 
162
- static encoding<V extends CoStream>(
173
+ static schema<V extends CoStream>(
174
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
163
175
  this: { new (...args: any): V } & typeof CoStream,
164
- def: { [ItemsSym]: V["_encoding"][ItemsSym] }
176
+ def: { [ItemsSym]: V["_schema"][ItemsSym] },
165
177
  ) {
166
- this._encoding ||= {};
167
- Object.assign(this._encoding, def);
178
+ this._schema ||= {};
179
+ Object.assign(this._schema, def);
168
180
  }
169
181
  }
170
182
 
@@ -176,38 +188,64 @@ function entryFromRawEntry<Item>(
176
188
  at: Date;
177
189
  value: JsonValue;
178
190
  },
179
- loadedAs: Account & Me,
191
+ loadedAs: Account,
180
192
  accountID: ID<Account> | undefined,
181
- itemField: Encoding
182
- ) {
193
+ itemField: Schema,
194
+ ): Omit<CoStreamEntry<Item>, "all"> {
183
195
  return {
184
- get value(): Item | undefined {
196
+ get value(): NonNullable<Item> extends CoValue
197
+ ? (CoValue & Item) | null
198
+ : Item {
185
199
  if (itemField === "json") {
186
- return rawEntry.value as Item;
200
+ return rawEntry.value as NonNullable<Item> extends CoValue
201
+ ? (CoValue & Item) | null
202
+ : Item;
187
203
  } 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;
204
+ return decodeSync(itemField.encoded)(rawEntry.value);
205
+ } else if (isRefEncoded(itemField)) {
206
+ return this.ref?.accessFrom(
207
+ accessFrom,
208
+ rawEntry.by +
209
+ rawEntry.tx.sessionID +
210
+ rawEntry.tx.txIndex +
211
+ ".value",
212
+ ) as NonNullable<Item> extends CoValue
213
+ ? (CoValue & Item) | null
214
+ : Item;
215
+ } else {
216
+ throw new Error("Invalid item field schema");
191
217
  }
192
218
  },
193
- get ref() {
194
- if (itemField !== "json" && "ref" in itemField) {
219
+ get ref(): NonNullable<Item> extends CoValue
220
+ ? Ref<NonNullable<Item>>
221
+ : never {
222
+ if (itemField !== "json" && isRefEncoded(itemField)) {
195
223
  const rawId = rawEntry.value;
196
224
  return new Ref(
197
225
  rawId as unknown as ID<CoValue>,
198
226
  loadedAs,
199
- itemField
200
- );
227
+ itemField,
228
+ ) as NonNullable<Item> extends CoValue
229
+ ? Ref<NonNullable<Item>>
230
+ : never;
231
+ } else {
232
+ return undefined as never;
201
233
  }
202
234
  },
203
235
  get by() {
204
236
  return (
205
237
  accountID &&
206
- new Ref(
238
+ new Ref<Account>(
207
239
  accountID as unknown as ID<Account>,
208
240
  loadedAs,
209
- {ref: () => Account}
210
- )?.accessFrom(accessFrom)
241
+ { ref: Account, optional: false },
242
+ )?.accessFrom(
243
+ accessFrom,
244
+ rawEntry.by +
245
+ rawEntry.tx.sessionID +
246
+ rawEntry.tx.txIndex +
247
+ ".by",
248
+ )
211
249
  );
212
250
  },
213
251
  madeAt: rawEntry.at,
@@ -242,15 +280,40 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
242
280
  const rawEntry = target._raw.lastItemBy(key as AccountID);
243
281
 
244
282
  if (!rawEntry) return;
245
- return entryFromRawEntry(
283
+ const entry = entryFromRawEntry(
246
284
  receiver,
247
285
  rawEntry,
248
286
  target._loadedAs,
249
287
  key as unknown as ID<Account>,
250
- target._encoding[ItemsSym]
288
+ target._schema[ItemsSym],
251
289
  );
290
+
291
+ Object.defineProperty(entry, "all", {
292
+ get: () => {
293
+ const allRawEntries = target._raw.itemsBy(key as AccountID);
294
+ return (function* () {
295
+ while (true) {
296
+ const rawEntry = allRawEntries.next();
297
+ if (rawEntry.done) return;
298
+ yield entryFromRawEntry(
299
+ receiver,
300
+ rawEntry.value,
301
+ target._loadedAs,
302
+ key as unknown as ID<Account>,
303
+ target._schema[ItemsSym],
304
+ );
305
+ }
306
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
307
+ })() satisfies IterableIterator<SingleCoStreamEntry<any>>;
308
+ },
309
+ });
310
+
311
+ return entry;
252
312
  } else if (key === "perSession") {
253
- return new Proxy(receiver, CoStreamPerSessionProxyHandler);
313
+ return new Proxy(
314
+ {},
315
+ CoStreamPerSessionProxyHandler(target, receiver),
316
+ );
254
317
  } else {
255
318
  return Reflect.get(target, key, receiver);
256
319
  }
@@ -261,8 +324,8 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
261
324
  typeof value === "object" &&
262
325
  SchemaInit in value
263
326
  ) {
264
- (target.constructor as typeof CoStream)._encoding ||= {};
265
- (target.constructor as typeof CoStream)._encoding[ItemsSym] =
327
+ (target.constructor as typeof CoStream)._schema ||= {};
328
+ (target.constructor as typeof CoStream)._schema[ItemsSym] =
266
329
  value[SchemaInit];
267
330
  init(target);
268
331
  return true;
@@ -277,8 +340,8 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
277
340
  typeof descriptor.value === "object" &&
278
341
  SchemaInit in descriptor.value
279
342
  ) {
280
- (target.constructor as typeof CoStream)._encoding ||= {};
281
- (target.constructor as typeof CoStream)._encoding[ItemsSym] =
343
+ (target.constructor as typeof CoStream)._schema ||= {};
344
+ (target.constructor as typeof CoStream)._schema[ItemsSym] =
282
345
  descriptor.value[SchemaInit];
283
346
  init(target);
284
347
  return true;
@@ -308,46 +371,88 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
308
371
  },
309
372
  };
310
373
 
311
- const CoStreamPerSessionProxyHandler: ProxyHandler<CoStream> = {
312
- get(target, key, receiver) {
374
+ const CoStreamPerSessionProxyHandler = (
375
+ innerTarget: CoStream,
376
+ accessFrom: CoStream,
377
+ ): ProxyHandler<Record<string, never>> => ({
378
+ get(_target, key, receiver) {
313
379
  if (typeof key === "string" && key.includes("session")) {
314
380
  const sessionID = key as SessionID;
315
- const rawEntry = target._raw.lastItemIn(sessionID);
381
+ const rawEntry = innerTarget._raw.lastItemIn(sessionID);
316
382
 
317
383
  if (!rawEntry) return;
318
384
  const by = cojsonInternals.accountOrAgentIDfromSessionID(sessionID);
319
- return entryFromRawEntry(
320
- target,
385
+
386
+ const entry = entryFromRawEntry(
387
+ accessFrom,
321
388
  rawEntry,
322
- target._loadedAs,
389
+ innerTarget._loadedAs,
323
390
  cojsonInternals.isAccountID(by)
324
391
  ? (by as unknown as ID<Account>)
325
392
  : undefined,
326
- target._encoding[ItemsSym]
393
+ innerTarget._schema[ItemsSym],
327
394
  );
395
+
396
+ Object.defineProperty(entry, "all", {
397
+ get: () => {
398
+ const allRawEntries = innerTarget._raw.itemsIn(sessionID);
399
+ return (function* () {
400
+ while (true) {
401
+ const rawEntry = allRawEntries.next();
402
+ if (rawEntry.done) return;
403
+ yield entryFromRawEntry(
404
+ accessFrom,
405
+ rawEntry.value,
406
+ innerTarget._loadedAs,
407
+ cojsonInternals.isAccountID(by)
408
+ ? (by as unknown as ID<Account>)
409
+ : undefined,
410
+ innerTarget._schema[ItemsSym],
411
+ );
412
+ }
413
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
414
+ })() satisfies IterableIterator<SingleCoStreamEntry<any>>;
415
+ },
416
+ });
417
+
418
+ return entry;
328
419
  } else {
329
- return Reflect.get(target, key, receiver);
420
+ return Reflect.get(innerTarget, key, receiver);
330
421
  }
331
422
  },
332
- };
423
+ ownKeys() {
424
+ return innerTarget._raw.sessions();
425
+ },
426
+ getOwnPropertyDescriptor(target, key) {
427
+ if (typeof key === "string" && key.startsWith("co_")) {
428
+ return {
429
+ configurable: true,
430
+ enumerable: true,
431
+ writable: false,
432
+ };
433
+ } else {
434
+ return Reflect.getOwnPropertyDescriptor(target, key);
435
+ }
436
+ },
437
+ });
333
438
 
439
+ /** @category CoValues */
334
440
  export class BinaryCoStream
335
441
  extends CoValueBase
336
442
  implements CoValue<"BinaryCoStream", RawBinaryCoStream>
337
443
  {
338
- id!: ID<this>;
339
- _type!: "BinaryCoStream";
340
- _raw!: RawBinaryCoStream;
444
+ declare id: ID<this>;
445
+ declare _type: "BinaryCoStream";
446
+ declare _raw: RawBinaryCoStream;
341
447
 
342
448
  constructor(
343
- init: [] | undefined,
344
449
  options:
345
450
  | {
346
451
  owner: Account | Group;
347
452
  }
348
453
  | {
349
454
  fromRaw: RawBinaryCoStream;
350
- }
455
+ },
351
456
  ) {
352
457
  super();
353
458
 
@@ -365,10 +470,18 @@ export class BinaryCoStream
365
470
  value: raw.id,
366
471
  enumerable: false,
367
472
  },
473
+ _type: { value: "BinaryCoStream", enumerable: false },
368
474
  _raw: { value: raw, enumerable: false },
369
475
  });
370
476
  }
371
477
 
478
+ static create<S extends BinaryCoStream>(
479
+ this: ClassOf<S>,
480
+ options: { owner: Account | Group },
481
+ ) {
482
+ return new this(options);
483
+ }
484
+
372
485
  getChunks(options?: {
373
486
  allowUnfinished?: boolean;
374
487
  }):
@@ -389,6 +502,77 @@ export class BinaryCoStream
389
502
  this._raw.endBinaryStream();
390
503
  }
391
504
 
505
+ toBlob(options?: { allowUnfinished?: boolean }): Blob | undefined {
506
+ const chunks = this.getChunks({
507
+ allowUnfinished: options?.allowUnfinished,
508
+ });
509
+
510
+ if (!chunks) {
511
+ return undefined;
512
+ }
513
+
514
+ return new Blob(chunks.chunks, { type: chunks.mimeType });
515
+ }
516
+
517
+ static async loadAsBlob(
518
+ id: ID<BinaryCoStream>,
519
+ as: Account,
520
+ options?: {
521
+ allowUnfinished?: boolean;
522
+ },
523
+ ): Promise<Blob | undefined> {
524
+ const stream = await this.load(id, as, []);
525
+
526
+ return stream?.toBlob({
527
+ allowUnfinished: options?.allowUnfinished,
528
+ });
529
+ }
530
+
531
+ static async createFromBlob(
532
+ blob: Blob | File,
533
+ options: {
534
+ owner: Group | Account;
535
+ onProgress?: (progress: number) => void;
536
+ },
537
+ ): Promise<BinaryCoStream> {
538
+ const stream = this.create({ owner: options.owner });
539
+
540
+ const start = Date.now();
541
+
542
+ const data = new Uint8Array(await blob.arrayBuffer());
543
+ stream.start({
544
+ mimeType: blob.type,
545
+ totalSizeBytes: blob.size,
546
+ fileName: blob instanceof File ? blob.name : undefined,
547
+ });
548
+ const chunkSize = MAX_RECOMMENDED_TX_SIZE;
549
+
550
+ let lastProgressUpdate = Date.now();
551
+
552
+ for (let idx = 0; idx < data.length; idx += chunkSize) {
553
+ stream.push(data.slice(idx, idx + chunkSize));
554
+
555
+ if (Date.now() - lastProgressUpdate > 100) {
556
+ options.onProgress?.(idx / data.length);
557
+ lastProgressUpdate = Date.now();
558
+ }
559
+
560
+ await new Promise((resolve) => setTimeout(resolve, 0));
561
+ }
562
+ stream.end();
563
+ const end = Date.now();
564
+
565
+ console.debug(
566
+ "Finished creating binary stream in",
567
+ (end - start) / 1000,
568
+ "s - Throughput in MB/s",
569
+ (1000 * (blob.size / (end - start))) / (1024 * 1024),
570
+ );
571
+ options.onProgress?.(1);
572
+
573
+ return stream;
574
+ }
575
+
392
576
  toJSON() {
393
577
  return {
394
578
  id: this.id,