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,77 @@
|
|
|
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 { findDuplicate } from './field-types/validation-utils'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Validate that field permissions reference existing fields and don't have duplicates.
|
|
12
|
+
*
|
|
13
|
+
* @param fieldPermissions - Array of field permissions to validate
|
|
14
|
+
* @param fieldNames - Set of valid field names in the table
|
|
15
|
+
* @returns Error object if validation fails, undefined if valid
|
|
16
|
+
*/
|
|
17
|
+
export const validateFieldPermissions = (
|
|
18
|
+
fieldPermissions: ReadonlyArray<{ readonly field: string }>,
|
|
19
|
+
fieldNames: ReadonlySet<string>
|
|
20
|
+
): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
|
|
21
|
+
// Check for duplicate field permissions
|
|
22
|
+
const fieldPermissionNames = fieldPermissions.map((fp) => fp.field)
|
|
23
|
+
const duplicateField = findDuplicate(fieldPermissionNames)
|
|
24
|
+
|
|
25
|
+
if (duplicateField) {
|
|
26
|
+
return {
|
|
27
|
+
message: `Duplicate field permission for field '${duplicateField}' - conflicting permission definitions`,
|
|
28
|
+
path: ['permissions', 'fields'],
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check for non-existent field references
|
|
33
|
+
const invalidFieldPermission = fieldPermissions.find(
|
|
34
|
+
(fieldPermission) => !fieldNames.has(fieldPermission.field)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if (invalidFieldPermission) {
|
|
38
|
+
return {
|
|
39
|
+
message: `Field permission references non-existent field '${invalidFieldPermission.field}' - field does not exist in table`,
|
|
40
|
+
path: ['permissions', 'fields'],
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return undefined
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Validate table permissions including field permissions.
|
|
49
|
+
*
|
|
50
|
+
* With the simplified 3-format permission system ('all', 'authenticated', role array),
|
|
51
|
+
* table-level CRUD permissions are validated by the schema itself.
|
|
52
|
+
* This function handles cross-field validation (field permissions referencing table fields).
|
|
53
|
+
*
|
|
54
|
+
* @param permissions - Table permissions to validate
|
|
55
|
+
* @param _fields - Table fields (unused, kept for interface compatibility)
|
|
56
|
+
* @param fieldNames - Set of valid field names
|
|
57
|
+
* @returns Validation error object if invalid, undefined if valid
|
|
58
|
+
*/
|
|
59
|
+
export const validateTablePermissions = (
|
|
60
|
+
permissions: {
|
|
61
|
+
readonly fields?: ReadonlyArray<{
|
|
62
|
+
readonly field: string
|
|
63
|
+
}>
|
|
64
|
+
},
|
|
65
|
+
_fields: ReadonlyArray<{ readonly name: string; readonly type: string }>,
|
|
66
|
+
fieldNames: ReadonlySet<string>
|
|
67
|
+
): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
|
|
68
|
+
// Validate field permissions reference existing fields
|
|
69
|
+
if (permissions.fields) {
|
|
70
|
+
const fieldPermissionsError = validateFieldPermissions(permissions.fields, fieldNames)
|
|
71
|
+
if (fieldPermissionsError) {
|
|
72
|
+
return fieldPermissionsError
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return undefined
|
|
77
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
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 { findDuplicate } from './field-types/validation-utils'
|
|
9
|
+
import { SPECIAL_FIELDS } from './table-formula-validation'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validate primary key configuration (field references, duplicates).
|
|
13
|
+
*
|
|
14
|
+
* @param primaryKey - Primary key configuration to validate
|
|
15
|
+
* @param fieldNames - Set of valid field names in the table
|
|
16
|
+
* @returns Error object if validation fails, undefined if valid
|
|
17
|
+
*/
|
|
18
|
+
export const validatePrimaryKey = (
|
|
19
|
+
primaryKey: { readonly type: string; readonly fields?: ReadonlyArray<string> } | undefined,
|
|
20
|
+
fieldNames: ReadonlySet<string>
|
|
21
|
+
): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
|
|
22
|
+
if (!primaryKey || primaryKey.type !== 'composite' || !primaryKey.fields) {
|
|
23
|
+
return undefined
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check for duplicate field references
|
|
27
|
+
const duplicateField = findDuplicate(primaryKey.fields)
|
|
28
|
+
|
|
29
|
+
if (duplicateField) {
|
|
30
|
+
return {
|
|
31
|
+
message: `Primary key field '${duplicateField}' is not unique - duplicate field references in composite primary key`,
|
|
32
|
+
path: ['primaryKey', 'fields'],
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check for non-existent field references (allow special fields)
|
|
37
|
+
const invalidField = primaryKey.fields.find(
|
|
38
|
+
(field) => !fieldNames.has(field) && !SPECIAL_FIELDS.has(field)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if (invalidField) {
|
|
42
|
+
return {
|
|
43
|
+
message: `Primary key references non-existent field '${invalidField}' - field not found in table`,
|
|
44
|
+
path: ['primaryKey', 'fields'],
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return undefined
|
|
49
|
+
}
|
|
@@ -0,0 +1,408 @@
|
|
|
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 { findDuplicate } from './field-types/validation-utils'
|
|
9
|
+
import { SPECIAL_FIELDS } from './table-formula-validation'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validate that view IDs are unique within a table.
|
|
13
|
+
*
|
|
14
|
+
* @param views - Array of views to validate
|
|
15
|
+
* @returns Error object if validation fails, undefined if valid
|
|
16
|
+
*/
|
|
17
|
+
const validateViewIds = (
|
|
18
|
+
views: ReadonlyArray<{ readonly id: string | number }>
|
|
19
|
+
): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
|
|
20
|
+
// Convert all view IDs to strings for comparison (ViewId can be number or string)
|
|
21
|
+
const viewIds = views.map((view) => String(view.id))
|
|
22
|
+
|
|
23
|
+
// Find duplicate view ID
|
|
24
|
+
const duplicateId = findDuplicate(viewIds)
|
|
25
|
+
|
|
26
|
+
if (duplicateId) {
|
|
27
|
+
return {
|
|
28
|
+
message: `Duplicate view id '${duplicateId}' - view id must be unique within the table`,
|
|
29
|
+
path: ['views'],
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return undefined
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Validate that only one view is marked as default within a table.
|
|
38
|
+
*
|
|
39
|
+
* @param views - Array of views to validate
|
|
40
|
+
* @returns Error object if validation fails, undefined if valid
|
|
41
|
+
*/
|
|
42
|
+
const validateDefaultViews = (
|
|
43
|
+
views: ReadonlyArray<{ readonly id: string | number; readonly isDefault?: boolean }>
|
|
44
|
+
): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
|
|
45
|
+
const defaultViews = views.filter((view) => view.isDefault === true)
|
|
46
|
+
|
|
47
|
+
if (defaultViews.length > 1) {
|
|
48
|
+
return {
|
|
49
|
+
message: 'Only one default view is allowed per table - multiple default views found',
|
|
50
|
+
path: ['views'],
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return undefined
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Extract field references from a filter node recursively.
|
|
59
|
+
* Handles single conditions, AND groups, and OR groups.
|
|
60
|
+
*
|
|
61
|
+
* @param filterNode - The filter node to extract fields from
|
|
62
|
+
* @returns Array of field names referenced in the filter
|
|
63
|
+
*/
|
|
64
|
+
const extractFieldReferencesFromFilter = (
|
|
65
|
+
filterNode:
|
|
66
|
+
| { readonly field: string; readonly operator: string; readonly value: unknown }
|
|
67
|
+
| { readonly and: ReadonlyArray<unknown> }
|
|
68
|
+
| { readonly or: ReadonlyArray<unknown> }
|
|
69
|
+
): ReadonlyArray<string> => {
|
|
70
|
+
// Single condition - extract field name
|
|
71
|
+
if ('field' in filterNode) {
|
|
72
|
+
return [filterNode.field]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// AND group - recursively extract from all conditions
|
|
76
|
+
if ('and' in filterNode && Array.isArray(filterNode.and)) {
|
|
77
|
+
return filterNode.and.flatMap((node) =>
|
|
78
|
+
extractFieldReferencesFromFilter(
|
|
79
|
+
node as
|
|
80
|
+
| { readonly field: string; readonly operator: string; readonly value: unknown }
|
|
81
|
+
| { readonly and: ReadonlyArray<unknown> }
|
|
82
|
+
| { readonly or: ReadonlyArray<unknown> }
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// OR group - recursively extract from all conditions
|
|
88
|
+
if ('or' in filterNode && Array.isArray(filterNode.or)) {
|
|
89
|
+
return filterNode.or.flatMap((node) =>
|
|
90
|
+
extractFieldReferencesFromFilter(
|
|
91
|
+
node as
|
|
92
|
+
| { readonly field: string; readonly operator: string; readonly value: unknown }
|
|
93
|
+
| { readonly and: ReadonlyArray<unknown> }
|
|
94
|
+
| { readonly or: ReadonlyArray<unknown> }
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return []
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Extract filter conditions from a filter node recursively.
|
|
104
|
+
* Returns array of conditions with field, operator, and value.
|
|
105
|
+
*
|
|
106
|
+
* @param filterNode - The filter node to extract conditions from
|
|
107
|
+
* @returns Array of filter conditions
|
|
108
|
+
*/
|
|
109
|
+
const extractFilterConditions = (
|
|
110
|
+
filterNode:
|
|
111
|
+
| { readonly field: string; readonly operator: string; readonly value: unknown }
|
|
112
|
+
| { readonly and: ReadonlyArray<unknown> }
|
|
113
|
+
| { readonly or: ReadonlyArray<unknown> }
|
|
114
|
+
): ReadonlyArray<{
|
|
115
|
+
readonly field: string
|
|
116
|
+
readonly operator: string
|
|
117
|
+
readonly value: unknown
|
|
118
|
+
}> => {
|
|
119
|
+
// Single condition - return as array
|
|
120
|
+
if ('field' in filterNode) {
|
|
121
|
+
return [filterNode]
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// AND group - recursively extract from all conditions
|
|
125
|
+
if ('and' in filterNode && Array.isArray(filterNode.and)) {
|
|
126
|
+
return filterNode.and.flatMap((node) =>
|
|
127
|
+
extractFilterConditions(
|
|
128
|
+
node as
|
|
129
|
+
| { readonly field: string; readonly operator: string; readonly value: unknown }
|
|
130
|
+
| { readonly and: ReadonlyArray<unknown> }
|
|
131
|
+
| { readonly or: ReadonlyArray<unknown> }
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// OR group - recursively extract from all conditions
|
|
137
|
+
if ('or' in filterNode && Array.isArray(filterNode.or)) {
|
|
138
|
+
return filterNode.or.flatMap((node) =>
|
|
139
|
+
extractFilterConditions(
|
|
140
|
+
node as
|
|
141
|
+
| { readonly field: string; readonly operator: string; readonly value: unknown }
|
|
142
|
+
| { readonly and: ReadonlyArray<unknown> }
|
|
143
|
+
| { readonly or: ReadonlyArray<unknown> }
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return []
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Operator compatibility rules for field types.
|
|
153
|
+
* Maps field types to their valid operators.
|
|
154
|
+
* Only enforces restrictions for specific field types (e.g., checkbox cannot use 'contains').
|
|
155
|
+
* Other operators are allowed by default to avoid breaking valid use cases.
|
|
156
|
+
*/
|
|
157
|
+
const FIELD_TYPE_OPERATORS: ReadonlyMap<string, ReadonlySet<string>> = new Map([
|
|
158
|
+
// Checkbox: only boolean operators allowed
|
|
159
|
+
['checkbox', new Set(['equals', 'isTrue', 'isFalse'])],
|
|
160
|
+
])
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Validate that filter operators are compatible with field types.
|
|
164
|
+
*
|
|
165
|
+
* @param views - Array of views to validate
|
|
166
|
+
* @param fields - Array of fields in the table
|
|
167
|
+
* @returns Error object if validation fails, undefined if valid
|
|
168
|
+
*/
|
|
169
|
+
const validateFilterOperatorCompatibility = (
|
|
170
|
+
views: ReadonlyArray<{
|
|
171
|
+
readonly id: string | number
|
|
172
|
+
readonly filters?:
|
|
173
|
+
| { readonly field: string; readonly operator: string; readonly value: unknown }
|
|
174
|
+
| { readonly and: ReadonlyArray<unknown> }
|
|
175
|
+
| { readonly or: ReadonlyArray<unknown> }
|
|
176
|
+
}>,
|
|
177
|
+
fields: ReadonlyArray<{ readonly name: string; readonly type: string }>
|
|
178
|
+
): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
|
|
179
|
+
const fieldTypeMap = new Map(fields.map((field) => [field.name, field.type]))
|
|
180
|
+
|
|
181
|
+
const incompatibleFilter = views
|
|
182
|
+
.filter((view) => view.filters !== undefined)
|
|
183
|
+
.flatMap((view) => {
|
|
184
|
+
const conditions = extractFilterConditions(view.filters!)
|
|
185
|
+
return conditions.flatMap((condition) => {
|
|
186
|
+
const fieldType = fieldTypeMap.get(condition.field)
|
|
187
|
+
if (!fieldType) {
|
|
188
|
+
return []
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const validOperators = FIELD_TYPE_OPERATORS.get(fieldType)
|
|
192
|
+
if (!validOperators) {
|
|
193
|
+
// No restrictions defined for this field type
|
|
194
|
+
return []
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!validOperators.has(condition.operator)) {
|
|
198
|
+
return [{ view, condition, fieldType }]
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return []
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
.at(0)
|
|
205
|
+
|
|
206
|
+
if (incompatibleFilter) {
|
|
207
|
+
return {
|
|
208
|
+
message: `Incompatible operator '${incompatibleFilter.condition.operator}' for field '${incompatibleFilter.condition.field}' with type '${incompatibleFilter.fieldType}' - operator is invalid for checkbox field type`,
|
|
209
|
+
path: ['views'],
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return undefined
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Validate that view filters reference existing fields in the table.
|
|
218
|
+
*
|
|
219
|
+
* @param views - Array of views to validate
|
|
220
|
+
* @param fieldNames - Set of valid field names in the table
|
|
221
|
+
* @returns Error object if validation fails, undefined if valid
|
|
222
|
+
*/
|
|
223
|
+
const validateViewFilters = (
|
|
224
|
+
views: ReadonlyArray<{
|
|
225
|
+
readonly id: string | number
|
|
226
|
+
readonly filters?:
|
|
227
|
+
| { readonly field: string; readonly operator: string; readonly value: unknown }
|
|
228
|
+
| { readonly and: ReadonlyArray<unknown> }
|
|
229
|
+
| { readonly or: ReadonlyArray<unknown> }
|
|
230
|
+
}>,
|
|
231
|
+
fieldNames: ReadonlySet<string>
|
|
232
|
+
): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
|
|
233
|
+
const invalidView = views
|
|
234
|
+
.filter((view) => view.filters !== undefined)
|
|
235
|
+
.flatMap((view) => {
|
|
236
|
+
const referencedFields = extractFieldReferencesFromFilter(view.filters!)
|
|
237
|
+
const invalidFields = referencedFields.filter(
|
|
238
|
+
(fieldName) => !fieldNames.has(fieldName) && !SPECIAL_FIELDS.has(fieldName)
|
|
239
|
+
)
|
|
240
|
+
return invalidFields.map((invalidField) => ({ view, invalidField }))
|
|
241
|
+
})
|
|
242
|
+
.at(0)
|
|
243
|
+
|
|
244
|
+
if (invalidView) {
|
|
245
|
+
return {
|
|
246
|
+
message: `Filter references non-existent field '${invalidView.invalidField}' - field not found in table`,
|
|
247
|
+
path: ['views'],
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return undefined
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Validate that view fields reference existing fields in the table.
|
|
256
|
+
*
|
|
257
|
+
* @param views - Array of views to validate
|
|
258
|
+
* @param fieldNames - Set of valid field names in the table
|
|
259
|
+
* @returns Error object if validation fails, undefined if valid
|
|
260
|
+
*/
|
|
261
|
+
const validateViewFields = (
|
|
262
|
+
views: ReadonlyArray<{ readonly id: string | number; readonly fields?: ReadonlyArray<string> }>,
|
|
263
|
+
fieldNames: ReadonlySet<string>
|
|
264
|
+
): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
|
|
265
|
+
const invalidView = views
|
|
266
|
+
.filter(
|
|
267
|
+
(view): view is typeof view & { readonly fields: ReadonlyArray<string> } =>
|
|
268
|
+
view.fields !== undefined && view.fields.length > 0
|
|
269
|
+
)
|
|
270
|
+
.flatMap((view) => {
|
|
271
|
+
const invalidFields = view.fields.filter(
|
|
272
|
+
(fieldName) => !fieldNames.has(fieldName) && !SPECIAL_FIELDS.has(fieldName)
|
|
273
|
+
)
|
|
274
|
+
return invalidFields.map((invalidField) => ({ view, invalidField }))
|
|
275
|
+
})
|
|
276
|
+
.at(0)
|
|
277
|
+
|
|
278
|
+
if (invalidView) {
|
|
279
|
+
return {
|
|
280
|
+
message: `View field '${invalidView.invalidField}' not found - view fields must reference existing table fields (non-existent field in view)`,
|
|
281
|
+
path: ['views'],
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return undefined
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Validate that view groupBy references existing fields in the table.
|
|
290
|
+
*
|
|
291
|
+
* @param views - Array of views to validate
|
|
292
|
+
* @param fieldNames - Set of valid field names in the table
|
|
293
|
+
* @returns Error object if validation fails, undefined if valid
|
|
294
|
+
*/
|
|
295
|
+
const validateViewGroupBy = (
|
|
296
|
+
views: ReadonlyArray<{
|
|
297
|
+
readonly id: string | number
|
|
298
|
+
readonly groupBy?: { readonly field: string }
|
|
299
|
+
}>,
|
|
300
|
+
fieldNames: ReadonlySet<string>
|
|
301
|
+
): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
|
|
302
|
+
const invalidView = views
|
|
303
|
+
.filter(
|
|
304
|
+
(view): view is typeof view & { readonly groupBy: { readonly field: string } } =>
|
|
305
|
+
view.groupBy !== undefined
|
|
306
|
+
)
|
|
307
|
+
.find((view) => !fieldNames.has(view.groupBy.field) && !SPECIAL_FIELDS.has(view.groupBy.field))
|
|
308
|
+
|
|
309
|
+
if (invalidView) {
|
|
310
|
+
return {
|
|
311
|
+
message: `groupBy references non-existent field '${invalidView.groupBy.field}' - field not found in table`,
|
|
312
|
+
path: ['views'],
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return undefined
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Validate that view sorts reference existing fields in the table.
|
|
321
|
+
*
|
|
322
|
+
* @param views - Array of views to validate
|
|
323
|
+
* @param fieldNames - Set of valid field names in the table
|
|
324
|
+
* @returns Error object if validation fails, undefined if valid
|
|
325
|
+
*/
|
|
326
|
+
const validateViewSorts = (
|
|
327
|
+
views: ReadonlyArray<{
|
|
328
|
+
readonly id: string | number
|
|
329
|
+
readonly sorts?: ReadonlyArray<{ readonly field: string; readonly direction: string }>
|
|
330
|
+
}>,
|
|
331
|
+
fieldNames: ReadonlySet<string>
|
|
332
|
+
): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
|
|
333
|
+
const invalidView = views
|
|
334
|
+
.filter(
|
|
335
|
+
(
|
|
336
|
+
view
|
|
337
|
+
): view is typeof view & {
|
|
338
|
+
readonly sorts: ReadonlyArray<{ readonly field: string; readonly direction: string }>
|
|
339
|
+
} => view.sorts !== undefined && view.sorts.length > 0
|
|
340
|
+
)
|
|
341
|
+
.flatMap((view) => {
|
|
342
|
+
const invalidFields = view.sorts.filter(
|
|
343
|
+
(sort) => !fieldNames.has(sort.field) && !SPECIAL_FIELDS.has(sort.field)
|
|
344
|
+
)
|
|
345
|
+
return invalidFields.map((sort) => ({ view, invalidField: sort.field }))
|
|
346
|
+
})
|
|
347
|
+
.at(0)
|
|
348
|
+
|
|
349
|
+
if (invalidView) {
|
|
350
|
+
return {
|
|
351
|
+
message: `Sort references non-existent field '${invalidView.invalidField}' - field not found in table`,
|
|
352
|
+
path: ['views'],
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return undefined
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Validate views configuration (IDs, default views, field references, filter references).
|
|
361
|
+
*
|
|
362
|
+
* @param views - Array of views to validate
|
|
363
|
+
* @param fields - Array of fields in the table
|
|
364
|
+
* @param fieldNames - Set of valid field names in the table
|
|
365
|
+
* @returns Error object if validation fails, undefined if valid
|
|
366
|
+
*/
|
|
367
|
+
export const validateViews = (
|
|
368
|
+
views: ReadonlyArray<{ readonly id: string | number; readonly isDefault?: boolean }>,
|
|
369
|
+
fields: ReadonlyArray<{ readonly name: string; readonly type: string }>,
|
|
370
|
+
fieldNames: ReadonlySet<string>
|
|
371
|
+
): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
|
|
372
|
+
const viewsValidationError = validateViewIds(views)
|
|
373
|
+
if (viewsValidationError) {
|
|
374
|
+
return viewsValidationError
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const defaultViewsValidationError = validateDefaultViews(views)
|
|
378
|
+
if (defaultViewsValidationError) {
|
|
379
|
+
return defaultViewsValidationError
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const viewFieldsValidationError = validateViewFields(views, fieldNames)
|
|
383
|
+
if (viewFieldsValidationError) {
|
|
384
|
+
return viewFieldsValidationError
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const viewFiltersValidationError = validateViewFilters(views, fieldNames)
|
|
388
|
+
if (viewFiltersValidationError) {
|
|
389
|
+
return viewFiltersValidationError
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const viewSortsValidationError = validateViewSorts(views, fieldNames)
|
|
393
|
+
if (viewSortsValidationError) {
|
|
394
|
+
return viewSortsValidationError
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const viewGroupByValidationError = validateViewGroupBy(views, fieldNames)
|
|
398
|
+
if (viewGroupByValidationError) {
|
|
399
|
+
return viewGroupByValidationError
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const operatorCompatibilityError = validateFilterOperatorCompatibility(views, fields)
|
|
403
|
+
if (operatorCompatibilityError) {
|
|
404
|
+
return operatorCompatibilityError
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return undefined
|
|
408
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
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 { Schema } from 'effect'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Unique Constraints
|
|
12
|
+
*
|
|
13
|
+
* Composite unique constraints ensure that combinations of multiple field values are unique across all rows. Use this when you need uniqueness across multiple fields (e.g., email + tenant_id must be unique together).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* [
|
|
18
|
+
* {
|
|
19
|
+
* "name": "uq_user_email_tenant",
|
|
20
|
+
* "fields": [
|
|
21
|
+
* "email",
|
|
22
|
+
* "tenant_id"
|
|
23
|
+
* ]
|
|
24
|
+
* },
|
|
25
|
+
* {
|
|
26
|
+
* "name": "uq_product_sku_variant",
|
|
27
|
+
* "fields": [
|
|
28
|
+
* "sku",
|
|
29
|
+
* "variant_id"
|
|
30
|
+
* ]
|
|
31
|
+
* }
|
|
32
|
+
* ]
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export const UniqueConstraintsSchema = Schema.Array(
|
|
36
|
+
Schema.Struct({
|
|
37
|
+
name: Schema.String.pipe(
|
|
38
|
+
Schema.minLength(1, { message: () => 'This field is required' }),
|
|
39
|
+
Schema.pattern(/^[a-zA-Z][a-zA-Z0-9_]*$/, {
|
|
40
|
+
message: () =>
|
|
41
|
+
"Name of the unique constraint. Use descriptive names like 'uq_tablename_field1_field2'",
|
|
42
|
+
}),
|
|
43
|
+
Schema.transform(Schema.String, {
|
|
44
|
+
decode: (name) => name.toLowerCase(),
|
|
45
|
+
encode: (name) => name,
|
|
46
|
+
}),
|
|
47
|
+
Schema.annotations({
|
|
48
|
+
description:
|
|
49
|
+
"Name of the unique constraint. Use descriptive names like 'uq_tablename_field1_field2'",
|
|
50
|
+
examples: ['uq_users_email_tenant', 'uq_products_sku_variant', 'uq_orders_number_year'],
|
|
51
|
+
})
|
|
52
|
+
),
|
|
53
|
+
fields: Schema.Array(
|
|
54
|
+
Schema.String.pipe(Schema.minLength(1, { message: () => 'This field is required' }))
|
|
55
|
+
).pipe(Schema.minItems(1, { message: () => 'At least one field is required' })),
|
|
56
|
+
})
|
|
57
|
+
).pipe(
|
|
58
|
+
Schema.filter((constraints) => {
|
|
59
|
+
const names = constraints.map((constraint) => constraint.name)
|
|
60
|
+
const uniqueNames = new Set(names)
|
|
61
|
+
return (
|
|
62
|
+
names.length === uniqueNames.size ||
|
|
63
|
+
'Duplicate constraint name - constraint name must be unique within the table'
|
|
64
|
+
)
|
|
65
|
+
}),
|
|
66
|
+
Schema.annotations({
|
|
67
|
+
title: 'Unique Constraints',
|
|
68
|
+
description:
|
|
69
|
+
'Composite unique constraints ensure that combinations of multiple field values are unique across all rows. Use this when you need uniqueness across multiple fields (e.g., email + tenant_id must be unique together).',
|
|
70
|
+
examples: [
|
|
71
|
+
[
|
|
72
|
+
{ name: 'uq_user_email_tenant', fields: ['email', 'tenant_id'] },
|
|
73
|
+
{ name: 'uq_product_sku_variant', fields: ['sku', 'variant_id'] },
|
|
74
|
+
],
|
|
75
|
+
],
|
|
76
|
+
})
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
export type UniqueConstraints = Schema.Schema.Type<typeof UniqueConstraintsSchema>
|
|
@@ -0,0 +1,28 @@
|
|
|
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 { Schema } from 'effect'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* View Fields Schema
|
|
12
|
+
*
|
|
13
|
+
* Array of field names to include in the view.
|
|
14
|
+
* Fields are included in the order specified.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* ['name', 'email', 'created_at']
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export const ViewFieldsSchema = Schema.Array(Schema.String).pipe(
|
|
22
|
+
Schema.annotations({
|
|
23
|
+
title: 'View Fields',
|
|
24
|
+
description: 'Array of field names to include in the view.',
|
|
25
|
+
})
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
export type ViewFields = Schema.Schema.Type<typeof ViewFieldsSchema>
|