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,124 @@
|
|
|
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 { buildAccessibilityRole, buildScrollAttributes } from '../html-element-helpers'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Common props for all rendered elements
|
|
13
|
+
*/
|
|
14
|
+
export interface ElementProps {
|
|
15
|
+
readonly [key: string]: unknown
|
|
16
|
+
readonly className?: string
|
|
17
|
+
readonly 'data-component'?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Configuration for renderHTMLElement
|
|
22
|
+
*/
|
|
23
|
+
export type HTMLElementConfig = {
|
|
24
|
+
readonly type:
|
|
25
|
+
| 'div'
|
|
26
|
+
| 'span'
|
|
27
|
+
| 'section'
|
|
28
|
+
| 'header'
|
|
29
|
+
| 'footer'
|
|
30
|
+
| 'main'
|
|
31
|
+
| 'article'
|
|
32
|
+
| 'aside'
|
|
33
|
+
| 'nav'
|
|
34
|
+
readonly props: ElementProps
|
|
35
|
+
readonly content: string | undefined
|
|
36
|
+
readonly children: readonly React.ReactNode[]
|
|
37
|
+
readonly interactions?: unknown
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Renders HTML structural elements (div, span, section, and HTML5 semantic elements)
|
|
42
|
+
*
|
|
43
|
+
* If content starts with '<', it's treated as HTML and rendered via dangerouslySetInnerHTML.
|
|
44
|
+
* Otherwise, content is rendered as plain text.
|
|
45
|
+
*
|
|
46
|
+
* SECURITY NOTE - Trusted Schema Content:
|
|
47
|
+
* HTML content is rendered without sanitization since it comes from trusted
|
|
48
|
+
* schema configuration. For user-generated content, use renderCustomHTML instead.
|
|
49
|
+
*
|
|
50
|
+
* SECURITY: Safe use of dangerouslySetInnerHTML
|
|
51
|
+
* - Content: Schema-defined HTML from page configuration
|
|
52
|
+
* - Source: Validated Page schema (section.content property)
|
|
53
|
+
* - Risk: Low - content is from server configuration, not user input
|
|
54
|
+
* - Validation: Content validated at build/runtime via schema
|
|
55
|
+
* - Purpose: Render rich HTML content in structural elements
|
|
56
|
+
* - XSS Protection: Use renderCustomHTML with DOMPurify for user-generated content
|
|
57
|
+
* - Condition: Only used when content starts with '<' character
|
|
58
|
+
*
|
|
59
|
+
* For section elements, automatically adds role="region" for accessibility best practices,
|
|
60
|
+
* ensuring sections are properly identified in the accessibility tree.
|
|
61
|
+
*
|
|
62
|
+
* Supports scroll interactions via data attributes for IntersectionObserver.
|
|
63
|
+
*/
|
|
64
|
+
export function renderHTMLElement(config: HTMLElementConfig): ReactElement {
|
|
65
|
+
const { type, props, content, children, interactions } = config
|
|
66
|
+
const Element = type
|
|
67
|
+
|
|
68
|
+
// Build element props immutably
|
|
69
|
+
const accessibilityRole = buildAccessibilityRole(type, children.length > 0, !!content, props.role)
|
|
70
|
+
const scrollAttributes = buildScrollAttributes(interactions)
|
|
71
|
+
const elementProps = { ...props, ...accessibilityRole, ...scrollAttributes }
|
|
72
|
+
|
|
73
|
+
// If content looks like HTML (starts with '<'), render as HTML
|
|
74
|
+
// This is safe for schema-defined content but should NOT be used for user input
|
|
75
|
+
if (content?.trim().startsWith('<')) {
|
|
76
|
+
return (
|
|
77
|
+
<Element
|
|
78
|
+
{...elementProps}
|
|
79
|
+
dangerouslySetInnerHTML={{ __html: content }}
|
|
80
|
+
/>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return <Element {...elementProps}>{content || children}</Element>
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Renders heading elements (h1-h6)
|
|
89
|
+
*/
|
|
90
|
+
export function renderHeading(
|
|
91
|
+
level: 1 | 2 | 3 | 4 | 5 | 6,
|
|
92
|
+
props: ElementProps,
|
|
93
|
+
content: string | undefined,
|
|
94
|
+
children: readonly React.ReactNode[]
|
|
95
|
+
): ReactElement {
|
|
96
|
+
const HeadingTag = `h${level}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
|
|
97
|
+
return <HeadingTag {...props}>{content || children}</HeadingTag>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Renders text element with dynamic level
|
|
102
|
+
*
|
|
103
|
+
* The text element supports a 'level' prop to determine the HTML tag.
|
|
104
|
+
* If level is h1-h6, renders as heading.
|
|
105
|
+
* If level is p, renders as paragraph.
|
|
106
|
+
* If level is label, renders as label.
|
|
107
|
+
* Otherwise renders as span to ensure proper ARIA generic role.
|
|
108
|
+
*/
|
|
109
|
+
export function renderTextElement(props: ElementProps, content: string | undefined): ReactElement {
|
|
110
|
+
const { level } = props
|
|
111
|
+
|
|
112
|
+
if (level === 'h1') return <h1 {...props}>{content}</h1>
|
|
113
|
+
if (level === 'h2') return <h2 {...props}>{content}</h2>
|
|
114
|
+
if (level === 'h3') return <h3 {...props}>{content}</h3>
|
|
115
|
+
if (level === 'h4') return <h4 {...props}>{content}</h4>
|
|
116
|
+
if (level === 'h5') return <h5 {...props}>{content}</h5>
|
|
117
|
+
if (level === 'h6') return <h6 {...props}>{content}</h6>
|
|
118
|
+
if (level === 'p') return <p {...props}>{content}</p>
|
|
119
|
+
if (level === 'label') return <label {...props}>{content}</label>
|
|
120
|
+
|
|
121
|
+
// Default to span for inline text to ensure proper ARIA generic role
|
|
122
|
+
// span elements with text content maintain generic role in ARIA tree
|
|
123
|
+
return <span {...props}>{content}</span>
|
|
124
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
* Element renderers barrel export
|
|
10
|
+
*
|
|
11
|
+
* This module exports all element renderer functions organized by category:
|
|
12
|
+
* - HTML element renderers (structural elements, headings, text)
|
|
13
|
+
* - Text content renderers (paragraph, code, pre)
|
|
14
|
+
* - Media renderers (image, video, audio, iframe)
|
|
15
|
+
* - Interactive renderers (button, link, form, input, icon)
|
|
16
|
+
* - Specialized renderers (alert, list, language-switcher)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// HTML element renderers
|
|
20
|
+
export {
|
|
21
|
+
renderHTMLElement,
|
|
22
|
+
renderHeading,
|
|
23
|
+
renderTextElement,
|
|
24
|
+
type ElementProps,
|
|
25
|
+
type HTMLElementConfig,
|
|
26
|
+
} from './html-element-renderer'
|
|
27
|
+
|
|
28
|
+
// Text content renderers
|
|
29
|
+
export { renderParagraph, renderCode, renderPre } from './text-content-renderers'
|
|
30
|
+
|
|
31
|
+
// Media renderers
|
|
32
|
+
export {
|
|
33
|
+
renderImage,
|
|
34
|
+
renderAvatar,
|
|
35
|
+
renderThumbnail,
|
|
36
|
+
renderHeroImage,
|
|
37
|
+
renderVideo,
|
|
38
|
+
renderAudio,
|
|
39
|
+
renderIframe,
|
|
40
|
+
} from './media-renderers'
|
|
41
|
+
|
|
42
|
+
// Interactive renderers
|
|
43
|
+
export {
|
|
44
|
+
renderButton,
|
|
45
|
+
renderLink,
|
|
46
|
+
renderForm,
|
|
47
|
+
renderInput,
|
|
48
|
+
renderIcon,
|
|
49
|
+
renderCustomHTML,
|
|
50
|
+
} from './interactive-renderers'
|
|
51
|
+
|
|
52
|
+
// Re-export specialized renderers for backward compatibility
|
|
53
|
+
export {
|
|
54
|
+
renderLanguageSwitcher,
|
|
55
|
+
renderAlert,
|
|
56
|
+
renderList,
|
|
57
|
+
renderUnorderedList,
|
|
58
|
+
renderListItem,
|
|
59
|
+
} from '../specialized-renderers'
|
|
@@ -0,0 +1,231 @@
|
|
|
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 DOMPurify from 'dompurify'
|
|
9
|
+
import * as LucideIcons from 'lucide-react'
|
|
10
|
+
import { type ReactElement } from 'react'
|
|
11
|
+
import type { ElementProps } from './html-element-renderer'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Build data attributes for click interactions
|
|
15
|
+
*/
|
|
16
|
+
function buildClickDataAttributes(clickInteraction: {
|
|
17
|
+
animation?: string
|
|
18
|
+
navigate?: string
|
|
19
|
+
openUrl?: string
|
|
20
|
+
openInNewTab?: boolean
|
|
21
|
+
scrollTo?: string
|
|
22
|
+
toggleElement?: string
|
|
23
|
+
submitForm?: string
|
|
24
|
+
}): Record<string, string> {
|
|
25
|
+
return {
|
|
26
|
+
...(clickInteraction.animation && { 'data-click-animation': clickInteraction.animation }),
|
|
27
|
+
...(clickInteraction.navigate && { 'data-click-navigate': clickInteraction.navigate }),
|
|
28
|
+
...(clickInteraction.openUrl && { 'data-click-open-url': clickInteraction.openUrl }),
|
|
29
|
+
...(clickInteraction.openInNewTab !== undefined && {
|
|
30
|
+
'data-click-open-in-new-tab': String(clickInteraction.openInNewTab),
|
|
31
|
+
}),
|
|
32
|
+
...(clickInteraction.scrollTo && { 'data-click-scroll-to': clickInteraction.scrollTo }),
|
|
33
|
+
...(clickInteraction.toggleElement && {
|
|
34
|
+
'data-click-toggle-element': clickInteraction.toggleElement,
|
|
35
|
+
}),
|
|
36
|
+
...(clickInteraction.submitForm && {
|
|
37
|
+
'data-click-submit-form': clickInteraction.submitForm,
|
|
38
|
+
}),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Renders button element with click interactions
|
|
44
|
+
*/
|
|
45
|
+
export function renderButton(
|
|
46
|
+
props: ElementProps,
|
|
47
|
+
content: string | undefined,
|
|
48
|
+
children: readonly React.ReactNode[],
|
|
49
|
+
interactions?: unknown
|
|
50
|
+
): ReactElement {
|
|
51
|
+
const interactionsTyped = interactions as
|
|
52
|
+
| {
|
|
53
|
+
click?: {
|
|
54
|
+
animation?: string
|
|
55
|
+
navigate?: string
|
|
56
|
+
openUrl?: string
|
|
57
|
+
openInNewTab?: boolean
|
|
58
|
+
scrollTo?: string
|
|
59
|
+
toggleElement?: string
|
|
60
|
+
submitForm?: string
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
| undefined
|
|
64
|
+
const clickInteraction = interactionsTyped?.click
|
|
65
|
+
|
|
66
|
+
// Store interaction data in data attributes for client-side JavaScript handler
|
|
67
|
+
const buttonProps = clickInteraction
|
|
68
|
+
? { ...props, ...buildClickDataAttributes(clickInteraction) }
|
|
69
|
+
: props
|
|
70
|
+
|
|
71
|
+
return <button {...buttonProps}>{content || children}</button>
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Renders link (anchor) element
|
|
76
|
+
*/
|
|
77
|
+
export function renderLink(
|
|
78
|
+
props: ElementProps,
|
|
79
|
+
content: string | undefined,
|
|
80
|
+
children: readonly React.ReactNode[]
|
|
81
|
+
): ReactElement {
|
|
82
|
+
return <a {...props}>{content || children}</a>
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Renders form element
|
|
87
|
+
*/
|
|
88
|
+
export function renderForm(
|
|
89
|
+
props: ElementProps,
|
|
90
|
+
children: readonly React.ReactNode[]
|
|
91
|
+
): ReactElement {
|
|
92
|
+
return <form {...props}>{children}</form>
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Renders input element
|
|
97
|
+
*/
|
|
98
|
+
export function renderInput(props: ElementProps): ReactElement {
|
|
99
|
+
return <input {...props} />
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Converts a kebab-case icon name to PascalCase for Lucide lookup
|
|
104
|
+
* Example: 'check-circle' -> 'CheckCircle', 'arrow-right' -> 'ArrowRight'
|
|
105
|
+
*/
|
|
106
|
+
function kebabToPascalCase(name: string): string {
|
|
107
|
+
return name
|
|
108
|
+
.split('-')
|
|
109
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
110
|
+
.join('')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Resolves a Lucide icon component by kebab-case name.
|
|
115
|
+
* Returns undefined if no matching component is found.
|
|
116
|
+
*
|
|
117
|
+
* Lucide icons use forwardRef, so they are objects (typeof === 'object') in the Bun runtime,
|
|
118
|
+
* not plain functions. We treat both as valid React components.
|
|
119
|
+
*/
|
|
120
|
+
function resolveLucideIcon(
|
|
121
|
+
iconName: string | undefined
|
|
122
|
+
): React.ComponentType<Record<string, unknown>> | undefined {
|
|
123
|
+
if (!iconName) return undefined
|
|
124
|
+
const component = (LucideIcons as Record<string, unknown>)[kebabToPascalCase(iconName)]
|
|
125
|
+
// Accept both function components and forwardRef objects (which are typeof 'object')
|
|
126
|
+
if (typeof component === 'function')
|
|
127
|
+
return component as React.ComponentType<Record<string, unknown>>
|
|
128
|
+
if (typeof component === 'object' && component !== null)
|
|
129
|
+
return component as React.ComponentType<Record<string, unknown>>
|
|
130
|
+
return undefined
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Renders icon using Lucide React icons
|
|
135
|
+
*
|
|
136
|
+
* Props extracted from elementProps:
|
|
137
|
+
* - name: kebab-case Lucide icon name (e.g. 'check-circle', 'arrow-right')
|
|
138
|
+
* - color: SVG stroke color (default: 'currentColor')
|
|
139
|
+
* - size: SVG width/height in pixels (default: 24)
|
|
140
|
+
* - strokeWidth: SVG stroke-width (default: 2)
|
|
141
|
+
* - ariaLabel: accessible label; if provided, sets role="img" instead of aria-hidden
|
|
142
|
+
* - className: CSS classes forwarded to the SVG
|
|
143
|
+
*
|
|
144
|
+
* Falls back to an empty SVG with data-testid for unknown icon names.
|
|
145
|
+
*/
|
|
146
|
+
export function renderIcon(
|
|
147
|
+
props: ElementProps,
|
|
148
|
+
_children: readonly React.ReactNode[]
|
|
149
|
+
): ReactElement {
|
|
150
|
+
const iconName = props.name as string | undefined
|
|
151
|
+
const iconColor = props.color as string | undefined
|
|
152
|
+
const iconSize = props.size as number | undefined
|
|
153
|
+
const iconStrokeWidth = props.strokeWidth as number | undefined
|
|
154
|
+
const ariaLabel = props.ariaLabel as string | undefined
|
|
155
|
+
|
|
156
|
+
// Build props to forward to the SVG — strip out icon-specific props and their data-* conversions
|
|
157
|
+
const {
|
|
158
|
+
name: _name,
|
|
159
|
+
color: _color,
|
|
160
|
+
size: _size,
|
|
161
|
+
strokeWidth: _strokeWidth,
|
|
162
|
+
ariaLabel: _ariaLabel,
|
|
163
|
+
'data-color': _dataColor,
|
|
164
|
+
'data-size': _dataSize,
|
|
165
|
+
'data-stroke-width': _dataStrokeWidth,
|
|
166
|
+
'data-aria-label': _dataAriaLabel,
|
|
167
|
+
...restProps
|
|
168
|
+
} = props as Record<string, unknown>
|
|
169
|
+
|
|
170
|
+
const testId = iconName ? `icon-${iconName}` : 'icon'
|
|
171
|
+
const colorProps = iconColor !== undefined ? { 'data-color': iconColor } : {}
|
|
172
|
+
const a11yProps = ariaLabel
|
|
173
|
+
? { role: 'img' as const, 'aria-label': ariaLabel }
|
|
174
|
+
: { 'aria-hidden': 'true' as const }
|
|
175
|
+
|
|
176
|
+
const LucideIcon = resolveLucideIcon(iconName)
|
|
177
|
+
|
|
178
|
+
if (LucideIcon) {
|
|
179
|
+
return (
|
|
180
|
+
<LucideIcon
|
|
181
|
+
{...restProps}
|
|
182
|
+
{...a11yProps}
|
|
183
|
+
{...colorProps}
|
|
184
|
+
size={iconSize ?? 24}
|
|
185
|
+
color={iconColor ?? 'currentColor'}
|
|
186
|
+
strokeWidth={iconStrokeWidth ?? 2}
|
|
187
|
+
data-testid={testId}
|
|
188
|
+
/>
|
|
189
|
+
) as ReactElement
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Fallback: render empty SVG placeholder for unknown icons
|
|
193
|
+
return (
|
|
194
|
+
<svg
|
|
195
|
+
{...(restProps as React.SVGProps<SVGSVGElement>)}
|
|
196
|
+
{...a11yProps}
|
|
197
|
+
{...colorProps}
|
|
198
|
+
data-testid={testId}
|
|
199
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
200
|
+
width={iconSize ?? 24}
|
|
201
|
+
height={iconSize ?? 24}
|
|
202
|
+
/>
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Renders custom HTML with DOMPurify sanitization
|
|
208
|
+
*
|
|
209
|
+
* SECURITY: This function sanitizes HTML to prevent XSS attacks.
|
|
210
|
+
* DOMPurify removes malicious scripts, event handlers, and dangerous attributes.
|
|
211
|
+
* Critical for user-generated content or external HTML sources.
|
|
212
|
+
*
|
|
213
|
+
* SECURITY: Safe use of dangerouslySetInnerHTML (after sanitization)
|
|
214
|
+
* - Content: User-provided or external HTML (props.html)
|
|
215
|
+
* - Source: Potentially untrusted - user input or external sources
|
|
216
|
+
* - Risk: None (after sanitization) - XSS prevented by DOMPurify
|
|
217
|
+
* - Validation: DOMPurify removes all malicious content
|
|
218
|
+
* - Purpose: Render custom HTML blocks safely
|
|
219
|
+
* - XSS Protection: DOMPurify sanitization removes scripts, event handlers, dangerous tags
|
|
220
|
+
* - Best Practice: This is the ONLY way to render user-generated HTML safely
|
|
221
|
+
*/
|
|
222
|
+
export function renderCustomHTML(props: ElementProps): ReactElement {
|
|
223
|
+
const sanitizedHTML = DOMPurify.sanitize((props.html as string | undefined) || '')
|
|
224
|
+
return (
|
|
225
|
+
<div
|
|
226
|
+
{...props}
|
|
227
|
+
// Safe to use dangerouslySetInnerHTML after DOMPurify sanitization
|
|
228
|
+
dangerouslySetInnerHTML={{ __html: sanitizedHTML }}
|
|
229
|
+
/>
|
|
230
|
+
)
|
|
231
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
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 { ElementProps } from './html-element-renderer'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Renders image element
|
|
13
|
+
*/
|
|
14
|
+
export function renderImage(props: ElementProps): ReactElement {
|
|
15
|
+
return (
|
|
16
|
+
<img
|
|
17
|
+
{...props}
|
|
18
|
+
alt={(props.alt as string | undefined) || ''}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Renders avatar image with circular border-radius
|
|
25
|
+
* Includes default dimensions to ensure visibility even when image fails to load
|
|
26
|
+
*/
|
|
27
|
+
export function renderAvatar(props: ElementProps): ReactElement {
|
|
28
|
+
const style = {
|
|
29
|
+
...((props.style as Record<string, unknown> | undefined) || {}),
|
|
30
|
+
minWidth: '48px',
|
|
31
|
+
minHeight: '48px',
|
|
32
|
+
width: '48px',
|
|
33
|
+
height: '48px',
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<img
|
|
38
|
+
{...props}
|
|
39
|
+
style={style}
|
|
40
|
+
alt={(props.alt as string | undefined) || ''}
|
|
41
|
+
className={[props.className, 'rounded-full'].filter(Boolean).join(' ')}
|
|
42
|
+
/>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Renders thumbnail image with moderate border-radius
|
|
48
|
+
* Applies md radius (0.375rem) for soft corners while preserving aspect ratio
|
|
49
|
+
*/
|
|
50
|
+
export function renderThumbnail(props: ElementProps): ReactElement {
|
|
51
|
+
return (
|
|
52
|
+
<img
|
|
53
|
+
{...props}
|
|
54
|
+
alt={(props.alt as string | undefined) || ''}
|
|
55
|
+
className={[props.className, 'rounded-md'].filter(Boolean).join(' ')}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Renders hero image with top-only border-radius
|
|
62
|
+
* Applies lg radius to top corners only for integration with card layout
|
|
63
|
+
*/
|
|
64
|
+
export function renderHeroImage(props: ElementProps): ReactElement {
|
|
65
|
+
return (
|
|
66
|
+
<img
|
|
67
|
+
{...props}
|
|
68
|
+
alt={(props.alt as string | undefined) || ''}
|
|
69
|
+
className={[props.className, 'rounded-t-lg'].filter(Boolean).join(' ')}
|
|
70
|
+
/>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Renders video element
|
|
76
|
+
*/
|
|
77
|
+
export function renderVideo(
|
|
78
|
+
props: ElementProps,
|
|
79
|
+
children: readonly React.ReactNode[]
|
|
80
|
+
): ReactElement {
|
|
81
|
+
return <video {...props}>{children}</video>
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Renders audio element
|
|
86
|
+
*/
|
|
87
|
+
export function renderAudio(
|
|
88
|
+
props: ElementProps,
|
|
89
|
+
children: readonly React.ReactNode[]
|
|
90
|
+
): ReactElement {
|
|
91
|
+
return <audio {...props}>{children}</audio>
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Renders iframe element
|
|
96
|
+
*/
|
|
97
|
+
export function renderIframe(
|
|
98
|
+
props: ElementProps,
|
|
99
|
+
children: readonly React.ReactNode[]
|
|
100
|
+
): ReactElement {
|
|
101
|
+
return <iframe {...props}>{children}</iframe>
|
|
102
|
+
}
|
|
@@ -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
|
+
import type { ElementProps } from './html-element-renderer'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Renders paragraph element
|
|
13
|
+
*/
|
|
14
|
+
export function renderParagraph(
|
|
15
|
+
props: ElementProps,
|
|
16
|
+
content: string | undefined,
|
|
17
|
+
children: readonly React.ReactNode[]
|
|
18
|
+
): ReactElement {
|
|
19
|
+
return <p {...props}>{content || children}</p>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Renders code element (inline code)
|
|
24
|
+
*/
|
|
25
|
+
export function renderCode(
|
|
26
|
+
props: ElementProps,
|
|
27
|
+
content: string | undefined,
|
|
28
|
+
children: readonly React.ReactNode[]
|
|
29
|
+
): ReactElement {
|
|
30
|
+
return <code {...props}>{content || children}</code>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Renders pre element (preformatted text block)
|
|
35
|
+
*/
|
|
36
|
+
export function renderPre(
|
|
37
|
+
props: ElementProps,
|
|
38
|
+
content: string | undefined,
|
|
39
|
+
children: readonly React.ReactNode[]
|
|
40
|
+
): ReactElement {
|
|
41
|
+
return <pre {...props}>{content || children}</pre>
|
|
42
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Re-export from element-renderers directory
|
|
10
|
+
*
|
|
11
|
+
* This file maintains backward compatibility with existing imports.
|
|
12
|
+
* The actual implementation has been split into:
|
|
13
|
+
* - html-element-renderer.tsx (renderHTMLElement, renderHeading, renderTextElement)
|
|
14
|
+
* - text-content-renderers.tsx (renderParagraph, renderCode, renderPre)
|
|
15
|
+
* - media-renderers.tsx (renderImage, renderVideo, renderAudio, etc.)
|
|
16
|
+
* - interactive-renderers.tsx (renderButton, renderLink, renderForm, etc.)
|
|
17
|
+
*
|
|
18
|
+
* @see src/presentation/ui/sections/renderers/element-renderers/
|
|
19
|
+
*/
|
|
20
|
+
export {
|
|
21
|
+
// Types
|
|
22
|
+
type ElementProps,
|
|
23
|
+
type HTMLElementConfig,
|
|
24
|
+
// HTML element renderers
|
|
25
|
+
renderHTMLElement,
|
|
26
|
+
renderHeading,
|
|
27
|
+
renderTextElement,
|
|
28
|
+
// Text content renderers
|
|
29
|
+
renderParagraph,
|
|
30
|
+
renderCode,
|
|
31
|
+
renderPre,
|
|
32
|
+
// Media renderers
|
|
33
|
+
renderImage,
|
|
34
|
+
renderAvatar,
|
|
35
|
+
renderThumbnail,
|
|
36
|
+
renderHeroImage,
|
|
37
|
+
renderVideo,
|
|
38
|
+
renderAudio,
|
|
39
|
+
renderIframe,
|
|
40
|
+
// Interactive renderers
|
|
41
|
+
renderButton,
|
|
42
|
+
renderLink,
|
|
43
|
+
renderForm,
|
|
44
|
+
renderInput,
|
|
45
|
+
renderIcon,
|
|
46
|
+
renderCustomHTML,
|
|
47
|
+
// Specialized renderers
|
|
48
|
+
renderLanguageSwitcher,
|
|
49
|
+
renderAlert,
|
|
50
|
+
renderList,
|
|
51
|
+
renderUnorderedList,
|
|
52
|
+
renderListItem,
|
|
53
|
+
} from './element-renderers/'
|