@jasonshimmy/vite-plugin-cer-app 0.23.2 → 0.24.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 (392) hide show
  1. package/dist/cli/checks/lighthouse.d.ts +26 -0
  2. package/dist/cli/checks/lighthouse.d.ts.map +1 -0
  3. package/dist/cli/checks/lighthouse.js +121 -0
  4. package/dist/cli/checks/lighthouse.js.map +1 -0
  5. package/dist/cli/checks/links.d.ts +12 -0
  6. package/dist/cli/checks/links.d.ts.map +1 -0
  7. package/dist/cli/checks/links.js +89 -0
  8. package/dist/cli/checks/links.js.map +1 -0
  9. package/dist/cli/checks/performance.d.ts +17 -0
  10. package/dist/cli/checks/performance.d.ts.map +1 -0
  11. package/dist/cli/checks/performance.js +61 -0
  12. package/dist/cli/checks/performance.js.map +1 -0
  13. package/dist/cli/commands/check.d.ts +3 -0
  14. package/dist/cli/commands/check.d.ts.map +1 -0
  15. package/dist/cli/commands/check.js +85 -0
  16. package/dist/cli/commands/check.js.map +1 -0
  17. package/dist/cli/create/index.d.ts +11 -1
  18. package/dist/cli/create/index.d.ts.map +1 -1
  19. package/dist/cli/create/index.js +135 -65
  20. package/dist/cli/create/index.js.map +1 -1
  21. package/dist/cli/create/templates/spa/package.json.tpl +5 -3
  22. package/dist/cli/create/templates/ssg/cer.config.ts.tpl +2 -0
  23. package/dist/cli/create/templates/ssg/package.json.tpl +5 -3
  24. package/dist/cli/create/templates/ssr/package.json.tpl +5 -3
  25. package/dist/cli/index.js +2 -0
  26. package/dist/cli/index.js.map +1 -1
  27. package/dist/index.d.ts +3 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/plugin/build-ssg.d.ts +22 -6
  30. package/dist/plugin/build-ssg.d.ts.map +1 -1
  31. package/dist/plugin/build-ssg.js +227 -17
  32. package/dist/plugin/build-ssg.js.map +1 -1
  33. package/dist/plugin/content/parser.d.ts.map +1 -1
  34. package/dist/plugin/content/parser.js +54 -10
  35. package/dist/plugin/content/parser.js.map +1 -1
  36. package/dist/plugin/dev-server.d.ts +4 -1
  37. package/dist/plugin/dev-server.d.ts.map +1 -1
  38. package/dist/plugin/dev-server.js.map +1 -1
  39. package/dist/plugin/dts-generator.d.ts.map +1 -1
  40. package/dist/plugin/dts-generator.js +50 -8
  41. package/dist/plugin/dts-generator.js.map +1 -1
  42. package/dist/plugin/generated-dir.d.ts +2 -0
  43. package/dist/plugin/generated-dir.d.ts.map +1 -1
  44. package/dist/plugin/generated-dir.js +17 -6
  45. package/dist/plugin/generated-dir.js.map +1 -1
  46. package/dist/plugin/html-post-process.d.ts +6 -0
  47. package/dist/plugin/html-post-process.d.ts.map +1 -1
  48. package/dist/plugin/html-post-process.js +24 -0
  49. package/dist/plugin/html-post-process.js.map +1 -1
  50. package/dist/plugin/index.d.ts.map +1 -1
  51. package/dist/plugin/index.js +60 -13
  52. package/dist/plugin/index.js.map +1 -1
  53. package/dist/plugin/transforms/auto-import.d.ts.map +1 -1
  54. package/dist/plugin/transforms/auto-import.js +7 -0
  55. package/dist/plugin/transforms/auto-import.js.map +1 -1
  56. package/dist/plugin/transforms/prune-runtime-css.d.ts +8 -0
  57. package/dist/plugin/transforms/prune-runtime-css.d.ts.map +1 -0
  58. package/dist/plugin/transforms/prune-runtime-css.js +40 -0
  59. package/dist/plugin/transforms/prune-runtime-css.js.map +1 -0
  60. package/dist/runtime/app-template.d.ts +1 -1
  61. package/dist/runtime/app-template.d.ts.map +1 -1
  62. package/dist/runtime/app-template.js +206 -36
  63. package/dist/runtime/app-template.js.map +1 -1
  64. package/dist/runtime/composables/index.d.ts +4 -0
  65. package/dist/runtime/composables/index.d.ts.map +1 -1
  66. package/dist/runtime/composables/index.js +2 -0
  67. package/dist/runtime/composables/index.js.map +1 -1
  68. package/dist/runtime/composables/use-active-headings.d.ts +24 -0
  69. package/dist/runtime/composables/use-active-headings.d.ts.map +1 -0
  70. package/dist/runtime/composables/use-active-headings.js +112 -0
  71. package/dist/runtime/composables/use-active-headings.js.map +1 -0
  72. package/dist/runtime/composables/use-content-page.d.ts +45 -0
  73. package/dist/runtime/composables/use-content-page.d.ts.map +1 -0
  74. package/dist/runtime/composables/use-content-page.js +179 -0
  75. package/dist/runtime/composables/use-content-page.js.map +1 -0
  76. package/dist/runtime/composables/use-content-search.d.ts.map +1 -1
  77. package/dist/runtime/composables/use-content-search.js +1 -5
  78. package/dist/runtime/composables/use-content-search.js.map +1 -1
  79. package/dist/runtime/composables/use-fetch.d.ts +2 -1
  80. package/dist/runtime/composables/use-fetch.d.ts.map +1 -1
  81. package/dist/runtime/composables/use-fetch.js +112 -43
  82. package/dist/runtime/composables/use-fetch.js.map +1 -1
  83. package/dist/runtime/composables/use-route.d.ts.map +1 -1
  84. package/dist/runtime/composables/use-route.js +39 -11
  85. package/dist/runtime/composables/use-route.js.map +1 -1
  86. package/dist/runtime/content/client.js +2 -2
  87. package/dist/runtime/content/client.js.map +1 -1
  88. package/dist/runtime/entry-server-template.d.ts +16 -1
  89. package/dist/runtime/entry-server-template.d.ts.map +1 -1
  90. package/dist/runtime/entry-server-template.js +120 -13
  91. package/dist/runtime/entry-server-template.js.map +1 -1
  92. package/dist/types/config.d.ts +45 -4
  93. package/dist/types/config.d.ts.map +1 -1
  94. package/dist/types/config.js.map +1 -1
  95. package/dist/types/middleware.d.ts +2 -2
  96. package/dist/types/middleware.d.ts.map +1 -1
  97. package/package.json +22 -19
  98. package/.github/copilot-instructions.md +0 -169
  99. package/.github/workflows/publish.yml +0 -257
  100. package/.nvmrc +0 -1
  101. package/CHANGELOG.md +0 -236
  102. package/CLAUDE.md +0 -113
  103. package/commits.txt +0 -1
  104. package/cypress.config.ts +0 -16
  105. package/docs/authentication.md +0 -285
  106. package/docs/cli.md +0 -324
  107. package/docs/components.md +0 -237
  108. package/docs/composables.md +0 -907
  109. package/docs/configuration.md +0 -696
  110. package/docs/content.md +0 -570
  111. package/docs/data-loading.md +0 -336
  112. package/docs/getting-started.md +0 -237
  113. package/docs/head-management.md +0 -212
  114. package/docs/i18n.md +0 -159
  115. package/docs/layouts.md +0 -224
  116. package/docs/middleware.md +0 -234
  117. package/docs/plugins.md +0 -170
  118. package/docs/rendering-modes.md +0 -377
  119. package/docs/routing.md +0 -485
  120. package/docs/server-api.md +0 -253
  121. package/docs/testing.md +0 -455
  122. package/docs/use-fetch.md +0 -183
  123. package/e2e/cypress/e2e/api.cy.ts +0 -123
  124. package/e2e/cypress/e2e/async-component.cy.ts +0 -27
  125. package/e2e/cypress/e2e/component-splitting.cy.ts +0 -50
  126. package/e2e/cypress/e2e/content.cy.ts +0 -425
  127. package/e2e/cypress/e2e/cookie.cy.ts +0 -85
  128. package/e2e/cypress/e2e/data.cy.ts +0 -158
  129. package/e2e/cypress/e2e/error-boundary.cy.ts +0 -88
  130. package/e2e/cypress/e2e/fouc.cy.ts +0 -254
  131. package/e2e/cypress/e2e/group-meta.cy.ts +0 -36
  132. package/e2e/cypress/e2e/head.cy.ts +0 -89
  133. package/e2e/cypress/e2e/i18n.cy.ts +0 -79
  134. package/e2e/cypress/e2e/interactive.cy.ts +0 -137
  135. package/e2e/cypress/e2e/isr-nested-runtime.cy.ts +0 -102
  136. package/e2e/cypress/e2e/jit-css-custom-colors.cy.ts +0 -43
  137. package/e2e/cypress/e2e/middleware-chain.cy.ts +0 -89
  138. package/e2e/cypress/e2e/middleware-status.cy.ts +0 -30
  139. package/e2e/cypress/e2e/middleware.cy.ts +0 -45
  140. package/e2e/cypress/e2e/navigate.cy.ts +0 -39
  141. package/e2e/cypress/e2e/observability.cy.ts +0 -77
  142. package/e2e/cypress/e2e/per-route-error.cy.ts +0 -42
  143. package/e2e/cypress/e2e/per-route-render.cy.ts +0 -70
  144. package/e2e/cypress/e2e/preview-hardening.cy.ts +0 -88
  145. package/e2e/cypress/e2e/routes.cy.ts +0 -128
  146. package/e2e/cypress/e2e/seo-meta.cy.ts +0 -108
  147. package/e2e/cypress/e2e/server-middleware.cy.ts +0 -46
  148. package/e2e/cypress/e2e/session-rotation.cy.ts +0 -25
  149. package/e2e/cypress/e2e/session.cy.ts +0 -73
  150. package/e2e/cypress/e2e/ssr-render-error.cy.ts +0 -68
  151. package/e2e/cypress/e2e/synthetic-404.cy.ts +0 -45
  152. package/e2e/cypress/e2e/use-page-data.cy.ts +0 -122
  153. package/e2e/cypress/e2e/use-state.cy.ts +0 -138
  154. package/e2e/cypress/support/commands.ts +0 -120
  155. package/e2e/cypress/support/e2e.ts +0 -10
  156. package/e2e/cypress/tsconfig.json +0 -14
  157. package/e2e/kitchen-sink/app/components/ks-badge.ts +0 -8
  158. package/e2e/kitchen-sink/app/composables/useKsCounter.ts +0 -9
  159. package/e2e/kitchen-sink/app/error.ts +0 -18
  160. package/e2e/kitchen-sink/app/layouts/admin.ts +0 -13
  161. package/e2e/kitchen-sink/app/layouts/default.ts +0 -25
  162. package/e2e/kitchen-sink/app/layouts/group.ts +0 -9
  163. package/e2e/kitchen-sink/app/layouts/minimal.ts +0 -7
  164. package/e2e/kitchen-sink/app/loading.ts +0 -9
  165. package/e2e/kitchen-sink/app/middleware/auth.ts +0 -9
  166. package/e2e/kitchen-sink/app/middleware/custom-log.ts +0 -4
  167. package/e2e/kitchen-sink/app/middleware/group-auth.ts +0 -7
  168. package/e2e/kitchen-sink/app/middleware/logger.ts +0 -14
  169. package/e2e/kitchen-sink/app/pages/(auth)/login.ts +0 -13
  170. package/e2e/kitchen-sink/app/pages/(auth)/protected.ts +0 -16
  171. package/e2e/kitchen-sink/app/pages/404.ts +0 -9
  172. package/e2e/kitchen-sink/app/pages/about.ts +0 -17
  173. package/e2e/kitchen-sink/app/pages/admin/_layout.ts +0 -1
  174. package/e2e/kitchen-sink/app/pages/admin/dashboard.ts +0 -11
  175. package/e2e/kitchen-sink/app/pages/async-component-error-test.ts +0 -18
  176. package/e2e/kitchen-sink/app/pages/async-component-test.ts +0 -18
  177. package/e2e/kitchen-sink/app/pages/blog/[slug].ts +0 -59
  178. package/e2e/kitchen-sink/app/pages/blog/index.ts +0 -51
  179. package/e2e/kitchen-sink/app/pages/content-blog.ts +0 -37
  180. package/e2e/kitchen-sink/app/pages/content-doc.ts +0 -42
  181. package/e2e/kitchen-sink/app/pages/content-fallback.ts +0 -36
  182. package/e2e/kitchen-sink/app/pages/content-guides.ts +0 -35
  183. package/e2e/kitchen-sink/app/pages/content-index.ts +0 -39
  184. package/e2e/kitchen-sink/app/pages/content-search.ts +0 -36
  185. package/e2e/kitchen-sink/app/pages/cookie-test.ts +0 -22
  186. package/e2e/kitchen-sink/app/pages/counter.ts +0 -17
  187. package/e2e/kitchen-sink/app/pages/group-auth-blocked.ts +0 -9
  188. package/e2e/kitchen-sink/app/pages/group-meta-test/_layout.ts +0 -6
  189. package/e2e/kitchen-sink/app/pages/group-meta-test/custom-mw.ts +0 -14
  190. package/e2e/kitchen-sink/app/pages/group-meta-test/page.ts +0 -9
  191. package/e2e/kitchen-sink/app/pages/group-meta-test/protected.ts +0 -8
  192. package/e2e/kitchen-sink/app/pages/head.ts +0 -20
  193. package/e2e/kitchen-sink/app/pages/i18n-test.ts +0 -14
  194. package/e2e/kitchen-sink/app/pages/index.ts +0 -27
  195. package/e2e/kitchen-sink/app/pages/isr-test.ts +0 -17
  196. package/e2e/kitchen-sink/app/pages/items/[id].ts +0 -20
  197. package/e2e/kitchen-sink/app/pages/jit-css-custom-colors-test.ts +0 -14
  198. package/e2e/kitchen-sink/app/pages/loader-error-test.ts +0 -13
  199. package/e2e/kitchen-sink/app/pages/loader-props-test.ts +0 -19
  200. package/e2e/kitchen-sink/app/pages/loader-response-error-test.ts +0 -13
  201. package/e2e/kitchen-sink/app/pages/middleware-chain-test.ts +0 -12
  202. package/e2e/kitchen-sink/app/pages/middleware-status-test.ts +0 -9
  203. package/e2e/kitchen-sink/app/pages/navigate-test.ts +0 -16
  204. package/e2e/kitchen-sink/app/pages/observability-test.ts +0 -25
  205. package/e2e/kitchen-sink/app/pages/per-route-error-test.error.ts +0 -13
  206. package/e2e/kitchen-sink/app/pages/per-route-error-test.ts +0 -15
  207. package/e2e/kitchen-sink/app/pages/plugin-dsd-test.ts +0 -10
  208. package/e2e/kitchen-sink/app/pages/render-error-test.ts +0 -12
  209. package/e2e/kitchen-sink/app/pages/render-server-test.ts +0 -12
  210. package/e2e/kitchen-sink/app/pages/render-spa-test.ts +0 -12
  211. package/e2e/kitchen-sink/app/pages/route-info.ts +0 -14
  212. package/e2e/kitchen-sink/app/pages/seo-test.ts +0 -23
  213. package/e2e/kitchen-sink/app/pages/session-rotation-test.ts +0 -25
  214. package/e2e/kitchen-sink/app/pages/use-state-test.ts +0 -37
  215. package/e2e/kitchen-sink/app/plugins/01.setup.ts +0 -7
  216. package/e2e/kitchen-sink/app/plugins/02.ks-plugin-components.ts +0 -27
  217. package/e2e/kitchen-sink/cer-auto-imports.d.ts +0 -51
  218. package/e2e/kitchen-sink/cer-env.d.ts +0 -35
  219. package/e2e/kitchen-sink/cer-tsconfig.json +0 -30
  220. package/e2e/kitchen-sink/cer.config.ts +0 -43
  221. package/e2e/kitchen-sink/content/01.guides/01.index.md +0 -8
  222. package/e2e/kitchen-sink/content/01.guides/02.intro.md +0 -8
  223. package/e2e/kitchen-sink/content/01.guides/10.advanced.md +0 -8
  224. package/e2e/kitchen-sink/content/blog/2026-04-01-hello.md +0 -26
  225. package/e2e/kitchen-sink/content/blog/2026-04-02-draft.md +0 -10
  226. package/e2e/kitchen-sink/content/blog/index.md +0 -8
  227. package/e2e/kitchen-sink/content/blog/no-frontmatter.md +0 -7
  228. package/e2e/kitchen-sink/content/docs/getting-started.md +0 -48
  229. package/e2e/kitchen-sink/content/index.md +0 -16
  230. package/e2e/kitchen-sink/server/api/echo.ts +0 -12
  231. package/e2e/kitchen-sink/server/api/health.ts +0 -3
  232. package/e2e/kitchen-sink/server/api/posts/[slug].ts +0 -11
  233. package/e2e/kitchen-sink/server/api/posts/index.ts +0 -5
  234. package/e2e/kitchen-sink/server/api/session.ts +0 -23
  235. package/e2e/kitchen-sink/server/data/posts.ts +0 -21
  236. package/e2e/kitchen-sink/server/middleware/01-headers.ts +0 -6
  237. package/e2e/kitchen-sink/server/middleware/02-status-test.ts +0 -10
  238. package/e2e/kitchen-sink/tsconfig.json +0 -3
  239. package/e2e/scripts/clean.mjs +0 -14
  240. package/eslint.config.ts +0 -22
  241. package/src/__tests__/cli/adapters/cloudflare-worker.integration.test.ts +0 -132
  242. package/src/__tests__/cli/adapters/cloudflare.test.ts +0 -332
  243. package/src/__tests__/cli/adapters/netlify-bridge.integration.test.ts +0 -139
  244. package/src/__tests__/cli/adapters/netlify.test.ts +0 -286
  245. package/src/__tests__/cli/adapters/vercel-launcher.integration.test.ts +0 -129
  246. package/src/__tests__/cli/adapters/vercel.test.ts +0 -263
  247. package/src/__tests__/cli/preview-hardening.test.ts +0 -265
  248. package/src/__tests__/cli/preview-isr.test.ts +0 -320
  249. package/src/__tests__/index.test.ts +0 -21
  250. package/src/__tests__/plugin/app-template.test.ts +0 -166
  251. package/src/__tests__/plugin/build-ssg-render.test.ts +0 -155
  252. package/src/__tests__/plugin/build-ssg.test.ts +0 -598
  253. package/src/__tests__/plugin/build-ssr.test.ts +0 -192
  254. package/src/__tests__/plugin/cer-app-plugin.test.ts +0 -552
  255. package/src/__tests__/plugin/content/emitter.test.ts +0 -117
  256. package/src/__tests__/plugin/content/loader.test.ts +0 -154
  257. package/src/__tests__/plugin/content/parser.test.ts +0 -381
  258. package/src/__tests__/plugin/content/path-utils.test.ts +0 -69
  259. package/src/__tests__/plugin/content/search.test.ts +0 -119
  260. package/src/__tests__/plugin/dev-server.test.ts +0 -813
  261. package/src/__tests__/plugin/dts-generator.test.ts +0 -394
  262. package/src/__tests__/plugin/entry-server-template.test.ts +0 -385
  263. package/src/__tests__/plugin/generated-dir.test.ts +0 -144
  264. package/src/__tests__/plugin/html-post-process.test.ts +0 -146
  265. package/src/__tests__/plugin/path-utils.test.ts +0 -411
  266. package/src/__tests__/plugin/resolve-config.test.ts +0 -233
  267. package/src/__tests__/plugin/scanner.test.ts +0 -186
  268. package/src/__tests__/plugin/transforms/auto-import.test.ts +0 -510
  269. package/src/__tests__/plugin/transforms/head-inject.test.ts +0 -178
  270. package/src/__tests__/plugin/virtual/composables.test.ts +0 -57
  271. package/src/__tests__/plugin/virtual/content-components.test.ts +0 -66
  272. package/src/__tests__/plugin/virtual/error.test.ts +0 -71
  273. package/src/__tests__/plugin/virtual/layouts.test.ts +0 -70
  274. package/src/__tests__/plugin/virtual/loading.test.ts +0 -72
  275. package/src/__tests__/plugin/virtual/middleware.test.ts +0 -83
  276. package/src/__tests__/plugin/virtual/plugins.test.ts +0 -129
  277. package/src/__tests__/plugin/virtual/routes.test.ts +0 -884
  278. package/src/__tests__/plugin/virtual/server-api.test.ts +0 -164
  279. package/src/__tests__/plugin/virtual/server-middleware.test.ts +0 -102
  280. package/src/__tests__/runtime/app-template.test.ts +0 -208
  281. package/src/__tests__/runtime/define-middleware.test.ts +0 -64
  282. package/src/__tests__/runtime/define-server-middleware.test.ts +0 -54
  283. package/src/__tests__/runtime/entry-client-template.test.ts +0 -61
  284. package/src/__tests__/runtime/isr-handler.test.ts +0 -338
  285. package/src/__tests__/runtime/oauth/handler.test.ts +0 -358
  286. package/src/__tests__/runtime/oauth/providers.test.ts +0 -47
  287. package/src/__tests__/runtime/oauth-index.test.ts +0 -43
  288. package/src/__tests__/runtime/use-auth.test.ts +0 -169
  289. package/src/__tests__/runtime/use-content-search-composable.test.ts +0 -405
  290. package/src/__tests__/runtime/use-content-search.test.ts +0 -163
  291. package/src/__tests__/runtime/use-content.test.ts +0 -226
  292. package/src/__tests__/runtime/use-cookie.test.ts +0 -277
  293. package/src/__tests__/runtime/use-fetch-component.test.ts +0 -214
  294. package/src/__tests__/runtime/use-fetch.test.ts +0 -377
  295. package/src/__tests__/runtime/use-head.test.ts +0 -311
  296. package/src/__tests__/runtime/use-inject-client.test.ts +0 -67
  297. package/src/__tests__/runtime/use-inject.test.ts +0 -66
  298. package/src/__tests__/runtime/use-locale.test.ts +0 -152
  299. package/src/__tests__/runtime/use-navigate.test.ts +0 -121
  300. package/src/__tests__/runtime/use-page-data.test.ts +0 -298
  301. package/src/__tests__/runtime/use-route.test.ts +0 -156
  302. package/src/__tests__/runtime/use-runtime-config.test.ts +0 -182
  303. package/src/__tests__/runtime/use-seo-meta.test.ts +0 -109
  304. package/src/__tests__/runtime/use-session.test.ts +0 -320
  305. package/src/__tests__/runtime/use-state.test.ts +0 -340
  306. package/src/__tests__/types/config.test.ts +0 -18
  307. package/src/cli/adapters/cloudflare.ts +0 -335
  308. package/src/cli/adapters/netlify.ts +0 -326
  309. package/src/cli/adapters/vercel.ts +0 -311
  310. package/src/cli/commands/adapt.ts +0 -84
  311. package/src/cli/commands/build.ts +0 -165
  312. package/src/cli/commands/dev.ts +0 -125
  313. package/src/cli/commands/generate.ts +0 -91
  314. package/src/cli/commands/preview-isr.ts +0 -172
  315. package/src/cli/commands/preview.ts +0 -523
  316. package/src/cli/create/index.ts +0 -275
  317. package/src/cli/create/templates/shared/.gitignore.tpl +0 -25
  318. package/src/cli/create/templates/shared/app/layouts/default.ts.tpl +0 -15
  319. package/src/cli/create/templates/shared/app/pages/index.ts.tpl +0 -8
  320. package/src/cli/create/templates/shared/index.html.tpl +0 -12
  321. package/src/cli/create/templates/shared/tsconfig.json.tpl +0 -6
  322. package/src/cli/create/templates/spa/cer.config.ts.tpl +0 -6
  323. package/src/cli/create/templates/spa/package.json.tpl +0 -18
  324. package/src/cli/create/templates/ssg/cer.config.ts.tpl +0 -10
  325. package/src/cli/create/templates/ssg/package.json.tpl +0 -19
  326. package/src/cli/create/templates/ssr/cer.config.ts.tpl +0 -6
  327. package/src/cli/create/templates/ssr/package.json.tpl +0 -18
  328. package/src/cli/index.ts +0 -22
  329. package/src/index.ts +0 -20
  330. package/src/plugin/build-ssg.ts +0 -403
  331. package/src/plugin/build-ssr.ts +0 -134
  332. package/src/plugin/content/emitter.ts +0 -50
  333. package/src/plugin/content/index.ts +0 -233
  334. package/src/plugin/content/parser.ts +0 -259
  335. package/src/plugin/content/path-utils.ts +0 -55
  336. package/src/plugin/content/scanner.ts +0 -26
  337. package/src/plugin/content/search.ts +0 -28
  338. package/src/plugin/dev-server.ts +0 -379
  339. package/src/plugin/dts-generator.ts +0 -273
  340. package/src/plugin/generated-dir.ts +0 -121
  341. package/src/plugin/html-post-process.ts +0 -96
  342. package/src/plugin/index.ts +0 -584
  343. package/src/plugin/path-utils.ts +0 -186
  344. package/src/plugin/scanner.ts +0 -65
  345. package/src/plugin/transforms/auto-import.ts +0 -206
  346. package/src/plugin/transforms/head-inject.ts +0 -161
  347. package/src/plugin/virtual/composables.ts +0 -28
  348. package/src/plugin/virtual/content-components.ts +0 -83
  349. package/src/plugin/virtual/error.ts +0 -34
  350. package/src/plugin/virtual/layouts.ts +0 -41
  351. package/src/plugin/virtual/loading.ts +0 -32
  352. package/src/plugin/virtual/middleware.ts +0 -45
  353. package/src/plugin/virtual/plugins.ts +0 -46
  354. package/src/plugin/virtual/routes.ts +0 -514
  355. package/src/plugin/virtual/server-api.ts +0 -85
  356. package/src/plugin/virtual/server-middleware.ts +0 -44
  357. package/src/runtime/app-template.ts +0 -377
  358. package/src/runtime/composables/define-middleware.ts +0 -17
  359. package/src/runtime/composables/define-server-middleware.ts +0 -18
  360. package/src/runtime/composables/index.ts +0 -27
  361. package/src/runtime/composables/use-auth.ts +0 -125
  362. package/src/runtime/composables/use-content-search.ts +0 -205
  363. package/src/runtime/composables/use-content.ts +0 -146
  364. package/src/runtime/composables/use-cookie.ts +0 -135
  365. package/src/runtime/composables/use-fetch.ts +0 -438
  366. package/src/runtime/composables/use-head.ts +0 -234
  367. package/src/runtime/composables/use-inject.ts +0 -49
  368. package/src/runtime/composables/use-locale.ts +0 -109
  369. package/src/runtime/composables/use-navigate.ts +0 -58
  370. package/src/runtime/composables/use-page-data.ts +0 -93
  371. package/src/runtime/composables/use-route.ts +0 -71
  372. package/src/runtime/composables/use-runtime-config.ts +0 -123
  373. package/src/runtime/composables/use-seo-meta.ts +0 -80
  374. package/src/runtime/composables/use-session.ts +0 -186
  375. package/src/runtime/composables/use-state.ts +0 -92
  376. package/src/runtime/content/client.ts +0 -168
  377. package/src/runtime/entry-client-template.ts +0 -39
  378. package/src/runtime/entry-server-template.ts +0 -493
  379. package/src/runtime/isr-handler.ts +0 -184
  380. package/src/runtime/oauth/handler.ts +0 -407
  381. package/src/runtime/oauth/index.ts +0 -49
  382. package/src/runtime/oauth/providers.ts +0 -38
  383. package/src/types/api.ts +0 -40
  384. package/src/types/config.ts +0 -379
  385. package/src/types/content.ts +0 -66
  386. package/src/types/index.ts +0 -7
  387. package/src/types/middleware.ts +0 -63
  388. package/src/types/page.ts +0 -117
  389. package/src/types/plugin.ts +0 -34
  390. package/tsconfig.build.json +0 -10
  391. package/tsconfig.json +0 -19
  392. 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
- }