@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
@@ -5,11 +5,23 @@ import { readProjectConfig } from '../dev-server/config.js';
5
5
  import { installDomShims } from '../shared/dom-shims.js';
6
6
  import { serveStaticFile, sendCompressed } from './serve-static.js';
7
7
  import { handleApiRoute } from './serve-api.js';
8
- import { handleLoaderRequest, handleLayoutLoaderRequest } from './serve-loaders.js';
8
+ import { handleLoaderRequest, handleLayoutLoaderRequest, handleSubscribeRequest, handleLayoutSubscribeRequest } from './serve-loaders.js';
9
9
  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 { 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';
13
25
  export async function serveProject(options) {
14
26
  const { projectDir } = options;
15
27
  const port = options.port || 3000;
@@ -17,35 +29,198 @@ export async function serveProject(options) {
17
29
  const clientDir = path.join(outDir, 'client');
18
30
  const serverDir = path.join(outDir, 'server');
19
31
  const manifestPath = path.join(outDir, 'manifest.json');
32
+ // Initialize structured logging
33
+ initLogger();
20
34
  if (!fs.existsSync(manifestPath)) {
21
- console.error('[LumenJS] No build found. Run `lumenjs build` first.');
35
+ logger.fatal('No build found. Run `lumenjs build` first.');
22
36
  process.exit(1);
23
37
  }
24
38
  const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
25
- const { title } = readProjectConfig(projectDir);
39
+ const config = readProjectConfig(projectDir);
40
+ const { title } = config;
26
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 });
27
48
  // Read the built index.html shell
28
49
  const indexHtmlPath = path.join(clientDir, 'index.html');
29
50
  if (!fs.existsSync(indexHtmlPath)) {
30
- console.error('[LumenJS] No index.html found in build output.');
51
+ logger.fatal('No index.html found in build output.');
31
52
  process.exit(1);
32
53
  }
33
54
  const indexHtmlShell = fs.readFileSync(indexHtmlPath, 'utf-8');
34
- // Load bundled SSR runtime first — it installs @lit-labs/ssr DOM shim
35
- // 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.
36
59
  const ssrRuntimePath = path.join(serverDir, 'ssr-runtime.js');
37
60
  let ssrRuntime = null;
38
61
  if (fs.existsSync(ssrRuntimePath)) {
39
62
  ssrRuntime = await import(ssrRuntimePath);
40
63
  }
41
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)
42
66
  installDomShims();
43
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()));
44
166
  const server = http.createServer(async (req, res) => {
167
+ const startTime = Date.now();
45
168
  const url = req.url || '/';
46
169
  const [pathname, queryString] = url.split('?');
47
170
  const method = req.method || 'GET';
48
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
+ }
49
224
  // 1. API routes
50
225
  if (pathname.startsWith('/api/')) {
51
226
  await handleApiRoute(manifest, serverDir, pathname, queryString, method, req, res);
@@ -62,17 +237,28 @@ export async function serveProject(options) {
62
237
  handleI18nRequest(localesDir, manifest.i18n.locales, pathname, req, res);
63
238
  return;
64
239
  }
65
- // 4. Layout loader endpoint
240
+ // 4. Layout subscribe endpoint (SSE)
241
+ if (pathname === '/__nk_subscribe/__layout/' || pathname === '/__nk_subscribe/__layout') {
242
+ const authUser = req.nkAuth?.user ?? undefined;
243
+ await handleLayoutSubscribeRequest(manifest, serverDir, queryString, req.headers, res, authUser);
244
+ return;
245
+ }
246
+ // 5. Subscribe endpoint (SSE)
247
+ if (pathname.startsWith('/__nk_subscribe/')) {
248
+ await handleSubscribeRequest(manifest, serverDir, pagesDir, pathname, queryString, req.headers, res, req.nkAuth?.user ?? undefined);
249
+ return;
250
+ }
251
+ // 6. Layout loader endpoint
66
252
  if (pathname === '/__nk_loader/__layout/' || pathname === '/__nk_loader/__layout') {
67
- await handleLayoutLoaderRequest(manifest, serverDir, queryString, req.headers, res);
253
+ await handleLayoutLoaderRequest(manifest, serverDir, queryString, req.headers, res, req.nkAuth?.user ?? undefined);
68
254
  return;
69
255
  }
70
- // 5. Loader endpoint for client-side navigation
256
+ // 7. Loader endpoint for client-side navigation
71
257
  if (pathname.startsWith('/__nk_loader/')) {
72
- 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);
73
259
  return;
74
260
  }
75
- // 6. Resolve locale and strip prefix for page routing
261
+ // 8. Resolve locale and strip prefix for page routing
76
262
  let resolvedPathname = pathname;
77
263
  let locale;
78
264
  if (manifest.i18n) {
@@ -80,16 +266,45 @@ export async function serveProject(options) {
80
266
  resolvedPathname = result.pathname;
81
267
  locale = result.locale;
82
268
  }
83
- // 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
84
285
  await handlePageRoute(manifest, serverDir, pagesDir, resolvedPathname, queryString, indexHtmlShell, title, ssrRuntime, req, res);
85
286
  }
86
287
  catch (err) {
87
- 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
+ });
88
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);
89
293
  sendCompressed(req, res, 500, 'text/html; charset=utf-8', html);
90
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
+ },
91
306
  });
92
307
  server.listen(port, () => {
93
- console.log(`[LumenJS] Production server running at http://localhost:${port}`);
308
+ logger.info(`Production server running at http://localhost:${port}`, { port });
94
309
  });
95
310
  }
package/dist/cli.js CHANGED
@@ -9,19 +9,32 @@ function getArg(name) {
9
9
  return undefined;
10
10
  }
11
11
  const USAGE = `Usage:
12
- lumenjs dev [--project <dir>] [--port <port>] [--base <path>] [--editor-mode]
13
- lumenjs build [--project <dir>] [--out <dir>]
14
- lumenjs serve [--project <dir>] [--port <port>]
15
- lumenjs add <integration>`;
16
- if (!command || !['dev', 'build', 'serve', 'add'].includes(command)) {
12
+ lumenjs create <name> [--template <default|blog|dashboard|social>]
13
+ lumenjs dev [--project <dir>] [--port <port>] [--base <path>] [--editor-mode]
14
+ lumenjs build [--project <dir>] [--out <dir>]
15
+ lumenjs serve [--project <dir>] [--port <port>]
16
+ lumenjs add <integration>
17
+ lumenjs db seed [--force] [--project <dir>]`;
18
+ if (!command || !['create', 'dev', 'build', 'serve', 'add', 'db'].includes(command)) {
17
19
  console.error(USAGE);
18
20
  process.exit(1);
19
21
  }
20
22
  const projectDir = path.resolve(getArg('project') || '.');
21
23
  async function main() {
22
- if (command === 'dev') {
24
+ if (command === 'create') {
25
+ const { createProject } = await import('./create.js');
26
+ const name = args[1];
27
+ const template = getArg('template') || 'default';
28
+ await createProject(name, template);
29
+ return;
30
+ }
31
+ else if (command === 'dev') {
23
32
  const { createDevServer } = await import('./dev-server/server.js');
24
33
  const port = parseInt(getArg('port') || '3000', 10);
34
+ if (isNaN(port) || port < 1 || port > 65535) {
35
+ console.error(`Invalid port number. Must be between 1 and 65535.`);
36
+ process.exit(1);
37
+ }
25
38
  const editorMode = args.includes('--editor-mode');
26
39
  const base = getArg('base') || '/';
27
40
  console.log(`[LumenJS] Starting dev server...`);
@@ -53,11 +66,29 @@ async function main() {
53
66
  else if (command === 'serve') {
54
67
  const { serveProject } = await import('./build/serve.js');
55
68
  const port = parseInt(getArg('port') || '3000', 10);
69
+ if (isNaN(port) || port < 1 || port > 65535) {
70
+ console.error(`Invalid port number. Must be between 1 and 65535.`);
71
+ process.exit(1);
72
+ }
56
73
  console.log(`[LumenJS] Starting production server...`);
57
74
  console.log(` Project: ${projectDir}`);
58
75
  console.log(` Port: ${port}`);
59
76
  await serveProject({ projectDir, port });
60
77
  }
78
+ else if (command === 'db') {
79
+ const subcommand = args[1];
80
+ if (subcommand === 'seed') {
81
+ const { setProjectDir } = await import('./db/context.js');
82
+ const { runSeed } = await import('./db/seed.js');
83
+ setProjectDir(projectDir);
84
+ const force = args.includes('--force');
85
+ await runSeed(projectDir, force);
86
+ }
87
+ else {
88
+ console.error(`Unknown db subcommand: ${subcommand}\n\n${USAGE}`);
89
+ process.exit(1);
90
+ }
91
+ }
61
92
  }
62
93
  main().catch(err => {
63
94
  console.error('[LumenJS] Failed to start:', err);
@@ -0,0 +1,35 @@
1
+ import type { KeyBundle, EncryptedEnvelope } from './types.js';
2
+ import type { CommunicationStore } from './store.js';
3
+ import type { LumenDb } from '../db/index.js';
4
+ /** Context for encryption handlers */
5
+ export interface EncryptionContext {
6
+ userId: string;
7
+ store: CommunicationStore;
8
+ /** Emit data to the current socket */
9
+ push: (data: any) => void;
10
+ /** Emit to a specific user's sockets */
11
+ emitToUser: (targetUserId: string, data: any) => void;
12
+ /** LumenJS database instance (optional) */
13
+ db?: LumenDb;
14
+ }
15
+ /**
16
+ * Handle a client uploading their public key bundle.
17
+ * Server stores the bundle — it never sees private keys.
18
+ */
19
+ export declare function handleUploadKeys(ctx: EncryptionContext, data: KeyBundle): Promise<void>;
20
+ /**
21
+ * Handle a client requesting another user's key bundle for session setup.
22
+ * Pops one one-time pre-key (consumed once).
23
+ */
24
+ export declare function handleRequestKeys(ctx: EncryptionContext, data: {
25
+ recipientId: string;
26
+ }): Promise<void>;
27
+ /**
28
+ * Relay a session initialization message to the recipient.
29
+ * The server never reads the envelope — just forwards it.
30
+ */
31
+ export declare function handleSessionInit(ctx: EncryptionContext, data: {
32
+ recipientId: string;
33
+ sessionId: string;
34
+ envelope: EncryptedEnvelope;
35
+ }): void;
@@ -0,0 +1,90 @@
1
+ // ── Key Upload ──────────────────────────────────────────────────
2
+ /**
3
+ * Handle a client uploading their public key bundle.
4
+ * Server stores the bundle — it never sees private keys.
5
+ */
6
+ export async function handleUploadKeys(ctx, data) {
7
+ // Ensure the bundle belongs to the sender
8
+ const bundle = {
9
+ ...data,
10
+ userId: ctx.userId,
11
+ uploadedAt: new Date().toISOString(),
12
+ };
13
+ // Store in memory
14
+ ctx.store.setKeyBundle(ctx.userId, bundle);
15
+ // Persist to DB if available
16
+ if (ctx.db) {
17
+ await ctx.db.run(`INSERT OR REPLACE INTO encryption_keys (user_id, identity_key, signed_pre_key_id, signed_pre_key, signed_pre_key_signature, uploaded_at)
18
+ VALUES (?, ?, ?, ?, ?, ?)`, ctx.userId, bundle.identityKey, bundle.signedPreKey.keyId, bundle.signedPreKey.publicKey, bundle.signedPreKeySignature, bundle.uploadedAt);
19
+ // Replace one-time pre-keys
20
+ await ctx.db.run(`DELETE FROM encryption_prekeys WHERE user_id = ?`, ctx.userId);
21
+ for (const otk of bundle.oneTimePreKeys) {
22
+ await ctx.db.run(`INSERT INTO encryption_prekeys (user_id, key_id, public_key) VALUES (?, ?, ?)`, ctx.userId, otk.keyId, otk.publicKey);
23
+ }
24
+ }
25
+ ctx.push({ event: 'encryption:keys-uploaded', data: { userId: ctx.userId, keyCount: bundle.oneTimePreKeys.length } });
26
+ }
27
+ // ── Key Request ─────────────────────────────────────────────────
28
+ /**
29
+ * Handle a client requesting another user's key bundle for session setup.
30
+ * Pops one one-time pre-key (consumed once).
31
+ */
32
+ export async function handleRequestKeys(ctx, data) {
33
+ let bundle = ctx.store.getKeyBundle(data.recipientId);
34
+ // Try loading from DB if not in memory
35
+ if (!bundle && ctx.db) {
36
+ const row = await ctx.db.get(`SELECT * FROM encryption_keys WHERE user_id = ?`, data.recipientId);
37
+ if (row) {
38
+ const prekeys = await ctx.db.all(`SELECT key_id, public_key FROM encryption_prekeys WHERE user_id = ? ORDER BY key_id`, data.recipientId);
39
+ bundle = {
40
+ userId: data.recipientId,
41
+ identityKey: row.identity_key,
42
+ signedPreKey: { keyId: row.signed_pre_key_id, publicKey: row.signed_pre_key },
43
+ signedPreKeySignature: row.signed_pre_key_signature,
44
+ oneTimePreKeys: prekeys.map((pk) => ({ keyId: pk.key_id, publicKey: pk.public_key })),
45
+ uploadedAt: row.uploaded_at,
46
+ };
47
+ ctx.store.setKeyBundle(data.recipientId, bundle);
48
+ }
49
+ }
50
+ if (!bundle) {
51
+ ctx.push({ event: 'encryption:keys-response', data: { error: 'no_keys', recipientId: data.recipientId } });
52
+ return;
53
+ }
54
+ // Pop one one-time pre-key (consumed once per session)
55
+ const oneTimePreKey = ctx.store.popOneTimePreKey(data.recipientId);
56
+ // Remove from DB too
57
+ if (oneTimePreKey && ctx.db) {
58
+ await ctx.db.run(`DELETE FROM encryption_prekeys WHERE user_id = ? AND key_id = ?`, data.recipientId, oneTimePreKey.keyId);
59
+ }
60
+ const response = {
61
+ recipientId: data.recipientId,
62
+ identityKey: bundle.identityKey,
63
+ signedPreKey: bundle.signedPreKey,
64
+ signedPreKeySignature: bundle.signedPreKeySignature,
65
+ oneTimePreKey,
66
+ };
67
+ ctx.push({ event: 'encryption:keys-response', data: response });
68
+ // Notify recipient if their one-time pre-keys are depleted
69
+ if (ctx.store.getOneTimePreKeyCount(data.recipientId) === 0) {
70
+ ctx.emitToUser(data.recipientId, {
71
+ event: 'encryption:keys-depleted',
72
+ data: { userId: data.recipientId },
73
+ });
74
+ }
75
+ }
76
+ // ── Session Init Relay ──────────────────────────────────────────
77
+ /**
78
+ * Relay a session initialization message to the recipient.
79
+ * The server never reads the envelope — just forwards it.
80
+ */
81
+ export function handleSessionInit(ctx, data) {
82
+ ctx.emitToUser(data.recipientId, {
83
+ event: 'encryption:session-init',
84
+ data: {
85
+ senderId: ctx.userId,
86
+ sessionId: data.sessionId,
87
+ envelope: data.envelope,
88
+ },
89
+ });
90
+ }
@@ -0,0 +1,27 @@
1
+ import type { CommunicationConfig } from '../types.js';
2
+ import type { CommunicationStore } from '../store.js';
3
+ import type { LumenDb } from '../../db/index.js';
4
+ import type { StorageAdapter } from '../../storage/adapters/types.js';
5
+ /** Context passed to every handler from the socket connection */
6
+ export interface HandlerContext {
7
+ userId: string;
8
+ store: CommunicationStore;
9
+ /** Resolved communication config */
10
+ config: CommunicationConfig;
11
+ /** Emit data to the current socket */
12
+ push: (data: any) => void;
13
+ /** Broadcast to all sockets in a room */
14
+ broadcastAll: (room: string, data: any) => void;
15
+ /** Broadcast to all sockets in a room except the sender */
16
+ broadcast: (room: string, data: any) => void;
17
+ /** Join a socket room */
18
+ joinRoom: (room: string) => void;
19
+ /** Leave a socket room */
20
+ leaveRoom: (room: string) => void;
21
+ /** Emit data to all sockets for a specific user */
22
+ emitToUser?: (userId: string, data: any) => void;
23
+ /** LumenJS database instance (optional — only if app has DB set up) */
24
+ db?: LumenDb;
25
+ /** Storage adapter for file uploads (optional — only if storage is configured) */
26
+ storage?: StorageAdapter;
27
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ import type { HandlerContext } from './context.js';
2
+ export declare function handleConversationCreate(ctx: HandlerContext, data: {
3
+ type: 'direct' | 'group';
4
+ name?: string;
5
+ participantIds: string[];
6
+ }): Promise<void>;
7
+ export declare function handleConversationJoin(ctx: HandlerContext, data: {
8
+ conversationId: string;
9
+ }): Promise<void>;
10
+ export declare function handleConversationLeave(ctx: HandlerContext, data: {
11
+ conversationId: string;
12
+ }): void;
13
+ export declare function handleConversationArchive(ctx: HandlerContext, data: {
14
+ conversationId: string;
15
+ archived: boolean;
16
+ }): Promise<void>;
17
+ export declare function handleConversationMute(ctx: HandlerContext, data: {
18
+ conversationId: string;
19
+ muted: boolean;
20
+ }): Promise<void>;
21
+ export declare function handleConversationPin(ctx: HandlerContext, data: {
22
+ conversationId: string;
23
+ pinned: boolean;
24
+ }): Promise<void>;