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,96 @@
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 { createVolatileFormulaTriggers } from '../formula/formula-trigger-generators'
11
+ import { generateIndexStatements } from '../generators/index-generators'
12
+ import {
13
+ generateCreatedAtTriggers,
14
+ generateAutonumberTriggers,
15
+ generateUpdatedByTriggers,
16
+ generateUpdatedAtTriggers,
17
+ } from '../generators/trigger-generators'
18
+ import { shouldUseView, getBaseTableName } from '../lookup/lookup-view-generators'
19
+ import {
20
+ executeSQLStatements,
21
+ executeSQLStatementsParallel,
22
+ type TransactionLike,
23
+ type SQLExecutionError,
24
+ } from '../sql/sql-execution'
25
+ import type { Table } from '@/domain/models/app/table'
26
+
27
+ /**
28
+ * Apply table features (indexes, triggers)
29
+ * Shared by both createNewTable and migrateExistingTable
30
+ * Note: Triggers are applied to the base table, not the VIEW
31
+ *
32
+ * Field-level permissions are enforced at the application layer,
33
+ * not via PostgreSQL column-level GRANTs.
34
+ */
35
+ export const applyTableFeatures = (
36
+ tx: TransactionLike,
37
+ table: Table
38
+ ): Effect.Effect<void, SQLExecutionError> =>
39
+ Effect.gen(function* () {
40
+ // Sanitize table name for PostgreSQL
41
+ const sanitized = sanitizeTableName(table.name)
42
+ // Determine actual table name (base table if using VIEW)
43
+ const physicalTableName = shouldUseView(table) ? getBaseTableName(sanitized) : sanitized
44
+
45
+ // Create table object with physical table name for trigger generation
46
+ const physicalTable = shouldUseView(table) ? { ...table, name: physicalTableName } : table
47
+
48
+ // Indexes and triggers (can run in parallel - all independent)
49
+ // These create IF NOT EXISTS so order doesn't matter
50
+ yield* Effect.all(
51
+ [
52
+ executeSQLStatementsParallel(tx, generateIndexStatements(physicalTable)),
53
+ executeSQLStatements(tx, generateCreatedAtTriggers(physicalTable)),
54
+ executeSQLStatements(tx, generateAutonumberTriggers(physicalTable)),
55
+ executeSQLStatements(tx, generateUpdatedByTriggers(physicalTable)),
56
+ executeSQLStatements(tx, generateUpdatedAtTriggers(physicalTable)),
57
+ Effect.promise(() => createVolatileFormulaTriggers(tx, physicalTableName, table.fields)),
58
+ ],
59
+ { concurrency: 'unbounded' }
60
+ )
61
+ })
62
+
63
+ /**
64
+ * Apply table features without indexes (triggers only)
65
+ * Used during migration when indexes are handled separately by syncIndexes
66
+ * Note: Triggers are applied to the base table, not the VIEW
67
+ *
68
+ * Field-level permissions are enforced at the application layer,
69
+ * not via PostgreSQL column-level GRANTs.
70
+ */
71
+ export const applyTableFeaturesWithoutIndexes = (
72
+ tx: TransactionLike,
73
+ table: Table
74
+ ): Effect.Effect<void, SQLExecutionError> =>
75
+ Effect.gen(function* () {
76
+ // Sanitize table name for PostgreSQL
77
+ const sanitized = sanitizeTableName(table.name)
78
+ // Determine actual table name (base table if using VIEW)
79
+ const physicalTableName = shouldUseView(table) ? getBaseTableName(sanitized) : sanitized
80
+
81
+ // Create table object with physical table name for trigger generation
82
+ const physicalTable = shouldUseView(table) ? { ...table, name: physicalTableName } : table
83
+
84
+ // Triggers (can run in parallel - all independent)
85
+ // These create IF NOT EXISTS so order doesn't matter
86
+ yield* Effect.all(
87
+ [
88
+ executeSQLStatements(tx, generateCreatedAtTriggers(physicalTable)),
89
+ executeSQLStatements(tx, generateAutonumberTriggers(physicalTable)),
90
+ executeSQLStatements(tx, generateUpdatedByTriggers(physicalTable)),
91
+ executeSQLStatements(tx, generateUpdatedAtTriggers(physicalTable)),
92
+ Effect.promise(() => createVolatileFormulaTriggers(tx, physicalTableName, table.fields)),
93
+ ],
94
+ { concurrency: 'unbounded' }
95
+ )
96
+ })
@@ -0,0 +1,58 @@
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
+ * Normalize PostgreSQL type names by removing size specifications
10
+ */
11
+ export const normalizeType = (type: string): string =>
12
+ type
13
+ .replace(/\(\d+\)/, '') // Remove (N) like varchar(255) -> varchar
14
+ .replace(/\(\d+,\s*\d+\)/, '') // Remove (N,M) like numeric(10,2) -> numeric
15
+ .trim()
16
+
17
+ /**
18
+ * Type compatibility groups - types within a group can be safely copied between each other
19
+ */
20
+ const TYPE_COMPATIBILITY_GROUPS: readonly (readonly string[])[] = [
21
+ // Integer types (can be widened but not narrowed, but for recreation we allow same family)
22
+ ['smallint', 'integer', 'bigint', 'int2', 'int4', 'int8'],
23
+ // Text types
24
+ ['text', 'varchar', 'character varying', 'char', 'character', 'bpchar'],
25
+ // Boolean
26
+ ['boolean', 'bool'],
27
+ // Floating point
28
+ ['real', 'double precision', 'float4', 'float8'],
29
+ // Numeric/Decimal
30
+ ['numeric', 'decimal'],
31
+ // Date/Time
32
+ ['timestamp', 'timestamp without time zone', 'timestamp with time zone', 'timestamptz'],
33
+ ['date'],
34
+ ['time', 'time without time zone', 'time with time zone', 'timetz'],
35
+ // UUID
36
+ ['uuid'],
37
+ // JSON
38
+ ['json', 'jsonb'],
39
+ ]
40
+
41
+ /**
42
+ * Check if two PostgreSQL data types are compatible for data copying
43
+ * Returns true if data can be safely copied from oldType to newType
44
+ */
45
+ export const areTypesCompatible = (oldType: string, newType: string): boolean => {
46
+ // Exact match is always compatible
47
+ if (oldType === newType) return true
48
+
49
+ const normalizedOld = normalizeType(oldType)
50
+ const normalizedNew = normalizeType(newType)
51
+
52
+ if (normalizedOld === normalizedNew) return true
53
+
54
+ // Check if both types are in the same compatibility group
55
+ return TYPE_COMPATIBILITY_GROUPS.some(
56
+ (group) => group.includes(normalizedOld) && group.includes(normalizedNew)
57
+ )
58
+ }
@@ -0,0 +1,47 @@
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 - Re-export Module
10
+ *
11
+ * This file re-exports all table operation utilities from the split module structure.
12
+ * Maintained for backwards compatibility with existing imports.
13
+ *
14
+ * @see ./table-operations/ for the actual implementations
15
+ */
16
+
17
+ // Type exports
18
+ export type { MigrationConfig } from './table-operations/'
19
+
20
+ // All function exports
21
+ export {
22
+ // Type compatibility utilities
23
+ normalizeType,
24
+ areTypesCompatible,
25
+ // Column generators
26
+ generateIdColumn,
27
+ needsAutomaticIdColumn,
28
+ generateCreatedAtColumn,
29
+ generateUpdatedAtColumn,
30
+ generateDeletedAtColumn,
31
+ generatePrimaryKeyConstraintIfNeeded,
32
+ // CREATE TABLE SQL generation
33
+ generateCreateTableSQL,
34
+ // Table features (indexes, triggers, field permissions)
35
+ applyTableFeatures,
36
+ applyTableFeaturesWithoutIndexes,
37
+ // Migration utilities
38
+ getCompatibleColumns,
39
+ copyDataAndResetSequences,
40
+ recreateTableWithDataEffect,
41
+ // Table effect operations
42
+ migrateExistingTableEffect,
43
+ createNewTableEffect,
44
+ createLookupViewsEffect,
45
+ createTableViewsEffect,
46
+ createOrMigrateTableEffect,
47
+ } from './table-operations/'
@@ -0,0 +1,80 @@
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 { db, SessionContextError } from '@/infrastructure/database'
10
+ import { injectCreateAuthorship } from '../mutation-helpers/authorship-helpers'
11
+ import { logActivity } from '../query-helpers/activity-log-helpers'
12
+ import { wrapDatabaseErrorWithValidation } from '../shared/error-handling'
13
+ import { validateTableName } from '../shared/validation'
14
+ import { createSingleRecordInBatch, runEffectInTx } from './batch-helpers'
15
+ import type { Session } from '@/infrastructure/auth/better-auth/schema'
16
+ import type { ValidationError } from '@/infrastructure/database'
17
+
18
+ /**
19
+ * Batch create records
20
+ *
21
+ * Creates multiple records in a single transaction.
22
+ * Permissions applied via application layer.
23
+ *
24
+ * @param session - Better Auth session
25
+ * @param tableName - Name of the table
26
+ * @param recordsData - Array of field objects to insert
27
+ * @returns Effect resolving to array of created records
28
+ */
29
+ export function batchCreateRecords(
30
+ session: Readonly<Session>,
31
+ tableName: string,
32
+ recordsData: readonly Record<string, unknown>[]
33
+ ): Effect.Effect<readonly Record<string, unknown>[], SessionContextError | ValidationError> {
34
+ return Effect.gen(function* () {
35
+ const createdRecords = yield* Effect.tryPromise({
36
+ try: () =>
37
+ db.transaction(async (tx) => {
38
+ validateTableName(tableName)
39
+
40
+ if (recordsData.length === 0) {
41
+ // eslint-disable-next-line functional/no-throw-statements -- Required for transaction error handling
42
+ throw new SessionContextError('Cannot create batch with no records', undefined)
43
+ }
44
+
45
+ // Inject authorship metadata for each record
46
+ const recordsWithAuthorship = await Promise.all(
47
+ recordsData.map((fields) =>
48
+ injectCreateAuthorship(fields, session.userId, tx, tableName)
49
+ )
50
+ )
51
+
52
+ // Use Effect.reduce with runEffectInTx to properly propagate ValidationError
53
+ return await runEffectInTx(
54
+ Effect.reduce(
55
+ recordsWithAuthorship,
56
+ [] as readonly Record<string, unknown>[],
57
+ (acc, fields) =>
58
+ createSingleRecordInBatch(tx, tableName, fields).pipe(
59
+ Effect.map((record) => (record ? [...acc, record] : acc))
60
+ )
61
+ )
62
+ )
63
+ }),
64
+ catch: wrapDatabaseErrorWithValidation(`Failed to create batch records in ${tableName}`),
65
+ })
66
+
67
+ // Log activity for each created record
68
+ yield* Effect.forEach(createdRecords, (record) =>
69
+ logActivity({
70
+ session,
71
+ tableName,
72
+ action: 'create',
73
+ recordId: String(record.id),
74
+ changes: { after: record },
75
+ })
76
+ ).pipe(Effect.asVoid)
77
+
78
+ return createdRecords
79
+ })
80
+ }
@@ -0,0 +1,212 @@
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 { sql } from 'drizzle-orm'
9
+ import { Effect } from 'effect'
10
+ import { db, SessionContextError, type DrizzleTransaction } from '@/infrastructure/database'
11
+ import { fetchRecordsByIds } from '../mutation-helpers/record-fetch-helpers'
12
+ import { logActivity } from '../query-helpers/activity-log-helpers'
13
+ import { wrapDatabaseError } from '../shared/error-handling'
14
+ import { validateTableName } from '../shared/validation'
15
+ import { runEffectInTx } from './batch-helpers'
16
+ import type { Session } from '@/infrastructure/auth/better-auth/schema'
17
+
18
+ /**
19
+ * Validate records exist for batch delete
20
+ */
21
+ async function validateRecordsForDelete(
22
+ tx: Readonly<DrizzleTransaction>,
23
+ tableIdent: Readonly<ReturnType<typeof sql.identifier>>,
24
+ recordIds: readonly string[]
25
+ ): Promise<void> {
26
+ const validationResults = await Promise.all(
27
+ recordIds.map(async (recordId) => {
28
+ const checkResult = (await tx.execute(
29
+ sql`SELECT id FROM ${tableIdent} WHERE id = ${recordId} LIMIT 1`
30
+ )) as readonly Record<string, unknown>[]
31
+
32
+ if (checkResult.length === 0) {
33
+ return { recordId, error: 'not found' }
34
+ }
35
+
36
+ return { recordId, error: undefined }
37
+ })
38
+ )
39
+
40
+ const firstError = validationResults.find((result) => result.error !== undefined)
41
+ if (firstError) {
42
+ // eslint-disable-next-line functional/no-throw-statements -- Required for Effect.tryPromise error handling
43
+ throw new Error(`Record ${firstError.recordId} not found`)
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Validate records exist for batch delete with Effect error handling
49
+ */
50
+ function validateRecordsForDeleteWithEffect(
51
+ tx: Readonly<DrizzleTransaction>,
52
+ tableIdent: Readonly<ReturnType<typeof sql.identifier>>,
53
+ recordIds: readonly string[]
54
+ ): Effect.Effect<void, SessionContextError> {
55
+ return Effect.tryPromise({
56
+ try: () => validateRecordsForDelete(tx, tableIdent, recordIds),
57
+ catch: (error) => {
58
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error'
59
+ return new SessionContextError(`Validation failed: ${errorMessage}`, error)
60
+ },
61
+ })
62
+ }
63
+
64
+ /**
65
+ * Check if table supports soft delete (has deleted_at column)
66
+ */
67
+ function checkSoftDeleteSupport(
68
+ tx: Readonly<DrizzleTransaction>,
69
+ tableName: string
70
+ ): Effect.Effect<boolean, SessionContextError> {
71
+ return Effect.tryPromise({
72
+ try: async () => {
73
+ const columnCheck = (await tx.execute(
74
+ sql`SELECT column_name FROM information_schema.columns WHERE table_name = ${tableName} AND column_name = 'deleted_at'`
75
+ )) as readonly Record<string, unknown>[]
76
+ return columnCheck.length > 0
77
+ },
78
+ catch: (error) => new SessionContextError('Failed to check deleted_at column', error),
79
+ })
80
+ }
81
+
82
+ /**
83
+ * Check if table has deleted_by column for authorship tracking
84
+ */
85
+ function checkDeletedBySupport(
86
+ tx: Readonly<DrizzleTransaction>,
87
+ tableName: string
88
+ ): Effect.Effect<boolean, SessionContextError> {
89
+ return Effect.tryPromise({
90
+ try: async () => {
91
+ const columnCheck = (await tx.execute(
92
+ sql`SELECT column_name FROM information_schema.columns WHERE table_name = ${tableName} AND column_name = 'deleted_by'`
93
+ )) as readonly Record<string, unknown>[]
94
+ return columnCheck.length > 0
95
+ },
96
+ catch: (error) => new SessionContextError('Failed to check deleted_by column', error),
97
+ })
98
+ }
99
+
100
+ /**
101
+ * Execute delete query (soft or hard delete based on parameters)
102
+ */
103
+ function executeDeleteQuery(
104
+ tx: Readonly<DrizzleTransaction>,
105
+ params: {
106
+ readonly tableName: string
107
+ readonly recordIds: readonly string[]
108
+ readonly hasSoftDelete: boolean
109
+ readonly hasDeletedBy: boolean
110
+ readonly permanent: boolean
111
+ readonly userId: string
112
+ }
113
+ ): Effect.Effect<number, SessionContextError> {
114
+ return Effect.tryPromise({
115
+ try: async () => {
116
+ const tableIdent = sql.identifier(params.tableName)
117
+ const idParams = sql.join(
118
+ params.recordIds.map((id) => sql`${id}`),
119
+ sql.raw(', ')
120
+ )
121
+
122
+ // Determine query type: permanent delete, soft delete, or hard delete (no soft delete support)
123
+ const query = params.permanent
124
+ ? sql`DELETE FROM ${tableIdent} WHERE id IN (${idParams}) RETURNING id`
125
+ : params.hasSoftDelete
126
+ ? params.hasDeletedBy
127
+ ? sql`UPDATE ${tableIdent} SET deleted_at = NOW(), deleted_by = ${params.userId} WHERE id IN (${idParams}) AND deleted_at IS NULL RETURNING id`
128
+ : sql`UPDATE ${tableIdent} SET deleted_at = NOW() WHERE id IN (${idParams}) AND deleted_at IS NULL RETURNING id`
129
+ : sql`DELETE FROM ${tableIdent} WHERE id IN (${idParams}) RETURNING id`
130
+
131
+ const result = (await tx.execute(query)) as readonly Record<string, unknown>[]
132
+ return result.length
133
+ },
134
+ catch: (error) =>
135
+ new SessionContextError(`Failed to delete records in ${params.tableName}`, error),
136
+ })
137
+ }
138
+
139
+ /**
140
+ * Log delete activities for all deleted records
141
+ */
142
+ function logDeleteActivities(
143
+ session: Readonly<Session>,
144
+ tableName: string,
145
+ recordsBefore: readonly Record<string, unknown>[]
146
+ ): Effect.Effect<void, never> {
147
+ return Effect.forEach(recordsBefore, (record) =>
148
+ logActivity({
149
+ session,
150
+ tableName,
151
+ action: 'delete',
152
+ recordId: String(record.id),
153
+ changes: { before: record },
154
+ })
155
+ ).pipe(Effect.asVoid)
156
+ }
157
+
158
+ /**
159
+ * Batch delete records
160
+ *
161
+ * Deletes multiple records (soft or hard delete based on parameters).
162
+ * Validates all records exist before deleting any.
163
+ * Rolls back if any record is not found.
164
+ * Permissions applied via application layer.
165
+ *
166
+ * @param session - Better Auth session
167
+ * @param tableName - Name of the table
168
+ * @param recordIds - Array of record IDs to delete
169
+ * @param permanent - If true, performs hard delete; otherwise soft delete (if supported)
170
+ * @returns Effect resolving to number of deleted records
171
+ */
172
+ export function batchDeleteRecords(
173
+ session: Readonly<Session>,
174
+ tableName: string,
175
+ recordIds: readonly string[],
176
+ permanent = false
177
+ ): Effect.Effect<number, SessionContextError> {
178
+ return Effect.gen(function* () {
179
+ const { deletedCount, recordsBefore } = yield* Effect.tryPromise({
180
+ try: () =>
181
+ db.transaction(async (tx) => {
182
+ validateTableName(tableName)
183
+ const tableIdent = sql.identifier(tableName)
184
+
185
+ // eslint-disable-next-line functional/no-expression-statements -- Required for transaction validation
186
+ await runEffectInTx(validateRecordsForDeleteWithEffect(tx, tableIdent, recordIds))
187
+
188
+ const before = await runEffectInTx(fetchRecordsByIds(tx, tableName, recordIds))
189
+ const hasSoftDelete = await runEffectInTx(checkSoftDeleteSupport(tx, tableName))
190
+ const hasDeletedBy = await runEffectInTx(checkDeletedBySupport(tx, tableName))
191
+
192
+ const count = await runEffectInTx(
193
+ executeDeleteQuery(tx, {
194
+ tableName,
195
+ recordIds,
196
+ hasSoftDelete,
197
+ hasDeletedBy,
198
+ permanent,
199
+ userId: session.userId,
200
+ })
201
+ )
202
+
203
+ return { deletedCount: count, recordsBefore: before }
204
+ }),
205
+ catch: wrapDatabaseError(`Failed to delete records in ${tableName}`),
206
+ })
207
+
208
+ yield* logDeleteActivities(session, tableName, recordsBefore)
209
+
210
+ return deletedCount
211
+ })
212
+ }
@@ -0,0 +1,124 @@
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 { sql } from 'drizzle-orm'
9
+ import { Data, Effect, Exit, Cause } from 'effect'
10
+ import { ValidationError, type DrizzleTransaction } from '@/infrastructure/database'
11
+ import { validateColumnName } from '../shared/validation'
12
+
13
+ /**
14
+ * Batch validation error - returned when batch validation fails
15
+ */
16
+ export class BatchValidationError extends Data.TaggedError('BatchValidationError')<{
17
+ readonly message: string
18
+ readonly details?: readonly string[]
19
+ }> {}
20
+
21
+ /**
22
+ * Run an Effect inside a database transaction, properly unwrapping errors.
23
+ *
24
+ * Unlike Effect.runPromise which wraps errors in FiberFailure (breaking instanceof checks
25
+ * in outer catch handlers), this helper extracts the original error via Cause.squash
26
+ * and re-throws it directly. This ensures SessionContextError, ValidationError, etc.
27
+ * are properly detected by instanceof in Effect.tryPromise catch handlers.
28
+ */
29
+ export async function runEffectInTx<A, E>(effect: Effect.Effect<A, E, never>): Promise<A> {
30
+ const exit = await Effect.runPromiseExit(effect)
31
+ if (Exit.isSuccess(exit)) return exit.value
32
+ // eslint-disable-next-line functional/no-throw-statements -- Required to propagate Effect errors in async transaction context
33
+ throw Cause.squash(exit.cause)
34
+ }
35
+
36
+ /**
37
+ * Build INSERT SQL clauses from fields object.
38
+ * Returns undefined if fields is empty.
39
+ */
40
+ function buildInsertClauses(fields: Readonly<Record<string, unknown>>):
41
+ | {
42
+ readonly columnsClause: ReturnType<typeof sql.join>
43
+ readonly valuesClause: ReturnType<typeof sql.join>
44
+ }
45
+ | undefined {
46
+ const entries = Object.entries(fields)
47
+ if (entries.length === 0) return undefined
48
+
49
+ const columnIdentifiers = entries.map(([key]) => {
50
+ validateColumnName(key)
51
+ return sql.identifier(key)
52
+ })
53
+ const valueParams = entries.map(([, value]) => sql`${value}`)
54
+
55
+ return {
56
+ columnsClause: sql.join(columnIdentifiers, sql.raw(', ')),
57
+ valuesClause: sql.join(valueParams, sql.raw(', ')),
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Handle PostgreSQL NOT NULL violation errors, converting to ValidationError.
63
+ */
64
+ // eslint-disable-next-line functional/prefer-immutable-types -- called from Effect.tryPromise catch
65
+ function handleInsertError(error: unknown): ValidationError {
66
+ const pgError = error as { code?: string; message?: string }
67
+ if (pgError.code === '23502' || pgError.message?.includes('null value in column')) {
68
+ return new ValidationError('Validation failed: Required field is missing', [
69
+ { record: 0, field: 'unknown', error: 'Required field is missing' },
70
+ ])
71
+ }
72
+ const errorMessage: string =
73
+ pgError.message !== undefined ? pgError.message : 'Insert failed due to constraint violation'
74
+ return new ValidationError(errorMessage, [])
75
+ }
76
+
77
+ /**
78
+ * Helper to create a single record within a transaction
79
+ */
80
+ export async function createSingleRecord(
81
+ tx: Readonly<DrizzleTransaction>,
82
+ tableName: string,
83
+ fields: Readonly<Record<string, unknown>>
84
+ ): Promise<Readonly<Record<string, unknown>> | undefined> {
85
+ const clauses = buildInsertClauses(fields)
86
+ if (!clauses) return undefined
87
+
88
+ try {
89
+ const result = (await tx.execute(
90
+ sql`INSERT INTO ${sql.identifier(tableName)} (${clauses.columnsClause}) VALUES (${clauses.valuesClause}) RETURNING *`
91
+ )) as readonly Record<string, unknown>[]
92
+
93
+ return result[0] ?? undefined
94
+ } catch (error) {
95
+ // eslint-disable-next-line functional/no-throw-statements -- Required for error propagation
96
+ throw handleInsertError(error)
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Effect-based helper to create a single record within a batch operation
102
+ *
103
+ * This is the Effect version of createSingleRecord, used by batchCreateRecords
104
+ * to properly propagate ValidationError through Effect.reduce.
105
+ */
106
+ export function createSingleRecordInBatch(
107
+ tx: Readonly<DrizzleTransaction>,
108
+ tableName: string,
109
+ fields: Readonly<Record<string, unknown>>
110
+ ): Effect.Effect<Record<string, unknown> | undefined, ValidationError> {
111
+ return Effect.tryPromise({
112
+ try: async () => {
113
+ const clauses = buildInsertClauses(fields)
114
+ if (!clauses) return undefined
115
+
116
+ const result = (await tx.execute(
117
+ sql`INSERT INTO ${sql.identifier(tableName)} (${clauses.columnsClause}) VALUES (${clauses.valuesClause}) RETURNING *`
118
+ )) as readonly Record<string, unknown>[]
119
+
120
+ return result[0] ?? undefined
121
+ },
122
+ catch: handleInsertError,
123
+ })
124
+ }