import { Context, Effect, Layer, Option } from "effect"; import { OperationError } from "../errors/index.js"; import { Sqlite } from "./sqlite.js"; /** * Per-row metadata stored alongside the CBOR payload. All fields are optional; * omitting a field falls back to the column default (no entity, version 0, * never expires, updated_at = now). */ export interface DocMeta { /** Entity name this row belongs to, or null for raw docstore rows. */ entity?: string | null; /** Schema version of the payload. */ version?: number; /** Absolute expiry (epoch ms), or null to never expire. */ expiresAt?: number | null; /** Last-write timestamp (epoch ms); defaults to now. */ updatedAt?: number; } export interface RawRow { pk: string; entity: string | null; version: number; expires_at: number | null; updated_at: number; data: Buffer; } declare const CorruptRowError_base: new = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & { readonly _tag: "CorruptRowError"; } & Readonly; /** * A point read hit a row whose CBOR payload cannot be decoded. The caller asked * for this exact key, so the failure is surfaced (rather than read as absent) * to let a repair tool detect and delete the bad row. */ export declare class CorruptRowError extends CorruptRowError_base<{ readonly pk: string; readonly cause: unknown; }> { get message(): string; } export declare function encodeDoc(data: unknown): Buffer; /** Decodes one stored payload; throws on corrupt CBOR. */ export declare function decodeDoc(data: Buffer): T; /** * Synchronous statements over the shared connection. Every function may throw * (better-sqlite3 / cbor); the `Docstore` service wraps them once as * `OperationError`. Exposed so `Entity` can compose several steps inside a * single transaction. */ export interface DocstoreSync { readonly getRawRow: (pk: string, now: number) => RawRow | undefined; readonly upsertDoc: (pk: string, data: unknown, meta: DocMeta, now: number) => number; readonly deleteDoc: (pk: string) => boolean; readonly getRawRowsByPrefix: (prefix: string) => RawRow[]; } export interface DocstoreShape { /** * Retrieves the document for a primary key. Expired rows read as absent. A * point read fails with `CorruptRowError` on an unreadable row; collection * reads skip such rows instead so one bad blob can't sink the whole batch. */ getDoc(pk: string): Effect.Effect, OperationError | CorruptRowError>; /** Raw-key escape hatch; `Entity` uses `getDocsByEntity`. Unreadable rows are skipped (warned). */ getDocsByPrefix(prefix: string): Effect.Effect; /** All live documents of an entity. Unreadable rows are skipped (warned). */ getDocsByEntity(entity: string): Effect.Effect; /** Upserts a document. Metadata defaults: no entity, version 0, no expiry, updated_at = now. */ upsertDoc(pk: string, data: T, meta?: DocMeta): Effect.Effect; /** Updates the expiry (and updated_at) of a live row. Resolves true if a row was touched. */ touchDoc(pk: string, expiresAt: number | null): Effect.Effect; /** Resolves true if a document was deleted. */ deleteDoc(pk: string): Effect.Effect; /** Resolves the number of documents deleted. */ deleteDocsByPrefix(prefix: string): Effect.Effect; /** Resolves the number of documents deleted. */ deleteDocsByEntity(entity: string): Effect.Effect; /** Existence check without deserializing. Expired rows read as absent. */ hasDoc(pk: string): Effect.Effect; countByPrefix(prefix: string): Effect.Effect; countByEntity(entity: string): Effect.Effect; /** All live primary keys matching a prefix (raw storage keys). */ getKeysByPrefix(prefix: string): Effect.Effect; /** * Physically deletes up to `limit` expired rows. Storage maintenance, not * expiry correctness (reads already ignore expired rows). */ cleanupExpired(limit?: number): Effect.Effect; /** The raw row (payload + metadata) for a live pk. */ getRawRow(pk: string): Effect.Effect, OperationError>; /** Raw rows (including expired ones) matching a prefix. Used by `Entity.migrate`. */ getRawRowsByPrefix(prefix: string): Effect.Effect; /** * Runs synchronous statements inside one write transaction. `fn` must not * return an Effect: it would never run. */ transaction(operation: string, fn: (tx: DocstoreSync) => A): Effect.Effect; /** Deletes every row. */ readonly clear: Effect.Effect; } declare const Docstore_base: Context.ServiceClass; /** * CBOR-encoded documents in a single `blobs` table of the shared `Sqlite` * connection, keyed by primary key with entity/version/expiry metadata columns. */ export declare class Docstore extends Docstore_base { /** Ensures the `blobs` schema on the shared connection and exposes the store. */ static readonly layer: Layer.Layer; /** Docstore over an in-memory database, for tests. Exposes `Sqlite` too. */ static readonly layerMemory: Layer.Layer; } export declare const getDoc: (pk: string) => Effect.Effect, CorruptRowError | OperationError, Docstore>; export declare const getDocsByPrefix: (prefix: string) => Effect.Effect; export declare const getDocsByEntity: (entity: string) => Effect.Effect; export declare const upsertDoc: (pk: string, data: T, meta?: DocMeta) => Effect.Effect; export declare const touchDoc: (pk: string, expiresAt: number | null) => Effect.Effect; export declare const deleteDoc: (pk: string) => Effect.Effect; export declare const deleteDocsByPrefix: (prefix: string) => Effect.Effect; export declare const deleteDocsByEntity: (entity: string) => Effect.Effect; export declare const hasDoc: (pk: string) => Effect.Effect; export declare const countByPrefix: (prefix: string) => Effect.Effect; export declare const countByEntity: (entity: string) => Effect.Effect; export declare const getKeysByPrefix: (prefix: string) => Effect.Effect; export declare const cleanupExpired: (limit?: number) => Effect.Effect; export declare const getRawRow: (pk: string) => Effect.Effect, OperationError, Docstore>; export declare const getRawRowsByPrefix: (prefix: string) => Effect.Effect; export declare const clearDocstore: Effect.Effect; export {};