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,198 @@
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 { resolveChildTranslation } from './translation-handler'
9
+ import type { Languages } from '@/domain/models/app/languages'
10
+
11
+ /**
12
+ * Builds i18n content data attribute for client-side language switching (functional approach)
13
+ *
14
+ * Extracts content translations from i18n object and serializes to JSON.
15
+ * Includes default language content as fallback.
16
+ *
17
+ * @param i18n - Component i18n translations object
18
+ * @param defaultContent - Default content for base language
19
+ * @param defaultLang - Default language code
20
+ * @returns JSON-stringified i18n content data or undefined if no translations
21
+ */
22
+ function buildI18nContentAttribute(
23
+ i18n: Record<string, unknown>,
24
+ defaultContent: string,
25
+ defaultLang: string | undefined
26
+ ): string | undefined {
27
+ // Extract content from i18n object using functional approach (reduce instead of for loop)
28
+ const i18nContentData = Object.entries(i18n).reduce<Record<string, string>>(
29
+ (acc, [lang, value]) => {
30
+ if (typeof value === 'object' && value !== null && 'content' in value && value.content) {
31
+ return { ...acc, [lang]: value.content as string }
32
+ }
33
+ return acc
34
+ },
35
+ {}
36
+ )
37
+
38
+ // Add default language content if not already present
39
+ const contentWithDefault =
40
+ defaultLang && !i18nContentData[defaultLang]
41
+ ? { ...i18nContentData, [defaultLang]: defaultContent }
42
+ : i18nContentData
43
+
44
+ // Only return attribute if there are translations
45
+ return Object.keys(contentWithDefault).length > 0 ? JSON.stringify(contentWithDefault) : undefined
46
+ }
47
+
48
+ /**
49
+ * Resolves component content with i18n priority
50
+ *
51
+ * Priority: component.i18n[lang].content > $t: pattern > content
52
+ *
53
+ * @param content - Base content string
54
+ * @param i18n - Component i18n translations
55
+ * @param currentLang - Current language code
56
+ * @param languages - Languages configuration
57
+ * @returns Resolved content string
58
+ */
59
+ function resolveComponentContent(
60
+ content: string | Record<string, unknown> | undefined,
61
+ i18n: Record<string, unknown> | undefined,
62
+ currentLang: string | undefined,
63
+ languages: Languages | undefined
64
+ ): string | undefined {
65
+ // If content is an object (structured content like { button: {...} }), return undefined
66
+ // The component renderer will handle structured content directly
67
+ if (content && typeof content === 'object') {
68
+ return undefined
69
+ }
70
+
71
+ if (i18n && currentLang) {
72
+ const langData = i18n[currentLang]
73
+ if (
74
+ langData &&
75
+ typeof langData === 'object' &&
76
+ 'content' in langData &&
77
+ typeof langData.content === 'string'
78
+ ) {
79
+ return langData.content
80
+ }
81
+ }
82
+ if (content) {
83
+ return resolveChildTranslation(content, currentLang, languages)
84
+ }
85
+ return content
86
+ }
87
+
88
+ /**
89
+ * Builds final element props with i18n data attribute
90
+ *
91
+ * @param baseProps - Base element props
92
+ * @param i18nAttribute - Optional i18n content JSON string
93
+ * @returns Element props with i18n data merged if present
94
+ */
95
+ function buildFinalElementProps(
96
+ baseProps: Record<string, unknown>,
97
+ i18nAttribute: string | undefined
98
+ ): Record<string, unknown> {
99
+ return i18nAttribute ? { ...baseProps, 'data-i18n-content': i18nAttribute } : baseProps
100
+ }
101
+
102
+ /**
103
+ * Configuration for resolving i18n content
104
+ */
105
+ export interface ResolveI18nContentConfig {
106
+ readonly content: string | Record<string, unknown> | undefined
107
+ readonly i18n: Record<string, unknown> | undefined
108
+ readonly currentLang: string | undefined
109
+ readonly languages: Languages | undefined
110
+ readonly elementProps: Record<string, unknown>
111
+ readonly elementPropsWithSpacing: Record<string, unknown>
112
+ }
113
+
114
+ /**
115
+ * Resolves i18n content and builds element props with i18n data attribute (config object signature)
116
+ */
117
+ export function resolveI18nContent(config: ResolveI18nContentConfig): {
118
+ readonly resolvedContent: string | undefined
119
+ readonly finalElementProps: Record<string, unknown>
120
+ readonly finalElementPropsWithSpacing: Record<string, unknown>
121
+ }
122
+
123
+ /**
124
+ * Resolves i18n content and builds element props with i18n data attribute (individual parameters signature)
125
+ */
126
+ // eslint-disable-next-line max-params -- Function overload signature
127
+ export function resolveI18nContent(
128
+ content: string | Record<string, unknown> | undefined,
129
+ i18n: Record<string, unknown> | undefined,
130
+ currentLang: string | undefined,
131
+ languages: Languages | undefined,
132
+ elementProps: Record<string, unknown>,
133
+ elementPropsWithSpacing: Record<string, unknown>
134
+ ): {
135
+ readonly resolvedContent: string | undefined
136
+ readonly finalElementProps: Record<string, unknown>
137
+ readonly finalElementPropsWithSpacing: Record<string, unknown>
138
+ }
139
+
140
+ /**
141
+ * Implementation
142
+ */
143
+ // eslint-disable-next-line max-params -- Implementation handles both signatures
144
+ export function resolveI18nContent(
145
+ configOrContent: ResolveI18nContentConfig | string | Record<string, unknown> | undefined,
146
+ i18n?: Record<string, unknown>,
147
+ currentLang?: string,
148
+ languages?: Languages,
149
+ elementProps?: Record<string, unknown>,
150
+ elementPropsWithSpacing?: Record<string, unknown>
151
+ ): {
152
+ readonly resolvedContent: string | undefined
153
+ readonly finalElementProps: Record<string, unknown>
154
+ readonly finalElementPropsWithSpacing: Record<string, unknown>
155
+ } {
156
+ // Support both config object and individual parameters
157
+ const config: ResolveI18nContentConfig =
158
+ configOrContent && typeof configOrContent === 'object' && 'content' in configOrContent
159
+ ? (configOrContent as ResolveI18nContentConfig)
160
+ : {
161
+ content: configOrContent as string | Record<string, unknown> | undefined,
162
+ i18n: i18n!,
163
+ currentLang: currentLang!,
164
+ languages: languages!,
165
+ elementProps: elementProps!,
166
+ elementPropsWithSpacing: elementPropsWithSpacing!,
167
+ }
168
+
169
+ const {
170
+ content,
171
+ i18n: i18nConfig,
172
+ currentLang: lang,
173
+ languages: langs,
174
+ elementProps: props,
175
+ elementPropsWithSpacing: propsWithSpacing,
176
+ } = config
177
+
178
+ // Resolve content with i18n priority: component.i18n[lang].content > $t: pattern > content
179
+ const resolvedContent = resolveComponentContent(content, i18nConfig, lang, langs)
180
+
181
+ // Build i18n content data attribute and merge into element props (functional approach)
182
+ const i18nContentAttribute =
183
+ i18nConfig && content
184
+ ? buildI18nContentAttribute(i18nConfig, content as string, langs?.default)
185
+ : undefined
186
+
187
+ const finalElementProps = buildFinalElementProps(props, i18nContentAttribute)
188
+ const finalElementPropsWithSpacing = buildFinalElementProps(
189
+ propsWithSpacing,
190
+ i18nContentAttribute
191
+ )
192
+
193
+ return {
194
+ resolvedContent,
195
+ finalElementProps,
196
+ finalElementPropsWithSpacing,
197
+ }
198
+ }
@@ -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 translation utilities
9
+ // Files will be added during migration
@@ -0,0 +1,143 @@
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 {
9
+ collectTranslationsForKey,
10
+ resolveTranslationPattern,
11
+ } from '@/presentation/translations/translation-resolver'
12
+ import type { Languages } from '@/domain/models/app/languages'
13
+ import type { Component } from '@/domain/models/app/page/sections'
14
+
15
+ /**
16
+ * Find first translation key in children
17
+ */
18
+ export function findFirstTranslationKey(
19
+ children: readonly (Component | string)[] | undefined
20
+ ): string | undefined {
21
+ return children
22
+ ?.find(
23
+ (child: Component | string): child is string =>
24
+ typeof child === 'string' && child.startsWith('$t:')
25
+ )
26
+ ?.slice(3) // Remove '$t:' prefix
27
+ }
28
+
29
+ /**
30
+ * Extract translation key from content string
31
+ * Returns the key portion after $t: prefix if content is a translation token
32
+ */
33
+ export function extractTranslationKeyFromContent(content: string | undefined): string | undefined {
34
+ if (!content || typeof content !== 'string') return undefined
35
+ return content.startsWith('$t:') ? content.slice(3) : undefined
36
+ }
37
+
38
+ /**
39
+ * Get current language with fallback
40
+ */
41
+ export function getCurrentLanguage(
42
+ currentLang: string | undefined,
43
+ languages: Languages | undefined
44
+ ): string {
45
+ return currentLang || languages?.default || 'en-US'
46
+ }
47
+
48
+ /**
49
+ * Resolve translation pattern for a child element
50
+ */
51
+ export function resolveChildTranslation(
52
+ child: string,
53
+ currentLang: string | undefined,
54
+ languages: Languages | undefined
55
+ ): string {
56
+ const lang = getCurrentLanguage(currentLang, languages)
57
+ return resolveTranslationPattern(child, lang, languages)
58
+ }
59
+
60
+ /**
61
+ * Collect translation data for a key
62
+ */
63
+ export function getTranslationData(
64
+ translationKey: string | undefined,
65
+ languages: Languages | undefined
66
+ ): Record<string, string> | undefined {
67
+ return translationKey ? collectTranslationsForKey(translationKey, languages) : undefined
68
+ }
69
+
70
+ /**
71
+ * Substitutes translation tokens in props recursively
72
+ *
73
+ * Walks through props object and replaces all $t:key patterns with actual translations.
74
+ * Handles nested objects (e.g., style props) recursively.
75
+ *
76
+ * @param props - Component props that may contain translation tokens
77
+ * @param currentLang - Current language code
78
+ * @param languages - Languages configuration
79
+ * @returns Props with translation tokens replaced
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * const languages = {
84
+ * default: 'en',
85
+ * translations: { 'en': { 'close.label': 'Close dialog' } }
86
+ * }
87
+ * const props = {
88
+ * 'aria-label': '$t:close.label',
89
+ * title: 'Static text'
90
+ * }
91
+ * substitutePropsTranslationTokens(props, 'en-US', languages)
92
+ * // {
93
+ * // 'aria-label': 'Close dialog',
94
+ * // title: 'Static text'
95
+ * // }
96
+ * ```
97
+ */
98
+ export function substitutePropsTranslationTokens(
99
+ props: Record<string, unknown> | undefined,
100
+ currentLang: string | undefined,
101
+ languages: Languages | undefined
102
+ ): Record<string, unknown> | undefined {
103
+ if (!props || !languages) {
104
+ return props
105
+ }
106
+
107
+ const lang = getCurrentLanguage(currentLang, languages)
108
+
109
+ // Use functional Object.entries + reduce for immutable transformation
110
+ return Object.entries(props).reduce<Record<string, unknown>>((acc, [key, value]) => {
111
+ if (typeof value === 'string') {
112
+ return { ...acc, [key]: resolveTranslationPattern(value, lang, languages) }
113
+ } else if (Array.isArray(value)) {
114
+ // Recursively handle arrays (like navigation links, footer columns)
115
+ return {
116
+ ...acc,
117
+ [key]: value.map((item) =>
118
+ item && typeof item === 'object' && !Array.isArray(item)
119
+ ? substitutePropsTranslationTokens(
120
+ item as Record<string, unknown>,
121
+ currentLang,
122
+ languages
123
+ )
124
+ : typeof item === 'string'
125
+ ? resolveTranslationPattern(item, lang, languages)
126
+ : item
127
+ ),
128
+ }
129
+ } else if (value && typeof value === 'object') {
130
+ // Recursively handle nested objects (like style props)
131
+ return {
132
+ ...acc,
133
+ [key]: substitutePropsTranslationTokens(
134
+ value as Record<string, unknown>,
135
+ currentLang,
136
+ languages
137
+ ),
138
+ }
139
+ } else {
140
+ return { ...acc, [key]: value }
141
+ }
142
+ }, {})
143
+ }
@@ -0,0 +1,225 @@
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
+ import type { Component } from '@/domain/models/app/page/sections'
10
+
11
+ /**
12
+ * Replaces variable placeholders in a string with actual values
13
+ *
14
+ * Uses regex with word boundary checking to avoid partial matches.
15
+ * For example, $icon will not match inside $iconColor.
16
+ *
17
+ * @param value - String that may contain $variable placeholders
18
+ * @param vars - Variables for substitution
19
+ * @returns String with all variables replaced
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * replaceVariables('$title', { title: 'Hello' }) // 'Hello'
24
+ * replaceVariables('$price/month', { price: '49' }) // '49/month'
25
+ * replaceVariables('box-$variant', { variant: 'primary' }) // 'box-primary'
26
+ * ```
27
+ */
28
+ function replaceVariables(value: string, vars: Record<string, string | number | boolean>): string {
29
+ // Use regex with word boundary to avoid partial matches
30
+ // e.g., $icon should not match inside $iconColor
31
+ return Object.entries(vars).reduce<string>((str, [varName, varValue]) => {
32
+ // Create regex that matches $varName as a whole word
33
+ // Use negative lookahead to ensure we don't match if followed by alphanumeric or underscore
34
+ const regex = new RegExp(`\\$${varName}(?![a-zA-Z0-9_])`, 'g')
35
+ return str.replace(regex, String(varValue))
36
+ }, value)
37
+ }
38
+
39
+ /**
40
+ * Substitutes variable values in a value
41
+ *
42
+ * Replaces `$variableName` patterns with actual variable values.
43
+ * Supports both full replacement ($title → 'Welcome') and partial replacement
44
+ * within strings ($price/month → '49/month').
45
+ *
46
+ * @param value - Value that may contain variable placeholders
47
+ * @param vars - Variables for substitution
48
+ * @returns Value with variables replaced
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * substituteVariableValues('$title', { title: 'Hello' }) // 'Hello'
53
+ * substituteVariableValues('$price/month', { price: '49' }) // '49/month'
54
+ * substituteVariableValues('static', { title: 'Hello' }) // 'static'
55
+ * substituteVariableValues(123, { title: 'Hello' }) // 123
56
+ * ```
57
+ */
58
+ export function substituteVariableValues(
59
+ value: unknown,
60
+ vars?: Record<string, string | number | boolean>
61
+ ): unknown {
62
+ if (typeof value !== 'string') {
63
+ return value
64
+ }
65
+
66
+ if (!vars || !value.includes('$')) {
67
+ return value
68
+ }
69
+
70
+ return replaceVariables(value, vars)
71
+ }
72
+
73
+ /**
74
+ * Substitutes variables in component children recursively
75
+ *
76
+ * Pure function that walks through the children tree and replaces $variable
77
+ * placeholders with actual values from the vars object.
78
+ *
79
+ * @param children - Array of children (can be strings or components)
80
+ * @param vars - Variables for substitution
81
+ * @returns Children with variables substituted
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * substituteChildrenVariables(
86
+ * [
87
+ * '$greeting',
88
+ * { type: 'span', content: '$name' }
89
+ * ],
90
+ * { greeting: 'Hello', name: 'World' }
91
+ * )
92
+ * // ['Hello', { type: 'span', content: 'World' }]
93
+ * ```
94
+ */
95
+ export function substituteChildrenVariables(
96
+ children: ReadonlyArray<Component | string> | undefined,
97
+ vars?: Record<string, string | number | boolean>
98
+ ): ReadonlyArray<Component | string> | undefined {
99
+ if (!children || !vars) {
100
+ return children
101
+ }
102
+
103
+ return children.map((child) => {
104
+ // If child is a string, apply variable substitution
105
+ if (typeof child === 'string') {
106
+ const substituted = substituteVariableValues(child, vars)
107
+ return substituted as string
108
+ }
109
+
110
+ // If child is a component, recursively substitute in its props, children and content
111
+ const substitutedProps = substitutePropsVariables(child.props, vars)
112
+ const substitutedChildren = substituteChildrenVariables(child.children, vars)
113
+ const substitutedContent =
114
+ typeof child.content === 'string'
115
+ ? (substituteVariableValues(child.content, vars) as string)
116
+ : child.content
117
+
118
+ return {
119
+ ...child,
120
+ props: substitutedProps,
121
+ children: substitutedChildren,
122
+ content: substitutedContent,
123
+ }
124
+ })
125
+ }
126
+
127
+ /**
128
+ * Converts camelCase prop names to kebab-case for HTML attributes
129
+ *
130
+ * React JSX expects certain attributes in kebab-case (aria-*, data-*),
131
+ * but our schema may define them in camelCase for convenience.
132
+ * This function normalizes prop names to the format React expects.
133
+ *
134
+ * Handles both camelCase (ariaLabel) and already-kebab-case (aria-label) inputs.
135
+ * If prop is already in kebab-case, it returns unchanged to avoid double-conversion.
136
+ *
137
+ * @param key - Property key (potentially in camelCase or kebab-case)
138
+ * @returns Normalized key (kebab-case for aria/data attributes)
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * normalizeAriaDataProps('ariaLabel') // 'aria-label'
143
+ * normalizeAriaDataProps('aria-label') // 'aria-label' (unchanged)
144
+ * normalizeAriaDataProps('dataTestId') // 'data-test-id'
145
+ * normalizeAriaDataProps('data-test-id') // 'data-test-id' (unchanged)
146
+ * normalizeAriaDataProps('className') // 'className' (unchanged)
147
+ * ```
148
+ */
149
+ function normalizeAriaDataProps(key: string): string {
150
+ // Convert ariaLabel → aria-label, dataTestId → data-test-id
151
+ // Check if fifth character is uppercase letter (not hyphen or other character)
152
+ // to avoid double-conversion (aria-label → aria--label)
153
+ if (key.startsWith('aria') && key.length > 4 && key[4] !== undefined && /[A-Z]/.test(key[4])) {
154
+ // ariaLabel → aria-label
155
+ const suffix = key.slice(4) // 'Label'
156
+ const kebabSuffix = toKebabCase(suffix) // 'label'
157
+ return `aria-${kebabSuffix}`
158
+ }
159
+
160
+ if (key.startsWith('data') && key.length > 4 && key[4] !== undefined && /[A-Z]/.test(key[4])) {
161
+ // dataTestId → data-test-id
162
+ const suffix = key.slice(4) // 'TestId'
163
+ const kebabSuffix = toKebabCase(suffix) // 'test-id'
164
+ return `data-${kebabSuffix}`
165
+ }
166
+
167
+ return key
168
+ }
169
+
170
+ /**
171
+ * Substitutes variables in props recursively
172
+ *
173
+ * Walks through props object and replaces all $variable strings with actual values.
174
+ * Handles nested objects (e.g., style props) recursively.
175
+ * Supports variable substitution in string values, including partial substitution
176
+ * within strings (e.g., 'box-$variant' → 'box-primary').
177
+ * Normalizes aria* and data* prop names to kebab-case for React compatibility.
178
+ *
179
+ * @param props - Component props that may contain variable placeholders
180
+ * @param vars - Variables for substitution
181
+ * @returns Props with variables replaced and prop names normalized
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * const vars = { variant: 'primary', boxId: 'main-box', label: 'Main content' }
186
+ * const props = {
187
+ * className: 'box-$variant',
188
+ * id: '$boxId',
189
+ * ariaLabel: '$label'
190
+ * }
191
+ * substitutePropsVariables(props, vars)
192
+ * // {
193
+ * // className: 'box-primary',
194
+ * // id: 'main-box',
195
+ * // 'aria-label': 'Main content'
196
+ * // }
197
+ * ```
198
+ */
199
+ export function substitutePropsVariables(
200
+ props: Record<string, unknown> | undefined,
201
+ vars?: Record<string, string | number | boolean>
202
+ ): Record<string, unknown> | undefined {
203
+ if (!props || !vars) {
204
+ return props
205
+ }
206
+
207
+ // Use functional Object.entries + reduce for immutable transformation
208
+ return Object.entries(props).reduce<Record<string, unknown>>((acc, [key, value]) => {
209
+ // Normalize aria/data prop names (ariaLabel → aria-label)
210
+ const normalizedKey = normalizeAriaDataProps(key)
211
+
212
+ if (typeof value === 'string') {
213
+ // Handle partial substitution within strings (e.g., 'box-$variant' → 'box-primary')
214
+ return { ...acc, [normalizedKey]: replaceVariables(value, vars) }
215
+ } else if (value && typeof value === 'object' && !Array.isArray(value)) {
216
+ // Recursively handle nested objects (like style props)
217
+ return {
218
+ ...acc,
219
+ [normalizedKey]: substitutePropsVariables(value as Record<string, unknown>, vars),
220
+ }
221
+ } else {
222
+ return { ...acc, [normalizedKey]: value }
223
+ }
224
+ }, {})
225
+ }
@@ -0,0 +1,82 @@
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
+ * Time string parsing and formatting utilities
10
+ *
11
+ * Provides pure functions for converting between time strings (e.g., "100ms", "0.5s")
12
+ * and millisecond values. Used for animation timing calculations.
13
+ */
14
+
15
+ /**
16
+ * Parse time string to milliseconds
17
+ *
18
+ * @param timeStr - Time string (e.g., "100ms", "0.5s", "1s")
19
+ * @returns Time in milliseconds
20
+ *
21
+ * @example
22
+ * parseTimeToMs('100ms') // => 100
23
+ * parseTimeToMs('0.5s') // => 500
24
+ * parseTimeToMs('1s') // => 1000
25
+ */
26
+ export function parseTimeToMs(timeStr: string): number {
27
+ if (timeStr.endsWith('ms')) {
28
+ return parseInt(timeStr.slice(0, -2), 10)
29
+ }
30
+ if (timeStr.endsWith('s')) {
31
+ return parseFloat(timeStr.slice(0, -1)) * 1000
32
+ }
33
+ return 0
34
+ }
35
+
36
+ /**
37
+ * Format milliseconds to time string
38
+ *
39
+ * @param ms - Time in milliseconds
40
+ * @returns Time string in milliseconds format
41
+ *
42
+ * @example
43
+ * formatMsToTime(100) // => "100ms"
44
+ * formatMsToTime(500) // => "500ms"
45
+ * formatMsToTime(1000) // => "1000ms"
46
+ */
47
+ export function formatMsToTime(ms: number): string {
48
+ return `${ms}ms`
49
+ }
50
+
51
+ /**
52
+ * Calculate total delay including stagger offset
53
+ *
54
+ * Adds stagger delay based on child index to create sequential animation effects.
55
+ * Commonly used for entrance animations of multiple child elements.
56
+ *
57
+ * @param delay - Base delay string (e.g., "100ms")
58
+ * @param stagger - Stagger delay per sibling (e.g., "50ms")
59
+ * @param childIndex - Current child index (0-based)
60
+ * @returns Total delay string or undefined if no stagger
61
+ *
62
+ * @example
63
+ * calculateTotalDelay('100ms', '50ms', 0) // => "100ms"
64
+ * calculateTotalDelay('100ms', '50ms', 1) // => "150ms"
65
+ * calculateTotalDelay('100ms', '50ms', 2) // => "200ms"
66
+ * calculateTotalDelay('100ms', undefined, 2) // => "100ms"
67
+ */
68
+ export function calculateTotalDelay(
69
+ delay: string | undefined,
70
+ stagger: string | undefined,
71
+ childIndex: number | undefined
72
+ ): string | undefined {
73
+ if (!stagger || childIndex === undefined) {
74
+ return delay
75
+ }
76
+
77
+ const baseDelayMs = delay ? parseTimeToMs(delay) : 0
78
+ const staggerMs = parseTimeToMs(stagger)
79
+ const totalDelayMs = baseDelayMs + staggerMs * childIndex
80
+
81
+ return formatMsToTime(totalDelayMs)
82
+ }