@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.
Files changed (300) hide show
  1. package/LICENSE +6 -0
  2. package/README.md +40 -0
  3. package/build-errors.txt +52 -0
  4. package/coverage/clover.xml +3739 -0
  5. package/coverage/coverage-final.json +31 -0
  6. package/coverage/lcov-report/base.css +224 -0
  7. package/coverage/lcov-report/block-navigation.js +87 -0
  8. package/coverage/lcov-report/favicon.png +0 -0
  9. package/coverage/lcov-report/index.html +266 -0
  10. package/coverage/lcov-report/prettify.css +1 -0
  11. package/coverage/lcov-report/prettify.js +2 -0
  12. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  13. package/coverage/lcov-report/sorter.js +210 -0
  14. package/coverage/lcov-report/src/api/ast-schema-editor.ts.html +952 -0
  15. package/coverage/lcov-report/src/api/errors.ts.html +472 -0
  16. package/coverage/lcov-report/src/api/graphql/graphql-schema-generator.ts.html +1069 -0
  17. package/coverage/lcov-report/src/api/graphql/index.html +116 -0
  18. package/coverage/lcov-report/src/api/index.html +176 -0
  19. package/coverage/lcov-report/src/api/openapi-generator.ts.html +565 -0
  20. package/coverage/lcov-report/src/api/rest/api-generator.ts.html +994 -0
  21. package/coverage/lcov-report/src/api/rest/index.html +131 -0
  22. package/coverage/lcov-report/src/api/rest/query-parser.ts.html +550 -0
  23. package/coverage/lcov-report/src/api/schema-editor-routes.ts.html +202 -0
  24. package/coverage/lcov-report/src/api/server.ts.html +823 -0
  25. package/coverage/lcov-report/src/auth/admin-routes.ts.html +973 -0
  26. package/coverage/lcov-report/src/auth/index.html +176 -0
  27. package/coverage/lcov-report/src/auth/jwt.ts.html +574 -0
  28. package/coverage/lcov-report/src/auth/middleware.ts.html +745 -0
  29. package/coverage/lcov-report/src/auth/password.ts.html +310 -0
  30. package/coverage/lcov-report/src/auth/services.ts.html +2074 -0
  31. package/coverage/lcov-report/src/collections/index.html +116 -0
  32. package/coverage/lcov-report/src/collections/loader.ts.html +232 -0
  33. package/coverage/lcov-report/src/db/auth-schema.ts.html +523 -0
  34. package/coverage/lcov-report/src/db/data-transformer.ts.html +1753 -0
  35. package/coverage/lcov-report/src/db/entityService.ts.html +700 -0
  36. package/coverage/lcov-report/src/db/index.html +146 -0
  37. package/coverage/lcov-report/src/db/services/EntityFetchService.ts.html +4048 -0
  38. package/coverage/lcov-report/src/db/services/EntityPersistService.ts.html +883 -0
  39. package/coverage/lcov-report/src/db/services/RelationService.ts.html +3121 -0
  40. package/coverage/lcov-report/src/db/services/entity-helpers.ts.html +442 -0
  41. package/coverage/lcov-report/src/db/services/index.html +176 -0
  42. package/coverage/lcov-report/src/db/services/index.ts.html +124 -0
  43. package/coverage/lcov-report/src/generate-drizzle-schema-logic.ts.html +1960 -0
  44. package/coverage/lcov-report/src/index.html +116 -0
  45. package/coverage/lcov-report/src/services/driver-registry.ts.html +631 -0
  46. package/coverage/lcov-report/src/services/index.html +131 -0
  47. package/coverage/lcov-report/src/services/postgresDataDriver.ts.html +3025 -0
  48. package/coverage/lcov-report/src/storage/LocalStorageController.ts.html +1189 -0
  49. package/coverage/lcov-report/src/storage/S3StorageController.ts.html +970 -0
  50. package/coverage/lcov-report/src/storage/index.html +161 -0
  51. package/coverage/lcov-report/src/storage/storage-registry.ts.html +646 -0
  52. package/coverage/lcov-report/src/storage/types.ts.html +451 -0
  53. package/coverage/lcov-report/src/utils/drizzle-conditions.ts.html +3082 -0
  54. package/coverage/lcov-report/src/utils/index.html +116 -0
  55. package/coverage/lcov.info +7179 -0
  56. package/dist/common/src/collections/CollectionRegistry.d.ts +56 -0
  57. package/dist/common/src/collections/index.d.ts +1 -0
  58. package/dist/common/src/data/buildRebaseData.d.ts +14 -0
  59. package/dist/common/src/index.d.ts +3 -0
  60. package/dist/common/src/util/builders.d.ts +57 -0
  61. package/dist/common/src/util/callbacks.d.ts +6 -0
  62. package/dist/common/src/util/collections.d.ts +11 -0
  63. package/dist/common/src/util/common.d.ts +2 -0
  64. package/dist/common/src/util/conditions.d.ts +26 -0
  65. package/dist/common/src/util/entities.d.ts +58 -0
  66. package/dist/common/src/util/enums.d.ts +3 -0
  67. package/dist/common/src/util/index.d.ts +16 -0
  68. package/dist/common/src/util/navigation_from_path.d.ts +34 -0
  69. package/dist/common/src/util/navigation_utils.d.ts +20 -0
  70. package/dist/common/src/util/parent_references_from_path.d.ts +6 -0
  71. package/dist/common/src/util/paths.d.ts +14 -0
  72. package/dist/common/src/util/permissions.d.ts +5 -0
  73. package/dist/common/src/util/references.d.ts +2 -0
  74. package/dist/common/src/util/relations.d.ts +22 -0
  75. package/dist/common/src/util/resolutions.d.ts +72 -0
  76. package/dist/common/src/util/storage.d.ts +24 -0
  77. package/dist/index-DXVBFp5V.js +37 -0
  78. package/dist/index-DXVBFp5V.js.map +1 -0
  79. package/dist/index.es.js +49934 -0
  80. package/dist/index.es.js.map +1 -0
  81. package/dist/index.umd.js +49968 -0
  82. package/dist/index.umd.js.map +1 -0
  83. package/dist/server-core/src/api/ast-schema-editor.d.ts +21 -0
  84. package/dist/server-core/src/api/collections_for_test/callbacks_test_collection.d.ts +2 -0
  85. package/dist/server-core/src/api/errors.d.ts +35 -0
  86. package/dist/server-core/src/api/graphql/graphql-schema-generator.d.ts +35 -0
  87. package/dist/server-core/src/api/graphql/index.d.ts +1 -0
  88. package/dist/server-core/src/api/index.d.ts +9 -0
  89. package/dist/server-core/src/api/openapi-generator.d.ts +16 -0
  90. package/dist/server-core/src/api/rest/api-generator.d.ts +64 -0
  91. package/dist/server-core/src/api/rest/index.d.ts +1 -0
  92. package/dist/server-core/src/api/rest/query-parser.d.ts +9 -0
  93. package/dist/server-core/src/api/schema-editor-routes.d.ts +3 -0
  94. package/dist/server-core/src/api/server.d.ts +40 -0
  95. package/dist/server-core/src/api/types.d.ts +90 -0
  96. package/dist/server-core/src/auth/admin-routes.d.ts +16 -0
  97. package/dist/server-core/src/auth/apple-oauth.d.ts +30 -0
  98. package/dist/server-core/src/auth/bitbucket-oauth.d.ts +11 -0
  99. package/dist/server-core/src/auth/discord-oauth.d.ts +14 -0
  100. package/dist/server-core/src/auth/facebook-oauth.d.ts +14 -0
  101. package/dist/server-core/src/auth/github-oauth.d.ts +15 -0
  102. package/dist/server-core/src/auth/gitlab-oauth.d.ts +13 -0
  103. package/dist/server-core/src/auth/google-oauth.d.ts +14 -0
  104. package/dist/server-core/src/auth/index.d.ts +23 -0
  105. package/dist/server-core/src/auth/interfaces.d.ts +309 -0
  106. package/dist/server-core/src/auth/jwt.d.ts +43 -0
  107. package/dist/server-core/src/auth/linkedin-oauth.d.ts +18 -0
  108. package/dist/server-core/src/auth/microsoft-oauth.d.ts +16 -0
  109. package/dist/server-core/src/auth/middleware.d.ts +81 -0
  110. package/dist/server-core/src/auth/password.d.ts +22 -0
  111. package/dist/server-core/src/auth/rate-limiter.d.ts +31 -0
  112. package/dist/server-core/src/auth/routes.d.ts +27 -0
  113. package/dist/server-core/src/auth/slack-oauth.d.ts +12 -0
  114. package/dist/server-core/src/auth/spotify-oauth.d.ts +12 -0
  115. package/dist/server-core/src/auth/twitter-oauth.d.ts +18 -0
  116. package/dist/server-core/src/bootstrappers/index.d.ts +0 -0
  117. package/dist/server-core/src/collections/BackendCollectionRegistry.d.ts +13 -0
  118. package/dist/server-core/src/collections/loader.d.ts +5 -0
  119. package/dist/server-core/src/cron/cron-loader.d.ts +17 -0
  120. package/dist/server-core/src/cron/cron-routes.d.ts +14 -0
  121. package/dist/server-core/src/cron/cron-scheduler.d.ts +61 -0
  122. package/dist/server-core/src/cron/cron-store.d.ts +32 -0
  123. package/dist/server-core/src/cron/index.d.ts +6 -0
  124. package/dist/server-core/src/db/interfaces.d.ts +18 -0
  125. package/dist/server-core/src/email/index.d.ts +6 -0
  126. package/dist/server-core/src/email/smtp-email-service.d.ts +25 -0
  127. package/dist/server-core/src/email/templates.d.ts +42 -0
  128. package/dist/server-core/src/email/types.d.ts +107 -0
  129. package/dist/server-core/src/functions/function-loader.d.ts +17 -0
  130. package/dist/server-core/src/functions/function-routes.d.ts +10 -0
  131. package/dist/server-core/src/functions/index.d.ts +3 -0
  132. package/dist/server-core/src/history/history-routes.d.ts +23 -0
  133. package/dist/server-core/src/history/index.d.ts +1 -0
  134. package/dist/server-core/src/index.d.ts +29 -0
  135. package/dist/server-core/src/init.d.ts +159 -0
  136. package/dist/server-core/src/serve-spa.d.ts +30 -0
  137. package/dist/server-core/src/services/driver-registry.d.ts +78 -0
  138. package/dist/server-core/src/singleton.d.ts +35 -0
  139. package/dist/server-core/src/storage/LocalStorageController.d.ts +46 -0
  140. package/dist/server-core/src/storage/S3StorageController.d.ts +36 -0
  141. package/dist/server-core/src/storage/index.d.ts +25 -0
  142. package/dist/server-core/src/storage/routes.d.ts +38 -0
  143. package/dist/server-core/src/storage/storage-registry.d.ts +78 -0
  144. package/dist/server-core/src/storage/types.d.ts +103 -0
  145. package/dist/server-core/src/types/index.d.ts +11 -0
  146. package/dist/server-core/src/utils/dev-port.d.ts +35 -0
  147. package/dist/server-core/src/utils/logger.d.ts +31 -0
  148. package/dist/server-core/src/utils/logging.d.ts +9 -0
  149. package/dist/server-core/src/utils/request-logger.d.ts +19 -0
  150. package/dist/server-core/src/utils/sql.d.ts +27 -0
  151. package/dist/types/src/controllers/analytics_controller.d.ts +7 -0
  152. package/dist/types/src/controllers/auth.d.ts +119 -0
  153. package/dist/types/src/controllers/client.d.ts +170 -0
  154. package/dist/types/src/controllers/collection_registry.d.ts +45 -0
  155. package/dist/types/src/controllers/customization_controller.d.ts +60 -0
  156. package/dist/types/src/controllers/data.d.ts +168 -0
  157. package/dist/types/src/controllers/data_driver.d.ts +160 -0
  158. package/dist/types/src/controllers/database_admin.d.ts +11 -0
  159. package/dist/types/src/controllers/dialogs_controller.d.ts +36 -0
  160. package/dist/types/src/controllers/effective_role.d.ts +4 -0
  161. package/dist/types/src/controllers/email.d.ts +34 -0
  162. package/dist/types/src/controllers/index.d.ts +18 -0
  163. package/dist/types/src/controllers/local_config_persistence.d.ts +20 -0
  164. package/dist/types/src/controllers/navigation.d.ts +213 -0
  165. package/dist/types/src/controllers/registry.d.ts +54 -0
  166. package/dist/types/src/controllers/side_dialogs_controller.d.ts +67 -0
  167. package/dist/types/src/controllers/side_entity_controller.d.ts +90 -0
  168. package/dist/types/src/controllers/snackbar.d.ts +24 -0
  169. package/dist/types/src/controllers/storage.d.ts +171 -0
  170. package/dist/types/src/index.d.ts +4 -0
  171. package/dist/types/src/rebase_context.d.ts +105 -0
  172. package/dist/types/src/types/backend.d.ts +536 -0
  173. package/dist/types/src/types/builders.d.ts +15 -0
  174. package/dist/types/src/types/chips.d.ts +5 -0
  175. package/dist/types/src/types/collections.d.ts +856 -0
  176. package/dist/types/src/types/cron.d.ts +102 -0
  177. package/dist/types/src/types/data_source.d.ts +64 -0
  178. package/dist/types/src/types/entities.d.ts +145 -0
  179. package/dist/types/src/types/entity_actions.d.ts +98 -0
  180. package/dist/types/src/types/entity_callbacks.d.ts +173 -0
  181. package/dist/types/src/types/entity_link_builder.d.ts +7 -0
  182. package/dist/types/src/types/entity_overrides.d.ts +10 -0
  183. package/dist/types/src/types/entity_views.d.ts +61 -0
  184. package/dist/types/src/types/export_import.d.ts +21 -0
  185. package/dist/types/src/types/index.d.ts +23 -0
  186. package/dist/types/src/types/locales.d.ts +4 -0
  187. package/dist/types/src/types/modify_collections.d.ts +5 -0
  188. package/dist/types/src/types/plugins.d.ts +279 -0
  189. package/dist/types/src/types/properties.d.ts +1176 -0
  190. package/dist/types/src/types/property_config.d.ts +70 -0
  191. package/dist/types/src/types/relations.d.ts +336 -0
  192. package/dist/types/src/types/slots.d.ts +252 -0
  193. package/dist/types/src/types/translations.d.ts +870 -0
  194. package/dist/types/src/types/user_management_delegate.d.ts +121 -0
  195. package/dist/types/src/types/websockets.d.ts +78 -0
  196. package/dist/types/src/users/index.d.ts +2 -0
  197. package/dist/types/src/users/roles.d.ts +22 -0
  198. package/dist/types/src/users/user.d.ts +46 -0
  199. package/history_diff.log +385 -0
  200. package/jest.config.cjs +16 -0
  201. package/package.json +86 -0
  202. package/scratch.ts +9 -0
  203. package/src/api/ast-schema-editor.ts +289 -0
  204. package/src/api/collections_for_test/callbacks_test_collection.ts +60 -0
  205. package/src/api/errors.ts +179 -0
  206. package/src/api/graphql/graphql-schema-generator.ts +336 -0
  207. package/src/api/graphql/index.ts +2 -0
  208. package/src/api/index.ts +11 -0
  209. package/src/api/openapi-generator.ts +715 -0
  210. package/src/api/rest/api-generator.ts +472 -0
  211. package/src/api/rest/index.ts +2 -0
  212. package/src/api/rest/query-parser.ts +155 -0
  213. package/src/api/schema-editor-routes.ts +41 -0
  214. package/src/api/server.ts +248 -0
  215. package/src/api/types.ts +90 -0
  216. package/src/auth/admin-routes.ts +529 -0
  217. package/src/auth/apple-oauth.ts +130 -0
  218. package/src/auth/bitbucket-oauth.ts +82 -0
  219. package/src/auth/discord-oauth.ts +83 -0
  220. package/src/auth/facebook-oauth.ts +72 -0
  221. package/src/auth/github-oauth.ts +110 -0
  222. package/src/auth/gitlab-oauth.ts +70 -0
  223. package/src/auth/google-oauth.ts +48 -0
  224. package/src/auth/index.ts +34 -0
  225. package/src/auth/interfaces.ts +363 -0
  226. package/src/auth/jwt.ts +181 -0
  227. package/src/auth/linkedin-oauth.ts +81 -0
  228. package/src/auth/microsoft-oauth.ts +88 -0
  229. package/src/auth/middleware.ts +384 -0
  230. package/src/auth/password.ts +77 -0
  231. package/src/auth/rate-limiter.ts +129 -0
  232. package/src/auth/routes.ts +788 -0
  233. package/src/auth/slack-oauth.ts +71 -0
  234. package/src/auth/spotify-oauth.ts +67 -0
  235. package/src/auth/twitter-oauth.ts +120 -0
  236. package/src/bootstrappers/index.ts +1 -0
  237. package/src/collections/BackendCollectionRegistry.ts +20 -0
  238. package/src/collections/loader.ts +49 -0
  239. package/src/cron/cron-loader.ts +89 -0
  240. package/src/cron/cron-routes.test.ts +265 -0
  241. package/src/cron/cron-routes.ts +85 -0
  242. package/src/cron/cron-scheduler.test.ts +421 -0
  243. package/src/cron/cron-scheduler.ts +413 -0
  244. package/src/cron/cron-store.ts +163 -0
  245. package/src/cron/index.ts +6 -0
  246. package/src/db/interfaces.ts +60 -0
  247. package/src/email/index.ts +18 -0
  248. package/src/email/smtp-email-service.ts +91 -0
  249. package/src/email/templates.ts +388 -0
  250. package/src/email/types.ts +105 -0
  251. package/src/functions/function-loader.ts +119 -0
  252. package/src/functions/function-routes.ts +31 -0
  253. package/src/functions/index.ts +3 -0
  254. package/src/history/history-routes.ts +129 -0
  255. package/src/history/index.ts +2 -0
  256. package/src/index.ts +66 -0
  257. package/src/init.ts +727 -0
  258. package/src/serve-spa.ts +81 -0
  259. package/src/services/driver-registry.ts +182 -0
  260. package/src/singleton.test.ts +28 -0
  261. package/src/singleton.ts +70 -0
  262. package/src/storage/LocalStorageController.ts +365 -0
  263. package/src/storage/S3StorageController.ts +298 -0
  264. package/src/storage/index.ts +43 -0
  265. package/src/storage/routes.ts +264 -0
  266. package/src/storage/storage-registry.ts +187 -0
  267. package/src/storage/types.ts +134 -0
  268. package/src/types/index.ts +27 -0
  269. package/src/utils/dev-port.ts +176 -0
  270. package/src/utils/logger.ts +143 -0
  271. package/src/utils/logging.ts +38 -0
  272. package/src/utils/request-logger.ts +66 -0
  273. package/src/utils/sql.ts +38 -0
  274. package/test/admin-routes.test.ts +640 -0
  275. package/test/api-generator.test.ts +501 -0
  276. package/test/ast-schema-editor.test.ts +63 -0
  277. package/test/auth-middleware-hono.test.ts +556 -0
  278. package/test/auth-routes.test.ts +1047 -0
  279. package/test/driver-registry.test.ts +282 -0
  280. package/test/error-propagation.test.ts +226 -0
  281. package/test/errors-hono.test.ts +133 -0
  282. package/test/errors.test.ts +155 -0
  283. package/test/jwt-security.test.ts +182 -0
  284. package/test/jwt.test.ts +324 -0
  285. package/test/middleware.test.ts +300 -0
  286. package/test/password.test.ts +165 -0
  287. package/test/query-parser.test.ts +263 -0
  288. package/test/rate-limiter.test.ts +102 -0
  289. package/test/safe-compare.test.ts +66 -0
  290. package/test/singleton.test.ts +59 -0
  291. package/test/storage-local.test.ts +271 -0
  292. package/test/storage-registry.test.ts +282 -0
  293. package/test/storage-routes.test.ts +222 -0
  294. package/test/storage-s3.test.ts +304 -0
  295. package/test-ast.ts +28 -0
  296. package/test.ts +6 -0
  297. package/test_output.txt +1133 -0
  298. package/tsconfig.json +49 -0
  299. package/tsconfig.prod.json +20 -0
  300. 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
+ }
@@ -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
+