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
package/src/cli.ts
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the Business Source License 1.1
|
|
6
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Sovrium CLI - Command-line interface for Sovrium operations
|
|
11
|
+
*
|
|
12
|
+
* This script provides commands for running a Sovrium server or building static sites.
|
|
13
|
+
*
|
|
14
|
+
* ## Commands
|
|
15
|
+
*
|
|
16
|
+
* ### sovrium start [config]
|
|
17
|
+
* Start a development server
|
|
18
|
+
* ```bash
|
|
19
|
+
* sovrium start app.json # Load from JSON file
|
|
20
|
+
* sovrium start app.yaml # Load from YAML file
|
|
21
|
+
* APP_SCHEMA='{"name":"My App"}' sovrium # Inline JSON
|
|
22
|
+
* APP_SCHEMA='name: My App' sovrium # Inline YAML
|
|
23
|
+
* APP_SCHEMA='https://example.com/app.yaml' # Remote URL
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* ### sovrium build [config]
|
|
27
|
+
* Build static site files
|
|
28
|
+
* ```bash
|
|
29
|
+
* sovrium build app.json # Load from JSON file
|
|
30
|
+
* SOVRIUM_OUTPUT_DIR=./dist sovrium build # Or use env variable
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* ## Arguments
|
|
34
|
+
* - `config` (optional) - Path to config file (JSON or YAML)
|
|
35
|
+
*
|
|
36
|
+
* ## Environment Variables (start command)
|
|
37
|
+
* - `APP_SCHEMA` (optional if file provided) - App schema (inline JSON, YAML, or remote URL)
|
|
38
|
+
* - `PORT` (optional) - Server port (default: 3000)
|
|
39
|
+
* - `HOSTNAME` (optional) - Server hostname (default: localhost)
|
|
40
|
+
*
|
|
41
|
+
* ## Environment Variables (build command)
|
|
42
|
+
* - `APP_SCHEMA` (optional if file provided) - App schema (inline JSON, YAML, or remote URL)
|
|
43
|
+
* - `SOVRIUM_OUTPUT_DIR` (optional) - Output directory (default: ./dist)
|
|
44
|
+
* - `SOVRIUM_BASE_URL` (optional) - Base URL for sitemap
|
|
45
|
+
* - `SOVRIUM_BASE_PATH` (optional) - Base path for deployments
|
|
46
|
+
* - `SOVRIUM_DEPLOYMENT` (optional) - Deployment type (github-pages | generic)
|
|
47
|
+
* - `SOVRIUM_LANGUAGES` (optional) - Comma-separated language codes
|
|
48
|
+
* - `SOVRIUM_DEFAULT_LANGUAGE` (optional) - Default language
|
|
49
|
+
* - `SOVRIUM_GENERATE_SITEMAP` (optional) - Generate sitemap.xml (true/false)
|
|
50
|
+
* - `SOVRIUM_GENERATE_ROBOTS` (optional) - Generate robots.txt (true/false)
|
|
51
|
+
* - `SOVRIUM_HYDRATION` (optional) - Enable client-side hydration (true/false)
|
|
52
|
+
* - `SOVRIUM_GENERATE_MANIFEST` (optional) - Generate manifest.json (true/false)
|
|
53
|
+
* - `SOVRIUM_BUNDLE_OPTIMIZATION` (optional) - Bundle optimization strategy
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
import { watch } from 'node:fs'
|
|
57
|
+
import { Effect, Console } from 'effect'
|
|
58
|
+
import { start, build, type StartOptions, type GenerateStaticOptions } from '@/index'
|
|
59
|
+
import { parseAppSchema, loadSchemaFromFileForReload } from '@/presentation/cli'
|
|
60
|
+
|
|
61
|
+
// Server instance type (returned by start())
|
|
62
|
+
type ServerInstance = Awaited<ReturnType<typeof start>>
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Reload server with new configuration from changed config file
|
|
66
|
+
*
|
|
67
|
+
* This function is called when the config file changes during --watch mode.
|
|
68
|
+
* It gracefully reloads the server with the new configuration.
|
|
69
|
+
*
|
|
70
|
+
* @param filePath - Path to the config file that changed
|
|
71
|
+
* @param currentServer - The currently running server instance
|
|
72
|
+
* @param options - Server options (port, hostname)
|
|
73
|
+
* @returns Promise with the new server instance
|
|
74
|
+
* @throws Error if config file cannot be parsed or new server fails to start
|
|
75
|
+
*/
|
|
76
|
+
const reloadServer = async (
|
|
77
|
+
filePath: string,
|
|
78
|
+
currentServer: ServerInstance,
|
|
79
|
+
options: StartOptions
|
|
80
|
+
): Promise<ServerInstance> => {
|
|
81
|
+
// Parse the new config file (throws on invalid JSON/YAML, doesn't exit process)
|
|
82
|
+
const newApp = await loadSchemaFromFileForReload(filePath)
|
|
83
|
+
|
|
84
|
+
// Stop the current server before starting the new one
|
|
85
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
86
|
+
await currentServer.stop()
|
|
87
|
+
|
|
88
|
+
// Start new server with the updated config
|
|
89
|
+
const newServer = await start(newApp, options)
|
|
90
|
+
|
|
91
|
+
return newServer
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Show CLI help text
|
|
96
|
+
*/
|
|
97
|
+
const showHelp = (): void => {
|
|
98
|
+
const helpText = [
|
|
99
|
+
'Sovrium CLI',
|
|
100
|
+
'',
|
|
101
|
+
'Commands:',
|
|
102
|
+
' sovrium start [config] Start a development server (default)',
|
|
103
|
+
' sovrium build [config] Build static site files',
|
|
104
|
+
' sovrium --help Show this help message',
|
|
105
|
+
'',
|
|
106
|
+
'Options:',
|
|
107
|
+
' --watch, -w Watch config file for changes and hot reload',
|
|
108
|
+
'',
|
|
109
|
+
'Supported config formats: .json, .yaml, .yml',
|
|
110
|
+
'',
|
|
111
|
+
'Examples:',
|
|
112
|
+
' # Start development server with JSON file',
|
|
113
|
+
' sovrium start app.json',
|
|
114
|
+
'',
|
|
115
|
+
' # Start with YAML file',
|
|
116
|
+
' sovrium start app.yaml',
|
|
117
|
+
'',
|
|
118
|
+
' # Start with watch mode (hot reload on config changes)',
|
|
119
|
+
' sovrium start app.yaml --watch',
|
|
120
|
+
'',
|
|
121
|
+
' # Start with environment variable (JSON, YAML, or URL)',
|
|
122
|
+
' APP_SCHEMA=\'{"name":"My App"}\' sovrium start',
|
|
123
|
+
'',
|
|
124
|
+
' # Build static site',
|
|
125
|
+
' sovrium build app.json',
|
|
126
|
+
'',
|
|
127
|
+
'For more information, see the documentation at https://sovrium.com/docs/cli',
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
Effect.runSync(Console.log(helpText.join('\n')))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Parse server options from environment variables
|
|
135
|
+
*/
|
|
136
|
+
const parseStartOptions = (): StartOptions => {
|
|
137
|
+
const port = Bun.env.PORT
|
|
138
|
+
const hostname = Bun.env.HOSTNAME
|
|
139
|
+
|
|
140
|
+
if (!port && !hostname) {
|
|
141
|
+
return {}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const parsedPort = port ? parseInt(port, 10) : undefined
|
|
145
|
+
if (parsedPort !== undefined && (isNaN(parsedPort) || parsedPort < 0 || parsedPort > 65_535)) {
|
|
146
|
+
Effect.runSync(
|
|
147
|
+
Console.error(
|
|
148
|
+
`Error: Invalid port number "${port}". Must be between 0 and 65535 (0 = auto-select).`
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
// Terminate process - imperative statement required for CLI
|
|
152
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
153
|
+
process.exit(1)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
...(parsedPort !== undefined && { port: parsedPort }),
|
|
158
|
+
...(hostname && { hostname }),
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Handle the 'start' command
|
|
164
|
+
*/
|
|
165
|
+
const handleStartCommand = async (filePath?: string, watchMode = false): Promise<void> => {
|
|
166
|
+
const app = await parseAppSchema('start', filePath)
|
|
167
|
+
const options = parseStartOptions()
|
|
168
|
+
|
|
169
|
+
Effect.runSync(
|
|
170
|
+
Effect.gen(function* () {
|
|
171
|
+
yield* Console.log('Starting Sovrium server from CLI...')
|
|
172
|
+
yield* Console.log(`App: ${app.name}${app.description ? ` - ${app.description}` : ''}`)
|
|
173
|
+
if (filePath) yield* Console.log(`Config: ${filePath}`)
|
|
174
|
+
if (options.port) yield* Console.log(`Port: ${options.port}`)
|
|
175
|
+
if (options.hostname) yield* Console.log(`Hostname: ${options.hostname}`)
|
|
176
|
+
if (watchMode) yield* Console.log(`Watch mode: enabled`)
|
|
177
|
+
yield* Console.log('')
|
|
178
|
+
})
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
// Start the server
|
|
182
|
+
const server = await start(app, options).catch((error) => {
|
|
183
|
+
Effect.runSync(Console.error('Failed to start server:', error))
|
|
184
|
+
// Terminate process - imperative statement required for CLI
|
|
185
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
186
|
+
process.exit(1)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
// If watch mode enabled, set up file watcher
|
|
190
|
+
if (watchMode && filePath) {
|
|
191
|
+
console.log(`\n👀 Watching ${filePath} for changes...\n`)
|
|
192
|
+
|
|
193
|
+
// Track current server instance (mutable for watch mode)
|
|
194
|
+
// eslint-disable-next-line functional/no-let
|
|
195
|
+
let currentServer = server
|
|
196
|
+
|
|
197
|
+
// Set up file watcher using Node.js fs.watch (stable in Bun)
|
|
198
|
+
|
|
199
|
+
watch(filePath, async (eventType) => {
|
|
200
|
+
if (eventType === 'change') {
|
|
201
|
+
console.log(`\n🔄 Config changed, reloading...`)
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
205
|
+
currentServer = await reloadServer(filePath, currentServer, options)
|
|
206
|
+
|
|
207
|
+
console.log(`✅ Server reloaded successfully\n`)
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error(`❌ Reload failed: ${error instanceof Error ? error.message : error}\n`)
|
|
210
|
+
// Keep the old server running on error
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Parse boolean environment variable
|
|
219
|
+
*/
|
|
220
|
+
const parseBooleanEnv = (value: string | undefined): boolean | undefined =>
|
|
221
|
+
value === 'true' ? true : value === 'false' ? false : undefined
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Parse build options from environment variables
|
|
225
|
+
*/
|
|
226
|
+
const parseBuildOptions = (): GenerateStaticOptions => {
|
|
227
|
+
// Parse environment variables
|
|
228
|
+
const envVars = {
|
|
229
|
+
outputDir: Bun.env.SOVRIUM_OUTPUT_DIR,
|
|
230
|
+
baseUrl: Bun.env.SOVRIUM_BASE_URL,
|
|
231
|
+
basePath: Bun.env.SOVRIUM_BASE_PATH,
|
|
232
|
+
deployment: Bun.env.SOVRIUM_DEPLOYMENT as 'github-pages' | 'generic' | undefined,
|
|
233
|
+
languages: Bun.env.SOVRIUM_LANGUAGES?.split(',').map((lang) => lang.trim()),
|
|
234
|
+
defaultLanguage: Bun.env.SOVRIUM_DEFAULT_LANGUAGE,
|
|
235
|
+
generateSitemap: parseBooleanEnv(Bun.env.SOVRIUM_GENERATE_SITEMAP),
|
|
236
|
+
generateRobotsTxt: parseBooleanEnv(Bun.env.SOVRIUM_GENERATE_ROBOTS),
|
|
237
|
+
hydration: parseBooleanEnv(Bun.env.SOVRIUM_HYDRATION),
|
|
238
|
+
generateManifest: parseBooleanEnv(Bun.env.SOVRIUM_GENERATE_MANIFEST),
|
|
239
|
+
bundleOptimization: Bun.env.SOVRIUM_BUNDLE_OPTIMIZATION as 'split' | 'none' | undefined,
|
|
240
|
+
publicDir: Bun.env.SOVRIUM_PUBLIC_DIR,
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Build options object with only defined values
|
|
244
|
+
const options = [
|
|
245
|
+
{ key: 'outputDir', value: envVars.outputDir },
|
|
246
|
+
{ key: 'baseUrl', value: envVars.baseUrl },
|
|
247
|
+
{ key: 'basePath', value: envVars.basePath },
|
|
248
|
+
{ key: 'deployment', value: envVars.deployment },
|
|
249
|
+
{ key: 'languages', value: envVars.languages },
|
|
250
|
+
{ key: 'defaultLanguage', value: envVars.defaultLanguage },
|
|
251
|
+
{ key: 'generateSitemap', value: envVars.generateSitemap },
|
|
252
|
+
{ key: 'generateRobotsTxt', value: envVars.generateRobotsTxt },
|
|
253
|
+
{ key: 'hydration', value: envVars.hydration },
|
|
254
|
+
{ key: 'generateManifest', value: envVars.generateManifest },
|
|
255
|
+
{ key: 'bundleOptimization', value: envVars.bundleOptimization },
|
|
256
|
+
{ key: 'publicDir', value: envVars.publicDir },
|
|
257
|
+
].reduce(
|
|
258
|
+
(acc, { key, value }) =>
|
|
259
|
+
value !== undefined && value !== false ? { ...acc, [key]: value } : acc,
|
|
260
|
+
{} as GenerateStaticOptions
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
return options
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Handle the 'build' command
|
|
268
|
+
*/
|
|
269
|
+
const handleBuildCommand = async (filePath?: string): Promise<void> => {
|
|
270
|
+
const app = await parseAppSchema('build', filePath)
|
|
271
|
+
const options = parseBuildOptions()
|
|
272
|
+
|
|
273
|
+
Effect.runSync(
|
|
274
|
+
Effect.gen(function* () {
|
|
275
|
+
yield* Console.log('Building static site from CLI...')
|
|
276
|
+
yield* Console.log(`App: ${app.name}${app.description ? ` - ${app.description}` : ''}`)
|
|
277
|
+
if (filePath) yield* Console.log(`Config: ${filePath}`)
|
|
278
|
+
if (options.outputDir) yield* Console.log(`Output directory: ${options.outputDir}`)
|
|
279
|
+
if (options.baseUrl) yield* Console.log(`Base URL: ${options.baseUrl}`)
|
|
280
|
+
if (options.deployment) yield* Console.log(`Deployment: ${options.deployment}`)
|
|
281
|
+
yield* Console.log('')
|
|
282
|
+
})
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
// Build static site
|
|
286
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
287
|
+
await build(app, options).catch((error) => {
|
|
288
|
+
Effect.runSync(Console.error('Failed to build static site:', error))
|
|
289
|
+
// Terminate process - imperative statement required for CLI
|
|
290
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
291
|
+
process.exit(1)
|
|
292
|
+
})
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Main CLI entry point
|
|
296
|
+
const args = Bun.argv.slice(2)
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Check if argument is a config file path (JSON, YAML, or YML)
|
|
300
|
+
*/
|
|
301
|
+
const isConfigFile = (arg: string | undefined): boolean =>
|
|
302
|
+
arg !== undefined &&
|
|
303
|
+
(arg.endsWith('.json') ||
|
|
304
|
+
arg.endsWith('.yaml') ||
|
|
305
|
+
arg.endsWith('.yml') ||
|
|
306
|
+
arg.endsWith('.JSON') ||
|
|
307
|
+
arg.endsWith('.YAML') ||
|
|
308
|
+
arg.endsWith('.YML') ||
|
|
309
|
+
arg.includes('/'))
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Parse CLI arguments into command, config file, and flags
|
|
313
|
+
*/
|
|
314
|
+
const parseArgs = (
|
|
315
|
+
argv: readonly string[]
|
|
316
|
+
): { readonly command: string; readonly configFile?: string; readonly watchMode: boolean } => {
|
|
317
|
+
const watchMode = argv.includes('--watch') || argv.includes('-w')
|
|
318
|
+
const nonFlagArgs = argv.filter((arg) => !arg.startsWith('-'))
|
|
319
|
+
|
|
320
|
+
const command = nonFlagArgs[0] || 'start'
|
|
321
|
+
const configFile = nonFlagArgs[1]
|
|
322
|
+
|
|
323
|
+
// Handle case where first arg is a config file (implicit 'start' command)
|
|
324
|
+
if (isConfigFile(command)) {
|
|
325
|
+
return { command: 'start', configFile: command, watchMode }
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return { command, configFile, watchMode }
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const { command, configFile, watchMode } = parseArgs(args)
|
|
332
|
+
|
|
333
|
+
// Execute command - side effects required for CLI operation
|
|
334
|
+
;(async () => {
|
|
335
|
+
switch (command) {
|
|
336
|
+
case 'start':
|
|
337
|
+
// eslint-disable-next-line functional/no-expression-statements -- CLI command execution requires side effects
|
|
338
|
+
await handleStartCommand(configFile, watchMode)
|
|
339
|
+
break
|
|
340
|
+
case 'build':
|
|
341
|
+
// eslint-disable-next-line functional/no-expression-statements -- CLI command execution requires side effects
|
|
342
|
+
await handleBuildCommand(configFile)
|
|
343
|
+
break
|
|
344
|
+
case '--help':
|
|
345
|
+
case '-h':
|
|
346
|
+
case 'help':
|
|
347
|
+
showHelp()
|
|
348
|
+
// Terminate process - imperative statement required for CLI
|
|
349
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
350
|
+
process.exit(0)
|
|
351
|
+
break
|
|
352
|
+
default:
|
|
353
|
+
if (!command.startsWith('-')) {
|
|
354
|
+
// Unknown command without dash - try as start command
|
|
355
|
+
// eslint-disable-next-line functional/no-expression-statements -- CLI command execution requires side effects
|
|
356
|
+
await handleStartCommand(undefined, watchMode)
|
|
357
|
+
} else {
|
|
358
|
+
Effect.runSync(
|
|
359
|
+
Effect.gen(function* () {
|
|
360
|
+
yield* Console.error(`Error: Unknown command "${command}"`)
|
|
361
|
+
yield* Console.error('')
|
|
362
|
+
})
|
|
363
|
+
)
|
|
364
|
+
showHelp()
|
|
365
|
+
// Terminate process - imperative statement required for CLI
|
|
366
|
+
// eslint-disable-next-line functional/no-expression-statements
|
|
367
|
+
process.exit(1)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
})()
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
* Factory function to create tagged error classes for Effect discriminated unions.
|
|
10
|
+
*
|
|
11
|
+
* This factory reduces boilerplate for error classes that follow the pattern:
|
|
12
|
+
* - `readonly _tag` property for discriminated union matching
|
|
13
|
+
* - `readonly cause` property to wrap underlying errors
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Instead of manually defining:
|
|
18
|
+
* export class MyError {
|
|
19
|
+
* readonly _tag = 'MyError'
|
|
20
|
+
* constructor(readonly cause: unknown) {}
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* // Use the factory:
|
|
24
|
+
* export const MyError = createTaggedError('MyError')
|
|
25
|
+
* export type MyError = InstanceType<typeof MyError>
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @param tag - The unique tag string for the error type (used in Effect matching)
|
|
29
|
+
* @returns A class constructor that creates tagged error instances
|
|
30
|
+
*/
|
|
31
|
+
export function createTaggedError<T extends string>(tag: T) {
|
|
32
|
+
return class {
|
|
33
|
+
readonly _tag = tag
|
|
34
|
+
constructor(readonly cause: unknown) {}
|
|
35
|
+
} as new (cause: unknown) => { readonly _tag: T; readonly cause: unknown }
|
|
36
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
export { createTaggedError } from './create-tagged-error'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Database session context error
|
|
12
|
+
*/
|
|
13
|
+
export class SessionContextError extends Error {
|
|
14
|
+
readonly _tag = 'SessionContextError'
|
|
15
|
+
override readonly cause?: unknown
|
|
16
|
+
|
|
17
|
+
constructor(message: string, cause?: unknown) {
|
|
18
|
+
super(message)
|
|
19
|
+
// eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
|
|
20
|
+
this.name = 'SessionContextError'
|
|
21
|
+
// eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
|
|
22
|
+
this.cause = cause
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Forbidden error for authorization failures
|
|
28
|
+
*/
|
|
29
|
+
export class ForbiddenError extends Error {
|
|
30
|
+
readonly _tag = 'ForbiddenError'
|
|
31
|
+
|
|
32
|
+
constructor(message: string) {
|
|
33
|
+
super(message)
|
|
34
|
+
// eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
|
|
35
|
+
this.name = 'ForbiddenError'
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Unique constraint violation error
|
|
41
|
+
* Thrown when attempting to insert/update a record that violates a unique constraint
|
|
42
|
+
*/
|
|
43
|
+
export class UniqueConstraintViolationError extends Error {
|
|
44
|
+
readonly _tag = 'UniqueConstraintViolationError'
|
|
45
|
+
override readonly cause?: unknown
|
|
46
|
+
|
|
47
|
+
constructor(message: string, cause?: unknown) {
|
|
48
|
+
super(message)
|
|
49
|
+
// eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
|
|
50
|
+
this.name = 'UniqueConstraintViolationError'
|
|
51
|
+
// eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
|
|
52
|
+
this.cause = cause
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Validation error for invalid input data
|
|
58
|
+
* Thrown when input data fails validation before database operations
|
|
59
|
+
*/
|
|
60
|
+
export class ValidationError extends Error {
|
|
61
|
+
readonly _tag = 'ValidationError'
|
|
62
|
+
readonly details?: readonly {
|
|
63
|
+
readonly record: number
|
|
64
|
+
readonly field: string
|
|
65
|
+
readonly error: string
|
|
66
|
+
}[]
|
|
67
|
+
|
|
68
|
+
constructor(
|
|
69
|
+
message: string,
|
|
70
|
+
details?: readonly { readonly record: number; readonly field: string; readonly error: string }[]
|
|
71
|
+
) {
|
|
72
|
+
super(message)
|
|
73
|
+
// eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
|
|
74
|
+
this.name = 'ValidationError'
|
|
75
|
+
// eslint-disable-next-line functional/no-expression-statements -- Required for Error subclass
|
|
76
|
+
this.details = details
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
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 { z } from 'zod'
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Collection Schema (POST /api/analytics/collect)
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Analytics collection payload schema
|
|
16
|
+
*
|
|
17
|
+
* Minimal payload sent by the tracking script.
|
|
18
|
+
* Single-letter keys to minimize bandwidth usage.
|
|
19
|
+
*/
|
|
20
|
+
export const analyticsCollectSchema = z.object({
|
|
21
|
+
/** Page path (required) */
|
|
22
|
+
p: z.string().min(1).describe('Page path being viewed'),
|
|
23
|
+
/** Page title (optional) */
|
|
24
|
+
t: z.string().optional().describe('Page title'),
|
|
25
|
+
/** Referrer URL (optional) */
|
|
26
|
+
r: z.string().optional().describe('Full referrer URL'),
|
|
27
|
+
/** Screen width (optional) */
|
|
28
|
+
sw: z.number().int().positive().optional().describe('Screen width in pixels'),
|
|
29
|
+
/** Screen height (optional) */
|
|
30
|
+
sh: z.number().int().positive().optional().describe('Screen height in pixels'),
|
|
31
|
+
/** UTM source (optional) */
|
|
32
|
+
us: z.string().optional().describe('UTM source parameter'),
|
|
33
|
+
/** UTM medium (optional) */
|
|
34
|
+
um: z.string().optional().describe('UTM medium parameter'),
|
|
35
|
+
/** UTM campaign (optional) */
|
|
36
|
+
uc: z.string().optional().describe('UTM campaign parameter'),
|
|
37
|
+
/** UTM content (optional) */
|
|
38
|
+
ux: z.string().optional().describe('UTM content parameter'),
|
|
39
|
+
/** UTM term (optional) */
|
|
40
|
+
ut: z.string().optional().describe('UTM term parameter'),
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
export type AnalyticsCollectPayload = z.infer<typeof analyticsCollectSchema>
|
|
44
|
+
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Query Parameters Schema (shared across query endpoints)
|
|
47
|
+
// ============================================================================
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Analytics query parameters schema
|
|
51
|
+
*
|
|
52
|
+
* Shared query parameters for all analytics query endpoints.
|
|
53
|
+
*/
|
|
54
|
+
export const analyticsQuerySchema = z.object({
|
|
55
|
+
/** Start of date range (ISO 8601) */
|
|
56
|
+
from: z.string().describe('Start of date range (ISO 8601 datetime)'),
|
|
57
|
+
/** End of date range (ISO 8601) */
|
|
58
|
+
to: z.string().describe('End of date range (ISO 8601 datetime)'),
|
|
59
|
+
/** Time series granularity */
|
|
60
|
+
granularity: z
|
|
61
|
+
.enum(['hour', 'day', 'week', 'month'])
|
|
62
|
+
.default('day')
|
|
63
|
+
.describe('Time series granularity'),
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
export type AnalyticsQueryParams = z.infer<typeof analyticsQuerySchema>
|
|
67
|
+
|
|
68
|
+
// ============================================================================
|
|
69
|
+
// Response Schemas
|
|
70
|
+
// ============================================================================
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Time series data point
|
|
74
|
+
*/
|
|
75
|
+
export const timeSeriesPointSchema = z.object({
|
|
76
|
+
period: z.string().describe('Time period start (ISO 8601)'),
|
|
77
|
+
pageViews: z.number().int().describe('Total page views in period'),
|
|
78
|
+
uniqueVisitors: z.number().int().describe('Unique visitors in period'),
|
|
79
|
+
sessions: z.number().int().describe('Unique sessions in period'),
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
export type TimeSeriesPoint = z.infer<typeof timeSeriesPointSchema>
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Analytics overview response schema
|
|
86
|
+
*
|
|
87
|
+
* GET /api/analytics/overview
|
|
88
|
+
*/
|
|
89
|
+
export const analyticsOverviewResponseSchema = z.object({
|
|
90
|
+
summary: z.object({
|
|
91
|
+
pageViews: z.number().int().describe('Total page views'),
|
|
92
|
+
uniqueVisitors: z.number().int().describe('Total unique visitors'),
|
|
93
|
+
sessions: z.number().int().describe('Total sessions'),
|
|
94
|
+
}),
|
|
95
|
+
timeSeries: z.array(timeSeriesPointSchema).describe('Time series data points'),
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
export type AnalyticsOverviewResponse = z.infer<typeof analyticsOverviewResponseSchema>
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Top pages response schema
|
|
102
|
+
*
|
|
103
|
+
* GET /api/analytics/pages
|
|
104
|
+
*/
|
|
105
|
+
export const analyticsTopPagesResponseSchema = z.object({
|
|
106
|
+
pages: z.array(
|
|
107
|
+
z.object({
|
|
108
|
+
path: z.string().describe('Page path'),
|
|
109
|
+
pageViews: z.number().int().describe('Total page views'),
|
|
110
|
+
uniqueVisitors: z.number().int().describe('Unique visitors'),
|
|
111
|
+
})
|
|
112
|
+
),
|
|
113
|
+
total: z.number().int().describe('Total number of pages'),
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
export type AnalyticsTopPagesResponse = z.infer<typeof analyticsTopPagesResponseSchema>
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Top referrers response schema
|
|
120
|
+
*
|
|
121
|
+
* GET /api/analytics/referrers
|
|
122
|
+
*/
|
|
123
|
+
export const analyticsTopReferrersResponseSchema = z.object({
|
|
124
|
+
referrers: z.array(
|
|
125
|
+
z.object({
|
|
126
|
+
domain: z.string().nullable().describe('Referrer domain (null for direct traffic)'),
|
|
127
|
+
pageViews: z.number().int().describe('Total page views from this referrer'),
|
|
128
|
+
uniqueVisitors: z.number().int().describe('Unique visitors from this referrer'),
|
|
129
|
+
})
|
|
130
|
+
),
|
|
131
|
+
total: z.number().int().describe('Total referrer entries'),
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
export type AnalyticsTopReferrersResponse = z.infer<typeof analyticsTopReferrersResponseSchema>
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Device breakdown entry schema
|
|
138
|
+
*/
|
|
139
|
+
const breakdownEntrySchema = z.object({
|
|
140
|
+
name: z.string().describe('Category name'),
|
|
141
|
+
count: z.number().int().describe('Number of page views'),
|
|
142
|
+
percentage: z.number().describe('Percentage of total (0-100)'),
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Device breakdown response schema
|
|
147
|
+
*
|
|
148
|
+
* GET /api/analytics/devices
|
|
149
|
+
*/
|
|
150
|
+
export const analyticsDevicesResponseSchema = z.object({
|
|
151
|
+
deviceTypes: z.array(breakdownEntrySchema).describe('Device type breakdown'),
|
|
152
|
+
browsers: z.array(breakdownEntrySchema).describe('Browser name breakdown'),
|
|
153
|
+
operatingSystems: z.array(breakdownEntrySchema).describe('OS name breakdown'),
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
export type AnalyticsDevicesResponse = z.infer<typeof analyticsDevicesResponseSchema>
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Campaign entry schema
|
|
160
|
+
*/
|
|
161
|
+
const campaignEntrySchema = z.object({
|
|
162
|
+
source: z.string().nullable().describe('UTM source'),
|
|
163
|
+
medium: z.string().nullable().describe('UTM medium'),
|
|
164
|
+
campaign: z.string().nullable().describe('UTM campaign'),
|
|
165
|
+
pageViews: z.number().int().describe('Total page views'),
|
|
166
|
+
uniqueVisitors: z.number().int().describe('Unique visitors'),
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Campaigns response schema
|
|
171
|
+
*
|
|
172
|
+
* GET /api/analytics/campaigns
|
|
173
|
+
*/
|
|
174
|
+
export const analyticsCampaignsResponseSchema = z.object({
|
|
175
|
+
campaigns: z.array(campaignEntrySchema).describe('UTM campaign breakdown'),
|
|
176
|
+
total: z.number().int().describe('Total campaign entries'),
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
export type AnalyticsCampaignsResponse = z.infer<typeof analyticsCampaignsResponseSchema>
|