A declared link between two tables. Hayson reports on it and never enforces it, so on_delete
and enforce are documentation for the application, not behaviour of the engine.
The write a trigger performs after its event commits: a document assembled by fields from
old/new, then put into table (which may be the trigger’s own source table or another
one) — always inside the same unit of work as the write that fired it.
A declarative AFTER trigger: fires once event commits on table, atomically appending
effect in the same unit of work as that write. v1 is AFTER-only and unconditional.
When a markdown field’s links get parsed (schema::extract_links) against the table’s
virtual-path index — on every write (the default), or only when the caller asks for it.
The event a trigger fires after, matched against what a live write actually did to the key —
Insert when it did not exist before the write, Update when it did, Delete when a live
delete removed an existing key.
Where one field of a trigger’s effect document gets its value from — the closed set of
transformations v1 supports. There is no scripting/plugin layer in this engine, so “effect”
cannot be arbitrary code the way it would be in the TS engine; it is this declared mapping,
evaluated by the engine itself against the firing operation’s old/new record.
Parses the catalog’s own serialized form back before it is allowed near the disk — a catalog
this build could write but not read back would leave the next open() unable to see its tables.
Builds the ordered document serialize_catalog renders to bytes — sections and known fields in
the fixed order of the format (docs/format.md, „hayson.json w formacie 2“), tables and
fields sorted alphabetically by key, indexes/checks/relations/views sorted by name,
and history left in the order it arrived.
db.checkIntegrity() — walks every declared relation and reports the records whose reference
has no counterpart. A diagnosis, not a gate: nothing here is repaired, removed or written.
tables supplies the open handles this call can read; a relation naming a table not in it
reads as empty, exactly like the TS reference’s this.#tables.has(name) ? ... : [].
db.createTable() — addytywna zmiana albo nowa tabela, patrz docs/schema.md „Ewolucja schemy“.
Migracja niekompatybilna (zmiana typu, zwężenie itd.) nie jest tu sprawdzana: schema już
przeszła przez schema::parse_schema, a diff rewizji zostaje na liście TODO niżej.
db.declareIndex() — adds a named index to tables.<table>.indexes[] and, when it is unique,
refuses the whole operation if any live record (read through table_handle) already shares a
value on it. Nothing is written when it refuses: the catalog handed back on success is the only
side effect, write_catalog is left to the caller (mirrors create_table/drop_field).
db.declareTrigger() — adds a declarative AFTER trigger to triggers[]. trigger_spec.table
and trigger_spec.effect.table must already be declared tables of catalog (checked by
parse_trigger_entry before anything else runs, ADR-s21j/0029): a spec naming an undeclared
table is refused here, before Database::declare_trigger ever reaches disk. Nothing is written
when it refuses — the catalog handed back on success is the only side effect, write_catalog is
left to the caller (mirrors declare_index).
db.dropField(table, field) na poziomie samego katalogu — docs/format.md
„dropField odmawia jednolicie na wszystkich czterech klasach zależności“ i ADR-qtr2 (cerebro,
„0030 — force nie wyburza deklaracji“): force potwierdza tylko rekordy nadal niosące pole
w logu, nigdy deklaracje katalogu — ten test jest więc zawsze egzekwowany, bez parametru
force, bo nic tutaj go nie omija.
Merges declared indexes[] with the sugar the field modifiers (index, fulltext) expand to,
deduplicated by index_identity — an explicit declaration wins over the name sugar generates.
Every <table>.schema.json a format-1 database at dir declares a table through, sorted.
migrate_format_1_to_2 reads exactly these, so the caller that persists the catalog it
returns can retire exactly these once the catalog is safely on disk.
Reads every *.schema.json of a format-1 database at dir and rewrites it into one format-2
Catalog, ready for write_catalog — the migration itself is left to the caller, which also
decides when createdAt/updatedAt are pinned. Refuses while .hayson.txn sits on disk
(assertJournalGone in packages/hayson/src/catalog/migrate.ts): an unfinished unit of work is
redone through the layout it was written in, so the format cannot change out from under it.
Also refuses — without writing anything — when a field declared unique already has two live
records sharing a value (uniqueViolationsOf in migrate.ts).
Validates a parsed hayson.json of a format-2 database and returns it typed, without opening
the database. Every message names the path it failed on, such as tables.users.indexes[1].fields.
Writes the catalog atomically (.tmp → fsync → rename, via write_atomic), inheriting
the permissions of the file it replaces so .tmp is never briefly world-readable
(see docs/format.md, “Zapis, kolejność kluczy, uprawnienia”). Refuses first, via
assert_catalog_readable, leaving the file on disk untouched.