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,167 @@
|
|
|
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 { Context } from 'hono'
|
|
9
|
+
|
|
10
|
+
type AggregateParams = {
|
|
11
|
+
readonly count?: boolean
|
|
12
|
+
readonly sum?: readonly string[]
|
|
13
|
+
readonly avg?: readonly string[]
|
|
14
|
+
readonly min?: readonly string[]
|
|
15
|
+
readonly max?: readonly string[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type FilterStructure =
|
|
19
|
+
| {
|
|
20
|
+
readonly and?: readonly {
|
|
21
|
+
readonly field: string
|
|
22
|
+
readonly operator: string
|
|
23
|
+
readonly value: unknown
|
|
24
|
+
}[]
|
|
25
|
+
}
|
|
26
|
+
| undefined
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if field is sensitive type (email, phone, currency)
|
|
30
|
+
*/
|
|
31
|
+
function isSensitiveFieldType(fieldType: string): boolean {
|
|
32
|
+
const sensitiveTypes = new Set(['email', 'phone-number', 'currency'])
|
|
33
|
+
return sensitiveTypes.has(fieldType)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check if field should be excluded for viewer role
|
|
38
|
+
*/
|
|
39
|
+
function shouldExcludeForViewer(fieldName: string, fieldType: string): boolean {
|
|
40
|
+
const allowedFieldTypes = new Set(['single-line-text'])
|
|
41
|
+
const allowedFieldNames = new Set(['name', 'title'])
|
|
42
|
+
|
|
43
|
+
// Exclude sensitive field types
|
|
44
|
+
if (isSensitiveFieldType(fieldType)) {
|
|
45
|
+
return true
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Only allow specific field names or types
|
|
49
|
+
if (!allowedFieldNames.has(fieldName) && !allowedFieldTypes.has(fieldType)) {
|
|
50
|
+
return true
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// For single-line-text, only allow if it's a name/title field
|
|
54
|
+
if (fieldType === 'single-line-text' && !allowedFieldNames.has(fieldName)) {
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check if field should be excluded based on default permission rules
|
|
63
|
+
* Matches logic from field-read-filter.ts
|
|
64
|
+
*/
|
|
65
|
+
export function shouldExcludeFieldByDefault(
|
|
66
|
+
fieldName: string,
|
|
67
|
+
userRole: string,
|
|
68
|
+
table:
|
|
69
|
+
| { readonly fields: readonly { readonly name: string; readonly type: string }[] }
|
|
70
|
+
| undefined
|
|
71
|
+
): boolean {
|
|
72
|
+
// Admin role has full access
|
|
73
|
+
if (userRole === 'admin') {
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Find field definition
|
|
78
|
+
const field = table?.fields.find((f) => f.name === fieldName)
|
|
79
|
+
if (!field) return false
|
|
80
|
+
|
|
81
|
+
// Viewer role: most restrictive access
|
|
82
|
+
if (userRole === 'viewer') {
|
|
83
|
+
return shouldExcludeForViewer(fieldName, field.type)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Member role: restrict sensitive financial data
|
|
87
|
+
if (userRole === 'member') {
|
|
88
|
+
return fieldName === 'salary' && field.type === 'currency'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return false
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Validate filter parameter - ensure user has permission to read filter fields
|
|
96
|
+
*/
|
|
97
|
+
export function validateFilterParam(
|
|
98
|
+
filter: FilterStructure,
|
|
99
|
+
table:
|
|
100
|
+
| { readonly fields: readonly { readonly name: string; readonly type: string }[] }
|
|
101
|
+
| undefined,
|
|
102
|
+
userRole: string,
|
|
103
|
+
c: Context
|
|
104
|
+
) {
|
|
105
|
+
if (!filter) return undefined
|
|
106
|
+
|
|
107
|
+
// Extract field names from filter structure
|
|
108
|
+
const filterFields = filter.and?.map((condition) => condition.field) ?? []
|
|
109
|
+
|
|
110
|
+
// Check if user has permission to read each filter field using find instead of for loop
|
|
111
|
+
const inaccessibleField = filterFields.find((fieldName) =>
|
|
112
|
+
shouldExcludeFieldByDefault(fieldName, userRole, table)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if (inaccessibleField) {
|
|
116
|
+
return c.json(
|
|
117
|
+
{
|
|
118
|
+
success: false,
|
|
119
|
+
message: `You do not have permission to perform this action. Cannot filter by field '${inaccessibleField}'`,
|
|
120
|
+
code: 'FORBIDDEN',
|
|
121
|
+
},
|
|
122
|
+
403
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return undefined
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Validate aggregate parameter - ensure user has permission to read aggregated fields
|
|
131
|
+
*/
|
|
132
|
+
export function validateAggregateParam(
|
|
133
|
+
aggregate: AggregateParams | undefined,
|
|
134
|
+
table:
|
|
135
|
+
| { readonly fields: readonly { readonly name: string; readonly type: string }[] }
|
|
136
|
+
| undefined,
|
|
137
|
+
userRole: string,
|
|
138
|
+
c: Context
|
|
139
|
+
) {
|
|
140
|
+
if (!aggregate) return undefined
|
|
141
|
+
|
|
142
|
+
// Extract all field names from aggregate operations
|
|
143
|
+
const aggregateFields = [
|
|
144
|
+
...(aggregate.sum ?? []),
|
|
145
|
+
...(aggregate.avg ?? []),
|
|
146
|
+
...(aggregate.min ?? []),
|
|
147
|
+
...(aggregate.max ?? []),
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
// Check if user has permission to read each aggregated field using find instead of for loop
|
|
151
|
+
const inaccessibleField = aggregateFields.find((fieldName) =>
|
|
152
|
+
shouldExcludeFieldByDefault(fieldName, userRole, table)
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
if (inaccessibleField) {
|
|
156
|
+
return c.json(
|
|
157
|
+
{
|
|
158
|
+
success: false,
|
|
159
|
+
message: `You do not have permission to perform this action. Cannot aggregate field '${inaccessibleField}'`,
|
|
160
|
+
code: 'FORBIDDEN',
|
|
161
|
+
},
|
|
162
|
+
403
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return undefined
|
|
167
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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 { validateFilterFieldPermissions } from '@/presentation/api/utils/filter-field-validator'
|
|
9
|
+
import type { App } from '@/domain/models/app'
|
|
10
|
+
import type { Context } from 'hono'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Filter parameter type matching the expected shape
|
|
14
|
+
*/
|
|
15
|
+
export type FilterParameter =
|
|
16
|
+
| {
|
|
17
|
+
readonly and?: readonly {
|
|
18
|
+
readonly field: string
|
|
19
|
+
readonly operator: string
|
|
20
|
+
readonly value: unknown
|
|
21
|
+
}[]
|
|
22
|
+
}
|
|
23
|
+
| undefined
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Result of parsing filter parameter
|
|
27
|
+
*/
|
|
28
|
+
export type ParseFilterResult =
|
|
29
|
+
| { success: true; filter: FilterParameter }
|
|
30
|
+
| { success: false; error: Response }
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Parse and validate filter parameter from query string
|
|
34
|
+
*
|
|
35
|
+
* Handles JSON parsing and field-level permission validation
|
|
36
|
+
* for filter parameters.
|
|
37
|
+
*
|
|
38
|
+
* @param config - Configuration object with filter details
|
|
39
|
+
* @returns ParseFilterResult indicating success with filter or failure with error response
|
|
40
|
+
*/
|
|
41
|
+
export function parseFilterParameter(config: {
|
|
42
|
+
filterParam: string | undefined
|
|
43
|
+
app: App
|
|
44
|
+
tableName: string
|
|
45
|
+
userRole: string
|
|
46
|
+
c: Context
|
|
47
|
+
}): ParseFilterResult {
|
|
48
|
+
const { filterParam, app, tableName, userRole, c } = config
|
|
49
|
+
|
|
50
|
+
if (!filterParam) {
|
|
51
|
+
return { success: true, filter: undefined }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const filter = JSON.parse(filterParam)
|
|
56
|
+
const forbiddenFields = validateFilterFieldPermissions(app, tableName, userRole, filter)
|
|
57
|
+
|
|
58
|
+
if (forbiddenFields.length > 0) {
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
error: c.json(
|
|
62
|
+
{ error: 'Forbidden', message: `Cannot filter by field: ${forbiddenFields[0]}` },
|
|
63
|
+
403
|
|
64
|
+
),
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { success: true, filter }
|
|
69
|
+
} catch {
|
|
70
|
+
return {
|
|
71
|
+
success: false,
|
|
72
|
+
error: c.json({ error: 'Bad Request', message: 'Invalid filter parameter' }, 400),
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Map operator symbol to internal operator name
|
|
10
|
+
*/
|
|
11
|
+
function mapOperator(operator: string): string | undefined {
|
|
12
|
+
const operatorMap: Record<string, string> = {
|
|
13
|
+
'=': 'equals',
|
|
14
|
+
'!=': 'notEquals',
|
|
15
|
+
'<': 'lessThan',
|
|
16
|
+
'<=': 'lessThanOrEqual',
|
|
17
|
+
'>': 'greaterThan',
|
|
18
|
+
'>=': 'greaterThanOrEqual',
|
|
19
|
+
}
|
|
20
|
+
return operatorMap[operator]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parse a single condition from regex match
|
|
25
|
+
*/
|
|
26
|
+
function parseCondition(match: RegExpExecArray):
|
|
27
|
+
| {
|
|
28
|
+
readonly field: string
|
|
29
|
+
readonly operator: string
|
|
30
|
+
readonly value: unknown
|
|
31
|
+
}
|
|
32
|
+
| undefined {
|
|
33
|
+
const field = match[1]
|
|
34
|
+
const operator = match[2]
|
|
35
|
+
const stringValue1 = match[3]
|
|
36
|
+
const stringValue2 = match[4]
|
|
37
|
+
const numberValue = match[5]
|
|
38
|
+
|
|
39
|
+
// Validate required captures
|
|
40
|
+
if (!field || !operator) {
|
|
41
|
+
return undefined
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Determine value (string or number)
|
|
45
|
+
const value = stringValue1 ?? stringValue2 ?? Number(numberValue)
|
|
46
|
+
|
|
47
|
+
const mappedOperator = mapOperator(operator)
|
|
48
|
+
if (!mappedOperator) {
|
|
49
|
+
return undefined
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
field,
|
|
54
|
+
operator: mappedOperator,
|
|
55
|
+
value,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Extract all conditions from formula string
|
|
61
|
+
*/
|
|
62
|
+
function extractConditions(conditions: string): Array<{
|
|
63
|
+
readonly field: string
|
|
64
|
+
readonly operator: string
|
|
65
|
+
readonly value: unknown
|
|
66
|
+
}> {
|
|
67
|
+
// Regex to match: {field}operator'value' or {field}operatorvalue
|
|
68
|
+
const conditionRegex = /\{([^}]+)\}\s*([!=<>]+)\s*(?:'([^']*)'|"([^"]*)"|(\d+))/g
|
|
69
|
+
|
|
70
|
+
const matches = [...conditions.matchAll(conditionRegex)]
|
|
71
|
+
const parsedConditions = matches
|
|
72
|
+
.map(parseCondition)
|
|
73
|
+
.filter((c): c is NonNullable<typeof c> => c !== undefined)
|
|
74
|
+
|
|
75
|
+
return parsedConditions
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Parse Airtable-style formula syntax into filter structure
|
|
80
|
+
*
|
|
81
|
+
* Supported syntax:
|
|
82
|
+
* - AND({field}='value', {field}>=value)
|
|
83
|
+
* - Field references: {fieldName}
|
|
84
|
+
* - Operators: =, !=, <, <=, >, >=
|
|
85
|
+
* - String values: 'value' or "value"
|
|
86
|
+
* - Number values: 123
|
|
87
|
+
*
|
|
88
|
+
* @param formula - Airtable-style formula string
|
|
89
|
+
* @returns Filter structure compatible with existing filter system
|
|
90
|
+
*/
|
|
91
|
+
export function parseFormulaToFilter(formula: string):
|
|
92
|
+
| {
|
|
93
|
+
readonly and?: Array<{
|
|
94
|
+
readonly field: string
|
|
95
|
+
readonly operator: string
|
|
96
|
+
readonly value: unknown
|
|
97
|
+
}>
|
|
98
|
+
}
|
|
99
|
+
| undefined {
|
|
100
|
+
// Match AND(...) pattern
|
|
101
|
+
const andMatch = formula.match(/^AND\((.*)\)$/i)
|
|
102
|
+
if (!andMatch) {
|
|
103
|
+
return undefined
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const conditions = andMatch[1]
|
|
107
|
+
if (!conditions) {
|
|
108
|
+
return undefined
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const filters = extractConditions(conditions)
|
|
112
|
+
|
|
113
|
+
if (filters.length === 0) {
|
|
114
|
+
return undefined
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return { and: filters }
|
|
118
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
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 { validateTable, enrichUserRole } from '@/presentation/api/middleware/table'
|
|
9
|
+
import { chainBatchRoutesMethods } from './batch-routes'
|
|
10
|
+
import { chainRecordRoutesMethods } from './record-routes'
|
|
11
|
+
import { chainTableRoutesMethods } from './table-routes'
|
|
12
|
+
import { chainViewRoutesMethods } from './view-routes'
|
|
13
|
+
import type { App } from '@/domain/models/app'
|
|
14
|
+
import type { Context, Hono, Next } from 'hono'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Guest context middleware handler
|
|
18
|
+
*
|
|
19
|
+
* Creates a minimal guest session for apps without auth configuration.
|
|
20
|
+
*/
|
|
21
|
+
async function guestContextHandler(c: Context, next: Next) {
|
|
22
|
+
// Create a minimal guest session for apps without auth
|
|
23
|
+
const guestSession = {
|
|
24
|
+
userId: 'guest',
|
|
25
|
+
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours from now
|
|
26
|
+
token: '',
|
|
27
|
+
ipAddress: '',
|
|
28
|
+
userAgent: '',
|
|
29
|
+
}
|
|
30
|
+
c.set('session', guestSession)
|
|
31
|
+
c.set('userRole', 'guest')
|
|
32
|
+
|
|
33
|
+
// eslint-disable-next-line functional/no-expression-statements -- Required for middleware to continue
|
|
34
|
+
await next()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Middleware to provide guest session and role for apps without authentication
|
|
39
|
+
*
|
|
40
|
+
* For apps without auth configuration, this middleware creates a minimal
|
|
41
|
+
* guest session and sets the userRole to 'guest', allowing routes to function
|
|
42
|
+
* without actual authentication while maintaining the expected context structure.
|
|
43
|
+
*/
|
|
44
|
+
function provideGuestContext() {
|
|
45
|
+
return guestContextHandler
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Chain table routes onto a Hono app
|
|
50
|
+
*
|
|
51
|
+
* **Middleware Chain** (applied before all :tableId routes):
|
|
52
|
+
* ```
|
|
53
|
+
* requireAuth() → validateTable() → enrichUserRole() → Handler (if auth configured)
|
|
54
|
+
* validateTable() → provideGuestContext() → Handler (if no auth)
|
|
55
|
+
* ```
|
|
56
|
+
* - `requireAuth`: Ensures session exists (applied in api-routes.ts if auth configured)
|
|
57
|
+
* - `validateTable`: Validates table exists, attaches tableName + tableId
|
|
58
|
+
* - `enrichUserRole`: Fetches user role from DB, attaches userRole (if auth configured)
|
|
59
|
+
* - `provideGuestContext`: Provides guest session and role (if no auth)
|
|
60
|
+
*
|
|
61
|
+
* **IMPORTANT**: Hono requires middleware registration for BOTH patterns:
|
|
62
|
+
* 1. `/api/tables/:tableId` - Exact match (e.g., GET /api/tables/123)
|
|
63
|
+
* 2. `/api/tables/:tableId/*` - Nested routes (e.g., GET /api/tables/123/records)
|
|
64
|
+
*
|
|
65
|
+
* Without both patterns, middleware won't run for all routes.
|
|
66
|
+
*
|
|
67
|
+
* **Context Variables Available After Chain** (via `ContextWithTableAndRole`):
|
|
68
|
+
* - `session`: Session (authenticated user or guest)
|
|
69
|
+
* - `tableName`: string (resolved table name)
|
|
70
|
+
* - `tableId`: string (original parameter)
|
|
71
|
+
* - `userRole`: string (user's role in organization or 'guest')
|
|
72
|
+
*
|
|
73
|
+
* Uses method chaining for proper Hono RPC type inference.
|
|
74
|
+
*
|
|
75
|
+
* @param honoApp - Hono instance to chain routes onto
|
|
76
|
+
* @param app - Application configuration containing table metadata
|
|
77
|
+
* @returns Hono app with table routes chained
|
|
78
|
+
*/
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Hono type inference with middleware requires flexible typing
|
|
80
|
+
export function chainTableRoutes<T extends Hono<any, any, any>>(honoApp: T, app: App) {
|
|
81
|
+
// Apply middleware for routes with :tableId parameter
|
|
82
|
+
// Middleware order: validateTable (404 if not found) → enrichUserRole (fetch role if auth configured)
|
|
83
|
+
// Note: requireAuth() is conditionally applied in api-routes.ts based on app.auth
|
|
84
|
+
//
|
|
85
|
+
// Two patterns needed:
|
|
86
|
+
// - '/api/tables/:tableId' for exact match (e.g., GET /api/tables/:tableId)
|
|
87
|
+
// - '/api/tables/:tableId/*' for nested routes (e.g., GET /api/tables/:tableId/records)
|
|
88
|
+
//
|
|
89
|
+
// enrichUserRole is only applied when auth is configured (app.auth exists)
|
|
90
|
+
// For apps without auth, provideGuestContext creates a guest session and role
|
|
91
|
+
const honoWithMiddleware = app.auth
|
|
92
|
+
? honoApp
|
|
93
|
+
.use('/api/tables/:tableId', validateTable(app))
|
|
94
|
+
.use('/api/tables/:tableId', enrichUserRole())
|
|
95
|
+
.use('/api/tables/:tableId/*', validateTable(app))
|
|
96
|
+
.use('/api/tables/:tableId/*', enrichUserRole())
|
|
97
|
+
: honoApp
|
|
98
|
+
.use('/api/tables/:tableId', validateTable(app))
|
|
99
|
+
.use('/api/tables/:tableId', provideGuestContext())
|
|
100
|
+
.use('/api/tables/:tableId/*', validateTable(app))
|
|
101
|
+
.use('/api/tables/:tableId/*', provideGuestContext())
|
|
102
|
+
|
|
103
|
+
// Route registration order matters for Hono's router.
|
|
104
|
+
// More specific routes (batch/restore) must be registered BEFORE
|
|
105
|
+
// parameterized routes (:recordId/restore) to avoid route collisions.
|
|
106
|
+
return chainViewRoutesMethods(
|
|
107
|
+
chainRecordRoutesMethods(
|
|
108
|
+
chainBatchRoutesMethods(chainTableRoutesMethods(honoWithMiddleware, app), app),
|
|
109
|
+
app
|
|
110
|
+
),
|
|
111
|
+
app
|
|
112
|
+
)
|
|
113
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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 { parseFilterParameter } from './filter-parser'
|
|
9
|
+
import { parseFormulaToFilter } from './formula-parser'
|
|
10
|
+
import type { App } from '@/domain/models/app'
|
|
11
|
+
import type { Context } from 'hono'
|
|
12
|
+
|
|
13
|
+
type FilterStructure =
|
|
14
|
+
| {
|
|
15
|
+
readonly and?: readonly {
|
|
16
|
+
readonly field: string
|
|
17
|
+
readonly operator: string
|
|
18
|
+
readonly value: unknown
|
|
19
|
+
}[]
|
|
20
|
+
}
|
|
21
|
+
| undefined
|
|
22
|
+
|
|
23
|
+
type FilterResult =
|
|
24
|
+
| { readonly error: false; readonly value: FilterStructure }
|
|
25
|
+
| { readonly error: true; readonly response?: Response }
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parse filter parameter from request (formula or standard filter)
|
|
29
|
+
*/
|
|
30
|
+
export function parseFilter(
|
|
31
|
+
c: Context,
|
|
32
|
+
app: App,
|
|
33
|
+
tableName: string,
|
|
34
|
+
userRole: string
|
|
35
|
+
): FilterResult {
|
|
36
|
+
const filterByFormula = c.req.query('filterByFormula')
|
|
37
|
+
|
|
38
|
+
if (filterByFormula) {
|
|
39
|
+
const parsedFormula = parseFormulaToFilter(filterByFormula)
|
|
40
|
+
return parsedFormula ? { error: false, value: parsedFormula } : { error: true }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const parsedFilterResult = parseFilterParameter({
|
|
44
|
+
filterParam: c.req.query('filter'),
|
|
45
|
+
app,
|
|
46
|
+
tableName,
|
|
47
|
+
userRole,
|
|
48
|
+
c,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
return parsedFilterResult.success
|
|
52
|
+
? { error: false, value: parsedFilterResult.filter }
|
|
53
|
+
: { error: true, response: parsedFilterResult.error }
|
|
54
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
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 { Context } from 'hono'
|
|
9
|
+
|
|
10
|
+
type AggregateParams = {
|
|
11
|
+
readonly count?: boolean
|
|
12
|
+
readonly sum?: readonly string[]
|
|
13
|
+
readonly avg?: readonly string[]
|
|
14
|
+
readonly min?: readonly string[]
|
|
15
|
+
readonly max?: readonly string[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Parse aggregate JSON parameter
|
|
20
|
+
*/
|
|
21
|
+
export function parseAggregateParam(
|
|
22
|
+
aggregateParam: string | undefined
|
|
23
|
+
): AggregateParams | undefined {
|
|
24
|
+
if (!aggregateParam) return undefined
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(aggregateParam) as AggregateParams
|
|
28
|
+
} catch {
|
|
29
|
+
// Invalid JSON, ignore aggregation
|
|
30
|
+
return undefined
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parse list records query parameters
|
|
36
|
+
*/
|
|
37
|
+
export function parseListRecordsParams(c: Context): {
|
|
38
|
+
readonly includeDeleted: boolean
|
|
39
|
+
readonly format: 'display' | undefined
|
|
40
|
+
readonly timezone: string | undefined
|
|
41
|
+
readonly sort: string | undefined
|
|
42
|
+
readonly fields: string | undefined
|
|
43
|
+
readonly limit: number | undefined
|
|
44
|
+
readonly offset: number | undefined
|
|
45
|
+
readonly aggregate: AggregateParams | undefined
|
|
46
|
+
} {
|
|
47
|
+
const includeDeleted = c.req.query('includeDeleted') === 'true'
|
|
48
|
+
const format = c.req.query('format') === 'display' ? ('display' as const) : undefined
|
|
49
|
+
const timezone = c.req.query('timezone')
|
|
50
|
+
const sort = c.req.query('sort')
|
|
51
|
+
const fields = c.req.query('fields')
|
|
52
|
+
const limitParam = c.req.query('limit')
|
|
53
|
+
const offsetParam = c.req.query('offset')
|
|
54
|
+
const limit = limitParam ? Number(limitParam) : undefined
|
|
55
|
+
const offset = offsetParam ? Number(offsetParam) : undefined
|
|
56
|
+
const aggregate = parseAggregateParam(c.req.query('aggregate'))
|
|
57
|
+
|
|
58
|
+
return { includeDeleted, format, timezone, sort, fields, limit, offset, aggregate }
|
|
59
|
+
}
|