jazz-tools 0.7.0-alpha.9 → 0.7.1

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