@rebasepro/server-core 0.0.1-canary.000dc36

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 (305) 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 +49249 -0
  80. package/dist/index.es.js.map +1 -0
  81. package/dist/index.umd.js +49283 -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 +76 -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 +21 -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 +106 -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 +168 -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 +46 -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 +195 -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/backend_hooks.d.ts +187 -0
  174. package/dist/types/src/types/builders.d.ts +15 -0
  175. package/dist/types/src/types/chips.d.ts +5 -0
  176. package/dist/types/src/types/collections.d.ts +857 -0
  177. package/dist/types/src/types/cron.d.ts +102 -0
  178. package/dist/types/src/types/data_source.d.ts +64 -0
  179. package/dist/types/src/types/entities.d.ts +145 -0
  180. package/dist/types/src/types/entity_actions.d.ts +98 -0
  181. package/dist/types/src/types/entity_callbacks.d.ts +173 -0
  182. package/dist/types/src/types/entity_link_builder.d.ts +7 -0
  183. package/dist/types/src/types/entity_overrides.d.ts +10 -0
  184. package/dist/types/src/types/entity_views.d.ts +59 -0
  185. package/dist/types/src/types/export_import.d.ts +21 -0
  186. package/dist/types/src/types/formex.d.ts +40 -0
  187. package/dist/types/src/types/index.d.ts +25 -0
  188. package/dist/types/src/types/locales.d.ts +4 -0
  189. package/dist/types/src/types/modify_collections.d.ts +5 -0
  190. package/dist/types/src/types/plugins.d.ts +282 -0
  191. package/dist/types/src/types/properties.d.ts +1148 -0
  192. package/dist/types/src/types/property_config.d.ts +70 -0
  193. package/dist/types/src/types/relations.d.ts +336 -0
  194. package/dist/types/src/types/slots.d.ts +262 -0
  195. package/dist/types/src/types/translations.d.ts +874 -0
  196. package/dist/types/src/types/user_management_delegate.d.ts +121 -0
  197. package/dist/types/src/types/websockets.d.ts +78 -0
  198. package/dist/types/src/users/index.d.ts +2 -0
  199. package/dist/types/src/users/roles.d.ts +22 -0
  200. package/dist/types/src/users/user.d.ts +46 -0
  201. package/history_diff.log +385 -0
  202. package/jest.config.cjs +16 -0
  203. package/package.json +86 -0
  204. package/scratch.ts +9 -0
  205. package/src/api/ast-schema-editor.ts +289 -0
  206. package/src/api/collections_for_test/callbacks_test_collection.ts +60 -0
  207. package/src/api/errors.ts +179 -0
  208. package/src/api/graphql/graphql-schema-generator.ts +336 -0
  209. package/src/api/graphql/index.ts +2 -0
  210. package/src/api/index.ts +11 -0
  211. package/src/api/openapi-generator.ts +715 -0
  212. package/src/api/rest/api-generator-count.test.ts +113 -0
  213. package/src/api/rest/api-generator.ts +573 -0
  214. package/src/api/rest/index.ts +2 -0
  215. package/src/api/rest/query-parser.ts +155 -0
  216. package/src/api/schema-editor-routes.ts +41 -0
  217. package/src/api/server.ts +249 -0
  218. package/src/api/types.ts +90 -0
  219. package/src/auth/admin-routes.ts +605 -0
  220. package/src/auth/apple-oauth.ts +120 -0
  221. package/src/auth/bitbucket-oauth.ts +82 -0
  222. package/src/auth/discord-oauth.ts +83 -0
  223. package/src/auth/facebook-oauth.ts +72 -0
  224. package/src/auth/github-oauth.ts +110 -0
  225. package/src/auth/gitlab-oauth.ts +70 -0
  226. package/src/auth/google-oauth.ts +48 -0
  227. package/src/auth/index.ts +34 -0
  228. package/src/auth/interfaces.ts +363 -0
  229. package/src/auth/jwt.ts +181 -0
  230. package/src/auth/linkedin-oauth.ts +81 -0
  231. package/src/auth/microsoft-oauth.ts +88 -0
  232. package/src/auth/middleware.ts +384 -0
  233. package/src/auth/password.ts +77 -0
  234. package/src/auth/rate-limiter.ts +133 -0
  235. package/src/auth/routes.ts +788 -0
  236. package/src/auth/slack-oauth.ts +71 -0
  237. package/src/auth/spotify-oauth.ts +67 -0
  238. package/src/auth/twitter-oauth.ts +120 -0
  239. package/src/bootstrappers/index.ts +1 -0
  240. package/src/collections/BackendCollectionRegistry.ts +20 -0
  241. package/src/collections/loader.ts +49 -0
  242. package/src/cron/cron-loader.ts +89 -0
  243. package/src/cron/cron-routes.test.ts +265 -0
  244. package/src/cron/cron-routes.ts +85 -0
  245. package/src/cron/cron-scheduler.test.ts +547 -0
  246. package/src/cron/cron-scheduler.ts +576 -0
  247. package/src/cron/cron-store.ts +163 -0
  248. package/src/cron/index.ts +6 -0
  249. package/src/db/interfaces.ts +60 -0
  250. package/src/email/index.ts +18 -0
  251. package/src/email/smtp-email-service.ts +91 -0
  252. package/src/email/templates.ts +388 -0
  253. package/src/email/types.ts +105 -0
  254. package/src/functions/function-loader.ts +119 -0
  255. package/src/functions/function-routes.ts +31 -0
  256. package/src/functions/index.ts +3 -0
  257. package/src/history/history-routes.ts +129 -0
  258. package/src/history/index.ts +2 -0
  259. package/src/index.ts +66 -0
  260. package/src/init.ts +737 -0
  261. package/src/serve-spa.ts +81 -0
  262. package/src/services/driver-registry.ts +182 -0
  263. package/src/singleton.test.ts +28 -0
  264. package/src/singleton.ts +70 -0
  265. package/src/storage/LocalStorageController.ts +365 -0
  266. package/src/storage/S3StorageController.ts +298 -0
  267. package/src/storage/index.ts +43 -0
  268. package/src/storage/routes.ts +264 -0
  269. package/src/storage/storage-registry.ts +187 -0
  270. package/src/storage/types.ts +134 -0
  271. package/src/types/index.ts +27 -0
  272. package/src/utils/dev-port.ts +176 -0
  273. package/src/utils/logger.ts +143 -0
  274. package/src/utils/logging.ts +38 -0
  275. package/src/utils/request-logger.ts +66 -0
  276. package/src/utils/sql.ts +38 -0
  277. package/test/admin-routes.test.ts +640 -0
  278. package/test/api-generator.test.ts +501 -0
  279. package/test/ast-schema-editor.test.ts +63 -0
  280. package/test/auth-middleware-hono.test.ts +556 -0
  281. package/test/auth-routes.test.ts +1047 -0
  282. package/test/backend-hooks-admin.test.ts +394 -0
  283. package/test/backend-hooks-data.test.ts +408 -0
  284. package/test/driver-registry.test.ts +282 -0
  285. package/test/error-propagation.test.ts +226 -0
  286. package/test/errors-hono.test.ts +133 -0
  287. package/test/errors.test.ts +155 -0
  288. package/test/jwt-security.test.ts +182 -0
  289. package/test/jwt.test.ts +324 -0
  290. package/test/middleware.test.ts +300 -0
  291. package/test/password.test.ts +165 -0
  292. package/test/query-parser.test.ts +263 -0
  293. package/test/rate-limiter.test.ts +102 -0
  294. package/test/safe-compare.test.ts +66 -0
  295. package/test/singleton.test.ts +59 -0
  296. package/test/storage-local.test.ts +271 -0
  297. package/test/storage-registry.test.ts +282 -0
  298. package/test/storage-routes.test.ts +222 -0
  299. package/test/storage-s3.test.ts +304 -0
  300. package/test-ast.ts +28 -0
  301. package/test.ts +6 -0
  302. package/test_output.txt +1133 -0
  303. package/tsconfig.json +49 -0
  304. package/tsconfig.prod.json +20 -0
  305. package/vite.config.ts +80 -0
@@ -0,0 +1,155 @@
1
+ import { QueryOptions } from "../types";
2
+
3
+ /**
4
+ * Map PostgREST-style operators to Rebase WhereFilterOp
5
+ */
6
+ export function mapOperator(op: string): string | null {
7
+ switch (op) {
8
+ case "eq": return "==";
9
+ case "neq": return "!=";
10
+ case "gt": return ">";
11
+ case "gte": return ">=";
12
+ case "lt": return "<";
13
+ case "lte": return "<=";
14
+ case "in": return "in";
15
+ case "nin": return "not-in";
16
+ case "cs": return "array-contains";
17
+ case "csa": return "array-contains-any";
18
+ default: return null;
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Parse query parameters into QueryOptions
24
+ */
25
+ export function parseQueryOptions(query: Record<string, unknown>): QueryOptions {
26
+ const options: QueryOptions = {};
27
+
28
+ // Pagination
29
+ if (query.limit) options.limit = parseInt(String(query.limit));
30
+ if (query.offset) options.offset = parseInt(String(query.offset));
31
+ if (query.page) {
32
+ const page = parseInt(String(query.page));
33
+ const limit = options.limit || 20;
34
+ options.offset = (page - 1) * limit;
35
+ }
36
+
37
+ // Filtering
38
+ options.where = {};
39
+
40
+ // Legacy JSON where clause
41
+ if (query.where) {
42
+ try {
43
+ const parsedWhere = typeof query.where === "string"
44
+ ? JSON.parse(query.where)
45
+ : query.where;
46
+ if (typeof parsedWhere !== "object" || parsedWhere === null || Array.isArray(parsedWhere)) {
47
+ throw new Error("Filter must be a JSON object");
48
+ }
49
+ Object.assign(options.where, parsedWhere);
50
+ } catch (e) {
51
+ const message = e instanceof Error ? e.message : "malformed JSON";
52
+ const err = new Error(`Invalid 'where' filter: ${message}`) as Error & { code?: string; statusCode?: number };
53
+ err.code = "BAD_REQUEST";
54
+ err.statusCode = 400;
55
+ throw err;
56
+ }
57
+ }
58
+
59
+ // PostgREST style filtering
60
+ const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "where", "include", "fields", "searchString"];
61
+ for (const [key, rawValue] of Object.entries(query)) {
62
+ if (reservedQueryKeys.includes(key)) continue;
63
+
64
+ const value = Array.isArray(rawValue) ? rawValue[rawValue.length - 1] : rawValue;
65
+
66
+ if (typeof value === "string") {
67
+ const parts = value.split(".");
68
+ if (parts.length >= 2) {
69
+ const op = parts[0];
70
+ const val = parts.slice(1).join(".");
71
+ const rebaseOp = mapOperator(op);
72
+
73
+ if (rebaseOp) {
74
+ let parsedVal: string | number | boolean | null | (string | number | boolean | null)[] = val;
75
+ // Attempt to parse primitive types or arrays
76
+ if (val === "true") parsedVal = true;
77
+ else if (val === "false") parsedVal = false;
78
+ else if (val === "null") parsedVal = null;
79
+ else if (!isNaN(Number(val)) && val.trim() !== "") parsedVal = Number(val);
80
+ else if (val.startsWith("(")) {
81
+ // Array for 'in' or 'not-in' ops (e.g. (1,2,3) or (a,b,c))
82
+ const arrayContent = val.endsWith(")") ? val.slice(1, -1) : val.slice(1);
83
+ parsedVal = arrayContent.split(",").map(v => {
84
+ const trimmed = v.trim();
85
+ if (!isNaN(Number(trimmed)) && trimmed !== "") return Number(trimmed);
86
+ if (trimmed === "true") return true;
87
+ if (trimmed === "false") return false;
88
+ if (trimmed === "null") return null;
89
+ return trimmed;
90
+ });
91
+ }
92
+
93
+ options.where[key] = [rebaseOp, parsedVal];
94
+ } else {
95
+ // Fallback: assume implicit eq if the dot wasn't an operator (e.g. email or float)
96
+ let parsedVal: string | number | boolean | null = value;
97
+ if (!isNaN(Number(value)) && value.trim() !== "") parsedVal = Number(value);
98
+ options.where[key] = ["==", parsedVal];
99
+ }
100
+ } else {
101
+ // Implicit eq
102
+ let parsedVal: string | number | boolean | null = value;
103
+ if (value === "true") parsedVal = true;
104
+ else if (value === "false") parsedVal = false;
105
+ else if (value === "null") parsedVal = null;
106
+ else if (!isNaN(Number(value)) && value.trim() !== "") parsedVal = Number(value);
107
+
108
+ options.where[key] = ["==", parsedVal];
109
+ }
110
+ }
111
+ }
112
+
113
+ if (Object.keys(options.where).length === 0) {
114
+ delete options.where;
115
+ }
116
+
117
+ // Sorting
118
+ if (query.orderBy) {
119
+ try {
120
+ options.orderBy = typeof query.orderBy === "string"
121
+ ? JSON.parse(query.orderBy)
122
+ : query.orderBy;
123
+ } catch {
124
+ // Try simple format: "field:direction"
125
+ if (typeof query.orderBy === "string") {
126
+ const [field, direction] = query.orderBy.split(":");
127
+ const dir = (direction === "desc" ? "desc" : "asc") as "asc" | "desc";
128
+ options.orderBy = [
129
+ {
130
+ field,
131
+ direction: dir
132
+ }
133
+ ];
134
+ }
135
+ }
136
+ }
137
+
138
+ // Relation includes
139
+ if (query.include) {
140
+ const includeStr = String(query.include).trim();
141
+ if (includeStr === "*") {
142
+ options.include = ["*"];
143
+ } else {
144
+ options.include = includeStr.split(",").map(s => s.trim()).filter(Boolean);
145
+ }
146
+ }
147
+
148
+ // Field selection
149
+ if (query.fields) {
150
+ const fieldsStr = String(query.fields).trim();
151
+ options.fields = fieldsStr.split(",").map(s => s.trim()).filter(Boolean);
152
+ }
153
+
154
+ return options;
155
+ }
@@ -0,0 +1,41 @@
1
+ import { Hono } from "hono";
2
+ import { AstSchemaEditor } from "./ast-schema-editor";
3
+ import { errorHandler } from "./errors";
4
+ import { HonoEnv } from "./types";
5
+
6
+ export function createSchemaEditorRoutes(collectionsDir: string): Hono<HonoEnv> {
7
+ const router = new Hono<HonoEnv>();
8
+ router.onError(errorHandler);
9
+ const editor = new AstSchemaEditor(collectionsDir);
10
+
11
+ router.post("/property/save", async (c) => {
12
+ const body = await c.req.json();
13
+ const { collectionId, propertyKey, propertyConfig } = body;
14
+ await editor.saveProperty(collectionId, propertyKey, propertyConfig);
15
+ return c.json({ success: true });
16
+ });
17
+
18
+ router.post("/property/delete", async (c) => {
19
+ const body = await c.req.json();
20
+ const { collectionId, propertyKey } = body;
21
+ await editor.deleteProperty(collectionId, propertyKey);
22
+ return c.json({ success: true });
23
+ });
24
+
25
+ router.post("/collection/save", async (c) => {
26
+ const body = await c.req.json();
27
+ const { collectionId, collectionData } = body;
28
+ await editor.saveCollection(collectionId, collectionData);
29
+ return c.json({ success: true });
30
+ });
31
+
32
+ router.post("/collection/delete", async (c) => {
33
+ const body = await c.req.json();
34
+ const { collectionId } = body;
35
+ await editor.deleteCollection(collectionId);
36
+ return c.json({ success: true });
37
+ });
38
+
39
+ return router;
40
+ }
41
+
@@ -0,0 +1,249 @@
1
+ import { Hono } from "hono";
2
+ import { cors } from "hono/cors";
3
+ import { secureHeaders } from "hono/secure-headers";
4
+ import { graphqlServer } from "@hono/graphql-server";
5
+ import { serve } from "@hono/node-server";
6
+ import { GraphQLSchemaGenerator } from "./graphql/graphql-schema-generator";
7
+ import { RestApiGenerator } from "./rest/api-generator";
8
+ import { DataDriver, EntityCollection, Relation } from "@rebasepro/types";
9
+ import { ApiConfig, HonoEnv } from "./types";
10
+ import { loadCollectionsFromDirectory } from "../collections/loader";
11
+ import { createSchemaEditorRoutes } from "./schema-editor-routes";
12
+ import { createAuthMiddleware, requireAuth, requireAdmin } from "../auth/middleware";
13
+ import { errorHandler } from "./errors";
14
+ import { generateOpenApiSpec } from "./openapi-generator";
15
+
16
+ /**
17
+ * Simplified API server that leverages existing Rebase infrastructure
18
+ * Can be used standalone or mounted on existing Hono app
19
+ */
20
+ export class RebaseApiServer {
21
+ private app: Hono<HonoEnv>;
22
+ private router: Hono<HonoEnv>;
23
+ private config: ApiConfig;
24
+ private driver: DataDriver;
25
+
26
+ private constructor(config: ApiConfig & { driver: DataDriver }) {
27
+ this.config = {
28
+ basePath: "/api",
29
+ enableGraphQL: true,
30
+ enableREST: true,
31
+ pagination: {
32
+ defaultLimit: 20,
33
+ maxLimit: 100
34
+ },
35
+ ...config
36
+ };
37
+
38
+ this.driver = config.driver;
39
+
40
+ this.app = new Hono<HonoEnv>();
41
+ this.router = new Hono<HonoEnv>();
42
+
43
+ this.setupMiddleware();
44
+ }
45
+
46
+ /**
47
+ * Factory method to create an asynchronously initialized ApiServer instance
48
+ */
49
+ public static async create(config: ApiConfig & { driver: DataDriver }): Promise<RebaseApiServer> {
50
+ if (config.collectionsDir && (!config.collections || config.collections.length === 0)) {
51
+ config.collections = await loadCollectionsFromDirectory(config.collectionsDir);
52
+ } else if (!config.collections) {
53
+ config.collections = [];
54
+ }
55
+
56
+ const server = new RebaseApiServer(config);
57
+ server.setupRoutes();
58
+ // Since we mount routes directly to router, we can let consumer attach it
59
+ server.app.route("/", server.router);
60
+
61
+ // Hono global error handler on the root app
62
+ server.app.onError(errorHandler);
63
+ server.router.onError(errorHandler);
64
+
65
+ return server;
66
+ }
67
+
68
+ /**
69
+ * Setup Hono middleware
70
+ */
71
+ private setupMiddleware(): void {
72
+ // Security headers
73
+ this.router.use("/*", secureHeaders());
74
+
75
+ // CORS — only applied if explicitly configured via `cors` option.
76
+ // If omitted, the user is expected to configure CORS on their own
77
+ // Hono app before mounting the API (recommended approach).
78
+ if (this.config.cors) {
79
+ const origin = this.config.cors.origin;
80
+ this.router.use("/*", cors({
81
+ origin: typeof origin === "boolean"
82
+ ? (origin ? ((o: string) => o) : "")
83
+ : (origin ?? "*"),
84
+ credentials: this.config.cors.credentials ?? false
85
+ }));
86
+ }
87
+
88
+ // Auth middleware
89
+ this.router.use("/*", createAuthMiddleware({
90
+ driver: this.driver,
91
+ requireAuth: this.config.requireAuth ?? true,
92
+ validator: this.config.authValidator
93
+ }));
94
+ }
95
+
96
+ /**
97
+ * Setup API routes using existing services
98
+ */
99
+ private setupRoutes(): void {
100
+ const basePath = this.config.basePath!;
101
+
102
+ // Health check
103
+ this.router.get(`${basePath}/health`, (c) => {
104
+ return c.json({
105
+ status: "healthy",
106
+ timestamp: new Date().toISOString(),
107
+ collections: this.config.collections?.map((col: EntityCollection) => col.slug) || [],
108
+ driver: this.driver.key
109
+ });
110
+ });
111
+
112
+ // Collections metadata endpoint
113
+ this.router.get(`${basePath}/collections`, (c) => {
114
+ const collectionsMetadata = (this.config.collections || []).map((col: EntityCollection) => ({
115
+ slug: col.slug,
116
+ name: col.name,
117
+ singularName: col.singularName,
118
+ description: col.description,
119
+ properties: Object.keys(col.properties),
120
+ relations: (col as EntityCollection & { relations?: Relation[] }).relations?.map((r: Relation) => ({
121
+ relationName: r.relationName,
122
+ target: typeof r.target === "function" ? r.target().slug : r.target,
123
+ cardinality: r.cardinality,
124
+ direction: r.direction
125
+ })) || []
126
+ }));
127
+
128
+ return c.json({ data: collectionsMetadata });
129
+ });
130
+
131
+ // GraphQL endpoint
132
+ if (this.config.enableGraphQL) {
133
+ const schemaGenerator = new GraphQLSchemaGenerator(this.config.collections || [], this.driver);
134
+ const schema = schemaGenerator.generateSchema();
135
+
136
+ // Context is automatically passed to resolvers via contextValue containing Hono's 'c'
137
+ this.router.use(`${basePath}/graphql`, graphqlServer({
138
+ schema
139
+ }));
140
+
141
+ // Lightweight GraphiQL IDE
142
+ if (process.env.NODE_ENV !== "production") {
143
+ this.router.get(`${basePath}/graphiql`, (c) => {
144
+ return c.html(`<!DOCTYPE html>
145
+ <html>
146
+ <head>
147
+ <meta charset=utf-8/>
148
+ <title>Rebase GraphiQL</title>
149
+ <link rel="stylesheet" href="https://unpkg.com/graphiql/graphiql.min.css" />
150
+ <style>body,html,#graphiql{height:100%;margin:0;width:100%;}</style>
151
+ </head>
152
+ <body>
153
+ <div id="graphiql">Loading...</div>
154
+ <script crossorigin src="https://unpkg.com/react/umd/react.production.min.js"></script>
155
+ <script crossorigin src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
156
+ <script src="https://unpkg.com/graphiql/graphiql.min.js"></script>
157
+ <script>
158
+ const fetcher = GraphiQL.createFetcher({ url: '${basePath}/graphql' });
159
+ ReactDOM.render(
160
+ React.createElement(GraphiQL, { fetcher }),
161
+ document.getElementById('graphiql'),
162
+ );
163
+ </script>
164
+ </body>
165
+ </html>`);
166
+ });
167
+ }
168
+ }
169
+
170
+ if (this.config.enableREST) {
171
+ const restGenerator = new RestApiGenerator(this.config.collections || [], this.driver);
172
+ const restRoutes = restGenerator.generateRoutes();
173
+ this.router.route(basePath, restRoutes);
174
+ }
175
+
176
+ // Schema Editor endpoints
177
+ if (this.config.collectionsDir) {
178
+ if (process.env.NODE_ENV === "production") {
179
+ console.warn("[RebaseApiServer] Schema Editor is disabled in production environments for security.");
180
+ } else {
181
+ // Auth middlewares applied to schema-editor via the router prefix
182
+ // MUST be declared before .route() so they execute first
183
+ this.router.use(`${basePath}/schema-editor/*`, requireAuth, requireAdmin);
184
+ const schemaEditorRoutes = createSchemaEditorRoutes(this.config.collectionsDir);
185
+ this.router.route(`${basePath}/schema-editor`, schemaEditorRoutes);
186
+ }
187
+ }
188
+
189
+ // OpenAPI endpoint
190
+ this.router.get(`${basePath}/docs`, (c) => {
191
+ const openApiSpec = generateOpenApiSpec(this.config.collections || [], {
192
+ basePath: this.config.basePath,
193
+ requireAuth: this.config.requireAuth ?? true
194
+ });
195
+ return c.json(openApiSpec);
196
+ });
197
+
198
+ // Simple Swagger UI
199
+ if (process.env.NODE_ENV !== "production") {
200
+ this.router.get(`${basePath}/swagger`, (c) => {
201
+ return c.html(`
202
+ <!DOCTYPE html>
203
+ <html>
204
+ <head>
205
+ <title>Rebase API Documentation</title>
206
+ <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui.css" />
207
+ </head>
208
+ <body>
209
+ <div id="swagger-ui"></div>
210
+ <script src="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui-bundle.js"></script>
211
+ <script>
212
+ SwaggerUIBundle({
213
+ url: '${basePath}/docs',
214
+ dom_id: '#swagger-ui'
215
+ });
216
+ </script>
217
+ </body>
218
+ </html>
219
+ `);
220
+ });
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Get the Hono router with all API routes
226
+ */
227
+ getRouter(): Hono<HonoEnv> {
228
+ return this.router;
229
+ }
230
+
231
+ /**
232
+ * Get the standalone Hono app
233
+ */
234
+ getApp(): Hono<HonoEnv> {
235
+ return this.app;
236
+ }
237
+
238
+ /**
239
+ * Start the server (standalone mode) via @hono/node-server
240
+ */
241
+ listen(port = 3000, callback?: () => void): void {
242
+ serve({
243
+ fetch: this.app.fetch,
244
+ port
245
+ }, () => {
246
+ if (callback) callback();
247
+ });
248
+ }
249
+ }
@@ -0,0 +1,90 @@
1
+ import { EntityCollection } from "@rebasepro/types";
2
+ import { AuthResult } from "../auth/middleware";
3
+ import { NodePgDatabase } from "drizzle-orm/node-postgres";
4
+ import { DataDriver } from "@rebasepro/types";
5
+
6
+ /**
7
+ * Hono Environment Variables
8
+ * Passed to generic Hono<HonoEnv> to type `c.get()`
9
+ */
10
+ export type HonoEnv = {
11
+ Variables: {
12
+ user?: AuthResult | { userId?: string, roles?: string[] };
13
+ driver?: DataDriver;
14
+ }
15
+ };
16
+
17
+ /**
18
+ * Configuration for API generation
19
+ */
20
+ /**
21
+ * Configuration for API generation
22
+ */
23
+ export interface ApiConfig {
24
+ collections?: EntityCollection[];
25
+ collectionsDir?: string;
26
+ basePath?: string;
27
+ enableGraphQL?: boolean;
28
+ enableREST?: boolean;
29
+ cors?: {
30
+ origin?: string | string[] | boolean;
31
+ credentials?: boolean;
32
+ };
33
+ /** Whether auth is required for API endpoints (default: true) */
34
+ requireAuth?: boolean;
35
+ /** Optional custom validator for authentication */
36
+ authValidator?: (c: import("hono").Context<import("./types").HonoEnv>) => Promise<AuthResult>;
37
+ pagination?: {
38
+ defaultLimit: number;
39
+ maxLimit: number;
40
+ };
41
+ }
42
+
43
+ /**
44
+ * Context passed to resolvers and handlers
45
+ */
46
+ export interface ApiContext {
47
+ user?: AuthResult;
48
+ collections: Map<string, EntityCollection>;
49
+ db: NodePgDatabase; // Drizzle DB instance
50
+ }
51
+
52
+ /**
53
+ * Standard API response format
54
+ */
55
+ export interface ApiResponse<T = unknown> {
56
+ data?: T;
57
+ error?: {
58
+ message: string;
59
+ code?: string;
60
+ details?: unknown;
61
+ };
62
+ meta?: {
63
+ total?: number;
64
+ page?: number;
65
+ limit?: number;
66
+ hasMore?: boolean;
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Query options for API endpoints
72
+ */
73
+ export interface QueryOptions {
74
+ limit?: number;
75
+ offset?: number;
76
+ where?: Record<string, unknown>;
77
+ orderBy?: Array<{ field: string; direction: "asc" | "desc" }>;
78
+ include?: string[];
79
+ /** Columns to return in the response (field-level selection) */
80
+ fields?: string[];
81
+ }
82
+
83
+ /**
84
+ * Relation resolution configuration
85
+ */
86
+ export interface RelationConfig {
87
+ relationName: string;
88
+ depth?: number;
89
+ include?: string[];
90
+ }