@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.
- package/README.md +2 -2
- package/dist/api/ast-schema-editor.d.ts +21 -0
- package/dist/api/collections_for_test/callbacks_test_collection.d.ts +2 -0
- package/dist/api/errors.d.ts +53 -0
- package/dist/api/index.d.ts +7 -0
- package/dist/api/logs-routes.d.ts +37 -0
- package/dist/api/openapi-generator.d.ts +16 -0
- package/dist/api/rest/api-generator.d.ts +68 -0
- package/{src/api/rest/index.ts → dist/api/rest/index.d.ts} +0 -1
- package/dist/api/rest/query-parser.d.ts +6 -0
- package/dist/api/schema-editor-routes.d.ts +3 -0
- package/dist/api/types.d.ts +56 -0
- package/dist/auth/adapter-middleware.d.ts +43 -0
- package/dist/auth/admin-roles-route.d.ts +18 -0
- package/dist/auth/admin-user-ops.d.ts +79 -0
- package/dist/auth/admin-users-route.d.ts +28 -0
- package/dist/auth/api-keys/api-key-middleware.d.ts +39 -0
- package/{src/auth/api-keys/api-key-permission-guard.ts → dist/auth/api-keys/api-key-permission-guard.d.ts} +2 -34
- package/dist/auth/api-keys/api-key-routes.d.ts +20 -0
- package/dist/auth/api-keys/api-key-store.d.ts +35 -0
- package/{src/auth/api-keys/api-key-types.ts → dist/auth/api-keys/api-key-types.d.ts} +0 -6
- package/{src/auth/api-keys/index.ts → dist/auth/api-keys/index.d.ts} +2 -22
- package/dist/auth/apple-oauth.d.ts +30 -0
- package/{src/auth/auth-hooks.ts → dist/auth/auth-hooks.d.ts} +15 -73
- package/dist/auth/bitbucket-oauth.d.ts +11 -0
- package/dist/auth/builtin-auth-adapter.d.ts +55 -0
- package/dist/auth/cookie-utils.d.ts +32 -0
- package/{src/auth/crypto-utils.ts → dist/auth/crypto-utils.d.ts} +1 -16
- package/dist/auth/custom-auth-adapter.d.ts +39 -0
- package/dist/auth/discord-oauth.d.ts +14 -0
- package/dist/auth/facebook-oauth.d.ts +14 -0
- package/dist/auth/github-oauth.d.ts +15 -0
- package/dist/auth/gitlab-oauth.d.ts +13 -0
- package/dist/auth/google-oauth.d.ts +47 -0
- package/{src/auth/index.ts → dist/auth/index.d.ts} +4 -22
- package/{src/auth/interfaces.ts → dist/auth/interfaces.d.ts} +20 -89
- package/dist/auth/jwt.d.ts +67 -0
- package/dist/auth/linkedin-oauth.d.ts +18 -0
- package/dist/auth/magic-link-routes.d.ts +32 -0
- package/dist/auth/mfa-crypto.d.ts +23 -0
- package/dist/auth/mfa-routes.d.ts +7 -0
- package/dist/auth/mfa.d.ts +49 -0
- package/dist/auth/microsoft-oauth.d.ts +16 -0
- package/dist/auth/middleware.d.ts +158 -0
- package/dist/auth/password.d.ts +22 -0
- package/dist/auth/rate-limiter.d.ts +50 -0
- package/dist/auth/reset-password-admin.d.ts +29 -0
- package/dist/auth/rls-scope.d.ts +31 -0
- package/dist/auth/routes.d.ts +66 -0
- package/dist/auth/session-routes.d.ts +29 -0
- package/dist/auth/slack-oauth.d.ts +12 -0
- package/dist/auth/spotify-oauth.d.ts +12 -0
- package/dist/auth/twitter-oauth.d.ts +18 -0
- package/dist/collections/BackendCollectionRegistry.d.ts +13 -0
- package/dist/collections/loader.d.ts +5 -0
- package/dist/cron/cron-loader.d.ts +18 -0
- package/dist/cron/cron-routes.d.ts +14 -0
- package/dist/cron/cron-scheduler.d.ts +106 -0
- package/dist/cron/cron-store.d.ts +32 -0
- package/dist/cron/define-cron.d.ts +28 -0
- package/{src/cron/index.ts → dist/cron/index.d.ts} +1 -0
- package/dist/db/interfaces.d.ts +18 -0
- package/dist/dynamic-import-Dvh-K5fl.js +22 -0
- package/dist/dynamic-import-Dvh-K5fl.js.map +1 -0
- package/dist/email/index.d.ts +6 -0
- package/dist/email/smtp-email-service.d.ts +30 -0
- package/dist/email/templates.d.ts +50 -0
- package/{src/email/types.ts → dist/email/types.d.ts} +40 -36
- package/dist/env.d.ts +102 -0
- package/dist/functions/define-function.d.ts +55 -0
- package/dist/functions/function-loader.d.ts +18 -0
- package/dist/functions/function-routes.d.ts +10 -0
- package/{src/functions/index.ts → dist/functions/index.d.ts} +2 -0
- package/dist/history/history-routes.d.ts +23 -0
- package/{src/history/index.ts → dist/history/index.d.ts} +0 -1
- package/dist/index.d.ts +35 -0
- package/dist/index.es.js +18184 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +48629 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/init/docs.d.ts +4 -0
- package/dist/init/health.d.ts +2 -0
- package/dist/init/middlewares.d.ts +10 -0
- package/dist/init/shutdown.d.ts +65 -0
- package/dist/init/storage.d.ts +5 -0
- package/dist/init.d.ts +344 -0
- package/dist/jwt-BwIn8xmk.js +4200 -0
- package/dist/jwt-BwIn8xmk.js.map +1 -0
- package/dist/logger-BYU66ENZ.js +94 -0
- package/dist/logger-BYU66ENZ.js.map +1 -0
- package/dist/ms-DnYXB-Wd.js +162 -0
- package/dist/ms-DnYXB-Wd.js.map +1 -0
- package/dist/openapi-generator-Z9oYWLf_.js +586 -0
- package/dist/openapi-generator-Z9oYWLf_.js.map +1 -0
- package/dist/schema-editor-routes-D3ef8zu1.js +219 -0
- package/dist/schema-editor-routes-D3ef8zu1.js.map +1 -0
- package/dist/serve-spa.d.ts +36 -0
- package/dist/services/driver-registry.d.ts +78 -0
- package/dist/services/routed-realtime-service.d.ts +43 -0
- package/dist/services/webhook-service.d.ts +29 -0
- package/dist/singleton.d.ts +51 -0
- package/dist/src-B4OLmNVa.js +437 -0
- package/dist/src-B4OLmNVa.js.map +1 -0
- package/dist/src-B5WkP1Rv.js +24778 -0
- package/dist/src-B5WkP1Rv.js.map +1 -0
- package/dist/storage/GCSStorageController.d.ts +43 -0
- package/dist/storage/LocalStorageController.d.ts +46 -0
- package/dist/storage/S3StorageController.d.ts +40 -0
- package/dist/storage/image-transform.d.ts +50 -0
- package/{src/storage/index.ts → dist/storage/index.d.ts} +6 -21
- package/dist/storage/routes.d.ts +70 -0
- package/dist/storage/storage-registry.d.ts +78 -0
- package/dist/storage/tus-handler.d.ts +53 -0
- package/{src/storage/types.ts → dist/storage/types.d.ts} +32 -38
- package/dist/types/index.d.ts +11 -0
- package/dist/utils/dev-port.d.ts +45 -0
- package/dist/utils/dynamic-import.d.ts +25 -0
- package/dist/utils/logger.d.ts +31 -0
- package/dist/utils/logging.d.ts +9 -0
- package/dist/utils/request-id.d.ts +4 -0
- package/dist/utils/request-logger.d.ts +19 -0
- package/{src/utils/sql.ts → dist/utils/sql.d.ts} +5 -16
- package/package.json +49 -22
- package/coverage/clover.xml +0 -3739
- package/coverage/coverage-final.json +0 -31
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -266
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov-report/src/api/ast-schema-editor.ts.html +0 -952
- package/coverage/lcov-report/src/api/errors.ts.html +0 -472
- package/coverage/lcov-report/src/api/graphql/graphql-schema-generator.ts.html +0 -1069
- package/coverage/lcov-report/src/api/graphql/index.html +0 -116
- package/coverage/lcov-report/src/api/index.html +0 -176
- package/coverage/lcov-report/src/api/openapi-generator.ts.html +0 -565
- package/coverage/lcov-report/src/api/rest/api-generator.ts.html +0 -994
- package/coverage/lcov-report/src/api/rest/index.html +0 -131
- package/coverage/lcov-report/src/api/rest/query-parser.ts.html +0 -550
- package/coverage/lcov-report/src/api/schema-editor-routes.ts.html +0 -202
- package/coverage/lcov-report/src/api/server.ts.html +0 -823
- package/coverage/lcov-report/src/auth/admin-routes.ts.html +0 -973
- package/coverage/lcov-report/src/auth/index.html +0 -176
- package/coverage/lcov-report/src/auth/jwt.ts.html +0 -574
- package/coverage/lcov-report/src/auth/middleware.ts.html +0 -745
- package/coverage/lcov-report/src/auth/password.ts.html +0 -310
- package/coverage/lcov-report/src/auth/services.ts.html +0 -2074
- package/coverage/lcov-report/src/collections/index.html +0 -116
- package/coverage/lcov-report/src/collections/loader.ts.html +0 -232
- package/coverage/lcov-report/src/db/auth-schema.ts.html +0 -523
- package/coverage/lcov-report/src/db/data-transformer.ts.html +0 -1753
- package/coverage/lcov-report/src/db/entityService.ts.html +0 -700
- package/coverage/lcov-report/src/db/index.html +0 -146
- package/coverage/lcov-report/src/db/services/EntityFetchService.ts.html +0 -4048
- package/coverage/lcov-report/src/db/services/EntityPersistService.ts.html +0 -883
- package/coverage/lcov-report/src/db/services/RelationService.ts.html +0 -3121
- package/coverage/lcov-report/src/db/services/entity-helpers.ts.html +0 -442
- package/coverage/lcov-report/src/db/services/index.html +0 -176
- package/coverage/lcov-report/src/db/services/index.ts.html +0 -124
- package/coverage/lcov-report/src/generate-drizzle-schema-logic.ts.html +0 -1960
- package/coverage/lcov-report/src/index.html +0 -116
- package/coverage/lcov-report/src/services/driver-registry.ts.html +0 -631
- package/coverage/lcov-report/src/services/index.html +0 -131
- package/coverage/lcov-report/src/services/postgresDataDriver.ts.html +0 -3025
- package/coverage/lcov-report/src/storage/LocalStorageController.ts.html +0 -1189
- package/coverage/lcov-report/src/storage/S3StorageController.ts.html +0 -970
- package/coverage/lcov-report/src/storage/index.html +0 -161
- package/coverage/lcov-report/src/storage/storage-registry.ts.html +0 -646
- package/coverage/lcov-report/src/storage/types.ts.html +0 -451
- package/coverage/lcov-report/src/utils/drizzle-conditions.ts.html +0 -3082
- package/coverage/lcov-report/src/utils/index.html +0 -116
- package/coverage/lcov.info +0 -7179
- package/jest.config.cjs +0 -19
- package/src/api/ast-schema-editor.ts +0 -289
- package/src/api/collections_for_test/callbacks_test_collection.ts +0 -61
- package/src/api/errors.ts +0 -278
- package/src/api/graphql/graphql-schema-generator.ts +0 -383
- package/src/api/graphql/index.ts +0 -2
- package/src/api/index.ts +0 -11
- package/src/api/logs-routes.ts +0 -132
- package/src/api/openapi-generator.ts +0 -727
- package/src/api/rest/api-generator-count.test.ts +0 -126
- package/src/api/rest/api-generator.ts +0 -747
- package/src/api/rest/query-parser.ts +0 -300
- package/src/api/schema-editor-routes.ts +0 -41
- package/src/api/server.ts +0 -271
- package/src/api/types.ts +0 -104
- package/src/auth/adapter-middleware.ts +0 -114
- package/src/auth/admin-roles-route.ts +0 -36
- package/src/auth/admin-user-ops.ts +0 -236
- package/src/auth/admin-users-route.ts +0 -302
- package/src/auth/api-keys/api-key-middleware.ts +0 -133
- package/src/auth/api-keys/api-key-routes.ts +0 -193
- package/src/auth/api-keys/api-key-store.ts +0 -340
- package/src/auth/apple-oauth.ts +0 -122
- package/src/auth/bitbucket-oauth.ts +0 -84
- package/src/auth/builtin-auth-adapter.ts +0 -380
- package/src/auth/custom-auth-adapter.ts +0 -86
- package/src/auth/discord-oauth.ts +0 -85
- package/src/auth/facebook-oauth.ts +0 -74
- package/src/auth/github-oauth.ts +0 -112
- package/src/auth/gitlab-oauth.ts +0 -72
- package/src/auth/google-oauth.ts +0 -223
- package/src/auth/jwt.ts +0 -208
- package/src/auth/linkedin-oauth.ts +0 -83
- package/src/auth/magic-link-routes.ts +0 -167
- package/src/auth/mfa-crypto.ts +0 -91
- package/src/auth/mfa-routes.ts +0 -323
- package/src/auth/mfa.ts +0 -161
- package/src/auth/microsoft-oauth.ts +0 -90
- package/src/auth/middleware.ts +0 -369
- package/src/auth/password.ts +0 -77
- package/src/auth/rate-limiter.ts +0 -225
- package/src/auth/reset-password-admin.ts +0 -160
- package/src/auth/rls-scope.ts +0 -58
- package/src/auth/routes.ts +0 -802
- package/src/auth/session-routes.ts +0 -353
- package/src/auth/slack-oauth.ts +0 -73
- package/src/auth/spotify-oauth.ts +0 -69
- package/src/auth/twitter-oauth.ts +0 -128
- package/src/collections/BackendCollectionRegistry.ts +0 -20
- package/src/collections/loader.ts +0 -50
- package/src/cron/cron-loader.ts +0 -84
- package/src/cron/cron-routes.test.ts +0 -265
- package/src/cron/cron-routes.ts +0 -85
- package/src/cron/cron-scheduler.test.ts +0 -559
- package/src/cron/cron-scheduler.ts +0 -581
- package/src/cron/cron-store.ts +0 -166
- package/src/db/interfaces.ts +0 -60
- package/src/email/index.ts +0 -19
- package/src/email/smtp-email-service.ts +0 -123
- package/src/email/templates.ts +0 -470
- package/src/env.ts +0 -227
- package/src/functions/function-loader.ts +0 -116
- package/src/functions/function-routes.ts +0 -31
- package/src/history/history-routes.ts +0 -129
- package/src/index.ts +0 -70
- package/src/init/docs.ts +0 -47
- package/src/init/health.ts +0 -37
- package/src/init/middlewares.ts +0 -61
- package/src/init/shutdown.ts +0 -56
- package/src/init/storage.ts +0 -57
- package/src/init.ts +0 -1041
- package/src/serve-spa.ts +0 -89
- package/src/services/driver-registry.ts +0 -183
- package/src/services/routed-realtime-service.ts +0 -113
- package/src/services/webhook-service.ts +0 -155
- package/src/singleton.ts +0 -70
- package/src/storage/LocalStorageController.ts +0 -389
- package/src/storage/S3StorageController.ts +0 -300
- package/src/storage/image-transform.ts +0 -218
- package/src/storage/routes.ts +0 -368
- package/src/storage/storage-registry.ts +0 -188
- package/src/storage/tus-handler.ts +0 -315
- package/src/types/index.ts +0 -27
- package/src/utils/dev-port.ts +0 -196
- package/src/utils/logger.ts +0 -143
- package/src/utils/logging.ts +0 -40
- package/src/utils/request-id.ts +0 -40
- package/src/utils/request-logger.ts +0 -72
- package/test/api-generator.test.ts +0 -588
- package/test/api-key-permission-guard.test.ts +0 -144
- package/test/ast-schema-editor.test.ts +0 -89
- package/test/auth-middleware-hono.test.ts +0 -556
- package/test/auth-routes.test.ts +0 -1098
- package/test/backend-hooks-data.test.ts +0 -477
- package/test/custom-auth-adapter.test.ts +0 -189
- package/test/driver-registry.test.ts +0 -282
- package/test/email-templates.test.ts +0 -174
- package/test/env.test.ts +0 -128
- package/test/error-propagation.test.ts +0 -226
- package/test/errors-hono.test.ts +0 -133
- package/test/errors.test.ts +0 -155
- package/test/function-loader.test.ts +0 -127
- package/test/graphql-schema-generator.test.ts +0 -554
- package/test/jwt-security.test.ts +0 -182
- package/test/jwt.test.ts +0 -326
- package/test/mfa.test.ts +0 -197
- package/test/middleware.test.ts +0 -305
- package/test/multi-datasource-routing.test.ts +0 -113
- package/test/password.test.ts +0 -165
- package/test/query-parser.test.ts +0 -233
- package/test/rate-limiter.test.ts +0 -102
- package/test/reset-password-admin.test.ts +0 -113
- package/test/routed-realtime-service.test.ts +0 -86
- package/test/safe-compare.test.ts +0 -66
- package/test/singleton.test.ts +0 -59
- package/test/smtp-email-service.test.ts +0 -169
- package/test/storage-local.test.ts +0 -271
- package/test/storage-registry.test.ts +0 -282
- package/test/storage-routes.test.ts +0 -222
- package/test/storage-s3.test.ts +0 -304
- package/test/transform-auth-response.test.ts +0 -305
- package/test/webhook-service.test.ts +0 -260
- package/tsconfig.json +0 -54
- package/tsconfig.prod.json +0 -23
- 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
|
-
});
|