@muhkoo/connect 0.10.9-alpha.0 → 0.10.10-alpha.0

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.
package/dist/connect.d.ts CHANGED
@@ -1706,6 +1706,14 @@ declare class FileStorage {
1706
1706
  data: Uint8Array;
1707
1707
  stat: FileStat;
1708
1708
  }>;
1709
+ /**
1710
+ * Streaming inverse of {@link writeFileToShards}: yields each chunk's
1711
+ * decrypted plaintext in order WITHOUT concatenating. Peak memory stays at
1712
+ * ~one chunk, so multi-GB files (which overflow a single `Uint8Array`) can be
1713
+ * piped straight to a file/socket. Same shard sourcing (cache → peers →
1714
+ * origin) and per-chunk auth as {@link readFileFromShards}.
1715
+ */
1716
+ readChunksFromShards(manifest: FileManifest): AsyncGenerator<Uint8Array>;
1709
1717
  /** As {@link writeFileToShards}, plus POSTs the manifest to `input.spaceId`. */
1710
1718
  writeFile(input: WriteFileInput): Promise<FileStat>;
1711
1719
  /** As {@link readFileFromShards}, but fetches the manifest from a space first. */
@@ -4146,6 +4154,11 @@ declare class Space {
4146
4154
  data: Uint8Array;
4147
4155
  stat: FileStat;
4148
4156
  }>;
4157
+ /** Streaming read: async-iterate a stored file's decrypted chunks in order,
4158
+ * never holding the whole file in memory. Use for multi-GB files (a single
4159
+ * `Uint8Array` can't hold them) — e.g. a transcoder piping the raw to a temp
4160
+ * file. Same peer/origin shard sourcing as {@link getFile}. */
4161
+ getFileStream(manifest: FileManifest): AsyncGenerator<Uint8Array>;
4149
4162
  /** Declare a file's shards (ref++ + meter) for a manifest already stored —
4150
4163
  * used to back-fill accounting for files written before commit existed, or to
4151
4164
  * register a manifest received via sharing. `putFile` already commits new
@@ -2976,7 +2976,7 @@ function base64UrlDecode(s) {
2976
2976
  * expect (and isn't serving a stale cached bundle). Keep this in sync with
2977
2977
  * `package.json` `version`.
2978
2978
  */
2979
- const VERSION = "0.10.9-alpha.0";
2979
+ const VERSION = "0.10.10-alpha.0";
2980
2980
 
2981
2981
  /**
2982
2982
  * `HttpClient` — the one place the unified {@link Client} talks to the
@@ -5832,31 +5832,37 @@ class FileStorage {
5832
5832
  */
5833
5833
  async writeFileToShards(input) {
5834
5834
  await this.codec.ready(this.rsWasmModule);
5835
- const data = await toUint8Array(input.data);
5836
5835
  const fileId = input.fileId ?? generateId();
5837
5836
  const createdAt = Date.now();
5838
5837
  const chunks = [];
5839
- const totalChunks = Math.max(1, Math.ceil(data.length / this.chunkSize));
5838
+ // Read the source per-chunk so the peak memory footprint stays at
5839
+ // ~chunkSize even for multi-GB files. A Blob/File is sliced lazily (only
5840
+ // the current chunk's bytes are materialized) — reading the whole thing
5841
+ // up front would blow past the browser's ~2GB ArrayBuffer cap and throw
5842
+ // "the file could not be read". A caller that already has the bytes in a
5843
+ // Uint8Array just gets zero-copy subarray views.
5844
+ const asBytes = input.data instanceof Uint8Array ? input.data : null;
5845
+ const size = asBytes ? asBytes.length : input.data.size;
5846
+ const readChunk = async (start, end) => asBytes ? asBytes.subarray(start, end)
5847
+ : new Uint8Array(await input.data.slice(start, end).arrayBuffer());
5848
+ const totalChunks = Math.max(1, Math.ceil(size / this.chunkSize));
5840
5849
  input.onProgress?.(0, totalChunks);
5841
- // Slicing + encrypting + encoding + uploading is done per-chunk so
5842
- // the peak memory footprint stays at ~chunkSize even for huge files.
5843
- // Within a chunk, the shard uploads are parallelized up to
5844
- // `this.concurrency`.
5845
- for (let chunkIndex = 0; chunkIndex * this.chunkSize < Math.max(1, data.length); chunkIndex++) {
5850
+ // Within a chunk, the shard uploads are parallelized up to `this.concurrency`.
5851
+ for (let chunkIndex = 0; chunkIndex * this.chunkSize < Math.max(1, size); chunkIndex++) {
5846
5852
  const start = chunkIndex * this.chunkSize;
5847
- const end = Math.min(start + this.chunkSize, data.length);
5848
- const plaintext = data.subarray(start, end);
5853
+ const end = Math.min(start + this.chunkSize, size);
5854
+ const plaintext = await readChunk(start, end);
5849
5855
  chunks.push(await this.writeChunk(plaintext, chunkIndex));
5850
5856
  input.onProgress?.(chunks.length, totalChunks);
5851
5857
  // Stop after one iteration if the file is empty — we still emit a
5852
5858
  // single zero-length chunk so the manifest has a stable shape.
5853
- if (data.length === 0)
5859
+ if (size === 0)
5854
5860
  break;
5855
5861
  }
5856
5862
  const manifest = {
5857
5863
  id: fileId,
5858
5864
  name: input.metadata.name,
5859
- size: data.length,
5865
+ size,
5860
5866
  type: input.metadata.type,
5861
5867
  path: input.metadata.path,
5862
5868
  lastModified: input.metadata.lastModified ?? createdAt,
@@ -5892,6 +5898,19 @@ class FileStorage {
5892
5898
  }
5893
5899
  return { data: out, stat: manifestToStat(manifest) };
5894
5900
  }
5901
+ /**
5902
+ * Streaming inverse of {@link writeFileToShards}: yields each chunk's
5903
+ * decrypted plaintext in order WITHOUT concatenating. Peak memory stays at
5904
+ * ~one chunk, so multi-GB files (which overflow a single `Uint8Array`) can be
5905
+ * piped straight to a file/socket. Same shard sourcing (cache → peers →
5906
+ * origin) and per-chunk auth as {@link readFileFromShards}.
5907
+ */
5908
+ async *readChunksFromShards(manifest) {
5909
+ await this.codec.ready(this.rsWasmModule);
5910
+ for (const chunkManifest of [...manifest.chunks].sort((a, b) => a.chunkIndex - b.chunkIndex)) {
5911
+ yield await this.readChunk(chunkManifest);
5912
+ }
5913
+ }
5895
5914
  // -------------------------------------------------------------------------
5896
5915
  // Space-backed convenience wrappers — encrypt + upload + write manifest.
5897
5916
  // Require a `SharedSpaceClient` to have been provided at construction.
@@ -6025,14 +6044,6 @@ class FileStorage {
6025
6044
  await Promise.all(workers);
6026
6045
  }
6027
6046
  }
6028
- /** Normalize `data` to a flat `Uint8Array`. */
6029
- async function toUint8Array(data) {
6030
- if (data instanceof Uint8Array)
6031
- return data;
6032
- // Blob covers File too (File extends Blob in every runtime we target).
6033
- const ab = await data.arrayBuffer();
6034
- return new Uint8Array(ab);
6035
- }
6036
6047
 
6037
6048
  /**
6038
6049
  * HTTP client for the gated file-manifest store on a SharedSpace
@@ -7348,6 +7359,13 @@ class Space {
7348
7359
  async getFile(manifest) {
7349
7360
  return this.fileStorage().readFileFromShards(manifest);
7350
7361
  }
7362
+ /** Streaming read: async-iterate a stored file's decrypted chunks in order,
7363
+ * never holding the whole file in memory. Use for multi-GB files (a single
7364
+ * `Uint8Array` can't hold them) — e.g. a transcoder piping the raw to a temp
7365
+ * file. Same peer/origin shard sourcing as {@link getFile}. */
7366
+ getFileStream(manifest) {
7367
+ return this.fileStorage().readChunksFromShards(manifest);
7368
+ }
7351
7369
  /** Declare a file's shards (ref++ + meter) for a manifest already stored —
7352
7370
  * used to back-fill accounting for files written before commit existed, or to
7353
7371
  * register a manifest received via sharing. `putFile` already commits new