/** The idOf ladder's input: any row-shaped thing. Engine `Row`s carry `id`, * payload rows carry `key`, op_records carry `uuid` + `key` — one structural * type so every spelling walks the same ladder. */ export interface RowLike { uuid?: string | null; key?: string | null; id?: string | null; } /** * THE row address: uuid when the record carries one, else the display key — * `mergeStable`'s ladder (store.ts), promoted to the one exported spelling. * Operational blocks require `identity: uuid`, so every operational attach * point is uuid-keyed and survives a display-column edit; canonical * identity-less blocks keep key-addressing, today's behaviour. Total: null, * undefined and the empty row all answer ''. */ export declare function idOf(row: RowLike | null | undefined): string; /** `#/r///` — the record hash, third * branch beside `#/c/` and `#/v/`. PER-SEGMENT encoded (encodeURIComponent * encodes `/` itself), so `split('/')` on the hash yields exactly three * segments no matter what a path, a block name or a free-text id contains — * the router never guesses where a slashed path ends and a block begins. */ export declare function recordHash(doc: string, block: string, idOrUuid: string): string; /** Where an address points and how to open it. `block`/`id` are `''` — never * null/undefined — for document owners: absent target parts are always the * empty string in this program (the unique-index spelling the DO tables use), * and one convention beats two. */ export interface Owner { kind: 'record' | 'document'; /** vault-relative document path */ doc: string; /** lowercased block name; '' for a document owner */ block: string; /** the idOf ladder's answer (uuid when present, else the display row key); * '' for a document owner */ id: string; /** the opening hash: `#/r/…` for records, `#/c/` for documents */ hash: string; } /** What every attach point can say about where its content lives: a comment * target ({concept, block, row}), a mention_live row ({node, block, row, * uuid}), a MentionIR — all carry this shape or more. */ export interface OwnerSource { /** vault-relative document path */ node: string; block?: string | null; /** display row key */ row?: string | null; uuid?: string | null; } /** * The owner of an attached thing — comment→record, cell mention→its row, * prose paragraph→the document. TOTAL: every input resolves, because the * ladder ends at the document and every source names its document. A block * with no row/uuid also lands on the document: a blockful address without a * row names no record, and inventing "the block" as a third owner kind would * hand every consumer a case nothing can open. */ export declare function resolveOwner(src: OwnerSource): Owner; /** What identifies ONE mention for read-state purposes. */ export interface MentionKeySource extends OwnerSource { /** lowercased handle, no '@' */ handle: string; column?: string | null; /** trimmed source line, ≤200 chars — the content half of "content-derived" */ context?: string | null; source?: string | null; } /** * The read-state key of one mention — CONTENT-DERIVED, deliberately: the same * mention re-indexed by a re-push (same handle, same owner, same line) keys * identically, so a read inbox item stays read across bakes with no server * state tying the two indexings together. The flip side is priced in the * design (§9): a mention whose LINE changes is a new key and re-notifies — * "the sentence about you changed" is treated as news, which is the honest * default for a read-state with no edit history. * * Shape: `m::` — the handle rides in clear so an operator * reading a `mention_seen` table can see whose items they are; the rest is * hashed because context is free text and a raw compound key would make the * column unreadable and unbounded. */ export declare function mentionItemKey(m: MentionKeySource): string; /** What identifies ONE event on a WATCHED record for read-state purposes. * `event` is the source's own word for what happened — an op_event op * (append · update · archive · restore · migrate) or a conversational verb * (comment · reaction) — never a user id and never a sequence number. */ export interface WatchKeySource extends OwnerSource { /** what happened, in its source's own vocabulary */ event: string; /** ISO-8601 stamp of the event */ at?: string | null; /** portable actor text — a directory handle, an email local-part or a git * author name (0020: never an id) */ actor?: string | null; /** the event's own content digest: the detail JSON, the message body, the * emoji. Free text, hashed, like a mention's context. */ context?: string | null; } /** * The read-state key of one WATCHED-RECORD event (E39 t3) — CONTENT-DERIVED, * for mentionItemKey's reason exactly: the inbox's two kinds share one read * state (`inbox_reads`), so both keys must survive a re-push, a re-index and a * restore with no server state tying two readings together. An op_event, a * comment and a reaction all key off what they SAY, so re-reading the same log * answers the same key and a marked item stays marked. * * Shape: `w::` — PREFIX-DISJOINT from mentionItemKey's * `m::…` by construction, which is what lets one table hold both * without a kind column: `m:` and `w:` are the two spellings that exist, no * handle can begin a `w:` key and no event can begin an `m:` one. The event * rides in clear for the same reason the handle does — an operator reading * inbox_reads can see WHAT a mark belongs to without a join. * * SEQ IS DELIBERATELY NOT IN THE KEY. op_event.seq is stable in practice and * would be the cheaper hash, but it is a STORE address, not content: a * restore-from-snapshot that renumbered would silently re-notify every watcher, * and the whole point of a content-derived key is that it cannot. */ export declare function watchItemKey(w: WatchKeySource): string;