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,102 @@
|
|
|
1
|
+
import type { GeneratedOpenApiMetadata, GeneratedOpenApiOperationMetadata, OpenApiSchema } from "../../../library/openapi";
|
|
2
|
+
|
|
3
|
+
export type GeneratedOpenApiSchemaModel = abstract new (...args: never[]) => object;
|
|
4
|
+
|
|
5
|
+
const EMPTY_GENERATED_OPENAPI_METADATA: GeneratedOpenApiMetadata = Object.freeze({
|
|
6
|
+
schemas: Object.freeze({}),
|
|
7
|
+
operations: Object.freeze({}),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
interface GeneratedOpenApiTargetSlice {
|
|
11
|
+
readonly metadata: GeneratedOpenApiMetadata;
|
|
12
|
+
readonly controllers?: ReadonlySet<abstract new (...args: never[]) => object>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let registered: GeneratedOpenApiTargetSlice[] = [];
|
|
16
|
+
let schemaNamesByModel = new Map<GeneratedOpenApiSchemaModel, string>();
|
|
17
|
+
|
|
18
|
+
export function registerGeneratedOpenApiMetadata(
|
|
19
|
+
metadata: GeneratedOpenApiMetadata,
|
|
20
|
+
controllers?: readonly (abstract new (...args: never[]) => object)[],
|
|
21
|
+
): void {
|
|
22
|
+
if (metadata === EMPTY_GENERATED_OPENAPI_METADATA || registered.some((entry) => entry.metadata === metadata)) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
registered.push({ metadata, ...(controllers === undefined ? {} : { controllers: new Set(controllers) }) });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Binds a real runtime model constructor to the exact generated component
|
|
30
|
+
* schema. This is the nominal bridge used by code-first consumers such as
|
|
31
|
+
* @UiProfile; they must not rediscover schemas through `ctor.name`.
|
|
32
|
+
*/
|
|
33
|
+
export function registerGeneratedOpenApiSchemaModel(
|
|
34
|
+
model: GeneratedOpenApiSchemaModel,
|
|
35
|
+
schemaName: string,
|
|
36
|
+
): void {
|
|
37
|
+
const normalized = schemaName.trim();
|
|
38
|
+
if (normalized.length === 0) {
|
|
39
|
+
throw new TypeError("Generated OpenAPI schema model name must not be empty.");
|
|
40
|
+
}
|
|
41
|
+
const existing = schemaNamesByModel.get(model);
|
|
42
|
+
if (existing !== undefined && existing !== normalized) {
|
|
43
|
+
throw new TypeError(
|
|
44
|
+
`OpenAPI model ${model.name || "<anonymous>"} is already bound to schema ${existing}; ` +
|
|
45
|
+
`cannot also bind it to ${normalized}.`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
schemaNamesByModel.set(model, normalized);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Exact generated component schema name for a runtime model constructor. */
|
|
52
|
+
export function getGeneratedOpenApiSchemaName(
|
|
53
|
+
model: GeneratedOpenApiSchemaModel,
|
|
54
|
+
): string | undefined {
|
|
55
|
+
return schemaNamesByModel.get(model);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function getGeneratedOpenApiMetadata(controllers?: readonly (abstract new (...args: never[]) => object)[]): GeneratedOpenApiMetadata {
|
|
59
|
+
const requested = controllers === undefined ? undefined : new Set(controllers);
|
|
60
|
+
const selected = requested === undefined
|
|
61
|
+
? registered
|
|
62
|
+
: registered.filter((entry) => entry.controllers === undefined || [...requested].some((controller) => entry.controllers?.has(controller) === true));
|
|
63
|
+
if (selected.length === 0) {
|
|
64
|
+
return EMPTY_GENERATED_OPENAPI_METADATA;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const schemas: Record<string, OpenApiSchema> = {};
|
|
68
|
+
const operations: Record<string, Record<string, GeneratedOpenApiOperationMetadata>> = {};
|
|
69
|
+
for (let index = 0; index < selected.length; index += 1) {
|
|
70
|
+
const metadata = selected[index]!.metadata;
|
|
71
|
+
Object.assign(schemas, metadata.schemas);
|
|
72
|
+
for (const [controllerName, methods] of Object.entries(metadata.operations)) {
|
|
73
|
+
operations[controllerName] ??= {};
|
|
74
|
+
Object.assign(operations[controllerName], methods);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return Object.freeze({
|
|
79
|
+
schemas: Object.freeze(schemas),
|
|
80
|
+
operations: Object.freeze(operations),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Internal generated-runtime transaction support. */
|
|
85
|
+
export function snapshotGeneratedOpenApiRegistry(): { readonly registered: readonly GeneratedOpenApiTargetSlice[]; readonly schemaNamesByModel: typeof schemaNamesByModel } {
|
|
86
|
+
return {
|
|
87
|
+
registered: registered.map((entry) => ({
|
|
88
|
+
metadata: entry.metadata,
|
|
89
|
+
...(entry.controllers === undefined ? {} : { controllers: new Set(entry.controllers) }),
|
|
90
|
+
})),
|
|
91
|
+
schemaNamesByModel: new Map(schemaNamesByModel),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Internal generated-runtime transaction support. */
|
|
96
|
+
export function restoreGeneratedOpenApiRegistry(snapshot: ReturnType<typeof snapshotGeneratedOpenApiRegistry>): void {
|
|
97
|
+
registered = snapshot.registered.map((entry) => ({
|
|
98
|
+
metadata: entry.metadata,
|
|
99
|
+
...(entry.controllers === undefined ? {} : { controllers: new Set(entry.controllers) }),
|
|
100
|
+
}));
|
|
101
|
+
schemaNamesByModel = snapshot.schemaNamesByModel;
|
|
102
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Class } from "../../di";
|
|
2
|
+
import { Environment } from "../../kernel";
|
|
3
|
+
import {
|
|
4
|
+
renderOpenApiHtml,
|
|
5
|
+
serveOpenApiDocs,
|
|
6
|
+
type OpenApiDocsOptions,
|
|
7
|
+
type PreparedOpenApiDocs,
|
|
8
|
+
} from "../../../library/openapi";
|
|
9
|
+
import { buildHttpOpenApiDocument } from "./openApiDocument";
|
|
10
|
+
import type { ApiVersioningOptions } from "../options";
|
|
11
|
+
|
|
12
|
+
interface PrepareOpenApiDocsInput {
|
|
13
|
+
readonly option?: OpenApiDocsOptions | boolean;
|
|
14
|
+
readonly controllers: readonly Class<object>[];
|
|
15
|
+
readonly globalPrefix?: string;
|
|
16
|
+
readonly versioning?: ApiVersioningOptions;
|
|
17
|
+
readonly environment?: Environment;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const DEFAULT_UI_PATH = "/docs";
|
|
21
|
+
const DEFAULT_TITLE = "Bazis API";
|
|
22
|
+
const DEFAULT_VERSION = "1.0.0";
|
|
23
|
+
|
|
24
|
+
export { serveOpenApiDocs };
|
|
25
|
+
|
|
26
|
+
export function prepareOpenApiDocs(input: PrepareOpenApiDocsInput): PreparedOpenApiDocs | undefined {
|
|
27
|
+
if (!isEnabled(input.option, input.environment)) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
const options = normalizeOptions(input.option);
|
|
31
|
+
const uiPath = normalizeAbsolutePath(options.path ?? DEFAULT_UI_PATH);
|
|
32
|
+
const specPath = normalizeAbsolutePath(options.specPath ?? joinDocsPath(uiPath, "openapi.json"));
|
|
33
|
+
const title = options.title ?? DEFAULT_TITLE;
|
|
34
|
+
const version = options.version ?? DEFAULT_VERSION;
|
|
35
|
+
const document = buildHttpOpenApiDocument({
|
|
36
|
+
controllers: input.controllers,
|
|
37
|
+
globalPrefix: input.globalPrefix,
|
|
38
|
+
versioning: input.versioning,
|
|
39
|
+
title,
|
|
40
|
+
version,
|
|
41
|
+
});
|
|
42
|
+
const json = JSON.stringify(document, null, 2);
|
|
43
|
+
return {
|
|
44
|
+
uiPath,
|
|
45
|
+
specPath,
|
|
46
|
+
document,
|
|
47
|
+
json,
|
|
48
|
+
html: renderOpenApiHtml(title, specPath),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isEnabled(option: OpenApiDocsOptions | boolean | undefined, environment: Environment | undefined): boolean {
|
|
53
|
+
if (typeof option === "boolean") {
|
|
54
|
+
return option;
|
|
55
|
+
}
|
|
56
|
+
if (option !== undefined) {
|
|
57
|
+
return option.enabled ?? true;
|
|
58
|
+
}
|
|
59
|
+
return environment?.debug ?? false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function normalizeOptions(option: OpenApiDocsOptions | boolean | undefined): OpenApiDocsOptions {
|
|
63
|
+
return typeof option === "object" && option !== null ? option : {};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function normalizeAbsolutePath(path: string): string {
|
|
67
|
+
const trimmed = path.trim();
|
|
68
|
+
const prefixed = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
69
|
+
const parts = prefixed.split("/").filter((part) => part.length > 0);
|
|
70
|
+
return `/${parts.join("/")}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function joinDocsPath(base: string, child: string): string {
|
|
74
|
+
return `${base.replace(/\/+$/, "")}/${child.replace(/^\/+/, "")}`;
|
|
75
|
+
}
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import type { Class } from "../../di";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import {
|
|
4
|
+
buildOpenApiDocument,
|
|
5
|
+
type OpenApiCatalogOperation,
|
|
6
|
+
type OpenApiDocumentBuildInput as LibraryOpenApiDocumentBuildInput,
|
|
7
|
+
type OpenApiParameter,
|
|
8
|
+
type OpenApiSchema,
|
|
9
|
+
} from "../../../library/openapi";
|
|
10
|
+
import { optionsFromSchema } from "../../../library/jsonapi";
|
|
11
|
+
import { resolveAuthorizeMeta } from "../Authorization/metadata";
|
|
12
|
+
import type { ParameterBinding } from "../Binding/bindings";
|
|
13
|
+
import { resolveGeneratedBindings } from "../Binding/autoBindings";
|
|
14
|
+
import { controllerMetaOf, type ActionMeta, type ControllerMeta } from "../Decorators/metadata";
|
|
15
|
+
import { joinPaths } from "../Routing/template";
|
|
16
|
+
import { getGeneratedOpenApiMetadata } from "./generatedOpenApiRegistry";
|
|
17
|
+
import type { ApiVersioningOptions } from "../options";
|
|
18
|
+
import type { GeneratedOpenApiMetadata } from "../../../library/openapi";
|
|
19
|
+
|
|
20
|
+
export interface HttpOpenApiDocumentBuildInput {
|
|
21
|
+
readonly controllers: readonly Class<object>[];
|
|
22
|
+
readonly globalPrefix?: string;
|
|
23
|
+
readonly versioning?: ApiVersioningOptions;
|
|
24
|
+
readonly title: string;
|
|
25
|
+
readonly version: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function buildHttpOpenApiDocument(input: HttpOpenApiDocumentBuildInput): OpenApiSchema {
|
|
29
|
+
const generated = getGeneratedOpenApiMetadata(input.controllers);
|
|
30
|
+
const documentInput: LibraryOpenApiDocumentBuildInput = {
|
|
31
|
+
title: input.title,
|
|
32
|
+
version: input.version,
|
|
33
|
+
schemas: generated.schemas,
|
|
34
|
+
operations: collectOpenApiOperations(input, generated),
|
|
35
|
+
generatedBy: "bazis:di-generate",
|
|
36
|
+
};
|
|
37
|
+
return buildOpenApiDocument(documentInput);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function collectOpenApiOperations(
|
|
41
|
+
input: HttpOpenApiDocumentBuildInput,
|
|
42
|
+
generated: GeneratedOpenApiMetadata,
|
|
43
|
+
): OpenApiCatalogOperation[] {
|
|
44
|
+
const operations: OpenApiCatalogOperation[] = [];
|
|
45
|
+
for (const controllerClass of input.controllers) {
|
|
46
|
+
const meta = controllerMetaOf(controllerClass);
|
|
47
|
+
if (!meta?.isController) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
for (const [methodName, action] of meta.actions) {
|
|
51
|
+
if (action.routes.length === 0) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const bindings = resolveGeneratedBindings(controllerClass, methodName) ?? [];
|
|
55
|
+
for (const route of action.routes) {
|
|
56
|
+
operations.push(createOperation({
|
|
57
|
+
controllerClass,
|
|
58
|
+
methodName,
|
|
59
|
+
meta,
|
|
60
|
+
action,
|
|
61
|
+
bindings,
|
|
62
|
+
httpMethod: route.httpMethod,
|
|
63
|
+
path: fullOpenApiPath(input.globalPrefix, input.versioning?.source, meta, action, route.template),
|
|
64
|
+
routeTemplate: joinPaths(meta.prefix, route.template),
|
|
65
|
+
versioning: input.versioning,
|
|
66
|
+
generated,
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return operations;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface CreateOperationInput {
|
|
75
|
+
readonly controllerClass: Class<object>;
|
|
76
|
+
readonly methodName: string | symbol;
|
|
77
|
+
readonly meta: ControllerMeta;
|
|
78
|
+
readonly action: ActionMeta;
|
|
79
|
+
readonly bindings: readonly ParameterBinding[];
|
|
80
|
+
readonly httpMethod: string;
|
|
81
|
+
readonly path: string;
|
|
82
|
+
readonly routeTemplate: string;
|
|
83
|
+
readonly versioning?: ApiVersioningOptions;
|
|
84
|
+
readonly generated: GeneratedOpenApiMetadata;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function createOperation(input: CreateOperationInput): OpenApiCatalogOperation {
|
|
88
|
+
const methodName = String(input.methodName);
|
|
89
|
+
const version = input.action.version ?? input.meta.version;
|
|
90
|
+
const auth = resolveAuthorizeMeta(input.controllerClass, input.methodName);
|
|
91
|
+
const generatedOperation = input.generated.operations[input.controllerClass.name]?.[methodName];
|
|
92
|
+
const status = String(successStatus(input.httpMethod, input.action));
|
|
93
|
+
const responseSchema = generatedOperation?.response;
|
|
94
|
+
const contentType = input.action.produces ?? "application/json";
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
path: input.path,
|
|
98
|
+
httpMethod: input.httpMethod,
|
|
99
|
+
tag: tagName(input.meta, input.controllerClass),
|
|
100
|
+
operationId: stableHttpOperationId(input.httpMethod, input.path),
|
|
101
|
+
summary: `${normalizedMethod(input.httpMethod)} ${input.path}`,
|
|
102
|
+
parameters: collectParameters(
|
|
103
|
+
input.bindings,
|
|
104
|
+
input.versioning,
|
|
105
|
+
version,
|
|
106
|
+
routeParamSchemas(input.routeTemplate),
|
|
107
|
+
),
|
|
108
|
+
requestBody: createRequestBody(input.bindings, input.action, input.generated),
|
|
109
|
+
responses: {
|
|
110
|
+
[status]: responseSchema && status !== "204"
|
|
111
|
+
? {
|
|
112
|
+
description: "Success",
|
|
113
|
+
content: {
|
|
114
|
+
[contentType]: {
|
|
115
|
+
schema: responseSchema,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
: { description: "Success" },
|
|
120
|
+
},
|
|
121
|
+
authorized: !auth.allowAnonymous && auth.authorize !== undefined,
|
|
122
|
+
...(version !== undefined ? { versions: [version] } : {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Stable transport identity owned by HTTP/OpenAPI. Controller and handler
|
|
128
|
+
* names are intentionally excluded: renaming implementation symbols must not
|
|
129
|
+
* invalidate compiled UI references or generated clients.
|
|
130
|
+
*/
|
|
131
|
+
function stableHttpOperationId(httpMethod: string, canonicalPath: string): string {
|
|
132
|
+
const method = normalizedMethod(httpMethod).toLowerCase();
|
|
133
|
+
const slug = readableOperationPath(canonicalPath);
|
|
134
|
+
const digest = createHash("sha256")
|
|
135
|
+
.update(JSON.stringify([method, canonicalPath]))
|
|
136
|
+
.digest("hex")
|
|
137
|
+
.slice(0, 32);
|
|
138
|
+
return `http_${method}_${slug}__${digest}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function readableOperationPath(path: string): string {
|
|
142
|
+
const slug = path
|
|
143
|
+
.replace(/\{([^{}]+)\}/g, " by $1 ")
|
|
144
|
+
.toLowerCase()
|
|
145
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
146
|
+
.replace(/^_+|_+$/g, "")
|
|
147
|
+
.slice(0, 80)
|
|
148
|
+
.replace(/_+$/g, "");
|
|
149
|
+
return slug.length > 0 ? slug : "root";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function collectParameters(
|
|
153
|
+
bindings: readonly ParameterBinding[],
|
|
154
|
+
versioning: ApiVersioningOptions | undefined,
|
|
155
|
+
version: string | undefined,
|
|
156
|
+
routeSchemas: Readonly<Record<string, OpenApiSchema>>,
|
|
157
|
+
): OpenApiParameter[] {
|
|
158
|
+
const parameters: OpenApiParameter[] = [];
|
|
159
|
+
for (const binding of bindings) {
|
|
160
|
+
switch (binding.source) {
|
|
161
|
+
case "route":
|
|
162
|
+
if (binding.name) {
|
|
163
|
+
parameters.push({
|
|
164
|
+
name: binding.name,
|
|
165
|
+
in: "path",
|
|
166
|
+
required: true,
|
|
167
|
+
schema: routeSchemas[binding.name] ?? schemaForValueType(binding.type),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
case "query":
|
|
172
|
+
if (binding.name) {
|
|
173
|
+
parameters.push({
|
|
174
|
+
name: binding.name,
|
|
175
|
+
in: "query",
|
|
176
|
+
required: binding.optional !== true && binding.defaultValue === undefined,
|
|
177
|
+
schema: schemaForValueType(binding.type),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
case "list":
|
|
182
|
+
parameters.push(...listParameters(binding));
|
|
183
|
+
break;
|
|
184
|
+
default:
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (version !== undefined && versioning !== undefined && versioning.source !== "url") {
|
|
189
|
+
const name = versioning.source === "query" ? (versioning.parameterName ?? "api-version") : (versioning.headerName ?? "x-api-version");
|
|
190
|
+
parameters.push({
|
|
191
|
+
name,
|
|
192
|
+
in: versioning.source,
|
|
193
|
+
required: versioning.defaultVersion === undefined,
|
|
194
|
+
schema: { type: "string", enum: [version] },
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
return parameters;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function listParameters(binding: ParameterBinding): OpenApiParameter[] {
|
|
201
|
+
const options = binding.listOptions ?? (binding.model ? optionsFromSchema(binding.model) : {});
|
|
202
|
+
const parameters: OpenApiParameter[] = [
|
|
203
|
+
{
|
|
204
|
+
name: "sort", in: "query", required: false, schema: { type: "string" },
|
|
205
|
+
"x-bazis-sort-fields": [...(options.sort ?? [])],
|
|
206
|
+
},
|
|
207
|
+
{ name: "page[number]", in: "query", required: false, schema: { type: "integer", minimum: 1 } },
|
|
208
|
+
{
|
|
209
|
+
name: "page[size]",
|
|
210
|
+
in: "query",
|
|
211
|
+
required: false,
|
|
212
|
+
schema: pageSizeSchema(options.page?.maxSize),
|
|
213
|
+
},
|
|
214
|
+
];
|
|
215
|
+
if (options.include && options.include.length > 0) {
|
|
216
|
+
parameters.push({ name: "include", in: "query", required: false, schema: { type: "string", enum: options.include } });
|
|
217
|
+
}
|
|
218
|
+
for (const [field, operators] of Object.entries(options.filter ?? {})) {
|
|
219
|
+
for (const operator of operators) {
|
|
220
|
+
parameters.push({
|
|
221
|
+
name: `filter[${field}][${operator}]`,
|
|
222
|
+
in: "query",
|
|
223
|
+
required: false,
|
|
224
|
+
schema: { type: "string" },
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return parameters;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function pageSizeSchema(maximum: number | undefined): OpenApiSchema {
|
|
232
|
+
return maximum === undefined ? { type: "integer", minimum: 1 } : { type: "integer", minimum: 1, maximum };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function createRequestBody(
|
|
236
|
+
bindings: readonly ParameterBinding[],
|
|
237
|
+
action: ActionMeta,
|
|
238
|
+
generated: GeneratedOpenApiMetadata,
|
|
239
|
+
): OpenApiSchema | undefined {
|
|
240
|
+
const body = bindings.find((binding) => binding.source === "body");
|
|
241
|
+
if (!body) {
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
const schema = body.model ? schemaForModel(body.model.name, generated) : { type: "object" };
|
|
245
|
+
return {
|
|
246
|
+
required: body.optional !== true,
|
|
247
|
+
content: {
|
|
248
|
+
[action.consumes ?? "application/json"]: {
|
|
249
|
+
schema,
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function schemaForModel(name: string, generated: GeneratedOpenApiMetadata): OpenApiSchema {
|
|
256
|
+
return generated.schemas[name] !== undefined
|
|
257
|
+
? { $ref: `#/components/schemas/${name}` }
|
|
258
|
+
: { type: "object", "x-bazis-model": name };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function schemaForValueType(type: ParameterBinding["type"]): OpenApiSchema {
|
|
262
|
+
switch (type) {
|
|
263
|
+
case "int":
|
|
264
|
+
return { type: "integer" };
|
|
265
|
+
case "number":
|
|
266
|
+
return { type: "number" };
|
|
267
|
+
case "bool":
|
|
268
|
+
return { type: "boolean" };
|
|
269
|
+
case "string":
|
|
270
|
+
case undefined:
|
|
271
|
+
return { type: "string" };
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function routeParamSchemas(template: string): Record<string, OpenApiSchema> {
|
|
276
|
+
const schemas: Record<string, OpenApiSchema> = {};
|
|
277
|
+
for (const segment of template.split("/")) {
|
|
278
|
+
const param = /^:([A-Za-z_][A-Za-z0-9_]*)(?:\(([a-z]+)\))?$/.exec(segment);
|
|
279
|
+
if (param) {
|
|
280
|
+
schemas[param[1] as string] = schemaForConstraint(param[2]);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const wildcard = /^\*([A-Za-z_][A-Za-z0-9_]*)?$/.exec(segment);
|
|
284
|
+
if (wildcard) {
|
|
285
|
+
schemas[(wildcard[1] as string | undefined) ?? "rest"] = { type: "string" };
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return schemas;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function schemaForConstraint(constraint: string | undefined): OpenApiSchema {
|
|
292
|
+
switch (constraint) {
|
|
293
|
+
case "int":
|
|
294
|
+
return { type: "integer" };
|
|
295
|
+
case "number":
|
|
296
|
+
return { type: "number" };
|
|
297
|
+
case "bool":
|
|
298
|
+
return { type: "boolean" };
|
|
299
|
+
case "uuid":
|
|
300
|
+
return { type: "string", format: "uuid" };
|
|
301
|
+
case "alpha":
|
|
302
|
+
return { type: "string", pattern: "^[A-Za-z]+$" };
|
|
303
|
+
default:
|
|
304
|
+
return { type: "string" };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function successStatus(method: string, action: ActionMeta): number {
|
|
309
|
+
if (action.httpCode !== undefined) {
|
|
310
|
+
return action.httpCode;
|
|
311
|
+
}
|
|
312
|
+
return method === "DELETE" ? 204 : 200;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function tagName(meta: ControllerMeta, controllerClass: Class<object>): string {
|
|
316
|
+
const firstPrefix = meta.prefix?.split("/").find((part) => part.length > 0);
|
|
317
|
+
return firstPrefix ?? controllerClass.name.replace(/Controller$/, "");
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function fullOpenApiPath(
|
|
321
|
+
globalPrefix: string | undefined,
|
|
322
|
+
versionSource: "url" | "query" | "header" | undefined,
|
|
323
|
+
meta: ControllerMeta,
|
|
324
|
+
action: ActionMeta,
|
|
325
|
+
template: string,
|
|
326
|
+
): string {
|
|
327
|
+
const version = action.version ?? meta.version;
|
|
328
|
+
const versionSegment = (versionSource === undefined || versionSource === "url") && version !== undefined ? `v${version}` : undefined;
|
|
329
|
+
const joined = joinPaths(globalPrefix, versionSegment, meta.prefix, template);
|
|
330
|
+
return `/${templateToOpenApiPath(joined)}`;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function templateToOpenApiPath(template: string): string {
|
|
334
|
+
if (template === "") {
|
|
335
|
+
return "";
|
|
336
|
+
}
|
|
337
|
+
return template
|
|
338
|
+
.split("/")
|
|
339
|
+
.filter((segment) => segment.length > 0)
|
|
340
|
+
.map((segment) => {
|
|
341
|
+
if (segment.startsWith(":")) {
|
|
342
|
+
const match = /^:([A-Za-z_][A-Za-z0-9_]*)/.exec(segment);
|
|
343
|
+
return match ? `{${match[1]}}` : segment;
|
|
344
|
+
}
|
|
345
|
+
if (segment.startsWith("*")) {
|
|
346
|
+
const name = segment.length > 1 ? segment.slice(1) : "rest";
|
|
347
|
+
return `{${name}}`;
|
|
348
|
+
}
|
|
349
|
+
return segment;
|
|
350
|
+
})
|
|
351
|
+
.join("/");
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function normalizedMethod(method: string): string {
|
|
355
|
+
return method === "*" ? "ANY" : method;
|
|
356
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# src/bazis/core/http: HTTP module (in the spirit of ASP.NET Core)
|
|
2
|
+
|
|
3
|
+
Controllers are classes with standard TC39 decorators; routes are compiled into a
|
|
4
|
+
radix tree at startup; each request lives in its own DI scope.
|
|
5
|
+
No external dependencies, no reflection, compatible with `bun build --compile`
|
|
6
|
+
(checked by the shared `bun run build:bin`).
|
|
7
|
+
|
|
8
|
+
The full specification of middleware and `@Middleware`: [SPEC.md](./SPEC.md).
|
|
9
|
+
|
|
10
|
+
## Quick start
|
|
11
|
+
|
|
12
|
+
First create an application by the [guide](../../../../docs/QUICKSTART.md), then
|
|
13
|
+
the module `bunx bazis g module Echo --empty`. To try it without a database, replace
|
|
14
|
+
the content of the generated `Echo.module.ts` with this code and fill in its `MODULE.md`:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { Module } from "bazis/core/di";
|
|
18
|
+
import { Controller, Get, Post, RequestModel } from "bazis/core/http";
|
|
19
|
+
import { Validator } from "bazis/library/validation";
|
|
20
|
+
|
|
21
|
+
@RequestModel()
|
|
22
|
+
export class EchoRequest {
|
|
23
|
+
@Validator({ required: true, type: "string", minLength: 3, maxLength: 100 })
|
|
24
|
+
text!: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Controller("echo")
|
|
28
|
+
export class EchoController {
|
|
29
|
+
@Get(":id(int)")
|
|
30
|
+
getById(id: number) { return { id }; }
|
|
31
|
+
|
|
32
|
+
@Post()
|
|
33
|
+
send(body: EchoRequest) { return { text: body.text }; }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Module({ controllers: [EchoController], exports: [] })
|
|
37
|
+
export class EchoModule {}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`bun run dev` runs codegen before the application. GET `/echo/42` returns
|
|
41
|
+
`{"id":42}`; POST `/echo` with `{"text":"hello"}` returns `{"text":"hello"}`.
|
|
42
|
+
An invalid body gets HTTP 400. If the host sets a prefix, add it to the path.
|
|
43
|
+
Parameter bindings are inferred from the signature before the run; TypeScript is
|
|
44
|
+
not analyzed on each request. For service dependencies a constructor and
|
|
45
|
+
`scoped(IService, Service)` are enough: the same codegen infers the usual `deps`.
|
|
46
|
+
|
|
47
|
+
## Binding conventions (instead of parameter decorators)
|
|
48
|
+
|
|
49
|
+
**Parameter** decorators (`getUser(@Param("id") id)`) do not exist in the TC39
|
|
50
|
+
standard, and `experimentalDecorators` would break the other framework modules.
|
|
51
|
+
Instead there are ASP.NET-style conventions that codegen infers from the signature:
|
|
52
|
+
|
|
53
|
+
| Method parameter | Binding |
|
|
54
|
+
|---|---|
|
|
55
|
+
| the name matches the route `:name` | the route value (converted by the constraint/type) |
|
|
56
|
+
| a DTO class from the application sources | the request body + `@Validator` validation (errors -> 400) |
|
|
57
|
+
| `HttpContext` / `Request` / `ResponseBuilder` | context / raw request / response builder |
|
|
58
|
+
| a primitive (`string`/`number`/`boolean` or inferred from the default) | a query parameter; `?` and a default -> optional |
|
|
59
|
+
|
|
60
|
+
If no convention can be inferred (for example a union of string literals, no
|
|
61
|
+
annotation or two body classes), `di:generate` fails with
|
|
62
|
+
`BAZIS_HTTP_BINDING_UNRESOLVED`, the method name and the reason. The previous
|
|
63
|
+
generated files are not replaced. Use a supported type; for headers and raw
|
|
64
|
+
bodies take `ctx: HttpContext`, and inject services into the constructor.
|
|
65
|
+
Checking allowed values belongs to the Validator or the service.
|
|
66
|
+
|
|
67
|
+
A `ctx: HttpContext` parameter stays a regular supported input. For older manual
|
|
68
|
+
HTTP assembly without generated metadata there is a runtime fallback with one
|
|
69
|
+
HttpContext; it does not replace codegen for typed actions.
|
|
70
|
+
|
|
71
|
+
### Nested request DTOs
|
|
72
|
+
|
|
73
|
+
`di:generate` also reads the field types of a request model and registers the shape
|
|
74
|
+
for recursive hydration without `reflect-metadata`. So the usual DX works for
|
|
75
|
+
objects and arrays too:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
export class AddressRequest {
|
|
79
|
+
@Validator({ required: true })
|
|
80
|
+
city!: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export class CreateUserRequest {
|
|
84
|
+
@Validator({ required: true, nested: true })
|
|
85
|
+
address!: AddressRequest;
|
|
86
|
+
|
|
87
|
+
@Validator({ nested: true })
|
|
88
|
+
previousAddresses!: AddressRequest[];
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Both classes must be named top-level exports. The binder creates real instances
|
|
93
|
+
of nested DTOs, removes unknown and prototype-polluting fields at every level,
|
|
94
|
+
then runs validation with paths like `address.city` and
|
|
95
|
+
`previousAddresses[0].city`. An ambiguous type with `nested: true` (for example a
|
|
96
|
+
union of two classes) stops codegen; with missing or stale generated metadata the
|
|
97
|
+
standard validator rejects a plain object fail-closed.
|
|
98
|
+
|
|
99
|
+
A request body field declared exactly as `string`, `number` or `boolean`
|
|
100
|
+
(also `| null`, optional or an array of such values) is checked against the
|
|
101
|
+
JSON type without `@Validator`: `{"done":"yes"}` for `done?: boolean` gives 400
|
|
102
|
+
with the code `type`. Codegen takes the type from the sources; literal unions, enums,
|
|
103
|
+
`Date` and mixed unions are not checked; they need `@Validator`.
|
|
104
|
+
Required-ness is still set with `required: true`. The check applies only at the
|
|
105
|
+
HTTP boundary: gRPC and Agent use the same binder without it.
|
|
106
|
+
|
|
107
|
+
Open JSON fields (`Record<string, unknown>`, `{}`, `unknown[]`) keep regular user
|
|
108
|
+
keys, but the binder clones them recursively and removes `__proto__`,
|
|
109
|
+
`constructor`, `prototype` at any depth. Cycles, nesting deeper than 64 containers
|
|
110
|
+
and overly complex graphs are rejected with 400 before reaching the action.
|
|
111
|
+
|
|
112
|
+
## Folder map
|
|
113
|
+
|
|
114
|
+
| Folder/file | Purpose |
|
|
115
|
+
|---|---|
|
|
116
|
+
| `Decorators/` | `@Controller`, `@Get`/`@Post`/... (+ inline options `{ code, produces, consumes, version, middleware }`), `@HttpCode`, `@Produces`, `@Consumes`, `@Middleware`, `@ApiVersion`, `@Catch`, `@ActionFilter`; metadata stored through `Symbol.metadata` |
|
|
117
|
+
| `Routing/` | template parser (`:id(int)`, `:u(uuid)`, `*rest`), a radix tree with backtracking, pipeline assembly at startup |
|
|
118
|
+
| `Binding/` | binding descriptors, type conversion (400 on error), model binding with prototype pollution protection and `@Validator` validation |
|
|
119
|
+
| `Results/` | `Ok`, `Created`, `NotFound`, `Redirect`, `File` (through `Bun.file`) and result normalization with an automatic Content-Type |
|
|
120
|
+
| `Middleware/` | the pipeline, `cors` (+ preflight), `errorHandler`, `rateLimit`; the access log is `@/logging/http` |
|
|
121
|
+
| `Versioning/` | reads the API version from a URL segment, a query parameter or a header |
|
|
122
|
+
| `HttpContext/` | the request context (params, DI scope, body cache) and `ResponseBuilder` |
|
|
123
|
+
| `Errors/` | `HttpError` and subclasses (4xx/5xx), `ModelValidationError`, `HttpSetupError` |
|
|
124
|
+
| `HttpServer.ts` | hosted service: `Bun.serve`, a scope per request, 404/405/preflight without the pipeline |
|
|
125
|
+
| `httpModule.ts` | the `BazisModule` factory: scoped controllers + the server |
|
|
126
|
+
|
|
127
|
+
## Guarantees
|
|
128
|
+
|
|
129
|
+
- **Performance**: all metadata is processed at startup; per request there is a
|
|
130
|
+
radix lookup O(segments), a compiled pipeline and a DI scope; the body is parsed
|
|
131
|
+
once (cached in the context).
|
|
132
|
+
- **Resilience**: a global error boundary is always installed
|
|
133
|
+
(`HttpError` -> status, anything else -> 500 without details in production);
|
|
134
|
+
`@Catch` on a controller for domain errors; broken JSON/conversions -> 400, not 500.
|
|
135
|
+
- **Security**: prototype pollution is filtered in model binding and in route
|
|
136
|
+
parameter names; `..`/`%zz` in the path -> 400; CORS with preflight; auth hooks
|
|
137
|
+
in as regular middleware (`@Middleware` or global); `rateLimit` is one line.
|
|
138
|
+
- **Configuration errors** (a duplicate route, an unknown constraint, a class
|
|
139
|
+
without `@Controller`) are `HttpSetupError` at startup, not at runtime.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An action result: a recipe for building the final `Response`.
|
|
3
|
+
* Returned by the fluent helpers (`Ok`, `Created`, `NotFound`, ...).
|
|
4
|
+
*/
|
|
5
|
+
export class HttpResult {
|
|
6
|
+
constructor(
|
|
7
|
+
readonly status: number,
|
|
8
|
+
/** Body value: object/array -> JSON, string -> text, Blob/bytes -> as-is, undefined -> empty. */
|
|
9
|
+
readonly body?: unknown,
|
|
10
|
+
readonly headers?: readonly [string, string][],
|
|
11
|
+
/** Explicit Content-Type (overrides automatic negotiation). */
|
|
12
|
+
readonly contentType?: string,
|
|
13
|
+
) {}
|
|
14
|
+
|
|
15
|
+
/** Returns a copy with an extra header. */
|
|
16
|
+
withHeader(name: string, value: string): HttpResult {
|
|
17
|
+
return new HttpResult(this.status, this.body, [...(this.headers ?? []), [name, value]], this.contentType);
|
|
18
|
+
}
|
|
19
|
+
}
|