@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
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "module";
|
|
2
|
+
import "process";
|
|
3
|
+
__createRequire(import.meta.url);
|
|
4
|
+
import { errorHandler } from "./index.es.js";
|
|
5
|
+
import * as fs$1 from "fs";
|
|
6
|
+
import * as path$1 from "path";
|
|
7
|
+
import { Hono } from "hono";
|
|
8
|
+
import { IndentationText, Project, SyntaxKind } from "ts-morph";
|
|
9
|
+
//#region src/api/ast-schema-editor.ts
|
|
10
|
+
var AstSchemaEditor = class {
|
|
11
|
+
project;
|
|
12
|
+
collectionsDir;
|
|
13
|
+
constructor(collectionsDir) {
|
|
14
|
+
this.project = new Project({ manipulationSettings: { indentationText: IndentationText.FourSpaces } });
|
|
15
|
+
if (fs$1.existsSync(collectionsDir)) this.project.addSourceFilesAtPaths(`${collectionsDir}/**/*.ts`);
|
|
16
|
+
this.collectionsDir = path$1.resolve(collectionsDir);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Sanitize collectionId to prevent path traversal attacks.
|
|
20
|
+
* Only allows alphanumeric characters, underscores, and hyphens.
|
|
21
|
+
*/
|
|
22
|
+
sanitizeCollectionId(collectionId) {
|
|
23
|
+
const sanitized = collectionId.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
24
|
+
if (!sanitized || sanitized !== collectionId) throw new Error(`Invalid collection ID: "${collectionId}". Only alphanumeric characters, underscores, and hyphens are allowed.`);
|
|
25
|
+
return sanitized;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolve a file path and ensure it falls within the collectionsDir.
|
|
29
|
+
*/
|
|
30
|
+
safePath(filename) {
|
|
31
|
+
const resolved = path$1.resolve(this.collectionsDir, filename);
|
|
32
|
+
if (!resolved.startsWith(this.collectionsDir + path$1.sep) && resolved !== this.collectionsDir) throw new Error("Path traversal detected: resolved path is outside the collections directory.");
|
|
33
|
+
return resolved;
|
|
34
|
+
}
|
|
35
|
+
getCollectionFile(collectionId) {
|
|
36
|
+
const safeId = this.sanitizeCollectionId(collectionId);
|
|
37
|
+
const filePath = this.safePath(`${safeId}.ts`);
|
|
38
|
+
let file = this.project.getSourceFile(filePath);
|
|
39
|
+
if (!file && fs$1.existsSync(filePath)) {
|
|
40
|
+
this.project.addSourceFilesAtPaths(`${this.collectionsDir}/**/*.ts`);
|
|
41
|
+
file = this.project.getSourceFile(filePath);
|
|
42
|
+
}
|
|
43
|
+
return file;
|
|
44
|
+
}
|
|
45
|
+
getCollectionObject(collectionId) {
|
|
46
|
+
const file = this.getCollectionFile(collectionId);
|
|
47
|
+
if (!file) return null;
|
|
48
|
+
const defaultExport = file.getDefaultExportSymbol();
|
|
49
|
+
if (defaultExport) {
|
|
50
|
+
const declaration = defaultExport.getDeclarations()[0];
|
|
51
|
+
if (declaration && declaration.getKind() === SyntaxKind.ExportAssignment) {
|
|
52
|
+
const expr = declaration.asKind(SyntaxKind.ExportAssignment)?.getExpression();
|
|
53
|
+
if (expr && expr.getKind() === SyntaxKind.Identifier) {
|
|
54
|
+
const varName = expr.getText();
|
|
55
|
+
return file.getVariableDeclaration(varName)?.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression) || null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const varDecls = file.getVariableDeclarations();
|
|
60
|
+
for (const varDecl of varDecls) {
|
|
61
|
+
const init = varDecl.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
|
|
62
|
+
if (init) return init;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
convertJsonToAstString(obj, indentLevel = 0, oldAstNode) {
|
|
67
|
+
const indentStr = " ";
|
|
68
|
+
const indent = indentStr.repeat(indentLevel);
|
|
69
|
+
const innerIndent = indentStr.repeat(indentLevel + 1);
|
|
70
|
+
if (obj === null || obj === void 0) return "undefined";
|
|
71
|
+
if (typeof obj === "string") return JSON.stringify(obj);
|
|
72
|
+
if (typeof obj === "number" || typeof obj === "boolean") return String(obj);
|
|
73
|
+
if (Array.isArray(obj)) {
|
|
74
|
+
if (obj.length === 0) return "[]";
|
|
75
|
+
return `[\n${innerIndent}${obj.map((item) => this.convertJsonToAstString(item, indentLevel + 1)).join(`,\n${innerIndent}`)}\n${indent}]`;
|
|
76
|
+
}
|
|
77
|
+
if (typeof obj === "object") {
|
|
78
|
+
const record = obj;
|
|
79
|
+
const keys = Object.keys(record);
|
|
80
|
+
const preservedProps = [];
|
|
81
|
+
if (oldAstNode) {
|
|
82
|
+
const oldProps = oldAstNode.getProperties();
|
|
83
|
+
for (const oldProp of oldProps) if (oldProp.isKind(SyntaxKind.PropertyAssignment)) {
|
|
84
|
+
let name = oldProp.getNameNode().getText();
|
|
85
|
+
if (name.startsWith("\"") && name.endsWith("\"")) name = name.slice(1, -1);
|
|
86
|
+
if (name.startsWith("'") && name.endsWith("'")) name = name.slice(1, -1);
|
|
87
|
+
if (!(name in record)) {
|
|
88
|
+
const init = oldProp.getInitializer();
|
|
89
|
+
if (init) {
|
|
90
|
+
const kind = init.getKind();
|
|
91
|
+
if (kind === SyntaxKind.ArrowFunction || kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.Identifier || kind === SyntaxKind.CallExpression || kind === SyntaxKind.JsxElement || name === "target" || name === "callbacks" || name === "permissions" || name === "securityRules") {
|
|
92
|
+
const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
93
|
+
preservedProps.push(`${keyStr}: ${init.getText()}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (keys.length === 0 && preservedProps.length === 0) return "{}";
|
|
100
|
+
return `{\n${innerIndent}${[...keys.map((key) => {
|
|
101
|
+
const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
102
|
+
let childAstNode;
|
|
103
|
+
if (oldAstNode && typeof record[key] === "object" && record[key] !== null && !Array.isArray(record[key])) {
|
|
104
|
+
const oldProp = oldAstNode.getProperty((p) => "getName" in p && typeof p.getName === "function" && (p.getName() === key || p.getName() === `"${key}"` || p.getName() === `'${key}'`));
|
|
105
|
+
if (oldProp && oldProp.isKind(SyntaxKind.PropertyAssignment)) childAstNode = oldProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
|
|
106
|
+
}
|
|
107
|
+
return `${keyStr}: ${this.convertJsonToAstString(record[key], indentLevel + 1, childAstNode)}`;
|
|
108
|
+
}), ...preservedProps].join(`,\n${innerIndent}`)}\n${indent}}`;
|
|
109
|
+
}
|
|
110
|
+
return "undefined";
|
|
111
|
+
}
|
|
112
|
+
async saveProperty(collectionId, propertyKey, propertyConfig) {
|
|
113
|
+
const collectionObj = this.getCollectionObject(collectionId);
|
|
114
|
+
if (!collectionObj) throw new Error(`Collection ${collectionId} not found in ATS workspace.`);
|
|
115
|
+
let propertiesProp = collectionObj.getProperty("properties");
|
|
116
|
+
if (!propertiesProp) propertiesProp = collectionObj.addPropertyAssignment({
|
|
117
|
+
name: "properties",
|
|
118
|
+
initializer: "{}"
|
|
119
|
+
});
|
|
120
|
+
const propsObj = propertiesProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
|
|
121
|
+
if (propsObj) {
|
|
122
|
+
const existingProp = propsObj.getProperty((p) => "getName" in p && typeof p.getName === "function" && (p.getName() === propertyKey || p.getName() === `"${propertyKey}"`));
|
|
123
|
+
let oldPropAstNode;
|
|
124
|
+
if (existingProp && existingProp.isKind(SyntaxKind.PropertyAssignment)) oldPropAstNode = existingProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
|
|
125
|
+
const newInitializer = this.convertJsonToAstString(propertyConfig, 2, oldPropAstNode);
|
|
126
|
+
if (existingProp) {
|
|
127
|
+
if (existingProp.isKind(SyntaxKind.PropertyAssignment)) existingProp.setInitializer(newInitializer);
|
|
128
|
+
} else propsObj.addPropertyAssignment({
|
|
129
|
+
name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey : JSON.stringify(propertyKey),
|
|
130
|
+
initializer: newInitializer
|
|
131
|
+
});
|
|
132
|
+
const file = this.getCollectionFile(collectionId);
|
|
133
|
+
if (file) file.formatText();
|
|
134
|
+
await this.project.save();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async deleteProperty(collectionId, propertyKey) {
|
|
138
|
+
const collectionObj = this.getCollectionObject(collectionId);
|
|
139
|
+
if (!collectionObj) return;
|
|
140
|
+
const propertiesProp = collectionObj.getProperty("properties");
|
|
141
|
+
if (propertiesProp) {
|
|
142
|
+
const propsObj = propertiesProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
|
|
143
|
+
if (propsObj) {
|
|
144
|
+
const existingProp = propsObj.getProperty((p) => "getName" in p && typeof p.getName === "function" && (p.getName() === propertyKey || p.getName() === `"${propertyKey}"`));
|
|
145
|
+
if (existingProp) {
|
|
146
|
+
existingProp.remove();
|
|
147
|
+
const file = this.getCollectionFile(collectionId);
|
|
148
|
+
if (file) file.formatText();
|
|
149
|
+
await this.project.save();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async saveCollection(collectionId, collectionData) {
|
|
155
|
+
let file = this.getCollectionFile(collectionId);
|
|
156
|
+
const collectionObj = this.getCollectionObject(collectionId);
|
|
157
|
+
if (!file || !collectionObj) {
|
|
158
|
+
const safeId = this.sanitizeCollectionId(collectionId);
|
|
159
|
+
const newFilePath = this.safePath(`${safeId}.ts`);
|
|
160
|
+
file = this.project.createSourceFile(newFilePath, `import { CollectionConfig } from "@rebasepro/types";\n\nconst ${safeId}Collection: CollectionConfig = ${this.convertJsonToAstString(collectionData)};\n\nexport default ${safeId}Collection;\n`, { overwrite: true });
|
|
161
|
+
} else {
|
|
162
|
+
if (!("securityRules" in collectionData) || collectionData.securityRules === void 0 || Array.isArray(collectionData.securityRules) && collectionData.securityRules.length === 0) {
|
|
163
|
+
const srProp = collectionObj.getProperty("securityRules");
|
|
164
|
+
if (srProp) srProp.remove();
|
|
165
|
+
delete collectionData["securityRules"];
|
|
166
|
+
}
|
|
167
|
+
for (const key of Object.keys(collectionData)) {
|
|
168
|
+
if (key === "relations") continue;
|
|
169
|
+
const prop = collectionObj.getProperty(key);
|
|
170
|
+
let oldAstNode;
|
|
171
|
+
if (prop && prop.isKind(SyntaxKind.PropertyAssignment)) oldAstNode = prop.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
|
|
172
|
+
const newInit = this.convertJsonToAstString(collectionData[key], 1, oldAstNode);
|
|
173
|
+
if (prop) prop.setInitializer(newInit);
|
|
174
|
+
else collectionObj.addPropertyAssignment({
|
|
175
|
+
name: key,
|
|
176
|
+
initializer: newInit
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (file) file.formatText();
|
|
181
|
+
await this.project.save();
|
|
182
|
+
}
|
|
183
|
+
async deleteCollection(collectionId) {
|
|
184
|
+
const file = this.getCollectionFile(collectionId);
|
|
185
|
+
if (file) file.deleteImmediatelySync();
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/api/schema-editor-routes.ts
|
|
190
|
+
function createSchemaEditorRoutes(collectionsDir) {
|
|
191
|
+
const router = new Hono();
|
|
192
|
+
router.onError(errorHandler);
|
|
193
|
+
const editor = new AstSchemaEditor(collectionsDir);
|
|
194
|
+
router.post("/property/save", async (c) => {
|
|
195
|
+
const { collectionId, propertyKey, propertyConfig } = await c.req.json();
|
|
196
|
+
await editor.saveProperty(collectionId, propertyKey, propertyConfig);
|
|
197
|
+
return c.json({ success: true });
|
|
198
|
+
});
|
|
199
|
+
router.post("/property/delete", async (c) => {
|
|
200
|
+
const { collectionId, propertyKey } = await c.req.json();
|
|
201
|
+
await editor.deleteProperty(collectionId, propertyKey);
|
|
202
|
+
return c.json({ success: true });
|
|
203
|
+
});
|
|
204
|
+
router.post("/collection/save", async (c) => {
|
|
205
|
+
const { collectionId, collectionData } = await c.req.json();
|
|
206
|
+
await editor.saveCollection(collectionId, collectionData);
|
|
207
|
+
return c.json({ success: true });
|
|
208
|
+
});
|
|
209
|
+
router.post("/collection/delete", async (c) => {
|
|
210
|
+
const { collectionId } = await c.req.json();
|
|
211
|
+
await editor.deleteCollection(collectionId);
|
|
212
|
+
return c.json({ success: true });
|
|
213
|
+
});
|
|
214
|
+
return router;
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
export { createSchemaEditorRoutes };
|
|
218
|
+
|
|
219
|
+
//# sourceMappingURL=schema-editor-routes-D3ef8zu1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-editor-routes-D3ef8zu1.js","names":[],"sources":["../src/api/ast-schema-editor.ts","../src/api/schema-editor-routes.ts"],"sourcesContent":["import { Project, SyntaxKind, ObjectLiteralExpression, ObjectLiteralElementLike, PropertyAssignment, VariableDeclaration, IndentationText } from \"ts-morph\";\nimport * as path from \"path\";\nimport * as fs from \"fs\";\n\nexport class AstSchemaEditor {\n private project: Project;\n private collectionsDir: string;\n\n constructor(collectionsDir: string) {\n this.project = new Project({\n manipulationSettings: {\n indentationText: IndentationText.FourSpaces\n }\n });\n if (fs.existsSync(collectionsDir)) {\n this.project.addSourceFilesAtPaths(`${collectionsDir}/**/*.ts`);\n }\n this.collectionsDir = path.resolve(collectionsDir);\n }\n\n /**\n * Sanitize collectionId to prevent path traversal attacks.\n * Only allows alphanumeric characters, underscores, and hyphens.\n */\n private sanitizeCollectionId(collectionId: string): string {\n const sanitized = collectionId.replace(/[^a-zA-Z0-9_-]/g, \"\");\n if (!sanitized || sanitized !== collectionId) {\n throw new Error(`Invalid collection ID: \"${collectionId}\". Only alphanumeric characters, underscores, and hyphens are allowed.`);\n }\n return sanitized;\n }\n\n /**\n * Resolve a file path and ensure it falls within the collectionsDir.\n */\n private safePath(filename: string): string {\n const resolved = path.resolve(this.collectionsDir, filename);\n if (!resolved.startsWith(this.collectionsDir + path.sep) && resolved !== this.collectionsDir) {\n throw new Error(\"Path traversal detected: resolved path is outside the collections directory.\");\n }\n return resolved;\n }\n\n private getCollectionFile(collectionId: string) {\n const safeId = this.sanitizeCollectionId(collectionId);\n const filePath = this.safePath(`${safeId}.ts`);\n let file = this.project.getSourceFile(filePath);\n if (!file && fs.existsSync(filePath)) {\n this.project.addSourceFilesAtPaths(`${this.collectionsDir}/**/*.ts`);\n file = this.project.getSourceFile(filePath);\n }\n return file;\n }\n\n private getCollectionObject(collectionId: string): ObjectLiteralExpression | null {\n const file = this.getCollectionFile(collectionId);\n if (!file) return null;\n\n const defaultExport = file.getDefaultExportSymbol();\n if (defaultExport) {\n const declaration = defaultExport.getDeclarations()[0];\n if (declaration && declaration.getKind() === SyntaxKind.ExportAssignment) {\n const expr = declaration.asKind(SyntaxKind.ExportAssignment)?.getExpression();\n if (expr && expr.getKind() === SyntaxKind.Identifier) {\n const varName = expr.getText();\n const varDecl = file.getVariableDeclaration(varName);\n return varDecl?.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression) || null;\n }\n }\n }\n // Fallback: Just get the first exported VariableDeclaration with an ObjectLiteral\n const varDecls = file.getVariableDeclarations();\n for (const varDecl of varDecls) {\n const init = varDecl.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);\n if (init) return init;\n }\n return null;\n }\n\n private convertJsonToAstString(obj: unknown, indentLevel = 0, oldAstNode?: ObjectLiteralExpression): string {\n // Base TS-morph parses arrays as 2 levels deep from the property key:\n // PropertiesObject = level 1, PropertyConfig = level 2.\n // We calibrate the spacing multiples to keep the items flush with standard TS format.\n const indentStr = \" \";\n const indent = indentStr.repeat(indentLevel);\n const innerIndent = indentStr.repeat(indentLevel + 1);\n\n if (obj === null || obj === undefined) {\n return \"undefined\";\n }\n if (typeof obj === \"string\") {\n return JSON.stringify(obj);\n }\n if (typeof obj === \"number\" || typeof obj === \"boolean\") {\n return String(obj);\n }\n if (Array.isArray(obj)) {\n if (obj.length === 0) return \"[]\";\n const items = obj.map(item => this.convertJsonToAstString(item, indentLevel + 1));\n return `[\\n${innerIndent}${items.join(`,\\n${innerIndent}`)}\\n${indent}]`;\n }\n if (typeof obj === \"object\") {\n const record = obj as Record<string, unknown>;\n const keys = Object.keys(record);\n\n // Collect preserved AST properties\n const preservedProps: string[] = [];\n if (oldAstNode) {\n const oldProps = oldAstNode.getProperties();\n for (const oldProp of oldProps) {\n if (oldProp.isKind(SyntaxKind.PropertyAssignment)) {\n const nameNode = oldProp.getNameNode();\n let name = nameNode.getText();\n if (name.startsWith('\"') && name.endsWith('\"')) name = name.slice(1, -1);\n if (name.startsWith(\"'\") && name.endsWith(\"'\")) name = name.slice(1, -1);\n\n // If the JSON object doesn't have this key, check if we should preserve it\n if (!(name in record)) {\n const init = oldProp.getInitializer();\n if (init) {\n const kind = init.getKind();\n const isCode = kind === SyntaxKind.ArrowFunction ||\n kind === SyntaxKind.FunctionExpression ||\n kind === SyntaxKind.Identifier ||\n kind === SyntaxKind.CallExpression ||\n kind === SyntaxKind.JsxElement;\n\n if (isCode || name === \"target\" || name === \"callbacks\" || name === \"permissions\" || name === \"securityRules\") {\n // Preserve this property exactly as it was\n const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);\n preservedProps.push(`${keyStr}: ${init.getText()}`);\n }\n }\n }\n }\n }\n }\n\n if (keys.length === 0 && preservedProps.length === 0) return \"{}\";\n\n const props = keys.map(key => {\n const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);\n\n // If the value is an object, pass the old AST node to recurse\n let childAstNode: ObjectLiteralExpression | undefined;\n if (oldAstNode && typeof record[key] === \"object\" && record[key] !== null && !Array.isArray(record[key])) {\n const oldProp = oldAstNode.getProperty(\n (p: ObjectLiteralElementLike) => \"getName\" in p && typeof (p as PropertyAssignment).getName === \"function\" && ((p as PropertyAssignment).getName() === key || (p as PropertyAssignment).getName() === `\"${key}\"` || (p as PropertyAssignment).getName() === `'${key}'`)\n );\n if (oldProp && oldProp.isKind(SyntaxKind.PropertyAssignment)) {\n childAstNode = oldProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);\n }\n }\n\n return `${keyStr}: ${this.convertJsonToAstString(record[key], indentLevel + 1, childAstNode)}`;\n });\n\n const allProps = [...props, ...preservedProps];\n return `{\\n${innerIndent}${allProps.join(`,\\n${innerIndent}`)}\\n${indent}}`;\n }\n return \"undefined\";\n }\n\n public async saveProperty(collectionId: string, propertyKey: string, propertyConfig: Record<string, unknown>) {\n const collectionObj = this.getCollectionObject(collectionId);\n if (!collectionObj) throw new Error(`Collection ${collectionId} not found in ATS workspace.`);\n\n let propertiesProp = collectionObj.getProperty(\"properties\") as PropertyAssignment;\n if (!propertiesProp) {\n propertiesProp = collectionObj.addPropertyAssignment({\n name: \"properties\",\n initializer: \"{}\"\n });\n }\n\n const propsObj = propertiesProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);\n if (propsObj) {\n const existingProp = propsObj.getProperty(\n (p: ObjectLiteralElementLike) => \"getName\" in p && typeof (p as PropertyAssignment).getName === \"function\" && ((p as PropertyAssignment).getName() === propertyKey || (p as PropertyAssignment).getName() === `\"${propertyKey}\"`)\n );\n\n let oldPropAstNode: ObjectLiteralExpression | undefined;\n if (existingProp && existingProp.isKind(SyntaxKind.PropertyAssignment)) {\n oldPropAstNode = existingProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);\n }\n\n const newInitializer = this.convertJsonToAstString(propertyConfig, 2, oldPropAstNode);\n\n if (existingProp) {\n if (existingProp.isKind(SyntaxKind.PropertyAssignment)) {\n existingProp.setInitializer(newInitializer);\n }\n } else {\n propsObj.addPropertyAssignment({\n name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey : JSON.stringify(propertyKey),\n initializer: newInitializer\n });\n }\n\n const file = this.getCollectionFile(collectionId);\n if (file) {\n file.formatText();\n }\n await this.project.save();\n }\n }\n\n public async deleteProperty(collectionId: string, propertyKey: string) {\n const collectionObj = this.getCollectionObject(collectionId);\n if (!collectionObj) return;\n\n const propertiesProp = collectionObj.getProperty(\"properties\") as PropertyAssignment;\n if (propertiesProp) {\n const propsObj = propertiesProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);\n if (propsObj) {\n const existingProp = propsObj.getProperty(\n (p: ObjectLiteralElementLike) => \"getName\" in p && typeof (p as PropertyAssignment).getName === \"function\" && ((p as PropertyAssignment).getName() === propertyKey || (p as PropertyAssignment).getName() === `\"${propertyKey}\"`)\n );\n if (existingProp) {\n existingProp.remove();\n const file = this.getCollectionFile(collectionId);\n if (file) {\n file.formatText();\n }\n await this.project.save();\n }\n }\n }\n }\n\n public async saveCollection(collectionId: string, collectionData: Record<string, unknown>) {\n let file = this.getCollectionFile(collectionId);\n const collectionObj = this.getCollectionObject(collectionId);\n\n if (!file || !collectionObj) {\n // Create a new file\n const safeId = this.sanitizeCollectionId(collectionId);\n const newFilePath = this.safePath(`${safeId}.ts`);\n file = this.project.createSourceFile(newFilePath, `import { CollectionConfig } from \"@rebasepro/types\";\\n\\nconst ${safeId}Collection: CollectionConfig = ${this.convertJsonToAstString(collectionData)};\\n\\nexport default ${safeId}Collection;\\n`, { overwrite: true });\n } else {\n // Update root level properties gracefully\n\n // Force delete securityRules if empty or undefined to handle Formex / serialization stripping\n if (!(\"securityRules\" in collectionData) || collectionData.securityRules === undefined || (Array.isArray(collectionData.securityRules) && collectionData.securityRules.length === 0)) {\n const srProp = collectionObj.getProperty(\"securityRules\");\n if (srProp) {\n srProp.remove();\n }\n\n // If it was in collectionData as an empty array, delete it so the loop below doesn't add it back as \"[]\"\n // Actually, if it's \"[]\", omitting it entirely from the TS file achieves the same logical effect (no RLS rules)\n // and correctly triggers \"unmapped policies\" if the DB still has them.\n delete collectionData[\"securityRules\"];\n }\n\n for (const key of Object.keys(collectionData)) {\n if (key === \"relations\") continue; // Kept via other AST functions or handled separately.\n\n const prop = collectionObj.getProperty(key) as PropertyAssignment;\n\n let oldAstNode: ObjectLiteralExpression | undefined;\n if (prop && prop.isKind(SyntaxKind.PropertyAssignment)) {\n oldAstNode = prop.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);\n }\n\n const newInit = this.convertJsonToAstString(collectionData[key], 1, oldAstNode);\n if (prop) {\n prop.setInitializer(newInit);\n } else {\n collectionObj.addPropertyAssignment({\n name: key,\n initializer: newInit\n });\n }\n }\n }\n if (file) {\n file.formatText();\n }\n await this.project.save();\n }\n\n public async deleteCollection(collectionId: string) {\n const file = this.getCollectionFile(collectionId);\n if (file) {\n file.deleteImmediatelySync();\n }\n }\n}\n","import { Hono } from \"hono\";\nimport { AstSchemaEditor } from \"./ast-schema-editor\";\nimport { errorHandler } from \"./errors\";\nimport { HonoEnv } from \"./types\";\n\nexport function createSchemaEditorRoutes(collectionsDir: string): Hono<HonoEnv> {\n const router = new Hono<HonoEnv>();\n router.onError(errorHandler);\n const editor = new AstSchemaEditor(collectionsDir);\n\n router.post(\"/property/save\", async (c) => {\n const body = await c.req.json();\n const { collectionId, propertyKey, propertyConfig } = body;\n await editor.saveProperty(collectionId, propertyKey, propertyConfig);\n return c.json({ success: true });\n });\n\n router.post(\"/property/delete\", async (c) => {\n const body = await c.req.json();\n const { collectionId, propertyKey } = body;\n await editor.deleteProperty(collectionId, propertyKey);\n return c.json({ success: true });\n });\n\n router.post(\"/collection/save\", async (c) => {\n const body = await c.req.json();\n const { collectionId, collectionData } = body;\n await editor.saveCollection(collectionId, collectionData);\n return c.json({ success: true });\n });\n\n router.post(\"/collection/delete\", async (c) => {\n const body = await c.req.json();\n const { collectionId } = body;\n await editor.deleteCollection(collectionId);\n return c.json({ success: true });\n });\n\n return router;\n}\n\n"],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for serving a Single Page Application
|
|
4
|
+
*/
|
|
5
|
+
export interface ServeSPAConfig {
|
|
6
|
+
/**
|
|
7
|
+
* Absolute path to the frontend build directory
|
|
8
|
+
* @example path.join(__dirname, "../../frontend/dist")
|
|
9
|
+
*/
|
|
10
|
+
frontendPath: string;
|
|
11
|
+
/**
|
|
12
|
+
* Base path for API routes (default: "/api")
|
|
13
|
+
* Requests to this path will be passed through to API handlers
|
|
14
|
+
*/
|
|
15
|
+
apiBasePath?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Additional paths to exclude from SPA handling
|
|
18
|
+
* These paths will be passed through to other handlers
|
|
19
|
+
* @example ["/health", "/ws", "/metrics"]
|
|
20
|
+
*/
|
|
21
|
+
excludePaths?: string[];
|
|
22
|
+
/**
|
|
23
|
+
* Index file to serve for SPA routes (default: "index.html")
|
|
24
|
+
*/
|
|
25
|
+
indexFile?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Serve a Single Page Application from an Hono app.
|
|
29
|
+
*
|
|
30
|
+
* @internal Not part of the stable public API. Exported only because the
|
|
31
|
+
* official app template (`packages/cli/templates/template/backend/src/index.ts`
|
|
32
|
+
* and `app/backend/src/index.ts`) calls it to serve the built frontend in
|
|
33
|
+
* production. Its request-handling behavior is an implementation detail and
|
|
34
|
+
* may change without a major version bump.
|
|
35
|
+
*/
|
|
36
|
+
export declare function serveSPA<E extends import("hono").Env>(app: Hono<E>, config: ServeSPAConfig): void;
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
import { DataDriver } from "@rebasepro/types";
|
|
14
|
+
/**
|
|
15
|
+
* The default driver identifier used when:
|
|
16
|
+
* - A single driver is provided (not a map)
|
|
17
|
+
* - A collection doesn't specify a databaseId
|
|
18
|
+
*/
|
|
19
|
+
export declare const DEFAULT_DRIVER_ID = "(default)";
|
|
20
|
+
/**
|
|
21
|
+
* Registry for managing multiple driver delegates
|
|
22
|
+
*/
|
|
23
|
+
export interface DriverRegistry {
|
|
24
|
+
/**
|
|
25
|
+
* Register a driver delegate with an ID
|
|
26
|
+
* @param id - Unique identifier for this driver (e.g., "analytics", "users")
|
|
27
|
+
* @param delegate - The DataDriver instance
|
|
28
|
+
*/
|
|
29
|
+
register(id: string, delegate: DataDriver): void;
|
|
30
|
+
/**
|
|
31
|
+
* Get the default driver delegate (id = "(default)")
|
|
32
|
+
* @throws Error if no default driver is registered
|
|
33
|
+
*/
|
|
34
|
+
getDefault(): DataDriver;
|
|
35
|
+
/**
|
|
36
|
+
* Get a driver delegate by ID
|
|
37
|
+
* @param id - Driver identifier, or undefined/null for default
|
|
38
|
+
* @returns The DataDriver, or undefined if not found
|
|
39
|
+
*/
|
|
40
|
+
get(id: string | undefined | null): DataDriver | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Get a driver delegate by ID, with fallback to default
|
|
43
|
+
* @param id - Driver identifier, or undefined/null for default
|
|
44
|
+
* @returns The DataDriver (falls back to default if id not found)
|
|
45
|
+
* @throws Error if neither the specified nor default driver exists
|
|
46
|
+
*/
|
|
47
|
+
getOrDefault(id: string | undefined | null): DataDriver;
|
|
48
|
+
/**
|
|
49
|
+
* Check if a driver with the given ID exists
|
|
50
|
+
*/
|
|
51
|
+
has(id: string): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* List all registered driver IDs
|
|
54
|
+
*/
|
|
55
|
+
list(): string[];
|
|
56
|
+
/**
|
|
57
|
+
* Get the number of registered drivers
|
|
58
|
+
*/
|
|
59
|
+
size(): number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Default implementation of DriverRegistry
|
|
63
|
+
*/
|
|
64
|
+
export declare class DefaultDriverRegistry implements DriverRegistry {
|
|
65
|
+
private delegates;
|
|
66
|
+
/**
|
|
67
|
+
* Create a DriverRegistry from either a single delegate or a map
|
|
68
|
+
* @param input - Single DataDriver (maps to "(default)") or Record<string, DataDriver>
|
|
69
|
+
*/
|
|
70
|
+
static create(input: DataDriver | Record<string, DataDriver>): DefaultDriverRegistry;
|
|
71
|
+
register(id: string, delegate: DataDriver): void;
|
|
72
|
+
getDefault(): DataDriver;
|
|
73
|
+
get(id: string | undefined | null): DataDriver | undefined;
|
|
74
|
+
getOrDefault(id: string | undefined | null): DataDriver;
|
|
75
|
+
has(id: string): boolean;
|
|
76
|
+
list(): string[];
|
|
77
|
+
size(): number;
|
|
78
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { RealtimeProvider } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* A realtime client message as forwarded by the WebSocket server.
|
|
4
|
+
*/
|
|
5
|
+
interface ClientMessage {
|
|
6
|
+
type: string;
|
|
7
|
+
payload?: Record<string, unknown>;
|
|
8
|
+
subscriptionId?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The concrete realtime service surface the WebSocket server drives — the
|
|
12
|
+
* typed {@link RealtimeProvider} plus the client-connection methods that
|
|
13
|
+
* every engine's realtime service implements.
|
|
14
|
+
*/
|
|
15
|
+
export interface WsRealtimeService extends RealtimeProvider {
|
|
16
|
+
addClient(clientId: string, ws: unknown): void;
|
|
17
|
+
handleClientMessage(clientId: string, message: ClientMessage, authContext?: unknown): Promise<void> | void;
|
|
18
|
+
}
|
|
19
|
+
export interface RoutedRealtimeOptions {
|
|
20
|
+
/** Per-engine realtime providers, keyed by data-source key. */
|
|
21
|
+
providers: Record<string, RealtimeProvider>;
|
|
22
|
+
/** Key of the default provider (handles channels/presence/broadcast). */
|
|
23
|
+
defaultKey: string;
|
|
24
|
+
/** Resolve a collection path to its data-source key. */
|
|
25
|
+
resolveKey: (collectionPath: string) => string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Compose multiple per-engine {@link RealtimeProvider}s into one that routes
|
|
29
|
+
* each subscription to the provider owning the subscribed collection — the
|
|
30
|
+
* realtime counterpart of `buildRoutedRebaseData`.
|
|
31
|
+
*
|
|
32
|
+
* The WebSocket server stays single and engine-agnostic; this composite is
|
|
33
|
+
* passed in its place. Routing rules:
|
|
34
|
+
* - `subscribe_collection` / `subscribe_entity` → the provider for the
|
|
35
|
+
* collection's data source (by `payload.path`).
|
|
36
|
+
* - `unsubscribe` → forwarded to all providers (a no-op on non-owners).
|
|
37
|
+
* - channel / presence / broadcast → the default provider (these are global
|
|
38
|
+
* pub/sub, not bound to an engine).
|
|
39
|
+
* - `addClient` and lifecycle (`onServerReady`/`destroy`/`stopListening`) →
|
|
40
|
+
* all providers (each registers its own ws close handler for cleanup).
|
|
41
|
+
*/
|
|
42
|
+
export declare function createRoutedRealtimeService(opts: RoutedRealtimeOptions): WsRealtimeService;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface WebhookConfig {
|
|
2
|
+
id: string;
|
|
3
|
+
url: string;
|
|
4
|
+
secret?: string;
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
events: string[];
|
|
7
|
+
table: string;
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface WebhookDeliveryResult {
|
|
11
|
+
webhookId: string;
|
|
12
|
+
event: string;
|
|
13
|
+
payload: Record<string, unknown>;
|
|
14
|
+
statusCode: number;
|
|
15
|
+
responseBody: string;
|
|
16
|
+
success: boolean;
|
|
17
|
+
attemptNumber: number;
|
|
18
|
+
}
|
|
19
|
+
export declare class WebhookDispatcher {
|
|
20
|
+
private webhooks;
|
|
21
|
+
private maxRetries;
|
|
22
|
+
private retryDelays;
|
|
23
|
+
/** Register webhooks to watch */
|
|
24
|
+
setWebhooks(webhooks: WebhookConfig[]): void;
|
|
25
|
+
/** Called when a entity changes — checks if any webhook matches */
|
|
26
|
+
onEntityChange(table: string, event: "INSERT" | "UPDATE" | "DELETE", id: string, entity: Record<string, unknown> | null, previousEntity?: Record<string, unknown> | null): Promise<WebhookDeliveryResult[]>;
|
|
27
|
+
private deliverWithRetry;
|
|
28
|
+
private deliver;
|
|
29
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { RebaseServerClient } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* @internal Called once during server initialization to set the backing instance.
|
|
4
|
+
* This is invoked by `initializeRebaseBackend()` — never call it manually.
|
|
5
|
+
*/
|
|
6
|
+
export declare function _initRebase(client: RebaseServerClient): void;
|
|
7
|
+
/**
|
|
8
|
+
* @internal Allows overriding the underlying instance for unit testing.
|
|
9
|
+
* Throws an error if used in a non-test environment to prevent production abuse.
|
|
10
|
+
*/
|
|
11
|
+
export declare function _setRebaseMock(mockInstance: Partial<RebaseServerClient>): void;
|
|
12
|
+
/**
|
|
13
|
+
* @internal Resets the singleton instance, useful for afterEach() in test suites.
|
|
14
|
+
*/
|
|
15
|
+
export declare function _resetRebaseMock(): void;
|
|
16
|
+
/**
|
|
17
|
+
* The server-side Rebase singleton.
|
|
18
|
+
*
|
|
19
|
+
* Initialized automatically during server startup. Provides access to all
|
|
20
|
+
* app-scoped services: **data**, **auth**, **storage**, and **email**.
|
|
21
|
+
*
|
|
22
|
+
* **Admin data plane** (`rebase.dataAsAdmin`):
|
|
23
|
+
* Backed by the native DataDriver — calls go directly to the database without
|
|
24
|
+
* JSON serialization, HTTP dispatch, or middleware overhead. The driver is
|
|
25
|
+
* scoped as `{ uid: "service", roles: ["admin"] }`, so **every read and write
|
|
26
|
+
* bypasses row-level-security policies**. No `REBASE_SERVICE_KEY` is required.
|
|
27
|
+
*
|
|
28
|
+
* ⚠️ Because it bypasses RLS, `rebase.dataAsAdmin` is for trusted background
|
|
29
|
+
* work (cron jobs, migrations, service tasks) — **not** for serving user-facing
|
|
30
|
+
* data. Inside a request handler, run user-scoped queries through the
|
|
31
|
+
* request-scoped driver (`c.var.driver`), which carries the caller's identity
|
|
32
|
+
* so RLS applies.
|
|
33
|
+
*
|
|
34
|
+
* `rebase.data` is a **deprecated alias** for `rebase.dataAsAdmin` — identical
|
|
35
|
+
* admin scope at runtime. Prefer `dataAsAdmin` so the privilege is explicit.
|
|
36
|
+
*
|
|
37
|
+
* **Control plane** (`rebase.auth`, `rebase.admin`, `rebase.storage`, etc.):
|
|
38
|
+
* Routes through the Hono app's internal request handler. An internal per-boot
|
|
39
|
+
* credential is generated automatically when `REBASE_SERVICE_KEY` is not set,
|
|
40
|
+
* so control-plane calls always authenticate.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* import { rebase } from "@rebasepro/server-core";
|
|
45
|
+
*
|
|
46
|
+
* // In a cron job, hook, or trusted service file (admin scope, bypasses RLS):
|
|
47
|
+
* await rebase.email.send({ to: "admin@co.com", subject: "Alert", html: "<p>Hi</p>" });
|
|
48
|
+
* const jobs = await rebase.dataAsAdmin.jobs.find({ limit: 10 });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare const rebase: RebaseServerClient;
|