@growth-labs/seo 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -134,6 +134,11 @@ export default {
134
134
  - `/podcast.xml`, `/listen.xml`
135
135
  - `POST /_seo/revalidate` — CMS webhook target (when `onDemandRevalidation: true`)
136
136
 
137
+ **Head-tag component** (`<AeoHead />`):
138
+ - Consumers import `import AeoHead from '@growth-labs/seo/components/AeoHead.astro'` and render `<AeoHead />` inside their layout's `<head>`. Required because integrations can't directly inject arbitrary HTML into every page's `<head>`.
139
+ - Emits the Apple News discovery link (`<link rel="alternate" type="application/rss+xml">`) when `appleNews.discoveryLink: true`.
140
+ - Emits a per-page markdown twin link (`<link rel="alternate" type="text/markdown" href="<twinUrl>">`) for discoverability on prerendered HTML served by Cloudflare Assets where middleware doesn't run.
141
+
137
142
  **Build-time:**
138
143
  - Emits `.md` twins + summary twins for public items (static/both modes) under `dist/client/`.
139
144
  - Validates hreflang reciprocity.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growth-labs/seo",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -40,10 +40,12 @@
40
40
  "./cron": {
41
41
  "types": "./dist/cron/prune-aeo-r2.d.ts",
42
42
  "import": "./dist/cron/prune-aeo-r2.js"
43
- }
43
+ },
44
+ "./components/AeoHead.astro": "./src/components/AeoHead.astro"
44
45
  },
45
46
  "files": [
46
47
  "dist",
48
+ "src/components",
47
49
  "README.md"
48
50
  ],
49
51
  "publishConfig": {
@@ -0,0 +1,61 @@
1
+ ---
2
+ // Side-effect: seeds state in whatever environment Vite bundles for.
3
+ // Required so `getConfig()` works in the Cloudflare prerender Worker.
4
+ import 'virtual:growth-labs/seo/config'
5
+
6
+ import { resolveAeoTwins } from '../options.js'
7
+ import { getConfig } from '../state.js'
8
+
9
+ export interface Props {
10
+ /**
11
+ * Override the URL this page resolves to for twin-link emission.
12
+ * Defaults to `Astro.url`. Useful when rendering head tags for a URL that
13
+ * differs from the request URL (e.g. canonicalizing away trailing slash).
14
+ */
15
+ canonical?: URL | string
16
+ }
17
+
18
+ const { canonical } = Astro.props
19
+ const config = getConfig()
20
+ const aeo = resolveAeoTwins(config.aeoTwins)
21
+
22
+ // ─── Apple News discovery link ───
23
+ const appleNews = config.appleNews
24
+ const appleNewsHref =
25
+ appleNews?.enabled && appleNews.discoveryLink
26
+ ? `${config.site.replace(/\/$/, '')}${appleNews.feedPath}`
27
+ : null
28
+
29
+ // ─── Per-page markdown twin link (rel="alternate") ───
30
+ // Addresses the gap where prerendered HTML is served directly by Cloudflare
31
+ // Assets — middleware never runs, so the Link: HTTP header it would append
32
+ // is absent. Embedding a <link rel="alternate"> in <head> gives static hosts
33
+ // the same AEO discoverability signal as middleware-backed modes.
34
+ const pageUrl = (() => {
35
+ if (!canonical) return Astro.url.toString()
36
+ if (typeof canonical === 'string') return canonical
37
+ return canonical.toString()
38
+ })()
39
+
40
+ function defaultTwinUrl(url: string): string {
41
+ return `${url.replace(/\/+$/, '')}.md`
42
+ }
43
+
44
+ const twinHref =
45
+ aeo && aeo.mode !== 'middleware'
46
+ ? (aeo.twinUrl ?? defaultTwinUrl)(pageUrl)
47
+ : null
48
+ ---
49
+
50
+ {appleNewsHref && (
51
+ <link
52
+ rel="alternate"
53
+ type="application/rss+xml"
54
+ title={`${appleNews?.channelName} on Apple News`}
55
+ href={appleNewsHref}
56
+ />
57
+ )}
58
+
59
+ {twinHref && (
60
+ <link rel="alternate" type="text/markdown" href={twinHref} />
61
+ )}