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,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Languages } from '@/domain/models/app/languages'
|
|
9
|
+
import type { Page } from '@/domain/models/app/pages'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Page language configuration
|
|
13
|
+
*/
|
|
14
|
+
export type PageLangConfig = {
|
|
15
|
+
readonly lang: string
|
|
16
|
+
readonly direction: 'ltr' | 'rtl'
|
|
17
|
+
readonly directionStyles: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolves language configuration for a page
|
|
22
|
+
*
|
|
23
|
+
* Priority: page.meta.lang > detectedLanguage > languages.default > 'en-US'
|
|
24
|
+
*
|
|
25
|
+
* @param page - Page configuration
|
|
26
|
+
* @param languages - Languages configuration
|
|
27
|
+
* @param detectedLanguage - Detected language from browser/URL
|
|
28
|
+
* @returns Language configuration with direction and styles
|
|
29
|
+
*/
|
|
30
|
+
export function resolvePageLanguage(
|
|
31
|
+
page: Page,
|
|
32
|
+
languages: Languages | undefined,
|
|
33
|
+
detectedLanguage: string | undefined
|
|
34
|
+
): PageLangConfig {
|
|
35
|
+
// Priority: page.meta.lang (EXPLICIT) > detectedLanguage > languages.default > fallback
|
|
36
|
+
// NOTE: Using explicit check to ensure page.meta.lang is always preferred when present
|
|
37
|
+
const lang =
|
|
38
|
+
page.meta && page.meta.lang
|
|
39
|
+
? page.meta.lang
|
|
40
|
+
: detectedLanguage
|
|
41
|
+
? detectedLanguage
|
|
42
|
+
: languages?.default
|
|
43
|
+
? languages.default
|
|
44
|
+
: 'en-US'
|
|
45
|
+
|
|
46
|
+
// Determine text direction from language configuration
|
|
47
|
+
// Match by code (en) or locale (en-US)
|
|
48
|
+
const langConfig = languages?.supported.find((l) => l.code === lang || l.locale === lang)
|
|
49
|
+
const direction = langConfig?.direction || 'ltr'
|
|
50
|
+
|
|
51
|
+
// Generate CSS for body direction to ensure RTL/LTR is applied as CSS property
|
|
52
|
+
const directionStyles = `
|
|
53
|
+
html[lang="${lang}"] { direction: ${direction}; }
|
|
54
|
+
html[lang="${lang}"] body { direction: ${direction}; }
|
|
55
|
+
`
|
|
56
|
+
|
|
57
|
+
return { lang, direction, directionStyles }
|
|
58
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { type ReactElement } from 'react'
|
|
9
|
+
import { SectionRenderer } from '@/presentation/ui/pages/SectionRenderer'
|
|
10
|
+
import { toSlug } from '@/presentation/utils/string-utils'
|
|
11
|
+
import type {
|
|
12
|
+
ComponentReference,
|
|
13
|
+
SimpleComponentReference,
|
|
14
|
+
} from '@/domain/models/app/component/common/component-reference'
|
|
15
|
+
import type { Components } from '@/domain/models/app/components'
|
|
16
|
+
import type { Languages } from '@/domain/models/app/languages'
|
|
17
|
+
import type { Component } from '@/domain/models/app/page/sections'
|
|
18
|
+
import type { Page } from '@/domain/models/app/pages'
|
|
19
|
+
import type { Theme } from '@/domain/models/app/theme'
|
|
20
|
+
|
|
21
|
+
type PageMainProps = {
|
|
22
|
+
readonly page: Page
|
|
23
|
+
readonly sections: ReadonlyArray<Component | SimpleComponentReference | ComponentReference>
|
|
24
|
+
readonly theme?: Theme
|
|
25
|
+
readonly components?: Components
|
|
26
|
+
readonly languages?: Languages
|
|
27
|
+
readonly currentLang: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Renders the main content area with page sections
|
|
32
|
+
*/
|
|
33
|
+
export function PageMain({
|
|
34
|
+
page,
|
|
35
|
+
sections,
|
|
36
|
+
theme,
|
|
37
|
+
components,
|
|
38
|
+
languages,
|
|
39
|
+
currentLang,
|
|
40
|
+
}: PageMainProps): Readonly<ReactElement> {
|
|
41
|
+
return (
|
|
42
|
+
<main
|
|
43
|
+
data-testid={`page-${toSlug(page.name ?? page.path)}`}
|
|
44
|
+
data-page-name={page.name}
|
|
45
|
+
data-page-id={page.id}
|
|
46
|
+
style={{ minHeight: '1px' }}
|
|
47
|
+
>
|
|
48
|
+
<SectionRenderer
|
|
49
|
+
sections={sections}
|
|
50
|
+
pageVars={page.vars}
|
|
51
|
+
theme={theme}
|
|
52
|
+
components={components}
|
|
53
|
+
languages={languages}
|
|
54
|
+
currentLang={currentLang}
|
|
55
|
+
/>
|
|
56
|
+
</main>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
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 { resolveTranslationPattern } from '@/presentation/translations/translation-resolver'
|
|
9
|
+
import type { Languages } from '@/domain/models/app/languages'
|
|
10
|
+
import type { Page } from '@/domain/models/app/pages'
|
|
11
|
+
import type { Theme } from '@/domain/models/app/theme'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Metadata derived from page configuration
|
|
15
|
+
*/
|
|
16
|
+
export type PageMetadata = {
|
|
17
|
+
readonly lang: string
|
|
18
|
+
readonly direction: 'ltr' | 'rtl'
|
|
19
|
+
readonly title: string
|
|
20
|
+
readonly description: string
|
|
21
|
+
readonly keywords?: string
|
|
22
|
+
readonly canonical?: string
|
|
23
|
+
readonly bodyStyle:
|
|
24
|
+
| {
|
|
25
|
+
readonly fontFamily?: string
|
|
26
|
+
readonly fontSize?: string
|
|
27
|
+
readonly lineHeight?: string
|
|
28
|
+
readonly fontStyle?: 'normal' | 'italic' | 'oblique'
|
|
29
|
+
readonly letterSpacing?: string
|
|
30
|
+
readonly textTransform?: 'none' | 'uppercase' | 'lowercase' | 'capitalize'
|
|
31
|
+
}
|
|
32
|
+
| undefined
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Build font-family string with fallback
|
|
37
|
+
*/
|
|
38
|
+
function buildFontFamily(family?: string, fallback?: string): string | undefined {
|
|
39
|
+
if (!family) {
|
|
40
|
+
return undefined
|
|
41
|
+
}
|
|
42
|
+
return fallback ? `${family}, ${fallback}` : family
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Build body style object from theme fonts configuration
|
|
47
|
+
*/
|
|
48
|
+
function buildBodyStyle(theme: Theme | undefined): PageMetadata['bodyStyle'] {
|
|
49
|
+
if (!theme?.fonts?.body) {
|
|
50
|
+
return undefined
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const { body } = theme.fonts
|
|
54
|
+
const fontFamily = buildFontFamily(body.family, body.fallback)
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
...(fontFamily && { fontFamily }),
|
|
58
|
+
...(body.size && { fontSize: body.size }),
|
|
59
|
+
...(body.lineHeight && { lineHeight: body.lineHeight }),
|
|
60
|
+
...(body.style && { fontStyle: body.style as 'normal' | 'italic' | 'oblique' }),
|
|
61
|
+
...(body.letterSpacing && { letterSpacing: body.letterSpacing }),
|
|
62
|
+
...(body.transform && {
|
|
63
|
+
textTransform: body.transform as 'none' | 'uppercase' | 'lowercase' | 'capitalize',
|
|
64
|
+
}),
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Determine language code (priority: page.meta.lang > detectedLanguage > default)
|
|
70
|
+
*/
|
|
71
|
+
function determineLanguage(
|
|
72
|
+
page: Page,
|
|
73
|
+
languages: Languages | undefined,
|
|
74
|
+
detectedLanguage: string | undefined
|
|
75
|
+
): string {
|
|
76
|
+
return page.meta?.lang || detectedLanguage || languages?.default || 'en-US'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Determine text direction from language configuration
|
|
81
|
+
*/
|
|
82
|
+
function determineDirection(languages: Languages | undefined, lang: string): 'ltr' | 'rtl' {
|
|
83
|
+
const langConfig = languages?.supported.find((l) => l.code === lang)
|
|
84
|
+
return langConfig?.direction || 'ltr'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Determine page title with translation resolution
|
|
89
|
+
* Priority: meta.i18n[lang].title > meta.title (with $t: pattern) > page.name > page.path
|
|
90
|
+
*/
|
|
91
|
+
function determineTitle(page: Page, lang: string, languages: Languages | undefined): string {
|
|
92
|
+
// Check if page has i18n translations for this language
|
|
93
|
+
if (page.meta?.i18n?.[lang]?.title) {
|
|
94
|
+
return page.meta.i18n[lang].title
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Fall back to base title with translation pattern resolution
|
|
98
|
+
const rawTitle = page.meta?.title || page.name || page.path
|
|
99
|
+
return resolveTranslationPattern(rawTitle, lang, languages)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Determine page description with translation resolution
|
|
104
|
+
* Priority: meta.i18n[lang].description > meta.description (with $t: pattern)
|
|
105
|
+
*/
|
|
106
|
+
function determineDescription(page: Page, lang: string, languages: Languages | undefined): string {
|
|
107
|
+
// Check if page has i18n translations for this language
|
|
108
|
+
if (page.meta?.i18n?.[lang]?.description) {
|
|
109
|
+
return page.meta.i18n[lang].description
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Fall back to base description with translation pattern resolution
|
|
113
|
+
const rawDescription = page.meta?.description || ''
|
|
114
|
+
return resolveTranslationPattern(rawDescription, lang, languages)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Determine page keywords with translation resolution
|
|
119
|
+
* Resolves $t: patterns in keywords
|
|
120
|
+
*/
|
|
121
|
+
function determineKeywords(
|
|
122
|
+
page: Page,
|
|
123
|
+
lang: string,
|
|
124
|
+
languages: Languages | undefined
|
|
125
|
+
): string | undefined {
|
|
126
|
+
if (!page.meta?.keywords) {
|
|
127
|
+
return undefined
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return resolveTranslationPattern(page.meta.keywords, lang, languages)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Extracts and computes metadata from page configuration
|
|
135
|
+
*
|
|
136
|
+
* Determines language (page.meta.lang > detectedLanguage > default),
|
|
137
|
+
* text direction from language config, title/description, and body styles from theme.
|
|
138
|
+
*
|
|
139
|
+
* @param page - Page configuration
|
|
140
|
+
* @param theme - Optional theme configuration
|
|
141
|
+
* @param languages - Optional languages configuration
|
|
142
|
+
* @param detectedLanguage - Optional detected language from browser or URL
|
|
143
|
+
* @returns Computed page metadata
|
|
144
|
+
*/
|
|
145
|
+
export function extractPageMetadata(
|
|
146
|
+
page: Page,
|
|
147
|
+
theme: Theme | undefined,
|
|
148
|
+
languages: Languages | undefined,
|
|
149
|
+
detectedLanguage: string | undefined
|
|
150
|
+
): Readonly<PageMetadata> {
|
|
151
|
+
const lang = determineLanguage(page, languages, detectedLanguage)
|
|
152
|
+
const direction = determineDirection(languages, lang)
|
|
153
|
+
const title = determineTitle(page, lang, languages)
|
|
154
|
+
const description = determineDescription(page, lang, languages)
|
|
155
|
+
const keywords = determineKeywords(page, lang, languages)
|
|
156
|
+
const canonical = page.meta?.canonical
|
|
157
|
+
const bodyStyle = buildBodyStyle(theme)
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
lang,
|
|
161
|
+
direction,
|
|
162
|
+
title,
|
|
163
|
+
description,
|
|
164
|
+
keywords,
|
|
165
|
+
canonical,
|
|
166
|
+
bodyStyle,
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
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 { resolveTranslationPattern } from '@/presentation/translations/translation-resolver'
|
|
9
|
+
import type { Languages } from '@/domain/models/app/languages'
|
|
10
|
+
import type { Meta } from '@/domain/models/app/page/meta'
|
|
11
|
+
import type { Page } from '@/domain/models/app/pages'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Resolves i18n metadata for a single language
|
|
15
|
+
*/
|
|
16
|
+
function resolveMetaI18n(
|
|
17
|
+
meta: Meta,
|
|
18
|
+
langCode: string,
|
|
19
|
+
languages: Languages
|
|
20
|
+
): { title?: string; description?: string; keywords?: string; 'og:site_name'?: string } {
|
|
21
|
+
const metaRecord = meta as Record<string, unknown>
|
|
22
|
+
const ogSiteName = metaRecord['og:site_name']
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
...(meta.title && {
|
|
26
|
+
title: resolveTranslationPattern(meta.title, langCode, languages),
|
|
27
|
+
}),
|
|
28
|
+
...(meta.description && {
|
|
29
|
+
description: resolveTranslationPattern(meta.description, langCode, languages),
|
|
30
|
+
}),
|
|
31
|
+
...(meta.keywords && {
|
|
32
|
+
keywords: resolveTranslationPattern(meta.keywords, langCode, languages),
|
|
33
|
+
}),
|
|
34
|
+
...(typeof ogSiteName === 'string' && {
|
|
35
|
+
'og:site_name': resolveTranslationPattern(ogSiteName, langCode, languages),
|
|
36
|
+
}),
|
|
37
|
+
...(meta.openGraph?.siteName &&
|
|
38
|
+
typeof ogSiteName !== 'string' && {
|
|
39
|
+
'og:site_name': resolveTranslationPattern(meta.openGraph.siteName, langCode, languages),
|
|
40
|
+
}),
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Merges existing i18n with generated i18n
|
|
46
|
+
*/
|
|
47
|
+
function mergeI18n(
|
|
48
|
+
generatedI18n: Record<
|
|
49
|
+
string,
|
|
50
|
+
{ title?: string; description?: string; keywords?: string; 'og:site_name'?: string }
|
|
51
|
+
>,
|
|
52
|
+
existingI18n:
|
|
53
|
+
| Record<
|
|
54
|
+
string,
|
|
55
|
+
{ title?: string; description?: string; keywords?: string; 'og:site_name'?: string }
|
|
56
|
+
>
|
|
57
|
+
| undefined
|
|
58
|
+
): typeof generatedI18n {
|
|
59
|
+
if (!existingI18n) {
|
|
60
|
+
return generatedI18n
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return Object.keys(generatedI18n).reduce(
|
|
64
|
+
(acc, langCode) => ({
|
|
65
|
+
...acc,
|
|
66
|
+
[langCode]: {
|
|
67
|
+
...generatedI18n[langCode],
|
|
68
|
+
...(existingI18n[langCode] || {}),
|
|
69
|
+
},
|
|
70
|
+
}),
|
|
71
|
+
{} as typeof generatedI18n
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Resolves base meta fields with translation patterns
|
|
77
|
+
*/
|
|
78
|
+
function resolveBaseMeta(meta: Meta, currentLang: string, languages: Languages): Meta {
|
|
79
|
+
const metaRecord = meta as Record<string, unknown>
|
|
80
|
+
const ogSiteName = metaRecord['og:site_name']
|
|
81
|
+
|
|
82
|
+
const resolvedMeta = {
|
|
83
|
+
...meta,
|
|
84
|
+
...(meta.title && { title: resolveTranslationPattern(meta.title, currentLang, languages) }),
|
|
85
|
+
...(meta.description && {
|
|
86
|
+
description: resolveTranslationPattern(meta.description, currentLang, languages),
|
|
87
|
+
}),
|
|
88
|
+
...(meta.keywords && {
|
|
89
|
+
keywords: resolveTranslationPattern(meta.keywords, currentLang, languages),
|
|
90
|
+
}),
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Resolve og:site_name if present (either as direct property or in openGraph object)
|
|
94
|
+
if (typeof ogSiteName === 'string') {
|
|
95
|
+
return {
|
|
96
|
+
...resolvedMeta,
|
|
97
|
+
'og:site_name': resolveTranslationPattern(ogSiteName, currentLang, languages),
|
|
98
|
+
} as Meta
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (meta.openGraph?.siteName) {
|
|
102
|
+
return {
|
|
103
|
+
...resolvedMeta,
|
|
104
|
+
openGraph: {
|
|
105
|
+
...meta.openGraph,
|
|
106
|
+
siteName: resolveTranslationPattern(meta.openGraph.siteName, currentLang, languages),
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return resolvedMeta
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Builds i18n metadata structure for client-side language switching
|
|
116
|
+
*
|
|
117
|
+
* Resolves translation patterns ($t:...) for all supported languages
|
|
118
|
+
* and constructs a complete i18n structure that the client can use
|
|
119
|
+
* to update meta tags when switching languages.
|
|
120
|
+
*
|
|
121
|
+
* CRITICAL: All $t: tokens in base meta fields are resolved to ensure
|
|
122
|
+
* no translation tokens appear in the serialized HTML output.
|
|
123
|
+
*
|
|
124
|
+
* @param page - Page configuration
|
|
125
|
+
* @param languages - Languages configuration
|
|
126
|
+
* @returns Page meta with i18n structure populated and all $t: tokens resolved
|
|
127
|
+
*/
|
|
128
|
+
export function buildPageMetadataI18n(
|
|
129
|
+
page: Page,
|
|
130
|
+
languages: Languages | undefined
|
|
131
|
+
): Meta | Record<string, never> {
|
|
132
|
+
if (!page.meta || !languages) {
|
|
133
|
+
return (page.meta || {}) as Meta | Record<string, never>
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const { meta } = page
|
|
137
|
+
const currentLang = meta.lang || languages.default
|
|
138
|
+
|
|
139
|
+
// Build i18n structure for all supported languages
|
|
140
|
+
const generatedI18n = languages.supported.reduce(
|
|
141
|
+
(acc, lang) => ({
|
|
142
|
+
...acc,
|
|
143
|
+
[lang.code]: resolveMetaI18n(meta, lang.code, languages),
|
|
144
|
+
}),
|
|
145
|
+
{} as Record<
|
|
146
|
+
string,
|
|
147
|
+
{ title?: string; description?: string; keywords?: string; 'og:site_name'?: string }
|
|
148
|
+
>
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
// Merge existing meta.i18n with generated i18n (existing takes precedence)
|
|
152
|
+
const existingI18n = (meta as Record<string, unknown>).i18n as
|
|
153
|
+
| Record<
|
|
154
|
+
string,
|
|
155
|
+
{ title?: string; description?: string; keywords?: string; 'og:site_name'?: string }
|
|
156
|
+
>
|
|
157
|
+
| undefined
|
|
158
|
+
|
|
159
|
+
const i18n = mergeI18n(generatedI18n, existingI18n)
|
|
160
|
+
|
|
161
|
+
// Resolve all $t: tokens in base meta fields for current language
|
|
162
|
+
const resolvedMeta = resolveBaseMeta(meta, currentLang, languages)
|
|
163
|
+
|
|
164
|
+
// Return resolved meta with i18n structure
|
|
165
|
+
return {
|
|
166
|
+
...resolvedMeta,
|
|
167
|
+
i18n,
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
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 { Page } from '@/domain/models/app/pages'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* External script configuration
|
|
12
|
+
*/
|
|
13
|
+
export type ExternalScript = {
|
|
14
|
+
readonly src: string
|
|
15
|
+
readonly position?: 'head' | 'body-start' | 'body-end'
|
|
16
|
+
readonly async?: boolean
|
|
17
|
+
readonly defer?: boolean
|
|
18
|
+
readonly module?: boolean
|
|
19
|
+
readonly integrity?: string
|
|
20
|
+
readonly crossorigin?: 'anonymous' | 'use-credentials'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Inline script configuration
|
|
25
|
+
*/
|
|
26
|
+
export type InlineScript = {
|
|
27
|
+
readonly code: string
|
|
28
|
+
readonly position?: 'head' | 'body-start' | 'body-end'
|
|
29
|
+
readonly async?: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Grouped scripts by position
|
|
34
|
+
*/
|
|
35
|
+
export type GroupedScripts = {
|
|
36
|
+
readonly external: {
|
|
37
|
+
readonly head: ReadonlyArray<ExternalScript>
|
|
38
|
+
readonly bodyStart: ReadonlyArray<ExternalScript>
|
|
39
|
+
readonly bodyEnd: ReadonlyArray<ExternalScript>
|
|
40
|
+
}
|
|
41
|
+
readonly inline: {
|
|
42
|
+
readonly head: ReadonlyArray<InlineScript>
|
|
43
|
+
readonly bodyStart: ReadonlyArray<InlineScript>
|
|
44
|
+
readonly bodyEnd: ReadonlyArray<InlineScript>
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Groups external and inline scripts by their position in the document
|
|
50
|
+
*
|
|
51
|
+
* Scripts can be positioned in 'head', 'body-start', or 'body-end'.
|
|
52
|
+
* Scripts without a position default to 'body-end'.
|
|
53
|
+
*
|
|
54
|
+
* @param page - Page configuration containing scripts
|
|
55
|
+
* @returns Scripts grouped by position
|
|
56
|
+
*/
|
|
57
|
+
export function groupScriptsByPosition(page: Page): Readonly<GroupedScripts> {
|
|
58
|
+
// Extract external scripts (support both test shorthand and canonical property)
|
|
59
|
+
const externalScripts = page.scripts?.externalScripts || page.scripts?.external || []
|
|
60
|
+
|
|
61
|
+
// Extract inline scripts
|
|
62
|
+
const inlineScripts = page.scripts?.inlineScripts || []
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
external: {
|
|
66
|
+
head: externalScripts.filter((script) => script.position === 'head'),
|
|
67
|
+
bodyStart: externalScripts.filter((script) => script.position === 'body-start'),
|
|
68
|
+
bodyEnd: externalScripts.filter(
|
|
69
|
+
(script) => !script.position || script.position === 'body-end'
|
|
70
|
+
),
|
|
71
|
+
},
|
|
72
|
+
inline: {
|
|
73
|
+
head: inlineScripts.filter((script) => script.position === 'head'),
|
|
74
|
+
bodyStart: inlineScripts.filter((script) => script.position === 'body-start'),
|
|
75
|
+
bodyEnd: inlineScripts.filter((script) => !script.position || script.position === 'body-end'),
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
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 { getComponentInfo } from '@/presentation/translations/component-utils'
|
|
10
|
+
import { renderSectionWithSpacing } from '@/presentation/ui/pages/SectionSpacing'
|
|
11
|
+
import { ComponentRenderer } from '@/presentation/ui/sections/component-renderer'
|
|
12
|
+
import type {
|
|
13
|
+
ComponentReference,
|
|
14
|
+
SimpleComponentReference,
|
|
15
|
+
} from '@/domain/models/app/component/common/component-reference'
|
|
16
|
+
import type { Components } from '@/domain/models/app/components'
|
|
17
|
+
import type { Languages } from '@/domain/models/app/languages'
|
|
18
|
+
import type { Component } from '@/domain/models/app/page/sections'
|
|
19
|
+
import type { Theme } from '@/domain/models/app/theme'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Props for the SectionRenderer component
|
|
23
|
+
*/
|
|
24
|
+
export interface SectionRendererProps {
|
|
25
|
+
readonly sections: ReadonlyArray<Component | SimpleComponentReference | ComponentReference>
|
|
26
|
+
readonly pageVars?: Record<string, string | number | boolean>
|
|
27
|
+
readonly theme?: Theme
|
|
28
|
+
readonly components?: Components
|
|
29
|
+
readonly languages?: Languages
|
|
30
|
+
readonly currentLang: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Render sections with theme spacing
|
|
35
|
+
* Handles section wrapping, container spacing, and component resolution
|
|
36
|
+
*
|
|
37
|
+
* @param props - Component props
|
|
38
|
+
* @returns React element with sections
|
|
39
|
+
*/
|
|
40
|
+
export function SectionRenderer({
|
|
41
|
+
sections,
|
|
42
|
+
pageVars,
|
|
43
|
+
theme,
|
|
44
|
+
components,
|
|
45
|
+
languages,
|
|
46
|
+
currentLang,
|
|
47
|
+
}: SectionRendererProps): Readonly<ReactElement> {
|
|
48
|
+
const renderedSections = sections.map((section, index) => {
|
|
49
|
+
const componentInfo = getComponentInfo(section, index, sections)
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<ComponentRenderer
|
|
53
|
+
key={index}
|
|
54
|
+
component={section}
|
|
55
|
+
pageVars={pageVars}
|
|
56
|
+
componentName={componentInfo?.name}
|
|
57
|
+
componentInstanceIndex={componentInfo?.instanceIndex}
|
|
58
|
+
components={components}
|
|
59
|
+
theme={theme}
|
|
60
|
+
languages={languages}
|
|
61
|
+
currentLang={currentLang}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
return renderSectionWithSpacing(theme, sections, renderedSections)
|
|
67
|
+
}
|