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,275 @@
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 { Console, Effect, Config } from 'effect'
9
+ import { Hono } from 'hono'
10
+ import { purgeOldAnalyticsData } from '@/application/use-cases/analytics/purge-old-data'
11
+ import { compileCSS } from '@/infrastructure/css/compiler'
12
+ import { runMigrations } from '@/infrastructure/database/drizzle/migrate'
13
+ import { AnalyticsRepositoryLive } from '@/infrastructure/database/repositories/analytics-repository-live'
14
+ import {
15
+ initializeSchema,
16
+ type AuthConfigRequiredForUserFields,
17
+ type SchemaInitializationError,
18
+ } from '@/infrastructure/database/schema/schema-initializer'
19
+ import { ServerCreationError } from '@/infrastructure/errors/server-creation-error'
20
+ import { createApiRoutes } from '@/infrastructure/server/route-setup/api-routes'
21
+ import {
22
+ setupAuthMiddleware,
23
+ setupAuthRoutes,
24
+ } from '@/infrastructure/server/route-setup/auth-routes'
25
+ import { setupOpenApiRoutes } from '@/infrastructure/server/route-setup/openapi-routes'
26
+ import {
27
+ setupPageRoutes,
28
+ type HonoAppConfig,
29
+ } from '@/infrastructure/server/route-setup/page-routes'
30
+ import { setupStaticAssets } from '@/infrastructure/server/route-setup/static-assets'
31
+ import type { ServerInstance } from '@/application/models/server'
32
+ import type { App } from '@/domain/models/app'
33
+ import type {
34
+ DatabaseConnectionError,
35
+ MigrationError,
36
+ } from '@/infrastructure/database/drizzle/migrate'
37
+ import type { CSSCompilationError } from '@/infrastructure/errors/css-compilation-error'
38
+ import type { Server } from 'bun'
39
+
40
+ /**
41
+ * Server configuration options
42
+ */
43
+ export interface ServerConfig {
44
+ readonly app: App
45
+ readonly port?: number
46
+ readonly hostname?: string
47
+ readonly publicDir?: string
48
+ readonly renderHomePage: (app: App, detectedLanguage?: string) => string
49
+ readonly renderPage: (app: App, path: string, detectedLanguage?: string) => string | undefined
50
+ readonly renderNotFoundPage: (app?: App, detectedLanguage?: string) => string
51
+ readonly renderErrorPage: (app?: App, detectedLanguage?: string) => string
52
+ }
53
+
54
+ /**
55
+ * Creates a Hono application with routes
56
+ *
57
+ * Mounts the following routes:
58
+ * - GET /api/* - API routes (health, tables, records) with RPC type safety
59
+ * - GET /api/openapi.json - Generated OpenAPI specification (application endpoints)
60
+ * - GET /api/auth/openapi.json - Generated OpenAPI specification (authentication endpoints)
61
+ * - GET /api/scalar - Unified Scalar API documentation UI (shows both API and Auth tabs)
62
+ * - POST/GET /api/auth/* - Better Auth authentication endpoints
63
+ * - GET / - Homepage
64
+ * - GET /assets/output.css - Compiled Tailwind CSS
65
+ * - GET /test/error - Test error handler (non-production only)
66
+ *
67
+ * @param config - Configuration object with app data and render functions
68
+ * @returns Configured Hono app instance
69
+ * @knip-ignore - Used by both createServer and createHonoAppForSSG
70
+ */
71
+ export function createHonoApp(config: HonoAppConfig): Readonly<Hono> {
72
+ const { app, renderNotFoundPage, renderErrorPage } = config
73
+
74
+ const honoApp = new Hono()
75
+
76
+ // Analytics retention cleanup middleware — purges stale page view records.
77
+ // Runs awaited on page requests to guarantee old data is removed before response.
78
+ const analyticsEnabled = app.analytics !== undefined && app.analytics !== false
79
+ if (analyticsEnabled) {
80
+ const retentionDays =
81
+ typeof app.analytics === 'object' ? app.analytics.retentionDays : undefined
82
+
83
+ honoApp.use('*', async (_c, next) => {
84
+ await Effect.runPromise(
85
+ purgeOldAnalyticsData(app.name, retentionDays).pipe(
86
+ Effect.provide(AnalyticsRepositoryLive),
87
+ Effect.catchAll(() => Effect.void)
88
+ )
89
+ )
90
+ // eslint-disable-next-line functional/no-expression-statements
91
+ await next()
92
+ })
93
+ }
94
+
95
+ // Create base Hono app and chain API routes directly
96
+ // This pattern is required for Hono RPC type inference to work correctly
97
+ // Setup all routes by chaining the setup functions
98
+ const honoWithRoutes = setupPageRoutes(
99
+ setupStaticAssets(
100
+ setupAuthRoutes(
101
+ setupAuthMiddleware(setupOpenApiRoutes(createApiRoutes(app, honoApp)), app),
102
+ app
103
+ ),
104
+ app,
105
+ config.publicDir
106
+ ),
107
+ config
108
+ )
109
+
110
+ // Add error handlers
111
+ return honoWithRoutes
112
+ .notFound((c) => c.html(renderNotFoundPage(app), 404))
113
+ .onError((error, c) => {
114
+ // Fire-and-forget error logging (onError handler is synchronous)
115
+ Effect.runPromise(Console.error('Server error:', error)).catch(() => {
116
+ // Silently ignore logging failures to prevent unhandled promise rejections
117
+ })
118
+ return c.html(renderErrorPage(app), 500)
119
+ })
120
+ }
121
+
122
+ /**
123
+ * Create server stop effect
124
+ */
125
+ const createStopEffect = (server: ReturnType<typeof Bun.serve>): Effect.Effect<void, never> =>
126
+ Effect.gen(function* () {
127
+ yield* Console.log('Stopping server...')
128
+ yield* Effect.sync(() => server.stop())
129
+ yield* Console.log('Server stopped')
130
+ })
131
+
132
+ /**
133
+ * Log server startup information
134
+ */
135
+ const logServerStartup = (url: string): Effect.Effect<void, never> =>
136
+ Effect.gen(function* () {
137
+ yield* Console.log('✓ Server started successfully!')
138
+ yield* Console.log(`✓ Homepage: ${url}`)
139
+ yield* Console.log(`✓ Health check: ${url}/api/health`)
140
+ yield* Console.log(`✓ API documentation: ${url}/api/scalar`)
141
+ yield* Console.log(`✓ OpenAPI schema: ${url}/api/openapi.json`)
142
+ yield* Console.log(`✓ Compiled CSS: ${url}/assets/output.css`)
143
+ })
144
+
145
+ /**
146
+ * Get database URL from environment configuration
147
+ */
148
+ const getDatabaseUrl = (): Effect.Effect<string, never> =>
149
+ Config.string('DATABASE_URL').pipe(
150
+ Config.withDefault(''),
151
+ Effect.catchAll(() => Effect.succeed(''))
152
+ )
153
+
154
+ /**
155
+ * Run migrations if database URL is configured
156
+ */
157
+ const runMigrationsIfConfigured = (
158
+ databaseUrl: string
159
+ ): Effect.Effect<
160
+ void,
161
+ DatabaseConnectionError | MigrationError | SchemaInitializationError,
162
+ never
163
+ > => (databaseUrl ? runMigrations(databaseUrl) : Effect.void)
164
+
165
+ /**
166
+ * Compile CSS and log results
167
+ */
168
+ const compileCSSWithLogging = (
169
+ app: App
170
+ ): Effect.Effect<{ css: string }, CSSCompilationError, never> =>
171
+ Effect.gen(function* () {
172
+ yield* Console.log('Compiling CSS...')
173
+ const cssResult = yield* compileCSS(app)
174
+ yield* Console.log(`CSS compiled: ${cssResult.css.length} bytes`)
175
+ return cssResult
176
+ })
177
+
178
+ /**
179
+ * Start Bun HTTP server
180
+ */
181
+ const startBunServer = (
182
+ honoApp: Readonly<Hono>,
183
+ port: number,
184
+ hostname: string
185
+ ): Effect.Effect<Server<undefined>, ServerCreationError, never> =>
186
+ Effect.try({
187
+ try: () =>
188
+ Bun.serve({
189
+ port,
190
+ hostname,
191
+ fetch: honoApp.fetch,
192
+ }),
193
+ catch: (error) => new ServerCreationError(error),
194
+ })
195
+
196
+ /**
197
+ * Creates and starts a Bun server with Hono
198
+ *
199
+ * This function:
200
+ * 1. Pre-compiles CSS on startup for faster initial requests
201
+ * 2. Creates a Hono app with routes (/, /assets/output.css, /api/*)
202
+ * 3. Starts a Bun HTTP server
203
+ * 4. Returns server instance with stop capability
204
+ *
205
+ * @param config - Server configuration with app data and optional port/hostname
206
+ * @returns Effect that yields ServerInstance or ServerCreationError
207
+ *
208
+ * @example
209
+ * ```typescript
210
+ * const program = Effect.gen(function* () {
211
+ * const server = yield* createServer({
212
+ * app: { name: 'My App' },
213
+ * port: 3000
214
+ * })
215
+ * console.log(`Server running at ${server.url}`)
216
+ * })
217
+ *
218
+ * Effect.runPromise(program)
219
+ * ```
220
+ */
221
+ // @knip-ignore - Used via dynamic import in StartServer.ts
222
+ export const createServer = (
223
+ config: ServerConfig
224
+ ): Effect.Effect<
225
+ ServerInstance,
226
+ | ServerCreationError
227
+ | CSSCompilationError
228
+ | AuthConfigRequiredForUserFields
229
+ | SchemaInitializationError
230
+ | Error
231
+ > =>
232
+ Effect.gen(function* () {
233
+ const {
234
+ app,
235
+ port = 3000,
236
+ hostname = 'localhost',
237
+ publicDir,
238
+ renderHomePage,
239
+ renderPage,
240
+ renderNotFoundPage,
241
+ renderErrorPage,
242
+ } = config
243
+
244
+ // Initialize database
245
+ const databaseUrl = yield* getDatabaseUrl()
246
+ yield* runMigrationsIfConfigured(databaseUrl)
247
+ yield* initializeSchema(app)
248
+
249
+ // Compile CSS
250
+ yield* compileCSSWithLogging(app)
251
+
252
+ // Create Hono app
253
+ const honoApp = createHonoApp({
254
+ app,
255
+ publicDir,
256
+ renderHomePage,
257
+ renderPage,
258
+ renderNotFoundPage,
259
+ renderErrorPage,
260
+ })
261
+
262
+ // Start server
263
+ const server = yield* startBunServer(honoApp, port, hostname)
264
+ const url = `http://${hostname}:${server.port}`
265
+
266
+ // Log and return
267
+ yield* logServerStartup(url)
268
+
269
+ return {
270
+ server,
271
+ url,
272
+ stop: createStopEffect(server),
273
+ app: honoApp,
274
+ }
275
+ })
@@ -0,0 +1,196 @@
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 { Effect, Data } from 'effect'
9
+ import { toSSG } from 'hono/bun'
10
+ import type { Hono } from 'hono'
11
+
12
+ /**
13
+ * SSG Generation Error - infrastructure layer error
14
+ */
15
+ export class SSGGenerationError extends Data.TaggedError('SSGGenerationError')<{
16
+ readonly message: string
17
+ readonly cause?: unknown
18
+ }> {}
19
+
20
+ /**
21
+ * Options for static site generation (infrastructure layer)
22
+ */
23
+ export interface SSGOptions {
24
+ readonly outputDir?: string
25
+ readonly baseUrl?: string
26
+ readonly basePath?: string
27
+ readonly deployment?: 'github-pages' | 'generic'
28
+ readonly languages?: readonly string[]
29
+ readonly defaultLanguage?: string
30
+ readonly generateSitemap?: boolean
31
+ readonly generateRobotsTxt?: boolean
32
+ readonly hydration?: boolean
33
+ readonly generateManifest?: boolean
34
+ readonly bundleOptimization?: 'split' | 'none'
35
+ readonly pagePaths?: readonly string[] // Explicit list of page paths to generate
36
+ readonly publicDir?: string // Directory containing static assets to copy
37
+ }
38
+
39
+ /**
40
+ * Register explicit page paths in Hono app for SSG discovery
41
+ */
42
+ function registerPagePaths(
43
+ // eslint-disable-next-line functional/prefer-immutable-types -- Hono type is mutable
44
+ app: Hono,
45
+ pagePaths: readonly string[]
46
+ ): void {
47
+ const mutableApp = app as Hono
48
+
49
+ // eslint-disable-next-line functional/no-loop-statements -- Imperative route registration required by Hono's mutable API for toSSG discovery
50
+ for (const path of pagePaths) {
51
+ if (path !== '/') {
52
+ // eslint-disable-next-line functional/no-expression-statements -- Necessary side effect to mutate Hono app for toSSG route discovery
53
+ mutableApp.get(path, (c) => c.text(''))
54
+ }
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Check if a route should be excluded from SSG
60
+ */
61
+ function shouldExcludeRoute(pathname: string): boolean {
62
+ return (
63
+ pathname === '/api/health' ||
64
+ pathname === '/api/openapi.json' ||
65
+ pathname === '/api/scalar' ||
66
+ pathname.startsWith('/api/auth/') ||
67
+ pathname === '/api/tables' || // Exact match for list endpoint
68
+ pathname.startsWith('/api/tables/') ||
69
+ pathname === '/api/records' || // Exact match for records endpoint
70
+ pathname.startsWith('/api/records/') ||
71
+ pathname.startsWith('/test/')
72
+ )
73
+ }
74
+
75
+ /**
76
+ * Normalize file path to be relative to output directory
77
+ */
78
+ function normalizeFilePath(file: string, outputDir: string): string {
79
+ const normalizedOutputDir = outputDir.startsWith('./') ? outputDir.substring(2) : outputDir
80
+ const normalizedFile = file.startsWith('./') ? file.substring(2) : file
81
+
82
+ if (normalizedFile.startsWith(normalizedOutputDir + '/')) {
83
+ return normalizedFile.substring(normalizedOutputDir.length + 1)
84
+ }
85
+ if (normalizedFile.startsWith(normalizedOutputDir)) {
86
+ return normalizedFile.substring(normalizedOutputDir.length)
87
+ }
88
+ return normalizedFile
89
+ }
90
+
91
+ /**
92
+ * Generate static site using Hono's SSG functionality
93
+ *
94
+ * This adapter wraps Hono's toSSG function with Effect.ts patterns
95
+ * and Sovrium-specific logic.
96
+ *
97
+ * @param app - Hono application instance
98
+ * @param options - Static generation options
99
+ * @returns Effect with output directory and generated files
100
+ */
101
+ export const generateStaticSite = (
102
+ // eslint-disable-next-line functional/prefer-immutable-types -- Hono is a mutable class from external library
103
+ app: Hono | Readonly<Hono>,
104
+ options: Readonly<SSGOptions>
105
+ ): Effect.Effect<
106
+ { readonly outputDir: string; readonly files: readonly string[] },
107
+ SSGGenerationError,
108
+ never
109
+ > =>
110
+ Effect.tryPromise({
111
+ try: async () => {
112
+ const outputDir = options.outputDir || './static'
113
+
114
+ // Register explicit page paths for SSG discovery
115
+ if (options.pagePaths && options.pagePaths.length > 0) {
116
+ registerPagePaths(app as Hono, options.pagePaths)
117
+ }
118
+
119
+ // Use Hono's toSSG to generate static files
120
+ const result = await toSSG(app as Hono, {
121
+ dir: outputDir,
122
+ beforeRequestHook: (req) => {
123
+ const url = new URL(req.url)
124
+ return shouldExcludeRoute(url.pathname) ? false : req
125
+ },
126
+ })
127
+
128
+ // Check if SSG generation was successful
129
+ if (!result.success) {
130
+ // eslint-disable-next-line functional/no-throw-statements -- Error handling requires throw
131
+ throw new Error(
132
+ `Static site generation failed: ${result.error?.message || 'Unknown error'}`
133
+ )
134
+ }
135
+
136
+ // Normalize file paths to be relative to outputDir
137
+ const normalizedFiles = (result.files as readonly string[]).map((file) =>
138
+ normalizeFilePath(file, outputDir)
139
+ )
140
+
141
+ // Return output directory and generated files from toSSG
142
+ return {
143
+ outputDir,
144
+ files: normalizedFiles,
145
+ } as const
146
+ },
147
+ catch: (error) =>
148
+ new SSGGenerationError({
149
+ message: `Static site generation failed: ${error instanceof Error ? error.message : String(error)}`,
150
+ cause: error,
151
+ }),
152
+ })
153
+
154
+ /**
155
+ * Page info for sitemap generation
156
+ */
157
+ interface SitemapPage {
158
+ readonly path: string
159
+ readonly priority?: string
160
+ readonly changefreq?: string
161
+ }
162
+
163
+ /**
164
+ * Plugin to generate sitemap.xml
165
+ */
166
+ export const sitemapPlugin = (baseUrl: string) => ({
167
+ name: 'sitemap',
168
+ async generate(pages: readonly SitemapPage[]) {
169
+ const entries = pages.map(
170
+ (page) => ` <url>
171
+ <loc>${baseUrl}${page.path}</loc>
172
+ <priority>${page.priority || '0.5'}</priority>
173
+ <changefreq>${page.changefreq || 'monthly'}</changefreq>
174
+ </url>`
175
+ )
176
+
177
+ return `<?xml version="1.0" encoding="UTF-8"?>
178
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
179
+ ${entries.join('\n')}
180
+ </urlset>`
181
+ },
182
+ })
183
+
184
+ /**
185
+ * Plugin to generate robots.txt
186
+ */
187
+ export const robotsPlugin = (baseUrl: string, includeSitemap: boolean = false) => ({
188
+ name: 'robots',
189
+ async generate() {
190
+ const baseLines = ['User-agent: *', 'Allow: /'] as const
191
+ const sitemapLine = includeSitemap ? ([`Sitemap: ${baseUrl}/sitemap.xml`] as const) : []
192
+ const lines = [...baseLines, ...sitemapLine] as readonly string[]
193
+
194
+ return lines.join('\n')
195
+ },
196
+ })
@@ -0,0 +1,20 @@
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 { Layer } from 'effect'
9
+ import { StaticSiteGenerator } from '@/application/ports/services/static-site-generator'
10
+ import { generateStaticSite } from '@/infrastructure/server/ssg-adapter'
11
+
12
+ /**
13
+ * Live implementation of StaticSiteGenerator using Hono SSG
14
+ *
15
+ * This layer provides the production implementation of static site generation
16
+ * using Hono's toSSG functionality.
17
+ */
18
+ export const StaticSiteGeneratorLive = Layer.succeed(StaticSiteGenerator, {
19
+ generate: generateStaticSite,
20
+ })
@@ -0,0 +1,106 @@
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
+ * Accept-Language Header Parser
10
+ *
11
+ * Parses the Accept-Language HTTP header and returns an ordered list of
12
+ * preferred languages based on quality values (q parameter).
13
+ *
14
+ * Format: Accept-Language: en-US,en;q=0.9,fr;q=0.8
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const languages = parseAcceptLanguage('en-US,en;q=0.9,fr;q=0.8')
19
+ * // Returns: ['en-US', 'en', 'fr']
20
+ * ```
21
+ */
22
+
23
+ interface LanguagePreference {
24
+ readonly language: string
25
+ readonly quality: number
26
+ }
27
+
28
+ /**
29
+ * Parse Accept-Language header into ordered list of language preferences
30
+ *
31
+ * @param header - Accept-Language header value (e.g., 'en-US,en;q=0.9,fr;q=0.8')
32
+ * @returns Array of language codes ordered by preference (highest quality first)
33
+ */
34
+ export function parseAcceptLanguage(header: string | null | undefined): ReadonlyArray<string> {
35
+ if (!header) {
36
+ return []
37
+ }
38
+
39
+ // Parse each language-quality pair and sort by quality (highest first)
40
+ const sortedPreferences: ReadonlyArray<LanguagePreference> = header
41
+ .split(',')
42
+ .map((lang) => {
43
+ const [language, qualityStr] = lang.trim().split(';')
44
+
45
+ // Skip invalid entries without a language code
46
+ if (!language) {
47
+ return undefined
48
+ }
49
+
50
+ // Extract quality value (default is 1.0 if not specified)
51
+ const quality = qualityStr ? parseFloat(qualityStr.split('=')[1] || '1.0') : 1.0
52
+
53
+ return {
54
+ language: language.trim(),
55
+ quality: isNaN(quality) ? 0 : quality,
56
+ }
57
+ })
58
+ .filter(
59
+ (pref): pref is LanguagePreference =>
60
+ pref !== undefined && pref.language.length > 0 && pref.quality > 0
61
+ )
62
+ .toSorted((a, b) => b.quality - a.quality)
63
+
64
+ return sortedPreferences.map((pref) => pref.language)
65
+ }
66
+
67
+ /**
68
+ * Detect best matching language from Accept-Language header
69
+ *
70
+ * Tries exact match first (e.g., 'fr-FR' === 'fr-FR'),
71
+ * then base language match (e.g., 'fr' from 'fr-FR' matches 'fr-CA')
72
+ *
73
+ * @param header - Accept-Language header value
74
+ * @param supportedLanguages - Array of supported language codes
75
+ * @returns Best matching language code or undefined
76
+ */
77
+ export function detectLanguageFromHeader(
78
+ header: string | null | undefined,
79
+ supportedLanguages: ReadonlyArray<string>
80
+ ): string | undefined {
81
+ const preferences = parseAcceptLanguage(header)
82
+
83
+ // Try exact matches first
84
+ const exactMatch = preferences.find((preferred) => supportedLanguages.includes(preferred))
85
+ if (exactMatch) {
86
+ return exactMatch
87
+ }
88
+
89
+ // Try base language matches (e.g., 'fr' from 'fr-FR')
90
+ const baseMatch = preferences
91
+ .map((preferred) => ({
92
+ preferred,
93
+ basePreferred: preferred.split('-')[0],
94
+ }))
95
+ .find(({ basePreferred }) =>
96
+ supportedLanguages.some((supported) => supported.split('-')[0] === basePreferred)
97
+ )
98
+
99
+ if (baseMatch) {
100
+ return supportedLanguages.find(
101
+ (supported) => supported.split('-')[0] === baseMatch.basePreferred
102
+ )
103
+ }
104
+
105
+ return undefined
106
+ }
@@ -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
+ * Simple glob pattern matcher for URL paths.
10
+ *
11
+ * Supports:
12
+ * - Exact matches: /admin
13
+ * - Wildcard suffix: /admin/* matches /admin/dashboard, /admin/users/123
14
+ * - Wildcard prefix: star/api matches /v1/api, /public/api
15
+ * - Wildcard anywhere: /admin/star/users matches /admin/123/users
16
+ *
17
+ * Does NOT support:
18
+ * - Character classes: [abc]
19
+ * - Brace expansion: curly braces
20
+ * - Double asterisk: treated same as single asterisk
21
+ *
22
+ * @param pattern - Glob pattern string (e.g., /admin/*)
23
+ * @param path - Path to test (e.g., /admin/dashboard)
24
+ * @returns true if path matches pattern, false otherwise
25
+ */
26
+ export function matchGlobPattern(pattern: string, path: string): boolean {
27
+ // Escape special regex characters except *
28
+ const escapedPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*')
29
+
30
+ // Create regex with exact start and end anchors
31
+ const regex = new RegExp(`^${escapedPattern}$`)
32
+
33
+ return regex.test(path)
34
+ }
35
+
36
+ /**
37
+ * Check if a path matches any of the provided glob patterns.
38
+ *
39
+ * @param patterns - Array of glob patterns
40
+ * @param path - Path to test
41
+ * @returns true if path matches any pattern, false otherwise
42
+ */
43
+ export function matchesAnyGlobPattern(
44
+ patterns: readonly string[] | undefined,
45
+ path: string
46
+ ): boolean {
47
+ if (!patterns || patterns.length === 0) return false
48
+
49
+ return patterns.some((pattern) => matchGlobPattern(pattern, path))
50
+ }