201 lines
5.7 KiB
TypeScript
201 lines
5.7 KiB
TypeScript
import type {
|
|
BrowserAccountScope,
|
|
BrowserDataResult,
|
|
BrowserStoragePolicy,
|
|
} from "./shared.ts";
|
|
|
|
/**
|
|
* Registry-issued, non-semantic dataset identity. Tokens must be random and
|
|
* must never contain a tenant, account, user, email, domain object ID, or the
|
|
* human-readable policy namespace.
|
|
*/
|
|
export type IndexedDbDatasetScope = Readonly<{
|
|
authorityToken: string;
|
|
namespaceToken: string;
|
|
partitionToken: string;
|
|
accountScope: BrowserAccountScope;
|
|
}>;
|
|
|
|
export type IndexedDbSynchronizationState =
|
|
| "PENDING"
|
|
| "CONFIRMED";
|
|
|
|
export type IndexedDbConnectionStatus =
|
|
| Readonly<{
|
|
kind: "CLOSED";
|
|
/**
|
|
* NOT_OPENED means the current open attempt has settled without a live
|
|
* connection. A blocked upgrade may therefore be observed as BLOCKED
|
|
* while it is waiting, then transition to CLOSED/NOT_OPENED when its
|
|
* blocked deadline fails closed.
|
|
*/
|
|
reason: "NOT_OPENED" | "VERSION_CHANGE" | "FORCED";
|
|
}>
|
|
| Readonly<{ kind: "OPENING"; targetVersion: number }>
|
|
| Readonly<{
|
|
/**
|
|
* A live open/upgrade attempt is currently waiting for another
|
|
* IndexedDB context. BLOCKED is transient and is not the settled state
|
|
* after the blocked deadline has expired.
|
|
*/
|
|
kind: "BLOCKED";
|
|
currentVersion: number;
|
|
targetVersion: number;
|
|
}>
|
|
| Readonly<{ kind: "READY"; schemaVersion: number }>
|
|
| Readonly<{ kind: "DISPOSED" }>;
|
|
|
|
export type IndexedDbCursorKey =
|
|
| string
|
|
| number
|
|
| Date
|
|
| ArrayBuffer
|
|
| readonly IndexedDbCursorKey[];
|
|
|
|
/**
|
|
* Opaque continuation state owned by an adapter query policy. Feature ports
|
|
* should wrap this value if a cursor crosses a presentation or URL boundary.
|
|
*/
|
|
export type IndexedDbCursor = Readonly<{
|
|
indexKey: IndexedDbCursorKey;
|
|
primaryKey: IndexedDbCursorKey;
|
|
}>;
|
|
|
|
export type IndexedDbPage<Value> = Readonly<{
|
|
items: readonly Value[];
|
|
nextCursor: IndexedDbCursor | null;
|
|
}>;
|
|
|
|
export type IndexedDbWriteReceipt = Readonly<{
|
|
key: string;
|
|
revision: number;
|
|
replayed: boolean;
|
|
}>;
|
|
|
|
export type IndexedDbCompareAndSwapInput<Value> = Readonly<{
|
|
key: string;
|
|
value: Value;
|
|
expectedRevision: number | null;
|
|
idempotencyKey: string;
|
|
/**
|
|
* Required only for UNTIL_SYNCED datasets. The adapter never infers server
|
|
* acknowledgement from a successful local write.
|
|
*/
|
|
synchronization?: IndexedDbSynchronizationState;
|
|
signal?: AbortSignal;
|
|
}>;
|
|
|
|
export type IndexedDbDeleteInput = Readonly<{
|
|
key: string;
|
|
expectedRevision: number;
|
|
idempotencyKey: string;
|
|
signal?: AbortSignal;
|
|
}>;
|
|
|
|
export type IndexedDbMaintenanceBatchInput = Readonly<{
|
|
/**
|
|
* Hard row-count ceiling for one invocation. The adapter also observes the
|
|
* cooperative duration budget between asynchronous storage operations.
|
|
*/
|
|
maxRows: number;
|
|
maxDurationMs: number;
|
|
signal?: AbortSignal;
|
|
}>;
|
|
|
|
export type IndexedDbMaintenanceBatchReceipt = Readonly<{
|
|
state: "MORE" | "COMPLETE";
|
|
scannedRows: number;
|
|
checkpointedRows: number;
|
|
migratedRows: number;
|
|
concurrentlyChangedRows: number;
|
|
budgetExhausted: boolean;
|
|
}>;
|
|
|
|
export type IndexedDbReceiptPruneBatchReceipt = Readonly<{
|
|
state: "MORE" | "COMPLETE";
|
|
scannedRows: number;
|
|
deletedRows: number;
|
|
budgetExhausted: boolean;
|
|
}>;
|
|
|
|
export type IndexedDbLifecycleAction =
|
|
| "SESSION_END"
|
|
| "LOGOUT"
|
|
| "ACCOUNT_DELETION"
|
|
| "RETENTION_SWEEP";
|
|
|
|
export type IndexedDbLifecycleBatchInput = Readonly<{
|
|
action: IndexedDbLifecycleAction;
|
|
maxRows: number;
|
|
maxDurationMs: number;
|
|
signal?: AbortSignal;
|
|
}>;
|
|
|
|
export type IndexedDbLifecycleBatchReceipt = Readonly<{
|
|
state: "MORE" | "COMPLETE";
|
|
scannedRows: number;
|
|
deletedRows: number;
|
|
budgetExhausted: boolean;
|
|
}>;
|
|
|
|
export type IndexedDbLifecycleAuthorityRequest = Readonly<{
|
|
action: IndexedDbLifecycleAction;
|
|
scope: IndexedDbDatasetScope;
|
|
storagePolicy: BrowserStoragePolicy;
|
|
signal?: AbortSignal;
|
|
}>;
|
|
|
|
export type IndexedDbLifecycleAuthorityDecision =
|
|
| Readonly<{ authorized: false }>
|
|
| Readonly<{
|
|
authorized: true;
|
|
/** Opaque, short-lived proof. It is validated and discarded, never stored. */
|
|
proofToken: string;
|
|
}>;
|
|
|
|
/**
|
|
* Domain-neutral asynchronous repository boundary. Native IndexedDB objects,
|
|
* object-store names, indexes and transaction callbacks remain adapter-local.
|
|
*/
|
|
export interface IndexedDbRepositoryPort<Value, Query> {
|
|
open(signal?: AbortSignal): Promise<BrowserDataResult<void>>;
|
|
read(
|
|
key: string,
|
|
signal?: AbortSignal,
|
|
): Promise<BrowserDataResult<Readonly<{ value: Value; revision: number }> | null>>;
|
|
query(
|
|
query: Query,
|
|
cursor?: IndexedDbCursor | null,
|
|
signal?: AbortSignal,
|
|
): Promise<BrowserDataResult<IndexedDbPage<Value>>>;
|
|
compareAndSwap(
|
|
input: IndexedDbCompareAndSwapInput<Value>,
|
|
): Promise<BrowserDataResult<IndexedDbWriteReceipt>>;
|
|
remove(
|
|
input: IndexedDbDeleteInput,
|
|
): Promise<BrowserDataResult<IndexedDbWriteReceipt>>;
|
|
/**
|
|
* Bounded destructive lifecycle work. Every deleting invocation is gated by
|
|
* the composition-root authority callback; callers cannot provide proof.
|
|
*/
|
|
enforceLifecycleBatch(
|
|
input: IndexedDbLifecycleBatchInput,
|
|
): Promise<BrowserDataResult<IndexedDbLifecycleBatchReceipt>>;
|
|
getStatus(): IndexedDbConnectionStatus;
|
|
subscribeStatus(listener: (status: IndexedDbConnectionStatus) => void): () => void;
|
|
close(): void;
|
|
}
|
|
|
|
/**
|
|
* Bounded, restart-safe maintenance boundary. Checkpoint keys and raw stored
|
|
* records stay private to the adapter; callers receive aggregate progress only.
|
|
*/
|
|
export interface IndexedDbMaintenancePort {
|
|
migrateCodecBatch(
|
|
input: IndexedDbMaintenanceBatchInput,
|
|
): Promise<BrowserDataResult<IndexedDbMaintenanceBatchReceipt>>;
|
|
pruneExpiredReceipts(
|
|
input: IndexedDbMaintenanceBatchInput,
|
|
): Promise<BrowserDataResult<IndexedDbReceiptPruneBatchReceipt>>;
|
|
}
|