Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
# vault items
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
- [migrations](./migrations/migrations.md) — <!-- begin:auto-docs --> _(no submodules)_- [helpers](./helpers.md) — `items-helpers.ts` extracts the pure transformation helpers used by the items repository: row-to-item projection (with…
- [items](./items.md) — <!-- begin:auto-docs --> _(no submodules)_- migrations — <!-- begin:auto-docs --> _(no submodule…
- [migrations](./migrations/migrations.md) — <!-- begin:auto-docs --> _(no submodules)_- [helpers](./helpers.md) — `items-helpers.ts` extracts the pure transformation helpers used by the items repository: row-to-item projection (with…
- [items](./items.md) — <!-- begin:auto-docs --> _(no submodules)_- [migrations](./migrations/migrations.md) — <!-- begin:auto-docs --> _(no submodules)_- [helpers](./helpers.md) — `items-helpers.ts` extracts the pure transformation helpers used by the items repository: row-to-item projection (with…
- [items](./items.md) — <!-- begin:auto-docs --> _(no submodules)_- migrations — <!-- begin:auto…
- [migrations](./migrations/migrations.md) — <!-- begin:auto-docs --> _(no submodules)_- [helpers](./helpers.md) — `items-helpers.ts` extracts the pure transformation helpers used by the items repository: row-to-item projection (with…
- [items](./items.md) — <!-- begin:auto-docs --> _(no submodules)_- migrations — <!-- begin:auto-docs --> _(no submodul…
- [migrations](./migrations/migrations.md) — <!-- begin:auto-docs --> _(no submodules)_- [helpers](./helpers.md) — `items-helpers.ts` extracts the pure transformation helpers used by the items repository: row-to-item projection (with…
- [items](./items.md) — <!-- begin:auto-docs --> _(no submodules)_
## Purpose
`items.ts` exposes the `VaultItems` class, the in-memory CRUD repository over the vault container's item map. It is the boundary between the public `vault.items` surface and the underlying state — every public method validates input via Zod, mutates the container `Map<string, VaultItemRow>`, appends an audit entry to the container history, encrypts concealed-field values per item, and notifies the orchestrator that a flush is due.
The repository operates on the in-memory state loaded from the encrypted container blob. It does NOT touch drive directly; persistence is the orchestrator's job (debounced flush + on lock). Concealed-field values (passwords / PINs / secure notes) are encrypted with per-item keys derived from the vault key, so even a leaked container snapshot keeps concealed values as ciphertext until decrypted on demand by `rowToItem`.
## Features
- `create(fields) → VaultItem` — Zod-validated insert with concealed-field encryption.
- `get(id) → VaultItem | null` — lazy decryption; returns null for missing or tombstoned ids.
- `update(id, patch, expectedVersion?) → VaultItem` — optimistic concurrency via `version`; throws `VAULT.ITEM.VERSION_CONFLICT` on stale writes.
- `delete(id)` — tombstone (not physical delete) so sync can propagate the deletion.
- `list(options?) → VaultItem[]` — filters by category / favorite, with offset / limit pagination.
- `allRows()` — snapshot of non-deleted rows for the search index.
- Every operation records a histogram sample and an outcome counter; OTel attributes are limited to `{ op, status }` — no item ids, no field names, no plaintext.
- Mutations call `onMutation()` so the orchestrator can debounce flushes.
## Usage: Basic Example
```ts
import { VaultItems } from "@suaveplan/vault/items/items";
const items = new VaultItems({
state,
vaultKey,
clock: Date.now,
rng: undefined,
onMutation: () => scheduleFlush(),
});
const created = items.create({
title: "GitHub",
category: "login",
fields: { username: "alice", url: "https://github.com" },
concealedFields: { password: "p@ssw0rd" },
tags: ["work"],
favorite: true,
});
```
## Usage: Advanced Example
```ts
import { VaultItems } from "@suaveplan/vault/items/items";
import { VaultError, VAULT_CODES } from "@suaveplan/vault/errors";
// Optimistic concurrency: capture version, retry on conflict.
function updateWithRetry(items: VaultItems, id: string, patch: Partial<ItemFields>) {
for (let attempt = 0; attempt < 3; attempt++) {
const current = items.get(id);
if (current === null) throw new Error("missing");
try {
return items.update(id, patch, current.version);
} catch (err) {
if (
VaultError.is(err) &&
err.code === VAULT_CODES["ITEM.VERSION_CONFLICT"] &&
attempt < 2
) continue;
throw err;
}
}
}
```
## API Reference
- `class VaultItems` — public surface for `vault.items`.
- `constructor(options: ItemsOptions)`.
- `create(fields: ItemFields): VaultItem` — throws `ITEM.VALIDATION_FAILED`.
- `get(id: string): VaultItem | null`.
- `update(id: string, patch: Partial<ItemFields>, expectedVersion?: number): VaultItem` — throws `ITEM.NOT_FOUND` / `ITEM.VERSION_CONFLICT` / `ITEM.VALIDATION_FAILED`.
- `delete(id: string): void` — throws `ITEM.NOT_FOUND`.
- `list(options?: ListOptions): VaultItem[]` — throws `ITEM.VALIDATION_FAILED`.
- `allRows(): VaultItemRow[]` — read-only snapshot for search.
- `interface ItemsOptions` — `{ state, vaultKey, clock, rng, onMutation }`.
## Implementation Notes
- The repository is **stateful** — it holds references to the container state map, the vault key, and the orchestrator's mutation callback. The vault layer creates a new `VaultItems` on every unlock; the state map is replaced when the container reloads from drive.
- `update` always increments `version` by 1 per successful call. Concurrent writers using `expectedVersion` will see exactly one winner; the others receive `VAULT.ITEM.VERSION_CONFLICT`.
- `delete` is a tombstone — the row stays in the map with `deleted: true` so the sync layer can propagate the deletion. A future garbage-collection pass (out of scope for this version) would prune tombstones older than a retention window.
- The history log is append-only: every successful `create` / `update` / `delete` appends one row with `(itemId, version, timestamp, operation)`. This feeds the audit-trail and CRDT merge paths.
- `list` walks the map insertion order — the same order in which rows were created or last updated. Callers that need stable cross-process ordering should sort by `createdAt` or `updatedAt` after fetch.
- The OTel `outcome` counter uses a fixed allowlist (`success | not_found | conflict | rejected`) — adding a new value requires an OpenSpec change in lockstep with `getVaultInstruments`.