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,124 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { type ReactElement } from 'react'
9
+ import { buildAccessibilityRole, buildScrollAttributes } from '../html-element-helpers'
10
+
11
+ /**
12
+ * Common props for all rendered elements
13
+ */
14
+ export interface ElementProps {
15
+ readonly [key: string]: unknown
16
+ readonly className?: string
17
+ readonly 'data-component'?: string
18
+ }
19
+
20
+ /**
21
+ * Configuration for renderHTMLElement
22
+ */
23
+ export type HTMLElementConfig = {
24
+ readonly type:
25
+ | 'div'
26
+ | 'span'
27
+ | 'section'
28
+ | 'header'
29
+ | 'footer'
30
+ | 'main'
31
+ | 'article'
32
+ | 'aside'
33
+ | 'nav'
34
+ readonly props: ElementProps
35
+ readonly content: string | undefined
36
+ readonly children: readonly React.ReactNode[]
37
+ readonly interactions?: unknown
38
+ }
39
+
40
+ /**
41
+ * Renders HTML structural elements (div, span, section, and HTML5 semantic elements)
42
+ *
43
+ * If content starts with '<', it's treated as HTML and rendered via dangerouslySetInnerHTML.
44
+ * Otherwise, content is rendered as plain text.
45
+ *
46
+ * SECURITY NOTE - Trusted Schema Content:
47
+ * HTML content is rendered without sanitization since it comes from trusted
48
+ * schema configuration. For user-generated content, use renderCustomHTML instead.
49
+ *
50
+ * SECURITY: Safe use of dangerouslySetInnerHTML
51
+ * - Content: Schema-defined HTML from page configuration
52
+ * - Source: Validated Page schema (section.content property)
53
+ * - Risk: Low - content is from server configuration, not user input
54
+ * - Validation: Content validated at build/runtime via schema
55
+ * - Purpose: Render rich HTML content in structural elements
56
+ * - XSS Protection: Use renderCustomHTML with DOMPurify for user-generated content
57
+ * - Condition: Only used when content starts with '<' character
58
+ *
59
+ * For section elements, automatically adds role="region" for accessibility best practices,
60
+ * ensuring sections are properly identified in the accessibility tree.
61
+ *
62
+ * Supports scroll interactions via data attributes for IntersectionObserver.
63
+ */
64
+ export function renderHTMLElement(config: HTMLElementConfig): ReactElement {
65
+ const { type, props, content, children, interactions } = config
66
+ const Element = type
67
+
68
+ // Build element props immutably
69
+ const accessibilityRole = buildAccessibilityRole(type, children.length > 0, !!content, props.role)
70
+ const scrollAttributes = buildScrollAttributes(interactions)
71
+ const elementProps = { ...props, ...accessibilityRole, ...scrollAttributes }
72
+
73
+ // If content looks like HTML (starts with '<'), render as HTML
74
+ // This is safe for schema-defined content but should NOT be used for user input
75
+ if (content?.trim().startsWith('<')) {
76
+ return (
77
+ <Element
78
+ {...elementProps}
79
+ dangerouslySetInnerHTML={{ __html: content }}
80
+ />
81
+ )
82
+ }
83
+
84
+ return <Element {...elementProps}>{content || children}</Element>
85
+ }
86
+
87
+ /**
88
+ * Renders heading elements (h1-h6)
89
+ */
90
+ export function renderHeading(
91
+ level: 1 | 2 | 3 | 4 | 5 | 6,
92
+ props: ElementProps,
93
+ content: string | undefined,
94
+ children: readonly React.ReactNode[]
95
+ ): ReactElement {
96
+ const HeadingTag = `h${level}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
97
+ return <HeadingTag {...props}>{content || children}</HeadingTag>
98
+ }
99
+
100
+ /**
101
+ * Renders text element with dynamic level
102
+ *
103
+ * The text element supports a 'level' prop to determine the HTML tag.
104
+ * If level is h1-h6, renders as heading.
105
+ * If level is p, renders as paragraph.
106
+ * If level is label, renders as label.
107
+ * Otherwise renders as span to ensure proper ARIA generic role.
108
+ */
109
+ export function renderTextElement(props: ElementProps, content: string | undefined): ReactElement {
110
+ const { level } = props
111
+
112
+ if (level === 'h1') return <h1 {...props}>{content}</h1>
113
+ if (level === 'h2') return <h2 {...props}>{content}</h2>
114
+ if (level === 'h3') return <h3 {...props}>{content}</h3>
115
+ if (level === 'h4') return <h4 {...props}>{content}</h4>
116
+ if (level === 'h5') return <h5 {...props}>{content}</h5>
117
+ if (level === 'h6') return <h6 {...props}>{content}</h6>
118
+ if (level === 'p') return <p {...props}>{content}</p>
119
+ if (level === 'label') return <label {...props}>{content}</label>
120
+
121
+ // Default to span for inline text to ensure proper ARIA generic role
122
+ // span elements with text content maintain generic role in ARIA tree
123
+ return <span {...props}>{content}</span>
124
+ }
@@ -0,0 +1,59 @@
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
+ * Element renderers barrel export
10
+ *
11
+ * This module exports all element renderer functions organized by category:
12
+ * - HTML element renderers (structural elements, headings, text)
13
+ * - Text content renderers (paragraph, code, pre)
14
+ * - Media renderers (image, video, audio, iframe)
15
+ * - Interactive renderers (button, link, form, input, icon)
16
+ * - Specialized renderers (alert, list, language-switcher)
17
+ */
18
+
19
+ // HTML element renderers
20
+ export {
21
+ renderHTMLElement,
22
+ renderHeading,
23
+ renderTextElement,
24
+ type ElementProps,
25
+ type HTMLElementConfig,
26
+ } from './html-element-renderer'
27
+
28
+ // Text content renderers
29
+ export { renderParagraph, renderCode, renderPre } from './text-content-renderers'
30
+
31
+ // Media renderers
32
+ export {
33
+ renderImage,
34
+ renderAvatar,
35
+ renderThumbnail,
36
+ renderHeroImage,
37
+ renderVideo,
38
+ renderAudio,
39
+ renderIframe,
40
+ } from './media-renderers'
41
+
42
+ // Interactive renderers
43
+ export {
44
+ renderButton,
45
+ renderLink,
46
+ renderForm,
47
+ renderInput,
48
+ renderIcon,
49
+ renderCustomHTML,
50
+ } from './interactive-renderers'
51
+
52
+ // Re-export specialized renderers for backward compatibility
53
+ export {
54
+ renderLanguageSwitcher,
55
+ renderAlert,
56
+ renderList,
57
+ renderUnorderedList,
58
+ renderListItem,
59
+ } from '../specialized-renderers'
@@ -0,0 +1,231 @@
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 * as LucideIcons from 'lucide-react'
10
+ import { type ReactElement } from 'react'
11
+ import type { ElementProps } from './html-element-renderer'
12
+
13
+ /**
14
+ * Build data attributes for click interactions
15
+ */
16
+ function buildClickDataAttributes(clickInteraction: {
17
+ animation?: string
18
+ navigate?: string
19
+ openUrl?: string
20
+ openInNewTab?: boolean
21
+ scrollTo?: string
22
+ toggleElement?: string
23
+ submitForm?: string
24
+ }): Record<string, string> {
25
+ return {
26
+ ...(clickInteraction.animation && { 'data-click-animation': clickInteraction.animation }),
27
+ ...(clickInteraction.navigate && { 'data-click-navigate': clickInteraction.navigate }),
28
+ ...(clickInteraction.openUrl && { 'data-click-open-url': clickInteraction.openUrl }),
29
+ ...(clickInteraction.openInNewTab !== undefined && {
30
+ 'data-click-open-in-new-tab': String(clickInteraction.openInNewTab),
31
+ }),
32
+ ...(clickInteraction.scrollTo && { 'data-click-scroll-to': clickInteraction.scrollTo }),
33
+ ...(clickInteraction.toggleElement && {
34
+ 'data-click-toggle-element': clickInteraction.toggleElement,
35
+ }),
36
+ ...(clickInteraction.submitForm && {
37
+ 'data-click-submit-form': clickInteraction.submitForm,
38
+ }),
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Renders button element with click interactions
44
+ */
45
+ export function renderButton(
46
+ props: ElementProps,
47
+ content: string | undefined,
48
+ children: readonly React.ReactNode[],
49
+ interactions?: unknown
50
+ ): ReactElement {
51
+ const interactionsTyped = interactions as
52
+ | {
53
+ click?: {
54
+ animation?: string
55
+ navigate?: string
56
+ openUrl?: string
57
+ openInNewTab?: boolean
58
+ scrollTo?: string
59
+ toggleElement?: string
60
+ submitForm?: string
61
+ }
62
+ }
63
+ | undefined
64
+ const clickInteraction = interactionsTyped?.click
65
+
66
+ // Store interaction data in data attributes for client-side JavaScript handler
67
+ const buttonProps = clickInteraction
68
+ ? { ...props, ...buildClickDataAttributes(clickInteraction) }
69
+ : props
70
+
71
+ return <button {...buttonProps}>{content || children}</button>
72
+ }
73
+
74
+ /**
75
+ * Renders link (anchor) element
76
+ */
77
+ export function renderLink(
78
+ props: ElementProps,
79
+ content: string | undefined,
80
+ children: readonly React.ReactNode[]
81
+ ): ReactElement {
82
+ return <a {...props}>{content || children}</a>
83
+ }
84
+
85
+ /**
86
+ * Renders form element
87
+ */
88
+ export function renderForm(
89
+ props: ElementProps,
90
+ children: readonly React.ReactNode[]
91
+ ): ReactElement {
92
+ return <form {...props}>{children}</form>
93
+ }
94
+
95
+ /**
96
+ * Renders input element
97
+ */
98
+ export function renderInput(props: ElementProps): ReactElement {
99
+ return <input {...props} />
100
+ }
101
+
102
+ /**
103
+ * Converts a kebab-case icon name to PascalCase for Lucide lookup
104
+ * Example: 'check-circle' -> 'CheckCircle', 'arrow-right' -> 'ArrowRight'
105
+ */
106
+ function kebabToPascalCase(name: string): string {
107
+ return name
108
+ .split('-')
109
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
110
+ .join('')
111
+ }
112
+
113
+ /**
114
+ * Resolves a Lucide icon component by kebab-case name.
115
+ * Returns undefined if no matching component is found.
116
+ *
117
+ * Lucide icons use forwardRef, so they are objects (typeof === 'object') in the Bun runtime,
118
+ * not plain functions. We treat both as valid React components.
119
+ */
120
+ function resolveLucideIcon(
121
+ iconName: string | undefined
122
+ ): React.ComponentType<Record<string, unknown>> | undefined {
123
+ if (!iconName) return undefined
124
+ const component = (LucideIcons as Record<string, unknown>)[kebabToPascalCase(iconName)]
125
+ // Accept both function components and forwardRef objects (which are typeof 'object')
126
+ if (typeof component === 'function')
127
+ return component as React.ComponentType<Record<string, unknown>>
128
+ if (typeof component === 'object' && component !== null)
129
+ return component as React.ComponentType<Record<string, unknown>>
130
+ return undefined
131
+ }
132
+
133
+ /**
134
+ * Renders icon using Lucide React icons
135
+ *
136
+ * Props extracted from elementProps:
137
+ * - name: kebab-case Lucide icon name (e.g. 'check-circle', 'arrow-right')
138
+ * - color: SVG stroke color (default: 'currentColor')
139
+ * - size: SVG width/height in pixels (default: 24)
140
+ * - strokeWidth: SVG stroke-width (default: 2)
141
+ * - ariaLabel: accessible label; if provided, sets role="img" instead of aria-hidden
142
+ * - className: CSS classes forwarded to the SVG
143
+ *
144
+ * Falls back to an empty SVG with data-testid for unknown icon names.
145
+ */
146
+ export function renderIcon(
147
+ props: ElementProps,
148
+ _children: readonly React.ReactNode[]
149
+ ): ReactElement {
150
+ const iconName = props.name as string | undefined
151
+ const iconColor = props.color as string | undefined
152
+ const iconSize = props.size as number | undefined
153
+ const iconStrokeWidth = props.strokeWidth as number | undefined
154
+ const ariaLabel = props.ariaLabel as string | undefined
155
+
156
+ // Build props to forward to the SVG — strip out icon-specific props and their data-* conversions
157
+ const {
158
+ name: _name,
159
+ color: _color,
160
+ size: _size,
161
+ strokeWidth: _strokeWidth,
162
+ ariaLabel: _ariaLabel,
163
+ 'data-color': _dataColor,
164
+ 'data-size': _dataSize,
165
+ 'data-stroke-width': _dataStrokeWidth,
166
+ 'data-aria-label': _dataAriaLabel,
167
+ ...restProps
168
+ } = props as Record<string, unknown>
169
+
170
+ const testId = iconName ? `icon-${iconName}` : 'icon'
171
+ const colorProps = iconColor !== undefined ? { 'data-color': iconColor } : {}
172
+ const a11yProps = ariaLabel
173
+ ? { role: 'img' as const, 'aria-label': ariaLabel }
174
+ : { 'aria-hidden': 'true' as const }
175
+
176
+ const LucideIcon = resolveLucideIcon(iconName)
177
+
178
+ if (LucideIcon) {
179
+ return (
180
+ <LucideIcon
181
+ {...restProps}
182
+ {...a11yProps}
183
+ {...colorProps}
184
+ size={iconSize ?? 24}
185
+ color={iconColor ?? 'currentColor'}
186
+ strokeWidth={iconStrokeWidth ?? 2}
187
+ data-testid={testId}
188
+ />
189
+ ) as ReactElement
190
+ }
191
+
192
+ // Fallback: render empty SVG placeholder for unknown icons
193
+ return (
194
+ <svg
195
+ {...(restProps as React.SVGProps<SVGSVGElement>)}
196
+ {...a11yProps}
197
+ {...colorProps}
198
+ data-testid={testId}
199
+ xmlns="http://www.w3.org/2000/svg"
200
+ width={iconSize ?? 24}
201
+ height={iconSize ?? 24}
202
+ />
203
+ )
204
+ }
205
+
206
+ /**
207
+ * Renders custom HTML with DOMPurify sanitization
208
+ *
209
+ * SECURITY: This function sanitizes HTML to prevent XSS attacks.
210
+ * DOMPurify removes malicious scripts, event handlers, and dangerous attributes.
211
+ * Critical for user-generated content or external HTML sources.
212
+ *
213
+ * SECURITY: Safe use of dangerouslySetInnerHTML (after sanitization)
214
+ * - Content: User-provided or external HTML (props.html)
215
+ * - Source: Potentially untrusted - user input or external sources
216
+ * - Risk: None (after sanitization) - XSS prevented by DOMPurify
217
+ * - Validation: DOMPurify removes all malicious content
218
+ * - Purpose: Render custom HTML blocks safely
219
+ * - XSS Protection: DOMPurify sanitization removes scripts, event handlers, dangerous tags
220
+ * - Best Practice: This is the ONLY way to render user-generated HTML safely
221
+ */
222
+ export function renderCustomHTML(props: ElementProps): ReactElement {
223
+ const sanitizedHTML = DOMPurify.sanitize((props.html as string | undefined) || '')
224
+ return (
225
+ <div
226
+ {...props}
227
+ // Safe to use dangerouslySetInnerHTML after DOMPurify sanitization
228
+ dangerouslySetInnerHTML={{ __html: sanitizedHTML }}
229
+ />
230
+ )
231
+ }
@@ -0,0 +1,102 @@
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 } from 'react'
9
+ import type { ElementProps } from './html-element-renderer'
10
+
11
+ /**
12
+ * Renders image element
13
+ */
14
+ export function renderImage(props: ElementProps): ReactElement {
15
+ return (
16
+ <img
17
+ {...props}
18
+ alt={(props.alt as string | undefined) || ''}
19
+ />
20
+ )
21
+ }
22
+
23
+ /**
24
+ * Renders avatar image with circular border-radius
25
+ * Includes default dimensions to ensure visibility even when image fails to load
26
+ */
27
+ export function renderAvatar(props: ElementProps): ReactElement {
28
+ const style = {
29
+ ...((props.style as Record<string, unknown> | undefined) || {}),
30
+ minWidth: '48px',
31
+ minHeight: '48px',
32
+ width: '48px',
33
+ height: '48px',
34
+ }
35
+
36
+ return (
37
+ <img
38
+ {...props}
39
+ style={style}
40
+ alt={(props.alt as string | undefined) || ''}
41
+ className={[props.className, 'rounded-full'].filter(Boolean).join(' ')}
42
+ />
43
+ )
44
+ }
45
+
46
+ /**
47
+ * Renders thumbnail image with moderate border-radius
48
+ * Applies md radius (0.375rem) for soft corners while preserving aspect ratio
49
+ */
50
+ export function renderThumbnail(props: ElementProps): ReactElement {
51
+ return (
52
+ <img
53
+ {...props}
54
+ alt={(props.alt as string | undefined) || ''}
55
+ className={[props.className, 'rounded-md'].filter(Boolean).join(' ')}
56
+ />
57
+ )
58
+ }
59
+
60
+ /**
61
+ * Renders hero image with top-only border-radius
62
+ * Applies lg radius to top corners only for integration with card layout
63
+ */
64
+ export function renderHeroImage(props: ElementProps): ReactElement {
65
+ return (
66
+ <img
67
+ {...props}
68
+ alt={(props.alt as string | undefined) || ''}
69
+ className={[props.className, 'rounded-t-lg'].filter(Boolean).join(' ')}
70
+ />
71
+ )
72
+ }
73
+
74
+ /**
75
+ * Renders video element
76
+ */
77
+ export function renderVideo(
78
+ props: ElementProps,
79
+ children: readonly React.ReactNode[]
80
+ ): ReactElement {
81
+ return <video {...props}>{children}</video>
82
+ }
83
+
84
+ /**
85
+ * Renders audio element
86
+ */
87
+ export function renderAudio(
88
+ props: ElementProps,
89
+ children: readonly React.ReactNode[]
90
+ ): ReactElement {
91
+ return <audio {...props}>{children}</audio>
92
+ }
93
+
94
+ /**
95
+ * Renders iframe element
96
+ */
97
+ export function renderIframe(
98
+ props: ElementProps,
99
+ children: readonly React.ReactNode[]
100
+ ): ReactElement {
101
+ return <iframe {...props}>{children}</iframe>
102
+ }
@@ -0,0 +1,42 @@
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 } from 'react'
9
+ import type { ElementProps } from './html-element-renderer'
10
+
11
+ /**
12
+ * Renders paragraph element
13
+ */
14
+ export function renderParagraph(
15
+ props: ElementProps,
16
+ content: string | undefined,
17
+ children: readonly React.ReactNode[]
18
+ ): ReactElement {
19
+ return <p {...props}>{content || children}</p>
20
+ }
21
+
22
+ /**
23
+ * Renders code element (inline code)
24
+ */
25
+ export function renderCode(
26
+ props: ElementProps,
27
+ content: string | undefined,
28
+ children: readonly React.ReactNode[]
29
+ ): ReactElement {
30
+ return <code {...props}>{content || children}</code>
31
+ }
32
+
33
+ /**
34
+ * Renders pre element (preformatted text block)
35
+ */
36
+ export function renderPre(
37
+ props: ElementProps,
38
+ content: string | undefined,
39
+ children: readonly React.ReactNode[]
40
+ ): ReactElement {
41
+ return <pre {...props}>{content || children}</pre>
42
+ }
@@ -0,0 +1,53 @@
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
+ * Re-export from element-renderers directory
10
+ *
11
+ * This file maintains backward compatibility with existing imports.
12
+ * The actual implementation has been split into:
13
+ * - html-element-renderer.tsx (renderHTMLElement, renderHeading, renderTextElement)
14
+ * - text-content-renderers.tsx (renderParagraph, renderCode, renderPre)
15
+ * - media-renderers.tsx (renderImage, renderVideo, renderAudio, etc.)
16
+ * - interactive-renderers.tsx (renderButton, renderLink, renderForm, etc.)
17
+ *
18
+ * @see src/presentation/ui/sections/renderers/element-renderers/
19
+ */
20
+ export {
21
+ // Types
22
+ type ElementProps,
23
+ type HTMLElementConfig,
24
+ // HTML element renderers
25
+ renderHTMLElement,
26
+ renderHeading,
27
+ renderTextElement,
28
+ // Text content renderers
29
+ renderParagraph,
30
+ renderCode,
31
+ renderPre,
32
+ // Media renderers
33
+ renderImage,
34
+ renderAvatar,
35
+ renderThumbnail,
36
+ renderHeroImage,
37
+ renderVideo,
38
+ renderAudio,
39
+ renderIframe,
40
+ // Interactive renderers
41
+ renderButton,
42
+ renderLink,
43
+ renderForm,
44
+ renderInput,
45
+ renderIcon,
46
+ renderCustomHTML,
47
+ // Specialized renderers
48
+ renderLanguageSwitcher,
49
+ renderAlert,
50
+ renderList,
51
+ renderUnorderedList,
52
+ renderListItem,
53
+ } from './element-renderers/'