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,252 @@
|
|
|
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 { escapeSqlString } from './sql-utils'
|
|
9
|
+
import type { Fields } from '@/domain/models/app/table/fields'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generate CHECK constraints for array fields with maxItems
|
|
13
|
+
*/
|
|
14
|
+
export const generateArrayConstraints = (fields: readonly Fields[number][]): readonly string[] =>
|
|
15
|
+
fields
|
|
16
|
+
.filter(
|
|
17
|
+
(field): field is Fields[number] & { type: 'array'; maxItems: number } =>
|
|
18
|
+
field.type === 'array' && 'maxItems' in field && typeof field.maxItems === 'number'
|
|
19
|
+
)
|
|
20
|
+
.map(
|
|
21
|
+
(field) =>
|
|
22
|
+
`CONSTRAINT check_${field.name}_max_items CHECK (array_length(${field.name}, 1) IS NULL OR array_length(${field.name}, 1) <= ${field.maxItems})`
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Generate CHECK constraints for multiple-attachments fields with maxFiles
|
|
27
|
+
*/
|
|
28
|
+
export const generateMultipleAttachmentsConstraints = (
|
|
29
|
+
fields: readonly Fields[number][]
|
|
30
|
+
): readonly string[] =>
|
|
31
|
+
fields
|
|
32
|
+
.filter(
|
|
33
|
+
(field): field is Fields[number] & { type: 'multiple-attachments'; maxFiles: number } =>
|
|
34
|
+
field.type === 'multiple-attachments' &&
|
|
35
|
+
'maxFiles' in field &&
|
|
36
|
+
typeof field.maxFiles === 'number'
|
|
37
|
+
)
|
|
38
|
+
.map(
|
|
39
|
+
(field) =>
|
|
40
|
+
`CONSTRAINT check_${field.name}_max_files CHECK (jsonb_array_length(${field.name}) IS NULL OR jsonb_array_length(${field.name}) <= ${field.maxFiles})`
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Generate CHECK constraints for numeric fields with min/max values
|
|
45
|
+
* Supports: integer, decimal, currency, percentage, rating
|
|
46
|
+
*/
|
|
47
|
+
export const generateNumericConstraints = (fields: readonly Fields[number][]): readonly string[] =>
|
|
48
|
+
fields
|
|
49
|
+
.filter(
|
|
50
|
+
(
|
|
51
|
+
field
|
|
52
|
+
): field is Fields[number] & {
|
|
53
|
+
type: 'integer' | 'decimal' | 'currency' | 'percentage' | 'rating'
|
|
54
|
+
} =>
|
|
55
|
+
(field.type === 'integer' ||
|
|
56
|
+
field.type === 'decimal' ||
|
|
57
|
+
field.type === 'currency' ||
|
|
58
|
+
field.type === 'percentage' ||
|
|
59
|
+
field.type === 'rating') &&
|
|
60
|
+
(('min' in field && typeof field.min === 'number') ||
|
|
61
|
+
('max' in field && typeof field.max === 'number'))
|
|
62
|
+
)
|
|
63
|
+
.map((field) => {
|
|
64
|
+
const hasMin = 'min' in field && typeof field.min === 'number'
|
|
65
|
+
const hasMax = 'max' in field && typeof field.max === 'number'
|
|
66
|
+
|
|
67
|
+
// Rating fields always have a minimum of 1 (ratings start from 1, not 0)
|
|
68
|
+
const effectiveMin = field.type === 'rating' && !hasMin ? 1 : hasMin ? field.min : undefined
|
|
69
|
+
|
|
70
|
+
const conditions = [
|
|
71
|
+
...(effectiveMin !== undefined ? [`${field.name} >= ${effectiveMin}`] : []),
|
|
72
|
+
...(hasMax ? [`${field.name} <= ${field.max}`] : []),
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
const constraintName = `check_${field.name}_range`
|
|
76
|
+
const constraintCondition = conditions.join(' AND ')
|
|
77
|
+
return `CONSTRAINT ${constraintName} CHECK (${constraintCondition})`
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Generate CHECK constraints for progress fields (automatic 0-100 range)
|
|
82
|
+
*/
|
|
83
|
+
export const generateProgressConstraints = (fields: readonly Fields[number][]): readonly string[] =>
|
|
84
|
+
fields
|
|
85
|
+
.filter((field): field is Fields[number] & { type: 'progress' } => field.type === 'progress')
|
|
86
|
+
.map((field) => {
|
|
87
|
+
const constraintName = `check_${field.name}_range`
|
|
88
|
+
return `CONSTRAINT ${constraintName} CHECK (${field.name} >= 0 AND ${field.name} <= 100)`
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Extract string values from field options
|
|
93
|
+
* Handles both simple string arrays (single-select) and object arrays with value property (status)
|
|
94
|
+
*/
|
|
95
|
+
const extractOptionValues = (
|
|
96
|
+
field: Fields[number]
|
|
97
|
+
): readonly string[] | readonly { value: string }[] => {
|
|
98
|
+
if ('options' in field && Array.isArray(field.options)) {
|
|
99
|
+
return field.options as readonly string[] | readonly { value: string }[]
|
|
100
|
+
}
|
|
101
|
+
return []
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Generate CHECK constraint for enum-based fields (single-select, status)
|
|
106
|
+
*
|
|
107
|
+
* SECURITY NOTE: Options come from validated Effect Schema (SingleSelectFieldSchema, StatusFieldSchema).
|
|
108
|
+
* We escape single quotes to prevent SQL injection following defense-in-depth security principles.
|
|
109
|
+
*
|
|
110
|
+
* DRY PRINCIPLE: This function consolidates enum constraint generation for both single-select
|
|
111
|
+
* (simple string options) and status (object options with value property) field types.
|
|
112
|
+
*/
|
|
113
|
+
const generateEnumCheckConstraint = (
|
|
114
|
+
field: Fields[number] & { readonly options: readonly unknown[] }
|
|
115
|
+
): string => {
|
|
116
|
+
const options = extractOptionValues(field)
|
|
117
|
+
const values = options
|
|
118
|
+
.map((opt) => {
|
|
119
|
+
const value = typeof opt === 'string' ? opt : (opt as { value: string }).value
|
|
120
|
+
return `'${escapeSqlString(value)}'`
|
|
121
|
+
})
|
|
122
|
+
.join(', ')
|
|
123
|
+
const constraintName = `check_${field.name}_enum`
|
|
124
|
+
return `CONSTRAINT ${constraintName} CHECK (${field.name} IN (${values}))`
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Generate CHECK constraints for single-select fields with enum options
|
|
129
|
+
*/
|
|
130
|
+
export const generateEnumConstraints = (fields: readonly Fields[number][]): readonly string[] =>
|
|
131
|
+
fields
|
|
132
|
+
.filter(
|
|
133
|
+
(field): field is Fields[number] & { type: 'single-select'; options: readonly string[] } =>
|
|
134
|
+
field.type === 'single-select' && 'options' in field && Array.isArray(field.options)
|
|
135
|
+
)
|
|
136
|
+
.map(generateEnumCheckConstraint)
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Generate CHECK constraints for status fields with status options
|
|
140
|
+
*/
|
|
141
|
+
export const generateStatusConstraints = (fields: readonly Fields[number][]): readonly string[] =>
|
|
142
|
+
fields
|
|
143
|
+
.filter(
|
|
144
|
+
(
|
|
145
|
+
field
|
|
146
|
+
): field is Fields[number] & {
|
|
147
|
+
type: 'status'
|
|
148
|
+
options: readonly { value: string; color?: string }[]
|
|
149
|
+
} => field.type === 'status' && 'options' in field && Array.isArray(field.options)
|
|
150
|
+
)
|
|
151
|
+
.map(generateEnumCheckConstraint)
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Generate CHECK constraints for rich-text fields with maxLength
|
|
155
|
+
*/
|
|
156
|
+
export const generateRichTextConstraints = (fields: readonly Fields[number][]): readonly string[] =>
|
|
157
|
+
fields
|
|
158
|
+
.filter(
|
|
159
|
+
(field): field is Fields[number] & { type: 'rich-text'; maxLength: number } =>
|
|
160
|
+
field.type === 'rich-text' && 'maxLength' in field && typeof field.maxLength === 'number'
|
|
161
|
+
)
|
|
162
|
+
.map((field) => {
|
|
163
|
+
const constraintName = `check_${field.name}_max_length`
|
|
164
|
+
return `CONSTRAINT ${constraintName} CHECK (LENGTH(${field.name}) <= ${field.maxLength})`
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Barcode format validation patterns
|
|
169
|
+
* Uses PostgreSQL regex patterns to validate barcode formats
|
|
170
|
+
*/
|
|
171
|
+
const barcodeFormatPatterns: Record<string, string> = {
|
|
172
|
+
'EAN-13': '^[0-9]{13}$',
|
|
173
|
+
'EAN-8': '^[0-9]{8}$',
|
|
174
|
+
'UPC-A': '^[0-9]{12}$',
|
|
175
|
+
'UPC-E': '^[0-9]{6,8}$',
|
|
176
|
+
'CODE-128': '^[\\x00-\\x7F]+$',
|
|
177
|
+
'CODE-39': '^[A-Z0-9\\-\\.\\$\\/\\+\\%\\ ]+$',
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Generate CHECK constraints for barcode fields with format validation
|
|
182
|
+
*/
|
|
183
|
+
export const generateBarcodeConstraints = (fields: readonly Fields[number][]): readonly string[] =>
|
|
184
|
+
fields
|
|
185
|
+
.filter(
|
|
186
|
+
(field): field is Fields[number] & { type: 'barcode'; format: string } =>
|
|
187
|
+
field.type === 'barcode' && 'format' in field && typeof field.format === 'string'
|
|
188
|
+
)
|
|
189
|
+
.map((field) => {
|
|
190
|
+
const pattern = barcodeFormatPatterns[field.format]
|
|
191
|
+
if (!pattern) {
|
|
192
|
+
return ''
|
|
193
|
+
}
|
|
194
|
+
const constraintName = `check_${field.name}_format`
|
|
195
|
+
return `CONSTRAINT ${constraintName} CHECK (${field.name} ~ '${pattern}')`
|
|
196
|
+
})
|
|
197
|
+
.filter((constraint) => constraint !== '')
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Generate CHECK constraints for color fields with hex color format validation
|
|
201
|
+
*/
|
|
202
|
+
export const generateColorConstraints = (fields: readonly Fields[number][]): readonly string[] =>
|
|
203
|
+
fields
|
|
204
|
+
.filter((field): field is Fields[number] & { type: 'color' } => field.type === 'color')
|
|
205
|
+
.map((field) => {
|
|
206
|
+
const constraintName = `check_${field.name}_format`
|
|
207
|
+
return `CONSTRAINT ${constraintName} CHECK (${field.name} ~ '^#[0-9a-fA-F]{6}$')`
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Generate CHECK constraints for multi-select fields with options
|
|
212
|
+
* Validates that all selected values are from the predefined options list
|
|
213
|
+
* Uses PostgreSQL <@ (contained by) operator for array validation
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* Field: { type: 'multi-select', options: ['work', 'personal'] }
|
|
217
|
+
* Constraint: CHECK (tags <@ ARRAY['work', 'personal']::text[])
|
|
218
|
+
*/
|
|
219
|
+
export const generateMultiSelectConstraints = (
|
|
220
|
+
fields: readonly Fields[number][]
|
|
221
|
+
): readonly string[] =>
|
|
222
|
+
fields
|
|
223
|
+
.filter(
|
|
224
|
+
(field): field is Fields[number] & { type: 'multi-select'; options: readonly string[] } =>
|
|
225
|
+
field.type === 'multi-select' && 'options' in field && Array.isArray(field.options)
|
|
226
|
+
)
|
|
227
|
+
.map((field) => {
|
|
228
|
+
const escapedOptions = field.options.map((opt) => `'${escapeSqlString(opt)}'`).join(', ')
|
|
229
|
+
const constraintName = `check_${field.name}_options`
|
|
230
|
+
return `CONSTRAINT ${constraintName} CHECK (${field.name} <@ ARRAY[${escapedOptions}]::text[])`
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Generate custom CHECK constraints defined at table level
|
|
235
|
+
*
|
|
236
|
+
* Used for complex business rules that involve multiple fields or
|
|
237
|
+
* conditional validation beyond field-level constraints.
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```typescript
|
|
241
|
+
* const constraints = [{
|
|
242
|
+
* name: 'chk_active_members_have_email',
|
|
243
|
+
* check: '(is_active = false) OR (email IS NOT NULL)'
|
|
244
|
+
* }]
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
export const generateCustomCheckConstraints = (
|
|
248
|
+
constraints?: readonly { readonly name: string; readonly check: string }[]
|
|
249
|
+
): readonly string[] =>
|
|
250
|
+
constraints
|
|
251
|
+
? constraints.map((constraint) => `CONSTRAINT ${constraint.name} CHECK (${constraint.check})`)
|
|
252
|
+
: []
|
|
@@ -0,0 +1,174 @@
|
|
|
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 {
|
|
9
|
+
isFormulaVolatile,
|
|
10
|
+
isFormulaReturningArray,
|
|
11
|
+
translateFormulaToPostgres,
|
|
12
|
+
} from '../formula/formula-utils'
|
|
13
|
+
import { isAutoTimestampField, isFieldNotNull, shouldUseSerial } from './sql-field-predicates'
|
|
14
|
+
import { mapFieldTypeToPostgres, mapFormulaResultTypeToPostgres } from './sql-type-mappings'
|
|
15
|
+
import { escapeSqlString } from './sql-utils'
|
|
16
|
+
import type { Fields } from '@/domain/models/app/table/fields'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Format default value for SQL
|
|
20
|
+
* Numbers and booleans are unquoted, strings are quoted and escaped
|
|
21
|
+
*/
|
|
22
|
+
const formatDefaultValue = (defaultValue: unknown): string => {
|
|
23
|
+
if (typeof defaultValue === 'boolean') {
|
|
24
|
+
return String(defaultValue)
|
|
25
|
+
}
|
|
26
|
+
if (typeof defaultValue === 'number') {
|
|
27
|
+
return String(defaultValue)
|
|
28
|
+
}
|
|
29
|
+
return `'${escapeSqlString(String(defaultValue))}'`
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generate SERIAL column definition for auto-increment fields
|
|
34
|
+
* When isPrimaryKey is true and it's a single-field PK, add PRIMARY KEY inline for better PostgreSQL constraint recognition
|
|
35
|
+
*/
|
|
36
|
+
const generateSerialColumn = (fieldName: string, isPrimaryKey: boolean = false): string =>
|
|
37
|
+
isPrimaryKey ? `${fieldName} SERIAL PRIMARY KEY` : `${fieldName} SERIAL NOT NULL`
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Generate NOT NULL constraint
|
|
41
|
+
*/
|
|
42
|
+
const generateNotNullConstraint = (
|
|
43
|
+
field: Fields[number],
|
|
44
|
+
isPrimaryKey: boolean,
|
|
45
|
+
hasAuthConfig: boolean = true
|
|
46
|
+
): string => {
|
|
47
|
+
return isFieldNotNull(field, isPrimaryKey, hasAuthConfig) ? ' NOT NULL' : ''
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Format array default value as PostgreSQL ARRAY literal
|
|
52
|
+
*/
|
|
53
|
+
const formatArrayDefault = (defaultValue: readonly unknown[]): string => {
|
|
54
|
+
const arrayValues = defaultValue.map((val) => `'${escapeSqlString(String(val))}'`).join(', ')
|
|
55
|
+
return ` DEFAULT ARRAY[${arrayValues}]`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Format special default values (PostgreSQL functions, INTERVAL, etc.)
|
|
60
|
+
*/
|
|
61
|
+
const formatSpecialDefault = (field: Fields[number], defaultValue: unknown): string | undefined => {
|
|
62
|
+
// PostgreSQL functions like CURRENT_DATE, NOW() should not be quoted
|
|
63
|
+
if (typeof defaultValue === 'string' && defaultValue.toUpperCase() === 'CURRENT_DATE') {
|
|
64
|
+
return ' DEFAULT CURRENT_DATE'
|
|
65
|
+
}
|
|
66
|
+
if (typeof defaultValue === 'string' && defaultValue.toUpperCase() === 'NOW()') {
|
|
67
|
+
return ' DEFAULT NOW()'
|
|
68
|
+
}
|
|
69
|
+
// Duration fields: convert seconds to INTERVAL
|
|
70
|
+
if (field.type === 'duration' && typeof defaultValue === 'number') {
|
|
71
|
+
return ` DEFAULT INTERVAL '${defaultValue} seconds'`
|
|
72
|
+
}
|
|
73
|
+
// Array fields (multi-select): convert to PostgreSQL array literal
|
|
74
|
+
if (Array.isArray(defaultValue)) {
|
|
75
|
+
return formatArrayDefault(defaultValue)
|
|
76
|
+
}
|
|
77
|
+
return undefined
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Generate DEFAULT clause
|
|
82
|
+
*/
|
|
83
|
+
const generateDefaultClause = (field: Fields[number]): string => {
|
|
84
|
+
// Auto-timestamp fields get CURRENT_TIMESTAMP default (PostgreSQL function for current timestamp)
|
|
85
|
+
if (isAutoTimestampField(field)) {
|
|
86
|
+
return ' DEFAULT CURRENT_TIMESTAMP'
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Progress fields with required=true get DEFAULT 0 automatically
|
|
90
|
+
if (field.type === 'progress' && field.required === true && !('default' in field)) {
|
|
91
|
+
return ' DEFAULT 0'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Explicit default values
|
|
95
|
+
if ('default' in field && field.default !== undefined) {
|
|
96
|
+
const defaultValue = field.default
|
|
97
|
+
const specialDefault = formatSpecialDefault(field, defaultValue)
|
|
98
|
+
if (specialDefault) {
|
|
99
|
+
return specialDefault
|
|
100
|
+
}
|
|
101
|
+
return ` DEFAULT ${formatDefaultValue(defaultValue)}`
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return ''
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Generate formula column definition (GENERATED ALWAYS AS or trigger-based)
|
|
109
|
+
*
|
|
110
|
+
* NOTE: Formula fields with volatile functions (CURRENT_DATE, NOW(), etc.) cannot use
|
|
111
|
+
* GENERATED ALWAYS AS because PostgreSQL requires generated columns to be immutable.
|
|
112
|
+
* For volatile formulas, we create regular columns and handle computation via triggers.
|
|
113
|
+
*/
|
|
114
|
+
const generateFormulaColumn = (
|
|
115
|
+
field: Fields[number] & { readonly type: 'formula'; readonly formula: string },
|
|
116
|
+
allFields?: readonly Fields[number][]
|
|
117
|
+
): string => {
|
|
118
|
+
const baseResultType =
|
|
119
|
+
'resultType' in field && field.resultType
|
|
120
|
+
? mapFormulaResultTypeToPostgres(field.resultType)
|
|
121
|
+
: 'TEXT'
|
|
122
|
+
|
|
123
|
+
// Auto-detect array return type for functions like STRING_TO_ARRAY
|
|
124
|
+
// If formula returns an array but resultType doesn't specify array, append []
|
|
125
|
+
const resultType =
|
|
126
|
+
isFormulaReturningArray(field.formula) && !baseResultType.endsWith('[]')
|
|
127
|
+
? `${baseResultType}[]`
|
|
128
|
+
: baseResultType
|
|
129
|
+
|
|
130
|
+
// Translate formula to PostgreSQL syntax with field type context
|
|
131
|
+
// Note: translateFormulaToPostgres handles ROUND with double precision by casting to NUMERIC
|
|
132
|
+
// and converts date::TEXT to TO_CHAR(date, 'format') which is STABLE (not IMMUTABLE)
|
|
133
|
+
const translatedFormula = translateFormulaToPostgres(field.formula, allFields)
|
|
134
|
+
|
|
135
|
+
// Volatile formulas (contain CURRENT_DATE, NOW(), etc.) need trigger-based computation
|
|
136
|
+
// because PostgreSQL GENERATED columns must be immutable
|
|
137
|
+
// Check volatility on TRANSLATED formula since date::TEXT becomes TO_CHAR (STABLE)
|
|
138
|
+
if (isFormulaVolatile(translatedFormula)) {
|
|
139
|
+
// Create regular column - trigger will populate it
|
|
140
|
+
return `${field.name} ${resultType}`
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Immutable formulas can use GENERATED ALWAYS AS
|
|
144
|
+
return `${field.name} ${resultType} GENERATED ALWAYS AS (${translatedFormula}) STORED`
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Generate column definition with constraints
|
|
149
|
+
*
|
|
150
|
+
* NOTE: UNIQUE constraints are NOT generated inline. Named UNIQUE constraints
|
|
151
|
+
* are generated at the table level via generateUniqueConstraints() to ensure
|
|
152
|
+
* they appear in information_schema.table_constraints with queryable constraint names.
|
|
153
|
+
*/
|
|
154
|
+
export const generateColumnDefinition = (
|
|
155
|
+
field: Fields[number],
|
|
156
|
+
isPrimaryKey: boolean,
|
|
157
|
+
allFields?: readonly Fields[number][],
|
|
158
|
+
hasAuthConfig: boolean = true
|
|
159
|
+
): string => {
|
|
160
|
+
// SERIAL columns for auto-increment fields
|
|
161
|
+
if (shouldUseSerial(field, isPrimaryKey)) {
|
|
162
|
+
return generateSerialColumn(field.name, isPrimaryKey)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Formula fields: check if formula is volatile
|
|
166
|
+
if (field.type === 'formula' && 'formula' in field && field.formula) {
|
|
167
|
+
return generateFormulaColumn(field, allFields)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const columnType = mapFieldTypeToPostgres(field)
|
|
171
|
+
const notNull = generateNotNullConstraint(field, isPrimaryKey, hasAuthConfig)
|
|
172
|
+
const defaultValue = generateDefaultClause(field)
|
|
173
|
+
return `${field.name} ${columnType}${notNull}${defaultValue}`
|
|
174
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
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 { Data, Effect } from 'effect'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Type definition for a database transaction that can execute raw SQL
|
|
12
|
+
*/
|
|
13
|
+
export interface TransactionLike {
|
|
14
|
+
readonly unsafe: (sql: string) => Promise<readonly unknown[]>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Error type for SQL execution failures
|
|
19
|
+
*/
|
|
20
|
+
export class SQLExecutionError extends Data.TaggedError('SQLExecutionError')<{
|
|
21
|
+
readonly message: string
|
|
22
|
+
readonly sql?: string
|
|
23
|
+
readonly cause?: unknown
|
|
24
|
+
}> {}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Type definition for information_schema.columns row
|
|
28
|
+
*/
|
|
29
|
+
export interface ColumnInfo {
|
|
30
|
+
readonly column_name: string
|
|
31
|
+
readonly data_type: string
|
|
32
|
+
readonly is_nullable: string
|
|
33
|
+
readonly column_default: string | null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Type definition for table existence query result
|
|
38
|
+
*/
|
|
39
|
+
interface TableExistsResult {
|
|
40
|
+
readonly exists: boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Type definition for table name query result
|
|
45
|
+
*/
|
|
46
|
+
interface TableNameResult {
|
|
47
|
+
readonly tablename: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Type definition for view name query result
|
|
52
|
+
*/
|
|
53
|
+
interface ViewNameResult {
|
|
54
|
+
readonly viewname: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Type definition for materialized view name query result
|
|
59
|
+
*/
|
|
60
|
+
interface MatViewNameResult {
|
|
61
|
+
readonly matviewname: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// SQL Statement Execution Helpers
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Execute a single SQL statement within an Effect context
|
|
70
|
+
*
|
|
71
|
+
* SECURITY NOTE: This function uses tx.unsafe() which is intentional for DDL execution.
|
|
72
|
+
* See schema-initializer.ts for detailed security rationale.
|
|
73
|
+
*/
|
|
74
|
+
export const executeSQL = (
|
|
75
|
+
tx: TransactionLike,
|
|
76
|
+
sql: string
|
|
77
|
+
): Effect.Effect<readonly unknown[], SQLExecutionError> =>
|
|
78
|
+
Effect.tryPromise({
|
|
79
|
+
try: () => tx.unsafe(sql),
|
|
80
|
+
catch: (error) =>
|
|
81
|
+
new SQLExecutionError({
|
|
82
|
+
message: `SQL execution failed: ${String(error)}`,
|
|
83
|
+
sql,
|
|
84
|
+
cause: error,
|
|
85
|
+
}),
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Execute multiple SQL statements sequentially
|
|
90
|
+
* Use this when statements must be executed in order (e.g., DDL that depends on previous statements)
|
|
91
|
+
*/
|
|
92
|
+
/* eslint-disable functional/no-loop-statements */
|
|
93
|
+
export const executeSQLStatements = (
|
|
94
|
+
tx: TransactionLike,
|
|
95
|
+
statements: readonly string[]
|
|
96
|
+
): Effect.Effect<void, SQLExecutionError> =>
|
|
97
|
+
statements.length === 0
|
|
98
|
+
? Effect.void
|
|
99
|
+
: Effect.gen(function* () {
|
|
100
|
+
for (const sql of statements) {
|
|
101
|
+
yield* executeSQL(tx, sql)
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
/* eslint-enable functional/no-loop-statements */
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Execute multiple SQL statements in parallel
|
|
108
|
+
* Use this when statements are independent (e.g., DROP VIEW statements, index creation)
|
|
109
|
+
*
|
|
110
|
+
* Note: PostgreSQL allows concurrent DDL operations within a transaction,
|
|
111
|
+
* but some operations may still serialize at the database level.
|
|
112
|
+
*/
|
|
113
|
+
export const executeSQLStatementsParallel = (
|
|
114
|
+
tx: TransactionLike,
|
|
115
|
+
statements: readonly string[]
|
|
116
|
+
): Effect.Effect<void, SQLExecutionError> =>
|
|
117
|
+
statements.length === 0
|
|
118
|
+
? Effect.void
|
|
119
|
+
: Effect.all(
|
|
120
|
+
statements.map((sql) => executeSQL(tx, sql)),
|
|
121
|
+
{ concurrency: 'unbounded' }
|
|
122
|
+
).pipe(Effect.asVoid)
|
|
123
|
+
|
|
124
|
+
// ============================================================================
|
|
125
|
+
// Information Schema Query Helpers
|
|
126
|
+
// ============================================================================
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Check if a table exists in the database
|
|
130
|
+
*
|
|
131
|
+
* SECURITY NOTE: String interpolation is used for tableName.
|
|
132
|
+
* This is SAFE because:
|
|
133
|
+
* 1. tableName comes from validated Effect Schema (Table.name field)
|
|
134
|
+
* 2. Table names are defined in schema configuration, not user input
|
|
135
|
+
* 3. The App schema is validated before reaching this code
|
|
136
|
+
* 4. information_schema queries are read-only (no data modification risk)
|
|
137
|
+
*/
|
|
138
|
+
export const tableExists = (
|
|
139
|
+
tx: TransactionLike,
|
|
140
|
+
tableName: string
|
|
141
|
+
): Effect.Effect<boolean, SQLExecutionError> =>
|
|
142
|
+
executeSQL(
|
|
143
|
+
tx,
|
|
144
|
+
`
|
|
145
|
+
SELECT EXISTS (
|
|
146
|
+
SELECT 1
|
|
147
|
+
FROM information_schema.tables
|
|
148
|
+
WHERE table_name = '${tableName}'
|
|
149
|
+
AND table_schema = 'public'
|
|
150
|
+
) as exists
|
|
151
|
+
`
|
|
152
|
+
).pipe(Effect.map((result) => (result as readonly TableExistsResult[])[0]?.exists ?? false))
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get existing columns from a table
|
|
156
|
+
*
|
|
157
|
+
* SECURITY NOTE: String interpolation is used for tableName.
|
|
158
|
+
* This is SAFE because tableName comes from validated schema configuration.
|
|
159
|
+
*/
|
|
160
|
+
export const getExistingColumns = (
|
|
161
|
+
tx: TransactionLike,
|
|
162
|
+
tableName: string
|
|
163
|
+
): Effect.Effect<
|
|
164
|
+
ReadonlyMap<string, { dataType: string; isNullable: string; columnDefault: string | null }>,
|
|
165
|
+
SQLExecutionError
|
|
166
|
+
> =>
|
|
167
|
+
executeSQL(
|
|
168
|
+
tx,
|
|
169
|
+
`
|
|
170
|
+
SELECT column_name, data_type, is_nullable, column_default
|
|
171
|
+
FROM information_schema.columns
|
|
172
|
+
WHERE table_name = '${tableName}'
|
|
173
|
+
AND table_schema = 'public'
|
|
174
|
+
`
|
|
175
|
+
).pipe(
|
|
176
|
+
Effect.map((result) => {
|
|
177
|
+
const rows = result as readonly ColumnInfo[]
|
|
178
|
+
return new Map(
|
|
179
|
+
rows.map((row) => [
|
|
180
|
+
row.column_name,
|
|
181
|
+
{
|
|
182
|
+
dataType: row.data_type,
|
|
183
|
+
isNullable: row.is_nullable,
|
|
184
|
+
columnDefault: row.column_default,
|
|
185
|
+
},
|
|
186
|
+
])
|
|
187
|
+
)
|
|
188
|
+
})
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get all existing table names in the public schema
|
|
193
|
+
*
|
|
194
|
+
* SECURITY NOTE: This query is read-only and uses pg_tables system catalog.
|
|
195
|
+
* No user input is involved.
|
|
196
|
+
*/
|
|
197
|
+
export const getExistingTableNames = (
|
|
198
|
+
tx: TransactionLike
|
|
199
|
+
): Effect.Effect<readonly string[], SQLExecutionError> =>
|
|
200
|
+
executeSQL(
|
|
201
|
+
tx,
|
|
202
|
+
`
|
|
203
|
+
SELECT tablename
|
|
204
|
+
FROM pg_tables
|
|
205
|
+
WHERE schemaname = 'public'
|
|
206
|
+
`
|
|
207
|
+
).pipe(Effect.map((result) => (result as readonly TableNameResult[]).map((row) => row.tablename)))
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get all existing view names in the public schema
|
|
211
|
+
*
|
|
212
|
+
* SECURITY NOTE: This query is read-only and uses pg_views system catalog.
|
|
213
|
+
* No user input is involved.
|
|
214
|
+
*/
|
|
215
|
+
export const getExistingViews = (
|
|
216
|
+
tx: TransactionLike
|
|
217
|
+
): Effect.Effect<readonly string[], SQLExecutionError> =>
|
|
218
|
+
executeSQL(
|
|
219
|
+
tx,
|
|
220
|
+
`
|
|
221
|
+
SELECT viewname
|
|
222
|
+
FROM pg_views
|
|
223
|
+
WHERE schemaname = 'public'
|
|
224
|
+
`
|
|
225
|
+
).pipe(Effect.map((result) => (result as readonly ViewNameResult[]).map((row) => row.viewname)))
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Get all existing materialized view names in the public schema
|
|
229
|
+
*
|
|
230
|
+
* SECURITY NOTE: This query is read-only and uses pg_matviews system catalog.
|
|
231
|
+
* No user input is involved.
|
|
232
|
+
*/
|
|
233
|
+
export const getExistingMaterializedViews = (
|
|
234
|
+
tx: TransactionLike
|
|
235
|
+
): Effect.Effect<readonly string[], SQLExecutionError> =>
|
|
236
|
+
executeSQL(
|
|
237
|
+
tx,
|
|
238
|
+
`
|
|
239
|
+
SELECT matviewname
|
|
240
|
+
FROM pg_matviews
|
|
241
|
+
WHERE schemaname = 'public'
|
|
242
|
+
`
|
|
243
|
+
).pipe(
|
|
244
|
+
Effect.map((result) => (result as readonly MatViewNameResult[]).map((row) => row.matviewname))
|
|
245
|
+
)
|