@muhkoo/connect 0.10.8-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/browser/index.js +45 -21
- package/dist/browser/index.js.map +1 -1
- package/dist/connect.d.ts +13 -0
- package/dist/server/index.js +45 -21
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
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
|
package/dist/server/index.js
CHANGED
|
@@ -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.
|
|
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
|
|
@@ -3499,7 +3499,13 @@ async function createPasskeyWithPrf(opts) {
|
|
|
3499
3499
|
user: { id: rand(16), name: opts.username, displayName: opts.username },
|
|
3500
3500
|
challenge: rand(32),
|
|
3501
3501
|
pubKeyCredParams: [{ type: "public-key", alg: -7 }, { type: "public-key", alg: -257 }],
|
|
3502
|
-
|
|
3502
|
+
// Force the PLATFORM authenticator (Touch ID / Windows Hello / iCloud
|
|
3503
|
+
// Keychain). Our factor needs the PRF extension to wrap the seed, and
|
|
3504
|
+
// third-party browser password-manager extensions register as cross-platform
|
|
3505
|
+
// authenticators that often DON'T implement PRF — letting one handle the
|
|
3506
|
+
// request is the main cause of "didn't return a PRF result" failures. Pinning
|
|
3507
|
+
// to platform routes it to the OS authenticator, which supports PRF.
|
|
3508
|
+
authenticatorSelection: { authenticatorAttachment: "platform", residentKey: "preferred", userVerification: "preferred" },
|
|
3503
3509
|
extensions: { prf: {} },
|
|
3504
3510
|
},
|
|
3505
3511
|
}));
|
|
@@ -5826,31 +5832,37 @@ class FileStorage {
|
|
|
5826
5832
|
*/
|
|
5827
5833
|
async writeFileToShards(input) {
|
|
5828
5834
|
await this.codec.ready(this.rsWasmModule);
|
|
5829
|
-
const data = await toUint8Array(input.data);
|
|
5830
5835
|
const fileId = input.fileId ?? generateId();
|
|
5831
5836
|
const createdAt = Date.now();
|
|
5832
5837
|
const chunks = [];
|
|
5833
|
-
|
|
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));
|
|
5834
5849
|
input.onProgress?.(0, totalChunks);
|
|
5835
|
-
//
|
|
5836
|
-
|
|
5837
|
-
// Within a chunk, the shard uploads are parallelized up to
|
|
5838
|
-
// `this.concurrency`.
|
|
5839
|
-
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++) {
|
|
5840
5852
|
const start = chunkIndex * this.chunkSize;
|
|
5841
|
-
const end = Math.min(start + this.chunkSize,
|
|
5842
|
-
const plaintext =
|
|
5853
|
+
const end = Math.min(start + this.chunkSize, size);
|
|
5854
|
+
const plaintext = await readChunk(start, end);
|
|
5843
5855
|
chunks.push(await this.writeChunk(plaintext, chunkIndex));
|
|
5844
5856
|
input.onProgress?.(chunks.length, totalChunks);
|
|
5845
5857
|
// Stop after one iteration if the file is empty — we still emit a
|
|
5846
5858
|
// single zero-length chunk so the manifest has a stable shape.
|
|
5847
|
-
if (
|
|
5859
|
+
if (size === 0)
|
|
5848
5860
|
break;
|
|
5849
5861
|
}
|
|
5850
5862
|
const manifest = {
|
|
5851
5863
|
id: fileId,
|
|
5852
5864
|
name: input.metadata.name,
|
|
5853
|
-
size
|
|
5865
|
+
size,
|
|
5854
5866
|
type: input.metadata.type,
|
|
5855
5867
|
path: input.metadata.path,
|
|
5856
5868
|
lastModified: input.metadata.lastModified ?? createdAt,
|
|
@@ -5886,6 +5898,19 @@ class FileStorage {
|
|
|
5886
5898
|
}
|
|
5887
5899
|
return { data: out, stat: manifestToStat(manifest) };
|
|
5888
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
|
+
}
|
|
5889
5914
|
// -------------------------------------------------------------------------
|
|
5890
5915
|
// Space-backed convenience wrappers — encrypt + upload + write manifest.
|
|
5891
5916
|
// Require a `SharedSpaceClient` to have been provided at construction.
|
|
@@ -6019,14 +6044,6 @@ class FileStorage {
|
|
|
6019
6044
|
await Promise.all(workers);
|
|
6020
6045
|
}
|
|
6021
6046
|
}
|
|
6022
|
-
/** Normalize `data` to a flat `Uint8Array`. */
|
|
6023
|
-
async function toUint8Array(data) {
|
|
6024
|
-
if (data instanceof Uint8Array)
|
|
6025
|
-
return data;
|
|
6026
|
-
// Blob covers File too (File extends Blob in every runtime we target).
|
|
6027
|
-
const ab = await data.arrayBuffer();
|
|
6028
|
-
return new Uint8Array(ab);
|
|
6029
|
-
}
|
|
6030
6047
|
|
|
6031
6048
|
/**
|
|
6032
6049
|
* HTTP client for the gated file-manifest store on a SharedSpace
|
|
@@ -7342,6 +7359,13 @@ class Space {
|
|
|
7342
7359
|
async getFile(manifest) {
|
|
7343
7360
|
return this.fileStorage().readFileFromShards(manifest);
|
|
7344
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
|
+
}
|
|
7345
7369
|
/** Declare a file's shards (ref++ + meter) for a manifest already stored —
|
|
7346
7370
|
* used to back-fill accounting for files written before commit existed, or to
|
|
7347
7371
|
* register a manifest received via sharing. `putFile` already commits new
|