@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,71 @@
1
+ import type { OAuthProvider, OAuthProviderProfile } from "./interfaces";
2
+ import { z } from "zod";
3
+
4
+ /**
5
+ * Creates a Slack OAuth Provider integration (OAuth 2.0 / "Sign in with Slack").
6
+ * Uses the OpenID Connect flow with the "openid,email,profile" scopes.
7
+ */
8
+ export function createSlackProvider(config: { clientId: string; clientSecret: string }): OAuthProvider<{ code: string; redirectUri: string }> {
9
+ return {
10
+ id: "slack",
11
+ schema: z.object({
12
+ code: z.string().min(1, "Auth code is required"),
13
+ redirectUri: z.string().url("Valid redirect URI is required")
14
+ }),
15
+ verify: async (payload: { code: string; redirectUri: string }): Promise<OAuthProviderProfile | null> => {
16
+ try {
17
+ const tokenResponse = await fetch("https://slack.com/api/openid.connect.token", {
18
+ method: "POST",
19
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
20
+ body: new URLSearchParams({
21
+ client_id: config.clientId,
22
+ client_secret: config.clientSecret,
23
+ code: payload.code,
24
+ redirect_uri: payload.redirectUri,
25
+ grant_type: "authorization_code"
26
+ })
27
+ });
28
+
29
+ if (!tokenResponse.ok) {
30
+ console.error("Failed to get Slack access token:", await tokenResponse.text());
31
+ return null;
32
+ }
33
+
34
+ const tokenData = await tokenResponse.json() as { ok: boolean; access_token?: string; error?: string };
35
+ if (!tokenData.ok || !tokenData.access_token) {
36
+ console.error("Slack token exchange failed:", tokenData.error);
37
+ return null;
38
+ }
39
+
40
+ const profileResponse = await fetch("https://slack.com/api/openid.connect.userInfo", {
41
+ headers: { "Authorization": `Bearer ${tokenData.access_token}` }
42
+ });
43
+
44
+ if (!profileResponse.ok) {
45
+ console.error("Failed to get Slack user info:", await profileResponse.text());
46
+ return null;
47
+ }
48
+
49
+ const p = await profileResponse.json() as {
50
+ ok: boolean; sub: string; name?: string;
51
+ email?: string; picture?: string; email_verified?: boolean;
52
+ };
53
+
54
+ if (!p.ok || !p.email) {
55
+ console.error("Slack user has no email");
56
+ return null;
57
+ }
58
+
59
+ return {
60
+ providerId: p.sub,
61
+ email: p.email,
62
+ displayName: p.name || null,
63
+ photoUrl: p.picture || null
64
+ };
65
+ } catch (error) {
66
+ console.error("Slack OAuth error:", error);
67
+ return null;
68
+ }
69
+ }
70
+ };
71
+ }
@@ -0,0 +1,67 @@
1
+ import type { OAuthProvider, OAuthProviderProfile } from "./interfaces";
2
+ import { z } from "zod";
3
+
4
+ /**
5
+ * Creates a Spotify OAuth Provider integration.
6
+ * Uses the authorization code flow with the "user-read-email" scope.
7
+ */
8
+ export function createSpotifyProvider(config: { clientId: string; clientSecret: string }): OAuthProvider<{ code: string; redirectUri: string }> {
9
+ return {
10
+ id: "spotify",
11
+ schema: z.object({
12
+ code: z.string().min(1, "Auth code is required"),
13
+ redirectUri: z.string().url("Valid redirect URI is required")
14
+ }),
15
+ verify: async (payload: { code: string; redirectUri: string }): Promise<OAuthProviderProfile | null> => {
16
+ try {
17
+ const basicAuth = Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64");
18
+
19
+ const tokenResponse = await fetch("https://accounts.spotify.com/api/token", {
20
+ method: "POST",
21
+ headers: {
22
+ "Content-Type": "application/x-www-form-urlencoded",
23
+ "Authorization": `Basic ${basicAuth}`
24
+ },
25
+ body: new URLSearchParams({
26
+ grant_type: "authorization_code",
27
+ code: payload.code,
28
+ redirect_uri: payload.redirectUri
29
+ })
30
+ });
31
+
32
+ if (!tokenResponse.ok) {
33
+ console.error("Failed to get Spotify access token:", await tokenResponse.text());
34
+ return null;
35
+ }
36
+
37
+ const tokenData = await tokenResponse.json() as { access_token: string };
38
+
39
+ const profileResponse = await fetch("https://api.spotify.com/v1/me", {
40
+ headers: { "Authorization": `Bearer ${tokenData.access_token}` }
41
+ });
42
+
43
+ if (!profileResponse.ok) {
44
+ console.error("Failed to get Spotify user info:", await profileResponse.text());
45
+ return null;
46
+ }
47
+
48
+ const p = await profileResponse.json() as {
49
+ id: string; display_name?: string | null;
50
+ email?: string; images?: Array<{ url: string }>;
51
+ };
52
+
53
+ if (!p.email) { console.error("Spotify user has no email"); return null; }
54
+
55
+ return {
56
+ providerId: p.id,
57
+ email: p.email,
58
+ displayName: p.display_name || null,
59
+ photoUrl: p.images?.[0]?.url || null
60
+ };
61
+ } catch (error) {
62
+ console.error("Spotify OAuth error:", error);
63
+ return null;
64
+ }
65
+ }
66
+ };
67
+ }
@@ -0,0 +1,120 @@
1
+ import type { OAuthProvider, OAuthProviderProfile } from "./interfaces";
2
+ import { z } from "zod";
3
+
4
+ /**
5
+ * Creates a Twitter/X OAuth 2.0 Provider integration.
6
+ *
7
+ * Uses OAuth 2.0 with PKCE (authorization code flow). The frontend must include
8
+ * the PKCE `code_verifier` when sending the authorization code.
9
+ *
10
+ * Twitter API v2 requires the "tweet.read" and "users.read" scopes at minimum,
11
+ * plus "offline.access" if refresh tokens are needed on Twitter's side.
12
+ */
13
+ export function createTwitterProvider(config: { clientId: string; clientSecret: string }): OAuthProvider<{
14
+ code: string;
15
+ redirectUri: string;
16
+ codeVerifier: string;
17
+ }> {
18
+ return {
19
+ id: "twitter",
20
+ schema: z.object({
21
+ code: z.string().min(1, "Auth code is required"),
22
+ redirectUri: z.string().url("Valid redirect URI is required"),
23
+ codeVerifier: z.string().min(1, "PKCE code verifier is required")
24
+ }),
25
+ verify: async (payload: {
26
+ code: string;
27
+ redirectUri: string;
28
+ codeVerifier: string;
29
+ }): Promise<OAuthProviderProfile | null> => {
30
+ try {
31
+ // Twitter OAuth 2.0 uses Basic auth for the token endpoint
32
+ const basicAuth = Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64");
33
+
34
+ // Exchange code for access token
35
+ const tokenResponse = await fetch("https://api.twitter.com/2/oauth2/token", {
36
+ method: "POST",
37
+ headers: {
38
+ "Content-Type": "application/x-www-form-urlencoded",
39
+ "Authorization": `Basic ${basicAuth}`
40
+ },
41
+ body: new URLSearchParams({
42
+ code: payload.code,
43
+ grant_type: "authorization_code",
44
+ redirect_uri: payload.redirectUri,
45
+ code_verifier: payload.codeVerifier
46
+ })
47
+ });
48
+
49
+ if (!tokenResponse.ok) {
50
+ console.error("Failed to get Twitter access token:", await tokenResponse.text());
51
+ return null;
52
+ }
53
+
54
+ const tokenData = await tokenResponse.json() as { access_token: string };
55
+ const accessToken = tokenData.access_token;
56
+
57
+ // Fetch user profile from Twitter API v2
58
+ const profileResponse = await fetch(
59
+ "https://api.twitter.com/2/users/me?user.fields=id,name,username,profile_image_url",
60
+ {
61
+ headers: { "Authorization": `Bearer ${accessToken}` }
62
+ }
63
+ );
64
+
65
+ if (!profileResponse.ok) {
66
+ console.error("Failed to get Twitter user info:", await profileResponse.text());
67
+ return null;
68
+ }
69
+
70
+ const profileResult = await profileResponse.json() as {
71
+ data: {
72
+ id: string;
73
+ name: string;
74
+ username: string;
75
+ profile_image_url?: string;
76
+ };
77
+ };
78
+
79
+ const profileData = profileResult.data;
80
+
81
+ // Twitter does NOT expose email via API v2 by default.
82
+ // It requires elevated access. If we can't get an email, we
83
+ // generate a placeholder email — the user can update it later.
84
+ // For apps with elevated access, fetch from v1.1 endpoint.
85
+ let email: string | null = null;
86
+ try {
87
+ const emailResponse = await fetch(
88
+ "https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true",
89
+ {
90
+ headers: { "Authorization": `Bearer ${accessToken}` }
91
+ }
92
+ );
93
+ if (emailResponse.ok) {
94
+ const emailData = await emailResponse.json() as { email?: string };
95
+ email = emailData.email || null;
96
+ }
97
+ } catch {
98
+ // Elevated access not available — fall through
99
+ }
100
+
101
+ if (!email) {
102
+ // Construct a deterministic placeholder email from the Twitter user ID.
103
+ // This allows the account to be created and linked; the user should
104
+ // update their email through the profile settings.
105
+ email = `${profileData.id}@twitter.placeholder.rebase`;
106
+ }
107
+
108
+ return {
109
+ providerId: profileData.id,
110
+ email,
111
+ displayName: profileData.name || profileData.username || null,
112
+ photoUrl: profileData.profile_image_url?.replace("_normal", "_400x400") || null
113
+ };
114
+ } catch (error) {
115
+ console.error("Twitter OAuth error:", error);
116
+ return null;
117
+ }
118
+ }
119
+ };
120
+ }
@@ -0,0 +1 @@
1
+ // PostgresBootstrapper was moved to @rebasepro/server-postgresql
@@ -0,0 +1,20 @@
1
+ import { CollectionRegistry } from "@rebasepro/common";
2
+ import { CollectionRegistryInterface } from "../db/interfaces";
3
+ import { EntityCollection } from "@rebasepro/types";
4
+
5
+ /**
6
+ * Backend-agnostic collection registry.
7
+ * Satisfies CollectionRegistryInterface through inheritance from CollectionRegistry.
8
+ */
9
+ export class BackendCollectionRegistry extends CollectionRegistry implements CollectionRegistryInterface {
10
+
11
+ /**
12
+ * Get the available relation keys for a given collection path.
13
+ * Maps from the collection's relation property names to the relation names.
14
+ */
15
+ getRelationKeysForCollection(collectionPath: string): string[] {
16
+ const collection = this.getCollectionByPath(collectionPath) as (EntityCollection & { relations?: { relationName?: string, localKey?: string }[] }) | undefined;
17
+ if (!collection?.relations) return [];
18
+ return collection.relations.map(r => (r.relationName || r.localKey || "") as string).filter(Boolean);
19
+ }
20
+ }
@@ -0,0 +1,49 @@
1
+ import { EntityCollection } from "@rebasepro/types";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import { pathToFileURL } from "url";
5
+
6
+ /**
7
+ * Asynchronously load collection files from a directory for backend initialization
8
+ */
9
+ export async function loadCollectionsFromDirectory(directory: string): Promise<EntityCollection[]> {
10
+ const collections: EntityCollection[] = [];
11
+ try {
12
+ if (!fs.existsSync(directory)) {
13
+ console.warn(`[loadCollectionsFromDirectory] Collections directory not found: ${directory}`);
14
+ return collections;
15
+ }
16
+
17
+ const files = fs.readdirSync(directory);
18
+ for (const file of files) {
19
+ // Only load .ts and .js files, ignore test files and declaration files
20
+ if ((file.endsWith(".ts") || file.endsWith(".js")) &&
21
+ !file.includes(".test.") &&
22
+ !file.endsWith(".d.ts") &&
23
+ file !== "index.ts" && file !== "index.js") {
24
+
25
+ const filePath = path.join(directory, file);
26
+ try {
27
+ const fileUrl = pathToFileURL(filePath).href;
28
+
29
+ // Use new Function to compile dynamic import natively and bypass tsc converting import() to require()
30
+ const dynamicImport = new Function("url", "return import(url)");
31
+ const module = await dynamicImport(fileUrl);
32
+
33
+ // Expect the collection to be the default export
34
+ if (module && module.default) {
35
+ collections.push(module.default);
36
+ } else {
37
+ console.warn(`[loadCollectionsFromDirectory] File ${file} does not have a default export. Skipping.`);
38
+ }
39
+ } catch (err: unknown) {
40
+ const message = err instanceof Error ? err.message : String(err);
41
+ console.error(`[loadCollectionsFromDirectory] Failed to load collection from ${file}: ${message}`);
42
+ }
43
+ }
44
+ }
45
+ } catch (err) {
46
+ console.error(`[loadCollectionsFromDirectory] Error reading collections directory: ${err}`);
47
+ }
48
+ return collections;
49
+ }
@@ -0,0 +1,89 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { pathToFileURL } from "url";
4
+ import type { CronJobDefinition } from "@rebasepro/types";
5
+
6
+ export interface LoadedCronJob {
7
+ /** Job ID derived from filename (e.g. "cleanup-sessions"). */
8
+ id: string;
9
+ /** The full definition. */
10
+ definition: CronJobDefinition;
11
+ }
12
+
13
+ /**
14
+ * Auto-discover cron job files from a directory.
15
+ *
16
+ * Each file should default-export a `CronJobDefinition`.
17
+ * The filename (without extension) becomes the job ID:
18
+ * `crons/cleanup-sessions.ts` → id = "cleanup-sessions"
19
+ *
20
+ * Follows the same discovery pattern as `loadFunctionsFromDirectory`.
21
+ */
22
+ export async function loadCronJobsFromDirectory(
23
+ directory: string
24
+ ): Promise<LoadedCronJob[]> {
25
+ const jobs: LoadedCronJob[] = [];
26
+
27
+ if (!fs.existsSync(directory)) {
28
+ return jobs;
29
+ }
30
+
31
+ const files = fs.readdirSync(directory);
32
+ for (const file of files) {
33
+ if (
34
+ (file.endsWith(".ts") || file.endsWith(".js")) &&
35
+ !file.includes(".test.") &&
36
+ !file.endsWith(".d.ts") &&
37
+ file !== "index.ts" &&
38
+ file !== "index.js"
39
+ ) {
40
+ const filePath = path.join(directory, file);
41
+ try {
42
+ const fileUrl = pathToFileURL(filePath).href;
43
+
44
+ // Native dynamic import — bypasses tsc down-compilation
45
+ const dynamicImport = new Function("url", "return import(url)");
46
+ const mod = await dynamicImport(fileUrl);
47
+
48
+ const exported: unknown = mod.default;
49
+
50
+ if (!exported || typeof exported !== "object") {
51
+ console.warn(
52
+ `[cron] ${file}: no valid default export. Skipping.`
53
+ );
54
+ continue;
55
+ }
56
+
57
+ const def = exported as Record<string, unknown>;
58
+ if (typeof def.schedule !== "string" || typeof def.handler !== "function") {
59
+ console.warn(
60
+ `[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`
61
+ );
62
+ continue;
63
+ }
64
+
65
+ const id = path.basename(file, path.extname(file));
66
+ const definition: CronJobDefinition = {
67
+ schedule: def.schedule as string,
68
+ name: (def.name as string) || id,
69
+ description: def.description as string | undefined,
70
+ enabled: def.enabled !== false,
71
+ timeoutSeconds: (def.timeoutSeconds as number) || 300,
72
+ handler: def.handler as CronJobDefinition["handler"]
73
+ };
74
+
75
+ jobs.push({ id,
76
+ definition });
77
+ console.log(`⏰ Loaded cron job: ${id} (${definition.schedule})`);
78
+ } catch (err: unknown) {
79
+ const message =
80
+ err instanceof Error ? err.message : String(err);
81
+ console.error(
82
+ `[cron] Failed to load ${file}: ${message}`
83
+ );
84
+ }
85
+ }
86
+ }
87
+
88
+ return jobs;
89
+ }