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