Rindle

API index and search · Build metadata

Supporting declarations

packages/remote/src/optimistic-source.ts. These declarations explain referenced types. Only package-page symbols are package exports.

Exact source

TransportFactory

/** Build a transport to a follower's public ws endpoint (READ-ROUTER-DESIGN.md §2.3). Default
 *  `(endpoint) => new WsTransport(endpoint)`. */
export type TransportFactory = (endpoint: string) => Transport;

RemoteOptimisticConnection

/** How the source obtains its transport:
 *  - a pre-built {@link Transport} (or `{ transport }`) — FIXED: no endpoint migration, `wsEndpoint`
 *    on leases is ignored (in-process / tests / a single static daemon);
 *  - `{ factory, endpoint? }` — REPLACEABLE: transports are built on demand. An initial `endpoint`
 *    (a static `wsUrl` or an SSR-injected bootstrap) opens eagerly; otherwise the first lease's
 *    `wsEndpoint` opens it lazily, and a later lease naming a DIFFERENT endpoint migrates the whole
 *    session there. */
export type RemoteOptimisticConnection = Transport | {
    transport: Transport;
} | {
    factory: TransportFactory;
    endpoint?: string;
};

RemoteOptimisticSourceOptions

export interface RemoteOptimisticSourceOptions {
    /** Resolve the upstream subscribe target. Defaults to embedded-server `{name,args}`. */
    resolveSubscribe?: SubscribeResolver;
    /** Override named-mutator delivery, e.g. POST envelopes to the app API server. */
    pushMutation?: MutationEnvelopeSender;
    /** Follower-affinity mode (FOLLOWER-AFFINITY-DESIGN.md §3): the shared ticket store. When set, the
     *  source records the follower's minted ticket from the `{t:"affinity"}` frame and CLEARS it on a
     *  sustained outage so the next (ticketless) reconnect anycasts to a live follower and re-pins
     *  (§8). Absent ⇒ affinity off (today's behavior). */
    affinity?: AffinityTicketStore | (() => AffinityTicketStore | undefined);
}

RemoteOptimisticSource

export declare class RemoteOptimisticSource implements OptimisticSource {
    /** The CURRENT transport (undefined in pure-lazy mode until the first lease opens one). */
    private transport;
    /** The ws endpoint the current transport points at (undefined for a fixed transport). */
    private currentEndpoint;
    /** Builds a transport for an endpoint; undefined ⇒ fixed transport (no migration). */
    private readonly transportFactory;
    private readonly clientID;
    private readonly resolveSubscribe;
    private readonly pushMutationSender?;
    /** Resolve the current affinity ticket store. A thunk lets the one-call client turn affinity on
     *  after its first pure-lazy lease returns a placement ticket, before it opens the socket. */
    private readonly affinityStore;
    private handler;
    private progressHandler;
    private restartHandler;
    private outcomeHandler;
    private resyncHandler;
    /** Set by {@link resync} when this is a LEASE-AUTH session (some sub presented a `leaseToken`):
     *  transport pushes queue in {@link pendingPushes} until the first authenticated re-subscribe's
     *  hello re-establishes the socket's subject, then flush (see QState.authed). Never set on a
     *  token-less (embedded/rindled) session — its pushes need no subject and go straight out. */
    private awaitingAuthedHello;
    /** Envelopes held while {@link awaitingAuthedHello} (H-v §7.5 rule 3). Every entry corresponds
     *  to a still-pending backend mutation (the re-send reconstructs from pending entries; app
     *  invokes in the window are pending by definition), so a superseding resync may CLEAR this —
     *  its own re-send regenerates whatever still matters. */
    private pendingPushes;
    private readonly subs;
    /** Queries whose subscribe is waiting for a transport to exist — endpoint-less subscribes issued
     *  in pure-lazy mode before any lease opens a transport (the lmid system query is registered by
     *  the backend at construction). Flushed when a transport comes up. */
    private readonly deferred;
    /** One warning per source when an APP lease resolves without a `wsEndpoint` in pure-lazy mode —
     *  nothing will ever open the transport, which is otherwise silent (views just stay empty). */
    private warnedEndpointlessLease;
    /** The daemon's boot id (from each `nhello`); a change means it restarted. */
    private lastBootId;
    /** The client's own typed per-table schemas, for hello validation (CRIT#4); set by the backend. */
    private clientTables;
    /** Once true (set by {@link close}), in-flight lease resolutions are inert — they must not open a
     *  new transport or send after teardown. */
    private closed;
    /** True once any lease has carried a routed `wsEndpoint`. Gates onDown re-leasing so a single
     *  UNROUTED daemon keeps its pre-router behavior (recover via reconnect→resync only), not an extra
     *  lease POST during an outage. */
    private sawRoutedEndpoint;
    /** Bumped at the start of every re-subscribe-all pass. A migrate triggered mid-pass starts a new
     *  pass (higher generation); the outer pass then aborts instead of re-subscribing queries twice. */
    private resubscribeGen;
    constructor(connection: RemoteOptimisticConnection, clientID: string, opts?: RemoteOptimisticSourceOptions);
    /** Wire a transport's handlers (no `init`). */
    private attach;
    /** Make `transport` the current one, announce identity, and (re)subscribe anything deferred. */
    private bringUp;
    /** Build + bring up a fresh transport to `endpoint` (replaceable mode only). */
    private openEndpoint;
    /** Migrate the whole session to a new follower (§2.3): build the new transport, tear the old one
     *  down, and re-subscribe EVERY active query there (re-leasing — the old tokens are
     *  follower-local and invalid on the new node). */
    private migrate;
    /** Re-subscribe every live query on the current transport (each re-resolves its lease). A
     *  re-subscribe can synchronously trigger a `migrate` (lease names a new endpoint), whose own
     *  re-subscribe pass supersedes this one — the generation check then aborts this pass so a query
     *  is never re-subscribed (and re-leased) twice. */
    private resubscribeAll;
    /** Flush subscribes deferred until a transport existed (e.g. the lmid query in pure-lazy mode). */
    private flushDeferred;
    /** The current follower's ws is sustainedly down — re-lease every query. The router returns a
     *  (possibly new) `wsEndpoint`: a changed one migrates the session; an unchanged one re-subscribes
     *  over the reconnecting transport (READ-ROUTER-DESIGN.md §3). No-op for an UNROUTED daemon (no
     *  lease ever carried a `wsEndpoint`) — there is nowhere to move, so we keep the pre-router
     *  behavior and let the transport's own reconnect→resync recover. */
    private onDown;
    /** Tear down the current transport and make any in-flight lease resolution inert (a late lease
     *  must NOT open a new transport after the consumer closed the client). */
    close(): void;
    /** Register a handler fired when the DAEMON restarts (a new boot id) — the backend resets its
     *  `cv` watermark so the new daemon's reset `cv` sequence is accepted instead of dropped. */
    onRestart(handler: () => void): void;
    expectClientSchema(tables: NormalizedTableSchema[]): void;
    registerQuery(qid: QueryId, remote: RemoteQuery): void;
    unregisterQuery(qid: QueryId): void;
    pushMutation(envelope: MutationEnvelope): Promise<void>;
    onNormalized(handler: (qid: QueryId, ev: NormalizedEvent) => void): void;
    onProgress(handler: (frame: ProgressFrame) => void): void;
    /** The room deopt handshake's verdict stream (H-v). Dispatched OUT-OF-BAND on arrival — see
     *  {@link onServerMsg}'s `mutationOutcome` arm for why it must never wait behind the cv buffer. */
    onMutationOutcome(handler: (frame: MutationOutcomeFrame) => void): void;
    /** Fired once per re-established session, SYNCHRONOUSLY inside {@link resync} — before any
     *  post-reconnect frame can release (the §7.5 rule-3 window: a replayed lmid snapshot must not
     *  retire an entry whose outcome frame died with the old socket before the re-send captured
     *  it). The backend re-sends the domain's unconfirmed pending envelopes with their original
     *  mids; on a lease-auth session their DELIVERY is deferred until the first token hello
     *  re-authenticates the socket ({@link pendingPushes}). */
    onResync(handler: () => void): void;
    private subscribe;
    private onServerMsg;
    /** On reconnect: re-announce identity, fire the `onResync` re-send, and re-subscribe every live
     *  query (each re-resolves its lease, so a restarted daemon re-materializes + re-leases on the
     *  transiently). The re-send fires HERE — synchronously, before any post-reconnect frame can be
     *  processed — because the §7.5 rule-3 window closes fast: the re-subscribed lmid stream's
     *  fresh snapshot may cover a mid whose outcome frame died with the OLD socket, and once the
     *  release retires that entry as an apparent success there is nothing left to re-send (the
     *  lost-deopt write would silently vanish). Firing now captures the in-flight set intact; on a
     *  lease-auth session the envelopes themselves are HELD ({@link pendingPushes}) until the first
     *  token re-subscribe's hello re-authenticates the socket, then flush in order — so the shell's
     *  subject gate never refuses them, and its re-answer (a recorded outcome for any non-applied
     *  mid) resolves even an already-retired entry via the handshake's not-found arm. */
    private resync;
    /** Track the daemon's boot id; a change (after the first) means it restarted — fire onRestart. */
    private observeBootId;
    /** Route a `queryError` by its 101 §5 classification: retryable ⇒ keep the QState (and the
     *  rows already folded downstream — 101 §6) and re-subscribe after a jittered backoff
     *  honoring `retryAfterMs`; terminal (or pre-classification servers) ⇒ drop the
     *  subscription, exactly as before. Fixes the stranded-client gap: a worker fault's or a
     *  shedding follower's error now heals end-to-end (FOLLOWER-LAG-SHED §6.3). */
    private onQueryError;
    private openSubscriber;
    private applyBatch;
}

createRemoteOptimisticSource

/** Convenience: a `RemoteOptimisticSource` over a ws URL or a custom transport. A URL becomes a
 *  replaceable connection seeded at that endpoint (so a routed lease can still migrate it); a
 *  pre-built transport stays fixed. */
export declare function createRemoteOptimisticSource(urlOrTransport: string | Transport, clientID: string, opts?: RemoteOptimisticSourceOptions): RemoteOptimisticSource;