@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,91 @@
1
+ import { createTransport, Transporter } from "nodemailer";
2
+ import { EmailService, EmailSendOptions, EmailConfig } from "./types";
3
+
4
+ /**
5
+ * SMTP Email Service implementation using Nodemailer
6
+ */
7
+ export class SMTPEmailService implements EmailService {
8
+ private transporter: Transporter | null = null;
9
+ private config: EmailConfig;
10
+
11
+ constructor(config: EmailConfig) {
12
+ this.config = config;
13
+
14
+ if (config.smtp) {
15
+ this.transporter = createTransport({
16
+ host: config.smtp.host,
17
+ port: config.smtp.port,
18
+ secure: config.smtp.secure ?? (config.smtp.port === 465),
19
+ auth: config.smtp.auth ? {
20
+ user: config.smtp.auth.user,
21
+ pass: config.smtp.auth.pass
22
+ } : undefined
23
+ });
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Check if the email service is properly configured
29
+ */
30
+ isConfigured(): boolean {
31
+ return !!(this.transporter || this.config.sendEmail);
32
+ }
33
+
34
+ /**
35
+ * Send an email using SMTP or custom send function
36
+ */
37
+ async send(options: EmailSendOptions): Promise<void> {
38
+ // Use custom send function if provided
39
+ if (this.config.sendEmail) {
40
+ await this.config.sendEmail(options);
41
+ return;
42
+ }
43
+
44
+ // Use SMTP transporter
45
+ if (!this.transporter) {
46
+ throw new Error("Email service not configured. Provide SMTP config or sendEmail function.");
47
+ }
48
+
49
+ const to = Array.isArray(options.to) ? options.to.join(", ") : options.to;
50
+
51
+ try {
52
+ await this.transporter.sendMail({
53
+ from: this.config.from,
54
+ to,
55
+ subject: options.subject,
56
+ html: options.html,
57
+ text: options.text,
58
+ replyTo: options.replyTo
59
+ });
60
+ } catch (error: unknown) {
61
+ const message = error instanceof Error ? error.message : String(error);
62
+ console.error("Failed to send email:", message);
63
+ throw new Error(`Failed to send email: ${message}`);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Verify SMTP connection (useful for startup checks)
69
+ */
70
+ async verifyConnection(): Promise<boolean> {
71
+ if (!this.transporter) {
72
+ return !!this.config.sendEmail;
73
+ }
74
+
75
+ try {
76
+ await this.transporter.verify();
77
+ return true;
78
+ } catch (error: unknown) {
79
+ const message = error instanceof Error ? error.message : String(error);
80
+ console.error("SMTP connection verification failed:", message);
81
+ return false;
82
+ }
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Create an email service from configuration
88
+ */
89
+ export function createEmailService(config: EmailConfig): EmailService {
90
+ return new SMTPEmailService(config);
91
+ }
@@ -0,0 +1,388 @@
1
+ /**
2
+ * Default email templates for authentication emails
3
+ */
4
+
5
+ interface TemplateUser {
6
+ email: string;
7
+ displayName?: string | null;
8
+ }
9
+
10
+ /**
11
+ * Get a greeting name for the user
12
+ */
13
+ function getGreeting(user: TemplateUser): string {
14
+ return user.displayName || user.email.split("@")[0];
15
+ }
16
+
17
+ /**
18
+ * Common email styles
19
+ */
20
+ const styles = {
21
+ container: `
22
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
23
+ max-width: 600px;
24
+ margin: 0 auto;
25
+ padding: 40px 20px;
26
+ background-color: #f8fafc;
27
+ `,
28
+ card: `
29
+ background-color: #ffffff;
30
+ border-radius: 12px;
31
+ padding: 40px;
32
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
33
+ `,
34
+ heading: `
35
+ color: #1e293b;
36
+ font-size: 24px;
37
+ font-weight: 600;
38
+ margin: 0 0 20px 0;
39
+ `,
40
+ paragraph: `
41
+ color: #475569;
42
+ font-size: 16px;
43
+ line-height: 1.6;
44
+ margin: 0 0 20px 0;
45
+ `,
46
+ button: `
47
+ display: inline-block;
48
+ background-color: #3b82f6;
49
+ color: #ffffff;
50
+ font-size: 16px;
51
+ font-weight: 600;
52
+ text-decoration: none;
53
+ padding: 14px 28px;
54
+ border-radius: 8px;
55
+ margin: 20px 0;
56
+ `,
57
+ footer: `
58
+ color: #94a3b8;
59
+ font-size: 14px;
60
+ margin-top: 30px;
61
+ padding-top: 20px;
62
+ border-top: 1px solid #e2e8f0;
63
+ `,
64
+ warning: `
65
+ color: #64748b;
66
+ font-size: 14px;
67
+ background-color: #fef3c7;
68
+ padding: 12px 16px;
69
+ border-radius: 6px;
70
+ margin-top: 20px;
71
+ `
72
+ };
73
+
74
+ /**
75
+ * Default password reset email template
76
+ */
77
+ export function getPasswordResetTemplate(
78
+ resetUrl: string,
79
+ user: TemplateUser,
80
+ appName = "Rebase"
81
+ ): { subject: string; html: string; text: string } {
82
+ const greeting = getGreeting(user);
83
+
84
+ const subject = `Reset your ${appName} password`;
85
+
86
+ const html = `
87
+ <!DOCTYPE html>
88
+ <html>
89
+ <head>
90
+ <meta charset="utf-8">
91
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
92
+ <title>${subject}</title>
93
+ </head>
94
+ <body style="margin: 0; padding: 0; background-color: #f8fafc;">
95
+ <div style="${styles.container}">
96
+ <div style="${styles.card}">
97
+ <h1 style="${styles.heading}">Reset Your Password</h1>
98
+
99
+ <p style="${styles.paragraph}">
100
+ Hi ${greeting},
101
+ </p>
102
+
103
+ <p style="${styles.paragraph}">
104
+ We received a request to reset your password for your ${appName} account.
105
+ Click the button below to create a new password:
106
+ </p>
107
+
108
+ <div style="text-align: center;">
109
+ <a href="${resetUrl}" style="${styles.button}">Reset Password</a>
110
+ </div>
111
+
112
+ <p style="${styles.paragraph}">
113
+ Or copy and paste this link into your browser:
114
+ </p>
115
+ <p style="color: #3b82f6; word-break: break-all; font-size: 14px;">
116
+ ${resetUrl}
117
+ </p>
118
+
119
+ <div style="${styles.warning}">
120
+ ⏰ This link will expire in 1 hour for security reasons.
121
+ </div>
122
+
123
+ <div style="${styles.footer}">
124
+ <p style="margin: 0;">
125
+ If you didn't request a password reset, you can safely ignore this email.
126
+ Your password will remain unchanged.
127
+ </p>
128
+ </div>
129
+ </div>
130
+ </div>
131
+ </body>
132
+ </html>
133
+ `.trim();
134
+
135
+ const text = `
136
+ Reset Your Password
137
+
138
+ Hi ${greeting},
139
+
140
+ We received a request to reset your password for your ${appName} account.
141
+
142
+ Click this link to create a new password:
143
+ ${resetUrl}
144
+
145
+ This link will expire in 1 hour for security reasons.
146
+
147
+ If you didn't request a password reset, you can safely ignore this email.
148
+ Your password will remain unchanged.
149
+ `.trim();
150
+
151
+ return { subject,
152
+ html,
153
+ text };
154
+ }
155
+
156
+ /**
157
+ * Default email verification template
158
+ */
159
+ export function getEmailVerificationTemplate(
160
+ verifyUrl: string,
161
+ user: TemplateUser,
162
+ appName = "Rebase"
163
+ ): { subject: string; html: string; text: string } {
164
+ const greeting = getGreeting(user);
165
+
166
+ const subject = `Verify your ${appName} email address`;
167
+
168
+ const html = `
169
+ <!DOCTYPE html>
170
+ <html>
171
+ <head>
172
+ <meta charset="utf-8">
173
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
174
+ <title>${subject}</title>
175
+ </head>
176
+ <body style="margin: 0; padding: 0; background-color: #f8fafc;">
177
+ <div style="${styles.container}">
178
+ <div style="${styles.card}">
179
+ <h1 style="${styles.heading}">Verify Your Email</h1>
180
+
181
+ <p style="${styles.paragraph}">
182
+ Hi ${greeting},
183
+ </p>
184
+
185
+ <p style="${styles.paragraph}">
186
+ Thanks for signing up for ${appName}! Please verify your email address
187
+ by clicking the button below:
188
+ </p>
189
+
190
+ <div style="text-align: center;">
191
+ <a href="${verifyUrl}" style="${styles.button}">Verify Email Address</a>
192
+ </div>
193
+
194
+ <p style="${styles.paragraph}">
195
+ Or copy and paste this link into your browser:
196
+ </p>
197
+ <p style="color: #3b82f6; word-break: break-all; font-size: 14px;">
198
+ ${verifyUrl}
199
+ </p>
200
+
201
+ <div style="${styles.footer}">
202
+ <p style="margin: 0;">
203
+ If you didn't create an account with ${appName}, you can safely ignore this email.
204
+ </p>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ </body>
209
+ </html>
210
+ `.trim();
211
+
212
+ const text = `
213
+ Verify Your Email
214
+
215
+ Hi ${greeting},
216
+
217
+ Thanks for signing up for ${appName}! Please verify your email address by clicking this link:
218
+ ${verifyUrl}
219
+
220
+ If you didn't create an account with ${appName}, you can safely ignore this email.
221
+ `.trim();
222
+
223
+ return { subject,
224
+ html,
225
+ text };
226
+ }
227
+
228
+ /**
229
+ * Default user invitation email template
230
+ * Sent when an admin creates a new user account
231
+ */
232
+ export function getUserInvitationTemplate(
233
+ setPasswordUrl: string,
234
+ user: TemplateUser,
235
+ appName = "Rebase"
236
+ ): { subject: string; html: string; text: string } {
237
+ const greeting = getGreeting(user);
238
+
239
+ const subject = `You've been invited to ${appName}`;
240
+
241
+ const html = `
242
+ <!DOCTYPE html>
243
+ <html>
244
+ <head>
245
+ <meta charset="utf-8">
246
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
247
+ <title>${subject}</title>
248
+ </head>
249
+ <body style="margin: 0; padding: 0; background-color: #f8fafc;">
250
+ <div style="${styles.container}">
251
+ <div style="${styles.card}">
252
+ <h1 style="${styles.heading}">Welcome to ${appName}!</h1>
253
+
254
+ <p style="${styles.paragraph}">
255
+ Hi ${greeting},
256
+ </p>
257
+
258
+ <p style="${styles.paragraph}">
259
+ An account has been created for you on ${appName}.
260
+ Click the button below to set your password and get started:
261
+ </p>
262
+
263
+ <div style="text-align: center;">
264
+ <a href="${setPasswordUrl}" style="${styles.button}">Set Your Password</a>
265
+ </div>
266
+
267
+ <p style="${styles.paragraph}">
268
+ Or copy and paste this link into your browser:
269
+ </p>
270
+ <p style="color: #3b82f6; word-break: break-all; font-size: 14px;">
271
+ ${setPasswordUrl}
272
+ </p>
273
+
274
+ <div style="${styles.warning}">
275
+ ⏰ This link will expire in 1 hour for security reasons.
276
+ </div>
277
+
278
+ <div style="${styles.footer}">
279
+ <p style="margin: 0;">
280
+ If you weren't expecting this invitation, you can safely ignore this email.
281
+ </p>
282
+ </div>
283
+ </div>
284
+ </div>
285
+ </body>
286
+ </html>
287
+ `.trim();
288
+
289
+ const text = `
290
+ Welcome to ${appName}!
291
+
292
+ Hi ${greeting},
293
+
294
+ An account has been created for you on ${appName}.
295
+
296
+ Click this link to set your password and get started:
297
+ ${setPasswordUrl}
298
+
299
+ This link will expire in 1 hour for security reasons.
300
+
301
+ If you weren't expecting this invitation, you can safely ignore this email.
302
+ `.trim();
303
+
304
+ return { subject,
305
+ html,
306
+ text };
307
+ }
308
+
309
+ /**
310
+ * Default welcome email template
311
+ * Sent automatically when a new user registers
312
+ */
313
+ export function getWelcomeEmailTemplate(
314
+ user: TemplateUser,
315
+ appName = "Rebase",
316
+ loginUrl?: string
317
+ ): { subject: string; html: string; text: string } {
318
+ const greeting = getGreeting(user);
319
+ const url = loginUrl || "";
320
+
321
+ const subject = `¡Bienvenido/a a ${appName}!`;
322
+
323
+ const html = `
324
+ <!DOCTYPE html>
325
+ <html>
326
+ <head>
327
+ <meta charset="utf-8">
328
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
329
+ <title>${subject}</title>
330
+ </head>
331
+ <body style="margin: 0; padding: 0; background-color: #f8fafc;">
332
+ <div style="${styles.container}">
333
+ <div style="${styles.card}">
334
+ <h1 style="${styles.heading}">¡Bienvenido/a a ${appName}!</h1>
335
+
336
+ <p style="${styles.paragraph}">
337
+ Hola ${greeting},
338
+ </p>
339
+
340
+ <p style="${styles.paragraph}">
341
+ Tu cuenta en ${appName} ha sido creada exitosamente.
342
+ Estamos encantados de tenerte con nosotros.
343
+ </p>
344
+
345
+ <p style="${styles.paragraph}">
346
+ Ya puedes acceder a tu panel y empezar a explorar todas las oportunidades
347
+ que tenemos para ti.
348
+ </p>
349
+
350
+ ${url ? `
351
+ <div style="text-align: center;">
352
+ <a href="${url}" style="${styles.button}">Ir a mi Panel</a>
353
+ </div>
354
+ ` : ""}
355
+
356
+ <p style="${styles.paragraph}">
357
+ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
358
+ </p>
359
+
360
+ <div style="${styles.footer}">
361
+ <p style="margin: 0;">
362
+ Este correo fue enviado porque se creó una cuenta con esta dirección de email en ${appName}.
363
+ </p>
364
+ </div>
365
+ </div>
366
+ </div>
367
+ </body>
368
+ </html>
369
+ `.trim();
370
+
371
+ const text = `
372
+ ¡Bienvenido/a a ${appName}!
373
+
374
+ Hola ${greeting},
375
+
376
+ Tu cuenta en ${appName} ha sido creada exitosamente. Estamos encantados de tenerte con nosotros.
377
+
378
+ Ya puedes acceder a tu panel y empezar a explorar todas las oportunidades que tenemos para ti.
379
+
380
+ ${url ? `Ir a mi panel: ${url}` : ""}
381
+
382
+ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
383
+ `.trim();
384
+
385
+ return { subject,
386
+ html,
387
+ text };
388
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Email service types and interfaces.
3
+ *
4
+ * The canonical `EmailService` and `EmailSendOptions` live in `@rebasepro/types`
5
+ * so they can be used on the `RebaseClient` interface without pulling in nodemailer.
6
+ * This file re-exports them for backward compatibility and adds server-specific
7
+ * config types (SMTP, template functions, etc.).
8
+ */
9
+
10
+ import type { EmailService, EmailSendOptions } from "@rebasepro/types";
11
+
12
+ export type { EmailService, EmailSendOptions };
13
+
14
+ /**
15
+ * SMTP server configuration
16
+ */
17
+ export interface SMTPConfig {
18
+ host: string;
19
+ port: number;
20
+ secure?: boolean;
21
+ auth?: {
22
+ user: string;
23
+ pass: string;
24
+ };
25
+ }
26
+
27
+ /**
28
+ * Template function for password reset emails
29
+ */
30
+ export type PasswordResetTemplateFunction = (
31
+ resetUrl: string,
32
+ user: { email: string; displayName?: string | null }
33
+ ) => { subject: string; html: string; text?: string };
34
+
35
+ /**
36
+ * Template function for email verification emails
37
+ */
38
+ export type EmailVerificationTemplateFunction = (
39
+ verifyUrl: string,
40
+ user: { email: string; displayName?: string | null }
41
+ ) => { subject: string; html: string; text?: string };
42
+
43
+ /**
44
+ * Template function for user invitation emails
45
+ */
46
+ export type UserInvitationTemplateFunction = (
47
+ setPasswordUrl: string,
48
+ user: { email: string; displayName?: string | null }
49
+ ) => { subject: string; html: string; text?: string };
50
+
51
+ /**
52
+ * Template function for welcome emails sent after registration
53
+ */
54
+ export type WelcomeEmailTemplateFunction = (
55
+ user: { email: string; displayName?: string | null },
56
+ appName: string
57
+ ) => { subject: string; html: string; text?: string };
58
+
59
+ /**
60
+ * Complete email configuration
61
+ */
62
+ export interface EmailConfig {
63
+ /**
64
+ * From address for all emails (e.g., "noreply@example.com" or "MyApp <noreply@example.com>")
65
+ */
66
+ from: string;
67
+
68
+ /**
69
+ * SMTP configuration for sending emails via SMTP server
70
+ */
71
+ smtp?: SMTPConfig;
72
+
73
+ /**
74
+ * Alternative: custom function to send emails
75
+ * Use this for custom email providers (AWS SES SDK, Resend, etc.)
76
+ */
77
+ sendEmail?: (options: EmailSendOptions) => Promise<void>;
78
+
79
+ /**
80
+ * Base URL for password reset links (e.g., "https://myapp.com")
81
+ * The reset link will be: {baseUrl}/reset-password?token={token}
82
+ */
83
+ resetPasswordUrl?: string;
84
+
85
+ /**
86
+ * Base URL for email verification links (e.g., "https://myapp.com")
87
+ * The verification link will be: {baseUrl}/verify-email?token={token}
88
+ */
89
+ verifyEmailUrl?: string;
90
+
91
+ /**
92
+ * Application name to use in email templates
93
+ */
94
+ appName?: string;
95
+
96
+ /**
97
+ * Custom email templates (optional - defaults are provided)
98
+ */
99
+ templates?: {
100
+ passwordReset?: PasswordResetTemplateFunction;
101
+ emailVerification?: EmailVerificationTemplateFunction;
102
+ userInvitation?: UserInvitationTemplateFunction;
103
+ welcomeEmail?: WelcomeEmailTemplateFunction;
104
+ };
105
+ }
@@ -0,0 +1,119 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { pathToFileURL } from "url";
4
+ import { Hono } from "hono";
5
+
6
+ export interface LoadedFunction {
7
+ /** Endpoint name derived from filename (e.g., "send-invoice") */
8
+ name: string;
9
+ /** The Hono sub-app to mount */
10
+ app: Hono<import("hono").Env>;
11
+ }
12
+
13
+ /**
14
+ * Auto-discover Hono route files from a directory.
15
+ *
16
+ * Each file should default-export a Hono app (or router).
17
+ * The filename (without extension) becomes the mount path:
18
+ * `functions/send-invoice.ts` → mounted at `/send-invoice`
19
+ *
20
+ * This mirrors how `loadCollectionsFromDirectory` works for collections.
21
+ */
22
+ export async function loadFunctionsFromDirectory(
23
+ directory: string
24
+ ): Promise<LoadedFunction[]> {
25
+ const functions: LoadedFunction[] = [];
26
+
27
+ if (!fs.existsSync(directory)) {
28
+ return functions;
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
+ // Use new Function to compile dynamic import natively and bypass
45
+ // tsc converting import() to require() — same pattern as collection loader
46
+ const dynamicImport = new Function("url", "return import(url)");
47
+ const mod = await dynamicImport(fileUrl);
48
+
49
+ const exported = mod.default;
50
+
51
+ if (!exported) {
52
+ console.warn(
53
+ `[functions] ${file}: no default export. Skipping.`
54
+ );
55
+ continue;
56
+ }
57
+
58
+ // Accept a Hono instance — use duck-typing to handle different
59
+ // Hono versions which may not share the same prototype.
60
+ if (isHonoLike(exported)) {
61
+ const name = path.basename(file, path.extname(file));
62
+ functions.push({ name,
63
+ app: exported as Hono });
64
+ console.log(`⚡ Loaded function route: ${name}`);
65
+ continue;
66
+ }
67
+
68
+ // Also accept a factory function that returns a Hono instance
69
+ if (typeof exported === "function") {
70
+ const result = exported();
71
+ if (isHonoLike(result)) {
72
+ const name = path.basename(file, path.extname(file));
73
+ functions.push({ name,
74
+ app: result as Hono });
75
+ console.log(`⚡ Loaded function route: ${name}`);
76
+ continue;
77
+ }
78
+ }
79
+
80
+ // Provide actionable diagnostics
81
+ const exportType = typeof exported;
82
+ const keys = exported && typeof exported === "object"
83
+ ? Object.getOwnPropertyNames(Object.getPrototypeOf(exported)).slice(0, 10).join(", ")
84
+ : "N/A";
85
+ console.warn(
86
+ `[functions] ${file}: default export is not a Hono app or factory. Skipping.\n` +
87
+ ` export type: ${exportType}${exported?.constructor?.name ? ` (${exported.constructor.name})` : ""}\n` +
88
+ ` prototype methods: ${keys}\n` +
89
+ " Hint: ensure the function exports a Hono app created with the same hono version as the server.\n" +
90
+ " The loader checks for .fetch() and .routes — any Hono-compatible app will work."
91
+ );
92
+ } catch (err: unknown) {
93
+ const message =
94
+ err instanceof Error ? err.message : String(err);
95
+ console.error(
96
+ `[functions] Failed to load ${file}: ${message}`
97
+ );
98
+ }
99
+ }
100
+ }
101
+
102
+ return functions;
103
+ }
104
+
105
+ /**
106
+ * Duck-type check for Hono apps.
107
+ * We avoid `instanceof Hono` because different Hono versions
108
+ * installed in the user's project vs. our dependencies will
109
+ * not share the same prototype, causing false negatives.
110
+ */
111
+ function isHonoLike(obj: unknown): boolean {
112
+ if (!obj || typeof obj !== "object") return false;
113
+ // Hono instances always have .fetch() and .routes
114
+ const record = obj as Record<string, unknown>;
115
+ return (
116
+ typeof record.fetch === "function" &&
117
+ Array.isArray(record.routes)
118
+ );
119
+ }