@qwickapps/server 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/README.md +311 -0
  2. package/dist/core/control-panel.d.ts.map +1 -1
  3. package/dist/core/control-panel.js +144 -2
  4. package/dist/core/control-panel.js.map +1 -1
  5. package/dist/core/plugin-registry.d.ts +36 -0
  6. package/dist/core/plugin-registry.d.ts.map +1 -1
  7. package/dist/core/plugin-registry.js +26 -0
  8. package/dist/core/plugin-registry.js.map +1 -1
  9. package/dist/core/types.d.ts +19 -0
  10. package/dist/core/types.d.ts.map +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +4 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/plugins/auth/adapter-wrapper.d.ts +47 -0
  16. package/dist/plugins/auth/adapter-wrapper.d.ts.map +1 -0
  17. package/dist/plugins/auth/adapter-wrapper.js +166 -0
  18. package/dist/plugins/auth/adapter-wrapper.js.map +1 -0
  19. package/dist/plugins/auth/adapter-wrapper.test.d.ts +7 -0
  20. package/dist/plugins/auth/adapter-wrapper.test.d.ts.map +1 -0
  21. package/dist/plugins/auth/adapter-wrapper.test.js +303 -0
  22. package/dist/plugins/auth/adapter-wrapper.test.js.map +1 -0
  23. package/dist/plugins/auth/adapters/index.d.ts +1 -0
  24. package/dist/plugins/auth/adapters/index.d.ts.map +1 -1
  25. package/dist/plugins/auth/adapters/index.js +1 -0
  26. package/dist/plugins/auth/adapters/index.js.map +1 -1
  27. package/dist/plugins/auth/adapters/supabase-adapter.d.ts.map +1 -1
  28. package/dist/plugins/auth/adapters/supabase-adapter.js.map +1 -1
  29. package/dist/plugins/auth/adapters/supertokens-adapter.d.ts +18 -0
  30. package/dist/plugins/auth/adapters/supertokens-adapter.d.ts.map +1 -0
  31. package/dist/plugins/auth/adapters/supertokens-adapter.js +267 -0
  32. package/dist/plugins/auth/adapters/supertokens-adapter.js.map +1 -0
  33. package/dist/plugins/auth/config-store.d.ts +11 -0
  34. package/dist/plugins/auth/config-store.d.ts.map +1 -0
  35. package/dist/plugins/auth/config-store.js +232 -0
  36. package/dist/plugins/auth/config-store.js.map +1 -0
  37. package/dist/plugins/auth/config-store.test.d.ts +7 -0
  38. package/dist/plugins/auth/config-store.test.d.ts.map +1 -0
  39. package/dist/plugins/auth/config-store.test.js +299 -0
  40. package/dist/plugins/auth/config-store.test.js.map +1 -0
  41. package/dist/plugins/auth/env-config.d.ts +138 -0
  42. package/dist/plugins/auth/env-config.d.ts.map +1 -0
  43. package/dist/plugins/auth/env-config.js +1122 -0
  44. package/dist/plugins/auth/env-config.js.map +1 -0
  45. package/dist/plugins/auth/index.d.ts +7 -1
  46. package/dist/plugins/auth/index.d.ts.map +1 -1
  47. package/dist/plugins/auth/index.js +7 -0
  48. package/dist/plugins/auth/index.js.map +1 -1
  49. package/dist/plugins/auth/supertokens-adapter.test.d.ts +10 -0
  50. package/dist/plugins/auth/supertokens-adapter.test.d.ts.map +1 -0
  51. package/dist/plugins/auth/supertokens-adapter.test.js +486 -0
  52. package/dist/plugins/auth/supertokens-adapter.test.js.map +1 -0
  53. package/dist/plugins/auth/types.d.ts +176 -0
  54. package/dist/plugins/auth/types.d.ts.map +1 -1
  55. package/dist/plugins/auth/types.js.map +1 -1
  56. package/dist/plugins/cache-plugin.test.js +3 -0
  57. package/dist/plugins/cache-plugin.test.js.map +1 -1
  58. package/dist/plugins/index.d.ts +6 -2
  59. package/dist/plugins/index.d.ts.map +1 -1
  60. package/dist/plugins/index.js +5 -1
  61. package/dist/plugins/index.js.map +1 -1
  62. package/dist/plugins/postgres-plugin.test.js +3 -0
  63. package/dist/plugins/postgres-plugin.test.js.map +1 -1
  64. package/dist/plugins/preferences/__tests__/deep-merge.test.d.ts +7 -0
  65. package/dist/plugins/preferences/__tests__/deep-merge.test.d.ts.map +1 -0
  66. package/dist/plugins/preferences/__tests__/deep-merge.test.js +215 -0
  67. package/dist/plugins/preferences/__tests__/deep-merge.test.js.map +1 -0
  68. package/dist/plugins/preferences/__tests__/preferences-plugin.test.d.ts +7 -0
  69. package/dist/plugins/preferences/__tests__/preferences-plugin.test.d.ts.map +1 -0
  70. package/dist/plugins/preferences/__tests__/preferences-plugin.test.js +265 -0
  71. package/dist/plugins/preferences/__tests__/preferences-plugin.test.js.map +1 -0
  72. package/dist/plugins/preferences/index.d.ts +12 -0
  73. package/dist/plugins/preferences/index.d.ts.map +1 -0
  74. package/dist/plugins/preferences/index.js +13 -0
  75. package/dist/plugins/preferences/index.js.map +1 -0
  76. package/dist/plugins/preferences/preferences-plugin.d.ts +39 -0
  77. package/dist/plugins/preferences/preferences-plugin.d.ts.map +1 -0
  78. package/dist/plugins/preferences/preferences-plugin.js +226 -0
  79. package/dist/plugins/preferences/preferences-plugin.js.map +1 -0
  80. package/dist/plugins/preferences/stores/index.d.ts +9 -0
  81. package/dist/plugins/preferences/stores/index.d.ts.map +1 -0
  82. package/dist/plugins/preferences/stores/index.js +9 -0
  83. package/dist/plugins/preferences/stores/index.js.map +1 -0
  84. package/dist/plugins/preferences/stores/postgres-store.d.ts +41 -0
  85. package/dist/plugins/preferences/stores/postgres-store.d.ts.map +1 -0
  86. package/dist/plugins/preferences/stores/postgres-store.js +181 -0
  87. package/dist/plugins/preferences/stores/postgres-store.js.map +1 -0
  88. package/dist/plugins/preferences/types.d.ts +91 -0
  89. package/dist/plugins/preferences/types.d.ts.map +1 -0
  90. package/dist/plugins/preferences/types.js +10 -0
  91. package/dist/plugins/preferences/types.js.map +1 -0
  92. package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.d.ts +7 -0
  93. package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.d.ts.map +1 -0
  94. package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.js +220 -0
  95. package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.js.map +1 -0
  96. package/dist/plugins/rate-limit/cleanup.d.ts +40 -0
  97. package/dist/plugins/rate-limit/cleanup.d.ts.map +1 -0
  98. package/dist/plugins/rate-limit/cleanup.js +72 -0
  99. package/dist/plugins/rate-limit/cleanup.js.map +1 -0
  100. package/dist/plugins/rate-limit/env-config.d.ts +91 -0
  101. package/dist/plugins/rate-limit/env-config.d.ts.map +1 -0
  102. package/dist/plugins/rate-limit/env-config.js +318 -0
  103. package/dist/plugins/rate-limit/env-config.js.map +1 -0
  104. package/dist/plugins/rate-limit/index.d.ts +76 -0
  105. package/dist/plugins/rate-limit/index.d.ts.map +1 -0
  106. package/dist/plugins/rate-limit/index.js +79 -0
  107. package/dist/plugins/rate-limit/index.js.map +1 -0
  108. package/dist/plugins/rate-limit/middleware.d.ts +40 -0
  109. package/dist/plugins/rate-limit/middleware.d.ts.map +1 -0
  110. package/dist/plugins/rate-limit/middleware.js +169 -0
  111. package/dist/plugins/rate-limit/middleware.js.map +1 -0
  112. package/dist/plugins/rate-limit/rate-limit-plugin.d.ts +44 -0
  113. package/dist/plugins/rate-limit/rate-limit-plugin.d.ts.map +1 -0
  114. package/dist/plugins/rate-limit/rate-limit-plugin.js +354 -0
  115. package/dist/plugins/rate-limit/rate-limit-plugin.js.map +1 -0
  116. package/dist/plugins/rate-limit/rate-limit-service.d.ts +110 -0
  117. package/dist/plugins/rate-limit/rate-limit-service.d.ts.map +1 -0
  118. package/dist/plugins/rate-limit/rate-limit-service.js +172 -0
  119. package/dist/plugins/rate-limit/rate-limit-service.js.map +1 -0
  120. package/dist/plugins/rate-limit/stores/cache-store.d.ts +33 -0
  121. package/dist/plugins/rate-limit/stores/cache-store.d.ts.map +1 -0
  122. package/dist/plugins/rate-limit/stores/cache-store.js +225 -0
  123. package/dist/plugins/rate-limit/stores/cache-store.js.map +1 -0
  124. package/dist/plugins/rate-limit/stores/index.d.ts +8 -0
  125. package/dist/plugins/rate-limit/stores/index.d.ts.map +1 -0
  126. package/dist/plugins/rate-limit/stores/index.js +8 -0
  127. package/dist/plugins/rate-limit/stores/index.js.map +1 -0
  128. package/dist/plugins/rate-limit/stores/postgres-store.d.ts +34 -0
  129. package/dist/plugins/rate-limit/stores/postgres-store.d.ts.map +1 -0
  130. package/dist/plugins/rate-limit/stores/postgres-store.js +320 -0
  131. package/dist/plugins/rate-limit/stores/postgres-store.js.map +1 -0
  132. package/dist/plugins/rate-limit/strategies/fixed-window.d.ts +21 -0
  133. package/dist/plugins/rate-limit/strategies/fixed-window.d.ts.map +1 -0
  134. package/dist/plugins/rate-limit/strategies/fixed-window.js +97 -0
  135. package/dist/plugins/rate-limit/strategies/fixed-window.js.map +1 -0
  136. package/dist/plugins/rate-limit/strategies/index.d.ts +14 -0
  137. package/dist/plugins/rate-limit/strategies/index.d.ts.map +1 -0
  138. package/dist/plugins/rate-limit/strategies/index.js +27 -0
  139. package/dist/plugins/rate-limit/strategies/index.js.map +1 -0
  140. package/dist/plugins/rate-limit/strategies/sliding-window.d.ts +22 -0
  141. package/dist/plugins/rate-limit/strategies/sliding-window.d.ts.map +1 -0
  142. package/dist/plugins/rate-limit/strategies/sliding-window.js +122 -0
  143. package/dist/plugins/rate-limit/strategies/sliding-window.js.map +1 -0
  144. package/dist/plugins/rate-limit/strategies/token-bucket.d.ts +28 -0
  145. package/dist/plugins/rate-limit/strategies/token-bucket.d.ts.map +1 -0
  146. package/dist/plugins/rate-limit/strategies/token-bucket.js +121 -0
  147. package/dist/plugins/rate-limit/strategies/token-bucket.js.map +1 -0
  148. package/dist/plugins/rate-limit/types.d.ts +265 -0
  149. package/dist/plugins/rate-limit/types.d.ts.map +1 -0
  150. package/dist/plugins/rate-limit/types.js +9 -0
  151. package/dist/plugins/rate-limit/types.js.map +1 -0
  152. package/dist/plugins/users/__tests__/users-plugin.test.d.ts +9 -0
  153. package/dist/plugins/users/__tests__/users-plugin.test.d.ts.map +1 -0
  154. package/dist/plugins/users/__tests__/users-plugin.test.js +546 -0
  155. package/dist/plugins/users/__tests__/users-plugin.test.js.map +1 -0
  156. package/dist/plugins/users/index.d.ts +2 -2
  157. package/dist/plugins/users/index.d.ts.map +1 -1
  158. package/dist/plugins/users/index.js +1 -1
  159. package/dist/plugins/users/index.js.map +1 -1
  160. package/dist/plugins/users/types.d.ts +36 -0
  161. package/dist/plugins/users/types.d.ts.map +1 -1
  162. package/dist/plugins/users/users-plugin.d.ts +8 -2
  163. package/dist/plugins/users/users-plugin.d.ts.map +1 -1
  164. package/dist/plugins/users/users-plugin.js +122 -5
  165. package/dist/plugins/users/users-plugin.js.map +1 -1
  166. package/dist-ui/assets/index-D7DoZ9rL.js +478 -0
  167. package/dist-ui/assets/index-D7DoZ9rL.js.map +1 -0
  168. package/dist-ui/index.html +1 -1
  169. package/dist-ui-lib/api/controlPanelApi.d.ts +194 -7
  170. package/dist-ui-lib/dashboard/WidgetComponentRegistry.d.ts +9 -5
  171. package/dist-ui-lib/dashboard/builtInWidgets.d.ts +7 -1
  172. package/dist-ui-lib/dashboard/widgets/AuthStatusWidget.d.ts +9 -0
  173. package/dist-ui-lib/dashboard/widgets/IntegrationStatusWidget.d.ts +9 -0
  174. package/dist-ui-lib/dashboard/widgets/index.d.ts +2 -0
  175. package/dist-ui-lib/index.js +3665 -3945
  176. package/dist-ui-lib/index.js.map +1 -1
  177. package/dist-ui-lib/pages/AuthPage.d.ts +1 -0
  178. package/dist-ui-lib/pages/IntegrationsPage.d.ts +1 -0
  179. package/dist-ui-lib/pages/PluginsPage.d.ts +1 -0
  180. package/dist-ui-lib/pages/RateLimitPage.d.ts +1 -0
  181. package/package.json +7 -2
  182. package/src/core/control-panel.ts +161 -2
  183. package/src/core/plugin-registry.ts +63 -0
  184. package/src/core/types.ts +17 -0
  185. package/src/index.ts +45 -0
  186. package/src/plugins/auth/adapter-wrapper.test.ts +395 -0
  187. package/src/plugins/auth/adapter-wrapper.ts +205 -0
  188. package/src/plugins/auth/adapters/index.ts +1 -0
  189. package/src/plugins/auth/adapters/supabase-adapter.ts +22 -14
  190. package/src/plugins/auth/adapters/supertokens-adapter.ts +326 -0
  191. package/src/plugins/auth/config-store.test.ts +417 -0
  192. package/src/plugins/auth/config-store.ts +305 -0
  193. package/src/plugins/auth/env-config.ts +1279 -0
  194. package/src/plugins/auth/index.ts +30 -0
  195. package/src/plugins/auth/supertokens-adapter.test.ts +621 -0
  196. package/src/plugins/auth/types.ts +218 -0
  197. package/src/plugins/cache-plugin.test.ts +3 -0
  198. package/src/plugins/index.ts +75 -0
  199. package/src/plugins/postgres-plugin.test.ts +3 -0
  200. package/src/plugins/preferences/__tests__/deep-merge.test.ts +242 -0
  201. package/src/plugins/preferences/__tests__/preferences-plugin.test.ts +350 -0
  202. package/src/plugins/preferences/index.ts +30 -0
  203. package/src/plugins/preferences/preferences-plugin.ts +270 -0
  204. package/src/plugins/preferences/stores/index.ts +9 -0
  205. package/src/plugins/preferences/stores/postgres-store.ts +252 -0
  206. package/src/plugins/preferences/types.ts +100 -0
  207. package/src/plugins/rate-limit/__tests__/rate-limit-plugin.test.ts +259 -0
  208. package/src/plugins/rate-limit/cleanup.ts +117 -0
  209. package/src/plugins/rate-limit/env-config.ts +400 -0
  210. package/src/plugins/rate-limit/index.ts +128 -0
  211. package/src/plugins/rate-limit/middleware.ts +212 -0
  212. package/src/plugins/rate-limit/rate-limit-plugin.ts +400 -0
  213. package/src/plugins/rate-limit/rate-limit-service.ts +228 -0
  214. package/src/plugins/rate-limit/stores/cache-store.ts +261 -0
  215. package/src/plugins/rate-limit/stores/index.ts +8 -0
  216. package/src/plugins/rate-limit/stores/postgres-store.ts +402 -0
  217. package/src/plugins/rate-limit/strategies/fixed-window.ts +116 -0
  218. package/src/plugins/rate-limit/strategies/index.ts +30 -0
  219. package/src/plugins/rate-limit/strategies/sliding-window.ts +157 -0
  220. package/src/plugins/rate-limit/strategies/token-bucket.ts +154 -0
  221. package/src/plugins/rate-limit/types.ts +338 -0
  222. package/src/plugins/users/__tests__/users-plugin.test.ts +690 -0
  223. package/src/plugins/users/index.ts +3 -0
  224. package/src/plugins/users/types.ts +38 -0
  225. package/src/plugins/users/users-plugin.ts +142 -5
  226. package/ui/src/App.tsx +35 -14
  227. package/ui/src/api/controlPanelApi.ts +326 -1
  228. package/ui/src/components/ControlPanelApp.tsx +3 -0
  229. package/ui/src/dashboard/PluginWidgetRenderer.tsx +13 -10
  230. package/ui/src/dashboard/WidgetComponentRegistry.tsx +13 -9
  231. package/ui/src/dashboard/builtInWidgets.tsx +13 -3
  232. package/ui/src/dashboard/widgets/AuthStatusWidget.tsx +143 -0
  233. package/ui/src/dashboard/widgets/IntegrationStatusWidget.tsx +135 -0
  234. package/ui/src/dashboard/widgets/index.ts +2 -0
  235. package/ui/src/pages/AuthPage.tsx +1103 -0
  236. package/ui/src/pages/IntegrationsPage.tsx +288 -0
  237. package/ui/src/pages/PluginsPage.tsx +394 -0
  238. package/ui/src/pages/RateLimitPage.tsx +292 -0
  239. package/ui/vite.lib.config.ts +5 -0
  240. package/dist-ui/assets/index-Bsp2ntcw.js +0 -465
  241. package/dist-ui/assets/index-Bsp2ntcw.js.map +0 -1
@@ -127,6 +127,47 @@ export interface BasicAdapterConfig {
127
127
  realm?: string;
128
128
  }
129
129
 
130
+ /**
131
+ * Supertokens adapter configuration
132
+ */
133
+ export interface SupertokensAdapterConfig {
134
+ /** Supertokens connection URI (e.g., 'http://localhost:3567') */
135
+ connectionUri: string;
136
+
137
+ /** Supertokens API key (for managed service) */
138
+ apiKey?: string;
139
+
140
+ /** App name for branding */
141
+ appName: string;
142
+
143
+ /** API domain (e.g., 'http://localhost:3000') */
144
+ apiDomain: string;
145
+
146
+ /** Website domain (e.g., 'http://localhost:3000') */
147
+ websiteDomain: string;
148
+
149
+ /** API base path (default: '/auth') */
150
+ apiBasePath?: string;
151
+
152
+ /** Website base path (default: '/auth') */
153
+ websiteBasePath?: string;
154
+
155
+ /** Enable email/password auth (default: true) */
156
+ enableEmailPassword?: boolean;
157
+
158
+ /** Social login providers */
159
+ socialProviders?: {
160
+ google?: { clientId: string; clientSecret: string };
161
+ apple?: {
162
+ clientId: string;
163
+ clientSecret: string;
164
+ keyId: string;
165
+ teamId: string;
166
+ };
167
+ github?: { clientId: string; clientSecret: string };
168
+ };
169
+ }
170
+
130
171
  /**
131
172
  * Auth plugin configuration
132
173
  */
@@ -163,3 +204,180 @@ export interface AuthenticatedRequest extends Request {
163
204
  export function isAuthenticatedRequest(req: Request): req is AuthenticatedRequest {
164
205
  return 'auth' in req && (req as AuthenticatedRequest).auth?.isAuthenticated === true;
165
206
  }
207
+
208
+ // ═══════════════════════════════════════════════════════════════════════════
209
+ // Environment Configuration Types
210
+ // ═══════════════════════════════════════════════════════════════════════════
211
+
212
+ /**
213
+ * Auth plugin state
214
+ */
215
+ export type AuthPluginState = 'disabled' | 'enabled' | 'error';
216
+
217
+ /**
218
+ * Options for createAuthPluginFromEnv (overrides only)
219
+ */
220
+ export interface AuthEnvPluginOptions {
221
+ /** Paths to exclude from authentication (can also use AUTH_EXCLUDE_PATHS env var) */
222
+ excludePaths?: string[];
223
+ /** Whether auth is required (can also use AUTH_REQUIRED env var, default: true) */
224
+ authRequired?: boolean;
225
+ /** Enable debug logging (can also use AUTH_DEBUG env var) */
226
+ debug?: boolean;
227
+ /** Custom unauthorized handler */
228
+ onUnauthorized?: (req: Request, res: Response) => void;
229
+ }
230
+
231
+ /**
232
+ * Auth configuration status returned by getAuthStatus()
233
+ */
234
+ export interface AuthConfigStatus {
235
+ /** Current plugin state */
236
+ state: AuthPluginState;
237
+ /** Active adapter name (null if disabled or error) */
238
+ adapter: string | null;
239
+ /** Error message if state is 'error' */
240
+ error?: string;
241
+ /** List of missing environment variables if state is 'error' */
242
+ missingVars?: string[];
243
+ /** Current configuration with secrets masked */
244
+ config?: Record<string, string>;
245
+ }
246
+
247
+ // ═══════════════════════════════════════════════════════════════════════════
248
+ // Runtime Configuration Types
249
+ // ═══════════════════════════════════════════════════════════════════════════
250
+
251
+ /**
252
+ * Supported adapter types for runtime configuration
253
+ */
254
+ export type AuthAdapterType = 'auth0' | 'supabase' | 'supertokens' | 'basic';
255
+
256
+ /**
257
+ * Runtime auth configuration (persisted to database)
258
+ */
259
+ export interface RuntimeAuthConfig {
260
+ /** Which adapter to use */
261
+ adapter: AuthAdapterType | null;
262
+
263
+ /** Adapter-specific configuration */
264
+ config: {
265
+ auth0?: Auth0AdapterConfig;
266
+ supabase?: SupabaseAdapterConfig;
267
+ supertokens?: SupertokensAdapterConfig;
268
+ basic?: BasicAdapterConfig;
269
+ };
270
+
271
+ /** General auth settings */
272
+ settings: {
273
+ authRequired?: boolean;
274
+ excludePaths?: string[];
275
+ debug?: boolean;
276
+ };
277
+
278
+ /** When the config was last updated */
279
+ updatedAt: string;
280
+
281
+ /** Who updated the config (optional) */
282
+ updatedBy?: string;
283
+ }
284
+
285
+ /**
286
+ * Request body for PUT /api/auth/config
287
+ */
288
+ export interface UpdateAuthConfigRequest {
289
+ /** Which adapter to use */
290
+ adapter: AuthAdapterType;
291
+
292
+ /** Adapter-specific configuration */
293
+ config: Record<string, unknown>;
294
+
295
+ /** General settings (optional) */
296
+ settings?: {
297
+ authRequired?: boolean;
298
+ excludePaths?: string[];
299
+ };
300
+ }
301
+
302
+ /**
303
+ * Request body for POST /api/auth/test-provider
304
+ */
305
+ export interface TestProviderRequest {
306
+ /** Which adapter to test */
307
+ adapter: AuthAdapterType;
308
+
309
+ /** Adapter configuration to test */
310
+ config: Record<string, unknown>;
311
+
312
+ /** For social provider test (optional) */
313
+ provider?: 'google' | 'github' | 'apple';
314
+ }
315
+
316
+ /**
317
+ * Response for POST /api/auth/test-provider
318
+ */
319
+ export interface TestProviderResponse {
320
+ /** Whether the test was successful */
321
+ success: boolean;
322
+
323
+ /** Human-readable message */
324
+ message: string;
325
+
326
+ /** Additional details */
327
+ details?: {
328
+ latency?: number;
329
+ version?: string;
330
+ };
331
+ }
332
+
333
+ /**
334
+ * Auth configuration store interface
335
+ */
336
+ export interface AuthConfigStore {
337
+ /** Store name for identification */
338
+ name: string;
339
+
340
+ /** Initialize the store (create tables if needed) */
341
+ initialize(): Promise<void>;
342
+
343
+ /** Load configuration from store */
344
+ load(): Promise<RuntimeAuthConfig | null>;
345
+
346
+ /** Save configuration to store */
347
+ save(config: RuntimeAuthConfig): Promise<void>;
348
+
349
+ /** Delete configuration (revert to env vars) */
350
+ delete(): Promise<boolean>;
351
+
352
+ /**
353
+ * Subscribe to configuration changes
354
+ * Returns unsubscribe function
355
+ */
356
+ onChange(callback: (config: RuntimeAuthConfig | null) => void): () => void;
357
+
358
+ /** Shutdown and cleanup */
359
+ shutdown(): Promise<void>;
360
+ }
361
+
362
+ /**
363
+ * PostgreSQL auth config store configuration
364
+ */
365
+ export interface PostgresAuthConfigStoreConfig {
366
+ /** PostgreSQL pool instance or factory function for lazy initialization */
367
+ pool: unknown | (() => unknown);
368
+
369
+ /** Table name (default: 'auth_config') */
370
+ tableName?: string;
371
+
372
+ /** Schema name (default: 'public') */
373
+ schema?: string;
374
+
375
+ /** Auto-create table on initialization (default: true) */
376
+ autoCreateTable?: boolean;
377
+
378
+ /** Enable pg_notify for cross-instance config updates (default: true) */
379
+ enableNotify?: boolean;
380
+
381
+ /** Channel name for pg_notify (default: 'auth_config_changed') */
382
+ notifyChannel?: string;
383
+ }
@@ -79,10 +79,13 @@ describe('Cache Plugin', () => {
79
79
  addMenuItem: vi.fn(),
80
80
  addPage: vi.fn(),
81
81
  addWidget: vi.fn(),
82
+ addConfigComponent: vi.fn(),
82
83
  getRoutes: vi.fn().mockReturnValue([]),
83
84
  getMenuItems: vi.fn().mockReturnValue([]),
84
85
  getPages: vi.fn().mockReturnValue([]),
85
86
  getWidgets: vi.fn().mockReturnValue([]),
87
+ getConfigComponents: vi.fn().mockReturnValue([]),
88
+ getPluginContributions: vi.fn().mockReturnValue({ routes: [], menuItems: [], pages: [], widgets: [], config: undefined }),
86
89
  getConfig: vi.fn().mockReturnValue({}),
87
90
  setConfig: vi.fn().mockResolvedValue(undefined),
88
91
  subscribe: vi.fn().mockReturnValue(() => {}),
@@ -28,6 +28,10 @@ export type { CachePluginConfig, CacheInstance } from './cache-plugin.js';
28
28
  // Auth plugin
29
29
  export {
30
30
  createAuthPlugin,
31
+ createAuthPluginFromEnv,
32
+ getAuthStatus,
33
+ setAuthConfigStore,
34
+ postgresAuthConfigStore,
31
35
  isAuthenticated,
32
36
  getAuthenticatedUser,
33
37
  getAccessToken,
@@ -37,6 +41,7 @@ export {
37
41
  auth0Adapter,
38
42
  basicAdapter,
39
43
  supabaseAdapter,
44
+ supertokensAdapter,
40
45
  isAuthenticatedRequest,
41
46
  } from './auth/index.js';
42
47
  export type {
@@ -47,6 +52,12 @@ export type {
47
52
  Auth0AdapterConfig,
48
53
  SupabaseAdapterConfig,
49
54
  BasicAdapterConfig,
55
+ SupertokensAdapterConfig,
56
+ AuthPluginState,
57
+ AuthEnvPluginOptions,
58
+ AuthConfigStatus,
59
+ AuthConfigStore,
60
+ PostgresAuthConfigStoreConfig,
50
61
  } from './auth/index.js';
51
62
 
52
63
  // Users plugin
@@ -130,3 +141,67 @@ export type {
130
141
  CachedEntitlements,
131
142
  EntitlementStats,
132
143
  } from './entitlements/index.js';
144
+
145
+ // Preferences plugin (depends on Users)
146
+ export {
147
+ createPreferencesPlugin,
148
+ getPreferencesStore,
149
+ getPreferences,
150
+ updatePreferences,
151
+ deletePreferences,
152
+ getDefaultPreferences,
153
+ postgresPreferencesStore,
154
+ deepMerge,
155
+ } from './preferences/index.js';
156
+ export type {
157
+ PreferencesPluginConfig,
158
+ PreferencesStore,
159
+ UserPreferences,
160
+ PostgresPreferencesStoreConfig,
161
+ PreferencesApiConfig,
162
+ } from './preferences/index.js';
163
+
164
+ // Rate Limit plugin
165
+ export {
166
+ createRateLimitPlugin,
167
+ createRateLimitPluginFromEnv,
168
+ getRateLimitConfigStatus,
169
+ postgresRateLimitStore,
170
+ createRateLimitCache,
171
+ createNoOpCache,
172
+ createSlidingWindowStrategy,
173
+ createFixedWindowStrategy,
174
+ createTokenBucketStrategy,
175
+ getStrategy,
176
+ rateLimitMiddleware,
177
+ rateLimitStatusMiddleware,
178
+ RateLimitService,
179
+ getRateLimitService,
180
+ isLimited,
181
+ checkLimit,
182
+ incrementLimit,
183
+ getRemainingRequests,
184
+ getLimitStatus,
185
+ clearLimit,
186
+ createCleanupJob,
187
+ } from './rate-limit/index.js';
188
+ export type {
189
+ RateLimitPluginConfig,
190
+ RateLimitEnvPluginOptions,
191
+ RateLimitStrategy,
192
+ LimitStatus,
193
+ Strategy,
194
+ StrategyOptions,
195
+ StrategyContext,
196
+ StoredLimit,
197
+ IncrementOptions,
198
+ RateLimitStore,
199
+ PostgresRateLimitStoreConfig,
200
+ CachedLimit,
201
+ RateLimitCache,
202
+ RateLimitCacheConfig,
203
+ RateLimitMiddlewareOptions,
204
+ CheckLimitOptions,
205
+ CleanupJob,
206
+ CleanupJobConfig,
207
+ } from './rate-limit/index.js';
@@ -56,10 +56,13 @@ describe('PostgreSQL Plugin', () => {
56
56
  addMenuItem: vi.fn(),
57
57
  addPage: vi.fn(),
58
58
  addWidget: vi.fn(),
59
+ addConfigComponent: vi.fn(),
59
60
  getRoutes: vi.fn().mockReturnValue([]),
60
61
  getMenuItems: vi.fn().mockReturnValue([]),
61
62
  getPages: vi.fn().mockReturnValue([]),
62
63
  getWidgets: vi.fn().mockReturnValue([]),
64
+ getConfigComponents: vi.fn().mockReturnValue([]),
65
+ getPluginContributions: vi.fn().mockReturnValue({ routes: [], menuItems: [], pages: [], widgets: [], config: undefined }),
63
66
  getConfig: vi.fn().mockReturnValue({}),
64
67
  setConfig: vi.fn().mockResolvedValue(undefined),
65
68
  subscribe: vi.fn().mockReturnValue(() => {}),
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Deep Merge Utility Tests
3
+ *
4
+ * Unit tests for the deep merge function used by the preferences plugin.
5
+ */
6
+
7
+ import { describe, it, expect } from 'vitest';
8
+ import { deepMerge } from '../stores/postgres-store.js';
9
+
10
+ describe('deepMerge', () => {
11
+ describe('basic merging', () => {
12
+ it('should merge flat objects', () => {
13
+ const target = { a: 1 };
14
+ const source = { b: 2 };
15
+ const result = deepMerge(target, source);
16
+ expect(result).toEqual({ a: 1, b: 2 });
17
+ });
18
+
19
+ it('should not mutate original objects', () => {
20
+ const target = { a: 1 };
21
+ const source = { b: 2 };
22
+ deepMerge(target, source);
23
+ expect(target).toEqual({ a: 1 });
24
+ expect(source).toEqual({ b: 2 });
25
+ });
26
+
27
+ it('should return a new object', () => {
28
+ const target = { a: 1 };
29
+ const source = { b: 2 };
30
+ const result = deepMerge(target, source);
31
+ expect(result).not.toBe(target);
32
+ expect(result).not.toBe(source);
33
+ });
34
+ });
35
+
36
+ describe('nested object merging', () => {
37
+ it('should merge nested objects recursively', () => {
38
+ const target = { a: { x: 1 } };
39
+ const source = { a: { y: 2 } };
40
+ const result = deepMerge(target, source);
41
+ expect(result).toEqual({ a: { x: 1, y: 2 } });
42
+ });
43
+
44
+ it('should handle deeply nested objects', () => {
45
+ const target = { a: { b: { c: { x: 1 } } } };
46
+ const source = { a: { b: { c: { y: 2 } } } };
47
+ const result = deepMerge(target, source);
48
+ expect(result).toEqual({ a: { b: { c: { x: 1, y: 2 } } } });
49
+ });
50
+
51
+ it('should merge multiple nested keys', () => {
52
+ const target = {
53
+ theme: 'light',
54
+ notifications: { email: true, push: true },
55
+ };
56
+ const source = {
57
+ notifications: { email: false },
58
+ };
59
+ const result = deepMerge(target, source);
60
+ expect(result).toEqual({
61
+ theme: 'light',
62
+ notifications: { email: false, push: true },
63
+ });
64
+ });
65
+ });
66
+
67
+ describe('value overwriting', () => {
68
+ it('should let source override target for same keys', () => {
69
+ const target = { a: 1 };
70
+ const source = { a: 2 };
71
+ const result = deepMerge(target, source);
72
+ expect(result).toEqual({ a: 2 });
73
+ });
74
+
75
+ it('should let source override target for nested keys', () => {
76
+ const target = { a: { x: 1 } };
77
+ const source = { a: { x: 2 } };
78
+ const result = deepMerge(target, source);
79
+ expect(result).toEqual({ a: { x: 2 } });
80
+ });
81
+ });
82
+
83
+ describe('array handling', () => {
84
+ it('should replace arrays (not merge)', () => {
85
+ const target = { a: [1, 2] };
86
+ const source = { a: [3, 4, 5] };
87
+ const result = deepMerge(target, source);
88
+ expect(result).toEqual({ a: [3, 4, 5] });
89
+ });
90
+
91
+ it('should replace array with empty array', () => {
92
+ const target = { a: [1, 2, 3] };
93
+ const source = { a: [] };
94
+ const result = deepMerge(target, source);
95
+ expect(result).toEqual({ a: [] });
96
+ });
97
+
98
+ it('should replace non-array with array', () => {
99
+ const target = { a: 'string' };
100
+ const source = { a: [1, 2] };
101
+ const result = deepMerge(target, source);
102
+ expect(result).toEqual({ a: [1, 2] });
103
+ });
104
+
105
+ it('should replace array with non-array', () => {
106
+ const target = { a: [1, 2] };
107
+ const source = { a: 'string' };
108
+ const result = deepMerge(target, source);
109
+ expect(result).toEqual({ a: 'string' });
110
+ });
111
+ });
112
+
113
+ describe('special values', () => {
114
+ it('should handle null values in source', () => {
115
+ const target = { a: 1 };
116
+ const source = { a: null };
117
+ const result = deepMerge(target, source);
118
+ expect(result).toEqual({ a: null });
119
+ });
120
+
121
+ it('should skip undefined values in source', () => {
122
+ const target = { a: 1 };
123
+ const source = { a: undefined };
124
+ const result = deepMerge(target, source);
125
+ expect(result).toEqual({ a: 1 });
126
+ });
127
+
128
+ it('should handle null in target', () => {
129
+ const target = { a: null };
130
+ const source = { a: { x: 1 } };
131
+ const result = deepMerge(target, source);
132
+ expect(result).toEqual({ a: { x: 1 } });
133
+ });
134
+
135
+ it('should replace object with null', () => {
136
+ const target = { a: { x: 1 } };
137
+ const source = { a: null };
138
+ const result = deepMerge(target, source);
139
+ expect(result).toEqual({ a: null });
140
+ });
141
+ });
142
+
143
+ describe('edge cases', () => {
144
+ it('should handle empty target', () => {
145
+ const target = {};
146
+ const source = { a: 1 };
147
+ const result = deepMerge(target, source);
148
+ expect(result).toEqual({ a: 1 });
149
+ });
150
+
151
+ it('should handle empty source', () => {
152
+ const target = { a: 1 };
153
+ const source = {};
154
+ const result = deepMerge(target, source);
155
+ expect(result).toEqual({ a: 1 });
156
+ });
157
+
158
+ it('should handle both empty', () => {
159
+ const target = {};
160
+ const source = {};
161
+ const result = deepMerge(target, source);
162
+ expect(result).toEqual({});
163
+ });
164
+
165
+ it('should handle primitive values becoming objects', () => {
166
+ const target = { a: 'string' };
167
+ const source = { a: { x: 1 } };
168
+ const result = deepMerge(target, source);
169
+ expect(result).toEqual({ a: { x: 1 } });
170
+ });
171
+
172
+ it('should handle objects becoming primitive values', () => {
173
+ const target = { a: { x: 1 } };
174
+ const source = { a: 'string' };
175
+ const result = deepMerge(target, source);
176
+ expect(result).toEqual({ a: 'string' });
177
+ });
178
+ });
179
+
180
+ describe('real-world preference scenarios', () => {
181
+ it('should merge default preferences with user preferences', () => {
182
+ const defaults = {
183
+ theme: 'system',
184
+ notifications: {
185
+ email: true,
186
+ push: true,
187
+ sms: false,
188
+ },
189
+ trading: {
190
+ defaultSymbol: 'SPY',
191
+ chartInterval: '5min',
192
+ },
193
+ };
194
+
195
+ const userPrefs = {
196
+ theme: 'dark',
197
+ notifications: {
198
+ email: false,
199
+ },
200
+ };
201
+
202
+ const result = deepMerge(defaults, userPrefs);
203
+ expect(result).toEqual({
204
+ theme: 'dark',
205
+ notifications: {
206
+ email: false,
207
+ push: true,
208
+ sms: false,
209
+ },
210
+ trading: {
211
+ defaultSymbol: 'SPY',
212
+ chartInterval: '5min',
213
+ },
214
+ });
215
+ });
216
+
217
+ it('should handle partial updates to preferences', () => {
218
+ const existing = {
219
+ theme: 'dark',
220
+ notifications: {
221
+ email: false,
222
+ push: true,
223
+ },
224
+ };
225
+
226
+ const update = {
227
+ notifications: {
228
+ push: false,
229
+ },
230
+ };
231
+
232
+ const result = deepMerge(existing, update);
233
+ expect(result).toEqual({
234
+ theme: 'dark',
235
+ notifications: {
236
+ email: false,
237
+ push: false,
238
+ },
239
+ });
240
+ });
241
+ });
242
+ });