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,226 @@
|
|
|
1
|
+
import { types } from "node:util";
|
|
2
|
+
import { OrmError, OrmTrackedMutationConflictError, OrmUndeclaredConflictTargetError, OrmUnsafeImmediateMutationError } from "../errors";
|
|
3
|
+
import type { EntityModel, PropertyModel } from "../Metadata/types";
|
|
4
|
+
import type { DbContextRuntime } from "../runtime";
|
|
5
|
+
import { hasTrackedEntriesForModel } from "../Tracking/ChangeTracker";
|
|
6
|
+
import { isPostgresJsonScalarParameter } from "../Providers/PostgresDialect";
|
|
7
|
+
import type { SqlParam } from "../Providers/types";
|
|
8
|
+
import type { QueryPlan } from "./QueryPlan";
|
|
9
|
+
import { Operand, type Condition, type FieldSelector } from "./conditions";
|
|
10
|
+
import { SqlTranslator } from "./SqlTranslator";
|
|
11
|
+
|
|
12
|
+
export interface OrmMutationResultV1 { readonly affectedRows: number; }
|
|
13
|
+
export interface OrmInsertIfAbsentResultV1 { readonly inserted: boolean; }
|
|
14
|
+
export type OrmUpdateValuesV1<T extends object> = Readonly<Partial<T>>;
|
|
15
|
+
export type OrmUniqueKeySelectorV1<T extends object> = (entity: FieldSelector<T>) => readonly Pick<Operand, "property">[];
|
|
16
|
+
|
|
17
|
+
export function executeImmediateUpdate<T extends object>(model: EntityModel, runtime: DbContextRuntime, plan: QueryPlan, values: OrmUpdateValuesV1<T>): Promise<OrmMutationResultV1> {
|
|
18
|
+
return runtime.runImmediateOperation(async () => {
|
|
19
|
+
guard(model, runtime); const admittedPlan = admitPlan(plan);
|
|
20
|
+
const conditions = effectiveConditions(model, admittedPlan); const assignments = encodeAssignments(runtime, admitUpdateValues(model, values));
|
|
21
|
+
const compiled = new SqlTranslator(model, runtime.provider.dialect).immediateUpdate(assignments, conditions, (property, value) => encode(runtime, property, snapshot(value), true));
|
|
22
|
+
Object.freeze(compiled.params);
|
|
23
|
+
guard(model, runtime);
|
|
24
|
+
const result = await runtime.provider.execute(compiled.sql, compiled.params);
|
|
25
|
+
return Object.freeze({ affectedRows: count(result.changes) });
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function executeImmediateDelete(model: EntityModel, runtime: DbContextRuntime, plan: QueryPlan): Promise<OrmMutationResultV1> {
|
|
30
|
+
return runtime.runImmediateOperation(async () => {
|
|
31
|
+
guard(model, runtime); const admittedPlan = admitPlan(plan); const conditions = effectiveConditions(model, admittedPlan);
|
|
32
|
+
const compiled = new SqlTranslator(model, runtime.provider.dialect).immediateDelete(conditions, (property, value) => encode(runtime, property, snapshot(value), true));
|
|
33
|
+
Object.freeze(compiled.params);
|
|
34
|
+
guard(model, runtime);
|
|
35
|
+
const result = await runtime.provider.execute(compiled.sql, compiled.params);
|
|
36
|
+
return Object.freeze({ affectedRows: count(result.changes) });
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function insertIfAbsent<T extends object>(model: EntityModel, runtime: DbContextRuntime, entity: T, options: { readonly conflictBy: OrmUniqueKeySelectorV1<T> }): Promise<OrmInsertIfAbsentResultV1> {
|
|
41
|
+
return runtime.runImmediateOperation(async () => {
|
|
42
|
+
guard(model, runtime); if (runtime.provider.dialect.name !== "postgres") unsafe();
|
|
43
|
+
const admitted = admitInsertValues(model, entity); const columns = conflictTarget(model, options); const values = encodeAssignments(runtime, admitted);
|
|
44
|
+
const compiled = new SqlTranslator(model, runtime.provider.dialect).immediateInsert(values, columns);
|
|
45
|
+
Object.freeze(compiled.params);
|
|
46
|
+
guard(model, runtime);
|
|
47
|
+
const result = await runtime.provider.execute(compiled.sql, compiled.params);
|
|
48
|
+
const changes = count(result.changes);
|
|
49
|
+
if (changes !== 0 && changes !== 1) throw new OrmError("ORM provider returned an invalid immediate mutation count.");
|
|
50
|
+
return Object.freeze({ inserted: changes === 1 });
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function guard(model: EntityModel, runtime: DbContextRuntime): void {
|
|
55
|
+
if (hasTrackedEntriesForModel(runtime.tracker, model)) throw new OrmTrackedMutationConflictError();
|
|
56
|
+
}
|
|
57
|
+
interface AdmittedImmediatePlan { readonly explicitConditions: readonly Condition[]; readonly ignoreQueryFilters: boolean; }
|
|
58
|
+
function admitPlan(plan: QueryPlan): AdmittedImmediatePlan {
|
|
59
|
+
try {
|
|
60
|
+
if (types.isProxy(plan) || typeof plan !== "object" || plan === null || Object.getPrototypeOf(plan) !== Object.prototype) unsafe();
|
|
61
|
+
const fields = Object.getOwnPropertyDescriptors(plan); const names = Object.getOwnPropertyNames(plan); if (Object.getOwnPropertySymbols(plan).length !== 0) unsafe();
|
|
62
|
+
const required = ["conditions", "orders", "noTracking", "includes", "ignoreQueryFilters", "projections"];
|
|
63
|
+
const optional = ["limit", "requestedLimit", "invalidRequestedLimit", "offset", "rowLock", "skipLocked"];
|
|
64
|
+
if (names.length !== required.length || required.some((key) => !Object.prototype.hasOwnProperty.call(fields, key)) || optional.some((key) => Object.prototype.hasOwnProperty.call(fields, key))) unsafe();
|
|
65
|
+
for (const key of required) if (!("value" in fields[key]!)) unsafe();
|
|
66
|
+
if (fields.noTracking!.value !== true || typeof fields.ignoreQueryFilters!.value !== "boolean") unsafe();
|
|
67
|
+
const empty = (value: unknown) => { if (!Array.isArray(value) || types.isProxy(value) || Object.getPrototypeOf(value) !== Array.prototype) unsafe(); const descriptors: Record<string, PropertyDescriptor> = Object.getOwnPropertyDescriptors(value as object); if (Object.getOwnPropertySymbols(value).length || Object.getOwnPropertyNames(value).length !== 1 || !("value" in descriptors.length!) || descriptors.length!.value !== 0) unsafe(); };
|
|
68
|
+
empty(fields.orders!.value); empty(fields.includes!.value); empty(fields.projections!.value);
|
|
69
|
+
const explicitConditions = readConditionList(fields.conditions!.value); if (explicitConditions.length === 0) unsafe();
|
|
70
|
+
return Object.freeze({ explicitConditions: Object.freeze(explicitConditions), ignoreQueryFilters: fields.ignoreQueryFilters!.value });
|
|
71
|
+
} catch (error) { if (error instanceof OrmUnsafeImmediateMutationError) throw error; unsafe(); }
|
|
72
|
+
}
|
|
73
|
+
function count(value: unknown): number { if (!Number.isSafeInteger(value) || (value as number) < 0) throw new OrmError("ORM provider returned an invalid immediate mutation count."); return value as number; }
|
|
74
|
+
function unsafe(): never { throw new OrmUnsafeImmediateMutationError(); }
|
|
75
|
+
|
|
76
|
+
interface AdmittedAssignment { readonly property: PropertyModel; readonly value: unknown; }
|
|
77
|
+
function admitUpdateValues<T extends object>(model: EntityModel, values: OrmUpdateValuesV1<T>): readonly AdmittedAssignment[] {
|
|
78
|
+
if (!plain(values)) unsafe();
|
|
79
|
+
const descriptors = ownData(values); const keys = [...descriptors.keys()]; if (keys.length === 0) unsafe();
|
|
80
|
+
const out: AdmittedAssignment[] = [];
|
|
81
|
+
for (const key of keys) {
|
|
82
|
+
const descriptor = descriptors.get(key)!;
|
|
83
|
+
const property = model.propertyByName(key); if (!property || property.isKey || property.generation !== "none" || property.convention || descriptor.value === undefined || descriptor.value === null) unsafe();
|
|
84
|
+
out.push(Object.freeze({ property, value: snapshot(descriptor.value) }));
|
|
85
|
+
}
|
|
86
|
+
return Object.freeze(out.map((value) => Object.freeze(value)));
|
|
87
|
+
}
|
|
88
|
+
function admitInsertValues<T extends object>(model: EntityModel, entity: T): readonly AdmittedAssignment[] {
|
|
89
|
+
if (!objectInput(entity)) unsafe();
|
|
90
|
+
const descriptors = ownData(entity);
|
|
91
|
+
const out: AdmittedAssignment[] = [];
|
|
92
|
+
for (const property of model.properties) {
|
|
93
|
+
const descriptor = descriptors.get(property.propertyName); if (!descriptor || descriptor.value === undefined) unsafe();
|
|
94
|
+
if (descriptor.value === null) { if (property.required) unsafe(); out.push(Object.freeze({ property, value: null })); continue; }
|
|
95
|
+
out.push(Object.freeze({ property, value: snapshot(descriptor.value) }));
|
|
96
|
+
}
|
|
97
|
+
return Object.freeze(out.map((value) => Object.freeze(value)));
|
|
98
|
+
}
|
|
99
|
+
function encodeAssignments(runtime: DbContextRuntime, assignments: readonly AdmittedAssignment[]): readonly { readonly property: PropertyModel; readonly value: SqlParam }[] {
|
|
100
|
+
return Object.freeze(assignments.map(({ property, value }) => Object.freeze({ property, value: value === null ? null : encode(runtime, property, value) })));
|
|
101
|
+
}
|
|
102
|
+
function encode(runtime: DbContextRuntime, property: PropertyModel, input: unknown, allowNull = false): SqlParam {
|
|
103
|
+
try {
|
|
104
|
+
const providerValue = property.converter ? property.converter.toProvider(input) : input;
|
|
105
|
+
const safeProvider = storageValue(providerValue, property.type, allowNull);
|
|
106
|
+
return snapshotEncodedSqlParam(runtime.provider.dialect.encode(safeProvider, property.type));
|
|
107
|
+
} catch { unsafe(); }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function conflictTarget<T extends object>(model: EntityModel, options: { readonly conflictBy: OrmUniqueKeySelectorV1<T> }): readonly string[] {
|
|
111
|
+
if (!plain(options)) unsafe();
|
|
112
|
+
const optionDescriptors = ownData(options); if (optionDescriptors.size !== 1) unsafe();
|
|
113
|
+
const callback = optionDescriptors.get("conflictBy")?.value; if (typeof callback !== "function" || types.isProxy(callback)) unsafe();
|
|
114
|
+
const issued = new WeakMap<Operand, string>();
|
|
115
|
+
const selector = new Proxy({}, { get(_target, property) { const operand = new Operand(String(property)); issued.set(operand, String(property)); return operand; } }) as FieldSelector<T>;
|
|
116
|
+
let target: readonly Operand[];
|
|
117
|
+
try { target = callback(selector); } catch { unsafe(); }
|
|
118
|
+
if (!Array.isArray(target) || types.isProxy(target) || Object.getPrototypeOf(target) !== Array.prototype) unsafe();
|
|
119
|
+
const targetDescriptors = ownData(target); const length = Object.getOwnPropertyDescriptor(target, "length")?.value;
|
|
120
|
+
if (!Number.isSafeInteger(length) || length < 1 || targetDescriptors.size !== length + 1) unsafe();
|
|
121
|
+
const properties: string[] = [];
|
|
122
|
+
for (let index = 0; index < length; index += 1) { const operand = targetDescriptors.get(String(index))?.value; if (typeof operand !== "object" || operand === null || types.isProxy(operand)) unsafe(); const property = issued.get(operand as Operand); if (!property) unsafe(); properties.push(property); }
|
|
123
|
+
if (new Set(properties).size !== properties.length) throw new OrmUndeclaredConflictTargetError();
|
|
124
|
+
const columns = properties.map((name) => model.propertyByName(name)?.columnName);
|
|
125
|
+
if (columns.some((column) => column === undefined)) throw new OrmUndeclaredConflictTargetError();
|
|
126
|
+
const candidates = [model.key.map((property) => property.columnName), ...model.indexes.filter((index) => index.unique).map((index) => index.columns)];
|
|
127
|
+
if (!candidates.some((candidate) => candidate.length === columns.length && candidate.every((column, index) => column === columns[index]))) throw new OrmUndeclaredConflictTargetError();
|
|
128
|
+
return Object.freeze(columns as string[]);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function effectiveConditions(model: EntityModel, plan: AdmittedImmediatePlan): readonly Condition[] {
|
|
132
|
+
const source: Condition[] = plan.ignoreQueryFilters ? [] : readConditionList(model.queryFilters);
|
|
133
|
+
if (!plan.ignoreQueryFilters && model.softDeleteProperty) source.push({ kind: "null", property: model.softDeleteProperty, negated: false });
|
|
134
|
+
source.push(...plan.explicitConditions); const active = new Set<object>(); const memo = new WeakMap<object, Condition>();
|
|
135
|
+
return Object.freeze(source.map((condition) => cloneCondition(model, condition, active, memo)));
|
|
136
|
+
}
|
|
137
|
+
function readConditionList(value: unknown): Condition[] {
|
|
138
|
+
if (!Array.isArray(value) || types.isProxy(value) || Object.getPrototypeOf(value) !== Array.prototype) unsafe(); const fields = ownData(value); const length = fields.get("length")?.value; if (!Number.isSafeInteger(length) || length < 0 || fields.size !== length + 1) unsafe(); const result: Condition[] = []; for (let index = 0; index < length; index += 1) { const descriptor = fields.get(String(index)); if (!descriptor) unsafe(); result.push(descriptor.value as Condition); } return result;
|
|
139
|
+
}
|
|
140
|
+
function cloneCondition(model: EntityModel, input: unknown, active: Set<object>, memo: WeakMap<object, Condition>): Condition {
|
|
141
|
+
if (!plain(input) || active.has(input)) unsafe(); if (memo.has(input)) return memo.get(input)!; active.add(input);
|
|
142
|
+
const fields = ownData(input); const kind = fields.get("kind")?.value;
|
|
143
|
+
const exact = (...keys: string[]) => { if (fields.size !== keys.length || keys.some((key) => !fields.has(key))) unsafe(); };
|
|
144
|
+
const property = () => { const value = fields.get("property")?.value; if (typeof value !== "string" || !model.propertyByName(value)) unsafe(); return value; };
|
|
145
|
+
let result: Condition;
|
|
146
|
+
if (kind === "compare") { const escaped = fields.get("escaped")?.value; if (escaped !== undefined && escaped !== true) unsafe(); exact(...(escaped === undefined ? ["kind", "property", "op", "value"] : ["kind", "property", "op", "value", "escaped"])); const op = fields.get("op")!.value; if (typeof op !== "string" || !["=", "<>", ">", ">=", "<", "<=", "LIKE"].includes(op) || (escaped === true && op !== "LIKE")) unsafe(); result = Object.freeze({ kind: "compare", property: property(), op: op as never, value: snapshot(fields.get("value")!.value, new Set(), new WeakMap(), true), ...(escaped === true ? { escaped: true } : {}) }); }
|
|
147
|
+
else if (kind === "in") { exact("kind", "property", "values"); result = Object.freeze({ kind: "in", property: property(), values: denseValues(fields.get("values")!.value) }); }
|
|
148
|
+
else if (kind === "tuples") { exact("kind", "properties", "values"); const properties = denseValues(fields.get("properties")!.value); if (properties.some((value) => typeof value !== "string" || !model.propertyByName(value as string))) unsafe(); const rows = denseValues(fields.get("values")!.value).map((row) => { const values = denseValues(row); if (values.length !== properties.length) unsafe(); return Object.freeze(values); }); result = Object.freeze({ kind: "tuples", properties: Object.freeze(properties as string[]), values: Object.freeze(rows) }); }
|
|
149
|
+
else if (kind === "null") { exact("kind", "property", "negated"); const negated = fields.get("negated")!.value; if (typeof negated !== "boolean") unsafe(); result = Object.freeze({ kind: "null", property: property(), negated }); }
|
|
150
|
+
else if (kind === "and" || kind === "or") { exact("kind", "left", "right"); const provisional = {} as Condition; memo.set(input, provisional); result = Object.freeze({ kind, left: cloneCondition(model, fields.get("left")!.value, active, memo), right: cloneCondition(model, fields.get("right")!.value, active, memo) }); }
|
|
151
|
+
else if (kind === "not") { exact("kind", "inner"); result = Object.freeze({ kind: "not", inner: cloneCondition(model, fields.get("inner")!.value, active, memo) }); }
|
|
152
|
+
else unsafe();
|
|
153
|
+
active.delete(input); memo.set(input, result!); return result!;
|
|
154
|
+
}
|
|
155
|
+
function denseValues(value: unknown): readonly unknown[] {
|
|
156
|
+
if (!Array.isArray(value) || types.isProxy(value) || Object.getPrototypeOf(value) !== Array.prototype) unsafe(); const fields = ownData(value); const length = fields.get("length")?.value; if (!Number.isSafeInteger(length) || length < 0 || fields.size !== length + 1) unsafe(); const result: unknown[] = []; for (let index = 0; index < length; index += 1) result.push(snapshot(fields.get(String(index))?.value, new Set(), new WeakMap(), true)); return Object.freeze(result);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function ownData(value: object): Map<string, PropertyDescriptor & { readonly value: unknown }> {
|
|
160
|
+
try {
|
|
161
|
+
const result = new Map<string, PropertyDescriptor & { readonly value: unknown }>();
|
|
162
|
+
for (const key of Reflect.ownKeys(value)) { if (typeof key !== "string") unsafe(); const descriptor = Object.getOwnPropertyDescriptor(value, key); if (!descriptor || !("value" in descriptor)) unsafe(); result.set(key, descriptor as PropertyDescriptor & { readonly value: unknown }); }
|
|
163
|
+
return result;
|
|
164
|
+
} catch { unsafe(); }
|
|
165
|
+
}
|
|
166
|
+
function plain(value: unknown): value is Record<string, unknown> { return objectInput(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null); }
|
|
167
|
+
function objectInput(value: unknown): value is Record<string, unknown> { try { return typeof value === "object" && value !== null && !Array.isArray(value) && !types.isProxy(value); } catch { unsafe(); } }
|
|
168
|
+
const CapturedDate = Date;
|
|
169
|
+
const capturedDatePrototype = CapturedDate.prototype;
|
|
170
|
+
const intrinsicDateToJson = capturedDatePrototype.toJSON, intrinsicDateGetTime = capturedDatePrototype.getTime;
|
|
171
|
+
const CapturedUint8Array = Uint8Array;
|
|
172
|
+
const capturedUint8ArrayPrototype = CapturedUint8Array.prototype;
|
|
173
|
+
const capturedUint8ArraySet = capturedUint8ArrayPrototype.set;
|
|
174
|
+
const typedArrayLength = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(capturedUint8ArrayPrototype), "length")!.get!;
|
|
175
|
+
function snapshot(value: unknown, active = new Set<object>(), memo = new WeakMap<object, unknown>(), freeze = false): unknown {
|
|
176
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "bigint") return value;
|
|
177
|
+
if (typeof value === "number") { if (!Number.isFinite(value)) unsafe(); return value; }
|
|
178
|
+
if (typeof value !== "object" || types.isProxy(value)) unsafe();
|
|
179
|
+
if (active.has(value)) unsafe(); if (memo.has(value)) return memo.get(value)!;
|
|
180
|
+
const prototype = Object.getPrototypeOf(value);
|
|
181
|
+
if (prototype === capturedDatePrototype) { const descriptor = Object.getOwnPropertyDescriptor(capturedDatePrototype, "toJSON"); if (!descriptor || !("value" in descriptor) || descriptor.value !== intrinsicDateToJson || Reflect.ownKeys(value).length !== 0) unsafe(); let time: number; try { time = intrinsicDateGetTime.call(value as Date); } catch { unsafe(); } if (!Number.isFinite(time)) unsafe(); return new CapturedDate(time); }
|
|
182
|
+
if (prototype === capturedUint8ArrayPrototype) { try { const names = Object.getOwnPropertyNames(value); const length = typedArrayLength.call(value); if (names.length !== length || names.some((name, index) => name !== String(index)) || Object.getOwnPropertySymbols(value).length) unsafe(); const copy = new CapturedUint8Array(length); capturedUint8ArraySet.call(copy, value as Uint8Array); return copy; } catch { unsafe(); } }
|
|
183
|
+
active.add(value);
|
|
184
|
+
if (Array.isArray(value)) { if (prototype !== Array.prototype || unsafeToJson(prototype)) unsafe(); const descriptors = ownData(value); const length = descriptors.get("length")?.value; if (!Number.isSafeInteger(length) || length < 0 || descriptors.size !== length + 1) unsafe(); const result: unknown[] = new Array(length); memo.set(value, result); for (let index = 0; index < length; index += 1) result[index] = snapshot(descriptors.get(String(index))?.value, active, memo, freeze); active.delete(value); return freeze ? Object.freeze(result) : result; }
|
|
185
|
+
if (!plain(value) || unsafeToJson(prototype)) unsafe(); const descriptors = ownData(value); const ownToJson = descriptors.get("toJSON")?.value; if (typeof ownToJson === "function") unsafe(); const result: Record<string, unknown> = Object.create(prototype); memo.set(value, result);
|
|
186
|
+
for (const [key, descriptor] of descriptors) Object.defineProperty(result, key, { value: snapshot(descriptor.value, active, memo, freeze), enumerable: descriptor.enumerable, configurable: false, writable: !freeze });
|
|
187
|
+
active.delete(value); return freeze ? Object.freeze(result) : result;
|
|
188
|
+
}
|
|
189
|
+
function unsafeToJson(prototype: object | null): boolean {
|
|
190
|
+
for (let current = prototype; current !== null; current = Object.getPrototypeOf(current)) { const descriptor = Object.getOwnPropertyDescriptor(current, "toJSON"); if (descriptor && (!("value" in descriptor) || typeof descriptor.value === "function")) return true; }
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
function storageValue(value: unknown, type: PropertyModel["type"], allowNull = false): SqlParam {
|
|
194
|
+
if (value === null && allowNull) return null;
|
|
195
|
+
const safe = snapshot(value, new Set(), new WeakMap(), true);
|
|
196
|
+
if (type === "text" && typeof safe !== "string") unsafe();
|
|
197
|
+
if (type === "boolean" && typeof safe !== "boolean") unsafe();
|
|
198
|
+
if (type === "integer" && !(typeof safe === "bigint" ? safe >= -9223372036854775808n && safe <= 9223372036854775807n : typeof safe === "number" && Number.isSafeInteger(safe))) unsafe();
|
|
199
|
+
if (type === "real" && !(typeof safe === "number" && Number.isFinite(safe))) unsafe();
|
|
200
|
+
if (type === "datetime" && (typeof safe !== "object" || safe === null || Object.getPrototypeOf(safe) !== capturedDatePrototype)) unsafe();
|
|
201
|
+
if (type === "json" && !jsonValue(safe)) unsafe();
|
|
202
|
+
return safe as SqlParam;
|
|
203
|
+
}
|
|
204
|
+
function snapshotEncodedSqlParam(value: unknown): SqlParam {
|
|
205
|
+
if (isPostgresJsonScalarParameter(value)) return value;
|
|
206
|
+
const copy = (current: unknown, nested: boolean, active = new Set<object>()): unknown => {
|
|
207
|
+
if (current === null || typeof current === "string" || typeof current === "boolean") return current;
|
|
208
|
+
if (typeof current === "number") { if (!Number.isFinite(current)) unsafe(); return current; }
|
|
209
|
+
if (typeof current === "bigint") { if (nested) unsafe(); return current; }
|
|
210
|
+
if (typeof current !== "object" || types.isProxy(current)) unsafe();
|
|
211
|
+
if (active.has(current)) unsafe();
|
|
212
|
+
const prototype = Object.getPrototypeOf(current);
|
|
213
|
+
if (prototype === capturedDatePrototype || prototype === capturedUint8ArrayPrototype) { if (nested) unsafe(); return snapshot(current, new Set(), new WeakMap(), true); }
|
|
214
|
+
active.add(current);
|
|
215
|
+
if (Array.isArray(current)) { if (prototype !== Array.prototype || unsafeToJson(prototype)) unsafe(); const fields = ownData(current); const length = fields.get("length")?.value; if (!Number.isSafeInteger(length) || length < 0 || fields.size !== length + 1) unsafe(); const result = new Array<unknown>(length); for (let index = 0; index < length; index += 1) result[index] = copy(fields.get(String(index))?.value, true, active); active.delete(current); return Object.freeze(result); }
|
|
216
|
+
if (!plain(current) || unsafeToJson(prototype)) unsafe(); const result: Record<string, unknown> = Object.create(prototype); for (const [key, descriptor] of ownData(current)) Object.defineProperty(result, key, { value: copy(descriptor.value, true, active), enumerable: descriptor.enumerable, writable: false, configurable: false }); active.delete(current); return Object.freeze(result);
|
|
217
|
+
};
|
|
218
|
+
return copy(value, false) as SqlParam;
|
|
219
|
+
}
|
|
220
|
+
function jsonValue(value: unknown): boolean {
|
|
221
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
222
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
223
|
+
if (Array.isArray(value)) return value.every(jsonValue);
|
|
224
|
+
if (plain(value)) return [...ownData(value).values()].every((descriptor) => jsonValue(descriptor.value));
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { OrmError } from "../errors";
|
|
2
|
+
import type { EntityModel } from "../Metadata/types";
|
|
3
|
+
import { KeyTuple } from "../Metadata/KeyTuple";
|
|
4
|
+
import { maxParametersPerInList } from "../Providers/limits";
|
|
5
|
+
import type { DbContextRuntime } from "../runtime";
|
|
6
|
+
import { materialize } from "./materialize";
|
|
7
|
+
import { EMPTY_PLAN } from "./QueryPlan";
|
|
8
|
+
import { SqlTranslator } from "./SqlTranslator";
|
|
9
|
+
|
|
10
|
+
interface Level {
|
|
11
|
+
readonly model: EntityModel;
|
|
12
|
+
readonly entities: object[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Eager loading of navigations with split queries: for each level of the path,
|
|
17
|
+
* related entities are loaded with one `WHERE key IN (...)` query and stitched
|
|
18
|
+
* together in memory. This avoids the Cartesian product of collection JOINs.
|
|
19
|
+
*/
|
|
20
|
+
export class IncludeLoader {
|
|
21
|
+
constructor(private readonly runtime: DbContextRuntime) {}
|
|
22
|
+
|
|
23
|
+
async load(rootModel: EntityModel, roots: object[], paths: readonly (readonly string[])[], noTracking: boolean): Promise<void> {
|
|
24
|
+
if (roots.length === 0) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const root: Level = { model: rootModel, entities: roots };
|
|
28
|
+
// A shared prefix must retain the same instances even without tracking.
|
|
29
|
+
// Keep this cache local: another execution must read its own graph.
|
|
30
|
+
const loaded = new Map<Level, Map<string, Level>>();
|
|
31
|
+
for (const path of paths) {
|
|
32
|
+
let level = root;
|
|
33
|
+
for (const navigationName of path) {
|
|
34
|
+
let navigations = loaded.get(level);
|
|
35
|
+
if (!navigations) {
|
|
36
|
+
navigations = new Map();
|
|
37
|
+
loaded.set(level, navigations);
|
|
38
|
+
}
|
|
39
|
+
let next = navigations.get(navigationName);
|
|
40
|
+
if (!next) {
|
|
41
|
+
next = await this.loadLevel(level, navigationName, noTracking);
|
|
42
|
+
navigations.set(navigationName, next);
|
|
43
|
+
}
|
|
44
|
+
level = next;
|
|
45
|
+
if (level.entities.length === 0) {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private async loadLevel(parent: Level, navigationName: string, noTracking: boolean): Promise<Level> {
|
|
53
|
+
const relation = parent.model.relationByName(navigationName);
|
|
54
|
+
if (!relation) {
|
|
55
|
+
throw new OrmError(`Navigation "${navigationName}" is not defined on entity "${parent.model.name}".`);
|
|
56
|
+
}
|
|
57
|
+
const targetModel = this.runtime.models.targetModel(relation.target);
|
|
58
|
+
if (relation.kind === "reference") {
|
|
59
|
+
return this.loadReference(parent, navigationName, relation.foreignKey, targetModel, noTracking);
|
|
60
|
+
}
|
|
61
|
+
return this.loadCollection(parent, navigationName, relation.foreignKey, targetModel, noTracking);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** many-to-one: the FK on the parent points to the target's key. */
|
|
65
|
+
private async loadReference(
|
|
66
|
+
parent: Level,
|
|
67
|
+
navigationName: string,
|
|
68
|
+
foreignKey: string | readonly string[],
|
|
69
|
+
targetModel: EntityModel,
|
|
70
|
+
noTracking: boolean,
|
|
71
|
+
): Promise<Level> {
|
|
72
|
+
if (typeof foreignKey !== "string") return this.loadCompositeReference(parent, navigationName, foreignKey, targetModel, noTracking);
|
|
73
|
+
const keys = this.distinct(targetModel, parent.entities.map((entity) => (entity as Record<string, unknown>)[foreignKey]));
|
|
74
|
+
if (keys.length === 0) {
|
|
75
|
+
for (const entity of parent.entities) {
|
|
76
|
+
(entity as Record<string, unknown>)[navigationName] = null;
|
|
77
|
+
}
|
|
78
|
+
return { model: targetModel, entities: [] };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const targetKey = this.scalarKey(targetModel);
|
|
82
|
+
const children = await this.queryIn(targetModel, targetKey.propertyName, keys, noTracking);
|
|
83
|
+
const byKey = new Map<string, object>();
|
|
84
|
+
for (const child of children) {
|
|
85
|
+
byKey.set(this.tuple(targetModel, [(child as Record<string, unknown>)[targetKey.propertyName]])!, child);
|
|
86
|
+
}
|
|
87
|
+
for (const entity of parent.entities) {
|
|
88
|
+
const fk = (entity as Record<string, unknown>)[foreignKey];
|
|
89
|
+
const key = this.tuple(targetModel, [fk]);
|
|
90
|
+
(entity as Record<string, unknown>)[navigationName] = key === undefined ? null : byKey.get(key) ?? null;
|
|
91
|
+
}
|
|
92
|
+
return { model: targetModel, entities: children };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** one-to-many: the FK on the target points to the parent's key. */
|
|
96
|
+
private async loadCollection(
|
|
97
|
+
parent: Level,
|
|
98
|
+
navigationName: string,
|
|
99
|
+
foreignKey: string | readonly string[],
|
|
100
|
+
targetModel: EntityModel,
|
|
101
|
+
noTracking: boolean,
|
|
102
|
+
): Promise<Level> {
|
|
103
|
+
if (typeof foreignKey !== "string") return this.loadCompositeCollection(parent, navigationName, foreignKey, targetModel, noTracking);
|
|
104
|
+
const keyProperty = this.scalarKey(parent.model).propertyName;
|
|
105
|
+
const keys = this.distinct(parent.model, parent.entities.map((entity) => (entity as Record<string, unknown>)[keyProperty]));
|
|
106
|
+
|
|
107
|
+
const children = keys.length > 0 ? await this.queryIn(targetModel, foreignKey, keys, noTracking) : [];
|
|
108
|
+
const groups = new Map<string, object[]>();
|
|
109
|
+
for (const child of children) {
|
|
110
|
+
const fk = this.tuple(parent.model, [(child as Record<string, unknown>)[foreignKey]]);
|
|
111
|
+
if (fk === undefined) continue;
|
|
112
|
+
const bucket = groups.get(fk);
|
|
113
|
+
if (bucket) {
|
|
114
|
+
bucket.push(child);
|
|
115
|
+
} else {
|
|
116
|
+
groups.set(fk, [child]);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
for (const entity of parent.entities) {
|
|
120
|
+
const key = this.tuple(parent.model, [(entity as Record<string, unknown>)[keyProperty]]);
|
|
121
|
+
(entity as Record<string, unknown>)[navigationName] = key === undefined ? [] : groups.get(key) ?? [];
|
|
122
|
+
}
|
|
123
|
+
return { model: targetModel, entities: children };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private async loadCompositeReference(parent: Level, navigationName: string, foreignKeys: readonly string[], target: EntityModel, noTracking: boolean): Promise<Level> {
|
|
127
|
+
const tuples = parent.entities.map((entity) => foreignKeys.map((name) => (entity as Record<string, unknown>)[name])).filter((tuple) => tuple.every((value) => value !== null && value !== undefined));
|
|
128
|
+
const children = await this.queryTuples(target, target.key.map((key) => key.propertyName), tuples, noTracking);
|
|
129
|
+
const byKey = new Map(children.map((child) => [this.tuple(target, target.key.map((key) => (child as Record<string, unknown>)[key.propertyName]))!, child]));
|
|
130
|
+
for (const entity of parent.entities) {
|
|
131
|
+
const key = this.tuple(target, foreignKeys.map((name) => (entity as Record<string, unknown>)[name]));
|
|
132
|
+
(entity as Record<string, unknown>)[navigationName] = key === undefined ? null : byKey.get(key) ?? null;
|
|
133
|
+
}
|
|
134
|
+
return { model: target, entities: children };
|
|
135
|
+
}
|
|
136
|
+
private async loadCompositeCollection(parent: Level, navigationName: string, foreignKeys: readonly string[], target: EntityModel, noTracking: boolean): Promise<Level> {
|
|
137
|
+
const properties = parent.model.key.map((key) => key.propertyName); if (properties.length !== foreignKeys.length) throw new OrmError("Composite collection relation does not match parent primary key.");
|
|
138
|
+
const tuples = parent.entities.map((entity) => properties.map((name) => (entity as Record<string, unknown>)[name])); const children = await this.queryTuples(target, foreignKeys, tuples, noTracking);
|
|
139
|
+
const groups = new Map<string, object[]>();
|
|
140
|
+
for (const child of children) {
|
|
141
|
+
const key = this.tuple(parent.model, foreignKeys.map((name) => (child as Record<string, unknown>)[name]));
|
|
142
|
+
if (key === undefined) continue;
|
|
143
|
+
const bucket = groups.get(key);
|
|
144
|
+
if (bucket) bucket.push(child); else groups.set(key, [child]);
|
|
145
|
+
}
|
|
146
|
+
for (const entity of parent.entities) {
|
|
147
|
+
const key = this.tuple(parent.model, properties.map((name) => (entity as Record<string, unknown>)[name]));
|
|
148
|
+
(entity as Record<string, unknown>)[navigationName] = key === undefined ? [] : groups.get(key) ?? [];
|
|
149
|
+
}
|
|
150
|
+
return { model: target, entities: children };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private scalarKey(model: EntityModel) {
|
|
154
|
+
if (model.key.length !== 1) throw new Error(`IncludeLoader does not support composite legacy relation metadata for "${model.name}".`);
|
|
155
|
+
return model.key[0];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private async queryIn(model: EntityModel, property: string, values: readonly unknown[], noTracking: boolean): Promise<object[]> {
|
|
159
|
+
const translator = new SqlTranslator(model, this.runtime.provider.dialect);
|
|
160
|
+
const result: object[] = [];
|
|
161
|
+
const chunkSize = maxParametersPerInList(this.runtime.provider);
|
|
162
|
+
for (let start = 0; start < values.length; start += chunkSize) {
|
|
163
|
+
const chunk = values.slice(start, start + chunkSize);
|
|
164
|
+
const { sql, params } = translator.selectAll({
|
|
165
|
+
...EMPTY_PLAN,
|
|
166
|
+
conditions: [{ kind: "in", property, values: chunk }],
|
|
167
|
+
noTracking,
|
|
168
|
+
});
|
|
169
|
+
const rows = await this.runtime.provider.query(sql, params);
|
|
170
|
+
for (const row of rows) {
|
|
171
|
+
let entity = materialize<object>(model, row, this.runtime.provider.dialect);
|
|
172
|
+
if (!noTracking) {
|
|
173
|
+
entity = this.runtime.tracker.trackLoaded(entity, model);
|
|
174
|
+
}
|
|
175
|
+
result.push(entity);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private async queryTuples(model: EntityModel, properties: readonly string[], values: readonly (readonly unknown[])[], noTracking: boolean): Promise<object[]> {
|
|
182
|
+
const result: object[] = []; const perTuple = properties.length; const chunkSize = Math.max(1, Math.floor(maxParametersPerInList(this.runtime.provider) / perTuple));
|
|
183
|
+
for (let start = 0; start < values.length; start += chunkSize) { const translator = new SqlTranslator(model, this.runtime.provider.dialect); const { sql, params } = translator.selectAll({ ...EMPTY_PLAN, conditions: [{ kind: "tuples", properties, values: values.slice(start, start + chunkSize) }], noTracking }); for (let entity of (await this.runtime.provider.query(sql, params)).map((row) => materialize<object>(model, row, this.runtime.provider.dialect))) { if (!noTracking) entity = this.runtime.tracker.trackLoaded(entity, model); result.push(entity); } }
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
private tuple(model: EntityModel, values: readonly unknown[]): string | undefined {
|
|
187
|
+
if (values.length !== model.key.length) throw new OrmError("Relation does not match target primary key.");
|
|
188
|
+
const record = Object.fromEntries(model.key.map((key, index) => [key.propertyName, values[index]]));
|
|
189
|
+
return KeyTuple.fromEntity(model, record)?.toString();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private distinct(model: EntityModel, values: readonly unknown[]): unknown[] {
|
|
193
|
+
const seen = new Map<string, unknown>();
|
|
194
|
+
for (const value of values) {
|
|
195
|
+
const key = this.tuple(model, [value]);
|
|
196
|
+
if (key !== undefined) seen.set(key, value);
|
|
197
|
+
}
|
|
198
|
+
return [...seen.values()];
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Condition } from "./conditions";
|
|
2
|
+
import type { RowLockMode } from "../Providers/types";
|
|
3
|
+
|
|
4
|
+
export interface OrderClause {
|
|
5
|
+
readonly property: string;
|
|
6
|
+
readonly descending: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Declarative query plan, built by the where/orderBy/take/skip chain and
|
|
11
|
+
* translated into SQL. Immutable: each step returns a new plan, so a base query
|
|
12
|
+
* can be reused and its translation cached.
|
|
13
|
+
*/
|
|
14
|
+
export interface QueryPlan {
|
|
15
|
+
readonly conditions: readonly Condition[];
|
|
16
|
+
readonly orders: readonly OrderClause[];
|
|
17
|
+
readonly limit?: number;
|
|
18
|
+
/** Original public take value, retained privately for SKIP LOCKED admission. */
|
|
19
|
+
readonly requestedLimit?: number;
|
|
20
|
+
/** Invalid explicit take survives implicit first/firstOrDefault take(1). */
|
|
21
|
+
readonly invalidRequestedLimit?: number;
|
|
22
|
+
readonly offset?: number;
|
|
23
|
+
/** AsNoTracking: do not register the result in the ChangeTracker. */
|
|
24
|
+
readonly noTracking: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Eager loading paths of navigations (Include/ThenInclude). Each path is a
|
|
27
|
+
* sequence of navigation names from the root, for example `["posts", "comments"]`.
|
|
28
|
+
*/
|
|
29
|
+
readonly includes: readonly (readonly string[])[];
|
|
30
|
+
/** Do not apply global `@QueryFilter`s and the soft-delete filter. */
|
|
31
|
+
readonly ignoreQueryFilters: boolean;
|
|
32
|
+
/** Column projection: alias -> propertyName (from `.select(...)`). */
|
|
33
|
+
readonly projections: readonly { readonly alias: string; readonly property: string }[];
|
|
34
|
+
/** Row lock held by the caller-owned transaction until commit/rollback. */
|
|
35
|
+
readonly rowLock?: RowLockMode;
|
|
36
|
+
/** Private renderer policy for the bounded SKIP LOCKED admission. */
|
|
37
|
+
readonly skipLocked?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const EMPTY_PLAN: QueryPlan = {
|
|
41
|
+
conditions: [],
|
|
42
|
+
orders: [],
|
|
43
|
+
noTracking: false,
|
|
44
|
+
includes: [],
|
|
45
|
+
ignoreQueryFilters: false,
|
|
46
|
+
projections: [],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export function withCondition(plan: QueryPlan, condition: Condition): QueryPlan {
|
|
50
|
+
return { ...plan, conditions: [...plan.conditions, condition] };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function withOrder(plan: QueryPlan, order: OrderClause): QueryPlan {
|
|
54
|
+
return { ...plan, orders: [...plan.orders, order] };
|
|
55
|
+
}
|