@nuraly/lumenjs 0.1.3 → 0.2.0

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 (306) 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 +82 -0
  11. package/dist/auth/native-auth.js +340 -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 +121 -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/totp.d.ts +22 -0
  31. package/dist/auth/routes/totp.js +232 -0
  32. package/dist/auth/routes/utils.d.ts +7 -0
  33. package/dist/auth/routes/utils.js +35 -0
  34. package/dist/auth/routes/verify.d.ts +3 -0
  35. package/dist/auth/routes/verify.js +26 -0
  36. package/dist/auth/routes.d.ts +8 -0
  37. package/dist/auth/routes.js +124 -0
  38. package/dist/auth/session.d.ts +8 -0
  39. package/dist/auth/session.js +54 -0
  40. package/dist/auth/token.d.ts +33 -0
  41. package/dist/auth/token.js +90 -0
  42. package/dist/auth/types.d.ts +156 -0
  43. package/dist/auth/types.js +2 -0
  44. package/dist/build/build-client.d.ts +15 -0
  45. package/dist/build/build-client.js +45 -0
  46. package/dist/build/build-prerender.d.ts +11 -0
  47. package/dist/build/build-prerender.js +159 -0
  48. package/dist/build/build-server.d.ts +18 -0
  49. package/dist/build/build-server.js +107 -0
  50. package/dist/build/build.js +60 -123
  51. package/dist/build/scan.d.ts +18 -0
  52. package/dist/build/scan.js +77 -6
  53. package/dist/build/serve-api.js +8 -2
  54. package/dist/build/serve-loaders.d.ts +4 -4
  55. package/dist/build/serve-loaders.js +26 -18
  56. package/dist/build/serve-ssr.js +38 -11
  57. package/dist/build/serve-static.js +3 -3
  58. package/dist/build/serve.js +341 -18
  59. package/dist/cli.js +37 -6
  60. package/dist/communication/encryption.d.ts +35 -0
  61. package/dist/communication/encryption.js +90 -0
  62. package/dist/communication/handlers/context.d.ts +27 -0
  63. package/dist/communication/handlers/context.js +1 -0
  64. package/dist/communication/handlers/conversation.d.ts +24 -0
  65. package/dist/communication/handlers/conversation.js +113 -0
  66. package/dist/communication/handlers/file-upload.d.ts +17 -0
  67. package/dist/communication/handlers/file-upload.js +62 -0
  68. package/dist/communication/handlers/messaging.d.ts +30 -0
  69. package/dist/communication/handlers/messaging.js +237 -0
  70. package/dist/communication/handlers/presence.d.ts +15 -0
  71. package/dist/communication/handlers/presence.js +76 -0
  72. package/dist/communication/handlers.d.ts +5 -0
  73. package/dist/communication/handlers.js +5 -0
  74. package/dist/communication/index.d.ts +9 -0
  75. package/dist/communication/index.js +7 -0
  76. package/dist/communication/link-preview.d.ts +18 -0
  77. package/dist/communication/link-preview.js +115 -0
  78. package/dist/communication/schema.d.ts +10 -0
  79. package/dist/communication/schema.js +101 -0
  80. package/dist/communication/server.d.ts +86 -0
  81. package/dist/communication/server.js +212 -0
  82. package/dist/communication/signaling.d.ts +43 -0
  83. package/dist/communication/signaling.js +271 -0
  84. package/dist/communication/store.d.ts +71 -0
  85. package/dist/communication/store.js +289 -0
  86. package/dist/communication/types.d.ts +454 -0
  87. package/dist/communication/types.js +1 -0
  88. package/dist/create.d.ts +1 -0
  89. package/dist/create.js +55 -0
  90. package/dist/db/auto-migrate.d.ts +3 -0
  91. package/dist/db/auto-migrate.js +100 -0
  92. package/dist/db/client.d.ts +3 -0
  93. package/dist/db/client.js +18 -0
  94. package/dist/db/index.d.ts +17 -13
  95. package/dist/db/index.js +205 -26
  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 +11 -0
  101. package/dist/dev-server/config.js +40 -20
  102. package/dist/dev-server/index-html.d.ts +4 -0
  103. package/dist/dev-server/index-html.js +21 -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.js +146 -13
  122. package/dist/dev-server/plugins/vite-plugin-routes.js +16 -5
  123. package/dist/dev-server/plugins/vite-plugin-socketio.d.ts +2 -0
  124. package/dist/dev-server/plugins/vite-plugin-socketio.js +51 -0
  125. package/dist/dev-server/plugins/vite-plugin-source-annotator.d.ts +2 -0
  126. package/dist/dev-server/plugins/vite-plugin-source-annotator.js +26 -3
  127. package/dist/dev-server/plugins/vite-plugin-storage.d.ts +10 -0
  128. package/dist/dev-server/plugins/vite-plugin-storage.js +126 -0
  129. package/dist/dev-server/plugins/vite-plugin-virtual-modules.js +140 -3
  130. package/dist/dev-server/server.js +242 -70
  131. package/dist/dev-server/ssr-render.d.ts +2 -1
  132. package/dist/dev-server/ssr-render.js +117 -50
  133. package/dist/editor/ai/backend.d.ts +20 -0
  134. package/dist/editor/ai/backend.js +113 -0
  135. package/dist/editor/ai/claude-code-client.d.ts +20 -0
  136. package/dist/editor/ai/claude-code-client.js +145 -0
  137. package/dist/editor/ai/deepseek-client.d.ts +7 -0
  138. package/dist/editor/ai/deepseek-client.js +113 -0
  139. package/dist/editor/ai/opencode-client.d.ts +14 -0
  140. package/dist/editor/ai/opencode-client.js +99 -0
  141. package/dist/editor/ai/snapshot-store.d.ts +22 -0
  142. package/dist/editor/ai/snapshot-store.js +35 -0
  143. package/dist/editor/ai/types.d.ts +30 -0
  144. package/dist/editor/ai/types.js +136 -0
  145. package/dist/editor/ai-chat-panel.d.ts +13 -0
  146. package/dist/editor/ai-chat-panel.js +613 -0
  147. package/dist/editor/ai-markdown.d.ts +10 -0
  148. package/dist/editor/ai-markdown.js +70 -0
  149. package/dist/editor/ai-project-panel.d.ts +11 -0
  150. package/dist/editor/ai-project-panel.js +332 -0
  151. package/dist/editor/ast-modification.d.ts +11 -0
  152. package/dist/editor/ast-modification.js +1 -0
  153. package/dist/editor/ast-service.d.ts +30 -0
  154. package/dist/editor/ast-service.js +180 -0
  155. package/dist/editor/css-rules.d.ts +54 -0
  156. package/dist/editor/css-rules.js +423 -0
  157. package/dist/editor/editor-api-client.d.ts +51 -0
  158. package/dist/editor/editor-api-client.js +162 -0
  159. package/dist/editor/editor-bridge.d.ts +1 -0
  160. package/dist/editor/editor-bridge.js +18 -8
  161. package/dist/editor/editor-toolbar.d.ts +14 -0
  162. package/dist/editor/editor-toolbar.js +115 -0
  163. package/dist/editor/file-editor.d.ts +9 -0
  164. package/dist/editor/file-editor.js +236 -0
  165. package/dist/editor/file-service.d.ts +16 -0
  166. package/dist/editor/file-service.js +52 -0
  167. package/dist/editor/i18n-key-gen.d.ts +1 -0
  168. package/dist/editor/i18n-key-gen.js +7 -0
  169. package/dist/editor/inline-text-edit.d.ts +5 -0
  170. package/dist/editor/inline-text-edit.js +173 -92
  171. package/dist/editor/overlay-events.d.ts +5 -0
  172. package/dist/editor/overlay-events.js +364 -0
  173. package/dist/editor/overlay-hmr.d.ts +2 -0
  174. package/dist/editor/overlay-hmr.js +76 -0
  175. package/dist/editor/overlay-selection.d.ts +29 -0
  176. package/dist/editor/overlay-selection.js +148 -0
  177. package/dist/editor/overlay-utils.d.ts +12 -0
  178. package/dist/editor/overlay-utils.js +59 -0
  179. package/dist/editor/properties-panel-persist.d.ts +14 -0
  180. package/dist/editor/properties-panel-persist.js +70 -0
  181. package/dist/editor/properties-panel-rows.d.ts +10 -0
  182. package/dist/editor/properties-panel-rows.js +349 -0
  183. package/dist/editor/properties-panel-styles.d.ts +4 -0
  184. package/dist/editor/properties-panel-styles.js +174 -0
  185. package/dist/editor/properties-panel.d.ts +4 -0
  186. package/dist/editor/properties-panel.js +148 -0
  187. package/dist/editor/property-registry.d.ts +16 -0
  188. package/dist/editor/property-registry.js +303 -0
  189. package/dist/editor/standalone-file-panel.d.ts +0 -0
  190. package/dist/editor/standalone-file-panel.js +1 -0
  191. package/dist/editor/standalone-overlay-dom.d.ts +0 -0
  192. package/dist/editor/standalone-overlay-dom.js +1 -0
  193. package/dist/editor/standalone-overlay-styles.d.ts +0 -0
  194. package/dist/editor/standalone-overlay-styles.js +1 -0
  195. package/dist/editor/standalone-overlay.d.ts +1 -0
  196. package/dist/editor/standalone-overlay.js +76 -0
  197. package/dist/editor/syntax-highlighter.d.ts +4 -0
  198. package/dist/editor/syntax-highlighter.js +81 -0
  199. package/dist/editor/text-toolbar.d.ts +11 -0
  200. package/dist/editor/text-toolbar.js +327 -0
  201. package/dist/editor/toolbar-styles.d.ts +4 -0
  202. package/dist/editor/toolbar-styles.js +198 -0
  203. package/dist/email/index.d.ts +32 -0
  204. package/dist/email/index.js +154 -0
  205. package/dist/email/providers/resend.d.ts +2 -0
  206. package/dist/email/providers/resend.js +24 -0
  207. package/dist/email/providers/sendgrid.d.ts +2 -0
  208. package/dist/email/providers/sendgrid.js +31 -0
  209. package/dist/email/providers/smtp.d.ts +13 -0
  210. package/dist/email/providers/smtp.js +125 -0
  211. package/dist/email/template-engine.d.ts +18 -0
  212. package/dist/email/template-engine.js +116 -0
  213. package/dist/email/templates/base.d.ts +9 -0
  214. package/dist/email/templates/base.js +65 -0
  215. package/dist/email/templates/password-reset.d.ts +5 -0
  216. package/dist/email/templates/password-reset.js +15 -0
  217. package/dist/email/templates/verify-email.d.ts +5 -0
  218. package/dist/email/templates/verify-email.js +15 -0
  219. package/dist/email/templates/welcome.d.ts +5 -0
  220. package/dist/email/templates/welcome.js +13 -0
  221. package/dist/email/types.d.ts +49 -0
  222. package/dist/email/types.js +1 -0
  223. package/dist/llms/generate.d.ts +46 -0
  224. package/dist/llms/generate.js +185 -0
  225. package/dist/permissions/guard.d.ts +28 -0
  226. package/dist/permissions/guard.js +30 -0
  227. package/dist/permissions/index.d.ts +6 -0
  228. package/dist/permissions/index.js +3 -0
  229. package/dist/permissions/service.d.ts +80 -0
  230. package/dist/permissions/service.js +210 -0
  231. package/dist/permissions/tables.d.ts +5 -0
  232. package/dist/permissions/tables.js +68 -0
  233. package/dist/permissions/types.d.ts +33 -0
  234. package/dist/permissions/types.js +1 -0
  235. package/dist/runtime/app-shell.d.ts +1 -1
  236. package/dist/runtime/app-shell.js +164 -0
  237. package/dist/runtime/auth.d.ts +10 -0
  238. package/dist/runtime/auth.js +30 -0
  239. package/dist/runtime/communication.d.ts +137 -0
  240. package/dist/runtime/communication.js +228 -0
  241. package/dist/runtime/error-boundary.d.ts +23 -0
  242. package/dist/runtime/error-boundary.js +120 -0
  243. package/dist/runtime/i18n.d.ts +6 -1
  244. package/dist/runtime/i18n.js +42 -21
  245. package/dist/runtime/island.d.ts +16 -0
  246. package/dist/runtime/island.js +80 -0
  247. package/dist/runtime/router-data.d.ts +3 -0
  248. package/dist/runtime/router-data.js +102 -17
  249. package/dist/runtime/router-hydration.js +34 -2
  250. package/dist/runtime/router.d.ts +19 -2
  251. package/dist/runtime/router.js +237 -43
  252. package/dist/runtime/socket-client.d.ts +2 -0
  253. package/dist/runtime/socket-client.js +30 -0
  254. package/dist/runtime/webrtc.d.ts +91 -0
  255. package/dist/runtime/webrtc.js +428 -0
  256. package/dist/shared/dom-shims.js +4 -2
  257. package/dist/shared/graceful-shutdown.d.ts +8 -0
  258. package/dist/shared/graceful-shutdown.js +36 -0
  259. package/dist/shared/health.d.ts +8 -0
  260. package/dist/shared/health.js +25 -0
  261. package/dist/shared/llms-txt.d.ts +31 -0
  262. package/dist/shared/llms-txt.js +85 -0
  263. package/dist/shared/logger.d.ts +32 -0
  264. package/dist/shared/logger.js +93 -0
  265. package/dist/shared/meta.d.ts +27 -0
  266. package/dist/shared/meta.js +71 -0
  267. package/dist/shared/middleware-runner.d.ts +9 -0
  268. package/dist/shared/middleware-runner.js +29 -0
  269. package/dist/shared/rate-limit.d.ts +18 -0
  270. package/dist/shared/rate-limit.js +71 -0
  271. package/dist/shared/request-id.d.ts +5 -0
  272. package/dist/shared/request-id.js +18 -0
  273. package/dist/shared/route-matching.js +16 -1
  274. package/dist/shared/security-headers.d.ts +18 -0
  275. package/dist/shared/security-headers.js +38 -0
  276. package/dist/shared/socket-io-setup.d.ts +11 -0
  277. package/dist/shared/socket-io-setup.js +51 -0
  278. package/dist/shared/types.d.ts +15 -0
  279. package/dist/shared/utils.d.ts +33 -7
  280. package/dist/shared/utils.js +164 -27
  281. package/dist/storage/adapters/local.d.ts +44 -0
  282. package/dist/storage/adapters/local.js +85 -0
  283. package/dist/storage/adapters/s3.d.ts +32 -0
  284. package/dist/storage/adapters/s3.js +119 -0
  285. package/dist/storage/adapters/types.d.ts +53 -0
  286. package/dist/storage/adapters/types.js +1 -0
  287. package/dist/storage/index.d.ts +76 -0
  288. package/dist/storage/index.js +83 -0
  289. package/package.json +45 -7
  290. package/templates/blog/api/posts.ts +4 -18
  291. package/templates/blog/data/migrations/001_init.sql +6 -5
  292. package/templates/blog/lumenjs.config.ts +3 -0
  293. package/templates/blog/package.json +14 -0
  294. package/templates/blog/pages/_layout.ts +25 -0
  295. package/templates/blog/pages/index.ts +48 -22
  296. package/templates/blog/pages/posts/[slug].ts +45 -20
  297. package/templates/blog/pages/tag/[tag].ts +44 -0
  298. package/templates/dashboard/api/stats.ts +8 -5
  299. package/templates/dashboard/lumenjs.config.ts +3 -0
  300. package/templates/dashboard/package.json +14 -0
  301. package/templates/dashboard/pages/_layout.ts +25 -0
  302. package/templates/dashboard/pages/index.ts +54 -23
  303. package/templates/dashboard/pages/settings/index.ts +29 -0
  304. package/templates/default/lumenjs.config.ts +3 -0
  305. package/templates/default/package.json +14 -0
  306. package/templates/default/pages/index.ts +24 -0
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Client-side communication SDK.
3
+ * Connects to the server via Socket.io and provides a clean API for chat, typing, and presence.
4
+ */
5
+ let _socket = null;
6
+ let _handlers = new Map();
7
+ function emit(event, data) {
8
+ if (!_socket)
9
+ throw new Error('Communication not connected. Call connectChat() first.');
10
+ _socket.emit(`nk:${event}`, data);
11
+ }
12
+ function addHandler(event, handler) {
13
+ let set = _handlers.get(event);
14
+ if (!set) {
15
+ set = new Set();
16
+ _handlers.set(event, set);
17
+ }
18
+ set.add(handler);
19
+ }
20
+ function removeHandler(event, handler) {
21
+ _handlers.get(event)?.delete(handler);
22
+ }
23
+ /**
24
+ * Connect to the communication socket.
25
+ * Must be called before using any other functions.
26
+ */
27
+ export async function connectChat(params) {
28
+ if (_socket)
29
+ return;
30
+ const { io } = await import('socket.io-client');
31
+ _socket = io('/nk/messages', {
32
+ path: '/__nk_socketio/',
33
+ query: { ...params, __params: JSON.stringify(params || {}) },
34
+ });
35
+ _socket.on('nk:data', (data) => {
36
+ if (data?.event) {
37
+ const handlers = _handlers.get(data.event);
38
+ if (handlers) {
39
+ for (const h of handlers)
40
+ h(data.data);
41
+ }
42
+ }
43
+ });
44
+ }
45
+ /** Join a conversation room to receive messages */
46
+ export function joinConversation(conversationId) {
47
+ emit('conversation:join', { conversationId });
48
+ }
49
+ /** Leave a conversation room */
50
+ export function leaveConversation(conversationId) {
51
+ emit('conversation:leave', { conversationId });
52
+ }
53
+ /** Send a message */
54
+ export function sendMessage(conversationId, content, type = 'text') {
55
+ emit('message:send', { conversationId, content, type });
56
+ }
57
+ /** Mark a message as read */
58
+ export function markRead(conversationId, messageId) {
59
+ emit('message:read', { conversationId, messageId });
60
+ }
61
+ /** React to a message with an emoji (toggle) */
62
+ export function reactToMessage(messageId, conversationId, emoji) {
63
+ emit('message:react', { messageId, conversationId, emoji });
64
+ }
65
+ /** Edit a message */
66
+ export function editMessage(messageId, conversationId, content) {
67
+ emit('message:edit', { messageId, conversationId, content });
68
+ }
69
+ /** Delete a message */
70
+ export function deleteMessage(messageId, conversationId) {
71
+ emit('message:delete', { messageId, conversationId });
72
+ }
73
+ /** Listen for reaction updates */
74
+ export function onReactionUpdate(handler) {
75
+ addHandler('message:reaction-update', handler);
76
+ return () => removeHandler('message:reaction-update', handler);
77
+ }
78
+ /** Listen for message edits */
79
+ export function onMessageUpdated(handler) {
80
+ addHandler('message:updated', handler);
81
+ return () => removeHandler('message:updated', handler);
82
+ }
83
+ /** Listen for message deletions */
84
+ export function onMessageDeleted(handler) {
85
+ addHandler('message:deleted', handler);
86
+ return () => removeHandler('message:deleted', handler);
87
+ }
88
+ /** Upload a file (returns attachment metadata) */
89
+ export async function uploadFile(file, filename, encrypted = false) {
90
+ const res = await fetch('/__nk_comm/upload', {
91
+ method: 'POST',
92
+ headers: {
93
+ 'Content-Type': file.type || 'application/octet-stream',
94
+ 'X-Filename': filename,
95
+ ...(encrypted ? { 'X-Encrypted': '1' } : {}),
96
+ },
97
+ body: file,
98
+ });
99
+ if (!res.ok)
100
+ throw new Error(`Upload failed: ${res.status}`);
101
+ return res.json();
102
+ }
103
+ /** Fetch link previews for a text */
104
+ export async function fetchLinkPreviews(text) {
105
+ const res = await fetch('/__nk_comm/link-preview', {
106
+ method: 'POST',
107
+ headers: { 'Content-Type': 'application/json' },
108
+ body: JSON.stringify({ text }),
109
+ });
110
+ if (!res.ok)
111
+ return [];
112
+ const data = await res.json();
113
+ return data.previews || [];
114
+ }
115
+ /** Start typing indicator */
116
+ export function startTyping(conversationId) {
117
+ emit('typing:start', { conversationId });
118
+ }
119
+ /** Stop typing indicator */
120
+ export function stopTyping(conversationId) {
121
+ emit('typing:stop', { conversationId });
122
+ }
123
+ /** Update presence status */
124
+ export function updatePresence(status) {
125
+ emit('presence:update', { status });
126
+ }
127
+ /** Listen for new messages */
128
+ export function onMessage(handler) {
129
+ addHandler('message:new', handler);
130
+ return () => removeHandler('message:new', handler);
131
+ }
132
+ /** Listen for typing updates */
133
+ export function onTyping(handler) {
134
+ addHandler('typing:update', handler);
135
+ return () => removeHandler('typing:update', handler);
136
+ }
137
+ /** Listen for presence changes */
138
+ export function onPresence(handler) {
139
+ addHandler('presence:changed', handler);
140
+ return () => removeHandler('presence:changed', handler);
141
+ }
142
+ /** Listen for read receipts */
143
+ export function onReadReceipt(handler) {
144
+ addHandler('read-receipt:update', handler);
145
+ return () => removeHandler('read-receipt:update', handler);
146
+ }
147
+ // ── Calls ─────────────────────────────────────────────────────────
148
+ /** Listen for incoming calls */
149
+ export function onIncomingCall(handler) {
150
+ addHandler('call:incoming', handler);
151
+ return () => removeHandler('call:incoming', handler);
152
+ }
153
+ /** Listen for call state changes (initiating, ringing, connecting, connected, ended) */
154
+ export function onCallStateChanged(handler) {
155
+ addHandler('call:state-changed', handler);
156
+ return () => removeHandler('call:state-changed', handler);
157
+ }
158
+ /** Listen for participants joining a call */
159
+ export function onParticipantJoined(handler) {
160
+ addHandler('call:participant-joined', handler);
161
+ return () => removeHandler('call:participant-joined', handler);
162
+ }
163
+ /** Listen for participants leaving a call */
164
+ export function onParticipantLeft(handler) {
165
+ addHandler('call:participant-left', handler);
166
+ return () => removeHandler('call:participant-left', handler);
167
+ }
168
+ /** Listen for media changes (mute/unmute/screenshare) */
169
+ export function onMediaChanged(handler) {
170
+ addHandler('call:media-changed', handler);
171
+ return () => removeHandler('call:media-changed', handler);
172
+ }
173
+ /** Initiate an audio or video call */
174
+ export function initiateCall(conversationId, type, calleeIds) {
175
+ emit('call:initiate', { conversationId, type, calleeIds });
176
+ }
177
+ /** Respond to an incoming call */
178
+ export function respondToCall(callId, action) {
179
+ emit('call:respond', { callId, action });
180
+ }
181
+ /** Hang up an active call */
182
+ export function hangup(callId, reason = 'completed') {
183
+ emit('call:hangup', { callId, reason });
184
+ }
185
+ /** Toggle audio/video/screenshare during a call */
186
+ export function toggleMedia(callId, opts) {
187
+ emit('call:media-toggle', { callId, ...opts });
188
+ }
189
+ // ── WebRTC Signaling ──────────────────────────────────────────────
190
+ /** Send SDP offer to a peer */
191
+ export function sendOffer(callId, toUserId, sdp) {
192
+ emit('signal:offer', { callId, fromUserId: '', toUserId, type: 'offer', sdp });
193
+ }
194
+ /** Send SDP answer to a peer */
195
+ export function sendAnswer(callId, toUserId, sdp) {
196
+ emit('signal:answer', { callId, fromUserId: '', toUserId, type: 'answer', sdp });
197
+ }
198
+ /** Send ICE candidate to a peer */
199
+ export function sendIceCandidate(callId, toUserId, candidate, sdpMLineIndex, sdpMid) {
200
+ emit('signal:ice-candidate', { callId, fromUserId: '', toUserId, candidate, sdpMLineIndex, sdpMid });
201
+ }
202
+ /** Listen for SDP offers from peers */
203
+ export function onSignalOffer(handler) {
204
+ addHandler('signal:offer', handler);
205
+ return () => removeHandler('signal:offer', handler);
206
+ }
207
+ /** Listen for SDP answers from peers */
208
+ export function onSignalAnswer(handler) {
209
+ addHandler('signal:answer', handler);
210
+ return () => removeHandler('signal:answer', handler);
211
+ }
212
+ /** Listen for ICE candidates from peers */
213
+ export function onIceCandidate(handler) {
214
+ addHandler('signal:ice-candidate', handler);
215
+ return () => removeHandler('signal:ice-candidate', handler);
216
+ }
217
+ /** Disconnect from communication socket */
218
+ export function disconnect() {
219
+ if (_socket) {
220
+ _socket.disconnect();
221
+ _socket = null;
222
+ _handlers.clear();
223
+ }
224
+ }
225
+ /** Check if connected */
226
+ export function isConnected() {
227
+ return _socket?.connected ?? false;
228
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * <nk-error-boundary> — Client-side error boundary for Lit/web component pages.
3
+ *
4
+ * Wraps page content via <slot>. If a child throws during render or in an
5
+ * event handler, catches the error and displays a fallback UI.
6
+ *
7
+ * Usage:
8
+ * <nk-error-boundary>
9
+ * <my-page></my-page>
10
+ * </nk-error-boundary>
11
+ *
12
+ * Attributes:
13
+ * fallback-message — Custom message shown on error (default: "Something went wrong")
14
+ */
15
+ declare class NkErrorBoundary extends HTMLElement {
16
+ private hasError;
17
+ private caughtError;
18
+ connectedCallback(): void;
19
+ disconnectedCallback(): void;
20
+ private handleError;
21
+ private showFallback;
22
+ private recover;
23
+ }
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ /**
3
+ * <nk-error-boundary> — Client-side error boundary for Lit/web component pages.
4
+ *
5
+ * Wraps page content via <slot>. If a child throws during render or in an
6
+ * event handler, catches the error and displays a fallback UI.
7
+ *
8
+ * Usage:
9
+ * <nk-error-boundary>
10
+ * <my-page></my-page>
11
+ * </nk-error-boundary>
12
+ *
13
+ * Attributes:
14
+ * fallback-message — Custom message shown on error (default: "Something went wrong")
15
+ */
16
+ class NkErrorBoundary extends HTMLElement {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.hasError = false;
20
+ this.caughtError = null;
21
+ this.handleError = (event) => {
22
+ event.stopPropagation();
23
+ this.showFallback(event.error || new Error(event.message));
24
+ };
25
+ }
26
+ connectedCallback() {
27
+ // Listen for unhandled errors from slotted children
28
+ this.addEventListener('error', this.handleError);
29
+ // Create a slot for normal content
30
+ if (!this.shadowRoot) {
31
+ const shadow = this.attachShadow({ mode: 'open' });
32
+ shadow.innerHTML = `
33
+ <style>
34
+ :host { display: contents; }
35
+ .nk-error-fallback {
36
+ display: flex;
37
+ flex-direction: column;
38
+ align-items: center;
39
+ justify-content: center;
40
+ padding: 2rem;
41
+ min-height: 200px;
42
+ font-family: system-ui, -apple-system, sans-serif;
43
+ color: #dc2626;
44
+ text-align: center;
45
+ }
46
+ .nk-error-fallback h2 {
47
+ margin: 0 0 0.5rem;
48
+ font-size: 1.25rem;
49
+ font-weight: 600;
50
+ }
51
+ .nk-error-fallback p {
52
+ margin: 0 0 1rem;
53
+ color: #6b7280;
54
+ font-size: 0.875rem;
55
+ }
56
+ .nk-error-fallback button {
57
+ padding: 0.5rem 1rem;
58
+ border: 1px solid #d1d5db;
59
+ border-radius: 0.375rem;
60
+ background: white;
61
+ color: #374151;
62
+ font-size: 0.875rem;
63
+ cursor: pointer;
64
+ }
65
+ .nk-error-fallback button:hover {
66
+ background: #f9fafb;
67
+ }
68
+ .nk-error-hidden { display: none; }
69
+ </style>
70
+ <slot></slot>
71
+ <div class="nk-error-fallback nk-error-hidden">
72
+ <h2></h2>
73
+ <p></p>
74
+ <button>Try again</button>
75
+ </div>
76
+ `;
77
+ const button = shadow.querySelector('button');
78
+ button.addEventListener('click', () => this.recover());
79
+ }
80
+ }
81
+ disconnectedCallback() {
82
+ this.removeEventListener('error', this.handleError);
83
+ }
84
+ showFallback(error) {
85
+ this.hasError = true;
86
+ this.caughtError = error;
87
+ const shadow = this.shadowRoot;
88
+ if (!shadow)
89
+ return;
90
+ const slot = shadow.querySelector('slot');
91
+ const fallback = shadow.querySelector('.nk-error-fallback');
92
+ const message = this.getAttribute('fallback-message') || 'Something went wrong';
93
+ slot.classList.add('nk-error-hidden');
94
+ fallback.classList.remove('nk-error-hidden');
95
+ fallback.querySelector('h2').textContent = message;
96
+ fallback.querySelector('p').textContent = error.message || 'An unexpected error occurred.';
97
+ // Dispatch custom event for external error tracking
98
+ this.dispatchEvent(new CustomEvent('nk-error', {
99
+ bubbles: true,
100
+ composed: true,
101
+ detail: { error },
102
+ }));
103
+ }
104
+ recover() {
105
+ this.hasError = false;
106
+ this.caughtError = null;
107
+ const shadow = this.shadowRoot;
108
+ if (!shadow)
109
+ return;
110
+ const slot = shadow.querySelector('slot');
111
+ const fallback = shadow.querySelector('.nk-error-fallback');
112
+ fallback.classList.add('nk-error-hidden');
113
+ slot.classList.remove('nk-error-hidden');
114
+ // Re-trigger the current route to re-render
115
+ window.dispatchEvent(new PopStateEvent('popstate'));
116
+ }
117
+ }
118
+ if (!customElements.get('nk-error-boundary')) {
119
+ customElements.define('nk-error-boundary', NkErrorBoundary);
120
+ }
@@ -1,6 +1,11 @@
1
1
  /**
2
2
  * LumenJS i18n runtime — provides translation lookup, locale management,
3
3
  * and translation loading for both SSR and client-side navigation.
4
+ *
5
+ * State is stored on globalThis so that it survives Vite's SSR module
6
+ * invalidation. During dev SSR, page and layout modules may each get a
7
+ * separate copy of this module after cache invalidation — globalThis
8
+ * ensures they all read from the same translation map.
4
9
  */
5
10
  /**
6
11
  * Look up a translation key. Returns the translated string, or the key itself
@@ -31,7 +36,7 @@ export declare function initI18n(config: {
31
36
  }, locale: string, trans: Record<string, string>): void;
32
37
  /**
33
38
  * Load translations for a locale from the server and swap them in.
34
- * Used during client-side locale switches.
39
+ * Used during client-side locale switches and HMR updates.
35
40
  */
36
41
  export declare function loadTranslations(locale: string): Promise<void>;
37
42
  /**
@@ -1,31 +1,38 @@
1
1
  /**
2
2
  * LumenJS i18n runtime — provides translation lookup, locale management,
3
3
  * and translation loading for both SSR and client-side navigation.
4
+ *
5
+ * State is stored on globalThis so that it survives Vite's SSR module
6
+ * invalidation. During dev SSR, page and layout modules may each get a
7
+ * separate copy of this module after cache invalidation — globalThis
8
+ * ensures they all read from the same translation map.
4
9
  */
5
- let currentLocale = 'en';
6
- let translations = {};
7
- let i18nConfig = null;
10
+ const G = globalThis;
11
+ if (!G.__nk_i18n) {
12
+ G.__nk_i18n = { locale: 'en', translations: {}, config: null };
13
+ }
14
+ const state = G.__nk_i18n;
8
15
  /**
9
16
  * Look up a translation key. Returns the translated string, or the key itself
10
17
  * if no translation is found.
11
18
  */
12
19
  export function t(key) {
13
- return translations[key] ?? key;
20
+ return state.translations[key] ?? key;
14
21
  }
15
22
  /** Returns the current locale. */
16
23
  export function getLocale() {
17
- return currentLocale;
24
+ return state.locale;
18
25
  }
19
26
  /** Returns the i18n config, or null if i18n is not enabled. */
20
27
  export function getI18nConfig() {
21
- return i18nConfig;
28
+ return state.config;
22
29
  }
23
30
  /**
24
31
  * Switch to a new locale. Navigates to the same pathname under the new
25
32
  * locale prefix and sets the `nk-locale` cookie.
26
33
  */
27
34
  export function setLocale(locale) {
28
- if (!i18nConfig || !i18nConfig.locales.includes(locale))
35
+ if (!state.config || !state.config.locales.includes(locale))
29
36
  return;
30
37
  document.cookie = `nk-locale=${locale};path=/;max-age=${60 * 60 * 24 * 365};SameSite=Lax`;
31
38
  const pathname = stripLocalePrefix(location.pathname);
@@ -37,13 +44,13 @@ export function setLocale(locale) {
37
44
  * Called during hydration (from SSR data) or on first load.
38
45
  */
39
46
  export function initI18n(config, locale, trans) {
40
- i18nConfig = config;
41
- currentLocale = locale;
42
- translations = trans;
47
+ state.config = config;
48
+ state.locale = locale;
49
+ state.translations = trans;
43
50
  }
44
51
  /**
45
52
  * Load translations for a locale from the server and swap them in.
46
- * Used during client-side locale switches.
53
+ * Used during client-side locale switches and HMR updates.
47
54
  */
48
55
  export async function loadTranslations(locale) {
49
56
  const res = await fetch(`/__nk_i18n/${locale}.json`);
@@ -51,8 +58,22 @@ export async function loadTranslations(locale) {
51
58
  console.error(`[i18n] Failed to load translations for locale "${locale}"`);
52
59
  return;
53
60
  }
54
- translations = await res.json();
55
- currentLocale = locale;
61
+ state.translations = await res.json();
62
+ state.locale = locale;
63
+ }
64
+ /**
65
+ * Register the HMR reload handler on the global scope.
66
+ * The i18n Vite plugin injects an inline script that calls this function
67
+ * when a locale file changes — ensuring translations are updated in this
68
+ * module instance (not a duplicate created by Vite's cache-busting).
69
+ */
70
+ if (typeof window !== 'undefined') {
71
+ window.__lumenjs_i18n_reload = async (locale) => {
72
+ if (locale !== state.locale)
73
+ return false;
74
+ await loadTranslations(locale);
75
+ return true;
76
+ };
56
77
  }
57
78
  /**
58
79
  * Strip the locale prefix from a pathname.
@@ -60,10 +81,10 @@ export async function loadTranslations(locale) {
60
81
  * /about → /about
61
82
  */
62
83
  export function stripLocalePrefix(pathname) {
63
- if (!i18nConfig)
84
+ if (!state.config)
64
85
  return pathname;
65
- for (const loc of i18nConfig.locales) {
66
- if (loc === i18nConfig.defaultLocale && !i18nConfig.prefixDefault)
86
+ for (const loc of state.config.locales) {
87
+ if (loc === state.config.defaultLocale && !state.config.prefixDefault)
67
88
  continue;
68
89
  if (pathname === `/${loc}` || pathname.startsWith(`/${loc}/`)) {
69
90
  return pathname.slice(loc.length + 1) || '/';
@@ -77,9 +98,9 @@ export function stripLocalePrefix(pathname) {
77
98
  * (en, /about) → /about (when prefixDefault=false)
78
99
  */
79
100
  export function buildLocalePath(locale, pathname) {
80
- if (!i18nConfig)
101
+ if (!state.config)
81
102
  return pathname;
82
- if (locale === i18nConfig.defaultLocale && !i18nConfig.prefixDefault) {
103
+ if (locale === state.config.defaultLocale && !state.config.prefixDefault) {
83
104
  return pathname;
84
105
  }
85
106
  return `/${locale}${pathname === '/' ? '' : pathname}` || `/${locale}`;
@@ -89,12 +110,12 @@ export function buildLocalePath(locale, pathname) {
89
110
  * Returns the locale and the pathname with the prefix stripped.
90
111
  */
91
112
  export function detectLocaleFromPath(pathname) {
92
- if (!i18nConfig)
113
+ if (!state.config)
93
114
  return { locale: 'en', pathname };
94
- for (const loc of i18nConfig.locales) {
115
+ for (const loc of state.config.locales) {
95
116
  if (pathname === `/${loc}` || pathname.startsWith(`/${loc}/`)) {
96
117
  return { locale: loc, pathname: pathname.slice(loc.length + 1) || '/' };
97
118
  }
98
119
  }
99
- return { locale: i18nConfig.defaultLocale, pathname };
120
+ return { locale: state.config.defaultLocale, pathname };
100
121
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * <nk-island> — Islands Architecture hydration wrapper.
3
+ *
4
+ * Strategies:
5
+ * client:load — import module immediately on page load
6
+ * client:visible — import module when the element scrolls into view
7
+ * client:idle — import module when the browser is idle (requestIdleCallback)
8
+ * client:media — import module when a media query matches (value = query string)
9
+ *
10
+ * The `import` attribute specifies the module path to load.
11
+ */
12
+ declare class NkIsland extends HTMLElement {
13
+ private _loaded;
14
+ connectedCallback(): void;
15
+ private _hydrate;
16
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ /**
3
+ * <nk-island> — Islands Architecture hydration wrapper.
4
+ *
5
+ * Strategies:
6
+ * client:load — import module immediately on page load
7
+ * client:visible — import module when the element scrolls into view
8
+ * client:idle — import module when the browser is idle (requestIdleCallback)
9
+ * client:media — import module when a media query matches (value = query string)
10
+ *
11
+ * The `import` attribute specifies the module path to load.
12
+ */
13
+ class NkIsland extends HTMLElement {
14
+ constructor() {
15
+ super(...arguments);
16
+ this._loaded = false;
17
+ }
18
+ connectedCallback() {
19
+ const importPath = this.getAttribute('import');
20
+ if (!importPath || this._loaded)
21
+ return;
22
+ if (this.hasAttribute('client:load')) {
23
+ this._hydrate(importPath);
24
+ }
25
+ else if (this.hasAttribute('client:visible')) {
26
+ const observer = new IntersectionObserver((entries) => {
27
+ if (entries[0]?.isIntersecting) {
28
+ observer.disconnect();
29
+ this._hydrate(importPath);
30
+ }
31
+ }, { rootMargin: '200px' });
32
+ observer.observe(this);
33
+ }
34
+ else if (this.hasAttribute('client:idle')) {
35
+ const cb = () => this._hydrate(importPath);
36
+ if ('requestIdleCallback' in window) {
37
+ window.requestIdleCallback(cb);
38
+ }
39
+ else {
40
+ setTimeout(cb, 200);
41
+ }
42
+ }
43
+ else if (this.hasAttribute('client:media')) {
44
+ const query = this.getAttribute('client:media') || '';
45
+ const mql = window.matchMedia(query);
46
+ const handler = () => {
47
+ if (mql.matches) {
48
+ mql.removeEventListener('change', handler);
49
+ this._hydrate(importPath);
50
+ }
51
+ };
52
+ if (mql.matches) {
53
+ this._hydrate(importPath);
54
+ }
55
+ else {
56
+ mql.addEventListener('change', handler);
57
+ }
58
+ }
59
+ }
60
+ _hydrate(importPath) {
61
+ if (this._loaded)
62
+ return;
63
+ this._loaded = true;
64
+ // Check global island registry (populated by pages with island imports)
65
+ const registry = window.__nk_islands;
66
+ const loader = registry?.[importPath];
67
+ const promise = loader
68
+ ? loader()
69
+ : import(/* @vite-ignore */ importPath);
70
+ promise.then(() => {
71
+ this.setAttribute('data-hydrated', '');
72
+ this.dispatchEvent(new Event('island-hydrated', { bubbles: true, composed: true }));
73
+ }).catch((err) => {
74
+ console.error(`[nk-island] Failed to load module: ${importPath}`, err);
75
+ });
76
+ }
77
+ }
78
+ if (!customElements.get('nk-island')) {
79
+ customElements.define('nk-island', NkIsland);
80
+ }
@@ -1,3 +1,6 @@
1
+ export declare function getCachedLoaderData(key: string): any | undefined;
2
+ export declare function prefetchLoaderData(pathname: string, params: Record<string, string>): Promise<any>;
3
+ export declare function prefetchLayoutLoaderData(dir: string): Promise<any>;
1
4
  export declare function fetchLoaderData(pathname: string, params: Record<string, string>): Promise<any>;
2
5
  export declare function fetchLayoutLoaderData(dir: string): Promise<any>;
3
6
  export declare function connectSubscribe(pathname: string, params: Record<string, string>): EventSource;