import { NestedHooks, Hookable } from 'hookable'; import { MergeHead, BaseBodyAttributes, HtmlAttributes as HtmlAttributes$1, Meta as Meta$1, Stringable, Merge, Base as Base$1, DefinedValueOrEmptyObject, LinkBase, HttpEventAttributes, DataKeys, Style as Style$1, ScriptBase, Noscript as Noscript$1, BodyEvents, MetaFlatInput } from 'zhead'; export { BodyEvents, DataKeys, DefinedValueOrEmptyObject, MergeHead, MetaFlatInput, ScriptBase, SpeculationRules } from 'zhead'; type Never = { [P in keyof T]?: never; }; type FalsyEntries = { [key in keyof T]?: T[key] | null | false | undefined; }; type UserTagConfigWithoutInnerContent = TagPriority & TagPosition & ResolvesDuplicates & Never & { processTemplateParams?: false; }; type UserAttributesConfig = ResolvesDuplicates & TagPriority & Never; interface SchemaAugmentations extends MergeHead { title: TagPriority; titleTemplate: TagPriority; base: UserAttributesConfig; htmlAttrs: UserAttributesConfig; bodyAttrs: UserAttributesConfig; link: UserTagConfigWithoutInnerContent; meta: UserTagConfigWithoutInnerContent; style: TagUserProperties; script: TagUserProperties; noscript: TagUserProperties; } type MaybeArray = T | T[]; type BaseBodyAttr = BaseBodyAttributes; type BaseHtmlAttr = HtmlAttributes$1; interface BodyAttr extends Omit { /** * The class global attribute is a space-separated list of the case-sensitive classes of the element. * * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class */ class?: MaybeArray | Record; } interface HtmlAttr extends Omit { /** * The class global attribute is a space-separated list of the case-sensitive classes of the element. * * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class */ class?: MaybeArray | Record; } interface BaseMeta extends Omit { /** * This attribute contains the value for the http-equiv, name or property attribute, depending on which is used. * * You can provide an array of values to create multiple tags sharing the same name, property or http-equiv. * * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#attr-content */ content?: MaybeArray | null; } type EntryAugmentation = undefined | Record; type MaybeFunctionEntries = { [key in keyof T]?: T[key] | ((e: Event) => void); }; type TitleTemplateResolver = string | ((title?: string) => string | null); type Title = string | FalsyEntries<({ textContent: string; } & SchemaAugmentations['title']) | null>; type TitleTemplate = TitleTemplateResolver | null | ({ textContent: TitleTemplateResolver; } & SchemaAugmentations['titleTemplate']); type Base> = Partial>> & DefinedValueOrEmptyObject; type Link> = FalsyEntries & MaybeFunctionEntries & DataKeys & SchemaAugmentations['link'] & DefinedValueOrEmptyObject; type Meta> = FalsyEntries & DataKeys & SchemaAugmentations['meta'] & DefinedValueOrEmptyObject; type Style> = FalsyEntries & DataKeys & SchemaAugmentations['style'] & DefinedValueOrEmptyObject; type Script> = FalsyEntries & MaybeFunctionEntries & DataKeys & SchemaAugmentations['script'] & DefinedValueOrEmptyObject; type Noscript> = FalsyEntries & DataKeys & SchemaAugmentations['noscript'] & DefinedValueOrEmptyObject; type HtmlAttributes> = FalsyEntries & DataKeys & SchemaAugmentations['htmlAttrs'] & DefinedValueOrEmptyObject; type BodyAttributes> = FalsyEntries & MaybeFunctionEntries & DataKeys & SchemaAugmentations['bodyAttrs'] & DefinedValueOrEmptyObject; interface HeadUtils { /** * Generate the title from a template. * * Should include a `%s` placeholder for the title, for example `%s - My Site`. */ titleTemplate?: TitleTemplate; /** * Variables used to substitute in the title and meta content. */ templateParams?: TemplateParams; } interface Head extends HeadUtils { /** * The `` HTML element defines the document's title that is shown in a browser's title bar or a page's tab. * It only contains text; tags within the element are ignored. * * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title */ title?: Title; /** * The `<base>` HTML element specifies the base URL to use for all relative URLs in a document. * There can be only one <base> element in a document. * * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base */ base?: Base<E['base']>; /** * The `<link>` HTML element specifies relationships between the current document and an external resource. * This element is most commonly used to link to stylesheets, but is also used to establish site icons * (both "favicon" style icons and icons for the home screen and apps on mobile devices) among other things. * * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-as */ link?: Link<E['link']>[]; /** * The `<meta>` element represents metadata that cannot be expressed in other HTML elements, like `<link>` or `<script>`. * * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta */ meta?: Meta<E['meta']>[]; /** * The `<style>` HTML element contains style information for a document, or part of a document. * It contains CSS, which is applied to the contents of the document containing the `<style>` element. * * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style */ style?: (Style<E['style']> | string)[]; /** * The `<script>` HTML element is used to embed executable code or data; this is typically used to embed or refer to JavaScript code. * * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script */ script?: (Script<E['script']> | string)[]; /** * The `<noscript>` HTML element defines a section of HTML to be inserted if a script type on the page is unsupported * or if scripting is currently turned off in the browser. * * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript */ noscript?: (Noscript<E['noscript']> | string)[]; /** * Attributes for the `<html>` HTML element. * * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html */ htmlAttrs?: HtmlAttributes<E['htmlAttrs']>; /** * Attributes for the `<body>` HTML element. * * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body */ bodyAttrs?: BodyAttributes<E['bodyAttrs']>; } type UseSeoMetaInput = MetaFlatInput & { title?: Title; titleTemplate?: TitleTemplate; }; interface ResolvesDuplicates { /** * By default, tags which share the same unique key `name`, `property` are de-duped. To allow duplicates * to be made you can provide a unique key for each entry. */ key?: string; /** * The strategy to use when a duplicate tag is encountered. * * - `replace` - Replace the existing tag with the new tag * - `merge` - Merge the existing tag with the new tag * * @default 'replace' (some tags will default to 'merge', such as htmlAttr) */ tagDuplicateStrategy?: 'replace' | 'merge'; /** * @deprecated Use `key` instead */ hid?: string; /** * @deprecated Use `key` instead */ vmid?: string; } type ValidTagPositions = 'head' | 'bodyClose' | 'bodyOpen'; interface TagPosition { /** * Specify where to render the tag. * * @default 'head' */ tagPosition?: ValidTagPositions; } type InnerContentVal = string | Record<string, any>; interface InnerContent { /** * Text content of the tag. * * Warning: This is not safe for XSS. Do not use this with user input, use `textContent` instead. */ innerHTML?: InnerContentVal; /** * Sets the textContent of an element. Safer for XSS. */ textContent?: InnerContentVal; /** * Sets the textContent of an element. * * @deprecated Use `textContent` or `innerHTML`. */ children?: InnerContentVal; } interface TagPriority { /** * The priority for rendering the tag, without this all tags are rendered as they are registered * (besides some special tags). * * The following special tags have default priorities: * -2 `<meta charset ...>` * -1 `<base>` * 0 `<meta http-equiv="content-security-policy" ...>` * * All other tags have a default priority of 10: `<meta>`, `<script>`, `<link>`, `<style>`, etc */ tagPriority?: number | 'critical' | 'high' | 'low' | `before:${string}` | `after:${string}`; } type TagUserProperties = FalsyEntries<TagPriority & TagPosition & InnerContent & ResolvesDuplicates & ProcessesTemplateParams>; type TagKey = keyof Head; type TemplateParams = { separator?: '|' | '-' | 'ยท' | string; } & Record<string, null | string | Record<string, string>>; interface ProcessesTemplateParams { processTemplateParams?: boolean; } interface HasTemplateParams { templateParams?: TemplateParams; } interface HeadTag extends TagPriority, TagPosition, ResolvesDuplicates, HasTemplateParams { tag: TagKey; props: Record<string, string>; processTemplateParams?: boolean; innerHTML?: string; textContent?: string; /** * Entry ID * @internal */ _e?: number; /** * Position * @internal */ _p?: number; /** * Dedupe key * @internal */ _d?: string; /** * Hash code used to represent the tag. * @internal */ _h?: string; /** * @internal */ _m?: RuntimeMode; /** * @internal */ _eventHandlers?: Record<string, ((e: Event) => {})>; } type HeadTagKeys = (keyof HeadTag)[]; type HookResult = Promise<void> | void; interface SSRHeadPayload { headTags: string; bodyTags: string; bodyTagsOpen: string; htmlAttrs: string; bodyAttrs: string; } interface RenderSSRHeadOptions { omitLineBreaks?: boolean; } interface EntryResolveCtx<T> { tags: HeadTag[]; entries: HeadEntry<T>[]; } interface DomRenderTagContext { id: string; $el: Element; shouldRender: boolean; tag: HeadTag; entry?: HeadEntry<any>; markSideEffect: (key: string, fn: () => void) => void; } interface DomBeforeRenderCtx extends ShouldRenderContext { /** * @deprecated will always be empty, prefer other hooks */ tags: DomRenderTagContext[]; } interface ShouldRenderContext { shouldRender: boolean; } interface SSRRenderContext { tags: HeadTag[]; html: SSRHeadPayload; } interface HeadHooks { 'init': (ctx: Unhead<any>) => HookResult; 'entries:updated': (ctx: Unhead<any>) => HookResult; 'entries:resolve': (ctx: EntryResolveCtx<any>) => HookResult; 'tag:normalise': (ctx: { tag: HeadTag; entry: HeadEntry<any>; resolvedOptions: CreateHeadOptions; }) => HookResult; 'tags:beforeResolve': (ctx: { tags: HeadTag[]; }) => HookResult; 'tags:resolve': (ctx: { tags: HeadTag[]; }) => HookResult; 'tags:afterResolve': (ctx: { tags: HeadTag[]; }) => HookResult; 'dom:beforeRender': (ctx: DomBeforeRenderCtx) => HookResult; 'dom:renderTag': (ctx: DomRenderTagContext, document: Document, track: any) => HookResult; 'dom:rendered': (ctx: { renders: DomRenderTagContext[]; }) => HookResult; 'ssr:beforeRender': (ctx: ShouldRenderContext) => HookResult; 'ssr:render': (ctx: { tags: HeadTag[]; }) => HookResult; 'ssr:rendered': (ctx: SSRRenderContext) => HookResult; 'script:updated': (ctx: { script: ScriptInstance<any>; }) => HookResult; 'script:instance-fn': (ctx: { script: ScriptInstance<any>; fn: string | symbol; exists: boolean; }) => HookResult; } /** * Side effects are mapped with a key and their cleanup function. * * For example, `meta:data-h-4h46h465`: () => { document.querySelector('meta[data-h-4h46h465]').remove() } */ type SideEffectsRecord = Record<string, () => void>; type RuntimeMode = 'server' | 'client'; interface HeadEntry<Input> { /** * User provided input for the entry. */ input: Input; /** * Optional resolved input which will be used if set. */ resolvedInput?: Input; /** * The mode that the entry should be used in. * * @internal */ mode?: RuntimeMode; /** * Transformer function for the entry. * * @internal */ transform?: (input: Input) => Promise<Input> | Input; /** * Head entry index * * @internal */ _i: number; /** * Default tag position. * * @internal */ tagPosition?: TagPosition['tagPosition']; /** * Default tag priority. * * @internal */ tagPriority?: TagPriority['tagPriority']; } type HeadPluginOptions = Omit<CreateHeadOptions, 'plugins'> & { mode?: RuntimeMode; }; type HeadPluginInput = (HeadPluginOptions & { key?: string; }) | ((head: Unhead) => HeadPluginOptions & { key?: string; }); type HeadPlugin = HeadPluginOptions & { key?: string; }; /** * An active head entry provides an API to manipulate it. */ interface ActiveHeadEntry<Input> { /** * Updates the entry with new input. * * Will first clear any side effects for previous input. */ patch: (input: Input) => void; /** * Dispose the entry, removing it from the active head. * * Will queue side effects for removal. */ dispose: () => void; } interface CreateHeadOptions { domDelayFn?: (fn: () => void) => void; document?: Document; plugins?: HeadPluginInput[]; hooks?: NestedHooks<HeadHooks>; } interface HeadEntryOptions extends TagPosition, TagPriority, ProcessesTemplateParams, ResolvesDuplicates { mode?: RuntimeMode; transform?: (input: unknown) => unknown; head?: Unhead; } interface Unhead<Input extends {} = Head> { /** * Registered plugins. */ plugins: HeadPlugin[]; /** * The active head entries. */ headEntries: () => HeadEntry<Input>[]; /** * Create a new head entry. */ push: (entry: Input, options?: HeadEntryOptions) => ActiveHeadEntry<Input>; /** * Resolve tags from head entries. */ resolveTags: () => Promise<HeadTag[]>; /** * Exposed hooks for easier extension. */ hooks: Hookable<HeadHooks>; /** * Resolved options */ resolvedOptions: CreateHeadOptions; /** * Use a head plugin, loads the plugins hooks. */ use: (plugin: HeadPluginInput) => void; /** * Is it a server-side render context. */ ssr: boolean; /** * @internal */ _dom?: DomState; /** * @internal */ _domUpdatePromise?: Promise<void>; /** * @internal */ _domDebouncedUpdatePromise?: Promise<void>; /** * @internal */ dirty: boolean; /** * @internal */ _scripts?: Record<string, any>; /** * @internal */ _templateParams?: TemplateParams; /** * @internal */ _separator?: string; } interface DomState { pendingSideEffects: SideEffectsRecord; sideEffects: SideEffectsRecord; elMap: Record<string, Element>; } type SafeBodyAttr = Pick<BodyAttr, 'id' | 'class'> & DataKeys; type SafeHtmlAttr = Pick<HtmlAttr, 'id' | 'class' | 'lang' | 'dir'> & DataKeys; type SafeMeta = Pick<Meta, 'id' | 'name' | 'property' | 'content' | 'charset'> & DataKeys; type SafeLink = Pick<Link, 'color' | 'crossorigin' | 'fetchpriority' | 'href' | 'hreflang' | 'imagesizes' | 'imagesrcset' | 'integrity' | 'media' | 'referrerpolicy' | 'sizes' | 'id'> & { rel?: Omit<Link['rel'], 'stylesheet' | 'canonical' | 'modulepreload' | 'prerender' | 'preload' | 'prefetch'>; type?: 'audio/aac' | 'application/x-abiword' | 'application/x-freearc' | 'image/avif' | 'video/x-msvideo' | 'application/vnd.amazon.ebook' | 'application/octet-stream' | 'image/bmp' | 'application/x-bzip' | 'application/x-bzip2' | 'application/x-cdf' | 'application/x-csh' | 'text/csv' | 'application/msword' | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' | 'application/vnd.ms-fontobject' | 'application/epub+zip' | 'application/gzip' | 'image/gif' | 'image/vnd.microsoft.icon' | 'text/calendar' | 'application/java-archive' | 'image/jpeg' | 'application/json' | 'application/ld+json' | 'audio/midi' | 'audio/x-midi' | 'audio/mpeg' | 'video/mp4' | 'video/mpeg' | 'application/vnd.apple.installer+xml' | 'application/vnd.oasis.opendocument.presentation' | 'application/vnd.oasis.opendocument.spreadsheet' | 'application/vnd.oasis.opendocument.text' | 'audio/ogg' | 'video/ogg' | 'application/ogg' | 'audio/opus' | 'font/otf' | 'image/png' | 'application/pdf' | 'application/x-httpd-php' | 'application/vnd.ms-powerpoint' | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' | 'application/vnd.rar' | 'application/rtf' | 'application/x-sh' | 'image/svg+xml' | 'application/x-tar' | 'image/tiff' | 'video/mp2t' | 'font/ttf' | 'text/plain' | 'application/vnd.visio' | 'audio/wav' | 'audio/webm' | 'video/webm' | 'image/webp' | 'font/woff' | 'font/woff2' | 'application/xhtml+xml' | 'application/vnd.ms-excel' | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' | 'text/xml' | 'application/atom+xml' | 'application/xml' | 'application/vnd.mozilla.xul+xml' | 'application/zip' | 'video/3gpp' | 'audio/3gpp' | 'video/3gpp2' | 'audio/3gpp2' | (string & Record<never, never>); } & DataKeys; type SafeScript = Pick<Script, 'id' | 'textContent'> & { type: 'application/json' | 'application/ld+json'; } & DataKeys; type SafeNoscript = Pick<Noscript, 'id' | 'textContent'> & DataKeys; interface HeadSafe extends Pick<Head, 'title' | 'titleTemplate' | 'templateParams'> { meta?: SafeMeta[]; link?: SafeLink[]; noscript?: SafeNoscript[]; script?: SafeScript[]; htmlAttrs?: SafeHtmlAttr; bodyAttrs?: SafeBodyAttr; } type UseScriptStatus = 'awaitingLoad' | 'loading' | 'loaded' | 'error' | 'removed'; /** * Either a string source for the script or full script properties. */ type UseScriptInput = string | (Omit<Script, 'src'> & { src: string; }); type UseScriptResolvedInput = Omit<Script, 'src'> & { src: string; }; type BaseScriptApi = Record<symbol | string, any>; type AsAsyncFunctionValues<T extends BaseScriptApi> = { [key in keyof T]: T[key] extends any[] ? T[key] : T[key] extends (...args: infer A) => infer R ? (...args: A) => R extends Promise<any> ? R : Promise<R> : T[key] extends Record<any, any> ? AsAsyncFunctionValues<T[key]> : never; }; interface ScriptInstance<T extends BaseScriptApi> { proxy: AsAsyncFunctionValues<T>; instance?: T; id: string; status: UseScriptStatus; entry?: ActiveHeadEntry<any>; load: () => Promise<T>; remove: () => boolean; setupTriggerHandler: (trigger: UseScriptOptions['trigger']) => void; onLoaded: (fn: (instance: T) => void | Promise<void>) => void; onError: (fn: (err?: Error) => void | Promise<void>) => void; /** * @internal */ _triggerAbortController?: AbortController | null; /** * @internal */ _triggerAbortPromise?: Promise<void>; /** * @internal */ _triggerPromises?: Promise<void>[]; /** * @internal */ _cbs: { loaded: null | ((instance: T) => void | Promise<void>)[]; error: null | ((err?: Error) => void | Promise<void>)[]; }; } type UseFunctionType<T, U> = T extends { use: infer V; } ? V extends (...args: any) => any ? ReturnType<V> : U : U; interface UseScriptOptions<T extends BaseScriptApi = {}, U = {}> extends HeadEntryOptions { /** * Resolve the script instance from the window. */ use?: () => T | undefined | null; /** * Stub the script instance. Useful for SSR or testing. */ stub?: ((ctx: { script: ScriptInstance<T>; fn: string | symbol; }) => any); /** * The trigger to load the script: * - `undefined` | `client` - (Default) Load the script on the client when this js is loaded. * - `manual` - Load the script manually by calling `$script.load()`, exists only on the client. * - `Promise` - Load the script when the promise resolves, exists only on the client. * - `Function` - Register a callback function to load the script, exists only on the client. * - `server` - Have the script injected on the server. */ trigger?: 'client' | 'server' | 'manual' | Promise<boolean | void> | ((fn: any) => any) | null; /** * Context to run events with. This is useful in Vue to attach the current instance context before * calling the event, allowing the event to be reactive. */ eventContext?: any; /** * Called before the script is initialized. Will not be triggered when the script is already loaded. This means * this is guaranteed to be called only once, unless the script is removed and re-added. */ beforeInit?: () => void; } export type { ActiveHeadEntry, AsAsyncFunctionValues, Base, BaseBodyAttr, BaseHtmlAttr, BaseMeta, BodyAttr, BodyAttributes, CreateHeadOptions, DomBeforeRenderCtx, DomRenderTagContext, DomState, EntryAugmentation, EntryResolveCtx, HasTemplateParams, Head, HeadEntry, HeadEntryOptions, HeadHooks, HeadPlugin, HeadPluginInput, HeadPluginOptions, HeadSafe, HeadTag, HeadTagKeys, HeadUtils, HookResult, HtmlAttr, HtmlAttributes, InnerContent, InnerContentVal, Link, MaybeArray, MaybeFunctionEntries, Meta, Noscript, ProcessesTemplateParams, RenderSSRHeadOptions, ResolvesDuplicates, RuntimeMode, SSRHeadPayload, SSRRenderContext, SafeBodyAttr, SafeHtmlAttr, SafeLink, SafeMeta, SafeNoscript, SafeScript, SchemaAugmentations, Script, ScriptInstance, ShouldRenderContext, SideEffectsRecord, Style, TagKey, TagPosition, TagPriority, TagUserProperties, TemplateParams, Title, TitleTemplate, Unhead, UseFunctionType, UseScriptInput, UseScriptOptions, UseScriptResolvedInput, UseScriptStatus, UseSeoMetaInput, UserAttributesConfig, UserTagConfigWithoutInnerContent, ValidTagPositions };