@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
@@ -0,0 +1,76 @@
1
+ import type { StorageAdapter } from './adapters/types.js';
2
+ export type { StorageAdapter, StoredFile, PutOptions, PresignPutOptions, PresignGetOptions, PresignedUpload } from './adapters/types.js';
3
+ export { LocalStorageAdapter } from './adapters/local.js';
4
+ export { S3StorageAdapter } from './adapters/s3.js';
5
+ export interface LocalStorageConfig {
6
+ provider: 'local';
7
+ /** Directory to write uploaded files. Default: './uploads' */
8
+ uploadDir?: string;
9
+ /** URL path prefix for serving files. Default: '/uploads' */
10
+ publicPath?: string;
11
+ }
12
+ export interface S3StorageConfig {
13
+ provider: 's3';
14
+ bucket: string;
15
+ region: string;
16
+ /** Read from env var LUMENJS_S3_ACCESS_KEY if omitted */
17
+ accessKeyId?: string;
18
+ /** Read from env var LUMENJS_S3_SECRET_KEY if omitted */
19
+ secretAccessKey?: string;
20
+ /** Custom endpoint for MinIO / Cloudflare R2 / other S3-compatible APIs */
21
+ endpoint?: string;
22
+ /** Public CDN base URL. Defaults to https://{bucket}.s3.{region}.amazonaws.com */
23
+ publicBaseUrl?: string;
24
+ }
25
+ export type StorageConfig = LocalStorageConfig | S3StorageConfig;
26
+ /**
27
+ * Create a storage adapter from a config object.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * // Local dev (default)
32
+ * const storage = createStorage({ provider: 'local', uploadDir: './uploads' });
33
+ *
34
+ * // AWS S3
35
+ * const storage = createStorage({
36
+ * provider: 's3',
37
+ * bucket: 'my-bucket',
38
+ * region: 'us-east-1',
39
+ * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
40
+ * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
41
+ * });
42
+ *
43
+ * // MinIO / Cloudflare R2
44
+ * const storage = createStorage({
45
+ * provider: 's3',
46
+ * endpoint: 'https://minio.example.com',
47
+ * bucket: 'my-bucket',
48
+ * region: 'us-east-1',
49
+ * accessKeyId: process.env.MINIO_KEY!,
50
+ * secretAccessKey: process.env.MINIO_SECRET!,
51
+ * });
52
+ * ```
53
+ */
54
+ export declare function createStorage(config: StorageConfig): StorageAdapter;
55
+ /**
56
+ * Get the global storage adapter (set by the LumenJS runtime or your app).
57
+ * Returns null if storage has not been configured.
58
+ *
59
+ * Available as `req.storage` in API route handlers.
60
+ */
61
+ export declare function useStorage(): StorageAdapter | null;
62
+ /**
63
+ * Set the global storage adapter.
64
+ * Call this in a `_middleware.ts` or app startup to configure storage.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * // pages/_middleware.ts
69
+ * import { setStorage, createStorage } from '@nuraly/lumenjs/dist/storage/index.js';
70
+ *
71
+ * setStorage(createStorage({ provider: 'local' }));
72
+ * ```
73
+ *
74
+ * @internal also called by vite-plugin-storage in dev mode
75
+ */
76
+ export declare function setStorage(adapter: StorageAdapter | null): void;
@@ -0,0 +1,83 @@
1
+ import { LocalStorageAdapter } from './adapters/local.js';
2
+ import { S3StorageAdapter } from './adapters/s3.js';
3
+ export { LocalStorageAdapter } from './adapters/local.js';
4
+ export { S3StorageAdapter } from './adapters/s3.js';
5
+ // ── Factory ───────────────────────────────────────────────────────
6
+ /**
7
+ * Create a storage adapter from a config object.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * // Local dev (default)
12
+ * const storage = createStorage({ provider: 'local', uploadDir: './uploads' });
13
+ *
14
+ * // AWS S3
15
+ * const storage = createStorage({
16
+ * provider: 's3',
17
+ * bucket: 'my-bucket',
18
+ * region: 'us-east-1',
19
+ * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
20
+ * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
21
+ * });
22
+ *
23
+ * // MinIO / Cloudflare R2
24
+ * const storage = createStorage({
25
+ * provider: 's3',
26
+ * endpoint: 'https://minio.example.com',
27
+ * bucket: 'my-bucket',
28
+ * region: 'us-east-1',
29
+ * accessKeyId: process.env.MINIO_KEY!,
30
+ * secretAccessKey: process.env.MINIO_SECRET!,
31
+ * });
32
+ * ```
33
+ */
34
+ export function createStorage(config) {
35
+ if (config.provider === 's3') {
36
+ const accessKeyId = config.accessKeyId ?? process.env.LUMENJS_S3_ACCESS_KEY ?? '';
37
+ const secretAccessKey = config.secretAccessKey ?? process.env.LUMENJS_S3_SECRET_KEY ?? '';
38
+ if (!accessKeyId || !secretAccessKey) {
39
+ throw new Error('[LumenJS:Storage] S3 credentials are required. ' +
40
+ 'Set accessKeyId/secretAccessKey in config, or set LUMENJS_S3_ACCESS_KEY / LUMENJS_S3_SECRET_KEY env vars.');
41
+ }
42
+ return new S3StorageAdapter({
43
+ bucket: config.bucket,
44
+ region: config.region,
45
+ accessKeyId,
46
+ secretAccessKey,
47
+ endpoint: config.endpoint,
48
+ publicBaseUrl: config.publicBaseUrl,
49
+ });
50
+ }
51
+ return new LocalStorageAdapter({
52
+ uploadDir: config.uploadDir ?? './uploads',
53
+ publicPath: config.publicPath ?? '/uploads',
54
+ });
55
+ }
56
+ // ── Singleton ─────────────────────────────────────────────────────
57
+ let _storage = null;
58
+ /**
59
+ * Get the global storage adapter (set by the LumenJS runtime or your app).
60
+ * Returns null if storage has not been configured.
61
+ *
62
+ * Available as `req.storage` in API route handlers.
63
+ */
64
+ export function useStorage() {
65
+ return _storage;
66
+ }
67
+ /**
68
+ * Set the global storage adapter.
69
+ * Call this in a `_middleware.ts` or app startup to configure storage.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * // pages/_middleware.ts
74
+ * import { setStorage, createStorage } from '@nuraly/lumenjs/dist/storage/index.js';
75
+ *
76
+ * setStorage(createStorage({ provider: 'local' }));
77
+ * ```
78
+ *
79
+ * @internal also called by vite-plugin-storage in dev mode
80
+ */
81
+ export function setStorage(adapter) {
82
+ _storage = adapter;
83
+ }
package/package.json CHANGED
@@ -1,16 +1,12 @@
1
1
  {
2
2
  "name": "@nuraly/lumenjs",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Full-stack Lit web component framework with file-based routing, server loaders, SSR, and API routes",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
7
7
  "bin": {
8
8
  "lumenjs": "dist/cli.js"
9
9
  },
10
- "exports": {
11
- ".": "./dist/cli.js",
12
- "./db": "./dist/db/index.js"
13
- },
14
10
  "files": [
15
11
  "dist",
16
12
  "templates",
@@ -47,13 +43,29 @@
47
43
  "license": "MIT",
48
44
  "dependencies": {
49
45
  "@lit-labs/ssr": "^3.2.0",
50
- "better-sqlite3": "^11.0.0",
46
+ "better-sqlite3": "^12.8.0",
47
+ "codejar": "^4.2.0",
51
48
  "glob": "^10.3.0",
52
49
  "lit": "^3.1.0",
50
+ "pg": "^8.20.0",
51
+ "socket.io": "^4.8.3",
52
+ "socket.io-client": "^4.8.3",
53
53
  "vite": "^5.4.0"
54
54
  },
55
+ "peerDependencies": {
56
+ "@aws-sdk/client-s3": "^3.0.0",
57
+ "@aws-sdk/s3-request-presigner": "^3.0.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "@aws-sdk/client-s3": {
61
+ "optional": true
62
+ },
63
+ "@aws-sdk/s3-request-presigner": {
64
+ "optional": true
65
+ }
66
+ },
55
67
  "devDependencies": {
56
- "@types/better-sqlite3": "^7.6.0",
68
+ "@types/better-sqlite3": "^7.6.13",
57
69
  "@types/node": "^20.14.2",
58
70
  "@vitest/coverage-v8": "^4.0.18",
59
71
  "typescript": "^5.4.5",
@@ -1,20 +1,6 @@
1
- import { useDb } from '@nuraly/lumenjs/db';
2
-
3
1
  export function GET() {
4
- const db = useDb();
5
- const posts = db.all('SELECT id, title, slug, content, date FROM posts ORDER BY date DESC');
6
- return { posts };
7
- }
8
-
9
- export function POST(req: { body: any }) {
10
- const { title, slug, content } = req.body;
11
- if (!title || !slug || !content) {
12
- throw { status: 400, message: 'title, slug, and content are required' };
13
- }
14
- const db = useDb();
15
- const result = db.run(
16
- 'INSERT INTO posts (title, slug, content) VALUES (?, ?, ?)',
17
- title, slug, content
18
- );
19
- return { id: result.lastInsertRowid, title, slug, content };
2
+ return Response.json([
3
+ { slug: 'hello-world', title: 'Hello World' },
4
+ { slug: 'getting-started', title: 'Getting Started with LumenJS' },
5
+ ]);
20
6
  }
@@ -3,10 +3,11 @@ CREATE TABLE IF NOT EXISTS posts (
3
3
  title TEXT NOT NULL,
4
4
  slug TEXT NOT NULL UNIQUE,
5
5
  content TEXT NOT NULL,
6
- date TEXT NOT NULL DEFAULT (date('now'))
6
+ date TEXT NOT NULL DEFAULT (date('now')),
7
+ tags TEXT NOT NULL DEFAULT '[]'
7
8
  );
8
9
 
9
- INSERT INTO posts (title, slug, content, date) VALUES
10
- ('Hello World', 'hello-world', 'Welcome to your new LumenJS blog! This post was loaded from SQLite.', '2025-01-15'),
11
- ('Getting Started with LumenJS', 'getting-started', 'LumenJS makes it easy to build full-stack web apps with Lit web components and file-based routing.', '2025-01-20'),
12
- ('SQLite Persistence', 'sqlite-persistence', 'LumenJS includes built-in SQLite support via better-sqlite3. Just use useDb() in your loaders and API routes.', '2025-01-25');
10
+ INSERT INTO posts (title, slug, content, date, tags) VALUES
11
+ ('Hello World', 'hello-world', 'Welcome to your new LumenJS blog! This post was loaded from SQLite.', '2025-01-15', '["introduction","lumenjs"]'),
12
+ ('Getting Started with LumenJS', 'getting-started', 'LumenJS makes it easy to build full-stack web apps with Lit web components and file-based routing.', '2025-01-20', '["tutorial","lumenjs","web-components"]'),
13
+ ('SQLite Persistence', 'sqlite-persistence', 'LumenJS includes built-in SQLite support via better-sqlite3. Just use useDb() in your loaders and API routes.', '2025-01-25', '["database","lumenjs"]');
@@ -0,0 +1,3 @@
1
+ export default {
2
+ title: '{{PROJECT_NAME}}',
3
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "lumenjs dev",
7
+ "build": "lumenjs build",
8
+ "serve": "lumenjs serve"
9
+ },
10
+ "dependencies": {
11
+ "@nuraly/lumenjs": "^0.1.3",
12
+ "lit": "^3.1.0"
13
+ }
14
+ }
@@ -0,0 +1,25 @@
1
+ import { LitElement, html, css } from 'lit';
2
+
3
+ export class LayoutRoot extends LitElement {
4
+ static styles = css`
5
+ :host { display: block; min-height: 100vh; font-family: system-ui; color: #1e293b; }
6
+ header { max-width: 720px; margin: 0 auto; padding: 1.5rem 1rem; display: flex; justify-content: space-between; align-items: center; }
7
+ header a { color: #7c3aed; text-decoration: none; font-weight: 600; font-size: 1.125rem; }
8
+ nav a { color: #64748b; text-decoration: none; margin-left: 1.5rem; }
9
+ nav a:hover { color: #7c3aed; }
10
+ main { max-width: 720px; margin: 0 auto; padding: 0 1rem 3rem; }
11
+ `;
12
+
13
+ render() {
14
+ return html`
15
+ <header>
16
+ <a href="/">{{PROJECT_NAME}}</a>
17
+ <nav>
18
+ <a href="/">Home</a>
19
+ <a href="/posts">Posts</a>
20
+ </nav>
21
+ </header>
22
+ <main><slot></slot></main>
23
+ `;
24
+ }
25
+ }
@@ -1,37 +1,63 @@
1
1
  import { LitElement, html, css } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
3
- import { useDb } from '@nuraly/lumenjs/db';
4
2
 
5
- export function loader() {
6
- const db = useDb();
7
- const posts = db.all('SELECT id, title, slug, content, date FROM posts ORDER BY date DESC');
8
- return { posts };
3
+ function estimateReadingTime(text: string): number {
4
+ const wordsPerMinute = 200;
5
+ const words = text.trim().split(/\s+/).length;
6
+ return Math.max(1, Math.ceil(words / wordsPerMinute));
9
7
  }
10
8
 
11
- @customElement('page-home')
12
- export class PageHome extends LitElement {
13
- @property({ type: Object }) data: any;
9
+ const POSTS = [
10
+ {
11
+ slug: 'hello-world',
12
+ title: 'Hello World',
13
+ date: '2025-01-15',
14
+ excerpt: 'Welcome to my blog built with LumenJS.',
15
+ content: 'Welcome to my blog! This is a sample post built with LumenJS. Each post is a dynamic route using the [slug] parameter.',
16
+ },
17
+ {
18
+ slug: 'getting-started',
19
+ title: 'Getting Started with LumenJS',
20
+ date: '2025-01-20',
21
+ excerpt: 'Learn how to build web apps with Lit components and file-based routing.',
22
+ content: 'LumenJS uses file-based routing with Lit web components. Create a file in pages/ and it becomes a route. Add a loader() function for server-side data fetching.',
23
+ },
24
+ ];
25
+
26
+ export async function loader() {
27
+ return {
28
+ posts: POSTS.map(({ content, ...post }) => ({
29
+ ...post,
30
+ readingTime: estimateReadingTime(content),
31
+ })),
32
+ };
33
+ }
34
+
35
+ export class PageIndex extends LitElement {
36
+ static properties = { loaderData: { type: Object } };
37
+ loaderData: any = {};
14
38
 
15
39
  static styles = css`
16
- :host { display: block; max-width: 720px; margin: 0 auto; padding: 2rem; font-family: system-ui, sans-serif; }
17
- h1 { font-size: 2rem; margin-bottom: 1.5rem; }
18
- .post { margin-bottom: 1.5rem; padding-bottom: 1.5rem; border-bottom: 1px solid #eee; }
19
- .post h2 { margin: 0 0 0.25rem; }
20
- .post a { color: #0066cc; text-decoration: none; }
21
- .post a:hover { text-decoration: underline; }
22
- .post .date { color: #666; font-size: 0.875rem; }
23
- .post p { color: #333; margin: 0.5rem 0 0; }
40
+ :host { display: block; }
41
+ h1 { font-size: 2rem; margin-bottom: 0.5rem; }
42
+ .subtitle { color: #64748b; margin-bottom: 2rem; }
43
+ .post { border-bottom: 1px solid #e2e8f0; padding: 1.5rem 0; }
44
+ .post:last-child { border-bottom: none; }
45
+ .post a { color: #1e293b; text-decoration: none; font-size: 1.25rem; font-weight: 600; }
46
+ .post a:hover { color: #7c3aed; }
47
+ .meta { color: #94a3b8; font-size: 0.875rem; margin-top: 0.25rem; }
48
+ .excerpt { color: #64748b; margin-top: 0.5rem; line-height: 1.5; }
24
49
  `;
25
50
 
26
51
  render() {
27
- const posts = this.data?.posts || [];
52
+ const posts = this.loaderData.posts || [];
28
53
  return html`
29
54
  <h1>Blog</h1>
30
- ${posts.map((post: any) => html`
55
+ <p class="subtitle">Thoughts and tutorials</p>
56
+ ${posts.map((p: any) => html`
31
57
  <div class="post">
32
- <h2><a href="/posts/${post.slug}">${post.title}</a></h2>
33
- <span class="date">${post.date}</span>
34
- <p>${post.content}</p>
58
+ <a href="/posts/${p.slug}">${p.title}</a>
59
+ <div class="meta">${p.date} · ${p.readingTime} min read</div>
60
+ <p class="excerpt">${p.excerpt}</p>
35
61
  </div>
36
62
  `)}
37
63
  `;
@@ -1,35 +1,60 @@
1
1
  import { LitElement, html, css } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
3
- import { useDb } from '@nuraly/lumenjs/db';
4
2
 
5
- export function loader({ params }: { params: { slug: string } }) {
6
- const db = useDb();
7
- const post = db.get('SELECT id, title, slug, content, date FROM posts WHERE slug = ?', params.slug);
8
- return { post: post || null };
3
+ function estimateReadingTime(text: string): number {
4
+ const wordsPerMinute = 200;
5
+ const words = text.trim().split(/\s+/).length;
6
+ return Math.max(1, Math.ceil(words / wordsPerMinute));
7
+ }
8
+
9
+ const POSTS: Record<string, { title: string; date: string; content: string }> = {
10
+ 'hello-world': {
11
+ title: 'Hello World',
12
+ date: '2025-01-15',
13
+ content: 'Welcome to my blog! This is a sample post built with LumenJS. Each post is a dynamic route using the [slug] parameter.',
14
+ },
15
+ 'getting-started': {
16
+ title: 'Getting Started with LumenJS',
17
+ date: '2025-01-20',
18
+ content: 'LumenJS uses file-based routing with Lit web components. Create a file in pages/ and it becomes a route. Add a loader() function for server-side data fetching.',
19
+ },
20
+ };
21
+
22
+ export async function loader({ params }: { params: { slug: string } }) {
23
+ const post = POSTS[params.slug];
24
+ if (!post) return { notFound: true };
25
+ return {
26
+ ...post,
27
+ readingTime: estimateReadingTime(post.content),
28
+ };
9
29
  }
10
30
 
11
- @customElement('page-post')
12
31
  export class PagePost extends LitElement {
13
- @property({ type: Object }) data: any;
32
+ static properties = { loaderData: { type: Object }, slug: { type: String } };
33
+ loaderData: any = {};
34
+ slug = '';
14
35
 
15
36
  static styles = css`
16
- :host { display: block; max-width: 720px; margin: 0 auto; padding: 2rem; font-family: system-ui, sans-serif; }
17
- a { color: #0066cc; text-decoration: none; }
18
- a:hover { text-decoration: underline; }
19
- .date { color: #666; font-size: 0.875rem; }
20
- .content { margin-top: 1rem; line-height: 1.6; color: #333; }
37
+ :host { display: block; }
38
+ .back { color: #7c3aed; text-decoration: none; font-size: 0.875rem; }
39
+ .back:hover { text-decoration: underline; }
40
+ h1 { font-size: 2rem; margin: 1rem 0 0.25rem; }
41
+ .date { color: #94a3b8; font-size: 0.875rem; margin-bottom: 1.5rem; }
42
+ .content { line-height: 1.8; color: #334155; }
43
+ .not-found { color: #64748b; }
21
44
  `;
22
45
 
23
46
  render() {
24
- const post = this.data?.post;
25
- if (!post) {
26
- return html`<p>Post not found. <a href="/">Back to blog</a></p>`;
47
+ if (this.loaderData.notFound) {
48
+ return html`
49
+ <a class="back" href="/posts">← Back to posts</a>
50
+ <p class="not-found">Post not found.</p>
51
+ `;
27
52
  }
28
53
  return html`
29
- <a href="/">&larr; Back to blog</a>
30
- <h1>${post.title}</h1>
31
- <span class="date">${post.date}</span>
32
- <div class="content">${post.content}</div>
54
+ <a class="back" href="/">← Back to posts</a>
55
+ <h1>${this.loaderData.title}</h1>
56
+ <div class="date">${this.loaderData.date} · ${this.loaderData.readingTime} min read</div>
57
+ <p class="content">${this.loaderData.content}</p>
33
58
  `;
34
59
  }
35
60
  }
@@ -0,0 +1,44 @@
1
+ import { LitElement, html, css } from 'lit';
2
+
3
+ const ALL_POSTS = [
4
+ { slug: 'hello-world', title: 'Hello World', date: '2025-01-15', excerpt: 'Welcome to my blog built with LumenJS.', tags: ['introduction', 'lumenjs'] },
5
+ { slug: 'getting-started', title: 'Getting Started with LumenJS', date: '2025-01-20', excerpt: 'Learn how to build web apps with Lit components and file-based routing.', tags: ['tutorial', 'lumenjs', 'web-components'] },
6
+ ];
7
+
8
+ export async function loader({ params }: { params: { tag: string } }) {
9
+ const posts = ALL_POSTS.filter(p => p.tags.includes(params.tag));
10
+ return { tag: params.tag, posts };
11
+ }
12
+
13
+ export class PageTag extends LitElement {
14
+ static properties = { loaderData: { type: Object } };
15
+ loaderData: any = {};
16
+
17
+ static styles = css`
18
+ :host { display: block; }
19
+ h1 { font-size: 2rem; margin-bottom: 0.5rem; }
20
+ .subtitle { color: #64748b; margin-bottom: 2rem; }
21
+ .post { border-bottom: 1px solid #e2e8f0; padding: 1.5rem 0; }
22
+ .post:last-child { border-bottom: none; }
23
+ .post a { color: #1e293b; text-decoration: none; font-size: 1.25rem; font-weight: 600; }
24
+ .post a:hover { color: #7c3aed; }
25
+ .meta { color: #94a3b8; font-size: 0.875rem; margin-top: 0.25rem; }
26
+ .back { color: #7c3aed; text-decoration: none; font-size: 0.875rem; }
27
+ .back:hover { text-decoration: underline; }
28
+ `;
29
+
30
+ render() {
31
+ const { tag, posts } = this.loaderData;
32
+ return html`
33
+ <a class="back" href="/">← All posts</a>
34
+ <h1>Tagged: ${tag}</h1>
35
+ <p class="subtitle">${posts?.length || 0} post${posts?.length !== 1 ? 's' : ''}</p>
36
+ ${(posts || []).map((p: any) => html`
37
+ <div class="post">
38
+ <a href="/posts/${p.slug}">${p.title}</a>
39
+ <div class="meta">${p.date}</div>
40
+ </div>
41
+ `)}
42
+ `;
43
+ }
44
+ }
@@ -1,7 +1,10 @@
1
- import { useDb } from '@nuraly/lumenjs/db';
2
-
3
1
  export function GET() {
4
- const db = useDb();
5
- const stats = db.all('SELECT id, label, value, unit, updated_at FROM stats ORDER BY id');
6
- return { stats };
2
+ return Response.json({
3
+ stats: [
4
+ { label: 'Users', value: 1234 },
5
+ { label: 'Revenue', value: 12345 },
6
+ { label: 'Orders', value: 567 },
7
+ { label: 'Conversion', value: 3.2 },
8
+ ],
9
+ });
7
10
  }
@@ -0,0 +1,3 @@
1
+ export default {
2
+ title: '{{PROJECT_NAME}}',
3
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "lumenjs dev",
7
+ "build": "lumenjs build",
8
+ "serve": "lumenjs serve"
9
+ },
10
+ "dependencies": {
11
+ "@nuraly/lumenjs": "^0.1.3",
12
+ "lit": "^3.1.0"
13
+ }
14
+ }
@@ -0,0 +1,25 @@
1
+ import { LitElement, html, css } from 'lit';
2
+
3
+ export class LayoutRoot extends LitElement {
4
+ static styles = css`
5
+ :host { display: flex; min-height: 100vh; font-family: system-ui; color: #1e293b; }
6
+ aside { width: 240px; background: #0f172a; color: #cbd5e1; padding: 1.5rem 0; flex-shrink: 0; }
7
+ .logo { padding: 0 1.25rem 1.5rem; font-weight: 700; font-size: 1.125rem; color: #fff; }
8
+ nav a { display: block; padding: 0.625rem 1.25rem; color: #94a3b8; text-decoration: none; font-size: 0.875rem; }
9
+ nav a:hover { background: #1e293b; color: #fff; }
10
+ main { flex: 1; background: #f8fafc; padding: 2rem; }
11
+ `;
12
+
13
+ render() {
14
+ return html`
15
+ <aside>
16
+ <div class="logo">{{PROJECT_NAME}}</div>
17
+ <nav>
18
+ <a href="/">Overview</a>
19
+ <a href="/settings">Settings</a>
20
+ </nav>
21
+ </aside>
22
+ <main><slot></slot></main>
23
+ `;
24
+ }
25
+ }