{"_id":"@bnomei/emdash-taki","_rev":"3-400a9cdafbdd12d5adfd721b8cc95cc8","name":"@bnomei/emdash-taki","dist-tags":{"latest":"0.1.3"},"versions":{"0.1.0":{"name":"@bnomei/emdash-taki","version":"0.1.0","keywords":["asset-map","astro","cache-busting","cloudflare","cloudflare-turnstile","cloudflare-web-analytics","cms","dynamic-metadata","emdash","emdash-plugin","emdash-taki","head","head-management","head-resolver","html-head","json-ld","metadata","open-graph","page-fragments","preload","resolver","resource-hints","runtime","seo","server-side","structured-data","stylesheet","taki","template-head","templates","third-party-scripts","turnstile","typescript","vite","waterfall","waterfall-head","web-analytics","zaraz"],"author":{"url":"https://bnomei.com","name":"Bruno Meilick","email":"b@bnomei.com"},"license":"MIT","_id":"@bnomei/emdash-taki@0.1.0","maintainers":[{"name":"bnomei","email":"b@bnomei.com"}],"homepage":"https://github.com/bnomei/emdash-taki#readme","bugs":{"url":"https://github.com/bnomei/emdash-taki/issues"},"dist":{"shasum":"b3138b6d57463861575890a3f2199fb5936d57c4","tarball":"https://registry.npmjs.org/@bnomei/emdash-taki/-/emdash-taki-0.1.0.tgz","fileCount":5,"integrity":"sha512-AvGtTkdoPPq9F4mA83MwVu5UALtR4MDtD0NnOOF1ggmphbwvb3vE60kR8tGMy90T0HZsklfxw7W+nchhAl2skw==","signatures":[{"sig":"MEYCIQDB7z+bu8NrNfLrdcx1qSwVRNDn3jO6WutN1FnWd28kAwIhANczh91oYk2cxrZtxpAOVZuMOCaa7KfB12924Hp01IgZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":78049},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.mts","engines":{"node":">=22.12.0"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"}},"gitHead":"a58fb42b374bde04b19b6f8d86ac793eedc43f12","scripts":{"build":"vp pack src/index.ts --format esm --dts --clean --tsconfig tsconfig.json","check":"vp check .","prepack":"npm run build","typecheck":"tsc --noEmit","pack:check":"vp pack src/index.ts --format esm --dts --clean --tsconfig tsconfig.json --publint","types:check":"attw --pack . --profile esm-only --no-summary","prepublishOnly":"npm run check && npm run typecheck && npm run pack:check && npm run types:check"},"_npmUser":{"name":"bnomei","email":"b@bnomei.com"},"repository":{"url":"git+https://github.com/bnomei/emdash-taki.git","type":"git"},"_npmVersion":"11.16.0","description":"Taki waterfall renderer and dynamic head helpers for EmDash, Astro, and Cloudflare sites.","directories":{},"sideEffects":false,"_nodeVersion":"26.3.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"emdash":"^0.19.0","publint":"^0.3.21","vite-plus":"^0.1.24","typescript":"^6.0.3","@arethetypeswrong/cli":"^0.18.3"},"peerDependencies":{"emdash":">=0.19.0"},"_npmOperationalInternal":{"tmp":"tmp/emdash-taki_0.1.0_1781536130139_0.47963103204027857","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@bnomei/emdash-taki","version":"0.1.1","keywords":["asset-map","astro","cache-busting","cloudflare","cloudflare-turnstile","cloudflare-web-analytics","cms","dynamic-metadata","emdash","emdash-plugin","emdash-taki","head","head-management","head-resolver","html-head","json-ld","metadata","open-graph","page-fragments","preload","resolver","resource-hints","runtime","seo","server-side","structured-data","stylesheet","taki","template-head","templates","third-party-scripts","turnstile","typescript","vite","waterfall","waterfall-head","web-analytics","zaraz"],"author":{"url":"https://bnomei.com","name":"Bruno Meilick","email":"b@bnomei.com"},"license":"MIT","_id":"@bnomei/emdash-taki@0.1.1","maintainers":[{"name":"bnomei","email":"b@bnomei.com"}],"homepage":"https://github.com/bnomei/emdash-taki#readme","bugs":{"url":"https://github.com/bnomei/emdash-taki/issues"},"dist":{"shasum":"90c5c71da8558e05cff2acc35995b0f909eee793","tarball":"https://registry.npmjs.org/@bnomei/emdash-taki/-/emdash-taki-0.1.1.tgz","fileCount":5,"integrity":"sha512-ppko3uKB1xlMLhgCr6KvlrH54uSqXGBasqUTfF0O3n1zV9pJ224pgn5bSI0CNXl9Kd09PynsHwSHLqeg1/3bKA==","signatures":[{"sig":"MEUCICJbvEqJoDX+uY4g3sRHLZenrZ7ThK/HpLHy2P1Ibx23AiEAtRH9IkVEmByX0YWfjsxUMrCwql9SjgayOiVmpccVwMI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":81147},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.mts","engines":{"node":">=22.12.0"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"}},"gitHead":"49c657a1d201ea80c09acd61af78e28e13c79b7c","scripts":{"test":"npm run build && node --test test/*.test.mjs tests/*.test.mjs","build":"vp pack src/index.ts --format esm --dts --clean --tsconfig tsconfig.json","check":"vp check .","prepack":"npm run build","typecheck":"tsc --noEmit","pack:check":"vp pack src/index.ts --format esm --dts --clean --tsconfig tsconfig.json --publint","types:check":"attw --pack . --profile esm-only --no-summary","prepublishOnly":"npm run check && npm run typecheck && npm run test && npm run pack:check && npm run types:check"},"_npmUser":{"name":"bnomei","email":"b@bnomei.com"},"repository":{"url":"git+https://github.com/bnomei/emdash-taki.git","type":"git"},"_npmVersion":"11.16.0","description":"Taki waterfall renderer and dynamic head helpers for EmDash, Astro, and Cloudflare sites.","directories":{},"sideEffects":false,"_nodeVersion":"26.3.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"npm@11.4.2","devDependencies":{"emdash":"^0.19.0","publint":"^0.3.21","vite-plus":"^0.1.24","typescript":"^6.0.3","@arethetypeswrong/cli":"^0.18.3"},"peerDependencies":{"emdash":">=0.19.0"},"_npmOperationalInternal":{"tmp":"tmp/emdash-taki_0.1.1_1781807388686_0.021726793797741895","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@bnomei/emdash-taki","version":"0.1.3","description":"Taki waterfall renderer and dynamic head helpers for EmDash, Astro, and Cloudflare sites.","keywords":["asset-map","astro","cache-busting","cloudflare","cloudflare-turnstile","cloudflare-web-analytics","cms","dynamic-metadata","emdash","emdash-plugin","emdash-taki","head","head-management","head-resolver","html-head","json-ld","metadata","open-graph","page-fragments","preload","resolver","resource-hints","runtime","seo","server-side","structured-data","stylesheet","taki","template-head","templates","third-party-scripts","turnstile","typescript","vite","waterfall","waterfall-head","web-analytics","zaraz"],"homepage":"https://github.com/bnomei/emdash-taki#readme","bugs":{"url":"https://github.com/bnomei/emdash-taki/issues"},"license":"MIT","author":{"name":"Bruno Meilick","email":"b@bnomei.com","url":"https://bnomei.com"},"repository":{"type":"git","url":"git+https://github.com/bnomei/emdash-taki.git"},"type":"module","sideEffects":false,"main":"./dist/index.mjs","types":"./dist/index.d.mts","exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"}},"publishConfig":{"access":"public"},"scripts":{"build":"vp pack src/index.ts --format esm --dts --clean --tsconfig tsconfig.json","check":"vp check .","pack:check":"vp pack src/index.ts --format esm --dts --clean --tsconfig tsconfig.json --publint","prepack":"npm run build","prepublishOnly":"npm run check && npm run typecheck && npm run test && npm run pack:check && npm run types:check","types:check":"attw --pack . --profile esm-only --no-summary","typecheck":"tsc --noEmit","test":"npm run build && node --test test/*.test.mjs tests/*.test.mjs"},"devDependencies":{"@arethetypeswrong/cli":"^0.18.3","emdash":"^0.19.0","publint":"^0.3.21","typescript":"^6.0.3","vite-plus":"^0.1.24"},"peerDependencies":{"emdash":">=0.19.0"},"engines":{"node":">=22.12.0"},"packageManager":"npm@11.4.2","gitHead":"1eac013006791414ad4bca36d67a7c0364e211db","_id":"@bnomei/emdash-taki@0.1.3","_nodeVersion":"26.4.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-x4eSayG6nP11KL8GQB5iQVlrAwCU6ghY8fvtJwy1fbA0OvxIHdY+JZlVjIoRLEbztLWXc6nnn8MdpCtCJAr6MQ==","shasum":"75ac8409f3c0ff8eb4289ab45fdfe2968a757e1b","tarball":"https://registry.npmjs.org/@bnomei/emdash-taki/-/emdash-taki-0.1.3.tgz","fileCount":5,"unpackedSize":94907,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC9cxq5bouQ2iH6SkZPvRlcP1O+aBHsPGXPg9Ss/oBBygIhANxrXHoqp32/GkA193Ha27cOi+ncxYX2qxojbjXB0pWM"}]},"_npmUser":{"name":"bnomei","email":"b@bnomei.com"},"directories":{},"maintainers":[{"name":"bnomei","email":"b@bnomei.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/emdash-taki_0.1.3_1782734959311_0.2871014282665929"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-15T15:08:49.989Z","modified":"2026-06-29T12:09:19.548Z","0.1.0":"2026-06-15T15:08:50.276Z","0.1.1":"2026-06-18T18:29:48.818Z","0.1.3":"2026-06-29T12:09:19.456Z"},"bugs":{"url":"https://github.com/bnomei/emdash-taki/issues"},"author":{"name":"Bruno Meilick","email":"b@bnomei.com","url":"https://bnomei.com"},"license":"MIT","homepage":"https://github.com/bnomei/emdash-taki#readme","keywords":["asset-map","astro","cache-busting","cloudflare","cloudflare-turnstile","cloudflare-web-analytics","cms","dynamic-metadata","emdash","emdash-plugin","emdash-taki","head","head-management","head-resolver","html-head","json-ld","metadata","open-graph","page-fragments","preload","resolver","resource-hints","runtime","seo","server-side","structured-data","stylesheet","taki","template-head","templates","third-party-scripts","turnstile","typescript","vite","waterfall","waterfall-head","web-analytics","zaraz"],"repository":{"type":"git","url":"git+https://github.com/bnomei/emdash-taki.git"},"description":"Taki waterfall renderer and dynamic head helpers for EmDash, Astro, and Cloudflare sites.","maintainers":[{"name":"bnomei","email":"b@bnomei.com"}],"readme":"# @bnomei/emdash-taki\n\n[![npm version](https://img.shields.io/npm/v/@bnomei/emdash-taki.svg)](https://www.npmjs.com/package/@bnomei/emdash-taki)\n[![npm downloads](https://img.shields.io/npm/dm/@bnomei/emdash-taki.svg)](https://www.npmjs.com/package/@bnomei/emdash-taki)\n[![license](https://img.shields.io/npm/l/@bnomei/emdash-taki.svg)](https://www.npmjs.com/package/@bnomei/emdash-taki)\n[![types](https://img.shields.io/badge/types-included-blue.svg)](./package.json)\n[![source](https://img.shields.io/badge/source-GitHub-181717.svg?logo=github)](https://github.com/bnomei/emdash-taki)\n\nHTML head waterfall renderer and dynamic helpers for EmDash, Astro, and\nCloudflare sites.\n\n`@bnomei/emdash-taki` provides dynamic, server-resolved head contributions with\nstatic fallbacks and a strict resource waterfall renderer. The goal is to keep\nshared head policy in one native EmDash plugin, compute page-specific metadata\non the server, and leave template-local rendering in Astro.\n\n## What You Get\n\n- Template-based server-resolved metadata with static fallbacks.\n- Typed helpers for metadata, JSON-LD, resource hints, CSS, scripts, icons, and\n  feeds.\n- A `renderTakiStart()` helper that renders critical resource hints\n  before EmDash's normal metadata output.\n- Optional fragment, Cloudflare, cache-busting, and waterfall helpers for\n  special cases.\n\n## Security and raw helper trust boundaries\n\nMost typed helpers construct HTML for you and escape generated attributes or text\nwhere appropriate. The raw helpers `htmlFragment()`, `inlineScript()`, and\n`inlineStyle()` are deliberate escape hatches for content your application\nalready trusts. Do not pass user-generated, CMS-authored, request-derived, or\nthird-party values to them unless that data has been validated and sanitized for\nthe exact HTML, JavaScript, or CSS context first.\n\nSee [SECURITY.md](./SECURITY.md) for the full raw-helper trust-boundary guidance.\n\n## Install\n\n```sh\nnpm install @bnomei/emdash-taki\n```\n\n## Quick Start: Template Taki Files With Static Fallbacks\n\nPut stable site-wide rules in `astro.config.mjs`, point `emdash-taki` at a\nruntime file, then add one file per template in `src/taki/`. When `runtime` is\nconfigured, template loading is automatic unless `templates: false` is set.\n\n### 1. Register Static Rules\n\n```js\nimport emdash from \"emdash/astro\";\nimport takiPlugin, * as taki from \"@bnomei/emdash-taki\";\n\nconst rules = [\n  taki.meta(\"theme-color\", \"#101820\"),\n  taki.meta(\"description\", \"Default description\"),\n  taki.property(\"og:title\", \"Example\"),\n  taki.deferScript(\"/scripts/app.js\"),\n  taki.icon(\"/favicon.svg\", { type: \"image/svg+xml\" }),\n];\n\nexport default {\n  integrations: [\n    emdash({\n      plugins: [\n        takiPlugin({\n          runtime: \"./src/emdash-taki-runtime.ts\",\n          capabilities: [\"content:read\"],\n          rules,\n        }),\n      ],\n    }),\n  ],\n};\n```\n\n### 2. Export the Runtime\n\nCreate the file referenced by `runtime`: `src/emdash-taki-runtime.ts`. EmDash\nloads this native module and expects it to export `createPlugin`.\n`defineTakiRuntime()` creates that export from a Vite glob.\n\n```ts\nimport { defineTakiRuntime } from \"@bnomei/emdash-taki\";\n\nexport const createPlugin = defineTakiRuntime(\n  import.meta.glob(\"./taki/*.{ts,js}\", { eager: true }),\n);\n```\n\n### 3. Add Template Taki Files\n\nEach file name maps to `page.pageType`: `src/taki/article.ts` handles the\n`article` template, `src/taki/product.ts` handles `product`, and so on.\n\n```ts\n// src/taki/article.ts\nimport { jsonLd, meta, property } from \"@bnomei/emdash-taki\";\n\nexport default async function articleTaki({ page, ctx }) {\n  if (!page.content || !ctx.content) return null;\n\n  const entry = await ctx.content.get(page.content.collection, page.content.id);\n  if (!entry) return null;\n\n  const title = String(entry.data.title ?? page.title ?? \"\");\n  const description = String(entry.seo?.description ?? page.description ?? \"\");\n\n  return [\n    meta(\"description\", description),\n    property(\"og:title\", title),\n    jsonLd(\"article\", {\n      \"@context\": \"https://schema.org\",\n      \"@type\": \"Article\",\n      headline: title,\n      url: page.url,\n    }),\n  ];\n}\n```\n\nTemplate modules can export `default`, `taki`, `<template>`, `<template>Taki`,\nor a single function export. For `src/taki/article.ts`, all of these are valid:\n`default`, `taki`, `article`, or `articleTaki`.\n\nThe naming split is from EmDash's native plugin contract:\n\n- `takiPlugin()` is what you register in `astro.config.mjs`.\n- `createPlugin` is the export EmDash loads from `src/emdash-taki-runtime.ts`.\n- `defineTakiRuntime()` builds that `createPlugin` export from your template\n  modules.\n\n### 4. Render Taki\n\nEdit the Astro layout that already renders your page `<head>`, usually\n`src/layouts/Base.astro` or the equivalent site shell. Keep the usual Astro and\nEmDash tags, and add the waterfall helper immediately before `EmDashHead`.\n\n```astro\n---\nimport { EmDashHead, EmDashBodyStart, EmDashBodyEnd } from \"emdash/ui\";\nimport { renderTakiStart } from \"@bnomei/emdash-taki\";\nconst resolvedTitle = pageContext.title ?? \"Example\";\nconst taki = await renderTakiStart(pageContext, Astro.locals);\n---\n\n<head>\n  <meta charset=\"utf-8\" />\n  <meta name=\"viewport\" content=\"width=device-width\" />\n  <title>{resolvedTitle}</title>\n  <Fragment set:html={taki} />\n  <EmDashHead page={pageContext} />\n</head>\n<body>\n  <EmDashBodyStart page={pageContext} />\n  <slot />\n  <EmDashBodyEnd page={pageContext} />\n</body>\n```\n\nThis gives you static fallback metadata, server-resolved page metadata, stable\ndedupe, and strict ordering for resource helpers. The usual first tags\n`charset`, `viewport`, and `title` stay in your layout. `renderTakiStart()`\nonly moves `emdash-taki`'s early resource fragments before `EmDashHead`; it also\nremoves those fragments from EmDash's cached fragment list so `EmDashHead` does\nnot render duplicates.\n\nTo disable automatic template loading, set `templates: false` and remove the\nglob from the runtime file:\n\n```js\ntakiPlugin({\n  runtime: \"./src/emdash-taki-runtime.ts\",\n  templates: false,\n  rules,\n});\n```\n\n## Strict Waterfall Renderer\n\nThe default renderer is `renderTakiStart()`. It complements EmDash's\nstock `EmDashHead`, so existing layouts only need one extra HTML line before\n`EmDashHead`.\n\nThe helper renders in this order:\n\n```html\n<!-- Your normal Astro layout -->\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width\" />\n<title>...</title>\n\n<!-- renderTakiStart() -->\n<link rel=\"preconnect\" href=\"...\" />\n<link rel=\"stylesheet\" href=\"...\" />\n<script src=\"...\" defer></script>\n\n<!-- EmDashHead -->\n<meta name=\"description\" content=\"...\" />\n<meta property=\"og:title\" content=\"...\" />\n<link rel=\"canonical\" href=\"...\" />\n<script type=\"application/ld+json\">\n  ...\n</script>\n\n<!-- Site identity and late fragments -->\n<link rel=\"icon\" href=\"...\" />\n```\n\nThe early group is controlled by the `emdash-taki` helpers.\n`renderTakiStart()` renders those early fragments and removes them from\nthe EmDash fragment cache before stock `EmDashHead` runs, so the same stylesheet,\npreload, or script is not emitted twice.\n\n### Page cache assumptions\n\n`emdash-taki` resolves plugin contributions once per EmDash page context object.\nThe native plugin keeps an in-memory `WeakMap` keyed by the exact `page` object\nthat EmDash passes to `page:metadata` and `page:fragments`, so metadata and\nfragments share one resolver pass when both hooks run for the same page object.\n\nThis cache is intentionally per plugin instance and per page object identity:\n\n- Reusing the same `page` object for multiple EmDash hook calls reuses the same\n  pending or fulfilled resolver promise.\n- Creating a new page context object, even with identical values, triggers a new\n  resolver pass.\n- Resolver output should therefore depend on the supplied `page`, runtime\n  options, and stable request context. Do not rely on resolver side effects\n  running separately for metadata and fragments on the same page object.\n- Treat the `page` object as immutable for the duration of a request. Because\n  the cache is keyed by object identity, mutating `page` fields (for example\n  `page.title`) after the first hook call does **not** re-run resolvers; the\n  first resolution is reused. If contributions must change, pass a new page\n  context object instead of mutating the existing one.\n- Because the cache uses `WeakMap`, entries can be garbage-collected after the\n  page object is no longer referenced by the host runtime.\n\nThese helpers default to `phase: \"early\"` because Harry Roberts' head waterfall\nputs resource discovery before SEO/social metadata:\n\n- `preconnect()`\n- `dnsPrefetch()`\n- `asyncScript()`\n- `blockingScript()`\n- `inlineStyle()`\n- `stylesheet()`\n- `preload()`\n- `deferScript()`\n- `prefetch()`\n- `prerender()`\n- `baseHref()`\n\nThese helpers stay late unless you pass `{ phase: \"early\" }`:\n\n- `externalScript()`\n- `inlineScript()`\n- `htmlFragment()`\n- `icon()`\n- `manifest()`\n- `feed()`\n- Cloudflare helpers\n\nUse stock `EmDashHead` when a project only needs metadata and does not care\nabout strict resource discovery order. With stock `EmDashHead`, all head\nfragments still render after typed metadata because that is EmDash core's\ncurrent render order.\n\nFor full control, use `renderTaki()` instead of `EmDashHead`. That\nhelper renders basics, early fragments, metadata, site identity, and late\nfragments in one HTML string. This is the advanced path for layouts that do not\nwant the stock EmDash head component at all.\n\n## Static Fallbacks and Constants\n\nUse static rules for site-wide constants, resource hints, and fallback values.\nTemplate files run after those rules, so dynamic template output can overwrite\nfallbacks with the same dedupe key.\n\n```js\nrules: [\n  taki.meta(\"theme-color\", \"#101820\", {\n    key: \"theme-color\",\n  }),\n  taki.meta(\"description\", \"Default description\", {\n    key: \"description\",\n  }),\n];\n```\n\nTemplate handlers and resolvers can return an ordered rule array, or an object\nwith `rules`, `metadata`, `fragments`, and `assetMap`.\n\n```ts\nreturn [\n  meta(\"robots\", \"noindex\", {\n    key: \"robots\",\n  }),\n  jsonLd(\"custom\", graph, {\n    key: \"custom-jsonld\",\n  }),\n];\n```\n\nIf template files return fragments and no static fragment rule already exists,\nopt automatic templates into the fragment hook:\n\n```js\ntakiPlugin({\n  runtime: \"./src/emdash-taki-runtime.ts\",\n  templates: { fragments: true },\n  rules,\n});\n```\n\nUse `capabilities` on `takiPlugin()` for anything the resolver needs from\n`ctx`, such as `content:read`, `media:read`, or `network:request`. Use\n`allowedHosts` with `network:request`.\n\n## Template, Collection, and URI Rules\n\nAutomatic template loading uses `page.pageType`. Use explicit rules only when\nyou want to restrict, rename, or add a non-template case.\n\n```js\n// Explicit template rule. Equivalent to the automatic pageType dispatch.\ntaki.template(\"article\");\n\n// Match a route section that is not cleanly expressed as a template.\ntaki.resolve({\n  when: { pathPrefix: \"/docs/\" },\n  input: { type: \"docs\" },\n});\n\n// Static assets can still use matchers.\ntaki.stylesheet(\"/styles/docs.css\", {\n  when: { collection: \"docs\" },\n});\n```\n\nMatchers can target `kind`, `pageType`, `collection`, `locale`, exact `path`,\nor `pathPrefix`. Keep the default path template-first. Use collection, kind, or\npath matching when the template name is not precise enough.\n\n## Advanced: Cache-Busted Assets\n\nNative plugin options are JSON data, so `rules` cannot contain Astro imported\nasset modules. Most projects should use public URLs, external URLs, or Astro\nimports in the template itself. Use `assetMap` only when a host, cache layer, or\nresolver already knows the final built URL.\n\n```js\nconst assetMap = {\n  \"src/styles/global.css\": \"/_astro/global.D7a8Qx4k.css\",\n  \"src/scripts/app.js\": \"/_astro/app.Dq8nT6pL.js\",\n  \"src/fonts/app.woff2\": \"/_astro/app.Dz3u1K2f.woff2\",\n};\n\ntakiPlugin({\n  assetMap,\n  rules: [\n    taki.stylesheet(\"src/styles/global.css\"),\n    taki.preload(\"src/fonts/app.woff2\", \"font\", {\n      type: \"font/woff2\",\n      crossorigin: true,\n    }),\n    taki.deferScript(\"src/scripts/app.js\"),\n  ],\n});\n```\n\nThe helper argument is the lookup key. If `assetMap` contains that key, the\nfinal cached URL is emitted. If the key is missing, the literal value is emitted\nbecause `emdash-taki` cannot invent the hashed URL.\n\nResolvers can also return an `assetMap` when URLs are only known at runtime.\nThe returned map is merged for the whole resolved page, so later collection uses\nit for static rules and resolver-returned rules.\n\nUse Astro instead when the asset needs Astro component semantics: scoped styles,\ncomponent scripts, ESM imports, image transforms, or template-local data.\n\n## Resource Helpers\n\nResource helpers emit fragments and default to `placement: \"head\"`. Use them in\nstatic `rules` or return them from a template file that opts into fragments.\nWith `renderTakiStart()`, resource-discovery helpers marked\n`phase: \"early\"` render before SEO/social metadata.\n\n```js\nrules: [\n  taki.preconnect(\"https://fonts.gstatic.com\", { crossorigin: true }),\n  taki.stylesheet(\"src/styles/global.css\"),\n  taki.preload(\"src/fonts/app.woff2\", \"font\", { crossorigin: true }),\n  taki.deferScript(\"src/scripts/app.js\"),\n  taki.icon(\"/favicon.svg\", { type: \"image/svg+xml\" }),\n  taki.manifest(\"/site.webmanifest\"),\n];\n```\n\n### Waterfall Order\n\nUse this order for strict browser resource discovery:\n\n```js\nrules: [\n  // 1. Early origin hints\n  taki.preconnect(\"https://fonts.gstatic.com\", { crossorigin: true }),\n  taki.dnsPrefetch(\"https://analytics.example.com\"),\n\n  // 2. Early async scripts, only when they need discovery before CSS\n  taki.asyncScript(\"src/scripts/app-async.js\"),\n\n  // 3. Blocking scripts, only when blocking is intentional\n  taki.blockingScript(\"src/scripts/app-blocking.js\"),\n\n  // 4. Critical inline CSS, then external CSS\n  taki.inlineStyle(\":root { color-scheme: light dark; }\", {\n    key: \"critical-css\",\n  }),\n  taki.stylesheet(\"src/styles/global.css\"),\n\n  // 5. Preload assets needed soon\n  taki.preload(\"src/fonts/app.woff2\", \"font\", {\n    type: \"font/woff2\",\n    crossorigin: true,\n  }),\n\n  // 6. Deferred scripts\n  taki.deferScript(\"src/scripts/app-defer.js\"),\n\n  // 7. Future navigation hints\n  taki.prefetch(\"/next-page/\"),\n  taki.prerender(\"/next-page/\"),\n\n  // 8. Favicons, app icons, manifests, feeds, and other stable extras\n  taki.icon(\"/favicon.svg\", { type: \"image/svg+xml\" }),\n  taki.manifest(\"/site.webmanifest\"),\n  taki.feed(\"/feed.xml\", { title: \"RSS\" }),\n];\n```\n\nIf a resource hint, stylesheet, or script must appear before all metadata for\nperformance reasons, add `renderTakiStart()` immediately before\nEmDash's stock `EmDashHead`.\n\n## Cloudflare Snippets\n\nUse the Cloudflare helpers when the site wants these snippets controlled by the\nsame head policy.\n\n```js\nrules: [\n  taki.cloudflareZaraz(),\n  taki.cloudflareTurnstile({ render: \"explicit\", preconnect: true }),\n  taki.cloudflareWebAnalytics(\"YOUR_TOKEN\"),\n];\n```\n\n## Runtime Boundaries\n\nThe serialized `takiPlugin({ rules })` side is intentionally data-only because\nEmDash writes native plugin options into the runtime module. Do not pass\nfunctions, class instances, Astro components, or imported asset objects in\n`rules`.\n\nFor simple dynamic SEO values, prefer the EmDash page context first.\nEmDash's stock `EmDashHead` derives base description, canonical, Open Graph,\nTwitter Card, article metadata, and primary JSON-LD from the current page\ncontext, with plugin metadata able to override by key. The advanced\n`renderTaki()` helper mirrors that behavior when replacing `EmDashHead`\nentirely.\n\nFor custom dynamic JSON-LD or fragments that depend on data outside the page\ncontext, use template Taki files plus the native runtime wrapper. Render the\nitem in Astro only when it depends on template-local data that should remain in\nthat template, or when the asset/component should stay inside Astro's own import\nand rendering pipeline.\n\nFor strict waterfall-critical resources, prefer `renderTakiStart()`\nimmediately before stock `EmDashHead`. Use the advanced `renderTaki()`\nhelper only when the layout wants to replace `EmDashHead` entirely.\n\nTemplates and resolvers are metadata-only by default. Use\n`templates: { fragments: true }`, `taki.templates({ fragments: true })`, or\n`fragments: true` on a resolver rule when dynamic output can include page\nfragments and no static fragment helper already registers the fragment hook.\n\nAutomatic template dispatch calls the matching template handler directly from\nthe page hooks. It does not issue an internal HTTP request to the same site. If\nyou also need a plugin API route for admin preview or debugging, expose a route\nin your wrapper and call the same handler from that route.\n\n## Option Reference\n\nEvery helper returns a plain JSON-serializable rule object. Native plugin\noptions are serialized by EmDash when it generates the runtime plugin module, so\ndo not pass functions, class instances, or runtime-only objects in `rules`.\n\n### Common Options\n\n`takiPlugin()` accepts:\n\n- `allowedHosts`: host allowlist used by `ctx.http` when `network:request` is\n  declared.\n- `assetMap`: serializable lookup from stable asset key to final cached URL.\n- `capabilities`: additional EmDash capabilities needed by server resolvers.\n- `runtime`: native runtime wrapper module used when registering resolvers.\n- `priority`: EmDash hook priority.\n- `rules`: ordered head rules.\n- `templates`: `true` by default when `runtime` is set; use `false` to disable\n  automatic template dispatch, or pass options such as `{ fragments: true }`.\n\nAll rules accept:\n\n- `key`: stable dedupe key.\n- `when`: page matcher object or array of matcher objects.\n\nFragment helpers also accept:\n\n- `placement`: `\"head\"`, `\"body:start\"`, or `\"body:end\"`.\n- `phase`: `\"early\"` or `\"late\"` for `renderTakiStart()` and\n  `renderTaki()`. Early head\n  fragments render before metadata.\n\nThe waterfall helpers default to `placement: \"head\"` unless documented\notherwise.\n\nURL fields in typed helpers resolve through `assetMap` first. Missing entries\nfall back to the literal value.\n\nWithin `emdash-taki`, later matching contributions with the same dedupe key\noverwrite earlier ones. This is how `taki.resolve()` can override static\nfallbacks while still returning a clean first-wins list to EmDash.\n\n### `defineTakiRuntime(runtime)`\n\nBuilds the `createPlugin` export for the runtime file referenced by\n`takiPlugin({ runtime })`.\n\n```ts\nexport const createPlugin = defineTakiRuntime(\n  import.meta.glob(\"./taki/*.{ts,js}\", { eager: true }),\n);\n```\n\nThe export must be named `createPlugin` because EmDash loads that symbol from\nthe native runtime module. Pass a Vite glob map for the default template-file\nworkflow. Template names are inferred from file names, and each module can\nexport `default`, `taki`, `<template>`, `<template>Taki`, or a single function\nexport.\n\nUse explicit maps or named resolvers only when the project needs more control:\n\n```ts\nexport const createPlugin = defineTakiRuntime({\n  templates: {\n    article: async ({ page, ctx }) => {\n      return [property(\"og:type\", \"article\")];\n    },\n  },\n  resolvers: {\n    productTaki: async ({ page, ctx }) => {\n      return [property(\"og:type\", \"product\")];\n    },\n  },\n});\n```\n\n### `renderTakiStart(page, locals)`\n\nReturns only the early resource-discovery fragments. Use this before stock\n`EmDashHead`.\n\n```astro\n---\nimport { EmDashHead } from \"emdash/ui\";\nimport { renderTakiStart } from \"@bnomei/emdash-taki\";\n\nconst taki = await renderTakiStart(pageContext, Astro.locals);\n---\n\n<Fragment set:html={taki} />\n<EmDashHead page={pageContext} />\n```\n\nArguments:\n\n- `page`: EmDash public page context.\n- `locals`: Astro locals, used to read the EmDash page runtime.\n\nCall this before `EmDashHead` with the same `pageContext` object. The helper\nremoves early fragments from EmDash's cached fragment list after rendering them,\nso stock `EmDashHead` still renders metadata, site identity, and late fragments\nwithout duplicating early resources.\n\n`renderTakiStart()` only emits early page fragments, which it reads from the\nEmDash page runtime in `locals`. When `locals` carries no runtime (for example a\npartially initialized `Astro.locals`), or when there are no early fragments, it\nreturns an empty string. Unlike `renderTaki()` — which renders `basics` and\nfallback SEO metadata from the `page` context even without a runtime — there is\nno standalone fallback here, because early resource hints are defined by the\nruntime. If you see no early output, verify that EmDash populated the page\nruntime on `locals`.\n\n### `renderTaki(page, locals, options)`\n\nReturns the full `<head>` contribution HTML that replaces EmDash's stock\n`EmDashHead`. This is the full-control path.\n\n```astro\n---\nimport { renderTaki } from \"@bnomei/emdash-taki\";\n\nconst headHtml = await renderTaki(pageContext, Astro.locals, {\n  basics: true,\n});\n---\n\n<Fragment set:html={headHtml} />\n```\n\nOptions:\n\n- `page`: EmDash public page context.\n- `locals`: Astro locals, used to read the EmDash page runtime.\n- `basics`: renders `<meta charset=\"utf-8\">`, viewport, and title from the page\n  context.\n- `charset`: `true` uses `utf-8`, a string overrides it, `false` disables it.\n- `viewport`: `true` uses `width=device-width`, a string overrides it, `false`\n  disables it.\n- `title`: `true` uses the page title, a string overrides it, `false` disables\n  it.\n\nThis helper replaces `EmDashHead`. Keep using `EmDashBodyStart` and\n`EmDashBodyEnd` from `emdash/ui`.\n\n### `template(name, options)`\n\nRuns the template dispatcher for one template name.\n\n```js\ntaki.template(\"article\");\ntaki.template(\"product\", { fragments: true });\n```\n\nBy default this matches `when: { pageType: name }` and passes\n`input: { template: name }` to the template dispatcher. Use this when automatic\ntemplate dispatch is disabled or when one template needs custom options.\n\n### `templates(options)`\n\nRuns the global template dispatcher.\n\n```js\ntaki.templates({ fragments: true });\n```\n\nWhen `takiPlugin({ runtime })` is configured, this rule is added automatically\nunless `templates: false` is set or an explicit template rule already exists.\nThe dispatcher uses `page.pageType` to select the matching template module.\n\n### `resolve(options)`\n\nRuns the default server resolver from the native runtime wrapper.\n\n```js\ntaki.resolve({\n  when: { pageType: \"article\" },\n  input: { type: \"article\" },\n});\ntaki.resolve({\n  when: { collection: \"products\" },\n  input: { type: \"product\" },\n});\n```\n\nUse this for non-template dynamic cases where the value cannot be expressed as\nstatic JSON in `astro.config.mjs`, but can be computed from `event.page`,\n`ctx.content`, `ctx.media`, `ctx.kv`, or another server-side EmDash context API.\nThe resolver input must be serializable.\n\nA `resolve()` rule does **not** suppress automatic template dispatch: with\n`takiPlugin({ runtime })`, the automatic `templates()` rule is still added unless\n`templates: false` is set or an explicit template rule exists. This is\nintentional — the default resolver and the template dispatcher are independent,\nand their contributions merge with last-wins dedupe, so you can combine custom\n`resolve()` logic with per-`pageType` template files. If you want a `resolve()`\nrule to fully replace automatic templates, set `templates: false` and add the\ntemplate dispatch explicitly where you need it.\n\nOptions:\n\n- `fragments`: set to `true` when this resolver can return page fragments and no\n  other static fragment rule registers the fragment hook.\n- `input`: JSON-serializable resolver input.\n- `onError`: `ignore` by default; use `throw` to fail the hook when a resolver\n  fails.\n\n### `resolve(resolver, options)`\n\nRuns a named server resolver from the native runtime wrapper.\n\n```js\ntaki.resolve(\"productTaki\", {\n  when: { collection: \"products\" },\n  input: { type: \"product\" },\n  onError: \"ignore\",\n});\n```\n\nUse this only when the runtime wrapper registers multiple resolvers. The\nresolver name and `input` must be serializable.\n\nOptions:\n\n- `fragments`: set to `true` when this resolver can return page fragments and no\n  other static fragment rule registers the fragment hook.\n- `input`: JSON-serializable resolver input.\n- `onError`: `ignore` by default; use `throw` to fail the hook when a resolver\n  fails.\n\n### `preconnect(href, options)`\n\nEmits `<link rel=\"preconnect\">`.\n\n```js\ntaki.preconnect(\"https://fonts.gstatic.com\", { crossorigin: true });\n```\n\nUse this before the browser discovers a critical cross-origin request. Avoid\nspraying preconnects for origins that are not needed immediately.\n\n### `dnsPrefetch(href, options)`\n\nEmits `<link rel=\"dns-prefetch\">`.\n\n```js\ntaki.dnsPrefetch(\"https://analytics.example.com\");\n```\n\nUse this for lower-priority origins where resolving DNS early is useful but a\nfull connection is too expensive.\n\n### `asyncScript(src, options)`\n\nEmits an external script with `async`.\n\n```js\ntaki.asyncScript(\"/vendor/app-async.js\");\n```\n\nUse only for scripts that can execute independently. `async` can still compete\nwith CSS discovery or inject more work, so keep it early only when early\ndiscovery is intentional. `src` resolves through `assetMap` when present.\n\n### `blockingScript(src, options)`\n\nEmits an external script with no `async` or `defer`.\n\n```js\ntaki.blockingScript(\"/vendor/app-blocking.js\");\n```\n\nThis blocks parsing. Use it only when the page truly depends on the script\nbefore rendering continues. `src` resolves through `assetMap` when present.\n\n### `inlineStyle(css, options)`\n\nEmits a `<style>` fragment.\n\n```js\ntaki.inlineStyle(\":root { color-scheme: light dark; }\", {\n  key: \"critical-css\",\n});\n```\n\nUse this for small, trusted critical CSS. It is a deliberate escape hatch: the\nCSS is emitted as raw global CSS and is not scoped, sanitized, bundled, deduped,\nor transformed by Astro. Do not interpolate untrusted values into inline CSS.\n\n### `stylesheet(href, options)`\n\nEmits `<link rel=\"stylesheet\">`.\n\n```js\ntaki.stylesheet(\"/styles/global.css\");\n```\n\nStylesheets are render-blocking. Keep scripts that do not need to run before CSS\nout of the gap between related stylesheets. Use public or remote URLs directly,\nor use `assetMap` when a cache layer already knows the final built CSS URL. For\ncomponent-scoped CSS or CSS that Astro should still discover and bundle from an\nimport, keep it in Astro.\n\n### `preload(href, as, options)`\n\nEmits `<link rel=\"preload\">`.\n\n```js\ntaki.preload(\"/fonts/app.woff2\", \"font\", {\n  type: \"font/woff2\",\n  crossorigin: true,\n});\n```\n\nUse this for assets needed soon by the current navigation. Preload has a cost;\ndo not use it for speculative assets. `href` resolves through `assetMap` when\npresent.\n\n### `deferScript(src, options)`\n\nEmits an external script with `defer`.\n\n```js\ntaki.deferScript(\"/vendor/app-defer.js\");\n```\n\nUse this for first-party scripts that should download during parsing and execute\nafter the document is parsed. Use `assetMap` when a cache layer already knows\nthe final built script URL. For component scripts that Astro should still\ndiscover, bundle, and dedupe from the template, keep them in Astro.\n\n### `prefetch(href, options)`\n\nEmits `<link rel=\"prefetch\">`.\n\n```js\ntaki.prefetch(\"/next-page/\");\n```\n\nUse this for likely future navigations or assets. It should come after current\npage critical work. `href` resolves through `assetMap` when present.\n\n### `prerender(href, options)`\n\nEmits `<link rel=\"prerender\">`.\n\n```js\ntaki.prerender(\"/next-page/\");\n```\n\nUse this only when the next navigation is highly likely and the page is safe to\npre-render. `href` resolves through `assetMap` when present.\n\n### `icon(href, options)`\n\nEmits `<link rel=\"icon\">`.\n\n```js\ntaki.icon(\"/favicon.svg\", { type: \"image/svg+xml\" });\n```\n\nUse this for favicons and app icons. EmDash site identity may also render a\nfavicon from site settings, so set stable `key` values if you need predictable\ndedupe. `href` resolves through `assetMap` when present.\n\n### `manifest(href, options)`\n\nEmits `<link rel=\"manifest\">`.\n\n```js\ntaki.manifest(\"/site.webmanifest\");\n```\n\nUse this for web app manifests and related static app metadata. `href` resolves\nthrough `assetMap` when present.\n\n### `feed(href, options)`\n\nEmits an RSS feed link.\n\n```js\ntaki.feed(\"/feed.xml\", { title: \"RSS\" });\n```\n\nThe helper emits `rel=\"alternate\"` and defaults `type` to\n`application/rss+xml`. `href` resolves through `assetMap` when present.\n\n### `linkTag(rel, href, options)`\n\nEmits a generic `<link>` fragment.\n\n```js\ntaki.linkTag(\"license\", \"https://example.com/license\");\n```\n\nUse this when no dedicated helper exists. For EmDash metadata rels such as\n`canonical`, `alternate`, `author`, `license`, `nlweb`, and\n`site.standard.document`, prefer the typed `link()` helper instead. `href`\nresolves through `assetMap` when present.\n\n### `baseHref(href, options)`\n\nEmits `<base href=\"...\">`.\n\n```js\ntaki.baseHref(\"https://example.com/\");\n```\n\nUse cautiously. Current EmDash core metadata and site identity render before\nhead fragments, so this cannot affect URLs that EmDash has already emitted.\n`href` resolves through `assetMap` when present.\n\n### `meta(name, content, options)`\n\nAdds a typed EmDash `meta` contribution.\n\n```js\ntaki.meta(\"robots\", \"noindex\", {\n  key: \"robots\",\n  when: { pathPrefix: \"/preview\" },\n});\n```\n\nUse this for standard name/content tags such as `robots`, `description`,\n`theme-color`, and similar page-level metadata.\n\n### `property(property, content, options)`\n\nAdds a typed EmDash property contribution.\n\n```js\ntaki.property(\"og:title\", \"Example Article\", {\n  key: \"og:title\",\n  when: { pageType: \"article\" },\n});\n```\n\nUse this for Open Graph and other property/content metadata.\n\n### `link(rel, href, options)`\n\nAdds a typed EmDash metadata link contribution.\n\n```js\ntaki.link(\"canonical\", \"https://example.com/articles/example\");\ntaki.link(\"alternate\", \"https://example.com/de/articles/example\", {\n  hreflang: \"de\",\n});\n```\n\nSupported rel values are `canonical`, `alternate`, `author`, `license`,\n`nlweb`, and `site.standard.document`. EmDash only renders metadata link `href`\nvalues with safe absolute schemes: `http://`, `https://`, or `at://`. `href`\nresolves through `assetMap` when present, but metadata links should usually stay\nabsolute.\n\nThis is the metadata helper. For stylesheet, preload, preconnect, prefetch,\nprerender, icon, manifest, and feed tags, use the fragment helpers above.\n\n### `jsonLd(id, graph, options)`\n\nAdds a typed EmDash JSON-LD contribution.\n\n```js\ntaki.jsonLd(\"organization\", {\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"Organization\",\n  name: \"Example\",\n  url: \"https://example.com\",\n});\n```\n\nUse a stable `id` so the JSON-LD block can be deduped predictably. Static\nJSON-LD objects are safely serialized by EmDash. JSON-LD that depends on\nserver-side data belongs in `taki.resolve()`. Keep it in Astro only when it\ndepends on template-local data.\n\n### `siteStandardDocument(href, options)`\n\nAdds EmDash's allowlisted `site.standard.document` metadata link.\n\n```js\ntaki.siteStandardDocument(\"https://example.com/.well-known/site-standard.json\");\n```\n\nThe URL must use a safe absolute scheme.\n\n### `nlweb(href, options)`\n\nAdds EmDash's allowlisted `nlweb` metadata link.\n\n```js\ntaki.nlweb(\"https://example.com/.well-known/nlweb.json\");\n```\n\nThe URL must use a safe absolute scheme.\n\n### `externalScript(src, options)`\n\nAdds a generic external script fragment.\n\n```js\ntaki.externalScript(\"https://cdn.example.com/widget.js\", { defer: true });\n```\n\nUse the specific `asyncScript()`, `blockingScript()`, or `deferScript()` helpers\nwhen the waterfall semantics matter. `src` resolves through `assetMap` when\npresent. Default placement is `\"head\"`.\n\n### `inlineScript(code, options)`\n\nAdds an inline script fragment.\n\n```js\ntaki.inlineScript(\"window.exampleConfig = { enabled: true };\", {\n  key: \"example-config\",\n});\n```\n\nUse sparingly. This is a deliberate escape hatch for trusted JavaScript: the\ncode is emitted as an inline script and is not sanitized by Taki. Inline scripts\nin `<head>` can block parsing and interfere with stylesheet discovery when\nplaced between CSS resources. Default placement is `\"head\"`.\n\n### `htmlFragment(html, options)`\n\nAdds a raw HTML fragment.\n\n```js\ntaki.htmlFragment('<meta name=\"vendor-verification\" content=\"abc123\">', {\n  key: \"vendor-verification\",\n});\n```\n\nUse this when EmDash has no typed primitive or dedicated helper yet. This is a\ndeliberate escape hatch for trusted markup: raw HTML is emitted as-is, is not\nsanitized, and is not scanned for `assetMap` replacements. Default placement is\n`\"head\"`.\n\n### `cloudflareWebAnalytics(token, options)`\n\nAdds Cloudflare Web Analytics.\n\n```js\ntaki.cloudflareWebAnalytics(\"YOUR_TOKEN\");\n```\n\nDefault placement is `body:end`, matching Cloudflare's manual snippet placement\nbefore `</body>`. Options:\n\n- `placement`: `body:end` by default; `\"head\"` is available but usually not\n  needed.\n- `src`: override the default beacon URL.\n- `spa`: writes Cloudflare's SPA flag into `data-cf-beacon`.\n- `attributes`: extra script attributes.\n\n### `cloudflareZaraz(options)`\n\nAdds Cloudflare Zaraz manual loading.\n\n```js\ntaki.cloudflareZaraz();\n```\n\nDefault placement is `\"head\"`, with source `/cdn-cgi/zaraz/i.js`. This is for\nsites where Cloudflare's Zaraz auto-inject option is disabled. Cloudflare places\nmanual Zaraz loading immediately before `</head>`, which matches the late\n`<EmDashHead />` slot.\n\nOptions:\n\n- `placement`: `\"head\"` by default; `body:end` is available for custom setups.\n- `src`: override `/cdn-cgi/zaraz/i.js`.\n- `referrerPolicy`: defaults to `origin`.\n- `attributes`: extra script attributes.\n\n### `cloudflareTurnstile(options)`\n\nAdds Cloudflare Turnstile.\n\n```js\ntaki.cloudflareTurnstile({ render: \"explicit\", preconnect: true });\n```\n\nDefault placement is `\"head\"`, with source\n`https://challenges.cloudflare.com/turnstile/v0/api.js`.\n\nOptions:\n\n- `render`: `implicit` by default. Use `explicit` to append `?render=explicit`.\n- `preconnect`: emits a preconnect to `https://challenges.cloudflare.com`.\n- `placement`: `\"head\"` by default; `body:end` is available for custom setups.\n- `attributes`: extra script attributes.\n\n## Matching\n\nEvery rule accepts `when` to limit where it applies:\n\n```js\ntaki.meta(\"robots\", \"noindex\", {\n  when: { pathPrefix: \"/preview\" },\n});\n\ntaki.jsonLd(\"article-extra\", graph, {\n  when: { pageType: \"article\", collection: [\"posts\", \"newsletters\"] },\n});\n```\n\nSupported match fields:\n\n- `kind`\n- `pageType`\n- `collection`\n- `locale`\n- `path`\n- `pathPrefix`\n\nArrays match any value. Multiple matcher objects also match any object.\n\n## Ordering, Dedupe, and Scope\n\nEmDash renders plugin metadata before site and base metadata. Since EmDash\nmetadata dedupe is first-wins, `emdash-taki` rules can override defaults when\nthey use the same metadata key. Non-canonical metadata links dedupe by `rel` plus an explicit `key`, `hreflang`, or `href`; canonical links always share the `link:canonical` key so one canonical URL wins.\n\nFragment order follows rule order within the fragment group. Metadata and\nfragments are separate groups in current EmDash core, so resource-order-sensitive\ntags should use the fragment helpers.\n\nGeneric response headers, cache tags, redirects, and Cloudflare request\npersonalization are intentionally out of scope for this package. Use Astro\nmiddleware or Worker code for those surfaces.\n\n## Research Notes\n\nThe waterfall is based on Harry Roberts' \"Get Your Head Straight\" guidance and\n`ct.css` diagnostics: the document head is render-critical, async scripts can\nstill affect CSS discovery, and SEO/social metadata can live later after\ncritical resource discovery. Astro changes the implementation detail, not the\nbrowser constraint: this package centralizes the head waterfall as EmDash rules\nwhile still using Astro for the layout shell.\n\nReferences:\n\n- [Get Your Head Straight](https://speakerdeck.com/csswizardry/get-your-head-straight)\n- [CSS and Network Performance](https://www.smashingmagazine.com/2021/09/css-head-tag/)\n- [ct.css](https://csswizardry.com/ct/)\n- [Astro client-side scripts](https://docs.astro.build/en/guides/client-side-scripts/)\n- [Astro styling and bundle control](https://docs.astro.build/en/guides/styling/)\n- [Cloudflare Zaraz manual loading](https://developers.cloudflare.com/zaraz/advanced/load-zaraz-manually/)\n- [Cloudflare Web Analytics setup](https://developers.cloudflare.com/web-analytics/get-started/)\n\n## Package Surface\n\n- ESM entry: `@bnomei/emdash-taki`.\n- Type declarations are included from `dist/`.\n- Peer dependency: `emdash` `>=0.19.0`.\n\n## Status\n\nThis package ships as a native EmDash plugin because trusted page fragments run\nas first-party browser code. Structured metadata rules remain compatible with\nEmDash's `page:metadata` contribution model.\n\n## License\n\nMIT.\n","readmeFilename":"README.md"}