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,80 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { type ReactElement } from 'react'
9
+ import { renderMetaTags } from './meta-utils'
10
+ import type { Page } from '@/domain/models/app/pages'
11
+
12
+ /**
13
+ * Twitter Card field mapping configuration
14
+ */
15
+ const twitterCardFieldMapping = [
16
+ { key: 'card', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.card },
17
+ { key: 'title', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.title },
18
+ { key: 'description', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.description },
19
+ { key: 'image', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.image },
20
+ { key: 'image:alt', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.imageAlt },
21
+ { key: 'site', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.site },
22
+ { key: 'creator', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.creator },
23
+ { key: 'player', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.player },
24
+ { key: 'player:width', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.playerWidth },
25
+ { key: 'player:height', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.playerHeight },
26
+ {
27
+ key: 'app:name:iphone',
28
+ getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.appName?.iPhone,
29
+ },
30
+ { key: 'app:name:ipad', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.appName?.iPad },
31
+ {
32
+ key: 'app:name:googleplay',
33
+ getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.appName?.googlePlay,
34
+ },
35
+ { key: 'app:id:iphone', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.appId?.iPhone },
36
+ { key: 'app:id:ipad', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.appId?.iPad },
37
+ {
38
+ key: 'app:id:googleplay',
39
+ getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.appId?.googlePlay,
40
+ },
41
+ ] as const
42
+
43
+ /**
44
+ * Build Twitter Card field array from twitter card configuration
45
+ * Extracts all fields including app metadata for Twitter/X sharing
46
+ *
47
+ * @param twitterCard - Twitter card configuration
48
+ * @returns Array of field key-value pairs
49
+ */
50
+ function buildTwitterCardFields(
51
+ twitterCard: NonNullable<Page['meta']>['twitter']
52
+ ): ReadonlyArray<{ readonly key: string; readonly value?: string | number }> {
53
+ return twitterCardFieldMapping.map(({ key, getter }) => ({
54
+ key,
55
+ value: getter(twitterCard),
56
+ }))
57
+ }
58
+
59
+ /**
60
+ * Render Twitter Card metadata tags
61
+ * Generates <meta name="twitter:*"> tags for Twitter/X sharing
62
+ * Supports both 'twitter' and 'twitterCard' field names for compatibility
63
+ *
64
+ * @param page - Page configuration
65
+ * @returns React fragment with Twitter meta tags
66
+ */
67
+ export function TwitterCardMeta({
68
+ page,
69
+ }: {
70
+ readonly page: Page
71
+ }): Readonly<ReactElement | undefined> {
72
+ // Support both 'twitter' (canonical) and 'twitterCard' (test alias)
73
+ const twitterCard = page.meta?.twitter ?? page.meta?.twitterCard
74
+ if (!twitterCard) {
75
+ return undefined
76
+ }
77
+
78
+ const fields = buildTwitterCardFields(twitterCard)
79
+ return renderMetaTags({ fields, prefix: 'twitter', attributeType: 'name' })
80
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { type ReactElement } from 'react'
9
+ import { createDefaultHomePageConfig } from '@/presentation/ui/pages/DefaultPageConfigs'
10
+ import { DynamicPage } from '@/presentation/ui/pages/DynamicPage'
11
+ import type { App } from '@/domain/models/app'
12
+
13
+ /**
14
+ * DefaultHomePage component - Default home page displaying application information
15
+ *
16
+ * This is the fallback home page shown when no custom page configuration is provided.
17
+ * Displays the app name, optional version badge, and optional description in a centered layout.
18
+ *
19
+ * Uses DynamicPage pattern with theme-generated styles for consistency across the application.
20
+ *
21
+ * @param props - Component props
22
+ * @param props.app - Validated application data from AppSchema
23
+ * @param props.builtInAnalyticsEnabled - Whether built-in analytics should be enabled
24
+ * @returns React element with app information
25
+ */
26
+ export function DefaultHomePage({
27
+ app,
28
+ builtInAnalyticsEnabled,
29
+ builtInAnalyticsSessionTimeout,
30
+ }: {
31
+ readonly app: App
32
+ readonly builtInAnalyticsEnabled?: boolean
33
+ readonly builtInAnalyticsSessionTimeout?: number
34
+ }): Readonly<ReactElement> {
35
+ const pageConfig = createDefaultHomePageConfig(app)
36
+ return (
37
+ <DynamicPage
38
+ page={pageConfig}
39
+ builtInAnalyticsEnabled={builtInAnalyticsEnabled}
40
+ builtInAnalyticsSessionTimeout={builtInAnalyticsSessionTimeout}
41
+ />
42
+ )
43
+ }
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import type { App } from '@/domain/models/app'
9
+ import type { Component } from '@/domain/models/app/page/sections'
10
+ import type { Page } from '@/domain/models/app/pages'
11
+
12
+ /**
13
+ * Creates version badge component
14
+ */
15
+ function createVersionBadge(version: string): Component {
16
+ return {
17
+ type: 'div',
18
+ props: {
19
+ className: 'flex justify-center',
20
+ },
21
+ children: [
22
+ {
23
+ type: 'span',
24
+ props: {
25
+ 'data-testid': 'app-version-badge',
26
+ className:
27
+ 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
28
+ },
29
+ children: [version],
30
+ },
31
+ ],
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Creates app content components (version badge, heading, description)
37
+ */
38
+ function createHomePageContent(app: App): ReadonlyArray<Component | string> {
39
+ return [
40
+ ...(app.version ? [createVersionBadge(app.version)] : []),
41
+ {
42
+ type: 'h1',
43
+ props: {
44
+ 'data-testid': 'app-name-heading',
45
+ className: 'scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl text-center',
46
+ },
47
+ children: [app.name],
48
+ },
49
+ ...(app.description
50
+ ? [
51
+ {
52
+ type: 'p',
53
+ props: {
54
+ 'data-testid': 'app-description',
55
+ className: 'text-xl text-muted-foreground text-center',
56
+ },
57
+ children: [app.description],
58
+ },
59
+ ]
60
+ : []),
61
+ ]
62
+ }
63
+
64
+ /**
65
+ * Creates a Page configuration for the default home page
66
+ *
67
+ * Renders:
68
+ * - Optional version badge
69
+ * - App name (H1 heading)
70
+ * - Optional app description
71
+ *
72
+ * Layout: Centered vertically and horizontally with max-width constraint
73
+ *
74
+ * @param app - Application data containing name, version, and description
75
+ * @returns Page configuration for DynamicPage rendering
76
+ */
77
+ export function createDefaultHomePageConfig(app: App): Page {
78
+ return {
79
+ name: 'home',
80
+ path: '/',
81
+ meta: {
82
+ lang: 'en-US',
83
+ title: `${app.name} - Powered by Sovrium`,
84
+ description: app.description ?? `Welcome to ${app.name}`,
85
+ },
86
+ sections: [
87
+ {
88
+ type: 'div',
89
+ props: {
90
+ className: 'h-screen overflow-hidden bg-gradient-to-br from-gray-50 to-gray-100',
91
+ },
92
+ children: [
93
+ {
94
+ type: 'div',
95
+ props: {
96
+ className: 'container mx-auto px-4 sm:px-6 lg:px-8 h-full',
97
+ },
98
+ children: [
99
+ {
100
+ type: 'div',
101
+ props: {
102
+ className: 'flex h-full flex-col items-center justify-center',
103
+ },
104
+ children: [
105
+ {
106
+ type: 'div',
107
+ props: {
108
+ className: 'w-full max-w-2xl space-y-6 text-center',
109
+ },
110
+ children: createHomePageContent(app),
111
+ },
112
+ ],
113
+ },
114
+ ],
115
+ },
116
+ ],
117
+ },
118
+ ],
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Creates error page section with centered layout
124
+ */
125
+ function createErrorSection(
126
+ code: string,
127
+ message: string,
128
+ codeColor: string = 'text-gray-900'
129
+ ): ReadonlyArray<Component> {
130
+ return [
131
+ {
132
+ type: 'div',
133
+ props: {
134
+ className: 'flex min-h-screen items-center justify-center bg-gray-50',
135
+ },
136
+ children: [
137
+ {
138
+ type: 'div',
139
+ props: {
140
+ className: 'text-center',
141
+ },
142
+ children: [
143
+ {
144
+ type: 'h1',
145
+ props: {
146
+ className: `mb-4 text-6xl font-bold ${codeColor}`,
147
+ },
148
+ children: [code],
149
+ },
150
+ {
151
+ type: 'p',
152
+ props: {
153
+ className: 'mb-8 text-xl text-gray-600',
154
+ },
155
+ children: [message],
156
+ },
157
+ {
158
+ type: 'link',
159
+ props: {
160
+ href: '/',
161
+ className: 'font-medium text-blue-600 hover:text-blue-700',
162
+ },
163
+ children: ['Go back home'],
164
+ },
165
+ ],
166
+ },
167
+ ],
168
+ },
169
+ ]
170
+ }
171
+
172
+ /**
173
+ * Creates a Page configuration for 404 Not Found error page
174
+ *
175
+ * Renders:
176
+ * - Large "404" heading
177
+ * - "Page not found" message
178
+ * - Link to return home
179
+ *
180
+ * Layout: Centered vertically and horizontally
181
+ *
182
+ * @returns Page configuration for DynamicPage rendering
183
+ */
184
+ export function createNotFoundPageConfig(): Page {
185
+ return {
186
+ name: 'not_found',
187
+ path: '/404',
188
+ meta: {
189
+ lang: 'en-US',
190
+ title: '404 - Not Found',
191
+ description: 'Page not found',
192
+ },
193
+ sections: createErrorSection('404', 'Page not found'),
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Creates a Page configuration for 500 Internal Server Error page
199
+ *
200
+ * Renders:
201
+ * - Large "500" heading (red color)
202
+ * - "Internal Server Error" message
203
+ * - Link to return home
204
+ *
205
+ * Layout: Centered vertically and horizontally
206
+ *
207
+ * @returns Page configuration for DynamicPage rendering
208
+ */
209
+ export function createErrorPageConfig(): Page {
210
+ return {
211
+ name: 'error',
212
+ path: '/500',
213
+ meta: {
214
+ lang: 'en-US',
215
+ title: '500 - Internal Server Error',
216
+ description: 'Internal Server Error',
217
+ },
218
+ sections: createErrorSection('500', 'Internal Server Error', 'text-red-600'),
219
+ }
220
+ }
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { type ReactElement } from 'react'
9
+ import { extractComponentMetaFromSections } from '@/presentation/ui/metadata/extract-component-meta'
10
+ import { PageBodyScripts } from '@/presentation/ui/pages/PageBodyScripts'
11
+ import { PageHead } from '@/presentation/ui/pages/PageHead'
12
+ import { resolvePageLanguage } from '@/presentation/ui/pages/PageLangResolver'
13
+ import { PageMain } from '@/presentation/ui/pages/PageMain'
14
+ import { extractPageMetadata } from '@/presentation/ui/pages/PageMetadata'
15
+ import { groupScriptsByPosition } from '@/presentation/ui/pages/PageScripts'
16
+ import type { Components } from '@/domain/models/app/components'
17
+ import type { Languages } from '@/domain/models/app/languages'
18
+ import type { Page } from '@/domain/models/app/pages'
19
+ import type { Theme } from '@/domain/models/app/theme'
20
+ import type { RouteParams } from '@/domain/utils/route-matcher'
21
+
22
+ type DynamicPageProps = {
23
+ readonly page: Page
24
+ readonly components?: Components
25
+ readonly theme?: Theme
26
+ readonly languages?: Languages
27
+ readonly detectedLanguage?: string
28
+ readonly routeParams?: RouteParams
29
+ readonly builtInAnalyticsEnabled?: boolean
30
+ readonly builtInAnalyticsSessionTimeout?: number
31
+ }
32
+
33
+ type DynamicPageHeadProps = {
34
+ readonly mergedPage: Page
35
+ readonly theme?: Theme
36
+ readonly directionStyles: string
37
+ readonly title: string
38
+ readonly description: string
39
+ readonly keywords?: string
40
+ readonly canonical?: string
41
+ readonly lang: string
42
+ readonly languages?: Languages
43
+ readonly scripts: ReturnType<typeof groupScriptsByPosition>
44
+ readonly builtInAnalyticsEnabled?: boolean
45
+ readonly builtInAnalyticsSessionTimeout?: number
46
+ }
47
+
48
+ /**
49
+ * Merges component metadata with page metadata
50
+ *
51
+ * @param page - Page configuration
52
+ * @param components - Available component templates
53
+ * @returns Page with merged metadata
54
+ */
55
+ function mergeComponentMetaIntoPage(page: Page, components?: Components): Page {
56
+ const componentOpenGraph = extractComponentMetaFromSections(page.sections, components)
57
+
58
+ if (!componentOpenGraph || !page.meta) return page
59
+
60
+ return {
61
+ ...page,
62
+ meta: {
63
+ ...page.meta,
64
+ openGraph: {
65
+ ...page.meta.openGraph,
66
+ ...componentOpenGraph,
67
+ },
68
+ },
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Renders the <head> section of DynamicPage
74
+ * Extracted to satisfy max-lines-per-function ESLint rule
75
+ */
76
+ function DynamicPageHead({
77
+ mergedPage,
78
+ theme,
79
+ directionStyles,
80
+ title,
81
+ description,
82
+ keywords,
83
+ canonical,
84
+ lang,
85
+ languages,
86
+ scripts,
87
+ builtInAnalyticsEnabled,
88
+ builtInAnalyticsSessionTimeout,
89
+ }: DynamicPageHeadProps): Readonly<ReactElement> {
90
+ const sessionTimeout = builtInAnalyticsSessionTimeout ?? 30
91
+ // Inline analytics script if enabled (contains /api/analytics/collect endpoint)
92
+ const analyticsScript = builtInAnalyticsEnabled
93
+ ? `(function(){
94
+ "use strict";
95
+ var E="/api/analytics/collect",A="${mergedPage.name || 'app'}",D=true,sessionTimeout=${sessionTimeout};
96
+ if(D&&navigator.doNotTrack==="1")return;
97
+ var u=function(){
98
+ try{var s=new URLSearchParams(location.search);
99
+ var d={p:location.pathname,t:document.title,r:document.referrer||void 0,
100
+ sw:screen.width,sh:screen.height,
101
+ us:s.get("utm_source")||void 0,um:s.get("utm_medium")||void 0,
102
+ uc:s.get("utm_campaign")||void 0,ux:s.get("utm_content")||void 0,
103
+ ut:s.get("utm_term")||void 0};
104
+ var b=JSON.stringify(d);
105
+ if(navigator.sendBeacon){navigator.sendBeacon(E,new Blob([b],{type:"application/json"}))}
106
+ else{var x=new XMLHttpRequest();x.open("POST",E,true);x.setRequestHeader("Content-Type","application/json");x.send(b)}
107
+ }catch(e){}};
108
+ u();
109
+ var op=history.pushState;
110
+ history.pushState=function(){op.apply(this,arguments);u()};
111
+ window.addEventListener("popstate",u);
112
+ })();`
113
+ : undefined
114
+
115
+ return (
116
+ <head>
117
+ <PageHead
118
+ page={mergedPage}
119
+ theme={theme}
120
+ directionStyles={directionStyles}
121
+ title={title}
122
+ description={description}
123
+ keywords={keywords}
124
+ canonical={canonical}
125
+ lang={lang}
126
+ languages={languages}
127
+ scripts={scripts}
128
+ />
129
+ {analyticsScript && <script dangerouslySetInnerHTML={{ __html: analyticsScript }} />}
130
+ </head>
131
+ )
132
+ }
133
+
134
+ type DynamicPageBodyProps = {
135
+ readonly page: Page
136
+ readonly components?: Components
137
+ readonly theme?: Theme
138
+ readonly languages?: Languages
139
+ readonly direction: 'ltr' | 'rtl'
140
+ readonly scripts: ReturnType<typeof groupScriptsByPosition>
141
+ readonly lang: string
142
+ readonly bodyStyle:
143
+ | {
144
+ readonly fontFamily?: string
145
+ readonly fontSize?: string
146
+ readonly lineHeight?: string
147
+ readonly fontStyle?: 'normal' | 'italic' | 'oblique'
148
+ readonly letterSpacing?: string
149
+ readonly textTransform?: 'none' | 'uppercase' | 'lowercase' | 'capitalize'
150
+ }
151
+ | undefined
152
+ readonly routeParams?: RouteParams
153
+ }
154
+
155
+ /**
156
+ * Generic parameter names that require context from path segments
157
+ * These parameters get prefixed with their context (e.g., product-id, user-key)
158
+ */
159
+ const GENERIC_PARAM_NAMES = new Set(['id', 'key', 'uid', 'pk'])
160
+
161
+ /**
162
+ * Converts route parameters to data attributes for testing
163
+ * Uses context-aware naming: generic params (id, key) get prefixed, descriptive ones (slug) don't
164
+ *
165
+ * @param routeParams - Route parameters extracted from URL
166
+ * @param path - Page path pattern (e.g., '/blog/:slug')
167
+ * @returns Data attributes object
168
+ */
169
+ function buildDataAttributes(
170
+ routeParams: RouteParams | undefined,
171
+ path: string | undefined
172
+ ): Record<string, string> {
173
+ if (!routeParams || !path) {
174
+ return {}
175
+ }
176
+
177
+ const pathSegments = path.split('/').filter(Boolean)
178
+
179
+ return Object.entries(routeParams).reduce<Record<string, string>>((acc, [key, value]) => {
180
+ // Check if parameter name is generic and needs context
181
+ if (GENERIC_PARAM_NAMES.has(key)) {
182
+ // Generic parameter - add context from previous path segment
183
+ // e.g., /products/:id -> data-product-id
184
+ const paramIndex = pathSegments.findIndex((seg) => seg === `:${key}`)
185
+ if (paramIndex > 0) {
186
+ const contextSegment = pathSegments[paramIndex - 1]
187
+ if (!contextSegment) {
188
+ return { ...acc, [`data-${key}`]: value }
189
+ }
190
+ // Convert plural to singular (e.g., 'products' -> 'product')
191
+ const singularContext = contextSegment.endsWith('s')
192
+ ? contextSegment.slice(0, -1)
193
+ : contextSegment
194
+ return { ...acc, [`data-${singularContext}-${key}`]: value }
195
+ }
196
+ // No context available, use parameter name only
197
+ return { ...acc, [`data-${key}`]: value }
198
+ }
199
+ // Descriptive parameter - use as-is (e.g., /blog/:slug -> data-slug)
200
+ return { ...acc, [`data-${key}`]: value }
201
+ }, {})
202
+ }
203
+
204
+ /**
205
+ * Renders the <body> section of DynamicPage
206
+ * Extracted to satisfy max-lines-per-function ESLint rule
207
+ */
208
+ function DynamicPageBody({
209
+ page,
210
+ components,
211
+ theme,
212
+ languages,
213
+ direction,
214
+ scripts,
215
+ lang,
216
+ bodyStyle,
217
+ routeParams,
218
+ }: DynamicPageBodyProps): Readonly<ReactElement> {
219
+ const dataAttributes = buildDataAttributes(routeParams, page.path)
220
+
221
+ return (
222
+ <body
223
+ {...(bodyStyle && { style: bodyStyle })}
224
+ {...dataAttributes}
225
+ >
226
+ <PageBodyScripts
227
+ page={page}
228
+ theme={theme}
229
+ languages={languages}
230
+ direction={direction}
231
+ scripts={scripts}
232
+ position="start"
233
+ />
234
+ <PageMain
235
+ page={page}
236
+ sections={page.sections}
237
+ theme={theme}
238
+ components={components}
239
+ languages={languages}
240
+ currentLang={lang}
241
+ />
242
+ <PageBodyScripts
243
+ page={page}
244
+ theme={theme}
245
+ languages={languages}
246
+ direction={direction}
247
+ scripts={scripts}
248
+ position="end"
249
+ />
250
+ </body>
251
+ )
252
+ }
253
+
254
+ /**
255
+ * Renders a page from configuration as a complete HTML document
256
+ * Theme CSS is compiled globally at server startup via /assets/output.css
257
+ * Theme is still passed for font URLs, animation flags, and debugging
258
+ */
259
+ export function DynamicPage({
260
+ page,
261
+ components,
262
+ theme,
263
+ languages,
264
+ detectedLanguage,
265
+ routeParams,
266
+ builtInAnalyticsEnabled,
267
+ builtInAnalyticsSessionTimeout,
268
+ }: DynamicPageProps): Readonly<ReactElement> {
269
+ const metadata = extractPageMetadata(page, theme, languages, detectedLanguage)
270
+ const langConfig = resolvePageLanguage(page, languages, detectedLanguage)
271
+ const scripts = groupScriptsByPosition(page)
272
+ const pageWithMeta = mergeComponentMetaIntoPage(page, components)
273
+
274
+ return (
275
+ <html
276
+ lang={langConfig.lang}
277
+ dir={langConfig.direction}
278
+ {...(page.scripts && { 'data-features': JSON.stringify(page.scripts.features || {}) })}
279
+ >
280
+ <DynamicPageHead
281
+ mergedPage={pageWithMeta}
282
+ theme={theme}
283
+ directionStyles={langConfig.directionStyles}
284
+ title={metadata.title}
285
+ description={metadata.description}
286
+ keywords={metadata.keywords}
287
+ canonical={metadata.canonical}
288
+ lang={langConfig.lang}
289
+ languages={languages}
290
+ scripts={scripts}
291
+ builtInAnalyticsEnabled={builtInAnalyticsEnabled}
292
+ builtInAnalyticsSessionTimeout={builtInAnalyticsSessionTimeout}
293
+ />
294
+ <DynamicPageBody
295
+ page={page}
296
+ components={components}
297
+ theme={theme}
298
+ languages={languages}
299
+ direction={langConfig.direction}
300
+ scripts={scripts}
301
+ lang={langConfig.lang}
302
+ bodyStyle={metadata.bodyStyle}
303
+ routeParams={routeParams}
304
+ />
305
+ </html>
306
+ )
307
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { type ReactElement } from 'react'
9
+ import { createErrorPageConfig } from '@/presentation/ui/pages/DefaultPageConfigs'
10
+ import { DynamicPage } from '@/presentation/ui/pages/DynamicPage'
11
+
12
+ /**
13
+ * ErrorPage component - 500 internal server error page
14
+ *
15
+ * This page is displayed when the server encounters an unexpected error.
16
+ * It provides a clear message and a link back to the homepage.
17
+ *
18
+ * Uses DynamicPage pattern with theme-generated styles for consistency across the application.
19
+ *
20
+ * @returns React element for 500 error page
21
+ */
22
+ export function ErrorPage(): Readonly<ReactElement> {
23
+ const pageConfig = createErrorPageConfig()
24
+ return <DynamicPage page={pageConfig} />
25
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { type ReactElement } from 'react'
9
+ import { createNotFoundPageConfig } from '@/presentation/ui/pages/DefaultPageConfigs'
10
+ import { DynamicPage } from '@/presentation/ui/pages/DynamicPage'
11
+
12
+ /**
13
+ * NotFoundPage component - 404 error page
14
+ *
15
+ * This page is displayed when a user navigates to a route that doesn't exist.
16
+ * It provides a clear message and a link back to the homepage.
17
+ *
18
+ * Uses DynamicPage pattern with theme-generated styles for consistency across the application.
19
+ *
20
+ * @returns React element for 404 error page
21
+ */
22
+ export function NotFoundPage(): Readonly<ReactElement> {
23
+ const pageConfig = createNotFoundPageConfig()
24
+ return <DynamicPage page={pageConfig} />
25
+ }