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.
- package/CHANGELOG.md +3497 -0
- package/LICENSE.md +147 -0
- package/LICENSE_EE.md +297 -0
- package/README.md +321 -0
- package/drizzle/0000_melted_kabuki.sql +163 -0
- package/drizzle/meta/0000_snapshot.json +1216 -0
- package/drizzle/meta/_journal.json +13 -0
- package/package.json +167 -0
- package/schemas/0.0.1/app.openapi.json +70 -0
- package/schemas/0.0.1/app.schema.json +7961 -0
- package/schemas/0.0.2/app.openapi.json +80 -0
- package/schemas/0.0.2/app.schema.json +8829 -0
- package/schemas/development/app.openapi.json +70 -0
- package/schemas/development/app.schema.json +7456 -0
- package/src/application/errors/app-validation-error.ts +14 -0
- package/src/application/errors/static-generation-error.ts +16 -0
- package/src/application/metadata/favicon-transformer.ts +127 -0
- package/src/application/models/server.ts +27 -0
- package/src/application/ports/models/user-metadata.ts +36 -0
- package/src/application/ports/models/user-session.ts +34 -0
- package/src/application/ports/repositories/activity-log-repository.ts +68 -0
- package/src/application/ports/repositories/activity-repository.ts +49 -0
- package/src/application/ports/repositories/analytics-repository.ts +164 -0
- package/src/application/ports/repositories/auth-repository.ts +33 -0
- package/src/application/ports/repositories/batch-repository.ts +86 -0
- package/src/application/ports/repositories/comment-repository.ts +150 -0
- package/src/application/ports/repositories/index.ts +41 -0
- package/src/application/ports/repositories/table-repository.ts +139 -0
- package/src/application/ports/services/css-compiler.ts +55 -0
- package/src/application/ports/services/index.ts +16 -0
- package/src/application/ports/services/page-renderer.ts +79 -0
- package/src/application/ports/services/server-factory.ts +80 -0
- package/src/application/ports/services/static-site-generator.ts +82 -0
- package/src/application/use-cases/activity/programs.ts +66 -0
- package/src/application/use-cases/analytics/collect-page-view.ts +114 -0
- package/src/application/use-cases/analytics/purge-old-data.ts +40 -0
- package/src/application/use-cases/analytics/query-campaigns.ts +43 -0
- package/src/application/use-cases/analytics/query-devices.ts +36 -0
- package/src/application/use-cases/analytics/query-overview.ts +50 -0
- package/src/application/use-cases/analytics/query-pages.ts +40 -0
- package/src/application/use-cases/analytics/query-referrers.ts +43 -0
- package/src/application/use-cases/analytics/ua-parser.ts +89 -0
- package/src/application/use-cases/analytics/visitor-hash.ts +77 -0
- package/src/application/use-cases/auth/bootstrap-admin.ts +270 -0
- package/src/application/use-cases/list-activity-logs.ts +123 -0
- package/src/application/use-cases/server/generate-static-helpers.ts +374 -0
- package/src/application/use-cases/server/generate-static.ts +287 -0
- package/src/application/use-cases/server/start-server.ts +118 -0
- package/src/application/use-cases/server/startup-error-handler.ts +69 -0
- package/src/application/use-cases/server/static-content-generators.ts +182 -0
- package/src/application/use-cases/server/static-language-generators.ts +181 -0
- package/src/application/use-cases/server/static-url-rewriter.ts +237 -0
- package/src/application/use-cases/server/translation-replacer.ts +164 -0
- package/src/application/use-cases/tables/activity-programs.ts +93 -0
- package/src/application/use-cases/tables/batch-operations.ts +156 -0
- package/src/application/use-cases/tables/comment-programs.ts +436 -0
- package/src/application/use-cases/tables/permissions/permissions.ts +25 -0
- package/src/application/use-cases/tables/programs.ts +435 -0
- package/src/application/use-cases/tables/table-operations.ts +412 -0
- package/src/application/use-cases/tables/user-role.ts +52 -0
- package/src/application/use-cases/tables/utils/display-formatter.ts +471 -0
- package/src/application/use-cases/tables/utils/field-read-filter.ts +189 -0
- package/src/application/use-cases/tables/utils/list-helpers.ts +122 -0
- package/src/application/use-cases/tables/utils/record-transformer.ts +319 -0
- package/src/cli.ts +370 -0
- package/src/domain/errors/create-tagged-error.ts +36 -0
- package/src/domain/errors/index.ts +78 -0
- package/src/domain/models/api/analytics.ts +179 -0
- package/src/domain/models/api/auth.ts +231 -0
- package/src/domain/models/api/common.ts +60 -0
- package/src/domain/models/api/error.ts +89 -0
- package/src/domain/models/api/health.ts +38 -0
- package/src/domain/models/api/index.ts +42 -0
- package/src/domain/models/api/request.ts +132 -0
- package/src/domain/models/api/tables.ts +444 -0
- package/src/domain/models/app/analytics/index.ts +129 -0
- package/src/domain/models/app/auth/config.ts +116 -0
- package/src/domain/models/app/auth/index.ts +230 -0
- package/src/domain/models/app/auth/methods/email-and-password.ts +67 -0
- package/src/domain/models/app/auth/methods/index.ts +11 -0
- package/src/domain/models/app/auth/methods/magic-link.ts +54 -0
- package/src/domain/models/app/auth/oauth/index.ts +8 -0
- package/src/domain/models/app/auth/oauth/providers.ts +105 -0
- package/src/domain/models/app/auth/plugins/admin.ts +130 -0
- package/src/domain/models/app/auth/plugins/index.ts +74 -0
- package/src/domain/models/app/auth/plugins/two-factor.ts +63 -0
- package/src/domain/models/app/auth/roles.ts +179 -0
- package/src/domain/models/app/auth/strategies.ts +191 -0
- package/src/domain/models/app/auth/validation.ts +127 -0
- package/src/domain/models/app/common/branded-ids.ts +200 -0
- package/src/domain/models/app/common/definitions.ts +187 -0
- package/src/domain/models/app/component/common/component-children.ts +119 -0
- package/src/domain/models/app/component/common/component-props.ts +89 -0
- package/src/domain/models/app/component/common/component-reference.ts +170 -0
- package/src/domain/models/app/component/component.ts +81 -0
- package/src/domain/models/app/components.ts +65 -0
- package/src/domain/models/app/description.ts +83 -0
- package/src/domain/models/app/index.ts +258 -0
- package/src/domain/models/app/language/language-config.ts +200 -0
- package/src/domain/models/app/languages.ts +205 -0
- package/src/domain/models/app/name.ts +66 -0
- package/src/domain/models/app/page/common/interactions/click-interaction.ts +116 -0
- package/src/domain/models/app/page/common/interactions/entrance-animation.ts +84 -0
- package/src/domain/models/app/page/common/interactions/hover-interaction.ts +144 -0
- package/src/domain/models/app/page/common/interactions/interactions.ts +64 -0
- package/src/domain/models/app/page/common/interactions/scroll-interaction.ts +93 -0
- package/src/domain/models/app/page/common/responsive.ts +114 -0
- package/src/domain/models/app/page/common/url.ts +35 -0
- package/src/domain/models/app/page/common/variable-reference.ts +53 -0
- package/src/domain/models/app/page/id.ts +44 -0
- package/src/domain/models/app/page/index.ts +270 -0
- package/src/domain/models/app/page/meta/analytics.ts +248 -0
- package/src/domain/models/app/page/meta/custom-elements.ts +180 -0
- package/src/domain/models/app/page/meta/dns-prefetch.ts +77 -0
- package/src/domain/models/app/page/meta/favicon-set.ts +203 -0
- package/src/domain/models/app/page/meta/favicon.ts +50 -0
- package/src/domain/models/app/page/meta/favicons-config.ts +73 -0
- package/src/domain/models/app/page/meta/index.ts +278 -0
- package/src/domain/models/app/page/meta/open-graph.ts +166 -0
- package/src/domain/models/app/page/meta/preload.ts +190 -0
- package/src/domain/models/app/page/meta/structured-data/article.ts +211 -0
- package/src/domain/models/app/page/meta/structured-data/breadcrumb.ts +115 -0
- package/src/domain/models/app/page/meta/structured-data/common-fields.ts +201 -0
- package/src/domain/models/app/page/meta/structured-data/education-event.ts +256 -0
- package/src/domain/models/app/page/meta/structured-data/faq-page.ts +127 -0
- package/src/domain/models/app/page/meta/structured-data/index.ts +95 -0
- package/src/domain/models/app/page/meta/structured-data/local-business.ts +247 -0
- package/src/domain/models/app/page/meta/structured-data/organization.ts +171 -0
- package/src/domain/models/app/page/meta/structured-data/person.ts +138 -0
- package/src/domain/models/app/page/meta/structured-data/postal-address.ts +106 -0
- package/src/domain/models/app/page/meta/structured-data/product.ts +214 -0
- package/src/domain/models/app/page/meta/twitter-card.ts +217 -0
- package/src/domain/models/app/page/name.ts +38 -0
- package/src/domain/models/app/page/path.ts +21 -0
- package/src/domain/models/app/page/scripts/external-scripts.ts +163 -0
- package/src/domain/models/app/page/scripts/features.ts +135 -0
- package/src/domain/models/app/page/scripts/inline-scripts.ts +114 -0
- package/src/domain/models/app/page/scripts/scripts.ts +102 -0
- package/src/domain/models/app/page/sections.ts +298 -0
- package/src/domain/models/app/pages.ts +61 -0
- package/src/domain/models/app/permissions/index.ts +61 -0
- package/src/domain/models/app/permissions/resource-action.ts +114 -0
- package/src/domain/models/app/permissions/roles.ts +120 -0
- package/src/domain/models/app/table/check-constraints.ts +105 -0
- package/src/domain/models/app/table/cycle-detection.ts +124 -0
- package/src/domain/models/app/table/database-identifier.ts +153 -0
- package/src/domain/models/app/table/field-name.ts +36 -0
- package/src/domain/models/app/table/field-types/advanced/array-field.ts +33 -0
- package/src/domain/models/app/table/field-types/advanced/autonumber-field.ts +54 -0
- package/src/domain/models/app/table/field-types/advanced/button-field.ts +56 -0
- package/src/domain/models/app/table/field-types/advanced/color-field.ts +57 -0
- package/src/domain/models/app/table/field-types/advanced/count-field.ts +54 -0
- package/src/domain/models/app/table/field-types/advanced/formula-field.ts +58 -0
- package/src/domain/models/app/table/field-types/advanced/geolocation-field.ts +49 -0
- package/src/domain/models/app/table/field-types/advanced/index.ts +16 -0
- package/src/domain/models/app/table/field-types/advanced/json-field.ts +25 -0
- package/src/domain/models/app/table/field-types/advanced/unknown-field.ts +85 -0
- package/src/domain/models/app/table/field-types/base-field.ts +42 -0
- package/src/domain/models/app/table/field-types/date-time/created-at-field.ts +49 -0
- package/src/domain/models/app/table/field-types/date-time/date-field.ts +95 -0
- package/src/domain/models/app/table/field-types/date-time/deleted-at-field.ts +56 -0
- package/src/domain/models/app/table/field-types/date-time/duration-field.ts +73 -0
- package/src/domain/models/app/table/field-types/date-time/index.ts +12 -0
- package/src/domain/models/app/table/field-types/date-time/updated-at-field.ts +50 -0
- package/src/domain/models/app/table/field-types/index.ts +19 -0
- package/src/domain/models/app/table/field-types/media/barcode-field.ts +58 -0
- package/src/domain/models/app/table/field-types/media/index.ts +10 -0
- package/src/domain/models/app/table/field-types/media/multiple-attachments-field.ts +80 -0
- package/src/domain/models/app/table/field-types/media/single-attachment-field.ts +81 -0
- package/src/domain/models/app/table/field-types/numeric/currency-field.ts +144 -0
- package/src/domain/models/app/table/field-types/numeric/decimal-field.ts +113 -0
- package/src/domain/models/app/table/field-types/numeric/index.ts +13 -0
- package/src/domain/models/app/table/field-types/numeric/integer-field.ts +98 -0
- package/src/domain/models/app/table/field-types/numeric/percentage-field.ts +115 -0
- package/src/domain/models/app/table/field-types/numeric/progress-field.ts +71 -0
- package/src/domain/models/app/table/field-types/numeric/rating-field.ts +74 -0
- package/src/domain/models/app/table/field-types/relational/index.ts +10 -0
- package/src/domain/models/app/table/field-types/relational/lookup-field.ts +46 -0
- package/src/domain/models/app/table/field-types/relational/relationship-field.ts +112 -0
- package/src/domain/models/app/table/field-types/relational/rollup-field.ts +58 -0
- package/src/domain/models/app/table/field-types/selection/checkbox-field.ts +51 -0
- package/src/domain/models/app/table/field-types/selection/index.ts +11 -0
- package/src/domain/models/app/table/field-types/selection/multi-select-field.ts +68 -0
- package/src/domain/models/app/table/field-types/selection/single-select-field.ts +54 -0
- package/src/domain/models/app/table/field-types/selection/status-field.ts +37 -0
- package/src/domain/models/app/table/field-types/text/email-field.ts +80 -0
- package/src/domain/models/app/table/field-types/text/index.ts +13 -0
- package/src/domain/models/app/table/field-types/text/long-text-field.ts +77 -0
- package/src/domain/models/app/table/field-types/text/phone-number-field.ts +82 -0
- package/src/domain/models/app/table/field-types/text/rich-text-field.ts +66 -0
- package/src/domain/models/app/table/field-types/text/single-line-text-field.ts +79 -0
- package/src/domain/models/app/table/field-types/text/url-field.ts +81 -0
- package/src/domain/models/app/table/field-types/user/created-by-field.ts +50 -0
- package/src/domain/models/app/table/field-types/user/deleted-by-field.ts +57 -0
- package/src/domain/models/app/table/field-types/user/index.ts +11 -0
- package/src/domain/models/app/table/field-types/user/updated-by-field.ts +51 -0
- package/src/domain/models/app/table/field-types/user/user-field.ts +52 -0
- package/src/domain/models/app/table/field-types/validation-utils.ts +166 -0
- package/src/domain/models/app/table/fields.ts +216 -0
- package/src/domain/models/app/table/foreign-keys.ts +111 -0
- package/src/domain/models/app/table/formula-keywords.ts +326 -0
- package/src/domain/models/app/table/id.ts +31 -0
- package/src/domain/models/app/table/index.ts +290 -0
- package/src/domain/models/app/table/indexes.ts +80 -0
- package/src/domain/models/app/table/name.ts +37 -0
- package/src/domain/models/app/table/permissions/field-permission.ts +83 -0
- package/src/domain/models/app/table/permissions/index.ts +167 -0
- package/src/domain/models/app/table/permissions/permission-evaluator.ts +372 -0
- package/src/domain/models/app/table/permissions/permission.ts +49 -0
- package/src/domain/models/app/table/primary-key.ts +62 -0
- package/src/domain/models/app/table/table-formula-validation.ts +168 -0
- package/src/domain/models/app/table/table-indexes-validation.ts +38 -0
- package/src/domain/models/app/table/table-permissions-validation.ts +77 -0
- package/src/domain/models/app/table/table-primary-key-validation.ts +49 -0
- package/src/domain/models/app/table/table-views-validation.ts +408 -0
- package/src/domain/models/app/table/unique-constraints.ts +79 -0
- package/src/domain/models/app/table/views/fields.ts +28 -0
- package/src/domain/models/app/table/views/filters.ts +162 -0
- package/src/domain/models/app/table/views/group-by.ts +32 -0
- package/src/domain/models/app/table/views/id.ts +50 -0
- package/src/domain/models/app/table/views/index.ts +177 -0
- package/src/domain/models/app/table/views/name.ts +32 -0
- package/src/domain/models/app/table/views/permissions.ts +98 -0
- package/src/domain/models/app/table/views/sorts.ts +31 -0
- package/src/domain/models/app/tables.ts +695 -0
- package/src/domain/models/app/theme/animations.ts +208 -0
- package/src/domain/models/app/theme/border-radius.ts +58 -0
- package/src/domain/models/app/theme/breakpoints.ts +62 -0
- package/src/domain/models/app/theme/colors.ts +110 -0
- package/src/domain/models/app/theme/fonts.ts +164 -0
- package/src/domain/models/app/theme/shadows.ts +61 -0
- package/src/domain/models/app/theme/spacing.ts +115 -0
- package/src/domain/models/app/theme.ts +66 -0
- package/src/domain/models/app/version.ts +87 -0
- package/src/domain/models/record-comment.ts +91 -0
- package/src/domain/utils/content-parsing.ts +49 -0
- package/src/domain/utils/format-detection.ts +69 -0
- package/src/domain/utils/index.ts +9 -0
- package/src/domain/utils/route-matcher.ts +184 -0
- package/src/domain/utils/translation-resolver.ts +170 -0
- package/src/index.ts +208 -0
- package/src/infrastructure/analytics/tracking-script.ts +48 -0
- package/src/infrastructure/auth/better-auth/auth.ts +216 -0
- package/src/infrastructure/auth/better-auth/email-handlers.ts +162 -0
- package/src/infrastructure/auth/better-auth/index.ts +16 -0
- package/src/infrastructure/auth/better-auth/layer.ts +97 -0
- package/src/infrastructure/auth/better-auth/plugins/admin.ts +56 -0
- package/src/infrastructure/auth/better-auth/plugins/magic-link.ts +31 -0
- package/src/infrastructure/auth/better-auth/plugins/two-factor.ts +19 -0
- package/src/infrastructure/auth/better-auth/schema.ts +152 -0
- package/src/infrastructure/auth/index.ts +27 -0
- package/src/infrastructure/css/cache/css-cache-service.ts +130 -0
- package/src/infrastructure/css/compiler.ts +210 -0
- package/src/infrastructure/css/css-compiler-live.ts +20 -0
- package/src/infrastructure/css/index.ts +25 -0
- package/src/infrastructure/css/styles/animation-styles-generator.ts +177 -0
- package/src/infrastructure/css/styles/click-animations.ts +147 -0
- package/src/infrastructure/css/styles/component-layer-generators.ts +147 -0
- package/src/infrastructure/css/theme/theme-generators.ts +130 -0
- package/src/infrastructure/css/theme/theme-layer-generators.ts +219 -0
- package/src/infrastructure/css/theme/theme-token-resolver.ts +76 -0
- package/src/infrastructure/database/activity-queries.ts +111 -0
- package/src/infrastructure/database/auth/auth-validation.ts +101 -0
- package/src/infrastructure/database/drizzle/db-bun.ts +17 -0
- package/src/infrastructure/database/drizzle/db.ts +17 -0
- package/src/infrastructure/database/drizzle/index.ts +16 -0
- package/src/infrastructure/database/drizzle/layer.ts +34 -0
- package/src/infrastructure/database/drizzle/migrate.ts +77 -0
- package/src/infrastructure/database/drizzle/schema/activity-log.ts +111 -0
- package/src/infrastructure/database/drizzle/schema/analytics-page-views.ts +116 -0
- package/src/infrastructure/database/drizzle/schema/migration-audit.ts +68 -0
- package/src/infrastructure/database/drizzle/schema/record-comments.ts +79 -0
- package/src/infrastructure/database/drizzle/schema.ts +12 -0
- package/src/infrastructure/database/field-utils.ts +87 -0
- package/src/infrastructure/database/filter-operators.ts +136 -0
- package/src/infrastructure/database/formula/formula-trigger-generators.ts +114 -0
- package/src/infrastructure/database/formula/formula-utils.ts +440 -0
- package/src/infrastructure/database/generators/index-generators.ts +152 -0
- package/src/infrastructure/database/generators/trigger-generators.ts +154 -0
- package/src/infrastructure/database/index.ts +35 -0
- package/src/infrastructure/database/lookup/lookup-expression-generators.ts +356 -0
- package/src/infrastructure/database/lookup/lookup-expressions.ts +116 -0
- package/src/infrastructure/database/lookup/lookup-view-generators.ts +403 -0
- package/src/infrastructure/database/lookup/lookup-view-helpers.ts +65 -0
- package/src/infrastructure/database/lookup/lookup-view-triggers.ts +121 -0
- package/src/infrastructure/database/migration-audit-trail.ts +375 -0
- package/src/infrastructure/database/repositories/activity-log-repository-live.ts +99 -0
- package/src/infrastructure/database/repositories/activity-repository-live.ts +21 -0
- package/src/infrastructure/database/repositories/analytics-repository-live.ts +316 -0
- package/src/infrastructure/database/repositories/auth-repository-live.ts +42 -0
- package/src/infrastructure/database/repositories/batch-repository-live.ts +29 -0
- package/src/infrastructure/database/repositories/comment-repository-live.ts +39 -0
- package/src/infrastructure/database/repositories/table-repository-live.ts +38 -0
- package/src/infrastructure/database/schema/schema-dependency-sorting.ts +142 -0
- package/src/infrastructure/database/schema/schema-initializer.ts +598 -0
- package/src/infrastructure/database/schema-migration/column-detection.ts +286 -0
- package/src/infrastructure/database/schema-migration/constants.ts +31 -0
- package/src/infrastructure/database/schema-migration/constraint-sync.ts +288 -0
- package/src/infrastructure/database/schema-migration/index-sync.ts +108 -0
- package/src/infrastructure/database/schema-migration/index.ts +66 -0
- package/src/infrastructure/database/schema-migration/migration-statements.ts +106 -0
- package/src/infrastructure/database/schema-migration/rename-detection.ts +87 -0
- package/src/infrastructure/database/schema-migration/table-operations.ts +65 -0
- package/src/infrastructure/database/schema-migration/type-utils.ts +98 -0
- package/src/infrastructure/database/schema-migration/types.ts +14 -0
- package/src/infrastructure/database/schema-migration-helpers.ts +53 -0
- package/src/infrastructure/database/session-context.ts +20 -0
- package/src/infrastructure/database/sql/sql-check-constraints.ts +252 -0
- package/src/infrastructure/database/sql/sql-column-generators.ts +174 -0
- package/src/infrastructure/database/sql/sql-execution.ts +245 -0
- package/src/infrastructure/database/sql/sql-field-predicates.ts +81 -0
- package/src/infrastructure/database/sql/sql-generators.ts +91 -0
- package/src/infrastructure/database/sql/sql-junction-tables.ts +79 -0
- package/src/infrastructure/database/sql/sql-key-constraints.ts +210 -0
- package/src/infrastructure/database/sql/sql-type-mappings.ts +106 -0
- package/src/infrastructure/database/sql/sql-utils.ts +53 -0
- package/src/infrastructure/database/table-live-layers.ts +30 -0
- package/src/infrastructure/database/table-operations/column-generators.ts +82 -0
- package/src/infrastructure/database/table-operations/create-table-sql.ts +81 -0
- package/src/infrastructure/database/table-operations/index.ts +55 -0
- package/src/infrastructure/database/table-operations/migration-utils.ts +157 -0
- package/src/infrastructure/database/table-operations/table-effects.ts +234 -0
- package/src/infrastructure/database/table-operations/table-features.ts +96 -0
- package/src/infrastructure/database/table-operations/type-compatibility.ts +58 -0
- package/src/infrastructure/database/table-operations.ts +47 -0
- package/src/infrastructure/database/table-queries/batch/batch-create.ts +80 -0
- package/src/infrastructure/database/table-queries/batch/batch-delete.ts +212 -0
- package/src/infrastructure/database/table-queries/batch/batch-helpers.ts +124 -0
- package/src/infrastructure/database/table-queries/batch/batch-restore.ts +161 -0
- package/src/infrastructure/database/table-queries/batch/batch-update.ts +146 -0
- package/src/infrastructure/database/table-queries/batch/batch-upsert.ts +357 -0
- package/src/infrastructure/database/table-queries/batch/batch.ts +14 -0
- package/src/infrastructure/database/table-queries/crud/crud-read.ts +351 -0
- package/src/infrastructure/database/table-queries/crud/crud-write.ts +399 -0
- package/src/infrastructure/database/table-queries/crud/crud.ts +16 -0
- package/src/infrastructure/database/table-queries/index.ts +11 -0
- package/src/infrastructure/database/table-queries/mutation-helpers/authorship-helpers.ts +152 -0
- package/src/infrastructure/database/table-queries/mutation-helpers/create-record-helpers.ts +90 -0
- package/src/infrastructure/database/table-queries/mutation-helpers/delete-helpers.ts +163 -0
- package/src/infrastructure/database/table-queries/mutation-helpers/record-fetch-helpers.ts +79 -0
- package/src/infrastructure/database/table-queries/mutation-helpers/update-helpers.ts +74 -0
- package/src/infrastructure/database/table-queries/query-helpers/activity-log-helpers.ts +53 -0
- package/src/infrastructure/database/table-queries/query-helpers/activity-queries.ts +106 -0
- package/src/infrastructure/database/table-queries/query-helpers/aggregation-helpers.ts +314 -0
- package/src/infrastructure/database/table-queries/query-helpers/comment-queries.ts +414 -0
- package/src/infrastructure/database/table-queries/query-helpers/record-validation-queries.ts +126 -0
- package/src/infrastructure/database/table-queries/query-helpers/trash-helpers.ts +58 -0
- package/src/infrastructure/database/table-queries/shared/error-handling.ts +47 -0
- package/src/infrastructure/database/table-queries/shared/typed-execute.ts +27 -0
- package/src/infrastructure/database/table-queries/shared/user-join-helpers.ts +38 -0
- package/src/infrastructure/database/table-queries/shared/validation.ts +39 -0
- package/src/infrastructure/database/views/view-generators.ts +258 -0
- package/src/infrastructure/devtools/devtools-layer.ts +43 -0
- package/src/infrastructure/devtools/index.ts +8 -0
- package/src/infrastructure/email/email-config.ts +103 -0
- package/src/infrastructure/email/email-service.ts +152 -0
- package/src/infrastructure/email/index.ts +107 -0
- package/src/infrastructure/email/nodemailer.ts +125 -0
- package/src/infrastructure/email/templates.ts +244 -0
- package/src/infrastructure/errors/auth-config-required-error.ts +21 -0
- package/src/infrastructure/errors/auth-error.ts +16 -0
- package/src/infrastructure/errors/css-compilation-error.ts +14 -0
- package/src/infrastructure/errors/index.ts +26 -0
- package/src/infrastructure/errors/schema-initialization-error.ts +19 -0
- package/src/infrastructure/errors/server-creation-error.ts +14 -0
- package/src/infrastructure/filesystem/copy-directory.ts +136 -0
- package/src/infrastructure/layers/app-layer.ts +61 -0
- package/src/infrastructure/layers/page-renderer-layer.ts +41 -0
- package/src/infrastructure/logging/index.ts +8 -0
- package/src/infrastructure/logging/logger.ts +204 -0
- package/src/infrastructure/schema/file-loader.ts +53 -0
- package/src/infrastructure/schema/index.ts +15 -0
- package/src/infrastructure/schema/remote-loader.ts +48 -0
- package/src/infrastructure/server/index.ts +26 -0
- package/src/infrastructure/server/language-detection.ts +87 -0
- package/src/infrastructure/server/lifecycle.ts +67 -0
- package/src/infrastructure/server/route-setup/api-routes.ts +310 -0
- package/src/infrastructure/server/route-setup/auth-route-utils.ts +399 -0
- package/src/infrastructure/server/route-setup/auth-routes.ts +245 -0
- package/src/infrastructure/server/route-setup/openapi-routes.ts +45 -0
- package/src/infrastructure/server/route-setup/openapi-schema.ts +120 -0
- package/src/infrastructure/server/route-setup/page-routes.ts +219 -0
- package/src/infrastructure/server/route-setup/static-assets.ts +191 -0
- package/src/infrastructure/server/server-factory-live.ts +45 -0
- package/src/infrastructure/server/server.ts +275 -0
- package/src/infrastructure/server/ssg-adapter.ts +196 -0
- package/src/infrastructure/server/static-site-generator-live.ts +20 -0
- package/src/infrastructure/utils/accept-language-parser.ts +106 -0
- package/src/infrastructure/utils/glob-matcher.ts +50 -0
- package/src/presentation/api/client.ts +114 -0
- package/src/presentation/api/middleware/auth.ts +233 -0
- package/src/presentation/api/middleware/table.ts +155 -0
- package/src/presentation/api/middleware/validation.ts +88 -0
- package/src/presentation/api/routes/activity/get-activity-by-id-handler.ts +77 -0
- package/src/presentation/api/routes/activity/index.ts +28 -0
- package/src/presentation/api/routes/activity.ts +339 -0
- package/src/presentation/api/routes/analytics.ts +328 -0
- package/src/presentation/api/routes/auth.ts +169 -0
- package/src/presentation/api/routes/index.ts +11 -0
- package/src/presentation/api/routes/tables/activity-handlers.ts +57 -0
- package/src/presentation/api/routes/tables/batch-permission-helpers.ts +163 -0
- package/src/presentation/api/routes/tables/batch-routes.ts +355 -0
- package/src/presentation/api/routes/tables/comment-handlers.ts +377 -0
- package/src/presentation/api/routes/tables/create-record-helpers.ts +179 -0
- package/src/presentation/api/routes/tables/effect-runner.ts +58 -0
- package/src/presentation/api/routes/tables/error-handlers.ts +53 -0
- package/src/presentation/api/routes/tables/field-permission-validation.ts +167 -0
- package/src/presentation/api/routes/tables/filter-parser.ts +75 -0
- package/src/presentation/api/routes/tables/formula-parser.ts +118 -0
- package/src/presentation/api/routes/tables/index.ts +113 -0
- package/src/presentation/api/routes/tables/list-records-filter.ts +54 -0
- package/src/presentation/api/routes/tables/param-parsers.ts +59 -0
- package/src/presentation/api/routes/tables/record-handlers.ts +484 -0
- package/src/presentation/api/routes/tables/record-routes.ts +53 -0
- package/src/presentation/api/routes/tables/record-update-handler.ts +200 -0
- package/src/presentation/api/routes/tables/sort-validation.ts +85 -0
- package/src/presentation/api/routes/tables/table-routes.ts +76 -0
- package/src/presentation/api/routes/tables/timezone-validation.ts +41 -0
- package/src/presentation/api/routes/tables/upsert-helpers.ts +471 -0
- package/src/presentation/api/routes/tables/utils.ts +159 -0
- package/src/presentation/api/routes/tables/view-routes.ts +51 -0
- package/src/presentation/api/routes/tables.ts +9 -0
- package/src/presentation/api/utils/context-helpers.ts +43 -0
- package/src/presentation/api/utils/error-sanitizer.ts +235 -0
- package/src/presentation/api/utils/field-permission-validator.ts +53 -0
- package/src/presentation/api/utils/filter-field-validator.ts +90 -0
- package/src/presentation/api/utils/index.ts +13 -0
- package/src/presentation/api/utils/run-effect.ts +94 -0
- package/src/presentation/api/utils/validate-request.ts +89 -0
- package/src/presentation/api/validation/index.ts +29 -0
- package/src/presentation/api/validation/rules/field-rules.ts +158 -0
- package/src/presentation/api/validation/rules/record-rules.ts +73 -0
- package/src/presentation/cli/index.ts +19 -0
- package/src/presentation/cli/schema-loader.ts +172 -0
- package/src/presentation/hooks/use-breakpoint.ts +155 -0
- package/src/presentation/rendering/render-error-pages.tsx +60 -0
- package/src/presentation/rendering/render-homepage.tsx +137 -0
- package/src/presentation/scripts/script-renderers.ts +112 -0
- package/src/presentation/styling/animation-composer.ts +117 -0
- package/src/presentation/styling/index.ts +13 -0
- package/src/presentation/styling/parse-style.ts +243 -0
- package/src/presentation/styling/style-utils.ts +50 -0
- package/src/presentation/styling/theme-colors.ts +53 -0
- package/src/presentation/translations/component-utils.ts +54 -0
- package/src/presentation/translations/index.ts +16 -0
- package/src/presentation/translations/translation-resolver.ts +22 -0
- package/src/presentation/ui/languages/language-switcher.tsx +119 -0
- package/src/presentation/ui/metadata/analytics-builders.tsx +174 -0
- package/src/presentation/ui/metadata/analytics-head.tsx +39 -0
- package/src/presentation/ui/metadata/custom-elements-builders.tsx +157 -0
- package/src/presentation/ui/metadata/extract-component-meta.ts +108 -0
- package/src/presentation/ui/metadata/head-elements.tsx +164 -0
- package/src/presentation/ui/metadata/index.tsx +35 -0
- package/src/presentation/ui/metadata/meta-utils.tsx +42 -0
- package/src/presentation/ui/metadata/open-graph-meta.tsx +57 -0
- package/src/presentation/ui/metadata/structured-data-from-component.tsx +134 -0
- package/src/presentation/ui/metadata/structured-data.tsx +88 -0
- package/src/presentation/ui/metadata/twitter-card-meta.tsx +80 -0
- package/src/presentation/ui/pages/DefaultHomePage.tsx +43 -0
- package/src/presentation/ui/pages/DefaultPageConfigs.ts +220 -0
- package/src/presentation/ui/pages/DynamicPage.tsx +307 -0
- package/src/presentation/ui/pages/ErrorPage.tsx +25 -0
- package/src/presentation/ui/pages/NotFoundPage.tsx +25 -0
- package/src/presentation/ui/pages/PageBodyScripts.tsx +242 -0
- package/src/presentation/ui/pages/PageBodyStyles.ts +52 -0
- package/src/presentation/ui/pages/PageHead.tsx +380 -0
- package/src/presentation/ui/pages/PageLangResolver.ts +58 -0
- package/src/presentation/ui/pages/PageMain.tsx +58 -0
- package/src/presentation/ui/pages/PageMetadata.ts +168 -0
- package/src/presentation/ui/pages/PageMetadataI18n.ts +169 -0
- package/src/presentation/ui/pages/PageScripts.ts +78 -0
- package/src/presentation/ui/pages/SectionRenderer.tsx +67 -0
- package/src/presentation/ui/pages/SectionSpacing.tsx +131 -0
- package/src/presentation/ui/sections/component-renderer.tsx +426 -0
- package/src/presentation/ui/sections/component-renderer.types.ts +33 -0
- package/src/presentation/ui/sections/components/component-reference-handler.tsx +74 -0
- package/src/presentation/ui/sections/components/component-resolution.ts +65 -0
- package/src/presentation/ui/sections/components/index.ts +9 -0
- package/src/presentation/ui/sections/hero.tsx +394 -0
- package/src/presentation/ui/sections/props/component-builder.ts +183 -0
- package/src/presentation/ui/sections/props/element-props.ts +179 -0
- package/src/presentation/ui/sections/props/index.ts +9 -0
- package/src/presentation/ui/sections/props/prop-conversion.ts +171 -0
- package/src/presentation/ui/sections/props/props-builder-config.ts +42 -0
- package/src/presentation/ui/sections/props/props-builder.ts +296 -0
- package/src/presentation/ui/sections/renderers/element-renderers/html-element-renderer.tsx +124 -0
- package/src/presentation/ui/sections/renderers/element-renderers/index.ts +59 -0
- package/src/presentation/ui/sections/renderers/element-renderers/interactive-renderers.tsx +231 -0
- package/src/presentation/ui/sections/renderers/element-renderers/media-renderers.tsx +102 -0
- package/src/presentation/ui/sections/renderers/element-renderers/text-content-renderers.tsx +42 -0
- package/src/presentation/ui/sections/renderers/element-renderers.ts +53 -0
- package/src/presentation/ui/sections/renderers/html-element-helpers.ts +100 -0
- package/src/presentation/ui/sections/renderers/specialized-renderers.tsx +212 -0
- package/src/presentation/ui/sections/rendering/component-dispatch-config.ts +31 -0
- package/src/presentation/ui/sections/rendering/component-registry/index.ts +39 -0
- package/src/presentation/ui/sections/rendering/component-registry/interactive-components.ts +54 -0
- package/src/presentation/ui/sections/rendering/component-registry/media-components.ts +36 -0
- package/src/presentation/ui/sections/rendering/component-registry/special-components.tsx +153 -0
- package/src/presentation/ui/sections/rendering/component-registry/structural-components.ts +215 -0
- package/src/presentation/ui/sections/rendering/component-registry/text-components.ts +57 -0
- package/src/presentation/ui/sections/rendering/component-registry-helpers.tsx +29 -0
- package/src/presentation/ui/sections/rendering/component-registry.tsx +21 -0
- package/src/presentation/ui/sections/rendering/component-type-dispatcher.tsx +33 -0
- package/src/presentation/ui/sections/rendering/index.ts +9 -0
- package/src/presentation/ui/sections/responsive/responsive-children-builder.tsx +96 -0
- package/src/presentation/ui/sections/responsive/responsive-content-builder.tsx +95 -0
- package/src/presentation/ui/sections/responsive/responsive-props-merger.ts +195 -0
- package/src/presentation/ui/sections/responsive/responsive-resolver.ts +213 -0
- package/src/presentation/ui/sections/styling/animation-composer-wrapper.ts +65 -0
- package/src/presentation/ui/sections/styling/class-builders.ts +45 -0
- package/src/presentation/ui/sections/styling/color-resolver.ts +43 -0
- package/src/presentation/ui/sections/styling/hover-interaction-handler.ts +107 -0
- package/src/presentation/ui/sections/styling/index.ts +9 -0
- package/src/presentation/ui/sections/styling/interaction-props-builder.ts +55 -0
- package/src/presentation/ui/sections/styling/shadow-resolver.ts +83 -0
- package/src/presentation/ui/sections/styling/spacing-resolver.ts +104 -0
- package/src/presentation/ui/sections/styling/style-processor.ts +170 -0
- package/src/presentation/ui/sections/styling/theme-tokens.ts +184 -0
- package/src/presentation/ui/sections/translations/i18n-content-resolver.ts +198 -0
- package/src/presentation/ui/sections/translations/index.ts +9 -0
- package/src/presentation/ui/sections/translations/translation-handler.ts +143 -0
- package/src/presentation/ui/sections/translations/variable-substitution.ts +225 -0
- package/src/presentation/ui/sections/utils/time-parser.ts +82 -0
- package/src/presentation/utils/link-attributes.ts +50 -0
- package/src/presentation/utils/string-utils.ts +58 -0
- package/src/presentation/utils/styles.ts +50 -0
- 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'
|