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,43 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import type { Session } from '@/application/ports/models/user-session'
9
+ import type { ContextWithSession } from '@/presentation/api/middleware/auth'
10
+ import type { ContextWithTableAndRole } from '@/presentation/api/middleware/table'
11
+ import type { Context } from 'hono'
12
+
13
+ /**
14
+ * Extract session from context (optional session)
15
+ *
16
+ * Use for routes with authMiddleware but without requireAuth
17
+ *
18
+ * @param c - Hono context (after authMiddleware)
19
+ * @returns Session if authenticated, undefined otherwise
20
+ */
21
+ export function getSessionContext(c: Context): Session | undefined {
22
+ return (c as ContextWithSession).var.session
23
+ }
24
+
25
+ /**
26
+ * Extract table context from request
27
+ *
28
+ * Use for routes with full middleware chain:
29
+ * authMiddleware → requireAuth → validateTable → enrichUserRole
30
+ *
31
+ * **Session is guaranteed to exist** (requireAuth ensures this)
32
+ *
33
+ * @param c - Hono context (after full middleware chain)
34
+ * @returns Object containing session, tableName, tableId, and userRole
35
+ */
36
+ export function getTableContext(c: Context): {
37
+ readonly session: Session
38
+ readonly tableName: string
39
+ readonly tableId: string
40
+ readonly userRole: string
41
+ } {
42
+ return (c as ContextWithTableAndRole).var
43
+ }
@@ -0,0 +1,235 @@
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 { logDebug, logError } from '@/infrastructure/logging/logger'
9
+ import type { ContentfulStatusCode } from 'hono/utils/http-status'
10
+
11
+ /**
12
+ * Sanitized error codes for client responses
13
+ *
14
+ * These codes are safe to expose to clients and map to standard HTTP status codes.
15
+ */
16
+ export type ErrorCode =
17
+ | 'UNAUTHORIZED'
18
+ | 'FORBIDDEN'
19
+ | 'NOT_FOUND'
20
+ | 'VALIDATION_ERROR'
21
+ | 'CONFLICT'
22
+ | 'RATE_LIMITED'
23
+ | 'INTERNAL_ERROR'
24
+ | 'SERVICE_UNAVAILABLE'
25
+
26
+ /**
27
+ * Sanitized error response for clients
28
+ *
29
+ * This interface ensures that only safe, user-friendly error information
30
+ * is exposed to clients, preventing information disclosure vulnerabilities.
31
+ */
32
+ export interface SanitizedError {
33
+ readonly error: string
34
+ readonly code: ErrorCode
35
+ readonly message?: string
36
+ readonly details?: readonly string[]
37
+ }
38
+
39
+ /**
40
+ * Check if error indicates "not found"
41
+ *
42
+ * Detects various patterns that indicate a resource was not found,
43
+ * including access denied cases (which we return as 404 to avoid
44
+ * leaking existence of protected resources).
45
+ */
46
+ function isNotFoundError(error: unknown): boolean {
47
+ const errorMessage =
48
+ error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase()
49
+
50
+ return errorMessage.includes('not found') || errorMessage.includes('access denied')
51
+ }
52
+
53
+ /**
54
+ * Error object with dynamic properties
55
+ */
56
+ interface ErrorObject {
57
+ readonly toJSON?: () => {
58
+ readonly cause?: {
59
+ readonly failure?: {
60
+ readonly _tag?: string
61
+ readonly message?: string
62
+ readonly details?: readonly string[]
63
+ }
64
+ }
65
+ }
66
+ readonly _tag?: string
67
+ readonly message?: string
68
+ readonly details?: readonly string[]
69
+ }
70
+
71
+ /**
72
+ * Log error details for debugging (server-side only)
73
+ */
74
+ function logErrorDetails(error: unknown, requestId: string | undefined): void {
75
+ logError(`[API Error] requestId=${requestId}`, error)
76
+ logDebug(`[API Error - error type] ${typeof error}`)
77
+ logDebug(
78
+ `[API Error - error own keys] ${error && typeof error === 'object' ? Object.keys(error).join(', ') : 'not an object'}`
79
+ )
80
+ logDebug(
81
+ `[API Error - error all keys] ${error && typeof error === 'object' ? Object.getOwnPropertyNames(error).join(', ') : 'not an object'}`
82
+ )
83
+ const errObj = error as ErrorObject
84
+ logDebug(`[API Error - error _tag] ${errObj._tag}`)
85
+ logDebug(`[API Error - error message] ${errObj.message}`)
86
+ logDebug(`[API Error - error details] ${JSON.stringify(errObj.details)}`)
87
+ }
88
+
89
+ /**
90
+ * Extract actual error from Effect FiberFailure wrapper
91
+ */
92
+ function extractActualError(error: unknown): ErrorObject {
93
+ const errorObj = error as ErrorObject
94
+
95
+ // Try to extract the error from FiberFailure via toJSON()
96
+ if (errorObj && typeof errorObj === 'object' && errorObj.toJSON) {
97
+ try {
98
+ const jsonRep = errorObj.toJSON()
99
+ if (jsonRep?.cause?.failure) {
100
+ const actualError = jsonRep.cause.failure
101
+ logDebug(
102
+ `[API Error - extracted from toJSON cause.failure] _tag=${actualError._tag} message=${actualError.message} details=${JSON.stringify(actualError.details)}`
103
+ )
104
+ return actualError
105
+ }
106
+ } catch (e) {
107
+ logDebug(`[API Error - toJSON extraction failed] ${e}`)
108
+ }
109
+ }
110
+
111
+ return errorObj
112
+ }
113
+
114
+ /**
115
+ * Map tagged error to sanitized response
116
+ */
117
+ function mapTaggedError(errorTag: string, actualError: ErrorObject): SanitizedError | undefined {
118
+ switch (errorTag) {
119
+ case 'ForbiddenError':
120
+ case 'ActivityLogForbiddenError':
121
+ return {
122
+ error: 'Forbidden',
123
+ code: 'FORBIDDEN',
124
+ message: actualError.message,
125
+ }
126
+ case 'ValidationError':
127
+ return {
128
+ error: 'Validation Error',
129
+ code: 'VALIDATION_ERROR',
130
+ message: actualError.message ?? 'Invalid input data',
131
+ details: actualError.details,
132
+ }
133
+ case 'UniqueConstraintViolationError':
134
+ return {
135
+ error: 'Conflict',
136
+ code: 'CONFLICT',
137
+ message: 'Resource already exists',
138
+ }
139
+ case 'NotFoundError':
140
+ case 'TableNotFoundError':
141
+ return {
142
+ error: 'Not Found',
143
+ code: 'NOT_FOUND',
144
+ message: 'Resource not found',
145
+ }
146
+ default:
147
+ return undefined
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Sanitize errors for client responses
153
+ *
154
+ * ✅ Removes internal details (file paths, SQL, stack traces, database schemas)
155
+ * ✅ Maps to generic error codes and user-safe messages
156
+ * ✅ Logs full error server-side for debugging
157
+ * ✅ Returns only information safe to expose to clients
158
+ *
159
+ * **Security Benefits:**
160
+ * - Prevents database schema discovery through constraint errors
161
+ * - Hides internal architecture (file paths, service URLs)
162
+ * - Conceals SQL query structure
163
+ * - Protects authorization logic details
164
+ *
165
+ * @param error - The error to sanitize
166
+ * @param requestId - Optional request ID for correlation in logs
167
+ * @returns Sanitized error safe for client consumption
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * try {
172
+ * await dangerousOperation()
173
+ * } catch (error) {
174
+ * const sanitized = sanitizeError(error, requestId)
175
+ * return c.json(sanitized, getStatusCode(sanitized.code))
176
+ * }
177
+ * ```
178
+ */
179
+ export function sanitizeError(error: unknown, requestId?: string): SanitizedError {
180
+ logErrorDetails(error, requestId)
181
+
182
+ const actualError = extractActualError(error)
183
+ const errorTag = actualError._tag
184
+
185
+ // Handle known safe error types
186
+ if (errorTag) {
187
+ const sanitized = mapTaggedError(errorTag, actualError)
188
+ if (sanitized) return sanitized
189
+ }
190
+
191
+ // Check for not-found patterns (includes access denied to avoid leaking existence)
192
+ if (isNotFoundError(error)) {
193
+ return {
194
+ error: 'Not Found',
195
+ code: 'NOT_FOUND',
196
+ message: 'Resource not found',
197
+ }
198
+ }
199
+
200
+ // Generic internal error (no details leaked to prevent information disclosure)
201
+ return {
202
+ error: 'Internal Server Error',
203
+ code: 'INTERNAL_ERROR',
204
+ message: 'An unexpected error occurred. Please try again later.',
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Get HTTP status code for error code
210
+ *
211
+ * Maps sanitized error codes to appropriate HTTP status codes.
212
+ *
213
+ * @param code - The error code
214
+ * @returns HTTP status code
215
+ */
216
+ export function getStatusCode(code: ErrorCode): ContentfulStatusCode {
217
+ switch (code) {
218
+ case 'UNAUTHORIZED':
219
+ return 401
220
+ case 'FORBIDDEN':
221
+ return 403
222
+ case 'NOT_FOUND':
223
+ return 404
224
+ case 'VALIDATION_ERROR':
225
+ return 400
226
+ case 'CONFLICT':
227
+ return 409
228
+ case 'RATE_LIMITED':
229
+ return 429
230
+ case 'INTERNAL_ERROR':
231
+ return 500
232
+ case 'SERVICE_UNAVAILABLE':
233
+ return 503
234
+ }
235
+ }
@@ -0,0 +1,53 @@
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 { hasPermission } from '@/domain/models/app/table/permissions'
9
+ import type { App } from '@/domain/models/app'
10
+
11
+ /**
12
+ * Check if user has permission to write to specific fields
13
+ *
14
+ * This implements Better Auth layer field permission validation.
15
+ * Returns forbidden fields that the user cannot write to.
16
+ *
17
+ * @param app - Application configuration
18
+ * @param tableName - Name of the table
19
+ * @param userRole - User's role
20
+ * @param fields - Fields being updated
21
+ * @returns Array of field names user cannot write to (empty if all allowed)
22
+ */
23
+ export function validateFieldWritePermissions(
24
+ app: App,
25
+ tableName: string,
26
+ userRole: string,
27
+ fields: Readonly<Record<string, unknown>>
28
+ ): readonly string[] {
29
+ // Find table definition
30
+ const table = app.tables?.find((t) => t.name === tableName)
31
+ if (!table) {
32
+ return []
33
+ }
34
+
35
+ // Check each field being updated using functional approach
36
+ const forbiddenFields = Object.keys(fields)
37
+ .map((fieldName) => {
38
+ // Check explicit field permissions first
39
+ const fieldPermission = table.permissions?.fields?.find((fp) => fp.field === fieldName)
40
+ if (fieldPermission?.write) {
41
+ if (!hasPermission(fieldPermission.write, userRole)) {
42
+ return fieldName
43
+ }
44
+ return undefined
45
+ }
46
+
47
+ // No explicit field permission configured — field is writable by all roles
48
+ return undefined
49
+ })
50
+ .filter((field): field is string => field !== undefined)
51
+
52
+ return forbiddenFields
53
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ import type { App } from '@/domain/models/app'
9
+ import type { TablePermission } from '@/domain/models/app/table/permissions'
10
+
11
+ /**
12
+ * Filter condition with field reference
13
+ */
14
+ interface FilterCondition {
15
+ readonly field: string
16
+ readonly operator: string
17
+ readonly value: unknown
18
+ }
19
+
20
+ /**
21
+ * Filter structure supporting AND/OR logic
22
+ */
23
+ interface Filter {
24
+ readonly and?: readonly FilterCondition[]
25
+ readonly or?: readonly FilterCondition[]
26
+ }
27
+
28
+ /**
29
+ * Validate that user has permission to filter by specified fields
30
+ *
31
+ * This prevents users from querying data based on fields they cannot read.
32
+ * Returns array of field names user cannot filter by.
33
+ *
34
+ * @param app - Application configuration
35
+ * @param tableName - Name of the table
36
+ * @param userRole - User's role
37
+ * @param filter - Filter object to validate
38
+ * @returns Array of field names user cannot filter by (empty if all allowed)
39
+ */
40
+ export function validateFilterFieldPermissions(
41
+ app: App,
42
+ tableName: string,
43
+ userRole: string,
44
+ filter: Filter
45
+ ): readonly string[] {
46
+ // Find table definition
47
+ const table = app.tables?.find((t) => t.name === tableName)
48
+ if (!table?.permissions?.fields) {
49
+ return [] // No field permissions defined
50
+ }
51
+
52
+ // Extract all field names from filter conditions using immutable patterns
53
+ const andFields = filter.and ? filter.and.map((condition) => condition.field) : []
54
+ const orFields = filter.or ? filter.or.map((condition) => condition.field) : []
55
+ const fieldNames = [...andFields, ...orFields]
56
+
57
+ // Check each field used in filter
58
+ const forbiddenFields = fieldNames
59
+ .map((fieldName) => {
60
+ const fieldPermission = table.permissions?.fields?.find((fp) => fp.field === fieldName)
61
+ if (!fieldPermission?.read) {
62
+ return undefined // No read restriction on this field
63
+ }
64
+
65
+ if (!hasReadPermission(fieldPermission.read, userRole)) {
66
+ return fieldName
67
+ }
68
+
69
+ return undefined
70
+ })
71
+ .filter((field): field is string => field !== undefined)
72
+
73
+ return forbiddenFields
74
+ }
75
+
76
+ /**
77
+ * Check if user's role has read permission
78
+ */
79
+ function hasReadPermission(permission: TablePermission, userRole: string): boolean {
80
+ if (permission === 'all') {
81
+ return true
82
+ }
83
+ if (permission === 'authenticated') {
84
+ return true // User is authenticated (has session)
85
+ }
86
+ if (Array.isArray(permission)) {
87
+ return permission.includes(userRole)
88
+ }
89
+ return false
90
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Copyright (c) 2025 ESSENTIAL SERVICES
3
+ *
4
+ * This source code is licensed under the Business Source License 1.1
5
+ * found in the LICENSE.md file in the root directory of this source tree.
6
+ */
7
+
8
+ /**
9
+ * API Utility Exports
10
+ */
11
+
12
+ export { runEffect } from './run-effect'
13
+ export { validateRequest } from './validate-request'
@@ -0,0 +1,94 @@
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 } from 'effect'
9
+ import { errorResponseSchema } from '@/domain/models/api/error'
10
+ import { sanitizeError, getStatusCode } from './error-sanitizer'
11
+ import type { Context } from 'hono'
12
+ import type { ContentfulStatusCode } from 'hono/utils/http-status'
13
+
14
+ /**
15
+ * Schema interface for Zod-compatible parsing
16
+ */
17
+ interface ParseableSchema<T> {
18
+ readonly parse: (data: unknown) => T
19
+ }
20
+
21
+ /**
22
+ * Handle error response generation
23
+ *
24
+ * Uses centralized error sanitization to prevent information disclosure.
25
+ * Removes internal details (file paths, SQL errors, stack traces) from client responses.
26
+ */
27
+ function handleErrorResponse(c: Context, error: unknown) {
28
+ // Generate unique request ID for error correlation
29
+ const requestId = crypto.randomUUID()
30
+
31
+ // Sanitize error (removes internal details, logs full error server-side)
32
+ const sanitized = sanitizeError(error, requestId)
33
+ const statusCode = getStatusCode(sanitized.code)
34
+
35
+ const errorData = {
36
+ success: false as const,
37
+ error: sanitized.error,
38
+ message: sanitized.message ?? sanitized.error,
39
+ code: sanitized.code,
40
+ ...(sanitized.details ? { details: sanitized.details } : {}),
41
+ }
42
+
43
+ return c.json(errorResponseSchema.parse(errorData), statusCode)
44
+ }
45
+
46
+ /**
47
+ * Run an Effect program and return a Hono JSON response
48
+ *
49
+ * This utility handles:
50
+ * - Running Effect programs as promises
51
+ * - Validating responses against Zod schemas
52
+ * - Converting errors to standardized error responses
53
+ *
54
+ * @param c - Hono context for response generation
55
+ * @param program - Effect program to execute
56
+ * @param schema - Zod schema for response validation
57
+ * @param successStatus - HTTP status code for successful response (default: 200)
58
+ * @returns JSON response with validated data or error
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * app.get('/api/users', async (c) =>
63
+ * runEffect(c, listUsersProgram(), listUsersResponseSchema)
64
+ * )
65
+ * app.post('/api/users', async (c) =>
66
+ * runEffect(c, createUserProgram(), createUserResponseSchema, 201)
67
+ * )
68
+ * ```
69
+ */
70
+ export async function runEffect<T, S>(
71
+ c: Context,
72
+ program: Effect.Effect<T, Error>,
73
+ schema?: ParseableSchema<S>,
74
+ successStatus: number = 200
75
+ ) {
76
+ try {
77
+ // Use Effect.either to preserve tagged error types (_tag property)
78
+ // Effect.runPromise wraps errors in FiberFailure which strips _tag,
79
+ // preventing error sanitizer from mapping to correct HTTP status codes.
80
+ // Effect.either converts failures to Either.Left, preserving the original error.
81
+ const either = await Effect.runPromise(Effect.either(program))
82
+
83
+ if (either._tag === 'Left') {
84
+ return handleErrorResponse(c, either.left)
85
+ }
86
+
87
+ const validated = schema ? schema.parse(either.right) : either.right
88
+ return c.json(validated, successStatus as ContentfulStatusCode)
89
+ } catch (error) {
90
+ // Catches defects (Effect.die), schema validation errors,
91
+ // and other unexpected runtime errors
92
+ return handleErrorResponse(c, error)
93
+ }
94
+ }
@@ -0,0 +1,89 @@
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
+ import { validationErrorResponseSchema } from '@/domain/models/api/error'
10
+ import type { Context, TypedResponse } from 'hono'
11
+
12
+ /**
13
+ * Result type for validation - either success with data or error response
14
+ */
15
+ export type ValidationResult<T> =
16
+ | { readonly success: true; readonly data: T }
17
+ | { readonly success: false; readonly response: TypedResponse<unknown> }
18
+
19
+ /**
20
+ * Format Zod validation errors into API-friendly format
21
+ */
22
+ const formatZodErrors = (error: z.ZodError) =>
23
+ error.issues.map((issue) => ({
24
+ field: issue.path.join('.'),
25
+ message: issue.message,
26
+ code: issue.code,
27
+ }))
28
+
29
+ /**
30
+ * Validate request body against a Zod schema
31
+ *
32
+ * Returns validated data on success, or a formatted error response.
33
+ *
34
+ * @param c - Hono context
35
+ * @param schema - Zod schema to validate against
36
+ * @returns Validation result with data or error response
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * app.post('/api/records', async (c) => {
41
+ * const result = await validateRequest(c, createRecordRequestSchema)
42
+ * if (!result.success) return result.response
43
+ * // result.data is fully typed and validated
44
+ * })
45
+ * ```
46
+ */
47
+ export async function validateRequest<T>(
48
+ c: Context,
49
+ schema: z.ZodType<T>
50
+ ): Promise<ValidationResult<T>> {
51
+ try {
52
+ const rawBody = await c.req.json()
53
+ const data = schema.parse(rawBody)
54
+ return { success: true, data }
55
+ } catch (error) {
56
+ if (error instanceof z.ZodError) {
57
+ // Check if error is about large array size exceeding maximum (Payload Too Large)
58
+ // Only return 413 for arrays with max >= 1000 (infrastructure limits)
59
+ // Return 400 for smaller limits (operational/business constraints)
60
+ const hasLargePayloadError = error.issues.some((issue) => {
61
+ if (issue.code !== 'too_big') return false
62
+ if (!('origin' in issue) || (issue as { origin?: string }).origin !== 'array') return false
63
+ if (!('maximum' in issue)) return false
64
+ const { maximum } = issue as { maximum?: number }
65
+ return typeof maximum === 'number' && maximum >= 1000
66
+ })
67
+
68
+ if (hasLargePayloadError) {
69
+ return { success: false, response: c.json({ error: 'PayloadTooLarge' }, 413) }
70
+ }
71
+
72
+ const errorResponse = validationErrorResponseSchema.parse({
73
+ success: false,
74
+ message: 'Validation failed',
75
+ code: 'VALIDATION_ERROR',
76
+ errors: formatZodErrors(error),
77
+ })
78
+ return { success: false, response: c.json(errorResponse, 400) }
79
+ }
80
+ // JSON parse error or unexpected error
81
+ const errorResponse = validationErrorResponseSchema.parse({
82
+ success: false,
83
+ message: 'Invalid JSON body',
84
+ code: 'VALIDATION_ERROR',
85
+ errors: [{ field: 'body', message: 'Request body must be valid JSON' }],
86
+ })
87
+ return { success: false, response: c.json(errorResponse, 400) }
88
+ }
89
+ }
@@ -0,0 +1,29 @@
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
+ * Validation Module Exports
10
+ */
11
+
12
+ export {
13
+ ValidationError,
14
+ PermissionError,
15
+ ValidationContext,
16
+ createValidationLayer,
17
+ formatValidationError,
18
+ type ValidationResult,
19
+ } from '../middleware/validation'
20
+
21
+ export {
22
+ validateReadonlyIdField,
23
+ validateDefaultFields,
24
+ validateRequiredFields,
25
+ filterAllowedFields,
26
+ validateFieldWritePermissions,
27
+ } from './rules/field-rules'
28
+
29
+ export { validateRecordCreation, validateRecordUpdate } from './rules/record-rules'