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,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { renderToString } from 'react-dom/server'
|
|
9
|
+
import { findMatchingRoute } from '@/domain/utils/route-matcher'
|
|
10
|
+
import { DefaultHomePage } from '@/presentation/ui/pages/DefaultHomePage'
|
|
11
|
+
import { DynamicPage } from '@/presentation/ui/pages/DynamicPage'
|
|
12
|
+
import type { App } from '@/domain/models/app'
|
|
13
|
+
import type { BuiltInAnalytics } from '@/domain/models/app/analytics'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Extract session timeout from analytics config, defaulting to 30 minutes
|
|
17
|
+
*/
|
|
18
|
+
function extractSessionTimeout(analytics: BuiltInAnalytics | undefined): number {
|
|
19
|
+
if (analytics === undefined || analytics === false || analytics === true) return 30
|
|
20
|
+
return analytics.sessionTimeout ?? 30
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Check if built-in analytics tracking should be injected for a given page path
|
|
25
|
+
*
|
|
26
|
+
* Returns true when analytics is configured and enabled, and the page path
|
|
27
|
+
* is not in the excludedPaths list.
|
|
28
|
+
*/
|
|
29
|
+
function shouldInjectAnalytics(analytics: BuiltInAnalytics | undefined, pagePath: string): boolean {
|
|
30
|
+
if (analytics === undefined || analytics === false) return false
|
|
31
|
+
if (analytics === true) return true
|
|
32
|
+
const { excludedPaths } = analytics
|
|
33
|
+
if (!excludedPaths || excludedPaths.length === 0) return true
|
|
34
|
+
return !excludedPaths.some((pattern: string) => {
|
|
35
|
+
// Support simple glob patterns: * matches any segment, ** matches anything
|
|
36
|
+
const regex = new RegExp('^' + pattern.replace(/\*\*/g, '.*').replace(/\*/g, '[^/]*') + '$')
|
|
37
|
+
return regex.test(pagePath)
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Renders a page by path to HTML string for server-side rendering
|
|
43
|
+
*
|
|
44
|
+
* Supports both static routes (exact match) and dynamic routes (with :param segments)
|
|
45
|
+
*
|
|
46
|
+
* @param app - Validated application data from AppSchema
|
|
47
|
+
* @param path - Page path to render (e.g., '/', '/about', '/blog/hello-world')
|
|
48
|
+
* @param detectedLanguage - Optional detected language from Accept-Language header or URL
|
|
49
|
+
* @returns Complete HTML document as string with DOCTYPE, or undefined if page not found
|
|
50
|
+
*/
|
|
51
|
+
export function renderPageByPath(
|
|
52
|
+
app: App,
|
|
53
|
+
path: string,
|
|
54
|
+
detectedLanguage?: string
|
|
55
|
+
): string | undefined {
|
|
56
|
+
// If no pages configured, return undefined
|
|
57
|
+
if (!app.pages || app.pages.length === 0) {
|
|
58
|
+
return undefined
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Extract all page patterns and find matching route
|
|
62
|
+
const pagePatterns = app.pages.map((p) => p.path)
|
|
63
|
+
const match = findMatchingRoute(pagePatterns, path)
|
|
64
|
+
|
|
65
|
+
if (!match) {
|
|
66
|
+
return undefined
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Get the matched page
|
|
70
|
+
const page = app.pages[match.index]
|
|
71
|
+
if (!page) {
|
|
72
|
+
return undefined
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const injectAnalytics = shouldInjectAnalytics(app.analytics, page.path)
|
|
76
|
+
const sessionTimeout = extractSessionTimeout(app.analytics)
|
|
77
|
+
|
|
78
|
+
const html = renderToString(
|
|
79
|
+
<DynamicPage
|
|
80
|
+
page={page}
|
|
81
|
+
components={app.components}
|
|
82
|
+
theme={app.theme}
|
|
83
|
+
languages={app.languages}
|
|
84
|
+
detectedLanguage={detectedLanguage}
|
|
85
|
+
routeParams={match.params}
|
|
86
|
+
builtInAnalyticsEnabled={injectAnalytics}
|
|
87
|
+
builtInAnalyticsSessionTimeout={sessionTimeout}
|
|
88
|
+
/>
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return `<!DOCTYPE html>\n${html}`
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Renders homepage to HTML string for server-side rendering
|
|
96
|
+
*
|
|
97
|
+
* If the app has custom pages configured, renders the page with path '/'
|
|
98
|
+
* Otherwise, renders the default homepage
|
|
99
|
+
*
|
|
100
|
+
* @param app - Validated application data from AppSchema
|
|
101
|
+
* @param detectedLanguage - Optional detected language from Accept-Language header
|
|
102
|
+
* @returns Complete HTML document as string with DOCTYPE
|
|
103
|
+
*/
|
|
104
|
+
// @knip-ignore - Used via dynamic import in StartServer.ts
|
|
105
|
+
export function renderHomePage(app: App, detectedLanguage?: string): string {
|
|
106
|
+
// Try to render custom homepage first
|
|
107
|
+
const customHomePage = renderPageByPath(app, '/', detectedLanguage)
|
|
108
|
+
if (customHomePage) {
|
|
109
|
+
return customHomePage
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Fallback to default homepage
|
|
113
|
+
// Check if analytics should be enabled for homepage (path '/')
|
|
114
|
+
const injectAnalytics = shouldInjectAnalytics(app.analytics, '/')
|
|
115
|
+
const defaultSessionTimeout = extractSessionTimeout(app.analytics)
|
|
116
|
+
const html = renderToString(
|
|
117
|
+
<DefaultHomePage
|
|
118
|
+
app={app}
|
|
119
|
+
builtInAnalyticsEnabled={injectAnalytics}
|
|
120
|
+
builtInAnalyticsSessionTimeout={defaultSessionTimeout}
|
|
121
|
+
/>
|
|
122
|
+
)
|
|
123
|
+
return `<!DOCTYPE html>\n${html}`
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Renders any page by path to HTML string for server-side rendering
|
|
128
|
+
*
|
|
129
|
+
* @param app - Validated application data from AppSchema
|
|
130
|
+
* @param path - Page path to render (e.g., '/', '/about')
|
|
131
|
+
* @param detectedLanguage - Optional detected language from Accept-Language header
|
|
132
|
+
* @returns Complete HTML document as string with DOCTYPE, or undefined if page not found
|
|
133
|
+
*/
|
|
134
|
+
// @knip-ignore - Used via dynamic import in server.ts
|
|
135
|
+
export function renderPage(app: App, path: string, detectedLanguage?: string): string | undefined {
|
|
136
|
+
return renderPageByPath(app, path, detectedLanguage)
|
|
137
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
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 React, { type ReactElement } from 'react'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Render a script tag with optional attributes
|
|
12
|
+
* Unified helper for rendering external scripts (analytics, external scripts, etc.)
|
|
13
|
+
*/
|
|
14
|
+
export function renderScriptTag({
|
|
15
|
+
src,
|
|
16
|
+
async: asyncProp,
|
|
17
|
+
defer,
|
|
18
|
+
module,
|
|
19
|
+
integrity,
|
|
20
|
+
crossOrigin,
|
|
21
|
+
dataTestId,
|
|
22
|
+
reactKey,
|
|
23
|
+
hidden,
|
|
24
|
+
}: {
|
|
25
|
+
readonly src: string
|
|
26
|
+
readonly async?: boolean
|
|
27
|
+
readonly defer?: boolean
|
|
28
|
+
readonly module?: boolean
|
|
29
|
+
readonly integrity?: string
|
|
30
|
+
readonly crossOrigin?: 'anonymous' | 'use-credentials'
|
|
31
|
+
readonly dataTestId?: string
|
|
32
|
+
readonly reactKey: string | number
|
|
33
|
+
readonly hidden?: boolean
|
|
34
|
+
}): Readonly<ReactElement> {
|
|
35
|
+
const props: Record<string, unknown> = {
|
|
36
|
+
key: reactKey,
|
|
37
|
+
src,
|
|
38
|
+
...(asyncProp && { async: true }),
|
|
39
|
+
...(defer && { defer: true }),
|
|
40
|
+
...(module && { type: 'module' }),
|
|
41
|
+
...(integrity && { integrity }),
|
|
42
|
+
...(crossOrigin && { crossOrigin }),
|
|
43
|
+
...(dataTestId && { 'data-testid': dataTestId }),
|
|
44
|
+
...(hidden && { style: { display: 'none' } }),
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return React.createElement('script', props)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Render an inline script tag with JavaScript code
|
|
52
|
+
* Wraps code in async IIFE if async property is true
|
|
53
|
+
*
|
|
54
|
+
* SECURITY: Safe use of dangerouslySetInnerHTML
|
|
55
|
+
* - Content: Inline JavaScript code from page configuration
|
|
56
|
+
* - Source: Validated InlineScripts schema (page.scripts.inlineScripts[].code)
|
|
57
|
+
* - Risk: Low - content is from server configuration, not user input
|
|
58
|
+
* - Validation: Schema validation ensures string type
|
|
59
|
+
* - Purpose: Render inline scripts for page-specific functionality
|
|
60
|
+
* - CSP: Inline script - consider using nonce for stricter CSP
|
|
61
|
+
* - Transformation: Optionally wraps in async IIFE for async execution
|
|
62
|
+
*/
|
|
63
|
+
export function renderInlineScriptTag({
|
|
64
|
+
code,
|
|
65
|
+
async: asyncProp,
|
|
66
|
+
reactKey,
|
|
67
|
+
}: {
|
|
68
|
+
readonly code: string
|
|
69
|
+
readonly async?: boolean
|
|
70
|
+
readonly reactKey: string | number
|
|
71
|
+
}): Readonly<ReactElement> {
|
|
72
|
+
const scriptContent = asyncProp ? `(async () => { ${code} })();` : code
|
|
73
|
+
|
|
74
|
+
return React.createElement('script', {
|
|
75
|
+
key: reactKey,
|
|
76
|
+
dangerouslySetInnerHTML: { __html: scriptContent },
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Renders an inline script tag that exposes configuration data to window object
|
|
82
|
+
* Merges with existing window property if it already exists
|
|
83
|
+
*
|
|
84
|
+
* SECURITY: Safe use of dangerouslySetInnerHTML
|
|
85
|
+
* - Content: Build-time generated configuration data (JSON.stringify)
|
|
86
|
+
* - Source: Validated schema from app/page configuration
|
|
87
|
+
* - Risk: None - no user input, server-controlled data only
|
|
88
|
+
* - Purpose: Expose configuration for client-side JavaScript access
|
|
89
|
+
* - CSP: Compatible - inline script with deterministic content
|
|
90
|
+
* - Note: Only public configuration (no secrets)
|
|
91
|
+
*
|
|
92
|
+
* @param windowKey - Name of the window property (e.g., 'APP_CONFIG', 'APP_LANGUAGES')
|
|
93
|
+
* @param data - Configuration object to expose
|
|
94
|
+
* @param reactKey - Unique React key for the script element
|
|
95
|
+
* @returns React script element with inline configuration
|
|
96
|
+
*/
|
|
97
|
+
export function renderWindowConfig({
|
|
98
|
+
windowKey,
|
|
99
|
+
data,
|
|
100
|
+
reactKey,
|
|
101
|
+
}: {
|
|
102
|
+
readonly windowKey: string
|
|
103
|
+
readonly data: unknown
|
|
104
|
+
readonly reactKey: string | number
|
|
105
|
+
}): Readonly<ReactElement> {
|
|
106
|
+
return React.createElement('script', {
|
|
107
|
+
key: reactKey,
|
|
108
|
+
dangerouslySetInnerHTML: {
|
|
109
|
+
__html: `window.${windowKey} = Object.assign({}, window.${windowKey} || {}, ${JSON.stringify(data)});`,
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
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 { toKebabCase } from '@/presentation/utils/string-utils'
|
|
9
|
+
import type { Theme } from '@/domain/models/app/theme'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Additional style properties to apply when composing animation
|
|
13
|
+
*/
|
|
14
|
+
interface AnimationStyleOptions {
|
|
15
|
+
readonly animationPlayState?: string
|
|
16
|
+
readonly animationFillMode?: string
|
|
17
|
+
readonly opacity?: number
|
|
18
|
+
readonly infinite?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Configuration for composing animations
|
|
23
|
+
*/
|
|
24
|
+
interface AnimationComposerConfig {
|
|
25
|
+
readonly baseStyle?: Record<string, unknown>
|
|
26
|
+
readonly componentType?: string
|
|
27
|
+
readonly animationName: string
|
|
28
|
+
readonly theme?: Theme
|
|
29
|
+
readonly defaultDuration: string
|
|
30
|
+
readonly defaultEasing: string
|
|
31
|
+
readonly options?: AnimationStyleOptions
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Animation timing configuration
|
|
36
|
+
*/
|
|
37
|
+
type AnimationTiming = {
|
|
38
|
+
readonly duration: string
|
|
39
|
+
readonly easing: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Extract animation timing from config
|
|
44
|
+
*/
|
|
45
|
+
function extractAnimationTiming(
|
|
46
|
+
animationConfig: unknown,
|
|
47
|
+
defaultDuration: string,
|
|
48
|
+
defaultEasing: string
|
|
49
|
+
): AnimationTiming {
|
|
50
|
+
const duration =
|
|
51
|
+
typeof animationConfig === 'object' && animationConfig !== null && 'duration' in animationConfig
|
|
52
|
+
? (animationConfig.duration as string)
|
|
53
|
+
: defaultDuration
|
|
54
|
+
|
|
55
|
+
const easing =
|
|
56
|
+
typeof animationConfig === 'object' && animationConfig !== null && 'easing' in animationConfig
|
|
57
|
+
? (animationConfig.easing as string)
|
|
58
|
+
: defaultEasing
|
|
59
|
+
|
|
60
|
+
return { duration, easing }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Build animation style object
|
|
65
|
+
*/
|
|
66
|
+
function buildAnimationStyle(
|
|
67
|
+
baseStyle: Record<string, unknown> | undefined,
|
|
68
|
+
animationValue: string,
|
|
69
|
+
options?: AnimationStyleOptions
|
|
70
|
+
): Record<string, unknown> {
|
|
71
|
+
return {
|
|
72
|
+
...baseStyle,
|
|
73
|
+
animation: animationValue,
|
|
74
|
+
...(options?.animationPlayState && { animationPlayState: options.animationPlayState }),
|
|
75
|
+
...(options?.animationFillMode && { animationFillMode: options.animationFillMode }),
|
|
76
|
+
...(options?.opacity !== undefined && { opacity: options.opacity }),
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Compose animation style for a given component type
|
|
82
|
+
*
|
|
83
|
+
* Extracts animation configuration from theme and composes it with base style.
|
|
84
|
+
* Follows functional programming principles: pure function, immutable operations.
|
|
85
|
+
*
|
|
86
|
+
* @param config - Configuration object for animation composition
|
|
87
|
+
* @returns New style object with animation composed
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* const style = composeAnimation({
|
|
92
|
+
* baseStyle,
|
|
93
|
+
* componentType: 'toast',
|
|
94
|
+
* animationName: 'fadeOut',
|
|
95
|
+
* theme,
|
|
96
|
+
* defaultDuration: '300ms',
|
|
97
|
+
* defaultEasing: 'ease-out'
|
|
98
|
+
* })
|
|
99
|
+
* // Returns: { ...baseStyle, animation: 'fade-out 300ms ease-out' }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export function composeAnimation(config: AnimationComposerConfig): Record<string, unknown> {
|
|
103
|
+
const animationConfig = config.theme?.animations?.[config.animationName]
|
|
104
|
+
if (!animationConfig) {
|
|
105
|
+
return config.baseStyle || {}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const { duration, easing } = extractAnimationTiming(
|
|
109
|
+
animationConfig,
|
|
110
|
+
config.defaultDuration,
|
|
111
|
+
config.defaultEasing
|
|
112
|
+
)
|
|
113
|
+
const infiniteSuffix = config.options?.infinite ? ' infinite' : ''
|
|
114
|
+
const animationValue = `${toKebabCase(config.animationName)} ${duration} ${easing}${infiniteSuffix}`
|
|
115
|
+
|
|
116
|
+
return buildAnimationStyle(config.baseStyle, animationValue, config.options)
|
|
117
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
// Utility functions
|
|
9
|
+
export { parseStyle, normalizeStyleAnimations } from './parse-style'
|
|
10
|
+
export { isCssValue, isTailwindClass } from './style-utils'
|
|
11
|
+
|
|
12
|
+
// Animation composition
|
|
13
|
+
export { composeAnimation } from './animation-composer'
|
|
@@ -0,0 +1,243 @@
|
|
|
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 { toKebabCase } from '@/presentation/utils/string-utils'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Normalize animation names in CSS value to kebab-case
|
|
12
|
+
*
|
|
13
|
+
* Converts animation names (first token) from camelCase to kebab-case
|
|
14
|
+
* while preserving other CSS values (duration, easing, etc.)
|
|
15
|
+
*
|
|
16
|
+
* @param animationValue - CSS animation value (e.g., "fadeIn 1s ease-in-out")
|
|
17
|
+
* @returns Normalized animation value (e.g., "fade-in 1s ease-in-out")
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* normalizeAnimationValue('fadeIn 1s ease-in-out') // 'fade-in 1s ease-in-out'
|
|
22
|
+
* normalizeAnimationValue('slideInUp 0.5s') // 'slide-in-up 0.5s'
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
function normalizeAnimationValue(animationValue: string): string {
|
|
26
|
+
const parts = animationValue.trim().split(/\s+/)
|
|
27
|
+
if (parts.length === 0) return animationValue
|
|
28
|
+
|
|
29
|
+
// First part is the animation name, convert to kebab-case
|
|
30
|
+
const animationName = parts[0]
|
|
31
|
+
if (!animationName) return animationValue
|
|
32
|
+
const normalizedName = toKebabCase(animationName)
|
|
33
|
+
|
|
34
|
+
// Rejoin with other parts (duration, easing, etc.)
|
|
35
|
+
return [normalizedName, ...parts.slice(1)].join(' ')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Common CSS properties in camelCase (used to identify CSS props in component props)
|
|
40
|
+
* This is not exhaustive but covers common use cases
|
|
41
|
+
*/
|
|
42
|
+
const CSS_PROPERTIES = new Set([
|
|
43
|
+
'alignContent',
|
|
44
|
+
'alignItems',
|
|
45
|
+
'alignSelf',
|
|
46
|
+
'animation',
|
|
47
|
+
'animationDelay',
|
|
48
|
+
'animationDirection',
|
|
49
|
+
'animationDuration',
|
|
50
|
+
'animationFillMode',
|
|
51
|
+
'animationIterationCount',
|
|
52
|
+
'animationName',
|
|
53
|
+
'animationPlayState',
|
|
54
|
+
'animationTimingFunction',
|
|
55
|
+
'background',
|
|
56
|
+
'backgroundColor',
|
|
57
|
+
'backgroundImage',
|
|
58
|
+
'backgroundPosition',
|
|
59
|
+
'backgroundRepeat',
|
|
60
|
+
'backgroundSize',
|
|
61
|
+
'border',
|
|
62
|
+
'borderBottom',
|
|
63
|
+
'borderColor',
|
|
64
|
+
'borderLeft',
|
|
65
|
+
'borderRadius',
|
|
66
|
+
'borderRight',
|
|
67
|
+
'borderStyle',
|
|
68
|
+
'borderTop',
|
|
69
|
+
'borderWidth',
|
|
70
|
+
'bottom',
|
|
71
|
+
'boxShadow',
|
|
72
|
+
'color',
|
|
73
|
+
'cursor',
|
|
74
|
+
'display',
|
|
75
|
+
'flex',
|
|
76
|
+
'flexBasis',
|
|
77
|
+
'flexDirection',
|
|
78
|
+
'flexGrow',
|
|
79
|
+
'flexShrink',
|
|
80
|
+
'flexWrap',
|
|
81
|
+
'font',
|
|
82
|
+
'fontFamily',
|
|
83
|
+
'fontSize',
|
|
84
|
+
'fontStyle',
|
|
85
|
+
'fontWeight',
|
|
86
|
+
'gap',
|
|
87
|
+
'grid',
|
|
88
|
+
'gridArea',
|
|
89
|
+
'gridAutoColumns',
|
|
90
|
+
'gridAutoFlow',
|
|
91
|
+
'gridAutoRows',
|
|
92
|
+
'gridColumn',
|
|
93
|
+
'gridColumnEnd',
|
|
94
|
+
'gridColumnGap',
|
|
95
|
+
'gridColumnStart',
|
|
96
|
+
'gridGap',
|
|
97
|
+
'gridRow',
|
|
98
|
+
'gridRowEnd',
|
|
99
|
+
'gridRowGap',
|
|
100
|
+
'gridRowStart',
|
|
101
|
+
'gridTemplate',
|
|
102
|
+
'gridTemplateAreas',
|
|
103
|
+
'gridTemplateColumns',
|
|
104
|
+
'gridTemplateRows',
|
|
105
|
+
'height',
|
|
106
|
+
'justifyContent',
|
|
107
|
+
'justifyItems',
|
|
108
|
+
'justifySelf',
|
|
109
|
+
'left',
|
|
110
|
+
'letterSpacing',
|
|
111
|
+
'lineHeight',
|
|
112
|
+
'listStyle',
|
|
113
|
+
'listStyleImage',
|
|
114
|
+
'listStylePosition',
|
|
115
|
+
'listStyleType',
|
|
116
|
+
'margin',
|
|
117
|
+
'marginBottom',
|
|
118
|
+
'marginLeft',
|
|
119
|
+
'marginRight',
|
|
120
|
+
'marginTop',
|
|
121
|
+
'maxHeight',
|
|
122
|
+
'maxWidth',
|
|
123
|
+
'minHeight',
|
|
124
|
+
'minWidth',
|
|
125
|
+
'objectFit',
|
|
126
|
+
'objectPosition',
|
|
127
|
+
'opacity',
|
|
128
|
+
'order',
|
|
129
|
+
'outline',
|
|
130
|
+
'outlineColor',
|
|
131
|
+
'outlineOffset',
|
|
132
|
+
'outlineStyle',
|
|
133
|
+
'outlineWidth',
|
|
134
|
+
'overflow',
|
|
135
|
+
'overflowX',
|
|
136
|
+
'overflowY',
|
|
137
|
+
'padding',
|
|
138
|
+
'paddingBottom',
|
|
139
|
+
'paddingLeft',
|
|
140
|
+
'paddingRight',
|
|
141
|
+
'paddingTop',
|
|
142
|
+
'position',
|
|
143
|
+
'right',
|
|
144
|
+
'textAlign',
|
|
145
|
+
'textDecoration',
|
|
146
|
+
'textOverflow',
|
|
147
|
+
'textShadow',
|
|
148
|
+
'textTransform',
|
|
149
|
+
'top',
|
|
150
|
+
'transform',
|
|
151
|
+
'transformOrigin',
|
|
152
|
+
'transition',
|
|
153
|
+
'transitionDelay',
|
|
154
|
+
'transitionDuration',
|
|
155
|
+
'transitionProperty',
|
|
156
|
+
'transitionTimingFunction',
|
|
157
|
+
'verticalAlign',
|
|
158
|
+
'visibility',
|
|
159
|
+
'whiteSpace',
|
|
160
|
+
'width',
|
|
161
|
+
'wordBreak',
|
|
162
|
+
'wordSpacing',
|
|
163
|
+
'wordWrap',
|
|
164
|
+
'zIndex',
|
|
165
|
+
])
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Check if a property name is a known CSS property
|
|
169
|
+
*
|
|
170
|
+
* @param propName - Property name to check
|
|
171
|
+
* @returns True if the property is a known CSS property
|
|
172
|
+
*/
|
|
173
|
+
export function isCssProperty(propName: string): boolean {
|
|
174
|
+
return CSS_PROPERTIES.has(propName)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Parse a CSS string into a React style object
|
|
179
|
+
*
|
|
180
|
+
* Converts kebab-case CSS properties to camelCase React style properties.
|
|
181
|
+
* Handles semicolon-separated CSS declarations.
|
|
182
|
+
* Normalizes animation names to kebab-case for consistency.
|
|
183
|
+
*
|
|
184
|
+
* @param styleString - CSS string (e.g., "background-color: #007bff; padding: 1rem;")
|
|
185
|
+
* @returns React style object (e.g., { backgroundColor: '#007bff', padding: '1rem' })
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* const style = parseStyle('background-color: #007bff; padding: 1rem;')
|
|
190
|
+
* // { backgroundColor: '#007bff', padding: '1rem' }
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
export function parseStyle(styleString: string): Record<string, string> {
|
|
194
|
+
// Split by semicolon and process each declaration
|
|
195
|
+
const declarations = styleString.split(';').filter((d) => d.trim())
|
|
196
|
+
|
|
197
|
+
// Use reduce for immutable accumulation instead of for-of loop with mutations
|
|
198
|
+
return declarations.reduce<Record<string, string>>((acc, declaration) => {
|
|
199
|
+
const [property, value] = declaration.split(':').map((s) => s.trim())
|
|
200
|
+
if (property && value) {
|
|
201
|
+
// Convert kebab-case to camelCase (e.g., background-color → backgroundColor)
|
|
202
|
+
const camelCaseProperty = property.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
|
|
203
|
+
|
|
204
|
+
// Normalize animation names to kebab-case
|
|
205
|
+
const normalizedValue =
|
|
206
|
+
camelCaseProperty === 'animation' ? normalizeAnimationValue(value) : value
|
|
207
|
+
|
|
208
|
+
return { ...acc, [camelCaseProperty]: normalizedValue }
|
|
209
|
+
}
|
|
210
|
+
return acc
|
|
211
|
+
}, {})
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Normalize animation names in style object to kebab-case
|
|
216
|
+
*
|
|
217
|
+
* Converts animation property values from camelCase to kebab-case.
|
|
218
|
+
* Useful for style objects (not CSS strings).
|
|
219
|
+
*
|
|
220
|
+
* @param style - React style object
|
|
221
|
+
* @returns Style object with normalized animation names
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```typescript
|
|
225
|
+
* const style = { animation: 'fadeIn 1s ease-in-out' }
|
|
226
|
+
* normalizeStyleAnimations(style) // { animation: 'fade-in 1s ease-in-out' }
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
export function normalizeStyleAnimations(
|
|
230
|
+
style: Record<string, unknown> | undefined
|
|
231
|
+
): Record<string, unknown> | undefined {
|
|
232
|
+
if (!style) return style
|
|
233
|
+
|
|
234
|
+
const animationValue = style.animation
|
|
235
|
+
if (typeof animationValue === 'string') {
|
|
236
|
+
return {
|
|
237
|
+
...style,
|
|
238
|
+
animation: normalizeAnimationValue(animationValue),
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return style
|
|
243
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
* Checks if a value is a CSS value with units (rem, px, em, %, vh, vw)
|
|
10
|
+
* CSS values must contain units and not include spaces (to distinguish from Tailwind classes)
|
|
11
|
+
*
|
|
12
|
+
* @param value - String value to check
|
|
13
|
+
* @returns true if value is a raw CSS value, false if it's a Tailwind class
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* isCssValue('4rem') // true
|
|
17
|
+
* isCssValue('80rem') // true
|
|
18
|
+
* isCssValue('py-16') // false
|
|
19
|
+
* isCssValue('py-16 sm:py-20') // false
|
|
20
|
+
*/
|
|
21
|
+
export const isCssValue = (value: string): boolean => {
|
|
22
|
+
return /\d+(rem|px|em|%|vh|vw)/.test(value) && !value.includes(' ')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks if a container spacing value is a Tailwind class (not a raw CSS value)
|
|
27
|
+
* Tailwind classes include utility classes like "max-w-7xl", "mx-auto", "px-4"
|
|
28
|
+
* CSS values like "80rem" or "1280px" should return false
|
|
29
|
+
*
|
|
30
|
+
* @param value - Container spacing value to check
|
|
31
|
+
* @returns true if value contains Tailwind classes, false if it's a raw CSS value
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* isTailwindClass('max-w-7xl mx-auto px-4') // true
|
|
35
|
+
* isTailwindClass('max-w-7xl') // true
|
|
36
|
+
* isTailwindClass('80rem') // false
|
|
37
|
+
* isTailwindClass('1280px') // false
|
|
38
|
+
*/
|
|
39
|
+
export const isTailwindClass = (value: string): boolean => {
|
|
40
|
+
// If it has spaces, it's multiple classes (Tailwind)
|
|
41
|
+
if (value.includes(' ')) {
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
44
|
+
// If it matches Tailwind patterns (max-w-*, mx-*, px-*, etc.), it's a class
|
|
45
|
+
if (/^(max-w-|mx-|px-|py-|p-|m-|w-|h-)/.test(value)) {
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
// Otherwise, assume it's a CSS value
|
|
49
|
+
return false
|
|
50
|
+
}
|