export type VelociousSyncRealtimeChannelDescriptor = import("../configuration-types.js").VelociousSyncRealtimeChannelDescriptor; export type VelociousSyncRealtimeWebsocketClient = import("../configuration-types.js").VelociousSyncRealtimeWebsocketClient; export type SyncRealtimeScopeRow = { /** * - Scope attribute conditions. */ conditions: Record>; /** * - Persisted cursor JSON payload. */ cursorPayload: string | null; /** * - Scope row id. */ id: string; /** * - Scope resource type. */ resourceType: string | null; /** * - Fixed-size scope digest. */ scopeDigest: string; /** * - Scope state. */ state: string; /** * - Physical store identity. */ storeIdentity: string; }; export type DerivedSyncChannel = { /** * - Channel name. */ channel: string; /** * - In-memory cursor watermark. */ cursor: import("./sync-api-client-types.js").SyncCursor; /** * - Default resource type for pushed changes. */ resourceType: string | null; /** * - Owning scope row, or null for legacy channels. */ scopeRow: SyncRealtimeScopeRow | null; /** * - Subscription handle. */ subscription: import("../configuration-types.js").VelociousSyncRealtimeSubscription; }; export type DerivedSyncChannelDescriptor = { /** * - Channel name. */ channel: string; /** * - In-memory cursor watermark (null for legacy channels). */ cursor: import("./sync-api-client-types.js").SyncCursor; /** * - Subscribe params (without the framework-injected auth token). */ params: Record>; /** * - Default resource type for pushed changes. */ resourceType: string | null; /** * - Owning scope row, or null for legacy channels. */ scopeRow: SyncRealtimeScopeRow | null; }; /** @typedef {import("../configuration-types.js").VelociousSyncRealtimeChannelDescriptor} VelociousSyncRealtimeChannelDescriptor */ /** @typedef {import("../configuration-types.js").VelociousSyncRealtimeWebsocketClient} VelociousSyncRealtimeWebsocketClient */ /** * The persisted scope row the bridge carries so it can advance the scope's cursor * on applied pushes. Mirrors the framework-owned scope store row shape. * @typedef {object} SyncRealtimeScopeRow * @property {Record>} conditions - Scope attribute conditions. * @property {string | null} cursorPayload - Persisted cursor JSON payload. * @property {string} id - Scope row id. * @property {string | null} resourceType - Scope resource type. * @property {string} scopeDigest - Fixed-size scope digest. * @property {string} state - Scope state. * @property {string} storeIdentity - Physical store identity. */ /** * One derived channel subscription carrying its owning scope row (null for legacy * app channels) and the in-memory cursor watermark advanced by applied pushes. * @typedef {object} DerivedSyncChannel * @property {string} channel - Channel name. * @property {import("./sync-api-client-types.js").SyncCursor} cursor - In-memory cursor watermark. * @property {string | null} resourceType - Default resource type for pushed changes. * @property {SyncRealtimeScopeRow | null} scopeRow - Owning scope row, or null for legacy channels. * @property {import("../configuration-types.js").VelociousSyncRealtimeSubscription} subscription - Subscription handle. */ /** * One derived channel to subscribe, carrying its owning scope row and cursor for * framework sync channel subscriptions (null scope row for legacy app channels). * @typedef {object} DerivedSyncChannelDescriptor * @property {string} channel - Channel name. * @property {import("./sync-api-client-types.js").SyncCursor} cursor - In-memory cursor watermark (null for legacy channels). * @property {Record>} params - Subscribe params (without the framework-injected auth token). * @property {string | null} resourceType - Default resource type for pushed changes. * @property {SyncRealtimeScopeRow | null} scopeRow - Owning scope row, or null for legacy channels. */ /** * Derived realtime push bridge for the sync client. Subscribes every declared * pull scope to the framework sync channel ({@link VELOCIOUS_SYNC_CHANNEL}) * automatically — the subscribe params mirror the scope's * `{resourceType, conditions}` so the server authorizes them through the same * sync resource authorization as pulls — plus any deprecated legacy channels * (config `sync.client.realtime.channels` callback and model-level * `static sync = {realtime: {channel}}` declarations). Pushed changes apply * through the same derived resource applier as pulls (with echo suppression * against tracked re-queueing), own-device messages are dropped by echo * origin, and a coalesced `pull()` fires when subscriptions become ready or * resume so offline gaps close. */ export default class SyncRealtimeBridge { syncClient: import("./sync-client.js").default; /** @type {Array} */ _channels: Array; /** @type {VelociousSyncRealtimeWebsocketClient | null} */ _client: VelociousSyncRealtimeWebsocketClient | null; /** @type {boolean} Whether the bridge created its own client (deprecated per-cycle path) and must disconnect it on unsubscribe. A shared connection is never owned. */ _ownsClient: boolean; /** @type {Promise} */ _applyPromise: Promise; /** @type {number} Subscription generation - bumped by unsubscribe so in-flight subscribes detect they became stale. */ _generation: number; /** @type {Promise | null} */ _subscribePromise: Promise | null; /** @type {"subscribed" | "subscribing" | "unsubscribed"} */ _state: "subscribed" | "subscribing" | "unsubscribed"; /** * Builds the bridge for a sync client. * @param {{syncClient: import("./sync-client.js").default}} args - Bridge args. */ constructor({ syncClient }: { syncClient: import("./sync-client.js").default; }); /** * Subscribes the derived realtime channels (idempotent and single-flighted): * an active subscription is kept as-is and a concurrent subscribe awaits the * in-flight attempt. Call `unsubscribe()` first to change the context. * @param {ReturnType} [context] - App context passed to the deprecated `sync.client.realtime.channels` callback (runtime scope values). * @param {{signal?: AbortSignal}} [options] - Subscription lifecycle options. * @returns {Promise} */ subscribe(context?: ReturnType, { signal }?: { signal?: AbortSignal; }): Promise; /** * Connects the websocket client, subscribes every derived channel, and waits * for each subscription's server acknowledgement before the gap-closing pull, * so no change can land between the pull and the subscriptions going live. * Locally created resources are only promoted to the bridge once everything * is live; an unsubscribe arriving during any await marks this attempt stale * and it tears its own resources down instead of resubscribing. * @param {ReturnType} context - App context passed to the channels callback. * @param {AbortSignal | undefined} signal - Subscription lifecycle signal. * @returns {Promise} */ _subscribe(context: ReturnType, signal: AbortSignal | undefined): Promise; /** * Closes every channel subscription (idempotent). The websocket is * disconnected only when the bridge owns it (deprecated per-cycle * `realtime.createClient` path); a shared app-lifetime connection stays open * so unsubscribing drops subscriptions without tearing down the socket. Also * marks any in-flight subscribe attempt stale so it tears itself down instead * of finishing the subscription afterwards. * @returns {Promise} */ unsubscribe(): Promise; /** * Reports the bridge subscription state and per-channel readiness. * @returns {{channels: Array<{channel: string, ready: boolean, resourceType: string | null}>, state: "subscribed" | "subscribing" | "unsubscribed"}} Realtime status. */ status(): { channels: Array<{ channel: string; ready: boolean; resourceType: string | null; }>; state: "subscribed" | "subscribing" | "unsubscribed"; }; /** * Awaits all enqueued message applies (tests, shutdown flows). * @returns {Promise} */ waitForApplied(): Promise; /** * Resolves the realtime configuration block, or null when the app declared * none (valid when a shared connection is configured). * @returns {import("../configuration-types.js").VelociousSyncClientRealtimeConfiguration | null} Realtime configuration, or null. */ realtimeConfiguration(): import("../configuration-types.js").VelociousSyncClientRealtimeConfiguration | null; /** * Resolves the realtime configuration and asserts a websocket client source * exists: a shared connection rides its own lifecycle, otherwise the * deprecated per-cycle `realtime.createClient` must be configured. * @param {import("../configuration-types.js").VelociousSyncRealtimeWebsocketClient | null} sharedClient - Shared connection, or null. * @returns {import("../configuration-types.js").VelociousSyncClientRealtimeConfiguration | null} Realtime configuration, or null. */ requireClientSource(sharedClient: import("../configuration-types.js").VelociousSyncRealtimeWebsocketClient | null): import("../configuration-types.js").VelociousSyncClientRealtimeConfiguration | null; /** * Derives the channel descriptors to subscribe: one framework sync channel * subscription per declared pull scope (the params mirror the scope's * `{resourceType, conditions}`), plus the deprecated legacy paths — * model-level static realtime declarations and the config channels callback. * Fails loudly when nothing is subscribable. * @param {ReturnType} context - App context passed to the deprecated channels callback. * @returns {Promise>} Channel descriptors. */ channelDescriptors(context: ReturnType): Promise>; /** * Translates a persisted scope's condition keys to the model's attribute * names so the framework channel subscription matches the publisher's * attribute-named scoping params: `serializedScopeFromQuery` persists the * query's model-normalized column names (for example `project_id`), while * scope-partition broadcasts carry attribute names (`projectId`). Keys * without a column mapping are already attribute names and pass through; * scopes on models without a declared sync resource fail loudly because no * attribute mapping exists for them. * * The all-types (user) scope has no resource type and no conditions - it * covers everything the server authorizes for the caller - so there is * nothing to map. * @param {{conditions: Record>, resourceType: string | null}} scopeRow - Active scope row. * @returns {Record>} Attribute-named scope conditions. */ attributeNamedConditions(scopeRow: { conditions: Record>; resourceType: string | null; }): Record>; /** * Chains one pushed message onto the serialized apply queue so changes apply * in arrival order; failures go to the sync client's error reporting. * @param {{body: ReturnType, channel: DerivedSyncChannel, generation?: number}} args - Message args. * @returns {void} */ enqueueApply({ body, channel, generation }: { body: ReturnType; channel: DerivedSyncChannel; generation?: number; }): void; /** * Applies one pushed message through the derived resource applier: drops * own-device messages by echo origin, defaults the channel's resourceType onto * envelopes without one, fails loudly on unknown resource types, and advances * the owning scope's cursor so a later replay resumes after the applied rows. * @param {{body: ReturnType, channel: DerivedSyncChannel}} args - Message args. * @returns {Promise} */ applyMessage({ body, channel }: { body: ReturnType; channel: DerivedSyncChannel; }): Promise; /** * Advances the owning scope's persisted cursor to the newest applied row, * monotonically (never backwards) so an out-of-order or duplicate push cannot * regress the cursor and re-deliver already-applied rows on a later replay. * Legacy channels with no owning scope are skipped. * @param {DerivedSyncChannel} channel - Channel owning the scope. * @param {Array>} syncPayloads - Applied sync entries. * @returns {Promise} */ advanceScopeCursor(channel: DerivedSyncChannel, syncPayloads: Array>): Promise; } //# sourceMappingURL=sync-realtime-bridge.d.ts.map