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,377 @@
|
|
|
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 {
|
|
9
|
+
createCommentProgram,
|
|
10
|
+
deleteCommentProgram,
|
|
11
|
+
getCommentProgram,
|
|
12
|
+
listCommentsProgram,
|
|
13
|
+
updateCommentProgram,
|
|
14
|
+
} from '@/application/use-cases/tables/comment-programs'
|
|
15
|
+
import { hasReadPermission } from '@/application/use-cases/tables/permissions/permissions'
|
|
16
|
+
import { getTableContext } from '@/presentation/api/utils/context-helpers'
|
|
17
|
+
import { runTableProgram } from './effect-runner'
|
|
18
|
+
import { handleRouteError } from './error-handlers'
|
|
19
|
+
import { isAuthorizationError } from './utils'
|
|
20
|
+
import type { App } from '@/domain/models/app'
|
|
21
|
+
import type { Context } from 'hono'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create comment request validation
|
|
25
|
+
*/
|
|
26
|
+
interface CreateCommentBody {
|
|
27
|
+
readonly content: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Validate create comment request body
|
|
32
|
+
*/
|
|
33
|
+
function validateCreateCommentBody(body: unknown): CreateCommentBody | undefined {
|
|
34
|
+
if (typeof body !== 'object' || body === undefined) {
|
|
35
|
+
return undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { content } = body as Record<string, unknown>
|
|
39
|
+
|
|
40
|
+
if (typeof content !== 'string') {
|
|
41
|
+
return undefined
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (content.length === 0) {
|
|
45
|
+
return undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (content.length > 10_000) {
|
|
49
|
+
return undefined
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { content }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Handle comment creation errors
|
|
57
|
+
*
|
|
58
|
+
* Authorization errors return 404 (prevents resource enumeration),
|
|
59
|
+
* all other errors are sanitized via the shared route error handler.
|
|
60
|
+
*/
|
|
61
|
+
function handleCommentError(c: Context, error: unknown) {
|
|
62
|
+
if (isAuthorizationError(error)) {
|
|
63
|
+
return c.json({ success: false, message: 'Resource not found', code: 'NOT_FOUND' }, 404)
|
|
64
|
+
}
|
|
65
|
+
return handleRouteError(c, error)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Handle create comment on a record
|
|
70
|
+
*/
|
|
71
|
+
export async function handleCreateComment(c: Context, app: App) {
|
|
72
|
+
const { session, userRole } = getTableContext(c)
|
|
73
|
+
const tableId = c.req.param('tableId')
|
|
74
|
+
const recordId = c.req.param('recordId')
|
|
75
|
+
|
|
76
|
+
// Find table by ID
|
|
77
|
+
const table = app.tables?.find((t) => String(t.id) === String(tableId))
|
|
78
|
+
if (!table) {
|
|
79
|
+
return c.json({ success: false, message: 'Resource not found', code: 'NOT_FOUND' }, 404)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check read permission (must be able to read the record to comment on it)
|
|
83
|
+
if (!hasReadPermission(table, userRole, app.tables)) {
|
|
84
|
+
return c.json(
|
|
85
|
+
{
|
|
86
|
+
success: false,
|
|
87
|
+
message: 'You do not have permission to perform this action',
|
|
88
|
+
code: 'FORBIDDEN',
|
|
89
|
+
},
|
|
90
|
+
403
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Parse and validate request body
|
|
95
|
+
const body = await c.req.json().catch(() => undefined)
|
|
96
|
+
const validated = validateCreateCommentBody(body)
|
|
97
|
+
|
|
98
|
+
if (!validated) {
|
|
99
|
+
return c.json(
|
|
100
|
+
{ success: false, message: 'Invalid request body', code: 'VALIDATION_ERROR' },
|
|
101
|
+
400
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Create comment
|
|
106
|
+
const program = createCommentProgram({
|
|
107
|
+
session,
|
|
108
|
+
tableId,
|
|
109
|
+
recordId,
|
|
110
|
+
tableName: table.name,
|
|
111
|
+
content: validated.content,
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
const result = await runTableProgram(program)
|
|
115
|
+
|
|
116
|
+
if (result._tag === 'Left') {
|
|
117
|
+
return handleCommentError(c, result.left)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return c.json(result.right, 201)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Handle delete comment error
|
|
125
|
+
*/
|
|
126
|
+
function handleDeleteCommentError(c: Context, error: unknown) {
|
|
127
|
+
// Check for authorization errors
|
|
128
|
+
if (isAuthorizationError(error)) {
|
|
129
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
130
|
+
|
|
131
|
+
// Forbidden error (user is not author and not admin)
|
|
132
|
+
if (errorMessage.includes('Forbidden')) {
|
|
133
|
+
return c.json({ success: false, code: 'FORBIDDEN' }, 403)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Not found error (comment doesn't exist, already deleted, or no record access)
|
|
137
|
+
return c.json({ success: false, message: 'Resource not found', code: 'NOT_FOUND' }, 404)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// All other errors - use shared sanitization
|
|
141
|
+
return handleRouteError(c, error)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Handle delete comment
|
|
146
|
+
*/
|
|
147
|
+
export async function handleDeleteComment(c: Context, app: App) {
|
|
148
|
+
const { session } = getTableContext(c)
|
|
149
|
+
const tableId = c.req.param('tableId')
|
|
150
|
+
const commentId = c.req.param('commentId')
|
|
151
|
+
|
|
152
|
+
// Find table by ID
|
|
153
|
+
const table = app.tables?.find((t) => String(t.id) === String(tableId))
|
|
154
|
+
if (!table) {
|
|
155
|
+
return c.json({ success: false, message: 'Resource not found', code: 'NOT_FOUND' }, 404)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Delete comment
|
|
159
|
+
const program = deleteCommentProgram({
|
|
160
|
+
session,
|
|
161
|
+
commentId,
|
|
162
|
+
tableName: table.name,
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const result = await runTableProgram(program)
|
|
166
|
+
|
|
167
|
+
if (result._tag === 'Left') {
|
|
168
|
+
return handleDeleteCommentError(c, result.left)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Return 204 No Content on success
|
|
172
|
+
// eslint-disable-next-line unicorn/no-null -- Hono's c.body() requires null for 204 No Content
|
|
173
|
+
return c.body(null, 204)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Handle get comment by ID
|
|
178
|
+
*/
|
|
179
|
+
export async function handleGetComment(c: Context, app: App) {
|
|
180
|
+
const { session, userRole } = getTableContext(c)
|
|
181
|
+
const tableId = c.req.param('tableId')
|
|
182
|
+
const commentId = c.req.param('commentId')
|
|
183
|
+
|
|
184
|
+
// Find table by ID
|
|
185
|
+
const table = app.tables?.find((t) => String(t.id) === String(tableId))
|
|
186
|
+
if (!table) {
|
|
187
|
+
return c.json({ success: false, message: 'Resource not found', code: 'NOT_FOUND' }, 404)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Check read permission
|
|
191
|
+
if (!hasReadPermission(table, userRole, app.tables)) {
|
|
192
|
+
return c.json(
|
|
193
|
+
{
|
|
194
|
+
success: false,
|
|
195
|
+
message: 'You do not have permission to perform this action',
|
|
196
|
+
code: 'FORBIDDEN',
|
|
197
|
+
},
|
|
198
|
+
403
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Get comment
|
|
203
|
+
const program = getCommentProgram({
|
|
204
|
+
session,
|
|
205
|
+
commentId,
|
|
206
|
+
tableName: table.name,
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
const result = await runTableProgram(program)
|
|
210
|
+
|
|
211
|
+
if (result._tag === 'Left') {
|
|
212
|
+
return c.json({ success: false, message: 'Resource not found', code: 'NOT_FOUND' }, 404)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return c.json(result.right, 200)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Validate update comment request body
|
|
220
|
+
*/
|
|
221
|
+
function validateUpdateCommentBody(body: unknown): CreateCommentBody | undefined {
|
|
222
|
+
if (typeof body !== 'object' || body === undefined) {
|
|
223
|
+
return undefined
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const { content } = body as Record<string, unknown>
|
|
227
|
+
|
|
228
|
+
if (typeof content !== 'string') {
|
|
229
|
+
return undefined
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (content.length === 0) {
|
|
233
|
+
return undefined
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (content.length > 10_000) {
|
|
237
|
+
return undefined
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return { content }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Handle update comment error
|
|
245
|
+
*/
|
|
246
|
+
function handleUpdateCommentError(c: Context, error: unknown) {
|
|
247
|
+
// Check for authorization errors
|
|
248
|
+
if (isAuthorizationError(error)) {
|
|
249
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
250
|
+
|
|
251
|
+
// Forbidden error (user is not author)
|
|
252
|
+
if (errorMessage.includes('Forbidden')) {
|
|
253
|
+
return c.json({ success: false, code: 'FORBIDDEN' }, 403)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Not found error (comment doesn't exist, already deleted, or no record access)
|
|
257
|
+
return c.json({ success: false, message: 'Resource not found', code: 'NOT_FOUND' }, 404)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Internal server error
|
|
261
|
+
return c.json(
|
|
262
|
+
{ success: false, message: 'Failed to update comment', code: 'INTERNAL_ERROR' },
|
|
263
|
+
500
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Handle update comment
|
|
269
|
+
*/
|
|
270
|
+
export async function handleUpdateComment(c: Context, app: App) {
|
|
271
|
+
const { session } = getTableContext(c)
|
|
272
|
+
const tableId = c.req.param('tableId')
|
|
273
|
+
const commentId = c.req.param('commentId')
|
|
274
|
+
|
|
275
|
+
// Find table by ID
|
|
276
|
+
const table = app.tables?.find((t) => String(t.id) === String(tableId))
|
|
277
|
+
if (!table) {
|
|
278
|
+
return c.json({ success: false, message: 'Resource not found', code: 'NOT_FOUND' }, 404)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Parse and validate request body
|
|
282
|
+
const body = await c.req.json().catch(() => undefined)
|
|
283
|
+
const validated = validateUpdateCommentBody(body)
|
|
284
|
+
|
|
285
|
+
if (!validated) {
|
|
286
|
+
return c.json(
|
|
287
|
+
{ success: false, message: 'Invalid request body', code: 'VALIDATION_ERROR' },
|
|
288
|
+
400
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Update comment
|
|
293
|
+
const program = updateCommentProgram({
|
|
294
|
+
session,
|
|
295
|
+
commentId,
|
|
296
|
+
tableName: table.name,
|
|
297
|
+
content: validated.content,
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
const result = await runTableProgram(program)
|
|
301
|
+
|
|
302
|
+
if (result._tag === 'Left') {
|
|
303
|
+
return handleUpdateCommentError(c, result.left)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return c.json(result.right, 200)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Parse sort query parameter (e.g., "createdAt:asc" or "createdAt:desc")
|
|
311
|
+
*/
|
|
312
|
+
function parseSortOrder(sortParam: string | undefined): 'asc' | 'desc' | undefined {
|
|
313
|
+
if (!sortParam) {
|
|
314
|
+
return undefined
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const [field, order] = sortParam.split(':')
|
|
318
|
+
if (field === 'createdAt' && (order === 'asc' || order === 'desc')) {
|
|
319
|
+
return order
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return undefined
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Handle list comments for a record
|
|
327
|
+
*/
|
|
328
|
+
export async function handleListComments(c: Context, app: App) {
|
|
329
|
+
const { session, userRole } = getTableContext(c)
|
|
330
|
+
const tableId = c.req.param('tableId')
|
|
331
|
+
const recordId = c.req.param('recordId')
|
|
332
|
+
|
|
333
|
+
// Find table by ID
|
|
334
|
+
const table = app.tables?.find((t) => String(t.id) === String(tableId))
|
|
335
|
+
if (!table) {
|
|
336
|
+
return c.json({ success: false, message: 'Resource not found', code: 'NOT_FOUND' }, 404)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Check read permission
|
|
340
|
+
if (!hasReadPermission(table, userRole, app.tables)) {
|
|
341
|
+
return c.json(
|
|
342
|
+
{
|
|
343
|
+
success: false,
|
|
344
|
+
message: 'You do not have permission to perform this action',
|
|
345
|
+
code: 'FORBIDDEN',
|
|
346
|
+
},
|
|
347
|
+
403
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Parse query parameters
|
|
352
|
+
const limitParam = c.req.query('limit')
|
|
353
|
+
const offsetParam = c.req.query('offset')
|
|
354
|
+
const sortParam = c.req.query('sort')
|
|
355
|
+
|
|
356
|
+
const limit = limitParam ? Number(limitParam) : undefined
|
|
357
|
+
const offset = offsetParam ? Number(offsetParam) : undefined
|
|
358
|
+
const sortOrder = parseSortOrder(sortParam)
|
|
359
|
+
|
|
360
|
+
// List comments
|
|
361
|
+
const program = listCommentsProgram({
|
|
362
|
+
session,
|
|
363
|
+
recordId,
|
|
364
|
+
tableName: table.name,
|
|
365
|
+
limit,
|
|
366
|
+
offset,
|
|
367
|
+
sortOrder,
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
const result = await runTableProgram(program)
|
|
371
|
+
|
|
372
|
+
if (result._tag === 'Left') {
|
|
373
|
+
return c.json({ success: false, message: 'Resource not found', code: 'NOT_FOUND' }, 404)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return c.json(result.right, 200)
|
|
377
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Context } from 'hono'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Validate required fields for a single record
|
|
12
|
+
* Returns array of missing field names
|
|
13
|
+
*/
|
|
14
|
+
export function validateRequiredFieldsForRecord(
|
|
15
|
+
table:
|
|
16
|
+
| {
|
|
17
|
+
readonly name: string
|
|
18
|
+
readonly fields: ReadonlyArray<{
|
|
19
|
+
readonly name: string
|
|
20
|
+
readonly required?: boolean
|
|
21
|
+
}>
|
|
22
|
+
readonly primaryKey?: {
|
|
23
|
+
readonly type: string
|
|
24
|
+
readonly fields?: ReadonlyArray<string>
|
|
25
|
+
readonly field?: string
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
| undefined,
|
|
29
|
+
fields: Record<string, unknown>
|
|
30
|
+
): readonly string[] {
|
|
31
|
+
if (!table) return []
|
|
32
|
+
|
|
33
|
+
// Get primary key field names to exclude from validation
|
|
34
|
+
const primaryKeyFields = new Set(
|
|
35
|
+
table.primaryKey?.fields ?? (table.primaryKey?.field ? [table.primaryKey.field] : [])
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
// Auto-injected fields that should be excluded from required field validation
|
|
39
|
+
const autoInjectedFields = new Set<string>([])
|
|
40
|
+
|
|
41
|
+
return table.fields
|
|
42
|
+
.filter(
|
|
43
|
+
(field) =>
|
|
44
|
+
field.required &&
|
|
45
|
+
!(field.name in fields) &&
|
|
46
|
+
!primaryKeyFields.has(field.name) && // Skip primary key fields
|
|
47
|
+
!autoInjectedFields.has(field.name) // Skip auto-injected fields
|
|
48
|
+
)
|
|
49
|
+
.map((field) => field.name)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Validate required fields for record creation
|
|
54
|
+
* Returns error response if required fields are missing, undefined otherwise
|
|
55
|
+
*/
|
|
56
|
+
export function validateRequiredFields(
|
|
57
|
+
table:
|
|
58
|
+
| {
|
|
59
|
+
readonly name: string
|
|
60
|
+
readonly fields: ReadonlyArray<{
|
|
61
|
+
readonly name: string
|
|
62
|
+
readonly required?: boolean
|
|
63
|
+
}>
|
|
64
|
+
readonly primaryKey?: {
|
|
65
|
+
readonly type: string
|
|
66
|
+
readonly fields?: ReadonlyArray<string>
|
|
67
|
+
readonly field?: string
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
| undefined,
|
|
71
|
+
fields: Record<string, unknown>,
|
|
72
|
+
c: Context
|
|
73
|
+
) {
|
|
74
|
+
if (!table) return undefined
|
|
75
|
+
|
|
76
|
+
// Get primary key field names to exclude from validation
|
|
77
|
+
const primaryKeyFields = new Set(
|
|
78
|
+
table.primaryKey?.fields ?? (table.primaryKey?.field ? [table.primaryKey.field] : [])
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
// Auto-injected fields that should be excluded from required field validation
|
|
82
|
+
const autoInjectedFields = new Set<string>([])
|
|
83
|
+
|
|
84
|
+
const missingRequiredFields = table.fields
|
|
85
|
+
.filter(
|
|
86
|
+
(field) =>
|
|
87
|
+
field.required &&
|
|
88
|
+
!(field.name in fields) &&
|
|
89
|
+
!primaryKeyFields.has(field.name) && // Skip primary key fields
|
|
90
|
+
!autoInjectedFields.has(field.name) // Skip auto-injected fields
|
|
91
|
+
)
|
|
92
|
+
.map((field) => field.name)
|
|
93
|
+
|
|
94
|
+
if (missingRequiredFields.length > 0) {
|
|
95
|
+
return c.json(
|
|
96
|
+
{
|
|
97
|
+
success: false,
|
|
98
|
+
message: 'Missing required fields',
|
|
99
|
+
code: 'VALIDATION_ERROR',
|
|
100
|
+
details: missingRequiredFields.map((field, index) => ({
|
|
101
|
+
record: index,
|
|
102
|
+
field,
|
|
103
|
+
error: 'Required field is missing',
|
|
104
|
+
})),
|
|
105
|
+
},
|
|
106
|
+
400
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return undefined
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check if user is trying to set readonly 'id' field
|
|
115
|
+
*/
|
|
116
|
+
export function checkReadonlyIdField(requestedFields: Record<string, unknown>, c: Context) {
|
|
117
|
+
if ('id' in requestedFields) {
|
|
118
|
+
return c.json(
|
|
119
|
+
{
|
|
120
|
+
success: false,
|
|
121
|
+
message: "Cannot write to readonly field 'id'",
|
|
122
|
+
code: 'FORBIDDEN',
|
|
123
|
+
},
|
|
124
|
+
403
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
return undefined
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Check if user is trying to set fields with default values (system-managed)
|
|
132
|
+
*/
|
|
133
|
+
export function checkDefaultFields(
|
|
134
|
+
table:
|
|
135
|
+
| {
|
|
136
|
+
readonly fields?: ReadonlyArray<{
|
|
137
|
+
readonly name: string
|
|
138
|
+
readonly default?: unknown
|
|
139
|
+
}>
|
|
140
|
+
}
|
|
141
|
+
| undefined,
|
|
142
|
+
requestedFields: Record<string, unknown>,
|
|
143
|
+
c: Context
|
|
144
|
+
) {
|
|
145
|
+
const fieldsWithDefaults =
|
|
146
|
+
table?.fields?.filter((f) => 'default' in f && f.default !== undefined) ?? []
|
|
147
|
+
const attemptedDefaultField = fieldsWithDefaults.find((f) => f.name in requestedFields)
|
|
148
|
+
|
|
149
|
+
if (attemptedDefaultField) {
|
|
150
|
+
return c.json(
|
|
151
|
+
{
|
|
152
|
+
success: false,
|
|
153
|
+
message: `Cannot write to readonly field '${attemptedDefaultField.name}'`,
|
|
154
|
+
code: 'FORBIDDEN',
|
|
155
|
+
},
|
|
156
|
+
403
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
return undefined
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Check field-level write permissions and return forbidden field error if needed
|
|
164
|
+
*/
|
|
165
|
+
export function checkFieldWritePermissions(forbiddenFields: readonly string[], c: Context) {
|
|
166
|
+
if (forbiddenFields.length > 0) {
|
|
167
|
+
const firstForbiddenField = forbiddenFields[0]
|
|
168
|
+
return c.json(
|
|
169
|
+
{
|
|
170
|
+
success: false,
|
|
171
|
+
message: `Cannot write to field '${firstForbiddenField}': insufficient permissions`,
|
|
172
|
+
code: 'FORBIDDEN',
|
|
173
|
+
field: firstForbiddenField,
|
|
174
|
+
},
|
|
175
|
+
403
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
return undefined
|
|
179
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
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 { TableLive } from '@/infrastructure/database/table-live-layers'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Run an Effect program with TableLive layer
|
|
13
|
+
*
|
|
14
|
+
* This utility consolidates the common pattern of providing TableLive, converting to Either,
|
|
15
|
+
* and running as Promise. It's used by all table-related route handlers.
|
|
16
|
+
*
|
|
17
|
+
* TableLive provides: TableRepository, BatchRepository, CommentRepository, ActivityRepository
|
|
18
|
+
*
|
|
19
|
+
* @param program - The Effect program to run (may require repository services from TableLive)
|
|
20
|
+
* @returns Promise resolving to Either (Left for errors, Right for success)
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const result = await runTableProgram(createCommentProgram({ session, tableId, content }))
|
|
24
|
+
* if (result._tag === 'Left') {
|
|
25
|
+
* return handleError(c, result.left)
|
|
26
|
+
* }
|
|
27
|
+
* return c.json(result.right, 201)
|
|
28
|
+
*/
|
|
29
|
+
export async function runTableProgram<A, E, R>(
|
|
30
|
+
program: Effect.Effect<A, E, R>
|
|
31
|
+
): Promise<
|
|
32
|
+
{ readonly _tag: 'Left'; readonly left: E } | { readonly _tag: 'Right'; readonly right: A }
|
|
33
|
+
> {
|
|
34
|
+
// Type assertion: TableLive provides all required repositories, so remaining requirements are never
|
|
35
|
+
const provided = Effect.provide(program, TableLive) as Effect.Effect<A, E, never>
|
|
36
|
+
return Effect.runPromise(Effect.either(provided))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Provide TableLive layer to an Effect program
|
|
41
|
+
*
|
|
42
|
+
* This is a simpler utility for cases where the program will be passed to runEffect
|
|
43
|
+
* (which handles the Either conversion and error handling itself).
|
|
44
|
+
*
|
|
45
|
+
* TableLive provides: TableRepository, BatchRepository, CommentRepository, ActivityRepository
|
|
46
|
+
*
|
|
47
|
+
* @param program - The Effect program to provide TableLive to (may require repository services from TableLive)
|
|
48
|
+
* @returns Effect program with TableLive provided (requirements resolved)
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* return runEffect(c, provideTableLive(batchCreateProgram({ ... })), responseSchema, 201)
|
|
52
|
+
*/
|
|
53
|
+
export function provideTableLive<A, E, R>(
|
|
54
|
+
program: Effect.Effect<A, E, R>
|
|
55
|
+
): Effect.Effect<A, E, never> {
|
|
56
|
+
// Type assertion: TableLive provides all required repositories, so remaining requirements are never
|
|
57
|
+
return Effect.provide(program, TableLive) as Effect.Effect<A, E, never>
|
|
58
|
+
}
|
|
@@ -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 { sanitizeError, getStatusCode } from '@/presentation/api/utils/error-sanitizer'
|
|
9
|
+
import type { Context } from 'hono'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Shared route error handler
|
|
13
|
+
*
|
|
14
|
+
* Uses centralized error sanitization to prevent information disclosure.
|
|
15
|
+
* Automatically detects not-found/authorization errors and returns appropriate status codes.
|
|
16
|
+
*
|
|
17
|
+
* @param c - Hono context
|
|
18
|
+
* @param error - The error to handle
|
|
19
|
+
* @returns JSON response with sanitized error details
|
|
20
|
+
*/
|
|
21
|
+
export function handleRouteError(c: Context, error: unknown): Response {
|
|
22
|
+
const requestId = crypto.randomUUID()
|
|
23
|
+
const sanitized = sanitizeError(error, requestId)
|
|
24
|
+
const statusCode = getStatusCode(sanitized.code)
|
|
25
|
+
|
|
26
|
+
return c.json(
|
|
27
|
+
{
|
|
28
|
+
success: false,
|
|
29
|
+
message: sanitized.message,
|
|
30
|
+
code: sanitized.code,
|
|
31
|
+
},
|
|
32
|
+
statusCode
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Handle errors from record restore operations
|
|
38
|
+
*
|
|
39
|
+
* Adds restore-specific "Record is not deleted" check, then delegates
|
|
40
|
+
* to the shared route error handler.
|
|
41
|
+
*/
|
|
42
|
+
export function handleRestoreRecordError(c: Context, error: unknown): Response {
|
|
43
|
+
// Check for "Record is not deleted" error (safe to expose)
|
|
44
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
45
|
+
if (errorMessage === 'Record is not deleted') {
|
|
46
|
+
return c.json(
|
|
47
|
+
{ success: false, message: 'Record is not deleted', code: 'VALIDATION_ERROR' },
|
|
48
|
+
400
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return handleRouteError(c, error)
|
|
53
|
+
}
|