import { type NetworkOptions, type Session } from './atproto.ts'; import { type Value } from './repo.ts'; /** The record type, and the collection listings live in. */ export declare const LISTING = "com.pipobscure.bundle.listing"; /** The `subject` of every app's listing: the sha256 of the NSID. A plugin's is its app's listing's address. */ export declare const SUBJECT: string; /** What a listing's name — its record key, and the name it installs as — may be. */ export declare const NAME_PATTERN = "^[a-z0-9][a-z0-9-]{0,63}$"; /** A domain a listing can name: lowercase, at least two labels, no trailing dot. */ export declare const DOMAIN_PATTERN = "^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"; /** The record as it is written: with a `url`, or a `domain`, never both. */ export interface ListingRecord { $type: typeof LISTING; /** `SUBJECT` for an app; for a plugin, the `at://` address of its app's listing. */ subject: string; /** Where the bundle is fetched from. */ url?: string | undefined; /** A domain whose `nzip:` TXT record says where the bundle is fetched from. */ domain?: string | undefined; title?: string | undefined; description?: string | undefined; createdAt: string; } /** A listing, as the index and the commands show it. */ export interface Listing { did: string; /** The publisher's handle, when it checks out both ways. */ handle?: string | undefined; name: string; title?: string | undefined; description?: string | undefined; /** Where it is fetched from — or, instead, */ url?: string | undefined; /** the domain whose `nzip:` record says where. */ domain?: string | undefined; createdAt: string; /** What `bundle install` takes: `@/`. */ install: string; uri: string; /** For a plugin: the address of its app's listing. */ for?: string | undefined; } /** Whether `name` can be a listing's name. */ export declare function isName(name: string): boolean; /** The `at://` address of `did`'s listing called `name`. */ export declare function listingUri(did: string, name: string): string; /** The DID and name in a listing's `at://` address, or null when it is not one. */ export declare function parseListingUri(uri: string): { did: string; name: string; } | null; /** For a plugin's listing, the address of the app's listing it is for. */ export declare function appOf(record: ListingRecord): string | undefined; /** What a record says, or why it is not a listing this tool will use. */ export declare function readListing(value: Value, name: string): { ok: true; record: ListingRecord; } | { ok: false; reason: string; }; /** Whether `text` is a domain a listing can name. */ export declare function isListedDomain(text: string): boolean; /** * Split `@/` — what `bundle install` takes for a listing — * or null for anything that does not start with `@`. */ export declare function parseListingTarget(target: string): { who: string; name: string; } | null; /** * Find the listing `@/` names: the handle resolved and * checked both ways, and the record fetched from the publisher's own PDS with * its proof, verified against their key. Whatever an index said, this is the * record an install uses. */ export declare function resolveListing(target: string, options?: NetworkOptions): Promise<{ did: string; name: string; uri: string; record: ListingRecord; }>; /** * The listing at an `at://` address, as it is now — what an install made from * one is remembered by, and what `update` asks again. */ export declare function followListing(uri: string, options?: NetworkOptions): Promise<{ did: string; name: string; uri: string; record: ListingRecord; }>; /** One listing to publish: a `url`, or a `domain` whose `nzip:` TXT record names one. */ export interface Publishing { name: string; url?: string | undefined; domain?: string | undefined; /** The address of an app's listing: this is a plugin for that app. */ for?: string | undefined; title?: string | undefined; description?: string | undefined; } /** * List a bundle under `name` from the session's account. Listing it again * replaces the record, keeping when it was first listed. */ export declare function publish(session: Session, listing: Publishing, options?: NetworkOptions): Promise<{ uri: string; replaced: boolean; record: ListingRecord; }>; /** * List several bundles at once: the account's listings read once — to know * which are there, and keep when each was first listed — and all of them * written in one batch (as many as `applyWrites` takes to a request). The PDS * saying they are written is taken as their being written; nothing is read * back, since every request counts against the account's rate limits. */ export declare function publishAll(session: Session, listings: Publishing[], options?: NetworkOptions): Promise<{ uri: string; replaced: boolean; record: ListingRecord; }[]>; /** Take a listing down. False when there was none to take down. */ export declare function unpublish(session: Session, name: string, options?: NetworkOptions): Promise; /** * Take several listings down: the account's listings read once, and those * that are there deleted in one batch. Says which were, and which were not. */ export declare function unpublishAll(session: Session, names: string[], options?: NetworkOptions): Promise<{ removed: string[]; missing: string[]; }>; /** How long a publisher's handle is believed before it is checked again (a day). */ export declare const HANDLE_AGE: number; /** How old the index may be before `listings` and `search` sync it first (an hour). */ export declare const INDEX_AGE: number; /** Where the index lives: `listings.sqlite` in the state directory. */ export declare function indexPath(): string; /** * When the index was last synced, from which backlink index, and for which * subjects — `SUBJECT`, and the listings of the apps whose plugins it holds — * or null when there is none. */ export declare function lastSync(path?: string): { at: Date; index: string; subjects: string[]; } | null; /** * Every app listed in the index, by name — or, with `for`, the address of an * app's listing, every plugin listed for that app. Empty when there is no * index yet. */ export declare function listings(path?: string, { for: app }?: { for?: string | undefined; }): Listing[]; /** * The listings matching every word of `text` — each word a prefix, anywhere in * the name, title, description or publisher's handle — best first. The name * counts most, then the title, the handle, and the description least. */ export declare function search(text: string, path?: string, { for: app }?: { for?: string | undefined; }): Listing[]; /** * What a person typed, as an FTS5 query: each word a quoted prefix term, so * nothing they type is read as query syntax, and all of them required. Words * with no letter or digit in them match nothing, and are dropped. */ export declare function matchQuery(text: string): string | null; /** What a sync did. */ export interface SyncReport { /** Publishers with at least one listing, according to the backlink index. */ publishers: number; /** Listings in the index afterwards. */ listings: number; /** Publishers whose listings were fetched afresh — new, or whose repository moved. */ refreshed: number; /** Publishers dropped, because the backlink index no longer names them. */ removed: number; /** Publishers that could not be reached; what the index had for them is kept. */ failed: { did: string; error: string; }[]; } /** * Bring the index up to date with the network, asking as little as possible. * * 1. The backlink index names every publisher with an app listed — one request * per hundred listings, and the only full pass — and every publisher of a * plugin for one of `apps`, the listings of the apps installed here. It is * also how a publisher who has taken everything down is noticed: they are * no longer named. * 2. Each publisher's PDS is asked for the revision of their repository. When * it is the one already recorded, nothing there has changed, and nothing * more is asked. The concept hash never changes, so this — not the backlink * index — is what notices an edited listing. * 3. A publisher whose revision moved, or who is new, has their listings * listed afresh: all of them, usually in one request. * * Handles are checked both ways when a publisher is first seen and then once a * day, since a handle can change without the repository changing. A publisher * that cannot be reached keeps what the index had, and is retried next time. * * The backlink index is asked first, before the file is touched, so a sync * that cannot start leaves the index exactly as it was. */ export declare function syncIndex({ index, path, apps, concurrency, handleAge, ...network }: NetworkOptions & { index: string; path?: string | undefined; /** The listings of the apps installed here, whose plugins to follow. */ apps?: string[] | undefined; concurrency?: number | undefined; handleAge?: number | undefined; }): Promise; //# sourceMappingURL=listing.d.ts.map