js-bao-wss-client 2.2.0-alpha.1 → 2.2.0-alpha.2
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/JsBaoClient.d.ts +577 -1
- package/dist/JsBaoClient.js +1823 -16
- package/dist/api/functionsApi.d.ts +63 -0
- package/dist/api/functionsApi.js +29 -0
- package/dist/browser.umd.js +2804 -126
- package/dist/internal/documentManager.d.ts +104 -1
- package/dist/internal/documentManager.js +282 -3
- package/dist/internal/large-documents/catch-up-ledger.d.ts +54 -0
- package/dist/internal/large-documents/catch-up-ledger.js +52 -0
- package/dist/internal/large-documents/document-format.d.ts +72 -0
- package/dist/internal/large-documents/document-format.js +102 -0
- package/dist/internal/large-documents/epoch-marks.d.ts +92 -0
- package/dist/internal/large-documents/epoch-marks.js +113 -0
- package/dist/internal/large-documents/grant-refresh.d.ts +48 -0
- package/dist/internal/large-documents/grant-refresh.js +42 -0
- package/dist/internal/large-documents/local-copy.d.ts +69 -0
- package/dist/internal/large-documents/local-copy.js +70 -0
- package/dist/internal/large-documents/outbound-ack.d.ts +80 -0
- package/dist/internal/large-documents/outbound-ack.js +129 -0
- package/dist/internal/large-documents/snapshot-ready.d.ts +68 -0
- package/dist/internal/large-documents/snapshot-ready.js +57 -0
- package/package.json +1 -1
package/dist/JsBaoClient.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Observable } from "lib0/observable";
|
|
2
2
|
import * as Y from "yjs";
|
|
3
|
-
import { DatabaseConfig } from "js-bao";
|
|
3
|
+
import { initJsBao, DatabaseConfig } from "js-bao";
|
|
4
4
|
import type { DiscoveredSchema } from "js-bao";
|
|
5
5
|
import type { TypedModelConstructor } from "./types/typed-model-constructor";
|
|
6
6
|
import { LogLevel } from "./internal/logger";
|
|
@@ -11,6 +11,7 @@ import { type AnalyticsEventInput } from "./internal/analyticsQueue";
|
|
|
11
11
|
import { type GoogleClientsConfig } from "./internal/authController";
|
|
12
12
|
import { type RequestOptions } from "./internal/httpClient";
|
|
13
13
|
import { type DocumentPermission as DocumentAccessLevel, type LocalDocumentEntry, type LocalMetadataEntry, type DocumentDebugSnapshot } from "./internal/documentManager";
|
|
14
|
+
import { type OfflineReplayNotice } from "js-bao";
|
|
14
15
|
import { DocumentsAPI, DocumentContext, type DocumentInfo, type ResolveAliasParams, type CreateDocumentOptions } from "./api/documentsApi";
|
|
15
16
|
import { type CacheFacade } from "./api/cacheFacade";
|
|
16
17
|
import { MeAPI } from "./api/meApi";
|
|
@@ -22,6 +23,7 @@ import { DatabasesAPI } from "./api/databasesApi";
|
|
|
22
23
|
import { GroupsAPI } from "./api/groupsApi";
|
|
23
24
|
import { CronTriggersAPI } from "./api/cronTriggersApi";
|
|
24
25
|
import { LocksAPI } from "./api/locksApi";
|
|
26
|
+
import { FunctionsAPI } from "./api/functionsApi";
|
|
25
27
|
import { ResourceMetadataAPI } from "./api/resourceMetadataApi";
|
|
26
28
|
import { RuleSetsAPI } from "./api/ruleSetsApi";
|
|
27
29
|
import { BlobBucketsAPI } from "./api/blobBucketsApi";
|
|
@@ -91,6 +93,7 @@ export type { DocumentDebugSnapshot, DocumentPermission, LocalDocumentEntry, Loc
|
|
|
91
93
|
export type { LogLevel } from "./internal/logger";
|
|
92
94
|
export type { RequestOptions } from "./internal/httpClient";
|
|
93
95
|
export type { LocksAPI } from "./api/locksApi";
|
|
96
|
+
export type { FunctionsAPI, FunctionInvokeOptions, FunctionInvokeResult, FunctionInvokeStatus, } from "./api/functionsApi";
|
|
94
97
|
export type { LockHandle, LockContention, AcquireResponse, AcquireOptions, BlockingAcquireOptions, ReleaseResult, RenewResult, LockStatus, LockListEntry, LockListResult, } from "./api/locksApi";
|
|
95
98
|
export type { ResourceMetadataAPI } from "./api/resourceMetadataApi";
|
|
96
99
|
export type { ResourceMetadataReadResult, ResourceMetadataWriteResult, ResourceMetadataBatchRequestItem, ResourceMetadataBatchParams, ResourceMetadataBatchCategoryResult, ResourceMetadataBatchResourceResult, ResourceMetadataBatchResult, ResourceMetadataListEntry, ResourceMetadataListResult, ResourceMetadataDeleteResult, ResourceMetadataResolveParams, ResourceMetadataResolveResult, } from "./api/resourceMetadataApi";
|
|
@@ -410,6 +413,27 @@ export interface JsBaoClientOptions {
|
|
|
410
413
|
enabled?: boolean;
|
|
411
414
|
};
|
|
412
415
|
databaseConfig?: DatabaseConfig;
|
|
416
|
+
/**
|
|
417
|
+
* What this device may keep of a large document.
|
|
418
|
+
*
|
|
419
|
+
* Loading a large document materializes its records into local storage. On a
|
|
420
|
+
* platform whose quota will not take the whole document, `models` names the
|
|
421
|
+
* ones worth the space — the rest are left unloaded, and the models named
|
|
422
|
+
* are queryable as usual. With none configured, a device short of space is
|
|
423
|
+
* refused with a typed error rather than loaded arbitrarily, and a platform
|
|
424
|
+
* with no durable storage at all is always refused.
|
|
425
|
+
*
|
|
426
|
+
* `capability` overrides the platform probe, for a host that owns its own
|
|
427
|
+
* store and knows what it can hold.
|
|
428
|
+
*/
|
|
429
|
+
largeDocumentStorage?: {
|
|
430
|
+
capability?: {
|
|
431
|
+
persistent: boolean;
|
|
432
|
+
quotaBytes: number | null;
|
|
433
|
+
usedBytes?: number;
|
|
434
|
+
};
|
|
435
|
+
models?: string[];
|
|
436
|
+
};
|
|
413
437
|
storageConfig?: StorageConfig;
|
|
414
438
|
/**
|
|
415
439
|
* Custom Yjs persistence factory for document storage.
|
|
@@ -488,6 +512,21 @@ export interface DocumentSyncStateChangedEvent {
|
|
|
488
512
|
documentId: string;
|
|
489
513
|
state: "syncing" | "synced" | "stale" | "error";
|
|
490
514
|
}
|
|
515
|
+
/**
|
|
516
|
+
* Payload of the `documentOfflineWritesResolved` event
|
|
517
|
+
* (`client.on("documentOfflineWritesResolved", ...)`).
|
|
518
|
+
*
|
|
519
|
+
* Fired when a large document that was away replays what it wrote offline and
|
|
520
|
+
* some of it did not simply apply: a write the online side clearly beat is
|
|
521
|
+
* DROPPED, and one whose order cannot be established is applied but reported.
|
|
522
|
+
* Silence means every offline write replayed cleanly.
|
|
523
|
+
*/
|
|
524
|
+
export interface DocumentOfflineWritesResolvedEvent {
|
|
525
|
+
documentId: string;
|
|
526
|
+
/** The epoch the writes were replayed onto. */
|
|
527
|
+
epoch: number;
|
|
528
|
+
notices: OfflineReplayNotice[];
|
|
529
|
+
}
|
|
491
530
|
/**
|
|
492
531
|
* Options for `client.syncMetadata()`. Controls whether the sync covers all
|
|
493
532
|
* of the user's document metadata or a single document (`scope` /
|
|
@@ -1481,6 +1520,35 @@ export interface EvictAllLocalOptions {
|
|
|
1481
1520
|
* handler)`: the handler's argument type is `JsBaoEvents[name]`. See each
|
|
1482
1521
|
* payload interface for when its event fires.
|
|
1483
1522
|
*/
|
|
1523
|
+
/**
|
|
1524
|
+
* How a large document's base snapshot load is going.
|
|
1525
|
+
*
|
|
1526
|
+
* A cold open of a large document streams a base of up to gigabytes before the
|
|
1527
|
+
* document is usable, which can take minutes. An app with nothing to show for
|
|
1528
|
+
* that is indistinguishable from one that has hung, so the load reports itself:
|
|
1529
|
+
* `started` once the manifest is in hand (which is where `totalRows` comes
|
|
1530
|
+
* from, so a progress bar exists before the first chunk lands), `progress` per
|
|
1531
|
+
* chunk, `model` as each model becomes queryable, and `loaded` at the end.
|
|
1532
|
+
*/
|
|
1533
|
+
export interface DocumentSnapshotLoadEvent {
|
|
1534
|
+
documentId: string;
|
|
1535
|
+
phase: "started" | "progress" | "model" | "loaded";
|
|
1536
|
+
/** The epoch the snapshot is a base for. */
|
|
1537
|
+
epoch: number;
|
|
1538
|
+
rows: number;
|
|
1539
|
+
totalRows: number;
|
|
1540
|
+
chunks: number;
|
|
1541
|
+
totalChunks: number;
|
|
1542
|
+
/** The model the event concerns, when it concerns one. */
|
|
1543
|
+
model?: string;
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* Every event the client emits, mapped to the payload its listener receives.
|
|
1547
|
+
*
|
|
1548
|
+
* `client.on(name, handler)` is typed from this map, so the handler's argument
|
|
1549
|
+
* is the payload type listed here — pick the event you want and follow the
|
|
1550
|
+
* link to see what it carries.
|
|
1551
|
+
*/
|
|
1484
1552
|
export interface JsBaoEvents {
|
|
1485
1553
|
error: {
|
|
1486
1554
|
error: unknown;
|
|
@@ -1516,9 +1584,11 @@ export interface JsBaoEvents {
|
|
|
1516
1584
|
documentOpened: DocumentEvent;
|
|
1517
1585
|
documentMetadataChanged: DocumentMetadataChangedEvent;
|
|
1518
1586
|
documentSyncStateChanged: DocumentSyncStateChangedEvent;
|
|
1587
|
+
documentOfflineWritesResolved: DocumentOfflineWritesResolvedEvent;
|
|
1519
1588
|
permission: PermissionEvent;
|
|
1520
1589
|
sync: SyncEvent;
|
|
1521
1590
|
awareness: AwarenessEvent;
|
|
1591
|
+
"document:snapshot-load": DocumentSnapshotLoadEvent;
|
|
1522
1592
|
"blobs:upload-progress": BlobUploadProgressEvent;
|
|
1523
1593
|
"blobs:upload-completed": BlobUploadCompletedEvent;
|
|
1524
1594
|
"blobs:upload-failed": BlobUploadFailedEvent;
|
|
@@ -1703,9 +1773,162 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
1703
1773
|
private jwtPersistenceCleared;
|
|
1704
1774
|
private docOpensInFlight;
|
|
1705
1775
|
private dbReady;
|
|
1776
|
+
/**
|
|
1777
|
+
* This client's own js-bao instance (#3039), destroyed with the client.
|
|
1778
|
+
*
|
|
1779
|
+
* Was impossible while `initJsBao` memoized one process-wide instance:
|
|
1780
|
+
* tearing it down would have taken every other client's engine with it.
|
|
1781
|
+
*/
|
|
1782
|
+
private jsBaoInstance;
|
|
1706
1783
|
private jsBaoConnectDoc;
|
|
1707
1784
|
private jsBaoDisconnectDoc;
|
|
1708
1785
|
private jsBaoIsDocumentConnected;
|
|
1786
|
+
/**
|
|
1787
|
+
* #2816, behavior 9 — the ORM side of format-2's durable acknowledgement:
|
|
1788
|
+
* the sequences a document's local writes have reached, and the prune the
|
|
1789
|
+
* server's `update.ack` authorizes. `null` on a legacy document.
|
|
1790
|
+
*/
|
|
1791
|
+
private jsBaoFormat2SyncMarks;
|
|
1792
|
+
private jsBaoFormat2Acknowledge;
|
|
1793
|
+
/**
|
|
1794
|
+
* #2816 — the epoch a document's own RECORD DATABASE says it follows,
|
|
1795
|
+
* readable before the ORM binds the document. `0` when that database holds
|
|
1796
|
+
* no document whatever the client's metadata claims; `null` when the engine
|
|
1797
|
+
* cannot be asked, which is not evidence either way.
|
|
1798
|
+
*/
|
|
1799
|
+
private jsBaoFormat2StoredEpoch;
|
|
1800
|
+
private jsBaoFormat2NoteEpoch;
|
|
1801
|
+
/**
|
|
1802
|
+
* #2816, behavior 18 — record that a document was in touch with the server,
|
|
1803
|
+
* and the offline window the server reported. Past that window without a
|
|
1804
|
+
* sync the ORM refuses local writes and keeps serving reads.
|
|
1805
|
+
*/
|
|
1806
|
+
private jsBaoFormat2NoteSync;
|
|
1807
|
+
/**
|
|
1808
|
+
* #2816, behavior 18 — record the offline window the handshake reported,
|
|
1809
|
+
* without claiming a sync. The number is the server's whatever else the
|
|
1810
|
+
* frame says; the mark it is measured from has to be earned.
|
|
1811
|
+
*/
|
|
1812
|
+
private jsBaoFormat2NoteWindow;
|
|
1813
|
+
/**
|
|
1814
|
+
* #2816 — resolves once a large document's derived query tables have caught
|
|
1815
|
+
* up with its merged view. Awaited before an update from the room counts as
|
|
1816
|
+
* applied, so `find()` and `query()` never answer from different epochs.
|
|
1817
|
+
*/
|
|
1818
|
+
private jsBaoWhenFormat2ProjectionSettled;
|
|
1819
|
+
private jsBaoFormat2NoteClockOffset;
|
|
1820
|
+
private jsBaoFormat2ForgetPendingOps;
|
|
1821
|
+
/**
|
|
1822
|
+
* #2816, behavior 17 — rewrite the pending ops a replay resolution narrowed.
|
|
1823
|
+
* A write that survived only in part must be recorded as the surviving part,
|
|
1824
|
+
* or the restore path would put the whole one back after a crash.
|
|
1825
|
+
*/
|
|
1826
|
+
private jsBaoFormat2NarrowPendingOps;
|
|
1827
|
+
/**
|
|
1828
|
+
* What the sealed overlays of a catch-up said was written online, per
|
|
1829
|
+
* document (#2816, behavior 17). Built while the chain is applied — those
|
|
1830
|
+
* artifacts are the online side of every conflict — and consumed by the move
|
|
1831
|
+
* that replays this client's offline writes onto the epoch it landed on.
|
|
1832
|
+
*/
|
|
1833
|
+
/**
|
|
1834
|
+
* A returning client's replay, held until the epoch it joined has synced
|
|
1835
|
+
* (#2816, behavior 17). Keyed by document; the timer is the fallback for a
|
|
1836
|
+
* sync that never completes.
|
|
1837
|
+
*/
|
|
1838
|
+
private readonly format2PendingReplays;
|
|
1839
|
+
/**
|
|
1840
|
+
* The last `epoch.info` this client acted on, per document — what a rebuild
|
|
1841
|
+
* started outside the handshake needs to plan itself (#2816).
|
|
1842
|
+
*/
|
|
1843
|
+
private readonly format2LastHandshakes;
|
|
1844
|
+
private readonly format2ConflictLedgers;
|
|
1845
|
+
/**
|
|
1846
|
+
* Documents already being rebuilt because an offline delete lost (#2816,
|
|
1847
|
+
* behavior 17).
|
|
1848
|
+
*
|
|
1849
|
+
* The rebuild replays the same resolution when it lands, and it would reach
|
|
1850
|
+
* the same conclusion — so without this the document would ask to be rebuilt
|
|
1851
|
+
* out of the rebuild, forever. Cleared once the move it was rebuilt for has
|
|
1852
|
+
* gone through.
|
|
1853
|
+
*/
|
|
1854
|
+
private readonly format2RebuildingForDrop;
|
|
1855
|
+
/**
|
|
1856
|
+
* The last sync note per document, kept in memory for the same reason the
|
|
1857
|
+
* epoch mark is: `epoch.info` arrives before the ORM binds the document, so
|
|
1858
|
+
* there is no record store to write it into yet. It is written through the
|
|
1859
|
+
* first time one exists.
|
|
1860
|
+
*/
|
|
1861
|
+
private readonly format2SyncNotes;
|
|
1862
|
+
/**
|
|
1863
|
+
* #2816, behavior 10 — what a client owes the next epoch, and the mark it
|
|
1864
|
+
* moves once that epoch's overlay is installed.
|
|
1865
|
+
*/
|
|
1866
|
+
private jsBaoFormat2PendingOps;
|
|
1867
|
+
private jsBaoFormat2SetEpoch;
|
|
1868
|
+
private jsBaoFormat2RebindEpochDocument;
|
|
1869
|
+
/**
|
|
1870
|
+
* Folds one sealed epoch's archived overlay into the document's merged view
|
|
1871
|
+
* — the step a client behind by whole epochs repeats per archive to catch up
|
|
1872
|
+
* (#2816, behavior 12).
|
|
1873
|
+
*/
|
|
1874
|
+
private jsBaoFormat2ApplySealedOverlay;
|
|
1875
|
+
/**
|
|
1876
|
+
* Throws away a merged view whose sealed-overlay chain was refused, so a
|
|
1877
|
+
* base snapshot can be loaded over nothing rather than over the state of an
|
|
1878
|
+
* epoch the room has left behind (#2816, behavior 12).
|
|
1879
|
+
*/
|
|
1880
|
+
private jsBaoFormat2DiscardMergedView;
|
|
1881
|
+
/**
|
|
1882
|
+
* Folds the open epoch's overlay into the merged view again — the step that
|
|
1883
|
+
* finishes a cold load, because a base snapshot's rows REPLACE whatever the
|
|
1884
|
+
* open epoch had already contributed through the ordinary sync (#2816,
|
|
1885
|
+
* behavior 15).
|
|
1886
|
+
*/
|
|
1887
|
+
private jsBaoFormat2RefoldOverlay;
|
|
1888
|
+
/**
|
|
1889
|
+
* Materializes a base snapshot into the document's merged view — how a
|
|
1890
|
+
* client with no local state opens a document that has rotated (#2816,
|
|
1891
|
+
* behavior 15).
|
|
1892
|
+
*/
|
|
1893
|
+
private jsBaoFormat2LoadSnapshot;
|
|
1894
|
+
/**
|
|
1895
|
+
* Runs an epoch move with the document's local writes held back: a write
|
|
1896
|
+
* that lands on the sealed overlay after what is owed has been read off it
|
|
1897
|
+
* goes down with that document (#2816, behavior 10).
|
|
1898
|
+
*/
|
|
1899
|
+
private jsBaoFormat2HoldWrites;
|
|
1900
|
+
/** Documents currently moving to a new epoch, so a repeat notice waits. */
|
|
1901
|
+
private readonly format2Rotations;
|
|
1902
|
+
/**
|
|
1903
|
+
* The freshest artifact grants each large document has been given (#2816).
|
|
1904
|
+
*
|
|
1905
|
+
* A grant lives an hour and a base snapshot can take longer, so a load that
|
|
1906
|
+
* outlives its signatures asks for new ones and keeps going; this is where
|
|
1907
|
+
* the answer lands.
|
|
1908
|
+
*/
|
|
1909
|
+
private readonly format2Grants;
|
|
1910
|
+
/** Reads parked on a grant refresh, woken by the room's answer. */
|
|
1911
|
+
private readonly format2GrantWaiters;
|
|
1912
|
+
/**
|
|
1913
|
+
* The epoch each large document's connection reported. `epoch.info` arrives
|
|
1914
|
+
* during the handshake, before the ORM has built the record store that holds
|
|
1915
|
+
* the mark durably, so the transport remembers it until then.
|
|
1916
|
+
*/
|
|
1917
|
+
private readonly format2EpochMarks;
|
|
1918
|
+
/**
|
|
1919
|
+
* Large documents that learned they are behind before the ORM had bound
|
|
1920
|
+
* them: the catch-up needs the merged view, so it is owed here and runs at
|
|
1921
|
+
* the bind. They stay silent until it has (#2816, behavior 12).
|
|
1922
|
+
*/
|
|
1923
|
+
private readonly format2OwedCatchUps;
|
|
1924
|
+
/**
|
|
1925
|
+
* Large documents stopped on a superseded epoch: their overlay belongs to an
|
|
1926
|
+
* epoch the room has left behind, so nothing of their local state may be
|
|
1927
|
+
* sent. See `format2RequireReload`.
|
|
1928
|
+
*/
|
|
1929
|
+
private readonly format2StaleEpochs;
|
|
1930
|
+
/** Outbound sequences per large document: queued, sent, acknowledged. */
|
|
1931
|
+
private readonly format2Ack;
|
|
1709
1932
|
private jsBaoAddDocumentModelMapping;
|
|
1710
1933
|
private jsBaoRemoveDocumentModelMapping;
|
|
1711
1934
|
private readonly localUpdateOrigin;
|
|
@@ -1794,6 +2017,11 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
1794
2017
|
/** Sub-API for the named lock API (acquire/release/renew/status/list).
|
|
1795
2018
|
* @group Sub-APIs */
|
|
1796
2019
|
locks: LocksAPI;
|
|
2020
|
+
/**
|
|
2021
|
+
* Server functions, available as `client.functions`. Invokes code an app's
|
|
2022
|
+
* team authored and pushed with `primitive config push`.
|
|
2023
|
+
*/
|
|
2024
|
+
functions: FunctionsAPI;
|
|
1797
2025
|
/** Sub-API for reading and writing typed resource metadata (single + batch).
|
|
1798
2026
|
* @group Sub-APIs */
|
|
1799
2027
|
resourceMetadata: ResourceMetadataAPI;
|
|
@@ -2039,6 +2267,14 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
2039
2267
|
/** Get the global admin app ID.
|
|
2040
2268
|
* @group Configuration */
|
|
2041
2269
|
getGlobalAdminAppId(): string;
|
|
2270
|
+
/**
|
|
2271
|
+
* Build this client's js-bao instance.
|
|
2272
|
+
*
|
|
2273
|
+
* A seam, not an extension point: the constructor starts initialization
|
|
2274
|
+
* without awaiting it, so the destroy/init race has no other place a test
|
|
2275
|
+
* can hold the instance back from.
|
|
2276
|
+
*/
|
|
2277
|
+
protected createJsBaoInstance(options: Parameters<typeof initJsBao>[0]): Promise<Awaited<ReturnType<typeof initJsBao>>>;
|
|
2042
2278
|
private initializeJsBao;
|
|
2043
2279
|
private _syncKvUserId;
|
|
2044
2280
|
private getAuthPersistenceContext;
|
|
@@ -2327,6 +2563,332 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
2327
2563
|
* background refresh or an interactive sign-in takes.
|
|
2328
2564
|
*/
|
|
2329
2565
|
private handleWsAuthChallenge;
|
|
2566
|
+
/**
|
|
2567
|
+
* A large document's write is durable (#2816, behavior 9).
|
|
2568
|
+
*
|
|
2569
|
+
* The server sends this only after the update-log row and the inline
|
|
2570
|
+
* projection committed together, so this is the one event that may prune
|
|
2571
|
+
* `_pending_ops` — and the one that may report the document as having
|
|
2572
|
+
* nothing unsynced left.
|
|
2573
|
+
*/
|
|
2574
|
+
private handleUpdateAck;
|
|
2575
|
+
/**
|
|
2576
|
+
* Move a large document onto the epoch that replaced the sealed one
|
|
2577
|
+
* (#2816, behavior 10).
|
|
2578
|
+
*
|
|
2579
|
+
* Serialized per document: a seal is broadcast to every connection, and a
|
|
2580
|
+
* late frame can draw an `epoch.resync` on top of it, so the same move can
|
|
2581
|
+
* be asked for more than once. Running them concurrently would race two
|
|
2582
|
+
* swaps of the same document's overlay.
|
|
2583
|
+
*
|
|
2584
|
+
* A client that cannot follow — it missed whole epochs, so its merged view
|
|
2585
|
+
* is behind by sealed overlays it never applied — is STOPPED rather than
|
|
2586
|
+
* resynced: see `format2RequireReload`.
|
|
2587
|
+
*/
|
|
2588
|
+
private followEpochSeal;
|
|
2589
|
+
/**
|
|
2590
|
+
* The epoch a large document is on (#2816, behavior 10).
|
|
2591
|
+
*
|
|
2592
|
+
* The record store is the durable home of the mark, but it is built when the
|
|
2593
|
+
* ORM binds the document — after `epoch.info` has already been and gone. The
|
|
2594
|
+
* document's persisted metadata carries the same mark and IS loaded before
|
|
2595
|
+
* the handshake, so it answers in between; without it a restarted client
|
|
2596
|
+
* would read the connection's own report back as its current epoch and
|
|
2597
|
+
* conclude it was up to date whatever epoch its overlay really belongs to.
|
|
2598
|
+
* Whichever source answered is written through to the store the first time
|
|
2599
|
+
* one exists.
|
|
2600
|
+
*/
|
|
2601
|
+
/**
|
|
2602
|
+
* Check a large document's epoch mark against its records, before the open
|
|
2603
|
+
* believes either (#2816).
|
|
2604
|
+
*
|
|
2605
|
+
* The mark is in the client's document metadata; the document is in the
|
|
2606
|
+
* record database. Those are two stores, and they can part company — an
|
|
2607
|
+
* in-memory merged view that a reload emptied, a database file replaced or
|
|
2608
|
+
* evicted, a store the app rebuilt. When the record database says it follows
|
|
2609
|
+
* no epoch, the mark is a claim with nothing behind it: left in place, this
|
|
2610
|
+
* client opens an empty document, reports itself current, and plans its next
|
|
2611
|
+
* save as a create that replaces a record the server still holds. Dropped,
|
|
2612
|
+
* it is simply a client with nothing local, and rebuilds from a base.
|
|
2613
|
+
*
|
|
2614
|
+
* Returns what the record database said, for the local-copy decision:
|
|
2615
|
+
* `null` means it could not be asked, which is not evidence either way.
|
|
2616
|
+
*/
|
|
2617
|
+
private format2ReconcileEpochMark;
|
|
2618
|
+
private format2Epoch;
|
|
2619
|
+
/**
|
|
2620
|
+
* The epoch this client believes a document is on, for the BIND (#2816).
|
|
2621
|
+
*
|
|
2622
|
+
* The record store is created following epoch 0, and 0 is what "this
|
|
2623
|
+
* database holds no document" means to the open path — so the store has to
|
|
2624
|
+
* be told at the moment it is made, from what the client already knows: its
|
|
2625
|
+
* own durable mark, and the epoch a connection reported and this client
|
|
2626
|
+
* accepted (a client that is BEHIND notes nothing, so it cannot claim the
|
|
2627
|
+
* room's epoch here before the overlays it missed have landed).
|
|
2628
|
+
*
|
|
2629
|
+
* `undefined` when it knows of none, which leaves the store saying so.
|
|
2630
|
+
*/
|
|
2631
|
+
private format2KnownEpoch;
|
|
2632
|
+
/**
|
|
2633
|
+
* Catch a large document up on the sealed overlays it missed (#2816,
|
|
2634
|
+
* behavior 12).
|
|
2635
|
+
*
|
|
2636
|
+
* A client that was away while the room rotated is behind by whole epochs.
|
|
2637
|
+
* It does not need the document back: the sealed overlays between the epoch
|
|
2638
|
+
* it holds and the one the room is on ARE the changes it missed, so applying
|
|
2639
|
+
* them in order — each bounded by the rotation threshold — brings its merged
|
|
2640
|
+
* view to the server's `records` state with no base on the wire.
|
|
2641
|
+
*
|
|
2642
|
+
* Serialized with the epoch moves for the same document: a seal arriving
|
|
2643
|
+
* mid-catch-up would otherwise swap the overlay underneath it.
|
|
2644
|
+
*
|
|
2645
|
+
* Returns `ok` when the document is on the room's epoch afterwards. When it
|
|
2646
|
+
* is not, `refused` says whether the CHAIN was the problem — a hole, a
|
|
2647
|
+
* discontinuity, or an archive retention pruned — as opposed to a download
|
|
2648
|
+
* that merely failed. Only the first is worth rebuilding from a base for;
|
|
2649
|
+
* the second is worth retrying.
|
|
2650
|
+
*/
|
|
2651
|
+
private format2CatchUp;
|
|
2652
|
+
/**
|
|
2653
|
+
* Move this document onto `epoch` (#2816, behaviors 10/12).
|
|
2654
|
+
*
|
|
2655
|
+
* Installs a fresh overlay for that epoch carrying whatever is still
|
|
2656
|
+
* unacknowledged, and resyncs on it. Only ever called once the document's
|
|
2657
|
+
* merged view is the room's state again — after a chain has landed or a base
|
|
2658
|
+
* has been rebuilt — so the jump is no longer a jump over anything unknown.
|
|
2659
|
+
*/
|
|
2660
|
+
private format2MoveOnto;
|
|
2661
|
+
/**
|
|
2662
|
+
* Hold a returning client's replay until the epoch it joined has synced
|
|
2663
|
+
* (#2816, behavior 17).
|
|
2664
|
+
*
|
|
2665
|
+
* The sync itself is the trigger — `syncComplete` is the room saying this
|
|
2666
|
+
* document now holds the epoch — but a sync that never completes must not
|
|
2667
|
+
* strand what this client owes: the timer states the writes anyway, which is
|
|
2668
|
+
* the plain offline-wins fallback the spec keeps for evidence that cannot be
|
|
2669
|
+
* had. Either way the writes are durable in `_pending_ops` the whole time.
|
|
2670
|
+
*/
|
|
2671
|
+
private format2HoldReplay;
|
|
2672
|
+
/** Forget a held replay's timer, without running it. */
|
|
2673
|
+
private format2ClearHeldReplay;
|
|
2674
|
+
/**
|
|
2675
|
+
* State a returning client's owed writes on the epoch it joined, now that
|
|
2676
|
+
* the epoch's own content is here (#2816, behavior 17).
|
|
2677
|
+
*
|
|
2678
|
+
* The order is the behavior: what the open epoch holds is noted as conflict
|
|
2679
|
+
* evidence FIRST, so the resolution the replay runs sees the whole online
|
|
2680
|
+
* side — the sealed overlays the catch-up applied and the epoch it landed
|
|
2681
|
+
* on. Only then is the same dropped-delete question asked of it: a delete
|
|
2682
|
+
* this epoch's writes beat leaves a record whose other fields live only in a
|
|
2683
|
+
* base, so that document is rebuilt rather than settled on a row it cannot
|
|
2684
|
+
* state.
|
|
2685
|
+
*/
|
|
2686
|
+
private format2SettlePendingReplay;
|
|
2687
|
+
/**
|
|
2688
|
+
* The sealed-epoch chain `epoch.info` described, parsed once.
|
|
2689
|
+
*
|
|
2690
|
+
* The frame is untrusted input, so an entry without a whole epoch number is
|
|
2691
|
+
* dropped rather than turned into `NaN` — a hole in the chain is refused by
|
|
2692
|
+
* the planner, which is the safe outcome; a `NaN` link would not be.
|
|
2693
|
+
*/
|
|
2694
|
+
private format2SealedChain;
|
|
2695
|
+
/**
|
|
2696
|
+
* Open a large document the client has no local state for (#2816,
|
|
2697
|
+
* behavior 15).
|
|
2698
|
+
*
|
|
2699
|
+
* A fresh client is not behind — it holds no overlay at all — but on a
|
|
2700
|
+
* document that has rotated, the epoch it would join carries only the recent
|
|
2701
|
+
* changes. So it rebuilds a base first: the latest snapshot when the room
|
|
2702
|
+
* offered one, or the sealed overlays from the first epoch while no build
|
|
2703
|
+
* has completed yet. Either way the sealed overlays after that base are
|
|
2704
|
+
* applied on top, and the move onto the room's epoch resyncs the current
|
|
2705
|
+
* one — so the merged view ends up as the server's `records` table.
|
|
2706
|
+
*
|
|
2707
|
+
* Returns `ok: false` when the document cannot be rebuilt, which stops it
|
|
2708
|
+
* rather than opening a document with holes in it.
|
|
2709
|
+
*/
|
|
2710
|
+
private format2ColdStart;
|
|
2711
|
+
/**
|
|
2712
|
+
* Rebuild a large document whose sealed-overlay chain was refused (#2816,
|
|
2713
|
+
* behavior 12's close-out).
|
|
2714
|
+
*
|
|
2715
|
+
* A hole in the chain, an epoch declaring a discontinuous base, or an
|
|
2716
|
+
* archive retention has pruned all mean the same thing: nothing available
|
|
2717
|
+
* turns this client's rows into the room's current state. It is therefore in
|
|
2718
|
+
* the position of a client with no local state — so it plans as one, and
|
|
2719
|
+
* discards the view it cannot repair before loading the base over it. Before
|
|
2720
|
+
* a snapshot loader existed this was a dead stop; it is now a reload.
|
|
2721
|
+
*/
|
|
2722
|
+
private format2RebuildFromBase;
|
|
2723
|
+
/** How this document should be rebuilt, from the epoch it counts as holding. */
|
|
2724
|
+
private format2PlanRebuild;
|
|
2725
|
+
/** Run a rebuild plan against this document. */
|
|
2726
|
+
private format2RunRebuild;
|
|
2727
|
+
/**
|
|
2728
|
+
* Fold the open epoch's overlay over the base a cold load just installed
|
|
2729
|
+
* (#2816, behavior 15).
|
|
2730
|
+
*
|
|
2731
|
+
* The open epoch is the part of the document the rebuild does not carry: it
|
|
2732
|
+
* arrives as an ordinary Y.Doc sync, on no schedule relative to the load,
|
|
2733
|
+
* and every snapshot row REPLACES what the merged view held for its record.
|
|
2734
|
+
* So a record patched or deleted in the open epoch comes back as the base's
|
|
2735
|
+
* older row unless the overlay is folded once more at the end — which is
|
|
2736
|
+
* idempotent, an overlay entry being state rather than an operation.
|
|
2737
|
+
*/
|
|
2738
|
+
private format2RefoldOverlay;
|
|
2739
|
+
/**
|
|
2740
|
+
* Throw away a merged view a refused chain left beyond repair.
|
|
2741
|
+
*
|
|
2742
|
+
* The rows belong to an epoch the room has left behind, and the base about
|
|
2743
|
+
* to be loaded over them is a set of rows rather than a diff — every record
|
|
2744
|
+
* deleted since would otherwise survive the reload.
|
|
2745
|
+
*/
|
|
2746
|
+
private format2DiscardMergedView;
|
|
2747
|
+
/**
|
|
2748
|
+
* Materialize the base snapshot the handshake offered into this document's
|
|
2749
|
+
* merged view (#2816, behavior 15).
|
|
2750
|
+
*/
|
|
2751
|
+
private format2LoadSnapshot;
|
|
2752
|
+
/**
|
|
2753
|
+
* Read a snapshot build through the ONE grant the handshake minted for it.
|
|
2754
|
+
*
|
|
2755
|
+
* `{path}` is the manifest and `{path}/{model}/{n}` is a chunk of the same
|
|
2756
|
+
* build (behavior 14). The client never learns an R2 key: it knows a signed
|
|
2757
|
+
* path and, from the manifest, which chunks that build holds.
|
|
2758
|
+
*/
|
|
2759
|
+
private format2SnapshotSource;
|
|
2760
|
+
/**
|
|
2761
|
+
* Read one sealed epoch's archived overlay.
|
|
2762
|
+
*
|
|
2763
|
+
* The handshake hands out an origin-relative path carrying an expiring
|
|
2764
|
+
* signature for exactly that artifact (behavior 14) — the room is a Durable
|
|
2765
|
+
* Object and has no reliable idea which host this client reached it through,
|
|
2766
|
+
* while this client knows exactly. No key, and no other credential: the
|
|
2767
|
+
* signature IS the authorization.
|
|
2768
|
+
*/
|
|
2769
|
+
private format2FetchArchive;
|
|
2770
|
+
/**
|
|
2771
|
+
* Ask the room for fresh artifact grants, mid-load (#2816).
|
|
2772
|
+
*
|
|
2773
|
+
* The reply is deliberately not an `epoch.info`: that frame is the
|
|
2774
|
+
* handshake, and answering one would re-run the cold-start/catch-up decision
|
|
2775
|
+
* on a document that is in the middle of being rebuilt. This asks for
|
|
2776
|
+
* signatures and nothing else.
|
|
2777
|
+
*
|
|
2778
|
+
* Resolves with what arrived, or `null` when nothing did within the window —
|
|
2779
|
+
* the caller then repeats its read with the grant it already had, which
|
|
2780
|
+
* fails and is reported, rather than waiting on a socket that may be gone.
|
|
2781
|
+
*/
|
|
2782
|
+
private format2RefreshGrants;
|
|
2783
|
+
/**
|
|
2784
|
+
* Stop a large document whose overlay belongs to an epoch the room has left
|
|
2785
|
+
* behind (#2816, behavior 10).
|
|
2786
|
+
*
|
|
2787
|
+
* Everything this client could send from here is stale: its overlay holds
|
|
2788
|
+
* epoch-E state the room archived, folded into `records`, and has since
|
|
2789
|
+
* written past. A state-complete sync of it would not fill a gap — it would
|
|
2790
|
+
* overwrite newer authoritative values with older ones, and re-create rows
|
|
2791
|
+
* that were deleted epochs ago. So the document stops syncing and stops
|
|
2792
|
+
* pushing, keeps answering reads from its merged view, and says so with a
|
|
2793
|
+
* typed error. Applying the sealed overlays in between is the fast-forward
|
|
2794
|
+
* path (behavior 12); reloading from a snapshot is Phase 4. Until one of
|
|
2795
|
+
* those exists this is a hard stop, exactly as the plan specifies.
|
|
2796
|
+
*/
|
|
2797
|
+
private format2RequireReload;
|
|
2798
|
+
/**
|
|
2799
|
+
* Record that a large document is in touch with the server (#2816,
|
|
2800
|
+
* behavior 18).
|
|
2801
|
+
*
|
|
2802
|
+
* Kept in memory as well as written through, because `epoch.info` — where
|
|
2803
|
+
* the window is reported — arrives before the ORM binds the document and
|
|
2804
|
+
* therefore before there is a record store to persist it into. The note is
|
|
2805
|
+
* flushed at the bind, exactly as the epoch mark is.
|
|
2806
|
+
*/
|
|
2807
|
+
private format2NoteSync;
|
|
2808
|
+
/**
|
|
2809
|
+
* Wait for a large document's derived query tables to catch up with its
|
|
2810
|
+
* merged view (#2816).
|
|
2811
|
+
*
|
|
2812
|
+
* Only for format-2 documents: a legacy document has neither a merged view
|
|
2813
|
+
* nor a projection queue, and its update path is unchanged. A failure to
|
|
2814
|
+
* settle is the ORM's to report — it has already logged and withdrawn the
|
|
2815
|
+
* projection mark — and must not turn an applied update into an error here.
|
|
2816
|
+
*/
|
|
2817
|
+
private format2SettleProjection;
|
|
2818
|
+
/**
|
|
2819
|
+
* Record the offline window the room reported, without claiming a sync
|
|
2820
|
+
* (#2816, behavior 18).
|
|
2821
|
+
*
|
|
2822
|
+
* `epoch.info` carries the window to every client that reaches the room,
|
|
2823
|
+
* including one that is behind by whole epochs — and that client's merged
|
|
2824
|
+
* view is NOT the room's state until its catch-up has run. Taking the mark
|
|
2825
|
+
* here would hand a document past its window its writes back on the strength
|
|
2826
|
+
* of a handshake whose catch-up then failed: it would go on recording
|
|
2827
|
+
* durable pending ops against a past nothing can reconcile them with. So the
|
|
2828
|
+
* number is taken and the mark is left to be earned, by a catch-up that
|
|
2829
|
+
* lands or by a document that was current all along.
|
|
2830
|
+
*/
|
|
2831
|
+
private format2NoteWindow;
|
|
2832
|
+
/**
|
|
2833
|
+
* Measure this client's clock against the room's, and remember when each
|
|
2834
|
+
* sealed epoch ended (#2816, behavior 17).
|
|
2835
|
+
*
|
|
2836
|
+
* Both come off `epoch.info`, and both are needed later than it: the offset
|
|
2837
|
+
* stamps every local write, and the seal times bound the epochs a replay
|
|
2838
|
+
* compares an offline write with.
|
|
2839
|
+
*/
|
|
2840
|
+
private format2NoteClock;
|
|
2841
|
+
/**
|
|
2842
|
+
* Resolve what a returning client still owes against what was written while
|
|
2843
|
+
* it was away (#2816, behavior 17, amended D1).
|
|
2844
|
+
*
|
|
2845
|
+
* Used only where a ledger exists — a catch-up has just folded the sealed
|
|
2846
|
+
* overlays that ARE the online side of every conflict. A write the online
|
|
2847
|
+
* side clearly beat is dropped, forgotten from `_pending_ops` so no later
|
|
2848
|
+
* move carries it, and reported; one whose order cannot be established is
|
|
2849
|
+
* kept and reported. A client that was simply present through a seal has no
|
|
2850
|
+
* ledger and carries everything, exactly as before.
|
|
2851
|
+
*/
|
|
2852
|
+
private format2ResolveReplay;
|
|
2853
|
+
/**
|
|
2854
|
+
* Whether joining `epoch` would abandon an offline write this client cannot
|
|
2855
|
+
* take back (#2816, behavior 17).
|
|
2856
|
+
*
|
|
2857
|
+
* Asked BEFORE the move, and answered by running the same resolution the
|
|
2858
|
+
* move will run — it reads nothing and writes nothing, so asking twice costs
|
|
2859
|
+
* only the comparison. The one case is a dropped delete: the record it
|
|
2860
|
+
* removed is not in any artifact this client still holds, so the merged view
|
|
2861
|
+
* has to come from a base again before the delete may be forgotten.
|
|
2862
|
+
*
|
|
2863
|
+
* Answers false once a rebuild for this document is already under way, or
|
|
2864
|
+
* the rebuild would ask to be rebuilt out of itself.
|
|
2865
|
+
*/
|
|
2866
|
+
private format2ReplayRequiresRebuild;
|
|
2867
|
+
/**
|
|
2868
|
+
* Write a held note through, once the document's store exists.
|
|
2869
|
+
*
|
|
2870
|
+
* A note with no time is a window this client was told about while it was
|
|
2871
|
+
* still behind: the number lands, the mark does not.
|
|
2872
|
+
*/
|
|
2873
|
+
private format2FlushSyncNote;
|
|
2874
|
+
/**
|
|
2875
|
+
* Whether a large document is stopped on a superseded epoch, in which case
|
|
2876
|
+
* nothing of its local state may go out (see `format2RequireReload`).
|
|
2877
|
+
*/
|
|
2878
|
+
private format2NeedsReload;
|
|
2879
|
+
/**
|
|
2880
|
+
* Run the catch-up a document owes, now that its merged view exists
|
|
2881
|
+
* (#2816, behavior 12).
|
|
2882
|
+
*
|
|
2883
|
+
* Called at the ORM bind and whenever a connection reports a later epoch on
|
|
2884
|
+
* an already-bound document. A document whose chain is REFUSED — a hole, a
|
|
2885
|
+
* discontinuity, a pruned archive — is rebuilt from the latest base instead
|
|
2886
|
+
* of stopped; only a document that cannot be rebuilt either is stopped and
|
|
2887
|
+
* told to reload.
|
|
2888
|
+
*/
|
|
2889
|
+
private format2SettleOwedCatchUp;
|
|
2890
|
+
/** The pieces `followEpoch` moves, wired to this client's owners. */
|
|
2891
|
+
private format2RotationHost;
|
|
2330
2892
|
private handleWebSocketMessage;
|
|
2331
2893
|
private handleWebSocketClose;
|
|
2332
2894
|
private handleWebSocketError;
|
|
@@ -2995,6 +3557,20 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
2995
3557
|
* the wire on exactly the sessions where storage was slow or unavailable.
|
|
2996
3558
|
*/
|
|
2997
3559
|
private _primeLocalOnlyFromLocalMetadata;
|
|
3560
|
+
/**
|
|
3561
|
+
* The format of a document being opened (#2816).
|
|
3562
|
+
*
|
|
3563
|
+
* A large document keeps its records in a persisted merged view and uses its
|
|
3564
|
+
* Y.Doc only as an epoch overlay, so js-bao has to be told at connect time —
|
|
3565
|
+
* an app should not have to carry that. The format is chosen at creation and
|
|
3566
|
+
* never migrated, so a locally cached answer is permanently valid and the
|
|
3567
|
+
* server is only asked when nothing local knows (and the answer is then kept
|
|
3568
|
+
* for the next open, which may be offline).
|
|
3569
|
+
*
|
|
3570
|
+
* `resolveDocumentFormat` never rejects: a document nothing knows about
|
|
3571
|
+
* opens as legacy, exactly as it does today.
|
|
3572
|
+
*/
|
|
3573
|
+
private _resolveDocumentFormat;
|
|
2998
3574
|
private _putMetadataToIdb;
|
|
2999
3575
|
private _deleteMetadataFromIdb;
|
|
3000
3576
|
private _enforceRetentionPolicy;
|