import { type CheckSubject, type PlatformActorId, type ReservedStoreOperation, type ReservedStoreResults, type ScopeId, type StoreOperationCatalog, type StoreRequest, type StoreResponse, type TenantId } from '@substrat-run/contracts'; import { type StoreCallLine } from './invocation-line.js'; import type { HostAdmin } from './scope-host.js'; /** Who a scope-store object belongs to. The host builds it. The runner keys on it. */ export interface ScopeStoreAddress { readonly tenantId: string; readonly vertical: string; readonly scopeId: string; readonly binding: string; } /** * What a kind ships for the pure adapter and for tests (#2213). One runner per * kind. `invoke` speaks the envelope. `start` runs on the first store call. * `stop` runs from `close`, including when `start` never ran, so it tolerates that. */ export interface ScopeStoreRunner { readonly kind: string; start(): Promise; stop(): Promise; invoke(address: ScopeStoreAddress, request: StoreRequest): Promise; } /** The runners a host was given, by binding. A binding named twice is refused. */ export interface ScopeStoreHostOptions { /** When set, a scope running any other vertical is refused. */ readonly vertical?: string; readonly runners?: readonly { readonly binding: string; readonly runner: ScopeStoreRunner; }[]; readonly kinds?: Readonly>; /** * `platform` fetches `store.substrat.internal`. The router signs and addresses * the object. `binding` is the direct namespace, for local development and * tests, and is not an isolation boundary. */ readonly transport?: 'platform' | 'binding'; } export declare function scopeStoreRunnersOf(options: ScopeStoreHostOptions | undefined): Map; /** Starts and stops one host's runners (#2213). */ export interface ScopeStoreLifecycle { start(): Promise; stop(): Promise; } /** * The lifecycle both adapters run their runners through. `start` starts each distinct * runner once, and concurrent callers share the pass. A runner that started stays * started when a later one throws, so the retry starts only the rest. * * `stop` first waits for a pass in flight, which starts no further runner once a stop * has begun (it rejects instead), so no runner is started after the stops are asked. * It then asks every runner, started or not, since a runner tolerates a stop without a * start. One that throws does not leave the others running. One failure is rethrown as * it is, and several as an `AggregateError` carrying each. */ export declare function scopeStoreLifecycleOf(runners: ReadonlyMap): ScopeStoreLifecycle; /** * The capability a `getScopeStore` door returns (#2207). * * `invoke` takes an operation and its input. It does not take `InvokeOptions`: * those belong to a scope operation (idempotency, observers), and a store call * is not one. A third argument is refused at runtime as well as by this type. * The catalog parameter narrows `operation` for a caller who knows the kind. */ export interface StoreStub { readonly tenantId: TenantId; readonly scopeId: ScopeId; readonly subjectKind?: CheckSubject['kind']; invoke(operation: C['operations'][number], input?: unknown): Promise; } /** What one store call leaves on an invocation line (#2245). Never the input. */ export type StoreCallReport = StoreCallLine; /** * Observers on a store stub (#2245). They carry no authority, the same way * {@link ScopeStubOptions} does not. With none passed, the call is recorded on the * request now running (RFC ยง6); `onStoreCall`, when passed, is heard instead. * A throw from either is swallowed: the call's result is the store's, not the log's. */ export interface ScopeStoreStubOptions { readonly onStoreCall?: (report: StoreCallReport) => void; } /** * Time one store call and report it. The report names the binding, the * operation, the outcome and the duration. It is built here, from those four * facts, so a caller cannot put the input on it. A passed observer wins; with * none, the call goes to the request now running, and outside one it is not * logged. */ export declare function observeStoreCall(binding: string, operation: string, run: () => Promise, onStoreCall?: (report: StoreCallReport) => void): Promise; /** * Send one envelope and read the answer (#2207). Reserved names are refused * here, before `send`, so the store never sees a `substrat.*` call from a * vertical. The platform's own caller does not come through this function. */ export declare function callScopeStore(send: (request: StoreRequest) => Promise, operation: string, input?: unknown, ...extra: unknown[]): Promise; /** What a reserved operation answers, parsed (#2239). */ export type ReservedStoreResult = ReservedStoreResults[O]; /** * The platform's caller (#2207). A name that is not reserved is refused before * `send`, the mirror of {@link callScopeStore}. The result is parsed against the * operation's schema, so a store that answers the wrong shape fails here, and the * caller gets that parsed type back rather than parsing again. */ export declare function callReservedStoreOperation(send: (request: StoreRequest) => Promise, operation: O, input?: unknown): Promise>; /** * What a restore's erasure replay did (#2207). Present only when there is something to report: * `storeErasuresReplayed > 0` or `storeErasureIncomplete`. Absent means no erasure had to be * replayed and none is missing (no store for the target scope on this host, or no tombstone * that applies). * * `storeErasureIncomplete` means some tombstone that applies to the restored data was not * re-erased here. Either a store call failed, which the same restore again converges, or * `storeErasureSourceUnknown`: the dump's source scope is not a scope of this tenant on this * host (another host's backup, another tenant's, a deleted snapshot), so its marks cannot * be read and running the restore again does not change that. */ export interface StoreErasureReplay { storeErasuresReplayed: number; storeErasureIncomplete: boolean; storeErasureSourceUnknown?: true; } /** * `substrat.erase` again for every subject tombstoned in the target or in the dump's source * (#2207). The target's marks always apply: the data now lives there. The source's apply * when this host knows the source; a reaped scope keeps its directory row and its marks, so * it is known. An unknown source never refuses the restore: it is reported. Each subject is * erased once, and `erase` returns how many bindings it called. */ export declare function replayStoreErasures(input: { admin: Pick; actor: PlatformActorId; tenantId: TenantId; target: ScopeId; source: ScopeId; erase: (subjectId: string) => Promise; }): Promise; //# sourceMappingURL=scope-store.d.ts.map