/** * Object URLs — RFC 0.5 §7, accepted 2026-09-03. * * https://.estiva.app/s a directory of that kind * https://.estiva.app//- one object * https://.estiva.app//- one event, which has no `d` * https://.estiva.app//-?thread= one event, inside an object * * **Here rather than in each app** because it is wire-visible: §7.1's whole * argument is that the URL one app puts in the address bar has to be resolvable * by another, which is SPEC §10's test for something the protocol owns. Two * implementations would disagree about what a slug may contain, and the * disagreement would show up as a link that resolves in the app that wrote it * and nowhere else. * * Peek's `src/lib/objectUrl.ts` is where `slugify`, `objectRef` and * `identifierFromRef` were written (PEE-14) and they are moved rather than * rewritten — an extraction that changes behaviour while every test still * passes is the thing to guard against, so Peek's own cases come with them. * * Two properties from §7.2 do all the work: * * - **The uuid is the whole identity and is never truncated.** A consumer * resolves `{"#d": [""]}` — one query, no index, and no service that * can go down and take every published link with it. * - **The slug is decorative.** Renaming the object changes it and the link * still resolves, because a reader ignores everything between the `` * segment and the final uuid. * * That second sentence is the amended one. §7.2 used to say a consumer MUST * ignore *everything* before the final uuid, which contradicted the paragraph * above it and, read literally, resolves `evil.example.com/issue/` as a * Ship issue. The `` segment is what selects the kind, and the host must * be one some app declares; only the slug is decoration. `matchObjectUrl` * implements the corrected rule. */ /** * A title reduced to URL-safe words. Decorative by contract: nothing resolves * through it, so it is free to be lossy — accents folded, punctuation dropped, * runs collapsed. */ export declare function slugify(title: string): string; /** `-`, or the bare identifier when the title slugs to nothing. */ export declare function objectRef(identifier: string, title?: string): string; /** * The identity out of a ref, or `null` if it carries none. * * Lowercased on the way out: the relay's `d` values are lowercase uuids, and a * link that has been through a system which upper-cased it should still resolve * rather than silently miss. */ export declare function identifierFromRef(ref: string): string | null; /** * The event id out of a ref, or null. * * **Why an event id in a URL rather than an `nevent`.** §7.3 rejects bech32 in * a URL on two grounds, and both apply to `nevent` exactly as they do to * `naddr`: a slice of it is checksum bytes rather than the identity, and relay * hints are part of the encoding, so one object has more than one spelling. * * A raw event id has neither problem. It is the whole identity of an event that * has no `d`, it is stable because nothing optional is encoded into it, and it * resolves with `{ids: [""]}` — one query, no index, exactly the property * §7.2 chose the bare uuid for. So the rule generalises rather than gaining an * exception: **the identity goes in the path, unencoded, and everything before * it is decoration.** */ export declare function eventIdFromRef(ref: string): string | null; /** * A URL shape an app says it serves — RFC 0.5 §7.5's `urls`, read off the * manifest event the way `web` is. * * `web` is outbound (given an object, build a link); this is inbound (given a * link, recover the object). Usually the same strings, separate fields because * they answer different questions — and because an app that changes its routes * still has to read the links it published under the old ones. */ export interface UrlPattern { /** e.g. `https://ship.estiva.app/issue/-` */ pattern: string; /** * The kind this shape names, when the app said so. * * §7.2 says the kind comes from the path segment — but `/issue/` means 30851 * only *to the app serving it*, and a consumer has never heard of "issue". * So an app MAY name the kind alongside the pattern. When it does not, a * consumer falls back to the kinds the manifest declares it handles, which is * sound because no `d` is reused under two kinds — measured across all 280 * production records at §7's acceptance. */ kind?: number; } /** * The `urls` shapes a manifest event declares, on its own origin. * * **A manifest declares `urls` only on its own origin** (SPEC §7.7, LTX-6): a * shape whose scheme, host and port are not those of one of the manifest's * `web` templates is dropped, and a manifest with no `web` declares none. * Every declared origin is one some manifest serves from, which is what lets * {@link matchObjectUrl} read a path on any of them. * * That keeps declarations consistent; it is not authentication. A reader * trusts a manifest's hosts as far as it trusts the manifest. * * why: https://github.com/estiva-app/estiva-docs/blob/main/decisions/a-files-path-is-the-same-on-every-apps-host.md */ export declare function urlPatternsOf(event: { tags: string[][]; }): UrlPattern[]; /** What a pasted URL turned out to name. */ export interface MatchedObjectUrl { /** A `d` for an addressable object, or an event id for one without. */ identifier: string; /** * How to resolve `identifier`: by `#d` for an addressable object, by `ids` * for an event that has none. * * Declared by the pattern's placeholder — `` or `` — rather than * guessed from the string, because a consumer that inspected the value would * be deciding a kind's addressing model from the shape of a hex string. */ by: 'd' | 'id'; /** Present only when the matched pattern named one. */ kind?: number; /** * The kinds to resolve under when shapes on other origins claim the path for * different kinds (SPEC §7.7, LTX-6); `kind` is then absent. One author must * hold the identity across all of them, so a second app's claim to the same * word turns the link plain rather than taking it over. */ kinds?: number[]; /** * The object the path names, when the identity came from the query — the * topic a `?thread=` link opens, read off `/topic/-`. Absent * when the path *is* the identity. A consumer resolving the thread needs * only `identifier`; this is for one that wants to say where it is. */ within?: { identifier: string; by: 'd' | 'id'; }; /** * One block of the object, when the pattern declares a `` in its * fragment and the URL carries one (SPEC §7.7, COM-2): Ship's block menu * copies `…/issue/-#block-`. The block is optional — the same * pattern still claims the URL without its fragment — so an app adds it to a * shape it already declares rather than declaring a second one. */ block?: string; } /** * Match a pasted URL against the shapes every manifest declares. * * **The `` segment is significant; the slug is not.** That is §7.2 as * amended: a consumer that matched on the trailing uuid alone would resolve * any URL from anywhere as some app's object. * * **The path is the same on every declared host** (SPEC §7.7, LTX-6). A link * whose origin (scheme, host and port) is one some shape is written on matches * every path shape by its path and query, whatever origin the shape is on, so * `leaf.estiva.app/issue/-` is Ship's issue as `ship.estiva.app`'s is. * A link on no declared origin matches nothing. A fragment route * (`/#/issue/`) matches only on its own origin, since no other app serves * it, and so does a shape that names no kind. * A match on the link's own origin wins whatever the specificity of others. * On either side, when shapes claim the path for different kinds, the match * carries all of them in `kinds`, and the reader's one-author rule decides. * Pass the shapes of every manifest, each read through {@link urlPatternsOf} * so that a manifest adds only its own origin. * * why: https://github.com/estiva-app/estiva-docs/blob/main/decisions/a-files-path-is-the-same-on-every-apps-host.md * * **A query parameter may carry the identity too** (FOL-38). Peek opens a * thread as `/topic/-?thread=`: the path names the topic, the * query names the root comment, and the link is *about the comment*. So a * pattern may write a placeholder in its query, and when it does, that * placeholder is the identity and the path's is the container (`within`). * A pattern with no query placeholder still ignores the query entirely — a * link that has been through a tracker's `?utm_…` resolves as before — and * when two patterns claim one URL, the one whose query placeholders the URL * satisfies wins over the one that ignored them, whatever order the manifest * declared them in. That is what lets an app declare the topic shape and the * thread shape as two `urls` tags rather than one grammar. * * Returns null for a URL no pattern claims, which is the honest outcome — §7.5 * says such a URL renders as a plain link, because it is one. */ export declare function matchObjectUrl(url: string, patterns: UrlPattern[]): MatchedObjectUrl | null; /** * The link that opens one block of an object, built from the app's own `urls` * shape for its kind — the outbound half of {@link matchObjectUrl}'s `block`. * * Built from `urls` rather than `web` because NIP-89's `web` template has one * placeholder, ``, and an app's `/o/` redirect is not obliged to * carry a fragment through. Returns undefined when the app declares no path * shape for that kind with a `` fragment; the caller then opens the * object without the block, which is still the right object. */ export declare function blockUrlOf(patterns: UrlPattern[], target: { kind: number; d: string; title?: string; block: string; }): string | undefined; //# sourceMappingURL=objectUrl.d.ts.map