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,403 @@
|
|
|
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 { sanitizeTableName } from '../field-utils'
|
|
9
|
+
import {
|
|
10
|
+
generateReverseLookupExpression,
|
|
11
|
+
generateManyToManyLookupExpression,
|
|
12
|
+
generateForwardLookupExpression,
|
|
13
|
+
} from './lookup-expressions'
|
|
14
|
+
import {
|
|
15
|
+
buildWhereClause,
|
|
16
|
+
mapAggregationToPostgres,
|
|
17
|
+
getDefaultValueForAggregation,
|
|
18
|
+
} from './lookup-view-helpers'
|
|
19
|
+
import {
|
|
20
|
+
getBaseFields,
|
|
21
|
+
generateInsertTrigger,
|
|
22
|
+
generateUpdateTrigger,
|
|
23
|
+
generateDeleteTrigger,
|
|
24
|
+
} from './lookup-view-triggers'
|
|
25
|
+
import type { Table } from '@/domain/models/app/table'
|
|
26
|
+
import type { Fields } from '@/domain/models/app/table/fields'
|
|
27
|
+
import type { ViewFilterCondition } from '@/domain/models/app/table/views/filters'
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Check if a field is a lookup field
|
|
31
|
+
*/
|
|
32
|
+
const isLookupField = (
|
|
33
|
+
field: Fields[number]
|
|
34
|
+
): field is Fields[number] & {
|
|
35
|
+
type: 'lookup'
|
|
36
|
+
relationshipField: string
|
|
37
|
+
relatedField: string
|
|
38
|
+
filters?: ViewFilterCondition
|
|
39
|
+
} =>
|
|
40
|
+
field.type === 'lookup' &&
|
|
41
|
+
'relationshipField' in field &&
|
|
42
|
+
'relatedField' in field &&
|
|
43
|
+
typeof field.relationshipField === 'string' &&
|
|
44
|
+
typeof field.relatedField === 'string'
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if a field is a rollup field
|
|
48
|
+
*/
|
|
49
|
+
const isRollupField = (
|
|
50
|
+
field: Fields[number]
|
|
51
|
+
): field is Fields[number] & {
|
|
52
|
+
type: 'rollup'
|
|
53
|
+
relationshipField: string
|
|
54
|
+
relatedField: string
|
|
55
|
+
aggregation: string
|
|
56
|
+
filters?: ViewFilterCondition
|
|
57
|
+
} =>
|
|
58
|
+
field.type === 'rollup' &&
|
|
59
|
+
'relationshipField' in field &&
|
|
60
|
+
'relatedField' in field &&
|
|
61
|
+
'aggregation' in field &&
|
|
62
|
+
typeof field.relationshipField === 'string' &&
|
|
63
|
+
typeof field.relatedField === 'string' &&
|
|
64
|
+
typeof field.aggregation === 'string'
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check if a field is a count field
|
|
68
|
+
*/
|
|
69
|
+
const isCountField = (
|
|
70
|
+
field: Fields[number]
|
|
71
|
+
): field is Fields[number] & {
|
|
72
|
+
type: 'count'
|
|
73
|
+
relationshipField: string
|
|
74
|
+
conditions?: readonly ViewFilterCondition[]
|
|
75
|
+
} =>
|
|
76
|
+
field.type === 'count' &&
|
|
77
|
+
'relationshipField' in field &&
|
|
78
|
+
typeof field.relationshipField === 'string'
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if a table has any lookup fields
|
|
82
|
+
*/
|
|
83
|
+
export const hasLookupFields = (table: Table): boolean =>
|
|
84
|
+
table.fields.some((field) => isLookupField(field))
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check if a table has any rollup fields
|
|
88
|
+
*/
|
|
89
|
+
export const hasRollupFields = (table: Table): boolean =>
|
|
90
|
+
table.fields.some((field) => isRollupField(field))
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Check if a table has any count fields
|
|
94
|
+
*/
|
|
95
|
+
export const hasCountFields = (table: Table): boolean =>
|
|
96
|
+
table.fields.some((field) => isCountField(field))
|
|
97
|
+
|
|
98
|
+
/** Check if relationship field is many-to-many with valid related table */
|
|
99
|
+
const isManyToMany = (field: Fields[number]): field is Fields[number] & { relatedTable: string } =>
|
|
100
|
+
'relationType' in field &&
|
|
101
|
+
field.relationType === 'many-to-many' &&
|
|
102
|
+
'relatedTable' in field &&
|
|
103
|
+
typeof field.relatedTable === 'string'
|
|
104
|
+
|
|
105
|
+
/** Check if relationship field has valid related table */
|
|
106
|
+
const hasRelatedTable = (
|
|
107
|
+
field: Fields[number]
|
|
108
|
+
): field is Fields[number] & { relatedTable: string } =>
|
|
109
|
+
'relatedTable' in field && typeof field.relatedTable === 'string'
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Generate lookup column expression
|
|
113
|
+
* Handles forward (many-to-one), reverse (one-to-many), and many-to-many lookups
|
|
114
|
+
*/
|
|
115
|
+
const generateLookupExpression = (
|
|
116
|
+
lookupField: Fields[number] & {
|
|
117
|
+
readonly type: 'lookup'
|
|
118
|
+
readonly relationshipField: string
|
|
119
|
+
readonly relatedField: string
|
|
120
|
+
readonly filters?: ViewFilterCondition
|
|
121
|
+
},
|
|
122
|
+
tableAlias: string,
|
|
123
|
+
allFields: readonly Fields[number][],
|
|
124
|
+
actualTableName: string
|
|
125
|
+
): string => {
|
|
126
|
+
const { name: lookupName, relationshipField, relatedField, filters } = lookupField
|
|
127
|
+
const relationshipFieldDef = allFields.find((f) => f.name === relationshipField)
|
|
128
|
+
|
|
129
|
+
// Reverse lookup (relationship field not in current table)
|
|
130
|
+
if (!relationshipFieldDef || relationshipFieldDef.type !== 'relationship') {
|
|
131
|
+
return generateReverseLookupExpression({
|
|
132
|
+
lookupName,
|
|
133
|
+
relationshipField,
|
|
134
|
+
relatedField,
|
|
135
|
+
filters,
|
|
136
|
+
tableAlias,
|
|
137
|
+
actualTableName,
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Many-to-many lookup (via junction table)
|
|
142
|
+
if (isManyToMany(relationshipFieldDef)) {
|
|
143
|
+
return generateManyToManyLookupExpression({
|
|
144
|
+
lookupName,
|
|
145
|
+
relatedTable: relationshipFieldDef.relatedTable,
|
|
146
|
+
relatedField,
|
|
147
|
+
filters,
|
|
148
|
+
tableAlias,
|
|
149
|
+
actualTableName,
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Forward lookup (many-to-one)
|
|
154
|
+
if (hasRelatedTable(relationshipFieldDef)) {
|
|
155
|
+
return generateForwardLookupExpression({
|
|
156
|
+
lookupName,
|
|
157
|
+
relationshipField,
|
|
158
|
+
relatedTable: relationshipFieldDef.relatedTable,
|
|
159
|
+
relatedField,
|
|
160
|
+
filters,
|
|
161
|
+
tableAlias,
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Fallback: NULL if relationship is invalid
|
|
166
|
+
return `NULL AS ${lookupName}`
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Generate rollup column expression with aggregation
|
|
171
|
+
*/
|
|
172
|
+
const generateRollupExpression = (
|
|
173
|
+
rollupField: Fields[number] & {
|
|
174
|
+
readonly type: 'rollup'
|
|
175
|
+
readonly relationshipField: string
|
|
176
|
+
readonly relatedField: string
|
|
177
|
+
readonly aggregation: string
|
|
178
|
+
readonly filters?: ViewFilterCondition
|
|
179
|
+
},
|
|
180
|
+
tableName: string,
|
|
181
|
+
allFields: readonly Fields[number][]
|
|
182
|
+
): string => {
|
|
183
|
+
const { name: rollupName, relationshipField, relatedField, aggregation, filters } = rollupField
|
|
184
|
+
|
|
185
|
+
const relationshipFieldDef = allFields.find((f) => f.name === relationshipField)
|
|
186
|
+
|
|
187
|
+
if (!relationshipFieldDef || relationshipFieldDef.type !== 'relationship') {
|
|
188
|
+
return `${getDefaultValueForAggregation(aggregation)} AS ${rollupName}`
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (
|
|
192
|
+
!('relatedTable' in relationshipFieldDef) ||
|
|
193
|
+
typeof relationshipFieldDef.relatedTable !== 'string'
|
|
194
|
+
) {
|
|
195
|
+
return `${getDefaultValueForAggregation(aggregation)} AS ${rollupName}`
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const { relatedTable } = relationshipFieldDef
|
|
199
|
+
const alias = `${relatedTable}_for_${rollupName}`
|
|
200
|
+
|
|
201
|
+
const aggregationExpr = mapAggregationToPostgres(aggregation, `${alias}.${relatedField}`)
|
|
202
|
+
const defaultValue = getDefaultValueForAggregation(aggregation)
|
|
203
|
+
|
|
204
|
+
// Determine the foreign key column in the related table
|
|
205
|
+
// For one-to-many relationships, the foreignKey property specifies the FK column in the related table
|
|
206
|
+
// For many-to-one relationships, the relationship field itself is the FK column
|
|
207
|
+
const foreignKeyColumn =
|
|
208
|
+
'foreignKey' in relationshipFieldDef && typeof relationshipFieldDef.foreignKey === 'string'
|
|
209
|
+
? relationshipFieldDef.foreignKey
|
|
210
|
+
: relationshipField
|
|
211
|
+
|
|
212
|
+
const baseCondition = `${alias}.${foreignKeyColumn} = ${tableName}.id`
|
|
213
|
+
const whereConditions = filters
|
|
214
|
+
? [baseCondition, buildWhereClause(filters, alias)]
|
|
215
|
+
: [baseCondition]
|
|
216
|
+
|
|
217
|
+
const whereClause = whereConditions.join(' AND ')
|
|
218
|
+
|
|
219
|
+
// Use the VIEW name (not base table) for rollup queries
|
|
220
|
+
// The VIEW will be created after all base tables exist, so it's safe to reference
|
|
221
|
+
return `COALESCE(
|
|
222
|
+
(SELECT ${aggregationExpr}
|
|
223
|
+
FROM ${relatedTable} AS ${alias}
|
|
224
|
+
WHERE ${whereClause}),
|
|
225
|
+
${defaultValue}
|
|
226
|
+
) AS ${rollupName}`
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Generate count column expression with optional filtering
|
|
231
|
+
* Counts linked records from a relationship field, with optional conditions
|
|
232
|
+
*/
|
|
233
|
+
const generateCountExpression = (
|
|
234
|
+
countField: Fields[number] & {
|
|
235
|
+
readonly type: 'count'
|
|
236
|
+
readonly relationshipField: string
|
|
237
|
+
readonly conditions?: readonly ViewFilterCondition[]
|
|
238
|
+
},
|
|
239
|
+
tableName: string,
|
|
240
|
+
allFields: readonly Fields[number][]
|
|
241
|
+
): string => {
|
|
242
|
+
const { name: countName, relationshipField, conditions } = countField
|
|
243
|
+
|
|
244
|
+
const relationshipFieldDef = allFields.find((f) => f.name === relationshipField)
|
|
245
|
+
|
|
246
|
+
// Count field must reference a valid relationship field in same table
|
|
247
|
+
if (!relationshipFieldDef || relationshipFieldDef.type !== 'relationship') {
|
|
248
|
+
return `0 AS ${countName}`
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (
|
|
252
|
+
!('relatedTable' in relationshipFieldDef) ||
|
|
253
|
+
typeof relationshipFieldDef.relatedTable !== 'string'
|
|
254
|
+
) {
|
|
255
|
+
return `0 AS ${countName}`
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const { relatedTable } = relationshipFieldDef
|
|
259
|
+
const alias = `${relatedTable}_for_${countName}`
|
|
260
|
+
|
|
261
|
+
// Determine the foreign key column in the related table
|
|
262
|
+
// For one-to-many relationships, the foreignKey property specifies the FK column in the related table
|
|
263
|
+
// For many-to-one relationships, the relationship field itself is the FK column
|
|
264
|
+
const foreignKeyColumn =
|
|
265
|
+
'foreignKey' in relationshipFieldDef && typeof relationshipFieldDef.foreignKey === 'string'
|
|
266
|
+
? relationshipFieldDef.foreignKey
|
|
267
|
+
: relationshipField
|
|
268
|
+
|
|
269
|
+
// Build WHERE clause with base condition + optional filter conditions
|
|
270
|
+
const baseCondition = `${alias}.${foreignKeyColumn} = ${tableName}.id`
|
|
271
|
+
|
|
272
|
+
// Convert conditions array to WHERE clauses
|
|
273
|
+
const filterConditions = conditions?.map((condition) => buildWhereClause(condition, alias)) ?? []
|
|
274
|
+
|
|
275
|
+
const whereConditions = [baseCondition, ...filterConditions]
|
|
276
|
+
const whereClause = whereConditions.join(' AND ')
|
|
277
|
+
|
|
278
|
+
// Use COALESCE to ensure 0 instead of NULL when no records match
|
|
279
|
+
return `COALESCE(
|
|
280
|
+
(SELECT COUNT(*)
|
|
281
|
+
FROM ${relatedTable} AS ${alias}
|
|
282
|
+
WHERE ${whereClause}),
|
|
283
|
+
0
|
|
284
|
+
) AS ${countName}`
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Build JOIN clauses for forward lookups (many-to-one only, not many-to-many)
|
|
289
|
+
*/
|
|
290
|
+
const buildForwardLookupJoins = (
|
|
291
|
+
lookupFields: readonly (Fields[number] & {
|
|
292
|
+
readonly type: 'lookup'
|
|
293
|
+
readonly relationshipField: string
|
|
294
|
+
readonly relatedField: string
|
|
295
|
+
readonly filters?: ViewFilterCondition
|
|
296
|
+
})[],
|
|
297
|
+
allFields: readonly Fields[number][]
|
|
298
|
+
): string => {
|
|
299
|
+
const forwardLookups = lookupFields.filter((field) => {
|
|
300
|
+
const relationshipFieldDef = allFields.find((f) => f.name === field.relationshipField)
|
|
301
|
+
return (
|
|
302
|
+
relationshipFieldDef &&
|
|
303
|
+
relationshipFieldDef.type === 'relationship' &&
|
|
304
|
+
'relatedTable' in relationshipFieldDef &&
|
|
305
|
+
'relationType' in relationshipFieldDef &&
|
|
306
|
+
relationshipFieldDef.relationType !== 'many-to-many'
|
|
307
|
+
)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
return forwardLookups
|
|
311
|
+
.map((field) => {
|
|
312
|
+
const relationshipFieldDef = allFields.find((f) => f.name === field.relationshipField)
|
|
313
|
+
if (!relationshipFieldDef || relationshipFieldDef.type !== 'relationship') {
|
|
314
|
+
return ''
|
|
315
|
+
}
|
|
316
|
+
const { relatedTable } = relationshipFieldDef as unknown as { relatedTable: string }
|
|
317
|
+
const alias = `${relatedTable}_for_${field.name}`
|
|
318
|
+
return `LEFT JOIN ${relatedTable} AS ${alias} ON ${alias}.id = base.${field.relationshipField}`
|
|
319
|
+
})
|
|
320
|
+
.filter((join) => join !== '')
|
|
321
|
+
.join('\n ')
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Generate CREATE VIEW statement for a table with lookup, rollup, and/or count fields
|
|
326
|
+
* Replaces the base table with a VIEW that includes looked-up, aggregated, and counted columns
|
|
327
|
+
* Returns empty string if table has no lookup, rollup, or count fields
|
|
328
|
+
*/
|
|
329
|
+
export const generateLookupViewSQL = (table: Table): string => {
|
|
330
|
+
const lookupFields = table.fields.filter(isLookupField)
|
|
331
|
+
const rollupFields = table.fields.filter(isRollupField)
|
|
332
|
+
const countFields = table.fields.filter(isCountField)
|
|
333
|
+
|
|
334
|
+
if (lookupFields.length === 0 && rollupFields.length === 0 && countFields.length === 0) {
|
|
335
|
+
return '' // No lookup, rollup, or count fields - no VIEW needed
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const sanitized = sanitizeTableName(table.name)
|
|
339
|
+
|
|
340
|
+
// Always include base.* to get all columns from the base table (including auto-generated id)
|
|
341
|
+
const baseFieldsWildcard = 'base.*'
|
|
342
|
+
|
|
343
|
+
// Generate expressions for lookup, rollup, and count fields
|
|
344
|
+
const lookupExpressions = lookupFields.map((field) =>
|
|
345
|
+
generateLookupExpression(field, 'base', table.fields, sanitized)
|
|
346
|
+
)
|
|
347
|
+
const rollupExpressions = rollupFields.map((field) =>
|
|
348
|
+
generateRollupExpression(field, 'base', table.fields)
|
|
349
|
+
)
|
|
350
|
+
const countExpressions = countFields.map((field) =>
|
|
351
|
+
generateCountExpression(field, 'base', table.fields)
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
// Build JOIN clauses for forward lookups
|
|
355
|
+
const joins = buildForwardLookupJoins(lookupFields, table.fields)
|
|
356
|
+
|
|
357
|
+
// Assemble the final VIEW SQL
|
|
358
|
+
const selectClause = [
|
|
359
|
+
baseFieldsWildcard,
|
|
360
|
+
...lookupExpressions,
|
|
361
|
+
...rollupExpressions,
|
|
362
|
+
...countExpressions,
|
|
363
|
+
].join(',\n ')
|
|
364
|
+
|
|
365
|
+
return `CREATE OR REPLACE VIEW ${sanitized} AS
|
|
366
|
+
SELECT
|
|
367
|
+
${selectClause}
|
|
368
|
+
FROM ${sanitized}_base AS base
|
|
369
|
+
${joins ? joins : ''}`
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Generate base table name for a table with lookup fields
|
|
374
|
+
* The actual table is named {table}_base, and the VIEW is named {table}
|
|
375
|
+
*/
|
|
376
|
+
export const getBaseTableName = (tableName: string): string => `${tableName}_base`
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Check if a table should use a VIEW (has lookup, rollup, or count fields)
|
|
380
|
+
*/
|
|
381
|
+
export const shouldUseView = (table: Table): boolean =>
|
|
382
|
+
hasLookupFields(table) || hasRollupFields(table) || hasCountFields(table)
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Generate INSTEAD OF triggers for a VIEW to make it writable
|
|
386
|
+
* These triggers redirect INSERT/UPDATE/DELETE operations to the base table
|
|
387
|
+
*/
|
|
388
|
+
export const generateLookupViewTriggers = (table: Table): readonly string[] => {
|
|
389
|
+
if (!shouldUseView(table)) {
|
|
390
|
+
return [] // No VIEW, no triggers needed
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const sanitized = sanitizeTableName(table.name)
|
|
394
|
+
const baseTableName = getBaseTableName(sanitized)
|
|
395
|
+
const viewName = sanitized
|
|
396
|
+
const baseFields = getBaseFields(table)
|
|
397
|
+
|
|
398
|
+
return [
|
|
399
|
+
...generateInsertTrigger(viewName, baseTableName, baseFields),
|
|
400
|
+
...generateUpdateTrigger(viewName, baseTableName, baseFields),
|
|
401
|
+
...generateDeleteTrigger(viewName, baseTableName),
|
|
402
|
+
]
|
|
403
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
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 { generateSqlCondition } from '../filter-operators'
|
|
9
|
+
import type { ViewFilterCondition } from '@/domain/models/app/table/views/filters'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Build WHERE clause from filter condition
|
|
13
|
+
*/
|
|
14
|
+
export const buildWhereClause = (filter: ViewFilterCondition, aliasPrefix: string): string => {
|
|
15
|
+
const { field, operator, value } = filter
|
|
16
|
+
const column = `${aliasPrefix}.${field}`
|
|
17
|
+
// Use legacy string escaping mode for backward compatibility
|
|
18
|
+
return generateSqlCondition(column, operator, value, { useEscapeSqlString: true })
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Map aggregation function name to PostgreSQL aggregate function
|
|
23
|
+
*/
|
|
24
|
+
export const mapAggregationToPostgres = (aggregation: string, relatedField: string): string => {
|
|
25
|
+
const upperAgg = aggregation.toUpperCase()
|
|
26
|
+
|
|
27
|
+
switch (upperAgg) {
|
|
28
|
+
case 'SUM':
|
|
29
|
+
return `SUM(${relatedField})`
|
|
30
|
+
case 'COUNT':
|
|
31
|
+
return `COUNT(${relatedField})`
|
|
32
|
+
case 'AVG':
|
|
33
|
+
return `AVG(${relatedField})`
|
|
34
|
+
case 'MIN':
|
|
35
|
+
return `MIN(${relatedField})`
|
|
36
|
+
case 'MAX':
|
|
37
|
+
return `MAX(${relatedField})`
|
|
38
|
+
case 'COUNTA':
|
|
39
|
+
return `COUNT(CASE WHEN ${relatedField} IS NOT NULL AND ${relatedField} != '' THEN 1 END)`
|
|
40
|
+
case 'COUNTALL':
|
|
41
|
+
return `COUNT(*)`
|
|
42
|
+
case 'ARRAYUNIQUE':
|
|
43
|
+
return `ARRAY_AGG(DISTINCT ${relatedField} ORDER BY ${relatedField})`
|
|
44
|
+
default:
|
|
45
|
+
return `SUM(${relatedField})`
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Generate default value for empty aggregation results
|
|
51
|
+
*/
|
|
52
|
+
export const getDefaultValueForAggregation = (aggregation: string): string => {
|
|
53
|
+
const upperAgg = aggregation.toUpperCase()
|
|
54
|
+
|
|
55
|
+
switch (upperAgg) {
|
|
56
|
+
case 'AVG':
|
|
57
|
+
case 'MIN':
|
|
58
|
+
case 'MAX':
|
|
59
|
+
return 'NULL'
|
|
60
|
+
case 'ARRAYUNIQUE':
|
|
61
|
+
return 'ARRAY[]::TEXT[]'
|
|
62
|
+
default:
|
|
63
|
+
return '0'
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
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 { Table } from '@/domain/models/app/table'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get base fields (non-lookup, non-rollup, non-count, non-one-to-many, non-many-to-many, non-id fields)
|
|
12
|
+
*/
|
|
13
|
+
export const getBaseFields = (table: Table): readonly string[] =>
|
|
14
|
+
table.fields
|
|
15
|
+
.filter((field) => {
|
|
16
|
+
// Exclude lookup, rollup, and count fields (handled by VIEW)
|
|
17
|
+
if (field.type === 'lookup' || field.type === 'rollup' || field.type === 'count') {
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
// Exclude one-to-many and many-to-many relationship fields (don't create columns)
|
|
21
|
+
if (
|
|
22
|
+
field.type === 'relationship' &&
|
|
23
|
+
'relationType' in field &&
|
|
24
|
+
(field.relationType === 'one-to-many' || field.relationType === 'many-to-many')
|
|
25
|
+
) {
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
// Exclude id field (auto-generated)
|
|
29
|
+
if (field.name === 'id') {
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
return true
|
|
33
|
+
})
|
|
34
|
+
.map((field) => field.name)
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Generate INSTEAD OF INSERT trigger for a VIEW
|
|
38
|
+
*/
|
|
39
|
+
export const generateInsertTrigger = (
|
|
40
|
+
viewName: string,
|
|
41
|
+
baseTableName: string,
|
|
42
|
+
baseFields: readonly string[]
|
|
43
|
+
): readonly string[] => {
|
|
44
|
+
const insertTriggerFunction = `${viewName}_instead_of_insert`
|
|
45
|
+
const insertTrigger = `${viewName}_insert_trigger`
|
|
46
|
+
const insertFieldsList = baseFields.join(', ')
|
|
47
|
+
const insertValuesList = baseFields.map((name) => `NEW.${name}`).join(', ')
|
|
48
|
+
|
|
49
|
+
return [
|
|
50
|
+
`CREATE OR REPLACE FUNCTION ${insertTriggerFunction}()
|
|
51
|
+
RETURNS TRIGGER AS $$
|
|
52
|
+
BEGIN
|
|
53
|
+
INSERT INTO ${baseTableName} (${insertFieldsList})
|
|
54
|
+
VALUES (${insertValuesList});
|
|
55
|
+
RETURN NEW;
|
|
56
|
+
END;
|
|
57
|
+
$$ LANGUAGE plpgsql`,
|
|
58
|
+
`DROP TRIGGER IF EXISTS ${insertTrigger} ON ${viewName}`,
|
|
59
|
+
`CREATE TRIGGER ${insertTrigger}
|
|
60
|
+
INSTEAD OF INSERT ON ${viewName}
|
|
61
|
+
FOR EACH ROW
|
|
62
|
+
EXECUTE FUNCTION ${insertTriggerFunction}()`,
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Generate INSTEAD OF UPDATE trigger for a VIEW
|
|
68
|
+
*/
|
|
69
|
+
export const generateUpdateTrigger = (
|
|
70
|
+
viewName: string,
|
|
71
|
+
baseTableName: string,
|
|
72
|
+
baseFields: readonly string[]
|
|
73
|
+
): readonly string[] => {
|
|
74
|
+
const updateTriggerFunction = `${viewName}_instead_of_update`
|
|
75
|
+
const updateTrigger = `${viewName}_update_trigger`
|
|
76
|
+
const updateSetList = baseFields.map((name) => `${name} = NEW.${name}`).join(', ')
|
|
77
|
+
|
|
78
|
+
return [
|
|
79
|
+
`CREATE OR REPLACE FUNCTION ${updateTriggerFunction}()
|
|
80
|
+
RETURNS TRIGGER AS $$
|
|
81
|
+
BEGIN
|
|
82
|
+
UPDATE ${baseTableName}
|
|
83
|
+
SET ${updateSetList}
|
|
84
|
+
WHERE id = OLD.id;
|
|
85
|
+
RETURN NEW;
|
|
86
|
+
END;
|
|
87
|
+
$$ LANGUAGE plpgsql`,
|
|
88
|
+
`DROP TRIGGER IF EXISTS ${updateTrigger} ON ${viewName}`,
|
|
89
|
+
`CREATE TRIGGER ${updateTrigger}
|
|
90
|
+
INSTEAD OF UPDATE ON ${viewName}
|
|
91
|
+
FOR EACH ROW
|
|
92
|
+
EXECUTE FUNCTION ${updateTriggerFunction}()`,
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Generate INSTEAD OF DELETE trigger for a VIEW
|
|
98
|
+
*/
|
|
99
|
+
export const generateDeleteTrigger = (
|
|
100
|
+
viewName: string,
|
|
101
|
+
baseTableName: string
|
|
102
|
+
): readonly string[] => {
|
|
103
|
+
const deleteTriggerFunction = `${viewName}_instead_of_delete`
|
|
104
|
+
const deleteTrigger = `${viewName}_delete_trigger`
|
|
105
|
+
|
|
106
|
+
return [
|
|
107
|
+
`CREATE OR REPLACE FUNCTION ${deleteTriggerFunction}()
|
|
108
|
+
RETURNS TRIGGER AS $$
|
|
109
|
+
BEGIN
|
|
110
|
+
DELETE FROM ${baseTableName}
|
|
111
|
+
WHERE id = OLD.id;
|
|
112
|
+
RETURN OLD;
|
|
113
|
+
END;
|
|
114
|
+
$$ LANGUAGE plpgsql`,
|
|
115
|
+
`DROP TRIGGER IF EXISTS ${deleteTrigger} ON ${viewName}`,
|
|
116
|
+
`CREATE TRIGGER ${deleteTrigger}
|
|
117
|
+
INSTEAD OF DELETE ON ${viewName}
|
|
118
|
+
FOR EACH ROW
|
|
119
|
+
EXECUTE FUNCTION ${deleteTriggerFunction}()`,
|
|
120
|
+
]
|
|
121
|
+
}
|