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,158 @@
|
|
|
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 { hasPermission } from '@/domain/models/app/table/permissions'
|
|
10
|
+
import { ValidationError, PermissionError, ValidationContext } from '../../middleware/validation'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Validate that 'id' field is not in the request (readonly)
|
|
14
|
+
*/
|
|
15
|
+
export function validateReadonlyIdField(
|
|
16
|
+
fields: Record<string, unknown>
|
|
17
|
+
): Effect.Effect<void, ValidationError, never> {
|
|
18
|
+
if ('id' in fields) {
|
|
19
|
+
return Effect.fail(new ValidationError("Cannot write to readonly field 'id'", 'id'))
|
|
20
|
+
}
|
|
21
|
+
return Effect.void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Validate that fields with default values are not in the request (system-managed)
|
|
26
|
+
*/
|
|
27
|
+
export function validateDefaultFields(
|
|
28
|
+
fields: Record<string, unknown>
|
|
29
|
+
): Effect.Effect<void, ValidationError, ValidationContext> {
|
|
30
|
+
return Effect.gen(function* () {
|
|
31
|
+
const ctx = yield* ValidationContext
|
|
32
|
+
const table = ctx.app.tables?.find((t) => t.name === ctx.tableName)
|
|
33
|
+
|
|
34
|
+
const fieldsWithDefaults =
|
|
35
|
+
table?.fields?.filter((f) => 'default' in f && f.default !== undefined) ?? []
|
|
36
|
+
|
|
37
|
+
const attemptedDefaultField = fieldsWithDefaults.find((f) => f.name in fields)
|
|
38
|
+
|
|
39
|
+
if (attemptedDefaultField) {
|
|
40
|
+
return yield* Effect.fail(
|
|
41
|
+
new ValidationError(
|
|
42
|
+
`Cannot write to readonly field '${attemptedDefaultField.name}'`,
|
|
43
|
+
attemptedDefaultField.name
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Validate required fields are present
|
|
52
|
+
*/
|
|
53
|
+
export function validateRequiredFields(
|
|
54
|
+
fields: Record<string, unknown>
|
|
55
|
+
): Effect.Effect<void, ValidationError, ValidationContext> {
|
|
56
|
+
return Effect.gen(function* () {
|
|
57
|
+
const ctx = yield* ValidationContext
|
|
58
|
+
const table = ctx.app.tables?.find((t) => t.name === ctx.tableName)
|
|
59
|
+
|
|
60
|
+
if (!table) return
|
|
61
|
+
|
|
62
|
+
// Get primary key field names to exclude from validation
|
|
63
|
+
const primaryKeyFields = new Set(
|
|
64
|
+
table.primaryKey?.fields ?? (table.primaryKey?.field ? [table.primaryKey.field] : [])
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
// Auto-injected fields that should be excluded from required field validation
|
|
68
|
+
const autoInjectedFields = new Set<string>([])
|
|
69
|
+
|
|
70
|
+
const missingRequiredFields = table.fields
|
|
71
|
+
.filter(
|
|
72
|
+
(field) =>
|
|
73
|
+
field.required &&
|
|
74
|
+
!(field.name in fields) &&
|
|
75
|
+
!primaryKeyFields.has(field.name) &&
|
|
76
|
+
!autoInjectedFields.has(field.name)
|
|
77
|
+
)
|
|
78
|
+
.map((field) => field.name)
|
|
79
|
+
|
|
80
|
+
if (missingRequiredFields.length > 0) {
|
|
81
|
+
return yield* Effect.fail(
|
|
82
|
+
new ValidationError(
|
|
83
|
+
'Missing required fields',
|
|
84
|
+
missingRequiredFields[0] // Report first missing field
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Check if a field permission restricts writing based on user role
|
|
93
|
+
*/
|
|
94
|
+
function hasWriteRoleRestriction(
|
|
95
|
+
fieldPermission: { write?: 'all' | 'authenticated' | readonly string[] } | null | undefined,
|
|
96
|
+
userRole: string
|
|
97
|
+
): boolean {
|
|
98
|
+
const writePermission = fieldPermission?.write
|
|
99
|
+
if (writePermission === undefined) return false
|
|
100
|
+
return !hasPermission(writePermission, userRole)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Filter fields based on write permissions
|
|
105
|
+
* Returns only fields the user is allowed to write
|
|
106
|
+
*/
|
|
107
|
+
export function filterAllowedFields(
|
|
108
|
+
fields: Record<string, unknown>
|
|
109
|
+
): Effect.Effect<
|
|
110
|
+
{ allowedData: Record<string, unknown>; forbiddenFields: readonly string[] },
|
|
111
|
+
never,
|
|
112
|
+
ValidationContext
|
|
113
|
+
> {
|
|
114
|
+
return Effect.gen(function* () {
|
|
115
|
+
const ctx = yield* ValidationContext
|
|
116
|
+
const table = ctx.app.tables?.find((t) => t.name === ctx.tableName)
|
|
117
|
+
|
|
118
|
+
// System-protected fields that cannot be modified
|
|
119
|
+
const SYSTEM_PROTECTED_FIELDS = new Set(['user_id'])
|
|
120
|
+
|
|
121
|
+
// Get forbidden fields based on field-level permissions (functional filter pattern)
|
|
122
|
+
const forbiddenFields: readonly string[] = Object.keys(fields).filter((fieldName) => {
|
|
123
|
+
const field = table?.fields?.find((f) => f.name === fieldName)
|
|
124
|
+
if (!field) return false
|
|
125
|
+
|
|
126
|
+
const fieldPermission = table?.permissions?.fields?.find((fp) => fp.field === fieldName)
|
|
127
|
+
return hasWriteRoleRestriction(fieldPermission, ctx.userRole)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
// Filter out forbidden and system-protected fields
|
|
131
|
+
const allowedData = Object.fromEntries(
|
|
132
|
+
Object.entries(fields).filter(
|
|
133
|
+
([fieldName]) =>
|
|
134
|
+
!forbiddenFields.includes(fieldName) && !SYSTEM_PROTECTED_FIELDS.has(fieldName)
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return { allowedData, forbiddenFields }
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Validate field write permissions - fails if any forbidden fields found
|
|
144
|
+
*/
|
|
145
|
+
export function validateFieldWritePermissions(
|
|
146
|
+
forbiddenFields: readonly string[]
|
|
147
|
+
): Effect.Effect<void, PermissionError, never> {
|
|
148
|
+
if (forbiddenFields.length > 0) {
|
|
149
|
+
const firstForbiddenField = forbiddenFields[0]
|
|
150
|
+
return Effect.fail(
|
|
151
|
+
new PermissionError(
|
|
152
|
+
`Cannot write to field '${firstForbiddenField}': insufficient permissions`,
|
|
153
|
+
firstForbiddenField
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
return Effect.void
|
|
158
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
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 {
|
|
10
|
+
validateReadonlyIdField,
|
|
11
|
+
validateDefaultFields,
|
|
12
|
+
validateRequiredFields,
|
|
13
|
+
filterAllowedFields,
|
|
14
|
+
validateFieldWritePermissions,
|
|
15
|
+
} from './field-rules'
|
|
16
|
+
import type {
|
|
17
|
+
ValidationError,
|
|
18
|
+
PermissionError,
|
|
19
|
+
ValidationContext,
|
|
20
|
+
} from '../../middleware/validation'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Validate fields for record creation
|
|
24
|
+
* Composes all field-level validations
|
|
25
|
+
*/
|
|
26
|
+
export function validateRecordCreation(
|
|
27
|
+
requestedFields: Record<string, unknown>
|
|
28
|
+
): Effect.Effect<Record<string, unknown>, ValidationError | PermissionError, ValidationContext> {
|
|
29
|
+
return Effect.gen(function* () {
|
|
30
|
+
// Step 1: Check readonly 'id' field
|
|
31
|
+
yield* validateReadonlyIdField(requestedFields)
|
|
32
|
+
|
|
33
|
+
// Step 2: Check fields with default values
|
|
34
|
+
yield* validateDefaultFields(requestedFields)
|
|
35
|
+
|
|
36
|
+
// Step 3: Filter fields based on write permissions
|
|
37
|
+
const { allowedData, forbiddenFields } = yield* filterAllowedFields(requestedFields)
|
|
38
|
+
|
|
39
|
+
// Step 4: Check for forbidden fields
|
|
40
|
+
yield* validateFieldWritePermissions(forbiddenFields)
|
|
41
|
+
|
|
42
|
+
// Step 5: Validate required fields (on allowed data)
|
|
43
|
+
yield* validateRequiredFields(allowedData)
|
|
44
|
+
|
|
45
|
+
return allowedData
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Validate fields for record update
|
|
51
|
+
* Similar to creation but without required field validation
|
|
52
|
+
*/
|
|
53
|
+
export function validateRecordUpdate(
|
|
54
|
+
requestedFields: Record<string, unknown>
|
|
55
|
+
): Effect.Effect<Record<string, unknown>, ValidationError | PermissionError, ValidationContext> {
|
|
56
|
+
return Effect.gen(function* () {
|
|
57
|
+
// Step 1: Check readonly 'id' field
|
|
58
|
+
yield* validateReadonlyIdField(requestedFields)
|
|
59
|
+
|
|
60
|
+
// Step 2: Check fields with default values
|
|
61
|
+
yield* validateDefaultFields(requestedFields)
|
|
62
|
+
|
|
63
|
+
// Step 3: Filter fields based on write permissions
|
|
64
|
+
const { allowedData, forbiddenFields } = yield* filterAllowedFields(requestedFields)
|
|
65
|
+
|
|
66
|
+
// Step 4: Check for forbidden fields
|
|
67
|
+
yield* validateFieldWritePermissions(forbiddenFields)
|
|
68
|
+
|
|
69
|
+
// Note: No required field validation for updates (partial updates allowed)
|
|
70
|
+
|
|
71
|
+
return allowedData
|
|
72
|
+
})
|
|
73
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
* CLI Presentation Module
|
|
10
|
+
*
|
|
11
|
+
* CLI-specific utilities with user-facing error handling.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
loadSchemaFromFile,
|
|
16
|
+
loadSchemaFromFileForReload,
|
|
17
|
+
parseSchemaFromEnv,
|
|
18
|
+
parseAppSchema,
|
|
19
|
+
} from './schema-loader'
|
|
@@ -0,0 +1,172 @@
|
|
|
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
|
+
* CLI Schema Loader - Presentation Layer
|
|
10
|
+
*
|
|
11
|
+
* CLI-specific schema loading with user-facing error messages and process.exit.
|
|
12
|
+
* Orchestrates domain and infrastructure layers.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { Effect, Console } from 'effect'
|
|
16
|
+
import {
|
|
17
|
+
detectFormat,
|
|
18
|
+
getFileExtension,
|
|
19
|
+
isInlineJson,
|
|
20
|
+
isUrl,
|
|
21
|
+
parseJsonContent,
|
|
22
|
+
parseYamlContent,
|
|
23
|
+
} from '@/domain/utils'
|
|
24
|
+
import {
|
|
25
|
+
loadSchemaFromFile as loadFromFile,
|
|
26
|
+
fileExists,
|
|
27
|
+
fetchRemoteSchema,
|
|
28
|
+
} from '@/infrastructure/schema'
|
|
29
|
+
import type { AppEncoded } from '@/domain/models/app'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Load schema from file with CLI error handling (calls process.exit on error)
|
|
33
|
+
*/
|
|
34
|
+
export const loadSchemaFromFile = async (
|
|
35
|
+
filePath: string,
|
|
36
|
+
command: string
|
|
37
|
+
): Promise<AppEncoded> => {
|
|
38
|
+
const exists = await fileExists(filePath)
|
|
39
|
+
|
|
40
|
+
if (!exists) {
|
|
41
|
+
Effect.runSync(
|
|
42
|
+
Effect.gen(function* () {
|
|
43
|
+
yield* Console.error(`Error: File not found: ${filePath}`)
|
|
44
|
+
yield* Console.error('')
|
|
45
|
+
yield* Console.error('Usage:')
|
|
46
|
+
yield* Console.error(` sovrium ${command} <config.json>`)
|
|
47
|
+
})
|
|
48
|
+
)
|
|
49
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
50
|
+
process.exit(1)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const format = detectFormat(filePath)
|
|
54
|
+
|
|
55
|
+
if (format === 'unsupported') {
|
|
56
|
+
Effect.runSync(
|
|
57
|
+
Effect.gen(function* () {
|
|
58
|
+
yield* Console.error(`Error: Unsupported file format: .${getFileExtension(filePath)}`)
|
|
59
|
+
yield* Console.error('')
|
|
60
|
+
yield* Console.error('Supported formats: .json, .yaml, .yml')
|
|
61
|
+
})
|
|
62
|
+
)
|
|
63
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
64
|
+
process.exit(1)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
return await loadFromFile(filePath)
|
|
69
|
+
} catch (error) {
|
|
70
|
+
Effect.runSync(
|
|
71
|
+
Effect.gen(function* () {
|
|
72
|
+
yield* Console.error(
|
|
73
|
+
`Error: Failed to parse ${format === 'json' ? 'JSON' : 'YAML'} file: ${filePath}`
|
|
74
|
+
)
|
|
75
|
+
yield* Console.error('')
|
|
76
|
+
yield* Console.error('Details:', error instanceof Error ? error.message : String(error))
|
|
77
|
+
})
|
|
78
|
+
)
|
|
79
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
80
|
+
process.exit(1)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Load schema from file for watch mode reloads (throws instead of process.exit)
|
|
86
|
+
*/
|
|
87
|
+
export const loadSchemaFromFileForReload = async (filePath: string): Promise<AppEncoded> =>
|
|
88
|
+
loadFromFile(filePath)
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Parse schema from environment variable value
|
|
92
|
+
* Supports: inline JSON, inline YAML, remote URL
|
|
93
|
+
*
|
|
94
|
+
* @throws Error if parsing fails
|
|
95
|
+
*/
|
|
96
|
+
export const parseSchemaFromEnv = async (envValue: string): Promise<AppEncoded> => {
|
|
97
|
+
const trimmedValue = envValue.trim()
|
|
98
|
+
|
|
99
|
+
// Detect if value is inline JSON
|
|
100
|
+
if (isInlineJson(trimmedValue)) {
|
|
101
|
+
try {
|
|
102
|
+
return parseJsonContent(trimmedValue)
|
|
103
|
+
} catch (error) {
|
|
104
|
+
// eslint-disable-next-line functional/no-throw-statements
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Invalid JSON in APP_SCHEMA: ${error instanceof Error ? error.message : String(error)}`
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Detect if value is a URL
|
|
112
|
+
if (isUrl(trimmedValue)) {
|
|
113
|
+
return fetchRemoteSchema(trimmedValue)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Otherwise, treat as YAML
|
|
117
|
+
try {
|
|
118
|
+
return parseYamlContent(trimmedValue)
|
|
119
|
+
} catch (error) {
|
|
120
|
+
// eslint-disable-next-line functional/no-throw-statements
|
|
121
|
+
throw new Error(
|
|
122
|
+
`Invalid YAML in APP_SCHEMA: ${error instanceof Error ? error.message : String(error)}`
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Show error message when no configuration is provided
|
|
129
|
+
*/
|
|
130
|
+
const showNoConfigError = (command: string): never => {
|
|
131
|
+
Effect.runSync(
|
|
132
|
+
Effect.gen(function* () {
|
|
133
|
+
yield* Console.error('Error: No configuration provided')
|
|
134
|
+
yield* Console.error('')
|
|
135
|
+
yield* Console.error('Usage:')
|
|
136
|
+
yield* Console.error(` sovrium ${command} <config.json>`)
|
|
137
|
+
yield* Console.error('')
|
|
138
|
+
yield* Console.error('Or with environment variable:')
|
|
139
|
+
yield* Console.error(` APP_SCHEMA='{"name":"My App"}' sovrium ${command}`)
|
|
140
|
+
})
|
|
141
|
+
)
|
|
142
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
143
|
+
process.exit(1)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Parse and validate app schema from file path or environment variable
|
|
148
|
+
*/
|
|
149
|
+
export const parseAppSchema = async (command: string, filePath?: string): Promise<AppEncoded> => {
|
|
150
|
+
// If a file path is provided, load from file (takes precedence over env)
|
|
151
|
+
if (filePath) {
|
|
152
|
+
return loadSchemaFromFile(filePath, command)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Try APP_SCHEMA environment variable
|
|
156
|
+
const appSchemaEnv = Bun.env.APP_SCHEMA
|
|
157
|
+
|
|
158
|
+
if (appSchemaEnv) {
|
|
159
|
+
try {
|
|
160
|
+
return await parseSchemaFromEnv(appSchemaEnv)
|
|
161
|
+
} catch (error) {
|
|
162
|
+
Effect.runSync(
|
|
163
|
+
Console.error(`Error: ${error instanceof Error ? error.message : String(error)}`)
|
|
164
|
+
)
|
|
165
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
166
|
+
process.exit(1)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// No configuration provided
|
|
171
|
+
return showNoConfigError(command)
|
|
172
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
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 { useSyncExternalStore } from 'react'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Breakpoint type matching responsive schema
|
|
12
|
+
*/
|
|
13
|
+
export type Breakpoint = 'mobile' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Tailwind default breakpoints (min-width)
|
|
17
|
+
*/
|
|
18
|
+
const BREAKPOINTS: Record<Exclude<Breakpoint, 'mobile'>, number> = {
|
|
19
|
+
sm: 640,
|
|
20
|
+
md: 768,
|
|
21
|
+
lg: 1024,
|
|
22
|
+
xl: 1280,
|
|
23
|
+
'2xl': 1536,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Gets current viewport width (works in both normal browsers and test environments)
|
|
28
|
+
*
|
|
29
|
+
* Prioritizes document.documentElement.clientWidth for Playwright compatibility
|
|
30
|
+
*
|
|
31
|
+
* @returns Viewport width in pixels
|
|
32
|
+
*/
|
|
33
|
+
function getViewportWidth(): number {
|
|
34
|
+
// Use document.documentElement.clientWidth first (most reliable in Playwright)
|
|
35
|
+
if (typeof document !== 'undefined' && document.documentElement) {
|
|
36
|
+
return document.documentElement.clientWidth
|
|
37
|
+
}
|
|
38
|
+
// Try visualViewport (reliable in some browsers/test environments)
|
|
39
|
+
if (typeof window !== 'undefined' && window.visualViewport?.width) {
|
|
40
|
+
return window.visualViewport.width
|
|
41
|
+
}
|
|
42
|
+
// Fallback to innerWidth
|
|
43
|
+
if (typeof window !== 'undefined' && window.innerWidth) {
|
|
44
|
+
return window.innerWidth
|
|
45
|
+
}
|
|
46
|
+
// Last resort fallback
|
|
47
|
+
return 0
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Determines current breakpoint from window width
|
|
52
|
+
*
|
|
53
|
+
* @param width - Window width in pixels
|
|
54
|
+
* @returns Current breakpoint name
|
|
55
|
+
*/
|
|
56
|
+
function getBreakpoint(width: number): Breakpoint {
|
|
57
|
+
if (width >= BREAKPOINTS['2xl']) return '2xl'
|
|
58
|
+
if (width >= BREAKPOINTS.xl) return 'xl'
|
|
59
|
+
if (width >= BREAKPOINTS.lg) return 'lg'
|
|
60
|
+
if (width >= BREAKPOINTS.md) return 'md'
|
|
61
|
+
if (width >= BREAKPOINTS.sm) return 'sm'
|
|
62
|
+
return 'mobile'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Subscribe to viewport breakpoint changes
|
|
67
|
+
*
|
|
68
|
+
* @param callback - Callback to invoke when breakpoint changes
|
|
69
|
+
* @returns Unsubscribe function
|
|
70
|
+
*/
|
|
71
|
+
function subscribe(callback: () => void): () => void {
|
|
72
|
+
if (typeof window === 'undefined') return () => {}
|
|
73
|
+
|
|
74
|
+
// Call callback immediately to ensure we have the latest viewport state
|
|
75
|
+
// This is important for detecting viewport changes that happened before subscription
|
|
76
|
+
callback()
|
|
77
|
+
|
|
78
|
+
// Listen to resize events
|
|
79
|
+
window.addEventListener('resize', callback)
|
|
80
|
+
|
|
81
|
+
// Create media query listeners for each breakpoint
|
|
82
|
+
const mediaQueries = [
|
|
83
|
+
window.matchMedia(`(min-width: ${BREAKPOINTS.sm}px)`),
|
|
84
|
+
window.matchMedia(`(min-width: ${BREAKPOINTS.md}px)`),
|
|
85
|
+
window.matchMedia(`(min-width: ${BREAKPOINTS.lg}px)`),
|
|
86
|
+
window.matchMedia(`(min-width: ${BREAKPOINTS.xl}px)`),
|
|
87
|
+
window.matchMedia(`(min-width: ${BREAKPOINTS['2xl']}px)`),
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
mediaQueries.forEach((mq) => {
|
|
91
|
+
// Modern API
|
|
92
|
+
if (mq.addEventListener) {
|
|
93
|
+
mq.addEventListener('change', callback)
|
|
94
|
+
} else {
|
|
95
|
+
// Legacy API fallback
|
|
96
|
+
mq.addListener(callback)
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// Listen to visualViewport resize (more reliable in some environments)
|
|
101
|
+
window.visualViewport?.addEventListener('resize', callback)
|
|
102
|
+
|
|
103
|
+
// Poll for viewport changes (for E2E tests where events might not fire)
|
|
104
|
+
// This ensures responsive behavior works in automated testing environments
|
|
105
|
+
// Using 16ms (one frame at 60fps) for fastest possible detection
|
|
106
|
+
const pollInterval = setInterval(callback, 16)
|
|
107
|
+
|
|
108
|
+
return () => {
|
|
109
|
+
window.removeEventListener('resize', callback)
|
|
110
|
+
mediaQueries.forEach((mq) => {
|
|
111
|
+
if (mq.removeEventListener) {
|
|
112
|
+
mq.removeEventListener('change', callback)
|
|
113
|
+
} else {
|
|
114
|
+
mq.removeListener(callback)
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
window.visualViewport?.removeEventListener('resize', callback)
|
|
118
|
+
clearInterval(pollInterval)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get current breakpoint snapshot
|
|
124
|
+
*
|
|
125
|
+
* @returns Current breakpoint based on viewport width
|
|
126
|
+
*/
|
|
127
|
+
function getSnapshot(): Breakpoint {
|
|
128
|
+
if (typeof window === 'undefined') return 'mobile'
|
|
129
|
+
return getBreakpoint(getViewportWidth())
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get server-side breakpoint (always mobile for SSR)
|
|
134
|
+
*
|
|
135
|
+
* @returns Mobile breakpoint
|
|
136
|
+
*/
|
|
137
|
+
function getServerSnapshot(): Breakpoint {
|
|
138
|
+
return 'mobile'
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Hook to detect current viewport breakpoint
|
|
143
|
+
*
|
|
144
|
+
* Returns the current breakpoint based on window width.
|
|
145
|
+
* Updates automatically when viewport is resized.
|
|
146
|
+
* Uses useSyncExternalStore for reliable external state synchronization.
|
|
147
|
+
*
|
|
148
|
+
* Uses matchMedia to detect breakpoint changes reliably, which works with
|
|
149
|
+
* both user-initiated resizes and programmatic viewport changes (e.g., Playwright's setViewportSize).
|
|
150
|
+
*
|
|
151
|
+
* @returns Current breakpoint name
|
|
152
|
+
*/
|
|
153
|
+
export function useBreakpoint(): Breakpoint {
|
|
154
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
|
|
155
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
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 { renderToString } from 'react-dom/server'
|
|
9
|
+
import { ErrorPage } from '@/presentation/ui/pages/ErrorPage'
|
|
10
|
+
import { NotFoundPage } from '@/presentation/ui/pages/NotFoundPage'
|
|
11
|
+
import { renderPageByPath } from './render-homepage'
|
|
12
|
+
import type { App } from '@/domain/models/app'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Renders NotFoundPage (404) to HTML string for server-side rendering
|
|
16
|
+
*
|
|
17
|
+
* If the app has a custom page configured at '/404', renders that page.
|
|
18
|
+
* Otherwise, renders the default NotFoundPage.
|
|
19
|
+
*
|
|
20
|
+
* @param app - Optional validated application data from AppSchema
|
|
21
|
+
* @param detectedLanguage - Optional detected language from Accept-Language header
|
|
22
|
+
* @returns Complete HTML document as string with DOCTYPE
|
|
23
|
+
*/
|
|
24
|
+
export function renderNotFoundPage(app?: App, detectedLanguage?: string): string {
|
|
25
|
+
// Try to render custom 404 page first if app is provided
|
|
26
|
+
if (app) {
|
|
27
|
+
const custom404 = renderPageByPath(app, '/404', detectedLanguage)
|
|
28
|
+
if (custom404) {
|
|
29
|
+
return custom404
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Fallback to default 404 page
|
|
34
|
+
const html = renderToString(<NotFoundPage />)
|
|
35
|
+
return `<!DOCTYPE html>\n${html}`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Renders ErrorPage (500) to HTML string for server-side rendering
|
|
40
|
+
*
|
|
41
|
+
* If the app has a custom page configured at '/500', renders that page.
|
|
42
|
+
* Otherwise, renders the default ErrorPage.
|
|
43
|
+
*
|
|
44
|
+
* @param app - Optional validated application data from AppSchema
|
|
45
|
+
* @param detectedLanguage - Optional detected language from Accept-Language header
|
|
46
|
+
* @returns Complete HTML document as string with DOCTYPE
|
|
47
|
+
*/
|
|
48
|
+
export function renderErrorPage(app?: App, detectedLanguage?: string): string {
|
|
49
|
+
// Try to render custom 500 page first if app is provided
|
|
50
|
+
if (app) {
|
|
51
|
+
const custom500 = renderPageByPath(app, '/500', detectedLanguage)
|
|
52
|
+
if (custom500) {
|
|
53
|
+
return custom500
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Fallback to default 500 page
|
|
58
|
+
const html = renderToString(<ErrorPage />)
|
|
59
|
+
return `<!DOCTYPE html>\n${html}`
|
|
60
|
+
}
|