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,684 @@
|
|
|
1
|
+
# Repository: specification
|
|
2
|
+
|
|
3
|
+
Repository (`IRepository<T>` / `Repository<T>`) is a **thin wrapper over the ORM** for working with **one entity**.
|
|
4
|
+
It writes no SQL and does not duplicate ORM logic: everything is delegated to `DbSet<T>` and `DbContext`.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## In plain words
|
|
9
|
+
|
|
10
|
+
Picture three layers:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Service (UserService)
|
|
14
|
+
↓
|
|
15
|
+
Repository<User> ← "work only with users"
|
|
16
|
+
↓
|
|
17
|
+
DbSet<User> + DbContext ← the whole ORM (queries, tracking, SaveChanges)
|
|
18
|
+
↓
|
|
19
|
+
Database
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Repository** is the "door" into the ORM for a concrete table/entity.
|
|
23
|
+
Instead of writing `ctx.users.where(...)` in every service or carrying the whole `DbContext` around, the service gets a ready `IRepository<User>` object through DI.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Why use it
|
|
28
|
+
|
|
29
|
+
| Without Repository | With Repository |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| The service depends on the whole `AppDbContext` | The service depends only on `IRepository<User>` |
|
|
32
|
+
| Easy to touch other tables by accident | One entity is visible, so fewer mistakes |
|
|
33
|
+
| Harder to test (needs the full context) | `IRepository<User>` can be replaced with a mock |
|
|
34
|
+
| Data access style is scattered across the project | One pattern: `query()` / `add()` / `saveChanges()` |
|
|
35
|
+
|
|
36
|
+
**Repository does not replace the ORM**: it **organizes** access to it in application code (services, controllers, background tasks).
|
|
37
|
+
|
|
38
|
+
### When Repository fits
|
|
39
|
+
|
|
40
|
+
- CRUD and LINQ-like queries over one entity
|
|
41
|
+
- The application service layer with DI
|
|
42
|
+
- Several repositories in one request (they all share one scoped `DbContext`)
|
|
43
|
+
|
|
44
|
+
### When to use `DbContext` directly
|
|
45
|
+
|
|
46
|
+
- A complex transaction over several unrelated entities with manual orchestration
|
|
47
|
+
- Scripts/migrations that need no DI (`DbContextFactory`)
|
|
48
|
+
- Raw SQL through `database.executeSqlRaw` that is not tied to one entity
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Module files
|
|
53
|
+
|
|
54
|
+
| File | Purpose |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `IRepository.ts` | The interface + the `IRepository` and `repositoryFor()` DI tokens |
|
|
57
|
+
| `Repository.ts` | The scoped implementation (delegates to the ORM) |
|
|
58
|
+
| `registerRepositories.ts` | Registers repositories in the DI container |
|
|
59
|
+
|
|
60
|
+
Exports: `bazis/core/orm` (`IRepository`, `repositoryFor`, `registerRepositories`); the `Repository` class itself is in `bazis/library/orm`.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Lifecycle and DI
|
|
65
|
+
|
|
66
|
+
- **Lifetime:** `scoped`: one `Repository<T>` instance per scope (usually one HTTP request)
|
|
67
|
+
- **DbContext** is scoped too → repositories registered for one context type in one scope **share its ChangeTracker**. Repositories of different contexts are independent
|
|
68
|
+
- `saveChanges()` on any repository saves **all** pending changes of the context (not only "its own" table)
|
|
69
|
+
- For a new application operation, make the write boundary explicit: inject your
|
|
70
|
+
`DbContext`, work with its DbSet and call `db.saveChanges()`. The repository
|
|
71
|
+
method stays a compatible shortcut for the same operation, not a separate commit.
|
|
72
|
+
|
|
73
|
+
Registration is automatic with `ormModule({ ... })`:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const DataModule = ormModule({
|
|
77
|
+
context: AppDbContext,
|
|
78
|
+
entities: [User, Post],
|
|
79
|
+
provider: postgres({ url: Bun.env.BAZIS_PG_URL! }),
|
|
80
|
+
registerRepositories: true, // true by default
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`ormModule` exports the `IRepository` family token so importing modules can inject `repositoryFor(User)`.
|
|
85
|
+
|
|
86
|
+
Turning repositories off:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
ormModule({ ..., registerRepositories: false })
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## API: `IRepository<T>`
|
|
95
|
+
|
|
96
|
+
Below are all the interface members. `Repository<T>` behaves **identically** (it only delegates).
|
|
97
|
+
|
|
98
|
+
### Queries
|
|
99
|
+
|
|
100
|
+
#### `query(): DbSet<T>`
|
|
101
|
+
|
|
102
|
+
The entry point to the ORM's LINQ-like queries. Returns the same object as `dbSet`.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const adults = await users
|
|
106
|
+
.query()
|
|
107
|
+
.where((u) => u.age.gte(18))
|
|
108
|
+
.orderBy((u) => u.name)
|
|
109
|
+
.take(20)
|
|
110
|
+
.toList();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### `readonly dbSet: DbSet<T>`
|
|
114
|
+
|
|
115
|
+
Direct access to the `DbSet` is an escape hatch for advanced scenarios.
|
|
116
|
+
`query()` and `dbSet` are the same thing; pick whichever reads better in the code.
|
|
117
|
+
|
|
118
|
+
**What `DbSet` / the chain after `query()` can do** (the full ORM list):
|
|
119
|
+
|
|
120
|
+
| Method | What it does |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| `where(predicate)` | Filter (several `where` = AND) |
|
|
123
|
+
| `orderBy(selector)` | Ascending sort |
|
|
124
|
+
| `orderByDescending(selector)` | Descending sort |
|
|
125
|
+
| `take(n)` | LIMIT |
|
|
126
|
+
| `skip(n)` | OFFSET |
|
|
127
|
+
| `asNoTracking()` | Do not track the result (faster for read-only) |
|
|
128
|
+
| `ignoreQueryFilters()` | Turn off `@QueryFilter` and the soft-delete filter |
|
|
129
|
+
| `select(u => ({ ... }))` | Projection into a plain object |
|
|
130
|
+
| `include(u => u.nav)` | Eager loading of a navigation |
|
|
131
|
+
| `thenInclude(...)` | Nested loading after `include` |
|
|
132
|
+
| `toList()` | Run the query, return an array |
|
|
133
|
+
| `first(predicate?)` | The first element or an error |
|
|
134
|
+
| `firstOrDefault(predicate?)` | The first element or `null` |
|
|
135
|
+
| `count(predicate?)` | Number of rows |
|
|
136
|
+
| `any(predicate?)` | Whether there is at least one row |
|
|
137
|
+
|
|
138
|
+
**Predicates** (a Proxy DSL, not strings in SQL):
|
|
139
|
+
|
|
140
|
+
`eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `like`, `startsWith`, `endsWith`, `contains`, `in`, `isNull`, `isNotNull`, the combinators `.and()`, `.or()`, `.not()`.
|
|
141
|
+
|
|
142
|
+
> Important: in predicates use `.and()` / `.or()`, not JavaScript `&&` / `||`.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
### Reading by key
|
|
147
|
+
|
|
148
|
+
#### `find(key: unknown): Promise<T | null>`
|
|
149
|
+
|
|
150
|
+
A primary key lookup runs SQL with the access filters and soft delete applied.
|
|
151
|
+
If the found entity is already tracked, the same object is returned (identity map).
|
|
152
|
+
Having the object in the tracker does not by itself allow skipping the query.
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
const user = await users.find(42);
|
|
156
|
+
if (user === null) {
|
|
157
|
+
// not found
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
### Changes (change tracking)
|
|
164
|
+
|
|
165
|
+
Changes **do not reach the database right away**: they pile up in the `ChangeTracker` until `saveChanges()` is called.
|
|
166
|
+
|
|
167
|
+
#### `add(entity: T): T`
|
|
168
|
+
|
|
169
|
+
Marks the entity for **INSERT**. Returns the same reference.
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
const user = users.add(Object.assign(new User(), { name: "Ann" }));
|
|
173
|
+
await users.saveChanges(); // INSERT, the id is generated
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### `addRange(entities: readonly T[]): void`
|
|
177
|
+
|
|
178
|
+
Batch insert (several `Added` in one call).
|
|
179
|
+
|
|
180
|
+
#### `update(entity: T): T`
|
|
181
|
+
|
|
182
|
+
Marks a **full UPDATE** of all non-key columns.
|
|
183
|
+
|
|
184
|
+
#### `remove(entity: T): T`
|
|
185
|
+
|
|
186
|
+
Marks for deletion. For `@SoftDelete` it sets a timestamp instead of DELETE.
|
|
187
|
+
|
|
188
|
+
#### `attach(entity: T): T`
|
|
189
|
+
|
|
190
|
+
Attaches an existing object as `Unchanged` (with a snapshot for change tracking).
|
|
191
|
+
|
|
192
|
+
#### `stateOf(entity: T): EntityState`
|
|
193
|
+
|
|
194
|
+
The current state in the tracker:
|
|
195
|
+
|
|
196
|
+
| `EntityState` | Meaning |
|
|
197
|
+
| --- | --- |
|
|
198
|
+
| `Detached` | Not tracked |
|
|
199
|
+
| `Unchanged` | Loaded, no changes |
|
|
200
|
+
| `Added` | Will be INSERTed |
|
|
201
|
+
| `Modified` | Will be UPDATEd |
|
|
202
|
+
| `Deleted` | Will be DELETEd (or soft-deleted) |
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### Saving (Unit of Work)
|
|
207
|
+
|
|
208
|
+
#### `saveChanges(): Promise<number>`
|
|
209
|
+
|
|
210
|
+
Applies **all** changes of the current `DbContext` in **one transaction**.
|
|
211
|
+
Returns the number of processed entities; `0` if there is nothing to save.
|
|
212
|
+
|
|
213
|
+
Before saving, the ORM runs:
|
|
214
|
+
|
|
215
|
+
1. `DetectChanges` (comparison with the snapshot)
|
|
216
|
+
2. Validation (`@Validator`, if `validateOnSave: true`)
|
|
217
|
+
3. INSERT / UPDATE / DELETE with parameterized SQL
|
|
218
|
+
4. `AcceptChanges` on success
|
|
219
|
+
|
|
220
|
+
On an error the transaction is rolled back and the tracker state is kept: you can fix the data and call `saveChanges()` again.
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
users.add(newUser);
|
|
224
|
+
posts.add(newPost);
|
|
225
|
+
await users.saveChanges(); // saves both User and Post: one context
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
### Infrastructure (read-only)
|
|
231
|
+
|
|
232
|
+
#### `readonly changeTracker: ChangeTracker`
|
|
233
|
+
|
|
234
|
+
Direct access to the context's tracker (rarely needed in services; for debugging and advanced cases).
|
|
235
|
+
|
|
236
|
+
#### `readonly database: DatabaseFacade`
|
|
237
|
+
|
|
238
|
+
The database facade of the same context:
|
|
239
|
+
|
|
240
|
+
- `ensureCreated()` / `migrate()` / `migrateVersioned()`
|
|
241
|
+
- `executeSqlRaw()` / `querySqlRaw()`
|
|
242
|
+
- `transaction(work)`
|
|
243
|
+
- `canConnect()`
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
await users.database.executeSqlRaw(
|
|
247
|
+
'UPDATE "Users" SET "active" = {0} WHERE "id" = {1}',
|
|
248
|
+
false,
|
|
249
|
+
userId,
|
|
250
|
+
);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## DI: tokens and injection
|
|
256
|
+
|
|
257
|
+
### `repositoryFor(User)`: the recommended way
|
|
258
|
+
|
|
259
|
+
A typed token for a concrete entity:
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
import { repositoryFor, type IRepository } from "@/core/orm";
|
|
263
|
+
|
|
264
|
+
class UserService {
|
|
265
|
+
constructor(private readonly users: IRepository<User>) {}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Registration in a module (if you do not use `ormModule`):
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
import { Module } from "@/core/di";
|
|
273
|
+
import { registerRepositories } from "@/core/orm";
|
|
274
|
+
|
|
275
|
+
@Module({
|
|
276
|
+
configure: (di) => registerRepositories(di, AppDbContext, [User, Post]),
|
|
277
|
+
exports: [IRepository],
|
|
278
|
+
})
|
|
279
|
+
class MyModule {}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Explicit dependencies in a provider
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
import { scoped, DI } from "@/core/di";
|
|
286
|
+
import { repositoryFor } from "@/core/orm";
|
|
287
|
+
|
|
288
|
+
providers: [
|
|
289
|
+
scoped(
|
|
290
|
+
DI.classProvider(UserService, UserService, [repositoryFor(User)] as const),
|
|
291
|
+
),
|
|
292
|
+
],
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### `IRepository` — open generic family
|
|
296
|
+
|
|
297
|
+
A low-level token for DI extensions:
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
IRepository.of(User) // the same as repositoryFor(User), but without the richer typing
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Full example
|
|
306
|
+
|
|
307
|
+
### 1. Entity and context
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
import { Column, DbContext, Entity, Key, ormModule } from "bazis/core/orm";
|
|
311
|
+
import { postgres } from "bazis/library/orm";
|
|
312
|
+
|
|
313
|
+
@Entity()
|
|
314
|
+
class User {
|
|
315
|
+
@Key() id = 0;
|
|
316
|
+
@Column({ type: "text" }) name = "";
|
|
317
|
+
@Column({ type: "integer" }) age = 0;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
class AppDbContext extends DbContext {
|
|
321
|
+
readonly users = this.set(User);
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### 2. Data module
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
export const DataModule = ormModule({
|
|
329
|
+
context: AppDbContext,
|
|
330
|
+
entities: [User],
|
|
331
|
+
provider: postgres({ url: Bun.env.BAZIS_PG_URL! }),
|
|
332
|
+
migrateOnStart: true,
|
|
333
|
+
});
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### 3. Service
|
|
337
|
+
|
|
338
|
+
```ts
|
|
339
|
+
import { repositoryFor, type IRepository } from "bazis/core/orm";
|
|
340
|
+
|
|
341
|
+
export class UserService {
|
|
342
|
+
constructor(private readonly users: IRepository<User>) {}
|
|
343
|
+
|
|
344
|
+
listAdults(limit: number) {
|
|
345
|
+
return this.users
|
|
346
|
+
.query()
|
|
347
|
+
.where((u) => u.age.gte(18))
|
|
348
|
+
.orderBy((u) => u.name)
|
|
349
|
+
.take(limit)
|
|
350
|
+
.toList();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async findById(id: number) {
|
|
354
|
+
return this.users.find(id);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async create(name: string, age: number) {
|
|
358
|
+
const user = this.users.add(Object.assign(new User(), { name, age }));
|
|
359
|
+
await this.users.saveChanges();
|
|
360
|
+
return user; // the id is filled in after saveChanges
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async rename(id: number, name: string) {
|
|
364
|
+
const user = await this.users.find(id);
|
|
365
|
+
if (user === null) return false;
|
|
366
|
+
user.name = name; // snapshot tracking picks up the change
|
|
367
|
+
await this.users.saveChanges();
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async removeById(id: number) {
|
|
372
|
+
const user = await this.users.find(id);
|
|
373
|
+
if (user === null) return false;
|
|
374
|
+
this.users.remove(user);
|
|
375
|
+
await this.users.saveChanges();
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### 4. Application module
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
import { Module, scoped } from "@/core/di";
|
|
385
|
+
import { repositoryFor } from "@/core/orm";
|
|
386
|
+
|
|
387
|
+
@Module({
|
|
388
|
+
imports: [DataModule],
|
|
389
|
+
providers: [
|
|
390
|
+
scoped(UserService, UserService, [repositoryFor(User)] as const),
|
|
391
|
+
],
|
|
392
|
+
exports: [UserService],
|
|
393
|
+
})
|
|
394
|
+
class UsersModule {}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## Query examples
|
|
400
|
+
|
|
401
|
+
### Filter and paging
|
|
402
|
+
|
|
403
|
+
```ts
|
|
404
|
+
const page = await users
|
|
405
|
+
.query()
|
|
406
|
+
.where((u) => u.age.gte(18).and(u.name.startsWith("A")))
|
|
407
|
+
.orderByDescending((u) => u.age)
|
|
408
|
+
.skip(20)
|
|
409
|
+
.take(10)
|
|
410
|
+
.toList();
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Projection (only the needed fields)
|
|
414
|
+
|
|
415
|
+
```ts
|
|
416
|
+
const labels = await users
|
|
417
|
+
.query()
|
|
418
|
+
.select((u) => ({ id: u.id, name: u.name }))
|
|
419
|
+
.where((u) => u.age.gt(21))
|
|
420
|
+
.toList();
|
|
421
|
+
// [{ id: 1, name: "Ann" }, ...]
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Eager loading of relations
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
const authors = await authorsRepo
|
|
428
|
+
.query()
|
|
429
|
+
.include((a) => a.books)
|
|
430
|
+
.thenInclude((b) => b.reviews)
|
|
431
|
+
.toList();
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Global filter and soft delete
|
|
435
|
+
|
|
436
|
+
```ts
|
|
437
|
+
// @QueryFilter on the entity goes into WHERE automatically
|
|
438
|
+
await docs.query().toList();
|
|
439
|
+
|
|
440
|
+
// See the "hidden" rows:
|
|
441
|
+
await docs.query().ignoreQueryFilters().toList();
|
|
442
|
+
|
|
443
|
+
// @SoftDelete: remove() sets deletedAt instead of DELETE
|
|
444
|
+
docs.remove(doc);
|
|
445
|
+
await docs.saveChanges();
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Read-only without tracking
|
|
449
|
+
|
|
450
|
+
```ts
|
|
451
|
+
const rows = await users.query().asNoTracking().toList();
|
|
452
|
+
// the objects are not in the changeTracker, which is faster for reports and lists
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## Several repositories in one service
|
|
458
|
+
|
|
459
|
+
```ts
|
|
460
|
+
class OrderService {
|
|
461
|
+
constructor(
|
|
462
|
+
private readonly orders: IRepository<Order>,
|
|
463
|
+
private readonly products: IRepository<Product>,
|
|
464
|
+
) {}
|
|
465
|
+
|
|
466
|
+
async placeOrder(productId: number, qty: number) {
|
|
467
|
+
const product = await this.products.find(productId);
|
|
468
|
+
if (product === null) throw new Error("product not found");
|
|
469
|
+
|
|
470
|
+
this.orders.add(Object.assign(new Order(), { productId, qty }));
|
|
471
|
+
product.stock -= qty;
|
|
472
|
+
|
|
473
|
+
await this.orders.saveChanges(); // saves both Order and Product
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Both repositories use **one** scoped `DbContext` → one transaction per `saveChanges()`.
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## What Repository does **not** do
|
|
483
|
+
|
|
484
|
+
- It does not create a separate Unit of Work: that is `DbContext.saveChanges()`
|
|
485
|
+
- It does not generate SQL: that is `SqlTranslator` / `SaveExecutor`
|
|
486
|
+
- It does not validate on its own: the ORM validates on `saveChanges()`
|
|
487
|
+
- It does not isolate transactions across scopes: each scope has its own context
|
|
488
|
+
- It does not register entities: only those passed to `ormModule({ entities: [...] })`
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## Common mistakes
|
|
493
|
+
|
|
494
|
+
### 1. Forgot `saveChanges()`
|
|
495
|
+
|
|
496
|
+
```ts
|
|
497
|
+
users.add(user);
|
|
498
|
+
// the data is not in the database yet!
|
|
499
|
+
await users.saveChanges();
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### 2. `add()` of the wrong entity
|
|
503
|
+
|
|
504
|
+
```ts
|
|
505
|
+
// ❌ a book added through authorsRepo: the ORM treats it as an Author
|
|
506
|
+
authors.add(book);
|
|
507
|
+
|
|
508
|
+
// ✅ its own repository
|
|
509
|
+
books.add(book);
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### 3. Dependencies of `IRepository<User>` without codegen
|
|
513
|
+
|
|
514
|
+
`di:generate` wires a constructor parameter typed `IRepository<User>` automatically. Explicit deps are needed only when the class is registered without codegen (for example in a hand-built test container):
|
|
515
|
+
|
|
516
|
+
```ts
|
|
517
|
+
scoped(UserService, UserService, [repositoryFor(User)] as const)
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### 4. `ensureCreated` in tests without starting the application
|
|
521
|
+
|
|
522
|
+
`ensureCreated: true` in `ormModule` runs in `OrmLifecycle.start()` (at application start).
|
|
523
|
+
In tests call it by hand:
|
|
524
|
+
|
|
525
|
+
```ts
|
|
526
|
+
await scope.resolve(AppDbContext).database.ensureCreated();
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### 5. Different scopes have different data in the tracker
|
|
530
|
+
|
|
531
|
+
```ts
|
|
532
|
+
const scopeA = container.createScope();
|
|
533
|
+
const scopeB = container.createScope();
|
|
534
|
+
// resolve in scopeA and scopeB gives different DbContext and Repository instances
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
## Relation to `DbContext.setOf()`
|
|
540
|
+
|
|
541
|
+
Inside, Repository calls:
|
|
542
|
+
|
|
543
|
+
```ts
|
|
544
|
+
context.setOf(EntityClass) // → DbSet<T>
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
The `setOf` method was added to `DbContext` exactly for generic access without declaring a `readonly users = this.set(User)` field in the subclass.
|
|
548
|
+
In application code prefer **Repository** over a direct `setOf`.
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
## Mocks and testing without a database
|
|
553
|
+
|
|
554
|
+
Repository is easy to replace in unit tests: the service is tested without PostgreSQL and without a `DbContext`.
|
|
555
|
+
|
|
556
|
+
### Level 1: a mock `IRepository<User>`
|
|
557
|
+
|
|
558
|
+
Testing `UserService` without a database: pass an object with the needed methods:
|
|
559
|
+
|
|
560
|
+
```ts
|
|
561
|
+
import { describe, expect, test } from "bun:test";
|
|
562
|
+
import type { IRepository } from "@/core/orm";
|
|
563
|
+
import { UserService } from "./UserService";
|
|
564
|
+
import { User } from "./User";
|
|
565
|
+
|
|
566
|
+
test("add calls repository add and saveChanges", async () => {
|
|
567
|
+
let saved = false;
|
|
568
|
+
const users: IRepository<User> = {
|
|
569
|
+
get dbSet() { throw new Error("not needed"); },
|
|
570
|
+
query() { throw new Error("not needed"); },
|
|
571
|
+
find: async () => null,
|
|
572
|
+
add: (entity) => entity,
|
|
573
|
+
addRange: () => {},
|
|
574
|
+
update: (entity) => entity,
|
|
575
|
+
remove: (entity) => entity,
|
|
576
|
+
attach: (entity) => entity,
|
|
577
|
+
stateOf: () => "Unchanged" as const,
|
|
578
|
+
saveChanges: async () => { saved = true; return 1; },
|
|
579
|
+
get changeTracker() { throw new Error("not needed"); },
|
|
580
|
+
get database() { throw new Error("not needed"); },
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const service = new UserService(users);
|
|
584
|
+
await service.add("Ann");
|
|
585
|
+
expect(saved).toBe(true);
|
|
586
|
+
});
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
A mock usually needs only the methods the service calls.
|
|
590
|
+
|
|
591
|
+
### Level 2: an in-memory store (without Repository)
|
|
592
|
+
|
|
593
|
+
For **controller** tests it is enough to replace the store contract the controller depends on (here a hypothetical `IUserStore`):
|
|
594
|
+
|
|
595
|
+
```ts
|
|
596
|
+
import { Module, createContainer, DI } from "bazis/core/di";
|
|
597
|
+
import { InMemoryUserStore } from "./InMemoryUserStore";
|
|
598
|
+
import { IUserStore } from "./IUserStore";
|
|
599
|
+
import { UsersController } from "./UsersController";
|
|
600
|
+
|
|
601
|
+
@Module({
|
|
602
|
+
controllers: [UsersController],
|
|
603
|
+
providers: [
|
|
604
|
+
DI.scoped(DI.valueProvider(IUserStore, new InMemoryUserStore())),
|
|
605
|
+
],
|
|
606
|
+
})
|
|
607
|
+
class TestUsersModule {}
|
|
608
|
+
|
|
609
|
+
const container = createContainer(TestUsersModule);
|
|
610
|
+
const scope = container.createScope();
|
|
611
|
+
const controller = scope.resolve(UsersController);
|
|
612
|
+
// call the controller methods or run HTTP e2e over the module
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
The controller and the HTTP layer do not touch the ORM, only the `IUserStore` contract.
|
|
616
|
+
|
|
617
|
+
### Level 3: an integration test with a real Repository
|
|
618
|
+
|
|
619
|
+
As in [orm.repository.test.ts](../../../core/orm/test/orm.repository.test.ts):
|
|
620
|
+
|
|
621
|
+
```ts
|
|
622
|
+
const DataModule = ormModule({
|
|
623
|
+
context: UsersDbContext,
|
|
624
|
+
entities: [User],
|
|
625
|
+
provider: postgres({ url: Bun.env.BAZIS_PG_URL! }),
|
|
626
|
+
healthCheck: false,
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
const container = createContainer(DataModule);
|
|
630
|
+
const scope = container.createScope();
|
|
631
|
+
await scope.resolve(UsersDbContext).database.ensureCreated();
|
|
632
|
+
|
|
633
|
+
const service = new UserService(scope.resolve(repositoryFor(User)));
|
|
634
|
+
await service.add("Integration");
|
|
635
|
+
expect(await service.list(10)).toHaveLength(1);
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
### What to mock in which tests
|
|
639
|
+
|
|
640
|
+
| You test | Mock | Database needed? |
|
|
641
|
+
| --- | --- | --- |
|
|
642
|
+
| `UserService` (domain rules) | `IRepository<User>` | No |
|
|
643
|
+
| `UsersController` (HTTP, @Catch) | the store contract / an in-memory store | No |
|
|
644
|
+
| Repository + ORM (queries, tracking) | — | Yes (PostgreSQL) |
|
|
645
|
+
| Full HTTP e2e | — | Yes (or a test module with an in-memory store) |
|
|
646
|
+
|
|
647
|
+
### Tips
|
|
648
|
+
|
|
649
|
+
1. **Do not mock `query()`** if you test SQL/filters: use PostgreSQL qualification.
|
|
650
|
+
2. **Mock `saveChanges()`** to check that the service saves data at all.
|
|
651
|
+
3. **One scope per test**: `scope.dispose()` in `afterEach`, otherwise scoped services leak.
|
|
652
|
+
4. In hand-built DI test containers (without codegen) with `validateOnBuild: true`, give Repository deps explicitly: `[repositoryFor(User)]`.
|
|
653
|
+
|
|
654
|
+
---
|
|
655
|
+
|
|
656
|
+
## Tests
|
|
657
|
+
|
|
658
|
+
Live examples and scenarios:
|
|
659
|
+
|
|
660
|
+
- ORM Repository: [orm.repository.test.ts](../../../core/orm/test/orm.repository.test.ts)
|
|
661
|
+
|
|
662
|
+
Covered: CRUD, queries, include, `@QueryFilter`, `@SoftDelete`, scoped DI, module encapsulation.
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
666
|
+
## Cheat sheet
|
|
667
|
+
|
|
668
|
+
```ts
|
|
669
|
+
// DI
|
|
670
|
+
constructor(private readonly users: IRepository<User>) {}
|
|
671
|
+
|
|
672
|
+
// Read
|
|
673
|
+
await users.find(id);
|
|
674
|
+
await users.query().where(...).toList();
|
|
675
|
+
|
|
676
|
+
// Write
|
|
677
|
+
users.add(entity);
|
|
678
|
+
users.update(entity);
|
|
679
|
+
users.remove(entity);
|
|
680
|
+
await users.saveChanges();
|
|
681
|
+
|
|
682
|
+
// DI token
|
|
683
|
+
repositoryFor(User)
|
|
684
|
+
```
|