mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
eslint --fix formatting sweep plus the manual residue: sync method bodies drop async behind Promise-returning signatures (the sqlite unit routes primitives through a settle() guard preserving the never-throws- synchronously contract), catch callbacks type their reason as unknown, loadAll's global slot is plain unknown (null semantics stay in JSDoc), a non-null assertion becomes a narrowing, and unsafe any assignments in tests gain explicit types. One justified eslint-disable for prefer-promise-reject-errors follows the core/session precedent — wrapping would discard the original StorageError code.
105 lines
4.2 KiB
TypeScript
105 lines
4.2 KiB
TypeScript
/**
|
|
* Backend-facing vocabulary of the storage hub: a backend owns one medium
|
|
* (a file-tree root, a database file) and exposes data-shape facets over it.
|
|
* This module is the normative contract text for backend implementers; the
|
|
* shared conformance suite in `tests/contract.ts` asserts every clause.
|
|
* @module @deepseek-ai/dsh-storage/src/backend
|
|
*/
|
|
|
|
/** Allowed shape for unit and table names: safe as a file name and as a SQL identifier segment without escaping. */
|
|
export const UNIT_NAME_RE = /^[a-z][a-z0-9_]*$/
|
|
|
|
/**
|
|
* One registered backend. A backend owns exactly one medium and shares its
|
|
* lifecycle across all facets; facets are optional members — a backend that
|
|
* cannot serve a shape simply omits it, and resolution fails loud instead.
|
|
*/
|
|
export interface StorageBackend {
|
|
/** Key-value data shape; absent when this backend cannot serve it. */
|
|
readonly kv?: KvFacet
|
|
|
|
/**
|
|
* Drain in-flight writes across all open units and release the medium.
|
|
* Idempotent; concurrent and repeated calls resolve once teardown finishes.
|
|
* @returns resolution after the medium is released.
|
|
*/
|
|
close(): Promise<void>
|
|
}
|
|
|
|
/** The key-value data shape: whole-unit snapshots plus per-record durable writes. */
|
|
export interface KvFacet {
|
|
/**
|
|
* Open one unit, creating it when the medium holds no trace of it yet
|
|
* (materialization may defer to the first write, but {@link KvUnit.loadAll}
|
|
* must immediately serve the empty shape). A version already stamped on the
|
|
* medium that differs from `descriptor.version` rejects with
|
|
* `version-mismatch`; a medium that cannot be parsed as this unit rejects
|
|
* with `malformed-medium`. Opening the same unit name twice without closing
|
|
* is a caller bug and rejects.
|
|
* @param descriptor - Static identity and shape of the unit to open.
|
|
* @returns the opened unit.
|
|
*/
|
|
open(descriptor: KvUnitDescriptor): Promise<KvUnit>
|
|
}
|
|
|
|
/** Static identity and shape of one KV unit, projected from its owner's spec. */
|
|
export interface KvUnitDescriptor {
|
|
/** Unit name; must match {@link UNIT_NAME_RE}. Also the file-name / SQL-identifier segment. */
|
|
readonly name: string
|
|
/** Unit format version; a non-negative integer stamped on the medium at first materialization. */
|
|
readonly version: number
|
|
/** Table names; each must match {@link UNIT_NAME_RE}. */
|
|
readonly tables: readonly string[]
|
|
/** Whether this unit carries the global singleton slot. */
|
|
readonly hasGlobal: boolean
|
|
}
|
|
|
|
/**
|
|
* One opened unit. Values are opaque JSON to this layer: no schema, no
|
|
* events, no domain meaning. The unit does NOT serialize concurrent writes —
|
|
* write ordering is the caller's responsibility (the domain layer runs one
|
|
* write chain per unit); the unit only guarantees that each single call is
|
|
* atomic on the medium and durable once resolved (a crash after resolution
|
|
* followed by a re-open observes the write). Any call after {@link close}
|
|
* rejects with `closed`.
|
|
*/
|
|
export interface KvUnit {
|
|
/**
|
|
* Read the full current snapshot.
|
|
* @returns every table's records keyed by table name, plus the global
|
|
* singleton (`null` when never written or not declared).
|
|
*/
|
|
loadAll(): Promise<{ tables: Record<string, Record<string, unknown>>; global: unknown }>
|
|
|
|
/**
|
|
* Upsert one record durably. Overwrite semantics: an existing key is replaced.
|
|
* @param table - Declared table name.
|
|
* @param key - Record key; any string is safe (keys never reach file paths).
|
|
* @param value - Opaque JSON-serializable record.
|
|
* @returns resolution after durability.
|
|
*/
|
|
putRecord(table: string, key: string, value: unknown): Promise<void>
|
|
|
|
/**
|
|
* Delete one record durably. Idempotent: a missing key is a no-op.
|
|
* @param table - Declared table name.
|
|
* @param key - Record key.
|
|
* @returns resolution after durability.
|
|
*/
|
|
deleteRecord(table: string, key: string): Promise<void>
|
|
|
|
/**
|
|
* Write the global singleton durably. Only valid when the descriptor
|
|
* declared `hasGlobal`.
|
|
* @param value - Opaque JSON-serializable value.
|
|
* @returns resolution after durability.
|
|
*/
|
|
setGlobal(value: unknown): Promise<void>
|
|
|
|
/**
|
|
* Drain this unit's in-flight writes and release it. Idempotent.
|
|
* @returns resolution after the unit is released.
|
|
*/
|
|
close(): Promise<void>
|
|
}
|