import type { JsDb } from './feltdb.js'; import type { FreshnessCapability, Revision } from './freshness.js'; import type { AuthorityScope } from './authority-scope.js'; import type { DecisionTransportExecutionRequest, DecisionTransportExecutionResponse } from './semantic-decision.js'; import { type ManagedClientHealth } from './managed-recovery.js'; interface JsResult { success: boolean; data?: string; error?: string; } interface HttpRuntimeOptions { url: string; token?: string; applicationId?: string; environment?: string; /** * Event-stream reconnection bounds. Tests pin them; applications should not * need to, which is why they are optional and undocumented in the README. */ reconnect?: { base?: number; max?: number; factor?: number; random?: () => number; }; /** * How long a canonical application request may take before it is abandoned. * * Thirty seconds remains the default, unchanged. It is settable because a * hard-coded timeout with no override is its own resilience problem: a * caller with a tighter latency budget than the transport's had no way to * express it, and a test that needs to reproduce a real timeout had no way * to do it in less than half a minute. */ requestTimeoutMs?: number; authorityScope?: AuthorityScope; } export interface AuthorityQueryRequest { collection: string; where?: Array<{ field: string; eq?: unknown; neq?: unknown; lt?: unknown; lte?: unknown; gt?: unknown; gte?: unknown; }>; orderBy?: Array<{ field: string; direction: 'asc' | 'desc'; }>; limit: number; cursor?: string; } export interface AuthorityQueryPage> { records: T[]; nextCursor?: string; exhausted: boolean; /** Embedded execution diagnostics; omitted by runtimes that cannot report them. */ stats?: { scanned: number; decoded: number; matched: number; sorted: number; materialized: number; returned: number; usedIndex: boolean; indexName?: string; indexLookupMs: number; executionMs: number; }; /** Server execution evidence; present on canonical managed queries. */ plan?: { access_method?: string; actual_rows_scanned?: number; actual_rows_returned?: number; matching_records?: number; unrelated_records_examined?: number; pagination_position?: number; index?: string; scan_fallback?: boolean; }; } /** Network adapter implementing the same collection runtime contract as WASM. */ export declare class HttpJsDb implements JsDb { readonly collection_queries_via_authority = true; private readonly url; private token; private readonly applicationId?; private readonly environment; private readonly authorityScope?; private readonly authWaiters; /** * Canonical revision discovery. * * This used to be `private activeRevision?: Promise` assigned with * `??=`, which cached the rejected promise a single timeout produced and * poisoned the client for its whole life. It is a state machine now, and the * state it refuses to keep is a failure. */ private readonly revisionCache; private readonly backoff; private readonly requestTimeoutMs; private connectionState; private availabilityState; private lastError?; private lastSuccessAt?; private nextRetryAt?; private streamFailures; private authorityFailures; constructor(options: HttpRuntimeOptions); /** * The operational view: connected, recovering, or unable to proceed. * * This does not replace the typed error a caller already receives. An * application handles `FeltDBServiceError`; an operator reads this. Before * PR39 the only signal for a swallowed failure was a `console.error` in the * collection refresh path, which no program can act on. */ health(): ManagedClientHealth; /** Record a failure for the operational surface, without swallowing it. */ private recordFailure; private recordSuccess; private headers; private updateToken; private waitForAuthChange; auth_request(operation: 'signup' | 'signin' | 'signout' | 'session', body?: unknown): Promise; private splitKey; /** * The canonical revision for this application and environment. * * A success is cached for the life of the client, because a revision pointer * the authority has already resolved does not change under a running client. * Concurrent callers share one lookup. A failure is delivered to everyone * waiting and cached by nobody, so the next operation tries again against * whatever the authority has become. * * Nothing here retries on its own. That is the design, not an omission: the * only automatic retry loop in this client is the event stream, and it is * bounded and jittered. A revision lookup is re-attempted exactly when the * application performs another operation, so a permanently misconfigured * application produces the application's own call rate against the * authority, never a client-generated storm on top of it. */ private revisionId; /** Discovery state for tests and diagnostics; never used to make decisions. */ revisionState(): ManagedClientHealth['revision_state']; private canonical; private transactionDocument; private canonicalTransaction; get(key: string): Promise; query(collection: string): Promise; authority_query>(query: AuthorityQueryRequest): Promise>; insert(key: string, value: string): Promise; update(key: string, value: string): Promise; /** * Commit a multi-operation transaction through the authority. * * The whole transaction travels as one request and is committed as one * durable record on the server. A refusal means nothing was applied. */ commit_transaction(request: { transactionId: string; operations: Array<{ collection: string; id: string; value?: Record; requireAbsent?: boolean; expectedVersion?: number; expectedEpoch?: number; expectedLeaseId?: string; }>; preconditions?: Array<{ collection: string; id: string; requireAbsent?: boolean; expectedVersion?: number; expectedEpoch?: number; expectedLeaseId?: string; }>; }): Promise<{ transactionId: string; baseRevision: number; commitRevision: number; status: 'committed'; duplicate: boolean; operations: number; stateBefore: number; stateAfter: number; revisionId: string; operationIds: string[]; }>; /** * Atomically create a record only if the key is free. * * Routed through the authority's own transaction, not emulated with a read * followed by a write: between a read and a write another writer commits, * and the whole point of this call is that exactly one caller wins. * * A lost race is a value, not an exception -- `{ inserted: false }` with the * record the winner wrote. Every other failure (authentication, schema, * transport, a server error) is raised, because reporting one of those as * "someone else got there first" would let a caller skip work it must do. * * One difference from the embedded runtimes is worth stating rather than * discovering: the authority owns `__version` on a conditional create and * stores 1, where an embedded runtime stores the caller's object verbatim. * `Collection.putIfAbsent` supplies version 1 either way, so a collection * caller sees no difference; a caller of this raw key/value surface does. */ putIfAbsent(key: string, value: string): Promise<{ inserted: boolean; value: string; }>; /** * Compare-and-set through the managed authority's transaction surface. * * The managed runtime serves the canonical application contract and not the * single-record `/cas` endpoint, so a conditional update reaches it as a * one-operation transaction fenced on the record's `__version`. It is the * same predicate, evaluated by the same authority, inside the same lock. */ private managedCas; /** * What the authority actually holds after it refused a conditional update. * * The refusal usually names it, and that is the answer worth having because * it was read inside the commit lock. When the failure does not carry a * version -- the record was deleted, or an epoch or lease predicate failed -- * the record is re-read, which is what the caller would have to do anyway. */ private observedConflict; cas(params: { key: string; expectedVersion: number; expectedEpoch?: number; expectedLeaseId?: string; rejectIfDiverged?: boolean; value: string; }): Promise<{ updated: boolean; currentVersion: number; currentEpoch?: number; conflictCode?: string; item?: any; }>; delete(key: string): Promise; /** Acquire a reference through the server's causal peer fabric. */ acquire(collection: string, id: string): Promise; /** Execute the built-in state-derived search capability remotely. */ search(collection: string, query: string, limit?: number): Promise; provenance(collection: string, id: string): Promise; storeContent(content: Uint8Array): Promise<{ hash: string; bytes: number; ref: string; }>; acquireContent(hash: string): Promise; command(path: string, body: unknown): Promise; application_request(path: string, init?: RequestInit): Promise; /** Canonical HTTP adapter used by the typed public substrate contract. */ public_request(path: string, init?: RequestInit): Promise; execute_semantic_decision(request: DecisionTransportExecutionRequest): Promise; /** * Subscribe to change notifications. * * The returned function is the only cancellation surface, and it has to do * two things rather than one: abort the connection that may be open, and * cancel the reconnect that may be pending. Cancelling only the first leaves * a timer that wakes up after the caller has unsubscribed and opens a new * connection to an authority nobody is listening to. */ subscribe_changes(callback: (collection: string) => void): () => void; private consumeEvents; private failure; get_capability_records(capability: string): Promise; execute_op(): JsResult; sync_info(): JsResult; add_peer(): JsResult; add_sync_peer(): JsResult; remove_sync_peer(): JsResult; get_pending_for_peer(): JsResult; acknowledge_peer_operations(): JsResult; instance_id(): string; /** * `get_sequence()` is a hardcoded `0` here, and that is why it could never be * the freshness contract: the client has no local counter worth reporting. * * The authority does have one. A single process advances `inner.sequence` * inside the same critical section that writes the durable transaction * record, so concurrent clients are serialized by the server and the revision * is totally ordered. `GET /revision` reads it without performing a write. */ get_sequence(): number; /** * The scope is the store's own instance id, reported by the server, not this * client's URL. * * That distinction matters behind a load balancer or after a failover: the * URL would stay the same across two different stores and make their counters * look comparable. The store's identity changes, so the mismatch surfaces as * a refusal in `readRevision` instead of a silently wrong comparison. * * It is remembered after the first read so the common path is one request, * and a later disagreement is exactly the signal worth raising. */ private advertisedScope?; freshness(): Promise; revision(): Promise; private body; private absorb; } export {}; //# sourceMappingURL=http-db.d.ts.map