import { type ShareConfig } from './config.js';
import { type ShareBackendKind } from './backend.js';
import { type ShareVisibility } from '../storage/visibility.js';
/** The share upload result. `body`/`retryAfter` carry the server's error
 * response so a failed publish can surface WHY (the Worker returns
 * `{"error":"…"}` + a `Retry-After` on a 429); they are read only on `!ok`
 * paths, and only ever fed through the bounded {@link extractShareHttpError}. */
export type PutResult = {
    ok: boolean;
    status: number;
    url?: string;
    /** Response body text, present on the `!ok` path so the error can be extracted. */
    body?: string;
    /** `Retry-After` header value, present on a 429. */
    retryAfter?: string;
};
export type PutFn = (url: string, body: Buffer, headers: Record<string, string>) => Promise<PutResult>;
export interface PublishEndpoint {
    baseUrl: string;
    token: string;
}
export interface PublishOptions {
    /** Internal resolved backend; publishFile sets this after authentication. */
    backendKind?: ShareBackendKind;
    slug?: string;
    /**
     * Auto-expire window. Relative (`30d`, `12h`), absolute (`2026-08-01`), or
     * `never` / `none` / `permanent` for no expiry. When omitted, publishes default
     * to {@link DEFAULT_SHARE_EXPIRE} so an accidental share decays instead of
     * living forever (RUSH-2443).
     */
    expire?: string;
    contentType?: string;
    /**
     * Server-enforced visibility (RUSH-3135). `public` (default) is listed in
     * the gallery; `unlisted` is a capability URL (GET still 200, X-Robots-Tag:
     * noindex, hidden from gallery/list — obscurity, NOT authentication);
     * `private` is token-gated (the Worker serves it only to a request carrying
     * the matching viewer key, else 404 — the real read-auth fix, PHNX-3654);
     * `me` requires a Phoenix session and is visible only to the signed-in owner;
     * `org` requires the same and is visible to members of the same Phoenix
     * organization.
     */
    visibility?: ShareVisibility;
    /**
     * Hide this page from the public `/<user>` gallery and `agents artifacts share list`
     * (metadata `visibility=unlisted`). The direct URL is still world-readable —
     * unlisted, not secret (RUSH-2443). Alias of `--visibility unlisted` /
     * `--private` on the CLI. Kept so `sessions share` and existing callers do
     * not break.
     */
    unlisted?: boolean;
    /**
     * Token-gate this page (`--protected` → `visibility=private`, PHNX-3654). The
     * CLI mints a random viewer token, the Worker stores only its SHA-256 hash,
     * and the published URL carries `?k=<token>`; a request without a matching
     * `?k=` / `Authorization: Bearer` gets a `404`. Unlike `unlisted` (obscurity),
     * this is enforced read-auth. Alias of `--visibility private`.
     */
    protected?: boolean;
    /**
     * Bypass the pre-publish sensitive-content scan (emails / credential-shaped
     * strings). Required when the page intentionally carries those patterns.
     */
    force?: boolean;
    /** Generate + attach an OG cover for HTML pages (default true). */
    cover?: boolean;
    /** Inject the Cloudflare Web Analytics beacon (default true for HTML). */
    analytics?: boolean;
    /** Override the analytics token from share config. */
    analyticsToken?: string;
    /** Override the GitHub username used for the URL namespace (BYO path). */
    githubUser?: string;
    /**
     * Override the managed public handle (PHNX-3547). Managed publishes normally
     * namespace under the email local-part; when that handle is taken — or a
     * vanity namespace is wanted — pass an alternate. Sanitized to
     * `[a-z0-9-]` (max 63 chars, matching the Worker); the Worker binds it with
     * the same first-writer claim as a derived handle. Ignored on BYO (use
     * `githubUser` there).
     */
    handle?: string;
    /** DI seam for tests — override the persisted share endpoint config. */
    config?: ShareConfig;
    /** DI seam for tests — override the keychain-backed write token. Selects BYO. */
    writeToken?: string;
    /** DI seam for tests — override `readSession()`. `null` means signed out. */
    session?: import('../identity/client.js').PhoenixSession | null;
    /**
     * Override the sharer's avatar URL stamped on the object (test seam). When
     * omitted it is derived from the signed-in email via {@link resolveShareAvatar};
     * an empty string suppresses the stamp (initials-only bar).
     */
    avatar?: string;
    /** Force the BYO Cloudflare path even when signed in. */
    byo?: boolean;
    /** DI seam for tests — override the real HTTP PUT. */
    uploader?: PutFn;
    /** DI seam for tests — override cover capture (returns a PNG buffer or null). */
    capturer?: (htmlPath: string) => Promise<Buffer | null>;
    /**
     * Human display title, shown instead of the slug in the gallery and
     * `agents artifacts share list`. When omitted, one is derived (HTML `<title>`,
     * else a Markdown frontmatter `title:`, else the filename) — a share always
     * carries a label, never a blocking prompt (see {@link deriveLabel}).
     */
    label?: string;
    /**
     * Structured metadata (`--meta key=value`, repeatable). Keys are validated by
     * {@link parseMetaEntries} — lowercase `[a-z0-9-]`, and may not collide with
     * {@link RESERVED_META_KEYS} (Worker-stamped owner/visibility/expires-at,
     * plus provenance the CLI sets automatically).
     */
    meta?: Record<string, string>;
    /**
     * Skip revision retention on this publish — overwrite an existing slug's
     * object in place with no `<slug>/rev-<ts>` backup of the version it replaces
     * (default: keep it; see docs/distribution.md).
     */
    noRevision?: boolean;
    /** DI seam for tests — override provenance auto-capture (agent/session/host/repo/date). */
    provenance?: ShareProvenance;
}
export { type ShareVisibility } from '../storage/visibility.js';
/** The visibility levels a publish `--visibility` may select — the Worker's own
 * set. `private` (token-gated, PHNX-3654) is settable only at publish time,
 * since it mints a viewer token the metadata-edit route can't carry, so it is
 * NOT in {@link SHARE_VISIBILITY_LEVELS} (the in-place `share visibility <level>`
 * set). */
export declare const PUBLISH_VISIBILITY_LEVELS: readonly ShareVisibility[];
/** The visibility levels an ALREADY-published share may be re-scoped to in place
 * — the set `share visibility <target> <level>` and the inline owner control
 * accept. Excludes `private`: re-scoping to token-gated needs a fresh viewer
 * token, which only the publish path mints. */
export declare const SHARE_VISIBILITY_LEVELS: readonly ShareVisibility[];
export interface PublishResult {
    url: string;
    /** URL-safe object name, explicit (`--slug`) or deterministically derived. */
    slug?: string;
    expiresAt?: string;
    coverUrl?: string;
    /** Server-enforced visibility stamped on the object. */
    visibility?: ShareVisibility;
    /** True when the page was published with `visibility=unlisted`. */
    unlisted?: boolean;
    /** The raw viewer token minted for a `private` (token-gated) publish, or
     * undefined for any other visibility. It rides in {@link url} as `?k=<token>`;
     * only its hash is stored server-side. Treat as a secret. */
    viewerToken?: string;
    /** The label stored with this share — explicit (`--label`) or derived. */
    label?: string;
    /** Whether `label` came from `--label` or was auto-derived. */
    labelSource?: 'explicit' | 'derived';
}
/**
 * `--protected` / `{ protected: true }` map to `private` (token-gated — it wins
 * over `--unlisted` when both are set, being the stronger control); `--unlisted`
 * maps to `unlisted`; `--visibility private|unlisted|me|org` passes through;
 * otherwise `visibility` (default public).
 */
export declare function resolveShareVisibility(opts?: {
    visibility?: ShareVisibility;
    unlisted?: boolean;
    protected?: boolean;
}): ShareVisibility;
/** Mint a fresh random viewer token for a `private` (token-gated) publish. The
 * raw token rides ONLY in the returned URL's `?k=`; the Worker stores just its
 * SHA-256 hash, so the object metadata never carries the secret. */
export declare function generateViewerToken(): string;
/** SHA-256 hex of a viewer token — the form the Worker also computes and stores,
 * so a CLI-side preview of the stored hash matches. Exported for tests. */
export declare function hashViewerToken(token: string): string;
/** A 64-bit random slug tail (16 lowercase hex chars) for a capability-URL
 * publish, so the slug can never be derived/guessed from the title (PHNX-3654).
 * `unlisted` leans on this for obscurity; `private` uses it as defense-in-depth
 * behind the viewer token. */
export declare function randomSlugTail(): string;
/**
 * The loud stderr warning printed on an `unlisted` / `--private` publish
 * (PHNX-3654): `unlisted` is obscurity, NOT read-authentication — anyone with
 * the URL can read it. Points the user at the real controls (`--protected`,
 * `--expire`, `me`/`org`). Lives here beside the visibility logic; `share.ts`
 * prints it so the lib layer stays free of `console.*`.
 */
export declare function unlistedNotPrivateWarning(): string;
export interface ShareProvenance {
    /** Harness/agent name (`AGENTS_AGENT_NAME`), when publishing from an agent run. */
    agent?: string;
    /** Session id (`AGENTS_SESSION_ID` / `AGENT_SESSION_ID`), when publishing from an agent run. */
    session?: string;
    /** The machine the publish ran from (`os.hostname()`) — always present. */
    host?: string;
    /** git repo name at publish time, absent outside a git checkout — never invented. */
    repo?: string;
    /** ISO date (`yyyy-mm-dd`) this publish happened, from the local clock. */
    date?: string;
}
/**
 * Reserved `customMetadata` keys a `--meta key=value` may not target (see
 * {@link parseMetaEntries}). Matches the Worker's `RESERVED_METADATA_KEYS`:
 * provenance the CLI sets automatically, plus `expires-at` / `visibility` /
 * `owner` which the Worker stamps itself.
 */
export declare const RESERVED_META_KEYS: readonly ["expires-at", "published-at", "visibility", "viewer-token-hash", "owner", "org_domain", "agent", "session", "host", "repo", "date", "avatar", "label", "label-source", "og-title", "og-description", "og-generated", "og-source-etag"];
/**
 * Auto-capture publish provenance from the exec env, git, and the local clock.
 * Every field is present only when the environment genuinely carries it — a
 * human running the command by hand outside a git repo yields `session`/`agent`/
 * `repo` all undefined, never an invented value. `host` is always present
 * (`os.hostname()` never fails to return something real about where the publish
 * ran, so it isn't "invented" in the same sense).
 */
export declare function resolveShareProvenance(opts?: {
    env?: NodeJS.ProcessEnv;
    hostname?: string;
    dir?: string;
    now?: Date;
}): ShareProvenance;
/**
 * The sharer's avatar URL, stamped so the share bar can show a real profile
 * picture instead of only the initials circle. A hosted OAuth profile image
 * already known to identity (PhoenixSession.avatarUrl — captured at login and
 * refreshed from `/api/v1/auth/me`) wins outright; only when none exists do we
 * fall back to a Gravatar keyed on the SHA-256 of the signed-in user's
 * lowercased email (Gravatar resolves either MD5 or SHA-256), with `d=404` so
 * Gravatar returns 404 for a user who has none — the bar's `<img>` onerror
 * then falls back to the initials circle. Only the hash lands in public
 * metadata, never the raw email. Returns '' when signed out (BYO without a
 * Phoenix session), leaving the bar on the initials circle.
 *
 * `opts.session === null` means "explicitly signed out" (a test seam / BYO) and
 * yields ''; `undefined` reads the real persisted session.
 */
export declare function resolveShareAvatar(opts?: {
    session?: import('../identity/client.js').PhoenixSession | null;
}): string;
/**
 * Parse repeated `--meta key=value` CLI args into a validated metadata record.
 * Keys are lowercase `[a-z0-9-]`, up to 64 characters, and may not collide with
 * {@link RESERVED_META_KEYS} (Worker-stamped owner/visibility/expires-at, plus
 * the provenance the CLI sets automatically, plus label/label-source) — throws
 * naming the offending pair on any violation.
 */
export declare function parseMetaEntries(pairs: string[]): Record<string, string>;
/** Throws when the combined `customMetadata` payload would exceed {@link MAX_METADATA_BYTES}. */
export declare function assertMetadataSize(customMetadata: Record<string, string>): void;
/**
 * Collapse a label to a single line before it goes into the `x-share-label`
 * header or public customMetadata. `<title>[^<]{1,200}</title>` matches
 * newlines (`[^<]` excludes only `<`), and `.trim()` only strips leading/
 * trailing whitespace, not embedded newlines — a multi-line `<title>` (or an
 * explicit `--label`/`--title` the caller typed with a literal newline) was
 * previously passed straight into `Headers.set()` unsanitized, which throws
 * an unhandled `TypeError: Invalid value` and crashes the publish outright.
 */
export declare function sanitizeLabel(text: string): string;
/**
 * Make a free-text value safe to put in an HTTP header.
 *
 * `fetch` encodes header values as a **ByteString**, so any code point above 255
 * throws `TypeError: Cannot convert argument to a ByteString` — an unhandled
 * crash with a stack trace, mid-publish, after the body has already been read.
 * Reproduced by publishing a session whose title ended in `…` (U+2026), and
 * reachable by any emoji, curly quote, accented name, or CJK text in a `--label`,
 * a `--meta` value, or a repo name.
 *
 * The transliterations above cover what actually shows up; anything else outside
 * latin1 is dropped, and a value that transliterates to nothing at all (a title
 * written entirely in a non-latin script) degrades to a marker rather than an
 * empty header. This value is the **latin1-safe floor** every Worker can read: a
 * pre-Unicode Worker only ever sees `x-share-<field>`, so it MUST stay folded.
 * Full Unicode rides ALONGSIDE it in a percent-encoded companion header
 * (`toPercentHeaderValue` / {@link needsUnicodeCompanion}), which a new Worker
 * opts into via `x-share-encoding: percent` and an old one ignores — so a
 * Japanese/emoji title renders in full on an updated Worker and still folds
 * gracefully everywhere else (PHNX-2786).
 */
export declare function toHeaderValue(text: string): string;
/**
 * Whether text carries a code point above latin1 (U+00FF) — the meaningful
 * display content `fetch`'s ByteString cannot hold, which {@link toHeaderValue}
 * therefore transliterates or drops. This is the range worth carrying in the
 * percent-encoded companion: an em dash, a curly quote, an emoji, or any
 * CJK/Arabic/Hindi text. A plain accented latin1 name (`José`, é = U+00E9) is
 * NOT lossy and needs no companion.
 *
 * `toHeaderValue` ALSO strips C0/C1 control characters (below U+0020, and
 * U+007F–U+009F), which this deliberately does not flag: a raw ANSI/control
 * sequence is not display text and must not be reconstructed into a page's
 * rendered metadata, so it stays dropped on both the old and new Worker paths.
 */
export declare function needsUnicodeCompanion(text: string): boolean;
/**
 * Percent-encode a single-line free-text value for the `x-share-<field>-u`
 * companion header. Whitespace is collapsed first (matching the folded value's
 * single-line shape), then `encodeURIComponent` makes it pure-ASCII and
 * header-safe. The Worker recovers the original with `decodeURIComponent`.
 */
export declare function toPercentHeaderValue(text: string): string;
/**
 * Best-effort human title when `--label` is omitted: the HTML `<title>`, else a
 * Markdown frontmatter `title:`, else the filename. Always returns something —
 * a headless publish must never hang waiting on a prompt for one.
 */
export declare function deriveLabel(filePath: string, body: Buffer): string;
/** Default auto-expire for unflagged publishes — accidental links decay (RUSH-2443). */
export declare const DEFAULT_SHARE_EXPIRE = "30d";
/** `30d` / `12h` / `2026-08-01` → an absolute ISO timestamp (or undefined). */
export declare function parseExpire(spec: string | undefined): string | undefined;
/**
 * Resolve the publish expiry. Omitted → {@link DEFAULT_SHARE_EXPIRE}. Explicit
 * `never` / `none` / `permanent` → no expiry. Anything else → {@link parseExpire}.
 */
export declare function resolveExpire(spec: string | undefined): string | undefined;
export type SensitiveHitKind = 'email' | 'credential';
export interface SensitiveHit {
    kind: SensitiveHitKind;
    /** Short redacted sample so the error names *what* was found without dumping it. */
    sample: string;
}
/**
 * Mask every email address in `text`, so a page can be published without tripping
 * {@link scanShareContent}.
 *
 * Lives here, beside the scanner, rather than in `lib/redact.ts`: it exists solely
 * to satisfy this gate, and sharing {@link EMAIL_RE} is what makes the masking
 * *sufficient* to clear it rather than merely reducing the hit count. Two copies of
 * the pattern in two modules would drift, and the failure mode of that drift is a
 * refused publish at runtime.
 *
 * Deliberately NOT part of `redactSecrets`: an email is not a credential, and a
 * transcript rendered for a private gist or local review reads better with the real
 * author addresses intact. It only becomes a leak once the text is PUBLISHED — a
 * world-readable page carrying seven of them is the RUSH-2428 incident.
 *
 * The whole address goes, domain included — a personal domain identifies its owner
 * as surely as the local part does, and keeping it would buy the reader nothing.
 */
export declare function redactEmails(text: string): string;
/**
 * Scan a text body for email addresses and credential-shaped strings. Returns
 * the first few hits (deduped by kind+sample). Binary / non-text bodies yield
 * nothing — the gate is for HTML/text reports, not screenshots.
 */
export declare function scanShareContent(body: string | Buffer): SensitiveHit[];
/** Build the refuse message when the pre-publish scan finds sensitive content. */
export declare function formatSensitiveContentError(hits: SensitiveHit[]): string;
/** Derive a URL-safe slug from a filename, stripping its path and extension. */
export declare function slugify(name: string): string;
/** The project the file belongs to — git repo name, else the cwd's basename. */
export declare function detectProject(dir?: string): string;
/**
 * Stable default slug for an artifact: prefer its HTML `<title>` or Markdown
 * frontmatter `title:`, then fall back to its filename. The same artifact title
 * always yields the same slug, so republishing without `--slug` updates the same
 * URL. `--slug` remains an exact override.
 */
export declare function defaultSlug(filePath: string, body?: Buffer): string;
/**
 * Best-effort OG cover: capture a screenshot, upload it as `<slug>.png`, and return
 * the page body with og:image meta injected (+ the cover URL). All IO is injected
 * (`put`, `capturer`), so this whole path is unit-testable without config/keychain.
 * Any miss — no capturer output, a failed upload — returns the original body and no
 * coverUrl, so publishing never fails because a cover couldn't be made.
 */
export declare function attachOgCover(filePath: string, body: Buffer, ctx: {
    /** Absolute URL to PUT the cover to, `${pageUrl}.png`. Doubles as the cover URL. */
    pngUrl: string;
    pageUrl: string;
    put: PutFn;
    pngHeaders: Record<string, string>;
    capturer: (p: string) => Promise<Buffer | null>;
}): Promise<{
    body: Buffer;
    coverUrl?: string;
}>;
/** Resolve the publisher's GitHub username, with an explicit override winning first. */
export declare function resolveShareUsername(opts?: {
    githubUser?: string;
}): Promise<string>;
/** Build the R2 object key from a namespace username and a slug part. */
export declare function buildShareKey(username: string, slugPart: string): string;
export declare function publishFile(filePath: string, opts?: PublishOptions): Promise<PublishResult>;
/** Sanitize a caller-chosen managed handle to the Worker's namespace shape.
 * Returns '' when the result is empty or over the Worker's 63-char cap (the
 * caller then falls back to the derived handle / errors). */
export declare function resolveManagedHandle(handle: string | undefined): string;
export declare function publishToEndpoint(filePath: string, endpoint: PublishEndpoint, opts?: PublishOptions): Promise<PublishResult>;
