@qwickapps/server 1.2.0 → 1.3.1

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 (299) hide show
  1. package/README.md +392 -0
  2. package/dist/core/control-panel.d.ts +7 -2
  3. package/dist/core/control-panel.d.ts.map +1 -1
  4. package/dist/core/control-panel.js +120 -54
  5. package/dist/core/control-panel.js.map +1 -1
  6. package/dist/core/gateway.d.ts +159 -79
  7. package/dist/core/gateway.d.ts.map +1 -1
  8. package/dist/core/gateway.js +679 -319
  9. package/dist/core/gateway.js.map +1 -1
  10. package/dist/core/index.d.ts +3 -1
  11. package/dist/core/index.d.ts.map +1 -1
  12. package/dist/core/index.js +2 -0
  13. package/dist/core/index.js.map +1 -1
  14. package/dist/core/plugin-registry.d.ts +307 -0
  15. package/dist/core/plugin-registry.d.ts.map +1 -0
  16. package/dist/core/plugin-registry.js +352 -0
  17. package/dist/core/plugin-registry.js.map +1 -0
  18. package/dist/core/types.d.ts +16 -33
  19. package/dist/core/types.d.ts.map +1 -1
  20. package/dist/index.d.ts +8 -5
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +15 -7
  23. package/dist/index.js.map +1 -1
  24. package/dist/plugins/auth/adapters/auth0-adapter.d.ts +14 -0
  25. package/dist/plugins/auth/adapters/auth0-adapter.d.ts.map +1 -0
  26. package/dist/plugins/auth/adapters/auth0-adapter.js +179 -0
  27. package/dist/plugins/auth/adapters/auth0-adapter.js.map +1 -0
  28. package/dist/plugins/auth/adapters/basic-adapter.d.ts +13 -0
  29. package/dist/plugins/auth/adapters/basic-adapter.d.ts.map +1 -0
  30. package/dist/plugins/auth/adapters/basic-adapter.js +51 -0
  31. package/dist/plugins/auth/adapters/basic-adapter.js.map +1 -0
  32. package/dist/plugins/auth/adapters/index.d.ts +10 -0
  33. package/dist/plugins/auth/adapters/index.d.ts.map +1 -0
  34. package/dist/plugins/auth/adapters/index.js +10 -0
  35. package/dist/plugins/auth/adapters/index.js.map +1 -0
  36. package/dist/plugins/auth/adapters/supabase-adapter.d.ts +13 -0
  37. package/dist/plugins/auth/adapters/supabase-adapter.d.ts.map +1 -0
  38. package/dist/plugins/auth/adapters/supabase-adapter.js +109 -0
  39. package/dist/plugins/auth/adapters/supabase-adapter.js.map +1 -0
  40. package/dist/plugins/auth/adapters/supertokens-adapter.d.ts +18 -0
  41. package/dist/plugins/auth/adapters/supertokens-adapter.d.ts.map +1 -0
  42. package/dist/plugins/auth/adapters/supertokens-adapter.js +267 -0
  43. package/dist/plugins/auth/adapters/supertokens-adapter.js.map +1 -0
  44. package/dist/plugins/auth/auth-plugin.d.ts +40 -0
  45. package/dist/plugins/auth/auth-plugin.d.ts.map +1 -0
  46. package/dist/plugins/auth/auth-plugin.js +255 -0
  47. package/dist/plugins/auth/auth-plugin.js.map +1 -0
  48. package/dist/plugins/auth/auth-plugin.test.d.ts +9 -0
  49. package/dist/plugins/auth/auth-plugin.test.d.ts.map +1 -0
  50. package/dist/plugins/auth/auth-plugin.test.js +147 -0
  51. package/dist/plugins/auth/auth-plugin.test.js.map +1 -0
  52. package/dist/plugins/auth/env-config.d.ts +88 -0
  53. package/dist/plugins/auth/env-config.d.ts.map +1 -0
  54. package/dist/plugins/auth/env-config.js +489 -0
  55. package/dist/plugins/auth/env-config.js.map +1 -0
  56. package/dist/plugins/auth/index.d.ts +14 -0
  57. package/dist/plugins/auth/index.d.ts.map +1 -0
  58. package/dist/plugins/auth/index.js +16 -0
  59. package/dist/plugins/auth/index.js.map +1 -0
  60. package/dist/plugins/auth/supertokens-adapter.test.d.ts +10 -0
  61. package/dist/plugins/auth/supertokens-adapter.test.d.ts.map +1 -0
  62. package/dist/plugins/auth/supertokens-adapter.test.js +486 -0
  63. package/dist/plugins/auth/supertokens-adapter.test.js.map +1 -0
  64. package/dist/plugins/auth/types.d.ts +218 -0
  65. package/dist/plugins/auth/types.d.ts.map +1 -0
  66. package/dist/plugins/auth/types.js +14 -0
  67. package/dist/plugins/auth/types.js.map +1 -0
  68. package/dist/plugins/bans/bans-plugin.d.ts +59 -0
  69. package/dist/plugins/bans/bans-plugin.d.ts.map +1 -0
  70. package/dist/plugins/bans/bans-plugin.js +428 -0
  71. package/dist/plugins/bans/bans-plugin.js.map +1 -0
  72. package/dist/plugins/bans/index.d.ts +9 -0
  73. package/dist/plugins/bans/index.d.ts.map +1 -0
  74. package/dist/plugins/bans/index.js +10 -0
  75. package/dist/plugins/bans/index.js.map +1 -0
  76. package/dist/plugins/bans/stores/index.d.ts +7 -0
  77. package/dist/plugins/bans/stores/index.d.ts.map +1 -0
  78. package/dist/plugins/bans/stores/index.js +7 -0
  79. package/dist/plugins/bans/stores/index.js.map +1 -0
  80. package/dist/plugins/bans/stores/postgres-store.d.ts +29 -0
  81. package/dist/plugins/bans/stores/postgres-store.d.ts.map +1 -0
  82. package/dist/plugins/bans/stores/postgres-store.js +132 -0
  83. package/dist/plugins/bans/stores/postgres-store.js.map +1 -0
  84. package/dist/plugins/bans/types.d.ts +128 -0
  85. package/dist/plugins/bans/types.d.ts.map +1 -0
  86. package/dist/plugins/bans/types.js +11 -0
  87. package/dist/plugins/bans/types.js.map +1 -0
  88. package/dist/plugins/cache-plugin.d.ts +14 -3
  89. package/dist/plugins/cache-plugin.d.ts.map +1 -1
  90. package/dist/plugins/cache-plugin.js +27 -7
  91. package/dist/plugins/cache-plugin.js.map +1 -1
  92. package/dist/plugins/cache-plugin.test.js +99 -32
  93. package/dist/plugins/cache-plugin.test.js.map +1 -1
  94. package/dist/plugins/config-plugin.d.ts +3 -2
  95. package/dist/plugins/config-plugin.d.ts.map +1 -1
  96. package/dist/plugins/config-plugin.js +17 -10
  97. package/dist/plugins/config-plugin.js.map +1 -1
  98. package/dist/plugins/diagnostics-plugin.d.ts +2 -2
  99. package/dist/plugins/diagnostics-plugin.d.ts.map +1 -1
  100. package/dist/plugins/diagnostics-plugin.js +17 -10
  101. package/dist/plugins/diagnostics-plugin.js.map +1 -1
  102. package/dist/plugins/entitlements/entitlements-plugin.d.ts +95 -0
  103. package/dist/plugins/entitlements/entitlements-plugin.d.ts.map +1 -0
  104. package/dist/plugins/entitlements/entitlements-plugin.js +707 -0
  105. package/dist/plugins/entitlements/entitlements-plugin.js.map +1 -0
  106. package/dist/plugins/entitlements/index.d.ts +12 -0
  107. package/dist/plugins/entitlements/index.d.ts.map +1 -0
  108. package/dist/plugins/entitlements/index.js +16 -0
  109. package/dist/plugins/entitlements/index.js.map +1 -0
  110. package/dist/plugins/entitlements/sources/index.d.ts +9 -0
  111. package/dist/plugins/entitlements/sources/index.d.ts.map +1 -0
  112. package/dist/plugins/entitlements/sources/index.js +9 -0
  113. package/dist/plugins/entitlements/sources/index.js.map +1 -0
  114. package/dist/plugins/entitlements/sources/postgres-source.d.ts +29 -0
  115. package/dist/plugins/entitlements/sources/postgres-source.d.ts.map +1 -0
  116. package/dist/plugins/entitlements/sources/postgres-source.js +169 -0
  117. package/dist/plugins/entitlements/sources/postgres-source.js.map +1 -0
  118. package/dist/plugins/entitlements/types.d.ts +232 -0
  119. package/dist/plugins/entitlements/types.d.ts.map +1 -0
  120. package/dist/plugins/entitlements/types.js +11 -0
  121. package/dist/plugins/entitlements/types.js.map +1 -0
  122. package/dist/plugins/frontend-app-plugin.d.ts +9 -3
  123. package/dist/plugins/frontend-app-plugin.d.ts.map +1 -1
  124. package/dist/plugins/frontend-app-plugin.js +14 -9
  125. package/dist/plugins/frontend-app-plugin.js.map +1 -1
  126. package/dist/plugins/health-plugin.d.ts +5 -2
  127. package/dist/plugins/health-plugin.d.ts.map +1 -1
  128. package/dist/plugins/health-plugin.js +20 -5
  129. package/dist/plugins/health-plugin.js.map +1 -1
  130. package/dist/plugins/index.d.ts +10 -2
  131. package/dist/plugins/index.d.ts.map +1 -1
  132. package/dist/plugins/index.js +10 -2
  133. package/dist/plugins/index.js.map +1 -1
  134. package/dist/plugins/logs-plugin.d.ts +3 -2
  135. package/dist/plugins/logs-plugin.d.ts.map +1 -1
  136. package/dist/plugins/logs-plugin.js +21 -12
  137. package/dist/plugins/logs-plugin.js.map +1 -1
  138. package/dist/plugins/postgres-plugin.d.ts +3 -3
  139. package/dist/plugins/postgres-plugin.d.ts.map +1 -1
  140. package/dist/plugins/postgres-plugin.js +9 -7
  141. package/dist/plugins/postgres-plugin.js.map +1 -1
  142. package/dist/plugins/postgres-plugin.test.js +50 -29
  143. package/dist/plugins/postgres-plugin.test.js.map +1 -1
  144. package/dist/plugins/preferences/__tests__/deep-merge.test.d.ts +7 -0
  145. package/dist/plugins/preferences/__tests__/deep-merge.test.d.ts.map +1 -0
  146. package/dist/plugins/preferences/__tests__/deep-merge.test.js +215 -0
  147. package/dist/plugins/preferences/__tests__/deep-merge.test.js.map +1 -0
  148. package/dist/plugins/preferences/__tests__/preferences-plugin.test.d.ts +7 -0
  149. package/dist/plugins/preferences/__tests__/preferences-plugin.test.d.ts.map +1 -0
  150. package/dist/plugins/preferences/__tests__/preferences-plugin.test.js +265 -0
  151. package/dist/plugins/preferences/__tests__/preferences-plugin.test.js.map +1 -0
  152. package/dist/plugins/preferences/index.d.ts +12 -0
  153. package/dist/plugins/preferences/index.d.ts.map +1 -0
  154. package/dist/plugins/preferences/index.js +13 -0
  155. package/dist/plugins/preferences/index.js.map +1 -0
  156. package/dist/plugins/preferences/preferences-plugin.d.ts +39 -0
  157. package/dist/plugins/preferences/preferences-plugin.d.ts.map +1 -0
  158. package/dist/plugins/preferences/preferences-plugin.js +226 -0
  159. package/dist/plugins/preferences/preferences-plugin.js.map +1 -0
  160. package/dist/plugins/preferences/stores/index.d.ts +9 -0
  161. package/dist/plugins/preferences/stores/index.d.ts.map +1 -0
  162. package/dist/plugins/preferences/stores/index.js +9 -0
  163. package/dist/plugins/preferences/stores/index.js.map +1 -0
  164. package/dist/plugins/preferences/stores/postgres-store.d.ts +41 -0
  165. package/dist/plugins/preferences/stores/postgres-store.d.ts.map +1 -0
  166. package/dist/plugins/preferences/stores/postgres-store.js +181 -0
  167. package/dist/plugins/preferences/stores/postgres-store.js.map +1 -0
  168. package/dist/plugins/preferences/types.d.ts +91 -0
  169. package/dist/plugins/preferences/types.d.ts.map +1 -0
  170. package/dist/plugins/preferences/types.js +10 -0
  171. package/dist/plugins/preferences/types.js.map +1 -0
  172. package/dist/plugins/users/__tests__/users-plugin.test.d.ts +9 -0
  173. package/dist/plugins/users/__tests__/users-plugin.test.d.ts.map +1 -0
  174. package/dist/plugins/users/__tests__/users-plugin.test.js +546 -0
  175. package/dist/plugins/users/__tests__/users-plugin.test.js.map +1 -0
  176. package/dist/plugins/users/index.d.ts +12 -0
  177. package/dist/plugins/users/index.d.ts.map +1 -0
  178. package/dist/plugins/users/index.js +13 -0
  179. package/dist/plugins/users/index.js.map +1 -0
  180. package/dist/plugins/users/stores/index.d.ts +7 -0
  181. package/dist/plugins/users/stores/index.d.ts.map +1 -0
  182. package/dist/plugins/users/stores/index.js +7 -0
  183. package/dist/plugins/users/stores/index.js.map +1 -0
  184. package/dist/plugins/users/stores/postgres-store.d.ts +28 -0
  185. package/dist/plugins/users/stores/postgres-store.d.ts.map +1 -0
  186. package/dist/plugins/users/stores/postgres-store.js +157 -0
  187. package/dist/plugins/users/stores/postgres-store.js.map +1 -0
  188. package/dist/plugins/users/types.d.ts +225 -0
  189. package/dist/plugins/users/types.d.ts.map +1 -0
  190. package/dist/plugins/users/types.js +12 -0
  191. package/dist/plugins/users/types.js.map +1 -0
  192. package/dist/plugins/users/users-plugin.d.ts +45 -0
  193. package/dist/plugins/users/users-plugin.d.ts.map +1 -0
  194. package/dist/plugins/users/users-plugin.js +359 -0
  195. package/dist/plugins/users/users-plugin.js.map +1 -0
  196. package/dist-ui/assets/index-BY8OxNgO.js +465 -0
  197. package/dist-ui/assets/index-BY8OxNgO.js.map +1 -0
  198. package/dist-ui/index.html +1 -1
  199. package/dist-ui-lib/api/controlPanelApi.d.ts +278 -0
  200. package/dist-ui-lib/components/ControlPanelApp.d.ts +61 -0
  201. package/dist-ui-lib/components/index.d.ts +18 -0
  202. package/dist-ui-lib/config/AppConfig.d.ts +7 -0
  203. package/dist-ui-lib/dashboard/DashboardWidgetRegistry.d.ts +62 -0
  204. package/dist-ui-lib/dashboard/DashboardWidgetRenderer.d.ts +8 -0
  205. package/dist-ui-lib/dashboard/PluginWidgetRenderer.d.ts +19 -0
  206. package/dist-ui-lib/dashboard/WidgetComponentRegistry.d.ts +48 -0
  207. package/dist-ui-lib/dashboard/builtInWidgets.d.ts +25 -0
  208. package/dist-ui-lib/dashboard/index.d.ts +13 -0
  209. package/dist-ui-lib/dashboard/widgets/ServiceHealthWidget.d.ts +12 -0
  210. package/dist-ui-lib/dashboard/widgets/index.d.ts +6 -0
  211. package/dist-ui-lib/index.js +5172 -0
  212. package/dist-ui-lib/index.js.map +1 -0
  213. package/dist-ui-lib/pages/AuthPage.d.ts +1 -0
  214. package/dist-ui-lib/pages/ConfigPage.d.ts +1 -0
  215. package/dist-ui-lib/pages/DashboardPage.d.ts +1 -0
  216. package/dist-ui-lib/pages/DiagnosticsPage.d.ts +1 -0
  217. package/dist-ui-lib/pages/EntitlementsPage.d.ts +17 -0
  218. package/dist-ui-lib/pages/LogsPage.d.ts +1 -0
  219. package/dist-ui-lib/pages/NotFoundPage.d.ts +1 -0
  220. package/dist-ui-lib/pages/PluginPage.d.ts +15 -0
  221. package/dist-ui-lib/pages/PluginsPage.d.ts +1 -0
  222. package/dist-ui-lib/pages/SystemPage.d.ts +1 -0
  223. package/dist-ui-lib/pages/UsersPage.d.ts +22 -0
  224. package/package.json +24 -7
  225. package/src/core/control-panel.ts +145 -61
  226. package/src/core/gateway.ts +863 -403
  227. package/src/core/index.ts +21 -2
  228. package/src/core/plugin-registry.ts +716 -0
  229. package/src/core/types.ts +31 -37
  230. package/src/index.ts +125 -19
  231. package/src/plugins/auth/adapters/auth0-adapter.ts +214 -0
  232. package/src/plugins/auth/adapters/basic-adapter.ts +61 -0
  233. package/src/plugins/auth/adapters/index.ts +10 -0
  234. package/src/plugins/auth/adapters/supabase-adapter.ts +149 -0
  235. package/src/plugins/auth/adapters/supertokens-adapter.ts +326 -0
  236. package/src/plugins/auth/auth-plugin.test.ts +176 -0
  237. package/src/plugins/auth/auth-plugin.ts +303 -0
  238. package/src/plugins/auth/env-config.ts +572 -0
  239. package/src/plugins/auth/index.ts +42 -0
  240. package/src/plugins/auth/supertokens-adapter.test.ts +621 -0
  241. package/src/plugins/auth/types.ts +245 -0
  242. package/src/plugins/bans/bans-plugin.ts +485 -0
  243. package/src/plugins/bans/index.ts +31 -0
  244. package/src/plugins/bans/stores/index.ts +7 -0
  245. package/src/plugins/bans/stores/postgres-store.ts +195 -0
  246. package/src/plugins/bans/types.ts +141 -0
  247. package/src/plugins/cache-plugin.test.ts +108 -32
  248. package/src/plugins/cache-plugin.ts +40 -9
  249. package/src/plugins/config-plugin.ts +23 -12
  250. package/src/plugins/diagnostics-plugin.ts +22 -12
  251. package/src/plugins/entitlements/entitlements-plugin.ts +820 -0
  252. package/src/plugins/entitlements/index.ts +51 -0
  253. package/src/plugins/entitlements/sources/index.ts +9 -0
  254. package/src/plugins/entitlements/sources/postgres-source.ts +253 -0
  255. package/src/plugins/entitlements/types.ts +256 -0
  256. package/src/plugins/frontend-app-plugin.ts +24 -12
  257. package/src/plugins/health-plugin.ts +27 -7
  258. package/src/plugins/index.ts +132 -4
  259. package/src/plugins/logs-plugin.ts +28 -14
  260. package/src/plugins/postgres-plugin.test.ts +52 -29
  261. package/src/plugins/postgres-plugin.ts +11 -9
  262. package/src/plugins/preferences/__tests__/deep-merge.test.ts +242 -0
  263. package/src/plugins/preferences/__tests__/preferences-plugin.test.ts +350 -0
  264. package/src/plugins/preferences/index.ts +30 -0
  265. package/src/plugins/preferences/preferences-plugin.ts +270 -0
  266. package/src/plugins/preferences/stores/index.ts +9 -0
  267. package/src/plugins/preferences/stores/postgres-store.ts +252 -0
  268. package/src/plugins/preferences/types.ts +100 -0
  269. package/src/plugins/users/__tests__/users-plugin.test.ts +690 -0
  270. package/src/plugins/users/index.ts +38 -0
  271. package/src/plugins/users/stores/index.ts +7 -0
  272. package/src/plugins/users/stores/postgres-store.ts +225 -0
  273. package/src/plugins/users/types.ts +247 -0
  274. package/src/plugins/users/users-plugin.ts +418 -0
  275. package/ui/src/App.tsx +188 -31
  276. package/ui/src/api/controlPanelApi.ts +453 -1
  277. package/ui/src/components/ControlPanelApp.tsx +212 -0
  278. package/ui/src/components/index.ts +62 -0
  279. package/ui/src/dashboard/DashboardWidgetRegistry.tsx +129 -0
  280. package/ui/src/dashboard/DashboardWidgetRenderer.tsx +34 -0
  281. package/ui/src/dashboard/PluginWidgetRenderer.tsx +118 -0
  282. package/ui/src/dashboard/WidgetComponentRegistry.tsx +120 -0
  283. package/ui/src/dashboard/builtInWidgets.tsx +35 -0
  284. package/ui/src/dashboard/index.ts +35 -0
  285. package/ui/src/dashboard/widgets/ServiceHealthWidget.tsx +140 -0
  286. package/ui/src/dashboard/widgets/index.ts +7 -0
  287. package/ui/src/pages/AuthPage.tsx +259 -0
  288. package/ui/src/pages/DashboardPage.tsx +28 -149
  289. package/ui/src/pages/EntitlementsPage.tsx +557 -0
  290. package/ui/src/pages/LogsPage.tsx +174 -8
  291. package/ui/src/pages/PluginPage.tsx +148 -0
  292. package/ui/src/pages/PluginsPage.tsx +394 -0
  293. package/ui/src/pages/SystemPage.tsx +445 -0
  294. package/ui/src/pages/UsersPage.tsx +837 -0
  295. package/ui/tsconfig.lib.json +11 -0
  296. package/ui/vite.lib.config.ts +56 -0
  297. package/dist-ui/assets/index-CW1BviRn.js +0 -465
  298. package/dist-ui/assets/index-CW1BviRn.js.map +0 -1
  299. package/ui/src/pages/HealthPage.tsx +0 -204
@@ -0,0 +1 @@
1
+ export declare function AuthPage(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function ConfigPage(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function DashboardPage(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function DiagnosticsPage(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * EntitlementsPage Component
3
+ *
4
+ * Entitlement catalog management page. Allows viewing and managing available entitlements.
5
+ * Write operations (create, edit, delete) are only available when source is not readonly.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+ export interface EntitlementsPageProps {
10
+ /** Page title */
11
+ title?: string;
12
+ /** Page subtitle */
13
+ subtitle?: string;
14
+ /** Custom actions to render in the header */
15
+ headerActions?: React.ReactNode;
16
+ }
17
+ export declare function EntitlementsPage({ title, subtitle, headerActions, }: EntitlementsPageProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function LogsPage(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function NotFoundPage(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * PluginPage Component
3
+ *
4
+ * A generic page component for plugin-contributed routes.
5
+ * Fetches and displays plugin-specific content from the API.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+ interface PluginPageProps {
10
+ pluginId: string;
11
+ title: string;
12
+ route: string;
13
+ }
14
+ export declare function PluginPage({ pluginId, title, route }: PluginPageProps): import("react/jsx-runtime").JSX.Element;
15
+ export {};
@@ -0,0 +1 @@
1
+ export declare function PluginsPage(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function SystemPage(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * UsersPage Component
3
+ *
4
+ * Generic user management page that works with Users, Bans, and Entitlements plugins.
5
+ * All features are optional and auto-detected based on available plugins.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+ import { type User, type PluginFeatures } from '../api/controlPanelApi';
10
+ export interface UsersPageProps {
11
+ /** Page title */
12
+ title?: string;
13
+ /** Page subtitle */
14
+ subtitle?: string;
15
+ /** Override automatic feature detection */
16
+ features?: Partial<PluginFeatures>;
17
+ /** Custom actions to render in the header */
18
+ headerActions?: React.ReactNode;
19
+ /** Callback when a user is selected */
20
+ onUserSelect?: (user: User) => void;
21
+ }
22
+ export declare function UsersPage({ title, subtitle, features: featureOverrides, headerActions, onUserSelect, }: UsersPageProps): import("react/jsx-runtime").JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qwickapps/server",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Plugin-based application server framework for building websites, APIs, admin dashboards, and full-stack products",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -13,25 +13,35 @@
13
13
  "./plugins": {
14
14
  "types": "./dist/plugins/index.d.ts",
15
15
  "import": "./dist/plugins/index.js"
16
+ },
17
+ "./ui": {
18
+ "types": "./dist-ui-lib/components/index.d.ts",
19
+ "import": "./dist-ui-lib/index.js"
16
20
  }
17
21
  },
18
22
  "files": [
19
23
  "dist",
20
24
  "dist-ui",
25
+ "dist-ui-lib",
21
26
  "src",
22
27
  "ui"
23
28
  ],
24
29
  "scripts": {
25
- "build": "npm run build:server && npm run build:ui",
30
+ "build": "npm run build:server && npm run build:ui && npm run build:ui-lib",
26
31
  "build:server": "tsc",
27
32
  "build:ui": "cd ui && vite build",
28
- "build:clean": "rm -rf dist dist-ui && npm run build",
33
+ "build:ui-lib": "cd ui && vite build --config vite.lib.config.ts && tsc -p tsconfig.lib.json",
34
+ "build:clean": "rm -rf dist dist-ui dist-ui-lib && npm run build",
29
35
  "dev": "tsc --watch",
30
36
  "dev:ui": "cd ui && vite",
31
- "clean": "rm -rf dist dist-ui node_modules",
37
+ "clean": "rm -rf dist dist-ui dist-ui-lib node_modules",
32
38
  "test": "vitest run",
33
39
  "test:watch": "vitest",
34
40
  "test:coverage": "vitest run --coverage",
41
+ "test:e2e": "playwright test",
42
+ "test:e2e:ui": "playwright test --ui",
43
+ "test:e2e:headed": "playwright test --headed",
44
+ "demo": "npx tsx examples/demo-server.ts",
35
45
  "type-check": "tsc --noEmit",
36
46
  "type-check:ui": "cd ui && tsc --noEmit",
37
47
  "validate:clean-install": "./qa/clean-install/validate.sh",
@@ -50,6 +60,7 @@
50
60
  "@emotion/styled": "^11.14.0",
51
61
  "@mui/icons-material": "^7.2.0",
52
62
  "@mui/material": "^7.2.0",
63
+ "@playwright/test": "^1.57.0",
53
64
  "@qwickapps/react-framework": "^1.5.5",
54
65
  "@types/compression": "^1.7.5",
55
66
  "@types/cors": "^2.8.17",
@@ -57,14 +68,16 @@
57
68
  "@types/node": "^20.10.5",
58
69
  "@types/pg": "^8.11.0",
59
70
  "@types/react": "^18.2.0",
60
- "ioredis": "^5.4.0",
61
- "pg": "^8.13.0",
62
71
  "@types/react-dom": "^18.2.0",
63
72
  "@vitejs/plugin-react": "^4.3.4",
64
73
  "express-openid-connect": "^2.19.3",
74
+ "ioredis": "^5.4.0",
75
+ "pg": "^8.13.0",
65
76
  "react": "^18.2.0",
66
77
  "react-dom": "^18.2.0",
67
78
  "react-router-dom": "^6.30.1",
79
+ "supertokens-node": "^20.1.7",
80
+ "tsx": "^4.20.6",
68
81
  "typescript": "^5.3.3",
69
82
  "vite": "^6.0.0",
70
83
  "vitest": "^2.1.0"
@@ -73,7 +86,8 @@
73
86
  "@qwickapps/react-framework": ">=1.0.0",
74
87
  "express-openid-connect": ">=2.0.0",
75
88
  "ioredis": ">=5.0.0",
76
- "pg": ">=8.0.0"
89
+ "pg": ">=8.0.0",
90
+ "supertokens-node": ">=20.0.0"
77
91
  },
78
92
  "peerDependenciesMeta": {
79
93
  "@qwickapps/react-framework": {
@@ -87,6 +101,9 @@
87
101
  },
88
102
  "pg": {
89
103
  "optional": true
104
+ },
105
+ "supertokens-node": {
106
+ "optional": true
90
107
  }
91
108
  },
92
109
  "keywords": [
@@ -10,7 +10,7 @@ import express, { type Application, type Router, type Request, type Response } f
10
10
  import helmet from 'helmet';
11
11
  import cors from 'cors';
12
12
  import compression from 'compression';
13
- import { existsSync } from 'node:fs';
13
+ import { existsSync, readFileSync } from 'node:fs';
14
14
  import { fileURLToPath } from 'node:url';
15
15
  import { dirname, join } from 'node:path';
16
16
  import { HealthManager } from './health-manager.js';
@@ -18,13 +18,17 @@ import { initializeLogging, getControlPanelLogger, type LoggingConfig } from './
18
18
  import { createRouteGuard } from './guards.js';
19
19
  import type {
20
20
  ControlPanelConfig,
21
- ControlPanelPlugin,
22
21
  ControlPanelInstance,
23
- PluginContext,
24
22
  DiagnosticsReport,
25
23
  HealthCheck,
26
24
  Logger,
27
25
  } from './types.js';
26
+ import {
27
+ createPluginRegistry,
28
+ type Plugin,
29
+ type PluginConfig,
30
+ type PluginRegistryImpl,
31
+ } from './plugin-registry.js';
28
32
 
29
33
  // Get the package root directory for serving UI assets
30
34
  const __filename = fileURLToPath(import.meta.url);
@@ -35,9 +39,22 @@ const packageRoot = __dirname.includes('/src/')
35
39
  : join(__dirname, '..', '..');
36
40
  const uiDistPath = join(packageRoot, 'dist-ui');
37
41
 
42
+ // Read @qwickapps/server package version
43
+ let frameworkVersion = '1.0.0';
44
+ try {
45
+ const packageJsonPath = join(packageRoot, 'package.json');
46
+ if (existsSync(packageJsonPath)) {
47
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
48
+ frameworkVersion = packageJson.version || '1.0.0';
49
+ }
50
+ } catch {
51
+ // Keep default version if reading fails
52
+ }
53
+
38
54
  export interface CreateControlPanelOptions {
39
55
  config: ControlPanelConfig;
40
- plugins?: ControlPanelPlugin[];
56
+ /** Plugins to start with the control panel */
57
+ plugins?: Array<{ plugin: Plugin; config?: PluginConfig }>;
41
58
  logger?: Logger;
42
59
  /** Logging configuration */
43
60
  logging?: LoggingConfig;
@@ -61,10 +78,18 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
61
78
  const app: Application = express();
62
79
  const router: Router = express.Router();
63
80
  const healthManager = new HealthManager(logger);
64
- const registeredPlugins: ControlPanelPlugin[] = [];
65
81
  let server: ReturnType<typeof app.listen> | null = null;
66
82
  const startTime = Date.now();
67
83
 
84
+ // Initialize the new plugin registry
85
+ const pluginRegistry = createPluginRegistry(
86
+ app,
87
+ router,
88
+ logger,
89
+ healthManager,
90
+ getControlPanelLogger
91
+ );
92
+
68
93
  // Security middleware
69
94
  app.use(
70
95
  helmet({
@@ -139,6 +164,8 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
139
164
  router.get('/info', (_req: Request, res: Response) => {
140
165
  res.json({
141
166
  product: config.productName,
167
+ logoName: config.logoName || config.productName,
168
+ logoIconUrl: config.logoIconUrl,
142
169
  version: config.version || 'unknown',
143
170
  uptime: Date.now() - startTime,
144
171
  links: config.links || [],
@@ -154,6 +181,61 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
154
181
  res.json(report);
155
182
  });
156
183
 
184
+ /**
185
+ * GET /api/ui-contributions - UI contributions from all plugins
186
+ *
187
+ * Returns menu items, pages, and widgets registered by plugins.
188
+ * Used by the React UI to build dynamic navigation and pages.
189
+ */
190
+ router.get('/ui-contributions', (_req: Request, res: Response) => {
191
+ res.json({
192
+ menuItems: pluginRegistry.getMenuItems(),
193
+ pages: pluginRegistry.getPages(),
194
+ widgets: pluginRegistry.getWidgets(),
195
+ plugins: pluginRegistry.listPlugins(),
196
+ });
197
+ });
198
+
199
+ /**
200
+ * GET /api/plugins - List all registered plugins with contribution counts
201
+ */
202
+ router.get('/plugins', (_req: Request, res: Response) => {
203
+ const plugins = pluginRegistry.listPlugins().map((plugin) => {
204
+ const contributions = pluginRegistry.getPluginContributions(plugin.id);
205
+ return {
206
+ ...plugin,
207
+ contributionCounts: {
208
+ routes: contributions.routes.length,
209
+ menuItems: contributions.menuItems.length,
210
+ pages: contributions.pages.length,
211
+ widgets: contributions.widgets.length,
212
+ hasConfig: !!contributions.config,
213
+ },
214
+ };
215
+ });
216
+ res.json({ plugins });
217
+ });
218
+
219
+ /**
220
+ * GET /api/plugins/:id - Get detailed plugin info with contributions
221
+ */
222
+ router.get('/plugins/:id', (req: Request, res: Response) => {
223
+ const { id } = req.params;
224
+ const plugins = pluginRegistry.listPlugins();
225
+ const plugin = plugins.find((p) => p.id === id);
226
+
227
+ if (!plugin) {
228
+ res.status(404).json({ error: `Plugin not found: ${id}` });
229
+ return;
230
+ }
231
+
232
+ const contributions = pluginRegistry.getPluginContributions(id);
233
+ res.json({
234
+ ...plugin,
235
+ contributions,
236
+ });
237
+ });
238
+
157
239
  /**
158
240
  * Serve dashboard UI at the configured mount path
159
241
  *
@@ -171,22 +253,61 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
171
253
 
172
254
  if (useRichUI) {
173
255
  logger.debug(`Serving React UI from ${effectiveUiPath}`);
256
+
257
+ // Read index.html template
258
+ const indexHtmlPath = join(effectiveUiPath, 'index.html');
259
+ const indexHtmlTemplate = readFileSync(indexHtmlPath, 'utf-8');
260
+
261
+ /**
262
+ * Get index.html with the base path injected.
263
+ *
264
+ * The server injects the base path as window.__APP_BASE_PATH__ so the React app
265
+ * can read it at runtime without complex detection logic. This is the standard
266
+ * pattern used by frameworks like Next.js (__NEXT_DATA__).
267
+ *
268
+ * When served behind a gateway proxy, use X-Forwarded-Prefix to determine
269
+ * the public path for assets and the React Router basename.
270
+ */
271
+ const getIndexHtml = (req: Request): string => {
272
+ // Determine the effective public path:
273
+ // - If X-Forwarded-Prefix header is set (proxied), use that
274
+ // - Otherwise, use the configured mountPath
275
+ const forwardedPrefix = req.get('X-Forwarded-Prefix');
276
+ const effectivePath = forwardedPrefix || mountPath;
277
+ const normalizedPath = effectivePath === '/' ? '' : effectivePath;
278
+
279
+ // Inject base path as global variable before other scripts
280
+ const basePathScript = `<script>window.__APP_BASE_PATH__="${normalizedPath}";</script>`;
281
+ let html = indexHtmlTemplate.replace('<head>', `<head>\n ${basePathScript}`);
282
+
283
+ // Rewrite asset paths if mounted at a subpath
284
+ if (normalizedPath) {
285
+ html = html.replace(/src="\/assets\//g, `src="${normalizedPath}/assets/`);
286
+ html = html.replace(/href="\/assets\//g, `href="${normalizedPath}/assets/`);
287
+ }
288
+
289
+ return html;
290
+ };
291
+
174
292
  // Serve static assets from dist-ui at the mount path
175
- app.use(mountPath, express.static(effectiveUiPath));
293
+ // Disable index: false to prevent serving index.html automatically
294
+ // We handle index.html separately with rewritten asset paths
295
+ app.use(mountPath, express.static(effectiveUiPath, { index: false }));
176
296
 
177
297
  // SPA fallback - serve index.html for all non-API routes under the mount path
178
- app.get(`${mountPath}/*`, (req: Request, res: Response, next) => {
298
+ const spaFallbackPath = mountPath === '/' ? '/*' : `${mountPath}/*`;
299
+ app.get(spaFallbackPath, (req: Request, res: Response, next) => {
179
300
  // Skip API routes
180
301
  if (req.path.startsWith(apiBasePath)) {
181
302
  return next();
182
303
  }
183
- res.sendFile(join(effectiveUiPath, 'index.html'));
304
+ res.type('html').send(getIndexHtml(req));
184
305
  });
185
306
 
186
307
  // Also serve the mount path root
187
308
  if (mountPath !== '/') {
188
- app.get(mountPath, (_req: Request, res: Response) => {
189
- res.sendFile(join(effectiveUiPath, 'index.html'));
309
+ app.get(mountPath, (req: Request, res: Response) => {
310
+ res.type('html').send(getIndexHtml(req));
190
311
  });
191
312
  }
192
313
  } else {
@@ -199,47 +320,9 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
199
320
  }
200
321
  }
201
322
 
202
- // Plugin context factory - creates context with plugin-specific logger
203
- const createPluginContext = (pluginName: string): PluginContext => ({
204
- config,
205
- app,
206
- router,
207
- logger: getControlPanelLogger(pluginName),
208
- registerHealthCheck: (check: HealthCheck) => healthManager.register(check),
209
- });
210
-
211
- // Register plugin
212
- const registerPlugin = async (plugin: ControlPanelPlugin): Promise<void> => {
213
- logger.debug(`Registering plugin: ${plugin.name}`);
214
-
215
- // Register routes
216
- if (plugin.routes) {
217
- for (const route of plugin.routes) {
218
- switch (route.method) {
219
- case 'get':
220
- router.get(route.path, route.handler);
221
- break;
222
- case 'post':
223
- router.post(route.path, route.handler);
224
- break;
225
- case 'put':
226
- router.put(route.path, route.handler);
227
- break;
228
- case 'delete':
229
- router.delete(route.path, route.handler);
230
- break;
231
- }
232
- logger.debug(`Registered route: ${route.method.toUpperCase()} ${route.path}`);
233
- }
234
- }
235
-
236
- // Initialize plugin with plugin-specific logger
237
- if (plugin.onInit) {
238
- await plugin.onInit(createPluginContext(plugin.name));
239
- }
240
-
241
- registeredPlugins.push(plugin);
242
- logger.debug(`Plugin registered: ${plugin.name}`);
323
+ // Start a plugin with the registry
324
+ const startPlugin = async (plugin: Plugin, pluginConfig: PluginConfig = {}): Promise<boolean> => {
325
+ return pluginRegistry.startPlugin(plugin, pluginConfig);
243
326
  };
244
327
 
245
328
  // Get diagnostics report
@@ -250,6 +333,7 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
250
333
  timestamp: new Date().toISOString(),
251
334
  product: config.productName,
252
335
  version: config.version,
336
+ frameworkVersion,
253
337
  uptime: Date.now() - startTime,
254
338
  health: healthManager.getResults(),
255
339
  system: {
@@ -270,9 +354,12 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
270
354
 
271
355
  // Start server
272
356
  const start = async (): Promise<void> => {
273
- // Register initial plugins
274
- for (const plugin of plugins) {
275
- await registerPlugin(plugin);
357
+ // Start initial plugins via registry
358
+ for (const { plugin, config: pluginConfig } of plugins) {
359
+ const success = await pluginRegistry.startPlugin(plugin, pluginConfig || {});
360
+ if (!success) {
361
+ logger.error(`Failed to start plugin: ${plugin.id}`);
362
+ }
276
363
  }
277
364
 
278
365
  return new Promise((resolve) => {
@@ -285,12 +372,8 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
285
372
 
286
373
  // Stop server
287
374
  const stop = async (): Promise<void> => {
288
- // Shutdown plugins
289
- for (const plugin of registeredPlugins) {
290
- if (plugin.onShutdown) {
291
- await plugin.onShutdown();
292
- }
293
- }
375
+ // Stop all plugins via registry
376
+ await pluginRegistry.stopAllPlugins();
294
377
 
295
378
  // Shutdown health manager
296
379
  healthManager.shutdown();
@@ -310,9 +393,10 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
310
393
  app,
311
394
  start,
312
395
  stop,
313
- registerPlugin,
396
+ startPlugin,
314
397
  getHealthStatus: () => healthManager.getResults(),
315
398
  getDiagnostics,
399
+ getPluginRegistry: () => pluginRegistry,
316
400
  };
317
401
  }
318
402