@nuraly/lumenjs 0.1.2 → 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 (337) hide show
  1. package/README.md +76 -235
  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 +52 -120
  49. package/dist/build/scan.d.ts +19 -0
  50. package/dist/build/scan.js +77 -6
  51. package/dist/build/serve-api.js +8 -2
  52. package/dist/build/serve-loaders.d.ts +4 -2
  53. package/dist/build/serve-loaders.js +128 -10
  54. package/dist/build/serve-ssr.js +38 -11
  55. package/dist/build/serve-static.js +3 -3
  56. package/dist/build/serve.js +229 -14
  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/context.d.ts +2 -0
  93. package/dist/db/context.js +9 -0
  94. package/dist/db/index.d.ts +23 -0
  95. package/dist/db/index.js +258 -0
  96. package/dist/db/seed.d.ts +12 -0
  97. package/dist/db/seed.js +88 -0
  98. package/dist/db/table.d.ts +10 -0
  99. package/dist/db/table.js +12 -0
  100. package/dist/dev-server/config.d.ts +14 -0
  101. package/dist/dev-server/config.js +26 -9
  102. package/dist/dev-server/index-html.d.ts +3 -0
  103. package/dist/dev-server/index-html.js +18 -6
  104. package/dist/dev-server/nuralyui-aliases.d.ts +0 -4
  105. package/dist/dev-server/nuralyui-aliases.js +115 -94
  106. package/dist/dev-server/plugins/vite-plugin-api-routes.js +29 -5
  107. package/dist/dev-server/plugins/vite-plugin-auth.d.ts +6 -0
  108. package/dist/dev-server/plugins/vite-plugin-auth.js +223 -0
  109. package/dist/dev-server/plugins/vite-plugin-auto-define.d.ts +16 -0
  110. package/dist/dev-server/plugins/vite-plugin-auto-define.js +111 -0
  111. package/dist/dev-server/plugins/vite-plugin-communication.d.ts +6 -0
  112. package/dist/dev-server/plugins/vite-plugin-communication.js +205 -0
  113. package/dist/dev-server/plugins/vite-plugin-editor-api.d.ts +6 -0
  114. package/dist/dev-server/plugins/vite-plugin-editor-api.js +318 -0
  115. package/dist/dev-server/plugins/vite-plugin-i18n.js +69 -2
  116. package/dist/dev-server/plugins/vite-plugin-lit-dedup.d.ts +6 -0
  117. package/dist/dev-server/plugins/vite-plugin-lit-dedup.js +78 -34
  118. package/dist/dev-server/plugins/vite-plugin-lit-hmr.js +44 -2
  119. package/dist/dev-server/plugins/vite-plugin-llms.d.ts +2 -0
  120. package/dist/dev-server/plugins/vite-plugin-llms.js +92 -0
  121. package/dist/dev-server/plugins/vite-plugin-loaders.d.ts +0 -1
  122. package/dist/dev-server/plugins/vite-plugin-loaders.js +311 -42
  123. package/dist/dev-server/plugins/vite-plugin-routes.js +18 -6
  124. package/dist/dev-server/plugins/vite-plugin-socketio.d.ts +2 -0
  125. package/dist/dev-server/plugins/vite-plugin-socketio.js +51 -0
  126. package/dist/dev-server/plugins/vite-plugin-source-annotator.d.ts +2 -0
  127. package/dist/dev-server/plugins/vite-plugin-source-annotator.js +26 -3
  128. package/dist/dev-server/plugins/vite-plugin-storage.d.ts +10 -0
  129. package/dist/dev-server/plugins/vite-plugin-storage.js +126 -0
  130. package/dist/dev-server/plugins/vite-plugin-virtual-modules.js +111 -2
  131. package/dist/dev-server/server.js +128 -12
  132. package/dist/dev-server/ssr-render.d.ts +2 -1
  133. package/dist/dev-server/ssr-render.js +107 -48
  134. package/dist/editor/ai/backend.d.ts +20 -0
  135. package/dist/editor/ai/backend.js +104 -0
  136. package/dist/editor/ai/claude-code-client.d.ts +20 -0
  137. package/dist/editor/ai/claude-code-client.js +145 -0
  138. package/dist/editor/ai/opencode-client.d.ts +14 -0
  139. package/dist/editor/ai/opencode-client.js +125 -0
  140. package/dist/editor/ai/snapshot-store.d.ts +22 -0
  141. package/dist/editor/ai/snapshot-store.js +35 -0
  142. package/dist/editor/ai/types.d.ts +30 -0
  143. package/dist/editor/ai/types.js +136 -0
  144. package/dist/editor/ai-chat-panel.d.ts +13 -0
  145. package/dist/editor/ai-chat-panel.js +587 -0
  146. package/dist/editor/ai-markdown.d.ts +10 -0
  147. package/dist/editor/ai-markdown.js +70 -0
  148. package/dist/editor/ai-project-panel.d.ts +11 -0
  149. package/dist/editor/ai-project-panel.js +332 -0
  150. package/dist/editor/ast-modification.d.ts +11 -0
  151. package/dist/editor/ast-modification.js +1 -0
  152. package/dist/editor/ast-service.d.ts +30 -0
  153. package/dist/editor/ast-service.js +180 -0
  154. package/dist/editor/css-rules.d.ts +54 -0
  155. package/dist/editor/css-rules.js +423 -0
  156. package/dist/editor/editor-api-client.d.ts +51 -0
  157. package/dist/editor/editor-api-client.js +162 -0
  158. package/dist/editor/editor-bridge.d.ts +1 -0
  159. package/dist/editor/editor-bridge.js +17 -8
  160. package/dist/editor/editor-toolbar.d.ts +14 -0
  161. package/dist/editor/editor-toolbar.js +115 -0
  162. package/dist/editor/file-editor.d.ts +9 -0
  163. package/dist/editor/file-editor.js +236 -0
  164. package/dist/editor/file-service.d.ts +16 -0
  165. package/dist/editor/file-service.js +52 -0
  166. package/dist/editor/i18n-key-gen.d.ts +1 -0
  167. package/dist/editor/i18n-key-gen.js +7 -0
  168. package/dist/editor/inline-text-edit.d.ts +5 -0
  169. package/dist/editor/inline-text-edit.js +173 -92
  170. package/dist/editor/overlay-events.d.ts +5 -0
  171. package/dist/editor/overlay-events.js +364 -0
  172. package/dist/editor/overlay-hmr.d.ts +2 -0
  173. package/dist/editor/overlay-hmr.js +75 -0
  174. package/dist/editor/overlay-selection.d.ts +29 -0
  175. package/dist/editor/overlay-selection.js +148 -0
  176. package/dist/editor/overlay-utils.d.ts +12 -0
  177. package/dist/editor/overlay-utils.js +59 -0
  178. package/dist/editor/properties-panel-persist.d.ts +14 -0
  179. package/dist/editor/properties-panel-persist.js +70 -0
  180. package/dist/editor/properties-panel-rows.d.ts +10 -0
  181. package/dist/editor/properties-panel-rows.js +349 -0
  182. package/dist/editor/properties-panel-styles.d.ts +4 -0
  183. package/dist/editor/properties-panel-styles.js +174 -0
  184. package/dist/editor/properties-panel.d.ts +4 -0
  185. package/dist/editor/properties-panel.js +148 -0
  186. package/dist/editor/property-registry.d.ts +16 -0
  187. package/dist/editor/property-registry.js +303 -0
  188. package/dist/editor/standalone-file-panel.d.ts +0 -0
  189. package/dist/editor/standalone-file-panel.js +1 -0
  190. package/dist/editor/standalone-overlay-dom.d.ts +0 -0
  191. package/dist/editor/standalone-overlay-dom.js +1 -0
  192. package/dist/editor/standalone-overlay-styles.d.ts +0 -0
  193. package/dist/editor/standalone-overlay-styles.js +1 -0
  194. package/dist/editor/standalone-overlay.d.ts +1 -0
  195. package/dist/editor/standalone-overlay.js +76 -0
  196. package/dist/editor/syntax-highlighter.d.ts +4 -0
  197. package/dist/editor/syntax-highlighter.js +81 -0
  198. package/dist/editor/text-toolbar.d.ts +11 -0
  199. package/dist/editor/text-toolbar.js +327 -0
  200. package/dist/editor/toolbar-styles.d.ts +4 -0
  201. package/dist/editor/toolbar-styles.js +198 -0
  202. package/dist/email/index.d.ts +32 -0
  203. package/dist/email/index.js +154 -0
  204. package/dist/email/providers/resend.d.ts +2 -0
  205. package/dist/email/providers/resend.js +24 -0
  206. package/dist/email/providers/sendgrid.d.ts +2 -0
  207. package/dist/email/providers/sendgrid.js +31 -0
  208. package/dist/email/providers/smtp.d.ts +13 -0
  209. package/dist/email/providers/smtp.js +125 -0
  210. package/dist/email/template-engine.d.ts +18 -0
  211. package/dist/email/template-engine.js +116 -0
  212. package/dist/email/templates/base.d.ts +9 -0
  213. package/dist/email/templates/base.js +65 -0
  214. package/dist/email/templates/password-reset.d.ts +5 -0
  215. package/dist/email/templates/password-reset.js +15 -0
  216. package/dist/email/templates/verify-email.d.ts +5 -0
  217. package/dist/email/templates/verify-email.js +15 -0
  218. package/dist/email/templates/welcome.d.ts +5 -0
  219. package/dist/email/templates/welcome.js +13 -0
  220. package/dist/email/types.d.ts +49 -0
  221. package/dist/email/types.js +1 -0
  222. package/dist/llms/generate.d.ts +46 -0
  223. package/dist/llms/generate.js +185 -0
  224. package/dist/permissions/guard.d.ts +28 -0
  225. package/dist/permissions/guard.js +30 -0
  226. package/dist/permissions/index.d.ts +6 -0
  227. package/dist/permissions/index.js +3 -0
  228. package/dist/permissions/service.d.ts +80 -0
  229. package/dist/permissions/service.js +210 -0
  230. package/dist/permissions/tables.d.ts +5 -0
  231. package/dist/permissions/tables.js +68 -0
  232. package/dist/permissions/types.d.ts +33 -0
  233. package/dist/permissions/types.js +1 -0
  234. package/dist/runtime/app-shell.js +163 -0
  235. package/dist/runtime/auth.d.ts +10 -0
  236. package/dist/runtime/auth.js +30 -0
  237. package/dist/runtime/communication.d.ts +137 -0
  238. package/dist/runtime/communication.js +228 -0
  239. package/dist/runtime/error-boundary.d.ts +23 -0
  240. package/dist/runtime/error-boundary.js +120 -0
  241. package/dist/runtime/i18n.d.ts +6 -1
  242. package/dist/runtime/i18n.js +42 -21
  243. package/dist/runtime/router-data.d.ts +5 -0
  244. package/dist/runtime/router-data.js +121 -16
  245. package/dist/runtime/router-hydration.js +25 -0
  246. package/dist/runtime/router.d.ts +21 -1
  247. package/dist/runtime/router.js +221 -39
  248. package/dist/runtime/socket-client.d.ts +2 -0
  249. package/dist/runtime/socket-client.js +30 -0
  250. package/dist/runtime/webrtc.d.ts +47 -0
  251. package/dist/runtime/webrtc.js +178 -0
  252. package/dist/shared/graceful-shutdown.d.ts +8 -0
  253. package/dist/shared/graceful-shutdown.js +36 -0
  254. package/dist/shared/health.d.ts +8 -0
  255. package/dist/shared/health.js +25 -0
  256. package/dist/shared/llms-txt.d.ts +31 -0
  257. package/dist/shared/llms-txt.js +85 -0
  258. package/dist/shared/logger.d.ts +32 -0
  259. package/dist/shared/logger.js +93 -0
  260. package/dist/shared/meta.d.ts +27 -0
  261. package/dist/shared/meta.js +71 -0
  262. package/dist/shared/middleware-runner.d.ts +9 -0
  263. package/dist/shared/middleware-runner.js +29 -0
  264. package/dist/shared/rate-limit.d.ts +18 -0
  265. package/dist/shared/rate-limit.js +71 -0
  266. package/dist/shared/request-id.d.ts +5 -0
  267. package/dist/shared/request-id.js +18 -0
  268. package/dist/shared/route-matching.js +16 -1
  269. package/dist/shared/security-headers.d.ts +18 -0
  270. package/dist/shared/security-headers.js +38 -0
  271. package/dist/shared/socket-io-setup.d.ts +11 -0
  272. package/dist/shared/socket-io-setup.js +51 -0
  273. package/dist/shared/types.d.ts +16 -0
  274. package/dist/shared/utils.d.ts +37 -7
  275. package/dist/shared/utils.js +175 -26
  276. package/dist/storage/adapters/local.d.ts +44 -0
  277. package/dist/storage/adapters/local.js +85 -0
  278. package/dist/storage/adapters/s3.d.ts +32 -0
  279. package/dist/storage/adapters/s3.js +116 -0
  280. package/dist/storage/adapters/types.d.ts +53 -0
  281. package/dist/storage/adapters/types.js +1 -0
  282. package/dist/storage/index.d.ts +76 -0
  283. package/dist/storage/index.js +83 -0
  284. package/package.json +20 -1
  285. package/templates/blog/api/posts.ts +6 -0
  286. package/templates/blog/data/migrations/001_init.sql +13 -0
  287. package/templates/blog/lumenjs.config.ts +3 -0
  288. package/templates/blog/package.json +14 -0
  289. package/templates/blog/pages/_layout.ts +25 -0
  290. package/templates/blog/pages/index.ts +65 -0
  291. package/templates/blog/pages/posts/[slug].ts +60 -0
  292. package/templates/blog/pages/tag/[tag].ts +44 -0
  293. package/templates/dashboard/api/stats.ts +10 -0
  294. package/templates/dashboard/data/migrations/001_init.sql +13 -0
  295. package/templates/dashboard/lumenjs.config.ts +3 -0
  296. package/templates/dashboard/package.json +14 -0
  297. package/templates/dashboard/pages/_layout.ts +25 -0
  298. package/templates/dashboard/pages/index.ts +72 -0
  299. package/templates/dashboard/pages/settings/index.ts +29 -0
  300. package/templates/default/lumenjs.config.ts +3 -0
  301. package/templates/default/package.json +14 -0
  302. package/templates/default/pages/index.ts +24 -0
  303. package/templates/social/api/posts/[id].ts +14 -0
  304. package/templates/social/api/posts.ts +11 -0
  305. package/templates/social/api/profile/[username].ts +10 -0
  306. package/templates/social/api/upload.ts +19 -0
  307. package/templates/social/data/migrations/001_init.sql +78 -0
  308. package/templates/social/data/migrations/002_add_image_url.sql +1 -0
  309. package/templates/social/data/migrations/003_auth.sql +7 -0
  310. package/templates/social/docs/architecture.md +76 -0
  311. package/templates/social/docs/components.md +100 -0
  312. package/templates/social/docs/data.md +89 -0
  313. package/templates/social/docs/pages.md +96 -0
  314. package/templates/social/docs/theming.md +52 -0
  315. package/templates/social/lib/media.ts +130 -0
  316. package/templates/social/lumenjs.auth.ts +21 -0
  317. package/templates/social/lumenjs.config.ts +3 -0
  318. package/templates/social/package.json +5 -0
  319. package/templates/social/pages/_layout.ts +239 -0
  320. package/templates/social/pages/apps/[id].ts +173 -0
  321. package/templates/social/pages/apps/index.ts +116 -0
  322. package/templates/social/pages/auth/login.ts +92 -0
  323. package/templates/social/pages/bookmarks.ts +57 -0
  324. package/templates/social/pages/explore.ts +73 -0
  325. package/templates/social/pages/index.ts +351 -0
  326. package/templates/social/pages/messages.ts +298 -0
  327. package/templates/social/pages/new.ts +77 -0
  328. package/templates/social/pages/notifications.ts +73 -0
  329. package/templates/social/pages/post/[id].ts +124 -0
  330. package/templates/social/pages/profile/[username].ts +100 -0
  331. package/templates/social/pages/settings/accessibility.ts +153 -0
  332. package/templates/social/pages/settings/account.ts +260 -0
  333. package/templates/social/pages/settings/help.ts +141 -0
  334. package/templates/social/pages/settings/language.ts +103 -0
  335. package/templates/social/pages/settings/privacy.ts +183 -0
  336. package/templates/social/pages/settings/security.ts +133 -0
  337. package/templates/social/pages/settings.ts +185 -0
@@ -2,6 +2,7 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { readBody } from '../shared/utils.js';
4
4
  import { matchRoute } from '../shared/route-matching.js';
5
+ import { useStorage } from '../storage/index.js';
5
6
  export async function handleApiRoute(manifest, serverDir, pathname, queryString, method, req, res) {
6
7
  const matched = matchRoute(manifest.apiRoutes, pathname);
7
8
  if (!matched) {
@@ -9,7 +10,9 @@ export async function handleApiRoute(manifest, serverDir, pathname, queryString,
9
10
  res.end(JSON.stringify({ error: 'API route not found' }));
10
11
  return;
11
12
  }
12
- const modulePath = path.join(serverDir, matched.route.module);
13
+ // Vite replaces [param] with _param_ in output filenames
14
+ const resolvedModule = matched.route.module.replace(/\[([^\]]+)\]/g, '_$1_');
15
+ const modulePath = path.join(serverDir, resolvedModule);
13
16
  if (!fs.existsSync(modulePath)) {
14
17
  res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
15
18
  res.end(JSON.stringify({ error: 'API route module not found' }));
@@ -43,13 +46,16 @@ export async function handleApiRoute(manifest, serverDir, pathname, queryString,
43
46
  params: matched.params,
44
47
  body,
45
48
  headers: req.headers,
49
+ storage: useStorage(),
50
+ nkAuth: req.nkAuth,
46
51
  });
47
52
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
48
53
  res.end(JSON.stringify(result));
49
54
  }
50
55
  catch (err) {
51
56
  const status = err?.status || 500;
52
- const message = err?.message || 'Internal server error';
57
+ // Only expose error messages for client errors (4xx); hide internals for 5xx
58
+ const message = status < 500 ? (err?.message || 'Request error') : 'Internal server error';
53
59
  res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
54
60
  res.end(JSON.stringify({ error: message }));
55
61
  }
@@ -1,4 +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 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,124 @@ 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 handleLoaderRequest(manifest, serverDir, pagesDir, pathname, queryString, headers, res) {
67
+ export async function handleLayoutSubscribeRequest(manifest, serverDir, queryString, headers, res, user) {
68
+ const query = {};
69
+ if (queryString) {
70
+ for (const pair of queryString.split('&')) {
71
+ const [key, val] = pair.split('=');
72
+ query[decodeURIComponent(key)] = decodeURIComponent(val || '');
73
+ }
74
+ }
75
+ const dir = query.__dir || '';
76
+ const layout = (manifest.layouts || []).find(l => l.dir === dir);
77
+ if (!layout || !layout.hasSubscribe || !layout.module) {
78
+ res.writeHead(204);
79
+ res.end();
80
+ return;
81
+ }
82
+ const modulePath = resolveModulePath(serverDir, layout.module);
83
+ if (!fs.existsSync(modulePath)) {
84
+ res.writeHead(204);
85
+ res.end();
86
+ return;
87
+ }
88
+ try {
89
+ const mod = await import(modulePath);
90
+ if (!mod.subscribe || typeof mod.subscribe !== 'function') {
91
+ res.writeHead(204);
92
+ res.end();
93
+ return;
94
+ }
95
+ res.writeHead(200, {
96
+ 'Content-Type': 'text/event-stream',
97
+ 'Cache-Control': 'no-cache',
98
+ 'Connection': 'keep-alive',
99
+ });
100
+ const locale = query.__locale;
101
+ const push = (data) => {
102
+ res.write(`data: ${JSON.stringify(data)}\n\n`);
103
+ };
104
+ const cleanup = mod.subscribe({ params: {}, push, headers, locale, user: user ?? null });
105
+ res.on('close', () => {
106
+ if (typeof cleanup === 'function')
107
+ cleanup();
108
+ });
109
+ }
110
+ catch (err) {
111
+ logger.error(`Layout subscribe error`, { dir, error: err?.message });
112
+ if (!res.headersSent) {
113
+ res.writeHead(500);
114
+ res.end();
115
+ }
116
+ }
117
+ }
118
+ export async function handleSubscribeRequest(manifest, serverDir, pagesDir, pathname, queryString, headers, res, user) {
119
+ const pagePath = pathname.replace('/__nk_subscribe', '') || '/';
120
+ const query = {};
121
+ if (queryString) {
122
+ for (const pair of queryString.split('&')) {
123
+ const [key, val] = pair.split('=');
124
+ query[decodeURIComponent(key)] = decodeURIComponent(val || '');
125
+ }
126
+ }
127
+ let params = {};
128
+ if (query.__params) {
129
+ try {
130
+ params = JSON.parse(query.__params);
131
+ }
132
+ catch { /* ignore */ }
133
+ delete query.__params;
134
+ }
135
+ const matched = matchRoute(manifest.routes.filter(r => r.hasSubscribe), pagePath);
136
+ if (!matched || !matched.route.module) {
137
+ res.writeHead(204);
138
+ res.end();
139
+ return;
140
+ }
141
+ const modulePath = resolveModulePath(serverDir, matched.route.module);
142
+ if (!fs.existsSync(modulePath)) {
143
+ res.writeHead(204);
144
+ res.end();
145
+ return;
146
+ }
147
+ try {
148
+ const mod = await import(modulePath);
149
+ if (!mod.subscribe || typeof mod.subscribe !== 'function') {
150
+ res.writeHead(204);
151
+ res.end();
152
+ return;
153
+ }
154
+ res.writeHead(200, {
155
+ 'Content-Type': 'text/event-stream',
156
+ 'Cache-Control': 'no-cache',
157
+ 'Connection': 'keep-alive',
158
+ });
159
+ const locale = query.__locale;
160
+ const push = (data) => {
161
+ res.write(`data: ${JSON.stringify(data)}\n\n`);
162
+ };
163
+ const cleanup = mod.subscribe({ params: matched.params, push, headers, locale, user: user ?? null });
164
+ res.on('close', () => {
165
+ if (typeof cleanup === 'function')
166
+ cleanup();
167
+ });
168
+ }
169
+ catch (err) {
170
+ logger.error(`Subscribe error`, { pagePath, error: err?.message });
171
+ if (!res.headersSent) {
172
+ res.writeHead(500);
173
+ res.end();
174
+ }
175
+ }
176
+ }
177
+ export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathname, queryString, headers, res, user) {
60
178
  const pagePath = pathname.replace('/__nk_loader', '') || '/';
61
179
  // Parse query params
62
180
  const query = {};
@@ -81,7 +199,7 @@ export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathnam
81
199
  res.end(JSON.stringify({ __nk_no_loader: true }));
82
200
  return;
83
201
  }
84
- const modulePath = path.join(serverDir, matched.route.module);
202
+ const modulePath = resolveModulePath(serverDir, matched.route.module);
85
203
  if (!fs.existsSync(modulePath)) {
86
204
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
87
205
  res.end(JSON.stringify({ __nk_no_loader: true }));
@@ -96,7 +214,7 @@ export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathnam
96
214
  }
97
215
  const locale = query.__locale;
98
216
  delete query.__locale;
99
- 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 });
100
218
  if (isRedirectResponse(result)) {
101
219
  res.writeHead(result.status || 302, { Location: result.location });
102
220
  res.end();
@@ -111,9 +229,9 @@ export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathnam
111
229
  res.end();
112
230
  return;
113
231
  }
114
- console.error(`[LumenJS] Loader error for ${pagePath}:`, err);
232
+ logger.error(`Loader error`, { pagePath, error: err?.message });
115
233
  const status = err?.status || 500;
116
- const message = err?.message || 'Loader failed';
234
+ const message = status < 500 ? (err?.message || 'Loader failed') : 'Internal server error';
117
235
  res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
118
236
  res.end(JSON.stringify({ error: message }));
119
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()) {