create-jen-app 1.2.3 → 1.2.4

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 (355) hide show
  1. package/dist/colors.js +0 -17
  2. package/dist/create.js +5 -17
  3. package/dist/generator.js +14 -31
  4. package/dist/index.js +6 -1
  5. package/package.json +1 -1
  6. package/templates/ssr-isr/README.md +77 -0
  7. package/templates/ssr-isr/build.js +118 -0
  8. package/templates/ssr-isr/jen.config.ts +109 -0
  9. package/templates/ssr-isr/jenjs.d.ts +22 -0
  10. package/templates/ssr-isr/lib/api/(hello).js +9 -0
  11. package/templates/ssr-isr/lib/auth/cookie-utils.js +79 -0
  12. package/templates/ssr-isr/lib/auth/index.js +2 -0
  13. package/templates/ssr-isr/lib/auth/jwt.js +57 -0
  14. package/templates/ssr-isr/lib/auth/session.js +92 -0
  15. package/templates/ssr-isr/lib/build/asset-hashing.d.ts +10 -0
  16. package/templates/ssr-isr/lib/build/asset-hashing.js +25 -0
  17. package/templates/ssr-isr/lib/build/asset-manifest.d.ts +11 -0
  18. package/templates/ssr-isr/lib/build/asset-manifest.js +21 -0
  19. package/templates/{static → ssr-isr}/lib/build/build.d.ts +1 -1
  20. package/templates/ssr-isr/lib/build/build.js +141 -0
  21. package/templates/{static → ssr-isr}/lib/build/island-hydration.d.ts +8 -5
  22. package/templates/ssr-isr/lib/build/island-hydration.js +44 -0
  23. package/templates/ssr-isr/lib/build/minifier.d.ts +20 -0
  24. package/templates/ssr-isr/lib/build/minifier.js +46 -0
  25. package/templates/ssr-isr/lib/build/page-renderer.d.ts +17 -0
  26. package/templates/ssr-isr/lib/build/page-renderer.js +28 -0
  27. package/templates/ssr-isr/lib/build/production-build.d.ts +10 -0
  28. package/templates/ssr-isr/lib/build/production-build.js +13 -0
  29. package/templates/ssr-isr/lib/build/ssg-pipeline.d.ts +15 -0
  30. package/templates/ssr-isr/lib/build/ssg-pipeline.js +113 -0
  31. package/templates/ssr-isr/lib/build-tools/build-site.js +36 -0
  32. package/templates/ssr-isr/lib/cache/index.js +10 -0
  33. package/templates/ssr-isr/lib/cache/memory.js +40 -0
  34. package/templates/ssr-isr/lib/cache/redis.js +61 -0
  35. package/templates/ssr-isr/lib/cli/banner.js +28 -0
  36. package/templates/ssr-isr/lib/cli/templates/ssg/jen.config.js +32 -0
  37. package/templates/ssr-isr/lib/cli/templates/ssg/site/index.js +9 -0
  38. package/templates/ssr-isr/lib/cli/templates/ssr/jen.config.js +32 -0
  39. package/templates/ssr-isr/lib/cli/templates/ssr/site/index.js +9 -0
  40. package/templates/ssr-isr/lib/compilers/esbuild-plugins.js +111 -0
  41. package/templates/ssr-isr/lib/compilers/svelte.js +44 -0
  42. package/templates/ssr-isr/lib/compilers/vue.js +90 -0
  43. package/templates/ssr-isr/lib/core/http.js +71 -0
  44. package/templates/ssr-isr/lib/core/middleware-hooks.js +97 -0
  45. package/templates/ssr-isr/lib/core/paths.js +39 -0
  46. package/templates/ssr-isr/lib/core/routes/match.js +47 -0
  47. package/templates/ssr-isr/lib/core/routes/scan.js +190 -0
  48. package/templates/ssr-isr/lib/core/types.js +1 -0
  49. package/templates/ssr-isr/lib/css/compiler.js +74 -0
  50. package/templates/ssr-isr/lib/db/connector.js +42 -0
  51. package/templates/ssr-isr/lib/db/drivers/jdb.js +44 -0
  52. package/templates/ssr-isr/lib/db/drivers/sql.js +182 -0
  53. package/templates/ssr-isr/lib/db/index.js +48 -0
  54. package/templates/ssr-isr/lib/db/types.js +1 -0
  55. package/templates/ssr-isr/lib/graphql/index.js +52 -0
  56. package/templates/ssr-isr/lib/graphql/resolvers.js +25 -0
  57. package/templates/ssr-isr/lib/graphql/schema.js +35 -0
  58. package/templates/ssr-isr/lib/i18n/en.json +4 -0
  59. package/templates/ssr-isr/lib/i18n/es.json +4 -0
  60. package/templates/ssr-isr/lib/i18n/index.js +15 -0
  61. package/templates/ssr-isr/lib/import/jen-import.js +161 -0
  62. package/templates/ssr-isr/lib/index.js +116 -0
  63. package/templates/ssr-isr/lib/jdb/engine.js +275 -0
  64. package/templates/ssr-isr/lib/jdb/index.js +34 -0
  65. package/templates/ssr-isr/lib/jdb/types.js +1 -0
  66. package/templates/ssr-isr/lib/jdb/utils.js +176 -0
  67. package/templates/{static → ssr-isr}/lib/middleware/builtins/body-parser.js +0 -17
  68. package/templates/ssr-isr/lib/middleware/builtins/cors.js +54 -0
  69. package/templates/{static → ssr-isr}/lib/middleware/builtins/logger.js +0 -17
  70. package/templates/{static → ssr-isr}/lib/middleware/builtins/rate-limit.js +0 -17
  71. package/templates/{static → ssr-isr}/lib/middleware/builtins/request-id.js +0 -17
  72. package/templates/{static → ssr-isr}/lib/middleware/builtins/security-headers.js +0 -17
  73. package/templates/ssr-isr/lib/middleware/context.js +124 -0
  74. package/templates/{static → ssr-isr}/lib/middleware/decorators.js +0 -17
  75. package/templates/{static → ssr-isr}/lib/middleware/errors/handler.js +0 -17
  76. package/templates/ssr-isr/lib/middleware/errors/http-error.js +10 -0
  77. package/templates/ssr-isr/lib/middleware/kernel.js +85 -0
  78. package/templates/ssr-isr/lib/middleware/pipeline.js +148 -0
  79. package/templates/ssr-isr/lib/middleware/registry.js +85 -0
  80. package/templates/ssr-isr/lib/middleware/response.js +107 -0
  81. package/templates/ssr-isr/lib/middleware/types.d.ts +1 -0
  82. package/templates/ssr-isr/lib/middleware/types.js +1 -0
  83. package/templates/ssr-isr/lib/middleware/utils/matcher.js +13 -0
  84. package/templates/{static → ssr-isr}/lib/native/bundle.js +0 -17
  85. package/templates/{static → ssr-isr}/lib/native/dev-server.js +0 -17
  86. package/templates/{static → ssr-isr}/lib/native/index.js +0 -17
  87. package/templates/{static → ssr-isr}/lib/native/optimizer.js +0 -17
  88. package/templates/ssr-isr/lib/native/style-compiler.js +19 -0
  89. package/templates/ssr-isr/lib/plugin/loader.js +36 -0
  90. package/templates/ssr-isr/lib/runtime/client-runtime.js +25 -0
  91. package/templates/ssr-isr/lib/runtime/hmr.js +59 -0
  92. package/templates/ssr-isr/lib/runtime/hydrate.js +55 -0
  93. package/templates/ssr-isr/lib/runtime/island-hydration-client.js +146 -0
  94. package/templates/ssr-isr/lib/runtime/islands.js +110 -0
  95. package/templates/ssr-isr/lib/runtime/render.js +244 -0
  96. package/templates/ssr-isr/lib/server/api-routes.js +237 -0
  97. package/templates/ssr-isr/lib/server/api.js +108 -0
  98. package/templates/ssr-isr/lib/server/app.js +438 -0
  99. package/templates/ssr-isr/lib/server/runtimeServe.js +169 -0
  100. package/templates/ssr-isr/lib/server/ssr.js +202 -0
  101. package/templates/ssr-isr/lib/shared/log.js +64 -0
  102. package/templates/ssr-isr/package.json +23 -0
  103. package/templates/ssr-isr/server.js +128 -0
  104. package/templates/ssr-isr/site/pages/(index).tsx +11 -0
  105. package/templates/ssr-isr/site/styles/global.scss +37 -0
  106. package/templates/ssr-isr/tsconfig.json +39 -0
  107. package/templates/static/build.js +30 -18
  108. package/templates/static/jen.config.ts +0 -18
  109. package/templates/static/jenjs.d.ts +0 -18
  110. package/templates/static/lib/api/(hello).js +0 -17
  111. package/templates/static/lib/api/examples/files/[...slug].js +22 -0
  112. package/templates/static/lib/api/examples/hello.js +11 -0
  113. package/templates/static/lib/api/examples/posts/[id].js +37 -0
  114. package/templates/static/lib/api/examples/posts.js +37 -0
  115. package/templates/static/lib/api/examples/search.js +23 -0
  116. package/templates/static/lib/api/index.js +41 -0
  117. package/templates/static/lib/api/loader.js +234 -0
  118. package/templates/static/lib/api/router.js +259 -0
  119. package/templates/static/lib/assets/types.js +1 -0
  120. package/templates/static/lib/auth/cookie-utils.js +3 -16
  121. package/templates/static/lib/auth/index.js +0 -17
  122. package/templates/static/lib/auth/jwt.js +0 -17
  123. package/templates/static/lib/auth/session.js +0 -17
  124. package/templates/static/lib/build/asset-hashing.js +44 -36
  125. package/templates/static/lib/build/asset-manifest.js +16 -33
  126. package/templates/static/lib/build/build.js +270 -125
  127. package/templates/static/lib/build/bundle-analyzer-ui.js +417 -0
  128. package/templates/static/lib/build/bundle-analyzer.js +945 -0
  129. package/templates/static/lib/build/code-splitter.js +194 -0
  130. package/templates/static/lib/build/feature-analyzer.js +190 -0
  131. package/templates/static/lib/build/feature-gate.js +257 -0
  132. package/templates/static/lib/build/island-hydration.js +17 -35
  133. package/templates/static/lib/build/lazy-loader.js +322 -0
  134. package/templates/static/lib/build/minifier.js +40 -59
  135. package/templates/static/lib/build/page-renderer.js +23 -40
  136. package/templates/static/lib/build/production-build.js +9 -26
  137. package/templates/static/lib/build/rust-hashing.js +71 -0
  138. package/templates/static/lib/build/script-optimizer.js +285 -0
  139. package/templates/static/lib/build/ssg-pipeline.js +100 -106
  140. package/templates/static/lib/build/vercel-output.js +298 -0
  141. package/templates/static/lib/build-tools/build-site.js +0 -17
  142. package/templates/static/lib/cache/index.js +0 -17
  143. package/templates/static/lib/cache/memory.js +0 -17
  144. package/templates/static/lib/cache/redis.js +0 -17
  145. package/templates/static/lib/cli/banner.js +0 -17
  146. package/templates/static/lib/cli/templates/ssg/jen.config.js +0 -17
  147. package/templates/static/lib/cli/templates/ssr/jen.config.js +0 -17
  148. package/templates/static/lib/client/Image.js +42 -0
  149. package/templates/static/lib/client/Link.js +190 -0
  150. package/templates/static/lib/client/PWA.js +46 -0
  151. package/templates/static/lib/client/Seo.js +97 -0
  152. package/templates/static/lib/client/index.js +9 -0
  153. package/templates/static/lib/client/useNavigation.js +25 -0
  154. package/templates/static/lib/client/useRouter.js +64 -0
  155. package/templates/static/lib/client-routing/Link.js +17 -0
  156. package/templates/static/lib/client-routing/index.js +19 -0
  157. package/templates/static/lib/client-routing/router.js +151 -0
  158. package/templates/static/lib/client-routing/signal.js +147 -0
  159. package/templates/static/lib/compilers/esbuild-plugins.js +0 -17
  160. package/templates/static/lib/compilers/svelte.js +0 -17
  161. package/templates/static/lib/compilers/vue.js +0 -17
  162. package/templates/static/lib/core/config.js +0 -17
  163. package/templates/static/lib/core/feature-guard.js +136 -0
  164. package/templates/static/lib/core/features.js +99 -0
  165. package/templates/static/lib/core/http.js +0 -17
  166. package/templates/static/lib/core/layouts/index.js +10 -0
  167. package/templates/static/lib/core/layouts/render.js +158 -0
  168. package/templates/static/lib/core/layouts/scan.js +112 -0
  169. package/templates/static/lib/core/layouts/types.js +1 -0
  170. package/templates/static/lib/core/lifecycle.js +129 -0
  171. package/templates/static/lib/core/loader-schema.js +81 -0
  172. package/templates/static/lib/core/middleware-hooks.js +0 -17
  173. package/templates/static/lib/core/paths.js +0 -17
  174. package/templates/static/lib/core/routes/advanced.js +114 -0
  175. package/templates/static/lib/core/routes/handlers.js +181 -0
  176. package/templates/static/lib/core/routes/match.js +89 -17
  177. package/templates/static/lib/core/routes/orchestrator.js +171 -0
  178. package/templates/static/lib/core/routes/rendering-config.js +131 -0
  179. package/templates/static/lib/core/routes/scan.js +0 -17
  180. package/templates/static/lib/core/types.js +0 -17
  181. package/templates/static/lib/css/compiler.js +1 -18
  182. package/templates/static/lib/data-fetching/cache.js +223 -0
  183. package/templates/static/lib/data-fetching/client.js +202 -0
  184. package/templates/static/lib/data-fetching/feature-guard.js +29 -0
  185. package/templates/static/lib/data-fetching/graphql.js +265 -0
  186. package/templates/static/lib/data-fetching/index.js +57 -0
  187. package/templates/static/lib/data-fetching/rest.js +256 -0
  188. package/templates/static/lib/data-fetching/server.js +182 -0
  189. package/templates/static/lib/data-fetching/types.js +5 -0
  190. package/templates/static/lib/db/connector.js +0 -17
  191. package/templates/static/lib/db/drivers/jdb.js +0 -17
  192. package/templates/static/lib/db/drivers/sql.js +0 -17
  193. package/templates/static/lib/db/index.js +0 -17
  194. package/templates/static/lib/db/types.js +0 -17
  195. package/templates/static/lib/devtools/component-tree.js +106 -0
  196. package/templates/static/lib/devtools/devtools.js +638 -0
  197. package/templates/static/lib/devtools/event-bus.js +29 -0
  198. package/templates/static/lib/devtools/event-logger.js +67 -0
  199. package/templates/static/lib/devtools/index.js +9 -0
  200. package/templates/static/lib/devtools/integration.js +149 -0
  201. package/templates/static/lib/devtools/performance.js +84 -0
  202. package/templates/static/lib/devtools/persistence.js +57 -0
  203. package/templates/static/lib/devtools/plugins.js +97 -0
  204. package/templates/static/lib/devtools/search.js +89 -0
  205. package/templates/static/lib/devtools/ui.js +769 -0
  206. package/templates/static/lib/features/api/handler.js +10 -0
  207. package/templates/static/lib/features/api/index.js +5 -0
  208. package/templates/static/lib/features/api/types.js +4 -0
  209. package/templates/static/lib/features/middleware/compiled.js +7 -0
  210. package/templates/static/lib/features/middleware/index.js +5 -0
  211. package/templates/static/lib/features/middleware/types.js +4 -0
  212. package/templates/static/lib/fonts/index.js +46 -0
  213. package/templates/static/lib/fonts/inject.js +125 -0
  214. package/templates/static/lib/fonts/loader.js +196 -0
  215. package/templates/static/lib/fonts/types.js +1 -0
  216. package/templates/static/lib/graphql/index.js +1 -18
  217. package/templates/static/lib/graphql/resolvers.js +20 -13
  218. package/templates/static/lib/graphql/schema.js +0 -17
  219. package/templates/static/lib/i18n/index.js +7 -19
  220. package/templates/static/lib/import/jen-import.js +1 -18
  221. package/templates/static/lib/index.js +79 -125
  222. package/templates/static/lib/jdb/engine.js +0 -17
  223. package/templates/static/lib/jdb/index.js +1 -18
  224. package/templates/static/lib/jdb/types.js +0 -17
  225. package/templates/static/lib/jdb/utils.js +0 -17
  226. package/templates/static/lib/middleware/builtins/cors.js +3 -16
  227. package/templates/static/lib/middleware/context.js +0 -17
  228. package/templates/static/lib/middleware/kernel.js +117 -25
  229. package/templates/static/lib/middleware/pipeline.js +0 -17
  230. package/templates/static/lib/middleware/registry.js +0 -17
  231. package/templates/static/lib/middleware/response.js +0 -17
  232. package/templates/static/lib/plugin/examples/analytics-plugin.js +183 -0
  233. package/templates/static/lib/plugin/examples/cdn-upload-plugin.js +94 -0
  234. package/templates/static/lib/plugin/loader.js +0 -17
  235. package/templates/static/lib/plugin/plugin-manager.js +177 -0
  236. package/templates/static/lib/plugin/types.js +28 -0
  237. package/templates/static/lib/runtime/client-runtime.js +0 -17
  238. package/templates/static/lib/runtime/hmr.js +0 -17
  239. package/templates/static/lib/runtime/hydrate.js +0 -17
  240. package/templates/static/lib/runtime/island-hydration-client.js +0 -17
  241. package/templates/static/lib/runtime/islands.js +0 -17
  242. package/templates/static/lib/runtime/render.js +208 -50
  243. package/templates/static/lib/security/security-config.js +60 -0
  244. package/templates/static/lib/security/security-middleware.js +229 -0
  245. package/templates/static/lib/server/api-routes.js +153 -43
  246. package/templates/static/lib/server/api.js +0 -17
  247. package/templates/static/lib/server/app.js +539 -223
  248. package/templates/static/lib/server/isr.js +365 -0
  249. package/templates/static/lib/server/runtimeServe.js +31 -24
  250. package/templates/static/lib/server/ssr.js +98 -22
  251. package/templates/static/lib/server-actions/handler.js +180 -0
  252. package/templates/static/lib/server-actions/index.js +19 -0
  253. package/templates/static/lib/server-actions/middleware.js +146 -0
  254. package/templates/static/lib/server-actions/scan.js +152 -0
  255. package/templates/static/lib/server-actions/types.js +1 -0
  256. package/templates/static/lib/server-actions/validators.js +156 -0
  257. package/templates/static/lib/shared/log.js +19 -20
  258. package/templates/static/lib/telemetry/api/rate-limiter.js +32 -0
  259. package/templates/static/lib/telemetry/api/validator.js +67 -0
  260. package/templates/static/lib/telemetry/client.js +121 -0
  261. package/templates/static/lib/telemetry/tests/rate-limiter.test.js +46 -0
  262. package/templates/static/lib/telemetry/tests/validator.test.js +62 -0
  263. package/templates/static/lib/vendor/glob/glob.js +4766 -0
  264. package/templates/static/lib/vendor/preact/LICENSE +21 -0
  265. package/templates/static/lib/vendor/preact/preact.module.js +797 -0
  266. package/templates/static/lib/vendor/sass/sass.node.mjs +212 -0
  267. package/templates/static/package.json +4 -0
  268. package/templates/static/server.js +22 -22
  269. package/templates/static/site/(home).tsx +0 -18
  270. package/templates/static/tsconfig.json +5 -1
  271. package/templates/static/.esbuild/jen.config.js +0 -19
  272. package/templates/static/lib/build/asset-hashing.d.ts +0 -10
  273. package/templates/static/lib/build/asset-manifest.d.ts +0 -11
  274. package/templates/static/lib/build/minifier.d.ts +0 -20
  275. package/templates/static/lib/build/page-renderer.d.ts +0 -17
  276. package/templates/static/lib/build/production-build.d.ts +0 -10
  277. package/templates/static/lib/build/ssg-pipeline.d.ts +0 -15
  278. package/templates/static/lib/middleware/errors/http-error.js +0 -27
  279. package/templates/static/lib/middleware/types.js +0 -18
  280. package/templates/static/lib/middleware/utils/matcher.js +0 -30
  281. package/templates/static/lib/native/style-compiler.js +0 -36
  282. /package/templates/{static → ssr-isr}/lib/api/(hello).d.ts +0 -0
  283. /package/templates/{static → ssr-isr}/lib/auth/cookie-utils.d.ts +0 -0
  284. /package/templates/{static → ssr-isr}/lib/auth/index.d.ts +0 -0
  285. /package/templates/{static → ssr-isr}/lib/auth/jwt.d.ts +0 -0
  286. /package/templates/{static → ssr-isr}/lib/auth/session.d.ts +0 -0
  287. /package/templates/{static → ssr-isr}/lib/build-tools/build-site.d.ts +0 -0
  288. /package/templates/{static → ssr-isr}/lib/cache/index.d.ts +0 -0
  289. /package/templates/{static → ssr-isr}/lib/cache/memory.d.ts +0 -0
  290. /package/templates/{static → ssr-isr}/lib/cache/redis.d.ts +0 -0
  291. /package/templates/{static → ssr-isr}/lib/cli/banner.d.ts +0 -0
  292. /package/templates/{static → ssr-isr}/lib/cli/templates/ssg/jen.config.d.ts +0 -0
  293. /package/templates/{static → ssr-isr}/lib/cli/templates/ssg/site/index.d.ts +0 -0
  294. /package/templates/{static → ssr-isr}/lib/cli/templates/ssr/jen.config.d.ts +0 -0
  295. /package/templates/{static → ssr-isr}/lib/cli/templates/ssr/site/index.d.ts +0 -0
  296. /package/templates/{static → ssr-isr}/lib/compilers/esbuild-plugins.d.ts +0 -0
  297. /package/templates/{static → ssr-isr}/lib/compilers/svelte.d.ts +0 -0
  298. /package/templates/{static → ssr-isr}/lib/compilers/vue.d.ts +0 -0
  299. /package/templates/{static → ssr-isr}/lib/core/config.d.ts +0 -0
  300. /package/templates/{static/lib/middleware/types.d.ts → ssr-isr/lib/core/config.js} +0 -0
  301. /package/templates/{static → ssr-isr}/lib/core/http.d.ts +0 -0
  302. /package/templates/{static → ssr-isr}/lib/core/middleware-hooks.d.ts +0 -0
  303. /package/templates/{static → ssr-isr}/lib/core/paths.d.ts +0 -0
  304. /package/templates/{static → ssr-isr}/lib/core/routes/match.d.ts +0 -0
  305. /package/templates/{static → ssr-isr}/lib/core/routes/scan.d.ts +0 -0
  306. /package/templates/{static → ssr-isr}/lib/core/types.d.ts +0 -0
  307. /package/templates/{static → ssr-isr}/lib/css/compiler.d.ts +0 -0
  308. /package/templates/{static → ssr-isr}/lib/db/connector.d.ts +0 -0
  309. /package/templates/{static → ssr-isr}/lib/db/drivers/jdb.d.ts +0 -0
  310. /package/templates/{static → ssr-isr}/lib/db/drivers/sql.d.ts +0 -0
  311. /package/templates/{static → ssr-isr}/lib/db/index.d.ts +0 -0
  312. /package/templates/{static → ssr-isr}/lib/db/types.d.ts +0 -0
  313. /package/templates/{static → ssr-isr}/lib/graphql/index.d.ts +0 -0
  314. /package/templates/{static → ssr-isr}/lib/graphql/resolvers.d.ts +0 -0
  315. /package/templates/{static → ssr-isr}/lib/graphql/schema.d.ts +0 -0
  316. /package/templates/{static → ssr-isr}/lib/i18n/index.d.ts +0 -0
  317. /package/templates/{static → ssr-isr}/lib/import/jen-import.d.ts +0 -0
  318. /package/templates/{static → ssr-isr}/lib/index.d.ts +0 -0
  319. /package/templates/{static → ssr-isr}/lib/jdb/engine.d.ts +0 -0
  320. /package/templates/{static → ssr-isr}/lib/jdb/index.d.ts +0 -0
  321. /package/templates/{static → ssr-isr}/lib/jdb/types.d.ts +0 -0
  322. /package/templates/{static → ssr-isr}/lib/jdb/utils.d.ts +0 -0
  323. /package/templates/{static → ssr-isr}/lib/middleware/builtins/body-parser.d.ts +0 -0
  324. /package/templates/{static → ssr-isr}/lib/middleware/builtins/cors.d.ts +0 -0
  325. /package/templates/{static → ssr-isr}/lib/middleware/builtins/logger.d.ts +0 -0
  326. /package/templates/{static → ssr-isr}/lib/middleware/builtins/rate-limit.d.ts +0 -0
  327. /package/templates/{static → ssr-isr}/lib/middleware/builtins/request-id.d.ts +0 -0
  328. /package/templates/{static → ssr-isr}/lib/middleware/builtins/security-headers.d.ts +0 -0
  329. /package/templates/{static → ssr-isr}/lib/middleware/context.d.ts +0 -0
  330. /package/templates/{static → ssr-isr}/lib/middleware/decorators.d.ts +0 -0
  331. /package/templates/{static → ssr-isr}/lib/middleware/errors/handler.d.ts +0 -0
  332. /package/templates/{static → ssr-isr}/lib/middleware/errors/http-error.d.ts +0 -0
  333. /package/templates/{static → ssr-isr}/lib/middleware/kernel.d.ts +0 -0
  334. /package/templates/{static → ssr-isr}/lib/middleware/pipeline.d.ts +0 -0
  335. /package/templates/{static → ssr-isr}/lib/middleware/registry.d.ts +0 -0
  336. /package/templates/{static → ssr-isr}/lib/middleware/response.d.ts +0 -0
  337. /package/templates/{static → ssr-isr}/lib/middleware/utils/matcher.d.ts +0 -0
  338. /package/templates/{static → ssr-isr}/lib/native/bundle.d.ts +0 -0
  339. /package/templates/{static → ssr-isr}/lib/native/dev-server.d.ts +0 -0
  340. /package/templates/{static → ssr-isr}/lib/native/index.d.ts +0 -0
  341. /package/templates/{static → ssr-isr}/lib/native/optimizer.d.ts +0 -0
  342. /package/templates/{static → ssr-isr}/lib/native/style-compiler.d.ts +0 -0
  343. /package/templates/{static → ssr-isr}/lib/plugin/loader.d.ts +0 -0
  344. /package/templates/{static → ssr-isr}/lib/runtime/client-runtime.d.ts +0 -0
  345. /package/templates/{static → ssr-isr}/lib/runtime/hmr.d.ts +0 -0
  346. /package/templates/{static → ssr-isr}/lib/runtime/hydrate.d.ts +0 -0
  347. /package/templates/{static → ssr-isr}/lib/runtime/island-hydration-client.d.ts +0 -0
  348. /package/templates/{static → ssr-isr}/lib/runtime/islands.d.ts +0 -0
  349. /package/templates/{static → ssr-isr}/lib/runtime/render.d.ts +0 -0
  350. /package/templates/{static → ssr-isr}/lib/server/api-routes.d.ts +0 -0
  351. /package/templates/{static → ssr-isr}/lib/server/api.d.ts +0 -0
  352. /package/templates/{static → ssr-isr}/lib/server/app.d.ts +0 -0
  353. /package/templates/{static → ssr-isr}/lib/server/runtimeServe.d.ts +0 -0
  354. /package/templates/{static → ssr-isr}/lib/server/ssr.d.ts +0 -0
  355. /package/templates/{static → ssr-isr}/lib/shared/log.d.ts +0 -0
@@ -1,33 +1,14 @@
1
- /*
2
- * This file is part of Jen.js.
3
- * Copyright (C) 2026 oopsio
4
- *
5
- * This program is free software: you can redistribute it and/or modify
6
- * it under the terms of the GNU General Public License as published by
7
- * the Free Software Foundation, either version 3 of the License, or
8
- * (at your option) any later version.
9
- *
10
- * This program is distributed in the hope that it will be useful,
11
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
- * GNU General Public License for more details.
14
- *
15
- * You should have received a copy of the GNU General Public License
16
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
- */
18
1
  import { existsSync, watch } from "node:fs";
19
- import { join, extname } from "node:path";
20
- import sirv from "sirv";
2
+ import { join, extname, resolve } from "node:path";
21
3
  import { createScssCompiler } from "../css/compiler.js";
22
4
  import { scanRoutes } from "../core/routes/scan.js";
23
- import { matchRoute } from "../core/routes/match.js";
5
+ import { createAdvancedRouter } from "../core/routes/orchestrator.js";
24
6
  import { log } from "../shared/log.js";
25
7
  import { Kernel } from "../middleware/kernel.js";
26
8
  import { renderRouteToHtml } from "../runtime/render.js";
27
9
  import { HMR_CLIENT_SCRIPT } from "../runtime/hmr.js";
28
10
  import { headersToObject, parseCookies } from "../core/http.js";
29
11
  import { tryHandleApiRoute } from "./api-routes.js";
30
- import { resolve } from "node:path";
31
12
  import {
32
13
  buildHydrationModule,
33
14
  runtimeHydrateModule,
@@ -37,6 +18,177 @@ import {
37
18
  invalidateVueCache,
38
19
  invalidateSvelteCache,
39
20
  } from "../compilers/esbuild-plugins.js";
21
+ import { fontServeMiddleware } from "../fonts/inject.js";
22
+ import { createServerActionsMiddleware } from "../server-actions/middleware.js";
23
+ import { runQuery } from "../graphql/index.js";
24
+ import { I18n } from "../i18n/index.js";
25
+ import sirv from "sirv";
26
+ /**
27
+ * Manages the lifecycle of file watchers and HMR connections.
28
+ * Ensures proper cleanup on shutdown to prevent memory leaks.
29
+ */
30
+ class AppLifecycle {
31
+ watcher = null;
32
+ debounceTimer = null;
33
+ hmrClients = new Set();
34
+ setWatcher(watcher) {
35
+ this.watcher = watcher;
36
+ }
37
+ setDebounceTimer(timer) {
38
+ this.debounceTimer = timer;
39
+ }
40
+ addHmrClient(client) {
41
+ this.hmrClients.add(client);
42
+ }
43
+ removeHmrClient(client) {
44
+ this.hmrClients.delete(client);
45
+ }
46
+ getHmrClients() {
47
+ return this.hmrClients;
48
+ }
49
+ /**
50
+ * Properly closes all resources.
51
+ * Called on server shutdown.
52
+ */
53
+ async close() {
54
+ // Clear debounce timer
55
+ if (this.debounceTimer) {
56
+ clearTimeout(this.debounceTimer);
57
+ this.debounceTimer = null;
58
+ }
59
+ // Close all HMR connections
60
+ for (const client of this.hmrClients) {
61
+ if (!client.writableEnded && !client.destroyed) {
62
+ client.end();
63
+ }
64
+ }
65
+ this.hmrClients.clear();
66
+ // Close file watcher
67
+ if (this.watcher) {
68
+ return new Promise((resolve) => {
69
+ const w = this.watcher;
70
+ if (w) {
71
+ w.close();
72
+ this.watcher = null;
73
+ }
74
+ resolve();
75
+ });
76
+ }
77
+ }
78
+ }
79
+ /**
80
+ * HTML template for 500 Internal Server Error responses.
81
+ * Used when middleware or request handlers throw uncaught exceptions.
82
+ */
83
+ const ERROR_500_TEMPLATE = `<!DOCTYPE html>
84
+ <html lang="en">
85
+ <head>
86
+ <meta charset="UTF-8">
87
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
88
+ <title>500 - Internal Server Error</title>
89
+ <style>
90
+ body {
91
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
92
+ background: #f5f5f5;
93
+ margin: 0;
94
+ padding: 20px;
95
+ }
96
+ .container {
97
+ max-width: 600px;
98
+ margin: 60px auto;
99
+ background: white;
100
+ padding: 40px;
101
+ border-radius: 8px;
102
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
103
+ }
104
+ h1 {
105
+ color: #d32f2f;
106
+ margin: 0 0 20px 0;
107
+ font-size: 32px;
108
+ }
109
+ p {
110
+ color: #666;
111
+ line-height: 1.6;
112
+ margin: 10px 0;
113
+ }
114
+ .error-details {
115
+ background: #fafafa;
116
+ border-left: 4px solid #d32f2f;
117
+ padding: 15px;
118
+ margin: 20px 0;
119
+ font-family: monospace;
120
+ font-size: 12px;
121
+ color: #333;
122
+ overflow-x: auto;
123
+ white-space: pre-wrap;
124
+ word-break: break-word;
125
+ }
126
+ </style>
127
+ </head>
128
+ <body>
129
+ <div class="container">
130
+ <h1>500 - Internal Server Error</h1>
131
+ <p>The server encountered an unexpected error while processing your request.</p>
132
+ <p>Our team has been notified. Please try again later.</p>
133
+ <div class="error-details" id="details" style="display:none;"></div>
134
+ </div>
135
+ </body>
136
+ </html>`;
137
+ /**
138
+ * Sends a safe 500 error response, checking if headers have already been sent.
139
+ * If headers were sent, attempts to destroy the socket to prevent further data transmission.
140
+ * Logs the error with stack trace for debugging.
141
+ *
142
+ * @param res Node.js ServerResponse object
143
+ * @param error Error object or string to log
144
+ * @param showDetails Whether to include error details in response (dev mode)
145
+ */
146
+ function sendSafeError(res, error, showDetails = false) {
147
+ const errorMsg = error instanceof Error ? error.message : String(error);
148
+ const errorStack = error instanceof Error ? error.stack : "";
149
+ // Log error with full stack trace
150
+ log.error(`[Error] ${errorMsg}`);
151
+ if (errorStack) {
152
+ log.error(`Stack:\n${errorStack}`);
153
+ }
154
+ // If headers already sent, destroy socket to prevent data corruption
155
+ if (res.headersSent) {
156
+ log.error("[Error Response] Headers already sent, destroying socket");
157
+ if (res.socket && !res.socket.destroyed) {
158
+ res.socket.destroy();
159
+ }
160
+ return;
161
+ }
162
+ // Send 500 response with safe error template
163
+ try {
164
+ let html = ERROR_500_TEMPLATE;
165
+ if (showDetails && errorStack) {
166
+ // In dev mode, include error details
167
+ html = html.replace('id="details" style="display:none;"', 'id="details"');
168
+ html = html.replace(
169
+ "<script>",
170
+ `<script>
171
+ document.getElementById('details').textContent = ${JSON.stringify(errorStack)};
172
+ `,
173
+ );
174
+ }
175
+ res.statusCode = 500;
176
+ res.setHeader("content-type", "text/html; charset=utf-8");
177
+ res.setHeader("cache-control", "no-store, no-cache, must-revalidate");
178
+ res.end(html);
179
+ } catch (e) {
180
+ // If error sending response, just try to end the response
181
+ log.error(`[Error Response] Failed to send error page: ${e}`);
182
+ try {
183
+ res.end();
184
+ } catch {
185
+ // Last resort: destroy socket
186
+ if (res.socket && !res.socket.destroyed) {
187
+ res.socket.destroy();
188
+ }
189
+ }
190
+ }
191
+ }
40
192
  /**
41
193
  * Creates and configures the main HTTP application handler.
42
194
  *
@@ -68,74 +220,86 @@ import {
68
220
  export async function createApp(opts) {
69
221
  const { config, mode, viteServer } = opts;
70
222
  /**
71
- * Set of active Server-Sent Events (SSE) connections for Hot Module Replacement.
72
- * Clients maintain a persistent SSE connection to receive change notifications.
73
- * Each response object in this set receives change events for CSS updates and full reloads.
223
+ * Lifecycle manager for watchers and HMR connections.
224
+ * Ensures proper cleanup on app shutdown.
74
225
  */
75
- const hmrClients = new Set();
226
+ const lifecycle = new AppLifecycle();
76
227
  if (mode === "dev") {
77
228
  const sitePath = join(process.cwd(), config.siteDir);
78
229
  log.info(`[HMR] Watching ${sitePath} for changes...`);
79
- /**
80
- * Debounce timer prevents multiple rapid change notifications.
81
- * Filesystem watchers often emit multiple events for a single file change.
82
- * This delay coalesces rapid changes into a single notification (100ms threshold).
83
- */
84
- let debounceTimer;
85
230
  try {
86
- watch(sitePath, { recursive: true }, (eventType, filename) => {
87
- if (!filename) return;
88
- /**
89
- * Filter out files that should not trigger HMR notifications.
90
- * Temporary files, build artifacts, and hidden files cause infinite loops
91
- * or are not meant for hot reload.
92
- */
93
- if (
94
- filename.startsWith(".") ||
95
- filename.includes("node_modules") ||
96
- filename.endsWith("~") ||
97
- filename.endsWith(".tmp") ||
98
- filename.endsWith(".esbuild.mjs") || // Ignore build artifacts
99
- filename.includes("\\.") || // Windows hidden files
100
- filename.includes("/.") // Unix hidden files
101
- ) {
102
- return;
103
- }
104
- // Debounce to avoid double events
105
- clearTimeout(debounceTimer);
106
- debounceTimer = setTimeout(() => {
107
- const ext = extname(filename);
108
- // Normalize path
109
- const fullPath = join(sitePath, filename);
110
- log.info(`[HMR] Change detected: ${filename}`);
111
- if (ext === ".css" || ext === ".scss") {
112
- /**
113
- * For CSS/SCSS changes, send a style-update event.
114
- * This allows the client to reload CSS without full page reload,
115
- * preserving component state and providing better developer experience.
116
- */
117
- const cssName = filename.replace(/\.scss$/, ".css");
118
- for (const client of hmrClients) {
119
- client.write(
120
- `event: style-update\ndata: ${JSON.stringify({ file: cssName })}\n\n`,
121
- );
122
- }
123
- } else {
124
- /**
125
- * For JavaScript/TypeScript/component changes, invalidate build caches
126
- * and send a full reload event.
127
- * Cache invalidation ensures the latest code is loaded on next request.
128
- */
129
- invalidateCache(fullPath);
130
- if (ext === ".vue") invalidateVueCache(fullPath);
131
- if (ext === ".svelte") invalidateSvelteCache(fullPath);
132
- // Full reload for JS/TS/Vue/Svelte/Other
133
- for (const client of hmrClients) {
134
- client.write(`event: reload\ndata: {}\n\n`);
231
+ const watcher = watch(
232
+ sitePath,
233
+ { recursive: true },
234
+ (eventType, filename) => {
235
+ if (!filename) return;
236
+ /**
237
+ * Filter out files that should not trigger HMR notifications.
238
+ * Temporary files, build artifacts, and hidden files cause infinite loops
239
+ * or are not meant for hot reload.
240
+ */
241
+ if (
242
+ filename.startsWith(".") ||
243
+ filename.includes("node_modules") ||
244
+ filename.endsWith("~") ||
245
+ filename.endsWith(".tmp") ||
246
+ filename.endsWith(".esbuild.mjs") || // Ignore build artifacts
247
+ filename.includes("\\.") || // Windows hidden files
248
+ filename.includes("/.") // Unix hidden files
249
+ ) {
250
+ return;
251
+ }
252
+ /**
253
+ * Debounce timer prevents multiple rapid change notifications.
254
+ * Filesystem watchers often emit multiple events for a single file change.
255
+ * This delay coalesces rapid changes into a single notification (100ms threshold).
256
+ * maxWait cap prevents indefinite waiting on continuous changes.
257
+ */
258
+ let debounceTimer = setTimeout(() => {
259
+ const ext = extname(filename);
260
+ // Normalize path
261
+ const fullPath = join(sitePath, filename);
262
+ log.info(`[HMR] Change detected: ${filename}`);
263
+ if (ext === ".css" || ext === ".scss") {
264
+ /**
265
+ * For CSS/SCSS changes, send a style-update event.
266
+ * This allows the client to reload CSS without full page reload,
267
+ * preserving component state and providing better developer experience.
268
+ */
269
+ const cssName = filename.replace(/\.scss$/, ".css");
270
+ for (const client of lifecycle.getHmrClients()) {
271
+ if (!client.writableEnded && !client.destroyed) {
272
+ client.write(
273
+ `event: style-update\ndata: ${JSON.stringify({ file: cssName })}\n\n`,
274
+ );
275
+ }
276
+ }
277
+ } else {
278
+ /**
279
+ * For JavaScript/TypeScript/component changes, invalidate build caches
280
+ * and send a full reload event.
281
+ * Cache invalidation ensures the latest code is loaded on next request.
282
+ */
283
+ invalidateCache(fullPath);
284
+ if (ext === ".vue") invalidateVueCache(fullPath);
285
+ if (ext === ".svelte") invalidateSvelteCache(fullPath);
286
+ // Full reload for JS/TS/Vue/Svelte/Other
287
+ for (const client of lifecycle.getHmrClients()) {
288
+ if (!client.writableEnded && !client.destroyed) {
289
+ client.write(`event: reload\ndata: {}\n\n`);
290
+ }
291
+ }
135
292
  }
293
+ }, 100);
294
+ // Use unref() to allow Node.js to exit if this is the only pending operation
295
+ if (debounceTimer.unref) {
296
+ debounceTimer.unref();
136
297
  }
137
- }, 100);
138
- });
298
+ lifecycle.setDebounceTimer(debounceTimer);
299
+ },
300
+ );
301
+ // Store watcher for cleanup on shutdown
302
+ lifecycle.setWatcher(watcher);
139
303
  } catch (err) {
140
304
  log.warn(`[HMR] Watch failed: ${err}`);
141
305
  }
@@ -146,6 +310,7 @@ export async function createApp(opts) {
146
310
  * See scanRoutes() for details on route naming conventions.
147
311
  */
148
312
  const routes = scanRoutes(config);
313
+ const router = createAdvancedRouter(routes, config);
149
314
  log.info(`Routes discovered: ${routes.length}`);
150
315
  for (const r of routes) log.info(` ${r.urlPath} -> ${r.filePath}`);
151
316
  const serveAssets = sirv(join(process.cwd(), config.siteDir), {
@@ -156,29 +321,76 @@ export async function createApp(opts) {
156
321
  dev: mode === "dev",
157
322
  etag: true,
158
323
  });
324
+ // Font serving middleware
325
+ const fontCacheDir = join(
326
+ process.cwd(),
327
+ config.build?.cacheDir ?? ".jen",
328
+ "fonts",
329
+ );
330
+ const serveFonts = fontServeMiddleware(fontCacheDir);
331
+ // Server actions middleware
332
+ const serverActionsMiddleware = await createServerActionsMiddleware({
333
+ config,
334
+ });
159
335
  const middlewares = [
160
336
  async (ctx, next) => {
161
- log.info(`${ctx.req.method} ${ctx.url.pathname}`);
162
- await next();
163
- },
164
- async (ctx, next) => {
165
- // runtime internal modules
166
- if (ctx.url.pathname === "/__runtime/hydrate.js") {
167
- ctx.res.statusCode = 200;
168
- ctx.res.setHeader(
169
- "content-type",
170
- "application/javascript; charset=utf-8",
337
+ try {
338
+ log.info(`${ctx.req.method} ${ctx.url.pathname}`);
339
+ // i18n middleware
340
+ if (config.features?.i18n !== false) {
341
+ const locales = config.i18n?.locales || ["en", "es"];
342
+ const defaultLocale = config.i18n?.defaultLocale || "en";
343
+ const firstSegment = ctx.url.pathname.split("/")[1];
344
+ const locale = locales.includes(firstSegment)
345
+ ? firstSegment
346
+ : defaultLocale;
347
+ ctx.i18n = new I18n(locale);
348
+ }
349
+ await next();
350
+ } catch (err) {
351
+ sendSafeError(
352
+ ctx.res,
353
+ err instanceof Error ? err : new Error(String(err)),
354
+ mode === "dev",
171
355
  );
172
- ctx.res.end(runtimeHydrateModule());
173
- return;
174
356
  }
175
- if (ctx.url.pathname === "/__runtime/island-hydration-client.js") {
176
- ctx.res.statusCode = 200;
177
- ctx.res.setHeader(
178
- "content-type",
179
- "application/javascript; charset=utf-8",
180
- );
181
- const islandCode = `
357
+ },
358
+ async (ctx, next) => {
359
+ try {
360
+ // GraphQL endpoint
361
+ if (ctx.url.pathname === "/graphql" && config.features?.graphql) {
362
+ if (ctx.req.method !== "POST") {
363
+ ctx.res.statusCode = 405;
364
+ ctx.res.end("Method Not Allowed");
365
+ return;
366
+ }
367
+ let body = "";
368
+ ctx.req.on("data", (chunk) => (body += chunk.toString()));
369
+ await new Promise((resolve) => ctx.req.on("end", resolve));
370
+ const { query, variables } = JSON.parse(body);
371
+ const result = await runQuery(query, variables);
372
+ ctx.res.statusCode = 200;
373
+ ctx.res.setHeader("content-type", "application/json");
374
+ ctx.res.end(JSON.stringify(result));
375
+ return;
376
+ }
377
+ // runtime internal modules
378
+ if (ctx.url.pathname === "/__runtime/hydrate.js") {
379
+ ctx.res.statusCode = 200;
380
+ ctx.res.setHeader(
381
+ "content-type",
382
+ "application/javascript; charset=utf-8",
383
+ );
384
+ ctx.res.end(runtimeHydrateModule());
385
+ return;
386
+ }
387
+ if (ctx.url.pathname === "/__runtime/island-hydration-client.js") {
388
+ ctx.res.statusCode = 200;
389
+ ctx.res.setHeader(
390
+ "content-type",
391
+ "application/javascript; charset=utf-8",
392
+ );
393
+ const islandCode = `
182
394
  import { hydrate } from "https://esm.sh/preact@10";
183
395
  import { h } from "https://esm.sh/preact@10";
184
396
 
@@ -272,151 +484,239 @@ export function initializeIslands() {
272
484
 
273
485
  initializeIslands();
274
486
  `;
275
- ctx.res.end(islandCode);
276
- return;
277
- }
278
- // HMR Endpoint (SSE)
279
- if (ctx.url.pathname === "/__hmr" && mode === "dev") {
280
- ctx.res.statusCode = 200;
281
- ctx.res.setHeader("content-type", "text/event-stream");
282
- ctx.res.setHeader("cache-control", "no-cache");
283
- ctx.res.setHeader("connection", "keep-alive");
284
- ctx.res.write("data: connected\n\n");
285
- hmrClients.add(ctx.res);
286
- ctx.req.on("close", () => {
287
- hmrClients.delete(ctx.res);
288
- });
289
- return;
290
- }
291
- if (ctx.url.pathname === "/__hydrate") {
292
- const file = ctx.url.searchParams.get("file");
293
- if (!file) {
294
- ctx.res.statusCode = 400;
295
- ctx.res.end("missing file");
487
+ ctx.res.end(islandCode);
296
488
  return;
297
489
  }
298
- const js = buildHydrationModule(file);
299
- ctx.res.statusCode = 200;
300
- ctx.res.setHeader(
301
- "content-type",
302
- "application/javascript; charset=utf-8",
490
+ // HMR Endpoint (SSE)
491
+ if (ctx.url.pathname === "/__hmr" && mode === "dev") {
492
+ ctx.res.statusCode = 200;
493
+ ctx.res.setHeader("content-type", "text/event-stream");
494
+ ctx.res.setHeader("cache-control", "no-cache");
495
+ ctx.res.setHeader("connection", "keep-alive");
496
+ ctx.res.write("data: connected\n\n");
497
+ lifecycle.addHmrClient(ctx.res);
498
+ ctx.req.on("close", () => {
499
+ lifecycle.removeHmrClient(ctx.res);
500
+ });
501
+ // Cleanup on response error
502
+ ctx.res.on("error", () => {
503
+ lifecycle.removeHmrClient(ctx.res);
504
+ });
505
+ return;
506
+ }
507
+ if (ctx.url.pathname === "/__hydrate") {
508
+ const file = ctx.url.searchParams.get("file");
509
+ if (!file) {
510
+ ctx.res.statusCode = 400;
511
+ ctx.res.end("missing file");
512
+ return;
513
+ }
514
+ const js = buildHydrationModule(file);
515
+ ctx.res.statusCode = 200;
516
+ ctx.res.setHeader(
517
+ "content-type",
518
+ "application/javascript; charset=utf-8",
519
+ );
520
+ ctx.res.setHeader("cache-control", "no-store");
521
+ ctx.res.end(js);
522
+ return;
523
+ }
524
+ await next();
525
+ } catch (err) {
526
+ sendSafeError(
527
+ ctx.res,
528
+ err instanceof Error ? err : new Error(String(err)),
529
+ mode === "dev",
303
530
  );
304
- ctx.res.setHeader("cache-control", "no-store");
305
- ctx.res.end(js);
306
- return;
307
531
  }
308
- await next();
309
532
  },
310
533
  async (ctx, next) => {
311
- // API routes
312
- const handled = await tryHandleApiRoute({
313
- req: ctx.req,
314
- res: ctx.res,
315
- siteDir: config.siteDir,
316
- });
317
- if (handled) return;
318
- await next();
534
+ try {
535
+ // Font serving (with proper cache headers)
536
+ const handled = await serveFonts(ctx.req, ctx.res);
537
+ if (handled) return;
538
+ await next();
539
+ } catch (err) {
540
+ sendSafeError(
541
+ ctx.res,
542
+ err instanceof Error ? err : new Error(String(err)),
543
+ mode === "dev",
544
+ );
545
+ }
546
+ },
547
+ async (ctx, next) => {
548
+ try {
549
+ // Server actions
550
+ await serverActionsMiddleware(ctx, next);
551
+ } catch (err) {
552
+ sendSafeError(
553
+ ctx.res,
554
+ err instanceof Error ? err : new Error(String(err)),
555
+ mode === "dev",
556
+ );
557
+ }
319
558
  },
320
559
  async (ctx, next) => {
321
- // dist
322
- if (mode === "prod") {
323
- await new Promise((resolve) => {
324
- serveDist(ctx.req, ctx.res, () => resolve());
560
+ try {
561
+ // API routes
562
+ const handled = await tryHandleApiRoute({
563
+ req: ctx.req,
564
+ res: ctx.res,
565
+ siteDir: config.siteDir,
325
566
  });
326
- if (ctx.res.writableEnded || ctx.res.headersSent) return;
567
+ if (handled) return;
568
+ await next();
569
+ } catch (err) {
570
+ sendSafeError(
571
+ ctx.res,
572
+ err instanceof Error ? err : new Error(String(err)),
573
+ mode === "dev",
574
+ );
327
575
  }
328
- await next();
329
576
  },
330
577
  async (ctx, next) => {
331
- // SCSS Compilation (Dev)
332
- if (mode === "dev" && ctx.url.pathname.endsWith(".css")) {
333
- let scssFile = null;
334
- const basePath = resolve(process.cwd());
335
- if (ctx.url.pathname === "/styles.css") {
336
- // Global SCSS
337
- if (config.css?.globalScss) {
338
- // Already includes siteDir if needed, but add it if it doesn't
339
- const scssPath = config.css.globalScss.startsWith(config.siteDir)
340
- ? config.css.globalScss
341
- : join(config.siteDir, config.css.globalScss);
342
- scssFile = join(basePath, scssPath);
343
- console.log(
344
- "[DEBUG CSS] Looking for global SCSS:",
345
- scssFile,
346
- "exists:",
347
- existsSync(scssFile),
348
- );
578
+ try {
579
+ // dist
580
+ if (mode === "prod") {
581
+ await new Promise((resolve) => {
582
+ serveDist(ctx.req, ctx.res, () => resolve());
583
+ });
584
+ if (ctx.res.writableEnded || ctx.res.headersSent) return;
585
+ }
586
+ await next();
587
+ } catch (err) {
588
+ sendSafeError(
589
+ ctx.res,
590
+ err instanceof Error ? err : new Error(String(err)),
591
+ mode === "dev",
592
+ );
593
+ }
594
+ },
595
+ async (ctx, next) => {
596
+ try {
597
+ // SCSS Compilation (Dev)
598
+ if (mode === "dev" && ctx.url.pathname.endsWith(".css")) {
599
+ let scssFile = null;
600
+ const basePath = resolve(process.cwd());
601
+ if (ctx.url.pathname === "/styles.css") {
602
+ // Global SCSS
603
+ if (config.css?.globalScss) {
604
+ // Already includes siteDir if needed, but add it if it doesn't
605
+ const scssPath = config.css.globalScss.startsWith(config.siteDir)
606
+ ? config.css.globalScss
607
+ : join(config.siteDir, config.css.globalScss);
608
+ scssFile = join(basePath, scssPath);
609
+ console.log(
610
+ "[DEBUG CSS] Looking for global SCSS:",
611
+ scssFile,
612
+ "exists:",
613
+ existsSync(scssFile),
614
+ );
615
+ } else {
616
+ console.log("[DEBUG CSS] No config.css.globalScss found");
617
+ }
349
618
  } else {
350
- console.log("[DEBUG CSS] No config.css.globalScss found");
351
- }
352
- } else {
353
- // Map /foo.css -> siteDir/foo.scss
354
- const rel = ctx.url.pathname.slice(1);
355
- const tryPath = join(
356
- basePath,
357
- config.siteDir,
358
- rel.replace(/\.css$/, ".scss"),
359
- );
360
- if (existsSync(tryPath)) {
361
- scssFile = tryPath;
619
+ // Map /foo.css -> siteDir/foo.scss
620
+ const rel = ctx.url.pathname.slice(1);
621
+ const tryPath = join(
622
+ basePath,
623
+ config.siteDir,
624
+ rel.replace(/\.css$/, ".scss"),
625
+ );
626
+ if (existsSync(tryPath)) {
627
+ scssFile = tryPath;
628
+ }
362
629
  }
363
- }
364
- if (scssFile && existsSync(scssFile)) {
365
- const compiler = createScssCompiler();
366
- const result = compiler.compile({
367
- inputPath: scssFile,
368
- minified: false,
369
- sourceMap: true,
370
- });
371
- if (result.error) {
372
- ctx.res.statusCode = 500;
630
+ if (scssFile && existsSync(scssFile)) {
631
+ const compiler = createScssCompiler();
632
+ const result = compiler.compile({
633
+ inputPath: scssFile,
634
+ minified: false,
635
+ sourceMap: true,
636
+ });
637
+ if (result.error) {
638
+ ctx.res.statusCode = 500;
639
+ ctx.res.setHeader("content-type", "text/css");
640
+ ctx.res.end(
641
+ `/* SCSS Error: ${result.error.replace(/\*\//g, "* /")} */ body::before { position:fixed; top:0; left:0; width:100%; content: "SCSS Error: ${result.error
642
+ .replace(/\\/g, "\\\\")
643
+ .replace(
644
+ /"/g,
645
+ '\\"',
646
+ )}"; display: block; background: red; color: white; padding: 1em; z-index:9999; white-space: pre-wrap; }`,
647
+ );
648
+ return;
649
+ }
650
+ ctx.res.statusCode = 200;
373
651
  ctx.res.setHeader("content-type", "text/css");
374
- ctx.res.end(
375
- `/* SCSS Error: ${result.error.replace(/\*\//g, "* /")} */ body::before { position:fixed; top:0; left:0; width:100%; content: "SCSS Error: ${result.error
376
- .replace(/\\/g, "\\\\")
377
- .replace(
378
- /"/g,
379
- '\\"',
380
- )}"; display: block; background: red; color: white; padding: 1em; z-index:9999; white-space: pre-wrap; }`,
381
- );
652
+ ctx.res.end(result.css);
382
653
  return;
383
654
  }
384
- ctx.res.statusCode = 200;
385
- ctx.res.setHeader("content-type", "text/css");
386
- ctx.res.end(result.css);
387
- return;
388
655
  }
656
+ await next();
657
+ } catch (err) {
658
+ sendSafeError(
659
+ ctx.res,
660
+ err instanceof Error ? err : new Error(String(err)),
661
+ mode === "dev",
662
+ );
389
663
  }
390
- await next();
391
664
  },
392
665
  async (ctx, next) => {
393
- // site assets in dev
394
- if (mode === "dev") {
395
- await new Promise((resolve) => {
396
- serveAssets(ctx.req, ctx.res, () => resolve());
397
- });
398
- if (ctx.res.writableEnded || ctx.res.headersSent) return;
666
+ try {
667
+ // site assets in dev
668
+ if (mode === "dev") {
669
+ await new Promise((resolve) => {
670
+ serveAssets(ctx.req, ctx.res, () => resolve());
671
+ });
672
+ if (ctx.res.writableEnded || ctx.res.headersSent) return;
673
+ }
674
+ await next();
675
+ } catch (err) {
676
+ sendSafeError(
677
+ ctx.res,
678
+ err instanceof Error ? err : new Error(String(err)),
679
+ mode === "dev",
680
+ );
399
681
  }
400
- await next();
401
682
  },
402
683
  async (ctx, next) => {
403
- // SSR
404
- if (ctx.req.method !== "GET") return next();
405
- const m = matchRoute(routes, ctx.url.pathname);
406
- if (!m) return next();
407
- const reqHeaders = headersToObject(ctx.req.headers);
408
- const cookies = parseCookies(ctx.req);
409
- const query = {};
410
- for (const [k, v] of ctx.url.searchParams.entries()) query[k] = v;
411
684
  try {
685
+ // Advanced routing with guards, redirects, and 404 handling
686
+ if (ctx.req.method !== "GET") return next();
687
+ const reqHeaders = headersToObject(ctx.req.headers);
688
+ const cookies = parseCookies(ctx.req);
689
+ // Use advanced router to resolve the route
690
+ const resolution = await router.resolve(
691
+ ctx.url.pathname,
692
+ ctx.url,
693
+ reqHeaders,
694
+ cookies,
695
+ );
696
+ if (resolution.type === "redirect") {
697
+ // Handle redirects (both app-level and route-level)
698
+ router.handleRedirect(
699
+ ctx.res,
700
+ resolution.location,
701
+ resolution.status,
702
+ );
703
+ return;
704
+ }
705
+ if (resolution.type === "not_found") {
706
+ // Handle 404 responses
707
+ await router.handle404(ctx.res, resolution.pathname);
708
+ return;
709
+ }
710
+ // Route matched - render the page
711
+ if (resolution.type !== "matched") return next();
412
712
  const html = await renderRouteToHtml({
413
713
  config,
414
- route: m.route,
714
+ route: resolution.route,
415
715
  req: ctx.req,
416
716
  res: ctx.res,
417
717
  url: ctx.url,
418
- params: m.params,
419
- query,
718
+ params: resolution.params,
719
+ query: resolution.query,
420
720
  headers: reqHeaders,
421
721
  cookies,
422
722
  });
@@ -433,23 +733,39 @@ initializeIslands();
433
733
  ctx.res.end(finalHtml);
434
734
  } catch (err) {
435
735
  // Middleware may have sent a response (redirect/json)
436
- if (err.message === "__REDIRECT__" || err.message === "__JSON__") {
736
+ if (
737
+ err &&
738
+ (err.message === "__REDIRECT__" || err.message === "__JSON__")
739
+ ) {
437
740
  return; // Response already sent
438
741
  }
439
- throw err;
742
+ sendSafeError(
743
+ ctx.res,
744
+ err instanceof Error ? err : new Error(String(err)),
745
+ mode === "dev",
746
+ );
440
747
  }
441
748
  },
442
- async (ctx) => {
443
- ctx.res.statusCode = 404;
444
- ctx.res.setHeader("content-type", "text/plain; charset=utf-8");
445
- ctx.res.end("404 Not Found");
446
- },
447
749
  ];
448
750
  const kernel = new Kernel();
449
751
  middlewares.forEach((m) => kernel.use(m));
450
752
  return {
451
753
  async handle(req, res) {
754
+ const start = Date.now();
755
+ const originalEnd = res.end.bind(res);
756
+ res.end = function (...args) {
757
+ const duration = Date.now() - start;
758
+ console.log(`${req.method} ${req.url} ${res.statusCode} ${duration}ms`);
759
+ return originalEnd(...args);
760
+ };
452
761
  await kernel.handle(req, res);
453
762
  },
763
+ /**
764
+ * Gracefully closes the app and cleans up resources.
765
+ * Should be called on server shutdown.
766
+ */
767
+ async close() {
768
+ await lifecycle.close();
769
+ },
454
770
  };
455
771
  }