@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,813 +0,0 @@
1
- import { vi, describe, it, expect, beforeEach } from 'vitest'
2
- import { EventEmitter } from 'node:events'
3
- vi.mock('node:fs', () => ({
4
- existsSync: vi.fn().mockReturnValue(false),
5
- readFileSync: vi.fn().mockReturnValue('<html>shell</html>'),
6
- }))
7
-
8
- import { existsSync, readFileSync } from 'node:fs'
9
- import { configureCerDevServer } from '../../plugin/dev-server.js'
10
- import type { ResolvedCerConfig } from '../../plugin/dev-server.js'
11
-
12
- // ─── Helpers ─────────────────────────────────────────────────────────────────
13
-
14
- /**
15
- * Creates a minimal mock IncomingMessage.
16
- * Body events are emitted via setImmediate so they fire after readBody
17
- * registers its 'data'/'end' listeners (which happens inside the async
18
- * middleware after several awaited ssrLoadModule calls).
19
- */
20
- function createReq(opts: {
21
- url?: string
22
- method?: string
23
- headers?: Record<string, string>
24
- body?: string
25
- } = {}) {
26
- const emitter = new EventEmitter()
27
- setImmediate(() => {
28
- if (opts.body !== undefined) emitter.emit('data', Buffer.from(opts.body))
29
- emitter.emit('end')
30
- })
31
- return Object.assign(emitter, {
32
- url: opts.url ?? '/',
33
- method: opts.method ?? 'GET',
34
- headers: opts.headers ?? {},
35
- })
36
- }
37
-
38
- function createRes() {
39
- const res: any = { statusCode: 200, setHeader: vi.fn(), end: vi.fn() }
40
- return res
41
- }
42
-
43
- function makeConfig(overrides: Partial<ResolvedCerConfig> = {}): ResolvedCerConfig {
44
- return {
45
- mode: 'spa',
46
- root: '/project',
47
- srcDir: '/project/app',
48
- pagesDir: '/project/app/pages',
49
- layoutsDir: '/project/app/layouts',
50
- componentsDir: '/project/app/components',
51
- composablesDir: '/project/app/composables',
52
- pluginsDir: '/project/app/plugins',
53
- middlewareDir: '/project/app/middleware',
54
- serverApiDir: '/project/server/api',
55
- serverMiddlewareDir: '/project/server/middleware',
56
- port: 3000,
57
- ssg: { routes: 'auto', concurrency: 2, fallback: false },
58
- router: {},
59
- jitCss: { content: [], extendedColors: false },
60
- autoImports: { components: true, composables: true, directives: true, runtime: true },
61
- ...overrides,
62
- } as ResolvedCerConfig
63
- }
64
-
65
- type MockServer = {
66
- middlewares: { use: ReturnType<typeof vi.fn> }
67
- ssrLoadModule: ReturnType<typeof vi.fn>
68
- ssrFixStacktrace: ReturnType<typeof vi.fn>
69
- transformIndexHtml: ReturnType<typeof vi.fn>
70
- }
71
-
72
- function makeServer(): { server: MockServer; getMiddleware: () => Function } {
73
- const registered: Function[] = []
74
- const server: MockServer = {
75
- middlewares: { use: vi.fn((fn: Function) => registered.push(fn)) },
76
- ssrLoadModule: vi.fn(async (path: string) => {
77
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
78
- return { apiRoutes: [] }
79
- }),
80
- ssrFixStacktrace: vi.fn(),
81
- transformIndexHtml: vi.fn(async (_url: string, html: string) => html),
82
- }
83
- return { server, getMiddleware: () => registered[0] }
84
- }
85
-
86
- beforeEach(() => {
87
- vi.mocked(existsSync).mockReturnValue(false)
88
- vi.mocked(readFileSync).mockReturnValue('<html>shell</html>' as never)
89
- })
90
-
91
- // ─── Registration ─────────────────────────────────────────────────────────────
92
-
93
- describe('configureCerDevServer — registration', () => {
94
- it('registers exactly one middleware via server.middlewares.use', () => {
95
- const { server } = makeServer()
96
- configureCerDevServer(server as any, makeConfig())
97
- expect(server.middlewares.use).toHaveBeenCalledTimes(1)
98
- expect(typeof (server.middlewares.use as any).mock.calls[0][0]).toBe('function')
99
- })
100
- })
101
-
102
- // ─── Non-API pass-through ─────────────────────────────────────────────────────
103
-
104
- describe('configureCerDevServer — non-API pass-through', () => {
105
- it('calls next() for a non-HTML request with no matching routes', async () => {
106
- const { server, getMiddleware } = makeServer()
107
- configureCerDevServer(server as any, makeConfig())
108
- const next = vi.fn()
109
- await getMiddleware()(createReq({ url: '/assets/main.js', headers: { accept: 'application/javascript' } }), createRes(), next)
110
- expect(next).toHaveBeenCalled()
111
- })
112
-
113
- it('calls next() when ssrLoadModule throws (module not ready)', async () => {
114
- const { server, getMiddleware } = makeServer()
115
- server.ssrLoadModule.mockRejectedValue(new Error('not ready'))
116
- configureCerDevServer(server as any, makeConfig())
117
- const next = vi.fn()
118
- await getMiddleware()(createReq({ url: '/test' }), createRes(), next)
119
- expect(next).toHaveBeenCalled()
120
- })
121
- })
122
-
123
- // ─── SPA shell handling ──────────────────────────────────────────────────────
124
-
125
- describe('configureCerDevServer — SPA shell handling', () => {
126
- it('serves the transformed SPA shell for direct HTML navigations in spa mode', async () => {
127
- const { server, getMiddleware } = makeServer()
128
- server.transformIndexHtml.mockResolvedValue('<html>spa shell</html>')
129
- vi.mocked(existsSync).mockReturnValue(true)
130
- vi.mocked(readFileSync).mockReturnValue('<html>source shell</html>' as never)
131
- const res = createRes()
132
-
133
- configureCerDevServer(
134
- server as any,
135
- makeConfig({ mode: 'spa' }),
136
- )
137
-
138
- const next = vi.fn()
139
- await getMiddleware()(
140
- createReq({ url: '/music/effects/pro-co/rat', headers: { accept: 'text/html' } }),
141
- res,
142
- next,
143
- )
144
-
145
- expect(server.transformIndexHtml).toHaveBeenCalled()
146
- expect(res.statusCode).toBe(200)
147
- expect(res.setHeader).toHaveBeenCalledWith('Content-Type', 'text/html; charset=utf-8')
148
- expect(res.end).toHaveBeenCalledWith('<html>spa shell</html>')
149
- expect(next).not.toHaveBeenCalled()
150
- })
151
-
152
- it('does not serve the SPA shell for API requests in spa mode', async () => {
153
- const { server, getMiddleware } = makeServer()
154
- configureCerDevServer(
155
- server as any,
156
- makeConfig({ mode: 'spa' }),
157
- )
158
-
159
- const next = vi.fn()
160
- await getMiddleware()(
161
- createReq({ url: '/api/missing', headers: { accept: 'text/html' } }),
162
- createRes(),
163
- next,
164
- )
165
-
166
- expect(server.transformIndexHtml).not.toHaveBeenCalled()
167
- expect(next).toHaveBeenCalled()
168
- })
169
- })
170
-
171
- // ─── API route matching ───────────────────────────────────────────────────────
172
-
173
- describe('configureCerDevServer — API route matching', () => {
174
- function makeServerWithRoute(route: { path: string; handlers: Record<string, unknown> }) {
175
- const { server, getMiddleware } = makeServer()
176
- server.ssrLoadModule.mockImplementation(async (path: string) => {
177
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
178
- return { apiRoutes: [route] }
179
- })
180
- configureCerDevServer(server as any, makeConfig())
181
- return getMiddleware()
182
- }
183
-
184
- it('calls GET handler for matching path', async () => {
185
- const handler = vi.fn((req: any, res: any) => res.end('ok'))
186
- const middleware = makeServerWithRoute({ path: '/api/health', handlers: { get: handler } })
187
- const next = vi.fn()
188
- await middleware(createReq({ url: '/api/health' }), createRes(), next)
189
- expect(handler).toHaveBeenCalled()
190
- expect(next).not.toHaveBeenCalled()
191
- })
192
-
193
- it('calls uppercase GET handler when lowercase is absent', async () => {
194
- const handler = vi.fn((req: any, res: any) => res.end('ok'))
195
- const middleware = makeServerWithRoute({ path: '/api/health', handlers: { GET: handler } })
196
- await middleware(createReq({ url: '/api/health' }), createRes(), vi.fn())
197
- expect(handler).toHaveBeenCalled()
198
- })
199
-
200
- it('calls default handler as fallback', async () => {
201
- const handler = vi.fn((req: any, res: any) => res.end('ok'))
202
- const middleware = makeServerWithRoute({ path: '/api/health', handlers: { default: handler } })
203
- await middleware(createReq({ url: '/api/health' }), createRes(), vi.fn())
204
- expect(handler).toHaveBeenCalled()
205
- })
206
-
207
- it('calls next() when no handler matches the HTTP method', async () => {
208
- const middleware = makeServerWithRoute({ path: '/api/health', handlers: {} })
209
- const next = vi.fn()
210
- await middleware(createReq({ url: '/api/health' }), createRes(), next)
211
- expect(next).toHaveBeenCalled()
212
- })
213
-
214
- it('does not match when segment counts differ', async () => {
215
- const handler = vi.fn()
216
- const middleware = makeServerWithRoute({ path: '/api/users/:id', handlers: { get: handler } })
217
- const next = vi.fn()
218
- await middleware(createReq({ url: '/api/users' }), createRes(), next)
219
- expect(handler).not.toHaveBeenCalled()
220
- expect(next).toHaveBeenCalled()
221
- })
222
-
223
- it('does not match when a static segment differs', async () => {
224
- const handler = vi.fn()
225
- const middleware = makeServerWithRoute({ path: '/api/posts', handlers: { get: handler } })
226
- const next = vi.fn()
227
- await middleware(createReq({ url: '/api/users' }), createRes(), next)
228
- expect(handler).not.toHaveBeenCalled()
229
- })
230
-
231
- it('extracts dynamic route params and attaches to req', async () => {
232
- let capturedReq: any
233
- const handler = vi.fn((req: any, res: any) => { capturedReq = req; res.end('ok') })
234
- const middleware = makeServerWithRoute({ path: '/api/users/:id', handlers: { get: handler } })
235
- await middleware(createReq({ url: '/api/users/42' }), createRes(), vi.fn())
236
- expect(capturedReq.params).toEqual({ id: '42' })
237
- })
238
-
239
- it('attaches parsed query string to req', async () => {
240
- let capturedReq: any
241
- const handler = vi.fn((req: any, res: any) => { capturedReq = req; res.end('ok') })
242
- const middleware = makeServerWithRoute({ path: '/api/search', handlers: { get: handler } })
243
- await middleware(createReq({ url: '/api/search?q=hello&page=2' }), createRes(), vi.fn())
244
- expect(capturedReq.query).toEqual({ q: 'hello', page: '2' })
245
- })
246
-
247
- it('attaches empty query object when no query string', async () => {
248
- let capturedReq: any
249
- const handler = vi.fn((req: any, res: any) => { capturedReq = req; res.end('ok') })
250
- const middleware = makeServerWithRoute({ path: '/api/health', handlers: { get: handler } })
251
- await middleware(createReq({ url: '/api/health' }), createRes(), vi.fn())
252
- expect(capturedReq.query).toEqual({})
253
- })
254
-
255
- it('handles query param with no value (flag style ?active)', async () => {
256
- let capturedReq: any
257
- const handler = vi.fn((req: any, res: any) => { capturedReq = req; res.end('ok') })
258
- const middleware = makeServerWithRoute({ path: '/api/items', handlers: { get: handler } })
259
- await middleware(createReq({ url: '/api/items?active' }), createRes(), vi.fn())
260
- expect(capturedReq.query).toEqual({ active: '' })
261
- })
262
-
263
- it('skips empty segments from double-ampersand query strings', async () => {
264
- let capturedReq: any
265
- const handler = vi.fn((req: any, res: any) => { capturedReq = req; res.end('ok') })
266
- const middleware = makeServerWithRoute({ path: '/api/items', handlers: { get: handler } })
267
- await middleware(createReq({ url: '/api/items?a=1&&b=2' }), createRes(), vi.fn())
268
- expect(capturedReq.query).toEqual({ a: '1', b: '2' })
269
- })
270
-
271
- it('decodes URL-encoded route params', async () => {
272
- let capturedReq: any
273
- const handler = vi.fn((req: any, res: any) => { capturedReq = req; res.end('ok') })
274
- const middleware = makeServerWithRoute({ path: '/api/posts/:slug', handlers: { get: handler } })
275
- await middleware(createReq({ url: '/api/posts/hello%20world' }), createRes(), vi.fn())
276
- expect(capturedReq.params.slug).toBe('hello world')
277
- })
278
-
279
- it('returns 500 when API handler throws', async () => {
280
- const handler = vi.fn(() => { throw new Error('oops') })
281
- const { server, getMiddleware } = makeServer()
282
- server.ssrLoadModule.mockImplementation(async (path: string) => {
283
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
284
- return { apiRoutes: [{ path: '/api/boom', handlers: { get: handler } }] }
285
- })
286
- configureCerDevServer(server as any, makeConfig())
287
- const res = createRes()
288
- await getMiddleware()(createReq({ url: '/api/boom' }), res, vi.fn())
289
- expect(res.statusCode).toBe(500)
290
- expect(res.end).toHaveBeenCalled()
291
- })
292
- })
293
-
294
- // ─── Augmented response helpers ───────────────────────────────────────────────
295
-
296
- describe('configureCerDevServer — augmented response helpers', () => {
297
- function makeServerWithRoute(handler: Function) {
298
- const { server, getMiddleware } = makeServer()
299
- server.ssrLoadModule.mockImplementation(async (path: string) => {
300
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
301
- return { apiRoutes: [{ path: '/api/test', handlers: { get: handler } }] }
302
- })
303
- configureCerDevServer(server as any, makeConfig())
304
- return getMiddleware()
305
- }
306
-
307
- it('res.json() serializes data and sets Content-Type', async () => {
308
- const res = createRes()
309
- const middleware = makeServerWithRoute((req: any, r: any) => r.json({ ok: true }))
310
- await middleware(createReq({ url: '/api/test' }), res, vi.fn())
311
- expect(res.setHeader).toHaveBeenCalledWith('Content-Type', 'application/json')
312
- expect(res.end).toHaveBeenCalledWith('{"ok":true}')
313
- })
314
-
315
- it('res.status() sets statusCode and is chainable', async () => {
316
- const res = createRes()
317
- const middleware = makeServerWithRoute((req: any, r: any) => r.status(404).end('Not Found'))
318
- await middleware(createReq({ url: '/api/test' }), res, vi.fn())
319
- expect(res.statusCode).toBe(404)
320
- expect(res.end).toHaveBeenCalledWith('Not Found')
321
- })
322
- })
323
-
324
- // ─── POST body parsing ────────────────────────────────────────────────────────
325
-
326
- describe('configureCerDevServer — POST body parsing', () => {
327
- it('parses JSON body for POST request', async () => {
328
- let capturedBody: unknown
329
- const { server, getMiddleware } = makeServer()
330
- server.ssrLoadModule.mockImplementation(async (path: string) => {
331
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
332
- return {
333
- apiRoutes: [{
334
- path: '/api/items',
335
- handlers: { post: (req: any, res: any) => { capturedBody = req.body; res.end('ok') } },
336
- }],
337
- }
338
- })
339
- configureCerDevServer(server as any, makeConfig())
340
- const req = createReq({
341
- url: '/api/items',
342
- method: 'POST',
343
- headers: { 'content-type': 'application/json' },
344
- body: JSON.stringify({ name: 'test' }),
345
- })
346
- await getMiddleware()(req, createRes(), vi.fn())
347
- expect(capturedBody).toEqual({ name: 'test' })
348
- })
349
-
350
- it('returns raw Buffer body for POST with non-JSON content-type', async () => {
351
- let capturedBody: unknown
352
- const { server, getMiddleware } = makeServer()
353
- server.ssrLoadModule.mockImplementation(async (path: string) => {
354
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
355
- return {
356
- apiRoutes: [{
357
- path: '/api/upload',
358
- handlers: { post: (req: any, res: any) => { capturedBody = req.body; res.end('ok') } },
359
- }],
360
- }
361
- })
362
- configureCerDevServer(server as any, makeConfig())
363
- const req = createReq({
364
- url: '/api/upload',
365
- method: 'POST',
366
- headers: { 'content-type': 'text/plain' },
367
- body: 'raw text',
368
- })
369
- await getMiddleware()(req, createRes(), vi.fn())
370
- expect(Buffer.isBuffer(capturedBody)).toBe(true)
371
- })
372
-
373
- it('attaches undefined body for GET requests', async () => {
374
- let capturedBody: unknown = 'sentinel'
375
- const { server, getMiddleware } = makeServer()
376
- server.ssrLoadModule.mockImplementation(async (path: string) => {
377
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
378
- return {
379
- apiRoutes: [{
380
- path: '/api/items',
381
- handlers: { get: (req: any, res: any) => { capturedBody = req.body; res.end('ok') } },
382
- }],
383
- }
384
- })
385
- configureCerDevServer(server as any, makeConfig())
386
- await getMiddleware()(createReq({ url: '/api/items' }), createRes(), vi.fn())
387
- expect(capturedBody).toBeUndefined()
388
- })
389
- })
390
-
391
- // ─── Server middleware execution ──────────────────────────────────────────────
392
-
393
- describe('configureCerDevServer — server middleware', () => {
394
- it('runs server middleware before API routes', async () => {
395
- const callOrder: string[] = []
396
- const smHandler = vi.fn((_req: any, _res: any, next: () => void) => {
397
- callOrder.push('sm')
398
- next()
399
- })
400
- const apiHandler = vi.fn((req: any, res: any) => { callOrder.push('api'); res.end('ok') })
401
- const { server, getMiddleware } = makeServer()
402
- server.ssrLoadModule.mockImplementation(async (path: string) => {
403
- if (path.includes('server-middleware')) return { serverMiddleware: [{ name: 'test', handler: smHandler }] }
404
- return { apiRoutes: [{ path: '/api/health', handlers: { get: apiHandler } }] }
405
- })
406
- configureCerDevServer(server as any, makeConfig())
407
- await getMiddleware()(createReq({ url: '/api/health' }), createRes(), vi.fn())
408
- expect(callOrder).toEqual(['sm', 'api'])
409
- })
410
-
411
- it('stops processing when server middleware does not call next()', async () => {
412
- const smHandler = vi.fn((_req: any, res: any) => { res.end('blocked') }) // no next() call
413
- const apiHandler = vi.fn()
414
- const { server, getMiddleware } = makeServer()
415
- server.ssrLoadModule.mockImplementation(async (path: string) => {
416
- if (path.includes('server-middleware')) return { serverMiddleware: [{ name: 'block', handler: smHandler }] }
417
- return { apiRoutes: [{ path: '/api/health', handlers: { get: apiHandler } }] }
418
- })
419
- configureCerDevServer(server as any, makeConfig())
420
- await getMiddleware()(createReq({ url: '/api/health' }), createRes(), vi.fn())
421
- expect(apiHandler).not.toHaveBeenCalled()
422
- })
423
-
424
- it('sends 500 and stops chain when middleware calls next(err)', async () => {
425
- const smHandler = vi.fn((_req: any, _res: any, next: (err?: unknown) => void) => {
426
- next(new Error('auth failed'))
427
- })
428
- const apiHandler = vi.fn()
429
- const { server, getMiddleware } = makeServer()
430
- server.ssrLoadModule.mockImplementation(async (path: string) => {
431
- if (path.includes('server-middleware')) return { serverMiddleware: [{ name: 'err', handler: smHandler }] }
432
- return { apiRoutes: [{ path: '/api/health', handlers: { get: apiHandler } }] }
433
- })
434
- configureCerDevServer(server as any, makeConfig())
435
- const res = createRes()
436
- await getMiddleware()(createReq({ url: '/api/health' }), res, vi.fn())
437
- expect(res.statusCode).toBe(500)
438
- expect(res.end).toHaveBeenCalledWith('Internal Server Error')
439
- expect(apiHandler).not.toHaveBeenCalled()
440
- })
441
-
442
- it('sends 500 and stops chain when middleware throws', async () => {
443
- const smHandler = vi.fn(() => { throw new Error('unexpected') })
444
- const apiHandler = vi.fn()
445
- const { server, getMiddleware } = makeServer()
446
- server.ssrLoadModule.mockImplementation(async (path: string) => {
447
- if (path.includes('server-middleware')) return { serverMiddleware: [{ name: 'throw', handler: smHandler }] }
448
- return { apiRoutes: [{ path: '/api/health', handlers: { get: apiHandler } }] }
449
- })
450
- configureCerDevServer(server as any, makeConfig())
451
- const res = createRes()
452
- await getMiddleware()(createReq({ url: '/api/health' }), res, vi.fn())
453
- expect(res.statusCode).toBe(500)
454
- expect(apiHandler).not.toHaveBeenCalled()
455
- })
456
-
457
- it('handles async middleware that returns a Promise and calls next() after awaiting', async () => {
458
- const callOrder: string[] = []
459
- const smHandler = vi.fn((_req: any, _res: any, next: () => void) =>
460
- new Promise<void>((resolve) => {
461
- // Simulate async work before calling next
462
- setImmediate(() => { callOrder.push('sm'); next(); resolve() })
463
- }),
464
- )
465
- const apiHandler = vi.fn((_req: any, res: any) => { callOrder.push('api'); res.end('ok') })
466
- const { server, getMiddleware } = makeServer()
467
- server.ssrLoadModule.mockImplementation(async (path: string) => {
468
- if (path.includes('server-middleware')) return { serverMiddleware: [{ name: 'async', handler: smHandler }] }
469
- return { apiRoutes: [{ path: '/api/health', handlers: { get: apiHandler } }] }
470
- })
471
- configureCerDevServer(server as any, makeConfig())
472
- await getMiddleware()(createReq({ url: '/api/health' }), createRes(), vi.fn())
473
- expect(callOrder).toEqual(['sm', 'api'])
474
- })
475
-
476
- it('handles async middleware that rejects (returns a rejected Promise)', async () => {
477
- const smHandler = vi.fn(() => Promise.reject(new Error('async failure')))
478
- const apiHandler = vi.fn()
479
- const { server, getMiddleware } = makeServer()
480
- server.ssrLoadModule.mockImplementation(async (path: string) => {
481
- if (path.includes('server-middleware')) return { serverMiddleware: [{ name: 'async-throw', handler: smHandler }] }
482
- return { apiRoutes: [{ path: '/api/health', handlers: { get: apiHandler } }] }
483
- })
484
- configureCerDevServer(server as any, makeConfig())
485
- const res = createRes()
486
- await getMiddleware()(createReq({ url: '/api/health' }), res, vi.fn())
487
- expect(res.statusCode).toBe(500)
488
- expect(apiHandler).not.toHaveBeenCalled()
489
- })
490
- })
491
-
492
- // ─── SSR mode ─────────────────────────────────────────────────────────────────
493
-
494
- describe('configureCerDevServer — SSR mode', () => {
495
- it('invokes SSR handler for HTML requests in ssr mode', async () => {
496
- const ssrHandler = vi.fn(async (req: any, res: any) => res.end('<html>SSR</html>'))
497
- const { server, getMiddleware } = makeServer()
498
- server.ssrLoadModule.mockImplementation(async (path: string) => {
499
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
500
- if (path.includes('server-api')) return { apiRoutes: [] }
501
- // SSR entry module
502
- return { handler: ssrHandler }
503
- })
504
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
505
- const res = createRes()
506
- await getMiddleware()(
507
- createReq({ url: '/', headers: { accept: 'text/html' } }),
508
- res,
509
- vi.fn(),
510
- )
511
- expect(ssrHandler).toHaveBeenCalled()
512
- })
513
-
514
- it('invokes SSR handler for HTML requests in ssg mode', async () => {
515
- const ssrHandler = vi.fn(async (req: any, res: any) => res.end('<html>SSG-DEV</html>'))
516
- const { server, getMiddleware } = makeServer()
517
- server.ssrLoadModule.mockImplementation(async (path: string) => {
518
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
519
- if (path.includes('server-api')) return { apiRoutes: [] }
520
- return { handler: ssrHandler }
521
- })
522
- configureCerDevServer(server as any, makeConfig({ mode: 'ssg' }))
523
- const res = createRes()
524
- await getMiddleware()(
525
- createReq({ url: '/', headers: { accept: 'text/html' } }),
526
- res,
527
- vi.fn(),
528
- )
529
- expect(ssrHandler).toHaveBeenCalled()
530
- })
531
-
532
- it('does not invoke SSR handler in spa mode', async () => {
533
- const ssrHandler = vi.fn()
534
- const { server, getMiddleware } = makeServer()
535
- server.ssrLoadModule.mockImplementation(async (path: string) => {
536
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
537
- if (path.includes('server-api')) return { apiRoutes: [] }
538
- return { handler: ssrHandler }
539
- })
540
- server.transformIndexHtml.mockResolvedValue('<html>spa shell</html>')
541
- vi.mocked(existsSync).mockReturnValue(true)
542
- vi.mocked(readFileSync).mockReturnValue('<html>source shell</html>' as never)
543
- configureCerDevServer(
544
- server as any,
545
- makeConfig({ mode: 'spa' }),
546
- )
547
- const next = vi.fn()
548
- const res = createRes()
549
- await getMiddleware()(
550
- createReq({ url: '/', headers: { accept: 'text/html' } }),
551
- res,
552
- next,
553
- )
554
- expect(ssrHandler).not.toHaveBeenCalled()
555
- expect(res.end).toHaveBeenCalledWith('<html>spa shell</html>')
556
- expect(next).not.toHaveBeenCalled()
557
- })
558
-
559
- it('does not invoke SSR handler for non-HTML requests (e.g. assets)', async () => {
560
- const ssrHandler = vi.fn()
561
- const { server, getMiddleware } = makeServer()
562
- server.ssrLoadModule.mockImplementation(async (path: string) => {
563
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
564
- if (path.includes('server-api')) return { apiRoutes: [] }
565
- return { handler: ssrHandler }
566
- })
567
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
568
- const next = vi.fn()
569
- await getMiddleware()(
570
- createReq({ url: '/assets/main.js', headers: { accept: 'application/javascript' } }),
571
- createRes(),
572
- next,
573
- )
574
- expect(ssrHandler).not.toHaveBeenCalled()
575
- expect(next).toHaveBeenCalled()
576
- })
577
-
578
- it('does not invoke SSR handler for non-HTML requests in ssg mode', async () => {
579
- const ssrHandler = vi.fn()
580
- const { server, getMiddleware } = makeServer()
581
- server.ssrLoadModule.mockImplementation(async (path: string) => {
582
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
583
- if (path.includes('server-api')) return { apiRoutes: [] }
584
- return { handler: ssrHandler }
585
- })
586
- configureCerDevServer(server as any, makeConfig({ mode: 'ssg' }))
587
- const next = vi.fn()
588
- await getMiddleware()(
589
- createReq({ url: '/assets/main.js', headers: { accept: 'application/javascript' } }),
590
- createRes(),
591
- next,
592
- )
593
- expect(ssrHandler).not.toHaveBeenCalled()
594
- expect(next).toHaveBeenCalled()
595
- })
596
-
597
- it('returns 500 and error text when SSR handler throws', async () => {
598
- const { server, getMiddleware } = makeServer()
599
- server.ssrLoadModule.mockImplementation(async (path: string) => {
600
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
601
- if (path.includes('server-api')) return { apiRoutes: [] }
602
- return { handler: async () => { throw new Error('render failed') } }
603
- })
604
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
605
- const res = createRes()
606
- await getMiddleware()(createReq({ url: '/', headers: { accept: 'text/html' } }), res, vi.fn())
607
- expect(res.statusCode).toBe(500)
608
- })
609
-
610
- it('calls next() for non-HTML in ssr mode without API match', async () => {
611
- const { server, getMiddleware } = makeServer()
612
- server.ssrLoadModule.mockImplementation(async (path: string) => {
613
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
614
- return { apiRoutes: [] }
615
- })
616
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
617
- const next = vi.fn()
618
- await getMiddleware()(
619
- createReq({ url: '/favicon.ico', headers: { accept: 'image/x-icon' } }),
620
- createRes(),
621
- next,
622
- )
623
- expect(next).toHaveBeenCalled()
624
- })
625
-
626
- it('uses renderFn fallback when SSR entry exports render instead of handler', async () => {
627
- const renderFn = vi.fn().mockResolvedValue({ html: '<div>rendered</div>' })
628
- const { server, getMiddleware } = makeServer()
629
- ;(server as any).transformIndexHtml = vi.fn().mockResolvedValue(
630
- '<!DOCTYPE html><html><body><div id="app"></div></body></html>',
631
- )
632
- server.ssrLoadModule.mockImplementation(async (path: string) => {
633
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
634
- if (path.includes('server-api')) return { apiRoutes: [] }
635
- return { render: renderFn }
636
- })
637
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
638
- const res = createRes()
639
- await getMiddleware()(createReq({ url: '/', headers: { accept: 'text/html' } }), res, vi.fn())
640
- expect(renderFn).toHaveBeenCalled()
641
- expect(res.end).toHaveBeenCalledWith(expect.stringContaining('<div>rendered</div>'))
642
- })
643
-
644
- it('accepts html request when url is / even without text/html accept header', async () => {
645
- const ssrHandler = vi.fn(async (req: any, res: any) => res.end('<html>home</html>'))
646
- const { server, getMiddleware } = makeServer()
647
- server.ssrLoadModule.mockImplementation(async (path: string) => {
648
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
649
- if (path.includes('server-api')) return { apiRoutes: [] }
650
- return { handler: ssrHandler }
651
- })
652
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
653
- // url='/' triggers acceptsHtml without needing accept header
654
- await getMiddleware()(createReq({ url: '/' }), createRes(), vi.fn())
655
- expect(ssrHandler).toHaveBeenCalled()
656
- })
657
- })
658
-
659
- // ─── Per-route render mode (SSR/SSG mode) ────────────────────────────────────
660
-
661
- describe("configureCerDevServer — per-route render mode in SSR", () => {
662
- /** Build a server where virtual:cer-routes returns the given routes array. */
663
- function makeServerWithRoutes(
664
- pageRoutes: Array<{ path: string; meta?: Record<string, unknown> }>,
665
- ssrHandler = vi.fn(async (_req: any, res: any) => res.end('<html>SSR</html>')),
666
- mode: 'ssr' | 'ssg' = 'ssr',
667
- ) {
668
- const { server, getMiddleware } = makeServer()
669
- server.ssrLoadModule.mockImplementation(async (path: string) => {
670
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
671
- if (path.includes('server-api') || path.includes('cer-server-api')) return { apiRoutes: [] }
672
- if (path.includes('cer-routes')) return { default: pageRoutes }
673
- return { handler: ssrHandler }
674
- })
675
- configureCerDevServer(server as any, makeConfig({ mode }))
676
- return { middleware: getMiddleware(), ssrHandler }
677
- }
678
-
679
- it("calls next() instead of SSR for a route with render: 'spa'", async () => {
680
- const { middleware, ssrHandler } = makeServerWithRoutes([
681
- { path: '/spa-page', meta: { render: 'spa' } },
682
- ])
683
- const next = vi.fn()
684
- await middleware(
685
- createReq({ url: '/spa-page', headers: { accept: 'text/html' } }),
686
- createRes(),
687
- next,
688
- )
689
- expect(next).toHaveBeenCalled()
690
- expect(ssrHandler).not.toHaveBeenCalled()
691
- })
692
-
693
- it("calls next() instead of SSR for a route with render: 'spa' in ssg mode", async () => {
694
- const { middleware, ssrHandler } = makeServerWithRoutes(
695
- [{ path: '/spa-page', meta: { render: 'spa' } }],
696
- vi.fn(async (_req: any, res: any) => res.end('<html>SSG</html>')),
697
- 'ssg',
698
- )
699
- const next = vi.fn()
700
- await middleware(
701
- createReq({ url: '/spa-page', headers: { accept: 'text/html' } }),
702
- createRes(),
703
- next,
704
- )
705
- expect(next).toHaveBeenCalled()
706
- expect(ssrHandler).not.toHaveBeenCalled()
707
- })
708
-
709
- it("proceeds to SSR for a route with render: 'server'", async () => {
710
- const { middleware, ssrHandler } = makeServerWithRoutes([
711
- { path: '/server-page', meta: { render: 'server' } },
712
- ])
713
- await middleware(
714
- createReq({ url: '/server-page', headers: { accept: 'text/html' } }),
715
- createRes(),
716
- vi.fn(),
717
- )
718
- expect(ssrHandler).toHaveBeenCalled()
719
- })
720
-
721
- it("proceeds to SSR for a route with no render meta", async () => {
722
- const { middleware, ssrHandler } = makeServerWithRoutes([
723
- { path: '/normal-page' },
724
- ])
725
- await middleware(
726
- createReq({ url: '/normal-page', headers: { accept: 'text/html' } }),
727
- createRes(),
728
- vi.fn(),
729
- )
730
- expect(ssrHandler).toHaveBeenCalled()
731
- })
732
-
733
- it("falls back to SSR when routes module throws during render mode check", async () => {
734
- const ssrHandler = vi.fn(async (_req: any, res: any) => res.end('<html>SSR</html>'))
735
- const { server, getMiddleware } = makeServer()
736
- server.ssrLoadModule.mockImplementation(async (path: string) => {
737
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
738
- if (path.includes('server-api') || path.includes('cer-server-api')) return { apiRoutes: [] }
739
- if (path.includes('cer-routes')) throw new Error('module not ready')
740
- return { handler: ssrHandler }
741
- })
742
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
743
- await getMiddleware()(
744
- createReq({ url: '/some-page', headers: { accept: 'text/html' } }),
745
- createRes(),
746
- vi.fn(),
747
- )
748
- // Despite the routes module throwing, SSR should still run
749
- expect(ssrHandler).toHaveBeenCalled()
750
- })
751
-
752
- it("does not load routes module in SPA mode (no render mode check needed)", async () => {
753
- const { server, getMiddleware } = makeServer()
754
- configureCerDevServer(server as any, makeConfig({ mode: 'spa' }))
755
- await getMiddleware()(
756
- createReq({ url: '/about', headers: { accept: 'text/html' } }),
757
- createRes(),
758
- vi.fn(),
759
- )
760
- const routesCalls = (server.ssrLoadModule as any).mock.calls.filter(
761
- ([p]: [string]) => p.includes('cer-routes'),
762
- )
763
- expect(routesCalls).toHaveLength(0)
764
- })
765
-
766
- it("loads routes module in ssg mode to check per-route render mode", async () => {
767
- const ssrHandler = vi.fn(async (_req: any, res: any) => res.end('<html>SSG</html>'))
768
- const { server, getMiddleware } = makeServer()
769
- server.ssrLoadModule.mockImplementation(async (path: string) => {
770
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
771
- if (path.includes('server-api') || path.includes('cer-server-api')) return { apiRoutes: [] }
772
- if (path.includes('cer-routes')) return { default: [] }
773
- return { handler: ssrHandler }
774
- })
775
- configureCerDevServer(server as any, makeConfig({ mode: 'ssg' }))
776
- await getMiddleware()(
777
- createReq({ url: '/about', headers: { accept: 'text/html' } }),
778
- createRes(),
779
- vi.fn(),
780
- )
781
- const routesCalls = (server.ssrLoadModule as any).mock.calls.filter(
782
- ([p]: [string]) => p.includes('cer-routes'),
783
- )
784
- expect(routesCalls.length).toBeGreaterThan(0)
785
- })
786
- })
787
-
788
- // ─── parseBody edge cases ─────────────────────────────────────────────────────
789
-
790
- describe('configureCerDevServer — malformed JSON body', () => {
791
- it('returns undefined when POST body is malformed JSON', async () => {
792
- let capturedBody: unknown = 'sentinel'
793
- const { server, getMiddleware } = makeServer()
794
- server.ssrLoadModule.mockImplementation(async (path: string) => {
795
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
796
- return {
797
- apiRoutes: [{
798
- path: '/api/items',
799
- handlers: { post: (req: any, res: any) => { capturedBody = req.body; res.end('ok') } },
800
- }],
801
- }
802
- })
803
- configureCerDevServer(server as any, makeConfig())
804
- const req = createReq({
805
- url: '/api/items',
806
- method: 'POST',
807
- headers: { 'content-type': 'application/json' },
808
- body: '{ invalid json }',
809
- })
810
- await getMiddleware()(req, createRes(), vi.fn())
811
- expect(capturedBody).toBeUndefined()
812
- })
813
- })