@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
@@ -0,0 +1,126 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ import { LocalStorageAdapter } from '../../storage/adapters/local.js';
4
+ import { setStorage } from '../../storage/index.js';
5
+ /**
6
+ * LumenJS storage plugin (dev mode).
7
+ *
8
+ * - Creates a LocalStorageAdapter pointing to `{projectDir}/uploads`
9
+ * - Registers it as the global storage singleton (`useStorage()`)
10
+ * - Serves uploaded files at `/uploads/*`
11
+ * - Handles presigned PUT requests at `/__nk_storage/upload/:token`
12
+ */
13
+ export function lumenStoragePlugin(projectDir) {
14
+ const uploadDir = path.join(projectDir, 'uploads');
15
+ const adapter = new LocalStorageAdapter({ uploadDir, publicPath: '/uploads' });
16
+ // Register global singleton so API routes and communication handlers can use it
17
+ setStorage(adapter);
18
+ return {
19
+ name: 'lumenjs-storage',
20
+ configureServer(server) {
21
+ // ── Presigned upload endpoint ──────────────────────────────
22
+ // Client sends a PUT to /__nk_storage/upload/:token with the (possibly
23
+ // encrypted) file body. The token was issued via adapter.presignPut().
24
+ server.middlewares.use('/__nk_storage/upload', async (req, res, next) => {
25
+ if (req.method !== 'PUT')
26
+ return next();
27
+ const token = req.url?.replace(/^\//, '').split('?')[0];
28
+ if (!token) {
29
+ res.statusCode = 400;
30
+ res.end(JSON.stringify({ error: 'missing token' }));
31
+ return;
32
+ }
33
+ const pending = adapter.consumeUpload(token);
34
+ if (!pending) {
35
+ res.statusCode = 410; // Gone — expired or unknown token
36
+ res.setHeader('Content-Type', 'application/json');
37
+ res.end(JSON.stringify({ error: 'upload token expired or invalid' }));
38
+ return;
39
+ }
40
+ // Read raw body with streaming size enforcement
41
+ const MAX_UPLOAD = pending.maxSize || 50 * 1024 * 1024; // 50MB default cap
42
+ const chunks = [];
43
+ let uploadSize = 0;
44
+ let aborted = false;
45
+ req.on('data', (c) => {
46
+ uploadSize += c.length;
47
+ if (uploadSize > MAX_UPLOAD) {
48
+ aborted = true;
49
+ req.destroy();
50
+ res.statusCode = 413;
51
+ res.setHeader('Content-Type', 'application/json');
52
+ res.end(JSON.stringify({ error: 'file_too_large', maxSize: MAX_UPLOAD, received: uploadSize }));
53
+ return;
54
+ }
55
+ chunks.push(c);
56
+ });
57
+ await new Promise((resolve, reject) => {
58
+ req.on('end', resolve);
59
+ req.on('error', reject);
60
+ });
61
+ if (aborted)
62
+ return;
63
+ const body = Buffer.concat(chunks);
64
+ // Enforce maxSize if specified (redundant safety check)
65
+ if (pending.maxSize && body.length > pending.maxSize) {
66
+ res.statusCode = 413;
67
+ res.setHeader('Content-Type', 'application/json');
68
+ res.end(JSON.stringify({
69
+ error: `file_too_large`,
70
+ maxSize: pending.maxSize,
71
+ received: body.length,
72
+ }));
73
+ return;
74
+ }
75
+ // Write to disk (validate path stays within uploadDir)
76
+ const filePath = path.resolve(uploadDir, pending.key);
77
+ if (!filePath.startsWith(uploadDir + path.sep) && filePath !== uploadDir) {
78
+ res.statusCode = 400;
79
+ res.setHeader('Content-Type', 'application/json');
80
+ res.end(JSON.stringify({ error: 'invalid storage key' }));
81
+ return;
82
+ }
83
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
84
+ fs.writeFileSync(filePath, body);
85
+ res.statusCode = 200;
86
+ res.setHeader('Content-Type', 'application/json');
87
+ res.end(JSON.stringify({
88
+ key: pending.key,
89
+ url: adapter.publicUrl(pending.key),
90
+ size: body.length,
91
+ }));
92
+ });
93
+ // ── Static file serving ────────────────────────────────────
94
+ // Serve files from uploadDir at /uploads/*
95
+ server.middlewares.use('/uploads', (req, res, next) => {
96
+ if (req.method !== 'GET' && req.method !== 'HEAD')
97
+ return next();
98
+ const filePath = path.resolve(uploadDir, (req.url?.split('?')[0] ?? '/').replace(/^\/+/, ''));
99
+ if (!filePath.startsWith(uploadDir + path.sep) && filePath !== uploadDir) {
100
+ return next();
101
+ }
102
+ if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) {
103
+ return next();
104
+ }
105
+ const ext = path.extname(filePath).toLowerCase();
106
+ const mimeTypes = {
107
+ '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png',
108
+ '.gif': 'image/gif', '.webp': 'image/webp', '.svg': 'image/svg+xml',
109
+ '.mp4': 'video/mp4', '.webm': 'video/webm', '.mp3': 'audio/mpeg',
110
+ '.pdf': 'application/pdf', '.json': 'application/json',
111
+ '.txt': 'text/plain', '.bin': 'application/octet-stream',
112
+ };
113
+ const contentType = mimeTypes[ext] ?? 'application/octet-stream';
114
+ const stat = fs.statSync(filePath);
115
+ res.setHeader('Content-Type', contentType);
116
+ res.setHeader('Content-Length', stat.size);
117
+ res.setHeader('Cache-Control', 'no-cache');
118
+ if (req.method === 'HEAD') {
119
+ res.end();
120
+ return;
121
+ }
122
+ fs.createReadStream(filePath).pipe(res);
123
+ });
124
+ },
125
+ };
126
+ }
@@ -15,16 +15,38 @@ export function virtualModulesPlugin(runtimeDir, editorDir) {
15
15
  'router-data': 'router-data.js',
16
16
  'router-hydration': 'router-hydration.js',
17
17
  'i18n': 'i18n.js',
18
+ 'auth': 'auth.js',
19
+ 'communication': 'communication.js',
20
+ 'webrtc': 'webrtc.js',
21
+ 'error-boundary': 'error-boundary.js',
22
+ 'hydrate-support': '__virtual__',
18
23
  };
19
24
  // Modules resolved via resolve.alias instead of virtual module.
20
25
  // They still appear in the map so relative import rewrites work.
21
- const aliasedModules = new Set(['i18n']);
26
+ const aliasedModules = new Set(['i18n', 'auth', 'communication', 'webrtc']);
22
27
  const editorModules = {
23
28
  'editor-bridge': 'editor-bridge.js',
24
29
  'element-annotator': 'element-annotator.js',
25
30
  'click-select': 'click-select.js',
26
31
  'hover-detect': 'hover-detect.js',
27
32
  'inline-text-edit': 'inline-text-edit.js',
33
+ 'editor-api-client': 'editor-api-client.js',
34
+ 'standalone-overlay': 'standalone-overlay.js',
35
+ 'standalone-overlay-dom': 'standalone-overlay-dom.js',
36
+ 'standalone-overlay-styles': 'standalone-overlay-styles.js',
37
+ 'standalone-file-panel': 'standalone-file-panel.js',
38
+ 'overlay-utils': 'overlay-utils.js',
39
+ 'text-toolbar': 'text-toolbar.js',
40
+ 'editor-toolbar': 'editor-toolbar.js',
41
+ 'css-rules': 'css-rules.js',
42
+ 'ast-modification': 'ast-modification.js',
43
+ 'ast-service': 'ast-service.js',
44
+ 'file-service': 'file-service.js',
45
+ 'property-registry': 'property-registry.js',
46
+ 'properties-panel': 'properties-panel.js',
47
+ 'i18n-key-gen': 'i18n-key-gen.js',
48
+ 'ai-chat-panel': 'ai-chat-panel.js',
49
+ 'ai-project-panel': 'ai-project-panel.js',
28
50
  };
29
51
  function rewriteRelativeImports(code, modules) {
30
52
  for (const name of Object.keys(modules)) {
@@ -55,8 +77,95 @@ export function virtualModulesPlugin(runtimeDir, editorDir) {
55
77
  if (!id.startsWith('\0lumenjs:'))
56
78
  return;
57
79
  const name = id.slice('\0lumenjs:'.length);
80
+ if (name === 'hydrate-support') {
81
+ // Custom hydrate support that catches digest mismatch errors and falls
82
+ // back to CSR instead of leaving double-rendered content.
83
+ // The stock @lit-labs/ssr-client/lit-element-hydrate-support.js throws
84
+ // on digest mismatch, sets _$AG=false before throwing, so the next
85
+ // update() appends fresh render alongside stale SSR content.
86
+ return `
87
+ import { render } from 'lit-html';
88
+ import { hydrate } from '@lit-labs/ssr-client';
89
+
90
+ globalThis.litElementHydrateSupport = ({LitElement}) => {
91
+ const observedGet = Object.getOwnPropertyDescriptor(
92
+ Object.getPrototypeOf(LitElement), 'observedAttributes'
93
+ ).get;
94
+ Object.defineProperty(LitElement, 'observedAttributes', {
95
+ get() { return [...observedGet.call(this), 'defer-hydration']; }
96
+ });
97
+
98
+ const origAttrChanged = LitElement.prototype.attributeChangedCallback;
99
+ LitElement.prototype.attributeChangedCallback = function(name, old, value) {
100
+ if (name === 'defer-hydration' && value === null) {
101
+ origConnected.call(this);
102
+ }
103
+ origAttrChanged.call(this, name, old, value);
104
+ };
105
+
106
+ const origConnected = LitElement.prototype.connectedCallback;
107
+ LitElement.prototype.connectedCallback = function() {
108
+ if (!this.hasAttribute('defer-hydration')) origConnected.call(this);
109
+ };
110
+
111
+ function adoptElementStyles(el) {
112
+ const styles = el.constructor.elementStyles;
113
+ if (styles?.length && el.renderRoot instanceof ShadowRoot) {
114
+ el.renderRoot.adoptedStyleSheets = styles.map(
115
+ s => s instanceof CSSStyleSheet ? s : s.styleSheet
116
+ );
117
+ }
118
+ }
119
+
120
+ const origCreateRoot = LitElement.prototype.createRenderRoot;
121
+ LitElement.prototype.createRenderRoot = function() {
122
+ if (this.shadowRoot) {
123
+ this._$AG = true;
124
+ // Adopt styles that SSR declarative shadow roots don't include
125
+ adoptElementStyles(this);
126
+ return this.shadowRoot;
127
+ }
128
+ return origCreateRoot.call(this);
129
+ };
130
+
131
+ const superUpdate = Object.getPrototypeOf(LitElement.prototype).update;
132
+ LitElement.prototype.update = function(changedProps) {
133
+ const value = this.render();
134
+ superUpdate.call(this, changedProps);
135
+ if (this._$AG) {
136
+ this._$AG = false;
137
+ for (const attr of this.getAttributeNames()) {
138
+ if (attr.startsWith('hydrate-internals-')) {
139
+ this.removeAttribute(attr.slice(18));
140
+ this.removeAttribute(attr);
141
+ }
142
+ }
143
+ try {
144
+ hydrate(value, this.renderRoot, this.renderOptions);
145
+ } catch (err) {
146
+ // Digest mismatch — clear SSR content and render fresh (CSR fallback)
147
+ console.warn('[LumenJS] Hydration failed for <' + this.localName + '>, falling back to CSR:', err.message);
148
+ const root = this.renderRoot;
149
+ while (root.firstChild) root.removeChild(root.firstChild);
150
+ delete root._$litPart$;
151
+ // Re-adopt styles since clearing removed SSR <style> tags
152
+ adoptElementStyles(this);
153
+ render(value, root, this.renderOptions);
154
+ }
155
+ } else {
156
+ render(value, this.renderRoot, this.renderOptions);
157
+ }
158
+ };
159
+ };
160
+ `;
161
+ }
58
162
  if (runtimeModules[name]) {
59
- const code = fs.readFileSync(path.join(runtimeDir, runtimeModules[name]), 'utf-8');
163
+ let code = fs.readFileSync(path.join(runtimeDir, runtimeModules[name]), 'utf-8');
164
+ // Prepend hydrate support to app-shell so all Lit imports share one module graph.
165
+ // Uses our custom hydrate-support virtual module (with CSR fallback) instead of stock.
166
+ if (name === 'app-shell') {
167
+ code = `import '/@lumenjs/hydrate-support';\n` + code;
168
+ }
60
169
  return rewriteRelativeImports(code, runtimeModules);
61
170
  }
62
171
  if (editorModules[name]) {
@@ -11,12 +11,21 @@ import { ssrRenderPage } from './ssr-render.js';
11
11
  import { readProjectConfig, getLumenJSNodeModules, getLumenJSDirs } from './config.js';
12
12
  import { getNuralyUIAliases, resolveNuralyUIPaths } from './nuralyui-aliases.js';
13
13
  import { litDedupPlugin } from './plugins/vite-plugin-lit-dedup.js';
14
+ import { autoDefinePlugin } from './plugins/vite-plugin-auto-define.js';
14
15
  import { autoImportPlugin } from './plugins/vite-plugin-auto-import.js';
15
16
  import { litHmrPlugin } from './plugins/vite-plugin-lit-hmr.js';
16
17
  import { sourceAnnotatorPlugin } from './plugins/vite-plugin-source-annotator.js';
18
+ import { editorApiPlugin } from './plugins/vite-plugin-editor-api.js';
17
19
  import { virtualModulesPlugin } from './plugins/vite-plugin-virtual-modules.js';
18
20
  import { i18nPlugin, loadTranslationsFromDisk } from './plugins/vite-plugin-i18n.js';
21
+ import { authPlugin } from './plugins/vite-plugin-auth.js';
22
+ import { communicationPlugin } from './plugins/vite-plugin-communication.js';
23
+ import { lumenStoragePlugin } from './plugins/vite-plugin-storage.js';
24
+ import { lumenSocketIOPlugin } from './plugins/vite-plugin-socketio.js';
19
25
  import { resolveLocale } from './middleware/locale.js';
26
+ import { setProjectDir } from '../db/context.js';
27
+ import { scanMiddleware, getMiddlewareDirsForPathname } from '../build/scan.js';
28
+ import { runMiddlewareChain, extractMiddleware } from '../shared/middleware-runner.js';
20
29
  // Re-export for backwards compatibility
21
30
  export { readProjectConfig, readProjectTitle, getLumenJSNodeModules, getLumenJSDirs } from './config.js';
22
31
  export { getNuralyUIAliases, resolveNuralyUIPaths } from './nuralyui-aliases.js';
@@ -29,24 +38,37 @@ export function getSharedViteConfig(projectDir, options) {
29
38
  const isDev = mode === 'development';
30
39
  const pagesDir = path.join(projectDir, 'pages');
31
40
  const lumenNodeModules = getLumenJSNodeModules();
32
- const { runtimeDir, editorDir } = getLumenJSDirs();
41
+ const { distDir, runtimeDir, editorDir } = getLumenJSDirs();
33
42
  // Resolve NuralyUI paths for aliases (only when nuralyui integration is enabled)
34
43
  const aliases = {};
35
44
  if (options?.integrations?.includes('nuralyui')) {
36
45
  const nuralyUIPaths = resolveNuralyUIPaths(projectDir);
37
46
  if (nuralyUIPaths) {
38
47
  Object.assign(aliases, getNuralyUIAliases(nuralyUIPaths.componentsPath, nuralyUIPaths.commonPath));
48
+ // Add root aliases for theme CSS imports
49
+ const nuralyUIRoot = path.resolve(nuralyUIPaths.componentsPath, '..');
50
+ aliases['@nuralyui-theme'] = path.join(nuralyUIRoot, 'shared/themes');
51
+ aliases['@nuralyui-common'] = nuralyUIPaths.commonPath;
39
52
  }
40
53
  }
41
54
  const resolve = {
42
55
  alias: {
43
56
  ...aliases,
44
- // Map @lumenjs/i18n to the physical dist file so Vite resolves it
45
- // without going through node_modules (it's not an npm package).
46
57
  '@lumenjs/i18n': path.join(runtimeDir, 'i18n.js'),
58
+ '@lumenjs/auth': path.join(runtimeDir, 'auth.js'),
59
+ '@nuraly/lumenjs-auth': path.join(runtimeDir, 'auth.js'),
60
+ '@lumenjs/communication': path.join(runtimeDir, 'communication.js'),
61
+ '@lumenjs/webrtc': path.join(runtimeDir, 'webrtc.js'),
62
+ '@lumenjs/db': path.join(distDir, 'db', 'client.js'),
63
+ '@lumenjs/permissions': path.join(distDir, 'permissions', 'index.js'),
64
+ '@lumenjs/storage': path.join(distDir, 'storage', 'index.js'),
65
+ '@nuraly/lumenjs': path.resolve(distDir, '..'),
47
66
  },
48
67
  conditions: isDev ? ['development', 'browser'] : ['browser'],
49
- dedupe: ['lit', 'lit-html', 'lit-element', '@lit/reactive-element'],
68
+ // Note: resolve.dedupe is NOT used — it resolves via Node's algorithm
69
+ // which ignores Vite's resolve.conditions, picking the `default` export
70
+ // (prod proxy) instead of `development`. The litDedupPlugin handles
71
+ // single-copy resolution with correct conditions instead.
50
72
  };
51
73
  const esbuild = {
52
74
  tsconfigRaw: {
@@ -59,6 +81,7 @@ export function getSharedViteConfig(projectDir, options) {
59
81
  const plugins = [
60
82
  lumenRoutesPlugin(pagesDir),
61
83
  lumenLoadersPlugin(pagesDir),
84
+ autoDefinePlugin(pagesDir),
62
85
  litDedupPlugin(lumenNodeModules, isDev),
63
86
  virtualModulesPlugin(runtimeDir, editorDir),
64
87
  ];
@@ -86,7 +109,13 @@ export async function createDevServer(options) {
86
109
  const apiDir = path.join(projectDir, 'api');
87
110
  const publicDir = path.join(projectDir, 'public');
88
111
  const config = readProjectConfig(projectDir);
89
- const { title, integrations, i18n: i18nConfig } = config;
112
+ const { title, integrations, i18n: i18nConfig, prefetch: prefetchStrategy } = config;
113
+ // Read optional head.html for blocking scripts (e.g. theme initialization)
114
+ const headHtmlPath = path.join(projectDir, 'head.html');
115
+ const headContent = fs.existsSync(headHtmlPath) ? fs.readFileSync(headHtmlPath, 'utf-8') : undefined;
116
+ // Set project dir for DB context (used by loaders, API routes, plugins)
117
+ setProjectDir(projectDir);
118
+ process.env.LUMENJS_PROJECT_DIR = projectDir;
90
119
  const shared = getSharedViteConfig(projectDir, { integrations });
91
120
  const server = await createViteServer({
92
121
  root: projectDir,
@@ -97,15 +126,90 @@ export async function createDevServer(options) {
97
126
  strictPort: false,
98
127
  allowedHosts: true,
99
128
  cors: true,
100
- hmr: true,
129
+ hmr: process.env.HMR_CLIENT_PORT ? { clientPort: parseInt(process.env.HMR_CLIENT_PORT), protocol: process.env.HMR_PROTOCOL || 'wss', host: process.env.HMR_HOST || undefined } : true,
130
+ fs: {
131
+ allow: [projectDir, getLumenJSNodeModules(), path.resolve(getLumenJSNodeModules(), '..')],
132
+ },
101
133
  },
102
134
  resolve: shared.resolve,
103
135
  plugins: [
136
+ ...(integrations.includes('auth') ? [authPlugin(projectDir)] : []),
104
137
  ...shared.plugins,
138
+ ...(integrations.includes('communication') ? [communicationPlugin(projectDir)] : []),
139
+ lumenStoragePlugin(projectDir),
105
140
  lumenApiRoutesPlugin(apiDir, projectDir),
106
141
  litHmrPlugin(projectDir),
107
142
  ...(i18nConfig ? [i18nPlugin(projectDir, i18nConfig)] : []),
108
- ...(editorMode ? [sourceAnnotatorPlugin(projectDir)] : []),
143
+ ...(editorMode ? [sourceAnnotatorPlugin(projectDir), editorApiPlugin(projectDir)] : []),
144
+ lumenSocketIOPlugin(pagesDir),
145
+ {
146
+ // Clear SSR module cache on file changes so the next SSR request uses fresh code.
147
+ // Without this, HMR updates the client but SSR keeps serving stale modules.
148
+ name: 'lumenjs-ssr-invalidate-on-change',
149
+ handleHotUpdate({ file, server }) {
150
+ const mods = server.moduleGraph.getModulesByFile(file);
151
+ if (mods) {
152
+ for (const m of mods) {
153
+ m.ssrModule = null;
154
+ m.ssrTransformResult = null;
155
+ }
156
+ }
157
+ },
158
+ },
159
+ {
160
+ name: 'lumenjs-user-middleware',
161
+ config(config) {
162
+ const entries = scanMiddleware(pagesDir);
163
+ if (entries.length === 0)
164
+ return;
165
+ const npmDeps = new Set();
166
+ for (const entry of entries) {
167
+ try {
168
+ const content = fs.readFileSync(entry.filePath, 'utf-8');
169
+ const importMatches = content.matchAll(/(?:import|require)\s*(?:\(?\s*['"]([^./][^'"]*)['"]\s*\)?|.*from\s*['"]([^./][^'"]*)['"]\s*)/g);
170
+ for (const m of importMatches) {
171
+ const pkg = m[1] || m[2];
172
+ if (pkg) {
173
+ const pkgName = pkg.startsWith('@') ? pkg.split('/').slice(0, 2).join('/') : pkg.split('/')[0];
174
+ npmDeps.add(pkgName);
175
+ }
176
+ }
177
+ }
178
+ catch { }
179
+ }
180
+ if (npmDeps.size > 0) {
181
+ const existing = config.ssr?.external || [];
182
+ return { ssr: { external: [...existing, ...npmDeps] } };
183
+ }
184
+ },
185
+ configureServer(server) {
186
+ server.middlewares.use(async (req, res, next) => {
187
+ const pathname = (req.url || '/').split('?')[0];
188
+ if (pathname.startsWith('/@') || pathname.startsWith('/node_modules') || pathname.includes('.')) {
189
+ return next();
190
+ }
191
+ const middlewareEntries = scanMiddleware(pagesDir);
192
+ if (middlewareEntries.length === 0)
193
+ return next();
194
+ const matchingDirs = getMiddlewareDirsForPathname(pathname, middlewareEntries);
195
+ if (matchingDirs.length === 0)
196
+ return next();
197
+ const allMw = [];
198
+ for (const entry of matchingDirs) {
199
+ try {
200
+ const mod = await server.ssrLoadModule(entry.filePath);
201
+ allMw.push(...extractMiddleware(mod));
202
+ }
203
+ catch (err) {
204
+ console.error(`[LumenJS] Failed to load _middleware.ts (${entry.dir || 'root'}):`, err);
205
+ }
206
+ }
207
+ if (allMw.length === 0)
208
+ return next();
209
+ runMiddlewareChain(allMw, req, res, next);
210
+ });
211
+ }
212
+ },
109
213
  {
110
214
  name: 'lumenjs-index-html',
111
215
  configureServer(server) {
@@ -136,7 +240,7 @@ export async function createDevServer(options) {
136
240
  translations = loadTranslationsFromDisk(projectDir, locale);
137
241
  }
138
242
  const SSR_PLACEHOLDER = '<!--__NK_SSR_CONTENT__-->';
139
- ssrRenderPage(server, pagesDir, pathname, req.headers, locale).then(async (ssrResult) => {
243
+ ssrRenderPage(server, pagesDir, pathname, req.headers, locale, req.nkAuth?.user ?? undefined).then(async (ssrResult) => {
140
244
  if (ssrResult?.redirect) {
141
245
  res.writeHead(ssrResult.redirect.status, { Location: ssrResult.redirect.location });
142
246
  res.end();
@@ -152,6 +256,9 @@ export async function createDevServer(options) {
152
256
  locale,
153
257
  i18nConfig: i18nConfig || undefined,
154
258
  translations,
259
+ prefetch: prefetchStrategy,
260
+ authUser: ssrResult?.authUser ?? req.nkAuth?.user ?? undefined,
261
+ headContent,
155
262
  });
156
263
  const transformed = await server.transformIndexHtml(req.url, shellHtml);
157
264
  const finalHtml = ssrResult
@@ -162,7 +269,7 @@ export async function createDevServer(options) {
162
269
  res.end(finalHtml);
163
270
  }).catch(err => {
164
271
  console.error('[LumenJS] SSR/HTML generation error:', err);
165
- const html = generateIndexHtml({ title, editorMode, integrations, locale, i18nConfig: i18nConfig || undefined, translations });
272
+ const html = generateIndexHtml({ title, editorMode, integrations, locale, i18nConfig: i18nConfig || undefined, translations, prefetch: prefetchStrategy, headContent });
166
273
  server.transformIndexHtml(req.url, html).then(transformed => {
167
274
  res.setHeader('Content-Type', 'text/html');
168
275
  res.setHeader('Cache-Control', 'no-store');
@@ -178,12 +285,21 @@ export async function createDevServer(options) {
178
285
  ],
179
286
  esbuild: shared.esbuild,
180
287
  optimizeDeps: {
181
- include: ['lit', 'lit/decorators.js', 'lit/directive.js', 'lit/directive-helpers.js', 'lit/async-directive.js', 'lit-html', 'lit-element', '@lit/reactive-element'],
182
- exclude: ['@lumenjs/i18n'],
288
+ exclude: [
289
+ '@lumenjs/i18n',
290
+ '@nuraly/lumenjs-auth',
291
+ // Lit packages must NOT be pre-bundled — pre-bundling creates separate
292
+ // module entries (/.vite/deps/) alongside raw /@fs/ files, causing
293
+ // multiple lit-html instances. Instead, resolve.dedupe forces all lit
294
+ // imports to a single copy. The project's lit version MUST match
295
+ // lumenjs's lit version for dedupe to work.
296
+ 'lit', 'lit-html', 'lit-element', '@lit/reactive-element',
297
+ '@lit-labs/ssr-client',
298
+ ],
183
299
  },
184
300
  ssr: {
185
301
  noExternal: true,
186
- external: ['node-domexception'],
302
+ external: ['node-domexception', 'socket.io-client', 'xmlhttprequest-ssl', 'engine.io-client', 'better-sqlite3', '@lumenjs/db', '@lumenjs/permissions'],
187
303
  resolve: {
188
304
  conditions: ['node', 'import'],
189
305
  },
@@ -9,7 +9,7 @@ export interface LayoutSSRData {
9
9
  *
10
10
  * Returns pre-rendered HTML and loader data, or null on failure (falls back to CSR).
11
11
  */
12
- export declare function ssrRenderPage(server: ViteDevServer, pagesDir: string, pathname: string, headers?: Record<string, string | string[] | undefined>, locale?: string): Promise<{
12
+ export declare function ssrRenderPage(server: ViteDevServer, pagesDir: string, pathname: string, headers?: Record<string, string | string[] | undefined>, locale?: string, user?: any): Promise<{
13
13
  html: string;
14
14
  loaderData: any;
15
15
  layoutsData?: LayoutSSRData[];
@@ -17,4 +17,5 @@ export declare function ssrRenderPage(server: ViteDevServer, pagesDir: string, p
17
17
  location: string;
18
18
  status: number;
19
19
  };
20
+ authUser?: any;
20
21
  } | null>;