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,108 @@
|
|
|
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 { ComponentMeta } from './structured-data-from-component'
|
|
9
|
+
import type {
|
|
10
|
+
ComponentReference,
|
|
11
|
+
SimpleComponentReference,
|
|
12
|
+
} from '@/domain/models/app/component/common/component-reference'
|
|
13
|
+
import type { Components } from '@/domain/models/app/components'
|
|
14
|
+
import type { OpenGraph } from '@/domain/models/app/page/meta/open-graph'
|
|
15
|
+
import type { Component } from '@/domain/models/app/page/sections'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Substitutes variables in a string value
|
|
19
|
+
*
|
|
20
|
+
* @param value - String value with potential $variable placeholders
|
|
21
|
+
* @param vars - Variables map
|
|
22
|
+
* @returns String with variables substituted
|
|
23
|
+
*/
|
|
24
|
+
function substituteMetaVariables(
|
|
25
|
+
value: string | undefined,
|
|
26
|
+
vars: Record<string, string | number | boolean> | undefined
|
|
27
|
+
): string | undefined {
|
|
28
|
+
if (!value || !vars) return value
|
|
29
|
+
|
|
30
|
+
// Use reduce for functional approach instead of loop with mutation
|
|
31
|
+
return Object.entries(vars).reduce(
|
|
32
|
+
(result, [key, varValue]) => result.replace(new RegExp(`\\$${key}`, 'g'), String(varValue)),
|
|
33
|
+
value
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Extracts Open Graph meta from a component's meta configuration
|
|
39
|
+
*
|
|
40
|
+
* @param meta - Component meta configuration
|
|
41
|
+
* @param vars - Variables for substitution
|
|
42
|
+
* @returns Partial Open Graph configuration
|
|
43
|
+
*/
|
|
44
|
+
function extractOpenGraphFromComponentMeta(
|
|
45
|
+
meta: ComponentMeta | undefined,
|
|
46
|
+
vars: Record<string, string | number | boolean> | undefined
|
|
47
|
+
): Partial<OpenGraph> | undefined {
|
|
48
|
+
if (!meta) return undefined
|
|
49
|
+
|
|
50
|
+
// Build immutably without mutation
|
|
51
|
+
const withImage = meta.image ? { image: substituteMetaVariables(meta.image, vars) } : {}
|
|
52
|
+
|
|
53
|
+
const withTitle = meta.title ? { title: substituteMetaVariables(meta.title, vars) } : {}
|
|
54
|
+
|
|
55
|
+
const withDescription = meta.description
|
|
56
|
+
? { description: substituteMetaVariables(meta.description, vars) }
|
|
57
|
+
: {}
|
|
58
|
+
|
|
59
|
+
const openGraph = {
|
|
60
|
+
...withImage,
|
|
61
|
+
...withTitle,
|
|
62
|
+
...withDescription,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return Object.keys(openGraph).length > 0 ? openGraph : undefined
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Extracts component meta from page sections
|
|
70
|
+
*
|
|
71
|
+
* Processes all sections to find component references, resolves them, and extracts
|
|
72
|
+
* meta information that should be included in the page's Open Graph meta tags.
|
|
73
|
+
*
|
|
74
|
+
* @param sections - Page sections
|
|
75
|
+
* @param components - Available component templates
|
|
76
|
+
* @returns Merged Open Graph configuration from all components
|
|
77
|
+
*/
|
|
78
|
+
export function extractComponentMetaFromSections(
|
|
79
|
+
sections: ReadonlyArray<Component | SimpleComponentReference | ComponentReference>,
|
|
80
|
+
components?: Components
|
|
81
|
+
): Partial<OpenGraph> | undefined {
|
|
82
|
+
if (!sections || !components) return undefined
|
|
83
|
+
|
|
84
|
+
// Use functional map/filter instead of loop with mutation
|
|
85
|
+
const openGraphParts = sections
|
|
86
|
+
.filter(
|
|
87
|
+
(section): section is SimpleComponentReference | ComponentReference =>
|
|
88
|
+
'component' in section || '$ref' in section
|
|
89
|
+
)
|
|
90
|
+
.map((section) => {
|
|
91
|
+
const componentName = 'component' in section ? section.component : section.$ref
|
|
92
|
+
const vars = 'vars' in section ? section.vars : undefined
|
|
93
|
+
|
|
94
|
+
// Find the component template definition
|
|
95
|
+
const template = components.find((b) => b.name === componentName)
|
|
96
|
+
if (!template?.props?.meta) return undefined
|
|
97
|
+
|
|
98
|
+
// Extract Open Graph meta from component meta
|
|
99
|
+
const meta = template.props.meta as ComponentMeta | undefined
|
|
100
|
+
return extractOpenGraphFromComponentMeta(meta, vars)
|
|
101
|
+
})
|
|
102
|
+
.filter((og): og is Partial<OpenGraph> => og !== undefined)
|
|
103
|
+
|
|
104
|
+
if (openGraphParts.length === 0) return undefined
|
|
105
|
+
|
|
106
|
+
// Merge all Open Graph parts (last one wins for duplicate keys)
|
|
107
|
+
return openGraphParts.reduce((acc, part) => ({ ...acc, ...part }), {})
|
|
108
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
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 { buildCustomElement } from './custom-elements-builders'
|
|
10
|
+
import type { CustomElements } from '@/domain/models/app/page/meta/custom-elements'
|
|
11
|
+
import type { FaviconSet } from '@/domain/models/app/page/meta/favicon-set'
|
|
12
|
+
import type { Preload } from '@/domain/models/app/page/meta/preload'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Render DNS prefetch link tags
|
|
16
|
+
* Generates <link rel="dns-prefetch" href="..."> tags for external domains
|
|
17
|
+
*
|
|
18
|
+
* @param dnsPrefetch - DNS prefetch configuration from page.meta
|
|
19
|
+
* @returns React fragment with DNS prefetch link tags
|
|
20
|
+
*/
|
|
21
|
+
export function DnsPrefetchLinks({
|
|
22
|
+
dnsPrefetch,
|
|
23
|
+
}: {
|
|
24
|
+
readonly dnsPrefetch?: ReadonlyArray<string>
|
|
25
|
+
}): Readonly<ReactElement | undefined> {
|
|
26
|
+
if (!dnsPrefetch || dnsPrefetch.length === 0) {
|
|
27
|
+
return undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
{dnsPrefetch.map((domain) => (
|
|
33
|
+
<link
|
|
34
|
+
key={domain}
|
|
35
|
+
rel="dns-prefetch"
|
|
36
|
+
href={domain}
|
|
37
|
+
/>
|
|
38
|
+
))}
|
|
39
|
+
</>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Render custom head elements
|
|
45
|
+
* Generates arbitrary HTML elements (meta, link, script, style, base) in <head>
|
|
46
|
+
*
|
|
47
|
+
* @param customElements - Custom elements configuration from page.meta
|
|
48
|
+
* @returns React fragment with custom head elements
|
|
49
|
+
*/
|
|
50
|
+
export function CustomElementsHead({
|
|
51
|
+
customElements,
|
|
52
|
+
}: {
|
|
53
|
+
readonly customElements?: CustomElements
|
|
54
|
+
}): Readonly<ReactElement | undefined> {
|
|
55
|
+
if (!customElements || customElements.length === 0) {
|
|
56
|
+
return undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return <>{customElements.map(buildCustomElement)}</>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Render single favicon link tag
|
|
64
|
+
* Generates simple <link rel="icon" href="..."> tag for default favicon
|
|
65
|
+
*
|
|
66
|
+
* @param favicon - Favicon path from page.meta.favicon
|
|
67
|
+
* @returns React element with favicon link tag or undefined
|
|
68
|
+
*/
|
|
69
|
+
export function FaviconLink({
|
|
70
|
+
favicon,
|
|
71
|
+
}: {
|
|
72
|
+
readonly favicon?: string
|
|
73
|
+
}): Readonly<ReactElement | undefined> {
|
|
74
|
+
if (!favicon) {
|
|
75
|
+
return undefined
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<link
|
|
80
|
+
rel="icon"
|
|
81
|
+
href={favicon}
|
|
82
|
+
/>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Render favicon set link tags
|
|
88
|
+
* Generates <link rel="..."> tags for multi-device favicon support
|
|
89
|
+
*
|
|
90
|
+
* Supports:
|
|
91
|
+
* - icon: Standard browser favicon (16x16, 32x32)
|
|
92
|
+
* - apple-touch-icon: iOS home screen icon (180x180)
|
|
93
|
+
* - manifest: PWA manifest file reference
|
|
94
|
+
* - mask-icon: Safari pinned tab icon (monochrome SVG with color)
|
|
95
|
+
*
|
|
96
|
+
* @param favicons - Favicon set configuration from page.meta
|
|
97
|
+
* @returns React fragment with favicon link tags
|
|
98
|
+
*/
|
|
99
|
+
export function FaviconSetLinks({
|
|
100
|
+
favicons,
|
|
101
|
+
}: {
|
|
102
|
+
readonly favicons?: FaviconSet
|
|
103
|
+
}): Readonly<ReactElement | undefined> {
|
|
104
|
+
if (!favicons || favicons.length === 0) {
|
|
105
|
+
return undefined
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<>
|
|
110
|
+
{favicons.map((favicon, index) => {
|
|
111
|
+
// Convert relative path (./favicon.png) to absolute path (/favicon.png)
|
|
112
|
+
// Remove the leading ./ to make it an absolute path from the root
|
|
113
|
+
const href = favicon.href.replace(/^\.\//, '/')
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<link
|
|
117
|
+
key={index}
|
|
118
|
+
rel={favicon.rel}
|
|
119
|
+
href={href}
|
|
120
|
+
{...(favicon.type && { type: favicon.type })}
|
|
121
|
+
{...(favicon.sizes && { sizes: favicon.sizes })}
|
|
122
|
+
{...(favicon.color && { color: favicon.color })}
|
|
123
|
+
/>
|
|
124
|
+
)
|
|
125
|
+
})}
|
|
126
|
+
</>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Render preload link tags
|
|
132
|
+
* Generates <link rel="preload" ...> tags for critical resources
|
|
133
|
+
*
|
|
134
|
+
* @param preload - Preload configuration from page.meta
|
|
135
|
+
* @returns React fragment with preload link tags
|
|
136
|
+
*/
|
|
137
|
+
export function PreloadLinks({
|
|
138
|
+
preload,
|
|
139
|
+
}: {
|
|
140
|
+
readonly preload?: Preload
|
|
141
|
+
}): Readonly<ReactElement | undefined> {
|
|
142
|
+
if (!preload || preload.length === 0) {
|
|
143
|
+
return undefined
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<>
|
|
148
|
+
{preload.map((item, index) => (
|
|
149
|
+
<link
|
|
150
|
+
key={index}
|
|
151
|
+
rel="preload"
|
|
152
|
+
href={item.href}
|
|
153
|
+
as={item.as}
|
|
154
|
+
{...(item.type && { type: item.type })}
|
|
155
|
+
{...(item.crossorigin !== undefined &&
|
|
156
|
+
(typeof item.crossorigin === 'boolean'
|
|
157
|
+
? item.crossorigin && { crossOrigin: '' }
|
|
158
|
+
: { crossOrigin: item.crossorigin }))}
|
|
159
|
+
{...(item.media && { media: item.media })}
|
|
160
|
+
/>
|
|
161
|
+
))}
|
|
162
|
+
</>
|
|
163
|
+
)
|
|
164
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Metadata components barrel export
|
|
10
|
+
*
|
|
11
|
+
* This module exports all metadata rendering components:
|
|
12
|
+
* - OpenGraphMeta: Facebook/LinkedIn Open Graph tags
|
|
13
|
+
* - TwitterCardMeta: Twitter/X card tags
|
|
14
|
+
* - StructuredDataScript: Schema.org JSON-LD
|
|
15
|
+
* - AnalyticsHead: Analytics provider scripts
|
|
16
|
+
* - DnsPrefetchLinks: DNS prefetch link tags
|
|
17
|
+
* - CustomElementsHead: Custom head elements
|
|
18
|
+
* - FaviconLink: Simple favicon link
|
|
19
|
+
* - FaviconSetLinks: Multi-device favicon set
|
|
20
|
+
* - PreloadLinks: Resource preload hints
|
|
21
|
+
*
|
|
22
|
+
* @see Individual component files for detailed documentation
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
export { OpenGraphMeta } from './open-graph-meta'
|
|
26
|
+
export { TwitterCardMeta } from './twitter-card-meta'
|
|
27
|
+
export { StructuredDataScript } from './structured-data'
|
|
28
|
+
export { AnalyticsHead } from './analytics-head'
|
|
29
|
+
export {
|
|
30
|
+
DnsPrefetchLinks,
|
|
31
|
+
CustomElementsHead,
|
|
32
|
+
FaviconLink,
|
|
33
|
+
FaviconSetLinks,
|
|
34
|
+
PreloadLinks,
|
|
35
|
+
} from './head-elements'
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Shared helper to render meta tags with configurable attribute type
|
|
12
|
+
* Eliminates duplication between OpenGraph and Twitter Card rendering
|
|
13
|
+
*
|
|
14
|
+
* @param fields - Array of key-value pairs to render as meta tags
|
|
15
|
+
* @param prefix - Prefix for meta tag name/property (e.g., 'og', 'twitter')
|
|
16
|
+
* @param attributeType - HTML attribute to use ('property' for OG, 'name' for Twitter)
|
|
17
|
+
* @returns React fragment with meta tags
|
|
18
|
+
*/
|
|
19
|
+
export function renderMetaTags({
|
|
20
|
+
fields,
|
|
21
|
+
prefix,
|
|
22
|
+
attributeType,
|
|
23
|
+
}: {
|
|
24
|
+
readonly fields: ReadonlyArray<{ readonly key: string; readonly value?: string | number }>
|
|
25
|
+
readonly prefix: string
|
|
26
|
+
readonly attributeType: 'property' | 'name'
|
|
27
|
+
}): ReactElement {
|
|
28
|
+
return (
|
|
29
|
+
<>
|
|
30
|
+
{fields.map(
|
|
31
|
+
({ key, value }) =>
|
|
32
|
+
value !== undefined && (
|
|
33
|
+
<meta
|
|
34
|
+
key={key}
|
|
35
|
+
{...{ [attributeType]: `${prefix}:${key}` }}
|
|
36
|
+
content={String(value)}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
)}
|
|
40
|
+
</>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
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 { resolveTranslationPattern } from '@/presentation/translations/translation-resolver'
|
|
10
|
+
import { renderMetaTags } from './meta-utils'
|
|
11
|
+
import type { Languages } from '@/domain/models/app/languages'
|
|
12
|
+
import type { OpenGraph } from '@/domain/models/app/page/meta/open-graph'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Render Open Graph metadata tags
|
|
16
|
+
* Generates <meta property="og:*"> tags for Facebook/LinkedIn sharing
|
|
17
|
+
*
|
|
18
|
+
* @param openGraph - Open Graph configuration from page.meta
|
|
19
|
+
* @param lang - Current language code for translation resolution
|
|
20
|
+
* @param languages - Languages configuration for translation resolution
|
|
21
|
+
* @returns React fragment with OG meta tags
|
|
22
|
+
*/
|
|
23
|
+
export function OpenGraphMeta({
|
|
24
|
+
openGraph,
|
|
25
|
+
lang,
|
|
26
|
+
languages,
|
|
27
|
+
}: {
|
|
28
|
+
readonly openGraph?: OpenGraph
|
|
29
|
+
readonly lang?: string
|
|
30
|
+
readonly languages?: Languages
|
|
31
|
+
}): Readonly<ReactElement | undefined> {
|
|
32
|
+
if (!openGraph) {
|
|
33
|
+
return undefined
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Resolve translation patterns in OpenGraph fields
|
|
37
|
+
const resolveValue = (value: string | undefined): string | undefined => {
|
|
38
|
+
if (!value || !lang) return value
|
|
39
|
+
return resolveTranslationPattern(value, lang, languages)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const fields: ReadonlyArray<{ readonly key: string; readonly value?: string }> = [
|
|
43
|
+
{ key: 'title', value: resolveValue(openGraph.title) },
|
|
44
|
+
{ key: 'description', value: resolveValue(openGraph.description) },
|
|
45
|
+
{ key: 'image', value: openGraph.image },
|
|
46
|
+
{ key: 'image:alt', value: resolveValue(openGraph.imageAlt) },
|
|
47
|
+
{ key: 'url', value: openGraph.url },
|
|
48
|
+
{ key: 'type', value: openGraph.type },
|
|
49
|
+
{ key: 'site_name', value: resolveValue(openGraph.siteName) },
|
|
50
|
+
{ key: 'locale', value: openGraph.locale },
|
|
51
|
+
{ key: 'determiner', value: openGraph.determiner },
|
|
52
|
+
{ key: 'video', value: openGraph.video },
|
|
53
|
+
{ key: 'audio', value: openGraph.audio },
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
return renderMetaTags({ fields, prefix: 'og', attributeType: 'property' })
|
|
57
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
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
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Component meta property type for structured data
|
|
12
|
+
*/
|
|
13
|
+
export type ComponentMeta = {
|
|
14
|
+
readonly title?: string
|
|
15
|
+
readonly description?: string
|
|
16
|
+
readonly image?: string
|
|
17
|
+
readonly price?: string
|
|
18
|
+
readonly currency?: string
|
|
19
|
+
readonly structuredData?: {
|
|
20
|
+
readonly type: string
|
|
21
|
+
readonly fields: readonly string[]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates an offers object for Schema.org
|
|
27
|
+
*
|
|
28
|
+
* @param price - Product price
|
|
29
|
+
* @param currency - Currency code
|
|
30
|
+
* @returns Offers object or empty object
|
|
31
|
+
*/
|
|
32
|
+
function createOffer(price?: string, currency?: string): Record<string, unknown> {
|
|
33
|
+
return price && currency
|
|
34
|
+
? {
|
|
35
|
+
offers: {
|
|
36
|
+
'@type': 'Offer',
|
|
37
|
+
price,
|
|
38
|
+
priceCurrency: currency,
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
: {}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Maps component meta field to Schema.org property
|
|
46
|
+
*
|
|
47
|
+
* @param fieldName - Schema.org field name
|
|
48
|
+
* @param meta - Component meta configuration
|
|
49
|
+
* @param fields - Enabled fields list
|
|
50
|
+
* @returns Field object or empty object
|
|
51
|
+
*/
|
|
52
|
+
function mapMetaField(
|
|
53
|
+
fieldName: string,
|
|
54
|
+
meta: ComponentMeta,
|
|
55
|
+
fields: readonly string[]
|
|
56
|
+
): Record<string, unknown> {
|
|
57
|
+
if (!fields.includes(fieldName)) return {}
|
|
58
|
+
|
|
59
|
+
const { title, description, image, price, currency } = meta
|
|
60
|
+
|
|
61
|
+
if (fieldName === 'name' && title) return { name: title }
|
|
62
|
+
if (fieldName === 'description' && description) return { description }
|
|
63
|
+
if (fieldName === 'image' && image) return { image }
|
|
64
|
+
if (fieldName === 'offers') return createOffer(price, currency)
|
|
65
|
+
|
|
66
|
+
return {}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Generates Schema.org structured data JSON-LD from component meta
|
|
71
|
+
*
|
|
72
|
+
* @param meta - Component meta configuration
|
|
73
|
+
* @returns JSON-LD object following Schema.org format
|
|
74
|
+
*/
|
|
75
|
+
function generateStructuredData(meta: ComponentMeta): Record<string, unknown> {
|
|
76
|
+
const { structuredData } = meta
|
|
77
|
+
|
|
78
|
+
if (!structuredData) {
|
|
79
|
+
return {}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Build JSON-LD immutably using functional composition
|
|
83
|
+
const baseJsonLd = {
|
|
84
|
+
'@context': 'https://schema.org',
|
|
85
|
+
'@type': structuredData.type,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const mappedFields = ['name', 'description', 'image', 'offers'].map((field) =>
|
|
89
|
+
mapMetaField(field, meta, structuredData.fields)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return mappedFields.reduce((acc, field) => ({ ...acc, ...field }), baseJsonLd)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Renders structured data script from component meta
|
|
97
|
+
*
|
|
98
|
+
* SECURITY: Safe use of dangerouslySetInnerHTML
|
|
99
|
+
* - Content: Schema.org JSON-LD from component metadata (JSON.stringify)
|
|
100
|
+
* - Source: Validated component meta configuration (component.meta.structuredData)
|
|
101
|
+
* - Risk: None - JSON data cannot execute as code
|
|
102
|
+
* - Validation: Schema validation ensures correct structure
|
|
103
|
+
* - Purpose: Generate rich search results from component content (SEO)
|
|
104
|
+
* - XSS Protection: type="application/ld+json" prevents script execution
|
|
105
|
+
* - Format: Safe serialization via JSON.stringify with formatting
|
|
106
|
+
*
|
|
107
|
+
* @param props - Component props
|
|
108
|
+
* @param props.meta - Component meta configuration
|
|
109
|
+
* @returns Script element with JSON-LD structured data, or null if no structured data
|
|
110
|
+
*/
|
|
111
|
+
export function StructuredDataFromComponent({
|
|
112
|
+
meta,
|
|
113
|
+
}: {
|
|
114
|
+
readonly meta: ComponentMeta | undefined
|
|
115
|
+
}): ReactElement | undefined {
|
|
116
|
+
if (!meta?.structuredData) {
|
|
117
|
+
return undefined
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const jsonLd = generateStructuredData(meta)
|
|
121
|
+
|
|
122
|
+
if (Object.keys(jsonLd).length === 0) {
|
|
123
|
+
return undefined
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<script
|
|
128
|
+
type="application/ld+json"
|
|
129
|
+
dangerouslySetInnerHTML={{
|
|
130
|
+
__html: JSON.stringify(jsonLd, undefined, 2),
|
|
131
|
+
}}
|
|
132
|
+
/>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
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 type { Page } from '@/domain/models/app/pages'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Render structured data as JSON-LD script tags
|
|
13
|
+
* Generates Schema.org structured data for rich search results
|
|
14
|
+
* Supports both 'schema' (canonical) and 'structuredData' (test alias)
|
|
15
|
+
*
|
|
16
|
+
* Handles two formats:
|
|
17
|
+
* 1. Direct Schema.org object: { "@context": "...", "@type": "...", ... }
|
|
18
|
+
* 2. Orchestrator schema: { organization: {...}, faqPage: {...}, ... }
|
|
19
|
+
*
|
|
20
|
+
* Each structured data type is rendered as a separate <script type="application/ld+json">
|
|
21
|
+
* tag for proper Schema.org validation
|
|
22
|
+
*
|
|
23
|
+
* SECURITY: Safe use of dangerouslySetInnerHTML
|
|
24
|
+
* - Content: Schema.org structured data (JSON.stringify)
|
|
25
|
+
* - Source: Validated Page schema (page.meta.schema or page.meta.structuredData)
|
|
26
|
+
* - Risk: None - JSON data cannot execute as code
|
|
27
|
+
* - Validation: Schema validation ensures correct structure
|
|
28
|
+
* - Purpose: Generate rich search results (SEO)
|
|
29
|
+
* - XSS Protection: type="application/ld+json" prevents script execution
|
|
30
|
+
* - Format: Safe serialization via JSON.stringify
|
|
31
|
+
*
|
|
32
|
+
* @param page - Page configuration
|
|
33
|
+
* @returns React fragment with script tags or undefined
|
|
34
|
+
*/
|
|
35
|
+
export function StructuredDataScript({
|
|
36
|
+
page,
|
|
37
|
+
}: {
|
|
38
|
+
readonly page: Page
|
|
39
|
+
}): Readonly<ReactElement | undefined> {
|
|
40
|
+
// Support both 'schema' (canonical) and 'structuredData' (test alias)
|
|
41
|
+
const structuredData = page.meta?.schema ?? page.meta?.structuredData
|
|
42
|
+
if (!structuredData) {
|
|
43
|
+
return undefined
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Type guard: ensure structuredData is an object
|
|
47
|
+
if (typeof structuredData !== 'object' || structuredData === null) {
|
|
48
|
+
return undefined
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Check if this is a direct Schema.org object (has @context and @type)
|
|
52
|
+
const isDirectSchemaObject = '@context' in structuredData && '@type' in structuredData
|
|
53
|
+
|
|
54
|
+
// Handle direct Schema.org object format
|
|
55
|
+
if (isDirectSchemaObject) {
|
|
56
|
+
return (
|
|
57
|
+
<script
|
|
58
|
+
type="application/ld+json"
|
|
59
|
+
dangerouslySetInnerHTML={{
|
|
60
|
+
__html: JSON.stringify(structuredData),
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle orchestrator schema format (organization, faqPage, etc.)
|
|
67
|
+
const structuredDataTypes = Object.entries(structuredData).filter(
|
|
68
|
+
([, value]) => value !== undefined && value !== null
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if (structuredDataTypes.length === 0) {
|
|
72
|
+
return undefined
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<>
|
|
77
|
+
{structuredDataTypes.map(([key, value]) => (
|
|
78
|
+
<script
|
|
79
|
+
key={key}
|
|
80
|
+
type="application/ld+json"
|
|
81
|
+
dangerouslySetInnerHTML={{
|
|
82
|
+
__html: JSON.stringify(value),
|
|
83
|
+
}}
|
|
84
|
+
/>
|
|
85
|
+
))}
|
|
86
|
+
</>
|
|
87
|
+
)
|
|
88
|
+
}
|