@rebasepro/server-core 0.7.0 → 0.9.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 (300) hide show
  1. package/README.md +2 -2
  2. package/dist/api/ast-schema-editor.d.ts +21 -0
  3. package/dist/api/collections_for_test/callbacks_test_collection.d.ts +2 -0
  4. package/dist/api/errors.d.ts +53 -0
  5. package/dist/api/index.d.ts +7 -0
  6. package/dist/api/logs-routes.d.ts +37 -0
  7. package/dist/api/openapi-generator.d.ts +16 -0
  8. package/dist/api/rest/api-generator.d.ts +68 -0
  9. package/{src/api/rest/index.ts → dist/api/rest/index.d.ts} +0 -1
  10. package/dist/api/rest/query-parser.d.ts +6 -0
  11. package/dist/api/schema-editor-routes.d.ts +3 -0
  12. package/dist/api/types.d.ts +56 -0
  13. package/dist/auth/adapter-middleware.d.ts +43 -0
  14. package/dist/auth/admin-roles-route.d.ts +18 -0
  15. package/dist/auth/admin-user-ops.d.ts +79 -0
  16. package/dist/auth/admin-users-route.d.ts +28 -0
  17. package/dist/auth/api-keys/api-key-middleware.d.ts +39 -0
  18. package/{src/auth/api-keys/api-key-permission-guard.ts → dist/auth/api-keys/api-key-permission-guard.d.ts} +2 -34
  19. package/dist/auth/api-keys/api-key-routes.d.ts +20 -0
  20. package/dist/auth/api-keys/api-key-store.d.ts +35 -0
  21. package/{src/auth/api-keys/api-key-types.ts → dist/auth/api-keys/api-key-types.d.ts} +0 -6
  22. package/{src/auth/api-keys/index.ts → dist/auth/api-keys/index.d.ts} +2 -22
  23. package/dist/auth/apple-oauth.d.ts +30 -0
  24. package/{src/auth/auth-hooks.ts → dist/auth/auth-hooks.d.ts} +15 -73
  25. package/dist/auth/bitbucket-oauth.d.ts +11 -0
  26. package/dist/auth/builtin-auth-adapter.d.ts +55 -0
  27. package/dist/auth/cookie-utils.d.ts +32 -0
  28. package/{src/auth/crypto-utils.ts → dist/auth/crypto-utils.d.ts} +1 -16
  29. package/dist/auth/custom-auth-adapter.d.ts +39 -0
  30. package/dist/auth/discord-oauth.d.ts +14 -0
  31. package/dist/auth/facebook-oauth.d.ts +14 -0
  32. package/dist/auth/github-oauth.d.ts +15 -0
  33. package/dist/auth/gitlab-oauth.d.ts +13 -0
  34. package/dist/auth/google-oauth.d.ts +47 -0
  35. package/{src/auth/index.ts → dist/auth/index.d.ts} +4 -22
  36. package/{src/auth/interfaces.ts → dist/auth/interfaces.d.ts} +20 -89
  37. package/dist/auth/jwt.d.ts +67 -0
  38. package/dist/auth/linkedin-oauth.d.ts +18 -0
  39. package/dist/auth/magic-link-routes.d.ts +32 -0
  40. package/dist/auth/mfa-crypto.d.ts +23 -0
  41. package/dist/auth/mfa-routes.d.ts +7 -0
  42. package/dist/auth/mfa.d.ts +49 -0
  43. package/dist/auth/microsoft-oauth.d.ts +16 -0
  44. package/dist/auth/middleware.d.ts +158 -0
  45. package/dist/auth/password.d.ts +22 -0
  46. package/dist/auth/rate-limiter.d.ts +50 -0
  47. package/dist/auth/reset-password-admin.d.ts +29 -0
  48. package/dist/auth/rls-scope.d.ts +31 -0
  49. package/dist/auth/routes.d.ts +66 -0
  50. package/dist/auth/session-routes.d.ts +29 -0
  51. package/dist/auth/slack-oauth.d.ts +12 -0
  52. package/dist/auth/spotify-oauth.d.ts +12 -0
  53. package/dist/auth/twitter-oauth.d.ts +18 -0
  54. package/dist/collections/BackendCollectionRegistry.d.ts +13 -0
  55. package/dist/collections/loader.d.ts +5 -0
  56. package/dist/cron/cron-loader.d.ts +18 -0
  57. package/dist/cron/cron-routes.d.ts +14 -0
  58. package/dist/cron/cron-scheduler.d.ts +106 -0
  59. package/dist/cron/cron-store.d.ts +32 -0
  60. package/dist/cron/define-cron.d.ts +28 -0
  61. package/{src/cron/index.ts → dist/cron/index.d.ts} +1 -0
  62. package/dist/db/interfaces.d.ts +18 -0
  63. package/dist/dynamic-import-Dvh-K5fl.js +22 -0
  64. package/dist/dynamic-import-Dvh-K5fl.js.map +1 -0
  65. package/dist/email/index.d.ts +6 -0
  66. package/dist/email/smtp-email-service.d.ts +30 -0
  67. package/dist/email/templates.d.ts +50 -0
  68. package/{src/email/types.ts → dist/email/types.d.ts} +40 -36
  69. package/dist/env.d.ts +102 -0
  70. package/dist/functions/define-function.d.ts +55 -0
  71. package/dist/functions/function-loader.d.ts +18 -0
  72. package/dist/functions/function-routes.d.ts +10 -0
  73. package/{src/functions/index.ts → dist/functions/index.d.ts} +2 -0
  74. package/dist/history/history-routes.d.ts +23 -0
  75. package/{src/history/index.ts → dist/history/index.d.ts} +0 -1
  76. package/dist/index.d.ts +35 -0
  77. package/dist/index.es.js +18184 -0
  78. package/dist/index.es.js.map +1 -0
  79. package/dist/index.umd.js +48629 -0
  80. package/dist/index.umd.js.map +1 -0
  81. package/dist/init/docs.d.ts +4 -0
  82. package/dist/init/health.d.ts +2 -0
  83. package/dist/init/middlewares.d.ts +10 -0
  84. package/dist/init/shutdown.d.ts +65 -0
  85. package/dist/init/storage.d.ts +5 -0
  86. package/dist/init.d.ts +344 -0
  87. package/dist/jwt-BwIn8xmk.js +4200 -0
  88. package/dist/jwt-BwIn8xmk.js.map +1 -0
  89. package/dist/logger-BYU66ENZ.js +94 -0
  90. package/dist/logger-BYU66ENZ.js.map +1 -0
  91. package/dist/ms-DnYXB-Wd.js +162 -0
  92. package/dist/ms-DnYXB-Wd.js.map +1 -0
  93. package/dist/openapi-generator-Z9oYWLf_.js +586 -0
  94. package/dist/openapi-generator-Z9oYWLf_.js.map +1 -0
  95. package/dist/schema-editor-routes-D3ef8zu1.js +219 -0
  96. package/dist/schema-editor-routes-D3ef8zu1.js.map +1 -0
  97. package/dist/serve-spa.d.ts +36 -0
  98. package/dist/services/driver-registry.d.ts +78 -0
  99. package/dist/services/routed-realtime-service.d.ts +43 -0
  100. package/dist/services/webhook-service.d.ts +29 -0
  101. package/dist/singleton.d.ts +51 -0
  102. package/dist/src-B4OLmNVa.js +437 -0
  103. package/dist/src-B4OLmNVa.js.map +1 -0
  104. package/dist/src-B5WkP1Rv.js +24778 -0
  105. package/dist/src-B5WkP1Rv.js.map +1 -0
  106. package/dist/storage/GCSStorageController.d.ts +43 -0
  107. package/dist/storage/LocalStorageController.d.ts +46 -0
  108. package/dist/storage/S3StorageController.d.ts +40 -0
  109. package/dist/storage/image-transform.d.ts +50 -0
  110. package/{src/storage/index.ts → dist/storage/index.d.ts} +6 -21
  111. package/dist/storage/routes.d.ts +70 -0
  112. package/dist/storage/storage-registry.d.ts +78 -0
  113. package/dist/storage/tus-handler.d.ts +53 -0
  114. package/{src/storage/types.ts → dist/storage/types.d.ts} +32 -38
  115. package/dist/types/index.d.ts +11 -0
  116. package/dist/utils/dev-port.d.ts +45 -0
  117. package/dist/utils/dynamic-import.d.ts +25 -0
  118. package/dist/utils/logger.d.ts +31 -0
  119. package/dist/utils/logging.d.ts +9 -0
  120. package/dist/utils/request-id.d.ts +4 -0
  121. package/dist/utils/request-logger.d.ts +19 -0
  122. package/{src/utils/sql.ts → dist/utils/sql.d.ts} +5 -16
  123. package/package.json +49 -22
  124. package/coverage/clover.xml +0 -3739
  125. package/coverage/coverage-final.json +0 -31
  126. package/coverage/lcov-report/base.css +0 -224
  127. package/coverage/lcov-report/block-navigation.js +0 -87
  128. package/coverage/lcov-report/favicon.png +0 -0
  129. package/coverage/lcov-report/index.html +0 -266
  130. package/coverage/lcov-report/prettify.css +0 -1
  131. package/coverage/lcov-report/prettify.js +0 -2
  132. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  133. package/coverage/lcov-report/sorter.js +0 -210
  134. package/coverage/lcov-report/src/api/ast-schema-editor.ts.html +0 -952
  135. package/coverage/lcov-report/src/api/errors.ts.html +0 -472
  136. package/coverage/lcov-report/src/api/graphql/graphql-schema-generator.ts.html +0 -1069
  137. package/coverage/lcov-report/src/api/graphql/index.html +0 -116
  138. package/coverage/lcov-report/src/api/index.html +0 -176
  139. package/coverage/lcov-report/src/api/openapi-generator.ts.html +0 -565
  140. package/coverage/lcov-report/src/api/rest/api-generator.ts.html +0 -994
  141. package/coverage/lcov-report/src/api/rest/index.html +0 -131
  142. package/coverage/lcov-report/src/api/rest/query-parser.ts.html +0 -550
  143. package/coverage/lcov-report/src/api/schema-editor-routes.ts.html +0 -202
  144. package/coverage/lcov-report/src/api/server.ts.html +0 -823
  145. package/coverage/lcov-report/src/auth/admin-routes.ts.html +0 -973
  146. package/coverage/lcov-report/src/auth/index.html +0 -176
  147. package/coverage/lcov-report/src/auth/jwt.ts.html +0 -574
  148. package/coverage/lcov-report/src/auth/middleware.ts.html +0 -745
  149. package/coverage/lcov-report/src/auth/password.ts.html +0 -310
  150. package/coverage/lcov-report/src/auth/services.ts.html +0 -2074
  151. package/coverage/lcov-report/src/collections/index.html +0 -116
  152. package/coverage/lcov-report/src/collections/loader.ts.html +0 -232
  153. package/coverage/lcov-report/src/db/auth-schema.ts.html +0 -523
  154. package/coverage/lcov-report/src/db/data-transformer.ts.html +0 -1753
  155. package/coverage/lcov-report/src/db/entityService.ts.html +0 -700
  156. package/coverage/lcov-report/src/db/index.html +0 -146
  157. package/coverage/lcov-report/src/db/services/EntityFetchService.ts.html +0 -4048
  158. package/coverage/lcov-report/src/db/services/EntityPersistService.ts.html +0 -883
  159. package/coverage/lcov-report/src/db/services/RelationService.ts.html +0 -3121
  160. package/coverage/lcov-report/src/db/services/entity-helpers.ts.html +0 -442
  161. package/coverage/lcov-report/src/db/services/index.html +0 -176
  162. package/coverage/lcov-report/src/db/services/index.ts.html +0 -124
  163. package/coverage/lcov-report/src/generate-drizzle-schema-logic.ts.html +0 -1960
  164. package/coverage/lcov-report/src/index.html +0 -116
  165. package/coverage/lcov-report/src/services/driver-registry.ts.html +0 -631
  166. package/coverage/lcov-report/src/services/index.html +0 -131
  167. package/coverage/lcov-report/src/services/postgresDataDriver.ts.html +0 -3025
  168. package/coverage/lcov-report/src/storage/LocalStorageController.ts.html +0 -1189
  169. package/coverage/lcov-report/src/storage/S3StorageController.ts.html +0 -970
  170. package/coverage/lcov-report/src/storage/index.html +0 -161
  171. package/coverage/lcov-report/src/storage/storage-registry.ts.html +0 -646
  172. package/coverage/lcov-report/src/storage/types.ts.html +0 -451
  173. package/coverage/lcov-report/src/utils/drizzle-conditions.ts.html +0 -3082
  174. package/coverage/lcov-report/src/utils/index.html +0 -116
  175. package/coverage/lcov.info +0 -7179
  176. package/jest.config.cjs +0 -19
  177. package/src/api/ast-schema-editor.ts +0 -289
  178. package/src/api/collections_for_test/callbacks_test_collection.ts +0 -61
  179. package/src/api/errors.ts +0 -278
  180. package/src/api/graphql/graphql-schema-generator.ts +0 -383
  181. package/src/api/graphql/index.ts +0 -2
  182. package/src/api/index.ts +0 -11
  183. package/src/api/logs-routes.ts +0 -132
  184. package/src/api/openapi-generator.ts +0 -727
  185. package/src/api/rest/api-generator-count.test.ts +0 -126
  186. package/src/api/rest/api-generator.ts +0 -747
  187. package/src/api/rest/query-parser.ts +0 -300
  188. package/src/api/schema-editor-routes.ts +0 -41
  189. package/src/api/server.ts +0 -271
  190. package/src/api/types.ts +0 -104
  191. package/src/auth/adapter-middleware.ts +0 -114
  192. package/src/auth/admin-roles-route.ts +0 -36
  193. package/src/auth/admin-user-ops.ts +0 -236
  194. package/src/auth/admin-users-route.ts +0 -302
  195. package/src/auth/api-keys/api-key-middleware.ts +0 -133
  196. package/src/auth/api-keys/api-key-routes.ts +0 -193
  197. package/src/auth/api-keys/api-key-store.ts +0 -340
  198. package/src/auth/apple-oauth.ts +0 -122
  199. package/src/auth/bitbucket-oauth.ts +0 -84
  200. package/src/auth/builtin-auth-adapter.ts +0 -380
  201. package/src/auth/custom-auth-adapter.ts +0 -86
  202. package/src/auth/discord-oauth.ts +0 -85
  203. package/src/auth/facebook-oauth.ts +0 -74
  204. package/src/auth/github-oauth.ts +0 -112
  205. package/src/auth/gitlab-oauth.ts +0 -72
  206. package/src/auth/google-oauth.ts +0 -223
  207. package/src/auth/jwt.ts +0 -208
  208. package/src/auth/linkedin-oauth.ts +0 -83
  209. package/src/auth/magic-link-routes.ts +0 -167
  210. package/src/auth/mfa-crypto.ts +0 -91
  211. package/src/auth/mfa-routes.ts +0 -323
  212. package/src/auth/mfa.ts +0 -161
  213. package/src/auth/microsoft-oauth.ts +0 -90
  214. package/src/auth/middleware.ts +0 -369
  215. package/src/auth/password.ts +0 -77
  216. package/src/auth/rate-limiter.ts +0 -225
  217. package/src/auth/reset-password-admin.ts +0 -160
  218. package/src/auth/rls-scope.ts +0 -58
  219. package/src/auth/routes.ts +0 -802
  220. package/src/auth/session-routes.ts +0 -353
  221. package/src/auth/slack-oauth.ts +0 -73
  222. package/src/auth/spotify-oauth.ts +0 -69
  223. package/src/auth/twitter-oauth.ts +0 -128
  224. package/src/collections/BackendCollectionRegistry.ts +0 -20
  225. package/src/collections/loader.ts +0 -50
  226. package/src/cron/cron-loader.ts +0 -84
  227. package/src/cron/cron-routes.test.ts +0 -265
  228. package/src/cron/cron-routes.ts +0 -85
  229. package/src/cron/cron-scheduler.test.ts +0 -559
  230. package/src/cron/cron-scheduler.ts +0 -581
  231. package/src/cron/cron-store.ts +0 -166
  232. package/src/db/interfaces.ts +0 -60
  233. package/src/email/index.ts +0 -19
  234. package/src/email/smtp-email-service.ts +0 -123
  235. package/src/email/templates.ts +0 -470
  236. package/src/env.ts +0 -227
  237. package/src/functions/function-loader.ts +0 -116
  238. package/src/functions/function-routes.ts +0 -31
  239. package/src/history/history-routes.ts +0 -129
  240. package/src/index.ts +0 -70
  241. package/src/init/docs.ts +0 -47
  242. package/src/init/health.ts +0 -37
  243. package/src/init/middlewares.ts +0 -61
  244. package/src/init/shutdown.ts +0 -56
  245. package/src/init/storage.ts +0 -57
  246. package/src/init.ts +0 -1041
  247. package/src/serve-spa.ts +0 -89
  248. package/src/services/driver-registry.ts +0 -183
  249. package/src/services/routed-realtime-service.ts +0 -113
  250. package/src/services/webhook-service.ts +0 -155
  251. package/src/singleton.ts +0 -70
  252. package/src/storage/LocalStorageController.ts +0 -389
  253. package/src/storage/S3StorageController.ts +0 -300
  254. package/src/storage/image-transform.ts +0 -218
  255. package/src/storage/routes.ts +0 -368
  256. package/src/storage/storage-registry.ts +0 -188
  257. package/src/storage/tus-handler.ts +0 -315
  258. package/src/types/index.ts +0 -27
  259. package/src/utils/dev-port.ts +0 -196
  260. package/src/utils/logger.ts +0 -143
  261. package/src/utils/logging.ts +0 -40
  262. package/src/utils/request-id.ts +0 -40
  263. package/src/utils/request-logger.ts +0 -72
  264. package/test/api-generator.test.ts +0 -588
  265. package/test/api-key-permission-guard.test.ts +0 -144
  266. package/test/ast-schema-editor.test.ts +0 -89
  267. package/test/auth-middleware-hono.test.ts +0 -556
  268. package/test/auth-routes.test.ts +0 -1098
  269. package/test/backend-hooks-data.test.ts +0 -477
  270. package/test/custom-auth-adapter.test.ts +0 -189
  271. package/test/driver-registry.test.ts +0 -282
  272. package/test/email-templates.test.ts +0 -174
  273. package/test/env.test.ts +0 -128
  274. package/test/error-propagation.test.ts +0 -226
  275. package/test/errors-hono.test.ts +0 -133
  276. package/test/errors.test.ts +0 -155
  277. package/test/function-loader.test.ts +0 -127
  278. package/test/graphql-schema-generator.test.ts +0 -554
  279. package/test/jwt-security.test.ts +0 -182
  280. package/test/jwt.test.ts +0 -326
  281. package/test/mfa.test.ts +0 -197
  282. package/test/middleware.test.ts +0 -305
  283. package/test/multi-datasource-routing.test.ts +0 -113
  284. package/test/password.test.ts +0 -165
  285. package/test/query-parser.test.ts +0 -233
  286. package/test/rate-limiter.test.ts +0 -102
  287. package/test/reset-password-admin.test.ts +0 -113
  288. package/test/routed-realtime-service.test.ts +0 -86
  289. package/test/safe-compare.test.ts +0 -66
  290. package/test/singleton.test.ts +0 -59
  291. package/test/smtp-email-service.test.ts +0 -169
  292. package/test/storage-local.test.ts +0 -271
  293. package/test/storage-registry.test.ts +0 -282
  294. package/test/storage-routes.test.ts +0 -222
  295. package/test/storage-s3.test.ts +0 -304
  296. package/test/transform-auth-response.test.ts +0 -305
  297. package/test/webhook-service.test.ts +0 -260
  298. package/tsconfig.json +0 -54
  299. package/tsconfig.prod.json +0 -23
  300. package/vite.config.ts +0 -86
package/src/serve-spa.ts DELETED
@@ -1,89 +0,0 @@
1
- import { Hono } from "hono";
2
- import { serveStatic } from "@hono/node-server/serve-static";
3
- import * as path from "path";
4
- import * as fs from "fs";
5
- import fsp from "node:fs/promises";
6
- import { logger } from "./utils/logger.js";
7
-
8
- /**
9
- * Configuration for serving a Single Page Application
10
- */
11
- export interface ServeSPAConfig {
12
- /**
13
- * Absolute path to the frontend build directory
14
- * @example path.join(__dirname, "../../frontend/dist")
15
- */
16
- frontendPath: string;
17
-
18
- /**
19
- * Base path for API routes (default: "/api")
20
- * Requests to this path will be passed through to API handlers
21
- */
22
- apiBasePath?: string;
23
-
24
- /**
25
- * Additional paths to exclude from SPA handling
26
- * These paths will be passed through to other handlers
27
- * @example ["/health", "/ws", "/metrics"]
28
- */
29
- excludePaths?: string[];
30
-
31
- /**
32
- * Index file to serve for SPA routes (default: "index.html")
33
- */
34
- indexFile?: string;
35
- }
36
-
37
- /**
38
- * Serve a Single Page Application from an Hono app.
39
- */
40
- export function serveSPA<E extends import("hono").Env>(app: Hono<E>, config: ServeSPAConfig): void {
41
- const {
42
- frontendPath,
43
- apiBasePath = "/api",
44
- excludePaths = [],
45
- indexFile = "index.html"
46
- } = config;
47
-
48
- // Validate frontend path exists
49
- if (!fs.existsSync(frontendPath)) {
50
- logger.warn(`⚠️ Frontend build path does not exist: ${frontendPath}`);
51
- logger.warn(" SPA serving is disabled. Build your frontend first.");
52
- return;
53
- }
54
-
55
- // Serve static files from frontend build
56
- app.use("/*", serveStatic({
57
- root: path.relative(process.cwd(), frontendPath)
58
- }));
59
-
60
- // Build list of paths to exclude from SPA handling
61
- const allExcludePaths = [apiBasePath, ...excludePaths];
62
-
63
- // Cache the index.html content to avoid re-reading from disk on every navigation request.
64
- let cachedHtml: string | null = null;
65
-
66
- // SPA fallback - serve index.html for all non-excluded routes
67
- app.get("*", async (c, next) => {
68
- // Skip excluded paths (API, health checks, etc.)
69
- if (allExcludePaths.some(p => c.req.path.startsWith(p))) {
70
- return next();
71
- }
72
-
73
- const indexPath = path.join(frontendPath, indexFile);
74
-
75
- if (!cachedHtml) {
76
- try {
77
- cachedHtml = await fsp.readFile(indexPath, "utf-8");
78
- } catch {
79
- logger.warn(`⚠️ Index file not found: ${indexPath}`);
80
- return next();
81
- }
82
- }
83
-
84
- return c.html(cachedHtml);
85
- });
86
-
87
- logger.info(`✅ SPA serving enabled from: ${frontendPath}`);
88
- }
89
-
@@ -1,183 +0,0 @@
1
- /**
2
- * Driver Registry
3
- *
4
- * Manages multiple driver delegates for Rebase backend.
5
- * Allows different databases for different collections.
6
- *
7
- * Usage:
8
- * - Single DB: Pass a single DataDriver → maps to "(default)"
9
- * - Multiple DBs: Pass a map of { dbId: DataDriver }
10
- * - Collections use `databaseId` property to specify which driver to use
11
- * - Collections without `databaseId` fallback to "(default)"
12
- */
13
-
14
- import { DataDriver } from "@rebasepro/types";
15
- import { logger } from "../utils/logger";
16
-
17
- /**
18
- * The default driver identifier used when:
19
- * - A single driver is provided (not a map)
20
- * - A collection doesn't specify a databaseId
21
- */
22
- export const DEFAULT_DRIVER_ID = "(default)";
23
-
24
- /**
25
- * Registry for managing multiple driver delegates
26
- */
27
- export interface DriverRegistry {
28
- /**
29
- * Register a driver delegate with an ID
30
- * @param id - Unique identifier for this driver (e.g., "analytics", "users")
31
- * @param delegate - The DataDriver instance
32
- */
33
- register(id: string, delegate: DataDriver): void;
34
-
35
- /**
36
- * Get the default driver delegate (id = "(default)")
37
- * @throws Error if no default driver is registered
38
- */
39
- getDefault(): DataDriver;
40
-
41
- /**
42
- * Get a driver delegate by ID
43
- * @param id - Driver identifier, or undefined/null for default
44
- * @returns The DataDriver, or undefined if not found
45
- */
46
- get(id: string | undefined | null): DataDriver | undefined;
47
-
48
- /**
49
- * Get a driver delegate by ID, with fallback to default
50
- * @param id - Driver identifier, or undefined/null for default
51
- * @returns The DataDriver (falls back to default if id not found)
52
- * @throws Error if neither the specified nor default driver exists
53
- */
54
- getOrDefault(id: string | undefined | null): DataDriver;
55
-
56
- /**
57
- * Check if a driver with the given ID exists
58
- */
59
- has(id: string): boolean;
60
-
61
- /**
62
- * List all registered driver IDs
63
- */
64
- list(): string[];
65
-
66
- /**
67
- * Get the number of registered drivers
68
- */
69
- size(): number;
70
- }
71
-
72
- /**
73
- * Default implementation of DriverRegistry
74
- */
75
- export class DefaultDriverRegistry implements DriverRegistry {
76
- private delegates = new Map<string, DataDriver>();
77
-
78
- /**
79
- * Create a DriverRegistry from either a single delegate or a map
80
- * @param input - Single DataDriver (maps to "(default)") or Record<string, DataDriver>
81
- */
82
- static create(
83
- input: DataDriver | Record<string, DataDriver>
84
- ): DefaultDriverRegistry {
85
- const registry = new DefaultDriverRegistry();
86
-
87
- if (isDataDriverDelegate(input)) {
88
- // Single delegate → register as "(default)"
89
- registry.register(DEFAULT_DRIVER_ID, input);
90
- } else {
91
- // Map of delegates → register each
92
- for (const [id, delegate] of Object.entries(input)) {
93
- registry.register(id, delegate);
94
- }
95
- // Ensure there's a default if not explicitly provided
96
- if (!registry.has(DEFAULT_DRIVER_ID) && registry.size() > 0) {
97
- // If no explicit "(default)", use the first one as default
98
- const firstId = Object.keys(input)[0];
99
- logger.warn(
100
- `[DriverRegistry] No "${DEFAULT_DRIVER_ID}" driver provided. ` +
101
- `Using "${firstId}" as the default.`
102
- );
103
- registry.register(DEFAULT_DRIVER_ID, input[firstId]);
104
- }
105
- }
106
-
107
- return registry;
108
- }
109
-
110
- register(id: string, delegate: DataDriver): void {
111
- if (this.delegates.has(id)) {
112
- logger.warn(`[DriverRegistry] Overwriting driver with id "${id}"`);
113
- }
114
- this.delegates.set(id, delegate);
115
- }
116
-
117
- getDefault(): DataDriver {
118
- const delegate = this.delegates.get(DEFAULT_DRIVER_ID);
119
- if (!delegate) {
120
- throw new Error(
121
- "[DriverRegistry] No default driver registered. " +
122
- `Register one with id "${DEFAULT_DRIVER_ID}" or pass a single DataDriver.`
123
- );
124
- }
125
- return delegate;
126
- }
127
-
128
- get(id: string | undefined | null): DataDriver | undefined {
129
- if (id === undefined || id === null) {
130
- return this.delegates.get(DEFAULT_DRIVER_ID);
131
- }
132
- return this.delegates.get(id);
133
- }
134
-
135
- getOrDefault(id: string | undefined | null): DataDriver {
136
- // If no ID specified, return default
137
- if (id === undefined || id === null) {
138
- return this.getDefault();
139
- }
140
-
141
- // Try to get by ID
142
- const delegate = this.delegates.get(id);
143
- if (delegate) {
144
- return delegate;
145
- }
146
-
147
- // Fallback to default with warning
148
- logger.warn(
149
- `[DriverRegistry] Driver "${id}" not found, falling back to "${DEFAULT_DRIVER_ID}"`
150
- );
151
- return this.getDefault();
152
- }
153
-
154
- has(id: string): boolean {
155
- return this.delegates.has(id);
156
- }
157
-
158
- list(): string[] {
159
- return Array.from(this.delegates.keys());
160
- }
161
-
162
- size(): number {
163
- return this.delegates.size;
164
- }
165
- }
166
-
167
- /**
168
- * Type guard to check if an object is a DataDriver
169
- */
170
- function isDataDriverDelegate(obj: unknown): obj is DataDriver {
171
- if (typeof obj !== "object" || obj === null) {
172
- return false;
173
- }
174
- const delegate = obj as DataDriver;
175
- // Check for required DataDriver properties
176
- return (
177
- typeof delegate.key === "string" &&
178
- typeof delegate.fetchCollection === "function" &&
179
- typeof delegate.fetchEntity === "function" &&
180
- typeof delegate.saveEntity === "function" &&
181
- typeof delegate.deleteEntity === "function"
182
- );
183
- }
@@ -1,113 +0,0 @@
1
- import { RealtimeProvider, Entity } from "@rebasepro/types";
2
-
3
- /**
4
- * A realtime client message as forwarded by the WebSocket server.
5
- */
6
- interface ClientMessage {
7
- type: string;
8
- payload?: Record<string, unknown>;
9
- subscriptionId?: string;
10
- }
11
-
12
- /**
13
- * The concrete realtime service surface the WebSocket server drives — the
14
- * typed {@link RealtimeProvider} plus the client-connection methods that
15
- * every engine's realtime service implements.
16
- */
17
- export interface WsRealtimeService extends RealtimeProvider {
18
- addClient(clientId: string, ws: unknown): void;
19
- handleClientMessage(clientId: string, message: ClientMessage, authContext?: unknown): Promise<void> | void;
20
- }
21
-
22
- /** Channel/presence/broadcast messages are engine-agnostic pub/sub. */
23
- const CHANNEL_MESSAGE_TYPES = new Set([
24
- "join_channel", "leave_channel", "broadcast",
25
- "presence_track", "presence_untrack", "presence_state"
26
- ]);
27
-
28
- export interface RoutedRealtimeOptions {
29
- /** Per-engine realtime providers, keyed by data-source key. */
30
- providers: Record<string, RealtimeProvider>;
31
- /** Key of the default provider (handles channels/presence/broadcast). */
32
- defaultKey: string;
33
- /** Resolve a collection path to its data-source key. */
34
- resolveKey: (collectionPath: string) => string;
35
- }
36
-
37
- /**
38
- * Compose multiple per-engine {@link RealtimeProvider}s into one that routes
39
- * each subscription to the provider owning the subscribed collection — the
40
- * realtime counterpart of `buildRoutedRebaseData`.
41
- *
42
- * The WebSocket server stays single and engine-agnostic; this composite is
43
- * passed in its place. Routing rules:
44
- * - `subscribe_collection` / `subscribe_entity` → the provider for the
45
- * collection's data source (by `payload.path`).
46
- * - `unsubscribe` → forwarded to all providers (a no-op on non-owners).
47
- * - channel / presence / broadcast → the default provider (these are global
48
- * pub/sub, not bound to an engine).
49
- * - `addClient` and lifecycle (`onServerReady`/`destroy`/`stopListening`) →
50
- * all providers (each registers its own ws close handler for cleanup).
51
- */
52
- export function createRoutedRealtimeService(opts: RoutedRealtimeOptions): WsRealtimeService {
53
- const { providers, defaultKey, resolveKey } = opts;
54
-
55
- const asWs = (p: RealtimeProvider): WsRealtimeService => p as unknown as WsRealtimeService;
56
- const all = (): WsRealtimeService[] => Object.values(providers).map(asWs);
57
- const fallback = (): WsRealtimeService => asWs(providers[defaultKey] ?? Object.values(providers)[0]);
58
- const forPath = (path?: string): WsRealtimeService => {
59
- if (!path) return fallback();
60
- const key = resolveKey(path);
61
- return asWs(providers[key] ?? providers[defaultKey] ?? Object.values(providers)[0]);
62
- };
63
-
64
- return {
65
- addClient(clientId, ws) {
66
- for (const p of all()) p.addClient?.(clientId, ws);
67
- },
68
-
69
- async handleClientMessage(clientId, message, authContext) {
70
- const { type } = message;
71
- if (type === "subscribe_collection" || type === "subscribe_entity") {
72
- await forPath(message.payload?.path as string | undefined)
73
- .handleClientMessage(clientId, message, authContext);
74
- return;
75
- }
76
- if (type === "unsubscribe") {
77
- // The owning provider acts; others no-op on an unknown id.
78
- await Promise.all(all().map((p) => p.handleClientMessage(clientId, message, authContext)));
79
- return;
80
- }
81
- // Channels/presence/broadcast (and anything else) → default provider.
82
- await fallback().handleClientMessage(clientId, message, authContext);
83
- },
84
-
85
- subscribeToCollection(subscriptionId, config, callback) {
86
- forPath((config as { path?: string }).path).subscribeToCollection(subscriptionId, config, callback);
87
- },
88
-
89
- subscribeToEntity(subscriptionId, config, callback) {
90
- forPath((config as { path?: string }).path).subscribeToEntity(subscriptionId, config, callback);
91
- },
92
-
93
- unsubscribe(subscriptionId) {
94
- for (const p of all()) p.unsubscribe(subscriptionId);
95
- },
96
-
97
- async notifyEntityUpdate(path: string, entityId: string, entity: Entity | null, databaseId?: string) {
98
- await forPath(path).notifyEntityUpdate(path, entityId, entity, databaseId);
99
- },
100
-
101
- onServerReady(serverInfo) {
102
- for (const p of all()) p.onServerReady?.(serverInfo);
103
- },
104
-
105
- async destroy() {
106
- await Promise.all(all().map((p) => p.destroy?.()));
107
- },
108
-
109
- async stopListening() {
110
- await Promise.all(all().map((p) => p.stopListening?.()));
111
- }
112
- };
113
- }
@@ -1,155 +0,0 @@
1
- import { createHmac, randomUUID } from "crypto";
2
-
3
- export interface WebhookConfig {
4
- id: string;
5
- url: string;
6
- secret?: string;
7
- headers?: Record<string, string>;
8
- events: string[];
9
- table: string;
10
- enabled: boolean;
11
- }
12
-
13
- export interface WebhookDeliveryResult {
14
- webhookId: string;
15
- event: string;
16
- payload: Record<string, unknown>;
17
- statusCode: number;
18
- responseBody: string;
19
- success: boolean;
20
- attemptNumber: number;
21
- }
22
-
23
- export class WebhookDispatcher {
24
- private webhooks: WebhookConfig[] = [];
25
- private maxRetries = 3;
26
- private retryDelays = [1000, 5000, 15000]; // Exponential backoff
27
-
28
- /** Register webhooks to watch */
29
- setWebhooks(webhooks: WebhookConfig[]): void {
30
- this.webhooks = webhooks.filter(w => w.enabled);
31
- }
32
-
33
- /** Called when an entity changes — checks if any webhook matches */
34
- async onEntityChange(
35
- table: string,
36
- event: "INSERT" | "UPDATE" | "DELETE",
37
- entityId: string,
38
- entity: Record<string, unknown> | null,
39
- previousEntity?: Record<string, unknown> | null
40
- ): Promise<WebhookDeliveryResult[]> {
41
- const matchingWebhooks = this.webhooks.filter(
42
- w => w.table === table && w.events.includes(event)
43
- );
44
-
45
- if (matchingWebhooks.length === 0) return [];
46
-
47
- const results: WebhookDeliveryResult[] = [];
48
-
49
- for (const webhook of matchingWebhooks) {
50
- const payload: Record<string, unknown> = {
51
- type: event,
52
- table,
53
- record: entity,
54
- old_record: event === "UPDATE" ? previousEntity : undefined,
55
- schema: "public",
56
- timestamp: new Date().toISOString()
57
- };
58
-
59
- const result = await this.deliverWithRetry(webhook, event, payload);
60
- results.push(result);
61
- }
62
-
63
- return results;
64
- }
65
-
66
- private async deliverWithRetry(
67
- webhook: WebhookConfig,
68
- event: string,
69
- payload: Record<string, unknown>
70
- ): Promise<WebhookDeliveryResult> {
71
- for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
72
- const result = await this.deliver(webhook, event, payload, attempt);
73
- if (result.success) return result;
74
-
75
- if (attempt < this.maxRetries) {
76
- await new Promise(r => setTimeout(r, this.retryDelays[attempt - 1]));
77
- } else {
78
- return result; // Final failure
79
- }
80
- }
81
-
82
- // Should never reach here, but satisfies TypeScript
83
- return {
84
- webhookId: webhook.id,
85
- event,
86
- payload,
87
- statusCode: 0,
88
- responseBody: "Max retries exceeded",
89
- success: false,
90
- attemptNumber: this.maxRetries
91
- };
92
- }
93
-
94
- private async deliver(
95
- webhook: WebhookConfig,
96
- event: string,
97
- payload: Record<string, unknown>,
98
- attemptNumber: number
99
- ): Promise<WebhookDeliveryResult> {
100
- const body = JSON.stringify(payload);
101
-
102
- const headers: Record<string, string> = {
103
- "Content-Type": "application/json",
104
- "X-Webhook-Id": webhook.id,
105
- "X-Webhook-Event": event,
106
- "X-Webhook-Delivery": randomUUID(),
107
- "X-Webhook-Attempt": String(attemptNumber),
108
- ...(webhook.headers || {})
109
- };
110
-
111
- // HMAC signature
112
- if (webhook.secret) {
113
- const signature = createHmac("sha256", webhook.secret).update(body).digest("hex");
114
- headers["X-Webhook-Signature"] = `sha256=${signature}`;
115
- }
116
-
117
- try {
118
- const controller = new AbortController();
119
- const timeout = setTimeout(() => controller.abort(), 10000); // 10s timeout
120
-
121
- const response = await fetch(webhook.url, {
122
- method: "POST",
123
- headers,
124
- body,
125
- signal: controller.signal
126
- });
127
-
128
- clearTimeout(timeout);
129
-
130
- const responseBody = await response.text().catch(() => "");
131
- const success = response.status >= 200 && response.status < 300;
132
-
133
- return {
134
- webhookId: webhook.id,
135
- event,
136
- payload,
137
- statusCode: response.status,
138
- responseBody: responseBody.slice(0, 1000), // Truncate
139
- success,
140
- attemptNumber
141
- };
142
- } catch (error: unknown) {
143
- const message = error instanceof Error ? error.message : String(error);
144
- return {
145
- webhookId: webhook.id,
146
- event,
147
- payload,
148
- statusCode: 0,
149
- responseBody: message.slice(0, 1000),
150
- success: false,
151
- attemptNumber
152
- };
153
- }
154
- }
155
- }
package/src/singleton.ts DELETED
@@ -1,70 +0,0 @@
1
- import type { RebaseClient } from "@rebasepro/types";
2
-
3
- let _instance: RebaseClient | null = null;
4
-
5
- /**
6
- * @internal Called once during server initialization to set the backing instance.
7
- * This is invoked by `initializeRebaseBackend()` — never call it manually.
8
- */
9
- export function _initRebase(client: RebaseClient): void {
10
- _instance = client;
11
- }
12
-
13
- /**
14
- * @internal Allows overriding the underlying instance for unit testing.
15
- * Throws an error if used in a non-test environment to prevent production abuse.
16
- */
17
- export function _setRebaseMock(mockInstance: Partial<RebaseClient>): void {
18
- if (process.env.NODE_ENV !== "test") {
19
- throw new Error("_setRebaseMock can only be called in a test environment (NODE_ENV=test).");
20
- }
21
- _instance = { ...(_instance || {} as RebaseClient),
22
- ...mockInstance } as RebaseClient;
23
- }
24
-
25
- /**
26
- * @internal Resets the singleton instance, useful for afterEach() in test suites.
27
- */
28
- export function _resetRebaseMock(): void {
29
- if (process.env.NODE_ENV !== "test") {
30
- throw new Error("_resetRebaseMock can only be called in a test environment.");
31
- }
32
- _instance = null;
33
- }
34
-
35
- /**
36
- * The server-side Rebase singleton.
37
- *
38
- * Initialized automatically during server startup. Provides access to all
39
- * app-scoped services: **data**, **auth**, **storage**, and **email**.
40
- *
41
- * `rebase.data` runs with **admin privileges** (no RLS). For user-scoped
42
- * queries inside request handlers, continue using the handler's context
43
- * or the RLS-scoped driver.
44
- *
45
- * @example
46
- * ```typescript
47
- * import { rebase } from "@rebasepro/server-core";
48
- *
49
- * // In a Hono handler, cron job, hook, or service file:
50
- * await rebase.email?.send({ to: "admin@co.com", subject: "Alert", html: "<p>Hi</p>" });
51
- * const jobs = await rebase.data.jobs.find({ limit: 10 });
52
- * ```
53
- */
54
- export const rebase: RebaseClient = new Proxy({} as RebaseClient, {
55
- get(_, prop) {
56
- if (!_instance) {
57
- throw new Error(
58
- `rebase.${String(prop)}: server not initialized yet. ` +
59
- "The singleton is available after Rebase starts — don't call it at import time."
60
- );
61
- }
62
- return _instance[prop as keyof RebaseClient];
63
- },
64
- set(_, prop) {
65
- throw new Error(
66
- `Cannot set rebase.${String(prop)} directly. ` +
67
- "The singleton is read-only. Use _initRebase() during server startup."
68
- );
69
- }
70
- });