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,179 @@
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 { CONTAINER_TYPES } from '../component-renderer.types'
9
+ import { buildFlexClasses, buildGridClasses } from '../styling/class-builders'
10
+ import type { Component } from '@/domain/models/app/page/sections'
11
+
12
+ /**
13
+ * Build data-testid based on component name and instance index
14
+ */
15
+ export function buildTestId(
16
+ componentName?: string,
17
+ componentInstanceIndex?: number,
18
+ type?: string,
19
+ substitutedProps?: Record<string, unknown>
20
+ ): string | undefined {
21
+ if (componentName) {
22
+ return componentInstanceIndex !== undefined
23
+ ? `component-${componentName}-${componentInstanceIndex}`
24
+ : `component-${componentName}`
25
+ }
26
+
27
+ const dataTestId = substitutedProps?.['data-testid']
28
+ if (typeof dataTestId === 'string') {
29
+ return dataTestId
30
+ }
31
+
32
+ // Auto-generate testid for common component types to support testing
33
+ if (type === 'container') return 'container'
34
+ if (type === 'flex') return 'flex'
35
+ if (type === 'text') return 'text'
36
+
37
+ return undefined
38
+ }
39
+
40
+ /**
41
+ * Component types that have corresponding CSS classes
42
+ * These types will automatically get their type name added as a CSS class
43
+ */
44
+ const COMPONENT_TYPES_WITH_CSS_CLASSES = new Set(['card', 'badge'])
45
+
46
+ /**
47
+ * Build final className based on component type
48
+ */
49
+ export function buildClassName(
50
+ type: string,
51
+ substitutedProps?: Record<string, unknown>
52
+ ): string | undefined {
53
+ if (type === 'flex') {
54
+ return [buildFlexClasses(substitutedProps), substitutedProps?.className]
55
+ .filter(Boolean)
56
+ .join(' ')
57
+ }
58
+
59
+ if (type === 'grid') {
60
+ return [buildGridClasses(substitutedProps), substitutedProps?.className]
61
+ .filter(Boolean)
62
+ .join(' ')
63
+ }
64
+
65
+ // Auto-apply component type as CSS class for types with CSS definitions
66
+ if (COMPONENT_TYPES_WITH_CSS_CLASSES.has(type)) {
67
+ return [type, substitutedProps?.className].filter(Boolean).join(' ')
68
+ }
69
+
70
+ return substitutedProps?.className as string | undefined
71
+ }
72
+
73
+ /**
74
+ * Build component data attributes
75
+ */
76
+ function buildComponentDataAttrs(componentName?: string, type?: string) {
77
+ if (!componentName) {
78
+ return {}
79
+ }
80
+ return {
81
+ 'data-component': componentName,
82
+ 'data-type': type,
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Build translation data attributes
88
+ */
89
+ function buildTranslationDataAttrs(
90
+ firstTranslationKey?: string,
91
+ translationData?: Record<string, unknown>
92
+ ) {
93
+ if (!firstTranslationKey || !translationData) {
94
+ return {}
95
+ }
96
+ return {
97
+ 'data-translation-key': firstTranslationKey,
98
+ 'data-translations': JSON.stringify(translationData),
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Build empty content style
104
+ */
105
+ function buildEmptyStyle(
106
+ componentName?: string,
107
+ type?: string,
108
+ hasContent?: boolean,
109
+ styleWithShadow?: Record<string, unknown>
110
+ ) {
111
+ if (componentName && !hasContent) {
112
+ return {
113
+ ...styleWithShadow,
114
+ minHeight: '1px',
115
+ minWidth: '1px',
116
+ display: 'inline-block',
117
+ }
118
+ }
119
+ if (!componentName && type === 'grid' && !hasContent) {
120
+ return {
121
+ ...styleWithShadow,
122
+ minHeight: '100px',
123
+ minWidth: '100px',
124
+ }
125
+ }
126
+ return styleWithShadow
127
+ }
128
+
129
+ /**
130
+ * Build element props including data attributes, ARIA, and styling
131
+ */
132
+ export function buildElementProps({
133
+ component,
134
+ substitutedProps,
135
+ finalClassName,
136
+ styleWithShadow,
137
+ testId,
138
+ componentName,
139
+ firstTranslationKey,
140
+ translationData,
141
+ hasScrollAnimation,
142
+ hasContent,
143
+ children,
144
+ }: {
145
+ readonly component: Component
146
+ readonly substitutedProps?: Record<string, unknown>
147
+ readonly finalClassName?: string
148
+ readonly styleWithShadow?: Record<string, unknown>
149
+ readonly testId?: string
150
+ readonly componentName?: string
151
+ readonly firstTranslationKey?: string
152
+ readonly translationData?: Record<string, unknown>
153
+ readonly hasScrollAnimation?: boolean
154
+ readonly hasContent?: boolean
155
+ readonly children?: readonly (Component | string)[]
156
+ }): Record<string, unknown> {
157
+ const { type } = component
158
+ const hasChildren = Boolean(children?.length)
159
+ const finalStyle = buildEmptyStyle(componentName, type, hasContent, styleWithShadow)
160
+
161
+ return {
162
+ ...substitutedProps,
163
+ className: finalClassName,
164
+ ...(finalStyle && { style: finalStyle }),
165
+ ...(testId && { 'data-testid': testId }),
166
+ ...buildComponentDataAttrs(componentName, type),
167
+ ...(componentName && hasChildren && CONTAINER_TYPES.includes(type) && { role: 'group' }),
168
+ ...buildTranslationDataAttrs(firstTranslationKey, translationData),
169
+ ...(hasScrollAnimation && { 'data-scroll-animation': 'scale-up' }),
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Convert custom props to data-* attributes for badge components
175
+ * Standard HTML attributes (className, style, id, etc.) pass through unchanged
176
+ *
177
+ * Re-exported from prop-conversion module for backward compatibility
178
+ */
179
+ export { convertBadgeProps } from './prop-conversion'
@@ -0,0 +1,9 @@
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
+ // Barrel export for props building utilities
9
+ // Files will be added during migration
@@ -0,0 +1,171 @@
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 { toKebabCase } from '@/presentation/utils/string-utils'
9
+
10
+ /**
11
+ * HTML/React props that should not be converted to data attributes
12
+ * This includes standard HTML attributes, React-specific props, ARIA attributes,
13
+ * event handlers, and existing data-* attributes
14
+ */
15
+ export const RESERVED_PROPS = new Set([
16
+ // Standard HTML/React props
17
+ 'className',
18
+ 'style',
19
+ 'children',
20
+ 'ref',
21
+ 'key',
22
+ 'id',
23
+ 'role',
24
+ 'title',
25
+ 'alt',
26
+ 'src',
27
+ 'href',
28
+ 'type',
29
+ 'value',
30
+ 'placeholder',
31
+ 'disabled',
32
+ 'checked',
33
+ 'selected',
34
+ 'multiple',
35
+ 'autoFocus',
36
+ 'autoComplete',
37
+ 'readOnly',
38
+ 'required',
39
+ 'maxLength',
40
+ 'minLength',
41
+ 'pattern',
42
+ 'name',
43
+ 'tabIndex',
44
+ // ARIA attributes
45
+ 'aria-label',
46
+ 'aria-labelledby',
47
+ 'aria-describedby',
48
+ // Event handlers
49
+ 'onClick',
50
+ 'onChange',
51
+ 'onSubmit',
52
+ 'onFocus',
53
+ 'onBlur',
54
+ 'onKeyDown',
55
+ 'onKeyUp',
56
+ 'onKeyPress',
57
+ 'onMouseEnter',
58
+ 'onMouseLeave',
59
+ // Application-specific props
60
+ 'level', // For text elements (h1-h6, p, label)
61
+ 'animation',
62
+ 'data-testid',
63
+ 'data-component',
64
+ 'data-type',
65
+ 'data-translation-key',
66
+ 'data-translations',
67
+ 'data-scroll-animation',
68
+ 'data-scroll-threshold',
69
+ 'data-scroll-delay',
70
+ 'data-scroll-duration',
71
+ 'data-scroll-once',
72
+ 'data-i18n-content',
73
+ ])
74
+
75
+ /**
76
+ * Checks if a prop should be skipped during conversion
77
+ */
78
+ function shouldSkipProp(key: string, _value: unknown): boolean {
79
+ return RESERVED_PROPS.has(key) || key.startsWith('data-') || key.startsWith('aria-')
80
+ }
81
+
82
+ /**
83
+ * Converts a value to a data attribute string
84
+ */
85
+ function valueToDataAttributeString(value: unknown): string | undefined {
86
+ if (typeof value === 'string') {
87
+ return value
88
+ }
89
+ if (typeof value === 'number' || typeof value === 'boolean') {
90
+ return value.toString()
91
+ }
92
+ if (Array.isArray(value)) {
93
+ return JSON.stringify(value)
94
+ }
95
+ if (typeof value === 'object' && value !== null) {
96
+ return JSON.stringify(value)
97
+ }
98
+ return undefined
99
+ }
100
+
101
+ /**
102
+ * Converts a single prop value to a data attribute value
103
+ *
104
+ * @param key - Prop key
105
+ * @param value - Prop value
106
+ * @returns Data attribute entry or undefined if should be skipped
107
+ */
108
+ function convertPropToDataAttribute(
109
+ key: string,
110
+ value: unknown
111
+ ): readonly [string, string] | undefined {
112
+ // Skip reserved props, data-*, aria-*, strings, and style objects
113
+ if (shouldSkipProp(key, value) || key === 'style') return undefined
114
+
115
+ const stringValue = valueToDataAttributeString(value)
116
+ return stringValue ? ([`data-${toKebabCase(key)}`, stringValue] as const) : undefined
117
+ }
118
+
119
+ /**
120
+ * Converts custom props to data attributes
121
+ * Handles numeric, boolean, array, and object values
122
+ *
123
+ * This is the comprehensive version that handles all prop types.
124
+ * Use this for components that need full prop conversion (e.g., dynamic components).
125
+ *
126
+ * @param props - Props object to convert
127
+ * @returns Object with data attributes for custom props
128
+ */
129
+ export function convertCustomPropsToDataAttributes(
130
+ props: Record<string, unknown> | undefined
131
+ ): Record<string, string> {
132
+ if (!props) return {}
133
+
134
+ return Object.entries(props).reduce<Record<string, string>>((acc, [key, value]) => {
135
+ const dataAttr = convertPropToDataAttribute(key, value)
136
+ return dataAttr ? { ...acc, [dataAttr[0]]: dataAttr[1] } : acc
137
+ }, {})
138
+ }
139
+
140
+ /**
141
+ * Convert custom props to data-* attributes (simplified version for badges)
142
+ * Standard HTML attributes (className, style, id, etc.) pass through unchanged
143
+ * String values are converted to data attributes (unlike the comprehensive version)
144
+ *
145
+ * This is used for badge components where all custom props should become data attributes.
146
+ *
147
+ * @param elementProps - Element props to convert
148
+ * @returns Props with custom values converted to data attributes
149
+ */
150
+ export function convertBadgeProps(elementProps: Record<string, unknown>): Record<string, unknown> {
151
+ const standardHtmlAttrs = new Set([
152
+ 'className',
153
+ 'style',
154
+ 'id',
155
+ 'role',
156
+ 'data-testid',
157
+ 'data-component',
158
+ 'data-type',
159
+ 'data-translation-key',
160
+ 'data-translations',
161
+ ])
162
+
163
+ return Object.entries(elementProps).reduce<Record<string, unknown>>((acc, [key, value]) => {
164
+ // Keep standard HTML attrs, data-*, and aria-* unchanged
165
+ if (standardHtmlAttrs.has(key) || key.startsWith('data-') || key.startsWith('aria-')) {
166
+ return { ...acc, [key]: value }
167
+ }
168
+ // Convert custom props to data-* attributes (including strings)
169
+ return { ...acc, [`data-${toKebabCase(key)}`]: value }
170
+ }, {})
171
+ }
@@ -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 { Interactions } from '@/domain/models/app/page/common/interactions/interactions'
9
+ import type { Component } from '@/domain/models/app/page/sections'
10
+ import type { Theme } from '@/domain/models/app/theme'
11
+
12
+ /**
13
+ * Configuration for building test IDs
14
+ * Replaces 5 parameters with a single config object
15
+ */
16
+ export interface TestIdConfig {
17
+ readonly type: Component['type']
18
+ readonly componentName?: string
19
+ readonly componentInstanceIndex?: number
20
+ readonly substitutedProps?: Record<string, unknown>
21
+ readonly childIndex?: number
22
+ }
23
+
24
+ /**
25
+ * Configuration for building element props
26
+ * Replaces 12 parameters with a single config object
27
+ */
28
+ export interface ElementPropsConfig {
29
+ readonly type: Component['type']
30
+ readonly substitutedProps: Record<string, unknown> | undefined
31
+ readonly finalClassName: string | undefined
32
+ readonly styleWithShadow: Record<string, unknown> | undefined
33
+ readonly componentName: string | undefined
34
+ readonly componentInstanceIndex: number | undefined
35
+ readonly firstTranslationKey: string | undefined
36
+ readonly translationData: Record<string, string> | undefined
37
+ readonly hasContent: boolean
38
+ readonly hasChildren: boolean
39
+ readonly theme: Theme | undefined
40
+ readonly childIndex?: number
41
+ readonly interactions?: Interactions
42
+ }
@@ -0,0 +1,296 @@
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 { calculateTotalDelay } from '../utils/time-parser'
9
+ import { convertCustomPropsToDataAttributes } from './prop-conversion'
10
+ import type { ElementPropsConfig, TestIdConfig } from './props-builder-config'
11
+
12
+ /**
13
+ * Component types that should receive role="group" when used as component references with children
14
+ */
15
+ const CONTAINER_TYPES = ['div', 'container', 'flex', 'grid', 'card', 'badge'] as const
16
+
17
+ /**
18
+ * Builds test ID for component references
19
+ */
20
+ function buildComponentTestId(
21
+ componentName: string,
22
+ componentInstanceIndex: number | undefined
23
+ ): string {
24
+ return componentInstanceIndex !== undefined
25
+ ? `component-${componentName}-${componentInstanceIndex}`
26
+ : `component-${componentName}`
27
+ }
28
+
29
+ /**
30
+ * Auto-generates test ID for common component types
31
+ */
32
+ function buildDefaultTestId(type: string): string | undefined {
33
+ const typeMap: Record<string, string> = {
34
+ container: 'container',
35
+ flex: 'flex',
36
+ 'hero-section': 'hero-section',
37
+ hero: 'hero',
38
+ text: 'text',
39
+ icon: 'icon',
40
+ }
41
+ return typeMap[type]
42
+ }
43
+
44
+ /**
45
+ * Build test ID for component using config object
46
+ */
47
+
48
+ function buildTestId(config: TestIdConfig): string | undefined {
49
+ const { type, componentName, componentInstanceIndex, substitutedProps, childIndex } = config
50
+
51
+ if (componentName) {
52
+ return buildComponentTestId(componentName, componentInstanceIndex)
53
+ }
54
+
55
+ if (substitutedProps?.['data-testid']) {
56
+ return substitutedProps['data-testid'] as string
57
+ }
58
+
59
+ if (childIndex !== undefined) {
60
+ return `child-${childIndex}`
61
+ }
62
+
63
+ return buildDefaultTestId(type)
64
+ }
65
+
66
+ /**
67
+ * Build element props with all attributes using config object
68
+ * Complexity reduced by extracting helper functions
69
+ */
70
+ export function buildElementProps(config: ElementPropsConfig): Record<string, unknown> {
71
+ return buildElementPropsFromConfig(config)
72
+ }
73
+
74
+ /**
75
+ * Build element props from config object
76
+ */
77
+ function buildElementPropsFromConfig(config: ElementPropsConfig): Record<string, unknown> {
78
+ const hasScrollAnimation = config.type === 'card' && Boolean(config.theme?.animations?.scaleUp)
79
+ const testId = buildTestId({
80
+ type: config.type,
81
+ componentName: config.componentName,
82
+ componentInstanceIndex: config.componentInstanceIndex,
83
+ substitutedProps: config.substitutedProps,
84
+ childIndex: config.childIndex,
85
+ })
86
+
87
+ // Build all props
88
+ const coreProps = buildCoreProps(config, testId)
89
+ const componentDataProps = buildComponentDataProps(config)
90
+ const translationProps = buildTranslationProps(config)
91
+ const animationProps = buildAnimationProps(hasScrollAnimation)
92
+ const entranceProps = buildEntranceInteractionProps(config)
93
+ const scrollProps = buildScrollInteractionProps(config)
94
+ const emptyStyleProps = buildEmptyElementStyles(config)
95
+
96
+ // Merge all style objects to prevent overwrites
97
+ const mergedStyle = {
98
+ ...(coreProps.style as Record<string, unknown> | undefined),
99
+ ...(entranceProps.style as Record<string, unknown> | undefined),
100
+ ...(scrollProps.style as Record<string, unknown> | undefined),
101
+ ...(emptyStyleProps.style as Record<string, unknown> | undefined),
102
+ }
103
+
104
+ // Remove style from individual prop objects
105
+ const { style: _coreStyle, ...corePropsWithoutStyle } = coreProps
106
+ const { style: _entranceStyle, ...entrancePropsWithoutStyle } = entranceProps
107
+ const { style: _scrollStyle, ...scrollPropsWithoutStyle } = scrollProps
108
+ const { style: _emptyStyle, ...emptyStylePropsWithoutStyle } = emptyStyleProps
109
+
110
+ // Remove animation and style props from substitutedProps (already applied to style object)
111
+ const {
112
+ animation: _animation,
113
+ style: _style,
114
+ ...substitutedPropsWithoutAnimation
115
+ } = config.substitutedProps || {}
116
+
117
+ // Convert custom props to data attributes
118
+ const customDataAttributes = convertCustomPropsToDataAttributes(substitutedPropsWithoutAnimation)
119
+
120
+ return {
121
+ ...substitutedPropsWithoutAnimation,
122
+ ...corePropsWithoutStyle,
123
+ ...componentDataProps,
124
+ ...translationProps,
125
+ ...animationProps,
126
+ ...entrancePropsWithoutStyle,
127
+ ...scrollPropsWithoutStyle,
128
+ ...emptyStylePropsWithoutStyle,
129
+ ...customDataAttributes,
130
+ ...(Object.keys(mergedStyle).length > 0 && { style: mergedStyle }),
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Build core props (className, style, data-testid)
136
+ * Handles animation prop by extracting it from substitutedProps and merging into style
137
+ *
138
+ * Note: data-component-type removed for clean production HTML
139
+ * This debug attribute was useful during development but adds unnecessary
140
+ * markup to static sites and production builds
141
+ */
142
+ function buildCoreProps(
143
+ config: ElementPropsConfig,
144
+ testId: string | undefined
145
+ ): Record<string, unknown> {
146
+ // Extract animation prop if present (after theme token substitution)
147
+ const animationProp = config.substitutedProps?.animation
148
+ const hasAnimationProp = typeof animationProp === 'string'
149
+
150
+ // Merge animation into style object if present
151
+ const styleWithAnimation = hasAnimationProp
152
+ ? { ...config.styleWithShadow, animation: animationProp }
153
+ : config.styleWithShadow
154
+
155
+ return {
156
+ className: config.finalClassName,
157
+ ...(styleWithAnimation && { style: styleWithAnimation }),
158
+ ...(testId && { 'data-testid': testId }),
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Build component-related data props (data-component, data-type, role)
164
+ */
165
+ function buildComponentDataProps(config: ElementPropsConfig): Record<string, unknown> {
166
+ if (!config.componentName) return {}
167
+
168
+ return {
169
+ 'data-component': config.componentName,
170
+ 'data-type': config.type,
171
+ ...(config.hasChildren &&
172
+ CONTAINER_TYPES.includes(config.type) && {
173
+ role: 'group',
174
+ }),
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Build translation props (data-translation-key, data-translations)
180
+ */
181
+ function buildTranslationProps(config: ElementPropsConfig): Record<string, unknown> {
182
+ if (!config.firstTranslationKey || !config.translationData) return {}
183
+
184
+ return {
185
+ 'data-translation-key': config.firstTranslationKey,
186
+ 'data-translations': JSON.stringify(config.translationData),
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Build animation props (data-scroll-animation)
192
+ */
193
+ function buildAnimationProps(hasScrollAnimation: boolean): Record<string, unknown> {
194
+ if (!hasScrollAnimation) return {}
195
+
196
+ return {
197
+ 'data-scroll-animation': 'scale-up',
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Build entrance interaction props (style for animations)
203
+ */
204
+ function buildEntranceInteractionProps(config: ElementPropsConfig): Record<string, unknown> {
205
+ if (!config.interactions?.entrance) return {}
206
+
207
+ const { delay, duration, stagger } = config.interactions.entrance
208
+
209
+ // Calculate total delay including stagger
210
+ const totalDelay = calculateTotalDelay(delay, stagger, config.childIndex)
211
+
212
+ // Build animation styles immutably
213
+ const delayStyle = totalDelay ? { animationDelay: totalDelay } : {}
214
+ const durationStyle = duration ? { animationDuration: duration } : {}
215
+ const animationStyles = { ...delayStyle, ...durationStyle }
216
+
217
+ // Combine all props immutably
218
+ const hasAnimationStyles = Object.keys(animationStyles).length > 0
219
+ const styleProps = hasAnimationStyles
220
+ ? { style: { ...config.styleWithShadow, ...animationStyles } }
221
+ : {}
222
+
223
+ return styleProps
224
+ }
225
+
226
+ /**
227
+ * Build scroll interaction props (data-scroll-*, style for animations)
228
+ */
229
+ function buildScrollInteractionProps(config: ElementPropsConfig): Record<string, unknown> {
230
+ if (!config.interactions?.scroll) return {}
231
+
232
+ const { animation, threshold, delay, duration, once } = config.interactions.scroll
233
+
234
+ // Build base data attributes immutably
235
+ const baseProps: Record<string, unknown> = {
236
+ 'data-scroll-animation': animation,
237
+ }
238
+
239
+ const thresholdProps =
240
+ threshold !== undefined ? { 'data-scroll-threshold': threshold.toString() } : {}
241
+ const delayProps = delay ? { 'data-scroll-delay': delay } : {}
242
+ const durationProps = duration ? { 'data-scroll-duration': duration } : {}
243
+ const onceProps = once === true ? { 'data-scroll-once': once.toString() } : {}
244
+
245
+ // Build animation styles immutably
246
+ const delayStyle = delay ? { animationDelay: delay } : {}
247
+ const durationStyle = duration ? { animationDuration: duration } : {}
248
+ const animationStyles = { ...delayStyle, ...durationStyle }
249
+
250
+ // Combine all props immutably
251
+ const hasAnimationStyles = Object.keys(animationStyles).length > 0
252
+ const styleProps = hasAnimationStyles
253
+ ? { style: { ...config.styleWithShadow, ...animationStyles } }
254
+ : {}
255
+
256
+ return {
257
+ ...baseProps,
258
+ ...thresholdProps,
259
+ ...delayProps,
260
+ ...durationProps,
261
+ ...onceProps,
262
+ ...styleProps,
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Build styles for empty elements (components and grids without content)
268
+ */
269
+ function buildEmptyElementStyles(config: ElementPropsConfig): Record<string, unknown> {
270
+ if (config.hasContent) return {}
271
+
272
+ // Component reference or child without content
273
+ if (config.componentName || config.childIndex !== undefined) {
274
+ return {
275
+ style: {
276
+ ...config.styleWithShadow,
277
+ minHeight: '1px',
278
+ minWidth: '1px',
279
+ display: 'inline-block',
280
+ },
281
+ }
282
+ }
283
+
284
+ // Grid without content and not a component reference
285
+ if (!config.componentName && config.type === 'grid') {
286
+ return {
287
+ style: {
288
+ ...config.styleWithShadow,
289
+ minHeight: '100px',
290
+ minWidth: '100px',
291
+ },
292
+ }
293
+ }
294
+
295
+ return {}
296
+ }