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,237 @@
|
|
|
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
|
+
/* eslint-disable max-params, max-lines-per-function -- Complex URL rewriting functions require explicit dependencies */
|
|
9
|
+
|
|
10
|
+
import { Effect, Console } from 'effect'
|
|
11
|
+
import { StaticGenerationError } from '@/application/errors/static-generation-error'
|
|
12
|
+
import type { FileSystemLike, PathModuleLike } from './generate-static-helpers'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Check if a URL is a GitHub Pages URL
|
|
16
|
+
* Security: Use proper hostname checking to avoid substring matching vulnerabilities
|
|
17
|
+
*/
|
|
18
|
+
export const isGitHubPagesUrl = (url: string): boolean => {
|
|
19
|
+
try {
|
|
20
|
+
const { hostname } = new URL(url)
|
|
21
|
+
return hostname === 'github.io' || hostname.endsWith('.github.io')
|
|
22
|
+
} catch {
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Rewrite URLs in HTML content with base path prefix
|
|
29
|
+
*
|
|
30
|
+
* Rewrites:
|
|
31
|
+
* - href="/..." → href="/basePath/..."
|
|
32
|
+
* - src="/..." → src="/basePath/..."
|
|
33
|
+
* - Adds canonical link if missing (when baseUrl is provided)
|
|
34
|
+
* - Skips URLs that already have base path
|
|
35
|
+
* - Skips external URLs (http://, https://, //, etc.)
|
|
36
|
+
*/
|
|
37
|
+
export const rewriteUrlsWithBasePath = (
|
|
38
|
+
html: string,
|
|
39
|
+
basePath: string,
|
|
40
|
+
baseUrl?: string,
|
|
41
|
+
pagePath?: string
|
|
42
|
+
): string => {
|
|
43
|
+
// Remove trailing slash from basePath if present
|
|
44
|
+
const normalizedBasePath = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath
|
|
45
|
+
|
|
46
|
+
// Extract base path without language code for assets
|
|
47
|
+
// If basePath is "/my-project/en", extract "/my-project" for assets
|
|
48
|
+
const basePathWithoutLang = normalizedBasePath.replace(/\/[a-z]{2}$/, '')
|
|
49
|
+
|
|
50
|
+
// Rewrite href="/..." to href="/basePath/..."
|
|
51
|
+
// But for /assets/*, use only the base path without language code
|
|
52
|
+
// Also handle hreflang alternate links specially (they should link to other languages)
|
|
53
|
+
const hrefRewritten = html.replace(
|
|
54
|
+
/(hrefLang|hreflang)="([^"]*)"[^>]*href="(\/[^"]*)"/g,
|
|
55
|
+
(_match, _hreflangAttr, langCode, url) => {
|
|
56
|
+
// For hreflang links, extract language from URL and construct correct path
|
|
57
|
+
// Example: href="/en/" → href="https://baseUrl/my-project/en/"
|
|
58
|
+
// Convert hrefLang to lowercase hreflang for standards compliance
|
|
59
|
+
const normalizedAttr = 'hreflang'
|
|
60
|
+
// Extract short code from full locale (e.g., "en-US" → "en")
|
|
61
|
+
const shortCode = langCode.split('-')[0]
|
|
62
|
+
// hreflang URLs should include full base URL
|
|
63
|
+
const fullUrl = baseUrl ? `${baseUrl}${url}` : `${basePathWithoutLang}${url}`
|
|
64
|
+
return `${normalizedAttr}="${shortCode}" href="${fullUrl}"`
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
// Now rewrite remaining href attributes (non-hreflang)
|
|
69
|
+
const allHrefRewritten = hrefRewritten.replace(
|
|
70
|
+
/(?<!hreflang=")href="(\/[^"]*)"/g,
|
|
71
|
+
(_match, url) => {
|
|
72
|
+
if (url.startsWith('/assets/')) {
|
|
73
|
+
// Assets are shared across languages - use base path only
|
|
74
|
+
return `href="${basePathWithoutLang}${url}"`
|
|
75
|
+
}
|
|
76
|
+
return `href="${normalizedBasePath}${url}"`
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
// Rewrite src="/..." to src="/basePath/..."
|
|
81
|
+
// Assets paths should not include language prefix
|
|
82
|
+
const srcRewritten = allHrefRewritten.replace(/src="(\/[^"]*)"/g, (_match, url) => {
|
|
83
|
+
if (url.startsWith('/assets/')) {
|
|
84
|
+
// Assets are shared across languages - use base path only
|
|
85
|
+
return `src="${basePathWithoutLang}${url}"`
|
|
86
|
+
}
|
|
87
|
+
return `src="${normalizedBasePath}${url}"`
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// Add canonical link if missing and baseUrl is provided
|
|
91
|
+
if (baseUrl && pagePath && !srcRewritten.includes('rel="canonical"')) {
|
|
92
|
+
// Construct canonical URL from baseUrl + pagePath
|
|
93
|
+
// Remove trailing slash from baseUrl for consistent URLs
|
|
94
|
+
const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl
|
|
95
|
+
const canonicalPath = pagePath === '/' ? '/' : pagePath
|
|
96
|
+
const canonicalUrl = `${normalizedBaseUrl}${canonicalPath}`
|
|
97
|
+
|
|
98
|
+
// Insert canonical link in <head> before </head>
|
|
99
|
+
return srcRewritten.replace('</head>', `<link rel="canonical" href="${canonicalUrl}"></head>`)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return srcRewritten
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Rewrite URLs in HTML files with base path
|
|
107
|
+
*/
|
|
108
|
+
export const rewriteBasePathInHtml = (
|
|
109
|
+
generatedFiles: readonly string[],
|
|
110
|
+
outputDir: string,
|
|
111
|
+
basePath: string,
|
|
112
|
+
baseUrl: string | undefined,
|
|
113
|
+
fsModule: FileSystemLike
|
|
114
|
+
): Effect.Effect<void, StaticGenerationError, never> =>
|
|
115
|
+
Effect.gen(function* () {
|
|
116
|
+
yield* Console.log(`🔗 Rewriting URLs with base path: ${basePath}...`)
|
|
117
|
+
// Only rewrite actual page HTML files, not assets with .html extension
|
|
118
|
+
const htmlFiles = generatedFiles.filter(
|
|
119
|
+
(f) =>
|
|
120
|
+
f.endsWith('.html') &&
|
|
121
|
+
!f.startsWith('assets/') &&
|
|
122
|
+
!f.includes('/assets/') &&
|
|
123
|
+
!f.endsWith('.js.html')
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
yield* Effect.forEach(
|
|
127
|
+
htmlFiles,
|
|
128
|
+
(file) =>
|
|
129
|
+
Effect.gen(function* () {
|
|
130
|
+
// file might be absolute or relative - handle both cases
|
|
131
|
+
const filePath =
|
|
132
|
+
file.startsWith('/') || file.startsWith(outputDir) ? file : `${outputDir}/${file}`
|
|
133
|
+
|
|
134
|
+
const content = yield* Effect.tryPromise({
|
|
135
|
+
try: () => fsModule.readFile(filePath, 'utf-8'),
|
|
136
|
+
catch: (error) =>
|
|
137
|
+
new StaticGenerationError({
|
|
138
|
+
message: `Failed to read HTML file: ${file}`,
|
|
139
|
+
cause: error,
|
|
140
|
+
}),
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// Determine page path from filename (index.html → /, about.html → /about)
|
|
144
|
+
const relativeFile = file.startsWith(outputDir) ? file.slice(outputDir.length + 1) : file
|
|
145
|
+
|
|
146
|
+
// Check if this file is in a language subdirectory (e.g., en/index.html, fr/about.html)
|
|
147
|
+
const langMatch = relativeFile.match(/^([a-z]{2})\//)
|
|
148
|
+
const langPrefix = langMatch ? `/${langMatch[1]}` : ''
|
|
149
|
+
|
|
150
|
+
// Extract page path without language prefix
|
|
151
|
+
const fileWithoutLang = langPrefix ? relativeFile.slice(3) : relativeFile // Skip "en/"
|
|
152
|
+
const basePagePath =
|
|
153
|
+
fileWithoutLang === 'index.html' ? '/' : `/${fileWithoutLang.replace('.html', '')}`
|
|
154
|
+
|
|
155
|
+
// Full page path includes basePath + language prefix
|
|
156
|
+
const fullBasePath = `${basePath}${langPrefix}`
|
|
157
|
+
|
|
158
|
+
// For canonical URL, use the page path relative to baseUrl
|
|
159
|
+
// If baseUrl includes the basePath (e.g., https://example.com/myapp),
|
|
160
|
+
// we should use basePagePath directly
|
|
161
|
+
const canonicalPagePath =
|
|
162
|
+
basePagePath === '/'
|
|
163
|
+
? langPrefix === ''
|
|
164
|
+
? '/'
|
|
165
|
+
: langPrefix
|
|
166
|
+
: `${langPrefix}${basePagePath}`
|
|
167
|
+
|
|
168
|
+
const rewrittenContent = rewriteUrlsWithBasePath(
|
|
169
|
+
content,
|
|
170
|
+
fullBasePath,
|
|
171
|
+
baseUrl,
|
|
172
|
+
canonicalPagePath
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
yield* Effect.tryPromise({
|
|
176
|
+
try: () => fsModule.writeFile(filePath, rewrittenContent, 'utf-8'),
|
|
177
|
+
catch: (error) =>
|
|
178
|
+
new StaticGenerationError({
|
|
179
|
+
message: `Failed to write rewritten HTML file: ${file}`,
|
|
180
|
+
cause: error,
|
|
181
|
+
}),
|
|
182
|
+
})
|
|
183
|
+
}),
|
|
184
|
+
{ concurrency: 'unbounded' }
|
|
185
|
+
)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Inject client-side hydration script into HTML files
|
|
190
|
+
*/
|
|
191
|
+
export const injectHydrationScript = (
|
|
192
|
+
generatedFiles: readonly string[],
|
|
193
|
+
outputDir: string,
|
|
194
|
+
basePath: string,
|
|
195
|
+
fsModule: FileSystemLike,
|
|
196
|
+
pathModule: PathModuleLike
|
|
197
|
+
): Effect.Effect<void, StaticGenerationError, never> =>
|
|
198
|
+
Effect.gen(function* () {
|
|
199
|
+
yield* Console.log('💧 Injecting hydration script into HTML files...')
|
|
200
|
+
const htmlFiles = generatedFiles.filter(
|
|
201
|
+
(f) =>
|
|
202
|
+
f.endsWith('.html') &&
|
|
203
|
+
!f.endsWith('.js.html') &&
|
|
204
|
+
!f.startsWith('assets/') &&
|
|
205
|
+
!f.includes('/assets/')
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
yield* Effect.forEach(
|
|
209
|
+
htmlFiles,
|
|
210
|
+
(file) =>
|
|
211
|
+
Effect.gen(function* () {
|
|
212
|
+
const filePath = file.startsWith('/') ? file : pathModule.join(outputDir, file)
|
|
213
|
+
const content = yield* Effect.tryPromise({
|
|
214
|
+
try: () => fsModule.readFile(filePath, 'utf-8'),
|
|
215
|
+
catch: (error) =>
|
|
216
|
+
new StaticGenerationError({
|
|
217
|
+
message: `Failed to read HTML file for hydration injection: ${file}`,
|
|
218
|
+
cause: error,
|
|
219
|
+
}),
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
// Inject hydration script before </body>
|
|
223
|
+
const hydrationScript = `<script src="${basePath}/assets/client.js" defer=""></script>`
|
|
224
|
+
const updatedContent = content.replace('</body>', `${hydrationScript}\n</body>`)
|
|
225
|
+
|
|
226
|
+
yield* Effect.tryPromise({
|
|
227
|
+
try: () => fsModule.writeFile(filePath, updatedContent, 'utf-8'),
|
|
228
|
+
catch: (error) =>
|
|
229
|
+
new StaticGenerationError({
|
|
230
|
+
message: `Failed to write HTML file with hydration script: ${file}`,
|
|
231
|
+
cause: error,
|
|
232
|
+
}),
|
|
233
|
+
})
|
|
234
|
+
}),
|
|
235
|
+
{ concurrency: 'unbounded' }
|
|
236
|
+
)
|
|
237
|
+
})
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { resolveTranslation } from '@/domain/utils/translation-resolver'
|
|
9
|
+
import type { App, Page } from '@/domain/models/app'
|
|
10
|
+
import type { LanguageConfig } from '@/domain/models/app/language/language-config'
|
|
11
|
+
import type { Languages } from '@/domain/models/app/languages'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Context for token replacement operations
|
|
15
|
+
*/
|
|
16
|
+
type TokenReplacementContext = {
|
|
17
|
+
readonly langCode: string
|
|
18
|
+
readonly langConfig: LanguageConfig
|
|
19
|
+
readonly languages: Languages | undefined
|
|
20
|
+
readonly translations: Record<string, string>
|
|
21
|
+
readonly currentPath?: string // Optional current page path for {{currentPath}} replacement
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Replace translation tokens in a string
|
|
26
|
+
*
|
|
27
|
+
* Replaces $t:key patterns with translations from the centralized translations dictionary.
|
|
28
|
+
* Also replaces {{currentPath}} with the current page path.
|
|
29
|
+
* Uses the same resolution logic as dynamic rendering (with fallback support).
|
|
30
|
+
*
|
|
31
|
+
* @param str - String potentially containing $t:key or {{currentPath}} patterns
|
|
32
|
+
* @param context - Token replacement context
|
|
33
|
+
* @returns String with all patterns resolved
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* replaceTokens('$t:welcome', context) // 'Welcome' (or 'Bienvenue' for fr)
|
|
38
|
+
* replaceTokens('$t:goodbye', context) // Falls back to default if missing
|
|
39
|
+
* replaceTokens('/en{{currentPath}}', { ...context, currentPath: '/about' }) // '/en/about'
|
|
40
|
+
* replaceTokens('Hello world', context) // 'Hello world' (no pattern)
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
function replaceTokens(str: string, context: TokenReplacementContext): string {
|
|
44
|
+
// Replace $t: translation pattern
|
|
45
|
+
const translatedStr = str.startsWith('$t:')
|
|
46
|
+
? resolveTranslation(str.slice(3), context.langCode, context.languages)
|
|
47
|
+
: str
|
|
48
|
+
|
|
49
|
+
// Replace {{currentPath}} pattern (for language switcher hrefs)
|
|
50
|
+
const currentPath = context.currentPath || '/'
|
|
51
|
+
return translatedStr.replace(/\{\{currentPath\}\}/g, currentPath)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Replace tokens in any value (recursively handles objects, arrays, strings)
|
|
56
|
+
*/
|
|
57
|
+
function replaceTokensInValue(value: unknown, context: TokenReplacementContext): unknown {
|
|
58
|
+
if (typeof value === 'string') {
|
|
59
|
+
return replaceTokens(value, context)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (Array.isArray(value)) {
|
|
63
|
+
return value.map((item) => replaceTokensInValue(item, context))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (value !== null && typeof value === 'object') {
|
|
67
|
+
return Object.fromEntries(
|
|
68
|
+
Object.entries(value).map(([key, val]) => [key, replaceTokensInValue(val, context)])
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return value
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Replace translation tokens in page meta and set lang attribute programmatically
|
|
77
|
+
*
|
|
78
|
+
* Sets meta.lang to the full locale (e.g., 'en-US', 'fr-FR') without using tokens.
|
|
79
|
+
* Then resolves any $t: patterns in the remaining meta fields.
|
|
80
|
+
*/
|
|
81
|
+
function replaceMetaTokens(meta: Page['meta'], context: TokenReplacementContext): Page['meta'] {
|
|
82
|
+
if (!meta) return meta
|
|
83
|
+
|
|
84
|
+
// Set lang to full locale programmatically (not via token)
|
|
85
|
+
const locale = context.langConfig.locale || context.langCode
|
|
86
|
+
const metaWithLang = {
|
|
87
|
+
...meta,
|
|
88
|
+
lang: locale,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Resolve $t: patterns in remaining meta fields
|
|
92
|
+
return replaceTokensInValue(metaWithLang, context) as Page['meta']
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Replace translation tokens in a page configuration
|
|
97
|
+
*
|
|
98
|
+
* Resolves $t:key patterns throughout the page and sets meta.lang programmatically.
|
|
99
|
+
* Also replaces {{currentPath}} patterns in language switcher hrefs.
|
|
100
|
+
*
|
|
101
|
+
* @param page - Page with potential $t:key translation tokens
|
|
102
|
+
* @param context - Token replacement context
|
|
103
|
+
* @returns Page with all $t: patterns resolved
|
|
104
|
+
*/
|
|
105
|
+
export function replacePageTokens(page: Page, context: TokenReplacementContext): Page {
|
|
106
|
+
const pageContext: TokenReplacementContext = {
|
|
107
|
+
...context,
|
|
108
|
+
currentPath: page.path, // Pass current page path for {{currentPath}} replacement
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Replace tokens in everything except meta
|
|
112
|
+
const { meta, ...restOfPage } = page
|
|
113
|
+
const replacedRest = replaceTokensInValue(restOfPage, pageContext) as Omit<Page, 'meta'>
|
|
114
|
+
|
|
115
|
+
// Replace meta separately with special lang handling
|
|
116
|
+
const replacedMeta = replaceMetaTokens(meta, pageContext)
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
...replacedRest,
|
|
120
|
+
meta: replacedMeta,
|
|
121
|
+
} as Page
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Replace translation tokens in app configuration for a specific language
|
|
126
|
+
*
|
|
127
|
+
* Resolves all $t:key patterns throughout the app for static site generation.
|
|
128
|
+
* This is a pure function that performs token replacement without throwing exceptions.
|
|
129
|
+
* Callers should validate language codes before calling this function.
|
|
130
|
+
*
|
|
131
|
+
* @param app - App configuration (may contain $t:key tokens in pages)
|
|
132
|
+
* @param langCode - Language code to generate for (e.g., 'en', 'fr')
|
|
133
|
+
* @returns App with all $t: patterns resolved for the language
|
|
134
|
+
* @internal This function assumes langCode exists in supported languages
|
|
135
|
+
*/
|
|
136
|
+
export function replaceAppTokens(app: App, langCode: string): App {
|
|
137
|
+
// If no languages configured, return app as-is
|
|
138
|
+
if (!app.languages) {
|
|
139
|
+
return app
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Find language config - caller must ensure langCode is valid
|
|
143
|
+
const langConfig = app.languages.supported.find((lang) => lang.code === langCode)!
|
|
144
|
+
|
|
145
|
+
// Get translations for this language
|
|
146
|
+
const translations = app.languages.translations?.[langCode] || {}
|
|
147
|
+
|
|
148
|
+
// Create context for token replacement
|
|
149
|
+
const context: TokenReplacementContext = {
|
|
150
|
+
langCode,
|
|
151
|
+
langConfig,
|
|
152
|
+
languages: app.languages,
|
|
153
|
+
translations,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Replace tokens in pages (with currentPath resolution)
|
|
157
|
+
const pages = app.pages?.map((page) => replacePageTokens(page, context))
|
|
158
|
+
|
|
159
|
+
// Return app with replaced pages
|
|
160
|
+
return {
|
|
161
|
+
...app,
|
|
162
|
+
pages,
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
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 } from 'effect'
|
|
9
|
+
import { ActivityRepository } from '@/application/ports/repositories/activity-repository'
|
|
10
|
+
import { SessionContextError } from '@/domain/errors'
|
|
11
|
+
import type { UserMetadataWithImage } from '@/application/ports/models/user-metadata'
|
|
12
|
+
import type { UserSession } from '@/application/ports/models/user-session'
|
|
13
|
+
import type { ActivityHistoryEntry } from '@/application/ports/repositories/activity-repository'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get record history configuration
|
|
17
|
+
*/
|
|
18
|
+
interface GetRecordHistoryConfig {
|
|
19
|
+
readonly session: Readonly<UserSession>
|
|
20
|
+
readonly tableName: string
|
|
21
|
+
readonly recordId: string
|
|
22
|
+
readonly limit?: number
|
|
23
|
+
readonly offset?: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Format activity history entry for API response
|
|
28
|
+
*/
|
|
29
|
+
function formatActivityEntry(entry: ActivityHistoryEntry) {
|
|
30
|
+
return {
|
|
31
|
+
action: entry.action,
|
|
32
|
+
createdAt: entry.createdAt.toISOString(),
|
|
33
|
+
changes: entry.changes,
|
|
34
|
+
user: entry.user,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get record history program
|
|
40
|
+
*/
|
|
41
|
+
export function getRecordHistoryProgram(config: GetRecordHistoryConfig): Effect.Effect<
|
|
42
|
+
{
|
|
43
|
+
readonly history: readonly {
|
|
44
|
+
readonly action: string
|
|
45
|
+
readonly createdAt: string
|
|
46
|
+
readonly changes: unknown
|
|
47
|
+
readonly user: UserMetadataWithImage | undefined
|
|
48
|
+
}[]
|
|
49
|
+
readonly pagination: {
|
|
50
|
+
readonly limit: number
|
|
51
|
+
readonly offset: number
|
|
52
|
+
readonly total: number
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
SessionContextError,
|
|
56
|
+
ActivityRepository
|
|
57
|
+
> {
|
|
58
|
+
return Effect.gen(function* () {
|
|
59
|
+
const activityRepo = yield* ActivityRepository
|
|
60
|
+
const { session, tableName, recordId, limit, offset } = config
|
|
61
|
+
|
|
62
|
+
// Check if record exists in the table (handles live records)
|
|
63
|
+
const recordExists = yield* activityRepo.checkRecordExists({ session, tableName, recordId })
|
|
64
|
+
|
|
65
|
+
// Fetch activity history with pagination (needed even for deleted records)
|
|
66
|
+
const { entries, total } = yield* activityRepo.getRecordHistory({
|
|
67
|
+
session,
|
|
68
|
+
tableName,
|
|
69
|
+
recordId,
|
|
70
|
+
limit,
|
|
71
|
+
offset,
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// If record doesn't exist in table AND has no activity logs, it truly doesn't exist
|
|
75
|
+
if (!recordExists && total === 0) {
|
|
76
|
+
return yield* Effect.fail(new SessionContextError('Record not found'))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Resolve pagination values (default: all results)
|
|
80
|
+
const resolvedLimit = limit ?? total
|
|
81
|
+
const resolvedOffset = offset ?? 0
|
|
82
|
+
|
|
83
|
+
// Format response
|
|
84
|
+
return {
|
|
85
|
+
history: entries.map(formatActivityEntry),
|
|
86
|
+
pagination: {
|
|
87
|
+
limit: resolvedLimit,
|
|
88
|
+
offset: resolvedOffset,
|
|
89
|
+
total,
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
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 } from 'effect'
|
|
9
|
+
import {
|
|
10
|
+
BatchRepository,
|
|
11
|
+
type BatchValidationError,
|
|
12
|
+
} from '@/application/ports/repositories/batch-repository'
|
|
13
|
+
import { transformRecords, type TransformedRecord } from './utils/record-transformer'
|
|
14
|
+
import type { UserSession } from '@/application/ports/models/user-session'
|
|
15
|
+
import type { SessionContextError, ValidationError } from '@/domain/errors'
|
|
16
|
+
import type { BatchRestoreRecordsResponse } from '@/domain/models/api/tables'
|
|
17
|
+
import type { App } from '@/domain/models/app'
|
|
18
|
+
|
|
19
|
+
export function batchCreateProgram(config: {
|
|
20
|
+
readonly session: Readonly<UserSession>
|
|
21
|
+
readonly tableName: string
|
|
22
|
+
readonly recordsData: readonly Record<string, unknown>[]
|
|
23
|
+
readonly returnRecords?: boolean
|
|
24
|
+
readonly app?: App
|
|
25
|
+
}): Effect.Effect<
|
|
26
|
+
{ readonly created: number; readonly records?: readonly TransformedRecord[] },
|
|
27
|
+
SessionContextError | ValidationError,
|
|
28
|
+
BatchRepository
|
|
29
|
+
> {
|
|
30
|
+
const { session, tableName, recordsData, returnRecords = false, app } = config
|
|
31
|
+
return Effect.gen(function* () {
|
|
32
|
+
const batch = yield* BatchRepository
|
|
33
|
+
|
|
34
|
+
// Create records in the database
|
|
35
|
+
const createdRecords = yield* batch.batchCreate(session, tableName, recordsData)
|
|
36
|
+
|
|
37
|
+
// Transform records to API format with app schema for numeric coercion
|
|
38
|
+
const transformed = transformRecords(createdRecords, { app, tableName })
|
|
39
|
+
|
|
40
|
+
// Use functional pattern to build response object
|
|
41
|
+
const response: { readonly created: number; readonly records?: readonly TransformedRecord[] } =
|
|
42
|
+
returnRecords
|
|
43
|
+
? {
|
|
44
|
+
created: transformed.length,
|
|
45
|
+
records: transformed as TransformedRecord[],
|
|
46
|
+
}
|
|
47
|
+
: {
|
|
48
|
+
created: transformed.length,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return response
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function batchUpdateProgram(config: {
|
|
56
|
+
readonly session: Readonly<UserSession>
|
|
57
|
+
readonly tableName: string
|
|
58
|
+
readonly recordsData: readonly {
|
|
59
|
+
readonly id: string
|
|
60
|
+
readonly fields?: Record<string, unknown>
|
|
61
|
+
}[]
|
|
62
|
+
readonly returnRecords?: boolean
|
|
63
|
+
readonly app?: App
|
|
64
|
+
}): Effect.Effect<
|
|
65
|
+
{ readonly updated: number; readonly records?: readonly TransformedRecord[] },
|
|
66
|
+
SessionContextError | ValidationError,
|
|
67
|
+
BatchRepository
|
|
68
|
+
> {
|
|
69
|
+
const { session, tableName, recordsData, returnRecords = false, app } = config
|
|
70
|
+
return Effect.gen(function* () {
|
|
71
|
+
const batch = yield* BatchRepository
|
|
72
|
+
const updatedRecords = yield* batch.batchUpdate(session, tableName, recordsData)
|
|
73
|
+
|
|
74
|
+
// Transform records to API format with app schema for numeric coercion
|
|
75
|
+
const transformed = transformRecords(updatedRecords, { app, tableName })
|
|
76
|
+
|
|
77
|
+
// Use functional pattern to build response object
|
|
78
|
+
const response: { readonly updated: number; readonly records?: readonly TransformedRecord[] } =
|
|
79
|
+
returnRecords
|
|
80
|
+
? {
|
|
81
|
+
updated: transformed.length,
|
|
82
|
+
records: transformed as TransformedRecord[],
|
|
83
|
+
}
|
|
84
|
+
: {
|
|
85
|
+
updated: transformed.length,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return response
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function batchDeleteProgram(
|
|
93
|
+
session: Readonly<UserSession>,
|
|
94
|
+
tableName: string,
|
|
95
|
+
ids: readonly string[],
|
|
96
|
+
permanent = false
|
|
97
|
+
): Effect.Effect<{ deleted: number }, SessionContextError, BatchRepository> {
|
|
98
|
+
return Effect.gen(function* () {
|
|
99
|
+
const batch = yield* BatchRepository
|
|
100
|
+
const deletedCount = yield* batch.batchDelete(session, tableName, ids, permanent)
|
|
101
|
+
return {
|
|
102
|
+
deleted: deletedCount,
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function batchRestoreProgram(
|
|
108
|
+
session: Readonly<UserSession>,
|
|
109
|
+
tableName: string,
|
|
110
|
+
ids: readonly string[]
|
|
111
|
+
): Effect.Effect<BatchRestoreRecordsResponse, SessionContextError, BatchRepository> {
|
|
112
|
+
return Effect.gen(function* () {
|
|
113
|
+
const batch = yield* BatchRepository
|
|
114
|
+
const restored = yield* batch.batchRestore(session, tableName, ids)
|
|
115
|
+
return {
|
|
116
|
+
success: true as const,
|
|
117
|
+
restored,
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function upsertProgram(
|
|
123
|
+
session: Readonly<UserSession>,
|
|
124
|
+
tableName: string,
|
|
125
|
+
params: {
|
|
126
|
+
readonly recordsData: readonly Record<string, unknown>[]
|
|
127
|
+
readonly fieldsToMergeOn: readonly string[]
|
|
128
|
+
readonly returnRecords: boolean
|
|
129
|
+
readonly app?: App
|
|
130
|
+
}
|
|
131
|
+
): Effect.Effect<
|
|
132
|
+
{
|
|
133
|
+
readonly records: readonly TransformedRecord[]
|
|
134
|
+
readonly created: number
|
|
135
|
+
readonly updated: number
|
|
136
|
+
},
|
|
137
|
+
SessionContextError | ValidationError | BatchValidationError,
|
|
138
|
+
BatchRepository
|
|
139
|
+
> {
|
|
140
|
+
return Effect.gen(function* () {
|
|
141
|
+
const batch = yield* BatchRepository
|
|
142
|
+
const result = yield* batch.upsert(
|
|
143
|
+
session,
|
|
144
|
+
tableName,
|
|
145
|
+
params.recordsData,
|
|
146
|
+
params.fieldsToMergeOn
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
const transformed = transformRecords(result.records, { app: params.app, tableName })
|
|
150
|
+
return {
|
|
151
|
+
records: transformed,
|
|
152
|
+
created: result.created,
|
|
153
|
+
updated: result.updated,
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
}
|