@muhkoo/connect 0.7.0-alpha.4 → 0.8.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 +1992 -54
- package/dist/browser/index.js.map +1 -1
- package/dist/connect.d.ts +898 -4
- package/dist/server/index.js +1992 -54
- package/dist/server/index.js.map +1 -1
- package/dist/workers/index.js +8 -0
- package/dist/workers/index.js.map +1 -1
- package/package.json +2 -1
package/dist/connect.d.ts
CHANGED
|
@@ -754,7 +754,13 @@ declare enum EventCoreEvents {
|
|
|
754
754
|
DATA_SENT = "data_sent",
|
|
755
755
|
MESSAGE = "message",
|
|
756
756
|
GET_HISTORY = "get_history",
|
|
757
|
-
RECEIVED_HISTORY = "received_history"
|
|
757
|
+
RECEIVED_HISTORY = "received_history",
|
|
758
|
+
ONLINE = "online",
|
|
759
|
+
OFFLINE = "offline",
|
|
760
|
+
SYNCING = "syncing",
|
|
761
|
+
SYNC_PROGRESS = "sync_progress",
|
|
762
|
+
SYNC_COMPLETE = "sync_complete",
|
|
763
|
+
SYNC_ERROR = "sync_error"
|
|
758
764
|
}
|
|
759
765
|
/**
|
|
760
766
|
* @public
|
|
@@ -1399,6 +1405,16 @@ declare function manifestToStat(manifest: FileManifest): FileStat;
|
|
|
1399
1405
|
* skip a PUT they'd otherwise dedup-rewrite. The
|
|
1400
1406
|
* server can ignore the optimization if it wants.
|
|
1401
1407
|
*/
|
|
1408
|
+
/**
|
|
1409
|
+
* A content-addressed byte cache for shards (the offline layer's `ShardCache`
|
|
1410
|
+
* satisfies this structurally). Declared here as a minimal interface so the
|
|
1411
|
+
* storage layer doesn't import the offline module — keeping the dependency
|
|
1412
|
+
* arrow one-way (offline → storage).
|
|
1413
|
+
*/
|
|
1414
|
+
interface ShardByteCache {
|
|
1415
|
+
get(hash: string): Promise<Uint8Array | null>;
|
|
1416
|
+
put(hash: string, bytes: Uint8Array): Promise<void>;
|
|
1417
|
+
}
|
|
1402
1418
|
interface ShardClientOptions {
|
|
1403
1419
|
/** Base URL of the accelerator (e.g. `https://muhkoo-accelerator.workers.dev`). */
|
|
1404
1420
|
baseUrl: string;
|
|
@@ -1418,6 +1434,18 @@ interface ShardClientOptions {
|
|
|
1418
1434
|
* Provided so callers can inject retries / auth / instrumentation.
|
|
1419
1435
|
*/
|
|
1420
1436
|
fetch?: typeof fetch;
|
|
1437
|
+
/**
|
|
1438
|
+
* Optional offline byte cache. When present, GETs are served cache-first and
|
|
1439
|
+
* populated on success, and PUTs write the cache before the network so a
|
|
1440
|
+
* just-uploaded file reads back offline. See {@link ShardByteCache}.
|
|
1441
|
+
*/
|
|
1442
|
+
cache?: ShardByteCache;
|
|
1443
|
+
/**
|
|
1444
|
+
* Called when a PUT can't reach the network (offline). Lets the caller queue
|
|
1445
|
+
* the upload for replay on reconnect; the bytes are already in {@link cache}
|
|
1446
|
+
* keyed by `hash`, so only the hash needs recording.
|
|
1447
|
+
*/
|
|
1448
|
+
deferUpload?: (hash: string) => Promise<void>;
|
|
1421
1449
|
}
|
|
1422
1450
|
/**
|
|
1423
1451
|
* Compute the lowercase hex SHA-256 of `bytes`. Available via WebCrypto in
|
|
@@ -1428,6 +1456,8 @@ declare class ShardClient {
|
|
|
1428
1456
|
private readonly baseUrl;
|
|
1429
1457
|
private readonly pathPrefix;
|
|
1430
1458
|
private readonly fetchFn;
|
|
1459
|
+
private readonly cache?;
|
|
1460
|
+
private readonly deferUpload?;
|
|
1431
1461
|
constructor(opts: ShardClientOptions);
|
|
1432
1462
|
/** Build the URL for a given shard hash — exposed for callers that want to log or pre-resolve. */
|
|
1433
1463
|
urlForHash(hash: string): string;
|
|
@@ -2695,6 +2725,8 @@ declare class HttpError extends Error {
|
|
|
2695
2725
|
}
|
|
2696
2726
|
declare class HttpClient {
|
|
2697
2727
|
readonly baseUrl: string;
|
|
2728
|
+
/** Origin of {@link baseUrl} — credentials are only attached same-origin. */
|
|
2729
|
+
private readonly baseOrigin;
|
|
2698
2730
|
private readonly apiKey;
|
|
2699
2731
|
private readonly getSessionToken;
|
|
2700
2732
|
private readonly onUnauthorized;
|
|
@@ -2706,6 +2738,8 @@ declare class HttpClient {
|
|
|
2706
2738
|
* up credentials without knowing about them.
|
|
2707
2739
|
*/
|
|
2708
2740
|
readonly fetch: typeof fetch;
|
|
2741
|
+
/** True when `input` resolves to {@link baseOrigin} (relative URLs always do). */
|
|
2742
|
+
private isSameOrigin;
|
|
2709
2743
|
get<T = unknown>(path: string): Promise<T>;
|
|
2710
2744
|
post<T = unknown>(path: string, body?: unknown): Promise<T>;
|
|
2711
2745
|
patch<T = unknown>(path: string, body?: unknown): Promise<T>;
|
|
@@ -2713,6 +2747,410 @@ declare class HttpClient {
|
|
|
2713
2747
|
private request;
|
|
2714
2748
|
}
|
|
2715
2749
|
|
|
2750
|
+
/**
|
|
2751
|
+
* `LWWRegister` — a last-write-wins register CRDT, the conflict model for a
|
|
2752
|
+
* `kv` value and for an app-state snapshot. A register holds a single value
|
|
2753
|
+
* stamped with the {@link HlcTimestamp} of the write that produced it. Two
|
|
2754
|
+
* concurrent writes converge to the one with the greater HLC; the loser is
|
|
2755
|
+
* discarded. A delete is just a write that sets `deleted` (a tombstone), so a
|
|
2756
|
+
* stale write that arrives after a newer delete can't resurrect the value.
|
|
2757
|
+
*
|
|
2758
|
+
* Because the HLC total-orders every write deterministically (down to a nodeId
|
|
2759
|
+
* tiebreak), every replica that has seen the same set of writes lands on the
|
|
2760
|
+
* same value — that's the convergence guarantee.
|
|
2761
|
+
*/
|
|
2762
|
+
interface LwwRegister<T> {
|
|
2763
|
+
/** The current value, or `null` when tombstoned / never written. */
|
|
2764
|
+
value: T | null;
|
|
2765
|
+
/** HLC of the write that produced this state. */
|
|
2766
|
+
hlc: string;
|
|
2767
|
+
/** True when the latest write was a delete. */
|
|
2768
|
+
deleted: boolean;
|
|
2769
|
+
}
|
|
2770
|
+
/** An empty register that loses to any real write. */
|
|
2771
|
+
declare function emptyRegister<T>(): LwwRegister<T>;
|
|
2772
|
+
/**
|
|
2773
|
+
* Merge two register states, returning the winner (greater HLC). Pure: neither
|
|
2774
|
+
* input is mutated. Used identically on the inbound change feed and on
|
|
2775
|
+
* sync-replay reconciliation so there's exactly one definition of "who wins".
|
|
2776
|
+
*/
|
|
2777
|
+
declare function mergeRegister<T>(a: LwwRegister<T>, b: LwwRegister<T>): LwwRegister<T>;
|
|
2778
|
+
/** Build the register produced by a `set`. */
|
|
2779
|
+
declare function writeRegister<T>(value: T, hlc: string): LwwRegister<T>;
|
|
2780
|
+
/** Build the register produced by a `delete` (a tombstone at `hlc`). */
|
|
2781
|
+
declare function deleteRegister<T>(hlc: string): LwwRegister<T>;
|
|
2782
|
+
|
|
2783
|
+
/**
|
|
2784
|
+
* `ConnectivityManager` — the single source of truth for "are we online", which
|
|
2785
|
+
* the offline layer needs to decide between a network call and a cache read, and
|
|
2786
|
+
* to know when to flush the durable outbound queue.
|
|
2787
|
+
*
|
|
2788
|
+
* `navigator.onLine` alone lies often (captive portals, VPN flaps, a laptop
|
|
2789
|
+
* that's "connected" to a network with no route), so we fuse three signals:
|
|
2790
|
+
* 1. the browser `online`/`offline` events (coarse but free),
|
|
2791
|
+
* 2. real fetch outcomes reported by {@link ../core/HttpClient} and the shard
|
|
2792
|
+
* client — a thrown `TypeError`/abort is the most reliable "offline" tell,
|
|
2793
|
+
* and a 2xx is the most reliable "online" tell,
|
|
2794
|
+
* 3. the sync engine, which flips us to `syncing` while it drains the queue.
|
|
2795
|
+
*
|
|
2796
|
+
* The offline→online edge fires `onReconnect` (wired to the sync engine) so
|
|
2797
|
+
* queued writes replay automatically. Transitions are debounced to avoid
|
|
2798
|
+
* flapping the UI on a brief blip. State changes also emit on {@link EventCore}
|
|
2799
|
+
* (`ONLINE`/`OFFLINE`/`SYNCING`) so apps can render a status indicator.
|
|
2800
|
+
*/
|
|
2801
|
+
type ConnectivityState = "online" | "offline" | "syncing";
|
|
2802
|
+
interface ConnectivityOptions {
|
|
2803
|
+
/** Called on each offline→online transition (drives queue replay). */
|
|
2804
|
+
onReconnect?: () => void;
|
|
2805
|
+
/** Debounce window for offline transitions, ms. Default 1500. */
|
|
2806
|
+
debounceMs?: number;
|
|
2807
|
+
}
|
|
2808
|
+
declare class ConnectivityManager {
|
|
2809
|
+
private state;
|
|
2810
|
+
private readonly listeners;
|
|
2811
|
+
private readonly onReconnect?;
|
|
2812
|
+
private readonly debounceMs;
|
|
2813
|
+
private offlineTimer;
|
|
2814
|
+
private started;
|
|
2815
|
+
private readonly handleOnline;
|
|
2816
|
+
private readonly handleOffline;
|
|
2817
|
+
constructor(opts?: ConnectivityOptions);
|
|
2818
|
+
/** Attach to the browser online/offline events. Safe to call in Node (no-op). */
|
|
2819
|
+
start(): void;
|
|
2820
|
+
stop(): void;
|
|
2821
|
+
get current(): ConnectivityState;
|
|
2822
|
+
get isOnline(): boolean;
|
|
2823
|
+
/** Subscribe to state changes. Returns an unsubscribe function. */
|
|
2824
|
+
onChange(cb: (state: ConnectivityState) => void): () => void;
|
|
2825
|
+
/** Report a failed network attempt (thrown fetch / abort). */
|
|
2826
|
+
reportFetchFailure(): void;
|
|
2827
|
+
/** Report a successful network round-trip — strongest "we're online" signal. */
|
|
2828
|
+
reportFetchSuccess(): void;
|
|
2829
|
+
/** The sync engine entering its drain phase. */
|
|
2830
|
+
markSyncing(): void;
|
|
2831
|
+
/** The sync engine finished — settle back to online. */
|
|
2832
|
+
markSynced(): void;
|
|
2833
|
+
private markOnline;
|
|
2834
|
+
private scheduleOffline;
|
|
2835
|
+
private set;
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2838
|
+
type StoreName = "kv-cache" | "db-row-cache" | "db-query-cache" | "space-messages" | "space-cursors" | "outbound-queue" | "snapshots" | "hlc-state" | "meta";
|
|
2839
|
+
|
|
2840
|
+
/**
|
|
2841
|
+
* `OfflineStore` — the pluggable persistence interface the offline layer is
|
|
2842
|
+
* built on, mirroring the {@link ../../core/Session.SessionStore} pattern. The
|
|
2843
|
+
* default browser implementation is {@link ./IndexedDbStore}; environments
|
|
2844
|
+
* without IndexedDB (Node, Workers, SSR) get {@link ./NoopStore}, whose every
|
|
2845
|
+
* method resolves to empty so the namespace hooks become branch-free no-ops.
|
|
2846
|
+
*
|
|
2847
|
+
* Keys are plain strings (most are prefixed with the user's `commitment` to
|
|
2848
|
+
* isolate users on a shared origin). Values are whatever the caller stores —
|
|
2849
|
+
* the store is format-agnostic; CRDT/ciphertext shape is the namespace's
|
|
2850
|
+
* concern.
|
|
2851
|
+
*/
|
|
2852
|
+
|
|
2853
|
+
/** A durable outbound mutation awaiting replay on reconnect. */
|
|
2854
|
+
interface QueueEntry {
|
|
2855
|
+
/** Auto-assigned monotonic sequence (insertion order). */
|
|
2856
|
+
seq: number;
|
|
2857
|
+
/** HLC stamp of the mutation — the order replays run in. */
|
|
2858
|
+
hlc: string;
|
|
2859
|
+
/** Client-generated id for idempotent replay / dedupe. */
|
|
2860
|
+
clientId: string;
|
|
2861
|
+
domain: "kv" | "db" | "file" | "space" | "snapshot";
|
|
2862
|
+
/** Namespace method to re-invoke (e.g. `"set"`, `"update"`, `"sendMessage"`). */
|
|
2863
|
+
method: string;
|
|
2864
|
+
/** Serializable arguments captured at enqueue time. */
|
|
2865
|
+
args: unknown;
|
|
2866
|
+
}
|
|
2867
|
+
interface OfflineEntry<T> {
|
|
2868
|
+
key: string;
|
|
2869
|
+
value: T;
|
|
2870
|
+
}
|
|
2871
|
+
interface OfflineStore {
|
|
2872
|
+
/** False for {@link ./NoopStore} so hooks can skip all work cheaply. */
|
|
2873
|
+
readonly enabled: boolean;
|
|
2874
|
+
/** Resolve once the backing store is open and ready (no-op when disabled). */
|
|
2875
|
+
ready(): Promise<void>;
|
|
2876
|
+
get<T>(store: StoreName, key: string): Promise<T | null>;
|
|
2877
|
+
put<T>(store: StoreName, key: string, value: T): Promise<void>;
|
|
2878
|
+
delete(store: StoreName, key: string): Promise<void>;
|
|
2879
|
+
/** Entries whose key starts with `prefix`, in ascending key order. */
|
|
2880
|
+
prefix<T>(store: StoreName, prefix: string): Promise<Array<OfflineEntry<T>>>;
|
|
2881
|
+
/** Entries whose key is in `[lower, upper)`, in ascending key order. */
|
|
2882
|
+
range<T>(store: StoreName, lower: string, upper: string): Promise<Array<OfflineEntry<T>>>;
|
|
2883
|
+
/** Every entry in a store, in ascending key order. */
|
|
2884
|
+
all<T>(store: StoreName): Promise<Array<OfflineEntry<T>>>;
|
|
2885
|
+
clear(store: StoreName): Promise<void>;
|
|
2886
|
+
/** Append a mutation to the durable outbound queue; resolves with its seq. */
|
|
2887
|
+
enqueue(entry: Omit<QueueEntry, "seq">): Promise<number>;
|
|
2888
|
+
/** Remove a drained queue entry by seq. */
|
|
2889
|
+
dequeue(seq: number): Promise<void>;
|
|
2890
|
+
/** All queued mutations, HLC-ordered (the order to replay them in). */
|
|
2891
|
+
queued(): Promise<QueueEntry[]>;
|
|
2892
|
+
}
|
|
2893
|
+
|
|
2894
|
+
/**
|
|
2895
|
+
* `OutboundQueue` — the durable record of writes made while offline (or that
|
|
2896
|
+
* failed mid-flight) plus the machinery to replay them on reconnect. This is
|
|
2897
|
+
* what makes offline writes *survive a reload*: unlike the in-memory buffer in
|
|
2898
|
+
* {@link ../transport/WSTransport}, every entry lives in IndexedDB until it has
|
|
2899
|
+
* been acknowledged by the server.
|
|
2900
|
+
*
|
|
2901
|
+
* Each domain registers a **replayer** — a function that re-applies one queued
|
|
2902
|
+
* mutation by re-invoking the real namespace method (with an `isReplay` flag so
|
|
2903
|
+
* it doesn't re-enqueue). `drain()` replays entries in HLC order:
|
|
2904
|
+
* - success → remove the entry;
|
|
2905
|
+
* - permanent failure (4xx) → drop it and surface a `SYNC_ERROR` (a write the
|
|
2906
|
+
* server will never accept must not wedge the queue forever);
|
|
2907
|
+
* - transient/network failure → stop draining and leave the rest for the next
|
|
2908
|
+
* reconnect.
|
|
2909
|
+
*
|
|
2910
|
+
* Replays are idempotent by construction (kv/db are LWW-by-key, messages dedupe
|
|
2911
|
+
* by `clientId`, file PUTs are content-addressed), so re-running a partially
|
|
2912
|
+
* drained queue is safe.
|
|
2913
|
+
*/
|
|
2914
|
+
|
|
2915
|
+
type Replayer = (entry: QueueEntry) => Promise<void>;
|
|
2916
|
+
declare class OutboundQueue {
|
|
2917
|
+
private readonly store;
|
|
2918
|
+
private readonly replayers;
|
|
2919
|
+
private draining;
|
|
2920
|
+
constructor(store: OfflineStore);
|
|
2921
|
+
/** A domain registers how to replay its mutations. */
|
|
2922
|
+
register(domain: QueueEntry["domain"], replayer: Replayer): void;
|
|
2923
|
+
/** Record a mutation for later replay; resolves with its assigned seq. */
|
|
2924
|
+
enqueue(entry: Omit<QueueEntry, "seq">): Promise<number>;
|
|
2925
|
+
/** Number of writes still awaiting sync. */
|
|
2926
|
+
pending(): Promise<number>;
|
|
2927
|
+
/**
|
|
2928
|
+
* Replay queued mutations in HLC order. Coalesces concurrent calls into a
|
|
2929
|
+
* single in-flight drain. Stops on the first transient failure, leaving the
|
|
2930
|
+
* remaining entries for the next attempt.
|
|
2931
|
+
*/
|
|
2932
|
+
drain(): Promise<void>;
|
|
2933
|
+
private runDrain;
|
|
2934
|
+
}
|
|
2935
|
+
|
|
2936
|
+
/**
|
|
2937
|
+
* `SyncEngine` — orchestrates catch-up when the client comes back online. It's
|
|
2938
|
+
* the thing wired to the {@link ConnectivityManager}'s offline→online edge.
|
|
2939
|
+
*
|
|
2940
|
+
* One `run()` does, in order:
|
|
2941
|
+
* 1. **Guard** — only proceed when the session is authenticated and unlocked
|
|
2942
|
+
* (replays may need to re-seal/re-encrypt, and there's no point pushing
|
|
2943
|
+
* writes for a logged-out user).
|
|
2944
|
+
* 2. **Drain** the durable {@link OutboundQueue} — push every offline write.
|
|
2945
|
+
* 3. **Catch-up tasks** — domain-registered pulls that reconcile *inbound*
|
|
2946
|
+
* state we missed while offline (e.g. a Space paging history forward to its
|
|
2947
|
+
* last-seen handle). Registered by the namespaces.
|
|
2948
|
+
*
|
|
2949
|
+
* Concurrent triggers coalesce into a single in-flight run (a reconnect often
|
|
2950
|
+
* fires several signals at once), mirroring `Client.recoverInFlight`.
|
|
2951
|
+
*/
|
|
2952
|
+
|
|
2953
|
+
interface SyncEngineDeps {
|
|
2954
|
+
queue: OutboundQueue;
|
|
2955
|
+
connectivity: ConnectivityManager;
|
|
2956
|
+
/** Proceed only when this returns true (authenticated + unlocked). */
|
|
2957
|
+
canSync: () => boolean;
|
|
2958
|
+
}
|
|
2959
|
+
declare class SyncEngine {
|
|
2960
|
+
private readonly deps;
|
|
2961
|
+
private readonly catchUpTasks;
|
|
2962
|
+
private running;
|
|
2963
|
+
constructor(deps: SyncEngineDeps);
|
|
2964
|
+
/** Register an inbound reconciliation task run after the queue drains. */
|
|
2965
|
+
registerCatchUp(task: () => Promise<void>): () => void;
|
|
2966
|
+
/** Drain + catch up. Concurrent calls share one in-flight run. */
|
|
2967
|
+
run(): Promise<void>;
|
|
2968
|
+
private execute;
|
|
2969
|
+
}
|
|
2970
|
+
|
|
2971
|
+
/**
|
|
2972
|
+
* `client.offline.snapshot` — a small, encrypted, app-state cache. Apps stash
|
|
2973
|
+
* whatever they need to repaint instantly on a cold offline boot: the last open
|
|
2974
|
+
* Space, a draft message, scroll position, the current route. It's deliberately
|
|
2975
|
+
* separate from `client.kv`: snapshots are **local-only by default** (instant,
|
|
2976
|
+
* never metered, never leave the device) and hold view state rather than domain
|
|
2977
|
+
* data.
|
|
2978
|
+
*
|
|
2979
|
+
* await client.offline.snapshot.save('ui', { route, openSpaceId, draft })
|
|
2980
|
+
* const ui = await client.offline.snapshot.load<UiState>('ui')
|
|
2981
|
+
*
|
|
2982
|
+
* Values are encrypted at rest with the same identity-derived {@link StorageCipher}
|
|
2983
|
+
* as `client.kv`, so a snapshot is unreadable until the user unlocks — `load`
|
|
2984
|
+
* returns `null` when locked or absent, and `save` throws {@link OfflineLockedError}.
|
|
2985
|
+
* Conflict model is a single-writer LWW register (the latest save wins).
|
|
2986
|
+
*/
|
|
2987
|
+
|
|
2988
|
+
interface SnapshotNamespaceDeps {
|
|
2989
|
+
store: OfflineStore;
|
|
2990
|
+
session: SessionState;
|
|
2991
|
+
/** Stamp the saved record (LWW ordering); supplied by the OfflineManager. */
|
|
2992
|
+
nextHlc: () => Promise<string>;
|
|
2993
|
+
}
|
|
2994
|
+
declare class SnapshotNamespace {
|
|
2995
|
+
private readonly deps;
|
|
2996
|
+
private cipher;
|
|
2997
|
+
private cipherIdentity;
|
|
2998
|
+
constructor(deps: SnapshotNamespaceDeps);
|
|
2999
|
+
/** Persist `state` under `name` (encrypted). No-op when offline support is off. */
|
|
3000
|
+
save<T = unknown>(name: string, state: T): Promise<void>;
|
|
3001
|
+
/** Load and decrypt the snapshot `name`, or `null` if absent/locked. */
|
|
3002
|
+
load<T = unknown>(name: string): Promise<T | null>;
|
|
3003
|
+
/** Remove the snapshot `name`. */
|
|
3004
|
+
delete(name: string): Promise<void>;
|
|
3005
|
+
/** Names of all snapshots for the current user. */
|
|
3006
|
+
list(): Promise<string[]>;
|
|
3007
|
+
private key;
|
|
3008
|
+
private commitment;
|
|
3009
|
+
private requireCipher;
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
/**
|
|
3013
|
+
* `ShardCache` — a Cache-API-backed store for file-shard bytes. Shards are
|
|
3014
|
+
* content-addressed (the key IS the SHA-256 of the ciphertext), so caching them
|
|
3015
|
+
* is uniquely safe: a cache hit is self-validating, immutable, and shareable
|
|
3016
|
+
* across files. The Cache API (rather than IndexedDB) is the right home for
|
|
3017
|
+
* large binary blobs — it streams, it's purpose-built for response bodies, and
|
|
3018
|
+
* it has its own eviction.
|
|
3019
|
+
*
|
|
3020
|
+
* Keys are SYNTHETIC absolute URLs (`https://shard.muhkoo.local/<hash>`) rather
|
|
3021
|
+
* than the real shard endpoint, so the cache is addressable by hash alone — the
|
|
3022
|
+
* sync engine can re-PUT a deferred shard knowing only its hash, without
|
|
3023
|
+
* reconstructing the accelerator URL. We never fetch these synthetic URLs; they
|
|
3024
|
+
* exist only as cache keys.
|
|
3025
|
+
*
|
|
3026
|
+
* Every method degrades to a no-op / null where the Cache API is unavailable
|
|
3027
|
+
* (Node, Workers, insecure contexts), so callers don't need to branch.
|
|
3028
|
+
*/
|
|
3029
|
+
declare class ShardCache {
|
|
3030
|
+
static available(): boolean;
|
|
3031
|
+
private key;
|
|
3032
|
+
private open;
|
|
3033
|
+
/** Cached shard bytes, or `null` on miss / when unavailable. */
|
|
3034
|
+
get(hash: string): Promise<Uint8Array | null>;
|
|
3035
|
+
/** Store shard bytes under their hash. Best-effort. */
|
|
3036
|
+
put(hash: string, bytes: Uint8Array): Promise<void>;
|
|
3037
|
+
/** Whether a shard is present in the cache. */
|
|
3038
|
+
has(hash: string): Promise<boolean>;
|
|
3039
|
+
}
|
|
3040
|
+
|
|
3041
|
+
/**
|
|
3042
|
+
* `OfflineManager` — the object `client.offline` points at, and the single
|
|
3043
|
+
* owner of everything the offline layer needs at runtime: the {@link
|
|
3044
|
+
* OfflineStore}, the {@link HLC} (Hybrid Logical Clock), the {@link
|
|
3045
|
+
* ConnectivityManager}, the durable {@link OutboundQueue}, the {@link
|
|
3046
|
+
* SyncEngine}, and the {@link SnapshotNamespace}.
|
|
3047
|
+
*
|
|
3048
|
+
* The {@link ../core/Client} constructs one of these (always — it's a cheap
|
|
3049
|
+
* no-op when offline support is off) and hands the namespaces the small set of
|
|
3050
|
+
* primitives they need to participate: `nextHlc()` to stamp a write,
|
|
3051
|
+
* `observeHlc()` to fold in a remote stamp, `enqueue()` to record an offline
|
|
3052
|
+
* mutation, and `registerReplayer()`/`registerCatchUp()` to define how their
|
|
3053
|
+
* domain re-syncs on reconnect. Keeping that wiring here means the namespaces
|
|
3054
|
+
* stay decoupled from IndexedDB and the clock.
|
|
3055
|
+
*/
|
|
3056
|
+
|
|
3057
|
+
interface OfflineManagerDeps {
|
|
3058
|
+
store: OfflineStore;
|
|
3059
|
+
session: SessionState;
|
|
3060
|
+
/** Whether offline support is active (browser) vs a no-op (Node/Workers). */
|
|
3061
|
+
enabled: boolean;
|
|
3062
|
+
}
|
|
3063
|
+
declare class OfflineManager {
|
|
3064
|
+
/** True in browsers with IndexedDB; false when running over a NoopStore. */
|
|
3065
|
+
readonly enabled: boolean;
|
|
3066
|
+
readonly store: OfflineStore;
|
|
3067
|
+
readonly connectivity: ConnectivityManager;
|
|
3068
|
+
readonly queue: OutboundQueue;
|
|
3069
|
+
readonly sync: SyncEngine;
|
|
3070
|
+
/** `client.offline.snapshot` — encrypted app-state cache. */
|
|
3071
|
+
readonly snapshot: SnapshotNamespace;
|
|
3072
|
+
/** Cache-API store for file-shard bytes (browser only). */
|
|
3073
|
+
readonly fileCache?: ShardCache;
|
|
3074
|
+
private hlc;
|
|
3075
|
+
private nodeId;
|
|
3076
|
+
private readonly bootPromise;
|
|
3077
|
+
private persistTimer;
|
|
3078
|
+
constructor(deps: OfflineManagerDeps);
|
|
3079
|
+
/**
|
|
3080
|
+
* Queue a shard upload that couldn't reach the network. The bytes are
|
|
3081
|
+
* already cached (keyed by `hash`), so only the hash is recorded; the file
|
|
3082
|
+
* replayer re-PUTs from the cache on reconnect.
|
|
3083
|
+
*/
|
|
3084
|
+
deferShardUpload(hash: string): Promise<void>;
|
|
3085
|
+
/** Resolve once the clock + node id are loaded. */
|
|
3086
|
+
ready(): Promise<void>;
|
|
3087
|
+
/** Current connectivity state (`online` | `offline` | `syncing`). */
|
|
3088
|
+
get status(): ConnectivityState;
|
|
3089
|
+
/** Subscribe to connectivity changes. Returns an unsubscribe function. */
|
|
3090
|
+
onStatusChange(cb: (state: ConnectivityState) => void): () => void;
|
|
3091
|
+
/** Stamp a locally-generated write with a fresh, monotonic HLC. */
|
|
3092
|
+
nextHlc(): Promise<string>;
|
|
3093
|
+
/** Fold an observed remote HLC into the clock (call for inbound stamps). */
|
|
3094
|
+
observeHlc(remote: string): Promise<void>;
|
|
3095
|
+
/** A fresh client-generated id (idempotent replay / message dedupe). */
|
|
3096
|
+
newClientId(): string;
|
|
3097
|
+
/** Record an offline mutation for replay on reconnect. */
|
|
3098
|
+
enqueue(entry: Omit<QueueEntry, "seq">): Promise<number>;
|
|
3099
|
+
/** A domain registers how to replay its queued mutations. */
|
|
3100
|
+
registerReplayer(domain: QueueEntry["domain"], replayer: Replayer): void;
|
|
3101
|
+
/** A domain registers an inbound catch-up task (run after the queue drains). */
|
|
3102
|
+
registerCatchUp(task: () => Promise<void>): () => void;
|
|
3103
|
+
/** Surface a network failure to connectivity tracking. */
|
|
3104
|
+
reportFetchFailure(): void;
|
|
3105
|
+
/** Surface a successful round-trip to connectivity tracking. */
|
|
3106
|
+
reportFetchSuccess(): void;
|
|
3107
|
+
/** Tear down listeners (tests / hot-reload). */
|
|
3108
|
+
dispose(): void;
|
|
3109
|
+
private boot;
|
|
3110
|
+
/** Coalesce HLC-state writes — a burst of stamps persists once. */
|
|
3111
|
+
private schedulePersist;
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
/**
|
|
3115
|
+
* `KvCache` — the offline adapter for `client.kv`. Stores each value's
|
|
3116
|
+
* ciphertext envelope (never plaintext) under `${commitment}|${collection}/${id}`
|
|
3117
|
+
* as an LWW-Register stamped with an HLC, so reads work offline and concurrent
|
|
3118
|
+
* writes converge (see {@link ./crdt/LWWRegister}). Deletes are causal
|
|
3119
|
+
* tombstones — a stale write can't resurrect a key a newer delete removed.
|
|
3120
|
+
*
|
|
3121
|
+
* True cross-device convergence also needs the accelerator to honor the HLC
|
|
3122
|
+
* (see the server's `kvmeta` sidecar); this adapter embeds the stamp now and
|
|
3123
|
+
* degrades gracefully (last-writer-by-arrival) until that ships.
|
|
3124
|
+
*/
|
|
3125
|
+
|
|
3126
|
+
type KvEntry = LwwRegister<unknown>;
|
|
3127
|
+
declare class KvCache {
|
|
3128
|
+
private readonly manager;
|
|
3129
|
+
private readonly store;
|
|
3130
|
+
constructor(manager: OfflineManager);
|
|
3131
|
+
get enabled(): boolean;
|
|
3132
|
+
nextHlc(): Promise<string>;
|
|
3133
|
+
newClientId(): string;
|
|
3134
|
+
private key;
|
|
3135
|
+
/** Read the cached register for `collection/id`, or null when absent. */
|
|
3136
|
+
read(commitment: string, fullKey: string): Promise<KvEntry | null>;
|
|
3137
|
+
/** Optimistically record a local `set` (ciphertext envelope + HLC). */
|
|
3138
|
+
writeLocal(commitment: string, fullKey: string, envelope: unknown, hlc: string): Promise<void>;
|
|
3139
|
+
/** Optimistically record a local `delete` (tombstone at HLC). */
|
|
3140
|
+
deleteLocal(commitment: string, fullKey: string, hlc: string): Promise<void>;
|
|
3141
|
+
/**
|
|
3142
|
+
* Merge an authoritative value (from a GET refresh or a realtime frame) into
|
|
3143
|
+
* the cache by HLC. When the server doesn't supply an HLC yet, treat it as
|
|
3144
|
+
* "now-ish" by stamping our clock so a server value still beats stale local
|
|
3145
|
+
* state but a fresh local write wins.
|
|
3146
|
+
*/
|
|
3147
|
+
mergeRemote(commitment: string, fullKey: string, envelope: unknown | null, op: "set" | "delete", hlc?: string): Promise<void>;
|
|
3148
|
+
/** Non-tombstoned full keys for a commitment (used for offline `list`). */
|
|
3149
|
+
liveKeys(commitment: string): Promise<string[]>;
|
|
3150
|
+
/** Queue a kv mutation for replay on reconnect. */
|
|
3151
|
+
enqueue(method: "set" | "delete", args: unknown, hlc: string, clientId: string): Promise<void>;
|
|
3152
|
+
}
|
|
3153
|
+
|
|
2716
3154
|
/**
|
|
2717
3155
|
* `client.kv` — per-user key/value storage backed by the accelerator's personal
|
|
2718
3156
|
* space.
|
|
@@ -2742,6 +3180,8 @@ interface KvNamespaceDeps {
|
|
|
2742
3180
|
session: SessionState;
|
|
2743
3181
|
/** WebSocket base (ws/wss) for the change feed; derived from baseUrl. */
|
|
2744
3182
|
wsBaseUrl: string;
|
|
3183
|
+
/** Offline cache + queue. Undefined ⇒ no offline behavior (network-only). */
|
|
3184
|
+
offline?: KvCache;
|
|
2745
3185
|
}
|
|
2746
3186
|
interface SetOptions {
|
|
2747
3187
|
/** Encrypt the value at rest (default: true). */
|
|
@@ -2759,6 +3199,8 @@ declare class KvNamespace {
|
|
|
2759
3199
|
private cipher;
|
|
2760
3200
|
private cipherIdentity;
|
|
2761
3201
|
constructor(deps: KvNamespaceDeps);
|
|
3202
|
+
/** Replay one queued kv mutation (called by the sync engine). */
|
|
3203
|
+
replay(entry: QueueEntry): Promise<void>;
|
|
2762
3204
|
/** Store `value` under `collection`/`id` (encrypted at rest by default). */
|
|
2763
3205
|
set<T = unknown>(collection: string, id: string, value: T, opts?: SetOptions): Promise<void>;
|
|
2764
3206
|
/** Fetch the value at `collection`/`id`, or `null` if absent. */
|
|
@@ -2787,6 +3229,45 @@ declare class KvNamespace {
|
|
|
2787
3229
|
private getCipher;
|
|
2788
3230
|
}
|
|
2789
3231
|
|
|
3232
|
+
/**
|
|
3233
|
+
* `DbCache` — the offline adapter for `client.db`. Unlike kv, db rows are
|
|
3234
|
+
* server-plaintext, so they're cached as-is. The SDK doesn't know a table's
|
|
3235
|
+
* primary-key column, so it caches schema-agnostically:
|
|
3236
|
+
* - `get(id)` results keyed by the requested id (`${table}|row|${id}`),
|
|
3237
|
+
* - `query(q)` result pages keyed by a stable hash of the query.
|
|
3238
|
+
*
|
|
3239
|
+
* Writes carry per-column HLC stamps (`_hlc`) so the accelerator's per-column
|
|
3240
|
+
* LWW merge can converge concurrent edits; offline writes are queued and
|
|
3241
|
+
* replayed. Optimistic row patches keep `get` fresh between sync cycles.
|
|
3242
|
+
*/
|
|
3243
|
+
|
|
3244
|
+
interface DbQueryPage {
|
|
3245
|
+
rows: Array<Record<string, unknown>>;
|
|
3246
|
+
nextCursor: string | null;
|
|
3247
|
+
}
|
|
3248
|
+
/** Stable JSON for query-cache keys (sorted object keys → deterministic). */
|
|
3249
|
+
declare function stableStringify(value: unknown): string;
|
|
3250
|
+
declare class DbCache {
|
|
3251
|
+
private readonly manager;
|
|
3252
|
+
private readonly store;
|
|
3253
|
+
constructor(manager: OfflineManager);
|
|
3254
|
+
get enabled(): boolean;
|
|
3255
|
+
nextHlc(): Promise<string>;
|
|
3256
|
+
newClientId(): string;
|
|
3257
|
+
/** Build the `{ [col]: hlc }` map for a write (one stamp across its columns). */
|
|
3258
|
+
columnStamps(values: Record<string, unknown>, hlc: string): Record<string, string>;
|
|
3259
|
+
private rowKey;
|
|
3260
|
+
private queryKey;
|
|
3261
|
+
readRow(table: string, id: string): Promise<Record<string, unknown> | null>;
|
|
3262
|
+
writeRow(table: string, id: string, row: Record<string, unknown>): Promise<void>;
|
|
3263
|
+
/** Shallow-merge a partial update into a cached row (optimistic). */
|
|
3264
|
+
patchRow(table: string, id: string, values: Record<string, unknown>): Promise<void>;
|
|
3265
|
+
deleteRow(table: string, id: string): Promise<void>;
|
|
3266
|
+
readQuery(table: string, q: unknown): Promise<DbQueryPage | null>;
|
|
3267
|
+
writeQuery(table: string, q: unknown, page: DbQueryPage): Promise<void>;
|
|
3268
|
+
enqueue(method: "insert" | "update" | "delete", args: unknown, hlc: string, clientId: string): Promise<void>;
|
|
3269
|
+
}
|
|
3270
|
+
|
|
2790
3271
|
/**
|
|
2791
3272
|
* `client.db` — the app's scalable database. A developer defines tables in the
|
|
2792
3273
|
* Muhkoo portal (columns + indexes); each table is backed by its own Durable
|
|
@@ -2818,6 +3299,8 @@ declare class KvNamespace {
|
|
|
2818
3299
|
|
|
2819
3300
|
interface DbNamespaceDeps {
|
|
2820
3301
|
http: HttpClient;
|
|
3302
|
+
/** Offline row/query cache + write queue. Undefined ⇒ network-only. */
|
|
3303
|
+
offline?: DbCache;
|
|
2821
3304
|
}
|
|
2822
3305
|
/** Comparison operators allowed in a {@link DbQuery} `where` clause. */
|
|
2823
3306
|
type DbFilterOp = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "like" | "likeStartsWith" | "likeContains";
|
|
@@ -2851,8 +3334,10 @@ interface DbQueryResult<T = Record<string, unknown>> {
|
|
|
2851
3334
|
declare class DbTable<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
2852
3335
|
private readonly http;
|
|
2853
3336
|
private readonly name;
|
|
2854
|
-
|
|
3337
|
+
private readonly offline?;
|
|
3338
|
+
constructor(http: HttpClient, name: string, offline?: DbCache | undefined);
|
|
2855
3339
|
private base;
|
|
3340
|
+
private rowUrl;
|
|
2856
3341
|
/** Insert a row. Returns the created row and its primary-key value. */
|
|
2857
3342
|
insert(values: Partial<T>): Promise<{
|
|
2858
3343
|
row: T;
|
|
@@ -2880,6 +3365,8 @@ declare class DbNamespace {
|
|
|
2880
3365
|
* const todos = client.db.table<Todo>('todos');
|
|
2881
3366
|
*/
|
|
2882
3367
|
table<T extends Record<string, unknown> = Record<string, unknown>>(name: string): DbTable<T>;
|
|
3368
|
+
/** Replay one queued db mutation (called by the sync engine). */
|
|
3369
|
+
replay(entry: QueueEntry): Promise<void>;
|
|
2883
3370
|
}
|
|
2884
3371
|
|
|
2885
3372
|
/**
|
|
@@ -2919,6 +3406,10 @@ interface FileNamespaceDeps {
|
|
|
2919
3406
|
* deploy-time-precompiled environments). Forwarded to {@link FileStorage}.
|
|
2920
3407
|
*/
|
|
2921
3408
|
rsWasmModule?: WebAssembly.Module;
|
|
3409
|
+
/** Offline shard-byte cache (browser). Enables offline reads of cached files. */
|
|
3410
|
+
shardCache?: ShardByteCache;
|
|
3411
|
+
/** Queue a shard PUT that couldn't reach the network (offline). */
|
|
3412
|
+
deferShardUpload?: (hash: string) => Promise<void>;
|
|
2922
3413
|
}
|
|
2923
3414
|
interface WriteFileOptions {
|
|
2924
3415
|
/** SharedSpace the file is written to (obtain via `client.space`). */
|
|
@@ -3304,6 +3795,46 @@ interface SpaceMessageEvent {
|
|
|
3304
3795
|
*/
|
|
3305
3796
|
handle: number;
|
|
3306
3797
|
message: Message;
|
|
3798
|
+
/**
|
|
3799
|
+
* Sender-generated id, present on locally-originated messages (carried in the
|
|
3800
|
+
* cleartext `cid` header). Lets the UI dedupe an optimistic send against its
|
|
3801
|
+
* server echo, and the offline cache replace a pending entry with the real one.
|
|
3802
|
+
*/
|
|
3803
|
+
clientId?: string;
|
|
3804
|
+
/** True for an optimistic local send not yet acknowledged by the server. */
|
|
3805
|
+
pending?: boolean;
|
|
3806
|
+
}
|
|
3807
|
+
/**
|
|
3808
|
+
* One sealed message frame as cached for offline use. The `packet` is the
|
|
3809
|
+
* ciphertext-bearing serialized {@link Packet} (decrypted lazily on read), so
|
|
3810
|
+
* nothing readable is persisted. Structurally matches the offline layer's
|
|
3811
|
+
* `MessageEntry`; declared here so the spaces module doesn't import `../offline`.
|
|
3812
|
+
*/
|
|
3813
|
+
interface CachedSpaceMessage {
|
|
3814
|
+
handle: string;
|
|
3815
|
+
packet: string | null;
|
|
3816
|
+
op: "msg" | "edit" | "delete";
|
|
3817
|
+
clientId?: string;
|
|
3818
|
+
hlc?: string;
|
|
3819
|
+
pending?: boolean;
|
|
3820
|
+
deleted?: boolean;
|
|
3821
|
+
}
|
|
3822
|
+
/** The offline adapter a {@link Space} drives. Implemented by `SpaceCache`. */
|
|
3823
|
+
interface SpaceOfflineAdapter {
|
|
3824
|
+
readonly enabled: boolean;
|
|
3825
|
+
newClientId(): string;
|
|
3826
|
+
nextHlc(): Promise<string>;
|
|
3827
|
+
putMessage(spaceId: string, entry: CachedSpaceMessage): Promise<void>;
|
|
3828
|
+
putDeleted(spaceId: string, handle: number): Promise<void>;
|
|
3829
|
+
dropPending(spaceId: string, clientId: string): Promise<void>;
|
|
3830
|
+
loadMessages(spaceId: string): Promise<CachedSpaceMessage[]>;
|
|
3831
|
+
getCursor(spaceId: string): Promise<{
|
|
3832
|
+
lastSeenHandle: number;
|
|
3833
|
+
oldestHandle: number;
|
|
3834
|
+
} | null>;
|
|
3835
|
+
observeHandle(spaceId: string, handle: number): Promise<void>;
|
|
3836
|
+
enqueueFrame(spaceId: string, frame: unknown, clientId: string, hlc: string): Promise<void>;
|
|
3837
|
+
registerCatchUp(task: () => Promise<void>): () => void;
|
|
3307
3838
|
}
|
|
3308
3839
|
/**
|
|
3309
3840
|
* An inbound ephemeral signal — an application broadcast the server relays to
|
|
@@ -3353,6 +3884,12 @@ interface SpaceDeps {
|
|
|
3353
3884
|
* without a live websocket.
|
|
3354
3885
|
*/
|
|
3355
3886
|
createChannel?: (url: string, myId: string) => SpaceChannelLike;
|
|
3887
|
+
/** Offline shard-byte cache for room files (browser). */
|
|
3888
|
+
shardCache?: ShardByteCache;
|
|
3889
|
+
/** Queue a room-file shard PUT that couldn't reach the network. */
|
|
3890
|
+
deferShardUpload?: (hash: string) => Promise<void>;
|
|
3891
|
+
/** Offline message cache + send queue. Undefined ⇒ no offline behavior. */
|
|
3892
|
+
offline?: SpaceOfflineAdapter;
|
|
3356
3893
|
}
|
|
3357
3894
|
/** The subset of {@link BroadcastChannel} a {@link Space} drives. */
|
|
3358
3895
|
interface SpaceChannelLike {
|
|
@@ -3376,6 +3913,7 @@ declare class Space {
|
|
|
3376
3913
|
/** Per-instance event bus for fan-out events (isolated across spaces). */
|
|
3377
3914
|
private readonly events;
|
|
3378
3915
|
private rawWired;
|
|
3916
|
+
private catchUpUnsub;
|
|
3379
3917
|
constructor(deps: SpaceDeps);
|
|
3380
3918
|
/** The encoded space id (same as the transport name for fan-out spaces). */
|
|
3381
3919
|
get id(): string;
|
|
@@ -3425,6 +3963,14 @@ declare class Space {
|
|
|
3425
3963
|
* original author. Receivers get an {@link onMessageDeleted} event.
|
|
3426
3964
|
*/
|
|
3427
3965
|
deleteMessage(handle: number): Promise<void>;
|
|
3966
|
+
/**
|
|
3967
|
+
* Decrypt and return the locally-cached message log for this space, in
|
|
3968
|
+
* handle order (pending sends last). Works offline — the source is the
|
|
3969
|
+
* IndexedDB cache populated by live frames + {@link history}. Apps call this
|
|
3970
|
+
* on boot to paint instantly, then subscribe to {@link onMessage} for live
|
|
3971
|
+
* updates. Returns `[]` when offline support is off.
|
|
3972
|
+
*/
|
|
3973
|
+
cachedMessages(): Promise<SpaceMessageEvent[]>;
|
|
3428
3974
|
/** Subscribe to in-place edits of persisted messages (new content at the same handle). */
|
|
3429
3975
|
onMessageEdited(handler: (e: SpaceMessageEvent) => void): () => void;
|
|
3430
3976
|
/** Subscribe to deletions of persisted messages. */
|
|
@@ -3506,6 +4052,15 @@ declare class Space {
|
|
|
3506
4052
|
* nothing to verify against → reject.
|
|
3507
4053
|
*/
|
|
3508
4054
|
private verifySender;
|
|
4055
|
+
/** Persist a decoded live/history/edit frame to the offline cache. */
|
|
4056
|
+
private cacheFrame;
|
|
4057
|
+
/**
|
|
4058
|
+
* Inbound reconciliation run on reconnect: page the server's forward delta
|
|
4059
|
+
* (`?since=<lastSeenHandle>`) to the head, pulling exactly the messages that
|
|
4060
|
+
* landed while we were offline into the cache. Bounded so a long absence
|
|
4061
|
+
* can't page forever. Falls back gracefully if the endpoint is unavailable.
|
|
4062
|
+
*/
|
|
4063
|
+
private catchUp;
|
|
3509
4064
|
private emitEvent;
|
|
3510
4065
|
private requireKeyring;
|
|
3511
4066
|
private requireCipher;
|
|
@@ -3638,9 +4193,14 @@ interface SpaceNamespaceDeps {
|
|
|
3638
4193
|
wsBaseUrl: string;
|
|
3639
4194
|
/** Optional PersonalSpace-backed cache of group keys (encrypted at rest). */
|
|
3640
4195
|
cache?: SpaceKeyCache;
|
|
4196
|
+
/** Offline manager — enables message caching + offline send replay. */
|
|
4197
|
+
offline?: OfflineManager;
|
|
3641
4198
|
}
|
|
3642
4199
|
declare class SpaceNamespace {
|
|
3643
4200
|
private readonly deps;
|
|
4201
|
+
private spaceCache?;
|
|
4202
|
+
/** Spaces built this session, by id — used to route offline send replays. */
|
|
4203
|
+
private readonly openSpaces;
|
|
3644
4204
|
constructor(deps: SpaceNamespaceDeps);
|
|
3645
4205
|
/**
|
|
3646
4206
|
* Create a new space: generate its keypair (public key = id), register
|
|
@@ -4085,6 +4645,22 @@ interface ClientOptions {
|
|
|
4085
4645
|
* staging (`auth.staging.muhkoo.dev`) or a self-hosted deployment.
|
|
4086
4646
|
*/
|
|
4087
4647
|
authBaseUrl?: string;
|
|
4648
|
+
/**
|
|
4649
|
+
* Offline support (`client.offline`) — transparent local caching + a durable
|
|
4650
|
+
* write queue + CRDT sync. **On by default in browsers** (IndexedDB + Cache
|
|
4651
|
+
* API present), a no-op everywhere else. Override to force it on/off or to
|
|
4652
|
+
* supply a custom {@link OfflineStore}.
|
|
4653
|
+
*/
|
|
4654
|
+
offline?: {
|
|
4655
|
+
/** Force enable/disable. Default: auto-detect (browser on, Node off). */
|
|
4656
|
+
enabled?: boolean;
|
|
4657
|
+
/** Pluggable persistence. Default: IndexedDB in the browser. */
|
|
4658
|
+
store?: OfflineStore;
|
|
4659
|
+
/** Cache file-shard bytes in the Cache API. Default: true in the browser. */
|
|
4660
|
+
cacheShards?: boolean;
|
|
4661
|
+
/** Soft cap on durable-queue bytes before degrading to online-only. */
|
|
4662
|
+
maxQueueBytes?: number;
|
|
4663
|
+
};
|
|
4088
4664
|
}
|
|
4089
4665
|
declare class Client {
|
|
4090
4666
|
readonly baseUrl: string;
|
|
@@ -4104,6 +4680,8 @@ declare class Client {
|
|
|
4104
4680
|
readonly agents: AgentsNamespace;
|
|
4105
4681
|
/** Serverless functions — `client.functions.deploy(appId, …)`, etc. */
|
|
4106
4682
|
readonly functions: FunctionsNamespace;
|
|
4683
|
+
/** Offline cache + sync — `client.offline.status`, `client.offline.snapshot`, etc. */
|
|
4684
|
+
readonly offline: OfflineManager;
|
|
4107
4685
|
private readonly session;
|
|
4108
4686
|
private readonly http;
|
|
4109
4687
|
/** Subscribers notified when a session expires and can't be recovered. */
|
|
@@ -4264,6 +4842,322 @@ declare function ejectAgentTools(appClass: Function, opts?: {
|
|
|
4264
4842
|
maxIterations?: number;
|
|
4265
4843
|
}): AgentToolsConfig;
|
|
4266
4844
|
|
|
4845
|
+
/**
|
|
4846
|
+
* Message-log helpers — the conflict model for a Space's message history.
|
|
4847
|
+
*
|
|
4848
|
+
* Messages are the easy CRDT: the accelerator assigns every persisted message a
|
|
4849
|
+
* monotonic, zero-padded `handle` that already totally-orders the log, and the
|
|
4850
|
+
* sealed body is immutable. So the "log" is just a grow-only set keyed by
|
|
4851
|
+
* `handle`, and convergence is structural — no server change needed.
|
|
4852
|
+
*
|
|
4853
|
+
* The only subtleties this module encodes:
|
|
4854
|
+
* - **Optimistic sends** have no server handle yet. We key them by a
|
|
4855
|
+
* provisional handle that sorts *after* every real one, and tag them with
|
|
4856
|
+
* the sender's `clientId` so the real frame (carrying the same `clientId`)
|
|
4857
|
+
* can replace the placeholder instead of duplicating it.
|
|
4858
|
+
* - **Edits** are a per-message LWW register on the body (greater HLC wins).
|
|
4859
|
+
* - **Deletes** are a tombstone at the message's handle.
|
|
4860
|
+
*/
|
|
4861
|
+
type MessageOp = "msg" | "edit" | "delete";
|
|
4862
|
+
interface MessageEntry {
|
|
4863
|
+
/** Server-assigned handle, or a {@link provisionalHandle} while pending. */
|
|
4864
|
+
handle: string;
|
|
4865
|
+
/** Sealed packet string (ciphertext at rest); `null` for a delete tombstone. */
|
|
4866
|
+
packet: string | null;
|
|
4867
|
+
op: MessageOp;
|
|
4868
|
+
/** Sender-generated id, present on locally-originated sends for dedupe. */
|
|
4869
|
+
clientId?: string;
|
|
4870
|
+
/** HLC stamp — orders edits to the same message. */
|
|
4871
|
+
hlc?: string;
|
|
4872
|
+
/** True while an optimistic send has not yet been acked by the server. */
|
|
4873
|
+
pending?: boolean;
|
|
4874
|
+
deleted?: boolean;
|
|
4875
|
+
}
|
|
4876
|
+
/** IndexedDB key for a message within a space. */
|
|
4877
|
+
declare function messageKey(spaceId: string, handle: string): string;
|
|
4878
|
+
/**
|
|
4879
|
+
* A handle for an un-acked optimistic message. Prefixed with `"~"` (0x7E),
|
|
4880
|
+
* which sorts after every digit, so pending sends always appear at the tail of
|
|
4881
|
+
* the handle-ordered log until the real handle arrives.
|
|
4882
|
+
*/
|
|
4883
|
+
declare function provisionalHandle(clientId: string): string;
|
|
4884
|
+
declare function isProvisional(handle: string): boolean;
|
|
4885
|
+
/**
|
|
4886
|
+
* Merge two entries for the SAME handle. A delete always wins (tombstone); an
|
|
4887
|
+
* edit wins over the entry it supersedes when its HLC is greater; otherwise the
|
|
4888
|
+
* existing entry stands (a re-delivered `msg` is idempotent).
|
|
4889
|
+
*/
|
|
4890
|
+
declare function mergeMessage(local: MessageEntry | null, incoming: MessageEntry): MessageEntry;
|
|
4891
|
+
|
|
4892
|
+
/**
|
|
4893
|
+
* `SpaceCache` — the offline adapter a {@link ../spaces/Space} talks to. It
|
|
4894
|
+
* persists sealed message frames (ciphertext at rest), tracks per-space sync
|
|
4895
|
+
* cursors, and records offline sends in the durable queue. Kept as its own
|
|
4896
|
+
* object (rather than wiring the Space straight to the store + clock) so the
|
|
4897
|
+
* Space stays decoupled from IndexedDB and so the no-op path is a single
|
|
4898
|
+
* `enabled` check.
|
|
4899
|
+
*
|
|
4900
|
+
* Messages are the structural CRDT (see {@link ./crdt/MessageLog}): the server
|
|
4901
|
+
* assigns a monotonic `handle` that totally-orders the log, so the cache just
|
|
4902
|
+
* stores frames keyed by a zero-padded handle (real) or a provisional key
|
|
4903
|
+
* (pending sends, which sort to the tail until their echo arrives).
|
|
4904
|
+
*/
|
|
4905
|
+
|
|
4906
|
+
/** Zero-pad a numeric server handle so string keys sort in handle order. */
|
|
4907
|
+
declare function padHandle(handle: number): string;
|
|
4908
|
+
interface SpaceCursor {
|
|
4909
|
+
lastSeenHandle: number;
|
|
4910
|
+
oldestHandle: number;
|
|
4911
|
+
fullyBackfilled: boolean;
|
|
4912
|
+
}
|
|
4913
|
+
/**
|
|
4914
|
+
* The surface a {@link ../spaces/Space} uses. Declared structurally on the
|
|
4915
|
+
* Space side too (as `SpaceOfflineAdapter`) so the spaces module never imports
|
|
4916
|
+
* the offline module.
|
|
4917
|
+
*/
|
|
4918
|
+
declare class SpaceCache {
|
|
4919
|
+
private readonly manager;
|
|
4920
|
+
private readonly store;
|
|
4921
|
+
constructor(manager: OfflineManager);
|
|
4922
|
+
get enabled(): boolean;
|
|
4923
|
+
newClientId(): string;
|
|
4924
|
+
nextHlc(): Promise<string>;
|
|
4925
|
+
/** Persist a sealed message frame keyed by its (real or provisional) handle. */
|
|
4926
|
+
putMessage(spaceId: string, entry: MessageEntry): Promise<void>;
|
|
4927
|
+
/** Tombstone the message at `handle`. */
|
|
4928
|
+
putDeleted(spaceId: string, handle: number): Promise<void>;
|
|
4929
|
+
/** Drop a pending (optimistic) entry once its real echo has been stored. */
|
|
4930
|
+
dropPending(spaceId: string, clientId: string): Promise<void>;
|
|
4931
|
+
/** All cached frames for a space, in handle order (pending ones last). */
|
|
4932
|
+
loadMessages(spaceId: string): Promise<MessageEntry[]>;
|
|
4933
|
+
getCursor(spaceId: string): Promise<SpaceCursor | null>;
|
|
4934
|
+
/** Advance the last-seen handle (monotonic) and track the oldest fetched. */
|
|
4935
|
+
observeHandle(spaceId: string, handle: number): Promise<void>;
|
|
4936
|
+
/** Queue a raw space frame for replay when the connection returns. */
|
|
4937
|
+
enqueueFrame(spaceId: string, frame: unknown, clientId: string, hlc: string): Promise<void>;
|
|
4938
|
+
/** Register an inbound catch-up task (forward/back history paging). */
|
|
4939
|
+
registerCatchUp(task: () => Promise<void>): () => void;
|
|
4940
|
+
}
|
|
4941
|
+
|
|
4942
|
+
/**
|
|
4943
|
+
* Errors specific to the offline layer.
|
|
4944
|
+
*/
|
|
4945
|
+
/**
|
|
4946
|
+
* Thrown when an offline read/write needs the user's identity (to decrypt or
|
|
4947
|
+
* re-encrypt cached data) but the client is locked — typically right after a
|
|
4948
|
+
* reload, where only the session token was restored and `unlock()` hasn't run
|
|
4949
|
+
* yet. Distinct from the generic "not signed in" identity error so apps can
|
|
4950
|
+
* specifically prompt the user to unlock to see their offline data.
|
|
4951
|
+
*/
|
|
4952
|
+
declare class OfflineLockedError extends Error {
|
|
4953
|
+
constructor(message?: string);
|
|
4954
|
+
}
|
|
4955
|
+
|
|
4956
|
+
/**
|
|
4957
|
+
* Environment probe deciding whether the offline layer turns itself on. Offline
|
|
4958
|
+
* support is **on by default in browsers** and a no-op everywhere else, so this
|
|
4959
|
+
* is the gate the {@link ../core/Client} consults to pick {@link
|
|
4960
|
+
* ./store/IndexedDbStore} vs {@link ./store/NoopStore}.
|
|
4961
|
+
*
|
|
4962
|
+
* We require all three web APIs the layer leans on — `indexedDB` (structured
|
|
4963
|
+
* cache + durable queue), the Cache API (file-shard bytes), and a `window`
|
|
4964
|
+
* (so we're in a real document with online/offline events) — and we *touch*
|
|
4965
|
+
* them inside a try/catch, because some sandboxed or private-mode contexts
|
|
4966
|
+
* expose the globals but throw on use (mirrors `defaultSessionStore`).
|
|
4967
|
+
*/
|
|
4968
|
+
declare function isOfflineCapable(): boolean;
|
|
4969
|
+
|
|
4970
|
+
/**
|
|
4971
|
+
* `IndexedDbStore` — the browser {@link OfflineStore}, backed by one IndexedDB
|
|
4972
|
+
* database per origin (see {@link ./schema}). It opens lazily on first use and
|
|
4973
|
+
* memoizes the connection. The queue methods sit on the autoincrement
|
|
4974
|
+
* `outbound-queue` store and return mutations in HLC order for the sync engine.
|
|
4975
|
+
*
|
|
4976
|
+
* Construct only where `indexedDB` exists — {@link ../detect.isOfflineCapable}
|
|
4977
|
+
* gates this, and the {@link ../../core/Client} falls back to {@link ./NoopStore}
|
|
4978
|
+
* everywhere else.
|
|
4979
|
+
*/
|
|
4980
|
+
|
|
4981
|
+
declare class IndexedDbStore implements OfflineStore {
|
|
4982
|
+
readonly enabled = true;
|
|
4983
|
+
private conn;
|
|
4984
|
+
private opening;
|
|
4985
|
+
private connect;
|
|
4986
|
+
ready(): Promise<void>;
|
|
4987
|
+
get<T>(store: StoreName, key: string): Promise<T | null>;
|
|
4988
|
+
put<T>(store: StoreName, key: string, value: T): Promise<void>;
|
|
4989
|
+
delete(store: StoreName, key: string): Promise<void>;
|
|
4990
|
+
prefix<T>(store: StoreName, prefix: string): Promise<Array<OfflineEntry<T>>>;
|
|
4991
|
+
range<T>(store: StoreName, lower: string, upper: string): Promise<Array<OfflineEntry<T>>>;
|
|
4992
|
+
all<T>(store: StoreName): Promise<Array<OfflineEntry<T>>>;
|
|
4993
|
+
clear(store: StoreName): Promise<void>;
|
|
4994
|
+
enqueue(entry: Omit<QueueEntry, "seq">): Promise<number>;
|
|
4995
|
+
dequeue(seq: number): Promise<void>;
|
|
4996
|
+
queued(): Promise<QueueEntry[]>;
|
|
4997
|
+
}
|
|
4998
|
+
|
|
4999
|
+
/**
|
|
5000
|
+
* `NoopStore` — the {@link OfflineStore} used wherever offline support is off
|
|
5001
|
+
* (Node, Workers, SSR, or when a consumer disables it). Every read returns
|
|
5002
|
+
* empty and every write is dropped, so the namespace hooks that call into it
|
|
5003
|
+
* behave exactly as they did before the offline layer existed. `enabled` is
|
|
5004
|
+
* `false` so hot paths can skip the call entirely.
|
|
5005
|
+
*/
|
|
5006
|
+
|
|
5007
|
+
declare class NoopStore implements OfflineStore {
|
|
5008
|
+
readonly enabled = false;
|
|
5009
|
+
ready(): Promise<void>;
|
|
5010
|
+
get<T>(_store: StoreName, _key: string): Promise<T | null>;
|
|
5011
|
+
put<T>(_store: StoreName, _key: string, _value: T): Promise<void>;
|
|
5012
|
+
delete(_store: StoreName, _key: string): Promise<void>;
|
|
5013
|
+
prefix<T>(_store: StoreName, _prefix: string): Promise<Array<OfflineEntry<T>>>;
|
|
5014
|
+
range<T>(_store: StoreName, _lower: string, _upper: string): Promise<Array<OfflineEntry<T>>>;
|
|
5015
|
+
all<T>(_store: StoreName): Promise<Array<OfflineEntry<T>>>;
|
|
5016
|
+
clear(_store: StoreName): Promise<void>;
|
|
5017
|
+
enqueue(_entry: Omit<QueueEntry, "seq">): Promise<number>;
|
|
5018
|
+
dequeue(_seq: number): Promise<void>;
|
|
5019
|
+
queued(): Promise<QueueEntry[]>;
|
|
5020
|
+
}
|
|
5021
|
+
|
|
5022
|
+
/** The smallest possible stamp — used as the "nothing stored yet" sentinel so a
|
|
5023
|
+
* first real write always wins (`compareHlc(real, ZERO_HLC) > 0`). */
|
|
5024
|
+
declare const ZERO_HLC: string;
|
|
5025
|
+
interface HlcParts {
|
|
5026
|
+
/** Physical wall-clock time, milliseconds since the Unix epoch. */
|
|
5027
|
+
wall: number;
|
|
5028
|
+
/** Logical counter within `wall`. */
|
|
5029
|
+
counter: number;
|
|
5030
|
+
/** Stable per-install node id (tiebreak). */
|
|
5031
|
+
nodeId: string;
|
|
5032
|
+
}
|
|
5033
|
+
/** Pack the three components into the sortable string form. */
|
|
5034
|
+
declare function pack(wall: number, counter: number, nodeId: string): string;
|
|
5035
|
+
/** Parse a packed stamp back into its components. Tolerates the empty/zero
|
|
5036
|
+
* sentinel. Throws on a malformed string so corruption surfaces loudly. */
|
|
5037
|
+
declare function unpack(stamp: string): HlcParts;
|
|
5038
|
+
/**
|
|
5039
|
+
* Total order over packed HLC strings: negative if `a < b`, positive if
|
|
5040
|
+
* `a > b`, `0` only when they are byte-identical. Because the fields are
|
|
5041
|
+
* fixed-width and ordered (wall, counter, nodeId), a single string comparison
|
|
5042
|
+
* already yields causal order — this helper just normalizes the result to the
|
|
5043
|
+
* `-1 | 0 | 1` shape callers expect.
|
|
5044
|
+
*/
|
|
5045
|
+
declare function compareHlc(a: string, b: string): number;
|
|
5046
|
+
/** Convenience: is `a` strictly newer than `b`? */
|
|
5047
|
+
declare function isNewer(a: string, b: string): boolean;
|
|
5048
|
+
|
|
5049
|
+
/**
|
|
5050
|
+
* `HLC` — a Hybrid Logical Clock. Hands out monotonically increasing
|
|
5051
|
+
* {@link HlcTimestamp} stamps that (a) track real time closely enough to be
|
|
5052
|
+
* human-meaningful, yet (b) never go backwards, even across a reload or when a
|
|
5053
|
+
* peer's clock is ahead of ours.
|
|
5054
|
+
*
|
|
5055
|
+
* Two operations, both the textbook HLC algorithm (Kulkarni et al.):
|
|
5056
|
+
* - `now()` — stamp a local event.
|
|
5057
|
+
* - `update(remote)` — observe a remote stamp (e.g. arriving on the realtime
|
|
5058
|
+
* change feed) and advance our clock past it, so any event we generate
|
|
5059
|
+
* afterwards is causally `> remote`.
|
|
5060
|
+
*
|
|
5061
|
+
* State is just `{ wall, counter }`; `nodeId` is fixed for the install. The
|
|
5062
|
+
* owner ({@link ../OfflineManager}) seeds the clock from persisted state on
|
|
5063
|
+
* boot and persists `getState()` after each tick so monotonicity survives page
|
|
5064
|
+
* reloads — without that, a reload could mint a stamp that loses to one written
|
|
5065
|
+
* seconds earlier.
|
|
5066
|
+
*/
|
|
5067
|
+
|
|
5068
|
+
interface HlcState {
|
|
5069
|
+
wall: number;
|
|
5070
|
+
counter: number;
|
|
5071
|
+
}
|
|
5072
|
+
/** Injected physical-time source — defaults to `Date.now`. Overridable so
|
|
5073
|
+
* tests can drive the clock deterministically. */
|
|
5074
|
+
type PhysicalClock = () => number;
|
|
5075
|
+
declare class HLC {
|
|
5076
|
+
private wall;
|
|
5077
|
+
private counter;
|
|
5078
|
+
private readonly nodeId;
|
|
5079
|
+
private readonly physical;
|
|
5080
|
+
constructor(nodeId: string, opts?: {
|
|
5081
|
+
state?: HlcState | null;
|
|
5082
|
+
physical?: PhysicalClock;
|
|
5083
|
+
});
|
|
5084
|
+
/** Stamp a locally-generated event. Advances the clock. */
|
|
5085
|
+
now(): string;
|
|
5086
|
+
/**
|
|
5087
|
+
* Merge in an observed remote stamp and advance our clock strictly past it.
|
|
5088
|
+
* Call this for every inbound HLC (change-feed frames, history rows) so the
|
|
5089
|
+
* next `now()` is causally after anything we've seen.
|
|
5090
|
+
*/
|
|
5091
|
+
update(remote: HlcParts): string;
|
|
5092
|
+
/** The current stamp without advancing the clock. */
|
|
5093
|
+
current(): string;
|
|
5094
|
+
/** Serializable `{ wall, counter }` for persistence. */
|
|
5095
|
+
getState(): HlcState;
|
|
5096
|
+
}
|
|
5097
|
+
|
|
5098
|
+
/**
|
|
5099
|
+
* `LWWMap` — a map of independent last-write-wins registers, one per column.
|
|
5100
|
+
* This is the conflict model for a `db` row: two devices that edit *different*
|
|
5101
|
+
* columns of the same row while offline both keep their edits (no lost update),
|
|
5102
|
+
* and two that edit the *same* column converge to the greater-HLC write. A row
|
|
5103
|
+
* delete is a causal tombstone (see {@link ./ORSet}); a later write to any
|
|
5104
|
+
* column resurrects the row with just the surviving columns.
|
|
5105
|
+
*
|
|
5106
|
+
* This mirrors, on the client, exactly what the accelerator's `_muhkoo_crdt`
|
|
5107
|
+
* sidecar does server-side — so a row materializes identically whether it was
|
|
5108
|
+
* merged locally (offline) or by the server on replay.
|
|
5109
|
+
*/
|
|
5110
|
+
interface LwwMap {
|
|
5111
|
+
/** Per-column value of the winning write. */
|
|
5112
|
+
fields: Record<string, unknown>;
|
|
5113
|
+
/** Per-column HLC of the winning write. */
|
|
5114
|
+
columnHlc: Record<string, string>;
|
|
5115
|
+
/** Row-delete tombstone HLC, or `null` when the row was never deleted. */
|
|
5116
|
+
tombstone: string | null;
|
|
5117
|
+
}
|
|
5118
|
+
declare function emptyMap(): LwwMap;
|
|
5119
|
+
/** Apply a local column write (insert/update) at the given per-column HLCs. */
|
|
5120
|
+
declare function writeColumns(map: LwwMap, values: Record<string, unknown>, hlc: string): LwwMap;
|
|
5121
|
+
/** Apply a row delete at `hlc` (causal tombstone). */
|
|
5122
|
+
declare function tombstoneRow(map: LwwMap, hlc: string): LwwMap;
|
|
5123
|
+
/** Merge two row states column-by-column. Pure; neither input is mutated. */
|
|
5124
|
+
declare function mergeMap(a: LwwMap, b: LwwMap): LwwMap;
|
|
5125
|
+
/**
|
|
5126
|
+
* Materialize the visible row from merged state, or `null` when the row is
|
|
5127
|
+
* deleted. A column is visible only when its write is causally newer than the
|
|
5128
|
+
* tombstone — so a delete hides every prior column, but a post-delete write to
|
|
5129
|
+
* a column brings the row back with just that column populated.
|
|
5130
|
+
*/
|
|
5131
|
+
declare function materializeRow(map: LwwMap): Record<string, unknown> | null;
|
|
5132
|
+
|
|
5133
|
+
/**
|
|
5134
|
+
* Tombstone helpers shared by the LWW CRDTs. We don't need a full
|
|
5135
|
+
* observed-remove set — every domain that deletes (kv keys, db rows) does so
|
|
5136
|
+
* by stamping a delete with an HLC and keeping that stamp around. The only
|
|
5137
|
+
* rule that matters is the causal one:
|
|
5138
|
+
*
|
|
5139
|
+
* a delete at HLC `d` hides the value, but a *later* write at HLC `w > d`
|
|
5140
|
+
* resurrects it; a *stale* write at `w <= d` is dropped.
|
|
5141
|
+
*
|
|
5142
|
+
* Keeping the delete HLC (rather than physically forgetting the key) is what
|
|
5143
|
+
* prevents a delayed offline write from "un-deleting" something a peer already
|
|
5144
|
+
* removed. Callers GC tombstones older than any possible offline window.
|
|
5145
|
+
*/
|
|
5146
|
+
/**
|
|
5147
|
+
* Does a write at `writeHlc` win against a tombstone at `tombstoneHlc`?
|
|
5148
|
+
* `true` ⇒ the write applies (resurrecting a deleted item if needed); `false`
|
|
5149
|
+
* ⇒ the tombstone still holds and the write is dropped. A missing tombstone
|
|
5150
|
+
* (`null`) means there's nothing suppressing the write.
|
|
5151
|
+
*/
|
|
5152
|
+
declare function writeBeatsTombstone(writeHlc: string, tombstoneHlc: string | null): boolean;
|
|
5153
|
+
/**
|
|
5154
|
+
* Does a delete at `deleteHlc` win against the latest write at `writeHlc`?
|
|
5155
|
+
* `true` ⇒ the item is now tombstoned. Ties go to the delete (remove-wins) so
|
|
5156
|
+
* that a delete and a write stamped in the same instant resolve deterministically
|
|
5157
|
+
* toward removal.
|
|
5158
|
+
*/
|
|
5159
|
+
declare function deleteBeatsWrite(deleteHlc: string, writeHlc: string | null): boolean;
|
|
5160
|
+
|
|
4267
5161
|
/**
|
|
4268
5162
|
* KeyringClient — HTTP implementation of {@link KeyringTransport} against the
|
|
4269
5163
|
* accelerator's per-space keyring endpoints. All blobs are opaque to the
|
|
@@ -4324,5 +5218,5 @@ declare class KeyringClient implements KeyringTransport {
|
|
|
4324
5218
|
fetchMetadata(): Promise<SpaceMetadata | null>;
|
|
4325
5219
|
}
|
|
4326
5220
|
|
|
4327
|
-
export { AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AgentsNamespace, AuthClient, AuthNamespace, AuthPublicInput, Authenticator, BroadcastChannel, BroadcastChannelEvents, ChannelExistsError, ChannelNotFoundError, ChunkCipher, Client, DEFAULT_AUTH_BASE_URL, DEFAULT_BASE_URL, DEFAULT_FN_HOST_SUFFIX, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, FunctionsNamespace, HashKnowledge, HttpClient, HttpError, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KvNamespace, LocalStorageSessionStore, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, MuhkooAgent, MuhkooDB, MuhkooFunction, MuhkooSpace, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PersonalSpaceClient, Poseidon, PreimagePoK, ReedSolomonCodec, Space as Room, SerializeMessage, SessionState, ShardClient, SharedSpaceClient, Space, SpaceKeyring, SpaceNamespace, SpacePacketCipher, StorageNamespace, VaultUnavailableError, WSTransport, ZkAuth, buildCommitment, compilePrograms, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultSessionStore, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, deriveIdentityFromSeed, deriveMasterSeedFromPassword, ejectAgentPrompt, ejectAgentTools, encodeToHex, encryptAesGcm, exportPublicKeyBase64, exportPublicKeyHex, generateAuthProof, generatePkce, getMuhkooAppDescriptor, getSubtle, initBn128Wasm, initRsWasm, initializeCircuits, manifestToStat, poseidonHash, quickVerify, randomBytes, randomState, sealSeed, shardHash, signMessage, toField, unsealSeed, unwrapWithPassphrase, verify, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase };
|
|
4328
|
-
export type { ACL, ACLEntry, AgentConfig, AgentCreateInput, AgentDbToolMode, AgentProvisioned, AgentScopeOpts, AgentSkill, AgentToolsConfig, AgentTrigger, AgentTriggerType, AgentUpdateInput, AgentsNamespaceDeps, 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, FunctionCaps, FunctionConfig, FunctionDeployInput, FunctionInvokeOptions, FunctionScopeOpts, FunctionTrigger, FunctionTriggerType, FunctionTriggers, FunctionUpdateInput, FunctionsNamespaceDeps, Groth16Proof, HttpClientOptions, IncomingFrame, KeyExchangeFrame, KeyPair, Keypair, KeyringClientDeps, KeyringTransport, KvNamespaceDeps, LoginOptions, MessageBody, MessageDeletedEvent, MessageEvent, MessageHeaders, MessageNamespaceDeps, MessageOptions, MessageSubscription, MuhkooAgentMeta, MuhkooAgentToolsConfig, MuhkooAppDescriptor, MuhkooDBAccess, MuhkooDBMeta, MuhkooFunctionMeta, MuhkooMessageEvent, MuhkooSpaceMeta, OAuthProvider, PacketCipher, PacketHeaders, PacketOptions, Participant, Permission, PermissionCheckRequest, PermissionCheckResult, PersonalSpaceClientOptions, Pkce, PublicKeyJWK, RatchetState, ReceiveResult, RegisterParams, Role, SpaceDeps as RoomDeps, SpaceFileMetadata as RoomFileMetadata, SealedHandoff, SealedHeaders, Session, SessionOptions, SessionStore, SetOptions, ShardClientOptions, SharedSpaceClientOptions, SnarkProof, SpaceChannelLike, SpaceDeps, SpaceFileMetadata, SpaceKeyCache, SpaceKeyringDeps, SpaceMessageEvent, SpaceNamespaceDeps, StorageChangeEvent, StoredSession, Subscription, UserIdentity, VaultFactor, VerificationKey, WSMessage, WSMessageType, WSTransportOptions, WrappedPayload, WriteFileInput, WriteFileOptions, WriteToShardsInput, ZkAuthDeps, ZkAuthenticateRequest, ZkChallengeResponse, ZkCompiled, ZkIdentity, ZkRegisterRequest, ZkRegisterResponse };
|
|
5221
|
+
export { AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AgentsNamespace, AuthClient, AuthNamespace, AuthPublicInput, Authenticator, BroadcastChannel, BroadcastChannelEvents, ChannelExistsError, ChannelNotFoundError, ChunkCipher, Client, ConnectivityManager, DEFAULT_AUTH_BASE_URL, DEFAULT_BASE_URL, DEFAULT_FN_HOST_SUFFIX, DbCache, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, FunctionsNamespace, HLC, HashKnowledge, HttpClient, HttpError, IndexedDbStore, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KvCache, KvNamespace, LocalStorageSessionStore, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, MuhkooAgent, MuhkooDB, MuhkooFunction, MuhkooSpace, NoopStore, OfflineLockedError, OfflineManager, OutboundQueue, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PersonalSpaceClient, Poseidon, PreimagePoK, ReedSolomonCodec, Space as Room, SerializeMessage, SessionState, ShardCache, ShardClient, SharedSpaceClient, SnapshotNamespace, Space, SpaceCache, SpaceKeyring, SpaceNamespace, SpacePacketCipher, StorageNamespace, SyncEngine, VaultUnavailableError, WSTransport, ZERO_HLC, ZkAuth, buildCommitment, compareHlc, compilePrograms, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultSessionStore, deleteBeatsWrite, deleteRegister, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, deriveIdentityFromSeed, deriveMasterSeedFromPassword, ejectAgentPrompt, ejectAgentTools, emptyMap, emptyRegister, encodeToHex, encryptAesGcm, exportPublicKeyBase64, exportPublicKeyHex, generateAuthProof, generatePkce, getMuhkooAppDescriptor, getSubtle, initBn128Wasm, initRsWasm, initializeCircuits, isNewer, isOfflineCapable, isProvisional, manifestToStat, materializeRow, mergeMap, mergeMessage, mergeRegister, messageKey, pack, padHandle, poseidonHash, provisionalHandle, quickVerify, randomBytes, randomState, sealSeed, shardHash, signMessage, stableStringify, toField, tombstoneRow, unpack, unsealSeed, unwrapWithPassphrase, verify, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase, writeBeatsTombstone, writeColumns, writeRegister };
|
|
5222
|
+
export type { ACL, ACLEntry, AgentConfig, AgentCreateInput, AgentDbToolMode, AgentProvisioned, AgentScopeOpts, AgentSkill, AgentToolsConfig, AgentTrigger, AgentTriggerType, AgentUpdateInput, AgentsNamespaceDeps, AuthClientOptions, AuthKeyPair, AuthSuccess, AuthToken, AuthType, AuthUser, Bn128WasmInstance, BroadcastChannelEvent, BroadcastChannelOptions, CachedSpaceMessage, ChannelLike, ChunkKeyMaterial, ChunkManifest, CipherFrame, CipherMessage, CipherMessageHeader, CircuitBufferConfig, CircuitUrls, ClientOptions, ConnectivityOptions, ConnectivityState, DbQueryPage, DehydratedKeypair, DehydratedKeys, DoubleRatchetCipherDeps, ECDHCurve, EncodeResult, EphemeralEvent, EpochKeyProvider, FileManifest, FileNamespaceDeps, FileStat, FileStorageOptions, FunctionCaps, FunctionConfig, FunctionDeployInput, FunctionInvokeOptions, FunctionScopeOpts, FunctionTrigger, FunctionTriggerType, FunctionTriggers, FunctionUpdateInput, FunctionsNamespaceDeps, Groth16Proof, HlcParts, HlcState, HttpClientOptions, IncomingFrame, KeyExchangeFrame, KeyPair, Keypair, KeyringClientDeps, KeyringTransport, KvEntry, KvNamespaceDeps, LoginOptions, LwwMap, LwwRegister, MessageBody, MessageDeletedEvent, MessageEntry, MessageEvent, MessageHeaders, MessageNamespaceDeps, MessageOp, MessageOptions, MessageSubscription, MuhkooAgentMeta, MuhkooAgentToolsConfig, MuhkooAppDescriptor, MuhkooDBAccess, MuhkooDBMeta, MuhkooFunctionMeta, MuhkooMessageEvent, MuhkooSpaceMeta, OAuthProvider, OfflineEntry, OfflineManagerDeps, OfflineStore, PacketCipher, PacketHeaders, PacketOptions, Participant, Permission, PermissionCheckRequest, PermissionCheckResult, PersonalSpaceClientOptions, Pkce, PublicKeyJWK, QueueEntry, RatchetState, ReceiveResult, RegisterParams, Replayer, Role, SpaceDeps as RoomDeps, SpaceFileMetadata as RoomFileMetadata, SealedHandoff, SealedHeaders, Session, SessionOptions, SessionStore, SetOptions, ShardByteCache, ShardClientOptions, SharedSpaceClientOptions, SnapshotNamespaceDeps, SnarkProof, SpaceChannelLike, SpaceCursor, SpaceDeps, SpaceFileMetadata, SpaceKeyCache, SpaceKeyringDeps, SpaceMessageEvent, SpaceNamespaceDeps, SpaceOfflineAdapter, StorageChangeEvent, StoreName, StoredSession, Subscription, UserIdentity, VaultFactor, VerificationKey, WSMessage, WSMessageType, WSTransportOptions, WrappedPayload, WriteFileInput, WriteFileOptions, WriteToShardsInput, ZkAuthDeps, ZkAuthenticateRequest, ZkChallengeResponse, ZkCompiled, ZkIdentity, ZkRegisterRequest, ZkRegisterResponse };
|