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,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 type { Fields } from '@/domain/models/app/table/fields'
9
+
10
+ /**
11
+ * Check if field is a user reference field (created-by, updated-by, deleted-by)
12
+ * Used to determine if Better Auth users table is required
13
+ * Exported for use in schema-initializer
14
+ */
15
+ export const isUserReferenceField = (field: Fields[number]): boolean =>
16
+ field.type === 'created-by' || field.type === 'updated-by' || field.type === 'deleted-by'
17
+
18
+ /**
19
+ * Check if field is an auto-populated user reference field (created-by only)
20
+ * created-by is always NOT NULL because it's auto-populated on creation
21
+ * Note: updated-by is NOT included because it's only set during update (nullable until first update)
22
+ * Note: deleted-by is NOT included because it's only set during soft-delete (nullable)
23
+ */
24
+ export const isAutoPopulatedUserField = (field: Fields[number]): boolean =>
25
+ field.type === 'created-by'
26
+
27
+ /**
28
+ * Check if field is a user field (type: 'user')
29
+ * Used to generate FOREIGN KEY constraints to users table
30
+ * Exported for use in schema-initializer
31
+ */
32
+ export const isUserField = (field: Fields[number]): boolean => field.type === 'user'
33
+
34
+ /**
35
+ * Check if field is a relationship field (type: 'relationship')
36
+ * Used to generate FOREIGN KEY constraints to related tables
37
+ */
38
+ export const isRelationshipField = (
39
+ field: Fields[number]
40
+ ): field is Fields[number] & { type: 'relationship'; relatedTable: string } =>
41
+ field.type === 'relationship' && 'relatedTable' in field && typeof field.relatedTable === 'string'
42
+
43
+ /**
44
+ * Check if field is an auto-timestamp field (created-at, updated-at)
45
+ */
46
+ export const isAutoTimestampField = (field: Fields[number]): boolean =>
47
+ field.type === 'created-at' || field.type === 'updated-at'
48
+
49
+ /**
50
+ * Check if field should use SERIAL type
51
+ */
52
+ export const shouldUseSerial = (field: Fields[number], isPrimaryKey: boolean): boolean =>
53
+ field.type === 'autonumber' || (field.type === 'integer' && isPrimaryKey)
54
+
55
+ /**
56
+ * Check if field should be NOT NULL
57
+ * Auto-managed fields (created-at, updated-at, created-by) and required fields are NOT NULL
58
+ * Note: When hasAuthConfig is false, created-by becomes NULLABLE (NULL when no auth)
59
+ * Note: updated-by is always nullable because it's only set during update (NULL until first update)
60
+ * Note: deleted-by is always nullable because it's only set during soft-delete
61
+ * Exported for use in schema-migration-helpers for nullability change detection
62
+ *
63
+ * @param hasAuthConfig - Whether auth is configured (default true). When false, auto-populated
64
+ * user fields (created-by) become nullable to support apps without authentication.
65
+ */
66
+ export const isFieldNotNull = (
67
+ field: Fields[number],
68
+ isPrimaryKey: boolean,
69
+ hasAuthConfig: boolean = true
70
+ ): boolean => {
71
+ // Auto-managed timestamp fields are always NOT NULL (created-at, updated-at)
72
+ if (isAutoTimestampField(field)) return true
73
+ // Auto-populated user fields (created-by) are NOT NULL only when auth is configured
74
+ // updated-by is excluded because it's only set during update (nullable until first update)
75
+ // deleted-by is excluded because it's only populated during soft-delete
76
+ if (isAutoPopulatedUserField(field)) return hasAuthConfig
77
+ // Primary key fields are always NOT NULL
78
+ if (isPrimaryKey) return true
79
+ // Check required property
80
+ return 'required' in field && field.required === true
81
+ }
@@ -0,0 +1,91 @@
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
+ * SQL Generators - PostgreSQL DDL Generation for Sovrium Tables
10
+ *
11
+ * This module coordinates SQL generation for table creation, constraints, and relationships.
12
+ * Implementation is split across focused modules:
13
+ * - sql-type-mappings.ts: Field type → PostgreSQL type conversions
14
+ * - sql-field-predicates.ts: Field type checking functions
15
+ * - sql-column-generators.ts: Column definition generation
16
+ * - sql-check-constraints.ts: CHECK constraint generation
17
+ * - sql-key-constraints.ts: UNIQUE, FOREIGN KEY, PRIMARY KEY constraints
18
+ * - sql-junction-tables.ts: Many-to-many relationship tables
19
+ */
20
+
21
+ import {
22
+ generateArrayConstraints,
23
+ generateBarcodeConstraints,
24
+ generateColorConstraints,
25
+ generateCustomCheckConstraints,
26
+ generateEnumConstraints,
27
+ generateMultiSelectConstraints,
28
+ generateMultipleAttachmentsConstraints,
29
+ generateNumericConstraints,
30
+ generateProgressConstraints,
31
+ generateRichTextConstraints,
32
+ generateStatusConstraints,
33
+ } from './sql-check-constraints'
34
+ import {
35
+ generateCompositeUniqueConstraints,
36
+ generateForeignKeyConstraints,
37
+ generatePrimaryKeyConstraint,
38
+ generateUniqueConstraints,
39
+ } from './sql-key-constraints'
40
+ import type { Table } from '@/domain/models/app/table'
41
+
42
+ // Re-export from sql-type-mappings
43
+ export { mapFieldTypeToPostgres } from './sql-type-mappings'
44
+
45
+ // Re-export from sql-field-predicates
46
+ export {
47
+ isFieldNotNull,
48
+ isRelationshipField,
49
+ isUserField,
50
+ isUserReferenceField,
51
+ } from './sql-field-predicates'
52
+
53
+ // Re-export from sql-column-generators
54
+ export { generateColumnDefinition } from './sql-column-generators'
55
+
56
+ // Re-export from sql-key-constraints
57
+ export { generateForeignKeyConstraints, generateUniqueConstraints } from './sql-key-constraints'
58
+
59
+ // Re-export from sql-junction-tables
60
+ export {
61
+ generateJunctionTableDDL,
62
+ generateJunctionTableName,
63
+ toSingular,
64
+ } from './sql-junction-tables'
65
+
66
+ /**
67
+ * Generate table constraints (CHECK constraints, UNIQUE constraints, FOREIGN KEY, primary key, etc.)
68
+ */
69
+ export const generateTableConstraints = (
70
+ table: Table,
71
+ tableUsesView?: ReadonlyMap<string, boolean>,
72
+ skipForeignKeys?: boolean
73
+ ): readonly string[] => [
74
+ ...generateArrayConstraints(table.fields),
75
+ ...generateMultipleAttachmentsConstraints(table.fields),
76
+ ...generateNumericConstraints(table.fields),
77
+ ...generateProgressConstraints(table.fields),
78
+ ...generateEnumConstraints(table.fields),
79
+ ...generateMultiSelectConstraints(table.fields),
80
+ ...generateStatusConstraints(table.fields),
81
+ ...generateRichTextConstraints(table.fields),
82
+ ...generateBarcodeConstraints(table.fields),
83
+ ...generateColorConstraints(table.fields),
84
+ ...generateCustomCheckConstraints(table.constraints),
85
+ ...generatePrimaryKeyConstraint(table),
86
+ ...generateUniqueConstraints(table.name, table.fields),
87
+ ...generateCompositeUniqueConstraints(table),
88
+ ...(skipForeignKeys
89
+ ? []
90
+ : generateForeignKeyConstraints(table.name, table.fields, tableUsesView, table.foreignKeys)),
91
+ ]
@@ -0,0 +1,79 @@
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
+ * Generate junction table name for many-to-many relationship
10
+ * Format: {table1}_{table2} (source table first, related table second)
11
+ */
12
+ export const generateJunctionTableName = (sourceTable: string, relatedTable: string): string =>
13
+ `${sourceTable}_${relatedTable}`
14
+
15
+ /**
16
+ * Common irregular plural to singular mappings for table naming
17
+ * Used by toSingular() to handle irregular English plurals
18
+ */
19
+ const IRREGULAR_PLURALS: Readonly<Record<string, string>> = {
20
+ people: 'person',
21
+ children: 'child',
22
+ men: 'man',
23
+ women: 'woman',
24
+ teeth: 'tooth',
25
+ feet: 'foot',
26
+ geese: 'goose',
27
+ mice: 'mouse',
28
+ dice: 'die',
29
+ oxen: 'ox',
30
+ indices: 'index',
31
+ matrices: 'matrix',
32
+ vertices: 'vertex',
33
+ analyses: 'analysis',
34
+ criteria: 'criterion',
35
+ phenomena: 'phenomenon',
36
+ data: 'datum',
37
+ media: 'medium',
38
+ }
39
+
40
+ /**
41
+ * Convert table name to singular form for junction table column naming
42
+ * Uses irregular plural mapping with fallback to 's' removal heuristic
43
+ */
44
+ export const toSingular = (tableName: string): string =>
45
+ IRREGULAR_PLURALS[tableName] ?? (tableName.endsWith('s') ? tableName.slice(0, -1) : tableName)
46
+
47
+ /**
48
+ * Generate CREATE TABLE statement for junction table (many-to-many relationship)
49
+ *
50
+ * Junction tables have:
51
+ * - Two foreign key columns: {sourceTable}_id, {relatedTable}_id (singular form)
52
+ * - Composite primary key on both columns
53
+ * - Foreign key constraints to both tables
54
+ */
55
+ export const generateJunctionTableDDL = (
56
+ sourceTable: string,
57
+ relatedTable: string,
58
+ tableUsesView?: ReadonlyMap<string, boolean>
59
+ ): string => {
60
+ const junctionTableName = generateJunctionTableName(sourceTable, relatedTable)
61
+ const sourceColumnName = `${toSingular(sourceTable)}_id`
62
+ const relatedColumnName = `${toSingular(relatedTable)}_id`
63
+
64
+ // Determine actual table names (base tables if using views)
65
+ const sourceTableName =
66
+ tableUsesView?.get(sourceTable) === true ? `${sourceTable}_base` : sourceTable
67
+ const relatedTableName =
68
+ tableUsesView?.get(relatedTable) === true ? `${relatedTable}_base` : relatedTable
69
+
70
+ const columns = [
71
+ `${sourceColumnName} INTEGER NOT NULL`,
72
+ `${relatedColumnName} INTEGER NOT NULL`,
73
+ `PRIMARY KEY (${sourceColumnName}, ${relatedColumnName})`,
74
+ `CONSTRAINT ${junctionTableName}_${sourceColumnName}_fkey FOREIGN KEY (${sourceColumnName}) REFERENCES ${sourceTableName}(id)`,
75
+ `CONSTRAINT ${junctionTableName}_${relatedColumnName}_fkey FOREIGN KEY (${relatedColumnName}) REFERENCES ${relatedTableName}(id)`,
76
+ ]
77
+
78
+ return `CREATE TABLE IF NOT EXISTS ${junctionTableName} (\n ${columns.join(',\n ')}\n)`
79
+ }
@@ -0,0 +1,210 @@
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 { isRelationshipField, isUserField, shouldUseSerial } from './sql-field-predicates'
9
+ import type { Table } from '@/domain/models/app/table'
10
+ import type { Fields } from '@/domain/models/app/table/fields'
11
+
12
+ /**
13
+ * Generate UNIQUE constraints for fields with unique property
14
+ * Uses PostgreSQL default naming convention: {table}_{column}_key
15
+ *
16
+ * NOTE: Geolocation fields are excluded because POINT type requires GiST index for UNIQUE,
17
+ * not btree. UNIQUE GiST indexes for geolocation fields are created separately in
18
+ * generateIndexStatements() in schema-initializer.ts
19
+ */
20
+ export const generateUniqueConstraints = (
21
+ tableName: string,
22
+ fields: readonly Fields[number][]
23
+ ): readonly string[] =>
24
+ fields
25
+ .filter(
26
+ (field): field is Fields[number] & { unique: true } =>
27
+ 'unique' in field && !!field.unique && field.type !== 'geolocation'
28
+ )
29
+ .map((field) => `CONSTRAINT ${tableName}_${field.name}_key UNIQUE (${field.name})`)
30
+
31
+ /**
32
+ * Map onDelete/onUpdate values to PostgreSQL referential actions
33
+ *
34
+ * @param action - The referential action (cascade, set-null, restrict, etc.)
35
+ * @param clauseType - The type of clause (delete or update)
36
+ * @returns PostgreSQL referential action clause (e.g., " ON DELETE CASCADE")
37
+ */
38
+ const mapReferentialAction = (
39
+ action: string | undefined,
40
+ clauseType: 'delete' | 'update'
41
+ ): string => {
42
+ if (!action) return ''
43
+ const upperAction = action.toUpperCase()
44
+ const validActions = ['CASCADE', 'SET NULL', 'SET DEFAULT', 'RESTRICT', 'NO ACTION']
45
+
46
+ // Map 'set-null' to 'SET NULL' for PostgreSQL compatibility
47
+ const normalizedAction = upperAction === 'SET-NULL' ? 'SET NULL' : upperAction
48
+
49
+ // Find matching PostgreSQL action
50
+ const postgresAction = validActions.find(
51
+ (valid) => valid.replace(' ', '-') === normalizedAction || valid === normalizedAction
52
+ )
53
+
54
+ if (!postgresAction) return ''
55
+
56
+ const clausePrefix = clauseType === 'delete' ? 'ON DELETE' : 'ON UPDATE'
57
+ return ` ${clausePrefix} ${postgresAction}`
58
+ }
59
+
60
+ /**
61
+ * Generate composite foreign key constraints from table.foreignKeys array
62
+ */
63
+ const generateCompositeForeignKeyConstraints = (
64
+ compositeForeignKeys: readonly {
65
+ readonly name: string
66
+ readonly fields: readonly string[]
67
+ readonly referencedTable: string
68
+ readonly referencedFields: readonly string[]
69
+ readonly onDelete?: string
70
+ readonly onUpdate?: string
71
+ }[]
72
+ ): readonly string[] =>
73
+ compositeForeignKeys.map((fk) => {
74
+ const localFields = fk.fields.join(', ')
75
+ const referencedFields = fk.referencedFields.join(', ')
76
+ const onDeleteClause = mapReferentialAction(fk.onDelete, 'delete')
77
+ const onUpdateClause = mapReferentialAction(fk.onUpdate, 'update')
78
+
79
+ return `CONSTRAINT ${fk.name} FOREIGN KEY (${localFields}) REFERENCES ${fk.referencedTable}(${referencedFields})${onDeleteClause}${onUpdateClause}`
80
+ })
81
+
82
+ /**
83
+ * Generate FOREIGN KEY constraint for relationship field
84
+ */
85
+ const generateRelationshipConstraint = (
86
+ tableName: string,
87
+ field: Fields[number] & { readonly type: 'relationship'; readonly relatedTable: string },
88
+ tableUsesView?: ReadonlyMap<string, boolean>
89
+ ): string => {
90
+ const constraintName = `${tableName}_${field.name}_fkey`
91
+ // If the related table uses a VIEW (has lookup fields), reference the base table instead
92
+ const relatedTableName =
93
+ tableUsesView?.get(field.relatedTable) === true
94
+ ? `${field.relatedTable}_base`
95
+ : field.relatedTable
96
+
97
+ // Build referential actions (ON DELETE, ON UPDATE)
98
+ const onDeleteClause =
99
+ 'onDelete' in field ? mapReferentialAction(field.onDelete as string | undefined, 'delete') : ''
100
+ const onUpdateClause =
101
+ 'onUpdate' in field ? mapReferentialAction(field.onUpdate as string | undefined, 'update') : ''
102
+
103
+ // Use relatedField if specified, otherwise default to 'id'
104
+ const referencedColumn = 'relatedField' in field && field.relatedField ? field.relatedField : 'id'
105
+
106
+ return `CONSTRAINT ${constraintName} FOREIGN KEY (${field.name}) REFERENCES ${relatedTableName}(${referencedColumn})${onDeleteClause}${onUpdateClause}`
107
+ }
108
+
109
+ /**
110
+ * Generate FOREIGN KEY constraints for user fields, relationship fields, and composite foreign keys
111
+ * Exported for use in migration system to sync FK constraints
112
+ */
113
+ export const generateForeignKeyConstraints = (
114
+ tableName: string,
115
+ fields: readonly Fields[number][],
116
+ tableUsesView?: ReadonlyMap<string, boolean>,
117
+ compositeForeignKeys?: readonly {
118
+ readonly name: string
119
+ readonly fields: readonly string[]
120
+ readonly referencedTable: string
121
+ readonly referencedFields: readonly string[]
122
+ readonly onDelete?: string
123
+ readonly onUpdate?: string
124
+ }[]
125
+ ): readonly string[] => {
126
+ // Generate foreign keys for user fields (type: 'user')
127
+ // References auth.user table (Better Auth users in dedicated auth schema)
128
+ const userFieldConstraints = fields.filter(isUserField).map((field) => {
129
+ const constraintName = `${tableName}_${field.name}_fkey`
130
+ return `CONSTRAINT ${constraintName} FOREIGN KEY (${field.name}) REFERENCES auth.user(id)`
131
+ })
132
+
133
+ // Generate foreign keys for relationship fields (type: 'relationship')
134
+ // Exclude one-to-many and many-to-many relationships as they don't create FK constraints on the parent side
135
+ const relationshipFieldConstraints = fields
136
+ .filter(isRelationshipField)
137
+ .filter((field) => {
138
+ // Only create FK for many-to-one relationships or relationships without explicit relationType
139
+ // Exclude one-to-many (FK in related table) and many-to-many (uses junction table)
140
+ return (
141
+ !('relationType' in field) ||
142
+ (field.relationType !== 'one-to-many' && field.relationType !== 'many-to-many')
143
+ )
144
+ })
145
+ .map((field) => generateRelationshipConstraint(tableName, field, tableUsesView))
146
+
147
+ // Foreign keys disabled for created-by/updated-by fields
148
+ // Blocked by: https://github.com/sovrium/sovrium/issues/3980
149
+ // Infrastructure ready - uncomment lines below when issue is resolved
150
+ const userReferenceConstraints: readonly string[] = []
151
+ // const userReferenceConstraints = fields
152
+ // .filter(isUserReferenceField)
153
+ // .map((field) => {
154
+ // const constraintName = `${tableName}_${field.name}_fkey`
155
+ // return `CONSTRAINT ${constraintName} FOREIGN KEY (${field.name}) REFERENCES auth.user(id)`
156
+ // })
157
+
158
+ // Generate composite foreign key constraints
159
+ const compositeFKs = generateCompositeForeignKeyConstraints(compositeForeignKeys ?? [])
160
+
161
+ return [
162
+ ...userFieldConstraints,
163
+ ...relationshipFieldConstraints,
164
+ ...userReferenceConstraints,
165
+ ...compositeFKs,
166
+ ]
167
+ }
168
+
169
+ /**
170
+ * Generate primary key constraint if defined
171
+ * Skips single-field composite keys when the field is SERIAL (PRIMARY KEY is already inline)
172
+ * Note: Special field 'id' is automatically SERIAL, so PRIMARY KEY is inline
173
+ */
174
+ export const generatePrimaryKeyConstraint = (table: Table): readonly string[] => {
175
+ if (table.primaryKey?.type === 'composite' && table.primaryKey.fields) {
176
+ // For single-field composite keys, check if the field is SERIAL (PRIMARY KEY already inline)
177
+ if (table.primaryKey.fields.length === 1) {
178
+ const pkFieldName = table.primaryKey.fields[0]
179
+ const pkField = table.fields.find((f) => f.name === pkFieldName)
180
+
181
+ // Special case: 'id' field is automatically SERIAL, PRIMARY KEY is inline
182
+ if (pkFieldName === 'id' && !pkField) {
183
+ // PRIMARY KEY is already inline in the automatic id column definition
184
+ return []
185
+ }
186
+
187
+ if (pkField && shouldUseSerial(pkField, true)) {
188
+ // PRIMARY KEY is already inline in the SERIAL column definition
189
+ return []
190
+ }
191
+ }
192
+ return [`PRIMARY KEY (${table.primaryKey.fields.join(', ')})`]
193
+ }
194
+ return []
195
+ }
196
+
197
+ /**
198
+ * Generate composite UNIQUE constraints from table.uniqueConstraints
199
+ * These are multi-column unique constraints (e.g., UNIQUE (email, tenant_id))
200
+ */
201
+ export const generateCompositeUniqueConstraints = (table: Table): readonly string[] => {
202
+ if (!table.uniqueConstraints || table.uniqueConstraints.length === 0) {
203
+ return []
204
+ }
205
+
206
+ return table.uniqueConstraints.map((constraint) => {
207
+ const fields = constraint.fields.join(', ')
208
+ return `CONSTRAINT ${constraint.name} UNIQUE (${fields})`
209
+ })
210
+ }
@@ -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 type { Fields } from '@/domain/models/app/table/fields'
9
+
10
+ /**
11
+ * Field type to PostgreSQL type mapping
12
+ * Note: button field type is included for type safety but should not create database columns
13
+ */
14
+ export const fieldTypeToPostgresMap: Record<string, string> = {
15
+ integer: 'INTEGER',
16
+ autonumber: 'INTEGER',
17
+ decimal: 'DECIMAL',
18
+ 'single-line-text': 'VARCHAR(255)',
19
+ 'long-text': 'TEXT',
20
+ email: 'VARCHAR(255)',
21
+ url: 'VARCHAR(255)',
22
+ 'phone-number': 'VARCHAR(255)',
23
+ 'rich-text': 'TEXT',
24
+ checkbox: 'BOOLEAN',
25
+ boolean: 'BOOLEAN', // Alias for checkbox (used in tests)
26
+ date: 'DATE',
27
+ datetime: 'TIMESTAMPTZ',
28
+ time: 'TIME',
29
+ 'single-select': 'VARCHAR(255)',
30
+ status: 'VARCHAR(255)',
31
+ 'multi-select': 'TEXT[]',
32
+ currency: 'DECIMAL',
33
+ percentage: 'DECIMAL',
34
+ rating: 'INTEGER',
35
+ duration: 'INTERVAL',
36
+ color: 'VARCHAR(7)',
37
+ progress: 'INTEGER',
38
+ json: 'JSONB',
39
+ geolocation: 'POINT',
40
+ barcode: 'VARCHAR(255)',
41
+ 'single-attachment': 'VARCHAR(255)',
42
+ 'multiple-attachments': 'JSONB',
43
+ relationship: 'INTEGER',
44
+ lookup: 'TEXT',
45
+ rollup: 'TEXT',
46
+ count: 'INTEGER',
47
+ formula: 'TEXT',
48
+ user: 'TEXT',
49
+ 'created-by': 'TEXT',
50
+ 'updated-by': 'TEXT',
51
+ 'deleted-by': 'TEXT',
52
+ 'created-at': 'TIMESTAMP',
53
+ 'updated-at': 'TIMESTAMP',
54
+ 'deleted-at': 'TIMESTAMP',
55
+ button: 'TEXT',
56
+ }
57
+
58
+ /**
59
+ * Map formula resultType to PostgreSQL type
60
+ */
61
+ const formulaResultTypeMap: Record<string, string> = {
62
+ decimal: 'DECIMAL',
63
+ number: 'DECIMAL',
64
+ numeric: 'DECIMAL',
65
+ integer: 'INTEGER',
66
+ int: 'INTEGER',
67
+ boolean: 'BOOLEAN',
68
+ bool: 'BOOLEAN',
69
+ text: 'TEXT',
70
+ string: 'TEXT',
71
+ 'text[]': 'TEXT[]',
72
+ 'string[]': 'TEXT[]',
73
+ date: 'DATE',
74
+ datetime: 'TIMESTAMPTZ',
75
+ timestamp: 'TIMESTAMPTZ',
76
+ }
77
+
78
+ export const mapFormulaResultTypeToPostgres = (resultType: string | undefined): string => {
79
+ if (!resultType) return 'TEXT'
80
+ return formulaResultTypeMap[resultType.toLowerCase()] ?? 'TEXT'
81
+ }
82
+
83
+ /**
84
+ * Map field type to PostgreSQL column type
85
+ * Throws error if field type is not recognized
86
+ */
87
+ export const mapFieldTypeToPostgres = (field: Fields[number]): string => {
88
+ if (field.type === 'array') {
89
+ const itemType = 'itemType' in field && field.itemType ? field.itemType : 'text'
90
+ return `${itemType.toUpperCase()}[]`
91
+ }
92
+
93
+ // Handle decimal/currency/percentage with precision
94
+ const numericTypesWithPrecision = ['decimal', 'currency', 'percentage']
95
+ if (numericTypesWithPrecision.includes(field.type) && 'precision' in field && field.precision) {
96
+ return `NUMERIC(${field.precision},2)`
97
+ }
98
+
99
+ const postgresType = fieldTypeToPostgresMap[field.type]
100
+ if (!postgresType) {
101
+ // eslint-disable-next-line functional/no-throw-statements -- Error is caught by Effect.try in table-operations.ts
102
+ throw new Error(`Unknown field type: ${field.type}`)
103
+ }
104
+
105
+ return postgresType
106
+ }
@@ -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
+ * Escape single quotes in SQL string literals to prevent SQL injection
10
+ * PostgreSQL escapes single quotes by doubling them: ' becomes ''
11
+ *
12
+ * Used across all SQL generators (view-generators, lookup-view-generators, sql-generators)
13
+ */
14
+ export const escapeSqlString = (value: string): string => value.replace(/'/g, "''")
15
+
16
+ /**
17
+ * Format a value for SQL interpolation with proper escaping
18
+ * Strings are escaped and quoted, numbers/booleans are used directly
19
+ */
20
+ export const formatSqlValue = (value: unknown): string => {
21
+ if (typeof value === 'string') {
22
+ return `'${escapeSqlString(value)}'`
23
+ }
24
+ if (typeof value === 'number' || typeof value === 'boolean') {
25
+ return String(value)
26
+ }
27
+ if (value === null) {
28
+ return 'NULL'
29
+ }
30
+ // For other types (objects, arrays), convert to JSON string
31
+ return `'${escapeSqlString(JSON.stringify(value))}'`
32
+ }
33
+
34
+ /**
35
+ * Generate SQL LIKE pattern with wildcards for pattern matching operators
36
+ * Automatically escapes the value to prevent SQL injection
37
+ */
38
+ export const formatLikePattern = (
39
+ value: unknown,
40
+ pattern: 'contains' | 'startsWith' | 'endsWith'
41
+ ): string => {
42
+ const stringValue = typeof value === 'string' ? value : String(value)
43
+ const escaped = escapeSqlString(stringValue)
44
+
45
+ switch (pattern) {
46
+ case 'contains':
47
+ return `'%${escaped}%'`
48
+ case 'startsWith':
49
+ return `'${escaped}%'`
50
+ case 'endsWith':
51
+ return `'%${escaped}'`
52
+ }
53
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { Layer } from 'effect'
9
+ import { ActivityRepositoryLive } from './repositories/activity-repository-live'
10
+ import { BatchRepositoryLive } from './repositories/batch-repository-live'
11
+ import { CommentRepositoryLive } from './repositories/comment-repository-live'
12
+ import { TableRepositoryLive } from './repositories/table-repository-live'
13
+
14
+ /**
15
+ * Composite layer providing all table-related repository implementations
16
+ *
17
+ * Import this single layer in presentation routes to satisfy
18
+ * all table, batch, comment, and activity repository requirements.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * runEffect(c, program.pipe(Effect.provide(TableLive)), schema)
23
+ * ```
24
+ */
25
+ export const TableLive = Layer.mergeAll(
26
+ TableRepositoryLive,
27
+ BatchRepositoryLive,
28
+ CommentRepositoryLive,
29
+ ActivityRepositoryLive
30
+ )