import { Effect, Option } from "effect"; import { type OperationError } from "../errors/index.js"; import { CorruptRowError, Docstore } from "./docstore.js"; /** A primary-key component value. Anything else fails with `InvalidKeyError`. */ export type PKValue = string | number | boolean; declare const InvalidKeyError_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: "InvalidKeyError"; } & Readonly; /** A primary-key component was not a finite number, a string or a boolean. */ export declare class InvalidKeyError extends InvalidKeyError_base<{ readonly entity: string; readonly property: string; readonly value: unknown; }> { get message(): string; } declare const EntityValidationError_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: "EntityValidationError"; } & Readonly; /** The entity's `validate` function rejected a payload on the way in. */ export declare class EntityValidationError extends EntityValidationError_base<{ readonly entity: string; readonly cause: unknown; }> { get message(): string; } export interface UpsertOptions { /** Time-to-live in ms from now. Overridden by `expiresAt`. */ ttlMs?: number; /** Absolute expiry (epoch ms). Takes precedence over `ttlMs`. */ expiresAt?: number; } export interface EntityOptions { name: string; pk: PKProps; /** Current payload schema version. Defaults to 0. */ version?: number; /** Upgrades a stored payload to the current version. Run by migrateAll(). */ migrate?: (data: unknown, fromVersion: number) => Data; /** * Validates/parses data on the way in; throw to reject. Pair with * `Schema.decodeUnknownSync(MySchema)`. A rejection fails the upsert with * `EntityValidationError`. */ validate?: (data: unknown) => Data; /** Default TTL (ms) applied to upserts that don't specify their own. */ defaultTtlMs?: number; } type PK = Pick; /** * A class representing an entity in the database * * Heavily inspired by ElectroDB except: * - It's stupidly simple * - It uses a local SQLite database * - It does not support any kind of query besides getting by primary key * * Construct entities at module level: the constructor touches no database, * it only records the entity in a process-wide registry that * `Entity.migrateAll()` walks. Every operation is an `Effect` that requires * the `Docstore` service. * * Migration model: this is not a lazy/dual-read store. Reads assume the current * on-disk representation. After upgrading the library, run `Entity.migrateAll()` * once at startup to rewrite existing rows into the current key encoding, * payload version, and metadata columns. Reading data that predates that * migration is undefined behavior. */ export declare class Entity { private static readonly registry; private readonly version; private readonly migrateFn?; private readonly validateFn?; private readonly defaultTtlMs?; readonly name: string; readonly pkProps: PKProps; constructor(name: string, pkProps: PKProps); constructor(options: EntityOptions); /** Builds the storage key for a primary key; throws `InvalidKeyError` on a bad component. */ getPk(arg: PK): string; private readonly pk; private readonly validate; private meta; private resolveExpiry; private nextExpiry; private readonly annotate; get(arg: PK): Effect.Effect, OperationError | CorruptRowError | InvalidKeyError, Docstore>; getAll(): Effect.Effect; upsert(data: Data, options?: UpsertOptions): Effect.Effect; delete(arg: PK): Effect.Effect; deleteAll(): Effect.Effect; has(arg: PK): Effect.Effect; count(): Effect.Effect; /** * Extends (or clears) the expiry of an existing entity without rewriting its * payload. Resolves true if a live row was touched. */ touch(arg: PK, options?: UpsertOptions): Effect.Effect; /** * Shallow read-modify-write in one transaction (`validate` runs on the * result). Primary-key fields are re-asserted from `arg` afterwards, so an untyped caller cannot move the row * by passing pk fields in `partial`. Existing expiry is preserved unless * `options` overrides it. Resolves `None` (and writes nothing) if the row is * absent. */ patch(arg: PK, partial: Partial>, options?: UpsertOptions): Effect.Effect, OperationError | InvalidKeyError | CorruptRowError | EntityValidationError, Docstore>; /** * Transactional read-modify-write. The callback receives the current value * and returns the next one; the whole cycle runs in a single SQLite * transaction, so keep it synchronous; `validate` runs on the result. * Existing expiry is preserved unless * `options` overrides it. Resolves `None` (and does nothing) if the row is * absent. */ update(arg: PK, fn: (current: Data) => Data, options?: UpsertOptions): Effect.Effect, OperationError | InvalidKeyError | CorruptRowError | EntityValidationError, Docstore>; /** The primary-key objects of all live entities. */ keys(): Effect.Effect[], OperationError, Docstore>; /** Physically removes expired rows across the whole store (all entities). */ cleanupExpired(limit?: number): Effect.Effect; /** * Rewrites every stored row for this entity into the current representation: * new key encoding, current payload version (via `migrate`), and the * entity/version/updated_at columns. Idempotent and safe to re-run. Existing * expiry is preserved. Resolves the number of rows rewritten. */ migrate(): Effect.Effect; private migrateRows; /** * Runs `migrate()` for every Entity constructed in this process. Call once at * startup. Resolves the total rows rewritten. */ static migrateAll(): Effect.Effect; } export {};