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
package/src/cli.ts ADDED
@@ -0,0 +1,370 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Copyright (c) 2025 ESSENTIAL SERVICES
4
+ *
5
+ * This source code is licensed under the Business Source License 1.1
6
+ * found in the LICENSE.md file in the root directory of this source tree.
7
+ */
8
+
9
+ /**
10
+ * Sovrium CLI - Command-line interface for Sovrium operations
11
+ *
12
+ * This script provides commands for running a Sovrium server or building static sites.
13
+ *
14
+ * ## Commands
15
+ *
16
+ * ### sovrium start [config]
17
+ * Start a development server
18
+ * ```bash
19
+ * sovrium start app.json # Load from JSON file
20
+ * sovrium start app.yaml # Load from YAML file
21
+ * APP_SCHEMA='{"name":"My App"}' sovrium # Inline JSON
22
+ * APP_SCHEMA='name: My App' sovrium # Inline YAML
23
+ * APP_SCHEMA='https://example.com/app.yaml' # Remote URL
24
+ * ```
25
+ *
26
+ * ### sovrium build [config]
27
+ * Build static site files
28
+ * ```bash
29
+ * sovrium build app.json # Load from JSON file
30
+ * SOVRIUM_OUTPUT_DIR=./dist sovrium build # Or use env variable
31
+ * ```
32
+ *
33
+ * ## Arguments
34
+ * - `config` (optional) - Path to config file (JSON or YAML)
35
+ *
36
+ * ## Environment Variables (start command)
37
+ * - `APP_SCHEMA` (optional if file provided) - App schema (inline JSON, YAML, or remote URL)
38
+ * - `PORT` (optional) - Server port (default: 3000)
39
+ * - `HOSTNAME` (optional) - Server hostname (default: localhost)
40
+ *
41
+ * ## Environment Variables (build command)
42
+ * - `APP_SCHEMA` (optional if file provided) - App schema (inline JSON, YAML, or remote URL)
43
+ * - `SOVRIUM_OUTPUT_DIR` (optional) - Output directory (default: ./dist)
44
+ * - `SOVRIUM_BASE_URL` (optional) - Base URL for sitemap
45
+ * - `SOVRIUM_BASE_PATH` (optional) - Base path for deployments
46
+ * - `SOVRIUM_DEPLOYMENT` (optional) - Deployment type (github-pages | generic)
47
+ * - `SOVRIUM_LANGUAGES` (optional) - Comma-separated language codes
48
+ * - `SOVRIUM_DEFAULT_LANGUAGE` (optional) - Default language
49
+ * - `SOVRIUM_GENERATE_SITEMAP` (optional) - Generate sitemap.xml (true/false)
50
+ * - `SOVRIUM_GENERATE_ROBOTS` (optional) - Generate robots.txt (true/false)
51
+ * - `SOVRIUM_HYDRATION` (optional) - Enable client-side hydration (true/false)
52
+ * - `SOVRIUM_GENERATE_MANIFEST` (optional) - Generate manifest.json (true/false)
53
+ * - `SOVRIUM_BUNDLE_OPTIMIZATION` (optional) - Bundle optimization strategy
54
+ */
55
+
56
+ import { watch } from 'node:fs'
57
+ import { Effect, Console } from 'effect'
58
+ import { start, build, type StartOptions, type GenerateStaticOptions } from '@/index'
59
+ import { parseAppSchema, loadSchemaFromFileForReload } from '@/presentation/cli'
60
+
61
+ // Server instance type (returned by start())
62
+ type ServerInstance = Awaited<ReturnType<typeof start>>
63
+
64
+ /**
65
+ * Reload server with new configuration from changed config file
66
+ *
67
+ * This function is called when the config file changes during --watch mode.
68
+ * It gracefully reloads the server with the new configuration.
69
+ *
70
+ * @param filePath - Path to the config file that changed
71
+ * @param currentServer - The currently running server instance
72
+ * @param options - Server options (port, hostname)
73
+ * @returns Promise with the new server instance
74
+ * @throws Error if config file cannot be parsed or new server fails to start
75
+ */
76
+ const reloadServer = async (
77
+ filePath: string,
78
+ currentServer: ServerInstance,
79
+ options: StartOptions
80
+ ): Promise<ServerInstance> => {
81
+ // Parse the new config file (throws on invalid JSON/YAML, doesn't exit process)
82
+ const newApp = await loadSchemaFromFileForReload(filePath)
83
+
84
+ // Stop the current server before starting the new one
85
+ // eslint-disable-next-line functional/no-expression-statements
86
+ await currentServer.stop()
87
+
88
+ // Start new server with the updated config
89
+ const newServer = await start(newApp, options)
90
+
91
+ return newServer
92
+ }
93
+
94
+ /**
95
+ * Show CLI help text
96
+ */
97
+ const showHelp = (): void => {
98
+ const helpText = [
99
+ 'Sovrium CLI',
100
+ '',
101
+ 'Commands:',
102
+ ' sovrium start [config] Start a development server (default)',
103
+ ' sovrium build [config] Build static site files',
104
+ ' sovrium --help Show this help message',
105
+ '',
106
+ 'Options:',
107
+ ' --watch, -w Watch config file for changes and hot reload',
108
+ '',
109
+ 'Supported config formats: .json, .yaml, .yml',
110
+ '',
111
+ 'Examples:',
112
+ ' # Start development server with JSON file',
113
+ ' sovrium start app.json',
114
+ '',
115
+ ' # Start with YAML file',
116
+ ' sovrium start app.yaml',
117
+ '',
118
+ ' # Start with watch mode (hot reload on config changes)',
119
+ ' sovrium start app.yaml --watch',
120
+ '',
121
+ ' # Start with environment variable (JSON, YAML, or URL)',
122
+ ' APP_SCHEMA=\'{"name":"My App"}\' sovrium start',
123
+ '',
124
+ ' # Build static site',
125
+ ' sovrium build app.json',
126
+ '',
127
+ 'For more information, see the documentation at https://sovrium.com/docs/cli',
128
+ ]
129
+
130
+ Effect.runSync(Console.log(helpText.join('\n')))
131
+ }
132
+
133
+ /**
134
+ * Parse server options from environment variables
135
+ */
136
+ const parseStartOptions = (): StartOptions => {
137
+ const port = Bun.env.PORT
138
+ const hostname = Bun.env.HOSTNAME
139
+
140
+ if (!port && !hostname) {
141
+ return {}
142
+ }
143
+
144
+ const parsedPort = port ? parseInt(port, 10) : undefined
145
+ if (parsedPort !== undefined && (isNaN(parsedPort) || parsedPort < 0 || parsedPort > 65_535)) {
146
+ Effect.runSync(
147
+ Console.error(
148
+ `Error: Invalid port number "${port}". Must be between 0 and 65535 (0 = auto-select).`
149
+ )
150
+ )
151
+ // Terminate process - imperative statement required for CLI
152
+ // eslint-disable-next-line functional/no-expression-statements
153
+ process.exit(1)
154
+ }
155
+
156
+ return {
157
+ ...(parsedPort !== undefined && { port: parsedPort }),
158
+ ...(hostname && { hostname }),
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Handle the 'start' command
164
+ */
165
+ const handleStartCommand = async (filePath?: string, watchMode = false): Promise<void> => {
166
+ const app = await parseAppSchema('start', filePath)
167
+ const options = parseStartOptions()
168
+
169
+ Effect.runSync(
170
+ Effect.gen(function* () {
171
+ yield* Console.log('Starting Sovrium server from CLI...')
172
+ yield* Console.log(`App: ${app.name}${app.description ? ` - ${app.description}` : ''}`)
173
+ if (filePath) yield* Console.log(`Config: ${filePath}`)
174
+ if (options.port) yield* Console.log(`Port: ${options.port}`)
175
+ if (options.hostname) yield* Console.log(`Hostname: ${options.hostname}`)
176
+ if (watchMode) yield* Console.log(`Watch mode: enabled`)
177
+ yield* Console.log('')
178
+ })
179
+ )
180
+
181
+ // Start the server
182
+ const server = await start(app, options).catch((error) => {
183
+ Effect.runSync(Console.error('Failed to start server:', error))
184
+ // Terminate process - imperative statement required for CLI
185
+ // eslint-disable-next-line functional/no-expression-statements
186
+ process.exit(1)
187
+ })
188
+
189
+ // If watch mode enabled, set up file watcher
190
+ if (watchMode && filePath) {
191
+ console.log(`\n👀 Watching ${filePath} for changes...\n`)
192
+
193
+ // Track current server instance (mutable for watch mode)
194
+ // eslint-disable-next-line functional/no-let
195
+ let currentServer = server
196
+
197
+ // Set up file watcher using Node.js fs.watch (stable in Bun)
198
+
199
+ watch(filePath, async (eventType) => {
200
+ if (eventType === 'change') {
201
+ console.log(`\n🔄 Config changed, reloading...`)
202
+
203
+ try {
204
+ // eslint-disable-next-line functional/no-expression-statements
205
+ currentServer = await reloadServer(filePath, currentServer, options)
206
+
207
+ console.log(`✅ Server reloaded successfully\n`)
208
+ } catch (error) {
209
+ console.error(`❌ Reload failed: ${error instanceof Error ? error.message : error}\n`)
210
+ // Keep the old server running on error
211
+ }
212
+ }
213
+ })
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Parse boolean environment variable
219
+ */
220
+ const parseBooleanEnv = (value: string | undefined): boolean | undefined =>
221
+ value === 'true' ? true : value === 'false' ? false : undefined
222
+
223
+ /**
224
+ * Parse build options from environment variables
225
+ */
226
+ const parseBuildOptions = (): GenerateStaticOptions => {
227
+ // Parse environment variables
228
+ const envVars = {
229
+ outputDir: Bun.env.SOVRIUM_OUTPUT_DIR,
230
+ baseUrl: Bun.env.SOVRIUM_BASE_URL,
231
+ basePath: Bun.env.SOVRIUM_BASE_PATH,
232
+ deployment: Bun.env.SOVRIUM_DEPLOYMENT as 'github-pages' | 'generic' | undefined,
233
+ languages: Bun.env.SOVRIUM_LANGUAGES?.split(',').map((lang) => lang.trim()),
234
+ defaultLanguage: Bun.env.SOVRIUM_DEFAULT_LANGUAGE,
235
+ generateSitemap: parseBooleanEnv(Bun.env.SOVRIUM_GENERATE_SITEMAP),
236
+ generateRobotsTxt: parseBooleanEnv(Bun.env.SOVRIUM_GENERATE_ROBOTS),
237
+ hydration: parseBooleanEnv(Bun.env.SOVRIUM_HYDRATION),
238
+ generateManifest: parseBooleanEnv(Bun.env.SOVRIUM_GENERATE_MANIFEST),
239
+ bundleOptimization: Bun.env.SOVRIUM_BUNDLE_OPTIMIZATION as 'split' | 'none' | undefined,
240
+ publicDir: Bun.env.SOVRIUM_PUBLIC_DIR,
241
+ }
242
+
243
+ // Build options object with only defined values
244
+ const options = [
245
+ { key: 'outputDir', value: envVars.outputDir },
246
+ { key: 'baseUrl', value: envVars.baseUrl },
247
+ { key: 'basePath', value: envVars.basePath },
248
+ { key: 'deployment', value: envVars.deployment },
249
+ { key: 'languages', value: envVars.languages },
250
+ { key: 'defaultLanguage', value: envVars.defaultLanguage },
251
+ { key: 'generateSitemap', value: envVars.generateSitemap },
252
+ { key: 'generateRobotsTxt', value: envVars.generateRobotsTxt },
253
+ { key: 'hydration', value: envVars.hydration },
254
+ { key: 'generateManifest', value: envVars.generateManifest },
255
+ { key: 'bundleOptimization', value: envVars.bundleOptimization },
256
+ { key: 'publicDir', value: envVars.publicDir },
257
+ ].reduce(
258
+ (acc, { key, value }) =>
259
+ value !== undefined && value !== false ? { ...acc, [key]: value } : acc,
260
+ {} as GenerateStaticOptions
261
+ )
262
+
263
+ return options
264
+ }
265
+
266
+ /**
267
+ * Handle the 'build' command
268
+ */
269
+ const handleBuildCommand = async (filePath?: string): Promise<void> => {
270
+ const app = await parseAppSchema('build', filePath)
271
+ const options = parseBuildOptions()
272
+
273
+ Effect.runSync(
274
+ Effect.gen(function* () {
275
+ yield* Console.log('Building static site from CLI...')
276
+ yield* Console.log(`App: ${app.name}${app.description ? ` - ${app.description}` : ''}`)
277
+ if (filePath) yield* Console.log(`Config: ${filePath}`)
278
+ if (options.outputDir) yield* Console.log(`Output directory: ${options.outputDir}`)
279
+ if (options.baseUrl) yield* Console.log(`Base URL: ${options.baseUrl}`)
280
+ if (options.deployment) yield* Console.log(`Deployment: ${options.deployment}`)
281
+ yield* Console.log('')
282
+ })
283
+ )
284
+
285
+ // Build static site
286
+ // eslint-disable-next-line functional/no-expression-statements
287
+ await build(app, options).catch((error) => {
288
+ Effect.runSync(Console.error('Failed to build static site:', error))
289
+ // Terminate process - imperative statement required for CLI
290
+ // eslint-disable-next-line functional/no-expression-statements
291
+ process.exit(1)
292
+ })
293
+ }
294
+
295
+ // Main CLI entry point
296
+ const args = Bun.argv.slice(2)
297
+
298
+ /**
299
+ * Check if argument is a config file path (JSON, YAML, or YML)
300
+ */
301
+ const isConfigFile = (arg: string | undefined): boolean =>
302
+ arg !== undefined &&
303
+ (arg.endsWith('.json') ||
304
+ arg.endsWith('.yaml') ||
305
+ arg.endsWith('.yml') ||
306
+ arg.endsWith('.JSON') ||
307
+ arg.endsWith('.YAML') ||
308
+ arg.endsWith('.YML') ||
309
+ arg.includes('/'))
310
+
311
+ /**
312
+ * Parse CLI arguments into command, config file, and flags
313
+ */
314
+ const parseArgs = (
315
+ argv: readonly string[]
316
+ ): { readonly command: string; readonly configFile?: string; readonly watchMode: boolean } => {
317
+ const watchMode = argv.includes('--watch') || argv.includes('-w')
318
+ const nonFlagArgs = argv.filter((arg) => !arg.startsWith('-'))
319
+
320
+ const command = nonFlagArgs[0] || 'start'
321
+ const configFile = nonFlagArgs[1]
322
+
323
+ // Handle case where first arg is a config file (implicit 'start' command)
324
+ if (isConfigFile(command)) {
325
+ return { command: 'start', configFile: command, watchMode }
326
+ }
327
+
328
+ return { command, configFile, watchMode }
329
+ }
330
+
331
+ const { command, configFile, watchMode } = parseArgs(args)
332
+
333
+ // Execute command - side effects required for CLI operation
334
+ ;(async () => {
335
+ switch (command) {
336
+ case 'start':
337
+ // eslint-disable-next-line functional/no-expression-statements -- CLI command execution requires side effects
338
+ await handleStartCommand(configFile, watchMode)
339
+ break
340
+ case 'build':
341
+ // eslint-disable-next-line functional/no-expression-statements -- CLI command execution requires side effects
342
+ await handleBuildCommand(configFile)
343
+ break
344
+ case '--help':
345
+ case '-h':
346
+ case 'help':
347
+ showHelp()
348
+ // Terminate process - imperative statement required for CLI
349
+ // eslint-disable-next-line functional/no-expression-statements
350
+ process.exit(0)
351
+ break
352
+ default:
353
+ if (!command.startsWith('-')) {
354
+ // Unknown command without dash - try as start command
355
+ // eslint-disable-next-line functional/no-expression-statements -- CLI command execution requires side effects
356
+ await handleStartCommand(undefined, watchMode)
357
+ } else {
358
+ Effect.runSync(
359
+ Effect.gen(function* () {
360
+ yield* Console.error(`Error: Unknown command "${command}"`)
361
+ yield* Console.error('')
362
+ })
363
+ )
364
+ showHelp()
365
+ // Terminate process - imperative statement required for CLI
366
+ // eslint-disable-next-line functional/no-expression-statements
367
+ process.exit(1)
368
+ }
369
+ }
370
+ })()
@@ -0,0 +1,36 @@
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
+ * Factory function to create tagged error classes for Effect discriminated unions.
10
+ *
11
+ * This factory reduces boilerplate for error classes that follow the pattern:
12
+ * - `readonly _tag` property for discriminated union matching
13
+ * - `readonly cause` property to wrap underlying errors
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // Instead of manually defining:
18
+ * export class MyError {
19
+ * readonly _tag = 'MyError'
20
+ * constructor(readonly cause: unknown) {}
21
+ * }
22
+ *
23
+ * // Use the factory:
24
+ * export const MyError = createTaggedError('MyError')
25
+ * export type MyError = InstanceType<typeof MyError>
26
+ * ```
27
+ *
28
+ * @param tag - The unique tag string for the error type (used in Effect matching)
29
+ * @returns A class constructor that creates tagged error instances
30
+ */
31
+ export function createTaggedError<T extends string>(tag: T) {
32
+ return class {
33
+ readonly _tag = tag
34
+ constructor(readonly cause: unknown) {}
35
+ } as new (cause: unknown) => { readonly _tag: T; readonly cause: unknown }
36
+ }
@@ -0,0 +1,78 @@
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
+ export { createTaggedError } from './create-tagged-error'
9
+
10
+ /**
11
+ * Database session context error
12
+ */
13
+ export class SessionContextError extends Error {
14
+ readonly _tag = 'SessionContextError'
15
+ override readonly cause?: unknown
16
+
17
+ constructor(message: string, cause?: unknown) {
18
+ super(message)
19
+ // eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
20
+ this.name = 'SessionContextError'
21
+ // eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
22
+ this.cause = cause
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Forbidden error for authorization failures
28
+ */
29
+ export class ForbiddenError extends Error {
30
+ readonly _tag = 'ForbiddenError'
31
+
32
+ constructor(message: string) {
33
+ super(message)
34
+ // eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
35
+ this.name = 'ForbiddenError'
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Unique constraint violation error
41
+ * Thrown when attempting to insert/update a record that violates a unique constraint
42
+ */
43
+ export class UniqueConstraintViolationError extends Error {
44
+ readonly _tag = 'UniqueConstraintViolationError'
45
+ override readonly cause?: unknown
46
+
47
+ constructor(message: string, cause?: unknown) {
48
+ super(message)
49
+ // eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
50
+ this.name = 'UniqueConstraintViolationError'
51
+ // eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
52
+ this.cause = cause
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Validation error for invalid input data
58
+ * Thrown when input data fails validation before database operations
59
+ */
60
+ export class ValidationError extends Error {
61
+ readonly _tag = 'ValidationError'
62
+ readonly details?: readonly {
63
+ readonly record: number
64
+ readonly field: string
65
+ readonly error: string
66
+ }[]
67
+
68
+ constructor(
69
+ message: string,
70
+ details?: readonly { readonly record: number; readonly field: string; readonly error: string }[]
71
+ ) {
72
+ super(message)
73
+ // eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
74
+ this.name = 'ValidationError'
75
+ // eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
76
+ this.details = details
77
+ }
78
+ }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import { z } from 'zod'
9
+
10
+ // ============================================================================
11
+ // Collection Schema (POST /api/analytics/collect)
12
+ // ============================================================================
13
+
14
+ /**
15
+ * Analytics collection payload schema
16
+ *
17
+ * Minimal payload sent by the tracking script.
18
+ * Single-letter keys to minimize bandwidth usage.
19
+ */
20
+ export const analyticsCollectSchema = z.object({
21
+ /** Page path (required) */
22
+ p: z.string().min(1).describe('Page path being viewed'),
23
+ /** Page title (optional) */
24
+ t: z.string().optional().describe('Page title'),
25
+ /** Referrer URL (optional) */
26
+ r: z.string().optional().describe('Full referrer URL'),
27
+ /** Screen width (optional) */
28
+ sw: z.number().int().positive().optional().describe('Screen width in pixels'),
29
+ /** Screen height (optional) */
30
+ sh: z.number().int().positive().optional().describe('Screen height in pixels'),
31
+ /** UTM source (optional) */
32
+ us: z.string().optional().describe('UTM source parameter'),
33
+ /** UTM medium (optional) */
34
+ um: z.string().optional().describe('UTM medium parameter'),
35
+ /** UTM campaign (optional) */
36
+ uc: z.string().optional().describe('UTM campaign parameter'),
37
+ /** UTM content (optional) */
38
+ ux: z.string().optional().describe('UTM content parameter'),
39
+ /** UTM term (optional) */
40
+ ut: z.string().optional().describe('UTM term parameter'),
41
+ })
42
+
43
+ export type AnalyticsCollectPayload = z.infer<typeof analyticsCollectSchema>
44
+
45
+ // ============================================================================
46
+ // Query Parameters Schema (shared across query endpoints)
47
+ // ============================================================================
48
+
49
+ /**
50
+ * Analytics query parameters schema
51
+ *
52
+ * Shared query parameters for all analytics query endpoints.
53
+ */
54
+ export const analyticsQuerySchema = z.object({
55
+ /** Start of date range (ISO 8601) */
56
+ from: z.string().describe('Start of date range (ISO 8601 datetime)'),
57
+ /** End of date range (ISO 8601) */
58
+ to: z.string().describe('End of date range (ISO 8601 datetime)'),
59
+ /** Time series granularity */
60
+ granularity: z
61
+ .enum(['hour', 'day', 'week', 'month'])
62
+ .default('day')
63
+ .describe('Time series granularity'),
64
+ })
65
+
66
+ export type AnalyticsQueryParams = z.infer<typeof analyticsQuerySchema>
67
+
68
+ // ============================================================================
69
+ // Response Schemas
70
+ // ============================================================================
71
+
72
+ /**
73
+ * Time series data point
74
+ */
75
+ export const timeSeriesPointSchema = z.object({
76
+ period: z.string().describe('Time period start (ISO 8601)'),
77
+ pageViews: z.number().int().describe('Total page views in period'),
78
+ uniqueVisitors: z.number().int().describe('Unique visitors in period'),
79
+ sessions: z.number().int().describe('Unique sessions in period'),
80
+ })
81
+
82
+ export type TimeSeriesPoint = z.infer<typeof timeSeriesPointSchema>
83
+
84
+ /**
85
+ * Analytics overview response schema
86
+ *
87
+ * GET /api/analytics/overview
88
+ */
89
+ export const analyticsOverviewResponseSchema = z.object({
90
+ summary: z.object({
91
+ pageViews: z.number().int().describe('Total page views'),
92
+ uniqueVisitors: z.number().int().describe('Total unique visitors'),
93
+ sessions: z.number().int().describe('Total sessions'),
94
+ }),
95
+ timeSeries: z.array(timeSeriesPointSchema).describe('Time series data points'),
96
+ })
97
+
98
+ export type AnalyticsOverviewResponse = z.infer<typeof analyticsOverviewResponseSchema>
99
+
100
+ /**
101
+ * Top pages response schema
102
+ *
103
+ * GET /api/analytics/pages
104
+ */
105
+ export const analyticsTopPagesResponseSchema = z.object({
106
+ pages: z.array(
107
+ z.object({
108
+ path: z.string().describe('Page path'),
109
+ pageViews: z.number().int().describe('Total page views'),
110
+ uniqueVisitors: z.number().int().describe('Unique visitors'),
111
+ })
112
+ ),
113
+ total: z.number().int().describe('Total number of pages'),
114
+ })
115
+
116
+ export type AnalyticsTopPagesResponse = z.infer<typeof analyticsTopPagesResponseSchema>
117
+
118
+ /**
119
+ * Top referrers response schema
120
+ *
121
+ * GET /api/analytics/referrers
122
+ */
123
+ export const analyticsTopReferrersResponseSchema = z.object({
124
+ referrers: z.array(
125
+ z.object({
126
+ domain: z.string().nullable().describe('Referrer domain (null for direct traffic)'),
127
+ pageViews: z.number().int().describe('Total page views from this referrer'),
128
+ uniqueVisitors: z.number().int().describe('Unique visitors from this referrer'),
129
+ })
130
+ ),
131
+ total: z.number().int().describe('Total referrer entries'),
132
+ })
133
+
134
+ export type AnalyticsTopReferrersResponse = z.infer<typeof analyticsTopReferrersResponseSchema>
135
+
136
+ /**
137
+ * Device breakdown entry schema
138
+ */
139
+ const breakdownEntrySchema = z.object({
140
+ name: z.string().describe('Category name'),
141
+ count: z.number().int().describe('Number of page views'),
142
+ percentage: z.number().describe('Percentage of total (0-100)'),
143
+ })
144
+
145
+ /**
146
+ * Device breakdown response schema
147
+ *
148
+ * GET /api/analytics/devices
149
+ */
150
+ export const analyticsDevicesResponseSchema = z.object({
151
+ deviceTypes: z.array(breakdownEntrySchema).describe('Device type breakdown'),
152
+ browsers: z.array(breakdownEntrySchema).describe('Browser name breakdown'),
153
+ operatingSystems: z.array(breakdownEntrySchema).describe('OS name breakdown'),
154
+ })
155
+
156
+ export type AnalyticsDevicesResponse = z.infer<typeof analyticsDevicesResponseSchema>
157
+
158
+ /**
159
+ * Campaign entry schema
160
+ */
161
+ const campaignEntrySchema = z.object({
162
+ source: z.string().nullable().describe('UTM source'),
163
+ medium: z.string().nullable().describe('UTM medium'),
164
+ campaign: z.string().nullable().describe('UTM campaign'),
165
+ pageViews: z.number().int().describe('Total page views'),
166
+ uniqueVisitors: z.number().int().describe('Unique visitors'),
167
+ })
168
+
169
+ /**
170
+ * Campaigns response schema
171
+ *
172
+ * GET /api/analytics/campaigns
173
+ */
174
+ export const analyticsCampaignsResponseSchema = z.object({
175
+ campaigns: z.array(campaignEntrySchema).describe('UTM campaign breakdown'),
176
+ total: z.number().int().describe('Total campaign entries'),
177
+ })
178
+
179
+ export type AnalyticsCampaignsResponse = z.infer<typeof analyticsCampaignsResponseSchema>