@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,298 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
2
- import { AsyncLocalStorage } from 'node:async_hooks'
3
-
4
- // --- Module-level mock for @jasonshimmy/custom-elements-runtime ---
5
- // usePageData imports getCurrentComponentContext from the runtime. We mock the
6
- // module so unit tests can control which context (if any) is "active".
7
- let _mockContext: Record<string, unknown> | null = null
8
-
9
- vi.mock('@jasonshimmy/custom-elements-runtime', () => ({
10
- getCurrentComponentContext: () => _mockContext,
11
- }))
12
-
13
- import { usePageData } from '../../runtime/composables/use-page-data.js'
14
-
15
- describe('usePageData', () => {
16
- beforeEach(() => {
17
- // Clean up global state between tests
18
- delete (globalThis as Record<string, unknown>)['__CER_DATA__']
19
- delete (globalThis as Record<string, unknown>)['__CER_DATA_STORE__']
20
- })
21
-
22
- afterEach(() => {
23
- delete (globalThis as Record<string, unknown>)['__CER_DATA_STORE__']
24
- })
25
-
26
- it('returns null when no SSR data is present', () => {
27
- expect(usePageData()).toBeNull()
28
- })
29
-
30
- it('returns the data when __CER_DATA__ is set on globalThis', () => {
31
- const data = { id: '1', name: 'Laptop', price: 999 }
32
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = data
33
- expect(usePageData()).toEqual(data)
34
- })
35
-
36
- it('does NOT clear __CER_DATA__ on read (cleared by app.ts after router.replace)', () => {
37
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = { title: 'Hello' }
38
- usePageData()
39
- expect((globalThis as Record<string, unknown>)['__CER_DATA__']).toEqual({ title: 'Hello' })
40
- })
41
-
42
- it('returns the same data on subsequent calls (not consumed on first read)', () => {
43
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = { title: 'Hello' }
44
- usePageData()
45
- expect(usePageData()).toEqual({ title: 'Hello' })
46
- })
47
-
48
- it('is generic and preserves the typed shape', () => {
49
- interface Post { slug: string; title: string }
50
- const post: Post = { slug: 'hello', title: 'Hello World' }
51
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = post
52
- const result = usePageData<Post>()
53
- expect(result?.slug).toBe('hello')
54
- expect(result?.title).toBe('Hello World')
55
- })
56
-
57
- it('returns null when __CER_DATA__ is explicitly null', () => {
58
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = null
59
- expect(usePageData()).toBeNull()
60
- })
61
- })
62
-
63
- describe('usePageData — AsyncLocalStorage (server-side)', () => {
64
- let store: AsyncLocalStorage<unknown>
65
-
66
- beforeEach(() => {
67
- delete (globalThis as Record<string, unknown>)['__CER_DATA__']
68
- store = new AsyncLocalStorage()
69
- ;(globalThis as Record<string, unknown>)['__CER_DATA_STORE__'] = store
70
- })
71
-
72
- afterEach(() => {
73
- delete (globalThis as Record<string, unknown>)['__CER_DATA_STORE__']
74
- delete (globalThis as Record<string, unknown>)['__CER_DATA__']
75
- })
76
-
77
- it('reads data from the ALS store when available', () => {
78
- const data = { title: 'SSR Post', body: 'Hello from ALS' }
79
- store.run(data, () => {
80
- expect(usePageData()).toEqual(data)
81
- })
82
- })
83
-
84
- it('returns null when ALS store is empty (null)', () => {
85
- store.run(null, () => {
86
- expect(usePageData()).toBeNull()
87
- })
88
- })
89
-
90
- it('prefers ALS store over globalThis.__CER_DATA__', () => {
91
- const alsData = { source: 'als' }
92
- const globalData = { source: 'global' }
93
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = globalData
94
- store.run(alsData, () => {
95
- expect(usePageData()).toEqual(alsData)
96
- })
97
- })
98
-
99
- it('does NOT delete __CER_DATA__ when returning ALS data', () => {
100
- const globalData = { source: 'global' }
101
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = globalData
102
- store.run({ source: 'als' }, () => {
103
- usePageData()
104
- })
105
- // global data should still be present — ALS path doesn't consume it
106
- expect((globalThis as Record<string, unknown>)['__CER_DATA__']).toEqual(globalData)
107
- })
108
-
109
- it('falls back to __CER_DATA__ when ALS store has no value outside run()', () => {
110
- // Outside any store.run() context, getStore() returns undefined
111
- const data = { fallback: true }
112
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = data
113
- // store is set but we're outside a run() context → getStore() === undefined
114
- expect(usePageData()).toEqual(data)
115
- })
116
-
117
- it('returns null when both ALS and __CER_DATA__ are absent', () => {
118
- store.run(null, () => {
119
- expect(usePageData()).toBeNull()
120
- })
121
- })
122
-
123
- it('is safe to call multiple times inside same ALS context', () => {
124
- const data = { count: 42 }
125
- store.run(data, () => {
126
- expect(usePageData()).toEqual(data)
127
- // ALS data is NOT consumed on read, so a second call still returns it
128
- expect(usePageData()).toEqual(data)
129
- })
130
- })
131
- })
132
-
133
- // ─── Component context caching ─────────────────────────────────────────────
134
- //
135
- // usePageData() caches the result on the component context (via Object.defineProperty)
136
- // so that re-renders of the same element instance return the same value even after
137
- // __CER_DATA__ is deleted by the post-hydration queueMicrotask cleanup.
138
- //
139
- // Background: renderFn passed to component() IS the render function — it runs on
140
- // every re-render, not just once as a setup phase. Without caching, calling
141
- // usePageData() on the second render after __CER_DATA__ is deleted returns null,
142
- // which flips `ssrData ? 'ssr' : 'client'` guards and re-triggers client fetches.
143
-
144
- describe('usePageData — component context caching', () => {
145
- const _PAGE_DATA_KEY = '_cerPageData'
146
-
147
- beforeEach(() => {
148
- delete (globalThis as Record<string, unknown>)['__CER_DATA__']
149
- delete (globalThis as Record<string, unknown>)['__CER_DATA_STORE__']
150
- _mockContext = null
151
- })
152
-
153
- afterEach(() => {
154
- delete (globalThis as Record<string, unknown>)['__CER_DATA__']
155
- _mockContext = null
156
- })
157
-
158
- it('returns null and does not cache when context is null (no render in progress)', () => {
159
- _mockContext = null
160
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = { value: 42 }
161
- const result = usePageData()
162
- expect(result).toEqual({ value: 42 })
163
- // No context to cache on — nothing to assert, just no crash
164
- })
165
-
166
- it('caches the result on the component context using Object.defineProperty', () => {
167
- const ctx: Record<string, unknown> = {}
168
- _mockContext = ctx
169
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = { value: 42 }
170
-
171
- usePageData()
172
-
173
- // Should be defined on ctx via Object.defineProperty (non-enumerable)
174
- expect(Object.prototype.hasOwnProperty.call(ctx, _PAGE_DATA_KEY)).toBe(true)
175
- expect(ctx[_PAGE_DATA_KEY]).toEqual({ value: 42 })
176
- })
177
-
178
- it('cached property is non-enumerable (does not appear in Object.keys)', () => {
179
- const ctx: Record<string, unknown> = {}
180
- _mockContext = ctx
181
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = { value: 42 }
182
-
183
- usePageData()
184
-
185
- expect(Object.keys(ctx)).not.toContain(_PAGE_DATA_KEY)
186
- })
187
-
188
- it('cached property is non-writable (reactive proxy set-trap cannot overwrite it)', () => {
189
- const ctx: Record<string, unknown> = {}
190
- _mockContext = ctx
191
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = { value: 42 }
192
-
193
- usePageData()
194
-
195
- // Attempting to overwrite a writable:false property silently fails in non-strict mode
196
- // and throws in strict mode. Verify the value doesn't change.
197
- try { ctx[_PAGE_DATA_KEY] = { value: 999 } } catch { /* strict mode */ }
198
- expect(ctx[_PAGE_DATA_KEY]).toEqual({ value: 42 })
199
- })
200
-
201
- it('returns cached value on second call even after __CER_DATA__ is deleted', () => {
202
- const ctx: Record<string, unknown> = {}
203
- _mockContext = ctx
204
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = { value: 42 }
205
-
206
- // First call (simulates initial render while __CER_DATA__ is present)
207
- const first = usePageData()
208
- expect(first).toEqual({ value: 42 })
209
-
210
- // Simulate post-hydration cleanup: queueMicrotask(() => delete __CER_DATA__)
211
- delete (globalThis as Record<string, unknown>)['__CER_DATA__']
212
-
213
- // Second call (simulates re-render after cleanup) — must return cached value
214
- const second = usePageData()
215
- expect(second).toEqual({ value: 42 })
216
- })
217
-
218
- it('caches null result so repeated calls with no data do not re-read the deleted global', () => {
219
- const ctx: Record<string, unknown> = {}
220
- _mockContext = ctx
221
- // No __CER_DATA__ — returns null and caches null
222
-
223
- const first = usePageData()
224
- expect(first).toBeNull()
225
-
226
- // Set __CER_DATA__ AFTER first call — second call should return cached null
227
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = { value: 99 }
228
- const second = usePageData()
229
- expect(second).toBeNull()
230
- })
231
-
232
- it('each component element instance has its own independent cache', () => {
233
- const ctxA: Record<string, unknown> = {}
234
- const ctxB: Record<string, unknown> = {}
235
-
236
- // First component: has SSR data
237
- _mockContext = ctxA
238
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = { page: 'home' }
239
- const resultA = usePageData()
240
- expect(resultA).toEqual({ page: 'home' })
241
-
242
- // Simulate navigation: delete home data, set blog data
243
- delete (globalThis as Record<string, unknown>)['__CER_DATA__']
244
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = { page: 'blog' }
245
-
246
- // Second component (different element, different context)
247
- _mockContext = ctxB
248
- const resultB = usePageData()
249
- expect(resultB).toEqual({ page: 'blog' })
250
-
251
- // First component re-rendered — still returns its cached value
252
- _mockContext = ctxA
253
- delete (globalThis as Record<string, unknown>)['__CER_DATA__']
254
- const resultA2 = usePageData()
255
- expect(resultA2).toEqual({ page: 'home' }) // cached, not stale blog data
256
- })
257
-
258
- it('ALS path takes precedence over context cache (server-side always uses ALS)', () => {
259
- const store = new AsyncLocalStorage<unknown>()
260
- ;(globalThis as Record<string, unknown>)['__CER_DATA_STORE__'] = store
261
-
262
- const ctx: Record<string, unknown> = {}
263
- // Pre-seed a stale client cache on the context
264
- Object.defineProperty(ctx, _PAGE_DATA_KEY, { value: { stale: true }, writable: false, configurable: true, enumerable: false })
265
- _mockContext = ctx
266
-
267
- const alsData = { fresh: true }
268
- store.run(alsData, () => {
269
- const result = usePageData()
270
- // ALS wins — context cache is not consulted for server-side renders
271
- expect(result).toEqual(alsData)
272
- })
273
-
274
- delete (globalThis as Record<string, unknown>)['__CER_DATA_STORE__']
275
- })
276
-
277
- it('context cache survives across multiple renderFn() invocations (re-render stability)', () => {
278
- const ctx: Record<string, unknown> = {}
279
- _mockContext = ctx
280
- ;(globalThis as Record<string, unknown>)['__CER_DATA__'] = { title: 'My Page' }
281
-
282
- // Simulate 5 re-renders (e.g. reactive state updates)
283
- for (let i = 0; i < 5; i++) {
284
- const result = usePageData<{ title: string }>()
285
- expect(result?.title).toBe('My Page')
286
- }
287
-
288
- // Delete __CER_DATA__ (post-hydration cleanup)
289
- delete (globalThis as Record<string, unknown>)['__CER_DATA__']
290
-
291
- // 5 more re-renders post-cleanup
292
- for (let i = 0; i < 5; i++) {
293
- const result = usePageData<{ title: string }>()
294
- expect(result?.title).toBe('My Page')
295
- }
296
- })
297
- })
298
-
@@ -1,156 +0,0 @@
1
- /**
2
- * useRoute() composable tests.
3
- *
4
- * Covers:
5
- * - Server path: reads from __CER_ROUTE_STORE__ AsyncLocalStorage
6
- * - Client path: reads from globalThis.__cerRouter
7
- * - Fallback: returns default route when neither is present
8
- */
9
-
10
- import { describe, it, expect, beforeEach, afterEach } from 'vitest'
11
- import { useRoute } from '../../runtime/composables/use-route.js'
12
-
13
- const g = globalThis as Record<string, unknown>
14
-
15
- function makeRouteStore(info: unknown) {
16
- const { AsyncLocalStorage } = require('node:async_hooks')
17
- const store = new AsyncLocalStorage()
18
- store.enterWith(info)
19
- return store
20
- }
21
-
22
- // ─── Server path ──────────────────────────────────────────────────────────────
23
-
24
- describe('useRoute() — server path (__CER_ROUTE_STORE__)', () => {
25
- let _orig: unknown
26
-
27
- beforeEach(() => {
28
- _orig = g['__CER_ROUTE_STORE__']
29
- })
30
-
31
- afterEach(() => {
32
- g['__CER_ROUTE_STORE__'] = _orig
33
- delete g['__cerRouter']
34
- })
35
-
36
- it('returns route info from the ALS store', () => {
37
- const info = { path: '/posts/42', params: { id: '42' }, query: {}, meta: { title: 'Post' } }
38
- g['__CER_ROUTE_STORE__'] = makeRouteStore(info)
39
-
40
- const route = useRoute()
41
- expect(route.path).toBe('/posts/42')
42
- expect(route.params).toEqual({ id: '42' })
43
- expect(route.meta).toEqual({ title: 'Post' })
44
- })
45
-
46
- it('falls through to client router when routeStore.getStore() returns null', () => {
47
- // Store is present but no active context (getStore returns null)
48
- const { AsyncLocalStorage } = require('node:async_hooks')
49
- const emptyStore = new AsyncLocalStorage()
50
- g['__CER_ROUTE_STORE__'] = emptyStore
51
- // Provide a client router so we get a real result instead of the fallback
52
- g['__cerRouter'] = {
53
- getCurrent: () => ({ path: '/client-path', query: {} }),
54
- matchRoute: () => ({ route: { meta: null }, params: {} }),
55
- }
56
-
57
- const route = useRoute()
58
- expect(route.path).toBe('/client-path')
59
- })
60
-
61
- it('returns query params from route info', () => {
62
- const info = { path: '/search', params: {}, query: { q: 'hello' }, meta: null }
63
- g['__CER_ROUTE_STORE__'] = makeRouteStore(info)
64
-
65
- const route = useRoute()
66
- expect(route.query).toEqual({ q: 'hello' })
67
- })
68
-
69
- it('returns null meta when not set', () => {
70
- g['__CER_ROUTE_STORE__'] = makeRouteStore({ path: '/', params: {}, query: {}, meta: null })
71
-
72
- const { meta } = useRoute()
73
- expect(meta).toBeNull()
74
- })
75
- })
76
-
77
- // ─── Client path ──────────────────────────────────────────────────────────────
78
-
79
- describe('useRoute() — client path (__cerRouter)', () => {
80
- beforeEach(() => {
81
- delete g['__CER_ROUTE_STORE__']
82
- })
83
-
84
- afterEach(() => {
85
- delete g['__cerRouter']
86
- })
87
-
88
- it('reads from the global router on the client', () => {
89
- g['__cerRouter'] = {
90
- getCurrent: () => ({ path: '/about', query: { ref: 'nav' } }),
91
- matchRoute: () => ({ route: { meta: { title: 'About' } }, params: {} }),
92
- }
93
-
94
- const route = useRoute()
95
- expect(route.path).toBe('/about')
96
- expect(route.query).toEqual({ ref: 'nav' })
97
- expect(route.meta).toEqual({ title: 'About' })
98
- })
99
-
100
- it('defaults query to {} when getCurrent() omits the query property', () => {
101
- g['__cerRouter'] = {
102
- getCurrent: () => ({ path: '/home' }),
103
- matchRoute: () => ({ route: { meta: null }, params: {} }),
104
- }
105
-
106
- const route = useRoute()
107
- expect(route.query).toEqual({})
108
- })
109
-
110
- it('extracts params from matched route', () => {
111
- g['__cerRouter'] = {
112
- getCurrent: () => ({ path: '/posts/7', query: {} }),
113
- matchRoute: () => ({ route: { meta: null }, params: { id: '7' } }),
114
- }
115
-
116
- const { params } = useRoute()
117
- expect(params).toEqual({ id: '7' })
118
- })
119
-
120
- it('returns null meta when route has none', () => {
121
- g['__cerRouter'] = {
122
- getCurrent: () => ({ path: '/', query: {} }),
123
- matchRoute: () => ({ route: { meta: null }, params: {} }),
124
- }
125
-
126
- expect(useRoute().meta).toBeNull()
127
- })
128
-
129
- it('handles unmatched route gracefully', () => {
130
- g['__cerRouter'] = {
131
- getCurrent: () => ({ path: '/unknown', query: {} }),
132
- matchRoute: () => null,
133
- }
134
-
135
- const route = useRoute()
136
- expect(route.params).toEqual({})
137
- expect(route.meta).toBeNull()
138
- })
139
- })
140
-
141
- // ─── Fallback ─────────────────────────────────────────────────────────────────
142
-
143
- describe('useRoute() — fallback (no store, no router)', () => {
144
- beforeEach(() => {
145
- delete g['__CER_ROUTE_STORE__']
146
- delete g['__cerRouter']
147
- })
148
-
149
- it('returns a default route object', () => {
150
- const route = useRoute()
151
- expect(route.path).toBe('/')
152
- expect(route.params).toEqual({})
153
- expect(route.query).toEqual({})
154
- expect(route.meta).toBeNull()
155
- })
156
- })
@@ -1,182 +0,0 @@
1
- import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
2
- import { useRuntimeConfig, initRuntimeConfig, resolvePrivateConfig } from '../../runtime/composables/use-runtime-config.js'
3
-
4
- beforeEach(() => {
5
- // Reset global state between tests
6
- delete (globalThis as Record<string, unknown>).__cerRuntimeConfig
7
- // Ensure window is not set (server context for most tests)
8
- delete (globalThis as Record<string, unknown>).window
9
- })
10
-
11
- afterEach(() => {
12
- delete (globalThis as Record<string, unknown>).window
13
- })
14
-
15
- describe('initRuntimeConfig', () => {
16
- it('stores the config on globalThis', () => {
17
- initRuntimeConfig({ public: { apiBase: '/api' } })
18
- expect((globalThis as Record<string, unknown>).__cerRuntimeConfig).toEqual({ public: { apiBase: '/api' } })
19
- })
20
-
21
- it('overwrites a previous config', () => {
22
- initRuntimeConfig({ public: { apiBase: '/v1' } })
23
- initRuntimeConfig({ public: { apiBase: '/v2' } })
24
- const stored = (globalThis as Record<string, unknown>).__cerRuntimeConfig as { public: Record<string, unknown> }
25
- expect(stored.public.apiBase).toBe('/v2')
26
- })
27
- })
28
-
29
- describe('useRuntimeConfig', () => {
30
- it('returns empty public config when not initialized', () => {
31
- const config = useRuntimeConfig()
32
- expect(config.public).toEqual({})
33
- })
34
-
35
- it('returns the config set by initRuntimeConfig', () => {
36
- initRuntimeConfig({ public: { apiBase: '/api' } })
37
- const config = useRuntimeConfig()
38
- expect(config.public.apiBase).toBe('/api')
39
- })
40
-
41
- it('returns the full public config object', () => {
42
- initRuntimeConfig({ public: { apiBase: '/api', version: '1.0', debug: false } })
43
- const config = useRuntimeConfig()
44
- expect(config.public).toEqual({ apiBase: '/api', version: '1.0', debug: false })
45
- })
46
-
47
- it('returns a reference to the stored config (not a copy)', () => {
48
- const stored = { public: { apiBase: '/api' } }
49
- initRuntimeConfig(stored)
50
- const config = useRuntimeConfig()
51
- expect(config).toBe(stored)
52
- })
53
-
54
- it('reflects updates when initRuntimeConfig is called again', () => {
55
- initRuntimeConfig({ public: { key: 'first' } })
56
- initRuntimeConfig({ public: { key: 'second' } })
57
- expect(useRuntimeConfig().public.key).toBe('second')
58
- })
59
-
60
- it('handles empty public config', () => {
61
- initRuntimeConfig({ public: {} })
62
- const config = useRuntimeConfig()
63
- expect(config.public).toEqual({})
64
- })
65
-
66
- it('returns private config when initialized with it', () => {
67
- initRuntimeConfig({ public: {}, private: { dbUrl: 'postgres://localhost', secretKey: 'abc' } })
68
- const config = useRuntimeConfig()
69
- expect(config.private).toEqual({ dbUrl: 'postgres://localhost', secretKey: 'abc' })
70
- })
71
-
72
- it('private is undefined when not supplied', () => {
73
- initRuntimeConfig({ public: { apiBase: '/api' } })
74
- const config = useRuntimeConfig()
75
- expect(config.private).toBeUndefined()
76
- })
77
-
78
- it('returns empty private config when initialized with empty object', () => {
79
- initRuntimeConfig({ public: {}, private: {} })
80
- expect(useRuntimeConfig().private).toEqual({})
81
- })
82
- })
83
-
84
- // ─── Browser Proxy (private config enforcement) ───────────────────────────────
85
-
86
- describe('useRuntimeConfig browser Proxy', () => {
87
- it('throws a clear error when .private is accessed in a browser context', () => {
88
- ;(globalThis as Record<string, unknown>).window = {}
89
- initRuntimeConfig({ public: { apiBase: '/api' }, private: { secret: 'shh' } })
90
- const config = useRuntimeConfig()
91
- expect(() => config.private).toThrow('[cer-app] runtimeConfig.private is not available in the browser')
92
- })
93
-
94
- it('allows .public access in a browser context', () => {
95
- ;(globalThis as Record<string, unknown>).window = {}
96
- initRuntimeConfig({ public: { apiBase: '/api' }, private: { secret: 'shh' } })
97
- const config = useRuntimeConfig()
98
- expect(config.public).toEqual({ apiBase: '/api' })
99
- })
100
-
101
- it('does NOT throw when .private is accessed in a server (non-browser) context', () => {
102
- // window is not set — server environment
103
- initRuntimeConfig({ public: {}, private: { secret: 'shh' } })
104
- const config = useRuntimeConfig()
105
- expect(() => config.private).not.toThrow()
106
- expect(config.private).toEqual({ secret: 'shh' })
107
- })
108
-
109
- it('error message instructs moving access to a loader or middleware', () => {
110
- ;(globalThis as Record<string, unknown>).window = {}
111
- initRuntimeConfig({ public: {} })
112
- const config = useRuntimeConfig()
113
- expect(() => config.private).toThrow('loader, middleware, or API handler')
114
- })
115
- })
116
-
117
- // ─── resolvePrivateConfig ─────────────────────────────────────────────────────
118
-
119
- describe('resolvePrivateConfig', () => {
120
- it('resolves a key from the exact-case env var', () => {
121
- const result = resolvePrivateConfig({ dbUrl: '' }, { dbUrl: 'postgres://localhost' })
122
- expect(result.dbUrl).toBe('postgres://localhost')
123
- })
124
-
125
- it('resolves a key from the ALL_CAPS env var when exact case is absent', () => {
126
- const result = resolvePrivateConfig({ dbUrl: '' }, { DB_URL: 'postgres://prod' })
127
- expect(result.dbUrl).toBe('postgres://prod')
128
- })
129
-
130
- it('falls back to the declared default when neither env var is set', () => {
131
- const result = resolvePrivateConfig({ dbUrl: 'default-db' }, {})
132
- expect(result.dbUrl).toBe('default-db')
133
- })
134
-
135
- it('exact-case env var takes precedence over ALL_CAPS', () => {
136
- const result = resolvePrivateConfig({ dbUrl: '' }, { dbUrl: 'exact', DB_URL: 'caps' })
137
- expect(result.dbUrl).toBe('exact')
138
- })
139
-
140
- it('handles multiple keys independently', () => {
141
- const result = resolvePrivateConfig(
142
- { dbUrl: '', secretKey: '', apiToken: 'default-token' },
143
- { dbUrl: 'pg://host', SECRET_KEY: 's3cr3t' },
144
- )
145
- expect(result.dbUrl).toBe('pg://host')
146
- expect(result.secretKey).toBe('s3cr3t')
147
- expect(result.apiToken).toBe('default-token')
148
- })
149
-
150
- it('returns an empty object when defaults is empty', () => {
151
- expect(resolvePrivateConfig({}, { ANY: 'value' })).toEqual({})
152
- })
153
-
154
- it('preserves key names exactly as declared in output (does not rename keys)', () => {
155
- const result = resolvePrivateConfig({ camelCase: 'def' }, { CAMEL_CASE: 'val' })
156
- expect(Object.keys(result)).toEqual(['camelCase'])
157
- expect(result.camelCase).toBe('val')
158
- })
159
-
160
- it('emits a console.warn when a key has an empty-string default and no env var is set', () => {
161
- const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
162
- resolvePrivateConfig({ dbUrl: '' }, {})
163
- expect(warn).toHaveBeenCalledOnce()
164
- expect(warn.mock.calls[0][0]).toContain('dbUrl')
165
- expect(warn.mock.calls[0][0]).toContain('DB_URL')
166
- warn.mockRestore()
167
- })
168
-
169
- it('does NOT warn when the env var supplies a value for an empty-default key', () => {
170
- const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
171
- resolvePrivateConfig({ dbUrl: '' }, { DB_URL: 'postgres://prod' })
172
- expect(warn).not.toHaveBeenCalled()
173
- warn.mockRestore()
174
- })
175
-
176
- it('does NOT warn when the declared default is non-empty and no env var is set', () => {
177
- const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
178
- resolvePrivateConfig({ apiToken: 'fallback-token' }, {})
179
- expect(warn).not.toHaveBeenCalled()
180
- warn.mockRestore()
181
- })
182
- })