@nuraly/lumenjs 0.1.3 → 0.1.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 (333) hide show
  1. package/README.md +62 -282
  2. package/dist/auth/config.d.ts +23 -0
  3. package/dist/auth/config.js +115 -0
  4. package/dist/auth/guard.d.ts +12 -0
  5. package/dist/auth/guard.js +28 -0
  6. package/dist/auth/index.d.ts +3 -0
  7. package/dist/auth/index.js +1 -0
  8. package/dist/auth/middleware.d.ts +23 -0
  9. package/dist/auth/middleware.js +89 -0
  10. package/dist/auth/native-auth.d.ts +73 -0
  11. package/dist/auth/native-auth.js +293 -0
  12. package/dist/auth/oidc-client.d.ts +17 -0
  13. package/dist/auth/oidc-client.js +123 -0
  14. package/dist/auth/providers/google.d.ts +23 -0
  15. package/dist/auth/providers/google.js +25 -0
  16. package/dist/auth/providers/index.d.ts +2 -0
  17. package/dist/auth/providers/index.js +1 -0
  18. package/dist/auth/routes/login.d.ts +8 -0
  19. package/dist/auth/routes/login.js +98 -0
  20. package/dist/auth/routes/logout.d.ts +4 -0
  21. package/dist/auth/routes/logout.js +79 -0
  22. package/dist/auth/routes/oidc-callback.d.ts +3 -0
  23. package/dist/auth/routes/oidc-callback.js +70 -0
  24. package/dist/auth/routes/password.d.ts +5 -0
  25. package/dist/auth/routes/password.js +149 -0
  26. package/dist/auth/routes/signup.d.ts +3 -0
  27. package/dist/auth/routes/signup.js +81 -0
  28. package/dist/auth/routes/token.d.ts +4 -0
  29. package/dist/auth/routes/token.js +70 -0
  30. package/dist/auth/routes/utils.d.ts +7 -0
  31. package/dist/auth/routes/utils.js +35 -0
  32. package/dist/auth/routes/verify.d.ts +3 -0
  33. package/dist/auth/routes/verify.js +26 -0
  34. package/dist/auth/routes.d.ts +8 -0
  35. package/dist/auth/routes.js +110 -0
  36. package/dist/auth/session.d.ts +8 -0
  37. package/dist/auth/session.js +54 -0
  38. package/dist/auth/token.d.ts +33 -0
  39. package/dist/auth/token.js +90 -0
  40. package/dist/auth/types.d.ts +156 -0
  41. package/dist/auth/types.js +2 -0
  42. package/dist/build/build-client.d.ts +15 -0
  43. package/dist/build/build-client.js +45 -0
  44. package/dist/build/build-prerender.d.ts +11 -0
  45. package/dist/build/build-prerender.js +159 -0
  46. package/dist/build/build-server.d.ts +17 -0
  47. package/dist/build/build-server.js +98 -0
  48. package/dist/build/build.js +48 -120
  49. package/dist/build/scan.d.ts +17 -0
  50. package/dist/build/scan.js +76 -6
  51. package/dist/build/serve-api.js +8 -2
  52. package/dist/build/serve-loaders.d.ts +4 -4
  53. package/dist/build/serve-loaders.js +26 -18
  54. package/dist/build/serve-ssr.js +38 -11
  55. package/dist/build/serve-static.js +3 -3
  56. package/dist/build/serve.js +218 -15
  57. package/dist/cli.js +37 -6
  58. package/dist/communication/encryption.d.ts +35 -0
  59. package/dist/communication/encryption.js +90 -0
  60. package/dist/communication/handlers/context.d.ts +27 -0
  61. package/dist/communication/handlers/context.js +1 -0
  62. package/dist/communication/handlers/conversation.d.ts +24 -0
  63. package/dist/communication/handlers/conversation.js +113 -0
  64. package/dist/communication/handlers/file-upload.d.ts +17 -0
  65. package/dist/communication/handlers/file-upload.js +62 -0
  66. package/dist/communication/handlers/messaging.d.ts +30 -0
  67. package/dist/communication/handlers/messaging.js +237 -0
  68. package/dist/communication/handlers/presence.d.ts +15 -0
  69. package/dist/communication/handlers/presence.js +76 -0
  70. package/dist/communication/handlers.d.ts +5 -0
  71. package/dist/communication/handlers.js +5 -0
  72. package/dist/communication/index.d.ts +9 -0
  73. package/dist/communication/index.js +7 -0
  74. package/dist/communication/link-preview.d.ts +18 -0
  75. package/dist/communication/link-preview.js +115 -0
  76. package/dist/communication/schema.d.ts +10 -0
  77. package/dist/communication/schema.js +101 -0
  78. package/dist/communication/server.d.ts +86 -0
  79. package/dist/communication/server.js +212 -0
  80. package/dist/communication/signaling.d.ts +43 -0
  81. package/dist/communication/signaling.js +271 -0
  82. package/dist/communication/store.d.ts +71 -0
  83. package/dist/communication/store.js +289 -0
  84. package/dist/communication/types.d.ts +454 -0
  85. package/dist/communication/types.js +1 -0
  86. package/dist/create.d.ts +1 -0
  87. package/dist/create.js +55 -0
  88. package/dist/db/auto-migrate.d.ts +3 -0
  89. package/dist/db/auto-migrate.js +100 -0
  90. package/dist/db/client.d.ts +3 -0
  91. package/dist/db/client.js +18 -0
  92. package/dist/db/index.d.ts +17 -13
  93. package/dist/db/index.js +205 -26
  94. package/dist/db/seed.d.ts +12 -0
  95. package/dist/db/seed.js +88 -0
  96. package/dist/db/table.d.ts +10 -0
  97. package/dist/db/table.js +12 -0
  98. package/dist/dev-server/config.d.ts +11 -0
  99. package/dist/dev-server/config.js +23 -20
  100. package/dist/dev-server/index-html.d.ts +3 -0
  101. package/dist/dev-server/index-html.js +18 -6
  102. package/dist/dev-server/nuralyui-aliases.d.ts +0 -4
  103. package/dist/dev-server/nuralyui-aliases.js +115 -94
  104. package/dist/dev-server/plugins/vite-plugin-api-routes.js +29 -5
  105. package/dist/dev-server/plugins/vite-plugin-auth.d.ts +6 -0
  106. package/dist/dev-server/plugins/vite-plugin-auth.js +223 -0
  107. package/dist/dev-server/plugins/vite-plugin-auto-define.d.ts +16 -0
  108. package/dist/dev-server/plugins/vite-plugin-auto-define.js +111 -0
  109. package/dist/dev-server/plugins/vite-plugin-communication.d.ts +6 -0
  110. package/dist/dev-server/plugins/vite-plugin-communication.js +205 -0
  111. package/dist/dev-server/plugins/vite-plugin-editor-api.d.ts +6 -0
  112. package/dist/dev-server/plugins/vite-plugin-editor-api.js +318 -0
  113. package/dist/dev-server/plugins/vite-plugin-i18n.js +69 -2
  114. package/dist/dev-server/plugins/vite-plugin-lit-dedup.d.ts +6 -0
  115. package/dist/dev-server/plugins/vite-plugin-lit-dedup.js +78 -34
  116. package/dist/dev-server/plugins/vite-plugin-lit-hmr.js +44 -2
  117. package/dist/dev-server/plugins/vite-plugin-llms.d.ts +2 -0
  118. package/dist/dev-server/plugins/vite-plugin-llms.js +92 -0
  119. package/dist/dev-server/plugins/vite-plugin-loaders.js +146 -13
  120. package/dist/dev-server/plugins/vite-plugin-routes.js +15 -5
  121. package/dist/dev-server/plugins/vite-plugin-socketio.d.ts +2 -0
  122. package/dist/dev-server/plugins/vite-plugin-socketio.js +51 -0
  123. package/dist/dev-server/plugins/vite-plugin-source-annotator.d.ts +2 -0
  124. package/dist/dev-server/plugins/vite-plugin-source-annotator.js +26 -3
  125. package/dist/dev-server/plugins/vite-plugin-storage.d.ts +10 -0
  126. package/dist/dev-server/plugins/vite-plugin-storage.js +126 -0
  127. package/dist/dev-server/plugins/vite-plugin-virtual-modules.js +111 -2
  128. package/dist/dev-server/server.js +127 -13
  129. package/dist/dev-server/ssr-render.d.ts +2 -1
  130. package/dist/dev-server/ssr-render.js +107 -48
  131. package/dist/editor/ai/backend.d.ts +20 -0
  132. package/dist/editor/ai/backend.js +104 -0
  133. package/dist/editor/ai/claude-code-client.d.ts +20 -0
  134. package/dist/editor/ai/claude-code-client.js +145 -0
  135. package/dist/editor/ai/opencode-client.d.ts +14 -0
  136. package/dist/editor/ai/opencode-client.js +125 -0
  137. package/dist/editor/ai/snapshot-store.d.ts +22 -0
  138. package/dist/editor/ai/snapshot-store.js +35 -0
  139. package/dist/editor/ai/types.d.ts +30 -0
  140. package/dist/editor/ai/types.js +136 -0
  141. package/dist/editor/ai-chat-panel.d.ts +13 -0
  142. package/dist/editor/ai-chat-panel.js +587 -0
  143. package/dist/editor/ai-markdown.d.ts +10 -0
  144. package/dist/editor/ai-markdown.js +70 -0
  145. package/dist/editor/ai-project-panel.d.ts +11 -0
  146. package/dist/editor/ai-project-panel.js +332 -0
  147. package/dist/editor/ast-modification.d.ts +11 -0
  148. package/dist/editor/ast-modification.js +1 -0
  149. package/dist/editor/ast-service.d.ts +30 -0
  150. package/dist/editor/ast-service.js +180 -0
  151. package/dist/editor/css-rules.d.ts +54 -0
  152. package/dist/editor/css-rules.js +423 -0
  153. package/dist/editor/editor-api-client.d.ts +51 -0
  154. package/dist/editor/editor-api-client.js +162 -0
  155. package/dist/editor/editor-bridge.d.ts +1 -0
  156. package/dist/editor/editor-bridge.js +17 -8
  157. package/dist/editor/editor-toolbar.d.ts +14 -0
  158. package/dist/editor/editor-toolbar.js +115 -0
  159. package/dist/editor/file-editor.d.ts +9 -0
  160. package/dist/editor/file-editor.js +236 -0
  161. package/dist/editor/file-service.d.ts +16 -0
  162. package/dist/editor/file-service.js +52 -0
  163. package/dist/editor/i18n-key-gen.d.ts +1 -0
  164. package/dist/editor/i18n-key-gen.js +7 -0
  165. package/dist/editor/inline-text-edit.d.ts +5 -0
  166. package/dist/editor/inline-text-edit.js +173 -92
  167. package/dist/editor/overlay-events.d.ts +5 -0
  168. package/dist/editor/overlay-events.js +364 -0
  169. package/dist/editor/overlay-hmr.d.ts +2 -0
  170. package/dist/editor/overlay-hmr.js +75 -0
  171. package/dist/editor/overlay-selection.d.ts +29 -0
  172. package/dist/editor/overlay-selection.js +148 -0
  173. package/dist/editor/overlay-utils.d.ts +12 -0
  174. package/dist/editor/overlay-utils.js +59 -0
  175. package/dist/editor/properties-panel-persist.d.ts +14 -0
  176. package/dist/editor/properties-panel-persist.js +70 -0
  177. package/dist/editor/properties-panel-rows.d.ts +10 -0
  178. package/dist/editor/properties-panel-rows.js +349 -0
  179. package/dist/editor/properties-panel-styles.d.ts +4 -0
  180. package/dist/editor/properties-panel-styles.js +174 -0
  181. package/dist/editor/properties-panel.d.ts +4 -0
  182. package/dist/editor/properties-panel.js +148 -0
  183. package/dist/editor/property-registry.d.ts +16 -0
  184. package/dist/editor/property-registry.js +303 -0
  185. package/dist/editor/standalone-file-panel.d.ts +0 -0
  186. package/dist/editor/standalone-file-panel.js +1 -0
  187. package/dist/editor/standalone-overlay-dom.d.ts +0 -0
  188. package/dist/editor/standalone-overlay-dom.js +1 -0
  189. package/dist/editor/standalone-overlay-styles.d.ts +0 -0
  190. package/dist/editor/standalone-overlay-styles.js +1 -0
  191. package/dist/editor/standalone-overlay.d.ts +1 -0
  192. package/dist/editor/standalone-overlay.js +76 -0
  193. package/dist/editor/syntax-highlighter.d.ts +4 -0
  194. package/dist/editor/syntax-highlighter.js +81 -0
  195. package/dist/editor/text-toolbar.d.ts +11 -0
  196. package/dist/editor/text-toolbar.js +327 -0
  197. package/dist/editor/toolbar-styles.d.ts +4 -0
  198. package/dist/editor/toolbar-styles.js +198 -0
  199. package/dist/email/index.d.ts +32 -0
  200. package/dist/email/index.js +154 -0
  201. package/dist/email/providers/resend.d.ts +2 -0
  202. package/dist/email/providers/resend.js +24 -0
  203. package/dist/email/providers/sendgrid.d.ts +2 -0
  204. package/dist/email/providers/sendgrid.js +31 -0
  205. package/dist/email/providers/smtp.d.ts +13 -0
  206. package/dist/email/providers/smtp.js +125 -0
  207. package/dist/email/template-engine.d.ts +18 -0
  208. package/dist/email/template-engine.js +116 -0
  209. package/dist/email/templates/base.d.ts +9 -0
  210. package/dist/email/templates/base.js +65 -0
  211. package/dist/email/templates/password-reset.d.ts +5 -0
  212. package/dist/email/templates/password-reset.js +15 -0
  213. package/dist/email/templates/verify-email.d.ts +5 -0
  214. package/dist/email/templates/verify-email.js +15 -0
  215. package/dist/email/templates/welcome.d.ts +5 -0
  216. package/dist/email/templates/welcome.js +13 -0
  217. package/dist/email/types.d.ts +49 -0
  218. package/dist/email/types.js +1 -0
  219. package/dist/llms/generate.d.ts +46 -0
  220. package/dist/llms/generate.js +185 -0
  221. package/dist/permissions/guard.d.ts +28 -0
  222. package/dist/permissions/guard.js +30 -0
  223. package/dist/permissions/index.d.ts +6 -0
  224. package/dist/permissions/index.js +3 -0
  225. package/dist/permissions/service.d.ts +80 -0
  226. package/dist/permissions/service.js +210 -0
  227. package/dist/permissions/tables.d.ts +5 -0
  228. package/dist/permissions/tables.js +68 -0
  229. package/dist/permissions/types.d.ts +33 -0
  230. package/dist/permissions/types.js +1 -0
  231. package/dist/runtime/app-shell.js +163 -0
  232. package/dist/runtime/auth.d.ts +10 -0
  233. package/dist/runtime/auth.js +30 -0
  234. package/dist/runtime/communication.d.ts +137 -0
  235. package/dist/runtime/communication.js +228 -0
  236. package/dist/runtime/error-boundary.d.ts +23 -0
  237. package/dist/runtime/error-boundary.js +120 -0
  238. package/dist/runtime/i18n.d.ts +6 -1
  239. package/dist/runtime/i18n.js +42 -21
  240. package/dist/runtime/router-data.d.ts +3 -0
  241. package/dist/runtime/router-data.js +102 -17
  242. package/dist/runtime/router-hydration.js +25 -0
  243. package/dist/runtime/router.d.ts +16 -1
  244. package/dist/runtime/router.js +188 -42
  245. package/dist/runtime/socket-client.d.ts +2 -0
  246. package/dist/runtime/socket-client.js +30 -0
  247. package/dist/runtime/webrtc.d.ts +47 -0
  248. package/dist/runtime/webrtc.js +178 -0
  249. package/dist/shared/graceful-shutdown.d.ts +8 -0
  250. package/dist/shared/graceful-shutdown.js +36 -0
  251. package/dist/shared/health.d.ts +8 -0
  252. package/dist/shared/health.js +25 -0
  253. package/dist/shared/llms-txt.d.ts +31 -0
  254. package/dist/shared/llms-txt.js +85 -0
  255. package/dist/shared/logger.d.ts +32 -0
  256. package/dist/shared/logger.js +93 -0
  257. package/dist/shared/meta.d.ts +27 -0
  258. package/dist/shared/meta.js +71 -0
  259. package/dist/shared/middleware-runner.d.ts +9 -0
  260. package/dist/shared/middleware-runner.js +29 -0
  261. package/dist/shared/rate-limit.d.ts +18 -0
  262. package/dist/shared/rate-limit.js +71 -0
  263. package/dist/shared/request-id.d.ts +5 -0
  264. package/dist/shared/request-id.js +18 -0
  265. package/dist/shared/route-matching.js +16 -1
  266. package/dist/shared/security-headers.d.ts +18 -0
  267. package/dist/shared/security-headers.js +38 -0
  268. package/dist/shared/socket-io-setup.d.ts +11 -0
  269. package/dist/shared/socket-io-setup.js +51 -0
  270. package/dist/shared/types.d.ts +14 -0
  271. package/dist/shared/utils.d.ts +33 -7
  272. package/dist/shared/utils.js +164 -27
  273. package/dist/storage/adapters/local.d.ts +44 -0
  274. package/dist/storage/adapters/local.js +85 -0
  275. package/dist/storage/adapters/s3.d.ts +32 -0
  276. package/dist/storage/adapters/s3.js +116 -0
  277. package/dist/storage/adapters/types.d.ts +53 -0
  278. package/dist/storage/adapters/types.js +1 -0
  279. package/dist/storage/index.d.ts +76 -0
  280. package/dist/storage/index.js +83 -0
  281. package/package.json +19 -7
  282. package/templates/blog/api/posts.ts +4 -18
  283. package/templates/blog/data/migrations/001_init.sql +6 -5
  284. package/templates/blog/lumenjs.config.ts +3 -0
  285. package/templates/blog/package.json +14 -0
  286. package/templates/blog/pages/_layout.ts +25 -0
  287. package/templates/blog/pages/index.ts +48 -22
  288. package/templates/blog/pages/posts/[slug].ts +45 -20
  289. package/templates/blog/pages/tag/[tag].ts +44 -0
  290. package/templates/dashboard/api/stats.ts +8 -5
  291. package/templates/dashboard/lumenjs.config.ts +3 -0
  292. package/templates/dashboard/package.json +14 -0
  293. package/templates/dashboard/pages/_layout.ts +25 -0
  294. package/templates/dashboard/pages/index.ts +54 -23
  295. package/templates/dashboard/pages/settings/index.ts +29 -0
  296. package/templates/default/lumenjs.config.ts +3 -0
  297. package/templates/default/package.json +14 -0
  298. package/templates/default/pages/index.ts +24 -0
  299. package/templates/social/api/posts/[id].ts +14 -0
  300. package/templates/social/api/posts.ts +11 -0
  301. package/templates/social/api/profile/[username].ts +10 -0
  302. package/templates/social/api/upload.ts +19 -0
  303. package/templates/social/data/migrations/001_init.sql +78 -0
  304. package/templates/social/data/migrations/002_add_image_url.sql +1 -0
  305. package/templates/social/data/migrations/003_auth.sql +7 -0
  306. package/templates/social/docs/architecture.md +76 -0
  307. package/templates/social/docs/components.md +100 -0
  308. package/templates/social/docs/data.md +89 -0
  309. package/templates/social/docs/pages.md +96 -0
  310. package/templates/social/docs/theming.md +52 -0
  311. package/templates/social/lib/media.ts +130 -0
  312. package/templates/social/lumenjs.auth.ts +21 -0
  313. package/templates/social/lumenjs.config.ts +3 -0
  314. package/templates/social/package.json +5 -0
  315. package/templates/social/pages/_layout.ts +239 -0
  316. package/templates/social/pages/apps/[id].ts +173 -0
  317. package/templates/social/pages/apps/index.ts +116 -0
  318. package/templates/social/pages/auth/login.ts +92 -0
  319. package/templates/social/pages/bookmarks.ts +57 -0
  320. package/templates/social/pages/explore.ts +73 -0
  321. package/templates/social/pages/index.ts +351 -0
  322. package/templates/social/pages/messages.ts +298 -0
  323. package/templates/social/pages/new.ts +77 -0
  324. package/templates/social/pages/notifications.ts +73 -0
  325. package/templates/social/pages/post/[id].ts +124 -0
  326. package/templates/social/pages/profile/[username].ts +100 -0
  327. package/templates/social/pages/settings/accessibility.ts +153 -0
  328. package/templates/social/pages/settings/account.ts +260 -0
  329. package/templates/social/pages/settings/help.ts +141 -0
  330. package/templates/social/pages/settings/language.ts +103 -0
  331. package/templates/social/pages/settings/privacy.ts +183 -0
  332. package/templates/social/pages/settings/security.ts +133 -0
  333. package/templates/social/pages/settings.ts +185 -0
@@ -1,6 +1,6 @@
1
1
  import http from 'http';
2
2
  import type { BuildManifest } from '../shared/types.js';
3
- export declare function handleLayoutLoaderRequest(manifest: BuildManifest, serverDir: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse): Promise<void>;
4
- export declare function handleLayoutSubscribeRequest(manifest: BuildManifest, serverDir: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse): Promise<void>;
5
- export declare function handleSubscribeRequest(manifest: BuildManifest, serverDir: string, pagesDir: string, pathname: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse): Promise<void>;
6
- export declare function handleLoaderRequest(manifest: BuildManifest, serverDir: string, pagesDir: string, pathname: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse): Promise<void>;
3
+ export declare function handleLayoutLoaderRequest(manifest: BuildManifest, serverDir: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse, user?: any): Promise<void>;
4
+ export declare function handleLayoutSubscribeRequest(manifest: BuildManifest, serverDir: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse, user?: any): Promise<void>;
5
+ export declare function handleSubscribeRequest(manifest: BuildManifest, serverDir: string, pagesDir: string, pathname: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse, user?: any): Promise<void>;
6
+ export declare function handleLoaderRequest(manifest: BuildManifest, serverDir: string, pagesDir: string, pathname: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse, user?: any): Promise<void>;
@@ -2,7 +2,15 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { isRedirectResponse } from '../shared/utils.js';
4
4
  import { matchRoute } from '../shared/route-matching.js';
5
- export async function handleLayoutLoaderRequest(manifest, serverDir, queryString, headers, res) {
5
+ import { logger } from '../shared/logger.js';
6
+ /** Resolve a module path, falling back to bracket-sanitized filename (Rollup replaces [] with _) */
7
+ function resolveModulePath(serverDir, moduleName) {
8
+ const p = path.join(serverDir, moduleName);
9
+ if (fs.existsSync(p))
10
+ return p;
11
+ return path.join(serverDir, moduleName.replace(/\[/g, '_').replace(/\]/g, '_'));
12
+ }
13
+ export async function handleLayoutLoaderRequest(manifest, serverDir, queryString, headers, res, user) {
6
14
  const query = {};
7
15
  if (queryString) {
8
16
  for (const pair of queryString.split('&')) {
@@ -18,7 +26,7 @@ export async function handleLayoutLoaderRequest(manifest, serverDir, queryString
18
26
  res.end(JSON.stringify({ __nk_no_loader: true }));
19
27
  return;
20
28
  }
21
- const modulePath = path.join(serverDir, layout.module);
29
+ const modulePath = resolveModulePath(serverDir, layout.module);
22
30
  if (!fs.existsSync(modulePath)) {
23
31
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
24
32
  res.end(JSON.stringify({ __nk_no_loader: true }));
@@ -34,7 +42,7 @@ export async function handleLayoutLoaderRequest(manifest, serverDir, queryString
34
42
  // Parse locale from query for layout loader
35
43
  const locale = query.__locale;
36
44
  delete query.__locale;
37
- const result = await mod.loader({ params: {}, query: {}, url: `/__layout/${dir}`, headers, locale });
45
+ const result = await mod.loader({ params: {}, query: {}, url: `/__layout/${dir}`, headers, locale, user: user ?? null });
38
46
  if (isRedirectResponse(result)) {
39
47
  res.writeHead(result.status || 302, { Location: result.location });
40
48
  res.end();
@@ -49,14 +57,14 @@ export async function handleLayoutLoaderRequest(manifest, serverDir, queryString
49
57
  res.end();
50
58
  return;
51
59
  }
52
- console.error(`[LumenJS] Layout loader error for dir=${dir}:`, err);
60
+ logger.error(`Layout loader error`, { dir, error: err?.message });
53
61
  const status = err?.status || 500;
54
- const message = err?.message || 'Layout loader failed';
62
+ const message = status < 500 ? (err?.message || 'Layout loader failed') : 'Internal server error';
55
63
  res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
56
64
  res.end(JSON.stringify({ error: message }));
57
65
  }
58
66
  }
59
- export async function handleLayoutSubscribeRequest(manifest, serverDir, queryString, headers, res) {
67
+ export async function handleLayoutSubscribeRequest(manifest, serverDir, queryString, headers, res, user) {
60
68
  const query = {};
61
69
  if (queryString) {
62
70
  for (const pair of queryString.split('&')) {
@@ -71,7 +79,7 @@ export async function handleLayoutSubscribeRequest(manifest, serverDir, queryStr
71
79
  res.end();
72
80
  return;
73
81
  }
74
- const modulePath = path.join(serverDir, layout.module);
82
+ const modulePath = resolveModulePath(serverDir, layout.module);
75
83
  if (!fs.existsSync(modulePath)) {
76
84
  res.writeHead(204);
77
85
  res.end();
@@ -93,21 +101,21 @@ export async function handleLayoutSubscribeRequest(manifest, serverDir, queryStr
93
101
  const push = (data) => {
94
102
  res.write(`data: ${JSON.stringify(data)}\n\n`);
95
103
  };
96
- const cleanup = mod.subscribe({ params: {}, push, headers, locale });
104
+ const cleanup = mod.subscribe({ params: {}, push, headers, locale, user: user ?? null });
97
105
  res.on('close', () => {
98
106
  if (typeof cleanup === 'function')
99
107
  cleanup();
100
108
  });
101
109
  }
102
110
  catch (err) {
103
- console.error(`[LumenJS] Layout subscribe error for dir=${dir}:`, err);
111
+ logger.error(`Layout subscribe error`, { dir, error: err?.message });
104
112
  if (!res.headersSent) {
105
113
  res.writeHead(500);
106
114
  res.end();
107
115
  }
108
116
  }
109
117
  }
110
- export async function handleSubscribeRequest(manifest, serverDir, pagesDir, pathname, queryString, headers, res) {
118
+ export async function handleSubscribeRequest(manifest, serverDir, pagesDir, pathname, queryString, headers, res, user) {
111
119
  const pagePath = pathname.replace('/__nk_subscribe', '') || '/';
112
120
  const query = {};
113
121
  if (queryString) {
@@ -130,7 +138,7 @@ export async function handleSubscribeRequest(manifest, serverDir, pagesDir, path
130
138
  res.end();
131
139
  return;
132
140
  }
133
- const modulePath = path.join(serverDir, matched.route.module);
141
+ const modulePath = resolveModulePath(serverDir, matched.route.module);
134
142
  if (!fs.existsSync(modulePath)) {
135
143
  res.writeHead(204);
136
144
  res.end();
@@ -152,21 +160,21 @@ export async function handleSubscribeRequest(manifest, serverDir, pagesDir, path
152
160
  const push = (data) => {
153
161
  res.write(`data: ${JSON.stringify(data)}\n\n`);
154
162
  };
155
- const cleanup = mod.subscribe({ params: matched.params, push, headers, locale });
163
+ const cleanup = mod.subscribe({ params: matched.params, push, headers, locale, user: user ?? null });
156
164
  res.on('close', () => {
157
165
  if (typeof cleanup === 'function')
158
166
  cleanup();
159
167
  });
160
168
  }
161
169
  catch (err) {
162
- console.error(`[LumenJS] Subscribe error for ${pagePath}:`, err);
170
+ logger.error(`Subscribe error`, { pagePath, error: err?.message });
163
171
  if (!res.headersSent) {
164
172
  res.writeHead(500);
165
173
  res.end();
166
174
  }
167
175
  }
168
176
  }
169
- export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathname, queryString, headers, res) {
177
+ export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathname, queryString, headers, res, user) {
170
178
  const pagePath = pathname.replace('/__nk_loader', '') || '/';
171
179
  // Parse query params
172
180
  const query = {};
@@ -191,7 +199,7 @@ export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathnam
191
199
  res.end(JSON.stringify({ __nk_no_loader: true }));
192
200
  return;
193
201
  }
194
- const modulePath = path.join(serverDir, matched.route.module);
202
+ const modulePath = resolveModulePath(serverDir, matched.route.module);
195
203
  if (!fs.existsSync(modulePath)) {
196
204
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
197
205
  res.end(JSON.stringify({ __nk_no_loader: true }));
@@ -206,7 +214,7 @@ export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathnam
206
214
  }
207
215
  const locale = query.__locale;
208
216
  delete query.__locale;
209
- const result = await mod.loader({ params: matched.params, query, url: pagePath, headers, locale });
217
+ const result = await mod.loader({ params: matched.params, query, url: pagePath, headers, locale, user: user ?? null });
210
218
  if (isRedirectResponse(result)) {
211
219
  res.writeHead(result.status || 302, { Location: result.location });
212
220
  res.end();
@@ -221,9 +229,9 @@ export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathnam
221
229
  res.end();
222
230
  return;
223
231
  }
224
- console.error(`[LumenJS] Loader error for ${pagePath}:`, err);
232
+ logger.error(`Loader error`, { pagePath, error: err?.message });
225
233
  const status = err?.status || 500;
226
- const message = err?.message || 'Loader failed';
234
+ const message = status < 500 ? (err?.message || 'Loader failed') : 'Internal server error';
227
235
  res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
228
236
  res.end(JSON.stringify({ error: message }));
229
237
  }
@@ -1,22 +1,27 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { stripOuterLitMarkers, dirToLayoutTagName, isRedirectResponse } from '../shared/utils.js';
3
+ import { stripOuterLitMarkers, dirToLayoutTagName, isRedirectResponse, patchLoaderDataSpread } from '../shared/utils.js';
4
4
  import { matchRoute } from '../shared/route-matching.js';
5
5
  import { sendCompressed } from './serve-static.js';
6
+ import { logger } from '../shared/logger.js';
6
7
  export async function handlePageRoute(manifest, serverDir, pagesDir, pathname, queryString, indexHtmlShell, title, ssrRuntime, req, res) {
7
8
  // Find matching route (any route, not just those with loaders)
8
9
  const allMatched = matchRoute(manifest.routes, pathname);
9
10
  // Try SSR for routes with loaders
10
11
  const matched = matchRoute(manifest.routes.filter(r => r.hasLoader), pathname);
11
12
  if (matched && matched.route.module) {
12
- const modulePath = path.join(serverDir, matched.route.module);
13
+ // Rollup sanitizes brackets in filenames: [...path] → _...path_
14
+ let modulePath = path.join(serverDir, matched.route.module);
15
+ if (!fs.existsSync(modulePath)) {
16
+ modulePath = path.join(serverDir, matched.route.module.replace(/\[/g, '_').replace(/\]/g, '_'));
17
+ }
13
18
  if (fs.existsSync(modulePath)) {
14
19
  try {
15
20
  const mod = await import(modulePath);
16
21
  // Run loader
17
22
  let loaderData = undefined;
18
23
  if (mod.loader && typeof mod.loader === 'function') {
19
- loaderData = await mod.loader({ params: matched.params, query: {}, url: pathname, headers: req.headers });
24
+ loaderData = await mod.loader({ params: matched.params, query: {}, url: pathname, headers: req.headers, user: req.nkAuth?.user ?? null });
20
25
  if (isRedirectResponse(loaderData)) {
21
26
  res.writeHead(loaderData.status || 302, { Location: loaderData.location });
22
27
  res.end();
@@ -39,7 +44,7 @@ export async function handlePageRoute(manifest, serverDir, pagesDir, pathname, q
39
44
  if (fs.existsSync(layoutModulePath)) {
40
45
  const layoutMod = await import(layoutModulePath);
41
46
  if (layoutMod.loader && typeof layoutMod.loader === 'function') {
42
- layoutLoaderData = await layoutMod.loader({ params: {}, query: {}, url: pathname, headers: req.headers });
47
+ layoutLoaderData = await layoutMod.loader({ params: {}, query: {}, url: pathname, headers: req.headers, user: req.nkAuth?.user ?? null });
43
48
  if (isRedirectResponse(layoutLoaderData)) {
44
49
  res.writeHead(layoutLoaderData.status || 302, { Location: layoutLoaderData.location });
45
50
  res.end();
@@ -52,6 +57,12 @@ export async function handlePageRoute(manifest, serverDir, pagesDir, pathname, q
52
57
  layoutsData.push({ loaderPath: dir, data: layoutLoaderData });
53
58
  layoutModules.push({ tagName: layoutTagName, loaderData: layoutLoaderData });
54
59
  }
60
+ // Patch element classes to spread loaderData into individual properties
61
+ for (const lm of layoutModules) {
62
+ patchLoaderDataSpread(lm.tagName);
63
+ }
64
+ if (tagName)
65
+ patchLoaderDataSpread(tagName);
55
66
  if (tagName && ssrRuntime) {
56
67
  // SSR render with the bundled @lit-labs/ssr runtime
57
68
  try {
@@ -90,14 +101,18 @@ export async function handlePageRoute(manifest, serverDir, pagesDir, pathname, q
90
101
  const loaderDataScript = ssrDataObj !== undefined
91
102
  ? `<script type="application/json" id="__nk_ssr_data__">${JSON.stringify(ssrDataObj).replace(/</g, '\\u003c')}</script>`
92
103
  : '';
93
- const hydrateScript = `<script type="module">import '@lit-labs/ssr-client/lit-element-hydrate-support.js';</script>`;
104
+ // Auth: inline user data for client hydration
105
+ const authUser = req.nkAuth?.user ?? null;
106
+ const authScript = authUser
107
+ ? `<script type="application/json" id="__nk_auth__">${JSON.stringify(authUser).replace(/</g, '\\u003c')}</script>`
108
+ : '';
94
109
  let html_out = indexHtmlShell;
95
- html_out = html_out.replace(/<nk-app><\/nk-app>/, `${loaderDataScript}<nk-app data-nk-ssr><div id="nk-router-outlet">${ssrHtml}</div></nk-app>${hydrateScript}`);
110
+ html_out = html_out.replace(/<nk-app><\/nk-app>/, `${authScript}${loaderDataScript}<nk-app data-nk-ssr><div id="nk-router-outlet">${ssrHtml}</div></nk-app>`);
96
111
  sendCompressed(req, res, 200, 'text/html; charset=utf-8', html_out);
97
112
  return;
98
113
  }
99
114
  catch (ssrErr) {
100
- console.error('[LumenJS] SSR render failed, falling back to CSR:', ssrErr);
115
+ logger.warn('SSR render failed, falling back to CSR', { error: ssrErr?.message });
101
116
  }
102
117
  }
103
118
  // Fallback: inject loader data without SSR HTML
@@ -106,16 +121,28 @@ export async function handlePageRoute(manifest, serverDir, pagesDir, pathname, q
106
121
  ? { page: loaderData, layouts: layoutsData }
107
122
  : loaderData;
108
123
  const loaderDataScript = `<script type="application/json" id="__nk_ssr_data__">${JSON.stringify(ssrDataObj).replace(/</g, '\\u003c')}</script>`;
109
- let html_out = indexHtmlShell.replace('<nk-app>', `${loaderDataScript}<nk-app>`);
124
+ const authUserFb = req.nkAuth?.user ?? null;
125
+ const authScriptFb = authUserFb
126
+ ? `<script type="application/json" id="__nk_auth__">${JSON.stringify(authUserFb).replace(/</g, '\\u003c')}</script>`
127
+ : '';
128
+ let html_out = indexHtmlShell.replace('<nk-app>', `${authScriptFb}${loaderDataScript}<nk-app>`);
110
129
  sendCompressed(req, res, 200, 'text/html; charset=utf-8', html_out);
111
130
  return;
112
131
  }
113
132
  }
114
133
  catch (err) {
115
- console.error('[LumenJS] Page handler error:', err);
134
+ logger.error('Page handler error', { error: err?.message });
116
135
  }
117
136
  }
118
137
  }
119
- // SPA fallback — serve the built index.html
120
- sendCompressed(req, res, 200, 'text/html; charset=utf-8', indexHtmlShell);
138
+ // SPA fallback — serve the built index.html with auth data injected
139
+ const fallbackUser = req.nkAuth?.user ?? null;
140
+ if (fallbackUser) {
141
+ const authTag = `<script type="application/json" id="__nk_auth__">${JSON.stringify(fallbackUser).replace(/</g, '\\u003c')}</script>`;
142
+ const html = indexHtmlShell.replace('<nk-app>', `${authTag}<nk-app>`);
143
+ sendCompressed(req, res, 200, 'text/html; charset=utf-8', html);
144
+ }
145
+ else {
146
+ sendCompressed(req, res, 200, 'text/html; charset=utf-8', indexHtmlShell);
147
+ }
121
148
  }
@@ -52,9 +52,9 @@ export function sendCompressed(req, res, statusCode, contentType, body) {
52
52
  }
53
53
  export function serveStaticFile(clientDir, pathname, req, res) {
54
54
  // Prevent directory traversal
55
- const safePath = path.normalize(pathname).replace(/^(\.\.[/\\])+/, '');
56
- const filePath = path.join(clientDir, safePath);
57
- if (!filePath.startsWith(clientDir)) {
55
+ const resolvedClientDir = path.resolve(clientDir);
56
+ const filePath = path.resolve(resolvedClientDir, pathname.replace(/^\/+/, ''));
57
+ if (!filePath.startsWith(resolvedClientDir + path.sep) && filePath !== resolvedClientDir) {
58
58
  return false;
59
59
  }
60
60
  if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) {
@@ -10,44 +10,217 @@ import { handlePageRoute } from './serve-ssr.js';
10
10
  import { renderErrorPage } from './error-page.js';
11
11
  import { handleI18nRequest } from './serve-i18n.js';
12
12
  import { resolveLocale } from '../dev-server/middleware/locale.js';
13
- import { setProjectDir } from '../db/context.js';
13
+ import { runMiddlewareChain, extractMiddleware } from '../shared/middleware-runner.js';
14
+ import { getMiddlewareDirsForPathname } from './scan.js';
15
+ import { createAuthMiddleware } from '../auth/middleware.js';
16
+ import { handleAuthRoutes } from '../auth/routes.js';
17
+ import { loadAuthConfigProd } from '../auth/config.js';
18
+ import { initLogger, logger } from '../shared/logger.js';
19
+ import { createSecurityHeadersMiddleware } from '../shared/security-headers.js';
20
+ import { createRateLimiter, createAuthRateLimiter } from '../shared/rate-limit.js';
21
+ import { createHealthCheckHandler } from '../shared/health.js';
22
+ import { createRequestIdMiddleware } from '../shared/request-id.js';
23
+ import { getRequestId } from '../shared/request-id.js';
24
+ import { setupGracefulShutdown } from '../shared/graceful-shutdown.js';
14
25
  export async function serveProject(options) {
15
26
  const { projectDir } = options;
16
- setProjectDir(projectDir);
17
27
  const port = options.port || 3000;
18
28
  const outDir = path.join(projectDir, '.lumenjs');
19
29
  const clientDir = path.join(outDir, 'client');
20
30
  const serverDir = path.join(outDir, 'server');
21
31
  const manifestPath = path.join(outDir, 'manifest.json');
32
+ // Initialize structured logging
33
+ initLogger();
22
34
  if (!fs.existsSync(manifestPath)) {
23
- console.error('[LumenJS] No build found. Run `lumenjs build` first.');
35
+ logger.fatal('No build found. Run `lumenjs build` first.');
24
36
  process.exit(1);
25
37
  }
26
38
  const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
27
- const { title } = readProjectConfig(projectDir);
39
+ const config = readProjectConfig(projectDir);
40
+ const { title } = config;
28
41
  const localesDir = path.join(outDir, 'locales');
42
+ // Production middleware stack
43
+ const requestIdMiddleware = createRequestIdMiddleware();
44
+ const securityHeaders = createSecurityHeadersMiddleware(config.securityHeaders);
45
+ const rateLimiter = createRateLimiter(config.rateLimit);
46
+ const authRateLimiter = createAuthRateLimiter();
47
+ const healthCheck = createHealthCheckHandler({ version: config.version });
29
48
  // Read the built index.html shell
30
49
  const indexHtmlPath = path.join(clientDir, 'index.html');
31
50
  if (!fs.existsSync(indexHtmlPath)) {
32
- console.error('[LumenJS] No index.html found in build output.');
51
+ logger.fatal('No index.html found in build output.');
33
52
  process.exit(1);
34
53
  }
35
54
  const indexHtmlShell = fs.readFileSync(indexHtmlPath, 'utf-8');
36
- // Load bundled SSR runtime first — it installs @lit-labs/ssr DOM shim
37
- // which must be in place before any Lit class is instantiated.
55
+ // Load bundled SSR runtime first — its install-global-dom-shim sets up
56
+ // the proper HTMLElement/window/document shims that @lit-labs/ssr needs.
57
+ // The lit-shared chunk handles missing HTMLElement via a fallback to its own shim,
58
+ // so no pre-installation is needed.
38
59
  const ssrRuntimePath = path.join(serverDir, 'ssr-runtime.js');
39
60
  let ssrRuntime = null;
40
61
  if (fs.existsSync(ssrRuntimePath)) {
41
62
  ssrRuntime = await import(ssrRuntimePath);
42
63
  }
43
64
  // Install additional DOM shims that NuralyUI components may need
65
+ // (must run AFTER SSR runtime so we don't block its window/HTMLElement setup)
44
66
  installDomShims();
45
67
  const pagesDir = path.join(projectDir, 'pages');
68
+ // Load bundled middleware at startup
69
+ const middlewareModules = new Map();
70
+ if (manifest.middlewares) {
71
+ for (const entry of manifest.middlewares) {
72
+ const modPath = path.join(serverDir, entry.module);
73
+ if (fs.existsSync(modPath)) {
74
+ try {
75
+ const mod = await import(modPath);
76
+ middlewareModules.set(entry.dir, extractMiddleware(mod));
77
+ }
78
+ catch (err) {
79
+ logger.error(`Failed to load middleware (${entry.dir || 'root'})`, { error: err?.message });
80
+ }
81
+ }
82
+ }
83
+ }
84
+ const middlewareEntries = manifest.middlewares
85
+ ? manifest.middlewares.map(e => ({ dir: e.dir, filePath: '' }))
86
+ : [];
87
+ // Load auth config if present
88
+ let authConfig = null;
89
+ let authMiddleware = null;
90
+ let authDb = null;
91
+ if (manifest.auth) {
92
+ try {
93
+ authConfig = await loadAuthConfigProd(serverDir, manifest.auth.configModule);
94
+ // Initialize DB for native auth
95
+ const { hasNativeAuth } = await import('../auth/config.js');
96
+ if (hasNativeAuth(authConfig)) {
97
+ try {
98
+ const { setProjectDir } = await import('../db/context.js');
99
+ const { useDb, waitForMigrations } = await import('../db/index.js');
100
+ const { ensureUsersTable } = await import('../auth/native-auth.js');
101
+ setProjectDir(projectDir);
102
+ authDb = useDb();
103
+ await waitForMigrations();
104
+ await ensureUsersTable(authDb);
105
+ // Run seed if not yet applied (SQLite and PG)
106
+ {
107
+ const seedModule = path.join(serverDir, 'seed.js');
108
+ if (fs.existsSync(seedModule)) {
109
+ try {
110
+ if (authDb.isPg) {
111
+ await authDb.exec(`CREATE TABLE IF NOT EXISTS _lumen_seed_applied (
112
+ name TEXT PRIMARY KEY,
113
+ applied_at TIMESTAMP NOT NULL DEFAULT NOW()
114
+ )`);
115
+ }
116
+ else {
117
+ await authDb.exec(`CREATE TABLE IF NOT EXISTS _lumen_seed_applied (
118
+ name TEXT PRIMARY KEY,
119
+ applied_at TEXT NOT NULL DEFAULT (datetime('now'))
120
+ )`);
121
+ }
122
+ const seedRow = authDb.isPg
123
+ ? await authDb.get('SELECT 1 FROM _lumen_seed_applied WHERE name = $1', 'data/seed.ts')
124
+ : await authDb.get('SELECT 1 FROM _lumen_seed_applied WHERE name = ?', 'data/seed.ts');
125
+ if (!seedRow) {
126
+ if (authDb.isPg) {
127
+ await authDb.run('INSERT INTO _lumen_seed_applied (name) VALUES ($1) ON CONFLICT DO NOTHING', 'data/seed.ts');
128
+ }
129
+ else {
130
+ await authDb.run('INSERT OR IGNORE INTO _lumen_seed_applied (name) VALUES (?)', 'data/seed.ts');
131
+ }
132
+ logger.info('Running seed file (prod)...');
133
+ const { default: seedFn } = await import(seedModule);
134
+ if (typeof seedFn === 'function')
135
+ await seedFn();
136
+ logger.info('Seed applied (prod).');
137
+ }
138
+ }
139
+ catch (seedErr) {
140
+ try {
141
+ if (authDb.isPg) {
142
+ await authDb.run('DELETE FROM _lumen_seed_applied WHERE name = $1', 'data/seed.ts');
143
+ }
144
+ else {
145
+ await authDb.run('DELETE FROM _lumen_seed_applied WHERE name = ?', 'data/seed.ts');
146
+ }
147
+ }
148
+ catch { }
149
+ logger.warn('Seed failed', { error: seedErr?.message });
150
+ }
151
+ }
152
+ }
153
+ }
154
+ catch (dbErr) {
155
+ logger.warn('Native auth DB init failed', { error: dbErr?.message });
156
+ }
157
+ }
158
+ authMiddleware = createAuthMiddleware(authConfig, authDb);
159
+ logger.info('Auth middleware loaded.');
160
+ }
161
+ catch (err) {
162
+ logger.error('Failed to load auth config', { error: err?.message });
163
+ }
164
+ }
165
+ const runMiddleware = (mw, req, res) => new Promise((resolve, reject) => mw(req, res, (err) => err ? reject(err) : resolve()));
46
166
  const server = http.createServer(async (req, res) => {
167
+ const startTime = Date.now();
47
168
  const url = req.url || '/';
48
169
  const [pathname, queryString] = url.split('?');
49
170
  const method = req.method || 'GET';
50
171
  try {
172
+ // --- Production middleware pipeline ---
173
+ // Request ID (always first)
174
+ await runMiddleware(requestIdMiddleware, req, res);
175
+ // Health check (bypass everything else)
176
+ let healthHandled = false;
177
+ await new Promise(resolve => healthCheck(req, res, () => { resolve(); }));
178
+ if (res.writableEnded)
179
+ return;
180
+ // Security headers
181
+ await runMiddleware(securityHeaders, req, res);
182
+ // Rate limiting (stricter for auth routes)
183
+ if (pathname.startsWith('/__nk_auth/')) {
184
+ await runMiddleware(authRateLimiter, req, res);
185
+ }
186
+ else {
187
+ await runMiddleware(rateLimiter, req, res);
188
+ }
189
+ if (res.writableEnded)
190
+ return;
191
+ // --- Original request handling ---
192
+ // -2. Auth session middleware (attach req.nkAuth — must run before auth routes and user middleware)
193
+ if (authMiddleware && !pathname.includes('.') && !pathname.startsWith('/@')) {
194
+ await new Promise(resolve => authMiddleware(req, res, resolve));
195
+ if (res.writableEnded)
196
+ return;
197
+ }
198
+ // -1. Auth routes (login, logout, me, signup, etc.)
199
+ if (authConfig && pathname.startsWith('/__nk_auth/')) {
200
+ const handled = await handleAuthRoutes(authConfig, req, res, authDb);
201
+ if (handled)
202
+ return;
203
+ }
204
+ // 0. Run user middleware chain
205
+ if (middlewareModules.size > 0 && !pathname.includes('.') && !pathname.startsWith('/__nk_')) {
206
+ const matching = getMiddlewareDirsForPathname(pathname, middlewareEntries);
207
+ const allMw = [];
208
+ for (const entry of matching) {
209
+ const mws = middlewareModules.get(entry.dir);
210
+ if (mws)
211
+ allMw.push(...mws);
212
+ }
213
+ if (allMw.length > 0) {
214
+ const err = await new Promise(resolve => runMiddlewareChain(allMw, req, res, resolve));
215
+ if (err) {
216
+ res.statusCode = 500;
217
+ res.end('Internal Server Error');
218
+ return;
219
+ }
220
+ if (res.writableEnded)
221
+ return;
222
+ }
223
+ }
51
224
  // 1. API routes
52
225
  if (pathname.startsWith('/api/')) {
53
226
  await handleApiRoute(manifest, serverDir, pathname, queryString, method, req, res);
@@ -66,25 +239,26 @@ export async function serveProject(options) {
66
239
  }
67
240
  // 4. Layout subscribe endpoint (SSE)
68
241
  if (pathname === '/__nk_subscribe/__layout/' || pathname === '/__nk_subscribe/__layout') {
69
- await handleLayoutSubscribeRequest(manifest, serverDir, queryString, req.headers, res);
242
+ const authUser = req.nkAuth?.user ?? undefined;
243
+ await handleLayoutSubscribeRequest(manifest, serverDir, queryString, req.headers, res, authUser);
70
244
  return;
71
245
  }
72
246
  // 5. Subscribe endpoint (SSE)
73
247
  if (pathname.startsWith('/__nk_subscribe/')) {
74
- await handleSubscribeRequest(manifest, serverDir, pagesDir, pathname, queryString, req.headers, res);
248
+ await handleSubscribeRequest(manifest, serverDir, pagesDir, pathname, queryString, req.headers, res, req.nkAuth?.user ?? undefined);
75
249
  return;
76
250
  }
77
251
  // 6. Layout loader endpoint
78
252
  if (pathname === '/__nk_loader/__layout/' || pathname === '/__nk_loader/__layout') {
79
- await handleLayoutLoaderRequest(manifest, serverDir, queryString, req.headers, res);
253
+ await handleLayoutLoaderRequest(manifest, serverDir, queryString, req.headers, res, req.nkAuth?.user ?? undefined);
80
254
  return;
81
255
  }
82
256
  // 7. Loader endpoint for client-side navigation
83
257
  if (pathname.startsWith('/__nk_loader/')) {
84
- await handleLoaderRequest(manifest, serverDir, pagesDir, pathname, queryString, req.headers, res);
258
+ await handleLoaderRequest(manifest, serverDir, pagesDir, pathname, queryString, req.headers, res, req.nkAuth?.user ?? undefined);
85
259
  return;
86
260
  }
87
- // 6. Resolve locale and strip prefix for page routing
261
+ // 8. Resolve locale and strip prefix for page routing
88
262
  let resolvedPathname = pathname;
89
263
  let locale;
90
264
  if (manifest.i18n) {
@@ -92,16 +266,45 @@ export async function serveProject(options) {
92
266
  resolvedPathname = result.pathname;
93
267
  locale = result.locale;
94
268
  }
95
- // 7. Page routes SSR render
269
+ // 9. Check for pre-rendered HTML file
270
+ const prerenderFile = path.join(clientDir, resolvedPathname === '/' ? '' : resolvedPathname, 'index.html');
271
+ if (resolvedPathname !== '/' && fs.existsSync(prerenderFile)) {
272
+ const prerenderHtml = fs.readFileSync(prerenderFile, 'utf-8');
273
+ sendCompressed(req, res, 200, 'text/html; charset=utf-8', prerenderHtml);
274
+ return;
275
+ }
276
+ // Check if the root index.html is a pre-rendered page (has data-nk-ssr attribute)
277
+ if (resolvedPathname === '/') {
278
+ const rootRoute = manifest.routes.find(r => r.path === '/');
279
+ if (rootRoute?.prerender) {
280
+ sendCompressed(req, res, 200, 'text/html; charset=utf-8', indexHtmlShell);
281
+ return;
282
+ }
283
+ }
284
+ // 10. Page routes — SSR render
96
285
  await handlePageRoute(manifest, serverDir, pagesDir, resolvedPathname, queryString, indexHtmlShell, title, ssrRuntime, req, res);
97
286
  }
98
287
  catch (err) {
99
- console.error('[LumenJS] Request error:', err);
288
+ logger.error('Request error', {
289
+ method, url: pathname, error: err?.message, stack: err?.stack,
290
+ requestId: getRequestId(req),
291
+ });
100
292
  const html = renderErrorPage(500, 'Something went wrong', 'An unexpected error occurred while processing your request.', process.env.NODE_ENV !== 'production' ? err?.stack || err?.message : undefined);
101
293
  sendCompressed(req, res, 500, 'text/html; charset=utf-8', html);
102
294
  }
295
+ finally {
296
+ // Log request completion
297
+ const duration = Date.now() - startTime;
298
+ logger.request(req, res.statusCode, duration, { requestId: getRequestId(req) });
299
+ }
300
+ });
301
+ // Graceful shutdown
302
+ setupGracefulShutdown(server, {
303
+ onShutdown: async () => {
304
+ logger.info('Cleaning up resources...');
305
+ },
103
306
  });
104
307
  server.listen(port, () => {
105
- console.log(`[LumenJS] Production server running at http://localhost:${port}`);
308
+ logger.info(`Production server running at http://localhost:${port}`, { port });
106
309
  });
107
310
  }