/** * Draft / preview mode - let an editor see unpublished content by flipping a signed cookie that * loaders read (`ctx.draft`) and ISR bypasses (fresh render, never cached). The cookie is **HMAC-signed** * (so a visitor can't forge it) and **HttpOnly** (JS can't read it). You gate `enableDraft` yourself - * behind an editor login or a `?token=` check - exactly like Next's `draftMode().enable()`. * * // a route you've protected (e.g. checked ?token= against a secret): * app.get("/api/draft", async (c) => { * await enableDraft(c, env.DRAFT_SECRET) * return redirect("/") * }) * * // wire the same secret so the framework can verify the cookie: * createWebApp({ ..., draftSecret: env.DRAFT_SECRET }) // → loaders get ctx.draft * withISR(app, { ..., draftSecret: env.DRAFT_SECRET }) // → editors bypass the cache */ import { type CookieOptions } from "@nifrajs/core/server"; /** The cookie name nifra uses for draft/preview mode. */ export declare const DRAFT_COOKIE = "__nifra_draft"; /** The response-cookie surface `enableDraft`/`disableDraft` need - nifra's `c.set`. Structural, so any * nifra handler context satisfies it without importing the full `Context`. */ export interface DraftCookieControls { cookie(name: string, value: string, options?: CookieOptions): void; deleteCookie(name: string, options?: { readonly path?: string; readonly domain?: string; }): void; } export interface EnableDraftOptions { /** Cookie lifetime in **seconds** (default `3600` = 1h). Keep it short - draft is an editor session. */ readonly maxAgeSeconds?: number; /** Cookie `Path` (default `"/"`). */ readonly path?: string; /** Override the `Secure` attribute (defaults to `true` - secure-by-default). Pass `false` only for * local `http://` dev, where a `Secure` cookie isn't stored. */ readonly secure?: boolean; } /** * Turn draft mode **on** for this client by setting a signed, HttpOnly `__nifra_draft` cookie. Call it * from a route you've already authorized. `secret` signs the cookie - pass the SAME secret to * `createWebApp({ draftSecret })` and `withISR({ draftSecret })` so the framework can verify it. */ export declare function enableDraft(c: { readonly set: DraftCookieControls; }, secret: string, options?: EnableDraftOptions): Promise; /** Turn draft mode **off**: clear the `__nifra_draft` cookie. Match the `path` used in `enableDraft`. */ export declare function disableDraft(c: { readonly set: DraftCookieControls; }, options?: { readonly path?: string; }): void; /** * Whether `request` carries a **valid** signed draft cookie (constant-time verify via `unsignValue`). * `createWebApp` uses it to set `ctx.draft`; `withISR` uses it to bypass the cache for editors. A * missing, forged, or tampered cookie returns `false`. */ export declare function isDraftEnabled(request: Request, secret: string): Promise; /** Config for {@link previewEndpoint}. */ export interface PreviewEndpointOptions { /** Shared secret the preview link must carry. Compared in **constant time**. */ readonly secret: string; /** Secret that **signs** the draft cookie - the same one passed to `createWebApp({ draftSecret })` * and `withISR({ draftSecret })`. Keep it distinct from {@link secret}: that one travels in URLs * (logs, `Referer`, browser history), this one never leaves the server. */ readonly draftSecret: string; /** Query parameter carrying the token. Default `"token"`. */ readonly tokenParam?: string; /** Query parameter carrying the destination. Default `"to"`. */ readonly redirectParam?: string; /** Where to send the editor when the destination parameter is absent. Default `"/"`. Must be * site-relative; a non-relative value throws at construction rather than at request time. */ readonly fallbackPath?: string; /** Cookie lifetime/path/secure overrides, exactly as {@link enableDraft} takes them. */ readonly cookie?: EnableDraftOptions; } /** * A **preview / draft-mode entry point** - a `fetch` handler that checks a preview token, turns draft * mode on, and redirects the editor to the page they wanted. `GET` with `?token=&to=/some/path`; * mount it on a nifra route, e.g. `app.get("/api/preview", (c) => handler(c.req))`. * * This is the link-borne sibling of `revalidateEndpoint`. It exists because the alternative - telling * you to gate the route yourself - means hand-rolling two checks that are easy to get subtly wrong and * that fail silently when you do: the token compare must not exit early on the first wrong character, * and the `?to=` destination must not be allowed to point off-site. Both are handled here. * * A CMS "Preview" button is a plain link, so the token has to ride the query string. That has a cost * no endpoint can remove - the secret lands in server logs, the `Referer` header, and browser history. * Use a preview token minted for that purpose, rotate it, and never reuse a token that grants anything * beyond draft mode. * * Wrong or missing token → `401`; an off-site `to` → `400`; success → `302` with the signed cookie and * `Cache-Control: no-store`, so no shared cache can ever replay one editor's draft session to a visitor. */ export declare function previewEndpoint(options: PreviewEndpointOptions): (request: Request) => Promise; //# sourceMappingURL=draft.d.ts.map