@nuraly/lumenjs 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. package/README.md +62 -282
  2. package/dist/auth/config.d.ts +23 -0
  3. package/dist/auth/config.js +115 -0
  4. package/dist/auth/guard.d.ts +12 -0
  5. package/dist/auth/guard.js +28 -0
  6. package/dist/auth/index.d.ts +3 -0
  7. package/dist/auth/index.js +1 -0
  8. package/dist/auth/middleware.d.ts +23 -0
  9. package/dist/auth/middleware.js +89 -0
  10. package/dist/auth/native-auth.d.ts +73 -0
  11. package/dist/auth/native-auth.js +293 -0
  12. package/dist/auth/oidc-client.d.ts +17 -0
  13. package/dist/auth/oidc-client.js +123 -0
  14. package/dist/auth/providers/google.d.ts +23 -0
  15. package/dist/auth/providers/google.js +25 -0
  16. package/dist/auth/providers/index.d.ts +2 -0
  17. package/dist/auth/providers/index.js +1 -0
  18. package/dist/auth/routes/login.d.ts +8 -0
  19. package/dist/auth/routes/login.js +98 -0
  20. package/dist/auth/routes/logout.d.ts +4 -0
  21. package/dist/auth/routes/logout.js +79 -0
  22. package/dist/auth/routes/oidc-callback.d.ts +3 -0
  23. package/dist/auth/routes/oidc-callback.js +70 -0
  24. package/dist/auth/routes/password.d.ts +5 -0
  25. package/dist/auth/routes/password.js +149 -0
  26. package/dist/auth/routes/signup.d.ts +3 -0
  27. package/dist/auth/routes/signup.js +81 -0
  28. package/dist/auth/routes/token.d.ts +4 -0
  29. package/dist/auth/routes/token.js +70 -0
  30. package/dist/auth/routes/utils.d.ts +7 -0
  31. package/dist/auth/routes/utils.js +35 -0
  32. package/dist/auth/routes/verify.d.ts +3 -0
  33. package/dist/auth/routes/verify.js +26 -0
  34. package/dist/auth/routes.d.ts +8 -0
  35. package/dist/auth/routes.js +110 -0
  36. package/dist/auth/session.d.ts +8 -0
  37. package/dist/auth/session.js +54 -0
  38. package/dist/auth/token.d.ts +33 -0
  39. package/dist/auth/token.js +90 -0
  40. package/dist/auth/types.d.ts +156 -0
  41. package/dist/auth/types.js +2 -0
  42. package/dist/build/build-client.d.ts +15 -0
  43. package/dist/build/build-client.js +45 -0
  44. package/dist/build/build-prerender.d.ts +11 -0
  45. package/dist/build/build-prerender.js +159 -0
  46. package/dist/build/build-server.d.ts +17 -0
  47. package/dist/build/build-server.js +98 -0
  48. package/dist/build/build.js +48 -120
  49. package/dist/build/scan.d.ts +17 -0
  50. package/dist/build/scan.js +76 -6
  51. package/dist/build/serve-api.js +8 -2
  52. package/dist/build/serve-loaders.d.ts +4 -4
  53. package/dist/build/serve-loaders.js +26 -18
  54. package/dist/build/serve-ssr.js +38 -11
  55. package/dist/build/serve-static.js +3 -3
  56. package/dist/build/serve.js +218 -15
  57. package/dist/cli.js +37 -6
  58. package/dist/communication/encryption.d.ts +35 -0
  59. package/dist/communication/encryption.js +90 -0
  60. package/dist/communication/handlers/context.d.ts +27 -0
  61. package/dist/communication/handlers/context.js +1 -0
  62. package/dist/communication/handlers/conversation.d.ts +24 -0
  63. package/dist/communication/handlers/conversation.js +113 -0
  64. package/dist/communication/handlers/file-upload.d.ts +17 -0
  65. package/dist/communication/handlers/file-upload.js +62 -0
  66. package/dist/communication/handlers/messaging.d.ts +30 -0
  67. package/dist/communication/handlers/messaging.js +237 -0
  68. package/dist/communication/handlers/presence.d.ts +15 -0
  69. package/dist/communication/handlers/presence.js +76 -0
  70. package/dist/communication/handlers.d.ts +5 -0
  71. package/dist/communication/handlers.js +5 -0
  72. package/dist/communication/index.d.ts +9 -0
  73. package/dist/communication/index.js +7 -0
  74. package/dist/communication/link-preview.d.ts +18 -0
  75. package/dist/communication/link-preview.js +115 -0
  76. package/dist/communication/schema.d.ts +10 -0
  77. package/dist/communication/schema.js +101 -0
  78. package/dist/communication/server.d.ts +86 -0
  79. package/dist/communication/server.js +212 -0
  80. package/dist/communication/signaling.d.ts +43 -0
  81. package/dist/communication/signaling.js +271 -0
  82. package/dist/communication/store.d.ts +71 -0
  83. package/dist/communication/store.js +289 -0
  84. package/dist/communication/types.d.ts +454 -0
  85. package/dist/communication/types.js +1 -0
  86. package/dist/create.d.ts +1 -0
  87. package/dist/create.js +55 -0
  88. package/dist/db/auto-migrate.d.ts +3 -0
  89. package/dist/db/auto-migrate.js +100 -0
  90. package/dist/db/client.d.ts +3 -0
  91. package/dist/db/client.js +18 -0
  92. package/dist/db/index.d.ts +17 -13
  93. package/dist/db/index.js +205 -26
  94. package/dist/db/seed.d.ts +12 -0
  95. package/dist/db/seed.js +88 -0
  96. package/dist/db/table.d.ts +10 -0
  97. package/dist/db/table.js +12 -0
  98. package/dist/dev-server/config.d.ts +11 -0
  99. package/dist/dev-server/config.js +23 -20
  100. package/dist/dev-server/index-html.d.ts +3 -0
  101. package/dist/dev-server/index-html.js +18 -6
  102. package/dist/dev-server/nuralyui-aliases.d.ts +0 -4
  103. package/dist/dev-server/nuralyui-aliases.js +115 -94
  104. package/dist/dev-server/plugins/vite-plugin-api-routes.js +29 -5
  105. package/dist/dev-server/plugins/vite-plugin-auth.d.ts +6 -0
  106. package/dist/dev-server/plugins/vite-plugin-auth.js +223 -0
  107. package/dist/dev-server/plugins/vite-plugin-auto-define.d.ts +16 -0
  108. package/dist/dev-server/plugins/vite-plugin-auto-define.js +111 -0
  109. package/dist/dev-server/plugins/vite-plugin-communication.d.ts +6 -0
  110. package/dist/dev-server/plugins/vite-plugin-communication.js +205 -0
  111. package/dist/dev-server/plugins/vite-plugin-editor-api.d.ts +6 -0
  112. package/dist/dev-server/plugins/vite-plugin-editor-api.js +318 -0
  113. package/dist/dev-server/plugins/vite-plugin-i18n.js +69 -2
  114. package/dist/dev-server/plugins/vite-plugin-lit-dedup.d.ts +6 -0
  115. package/dist/dev-server/plugins/vite-plugin-lit-dedup.js +78 -34
  116. package/dist/dev-server/plugins/vite-plugin-lit-hmr.js +44 -2
  117. package/dist/dev-server/plugins/vite-plugin-llms.d.ts +2 -0
  118. package/dist/dev-server/plugins/vite-plugin-llms.js +92 -0
  119. package/dist/dev-server/plugins/vite-plugin-loaders.js +146 -13
  120. package/dist/dev-server/plugins/vite-plugin-routes.js +15 -5
  121. package/dist/dev-server/plugins/vite-plugin-socketio.d.ts +2 -0
  122. package/dist/dev-server/plugins/vite-plugin-socketio.js +51 -0
  123. package/dist/dev-server/plugins/vite-plugin-source-annotator.d.ts +2 -0
  124. package/dist/dev-server/plugins/vite-plugin-source-annotator.js +26 -3
  125. package/dist/dev-server/plugins/vite-plugin-storage.d.ts +10 -0
  126. package/dist/dev-server/plugins/vite-plugin-storage.js +126 -0
  127. package/dist/dev-server/plugins/vite-plugin-virtual-modules.js +111 -2
  128. package/dist/dev-server/server.js +127 -13
  129. package/dist/dev-server/ssr-render.d.ts +2 -1
  130. package/dist/dev-server/ssr-render.js +107 -48
  131. package/dist/editor/ai/backend.d.ts +20 -0
  132. package/dist/editor/ai/backend.js +104 -0
  133. package/dist/editor/ai/claude-code-client.d.ts +20 -0
  134. package/dist/editor/ai/claude-code-client.js +145 -0
  135. package/dist/editor/ai/opencode-client.d.ts +14 -0
  136. package/dist/editor/ai/opencode-client.js +125 -0
  137. package/dist/editor/ai/snapshot-store.d.ts +22 -0
  138. package/dist/editor/ai/snapshot-store.js +35 -0
  139. package/dist/editor/ai/types.d.ts +30 -0
  140. package/dist/editor/ai/types.js +136 -0
  141. package/dist/editor/ai-chat-panel.d.ts +13 -0
  142. package/dist/editor/ai-chat-panel.js +587 -0
  143. package/dist/editor/ai-markdown.d.ts +10 -0
  144. package/dist/editor/ai-markdown.js +70 -0
  145. package/dist/editor/ai-project-panel.d.ts +11 -0
  146. package/dist/editor/ai-project-panel.js +332 -0
  147. package/dist/editor/ast-modification.d.ts +11 -0
  148. package/dist/editor/ast-modification.js +1 -0
  149. package/dist/editor/ast-service.d.ts +30 -0
  150. package/dist/editor/ast-service.js +180 -0
  151. package/dist/editor/css-rules.d.ts +54 -0
  152. package/dist/editor/css-rules.js +423 -0
  153. package/dist/editor/editor-api-client.d.ts +51 -0
  154. package/dist/editor/editor-api-client.js +162 -0
  155. package/dist/editor/editor-bridge.d.ts +1 -0
  156. package/dist/editor/editor-bridge.js +17 -8
  157. package/dist/editor/editor-toolbar.d.ts +14 -0
  158. package/dist/editor/editor-toolbar.js +115 -0
  159. package/dist/editor/file-editor.d.ts +9 -0
  160. package/dist/editor/file-editor.js +236 -0
  161. package/dist/editor/file-service.d.ts +16 -0
  162. package/dist/editor/file-service.js +52 -0
  163. package/dist/editor/i18n-key-gen.d.ts +1 -0
  164. package/dist/editor/i18n-key-gen.js +7 -0
  165. package/dist/editor/inline-text-edit.d.ts +5 -0
  166. package/dist/editor/inline-text-edit.js +173 -92
  167. package/dist/editor/overlay-events.d.ts +5 -0
  168. package/dist/editor/overlay-events.js +364 -0
  169. package/dist/editor/overlay-hmr.d.ts +2 -0
  170. package/dist/editor/overlay-hmr.js +75 -0
  171. package/dist/editor/overlay-selection.d.ts +29 -0
  172. package/dist/editor/overlay-selection.js +148 -0
  173. package/dist/editor/overlay-utils.d.ts +12 -0
  174. package/dist/editor/overlay-utils.js +59 -0
  175. package/dist/editor/properties-panel-persist.d.ts +14 -0
  176. package/dist/editor/properties-panel-persist.js +70 -0
  177. package/dist/editor/properties-panel-rows.d.ts +10 -0
  178. package/dist/editor/properties-panel-rows.js +349 -0
  179. package/dist/editor/properties-panel-styles.d.ts +4 -0
  180. package/dist/editor/properties-panel-styles.js +174 -0
  181. package/dist/editor/properties-panel.d.ts +4 -0
  182. package/dist/editor/properties-panel.js +148 -0
  183. package/dist/editor/property-registry.d.ts +16 -0
  184. package/dist/editor/property-registry.js +303 -0
  185. package/dist/editor/standalone-file-panel.d.ts +0 -0
  186. package/dist/editor/standalone-file-panel.js +1 -0
  187. package/dist/editor/standalone-overlay-dom.d.ts +0 -0
  188. package/dist/editor/standalone-overlay-dom.js +1 -0
  189. package/dist/editor/standalone-overlay-styles.d.ts +0 -0
  190. package/dist/editor/standalone-overlay-styles.js +1 -0
  191. package/dist/editor/standalone-overlay.d.ts +1 -0
  192. package/dist/editor/standalone-overlay.js +76 -0
  193. package/dist/editor/syntax-highlighter.d.ts +4 -0
  194. package/dist/editor/syntax-highlighter.js +81 -0
  195. package/dist/editor/text-toolbar.d.ts +11 -0
  196. package/dist/editor/text-toolbar.js +327 -0
  197. package/dist/editor/toolbar-styles.d.ts +4 -0
  198. package/dist/editor/toolbar-styles.js +198 -0
  199. package/dist/email/index.d.ts +32 -0
  200. package/dist/email/index.js +154 -0
  201. package/dist/email/providers/resend.d.ts +2 -0
  202. package/dist/email/providers/resend.js +24 -0
  203. package/dist/email/providers/sendgrid.d.ts +2 -0
  204. package/dist/email/providers/sendgrid.js +31 -0
  205. package/dist/email/providers/smtp.d.ts +13 -0
  206. package/dist/email/providers/smtp.js +125 -0
  207. package/dist/email/template-engine.d.ts +18 -0
  208. package/dist/email/template-engine.js +116 -0
  209. package/dist/email/templates/base.d.ts +9 -0
  210. package/dist/email/templates/base.js +65 -0
  211. package/dist/email/templates/password-reset.d.ts +5 -0
  212. package/dist/email/templates/password-reset.js +15 -0
  213. package/dist/email/templates/verify-email.d.ts +5 -0
  214. package/dist/email/templates/verify-email.js +15 -0
  215. package/dist/email/templates/welcome.d.ts +5 -0
  216. package/dist/email/templates/welcome.js +13 -0
  217. package/dist/email/types.d.ts +49 -0
  218. package/dist/email/types.js +1 -0
  219. package/dist/llms/generate.d.ts +46 -0
  220. package/dist/llms/generate.js +185 -0
  221. package/dist/permissions/guard.d.ts +28 -0
  222. package/dist/permissions/guard.js +30 -0
  223. package/dist/permissions/index.d.ts +6 -0
  224. package/dist/permissions/index.js +3 -0
  225. package/dist/permissions/service.d.ts +80 -0
  226. package/dist/permissions/service.js +210 -0
  227. package/dist/permissions/tables.d.ts +5 -0
  228. package/dist/permissions/tables.js +68 -0
  229. package/dist/permissions/types.d.ts +33 -0
  230. package/dist/permissions/types.js +1 -0
  231. package/dist/runtime/app-shell.js +163 -0
  232. package/dist/runtime/auth.d.ts +10 -0
  233. package/dist/runtime/auth.js +30 -0
  234. package/dist/runtime/communication.d.ts +137 -0
  235. package/dist/runtime/communication.js +228 -0
  236. package/dist/runtime/error-boundary.d.ts +23 -0
  237. package/dist/runtime/error-boundary.js +120 -0
  238. package/dist/runtime/i18n.d.ts +6 -1
  239. package/dist/runtime/i18n.js +42 -21
  240. package/dist/runtime/router-data.d.ts +3 -0
  241. package/dist/runtime/router-data.js +102 -17
  242. package/dist/runtime/router-hydration.js +25 -0
  243. package/dist/runtime/router.d.ts +16 -1
  244. package/dist/runtime/router.js +188 -42
  245. package/dist/runtime/socket-client.d.ts +2 -0
  246. package/dist/runtime/socket-client.js +30 -0
  247. package/dist/runtime/webrtc.d.ts +47 -0
  248. package/dist/runtime/webrtc.js +178 -0
  249. package/dist/shared/graceful-shutdown.d.ts +8 -0
  250. package/dist/shared/graceful-shutdown.js +36 -0
  251. package/dist/shared/health.d.ts +8 -0
  252. package/dist/shared/health.js +25 -0
  253. package/dist/shared/llms-txt.d.ts +31 -0
  254. package/dist/shared/llms-txt.js +85 -0
  255. package/dist/shared/logger.d.ts +32 -0
  256. package/dist/shared/logger.js +93 -0
  257. package/dist/shared/meta.d.ts +27 -0
  258. package/dist/shared/meta.js +71 -0
  259. package/dist/shared/middleware-runner.d.ts +9 -0
  260. package/dist/shared/middleware-runner.js +29 -0
  261. package/dist/shared/rate-limit.d.ts +18 -0
  262. package/dist/shared/rate-limit.js +71 -0
  263. package/dist/shared/request-id.d.ts +5 -0
  264. package/dist/shared/request-id.js +18 -0
  265. package/dist/shared/route-matching.js +16 -1
  266. package/dist/shared/security-headers.d.ts +18 -0
  267. package/dist/shared/security-headers.js +38 -0
  268. package/dist/shared/socket-io-setup.d.ts +11 -0
  269. package/dist/shared/socket-io-setup.js +51 -0
  270. package/dist/shared/types.d.ts +14 -0
  271. package/dist/shared/utils.d.ts +33 -7
  272. package/dist/shared/utils.js +164 -27
  273. package/dist/storage/adapters/local.d.ts +44 -0
  274. package/dist/storage/adapters/local.js +85 -0
  275. package/dist/storage/adapters/s3.d.ts +32 -0
  276. package/dist/storage/adapters/s3.js +116 -0
  277. package/dist/storage/adapters/types.d.ts +53 -0
  278. package/dist/storage/adapters/types.js +1 -0
  279. package/dist/storage/index.d.ts +76 -0
  280. package/dist/storage/index.js +83 -0
  281. package/package.json +19 -7
  282. package/templates/blog/api/posts.ts +4 -18
  283. package/templates/blog/data/migrations/001_init.sql +6 -5
  284. package/templates/blog/lumenjs.config.ts +3 -0
  285. package/templates/blog/package.json +14 -0
  286. package/templates/blog/pages/_layout.ts +25 -0
  287. package/templates/blog/pages/index.ts +48 -22
  288. package/templates/blog/pages/posts/[slug].ts +45 -20
  289. package/templates/blog/pages/tag/[tag].ts +44 -0
  290. package/templates/dashboard/api/stats.ts +8 -5
  291. package/templates/dashboard/lumenjs.config.ts +3 -0
  292. package/templates/dashboard/package.json +14 -0
  293. package/templates/dashboard/pages/_layout.ts +25 -0
  294. package/templates/dashboard/pages/index.ts +54 -23
  295. package/templates/dashboard/pages/settings/index.ts +29 -0
  296. package/templates/default/lumenjs.config.ts +3 -0
  297. package/templates/default/package.json +14 -0
  298. package/templates/default/pages/index.ts +24 -0
  299. package/templates/social/api/posts/[id].ts +14 -0
  300. package/templates/social/api/posts.ts +11 -0
  301. package/templates/social/api/profile/[username].ts +10 -0
  302. package/templates/social/api/upload.ts +19 -0
  303. package/templates/social/data/migrations/001_init.sql +78 -0
  304. package/templates/social/data/migrations/002_add_image_url.sql +1 -0
  305. package/templates/social/data/migrations/003_auth.sql +7 -0
  306. package/templates/social/docs/architecture.md +76 -0
  307. package/templates/social/docs/components.md +100 -0
  308. package/templates/social/docs/data.md +89 -0
  309. package/templates/social/docs/pages.md +96 -0
  310. package/templates/social/docs/theming.md +52 -0
  311. package/templates/social/lib/media.ts +130 -0
  312. package/templates/social/lumenjs.auth.ts +21 -0
  313. package/templates/social/lumenjs.config.ts +3 -0
  314. package/templates/social/package.json +5 -0
  315. package/templates/social/pages/_layout.ts +239 -0
  316. package/templates/social/pages/apps/[id].ts +173 -0
  317. package/templates/social/pages/apps/index.ts +116 -0
  318. package/templates/social/pages/auth/login.ts +92 -0
  319. package/templates/social/pages/bookmarks.ts +57 -0
  320. package/templates/social/pages/explore.ts +73 -0
  321. package/templates/social/pages/index.ts +351 -0
  322. package/templates/social/pages/messages.ts +298 -0
  323. package/templates/social/pages/new.ts +77 -0
  324. package/templates/social/pages/notifications.ts +73 -0
  325. package/templates/social/pages/post/[id].ts +124 -0
  326. package/templates/social/pages/profile/[username].ts +100 -0
  327. package/templates/social/pages/settings/accessibility.ts +153 -0
  328. package/templates/social/pages/settings/account.ts +260 -0
  329. package/templates/social/pages/settings/help.ts +141 -0
  330. package/templates/social/pages/settings/language.ts +103 -0
  331. package/templates/social/pages/settings/privacy.ts +183 -0
  332. package/templates/social/pages/settings/security.ts +133 -0
  333. package/templates/social/pages/settings.ts +185 -0
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { dirToLayoutTagName, fileHasLoader, fileHasSubscribe, filePathToRoute, filePathToTagName } from '../../shared/utils.js';
3
+ import { dirToLayoutTagName, fileHasLoader, fileHasSubscribe, fileHasAuth, fileHasMeta, fileHasStandalone, filePathToRoute, filePathToTagName } from '../../shared/utils.js';
4
4
  const VIRTUAL_MODULE_ID = 'virtual:lumenjs-routes';
5
5
  const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID;
6
6
  /**
@@ -28,7 +28,7 @@ export function lumenRoutesPlugin(pagesDir) {
28
28
  const tagName = dirToLayoutTagName(relativePath);
29
29
  layouts.push({ dir: relativePath.replace(/\\/g, '/'), filePath, tagName });
30
30
  }
31
- if (entry.isDirectory()) {
31
+ if (entry.isDirectory() && !entry.name.startsWith('_')) {
32
32
  walkForLayouts(baseDir, path.join(relativePath, entry.name), layouts);
33
33
  }
34
34
  }
@@ -55,12 +55,18 @@ export function lumenRoutesPlugin(pagesDir) {
55
55
  function walkDir(baseDir, relativePath, routes) {
56
56
  const fullDir = path.join(baseDir, relativePath);
57
57
  const entries = fs.readdirSync(fullDir, { withFileTypes: true });
58
+ // Check if this subdirectory contains an index file (folder route)
59
+ // Only applies to subdirectories, not the root pages directory
60
+ const hasIndex = relativePath !== '' && entries.some(e => e.isFile() && /^index\.(ts|js)$/.test(e.name));
58
61
  for (const entry of entries) {
59
62
  const entryRelative = path.join(relativePath, entry.name);
60
- if (entry.isDirectory()) {
63
+ if (entry.isDirectory() && !entry.name.startsWith('_')) {
61
64
  walkDir(baseDir, entryRelative, routes);
62
65
  }
63
66
  else if (entry.isFile() && /\.(ts|js)$/.test(entry.name) && !entry.name.startsWith('_')) {
67
+ // In a folder route (has index file), only register the index file and dynamic param files
68
+ if (hasIndex && !/^index\.(ts|js)$/.test(entry.name) && !entry.name.startsWith('['))
69
+ continue;
64
70
  const routePath = filePathToRoute(entryRelative);
65
71
  const componentPath = path.join(pagesDir, entryRelative);
66
72
  const tagName = filePathToTagName(entryRelative);
@@ -99,8 +105,12 @@ export function lumenRoutesPlugin(pagesDir) {
99
105
  .map(r => {
100
106
  const hasLoader = fileHasLoader(r.componentPath);
101
107
  const hasSubscribe = fileHasSubscribe(r.componentPath);
108
+ const hasAuth = fileHasAuth(r.componentPath);
109
+ const hasMeta = fileHasMeta(r.componentPath);
110
+ const isStandalone = fileHasStandalone(r.componentPath);
102
111
  const componentPath = r.componentPath.replace(/\\/g, '/');
103
- const chain = getLayoutChain(r.componentPath, layouts);
112
+ // Standalone pages skip all layouts
113
+ const chain = isStandalone ? [] : getLayoutChain(r.componentPath, layouts);
104
114
  let layoutsStr = '';
105
115
  if (chain.length > 0) {
106
116
  const items = chain.map(l => {
@@ -111,7 +121,7 @@ export function lumenRoutesPlugin(pagesDir) {
111
121
  });
112
122
  layoutsStr = `, layouts: [${items.join(', ')}]`;
113
123
  }
114
- return ` { path: ${JSON.stringify(r.path)}, tagName: ${JSON.stringify(r.tagName)}${hasLoader ? ', hasLoader: true' : ''}${hasSubscribe ? ', hasSubscribe: true' : ''}, load: () => import('${componentPath}')${layoutsStr} }`;
124
+ return ` { path: ${JSON.stringify(r.path)}, tagName: ${JSON.stringify(r.tagName)}${hasLoader ? ', hasLoader: true' : ''}${hasSubscribe ? ', hasSubscribe: true' : ''}${hasMeta ? ', hasMeta: true' : ''}${hasAuth ? ', __nk_has_auth: true' : ''}, load: () => import('${componentPath}')${layoutsStr} }`;
115
125
  })
116
126
  .join(',\n');
117
127
  return `export const routes = [\n${routeArray}\n];\n`;
@@ -0,0 +1,2 @@
1
+ import { Plugin } from 'vite';
2
+ export declare function lumenSocketIOPlugin(pagesDir: string): Plugin;
@@ -0,0 +1,51 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileHasSocket, filePathToRoute } from '../../shared/utils.js';
4
+ export function lumenSocketIOPlugin(pagesDir) {
5
+ return {
6
+ name: 'lumenjs-socketio',
7
+ configureServer(server) {
8
+ if (!server.httpServer)
9
+ return;
10
+ import('../../shared/socket-io-setup.js').then(({ setupSocketIO }) => {
11
+ const routes = scanSocketRoutes(pagesDir);
12
+ setupSocketIO({
13
+ httpServer: server.httpServer,
14
+ loadModule: (fp) => server.ssrLoadModule(fp),
15
+ routes,
16
+ projectDir: path.dirname(pagesDir),
17
+ }).catch((err) => {
18
+ console.warn('[LumenJS] Socket.IO setup failed:', err.message);
19
+ console.warn('[LumenJS] Make sure socket.io is installed: lumenjs add socketio');
20
+ });
21
+ }).catch((err) => {
22
+ console.warn('[LumenJS] Socket.IO plugin load failed:', err.message);
23
+ });
24
+ },
25
+ };
26
+ }
27
+ function scanSocketRoutes(pagesDir) {
28
+ const routes = [];
29
+ if (!fs.existsSync(pagesDir))
30
+ return routes;
31
+ walkDir(pagesDir, '', routes, pagesDir);
32
+ return routes;
33
+ }
34
+ function walkDir(baseDir, relativePath, routes, pagesDir) {
35
+ const fullDir = path.join(baseDir, relativePath);
36
+ const entries = fs.readdirSync(fullDir, { withFileTypes: true });
37
+ for (const entry of entries) {
38
+ const entryRelative = path.join(relativePath, entry.name);
39
+ if (entry.isDirectory()) {
40
+ walkDir(baseDir, entryRelative, routes, pagesDir);
41
+ }
42
+ else if (entry.isFile() && /\.(ts|js)$/.test(entry.name) && !entry.name.startsWith('_')) {
43
+ const filePath = path.join(pagesDir, entryRelative);
44
+ const hasSocket = fileHasSocket(filePath);
45
+ if (hasSocket) {
46
+ const routePath = filePathToRoute(entryRelative);
47
+ routes.push({ path: routePath, hasSocket: true, filePath });
48
+ }
49
+ }
50
+ }
51
+ }
@@ -1,5 +1,7 @@
1
1
  import { Plugin } from 'vite';
2
2
  /**
3
3
  * In editor mode, inject data-nk-source attributes into html`` template literals.
4
+ * Reads the original source file from disk to compute correct line numbers,
5
+ * since Vite's transform hook receives esbuild-compiled code with shifted lines.
4
6
  */
5
7
  export declare function sourceAnnotatorPlugin(projectDir: string): Plugin;
@@ -1,21 +1,44 @@
1
1
  import path from 'path';
2
+ import fs from 'fs';
2
3
  /**
3
4
  * In editor mode, inject data-nk-source attributes into html`` template literals.
5
+ * Reads the original source file from disk to compute correct line numbers,
6
+ * since Vite's transform hook receives esbuild-compiled code with shifted lines.
4
7
  */
5
8
  export function sourceAnnotatorPlugin(projectDir) {
6
9
  return {
7
10
  name: 'lumenjs-source-annotator',
11
+ enforce: 'pre',
8
12
  transform(code, id) {
9
13
  if (!id.startsWith(projectDir) || !id.endsWith('.ts'))
10
14
  return;
11
15
  if (!code.includes('html`'))
12
16
  return;
17
+ // Read the original source to get correct line numbers
18
+ let originalSource;
19
+ try {
20
+ originalSource = fs.readFileSync(id, 'utf-8');
21
+ }
22
+ catch {
23
+ originalSource = code;
24
+ }
13
25
  const relativePath = path.relative(projectDir, id);
26
+ // Build ordered list of html` base lines from original source
27
+ const originalBaseLines = [];
28
+ const origRegex = /html`/g;
29
+ let origMatch;
30
+ while ((origMatch = origRegex.exec(originalSource)) !== null) {
31
+ const before = originalSource.substring(0, origMatch.index + 5); // include 'html`'
32
+ originalBaseLines.push(before.split('\n').length);
33
+ }
34
+ let templateIndex = 0;
14
35
  const transformed = code.replace(/html`([\s\S]*?)`/g, (match, templateContent) => {
15
36
  let offset = 0;
16
- const beforeTemplate = code.substring(0, code.indexOf(match));
17
- const baseLine = beforeTemplate.split('\n').length;
18
- const annotated = templateContent.replace(/<([a-z][a-z0-9]*-[a-z0-9-]*)([\s>])/gi, (tagMatch, tagName, after) => {
37
+ // Use original source line for this Nth html` template
38
+ const baseLine = originalBaseLines[templateIndex] ?? code.substring(0, code.indexOf(match)).split('\n').length;
39
+ templateIndex++;
40
+ // Annotate both custom elements (tags with hyphens) and standard HTML elements
41
+ const annotated = templateContent.replace(/<((?:[a-z][a-z0-9]*-[a-z0-9-]*)|(?:div|section|article|aside|main|nav|header|footer|h[1-6]|p|span|a|ul|ol|li|button|form|input|textarea|select|label|img|table|tr|td|th|thead|tbody))([\s>])/gi, (tagMatch, tagName, after) => {
19
42
  const beforeTag = templateContent.substring(0, templateContent.indexOf(tagMatch, offset));
20
43
  const lineInTemplate = beforeTag.split('\n').length - 1;
21
44
  offset = templateContent.indexOf(tagMatch, offset) + tagMatch.length;
@@ -0,0 +1,10 @@
1
+ import type { Plugin } from 'vite';
2
+ /**
3
+ * LumenJS storage plugin (dev mode).
4
+ *
5
+ * - Creates a LocalStorageAdapter pointing to `{projectDir}/uploads`
6
+ * - Registers it as the global storage singleton (`useStorage()`)
7
+ * - Serves uploaded files at `/uploads/*`
8
+ * - Handles presigned PUT requests at `/__nk_storage/upload/:token`
9
+ */
10
+ export declare function lumenStoragePlugin(projectDir: string): Plugin;
@@ -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]) {