@spooky-sync/core 0.0.1-canary.162 → 0.0.1-canary.164
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/AGENTS.md +2 -1
- package/dist/index.d.ts +310 -2
- package/dist/index.js +1126 -10
- package/dist/types.d.ts +26 -0
- package/package.json +3 -3
- package/src/index.ts +7 -0
- package/src/modules/devtools/index.ts +17 -1
- package/src/modules/devtools/storage-info.ts +25 -0
- package/src/modules/sync/sync.ts +25 -2
- package/src/services/blobs/blob-cache.test.ts +359 -0
- package/src/services/blobs/blob-cache.ts +603 -0
- package/src/services/blobs/blob-manifest.ts +227 -0
- package/src/services/blobs/blob-store.test.ts +77 -0
- package/src/services/blobs/blob-store.ts +359 -0
- package/src/services/blobs/blob.fixture.ts +90 -0
- package/src/services/blobs/index.ts +70 -0
- package/src/services/database/connection-supervisor.test.ts +34 -0
- package/src/services/database/connection-supervisor.ts +66 -2
- package/src/services/database/local-migrator.ts +1 -0
- package/src/services/database/sqlite-cache-engine.ts +4 -0
- package/src/sp00ky.ts +133 -2
- package/src/types.ts +26 -0
package/AGENTS.md
CHANGED
|
@@ -24,7 +24,8 @@ Local mutations are applied optimistically, ingested into a DBSP layer that driv
|
|
|
24
24
|
## Key exports (`src/index.ts`)
|
|
25
25
|
|
|
26
26
|
- `Sp00kyClient<S>` — main class. Methods: `init()`, `create(id, payload)`, `update(table, id, payload, options?)`, `delete(table, idOrSelector)`, `query(table, opts?)`, `run(backend, route, payload)`, `bucket(name)`, `useRemote(fn)`, `authenticate(token)`, `signOut()`. Plus `pendingMutationCount` and `subscribeToPendingMutations(cb)`.
|
|
27
|
-
- `BucketHandle` — file storage handle (`put`, `get`, `delete`, `exists`).
|
|
27
|
+
- `BucketHandle` — file storage handle (`put`, `get`, `delete`, `exists`), plus the cache-aware `read`, `url`, `pin`/`unpin`, `evict`, `prefetch`. `get` is always remote; `read`/`url` go through the blob cache.
|
|
28
|
+
- `services/blobs/` — durable cache for bucket bytes: OPFS holds the files, `_00_blob` holds a manifest that reconcile rebuilds from disk (so a wiped local store costs metadata, not the offline cache). Nothing expires on a timer; eviction is LRU under a byte budget only, skipping pinned and on-screen entries. Config: `blobCache` in `types.ts`.
|
|
28
29
|
- `AuthService` — token management, sign-in/sign-out events.
|
|
29
30
|
- `CrdtManager`, `CrdtField`, `cursorColorFromName`, `CURSOR_COLORS` — Loro-CRDT integration.
|
|
30
31
|
- Types: `Sp00kyConfig`, `SyncedDbConfig` (re-exported by client-solid as the consumer-facing shape), `QueryTimeToLive`, `PersistenceClient`, `StoreType`, `UpdateOptions`, `RunOptions`.
|
package/dist/index.d.ts
CHANGED
|
@@ -119,8 +119,12 @@ declare class ConnectionSupervisor {
|
|
|
119
119
|
private disposed;
|
|
120
120
|
private heartbeatTimer;
|
|
121
121
|
private heartbeatInFlight;
|
|
122
|
+
/** Consecutive failed probes. See {@link FAILURES_BEFORE_TEARDOWN}. */
|
|
123
|
+
private heartbeatFailures;
|
|
122
124
|
private reviveTimer;
|
|
123
125
|
private reviveAttempts;
|
|
126
|
+
/** Timestamp of the last wake-triggered probe, for rate limiting. */
|
|
127
|
+
private lastWakeProbeAt;
|
|
124
128
|
private reviving;
|
|
125
129
|
/**
|
|
126
130
|
* Set while the browser reports itself offline. Retrying a socket against a
|
|
@@ -129,6 +133,23 @@ declare class ConnectionSupervisor {
|
|
|
129
133
|
private suspended;
|
|
130
134
|
private teardown;
|
|
131
135
|
private static readonly REVIVE_BASE_MS;
|
|
136
|
+
/**
|
|
137
|
+
* How many consecutive heartbeat failures it takes to tear the socket down.
|
|
138
|
+
*
|
|
139
|
+
* The probe rides the same serialized queue as every other RPC (deliberately
|
|
140
|
+
* — see {@link beat}), which means it cannot distinguish a WEDGED queue from
|
|
141
|
+
* a merely BUSY one. A single slow window (a large sync burst, one heavy
|
|
142
|
+
* app query) used to be enough to force-close a perfectly healthy socket,
|
|
143
|
+
* and the resulting reconnect re-registered every active query about a
|
|
144
|
+
* second later. That self-inflicted teardown manufactured the very reconnect
|
|
145
|
+
* storms this class exists to survive. A genuinely dead socket still fails
|
|
146
|
+
* every probe, so it is torn down one interval later than before.
|
|
147
|
+
*/
|
|
148
|
+
private static readonly FAILURES_BEFORE_TEARDOWN;
|
|
149
|
+
/** Retry delay after an inconclusive (first) heartbeat failure. */
|
|
150
|
+
private static readonly HEARTBEAT_RETRY_MS;
|
|
151
|
+
/** Floor between probes triggered by wake events (tab focus, pageshow). */
|
|
152
|
+
private static readonly WAKE_PROBE_MIN_INTERVAL_MS;
|
|
132
153
|
constructor(remote: RemoteDatabaseService, logger: Logger$1, config?: Required<ReconnectConfig>);
|
|
133
154
|
/** Latest observed transport state. */
|
|
134
155
|
get connection(): ConnectionState;
|
|
@@ -1068,6 +1089,14 @@ declare class Sp00kySync<S extends SchemaStructure> {
|
|
|
1068
1089
|
* initial connect. See {@link subscribeToReconnect}.
|
|
1069
1090
|
*/
|
|
1070
1091
|
private needsResubscribe;
|
|
1092
|
+
/** When the last reconnect-driven full refetch ran, for burst coalescing. */
|
|
1093
|
+
private lastReconnectRefetchAt;
|
|
1094
|
+
/**
|
|
1095
|
+
* Minimum gap between reconnect-driven full refetches. Long enough to absorb
|
|
1096
|
+
* a flapping socket (the SDK reconnect ladder starts at 1s), short enough
|
|
1097
|
+
* that a genuine drop minutes later still refetches.
|
|
1098
|
+
*/
|
|
1099
|
+
private static readonly RECONNECT_REFETCH_COOLDOWN_MS;
|
|
1071
1100
|
events: SyncEventSystem;
|
|
1072
1101
|
private currentUserId;
|
|
1073
1102
|
private tabRole;
|
|
@@ -1712,13 +1741,286 @@ declare class AppReleaseModule<S extends SchemaStructure> {
|
|
|
1712
1741
|
private applyRecords;
|
|
1713
1742
|
}
|
|
1714
1743
|
//#endregion
|
|
1744
|
+
//#region src/services/blobs/blob-store.d.ts
|
|
1745
|
+
/**
|
|
1746
|
+
* Byte storage for cached bucket files.
|
|
1747
|
+
*
|
|
1748
|
+
* The default implementation is OPFS. Bucket files never arrive over HTTP in
|
|
1749
|
+
* this client — `BucketHandle.get()` is a SurrealQL RPC on the sync socket — so
|
|
1750
|
+
* neither the browser's HTTP cache nor the Cache API can hold them. We persist
|
|
1751
|
+
* the bytes ourselves, and OPFS is the cheapest place to put them: a read is
|
|
1752
|
+
* `getFile()` → a disk-backed lazy `File` that `URL.createObjectURL` can serve
|
|
1753
|
+
* without ever moving the bytes through the JS heap.
|
|
1754
|
+
*
|
|
1755
|
+
* Layout is real nested directories rather than one hashed filename:
|
|
1756
|
+
*
|
|
1757
|
+
* sp00ky-blobs/<namespace>/<bucket>/<...path segments>
|
|
1758
|
+
*
|
|
1759
|
+
* That costs a `getDirectoryHandle` per segment on write, and buys the property
|
|
1760
|
+
* the whole orphan story rests on: the full `(bucket, path)` key is recoverable
|
|
1761
|
+
* from a directory walk alone. The `_00_blob` manifest can therefore be wiped
|
|
1762
|
+
* (memory fallback, SQLite pool wipe, IndexedDB corruption recovery) and be
|
|
1763
|
+
* rebuilt from disk instead of taking the cached bytes down with it.
|
|
1764
|
+
*/
|
|
1765
|
+
/** Identifies one cached file: the bucket it lives in and its path within. */
|
|
1766
|
+
interface BlobKey {
|
|
1767
|
+
bucket: string;
|
|
1768
|
+
path: string;
|
|
1769
|
+
}
|
|
1770
|
+
/** What a directory walk can tell us about a stored file, with no manifest. */
|
|
1771
|
+
interface BlobStat {
|
|
1772
|
+
key: BlobKey;
|
|
1773
|
+
size: number;
|
|
1774
|
+
/** File mtime. Seeds `lastAccess` when a manifest row has to be rebuilt. */
|
|
1775
|
+
mtime: number;
|
|
1776
|
+
}
|
|
1777
|
+
interface BlobStore {
|
|
1778
|
+
/** False for {@link MemoryBlobStore} and for OPFS-less environments: the
|
|
1779
|
+
* cache still dedupes and serves within a tab, but nothing survives reload. */
|
|
1780
|
+
readonly persistent: boolean;
|
|
1781
|
+
/** Namespace (the local bucketId) all keys are resolved under. */
|
|
1782
|
+
readonly namespace: string;
|
|
1783
|
+
read(key: BlobKey): Promise<Blob | null>;
|
|
1784
|
+
/** Returns the number of bytes written. Throws on quota exhaustion. */
|
|
1785
|
+
write(key: BlobKey, bytes: Blob): Promise<number>;
|
|
1786
|
+
remove(key: BlobKey): Promise<void>;
|
|
1787
|
+
/** Every committed file under the current namespace. Sweeps torn writes. */
|
|
1788
|
+
list(): Promise<BlobStat[]>;
|
|
1789
|
+
/** Drop the whole namespace (sign-out with `clearOnSignOut`, or a reset). */
|
|
1790
|
+
clear(): Promise<void>;
|
|
1791
|
+
/** Point at another namespace. Does not touch the bytes of the old one. */
|
|
1792
|
+
setNamespace(namespace: string): void;
|
|
1793
|
+
}
|
|
1794
|
+
//#endregion
|
|
1795
|
+
//#region src/services/blobs/blob-manifest.d.ts
|
|
1796
|
+
interface BlobEntry {
|
|
1797
|
+
/** `${bucket}/${path}` — also the `_00_blob` row id. */
|
|
1798
|
+
id: string;
|
|
1799
|
+
bucket: string;
|
|
1800
|
+
path: string;
|
|
1801
|
+
size: number;
|
|
1802
|
+
contentType: string;
|
|
1803
|
+
createdAt: number;
|
|
1804
|
+
lastAccess: number;
|
|
1805
|
+
hits: number;
|
|
1806
|
+
/** Exempt from pressure eviction. Never expires on its own. */
|
|
1807
|
+
pinned: boolean;
|
|
1808
|
+
}
|
|
1809
|
+
declare class BlobManifest {
|
|
1810
|
+
private local;
|
|
1811
|
+
private entries;
|
|
1812
|
+
/** Ids whose in-memory state has not been written back yet. */
|
|
1813
|
+
private dirty;
|
|
1814
|
+
private removed;
|
|
1815
|
+
private flushing;
|
|
1816
|
+
constructor(local: LocalStore);
|
|
1817
|
+
/**
|
|
1818
|
+
* Hydrate from the rows matching `keys`. Ids come from the OPFS listing, so
|
|
1819
|
+
* this never needs a full-table scan (and therefore never needs a QueryPlan).
|
|
1820
|
+
* Any read failure yields an empty manifest: reconcile then rebuilds every
|
|
1821
|
+
* row from disk, which is exactly the desired degradation.
|
|
1822
|
+
*/
|
|
1823
|
+
load(ids: string[]): Promise<void>;
|
|
1824
|
+
get(key: BlobKey): BlobEntry | undefined;
|
|
1825
|
+
getById(id: string): BlobEntry | undefined;
|
|
1826
|
+
all(): BlobEntry[];
|
|
1827
|
+
totalBytes(): number;
|
|
1828
|
+
pinnedBytes(): number;
|
|
1829
|
+
put(entry: BlobEntry): void;
|
|
1830
|
+
touch(id: string, now: number): void;
|
|
1831
|
+
setPinned(id: string, pinned: boolean): boolean;
|
|
1832
|
+
remove(id: string): void;
|
|
1833
|
+
/** Forget everything without scheduling deletes — for a bucket switch, where
|
|
1834
|
+
* the rows belong to the store we are leaving and must stay put. */
|
|
1835
|
+
reset(): void;
|
|
1836
|
+
hasPendingWrites(): boolean;
|
|
1837
|
+
/**
|
|
1838
|
+
* Write back pending changes. Serialized: a second concurrent flush awaits
|
|
1839
|
+
* the first rather than racing it into the same rows. Failures are swallowed
|
|
1840
|
+
* on purpose — a lost metadata write costs an LRU timestamp, and the entry is
|
|
1841
|
+
* rebuilt from disk on the next reconcile.
|
|
1842
|
+
*/
|
|
1843
|
+
flush(): Promise<void>;
|
|
1844
|
+
private doFlush;
|
|
1845
|
+
}
|
|
1846
|
+
//#endregion
|
|
1847
|
+
//#region src/services/blobs/blob-cache.d.ts
|
|
1848
|
+
interface BlobUrlLease {
|
|
1849
|
+
url: string;
|
|
1850
|
+
release(): void;
|
|
1851
|
+
}
|
|
1852
|
+
interface BlobReadOptions {
|
|
1853
|
+
/** Write through to L1 on a miss. Default true. */
|
|
1854
|
+
persist?: boolean;
|
|
1855
|
+
/** Mark the entry exempt from pressure eviction. */
|
|
1856
|
+
pin?: boolean;
|
|
1857
|
+
/**
|
|
1858
|
+
* Default `'never'`: a bucket path is treated as immutable, which is how the
|
|
1859
|
+
* client writes them (`crypto.randomUUID() + ext`). `'head'` spends a remote
|
|
1860
|
+
* `head()` to compare sizes before trusting L1.
|
|
1861
|
+
*/
|
|
1862
|
+
revalidate?: 'never' | 'head';
|
|
1863
|
+
/** Skip L0/L1 entirely and refill from remote. Backs `refetch()`. */
|
|
1864
|
+
reload?: boolean;
|
|
1865
|
+
}
|
|
1866
|
+
interface BlobCacheStats {
|
|
1867
|
+
entries: number;
|
|
1868
|
+
totalBytes: number;
|
|
1869
|
+
budgetBytes: number;
|
|
1870
|
+
pinnedBytes: number;
|
|
1871
|
+
evictedEntries: number;
|
|
1872
|
+
evictedBytes: number;
|
|
1873
|
+
reconciledEntries: number;
|
|
1874
|
+
hits: number;
|
|
1875
|
+
misses: number;
|
|
1876
|
+
persistent: boolean;
|
|
1877
|
+
/** True when pinned bytes alone exceed the budget: new entries stop being
|
|
1878
|
+
* written rather than pinned ones being thrown away. */
|
|
1879
|
+
persistPaused: boolean;
|
|
1880
|
+
}
|
|
1881
|
+
interface BlobCacheOptions {
|
|
1882
|
+
store: BlobStore;
|
|
1883
|
+
manifest: BlobManifest;
|
|
1884
|
+
/** L2 read. Resolves to null when the file does not exist remotely. */
|
|
1885
|
+
fetchRemote(key: BlobKey): Promise<Blob | null>;
|
|
1886
|
+
/** L2 metadata, for `revalidate: 'head'`. */
|
|
1887
|
+
headRemote?(key: BlobKey): Promise<Record<string, unknown> | null>;
|
|
1888
|
+
logger: Logger$1;
|
|
1889
|
+
maxBytes: number;
|
|
1890
|
+
now?: () => number;
|
|
1891
|
+
/** Injected so the URL layer is exercisable off a DOM (node tests). */
|
|
1892
|
+
urls?: {
|
|
1893
|
+
create(blob: Blob): string;
|
|
1894
|
+
revoke(url: string): void;
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
declare class BlobCache {
|
|
1898
|
+
private readonly store;
|
|
1899
|
+
private readonly manifest;
|
|
1900
|
+
private readonly fetchRemote;
|
|
1901
|
+
private readonly headRemote?;
|
|
1902
|
+
private readonly logger;
|
|
1903
|
+
private readonly now;
|
|
1904
|
+
private readonly urlFactory;
|
|
1905
|
+
private maxBytes;
|
|
1906
|
+
private persistPaused;
|
|
1907
|
+
/** Set after a quota failure survives one forced eviction. */
|
|
1908
|
+
private persistDisabled;
|
|
1909
|
+
private readonly urls;
|
|
1910
|
+
/** Ids at zero references, oldest first — the hot-URL window. */
|
|
1911
|
+
private idleUrls;
|
|
1912
|
+
private readonly inflight;
|
|
1913
|
+
private flushTimer;
|
|
1914
|
+
private readonly onPageHide;
|
|
1915
|
+
/**
|
|
1916
|
+
* Resolves once the manifest has been reconciled against disk. Reads await
|
|
1917
|
+
* it, so `start()` does NOT have to be awaited on the boot path — blocking
|
|
1918
|
+
* boot on an OPFS directory walk delayed the WebSocket connect (and with it
|
|
1919
|
+
* the connection supervisor) for no benefit.
|
|
1920
|
+
*/
|
|
1921
|
+
private ready;
|
|
1922
|
+
private hits;
|
|
1923
|
+
private misses;
|
|
1924
|
+
private evictedEntries;
|
|
1925
|
+
private evictedBytes;
|
|
1926
|
+
private reconciledEntries;
|
|
1927
|
+
constructor(opts: BlobCacheOptions);
|
|
1928
|
+
/** Coalesce manifest write-back. Metadata only, so losing the tail costs an
|
|
1929
|
+
* LRU timestamp that reconcile reseeds from the file mtime. */
|
|
1930
|
+
private scheduleFlush;
|
|
1931
|
+
/**
|
|
1932
|
+
* Resolve the bytes for `key`, filling L1 on the way when `persist` is on.
|
|
1933
|
+
* Returns null when the file does not exist remotely and is not cached.
|
|
1934
|
+
*/
|
|
1935
|
+
read(key: BlobKey, options?: BlobReadOptions): Promise<Blob | null>;
|
|
1936
|
+
/**
|
|
1937
|
+
* An object URL for `key`, refcounted. Callers MUST `release()`; the URL is
|
|
1938
|
+
* revoked once the last holder lets go and it falls out of the hot window.
|
|
1939
|
+
*/
|
|
1940
|
+
acquireUrl(key: BlobKey, options?: BlobReadOptions): Promise<BlobUrlLease | null>;
|
|
1941
|
+
private lease;
|
|
1942
|
+
private releaseUrl;
|
|
1943
|
+
private revokeUrl;
|
|
1944
|
+
/** L1 lookup with the size check that catches torn and cross-tab writes. */
|
|
1945
|
+
private readLocal;
|
|
1946
|
+
/** True when the remote agrees with the cached size, or cannot be reached. */
|
|
1947
|
+
private headMatches;
|
|
1948
|
+
private fetchDeduped;
|
|
1949
|
+
private persist;
|
|
1950
|
+
private writeThrough;
|
|
1951
|
+
/** Forget one path everywhere. Called on `bucket.put()`/`bucket.delete()`. */
|
|
1952
|
+
invalidate(key: BlobKey): Promise<void>;
|
|
1953
|
+
private dropLocal;
|
|
1954
|
+
setPinned(key: BlobKey, pinned: boolean): void;
|
|
1955
|
+
/**
|
|
1956
|
+
* Bring total bytes under budget by dropping the least recently used
|
|
1957
|
+
* entries. Pinned entries and anything with a live object URL are skipped —
|
|
1958
|
+
* evicting bytes that a mounted `<img>` is displaying would blank it.
|
|
1959
|
+
*/
|
|
1960
|
+
private enforceBudget;
|
|
1961
|
+
/** Evict LRU-first until at or below `target`. Returns the resulting total. */
|
|
1962
|
+
private evictTo;
|
|
1963
|
+
/**
|
|
1964
|
+
* Rebuild the manifest from what is actually on disk. OPFS wins on existence
|
|
1965
|
+
* in both directions: files with no row get a row (seeded from mtime), rows
|
|
1966
|
+
* are only loaded for files that exist, and torn `.part-` writes are swept by
|
|
1967
|
+
* the walk itself.
|
|
1968
|
+
*
|
|
1969
|
+
* Rows whose file vanished outside our control (a browser origin eviction)
|
|
1970
|
+
* are left in `_00_blob`. They are inert — `load()` only ever asks for ids it
|
|
1971
|
+
* found on disk — and are overwritten if that path is cached again.
|
|
1972
|
+
*/
|
|
1973
|
+
reconcile(): Promise<void>;
|
|
1974
|
+
/** Warm the cache for offline use. Skips anything already cached. */
|
|
1975
|
+
prefetch(keys: BlobKey[]): Promise<void>;
|
|
1976
|
+
/** Bind to the boot bucket and hydrate the manifest from disk. Separate from
|
|
1977
|
+
* {@link setNamespace} because boot must reconcile even when the namespace
|
|
1978
|
+
* it lands on is the one the store was constructed with. */
|
|
1979
|
+
start(namespace: string): Promise<void>;
|
|
1980
|
+
/** Repoint at another local bucket. The bytes of the old one stay on disk so
|
|
1981
|
+
* switching back (or signing back in) is still warm. */
|
|
1982
|
+
setNamespace(namespace: string): Promise<void>;
|
|
1983
|
+
setMaxBytes(maxBytes: number): void;
|
|
1984
|
+
/** Delete every cached byte in the current namespace. */
|
|
1985
|
+
clear(): Promise<void>;
|
|
1986
|
+
flush(): Promise<void>;
|
|
1987
|
+
/** Flush metadata and drop every object URL. Must run before the local store
|
|
1988
|
+
* closes — the flush writes through it. */
|
|
1989
|
+
close(): Promise<void>;
|
|
1990
|
+
stats(): BlobCacheStats;
|
|
1991
|
+
}
|
|
1992
|
+
//#endregion
|
|
1715
1993
|
//#region src/sp00ky.d.ts
|
|
1994
|
+
/** Coerce whatever the `.get()` RPC hands back into a Blob. */
|
|
1995
|
+
declare function bucketContentToBlob(content: unknown): Blob | null;
|
|
1716
1996
|
declare class BucketHandle {
|
|
1717
1997
|
private bucketName;
|
|
1718
1998
|
private remote;
|
|
1719
|
-
|
|
1999
|
+
/** Absent on the raw handle the cache itself reads through. */
|
|
2000
|
+
private blobs?;
|
|
2001
|
+
constructor(bucketName: string, remote: RemoteDatabaseService, /** Absent on the raw handle the cache itself reads through. */
|
|
2002
|
+
blobs?: (BlobCache | null) | undefined);
|
|
1720
2003
|
put(path: string, content: string | Uint8Array | Blob): Promise<void>;
|
|
1721
2004
|
get(path: string): Promise<unknown>;
|
|
2005
|
+
/**
|
|
2006
|
+
* Read through the local blob cache: OPFS first, the bucket second. Unlike
|
|
2007
|
+
* {@link get} this survives a reload and works offline. Returns null when the
|
|
2008
|
+
* file exists in neither place.
|
|
2009
|
+
*/
|
|
2010
|
+
read(path: string, options?: BlobReadOptions): Promise<Blob | null>;
|
|
2011
|
+
/**
|
|
2012
|
+
* A refcounted object URL for `path`, suitable for `<img src>`. The caller
|
|
2013
|
+
* MUST call `release()` when the URL goes off screen. Returns null when the
|
|
2014
|
+
* file does not exist, or when object URLs are unavailable (non-browser).
|
|
2015
|
+
*/
|
|
2016
|
+
url(path: string, options?: BlobReadOptions): Promise<BlobUrlLease | null>;
|
|
2017
|
+
/** Exempt `path` from pressure eviction. Pinned bytes never expire. */
|
|
2018
|
+
pin(path: string): void;
|
|
2019
|
+
unpin(path: string): void;
|
|
2020
|
+
/** Drop `path` from the local cache without touching the remote file. */
|
|
2021
|
+
evict(path: string): Promise<void>;
|
|
2022
|
+
/** Warm the cache for offline use. Already-cached paths are skipped. */
|
|
2023
|
+
prefetch(paths: string[]): Promise<void>;
|
|
1722
2024
|
delete(path: string): Promise<void>;
|
|
1723
2025
|
exists(path: string): Promise<boolean>;
|
|
1724
2026
|
head(path: string): Promise<Record<string, unknown>>;
|
|
@@ -1730,6 +2032,7 @@ declare class Sp00kyClient<S extends SchemaStructure> {
|
|
|
1730
2032
|
private config;
|
|
1731
2033
|
private local;
|
|
1732
2034
|
private remote;
|
|
2035
|
+
private blobs;
|
|
1733
2036
|
private connectionSupervisor;
|
|
1734
2037
|
private persistenceClient;
|
|
1735
2038
|
private migrator;
|
|
@@ -1909,6 +2212,11 @@ declare class Sp00kyClient<S extends SchemaStructure> {
|
|
|
1909
2212
|
reportFrontendTiming(queryHash: string, ms: number): void;
|
|
1910
2213
|
run<B extends BackendNames<S>, R extends BackendRoutes<S, B>>(backend: B, path: R, payload: RoutePayload<S, B, R>, options?: RunOptions): Promise<void>;
|
|
1911
2214
|
bucket<B extends BucketNames<S>>(name: B): BucketHandle;
|
|
2215
|
+
/** Cache-free handle. The blob cache reads the remote through this, so a
|
|
2216
|
+
* cache miss can't loop back into the cache. */
|
|
2217
|
+
private rawBucket;
|
|
2218
|
+
/** Blob cache counters for DevTools. */
|
|
2219
|
+
getBlobCacheStats(): BlobCacheStats;
|
|
1912
2220
|
create(id: string, data: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
1913
2221
|
update(table: string, id: string, data: Record<string, unknown>, options?: UpdateOptions): Promise<{
|
|
1914
2222
|
[x: string]: /*elided*/any;
|
|
@@ -1940,4 +2248,4 @@ declare function textToHtml(text: string): string;
|
|
|
1940
2248
|
*/
|
|
1941
2249
|
|
|
1942
2250
|
//#endregion
|
|
1943
|
-
export { AppReleaseHandle, AppReleaseModule, type AppReleaseOptions, type AppReleaseSnapshot, AuthEventSystem, AuthEventTypeMap, AuthEventTypes, AuthService, BucketHandle, CURSOR_COLORS, ConnectionState, CrdtField, CrdtManager, DebounceOptions, EventSubscriptionOptions, FeatureFlagHandle, FeatureFlagModule, type FeatureFlagOptions, type FeatureFlagSnapshot, Level, MATERIALIZATION_SAMPLE_WINDOW, MutationCallback, MutationEvent, MutationEventType, PersistenceClient, PhaseStat, PinoTransmit, PreloadOptions, PreloadRefresh, QueryConfig, QueryConfigRecord, QueryHash, QueryState, QueryStatus, QueryStatusCallback, QueryTimeToLive, QueryTimings, QueryUpdateCallback, ReconnectConfig, RecordVersionArray, RecordVersionDiff, RegistrationTimings, RunOptions, Sp00kyClient, Sp00kyConfig, Sp00kyQueryResult, Sp00kyQueryResultPromise, StorageHealth, StorageHealthStatus, StoreType, SyncHealth, SyncHealthConfig, SyncHealthStatus, TimingPhase, UpdateOptions, createAuthEventSystem, cursorColorFromName, fileToUint8Array, semverGt, textToHtml };
|
|
2251
|
+
export { AppReleaseHandle, AppReleaseModule, type AppReleaseOptions, type AppReleaseSnapshot, AuthEventSystem, AuthEventTypeMap, AuthEventTypes, AuthService, type BlobCacheStats, type BlobEntry, type BlobKey, type BlobReadOptions, type BlobUrlLease, BucketHandle, CURSOR_COLORS, ConnectionState, CrdtField, CrdtManager, DebounceOptions, EventSubscriptionOptions, FeatureFlagHandle, FeatureFlagModule, type FeatureFlagOptions, type FeatureFlagSnapshot, Level, MATERIALIZATION_SAMPLE_WINDOW, MutationCallback, MutationEvent, MutationEventType, PersistenceClient, PhaseStat, PinoTransmit, PreloadOptions, PreloadRefresh, QueryConfig, QueryConfigRecord, QueryHash, QueryState, QueryStatus, QueryStatusCallback, QueryTimeToLive, QueryTimings, QueryUpdateCallback, ReconnectConfig, RecordVersionArray, RecordVersionDiff, RegistrationTimings, RunOptions, Sp00kyClient, Sp00kyConfig, Sp00kyQueryResult, Sp00kyQueryResultPromise, StorageHealth, StorageHealthStatus, StoreType, SyncHealth, SyncHealthConfig, SyncHealthStatus, TimingPhase, UpdateOptions, bucketContentToBlob, createAuthEventSystem, cursorColorFromName, fileToUint8Array, semverGt, textToHtml };
|