@jasonshimmy/vite-plugin-cer-app 0.23.2 → 0.25.0

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 (404) hide show
  1. package/dist/cli/adapters/netlify.js +4 -2
  2. package/dist/cli/adapters/netlify.js.map +1 -1
  3. package/dist/cli/checks/lighthouse.d.ts +26 -0
  4. package/dist/cli/checks/lighthouse.d.ts.map +1 -0
  5. package/dist/cli/checks/lighthouse.js +121 -0
  6. package/dist/cli/checks/lighthouse.js.map +1 -0
  7. package/dist/cli/checks/links.d.ts +12 -0
  8. package/dist/cli/checks/links.d.ts.map +1 -0
  9. package/dist/cli/checks/links.js +89 -0
  10. package/dist/cli/checks/links.js.map +1 -0
  11. package/dist/cli/checks/performance.d.ts +17 -0
  12. package/dist/cli/checks/performance.d.ts.map +1 -0
  13. package/dist/cli/checks/performance.js +61 -0
  14. package/dist/cli/checks/performance.js.map +1 -0
  15. package/dist/cli/commands/check.d.ts +3 -0
  16. package/dist/cli/commands/check.d.ts.map +1 -0
  17. package/dist/cli/commands/check.js +85 -0
  18. package/dist/cli/commands/check.js.map +1 -0
  19. package/dist/cli/commands/preview.d.ts.map +1 -1
  20. package/dist/cli/commands/preview.js +4 -2
  21. package/dist/cli/commands/preview.js.map +1 -1
  22. package/dist/cli/create/index.d.ts +11 -1
  23. package/dist/cli/create/index.d.ts.map +1 -1
  24. package/dist/cli/create/index.js +135 -65
  25. package/dist/cli/create/index.js.map +1 -1
  26. package/dist/cli/create/templates/spa/package.json.tpl +5 -3
  27. package/dist/cli/create/templates/ssg/cer.config.ts.tpl +2 -0
  28. package/dist/cli/create/templates/ssg/package.json.tpl +5 -3
  29. package/dist/cli/create/templates/ssr/package.json.tpl +5 -3
  30. package/dist/cli/index.js +2 -0
  31. package/dist/cli/index.js.map +1 -1
  32. package/dist/index.d.ts +4 -2
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/plugin/build-ssg.d.ts +22 -6
  35. package/dist/plugin/build-ssg.d.ts.map +1 -1
  36. package/dist/plugin/build-ssg.js +226 -17
  37. package/dist/plugin/build-ssg.js.map +1 -1
  38. package/dist/plugin/content/index.d.ts +1 -1
  39. package/dist/plugin/content/index.d.ts.map +1 -1
  40. package/dist/plugin/content/index.js +8 -8
  41. package/dist/plugin/content/index.js.map +1 -1
  42. package/dist/plugin/content/parser.d.ts +3 -3
  43. package/dist/plugin/content/parser.d.ts.map +1 -1
  44. package/dist/plugin/content/parser.js +103 -24
  45. package/dist/plugin/content/parser.js.map +1 -1
  46. package/dist/plugin/dev-server.d.ts +4 -1
  47. package/dist/plugin/dev-server.d.ts.map +1 -1
  48. package/dist/plugin/dev-server.js.map +1 -1
  49. package/dist/plugin/dts-generator.d.ts.map +1 -1
  50. package/dist/plugin/dts-generator.js +50 -8
  51. package/dist/plugin/dts-generator.js.map +1 -1
  52. package/dist/plugin/generated-dir.d.ts +2 -0
  53. package/dist/plugin/generated-dir.d.ts.map +1 -1
  54. package/dist/plugin/generated-dir.js +17 -6
  55. package/dist/plugin/generated-dir.js.map +1 -1
  56. package/dist/plugin/html-post-process.d.ts +7 -0
  57. package/dist/plugin/html-post-process.d.ts.map +1 -1
  58. package/dist/plugin/html-post-process.js +26 -0
  59. package/dist/plugin/html-post-process.js.map +1 -1
  60. package/dist/plugin/index.d.ts.map +1 -1
  61. package/dist/plugin/index.js +60 -13
  62. package/dist/plugin/index.js.map +1 -1
  63. package/dist/plugin/transforms/auto-import.d.ts.map +1 -1
  64. package/dist/plugin/transforms/auto-import.js +7 -0
  65. package/dist/plugin/transforms/auto-import.js.map +1 -1
  66. package/dist/plugin/transforms/prune-runtime-css.d.ts +8 -0
  67. package/dist/plugin/transforms/prune-runtime-css.d.ts.map +1 -0
  68. package/dist/plugin/transforms/prune-runtime-css.js +40 -0
  69. package/dist/plugin/transforms/prune-runtime-css.js.map +1 -0
  70. package/dist/runtime/app-template.d.ts +1 -1
  71. package/dist/runtime/app-template.d.ts.map +1 -1
  72. package/dist/runtime/app-template.js +206 -36
  73. package/dist/runtime/app-template.js.map +1 -1
  74. package/dist/runtime/composables/index.d.ts +4 -0
  75. package/dist/runtime/composables/index.d.ts.map +1 -1
  76. package/dist/runtime/composables/index.js +2 -0
  77. package/dist/runtime/composables/index.js.map +1 -1
  78. package/dist/runtime/composables/use-active-headings.d.ts +24 -0
  79. package/dist/runtime/composables/use-active-headings.d.ts.map +1 -0
  80. package/dist/runtime/composables/use-active-headings.js +112 -0
  81. package/dist/runtime/composables/use-active-headings.js.map +1 -0
  82. package/dist/runtime/composables/use-content-page.d.ts +45 -0
  83. package/dist/runtime/composables/use-content-page.d.ts.map +1 -0
  84. package/dist/runtime/composables/use-content-page.js +181 -0
  85. package/dist/runtime/composables/use-content-page.js.map +1 -0
  86. package/dist/runtime/composables/use-content-search.d.ts.map +1 -1
  87. package/dist/runtime/composables/use-content-search.js +1 -5
  88. package/dist/runtime/composables/use-content-search.js.map +1 -1
  89. package/dist/runtime/composables/use-fetch.d.ts +2 -1
  90. package/dist/runtime/composables/use-fetch.d.ts.map +1 -1
  91. package/dist/runtime/composables/use-fetch.js +112 -43
  92. package/dist/runtime/composables/use-fetch.js.map +1 -1
  93. package/dist/runtime/composables/use-route.d.ts.map +1 -1
  94. package/dist/runtime/composables/use-route.js +39 -11
  95. package/dist/runtime/composables/use-route.js.map +1 -1
  96. package/dist/runtime/content/client.js +2 -2
  97. package/dist/runtime/content/client.js.map +1 -1
  98. package/dist/runtime/entry-server-template.d.ts +16 -1
  99. package/dist/runtime/entry-server-template.d.ts.map +1 -1
  100. package/dist/runtime/entry-server-template.js +120 -13
  101. package/dist/runtime/entry-server-template.js.map +1 -1
  102. package/dist/types/config.d.ts +45 -4
  103. package/dist/types/config.d.ts.map +1 -1
  104. package/dist/types/config.js.map +1 -1
  105. package/dist/types/content.d.ts +18 -1
  106. package/dist/types/content.d.ts.map +1 -1
  107. package/dist/types/middleware.d.ts +2 -2
  108. package/dist/types/middleware.d.ts.map +1 -1
  109. package/package.json +22 -19
  110. package/.github/copilot-instructions.md +0 -169
  111. package/.github/workflows/publish.yml +0 -257
  112. package/.nvmrc +0 -1
  113. package/CHANGELOG.md +0 -236
  114. package/CLAUDE.md +0 -113
  115. package/commits.txt +0 -1
  116. package/cypress.config.ts +0 -16
  117. package/docs/authentication.md +0 -285
  118. package/docs/cli.md +0 -324
  119. package/docs/components.md +0 -237
  120. package/docs/composables.md +0 -907
  121. package/docs/configuration.md +0 -696
  122. package/docs/content.md +0 -570
  123. package/docs/data-loading.md +0 -336
  124. package/docs/getting-started.md +0 -237
  125. package/docs/head-management.md +0 -212
  126. package/docs/i18n.md +0 -159
  127. package/docs/layouts.md +0 -224
  128. package/docs/middleware.md +0 -234
  129. package/docs/plugins.md +0 -170
  130. package/docs/rendering-modes.md +0 -377
  131. package/docs/routing.md +0 -485
  132. package/docs/server-api.md +0 -253
  133. package/docs/testing.md +0 -455
  134. package/docs/use-fetch.md +0 -183
  135. package/e2e/cypress/e2e/api.cy.ts +0 -123
  136. package/e2e/cypress/e2e/async-component.cy.ts +0 -27
  137. package/e2e/cypress/e2e/component-splitting.cy.ts +0 -50
  138. package/e2e/cypress/e2e/content.cy.ts +0 -425
  139. package/e2e/cypress/e2e/cookie.cy.ts +0 -85
  140. package/e2e/cypress/e2e/data.cy.ts +0 -158
  141. package/e2e/cypress/e2e/error-boundary.cy.ts +0 -88
  142. package/e2e/cypress/e2e/fouc.cy.ts +0 -254
  143. package/e2e/cypress/e2e/group-meta.cy.ts +0 -36
  144. package/e2e/cypress/e2e/head.cy.ts +0 -89
  145. package/e2e/cypress/e2e/i18n.cy.ts +0 -79
  146. package/e2e/cypress/e2e/interactive.cy.ts +0 -137
  147. package/e2e/cypress/e2e/isr-nested-runtime.cy.ts +0 -102
  148. package/e2e/cypress/e2e/jit-css-custom-colors.cy.ts +0 -43
  149. package/e2e/cypress/e2e/middleware-chain.cy.ts +0 -89
  150. package/e2e/cypress/e2e/middleware-status.cy.ts +0 -30
  151. package/e2e/cypress/e2e/middleware.cy.ts +0 -45
  152. package/e2e/cypress/e2e/navigate.cy.ts +0 -39
  153. package/e2e/cypress/e2e/observability.cy.ts +0 -77
  154. package/e2e/cypress/e2e/per-route-error.cy.ts +0 -42
  155. package/e2e/cypress/e2e/per-route-render.cy.ts +0 -70
  156. package/e2e/cypress/e2e/preview-hardening.cy.ts +0 -88
  157. package/e2e/cypress/e2e/routes.cy.ts +0 -128
  158. package/e2e/cypress/e2e/seo-meta.cy.ts +0 -108
  159. package/e2e/cypress/e2e/server-middleware.cy.ts +0 -46
  160. package/e2e/cypress/e2e/session-rotation.cy.ts +0 -25
  161. package/e2e/cypress/e2e/session.cy.ts +0 -73
  162. package/e2e/cypress/e2e/ssr-render-error.cy.ts +0 -68
  163. package/e2e/cypress/e2e/synthetic-404.cy.ts +0 -45
  164. package/e2e/cypress/e2e/use-page-data.cy.ts +0 -122
  165. package/e2e/cypress/e2e/use-state.cy.ts +0 -138
  166. package/e2e/cypress/support/commands.ts +0 -120
  167. package/e2e/cypress/support/e2e.ts +0 -10
  168. package/e2e/cypress/tsconfig.json +0 -14
  169. package/e2e/kitchen-sink/app/components/ks-badge.ts +0 -8
  170. package/e2e/kitchen-sink/app/composables/useKsCounter.ts +0 -9
  171. package/e2e/kitchen-sink/app/error.ts +0 -18
  172. package/e2e/kitchen-sink/app/layouts/admin.ts +0 -13
  173. package/e2e/kitchen-sink/app/layouts/default.ts +0 -25
  174. package/e2e/kitchen-sink/app/layouts/group.ts +0 -9
  175. package/e2e/kitchen-sink/app/layouts/minimal.ts +0 -7
  176. package/e2e/kitchen-sink/app/loading.ts +0 -9
  177. package/e2e/kitchen-sink/app/middleware/auth.ts +0 -9
  178. package/e2e/kitchen-sink/app/middleware/custom-log.ts +0 -4
  179. package/e2e/kitchen-sink/app/middleware/group-auth.ts +0 -7
  180. package/e2e/kitchen-sink/app/middleware/logger.ts +0 -14
  181. package/e2e/kitchen-sink/app/pages/(auth)/login.ts +0 -13
  182. package/e2e/kitchen-sink/app/pages/(auth)/protected.ts +0 -16
  183. package/e2e/kitchen-sink/app/pages/404.ts +0 -9
  184. package/e2e/kitchen-sink/app/pages/about.ts +0 -17
  185. package/e2e/kitchen-sink/app/pages/admin/_layout.ts +0 -1
  186. package/e2e/kitchen-sink/app/pages/admin/dashboard.ts +0 -11
  187. package/e2e/kitchen-sink/app/pages/async-component-error-test.ts +0 -18
  188. package/e2e/kitchen-sink/app/pages/async-component-test.ts +0 -18
  189. package/e2e/kitchen-sink/app/pages/blog/[slug].ts +0 -59
  190. package/e2e/kitchen-sink/app/pages/blog/index.ts +0 -51
  191. package/e2e/kitchen-sink/app/pages/content-blog.ts +0 -37
  192. package/e2e/kitchen-sink/app/pages/content-doc.ts +0 -42
  193. package/e2e/kitchen-sink/app/pages/content-fallback.ts +0 -36
  194. package/e2e/kitchen-sink/app/pages/content-guides.ts +0 -35
  195. package/e2e/kitchen-sink/app/pages/content-index.ts +0 -39
  196. package/e2e/kitchen-sink/app/pages/content-search.ts +0 -36
  197. package/e2e/kitchen-sink/app/pages/cookie-test.ts +0 -22
  198. package/e2e/kitchen-sink/app/pages/counter.ts +0 -17
  199. package/e2e/kitchen-sink/app/pages/group-auth-blocked.ts +0 -9
  200. package/e2e/kitchen-sink/app/pages/group-meta-test/_layout.ts +0 -6
  201. package/e2e/kitchen-sink/app/pages/group-meta-test/custom-mw.ts +0 -14
  202. package/e2e/kitchen-sink/app/pages/group-meta-test/page.ts +0 -9
  203. package/e2e/kitchen-sink/app/pages/group-meta-test/protected.ts +0 -8
  204. package/e2e/kitchen-sink/app/pages/head.ts +0 -20
  205. package/e2e/kitchen-sink/app/pages/i18n-test.ts +0 -14
  206. package/e2e/kitchen-sink/app/pages/index.ts +0 -27
  207. package/e2e/kitchen-sink/app/pages/isr-test.ts +0 -17
  208. package/e2e/kitchen-sink/app/pages/items/[id].ts +0 -20
  209. package/e2e/kitchen-sink/app/pages/jit-css-custom-colors-test.ts +0 -14
  210. package/e2e/kitchen-sink/app/pages/loader-error-test.ts +0 -13
  211. package/e2e/kitchen-sink/app/pages/loader-props-test.ts +0 -19
  212. package/e2e/kitchen-sink/app/pages/loader-response-error-test.ts +0 -13
  213. package/e2e/kitchen-sink/app/pages/middleware-chain-test.ts +0 -12
  214. package/e2e/kitchen-sink/app/pages/middleware-status-test.ts +0 -9
  215. package/e2e/kitchen-sink/app/pages/navigate-test.ts +0 -16
  216. package/e2e/kitchen-sink/app/pages/observability-test.ts +0 -25
  217. package/e2e/kitchen-sink/app/pages/per-route-error-test.error.ts +0 -13
  218. package/e2e/kitchen-sink/app/pages/per-route-error-test.ts +0 -15
  219. package/e2e/kitchen-sink/app/pages/plugin-dsd-test.ts +0 -10
  220. package/e2e/kitchen-sink/app/pages/render-error-test.ts +0 -12
  221. package/e2e/kitchen-sink/app/pages/render-server-test.ts +0 -12
  222. package/e2e/kitchen-sink/app/pages/render-spa-test.ts +0 -12
  223. package/e2e/kitchen-sink/app/pages/route-info.ts +0 -14
  224. package/e2e/kitchen-sink/app/pages/seo-test.ts +0 -23
  225. package/e2e/kitchen-sink/app/pages/session-rotation-test.ts +0 -25
  226. package/e2e/kitchen-sink/app/pages/use-state-test.ts +0 -37
  227. package/e2e/kitchen-sink/app/plugins/01.setup.ts +0 -7
  228. package/e2e/kitchen-sink/app/plugins/02.ks-plugin-components.ts +0 -27
  229. package/e2e/kitchen-sink/cer-auto-imports.d.ts +0 -51
  230. package/e2e/kitchen-sink/cer-env.d.ts +0 -35
  231. package/e2e/kitchen-sink/cer-tsconfig.json +0 -30
  232. package/e2e/kitchen-sink/cer.config.ts +0 -43
  233. package/e2e/kitchen-sink/content/01.guides/01.index.md +0 -8
  234. package/e2e/kitchen-sink/content/01.guides/02.intro.md +0 -8
  235. package/e2e/kitchen-sink/content/01.guides/10.advanced.md +0 -8
  236. package/e2e/kitchen-sink/content/blog/2026-04-01-hello.md +0 -26
  237. package/e2e/kitchen-sink/content/blog/2026-04-02-draft.md +0 -10
  238. package/e2e/kitchen-sink/content/blog/index.md +0 -8
  239. package/e2e/kitchen-sink/content/blog/no-frontmatter.md +0 -7
  240. package/e2e/kitchen-sink/content/docs/getting-started.md +0 -48
  241. package/e2e/kitchen-sink/content/index.md +0 -16
  242. package/e2e/kitchen-sink/server/api/echo.ts +0 -12
  243. package/e2e/kitchen-sink/server/api/health.ts +0 -3
  244. package/e2e/kitchen-sink/server/api/posts/[slug].ts +0 -11
  245. package/e2e/kitchen-sink/server/api/posts/index.ts +0 -5
  246. package/e2e/kitchen-sink/server/api/session.ts +0 -23
  247. package/e2e/kitchen-sink/server/data/posts.ts +0 -21
  248. package/e2e/kitchen-sink/server/middleware/01-headers.ts +0 -6
  249. package/e2e/kitchen-sink/server/middleware/02-status-test.ts +0 -10
  250. package/e2e/kitchen-sink/tsconfig.json +0 -3
  251. package/e2e/scripts/clean.mjs +0 -14
  252. package/eslint.config.ts +0 -22
  253. package/src/__tests__/cli/adapters/cloudflare-worker.integration.test.ts +0 -132
  254. package/src/__tests__/cli/adapters/cloudflare.test.ts +0 -332
  255. package/src/__tests__/cli/adapters/netlify-bridge.integration.test.ts +0 -139
  256. package/src/__tests__/cli/adapters/netlify.test.ts +0 -286
  257. package/src/__tests__/cli/adapters/vercel-launcher.integration.test.ts +0 -129
  258. package/src/__tests__/cli/adapters/vercel.test.ts +0 -263
  259. package/src/__tests__/cli/preview-hardening.test.ts +0 -265
  260. package/src/__tests__/cli/preview-isr.test.ts +0 -320
  261. package/src/__tests__/index.test.ts +0 -21
  262. package/src/__tests__/plugin/app-template.test.ts +0 -166
  263. package/src/__tests__/plugin/build-ssg-render.test.ts +0 -155
  264. package/src/__tests__/plugin/build-ssg.test.ts +0 -598
  265. package/src/__tests__/plugin/build-ssr.test.ts +0 -192
  266. package/src/__tests__/plugin/cer-app-plugin.test.ts +0 -552
  267. package/src/__tests__/plugin/content/emitter.test.ts +0 -117
  268. package/src/__tests__/plugin/content/loader.test.ts +0 -154
  269. package/src/__tests__/plugin/content/parser.test.ts +0 -381
  270. package/src/__tests__/plugin/content/path-utils.test.ts +0 -69
  271. package/src/__tests__/plugin/content/search.test.ts +0 -119
  272. package/src/__tests__/plugin/dev-server.test.ts +0 -813
  273. package/src/__tests__/plugin/dts-generator.test.ts +0 -394
  274. package/src/__tests__/plugin/entry-server-template.test.ts +0 -385
  275. package/src/__tests__/plugin/generated-dir.test.ts +0 -144
  276. package/src/__tests__/plugin/html-post-process.test.ts +0 -146
  277. package/src/__tests__/plugin/path-utils.test.ts +0 -411
  278. package/src/__tests__/plugin/resolve-config.test.ts +0 -233
  279. package/src/__tests__/plugin/scanner.test.ts +0 -186
  280. package/src/__tests__/plugin/transforms/auto-import.test.ts +0 -510
  281. package/src/__tests__/plugin/transforms/head-inject.test.ts +0 -178
  282. package/src/__tests__/plugin/virtual/composables.test.ts +0 -57
  283. package/src/__tests__/plugin/virtual/content-components.test.ts +0 -66
  284. package/src/__tests__/plugin/virtual/error.test.ts +0 -71
  285. package/src/__tests__/plugin/virtual/layouts.test.ts +0 -70
  286. package/src/__tests__/plugin/virtual/loading.test.ts +0 -72
  287. package/src/__tests__/plugin/virtual/middleware.test.ts +0 -83
  288. package/src/__tests__/plugin/virtual/plugins.test.ts +0 -129
  289. package/src/__tests__/plugin/virtual/routes.test.ts +0 -884
  290. package/src/__tests__/plugin/virtual/server-api.test.ts +0 -164
  291. package/src/__tests__/plugin/virtual/server-middleware.test.ts +0 -102
  292. package/src/__tests__/runtime/app-template.test.ts +0 -208
  293. package/src/__tests__/runtime/define-middleware.test.ts +0 -64
  294. package/src/__tests__/runtime/define-server-middleware.test.ts +0 -54
  295. package/src/__tests__/runtime/entry-client-template.test.ts +0 -61
  296. package/src/__tests__/runtime/isr-handler.test.ts +0 -338
  297. package/src/__tests__/runtime/oauth/handler.test.ts +0 -358
  298. package/src/__tests__/runtime/oauth/providers.test.ts +0 -47
  299. package/src/__tests__/runtime/oauth-index.test.ts +0 -43
  300. package/src/__tests__/runtime/use-auth.test.ts +0 -169
  301. package/src/__tests__/runtime/use-content-search-composable.test.ts +0 -405
  302. package/src/__tests__/runtime/use-content-search.test.ts +0 -163
  303. package/src/__tests__/runtime/use-content.test.ts +0 -226
  304. package/src/__tests__/runtime/use-cookie.test.ts +0 -277
  305. package/src/__tests__/runtime/use-fetch-component.test.ts +0 -214
  306. package/src/__tests__/runtime/use-fetch.test.ts +0 -377
  307. package/src/__tests__/runtime/use-head.test.ts +0 -311
  308. package/src/__tests__/runtime/use-inject-client.test.ts +0 -67
  309. package/src/__tests__/runtime/use-inject.test.ts +0 -66
  310. package/src/__tests__/runtime/use-locale.test.ts +0 -152
  311. package/src/__tests__/runtime/use-navigate.test.ts +0 -121
  312. package/src/__tests__/runtime/use-page-data.test.ts +0 -298
  313. package/src/__tests__/runtime/use-route.test.ts +0 -156
  314. package/src/__tests__/runtime/use-runtime-config.test.ts +0 -182
  315. package/src/__tests__/runtime/use-seo-meta.test.ts +0 -109
  316. package/src/__tests__/runtime/use-session.test.ts +0 -320
  317. package/src/__tests__/runtime/use-state.test.ts +0 -340
  318. package/src/__tests__/types/config.test.ts +0 -18
  319. package/src/cli/adapters/cloudflare.ts +0 -335
  320. package/src/cli/adapters/netlify.ts +0 -326
  321. package/src/cli/adapters/vercel.ts +0 -311
  322. package/src/cli/commands/adapt.ts +0 -84
  323. package/src/cli/commands/build.ts +0 -165
  324. package/src/cli/commands/dev.ts +0 -125
  325. package/src/cli/commands/generate.ts +0 -91
  326. package/src/cli/commands/preview-isr.ts +0 -172
  327. package/src/cli/commands/preview.ts +0 -523
  328. package/src/cli/create/index.ts +0 -275
  329. package/src/cli/create/templates/shared/.gitignore.tpl +0 -25
  330. package/src/cli/create/templates/shared/app/layouts/default.ts.tpl +0 -15
  331. package/src/cli/create/templates/shared/app/pages/index.ts.tpl +0 -8
  332. package/src/cli/create/templates/shared/index.html.tpl +0 -12
  333. package/src/cli/create/templates/shared/tsconfig.json.tpl +0 -6
  334. package/src/cli/create/templates/spa/cer.config.ts.tpl +0 -6
  335. package/src/cli/create/templates/spa/package.json.tpl +0 -18
  336. package/src/cli/create/templates/ssg/cer.config.ts.tpl +0 -10
  337. package/src/cli/create/templates/ssg/package.json.tpl +0 -19
  338. package/src/cli/create/templates/ssr/cer.config.ts.tpl +0 -6
  339. package/src/cli/create/templates/ssr/package.json.tpl +0 -18
  340. package/src/cli/index.ts +0 -22
  341. package/src/index.ts +0 -20
  342. package/src/plugin/build-ssg.ts +0 -403
  343. package/src/plugin/build-ssr.ts +0 -134
  344. package/src/plugin/content/emitter.ts +0 -50
  345. package/src/plugin/content/index.ts +0 -233
  346. package/src/plugin/content/parser.ts +0 -259
  347. package/src/plugin/content/path-utils.ts +0 -55
  348. package/src/plugin/content/scanner.ts +0 -26
  349. package/src/plugin/content/search.ts +0 -28
  350. package/src/plugin/dev-server.ts +0 -379
  351. package/src/plugin/dts-generator.ts +0 -273
  352. package/src/plugin/generated-dir.ts +0 -121
  353. package/src/plugin/html-post-process.ts +0 -96
  354. package/src/plugin/index.ts +0 -584
  355. package/src/plugin/path-utils.ts +0 -186
  356. package/src/plugin/scanner.ts +0 -65
  357. package/src/plugin/transforms/auto-import.ts +0 -206
  358. package/src/plugin/transforms/head-inject.ts +0 -161
  359. package/src/plugin/virtual/composables.ts +0 -28
  360. package/src/plugin/virtual/content-components.ts +0 -83
  361. package/src/plugin/virtual/error.ts +0 -34
  362. package/src/plugin/virtual/layouts.ts +0 -41
  363. package/src/plugin/virtual/loading.ts +0 -32
  364. package/src/plugin/virtual/middleware.ts +0 -45
  365. package/src/plugin/virtual/plugins.ts +0 -46
  366. package/src/plugin/virtual/routes.ts +0 -514
  367. package/src/plugin/virtual/server-api.ts +0 -85
  368. package/src/plugin/virtual/server-middleware.ts +0 -44
  369. package/src/runtime/app-template.ts +0 -377
  370. package/src/runtime/composables/define-middleware.ts +0 -17
  371. package/src/runtime/composables/define-server-middleware.ts +0 -18
  372. package/src/runtime/composables/index.ts +0 -27
  373. package/src/runtime/composables/use-auth.ts +0 -125
  374. package/src/runtime/composables/use-content-search.ts +0 -205
  375. package/src/runtime/composables/use-content.ts +0 -146
  376. package/src/runtime/composables/use-cookie.ts +0 -135
  377. package/src/runtime/composables/use-fetch.ts +0 -438
  378. package/src/runtime/composables/use-head.ts +0 -234
  379. package/src/runtime/composables/use-inject.ts +0 -49
  380. package/src/runtime/composables/use-locale.ts +0 -109
  381. package/src/runtime/composables/use-navigate.ts +0 -58
  382. package/src/runtime/composables/use-page-data.ts +0 -93
  383. package/src/runtime/composables/use-route.ts +0 -71
  384. package/src/runtime/composables/use-runtime-config.ts +0 -123
  385. package/src/runtime/composables/use-seo-meta.ts +0 -80
  386. package/src/runtime/composables/use-session.ts +0 -186
  387. package/src/runtime/composables/use-state.ts +0 -92
  388. package/src/runtime/content/client.ts +0 -168
  389. package/src/runtime/entry-client-template.ts +0 -39
  390. package/src/runtime/entry-server-template.ts +0 -493
  391. package/src/runtime/isr-handler.ts +0 -184
  392. package/src/runtime/oauth/handler.ts +0 -407
  393. package/src/runtime/oauth/index.ts +0 -49
  394. package/src/runtime/oauth/providers.ts +0 -38
  395. package/src/types/api.ts +0 -40
  396. package/src/types/config.ts +0 -379
  397. package/src/types/content.ts +0 -66
  398. package/src/types/index.ts +0 -7
  399. package/src/types/middleware.ts +0 -63
  400. package/src/types/page.ts +0 -117
  401. package/src/types/plugin.ts +0 -34
  402. package/tsconfig.build.json +0 -10
  403. package/tsconfig.json +0 -19
  404. package/vitest.config.ts +0 -33
@@ -1,377 +0,0 @@
1
- /**
2
- * Generates the `.cer/app.ts` framework client entry point.
3
- *
4
- * Always written to `.cer/app.ts` on every dev/build so consumers
5
- * automatically receive the latest bootstrap code on plugin update.
6
- * This file is gitignored and should never be edited directly.
7
- */
8
- export function generateAppEntryTemplate(): string {
9
- return `// AUTO-GENERATED by @jasonshimmy/vite-plugin-cer-app — do not edit.
10
- // Regenerated automatically on every dev server start and build.
11
-
12
- import '@jasonshimmy/custom-elements-runtime/css'
13
- import 'virtual:cer-jit-css'
14
- // virtual:cer-jit-init must run before virtual:cer-layouts and virtual:cer-plugins
15
- // so JIT CSS is enabled before those modules upgrade custom elements via
16
- // customElements.define(). Static imports execute depth-first in module order,
17
- // so placing this import here guarantees enableJITCSS() fires first.
18
- import 'virtual:cer-jit-init'
19
- import 'virtual:cer-content-components'
20
- import routes from 'virtual:cer-routes'
21
- import layouts from 'virtual:cer-layouts'
22
- import plugins from 'virtual:cer-plugins'
23
- import { hasLoading, loadingTag } from 'virtual:cer-loading'
24
- import { hasError, errorTag } from 'virtual:cer-error'
25
- import { runtimeConfig } from 'virtual:cer-app-config'
26
- import {
27
- component,
28
- ref,
29
- provide,
30
- setDevMode,
31
- useOnConnected,
32
- useOnDisconnected,
33
- registerBuiltinComponents,
34
- } from '@jasonshimmy/custom-elements-runtime'
35
- import { initRouter } from '@jasonshimmy/custom-elements-runtime/router'
36
- import { initRuntimeConfig } from '@jasonshimmy/vite-plugin-cer-app/composables'
37
-
38
- const _cerProcess = (globalThis).process
39
- const _cerNodeEnv = _cerProcess?.env?.NODE_ENV ?? _cerProcess?.env?.MODE
40
- const _cerRuntimeDev =
41
- typeof _cerNodeEnv === 'string'
42
- ? _cerNodeEnv !== 'production'
43
- : typeof import.meta.env?.DEV === 'boolean'
44
- ? import.meta.env.DEV
45
- : typeof import.meta.env?.PROD === 'boolean'
46
- ? !import.meta.env.PROD
47
- : typeof import.meta.env?.MODE === 'string'
48
- ? import.meta.env.MODE !== 'production'
49
- : false
50
- ;(globalThis).__CE_RUNTIME_DEV__ = _cerRuntimeDev
51
-
52
- registerBuiltinComponents()
53
- setDevMode(_cerRuntimeDev)
54
- initRuntimeConfig(runtimeConfig)
55
-
56
- const router = initRouter({ routes })
57
-
58
- // Expose the router globally so useRoute() and navigateTo() can access it
59
- // from any composable without circular imports.
60
- ;(globalThis).__cerRouter = router
61
-
62
- // ─── Page loader state ───────────────────────────────────────────────────────
63
-
64
- // Holds the pre-loaded page tag and loader-derived attrs for the current route.
65
- // Set by _loadPageForPath before every navigation so cer-layout-view can render
66
- // the page element directly with the correct attributes (enabling useProps()).
67
- let _currentPageTag = null
68
- let _currentPageAttrs = {}
69
- // The pathname this tag was loaded for — used to detect router-internal
70
- // redirects (e.g. middleware returning '/login') so cer-layout-view falls
71
- // back to <router-view> when the current route differs from what was pre-loaded.
72
- let _currentPagePath = null
73
-
74
- function _toLoaderAttrs(data) {
75
- if (data === undefined || data === null || typeof data !== 'object') return {}
76
- return Object.fromEntries(
77
- Object.entries(data).filter(([, v]) => v !== null && v !== undefined && typeof v !== 'object' && typeof v !== 'function')
78
- )
79
- }
80
-
81
- // Pre-loads the page module for \`path\`, calls its loader if present, and
82
- // stores the results so cer-layout-view can pass them as element attributes.
83
- async function _loadPageForPath(path, options = {}) {
84
- try {
85
- const url = new URL(path, 'http://x')
86
- const query = Object.fromEntries(url.searchParams)
87
- const { runLoader = true, initialData } = options ?? {}
88
- const matched = router.matchRoute(url.pathname)
89
- const route = matched?.route
90
- if (!route?.load) {
91
- _currentPageTag = null
92
- _currentPageAttrs = {}
93
- _currentPagePath = url.pathname
94
- return
95
- }
96
- const mod = await route.load()
97
- _currentPageTag = mod.default ?? null
98
- _currentPagePath = url.pathname
99
- const params = matched.params ?? {}
100
- let loaderAttrs = { ...params }
101
- let loaderData = initialData
102
- if (typeof mod.loader === 'function' && runLoader) {
103
- try {
104
- const data = await mod.loader({ params, query })
105
- if (data !== undefined && data !== null) {
106
- loaderData = data
107
- // Make loader data available via usePageData() for this navigation.
108
- ;(globalThis).__CER_DATA__ = data
109
- }
110
- } catch (err) {
111
- // Loader errors are surfaced through currentError so the error boundary
112
- // (app/error.ts) can display a meaningful message — consistent with how
113
- // the server-side handler behaves when a loader throws.
114
- currentError.value = err instanceof Error ? err.message : String(err)
115
- }
116
- }
117
- // For the very first hydration pass in SSR/SSG we already have the server's
118
- // loader payload in globalThis.__CER_DATA__ / window.__CER_DATA__. Reuse it
119
- // instead of re-running the loader on the client, then derive primitive attrs
120
- // from that payload so useProps() stays consistent with usePageData().
121
- loaderAttrs = { ...loaderAttrs, ..._toLoaderAttrs(loaderData) }
122
- _currentPageAttrs = loaderAttrs
123
- } catch {
124
- _currentPageTag = null
125
- _currentPageAttrs = {}
126
- _currentPagePath = null
127
- }
128
- }
129
-
130
- // ─── Navigation state ────────────────────────────────────────────────────────
131
-
132
- const isNavigating = ref(false)
133
- const currentError = ref(null)
134
-
135
- const resetError = () => {
136
- currentError.value = null
137
- void router.replace(router.getCurrent().path)
138
- }
139
- ;(globalThis).resetError = resetError
140
-
141
- const _push = router.push.bind(router)
142
- const _replace = router.replace.bind(router)
143
-
144
- router.push = async (path) => {
145
- isNavigating.value = true
146
- currentError.value = null
147
- try {
148
- // Clear stale loader data from the previous route before loading the new one.
149
- // If the new route has no loader, __CER_DATA__ stays undefined so usePageData()
150
- // correctly returns null instead of leaking the previous page's data.
151
- delete (globalThis).__CER_DATA__
152
- await _loadPageForPath(path)
153
- await _push(path)
154
- } catch (err) {
155
- currentError.value = err instanceof Error ? err.message : String(err)
156
- } finally {
157
- isNavigating.value = false
158
- }
159
- }
160
-
161
- router.replace = async (path) => {
162
- isNavigating.value = true
163
- currentError.value = null
164
- try {
165
- // Clear stale loader data from the previous route before loading the new one.
166
- delete (globalThis).__CER_DATA__
167
- await _loadPageForPath(path)
168
- await _replace(path)
169
- } catch (err) {
170
- currentError.value = err instanceof Error ? err.message : String(err)
171
- } finally {
172
- isNavigating.value = false
173
- }
174
- }
175
-
176
- // ─── Plugins ─────────────────────────────────────────────────────────────────
177
-
178
- // Collect plugin-provided values so cer-layout-view can forward them into
179
- // the component context tree via the real provide() hook (which inject() walks).
180
- // Declared BEFORE component('cer-layout-view') to avoid a temporal dead zone
181
- // ReferenceError: customElements.define() upgrades existing DOM elements
182
- // synchronously, calling the render function immediately.
183
- const _pluginProvides = new Map()
184
- // Expose plugin provides globally so page components can read them synchronously
185
- // regardless of render order.
186
- ;(globalThis).__cerPluginProvides = _pluginProvides
187
-
188
- // ─── <cer-layout-view> ───────────────────────────────────────────────────────
189
-
190
- // True until the initial page module has loaded and _replace has been called.
191
- // While true, cer-layout-view renders a <slot> so the SSR/SSG pre-rendered
192
- // light-DOM content stays visible and no FOUC occurs during the hydration gap.
193
- // Declared here (before component()) because customElements.define() upgrades
194
- // existing DOM elements synchronously, so the render function reads this ref
195
- // on the very first call — before _doHydrate has a chance to run.
196
- const _cerHydrating = ref(true)
197
-
198
- component('cer-layout-view', () => {
199
- // Forward plugin-provided values into the component context so inject() in
200
- // any descendant component can resolve them by walking up the DOM tree.
201
- for (const [key, value] of _pluginProvides) {
202
- provide(key, value)
203
- }
204
-
205
- const current = ref(router.getCurrent())
206
- let unsub
207
- let _sawInitialRouteState = false
208
-
209
- useOnConnected(() => {
210
- // store.subscribe() synchronously pushes the current route once on
211
- // subscription. That initial callback is not a real navigation and must not
212
- // tear down the SSR slot before _doHydrate has pre-loaded the page.
213
- // Subsequent callbacks may be real navigations, but initRouter() also fires
214
- // a queueMicrotask(() => navigate(...)) to run guards on the entry URL.
215
- // That startup microtask resolves during the await route.load() gap —
216
- // before _currentPageTag is set — so we must not drop the slot until the
217
- // page module has actually been loaded (_currentPageTag !== null).
218
- // The explicit _cerHydrating.value = false in _doHydrate() fires after
219
- // _loadPageForPath completes and is the authoritative hydration trigger.
220
- unsub = router.subscribe((s) => {
221
- const _isInitialSubscribePush = !_sawInitialRouteState
222
- _sawInitialRouteState = true
223
- if (_isInitialSubscribePush) {
224
- current.value = s
225
- return
226
- }
227
- if (_cerHydrating.value && _currentPageTag !== null) _cerHydrating.value = false
228
- current.value = s
229
- })
230
- })
231
- useOnDisconnected(() => { unsub?.(); unsub = undefined })
232
-
233
- // While the initial page module is being loaded, keep the SSR/SSG
234
- // pre-rendered light-DOM content visible by forwarding it through a <slot>.
235
- // This prevents the intermediate blank state that occurs when the shadow DOM
236
- // renders layout-default with an empty <router-view> before the page chunk
237
- // has arrived. In SPA mode (no pre-rendered content) the slot is empty, which
238
- // is no worse than the current router-view behaviour.
239
- if (_cerHydrating.value) {
240
- return { tag: 'slot', props: {}, children: [] }
241
- }
242
-
243
- const matched = router.matchRoute(current.value.path)
244
- const routeMeta = matched?.route?.meta
245
-
246
- if (currentError.value !== null) {
247
- const routeErrorTag = routeMeta?.errorTag ?? null
248
- const effectiveErrorTag = routeErrorTag ?? (hasError ? errorTag : null)
249
- if (effectiveErrorTag) {
250
- return { tag: effectiveErrorTag, props: { attrs: { error: String(currentError.value) } }, children: [] }
251
- }
252
- return { tag: 'div', props: { attrs: { style: 'padding:2rem;font-family:monospace' } }, children: String(currentError.value) }
253
- }
254
-
255
- if (isNavigating.value && hasLoading && loadingTag) {
256
- return { tag: loadingTag, props: {}, children: [] }
257
- }
258
-
259
- // Render the page component directly when a pre-loaded tag is available AND
260
- // the loaded path matches the current route. The path guard detects
261
- // router-internal redirects (e.g. middleware returning '/login'): when the
262
- // router redirects to a different path, _currentPagePath won't match
263
- // current.value.path and we fall back to <router-view> so the correct page
264
- // is rendered without stale pre-loaded state.
265
- const _useDirectRender = _currentPageTag && _currentPagePath === current.value.path
266
- const pageVnode = _useDirectRender
267
- ? { tag: _currentPageTag, props: { attrs: _currentPageAttrs }, children: [] }
268
- : { tag: 'router-view', props: {}, children: [] }
269
-
270
- // Support nested layout chains: meta.layoutChain = ['default', 'admin']
271
- // renders <layout-default><layout-admin><page/></layout-admin></layout-default>
272
- const chain = routeMeta?.layoutChain
273
- ? routeMeta.layoutChain
274
- : [routeMeta?.layout ?? 'default']
275
-
276
- // Build nested vnodes from innermost to outermost.
277
- let vnode = pageVnode
278
- for (let i = chain.length - 1; i >= 0; i--) {
279
- const tag = layouts[chain[i]]
280
- if (tag) vnode = { tag, props: {}, children: [vnode] }
281
- }
282
- return vnode
283
- })
284
-
285
- for (const plugin of plugins) {
286
- if (plugin && typeof plugin.setup === 'function') {
287
- await plugin.setup({
288
- router,
289
- provide: (key, value) => { _pluginProvides.set(key, value) },
290
- config: {},
291
- })
292
- }
293
- }
294
-
295
- // ─── Pre-load initial route + hydration strategy ─────────────────────────────
296
- // Download the current page's route chunk AFTER plugins run so that
297
- // cer-layout-view's first render (which calls provide()) completes before
298
- // page components are defined and their renders are scheduled. This ensures
299
- // inject() in child components can find values stored by provide().
300
- //
301
- // meta.hydrate controls WHEN the initial page activates:
302
- // 'load' — immediately (default)
303
- // 'idle' — deferred until requestIdleCallback (browser idle)
304
- // 'visible' — deferred until cer-layout-view enters the viewport
305
- // 'none' — never: SSR HTML stays as-is, no JS activation
306
-
307
- if (typeof window !== 'undefined') {
308
- const _initMatch = router.matchRoute(window.location.pathname)
309
- const _hydrateStrategy = _initMatch?.route?.meta?.hydrate ?? 'load'
310
-
311
- if (_hydrateStrategy === 'none') {
312
- // Static HTML only — leave SSR output untouched, clean up data immediately.
313
- delete (globalThis).__CER_DATA__
314
- } else {
315
- const _doHydrate = async () => {
316
- const _initPath = window.location.pathname + window.location.search + window.location.hash
317
- const _hasInitialLoaderData = Object.prototype.hasOwnProperty.call(globalThis, '__CER_DATA__')
318
- // Pre-load the page module and run the loader so cer-layout-view renders
319
- // the page tag directly with loader attrs (enables useProps() on hydration).
320
- await _loadPageForPath(
321
- _initPath,
322
- _hasInitialLoaderData
323
- ? { runLoader: false, initialData: (globalThis).__CER_DATA__ }
324
- : undefined,
325
- )
326
- // Only activate the initial route if the URL hasn't changed while we were
327
- // loading the module asynchronously (e.g. a test or plugin navigated away
328
- // before _doHydrate finished). Calling _replace with a stale path would
329
- // override any navigation that happened during the async gap.
330
- const _currentPath = window.location.pathname + window.location.search + window.location.hash
331
- // Drop the hydration slot cover unconditionally: the page module is loaded
332
- // and cer-layout-view must switch from the <slot> to live rendering whether
333
- // or not a redirect changed the path during loading.
334
- _cerHydrating.value = false
335
- if (_currentPath === _initPath) {
336
- // Use the original (unwrapped) replace so isNavigating stays false during
337
- // the initial paint — the loading component must not flash over pre-rendered content.
338
- await _replace(_initPath)
339
- }
340
- // The client entry marks the already-rendered entry URL so generated
341
- // route middleware can skip the browser router's startup replay once.
342
- // Clear any leftover flag after the first hydrated route commits so later
343
- // navigations always run middleware normally.
344
- queueMicrotask(() => { delete (globalThis).__CER_HYDRATION_ENTRY__ })
345
- // Keep the initial route's SSR data available after hydration.
346
- // Browser engines do not all upgrade and re-render custom elements with
347
- // identical timing, so deleting __CER_DATA__ here can cause a later
348
- // hydration render to fall back to the client-only path. The next real
349
- // router.push/router.replace clears __CER_DATA__ before loading the new
350
- // route, so stale page data still does not leak across navigations.
351
- }
352
-
353
- if (_hydrateStrategy === 'idle') {
354
- // Defer until the browser has finished higher-priority work.
355
- if (typeof requestIdleCallback !== 'undefined') {
356
- requestIdleCallback(() => { void _doHydrate() })
357
- } else {
358
- // Safari / older environments fallback.
359
- setTimeout(() => { void _doHydrate() }, 1)
360
- }
361
- } else if (_hydrateStrategy === 'visible') {
362
- // Defer until cer-layout-view (or body as fallback) enters the viewport.
363
- const _el = document.querySelector('cer-layout-view') ?? document.body
364
- const _io = new IntersectionObserver(([entry]) => {
365
- if (entry.isIntersecting) { _io.disconnect(); void _doHydrate() }
366
- })
367
- _io.observe(_el)
368
- } else {
369
- // 'load' — hydrate immediately (default behaviour).
370
- await _doHydrate()
371
- }
372
- }
373
- }
374
-
375
- export { router }
376
- `
377
- }
@@ -1,17 +0,0 @@
1
- import type { MiddlewareFn } from '../../types/middleware.js'
2
-
3
- /**
4
- * Identity helper that gives TypeScript the correct `MiddlewareFn` type
5
- * without any runtime cost.
6
- *
7
- * @example
8
- * // app/middleware/auth.ts
9
- * export default defineMiddleware(async (to, from) => {
10
- * const isLoggedIn = checkSession()
11
- * if (!isLoggedIn) return '/login' // redirect
12
- * return true // allow
13
- * })
14
- */
15
- export function defineMiddleware(fn: MiddlewareFn): MiddlewareFn {
16
- return fn
17
- }
@@ -1,18 +0,0 @@
1
- import type { ServerMiddleware } from '../../types/middleware.js'
2
-
3
- /**
4
- * Identity helper that gives `ServerMiddleware` functions full TypeScript
5
- * types without any runtime cost. Usage:
6
- *
7
- * ```ts
8
- * // server/middleware/auth.ts
9
- * export default defineServerMiddleware(async (req, res, next) => {
10
- * const session = await useSession().get()
11
- * if (!session) { res.statusCode = 401; res.end('Unauthorized'); return }
12
- * next()
13
- * })
14
- * ```
15
- */
16
- export function defineServerMiddleware(fn: ServerMiddleware): ServerMiddleware {
17
- return fn
18
- }
@@ -1,27 +0,0 @@
1
- export { useHead, beginHeadCollection, endHeadCollection, serializeHeadTags } from './use-head.js'
2
- export type { HeadInput } from './use-head.js'
3
- export { usePageData } from './use-page-data.js'
4
- export { useInject } from './use-inject.js'
5
- export { useRuntimeConfig, initRuntimeConfig, resolvePrivateConfig } from './use-runtime-config.js'
6
- export type { RuntimeConfigResult, RuntimeConfigPublic, RuntimeConfigPrivate } from './use-runtime-config.js'
7
- export { defineMiddleware } from './define-middleware.js'
8
- export { defineServerMiddleware } from './define-server-middleware.js'
9
- export { useSeoMeta } from './use-seo-meta.js'
10
- export type { SeoMetaInput } from './use-seo-meta.js'
11
- export { useCookie } from './use-cookie.js'
12
- export type { CookieOptions, CookieRef } from './use-cookie.js'
13
- export { useSession } from './use-session.js'
14
- export type { SessionOptions, SessionComposable } from './use-session.js'
15
- export { useAuth } from './use-auth.js'
16
- export type { AuthUser, AuthComposable } from './use-auth.js'
17
- export { useFetch } from './use-fetch.js'
18
- export type { UseFetchOptions, UseFetchReturn, UseFetchResult, UseFetchReactiveReturn } from './use-fetch.js'
19
- export { useRoute } from './use-route.js'
20
- export type { RouteInfo } from './use-route.js'
21
- export { navigateTo } from './use-navigate.js'
22
- export { useState } from './use-state.js'
23
- export { useLocale } from './use-locale.js'
24
- export type { LocaleComposable } from './use-locale.js'
25
- export { queryContent, QueryBuilder } from './use-content.js'
26
- export { useContentSearch } from './use-content-search.js'
27
- export type { UseContentSearchReturn } from './use-content-search.js'
@@ -1,125 +0,0 @@
1
- import { useSession } from './use-session.js'
2
-
3
- // ─── Types ────────────────────────────────────────────────────────────────────
4
-
5
- /**
6
- * Normalised user profile stored in the auth session after a successful OAuth login.
7
- * The `provider`, `id`, and at least one of `name`/`email` fields are populated by the built-in
8
- * provider normalisers. Additional fields from `mapUser()` are merged in.
9
- */
10
- export interface AuthUser {
11
- provider: string
12
- id: string
13
- name?: string
14
- email?: string
15
- avatar?: string
16
- [key: string]: unknown
17
- }
18
-
19
- /** Return value of `useAuth()`. Exposes the current user and helpers for login/logout. */
20
- export interface AuthComposable {
21
- /** The currently authenticated user, or `null` if not logged in. */
22
- readonly user: AuthUser | null
23
- /** `true` when a user is authenticated. */
24
- readonly loggedIn: boolean
25
- /**
26
- * Initiates an OAuth login flow by redirecting to `/api/auth/:provider`.
27
- * Only meaningful on the client — calling this on the server is a no-op.
28
- */
29
- login(provider: string): void
30
- /**
31
- * Logs out the current user.
32
- *
33
- * - **Server context** (inside a loader / middleware): clears the auth
34
- * session cookie immediately via `useSession().clear()`.
35
- * - **Client context**: redirects to `/api/auth/logout` which clears the
36
- * cookie server-side and redirects to `redirectAfterLogout`.
37
- */
38
- logout(): Promise<void>
39
- }
40
-
41
- // ─── Composable ───────────────────────────────────────────────────────────────
42
-
43
- /**
44
- * Returns the currently authenticated user and helpers for login/logout.
45
- *
46
- * Works isomorphically:
47
- * - **Server render pass** — reads from `globalThis.__CER_AUTH_STORE__`
48
- * (AsyncLocalStorage populated by the entry-server handler before rendering).
49
- * - **Client hydration** — reads from `globalThis.__CER_AUTH_USER__` (injected
50
- * as `window.__CER_AUTH_USER__` by the entry-server and captured by the
51
- * client entry before the app boots).
52
- *
53
- * @param sessionKey - Session cookie name; must match `auth.sessionKey` in
54
- * `cer.config.ts` (defaults to `'auth'`).
55
- *
56
- * @example
57
- * ```ts
58
- * // app/pages/profile.ts
59
- * component('page-profile', () => {
60
- * const { user, loggedIn, logout } = useAuth()
61
- *
62
- * if (!loggedIn) {
63
- * // Redirect to login
64
- * useHead({ title: 'Login required' })
65
- * }
66
- * })
67
- * ```
68
- *
69
- * @example
70
- * ```ts
71
- * // app/middleware/require-auth.ts
72
- * export default defineMiddleware((to) => {
73
- * const { loggedIn } = useAuth()
74
- * if (!loggedIn) return '/login'
75
- * return true
76
- * })
77
- * ```
78
- */
79
- export function useAuth(sessionKey = 'auth'): AuthComposable {
80
- const g = globalThis as Record<string, unknown>
81
-
82
- // Server: read from the per-request AsyncLocalStorage context.
83
- // __CER_AUTH_STORE__ is only present in Node.js (tree-shaken on client).
84
- const authStore = g['__CER_AUTH_STORE__'] as { getStore(): unknown } | undefined
85
- let user: AuthUser | null = null
86
-
87
- if (authStore) {
88
- user = (authStore.getStore() as AuthUser | null) ?? null
89
- } else {
90
- // Client: read from the global hydrated by the client entry.
91
- user = (g['__CER_AUTH_USER__'] as AuthUser | null) ?? null
92
- }
93
-
94
- return {
95
- get user() {
96
- return user
97
- },
98
-
99
- get loggedIn() {
100
- return user !== null
101
- },
102
-
103
- login(provider: string) {
104
- if (typeof window !== 'undefined') {
105
- window.location.href = `/api/auth/${provider}`
106
- }
107
- },
108
-
109
- async logout() {
110
- if (typeof window !== 'undefined') {
111
- // Client: let the server clear the cookie via the logout route.
112
- window.location.href = '/api/auth/logout'
113
- } else {
114
- // Server context: clear the session cookie directly.
115
- const session = useSession({ name: sessionKey })
116
- await session.clear()
117
- // Also clear the local user reference so the current render sees null.
118
- user = null
119
- // Clear the ALS store value so concurrent requests are unaffected.
120
- const store = (g['__CER_AUTH_STORE__'] as { enterWith(v: unknown): void } | undefined)
121
- if (store) store.enterWith(null)
122
- }
123
- },
124
- }
125
- }