@alepha/react 0.14.2 → 0.14.4

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 (57) hide show
  1. package/dist/auth/index.browser.js +29 -14
  2. package/dist/auth/index.browser.js.map +1 -1
  3. package/dist/auth/index.js +960 -195
  4. package/dist/auth/index.js.map +1 -1
  5. package/dist/core/index.d.ts +4 -0
  6. package/dist/core/index.d.ts.map +1 -1
  7. package/dist/core/index.js +7 -4
  8. package/dist/core/index.js.map +1 -1
  9. package/dist/head/index.browser.js +59 -19
  10. package/dist/head/index.browser.js.map +1 -1
  11. package/dist/head/index.d.ts +99 -560
  12. package/dist/head/index.d.ts.map +1 -1
  13. package/dist/head/index.js +92 -87
  14. package/dist/head/index.js.map +1 -1
  15. package/dist/router/index.browser.js +30 -15
  16. package/dist/router/index.browser.js.map +1 -1
  17. package/dist/router/index.d.ts +616 -192
  18. package/dist/router/index.d.ts.map +1 -1
  19. package/dist/router/index.js +961 -196
  20. package/dist/router/index.js.map +1 -1
  21. package/package.json +4 -4
  22. package/src/auth/__tests__/$auth.spec.ts +188 -0
  23. package/src/core/__tests__/Router.spec.tsx +169 -0
  24. package/src/core/hooks/useAction.browser.spec.tsx +569 -0
  25. package/src/core/hooks/useAction.ts +11 -0
  26. package/src/form/hooks/useForm.browser.spec.tsx +366 -0
  27. package/src/head/helpers/SeoExpander.spec.ts +203 -0
  28. package/src/head/hooks/useHead.spec.tsx +288 -0
  29. package/src/head/index.ts +11 -28
  30. package/src/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  31. package/src/head/providers/BrowserHeadProvider.ts +25 -19
  32. package/src/head/providers/HeadProvider.ts +76 -10
  33. package/src/head/providers/ServerHeadProvider.ts +22 -138
  34. package/src/i18n/__tests__/integration.spec.tsx +239 -0
  35. package/src/i18n/components/Localize.spec.tsx +357 -0
  36. package/src/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  37. package/src/i18n/providers/I18nProvider.spec.ts +389 -0
  38. package/src/router/__tests__/page-head-browser.browser.spec.ts +91 -0
  39. package/src/router/__tests__/page-head.spec.ts +44 -0
  40. package/src/router/__tests__/seo-head.spec.ts +121 -0
  41. package/src/router/atoms/ssrManifestAtom.ts +60 -0
  42. package/src/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  43. package/src/router/errors/Redirection.ts +1 -1
  44. package/src/router/index.shared.ts +1 -0
  45. package/src/router/index.ts +16 -2
  46. package/src/router/primitives/$page.browser.spec.tsx +702 -0
  47. package/src/router/primitives/$page.spec.tsx +702 -0
  48. package/src/router/primitives/$page.ts +46 -10
  49. package/src/router/providers/ReactBrowserProvider.ts +14 -29
  50. package/src/router/providers/ReactBrowserRouterProvider.ts +5 -0
  51. package/src/router/providers/ReactPageProvider.ts +11 -4
  52. package/src/router/providers/ReactServerProvider.spec.tsx +316 -0
  53. package/src/router/providers/ReactServerProvider.ts +331 -315
  54. package/src/router/providers/ReactServerTemplateProvider.ts +775 -0
  55. package/src/router/providers/SSRManifestProvider.ts +365 -0
  56. package/src/router/services/ReactPageServerService.ts +5 -3
  57. package/src/router/services/ReactRouter.ts +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/router/errors/Redirection.ts","../../src/router/providers/ReactPageProvider.ts","../../src/router/services/ReactPageService.ts","../../src/router/primitives/$page.ts","../../src/core/index.ts","../../src/router/providers/ReactBrowserProvider.ts","../../src/router/providers/ReactServerProvider.ts","../../src/router/index.ts","../../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/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA;;;;ACGgC;AAEjB,cDLF,WAAA,SAAoB,WAAA,CCKlB;EAAA,SAAA,QAAA,EAAA,MAAA;aAAA,CAAA,QAAA,EAAA,MAAA;;;;cAAT,aAEJ,QAAA,CAFa;qBAEb,QAAA,CAAA;;;wBAG6B,QAAQ,cAAc;;;;;;AClBvC,UD+lBG,cAAA,SACP,IChmBI,CDgmBC,oBChmBD,EAAA,UAAA,GAAA,QAAA,CAAA,CAAA;EAFT,QAAA,CAAA,EDmmBQ,cCnmBR,EAAA;;AC+GgC,UFigBpB,SAAA,SAAkB,cEjgBE,CAAA;EACX,IAAA,EAAA,MAAA;EACM,IAAA,EAAA,MAAA;EAuBrB,MAAA,CAAA,EF2eA,SE3eA;EAgBuB,KAAA,EAAA,MAAA;;AAAZ,UF+dL,KAAA,CE/dK;EAA6C,MAAA,CAAA,EAAA;IAAN,KAAA,CAAA,EFiejD,MEjeiD,CAAA,MAAA,EAAA,GAAA,CAAA;IAOrC,MAAA,CAAA,EF2dX,ME3dW,CAAA,MAAA,EAAA,GAAA,CAAA;IAAR,OAAA,CAAA,EF6dF,ME7dE,CAAA,MAAA,EAAA,GAAA,CAAA;EAQC,CAAA;EAAS,IAAA,EAAA,MAAA;EAAZ,KAAA,CAAA,EFydJ,MEzdI,CAAA,MAAA,EAAA,GAAA,CAAA;EAQuB,KAAA,CAAA,EFkd3B,KEld2B;EAAS,IAAA,CAAA,EAAA,MAAA;EAAZ,OAAA,EFodvB,SEpduB;EAAnB,KAAA,EAAA,MAAA;EAMI,IAAA,EAAA,MAAA;EAAN,KAAA,CAAA,EFidH,SEjdG;EAAoC,KAAA,CAAA,EAAA,OAAA;;AAKxB,KFgdb,iBAAA,GAAoB,IEhdP,CFgdY,KEhdZ,EAAA,SAAA,GAAA,OAAA,GAAA,MAAA,CAAA;AA4DO,UF2Zf,gBAAA,CE3Ze;EAAR;;;EASH,MAAA,EFsZX,KEtZW,CFsZL,KEtZK,CAAA;EAKU;;;EAoDnB,GAAA,EFkWL,GElWK;EACH;;;EAEQ,OAAA,EFoWN,YEpWM;EAAW;AAE5B;;EACqC,MAAA,EFsW3B,MEtW2B,CAAA,MAAA,EAAA,GAAA,CAAA;EACX;;;EAEwB,KAAA,EFwWzC,MExWyC,CAAA,MAAA,EAAA,MAAA,CAAA;EAAQ;;;EAyB5C,IAAA,EFoVN,MEpVM,CAAA,MAAA,EAAA,GAAA,CAAA;EACD,IAAA,CAAA,EAAA,MAAA;;;;;;;uBD/VS,gBAAA;oCAGT,6BACR;;cAES;;iCAOD,6BACR,QAAQ;;;;;ACqPQ,UAjJJ,oBAiJI,CAAA,gBAhJH,gBAgJG,GAhJgB,gBAgJhB,EAAA,eAAA,MAAA,GA/IK,aA+IL,EAAA,qBAAA,MAAA,GA9IW,mBA8IX,CAAA,CAAA;EAKU;;;AAoD/B;;EAES,IAAA,CAAA,EAAA,MAAA;EACJ;;;AAEL;;;;EAGgC,IAAA,CAAA,EAAA,MAAA;EACS;;;;;EAyB3B,MAAA,CAAA,EAlNH,OAkNG;EACD;;;;;;;AA0Bb;AAKA;AAEA;AAEA;AAcA;AAMA;;EACqC,OAAA,CAAA,EAAA,CAAA,OAAA,EA3Pf,WA2Pe,CA3PH,OA2PG,EA3PM,YA2PN,CAAA,EAAA,GA3PwB,KA2PxB,CA3P8B,MA2P9B,CAAA;EAE3B;;;;;EAID,KAAA,CAAA,EAAA,GAAA,GA1PO,OA0PP,CA1Pe,MA0Pf,CAAA;EAAyB;;;;;AAKlC;EACkB,SAAA,CAAA,EAxPJ,EAwPI,CAxPD,MAwPC,GAxPQ,YAwPR,CAAA;EAAmB;;;;;;EAInC,IAAA,CAAA,EAAA,GAAA,GApPa,OAoPb,CAAA;IAAI,OAAA,EApP4B,EAoP5B,CApP+B,MAoP/B,GApPwC,YAoPxC,CAAA;EAEM,CAAA,CAAA;EACR;;;;EAGC,QAAA,CAAA,EApPQ,KAoPR,CApPc,aAoPK,CAAA,GAAA,CAAA,GAAA,GApPmB,KAoPnB,CApPyB,aAoPzB,CAAA,CAAA;EACpB;;;EAGS,MAAA,CAAA,EAnPF,aAmPE,CAnPY,gBAmPZ,EAnP8B,YAmP9B,EAAA,GAAA,CAAA;EAAe;;AAAgB;AAGvB;;;;EC3ae;;;;;;;;ACO8B;AAInD;;;;;;;;;AAWf;;;;;AAUA;AAEE;;;;;AA6PF;;;;ACnSmG;;;;cAIpF,CAAA,EHoOE,YGpOF;EAAA;AAAA;;;;;;;EAMgB,MAAA,CAAA,EAAA,OAAA,GAAA;IAAA,OAAA,CAAA,EH2Ob,KG3Oa,CH2OP,OG3OO,CH2OC,iBG3OD,CH2OmB,OG3OnB,CAAA,CAAA,CAAA;EAUlB,CAAA;EAkBX,KAAA,CAAA,EHkNQ,gBGlNR;;;;;EAlB6B,MAAA,CAAA,EAAA,OAAA,GH0OV,eG1OU;EAAA;AAoB/B;AAEE;EAAA,gBAAA,CAAA,EAAA,CAAA,OAAA,EHyN6B,aGzN7B,EAAA,GAAA,OAAA;;;;;;;ACjCiD;;;;;;;;;;;;;;;;;;;;;;;;;ACLnD;AAMA;AA+CA;;;;;;;;;AAaA;;;;EC5Da,SAAA,CAAA,EN0SC,aM1SU;;AAEd,KN2SE,YAAA,GM3SF,CAAA,KAAA,EN4SD,KM5SC,EAAA,KAAA,EN6SD,gBM7SC,EAAA,GN8SL,SM9SK,GN8SO,WM9SP,GAAA,SAAA;AACA,cN+SG,aM/SH,CAAA,gBNgTQ,gBMhTR,GNgT2B,gBMhT3B,EAAA,eAAA,MAAA,GNiTgB,aMjThB,EAAA,qBAAA,MAAA,GNkTsB,mBMlTtB,CAAA,SNmTA,SMnTA,CNmTU,oBMnTV,CNmT+B,OMnT/B,ENmTwC,MMnTxC,ENmTgD,YMnThD,CAAA,CAAA,CAAA;EAuCwB,mBAAA,gBAAA,EN6QG,gBM7QH;EAAY,UAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EA2Cd,IAAA,IAAA,CAAA,CAAA,EAAA,MAAA;EAAY;;;;;ACrG5C;EACgC,MAAA,CAAA,OAAA,CAAA,EP8VlB,0BO9VkB,CAAA,EP+V3B,OO/V2B,CP+VnB,yBO/VmB,CAAA;EAER,KAAA,CAAA,OAAA,CAAA,EPiWO,0BOjWP,CAAA,EPiWoC,OOjWpC,CAAA;IAAc,IAAA,EAAA,MAAA;IAApB,QAAA,EPmWJ,QOnWI;EAEO,CAAA,CAAA;EAiBI,KAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAA;EAAwB,QAAA,CAAA,MAAA,EAAA,GAAA,CAAA,EAAA,MAAA;;AAc1C,UPsVM,gBAAA,COtVN;EACA,KAAA,CAAA,EPsVD,OOtVC;EAAM,MAAA,CAAA,EPuVN,OOvVM;;KP0VL,aAAA;KAEA,mBAAA;AQ/XC,URiYI,0BAAA,CQ/XhB;EAF8B,MAAA,CAAA,ERkYpB,MQlYoB,CAAA,MAAA,EAAA,MAAA,CAAA;EAAoB,KAAA,CAAA,ERmYzC,MQnYyC,CAAA,MAAA,EAAA,MAAA,CAAA;;;;AAMnD;AAIA;;EAC6B,IAAA,CAAA,EAAA,OAAA;EADM,SAAA,CAAA,EAAA,OAAA;;URqYlB,yBAAA;;SAER;ESjYI,QAAA,CAAA,EA+BZ,MAAA;AAED;AAA6B,UToWZ,iBSpWY,CAAA,gBTqWX,gBSrWW,GTqWQ,gBSrWR,CAAA,CAAA;EAAoB,MAAA,ETuWvC,OSvWuC,CAAA,QAAA,CAAA,STuWb,OSvWa,GTwW3C,MSxW2C,CTwWpC,OSxWoC,CAAA,QAAA,CAAA,CAAA,GTyW3C,MSzW2C,CAAA,MAAA,EAAA,MAAA,CAAA;EAAS,KAAA,ET2WjD,OS3WiD,CAAA,OAAA,CAAA,ST2WxB,OS3WwB,GT4WpD,MS5WoD,CT4W7C,OS5W6C,CAAA,OAAA,CAAA,CAAA,GT6WpD,MS7WoD,CAAA,MAAA,EAAA,MAAA,CAAA;;AAE9C,KT8WA,WS9Wa,CAAA,gBT+WP,gBS/WO,GT+WY,gBS/WZ,EAAA,qBAAA,MAAA,GTgXO,mBShXP,CAAA,GTiXrB,iBSjXqB,CTiXH,OSjXG,CAAA,GTkXvB,YSlXuB,GTmXvB,ISnXuB,CTmXlB,gBSnXkB,EAAA,QAAA,GAAA,SAAA,CAAA;AACvB,KToXU,aAAA,GACR,mBSrXF,GAAA,CAAA,CAAA,KAAA,ETsXW,gBStXX,EAAA,GTsXgC,mBStXhC,GAAA,SAAA,CAAA;KTwXG,mBAAA,GACD,gBSxXM,GAAA;EAAoB,KAAA,CAAA,ET0XhB,YS1XgB,GT0XD,gBS1XC;EAAS,IAAA,CAAA,ET2X1B,YS3X0B,GT2XX,gBS3XW;CAAI;KT8XtC,gBAAA;KAEA,YAAA;;EUvbQ,QAAA,CAAA,EAAA,MAAA;EACoB,MAAA,CAAA,EAAA,MAAA;CACQ;;;;;Iba5B;;;;ICKP,oBAEJ,EAAA;MAFa,IAAA,EAAA,MAAA;MAAA,EAAA,CAAA,EAAA,MAAA;IAAA,CAAA;IAKsC;;;;IAAf,sBAAA,EAAA;MAAA,IAAA,EAAA,MAAA;MA6kBrB,EAAA,CAAA,EAAA,MACf;IAAa,CAAA;IACF;;;AAab;IAOiB,oBAAK,EAAA;MAEV,IAAA,EAAA,MAAA;MACC,EAAA,CAAA,EAAA,MAAA;MAEC,KAAA,EG1lBD,KH0lBC;IAIJ,CAAA;IACA;;;;IASE,kBAAiB,EAAA;MAOZ,IAAA,EAAA,MAAgB;MAIjB,EAAA,CAAA,EAAA,MAAA;IAAN,CAAA;EAKH;;;;;;;;;AC9pBP;;;;;cGoBM,aAEJ,QAAA,CAFa;iBAEb,QAAA,CAAA;;;wBAGsB,QAAQ,cAAc;;ALX9C;;;cKiBa,qBAAmB,QAAA,CAAA,cAAA;EJZ1B,iBAEJ,EIkBA,QAAA,CAAA,OJlBA,CAAA,KAAA,GAAA,QAFa,CAAA;AAAA,CAAA,CAAA,EAAA,8BAAA,CAAA;AAAA,KIsBH,2BAAA,GAA8B,MJtB3B,CAAA,OIuBN,mBAAA,CAAoB,MJvBd,CAAA;eAAA,QAAA,CAAA;EAKsC,UAAA,KAAA,CAAA;IAAd,CIuBlC,mBAAA,CAAoB,GAAA,CJvBc,EIuBR,2BJvBQ;EAAR;;AE2FM,KEqLzB,mBAAA,GFrLyB;EACX,MAAA,CAAA,EEqLf,KFrLe,CEqLT,iBFrLS,CAAA;CACM,GAAA;EAuBrB,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,GAAA;CAgBuB;;;cGnJ5B,oBAAS;wCAGb,QAAA,CAAA,QAAA;;;;wBAGsB,QAAQ,cAAc;;INDjC,yBAAoB,CAAA,EAAA,OAAW;;;;ACGZ;AAEjB;;AAAA,cKMF,kBLNE,EKMgB,QAAA,CAAA,ILNhB,UKMgB,OLNhB,CAAA;EAKsC,SAAA,EKmBnD,QAAA,CAAA,OLnBmD;EAAd,YAAA,kBAAA,CAAA;IAAR,QAAA,mBAAA;IAAO,IAAA,kBAAA;EAAA,CAAA,CAAA;CAAA,CAAA,EAAA,6BAAA,CAAA;AA6kBrB,KKxjBL,0BAAA,GAA6B,MLyjBvC,CAAA,OKxjBO,kBAAA,CAAmB,MLwjB1B,CAAA;eAAa,QAAA,CAAA;EACF,UAAA,KAAA,CAAA;IADH,CKnjBL,kBAAA,CAAmB,GAAA,CLmjBd,EKnjBoB,0BLmjBpB;EAAI;AAcd;AAOA;;;;;;;;;kCMvmBkC;;;IPNrB;;;;MCKP,OAEJ,CAAA,EMOc,aNPd;MAFa,KAAA,EMUF,gBNVE;IAAA,CAAA;IAAA;;;IAKgB,yBAAA,EAAA;MAAO,OAAA,CAAA,EMWtB,aNXsB;MAAA,KAAA,EMYzB,gBNZyB;MAAA,IAAA,EAAA,MAAA;IA6kBrB,CAAA;IACF;;;IAAD,sBAAA,EAAA;MAcG,IAAU,EMxkBf,WN2kBD;MAIW,OAAA,EM9kBP,SN8kBO;MAEV,KAAA,EM/kBC,gBN+kBD;MACC,SAAA,CAAA,EM/kBK,mBN+kBL;IAEC,CAAA;IAIJ;;;IAMA,wBAAA,EAAA;MAAS,QAAA,EMnlBH,gBNmlBG;MAIP,KAAA,EMtlBC,gBNslBgB;MAOZ,SAAA,CAAA,EM5lBC,aN4lBe;IAIjB,CAAA;IAAN;;;IAeA,0BAAA,EAAA;MAKD,KAAA,EM9mBI,gBN8mBJ;IAKD,CAAA;IAAM;;;;MClrBQ,KAAA,EKqET,gBLrEyB;MAGzB,KAAA,EKmEA,KLnEA;IAGC,CAAA;IAFT;;;IAUA,sBAAA,EAAA;MAAO,KAAA,EK8DC,gBL9DD;;;;ACoGZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AHpGA;UQLiB,IAAA,SAAa,YAAY;;APQV;AAEjB;;AAAA,UOJE,GAAA,CPIF;EAKsC;EAAd,WAAA,CAAA,EAAA,MAAA;EAAR;EAAO,KAAA,CAAA,EAAA,MAAA;EAAA;EAAA,GAAA,CAAA,EAAA,MAAA;EA6kBrB;EACF,QAAA,CAAA,EAAA,MAAA;EACF;EADH,MAAA,CAAA,EAAA,MAAA;EAAI;EAcG,IAAA,CAAA,EAAA,SAAU,GAAA,SAGhB,GAAA,SAHwB,GAAA,SAAc,GAAA,MAAA;EAOhC;EAEL,UAAA,CAAA,EAAA,MAAA;EACC;EAEC,WAAA,CAAA,EAAA,MAAA;EAIJ;EACA,QAAA,CAAA,EAAA,MAAA;EAEC;EAGD,OAAA,CAAA,EAAA;IAAS;IAIP,IAAA,CAAA,EAAA,SAAiB,GAAA,qBAAO,GAAA,KAAA,GAAA,QAAA;IAOnB;IAID,IAAA,CAAA,EAAA,MAAA;IAAN;IAKH,OAAA,CAAA,EAAA,MAAA;IAKI;IAKD,KAAA,CAAA,EAAA,MAAA;IAKD;IAKD,WAAA,CAAA,EAAA,MAAA;IAAM;;;;EClrBQ,EAAA,CAAA,EAAA;IAGT;IAGC,KAAA,CAAA,EAAA,MAAA;IAFT;IASQ,WAAA,CAAA,EAAA,MAAA;IACA;IAAR,KAAA,CAAA,EAAA,MAAA;EAAO,CAAA;;UMgDK,UAAA;;ELoDA,cAAA,CAAA,EAAA,MAAoB;EACnB,cAAA,CAAA,EKlDC,MLkDD,CAAA,MAAA,EAAA,MAAA,CAAA;EAAmB,cAAA,CAAA,EKjDlB,MLiDkB,CAAA,MAAA,EAAA,MAAA,CAAA;EACX;EACM,IAAA,CAAA,EKjDvB,KLiDuB,CKjDjB,QLiDiB,CAAA;EAuBrB;EAgBuB,IAAA,CAAA,EKtFzB,KLsFyB,CAAA;IAAS,GAAA,EAAA,MAAA;IAArB,IAAA,EAAA,MAAA;EAA6C,CAAA,CAAA;EAAN;EAOrC,MAAA,CAAA,EK3Fb,KL2Fa,CK3FP,ML2FO,CAAA,MAAA,EAAA,MAAA,GAAA,OAAA,CAAA,CAAA;;AAQP,UKhGA,QAAA,CLgGA;EAAS;EAAZ,IAAA,CAAA,EAAA,MAAA;EAQuB;EAAS,QAAA,CAAA,EAAA,MAAA;EAAZ;EAAnB,OAAA,EAAA,MAAA;;;;;;;;;;;;;;;;;AHrKf;;;;ACGgC;AAEjB;AAAA,cQJF,WAAA,CRIE;QAAA,CAAA,IAAA,EQHO,IRGP,CAAA,EAAA;IAKsC,IAAA,EQP3C,QRO2C,EAAA;IAAd,IAAA,EQN7B,KRM6B,CAAA;MAAR,GAAA,EAAA,MAAA;MAAO,IAAA,EAAA,MAAA;IAAA,CAAA,CAAA;EAAA,CAAA;EA6kBrB,UAAA,eACf,CAAA,IAAA,EQ7iBgC,IR6iBhC,EAAA,IAAA,EQ7iB4C,QR6iB5C,EAAA,CAAA,EAAA,IAAA;EAAa,UAAA,aAAA,CAAA,IAAA,EQlgBiB,IRkgBjB,EAAA,IAAA,EQlgB6B,QRkgB7B,EAAA,CAAA,EAAA,IAAA;;;;cSvmBF,YAAA;kCACmB;WAEd,MAAM,cAAc;kBAEb;6BAiBI,wBAAwB;iCAa3C,kBACC,yBACA;;;;;;;cCnCE;YAAkB,uBAAoB;;;KAMvC,oBAAA,GAAuB,cAAc;cAIpC,aAAA,SAAsB,UAAU;+BAChB;;AXE7B;;;;;;;;;;;;;;;;AAAA;;;;ACKM,cWFO,OXIX,EAAA,CAAA,OAAA,CAAA,EWJgC,cXIhC,EAAA,GWJiD,aXEpC;AAAA,KW+BH,cAAA,GAAiB,IX/Bd,GAAA,CAAA,CAAA,QAAA,CAAA,EW+BkC,IX/BlC,EAAA,GW+B2C,IX/B3C,CAAA;AAAA,KWiCH,aAAA,GXjCG,CWkCb,IXlCa,EAKsC,CAAA,IAAA,CAAA,EW8B3C,IX9B2C,GAAA,CAAA,CAAA,QAAA,CAAA,EW8BvB,IX9BuB,EAAA,GW8Bd,IX9Bc,CAAA,EAAA,GAAA,IAAA;;;cYzBxC,kBAAA;mCACoB;2CACQ;wCAAA,QAAA,CAEH;qCAYM;qDAmEjC;8CASmC;;gCAwBd;oCAYI;;;;;iDC5GhB,mBAAmB,0CACX,6CACM;WAEvB,gBAAgB,mBAAmB,SAAS;;;;;IdT1C,IAAA,EcgBH,IdhBe;;;;ACGO;AAEjB;;;;;;;;;AAklBE,catjBJ,ebujBX,EavjB0B,QAAA,CAAA,ObujB1B,CanjBA,QAAA,CAJ0B,MAAA,CbujB1B"}
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,6 +1,6 @@
1
1
  import { AlephaReact, useInject } from "@alepha/react";
2
- import { $hook, $inject, $module, Alepha, KIND, Primitive, createPrimitive } from "alepha";
3
- import { ServerTimingProvider } from "alepha/server";
2
+ import { $inject, $module, Alepha, KIND, Primitive, createPrimitive } from "alepha";
3
+ import { $logger } from "alepha/logger";
4
4
  import { useCallback, useEffect, useMemo } from "react";
5
5
 
6
6
  //#region ../../src/head/helpers/SeoExpander.ts
@@ -136,9 +136,43 @@ var SeoExpander = class {
136
136
 
137
137
  //#endregion
138
138
  //#region ../../src/head/providers/HeadProvider.ts
139
+ /**
140
+ * Provides methods to fill and merge head information into the application state.
141
+ *
142
+ * Used both on server and client side to manage document head.
143
+ *
144
+ * @see {@link SeoExpander}
145
+ * @see {@link ServerHeadProvider}
146
+ * @see {@link BrowserHeadProvider}
147
+ */
139
148
  var HeadProvider = class {
149
+ log = $logger();
140
150
  seoExpander = $inject(SeoExpander);
141
151
  global = [];
152
+ /**
153
+ * Track if we've warned about page-level htmlAttributes to avoid spam.
154
+ */
155
+ warnedAboutHtmlAttributes = false;
156
+ /**
157
+ * Resolve global head configuration (from $head primitives only).
158
+ *
159
+ * This is used to get htmlAttributes early, before page loaders run.
160
+ * Only htmlAttributes from global $head are allowed; page-level htmlAttributes
161
+ * are ignored for early streaming optimization.
162
+ *
163
+ * @returns Merged global head with htmlAttributes
164
+ */
165
+ resolveGlobalHead() {
166
+ const head = {};
167
+ for (const h of this.global ?? []) {
168
+ const resolved = typeof h === "function" ? h() : h;
169
+ if (resolved.htmlAttributes) head.htmlAttributes = {
170
+ ...head.htmlAttributes,
171
+ ...resolved.htmlAttributes
172
+ };
173
+ }
174
+ return head;
175
+ }
142
176
  fillHead(state) {
143
177
  state.head = { ...state.head };
144
178
  for (const h of this.global ?? []) {
@@ -178,10 +212,10 @@ var HeadProvider = class {
178
212
  else state.head.title = head.title;
179
213
  state.head.titleSeparator = head.titleSeparator;
180
214
  }
181
- if (head.htmlAttributes) state.head.htmlAttributes = {
182
- ...state.head.htmlAttributes,
183
- ...head.htmlAttributes
184
- };
215
+ if (head.htmlAttributes && !this.warnedAboutHtmlAttributes) {
216
+ this.warnedAboutHtmlAttributes = true;
217
+ this.log.warn("Page-level htmlAttributes are ignored. Use global $head() for htmlAttributes instead, as they are sent before page loaders run for early streaming optimization.");
218
+ }
185
219
  if (head.bodyAttributes) state.head.bodyAttributes = {
186
220
  ...state.head.bodyAttributes,
187
221
  ...head.bodyAttributes
@@ -208,91 +242,32 @@ var HeadPrimitive = class extends Primitive {
208
242
  };
209
243
  $head[KIND] = HeadPrimitive;
210
244
 
211
- //#endregion
212
- //#region ../../src/head/providers/ServerHeadProvider.ts
213
- var ServerHeadProvider = class {
214
- headProvider = $inject(HeadProvider);
215
- serverTimingProvider = $inject(ServerTimingProvider);
216
- onServerRenderEnd = $hook({
217
- on: "react:server:render:end",
218
- handler: async (ev) => {
219
- this.serverTimingProvider.beginTiming("renderHead");
220
- this.headProvider.fillHead(ev.state);
221
- if (ev.state.head) ev.html = this.renderHead(ev.html, ev.state.head);
222
- this.serverTimingProvider.endTiming("renderHead");
223
- }
224
- });
225
- renderHead(template, head) {
226
- let result = template;
227
- const htmlAttributes = head.htmlAttributes;
228
- if (htmlAttributes) result = result.replace(/<html([^>]*)>/i, (_, existingAttrs) => `<html${this.mergeAttributes(existingAttrs, htmlAttributes)}>`);
229
- const bodyAttributes = head.bodyAttributes;
230
- if (bodyAttributes) result = result.replace(/<body([^>]*)>/i, (_, existingAttrs) => `<body${this.mergeAttributes(existingAttrs, bodyAttributes)}>`);
231
- let headContent = "";
232
- const title = head.title;
233
- if (title) if (template.includes("<title>")) result = result.replace(/<title>(.*?)<\/title>/i, () => `<title>${this.escapeHtml(title)}</title>`);
234
- else headContent += `<title>${this.escapeHtml(title)}</title>\n`;
235
- if (head.meta) for (const meta of head.meta) headContent += this.renderMetaTag(meta);
236
- if (head.link) for (const link of head.link) headContent += `<link rel="${this.escapeHtml(link.rel)}" href="${this.escapeHtml(link.href)}">\n`;
237
- if (head.script) for (const script of head.script) headContent += this.renderScriptTag(script);
238
- result = result.replace(/<head([^>]*)>(.*?)<\/head>/is, (_, existingAttrs, existingHead) => `<head${existingAttrs}>${existingHead}${headContent}</head>`);
239
- return result.trim();
240
- }
241
- mergeAttributes(existing, attrs) {
242
- const merged = {
243
- ...this.parseAttributes(existing),
244
- ...attrs
245
- };
246
- return Object.entries(merged).map(([k, v]) => ` ${k}="${this.escapeHtml(v)}"`).join("");
247
- }
248
- parseAttributes(attrStr) {
249
- attrStr = attrStr.replaceAll("'", "\"");
250
- const attrs = {};
251
- const attrRegex = /([^\s=]+)(?:="([^"]*)")?/g;
252
- let match = attrRegex.exec(attrStr);
253
- while (match) {
254
- attrs[match[1]] = match[2] ?? "";
255
- match = attrRegex.exec(attrStr);
256
- }
257
- return attrs;
258
- }
259
- escapeHtml(str) {
260
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
261
- }
262
- renderMetaTag(meta) {
263
- if (meta.property) return `<meta property="${this.escapeHtml(meta.property)}" content="${this.escapeHtml(meta.content)}">\n`;
264
- if (meta.name) return `<meta name="${this.escapeHtml(meta.name)}" content="${this.escapeHtml(meta.content)}">\n`;
265
- return "";
266
- }
267
- renderScriptTag(script) {
268
- return `<script ${Object.entries(script).filter(([, value]) => value !== false).map(([key, value]) => {
269
- if (value === true) return key;
270
- return `${key}="${this.escapeHtml(String(value))}"`;
271
- }).join(" ")}><\/script>\n`;
272
- }
273
- };
274
-
275
245
  //#endregion
276
246
  //#region ../../src/head/providers/BrowserHeadProvider.ts
247
+ /**
248
+ * Browser-side head provider that manages document head elements.
249
+ *
250
+ * Used by ReactBrowserProvider and ReactBrowserRouterProvider to update
251
+ * document title, meta tags, and other head elements during client-side
252
+ * navigation.
253
+ */
277
254
  var BrowserHeadProvider = class {
255
+ alepha = $inject(Alepha);
278
256
  headProvider = $inject(HeadProvider);
279
257
  get document() {
280
258
  return window.document;
281
259
  }
282
- onBrowserRender = $hook({
283
- on: "react:browser:render",
284
- handler: async ({ state }) => {
285
- this.headProvider.fillHead(state);
286
- if (state.head) this.renderHead(this.document, state.head);
287
- }
288
- });
289
- onTransitionEnd = $hook({
290
- on: "react:transition:end",
291
- handler: async ({ state }) => {
292
- this.headProvider.fillHead(state);
293
- if (state.head) this.renderHead(this.document, state.head);
294
- }
295
- });
260
+ /**
261
+ * Fill head state from route configurations and render to document.
262
+ * Combines fillHead from HeadProvider with renderHead to the DOM.
263
+ *
264
+ * Only runs in browser environment - no-op on server.
265
+ */
266
+ fillAndRenderHead(state) {
267
+ if (!this.alepha.isBrowser()) return;
268
+ this.headProvider.fillHead(state);
269
+ if (state.head) this.renderHead(this.document, state.head);
270
+ }
296
271
  getHead(document) {
297
272
  return {
298
273
  get title() {
@@ -374,6 +349,34 @@ var BrowserHeadProvider = class {
374
349
  }
375
350
  };
376
351
 
352
+ //#endregion
353
+ //#region ../../src/head/providers/ServerHeadProvider.ts
354
+ /**
355
+ * Server-side head provider that fills head content from route configurations.
356
+ *
357
+ * Used by ReactServerProvider to collect title, meta tags, and other head
358
+ * elements which are then rendered by ReactServerTemplateProvider.
359
+ */
360
+ var ServerHeadProvider = class {
361
+ headProvider = $inject(HeadProvider);
362
+ /**
363
+ * Resolve global head configuration (htmlAttributes only).
364
+ *
365
+ * Used for early streaming optimization - htmlAttributes can be sent
366
+ * before page loaders run since they come from global $head only.
367
+ */
368
+ resolveGlobalHead() {
369
+ return this.headProvider.resolveGlobalHead();
370
+ }
371
+ /**
372
+ * Fill head state from route configurations.
373
+ * Delegates to HeadProvider to merge head data from all route layers.
374
+ */
375
+ fillHead(state) {
376
+ this.headProvider.fillHead(state);
377
+ }
378
+ };
379
+
377
380
  //#endregion
378
381
  //#region ../../src/head/hooks/useHead.ts
379
382
  /**
@@ -427,11 +430,13 @@ const AlephaReactHead = $module({
427
430
  primitives: [$head],
428
431
  services: [
429
432
  AlephaReact,
430
- ServerHeadProvider,
431
- HeadProvider
433
+ BrowserHeadProvider,
434
+ HeadProvider,
435
+ SeoExpander,
436
+ ServerHeadProvider
432
437
  ]
433
438
  });
434
439
 
435
440
  //#endregion
436
- export { $head, AlephaReactHead, HeadPrimitive, SeoExpander, ServerHeadProvider, useHead };
441
+ export { $head, AlephaReactHead, BrowserHeadProvider, HeadPrimitive, SeoExpander, ServerHeadProvider, useHead };
437
442
  //# sourceMappingURL=index.js.map
@@ -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/ServerHeadProvider.ts","../../src/head/providers/BrowserHeadProvider.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 type { PageRoute, ReactRouterState } from \"@alepha/react/router\";\nimport { $inject } from \"alepha\";\nimport { SeoExpander } from \"../helpers/SeoExpander.ts\";\nimport type { Head } from \"../interfaces/Head.ts\";\n\nexport class HeadProvider {\n protected readonly seoExpander = $inject(SeoExpander);\n\n public global?: Array<Head | (() => Head)> = [];\n\n public fillHead(state: ReactRouterState) {\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: ReactRouterState, 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: PageRoute,\n state: ReactRouterState,\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 if (head.htmlAttributes) {\n state.head.htmlAttributes = {\n ...state.head.htmlAttributes,\n ...head.htmlAttributes,\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","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 { $hook, $inject } from \"alepha\";\nimport { ServerTimingProvider } from \"alepha/server\";\nimport type { HeadMeta, SimpleHead } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"./HeadProvider.ts\";\n\nexport class ServerHeadProvider {\n protected readonly headProvider = $inject(HeadProvider);\n protected readonly serverTimingProvider = $inject(ServerTimingProvider);\n\n protected readonly onServerRenderEnd = $hook({\n on: \"react:server:render:end\",\n handler: async (ev) => {\n this.serverTimingProvider.beginTiming(\"renderHead\");\n this.headProvider.fillHead(ev.state);\n if (ev.state.head) {\n ev.html = this.renderHead(ev.html, ev.state.head);\n }\n this.serverTimingProvider.endTiming(\"renderHead\");\n },\n });\n\n public renderHead(template: string, head: SimpleHead): string {\n let result = template;\n\n // Inject htmlAttributes\n const htmlAttributes = head.htmlAttributes;\n if (htmlAttributes) {\n result = result.replace(\n /<html([^>]*)>/i,\n (_, existingAttrs) =>\n `<html${this.mergeAttributes(existingAttrs, htmlAttributes)}>`,\n );\n }\n\n // Inject bodyAttributes\n const bodyAttributes = head.bodyAttributes;\n if (bodyAttributes) {\n result = result.replace(\n /<body([^>]*)>/i,\n (_, existingAttrs) =>\n `<body${this.mergeAttributes(existingAttrs, bodyAttributes)}>`,\n );\n }\n\n // Build head content\n let headContent = \"\";\n const title = head.title;\n if (title) {\n if (template.includes(\"<title>\")) {\n result = result.replace(\n /<title>(.*?)<\\/title>/i,\n () => `<title>${this.escapeHtml(title)}</title>`,\n );\n } else {\n headContent += `<title>${this.escapeHtml(title)}</title>\\n`;\n }\n }\n\n if (head.meta) {\n for (const meta of head.meta) {\n headContent += this.renderMetaTag(meta);\n }\n }\n\n if (head.link) {\n for (const link of head.link) {\n headContent += `<link rel=\"${this.escapeHtml(link.rel)}\" href=\"${this.escapeHtml(link.href)}\">\\n`;\n }\n }\n\n if (head.script) {\n for (const script of head.script) {\n headContent += this.renderScriptTag(script);\n }\n }\n\n // Inject into <head>...</head>\n result = result.replace(\n /<head([^>]*)>(.*?)<\\/head>/is,\n (_, existingAttrs, existingHead) =>\n `<head${existingAttrs}>${existingHead}${headContent}</head>`,\n );\n\n return result.trim();\n }\n\n protected mergeAttributes(\n existing: string,\n attrs: Record<string, string>,\n ): string {\n const existingAttrs = this.parseAttributes(existing);\n const merged = { ...existingAttrs, ...attrs };\n return Object.entries(merged)\n .map(([k, v]) => ` ${k}=\"${this.escapeHtml(v)}\"`)\n .join(\"\");\n }\n\n protected parseAttributes(attrStr: string): Record<string, string> {\n attrStr = attrStr.replaceAll(\"'\", '\"');\n\n const attrs: Record<string, string> = {};\n const attrRegex = /([^\\s=]+)(?:=\"([^\"]*)\")?/g;\n let match: RegExpExecArray | null = attrRegex.exec(attrStr);\n\n while (match) {\n attrs[match[1]] = match[2] ?? \"\";\n match = attrRegex.exec(attrStr);\n }\n\n return attrs;\n }\n\n protected escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n }\n\n protected renderMetaTag(meta: HeadMeta): string {\n // OpenGraph tags use property attribute\n if (meta.property) {\n return `<meta property=\"${this.escapeHtml(meta.property)}\" content=\"${this.escapeHtml(meta.content)}\">\\n`;\n }\n // Standard meta tags use name attribute\n if (meta.name) {\n return `<meta name=\"${this.escapeHtml(meta.name)}\" content=\"${this.escapeHtml(meta.content)}\">\\n`;\n }\n return \"\";\n }\n\n protected renderScriptTag(script: Record<string, string | boolean>): string {\n const attrs = Object.entries(script)\n .filter(([, value]) => value !== false)\n .map(([key, value]) => {\n // Boolean attributes - render without value if true\n if (value === true) {\n return key;\n }\n return `${key}=\"${this.escapeHtml(String(value))}\"`;\n })\n .join(\" \");\n return `<script ${attrs}></script>\\n`;\n }\n}\n","import { $hook, $inject } from \"alepha\";\nimport type { Head, HeadMeta } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"./HeadProvider.ts\";\n\nexport class BrowserHeadProvider {\n protected readonly headProvider = $inject(HeadProvider);\n\n protected get document(): Document {\n return window.document;\n }\n\n protected readonly onBrowserRender = $hook({\n on: \"react:browser:render\",\n handler: async ({ state }) => {\n this.headProvider.fillHead(state);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\n },\n });\n\n protected readonly onTransitionEnd = $hook({\n on: \"react:transition:end\",\n handler: async ({ state }) => {\n this.headProvider.fillHead(state);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\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 type {\n PageConfigSchema,\n TPropsDefault,\n TPropsParentDefault,\n} from \"@alepha/react/router\";\nimport { $module } from \"alepha\";\nimport { $head } from \"./primitives/$head.ts\";\nimport type { Head } from \"./interfaces/Head.ts\";\nimport { ServerHeadProvider } from \"./providers/ServerHeadProvider.ts\";\nimport { HeadProvider } from \"./providers/HeadProvider.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\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n// Augment PagePrimitiveOptions in router module\ndeclare module \"@alepha/react/router\" {\n interface PagePrimitiveOptions<\n TConfig extends PageConfigSchema = PageConfigSchema,\n TProps extends object = TPropsDefault,\n TPropsParent extends object = TPropsParentDefault,\n > {\n head?: Head | ((props: TProps, previous?: Head) => Head);\n }\n}\n\n// Augment ReactRouterState in router module\ndeclare module \"@alepha/react/router\" {\n interface ReactRouterState {\n head: Head;\n }\n}\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: [AlephaReact, ServerHeadProvider, HeadProvider],\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 ?? (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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"}
@@ -2,6 +2,7 @@ import { $atom, $env, $hook, $inject, $module, $use, Alepha, AlephaError, KIND,
2
2
  import { AlephaDateTime, DateTimeProvider } from "alepha/datetime";
3
3
  import { $logger } from "alepha/logger";
4
4
  import { AlephaServerLinks, LinkProvider } from "alepha/server/links";
5
+ import { BrowserHeadProvider } from "@alepha/react/head";
5
6
  import { RouterProvider } from "alepha/router";
6
7
  import { StrictMode, createContext, createElement, memo, use, useEffect, useRef, useState } from "react";
7
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -22,6 +23,15 @@ var ReactPageService = class {
22
23
  }
23
24
  };
24
25
 
26
+ //#endregion
27
+ //#region ../../src/router/constants/PAGE_PRELOAD_KEY.ts
28
+ /**
29
+ * Symbol key for SSR module preloading path.
30
+ * Using Symbol.for() allows the Vite plugin to inject this at build time.
31
+ * @internal
32
+ */
33
+ const PAGE_PRELOAD_KEY = Symbol.for("alepha.page.preload");
34
+
25
35
  //#endregion
26
36
  //#region ../../src/router/primitives/$page.ts
27
37
  /**
@@ -36,7 +46,7 @@ var ReactPageService = class {
36
46
  * - Type-safe URL parameter and query string validation
37
47
  *
38
48
  * **Data Loading**
39
- * - Server-side data fetching with the `resolve` function
49
+ * - Server-side data fetching with the `loader` function
40
50
  * - Automatic serialization and hydration for SSR
41
51
  * - Access to request context, URL params, and parent data
42
52
  *
@@ -73,7 +83,7 @@ var ReactPageService = class {
73
83
  * params: t.object({ id: t.integer() }),
74
84
  * query: t.object({ tab: t.optional(t.text()) })
75
85
  * },
76
- * resolve: async ({ params }) => {
86
+ * loader: async ({ params }) => {
77
87
  * const user = await userApi.getUser(params.id);
78
88
  * return { user };
79
89
  * },
@@ -86,7 +96,7 @@ var ReactPageService = class {
86
96
  * const projectSection = $page({
87
97
  * path: "/projects/:id",
88
98
  * children: () => [projectBoard, projectSettings],
89
- * resolve: async ({ params }) => {
99
+ * loader: async ({ params }) => {
90
100
  * const project = await projectApi.get(params.id);
91
101
  * return { project };
92
102
  * },
@@ -105,7 +115,7 @@ var ReactPageService = class {
105
115
  * static: {
106
116
  * entries: posts.map(p => ({ params: { slug: p.slug } }))
107
117
  * },
108
- * resolve: async ({ params }) => {
118
+ * loader: async ({ params }) => {
109
119
  * const post = await loadPost(params.slug);
110
120
  * return { post };
111
121
  * }
@@ -586,7 +596,7 @@ const RouterLayerContext = createContext(void 0);
586
596
  * import { Redirection } from "@alepha/react";
587
597
  *
588
598
  * const MyPage = $page({
589
- * resolve: async () => {
599
+ * loader: async () => {
590
600
  * if (needRedirect) {
591
601
  * throw new Redirection("/new-path");
592
602
  * }
@@ -742,13 +752,13 @@ function parseAnimation(animationLike, state, type = "enter") {
742
752
 
743
753
  //#endregion
744
754
  //#region ../../src/router/providers/ReactPageProvider.ts
745
- const envSchema$1 = t.object({ REACT_STRICT_MODE: t.boolean({ default: true }) });
755
+ const envSchema = t.object({ REACT_STRICT_MODE: t.boolean({ default: true }) });
746
756
  /**
747
757
  * Handle page routes for React applications. (Browser and Server)
748
758
  */
749
759
  var ReactPageProvider = class {
750
760
  log = $logger();
751
- env = $env(envSchema$1);
761
+ env = $env(envSchema);
752
762
  alepha = $inject(Alepha);
753
763
  pages = [];
754
764
  getPages() {
@@ -868,11 +878,11 @@ var ReactPageProvider = class {
868
878
  }
869
879
  forceRefresh = true;
870
880
  }
871
- if (!route$1.resolve) continue;
881
+ if (!route$1.loader) continue;
872
882
  try {
873
883
  const args = Object.create(state);
874
884
  Object.assign(args, config, context);
875
- const props = await route$1.resolve?.(args) ?? {};
885
+ const props = await route$1.loader?.(args) ?? {};
876
886
  it.props = { ...props };
877
887
  context = {
878
888
  ...context,
@@ -880,7 +890,7 @@ var ReactPageProvider = class {
880
890
  };
881
891
  } catch (e) {
882
892
  if (e instanceof Redirection) return { redirect: e.redirect };
883
- this.log.error("Page resolver has failed", e);
893
+ this.log.error("Page loader has failed", e);
884
894
  it.error = e;
885
895
  break;
886
896
  }
@@ -1075,6 +1085,7 @@ var ReactBrowserRouterProvider = class extends RouterProvider {
1075
1085
  log = $logger();
1076
1086
  alepha = $inject(Alepha);
1077
1087
  pageApi = $inject(ReactPageProvider);
1088
+ browserHeadProvider = $inject(BrowserHeadProvider);
1078
1089
  add(entry) {
1079
1090
  this.pageApi.add(entry);
1080
1091
  }
@@ -1145,6 +1156,7 @@ var ReactBrowserRouterProvider = class extends RouterProvider {
1145
1156
  this.alepha.store.set("alepha.react.router.state", state);
1146
1157
  await this.alepha.events.emit("react:action:end", { type: "transition" });
1147
1158
  await this.alepha.events.emit("react:transition:end", { state });
1159
+ this.browserHeadProvider.fillAndRenderHead(state);
1148
1160
  }
1149
1161
  root(state) {
1150
1162
  return this.pageApi.root(state);
@@ -1153,7 +1165,6 @@ var ReactBrowserRouterProvider = class extends RouterProvider {
1153
1165
 
1154
1166
  //#endregion
1155
1167
  //#region ../../src/router/providers/ReactBrowserProvider.ts
1156
- const envSchema = t.object({ REACT_ROOT_ID: t.text({ default: "root" }) });
1157
1168
  /**
1158
1169
  * React browser renderer configuration atom
1159
1170
  */
@@ -1163,18 +1174,21 @@ const reactBrowserOptions = $atom({
1163
1174
  default: { scrollRestoration: "top" }
1164
1175
  });
1165
1176
  var ReactBrowserProvider = class {
1166
- env = $env(envSchema);
1167
1177
  log = $logger();
1168
1178
  client = $inject(LinkProvider);
1169
1179
  alepha = $inject(Alepha);
1170
1180
  router = $inject(ReactBrowserRouterProvider);
1171
1181
  dateTimeProvider = $inject(DateTimeProvider);
1182
+ browserHeadProvider = $inject(BrowserHeadProvider);
1172
1183
  options = $use(reactBrowserOptions);
1184
+ get rootId() {
1185
+ return "root";
1186
+ }
1173
1187
  getRootElement() {
1174
- const root = this.document.getElementById(this.env.REACT_ROOT_ID);
1188
+ const root = this.document.getElementById(this.rootId);
1175
1189
  if (root) return root;
1176
1190
  const div = this.document.createElement("div");
1177
- div.id = this.env.REACT_ROOT_ID;
1191
+ div.id = this.rootId;
1178
1192
  this.document.body.prepend(div);
1179
1193
  return div;
1180
1194
  }
@@ -1307,6 +1321,7 @@ var ReactBrowserProvider = class {
1307
1321
  hydration,
1308
1322
  state: this.state
1309
1323
  });
1324
+ this.browserHeadProvider.fillAndRenderHead(this.state);
1310
1325
  window.addEventListener("popstate", () => {
1311
1326
  if (this.base + this.state.url.pathname === this.location.pathname) return;
1312
1327
  this.log.debug("Popstate event triggered - rendering new state", { url: this.location.pathname + this.location.search });
@@ -1596,5 +1611,5 @@ const AlephaReactRouter = $module({
1596
1611
  });
1597
1612
 
1598
1613
  //#endregion
1599
- export { $page, AlephaReactRouter, ErrorViewer_default as ErrorViewer, Link_default as Link, NestedView_default as NestedView, NotFound_default as NotFound, PagePrimitive, ReactBrowserProvider, ReactBrowserRendererProvider, ReactBrowserRouterProvider, ReactPageProvider, ReactPageService, ReactRouter, Redirection, RouterLayerContext, isPageRoute, reactBrowserOptions, useActive, useQueryParams, useRouter, useRouterState };
1614
+ export { $page, AlephaReactRouter, ErrorViewer_default as ErrorViewer, Link_default as Link, NestedView_default as NestedView, NotFound_default as NotFound, PAGE_PRELOAD_KEY, PagePrimitive, ReactBrowserProvider, ReactBrowserRendererProvider, ReactBrowserRouterProvider, ReactPageProvider, ReactPageService, ReactRouter, Redirection, RouterLayerContext, isPageRoute, reactBrowserOptions, useActive, useQueryParams, useRouter, useRouterState };
1600
1615
  //# sourceMappingURL=index.browser.js.map