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,131 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { type ReactElement, type ReactNode } from 'react'
9
+ import { isCssValue, isTailwindClass } from '@/presentation/styling/style-utils'
10
+ import type {
11
+ ComponentReference,
12
+ SimpleComponentReference,
13
+ } from '@/domain/models/app/component/common/component-reference'
14
+ import type { Component } from '@/domain/models/app/page/sections'
15
+ import type { Theme } from '@/domain/models/app/theme'
16
+
17
+ /**
18
+ * Render a container div with spacing
19
+ *
20
+ * @param testId - Data testid for the container
21
+ * @param value - Spacing value (Tailwind class or CSS value)
22
+ * @returns Container div element
23
+ */
24
+ function renderContainer(testId: string, value: string): ReactElement {
25
+ return (
26
+ <div
27
+ data-testid={testId}
28
+ {...(isTailwindClass(value) ? { className: value } : { style: { maxWidth: value } })}
29
+ />
30
+ )
31
+ }
32
+
33
+ /**
34
+ * Render optional container elements based on theme spacing
35
+ *
36
+ * @param theme - Theme configuration
37
+ * @param sections - Sections to check for existing containers
38
+ * @returns Container elements or undefined
39
+ */
40
+ function renderContainerElements(
41
+ theme: Theme,
42
+ sections: ReadonlyArray<Component | SimpleComponentReference | ComponentReference>
43
+ ): ReactElement | null {
44
+ const hasContainer = sections.some((s) => 'type' in s && s.type === 'container')
45
+
46
+ return (
47
+ <>
48
+ {theme?.spacing?.container &&
49
+ !hasContainer &&
50
+ renderContainer('container', theme.spacing.container)}
51
+
52
+ {(() => {
53
+ const containerSmall = (theme?.spacing as Record<string, unknown>)?.['container-small']
54
+ if (typeof containerSmall === 'string') {
55
+ return renderContainer('container-small', containerSmall)
56
+ }
57
+ return undefined
58
+ })()}
59
+
60
+ {(() => {
61
+ const containerXSmall = (theme?.spacing as Record<string, unknown>)?.['container-xsmall']
62
+ if (typeof containerXSmall === 'string') {
63
+ return renderContainer('container-xsmall', containerXSmall)
64
+ }
65
+ return undefined
66
+ })()}
67
+ </>
68
+ )
69
+ }
70
+
71
+ /**
72
+ * Wrap sections in a section element if needed
73
+ *
74
+ * @param sections - Sections to check
75
+ * @param theme - Theme configuration
76
+ * @param children - Content to wrap
77
+ * @returns Section-wrapped content or fragment with children
78
+ */
79
+ export function wrapWithSectionIfNeeded(
80
+ sections: ReadonlyArray<Component | SimpleComponentReference | ComponentReference>,
81
+ theme: Theme | undefined,
82
+ children: ReactNode
83
+ ): ReactElement {
84
+ const hasSection = sections.some((s) => 'type' in s && s.type === 'section')
85
+ const sectionSpacing = theme?.spacing?.section
86
+ const shouldWrap = !hasSection && sectionSpacing
87
+
88
+ if (!shouldWrap || !sectionSpacing) {
89
+ // eslint-disable-next-line react/jsx-no-useless-fragment -- Required to return ReactElement
90
+ return <>{children}</>
91
+ }
92
+
93
+ const useTailwind = !isCssValue(sectionSpacing)
94
+
95
+ return (
96
+ <section
97
+ data-testid="section"
98
+ {...(useTailwind && { className: sectionSpacing })}
99
+ {...(!useTailwind && { style: { padding: sectionSpacing } })}
100
+ >
101
+ {children}
102
+ </section>
103
+ )
104
+ }
105
+
106
+ /**
107
+ * Render complete section with spacing wrappers
108
+ *
109
+ * @param theme - Theme configuration
110
+ * @param sections - Sections to render
111
+ * @param children - Pre-rendered section content
112
+ * @returns Section with spacing applied
113
+ */
114
+ export function renderSectionWithSpacing(
115
+ theme: Theme | undefined,
116
+ sections: ReadonlyArray<Component | SimpleComponentReference | ComponentReference>,
117
+ children: ReactNode
118
+ ): ReactElement {
119
+ // No theme spacing - render directly
120
+ if (!theme?.spacing?.section && !theme?.spacing?.container) {
121
+ // eslint-disable-next-line react/jsx-no-useless-fragment -- Required to return ReactElement
122
+ return <>{children}</>
123
+ }
124
+
125
+ return (
126
+ <>
127
+ {wrapWithSectionIfNeeded(sections, theme, children)}
128
+ {theme && renderContainerElements(theme, sections)}
129
+ </>
130
+ )
131
+ }
@@ -0,0 +1,426 @@
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
+ /* eslint-disable max-lines, max-lines-per-function, max-statements -- Complex component renderer with many helpers */
9
+
10
+ import { type ReactElement, Fragment, useId } from 'react'
11
+ import { useBreakpoint } from '@/presentation/hooks/use-breakpoint'
12
+ import {
13
+ StructuredDataFromComponent,
14
+ type ComponentMeta,
15
+ } from '@/presentation/ui/metadata/structured-data-from-component'
16
+ import {
17
+ extractComponentReference,
18
+ renderComponentReferenceError,
19
+ } from './components/component-reference-handler'
20
+ import { resolveComponent } from './components/component-resolution'
21
+ import { buildComponentProps } from './props/component-builder'
22
+ import { dispatchComponentType } from './rendering/component-type-dispatcher'
23
+ import { buildResponsiveChildrenVariants } from './responsive/responsive-children-builder'
24
+ import { buildResponsiveContentVariants } from './responsive/responsive-content-builder'
25
+ import { mergeResponsiveProps } from './responsive/responsive-props-merger'
26
+ import { buildInteractionProps } from './styling/interaction-props-builder'
27
+ import { resolveI18nContent } from './translations/i18n-content-resolver'
28
+ import { resolveChildTranslation } from './translations/translation-handler'
29
+ import {
30
+ substituteVariableValues,
31
+ substitutePropsVariables,
32
+ substituteChildrenVariables,
33
+ } from './translations/variable-substitution'
34
+ import type {
35
+ ComponentReference,
36
+ SimpleComponentReference,
37
+ } from '@/domain/models/app/component/common/component-reference'
38
+ import type { Components } from '@/domain/models/app/components'
39
+ import type { Languages } from '@/domain/models/app/languages'
40
+ import type { VariantOverrides } from '@/domain/models/app/page/common/responsive'
41
+ import type { Component } from '@/domain/models/app/page/sections'
42
+ import type { Theme } from '@/domain/models/app/theme'
43
+
44
+ /**
45
+ * Component renderer props
46
+ */
47
+ type ComponentRendererProps = {
48
+ readonly component: Component | SimpleComponentReference | ComponentReference
49
+ readonly pageVars?: Record<string, string | number | boolean>
50
+ readonly componentName?: string
51
+ readonly componentInstanceIndex?: number
52
+ readonly components?: Components
53
+ readonly theme?: Theme
54
+ readonly languages?: Languages
55
+ readonly currentLang?: string
56
+ readonly childIndex?: number
57
+ }
58
+
59
+ /**
60
+ * Handles component reference resolution and rendering
61
+ *
62
+ * @param component - Component reference
63
+ * @param props - Component renderer props
64
+ * @returns Rendered component reference or error
65
+ */
66
+ function renderComponentReference(
67
+ component: SimpleComponentReference | ComponentReference,
68
+ props: ComponentRendererProps
69
+ ): ReactElement | null {
70
+ const { refName, vars } = extractComponentReference(component)
71
+ const resolved = resolveComponent(refName, props.components, vars)
72
+
73
+ if (!resolved) {
74
+ return renderComponentReferenceError({ refName, components: props.components })
75
+ }
76
+
77
+ return (
78
+ <ComponentRenderer
79
+ component={resolved.component}
80
+ pageVars={props.pageVars}
81
+ componentName={resolved.name}
82
+ componentInstanceIndex={props.componentInstanceIndex}
83
+ components={props.components}
84
+ theme={props.theme}
85
+ languages={props.languages}
86
+ currentLang={props.currentLang}
87
+ />
88
+ )
89
+ }
90
+
91
+ /**
92
+ * Renders children recursively
93
+ *
94
+ * @param children - Child components or strings
95
+ * @param props - Component renderer props
96
+ * @returns Rendered children elements (cast to ReactElement[] for compatibility)
97
+ */
98
+ function renderChildren(
99
+ children: ReadonlyArray<Component | string> | undefined,
100
+ props: ComponentRendererProps
101
+ ): readonly ReactElement[] {
102
+ if (!children) return []
103
+
104
+ return children.map((child: Component | string, index: number) =>
105
+ typeof child === 'string' ? (
106
+ resolveChildTranslation(child, props.currentLang, props.languages)
107
+ ) : (
108
+ <ComponentRenderer
109
+ key={index}
110
+ component={child}
111
+ pageVars={props.pageVars}
112
+ components={props.components}
113
+ theme={props.theme}
114
+ languages={props.languages}
115
+ currentLang={props.currentLang}
116
+ childIndex={index}
117
+ />
118
+ )
119
+ ) as ReactElement[]
120
+ }
121
+
122
+ /**
123
+ * Apply page-level variable substitution to component
124
+ */
125
+ function applyVariableSubstitution(
126
+ component: Component,
127
+ pageVars: ComponentRendererProps['pageVars']
128
+ ): Component {
129
+ if (!pageVars) return component
130
+
131
+ return {
132
+ ...component,
133
+ props: substitutePropsVariables(component.props, pageVars),
134
+ children: substituteChildrenVariables(component.children, pageVars),
135
+ content:
136
+ typeof component.content === 'string'
137
+ ? (substituteVariableValues(component.content, pageVars) as string)
138
+ : component.content,
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Add data-component-type attribute for testing
144
+ */
145
+ function addComponentTypeAttribute(
146
+ elementProps: Record<string, unknown>,
147
+ type: string
148
+ ): Record<string, unknown> {
149
+ return {
150
+ ...elementProps,
151
+ 'data-component-type': type,
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Check if component has responsive content overrides
157
+ */
158
+ function hasResponsiveContentOverrides(responsive: Component['responsive']): boolean {
159
+ return (
160
+ !!responsive &&
161
+ Object.values(responsive).some(
162
+ (override) => (override as VariantOverrides).content !== undefined
163
+ )
164
+ )
165
+ }
166
+
167
+ /**
168
+ * Check if component has responsive children overrides
169
+ */
170
+ function hasResponsiveChildrenOverrides(responsive: Component['responsive']): boolean {
171
+ return (
172
+ !!responsive &&
173
+ Object.values(responsive).some(
174
+ (override) => (override as VariantOverrides).children !== undefined
175
+ )
176
+ )
177
+ }
178
+
179
+ /**
180
+ * Render component with responsive content variants
181
+ */
182
+ function renderWithResponsiveContent(config: {
183
+ responsive: Component['responsive']
184
+ type: string
185
+ finalElementPropsWithType: Record<string, unknown>
186
+ finalElementPropsWithSpacingAndType: Record<string, unknown>
187
+ hoverData?: { styleContent: string }
188
+ }): ReactElement | null {
189
+ const responsiveVariants = buildResponsiveContentVariants(
190
+ config.responsive!,
191
+ config.type,
192
+ config.finalElementPropsWithType,
193
+ config.finalElementPropsWithSpacingAndType
194
+ )
195
+
196
+ if (config.hoverData) {
197
+ return (
198
+ <Fragment>
199
+ <style>{config.hoverData.styleContent}</style>
200
+ {responsiveVariants}
201
+ </Fragment>
202
+ )
203
+ }
204
+
205
+ return responsiveVariants
206
+ }
207
+
208
+ /**
209
+ * Build responsive children with variant rendering
210
+ */
211
+ function buildResponsiveChildren(
212
+ responsive: Component['responsive'],
213
+ baseChildren: readonly ReactElement[],
214
+ props: ComponentRendererProps
215
+ ): readonly ReactElement[] {
216
+ return buildResponsiveChildrenVariants({
217
+ responsive: responsive!,
218
+ baseChildren,
219
+ renderChild: (child, index, breakpoint, additionalClassName) => {
220
+ if (typeof child === 'string') {
221
+ const resolvedText = resolveChildTranslation(child, props.currentLang, props.languages)
222
+ return (
223
+ <span
224
+ key={`${breakpoint}-${index}`}
225
+ className={additionalClassName}
226
+ data-responsive-breakpoint={breakpoint}
227
+ >
228
+ {resolvedText}
229
+ </span>
230
+ )
231
+ }
232
+
233
+ const childWithVisibility: Component = additionalClassName
234
+ ? {
235
+ ...child,
236
+ props: {
237
+ ...(child.props || {}),
238
+ className: (child.props as { className?: string } | undefined)?.className
239
+ ? `${(child.props as { className: string }).className} ${additionalClassName}`
240
+ : additionalClassName,
241
+ } as Record<string, unknown>,
242
+ }
243
+ : child
244
+
245
+ return (
246
+ <ComponentRenderer
247
+ key={`${breakpoint}-${index}`}
248
+ component={childWithVisibility}
249
+ pageVars={props.pageVars}
250
+ components={props.components}
251
+ theme={props.theme}
252
+ languages={props.languages}
253
+ currentLang={props.currentLang}
254
+ childIndex={index}
255
+ />
256
+ )
257
+ },
258
+ })
259
+ }
260
+
261
+ /**
262
+ * Renders direct component (non-reference)
263
+ *
264
+ * This is a React component (not a helper function) because it uses the useId hook.
265
+ * React components must start with an uppercase letter.
266
+ *
267
+ * @param component - Direct component
268
+ * @param props - Component renderer props
269
+ * @returns Rendered component
270
+ */
271
+ function RenderDirectComponent({
272
+ component,
273
+ props,
274
+ }: {
275
+ component: Component
276
+ props: ComponentRendererProps
277
+ }): ReactElement | null {
278
+ // Apply page-level variable substitution if pageVars are provided
279
+ const substitutedComponent = applyVariableSubstitution(component, props.pageVars)
280
+
281
+ const {
282
+ type,
283
+ props: componentProps,
284
+ children,
285
+ content,
286
+ interactions,
287
+ i18n,
288
+ responsive,
289
+ } = substitutedComponent
290
+ const uniqueId = useId()
291
+ const currentBreakpoint = useBreakpoint()
292
+
293
+ // Merge responsive props using extracted module
294
+ const {
295
+ mergedProps: mergedPropsWithVisibility,
296
+ mergedChildren,
297
+ mergedContent,
298
+ } = mergeResponsiveProps(responsive, componentProps, children, content, currentBreakpoint)
299
+
300
+ const { elementProps, elementPropsWithSpacing } = buildComponentProps({
301
+ type,
302
+ props: mergedPropsWithVisibility,
303
+ children: mergedChildren,
304
+ content: typeof mergedContent === 'string' ? mergedContent : undefined,
305
+ componentName: props.componentName,
306
+ componentInstanceIndex: props.componentInstanceIndex,
307
+ theme: props.theme,
308
+ languages: props.languages,
309
+ currentLang: props.currentLang,
310
+ childIndex: props.childIndex,
311
+ interactions,
312
+ })
313
+
314
+ // Build interaction props using extracted module
315
+ const {
316
+ finalElementProps: interactionElementProps,
317
+ finalElementPropsWithSpacing: interactionElementPropsWithSpacing,
318
+ hoverData,
319
+ } = buildInteractionProps(interactions, uniqueId, elementProps, elementPropsWithSpacing)
320
+
321
+ const baseRenderedChildren = renderChildren(mergedChildren, props)
322
+
323
+ // Resolve i18n content using extracted module
324
+ const { resolvedContent, finalElementProps, finalElementPropsWithSpacing } = resolveI18nContent(
325
+ mergedContent,
326
+ i18n,
327
+ props.currentLang,
328
+ props.languages,
329
+ interactionElementProps,
330
+ interactionElementPropsWithSpacing
331
+ )
332
+
333
+ // Add data-component-type attribute for testing
334
+ const finalElementPropsWithType = addComponentTypeAttribute(finalElementProps, type)
335
+ const finalElementPropsWithSpacingAndType = addComponentTypeAttribute(
336
+ finalElementPropsWithSpacing,
337
+ type
338
+ )
339
+
340
+ // Check if component has meta property with structured data
341
+ const meta = componentProps?.meta as ComponentMeta | undefined
342
+ const structuredDataScript = meta ? <StructuredDataFromComponent meta={meta} /> : undefined
343
+
344
+ // Check if component has responsive content overrides
345
+ if (hasResponsiveContentOverrides(responsive)) {
346
+ return renderWithResponsiveContent({
347
+ responsive,
348
+ type,
349
+ finalElementPropsWithType,
350
+ finalElementPropsWithSpacingAndType,
351
+ hoverData,
352
+ })
353
+ }
354
+
355
+ // Check if component has responsive children overrides
356
+ const finalRenderedChildren = hasResponsiveChildrenOverrides(responsive)
357
+ ? buildResponsiveChildren(responsive, baseRenderedChildren, props)
358
+ : baseRenderedChildren
359
+
360
+ // Inject structured data script as first child if it exists
361
+ const finalChildren = structuredDataScript
362
+ ? ([structuredDataScript, ...finalRenderedChildren] as readonly ReactElement[])
363
+ : finalRenderedChildren
364
+
365
+ // Default rendering without responsive content
366
+ const renderedComponent = dispatchComponentType({
367
+ type,
368
+ elementProps: finalElementPropsWithType,
369
+ elementPropsWithSpacing: finalElementPropsWithSpacingAndType,
370
+ content:
371
+ resolvedContent !== undefined
372
+ ? resolvedContent
373
+ : typeof mergedContent === 'string'
374
+ ? mergedContent
375
+ : undefined,
376
+ renderedChildren: finalChildren,
377
+ theme: props.theme,
378
+ languages: props.languages,
379
+ interactions,
380
+ })
381
+
382
+ if (hoverData) {
383
+ return (
384
+ <Fragment>
385
+ <style>{hoverData.styleContent}</style>
386
+ {renderedComponent}
387
+ </Fragment>
388
+ )
389
+ }
390
+
391
+ return renderedComponent
392
+ }
393
+
394
+ /**
395
+ * ComponentRenderer - Renders a dynamic component based on its type
396
+ *
397
+ * This component handles the recursive rendering of sections, converting
398
+ * the declarative component configuration into React elements.
399
+ * Supports component references for reusable components and theme token substitution.
400
+ *
401
+ * @param props - Component props
402
+ * @param props.component - Component configuration from sections schema (can be a direct component or component reference)
403
+ * @param props.componentName - Optional component template name for data-component attribute
404
+ * @param props.componentInstanceIndex - Optional instance index for components used multiple times (for unique data-testid)
405
+ * @param props.components - Optional components array for resolving component references
406
+ * @param props.theme - Optional theme configuration for token substitution
407
+ * @param props.languages - Optional languages configuration for language-switcher components
408
+ * @param props.currentLang - Current page language (defaults to languages.default)
409
+ * @returns React element matching the component type
410
+ */
411
+ export function ComponentRenderer(props: ComponentRendererProps): Readonly<ReactElement | null> {
412
+ const { component } = props
413
+
414
+ // Handle component references - supports both { component: 'name' } and { $ref: 'name' } syntaxes
415
+ if ('component' in component || '$ref' in component) {
416
+ return renderComponentReference(component, props)
417
+ }
418
+
419
+ // Direct component rendering
420
+ return (
421
+ <RenderDirectComponent
422
+ component={component as Component}
423
+ props={props}
424
+ />
425
+ )
426
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import type {
9
+ ComponentReference,
10
+ SimpleComponentReference,
11
+ } from '@/domain/models/app/component/common/component-reference'
12
+ import type { Components } from '@/domain/models/app/components'
13
+ import type { Languages } from '@/domain/models/app/languages'
14
+ import type { Component } from '@/domain/models/app/page/sections'
15
+ import type { Theme } from '@/domain/models/app/theme'
16
+
17
+ /**
18
+ * ComponentRenderer Props
19
+ */
20
+ export type ComponentRendererProps = {
21
+ readonly component: Component | SimpleComponentReference | ComponentReference
22
+ readonly componentName?: string
23
+ readonly componentInstanceIndex?: number
24
+ readonly components?: Components
25
+ readonly theme?: Theme
26
+ readonly languages?: Languages
27
+ readonly currentLang?: string
28
+ }
29
+
30
+ /**
31
+ * Component types that should receive role="group" when used as component references with children
32
+ */
33
+ export const CONTAINER_TYPES = ['div', 'container', 'flex', 'grid', 'card', 'badge'] as const
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import type {
9
+ ComponentReference,
10
+ SimpleComponentReference,
11
+ } from '@/domain/models/app/component/common/component-reference'
12
+ import type { Components } from '@/domain/models/app/components'
13
+ import type { Languages } from '@/domain/models/app/languages'
14
+ import type { Component } from '@/domain/models/app/page/sections'
15
+ import type { Theme } from '@/domain/models/app/theme'
16
+ import type { ReactElement } from 'react'
17
+
18
+ /**
19
+ * Props for rendering a component reference error
20
+ */
21
+ interface ComponentReferenceErrorProps {
22
+ readonly refName: string
23
+ readonly components: Components | undefined
24
+ }
25
+
26
+ /**
27
+ * Render an error message for missing component references
28
+ */
29
+ export function renderComponentReferenceError({
30
+ refName,
31
+ components,
32
+ }: ComponentReferenceErrorProps): ReactElement {
33
+ return (
34
+ <div
35
+ style={{
36
+ padding: '1rem',
37
+ border: '2px dashed red',
38
+ color: 'red',
39
+ fontFamily: 'monospace',
40
+ }}
41
+ >
42
+ Component not found: &quot;{refName}&quot;
43
+ <br />
44
+ <small>Available components: {components?.map((b) => b.name).join(', ') || 'none'}</small>
45
+ </div>
46
+ )
47
+ }
48
+
49
+ /**
50
+ * Extract reference name and vars from component reference
51
+ */
52
+ export function extractComponentReference(
53
+ component: SimpleComponentReference | ComponentReference
54
+ ): {
55
+ refName: string
56
+ vars: Record<string, string | number | boolean> | undefined
57
+ } {
58
+ const refName = 'component' in component ? component.component : component.$ref
59
+ const vars = 'vars' in component ? component.vars : undefined
60
+ return { refName, vars }
61
+ }
62
+
63
+ /**
64
+ * Props for component reference rendering
65
+ */
66
+ export interface ComponentReferenceRenderProps {
67
+ readonly component: Component
68
+ readonly componentName: string
69
+ readonly componentInstanceIndex: number | undefined
70
+ readonly components: Components | undefined
71
+ readonly theme: Theme | undefined
72
+ readonly languages: Languages | undefined
73
+ readonly currentLang: string | undefined
74
+ }