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,275 @@
|
|
|
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 { Console, Effect, Config } from 'effect'
|
|
9
|
+
import { Hono } from 'hono'
|
|
10
|
+
import { purgeOldAnalyticsData } from '@/application/use-cases/analytics/purge-old-data'
|
|
11
|
+
import { compileCSS } from '@/infrastructure/css/compiler'
|
|
12
|
+
import { runMigrations } from '@/infrastructure/database/drizzle/migrate'
|
|
13
|
+
import { AnalyticsRepositoryLive } from '@/infrastructure/database/repositories/analytics-repository-live'
|
|
14
|
+
import {
|
|
15
|
+
initializeSchema,
|
|
16
|
+
type AuthConfigRequiredForUserFields,
|
|
17
|
+
type SchemaInitializationError,
|
|
18
|
+
} from '@/infrastructure/database/schema/schema-initializer'
|
|
19
|
+
import { ServerCreationError } from '@/infrastructure/errors/server-creation-error'
|
|
20
|
+
import { createApiRoutes } from '@/infrastructure/server/route-setup/api-routes'
|
|
21
|
+
import {
|
|
22
|
+
setupAuthMiddleware,
|
|
23
|
+
setupAuthRoutes,
|
|
24
|
+
} from '@/infrastructure/server/route-setup/auth-routes'
|
|
25
|
+
import { setupOpenApiRoutes } from '@/infrastructure/server/route-setup/openapi-routes'
|
|
26
|
+
import {
|
|
27
|
+
setupPageRoutes,
|
|
28
|
+
type HonoAppConfig,
|
|
29
|
+
} from '@/infrastructure/server/route-setup/page-routes'
|
|
30
|
+
import { setupStaticAssets } from '@/infrastructure/server/route-setup/static-assets'
|
|
31
|
+
import type { ServerInstance } from '@/application/models/server'
|
|
32
|
+
import type { App } from '@/domain/models/app'
|
|
33
|
+
import type {
|
|
34
|
+
DatabaseConnectionError,
|
|
35
|
+
MigrationError,
|
|
36
|
+
} from '@/infrastructure/database/drizzle/migrate'
|
|
37
|
+
import type { CSSCompilationError } from '@/infrastructure/errors/css-compilation-error'
|
|
38
|
+
import type { Server } from 'bun'
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Server configuration options
|
|
42
|
+
*/
|
|
43
|
+
export interface ServerConfig {
|
|
44
|
+
readonly app: App
|
|
45
|
+
readonly port?: number
|
|
46
|
+
readonly hostname?: string
|
|
47
|
+
readonly publicDir?: string
|
|
48
|
+
readonly renderHomePage: (app: App, detectedLanguage?: string) => string
|
|
49
|
+
readonly renderPage: (app: App, path: string, detectedLanguage?: string) => string | undefined
|
|
50
|
+
readonly renderNotFoundPage: (app?: App, detectedLanguage?: string) => string
|
|
51
|
+
readonly renderErrorPage: (app?: App, detectedLanguage?: string) => string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Creates a Hono application with routes
|
|
56
|
+
*
|
|
57
|
+
* Mounts the following routes:
|
|
58
|
+
* - GET /api/* - API routes (health, tables, records) with RPC type safety
|
|
59
|
+
* - GET /api/openapi.json - Generated OpenAPI specification (application endpoints)
|
|
60
|
+
* - GET /api/auth/openapi.json - Generated OpenAPI specification (authentication endpoints)
|
|
61
|
+
* - GET /api/scalar - Unified Scalar API documentation UI (shows both API and Auth tabs)
|
|
62
|
+
* - POST/GET /api/auth/* - Better Auth authentication endpoints
|
|
63
|
+
* - GET / - Homepage
|
|
64
|
+
* - GET /assets/output.css - Compiled Tailwind CSS
|
|
65
|
+
* - GET /test/error - Test error handler (non-production only)
|
|
66
|
+
*
|
|
67
|
+
* @param config - Configuration object with app data and render functions
|
|
68
|
+
* @returns Configured Hono app instance
|
|
69
|
+
* @knip-ignore - Used by both createServer and createHonoAppForSSG
|
|
70
|
+
*/
|
|
71
|
+
export function createHonoApp(config: HonoAppConfig): Readonly<Hono> {
|
|
72
|
+
const { app, renderNotFoundPage, renderErrorPage } = config
|
|
73
|
+
|
|
74
|
+
const honoApp = new Hono()
|
|
75
|
+
|
|
76
|
+
// Analytics retention cleanup middleware — purges stale page view records.
|
|
77
|
+
// Runs awaited on page requests to guarantee old data is removed before response.
|
|
78
|
+
const analyticsEnabled = app.analytics !== undefined && app.analytics !== false
|
|
79
|
+
if (analyticsEnabled) {
|
|
80
|
+
const retentionDays =
|
|
81
|
+
typeof app.analytics === 'object' ? app.analytics.retentionDays : undefined
|
|
82
|
+
|
|
83
|
+
honoApp.use('*', async (_c, next) => {
|
|
84
|
+
await Effect.runPromise(
|
|
85
|
+
purgeOldAnalyticsData(app.name, retentionDays).pipe(
|
|
86
|
+
Effect.provide(AnalyticsRepositoryLive),
|
|
87
|
+
Effect.catchAll(() => Effect.void)
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
91
|
+
await next()
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Create base Hono app and chain API routes directly
|
|
96
|
+
// This pattern is required for Hono RPC type inference to work correctly
|
|
97
|
+
// Setup all routes by chaining the setup functions
|
|
98
|
+
const honoWithRoutes = setupPageRoutes(
|
|
99
|
+
setupStaticAssets(
|
|
100
|
+
setupAuthRoutes(
|
|
101
|
+
setupAuthMiddleware(setupOpenApiRoutes(createApiRoutes(app, honoApp)), app),
|
|
102
|
+
app
|
|
103
|
+
),
|
|
104
|
+
app,
|
|
105
|
+
config.publicDir
|
|
106
|
+
),
|
|
107
|
+
config
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
// Add error handlers
|
|
111
|
+
return honoWithRoutes
|
|
112
|
+
.notFound((c) => c.html(renderNotFoundPage(app), 404))
|
|
113
|
+
.onError((error, c) => {
|
|
114
|
+
// Fire-and-forget error logging (onError handler is synchronous)
|
|
115
|
+
Effect.runPromise(Console.error('Server error:', error)).catch(() => {
|
|
116
|
+
// Silently ignore logging failures to prevent unhandled promise rejections
|
|
117
|
+
})
|
|
118
|
+
return c.html(renderErrorPage(app), 500)
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Create server stop effect
|
|
124
|
+
*/
|
|
125
|
+
const createStopEffect = (server: ReturnType<typeof Bun.serve>): Effect.Effect<void, never> =>
|
|
126
|
+
Effect.gen(function* () {
|
|
127
|
+
yield* Console.log('Stopping server...')
|
|
128
|
+
yield* Effect.sync(() => server.stop())
|
|
129
|
+
yield* Console.log('Server stopped')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Log server startup information
|
|
134
|
+
*/
|
|
135
|
+
const logServerStartup = (url: string): Effect.Effect<void, never> =>
|
|
136
|
+
Effect.gen(function* () {
|
|
137
|
+
yield* Console.log('✓ Server started successfully!')
|
|
138
|
+
yield* Console.log(`✓ Homepage: ${url}`)
|
|
139
|
+
yield* Console.log(`✓ Health check: ${url}/api/health`)
|
|
140
|
+
yield* Console.log(`✓ API documentation: ${url}/api/scalar`)
|
|
141
|
+
yield* Console.log(`✓ OpenAPI schema: ${url}/api/openapi.json`)
|
|
142
|
+
yield* Console.log(`✓ Compiled CSS: ${url}/assets/output.css`)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get database URL from environment configuration
|
|
147
|
+
*/
|
|
148
|
+
const getDatabaseUrl = (): Effect.Effect<string, never> =>
|
|
149
|
+
Config.string('DATABASE_URL').pipe(
|
|
150
|
+
Config.withDefault(''),
|
|
151
|
+
Effect.catchAll(() => Effect.succeed(''))
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Run migrations if database URL is configured
|
|
156
|
+
*/
|
|
157
|
+
const runMigrationsIfConfigured = (
|
|
158
|
+
databaseUrl: string
|
|
159
|
+
): Effect.Effect<
|
|
160
|
+
void,
|
|
161
|
+
DatabaseConnectionError | MigrationError | SchemaInitializationError,
|
|
162
|
+
never
|
|
163
|
+
> => (databaseUrl ? runMigrations(databaseUrl) : Effect.void)
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Compile CSS and log results
|
|
167
|
+
*/
|
|
168
|
+
const compileCSSWithLogging = (
|
|
169
|
+
app: App
|
|
170
|
+
): Effect.Effect<{ css: string }, CSSCompilationError, never> =>
|
|
171
|
+
Effect.gen(function* () {
|
|
172
|
+
yield* Console.log('Compiling CSS...')
|
|
173
|
+
const cssResult = yield* compileCSS(app)
|
|
174
|
+
yield* Console.log(`CSS compiled: ${cssResult.css.length} bytes`)
|
|
175
|
+
return cssResult
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Start Bun HTTP server
|
|
180
|
+
*/
|
|
181
|
+
const startBunServer = (
|
|
182
|
+
honoApp: Readonly<Hono>,
|
|
183
|
+
port: number,
|
|
184
|
+
hostname: string
|
|
185
|
+
): Effect.Effect<Server<undefined>, ServerCreationError, never> =>
|
|
186
|
+
Effect.try({
|
|
187
|
+
try: () =>
|
|
188
|
+
Bun.serve({
|
|
189
|
+
port,
|
|
190
|
+
hostname,
|
|
191
|
+
fetch: honoApp.fetch,
|
|
192
|
+
}),
|
|
193
|
+
catch: (error) => new ServerCreationError(error),
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Creates and starts a Bun server with Hono
|
|
198
|
+
*
|
|
199
|
+
* This function:
|
|
200
|
+
* 1. Pre-compiles CSS on startup for faster initial requests
|
|
201
|
+
* 2. Creates a Hono app with routes (/, /assets/output.css, /api/*)
|
|
202
|
+
* 3. Starts a Bun HTTP server
|
|
203
|
+
* 4. Returns server instance with stop capability
|
|
204
|
+
*
|
|
205
|
+
* @param config - Server configuration with app data and optional port/hostname
|
|
206
|
+
* @returns Effect that yields ServerInstance or ServerCreationError
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```typescript
|
|
210
|
+
* const program = Effect.gen(function* () {
|
|
211
|
+
* const server = yield* createServer({
|
|
212
|
+
* app: { name: 'My App' },
|
|
213
|
+
* port: 3000
|
|
214
|
+
* })
|
|
215
|
+
* console.log(`Server running at ${server.url}`)
|
|
216
|
+
* })
|
|
217
|
+
*
|
|
218
|
+
* Effect.runPromise(program)
|
|
219
|
+
* ```
|
|
220
|
+
*/
|
|
221
|
+
// @knip-ignore - Used via dynamic import in StartServer.ts
|
|
222
|
+
export const createServer = (
|
|
223
|
+
config: ServerConfig
|
|
224
|
+
): Effect.Effect<
|
|
225
|
+
ServerInstance,
|
|
226
|
+
| ServerCreationError
|
|
227
|
+
| CSSCompilationError
|
|
228
|
+
| AuthConfigRequiredForUserFields
|
|
229
|
+
| SchemaInitializationError
|
|
230
|
+
| Error
|
|
231
|
+
> =>
|
|
232
|
+
Effect.gen(function* () {
|
|
233
|
+
const {
|
|
234
|
+
app,
|
|
235
|
+
port = 3000,
|
|
236
|
+
hostname = 'localhost',
|
|
237
|
+
publicDir,
|
|
238
|
+
renderHomePage,
|
|
239
|
+
renderPage,
|
|
240
|
+
renderNotFoundPage,
|
|
241
|
+
renderErrorPage,
|
|
242
|
+
} = config
|
|
243
|
+
|
|
244
|
+
// Initialize database
|
|
245
|
+
const databaseUrl = yield* getDatabaseUrl()
|
|
246
|
+
yield* runMigrationsIfConfigured(databaseUrl)
|
|
247
|
+
yield* initializeSchema(app)
|
|
248
|
+
|
|
249
|
+
// Compile CSS
|
|
250
|
+
yield* compileCSSWithLogging(app)
|
|
251
|
+
|
|
252
|
+
// Create Hono app
|
|
253
|
+
const honoApp = createHonoApp({
|
|
254
|
+
app,
|
|
255
|
+
publicDir,
|
|
256
|
+
renderHomePage,
|
|
257
|
+
renderPage,
|
|
258
|
+
renderNotFoundPage,
|
|
259
|
+
renderErrorPage,
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
// Start server
|
|
263
|
+
const server = yield* startBunServer(honoApp, port, hostname)
|
|
264
|
+
const url = `http://${hostname}:${server.port}`
|
|
265
|
+
|
|
266
|
+
// Log and return
|
|
267
|
+
yield* logServerStartup(url)
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
server,
|
|
271
|
+
url,
|
|
272
|
+
stop: createStopEffect(server),
|
|
273
|
+
app: honoApp,
|
|
274
|
+
}
|
|
275
|
+
})
|
|
@@ -0,0 +1,196 @@
|
|
|
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 { Effect, Data } from 'effect'
|
|
9
|
+
import { toSSG } from 'hono/bun'
|
|
10
|
+
import type { Hono } from 'hono'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* SSG Generation Error - infrastructure layer error
|
|
14
|
+
*/
|
|
15
|
+
export class SSGGenerationError extends Data.TaggedError('SSGGenerationError')<{
|
|
16
|
+
readonly message: string
|
|
17
|
+
readonly cause?: unknown
|
|
18
|
+
}> {}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Options for static site generation (infrastructure layer)
|
|
22
|
+
*/
|
|
23
|
+
export interface SSGOptions {
|
|
24
|
+
readonly outputDir?: string
|
|
25
|
+
readonly baseUrl?: string
|
|
26
|
+
readonly basePath?: string
|
|
27
|
+
readonly deployment?: 'github-pages' | 'generic'
|
|
28
|
+
readonly languages?: readonly string[]
|
|
29
|
+
readonly defaultLanguage?: string
|
|
30
|
+
readonly generateSitemap?: boolean
|
|
31
|
+
readonly generateRobotsTxt?: boolean
|
|
32
|
+
readonly hydration?: boolean
|
|
33
|
+
readonly generateManifest?: boolean
|
|
34
|
+
readonly bundleOptimization?: 'split' | 'none'
|
|
35
|
+
readonly pagePaths?: readonly string[] // Explicit list of page paths to generate
|
|
36
|
+
readonly publicDir?: string // Directory containing static assets to copy
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Register explicit page paths in Hono app for SSG discovery
|
|
41
|
+
*/
|
|
42
|
+
function registerPagePaths(
|
|
43
|
+
// eslint-disable-next-line functional/prefer-immutable-types -- Hono type is mutable
|
|
44
|
+
app: Hono,
|
|
45
|
+
pagePaths: readonly string[]
|
|
46
|
+
): void {
|
|
47
|
+
const mutableApp = app as Hono
|
|
48
|
+
|
|
49
|
+
// eslint-disable-next-line functional/no-loop-statements -- Imperative route registration required by Hono's mutable API for toSSG discovery
|
|
50
|
+
for (const path of pagePaths) {
|
|
51
|
+
if (path !== '/') {
|
|
52
|
+
// eslint-disable-next-line functional/no-expression-statements -- Necessary side effect to mutate Hono app for toSSG route discovery
|
|
53
|
+
mutableApp.get(path, (c) => c.text(''))
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check if a route should be excluded from SSG
|
|
60
|
+
*/
|
|
61
|
+
function shouldExcludeRoute(pathname: string): boolean {
|
|
62
|
+
return (
|
|
63
|
+
pathname === '/api/health' ||
|
|
64
|
+
pathname === '/api/openapi.json' ||
|
|
65
|
+
pathname === '/api/scalar' ||
|
|
66
|
+
pathname.startsWith('/api/auth/') ||
|
|
67
|
+
pathname === '/api/tables' || // Exact match for list endpoint
|
|
68
|
+
pathname.startsWith('/api/tables/') ||
|
|
69
|
+
pathname === '/api/records' || // Exact match for records endpoint
|
|
70
|
+
pathname.startsWith('/api/records/') ||
|
|
71
|
+
pathname.startsWith('/test/')
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Normalize file path to be relative to output directory
|
|
77
|
+
*/
|
|
78
|
+
function normalizeFilePath(file: string, outputDir: string): string {
|
|
79
|
+
const normalizedOutputDir = outputDir.startsWith('./') ? outputDir.substring(2) : outputDir
|
|
80
|
+
const normalizedFile = file.startsWith('./') ? file.substring(2) : file
|
|
81
|
+
|
|
82
|
+
if (normalizedFile.startsWith(normalizedOutputDir + '/')) {
|
|
83
|
+
return normalizedFile.substring(normalizedOutputDir.length + 1)
|
|
84
|
+
}
|
|
85
|
+
if (normalizedFile.startsWith(normalizedOutputDir)) {
|
|
86
|
+
return normalizedFile.substring(normalizedOutputDir.length)
|
|
87
|
+
}
|
|
88
|
+
return normalizedFile
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Generate static site using Hono's SSG functionality
|
|
93
|
+
*
|
|
94
|
+
* This adapter wraps Hono's toSSG function with Effect.ts patterns
|
|
95
|
+
* and Sovrium-specific logic.
|
|
96
|
+
*
|
|
97
|
+
* @param app - Hono application instance
|
|
98
|
+
* @param options - Static generation options
|
|
99
|
+
* @returns Effect with output directory and generated files
|
|
100
|
+
*/
|
|
101
|
+
export const generateStaticSite = (
|
|
102
|
+
// eslint-disable-next-line functional/prefer-immutable-types -- Hono is a mutable class from external library
|
|
103
|
+
app: Hono | Readonly<Hono>,
|
|
104
|
+
options: Readonly<SSGOptions>
|
|
105
|
+
): Effect.Effect<
|
|
106
|
+
{ readonly outputDir: string; readonly files: readonly string[] },
|
|
107
|
+
SSGGenerationError,
|
|
108
|
+
never
|
|
109
|
+
> =>
|
|
110
|
+
Effect.tryPromise({
|
|
111
|
+
try: async () => {
|
|
112
|
+
const outputDir = options.outputDir || './static'
|
|
113
|
+
|
|
114
|
+
// Register explicit page paths for SSG discovery
|
|
115
|
+
if (options.pagePaths && options.pagePaths.length > 0) {
|
|
116
|
+
registerPagePaths(app as Hono, options.pagePaths)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Use Hono's toSSG to generate static files
|
|
120
|
+
const result = await toSSG(app as Hono, {
|
|
121
|
+
dir: outputDir,
|
|
122
|
+
beforeRequestHook: (req) => {
|
|
123
|
+
const url = new URL(req.url)
|
|
124
|
+
return shouldExcludeRoute(url.pathname) ? false : req
|
|
125
|
+
},
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// Check if SSG generation was successful
|
|
129
|
+
if (!result.success) {
|
|
130
|
+
// eslint-disable-next-line functional/no-throw-statements -- Error handling requires throw
|
|
131
|
+
throw new Error(
|
|
132
|
+
`Static site generation failed: ${result.error?.message || 'Unknown error'}`
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Normalize file paths to be relative to outputDir
|
|
137
|
+
const normalizedFiles = (result.files as readonly string[]).map((file) =>
|
|
138
|
+
normalizeFilePath(file, outputDir)
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
// Return output directory and generated files from toSSG
|
|
142
|
+
return {
|
|
143
|
+
outputDir,
|
|
144
|
+
files: normalizedFiles,
|
|
145
|
+
} as const
|
|
146
|
+
},
|
|
147
|
+
catch: (error) =>
|
|
148
|
+
new SSGGenerationError({
|
|
149
|
+
message: `Static site generation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
150
|
+
cause: error,
|
|
151
|
+
}),
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Page info for sitemap generation
|
|
156
|
+
*/
|
|
157
|
+
interface SitemapPage {
|
|
158
|
+
readonly path: string
|
|
159
|
+
readonly priority?: string
|
|
160
|
+
readonly changefreq?: string
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Plugin to generate sitemap.xml
|
|
165
|
+
*/
|
|
166
|
+
export const sitemapPlugin = (baseUrl: string) => ({
|
|
167
|
+
name: 'sitemap',
|
|
168
|
+
async generate(pages: readonly SitemapPage[]) {
|
|
169
|
+
const entries = pages.map(
|
|
170
|
+
(page) => ` <url>
|
|
171
|
+
<loc>${baseUrl}${page.path}</loc>
|
|
172
|
+
<priority>${page.priority || '0.5'}</priority>
|
|
173
|
+
<changefreq>${page.changefreq || 'monthly'}</changefreq>
|
|
174
|
+
</url>`
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
178
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
179
|
+
${entries.join('\n')}
|
|
180
|
+
</urlset>`
|
|
181
|
+
},
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Plugin to generate robots.txt
|
|
186
|
+
*/
|
|
187
|
+
export const robotsPlugin = (baseUrl: string, includeSitemap: boolean = false) => ({
|
|
188
|
+
name: 'robots',
|
|
189
|
+
async generate() {
|
|
190
|
+
const baseLines = ['User-agent: *', 'Allow: /'] as const
|
|
191
|
+
const sitemapLine = includeSitemap ? ([`Sitemap: ${baseUrl}/sitemap.xml`] as const) : []
|
|
192
|
+
const lines = [...baseLines, ...sitemapLine] as readonly string[]
|
|
193
|
+
|
|
194
|
+
return lines.join('\n')
|
|
195
|
+
},
|
|
196
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
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 { Layer } from 'effect'
|
|
9
|
+
import { StaticSiteGenerator } from '@/application/ports/services/static-site-generator'
|
|
10
|
+
import { generateStaticSite } from '@/infrastructure/server/ssg-adapter'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Live implementation of StaticSiteGenerator using Hono SSG
|
|
14
|
+
*
|
|
15
|
+
* This layer provides the production implementation of static site generation
|
|
16
|
+
* using Hono's toSSG functionality.
|
|
17
|
+
*/
|
|
18
|
+
export const StaticSiteGeneratorLive = Layer.succeed(StaticSiteGenerator, {
|
|
19
|
+
generate: generateStaticSite,
|
|
20
|
+
})
|
|
@@ -0,0 +1,106 @@
|
|
|
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
|
+
* Accept-Language Header Parser
|
|
10
|
+
*
|
|
11
|
+
* Parses the Accept-Language HTTP header and returns an ordered list of
|
|
12
|
+
* preferred languages based on quality values (q parameter).
|
|
13
|
+
*
|
|
14
|
+
* Format: Accept-Language: en-US,en;q=0.9,fr;q=0.8
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const languages = parseAcceptLanguage('en-US,en;q=0.9,fr;q=0.8')
|
|
19
|
+
* // Returns: ['en-US', 'en', 'fr']
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
interface LanguagePreference {
|
|
24
|
+
readonly language: string
|
|
25
|
+
readonly quality: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Parse Accept-Language header into ordered list of language preferences
|
|
30
|
+
*
|
|
31
|
+
* @param header - Accept-Language header value (e.g., 'en-US,en;q=0.9,fr;q=0.8')
|
|
32
|
+
* @returns Array of language codes ordered by preference (highest quality first)
|
|
33
|
+
*/
|
|
34
|
+
export function parseAcceptLanguage(header: string | null | undefined): ReadonlyArray<string> {
|
|
35
|
+
if (!header) {
|
|
36
|
+
return []
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Parse each language-quality pair and sort by quality (highest first)
|
|
40
|
+
const sortedPreferences: ReadonlyArray<LanguagePreference> = header
|
|
41
|
+
.split(',')
|
|
42
|
+
.map((lang) => {
|
|
43
|
+
const [language, qualityStr] = lang.trim().split(';')
|
|
44
|
+
|
|
45
|
+
// Skip invalid entries without a language code
|
|
46
|
+
if (!language) {
|
|
47
|
+
return undefined
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Extract quality value (default is 1.0 if not specified)
|
|
51
|
+
const quality = qualityStr ? parseFloat(qualityStr.split('=')[1] || '1.0') : 1.0
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
language: language.trim(),
|
|
55
|
+
quality: isNaN(quality) ? 0 : quality,
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
.filter(
|
|
59
|
+
(pref): pref is LanguagePreference =>
|
|
60
|
+
pref !== undefined && pref.language.length > 0 && pref.quality > 0
|
|
61
|
+
)
|
|
62
|
+
.toSorted((a, b) => b.quality - a.quality)
|
|
63
|
+
|
|
64
|
+
return sortedPreferences.map((pref) => pref.language)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Detect best matching language from Accept-Language header
|
|
69
|
+
*
|
|
70
|
+
* Tries exact match first (e.g., 'fr-FR' === 'fr-FR'),
|
|
71
|
+
* then base language match (e.g., 'fr' from 'fr-FR' matches 'fr-CA')
|
|
72
|
+
*
|
|
73
|
+
* @param header - Accept-Language header value
|
|
74
|
+
* @param supportedLanguages - Array of supported language codes
|
|
75
|
+
* @returns Best matching language code or undefined
|
|
76
|
+
*/
|
|
77
|
+
export function detectLanguageFromHeader(
|
|
78
|
+
header: string | null | undefined,
|
|
79
|
+
supportedLanguages: ReadonlyArray<string>
|
|
80
|
+
): string | undefined {
|
|
81
|
+
const preferences = parseAcceptLanguage(header)
|
|
82
|
+
|
|
83
|
+
// Try exact matches first
|
|
84
|
+
const exactMatch = preferences.find((preferred) => supportedLanguages.includes(preferred))
|
|
85
|
+
if (exactMatch) {
|
|
86
|
+
return exactMatch
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Try base language matches (e.g., 'fr' from 'fr-FR')
|
|
90
|
+
const baseMatch = preferences
|
|
91
|
+
.map((preferred) => ({
|
|
92
|
+
preferred,
|
|
93
|
+
basePreferred: preferred.split('-')[0],
|
|
94
|
+
}))
|
|
95
|
+
.find(({ basePreferred }) =>
|
|
96
|
+
supportedLanguages.some((supported) => supported.split('-')[0] === basePreferred)
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if (baseMatch) {
|
|
100
|
+
return supportedLanguages.find(
|
|
101
|
+
(supported) => supported.split('-')[0] === baseMatch.basePreferred
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return undefined
|
|
106
|
+
}
|
|
@@ -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
|
+
* Simple glob pattern matcher for URL paths.
|
|
10
|
+
*
|
|
11
|
+
* Supports:
|
|
12
|
+
* - Exact matches: /admin
|
|
13
|
+
* - Wildcard suffix: /admin/* matches /admin/dashboard, /admin/users/123
|
|
14
|
+
* - Wildcard prefix: star/api matches /v1/api, /public/api
|
|
15
|
+
* - Wildcard anywhere: /admin/star/users matches /admin/123/users
|
|
16
|
+
*
|
|
17
|
+
* Does NOT support:
|
|
18
|
+
* - Character classes: [abc]
|
|
19
|
+
* - Brace expansion: curly braces
|
|
20
|
+
* - Double asterisk: treated same as single asterisk
|
|
21
|
+
*
|
|
22
|
+
* @param pattern - Glob pattern string (e.g., /admin/*)
|
|
23
|
+
* @param path - Path to test (e.g., /admin/dashboard)
|
|
24
|
+
* @returns true if path matches pattern, false otherwise
|
|
25
|
+
*/
|
|
26
|
+
export function matchGlobPattern(pattern: string, path: string): boolean {
|
|
27
|
+
// Escape special regex characters except *
|
|
28
|
+
const escapedPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*')
|
|
29
|
+
|
|
30
|
+
// Create regex with exact start and end anchors
|
|
31
|
+
const regex = new RegExp(`^${escapedPattern}$`)
|
|
32
|
+
|
|
33
|
+
return regex.test(path)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check if a path matches any of the provided glob patterns.
|
|
38
|
+
*
|
|
39
|
+
* @param patterns - Array of glob patterns
|
|
40
|
+
* @param path - Path to test
|
|
41
|
+
* @returns true if path matches any pattern, false otherwise
|
|
42
|
+
*/
|
|
43
|
+
export function matchesAnyGlobPattern(
|
|
44
|
+
patterns: readonly string[] | undefined,
|
|
45
|
+
path: string
|
|
46
|
+
): boolean {
|
|
47
|
+
if (!patterns || patterns.length === 0) return false
|
|
48
|
+
|
|
49
|
+
return patterns.some((pattern) => matchGlobPattern(pattern, path))
|
|
50
|
+
}
|