@rebasepro/server-core 0.0.1-canary.09e5ec5
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/LICENSE +6 -0
- package/README.md +40 -0
- package/build-errors.txt +52 -0
- package/coverage/clover.xml +3739 -0
- package/coverage/coverage-final.json +31 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +266 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/src/api/ast-schema-editor.ts.html +952 -0
- package/coverage/lcov-report/src/api/errors.ts.html +472 -0
- package/coverage/lcov-report/src/api/graphql/graphql-schema-generator.ts.html +1069 -0
- package/coverage/lcov-report/src/api/graphql/index.html +116 -0
- package/coverage/lcov-report/src/api/index.html +176 -0
- package/coverage/lcov-report/src/api/openapi-generator.ts.html +565 -0
- package/coverage/lcov-report/src/api/rest/api-generator.ts.html +994 -0
- package/coverage/lcov-report/src/api/rest/index.html +131 -0
- package/coverage/lcov-report/src/api/rest/query-parser.ts.html +550 -0
- package/coverage/lcov-report/src/api/schema-editor-routes.ts.html +202 -0
- package/coverage/lcov-report/src/api/server.ts.html +823 -0
- package/coverage/lcov-report/src/auth/admin-routes.ts.html +973 -0
- package/coverage/lcov-report/src/auth/index.html +176 -0
- package/coverage/lcov-report/src/auth/jwt.ts.html +574 -0
- package/coverage/lcov-report/src/auth/middleware.ts.html +745 -0
- package/coverage/lcov-report/src/auth/password.ts.html +310 -0
- package/coverage/lcov-report/src/auth/services.ts.html +2074 -0
- package/coverage/lcov-report/src/collections/index.html +116 -0
- package/coverage/lcov-report/src/collections/loader.ts.html +232 -0
- package/coverage/lcov-report/src/db/auth-schema.ts.html +523 -0
- package/coverage/lcov-report/src/db/data-transformer.ts.html +1753 -0
- package/coverage/lcov-report/src/db/entityService.ts.html +700 -0
- package/coverage/lcov-report/src/db/index.html +146 -0
- package/coverage/lcov-report/src/db/services/EntityFetchService.ts.html +4048 -0
- package/coverage/lcov-report/src/db/services/EntityPersistService.ts.html +883 -0
- package/coverage/lcov-report/src/db/services/RelationService.ts.html +3121 -0
- package/coverage/lcov-report/src/db/services/entity-helpers.ts.html +442 -0
- package/coverage/lcov-report/src/db/services/index.html +176 -0
- package/coverage/lcov-report/src/db/services/index.ts.html +124 -0
- package/coverage/lcov-report/src/generate-drizzle-schema-logic.ts.html +1960 -0
- package/coverage/lcov-report/src/index.html +116 -0
- package/coverage/lcov-report/src/services/driver-registry.ts.html +631 -0
- package/coverage/lcov-report/src/services/index.html +131 -0
- package/coverage/lcov-report/src/services/postgresDataDriver.ts.html +3025 -0
- package/coverage/lcov-report/src/storage/LocalStorageController.ts.html +1189 -0
- package/coverage/lcov-report/src/storage/S3StorageController.ts.html +970 -0
- package/coverage/lcov-report/src/storage/index.html +161 -0
- package/coverage/lcov-report/src/storage/storage-registry.ts.html +646 -0
- package/coverage/lcov-report/src/storage/types.ts.html +451 -0
- package/coverage/lcov-report/src/utils/drizzle-conditions.ts.html +3082 -0
- package/coverage/lcov-report/src/utils/index.html +116 -0
- package/coverage/lcov.info +7179 -0
- package/dist/common/src/collections/CollectionRegistry.d.ts +56 -0
- package/dist/common/src/collections/index.d.ts +1 -0
- package/dist/common/src/data/buildRebaseData.d.ts +14 -0
- package/dist/common/src/index.d.ts +3 -0
- package/dist/common/src/util/builders.d.ts +57 -0
- package/dist/common/src/util/callbacks.d.ts +6 -0
- package/dist/common/src/util/collections.d.ts +11 -0
- package/dist/common/src/util/common.d.ts +2 -0
- package/dist/common/src/util/conditions.d.ts +26 -0
- package/dist/common/src/util/entities.d.ts +58 -0
- package/dist/common/src/util/enums.d.ts +3 -0
- package/dist/common/src/util/index.d.ts +16 -0
- package/dist/common/src/util/navigation_from_path.d.ts +34 -0
- package/dist/common/src/util/navigation_utils.d.ts +20 -0
- package/dist/common/src/util/parent_references_from_path.d.ts +6 -0
- package/dist/common/src/util/paths.d.ts +14 -0
- package/dist/common/src/util/permissions.d.ts +5 -0
- package/dist/common/src/util/references.d.ts +2 -0
- package/dist/common/src/util/relations.d.ts +22 -0
- package/dist/common/src/util/resolutions.d.ts +72 -0
- package/dist/common/src/util/storage.d.ts +24 -0
- package/dist/index-DXVBFp5V.js +37 -0
- package/dist/index-DXVBFp5V.js.map +1 -0
- package/dist/index.es.js +49934 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +49968 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/server-core/src/api/ast-schema-editor.d.ts +21 -0
- package/dist/server-core/src/api/collections_for_test/callbacks_test_collection.d.ts +2 -0
- package/dist/server-core/src/api/errors.d.ts +35 -0
- package/dist/server-core/src/api/graphql/graphql-schema-generator.d.ts +35 -0
- package/dist/server-core/src/api/graphql/index.d.ts +1 -0
- package/dist/server-core/src/api/index.d.ts +9 -0
- package/dist/server-core/src/api/openapi-generator.d.ts +16 -0
- package/dist/server-core/src/api/rest/api-generator.d.ts +64 -0
- package/dist/server-core/src/api/rest/index.d.ts +1 -0
- package/dist/server-core/src/api/rest/query-parser.d.ts +9 -0
- package/dist/server-core/src/api/schema-editor-routes.d.ts +3 -0
- package/dist/server-core/src/api/server.d.ts +40 -0
- package/dist/server-core/src/api/types.d.ts +90 -0
- package/dist/server-core/src/auth/admin-routes.d.ts +16 -0
- package/dist/server-core/src/auth/apple-oauth.d.ts +30 -0
- package/dist/server-core/src/auth/bitbucket-oauth.d.ts +11 -0
- package/dist/server-core/src/auth/discord-oauth.d.ts +14 -0
- package/dist/server-core/src/auth/facebook-oauth.d.ts +14 -0
- package/dist/server-core/src/auth/github-oauth.d.ts +15 -0
- package/dist/server-core/src/auth/gitlab-oauth.d.ts +13 -0
- package/dist/server-core/src/auth/google-oauth.d.ts +14 -0
- package/dist/server-core/src/auth/index.d.ts +23 -0
- package/dist/server-core/src/auth/interfaces.d.ts +309 -0
- package/dist/server-core/src/auth/jwt.d.ts +43 -0
- package/dist/server-core/src/auth/linkedin-oauth.d.ts +18 -0
- package/dist/server-core/src/auth/microsoft-oauth.d.ts +16 -0
- package/dist/server-core/src/auth/middleware.d.ts +81 -0
- package/dist/server-core/src/auth/password.d.ts +22 -0
- package/dist/server-core/src/auth/rate-limiter.d.ts +31 -0
- package/dist/server-core/src/auth/routes.d.ts +27 -0
- package/dist/server-core/src/auth/slack-oauth.d.ts +12 -0
- package/dist/server-core/src/auth/spotify-oauth.d.ts +12 -0
- package/dist/server-core/src/auth/twitter-oauth.d.ts +18 -0
- package/dist/server-core/src/bootstrappers/index.d.ts +0 -0
- package/dist/server-core/src/collections/BackendCollectionRegistry.d.ts +13 -0
- package/dist/server-core/src/collections/loader.d.ts +5 -0
- package/dist/server-core/src/cron/cron-loader.d.ts +17 -0
- package/dist/server-core/src/cron/cron-routes.d.ts +14 -0
- package/dist/server-core/src/cron/cron-scheduler.d.ts +61 -0
- package/dist/server-core/src/cron/cron-store.d.ts +32 -0
- package/dist/server-core/src/cron/index.d.ts +6 -0
- package/dist/server-core/src/db/interfaces.d.ts +18 -0
- package/dist/server-core/src/email/index.d.ts +6 -0
- package/dist/server-core/src/email/smtp-email-service.d.ts +25 -0
- package/dist/server-core/src/email/templates.d.ts +42 -0
- package/dist/server-core/src/email/types.d.ts +107 -0
- package/dist/server-core/src/functions/function-loader.d.ts +17 -0
- package/dist/server-core/src/functions/function-routes.d.ts +10 -0
- package/dist/server-core/src/functions/index.d.ts +3 -0
- package/dist/server-core/src/history/history-routes.d.ts +23 -0
- package/dist/server-core/src/history/index.d.ts +1 -0
- package/dist/server-core/src/index.d.ts +29 -0
- package/dist/server-core/src/init.d.ts +159 -0
- package/dist/server-core/src/serve-spa.d.ts +30 -0
- package/dist/server-core/src/services/driver-registry.d.ts +78 -0
- package/dist/server-core/src/singleton.d.ts +35 -0
- package/dist/server-core/src/storage/LocalStorageController.d.ts +46 -0
- package/dist/server-core/src/storage/S3StorageController.d.ts +36 -0
- package/dist/server-core/src/storage/index.d.ts +25 -0
- package/dist/server-core/src/storage/routes.d.ts +38 -0
- package/dist/server-core/src/storage/storage-registry.d.ts +78 -0
- package/dist/server-core/src/storage/types.d.ts +103 -0
- package/dist/server-core/src/types/index.d.ts +11 -0
- package/dist/server-core/src/utils/dev-port.d.ts +35 -0
- package/dist/server-core/src/utils/logger.d.ts +31 -0
- package/dist/server-core/src/utils/logging.d.ts +9 -0
- package/dist/server-core/src/utils/request-logger.d.ts +19 -0
- package/dist/server-core/src/utils/sql.d.ts +27 -0
- package/dist/types/src/controllers/analytics_controller.d.ts +7 -0
- package/dist/types/src/controllers/auth.d.ts +119 -0
- package/dist/types/src/controllers/client.d.ts +170 -0
- package/dist/types/src/controllers/collection_registry.d.ts +45 -0
- package/dist/types/src/controllers/customization_controller.d.ts +60 -0
- package/dist/types/src/controllers/data.d.ts +168 -0
- package/dist/types/src/controllers/data_driver.d.ts +160 -0
- package/dist/types/src/controllers/database_admin.d.ts +11 -0
- package/dist/types/src/controllers/dialogs_controller.d.ts +36 -0
- package/dist/types/src/controllers/effective_role.d.ts +4 -0
- package/dist/types/src/controllers/email.d.ts +34 -0
- package/dist/types/src/controllers/index.d.ts +18 -0
- package/dist/types/src/controllers/local_config_persistence.d.ts +20 -0
- package/dist/types/src/controllers/navigation.d.ts +213 -0
- package/dist/types/src/controllers/registry.d.ts +54 -0
- package/dist/types/src/controllers/side_dialogs_controller.d.ts +67 -0
- package/dist/types/src/controllers/side_entity_controller.d.ts +90 -0
- package/dist/types/src/controllers/snackbar.d.ts +24 -0
- package/dist/types/src/controllers/storage.d.ts +171 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/rebase_context.d.ts +105 -0
- package/dist/types/src/types/backend.d.ts +536 -0
- package/dist/types/src/types/builders.d.ts +15 -0
- package/dist/types/src/types/chips.d.ts +5 -0
- package/dist/types/src/types/collections.d.ts +856 -0
- package/dist/types/src/types/cron.d.ts +102 -0
- package/dist/types/src/types/data_source.d.ts +64 -0
- package/dist/types/src/types/entities.d.ts +145 -0
- package/dist/types/src/types/entity_actions.d.ts +98 -0
- package/dist/types/src/types/entity_callbacks.d.ts +173 -0
- package/dist/types/src/types/entity_link_builder.d.ts +7 -0
- package/dist/types/src/types/entity_overrides.d.ts +10 -0
- package/dist/types/src/types/entity_views.d.ts +61 -0
- package/dist/types/src/types/export_import.d.ts +21 -0
- package/dist/types/src/types/index.d.ts +23 -0
- package/dist/types/src/types/locales.d.ts +4 -0
- package/dist/types/src/types/modify_collections.d.ts +5 -0
- package/dist/types/src/types/plugins.d.ts +279 -0
- package/dist/types/src/types/properties.d.ts +1176 -0
- package/dist/types/src/types/property_config.d.ts +70 -0
- package/dist/types/src/types/relations.d.ts +336 -0
- package/dist/types/src/types/slots.d.ts +252 -0
- package/dist/types/src/types/translations.d.ts +870 -0
- package/dist/types/src/types/user_management_delegate.d.ts +121 -0
- package/dist/types/src/types/websockets.d.ts +78 -0
- package/dist/types/src/users/index.d.ts +2 -0
- package/dist/types/src/users/roles.d.ts +22 -0
- package/dist/types/src/users/user.d.ts +46 -0
- package/history_diff.log +385 -0
- package/jest.config.cjs +16 -0
- package/package.json +86 -0
- package/scratch.ts +9 -0
- package/src/api/ast-schema-editor.ts +289 -0
- package/src/api/collections_for_test/callbacks_test_collection.ts +60 -0
- package/src/api/errors.ts +179 -0
- package/src/api/graphql/graphql-schema-generator.ts +336 -0
- package/src/api/graphql/index.ts +2 -0
- package/src/api/index.ts +11 -0
- package/src/api/openapi-generator.ts +715 -0
- package/src/api/rest/api-generator.ts +472 -0
- package/src/api/rest/index.ts +2 -0
- package/src/api/rest/query-parser.ts +155 -0
- package/src/api/schema-editor-routes.ts +41 -0
- package/src/api/server.ts +248 -0
- package/src/api/types.ts +90 -0
- package/src/auth/admin-routes.ts +529 -0
- package/src/auth/apple-oauth.ts +130 -0
- package/src/auth/bitbucket-oauth.ts +82 -0
- package/src/auth/discord-oauth.ts +83 -0
- package/src/auth/facebook-oauth.ts +72 -0
- package/src/auth/github-oauth.ts +110 -0
- package/src/auth/gitlab-oauth.ts +70 -0
- package/src/auth/google-oauth.ts +48 -0
- package/src/auth/index.ts +34 -0
- package/src/auth/interfaces.ts +363 -0
- package/src/auth/jwt.ts +181 -0
- package/src/auth/linkedin-oauth.ts +81 -0
- package/src/auth/microsoft-oauth.ts +88 -0
- package/src/auth/middleware.ts +384 -0
- package/src/auth/password.ts +77 -0
- package/src/auth/rate-limiter.ts +129 -0
- package/src/auth/routes.ts +788 -0
- package/src/auth/slack-oauth.ts +71 -0
- package/src/auth/spotify-oauth.ts +67 -0
- package/src/auth/twitter-oauth.ts +120 -0
- package/src/bootstrappers/index.ts +1 -0
- package/src/collections/BackendCollectionRegistry.ts +20 -0
- package/src/collections/loader.ts +49 -0
- package/src/cron/cron-loader.ts +89 -0
- package/src/cron/cron-routes.test.ts +265 -0
- package/src/cron/cron-routes.ts +85 -0
- package/src/cron/cron-scheduler.test.ts +421 -0
- package/src/cron/cron-scheduler.ts +413 -0
- package/src/cron/cron-store.ts +163 -0
- package/src/cron/index.ts +6 -0
- package/src/db/interfaces.ts +60 -0
- package/src/email/index.ts +18 -0
- package/src/email/smtp-email-service.ts +91 -0
- package/src/email/templates.ts +388 -0
- package/src/email/types.ts +105 -0
- package/src/functions/function-loader.ts +119 -0
- package/src/functions/function-routes.ts +31 -0
- package/src/functions/index.ts +3 -0
- package/src/history/history-routes.ts +129 -0
- package/src/history/index.ts +2 -0
- package/src/index.ts +66 -0
- package/src/init.ts +727 -0
- package/src/serve-spa.ts +81 -0
- package/src/services/driver-registry.ts +182 -0
- package/src/singleton.test.ts +28 -0
- package/src/singleton.ts +70 -0
- package/src/storage/LocalStorageController.ts +365 -0
- package/src/storage/S3StorageController.ts +298 -0
- package/src/storage/index.ts +43 -0
- package/src/storage/routes.ts +264 -0
- package/src/storage/storage-registry.ts +187 -0
- package/src/storage/types.ts +134 -0
- package/src/types/index.ts +27 -0
- package/src/utils/dev-port.ts +176 -0
- package/src/utils/logger.ts +143 -0
- package/src/utils/logging.ts +38 -0
- package/src/utils/request-logger.ts +66 -0
- package/src/utils/sql.ts +38 -0
- package/test/admin-routes.test.ts +640 -0
- package/test/api-generator.test.ts +501 -0
- package/test/ast-schema-editor.test.ts +63 -0
- package/test/auth-middleware-hono.test.ts +556 -0
- package/test/auth-routes.test.ts +1047 -0
- package/test/driver-registry.test.ts +282 -0
- package/test/error-propagation.test.ts +226 -0
- package/test/errors-hono.test.ts +133 -0
- package/test/errors.test.ts +155 -0
- package/test/jwt-security.test.ts +182 -0
- package/test/jwt.test.ts +324 -0
- package/test/middleware.test.ts +300 -0
- package/test/password.test.ts +165 -0
- package/test/query-parser.test.ts +263 -0
- package/test/rate-limiter.test.ts +102 -0
- package/test/safe-compare.test.ts +66 -0
- package/test/singleton.test.ts +59 -0
- package/test/storage-local.test.ts +271 -0
- package/test/storage-registry.test.ts +282 -0
- package/test/storage-routes.test.ts +222 -0
- package/test/storage-s3.test.ts +304 -0
- package/test-ast.ts +28 -0
- package/test.ts +6 -0
- package/test_output.txt +1133 -0
- package/tsconfig.json +49 -0
- package/tsconfig.prod.json +20 -0
- package/vite.config.ts +80 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev-mode port resolution utilities.
|
|
3
|
+
*
|
|
4
|
+
* Provides a `listen` wrapper that automatically retries the next port when
|
|
5
|
+
* the requested one is already in use, and writes the resolved port to a
|
|
6
|
+
* well-known temp file so the CLI / frontend can discover it.
|
|
7
|
+
*
|
|
8
|
+
* Port affinity: when a port file already exists (e.g. after a tsx watch
|
|
9
|
+
* restart), the saved port is tried FIRST so the backend stays on the same
|
|
10
|
+
* port the frontend was configured with.
|
|
11
|
+
*
|
|
12
|
+
* This module is dev-only and should never run in production.
|
|
13
|
+
*/
|
|
14
|
+
import type { Server } from "http";
|
|
15
|
+
import path from "path";
|
|
16
|
+
import fs from "fs";
|
|
17
|
+
|
|
18
|
+
const MAX_PORT_ATTEMPTS = 20;
|
|
19
|
+
|
|
20
|
+
/** Filename written next to the project `.env` so the CLI can read it. */
|
|
21
|
+
export const DEV_PORT_FILENAME = ".rebase-dev-port";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Try to `listen` on `startPort`. If the port is busy (`EADDRINUSE`), increment
|
|
25
|
+
* and retry up to `maxAttempts` times.
|
|
26
|
+
*
|
|
27
|
+
* When a port file already exists (written by a previous run), the saved port
|
|
28
|
+
* is tried first to maintain port affinity across tsx watch restarts.
|
|
29
|
+
*
|
|
30
|
+
* Resolves with the port that was actually bound.
|
|
31
|
+
*/
|
|
32
|
+
export function listenWithPortRetry(
|
|
33
|
+
server: Server,
|
|
34
|
+
startPort: number,
|
|
35
|
+
options?: {
|
|
36
|
+
host?: string;
|
|
37
|
+
maxAttempts?: number;
|
|
38
|
+
/** Absolute path to write the resolved port file into. Defaults to `process.cwd()`. */
|
|
39
|
+
portFileDir?: string;
|
|
40
|
+
}
|
|
41
|
+
): Promise<number> {
|
|
42
|
+
const host = options?.host ?? "0.0.0.0";
|
|
43
|
+
const maxAttempts = options?.maxAttempts ?? MAX_PORT_ATTEMPTS;
|
|
44
|
+
const portFileDir = options?.portFileDir;
|
|
45
|
+
|
|
46
|
+
// Read affinity port from a previous run's port file.
|
|
47
|
+
// This ensures tsx watch restarts land on the same port the frontend was
|
|
48
|
+
// configured with, even if the CLI-computed port was different.
|
|
49
|
+
let affinityPort: number | null = null;
|
|
50
|
+
if (portFileDir) {
|
|
51
|
+
try {
|
|
52
|
+
const portFile = path.join(portFileDir, DEV_PORT_FILENAME);
|
|
53
|
+
if (fs.existsSync(portFile)) {
|
|
54
|
+
const saved = parseInt(fs.readFileSync(portFile, "utf-8").trim(), 10);
|
|
55
|
+
if (saved > 0 && saved < 65536 && saved !== startPort) {
|
|
56
|
+
affinityPort = saved;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} catch { /* ignore */ }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return new Promise<number>((resolve, reject) => {
|
|
63
|
+
let attempt = 0;
|
|
64
|
+
// Build the ordered list of ports to try:
|
|
65
|
+
// 1. The affinity port (if different from startPort)
|
|
66
|
+
// 2. startPort, startPort+1, startPort+2, ...
|
|
67
|
+
const portsToTry: number[] = [];
|
|
68
|
+
if (affinityPort) portsToTry.push(affinityPort);
|
|
69
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
70
|
+
const p = startPort + i;
|
|
71
|
+
if (p !== affinityPort) portsToTry.push(p);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function tryNext(index: number) {
|
|
75
|
+
if (index >= portsToTry.length) {
|
|
76
|
+
reject(new Error(
|
|
77
|
+
"All attempted ports are in use. " +
|
|
78
|
+
"Stop other Rebase instances or specify a different port with --port."
|
|
79
|
+
));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const port = portsToTry[index];
|
|
84
|
+
attempt++;
|
|
85
|
+
|
|
86
|
+
const onError = (err: NodeJS.ErrnoException) => {
|
|
87
|
+
if (err.code === "EADDRINUSE") {
|
|
88
|
+
server.removeListener("error", onError);
|
|
89
|
+
tryNext(index + 1);
|
|
90
|
+
} else {
|
|
91
|
+
reject(err);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
server.once("error", onError);
|
|
96
|
+
|
|
97
|
+
server.listen(port, host, () => {
|
|
98
|
+
server.removeListener("error", onError);
|
|
99
|
+
|
|
100
|
+
// Write the port file so the CLI can pick it up
|
|
101
|
+
if (portFileDir) {
|
|
102
|
+
try {
|
|
103
|
+
const portFile = path.join(portFileDir, DEV_PORT_FILENAME);
|
|
104
|
+
fs.writeFileSync(portFile, String(port), "utf-8");
|
|
105
|
+
} catch {
|
|
106
|
+
// Non-fatal — the CLI will fall back to parsing stdout
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Write .rebase/state.json so external scripts can discover
|
|
110
|
+
// the running server port, URL, etc.
|
|
111
|
+
writeStateFile(portFileDir, port);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
resolve(port);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
tryNext(0);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Clean up the dev port file and state file (call on graceful shutdown).
|
|
124
|
+
*/
|
|
125
|
+
export function cleanupDevPortFile(dir: string): void {
|
|
126
|
+
try {
|
|
127
|
+
const portFile = path.join(dir, DEV_PORT_FILENAME);
|
|
128
|
+
if (fs.existsSync(portFile)) {
|
|
129
|
+
fs.unlinkSync(portFile);
|
|
130
|
+
}
|
|
131
|
+
} catch {
|
|
132
|
+
// ignore
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const stateFile = path.join(dir, ".rebase", "state.json");
|
|
136
|
+
if (fs.existsSync(stateFile)) {
|
|
137
|
+
fs.unlinkSync(stateFile);
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
// ignore
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Write `.rebase/state.json` with runtime info for external scripts.
|
|
146
|
+
*
|
|
147
|
+
* Scripts can read this file to discover:
|
|
148
|
+
* - `port` — the actual port the backend is listening on
|
|
149
|
+
* - `baseUrl` — full URL including protocol and port
|
|
150
|
+
* - `pid` — the backend process ID
|
|
151
|
+
* - `startedAt` — ISO timestamp of when the server started
|
|
152
|
+
*
|
|
153
|
+
* @example Reading from a script:
|
|
154
|
+
* ```ts
|
|
155
|
+
* const state = JSON.parse(fs.readFileSync('.rebase/state.json', 'utf-8'));
|
|
156
|
+
* const apiUrl = state.baseUrl; // "http://localhost:3519"
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
function writeStateFile(projectRoot: string, port: number): void {
|
|
160
|
+
try {
|
|
161
|
+
const rebaseDir = path.join(projectRoot, ".rebase");
|
|
162
|
+
if (!fs.existsSync(rebaseDir)) {
|
|
163
|
+
fs.mkdirSync(rebaseDir, { recursive: true });
|
|
164
|
+
}
|
|
165
|
+
const stateFile = path.join(rebaseDir, "state.json");
|
|
166
|
+
const state = {
|
|
167
|
+
port,
|
|
168
|
+
baseUrl: `http://localhost:${port}`,
|
|
169
|
+
pid: process.pid,
|
|
170
|
+
startedAt: new Date().toISOString()
|
|
171
|
+
};
|
|
172
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2), "utf-8");
|
|
173
|
+
} catch {
|
|
174
|
+
// Non-fatal
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured Logger for Rebase Backend
|
|
3
|
+
*
|
|
4
|
+
* Outputs JSON lines when `NODE_ENV=production`, human-readable prefixed
|
|
5
|
+
* lines otherwise. Designed to work with Google Cloud Logging severity levels.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { logger } from "./utils/logger";
|
|
9
|
+
* logger.info("Server started", { port: 3001 });
|
|
10
|
+
* logger.error("Request failed", { path: "/api/test", error: err });
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
14
|
+
|
|
15
|
+
/** Google Cloud Logging severity strings. */
|
|
16
|
+
const GCP_SEVERITY: Record<LogLevel, string> = {
|
|
17
|
+
debug: "DEBUG",
|
|
18
|
+
info: "INFO",
|
|
19
|
+
warn: "WARNING",
|
|
20
|
+
error: "ERROR"
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const LOG_PRIORITY: Record<LogLevel, number> = {
|
|
24
|
+
debug: 0,
|
|
25
|
+
info: 1,
|
|
26
|
+
warn: 2,
|
|
27
|
+
error: 3
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export interface LogEntry {
|
|
31
|
+
severity: string;
|
|
32
|
+
message: string;
|
|
33
|
+
timestamp: string;
|
|
34
|
+
[key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface Logger {
|
|
38
|
+
debug(message: string, data?: Record<string, unknown>): void;
|
|
39
|
+
info(message: string, data?: Record<string, unknown>): void;
|
|
40
|
+
warn(message: string, data?: Record<string, unknown>): void;
|
|
41
|
+
error(message: string, data?: Record<string, unknown>): void;
|
|
42
|
+
child(defaultFields: Record<string, unknown>): Logger;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isProduction(): boolean {
|
|
46
|
+
return process.env.NODE_ENV === "production";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getMinLevel(): LogLevel {
|
|
50
|
+
const env = (process.env.LOG_LEVEL || "info").toLowerCase();
|
|
51
|
+
if (env in LOG_PRIORITY) return env as LogLevel;
|
|
52
|
+
return "info";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Serialise an Error into a plain object (stack + message).
|
|
57
|
+
* Handles non-Error values gracefully.
|
|
58
|
+
*/
|
|
59
|
+
function serialiseError(value: unknown): Record<string, unknown> {
|
|
60
|
+
if (value instanceof Error) {
|
|
61
|
+
return {
|
|
62
|
+
name: value.name,
|
|
63
|
+
message: value.message,
|
|
64
|
+
stack: value.stack
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return { value: String(value) };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function formatData(data?: Record<string, unknown>): Record<string, unknown> | undefined {
|
|
71
|
+
if (!data) return undefined;
|
|
72
|
+
const out: Record<string, unknown> = {};
|
|
73
|
+
for (const [key, val] of Object.entries(data)) {
|
|
74
|
+
if (val instanceof Error) {
|
|
75
|
+
out[key] = serialiseError(val);
|
|
76
|
+
} else {
|
|
77
|
+
out[key] = val;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function createLogger(defaultFields: Record<string, unknown> = {}): Logger {
|
|
84
|
+
const minLevel = getMinLevel();
|
|
85
|
+
|
|
86
|
+
function emit(level: LogLevel, message: string, data?: Record<string, unknown>): void {
|
|
87
|
+
if (LOG_PRIORITY[level] < LOG_PRIORITY[minLevel]) return;
|
|
88
|
+
|
|
89
|
+
const merged = { ...defaultFields,
|
|
90
|
+
...formatData(data) };
|
|
91
|
+
|
|
92
|
+
if (isProduction()) {
|
|
93
|
+
// Structured JSON for Cloud Logging
|
|
94
|
+
const entry: LogEntry = {
|
|
95
|
+
severity: GCP_SEVERITY[level],
|
|
96
|
+
message,
|
|
97
|
+
timestamp: new Date().toISOString(),
|
|
98
|
+
...merged
|
|
99
|
+
};
|
|
100
|
+
const line = JSON.stringify(entry);
|
|
101
|
+
|
|
102
|
+
if (level === "error") {
|
|
103
|
+
process.stderr.write(line + "\n");
|
|
104
|
+
} else {
|
|
105
|
+
process.stdout.write(line + "\n");
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
// Human-readable for development
|
|
109
|
+
const prefix = level === "error" ? "❌"
|
|
110
|
+
: level === "warn" ? "⚠️"
|
|
111
|
+
: level === "info" ? "ℹ️"
|
|
112
|
+
: "🐛";
|
|
113
|
+
const extra = Object.keys(merged).length > 0 ? ` ${JSON.stringify(merged)}` : "";
|
|
114
|
+
const out = `${prefix} [${level.toUpperCase()}] ${message}${extra}`;
|
|
115
|
+
|
|
116
|
+
if (level === "error") {
|
|
117
|
+
console.error(out);
|
|
118
|
+
} else if (level === "warn") {
|
|
119
|
+
console.warn(out);
|
|
120
|
+
} else {
|
|
121
|
+
console.log(out);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
debug: (msg, data) => emit("debug", msg, data),
|
|
128
|
+
info: (msg, data) => emit("info", msg, data),
|
|
129
|
+
warn: (msg, data) => emit("warn", msg, data),
|
|
130
|
+
error: (msg, data) => emit("error", msg, data),
|
|
131
|
+
child(fields: Record<string, unknown>): Logger {
|
|
132
|
+
return createLogger({ ...defaultFields,
|
|
133
|
+
...fields });
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Singleton logger instance.
|
|
140
|
+
* In production: emits JSON lines with `severity`, `message`, `timestamp`.
|
|
141
|
+
* In development: emits human-readable prefixed lines.
|
|
142
|
+
*/
|
|
143
|
+
export const logger: Logger = createLogger();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configure console log levels based on environment variable
|
|
3
|
+
* Call this early in your application to set up proper logging levels
|
|
4
|
+
*/
|
|
5
|
+
export function configureLogLevel(logLevel?: string) {
|
|
6
|
+
const LOG_LEVEL = logLevel || process.env.LOG_LEVEL || "info";
|
|
7
|
+
const logLevels = { error: 0,
|
|
8
|
+
warn: 1,
|
|
9
|
+
info: 2,
|
|
10
|
+
debug: 3 };
|
|
11
|
+
const currentLevel = logLevels[LOG_LEVEL as keyof typeof logLevels] ?? 2;
|
|
12
|
+
|
|
13
|
+
if (currentLevel < 3) console.debug = () => { };
|
|
14
|
+
if (currentLevel < 2) console.log = () => { };
|
|
15
|
+
if (currentLevel < 1) console.warn = () => { };
|
|
16
|
+
if (currentLevel < 0) console.error = () => { };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Reset console methods to their original state
|
|
21
|
+
*/
|
|
22
|
+
export function resetConsole() {
|
|
23
|
+
// Store original methods if not already stored
|
|
24
|
+
if (!(global as unknown as Record<string, unknown>).__originalConsole) {
|
|
25
|
+
(global as unknown as Record<string, unknown>).__originalConsole = {
|
|
26
|
+
log: console.log,
|
|
27
|
+
warn: console.warn,
|
|
28
|
+
error: console.error,
|
|
29
|
+
debug: console.debug
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const original = (global as unknown as Record<string, unknown>).__originalConsole as Console;
|
|
34
|
+
console.log = original.log;
|
|
35
|
+
console.warn = original.warn;
|
|
36
|
+
console.error = original.error;
|
|
37
|
+
console.debug = original.debug;
|
|
38
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured HTTP request logging middleware for Hono.
|
|
3
|
+
*
|
|
4
|
+
* Logs every request with method, path, status code, latency, and
|
|
5
|
+
* content-length. In production, outputs JSON for Cloud Logging; in
|
|
6
|
+
* development, emits a coloured one-liner.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { requestLogger } from "@rebasepro/server-core";
|
|
11
|
+
* app.use("/*", requestLogger());
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
import type { MiddlewareHandler } from "hono";
|
|
15
|
+
import { logger as log } from "./logger";
|
|
16
|
+
|
|
17
|
+
export interface RequestLoggerOptions {
|
|
18
|
+
/** Paths to skip logging (e.g. "/health"). Supports exact match. */
|
|
19
|
+
skip?: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function requestLogger(options?: RequestLoggerOptions): MiddlewareHandler {
|
|
23
|
+
const skipPaths = new Set(options?.skip ?? ["/health", "/favicon.ico"]);
|
|
24
|
+
|
|
25
|
+
return async (c, next) => {
|
|
26
|
+
const start = performance.now();
|
|
27
|
+
const method = c.req.method;
|
|
28
|
+
const path = c.req.path;
|
|
29
|
+
|
|
30
|
+
// Skip noisy endpoints
|
|
31
|
+
if (skipPaths.has(path)) {
|
|
32
|
+
return next();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await next();
|
|
36
|
+
|
|
37
|
+
const latencyMs = Math.round(performance.now() - start);
|
|
38
|
+
const status = c.res.status;
|
|
39
|
+
const contentLength = c.res.headers.get("content-length");
|
|
40
|
+
|
|
41
|
+
const data: Record<string, unknown> = {
|
|
42
|
+
method,
|
|
43
|
+
path,
|
|
44
|
+
status,
|
|
45
|
+
latencyMs
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
if (contentLength) {
|
|
49
|
+
data.contentLength = parseInt(contentLength, 10);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Extract user ID from context if auth middleware ran
|
|
53
|
+
const userId = c.get("userId" as never) as string | undefined;
|
|
54
|
+
if (userId) {
|
|
55
|
+
data.userId = userId;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (status >= 500) {
|
|
59
|
+
log.error("request", data);
|
|
60
|
+
} else if (status >= 400) {
|
|
61
|
+
log.warn("request", data);
|
|
62
|
+
} else {
|
|
63
|
+
log.info("request", data);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
package/src/utils/sql.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { sql, SQL } from "drizzle-orm";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns a SQL chunk calling `auth.uid()` — the current user's ID.
|
|
5
|
+
* This is a Supabase-style helper function created in the `auth` schema
|
|
6
|
+
* that reads `app.user_id` set per-transaction by `withAuth()`.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* sql`${table.user_id} = ${authUid()}`
|
|
10
|
+
*/
|
|
11
|
+
export const authUid = (): SQL => {
|
|
12
|
+
return sql`auth.uid()`;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns a SQL chunk calling `auth.roles()` — the current user's roles
|
|
17
|
+
* as a comma-separated string.
|
|
18
|
+
* Reads `app.user_roles` set per-transaction by `withAuth()`.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* sql`auth.roles() ~ 'admin'`
|
|
22
|
+
*/
|
|
23
|
+
export const authRoles = (): SQL => {
|
|
24
|
+
return sql`auth.roles()`;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Returns a SQL chunk calling `auth.jwt()` — the full JWT claims as JSONB.
|
|
29
|
+
* Reads `app.jwt` set per-transaction by `withAuth()`.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* sql`auth.jwt()->>'sub'`
|
|
33
|
+
*/
|
|
34
|
+
export const authJwt = (): SQL => {
|
|
35
|
+
return sql`auth.jwt()`;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
|