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,158 @@
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 { hasPermission } from '@/domain/models/app/table/permissions'
10
+ import { ValidationError, PermissionError, ValidationContext } from '../../middleware/validation'
11
+
12
+ /**
13
+ * Validate that 'id' field is not in the request (readonly)
14
+ */
15
+ export function validateReadonlyIdField(
16
+ fields: Record<string, unknown>
17
+ ): Effect.Effect<void, ValidationError, never> {
18
+ if ('id' in fields) {
19
+ return Effect.fail(new ValidationError("Cannot write to readonly field 'id'", 'id'))
20
+ }
21
+ return Effect.void
22
+ }
23
+
24
+ /**
25
+ * Validate that fields with default values are not in the request (system-managed)
26
+ */
27
+ export function validateDefaultFields(
28
+ fields: Record<string, unknown>
29
+ ): Effect.Effect<void, ValidationError, ValidationContext> {
30
+ return Effect.gen(function* () {
31
+ const ctx = yield* ValidationContext
32
+ const table = ctx.app.tables?.find((t) => t.name === ctx.tableName)
33
+
34
+ const fieldsWithDefaults =
35
+ table?.fields?.filter((f) => 'default' in f && f.default !== undefined) ?? []
36
+
37
+ const attemptedDefaultField = fieldsWithDefaults.find((f) => f.name in fields)
38
+
39
+ if (attemptedDefaultField) {
40
+ return yield* Effect.fail(
41
+ new ValidationError(
42
+ `Cannot write to readonly field '${attemptedDefaultField.name}'`,
43
+ attemptedDefaultField.name
44
+ )
45
+ )
46
+ }
47
+ })
48
+ }
49
+
50
+ /**
51
+ * Validate required fields are present
52
+ */
53
+ export function validateRequiredFields(
54
+ fields: Record<string, unknown>
55
+ ): Effect.Effect<void, ValidationError, ValidationContext> {
56
+ return Effect.gen(function* () {
57
+ const ctx = yield* ValidationContext
58
+ const table = ctx.app.tables?.find((t) => t.name === ctx.tableName)
59
+
60
+ if (!table) return
61
+
62
+ // Get primary key field names to exclude from validation
63
+ const primaryKeyFields = new Set(
64
+ table.primaryKey?.fields ?? (table.primaryKey?.field ? [table.primaryKey.field] : [])
65
+ )
66
+
67
+ // Auto-injected fields that should be excluded from required field validation
68
+ const autoInjectedFields = new Set<string>([])
69
+
70
+ const missingRequiredFields = table.fields
71
+ .filter(
72
+ (field) =>
73
+ field.required &&
74
+ !(field.name in fields) &&
75
+ !primaryKeyFields.has(field.name) &&
76
+ !autoInjectedFields.has(field.name)
77
+ )
78
+ .map((field) => field.name)
79
+
80
+ if (missingRequiredFields.length > 0) {
81
+ return yield* Effect.fail(
82
+ new ValidationError(
83
+ 'Missing required fields',
84
+ missingRequiredFields[0] // Report first missing field
85
+ )
86
+ )
87
+ }
88
+ })
89
+ }
90
+
91
+ /**
92
+ * Check if a field permission restricts writing based on user role
93
+ */
94
+ function hasWriteRoleRestriction(
95
+ fieldPermission: { write?: 'all' | 'authenticated' | readonly string[] } | null | undefined,
96
+ userRole: string
97
+ ): boolean {
98
+ const writePermission = fieldPermission?.write
99
+ if (writePermission === undefined) return false
100
+ return !hasPermission(writePermission, userRole)
101
+ }
102
+
103
+ /**
104
+ * Filter fields based on write permissions
105
+ * Returns only fields the user is allowed to write
106
+ */
107
+ export function filterAllowedFields(
108
+ fields: Record<string, unknown>
109
+ ): Effect.Effect<
110
+ { allowedData: Record<string, unknown>; forbiddenFields: readonly string[] },
111
+ never,
112
+ ValidationContext
113
+ > {
114
+ return Effect.gen(function* () {
115
+ const ctx = yield* ValidationContext
116
+ const table = ctx.app.tables?.find((t) => t.name === ctx.tableName)
117
+
118
+ // System-protected fields that cannot be modified
119
+ const SYSTEM_PROTECTED_FIELDS = new Set(['user_id'])
120
+
121
+ // Get forbidden fields based on field-level permissions (functional filter pattern)
122
+ const forbiddenFields: readonly string[] = Object.keys(fields).filter((fieldName) => {
123
+ const field = table?.fields?.find((f) => f.name === fieldName)
124
+ if (!field) return false
125
+
126
+ const fieldPermission = table?.permissions?.fields?.find((fp) => fp.field === fieldName)
127
+ return hasWriteRoleRestriction(fieldPermission, ctx.userRole)
128
+ })
129
+
130
+ // Filter out forbidden and system-protected fields
131
+ const allowedData = Object.fromEntries(
132
+ Object.entries(fields).filter(
133
+ ([fieldName]) =>
134
+ !forbiddenFields.includes(fieldName) && !SYSTEM_PROTECTED_FIELDS.has(fieldName)
135
+ )
136
+ )
137
+
138
+ return { allowedData, forbiddenFields }
139
+ })
140
+ }
141
+
142
+ /**
143
+ * Validate field write permissions - fails if any forbidden fields found
144
+ */
145
+ export function validateFieldWritePermissions(
146
+ forbiddenFields: readonly string[]
147
+ ): Effect.Effect<void, PermissionError, never> {
148
+ if (forbiddenFields.length > 0) {
149
+ const firstForbiddenField = forbiddenFields[0]
150
+ return Effect.fail(
151
+ new PermissionError(
152
+ `Cannot write to field '${firstForbiddenField}': insufficient permissions`,
153
+ firstForbiddenField
154
+ )
155
+ )
156
+ }
157
+ return Effect.void
158
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { Effect } from 'effect'
9
+ import {
10
+ validateReadonlyIdField,
11
+ validateDefaultFields,
12
+ validateRequiredFields,
13
+ filterAllowedFields,
14
+ validateFieldWritePermissions,
15
+ } from './field-rules'
16
+ import type {
17
+ ValidationError,
18
+ PermissionError,
19
+ ValidationContext,
20
+ } from '../../middleware/validation'
21
+
22
+ /**
23
+ * Validate fields for record creation
24
+ * Composes all field-level validations
25
+ */
26
+ export function validateRecordCreation(
27
+ requestedFields: Record<string, unknown>
28
+ ): Effect.Effect<Record<string, unknown>, ValidationError | PermissionError, ValidationContext> {
29
+ return Effect.gen(function* () {
30
+ // Step 1: Check readonly 'id' field
31
+ yield* validateReadonlyIdField(requestedFields)
32
+
33
+ // Step 2: Check fields with default values
34
+ yield* validateDefaultFields(requestedFields)
35
+
36
+ // Step 3: Filter fields based on write permissions
37
+ const { allowedData, forbiddenFields } = yield* filterAllowedFields(requestedFields)
38
+
39
+ // Step 4: Check for forbidden fields
40
+ yield* validateFieldWritePermissions(forbiddenFields)
41
+
42
+ // Step 5: Validate required fields (on allowed data)
43
+ yield* validateRequiredFields(allowedData)
44
+
45
+ return allowedData
46
+ })
47
+ }
48
+
49
+ /**
50
+ * Validate fields for record update
51
+ * Similar to creation but without required field validation
52
+ */
53
+ export function validateRecordUpdate(
54
+ requestedFields: Record<string, unknown>
55
+ ): Effect.Effect<Record<string, unknown>, ValidationError | PermissionError, ValidationContext> {
56
+ return Effect.gen(function* () {
57
+ // Step 1: Check readonly 'id' field
58
+ yield* validateReadonlyIdField(requestedFields)
59
+
60
+ // Step 2: Check fields with default values
61
+ yield* validateDefaultFields(requestedFields)
62
+
63
+ // Step 3: Filter fields based on write permissions
64
+ const { allowedData, forbiddenFields } = yield* filterAllowedFields(requestedFields)
65
+
66
+ // Step 4: Check for forbidden fields
67
+ yield* validateFieldWritePermissions(forbiddenFields)
68
+
69
+ // Note: No required field validation for updates (partial updates allowed)
70
+
71
+ return allowedData
72
+ })
73
+ }
@@ -0,0 +1,19 @@
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
+ * CLI Presentation Module
10
+ *
11
+ * CLI-specific utilities with user-facing error handling.
12
+ */
13
+
14
+ export {
15
+ loadSchemaFromFile,
16
+ loadSchemaFromFileForReload,
17
+ parseSchemaFromEnv,
18
+ parseAppSchema,
19
+ } from './schema-loader'
@@ -0,0 +1,172 @@
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
+ * CLI Schema Loader - Presentation Layer
10
+ *
11
+ * CLI-specific schema loading with user-facing error messages and process.exit.
12
+ * Orchestrates domain and infrastructure layers.
13
+ */
14
+
15
+ import { Effect, Console } from 'effect'
16
+ import {
17
+ detectFormat,
18
+ getFileExtension,
19
+ isInlineJson,
20
+ isUrl,
21
+ parseJsonContent,
22
+ parseYamlContent,
23
+ } from '@/domain/utils'
24
+ import {
25
+ loadSchemaFromFile as loadFromFile,
26
+ fileExists,
27
+ fetchRemoteSchema,
28
+ } from '@/infrastructure/schema'
29
+ import type { AppEncoded } from '@/domain/models/app'
30
+
31
+ /**
32
+ * Load schema from file with CLI error handling (calls process.exit on error)
33
+ */
34
+ export const loadSchemaFromFile = async (
35
+ filePath: string,
36
+ command: string
37
+ ): Promise<AppEncoded> => {
38
+ const exists = await fileExists(filePath)
39
+
40
+ if (!exists) {
41
+ Effect.runSync(
42
+ Effect.gen(function* () {
43
+ yield* Console.error(`Error: File not found: ${filePath}`)
44
+ yield* Console.error('')
45
+ yield* Console.error('Usage:')
46
+ yield* Console.error(` sovrium ${command} <config.json>`)
47
+ })
48
+ )
49
+ // eslint-disable-next-line functional/no-expression-statements
50
+ process.exit(1)
51
+ }
52
+
53
+ const format = detectFormat(filePath)
54
+
55
+ if (format === 'unsupported') {
56
+ Effect.runSync(
57
+ Effect.gen(function* () {
58
+ yield* Console.error(`Error: Unsupported file format: .${getFileExtension(filePath)}`)
59
+ yield* Console.error('')
60
+ yield* Console.error('Supported formats: .json, .yaml, .yml')
61
+ })
62
+ )
63
+ // eslint-disable-next-line functional/no-expression-statements
64
+ process.exit(1)
65
+ }
66
+
67
+ try {
68
+ return await loadFromFile(filePath)
69
+ } catch (error) {
70
+ Effect.runSync(
71
+ Effect.gen(function* () {
72
+ yield* Console.error(
73
+ `Error: Failed to parse ${format === 'json' ? 'JSON' : 'YAML'} file: ${filePath}`
74
+ )
75
+ yield* Console.error('')
76
+ yield* Console.error('Details:', error instanceof Error ? error.message : String(error))
77
+ })
78
+ )
79
+ // eslint-disable-next-line functional/no-expression-statements
80
+ process.exit(1)
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Load schema from file for watch mode reloads (throws instead of process.exit)
86
+ */
87
+ export const loadSchemaFromFileForReload = async (filePath: string): Promise<AppEncoded> =>
88
+ loadFromFile(filePath)
89
+
90
+ /**
91
+ * Parse schema from environment variable value
92
+ * Supports: inline JSON, inline YAML, remote URL
93
+ *
94
+ * @throws Error if parsing fails
95
+ */
96
+ export const parseSchemaFromEnv = async (envValue: string): Promise<AppEncoded> => {
97
+ const trimmedValue = envValue.trim()
98
+
99
+ // Detect if value is inline JSON
100
+ if (isInlineJson(trimmedValue)) {
101
+ try {
102
+ return parseJsonContent(trimmedValue)
103
+ } catch (error) {
104
+ // eslint-disable-next-line functional/no-throw-statements
105
+ throw new Error(
106
+ `Invalid JSON in APP_SCHEMA: ${error instanceof Error ? error.message : String(error)}`
107
+ )
108
+ }
109
+ }
110
+
111
+ // Detect if value is a URL
112
+ if (isUrl(trimmedValue)) {
113
+ return fetchRemoteSchema(trimmedValue)
114
+ }
115
+
116
+ // Otherwise, treat as YAML
117
+ try {
118
+ return parseYamlContent(trimmedValue)
119
+ } catch (error) {
120
+ // eslint-disable-next-line functional/no-throw-statements
121
+ throw new Error(
122
+ `Invalid YAML in APP_SCHEMA: ${error instanceof Error ? error.message : String(error)}`
123
+ )
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Show error message when no configuration is provided
129
+ */
130
+ const showNoConfigError = (command: string): never => {
131
+ Effect.runSync(
132
+ Effect.gen(function* () {
133
+ yield* Console.error('Error: No configuration provided')
134
+ yield* Console.error('')
135
+ yield* Console.error('Usage:')
136
+ yield* Console.error(` sovrium ${command} <config.json>`)
137
+ yield* Console.error('')
138
+ yield* Console.error('Or with environment variable:')
139
+ yield* Console.error(` APP_SCHEMA='{"name":"My App"}' sovrium ${command}`)
140
+ })
141
+ )
142
+ // eslint-disable-next-line functional/no-expression-statements
143
+ process.exit(1)
144
+ }
145
+
146
+ /**
147
+ * Parse and validate app schema from file path or environment variable
148
+ */
149
+ export const parseAppSchema = async (command: string, filePath?: string): Promise<AppEncoded> => {
150
+ // If a file path is provided, load from file (takes precedence over env)
151
+ if (filePath) {
152
+ return loadSchemaFromFile(filePath, command)
153
+ }
154
+
155
+ // Try APP_SCHEMA environment variable
156
+ const appSchemaEnv = Bun.env.APP_SCHEMA
157
+
158
+ if (appSchemaEnv) {
159
+ try {
160
+ return await parseSchemaFromEnv(appSchemaEnv)
161
+ } catch (error) {
162
+ Effect.runSync(
163
+ Console.error(`Error: ${error instanceof Error ? error.message : String(error)}`)
164
+ )
165
+ // eslint-disable-next-line functional/no-expression-statements
166
+ process.exit(1)
167
+ }
168
+ }
169
+
170
+ // No configuration provided
171
+ return showNoConfigError(command)
172
+ }
@@ -0,0 +1,155 @@
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 { useSyncExternalStore } from 'react'
9
+
10
+ /**
11
+ * Breakpoint type matching responsive schema
12
+ */
13
+ export type Breakpoint = 'mobile' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
14
+
15
+ /**
16
+ * Tailwind default breakpoints (min-width)
17
+ */
18
+ const BREAKPOINTS: Record<Exclude<Breakpoint, 'mobile'>, number> = {
19
+ sm: 640,
20
+ md: 768,
21
+ lg: 1024,
22
+ xl: 1280,
23
+ '2xl': 1536,
24
+ }
25
+
26
+ /**
27
+ * Gets current viewport width (works in both normal browsers and test environments)
28
+ *
29
+ * Prioritizes document.documentElement.clientWidth for Playwright compatibility
30
+ *
31
+ * @returns Viewport width in pixels
32
+ */
33
+ function getViewportWidth(): number {
34
+ // Use document.documentElement.clientWidth first (most reliable in Playwright)
35
+ if (typeof document !== 'undefined' && document.documentElement) {
36
+ return document.documentElement.clientWidth
37
+ }
38
+ // Try visualViewport (reliable in some browsers/test environments)
39
+ if (typeof window !== 'undefined' && window.visualViewport?.width) {
40
+ return window.visualViewport.width
41
+ }
42
+ // Fallback to innerWidth
43
+ if (typeof window !== 'undefined' && window.innerWidth) {
44
+ return window.innerWidth
45
+ }
46
+ // Last resort fallback
47
+ return 0
48
+ }
49
+
50
+ /**
51
+ * Determines current breakpoint from window width
52
+ *
53
+ * @param width - Window width in pixels
54
+ * @returns Current breakpoint name
55
+ */
56
+ function getBreakpoint(width: number): Breakpoint {
57
+ if (width >= BREAKPOINTS['2xl']) return '2xl'
58
+ if (width >= BREAKPOINTS.xl) return 'xl'
59
+ if (width >= BREAKPOINTS.lg) return 'lg'
60
+ if (width >= BREAKPOINTS.md) return 'md'
61
+ if (width >= BREAKPOINTS.sm) return 'sm'
62
+ return 'mobile'
63
+ }
64
+
65
+ /**
66
+ * Subscribe to viewport breakpoint changes
67
+ *
68
+ * @param callback - Callback to invoke when breakpoint changes
69
+ * @returns Unsubscribe function
70
+ */
71
+ function subscribe(callback: () => void): () => void {
72
+ if (typeof window === 'undefined') return () => {}
73
+
74
+ // Call callback immediately to ensure we have the latest viewport state
75
+ // This is important for detecting viewport changes that happened before subscription
76
+ callback()
77
+
78
+ // Listen to resize events
79
+ window.addEventListener('resize', callback)
80
+
81
+ // Create media query listeners for each breakpoint
82
+ const mediaQueries = [
83
+ window.matchMedia(`(min-width: ${BREAKPOINTS.sm}px)`),
84
+ window.matchMedia(`(min-width: ${BREAKPOINTS.md}px)`),
85
+ window.matchMedia(`(min-width: ${BREAKPOINTS.lg}px)`),
86
+ window.matchMedia(`(min-width: ${BREAKPOINTS.xl}px)`),
87
+ window.matchMedia(`(min-width: ${BREAKPOINTS['2xl']}px)`),
88
+ ]
89
+
90
+ mediaQueries.forEach((mq) => {
91
+ // Modern API
92
+ if (mq.addEventListener) {
93
+ mq.addEventListener('change', callback)
94
+ } else {
95
+ // Legacy API fallback
96
+ mq.addListener(callback)
97
+ }
98
+ })
99
+
100
+ // Listen to visualViewport resize (more reliable in some environments)
101
+ window.visualViewport?.addEventListener('resize', callback)
102
+
103
+ // Poll for viewport changes (for E2E tests where events might not fire)
104
+ // This ensures responsive behavior works in automated testing environments
105
+ // Using 16ms (one frame at 60fps) for fastest possible detection
106
+ const pollInterval = setInterval(callback, 16)
107
+
108
+ return () => {
109
+ window.removeEventListener('resize', callback)
110
+ mediaQueries.forEach((mq) => {
111
+ if (mq.removeEventListener) {
112
+ mq.removeEventListener('change', callback)
113
+ } else {
114
+ mq.removeListener(callback)
115
+ }
116
+ })
117
+ window.visualViewport?.removeEventListener('resize', callback)
118
+ clearInterval(pollInterval)
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Get current breakpoint snapshot
124
+ *
125
+ * @returns Current breakpoint based on viewport width
126
+ */
127
+ function getSnapshot(): Breakpoint {
128
+ if (typeof window === 'undefined') return 'mobile'
129
+ return getBreakpoint(getViewportWidth())
130
+ }
131
+
132
+ /**
133
+ * Get server-side breakpoint (always mobile for SSR)
134
+ *
135
+ * @returns Mobile breakpoint
136
+ */
137
+ function getServerSnapshot(): Breakpoint {
138
+ return 'mobile'
139
+ }
140
+
141
+ /**
142
+ * Hook to detect current viewport breakpoint
143
+ *
144
+ * Returns the current breakpoint based on window width.
145
+ * Updates automatically when viewport is resized.
146
+ * Uses useSyncExternalStore for reliable external state synchronization.
147
+ *
148
+ * Uses matchMedia to detect breakpoint changes reliably, which works with
149
+ * both user-initiated resizes and programmatic viewport changes (e.g., Playwright's setViewportSize).
150
+ *
151
+ * @returns Current breakpoint name
152
+ */
153
+ export function useBreakpoint(): Breakpoint {
154
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
155
+ }
@@ -0,0 +1,60 @@
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 { renderToString } from 'react-dom/server'
9
+ import { ErrorPage } from '@/presentation/ui/pages/ErrorPage'
10
+ import { NotFoundPage } from '@/presentation/ui/pages/NotFoundPage'
11
+ import { renderPageByPath } from './render-homepage'
12
+ import type { App } from '@/domain/models/app'
13
+
14
+ /**
15
+ * Renders NotFoundPage (404) to HTML string for server-side rendering
16
+ *
17
+ * If the app has a custom page configured at '/404', renders that page.
18
+ * Otherwise, renders the default NotFoundPage.
19
+ *
20
+ * @param app - Optional validated application data from AppSchema
21
+ * @param detectedLanguage - Optional detected language from Accept-Language header
22
+ * @returns Complete HTML document as string with DOCTYPE
23
+ */
24
+ export function renderNotFoundPage(app?: App, detectedLanguage?: string): string {
25
+ // Try to render custom 404 page first if app is provided
26
+ if (app) {
27
+ const custom404 = renderPageByPath(app, '/404', detectedLanguage)
28
+ if (custom404) {
29
+ return custom404
30
+ }
31
+ }
32
+
33
+ // Fallback to default 404 page
34
+ const html = renderToString(<NotFoundPage />)
35
+ return `<!DOCTYPE html>\n${html}`
36
+ }
37
+
38
+ /**
39
+ * Renders ErrorPage (500) to HTML string for server-side rendering
40
+ *
41
+ * If the app has a custom page configured at '/500', renders that page.
42
+ * Otherwise, renders the default ErrorPage.
43
+ *
44
+ * @param app - Optional validated application data from AppSchema
45
+ * @param detectedLanguage - Optional detected language from Accept-Language header
46
+ * @returns Complete HTML document as string with DOCTYPE
47
+ */
48
+ export function renderErrorPage(app?: App, detectedLanguage?: string): string {
49
+ // Try to render custom 500 page first if app is provided
50
+ if (app) {
51
+ const custom500 = renderPageByPath(app, '/500', detectedLanguage)
52
+ if (custom500) {
53
+ return custom500
54
+ }
55
+ }
56
+
57
+ // Fallback to default 500 page
58
+ const html = renderToString(<ErrorPage />)
59
+ return `<!DOCTYPE html>\n${html}`
60
+ }