@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
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>;
@@ -0,0 +1,113 @@
1
+ // ── Conversation ────────────────────────────────────────────────
2
+ export async function handleConversationCreate(ctx, data) {
3
+ const now = new Date().toISOString();
4
+ let conversation;
5
+ if (ctx.db) {
6
+ const convId = crypto.randomUUID();
7
+ await ctx.db.run(`INSERT INTO conversations (id, type, name, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`, convId, data.type, data.name || null, now, now);
8
+ // Add the creator as a participant with 'owner' role
9
+ const allParticipantIds = [ctx.userId, ...data.participantIds.filter(id => id !== ctx.userId)];
10
+ for (const uid of allParticipantIds) {
11
+ const role = uid === ctx.userId ? 'owner' : 'member';
12
+ await ctx.db.run(`INSERT INTO conversation_participants (conversation_id, user_id, role, joined_at) VALUES (?, ?, ?, ?)`, convId, uid, role, now);
13
+ }
14
+ conversation = {
15
+ id: convId,
16
+ type: data.type,
17
+ name: data.name,
18
+ participants: allParticipantIds.map(uid => ({
19
+ userId: uid,
20
+ displayName: '',
21
+ role: uid === ctx.userId ? 'owner' : 'member',
22
+ joinedAt: now,
23
+ presence: ctx.store.getPresence(uid)?.status || 'offline',
24
+ })),
25
+ createdAt: now,
26
+ updatedAt: now,
27
+ unreadCount: 0,
28
+ };
29
+ }
30
+ else {
31
+ const allParticipantIds = [ctx.userId, ...data.participantIds.filter(id => id !== ctx.userId)];
32
+ conversation = {
33
+ id: crypto.randomUUID(),
34
+ type: data.type,
35
+ name: data.name,
36
+ participants: allParticipantIds.map(uid => ({
37
+ userId: uid,
38
+ displayName: '',
39
+ role: uid === ctx.userId ? 'owner' : 'member',
40
+ joinedAt: now,
41
+ presence: ctx.store.getPresence(uid)?.status || 'offline',
42
+ })),
43
+ createdAt: now,
44
+ updatedAt: now,
45
+ unreadCount: 0,
46
+ };
47
+ }
48
+ // Auto-join the creator to the conversation room
49
+ ctx.joinRoom(`conv:${conversation.id}`);
50
+ // Notify the creator
51
+ ctx.push({ event: 'conversation:new', data: conversation });
52
+ // Notify other participants (they haven't joined the room yet)
53
+ if (ctx.emitToUser) {
54
+ for (const uid of data.participantIds) {
55
+ if (uid !== ctx.userId) {
56
+ ctx.emitToUser(uid, { event: 'conversation:new', data: conversation });
57
+ }
58
+ }
59
+ }
60
+ }
61
+ export async function handleConversationJoin(ctx, data) {
62
+ // Verify the user is an actual participant of this conversation
63
+ if (ctx.db) {
64
+ const row = await ctx.db.get('SELECT 1 FROM conversation_participants WHERE conversation_id = ? AND user_id = ?', data.conversationId, ctx.userId);
65
+ if (!row) {
66
+ ctx.push({ event: 'error', data: { code: 'FORBIDDEN', message: 'Not a participant of this conversation' } });
67
+ return;
68
+ }
69
+ }
70
+ ctx.joinRoom(`conv:${data.conversationId}`);
71
+ ctx.store.addConversationMember(data.conversationId, ctx.userId);
72
+ ctx.store.joinConversation(ctx.userId, data.conversationId);
73
+ }
74
+ export function handleConversationLeave(ctx, data) {
75
+ ctx.leaveRoom(`conv:${data.conversationId}`);
76
+ ctx.store.removeConversationMember(data.conversationId, ctx.userId);
77
+ ctx.store.leaveConversation(ctx.userId, data.conversationId);
78
+ }
79
+ export async function handleConversationArchive(ctx, data) {
80
+ // Verify the user is a participant
81
+ if (ctx.db) {
82
+ const row = await ctx.db.get('SELECT 1 FROM conversation_participants WHERE conversation_id = ? AND user_id = ?', data.conversationId, ctx.userId);
83
+ if (!row) {
84
+ ctx.push({ event: 'error', data: { code: 'FORBIDDEN', message: 'Not a participant of this conversation' } });
85
+ return;
86
+ }
87
+ await ctx.db.run(`UPDATE conversations SET archived = ?, updated_at = ? WHERE id = ?`, data.archived ? 1 : 0, new Date().toISOString(), data.conversationId);
88
+ }
89
+ ctx.broadcastAll(`conv:${data.conversationId}`, {
90
+ event: 'conversation:updated',
91
+ data: { id: data.conversationId, archived: data.archived },
92
+ });
93
+ }
94
+ export async function handleConversationMute(ctx, data) {
95
+ if (ctx.db) {
96
+ await ctx.db.run(`UPDATE conversation_participants SET muted = ?, updated_at = ? WHERE conversation_id = ? AND user_id = ?`, data.muted ? 1 : 0, new Date().toISOString(), data.conversationId, ctx.userId);
97
+ }
98
+ // Only notify the requesting user — mute is per-user
99
+ ctx.push({
100
+ event: 'conversation:updated',
101
+ data: { id: data.conversationId, muted: data.muted },
102
+ });
103
+ }
104
+ export async function handleConversationPin(ctx, data) {
105
+ if (ctx.db) {
106
+ await ctx.db.run(`UPDATE conversation_participants SET pinned = ?, updated_at = ? WHERE conversation_id = ? AND user_id = ?`, data.pinned ? 1 : 0, new Date().toISOString(), data.conversationId, ctx.userId);
107
+ }
108
+ // Only notify the requesting user — pin is per-user
109
+ ctx.push({
110
+ event: 'conversation:updated',
111
+ data: { id: data.conversationId, pinned: data.pinned },
112
+ });
113
+ }
@@ -0,0 +1,17 @@
1
+ import type { HandlerContext } from './context.js';
2
+ /**
3
+ * Handle a client requesting a presigned upload URL for a chat file attachment.
4
+ *
5
+ * E2E security model:
6
+ * 1. Client encrypts the file locally (AES-256-GCM with a random key).
7
+ * 2. This handler returns a presigned PUT URL — the server never sees the plaintext.
8
+ * 3. Client PUTs the encrypted blob directly to storage (S3 / local).
9
+ * 4. The file encryption key is included inside the Signal message envelope,
10
+ * encrypted per-recipient. The server cannot decrypt either the file or the key.
11
+ */
12
+ export declare function handleFileUploadRequest(ctx: HandlerContext, data: {
13
+ conversationId: string;
14
+ mimeType: string;
15
+ size: number;
16
+ fileName: string;
17
+ }): Promise<void>;
@@ -0,0 +1,62 @@
1
+ // ── File Upload (Presigned URL) ──────────────────────────────────
2
+ /**
3
+ * Handle a client requesting a presigned upload URL for a chat file attachment.
4
+ *
5
+ * E2E security model:
6
+ * 1. Client encrypts the file locally (AES-256-GCM with a random key).
7
+ * 2. This handler returns a presigned PUT URL — the server never sees the plaintext.
8
+ * 3. Client PUTs the encrypted blob directly to storage (S3 / local).
9
+ * 4. The file encryption key is included inside the Signal message envelope,
10
+ * encrypted per-recipient. The server cannot decrypt either the file or the key.
11
+ */
12
+ export async function handleFileUploadRequest(ctx, data) {
13
+ if (!ctx.storage) {
14
+ ctx.push({
15
+ event: 'file:upload-error',
16
+ data: { code: 'STORAGE_NOT_CONFIGURED', message: 'Storage is not configured on this server.' },
17
+ });
18
+ return;
19
+ }
20
+ // Validate against file upload config
21
+ if (ctx.config.fileUpload) {
22
+ const { maxFileSize, allowedMimeTypes } = ctx.config.fileUpload;
23
+ if (data.size > maxFileSize) {
24
+ ctx.push({
25
+ event: 'file:upload-error',
26
+ data: {
27
+ code: 'FILE_TOO_LARGE',
28
+ message: `File size ${data.size} exceeds maximum allowed size of ${maxFileSize} bytes.`,
29
+ },
30
+ });
31
+ return;
32
+ }
33
+ if (allowedMimeTypes && allowedMimeTypes.length > 0 && !allowedMimeTypes.includes(data.mimeType)) {
34
+ ctx.push({
35
+ event: 'file:upload-error',
36
+ data: {
37
+ code: 'MIME_TYPE_NOT_ALLOWED',
38
+ message: `MIME type '${data.mimeType}' is not allowed.`,
39
+ },
40
+ });
41
+ return;
42
+ }
43
+ }
44
+ // Generate a unique file key scoped to the conversation
45
+ const fileId = crypto.randomUUID();
46
+ const ext = data.fileName.includes('.') ? `.${data.fileName.split('.').pop()}` : '';
47
+ const key = `conversations/${data.conversationId}/${fileId}${ext}`;
48
+ const presigned = await ctx.storage.presignPut(key, {
49
+ mimeType: data.mimeType,
50
+ expiresIn: 300, // 5 minutes to complete the upload
51
+ maxSize: ctx.config.fileUpload?.maxFileSize,
52
+ });
53
+ ctx.push({
54
+ event: 'file:upload-ready',
55
+ data: {
56
+ fileId,
57
+ uploadUrl: presigned.uploadUrl,
58
+ key: presigned.key,
59
+ expiresAt: presigned.expiresAt,
60
+ },
61
+ });
62
+ }
@@ -0,0 +1,30 @@
1
+ import type { Message, MessageAttachment, MessageForward, EncryptedEnvelope } from '../types.js';
2
+ import type { HandlerContext } from './context.js';
3
+ export declare function handleMessageSend(ctx: HandlerContext, data: {
4
+ conversationId: string;
5
+ content: string;
6
+ type: Message['type'];
7
+ replyTo?: string;
8
+ attachment?: MessageAttachment;
9
+ encrypted?: boolean;
10
+ envelope?: EncryptedEnvelope;
11
+ }): Promise<void>;
12
+ export declare function handleMessageRead(ctx: HandlerContext, data: {
13
+ conversationId: string;
14
+ messageId: string;
15
+ }): Promise<void>;
16
+ export declare function handleMessageReact(ctx: HandlerContext, data: {
17
+ messageId: string;
18
+ conversationId: string;
19
+ emoji: string;
20
+ }): Promise<void>;
21
+ export declare function handleMessageEdit(ctx: HandlerContext, data: {
22
+ messageId: string;
23
+ conversationId: string;
24
+ content: string;
25
+ }): Promise<void>;
26
+ export declare function handleMessageDelete(ctx: HandlerContext, data: {
27
+ messageId: string;
28
+ conversationId: string;
29
+ }): Promise<void>;
30
+ export declare function handleMessageForward(ctx: HandlerContext, data: MessageForward): Promise<void>;