sovrium 0.0.2

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 (527) hide show
  1. package/CHANGELOG.md +3497 -0
  2. package/LICENSE.md +147 -0
  3. package/LICENSE_EE.md +297 -0
  4. package/README.md +321 -0
  5. package/drizzle/0000_melted_kabuki.sql +163 -0
  6. package/drizzle/meta/0000_snapshot.json +1216 -0
  7. package/drizzle/meta/_journal.json +13 -0
  8. package/package.json +167 -0
  9. package/schemas/0.0.1/app.openapi.json +70 -0
  10. package/schemas/0.0.1/app.schema.json +7961 -0
  11. package/schemas/0.0.2/app.openapi.json +80 -0
  12. package/schemas/0.0.2/app.schema.json +8829 -0
  13. package/schemas/development/app.openapi.json +70 -0
  14. package/schemas/development/app.schema.json +7456 -0
  15. package/src/application/errors/app-validation-error.ts +14 -0
  16. package/src/application/errors/static-generation-error.ts +16 -0
  17. package/src/application/metadata/favicon-transformer.ts +127 -0
  18. package/src/application/models/server.ts +27 -0
  19. package/src/application/ports/models/user-metadata.ts +36 -0
  20. package/src/application/ports/models/user-session.ts +34 -0
  21. package/src/application/ports/repositories/activity-log-repository.ts +68 -0
  22. package/src/application/ports/repositories/activity-repository.ts +49 -0
  23. package/src/application/ports/repositories/analytics-repository.ts +164 -0
  24. package/src/application/ports/repositories/auth-repository.ts +33 -0
  25. package/src/application/ports/repositories/batch-repository.ts +86 -0
  26. package/src/application/ports/repositories/comment-repository.ts +150 -0
  27. package/src/application/ports/repositories/index.ts +41 -0
  28. package/src/application/ports/repositories/table-repository.ts +139 -0
  29. package/src/application/ports/services/css-compiler.ts +55 -0
  30. package/src/application/ports/services/index.ts +16 -0
  31. package/src/application/ports/services/page-renderer.ts +79 -0
  32. package/src/application/ports/services/server-factory.ts +80 -0
  33. package/src/application/ports/services/static-site-generator.ts +82 -0
  34. package/src/application/use-cases/activity/programs.ts +66 -0
  35. package/src/application/use-cases/analytics/collect-page-view.ts +114 -0
  36. package/src/application/use-cases/analytics/purge-old-data.ts +40 -0
  37. package/src/application/use-cases/analytics/query-campaigns.ts +43 -0
  38. package/src/application/use-cases/analytics/query-devices.ts +36 -0
  39. package/src/application/use-cases/analytics/query-overview.ts +50 -0
  40. package/src/application/use-cases/analytics/query-pages.ts +40 -0
  41. package/src/application/use-cases/analytics/query-referrers.ts +43 -0
  42. package/src/application/use-cases/analytics/ua-parser.ts +89 -0
  43. package/src/application/use-cases/analytics/visitor-hash.ts +77 -0
  44. package/src/application/use-cases/auth/bootstrap-admin.ts +270 -0
  45. package/src/application/use-cases/list-activity-logs.ts +123 -0
  46. package/src/application/use-cases/server/generate-static-helpers.ts +374 -0
  47. package/src/application/use-cases/server/generate-static.ts +287 -0
  48. package/src/application/use-cases/server/start-server.ts +118 -0
  49. package/src/application/use-cases/server/startup-error-handler.ts +69 -0
  50. package/src/application/use-cases/server/static-content-generators.ts +182 -0
  51. package/src/application/use-cases/server/static-language-generators.ts +181 -0
  52. package/src/application/use-cases/server/static-url-rewriter.ts +237 -0
  53. package/src/application/use-cases/server/translation-replacer.ts +164 -0
  54. package/src/application/use-cases/tables/activity-programs.ts +93 -0
  55. package/src/application/use-cases/tables/batch-operations.ts +156 -0
  56. package/src/application/use-cases/tables/comment-programs.ts +436 -0
  57. package/src/application/use-cases/tables/permissions/permissions.ts +25 -0
  58. package/src/application/use-cases/tables/programs.ts +435 -0
  59. package/src/application/use-cases/tables/table-operations.ts +412 -0
  60. package/src/application/use-cases/tables/user-role.ts +52 -0
  61. package/src/application/use-cases/tables/utils/display-formatter.ts +471 -0
  62. package/src/application/use-cases/tables/utils/field-read-filter.ts +189 -0
  63. package/src/application/use-cases/tables/utils/list-helpers.ts +122 -0
  64. package/src/application/use-cases/tables/utils/record-transformer.ts +319 -0
  65. package/src/cli.ts +370 -0
  66. package/src/domain/errors/create-tagged-error.ts +36 -0
  67. package/src/domain/errors/index.ts +78 -0
  68. package/src/domain/models/api/analytics.ts +179 -0
  69. package/src/domain/models/api/auth.ts +231 -0
  70. package/src/domain/models/api/common.ts +60 -0
  71. package/src/domain/models/api/error.ts +89 -0
  72. package/src/domain/models/api/health.ts +38 -0
  73. package/src/domain/models/api/index.ts +42 -0
  74. package/src/domain/models/api/request.ts +132 -0
  75. package/src/domain/models/api/tables.ts +444 -0
  76. package/src/domain/models/app/analytics/index.ts +129 -0
  77. package/src/domain/models/app/auth/config.ts +116 -0
  78. package/src/domain/models/app/auth/index.ts +230 -0
  79. package/src/domain/models/app/auth/methods/email-and-password.ts +67 -0
  80. package/src/domain/models/app/auth/methods/index.ts +11 -0
  81. package/src/domain/models/app/auth/methods/magic-link.ts +54 -0
  82. package/src/domain/models/app/auth/oauth/index.ts +8 -0
  83. package/src/domain/models/app/auth/oauth/providers.ts +105 -0
  84. package/src/domain/models/app/auth/plugins/admin.ts +130 -0
  85. package/src/domain/models/app/auth/plugins/index.ts +74 -0
  86. package/src/domain/models/app/auth/plugins/two-factor.ts +63 -0
  87. package/src/domain/models/app/auth/roles.ts +179 -0
  88. package/src/domain/models/app/auth/strategies.ts +191 -0
  89. package/src/domain/models/app/auth/validation.ts +127 -0
  90. package/src/domain/models/app/common/branded-ids.ts +200 -0
  91. package/src/domain/models/app/common/definitions.ts +187 -0
  92. package/src/domain/models/app/component/common/component-children.ts +119 -0
  93. package/src/domain/models/app/component/common/component-props.ts +89 -0
  94. package/src/domain/models/app/component/common/component-reference.ts +170 -0
  95. package/src/domain/models/app/component/component.ts +81 -0
  96. package/src/domain/models/app/components.ts +65 -0
  97. package/src/domain/models/app/description.ts +83 -0
  98. package/src/domain/models/app/index.ts +258 -0
  99. package/src/domain/models/app/language/language-config.ts +200 -0
  100. package/src/domain/models/app/languages.ts +205 -0
  101. package/src/domain/models/app/name.ts +66 -0
  102. package/src/domain/models/app/page/common/interactions/click-interaction.ts +116 -0
  103. package/src/domain/models/app/page/common/interactions/entrance-animation.ts +84 -0
  104. package/src/domain/models/app/page/common/interactions/hover-interaction.ts +144 -0
  105. package/src/domain/models/app/page/common/interactions/interactions.ts +64 -0
  106. package/src/domain/models/app/page/common/interactions/scroll-interaction.ts +93 -0
  107. package/src/domain/models/app/page/common/responsive.ts +114 -0
  108. package/src/domain/models/app/page/common/url.ts +35 -0
  109. package/src/domain/models/app/page/common/variable-reference.ts +53 -0
  110. package/src/domain/models/app/page/id.ts +44 -0
  111. package/src/domain/models/app/page/index.ts +270 -0
  112. package/src/domain/models/app/page/meta/analytics.ts +248 -0
  113. package/src/domain/models/app/page/meta/custom-elements.ts +180 -0
  114. package/src/domain/models/app/page/meta/dns-prefetch.ts +77 -0
  115. package/src/domain/models/app/page/meta/favicon-set.ts +203 -0
  116. package/src/domain/models/app/page/meta/favicon.ts +50 -0
  117. package/src/domain/models/app/page/meta/favicons-config.ts +73 -0
  118. package/src/domain/models/app/page/meta/index.ts +278 -0
  119. package/src/domain/models/app/page/meta/open-graph.ts +166 -0
  120. package/src/domain/models/app/page/meta/preload.ts +190 -0
  121. package/src/domain/models/app/page/meta/structured-data/article.ts +211 -0
  122. package/src/domain/models/app/page/meta/structured-data/breadcrumb.ts +115 -0
  123. package/src/domain/models/app/page/meta/structured-data/common-fields.ts +201 -0
  124. package/src/domain/models/app/page/meta/structured-data/education-event.ts +256 -0
  125. package/src/domain/models/app/page/meta/structured-data/faq-page.ts +127 -0
  126. package/src/domain/models/app/page/meta/structured-data/index.ts +95 -0
  127. package/src/domain/models/app/page/meta/structured-data/local-business.ts +247 -0
  128. package/src/domain/models/app/page/meta/structured-data/organization.ts +171 -0
  129. package/src/domain/models/app/page/meta/structured-data/person.ts +138 -0
  130. package/src/domain/models/app/page/meta/structured-data/postal-address.ts +106 -0
  131. package/src/domain/models/app/page/meta/structured-data/product.ts +214 -0
  132. package/src/domain/models/app/page/meta/twitter-card.ts +217 -0
  133. package/src/domain/models/app/page/name.ts +38 -0
  134. package/src/domain/models/app/page/path.ts +21 -0
  135. package/src/domain/models/app/page/scripts/external-scripts.ts +163 -0
  136. package/src/domain/models/app/page/scripts/features.ts +135 -0
  137. package/src/domain/models/app/page/scripts/inline-scripts.ts +114 -0
  138. package/src/domain/models/app/page/scripts/scripts.ts +102 -0
  139. package/src/domain/models/app/page/sections.ts +298 -0
  140. package/src/domain/models/app/pages.ts +61 -0
  141. package/src/domain/models/app/permissions/index.ts +61 -0
  142. package/src/domain/models/app/permissions/resource-action.ts +114 -0
  143. package/src/domain/models/app/permissions/roles.ts +120 -0
  144. package/src/domain/models/app/table/check-constraints.ts +105 -0
  145. package/src/domain/models/app/table/cycle-detection.ts +124 -0
  146. package/src/domain/models/app/table/database-identifier.ts +153 -0
  147. package/src/domain/models/app/table/field-name.ts +36 -0
  148. package/src/domain/models/app/table/field-types/advanced/array-field.ts +33 -0
  149. package/src/domain/models/app/table/field-types/advanced/autonumber-field.ts +54 -0
  150. package/src/domain/models/app/table/field-types/advanced/button-field.ts +56 -0
  151. package/src/domain/models/app/table/field-types/advanced/color-field.ts +57 -0
  152. package/src/domain/models/app/table/field-types/advanced/count-field.ts +54 -0
  153. package/src/domain/models/app/table/field-types/advanced/formula-field.ts +58 -0
  154. package/src/domain/models/app/table/field-types/advanced/geolocation-field.ts +49 -0
  155. package/src/domain/models/app/table/field-types/advanced/index.ts +16 -0
  156. package/src/domain/models/app/table/field-types/advanced/json-field.ts +25 -0
  157. package/src/domain/models/app/table/field-types/advanced/unknown-field.ts +85 -0
  158. package/src/domain/models/app/table/field-types/base-field.ts +42 -0
  159. package/src/domain/models/app/table/field-types/date-time/created-at-field.ts +49 -0
  160. package/src/domain/models/app/table/field-types/date-time/date-field.ts +95 -0
  161. package/src/domain/models/app/table/field-types/date-time/deleted-at-field.ts +56 -0
  162. package/src/domain/models/app/table/field-types/date-time/duration-field.ts +73 -0
  163. package/src/domain/models/app/table/field-types/date-time/index.ts +12 -0
  164. package/src/domain/models/app/table/field-types/date-time/updated-at-field.ts +50 -0
  165. package/src/domain/models/app/table/field-types/index.ts +19 -0
  166. package/src/domain/models/app/table/field-types/media/barcode-field.ts +58 -0
  167. package/src/domain/models/app/table/field-types/media/index.ts +10 -0
  168. package/src/domain/models/app/table/field-types/media/multiple-attachments-field.ts +80 -0
  169. package/src/domain/models/app/table/field-types/media/single-attachment-field.ts +81 -0
  170. package/src/domain/models/app/table/field-types/numeric/currency-field.ts +144 -0
  171. package/src/domain/models/app/table/field-types/numeric/decimal-field.ts +113 -0
  172. package/src/domain/models/app/table/field-types/numeric/index.ts +13 -0
  173. package/src/domain/models/app/table/field-types/numeric/integer-field.ts +98 -0
  174. package/src/domain/models/app/table/field-types/numeric/percentage-field.ts +115 -0
  175. package/src/domain/models/app/table/field-types/numeric/progress-field.ts +71 -0
  176. package/src/domain/models/app/table/field-types/numeric/rating-field.ts +74 -0
  177. package/src/domain/models/app/table/field-types/relational/index.ts +10 -0
  178. package/src/domain/models/app/table/field-types/relational/lookup-field.ts +46 -0
  179. package/src/domain/models/app/table/field-types/relational/relationship-field.ts +112 -0
  180. package/src/domain/models/app/table/field-types/relational/rollup-field.ts +58 -0
  181. package/src/domain/models/app/table/field-types/selection/checkbox-field.ts +51 -0
  182. package/src/domain/models/app/table/field-types/selection/index.ts +11 -0
  183. package/src/domain/models/app/table/field-types/selection/multi-select-field.ts +68 -0
  184. package/src/domain/models/app/table/field-types/selection/single-select-field.ts +54 -0
  185. package/src/domain/models/app/table/field-types/selection/status-field.ts +37 -0
  186. package/src/domain/models/app/table/field-types/text/email-field.ts +80 -0
  187. package/src/domain/models/app/table/field-types/text/index.ts +13 -0
  188. package/src/domain/models/app/table/field-types/text/long-text-field.ts +77 -0
  189. package/src/domain/models/app/table/field-types/text/phone-number-field.ts +82 -0
  190. package/src/domain/models/app/table/field-types/text/rich-text-field.ts +66 -0
  191. package/src/domain/models/app/table/field-types/text/single-line-text-field.ts +79 -0
  192. package/src/domain/models/app/table/field-types/text/url-field.ts +81 -0
  193. package/src/domain/models/app/table/field-types/user/created-by-field.ts +50 -0
  194. package/src/domain/models/app/table/field-types/user/deleted-by-field.ts +57 -0
  195. package/src/domain/models/app/table/field-types/user/index.ts +11 -0
  196. package/src/domain/models/app/table/field-types/user/updated-by-field.ts +51 -0
  197. package/src/domain/models/app/table/field-types/user/user-field.ts +52 -0
  198. package/src/domain/models/app/table/field-types/validation-utils.ts +166 -0
  199. package/src/domain/models/app/table/fields.ts +216 -0
  200. package/src/domain/models/app/table/foreign-keys.ts +111 -0
  201. package/src/domain/models/app/table/formula-keywords.ts +326 -0
  202. package/src/domain/models/app/table/id.ts +31 -0
  203. package/src/domain/models/app/table/index.ts +290 -0
  204. package/src/domain/models/app/table/indexes.ts +80 -0
  205. package/src/domain/models/app/table/name.ts +37 -0
  206. package/src/domain/models/app/table/permissions/field-permission.ts +83 -0
  207. package/src/domain/models/app/table/permissions/index.ts +167 -0
  208. package/src/domain/models/app/table/permissions/permission-evaluator.ts +372 -0
  209. package/src/domain/models/app/table/permissions/permission.ts +49 -0
  210. package/src/domain/models/app/table/primary-key.ts +62 -0
  211. package/src/domain/models/app/table/table-formula-validation.ts +168 -0
  212. package/src/domain/models/app/table/table-indexes-validation.ts +38 -0
  213. package/src/domain/models/app/table/table-permissions-validation.ts +77 -0
  214. package/src/domain/models/app/table/table-primary-key-validation.ts +49 -0
  215. package/src/domain/models/app/table/table-views-validation.ts +408 -0
  216. package/src/domain/models/app/table/unique-constraints.ts +79 -0
  217. package/src/domain/models/app/table/views/fields.ts +28 -0
  218. package/src/domain/models/app/table/views/filters.ts +162 -0
  219. package/src/domain/models/app/table/views/group-by.ts +32 -0
  220. package/src/domain/models/app/table/views/id.ts +50 -0
  221. package/src/domain/models/app/table/views/index.ts +177 -0
  222. package/src/domain/models/app/table/views/name.ts +32 -0
  223. package/src/domain/models/app/table/views/permissions.ts +98 -0
  224. package/src/domain/models/app/table/views/sorts.ts +31 -0
  225. package/src/domain/models/app/tables.ts +695 -0
  226. package/src/domain/models/app/theme/animations.ts +208 -0
  227. package/src/domain/models/app/theme/border-radius.ts +58 -0
  228. package/src/domain/models/app/theme/breakpoints.ts +62 -0
  229. package/src/domain/models/app/theme/colors.ts +110 -0
  230. package/src/domain/models/app/theme/fonts.ts +164 -0
  231. package/src/domain/models/app/theme/shadows.ts +61 -0
  232. package/src/domain/models/app/theme/spacing.ts +115 -0
  233. package/src/domain/models/app/theme.ts +66 -0
  234. package/src/domain/models/app/version.ts +87 -0
  235. package/src/domain/models/record-comment.ts +91 -0
  236. package/src/domain/utils/content-parsing.ts +49 -0
  237. package/src/domain/utils/format-detection.ts +69 -0
  238. package/src/domain/utils/index.ts +9 -0
  239. package/src/domain/utils/route-matcher.ts +184 -0
  240. package/src/domain/utils/translation-resolver.ts +170 -0
  241. package/src/index.ts +208 -0
  242. package/src/infrastructure/analytics/tracking-script.ts +48 -0
  243. package/src/infrastructure/auth/better-auth/auth.ts +216 -0
  244. package/src/infrastructure/auth/better-auth/email-handlers.ts +162 -0
  245. package/src/infrastructure/auth/better-auth/index.ts +16 -0
  246. package/src/infrastructure/auth/better-auth/layer.ts +97 -0
  247. package/src/infrastructure/auth/better-auth/plugins/admin.ts +56 -0
  248. package/src/infrastructure/auth/better-auth/plugins/magic-link.ts +31 -0
  249. package/src/infrastructure/auth/better-auth/plugins/two-factor.ts +19 -0
  250. package/src/infrastructure/auth/better-auth/schema.ts +152 -0
  251. package/src/infrastructure/auth/index.ts +27 -0
  252. package/src/infrastructure/css/cache/css-cache-service.ts +130 -0
  253. package/src/infrastructure/css/compiler.ts +210 -0
  254. package/src/infrastructure/css/css-compiler-live.ts +20 -0
  255. package/src/infrastructure/css/index.ts +25 -0
  256. package/src/infrastructure/css/styles/animation-styles-generator.ts +177 -0
  257. package/src/infrastructure/css/styles/click-animations.ts +147 -0
  258. package/src/infrastructure/css/styles/component-layer-generators.ts +147 -0
  259. package/src/infrastructure/css/theme/theme-generators.ts +130 -0
  260. package/src/infrastructure/css/theme/theme-layer-generators.ts +219 -0
  261. package/src/infrastructure/css/theme/theme-token-resolver.ts +76 -0
  262. package/src/infrastructure/database/activity-queries.ts +111 -0
  263. package/src/infrastructure/database/auth/auth-validation.ts +101 -0
  264. package/src/infrastructure/database/drizzle/db-bun.ts +17 -0
  265. package/src/infrastructure/database/drizzle/db.ts +17 -0
  266. package/src/infrastructure/database/drizzle/index.ts +16 -0
  267. package/src/infrastructure/database/drizzle/layer.ts +34 -0
  268. package/src/infrastructure/database/drizzle/migrate.ts +77 -0
  269. package/src/infrastructure/database/drizzle/schema/activity-log.ts +111 -0
  270. package/src/infrastructure/database/drizzle/schema/analytics-page-views.ts +116 -0
  271. package/src/infrastructure/database/drizzle/schema/migration-audit.ts +68 -0
  272. package/src/infrastructure/database/drizzle/schema/record-comments.ts +79 -0
  273. package/src/infrastructure/database/drizzle/schema.ts +12 -0
  274. package/src/infrastructure/database/field-utils.ts +87 -0
  275. package/src/infrastructure/database/filter-operators.ts +136 -0
  276. package/src/infrastructure/database/formula/formula-trigger-generators.ts +114 -0
  277. package/src/infrastructure/database/formula/formula-utils.ts +440 -0
  278. package/src/infrastructure/database/generators/index-generators.ts +152 -0
  279. package/src/infrastructure/database/generators/trigger-generators.ts +154 -0
  280. package/src/infrastructure/database/index.ts +35 -0
  281. package/src/infrastructure/database/lookup/lookup-expression-generators.ts +356 -0
  282. package/src/infrastructure/database/lookup/lookup-expressions.ts +116 -0
  283. package/src/infrastructure/database/lookup/lookup-view-generators.ts +403 -0
  284. package/src/infrastructure/database/lookup/lookup-view-helpers.ts +65 -0
  285. package/src/infrastructure/database/lookup/lookup-view-triggers.ts +121 -0
  286. package/src/infrastructure/database/migration-audit-trail.ts +375 -0
  287. package/src/infrastructure/database/repositories/activity-log-repository-live.ts +99 -0
  288. package/src/infrastructure/database/repositories/activity-repository-live.ts +21 -0
  289. package/src/infrastructure/database/repositories/analytics-repository-live.ts +316 -0
  290. package/src/infrastructure/database/repositories/auth-repository-live.ts +42 -0
  291. package/src/infrastructure/database/repositories/batch-repository-live.ts +29 -0
  292. package/src/infrastructure/database/repositories/comment-repository-live.ts +39 -0
  293. package/src/infrastructure/database/repositories/table-repository-live.ts +38 -0
  294. package/src/infrastructure/database/schema/schema-dependency-sorting.ts +142 -0
  295. package/src/infrastructure/database/schema/schema-initializer.ts +598 -0
  296. package/src/infrastructure/database/schema-migration/column-detection.ts +286 -0
  297. package/src/infrastructure/database/schema-migration/constants.ts +31 -0
  298. package/src/infrastructure/database/schema-migration/constraint-sync.ts +288 -0
  299. package/src/infrastructure/database/schema-migration/index-sync.ts +108 -0
  300. package/src/infrastructure/database/schema-migration/index.ts +66 -0
  301. package/src/infrastructure/database/schema-migration/migration-statements.ts +106 -0
  302. package/src/infrastructure/database/schema-migration/rename-detection.ts +87 -0
  303. package/src/infrastructure/database/schema-migration/table-operations.ts +65 -0
  304. package/src/infrastructure/database/schema-migration/type-utils.ts +98 -0
  305. package/src/infrastructure/database/schema-migration/types.ts +14 -0
  306. package/src/infrastructure/database/schema-migration-helpers.ts +53 -0
  307. package/src/infrastructure/database/session-context.ts +20 -0
  308. package/src/infrastructure/database/sql/sql-check-constraints.ts +252 -0
  309. package/src/infrastructure/database/sql/sql-column-generators.ts +174 -0
  310. package/src/infrastructure/database/sql/sql-execution.ts +245 -0
  311. package/src/infrastructure/database/sql/sql-field-predicates.ts +81 -0
  312. package/src/infrastructure/database/sql/sql-generators.ts +91 -0
  313. package/src/infrastructure/database/sql/sql-junction-tables.ts +79 -0
  314. package/src/infrastructure/database/sql/sql-key-constraints.ts +210 -0
  315. package/src/infrastructure/database/sql/sql-type-mappings.ts +106 -0
  316. package/src/infrastructure/database/sql/sql-utils.ts +53 -0
  317. package/src/infrastructure/database/table-live-layers.ts +30 -0
  318. package/src/infrastructure/database/table-operations/column-generators.ts +82 -0
  319. package/src/infrastructure/database/table-operations/create-table-sql.ts +81 -0
  320. package/src/infrastructure/database/table-operations/index.ts +55 -0
  321. package/src/infrastructure/database/table-operations/migration-utils.ts +157 -0
  322. package/src/infrastructure/database/table-operations/table-effects.ts +234 -0
  323. package/src/infrastructure/database/table-operations/table-features.ts +96 -0
  324. package/src/infrastructure/database/table-operations/type-compatibility.ts +58 -0
  325. package/src/infrastructure/database/table-operations.ts +47 -0
  326. package/src/infrastructure/database/table-queries/batch/batch-create.ts +80 -0
  327. package/src/infrastructure/database/table-queries/batch/batch-delete.ts +212 -0
  328. package/src/infrastructure/database/table-queries/batch/batch-helpers.ts +124 -0
  329. package/src/infrastructure/database/table-queries/batch/batch-restore.ts +161 -0
  330. package/src/infrastructure/database/table-queries/batch/batch-update.ts +146 -0
  331. package/src/infrastructure/database/table-queries/batch/batch-upsert.ts +357 -0
  332. package/src/infrastructure/database/table-queries/batch/batch.ts +14 -0
  333. package/src/infrastructure/database/table-queries/crud/crud-read.ts +351 -0
  334. package/src/infrastructure/database/table-queries/crud/crud-write.ts +399 -0
  335. package/src/infrastructure/database/table-queries/crud/crud.ts +16 -0
  336. package/src/infrastructure/database/table-queries/index.ts +11 -0
  337. package/src/infrastructure/database/table-queries/mutation-helpers/authorship-helpers.ts +152 -0
  338. package/src/infrastructure/database/table-queries/mutation-helpers/create-record-helpers.ts +90 -0
  339. package/src/infrastructure/database/table-queries/mutation-helpers/delete-helpers.ts +163 -0
  340. package/src/infrastructure/database/table-queries/mutation-helpers/record-fetch-helpers.ts +79 -0
  341. package/src/infrastructure/database/table-queries/mutation-helpers/update-helpers.ts +74 -0
  342. package/src/infrastructure/database/table-queries/query-helpers/activity-log-helpers.ts +53 -0
  343. package/src/infrastructure/database/table-queries/query-helpers/activity-queries.ts +106 -0
  344. package/src/infrastructure/database/table-queries/query-helpers/aggregation-helpers.ts +314 -0
  345. package/src/infrastructure/database/table-queries/query-helpers/comment-queries.ts +414 -0
  346. package/src/infrastructure/database/table-queries/query-helpers/record-validation-queries.ts +126 -0
  347. package/src/infrastructure/database/table-queries/query-helpers/trash-helpers.ts +58 -0
  348. package/src/infrastructure/database/table-queries/shared/error-handling.ts +47 -0
  349. package/src/infrastructure/database/table-queries/shared/typed-execute.ts +27 -0
  350. package/src/infrastructure/database/table-queries/shared/user-join-helpers.ts +38 -0
  351. package/src/infrastructure/database/table-queries/shared/validation.ts +39 -0
  352. package/src/infrastructure/database/views/view-generators.ts +258 -0
  353. package/src/infrastructure/devtools/devtools-layer.ts +43 -0
  354. package/src/infrastructure/devtools/index.ts +8 -0
  355. package/src/infrastructure/email/email-config.ts +103 -0
  356. package/src/infrastructure/email/email-service.ts +152 -0
  357. package/src/infrastructure/email/index.ts +107 -0
  358. package/src/infrastructure/email/nodemailer.ts +125 -0
  359. package/src/infrastructure/email/templates.ts +244 -0
  360. package/src/infrastructure/errors/auth-config-required-error.ts +21 -0
  361. package/src/infrastructure/errors/auth-error.ts +16 -0
  362. package/src/infrastructure/errors/css-compilation-error.ts +14 -0
  363. package/src/infrastructure/errors/index.ts +26 -0
  364. package/src/infrastructure/errors/schema-initialization-error.ts +19 -0
  365. package/src/infrastructure/errors/server-creation-error.ts +14 -0
  366. package/src/infrastructure/filesystem/copy-directory.ts +136 -0
  367. package/src/infrastructure/layers/app-layer.ts +61 -0
  368. package/src/infrastructure/layers/page-renderer-layer.ts +41 -0
  369. package/src/infrastructure/logging/index.ts +8 -0
  370. package/src/infrastructure/logging/logger.ts +204 -0
  371. package/src/infrastructure/schema/file-loader.ts +53 -0
  372. package/src/infrastructure/schema/index.ts +15 -0
  373. package/src/infrastructure/schema/remote-loader.ts +48 -0
  374. package/src/infrastructure/server/index.ts +26 -0
  375. package/src/infrastructure/server/language-detection.ts +87 -0
  376. package/src/infrastructure/server/lifecycle.ts +67 -0
  377. package/src/infrastructure/server/route-setup/api-routes.ts +310 -0
  378. package/src/infrastructure/server/route-setup/auth-route-utils.ts +399 -0
  379. package/src/infrastructure/server/route-setup/auth-routes.ts +245 -0
  380. package/src/infrastructure/server/route-setup/openapi-routes.ts +45 -0
  381. package/src/infrastructure/server/route-setup/openapi-schema.ts +120 -0
  382. package/src/infrastructure/server/route-setup/page-routes.ts +219 -0
  383. package/src/infrastructure/server/route-setup/static-assets.ts +191 -0
  384. package/src/infrastructure/server/server-factory-live.ts +45 -0
  385. package/src/infrastructure/server/server.ts +275 -0
  386. package/src/infrastructure/server/ssg-adapter.ts +196 -0
  387. package/src/infrastructure/server/static-site-generator-live.ts +20 -0
  388. package/src/infrastructure/utils/accept-language-parser.ts +106 -0
  389. package/src/infrastructure/utils/glob-matcher.ts +50 -0
  390. package/src/presentation/api/client.ts +114 -0
  391. package/src/presentation/api/middleware/auth.ts +233 -0
  392. package/src/presentation/api/middleware/table.ts +155 -0
  393. package/src/presentation/api/middleware/validation.ts +88 -0
  394. package/src/presentation/api/routes/activity/get-activity-by-id-handler.ts +77 -0
  395. package/src/presentation/api/routes/activity/index.ts +28 -0
  396. package/src/presentation/api/routes/activity.ts +339 -0
  397. package/src/presentation/api/routes/analytics.ts +328 -0
  398. package/src/presentation/api/routes/auth.ts +169 -0
  399. package/src/presentation/api/routes/index.ts +11 -0
  400. package/src/presentation/api/routes/tables/activity-handlers.ts +57 -0
  401. package/src/presentation/api/routes/tables/batch-permission-helpers.ts +163 -0
  402. package/src/presentation/api/routes/tables/batch-routes.ts +355 -0
  403. package/src/presentation/api/routes/tables/comment-handlers.ts +377 -0
  404. package/src/presentation/api/routes/tables/create-record-helpers.ts +179 -0
  405. package/src/presentation/api/routes/tables/effect-runner.ts +58 -0
  406. package/src/presentation/api/routes/tables/error-handlers.ts +53 -0
  407. package/src/presentation/api/routes/tables/field-permission-validation.ts +167 -0
  408. package/src/presentation/api/routes/tables/filter-parser.ts +75 -0
  409. package/src/presentation/api/routes/tables/formula-parser.ts +118 -0
  410. package/src/presentation/api/routes/tables/index.ts +113 -0
  411. package/src/presentation/api/routes/tables/list-records-filter.ts +54 -0
  412. package/src/presentation/api/routes/tables/param-parsers.ts +59 -0
  413. package/src/presentation/api/routes/tables/record-handlers.ts +484 -0
  414. package/src/presentation/api/routes/tables/record-routes.ts +53 -0
  415. package/src/presentation/api/routes/tables/record-update-handler.ts +200 -0
  416. package/src/presentation/api/routes/tables/sort-validation.ts +85 -0
  417. package/src/presentation/api/routes/tables/table-routes.ts +76 -0
  418. package/src/presentation/api/routes/tables/timezone-validation.ts +41 -0
  419. package/src/presentation/api/routes/tables/upsert-helpers.ts +471 -0
  420. package/src/presentation/api/routes/tables/utils.ts +159 -0
  421. package/src/presentation/api/routes/tables/view-routes.ts +51 -0
  422. package/src/presentation/api/routes/tables.ts +9 -0
  423. package/src/presentation/api/utils/context-helpers.ts +43 -0
  424. package/src/presentation/api/utils/error-sanitizer.ts +235 -0
  425. package/src/presentation/api/utils/field-permission-validator.ts +53 -0
  426. package/src/presentation/api/utils/filter-field-validator.ts +90 -0
  427. package/src/presentation/api/utils/index.ts +13 -0
  428. package/src/presentation/api/utils/run-effect.ts +94 -0
  429. package/src/presentation/api/utils/validate-request.ts +89 -0
  430. package/src/presentation/api/validation/index.ts +29 -0
  431. package/src/presentation/api/validation/rules/field-rules.ts +158 -0
  432. package/src/presentation/api/validation/rules/record-rules.ts +73 -0
  433. package/src/presentation/cli/index.ts +19 -0
  434. package/src/presentation/cli/schema-loader.ts +172 -0
  435. package/src/presentation/hooks/use-breakpoint.ts +155 -0
  436. package/src/presentation/rendering/render-error-pages.tsx +60 -0
  437. package/src/presentation/rendering/render-homepage.tsx +137 -0
  438. package/src/presentation/scripts/script-renderers.ts +112 -0
  439. package/src/presentation/styling/animation-composer.ts +117 -0
  440. package/src/presentation/styling/index.ts +13 -0
  441. package/src/presentation/styling/parse-style.ts +243 -0
  442. package/src/presentation/styling/style-utils.ts +50 -0
  443. package/src/presentation/styling/theme-colors.ts +53 -0
  444. package/src/presentation/translations/component-utils.ts +54 -0
  445. package/src/presentation/translations/index.ts +16 -0
  446. package/src/presentation/translations/translation-resolver.ts +22 -0
  447. package/src/presentation/ui/languages/language-switcher.tsx +119 -0
  448. package/src/presentation/ui/metadata/analytics-builders.tsx +174 -0
  449. package/src/presentation/ui/metadata/analytics-head.tsx +39 -0
  450. package/src/presentation/ui/metadata/custom-elements-builders.tsx +157 -0
  451. package/src/presentation/ui/metadata/extract-component-meta.ts +108 -0
  452. package/src/presentation/ui/metadata/head-elements.tsx +164 -0
  453. package/src/presentation/ui/metadata/index.tsx +35 -0
  454. package/src/presentation/ui/metadata/meta-utils.tsx +42 -0
  455. package/src/presentation/ui/metadata/open-graph-meta.tsx +57 -0
  456. package/src/presentation/ui/metadata/structured-data-from-component.tsx +134 -0
  457. package/src/presentation/ui/metadata/structured-data.tsx +88 -0
  458. package/src/presentation/ui/metadata/twitter-card-meta.tsx +80 -0
  459. package/src/presentation/ui/pages/DefaultHomePage.tsx +43 -0
  460. package/src/presentation/ui/pages/DefaultPageConfigs.ts +220 -0
  461. package/src/presentation/ui/pages/DynamicPage.tsx +307 -0
  462. package/src/presentation/ui/pages/ErrorPage.tsx +25 -0
  463. package/src/presentation/ui/pages/NotFoundPage.tsx +25 -0
  464. package/src/presentation/ui/pages/PageBodyScripts.tsx +242 -0
  465. package/src/presentation/ui/pages/PageBodyStyles.ts +52 -0
  466. package/src/presentation/ui/pages/PageHead.tsx +380 -0
  467. package/src/presentation/ui/pages/PageLangResolver.ts +58 -0
  468. package/src/presentation/ui/pages/PageMain.tsx +58 -0
  469. package/src/presentation/ui/pages/PageMetadata.ts +168 -0
  470. package/src/presentation/ui/pages/PageMetadataI18n.ts +169 -0
  471. package/src/presentation/ui/pages/PageScripts.ts +78 -0
  472. package/src/presentation/ui/pages/SectionRenderer.tsx +67 -0
  473. package/src/presentation/ui/pages/SectionSpacing.tsx +131 -0
  474. package/src/presentation/ui/sections/component-renderer.tsx +426 -0
  475. package/src/presentation/ui/sections/component-renderer.types.ts +33 -0
  476. package/src/presentation/ui/sections/components/component-reference-handler.tsx +74 -0
  477. package/src/presentation/ui/sections/components/component-resolution.ts +65 -0
  478. package/src/presentation/ui/sections/components/index.ts +9 -0
  479. package/src/presentation/ui/sections/hero.tsx +394 -0
  480. package/src/presentation/ui/sections/props/component-builder.ts +183 -0
  481. package/src/presentation/ui/sections/props/element-props.ts +179 -0
  482. package/src/presentation/ui/sections/props/index.ts +9 -0
  483. package/src/presentation/ui/sections/props/prop-conversion.ts +171 -0
  484. package/src/presentation/ui/sections/props/props-builder-config.ts +42 -0
  485. package/src/presentation/ui/sections/props/props-builder.ts +296 -0
  486. package/src/presentation/ui/sections/renderers/element-renderers/html-element-renderer.tsx +124 -0
  487. package/src/presentation/ui/sections/renderers/element-renderers/index.ts +59 -0
  488. package/src/presentation/ui/sections/renderers/element-renderers/interactive-renderers.tsx +231 -0
  489. package/src/presentation/ui/sections/renderers/element-renderers/media-renderers.tsx +102 -0
  490. package/src/presentation/ui/sections/renderers/element-renderers/text-content-renderers.tsx +42 -0
  491. package/src/presentation/ui/sections/renderers/element-renderers.ts +53 -0
  492. package/src/presentation/ui/sections/renderers/html-element-helpers.ts +100 -0
  493. package/src/presentation/ui/sections/renderers/specialized-renderers.tsx +212 -0
  494. package/src/presentation/ui/sections/rendering/component-dispatch-config.ts +31 -0
  495. package/src/presentation/ui/sections/rendering/component-registry/index.ts +39 -0
  496. package/src/presentation/ui/sections/rendering/component-registry/interactive-components.ts +54 -0
  497. package/src/presentation/ui/sections/rendering/component-registry/media-components.ts +36 -0
  498. package/src/presentation/ui/sections/rendering/component-registry/special-components.tsx +153 -0
  499. package/src/presentation/ui/sections/rendering/component-registry/structural-components.ts +215 -0
  500. package/src/presentation/ui/sections/rendering/component-registry/text-components.ts +57 -0
  501. package/src/presentation/ui/sections/rendering/component-registry-helpers.tsx +29 -0
  502. package/src/presentation/ui/sections/rendering/component-registry.tsx +21 -0
  503. package/src/presentation/ui/sections/rendering/component-type-dispatcher.tsx +33 -0
  504. package/src/presentation/ui/sections/rendering/index.ts +9 -0
  505. package/src/presentation/ui/sections/responsive/responsive-children-builder.tsx +96 -0
  506. package/src/presentation/ui/sections/responsive/responsive-content-builder.tsx +95 -0
  507. package/src/presentation/ui/sections/responsive/responsive-props-merger.ts +195 -0
  508. package/src/presentation/ui/sections/responsive/responsive-resolver.ts +213 -0
  509. package/src/presentation/ui/sections/styling/animation-composer-wrapper.ts +65 -0
  510. package/src/presentation/ui/sections/styling/class-builders.ts +45 -0
  511. package/src/presentation/ui/sections/styling/color-resolver.ts +43 -0
  512. package/src/presentation/ui/sections/styling/hover-interaction-handler.ts +107 -0
  513. package/src/presentation/ui/sections/styling/index.ts +9 -0
  514. package/src/presentation/ui/sections/styling/interaction-props-builder.ts +55 -0
  515. package/src/presentation/ui/sections/styling/shadow-resolver.ts +83 -0
  516. package/src/presentation/ui/sections/styling/spacing-resolver.ts +104 -0
  517. package/src/presentation/ui/sections/styling/style-processor.ts +170 -0
  518. package/src/presentation/ui/sections/styling/theme-tokens.ts +184 -0
  519. package/src/presentation/ui/sections/translations/i18n-content-resolver.ts +198 -0
  520. package/src/presentation/ui/sections/translations/index.ts +9 -0
  521. package/src/presentation/ui/sections/translations/translation-handler.ts +143 -0
  522. package/src/presentation/ui/sections/translations/variable-substitution.ts +225 -0
  523. package/src/presentation/ui/sections/utils/time-parser.ts +82 -0
  524. package/src/presentation/utils/link-attributes.ts +50 -0
  525. package/src/presentation/utils/string-utils.ts +58 -0
  526. package/src/presentation/utils/styles.ts +50 -0
  527. package/tsconfig.json +46 -0
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { sendEmail } from '../../email/email-service'
9
+ import { passwordResetEmail, emailVerificationEmail } from '../../email/templates'
10
+ import { logError } from '../../logging'
11
+ import type { Auth, AuthEmailTemplate } from '@/domain/models/app/auth'
12
+
13
+ /**
14
+ * Substitute variables in a template string
15
+ *
16
+ * Replaces $variable patterns with actual values from the context.
17
+ * Supported variables: $name, $url, $email, $organizationName, $inviterName
18
+ */
19
+ const substituteVariables = (
20
+ template: string,
21
+ context: Readonly<{
22
+ name?: string
23
+ url: string
24
+ email: string
25
+ organizationName?: string
26
+ inviterName?: string
27
+ }>
28
+ ): string => {
29
+ return template
30
+ .replace(/\$name/g, context.name ?? 'there')
31
+ .replace(/\$url/g, context.url)
32
+ .replace(/\$email/g, context.email)
33
+ .replace(/\$organizationName/g, context.organizationName ?? 'the organization')
34
+ .replace(/\$inviterName/g, context.inviterName ?? 'Someone')
35
+ }
36
+
37
+ /**
38
+ * Email handler configuration for the factory
39
+ */
40
+ type EmailHandlerConfig = Readonly<{
41
+ /** Email type for logging (e.g., 'password reset', 'verification') */
42
+ emailType: string
43
+ /** Function to build the action URL from base URL and token */
44
+ buildUrl: (url: string, token: string) => string
45
+ /** Function to generate the default template when no custom template is provided */
46
+ getDefaultTemplate: (params: Readonly<{ userName?: string; actionUrl: string }>) => Readonly<{
47
+ subject: string
48
+ html: string
49
+ text: string
50
+ }>
51
+ }>
52
+
53
+ /**
54
+ * Generic email handler factory - eliminates duplication between email types
55
+ *
56
+ * Creates a Better Auth email callback that:
57
+ * 1. Builds the action URL using the provided strategy
58
+ * 2. Sends custom template if provided (with variable substitution)
59
+ * 3. Falls back to default template otherwise
60
+ * 4. Handles errors silently to prevent user enumeration
61
+ */
62
+ const createEmailHandler = (config: EmailHandlerConfig, customTemplate?: AuthEmailTemplate) => {
63
+ return async ({
64
+ user,
65
+ url,
66
+ token,
67
+ }: Readonly<{
68
+ user: Readonly<{ email: string; name?: string }>
69
+ url: string
70
+ token: string
71
+ }>) => {
72
+ const actionUrl = config.buildUrl(url, token)
73
+ const context = { name: user.name, url: actionUrl, email: user.email }
74
+
75
+ try {
76
+ // Custom template takes precedence - use it entirely (don't mix with defaults)
77
+ if (customTemplate?.subject) {
78
+ // eslint-disable-next-line functional/no-expression-statements -- Better Auth email callback requires side effect
79
+ await sendEmail({
80
+ to: user.email,
81
+ subject: substituteVariables(customTemplate.subject, context),
82
+ html: customTemplate.html ? substituteVariables(customTemplate.html, context) : undefined,
83
+ text: customTemplate.text ? substituteVariables(customTemplate.text, context) : undefined,
84
+ })
85
+ } else {
86
+ // Use default template
87
+ const defaultTemplate = config.getDefaultTemplate({
88
+ userName: user.name,
89
+ actionUrl,
90
+ })
91
+
92
+ // eslint-disable-next-line functional/no-expression-statements -- Better Auth email callback requires side effect
93
+ await sendEmail({
94
+ to: user.email,
95
+ subject: defaultTemplate.subject,
96
+ html: defaultTemplate.html,
97
+ text: defaultTemplate.text,
98
+ })
99
+ }
100
+ } catch (error) {
101
+ // Don't throw - silent failure prevents user enumeration attacks
102
+ logError(`[EMAIL] Failed to send ${config.emailType} email to ${user.email}`, error)
103
+ }
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Create password reset email handler with optional custom templates
109
+ */
110
+ const createPasswordResetEmailHandler = (customTemplate?: AuthEmailTemplate) =>
111
+ createEmailHandler(
112
+ {
113
+ emailType: 'password reset',
114
+ buildUrl: (url, token) => `${url}?token=${token}`,
115
+ getDefaultTemplate: ({ userName, actionUrl }) =>
116
+ passwordResetEmail({ userName, resetUrl: actionUrl, expiresIn: '1 hour' }),
117
+ },
118
+ customTemplate
119
+ )
120
+
121
+ /**
122
+ * Create email verification handler with optional custom templates
123
+ */
124
+ const createVerificationEmailHandler = (customTemplate?: AuthEmailTemplate) =>
125
+ createEmailHandler(
126
+ {
127
+ emailType: 'verification',
128
+ // Better Auth sometimes includes token in URL already
129
+ buildUrl: (url, token) => (url.includes('token=') ? url : `${url}?token=${token}`),
130
+ getDefaultTemplate: ({ userName, actionUrl }) =>
131
+ emailVerificationEmail({ userName, verifyUrl: actionUrl, expiresIn: '24 hours' }),
132
+ },
133
+ customTemplate
134
+ )
135
+
136
+ /**
137
+ * Create magic link email handler with optional custom templates
138
+ */
139
+ const createMagicLinkEmailHandler = (customTemplate?: AuthEmailTemplate) =>
140
+ createEmailHandler(
141
+ {
142
+ emailType: 'magic link',
143
+ buildUrl: (url, token) => `${url}?token=${token}`,
144
+ getDefaultTemplate: ({ userName, actionUrl }) => ({
145
+ subject: 'Sign in to your account',
146
+ html: `<p>Hi ${userName ?? 'there'},</p><p>Click here to sign in: <a href="${actionUrl}">Sign In</a></p><p>This link will expire in 10 minutes.</p>`,
147
+ text: `Hi ${userName ?? 'there'},\n\nClick here to sign in: ${actionUrl}\n\nThis link will expire in 10 minutes.`,
148
+ }),
149
+ },
150
+ customTemplate
151
+ )
152
+
153
+ /**
154
+ * Create email handlers from auth configuration
155
+ */
156
+ export const createEmailHandlers = (authConfig?: Auth) => {
157
+ return {
158
+ passwordReset: createPasswordResetEmailHandler(authConfig?.emailTemplates?.resetPassword),
159
+ verification: createVerificationEmailHandler(authConfig?.emailTemplates?.verification),
160
+ magicLink: createMagicLinkEmailHandler(authConfig?.emailTemplates?.magicLink),
161
+ }
162
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ /**
9
+ * Better Auth Module
10
+ *
11
+ * Provides authentication functionality using Better Auth library.
12
+ * Re-exports all auth-related services and types.
13
+ */
14
+ export { auth } from './auth'
15
+ export { Auth, AuthLive, createAuthLayer } from './layer'
16
+ export { AuthError } from '../../errors/auth-error'
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { Context, Effect, Layer } from 'effect'
9
+ import { AuthError } from '../../errors/auth-error'
10
+ import { createAuthInstance } from './auth'
11
+ import type { auth } from './auth'
12
+ import type { Auth as AuthConfig } from '@/domain/models/app/auth'
13
+
14
+ // Re-export AuthError for convenience
15
+ export { AuthError }
16
+
17
+ /**
18
+ * Auth Effect Context
19
+ *
20
+ * Provides authentication service for dependency injection in Effect programs.
21
+ * Use this in Application layer to access authentication without direct imports.
22
+ *
23
+ * Implementation uses Better Auth library internally.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const protectedProgram = Effect.gen(function* () {
28
+ * const authService = yield* Auth
29
+ * const session = yield* authService.requireSession(headers)
30
+ * return { userId: session.user.id, email: session.user.email }
31
+ * })
32
+ * ```
33
+ */
34
+ export class Auth extends Context.Tag('Auth')<
35
+ Auth,
36
+ {
37
+ readonly api: ReturnType<typeof createAuthInstance>['api']
38
+ readonly handler: ReturnType<typeof createAuthInstance>['handler']
39
+ readonly getSession: (
40
+ headers: Headers
41
+ ) => Effect.Effect<Awaited<ReturnType<typeof auth.api.getSession>>, AuthError>
42
+ readonly requireSession: (
43
+ headers: Headers
44
+ ) => Effect.Effect<NonNullable<Awaited<ReturnType<typeof auth.api.getSession>>>, AuthError>
45
+ }
46
+ >() {}
47
+
48
+ /**
49
+ * Create an Auth Layer with a specific auth configuration
50
+ *
51
+ * This allows us to create an Auth layer with app-specific configuration
52
+ * (e.g., with admin plugin enabled) instead of using the default instance.
53
+ *
54
+ * @param authConfig - Optional auth configuration from app schema
55
+ * @returns Layer providing Auth service with the specified configuration
56
+ */
57
+ export const createAuthLayer = (authConfig?: AuthConfig): Layer.Layer<Auth> => {
58
+ const authInstance = createAuthInstance(authConfig)
59
+
60
+ return Layer.succeed(
61
+ Auth,
62
+ Auth.of({
63
+ api: authInstance.api,
64
+ handler: authInstance.handler,
65
+
66
+ getSession: (headers) =>
67
+ Effect.tryPromise({
68
+ try: () => authInstance.api.getSession({ headers }),
69
+ catch: (error) => new AuthError(error),
70
+ }),
71
+
72
+ requireSession: (headers) =>
73
+ Effect.gen(function* () {
74
+ const session = yield* Effect.tryPromise({
75
+ try: () => authInstance.api.getSession({ headers }),
76
+ catch: (error) => new AuthError(error),
77
+ })
78
+
79
+ if (!session) {
80
+ return yield* Effect.fail(new AuthError('Unauthorized'))
81
+ }
82
+
83
+ return session
84
+ }),
85
+ })
86
+ )
87
+ }
88
+
89
+ /**
90
+ * Live Auth Layer
91
+ *
92
+ * Provides the production authentication service with Effect-wrapped methods.
93
+ * Implementation uses Better Auth library internally with default configuration.
94
+ *
95
+ * @deprecated Use createAuthLayer(authConfig) instead for app-specific configuration
96
+ */
97
+ export const AuthLive = createAuthLayer()
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { admin } from 'better-auth/plugins'
9
+ import type { Auth } from '@/domain/models/app/auth'
10
+
11
+ /**
12
+ * Admin plugin configuration extracted from auth config
13
+ */
14
+ export interface AdminPluginConfig {
15
+ readonly defaultRole: string
16
+ readonly firstUserAdmin: boolean
17
+ readonly impersonation: boolean
18
+ }
19
+
20
+ /**
21
+ * Parse admin plugin configuration from auth config
22
+ *
23
+ * Admin features are always enabled when auth is configured.
24
+ * Uses `defaultRole` from auth config (defaults to 'member').
25
+ */
26
+ export const parseAdminConfig = (authConfig?: Auth): AdminPluginConfig | undefined => {
27
+ if (!authConfig) return undefined
28
+
29
+ return {
30
+ defaultRole: authConfig.defaultRole ?? 'member',
31
+ firstUserAdmin: true,
32
+ impersonation: false,
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Build admin plugin if auth is configured
38
+ *
39
+ * The admin plugin provides:
40
+ * - User management (list, ban, unban, impersonate)
41
+ * - Role-based access control (admin, member, viewer roles)
42
+ *
43
+ * Admin features are always enabled when auth is configured — no separate toggle.
44
+ */
45
+ export const buildAdminPlugin = (authConfig?: Auth) => {
46
+ const config = parseAdminConfig(authConfig)
47
+ if (!config) return []
48
+
49
+ return [
50
+ admin({
51
+ defaultRole: config.defaultRole,
52
+ adminRoles: ['admin'],
53
+ impersonationSessionDuration: config.impersonation ? 60 * 60 : undefined,
54
+ }),
55
+ ]
56
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { magicLink } from 'better-auth/plugins'
9
+ import { hasStrategy } from '@/domain/models/app/auth'
10
+ import type { Auth } from '@/domain/models/app/auth'
11
+
12
+ /**
13
+ * Build magic link plugin if enabled in auth configuration
14
+ */
15
+ export const buildMagicLinkPlugin = (
16
+ sendMagicLink: (data: {
17
+ readonly user: { readonly email: string }
18
+ readonly url: string
19
+ readonly token: string
20
+ }) => Promise<void>,
21
+ authConfig?: Auth
22
+ ) => {
23
+ return hasStrategy(authConfig, 'magicLink')
24
+ ? [
25
+ magicLink({
26
+ sendMagicLink: async ({ email, token, url }) =>
27
+ sendMagicLink({ user: { email }, url, token }),
28
+ }),
29
+ ]
30
+ : []
31
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { twoFactor } from 'better-auth/plugins'
9
+ import type { Auth } from '@/domain/models/app/auth'
10
+
11
+ /**
12
+ * Build two-factor plugin if enabled in auth configuration
13
+ *
14
+ * NOTE: modelName option removed - drizzleSchema in auth.ts uses standard model names
15
+ * and Drizzle pgTable() definitions specify actual database table names
16
+ */
17
+ export const buildTwoFactorPlugin = (authConfig?: Auth) => {
18
+ return authConfig?.twoFactor ? [twoFactor()] : []
19
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { relations } from 'drizzle-orm'
9
+ import { text, boolean, timestamp, pgSchema, index } from 'drizzle-orm/pg-core'
10
+
11
+ // Better Auth schema - isolated from main app schema
12
+ // All auth tables are created in the "auth" PostgreSQL schema
13
+ export const authSchema = pgSchema('auth')
14
+
15
+ // Better Auth Tables (using native table names in dedicated auth schema)
16
+ // Schema isolation prevents conflicts when users create their own tables
17
+ export const users = authSchema.table('user', {
18
+ id: text('id').primaryKey(),
19
+ name: text('name').notNull(),
20
+ email: text('email').notNull().unique(),
21
+ emailVerified: boolean('email_verified').notNull().default(false),
22
+ image: text('image'),
23
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
24
+ updatedAt: timestamp('updated_at', { withTimezone: true })
25
+ .notNull()
26
+ .defaultNow()
27
+ .$onUpdate(() => new Date()),
28
+ // Admin plugin fields
29
+ role: text('role'),
30
+ banned: boolean('banned').default(false),
31
+ banReason: text('ban_reason'),
32
+ banExpires: timestamp('ban_expires', { withTimezone: true }),
33
+ // Two-factor plugin fields
34
+ twoFactorEnabled: boolean('two_factor_enabled').default(false),
35
+ })
36
+
37
+ export const sessions = authSchema.table(
38
+ 'session',
39
+ {
40
+ id: text('id').primaryKey(),
41
+ expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
42
+ token: text('token').notNull().unique(),
43
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
44
+ updatedAt: timestamp('updated_at', { withTimezone: true })
45
+ .notNull()
46
+ .defaultNow()
47
+ .$onUpdate(() => new Date()),
48
+ ipAddress: text('ip_address'),
49
+ userAgent: text('user_agent'),
50
+ userId: text('user_id')
51
+ .notNull()
52
+ .references(() => users.id, { onDelete: 'cascade' }),
53
+ // Admin plugin fields
54
+ impersonatedBy: text('impersonated_by'),
55
+ },
56
+ (table) => [index('session_userId_idx').on(table.userId)]
57
+ )
58
+
59
+ export const accounts = authSchema.table(
60
+ 'account',
61
+ {
62
+ id: text('id').primaryKey(),
63
+ accountId: text('account_id').notNull(),
64
+ providerId: text('provider_id').notNull(),
65
+ userId: text('user_id')
66
+ .notNull()
67
+ .references(() => users.id, { onDelete: 'cascade' }),
68
+ accessToken: text('access_token'),
69
+ refreshToken: text('refresh_token'),
70
+ idToken: text('id_token'),
71
+ accessTokenExpiresAt: timestamp('access_token_expires_at', { withTimezone: true }),
72
+ refreshTokenExpiresAt: timestamp('refresh_token_expires_at', { withTimezone: true }),
73
+ scope: text('scope'),
74
+ password: text('password'),
75
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
76
+ updatedAt: timestamp('updated_at', { withTimezone: true })
77
+ .notNull()
78
+ .defaultNow()
79
+ .$onUpdate(() => new Date()),
80
+ },
81
+ (table) => [index('account_userId_idx').on(table.userId)]
82
+ )
83
+
84
+ export const verifications = authSchema.table(
85
+ 'verification',
86
+ {
87
+ id: text('id').primaryKey(),
88
+ identifier: text('identifier').notNull(),
89
+ value: text('value').notNull(),
90
+ expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
91
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
92
+ updatedAt: timestamp('updated_at', { withTimezone: true })
93
+ .defaultNow()
94
+ .notNull()
95
+ .$onUpdate(() => new Date()),
96
+ },
97
+ (table) => [index('verification_identifier_idx').on(table.identifier)]
98
+ )
99
+
100
+ // Two-factor plugin table
101
+ export const twoFactors = authSchema.table(
102
+ 'two_factor',
103
+ {
104
+ id: text('id').primaryKey(),
105
+ userId: text('user_id')
106
+ .notNull()
107
+ .references(() => users.id, { onDelete: 'cascade' }),
108
+ secret: text('secret').notNull(),
109
+ backupCodes: text('backup_codes').notNull(),
110
+ },
111
+ (table) => [
112
+ index('twoFactor_secret_idx').on(table.secret),
113
+ index('twoFactor_userId_idx').on(table.userId),
114
+ ]
115
+ )
116
+
117
+ // Drizzle Relations (for type-safe joins)
118
+ export const usersRelations = relations(users, ({ many }) => ({
119
+ sessions: many(sessions),
120
+ accounts: many(accounts),
121
+ twoFactors: many(twoFactors),
122
+ }))
123
+
124
+ export const sessionsRelations = relations(sessions, ({ one }) => ({
125
+ user: one(users, {
126
+ fields: [sessions.userId],
127
+ references: [users.id],
128
+ }),
129
+ }))
130
+
131
+ export const accountsRelations = relations(accounts, ({ one }) => ({
132
+ user: one(users, {
133
+ fields: [accounts.userId],
134
+ references: [users.id],
135
+ }),
136
+ }))
137
+
138
+ export const twoFactorsRelations = relations(twoFactors, ({ one }) => ({
139
+ user: one(users, {
140
+ fields: [twoFactors.userId],
141
+ references: [users.id],
142
+ }),
143
+ }))
144
+
145
+ // Type inference
146
+ export type User = typeof users.$inferSelect
147
+ export type NewUser = typeof users.$inferInsert
148
+ export type Session = typeof sessions.$inferSelect
149
+ export type Account = typeof accounts.$inferSelect
150
+ export type Verification = typeof verifications.$inferSelect
151
+ export type TwoFactor = typeof twoFactors.$inferSelect
152
+ export type NewTwoFactor = typeof twoFactors.$inferInsert
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ /**
9
+ * Authentication Infrastructure Module
10
+ *
11
+ * Provides authentication services and utilities.
12
+ * Currently uses Better Auth for authentication.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { Auth, AuthLive } from '@/infrastructure/auth'
17
+ *
18
+ * const program = Effect.gen(function* () {
19
+ * const auth = yield* Auth
20
+ * const session = yield* auth.requireSession(headers)
21
+ * return session.user
22
+ * }).pipe(Effect.provide(AuthLive))
23
+ * ```
24
+ */
25
+
26
+ export { auth } from './better-auth/auth'
27
+ export { Auth, AuthLive, AuthError } from './better-auth/layer'
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { Effect, Ref, pipe } from 'effect'
9
+ import type { Theme } from '@/domain/models/app/theme'
10
+
11
+ /**
12
+ * Compiled CSS result with metadata
13
+ */
14
+ export interface CompiledCSS {
15
+ readonly css: string
16
+ readonly timestamp: number
17
+ }
18
+
19
+ /**
20
+ * In-memory cache for compiled CSS using Effect.Ref
21
+ * Stores multiple themes keyed by normalized theme hash
22
+ * Avoids recompiling on every request for better performance
23
+ * Uses functional state management to avoid mutations
24
+ */
25
+ const cssCache = Ref.unsafeMake<Map<string, CompiledCSS>>(new Map())
26
+
27
+ /**
28
+ * Recursively sort object keys for consistent JSON serialization
29
+ * This ensures the same theme always produces the same cache key
30
+ * regardless of property insertion order
31
+ */
32
+ const sortObjectKeys = (obj: unknown): unknown => {
33
+ if (obj === null || typeof obj !== 'object') {
34
+ return obj
35
+ }
36
+
37
+ if (Array.isArray(obj)) {
38
+ return obj.map(sortObjectKeys)
39
+ }
40
+
41
+ const record = obj as Record<string, unknown>
42
+ const sortedKeys = Object.keys(record).toSorted()
43
+
44
+ return sortedKeys.reduce<Record<string, unknown>>(
45
+ (acc, key) => ({ ...acc, [key]: sortObjectKeys(record[key]) }),
46
+ {}
47
+ )
48
+ }
49
+
50
+ /**
51
+ * Normalize theme for consistent cache key generation
52
+ * Sorts object keys recursively to ensure property order independence
53
+ *
54
+ * @param theme - Optional theme configuration
55
+ * @returns Normalized theme (or undefined if no theme)
56
+ */
57
+ export const normalizeTheme = (theme?: Theme): Theme | undefined => {
58
+ if (!theme) return undefined
59
+ return sortObjectKeys(theme) as Theme
60
+ }
61
+
62
+ /**
63
+ * Create theme cache key from app theme
64
+ * Returns consistent hash for same theme content regardless of property order
65
+ *
66
+ * @param theme - Optional theme configuration
67
+ * @returns Cache key string (JSON stringified normalized theme)
68
+ *
69
+ * @example
70
+ * // These produce the same cache key:
71
+ * getThemeCacheKey({ colors: { primary: '#ff5733' }, fonts: { sans: 'Inter' } })
72
+ * getThemeCacheKey({ fonts: { sans: 'Inter' }, colors: { primary: '#ff5733' } })
73
+ */
74
+ export const getThemeCacheKey = (theme?: Theme): string => {
75
+ const normalized = normalizeTheme(theme)
76
+ return JSON.stringify(normalized ?? {})
77
+ }
78
+
79
+ /**
80
+ * Get cached CSS if available
81
+ *
82
+ * @param cacheKey - Cache key for the theme
83
+ * @returns Effect that yields cached CSS or undefined
84
+ */
85
+ export const getCachedCSS = (cacheKey: string): Effect.Effect<CompiledCSS | undefined, never> =>
86
+ pipe(
87
+ Ref.get(cssCache),
88
+ Effect.map((cache) => cache.get(cacheKey))
89
+ )
90
+
91
+ /**
92
+ * Store compiled CSS in cache
93
+ *
94
+ * @param cacheKey - Cache key for the theme
95
+ * @param compiled - Compiled CSS result
96
+ * @returns Effect that updates the cache
97
+ */
98
+ export const setCachedCSS = (cacheKey: string, compiled: CompiledCSS): Effect.Effect<void, never> =>
99
+ Ref.update(cssCache, (currentCache) => new Map([...currentCache, [cacheKey, compiled]]))
100
+
101
+ /**
102
+ * Clear all cached CSS (useful for testing or hot reload)
103
+ *
104
+ * @returns Effect that clears the cache
105
+ */
106
+ export const clearCSSCache = (): Effect.Effect<void, never> => Ref.set(cssCache, new Map())
107
+
108
+ /**
109
+ * Get or compute cached CSS
110
+ * This is a convenience function that combines getCachedCSS and setCachedCSS
111
+ * with a computation function for cleaner usage
112
+ *
113
+ * @param cacheKey - Cache key for the theme
114
+ * @param compute - Effect that computes the CSS if not cached
115
+ * @returns Effect that yields cached or newly computed CSS
116
+ */
117
+ export const getOrComputeCachedCSS = <E>(
118
+ cacheKey: string,
119
+ compute: Effect.Effect<CompiledCSS, E>
120
+ ): Effect.Effect<CompiledCSS, E> =>
121
+ Effect.gen(function* () {
122
+ const cached = yield* getCachedCSS(cacheKey)
123
+ if (cached !== undefined) {
124
+ return cached
125
+ }
126
+
127
+ const compiled = yield* compute
128
+ yield* setCachedCSS(cacheKey, compiled)
129
+ return compiled
130
+ })