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,100 @@
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
+ * Helper functions for HTML element rendering
10
+ *
11
+ * This module provides pure functions for building element props and attributes
12
+ * Used by element-renderers.tsx to reduce file size and complexity
13
+ */
14
+
15
+ /**
16
+ * Adds accessibility role based on element type and content
17
+ */
18
+ export function buildAccessibilityRole(
19
+ type: string,
20
+ hasChildren: boolean,
21
+ hasContent: boolean,
22
+ existingRole: unknown
23
+ ): Record<string, unknown> {
24
+ if (type === 'section') {
25
+ return { role: 'region' }
26
+ }
27
+ if (type === 'div' && hasChildren && !hasContent && !existingRole) {
28
+ return { role: 'group' }
29
+ }
30
+ return {}
31
+ }
32
+
33
+ /**
34
+ * Builds scroll interaction data attributes
35
+ */
36
+ export function buildScrollAttributes(interactions: unknown): Record<string, unknown> {
37
+ const scrollInteractions = interactions as
38
+ | {
39
+ scroll?: {
40
+ animation?: string
41
+ threshold?: number
42
+ delay?: string
43
+ duration?: string
44
+ once?: boolean
45
+ }
46
+ }
47
+ | undefined
48
+
49
+ const { scroll } = scrollInteractions ?? {}
50
+ if (!scroll) return {}
51
+
52
+ return {
53
+ 'data-scroll-animation': scroll.animation,
54
+ ...(scroll.threshold !== undefined && { 'data-scroll-threshold': scroll.threshold }),
55
+ ...(scroll.delay && { 'data-scroll-delay': scroll.delay }),
56
+ ...(scroll.duration && { 'data-scroll-duration': scroll.duration }),
57
+ ...(scroll.once === true && { 'data-scroll-once': scroll.once }),
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Extracts animation config from theme
63
+ */
64
+ export function getAnimationConfig(theme: { animations?: unknown } | undefined) {
65
+ const animations = theme?.animations as { fadeIn?: unknown } | undefined
66
+ const fadeInConfig = animations?.fadeIn
67
+ return fadeInConfig && typeof fadeInConfig === 'object' && !Array.isArray(fadeInConfig)
68
+ ? (fadeInConfig as Record<string, unknown>)
69
+ : undefined
70
+ }
71
+
72
+ /**
73
+ * Calculates stagger delay from duration
74
+ */
75
+ export function calculateStaggerDelay(duration: string): number {
76
+ const durationMs = parseInt(duration.replace('ms', ''), 10)
77
+ return Math.max(50, durationMs / 4) // 25% of duration, min 50ms
78
+ }
79
+
80
+ /**
81
+ * Builds alert variant styles from theme colors
82
+ */
83
+ export function buildAlertVariantStyles(
84
+ variant: string | undefined,
85
+ theme: { colors?: Record<string, unknown> } | undefined
86
+ ): Record<string, unknown> {
87
+ if (!variant || !theme?.colors) {
88
+ return {}
89
+ }
90
+
91
+ const colorKey = variant as keyof typeof theme.colors
92
+ const lightColorKey = `${variant}-light` as keyof typeof theme.colors
93
+ const color = theme.colors[colorKey] as string | undefined
94
+ const lightColor = theme.colors[lightColorKey] as string | undefined
95
+
96
+ return {
97
+ ...(color && { color, borderColor: color }),
98
+ ...(lightColor && { backgroundColor: lightColor }),
99
+ }
100
+ }
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import DOMPurify from 'dompurify'
9
+ import { type ReactElement } from 'react'
10
+ import { type Languages } from '@/domain/models/app/languages'
11
+ import { type Theme } from '@/domain/models/app/theme'
12
+ import { LanguageSwitcher } from '@/presentation/ui/languages/language-switcher'
13
+ import {
14
+ getAnimationConfig,
15
+ calculateStaggerDelay,
16
+ buildAlertVariantStyles,
17
+ } from './html-element-helpers'
18
+ import type { ElementProps } from './element-renderers'
19
+
20
+ /**
21
+ * Renders language switcher component
22
+ *
23
+ * This is a special component that requires languages configuration from app schema.
24
+ * If languages is not provided, renders a warning message.
25
+ *
26
+ * Note: Languages are already validated at server startup via AppSchema validation.
27
+ * No need to re-validate here since the data comes from the validated app config.
28
+ *
29
+ * Props:
30
+ * - variant: Display variant (dropdown, inline, tabs) - defaults to dropdown
31
+ * - showFlags: Whether to show flag emojis - defaults to false
32
+ * - position: Position on page (top-right, header, footer, sidebar)
33
+ */
34
+ export function renderLanguageSwitcher(props: ElementProps, languages?: Languages): ReactElement {
35
+ if (!languages) {
36
+ // DEVELOPMENT WARNING: Keep console.warn for development debugging
37
+ // This warning alerts developers when language-switcher component is used without languages config
38
+ // Safe to keep - helps identify configuration issues during development
39
+ console.warn('language-switcher component requires languages configuration')
40
+ return (
41
+ <div
42
+ style={{
43
+ padding: '1rem',
44
+ border: '2px dashed orange',
45
+ color: 'orange',
46
+ fontFamily: 'monospace',
47
+ }}
48
+ >
49
+ language-switcher: missing app.languages configuration
50
+ </div>
51
+ )
52
+ }
53
+
54
+ // Extract props
55
+ const variant = (props.variant as string | undefined) || 'dropdown'
56
+ // Auto-enable showFlags if any language has a flag property
57
+ const hasFlags = languages.supported.some((lang) => lang.flag)
58
+ const showFlags = (props.showFlags as boolean | undefined) ?? hasFlags
59
+
60
+ // Languages already validated at server startup (start-server.ts)
61
+ return (
62
+ <LanguageSwitcher
63
+ languages={languages}
64
+ variant={variant}
65
+ showFlags={showFlags}
66
+ />
67
+ )
68
+ }
69
+
70
+ /**
71
+ * Renders alert element with variant support
72
+ *
73
+ * Creates an alert component with semantic variants (success, danger, warning, info).
74
+ * The variant prop determines the visual styling based on theme colors.
75
+ * Uses inline styles derived from theme tokens for color variants.
76
+ *
77
+ * @param props - Element props including variant and data-testid
78
+ * @param content - Alert message text
79
+ * @param children - Optional child elements
80
+ * @param theme - Theme configuration for color resolution
81
+ * @returns React element for alert component
82
+ */
83
+ export function renderAlert(
84
+ props: ElementProps,
85
+ content: string | undefined,
86
+ children: readonly React.ReactNode[],
87
+ theme?: Theme
88
+ ): ReactElement {
89
+ const variant = props.variant as string | undefined
90
+ const existingStyle = (props.style as Record<string, unknown> | undefined) || {}
91
+
92
+ // Merge existing styles with variant styles
93
+ const mergedStyle = {
94
+ padding: '12px 16px',
95
+ borderRadius: '4px',
96
+ border: '1px solid',
97
+ ...buildAlertVariantStyles(variant, theme),
98
+ ...existingStyle,
99
+ }
100
+
101
+ return (
102
+ <div
103
+ {...props}
104
+ role="alert"
105
+ style={mergedStyle}
106
+ >
107
+ {content || children}
108
+ </div>
109
+ )
110
+ }
111
+
112
+ /**
113
+ * Renders list element with staggered fadeIn animations for items
114
+ *
115
+ * Parses HTML content to extract <li> elements and applies incremental
116
+ * animation delays for a cascading appearance effect.
117
+ *
118
+ * SECURITY: Safe use of dangerouslySetInnerHTML (after sanitization)
119
+ * - Content: List item HTML from page configuration
120
+ * - Source: Validated Page schema (list content property)
121
+ * - Risk: None (after sanitization) - DOMPurify removes malicious content
122
+ * - Validation: DOMPurify sanitization before rendering
123
+ * - Purpose: Render animated list items with staggered appearance
124
+ * - XSS Protection: DOMPurify removes scripts, event handlers, dangerous tags
125
+ * - Process: Extract <li> tags, sanitize, apply animations
126
+ *
127
+ * @param props - Element props including data-testid
128
+ * @param content - HTML string containing <li> elements
129
+ * @param theme - Theme configuration for animation settings
130
+ * @returns React element with list items animated with stagger effect
131
+ */
132
+ export function renderList(
133
+ props: ElementProps,
134
+ content: string | undefined,
135
+ theme?: Theme
136
+ ): ReactElement {
137
+ if (!content) {
138
+ return <ul {...props} />
139
+ }
140
+
141
+ const sanitizedContent = DOMPurify.sanitize(content)
142
+ const liMatches = sanitizedContent.match(/<li[^>]*>.*?<\/li>/gs) || []
143
+
144
+ const animationConfig = getAnimationConfig(theme)
145
+ const duration = (animationConfig?.duration as string | undefined) || '400ms'
146
+ const easing = (animationConfig?.easing as string | undefined) || 'ease-out'
147
+ const staggerDelay = calculateStaggerDelay(duration)
148
+
149
+ const renderedItems = liMatches.map((liHtml, index) => {
150
+ const delay = `${index * staggerDelay}ms`
151
+ const animationValue = `fade-in ${duration} ${easing} ${delay} both`
152
+ const innerHtml = liHtml.replace(/<li[^>]*>|<\/li>/g, '')
153
+
154
+ return (
155
+ <li
156
+ key={index}
157
+ style={{ animation: animationValue }}
158
+ dangerouslySetInnerHTML={{ __html: innerHtml }}
159
+ />
160
+ )
161
+ })
162
+
163
+ return <ul {...props}>{renderedItems}</ul>
164
+ }
165
+
166
+ /**
167
+ * Renders unordered list element (ul)
168
+ *
169
+ * Supports recursive children rendering for nested lists.
170
+ * Used by component system for component-based list structures.
171
+ *
172
+ * @param props - Element props including data-testid
173
+ * @param content - Optional text content
174
+ * @param children - Child elements (typically li elements)
175
+ * @returns React element for unordered list
176
+ */
177
+ export function renderUnorderedList(
178
+ props: ElementProps,
179
+ content: string | undefined,
180
+ children: readonly React.ReactNode[]
181
+ ): ReactElement {
182
+ return <ul {...props}>{content || children}</ul>
183
+ }
184
+
185
+ /**
186
+ * Renders list item element (li)
187
+ *
188
+ * Supports recursive children rendering for nested list structures.
189
+ * Content and children can be combined - content appears first if both present.
190
+ *
191
+ * @param props - Element props including data-testid
192
+ * @param content - Optional text content
193
+ * @param children - Optional child elements (for nested lists)
194
+ * @returns React element for list item
195
+ */
196
+ export function renderListItem(
197
+ props: ElementProps,
198
+ content: string | undefined,
199
+ children: readonly React.ReactNode[]
200
+ ): ReactElement {
201
+ // If both content and children exist, render both (content first)
202
+ if (content && children && children.length > 0) {
203
+ return (
204
+ <li {...props}>
205
+ {content}
206
+ {children}
207
+ </li>
208
+ )
209
+ }
210
+ // Otherwise use content or children (whichever is present)
211
+ return <li {...props}>{content || children}</li>
212
+ }
@@ -0,0 +1,31 @@
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 { Languages } from '@/domain/models/app/languages'
9
+ import type { Component } from '@/domain/models/app/page/sections'
10
+ import type { Theme } from '@/domain/models/app/theme'
11
+ import type { ReactElement } from 'react'
12
+
13
+ /**
14
+ * Configuration object for component dispatching
15
+ * Replaces multiple function parameters with a single config object
16
+ */
17
+ export interface ComponentDispatchConfig {
18
+ readonly type: Component['type']
19
+ readonly elementProps: Record<string, unknown>
20
+ readonly elementPropsWithSpacing: Record<string, unknown>
21
+ readonly content: string | undefined
22
+ readonly renderedChildren: readonly ReactElement[]
23
+ readonly theme: Theme | undefined
24
+ readonly languages: Languages | undefined
25
+ readonly interactions?: Component['interactions']
26
+ }
27
+
28
+ /**
29
+ * Component renderer function type
30
+ */
31
+ export type ComponentRenderer = (config: ComponentDispatchConfig) => ReactElement | null
@@ -0,0 +1,39 @@
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 { interactiveComponents } from './interactive-components'
9
+ import { mediaComponents } from './media-components'
10
+ import { specialComponents } from './special-components'
11
+ import { structuralComponents } from './structural-components'
12
+ import { textComponents } from './text-components'
13
+ import type { ComponentRenderer } from '../component-dispatch-config'
14
+ import type { Component } from '@/domain/models/app/page/sections'
15
+
16
+ /**
17
+ * Component registry mapping component types to their renderer functions
18
+ *
19
+ * This registry combines all component categories:
20
+ * - Structural: section, header, footer, div, container, etc.
21
+ * - Text: h1-h6, heading, text, paragraph, code, etc.
22
+ * - Media: image, video, audio, iframe, etc.
23
+ * - Interactive: button, link, form, input, icon, badge, etc.
24
+ * - Special: hero, card-*, speech-bubble, navigation, list, etc.
25
+ */
26
+ export const COMPONENT_REGISTRY: Partial<Record<Component['type'], ComponentRenderer>> = {
27
+ ...structuralComponents,
28
+ ...textComponents,
29
+ ...mediaComponents,
30
+ ...interactiveComponents,
31
+ ...specialComponents,
32
+ }
33
+
34
+ // Re-export individual component groups for granular imports if needed
35
+ export { structuralComponents } from './structural-components'
36
+ export { textComponents } from './text-components'
37
+ export { mediaComponents } from './media-components'
38
+ export { interactiveComponents } from './interactive-components'
39
+ export { specialComponents } from './special-components'
@@ -0,0 +1,54 @@
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 * as Renderers from '../../renderers/element-renderers'
9
+ import { convertBadgeProps } from '../component-registry-helpers'
10
+ import type { ComponentRenderer } from '../component-dispatch-config'
11
+ import type { Component } from '@/domain/models/app/page/sections'
12
+
13
+ /**
14
+ * Interactive components (button, link, form, input, icon, badge, etc.)
15
+ *
16
+ * These components render interactive elements and form controls.
17
+ */
18
+ export const interactiveComponents: Partial<Record<Component['type'], ComponentRenderer>> = {
19
+ button: ({ elementProps, content, renderedChildren, interactions }) =>
20
+ Renderers.renderButton(elementProps, content, renderedChildren, interactions),
21
+
22
+ link: ({ elementProps, content, renderedChildren }) =>
23
+ Renderers.renderLink(elementProps, content, renderedChildren),
24
+
25
+ a: ({ elementProps, content, renderedChildren }) =>
26
+ Renderers.renderLink(elementProps, content, renderedChildren),
27
+
28
+ alert: ({ elementProps, content, renderedChildren, theme }) =>
29
+ Renderers.renderAlert(elementProps, content, renderedChildren, theme),
30
+
31
+ form: ({ elementProps, renderedChildren }) =>
32
+ Renderers.renderForm(elementProps, renderedChildren),
33
+
34
+ input: ({ elementProps }) => Renderers.renderInput(elementProps),
35
+
36
+ icon: ({ elementProps, renderedChildren }) =>
37
+ Renderers.renderIcon(elementProps, renderedChildren),
38
+
39
+ badge: ({ elementProps, content, renderedChildren, interactions }) => {
40
+ const badgeProps = convertBadgeProps(elementProps)
41
+ return Renderers.renderHTMLElement({
42
+ type: 'span',
43
+ props: badgeProps,
44
+ content: content,
45
+ children: renderedChildren,
46
+ interactions: interactions,
47
+ })
48
+ },
49
+
50
+ customHTML: ({ elementProps }) => Renderers.renderCustomHTML(elementProps),
51
+
52
+ 'language-switcher': ({ elementProps, languages }) =>
53
+ Renderers.renderLanguageSwitcher(elementProps, languages),
54
+ }
@@ -0,0 +1,36 @@
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 * as Renderers from '../../renderers/element-renderers'
9
+ import type { ComponentRenderer } from '../component-dispatch-config'
10
+ import type { Component } from '@/domain/models/app/page/sections'
11
+
12
+ /**
13
+ * Media components (image, video, audio, iframe, etc.)
14
+ *
15
+ * These components render media content and embedded elements.
16
+ */
17
+ export const mediaComponents: Partial<Record<Component['type'], ComponentRenderer>> = {
18
+ image: ({ elementProps }) => Renderers.renderImage(elementProps),
19
+
20
+ img: ({ elementProps }) => Renderers.renderImage(elementProps),
21
+
22
+ avatar: ({ elementProps }) => Renderers.renderAvatar(elementProps),
23
+
24
+ thumbnail: ({ elementProps }) => Renderers.renderThumbnail(elementProps),
25
+
26
+ 'hero-image': ({ elementProps }) => Renderers.renderHeroImage(elementProps),
27
+
28
+ video: ({ elementProps, renderedChildren }) =>
29
+ Renderers.renderVideo(elementProps, renderedChildren),
30
+
31
+ audio: ({ elementProps, renderedChildren }) =>
32
+ Renderers.renderAudio(elementProps, renderedChildren),
33
+
34
+ iframe: ({ elementProps, renderedChildren }) =>
35
+ Renderers.renderIframe(elementProps, renderedChildren),
36
+ }
@@ -0,0 +1,153 @@
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 { Hero } from '@/presentation/ui/sections/hero'
9
+ import * as Renderers from '../../renderers/element-renderers'
10
+ import { parseHTMLContent } from '../component-registry-helpers'
11
+ import type { ComponentRenderer } from '../component-dispatch-config'
12
+ import type { Component } from '@/domain/models/app/page/sections'
13
+ import type { ReactElement } from 'react'
14
+
15
+ /** Inline class maps for card sub-components */
16
+ const CARD_CLASSES = {
17
+ 'card-with-header': 'bg-white border border-gray-200 overflow-hidden rounded-lg',
18
+ 'card-header': 'bg-gray-50 px-4 py-3 border-b border-gray-200 rounded-t-lg',
19
+ 'card-body': 'px-4 py-3',
20
+ 'card-footer': 'bg-gray-50 px-4 py-3 border-t border-gray-200 rounded-b-lg',
21
+ } as const
22
+
23
+ /**
24
+ * Creates a renderer for card components using inline div elements
25
+ */
26
+ function createCardRenderer(baseClasses: string): ComponentRenderer {
27
+ return ({ elementProps, content, renderedChildren }) => {
28
+ const extra = elementProps.className as string | undefined
29
+ const className = extra ? `${baseClasses} ${extra}` : baseClasses
30
+ return (
31
+ <div
32
+ data-testid={elementProps['data-testid'] as string | undefined}
33
+ className={className}
34
+ >
35
+ {content || renderedChildren}
36
+ </div>
37
+ )
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Shared renderer for hero and hero-section component types
43
+ */
44
+ const renderHeroSection: ComponentRenderer = ({
45
+ elementProps,
46
+ theme,
47
+ content,
48
+ renderedChildren,
49
+ }) => {
50
+ // If content is an HTML string, parse it as children
51
+ const children =
52
+ typeof content === 'string' && content.trim().startsWith('<')
53
+ ? parseHTMLContent(content)
54
+ : renderedChildren
55
+
56
+ return (
57
+ <Hero
58
+ theme={theme}
59
+ content={
60
+ typeof content === 'object'
61
+ ? (content as { button?: { text: string; animation?: string } } | undefined)
62
+ : undefined
63
+ }
64
+ data-testid={elementProps['data-testid'] as string | undefined}
65
+ >
66
+ {children}
67
+ </Hero>
68
+ )
69
+ }
70
+
71
+ /**
72
+ * Special components (hero, card-*, speech-bubble, navigation, list, etc.)
73
+ *
74
+ * These components have complex rendering logic or use custom UI components.
75
+ */
76
+ export const specialComponents: Partial<Record<Component['type'], ComponentRenderer>> = {
77
+ 'speech-bubble': ({ elementProps, content, renderedChildren }) => {
78
+ const base =
79
+ 'bg-blue-100 border border-blue-300 px-4 py-3 text-sm rounded-tl-md rounded-tr-md rounded-br-md rounded-bl-none'
80
+ const extra = elementProps.className as string | undefined
81
+ const className = extra ? `${base} ${extra}` : base
82
+ return (
83
+ <div
84
+ data-testid={elementProps['data-testid'] as string | undefined}
85
+ className={className}
86
+ >
87
+ {content || renderedChildren}
88
+ </div>
89
+ )
90
+ },
91
+
92
+ 'card-with-header': createCardRenderer(CARD_CLASSES['card-with-header']),
93
+ 'card-header': createCardRenderer(CARD_CLASSES['card-header']),
94
+ 'card-body': createCardRenderer(CARD_CLASSES['card-body']),
95
+ 'card-footer': createCardRenderer(CARD_CLASSES['card-footer']),
96
+
97
+ hero: renderHeroSection,
98
+
99
+ 'hero-section': renderHeroSection,
100
+
101
+ list: ({ elementProps, content, theme }) => Renderers.renderList(elementProps, content, theme),
102
+
103
+ navigation: ({ elementPropsWithSpacing, content, renderedChildren, interactions }) => {
104
+ // Default hamburger menu button when no children provided
105
+ // Rationale: Navigation without children should display something visible
106
+ // Used in APP-THEME-BREAKPOINTS-APPLICATION-002 test for default rendering
107
+ const defaultChildren =
108
+ renderedChildren.length === 0 ? (
109
+ <button
110
+ type="button"
111
+ aria-label="Menu"
112
+ className="cursor-pointer rounded-md border-none bg-blue-500 px-3 py-3 text-xl leading-none text-white"
113
+ >
114
+
115
+ </button>
116
+ ) : (
117
+ renderedChildren
118
+ )
119
+
120
+ // Add default padding to nav element
121
+ const navProps = {
122
+ ...elementPropsWithSpacing,
123
+ className: elementPropsWithSpacing.className
124
+ ? `${elementPropsWithSpacing.className} p-4`
125
+ : 'p-4',
126
+ }
127
+
128
+ return Renderers.renderHTMLElement({
129
+ type: 'nav',
130
+ props: navProps,
131
+ content: content,
132
+ children: defaultChildren as readonly ReactElement[],
133
+ interactions: interactions,
134
+ })
135
+ },
136
+
137
+ ul: ({ elementProps, content, renderedChildren }) =>
138
+ Renderers.renderUnorderedList(elementProps, content, renderedChildren),
139
+
140
+ li: ({ elementProps, content, renderedChildren }) =>
141
+ Renderers.renderListItem(elementProps, content, renderedChildren),
142
+
143
+ 'responsive-grid': () => {
144
+ return (
145
+ <section
146
+ data-testid="responsive-section"
147
+ className="p-8 md:p-16"
148
+ >
149
+ <div className="responsive-grid grid gap-4 lg:gap-8">Grid items</div>
150
+ </section>
151
+ )
152
+ },
153
+ }