@alepha/react 0.14.4 → 0.15.1

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.
Files changed (82) hide show
  1. package/README.md +10 -0
  2. package/dist/auth/index.browser.js +603 -242
  3. package/dist/auth/index.browser.js.map +1 -1
  4. package/dist/auth/index.d.ts +2 -2
  5. package/dist/auth/index.d.ts.map +1 -1
  6. package/dist/auth/index.js +1317 -952
  7. package/dist/auth/index.js.map +1 -1
  8. package/dist/core/index.d.ts +17 -17
  9. package/dist/core/index.d.ts.map +1 -1
  10. package/dist/core/index.js +20 -20
  11. package/dist/core/index.js.map +1 -1
  12. package/dist/form/index.d.ts +9 -10
  13. package/dist/form/index.d.ts.map +1 -1
  14. package/dist/form/index.js +15 -15
  15. package/dist/form/index.js.map +1 -1
  16. package/dist/head/index.browser.js +20 -0
  17. package/dist/head/index.browser.js.map +1 -1
  18. package/dist/head/index.d.ts +62 -64
  19. package/dist/head/index.d.ts.map +1 -1
  20. package/dist/head/index.js +20 -0
  21. package/dist/head/index.js.map +1 -1
  22. package/dist/i18n/index.d.ts +9 -9
  23. package/dist/i18n/index.d.ts.map +1 -1
  24. package/dist/i18n/index.js.map +1 -1
  25. package/dist/router/index.browser.js +605 -244
  26. package/dist/router/index.browser.js.map +1 -1
  27. package/dist/router/index.d.ts +100 -111
  28. package/dist/router/index.d.ts.map +1 -1
  29. package/dist/router/index.js +1317 -952
  30. package/dist/router/index.js.map +1 -1
  31. package/dist/websocket/index.d.ts +0 -1
  32. package/dist/websocket/index.d.ts.map +1 -1
  33. package/package.json +6 -6
  34. package/src/auth/__tests__/$auth.spec.ts +164 -150
  35. package/src/auth/index.ts +9 -3
  36. package/src/auth/services/ReactAuth.ts +15 -5
  37. package/src/core/hooks/useAction.ts +1 -2
  38. package/src/core/index.ts +4 -4
  39. package/src/form/errors/FormValidationError.ts +4 -6
  40. package/src/form/hooks/useFormState.ts +1 -1
  41. package/src/form/index.ts +1 -1
  42. package/src/form/services/FormModel.ts +31 -25
  43. package/src/head/helpers/SeoExpander.ts +2 -1
  44. package/src/head/hooks/useHead.spec.tsx +2 -2
  45. package/src/head/index.browser.ts +2 -2
  46. package/src/head/index.ts +4 -4
  47. package/src/head/interfaces/Head.ts +15 -3
  48. package/src/head/primitives/$head.ts +2 -5
  49. package/src/head/providers/BrowserHeadProvider.ts +55 -0
  50. package/src/head/providers/HeadProvider.ts +4 -1
  51. package/src/i18n/__tests__/integration.spec.tsx +1 -1
  52. package/src/i18n/components/Localize.spec.tsx +2 -2
  53. package/src/i18n/hooks/useI18n.browser.spec.tsx +2 -2
  54. package/src/i18n/index.ts +1 -1
  55. package/src/i18n/primitives/$dictionary.ts +1 -1
  56. package/src/i18n/providers/I18nProvider.spec.ts +1 -1
  57. package/src/i18n/providers/I18nProvider.ts +1 -1
  58. package/src/router/__tests__/page-head-browser.browser.spec.ts +5 -1
  59. package/src/router/__tests__/page-head.spec.ts +11 -7
  60. package/src/router/__tests__/seo-head.spec.ts +7 -3
  61. package/src/router/atoms/ssrManifestAtom.ts +2 -11
  62. package/src/router/components/ErrorViewer.tsx +626 -167
  63. package/src/router/components/Link.tsx +4 -2
  64. package/src/router/components/NestedView.tsx +7 -9
  65. package/src/router/components/NotFound.tsx +2 -2
  66. package/src/router/hooks/useQueryParams.ts +1 -1
  67. package/src/router/hooks/useRouter.ts +1 -1
  68. package/src/router/hooks/useRouterState.ts +1 -1
  69. package/src/router/index.browser.ts +10 -11
  70. package/src/router/index.shared.ts +7 -7
  71. package/src/router/index.ts +10 -7
  72. package/src/router/primitives/$page.browser.spec.tsx +6 -1
  73. package/src/router/primitives/$page.spec.tsx +7 -1
  74. package/src/router/primitives/$page.ts +5 -9
  75. package/src/router/providers/ReactBrowserProvider.ts +17 -6
  76. package/src/router/providers/ReactBrowserRouterProvider.ts +1 -1
  77. package/src/router/providers/ReactPageProvider.ts +4 -3
  78. package/src/router/providers/ReactServerProvider.ts +32 -50
  79. package/src/router/providers/ReactServerTemplateProvider.ts +336 -155
  80. package/src/router/providers/SSRManifestProvider.ts +17 -60
  81. package/src/router/services/ReactPageService.ts +4 -1
  82. package/src/router/services/ReactRouter.ts +6 -5
@@ -319,9 +319,29 @@ var BrowserHeadProvider = class {
319
319
  link = document.createElement("link");
320
320
  link.setAttribute("rel", rel);
321
321
  link.setAttribute("href", href);
322
+ if (it.type) link.setAttribute("type", it.type);
323
+ if (it.as) link.setAttribute("as", it.as);
324
+ if (it.crossorigin != null) link.setAttribute("crossorigin", "");
322
325
  document.head.appendChild(link);
323
326
  }
324
327
  }
328
+ if (head.script) for (const it of head.script) this.renderScriptTag(document, it);
329
+ }
330
+ renderScriptTag(document, script) {
331
+ const el = document.createElement("script");
332
+ if (typeof script === "string") {
333
+ el.textContent = script;
334
+ document.head.appendChild(el);
335
+ return;
336
+ }
337
+ const { content, ...attrs } = script;
338
+ if (attrs.src) {
339
+ if (document.querySelector(`script[src="${attrs.src}"]`)) return;
340
+ }
341
+ for (const [key, value] of Object.entries(attrs)) if (value === true) el.setAttribute(key, "");
342
+ else if (value !== void 0 && value !== false) el.setAttribute(key, String(value));
343
+ if (content) el.textContent = content;
344
+ document.head.appendChild(el);
325
345
  }
326
346
  renderMetaTag(document, meta) {
327
347
  const { content } = meta;
@@ -1 +1 @@
1
- {"version":3,"file":"index.browser.js","names":[],"sources":["../../src/head/helpers/SeoExpander.ts","../../src/head/providers/HeadProvider.ts","../../src/head/primitives/$head.ts","../../src/head/providers/BrowserHeadProvider.ts","../../src/head/hooks/useHead.ts","../../src/head/index.browser.ts"],"sourcesContent":["import type { Head, HeadMeta } from \"../interfaces/Head.ts\";\n\n/**\n * Expands Head configuration into SEO meta tags.\n *\n * Generates:\n * - `<meta name=\"description\">` from head.description\n * - `<meta property=\"og:*\">` OpenGraph tags\n * - `<meta name=\"twitter:*\">` Twitter Card tags\n *\n * @example\n * ```ts\n * const helper = new SeoExpander();\n * const { meta, link } = helper.expand({\n * title: \"My App\",\n * description: \"Build amazing apps\",\n * image: \"https://example.com/og.png\",\n * url: \"https://example.com/\",\n * });\n * ```\n */\nexport class SeoExpander {\n public expand(head: Head): {\n meta: HeadMeta[];\n link: Array<{ rel: string; href: string }>;\n } {\n const meta: HeadMeta[] = [];\n const link: Array<{ rel: string; href: string }> = [];\n\n // Only expand SEO if there's meaningful content beyond just title\n const hasSeoContent =\n head.description ||\n head.image ||\n head.url ||\n head.siteName ||\n head.locale ||\n head.type ||\n head.og ||\n head.twitter;\n\n if (!hasSeoContent) {\n return { meta, link };\n }\n\n // Base description\n if (head.description) {\n meta.push({ name: \"description\", content: head.description });\n }\n\n // Canonical URL\n if (head.url) {\n link.push({ rel: \"canonical\", href: head.url });\n }\n\n // OpenGraph tags\n this.expandOpenGraph(head, meta);\n\n // Twitter Card tags\n this.expandTwitter(head, meta);\n\n return { meta, link };\n }\n\n protected expandOpenGraph(head: Head, meta: HeadMeta[]): void {\n const ogTitle = head.og?.title ?? head.title;\n const ogDescription = head.og?.description ?? head.description;\n const ogImage = head.og?.image ?? head.image;\n\n if (head.type || ogTitle) {\n meta.push({ property: \"og:type\", content: head.type ?? \"website\" });\n }\n if (head.url) {\n meta.push({ property: \"og:url\", content: head.url });\n }\n if (ogTitle) {\n meta.push({ property: \"og:title\", content: ogTitle });\n }\n if (ogDescription) {\n meta.push({ property: \"og:description\", content: ogDescription });\n }\n if (ogImage) {\n meta.push({ property: \"og:image\", content: ogImage });\n if (head.imageWidth) {\n meta.push({\n property: \"og:image:width\",\n content: String(head.imageWidth),\n });\n }\n if (head.imageHeight) {\n meta.push({\n property: \"og:image:height\",\n content: String(head.imageHeight),\n });\n }\n if (head.imageAlt) {\n meta.push({ property: \"og:image:alt\", content: head.imageAlt });\n }\n }\n if (head.siteName) {\n meta.push({ property: \"og:site_name\", content: head.siteName });\n }\n if (head.locale) {\n meta.push({ property: \"og:locale\", content: head.locale });\n }\n }\n\n protected expandTwitter(head: Head, meta: HeadMeta[]): void {\n const twitterTitle = head.twitter?.title ?? head.title;\n const twitterDescription = head.twitter?.description ?? head.description;\n const twitterImage = head.twitter?.image ?? head.image;\n\n if (head.twitter?.card || twitterTitle || twitterImage) {\n meta.push({\n name: \"twitter:card\",\n content:\n head.twitter?.card ?? (twitterImage ? \"summary_large_image\" : \"summary\"),\n });\n }\n if (head.url) {\n meta.push({ name: \"twitter:url\", content: head.url });\n }\n if (twitterTitle) {\n meta.push({ name: \"twitter:title\", content: twitterTitle });\n }\n if (twitterDescription) {\n meta.push({ name: \"twitter:description\", content: twitterDescription });\n }\n if (twitterImage) {\n meta.push({ name: \"twitter:image\", content: twitterImage });\n if (head.imageAlt) {\n meta.push({ name: \"twitter:image:alt\", content: head.imageAlt });\n }\n }\n if (head.twitter?.site) {\n meta.push({ name: \"twitter:site\", content: head.twitter.site });\n }\n if (head.twitter?.creator) {\n meta.push({ name: \"twitter:creator\", content: head.twitter.creator });\n }\n }\n}\n","import { $inject } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { SeoExpander } from \"../helpers/SeoExpander.ts\";\nimport type { Head } from \"../interfaces/Head.ts\";\n\n/**\n * Provides methods to fill and merge head information into the application state.\n *\n * Used both on server and client side to manage document head.\n *\n * @see {@link SeoExpander}\n * @see {@link ServerHeadProvider}\n * @see {@link BrowserHeadProvider}\n */\nexport class HeadProvider {\n protected readonly log = $logger();\n protected readonly seoExpander = $inject(SeoExpander);\n\n public global?: Array<Head | (() => Head)> = [];\n\n /**\n * Track if we've warned about page-level htmlAttributes to avoid spam.\n */\n protected warnedAboutHtmlAttributes = false;\n\n /**\n * Resolve global head configuration (from $head primitives only).\n *\n * This is used to get htmlAttributes early, before page loaders run.\n * Only htmlAttributes from global $head are allowed; page-level htmlAttributes\n * are ignored for early streaming optimization.\n *\n * @returns Merged global head with htmlAttributes\n */\n public resolveGlobalHead(): Head {\n const head: Head = {};\n\n for (const h of this.global ?? []) {\n const resolved = typeof h === \"function\" ? h() : h;\n if (resolved.htmlAttributes) {\n head.htmlAttributes = {\n ...head.htmlAttributes,\n ...resolved.htmlAttributes,\n };\n }\n }\n\n return head;\n }\n\n public fillHead(state: HeadState) {\n state.head = {\n ...state.head,\n };\n\n for (const h of this.global ?? []) {\n const head = typeof h === \"function\" ? h() : h;\n this.mergeHead(state, head);\n }\n\n for (const layer of state.layers) {\n if (layer.route?.head && !layer.error) {\n this.fillHeadByPage(layer.route, state, layer.props ?? {});\n }\n }\n }\n\n protected mergeHead(state: HeadState, head: Head): void {\n // Expand SEO fields into meta tags\n const { meta, link } = this.seoExpander.expand(head);\n state.head = {\n ...state.head,\n ...head,\n meta: [...(state.head.meta ?? []), ...meta, ...(head.meta ?? [])],\n link: [...(state.head.link ?? []), ...link, ...(head.link ?? [])],\n script: [...(state.head.script ?? []), ...(head.script ?? [])],\n };\n }\n\n protected fillHeadByPage(\n page: HeadRoute,\n state: HeadState,\n props: Record<string, any>,\n ): void {\n if (!page.head) {\n return;\n }\n\n state.head ??= {};\n\n const head =\n typeof page.head === \"function\"\n ? page.head(props, state.head)\n : page.head;\n\n // Expand SEO fields into meta tags\n const { meta, link } = this.seoExpander.expand(head);\n state.head.meta = [...(state.head.meta ?? []), ...meta];\n state.head.link = [...(state.head.link ?? []), ...link];\n\n if (head.title) {\n state.head ??= {};\n\n if (state.head.titleSeparator) {\n state.head.title = `${head.title}${state.head.titleSeparator}${state.head.title}`;\n } else {\n state.head.title = head.title;\n }\n\n state.head.titleSeparator = head.titleSeparator;\n }\n\n // htmlAttributes from pages are ignored for early streaming optimization.\n // Only global $head can set htmlAttributes.\n if (head.htmlAttributes && !this.warnedAboutHtmlAttributes) {\n this.warnedAboutHtmlAttributes = true;\n this.log.warn(\n \"Page-level htmlAttributes are ignored. Use global $head() for htmlAttributes instead, \" +\n \"as they are sent before page loaders run for early streaming optimization.\",\n );\n }\n\n if (head.bodyAttributes) {\n state.head.bodyAttributes = {\n ...state.head.bodyAttributes,\n ...head.bodyAttributes,\n };\n }\n\n if (head.meta) {\n state.head.meta = [...(state.head.meta ?? []), ...(head.meta ?? [])];\n }\n\n if (head.link) {\n state.head.link = [...(state.head.link ?? []), ...(head.link ?? [])];\n }\n\n if (head.script) {\n state.head.script = [...(state.head.script ?? []), ...(head.script ?? [])];\n }\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Minimal route interface for head processing.\n * Avoids circular dependency with @alepha/react/router.\n */\ninterface HeadRoute {\n head?: Head | ((props: Record<string, any>, previous?: Head) => Head);\n}\n\n/**\n * Minimal state interface for head processing.\n * Avoids circular dependency with @alepha/react/router.\n */\ninterface HeadState {\n head: Head;\n layers: Array<{\n route?: HeadRoute;\n props?: Record<string, any>;\n error?: Error;\n }>;\n}\n","import { $inject, createPrimitive, Primitive, KIND } from \"alepha\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"../providers/HeadProvider.ts\";\n\n/**\n * Set global `<head>` options for the application.\n */\nexport const $head = (options: HeadPrimitiveOptions) => {\n return createPrimitive(HeadPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type HeadPrimitiveOptions = Head | (() => Head);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class HeadPrimitive extends Primitive<HeadPrimitiveOptions> {\n protected readonly provider = $inject(HeadProvider);\n protected onInit() {\n this.provider.global = [\n ...(this.provider.global ?? []),\n this.options,\n ];\n }\n}\n\n$head[KIND] = HeadPrimitive;\n","import { $inject, Alepha } from \"alepha\";\nimport type { Head, HeadMeta } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"./HeadProvider.ts\";\n\n/**\n * Browser-side head provider that manages document head elements.\n *\n * Used by ReactBrowserProvider and ReactBrowserRouterProvider to update\n * document title, meta tags, and other head elements during client-side\n * navigation.\n */\nexport class BrowserHeadProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly headProvider = $inject(HeadProvider);\n\n protected get document(): Document {\n return window.document;\n }\n\n /**\n * Fill head state from route configurations and render to document.\n * Combines fillHead from HeadProvider with renderHead to the DOM.\n *\n * Only runs in browser environment - no-op on server.\n */\n public fillAndRenderHead(state: { head: Head; layers: Array<any> }): void {\n // Skip on server-side\n if (!this.alepha.isBrowser()) {\n return;\n }\n\n this.headProvider.fillHead(state as any);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\n }\n\n public getHead(document: Document): Head {\n return {\n get title() {\n return document.title;\n },\n get htmlAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.documentElement.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get bodyAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.body.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get meta() {\n const metas: HeadMeta[] = [];\n // Get meta tags with name attribute\n for (const meta of document.head.querySelectorAll(\"meta[name]\")) {\n const name = meta.getAttribute(\"name\");\n const content = meta.getAttribute(\"content\");\n if (name && content) {\n metas.push({ name, content });\n }\n }\n // Get meta tags with property attribute (OpenGraph)\n for (const meta of document.head.querySelectorAll(\"meta[property]\")) {\n const property = meta.getAttribute(\"property\");\n const content = meta.getAttribute(\"content\");\n if (property && content) {\n metas.push({ property, content });\n }\n }\n return metas;\n },\n };\n }\n\n public renderHead(document: Document, head: Head): void {\n if (head.title) {\n document.title = head.title;\n }\n\n if (head.bodyAttributes) {\n for (const [key, value] of Object.entries(head.bodyAttributes)) {\n if (value) {\n document.body.setAttribute(key, value);\n } else {\n document.body.removeAttribute(key);\n }\n }\n }\n\n if (head.htmlAttributes) {\n for (const [key, value] of Object.entries(head.htmlAttributes)) {\n if (value) {\n document.documentElement.setAttribute(key, value);\n } else {\n document.documentElement.removeAttribute(key);\n }\n }\n }\n\n if (head.meta) {\n for (const it of head.meta) {\n this.renderMetaTag(document, it);\n }\n }\n\n if (head.link) {\n for (const it of head.link) {\n const { rel, href } = it;\n let link = document.querySelector(`link[rel=\"${rel}\"][href=\"${href}\"]`);\n if (!link) {\n link = document.createElement(\"link\");\n link.setAttribute(\"rel\", rel);\n link.setAttribute(\"href\", href);\n document.head.appendChild(link);\n }\n }\n }\n }\n\n protected renderMetaTag(document: Document, meta: HeadMeta): void {\n const { content } = meta;\n\n // Handle OpenGraph tags (property attribute)\n if (meta.property) {\n const existing = document.querySelector(\n `meta[property=\"${meta.property}\"]`,\n );\n if (existing) {\n existing.setAttribute(\"content\", content);\n } else {\n const newMeta = document.createElement(\"meta\");\n newMeta.setAttribute(\"property\", meta.property);\n newMeta.setAttribute(\"content\", content);\n document.head.appendChild(newMeta);\n }\n return;\n }\n\n // Handle standard meta tags (name attribute)\n if (meta.name) {\n const existing = document.querySelector(`meta[name=\"${meta.name}\"]`);\n if (existing) {\n existing.setAttribute(\"content\", content);\n } else {\n const newMeta = document.createElement(\"meta\");\n newMeta.setAttribute(\"name\", meta.name);\n newMeta.setAttribute(\"content\", content);\n document.head.appendChild(newMeta);\n }\n }\n }\n}\n","import { useInject } from \"@alepha/react\";\nimport { Alepha } from \"alepha\";\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { BrowserHeadProvider } from \"../providers/BrowserHeadProvider.ts\";\n\n/**\n * ```tsx\n * const App = () => {\n * const [head, setHead] = useHead({\n * // will set the document title on the first render\n * title: \"My App\",\n * });\n *\n * return (\n * // This will update the document title when the button is clicked\n * <button onClick={() => setHead({ title: \"Change Title\" })}>\n * Change Title {head.title}\n * </button>\n * );\n * }\n * ```\n */\nexport const useHead = (options?: UseHeadOptions): UseHeadReturn => {\n const alepha = useInject(Alepha);\n\n const current = useMemo(() => {\n if (!alepha.isBrowser()) {\n return {};\n }\n\n return alepha.inject(BrowserHeadProvider).getHead(window.document);\n }, []);\n\n const setHead = useCallback((head?: Head | ((previous?: Head) => Head)) => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n alepha\n .inject(BrowserHeadProvider)\n .renderHead(\n window.document,\n typeof head === \"function\" ? head(current) : head || {},\n );\n }, []);\n\n useEffect(() => {\n if (options) {\n setHead(options);\n }\n }, []);\n\n return [current, setHead];\n};\n\nexport type UseHeadOptions = Head | ((previous?: Head) => Head);\n\nexport type UseHeadReturn = [\n Head,\n (head?: Head | ((previous?: Head) => Head)) => void,\n];\n","import { AlephaReact } from \"@alepha/react\";\nimport { $module } from \"alepha\";\nimport { $head } from \"./primitives/$head.ts\";\nimport { BrowserHeadProvider } from \"./providers/BrowserHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$head.ts\";\nexport * from \"./hooks/useHead.ts\";\nexport * from \"./interfaces/Head.ts\";\nexport * from \"./helpers/SeoExpander.ts\";\nexport * from \"./providers/BrowserHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Alepha React Head Module\n *\n * @see {@link BrowserHeadProvider}\n * @module alepha.react.head\n */\nexport const AlephaReactHead = $module({\n name: \"alepha.react.head\",\n primitives: [$head],\n services: [AlephaReact, BrowserHeadProvider],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.browser.js","names":[],"sources":["../../src/head/helpers/SeoExpander.ts","../../src/head/providers/HeadProvider.ts","../../src/head/primitives/$head.ts","../../src/head/providers/BrowserHeadProvider.ts","../../src/head/hooks/useHead.ts","../../src/head/index.browser.ts"],"sourcesContent":["import type { Head, HeadMeta } from \"../interfaces/Head.ts\";\n\n/**\n * Expands Head configuration into SEO meta tags.\n *\n * Generates:\n * - `<meta name=\"description\">` from head.description\n * - `<meta property=\"og:*\">` OpenGraph tags\n * - `<meta name=\"twitter:*\">` Twitter Card tags\n *\n * @example\n * ```ts\n * const helper = new SeoExpander();\n * const { meta, link } = helper.expand({\n * title: \"My App\",\n * description: \"Build amazing apps\",\n * image: \"https://example.com/og.png\",\n * url: \"https://example.com/\",\n * });\n * ```\n */\nexport class SeoExpander {\n public expand(head: Head): {\n meta: HeadMeta[];\n link: Array<{ rel: string; href: string }>;\n } {\n const meta: HeadMeta[] = [];\n const link: Array<{ rel: string; href: string }> = [];\n\n // Only expand SEO if there's meaningful content beyond just title\n const hasSeoContent =\n head.description ||\n head.image ||\n head.url ||\n head.siteName ||\n head.locale ||\n head.type ||\n head.og ||\n head.twitter;\n\n if (!hasSeoContent) {\n return { meta, link };\n }\n\n // Base description\n if (head.description) {\n meta.push({ name: \"description\", content: head.description });\n }\n\n // Canonical URL\n if (head.url) {\n link.push({ rel: \"canonical\", href: head.url });\n }\n\n // OpenGraph tags\n this.expandOpenGraph(head, meta);\n\n // Twitter Card tags\n this.expandTwitter(head, meta);\n\n return { meta, link };\n }\n\n protected expandOpenGraph(head: Head, meta: HeadMeta[]): void {\n const ogTitle = head.og?.title ?? head.title;\n const ogDescription = head.og?.description ?? head.description;\n const ogImage = head.og?.image ?? head.image;\n\n if (head.type || ogTitle) {\n meta.push({ property: \"og:type\", content: head.type ?? \"website\" });\n }\n if (head.url) {\n meta.push({ property: \"og:url\", content: head.url });\n }\n if (ogTitle) {\n meta.push({ property: \"og:title\", content: ogTitle });\n }\n if (ogDescription) {\n meta.push({ property: \"og:description\", content: ogDescription });\n }\n if (ogImage) {\n meta.push({ property: \"og:image\", content: ogImage });\n if (head.imageWidth) {\n meta.push({\n property: \"og:image:width\",\n content: String(head.imageWidth),\n });\n }\n if (head.imageHeight) {\n meta.push({\n property: \"og:image:height\",\n content: String(head.imageHeight),\n });\n }\n if (head.imageAlt) {\n meta.push({ property: \"og:image:alt\", content: head.imageAlt });\n }\n }\n if (head.siteName) {\n meta.push({ property: \"og:site_name\", content: head.siteName });\n }\n if (head.locale) {\n meta.push({ property: \"og:locale\", content: head.locale });\n }\n }\n\n protected expandTwitter(head: Head, meta: HeadMeta[]): void {\n const twitterTitle = head.twitter?.title ?? head.title;\n const twitterDescription = head.twitter?.description ?? head.description;\n const twitterImage = head.twitter?.image ?? head.image;\n\n if (head.twitter?.card || twitterTitle || twitterImage) {\n meta.push({\n name: \"twitter:card\",\n content:\n head.twitter?.card ??\n (twitterImage ? \"summary_large_image\" : \"summary\"),\n });\n }\n if (head.url) {\n meta.push({ name: \"twitter:url\", content: head.url });\n }\n if (twitterTitle) {\n meta.push({ name: \"twitter:title\", content: twitterTitle });\n }\n if (twitterDescription) {\n meta.push({ name: \"twitter:description\", content: twitterDescription });\n }\n if (twitterImage) {\n meta.push({ name: \"twitter:image\", content: twitterImage });\n if (head.imageAlt) {\n meta.push({ name: \"twitter:image:alt\", content: head.imageAlt });\n }\n }\n if (head.twitter?.site) {\n meta.push({ name: \"twitter:site\", content: head.twitter.site });\n }\n if (head.twitter?.creator) {\n meta.push({ name: \"twitter:creator\", content: head.twitter.creator });\n }\n }\n}\n","import { $inject } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { SeoExpander } from \"../helpers/SeoExpander.ts\";\nimport type { Head } from \"../interfaces/Head.ts\";\n\n/**\n * Provides methods to fill and merge head information into the application state.\n *\n * Used both on server and client side to manage document head.\n *\n * @see {@link SeoExpander}\n * @see {@link ServerHeadProvider}\n * @see {@link BrowserHeadProvider}\n */\nexport class HeadProvider {\n protected readonly log = $logger();\n protected readonly seoExpander = $inject(SeoExpander);\n\n public global?: Array<Head | (() => Head)> = [];\n\n /**\n * Track if we've warned about page-level htmlAttributes to avoid spam.\n */\n protected warnedAboutHtmlAttributes = false;\n\n /**\n * Resolve global head configuration (from $head primitives only).\n *\n * This is used to get htmlAttributes early, before page loaders run.\n * Only htmlAttributes from global $head are allowed; page-level htmlAttributes\n * are ignored for early streaming optimization.\n *\n * @returns Merged global head with htmlAttributes\n */\n public resolveGlobalHead(): Head {\n const head: Head = {};\n\n for (const h of this.global ?? []) {\n const resolved = typeof h === \"function\" ? h() : h;\n if (resolved.htmlAttributes) {\n head.htmlAttributes = {\n ...head.htmlAttributes,\n ...resolved.htmlAttributes,\n };\n }\n }\n\n return head;\n }\n\n public fillHead(state: HeadState) {\n state.head = {\n ...state.head,\n };\n\n for (const h of this.global ?? []) {\n const head = typeof h === \"function\" ? h() : h;\n this.mergeHead(state, head);\n }\n\n for (const layer of state.layers) {\n if (layer.route?.head && !layer.error) {\n this.fillHeadByPage(layer.route, state, layer.props ?? {});\n }\n }\n }\n\n protected mergeHead(state: HeadState, head: Head): void {\n // Expand SEO fields into meta tags\n const { meta, link } = this.seoExpander.expand(head);\n state.head = {\n ...state.head,\n ...head,\n meta: [...(state.head.meta ?? []), ...meta, ...(head.meta ?? [])],\n link: [...(state.head.link ?? []), ...link, ...(head.link ?? [])],\n script: [...(state.head.script ?? []), ...(head.script ?? [])],\n };\n }\n\n protected fillHeadByPage(\n page: HeadRoute,\n state: HeadState,\n props: Record<string, any>,\n ): void {\n if (!page.head) {\n return;\n }\n\n state.head ??= {};\n\n const head =\n typeof page.head === \"function\"\n ? page.head(props, state.head)\n : page.head;\n\n // Expand SEO fields into meta tags\n const { meta, link } = this.seoExpander.expand(head);\n state.head.meta = [...(state.head.meta ?? []), ...meta];\n state.head.link = [...(state.head.link ?? []), ...link];\n\n if (head.title) {\n state.head ??= {};\n\n if (state.head.titleSeparator) {\n state.head.title = `${head.title}${state.head.titleSeparator}${state.head.title}`;\n } else {\n state.head.title = head.title;\n }\n\n state.head.titleSeparator = head.titleSeparator;\n }\n\n // htmlAttributes from pages are ignored for early streaming optimization.\n // Only global $head can set htmlAttributes.\n if (head.htmlAttributes && !this.warnedAboutHtmlAttributes) {\n this.warnedAboutHtmlAttributes = true;\n this.log.warn(\n \"Page-level htmlAttributes are ignored. Use global $head() for htmlAttributes instead, \" +\n \"as they are sent before page loaders run for early streaming optimization.\",\n );\n }\n\n if (head.bodyAttributes) {\n state.head.bodyAttributes = {\n ...state.head.bodyAttributes,\n ...head.bodyAttributes,\n };\n }\n\n if (head.meta) {\n state.head.meta = [...(state.head.meta ?? []), ...(head.meta ?? [])];\n }\n\n if (head.link) {\n state.head.link = [...(state.head.link ?? []), ...(head.link ?? [])];\n }\n\n if (head.script) {\n state.head.script = [\n ...(state.head.script ?? []),\n ...(head.script ?? []),\n ];\n }\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Minimal route interface for head processing.\n * Avoids circular dependency with @alepha/react/router.\n */\ninterface HeadRoute {\n head?: Head | ((props: Record<string, any>, previous?: Head) => Head);\n}\n\n/**\n * Minimal state interface for head processing.\n * Avoids circular dependency with @alepha/react/router.\n */\ninterface HeadState {\n head: Head;\n layers: Array<{\n route?: HeadRoute;\n props?: Record<string, any>;\n error?: Error;\n }>;\n}\n","import { $inject, createPrimitive, KIND, Primitive } from \"alepha\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"../providers/HeadProvider.ts\";\n\n/**\n * Set global `<head>` options for the application.\n */\nexport const $head = (options: HeadPrimitiveOptions) => {\n return createPrimitive(HeadPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type HeadPrimitiveOptions = Head | (() => Head);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class HeadPrimitive extends Primitive<HeadPrimitiveOptions> {\n protected readonly provider = $inject(HeadProvider);\n protected onInit() {\n this.provider.global = [...(this.provider.global ?? []), this.options];\n }\n}\n\n$head[KIND] = HeadPrimitive;\n","import { $inject, Alepha } from \"alepha\";\nimport type { Head, HeadMeta } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"./HeadProvider.ts\";\n\n/**\n * Browser-side head provider that manages document head elements.\n *\n * Used by ReactBrowserProvider and ReactBrowserRouterProvider to update\n * document title, meta tags, and other head elements during client-side\n * navigation.\n */\nexport class BrowserHeadProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly headProvider = $inject(HeadProvider);\n\n protected get document(): Document {\n return window.document;\n }\n\n /**\n * Fill head state from route configurations and render to document.\n * Combines fillHead from HeadProvider with renderHead to the DOM.\n *\n * Only runs in browser environment - no-op on server.\n */\n public fillAndRenderHead(state: { head: Head; layers: Array<any> }): void {\n // Skip on server-side\n if (!this.alepha.isBrowser()) {\n return;\n }\n\n this.headProvider.fillHead(state as any);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\n }\n\n public getHead(document: Document): Head {\n return {\n get title() {\n return document.title;\n },\n get htmlAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.documentElement.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get bodyAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.body.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get meta() {\n const metas: HeadMeta[] = [];\n // Get meta tags with name attribute\n for (const meta of document.head.querySelectorAll(\"meta[name]\")) {\n const name = meta.getAttribute(\"name\");\n const content = meta.getAttribute(\"content\");\n if (name && content) {\n metas.push({ name, content });\n }\n }\n // Get meta tags with property attribute (OpenGraph)\n for (const meta of document.head.querySelectorAll(\"meta[property]\")) {\n const property = meta.getAttribute(\"property\");\n const content = meta.getAttribute(\"content\");\n if (property && content) {\n metas.push({ property, content });\n }\n }\n return metas;\n },\n };\n }\n\n public renderHead(document: Document, head: Head): void {\n if (head.title) {\n document.title = head.title;\n }\n\n if (head.bodyAttributes) {\n for (const [key, value] of Object.entries(head.bodyAttributes)) {\n if (value) {\n document.body.setAttribute(key, value);\n } else {\n document.body.removeAttribute(key);\n }\n }\n }\n\n if (head.htmlAttributes) {\n for (const [key, value] of Object.entries(head.htmlAttributes)) {\n if (value) {\n document.documentElement.setAttribute(key, value);\n } else {\n document.documentElement.removeAttribute(key);\n }\n }\n }\n\n if (head.meta) {\n for (const it of head.meta) {\n this.renderMetaTag(document, it);\n }\n }\n\n if (head.link) {\n for (const it of head.link) {\n const { rel, href } = it;\n let link = document.querySelector(`link[rel=\"${rel}\"][href=\"${href}\"]`);\n if (!link) {\n link = document.createElement(\"link\");\n link.setAttribute(\"rel\", rel);\n link.setAttribute(\"href\", href);\n if (it.type) {\n link.setAttribute(\"type\", it.type);\n }\n if (it.as) {\n link.setAttribute(\"as\", it.as);\n }\n if (it.crossorigin != null) {\n link.setAttribute(\"crossorigin\", \"\");\n }\n document.head.appendChild(link);\n }\n }\n }\n\n if (head.script) {\n for (const it of head.script) {\n this.renderScriptTag(document, it);\n }\n }\n }\n\n protected renderScriptTag(\n document: Document,\n script:\n | string\n | (Record<string, string | boolean | undefined> & { content?: string }),\n ): void {\n const el = document.createElement(\"script\");\n\n // Handle plain string as inline script\n if (typeof script === \"string\") {\n el.textContent = script;\n document.head.appendChild(el);\n return;\n }\n\n const { content, ...attrs } = script;\n\n // For scripts with src, check if already exists\n if (attrs.src) {\n const existing = document.querySelector(`script[src=\"${attrs.src}\"]`);\n if (existing) {\n return;\n }\n }\n\n for (const [key, value] of Object.entries(attrs)) {\n if (value === true) {\n el.setAttribute(key, \"\");\n } else if (value !== undefined && value !== false) {\n el.setAttribute(key, String(value));\n }\n }\n\n if (content) {\n el.textContent = content;\n }\n\n document.head.appendChild(el);\n }\n\n protected renderMetaTag(document: Document, meta: HeadMeta): void {\n const { content } = meta;\n\n // Handle OpenGraph tags (property attribute)\n if (meta.property) {\n const existing = document.querySelector(\n `meta[property=\"${meta.property}\"]`,\n );\n if (existing) {\n existing.setAttribute(\"content\", content);\n } else {\n const newMeta = document.createElement(\"meta\");\n newMeta.setAttribute(\"property\", meta.property);\n newMeta.setAttribute(\"content\", content);\n document.head.appendChild(newMeta);\n }\n return;\n }\n\n // Handle standard meta tags (name attribute)\n if (meta.name) {\n const existing = document.querySelector(`meta[name=\"${meta.name}\"]`);\n if (existing) {\n existing.setAttribute(\"content\", content);\n } else {\n const newMeta = document.createElement(\"meta\");\n newMeta.setAttribute(\"name\", meta.name);\n newMeta.setAttribute(\"content\", content);\n document.head.appendChild(newMeta);\n }\n }\n }\n}\n","import { useInject } from \"@alepha/react\";\nimport { Alepha } from \"alepha\";\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { BrowserHeadProvider } from \"../providers/BrowserHeadProvider.ts\";\n\n/**\n * ```tsx\n * const App = () => {\n * const [head, setHead] = useHead({\n * // will set the document title on the first render\n * title: \"My App\",\n * });\n *\n * return (\n * // This will update the document title when the button is clicked\n * <button onClick={() => setHead({ title: \"Change Title\" })}>\n * Change Title {head.title}\n * </button>\n * );\n * }\n * ```\n */\nexport const useHead = (options?: UseHeadOptions): UseHeadReturn => {\n const alepha = useInject(Alepha);\n\n const current = useMemo(() => {\n if (!alepha.isBrowser()) {\n return {};\n }\n\n return alepha.inject(BrowserHeadProvider).getHead(window.document);\n }, []);\n\n const setHead = useCallback((head?: Head | ((previous?: Head) => Head)) => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n alepha\n .inject(BrowserHeadProvider)\n .renderHead(\n window.document,\n typeof head === \"function\" ? head(current) : head || {},\n );\n }, []);\n\n useEffect(() => {\n if (options) {\n setHead(options);\n }\n }, []);\n\n return [current, setHead];\n};\n\nexport type UseHeadOptions = Head | ((previous?: Head) => Head);\n\nexport type UseHeadReturn = [\n Head,\n (head?: Head | ((previous?: Head) => Head)) => void,\n];\n","import { AlephaReact } from \"@alepha/react\";\nimport { $module } from \"alepha\";\nimport { $head } from \"./primitives/$head.ts\";\nimport { BrowserHeadProvider } from \"./providers/BrowserHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./helpers/SeoExpander.ts\";\nexport * from \"./hooks/useHead.ts\";\nexport * from \"./interfaces/Head.ts\";\nexport * from \"./primitives/$head.ts\";\nexport * from \"./providers/BrowserHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Alepha React Head Module\n *\n * @see {@link BrowserHeadProvider}\n * @module alepha.react.head\n */\nexport const AlephaReactHead = $module({\n name: \"alepha.react.head\",\n primitives: [$head],\n services: [AlephaReact, BrowserHeadProvider],\n});\n"],"mappings":"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"}
@@ -3,7 +3,6 @@ import { Alepha, KIND, Primitive } from "alepha";
3
3
  import * as alepha_logger0 from "alepha/logger";
4
4
 
5
5
  //#region ../../src/head/interfaces/Head.d.ts
6
-
7
6
  /**
8
7
  * Complete head configuration combining basic head elements with SEO fields.
9
8
  *
@@ -45,26 +44,17 @@ interface Seo {
45
44
  imageAlt?: string;
46
45
  /** Twitter-specific overrides */
47
46
  twitter?: {
48
- /** Twitter card type */
49
- card?: "summary" | "summary_large_image" | "app" | "player";
50
- /** @username of website */
51
- site?: string;
52
- /** @username of content creator */
53
- creator?: string;
54
- /** Override title for Twitter */
55
- title?: string;
56
- /** Override description for Twitter */
57
- description?: string;
58
- /** Override image for Twitter */
47
+ /** Twitter card type */card?: "summary" | "summary_large_image" | "app" | "player"; /** @username of website */
48
+ site?: string; /** @username of content creator */
49
+ creator?: string; /** Override title for Twitter */
50
+ title?: string; /** Override description for Twitter */
51
+ description?: string; /** Override image for Twitter */
59
52
  image?: string;
60
53
  };
61
54
  /** OpenGraph-specific overrides */
62
55
  og?: {
63
- /** Override title for OpenGraph */
64
- title?: string;
65
- /** Override description for OpenGraph */
66
- description?: string;
67
- /** Override image for OpenGraph */
56
+ /** Override title for OpenGraph */title?: string; /** Override description for OpenGraph */
57
+ description?: string; /** Override image for OpenGraph */
68
58
  image?: string;
69
59
  };
70
60
  }
@@ -79,9 +69,14 @@ interface SimpleHead {
79
69
  link?: Array<{
80
70
  rel: string;
81
71
  href: string;
72
+ type?: string;
73
+ as?: string;
74
+ crossorigin?: string;
82
75
  }>;
83
- /** Script tags - any valid script attributes (src, type, async, defer, etc.) */
84
- script?: Array<Record<string, string | boolean>>;
76
+ /** Script tags - string for inline code, or object with attributes */
77
+ script?: Array<string | (Record<string, string | boolean | undefined> & {
78
+ /** Inline JavaScript code */content?: string;
79
+ })>;
85
80
  }
86
81
  interface HeadMeta {
87
82
  /** Meta name attribute (e.g., "description", "twitter:card") */
@@ -124,6 +119,28 @@ declare class SeoExpander {
124
119
  protected expandTwitter(head: Head, meta: HeadMeta[]): void;
125
120
  }
126
121
  //#endregion
122
+ //#region ../../src/head/hooks/useHead.d.ts
123
+ /**
124
+ * ```tsx
125
+ * const App = () => {
126
+ * const [head, setHead] = useHead({
127
+ * // will set the document title on the first render
128
+ * title: "My App",
129
+ * });
130
+ *
131
+ * return (
132
+ * // This will update the document title when the button is clicked
133
+ * <button onClick={() => setHead({ title: "Change Title" })}>
134
+ * Change Title {head.title}
135
+ * </button>
136
+ * );
137
+ * }
138
+ * ```
139
+ */
140
+ declare const useHead: (options?: UseHeadOptions) => UseHeadReturn;
141
+ type UseHeadOptions = Head | ((previous?: Head) => Head);
142
+ type UseHeadReturn = [Head, (head?: Head | ((previous?: Head) => Head)) => void];
143
+ //#endregion
127
144
  //#region ../../src/head/providers/HeadProvider.d.ts
128
145
  /**
129
146
  * Provides methods to fill and merge head information into the application state.
@@ -190,27 +207,35 @@ declare class HeadPrimitive extends Primitive<HeadPrimitiveOptions> {
190
207
  protected onInit(): void;
191
208
  }
192
209
  //#endregion
193
- //#region ../../src/head/hooks/useHead.d.ts
210
+ //#region ../../src/head/providers/BrowserHeadProvider.d.ts
194
211
  /**
195
- * ```tsx
196
- * const App = () => {
197
- * const [head, setHead] = useHead({
198
- * // will set the document title on the first render
199
- * title: "My App",
200
- * });
212
+ * Browser-side head provider that manages document head elements.
201
213
  *
202
- * return (
203
- * // This will update the document title when the button is clicked
204
- * <button onClick={() => setHead({ title: "Change Title" })}>
205
- * Change Title {head.title}
206
- * </button>
207
- * );
208
- * }
209
- * ```
214
+ * Used by ReactBrowserProvider and ReactBrowserRouterProvider to update
215
+ * document title, meta tags, and other head elements during client-side
216
+ * navigation.
210
217
  */
211
- declare const useHead: (options?: UseHeadOptions) => UseHeadReturn;
212
- type UseHeadOptions = Head | ((previous?: Head) => Head);
213
- type UseHeadReturn = [Head, (head?: Head | ((previous?: Head) => Head)) => void];
218
+ declare class BrowserHeadProvider {
219
+ protected readonly alepha: Alepha;
220
+ protected readonly headProvider: HeadProvider;
221
+ protected get document(): Document;
222
+ /**
223
+ * Fill head state from route configurations and render to document.
224
+ * Combines fillHead from HeadProvider with renderHead to the DOM.
225
+ *
226
+ * Only runs in browser environment - no-op on server.
227
+ */
228
+ fillAndRenderHead(state: {
229
+ head: Head;
230
+ layers: Array<any>;
231
+ }): void;
232
+ getHead(document: Document): Head;
233
+ renderHead(document: Document, head: Head): void;
234
+ protected renderScriptTag(document: Document, script: string | (Record<string, string | boolean | undefined> & {
235
+ content?: string;
236
+ })): void;
237
+ protected renderMetaTag(document: Document, meta: HeadMeta): void;
238
+ }
214
239
  //#endregion
215
240
  //#region ../../src/head/providers/ServerHeadProvider.d.ts
216
241
  /**
@@ -238,33 +263,6 @@ declare class ServerHeadProvider {
238
263
  }): void;
239
264
  }
240
265
  //#endregion
241
- //#region ../../src/head/providers/BrowserHeadProvider.d.ts
242
- /**
243
- * Browser-side head provider that manages document head elements.
244
- *
245
- * Used by ReactBrowserProvider and ReactBrowserRouterProvider to update
246
- * document title, meta tags, and other head elements during client-side
247
- * navigation.
248
- */
249
- declare class BrowserHeadProvider {
250
- protected readonly alepha: Alepha;
251
- protected readonly headProvider: HeadProvider;
252
- protected get document(): Document;
253
- /**
254
- * Fill head state from route configurations and render to document.
255
- * Combines fillHead from HeadProvider with renderHead to the DOM.
256
- *
257
- * Only runs in browser environment - no-op on server.
258
- */
259
- fillAndRenderHead(state: {
260
- head: Head;
261
- layers: Array<any>;
262
- }): void;
263
- getHead(document: Document): Head;
264
- renderHead(document: Document, head: Head): void;
265
- protected renderMetaTag(document: Document, meta: HeadMeta): void;
266
- }
267
- //#endregion
268
266
  //#region ../../src/head/index.d.ts
269
267
  /**
270
268
  * Fill `<head>` server & client side.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/head/interfaces/Head.ts","../../src/head/helpers/SeoExpander.ts","../../src/head/providers/HeadProvider.ts","../../src/head/primitives/$head.ts","../../src/head/hooks/useHead.ts","../../src/head/providers/ServerHeadProvider.ts","../../src/head/providers/BrowserHeadProvider.ts","../../src/head/index.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/head/interfaces/Head.ts","../../src/head/helpers/SeoExpander.ts","../../src/head/hooks/useHead.ts","../../src/head/providers/HeadProvider.ts","../../src/head/primitives/$head.ts","../../src/head/providers/BrowserHeadProvider.ts","../../src/head/providers/ServerHeadProvider.ts","../../src/head/index.ts"],"mappings":"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"}
@@ -319,9 +319,29 @@ var BrowserHeadProvider = class {
319
319
  link = document.createElement("link");
320
320
  link.setAttribute("rel", rel);
321
321
  link.setAttribute("href", href);
322
+ if (it.type) link.setAttribute("type", it.type);
323
+ if (it.as) link.setAttribute("as", it.as);
324
+ if (it.crossorigin != null) link.setAttribute("crossorigin", "");
322
325
  document.head.appendChild(link);
323
326
  }
324
327
  }
328
+ if (head.script) for (const it of head.script) this.renderScriptTag(document, it);
329
+ }
330
+ renderScriptTag(document, script) {
331
+ const el = document.createElement("script");
332
+ if (typeof script === "string") {
333
+ el.textContent = script;
334
+ document.head.appendChild(el);
335
+ return;
336
+ }
337
+ const { content, ...attrs } = script;
338
+ if (attrs.src) {
339
+ if (document.querySelector(`script[src="${attrs.src}"]`)) return;
340
+ }
341
+ for (const [key, value] of Object.entries(attrs)) if (value === true) el.setAttribute(key, "");
342
+ else if (value !== void 0 && value !== false) el.setAttribute(key, String(value));
343
+ if (content) el.textContent = content;
344
+ document.head.appendChild(el);
325
345
  }
326
346
  renderMetaTag(document, meta) {
327
347
  const { content } = meta;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/head/helpers/SeoExpander.ts","../../src/head/providers/HeadProvider.ts","../../src/head/primitives/$head.ts","../../src/head/providers/BrowserHeadProvider.ts","../../src/head/providers/ServerHeadProvider.ts","../../src/head/hooks/useHead.ts","../../src/head/index.ts"],"sourcesContent":["import type { Head, HeadMeta } from \"../interfaces/Head.ts\";\n\n/**\n * Expands Head configuration into SEO meta tags.\n *\n * Generates:\n * - `<meta name=\"description\">` from head.description\n * - `<meta property=\"og:*\">` OpenGraph tags\n * - `<meta name=\"twitter:*\">` Twitter Card tags\n *\n * @example\n * ```ts\n * const helper = new SeoExpander();\n * const { meta, link } = helper.expand({\n * title: \"My App\",\n * description: \"Build amazing apps\",\n * image: \"https://example.com/og.png\",\n * url: \"https://example.com/\",\n * });\n * ```\n */\nexport class SeoExpander {\n public expand(head: Head): {\n meta: HeadMeta[];\n link: Array<{ rel: string; href: string }>;\n } {\n const meta: HeadMeta[] = [];\n const link: Array<{ rel: string; href: string }> = [];\n\n // Only expand SEO if there's meaningful content beyond just title\n const hasSeoContent =\n head.description ||\n head.image ||\n head.url ||\n head.siteName ||\n head.locale ||\n head.type ||\n head.og ||\n head.twitter;\n\n if (!hasSeoContent) {\n return { meta, link };\n }\n\n // Base description\n if (head.description) {\n meta.push({ name: \"description\", content: head.description });\n }\n\n // Canonical URL\n if (head.url) {\n link.push({ rel: \"canonical\", href: head.url });\n }\n\n // OpenGraph tags\n this.expandOpenGraph(head, meta);\n\n // Twitter Card tags\n this.expandTwitter(head, meta);\n\n return { meta, link };\n }\n\n protected expandOpenGraph(head: Head, meta: HeadMeta[]): void {\n const ogTitle = head.og?.title ?? head.title;\n const ogDescription = head.og?.description ?? head.description;\n const ogImage = head.og?.image ?? head.image;\n\n if (head.type || ogTitle) {\n meta.push({ property: \"og:type\", content: head.type ?? \"website\" });\n }\n if (head.url) {\n meta.push({ property: \"og:url\", content: head.url });\n }\n if (ogTitle) {\n meta.push({ property: \"og:title\", content: ogTitle });\n }\n if (ogDescription) {\n meta.push({ property: \"og:description\", content: ogDescription });\n }\n if (ogImage) {\n meta.push({ property: \"og:image\", content: ogImage });\n if (head.imageWidth) {\n meta.push({\n property: \"og:image:width\",\n content: String(head.imageWidth),\n });\n }\n if (head.imageHeight) {\n meta.push({\n property: \"og:image:height\",\n content: String(head.imageHeight),\n });\n }\n if (head.imageAlt) {\n meta.push({ property: \"og:image:alt\", content: head.imageAlt });\n }\n }\n if (head.siteName) {\n meta.push({ property: \"og:site_name\", content: head.siteName });\n }\n if (head.locale) {\n meta.push({ property: \"og:locale\", content: head.locale });\n }\n }\n\n protected expandTwitter(head: Head, meta: HeadMeta[]): void {\n const twitterTitle = head.twitter?.title ?? head.title;\n const twitterDescription = head.twitter?.description ?? head.description;\n const twitterImage = head.twitter?.image ?? head.image;\n\n if (head.twitter?.card || twitterTitle || twitterImage) {\n meta.push({\n name: \"twitter:card\",\n content:\n head.twitter?.card ?? (twitterImage ? \"summary_large_image\" : \"summary\"),\n });\n }\n if (head.url) {\n meta.push({ name: \"twitter:url\", content: head.url });\n }\n if (twitterTitle) {\n meta.push({ name: \"twitter:title\", content: twitterTitle });\n }\n if (twitterDescription) {\n meta.push({ name: \"twitter:description\", content: twitterDescription });\n }\n if (twitterImage) {\n meta.push({ name: \"twitter:image\", content: twitterImage });\n if (head.imageAlt) {\n meta.push({ name: \"twitter:image:alt\", content: head.imageAlt });\n }\n }\n if (head.twitter?.site) {\n meta.push({ name: \"twitter:site\", content: head.twitter.site });\n }\n if (head.twitter?.creator) {\n meta.push({ name: \"twitter:creator\", content: head.twitter.creator });\n }\n }\n}\n","import { $inject } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { SeoExpander } from \"../helpers/SeoExpander.ts\";\nimport type { Head } from \"../interfaces/Head.ts\";\n\n/**\n * Provides methods to fill and merge head information into the application state.\n *\n * Used both on server and client side to manage document head.\n *\n * @see {@link SeoExpander}\n * @see {@link ServerHeadProvider}\n * @see {@link BrowserHeadProvider}\n */\nexport class HeadProvider {\n protected readonly log = $logger();\n protected readonly seoExpander = $inject(SeoExpander);\n\n public global?: Array<Head | (() => Head)> = [];\n\n /**\n * Track if we've warned about page-level htmlAttributes to avoid spam.\n */\n protected warnedAboutHtmlAttributes = false;\n\n /**\n * Resolve global head configuration (from $head primitives only).\n *\n * This is used to get htmlAttributes early, before page loaders run.\n * Only htmlAttributes from global $head are allowed; page-level htmlAttributes\n * are ignored for early streaming optimization.\n *\n * @returns Merged global head with htmlAttributes\n */\n public resolveGlobalHead(): Head {\n const head: Head = {};\n\n for (const h of this.global ?? []) {\n const resolved = typeof h === \"function\" ? h() : h;\n if (resolved.htmlAttributes) {\n head.htmlAttributes = {\n ...head.htmlAttributes,\n ...resolved.htmlAttributes,\n };\n }\n }\n\n return head;\n }\n\n public fillHead(state: HeadState) {\n state.head = {\n ...state.head,\n };\n\n for (const h of this.global ?? []) {\n const head = typeof h === \"function\" ? h() : h;\n this.mergeHead(state, head);\n }\n\n for (const layer of state.layers) {\n if (layer.route?.head && !layer.error) {\n this.fillHeadByPage(layer.route, state, layer.props ?? {});\n }\n }\n }\n\n protected mergeHead(state: HeadState, head: Head): void {\n // Expand SEO fields into meta tags\n const { meta, link } = this.seoExpander.expand(head);\n state.head = {\n ...state.head,\n ...head,\n meta: [...(state.head.meta ?? []), ...meta, ...(head.meta ?? [])],\n link: [...(state.head.link ?? []), ...link, ...(head.link ?? [])],\n script: [...(state.head.script ?? []), ...(head.script ?? [])],\n };\n }\n\n protected fillHeadByPage(\n page: HeadRoute,\n state: HeadState,\n props: Record<string, any>,\n ): void {\n if (!page.head) {\n return;\n }\n\n state.head ??= {};\n\n const head =\n typeof page.head === \"function\"\n ? page.head(props, state.head)\n : page.head;\n\n // Expand SEO fields into meta tags\n const { meta, link } = this.seoExpander.expand(head);\n state.head.meta = [...(state.head.meta ?? []), ...meta];\n state.head.link = [...(state.head.link ?? []), ...link];\n\n if (head.title) {\n state.head ??= {};\n\n if (state.head.titleSeparator) {\n state.head.title = `${head.title}${state.head.titleSeparator}${state.head.title}`;\n } else {\n state.head.title = head.title;\n }\n\n state.head.titleSeparator = head.titleSeparator;\n }\n\n // htmlAttributes from pages are ignored for early streaming optimization.\n // Only global $head can set htmlAttributes.\n if (head.htmlAttributes && !this.warnedAboutHtmlAttributes) {\n this.warnedAboutHtmlAttributes = true;\n this.log.warn(\n \"Page-level htmlAttributes are ignored. Use global $head() for htmlAttributes instead, \" +\n \"as they are sent before page loaders run for early streaming optimization.\",\n );\n }\n\n if (head.bodyAttributes) {\n state.head.bodyAttributes = {\n ...state.head.bodyAttributes,\n ...head.bodyAttributes,\n };\n }\n\n if (head.meta) {\n state.head.meta = [...(state.head.meta ?? []), ...(head.meta ?? [])];\n }\n\n if (head.link) {\n state.head.link = [...(state.head.link ?? []), ...(head.link ?? [])];\n }\n\n if (head.script) {\n state.head.script = [...(state.head.script ?? []), ...(head.script ?? [])];\n }\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Minimal route interface for head processing.\n * Avoids circular dependency with @alepha/react/router.\n */\ninterface HeadRoute {\n head?: Head | ((props: Record<string, any>, previous?: Head) => Head);\n}\n\n/**\n * Minimal state interface for head processing.\n * Avoids circular dependency with @alepha/react/router.\n */\ninterface HeadState {\n head: Head;\n layers: Array<{\n route?: HeadRoute;\n props?: Record<string, any>;\n error?: Error;\n }>;\n}\n","import { $inject, createPrimitive, Primitive, KIND } from \"alepha\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"../providers/HeadProvider.ts\";\n\n/**\n * Set global `<head>` options for the application.\n */\nexport const $head = (options: HeadPrimitiveOptions) => {\n return createPrimitive(HeadPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type HeadPrimitiveOptions = Head | (() => Head);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class HeadPrimitive extends Primitive<HeadPrimitiveOptions> {\n protected readonly provider = $inject(HeadProvider);\n protected onInit() {\n this.provider.global = [\n ...(this.provider.global ?? []),\n this.options,\n ];\n }\n}\n\n$head[KIND] = HeadPrimitive;\n","import { $inject, Alepha } from \"alepha\";\nimport type { Head, HeadMeta } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"./HeadProvider.ts\";\n\n/**\n * Browser-side head provider that manages document head elements.\n *\n * Used by ReactBrowserProvider and ReactBrowserRouterProvider to update\n * document title, meta tags, and other head elements during client-side\n * navigation.\n */\nexport class BrowserHeadProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly headProvider = $inject(HeadProvider);\n\n protected get document(): Document {\n return window.document;\n }\n\n /**\n * Fill head state from route configurations and render to document.\n * Combines fillHead from HeadProvider with renderHead to the DOM.\n *\n * Only runs in browser environment - no-op on server.\n */\n public fillAndRenderHead(state: { head: Head; layers: Array<any> }): void {\n // Skip on server-side\n if (!this.alepha.isBrowser()) {\n return;\n }\n\n this.headProvider.fillHead(state as any);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\n }\n\n public getHead(document: Document): Head {\n return {\n get title() {\n return document.title;\n },\n get htmlAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.documentElement.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get bodyAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.body.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get meta() {\n const metas: HeadMeta[] = [];\n // Get meta tags with name attribute\n for (const meta of document.head.querySelectorAll(\"meta[name]\")) {\n const name = meta.getAttribute(\"name\");\n const content = meta.getAttribute(\"content\");\n if (name && content) {\n metas.push({ name, content });\n }\n }\n // Get meta tags with property attribute (OpenGraph)\n for (const meta of document.head.querySelectorAll(\"meta[property]\")) {\n const property = meta.getAttribute(\"property\");\n const content = meta.getAttribute(\"content\");\n if (property && content) {\n metas.push({ property, content });\n }\n }\n return metas;\n },\n };\n }\n\n public renderHead(document: Document, head: Head): void {\n if (head.title) {\n document.title = head.title;\n }\n\n if (head.bodyAttributes) {\n for (const [key, value] of Object.entries(head.bodyAttributes)) {\n if (value) {\n document.body.setAttribute(key, value);\n } else {\n document.body.removeAttribute(key);\n }\n }\n }\n\n if (head.htmlAttributes) {\n for (const [key, value] of Object.entries(head.htmlAttributes)) {\n if (value) {\n document.documentElement.setAttribute(key, value);\n } else {\n document.documentElement.removeAttribute(key);\n }\n }\n }\n\n if (head.meta) {\n for (const it of head.meta) {\n this.renderMetaTag(document, it);\n }\n }\n\n if (head.link) {\n for (const it of head.link) {\n const { rel, href } = it;\n let link = document.querySelector(`link[rel=\"${rel}\"][href=\"${href}\"]`);\n if (!link) {\n link = document.createElement(\"link\");\n link.setAttribute(\"rel\", rel);\n link.setAttribute(\"href\", href);\n document.head.appendChild(link);\n }\n }\n }\n }\n\n protected renderMetaTag(document: Document, meta: HeadMeta): void {\n const { content } = meta;\n\n // Handle OpenGraph tags (property attribute)\n if (meta.property) {\n const existing = document.querySelector(\n `meta[property=\"${meta.property}\"]`,\n );\n if (existing) {\n existing.setAttribute(\"content\", content);\n } else {\n const newMeta = document.createElement(\"meta\");\n newMeta.setAttribute(\"property\", meta.property);\n newMeta.setAttribute(\"content\", content);\n document.head.appendChild(newMeta);\n }\n return;\n }\n\n // Handle standard meta tags (name attribute)\n if (meta.name) {\n const existing = document.querySelector(`meta[name=\"${meta.name}\"]`);\n if (existing) {\n existing.setAttribute(\"content\", content);\n } else {\n const newMeta = document.createElement(\"meta\");\n newMeta.setAttribute(\"name\", meta.name);\n newMeta.setAttribute(\"content\", content);\n document.head.appendChild(newMeta);\n }\n }\n }\n}\n","import { $inject } from \"alepha\";\nimport type { Head, SimpleHead } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"./HeadProvider.ts\";\n\n/**\n * Server-side head provider that fills head content from route configurations.\n *\n * Used by ReactServerProvider to collect title, meta tags, and other head\n * elements which are then rendered by ReactServerTemplateProvider.\n */\nexport class ServerHeadProvider {\n protected readonly headProvider = $inject(HeadProvider);\n\n /**\n * Resolve global head configuration (htmlAttributes only).\n *\n * Used for early streaming optimization - htmlAttributes can be sent\n * before page loaders run since they come from global $head only.\n */\n public resolveGlobalHead(): Head {\n return this.headProvider.resolveGlobalHead();\n }\n\n /**\n * Fill head state from route configurations.\n * Delegates to HeadProvider to merge head data from all route layers.\n */\n public fillHead(state: { head: SimpleHead; layers: Array<any> }): void {\n this.headProvider.fillHead(state as any);\n }\n}\n","import { useInject } from \"@alepha/react\";\nimport { Alepha } from \"alepha\";\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { BrowserHeadProvider } from \"../providers/BrowserHeadProvider.ts\";\n\n/**\n * ```tsx\n * const App = () => {\n * const [head, setHead] = useHead({\n * // will set the document title on the first render\n * title: \"My App\",\n * });\n *\n * return (\n * // This will update the document title when the button is clicked\n * <button onClick={() => setHead({ title: \"Change Title\" })}>\n * Change Title {head.title}\n * </button>\n * );\n * }\n * ```\n */\nexport const useHead = (options?: UseHeadOptions): UseHeadReturn => {\n const alepha = useInject(Alepha);\n\n const current = useMemo(() => {\n if (!alepha.isBrowser()) {\n return {};\n }\n\n return alepha.inject(BrowserHeadProvider).getHead(window.document);\n }, []);\n\n const setHead = useCallback((head?: Head | ((previous?: Head) => Head)) => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n alepha\n .inject(BrowserHeadProvider)\n .renderHead(\n window.document,\n typeof head === \"function\" ? head(current) : head || {},\n );\n }, []);\n\n useEffect(() => {\n if (options) {\n setHead(options);\n }\n }, []);\n\n return [current, setHead];\n};\n\nexport type UseHeadOptions = Head | ((previous?: Head) => Head);\n\nexport type UseHeadReturn = [\n Head,\n (head?: Head | ((previous?: Head) => Head)) => void,\n];\n","import { AlephaReact } from \"@alepha/react\";\nimport { $module } from \"alepha\";\nimport { $head } from \"./primitives/$head.ts\";\nimport { BrowserHeadProvider } from \"./providers/BrowserHeadProvider.ts\";\nimport { HeadProvider } from \"./providers/HeadProvider.ts\";\nimport { SeoExpander } from \"./helpers/SeoExpander.ts\";\nimport { ServerHeadProvider } from \"./providers/ServerHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$head.ts\";\nexport * from \"./hooks/useHead.ts\";\nexport * from \"./interfaces/Head.ts\";\nexport * from \"./helpers/SeoExpander.ts\";\nexport * from \"./providers/ServerHeadProvider.ts\";\nexport * from \"./providers/BrowserHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Fill `<head>` server & client side.\n *\n * Generate SEO-friendly meta tags and titles for your React application using AlephaReactHead module.\n *\n * This module provides services and primitives to manage the document head both on the server and client side,\n * ensuring that your application is optimized for search engines and social media sharing.\n *\n * @see {@link ServerHeadProvider}\n * @module alepha.react.head\n */\nexport const AlephaReactHead = $module({\n name: \"alepha.react.head\",\n primitives: [$head],\n services: [\n AlephaReact,\n BrowserHeadProvider,\n HeadProvider,\n SeoExpander,\n ServerHeadProvider,\n ],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/head/helpers/SeoExpander.ts","../../src/head/providers/HeadProvider.ts","../../src/head/primitives/$head.ts","../../src/head/providers/BrowserHeadProvider.ts","../../src/head/providers/ServerHeadProvider.ts","../../src/head/hooks/useHead.ts","../../src/head/index.ts"],"sourcesContent":["import type { Head, HeadMeta } from \"../interfaces/Head.ts\";\n\n/**\n * Expands Head configuration into SEO meta tags.\n *\n * Generates:\n * - `<meta name=\"description\">` from head.description\n * - `<meta property=\"og:*\">` OpenGraph tags\n * - `<meta name=\"twitter:*\">` Twitter Card tags\n *\n * @example\n * ```ts\n * const helper = new SeoExpander();\n * const { meta, link } = helper.expand({\n * title: \"My App\",\n * description: \"Build amazing apps\",\n * image: \"https://example.com/og.png\",\n * url: \"https://example.com/\",\n * });\n * ```\n */\nexport class SeoExpander {\n public expand(head: Head): {\n meta: HeadMeta[];\n link: Array<{ rel: string; href: string }>;\n } {\n const meta: HeadMeta[] = [];\n const link: Array<{ rel: string; href: string }> = [];\n\n // Only expand SEO if there's meaningful content beyond just title\n const hasSeoContent =\n head.description ||\n head.image ||\n head.url ||\n head.siteName ||\n head.locale ||\n head.type ||\n head.og ||\n head.twitter;\n\n if (!hasSeoContent) {\n return { meta, link };\n }\n\n // Base description\n if (head.description) {\n meta.push({ name: \"description\", content: head.description });\n }\n\n // Canonical URL\n if (head.url) {\n link.push({ rel: \"canonical\", href: head.url });\n }\n\n // OpenGraph tags\n this.expandOpenGraph(head, meta);\n\n // Twitter Card tags\n this.expandTwitter(head, meta);\n\n return { meta, link };\n }\n\n protected expandOpenGraph(head: Head, meta: HeadMeta[]): void {\n const ogTitle = head.og?.title ?? head.title;\n const ogDescription = head.og?.description ?? head.description;\n const ogImage = head.og?.image ?? head.image;\n\n if (head.type || ogTitle) {\n meta.push({ property: \"og:type\", content: head.type ?? \"website\" });\n }\n if (head.url) {\n meta.push({ property: \"og:url\", content: head.url });\n }\n if (ogTitle) {\n meta.push({ property: \"og:title\", content: ogTitle });\n }\n if (ogDescription) {\n meta.push({ property: \"og:description\", content: ogDescription });\n }\n if (ogImage) {\n meta.push({ property: \"og:image\", content: ogImage });\n if (head.imageWidth) {\n meta.push({\n property: \"og:image:width\",\n content: String(head.imageWidth),\n });\n }\n if (head.imageHeight) {\n meta.push({\n property: \"og:image:height\",\n content: String(head.imageHeight),\n });\n }\n if (head.imageAlt) {\n meta.push({ property: \"og:image:alt\", content: head.imageAlt });\n }\n }\n if (head.siteName) {\n meta.push({ property: \"og:site_name\", content: head.siteName });\n }\n if (head.locale) {\n meta.push({ property: \"og:locale\", content: head.locale });\n }\n }\n\n protected expandTwitter(head: Head, meta: HeadMeta[]): void {\n const twitterTitle = head.twitter?.title ?? head.title;\n const twitterDescription = head.twitter?.description ?? head.description;\n const twitterImage = head.twitter?.image ?? head.image;\n\n if (head.twitter?.card || twitterTitle || twitterImage) {\n meta.push({\n name: \"twitter:card\",\n content:\n head.twitter?.card ??\n (twitterImage ? \"summary_large_image\" : \"summary\"),\n });\n }\n if (head.url) {\n meta.push({ name: \"twitter:url\", content: head.url });\n }\n if (twitterTitle) {\n meta.push({ name: \"twitter:title\", content: twitterTitle });\n }\n if (twitterDescription) {\n meta.push({ name: \"twitter:description\", content: twitterDescription });\n }\n if (twitterImage) {\n meta.push({ name: \"twitter:image\", content: twitterImage });\n if (head.imageAlt) {\n meta.push({ name: \"twitter:image:alt\", content: head.imageAlt });\n }\n }\n if (head.twitter?.site) {\n meta.push({ name: \"twitter:site\", content: head.twitter.site });\n }\n if (head.twitter?.creator) {\n meta.push({ name: \"twitter:creator\", content: head.twitter.creator });\n }\n }\n}\n","import { $inject } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { SeoExpander } from \"../helpers/SeoExpander.ts\";\nimport type { Head } from \"../interfaces/Head.ts\";\n\n/**\n * Provides methods to fill and merge head information into the application state.\n *\n * Used both on server and client side to manage document head.\n *\n * @see {@link SeoExpander}\n * @see {@link ServerHeadProvider}\n * @see {@link BrowserHeadProvider}\n */\nexport class HeadProvider {\n protected readonly log = $logger();\n protected readonly seoExpander = $inject(SeoExpander);\n\n public global?: Array<Head | (() => Head)> = [];\n\n /**\n * Track if we've warned about page-level htmlAttributes to avoid spam.\n */\n protected warnedAboutHtmlAttributes = false;\n\n /**\n * Resolve global head configuration (from $head primitives only).\n *\n * This is used to get htmlAttributes early, before page loaders run.\n * Only htmlAttributes from global $head are allowed; page-level htmlAttributes\n * are ignored for early streaming optimization.\n *\n * @returns Merged global head with htmlAttributes\n */\n public resolveGlobalHead(): Head {\n const head: Head = {};\n\n for (const h of this.global ?? []) {\n const resolved = typeof h === \"function\" ? h() : h;\n if (resolved.htmlAttributes) {\n head.htmlAttributes = {\n ...head.htmlAttributes,\n ...resolved.htmlAttributes,\n };\n }\n }\n\n return head;\n }\n\n public fillHead(state: HeadState) {\n state.head = {\n ...state.head,\n };\n\n for (const h of this.global ?? []) {\n const head = typeof h === \"function\" ? h() : h;\n this.mergeHead(state, head);\n }\n\n for (const layer of state.layers) {\n if (layer.route?.head && !layer.error) {\n this.fillHeadByPage(layer.route, state, layer.props ?? {});\n }\n }\n }\n\n protected mergeHead(state: HeadState, head: Head): void {\n // Expand SEO fields into meta tags\n const { meta, link } = this.seoExpander.expand(head);\n state.head = {\n ...state.head,\n ...head,\n meta: [...(state.head.meta ?? []), ...meta, ...(head.meta ?? [])],\n link: [...(state.head.link ?? []), ...link, ...(head.link ?? [])],\n script: [...(state.head.script ?? []), ...(head.script ?? [])],\n };\n }\n\n protected fillHeadByPage(\n page: HeadRoute,\n state: HeadState,\n props: Record<string, any>,\n ): void {\n if (!page.head) {\n return;\n }\n\n state.head ??= {};\n\n const head =\n typeof page.head === \"function\"\n ? page.head(props, state.head)\n : page.head;\n\n // Expand SEO fields into meta tags\n const { meta, link } = this.seoExpander.expand(head);\n state.head.meta = [...(state.head.meta ?? []), ...meta];\n state.head.link = [...(state.head.link ?? []), ...link];\n\n if (head.title) {\n state.head ??= {};\n\n if (state.head.titleSeparator) {\n state.head.title = `${head.title}${state.head.titleSeparator}${state.head.title}`;\n } else {\n state.head.title = head.title;\n }\n\n state.head.titleSeparator = head.titleSeparator;\n }\n\n // htmlAttributes from pages are ignored for early streaming optimization.\n // Only global $head can set htmlAttributes.\n if (head.htmlAttributes && !this.warnedAboutHtmlAttributes) {\n this.warnedAboutHtmlAttributes = true;\n this.log.warn(\n \"Page-level htmlAttributes are ignored. Use global $head() for htmlAttributes instead, \" +\n \"as they are sent before page loaders run for early streaming optimization.\",\n );\n }\n\n if (head.bodyAttributes) {\n state.head.bodyAttributes = {\n ...state.head.bodyAttributes,\n ...head.bodyAttributes,\n };\n }\n\n if (head.meta) {\n state.head.meta = [...(state.head.meta ?? []), ...(head.meta ?? [])];\n }\n\n if (head.link) {\n state.head.link = [...(state.head.link ?? []), ...(head.link ?? [])];\n }\n\n if (head.script) {\n state.head.script = [\n ...(state.head.script ?? []),\n ...(head.script ?? []),\n ];\n }\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Minimal route interface for head processing.\n * Avoids circular dependency with @alepha/react/router.\n */\ninterface HeadRoute {\n head?: Head | ((props: Record<string, any>, previous?: Head) => Head);\n}\n\n/**\n * Minimal state interface for head processing.\n * Avoids circular dependency with @alepha/react/router.\n */\ninterface HeadState {\n head: Head;\n layers: Array<{\n route?: HeadRoute;\n props?: Record<string, any>;\n error?: Error;\n }>;\n}\n","import { $inject, createPrimitive, KIND, Primitive } from \"alepha\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"../providers/HeadProvider.ts\";\n\n/**\n * Set global `<head>` options for the application.\n */\nexport const $head = (options: HeadPrimitiveOptions) => {\n return createPrimitive(HeadPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type HeadPrimitiveOptions = Head | (() => Head);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class HeadPrimitive extends Primitive<HeadPrimitiveOptions> {\n protected readonly provider = $inject(HeadProvider);\n protected onInit() {\n this.provider.global = [...(this.provider.global ?? []), this.options];\n }\n}\n\n$head[KIND] = HeadPrimitive;\n","import { $inject, Alepha } from \"alepha\";\nimport type { Head, HeadMeta } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"./HeadProvider.ts\";\n\n/**\n * Browser-side head provider that manages document head elements.\n *\n * Used by ReactBrowserProvider and ReactBrowserRouterProvider to update\n * document title, meta tags, and other head elements during client-side\n * navigation.\n */\nexport class BrowserHeadProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly headProvider = $inject(HeadProvider);\n\n protected get document(): Document {\n return window.document;\n }\n\n /**\n * Fill head state from route configurations and render to document.\n * Combines fillHead from HeadProvider with renderHead to the DOM.\n *\n * Only runs in browser environment - no-op on server.\n */\n public fillAndRenderHead(state: { head: Head; layers: Array<any> }): void {\n // Skip on server-side\n if (!this.alepha.isBrowser()) {\n return;\n }\n\n this.headProvider.fillHead(state as any);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\n }\n\n public getHead(document: Document): Head {\n return {\n get title() {\n return document.title;\n },\n get htmlAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.documentElement.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get bodyAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.body.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get meta() {\n const metas: HeadMeta[] = [];\n // Get meta tags with name attribute\n for (const meta of document.head.querySelectorAll(\"meta[name]\")) {\n const name = meta.getAttribute(\"name\");\n const content = meta.getAttribute(\"content\");\n if (name && content) {\n metas.push({ name, content });\n }\n }\n // Get meta tags with property attribute (OpenGraph)\n for (const meta of document.head.querySelectorAll(\"meta[property]\")) {\n const property = meta.getAttribute(\"property\");\n const content = meta.getAttribute(\"content\");\n if (property && content) {\n metas.push({ property, content });\n }\n }\n return metas;\n },\n };\n }\n\n public renderHead(document: Document, head: Head): void {\n if (head.title) {\n document.title = head.title;\n }\n\n if (head.bodyAttributes) {\n for (const [key, value] of Object.entries(head.bodyAttributes)) {\n if (value) {\n document.body.setAttribute(key, value);\n } else {\n document.body.removeAttribute(key);\n }\n }\n }\n\n if (head.htmlAttributes) {\n for (const [key, value] of Object.entries(head.htmlAttributes)) {\n if (value) {\n document.documentElement.setAttribute(key, value);\n } else {\n document.documentElement.removeAttribute(key);\n }\n }\n }\n\n if (head.meta) {\n for (const it of head.meta) {\n this.renderMetaTag(document, it);\n }\n }\n\n if (head.link) {\n for (const it of head.link) {\n const { rel, href } = it;\n let link = document.querySelector(`link[rel=\"${rel}\"][href=\"${href}\"]`);\n if (!link) {\n link = document.createElement(\"link\");\n link.setAttribute(\"rel\", rel);\n link.setAttribute(\"href\", href);\n if (it.type) {\n link.setAttribute(\"type\", it.type);\n }\n if (it.as) {\n link.setAttribute(\"as\", it.as);\n }\n if (it.crossorigin != null) {\n link.setAttribute(\"crossorigin\", \"\");\n }\n document.head.appendChild(link);\n }\n }\n }\n\n if (head.script) {\n for (const it of head.script) {\n this.renderScriptTag(document, it);\n }\n }\n }\n\n protected renderScriptTag(\n document: Document,\n script:\n | string\n | (Record<string, string | boolean | undefined> & { content?: string }),\n ): void {\n const el = document.createElement(\"script\");\n\n // Handle plain string as inline script\n if (typeof script === \"string\") {\n el.textContent = script;\n document.head.appendChild(el);\n return;\n }\n\n const { content, ...attrs } = script;\n\n // For scripts with src, check if already exists\n if (attrs.src) {\n const existing = document.querySelector(`script[src=\"${attrs.src}\"]`);\n if (existing) {\n return;\n }\n }\n\n for (const [key, value] of Object.entries(attrs)) {\n if (value === true) {\n el.setAttribute(key, \"\");\n } else if (value !== undefined && value !== false) {\n el.setAttribute(key, String(value));\n }\n }\n\n if (content) {\n el.textContent = content;\n }\n\n document.head.appendChild(el);\n }\n\n protected renderMetaTag(document: Document, meta: HeadMeta): void {\n const { content } = meta;\n\n // Handle OpenGraph tags (property attribute)\n if (meta.property) {\n const existing = document.querySelector(\n `meta[property=\"${meta.property}\"]`,\n );\n if (existing) {\n existing.setAttribute(\"content\", content);\n } else {\n const newMeta = document.createElement(\"meta\");\n newMeta.setAttribute(\"property\", meta.property);\n newMeta.setAttribute(\"content\", content);\n document.head.appendChild(newMeta);\n }\n return;\n }\n\n // Handle standard meta tags (name attribute)\n if (meta.name) {\n const existing = document.querySelector(`meta[name=\"${meta.name}\"]`);\n if (existing) {\n existing.setAttribute(\"content\", content);\n } else {\n const newMeta = document.createElement(\"meta\");\n newMeta.setAttribute(\"name\", meta.name);\n newMeta.setAttribute(\"content\", content);\n document.head.appendChild(newMeta);\n }\n }\n }\n}\n","import { $inject } from \"alepha\";\nimport type { Head, SimpleHead } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"./HeadProvider.ts\";\n\n/**\n * Server-side head provider that fills head content from route configurations.\n *\n * Used by ReactServerProvider to collect title, meta tags, and other head\n * elements which are then rendered by ReactServerTemplateProvider.\n */\nexport class ServerHeadProvider {\n protected readonly headProvider = $inject(HeadProvider);\n\n /**\n * Resolve global head configuration (htmlAttributes only).\n *\n * Used for early streaming optimization - htmlAttributes can be sent\n * before page loaders run since they come from global $head only.\n */\n public resolveGlobalHead(): Head {\n return this.headProvider.resolveGlobalHead();\n }\n\n /**\n * Fill head state from route configurations.\n * Delegates to HeadProvider to merge head data from all route layers.\n */\n public fillHead(state: { head: SimpleHead; layers: Array<any> }): void {\n this.headProvider.fillHead(state as any);\n }\n}\n","import { useInject } from \"@alepha/react\";\nimport { Alepha } from \"alepha\";\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { BrowserHeadProvider } from \"../providers/BrowserHeadProvider.ts\";\n\n/**\n * ```tsx\n * const App = () => {\n * const [head, setHead] = useHead({\n * // will set the document title on the first render\n * title: \"My App\",\n * });\n *\n * return (\n * // This will update the document title when the button is clicked\n * <button onClick={() => setHead({ title: \"Change Title\" })}>\n * Change Title {head.title}\n * </button>\n * );\n * }\n * ```\n */\nexport const useHead = (options?: UseHeadOptions): UseHeadReturn => {\n const alepha = useInject(Alepha);\n\n const current = useMemo(() => {\n if (!alepha.isBrowser()) {\n return {};\n }\n\n return alepha.inject(BrowserHeadProvider).getHead(window.document);\n }, []);\n\n const setHead = useCallback((head?: Head | ((previous?: Head) => Head)) => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n alepha\n .inject(BrowserHeadProvider)\n .renderHead(\n window.document,\n typeof head === \"function\" ? head(current) : head || {},\n );\n }, []);\n\n useEffect(() => {\n if (options) {\n setHead(options);\n }\n }, []);\n\n return [current, setHead];\n};\n\nexport type UseHeadOptions = Head | ((previous?: Head) => Head);\n\nexport type UseHeadReturn = [\n Head,\n (head?: Head | ((previous?: Head) => Head)) => void,\n];\n","import { AlephaReact } from \"@alepha/react\";\nimport { $module } from \"alepha\";\nimport { SeoExpander } from \"./helpers/SeoExpander.ts\";\nimport { $head } from \"./primitives/$head.ts\";\nimport { BrowserHeadProvider } from \"./providers/BrowserHeadProvider.ts\";\nimport { HeadProvider } from \"./providers/HeadProvider.ts\";\nimport { ServerHeadProvider } from \"./providers/ServerHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./helpers/SeoExpander.ts\";\nexport * from \"./hooks/useHead.ts\";\nexport * from \"./interfaces/Head.ts\";\nexport * from \"./primitives/$head.ts\";\nexport * from \"./providers/BrowserHeadProvider.ts\";\nexport * from \"./providers/ServerHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Fill `<head>` server & client side.\n *\n * Generate SEO-friendly meta tags and titles for your React application using AlephaReactHead module.\n *\n * This module provides services and primitives to manage the document head both on the server and client side,\n * ensuring that your application is optimized for search engines and social media sharing.\n *\n * @see {@link ServerHeadProvider}\n * @module alepha.react.head\n */\nexport const AlephaReactHead = $module({\n name: \"alepha.react.head\",\n primitives: [$head],\n services: [\n AlephaReact,\n BrowserHeadProvider,\n HeadProvider,\n SeoExpander,\n ServerHeadProvider,\n ],\n});\n"],"mappings":"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"}
@@ -31,15 +31,8 @@ interface LocalizeProps {
31
31
  }
32
32
  declare const Localize: (props: LocalizeProps) => string | number;
33
33
  //#endregion
34
- //#region ../../src/i18n/hooks/useI18n.d.ts
35
- /**
36
- * Hook to access the i18n service.
37
- */
38
- declare const useI18n: <S extends object, K$1 extends keyof ServiceDictionary<S>>() => I18nProvider<S, K$1>;
39
- type ServiceDictionary<T extends object> = { [K in keyof T]: T[K] extends DictionaryPrimitive<infer U> ? U : never };
40
- //#endregion
41
34
  //#region ../../src/i18n/providers/I18nProvider.d.ts
42
- declare class I18nProvider<S extends object, K$1 extends keyof ServiceDictionary<S>> {
35
+ declare class I18nProvider<S extends object, K extends keyof ServiceDictionary<S>> {
43
36
  protected log: alepha_logger0.Logger;
44
37
  protected alepha: Alepha;
45
38
  protected dateTimeProvider: DateTimeProvider;
@@ -70,7 +63,7 @@ declare class I18nProvider<S extends object, K$1 extends keyof ServiceDictionary
70
63
  get lang(): string;
71
64
  translate: (key: string, args?: string[]) => string;
72
65
  readonly l: (value: I18nLocalizeType, options?: I18nLocalizeOptions) => string | number;
73
- readonly tr: (key: keyof ServiceDictionary<S>[K$1], options?: {
66
+ readonly tr: (key: keyof ServiceDictionary<S>[K], options?: {
74
67
  args?: string[];
75
68
  default?: string;
76
69
  }) => string;
@@ -149,6 +142,13 @@ declare class DictionaryPrimitive<T extends Record<string, string>> extends Prim
149
142
  protected onInit(): void;
150
143
  }
151
144
  //#endregion
145
+ //#region ../../src/i18n/hooks/useI18n.d.ts
146
+ /**
147
+ * Hook to access the i18n service.
148
+ */
149
+ declare const useI18n: <S extends object, K extends keyof ServiceDictionary<S>>() => I18nProvider<S, K>;
150
+ type ServiceDictionary<T extends object> = { [K in keyof T]: T[K] extends DictionaryPrimitive<infer U> ? U : never };
151
+ //#endregion
152
152
  //#region ../../src/i18n/index.d.ts
153
153
  declare module "alepha" {
154
154
  interface State {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/i18n/components/Localize.tsx","../../src/i18n/hooks/useI18n.ts","../../src/i18n/providers/I18nProvider.ts","../../src/i18n/primitives/$dictionary.ts","../../src/i18n/index.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/i18n/components/Localize.tsx","../../src/i18n/providers/I18nProvider.ts","../../src/i18n/primitives/$dictionary.ts","../../src/i18n/hooks/useI18n.ts","../../src/i18n/index.ts"],"mappings":";;;;;;;UAIiB,aAAA;EACf,KAAA,oBAAyB,IAAA,GAAO,QAAA,GAAW,YAAA;;;;AAD7C;EAME,MAAA,GAAS,IAAA,CAAK,mBAAA;;;;;;;;;;EAUd,IAAA,wBAA4B,IAAA,CAAK,qBAAA;EAfD;;;;;EAqBhC,QAAA;AAAA;AAAA,cAGI,QAAA,GAAY,KAAA,EAAO,aAAA;;;cCvBZ,YAAA,mCAEK,iBAAA,CAAkB,CAAA;EAAA,UAExB,GAAA,EAFuB,cAAA,CAEpB,MAAA;EAAA,UACH,MAAA,EAAM,MAAA;EAAA,UACN,gBAAA,EAAgB,gBAAA;EAAA,UAEhB,MAAA,EAAM,sBAAA,CAAA,uBAAA,CAFU,OAAA,CAEV,OAAA;EAAA,SAMA,QAAA,EAAU,KAAA;IACxB,MAAA;IACA,IAAA;IACA,IAAA;IACA,MAAA,QAAc,OAAA,CAAQ,MAAA;IACtB,YAAA,EAAc,MAAA;EAAA;EAGhB,OAAA;;;EAIO,UAAA;IAAc,MAAA,GAAS,KAAA,EAAO,IAAA;EAAA;EAG9B,YAAA;IAAgB,MAAA,GAAS,KAAA;EAAA;EAAA,IAGrB,SAAA,CAAA;;qBAeQ,QAAA,EArBsB,OAAA,CAqBd,aAAA;EAAA,mBAQR,OAAA,EARQ,OAAA,CAQD,aAAA;EAAA,UAgChB,aAAA,CAAA;EAOH,OAAA,GAAiB,IAAA,aAAY,OAAA;EAAA,mBAiBjB,MAAA,EAjBiB,OAAA,CAiBX,aAAA;EAAA,IAwBd,IAAA,CAAA;EAOJ,SAAA,GAAa,GAAA,UAAa,IAAA;EAAA,SAoBjB,CAAA,GACd,KAAA,EAAO,gBAAA,EACP,OAAA,GAAS,mBAAA;EAAA,SA4DK,EAAA,GACd,GAAA,QAAW,iBAAA,CAAkB,CAAA,EAAG,CAAA,GAChC,OAAA;IACE,IAAA;IACA,OAAA;EAAA;EAAA,UAUM,MAAA,CAAO,IAAA,UAAc,IAAA;AAAA;AAAA,KASrB,gBAAA,qBAAqC,IAAA,GAAO,QAAA,GAAW,YAAA;AAAA,UAElD,mBAAA;EAvPC;;;;EA4PhB,MAAA,GAAS,IAAA,CAAK,mBAAA;EAtPE;;;;;;;;;EAgQhB,IAAA,wBAA4B,IAAA,CAAK,qBAAA;EArGxB;;;;;EA2GT,QAAA;AAAA;;;;;;;;ADhRF;;;;;;;;;;;;;;;;;;;;;;;AAuBC;;;cEOY,WAAA;EAAA,WAAyB,MAAA,kBAAsB,OAAA,EACjD,0BAAA,CAA2B,CAAA,IACnC,mBAAA,CAAoB,CAAA;EAAA;;UAMN,0BAAA,WAAqC,MAAA;EACpD,IAAA;EACA,IAAA;EACA,IAAA,QAAY,KAAA;IAAQ,OAAA,EAAS,CAAA;EAAA;AAAA;AAAA,cAKlB,mBAAA,WACD,MAAA,0BACF,SAAA,CAAU,0BAAA,CAA2B,CAAA;EAAA,UACnC,QAAA,EAAQ,YAAA;EAAA,UACR,MAAA,CAAA;AAAA;;;;;;cC/CC,OAAA,qCAEK,iBAAA,CAAkB,CAAA,QAC/B,YAAA,CAAa,CAAA,EAAG,CAAA;AAAA,KAKT,iBAAA,mCACE,CAAA,GAAI,CAAA,CAAE,CAAA,UAAW,mBAAA,YAA+B,CAAA;;;;YCD3C,KAAA;IACf,wBAAA;EAAA;AAAA;;;;;;;;cAaS,eAAA,EAAe,OAAA,CAAA,OAAA,CAI1B,OAAA,CAJ0B,MAAA"}