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,375 @@
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
+ * Migration Audit Trail
10
+ *
11
+ * Provides functions to track schema migrations, record checksums, and log rollback operations.
12
+ *
13
+ * ## Why Raw SQL Instead of Drizzle Query Builder?
14
+ *
15
+ * These functions use raw SQL via `executeSQL()` instead of Drizzle's query builder because:
16
+ *
17
+ * 1. **Transaction Type Incompatibility**: The schema-initializer uses `SQL.begin()` from bun:sql,
18
+ * which provides a transaction object with only an `unsafe()` method for raw SQL execution.
19
+ *
20
+ * 2. **Drizzle Requirement**: Drizzle's query builder (`insert()`, `select()`, `where()`) requires
21
+ * a full `drizzle()` database instance wrapping an `SQL` client, not the raw transaction object.
22
+ *
23
+ * 3. **Architectural Constraint**: Refactoring to use `db.transaction()` instead of `SQL.begin()`
24
+ * would require significant changes to schema-initializer.ts and related files.
25
+ *
26
+ * The Drizzle schema definitions in `./drizzle/schema/migration-audit.ts` are used for:
27
+ * - Drizzle migrations (creating the tables)
28
+ * - Type exports for consumers
29
+ * - Table name constants (below)
30
+ *
31
+ * @see src/infrastructure/database/drizzle/schema/migration-audit.ts - Drizzle schema definitions
32
+ * @see src/infrastructure/database/schema-initializer.ts - Uses SQL.begin() transactions
33
+ */
34
+
35
+ import { createHash } from 'node:crypto'
36
+ import { getTableName } from 'drizzle-orm'
37
+ import { Effect } from 'effect'
38
+ import { logInfo } from '@/infrastructure/logging/logger'
39
+ import {
40
+ sovriumMigrationHistory,
41
+ sovriumMigrationLog,
42
+ sovriumSchemaChecksum,
43
+ } from './drizzle/schema'
44
+ import { executeSQL, SQLExecutionError, type TransactionLike } from './sql/sql-execution'
45
+ import { escapeSqlString } from './sql/sql-utils'
46
+ import type { App } from '@/domain/models/app'
47
+
48
+ // Re-export types from Drizzle schema for consumers
49
+ export type {
50
+ SovriumMigrationHistory,
51
+ SovriumMigrationLog,
52
+ SovriumSchemaChecksum,
53
+ } from './drizzle/schema'
54
+
55
+ /**
56
+ * Table name constants derived from Drizzle schema
57
+ * Using getTableName() ensures consistency with schema definitions
58
+ * Schema-qualified names are used for system schema tables
59
+ */
60
+ const MIGRATION_HISTORY_TABLE = `system.${getTableName(sovriumMigrationHistory)}`
61
+ const MIGRATION_LOG_TABLE = `system.${getTableName(sovriumMigrationLog)}`
62
+ const SCHEMA_CHECKSUM_TABLE = `system.${getTableName(sovriumSchemaChecksum)}`
63
+
64
+ /**
65
+ * Create schema snapshot object from app configuration
66
+ * Extracts tables array (including views) for consistent serialization
67
+ *
68
+ * IMPORTANT: Views are part of table definitions (table.views property).
69
+ * Including tables in the snapshot automatically includes views, since views
70
+ * are nested within table objects. This ensures that view changes trigger
71
+ * schema migrations correctly.
72
+ */
73
+ const createSchemaSnapshot = (app: App): { readonly tables: readonly object[] } => ({
74
+ tables: app.tables ?? [],
75
+ })
76
+
77
+ /**
78
+ * Recursively sort object keys to ensure consistent serialization
79
+ * PostgreSQL JSONB reorders object properties, so we must normalize before hashing
80
+ */
81
+ const sortObjectKeys = (obj: unknown): unknown => {
82
+ if (obj === null || obj === undefined) return obj
83
+ if (typeof obj !== 'object') return obj
84
+ if (Array.isArray(obj)) return obj.map(sortObjectKeys)
85
+
86
+ // Sort object keys alphabetically and recursively sort nested objects
87
+ const objRecord = obj as Record<string, unknown>
88
+ const keys = Object.keys(objRecord).toSorted()
89
+
90
+ return keys.reduce(
91
+ (sorted, key) => ({
92
+ ...sorted,
93
+ [key]: sortObjectKeys(objRecord[key]),
94
+ }),
95
+ {} as Record<string, unknown>
96
+ )
97
+ }
98
+
99
+ /**
100
+ * Calculate SHA-256 checksum from schema tables
101
+ * Used by both generation and validation
102
+ *
103
+ * CRITICAL: Properties are sorted before hashing to ensure consistent checksums
104
+ * regardless of property insertion order (JavaScript objects) or JSONB normalization.
105
+ */
106
+ const calculateChecksum = (tables: readonly object[], context: string = ''): string => {
107
+ // Sort object keys recursively to normalize property order
108
+ const normalizedTables = sortObjectKeys(tables)
109
+ const schemaJson = JSON.stringify(normalizedTables, undefined, 2)
110
+
111
+ if (context) {
112
+ logInfo(`[calculateChecksum] DEBUG ${context} - JSON string: ${schemaJson.substring(0, 300)}`)
113
+ logInfo(`[calculateChecksum] DEBUG ${context} - JSON length: ${schemaJson.length}`)
114
+ }
115
+ return createHash('sha256').update(schemaJson).digest('hex')
116
+ }
117
+
118
+ /**
119
+ * Generate checksum for the current schema state
120
+ * Uses SHA-256 hash of the JSON-serialized schema
121
+ */
122
+ export const generateSchemaChecksum = (app: App): string => {
123
+ const schemaSnapshot = createSchemaSnapshot(app)
124
+ return calculateChecksum(schemaSnapshot.tables, 'GENERATION')
125
+ }
126
+
127
+ /**
128
+ * Record a migration in the history table
129
+ * Stores the checksum, schema snapshot, and timestamp
130
+ */
131
+ export const recordMigration = (
132
+ tx: TransactionLike,
133
+ app: App
134
+ ): Effect.Effect<void, SQLExecutionError> =>
135
+ Effect.gen(function* () {
136
+ logInfo('[recordMigration] Recording migration in history table...')
137
+ const checksum = generateSchemaChecksum(app)
138
+ const schemaSnapshot = createSchemaSnapshot(app)
139
+
140
+ // Get the next version number
141
+ const versionQuery = `
142
+ SELECT COALESCE(MAX(version), 0) + 1 as next_version
143
+ FROM ${MIGRATION_HISTORY_TABLE}
144
+ `
145
+ const versionResult = yield* executeSQL(tx, versionQuery)
146
+ // executeSQL returns an array directly, not {rows, rowCount}
147
+ const nextVersion =
148
+ (versionResult[0] as { next_version: number } | undefined)?.next_version ?? 1
149
+ logInfo(`[recordMigration] Next version: ${nextVersion}`)
150
+
151
+ // Insert migration record
152
+ // SECURITY NOTE: Using string interpolation for version (number) and proper escaping for JSON
153
+ // - nextVersion is a number from database query, not user input
154
+ // - checksum is a hex string from SHA-256 hash, safe
155
+ // - schemaSnapshot is JSON-escaped to prevent SQL injection
156
+ // NOTE: JSON.stringify is appropriate here - serializing trusted data for storage, not validation
157
+ // @effect-diagnostics effect/preferSchemaOverJson:off
158
+ const escapedSchema = escapeSqlString(JSON.stringify(schemaSnapshot))
159
+ const insertSQL = `
160
+ INSERT INTO ${MIGRATION_HISTORY_TABLE} (version, checksum, schema)
161
+ VALUES (${nextVersion}, '${checksum}', '${escapedSchema}')
162
+ `
163
+ yield* executeSQL(tx, insertSQL)
164
+ logInfo('[recordMigration] Migration recorded successfully')
165
+ })
166
+
167
+ /**
168
+ * Log a rollback operation in the migration log table
169
+ * Records the error reason and marks status as COMPLETED
170
+ */
171
+ export const logRollbackOperation = (
172
+ tx: TransactionLike,
173
+ reason: string
174
+ ): Effect.Effect<void, SQLExecutionError> =>
175
+ Effect.gen(function* () {
176
+ logInfo('[logRollbackOperation] Logging rollback operation...')
177
+ // Escape single quotes in reason string to prevent SQL injection
178
+ const escapedReason = escapeSqlString(reason)
179
+ const insertSQL = `
180
+ INSERT INTO ${MIGRATION_LOG_TABLE} (operation, reason, status)
181
+ VALUES ('ROLLBACK', '${escapedReason}', 'COMPLETED')
182
+ `
183
+ yield* executeSQL(tx, insertSQL)
184
+ logInfo('[logRollbackOperation] Rollback operation logged')
185
+ })
186
+
187
+ /**
188
+ * Store the schema checksum in the system.schema_checksum table
189
+ * Uses a singleton row with id='singleton' to store the current checksum
190
+ */
191
+ export const storeSchemaChecksum = (
192
+ tx: TransactionLike,
193
+ app: App
194
+ ): Effect.Effect<void, SQLExecutionError> =>
195
+ Effect.gen(function* () {
196
+ logInfo('[storeSchemaChecksum] Storing schema checksum...')
197
+ const checksum = generateSchemaChecksum(app)
198
+ const schemaSnapshot = createSchemaSnapshot(app)
199
+
200
+ // DEBUG: Log what we're hashing AND storing
201
+ // NOTE: JSON.stringify for pretty-printing debug output, not validation (Effect Schema not needed)
202
+ // @effect-diagnostics effect/preferSchemaOverJson:off
203
+ const tablesJson = JSON.stringify(schemaSnapshot.tables, undefined, 2)
204
+ // @effect-diagnostics effect/preferSchemaOverJson:off
205
+ const fullSchemaJson = JSON.stringify(schemaSnapshot, undefined, 2)
206
+ logInfo(
207
+ `[storeSchemaChecksum] DEBUG - Tables JSON being hashed: ${tablesJson.substring(0, 500)}`
208
+ )
209
+ logInfo(
210
+ `[storeSchemaChecksum] DEBUG - Full schema JSON being stored: ${fullSchemaJson.substring(0, 500)}`
211
+ )
212
+ logInfo(`[storeSchemaChecksum] DEBUG - Generated checksum: ${checksum}`)
213
+
214
+ // Use INSERT ... ON CONFLICT to update existing singleton row or create new one
215
+ // IMPORTANT: Use same JSON formatting (2-space indent) as calculateChecksum for consistency
216
+ const escapedSchema = escapeSqlString(fullSchemaJson)
217
+ const upsertSQL = `
218
+ INSERT INTO ${SCHEMA_CHECKSUM_TABLE} (id, checksum, schema, updated_at)
219
+ VALUES ('singleton', '${checksum}', '${escapedSchema}', NOW())
220
+ ON CONFLICT (id)
221
+ DO UPDATE SET checksum = EXCLUDED.checksum, schema = EXCLUDED.schema, updated_at = NOW()
222
+ `
223
+ yield* executeSQL(tx, upsertSQL)
224
+ logInfo('[storeSchemaChecksum] Schema checksum stored successfully')
225
+ })
226
+
227
+ /**
228
+ * Retrieve the previous schema from the system.schema_checksum table
229
+ * Returns undefined if no previous schema exists (first migration)
230
+ */
231
+ export const getPreviousSchema = (
232
+ tx: TransactionLike
233
+ ): Effect.Effect<{ readonly tables: readonly object[] } | undefined, SQLExecutionError> =>
234
+ Effect.gen(function* () {
235
+ logInfo('[getPreviousSchema] Retrieving previous schema...')
236
+
237
+ // Retrieve previous schema from singleton row
238
+ // Table is guaranteed to exist after Drizzle migrations
239
+ const selectSQL = `SELECT schema FROM ${SCHEMA_CHECKSUM_TABLE} WHERE id = 'singleton'`
240
+ const result = yield* executeSQL(tx, selectSQL)
241
+
242
+ if (!result || result.length === 0) {
243
+ logInfo('[getPreviousSchema] No previous schema found')
244
+ return undefined
245
+ }
246
+
247
+ const schemaData = (result[0] as { schema: { tables: readonly object[] } } | undefined)?.schema
248
+ logInfo('[getPreviousSchema] Previous schema retrieved successfully')
249
+ return schemaData
250
+ })
251
+
252
+ /**
253
+ * Retrieve the stored checksum from the system.schema_checksum table
254
+ * Returns undefined if no previous checksum exists (first migration)
255
+ */
256
+ export const getStoredChecksum = (
257
+ tx: TransactionLike
258
+ ): Effect.Effect<string | undefined, SQLExecutionError> =>
259
+ Effect.gen(function* () {
260
+ logInfo('[getStoredChecksum] Retrieving stored checksum...')
261
+
262
+ // Check if the checksum table exists first
263
+ const tableExistsSQL = `
264
+ SELECT EXISTS (
265
+ SELECT FROM information_schema.tables
266
+ WHERE table_schema = 'system' AND table_name = 'schema_checksum'
267
+ ) as exists
268
+ `
269
+ const tableExistsResult = yield* executeSQL(tx, tableExistsSQL)
270
+ const tableExists = (tableExistsResult[0] as { exists: boolean } | undefined)?.exists
271
+
272
+ if (!tableExists) {
273
+ logInfo('[getStoredChecksum] Checksum table does not exist')
274
+ return undefined
275
+ }
276
+
277
+ // Retrieve checksum from singleton row
278
+ const selectSQL = `SELECT checksum FROM ${SCHEMA_CHECKSUM_TABLE} WHERE id = 'singleton'`
279
+ const result = yield* executeSQL(tx, selectSQL)
280
+
281
+ if (!result || result.length === 0) {
282
+ logInfo('[getStoredChecksum] No stored checksum found')
283
+ return undefined
284
+ }
285
+
286
+ const storedChecksum = (result[0] as { checksum: string } | undefined)?.checksum
287
+ logInfo(`[getStoredChecksum] Retrieved checksum: ${storedChecksum}`)
288
+ return storedChecksum
289
+ })
290
+
291
+ /**
292
+ * Check if checksum table exists in database
293
+ */
294
+ const checksumTableExists = (tx: TransactionLike): Effect.Effect<boolean, SQLExecutionError> =>
295
+ Effect.gen(function* () {
296
+ const tableExistsSQL = `
297
+ SELECT EXISTS (
298
+ SELECT FROM information_schema.tables
299
+ WHERE table_schema = 'system' AND table_name = 'schema_checksum'
300
+ ) as exists
301
+ `
302
+ const tableExistsResult = yield* executeSQL(tx, tableExistsSQL)
303
+ return (tableExistsResult[0] as { exists: boolean } | undefined)?.exists ?? false
304
+ })
305
+
306
+ /**
307
+ * Retrieve stored checksum and schema from database
308
+ */
309
+ const getStoredChecksumData = (
310
+ tx: TransactionLike
311
+ ): Effect.Effect<
312
+ { checksum: string; schema: { tables: readonly object[] } } | undefined,
313
+ SQLExecutionError
314
+ > =>
315
+ Effect.gen(function* () {
316
+ const selectSQL = `SELECT checksum, schema FROM ${SCHEMA_CHECKSUM_TABLE} WHERE id = 'singleton'`
317
+ const result = yield* executeSQL(tx, selectSQL)
318
+
319
+ if (!result || result.length === 0) {
320
+ return undefined
321
+ }
322
+
323
+ return result[0] as { checksum: string; schema: { tables: readonly object[] } } | undefined
324
+ })
325
+
326
+ /**
327
+ * Validate stored checksum against recalculated checksum from stored schema
328
+ * Detects schema drift or checksum tampering
329
+ * Throws error if mismatch detected
330
+ */
331
+ export const validateStoredChecksum = (
332
+ tx: TransactionLike
333
+ ): Effect.Effect<void, SQLExecutionError> =>
334
+ Effect.gen(function* () {
335
+ logInfo('[validateStoredChecksum] Validating stored checksum...')
336
+
337
+ const tableExists = yield* checksumTableExists(tx)
338
+ if (!tableExists) {
339
+ logInfo('[validateStoredChecksum] Checksum table does not exist - skipping validation')
340
+ return
341
+ }
342
+
343
+ const row = yield* getStoredChecksumData(tx)
344
+ if (!row) {
345
+ logInfo('[validateStoredChecksum] No stored checksum found - skipping validation')
346
+ return
347
+ }
348
+
349
+ const storedChecksum = row.checksum
350
+ const storedSchema = row.schema
351
+ const recalculatedChecksum = calculateChecksum(storedSchema.tables, 'VALIDATION')
352
+
353
+ logInfo(`[validateStoredChecksum] Stored checksum: ${storedChecksum}`)
354
+ logInfo(`[validateStoredChecksum] Recalculated checksum: ${recalculatedChecksum}`)
355
+
356
+ // DEBUG: Log the actual JSON being hashed (JSON.stringify for debug output, not validation)
357
+ // NOTE: JSON.stringify appropriate for debug output (Effect Schema not needed)
358
+ // @effect-diagnostics effect/preferSchemaOverJson:off
359
+ logInfo(
360
+ `[validateStoredChecksum] DEBUG - Stored tables JSON: ${JSON.stringify(storedSchema.tables, undefined, 2).substring(0, 500)}`
361
+ )
362
+
363
+ if (storedChecksum !== recalculatedChecksum) {
364
+ const errorMsg =
365
+ 'Schema drift detected: checksum mismatch. The stored checksum does not match the recalculated checksum from the stored schema. This indicates database tampering or corruption.'
366
+ logInfo(`[validateStoredChecksum] ${errorMsg}`)
367
+ return yield* new SQLExecutionError({
368
+ message: errorMsg,
369
+ sql: 'validateStoredChecksum',
370
+ cause: new Error(errorMsg),
371
+ })
372
+ }
373
+
374
+ logInfo('[validateStoredChecksum] Checksum validation passed')
375
+ })
@@ -0,0 +1,99 @@
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 { desc, eq, gte, sql } from 'drizzle-orm'
9
+ import { Effect, Layer } from 'effect'
10
+ import {
11
+ ActivityLogRepository,
12
+ ActivityLogDatabaseError,
13
+ } from '@/application/ports/repositories/activity-log-repository'
14
+ import { users } from '@/infrastructure/auth/better-auth/schema'
15
+ import { db } from '@/infrastructure/database'
16
+ import { activityLogs } from '@/infrastructure/database/drizzle/schema/activity-log'
17
+
18
+ /**
19
+ * Activity Log Repository Implementation
20
+ *
21
+ * Uses Drizzle ORM query builder for type-safe, SQL-injection-proof queries.
22
+ */
23
+ export const ActivityLogRepositoryLive = Layer.succeed(ActivityLogRepository, {
24
+ /**
25
+ * List all activity logs with user metadata
26
+ */
27
+ listAll: () =>
28
+ Effect.tryPromise({
29
+ try: async () => {
30
+ const rows = await db
31
+ .select({
32
+ id: activityLogs.id,
33
+ createdAt: activityLogs.createdAt,
34
+ userId: activityLogs.userId,
35
+ sessionId: activityLogs.sessionId,
36
+ action: activityLogs.action,
37
+ tableName: activityLogs.tableName,
38
+ tableId: activityLogs.tableId,
39
+ recordId: activityLogs.recordId,
40
+ changes: activityLogs.changes,
41
+ ipAddress: activityLogs.ipAddress,
42
+ userAgent: activityLogs.userAgent,
43
+ userName: users.name,
44
+ userEmail: users.email,
45
+ })
46
+ .from(activityLogs)
47
+ .leftJoin(users, eq(activityLogs.userId, users.id))
48
+ .where(gte(activityLogs.createdAt, sql`NOW() - INTERVAL '1 year'`))
49
+ .orderBy(sql`(${activityLogs.userId} IS NULL) DESC`, desc(activityLogs.createdAt))
50
+
51
+ return rows.map((row) => ({
52
+ id: row.id,
53
+ createdAt: row.createdAt,
54
+ userId: row.userId,
55
+ sessionId: row.sessionId,
56
+ action: row.action,
57
+ tableName: row.tableName,
58
+ tableId: row.tableId,
59
+ recordId: row.recordId,
60
+ changes: row.changes,
61
+ ipAddress: row.ipAddress,
62
+ userAgent: row.userAgent,
63
+ user:
64
+ row.userId && row.userName && row.userEmail
65
+ ? { id: row.userId, name: row.userName, email: row.userEmail }
66
+ : // eslint-disable-next-line unicorn/no-null -- Null is intentional for system-logged activities (no user_id)
67
+ null,
68
+ }))
69
+ },
70
+ catch: (error) => new ActivityLogDatabaseError({ cause: error }),
71
+ }),
72
+
73
+ /**
74
+ * Create activity log entry
75
+ */
76
+ create: (log) =>
77
+ Effect.tryPromise({
78
+ try: async () => {
79
+ const result = await db
80
+ .insert(activityLogs)
81
+ .values({
82
+ id: crypto.randomUUID(),
83
+ userId: log.userId,
84
+ action: log.action,
85
+ tableName: log.tableName,
86
+ tableId: log.tableId,
87
+ recordId: log.recordId,
88
+ changes: log.changes,
89
+ sessionId: log.sessionId,
90
+ ipAddress: log.ipAddress,
91
+ userAgent: log.userAgent,
92
+ })
93
+ .returning()
94
+
95
+ return result[0]!
96
+ },
97
+ catch: (error) => new ActivityLogDatabaseError({ cause: error }),
98
+ }),
99
+ })
@@ -0,0 +1,21 @@
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 { Layer } from 'effect'
9
+ import { ActivityRepository } from '@/application/ports/repositories/activity-repository'
10
+ import { getRecordHistory } from '@/infrastructure/database/table-queries/query-helpers/activity-queries'
11
+ import { checkRecordExists } from '@/infrastructure/database/table-queries/query-helpers/record-validation-queries'
12
+
13
+ /**
14
+ * Live implementation of ActivityRepository using activity-queries infrastructure
15
+ *
16
+ * Maps port method names to infrastructure function names.
17
+ */
18
+ export const ActivityRepositoryLive = Layer.succeed(ActivityRepository, {
19
+ getRecordHistory,
20
+ checkRecordExists,
21
+ })