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,372 @@
|
|
|
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 type { TablePermissions, TableFieldPermissions } from '.'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Check if user has permission based on permission configuration.
|
|
12
|
+
*
|
|
13
|
+
* Permission format (3-format system):
|
|
14
|
+
* - `'all'` — Everyone (including unauthenticated)
|
|
15
|
+
* - `'authenticated'` — Any logged-in user
|
|
16
|
+
* - `string[]` — Specific role names
|
|
17
|
+
*/
|
|
18
|
+
export function hasPermission(permission: unknown, userRole: string): boolean {
|
|
19
|
+
if (!permission) return false
|
|
20
|
+
if (permission === 'all') return true
|
|
21
|
+
if (permission === 'authenticated') return true
|
|
22
|
+
if (Array.isArray(permission)) return permission.includes(userRole)
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if user role has admin privileges
|
|
28
|
+
*/
|
|
29
|
+
export function isAdminRole(userRole: string): boolean {
|
|
30
|
+
return userRole === 'admin'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Check permission with admin override
|
|
35
|
+
*/
|
|
36
|
+
export function checkPermissionWithAdminOverride(
|
|
37
|
+
isAdmin: boolean,
|
|
38
|
+
permission: unknown,
|
|
39
|
+
userRole: string
|
|
40
|
+
): boolean {
|
|
41
|
+
return isAdmin || hasPermission(permission, userRole)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Evaluate table-level permissions for a user
|
|
46
|
+
*/
|
|
47
|
+
export function evaluateTablePermissions(
|
|
48
|
+
tablePermissions: TablePermissions | undefined,
|
|
49
|
+
userRole: string,
|
|
50
|
+
isAdmin: boolean
|
|
51
|
+
): Readonly<{ read: boolean; create: boolean; update: boolean; delete: boolean }> {
|
|
52
|
+
return {
|
|
53
|
+
read: checkPermissionWithAdminOverride(isAdmin, tablePermissions?.read, userRole),
|
|
54
|
+
create: checkPermissionWithAdminOverride(isAdmin, tablePermissions?.create, userRole),
|
|
55
|
+
update: checkPermissionWithAdminOverride(isAdmin, tablePermissions?.update, userRole),
|
|
56
|
+
// eslint-disable-next-line drizzle/enforce-delete-with-where -- This is accessing a property, not a Drizzle delete operation
|
|
57
|
+
delete: checkPermissionWithAdminOverride(isAdmin, tablePermissions?.delete, userRole),
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Evaluate field-level permissions for a user
|
|
63
|
+
*/
|
|
64
|
+
export function evaluateFieldPermissions(
|
|
65
|
+
fieldPerms: TableFieldPermissions | undefined,
|
|
66
|
+
userRole: string,
|
|
67
|
+
isAdmin: boolean
|
|
68
|
+
): Record<string, { read: boolean; write: boolean }> {
|
|
69
|
+
const fields = fieldPerms ?? []
|
|
70
|
+
return Object.fromEntries(
|
|
71
|
+
fields.map((fieldPerm) => [
|
|
72
|
+
fieldPerm.field,
|
|
73
|
+
{
|
|
74
|
+
read: checkPermissionWithAdminOverride(isAdmin, fieldPerm.read, userRole),
|
|
75
|
+
write: checkPermissionWithAdminOverride(isAdmin, fieldPerm.write, userRole),
|
|
76
|
+
},
|
|
77
|
+
])
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Check if user has role-based create permission for a table
|
|
83
|
+
* Returns true if permission granted, false if denied
|
|
84
|
+
*
|
|
85
|
+
* Permission logic:
|
|
86
|
+
* - Viewers: denied by default (tables must explicitly grant viewer create access)
|
|
87
|
+
* - Other roles: allowed by default (unless table restricts with role-based permissions)
|
|
88
|
+
*
|
|
89
|
+
* Supports permission inheritance via the `inherit` field.
|
|
90
|
+
*/
|
|
91
|
+
export function hasCreatePermission(
|
|
92
|
+
table:
|
|
93
|
+
| Readonly<{
|
|
94
|
+
name: string
|
|
95
|
+
permissions?: Readonly<{
|
|
96
|
+
create?: unknown
|
|
97
|
+
inherit?: string
|
|
98
|
+
override?: { create?: unknown }
|
|
99
|
+
}>
|
|
100
|
+
}>
|
|
101
|
+
| undefined,
|
|
102
|
+
userRole: string,
|
|
103
|
+
allTables?: readonly Readonly<{ name: string; permissions?: TablePermissions }>[]
|
|
104
|
+
): boolean {
|
|
105
|
+
const effectivePerms = getEffectivePermissions(table, allTables) as
|
|
106
|
+
| Readonly<{ create?: unknown }>
|
|
107
|
+
| undefined
|
|
108
|
+
|
|
109
|
+
if (inheritanceFailed(table, allTables, effectivePerms)) return false
|
|
110
|
+
if (userRole === 'viewer') return false
|
|
111
|
+
|
|
112
|
+
const createPermission = effectivePerms?.create
|
|
113
|
+
if (!createPermission || !Array.isArray(createPermission)) return true
|
|
114
|
+
return createPermission.includes(userRole)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check if user has delete permission for the table
|
|
119
|
+
*
|
|
120
|
+
* Supports permission inheritance via the `inherit` field.
|
|
121
|
+
*/
|
|
122
|
+
export function hasDeletePermission(
|
|
123
|
+
table:
|
|
124
|
+
| Readonly<{
|
|
125
|
+
name: string
|
|
126
|
+
permissions?: Readonly<{
|
|
127
|
+
delete?: unknown
|
|
128
|
+
inherit?: string
|
|
129
|
+
override?: { delete?: unknown }
|
|
130
|
+
}>
|
|
131
|
+
}>
|
|
132
|
+
| undefined,
|
|
133
|
+
userRole: string,
|
|
134
|
+
allTables?: readonly Readonly<{ name: string; permissions?: TablePermissions }>[]
|
|
135
|
+
): boolean {
|
|
136
|
+
const effectivePerms = getEffectivePermissions(table, allTables) as
|
|
137
|
+
| Readonly<{ delete?: unknown }>
|
|
138
|
+
| undefined
|
|
139
|
+
|
|
140
|
+
if (inheritanceFailed(table, allTables, effectivePerms)) return false
|
|
141
|
+
|
|
142
|
+
// eslint-disable-next-line drizzle/enforce-delete-with-where -- This is not a Drizzle delete operation, it's accessing a property
|
|
143
|
+
const deletePermission = effectivePerms?.delete
|
|
144
|
+
|
|
145
|
+
if (userRole === 'viewer') {
|
|
146
|
+
return Array.isArray(deletePermission) && deletePermission.includes(userRole)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!deletePermission || !Array.isArray(deletePermission)) return true
|
|
150
|
+
return deletePermission.includes(userRole)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Check if user has update permission for a table
|
|
155
|
+
* Returns true if permission granted, false if denied
|
|
156
|
+
*
|
|
157
|
+
* Note: When no explicit permissions are defined:
|
|
158
|
+
* - Admins and members: allowed by default
|
|
159
|
+
* - Viewers: denied by default (tables must explicitly grant viewer update access)
|
|
160
|
+
*
|
|
161
|
+
* Supports permission inheritance via the `inherit` field.
|
|
162
|
+
*/
|
|
163
|
+
export function hasUpdatePermission(
|
|
164
|
+
table:
|
|
165
|
+
| Readonly<{
|
|
166
|
+
name: string
|
|
167
|
+
permissions?: Readonly<{
|
|
168
|
+
update?: unknown
|
|
169
|
+
inherit?: string
|
|
170
|
+
override?: { update?: unknown }
|
|
171
|
+
}>
|
|
172
|
+
}>
|
|
173
|
+
| undefined,
|
|
174
|
+
userRole: string,
|
|
175
|
+
allTables?: readonly Readonly<{ name: string; permissions?: TablePermissions }>[]
|
|
176
|
+
): boolean {
|
|
177
|
+
const effectivePerms = getEffectivePermissions(table, allTables) as
|
|
178
|
+
| Readonly<{ update?: unknown }>
|
|
179
|
+
| undefined
|
|
180
|
+
|
|
181
|
+
if (inheritanceFailed(table, allTables, effectivePerms)) return false
|
|
182
|
+
|
|
183
|
+
const updatePermission = effectivePerms?.update
|
|
184
|
+
|
|
185
|
+
if (Array.isArray(updatePermission)) {
|
|
186
|
+
return updatePermission.includes(userRole)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (userRole === 'viewer') return false
|
|
190
|
+
|
|
191
|
+
return true
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Resolve effective permissions considering inheritance
|
|
196
|
+
*/
|
|
197
|
+
function getEffectivePermissions(
|
|
198
|
+
table: Readonly<{ name: string; permissions?: unknown }> | undefined,
|
|
199
|
+
allTables: readonly Readonly<{ name: string; permissions?: unknown }>[] | undefined
|
|
200
|
+
): unknown {
|
|
201
|
+
if (!allTables || !table) return table?.permissions
|
|
202
|
+
|
|
203
|
+
const tableWithInheritance = table as Readonly<{
|
|
204
|
+
name: string
|
|
205
|
+
permissions?: Readonly<{ inherit?: string }>
|
|
206
|
+
}>
|
|
207
|
+
|
|
208
|
+
if (!tableWithInheritance.permissions?.inherit) {
|
|
209
|
+
return table.permissions
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
return resolveInheritedPermissions(
|
|
214
|
+
table as Readonly<{ name: string; permissions?: TablePermissions }>,
|
|
215
|
+
allTables as readonly Readonly<{ name: string; permissions?: TablePermissions }>[]
|
|
216
|
+
)
|
|
217
|
+
} catch {
|
|
218
|
+
return undefined
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Check if inheritance resolution failed
|
|
224
|
+
*/
|
|
225
|
+
function inheritanceFailed(
|
|
226
|
+
table: Readonly<{ permissions?: Readonly<{ inherit?: string }> }> | undefined,
|
|
227
|
+
allTables: readonly unknown[] | undefined,
|
|
228
|
+
effectivePermissions: unknown
|
|
229
|
+
): boolean {
|
|
230
|
+
return Boolean(allTables && table?.permissions?.inherit && !effectivePermissions)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Check if circular inheritance exists
|
|
235
|
+
*/
|
|
236
|
+
function hasCircularInheritance(tableName: string, visited: ReadonlySet<string>): boolean {
|
|
237
|
+
return visited.has(tableName)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Find parent table by name
|
|
242
|
+
*/
|
|
243
|
+
function findParentTable(
|
|
244
|
+
parentName: string | undefined,
|
|
245
|
+
allTables: readonly Readonly<{ name: string; permissions?: TablePermissions }>[]
|
|
246
|
+
): Readonly<{ name: string; permissions?: TablePermissions }> | undefined {
|
|
247
|
+
if (!parentName) return undefined
|
|
248
|
+
return allTables.find((t) => t.name === parentName)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Merge a single permission property with override support
|
|
253
|
+
*/
|
|
254
|
+
function mergePermission<T>(
|
|
255
|
+
overrideValue: T | undefined,
|
|
256
|
+
currentValue: T | undefined,
|
|
257
|
+
parentValue: T | undefined
|
|
258
|
+
): T | undefined {
|
|
259
|
+
return overrideValue ?? currentValue ?? parentValue
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Merge parent and current permissions with override support
|
|
264
|
+
*/
|
|
265
|
+
function mergePermissions(
|
|
266
|
+
permissions: TablePermissions,
|
|
267
|
+
parentPermissions: TablePermissions
|
|
268
|
+
): TablePermissions {
|
|
269
|
+
const { override, read, comment, create, update, delete: deletePerms, fields } = permissions
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
read: mergePermission(override?.read, read, parentPermissions.read),
|
|
273
|
+
comment: mergePermission(override?.comment, comment, parentPermissions.comment),
|
|
274
|
+
create: mergePermission(override?.create, create, parentPermissions.create),
|
|
275
|
+
update: mergePermission(override?.update, update, parentPermissions.update),
|
|
276
|
+
// eslint-disable-next-line drizzle/enforce-delete-with-where -- This is accessing a property, not a Drizzle delete operation
|
|
277
|
+
delete: mergePermission(override?.delete, deletePerms, parentPermissions.delete),
|
|
278
|
+
fields: fields ?? parentPermissions.fields,
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Resolve inherited permissions for a table
|
|
284
|
+
*
|
|
285
|
+
* Recursively resolves permissions by following the inheritance chain.
|
|
286
|
+
* Handles circular inheritance detection and merges override permissions.
|
|
287
|
+
*
|
|
288
|
+
* @param table - The table to resolve permissions for
|
|
289
|
+
* @param allTables - All tables in the app (for parent lookup)
|
|
290
|
+
* @param visited - Set of visited table names (for circular detection)
|
|
291
|
+
* @returns Resolved permissions or undefined if inheritance chain is invalid
|
|
292
|
+
* @throws Error if circular inheritance detected or parent table not found
|
|
293
|
+
*/
|
|
294
|
+
export function resolveInheritedPermissions(
|
|
295
|
+
table: Readonly<{ name: string; permissions?: TablePermissions }> | undefined,
|
|
296
|
+
allTables: readonly Readonly<{ name: string; permissions?: TablePermissions }>[],
|
|
297
|
+
visited: ReadonlySet<string> = new Set()
|
|
298
|
+
): TablePermissions | undefined {
|
|
299
|
+
if (!table?.permissions) return undefined
|
|
300
|
+
|
|
301
|
+
const { permissions } = table
|
|
302
|
+
|
|
303
|
+
// If no inheritance, return current permissions
|
|
304
|
+
if (!permissions.inherit) {
|
|
305
|
+
return permissions
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Circular inheritance detection
|
|
309
|
+
if (hasCircularInheritance(table.name, visited)) {
|
|
310
|
+
// Return undefined to indicate error (caught by callers)
|
|
311
|
+
return undefined
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Find parent table
|
|
315
|
+
const parentTable = findParentTable(permissions.inherit, allTables)
|
|
316
|
+
if (!parentTable) {
|
|
317
|
+
// Return undefined to indicate error (caught by callers)
|
|
318
|
+
return undefined
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Recursively resolve parent permissions
|
|
322
|
+
const parentPermissions = resolveInheritedPermissions(
|
|
323
|
+
parentTable,
|
|
324
|
+
allTables,
|
|
325
|
+
new Set([...visited, table.name])
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
if (!parentPermissions) return permissions
|
|
329
|
+
|
|
330
|
+
// Merge parent permissions with current permissions (current takes precedence)
|
|
331
|
+
return mergePermissions(permissions, parentPermissions)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Check if user has read permission for a table
|
|
336
|
+
* Returns true if permission granted, false if denied
|
|
337
|
+
*
|
|
338
|
+
* Note: When no explicit permissions are defined:
|
|
339
|
+
* - Admins and members: allowed by default
|
|
340
|
+
* - Viewers: denied by default (tables must explicitly grant viewer access)
|
|
341
|
+
*
|
|
342
|
+
* Supports permission inheritance via the `inherit` field.
|
|
343
|
+
*/
|
|
344
|
+
export function hasReadPermission(
|
|
345
|
+
table:
|
|
346
|
+
| Readonly<{
|
|
347
|
+
name: string
|
|
348
|
+
permissions?: Readonly<{ read?: unknown; inherit?: string; override?: { read?: unknown } }>
|
|
349
|
+
}>
|
|
350
|
+
| undefined,
|
|
351
|
+
userRole: string,
|
|
352
|
+
allTables?: readonly Readonly<{ name: string; permissions?: TablePermissions }>[]
|
|
353
|
+
): boolean {
|
|
354
|
+
const effectivePerms = getEffectivePermissions(table, allTables) as
|
|
355
|
+
| Readonly<{ read?: unknown }>
|
|
356
|
+
| undefined
|
|
357
|
+
|
|
358
|
+
if (inheritanceFailed(table, allTables, effectivePerms)) return false
|
|
359
|
+
|
|
360
|
+
const readPermission = effectivePerms?.read
|
|
361
|
+
|
|
362
|
+
if (Array.isArray(readPermission)) {
|
|
363
|
+
return readPermission.includes(userRole)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (readPermission === 'all') return true
|
|
367
|
+
if (readPermission === 'authenticated') return true
|
|
368
|
+
|
|
369
|
+
if (userRole === 'viewer') return false
|
|
370
|
+
|
|
371
|
+
return true
|
|
372
|
+
}
|
|
@@ -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 { Schema } from 'effect'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Table Permission Schema
|
|
12
|
+
*
|
|
13
|
+
* Simplified permission value for a single CRUD operation.
|
|
14
|
+
* Accepts one of 3 formats:
|
|
15
|
+
*
|
|
16
|
+
* - `'all'` — Everyone (including unauthenticated users)
|
|
17
|
+
* - `'authenticated'` — Any logged-in user
|
|
18
|
+
* - `['admin', 'editor']` — Specific role names (array)
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```yaml
|
|
22
|
+
* permissions:
|
|
23
|
+
* read: all
|
|
24
|
+
* comment: authenticated
|
|
25
|
+
* create: ['admin', 'editor']
|
|
26
|
+
* update: ['admin', 'editor']
|
|
27
|
+
* delete: ['admin']
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export const TablePermissionSchema = Schema.Union(
|
|
31
|
+
Schema.Literal('all'),
|
|
32
|
+
Schema.Literal('authenticated'),
|
|
33
|
+
Schema.Array(Schema.String).pipe(
|
|
34
|
+
Schema.minItems(1),
|
|
35
|
+
Schema.annotations({
|
|
36
|
+
description: 'Array of role names that have access (e.g., admin, editor). At least one role.',
|
|
37
|
+
examples: [['admin'], ['admin', 'editor'], ['admin', 'member', 'viewer']],
|
|
38
|
+
})
|
|
39
|
+
)
|
|
40
|
+
).pipe(
|
|
41
|
+
Schema.annotations({
|
|
42
|
+
title: 'Table Permission',
|
|
43
|
+
description:
|
|
44
|
+
"Permission value for a single operation. 'all' (everyone), 'authenticated' (logged-in users), or role array ['admin', 'editor'].",
|
|
45
|
+
examples: ['all', 'authenticated', ['admin'], ['admin', 'editor']],
|
|
46
|
+
})
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
export type TablePermission = Schema.Schema.Type<typeof TablePermissionSchema>
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
* Primary Key
|
|
12
|
+
*
|
|
13
|
+
* Primary key configuration for the table. The primary key uniquely identifies each row and is automatically indexed.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* {
|
|
18
|
+
* "type": "auto-increment",
|
|
19
|
+
* "field": "id"
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export const PrimaryKeySchema = Schema.Struct({
|
|
24
|
+
type: Schema.String.pipe(
|
|
25
|
+
Schema.minLength(1),
|
|
26
|
+
Schema.annotations({
|
|
27
|
+
description:
|
|
28
|
+
"Primary key generation strategy. 'auto-increment' uses sequential integers (1, 2, 3...), 'uuid' generates random unique identifiers, 'composite' uses multiple fields together.",
|
|
29
|
+
})
|
|
30
|
+
),
|
|
31
|
+
field: Schema.optional(
|
|
32
|
+
Schema.String.pipe(
|
|
33
|
+
Schema.pattern(/^[a-z][a-z0-9_]*$/, {
|
|
34
|
+
message: () =>
|
|
35
|
+
"Field name for single-column primary key. Only used with 'auto-increment' or 'uuid' type.",
|
|
36
|
+
}),
|
|
37
|
+
Schema.annotations({
|
|
38
|
+
description:
|
|
39
|
+
"Field name for single-column primary key. Only used with 'auto-increment' or 'uuid' type.",
|
|
40
|
+
examples: ['id', 'user_id', 'product_id'],
|
|
41
|
+
})
|
|
42
|
+
)
|
|
43
|
+
),
|
|
44
|
+
fields: Schema.optional(
|
|
45
|
+
Schema.Array(
|
|
46
|
+
Schema.String.pipe(Schema.minLength(1, { message: () => 'This field is required' }))
|
|
47
|
+
)
|
|
48
|
+
),
|
|
49
|
+
}).pipe(
|
|
50
|
+
Schema.annotations({
|
|
51
|
+
title: 'Primary Key',
|
|
52
|
+
description:
|
|
53
|
+
'Primary key configuration for the table. The primary key uniquely identifies each row and is automatically indexed.',
|
|
54
|
+
examples: [
|
|
55
|
+
{ type: 'auto-increment', field: 'id' },
|
|
56
|
+
{ type: 'uuid', field: 'id' },
|
|
57
|
+
{ type: 'composite', fields: ['tenant_id', 'user_id'] },
|
|
58
|
+
],
|
|
59
|
+
})
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
export type PrimaryKey = Schema.Schema.Type<typeof PrimaryKeySchema>
|
|
@@ -0,0 +1,168 @@
|
|
|
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 { detectCycles } from './cycle-detection'
|
|
9
|
+
import { FORMULA_KEYWORDS } from './formula-keywords'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Special field references that are always available in formulas.
|
|
13
|
+
* These are system-managed fields that exist on all tables.
|
|
14
|
+
*/
|
|
15
|
+
export const SPECIAL_FIELDS = new Set([
|
|
16
|
+
'id',
|
|
17
|
+
'created_at',
|
|
18
|
+
'updated_at',
|
|
19
|
+
'deleted_at',
|
|
20
|
+
]) as ReadonlySet<string>
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Validate formula syntax to detect common syntax errors.
|
|
24
|
+
* Checks for patterns that would cause SQL syntax errors.
|
|
25
|
+
*
|
|
26
|
+
* @param formula - The formula expression to validate
|
|
27
|
+
* @returns Error message if invalid, undefined if valid
|
|
28
|
+
*/
|
|
29
|
+
const validateFormulaSyntax = (formula: string): string | undefined => {
|
|
30
|
+
// Remove string literals and regex patterns to avoid false positives
|
|
31
|
+
// String literals: 'text' or "text"
|
|
32
|
+
// Regex patterns: text ~ 'pattern' or text ~ '^pattern$'
|
|
33
|
+
const withoutLiterals = formula
|
|
34
|
+
.replace(/'[^']*'/g, '') // Remove single-quoted strings
|
|
35
|
+
.replace(/"[^"]*"/g, '') // Remove double-quoted strings
|
|
36
|
+
|
|
37
|
+
// Check for consecutive operators (e.g., "* *", "+ +", "- -")
|
|
38
|
+
// Only check outside of string literals and regex patterns
|
|
39
|
+
const consecutiveOperatorPattern = /[+\-*/%]\s*[+\-*/%]/
|
|
40
|
+
if (consecutiveOperatorPattern.test(withoutLiterals)) {
|
|
41
|
+
return 'Invalid formula syntax: consecutive operators detected'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check for unmatched parentheses
|
|
45
|
+
const openParens = (formula.match(/\(/g) || []).length
|
|
46
|
+
const closeParens = (formula.match(/\)/g) || []).length
|
|
47
|
+
if (openParens !== closeParens) {
|
|
48
|
+
return 'Invalid formula syntax: unmatched parentheses'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Check for empty parentheses
|
|
52
|
+
if (/\(\s*\)/.test(formula)) {
|
|
53
|
+
return 'Invalid formula syntax: empty parentheses'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Extract potential field references from a formula expression.
|
|
61
|
+
* This is a simplified parser that extracts identifiers (words) from the formula,
|
|
62
|
+
* excluding string literals (content within quotes).
|
|
63
|
+
* It doesn't handle complex syntax but catches common field reference patterns.
|
|
64
|
+
*
|
|
65
|
+
* @param formula - The formula expression to parse
|
|
66
|
+
* @returns Array of field names referenced in the formula
|
|
67
|
+
*/
|
|
68
|
+
export const extractFieldReferences = (formula: string): ReadonlyArray<string> => {
|
|
69
|
+
// Remove single-quoted and double-quoted string literals to avoid treating
|
|
70
|
+
// literal values as field names (e.g., 'World' in STRPOS(text, 'World'))
|
|
71
|
+
const withoutStringLiterals = formula
|
|
72
|
+
.replace(/'[^']*'/g, '') // Remove single-quoted strings
|
|
73
|
+
.replace(/"[^"]*"/g, '') // Remove double-quoted strings
|
|
74
|
+
|
|
75
|
+
// Match word characters (field names) - exclude function names and operators
|
|
76
|
+
// This regex matches identifiers that could be field names
|
|
77
|
+
const identifierPattern = /\b([a-z_][a-z0-9_]*)\b/gi
|
|
78
|
+
const matches = withoutStringLiterals.match(identifierPattern) || []
|
|
79
|
+
|
|
80
|
+
return matches
|
|
81
|
+
.map((match) => match.toLowerCase())
|
|
82
|
+
.filter((identifier) => !FORMULA_KEYWORDS.has(identifier))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Detect circular dependencies in formula fields using depth-first search.
|
|
87
|
+
* A circular dependency exists when a formula field references itself directly or indirectly
|
|
88
|
+
* through a chain of other formula fields.
|
|
89
|
+
*
|
|
90
|
+
* @param fields - Array of fields to validate
|
|
91
|
+
* @returns Array of field names involved in circular dependencies, or empty array if none found
|
|
92
|
+
*/
|
|
93
|
+
const detectCircularDependencies = (
|
|
94
|
+
fields: ReadonlyArray<{ readonly name: string; readonly type: string; readonly formula?: string }>
|
|
95
|
+
): ReadonlyArray<string> => {
|
|
96
|
+
// Build dependency graph: field name -> fields it references
|
|
97
|
+
const dependencyGraph: ReadonlyMap<string, ReadonlyArray<string>> = new Map(
|
|
98
|
+
fields
|
|
99
|
+
.filter(
|
|
100
|
+
(field): field is typeof field & { formula: string } =>
|
|
101
|
+
'formula' in field && typeof field.formula === 'string'
|
|
102
|
+
)
|
|
103
|
+
.map((field) => [field.name, extractFieldReferences(field.formula)] as const)
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
// Use shared cycle detection utility
|
|
107
|
+
return detectCycles(dependencyGraph)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Validate formula fields in a table (syntax, field references, circular dependencies).
|
|
112
|
+
*
|
|
113
|
+
* @param fields - Array of fields to validate
|
|
114
|
+
* @returns Error object if invalid, undefined if valid
|
|
115
|
+
*/
|
|
116
|
+
export const validateFormulaFields = (
|
|
117
|
+
fields: ReadonlyArray<{ readonly name: string; readonly type: string; readonly formula?: string }>
|
|
118
|
+
): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
|
|
119
|
+
const fieldNames = new Set(fields.map((field) => field.name))
|
|
120
|
+
const formulaFields = fields.filter(
|
|
121
|
+
(field): field is typeof field & { readonly formula: string } =>
|
|
122
|
+
field.type === 'formula' && typeof field.formula === 'string'
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
// Validate formula syntax first (before checking field references)
|
|
126
|
+
const syntaxError = formulaFields
|
|
127
|
+
.map((formulaField) => ({
|
|
128
|
+
field: formulaField,
|
|
129
|
+
error: validateFormulaSyntax(formulaField.formula),
|
|
130
|
+
}))
|
|
131
|
+
.find((result) => result.error !== undefined)
|
|
132
|
+
|
|
133
|
+
if (syntaxError?.error) {
|
|
134
|
+
return {
|
|
135
|
+
message: syntaxError.error,
|
|
136
|
+
path: ['fields'],
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Find the first invalid field reference across all formula fields
|
|
141
|
+
const invalidReference = formulaFields
|
|
142
|
+
.flatMap((formulaField) => {
|
|
143
|
+
const referencedFields = extractFieldReferences(formulaField.formula)
|
|
144
|
+
const invalidField = referencedFields.find(
|
|
145
|
+
(refField) => !fieldNames.has(refField) && !SPECIAL_FIELDS.has(refField)
|
|
146
|
+
)
|
|
147
|
+
return invalidField ? [{ formulaField, invalidField }] : []
|
|
148
|
+
})
|
|
149
|
+
.at(0)
|
|
150
|
+
|
|
151
|
+
if (invalidReference) {
|
|
152
|
+
return {
|
|
153
|
+
message: `Invalid field reference: field '${invalidReference.invalidField}' not found in formula '${invalidReference.formulaField.formula}'`,
|
|
154
|
+
path: ['fields'],
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Detect circular dependencies in formula fields
|
|
159
|
+
const circularFields = detectCircularDependencies(fields)
|
|
160
|
+
if (circularFields.length > 0) {
|
|
161
|
+
return {
|
|
162
|
+
message: `Circular dependency detected in formula fields: ${circularFields.join(' -> ')}`,
|
|
163
|
+
path: ['fields'],
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return undefined
|
|
168
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
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
|
+
* Validate that all index fields exist in the table.
|
|
10
|
+
*
|
|
11
|
+
* @param indexes - Array of index definitions
|
|
12
|
+
* @param fieldNames - Set of valid field names
|
|
13
|
+
* @returns Validation error object if invalid, undefined if valid
|
|
14
|
+
*/
|
|
15
|
+
export const validateIndexes = (
|
|
16
|
+
indexes: ReadonlyArray<{
|
|
17
|
+
readonly name: string
|
|
18
|
+
readonly fields: ReadonlyArray<string>
|
|
19
|
+
}>,
|
|
20
|
+
fieldNames: ReadonlySet<string>
|
|
21
|
+
): { readonly message: string; readonly path: ReadonlyArray<string> } | undefined => {
|
|
22
|
+
const invalidIndex = indexes
|
|
23
|
+
.flatMap((index) =>
|
|
24
|
+
index.fields
|
|
25
|
+
.filter((fieldName) => !fieldNames.has(fieldName))
|
|
26
|
+
.map((fieldName) => ({ indexName: index.name, fieldName }))
|
|
27
|
+
)
|
|
28
|
+
.at(0)
|
|
29
|
+
|
|
30
|
+
if (invalidIndex) {
|
|
31
|
+
return {
|
|
32
|
+
message: `Index "${invalidIndex.indexName}" references non-existent column "${invalidIndex.fieldName}"`,
|
|
33
|
+
path: ['indexes'],
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return undefined
|
|
38
|
+
}
|