@muhkoo/connect 0.2.0-alpha.2 → 0.3.0-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 +580 -275
- package/dist/browser/index.js.map +1 -1
- package/dist/connect.d.ts +335 -120
- package/dist/server/index.js +1204 -899
- package/dist/server/index.js.map +1 -1
- package/dist/workers/index.js +118 -3
- package/dist/workers/index.js.map +1 -1
- package/package.json +1 -1
package/dist/connect.d.ts
CHANGED
|
@@ -936,6 +936,14 @@ declare class EncryptedSession {
|
|
|
936
936
|
interface WSTransportOptions {
|
|
937
937
|
/** WebSocket URL, e.g. `ws://localhost:8787/foo`. */
|
|
938
938
|
url: string;
|
|
939
|
+
/**
|
|
940
|
+
* Stable identifier for this connection (a channel / space / personal-space
|
|
941
|
+
* id). Lifecycle + MESSAGE events are namespaced by it on the shared
|
|
942
|
+
* `EventCore` bus so concurrent transports (chat room A, room B, the storage
|
|
943
|
+
* feed, …) never receive each other's frames. Optional — a unique id is
|
|
944
|
+
* generated when omitted; pass a semantic one for clearer debugging.
|
|
945
|
+
*/
|
|
946
|
+
id?: string;
|
|
939
947
|
/** Reconnect automatically after unexpected disconnect (default `true`). */
|
|
940
948
|
autoReconnect?: boolean;
|
|
941
949
|
/** Delay between reconnect attempts in ms (default `3000`). */
|
|
@@ -947,11 +955,41 @@ interface WSTransportOptions {
|
|
|
947
955
|
* throws synchronously. Default `100`.
|
|
948
956
|
*/
|
|
949
957
|
maxQueueSize?: number;
|
|
958
|
+
/**
|
|
959
|
+
* Heartbeat interval in ms. While connected, a `pingFrame` is sent every
|
|
960
|
+
* interval to keep the socket warm (long-lived WebSockets get dropped by CF
|
|
961
|
+
* / intermediaries when idle) and to detect a silently-dead connection.
|
|
962
|
+
* Default `30000`. Set `0` to disable the heartbeat entirely.
|
|
963
|
+
*/
|
|
964
|
+
heartbeatInterval?: number;
|
|
965
|
+
/**
|
|
966
|
+
* How long (ms) to wait for a `pongFrame` after sending a ping before
|
|
967
|
+
* treating the connection as dead and forcing a reconnect. Should be well
|
|
968
|
+
* under `heartbeatInterval`. Default `10000`.
|
|
969
|
+
*/
|
|
970
|
+
heartbeatTimeout?: number;
|
|
971
|
+
/** Outbound keep-alive frame. Default `"ping"`. */
|
|
972
|
+
pingFrame?: string;
|
|
973
|
+
/**
|
|
974
|
+
* Inbound keep-alive ack. Matching frames are swallowed (never surfaced as a
|
|
975
|
+
* `MESSAGE`) and reset the liveness timer. Must match the server's
|
|
976
|
+
* auto-response. Default `"pong"`.
|
|
977
|
+
*/
|
|
978
|
+
pongFrame?: string;
|
|
950
979
|
}
|
|
951
980
|
declare class WSTransport extends EventCore {
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
981
|
+
/**
|
|
982
|
+
* Per-instance event scope. `EventCore` is a process-global static emitter,
|
|
983
|
+
* so without namespacing every WSTransport would hear every other one's
|
|
984
|
+
* frames. We prefix each event with this transport's `id` and expose
|
|
985
|
+
* `on`/`off`/`emit` that scope transparently — callers still use the plain
|
|
986
|
+
* `EventCoreEvents` enum and only ever see their own connection's events.
|
|
987
|
+
*/
|
|
988
|
+
readonly id: string;
|
|
989
|
+
readonly emit: (event: EventCoreEvents, data: unknown) => void;
|
|
990
|
+
readonly on: (event: EventCoreEvents, handler: EventListener | CallableFunction) => void;
|
|
991
|
+
readonly off: (event: EventCoreEvents, handler: EventListener | CallableFunction) => void;
|
|
992
|
+
private scopedEvent;
|
|
955
993
|
private url;
|
|
956
994
|
private socket;
|
|
957
995
|
private _isConnected;
|
|
@@ -963,6 +1001,12 @@ declare class WSTransport extends EventCore {
|
|
|
963
1001
|
private reconnectTimeout;
|
|
964
1002
|
private outboundQueue;
|
|
965
1003
|
private maxQueueSize;
|
|
1004
|
+
private heartbeatInterval;
|
|
1005
|
+
private heartbeatTimeout;
|
|
1006
|
+
private pingFrame;
|
|
1007
|
+
private pongFrame;
|
|
1008
|
+
private heartbeatTimer;
|
|
1009
|
+
private pongTimer;
|
|
966
1010
|
constructor(opts: WSTransportOptions);
|
|
967
1011
|
/** Open the socket. Resolves when CONNECTED fires. Rejects on first error. */
|
|
968
1012
|
connect(): Promise<void>;
|
|
@@ -977,6 +1021,25 @@ declare class WSTransport extends EventCore {
|
|
|
977
1021
|
isConnecting(): boolean;
|
|
978
1022
|
queuedFrames(): number;
|
|
979
1023
|
reconnectAttemptCount(): number;
|
|
1024
|
+
/** Begin the keep-alive loop (no-op when disabled). */
|
|
1025
|
+
private startHeartbeat;
|
|
1026
|
+
/** Stop the keep-alive loop and clear any pending liveness deadline. */
|
|
1027
|
+
private stopHeartbeat;
|
|
1028
|
+
/**
|
|
1029
|
+
* Send one ping and arm a liveness deadline. If no `pongFrame` arrives before
|
|
1030
|
+
* `heartbeatTimeout`, the connection is considered dead and we force a
|
|
1031
|
+
* reconnect — this is what catches a silently-dropped TCP connection that
|
|
1032
|
+
* never fired `onclose` (the failure mode that "loses connection with the
|
|
1033
|
+
* Accelerator" on long sessions).
|
|
1034
|
+
*/
|
|
1035
|
+
private sendHeartbeat;
|
|
1036
|
+
/**
|
|
1037
|
+
* Tear down a connection we believe is dead and kick off reconnect, without
|
|
1038
|
+
* waiting on the OS/browser to eventually fire `onclose` (which a half-open
|
|
1039
|
+
* socket may never do). We detach the handlers first so the normal close path
|
|
1040
|
+
* can't double-fire the reconnect.
|
|
1041
|
+
*/
|
|
1042
|
+
private forceReconnect;
|
|
980
1043
|
private scheduleReconnect;
|
|
981
1044
|
private flushQueue;
|
|
982
1045
|
}
|
|
@@ -1382,123 +1445,50 @@ declare class ShardClient {
|
|
|
1382
1445
|
}
|
|
1383
1446
|
|
|
1384
1447
|
/**
|
|
1385
|
-
* HTTP client for the
|
|
1386
|
-
*
|
|
1387
|
-
* Mirrors the proven shape of `PersonalSpaceClient` — every gated call:
|
|
1388
|
-
* 1. POSTs `/challenge` to get a fresh nonce keyed to the caller's commitment
|
|
1389
|
-
* 2. Generates a Groth16 proof for the `preimagePoK` circuit with the nonce
|
|
1390
|
-
* reduced into a BN254 field element as the public signal
|
|
1391
|
-
* 3. POSTs the operation with `{ challengeId, proof, publicSignals, ... }`
|
|
1392
|
-
*
|
|
1393
|
-
* What's different from `PersonalSpaceClient`:
|
|
1394
|
-
* - Addressed by space ID, not user commitment. Multiple users can be
|
|
1395
|
-
* members of the same space.
|
|
1396
|
-
* - The space carries a participants list + ACL (shared types from
|
|
1397
|
-
* `src/types/permissions.ts`). The server checks that the proving
|
|
1398
|
-
* commitment is a participant with the requested permission before each
|
|
1399
|
-
* mutation.
|
|
1400
|
-
* - Stores file manifests (not generic KV blobs). The shape is constrained
|
|
1401
|
-
* by `FileManifest` from `../types`.
|
|
1448
|
+
* HTTP client for the gated file-manifest store on a SharedSpace
|
|
1449
|
+
* (`/api/spaces/:spaceId/files`).
|
|
1402
1450
|
*
|
|
1403
|
-
*
|
|
1404
|
-
*
|
|
1451
|
+
* A "file" in a space is its **manifest** (per-chunk keys + the SHA-256 of each
|
|
1452
|
+
* global shard). The shard bytes live in the open `/api/shards` store; the
|
|
1453
|
+
* manifest is the capability. This client only moves manifests in/out of the
|
|
1454
|
+
* space's gated store.
|
|
1405
1455
|
*
|
|
1406
|
-
*
|
|
1407
|
-
*
|
|
1408
|
-
*
|
|
1456
|
+
* Authorization is **session-based**, consistent with the rest of the
|
|
1457
|
+
* SharedSpace (keyring, websocket): the SDK's credential-stamping `fetch`
|
|
1458
|
+
* (from {@link HttpClient}) attaches the app key + session token, the platform
|
|
1459
|
+
* validates the session and injects a trusted `X-Muhkoo-User-Context`, and the
|
|
1460
|
+
* DO authorizes on that + channel membership. There is no per-op Groth16 proof.
|
|
1409
1461
|
*
|
|
1410
|
-
*
|
|
1411
|
-
*
|
|
1412
|
-
*
|
|
1413
|
-
*
|
|
1414
|
-
*
|
|
1415
|
-
* body: { challengeId, proof, publicSignals, manifest }
|
|
1416
|
-
* returns: { ok: true, fileId }
|
|
1417
|
-
*
|
|
1418
|
-
* POST /api/spaces/:spaceId/files/:fileId/get read a file manifest
|
|
1419
|
-
* body: { challengeId, proof, publicSignals }
|
|
1420
|
-
* returns: { manifest }
|
|
1421
|
-
*
|
|
1422
|
-
* DELETE /api/spaces/:spaceId/files/:fileId delete a file manifest
|
|
1423
|
-
* body: { challengeId, proof, publicSignals }
|
|
1424
|
-
* returns: { ok: true, existed: boolean }
|
|
1425
|
-
*
|
|
1426
|
-
* POST /api/spaces/:spaceId/files/list list manifests in space
|
|
1427
|
-
* body: { challengeId, proof, publicSignals }
|
|
1428
|
-
* returns: { files: FileStat[] }
|
|
1429
|
-
*
|
|
1430
|
-
* POST /api/spaces/:spaceId/participants add a participant
|
|
1431
|
-
* body: { challengeId, proof, publicSignals, participant: Participant }
|
|
1432
|
-
* returns: { ok: true }
|
|
1433
|
-
*
|
|
1434
|
-
* DELETE /api/spaces/:spaceId/participants/:commitment remove a participant
|
|
1435
|
-
* body: { challengeId, proof, publicSignals }
|
|
1436
|
-
* returns: { ok: true, existed: boolean }
|
|
1437
|
-
*
|
|
1438
|
-
* POST /api/spaces/:spaceId/acl set / replace an ACL entry
|
|
1439
|
-
* body: { challengeId, proof, publicSignals, entry: ACLEntry }
|
|
1440
|
-
* returns: { ok: true }
|
|
1462
|
+
* Wire protocol (server: `SharedSpaceDO`):
|
|
1463
|
+
* POST /api/spaces/:spaceId/files body: { manifest } → { ok, fileId }
|
|
1464
|
+
* GET /api/spaces/:spaceId/files → { files: FileStat[] }
|
|
1465
|
+
* GET /api/spaces/:spaceId/files/:fileId → { manifest }
|
|
1466
|
+
* DELETE /api/spaces/:spaceId/files/:fileId → { ok, existed }
|
|
1441
1467
|
*/
|
|
1442
1468
|
|
|
1443
1469
|
interface SharedSpaceClientOptions {
|
|
1444
|
-
/** Base URL of the accelerator. */
|
|
1470
|
+
/** Base URL of the accelerator (e.g. `https://api.muhkoo.dev`). */
|
|
1445
1471
|
baseUrl: string;
|
|
1446
|
-
/**
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
ecdsaPub: string;
|
|
1452
|
-
/** Public signal: Poseidon hash of `ecdsaPub`. */
|
|
1453
|
-
ecdsaPubHash: string;
|
|
1454
|
-
/** Compiled circuit assets — same files PersonalSpaceClient uses. */
|
|
1455
|
-
circuits: {
|
|
1456
|
-
wasmUrl: string;
|
|
1457
|
-
zkeyUrl: string;
|
|
1458
|
-
};
|
|
1459
|
-
/** Optional custom fetch (defaults to `globalThis.fetch`). */
|
|
1472
|
+
/**
|
|
1473
|
+
* Credential-stamping fetch — pass {@link HttpClient.fetch} so requests
|
|
1474
|
+
* carry the app key + session token. Defaults to `globalThis.fetch`
|
|
1475
|
+
* (unauthenticated; only useful in tests).
|
|
1476
|
+
*/
|
|
1460
1477
|
fetch?: typeof fetch;
|
|
1461
1478
|
}
|
|
1462
1479
|
declare class SharedSpaceClient {
|
|
1463
1480
|
private readonly baseUrl;
|
|
1464
|
-
private readonly commitment;
|
|
1465
|
-
private readonly secret;
|
|
1466
|
-
private readonly salt;
|
|
1467
|
-
private readonly ecdsaPub;
|
|
1468
|
-
private readonly ecdsaPubHash;
|
|
1469
|
-
private readonly wasmUrl;
|
|
1470
|
-
private readonly zkeyUrl;
|
|
1471
1481
|
private readonly fetchFn;
|
|
1472
1482
|
constructor(opts: SharedSpaceClientOptions);
|
|
1473
|
-
/**
|
|
1474
|
-
* Create a brand new space. The caller becomes the owner; `initialAcl`
|
|
1475
|
-
* adds any additional ACL entries up front (e.g. inviting a co-owner at
|
|
1476
|
-
* creation time).
|
|
1477
|
-
*/
|
|
1478
|
-
createSpace(initialAcl?: ACLEntry[]): Promise<{
|
|
1479
|
-
spaceId: string;
|
|
1480
|
-
}>;
|
|
1481
|
-
/** Write a file manifest into the space. Requires `write` permission. */
|
|
1483
|
+
/** Write a file manifest into the space. Requires space membership. */
|
|
1482
1484
|
writeFileManifest(spaceId: string, manifest: FileManifest): Promise<void>;
|
|
1483
|
-
/** Read a file manifest by id. Requires
|
|
1485
|
+
/** Read a file manifest by id. Requires space membership. */
|
|
1484
1486
|
readFileManifest(spaceId: string, fileId: string): Promise<FileManifest>;
|
|
1485
|
-
/** Delete a file manifest by id. Requires
|
|
1487
|
+
/** Delete a file manifest by id. Requires space membership. */
|
|
1486
1488
|
deleteFileManifest(spaceId: string, fileId: string): Promise<boolean>;
|
|
1487
|
-
/** List file summaries in the space. Requires
|
|
1489
|
+
/** List file summaries in the space. Requires space membership. */
|
|
1488
1490
|
listFiles(spaceId: string): Promise<FileStat[]>;
|
|
1489
|
-
|
|
1490
|
-
addParticipant(spaceId: string, participant: Participant): Promise<void>;
|
|
1491
|
-
/** Remove a participant by their public-key identity. Requires `admin`. */
|
|
1492
|
-
removeParticipant(spaceId: string, participantCommitment: string): Promise<boolean>;
|
|
1493
|
-
/** Set / replace an ACL entry. Requires `admin`. */
|
|
1494
|
-
setACLEntry(spaceId: string, entry: ACLEntry): Promise<void>;
|
|
1495
|
-
/** Request a challenge for `spaceId` and generate the matching proof. */
|
|
1496
|
-
private proveFreshChallenge;
|
|
1497
|
-
/**
|
|
1498
|
-
* Hit `/challenge`. The body includes the caller's commitment so the
|
|
1499
|
-
* server can scope the issued nonce to this principal.
|
|
1500
|
-
*/
|
|
1501
|
-
private requestChallenge;
|
|
1491
|
+
private filesBase;
|
|
1502
1492
|
private jsonRequest;
|
|
1503
1493
|
}
|
|
1504
1494
|
|
|
@@ -2158,6 +2148,30 @@ declare class ZkAuth {
|
|
|
2158
2148
|
* both the session and the identity are stored on the client.
|
|
2159
2149
|
*/
|
|
2160
2150
|
login(username: string, password: string, opts?: LoginOptions): Promise<AuthUser>;
|
|
2151
|
+
/**
|
|
2152
|
+
* Silently re-authenticate the current user without prompting for a
|
|
2153
|
+
* password. Only possible while the client is **unlocked** — i.e. the
|
|
2154
|
+
* derived {@link ZkIdentity} (secret + keypairs) is still in memory from a
|
|
2155
|
+
* prior `login()`/`unlock()`. Re-runs the full challenge→proof→authenticate
|
|
2156
|
+
* dance with that in-memory identity and swaps in the fresh token.
|
|
2157
|
+
*
|
|
2158
|
+
* Returns `true` if a new session was minted, `false` if recovery isn't
|
|
2159
|
+
* possible (no active user, or locked — the identity was never derived or
|
|
2160
|
+
* was lost on reload). A `false` result is the app's cue to send the user
|
|
2161
|
+
* back to the login screen.
|
|
2162
|
+
*
|
|
2163
|
+
* Used automatically by the {@link Client} when a token-gated request comes
|
|
2164
|
+
* back `401` (stale/expired token), so transient session expiry self-heals
|
|
2165
|
+
* instead of surfacing errors mid-session.
|
|
2166
|
+
*/
|
|
2167
|
+
recover(): Promise<boolean>;
|
|
2168
|
+
/**
|
|
2169
|
+
* Shared sign-in core: prove knowledge of `identity` against a fresh
|
|
2170
|
+
* challenge and persist the resulting session + identity. Used by both
|
|
2171
|
+
* `login()` (identity derived from a password) and `recover()` (identity
|
|
2172
|
+
* already in memory).
|
|
2173
|
+
*/
|
|
2174
|
+
private proveAndStore;
|
|
2161
2175
|
/**
|
|
2162
2176
|
* Restore a persisted session on boot. Validates the stored token with the
|
|
2163
2177
|
* accelerator; returns the user when valid, or `null` (and clears the
|
|
@@ -2231,6 +2245,18 @@ interface HttpClientOptions {
|
|
|
2231
2245
|
getSessionToken?: () => string | null;
|
|
2232
2246
|
/** Custom fetch — defaults to `globalThis.fetch`. */
|
|
2233
2247
|
fetch?: typeof fetch;
|
|
2248
|
+
/**
|
|
2249
|
+
* Called when a token-gated request comes back `401` (stale/expired
|
|
2250
|
+
* session). Should attempt to silently re-establish a session and resolve
|
|
2251
|
+
* `true` if it succeeded (the request is then retried once with the fresh
|
|
2252
|
+
* token) or `false` if it couldn't (the `401` propagates as an
|
|
2253
|
+
* {@link HttpError}). Wired by {@link Client} to `auth.zk.recover()`.
|
|
2254
|
+
*
|
|
2255
|
+
* Only consulted by the JSON helpers (`get`/`post`/`del`); the raw
|
|
2256
|
+
* {@link fetch} passes 401s straight through so the auth wire calls that
|
|
2257
|
+
* power recovery itself can't recurse.
|
|
2258
|
+
*/
|
|
2259
|
+
onUnauthorized?: () => Promise<boolean>;
|
|
2234
2260
|
}
|
|
2235
2261
|
/** Thrown for any non-2xx accelerator response. Carries the HTTP status. */
|
|
2236
2262
|
declare class HttpError extends Error {
|
|
@@ -2242,6 +2268,7 @@ declare class HttpClient {
|
|
|
2242
2268
|
readonly baseUrl: string;
|
|
2243
2269
|
private readonly apiKey;
|
|
2244
2270
|
private readonly getSessionToken;
|
|
2271
|
+
private readonly onUnauthorized;
|
|
2245
2272
|
private readonly fetchFn;
|
|
2246
2273
|
constructor(opts: HttpClientOptions);
|
|
2247
2274
|
/**
|
|
@@ -2257,14 +2284,14 @@ declare class HttpClient {
|
|
|
2257
2284
|
}
|
|
2258
2285
|
|
|
2259
2286
|
/**
|
|
2260
|
-
* `client.
|
|
2261
|
-
*
|
|
2287
|
+
* `client.kv` — per-user key/value storage backed by the accelerator's personal
|
|
2288
|
+
* space.
|
|
2262
2289
|
*
|
|
2263
|
-
* await client.
|
|
2264
|
-
* const todo = await client.
|
|
2265
|
-
* const ids = await client.
|
|
2266
|
-
* await client.
|
|
2267
|
-
* const off = client.
|
|
2290
|
+
* await client.kv.set('todos', id, { title, completed })
|
|
2291
|
+
* const todo = await client.kv.get('todos', id)
|
|
2292
|
+
* const ids = await client.kv.list('todos')
|
|
2293
|
+
* await client.kv.delete('todos', id)
|
|
2294
|
+
* const off = client.kv.on('change', e => …) // realtime, cross-device
|
|
2268
2295
|
*
|
|
2269
2296
|
* A *collection* is a key namespace within the user's space; the stored key is
|
|
2270
2297
|
* `${collection}/${id}`. Operations are authorized by the user's session token
|
|
@@ -2272,13 +2299,15 @@ declare class HttpClient {
|
|
|
2272
2299
|
*
|
|
2273
2300
|
* Values are encrypted at rest by default ({@link StorageCipher}, an AES-GCM
|
|
2274
2301
|
* key derived from the ZK identity); pass `{ encrypt: false }` to store
|
|
2275
|
-
* plaintext. The realtime feed rides the personal space's
|
|
2276
|
-
*
|
|
2302
|
+
* plaintext. The realtime feed rides the personal space's websocket over a
|
|
2303
|
+
* {@link WSTransport} (heartbeat keep-alive + auto-reconnect, event-namespaced
|
|
2304
|
+
* so it doesn't cross wires with chat/space transports).
|
|
2277
2305
|
*
|
|
2278
|
-
*
|
|
2306
|
+
* For **files** (blobs), use `client.storage` instead — `client.kv` is for
|
|
2307
|
+
* small structured values. `query()` is intentionally deferred.
|
|
2279
2308
|
*/
|
|
2280
2309
|
|
|
2281
|
-
interface
|
|
2310
|
+
interface KvNamespaceDeps {
|
|
2282
2311
|
http: HttpClient;
|
|
2283
2312
|
session: SessionState;
|
|
2284
2313
|
/** WebSocket base (ws/wss) for the change feed; derived from baseUrl. */
|
|
@@ -2295,11 +2324,11 @@ interface StorageChangeEvent<T = unknown> {
|
|
|
2295
2324
|
/** The decrypted value for `set`; `null` for `delete`. */
|
|
2296
2325
|
data: T | null;
|
|
2297
2326
|
}
|
|
2298
|
-
declare class
|
|
2327
|
+
declare class KvNamespace {
|
|
2299
2328
|
private readonly deps;
|
|
2300
2329
|
private cipher;
|
|
2301
2330
|
private cipherIdentity;
|
|
2302
|
-
constructor(deps:
|
|
2331
|
+
constructor(deps: KvNamespaceDeps);
|
|
2303
2332
|
/** Store `value` under `collection`/`id` (encrypted at rest by default). */
|
|
2304
2333
|
set<T = unknown>(collection: string, id: string, value: T, opts?: SetOptions): Promise<void>;
|
|
2305
2334
|
/** Fetch the value at `collection`/`id`, or `null` if absent. */
|
|
@@ -2328,6 +2357,100 @@ declare class StorageNamespace {
|
|
|
2328
2357
|
private getCipher;
|
|
2329
2358
|
}
|
|
2330
2359
|
|
|
2360
|
+
/**
|
|
2361
|
+
* `client.storage` — file storage. Hides the `FileStorage` / `ShardClient` /
|
|
2362
|
+
* `SharedSpaceClient` primitives behind the unified client.
|
|
2363
|
+
*
|
|
2364
|
+
* const { stat, manifest } = await client.storage.writeFile({ spaceId, data, metadata })
|
|
2365
|
+
* const { data } = await client.storage.readFile(spaceId, fileId)
|
|
2366
|
+
* const files = await client.storage.listFiles() // my shared files
|
|
2367
|
+
* const files = await client.storage.listFiles({ spaceId }) // a space's files
|
|
2368
|
+
* await client.storage.deleteFile(spaceId, fileId)
|
|
2369
|
+
*
|
|
2370
|
+
* Model:
|
|
2371
|
+
* - **Files are written to a SharedSpace** (never the personal space). A file
|
|
2372
|
+
* is its manifest (per-chunk keys + global shard hashes); the manifest lives
|
|
2373
|
+
* in the space's gated, metered `/files` store, the shard ciphertext in the
|
|
2374
|
+
* global open `/api/shards` store.
|
|
2375
|
+
* - **The manifest is the capability** — anyone holding it reads the file from
|
|
2376
|
+
* the global shards, anywhere ({@link readByManifest}). Share a file by
|
|
2377
|
+
* sharing its manifest.
|
|
2378
|
+
* - **Personal discovery mirror** — on a successful write the SDK also records
|
|
2379
|
+
* the manifest in the user's `client.kv` under a reserved collection, so
|
|
2380
|
+
* {@link listFiles} (no `spaceId`) can show "files I've shared" without
|
|
2381
|
+
* enumerating spaces. The mirror is unmetered and is NOT a write path.
|
|
2382
|
+
*
|
|
2383
|
+
* For small structured values use `client.kv` instead.
|
|
2384
|
+
*/
|
|
2385
|
+
|
|
2386
|
+
interface FileNamespaceDeps {
|
|
2387
|
+
http: HttpClient;
|
|
2388
|
+
baseUrl: string;
|
|
2389
|
+
/** Used for the unmetered personal discovery mirror. */
|
|
2390
|
+
kv: KvNamespace;
|
|
2391
|
+
/**
|
|
2392
|
+
* Optional pre-compiled Reed–Solomon WASM module. Defaults to the bundled
|
|
2393
|
+
* loader; pass one where the bundled loader path isn't available (vitest,
|
|
2394
|
+
* deploy-time-precompiled environments). Forwarded to {@link FileStorage}.
|
|
2395
|
+
*/
|
|
2396
|
+
rsWasmModule?: WebAssembly.Module;
|
|
2397
|
+
}
|
|
2398
|
+
interface WriteFileOptions {
|
|
2399
|
+
/** SharedSpace the file is written to (obtain via `client.space`). */
|
|
2400
|
+
spaceId: string;
|
|
2401
|
+
data: Uint8Array | Blob | File;
|
|
2402
|
+
metadata: {
|
|
2403
|
+
name: string;
|
|
2404
|
+
type: string;
|
|
2405
|
+
path?: string;
|
|
2406
|
+
lastModified?: number;
|
|
2407
|
+
};
|
|
2408
|
+
}
|
|
2409
|
+
declare class StorageNamespace {
|
|
2410
|
+
private readonly deps;
|
|
2411
|
+
private fileStorage;
|
|
2412
|
+
constructor(deps: FileNamespaceDeps);
|
|
2413
|
+
/**
|
|
2414
|
+
* Write a file to a SharedSpace: shard ciphertext → the global store, the
|
|
2415
|
+
* manifest → the space's gated store, and a mirror copy → the caller's
|
|
2416
|
+
* personal index. Returns the `stat` and the `manifest` (share the manifest
|
|
2417
|
+
* to let others read the file).
|
|
2418
|
+
*/
|
|
2419
|
+
writeFile(input: WriteFileOptions): Promise<{
|
|
2420
|
+
stat: FileStat;
|
|
2421
|
+
manifest: FileManifest;
|
|
2422
|
+
}>;
|
|
2423
|
+
/** Read a file from its space (resolves the manifest, then the shards). */
|
|
2424
|
+
readFile(spaceId: string, fileId: string): Promise<{
|
|
2425
|
+
data: Uint8Array;
|
|
2426
|
+
stat: FileStat;
|
|
2427
|
+
}>;
|
|
2428
|
+
/**
|
|
2429
|
+
* Read a file straight from a manifest — the capability path. No space
|
|
2430
|
+
* membership needed; this is how a shared file is opened.
|
|
2431
|
+
*/
|
|
2432
|
+
readByManifest(manifest: FileManifest): Promise<{
|
|
2433
|
+
data: Uint8Array;
|
|
2434
|
+
stat: FileStat;
|
|
2435
|
+
}>;
|
|
2436
|
+
/**
|
|
2437
|
+
* List files. With no argument, returns the caller's own shared files from
|
|
2438
|
+
* the personal mirror; with `{ spaceId }`, lists that space's files.
|
|
2439
|
+
*/
|
|
2440
|
+
listFiles(opts?: {
|
|
2441
|
+
spaceId?: string;
|
|
2442
|
+
}): Promise<FileStat[]>;
|
|
2443
|
+
/** Delete a file from its space and remove the personal mirror entry. */
|
|
2444
|
+
deleteFile(spaceId: string, fileId: string): Promise<boolean>;
|
|
2445
|
+
/** Read a manifest verbatim (e.g. to share a file). */
|
|
2446
|
+
getManifest(spaceId: string, fileId: string): Promise<FileManifest>;
|
|
2447
|
+
private fs;
|
|
2448
|
+
private _shards;
|
|
2449
|
+
private shards;
|
|
2450
|
+
private _space;
|
|
2451
|
+
private space;
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2331
2454
|
/**
|
|
2332
2455
|
* Shared types for the Space fan-out group-encryption layer.
|
|
2333
2456
|
*
|
|
@@ -2631,8 +2754,35 @@ interface SpaceMessageEvent {
|
|
|
2631
2754
|
channel: string;
|
|
2632
2755
|
epoch: number;
|
|
2633
2756
|
contentType?: string;
|
|
2757
|
+
/**
|
|
2758
|
+
* Server storage handle — the monotonic server timestamp this message is
|
|
2759
|
+
* persisted under (`msg:<handle>`). Stable across history replay; pass it to
|
|
2760
|
+
* {@link Space.editMessage} / {@link Space.deleteMessage} to mutate the
|
|
2761
|
+
* persisted entry. Carried on edit events too (same handle as the original).
|
|
2762
|
+
*/
|
|
2763
|
+
handle: number;
|
|
2634
2764
|
message: Message;
|
|
2635
2765
|
}
|
|
2766
|
+
/**
|
|
2767
|
+
* An inbound ephemeral signal — an application broadcast the server relays to
|
|
2768
|
+
* the room but never persists (no history). Rides the generic `pub` relay:
|
|
2769
|
+
* `subject` is an app-defined routing key, `data` is opaque app JSON, and
|
|
2770
|
+
* `from` is the server-authenticated sender. Typing indicators, presence pings,
|
|
2771
|
+
* live cursors, etc. are built on top of this in the app layer — the SDK and
|
|
2772
|
+
* server stay domain-agnostic.
|
|
2773
|
+
*/
|
|
2774
|
+
interface EphemeralEvent {
|
|
2775
|
+
/** Server-stamped authenticated sender. */
|
|
2776
|
+
from: string;
|
|
2777
|
+
/** Application-defined routing key (e.g. "typing"). */
|
|
2778
|
+
subject: string;
|
|
2779
|
+
/** Opaque application payload. */
|
|
2780
|
+
data: unknown;
|
|
2781
|
+
}
|
|
2782
|
+
/** A server-authoritative deletion of the persisted message at `handle`. */
|
|
2783
|
+
interface MessageDeletedEvent {
|
|
2784
|
+
handle: number;
|
|
2785
|
+
}
|
|
2636
2786
|
interface SpaceDeps {
|
|
2637
2787
|
/** Transport room id. For fan-out spaces this is the encoded space pubkey. */
|
|
2638
2788
|
name: string;
|
|
@@ -2715,6 +2865,38 @@ declare class Space {
|
|
|
2715
2865
|
}): Promise<void>;
|
|
2716
2866
|
/** Subscribe to decrypted fan-out messages. Returns an unsubscribe fn. */
|
|
2717
2867
|
onMessage(handler: (e: SpaceMessageEvent) => void): () => void;
|
|
2868
|
+
/**
|
|
2869
|
+
* Edit a persisted message IN PLACE, addressed by its server
|
|
2870
|
+
* {@link SpaceMessageEvent.handle}. Re-seals `payload` and asks the server
|
|
2871
|
+
* to replace the stored entry; the server authorizes against the original
|
|
2872
|
+
* author. Receivers get an {@link onMessageEdited} event carrying the new
|
|
2873
|
+
* content at the same handle. This is a generic persisted-item edit — the
|
|
2874
|
+
* server has no notion of what the payload means.
|
|
2875
|
+
*/
|
|
2876
|
+
editMessage(handle: number, payload: unknown, opts?: {
|
|
2877
|
+
channel?: string;
|
|
2878
|
+
contentType?: string;
|
|
2879
|
+
}): Promise<void>;
|
|
2880
|
+
/**
|
|
2881
|
+
* Hard-delete a persisted message by its server {@link SpaceMessageEvent.handle}
|
|
2882
|
+
* — the ciphertext is removed from storage. Server authorizes against the
|
|
2883
|
+
* original author. Receivers get an {@link onMessageDeleted} event.
|
|
2884
|
+
*/
|
|
2885
|
+
deleteMessage(handle: number): Promise<void>;
|
|
2886
|
+
/** Subscribe to in-place edits of persisted messages (new content at the same handle). */
|
|
2887
|
+
onMessageEdited(handler: (e: SpaceMessageEvent) => void): () => void;
|
|
2888
|
+
/** Subscribe to deletions of persisted messages. */
|
|
2889
|
+
onMessageDeleted(handler: (e: MessageDeletedEvent) => void): () => void;
|
|
2890
|
+
/**
|
|
2891
|
+
* Broadcast an ephemeral, never-persisted signal to the room over the
|
|
2892
|
+
* generic `pub` relay. `subject` is an app-defined routing key; `data` is
|
|
2893
|
+
* opaque. The transport stays domain-agnostic — app concepts like typing
|
|
2894
|
+
* indicators, presence, or live cursors are built on top of this. No-op if
|
|
2895
|
+
* the space isn't connected (ephemeral signals aren't worth queuing).
|
|
2896
|
+
*/
|
|
2897
|
+
sendEphemeral(subject: string, data: unknown): void;
|
|
2898
|
+
/** Subscribe to inbound ephemeral signals. `from` is the authenticated sender. */
|
|
2899
|
+
onEphemeral(handler: (e: EphemeralEvent) => void): () => void;
|
|
2718
2900
|
/** Subscribe to inbound join requests (for a key-holder to admit). */
|
|
2719
2901
|
onJoinRequest(handler: (req: JoinRequest) => void): () => void;
|
|
2720
2902
|
/**
|
|
@@ -2759,10 +2941,18 @@ declare class Space {
|
|
|
2759
2941
|
/** Wire the RAW_FRAME handler that drives fan-out events. */
|
|
2760
2942
|
private wireFanout;
|
|
2761
2943
|
private onRawFrame;
|
|
2944
|
+
/**
|
|
2945
|
+
* Seal `payload` once with the current group key + sign it, returning the
|
|
2946
|
+
* serialized `Packet` string. Shared by send (a new message) and edit
|
|
2947
|
+
* (replacement content for an existing handle). Requires a keyring.
|
|
2948
|
+
*/
|
|
2949
|
+
private sealMessage;
|
|
2762
2950
|
/**
|
|
2763
2951
|
* Decode one persisted/broadcast frame into a SpaceMessageEvent.
|
|
2764
2952
|
* `isRawFrame` true means `input` is the already-extracted `spaceMessage`
|
|
2765
|
-
* packet string
|
|
2953
|
+
* packet string (and `serverTs` carries the message's storage handle from
|
|
2954
|
+
* the enclosing frame); false means `input` is the full
|
|
2955
|
+
* `{spaceMessage, name, timestamp}` frame and the handle is read from it.
|
|
2766
2956
|
*/
|
|
2767
2957
|
private decodeFrame;
|
|
2768
2958
|
/**
|
|
@@ -2990,7 +3180,9 @@ declare class Client {
|
|
|
2990
3180
|
readonly baseUrl: string;
|
|
2991
3181
|
/** Authentication — `client.auth.zk.login(...)`, etc. */
|
|
2992
3182
|
readonly auth: AuthNamespace;
|
|
2993
|
-
/** Per-user
|
|
3183
|
+
/** Per-user key/value storage — `client.kv.set(...)`, etc. */
|
|
3184
|
+
readonly kv: KvNamespace;
|
|
3185
|
+
/** File storage — `client.storage.writeFile(...)`, etc. */
|
|
2994
3186
|
readonly storage: StorageNamespace;
|
|
2995
3187
|
/** Realtime messaging — `client.message.subscribe(...)`, etc. */
|
|
2996
3188
|
readonly message: MessageNamespace;
|
|
@@ -2998,11 +3190,34 @@ declare class Client {
|
|
|
2998
3190
|
readonly space: SpaceNamespace;
|
|
2999
3191
|
private readonly session;
|
|
3000
3192
|
private readonly http;
|
|
3193
|
+
/** Subscribers notified when a session expires and can't be recovered. */
|
|
3194
|
+
private readonly sessionExpiredHandlers;
|
|
3195
|
+
/** Shared in-flight recovery so a burst of 401s triggers one re-auth. */
|
|
3196
|
+
private recoverInFlight;
|
|
3001
3197
|
constructor(options?: ClientOptions);
|
|
3002
3198
|
/** The currently signed-in user, or `null`. */
|
|
3003
3199
|
get user(): AuthUser | null;
|
|
3004
3200
|
/** Whether a token-bearing session is active. */
|
|
3005
3201
|
get isAuthenticated(): boolean;
|
|
3202
|
+
/**
|
|
3203
|
+
* Subscribe to session expiry that couldn't be silently recovered (the
|
|
3204
|
+
* user's identity wasn't in memory to re-prove — typically after a reload
|
|
3205
|
+
* where only the token was persisted, and that token has since gone stale).
|
|
3206
|
+
* This is the signal to send the user back to the login screen.
|
|
3207
|
+
*
|
|
3208
|
+
* Returns an unsubscribe function.
|
|
3209
|
+
*
|
|
3210
|
+
* const off = client.onSessionExpired(() => router.push('/login'));
|
|
3211
|
+
*/
|
|
3212
|
+
onSessionExpired(handler: () => void): () => void;
|
|
3213
|
+
/**
|
|
3214
|
+
* Attempt a silent re-auth of the current user. Concurrent callers share a
|
|
3215
|
+
* single in-flight attempt (a stale token usually 401s several requests at
|
|
3216
|
+
* once — we re-auth once, not once per request). On failure the
|
|
3217
|
+
* session-expired event fires so the app can react. Exposed so apps can
|
|
3218
|
+
* also drive recovery proactively (e.g. on `visibilitychange`).
|
|
3219
|
+
*/
|
|
3220
|
+
recoverSession(): Promise<boolean>;
|
|
3006
3221
|
}
|
|
3007
3222
|
|
|
3008
3223
|
/**
|
|
@@ -3043,5 +3258,5 @@ declare class KeyringClient implements KeyringTransport {
|
|
|
3043
3258
|
fetchMetadata(): Promise<SpaceMetadata | null>;
|
|
3044
3259
|
}
|
|
3045
3260
|
|
|
3046
|
-
export { AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AuthClient, AuthNamespace, AuthPublicInput, Authenticator, BroadcastChannel, BroadcastChannelEvents, ChannelExistsError, ChannelNotFoundError, ChunkCipher, Client, DEFAULT_BASE_URL, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, HashKnowledge, HttpClient, HttpError, KEEPER_MEMBER_ID, KeyStore, KeyringClient, LocalStorageSessionStore, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PersonalSpaceClient, Poseidon, PreimagePoK, ReedSolomonCodec, Space as Room, SerializeMessage, SessionState, ShardClient, SharedSpaceClient, Space, SpaceKeyring, SpaceNamespace, SpacePacketCipher, StorageNamespace, WSTransport, ZkAuth, buildCommitment, compilePrograms, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultSessionStore, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, encodeToHex, encryptAesGcm, exportPublicKeyBase64, exportPublicKeyHex, generateAuthProof, getSubtle, initBn128Wasm, initRsWasm, initializeCircuits, manifestToStat, poseidonHash, quickVerify, randomBytes, shardHash, signMessage, toField, unwrapWithPassphrase, verify, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase };
|
|
3047
|
-
export type { ACL, ACLEntry, AuthClientOptions, AuthKeyPair, AuthSuccess, AuthToken, AuthType, AuthUser, Bn128WasmInstance, BroadcastChannelEvent, BroadcastChannelOptions, ChannelLike, ChunkKeyMaterial, ChunkManifest, CipherFrame, CipherMessage, CipherMessageHeader, CircuitBufferConfig, CircuitUrls, ClientOptions, DehydratedKeypair, DehydratedKeys, DoubleRatchetCipherDeps, ECDHCurve, EncodeResult, EpochKeyProvider, FileManifest, FileStat, FileStorageOptions, Groth16Proof, HttpClientOptions, IncomingFrame, KeyExchangeFrame, KeyPair, Keypair, KeyringClientDeps, KeyringTransport, LoginOptions, MessageBody, MessageEvent, MessageHeaders, MessageNamespaceDeps, MessageOptions, MessageSubscription, MuhkooMessageEvent, OAuthProvider, PacketCipher, PacketHeaders, PacketOptions, Participant, Permission, PermissionCheckRequest, PermissionCheckResult, PersonalSpaceClientOptions, PublicKeyJWK, RatchetState, ReceiveResult, RegisterParams, Role, SpaceDeps as RoomDeps, SpaceFileMetadata as RoomFileMetadata, SealedHeaders, Session, SessionOptions, SessionStore, SetOptions, ShardClientOptions, SharedSpaceClientOptions, SnarkProof, SpaceChannelLike, SpaceDeps, SpaceFileMetadata, SpaceKeyCache, SpaceKeyringDeps, SpaceMessageEvent, SpaceNamespaceDeps, StorageChangeEvent,
|
|
3261
|
+
export { AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AuthClient, AuthNamespace, AuthPublicInput, Authenticator, BroadcastChannel, BroadcastChannelEvents, ChannelExistsError, ChannelNotFoundError, ChunkCipher, Client, DEFAULT_BASE_URL, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, HashKnowledge, HttpClient, HttpError, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KvNamespace, LocalStorageSessionStore, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PersonalSpaceClient, Poseidon, PreimagePoK, ReedSolomonCodec, Space as Room, SerializeMessage, SessionState, ShardClient, SharedSpaceClient, Space, SpaceKeyring, SpaceNamespace, SpacePacketCipher, StorageNamespace, WSTransport, ZkAuth, buildCommitment, compilePrograms, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultSessionStore, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, encodeToHex, encryptAesGcm, exportPublicKeyBase64, exportPublicKeyHex, generateAuthProof, getSubtle, initBn128Wasm, initRsWasm, initializeCircuits, manifestToStat, poseidonHash, quickVerify, randomBytes, shardHash, signMessage, toField, unwrapWithPassphrase, verify, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase };
|
|
3262
|
+
export type { ACL, ACLEntry, AuthClientOptions, AuthKeyPair, AuthSuccess, AuthToken, AuthType, AuthUser, Bn128WasmInstance, BroadcastChannelEvent, BroadcastChannelOptions, ChannelLike, ChunkKeyMaterial, ChunkManifest, CipherFrame, CipherMessage, CipherMessageHeader, CircuitBufferConfig, CircuitUrls, ClientOptions, DehydratedKeypair, DehydratedKeys, DoubleRatchetCipherDeps, ECDHCurve, EncodeResult, EphemeralEvent, EpochKeyProvider, FileManifest, FileNamespaceDeps, FileStat, FileStorageOptions, Groth16Proof, HttpClientOptions, IncomingFrame, KeyExchangeFrame, KeyPair, Keypair, KeyringClientDeps, KeyringTransport, KvNamespaceDeps, LoginOptions, MessageBody, MessageDeletedEvent, MessageEvent, MessageHeaders, MessageNamespaceDeps, MessageOptions, MessageSubscription, MuhkooMessageEvent, OAuthProvider, PacketCipher, PacketHeaders, PacketOptions, Participant, Permission, PermissionCheckRequest, PermissionCheckResult, PersonalSpaceClientOptions, PublicKeyJWK, RatchetState, ReceiveResult, RegisterParams, Role, SpaceDeps as RoomDeps, SpaceFileMetadata as RoomFileMetadata, SealedHeaders, Session, SessionOptions, SessionStore, SetOptions, ShardClientOptions, SharedSpaceClientOptions, SnarkProof, SpaceChannelLike, SpaceDeps, SpaceFileMetadata, SpaceKeyCache, SpaceKeyringDeps, SpaceMessageEvent, SpaceNamespaceDeps, StorageChangeEvent, StoredSession, Subscription, UserIdentity, VerificationKey, WSMessage, WSMessageType, WSTransportOptions, WrappedPayload, WriteFileInput, WriteFileOptions, WriteToShardsInput, ZkAuthDeps, ZkAuthenticateRequest, ZkChallengeResponse, ZkCompiled, ZkIdentity, ZkRegisterRequest, ZkRegisterResponse };
|