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,137 @@
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 { renderToString } from 'react-dom/server'
9
+ import { findMatchingRoute } from '@/domain/utils/route-matcher'
10
+ import { DefaultHomePage } from '@/presentation/ui/pages/DefaultHomePage'
11
+ import { DynamicPage } from '@/presentation/ui/pages/DynamicPage'
12
+ import type { App } from '@/domain/models/app'
13
+ import type { BuiltInAnalytics } from '@/domain/models/app/analytics'
14
+
15
+ /**
16
+ * Extract session timeout from analytics config, defaulting to 30 minutes
17
+ */
18
+ function extractSessionTimeout(analytics: BuiltInAnalytics | undefined): number {
19
+ if (analytics === undefined || analytics === false || analytics === true) return 30
20
+ return analytics.sessionTimeout ?? 30
21
+ }
22
+
23
+ /**
24
+ * Check if built-in analytics tracking should be injected for a given page path
25
+ *
26
+ * Returns true when analytics is configured and enabled, and the page path
27
+ * is not in the excludedPaths list.
28
+ */
29
+ function shouldInjectAnalytics(analytics: BuiltInAnalytics | undefined, pagePath: string): boolean {
30
+ if (analytics === undefined || analytics === false) return false
31
+ if (analytics === true) return true
32
+ const { excludedPaths } = analytics
33
+ if (!excludedPaths || excludedPaths.length === 0) return true
34
+ return !excludedPaths.some((pattern: string) => {
35
+ // Support simple glob patterns: * matches any segment, ** matches anything
36
+ const regex = new RegExp('^' + pattern.replace(/\*\*/g, '.*').replace(/\*/g, '[^/]*') + '$')
37
+ return regex.test(pagePath)
38
+ })
39
+ }
40
+
41
+ /**
42
+ * Renders a page by path to HTML string for server-side rendering
43
+ *
44
+ * Supports both static routes (exact match) and dynamic routes (with :param segments)
45
+ *
46
+ * @param app - Validated application data from AppSchema
47
+ * @param path - Page path to render (e.g., '/', '/about', '/blog/hello-world')
48
+ * @param detectedLanguage - Optional detected language from Accept-Language header or URL
49
+ * @returns Complete HTML document as string with DOCTYPE, or undefined if page not found
50
+ */
51
+ export function renderPageByPath(
52
+ app: App,
53
+ path: string,
54
+ detectedLanguage?: string
55
+ ): string | undefined {
56
+ // If no pages configured, return undefined
57
+ if (!app.pages || app.pages.length === 0) {
58
+ return undefined
59
+ }
60
+
61
+ // Extract all page patterns and find matching route
62
+ const pagePatterns = app.pages.map((p) => p.path)
63
+ const match = findMatchingRoute(pagePatterns, path)
64
+
65
+ if (!match) {
66
+ return undefined
67
+ }
68
+
69
+ // Get the matched page
70
+ const page = app.pages[match.index]
71
+ if (!page) {
72
+ return undefined
73
+ }
74
+
75
+ const injectAnalytics = shouldInjectAnalytics(app.analytics, page.path)
76
+ const sessionTimeout = extractSessionTimeout(app.analytics)
77
+
78
+ const html = renderToString(
79
+ <DynamicPage
80
+ page={page}
81
+ components={app.components}
82
+ theme={app.theme}
83
+ languages={app.languages}
84
+ detectedLanguage={detectedLanguage}
85
+ routeParams={match.params}
86
+ builtInAnalyticsEnabled={injectAnalytics}
87
+ builtInAnalyticsSessionTimeout={sessionTimeout}
88
+ />
89
+ )
90
+
91
+ return `<!DOCTYPE html>\n${html}`
92
+ }
93
+
94
+ /**
95
+ * Renders homepage to HTML string for server-side rendering
96
+ *
97
+ * If the app has custom pages configured, renders the page with path '/'
98
+ * Otherwise, renders the default homepage
99
+ *
100
+ * @param app - Validated application data from AppSchema
101
+ * @param detectedLanguage - Optional detected language from Accept-Language header
102
+ * @returns Complete HTML document as string with DOCTYPE
103
+ */
104
+ // @knip-ignore - Used via dynamic import in StartServer.ts
105
+ export function renderHomePage(app: App, detectedLanguage?: string): string {
106
+ // Try to render custom homepage first
107
+ const customHomePage = renderPageByPath(app, '/', detectedLanguage)
108
+ if (customHomePage) {
109
+ return customHomePage
110
+ }
111
+
112
+ // Fallback to default homepage
113
+ // Check if analytics should be enabled for homepage (path '/')
114
+ const injectAnalytics = shouldInjectAnalytics(app.analytics, '/')
115
+ const defaultSessionTimeout = extractSessionTimeout(app.analytics)
116
+ const html = renderToString(
117
+ <DefaultHomePage
118
+ app={app}
119
+ builtInAnalyticsEnabled={injectAnalytics}
120
+ builtInAnalyticsSessionTimeout={defaultSessionTimeout}
121
+ />
122
+ )
123
+ return `<!DOCTYPE html>\n${html}`
124
+ }
125
+
126
+ /**
127
+ * Renders any page by path to HTML string for server-side rendering
128
+ *
129
+ * @param app - Validated application data from AppSchema
130
+ * @param path - Page path to render (e.g., '/', '/about')
131
+ * @param detectedLanguage - Optional detected language from Accept-Language header
132
+ * @returns Complete HTML document as string with DOCTYPE, or undefined if page not found
133
+ */
134
+ // @knip-ignore - Used via dynamic import in server.ts
135
+ export function renderPage(app: App, path: string, detectedLanguage?: string): string | undefined {
136
+ return renderPageByPath(app, path, detectedLanguage)
137
+ }
@@ -0,0 +1,112 @@
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 React, { type ReactElement } from 'react'
9
+
10
+ /**
11
+ * Render a script tag with optional attributes
12
+ * Unified helper for rendering external scripts (analytics, external scripts, etc.)
13
+ */
14
+ export function renderScriptTag({
15
+ src,
16
+ async: asyncProp,
17
+ defer,
18
+ module,
19
+ integrity,
20
+ crossOrigin,
21
+ dataTestId,
22
+ reactKey,
23
+ hidden,
24
+ }: {
25
+ readonly src: string
26
+ readonly async?: boolean
27
+ readonly defer?: boolean
28
+ readonly module?: boolean
29
+ readonly integrity?: string
30
+ readonly crossOrigin?: 'anonymous' | 'use-credentials'
31
+ readonly dataTestId?: string
32
+ readonly reactKey: string | number
33
+ readonly hidden?: boolean
34
+ }): Readonly<ReactElement> {
35
+ const props: Record<string, unknown> = {
36
+ key: reactKey,
37
+ src,
38
+ ...(asyncProp && { async: true }),
39
+ ...(defer && { defer: true }),
40
+ ...(module && { type: 'module' }),
41
+ ...(integrity && { integrity }),
42
+ ...(crossOrigin && { crossOrigin }),
43
+ ...(dataTestId && { 'data-testid': dataTestId }),
44
+ ...(hidden && { style: { display: 'none' } }),
45
+ }
46
+
47
+ return React.createElement('script', props)
48
+ }
49
+
50
+ /**
51
+ * Render an inline script tag with JavaScript code
52
+ * Wraps code in async IIFE if async property is true
53
+ *
54
+ * SECURITY: Safe use of dangerouslySetInnerHTML
55
+ * - Content: Inline JavaScript code from page configuration
56
+ * - Source: Validated InlineScripts schema (page.scripts.inlineScripts[].code)
57
+ * - Risk: Low - content is from server configuration, not user input
58
+ * - Validation: Schema validation ensures string type
59
+ * - Purpose: Render inline scripts for page-specific functionality
60
+ * - CSP: Inline script - consider using nonce for stricter CSP
61
+ * - Transformation: Optionally wraps in async IIFE for async execution
62
+ */
63
+ export function renderInlineScriptTag({
64
+ code,
65
+ async: asyncProp,
66
+ reactKey,
67
+ }: {
68
+ readonly code: string
69
+ readonly async?: boolean
70
+ readonly reactKey: string | number
71
+ }): Readonly<ReactElement> {
72
+ const scriptContent = asyncProp ? `(async () => { ${code} })();` : code
73
+
74
+ return React.createElement('script', {
75
+ key: reactKey,
76
+ dangerouslySetInnerHTML: { __html: scriptContent },
77
+ })
78
+ }
79
+
80
+ /**
81
+ * Renders an inline script tag that exposes configuration data to window object
82
+ * Merges with existing window property if it already exists
83
+ *
84
+ * SECURITY: Safe use of dangerouslySetInnerHTML
85
+ * - Content: Build-time generated configuration data (JSON.stringify)
86
+ * - Source: Validated schema from app/page configuration
87
+ * - Risk: None - no user input, server-controlled data only
88
+ * - Purpose: Expose configuration for client-side JavaScript access
89
+ * - CSP: Compatible - inline script with deterministic content
90
+ * - Note: Only public configuration (no secrets)
91
+ *
92
+ * @param windowKey - Name of the window property (e.g., 'APP_CONFIG', 'APP_LANGUAGES')
93
+ * @param data - Configuration object to expose
94
+ * @param reactKey - Unique React key for the script element
95
+ * @returns React script element with inline configuration
96
+ */
97
+ export function renderWindowConfig({
98
+ windowKey,
99
+ data,
100
+ reactKey,
101
+ }: {
102
+ readonly windowKey: string
103
+ readonly data: unknown
104
+ readonly reactKey: string | number
105
+ }): Readonly<ReactElement> {
106
+ return React.createElement('script', {
107
+ key: reactKey,
108
+ dangerouslySetInnerHTML: {
109
+ __html: `window.${windowKey} = Object.assign({}, window.${windowKey} || {}, ${JSON.stringify(data)});`,
110
+ },
111
+ })
112
+ }
@@ -0,0 +1,117 @@
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 { Theme } from '@/domain/models/app/theme'
10
+
11
+ /**
12
+ * Additional style properties to apply when composing animation
13
+ */
14
+ interface AnimationStyleOptions {
15
+ readonly animationPlayState?: string
16
+ readonly animationFillMode?: string
17
+ readonly opacity?: number
18
+ readonly infinite?: boolean
19
+ }
20
+
21
+ /**
22
+ * Configuration for composing animations
23
+ */
24
+ interface AnimationComposerConfig {
25
+ readonly baseStyle?: Record<string, unknown>
26
+ readonly componentType?: string
27
+ readonly animationName: string
28
+ readonly theme?: Theme
29
+ readonly defaultDuration: string
30
+ readonly defaultEasing: string
31
+ readonly options?: AnimationStyleOptions
32
+ }
33
+
34
+ /**
35
+ * Animation timing configuration
36
+ */
37
+ type AnimationTiming = {
38
+ readonly duration: string
39
+ readonly easing: string
40
+ }
41
+
42
+ /**
43
+ * Extract animation timing from config
44
+ */
45
+ function extractAnimationTiming(
46
+ animationConfig: unknown,
47
+ defaultDuration: string,
48
+ defaultEasing: string
49
+ ): AnimationTiming {
50
+ const duration =
51
+ typeof animationConfig === 'object' && animationConfig !== null && 'duration' in animationConfig
52
+ ? (animationConfig.duration as string)
53
+ : defaultDuration
54
+
55
+ const easing =
56
+ typeof animationConfig === 'object' && animationConfig !== null && 'easing' in animationConfig
57
+ ? (animationConfig.easing as string)
58
+ : defaultEasing
59
+
60
+ return { duration, easing }
61
+ }
62
+
63
+ /**
64
+ * Build animation style object
65
+ */
66
+ function buildAnimationStyle(
67
+ baseStyle: Record<string, unknown> | undefined,
68
+ animationValue: string,
69
+ options?: AnimationStyleOptions
70
+ ): Record<string, unknown> {
71
+ return {
72
+ ...baseStyle,
73
+ animation: animationValue,
74
+ ...(options?.animationPlayState && { animationPlayState: options.animationPlayState }),
75
+ ...(options?.animationFillMode && { animationFillMode: options.animationFillMode }),
76
+ ...(options?.opacity !== undefined && { opacity: options.opacity }),
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Compose animation style for a given component type
82
+ *
83
+ * Extracts animation configuration from theme and composes it with base style.
84
+ * Follows functional programming principles: pure function, immutable operations.
85
+ *
86
+ * @param config - Configuration object for animation composition
87
+ * @returns New style object with animation composed
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * const style = composeAnimation({
92
+ * baseStyle,
93
+ * componentType: 'toast',
94
+ * animationName: 'fadeOut',
95
+ * theme,
96
+ * defaultDuration: '300ms',
97
+ * defaultEasing: 'ease-out'
98
+ * })
99
+ * // Returns: { ...baseStyle, animation: 'fade-out 300ms ease-out' }
100
+ * ```
101
+ */
102
+ export function composeAnimation(config: AnimationComposerConfig): Record<string, unknown> {
103
+ const animationConfig = config.theme?.animations?.[config.animationName]
104
+ if (!animationConfig) {
105
+ return config.baseStyle || {}
106
+ }
107
+
108
+ const { duration, easing } = extractAnimationTiming(
109
+ animationConfig,
110
+ config.defaultDuration,
111
+ config.defaultEasing
112
+ )
113
+ const infiniteSuffix = config.options?.infinite ? ' infinite' : ''
114
+ const animationValue = `${toKebabCase(config.animationName)} ${duration} ${easing}${infiniteSuffix}`
115
+
116
+ return buildAnimationStyle(config.baseStyle, animationValue, config.options)
117
+ }
@@ -0,0 +1,13 @@
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
+ // Utility functions
9
+ export { parseStyle, normalizeStyleAnimations } from './parse-style'
10
+ export { isCssValue, isTailwindClass } from './style-utils'
11
+
12
+ // Animation composition
13
+ export { composeAnimation } from './animation-composer'
@@ -0,0 +1,243 @@
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
+ * Normalize animation names in CSS value to kebab-case
12
+ *
13
+ * Converts animation names (first token) from camelCase to kebab-case
14
+ * while preserving other CSS values (duration, easing, etc.)
15
+ *
16
+ * @param animationValue - CSS animation value (e.g., "fadeIn 1s ease-in-out")
17
+ * @returns Normalized animation value (e.g., "fade-in 1s ease-in-out")
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * normalizeAnimationValue('fadeIn 1s ease-in-out') // 'fade-in 1s ease-in-out'
22
+ * normalizeAnimationValue('slideInUp 0.5s') // 'slide-in-up 0.5s'
23
+ * ```
24
+ */
25
+ function normalizeAnimationValue(animationValue: string): string {
26
+ const parts = animationValue.trim().split(/\s+/)
27
+ if (parts.length === 0) return animationValue
28
+
29
+ // First part is the animation name, convert to kebab-case
30
+ const animationName = parts[0]
31
+ if (!animationName) return animationValue
32
+ const normalizedName = toKebabCase(animationName)
33
+
34
+ // Rejoin with other parts (duration, easing, etc.)
35
+ return [normalizedName, ...parts.slice(1)].join(' ')
36
+ }
37
+
38
+ /**
39
+ * Common CSS properties in camelCase (used to identify CSS props in component props)
40
+ * This is not exhaustive but covers common use cases
41
+ */
42
+ const CSS_PROPERTIES = new Set([
43
+ 'alignContent',
44
+ 'alignItems',
45
+ 'alignSelf',
46
+ 'animation',
47
+ 'animationDelay',
48
+ 'animationDirection',
49
+ 'animationDuration',
50
+ 'animationFillMode',
51
+ 'animationIterationCount',
52
+ 'animationName',
53
+ 'animationPlayState',
54
+ 'animationTimingFunction',
55
+ 'background',
56
+ 'backgroundColor',
57
+ 'backgroundImage',
58
+ 'backgroundPosition',
59
+ 'backgroundRepeat',
60
+ 'backgroundSize',
61
+ 'border',
62
+ 'borderBottom',
63
+ 'borderColor',
64
+ 'borderLeft',
65
+ 'borderRadius',
66
+ 'borderRight',
67
+ 'borderStyle',
68
+ 'borderTop',
69
+ 'borderWidth',
70
+ 'bottom',
71
+ 'boxShadow',
72
+ 'color',
73
+ 'cursor',
74
+ 'display',
75
+ 'flex',
76
+ 'flexBasis',
77
+ 'flexDirection',
78
+ 'flexGrow',
79
+ 'flexShrink',
80
+ 'flexWrap',
81
+ 'font',
82
+ 'fontFamily',
83
+ 'fontSize',
84
+ 'fontStyle',
85
+ 'fontWeight',
86
+ 'gap',
87
+ 'grid',
88
+ 'gridArea',
89
+ 'gridAutoColumns',
90
+ 'gridAutoFlow',
91
+ 'gridAutoRows',
92
+ 'gridColumn',
93
+ 'gridColumnEnd',
94
+ 'gridColumnGap',
95
+ 'gridColumnStart',
96
+ 'gridGap',
97
+ 'gridRow',
98
+ 'gridRowEnd',
99
+ 'gridRowGap',
100
+ 'gridRowStart',
101
+ 'gridTemplate',
102
+ 'gridTemplateAreas',
103
+ 'gridTemplateColumns',
104
+ 'gridTemplateRows',
105
+ 'height',
106
+ 'justifyContent',
107
+ 'justifyItems',
108
+ 'justifySelf',
109
+ 'left',
110
+ 'letterSpacing',
111
+ 'lineHeight',
112
+ 'listStyle',
113
+ 'listStyleImage',
114
+ 'listStylePosition',
115
+ 'listStyleType',
116
+ 'margin',
117
+ 'marginBottom',
118
+ 'marginLeft',
119
+ 'marginRight',
120
+ 'marginTop',
121
+ 'maxHeight',
122
+ 'maxWidth',
123
+ 'minHeight',
124
+ 'minWidth',
125
+ 'objectFit',
126
+ 'objectPosition',
127
+ 'opacity',
128
+ 'order',
129
+ 'outline',
130
+ 'outlineColor',
131
+ 'outlineOffset',
132
+ 'outlineStyle',
133
+ 'outlineWidth',
134
+ 'overflow',
135
+ 'overflowX',
136
+ 'overflowY',
137
+ 'padding',
138
+ 'paddingBottom',
139
+ 'paddingLeft',
140
+ 'paddingRight',
141
+ 'paddingTop',
142
+ 'position',
143
+ 'right',
144
+ 'textAlign',
145
+ 'textDecoration',
146
+ 'textOverflow',
147
+ 'textShadow',
148
+ 'textTransform',
149
+ 'top',
150
+ 'transform',
151
+ 'transformOrigin',
152
+ 'transition',
153
+ 'transitionDelay',
154
+ 'transitionDuration',
155
+ 'transitionProperty',
156
+ 'transitionTimingFunction',
157
+ 'verticalAlign',
158
+ 'visibility',
159
+ 'whiteSpace',
160
+ 'width',
161
+ 'wordBreak',
162
+ 'wordSpacing',
163
+ 'wordWrap',
164
+ 'zIndex',
165
+ ])
166
+
167
+ /**
168
+ * Check if a property name is a known CSS property
169
+ *
170
+ * @param propName - Property name to check
171
+ * @returns True if the property is a known CSS property
172
+ */
173
+ export function isCssProperty(propName: string): boolean {
174
+ return CSS_PROPERTIES.has(propName)
175
+ }
176
+
177
+ /**
178
+ * Parse a CSS string into a React style object
179
+ *
180
+ * Converts kebab-case CSS properties to camelCase React style properties.
181
+ * Handles semicolon-separated CSS declarations.
182
+ * Normalizes animation names to kebab-case for consistency.
183
+ *
184
+ * @param styleString - CSS string (e.g., "background-color: #007bff; padding: 1rem;")
185
+ * @returns React style object (e.g., { backgroundColor: '#007bff', padding: '1rem' })
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * const style = parseStyle('background-color: #007bff; padding: 1rem;')
190
+ * // { backgroundColor: '#007bff', padding: '1rem' }
191
+ * ```
192
+ */
193
+ export function parseStyle(styleString: string): Record<string, string> {
194
+ // Split by semicolon and process each declaration
195
+ const declarations = styleString.split(';').filter((d) => d.trim())
196
+
197
+ // Use reduce for immutable accumulation instead of for-of loop with mutations
198
+ return declarations.reduce<Record<string, string>>((acc, declaration) => {
199
+ const [property, value] = declaration.split(':').map((s) => s.trim())
200
+ if (property && value) {
201
+ // Convert kebab-case to camelCase (e.g., background-color → backgroundColor)
202
+ const camelCaseProperty = property.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
203
+
204
+ // Normalize animation names to kebab-case
205
+ const normalizedValue =
206
+ camelCaseProperty === 'animation' ? normalizeAnimationValue(value) : value
207
+
208
+ return { ...acc, [camelCaseProperty]: normalizedValue }
209
+ }
210
+ return acc
211
+ }, {})
212
+ }
213
+
214
+ /**
215
+ * Normalize animation names in style object to kebab-case
216
+ *
217
+ * Converts animation property values from camelCase to kebab-case.
218
+ * Useful for style objects (not CSS strings).
219
+ *
220
+ * @param style - React style object
221
+ * @returns Style object with normalized animation names
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * const style = { animation: 'fadeIn 1s ease-in-out' }
226
+ * normalizeStyleAnimations(style) // { animation: 'fade-in 1s ease-in-out' }
227
+ * ```
228
+ */
229
+ export function normalizeStyleAnimations(
230
+ style: Record<string, unknown> | undefined
231
+ ): Record<string, unknown> | undefined {
232
+ if (!style) return style
233
+
234
+ const animationValue = style.animation
235
+ if (typeof animationValue === 'string') {
236
+ return {
237
+ ...style,
238
+ animation: normalizeAnimationValue(animationValue),
239
+ }
240
+ }
241
+
242
+ return style
243
+ }
@@ -0,0 +1,50 @@
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
+ * Checks if a value is a CSS value with units (rem, px, em, %, vh, vw)
10
+ * CSS values must contain units and not include spaces (to distinguish from Tailwind classes)
11
+ *
12
+ * @param value - String value to check
13
+ * @returns true if value is a raw CSS value, false if it's a Tailwind class
14
+ *
15
+ * @example
16
+ * isCssValue('4rem') // true
17
+ * isCssValue('80rem') // true
18
+ * isCssValue('py-16') // false
19
+ * isCssValue('py-16 sm:py-20') // false
20
+ */
21
+ export const isCssValue = (value: string): boolean => {
22
+ return /\d+(rem|px|em|%|vh|vw)/.test(value) && !value.includes(' ')
23
+ }
24
+
25
+ /**
26
+ * Checks if a container spacing value is a Tailwind class (not a raw CSS value)
27
+ * Tailwind classes include utility classes like "max-w-7xl", "mx-auto", "px-4"
28
+ * CSS values like "80rem" or "1280px" should return false
29
+ *
30
+ * @param value - Container spacing value to check
31
+ * @returns true if value contains Tailwind classes, false if it's a raw CSS value
32
+ *
33
+ * @example
34
+ * isTailwindClass('max-w-7xl mx-auto px-4') // true
35
+ * isTailwindClass('max-w-7xl') // true
36
+ * isTailwindClass('80rem') // false
37
+ * isTailwindClass('1280px') // false
38
+ */
39
+ export const isTailwindClass = (value: string): boolean => {
40
+ // If it has spaces, it's multiple classes (Tailwind)
41
+ if (value.includes(' ')) {
42
+ return true
43
+ }
44
+ // If it matches Tailwind patterns (max-w-*, mx-*, px-*, etc.), it's a class
45
+ if (/^(max-w-|mx-|px-|py-|p-|m-|w-|h-)/.test(value)) {
46
+ return true
47
+ }
48
+ // Otherwise, assume it's a CSS value
49
+ return false
50
+ }