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,77 @@
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 { findDuplicate } from './field-types/validation-utils'
9
+
10
+ /**
11
+ * Validate that field permissions reference existing fields and don't have duplicates.
12
+ *
13
+ * @param fieldPermissions - Array of field permissions to validate
14
+ * @param fieldNames - Set of valid field names in the table
15
+ * @returns Error object if validation fails, undefined if valid
16
+ */
17
+ export const validateFieldPermissions = (
18
+ fieldPermissions: ReadonlyArray<{ readonly field: string }>,
19
+ fieldNames: ReadonlySet<string>
20
+ ): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
21
+ // Check for duplicate field permissions
22
+ const fieldPermissionNames = fieldPermissions.map((fp) => fp.field)
23
+ const duplicateField = findDuplicate(fieldPermissionNames)
24
+
25
+ if (duplicateField) {
26
+ return {
27
+ message: `Duplicate field permission for field '${duplicateField}' - conflicting permission definitions`,
28
+ path: ['permissions', 'fields'],
29
+ }
30
+ }
31
+
32
+ // Check for non-existent field references
33
+ const invalidFieldPermission = fieldPermissions.find(
34
+ (fieldPermission) => !fieldNames.has(fieldPermission.field)
35
+ )
36
+
37
+ if (invalidFieldPermission) {
38
+ return {
39
+ message: `Field permission references non-existent field '${invalidFieldPermission.field}' - field does not exist in table`,
40
+ path: ['permissions', 'fields'],
41
+ }
42
+ }
43
+
44
+ return undefined
45
+ }
46
+
47
+ /**
48
+ * Validate table permissions including field permissions.
49
+ *
50
+ * With the simplified 3-format permission system ('all', 'authenticated', role array),
51
+ * table-level CRUD permissions are validated by the schema itself.
52
+ * This function handles cross-field validation (field permissions referencing table fields).
53
+ *
54
+ * @param permissions - Table permissions to validate
55
+ * @param _fields - Table fields (unused, kept for interface compatibility)
56
+ * @param fieldNames - Set of valid field names
57
+ * @returns Validation error object if invalid, undefined if valid
58
+ */
59
+ export const validateTablePermissions = (
60
+ permissions: {
61
+ readonly fields?: ReadonlyArray<{
62
+ readonly field: string
63
+ }>
64
+ },
65
+ _fields: ReadonlyArray<{ readonly name: string; readonly type: string }>,
66
+ fieldNames: ReadonlySet<string>
67
+ ): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
68
+ // Validate field permissions reference existing fields
69
+ if (permissions.fields) {
70
+ const fieldPermissionsError = validateFieldPermissions(permissions.fields, fieldNames)
71
+ if (fieldPermissionsError) {
72
+ return fieldPermissionsError
73
+ }
74
+ }
75
+
76
+ return undefined
77
+ }
@@ -0,0 +1,49 @@
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 { findDuplicate } from './field-types/validation-utils'
9
+ import { SPECIAL_FIELDS } from './table-formula-validation'
10
+
11
+ /**
12
+ * Validate primary key configuration (field references, duplicates).
13
+ *
14
+ * @param primaryKey - Primary key configuration to validate
15
+ * @param fieldNames - Set of valid field names in the table
16
+ * @returns Error object if validation fails, undefined if valid
17
+ */
18
+ export const validatePrimaryKey = (
19
+ primaryKey: { readonly type: string; readonly fields?: ReadonlyArray<string> } | undefined,
20
+ fieldNames: ReadonlySet<string>
21
+ ): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
22
+ if (!primaryKey || primaryKey.type !== 'composite' || !primaryKey.fields) {
23
+ return undefined
24
+ }
25
+
26
+ // Check for duplicate field references
27
+ const duplicateField = findDuplicate(primaryKey.fields)
28
+
29
+ if (duplicateField) {
30
+ return {
31
+ message: `Primary key field '${duplicateField}' is not unique - duplicate field references in composite primary key`,
32
+ path: ['primaryKey', 'fields'],
33
+ }
34
+ }
35
+
36
+ // Check for non-existent field references (allow special fields)
37
+ const invalidField = primaryKey.fields.find(
38
+ (field) => !fieldNames.has(field) && !SPECIAL_FIELDS.has(field)
39
+ )
40
+
41
+ if (invalidField) {
42
+ return {
43
+ message: `Primary key references non-existent field '${invalidField}' - field not found in table`,
44
+ path: ['primaryKey', 'fields'],
45
+ }
46
+ }
47
+
48
+ return undefined
49
+ }
@@ -0,0 +1,408 @@
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 { findDuplicate } from './field-types/validation-utils'
9
+ import { SPECIAL_FIELDS } from './table-formula-validation'
10
+
11
+ /**
12
+ * Validate that view IDs are unique within a table.
13
+ *
14
+ * @param views - Array of views to validate
15
+ * @returns Error object if validation fails, undefined if valid
16
+ */
17
+ const validateViewIds = (
18
+ views: ReadonlyArray<{ readonly id: string | number }>
19
+ ): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
20
+ // Convert all view IDs to strings for comparison (ViewId can be number or string)
21
+ const viewIds = views.map((view) => String(view.id))
22
+
23
+ // Find duplicate view ID
24
+ const duplicateId = findDuplicate(viewIds)
25
+
26
+ if (duplicateId) {
27
+ return {
28
+ message: `Duplicate view id '${duplicateId}' - view id must be unique within the table`,
29
+ path: ['views'],
30
+ }
31
+ }
32
+
33
+ return undefined
34
+ }
35
+
36
+ /**
37
+ * Validate that only one view is marked as default within a table.
38
+ *
39
+ * @param views - Array of views to validate
40
+ * @returns Error object if validation fails, undefined if valid
41
+ */
42
+ const validateDefaultViews = (
43
+ views: ReadonlyArray<{ readonly id: string | number; readonly isDefault?: boolean }>
44
+ ): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
45
+ const defaultViews = views.filter((view) => view.isDefault === true)
46
+
47
+ if (defaultViews.length > 1) {
48
+ return {
49
+ message: 'Only one default view is allowed per table - multiple default views found',
50
+ path: ['views'],
51
+ }
52
+ }
53
+
54
+ return undefined
55
+ }
56
+
57
+ /**
58
+ * Extract field references from a filter node recursively.
59
+ * Handles single conditions, AND groups, and OR groups.
60
+ *
61
+ * @param filterNode - The filter node to extract fields from
62
+ * @returns Array of field names referenced in the filter
63
+ */
64
+ const extractFieldReferencesFromFilter = (
65
+ filterNode:
66
+ | { readonly field: string; readonly operator: string; readonly value: unknown }
67
+ | { readonly and: ReadonlyArray<unknown> }
68
+ | { readonly or: ReadonlyArray<unknown> }
69
+ ): ReadonlyArray<string> => {
70
+ // Single condition - extract field name
71
+ if ('field' in filterNode) {
72
+ return [filterNode.field]
73
+ }
74
+
75
+ // AND group - recursively extract from all conditions
76
+ if ('and' in filterNode && Array.isArray(filterNode.and)) {
77
+ return filterNode.and.flatMap((node) =>
78
+ extractFieldReferencesFromFilter(
79
+ node as
80
+ | { readonly field: string; readonly operator: string; readonly value: unknown }
81
+ | { readonly and: ReadonlyArray<unknown> }
82
+ | { readonly or: ReadonlyArray<unknown> }
83
+ )
84
+ )
85
+ }
86
+
87
+ // OR group - recursively extract from all conditions
88
+ if ('or' in filterNode && Array.isArray(filterNode.or)) {
89
+ return filterNode.or.flatMap((node) =>
90
+ extractFieldReferencesFromFilter(
91
+ node as
92
+ | { readonly field: string; readonly operator: string; readonly value: unknown }
93
+ | { readonly and: ReadonlyArray<unknown> }
94
+ | { readonly or: ReadonlyArray<unknown> }
95
+ )
96
+ )
97
+ }
98
+
99
+ return []
100
+ }
101
+
102
+ /**
103
+ * Extract filter conditions from a filter node recursively.
104
+ * Returns array of conditions with field, operator, and value.
105
+ *
106
+ * @param filterNode - The filter node to extract conditions from
107
+ * @returns Array of filter conditions
108
+ */
109
+ const extractFilterConditions = (
110
+ filterNode:
111
+ | { readonly field: string; readonly operator: string; readonly value: unknown }
112
+ | { readonly and: ReadonlyArray<unknown> }
113
+ | { readonly or: ReadonlyArray<unknown> }
114
+ ): ReadonlyArray<{
115
+ readonly field: string
116
+ readonly operator: string
117
+ readonly value: unknown
118
+ }> => {
119
+ // Single condition - return as array
120
+ if ('field' in filterNode) {
121
+ return [filterNode]
122
+ }
123
+
124
+ // AND group - recursively extract from all conditions
125
+ if ('and' in filterNode && Array.isArray(filterNode.and)) {
126
+ return filterNode.and.flatMap((node) =>
127
+ extractFilterConditions(
128
+ node as
129
+ | { readonly field: string; readonly operator: string; readonly value: unknown }
130
+ | { readonly and: ReadonlyArray<unknown> }
131
+ | { readonly or: ReadonlyArray<unknown> }
132
+ )
133
+ )
134
+ }
135
+
136
+ // OR group - recursively extract from all conditions
137
+ if ('or' in filterNode && Array.isArray(filterNode.or)) {
138
+ return filterNode.or.flatMap((node) =>
139
+ extractFilterConditions(
140
+ node as
141
+ | { readonly field: string; readonly operator: string; readonly value: unknown }
142
+ | { readonly and: ReadonlyArray<unknown> }
143
+ | { readonly or: ReadonlyArray<unknown> }
144
+ )
145
+ )
146
+ }
147
+
148
+ return []
149
+ }
150
+
151
+ /**
152
+ * Operator compatibility rules for field types.
153
+ * Maps field types to their valid operators.
154
+ * Only enforces restrictions for specific field types (e.g., checkbox cannot use 'contains').
155
+ * Other operators are allowed by default to avoid breaking valid use cases.
156
+ */
157
+ const FIELD_TYPE_OPERATORS: ReadonlyMap<string, ReadonlySet<string>> = new Map([
158
+ // Checkbox: only boolean operators allowed
159
+ ['checkbox', new Set(['equals', 'isTrue', 'isFalse'])],
160
+ ])
161
+
162
+ /**
163
+ * Validate that filter operators are compatible with field types.
164
+ *
165
+ * @param views - Array of views to validate
166
+ * @param fields - Array of fields in the table
167
+ * @returns Error object if validation fails, undefined if valid
168
+ */
169
+ const validateFilterOperatorCompatibility = (
170
+ views: ReadonlyArray<{
171
+ readonly id: string | number
172
+ readonly filters?:
173
+ | { readonly field: string; readonly operator: string; readonly value: unknown }
174
+ | { readonly and: ReadonlyArray<unknown> }
175
+ | { readonly or: ReadonlyArray<unknown> }
176
+ }>,
177
+ fields: ReadonlyArray<{ readonly name: string; readonly type: string }>
178
+ ): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
179
+ const fieldTypeMap = new Map(fields.map((field) => [field.name, field.type]))
180
+
181
+ const incompatibleFilter = views
182
+ .filter((view) => view.filters !== undefined)
183
+ .flatMap((view) => {
184
+ const conditions = extractFilterConditions(view.filters!)
185
+ return conditions.flatMap((condition) => {
186
+ const fieldType = fieldTypeMap.get(condition.field)
187
+ if (!fieldType) {
188
+ return []
189
+ }
190
+
191
+ const validOperators = FIELD_TYPE_OPERATORS.get(fieldType)
192
+ if (!validOperators) {
193
+ // No restrictions defined for this field type
194
+ return []
195
+ }
196
+
197
+ if (!validOperators.has(condition.operator)) {
198
+ return [{ view, condition, fieldType }]
199
+ }
200
+
201
+ return []
202
+ })
203
+ })
204
+ .at(0)
205
+
206
+ if (incompatibleFilter) {
207
+ return {
208
+ message: `Incompatible operator '${incompatibleFilter.condition.operator}' for field '${incompatibleFilter.condition.field}' with type '${incompatibleFilter.fieldType}' - operator is invalid for checkbox field type`,
209
+ path: ['views'],
210
+ }
211
+ }
212
+
213
+ return undefined
214
+ }
215
+
216
+ /**
217
+ * Validate that view filters reference existing fields in the table.
218
+ *
219
+ * @param views - Array of views to validate
220
+ * @param fieldNames - Set of valid field names in the table
221
+ * @returns Error object if validation fails, undefined if valid
222
+ */
223
+ const validateViewFilters = (
224
+ views: ReadonlyArray<{
225
+ readonly id: string | number
226
+ readonly filters?:
227
+ | { readonly field: string; readonly operator: string; readonly value: unknown }
228
+ | { readonly and: ReadonlyArray<unknown> }
229
+ | { readonly or: ReadonlyArray<unknown> }
230
+ }>,
231
+ fieldNames: ReadonlySet<string>
232
+ ): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
233
+ const invalidView = views
234
+ .filter((view) => view.filters !== undefined)
235
+ .flatMap((view) => {
236
+ const referencedFields = extractFieldReferencesFromFilter(view.filters!)
237
+ const invalidFields = referencedFields.filter(
238
+ (fieldName) => !fieldNames.has(fieldName) && !SPECIAL_FIELDS.has(fieldName)
239
+ )
240
+ return invalidFields.map((invalidField) => ({ view, invalidField }))
241
+ })
242
+ .at(0)
243
+
244
+ if (invalidView) {
245
+ return {
246
+ message: `Filter references non-existent field '${invalidView.invalidField}' - field not found in table`,
247
+ path: ['views'],
248
+ }
249
+ }
250
+
251
+ return undefined
252
+ }
253
+
254
+ /**
255
+ * Validate that view fields reference existing fields in the table.
256
+ *
257
+ * @param views - Array of views to validate
258
+ * @param fieldNames - Set of valid field names in the table
259
+ * @returns Error object if validation fails, undefined if valid
260
+ */
261
+ const validateViewFields = (
262
+ views: ReadonlyArray<{ readonly id: string | number; readonly fields?: ReadonlyArray<string> }>,
263
+ fieldNames: ReadonlySet<string>
264
+ ): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
265
+ const invalidView = views
266
+ .filter(
267
+ (view): view is typeof view & { readonly fields: ReadonlyArray<string> } =>
268
+ view.fields !== undefined && view.fields.length > 0
269
+ )
270
+ .flatMap((view) => {
271
+ const invalidFields = view.fields.filter(
272
+ (fieldName) => !fieldNames.has(fieldName) && !SPECIAL_FIELDS.has(fieldName)
273
+ )
274
+ return invalidFields.map((invalidField) => ({ view, invalidField }))
275
+ })
276
+ .at(0)
277
+
278
+ if (invalidView) {
279
+ return {
280
+ message: `View field '${invalidView.invalidField}' not found - view fields must reference existing table fields (non-existent field in view)`,
281
+ path: ['views'],
282
+ }
283
+ }
284
+
285
+ return undefined
286
+ }
287
+
288
+ /**
289
+ * Validate that view groupBy references existing fields in the table.
290
+ *
291
+ * @param views - Array of views to validate
292
+ * @param fieldNames - Set of valid field names in the table
293
+ * @returns Error object if validation fails, undefined if valid
294
+ */
295
+ const validateViewGroupBy = (
296
+ views: ReadonlyArray<{
297
+ readonly id: string | number
298
+ readonly groupBy?: { readonly field: string }
299
+ }>,
300
+ fieldNames: ReadonlySet<string>
301
+ ): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
302
+ const invalidView = views
303
+ .filter(
304
+ (view): view is typeof view & { readonly groupBy: { readonly field: string } } =>
305
+ view.groupBy !== undefined
306
+ )
307
+ .find((view) => !fieldNames.has(view.groupBy.field) && !SPECIAL_FIELDS.has(view.groupBy.field))
308
+
309
+ if (invalidView) {
310
+ return {
311
+ message: `groupBy references non-existent field '${invalidView.groupBy.field}' - field not found in table`,
312
+ path: ['views'],
313
+ }
314
+ }
315
+
316
+ return undefined
317
+ }
318
+
319
+ /**
320
+ * Validate that view sorts reference existing fields in the table.
321
+ *
322
+ * @param views - Array of views to validate
323
+ * @param fieldNames - Set of valid field names in the table
324
+ * @returns Error object if validation fails, undefined if valid
325
+ */
326
+ const validateViewSorts = (
327
+ views: ReadonlyArray<{
328
+ readonly id: string | number
329
+ readonly sorts?: ReadonlyArray<{ readonly field: string; readonly direction: string }>
330
+ }>,
331
+ fieldNames: ReadonlySet<string>
332
+ ): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
333
+ const invalidView = views
334
+ .filter(
335
+ (
336
+ view
337
+ ): view is typeof view & {
338
+ readonly sorts: ReadonlyArray<{ readonly field: string; readonly direction: string }>
339
+ } => view.sorts !== undefined && view.sorts.length > 0
340
+ )
341
+ .flatMap((view) => {
342
+ const invalidFields = view.sorts.filter(
343
+ (sort) => !fieldNames.has(sort.field) && !SPECIAL_FIELDS.has(sort.field)
344
+ )
345
+ return invalidFields.map((sort) => ({ view, invalidField: sort.field }))
346
+ })
347
+ .at(0)
348
+
349
+ if (invalidView) {
350
+ return {
351
+ message: `Sort references non-existent field '${invalidView.invalidField}' - field not found in table`,
352
+ path: ['views'],
353
+ }
354
+ }
355
+
356
+ return undefined
357
+ }
358
+
359
+ /**
360
+ * Validate views configuration (IDs, default views, field references, filter references).
361
+ *
362
+ * @param views - Array of views to validate
363
+ * @param fields - Array of fields in the table
364
+ * @param fieldNames - Set of valid field names in the table
365
+ * @returns Error object if validation fails, undefined if valid
366
+ */
367
+ export const validateViews = (
368
+ views: ReadonlyArray<{ readonly id: string | number; readonly isDefault?: boolean }>,
369
+ fields: ReadonlyArray<{ readonly name: string; readonly type: string }>,
370
+ fieldNames: ReadonlySet<string>
371
+ ): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
372
+ const viewsValidationError = validateViewIds(views)
373
+ if (viewsValidationError) {
374
+ return viewsValidationError
375
+ }
376
+
377
+ const defaultViewsValidationError = validateDefaultViews(views)
378
+ if (defaultViewsValidationError) {
379
+ return defaultViewsValidationError
380
+ }
381
+
382
+ const viewFieldsValidationError = validateViewFields(views, fieldNames)
383
+ if (viewFieldsValidationError) {
384
+ return viewFieldsValidationError
385
+ }
386
+
387
+ const viewFiltersValidationError = validateViewFilters(views, fieldNames)
388
+ if (viewFiltersValidationError) {
389
+ return viewFiltersValidationError
390
+ }
391
+
392
+ const viewSortsValidationError = validateViewSorts(views, fieldNames)
393
+ if (viewSortsValidationError) {
394
+ return viewSortsValidationError
395
+ }
396
+
397
+ const viewGroupByValidationError = validateViewGroupBy(views, fieldNames)
398
+ if (viewGroupByValidationError) {
399
+ return viewGroupByValidationError
400
+ }
401
+
402
+ const operatorCompatibilityError = validateFilterOperatorCompatibility(views, fields)
403
+ if (operatorCompatibilityError) {
404
+ return operatorCompatibilityError
405
+ }
406
+
407
+ return undefined
408
+ }
@@ -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
+ import { Schema } from 'effect'
9
+
10
+ /**
11
+ * Unique Constraints
12
+ *
13
+ * Composite unique constraints ensure that combinations of multiple field values are unique across all rows. Use this when you need uniqueness across multiple fields (e.g., email + tenant_id must be unique together).
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * [
18
+ * {
19
+ * "name": "uq_user_email_tenant",
20
+ * "fields": [
21
+ * "email",
22
+ * "tenant_id"
23
+ * ]
24
+ * },
25
+ * {
26
+ * "name": "uq_product_sku_variant",
27
+ * "fields": [
28
+ * "sku",
29
+ * "variant_id"
30
+ * ]
31
+ * }
32
+ * ]
33
+ * ```
34
+ */
35
+ export const UniqueConstraintsSchema = Schema.Array(
36
+ Schema.Struct({
37
+ name: Schema.String.pipe(
38
+ Schema.minLength(1, { message: () => 'This field is required' }),
39
+ Schema.pattern(/^[a-zA-Z][a-zA-Z0-9_]*$/, {
40
+ message: () =>
41
+ "Name of the unique constraint. Use descriptive names like 'uq_tablename_field1_field2'",
42
+ }),
43
+ Schema.transform(Schema.String, {
44
+ decode: (name) => name.toLowerCase(),
45
+ encode: (name) => name,
46
+ }),
47
+ Schema.annotations({
48
+ description:
49
+ "Name of the unique constraint. Use descriptive names like 'uq_tablename_field1_field2'",
50
+ examples: ['uq_users_email_tenant', 'uq_products_sku_variant', 'uq_orders_number_year'],
51
+ })
52
+ ),
53
+ fields: Schema.Array(
54
+ Schema.String.pipe(Schema.minLength(1, { message: () => 'This field is required' }))
55
+ ).pipe(Schema.minItems(1, { message: () => 'At least one field is required' })),
56
+ })
57
+ ).pipe(
58
+ Schema.filter((constraints) => {
59
+ const names = constraints.map((constraint) => constraint.name)
60
+ const uniqueNames = new Set(names)
61
+ return (
62
+ names.length === uniqueNames.size ||
63
+ 'Duplicate constraint name - constraint name must be unique within the table'
64
+ )
65
+ }),
66
+ Schema.annotations({
67
+ title: 'Unique Constraints',
68
+ description:
69
+ 'Composite unique constraints ensure that combinations of multiple field values are unique across all rows. Use this when you need uniqueness across multiple fields (e.g., email + tenant_id must be unique together).',
70
+ examples: [
71
+ [
72
+ { name: 'uq_user_email_tenant', fields: ['email', 'tenant_id'] },
73
+ { name: 'uq_product_sku_variant', fields: ['sku', 'variant_id'] },
74
+ ],
75
+ ],
76
+ })
77
+ )
78
+
79
+ export type UniqueConstraints = Schema.Schema.Type<typeof UniqueConstraintsSchema>
@@ -0,0 +1,28 @@
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 { Schema } from 'effect'
9
+
10
+ /**
11
+ * View Fields Schema
12
+ *
13
+ * Array of field names to include in the view.
14
+ * Fields are included in the order specified.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * ['name', 'email', 'created_at']
19
+ * ```
20
+ */
21
+ export const ViewFieldsSchema = Schema.Array(Schema.String).pipe(
22
+ Schema.annotations({
23
+ title: 'View Fields',
24
+ description: 'Array of field names to include in the view.',
25
+ })
26
+ )
27
+
28
+ export type ViewFields = Schema.Schema.Type<typeof ViewFieldsSchema>