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,80 @@
|
|
|
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 ReactElement } from 'react'
|
|
9
|
+
import { renderMetaTags } from './meta-utils'
|
|
10
|
+
import type { Page } from '@/domain/models/app/pages'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Twitter Card field mapping configuration
|
|
14
|
+
*/
|
|
15
|
+
const twitterCardFieldMapping = [
|
|
16
|
+
{ key: 'card', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.card },
|
|
17
|
+
{ key: 'title', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.title },
|
|
18
|
+
{ key: 'description', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.description },
|
|
19
|
+
{ key: 'image', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.image },
|
|
20
|
+
{ key: 'image:alt', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.imageAlt },
|
|
21
|
+
{ key: 'site', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.site },
|
|
22
|
+
{ key: 'creator', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.creator },
|
|
23
|
+
{ key: 'player', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.player },
|
|
24
|
+
{ key: 'player:width', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.playerWidth },
|
|
25
|
+
{ key: 'player:height', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.playerHeight },
|
|
26
|
+
{
|
|
27
|
+
key: 'app:name:iphone',
|
|
28
|
+
getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.appName?.iPhone,
|
|
29
|
+
},
|
|
30
|
+
{ key: 'app:name:ipad', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.appName?.iPad },
|
|
31
|
+
{
|
|
32
|
+
key: 'app:name:googleplay',
|
|
33
|
+
getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.appName?.googlePlay,
|
|
34
|
+
},
|
|
35
|
+
{ key: 'app:id:iphone', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.appId?.iPhone },
|
|
36
|
+
{ key: 'app:id:ipad', getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.appId?.iPad },
|
|
37
|
+
{
|
|
38
|
+
key: 'app:id:googleplay',
|
|
39
|
+
getter: (tc: NonNullable<Page['meta']>['twitter']) => tc?.appId?.googlePlay,
|
|
40
|
+
},
|
|
41
|
+
] as const
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Build Twitter Card field array from twitter card configuration
|
|
45
|
+
* Extracts all fields including app metadata for Twitter/X sharing
|
|
46
|
+
*
|
|
47
|
+
* @param twitterCard - Twitter card configuration
|
|
48
|
+
* @returns Array of field key-value pairs
|
|
49
|
+
*/
|
|
50
|
+
function buildTwitterCardFields(
|
|
51
|
+
twitterCard: NonNullable<Page['meta']>['twitter']
|
|
52
|
+
): ReadonlyArray<{ readonly key: string; readonly value?: string | number }> {
|
|
53
|
+
return twitterCardFieldMapping.map(({ key, getter }) => ({
|
|
54
|
+
key,
|
|
55
|
+
value: getter(twitterCard),
|
|
56
|
+
}))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Render Twitter Card metadata tags
|
|
61
|
+
* Generates <meta name="twitter:*"> tags for Twitter/X sharing
|
|
62
|
+
* Supports both 'twitter' and 'twitterCard' field names for compatibility
|
|
63
|
+
*
|
|
64
|
+
* @param page - Page configuration
|
|
65
|
+
* @returns React fragment with Twitter meta tags
|
|
66
|
+
*/
|
|
67
|
+
export function TwitterCardMeta({
|
|
68
|
+
page,
|
|
69
|
+
}: {
|
|
70
|
+
readonly page: Page
|
|
71
|
+
}): Readonly<ReactElement | undefined> {
|
|
72
|
+
// Support both 'twitter' (canonical) and 'twitterCard' (test alias)
|
|
73
|
+
const twitterCard = page.meta?.twitter ?? page.meta?.twitterCard
|
|
74
|
+
if (!twitterCard) {
|
|
75
|
+
return undefined
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const fields = buildTwitterCardFields(twitterCard)
|
|
79
|
+
return renderMetaTags({ fields, prefix: 'twitter', attributeType: 'name' })
|
|
80
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { type ReactElement } from 'react'
|
|
9
|
+
import { createDefaultHomePageConfig } from '@/presentation/ui/pages/DefaultPageConfigs'
|
|
10
|
+
import { DynamicPage } from '@/presentation/ui/pages/DynamicPage'
|
|
11
|
+
import type { App } from '@/domain/models/app'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* DefaultHomePage component - Default home page displaying application information
|
|
15
|
+
*
|
|
16
|
+
* This is the fallback home page shown when no custom page configuration is provided.
|
|
17
|
+
* Displays the app name, optional version badge, and optional description in a centered layout.
|
|
18
|
+
*
|
|
19
|
+
* Uses DynamicPage pattern with theme-generated styles for consistency across the application.
|
|
20
|
+
*
|
|
21
|
+
* @param props - Component props
|
|
22
|
+
* @param props.app - Validated application data from AppSchema
|
|
23
|
+
* @param props.builtInAnalyticsEnabled - Whether built-in analytics should be enabled
|
|
24
|
+
* @returns React element with app information
|
|
25
|
+
*/
|
|
26
|
+
export function DefaultHomePage({
|
|
27
|
+
app,
|
|
28
|
+
builtInAnalyticsEnabled,
|
|
29
|
+
builtInAnalyticsSessionTimeout,
|
|
30
|
+
}: {
|
|
31
|
+
readonly app: App
|
|
32
|
+
readonly builtInAnalyticsEnabled?: boolean
|
|
33
|
+
readonly builtInAnalyticsSessionTimeout?: number
|
|
34
|
+
}): Readonly<ReactElement> {
|
|
35
|
+
const pageConfig = createDefaultHomePageConfig(app)
|
|
36
|
+
return (
|
|
37
|
+
<DynamicPage
|
|
38
|
+
page={pageConfig}
|
|
39
|
+
builtInAnalyticsEnabled={builtInAnalyticsEnabled}
|
|
40
|
+
builtInAnalyticsSessionTimeout={builtInAnalyticsSessionTimeout}
|
|
41
|
+
/>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { App } from '@/domain/models/app'
|
|
9
|
+
import type { Component } from '@/domain/models/app/page/sections'
|
|
10
|
+
import type { Page } from '@/domain/models/app/pages'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates version badge component
|
|
14
|
+
*/
|
|
15
|
+
function createVersionBadge(version: string): Component {
|
|
16
|
+
return {
|
|
17
|
+
type: 'div',
|
|
18
|
+
props: {
|
|
19
|
+
className: 'flex justify-center',
|
|
20
|
+
},
|
|
21
|
+
children: [
|
|
22
|
+
{
|
|
23
|
+
type: 'span',
|
|
24
|
+
props: {
|
|
25
|
+
'data-testid': 'app-version-badge',
|
|
26
|
+
className:
|
|
27
|
+
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
|
|
28
|
+
},
|
|
29
|
+
children: [version],
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates app content components (version badge, heading, description)
|
|
37
|
+
*/
|
|
38
|
+
function createHomePageContent(app: App): ReadonlyArray<Component | string> {
|
|
39
|
+
return [
|
|
40
|
+
...(app.version ? [createVersionBadge(app.version)] : []),
|
|
41
|
+
{
|
|
42
|
+
type: 'h1',
|
|
43
|
+
props: {
|
|
44
|
+
'data-testid': 'app-name-heading',
|
|
45
|
+
className: 'scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl text-center',
|
|
46
|
+
},
|
|
47
|
+
children: [app.name],
|
|
48
|
+
},
|
|
49
|
+
...(app.description
|
|
50
|
+
? [
|
|
51
|
+
{
|
|
52
|
+
type: 'p',
|
|
53
|
+
props: {
|
|
54
|
+
'data-testid': 'app-description',
|
|
55
|
+
className: 'text-xl text-muted-foreground text-center',
|
|
56
|
+
},
|
|
57
|
+
children: [app.description],
|
|
58
|
+
},
|
|
59
|
+
]
|
|
60
|
+
: []),
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Creates a Page configuration for the default home page
|
|
66
|
+
*
|
|
67
|
+
* Renders:
|
|
68
|
+
* - Optional version badge
|
|
69
|
+
* - App name (H1 heading)
|
|
70
|
+
* - Optional app description
|
|
71
|
+
*
|
|
72
|
+
* Layout: Centered vertically and horizontally with max-width constraint
|
|
73
|
+
*
|
|
74
|
+
* @param app - Application data containing name, version, and description
|
|
75
|
+
* @returns Page configuration for DynamicPage rendering
|
|
76
|
+
*/
|
|
77
|
+
export function createDefaultHomePageConfig(app: App): Page {
|
|
78
|
+
return {
|
|
79
|
+
name: 'home',
|
|
80
|
+
path: '/',
|
|
81
|
+
meta: {
|
|
82
|
+
lang: 'en-US',
|
|
83
|
+
title: `${app.name} - Powered by Sovrium`,
|
|
84
|
+
description: app.description ?? `Welcome to ${app.name}`,
|
|
85
|
+
},
|
|
86
|
+
sections: [
|
|
87
|
+
{
|
|
88
|
+
type: 'div',
|
|
89
|
+
props: {
|
|
90
|
+
className: 'h-screen overflow-hidden bg-gradient-to-br from-gray-50 to-gray-100',
|
|
91
|
+
},
|
|
92
|
+
children: [
|
|
93
|
+
{
|
|
94
|
+
type: 'div',
|
|
95
|
+
props: {
|
|
96
|
+
className: 'container mx-auto px-4 sm:px-6 lg:px-8 h-full',
|
|
97
|
+
},
|
|
98
|
+
children: [
|
|
99
|
+
{
|
|
100
|
+
type: 'div',
|
|
101
|
+
props: {
|
|
102
|
+
className: 'flex h-full flex-col items-center justify-center',
|
|
103
|
+
},
|
|
104
|
+
children: [
|
|
105
|
+
{
|
|
106
|
+
type: 'div',
|
|
107
|
+
props: {
|
|
108
|
+
className: 'w-full max-w-2xl space-y-6 text-center',
|
|
109
|
+
},
|
|
110
|
+
children: createHomePageContent(app),
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Creates error page section with centered layout
|
|
124
|
+
*/
|
|
125
|
+
function createErrorSection(
|
|
126
|
+
code: string,
|
|
127
|
+
message: string,
|
|
128
|
+
codeColor: string = 'text-gray-900'
|
|
129
|
+
): ReadonlyArray<Component> {
|
|
130
|
+
return [
|
|
131
|
+
{
|
|
132
|
+
type: 'div',
|
|
133
|
+
props: {
|
|
134
|
+
className: 'flex min-h-screen items-center justify-center bg-gray-50',
|
|
135
|
+
},
|
|
136
|
+
children: [
|
|
137
|
+
{
|
|
138
|
+
type: 'div',
|
|
139
|
+
props: {
|
|
140
|
+
className: 'text-center',
|
|
141
|
+
},
|
|
142
|
+
children: [
|
|
143
|
+
{
|
|
144
|
+
type: 'h1',
|
|
145
|
+
props: {
|
|
146
|
+
className: `mb-4 text-6xl font-bold ${codeColor}`,
|
|
147
|
+
},
|
|
148
|
+
children: [code],
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: 'p',
|
|
152
|
+
props: {
|
|
153
|
+
className: 'mb-8 text-xl text-gray-600',
|
|
154
|
+
},
|
|
155
|
+
children: [message],
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
type: 'link',
|
|
159
|
+
props: {
|
|
160
|
+
href: '/',
|
|
161
|
+
className: 'font-medium text-blue-600 hover:text-blue-700',
|
|
162
|
+
},
|
|
163
|
+
children: ['Go back home'],
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Creates a Page configuration for 404 Not Found error page
|
|
174
|
+
*
|
|
175
|
+
* Renders:
|
|
176
|
+
* - Large "404" heading
|
|
177
|
+
* - "Page not found" message
|
|
178
|
+
* - Link to return home
|
|
179
|
+
*
|
|
180
|
+
* Layout: Centered vertically and horizontally
|
|
181
|
+
*
|
|
182
|
+
* @returns Page configuration for DynamicPage rendering
|
|
183
|
+
*/
|
|
184
|
+
export function createNotFoundPageConfig(): Page {
|
|
185
|
+
return {
|
|
186
|
+
name: 'not_found',
|
|
187
|
+
path: '/404',
|
|
188
|
+
meta: {
|
|
189
|
+
lang: 'en-US',
|
|
190
|
+
title: '404 - Not Found',
|
|
191
|
+
description: 'Page not found',
|
|
192
|
+
},
|
|
193
|
+
sections: createErrorSection('404', 'Page not found'),
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Creates a Page configuration for 500 Internal Server Error page
|
|
199
|
+
*
|
|
200
|
+
* Renders:
|
|
201
|
+
* - Large "500" heading (red color)
|
|
202
|
+
* - "Internal Server Error" message
|
|
203
|
+
* - Link to return home
|
|
204
|
+
*
|
|
205
|
+
* Layout: Centered vertically and horizontally
|
|
206
|
+
*
|
|
207
|
+
* @returns Page configuration for DynamicPage rendering
|
|
208
|
+
*/
|
|
209
|
+
export function createErrorPageConfig(): Page {
|
|
210
|
+
return {
|
|
211
|
+
name: 'error',
|
|
212
|
+
path: '/500',
|
|
213
|
+
meta: {
|
|
214
|
+
lang: 'en-US',
|
|
215
|
+
title: '500 - Internal Server Error',
|
|
216
|
+
description: 'Internal Server Error',
|
|
217
|
+
},
|
|
218
|
+
sections: createErrorSection('500', 'Internal Server Error', 'text-red-600'),
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
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 ReactElement } from 'react'
|
|
9
|
+
import { extractComponentMetaFromSections } from '@/presentation/ui/metadata/extract-component-meta'
|
|
10
|
+
import { PageBodyScripts } from '@/presentation/ui/pages/PageBodyScripts'
|
|
11
|
+
import { PageHead } from '@/presentation/ui/pages/PageHead'
|
|
12
|
+
import { resolvePageLanguage } from '@/presentation/ui/pages/PageLangResolver'
|
|
13
|
+
import { PageMain } from '@/presentation/ui/pages/PageMain'
|
|
14
|
+
import { extractPageMetadata } from '@/presentation/ui/pages/PageMetadata'
|
|
15
|
+
import { groupScriptsByPosition } from '@/presentation/ui/pages/PageScripts'
|
|
16
|
+
import type { Components } from '@/domain/models/app/components'
|
|
17
|
+
import type { Languages } from '@/domain/models/app/languages'
|
|
18
|
+
import type { Page } from '@/domain/models/app/pages'
|
|
19
|
+
import type { Theme } from '@/domain/models/app/theme'
|
|
20
|
+
import type { RouteParams } from '@/domain/utils/route-matcher'
|
|
21
|
+
|
|
22
|
+
type DynamicPageProps = {
|
|
23
|
+
readonly page: Page
|
|
24
|
+
readonly components?: Components
|
|
25
|
+
readonly theme?: Theme
|
|
26
|
+
readonly languages?: Languages
|
|
27
|
+
readonly detectedLanguage?: string
|
|
28
|
+
readonly routeParams?: RouteParams
|
|
29
|
+
readonly builtInAnalyticsEnabled?: boolean
|
|
30
|
+
readonly builtInAnalyticsSessionTimeout?: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type DynamicPageHeadProps = {
|
|
34
|
+
readonly mergedPage: Page
|
|
35
|
+
readonly theme?: Theme
|
|
36
|
+
readonly directionStyles: string
|
|
37
|
+
readonly title: string
|
|
38
|
+
readonly description: string
|
|
39
|
+
readonly keywords?: string
|
|
40
|
+
readonly canonical?: string
|
|
41
|
+
readonly lang: string
|
|
42
|
+
readonly languages?: Languages
|
|
43
|
+
readonly scripts: ReturnType<typeof groupScriptsByPosition>
|
|
44
|
+
readonly builtInAnalyticsEnabled?: boolean
|
|
45
|
+
readonly builtInAnalyticsSessionTimeout?: number
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Merges component metadata with page metadata
|
|
50
|
+
*
|
|
51
|
+
* @param page - Page configuration
|
|
52
|
+
* @param components - Available component templates
|
|
53
|
+
* @returns Page with merged metadata
|
|
54
|
+
*/
|
|
55
|
+
function mergeComponentMetaIntoPage(page: Page, components?: Components): Page {
|
|
56
|
+
const componentOpenGraph = extractComponentMetaFromSections(page.sections, components)
|
|
57
|
+
|
|
58
|
+
if (!componentOpenGraph || !page.meta) return page
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
...page,
|
|
62
|
+
meta: {
|
|
63
|
+
...page.meta,
|
|
64
|
+
openGraph: {
|
|
65
|
+
...page.meta.openGraph,
|
|
66
|
+
...componentOpenGraph,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Renders the <head> section of DynamicPage
|
|
74
|
+
* Extracted to satisfy max-lines-per-function ESLint rule
|
|
75
|
+
*/
|
|
76
|
+
function DynamicPageHead({
|
|
77
|
+
mergedPage,
|
|
78
|
+
theme,
|
|
79
|
+
directionStyles,
|
|
80
|
+
title,
|
|
81
|
+
description,
|
|
82
|
+
keywords,
|
|
83
|
+
canonical,
|
|
84
|
+
lang,
|
|
85
|
+
languages,
|
|
86
|
+
scripts,
|
|
87
|
+
builtInAnalyticsEnabled,
|
|
88
|
+
builtInAnalyticsSessionTimeout,
|
|
89
|
+
}: DynamicPageHeadProps): Readonly<ReactElement> {
|
|
90
|
+
const sessionTimeout = builtInAnalyticsSessionTimeout ?? 30
|
|
91
|
+
// Inline analytics script if enabled (contains /api/analytics/collect endpoint)
|
|
92
|
+
const analyticsScript = builtInAnalyticsEnabled
|
|
93
|
+
? `(function(){
|
|
94
|
+
"use strict";
|
|
95
|
+
var E="/api/analytics/collect",A="${mergedPage.name || 'app'}",D=true,sessionTimeout=${sessionTimeout};
|
|
96
|
+
if(D&&navigator.doNotTrack==="1")return;
|
|
97
|
+
var u=function(){
|
|
98
|
+
try{var s=new URLSearchParams(location.search);
|
|
99
|
+
var d={p:location.pathname,t:document.title,r:document.referrer||void 0,
|
|
100
|
+
sw:screen.width,sh:screen.height,
|
|
101
|
+
us:s.get("utm_source")||void 0,um:s.get("utm_medium")||void 0,
|
|
102
|
+
uc:s.get("utm_campaign")||void 0,ux:s.get("utm_content")||void 0,
|
|
103
|
+
ut:s.get("utm_term")||void 0};
|
|
104
|
+
var b=JSON.stringify(d);
|
|
105
|
+
if(navigator.sendBeacon){navigator.sendBeacon(E,new Blob([b],{type:"application/json"}))}
|
|
106
|
+
else{var x=new XMLHttpRequest();x.open("POST",E,true);x.setRequestHeader("Content-Type","application/json");x.send(b)}
|
|
107
|
+
}catch(e){}};
|
|
108
|
+
u();
|
|
109
|
+
var op=history.pushState;
|
|
110
|
+
history.pushState=function(){op.apply(this,arguments);u()};
|
|
111
|
+
window.addEventListener("popstate",u);
|
|
112
|
+
})();`
|
|
113
|
+
: undefined
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<head>
|
|
117
|
+
<PageHead
|
|
118
|
+
page={mergedPage}
|
|
119
|
+
theme={theme}
|
|
120
|
+
directionStyles={directionStyles}
|
|
121
|
+
title={title}
|
|
122
|
+
description={description}
|
|
123
|
+
keywords={keywords}
|
|
124
|
+
canonical={canonical}
|
|
125
|
+
lang={lang}
|
|
126
|
+
languages={languages}
|
|
127
|
+
scripts={scripts}
|
|
128
|
+
/>
|
|
129
|
+
{analyticsScript && <script dangerouslySetInnerHTML={{ __html: analyticsScript }} />}
|
|
130
|
+
</head>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
type DynamicPageBodyProps = {
|
|
135
|
+
readonly page: Page
|
|
136
|
+
readonly components?: Components
|
|
137
|
+
readonly theme?: Theme
|
|
138
|
+
readonly languages?: Languages
|
|
139
|
+
readonly direction: 'ltr' | 'rtl'
|
|
140
|
+
readonly scripts: ReturnType<typeof groupScriptsByPosition>
|
|
141
|
+
readonly lang: string
|
|
142
|
+
readonly bodyStyle:
|
|
143
|
+
| {
|
|
144
|
+
readonly fontFamily?: string
|
|
145
|
+
readonly fontSize?: string
|
|
146
|
+
readonly lineHeight?: string
|
|
147
|
+
readonly fontStyle?: 'normal' | 'italic' | 'oblique'
|
|
148
|
+
readonly letterSpacing?: string
|
|
149
|
+
readonly textTransform?: 'none' | 'uppercase' | 'lowercase' | 'capitalize'
|
|
150
|
+
}
|
|
151
|
+
| undefined
|
|
152
|
+
readonly routeParams?: RouteParams
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Generic parameter names that require context from path segments
|
|
157
|
+
* These parameters get prefixed with their context (e.g., product-id, user-key)
|
|
158
|
+
*/
|
|
159
|
+
const GENERIC_PARAM_NAMES = new Set(['id', 'key', 'uid', 'pk'])
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Converts route parameters to data attributes for testing
|
|
163
|
+
* Uses context-aware naming: generic params (id, key) get prefixed, descriptive ones (slug) don't
|
|
164
|
+
*
|
|
165
|
+
* @param routeParams - Route parameters extracted from URL
|
|
166
|
+
* @param path - Page path pattern (e.g., '/blog/:slug')
|
|
167
|
+
* @returns Data attributes object
|
|
168
|
+
*/
|
|
169
|
+
function buildDataAttributes(
|
|
170
|
+
routeParams: RouteParams | undefined,
|
|
171
|
+
path: string | undefined
|
|
172
|
+
): Record<string, string> {
|
|
173
|
+
if (!routeParams || !path) {
|
|
174
|
+
return {}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const pathSegments = path.split('/').filter(Boolean)
|
|
178
|
+
|
|
179
|
+
return Object.entries(routeParams).reduce<Record<string, string>>((acc, [key, value]) => {
|
|
180
|
+
// Check if parameter name is generic and needs context
|
|
181
|
+
if (GENERIC_PARAM_NAMES.has(key)) {
|
|
182
|
+
// Generic parameter - add context from previous path segment
|
|
183
|
+
// e.g., /products/:id -> data-product-id
|
|
184
|
+
const paramIndex = pathSegments.findIndex((seg) => seg === `:${key}`)
|
|
185
|
+
if (paramIndex > 0) {
|
|
186
|
+
const contextSegment = pathSegments[paramIndex - 1]
|
|
187
|
+
if (!contextSegment) {
|
|
188
|
+
return { ...acc, [`data-${key}`]: value }
|
|
189
|
+
}
|
|
190
|
+
// Convert plural to singular (e.g., 'products' -> 'product')
|
|
191
|
+
const singularContext = contextSegment.endsWith('s')
|
|
192
|
+
? contextSegment.slice(0, -1)
|
|
193
|
+
: contextSegment
|
|
194
|
+
return { ...acc, [`data-${singularContext}-${key}`]: value }
|
|
195
|
+
}
|
|
196
|
+
// No context available, use parameter name only
|
|
197
|
+
return { ...acc, [`data-${key}`]: value }
|
|
198
|
+
}
|
|
199
|
+
// Descriptive parameter - use as-is (e.g., /blog/:slug -> data-slug)
|
|
200
|
+
return { ...acc, [`data-${key}`]: value }
|
|
201
|
+
}, {})
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Renders the <body> section of DynamicPage
|
|
206
|
+
* Extracted to satisfy max-lines-per-function ESLint rule
|
|
207
|
+
*/
|
|
208
|
+
function DynamicPageBody({
|
|
209
|
+
page,
|
|
210
|
+
components,
|
|
211
|
+
theme,
|
|
212
|
+
languages,
|
|
213
|
+
direction,
|
|
214
|
+
scripts,
|
|
215
|
+
lang,
|
|
216
|
+
bodyStyle,
|
|
217
|
+
routeParams,
|
|
218
|
+
}: DynamicPageBodyProps): Readonly<ReactElement> {
|
|
219
|
+
const dataAttributes = buildDataAttributes(routeParams, page.path)
|
|
220
|
+
|
|
221
|
+
return (
|
|
222
|
+
<body
|
|
223
|
+
{...(bodyStyle && { style: bodyStyle })}
|
|
224
|
+
{...dataAttributes}
|
|
225
|
+
>
|
|
226
|
+
<PageBodyScripts
|
|
227
|
+
page={page}
|
|
228
|
+
theme={theme}
|
|
229
|
+
languages={languages}
|
|
230
|
+
direction={direction}
|
|
231
|
+
scripts={scripts}
|
|
232
|
+
position="start"
|
|
233
|
+
/>
|
|
234
|
+
<PageMain
|
|
235
|
+
page={page}
|
|
236
|
+
sections={page.sections}
|
|
237
|
+
theme={theme}
|
|
238
|
+
components={components}
|
|
239
|
+
languages={languages}
|
|
240
|
+
currentLang={lang}
|
|
241
|
+
/>
|
|
242
|
+
<PageBodyScripts
|
|
243
|
+
page={page}
|
|
244
|
+
theme={theme}
|
|
245
|
+
languages={languages}
|
|
246
|
+
direction={direction}
|
|
247
|
+
scripts={scripts}
|
|
248
|
+
position="end"
|
|
249
|
+
/>
|
|
250
|
+
</body>
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Renders a page from configuration as a complete HTML document
|
|
256
|
+
* Theme CSS is compiled globally at server startup via /assets/output.css
|
|
257
|
+
* Theme is still passed for font URLs, animation flags, and debugging
|
|
258
|
+
*/
|
|
259
|
+
export function DynamicPage({
|
|
260
|
+
page,
|
|
261
|
+
components,
|
|
262
|
+
theme,
|
|
263
|
+
languages,
|
|
264
|
+
detectedLanguage,
|
|
265
|
+
routeParams,
|
|
266
|
+
builtInAnalyticsEnabled,
|
|
267
|
+
builtInAnalyticsSessionTimeout,
|
|
268
|
+
}: DynamicPageProps): Readonly<ReactElement> {
|
|
269
|
+
const metadata = extractPageMetadata(page, theme, languages, detectedLanguage)
|
|
270
|
+
const langConfig = resolvePageLanguage(page, languages, detectedLanguage)
|
|
271
|
+
const scripts = groupScriptsByPosition(page)
|
|
272
|
+
const pageWithMeta = mergeComponentMetaIntoPage(page, components)
|
|
273
|
+
|
|
274
|
+
return (
|
|
275
|
+
<html
|
|
276
|
+
lang={langConfig.lang}
|
|
277
|
+
dir={langConfig.direction}
|
|
278
|
+
{...(page.scripts && { 'data-features': JSON.stringify(page.scripts.features || {}) })}
|
|
279
|
+
>
|
|
280
|
+
<DynamicPageHead
|
|
281
|
+
mergedPage={pageWithMeta}
|
|
282
|
+
theme={theme}
|
|
283
|
+
directionStyles={langConfig.directionStyles}
|
|
284
|
+
title={metadata.title}
|
|
285
|
+
description={metadata.description}
|
|
286
|
+
keywords={metadata.keywords}
|
|
287
|
+
canonical={metadata.canonical}
|
|
288
|
+
lang={langConfig.lang}
|
|
289
|
+
languages={languages}
|
|
290
|
+
scripts={scripts}
|
|
291
|
+
builtInAnalyticsEnabled={builtInAnalyticsEnabled}
|
|
292
|
+
builtInAnalyticsSessionTimeout={builtInAnalyticsSessionTimeout}
|
|
293
|
+
/>
|
|
294
|
+
<DynamicPageBody
|
|
295
|
+
page={page}
|
|
296
|
+
components={components}
|
|
297
|
+
theme={theme}
|
|
298
|
+
languages={languages}
|
|
299
|
+
direction={langConfig.direction}
|
|
300
|
+
scripts={scripts}
|
|
301
|
+
lang={langConfig.lang}
|
|
302
|
+
bodyStyle={metadata.bodyStyle}
|
|
303
|
+
routeParams={routeParams}
|
|
304
|
+
/>
|
|
305
|
+
</html>
|
|
306
|
+
)
|
|
307
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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 ReactElement } from 'react'
|
|
9
|
+
import { createErrorPageConfig } from '@/presentation/ui/pages/DefaultPageConfigs'
|
|
10
|
+
import { DynamicPage } from '@/presentation/ui/pages/DynamicPage'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* ErrorPage component - 500 internal server error page
|
|
14
|
+
*
|
|
15
|
+
* This page is displayed when the server encounters an unexpected error.
|
|
16
|
+
* It provides a clear message and a link back to the homepage.
|
|
17
|
+
*
|
|
18
|
+
* Uses DynamicPage pattern with theme-generated styles for consistency across the application.
|
|
19
|
+
*
|
|
20
|
+
* @returns React element for 500 error page
|
|
21
|
+
*/
|
|
22
|
+
export function ErrorPage(): Readonly<ReactElement> {
|
|
23
|
+
const pageConfig = createErrorPageConfig()
|
|
24
|
+
return <DynamicPage page={pageConfig} />
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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 ReactElement } from 'react'
|
|
9
|
+
import { createNotFoundPageConfig } from '@/presentation/ui/pages/DefaultPageConfigs'
|
|
10
|
+
import { DynamicPage } from '@/presentation/ui/pages/DynamicPage'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* NotFoundPage component - 404 error page
|
|
14
|
+
*
|
|
15
|
+
* This page is displayed when a user navigates to a route that doesn't exist.
|
|
16
|
+
* It provides a clear message and a link back to the homepage.
|
|
17
|
+
*
|
|
18
|
+
* Uses DynamicPage pattern with theme-generated styles for consistency across the application.
|
|
19
|
+
*
|
|
20
|
+
* @returns React element for 404 error page
|
|
21
|
+
*/
|
|
22
|
+
export function NotFoundPage(): Readonly<ReactElement> {
|
|
23
|
+
const pageConfig = createNotFoundPageConfig()
|
|
24
|
+
return <DynamicPage page={pageConfig} />
|
|
25
|
+
}
|