js-bao-wss-client 2.2.0-alpha.12 → 2.2.0-alpha.13
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/README.md +32 -3
- package/dist/JsBaoClient.d.ts +342 -13
- package/dist/JsBaoClient.js +817 -48
- package/dist/browser.umd.js +2663 -362
- package/dist/internal/authController.d.ts +20 -1
- package/dist/internal/authController.js +24 -4
- package/dist/internal/documentManager.d.ts +442 -11
- package/dist/internal/documentManager.js +967 -136
- package/dist/internal/functionRunStatus.js +4 -1
- package/dist/internal/large-documents/persisted-naming.d.ts +45 -0
- package/dist/internal/large-documents/persisted-naming.js +70 -0
- package/dist/internal/offlineStore.d.ts +56 -0
- package/dist/internal/offlineStore.js +151 -18
- package/dist/internal/storage/indexedDbStorageProvider.d.ts +16 -0
- package/dist/internal/storage/indexedDbStorageProvider.js +71 -0
- package/dist/internal/storage/memoryStorageProvider.d.ts +9 -0
- package/dist/internal/storage/memoryStorageProvider.js +24 -0
- package/dist/internal/storage/sqliteStorageProvider.d.ts +16 -0
- package/dist/internal/storage/sqliteStorageProvider.js +59 -0
- package/dist/internal/storage/types.d.ts +64 -5
- package/dist/internal/y-indexeddb/src/y-indexeddb.d.ts +25 -2
- package/dist/internal/y-indexeddb/src/y-indexeddb.js +439 -80
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -123,9 +123,10 @@ const client = await initializeClient({
|
|
|
123
123
|
appId: "my-app",
|
|
124
124
|
models: [Task, Project],
|
|
125
125
|
|
|
126
|
-
// Yjs document persistence for Node.js
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
// Yjs document persistence for Node.js. The first argument is the store
|
|
127
|
+
// name — see "What the first argument is" below.
|
|
128
|
+
yjsPersistence: (storeName, ydoc, { appId, userId }) => {
|
|
129
|
+
return new SqlitePersistence(storeName, ydoc, {
|
|
129
130
|
dbPath: `${process.env.HOME}/.my-app/${appId}/${userId}/yjs.sqlite`
|
|
130
131
|
});
|
|
131
132
|
},
|
|
@@ -137,6 +138,34 @@ const { ydoc } = await client.documents.open("doc-123", {
|
|
|
137
138
|
});
|
|
138
139
|
```
|
|
139
140
|
|
|
141
|
+
**What the first argument is**
|
|
142
|
+
|
|
143
|
+
The factory's first argument is the **store name** to persist this Yjs state
|
|
144
|
+
under, not necessarily the document id. For an ordinary document, and for a
|
|
145
|
+
large document (`documentFormat: 2`) that has not rotated yet, the two are the
|
|
146
|
+
same value.
|
|
147
|
+
|
|
148
|
+
A large document's Yjs state is an epoch *overlay* rather than the document
|
|
149
|
+
itself, so once it rotates each epoch gets its own store name,
|
|
150
|
+
`{documentId}@{epoch}`. A rotation attaches a provider for the fresh epoch and
|
|
151
|
+
then clears the sealed one, so a factory that put both epochs under one name
|
|
152
|
+
would lose every write made since the rotation to that clear. Keying your
|
|
153
|
+
storage on the first argument — as the example above does — is correct for both
|
|
154
|
+
kinds of document, and needs no change when a document grows large.
|
|
155
|
+
|
|
156
|
+
The third argument carries the rest of what the store is for:
|
|
157
|
+
|
|
158
|
+
| Field | Meaning |
|
|
159
|
+
|-------|---------|
|
|
160
|
+
| `appId` | The application id |
|
|
161
|
+
| `userId` | The authenticated user, or `"anon"` |
|
|
162
|
+
| `documentId` | The plain document id, without the epoch suffix |
|
|
163
|
+
| `documentFormat` | `2` for a large document, `1` for an ordinary one — absent on the very first open of a document created elsewhere, whose format nothing local knows yet |
|
|
164
|
+
| `epoch` | The epoch the store name is scoped to, or `null` when the name is the plain document id |
|
|
165
|
+
|
|
166
|
+
Use `context.documentId` when you want to group every store belonging to one
|
|
167
|
+
document; use the first argument whenever you are naming storage.
|
|
168
|
+
|
|
140
169
|
**Why two storage systems?**
|
|
141
170
|
|
|
142
171
|
- `storageConfig` → General key-value data (auth, cache, metadata)
|
package/dist/JsBaoClient.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { type GoogleClientsConfig } from "./internal/authController";
|
|
|
14
14
|
import { type RequestOptions } from "./internal/httpClient";
|
|
15
15
|
import { type DocumentPermission as DocumentAccessLevel, type LocalDocumentEntry, type LocalMetadataEntry, type DocumentDebugSnapshot } from "./internal/documentManager";
|
|
16
16
|
import { type OfflineReplayNotice } from "js-bao";
|
|
17
|
+
import type { DocumentOfflineWindowError } from "js-bao";
|
|
17
18
|
import { DocumentsAPI, DocumentContext, type DocumentInfo, type ResolveAliasParams, type CreateDocumentOptions } from "./api/documentsApi";
|
|
18
19
|
import { type CacheFacade } from "./api/cacheFacade";
|
|
19
20
|
import { MeAPI } from "./api/meApi";
|
|
@@ -420,6 +421,15 @@ export interface ExecutePromptResult<R = unknown> {
|
|
|
420
421
|
* Absent when the provider reports none.
|
|
421
422
|
*/
|
|
422
423
|
reasoningTokens?: number;
|
|
424
|
+
/**
|
|
425
|
+
* What this call cost, in USD, as the provider reported it.
|
|
426
|
+
*
|
|
427
|
+
* A decisions model's case is largely economic — a fraction of a chat
|
|
428
|
+
* model's cost on the same workload — so a result that reported tokens
|
|
429
|
+
* and not price would leave the number the choice turns on off the wire.
|
|
430
|
+
* Absent when the provider reports none.
|
|
431
|
+
*/
|
|
432
|
+
cost?: number;
|
|
423
433
|
};
|
|
424
434
|
rawResponse: R;
|
|
425
435
|
configId: string;
|
|
@@ -519,6 +529,18 @@ export interface JsBaoClientOptions {
|
|
|
519
529
|
* - Browser: uses y-indexeddb (built-in)
|
|
520
530
|
* - Node.js: no Yjs persistence (documents only synced via server)
|
|
521
531
|
*
|
|
532
|
+
* The factory's first argument is the STORE NAME to persist this Yjs state
|
|
533
|
+
* under, not necessarily the document id. For an ordinary document, and for a
|
|
534
|
+
* large document (`documentFormat: 2`) that has not rotated yet, the two are
|
|
535
|
+
* the same. A large document's Yjs state is an epoch OVERLAY, so once it
|
|
536
|
+
* rotates each epoch gets its own name (`{documentId}@{epoch}`): a rotation
|
|
537
|
+
* attaches a provider for the fresh epoch and then clears the sealed one, so
|
|
538
|
+
* a factory that put both epochs under one name would lose the writes made
|
|
539
|
+
* since the rotation to that clear. Keying storage on the first argument, as
|
|
540
|
+
* the example below does, is therefore correct for both kinds of document;
|
|
541
|
+
* `context.documentId` is the plain document id when you want to group a
|
|
542
|
+
* document's stores.
|
|
543
|
+
*
|
|
522
544
|
* For Node.js persistence, use y-sqlite3:
|
|
523
545
|
* @example
|
|
524
546
|
* ```typescript
|
|
@@ -526,8 +548,8 @@ export interface JsBaoClientOptions {
|
|
|
526
548
|
*
|
|
527
549
|
* const client = new JsBaoClient({
|
|
528
550
|
* // ...
|
|
529
|
-
* yjsPersistence: (
|
|
530
|
-
* return new SqlitePersistence(
|
|
551
|
+
* yjsPersistence: (storeName, ydoc, { appId, userId }) => {
|
|
552
|
+
* return new SqlitePersistence(storeName, ydoc, {
|
|
531
553
|
* dbPath: `~/.my-app/${appId}/${userId}/yjs.sqlite`
|
|
532
554
|
* });
|
|
533
555
|
* }
|
|
@@ -615,6 +637,32 @@ export interface DocumentOfflineWritesResolvedEvent {
|
|
|
615
637
|
epoch: number;
|
|
616
638
|
notices: OfflineReplayNotice[];
|
|
617
639
|
}
|
|
640
|
+
/**
|
|
641
|
+
* Payload of the `document:write-refused` event
|
|
642
|
+
* (`client.on("document:write-refused", ...)`).
|
|
643
|
+
*
|
|
644
|
+
* Fired for EVERY local write a large document refuses because it is past its
|
|
645
|
+
* offline write window — the same event, by the same name, the Swift client
|
|
646
|
+
* emits. A call that can throw throws {@link error} as well, so the throw
|
|
647
|
+
* stays the call site's signal; this is the channel for handling the refusal
|
|
648
|
+
* once, in one place, on either client.
|
|
649
|
+
*
|
|
650
|
+
* Nothing is emitted for an accepted write, for an ordinary (format-1)
|
|
651
|
+
* document, or for a refusal of another kind.
|
|
652
|
+
*/
|
|
653
|
+
export interface DocumentWriteRefusedEvent {
|
|
654
|
+
documentId: string;
|
|
655
|
+
/** The model the refused write was on. */
|
|
656
|
+
model: string;
|
|
657
|
+
/** The record it was on. */
|
|
658
|
+
recordId: string;
|
|
659
|
+
/**
|
|
660
|
+
* The very error the call threw: `code` is
|
|
661
|
+
* `DOCUMENT_OFFLINE_WINDOW_EXPIRED`, and `details` carries `documentId`,
|
|
662
|
+
* `lastSyncAt`, `windowDays` and `overdueMs`.
|
|
663
|
+
*/
|
|
664
|
+
error: DocumentOfflineWindowError;
|
|
665
|
+
}
|
|
618
666
|
/**
|
|
619
667
|
* Options for `client.syncMetadata()`. Controls whether the sync covers all
|
|
620
668
|
* of the user's document metadata or a single document (`scope` /
|
|
@@ -1649,6 +1697,13 @@ export interface ListLocalDocumentsOptions {
|
|
|
1649
1697
|
export interface EvictAllLocalOptions {
|
|
1650
1698
|
/** If true, only evicts documents that are fully synced with the server. */
|
|
1651
1699
|
onlySynced?: boolean;
|
|
1700
|
+
/**
|
|
1701
|
+
* Whose local stores to remove, when it is not whoever is signed in.
|
|
1702
|
+
*
|
|
1703
|
+
* @internal The sign-out path captures the departing user before the token is
|
|
1704
|
+
* cleared and passes it here; nothing else needs to.
|
|
1705
|
+
*/
|
|
1706
|
+
storageUser?: string | null;
|
|
1652
1707
|
}
|
|
1653
1708
|
/**
|
|
1654
1709
|
* Map of every event the client emits to its payload type. This is the
|
|
@@ -1738,6 +1793,7 @@ export interface JsBaoEvents {
|
|
|
1738
1793
|
sync: SyncEvent;
|
|
1739
1794
|
awareness: AwarenessEvent;
|
|
1740
1795
|
"document:snapshot-load": DocumentSnapshotLoadEvent;
|
|
1796
|
+
"document:write-refused": DocumentWriteRefusedEvent;
|
|
1741
1797
|
"blobs:upload-progress": BlobUploadProgressEvent;
|
|
1742
1798
|
"blobs:upload-completed": BlobUploadCompletedEvent;
|
|
1743
1799
|
"blobs:upload-failed": BlobUploadFailedEvent;
|
|
@@ -2005,6 +2061,17 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
2005
2061
|
private jsBaoFormat2NoteDiscontinuity;
|
|
2006
2062
|
/** #3435 — the convergence landed: forget what it reconciled. */
|
|
2007
2063
|
private jsBaoFormat2ClearDiscontinuities;
|
|
2064
|
+
/**
|
|
2065
|
+
* #3755 — record durably that this client owes a deferred judgement.
|
|
2066
|
+
*
|
|
2067
|
+
* Written BEFORE the overlay swap and again inside the epoch mark's own
|
|
2068
|
+
* transaction: the client keeps two marks (the document metadata row and the
|
|
2069
|
+
* store's `_epoch`) and the bind raises the second to the first, so a crash
|
|
2070
|
+
* between them reproduces the window the note exists to close.
|
|
2071
|
+
*/
|
|
2072
|
+
private jsBaoFormat2NoteDeferredReplay;
|
|
2073
|
+
/** #3755 — the deferred judgement has been made: forget the note. */
|
|
2074
|
+
private jsBaoFormat2ClearDeferredReplay;
|
|
2008
2075
|
/**
|
|
2009
2076
|
* #3435 — the models this client holds rows of, which is the inventory a
|
|
2010
2077
|
* convergence plan decides its whole-model prunes against.
|
|
@@ -2088,12 +2155,33 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
2088
2155
|
* behavior 15).
|
|
2089
2156
|
*/
|
|
2090
2157
|
private jsBaoFormat2LoadSnapshot;
|
|
2158
|
+
/**
|
|
2159
|
+
* Remove one large document's local record store (#3754).
|
|
2160
|
+
*
|
|
2161
|
+
* The three doors an app removes local data through — `documents.evict`,
|
|
2162
|
+
* `closeDocument({ evictLocal: true })` and `logout({ wipeLocal: true })` —
|
|
2163
|
+
* cleared the Yjs overlay and the metadata row and nothing else, so a large
|
|
2164
|
+
* document's records, its unacknowledged writes and its projected query rows
|
|
2165
|
+
* stayed on disk and opened read-only from the same profile. This is what
|
|
2166
|
+
* removes them, and the Swift client has had its twin since #3436.
|
|
2167
|
+
*/
|
|
2168
|
+
private jsBaoFormat2Purge;
|
|
2169
|
+
/** The same for every large document of one scope except `keep` (#3754). */
|
|
2170
|
+
private jsBaoFormat2PurgeAll;
|
|
2091
2171
|
/**
|
|
2092
2172
|
* Runs an epoch move with the document's local writes held back: a write
|
|
2093
2173
|
* that lands on the sealed overlay after what is owed has been read off it
|
|
2094
2174
|
* goes down with that document (#2816, behavior 10).
|
|
2095
2175
|
*/
|
|
2096
2176
|
private jsBaoFormat2HoldWrites;
|
|
2177
|
+
/**
|
|
2178
|
+
* How to stop hearing this ORM instance's refused writes.
|
|
2179
|
+
*
|
|
2180
|
+
* Held so the subscription goes down with the instance it belongs to: an
|
|
2181
|
+
* ORM torn down and replaced would otherwise leave a listener re-emitting
|
|
2182
|
+
* on a client that has moved on.
|
|
2183
|
+
*/
|
|
2184
|
+
private jsBaoFormat2WriteRefusedUnsubscribe;
|
|
2097
2185
|
/**
|
|
2098
2186
|
* Documents whose writes this client is already holding.
|
|
2099
2187
|
*
|
|
@@ -2181,6 +2269,33 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
2181
2269
|
* the bind. They stay silent until it has (#2816, behavior 12).
|
|
2182
2270
|
*/
|
|
2183
2271
|
private readonly format2OwedCatchUps;
|
|
2272
|
+
/**
|
|
2273
|
+
* Documents whose last move was REFUSED because its deferred judgement
|
|
2274
|
+
* could not be recorded durably (#3755, finding 3755-R1).
|
|
2275
|
+
*
|
|
2276
|
+
* The debt stays owed and the document stays held behind the room, so the
|
|
2277
|
+
* existing owed-catch-up path retries the catch-up and the move on the next
|
|
2278
|
+
* `epoch.info` or `snapshot.ready`. Deliberately not a stop: nothing is
|
|
2279
|
+
* broken, one write did not land.
|
|
2280
|
+
*/
|
|
2281
|
+
private readonly format2DeferredNoteRefusals;
|
|
2282
|
+
/**
|
|
2283
|
+
* Documents whose deferred judgement is being planned right now (#3755).
|
|
2284
|
+
*
|
|
2285
|
+
* Both triggers — the bind and the document's `epoch.info` — can land while
|
|
2286
|
+
* the other's archive reads are in flight, and two resumes over one note
|
|
2287
|
+
* would judge the same writes twice.
|
|
2288
|
+
*/
|
|
2289
|
+
private readonly format2ResumingJudgements;
|
|
2290
|
+
/**
|
|
2291
|
+
* Test-only (#3755): hold a deferred judgement instead of settling it.
|
|
2292
|
+
*
|
|
2293
|
+
* `null` in every build an application runs, and read in exactly one place
|
|
2294
|
+
* ({@link format2SettlePendingReplay}). Not on the package's public surface:
|
|
2295
|
+
* an integrated test reaches it the way it reaches the other private fields
|
|
2296
|
+
* it drives, and nothing an app can call installs it.
|
|
2297
|
+
*/
|
|
2298
|
+
private format2ReplaySettleGate;
|
|
2184
2299
|
/**
|
|
2185
2300
|
* Large documents stopped on a superseded epoch: their overlay belongs to an
|
|
2186
2301
|
* epoch the room has left behind, so nothing of their local state may be
|
|
@@ -2829,6 +2944,21 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
2829
2944
|
private getServiceWorkerApiBase;
|
|
2830
2945
|
private handleServiceWorkerAuthChange;
|
|
2831
2946
|
private emitAuthSuccess;
|
|
2947
|
+
/**
|
|
2948
|
+
* What a large document's browser store is named by, for one NAMED user
|
|
2949
|
+
* (#3754).
|
|
2950
|
+
*
|
|
2951
|
+
* The same three parts every open uses. Taking the user as an argument is the
|
|
2952
|
+
* whole point: at logout the token has already gone, so anything that asked
|
|
2953
|
+
* "who is signed in?" would answer `anon` and name stores this user never
|
|
2954
|
+
* wrote (finding R1).
|
|
2955
|
+
*/
|
|
2956
|
+
private largeDocumentScopeFor;
|
|
2957
|
+
/**
|
|
2958
|
+
* The purge hook as an eviction context wants it: the scope filled in from
|
|
2959
|
+
* the user the caller captured (#3754).
|
|
2960
|
+
*/
|
|
2961
|
+
private largeDocumentPurgeContext;
|
|
2832
2962
|
private getCurrentUserIdForStorage;
|
|
2833
2963
|
private resolveAnalyticsUserUlid;
|
|
2834
2964
|
private resolveAnalyticsPlan;
|
|
@@ -3209,6 +3339,30 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
3209
3339
|
* next frame on THIS connection reads, before any store has been bound.
|
|
3210
3340
|
*/
|
|
3211
3341
|
private format2NoteDiscontinuity;
|
|
3342
|
+
/**
|
|
3343
|
+
* Record durably that this document owes a deferred judgement (#3755).
|
|
3344
|
+
*
|
|
3345
|
+
* Written BEFORE the overlay swap, and again inside the epoch mark's own
|
|
3346
|
+
* transaction: the client keeps TWO epoch marks — the document metadata row
|
|
3347
|
+
* (moved by `swapEpochDocument`) and the store's `_epoch` (moved after it) —
|
|
3348
|
+
* and the bind raises the second to the first. A crash between them would
|
|
3349
|
+
* otherwise leave a store whose mark reads current with nothing saying a
|
|
3350
|
+
* judgement is owed, which is the very defect this note closes. One row, one
|
|
3351
|
+
* idempotent upsert, whichever of the two lands.
|
|
3352
|
+
*
|
|
3353
|
+
* REJECTS on failure rather than logging and carrying on (finding 3755-R1):
|
|
3354
|
+
* the caller refuses the move, so nothing advances without the note.
|
|
3355
|
+
*/
|
|
3356
|
+
private format2NoteDeferredReplay;
|
|
3357
|
+
/**
|
|
3358
|
+
* The deferred judgement has been made: forget the note (#3755).
|
|
3359
|
+
*
|
|
3360
|
+
* Awaited before the withhold is released, so a flush that fires during the
|
|
3361
|
+
* clear still finds the covered sequences held back. A failure leaves the
|
|
3362
|
+
* note standing — the survivors are stated and safe; only their claim waits
|
|
3363
|
+
* for the next bind to read the note again.
|
|
3364
|
+
*/
|
|
3365
|
+
private format2ClearDeferredReplay;
|
|
3212
3366
|
/**
|
|
3213
3367
|
* Run `run` with this document's local writes held back, once.
|
|
3214
3368
|
*
|
|
@@ -3229,6 +3383,23 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
3229
3383
|
* prevent.
|
|
3230
3384
|
*/
|
|
3231
3385
|
private format2WithholdSequences;
|
|
3386
|
+
/**
|
|
3387
|
+
* Hold back every sequence a DURABLE deferred judgement still covers
|
|
3388
|
+
* (#3755, finding 3755-R3).
|
|
3389
|
+
*
|
|
3390
|
+
* Applied by every caller of `claimCatchUp` before it claims, because there
|
|
3391
|
+
* is no barrier between the ORM's adoption and the post-bind hook:
|
|
3392
|
+
* `handleSyncStep1` reads these very marks and claims straight off them, and
|
|
3393
|
+
* a handshake landing in that window would otherwise claim sequences whose
|
|
3394
|
+
* content is on no frame. One synchronous read of the mirror the adoption
|
|
3395
|
+
* refreshed, on a path the client already takes.
|
|
3396
|
+
*
|
|
3397
|
+
* The floor is the lowest UNACKNOWLEDGED sequence, not the note's own span:
|
|
3398
|
+
* the server's mark is contiguous, so nothing above a covered sequence can be
|
|
3399
|
+
* acknowledged anyway, and claiming it would only invite the prune this
|
|
3400
|
+
* exists to prevent.
|
|
3401
|
+
*/
|
|
3402
|
+
private format2ApplyDurableWithhold;
|
|
3232
3403
|
/** The withheld writes have been replayed: their sequences may travel. */
|
|
3233
3404
|
private format2ReleaseWithheldSequences;
|
|
3234
3405
|
/** The highest bulk-load epoch this document has not converged past (#3435). */
|
|
@@ -3396,6 +3567,107 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
3396
3567
|
* about and classified by presence alone (finding 3435-R07).
|
|
3397
3568
|
*/
|
|
3398
3569
|
private format2SettleConvergenceReplay;
|
|
3570
|
+
/**
|
|
3571
|
+
* Resume a judgement a restart interrupted (#3755, C4).
|
|
3572
|
+
*
|
|
3573
|
+
* The debt is durable and this is where it is paid. Runs when BOTH halves
|
|
3574
|
+
* are in — the store is bound (so the note and the owed writes are readable)
|
|
3575
|
+
* and the document's `epoch.info` has arrived (so the chain and the seal
|
|
3576
|
+
* times are) — whichever comes second.
|
|
3577
|
+
*
|
|
3578
|
+
* The evidence is rebuilt into the DOCUMENT's own conflict ledger, never a
|
|
3579
|
+
* replacement (finding 3755-R2): `format2NoteClock` has already seeded it
|
|
3580
|
+
* with the handshake's seal times, and a fresh ledger without them would
|
|
3581
|
+
* make a provably outdated write merely ambiguous and replay it. Each
|
|
3582
|
+
* archive of `[from_epoch, to_epoch)` is NOTED, never folded — the merged
|
|
3583
|
+
* view already holds those epochs; what is missing is the record of what
|
|
3584
|
+
* they touched.
|
|
3585
|
+
*
|
|
3586
|
+
* The judgement itself is then held exactly as an in-session deferral is, so
|
|
3587
|
+
* it runs on the joined epoch's `syncComplete` (with the open epoch's content
|
|
3588
|
+
* noted first and the dropped-delete question asked) or on the same 15 s
|
|
3589
|
+
* fallback.
|
|
3590
|
+
*/
|
|
3591
|
+
private format2MaybeResumeDeferredJudgement;
|
|
3592
|
+
/**
|
|
3593
|
+
* Pay a durable judgement NOW, from the recorded mutations (#3755, findings
|
|
3594
|
+
* 3755-C01 and 3755-C03).
|
|
3595
|
+
*
|
|
3596
|
+
* A note stands exactly where the writes it covers are on no overlay: a
|
|
3597
|
+
* deferral installed a fresh one without them, or a bind kept them off it on
|
|
3598
|
+
* purpose. So no carry read off an overlay can ever settle this debt —
|
|
3599
|
+
* `carryUnackedOverlay` finds nothing to carry and the note would be cleared
|
|
3600
|
+
* over writes that reached no frame. It is settled here instead, from the
|
|
3601
|
+
* durable log, and closed in the order an uninterrupted judgement closes it.
|
|
3602
|
+
*
|
|
3603
|
+
* Unlike {@link format2MaybeResumeDeferredJudgement} this does not wait for
|
|
3604
|
+
* the joined epoch to sync: every caller is already at the point where that
|
|
3605
|
+
* epoch's content is in (a judgement that has just run, or a rebuild that
|
|
3606
|
+
* has just landed its base).
|
|
3607
|
+
*
|
|
3608
|
+
* Answers whether there was a debt; a caller told `false` still owes its own
|
|
3609
|
+
* close-out.
|
|
3610
|
+
*/
|
|
3611
|
+
private format2SettleDurableDebt;
|
|
3612
|
+
/**
|
|
3613
|
+
* The durable debt this document is standing under, if any (#3755).
|
|
3614
|
+
*
|
|
3615
|
+
* The same reading the bind makes, off the same marks: a note is honoured
|
|
3616
|
+
* only where it still names writes this store has not had acknowledged.
|
|
3617
|
+
*/
|
|
3618
|
+
private format2DurableDebtNote;
|
|
3619
|
+
/**
|
|
3620
|
+
* The writes a resumed judgement is about (#3755).
|
|
3621
|
+
*
|
|
3622
|
+
* This client's ops inside the note's span, MINUS the ones a bulk load's
|
|
3623
|
+
* convergence owns: the discontinuity path judges everything at or below its
|
|
3624
|
+
* boundary (#3435, finding FROZEN-01), so the two sets are disjoint and no
|
|
3625
|
+
* write is judged twice (edge E4).
|
|
3626
|
+
*/
|
|
3627
|
+
private format2OwedForJudgement;
|
|
3628
|
+
/**
|
|
3629
|
+
* Note the archives of `[from_epoch, to_epoch)` as conflict evidence, without
|
|
3630
|
+
* folding any of them (#3755, behavior 13).
|
|
3631
|
+
*
|
|
3632
|
+
* The merged view already holds those epochs — the interrupted session
|
|
3633
|
+
* applied them — so folding would re-apply state the document has. What was
|
|
3634
|
+
* lost is the RECORD of which records and fields each of them touched, which
|
|
3635
|
+
* is what a recency judgement is made of. An archive a concurrent catch-up
|
|
3636
|
+
* has already noted is not fetched again.
|
|
3637
|
+
*/
|
|
3638
|
+
private format2NoteResumedChain;
|
|
3639
|
+
/** The document's conflict ledger, created only if it has none. */
|
|
3640
|
+
private format2EnsureConflictLedger;
|
|
3641
|
+
/**
|
|
3642
|
+
* Judge a resumed deferral and state what survives (#3755, C4 steps 1-3).
|
|
3643
|
+
*
|
|
3644
|
+
* The same three things an uninterrupted judgement does, in the same order,
|
|
3645
|
+
* which is why the verdict an app receives is the same either way.
|
|
3646
|
+
*/
|
|
3647
|
+
private format2RunResumedJudgement;
|
|
3648
|
+
/**
|
|
3649
|
+
* Steps 1 and 2 of a resumed judgement: resolve, and state what survives.
|
|
3650
|
+
*
|
|
3651
|
+
* Apart from the close-out, so a caller that has more to do before the note
|
|
3652
|
+
* may be cleared can take the same two steps (#3755, finding 3755-C01).
|
|
3653
|
+
*/
|
|
3654
|
+
private format2StateResumedJudgement;
|
|
3655
|
+
/**
|
|
3656
|
+
* Close out a judgement, in the order that keeps a flush honest (#3755,
|
|
3657
|
+
* finding 3755-R5).
|
|
3658
|
+
*
|
|
3659
|
+
* Clear the note durably, THEN release the withhold, THEN send the
|
|
3660
|
+
* state-complete claim. A flush that fires during the awaited clear still
|
|
3661
|
+
* finds the covered sequences held back, so it carries the survivors and
|
|
3662
|
+
* claims nothing — and the claim that follows covers them.
|
|
3663
|
+
*
|
|
3664
|
+
* The claim is not conditional on anything having been dropped: the
|
|
3665
|
+
* survivors are STATED from their recorded mutations rather than committed,
|
|
3666
|
+
* so no sequence is noted for them and an ordinary frame's claim starts at
|
|
3667
|
+
* the session floor. Only a state-complete frame may claim what this session
|
|
3668
|
+
* did not write, and this is that frame.
|
|
3669
|
+
*/
|
|
3670
|
+
private format2FinishDeferredJudgement;
|
|
3399
3671
|
/**
|
|
3400
3672
|
* The owed writes a RESTART left for the convergence to judge (#3435,
|
|
3401
3673
|
* finding FROZEN-01).
|
|
@@ -4478,26 +4750,39 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
4478
4750
|
private metadataHydratedFromIdb;
|
|
4479
4751
|
private _loadAllMetadataFromIdb;
|
|
4480
4752
|
/**
|
|
4481
|
-
* Seed the
|
|
4753
|
+
* Seed what the open path must know about a document BEFORE it acts on it,
|
|
4754
|
+
* from that document's persisted metadata row.
|
|
4482
4755
|
*
|
|
4483
|
-
*
|
|
4484
|
-
* `localOnly: true` in an earlier session, and nothing on the open path
|
|
4485
|
-
* reads local storage first — the in-memory metadata index is only ever
|
|
4486
|
-
* filled by a listing call, which an app that stores document ids need never
|
|
4487
|
-
* make. Without this, the document opens looking ordinary and its first edit
|
|
4488
|
-
* goes out as an `update` frame (#2691).
|
|
4756
|
+
* Two facts, one keyed read:
|
|
4489
4757
|
*
|
|
4490
|
-
*
|
|
4491
|
-
*
|
|
4492
|
-
*
|
|
4758
|
+
* - Local-only-ness (#2691). `openDocument` can be handed the id of a
|
|
4759
|
+
* document created with `localOnly: true` in an earlier session, and
|
|
4760
|
+
* nothing else on the open path reads local storage first — the in-memory
|
|
4761
|
+
* metadata index is only ever filled by a listing call, which an app that
|
|
4762
|
+
* stores document ids need never make. Without this the document opens
|
|
4763
|
+
* looking ordinary and its first edit goes out as an `update` frame.
|
|
4764
|
+
* - The naming fields (#3669). A large document's local overlay is stored
|
|
4765
|
+
* under `documentId@format2Epoch`, and `format2Epoch` lives in that row.
|
|
4766
|
+
* A by-id open in an un-hydrated session would otherwise attach the plain
|
|
4767
|
+
* name — an empty overlay — and the whole-row metadata write that follows
|
|
4768
|
+
* would erase the fields that said which store was the real one.
|
|
4493
4769
|
*
|
|
4494
4770
|
* "Hydrated" means a read that actually reached the store
|
|
4495
4771
|
* (`metadataHydratedFromIdb`), not one that was merely attempted: the bulk
|
|
4496
4772
|
* read gives up after 2s and swallows its own failures, and taking that
|
|
4497
4773
|
* empty result as an answer would put a stored local-only document back on
|
|
4498
4774
|
* the wire on exactly the sessions where storage was slow or unavailable.
|
|
4775
|
+
* The naming half is read even on a hydrated session when the cache says
|
|
4776
|
+
* this is a large document, because another tab may have rotated since the
|
|
4777
|
+
* hydration — one keyed get per open of a large document, and nothing at all
|
|
4778
|
+
* for an ordinary one.
|
|
4779
|
+
*
|
|
4780
|
+
* @returns whether the read produced the durable ROW. There is no definite
|
|
4781
|
+
* null to be had below this call — every layer answers a failure by
|
|
4782
|
+
* resolving `null` — so an absent row is read as a naming that is not
|
|
4783
|
+
* durable, and no store is reclaimed on one.
|
|
4499
4784
|
*/
|
|
4500
|
-
private
|
|
4785
|
+
private _primeFromLocalMetadata;
|
|
4501
4786
|
/**
|
|
4502
4787
|
* The format of a document being opened (#2816).
|
|
4503
4788
|
*
|
|
@@ -4512,7 +4797,30 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
4512
4797
|
* opens as legacy, exactly as it does today.
|
|
4513
4798
|
*/
|
|
4514
4799
|
private _resolveDocumentFormat;
|
|
4800
|
+
/**
|
|
4801
|
+
* The durable naming read the manager uses before it chooses a store name
|
|
4802
|
+
* (#3745), or `undefined` when there is nothing a read could add.
|
|
4803
|
+
*
|
|
4804
|
+
* Only worth a keyed read when the in-memory index is NOT already the whole
|
|
4805
|
+
* picture: a hydration that reached the store answered for every document in
|
|
4806
|
+
* it, so an ordinary open must not pay a second read. The same gate
|
|
4807
|
+
* `_resolveDocumentFormat` applies to its own persisted source.
|
|
4808
|
+
*
|
|
4809
|
+
* A LARGE document is the one exception, and it is why this takes the
|
|
4810
|
+
* document rather than answering for the session (#3669): another tab can
|
|
4811
|
+
* rotate it after the hydration, so the epoch the index holds for it can be
|
|
4812
|
+
* behind the row on disk, and the sweep on open re-reads that row under the
|
|
4813
|
+
* epoch lock. An ordinary document on a hydrated session still pays nothing.
|
|
4814
|
+
*/
|
|
4815
|
+
private _persistedNamingReader;
|
|
4515
4816
|
private _putMetadataToIdb;
|
|
4817
|
+
/**
|
|
4818
|
+
* Persist a DELTA of one document's metadata row (#3746) — the fields named
|
|
4819
|
+
* and nothing else, merged into whatever is stored. What the document-open
|
|
4820
|
+
* path writes with, so opening one document in one tab cannot regress a field
|
|
4821
|
+
* another tab's rotation or a listing wrote.
|
|
4822
|
+
*/
|
|
4823
|
+
private _mergeMetadataToIdb;
|
|
4516
4824
|
private _deleteMetadataFromIdb;
|
|
4517
4825
|
private _enforceRetentionPolicy;
|
|
4518
4826
|
/** Get the current network status including connectivity and transport state.
|
|
@@ -4635,7 +4943,28 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
4635
4943
|
evictLocalDocument(documentId: string, opts?: {
|
|
4636
4944
|
force?: boolean;
|
|
4637
4945
|
suppressMetadataEvent?: boolean;
|
|
4946
|
+
/**
|
|
4947
|
+
* Whose local stores this is about. Defaults to the signed-in user; the
|
|
4948
|
+
* sign-out path names the user who is signing OUT, because by then
|
|
4949
|
+
* nobody is.
|
|
4950
|
+
*
|
|
4951
|
+
* @internal
|
|
4952
|
+
*/
|
|
4953
|
+
storageUser?: string | null;
|
|
4638
4954
|
}): Promise<void>;
|
|
4955
|
+
/**
|
|
4956
|
+
* Whose stores an eviction is about, decided NOW (#3754).
|
|
4957
|
+
*
|
|
4958
|
+
* The sign-out path names the user explicitly, because by the time it evicts
|
|
4959
|
+
* the token is gone and nobody is signed in (finding R1). Every other caller
|
|
4960
|
+
* names nobody — and "nobody" used to mean "whoever is signed in when the
|
|
4961
|
+
* purge eventually runs", which is a different user for the one eviction that
|
|
4962
|
+
* does not run at once: evicting a document that is OPEN defers its purge to
|
|
4963
|
+
* the close, and a sign-out or a sign-in as somebody else can happen in
|
|
4964
|
+
* between. Resolved at the door, the scope the close purges under is the one
|
|
4965
|
+
* the eviction was asked for (criterion 5, finding 3754-C01).
|
|
4966
|
+
*/
|
|
4967
|
+
private capturedStorageUser;
|
|
4639
4968
|
/** Remove all locally stored document data.
|
|
4640
4969
|
* @param opts - Optional eviction behavior
|
|
4641
4970
|
* @group Offline & Sync */
|