bazis 0.96.1
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/LICENSE +21 -0
- package/README.md +146 -0
- package/cli/build.ts +147 -0
- package/cli/codegen.ts +40 -0
- package/cli/generateModule.ts +187 -0
- package/cli/generateProject.ts +183 -0
- package/cli/main.ts +125 -0
- package/cli/moduleRegistration.ts +127 -0
- package/cli/naming.ts +124 -0
- package/cli/parseCli.ts +151 -0
- package/cli/templates/module.ts +497 -0
- package/cli/templates/pack.ts +76 -0
- package/cli/templates/passport.ts +178 -0
- package/core/agent/AgentContextBuilder.ts +567 -0
- package/core/agent/AgentDataDefinition.ts +8 -0
- package/core/agent/AgentRegistry.ts +539 -0
- package/core/agent/AgentRuntime.ts +47 -0
- package/core/agent/AgentToolExecutor.ts +2130 -0
- package/core/agent/AgentToolHooks.ts +39 -0
- package/core/agent/ModelProvider.ts +21 -0
- package/core/agent/README.md +229 -0
- package/core/agent/Tool.contract.ts +20 -0
- package/core/agent/decorators/Agent.ts +11 -0
- package/core/agent/decorators/Prompt.ts +11 -0
- package/core/agent/decorators/Task.ts +29 -0
- package/core/agent/decorators/Tool.ts +12 -0
- package/core/agent/errors.ts +43 -0
- package/core/agent/generated/catalog.ts +26 -0
- package/core/agent/index.ts +170 -0
- package/core/agent/internal/AgentExecutionDriver.ts +1497 -0
- package/core/agent/internal/AgentExecutionState.ts +775 -0
- package/core/agent/internal/AgentJsonSchema.ts +150 -0
- package/core/agent/internal/AgentModelBinding.ts +57 -0
- package/core/agent/internal/Schema.contract.ts +36 -0
- package/core/agent/internal/ToolAudit.projector.ts +163 -0
- package/core/agent/internal/ToolContract.validator.ts +102 -0
- package/core/agent/metadata.ts +398 -0
- package/core/agent/moduleContributions-v1.ts +113 -0
- package/core/agent/semantic.ts +903 -0
- package/core/agent/session/codec.ts +33 -0
- package/core/agent/session/contracts.ts +96 -0
- package/core/agent/session/entities.ts +84 -0
- package/core/agent/session/protector.ts +92 -0
- package/core/app/index.ts +5 -0
- package/core/app/runApp.ts +293 -0
- package/core/app/ui/index.ts +29 -0
- package/core/app/ui/uiProfileResolver.ts +966 -0
- package/core/app/ui/uiSurfaceHosting.ts +38 -0
- package/core/app/ui/uiSurfaceHttp.ts +476 -0
- package/core/background/BackgroundService.ts +223 -0
- package/core/background/backgroundModule.ts +42 -0
- package/core/background/decorator.ts +43 -0
- package/core/background/delay.ts +27 -0
- package/core/background/index.ts +15 -0
- package/core/cache/COMPATIBILITY.md +13 -0
- package/core/cache/ICache.ts +71 -0
- package/core/cache/MemoryCache.ts +394 -0
- package/core/cache/README.md +83 -0
- package/core/cache/SPEC.md +1355 -0
- package/core/cache/cacheModule.ts +21 -0
- package/core/cache/decorators/Cacheable.ts +38 -0
- package/core/cache/decorators/CacheableRedis.ts +39 -0
- package/core/cache/decorators/OutputCache.ts +33 -0
- package/core/cache/decorators/OutputRedisCache.ts +33 -0
- package/core/cache/decorators/cacheableMetadata.ts +78 -0
- package/core/cache/decorators/cacheableRedisMetadata.ts +81 -0
- package/core/cache/decorators/outputCacheMetadata.ts +71 -0
- package/core/cache/decorators/outputRedisCacheMetadata.ts +77 -0
- package/core/cache/di/classProviderHook.ts +26 -0
- package/core/cache/distributed/CacheCodec.ts +5 -0
- package/core/cache/distributed/DistributedCache.ts +403 -0
- package/core/cache/distributed/DistributedCacheDriver.ts +83 -0
- package/core/cache/distributed/DistributedCacheStores.ts +28 -0
- package/core/cache/distributed/IDistributedCache.ts +40 -0
- package/core/cache/distributed/NamedCacheRegistry.ts +23 -0
- package/core/cache/distributed/codecs.ts +32 -0
- package/core/cache/distributed/index.ts +19 -0
- package/core/cache/errors/CacheError.ts +16 -0
- package/core/cache/http/CachedHttpPayload.ts +213 -0
- package/core/cache/http/applyClientCacheHeaders.ts +61 -0
- package/core/cache/http/buildOutputCacheKey.ts +154 -0
- package/core/cache/http/buildRedisOutputCacheKey.ts +16 -0
- package/core/cache/http/composeOutputCache.ts +130 -0
- package/core/cache/http/outputCacheMiddleware.ts +111 -0
- package/core/cache/http/outputCacheSecurityWarning.ts +58 -0
- package/core/cache/http/outputRedisCacheMiddleware.ts +118 -0
- package/core/cache/index.ts +97 -0
- package/core/cache/internal/AutomaticCacheKey.ts +106 -0
- package/core/cache/internal/CacheKeyGuard.ts +56 -0
- package/core/cache/internal/buildCacheProviders.ts +146 -0
- package/core/cache/internal/normalizeCacheKey.ts +9 -0
- package/core/cache/internal/resolveCachePolicy.ts +32 -0
- package/core/cache/internal/resolveCacheableRedisPolicy.ts +17 -0
- package/core/cache/internal/resolveOutputRedisCachePolicy.ts +17 -0
- package/core/cache/providers/cachedProviders.ts +68 -0
- package/core/cache/services/cacheProxy.ts +243 -0
- package/core/cache/tokens/CACHE_POLICIES.ts +5 -0
- package/core/cache/tokens/DISTRIBUTED_CACHE.ts +30 -0
- package/core/cache/types/CacheEntry.ts +7 -0
- package/core/cache/types/CacheOptions.ts +60 -0
- package/core/cache/types/CachePolicy.ts +74 -0
- package/core/cache/types/CacheableRedisOptions.ts +23 -0
- package/core/cache/types/OutputRedisCacheOptions.ts +25 -0
- package/core/di/README.md +104 -0
- package/core/di/ServiceCollection.ts +185 -0
- package/core/di/ServiceProvider.ts +689 -0
- package/core/di/ServiceScope.ts +61 -0
- package/core/di/container.ts +38 -0
- package/core/di/errors/AmbiguousNamedDependencyError.ts +10 -0
- package/core/di/errors/AsyncResolutionRequiredError.ts +12 -0
- package/core/di/errors/CircularDependencyError.ts +10 -0
- package/core/di/errors/ClassDependenciesMismatchError.ts +15 -0
- package/core/di/errors/DiError.ts +6 -0
- package/core/di/errors/InvalidProviderError.ts +9 -0
- package/core/di/errors/ModuleEncapsulationError.ts +7 -0
- package/core/di/errors/ModuleOwnedProviderConflictError.ts +8 -0
- package/core/di/errors/NamedDependencyNotFoundError.ts +7 -0
- package/core/di/errors/OptionsValidationError.ts +7 -0
- package/core/di/errors/ProviderNotFoundError.ts +10 -0
- package/core/di/errors/ScopeDisposedError.ts +7 -0
- package/core/di/errors/ScopedServiceFromRootError.ts +9 -0
- package/core/di/errors/ServiceValidationError.ts +7 -0
- package/core/di/errors/index.ts +14 -0
- package/core/di/extensions/activator.ts +39 -0
- package/core/di/extensions/application.ts +117 -0
- package/core/di/extensions/hosted-service.ts +124 -0
- package/core/di/extensions/index.ts +23 -0
- package/core/di/extensions/options-reloadable.ts +137 -0
- package/core/di/extensions/options.ts +91 -0
- package/core/di/extensions/service-provider-token.ts +21 -0
- package/core/di/index.ts +97 -0
- package/core/di/internal/GraphValidator.ts +119 -0
- package/core/di/internal/HostedServicePlan.ts +158 -0
- package/core/di/internal/NamedTokenIndex.ts +55 -0
- package/core/di/internal/OpenGenericRegistration.ts +14 -0
- package/core/di/internal/ResolutionPlan.ts +18 -0
- package/core/di/internal/ResolutionScopeState.ts +19 -0
- package/core/di/internal/ResolutionTracker.ts +153 -0
- package/core/di/internal/ScopeLifecycle.ts +159 -0
- package/core/di/internal/ServiceRegistration.ts +14 -0
- package/core/di/internal/ServiceRegistry.ts +252 -0
- package/core/di/internal/classDeps.ts +38 -0
- package/core/di/internal/disposal.ts +58 -0
- package/core/di/internal/providerDeps.ts +26 -0
- package/core/di/module/DI.ts +146 -0
- package/core/di/module/Global.ts +25 -0
- package/core/di/module/Module.ts +44 -0
- package/core/di/module/ModuleRegistrar.ts +69 -0
- package/core/di/module/autoDeps.ts +39 -0
- package/core/di/module/classProviderHooks.ts +66 -0
- package/core/di/module/collectModuleConfigs.ts +57 -0
- package/core/di/module/collectModuleControllers.ts +53 -0
- package/core/di/module/collectModuleUiProfiles.ts +53 -0
- package/core/di/module/createContainer.ts +261 -0
- package/core/di/module/encapsulation.ts +416 -0
- package/core/di/module/encapsulationHooks.ts +36 -0
- package/core/di/module/generatedProviderAttachments.ts +102 -0
- package/core/di/module/index.ts +41 -0
- package/core/di/module/moduleExtensions.ts +35 -0
- package/core/di/module/moduleNameBinding.ts +136 -0
- package/core/di/module/moduleOwnedProviderContributors.ts +409 -0
- package/core/di/module/providerSelection.ts +90 -0
- package/core/di/module/shortcuts.ts +111 -0
- package/core/di/module/types/BazisModule.ts +71 -0
- package/core/di/module/types/DiRegistrar.ts +24 -0
- package/core/di/module/types/index.ts +2 -0
- package/core/di/provider/index.ts +26 -0
- package/core/di/provider/providerGuards.ts +17 -0
- package/core/di/provider/types/AsyncFactoryProvider.ts +15 -0
- package/core/di/provider/types/ClassProvider.ts +18 -0
- package/core/di/provider/types/FactoryProvider.ts +20 -0
- package/core/di/provider/types/KeyedDependency.ts +20 -0
- package/core/di/provider/types/LazyDependency.ts +31 -0
- package/core/di/provider/types/NamedDependency.ts +16 -0
- package/core/di/provider/types/Provider.ts +10 -0
- package/core/di/provider/types/ProviderDefinition.ts +11 -0
- package/core/di/provider/types/ProviderDependencyList.ts +11 -0
- package/core/di/provider/types/ProviderLifetime.ts +1 -0
- package/core/di/provider/types/ProviderOwnership.ts +7 -0
- package/core/di/provider/types/ResolvedDeps.ts +18 -0
- package/core/di/provider/types/ValueProvider.ts +6 -0
- package/core/di/provider/types/index.ts +13 -0
- package/core/di/token.ts +84 -0
- package/core/di/types/BuildServiceProviderOptions.ts +4 -0
- package/core/di/types/ServiceKey.ts +1 -0
- package/core/di/types/ServiceResolver.ts +16 -0
- package/core/di/types/index.ts +3 -0
- package/core/generatedFingerprint.ts +60 -0
- package/core/generatedRuntime.ts +187 -0
- package/core/grpc/Grpc.module.ts +23 -0
- package/core/grpc/GrpcBinding.contract.ts +35 -0
- package/core/grpc/GrpcCall.ts +73 -0
- package/core/grpc/GrpcClient.ts +172 -0
- package/core/grpc/GrpcClientCall.ts +267 -0
- package/core/grpc/GrpcError.ts +19 -0
- package/core/grpc/GrpcRequest.binding.ts +45 -0
- package/core/grpc/GrpcServer.ts +204 -0
- package/core/grpc/GrpcStatus.ts +8 -0
- package/core/grpc/GrpcTransport.ts +174 -0
- package/core/grpc/Metadata.ts +73 -0
- package/core/grpc/ProtoCodec.ts +225 -0
- package/core/grpc/ProtoParser.ts +192 -0
- package/core/grpc/ProtoReader.ts +34 -0
- package/core/grpc/ProtoSchema.ts +52 -0
- package/core/grpc/ServerCredentials.ts +30 -0
- package/core/grpc/clientContracts.ts +39 -0
- package/core/grpc/contracts.ts +45 -0
- package/core/grpc/decorators.ts +82 -0
- package/core/grpc/grpcClientProvider.ts +9 -0
- package/core/grpc/index.ts +17 -0
- package/core/grpc/moduleControllers.ts +38 -0
- package/core/grpc/protobuf.ts +80 -0
- package/core/grpc/serviceDefinition.ts +20 -0
- package/core/http/Authorization/Authorize.ts +52 -0
- package/core/http/Authorization/authorizeComposer.ts +31 -0
- package/core/http/Authorization/metadata.ts +158 -0
- package/core/http/Binding/ParameterBinder.ts +109 -0
- package/core/http/Binding/RequestModel.ts +27 -0
- package/core/http/Binding/autoBindings.ts +125 -0
- package/core/http/Binding/bindings.ts +19 -0
- package/core/http/Binding/convert.ts +48 -0
- package/core/http/Binding/generatedSpec.ts +18 -0
- package/core/http/Binding/listModelRegistry.ts +47 -0
- package/core/http/Binding/modelBinder.ts +345 -0
- package/core/http/Binding/modelValidator.ts +30 -0
- package/core/http/Binding/requestModelRegistry.ts +117 -0
- package/core/http/Decorators/attributes.ts +92 -0
- package/core/http/Decorators/controller.ts +24 -0
- package/core/http/Decorators/metadata.ts +107 -0
- package/core/http/Decorators/routes.ts +131 -0
- package/core/http/Errors/HttpError.ts +92 -0
- package/core/http/HttpContext/HttpContext.ts +148 -0
- package/core/http/HttpContext/ResponseBuilder.ts +37 -0
- package/core/http/HttpContext/inspectableRedirects.ts +47 -0
- package/core/http/HttpContext/principal.ts +24 -0
- package/core/http/HttpContext/responseLifetime.ts +45 -0
- package/core/http/HttpServer.ts +627 -0
- package/core/http/Middleware/accessLog.ts +90 -0
- package/core/http/Middleware/cors.ts +160 -0
- package/core/http/Middleware/errorHandler.ts +100 -0
- package/core/http/Middleware/pipeline.ts +21 -0
- package/core/http/Middleware/rateLimit.ts +104 -0
- package/core/http/Middleware/securityHeaders.ts +118 -0
- package/core/http/Middleware/types.ts +19 -0
- package/core/http/OpenApi/generatedOpenApiRegistry.ts +102 -0
- package/core/http/OpenApi/openApiDocs.ts +75 -0
- package/core/http/OpenApi/openApiDocument.ts +356 -0
- package/core/http/README.md +139 -0
- package/core/http/Results/HttpResult.ts +19 -0
- package/core/http/Results/normalize.ts +93 -0
- package/core/http/Results/results.ts +70 -0
- package/core/http/Routing/Router.ts +201 -0
- package/core/http/Routing/RouterBuilder.ts +184 -0
- package/core/http/Routing/template.ts +112 -0
- package/core/http/SPEC.md +981 -0
- package/core/http/Versioning/VersionReader.ts +20 -0
- package/core/http/WebSocketUpgrade.ts +29 -0
- package/core/http/composeRouteMiddlewareComposers.ts +21 -0
- package/core/http/correlation/createCorrelationIdMiddleware.ts +79 -0
- package/core/http/httpModule.ts +48 -0
- package/core/http/index.ts +140 -0
- package/core/http/middlewareTokens.ts +48 -0
- package/core/http/options.ts +134 -0
- package/core/http-client/index.ts +105 -0
- package/core/infra/Infra.ts +158 -0
- package/core/infra/InfraConnector.ts +86 -0
- package/core/infra/InfraLifecycle.ts +86 -0
- package/core/infra/cache/RedisDistributedCacheBackend.ts +88 -0
- package/core/infra/cache/RedisDistributedCacheDriver.ts +186 -0
- package/core/infra/cache/index.ts +7 -0
- package/core/infra/connectorConfig.ts +29 -0
- package/core/infra/connectorIdentity.ts +39 -0
- package/core/infra/connectors/codex/CodexAppServer.ts +146 -0
- package/core/infra/connectors/codex/CodexAppServerClient.ts +258 -0
- package/core/infra/connectors/codex/CodexPolicy.ts +114 -0
- package/core/infra/connectors/codex/CodexTurn.ts +133 -0
- package/core/infra/connectors/codex/contracts.ts +82 -0
- package/core/infra/connectors/codex.ts +26 -0
- package/core/infra/connectors/llm.ts +78 -0
- package/core/infra/connectors/llmRouter.ts +443 -0
- package/core/infra/connectors/openaiCompatible.ts +590 -0
- package/core/infra/connectors/openaiTextStream.ts +134 -0
- package/core/infra/connectors/opensearch.ts +307 -0
- package/core/infra/connectors/postgres.ts +105 -0
- package/core/infra/connectors/redis.ts +132 -0
- package/core/infra/index.ts +58 -0
- package/core/kernel/ApplicationLifetime.ts +91 -0
- package/core/kernel/Bazis.ts +73 -0
- package/core/kernel/Environment.ts +53 -0
- package/core/kernel/Kernel.ts +391 -0
- package/core/kernel/KernelBuilder.ts +172 -0
- package/core/kernel/LifecycleCoordinator.ts +204 -0
- package/core/kernel/README.md +66 -0
- package/core/kernel/SupervisedHostedService.ts +147 -0
- package/core/kernel/config/ConfigRegistry.ts +48 -0
- package/core/kernel/config/Configuration.ts +110 -0
- package/core/kernel/config/README.md +62 -0
- package/core/kernel/config/Secret.ts +29 -0
- package/core/kernel/config/addConfigOptions.ts +63 -0
- package/core/kernel/config/defineConfig.ts +211 -0
- package/core/kernel/config/sources.ts +123 -0
- package/core/kernel/correlation/requestContext.ts +57 -0
- package/core/kernel/errors/ConfigKeyMissingError.ts +7 -0
- package/core/kernel/errors/KernelError.ts +7 -0
- package/core/kernel/errors/ShutdownTimeoutError.ts +7 -0
- package/core/kernel/errors/StartupAbortedError.ts +7 -0
- package/core/kernel/errors/StartupTimeoutError.ts +7 -0
- package/core/kernel/errors/index.ts +5 -0
- package/core/kernel/events/EventBus.ts +140 -0
- package/core/kernel/events/EventToken.ts +17 -0
- package/core/kernel/events/eventSubscribers.ts +126 -0
- package/core/kernel/events/kernelEvents.ts +16 -0
- package/core/kernel/events/onEvent.ts +32 -0
- package/core/kernel/health/HealthCheckContracts.ts +42 -0
- package/core/kernel/health/HealthService.ts +93 -0
- package/core/kernel/index.ts +96 -0
- package/core/kernel/internal/awaitAbortable.ts +18 -0
- package/core/kernel/internal/awaitShutdown.ts +21 -0
- package/core/kernel/internal/reportDiagnosticFailure.ts +8 -0
- package/core/kernel/internal/validateTimeout.ts +8 -0
- package/core/kernel/lifecycleHooks.ts +18 -0
- package/core/kernel/logging/ConsoleLogger.ts +68 -0
- package/core/kernel/logging/Logger.ts +22 -0
- package/core/kernel/types/ConfigSource.ts +9 -0
- package/core/kernel/types/EnvironmentName.ts +1 -0
- package/core/kernel/types/KernelOptions.ts +23 -0
- package/core/kernel/types/LifecycleHook.ts +18 -0
- package/core/kernel/types/index.ts +4 -0
- package/core/orm/DATABASE_PROVIDER.ts +5 -0
- package/core/orm/OrmHostedPlan.validator.ts +55 -0
- package/core/orm/OrmLifecycle.ts +146 -0
- package/core/orm/OrmOwnedStoreLifecycle.ts +96 -0
- package/core/orm/buildOrmProviders.ts +152 -0
- package/core/orm/databaseConnector.ts +108 -0
- package/core/orm/encapsulationHook.ts +19 -0
- package/core/orm/index.ts +95 -0
- package/core/orm/listQuery.ts +198 -0
- package/core/orm/ormModule.ts +173 -0
- package/core/orm/ownedStoreContributions.ts +195 -0
- package/core/orm/registerRepositories.ts +27 -0
- package/core/orm/repository.ts +45 -0
- package/core/orm/strictSchemaHostedIdentity.ts +17 -0
- package/core/scripts/agent-codegen.ts +1166 -0
- package/core/scripts/di-generate-target.ts +90 -0
- package/core/scripts/di-generate.ts +1837 -0
- package/core/scripts/orm-predicate-codegen.ts +33 -0
- package/core/scripts/request-model-codegen.ts +326 -0
- package/core/websocket/DESIGN.md +297 -0
- package/core/websocket/WsRateLimiter.ts +101 -0
- package/core/websocket/adapter/adapter.interface.ts +45 -0
- package/core/websocket/adapter/in-memory.adapter.ts +190 -0
- package/core/websocket/adapter/redis-delivery-scripts.ts +135 -0
- package/core/websocket/adapter/redis-delivery-store.ts +86 -0
- package/core/websocket/adapter/redis-operations.ts +33 -0
- package/core/websocket/adapter/redis.adapter.ts +218 -0
- package/core/websocket/codec/binary.codec.ts +87 -0
- package/core/websocket/codec/json.codec.ts +34 -0
- package/core/websocket/codec/packet-codec.interface.ts +17 -0
- package/core/websocket/decorators.ts +165 -0
- package/core/websocket/explorer.ts +126 -0
- package/core/websocket/index.ts +78 -0
- package/core/websocket/middleware.ts +146 -0
- package/core/websocket/packet-codec.ts +75 -0
- package/core/websocket/reliable-delivery.ts +56 -0
- package/core/websocket/session-creation.ts +16 -0
- package/core/websocket/session-manager.ts +856 -0
- package/core/websocket/socket-wrapper.ts +314 -0
- package/core/websocket/topic-cache.ts +59 -0
- package/core/websocket/types.ts +122 -0
- package/core/websocket/websocketModule.ts +96 -0
- package/core/websocket/ws-dispatch.ts +123 -0
- package/core/websocket/ws-server.ts +1707 -0
- package/index.ts +281 -0
- package/library/boundary/diagnostics-v1.ts +136 -0
- package/library/boundary/index.ts +73 -0
- package/library/boundary/json/decodeBoundedJsonV1.ts +518 -0
- package/library/boundary/json/limits-v1.ts +46 -0
- package/library/boundary/json/normalizeBoundedJsonV1.ts +256 -0
- package/library/boundary/json/types-v1.ts +11 -0
- package/library/boundary/schema/boundarySchemaHashV1.ts +6 -0
- package/library/boundary/schema/collectBoundaryValueDiagnosticsV1.ts +325 -0
- package/library/boundary/schema/decodeBoundarySchemaV1.ts +646 -0
- package/library/boundary/schema/index.ts +8 -0
- package/library/boundary/schema/types-v1.ts +99 -0
- package/library/boundary/schema/validateBoundaryValueV1.ts +58 -0
- package/library/boundary/serialization/canonicalJsonV1.ts +66 -0
- package/library/http-client/HttpClient.ts +969 -0
- package/library/http-client/HttpClientFactory.ts +45 -0
- package/library/http-client/errors.ts +68 -0
- package/library/http-client/index.ts +43 -0
- package/library/http-client/interceptors.ts +40 -0
- package/library/http-client/progress.ts +62 -0
- package/library/http-client/redirectProtocol.ts +6 -0
- package/library/http-client/serialize.ts +133 -0
- package/library/http-client/types.ts +174 -0
- package/library/jsonapi/ListQueryError.ts +25 -0
- package/library/jsonapi/ListRequest.ts +37 -0
- package/library/jsonapi/decorators.ts +161 -0
- package/library/jsonapi/document.ts +124 -0
- package/library/jsonapi/index.ts +40 -0
- package/library/jsonapi/parseListQuery.ts +291 -0
- package/library/jsonapi/types.ts +125 -0
- package/library/jwt/JwtEncoder.ts +40 -0
- package/library/jwt/JwtKeyRing.ts +164 -0
- package/library/jwt/JwtValidator.ts +210 -0
- package/library/jwt/TokenIssuer.ts +149 -0
- package/library/jwt/TokenService.ts +51 -0
- package/library/jwt/base64url.ts +61 -0
- package/library/jwt/claims.ts +36 -0
- package/library/jwt/errors.ts +44 -0
- package/library/jwt/index.ts +59 -0
- package/library/jwt/limits.ts +8 -0
- package/library/jwt/signing/Hs256Algorithm.ts +62 -0
- package/library/jwt/signing/Rs256Algorithm.ts +133 -0
- package/library/jwt/signing/SigningAlgorithm.ts +18 -0
- package/library/jwt/signing/pem.ts +30 -0
- package/library/openapi/codegen.ts +875 -0
- package/library/openapi/document.ts +326 -0
- package/library/openapi/index.ts +13 -0
- package/library/openapi/types.ts +69 -0
- package/library/openapi/ui.ts +182 -0
- package/library/orm/DatabaseFacade.ts +209 -0
- package/library/orm/DbContext.ts +107 -0
- package/library/orm/DbContextFactory.ts +25 -0
- package/library/orm/DbContextOptions.ts +33 -0
- package/library/orm/Metadata/DynamicModelBuilder.ts +497 -0
- package/library/orm/Metadata/KeyTuple.ts +36 -0
- package/library/orm/Metadata/ModelBuilder.ts +299 -0
- package/library/orm/Metadata/OrmModel.ts +80 -0
- package/library/orm/Metadata/ValueConverter.ts +57 -0
- package/library/orm/Metadata/conventions.ts +54 -0
- package/library/orm/Metadata/decorators.ts +396 -0
- package/library/orm/Metadata/types.ts +140 -0
- package/library/orm/Providers/OwnedStoreCatalog.reader.ts +1802 -0
- package/library/orm/Providers/PostgresDialect.ts +234 -0
- package/library/orm/Providers/PostgresProvider.ts +1494 -0
- package/library/orm/Providers/PostgresSchema.decoder.ts +107 -0
- package/library/orm/Providers/limits.ts +29 -0
- package/library/orm/Providers/operationDeadline.ts +43 -0
- package/library/orm/Providers/ormOwnedStoreRuntime.ts +19 -0
- package/library/orm/Providers/ormTransactionRuntime.ts +117 -0
- package/library/orm/Providers/propertyConversion.ts +14 -0
- package/library/orm/Providers/resolvedForeignKey.ts +16 -0
- package/library/orm/Providers/transactionCallbacks.ts +45 -0
- package/library/orm/Providers/transactionOutcome.ts +82 -0
- package/library/orm/Providers/transactionScopes.ts +78 -0
- package/library/orm/Providers/types.ts +207 -0
- package/library/orm/Query/DbSet.ts +78 -0
- package/library/orm/Query/EntityQuery.ts +247 -0
- package/library/orm/Query/ImmediateMutations.ts +226 -0
- package/library/orm/Query/IncludeLoader.ts +200 -0
- package/library/orm/Query/QueryPlan.ts +55 -0
- package/library/orm/Query/SqlTranslator.ts +195 -0
- package/library/orm/Query/conditions.ts +127 -0
- package/library/orm/Query/materialize.ts +39 -0
- package/library/orm/README.md +256 -0
- package/library/orm/Repository/README.md +684 -0
- package/library/orm/Repository/Repository.ts +68 -0
- package/library/orm/Saving/CommandBuilder.ts +139 -0
- package/library/orm/Saving/ExecutionStrategy.ts +128 -0
- package/library/orm/Saving/SaveExecutor.ts +413 -0
- package/library/orm/Saving/applyConventions.ts +64 -0
- package/library/orm/Schema/CheckExpression.ts +327 -0
- package/library/orm/Schema/ExactSchemaVerifier.ts +168 -0
- package/library/orm/Schema/ExpectedSchema.ts +43 -0
- package/library/orm/Schema/MigrationRunner.ts +146 -0
- package/library/orm/Schema/Migrator.ts +73 -0
- package/library/orm/Schema/OrmOwnedStore.ts +123 -0
- package/library/orm/Schema/OwnedStoreAdmission.ts +752 -0
- package/library/orm/Schema/OwnedStoreCanonical.ts +273 -0
- package/library/orm/Schema/OwnedStoreCatalog.ts +1325 -0
- package/library/orm/Schema/SafeAdditiveSchema.ts +100 -0
- package/library/orm/Schema/SchemaAdmission.ts +62 -0
- package/library/orm/Schema/SchemaDiffer.ts +78 -0
- package/library/orm/Schema/formatSchemaDifferences.ts +62 -0
- package/library/orm/Schema/introspection.ts +52 -0
- package/library/orm/Schema/physicalColumnTypes.ts +37 -0
- package/library/orm/Schema/tableKey.ts +26 -0
- package/library/orm/Tracking/ChangeTracker.ts +448 -0
- package/library/orm/Tracking/EntityState.ts +13 -0
- package/library/orm/Transactions/OrmTransaction.ts +30 -0
- package/library/orm/Transactions/TransactionScopeCoordinator.ts +377 -0
- package/library/orm/errors.ts +190 -0
- package/library/orm/index.ts +162 -0
- package/library/orm/runtime.ts +14 -0
- package/library/redaction/index.ts +166 -0
- package/library/ui/UiProfile.ts +77 -0
- package/library/ui/compiled-v1.ts +131 -0
- package/library/ui/compiler/compileUiSurfaceV1.ts +717 -0
- package/library/ui/compiler/contracts-v1.ts +25 -0
- package/library/ui/compiler/index.ts +12 -0
- package/library/ui/compiler/openApiIndex.ts +246 -0
- package/library/ui/defineUiProfile.ts +340 -0
- package/library/ui/diagnostics-v1.ts +33 -0
- package/library/ui/index.ts +129 -0
- package/library/ui/profile-v1.ts +191 -0
- package/library/ui/serialization.ts +130 -0
- package/library/ui/uiProfileAuthoring.ts +289 -0
- package/library/ui/uiProfileAuthoringMetadata.ts +78 -0
- package/library/validation/MessageRegistry.ts +144 -0
- package/library/validation/README.md +62 -0
- package/library/validation/RuleCompiler.ts +183 -0
- package/library/validation/RuleEngine.ts +401 -0
- package/library/validation/SPEC.md +579 -0
- package/library/validation/ValidationError.ts +37 -0
- package/library/validation/ValidationResult.ts +63 -0
- package/library/validation/Validator.ts +101 -0
- package/library/validation/index.ts +16 -0
- package/library/validation/metadata.ts +69 -0
- package/library/validation/modelValidatorAdapter.ts +119 -0
- package/library/validation/types/ValidationCode.ts +69 -0
- package/library/validation/types/ValidatorOptions.ts +153 -0
- package/package.json +80 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { OrmError } from "../errors";
|
|
2
|
+
import { renderPostgresOnConflictDoNothing, renderPostgresSkipLocked } from "../Providers/PostgresDialect";
|
|
3
|
+
import type { EntityModel, PropertyModel } from "../Metadata/types";
|
|
4
|
+
import type { SqlDialect, SqlParam } from "../Providers/types";
|
|
5
|
+
import { encodeProperty } from "../Providers/propertyConversion";
|
|
6
|
+
import type { Condition } from "./conditions";
|
|
7
|
+
import type { QueryPlan } from "./QueryPlan";
|
|
8
|
+
|
|
9
|
+
export interface CompiledSql {
|
|
10
|
+
readonly sql: string;
|
|
11
|
+
readonly params: SqlParam[];
|
|
12
|
+
}
|
|
13
|
+
type ConditionValueEncoder = (property: PropertyModel, value: unknown) => SqlParam;
|
|
14
|
+
|
|
15
|
+
/** Cache of the quoted column list per (model, dialect), built once. */
|
|
16
|
+
const columnListCache = new WeakMap<EntityModel, Map<string, string>>();
|
|
17
|
+
|
|
18
|
+
/** Quoted `"a", "b", ...` list of all entity columns (memoized). */
|
|
19
|
+
export function quotedColumnList(model: EntityModel, dialect: SqlDialect): string {
|
|
20
|
+
let byDialect = columnListCache.get(model);
|
|
21
|
+
if (!byDialect) {
|
|
22
|
+
byDialect = new Map();
|
|
23
|
+
columnListCache.set(model, byDialect);
|
|
24
|
+
}
|
|
25
|
+
let list = byDialect.get(dialect.name);
|
|
26
|
+
if (list === undefined) {
|
|
27
|
+
list = model.properties.map((property) => dialect.quoteId(property.columnName)).join(", ");
|
|
28
|
+
byDialect.set(dialect.name, list);
|
|
29
|
+
}
|
|
30
|
+
return list;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Translates a query plan into parameterized SQL. Values go only into the
|
|
35
|
+
* parameter array (no concatenation of user data).
|
|
36
|
+
*/
|
|
37
|
+
export class SqlTranslator {
|
|
38
|
+
constructor(
|
|
39
|
+
private readonly model: EntityModel,
|
|
40
|
+
private readonly dialect: SqlDialect,
|
|
41
|
+
) {}
|
|
42
|
+
|
|
43
|
+
/** SELECT of all columns (or a projection) with WHERE/ORDER/LIMIT/OFFSET. */
|
|
44
|
+
selectAll(plan: QueryPlan): CompiledSql {
|
|
45
|
+
const params: SqlParam[] = [];
|
|
46
|
+
const columns =
|
|
47
|
+
plan.projections.length > 0
|
|
48
|
+
? plan.projections
|
|
49
|
+
.map(({ alias, property }) => {
|
|
50
|
+
const prop = this.requireProperty(property);
|
|
51
|
+
return `${this.dialect.quoteId(prop.columnName)} AS ${this.dialect.quoteId(alias)}`;
|
|
52
|
+
})
|
|
53
|
+
.join(", ")
|
|
54
|
+
: quotedColumnList(this.model, this.dialect);
|
|
55
|
+
let sql = `SELECT ${columns} FROM ${this.dialect.qualifyTable(this.model)}`;
|
|
56
|
+
sql += this.whereClause(plan, params);
|
|
57
|
+
sql += this.orderClause(plan);
|
|
58
|
+
sql += this.limitClause(plan, params);
|
|
59
|
+
if (plan.rowLock) {
|
|
60
|
+
sql += this.dialect.rowLockClause(plan.rowLock);
|
|
61
|
+
if (plan.skipLocked) sql += renderPostgresSkipLocked(this.dialect);
|
|
62
|
+
}
|
|
63
|
+
return { sql, params };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** SELECT COUNT(*) with the same filters (without order/limit). */
|
|
67
|
+
selectCount(plan: QueryPlan): CompiledSql {
|
|
68
|
+
const params: SqlParam[] = [];
|
|
69
|
+
let sql = `SELECT COUNT(*) AS count FROM ${this.dialect.qualifyTable(this.model)}`;
|
|
70
|
+
sql += this.whereClause(plan, params);
|
|
71
|
+
return { sql, params };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Private immediate-DML renderers; the public query DSL remains unchanged. */
|
|
75
|
+
immediateUpdate(values: readonly { readonly property: PropertyModel; readonly value: SqlParam }[], conditions: readonly Condition[], encode: ConditionValueEncoder): CompiledSql {
|
|
76
|
+
const params: SqlParam[] = values.map((value) => value.value);
|
|
77
|
+
const assignments = values.map((value, index) => `${this.dialect.quoteId(value.property.columnName)} = ${this.dialect.parameter(index)}`).join(", ");
|
|
78
|
+
return { sql: `UPDATE ${this.dialect.qualifyTable(this.model)} SET ${assignments}${this.immediateWhere(conditions, params, encode)}`, params };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
immediateDelete(conditions: readonly Condition[], encode: ConditionValueEncoder): CompiledSql {
|
|
82
|
+
const params: SqlParam[] = [];
|
|
83
|
+
return { sql: `DELETE FROM ${this.dialect.qualifyTable(this.model)}${this.immediateWhere(conditions, params, encode)}`, params };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private immediateWhere(conditions: readonly Condition[], params: SqlParam[], encode: ConditionValueEncoder): string {
|
|
87
|
+
if (conditions.length === 0) return "";
|
|
88
|
+
return ` WHERE ${conditions.map((condition) => this.condition(condition, params, encode)).join(" AND ")}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
immediateInsert(values: readonly { readonly property: PropertyModel; readonly value: SqlParam }[], conflictColumns: readonly string[]): CompiledSql {
|
|
92
|
+
const params = values.map((value) => value.value);
|
|
93
|
+
const columns = values.map((value) => this.dialect.quoteId(value.property.columnName)).join(", ");
|
|
94
|
+
const placeholders = values.map((_value, index) => this.dialect.parameter(index)).join(", ");
|
|
95
|
+
return { sql: `INSERT INTO ${this.dialect.qualifyTable(this.model)} (${columns}) VALUES (${placeholders})${renderPostgresOnConflictDoNothing(this.dialect, conflictColumns)}`, params };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private whereClause(plan: QueryPlan, params: SqlParam[], encode: ConditionValueEncoder = (property, value) => encodeProperty(property, value, this.dialect)): string {
|
|
99
|
+
const parts: string[] = [];
|
|
100
|
+
if (!plan.ignoreQueryFilters) {
|
|
101
|
+
for (const filter of this.model.queryFilters) {
|
|
102
|
+
parts.push(this.condition(filter, params, encode));
|
|
103
|
+
}
|
|
104
|
+
if (this.model.softDeleteProperty) {
|
|
105
|
+
parts.push(this.condition({ kind: "null", property: this.model.softDeleteProperty, negated: false }, params, encode));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const condition of plan.conditions) {
|
|
109
|
+
parts.push(this.condition(condition, params, encode));
|
|
110
|
+
}
|
|
111
|
+
if (parts.length === 0) {
|
|
112
|
+
return "";
|
|
113
|
+
}
|
|
114
|
+
return ` WHERE ${parts.join(" AND ")}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private orderClause(plan: QueryPlan): string {
|
|
118
|
+
if (plan.orders.length === 0) {
|
|
119
|
+
return "";
|
|
120
|
+
}
|
|
121
|
+
const parts = plan.orders.map((order) => {
|
|
122
|
+
const column = this.requireProperty(order.property).columnName;
|
|
123
|
+
return `${this.dialect.quoteId(column)}${order.descending ? " DESC" : " ASC"}`;
|
|
124
|
+
});
|
|
125
|
+
return ` ORDER BY ${parts.join(", ")}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private limitClause(plan: QueryPlan, params: SqlParam[]): string {
|
|
129
|
+
let sql = "";
|
|
130
|
+
if (plan.limit !== undefined) {
|
|
131
|
+
sql += ` LIMIT ${this.dialect.parameter(params.length)}`;
|
|
132
|
+
params.push(plan.limit);
|
|
133
|
+
}
|
|
134
|
+
if (plan.offset !== undefined) {
|
|
135
|
+
sql += ` OFFSET ${this.dialect.parameter(params.length)}`;
|
|
136
|
+
params.push(plan.offset);
|
|
137
|
+
}
|
|
138
|
+
return sql;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private condition(condition: Condition, params: SqlParam[], encode: ConditionValueEncoder): string {
|
|
142
|
+
switch (condition.kind) {
|
|
143
|
+
case "compare": {
|
|
144
|
+
const property = this.requireProperty(condition.property);
|
|
145
|
+
const placeholder = this.dialect.parameter(params.length);
|
|
146
|
+
params.push(encode(property, condition.value));
|
|
147
|
+
// startsWith/endsWith/contains escape wildcards with `\`; this works
|
|
148
|
+
// only with an explicit `ESCAPE '\'` (otherwise `%`/`_` stay wildcards).
|
|
149
|
+
const escape = condition.escaped ? " ESCAPE '\\'" : "";
|
|
150
|
+
return `${this.dialect.quoteId(property.columnName)} ${condition.op} ${placeholder}${escape}`;
|
|
151
|
+
}
|
|
152
|
+
case "in": {
|
|
153
|
+
const property = this.requireProperty(condition.property);
|
|
154
|
+
if (condition.values.length === 0) {
|
|
155
|
+
return "0 = 1"; // an empty IN is always false
|
|
156
|
+
}
|
|
157
|
+
const placeholders = condition.values.map((value) => {
|
|
158
|
+
const placeholder = this.dialect.parameter(params.length);
|
|
159
|
+
params.push(encode(property, value));
|
|
160
|
+
return placeholder;
|
|
161
|
+
});
|
|
162
|
+
return `${this.dialect.quoteId(property.columnName)} IN (${placeholders.join(", ")})`;
|
|
163
|
+
}
|
|
164
|
+
case "tuples": {
|
|
165
|
+
if (condition.properties.length === 0 || condition.values.length === 0) return "0 = 1";
|
|
166
|
+
return `(${condition.values.map((tuple) => {
|
|
167
|
+
if (tuple.length !== condition.properties.length) throw new OrmError("Tuple predicate component count mismatch.");
|
|
168
|
+
return `(${tuple.map((value, index) => { const property = this.requireProperty(condition.properties[index]!); const placeholder = this.dialect.parameter(params.length); params.push(encode(property, value)); return `${this.dialect.quoteId(property.columnName)} = ${placeholder}`; }).join(" AND ")})`;
|
|
169
|
+
}).join(" OR ")})`;
|
|
170
|
+
}
|
|
171
|
+
case "null": {
|
|
172
|
+
const property = this.requireProperty(condition.property);
|
|
173
|
+
return `${this.dialect.quoteId(property.columnName)} IS ${condition.negated ? "NOT " : ""}NULL`;
|
|
174
|
+
}
|
|
175
|
+
case "and":
|
|
176
|
+
case "or": {
|
|
177
|
+
const left = this.condition(condition.left, params, encode);
|
|
178
|
+
const right = this.condition(condition.right, params, encode);
|
|
179
|
+
return `(${left} ${condition.kind === "and" ? "AND" : "OR"} ${right})`;
|
|
180
|
+
}
|
|
181
|
+
case "not":
|
|
182
|
+
return `NOT (${this.condition(condition.inner, params, encode)})`;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private requireProperty(propertyName: string): PropertyModel {
|
|
187
|
+
const property = this.model.propertyByName(propertyName);
|
|
188
|
+
if (!property) {
|
|
189
|
+
throw new OrmError(
|
|
190
|
+
`Property "${propertyName}" is not mapped on entity "${this.model.name}". Did you forget @Column()?`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
return property;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST of WHERE conditions and a Proxy DSL to build it.
|
|
3
|
+
*
|
|
4
|
+
* Instead of parsing JS lambdas (unsafe and slow), the predicate is built
|
|
5
|
+
* through a Proxy: `x => x.age.gt(18).and(x.name.startsWith("A"))`. `x.age`
|
|
6
|
+
* returns a column operand, comparison methods produce AST nodes, and
|
|
7
|
+
* `.and()/.or()/.not()` combine them. No user strings in the SQL, only parameters.
|
|
8
|
+
*
|
|
9
|
+
* Note: `&&`/`||` are not intercepted; use `.and()/.or()`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export type CompareOp = "=" | "<>" | ">" | ">=" | "<" | "<=" | "LIKE";
|
|
13
|
+
|
|
14
|
+
export type Condition =
|
|
15
|
+
| {
|
|
16
|
+
readonly kind: "compare";
|
|
17
|
+
readonly property: string;
|
|
18
|
+
readonly op: CompareOp;
|
|
19
|
+
readonly value: unknown;
|
|
20
|
+
/** For LIKE from startsWith/endsWith/contains: add `ESCAPE '\'`. */
|
|
21
|
+
readonly escaped?: boolean;
|
|
22
|
+
}
|
|
23
|
+
| { readonly kind: "in"; readonly property: string; readonly values: readonly unknown[] }
|
|
24
|
+
| { readonly kind: "tuples"; readonly properties: readonly string[]; readonly values: readonly (readonly unknown[])[] }
|
|
25
|
+
| { readonly kind: "null"; readonly property: string; readonly negated: boolean }
|
|
26
|
+
| { readonly kind: "and" | "or"; readonly left: Condition; readonly right: Condition }
|
|
27
|
+
| { readonly kind: "not"; readonly inner: Condition };
|
|
28
|
+
|
|
29
|
+
/** Predicate result: a wrapper over an AST node with logical combinators. */
|
|
30
|
+
export class Predicate {
|
|
31
|
+
constructor(readonly node: Condition) {}
|
|
32
|
+
|
|
33
|
+
and(other: Predicate): Predicate {
|
|
34
|
+
return new Predicate({ kind: "and", left: this.node, right: other.node });
|
|
35
|
+
}
|
|
36
|
+
or(other: Predicate): Predicate {
|
|
37
|
+
return new Predicate({ kind: "or", left: this.node, right: other.node });
|
|
38
|
+
}
|
|
39
|
+
not(): Predicate {
|
|
40
|
+
return new Predicate({ kind: "not", inner: this.node });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Column operand: the entry point for building conditions on a property. */
|
|
45
|
+
export class Operand<T = unknown> {
|
|
46
|
+
constructor(readonly property: string) {}
|
|
47
|
+
|
|
48
|
+
private compare(op: CompareOp, value: unknown): Predicate {
|
|
49
|
+
return new Predicate({ kind: "compare", property: this.property, op, value });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** LIKE with escaped wildcards (needs `ESCAPE '\'` in SQL). */
|
|
53
|
+
private likeEscaped(pattern: string): Predicate {
|
|
54
|
+
return new Predicate({ kind: "compare", property: this.property, op: "LIKE", value: pattern, escaped: true });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
eq(value: T): Predicate {
|
|
58
|
+
return value === null ? this.isNull() : this.compare("=", value);
|
|
59
|
+
}
|
|
60
|
+
ne(value: T): Predicate {
|
|
61
|
+
return value === null ? this.isNotNull() : this.compare("<>", value);
|
|
62
|
+
}
|
|
63
|
+
gt(value: OrderedValue<T>): Predicate {
|
|
64
|
+
return this.compare(">", value);
|
|
65
|
+
}
|
|
66
|
+
gte(value: OrderedValue<T>): Predicate {
|
|
67
|
+
return this.compare(">=", value);
|
|
68
|
+
}
|
|
69
|
+
lt(value: OrderedValue<T>): Predicate {
|
|
70
|
+
return this.compare("<", value);
|
|
71
|
+
}
|
|
72
|
+
lte(value: OrderedValue<T>): Predicate {
|
|
73
|
+
return this.compare("<=", value);
|
|
74
|
+
}
|
|
75
|
+
like(pattern: TextValue<T>): Predicate {
|
|
76
|
+
return this.compare("LIKE", pattern);
|
|
77
|
+
}
|
|
78
|
+
startsWith(value: TextValue<T>): Predicate {
|
|
79
|
+
return this.likeEscaped(`${escapeLike(value)}%`);
|
|
80
|
+
}
|
|
81
|
+
endsWith(value: TextValue<T>): Predicate {
|
|
82
|
+
return this.likeEscaped(`%${escapeLike(value)}`);
|
|
83
|
+
}
|
|
84
|
+
contains(value: TextValue<T>): Predicate {
|
|
85
|
+
return this.likeEscaped(`%${escapeLike(value)}%`);
|
|
86
|
+
}
|
|
87
|
+
in(values: readonly T[]): Predicate {
|
|
88
|
+
return new Predicate({ kind: "in", property: this.property, values: [...values] });
|
|
89
|
+
}
|
|
90
|
+
isNull(): Predicate {
|
|
91
|
+
return new Predicate({ kind: "null", property: this.property, negated: false });
|
|
92
|
+
}
|
|
93
|
+
isNotNull(): Predicate {
|
|
94
|
+
return new Predicate({ kind: "null", property: this.property, negated: true });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** `%` and `_` in startsWith/endsWith/contains must not act as wildcards. */
|
|
99
|
+
function escapeLike(value: string): string {
|
|
100
|
+
return value.replace(/[%_\\]/g, (char) => `\\${char}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
type OrderedValue<T> = unknown extends T ? unknown
|
|
104
|
+
: NonNullable<T> extends string | number | bigint | Date ? NonNullable<T> : never;
|
|
105
|
+
type TextValue<T> = unknown extends T ? string : NonNullable<T> extends string ? string : never;
|
|
106
|
+
|
|
107
|
+
/** Field selector (for where/orderBy). Returns a typed property operand. */
|
|
108
|
+
export type FieldSelector<T> = {
|
|
109
|
+
readonly [K in keyof T]-?: Operand<T[K]>;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/** Predicate function passed to where. */
|
|
113
|
+
export type PredicateFn<T> = (entity: FieldSelector<T>) => Predicate;
|
|
114
|
+
|
|
115
|
+
/** Sorting needs only the column name, whatever the type of its value. */
|
|
116
|
+
export type KeySelectorFn<T> = (entity: FieldSelector<T>) => Pick<Operand, "property">;
|
|
117
|
+
|
|
118
|
+
const FIELD_PROXY: ProxyHandler<object> = {
|
|
119
|
+
get(_target, property): Operand {
|
|
120
|
+
return new Operand(String(property));
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/** Creates a Proxy selector of the entity fields. */
|
|
125
|
+
export function fieldSelector<T>(): FieldSelector<T> {
|
|
126
|
+
return new Proxy({}, FIELD_PROXY) as FieldSelector<T>;
|
|
127
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { EntityModel } from "../Metadata/types";
|
|
2
|
+
import type { Row, SqlDialect } from "../Providers/types";
|
|
3
|
+
import { decodeProperty } from "../Providers/propertyConversion";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hydrates a result row into an entity instance. A real class instance is
|
|
7
|
+
* created (the constructor is called without arguments), then columns are
|
|
8
|
+
* mapped to properties with reverse type conversion (through the dialect).
|
|
9
|
+
*/
|
|
10
|
+
export function materialize<T extends object>(model: EntityModel, row: Row, dialect: SqlDialect): T {
|
|
11
|
+
const entity = new model.ctor() as Record<string, unknown>;
|
|
12
|
+
for (const property of model.properties) {
|
|
13
|
+
if (property.columnName in row) {
|
|
14
|
+
entity[property.propertyName] = decodeProperty(property, row[property.columnName], dialect);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return entity as T;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Projects a row into a plain object by the alias -> property list. */
|
|
21
|
+
export function materializeProjection(
|
|
22
|
+
model: EntityModel,
|
|
23
|
+
row: Row,
|
|
24
|
+
dialect: SqlDialect,
|
|
25
|
+
projections: readonly { readonly alias: string; readonly property: string }[],
|
|
26
|
+
): Record<string, unknown> {
|
|
27
|
+
const result: Record<string, unknown> = {};
|
|
28
|
+
for (const { alias, property: propertyName } of projections) {
|
|
29
|
+
const property = model.propertyByName(propertyName);
|
|
30
|
+
if (!property) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const column = alias in row ? alias : property.columnName;
|
|
34
|
+
if (column in row) {
|
|
35
|
+
result[alias] = decodeProperty(property, row[column], dialect);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# ORM (`bazis` / `bazis/library/orm`)
|
|
2
|
+
|
|
3
|
+
The ORM uses TC39 decorators, `DbContext`/`DbSet`, a scoped Unit of Work and
|
|
4
|
+
parameterized SQL without runtime npm dependencies.
|
|
5
|
+
|
|
6
|
+
## Queries and saving
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
// db.users is the DbSet of a model with the fields tenantId:string and age:number.
|
|
10
|
+
const adults = await db.users
|
|
11
|
+
.where(user => user.tenantId.eq("team-a").and(user.age.gte(18)))
|
|
12
|
+
.orderBy(user => user.id)
|
|
13
|
+
.take(20)
|
|
14
|
+
.asNoTracking()
|
|
15
|
+
.toList();
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The field type is kept in Operand<T>: age.eq("18") and age.contains("18") are
|
|
19
|
+
TypeScript errors. eq/ne/in take the field type; strings support LIKE/contains,
|
|
20
|
+
ordered values gt/gte/lt/lte. For null there are isNull/isNotNull, and eq(null) is
|
|
21
|
+
allowed on nullable fields. Dynamic models with unknown keep the dynamic API.
|
|
22
|
+
|
|
23
|
+
Conditions are combined with .and/.or/.not. JavaScript `&&`, `||` and `!` create no
|
|
24
|
+
SQL conditions. `di:generate` reports such operations over the Predicate type in the
|
|
25
|
+
sources of the chosen target and stops generation before files are replaced. This is
|
|
26
|
+
a build check: running JS/any or sources without codegen does not get this guarantee.
|
|
27
|
+
After changing a query, run `di:generate` and the TypeScript check.
|
|
28
|
+
|
|
29
|
+
The write boundary is DbContext.saveChanges(), which saves all changes of the context.
|
|
30
|
+
IRepository.saveChanges() stays an alias of the same operation. For new services,
|
|
31
|
+
inject the domain DbContext so the save scope is visible in the code.
|
|
32
|
+
[More about repositories](Repository/README.md).
|
|
33
|
+
|
|
34
|
+
## PostgreSQL exact ensureCreated
|
|
35
|
+
|
|
36
|
+
A production PostgreSQL schema is described once with the explicit
|
|
37
|
+
`ormBazis.entities` list and entity decorators. The shared connection belongs to
|
|
38
|
+
`@Infra`; a feature does not create its own PostgreSQL provider.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { Column, DbContext, Entity, ForeignKey, Infra, Key, Module, Schema, ormBazisConnect } from "bazis";
|
|
42
|
+
|
|
43
|
+
@Schema("billing")
|
|
44
|
+
@Entity({ table: "orders" })
|
|
45
|
+
class Order {
|
|
46
|
+
@Key(["tenantId", "orderId"], { name: "pk_orders" })
|
|
47
|
+
@Column({ type: "text" }) tenantId = "";
|
|
48
|
+
@Column({ type: "text" }) orderId = "";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Schema("billing")
|
|
52
|
+
@Entity({ table: "lines" })
|
|
53
|
+
@ForeignKey(() => Order, { name: "fk_lines_order", properties: ["tenantId", "orderId"] })
|
|
54
|
+
class OrderLine {
|
|
55
|
+
@Key(["tenantId", "lineId"]) @Column({ type: "text" }) tenantId = "";
|
|
56
|
+
@Column({ type: "text" }) lineId = "";
|
|
57
|
+
@Column({ type: "text" }) orderId = "";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class BillingContext extends DbContext {}
|
|
61
|
+
@Infra({ db: ormBazisConnect(dbConfig) }) class AppInfra {}
|
|
62
|
+
@Module({ ormBazis: { context: BillingContext, entities: [Order, OrderLine], ensureCreated: true } })
|
|
63
|
+
class BillingModule {}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`@Key()` declares a single primary-key component. `@Key(["a", "b"])`
|
|
67
|
+
declares the sole ordered composite key tuple; `@UUID()` declares a v4 UUID
|
|
68
|
+
key generated by PostgreSQL, and `@UUID({ version: "v7" })` a time-ordered v7
|
|
69
|
+
key that the ORM assigns before INSERT (native `uuid` column, no default). `@ForeignKey`, `@Index`, `@Check` and `@Schema` contribute to the same
|
|
70
|
+
expected physical schema.
|
|
71
|
+
|
|
72
|
+
For PostgreSQL, `ensureCreated()` acquires sorted advisory locks per physical
|
|
73
|
+
schema and runs all catalog reads and DDL on one reserved-session transaction:
|
|
74
|
+
|
|
75
|
+
1. classifies the complete declared PostgreSQL diff before any DDL;
|
|
76
|
+
2. creates missing schemas and whole missing tables, or applies only safe additions;
|
|
77
|
+
3. safe existing-table additions are nullable columns, non-null columns with a closed literal `@Column({ default })`, immediate CHECK/FK and ordinary/unique B-tree indexes;
|
|
78
|
+
4. re-introspect and exact-verify before commit.
|
|
79
|
+
|
|
80
|
+
Column order is ignored, but type/nullability/default/generation and named,
|
|
81
|
+
ordered PK/FK/index/CHECK contracts are exact. A type/null/default/generation/
|
|
82
|
+
PK change, rename/drop, generated/UUID existing-table column, `NOT NULL`
|
|
83
|
+
column without a usable literal default, or changed/unknown object stops startup
|
|
84
|
+
with `ORM_SCHEMA_MIGRATION_REQUIRED` and executes zero DDL. No backfill,
|
|
85
|
+
rebuild, `IF NOT EXISTS` or concurrent index build is performed.
|
|
86
|
+
|
|
87
|
+
`SchemaMigrationRequiredError` and `SchemaVerificationError` include the table,
|
|
88
|
+
object, difference code, expected ORM state and actual database state in `message`.
|
|
89
|
+
The existing `code` and structured `verification.differences` remain available.
|
|
90
|
+
For example, an extra column produces:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
PostgreSQL schema change requires an explicit migration.
|
|
94
|
+
- Table "public"."dm_table", object "bazis_drill_unexpected": column exists in the database but is absent from the ORM model (column.unexpected); expected (ORM): absent; actual (database): present.
|
|
95
|
+
Check the application version and target database. Align the ORM model and database schema; apply an explicit migration if a database change is intended.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
These diagnostics use the verifier's safe descriptors. Literal defaults and CHECK
|
|
99
|
+
expressions remain represented by SHA-256 fingerprints; SQL, driver errors and row
|
|
100
|
+
values are not added to the message. The message alone does not establish that the
|
|
101
|
+
target database is outdated or should be modified: first check the application and
|
|
102
|
+
database pairing. `Bazis.run()` prints the redacted nested error details without
|
|
103
|
+
console depth collapsing them to `[Object ...]`.
|
|
104
|
+
|
|
105
|
+
`ColumnOptions.default` is a closed physical literal (`null | boolean | number |
|
|
106
|
+
string`), not SQL and not an entity initializer. It is supported only by the
|
|
107
|
+
PostgreSQL exact path.
|
|
108
|
+
|
|
109
|
+
Direct `context.database.ensureCreated()` uses the same PostgreSQL admission
|
|
110
|
+
engine.
|
|
111
|
+
|
|
112
|
+
## Other ORM capabilities
|
|
113
|
+
|
|
114
|
+
- `where`, ordering, pagination, include and typed scalar/composite lookup;
|
|
115
|
+
- snapshot tracking and transactional `saveChanges()`;
|
|
116
|
+
- declarative FK/index/CHECK metadata;
|
|
117
|
+
- explicit additive `migrate()` and legacy versioned `Migration[]` paths.
|
|
118
|
+
|
|
119
|
+
The additive migration paths are separate from PostgreSQL exact admission.
|
|
120
|
+
|
|
121
|
+
## Saving and retrying after an error
|
|
122
|
+
|
|
123
|
+
`saveChanges()` inside `database.transaction()` joins the current transaction. An
|
|
124
|
+
error of such a save forbids its COMMIT, even if the calling code caught the
|
|
125
|
+
exception. To recover inside an outer transaction, use `transactionScope()` with a
|
|
126
|
+
savepoint. Automatic save retries inside an active transaction are disabled.
|
|
127
|
+
|
|
128
|
+
A unique constraint violation (SQLSTATE 23505) in `saveChanges()` arrives as
|
|
129
|
+
`UniqueViolationError` (a `DbUpdateError` subclass) with the `constraint` and
|
|
130
|
+
`table` fields and the original driver error in `cause`; the changes of this save are
|
|
131
|
+
rolled back. Catch it to answer 409 without a check up front: the index decides, so
|
|
132
|
+
there is no race. Other driver errors are not wrapped.
|
|
133
|
+
|
|
134
|
+
If there is no reliable answer after COMMIT is sent, the ORM throws `DbUpdateError`
|
|
135
|
+
with `code: "ORM_TRANSACTION_OUTCOME_UNKNOWN"`. `ExecutionStrategy` and `withRetry`
|
|
136
|
+
do not retry such an operation, including a user `isTransient`.
|
|
137
|
+
All contexts that saved changes in this transaction block further `saveChanges()`.
|
|
138
|
+
First reconcile the result against the database data, then continue in a new
|
|
139
|
+
`DbContext`. The received keys are kept for the reconciliation; having a key or the
|
|
140
|
+
`Unchanged` state does not prove COMMIT. User `afterCommit` and `afterRollback` are
|
|
141
|
+
not called on an unknown outcome.
|
|
142
|
+
|
|
143
|
+
After a confirmed rollback the keys and tracking are restored. A confirmed COMMIT
|
|
144
|
+
with a handler error keeps the existing `PostCommitError` contract.
|
|
145
|
+
|
|
146
|
+
The native `Bun.SQL Query.cancel()` on the pinned Bun 1.4.0 did not confirm
|
|
147
|
+
cancelling an already running query: six checks on PostgreSQL 17.11 left the query
|
|
148
|
+
active after 1.5 seconds. `cancelled === true` alone does not prove cancellation.
|
|
149
|
+
On leaving `transactionScope()` with an unfinished operation the ORM forbids COMMIT.
|
|
150
|
+
If an unfinished PostgreSQL query remains, the ORM forbids further work on this
|
|
151
|
+
reservation and calls `pg_cancel_backend` through a separate service Bun.SQL
|
|
152
|
+
connection. It waits for the cancel command's response, the completion of the
|
|
153
|
+
original native queries, a confirmed ROLLBACK and a readback of the same backend
|
|
154
|
+
identity. After that the connection can be reused.
|
|
155
|
+
The broken `Query.cancel()` is not used on this path. For unfinished user JS without
|
|
156
|
+
SQL a 5-second wait limit is kept; an explicit cancellation signal also interrupts that wait.
|
|
157
|
+
|
|
158
|
+
A long query inside the callback can be cancelled from outside:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
await db.transactionScope(async (tx) => {
|
|
162
|
+
await tx.databaseTime();
|
|
163
|
+
await db.database.querySqlRaw("SELECT pg_sleep({0})", 30);
|
|
164
|
+
}, { signal: AbortSignal.timeout(1_000) });
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
An already aborted signal rejects the call before BEGIN. When an active scope is
|
|
168
|
+
cancelled, new and deferred operations are blocked. Server cancellation rolls back the
|
|
169
|
+
whole physical transaction, including the outer one when a nested scope is cancelled;
|
|
170
|
+
a caught error does not allow its COMMIT. After the callback and its operations
|
|
171
|
+
finish, control passes to the provider: its operation timeout applies until the COMMIT
|
|
172
|
+
response. The signal is passed to `reserve({ signal })`; a connection handed out late
|
|
173
|
+
is closed without BEGIN. `options.timeoutMs` bounds waiting for a connection, BEGIN,
|
|
174
|
+
the callback, SQL and COMMIT; the default is the provider's `operationTimeoutMs`
|
|
175
|
+
(30 seconds). Confirming a cancellation has a separate `cancellationTimeoutMs`
|
|
176
|
+
budget (5 seconds), and releasing the resource one more such budget. This is not a
|
|
177
|
+
promise to finish the whole call within `timeoutMs`. User afterCommit/afterRollback
|
|
178
|
+
have their own execution time; a late cancellation does not change a confirmed
|
|
179
|
+
COMMIT. Arbitrary JS is not stopped, but its late calls into the ORM are forbidden.
|
|
180
|
+
|
|
181
|
+
If the SQL completion and the rollback on the same backend are not confirmed, the ORM
|
|
182
|
+
returns `ORM_TRANSACTION_OUTCOME_UNKNOWN` with `phase: "cancellation"`. The user
|
|
183
|
+
rollback callback is not called. All contexts included in the scope block new SQL
|
|
184
|
+
and saves; reconcile the result through a new context. A lost COMMIT response keeps
|
|
185
|
+
`phase: "commit"`. There is no automatic retry.
|
|
186
|
+
|
|
187
|
+
With an explicit `cancellationMode: "close"` the former policy of closing and
|
|
188
|
+
checking that the backend is gone is kept. In this mode on Bun 1.4.0 **fast
|
|
189
|
+
cancellation over TLS is not confirmed**: the connection-close Promise settles, but
|
|
190
|
+
SQL on the server may stay active. In a check of a 10-second query the backend stayed
|
|
191
|
+
active after five seconds; the ORM returned an unknown outcome within its budget. With
|
|
192
|
+
an explicitly configured server `statementTimeoutMs: 600` a separate check ended with
|
|
193
|
+
a confirmed rollback in 641 ms. The server limit counts from the start of the SQL, so
|
|
194
|
+
it does not guarantee such a delay from any later abort.
|
|
195
|
+
|
|
196
|
+
`postgres({ operationTimeoutMs, cancellationTimeoutMs, cancellationMode, maxPendingOperations,
|
|
197
|
+
serverTimeouts, onOperation })` configures the provider policy.
|
|
198
|
+
`cancellationMode` takes `server` (the default) or `close`. The server mode uses a
|
|
199
|
+
lazy service pool max=1 in addition to the working pool, with the same credentials
|
|
200
|
+
and TLS. Both pools close together with the provider. Borrowed advisory locks are
|
|
201
|
+
released explicitly after a confirmed rollback, before the connection is returned.
|
|
202
|
+
After a forced close the pool checks later reservations with SELECT 1 before user
|
|
203
|
+
work. A native connection timeout on admission allows one retry within the shared
|
|
204
|
+
deadline; business SQL, the callback and COMMIT are not retried. The check adds one
|
|
205
|
+
round trip after such a failure until the provider is recreated; a regular confirmed
|
|
206
|
+
server cancellation does not enable it.
|
|
207
|
+
|
|
208
|
+
The SQL check of PID/backend_start/query_start is not atomic against the system
|
|
209
|
+
reusing the PID; this API does not give the PID+secret guarantee of the protocol.
|
|
210
|
+
`serverTimeouts` supports `statementTimeoutMs`, `lockTimeoutMs`,
|
|
211
|
+
`idleInTransactionTimeoutMs`, `transactionTimeoutMs` (the last one is PostgreSQL 17+).
|
|
212
|
+
The settings are applied with SET LOCAL and checked at BEGIN; by default the server
|
|
213
|
+
limits do not change. Root query/execute and ping have a client deadline, but the
|
|
214
|
+
SET LOCAL profile applies only inside transactions. Unfinished native operations hold
|
|
215
|
+
a slot until they actually finish; the default limit is 256, with a bounded reserve
|
|
216
|
+
for cleanup. `statistics()` and `onOperation` show the number of such operations,
|
|
217
|
+
durations and unknown outcomes without SQL or parameters.
|
|
218
|
+
|
|
219
|
+
The ORM connector passes the pool, connection and TLS settings and the listed limits.
|
|
220
|
+
Units, validation and a connection example are in the
|
|
221
|
+
[core/orm passport](../../core/orm/MODULE.md).
|
|
222
|
+
|
|
223
|
+
Results of the current server mode, real AbortSignal TCP/TLS scenarios and binary
|
|
224
|
+
qualification are in the [integration report](https://github.com/asus97178/osnova/blob/33a4513a56abb43a1694e7a6e56373187b928a70/docs/audits/orm-server-cancel-integration-2026-09-14.md).
|
|
225
|
+
Earlier physical checks of the close mode confirmed cancellation in 395–402 ms,
|
|
226
|
+
rollback, key restoration, advisory lock release, an unaffected neighboring active
|
|
227
|
+
query in the same pool and further saves. This is a measurement of a local run, not a
|
|
228
|
+
deadline for any environment. [Fix and evidence](https://github.com/asus97178/osnova/blob/33a4513a56abb43a1694e7a6e56373187b928a70/docs/audits/orm-2026-09-14-cancellation-fix.md),
|
|
229
|
+
[cancellation contract](MODULE.md). A direct `Bun.SQL Query.cancel()` call stays a
|
|
230
|
+
separate failed gate from the [earlier qualification](https://github.com/asus97178/osnova/blob/33a4513a56abb43a1694e7a6e56373187b928a70/docs/audits/orm-2026-09-14-qualification.md).
|
|
231
|
+
New time limits, network failures, TLS and binary execution are described in the
|
|
232
|
+
[hardening report](https://github.com/asus97178/osnova/blob/33a4513a56abb43a1694e7a6e56373187b928a70/docs/audits/orm-bun-sql-hardening-2026-09-14.md).
|
|
233
|
+
|
|
234
|
+
`findForUpdate()` refreshes only a clean tracked entity. Unsaved regular assignments,
|
|
235
|
+
JSON/Date changes and an explicit `update()` cause a rejection before the values are
|
|
236
|
+
overwritten. Models with only a generated key are saved with separate
|
|
237
|
+
`INSERT DEFAULT VALUES` inside the shared transaction.
|
|
238
|
+
|
|
239
|
+
## Physical types and SQL names
|
|
240
|
+
|
|
241
|
+
A PostgreSQL FK to a native UUID key gets the physical `uuid` type; the logical
|
|
242
|
+
`PropertyModel.type` and converters are kept. This applies to explicit FKs,
|
|
243
|
+
navigations and dynamic models, including composite relations and PK/FK chains.
|
|
244
|
+
Incompatible types are rejected before DDL. Existing columns are not converted
|
|
245
|
+
automatically: a mismatch needs a separate migration. Exact admission rejects
|
|
246
|
+
`GENERATED ALWAYS` if the model declares `BY DEFAULT`.
|
|
247
|
+
|
|
248
|
+
`PostgresDialect.quoteId()` quotes **one** name:
|
|
249
|
+
`quoteId("external.name")` returns `"external.name"`. For a table with a schema use
|
|
250
|
+
`qualifyTable(model)` or join a separately quoted schema and name. The old
|
|
251
|
+
`tableName` format of dynamic models is kept; the ORM uses the original schema and
|
|
252
|
+
table components without splitting on dots. A manually passed
|
|
253
|
+
`ForeignKeyConstraint.referencedTable` keeps the existing `schema.table` format.
|
|
254
|
+
|
|
255
|
+
`include()` uses the `KeyTuple` semantics shared with the identity map for references
|
|
256
|
+
and collections, including `bigint`, `Date` and converter-backed binary keys.
|