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,82 @@
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
+
10
+ /**
11
+ * Generate automatic id column definition based on primary key type
12
+ * @param primaryKeyType - Type of primary key (uuid, bigserial, or default serial)
13
+ * @param isPrimaryKey - Whether this id column is the primary key (adds PRIMARY KEY constraint inline)
14
+ */
15
+ export const generateIdColumn = (
16
+ primaryKeyType: string | undefined,
17
+ isPrimaryKey: boolean
18
+ ): string => {
19
+ const pkConstraint = isPrimaryKey ? ' PRIMARY KEY' : ''
20
+ if (primaryKeyType === 'uuid') {
21
+ return `id UUID NOT NULL DEFAULT gen_random_uuid()${pkConstraint}`
22
+ }
23
+ if (primaryKeyType === 'bigserial') {
24
+ return `id BIGSERIAL NOT NULL${pkConstraint}`
25
+ }
26
+ return `id SERIAL NOT NULL${pkConstraint}`
27
+ }
28
+
29
+ /**
30
+ * Determine if table needs an automatic id column
31
+ * Creates automatic id column if:
32
+ * - No explicit id field is defined in the fields array
33
+ * - Either: no primary key is defined OR primary key references the special 'id' field
34
+ */
35
+ export const needsAutomaticIdColumn = (
36
+ table: Table,
37
+ primaryKeyFields: readonly string[]
38
+ ): boolean => {
39
+ const hasIdField = table.fields.some((field) => field.name === 'id')
40
+ const primaryKeyReferencesId = primaryKeyFields.includes('id')
41
+ const hasNonIdPrimaryKey = primaryKeyFields.length > 0 && !primaryKeyReferencesId
42
+ return !hasIdField && !hasNonIdPrimaryKey
43
+ }
44
+
45
+ /**
46
+ * Generate created_at column definition if not explicitly defined
47
+ * Note: Includes DEFAULT CURRENT_TIMESTAMP to support INSERT ... DEFAULT VALUES
48
+ * Triggers also set the value to ensure consistency
49
+ */
50
+ export const generateCreatedAtColumn = (table: Table): readonly string[] => {
51
+ const hasCreatedAtField = table.fields.some((field) => field.name === 'created_at')
52
+ return !hasCreatedAtField ? ['created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP'] : []
53
+ }
54
+
55
+ /**
56
+ * Generate updated_at column definition if not explicitly defined
57
+ * Note: Includes DEFAULT CURRENT_TIMESTAMP to support INSERT ... DEFAULT VALUES
58
+ * Triggers update the value on INSERT and UPDATE to ensure currency
59
+ */
60
+ export const generateUpdatedAtColumn = (table: Table): readonly string[] => {
61
+ const hasUpdatedAtField = table.fields.some((field) => field.name === 'updated_at')
62
+ return !hasUpdatedAtField ? ['updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP'] : []
63
+ }
64
+
65
+ /**
66
+ * Generate deleted_at column definition if not explicitly defined
67
+ */
68
+ export const generateDeletedAtColumn = (table: Table): readonly string[] => {
69
+ const hasDeletedAtField = table.fields.some((field) => field.name === 'deleted_at')
70
+ return !hasDeletedAtField ? ['deleted_at TIMESTAMPTZ'] : []
71
+ }
72
+
73
+ /**
74
+ * Generate primary key constraint if needed
75
+ */
76
+ export const generatePrimaryKeyConstraintIfNeeded = (
77
+ table: Table,
78
+ primaryKeyFields: readonly string[]
79
+ ): readonly string[] =>
80
+ needsAutomaticIdColumn(table, primaryKeyFields) && primaryKeyFields.length === 0
81
+ ? ['PRIMARY KEY (id)']
82
+ : []
@@ -0,0 +1,81 @@
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 { shouldCreateDatabaseColumn, sanitizeTableName } from '../field-utils'
9
+ import { shouldUseView, getBaseTableName } from '../lookup/lookup-view-generators'
10
+ import { generateColumnDefinition, generateTableConstraints } from '../sql/sql-generators'
11
+ import {
12
+ generateIdColumn,
13
+ needsAutomaticIdColumn,
14
+ generateCreatedAtColumn,
15
+ generateUpdatedAtColumn,
16
+ generateDeletedAtColumn,
17
+ generatePrimaryKeyConstraintIfNeeded,
18
+ } from './column-generators'
19
+ import type { Table } from '@/domain/models/app/table'
20
+
21
+ /**
22
+ * Generate CREATE TABLE statement
23
+ * When table has lookup fields, creates a base table (_base suffix) and will later create a VIEW
24
+ *
25
+ * @param table - Table definition
26
+ * @param tableUsesView - Map of table names to whether they use a VIEW
27
+ * @param skipForeignKeys - Skip foreign key constraints (for circular dependencies)
28
+ */
29
+ export const generateCreateTableSQL = (
30
+ table: Table,
31
+ tableUsesView?: ReadonlyMap<string, boolean>,
32
+ skipForeignKeys?: boolean,
33
+ hasAuthConfig: boolean = true
34
+ ): string => {
35
+ // Sanitize table name for PostgreSQL (lowercase, underscores)
36
+ const sanitized = sanitizeTableName(table.name)
37
+ // Determine table name (add _base suffix if using VIEW for lookup fields)
38
+ const tableName = shouldUseView(table) ? getBaseTableName(sanitized) : sanitized
39
+
40
+ // Identify primary key fields
41
+ const primaryKeyFields =
42
+ table.primaryKey?.type === 'composite' ? (table.primaryKey.fields ?? []) : []
43
+
44
+ // Generate automatic id column based on primary key type
45
+ // Add PRIMARY KEY inline if the primary key is on the 'id' field
46
+ const primaryKeyOnId = primaryKeyFields.length === 1 && primaryKeyFields[0] === 'id'
47
+ const idColumnDefinition = needsAutomaticIdColumn(table, primaryKeyFields)
48
+ ? [generateIdColumn(table.primaryKey?.type, primaryKeyOnId)]
49
+ : []
50
+
51
+ // Filter out UI-only fields (like button), lookup fields, and rollup fields (handled by VIEW)
52
+ // Lookup and rollup fields don't exist as columns in the base table
53
+ const columnDefinitions = table.fields
54
+ .filter(
55
+ (field) =>
56
+ shouldCreateDatabaseColumn(field) && field.type !== 'lookup' && field.type !== 'rollup'
57
+ )
58
+ .map((field) => {
59
+ // Only add inline PRIMARY KEY for single-field composite keys (handled by generateSerialColumn)
60
+ // Multi-field composite keys must have PRIMARY KEY at table level to avoid "multiple primary keys" error
61
+ const isPrimaryKey = primaryKeyFields.includes(field.name) && primaryKeyFields.length === 1
62
+ return generateColumnDefinition(field, isPrimaryKey, table.fields, hasAuthConfig)
63
+ })
64
+
65
+ // Add PRIMARY KEY constraint on id if no custom primary key is defined
66
+ const tableConstraints = generateTableConstraints(table, tableUsesView, skipForeignKeys)
67
+
68
+ const allDefinitions = [
69
+ ...idColumnDefinition,
70
+ ...generateCreatedAtColumn(table),
71
+ ...generateUpdatedAtColumn(table),
72
+ ...generateDeletedAtColumn(table),
73
+ ...columnDefinitions,
74
+ ...tableConstraints,
75
+ ...generatePrimaryKeyConstraintIfNeeded(table, primaryKeyFields),
76
+ ]
77
+
78
+ return `CREATE TABLE IF NOT EXISTS ${tableName} (
79
+ ${allDefinitions.join(',\n ')}
80
+ )`
81
+ }
@@ -0,0 +1,55 @@
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
+ * Table Operations Module
10
+ *
11
+ * This module provides utilities for database table operations:
12
+ * - CREATE TABLE statement generation
13
+ * - Table migration (ALTER TABLE, data preservation)
14
+ * - Table feature application (indexes, triggers, RLS policies)
15
+ * - Lookup VIEW creation
16
+ * - User-defined VIEW creation
17
+ *
18
+ * All functions use Effect for type-safe error handling.
19
+ */
20
+
21
+ // Type compatibility utilities
22
+ export { normalizeType, areTypesCompatible } from './type-compatibility'
23
+
24
+ // Column generators
25
+ export {
26
+ generateIdColumn,
27
+ needsAutomaticIdColumn,
28
+ generateCreatedAtColumn,
29
+ generateUpdatedAtColumn,
30
+ generateDeletedAtColumn,
31
+ generatePrimaryKeyConstraintIfNeeded,
32
+ } from './column-generators'
33
+
34
+ // CREATE TABLE SQL generation
35
+ export { generateCreateTableSQL } from './create-table-sql'
36
+
37
+ // Table features (indexes, triggers, RLS policies)
38
+ export { applyTableFeatures, applyTableFeaturesWithoutIndexes } from './table-features'
39
+
40
+ // Migration utilities
41
+ export {
42
+ getCompatibleColumns,
43
+ copyDataAndResetSequences,
44
+ recreateTableWithDataEffect,
45
+ } from './migration-utils'
46
+
47
+ // Table effect operations
48
+ export type { MigrationConfig } from './table-effects'
49
+ export {
50
+ migrateExistingTableEffect,
51
+ createNewTableEffect,
52
+ createLookupViewsEffect,
53
+ createTableViewsEffect,
54
+ createOrMigrateTableEffect,
55
+ } from './table-effects'
@@ -0,0 +1,157 @@
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 { sanitizeTableName } from '../field-utils'
10
+ import { shouldUseView, getBaseTableName } from '../lookup/lookup-view-generators'
11
+ import { executeSQL, SQLExecutionError, type TransactionLike } from '../sql/sql-execution'
12
+ import { generateCreateTableSQL } from './create-table-sql'
13
+ import { areTypesCompatible } from './type-compatibility'
14
+ import type { Table } from '@/domain/models/app/table'
15
+
16
+ /**
17
+ * Get compatible columns between existing and new table for data migration
18
+ */
19
+ export const getCompatibleColumns = (
20
+ existingColumns: ReadonlyMap<
21
+ string,
22
+ { dataType: string; isNullable: string; columnDefault: string | null }
23
+ >,
24
+ newColumnInfo: ReadonlyMap<string, { columnDefault: string | null; dataType: string }>
25
+ ): readonly string[] =>
26
+ Array.from(existingColumns.keys()).filter((col) => {
27
+ const newColInfo = newColumnInfo.get(col)
28
+ if (!newColInfo) return false
29
+ const oldType = existingColumns.get(col)?.dataType.toLowerCase() ?? ''
30
+ return areTypesCompatible(oldType, newColInfo.dataType.toLowerCase())
31
+ })
32
+
33
+ interface CopyDataParams {
34
+ readonly tx: TransactionLike
35
+ readonly tempTableName: string
36
+ readonly physicalTableName: string
37
+ readonly commonColumns: readonly string[]
38
+ readonly newColumnInfo: ReadonlyMap<string, { columnDefault: string | null; dataType: string }>
39
+ }
40
+
41
+ /**
42
+ * Copy data and reset SERIAL sequences for migration
43
+ */
44
+ export const copyDataAndResetSequences = (
45
+ params: CopyDataParams
46
+ ): Effect.Effect<void, SQLExecutionError> =>
47
+ Effect.gen(function* () {
48
+ const { tx, tempTableName, physicalTableName, commonColumns, newColumnInfo } = params
49
+ if (commonColumns.length === 0) return
50
+
51
+ const columnList = commonColumns.join(', ')
52
+ yield* executeSQL(
53
+ tx,
54
+ `INSERT INTO ${tempTableName} (${columnList}) SELECT ${columnList} FROM ${physicalTableName}`
55
+ )
56
+
57
+ // Reset SERIAL sequences to max value + 1 to avoid conflicts
58
+ const serialColumns = commonColumns.filter((col) =>
59
+ newColumnInfo.get(col)?.columnDefault?.includes('nextval')
60
+ )
61
+ yield* Effect.forEach(serialColumns, (col) =>
62
+ executeSQL(
63
+ tx,
64
+ `SELECT setval(pg_get_serial_sequence('${tempTableName}', '${col}'), COALESCE((SELECT MAX(${col}) FROM ${tempTableName}), 1), true)`
65
+ )
66
+ )
67
+ })
68
+
69
+ /** Get column metadata from a table */
70
+ const fetchColumnInfo = (
71
+ tx: TransactionLike,
72
+ tableName: string
73
+ ): Effect.Effect<
74
+ Map<string, { columnDefault: string | null; dataType: string }>,
75
+ SQLExecutionError
76
+ > =>
77
+ Effect.gen(function* () {
78
+ const columns = yield* executeSQL(
79
+ tx,
80
+ `SELECT column_name, column_default, data_type FROM information_schema.columns WHERE table_name = '${tableName}'`
81
+ )
82
+ return new Map(
83
+ (
84
+ columns as readonly {
85
+ column_name: string
86
+ column_default: string | null
87
+ data_type: string
88
+ }[]
89
+ ).map((row) => [
90
+ row.column_name,
91
+ { columnDefault: row.column_default, dataType: row.data_type },
92
+ ])
93
+ )
94
+ })
95
+
96
+ /** Finalize table recreation by dropping old table and renaming temp table */
97
+ const finalizeTableRecreation = (
98
+ tx: TransactionLike,
99
+ physicalTableName: string,
100
+ tempTableName: string
101
+ ): Effect.Effect<void, SQLExecutionError> =>
102
+ Effect.gen(function* () {
103
+ yield* executeSQL(tx, `DROP TABLE ${physicalTableName} CASCADE`)
104
+ yield* executeSQL(tx, `ALTER TABLE ${tempTableName} RENAME TO ${physicalTableName}`)
105
+ yield* executeSQL(
106
+ tx,
107
+ `DO $$ BEGIN ALTER TABLE ${physicalTableName} RENAME CONSTRAINT ${tempTableName}_pkey TO ${physicalTableName}_pkey; EXCEPTION WHEN undefined_object THEN NULL; END $$`
108
+ )
109
+ })
110
+
111
+ /**
112
+ * Recreate table with data preservation when schema changes are incompatible with ALTER TABLE
113
+ * Used when primary key type changes or other incompatible schema modifications occur
114
+ */
115
+ export const recreateTableWithDataEffect = (
116
+ tx: TransactionLike,
117
+ table: Table,
118
+ existingColumns: ReadonlyMap<
119
+ string,
120
+ { dataType: string; isNullable: string; columnDefault: string | null }
121
+ >,
122
+ tableUsesView?: ReadonlyMap<string, boolean>
123
+ ): Effect.Effect<void, SQLExecutionError> =>
124
+ Effect.gen(function* () {
125
+ const sanitized = sanitizeTableName(table.name)
126
+ const physicalTableName = shouldUseView(table) ? getBaseTableName(sanitized) : sanitized
127
+ const tempTableName = `${physicalTableName}_migration_temp`
128
+
129
+ // Create temporary table with new schema
130
+ const createTableSQL = yield* Effect.try({
131
+ try: () =>
132
+ generateCreateTableSQL(table, tableUsesView).replace(
133
+ `CREATE TABLE IF NOT EXISTS ${physicalTableName}`,
134
+ `CREATE TABLE ${tempTableName}`
135
+ ),
136
+ catch: (error) =>
137
+ new SQLExecutionError({
138
+ message: `Failed to generate CREATE TABLE DDL for migration: ${String(error)}`,
139
+ cause: error,
140
+ }),
141
+ })
142
+ yield* executeSQL(tx, createTableSQL)
143
+
144
+ // Get new table column info and copy compatible data
145
+ const newColumnInfo = yield* fetchColumnInfo(tx, tempTableName)
146
+ const commonColumns = getCompatibleColumns(existingColumns, newColumnInfo)
147
+ yield* copyDataAndResetSequences({
148
+ tx,
149
+ tempTableName,
150
+ physicalTableName,
151
+ commonColumns,
152
+ newColumnInfo,
153
+ })
154
+
155
+ // Finalize by dropping old table and renaming temp table
156
+ yield* finalizeTableRecreation(tx, physicalTableName, tempTableName)
157
+ })
@@ -0,0 +1,234 @@
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
+ shouldUseView,
11
+ generateLookupViewSQL,
12
+ generateLookupViewTriggers,
13
+ } from '../lookup/lookup-view-generators'
14
+ import {
15
+ generateAlterTableStatements,
16
+ syncUniqueConstraints,
17
+ syncForeignKeyConstraints,
18
+ syncCheckConstraints,
19
+ syncIndexes,
20
+ type BunSQLTransaction,
21
+ } from '../schema-migration-helpers'
22
+ import {
23
+ executeSQL,
24
+ executeSQLStatements,
25
+ getExistingColumns,
26
+ SQLExecutionError,
27
+ type TransactionLike,
28
+ } from '../sql/sql-execution'
29
+ import { generateTableViewStatements, generateReadOnlyViewTrigger } from '../views/view-generators'
30
+ import { generateCreateTableSQL } from './create-table-sql'
31
+ import { recreateTableWithDataEffect } from './migration-utils'
32
+ import { applyTableFeatures, applyTableFeaturesWithoutIndexes } from './table-features'
33
+ import type { Table } from '@/domain/models/app/table'
34
+
35
+ /**
36
+ * Configuration for table migration operations
37
+ */
38
+ export type MigrationConfig = {
39
+ readonly tableUsesView?: ReadonlyMap<string, boolean>
40
+ readonly previousSchema?: { readonly tables: readonly object[] }
41
+ }
42
+
43
+ /**
44
+ * Migrate existing table (ALTER statements + constraints + indexes)
45
+ */
46
+ export const migrateExistingTableEffect = (params: {
47
+ readonly tx: TransactionLike
48
+ readonly table: Table
49
+ readonly existingColumns: ReadonlyMap<
50
+ string,
51
+ { dataType: string; isNullable: string; columnDefault: string | null }
52
+ >
53
+ readonly tableUsesView?: ReadonlyMap<string, boolean>
54
+ readonly previousSchema?: { readonly tables: readonly object[] }
55
+ }): Effect.Effect<void, SQLExecutionError> =>
56
+ Effect.gen(function* () {
57
+ const { tx, table, existingColumns, tableUsesView, previousSchema } = params
58
+ const alterStatements = generateAlterTableStatements(table, existingColumns, previousSchema)
59
+
60
+ // If alterStatements is empty, table has incompatible schema changes
61
+ // (e.g., primary key type change) - need to recreate with data preservation
62
+ if (alterStatements.length === 0) {
63
+ yield* recreateTableWithDataEffect(tx, table, existingColumns, tableUsesView)
64
+ } else {
65
+ // Apply incremental migrations
66
+ yield* executeSQLStatements(tx, alterStatements)
67
+ }
68
+
69
+ // Always add/update unique constraints for existing tables
70
+ yield* syncUniqueConstraints(tx, table, previousSchema)
71
+
72
+ // Always sync foreign key constraints to ensure referential actions are up-to-date
73
+ yield* syncForeignKeyConstraints(tx, table, tableUsesView)
74
+
75
+ // Always sync CHECK constraints for fields with validation requirements
76
+ yield* syncCheckConstraints(tx, table)
77
+
78
+ // Always sync indexes when field indexed property changes or custom indexes are modified
79
+ yield* syncIndexes(tx, table, previousSchema)
80
+
81
+ // Apply table features (triggers, RLS) - indexes handled by syncIndexes above
82
+ yield* applyTableFeaturesWithoutIndexes(tx, table)
83
+ })
84
+
85
+ /**
86
+ * Create new table (CREATE statement + indexes + triggers)
87
+ * Note: VIEWs are created in a separate phase after all base tables exist
88
+ *
89
+ * @param tx - Transaction object
90
+ * @param table - Table definition
91
+ * @param tableUsesView - Map of table names to whether they use a VIEW
92
+ * @param skipForeignKeys - Skip foreign key constraints (for circular dependencies)
93
+ */
94
+ export const createNewTableEffect = (params: {
95
+ readonly tx: TransactionLike
96
+ readonly table: Table
97
+ readonly tableUsesView?: ReadonlyMap<string, boolean>
98
+ readonly skipForeignKeys?: boolean
99
+ readonly hasAuthConfig?: boolean
100
+ }): Effect.Effect<void, SQLExecutionError> =>
101
+ Effect.gen(function* () {
102
+ const { tx, table, tableUsesView, skipForeignKeys, hasAuthConfig = true } = params
103
+ // Wrap DDL generation in Effect.try to catch synchronous errors (e.g., unknown field types)
104
+ const createTableSQL = yield* Effect.try({
105
+ try: () => generateCreateTableSQL(table, tableUsesView, skipForeignKeys, hasAuthConfig),
106
+ catch: (error) =>
107
+ new SQLExecutionError({
108
+ message: `Failed to generate CREATE TABLE DDL: ${String(error)}`,
109
+ cause: error,
110
+ }),
111
+ })
112
+ yield* executeSQL(tx, createTableSQL)
113
+
114
+ // Apply all table features (indexes, triggers, RLS)
115
+ yield* applyTableFeatures(tx, table)
116
+ })
117
+
118
+ /**
119
+ * Create lookup VIEWs for tables with lookup fields
120
+ * Called after all base tables have been created to avoid dependency issues
121
+ */
122
+ export const createLookupViewsEffect = (
123
+ tx: TransactionLike,
124
+ table: Table
125
+ ): Effect.Effect<void, SQLExecutionError> =>
126
+ Effect.gen(function* () {
127
+ if (shouldUseView(table)) {
128
+ const createViewSQL = generateLookupViewSQL(table)
129
+ if (createViewSQL) {
130
+ // Drop existing table if it exists (to allow VIEW creation)
131
+ // This handles the transition from TABLE to VIEW when rollup/lookup fields are added
132
+ yield* executeSQL(tx, `DROP TABLE IF EXISTS ${table.name} CASCADE`)
133
+
134
+ yield* executeSQL(tx, createViewSQL)
135
+
136
+ // Create INSTEAD OF triggers to make the VIEW writable
137
+ const triggerStatements = generateLookupViewTriggers(table)
138
+ yield* executeSQLStatements(tx, triggerStatements)
139
+ }
140
+ }
141
+ })
142
+
143
+ /**
144
+ * Create table views (user-defined VIEWs from table.views configuration)
145
+ * Called after all tables and lookup views have been created
146
+ */
147
+ export const createTableViewsEffect = (
148
+ tx: TransactionLike,
149
+ table: Table
150
+ ): Effect.Effect<void, SQLExecutionError> =>
151
+ Effect.gen(function* () {
152
+ // Views are dropped globally in schema-initializer.ts before this function is called
153
+ // This ensures all obsolete views are removed before creating new ones
154
+
155
+ // Only create views if table has views defined
156
+ if (!table.views || table.views.length === 0) {
157
+ return
158
+ }
159
+
160
+ // Drop and recreate each view (PostgreSQL doesn't support IF NOT EXISTS for views)
161
+ const viewSQL = generateTableViewStatements(table)
162
+
163
+ // Process each view sequentially (views may depend on each other)
164
+ /* eslint-disable functional/no-loop-statements */
165
+ for (const view of table.views) {
166
+ // Convert view.id to string (ViewId can be number or string)
167
+ const viewIdStr = String(view.id)
168
+
169
+ // Drop existing view or materialized view (if any)
170
+ // Try both types since we don't know what exists in the database
171
+ if (view.materialized) {
172
+ yield* executeSQL(tx, `DROP MATERIALIZED VIEW IF EXISTS ${viewIdStr} CASCADE`)
173
+ } else {
174
+ yield* executeSQL(tx, `DROP VIEW IF EXISTS ${viewIdStr} CASCADE`)
175
+ }
176
+
177
+ // Create view (regular or materialized)
178
+ const createSQL = viewSQL.find((sql) => sql.includes(viewIdStr))
179
+ if (createSQL) {
180
+ yield* executeSQL(tx, createSQL)
181
+
182
+ // For regular (non-materialized) views, add read-only triggers
183
+ // PostgreSQL views can be automatically updatable, so we need triggers to prevent modifications
184
+ if (!view.materialized) {
185
+ const readOnlyTriggerSQL = generateReadOnlyViewTrigger(view.id)
186
+ /* eslint-disable functional/no-loop-statements */
187
+ for (const triggerSQL of readOnlyTriggerSQL) {
188
+ yield* executeSQL(tx, triggerSQL)
189
+ }
190
+ /* eslint-enable functional/no-loop-statements */
191
+ }
192
+
193
+ // Refresh materialized view if requested
194
+ if (view.materialized && view.refreshOnMigration) {
195
+ yield* executeSQL(tx, `REFRESH MATERIALIZED VIEW ${viewIdStr}`)
196
+ }
197
+ }
198
+ }
199
+ })
200
+
201
+ /**
202
+ * Create or migrate table based on existence
203
+ */
204
+ export const createOrMigrateTableEffect = (params: {
205
+ readonly tx: BunSQLTransaction
206
+ readonly table: Table
207
+ readonly exists: boolean
208
+ readonly tableUsesView?: ReadonlyMap<string, boolean>
209
+ readonly previousSchema?: { readonly tables: readonly object[] }
210
+ readonly skipForeignKeys?: boolean
211
+ readonly hasAuthConfig?: boolean
212
+ }): Effect.Effect<void, SQLExecutionError> =>
213
+ Effect.gen(function* () {
214
+ const { tx, table, exists, tableUsesView, previousSchema, skipForeignKeys, hasAuthConfig } =
215
+ params
216
+ if (exists) {
217
+ const existingColumns = yield* getExistingColumns(tx, table.name)
218
+ yield* migrateExistingTableEffect({
219
+ tx,
220
+ table,
221
+ existingColumns,
222
+ tableUsesView,
223
+ previousSchema,
224
+ })
225
+ } else {
226
+ yield* createNewTableEffect({
227
+ tx,
228
+ table,
229
+ tableUsesView,
230
+ skipForeignKeys,
231
+ hasAuthConfig: hasAuthConfig ?? true,
232
+ })
233
+ }
234
+ })