@abloatai/ablo 0.29.2 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +3 -2
- package/dist/BaseSyncedStore.js +3 -1
- package/dist/Database.js +29 -2
- package/dist/SyncClient.d.ts +5 -3
- package/dist/SyncClient.js +8 -4
- package/dist/ai-sdk/coordinatedTool.d.ts +0 -1
- package/dist/ai-sdk/coordinatedTool.js +0 -1
- package/dist/ai-sdk/coordinationContext.js +1 -5
- package/dist/cli.cjs +561 -90
- package/dist/client/Ablo.d.ts +1 -1
- package/dist/client/Ablo.js +24 -36
- package/dist/client/auth.d.ts +8 -18
- package/dist/client/auth.js +16 -96
- package/dist/client/createModelProxy.d.ts +16 -11
- package/dist/client/createModelProxy.js +162 -20
- package/dist/client/httpClient.d.ts +1 -1
- package/dist/client/httpTransport.js +263 -89
- package/dist/client/options.d.ts +0 -15
- package/dist/client/resourceTypes.d.ts +13 -26
- package/dist/client/schemaConfig.js +5 -0
- package/dist/client/writeOptionsSchema.d.ts +1 -0
- package/dist/client/writeOptionsSchema.js +11 -2
- package/dist/client/wsMutationExecutor.d.ts +3 -5
- package/dist/coordination/schema.d.ts +17 -13
- package/dist/coordination/schema.js +36 -10
- package/dist/errorCodes.d.ts +9 -4
- package/dist/errorCodes.js +10 -5
- package/dist/errors.js +4 -9
- package/dist/index.d.ts +4 -0
- package/dist/interfaces/index.d.ts +31 -19
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.js +4 -1
- package/dist/schema/schema.d.ts +13 -0
- package/dist/schema/select.js +7 -0
- package/dist/schema/syncDeltaRow.d.ts +31 -13
- package/dist/schema/syncDeltaRow.js +33 -15
- package/dist/schema/tenancy.d.ts +52 -16
- package/dist/schema/tenancy.js +86 -25
- package/dist/server/commit.d.ts +8 -24
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/readConfig.d.ts +10 -0
- package/dist/source/adapter.d.ts +26 -15
- package/dist/source/adapter.js +5 -3
- package/dist/source/adapters/drizzle.js +35 -9
- package/dist/source/adapters/kysely.d.ts +33 -61
- package/dist/source/adapters/kysely.js +170 -177
- package/dist/source/adapters/kyselyMutationCore.d.ts +76 -0
- package/dist/source/adapters/kyselyMutationCore.js +125 -0
- package/dist/source/adapters/memory.d.ts +1 -1
- package/dist/source/adapters/memory.js +24 -10
- package/dist/source/adapters/prisma.js +35 -8
- package/dist/source/conformance.d.ts +26 -28
- package/dist/source/conformance.js +142 -69
- package/dist/source/contract.d.ts +46 -4
- package/dist/source/contract.js +66 -5
- package/dist/source/factory.d.ts +3 -3
- package/dist/source/factory.js +24 -3
- package/dist/source/idempotency.d.ts +46 -0
- package/dist/source/idempotency.js +129 -0
- package/dist/source/index.d.ts +6 -4
- package/dist/source/index.js +5 -3
- package/dist/source/migrations.d.ts +9 -5
- package/dist/source/migrations.js +39 -6
- package/dist/source/types.d.ts +88 -14
- package/dist/source/types.js +16 -0
- package/dist/surface.d.ts +1 -1
- package/dist/surface.js +0 -1
- package/dist/sync/BootstrapFetcher.js +7 -0
- package/dist/sync/SyncWebSocket.d.ts +5 -27
- package/dist/sync/awaitClaimGrant.d.ts +6 -0
- package/dist/sync/awaitClaimGrant.js +12 -2
- package/dist/sync/commitFrames.d.ts +2 -4
- package/dist/sync/commitFrames.js +1 -0
- package/dist/sync/createClaimStream.d.ts +1 -1
- package/dist/sync/createClaimStream.js +13 -15
- package/dist/sync/deltaPipeline.d.ts +1 -1
- package/dist/sync/deltaPipeline.js +1 -1
- package/dist/sync/participants.d.ts +2 -3
- package/dist/sync/participants.js +2 -2
- package/dist/sync/wsFrameHandlers.js +35 -15
- package/dist/testing/mocks/MockMutationExecutor.d.ts +21 -0
- package/dist/testing/mocks/MockMutationExecutor.js +53 -1
- package/dist/transactions/TransactionQueue.d.ts +61 -9
- package/dist/transactions/TransactionQueue.js +502 -114
- package/dist/transactions/commitEnvelope.d.ts +4 -0
- package/dist/transactions/commitEnvelope.js +25 -0
- package/dist/transactions/commitPayload.d.ts +23 -5
- package/dist/transactions/commitPayload.js +3 -0
- package/dist/transactions/deltaConfirmation.d.ts +2 -1
- package/dist/transactions/deltaConfirmation.js +22 -4
- package/dist/transactions/durableWriteStore.d.ts +8 -1
- package/dist/transactions/httpCommitEnvelope.d.ts +3 -1
- package/dist/transactions/httpCommitEnvelope.js +30 -0
- package/dist/transactions/replayValidation.d.ts +4 -0
- package/dist/transactions/replayValidation.js +6 -0
- package/dist/types/streams.d.ts +29 -17
- package/dist/wire/commit.d.ts +671 -0
- package/dist/wire/commit.js +300 -0
- package/dist/wire/delta.d.ts +4 -0
- package/dist/wire/delta.js +9 -0
- package/dist/wire/frames.d.ts +17 -45
- package/dist/wire/index.d.ts +2 -0
- package/dist/wire/index.js +4 -0
- package/docs/agent-messaging.md +4 -5
- package/docs/agents.md +1 -2
- package/docs/api-keys.md +1 -2
- package/docs/client-behavior.md +4 -6
- package/docs/coordination.md +64 -9
- package/docs/data-sources.md +4 -7
- package/docs/integration-guide.md +6 -8
- package/docs/migration.md +0 -3
- package/docs/quickstart.md +7 -8
- package/docs/schema-contract.md +7 -8
- package/package.json +4 -4
- package/dist/client/registerDataSource.d.ts +0 -19
- package/dist/client/registerDataSource.js +0 -66
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { autorun } from 'mobx';
|
|
14
14
|
import { AbloClaimedError, AbloStaleContextError, AbloValidationError, formatClaimedErrorMessage, toAbloError, } from '../errors.js';
|
|
15
|
-
import { descriptionFromMeta } from '../coordination/schema.js';
|
|
16
15
|
import { reconcileFunctionalUpdate, } from './functionalUpdate.js';
|
|
17
16
|
import { Model, modelAsRow } from '../Model.js';
|
|
18
17
|
import { toMs } from '../utils/duration.js';
|
|
@@ -77,7 +76,7 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
77
76
|
// `claim({ id })` took, without a per-call handle. Released on dispose,
|
|
78
77
|
// explicit release, or TTL expiry.
|
|
79
78
|
//
|
|
80
|
-
// `target`, `
|
|
79
|
+
// `target`, `description`, and `expiresAt` are kept alongside the lease so
|
|
81
80
|
// `claim.state` can synthesize a self-claim: the server excludes a holder's
|
|
82
81
|
// own presence frames, so this proxy is the only place that knows the client
|
|
83
82
|
// holds the row. `expiresAt` is the client's best estimate from the requested
|
|
@@ -94,19 +93,13 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
94
93
|
value.object === 'claim' &&
|
|
95
94
|
typeof value.id === 'string' &&
|
|
96
95
|
typeof value.release === 'function';
|
|
97
|
-
const claimMeta = (options) =>
|
|
98
|
-
if (!options?.description)
|
|
99
|
-
return options?.meta;
|
|
100
|
-
return { ...(options.meta ?? {}), description: options.description };
|
|
101
|
-
};
|
|
96
|
+
const claimMeta = (options) => options?.meta;
|
|
102
97
|
const claimContextFromClaim = (claim) => {
|
|
103
|
-
const description = claim.description ?? descriptionFromMeta(claim.target.meta);
|
|
104
98
|
return {
|
|
105
99
|
id: claim.id,
|
|
106
100
|
actor: claim.heldBy,
|
|
107
101
|
participantKind: claim.participantKind,
|
|
108
|
-
|
|
109
|
-
...(description ? { description } : {}),
|
|
102
|
+
description: claim.description,
|
|
110
103
|
field: claim.target.field,
|
|
111
104
|
status: claim.status,
|
|
112
105
|
expiresAt: claim.expiresAt,
|
|
@@ -192,7 +185,7 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
192
185
|
...(options.range ? { range: options.range } : {}),
|
|
193
186
|
...(claimMeta(options) ? { meta: claimMeta(options) } : {}),
|
|
194
187
|
},
|
|
195
|
-
|
|
188
|
+
description: options.description ?? 'editing',
|
|
196
189
|
ttl: options.ttl,
|
|
197
190
|
queue: !failFast,
|
|
198
191
|
maxQueueDepth: options.maxQueueDepth,
|
|
@@ -215,7 +208,7 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
215
208
|
model = objectPool.get(id) ?? model;
|
|
216
209
|
}
|
|
217
210
|
const snapshot = collaboration.createSnapshot(schemaKey, id);
|
|
218
|
-
const
|
|
211
|
+
const description = options.description ?? 'editing';
|
|
219
212
|
// The self-claim's `ClaimTarget` mirrors what a peer's `claim.state` would
|
|
220
213
|
// report (`state` maps `held.target.model` to `type`), so a holder and a
|
|
221
214
|
// peer see the same `target.type` for one row — the wire model token.
|
|
@@ -233,7 +226,7 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
233
226
|
lease,
|
|
234
227
|
snapshot,
|
|
235
228
|
target: selfTarget,
|
|
236
|
-
|
|
229
|
+
description,
|
|
237
230
|
expiresAt,
|
|
238
231
|
});
|
|
239
232
|
const target = {
|
|
@@ -281,9 +274,11 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
281
274
|
object: 'claim',
|
|
282
275
|
id: lease.id,
|
|
283
276
|
readAt: snapshot.stamp,
|
|
277
|
+
// The fencing token the server minted for this grant, forwarded from the
|
|
278
|
+
// lease so writes taken under this handle carry it (Option B).
|
|
279
|
+
...(lease.fenceToken !== undefined ? { fenceToken: lease.fenceToken } : {}),
|
|
284
280
|
target,
|
|
285
|
-
|
|
286
|
-
...(options.description ? { description: options.description } : {}),
|
|
281
|
+
description,
|
|
287
282
|
data: modelAsRow(model),
|
|
288
283
|
release,
|
|
289
284
|
revoke: () => {
|
|
@@ -293,12 +288,153 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
293
288
|
[Symbol.asyncDispose]: release,
|
|
294
289
|
};
|
|
295
290
|
};
|
|
296
|
-
|
|
291
|
+
// The row-free sibling of `takeClaim`: locks a key by id alone, for a row that
|
|
292
|
+
// lives only in the customer's own database and was never synced into Ablo.
|
|
293
|
+
// Everything about the lease — the fail-fast contention check, the scope pin,
|
|
294
|
+
// the `createClaim` grant, the heartbeat wiring, and the `activeClaims`
|
|
295
|
+
// bookkeeping — is identical; what's dropped is the object-pool `load` (and its
|
|
296
|
+
// `entity_not_found` throw), the post-grant re-read, and the `.data` field,
|
|
297
|
+
// since there is no local row to hydrate or return.
|
|
298
|
+
const takeRowFreeClaim = async (id, options) => {
|
|
299
|
+
if (!collaboration) {
|
|
300
|
+
throw new AbloValidationError(`Model "${schemaKey}" was built without the collaboration runtime, so claim() is unavailable here. Claiming needs no per-model config — use the standard Ablo({ schema, apiKey }) client and every model is claimable.`, { code: 'model_claim_not_configured' });
|
|
301
|
+
}
|
|
302
|
+
// Is someone else already on this target? Read the local coordination
|
|
303
|
+
// snapshot up front so a `queue: false` caller can reject before announcing
|
|
304
|
+
// a claim the server would refuse.
|
|
305
|
+
const held = collaboration.state({ model: wireModel, id });
|
|
306
|
+
const contended = !!held && held.heldBy !== collaboration.selfParticipantId;
|
|
307
|
+
const failFast = options.queue === false;
|
|
308
|
+
// Fail-fast (`queue: false`): reject now if a holder is already visible.
|
|
309
|
+
// Best-effort at the client — a row this participant never synced usually
|
|
310
|
+
// carries no local claim state either, so a peer gets the deterministic
|
|
311
|
+
// rejection only once it has observed the holder (entered the row's entity
|
|
312
|
+
// scope). The server's queue is the backstop for the queuing path.
|
|
313
|
+
if (failFast && contended) {
|
|
314
|
+
const claim = claimContextFromClaim(held);
|
|
315
|
+
throw new AbloClaimedError(formatClaimedErrorMessage({
|
|
316
|
+
targetLabel: `${registeredModelName}/${id}`,
|
|
317
|
+
heldBy: held.heldBy,
|
|
318
|
+
claim,
|
|
319
|
+
fallback: `${registeredModelName}/${id} is held by ${held.heldBy ?? 'another participant'}.`,
|
|
320
|
+
}), { code: 'entity_claimed', claims: [claim] });
|
|
321
|
+
}
|
|
322
|
+
// Enter the entity scope before acquiring the lease so the holder's claim
|
|
323
|
+
// presence broadcasts to everyone in this entity group — the same ordering
|
|
324
|
+
// the row-bearing path relies on. No pool `load` and no `entity_not_found`
|
|
325
|
+
// throw: the row lives only in the customer's database, so there is nothing
|
|
326
|
+
// to hydrate here and nothing to re-read after the grant.
|
|
327
|
+
await collaboration.pinScope?.({ [schemaKey]: id });
|
|
328
|
+
const lease = await collaboration.createClaim({
|
|
329
|
+
target: {
|
|
330
|
+
model: wireModel,
|
|
331
|
+
id,
|
|
332
|
+
...(options.field ? { field: options.field } : {}),
|
|
333
|
+
...(options.path ? { path: options.path } : {}),
|
|
334
|
+
...(options.range ? { range: options.range } : {}),
|
|
335
|
+
...(claimMeta(options) ? { meta: claimMeta(options) } : {}),
|
|
336
|
+
},
|
|
337
|
+
description: options.description ?? 'editing',
|
|
338
|
+
ttl: options.ttl,
|
|
339
|
+
queue: !failFast,
|
|
340
|
+
maxQueueDepth: options.maxQueueDepth,
|
|
341
|
+
});
|
|
342
|
+
// A watermark-only snapshot: `createSnapshot` still reads the engine's
|
|
343
|
+
// current `lastSyncId` even though the pool holds no row (the bucket is
|
|
344
|
+
// empty). It costs nothing extra and gives a write taken under this lease a
|
|
345
|
+
// real `readAt` to guard against changes since the lease was acquired.
|
|
346
|
+
const snapshot = collaboration.createSnapshot(schemaKey, id);
|
|
347
|
+
const description = options.description ?? 'editing';
|
|
348
|
+
const selfTarget = {
|
|
349
|
+
type: wireModel,
|
|
350
|
+
id,
|
|
351
|
+
...(options.field ? { field: options.field } : {}),
|
|
352
|
+
...(options.path ? { path: options.path } : {}),
|
|
353
|
+
...(options.range ? { range: options.range } : {}),
|
|
354
|
+
...(claimMeta(options) ? { meta: claimMeta(options) } : {}),
|
|
355
|
+
};
|
|
356
|
+
const ttlMs = options.ttl !== undefined ? toMs(options.ttl) : DEFAULT_LEASE_TTL_MS;
|
|
357
|
+
const expiresAt = Date.now() + ttlMs;
|
|
358
|
+
activeClaims.set(id, {
|
|
359
|
+
lease,
|
|
360
|
+
snapshot,
|
|
361
|
+
target: selfTarget,
|
|
362
|
+
description,
|
|
363
|
+
expiresAt,
|
|
364
|
+
});
|
|
365
|
+
const target = {
|
|
366
|
+
type: schemaKey,
|
|
367
|
+
id,
|
|
368
|
+
...(options.field ? { field: options.field } : {}),
|
|
369
|
+
...(options.path ? { path: options.path } : {}),
|
|
370
|
+
...(options.range ? { range: options.range } : {}),
|
|
371
|
+
...(claimMeta(options) ? { meta: claimMeta(options) } : {}),
|
|
372
|
+
};
|
|
373
|
+
// A beat resolves with the server's extended expiry; keep the local
|
|
374
|
+
// self-claim estimate in step so `claim.state` renders the real window.
|
|
375
|
+
const heartbeat = async (beatOptions) => {
|
|
376
|
+
if (!lease.heartbeat) {
|
|
377
|
+
throw new AbloValidationError('This claim handle has no heartbeat wiring, which the standard Ablo({ schema, apiKey }) client provides on every claim. This appears only when a claim is minted through an internal path that predates heartbeats.', { code: 'claim_not_wired' });
|
|
378
|
+
}
|
|
379
|
+
const resolved = resolveHeartbeatOptions(beatOptions);
|
|
380
|
+
const beat = await lease.heartbeat({
|
|
381
|
+
ttl: resolved.ttl ?? options.ttl,
|
|
382
|
+
...(resolved.details !== undefined ? { details: resolved.details } : {}),
|
|
383
|
+
});
|
|
384
|
+
const held = activeClaims.get(id);
|
|
385
|
+
if (held)
|
|
386
|
+
held.expiresAt = beat.expiresAt;
|
|
387
|
+
options.onHeartbeat?.(beat);
|
|
388
|
+
return beat;
|
|
389
|
+
};
|
|
390
|
+
const stopHeartbeatLoop = options.heartbeat
|
|
391
|
+
? startClaimHeartbeatLoop({
|
|
392
|
+
beat: () => heartbeat(),
|
|
393
|
+
intervalMs: heartbeatCadenceMs(ttlMs, options.heartbeat),
|
|
394
|
+
...(options.onHeartbeatLost
|
|
395
|
+
? { onLost: options.onHeartbeatLost }
|
|
396
|
+
: {}),
|
|
397
|
+
})
|
|
398
|
+
: undefined;
|
|
399
|
+
const release = () => {
|
|
400
|
+
stopHeartbeatLoop?.();
|
|
401
|
+
return releaseClaim(id);
|
|
402
|
+
};
|
|
403
|
+
return {
|
|
404
|
+
object: 'claim',
|
|
405
|
+
id: lease.id,
|
|
406
|
+
readAt: snapshot.stamp,
|
|
407
|
+
// Forward the grant's fencing token so writes under this row-free lease
|
|
408
|
+
// carry it (Option B), exactly as the row-bearing claim does.
|
|
409
|
+
...(lease.fenceToken !== undefined ? { fenceToken: lease.fenceToken } : {}),
|
|
410
|
+
target,
|
|
411
|
+
description,
|
|
412
|
+
release,
|
|
413
|
+
revoke: () => {
|
|
414
|
+
void release();
|
|
415
|
+
},
|
|
416
|
+
heartbeat,
|
|
417
|
+
[Symbol.asyncDispose]: release,
|
|
418
|
+
};
|
|
419
|
+
};
|
|
420
|
+
// `claim` overloads on its first argument: an options object claims a synced
|
|
421
|
+
// row and resolves to a HeldClaim (carrying `.data`); a bare id claims a key
|
|
422
|
+
// whose row Ablo doesn't hold and resolves to a HeldLease (no `.data`). Both
|
|
423
|
+
// route their throws through `toAbloError` via the guarded takers, so the
|
|
424
|
+
// two-signature shape survives — wrapping the dispatcher itself in `guard`
|
|
425
|
+
// would collapse the overloads to one.
|
|
426
|
+
const guardedTakeClaim = guard(takeClaim);
|
|
427
|
+
const guardedTakeRowFreeClaim = guard(takeRowFreeClaim);
|
|
428
|
+
function claim(arg, opts) {
|
|
429
|
+
return typeof arg === 'string'
|
|
430
|
+
? guardedTakeRowFreeClaim(arg, opts ?? {})
|
|
431
|
+
: guardedTakeClaim(arg);
|
|
432
|
+
}
|
|
297
433
|
// `claim` is a callable namespace: invoke it to take a claim, reach its
|
|
298
434
|
// members to read/steer the coordination plane. Attach the readers to the
|
|
299
|
-
//
|
|
435
|
+
// callable so `ablo.<model>.claim(...)` and `ablo.<model>.claim.state(...)`
|
|
300
436
|
// are the same object.
|
|
301
|
-
const claimApi = Object.assign(
|
|
437
|
+
const claimApi = Object.assign(claim, {
|
|
302
438
|
state(params) {
|
|
303
439
|
// Read interest: a passive observer of a row's claim state must enter that
|
|
304
440
|
// row's entity scope, or it sits only on broader `org:`/`user:` groups and
|
|
@@ -316,7 +452,7 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
316
452
|
id: own.lease.id,
|
|
317
453
|
status: 'active',
|
|
318
454
|
target: own.target,
|
|
319
|
-
|
|
455
|
+
description: own.description,
|
|
320
456
|
heldBy: collaboration?.selfParticipantId ?? '',
|
|
321
457
|
participantKind: collaboration?.selfParticipantKind ?? 'user',
|
|
322
458
|
expiresAt: own.expiresAt,
|
|
@@ -416,7 +552,7 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
416
552
|
...(claim.range ? { range: claim.range } : {}),
|
|
417
553
|
...(claimMeta(claim) ? { meta: claimMeta(claim) } : {}),
|
|
418
554
|
},
|
|
419
|
-
|
|
555
|
+
description: claim.description ?? 'creating',
|
|
420
556
|
ttl: claim.ttl,
|
|
421
557
|
queue: claim.queue !== false,
|
|
422
558
|
maxQueueDepth: claim.maxQueueDepth,
|
|
@@ -540,6 +676,9 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
540
676
|
wait: 'confirmed',
|
|
541
677
|
readAt: handle.readAt,
|
|
542
678
|
onStale: 'reject',
|
|
679
|
+
...(handle.fenceToken !== undefined
|
|
680
|
+
? { fenceToken: handle.fenceToken }
|
|
681
|
+
: {}),
|
|
543
682
|
}
|
|
544
683
|
: {}),
|
|
545
684
|
...opts,
|
|
@@ -589,6 +728,9 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
589
728
|
readAt: claimed.snapshot.stamp,
|
|
590
729
|
onStale: 'reject',
|
|
591
730
|
claimRef: { id: claimed.lease.id },
|
|
731
|
+
...(claimed.lease.fenceToken !== undefined
|
|
732
|
+
? { fenceToken: claimed.lease.fenceToken }
|
|
733
|
+
: {}),
|
|
592
734
|
...opts,
|
|
593
735
|
}
|
|
594
736
|
: {
|
|
@@ -60,7 +60,7 @@ export interface HttpModelClient<T, C = T> {
|
|
|
60
60
|
export type AbloHttpClient<S extends SchemaRecord> = {
|
|
61
61
|
readonly [K in keyof S & string]: HttpModelClient<InferModel<Schema<S>, K>, InferCreate<Schema<S>, K>>;
|
|
62
62
|
} & {
|
|
63
|
-
/** Runs one-time setup
|
|
63
|
+
/** Runs one-time setup (durable-outbox scope resolution and replay) before the client is used. It also runs lazily ahead of the first request, so calling it yourself is optional. */
|
|
64
64
|
ready(): Promise<void>;
|
|
65
65
|
/** Replays every pending durable HTTP write in seal order and waits for settlement. */
|
|
66
66
|
waitForFlush(): Promise<void>;
|