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,108 @@
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 } from 'effect'
9
+ import { generateIndexStatements } from '../generators/index-generators'
10
+ import {
11
+ executeSQLStatements,
12
+ type TransactionLike,
13
+ type SQLExecutionError,
14
+ } from '../sql/sql-execution'
15
+ import type { Table } from '@/domain/models/app/table'
16
+
17
+ /**
18
+ * Helper to generate DROP INDEX statements for indexes that need to be removed
19
+ */
20
+ const generateDropIndexStatements = (
21
+ table: Table,
22
+ previousTable:
23
+ | {
24
+ readonly name: string
25
+ readonly fields?: readonly { name?: string; indexed?: boolean }[]
26
+ readonly indexes?: readonly { name: string }[]
27
+ }
28
+ | undefined
29
+ ): readonly string[] => {
30
+ if (!previousTable) return []
31
+
32
+ // Drop indexes for fields that no longer have indexed: true
33
+ const previousIndexedFields =
34
+ previousTable.fields
35
+ ?.filter((f) => f.name && 'indexed' in f && f.indexed)
36
+ .map((f) => f.name!) ?? []
37
+
38
+ const currentIndexedFields = new Set(
39
+ table.fields.filter((f) => 'indexed' in f && f.indexed).map((f) => f.name)
40
+ )
41
+
42
+ const removedIndexedFields = previousIndexedFields.filter(
43
+ (fieldName) => !currentIndexedFields.has(fieldName)
44
+ )
45
+
46
+ const fieldIndexDrops = removedIndexedFields.map((fieldName) => {
47
+ const indexName = `idx_${table.name}_${fieldName}`
48
+ return `DROP INDEX IF EXISTS ${indexName}`
49
+ })
50
+
51
+ // Drop indexes for fields that changed from indexed to unique
52
+ const currentUniqueFields = new Set(
53
+ table.fields.filter((f) => 'unique' in f && f.unique).map((f) => f.name)
54
+ )
55
+
56
+ const indexToUniqueFields = previousIndexedFields.filter((fieldName) =>
57
+ currentUniqueFields.has(fieldName)
58
+ )
59
+
60
+ const indexToUniqueDrops = indexToUniqueFields.map((fieldName) => {
61
+ const indexName = `idx_${table.name}_${fieldName}`
62
+ return `DROP INDEX IF EXISTS ${indexName}`
63
+ })
64
+
65
+ // Drop custom indexes that were removed
66
+ const previousCustomIndexes = previousTable.indexes?.map((idx) => idx.name) ?? []
67
+ const currentCustomIndexes = table.indexes?.map((idx) => idx.name) ?? []
68
+
69
+ const removedCustomIndexes = previousCustomIndexes.filter(
70
+ (name) => !currentCustomIndexes.includes(name)
71
+ )
72
+
73
+ const customIndexDrops = removedCustomIndexes.map((name) => `DROP INDEX IF EXISTS ${name}`)
74
+
75
+ return [...fieldIndexDrops, ...indexToUniqueDrops, ...customIndexDrops]
76
+ }
77
+
78
+ /**
79
+ * Sync indexes for existing table
80
+ * Drops indexes that are no longer needed and creates new indexes
81
+ * This is needed when field indexed property changes or custom indexes are added/removed
82
+ */
83
+ export const syncIndexes = (
84
+ tx: TransactionLike,
85
+ table: Table,
86
+ previousSchema?: { readonly tables: readonly object[] }
87
+ ): Effect.Effect<void, SQLExecutionError> =>
88
+ Effect.gen(function* () {
89
+ // Get previous table definition
90
+ const previousTable = previousSchema?.tables.find(
91
+ (t: object) => 'name' in t && t.name === table.name
92
+ ) as
93
+ | {
94
+ name: string
95
+ fields?: readonly { name?: string; indexed?: boolean }[]
96
+ indexes?: readonly { name: string }[]
97
+ }
98
+ | undefined
99
+
100
+ // Determine which indexes should be dropped
101
+ const dropStatements = generateDropIndexStatements(table, previousTable)
102
+
103
+ // Generate CREATE INDEX statements for all current indexes
104
+ const createStatements = generateIndexStatements(table)
105
+
106
+ // Execute drop statements first, then create statements
107
+ yield* executeSQLStatements(tx, [...dropStatements, ...createStatements])
108
+ })
@@ -0,0 +1,66 @@
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
+ * Schema Migration Helpers
10
+ *
11
+ * This module provides utilities for database schema migrations:
12
+ * - Table operations (rename, drop obsolete)
13
+ * - Column detection (add, drop, modify)
14
+ * - Type conversions and nullability changes
15
+ * - Constraint synchronization (unique, foreign key, check)
16
+ * - Index synchronization
17
+ *
18
+ * All functions use Effect for type-safe error handling.
19
+ */
20
+
21
+ // Type exports
22
+ export type { ExistingColumnInfo } from './column-detection'
23
+ export { type TransactionLike as BunSQLTransaction } from '../sql/sql-execution'
24
+
25
+ // Constants
26
+ export { PROTECTED_SYSTEM_TABLES } from './constants'
27
+
28
+ // Rename detection
29
+ export { detectFieldRenames, detectTableRenames } from './rename-detection'
30
+
31
+ // Table operations
32
+ export { renameTablesIfNeeded, dropObsoleteTables } from './table-operations'
33
+
34
+ // Type utilities
35
+ export {
36
+ normalizeDataType,
37
+ doesColumnTypeMatch,
38
+ generateAlterColumnTypeStatement,
39
+ } from './type-utils'
40
+
41
+ // Column detection
42
+ export {
43
+ needsIdColumnRecreation,
44
+ findColumnsToAdd,
45
+ findColumnsToDrop,
46
+ filterModifiableFields,
47
+ findTypeChanges,
48
+ generateNotNullValidationQuery,
49
+ generateBackfillQuery,
50
+ findNullabilityChanges,
51
+ findDefaultValueChanges,
52
+ buildColumnStatements,
53
+ } from './column-detection'
54
+
55
+ // Migration statement generation
56
+ export { generateAlterTableStatements } from './migration-statements'
57
+
58
+ // Constraint synchronization
59
+ export {
60
+ syncUniqueConstraints,
61
+ syncForeignKeyConstraints,
62
+ syncCheckConstraints,
63
+ } from './constraint-sync'
64
+
65
+ // Index synchronization
66
+ export { syncIndexes } from './index-sync'
@@ -0,0 +1,106 @@
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 {
9
+ needsIdColumnRecreation,
10
+ findColumnsToAdd,
11
+ findColumnsToDrop,
12
+ findTypeChanges,
13
+ findNullabilityChanges,
14
+ findDefaultValueChanges,
15
+ buildColumnStatements,
16
+ type ExistingColumnInfo,
17
+ } from './column-detection'
18
+ import { detectFieldRenames } from './rename-detection'
19
+ import type { Table } from '@/domain/models/app/table'
20
+ import type { Fields } from '@/domain/models/app/table/fields'
21
+
22
+ /** Generate statements for automatic timestamp fields (created_at, updated_at, deleted_at) */
23
+ const generateSpecialFieldStatements = (
24
+ table: Table,
25
+ existingColumns: ReadonlyMap<string, ExistingColumnInfo>
26
+ ): readonly string[] => {
27
+ const fieldNames = new Set(table.fields.map((f) => f.name))
28
+ const needsCreatedAt = !fieldNames.has('created_at') && !existingColumns.has('created_at')
29
+ const needsUpdatedAt = !fieldNames.has('updated_at') && !existingColumns.has('updated_at')
30
+ const needsDeletedAt = !fieldNames.has('deleted_at') && !existingColumns.has('deleted_at')
31
+
32
+ return [
33
+ ...(needsCreatedAt
34
+ ? [`ALTER TABLE ${table.name} ADD COLUMN created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`]
35
+ : []),
36
+ ...(needsUpdatedAt
37
+ ? [`ALTER TABLE ${table.name} ADD COLUMN updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`]
38
+ : []),
39
+ ...(needsDeletedAt ? [`ALTER TABLE ${table.name} ADD COLUMN deleted_at TIMESTAMPTZ`] : []),
40
+ ]
41
+ }
42
+
43
+ /** Validate destructive operations require explicit confirmation */
44
+ const validateDestructiveOps = (table: Table, columnsToDrop: readonly string[]): void => {
45
+ if (columnsToDrop.length > 0 && !table.allowDestructive) {
46
+ const droppedColumns = columnsToDrop.join(', ')
47
+ /* eslint-disable-next-line functional/no-throw-statements */
48
+ throw new Error(
49
+ `Destructive operation detected: Dropping column(s) [${droppedColumns}] from table '${table.name}' requires confirmation. Set allowDestructive: true to proceed with data loss, or keep the field(s) in the schema to preserve data.`
50
+ )
51
+ }
52
+ }
53
+
54
+ /** Generate ALTER TABLE statements for schema migrations */
55
+ export const generateAlterTableStatements = (
56
+ table: Table,
57
+ existingColumns: ReadonlyMap<string, ExistingColumnInfo>,
58
+ previousSchema?: { readonly tables: readonly object[] }
59
+ ): readonly string[] => {
60
+ const primaryKeyFields =
61
+ table.primaryKey?.type === 'composite' ? (table.primaryKey.fields ?? []) : []
62
+ const hasIdField = table.fields.some((field) => field.name === 'id')
63
+ const shouldProtectIdColumn = !hasIdField && !(table.primaryKey && primaryKeyFields.length > 0)
64
+
65
+ if (needsIdColumnRecreation(existingColumns, shouldProtectIdColumn)) return []
66
+
67
+ const fieldRenames = detectFieldRenames(table.name, table.fields, previousSchema)
68
+ const renameStatements = Array.from(fieldRenames.entries()).map(
69
+ ([oldName, newName]) => `ALTER TABLE ${table.name} RENAME COLUMN ${oldName} TO ${newName}`
70
+ )
71
+
72
+ const renamedOldNames = new Set(fieldRenames.keys())
73
+ const renamedNewNames = new Set(fieldRenames.values())
74
+ const schemaFieldsByName = new Map<string, Fields[number]>(
75
+ table.fields.map((field) => [field.name, field])
76
+ )
77
+
78
+ const columnsToAdd = findColumnsToAdd(table, existingColumns, renamedNewNames)
79
+ const columnsToDrop = findColumnsToDrop(
80
+ existingColumns,
81
+ schemaFieldsByName,
82
+ shouldProtectIdColumn,
83
+ renamedOldNames
84
+ )
85
+
86
+ validateDestructiveOps(table, columnsToDrop)
87
+
88
+ const { dropStatements, addStatements } = buildColumnStatements({
89
+ tableName: table.name,
90
+ columnsToDrop,
91
+ columnsToAdd,
92
+ primaryKeyFields,
93
+ allFields: table.fields,
94
+ })
95
+
96
+ // ORDER: rename → drop → add → special fields → type changes → defaults → nullability
97
+ return [
98
+ ...renameStatements,
99
+ ...dropStatements,
100
+ ...addStatements,
101
+ ...generateSpecialFieldStatements(table, existingColumns),
102
+ ...findTypeChanges(table, existingColumns, renamedNewNames),
103
+ ...findDefaultValueChanges(table, existingColumns, renamedNewNames, previousSchema),
104
+ ...findNullabilityChanges(table, existingColumns, renamedNewNames, primaryKeyFields),
105
+ ]
106
+ }
@@ -0,0 +1,87 @@
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 type { Table } from '@/domain/models/app/table'
9
+ import type { Fields } from '@/domain/models/app/table/fields'
10
+
11
+ /**
12
+ * Detect field renames by comparing field IDs between previous and current schema
13
+ * Returns a map of old field name to new field name for renamed fields
14
+ */
15
+ export const detectFieldRenames = (
16
+ tableName: string,
17
+ currentFields: readonly Fields[number][],
18
+ previousSchema?: { readonly tables: readonly object[] }
19
+ ): ReadonlyMap<string, string> => {
20
+ if (!previousSchema) return new Map()
21
+
22
+ // Find previous table definition
23
+ const previousTable = previousSchema.tables.find(
24
+ (t: object) => 'name' in t && t.name === tableName
25
+ ) as { name: string; fields?: readonly { id?: number; name?: string }[] } | undefined
26
+
27
+ if (!previousTable || !previousTable.fields) return new Map()
28
+
29
+ // Build map of field ID to field name for both schemas
30
+ const previousFieldsById = new Map(
31
+ previousTable.fields
32
+ .filter((f) => f.id !== undefined && f.name !== undefined)
33
+ .map((f) => [f.id!, f.name!])
34
+ )
35
+
36
+ const currentFieldsById = new Map(
37
+ currentFields.filter((f) => f.id !== undefined).map((f) => [f.id, f.name])
38
+ )
39
+
40
+ // Detect renames: same ID, different name (functional construction)
41
+ const renames = new Map<string, string>(
42
+ Array.from(currentFieldsById.entries())
43
+ .map(([fieldId, newName]) => {
44
+ const oldName = previousFieldsById.get(fieldId)
45
+ return oldName && oldName !== newName ? [oldName, newName] : undefined
46
+ })
47
+ .filter((entry): entry is [string, string] => entry !== undefined)
48
+ )
49
+
50
+ return renames
51
+ }
52
+
53
+ /**
54
+ * Detect table renames by comparing table IDs between previous and current schema
55
+ * Returns a map of old table name to new table name for renamed tables
56
+ */
57
+ export const detectTableRenames = (
58
+ currentTables: readonly Table[],
59
+ previousSchema?: { readonly tables: readonly object[] }
60
+ ): ReadonlyMap<string, string> => {
61
+ if (!previousSchema) return new Map()
62
+
63
+ // Build map of table ID to table name for both schemas
64
+ const previousTablesById = new Map(
65
+ previousSchema.tables
66
+ .filter((t: object) => 'id' in t && 'name' in t && t.id !== undefined && t.name !== undefined)
67
+ .map((t: object) => [(t as { id: number }).id, (t as { name: string }).name])
68
+ )
69
+
70
+ const currentTablesById = new Map(
71
+ currentTables
72
+ .filter((t): t is Table & { id: number } => t.id !== undefined)
73
+ .map((t) => [t.id, t.name])
74
+ )
75
+
76
+ // Detect renames: same ID, different name (functional construction)
77
+ const renames = new Map<string, string>(
78
+ Array.from(currentTablesById.entries())
79
+ .map(([tableId, newName]) => {
80
+ const oldName = previousTablesById.get(tableId)
81
+ return oldName && oldName !== newName ? [oldName, newName] : undefined
82
+ })
83
+ .filter((entry): entry is [string, string] => entry !== undefined)
84
+ )
85
+
86
+ return renames
87
+ }
@@ -0,0 +1,65 @@
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 } from 'effect'
9
+ import {
10
+ getExistingTableNames,
11
+ executeSQLStatements,
12
+ type TransactionLike,
13
+ type SQLExecutionError,
14
+ } from '../sql/sql-execution'
15
+ import { PROTECTED_SYSTEM_TABLES } from './constants'
16
+ import { detectTableRenames } from './rename-detection'
17
+ import type { Table } from '@/domain/models/app/table'
18
+
19
+ /**
20
+ * Rename tables that have changed names (same table ID, different name)
21
+ * Uses ALTER TABLE RENAME TO to preserve data, indexes, and constraints
22
+ */
23
+ export const renameTablesIfNeeded = (
24
+ tx: TransactionLike,
25
+ tables: readonly Table[],
26
+ previousSchema?: { readonly tables: readonly object[] }
27
+ ): Effect.Effect<void, SQLExecutionError> =>
28
+ Effect.gen(function* () {
29
+ const tableRenames = detectTableRenames(tables, previousSchema)
30
+
31
+ if (tableRenames.size === 0) return
32
+
33
+ // Generate ALTER TABLE RENAME TO statements
34
+ const renameStatements = Array.from(tableRenames.entries()).map(
35
+ ([oldName, newName]) => `ALTER TABLE ${oldName} RENAME TO ${newName}`
36
+ )
37
+
38
+ yield* executeSQLStatements(tx, renameStatements)
39
+ })
40
+
41
+ /**
42
+ * Drop tables that exist in database but are not defined in schema
43
+ *
44
+ * SECURITY NOTE: Table names are validated before reaching this function.
45
+ * This is SAFE because:
46
+ * 1. existingTableNames comes from pg_tables system catalog (trusted source)
47
+ * 2. schemaTableNames comes from validated Effect Schema objects
48
+ * 3. Only tables not in schema are dropped (explicit comparison)
49
+ * 4. Better Auth system tables are protected and never dropped
50
+ */
51
+ export const dropObsoleteTables = (
52
+ tx: TransactionLike,
53
+ tables: readonly Table[]
54
+ ): Effect.Effect<void, SQLExecutionError> =>
55
+ Effect.gen(function* () {
56
+ const existingTableNames = yield* getExistingTableNames(tx)
57
+ const schemaTableNames = new Set(tables.map((table) => table.name))
58
+ const tablesToDrop = existingTableNames.filter(
59
+ (tableName) => !schemaTableNames.has(tableName) && !PROTECTED_SYSTEM_TABLES.has(tableName)
60
+ )
61
+
62
+ // Drop all obsolete tables sequentially
63
+ const dropStatements = tablesToDrop.map((tableName) => `DROP TABLE ${tableName} CASCADE`)
64
+ yield* executeSQLStatements(tx, dropStatements)
65
+ })
@@ -0,0 +1,98 @@
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 { mapFieldTypeToPostgres } from '../sql/sql-generators'
9
+ import type { Fields } from '@/domain/models/app/table/fields'
10
+
11
+ /**
12
+ * Normalize PostgreSQL data type for comparison
13
+ * Maps similar types to a canonical form (e.g., 'varchar' and 'character varying' both map to 'varchar')
14
+ * Strips length specifiers and precision for type matching
15
+ */
16
+ export const normalizeDataType = (dataType: string): string => {
17
+ const normalized = dataType.toLowerCase().trim()
18
+
19
+ // Map 'character varying' to 'varchar' for easier comparison
20
+ if (normalized.startsWith('character varying')) return 'varchar'
21
+ if (normalized.startsWith('timestamp')) return 'timestamp'
22
+ if (normalized.startsWith('numeric') || normalized.startsWith('decimal')) return 'numeric'
23
+
24
+ // Map PostgreSQL ARRAY type to text[] for comparison with field type mappings
25
+ // information_schema.columns returns 'ARRAY' for all array columns
26
+ if (normalized === 'array') return 'text[]'
27
+
28
+ // Strip length specifiers for varchar, char, etc.
29
+ // varchar(255) → varchar, char(10) → char, numeric(10,2) → numeric
30
+ const withoutLength = normalized.replace(/\([^)]*\)/, '')
31
+
32
+ return withoutLength
33
+ }
34
+
35
+ /**
36
+ * Check if column data type matches the expected type from schema
37
+ */
38
+ export const doesColumnTypeMatch = (field: Fields[number], existingDataType: string): boolean => {
39
+ const expectedType = mapFieldTypeToPostgres(field)
40
+ const normalizedExpected = normalizeDataType(expectedType)
41
+ const normalizedExisting = normalizeDataType(existingDataType)
42
+
43
+ // For varchar/text types, check if both are string types
44
+ if (
45
+ (normalizedExpected === 'varchar' || normalizedExpected === 'text') &&
46
+ (normalizedExisting === 'varchar' || normalizedExisting === 'text')
47
+ ) {
48
+ // Match if both are string types (varchar/text are interchangeable for our purposes)
49
+ return normalizedExpected === normalizedExisting
50
+ }
51
+
52
+ // For other types, exact match required
53
+ return normalizedExpected === normalizedExisting
54
+ }
55
+
56
+ /**
57
+ * Generate ALTER COLUMN TYPE statement with USING clause if needed
58
+ * Handles type conversions that require explicit casting or transformation
59
+ */
60
+ export const generateAlterColumnTypeStatement = (
61
+ tableName: string,
62
+ field: Fields[number],
63
+ existingDataType: string
64
+ ): string => {
65
+ const targetType = mapFieldTypeToPostgres(field)
66
+ const normalizedTarget = normalizeDataType(targetType)
67
+ const normalizedExisting = normalizeDataType(existingDataType)
68
+
69
+ // Determine if USING clause is needed for type conversion
70
+ const usingClause = (() => {
71
+ // TEXT → VARCHAR requires LEFT() to truncate
72
+ if (normalizedExisting === 'text' && normalizedTarget === 'varchar') {
73
+ const lengthMatch = targetType.match(/VARCHAR\((\d+)\)/)
74
+ const length = lengthMatch ? lengthMatch[1] : '255'
75
+ return ` USING LEFT(${field.name}, ${length})`
76
+ }
77
+
78
+ // TEXT → INTEGER requires explicit cast
79
+ if (normalizedExisting === 'text' && normalizedTarget === 'integer') {
80
+ return ` USING ${field.name}::INTEGER`
81
+ }
82
+
83
+ // TEXT → TIMESTAMP requires explicit cast
84
+ if (normalizedExisting === 'text' && normalizedTarget === 'timestamp') {
85
+ return ` USING ${field.name}::TIMESTAMPTZ`
86
+ }
87
+
88
+ // INTEGER → NUMERIC conversion (automatic, no USING clause needed)
89
+ // PostgreSQL can implicitly convert INTEGER to NUMERIC
90
+ if (normalizedExisting === 'integer' && normalizedTarget === 'numeric') {
91
+ return ''
92
+ }
93
+
94
+ return ''
95
+ })()
96
+
97
+ return `ALTER TABLE ${tableName} ALTER COLUMN ${field.name} TYPE ${targetType}${usingClause}`
98
+ }
@@ -0,0 +1,14 @@
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 type { TransactionLike } from '../sql/sql-execution'
9
+
10
+ /**
11
+ * Type definition for Bun SQL transaction
12
+ * Re-exported from sql-execution.ts for backward compatibility
13
+ */
14
+ export type BunSQLTransaction = TransactionLike
@@ -0,0 +1,53 @@
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
+ * Schema Migration Helpers - Re-export Module
10
+ *
11
+ * This file re-exports all schema migration utilities from the split module structure.
12
+ * Maintained for backwards compatibility with existing imports.
13
+ *
14
+ * @see ./schema-migration/ for the actual implementations
15
+ */
16
+
17
+ // Type exports
18
+ export type { BunSQLTransaction, ExistingColumnInfo } from './schema-migration'
19
+
20
+ // All function exports
21
+ export {
22
+ // Constants
23
+ PROTECTED_SYSTEM_TABLES,
24
+ // Rename detection
25
+ detectFieldRenames,
26
+ detectTableRenames,
27
+ // Table operations
28
+ renameTablesIfNeeded,
29
+ dropObsoleteTables,
30
+ // Type utilities
31
+ normalizeDataType,
32
+ doesColumnTypeMatch,
33
+ generateAlterColumnTypeStatement,
34
+ // Column detection
35
+ needsIdColumnRecreation,
36
+ findColumnsToAdd,
37
+ findColumnsToDrop,
38
+ filterModifiableFields,
39
+ findTypeChanges,
40
+ generateNotNullValidationQuery,
41
+ generateBackfillQuery,
42
+ findNullabilityChanges,
43
+ findDefaultValueChanges,
44
+ buildColumnStatements,
45
+ // Migration statement generation
46
+ generateAlterTableStatements,
47
+ // Constraint synchronization
48
+ syncUniqueConstraints,
49
+ syncForeignKeyConstraints,
50
+ syncCheckConstraints,
51
+ // Index synchronization
52
+ syncIndexes,
53
+ } from './schema-migration'
@@ -0,0 +1,20 @@
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
+ * Re-export domain error types for backward compatibility
10
+ *
11
+ * Error classes are now defined in @/domain/errors (the domain layer)
12
+ * where they belong. This file re-exports them so existing infrastructure
13
+ * imports continue to work without changes.
14
+ */
15
+ export {
16
+ SessionContextError,
17
+ ForbiddenError,
18
+ UniqueConstraintViolationError,
19
+ ValidationError,
20
+ } from '@/domain/errors'