@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,49 @@
1
+ export interface TemplateData {
2
+ appName: string;
3
+ url: string;
4
+ userName?: string;
5
+ email?: string;
6
+ loginUrl?: string;
7
+ /** Arbitrary extra data for custom templates (arrays, objects, etc.) */
8
+ [key: string]: any;
9
+ }
10
+ export type TemplateRenderer = (data: TemplateData) => string;
11
+ export interface EmailTemplates {
12
+ 'verify-email'?: TemplateRenderer;
13
+ 'password-reset'?: TemplateRenderer;
14
+ 'welcome'?: TemplateRenderer;
15
+ [key: string]: TemplateRenderer | undefined;
16
+ }
17
+ export interface EmailConfig {
18
+ provider: 'smtp' | 'resend' | 'sendgrid';
19
+ from: string;
20
+ smtp?: {
21
+ host: string;
22
+ port: number;
23
+ secure?: boolean;
24
+ rejectUnauthorized?: boolean;
25
+ auth: {
26
+ user: string;
27
+ pass: string;
28
+ };
29
+ };
30
+ resend?: {
31
+ apiKey: string;
32
+ };
33
+ sendgrid?: {
34
+ apiKey: string;
35
+ };
36
+ /** Custom email templates. Override built-in templates or add new ones. */
37
+ templates?: EmailTemplates;
38
+ }
39
+ export interface EmailMessage {
40
+ to: string;
41
+ subject: string;
42
+ html: string;
43
+ text?: string;
44
+ }
45
+ export interface EmailProvider {
46
+ send(message: EmailMessage & {
47
+ from: string;
48
+ }): Promise<void>;
49
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
1
+ export interface LlmsPage {
2
+ path: string;
3
+ hasLoader: boolean;
4
+ hasSubscribe: boolean;
5
+ loaderData?: any;
6
+ dynamicEntries?: {
7
+ path: string;
8
+ loaderData: any;
9
+ }[];
10
+ }
11
+ export interface LlmsApiRoute {
12
+ path: string;
13
+ methods: string[];
14
+ }
15
+ export interface LlmsTxtInput {
16
+ title: string;
17
+ pages: LlmsPage[];
18
+ apiRoutes: LlmsApiRoute[];
19
+ integrations: string[];
20
+ i18n?: {
21
+ locales: string[];
22
+ defaultLocale: string;
23
+ };
24
+ db?: {
25
+ path?: string;
26
+ };
27
+ }
28
+ /**
29
+ * Generate the llms.txt content from project metadata and resolved page data.
30
+ */
31
+ export declare function generateLlmsTxt(input: LlmsTxtInput): string;
32
+ /**
33
+ * Try to resolve dynamic route entries by finding a parent/sibling index page
34
+ * whose loader returns an array, then calling the dynamic page's loader for each item.
35
+ */
36
+ export declare function resolveDynamicEntries(dynamicPage: {
37
+ path: string;
38
+ paramName: string;
39
+ }, loadModule: (filePath: string) => Promise<any>, pages: {
40
+ path: string;
41
+ filePath: string;
42
+ hasLoader: boolean;
43
+ }[]): Promise<{
44
+ path: string;
45
+ loaderData: any;
46
+ }[] | null>;
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Generate the llms.txt content from project metadata and resolved page data.
3
+ */
4
+ export function generateLlmsTxt(input) {
5
+ const lines = [];
6
+ lines.push(`# ${input.title}`);
7
+ lines.push('');
8
+ lines.push('> Built with LumenJS');
9
+ lines.push('');
10
+ // Pages section
11
+ if (input.pages.length > 0) {
12
+ lines.push('## Pages');
13
+ lines.push('');
14
+ for (const page of input.pages) {
15
+ lines.push(`### ${page.path}`);
16
+ const isDynamic = page.path.includes(':');
17
+ if (isDynamic) {
18
+ // Dynamic route — show expanded entries if available
19
+ if (page.dynamicEntries && page.dynamicEntries.length > 0) {
20
+ lines.push(`Dynamic route — ${page.dynamicEntries.length} ${page.dynamicEntries.length === 1 ? 'entry' : 'entries'}:`);
21
+ lines.push('');
22
+ for (const entry of page.dynamicEntries) {
23
+ lines.push(`#### ${entry.path}`);
24
+ if (entry.loaderData) {
25
+ lines.push(flattenData(entry.loaderData));
26
+ }
27
+ lines.push('');
28
+ }
29
+ }
30
+ else {
31
+ lines.push('- Dynamic route');
32
+ lines.push('');
33
+ }
34
+ }
35
+ else if (page.loaderData && typeof page.loaderData === 'object') {
36
+ // Static page with loader data
37
+ lines.push(flattenData(page.loaderData));
38
+ lines.push('');
39
+ }
40
+ else {
41
+ // Simple page
42
+ const features = [];
43
+ if (page.hasLoader)
44
+ features.push('with loader data');
45
+ if (page.hasSubscribe)
46
+ features.push('with live data');
47
+ lines.push(`- Server-rendered page${features.length ? ' ' + features.join(', ') : ''}`);
48
+ lines.push('');
49
+ }
50
+ }
51
+ }
52
+ // API Routes section
53
+ if (input.apiRoutes.length > 0) {
54
+ lines.push('## API Routes');
55
+ lines.push('');
56
+ for (const route of input.apiRoutes) {
57
+ for (const method of route.methods) {
58
+ lines.push(`- ${method} /api/${route.path}`);
59
+ }
60
+ }
61
+ lines.push('');
62
+ }
63
+ // Features section
64
+ const features = [];
65
+ if (input.db)
66
+ features.push('SQLite Database');
67
+ if (input.i18n) {
68
+ features.push(`Internationalization (${input.i18n.locales.join(', ')})`);
69
+ }
70
+ for (const integration of input.integrations) {
71
+ if (integration === 'tailwind')
72
+ features.push('Tailwind CSS');
73
+ else if (integration === 'nuralyui')
74
+ features.push('NuralyUI Components');
75
+ else
76
+ features.push(integration);
77
+ }
78
+ if (features.length > 0) {
79
+ lines.push('## Features');
80
+ lines.push('');
81
+ for (const feature of features) {
82
+ lines.push(`- ${feature}`);
83
+ }
84
+ lines.push('');
85
+ }
86
+ return lines.join('\n').trimEnd() + '\n';
87
+ }
88
+ /**
89
+ * Flatten a loader data object into key-value text lines.
90
+ */
91
+ function flattenData(data, prefix = '') {
92
+ if (data == null)
93
+ return '';
94
+ if (typeof data !== 'object')
95
+ return `${prefix}${String(data)}`;
96
+ // If it's an array, skip it (arrays are for lists, not flat display)
97
+ if (Array.isArray(data))
98
+ return '';
99
+ const lines = [];
100
+ for (const [key, value] of Object.entries(data)) {
101
+ if (value == null)
102
+ continue;
103
+ if (typeof value === 'object' && !Array.isArray(value)) {
104
+ lines.push(flattenData(value, `${prefix}${key}.`));
105
+ }
106
+ else if (Array.isArray(value)) {
107
+ // Skip arrays in flat display
108
+ continue;
109
+ }
110
+ else {
111
+ const strVal = String(value);
112
+ // Truncate very long values
113
+ const displayVal = strVal.length > 300 ? strVal.slice(0, 300) + '...' : strVal;
114
+ lines.push(`${prefix}${key}: ${displayVal}`);
115
+ }
116
+ }
117
+ return lines.filter(Boolean).join('\n');
118
+ }
119
+ /**
120
+ * Try to resolve dynamic route entries by finding a parent/sibling index page
121
+ * whose loader returns an array, then calling the dynamic page's loader for each item.
122
+ */
123
+ export async function resolveDynamicEntries(dynamicPage, loadModule, pages) {
124
+ // Extract the parent path from the dynamic route
125
+ // e.g., /blog/:slug -> /blog, /docs/:id -> /docs
126
+ const segments = dynamicPage.path.split('/');
127
+ segments.pop(); // remove the dynamic segment
128
+ const parentPath = segments.join('/') || '/';
129
+ // Find parent or sibling index page with a loader
130
+ const indexPage = pages.find(p => p.hasLoader && (p.path === parentPath || p.path === parentPath + '/'));
131
+ if (!indexPage)
132
+ return null;
133
+ try {
134
+ const indexMod = await loadModule(indexPage.filePath);
135
+ if (!indexMod?.loader)
136
+ return null;
137
+ const indexData = await indexMod.loader({ params: {}, query: {}, url: indexPage.path, headers: {} });
138
+ if (!indexData || typeof indexData !== 'object')
139
+ return null;
140
+ // Find the array in the loader data (look for the first array value)
141
+ let items = null;
142
+ for (const value of Object.values(indexData)) {
143
+ if (Array.isArray(value)) {
144
+ items = value;
145
+ break;
146
+ }
147
+ }
148
+ // If the returned data is itself an array
149
+ if (!items && Array.isArray(indexData)) {
150
+ items = indexData;
151
+ }
152
+ if (!items || items.length === 0)
153
+ return null;
154
+ // For each item, try to call the dynamic page's loader
155
+ const dynamicMod = await loadModule(pages.find(p => p.path === dynamicPage.path)?.filePath || '');
156
+ if (!dynamicMod?.loader)
157
+ return null;
158
+ const entries = [];
159
+ for (const item of items) {
160
+ // Try to extract the param value from the item
161
+ const paramValue = item[dynamicPage.paramName] || item.slug || item.id || item.name;
162
+ if (!paramValue)
163
+ continue;
164
+ try {
165
+ const loaderData = await dynamicMod.loader({
166
+ params: { [dynamicPage.paramName]: String(paramValue) },
167
+ query: {},
168
+ url: dynamicPage.path.replace(`:${dynamicPage.paramName}`, String(paramValue)),
169
+ headers: {},
170
+ });
171
+ if (loaderData) {
172
+ const resolvedPath = dynamicPage.path.replace(`:${dynamicPage.paramName}`, String(paramValue));
173
+ entries.push({ path: resolvedPath, loaderData });
174
+ }
175
+ }
176
+ catch {
177
+ // Skip items whose loader fails
178
+ }
179
+ }
180
+ return entries.length > 0 ? entries : null;
181
+ }
182
+ catch {
183
+ return null;
184
+ }
185
+ }
@@ -0,0 +1,28 @@
1
+ import type { AuthUser } from '../auth/types.js';
2
+ import type { PermissionService } from './service.js';
3
+ export type GuardResult = {
4
+ allowed: true;
5
+ } | {
6
+ redirect: string;
7
+ } | {
8
+ forbidden: true;
9
+ };
10
+ /**
11
+ * Enforce a permission guard on a page.
12
+ *
13
+ * Called when a page exports:
14
+ * export const auth = { permission: 'workflow:read', resourceParam: 'workflowId' };
15
+ *
16
+ * Checks authentication first, then resource-level permission.
17
+ */
18
+ export declare function enforcePermissionGuard(authExport: {
19
+ permission: string;
20
+ resourceParam: string;
21
+ }, user: AuthUser | null | undefined, loginUrl: string, pathname: string, urlParams: Record<string, string>, permissionService: PermissionService): Promise<GuardResult>;
22
+ /**
23
+ * Check if an auth export contains a permission guard (has 'permission' field).
24
+ */
25
+ export declare function isPermissionGuard(authExport: any): authExport is {
26
+ permission: string;
27
+ resourceParam: string;
28
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Enforce a permission guard on a page.
3
+ *
4
+ * Called when a page exports:
5
+ * export const auth = { permission: 'workflow:read', resourceParam: 'workflowId' };
6
+ *
7
+ * Checks authentication first, then resource-level permission.
8
+ */
9
+ export async function enforcePermissionGuard(authExport, user, loginUrl, pathname, urlParams, permissionService) {
10
+ // Must be authenticated
11
+ if (!user) {
12
+ return { redirect: `${loginUrl}?returnTo=${encodeURIComponent(pathname)}` };
13
+ }
14
+ const resourceId = urlParams[authExport.resourceParam];
15
+ if (!resourceId) {
16
+ return { allowed: true };
17
+ }
18
+ const [resourceType] = authExport.permission.split(':');
19
+ const hasPermission = await permissionService.canAccess(user.sub, authExport.permission, resourceType, resourceId);
20
+ if (!hasPermission) {
21
+ return { forbidden: true };
22
+ }
23
+ return { allowed: true };
24
+ }
25
+ /**
26
+ * Check if an auth export contains a permission guard (has 'permission' field).
27
+ */
28
+ export function isPermissionGuard(authExport) {
29
+ return authExport && typeof authExport === 'object' && typeof authExport.permission === 'string';
30
+ }
@@ -0,0 +1,6 @@
1
+ export { PermissionService, usePermissions } from './service.js';
2
+ export type { AuditLogEntry } from './service.js';
3
+ export { ensurePermissionTables } from './tables.js';
4
+ export { enforcePermissionGuard, isPermissionGuard } from './guard.js';
5
+ export type { GuardResult } from './guard.js';
6
+ export type { ResourcePermission, Role, UserRole, PermissionsConfig, ResolvedPermissionsConfig, } from './types.js';
@@ -0,0 +1,3 @@
1
+ export { PermissionService, usePermissions } from './service.js';
2
+ export { ensurePermissionTables } from './tables.js';
3
+ export { enforcePermissionGuard, isPermissionGuard } from './guard.js';
@@ -0,0 +1,80 @@
1
+ import type { ResourcePermission, ResolvedPermissionsConfig } from './types.js';
2
+ export interface AuditLogEntry {
3
+ id: number;
4
+ action: string;
5
+ resource_type: string | null;
6
+ resource_id: string | null;
7
+ grantee_type: string | null;
8
+ grantee_id: string | null;
9
+ permission: string | null;
10
+ role_id: string | null;
11
+ actor_id: string;
12
+ details: string | null;
13
+ created_at: string;
14
+ }
15
+ export declare class PermissionService {
16
+ private db;
17
+ private config;
18
+ constructor(db: any, config?: Partial<ResolvedPermissionsConfig>);
19
+ private audit;
20
+ /**
21
+ * Query audit log entries.
22
+ */
23
+ getAuditLog(opts?: {
24
+ resourceType?: string;
25
+ resourceId?: string;
26
+ actorId?: string;
27
+ limit?: number;
28
+ }): Promise<AuditLogEntry[]>;
29
+ /**
30
+ * Check if a user can perform an action on a resource.
31
+ *
32
+ * Check order:
33
+ * 1. Direct user grant
34
+ * 2. Public grant (anyone with the link)
35
+ * 3. Anonymous grant (only if anonymous flag set)
36
+ * 4. Role-based permissions (global roles, then resource-scoped roles)
37
+ * 5. Wildcard '*' permission matches any action
38
+ */
39
+ canAccess(userId: string | null, permissionType: string, resourceType: string, resourceId: string, anonymous?: boolean): Promise<boolean>;
40
+ /**
41
+ * Get all resource IDs of a given type that a user can access.
42
+ */
43
+ getAccessibleResourceIds(userId: string, resourceType: string, permission: string): Promise<string[]>;
44
+ /**
45
+ * Get all permissions for a resource.
46
+ */
47
+ getPermissionsForResource(resourceType: string, resourceId: string): Promise<ResourcePermission[]>;
48
+ /**
49
+ * Initialize owner permissions when a resource is created.
50
+ */
51
+ initOwnerPermissions(resourceType: string, resourceId: string, userId: string): Promise<void>;
52
+ /**
53
+ * Grant a permission on a resource.
54
+ */
55
+ grant(resourceType: string, resourceId: string, granteeType: string, granteeId: string | null, permission: string, grantedBy: string): Promise<void>;
56
+ /**
57
+ * Revoke a permission on a resource.
58
+ */
59
+ revoke(resourceType: string, resourceId: string, granteeType: string, granteeId: string | null, permission: string, revokedBy: string): Promise<void>;
60
+ /**
61
+ * Make a resource publicly accessible (read).
62
+ */
63
+ makePublic(resourceType: string, resourceId: string, grantedBy: string): Promise<void>;
64
+ /**
65
+ * Remove public access from a resource.
66
+ */
67
+ removePublic(resourceType: string, resourceId: string, removedBy: string): Promise<void>;
68
+ /**
69
+ * Assign a role to a user (globally or scoped to a resource).
70
+ */
71
+ assignRole(userId: string, roleId: string, assignedBy: string, resourceType?: string, resourceId?: string): Promise<void>;
72
+ /**
73
+ * Remove a role from a user.
74
+ */
75
+ removeRole(userId: string, roleId: string, removedBy: string, resourceType?: string, resourceId?: string): Promise<void>;
76
+ }
77
+ /**
78
+ * Create a PermissionService instance from the LumenJS database.
79
+ */
80
+ export declare function usePermissions(db: any, config?: Partial<ResolvedPermissionsConfig>): PermissionService;
@@ -0,0 +1,210 @@
1
+ const DEFAULT_OWNER_GRANTS = ['read', 'write', 'delete', 'share'];
2
+ export class PermissionService {
3
+ constructor(db, config) {
4
+ this.db = db;
5
+ this.config = {
6
+ enabled: config?.enabled ?? true,
7
+ defaultOwnerGrants: config?.defaultOwnerGrants ?? DEFAULT_OWNER_GRANTS,
8
+ };
9
+ }
10
+ // ── Audit logging ──────────────────────────────────────────────
11
+ async audit(action, actorId, opts = {}) {
12
+ await this.db.run(`INSERT INTO _nk_permission_audit_log
13
+ (action, resource_type, resource_id, grantee_type, grantee_id, permission, role_id, actor_id, details)
14
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, action, opts.resourceType ?? null, opts.resourceId ?? null, opts.granteeType ?? null, opts.granteeId ?? null, opts.permission ?? null, opts.roleId ?? null, actorId, opts.details ?? null);
15
+ }
16
+ /**
17
+ * Query audit log entries.
18
+ */
19
+ async getAuditLog(opts) {
20
+ let sql = 'SELECT * FROM _nk_permission_audit_log WHERE 1=1';
21
+ const params = [];
22
+ if (opts?.resourceType) {
23
+ sql += ' AND resource_type = ?';
24
+ params.push(opts.resourceType);
25
+ }
26
+ if (opts?.resourceId) {
27
+ sql += ' AND resource_id = ?';
28
+ params.push(opts.resourceId);
29
+ }
30
+ if (opts?.actorId) {
31
+ sql += ' AND actor_id = ?';
32
+ params.push(opts.actorId);
33
+ }
34
+ sql += ' ORDER BY created_at DESC LIMIT ?';
35
+ params.push(opts?.limit ?? 100);
36
+ return this.db.all(sql, ...params);
37
+ }
38
+ // ── Permission checks ─────────────────────────────────────────
39
+ /**
40
+ * Check if a user can perform an action on a resource.
41
+ *
42
+ * Check order:
43
+ * 1. Direct user grant
44
+ * 2. Public grant (anyone with the link)
45
+ * 3. Anonymous grant (only if anonymous flag set)
46
+ * 4. Role-based permissions (global roles, then resource-scoped roles)
47
+ * 5. Wildcard '*' permission matches any action
48
+ */
49
+ async canAccess(userId, permissionType, resourceType, resourceId, anonymous) {
50
+ if (!this.config.enabled)
51
+ return true;
52
+ const [, action] = permissionType.includes(':')
53
+ ? permissionType.split(':')
54
+ : [resourceType, permissionType];
55
+ // 1. Direct user grant
56
+ if (userId) {
57
+ const userGrant = await this.db.get(`SELECT 1 FROM _nk_resource_permissions
58
+ WHERE resource_type = ? AND resource_id = ?
59
+ AND grantee_type = 'user' AND grantee_id = ?
60
+ AND (permission = ? OR permission = '*')
61
+ AND (expires_at IS NULL OR expires_at > datetime('now'))`, resourceType, resourceId, userId, action);
62
+ if (userGrant)
63
+ return true;
64
+ }
65
+ // 2. Public grant
66
+ const publicGrant = await this.db.get(`SELECT 1 FROM _nk_resource_permissions
67
+ WHERE resource_type = ? AND resource_id = ?
68
+ AND grantee_type = 'public'
69
+ AND (permission = ? OR permission = '*')
70
+ AND (expires_at IS NULL OR expires_at > datetime('now'))`, resourceType, resourceId, action);
71
+ if (publicGrant)
72
+ return true;
73
+ // 3. Anonymous grant
74
+ if (anonymous) {
75
+ const anonGrant = await this.db.get(`SELECT 1 FROM _nk_resource_permissions
76
+ WHERE resource_type = ? AND resource_id = ?
77
+ AND grantee_type = 'anonymous'
78
+ AND (permission = ? OR permission = '*')
79
+ AND (expires_at IS NULL OR expires_at > datetime('now'))`, resourceType, resourceId, action);
80
+ if (anonGrant)
81
+ return true;
82
+ }
83
+ // 4. Role-based: check user's roles → role permissions
84
+ if (userId) {
85
+ const roleMatch = await this.db.get(`SELECT 1 FROM _nk_user_roles ur
86
+ JOIN _nk_roles r ON r.id = ur.role_id
87
+ WHERE ur.user_id = ?
88
+ AND (
89
+ (ur.resource_type IS NULL OR ur.resource_type = '')
90
+ OR (ur.resource_type = ? AND ur.resource_id = ?)
91
+ )
92
+ AND EXISTS (
93
+ SELECT 1 FROM json_each(r.permissions) je
94
+ WHERE je.value = ? OR je.value = ? || ':*' OR je.value = '*'
95
+ )`, userId, resourceType, resourceId, permissionType, resourceType);
96
+ if (roleMatch)
97
+ return true;
98
+ }
99
+ return false;
100
+ }
101
+ // ── Resource queries ──────────────────────────────────────────
102
+ /**
103
+ * Get all resource IDs of a given type that a user can access.
104
+ */
105
+ async getAccessibleResourceIds(userId, resourceType, permission) {
106
+ const [, action] = permission.includes(':')
107
+ ? permission.split(':')
108
+ : [resourceType, permission];
109
+ const rows = await this.db.all(`SELECT DISTINCT resource_id FROM _nk_resource_permissions
110
+ WHERE resource_type = ?
111
+ AND (
112
+ (grantee_type = 'user' AND grantee_id = ?)
113
+ OR grantee_type = 'public'
114
+ )
115
+ AND (permission = ? OR permission = '*')
116
+ AND (expires_at IS NULL OR expires_at > datetime('now'))`, resourceType, userId, action);
117
+ return rows.map((r) => r.resource_id);
118
+ }
119
+ /**
120
+ * Get all permissions for a resource.
121
+ */
122
+ async getPermissionsForResource(resourceType, resourceId) {
123
+ return this.db.all(`SELECT * FROM _nk_resource_permissions
124
+ WHERE resource_type = ? AND resource_id = ?
125
+ AND (expires_at IS NULL OR expires_at > datetime('now'))
126
+ ORDER BY created_at`, resourceType, resourceId);
127
+ }
128
+ // ── Mutations (all audited) ───────────────────────────────────
129
+ /**
130
+ * Initialize owner permissions when a resource is created.
131
+ */
132
+ async initOwnerPermissions(resourceType, resourceId, userId) {
133
+ for (const perm of this.config.defaultOwnerGrants) {
134
+ await this.db.run(`INSERT OR IGNORE INTO _nk_resource_permissions
135
+ (resource_type, resource_id, grantee_type, grantee_id, permission, granted_by)
136
+ VALUES (?, ?, 'user', ?, ?, ?)`, resourceType, resourceId, userId, perm, userId);
137
+ }
138
+ await this.audit('init_owner', userId, {
139
+ resourceType, resourceId,
140
+ details: `Granted: ${this.config.defaultOwnerGrants.join(', ')}`,
141
+ });
142
+ }
143
+ /**
144
+ * Grant a permission on a resource.
145
+ */
146
+ async grant(resourceType, resourceId, granteeType, granteeId, permission, grantedBy) {
147
+ await this.db.run(`INSERT OR IGNORE INTO _nk_resource_permissions
148
+ (resource_type, resource_id, grantee_type, grantee_id, permission, granted_by)
149
+ VALUES (?, ?, ?, ?, ?, ?)`, resourceType, resourceId, granteeType, granteeId, permission, grantedBy);
150
+ await this.audit('grant', grantedBy, {
151
+ resourceType, resourceId, granteeType, granteeId, permission,
152
+ });
153
+ }
154
+ /**
155
+ * Revoke a permission on a resource.
156
+ */
157
+ async revoke(resourceType, resourceId, granteeType, granteeId, permission, revokedBy) {
158
+ await this.db.run(`DELETE FROM _nk_resource_permissions
159
+ WHERE resource_type = ? AND resource_id = ?
160
+ AND grantee_type = ? AND COALESCE(grantee_id, '') = COALESCE(?, '')
161
+ AND permission = ?`, resourceType, resourceId, granteeType, granteeId, permission);
162
+ await this.audit('revoke', revokedBy, {
163
+ resourceType, resourceId, granteeType, granteeId, permission,
164
+ });
165
+ }
166
+ /**
167
+ * Make a resource publicly accessible (read).
168
+ */
169
+ async makePublic(resourceType, resourceId, grantedBy) {
170
+ await this.grant(resourceType, resourceId, 'public', null, 'read', grantedBy);
171
+ }
172
+ /**
173
+ * Remove public access from a resource.
174
+ */
175
+ async removePublic(resourceType, resourceId, removedBy) {
176
+ await this.db.run(`DELETE FROM _nk_resource_permissions
177
+ WHERE resource_type = ? AND resource_id = ? AND grantee_type = 'public'`, resourceType, resourceId);
178
+ await this.audit('remove_public', removedBy, { resourceType, resourceId });
179
+ }
180
+ /**
181
+ * Assign a role to a user (globally or scoped to a resource).
182
+ */
183
+ async assignRole(userId, roleId, assignedBy, resourceType, resourceId) {
184
+ await this.db.run(`INSERT OR IGNORE INTO _nk_user_roles (user_id, role_id, resource_type, resource_id)
185
+ VALUES (?, ?, ?, ?)`, userId, roleId, resourceType ?? null, resourceId ?? null);
186
+ await this.audit('assign_role', assignedBy, {
187
+ granteeType: 'user', granteeId: userId, roleId,
188
+ resourceType, resourceId,
189
+ });
190
+ }
191
+ /**
192
+ * Remove a role from a user.
193
+ */
194
+ async removeRole(userId, roleId, removedBy, resourceType, resourceId) {
195
+ await this.db.run(`DELETE FROM _nk_user_roles
196
+ WHERE user_id = ? AND role_id = ?
197
+ AND COALESCE(resource_type, '') = COALESCE(?, '')
198
+ AND COALESCE(resource_id, '') = COALESCE(?, '')`, userId, roleId, resourceType ?? null, resourceId ?? null);
199
+ await this.audit('remove_role', removedBy, {
200
+ granteeType: 'user', granteeId: userId, roleId,
201
+ resourceType, resourceId,
202
+ });
203
+ }
204
+ }
205
+ /**
206
+ * Create a PermissionService instance from the LumenJS database.
207
+ */
208
+ export function usePermissions(db, config) {
209
+ return new PermissionService(db, config);
210
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Auto-create permission tables in the LumenJS SQLite database.
3
+ * Follows the same pattern as ensureUsersTable() in auth/native-auth.ts.
4
+ */
5
+ export declare function ensurePermissionTables(db: any): Promise<void>;