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,204 @@
|
|
|
1
|
+
import { createServer, createSecureServer, type Http2Server, type Http2SecureServer, type ServerHttp2Session, type ServerHttp2Stream, type IncomingHttpHeaders } from "node:http2";
|
|
2
|
+
import type { AddressInfo } from "node:net";
|
|
3
|
+
import type { HostedService, ModuleOwnedProviderContribution, ServiceProvider } from "../di";
|
|
4
|
+
import type { GrpcControllerDefinition, GrpcModuleOptions } from "./contracts";
|
|
5
|
+
import { ServerCredentials } from "./ServerCredentials";
|
|
6
|
+
import { GrpcStatus } from "./GrpcStatus";
|
|
7
|
+
import { GrpcCall } from "./GrpcCall";
|
|
8
|
+
import { GrpcTransport } from "./GrpcTransport";
|
|
9
|
+
import { GrpcError } from "./GrpcError";
|
|
10
|
+
import type { MethodDefinition } from "./serviceDefinition";
|
|
11
|
+
import { modelValidatorAdapter } from "../../library/validation";
|
|
12
|
+
import { getGrpcRequestBinding } from "./GrpcBinding.contract";
|
|
13
|
+
import { GrpcRequestBinding } from "./GrpcRequest.binding";
|
|
14
|
+
|
|
15
|
+
type Contribution = ModuleOwnedProviderContribution<GrpcControllerDefinition, object>;
|
|
16
|
+
|
|
17
|
+
/** Kernel-managed gRPC server built exclusively on the runtime's HTTP/2 API. */
|
|
18
|
+
export class GrpcServer implements HostedService {
|
|
19
|
+
readonly phase: number;
|
|
20
|
+
private readonly server: Http2Server | Http2SecureServer;
|
|
21
|
+
private readonly hostname: string;
|
|
22
|
+
private readonly listenPort: number;
|
|
23
|
+
private readonly receiveLimit: number;
|
|
24
|
+
private readonly sendLimit: number;
|
|
25
|
+
private readonly maxConcurrentCalls: number;
|
|
26
|
+
private readonly shutdownTimeoutMs: number;
|
|
27
|
+
private readonly routes = new Map<string, { entry: Contribution; method: MethodDefinition; name: string | symbol; binding?: GrpcRequestBinding }>();
|
|
28
|
+
private readonly sessions = new Set<ServerHttp2Session>();
|
|
29
|
+
private readonly active = new Map<GrpcCall, Promise<void>>();
|
|
30
|
+
private state: "idle" | "starting" | "running" | "stopping" | "stopped" = "idle";
|
|
31
|
+
private starting?: Promise<void>;
|
|
32
|
+
private abortStart?: (error: unknown) => void;
|
|
33
|
+
private stopping?: Promise<void>;
|
|
34
|
+
private boundPort?: number;
|
|
35
|
+
|
|
36
|
+
constructor(options: GrpcModuleOptions, private readonly provider: ServiceProvider, controllers: readonly Contribution[]) {
|
|
37
|
+
const address = options.address ?? "127.0.0.1:50051";
|
|
38
|
+
const match = typeof address === "string" && /^(?:\[([^\]]+)\]|([^:[\]\s]+)):(\d{1,5})$/.exec(address);
|
|
39
|
+
if (!match || Number(match[3]) > 65535 || options.address === null) throw new TypeError("gRPC address must be host:port or [IPv6]:port.");
|
|
40
|
+
this.hostname = match[1] ?? match[2]!;
|
|
41
|
+
this.listenPort = Number(match[3]);
|
|
42
|
+
this.phase = options.phase === undefined ? 10 : options.phase;
|
|
43
|
+
if (!Number.isSafeInteger(this.phase)) throw new TypeError("gRPC phase must be a safe integer.");
|
|
44
|
+
this.maxConcurrentCalls = positiveInteger(options.maxConcurrentCalls, 1024, "maxConcurrentCalls");
|
|
45
|
+
this.shutdownTimeoutMs = positiveInteger(options.shutdownTimeoutMs, 5000, "shutdownTimeoutMs");
|
|
46
|
+
if (this.shutdownTimeoutMs > 2_147_483_647) throw new TypeError("gRPC shutdownTimeoutMs exceeds the timer range.");
|
|
47
|
+
const settings = options.serverOptions;
|
|
48
|
+
if (settings !== undefined && (!settings || typeof settings !== "object" || Array.isArray(settings))) throw new TypeError("Invalid gRPC serverOptions.");
|
|
49
|
+
const allowed = new Set(["grpc.max_receive_message_length", "grpc.max_send_message_length", "grpc.max_concurrent_streams"]);
|
|
50
|
+
for (const key of Object.keys(settings ?? {})) {
|
|
51
|
+
if (!allowed.has(key)) throw new TypeError("Unsupported gRPC server option: " + key);
|
|
52
|
+
}
|
|
53
|
+
this.receiveLimit = messageLimit(settings?.["grpc.max_receive_message_length"]);
|
|
54
|
+
this.sendLimit = messageLimit(settings?.["grpc.max_send_message_length"]);
|
|
55
|
+
const maxStreams = positiveInteger(settings?.["grpc.max_concurrent_streams"], 1024, "max_concurrent_streams");
|
|
56
|
+
if (maxStreams > 0xffffffff) throw new TypeError("gRPC max_concurrent_streams exceeds HTTP/2 range.");
|
|
57
|
+
const credentials = options.credentials === undefined ? ServerCredentials.createInsecure() : options.credentials;
|
|
58
|
+
if (!(credentials instanceof ServerCredentials)) throw new TypeError("gRPC credentials must be Bazis ServerCredentials.");
|
|
59
|
+
const validator = options.validator === undefined ? modelValidatorAdapter : options.validator;
|
|
60
|
+
if (!validator || typeof validator.validate !== "function") throw new TypeError("gRPC validator must implement ModelValidator.");
|
|
61
|
+
for (const entry of controllers) {
|
|
62
|
+
for (const [key, name] of entry.payload.methods) {
|
|
63
|
+
const method = entry.payload.service[key]!;
|
|
64
|
+
if (this.routes.has(method.path)) throw new TypeError("Duplicate gRPC method: " + method.path + ".");
|
|
65
|
+
const explicit = entry.payload.requestModels?.get(name);
|
|
66
|
+
const generated = getGrpcRequestBinding(entry.payload.controller, name);
|
|
67
|
+
if (!explicit && generated && generated.requestStream !== method.requestStream) {
|
|
68
|
+
throw new TypeError(`gRPC request stream/DTO mismatch: ${method.path}.`);
|
|
69
|
+
}
|
|
70
|
+
const model = explicit ?? generated?.model;
|
|
71
|
+
this.routes.set(method.path, { entry, method, name, binding: model ? new GrpcRequestBinding(model, validator) : undefined });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const tls = credentials.http2Options();
|
|
75
|
+
const http2 = { settings: { maxConcurrentStreams: maxStreams, maxHeaderListSize: 8192 }, maxHeaderListPairs: 128 };
|
|
76
|
+
this.server = tls ? createSecureServer({ ...tls, ...http2 }) : createServer(http2);
|
|
77
|
+
this.server.on("error", () => {}); // start() observes bind failures; peer/session failures never crash the process.
|
|
78
|
+
this.server.on("session", (session) => {
|
|
79
|
+
this.sessions.add(session);
|
|
80
|
+
session.on("error", () => {});
|
|
81
|
+
session.once("close", () => this.sessions.delete(session));
|
|
82
|
+
if (this.state === "stopping" || this.state === "stopped") session.destroy();
|
|
83
|
+
});
|
|
84
|
+
this.server.on("stream", (stream, headers) => this.accept(stream as ServerHttp2Stream, headers));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
get port(): number | undefined { return this.boundPort; }
|
|
88
|
+
get activeCalls(): number { return this.active.size; }
|
|
89
|
+
|
|
90
|
+
private accept(stream: ServerHttp2Stream, headers: IncomingHttpHeaders): void {
|
|
91
|
+
stream.on("error", () => {});
|
|
92
|
+
stream.once("finish", () => { if (!stream.readableEnded && !stream.closed) stream.close(); });
|
|
93
|
+
if (!/^application\/grpc(?:\+proto)?(?:;|$)/.test(String(headers["content-type"]))) {
|
|
94
|
+
stream.respond({ ":status": 415 }, { endStream: true }); return;
|
|
95
|
+
}
|
|
96
|
+
const reject = (code: GrpcStatus, message: string): void => {
|
|
97
|
+
if (!stream.destroyed) stream.respond({
|
|
98
|
+
":status": 200, "content-type": "application/grpc+proto", "grpc-accept-encoding": "identity",
|
|
99
|
+
"grpc-status": String(code), "grpc-message": encodeURIComponent(message),
|
|
100
|
+
}, { endStream: true });
|
|
101
|
+
};
|
|
102
|
+
if (headers[":method"] !== "POST") { reject(GrpcStatus.UNIMPLEMENTED, "gRPC requires POST."); return; }
|
|
103
|
+
if (this.state !== "running") { reject(GrpcStatus.UNAVAILABLE, "Server is stopping."); return; }
|
|
104
|
+
const route = this.routes.get(String(headers[":path"]));
|
|
105
|
+
if (!route) { reject(GrpcStatus.UNIMPLEMENTED, "Unknown gRPC method."); return; }
|
|
106
|
+
if (this.active.size >= this.maxConcurrentCalls) { reject(GrpcStatus.RESOURCE_EXHAUSTED, "Too many concurrent RPCs."); return; }
|
|
107
|
+
if (headers["grpc-encoding"] !== undefined && headers["grpc-encoding"] !== "identity") {
|
|
108
|
+
reject(GrpcStatus.UNIMPLEMENTED, "Compressed gRPC messages are not supported."); return;
|
|
109
|
+
}
|
|
110
|
+
let transport: GrpcTransport;
|
|
111
|
+
try { transport = new GrpcTransport(stream, headers, route.method, this.receiveLimit, this.sendLimit); }
|
|
112
|
+
catch (error) {
|
|
113
|
+
reject(error instanceof GrpcError ? error.code : GrpcStatus.INVALID_ARGUMENT, error instanceof GrpcError ? error.message : "Invalid gRPC headers.");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const execution = new GrpcCall(transport, route.method, route.name, route.entry.activation, this.provider, route.binding);
|
|
117
|
+
const pending = execution.run().catch((error: unknown) => transport.fail(error))
|
|
118
|
+
.finally(() => this.active.delete(execution));
|
|
119
|
+
this.active.set(execution, pending);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
start(signal?: AbortSignal): Promise<void> {
|
|
123
|
+
if (this.state === "running") return Promise.resolve();
|
|
124
|
+
if (this.state === "starting") return this.starting!;
|
|
125
|
+
if (this.state !== "idle") return Promise.reject(new Error("A stopped gRPC server cannot be restarted."));
|
|
126
|
+
if (signal?.aborted) { this.state = "stopped"; return Promise.reject(signal.reason); }
|
|
127
|
+
this.state = "starting";
|
|
128
|
+
this.starting = new Promise<void>((resolve, reject) => {
|
|
129
|
+
let settled = false;
|
|
130
|
+
const cleanup = (): void => {
|
|
131
|
+
signal?.removeEventListener("abort", abort);
|
|
132
|
+
this.server.off("error", fail);
|
|
133
|
+
this.abortStart = undefined;
|
|
134
|
+
};
|
|
135
|
+
const fail = (error: unknown): void => {
|
|
136
|
+
if (settled) return;
|
|
137
|
+
settled = true;
|
|
138
|
+
cleanup(); this.state = "stopped"; this.forceClose(); reject(error);
|
|
139
|
+
};
|
|
140
|
+
const abort = (): void => fail(signal?.reason ?? new Error("gRPC startup cancelled."));
|
|
141
|
+
this.abortStart = fail;
|
|
142
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
143
|
+
this.server.once("error", fail);
|
|
144
|
+
try {
|
|
145
|
+
this.server.listen(this.listenPort, this.hostname, () => {
|
|
146
|
+
cleanup();
|
|
147
|
+
if (this.state !== "starting") { this.forceClose(); reject(new Error("gRPC startup cancelled.")); return; }
|
|
148
|
+
settled = true;
|
|
149
|
+
this.boundPort = (this.server.address() as AddressInfo).port;
|
|
150
|
+
this.state = "running"; resolve();
|
|
151
|
+
});
|
|
152
|
+
} catch (error) { fail(error); }
|
|
153
|
+
});
|
|
154
|
+
return this.starting;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
stop(): Promise<void> {
|
|
158
|
+
if (this.stopping) return this.stopping;
|
|
159
|
+
this.stopping = this.stopServer();
|
|
160
|
+
return this.stopping;
|
|
161
|
+
}
|
|
162
|
+
private async stopServer(): Promise<void> {
|
|
163
|
+
if (this.state === "idle" || this.state === "stopped") { this.state = "stopped"; return; }
|
|
164
|
+
if (this.state === "starting") {
|
|
165
|
+
this.abortStart?.(new Error("gRPC startup cancelled by stop."));
|
|
166
|
+
await this.starting?.catch(() => {});
|
|
167
|
+
}
|
|
168
|
+
this.state = "stopping";
|
|
169
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
170
|
+
try {
|
|
171
|
+
const closed = new Promise<void>((resolve) => {
|
|
172
|
+
if (!this.server.listening) { resolve(); return; }
|
|
173
|
+
this.server.close(() => resolve());
|
|
174
|
+
});
|
|
175
|
+
for (const session of this.sessions) session.close();
|
|
176
|
+
const expired = new Promise<never>((_resolve, reject) => {
|
|
177
|
+
timeout = setTimeout(() => {
|
|
178
|
+
for (const execution of this.active.keys()) execution.cancel();
|
|
179
|
+
this.forceClose();
|
|
180
|
+
reject(new Error("gRPC shutdown timed out; transport forced closed."));
|
|
181
|
+
}, this.shutdownTimeoutMs);
|
|
182
|
+
});
|
|
183
|
+
await Promise.race([Promise.all([closed, ...this.active.values()]), expired]);
|
|
184
|
+
} finally {
|
|
185
|
+
if (timeout) clearTimeout(timeout);
|
|
186
|
+
this.forceClose(); this.boundPort = undefined; this.state = "stopped";
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
private forceClose(): void {
|
|
190
|
+
for (const session of this.sessions) session.destroy();
|
|
191
|
+
if (this.server.listening) this.server.close();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function positiveInteger(value: number | undefined, fallback: number, name: string): number {
|
|
196
|
+
const result = value === undefined ? fallback : value;
|
|
197
|
+
if (!Number.isSafeInteger(result) || result <= 0) throw new TypeError("gRPC " + name + " must be a positive safe integer.");
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
function messageLimit(value: number | undefined): number {
|
|
201
|
+
const result = value === undefined ? 4 * 1024 * 1024 : value;
|
|
202
|
+
if (!Number.isInteger(result) || result < 0 || result > 0x7fffffff) throw new TypeError("gRPC message limit must be 0..2147483647 bytes.");
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Standard gRPC status numbers, independent of any client/server library. */
|
|
2
|
+
export enum GrpcStatus {
|
|
3
|
+
OK = 0, CANCELLED = 1, UNKNOWN = 2, INVALID_ARGUMENT = 3,
|
|
4
|
+
DEADLINE_EXCEEDED = 4, NOT_FOUND = 5, ALREADY_EXISTS = 6,
|
|
5
|
+
PERMISSION_DENIED = 7, RESOURCE_EXHAUSTED = 8, FAILED_PRECONDITION = 9,
|
|
6
|
+
ABORTED = 10, OUT_OF_RANGE = 11, UNIMPLEMENTED = 12, INTERNAL = 13,
|
|
7
|
+
UNAVAILABLE = 14, DATA_LOSS = 15, UNAUTHENTICATED = 16,
|
|
8
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { IncomingHttpHeaders, ServerHttp2Stream } from "node:http2";
|
|
2
|
+
import type { GrpcContext } from "./contracts";
|
|
3
|
+
import { Metadata } from "./Metadata";
|
|
4
|
+
import { GrpcError } from "./GrpcError";
|
|
5
|
+
import { GrpcStatus } from "./GrpcStatus";
|
|
6
|
+
import type { MethodDefinition } from "./serviceDefinition";
|
|
7
|
+
|
|
8
|
+
/** Owns the standard five-byte gRPC envelope and one built-in HTTP/2 stream. */
|
|
9
|
+
export class GrpcTransport {
|
|
10
|
+
readonly context: GrpcContext;
|
|
11
|
+
private readonly abort = new AbortController();
|
|
12
|
+
private sentHeaders = false;
|
|
13
|
+
private ended = false;
|
|
14
|
+
private consumed = false;
|
|
15
|
+
private timer?: ReturnType<typeof setTimeout>;
|
|
16
|
+
private trailers: import("node:http2").OutgoingHttpHeaders = {};
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
private readonly stream: ServerHttp2Stream,
|
|
20
|
+
headers: IncomingHttpHeaders,
|
|
21
|
+
private readonly method: MethodDefinition,
|
|
22
|
+
private readonly receiveLimit: number,
|
|
23
|
+
private readonly sendLimit: number,
|
|
24
|
+
) {
|
|
25
|
+
const deadline = parseDeadline(headers["grpc-timeout"]);
|
|
26
|
+
this.context = Object.freeze({
|
|
27
|
+
metadata: Metadata.fromHttp2Headers(headers),
|
|
28
|
+
signal: this.abort.signal,
|
|
29
|
+
deadline,
|
|
30
|
+
path: method.path,
|
|
31
|
+
peer: stream.session?.socket.remoteAddress ?? "",
|
|
32
|
+
sendMetadata: (metadata: Metadata) => this.sendMetadata(metadata),
|
|
33
|
+
});
|
|
34
|
+
stream.on("error", () => this.disconnected());
|
|
35
|
+
stream.on("aborted", () => this.disconnected());
|
|
36
|
+
stream.on("close", () => { this.clearTimer(); if (!this.ended) this.disconnected(); });
|
|
37
|
+
stream.on("wantTrailers", () => {
|
|
38
|
+
if (!stream.destroyed && !stream.closed) {
|
|
39
|
+
try { stream.sendTrailers(this.trailers); } catch { this.disconnected(); }
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
if (deadline !== Infinity) this.armDeadline(deadline);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
cancel(code = GrpcStatus.CANCELLED, message = "RPC cancelled."): void {
|
|
46
|
+
const error = new GrpcError(code, message);
|
|
47
|
+
this.abort.abort(error);
|
|
48
|
+
this.fail(error);
|
|
49
|
+
}
|
|
50
|
+
private disconnected(): void {
|
|
51
|
+
this.clearTimer();
|
|
52
|
+
this.abort.abort(new GrpcError(GrpcStatus.CANCELLED, "RPC cancelled."));
|
|
53
|
+
this.ended = true;
|
|
54
|
+
}
|
|
55
|
+
private armDeadline(deadline: number): void {
|
|
56
|
+
const remaining = deadline - Date.now();
|
|
57
|
+
if (remaining <= 0) {
|
|
58
|
+
const error = new GrpcError(GrpcStatus.DEADLINE_EXCEEDED, "Deadline exceeded.");
|
|
59
|
+
this.abort.abort(error); this.fail(error);
|
|
60
|
+
} else this.timer = setTimeout(() => this.armDeadline(deadline), Math.min(remaining, 2_147_483_647));
|
|
61
|
+
}
|
|
62
|
+
private clearTimer(): void { if (this.timer) clearTimeout(this.timer); this.timer = undefined; }
|
|
63
|
+
|
|
64
|
+
async unaryRequest(): Promise<unknown> {
|
|
65
|
+
let value: unknown, count = 0;
|
|
66
|
+
for await (const request of this.requests()) {
|
|
67
|
+
if (++count > 1) throw new GrpcError(GrpcStatus.INTERNAL, "Unary RPC requires exactly one message.");
|
|
68
|
+
value = request;
|
|
69
|
+
}
|
|
70
|
+
if (count !== 1) throw new GrpcError(GrpcStatus.INTERNAL, "Unary RPC requires exactly one message.");
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async *requests(): AsyncGenerator<unknown> {
|
|
75
|
+
if (this.consumed) throw new Error("gRPC request stream can only be consumed once.");
|
|
76
|
+
this.consumed = true;
|
|
77
|
+
const iterator = this.stream.iterator({ destroyOnReturn: false });
|
|
78
|
+
const prefix = Buffer.alloc(5);
|
|
79
|
+
let prefixBytes = 0, message: Buffer | undefined, messageBytes = 0;
|
|
80
|
+
try {
|
|
81
|
+
while (true) {
|
|
82
|
+
const next = await abortable(iterator.next(), this.abort.signal);
|
|
83
|
+
if (next.done) break;
|
|
84
|
+
const chunk = next.value as Buffer;
|
|
85
|
+
let offset = 0;
|
|
86
|
+
while (offset < chunk.length) {
|
|
87
|
+
this.abort.signal.throwIfAborted();
|
|
88
|
+
if (!message) {
|
|
89
|
+
const amount = Math.min(5 - prefixBytes, chunk.length - offset);
|
|
90
|
+
chunk.copy(prefix, prefixBytes, offset, offset + amount);
|
|
91
|
+
offset += amount; prefixBytes += amount;
|
|
92
|
+
if (prefixBytes !== 5) continue;
|
|
93
|
+
if (prefix[0] !== 0) {
|
|
94
|
+
throw new GrpcError(prefix[0] === 1 ? GrpcStatus.UNIMPLEMENTED : GrpcStatus.INTERNAL,
|
|
95
|
+
prefix[0] === 1 ? "Compressed gRPC messages are not supported." : "Invalid gRPC compression flag.");
|
|
96
|
+
}
|
|
97
|
+
const length = prefix.readUInt32BE(1);
|
|
98
|
+
if (length > this.receiveLimit) throw new GrpcError(GrpcStatus.RESOURCE_EXHAUSTED, "gRPC request message is too large.");
|
|
99
|
+
message = Buffer.alloc(length); messageBytes = 0;
|
|
100
|
+
}
|
|
101
|
+
const amount = Math.min(message.length - messageBytes, chunk.length - offset);
|
|
102
|
+
chunk.copy(message, messageBytes, offset, offset + amount);
|
|
103
|
+
offset += amount; messageBytes += amount;
|
|
104
|
+
if (messageBytes === message.length) {
|
|
105
|
+
let value: unknown;
|
|
106
|
+
try { value = this.method.requestDeserialize(message); }
|
|
107
|
+
catch { throw new GrpcError(GrpcStatus.INTERNAL, "Invalid protobuf request."); }
|
|
108
|
+
message = undefined; prefixBytes = 0;
|
|
109
|
+
yield value;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
this.abort.signal.throwIfAborted();
|
|
114
|
+
if (message || prefixBytes !== 0) throw new GrpcError(GrpcStatus.INTERNAL, "Incomplete gRPC message.");
|
|
115
|
+
} finally {
|
|
116
|
+
// return() never destroys the duplex response side. A pending read is
|
|
117
|
+
// released by stream close after status/cancellation.
|
|
118
|
+
void iterator.return?.().catch(() => {});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
sendMetadata(metadata: Metadata): void {
|
|
123
|
+
if (!(metadata instanceof Metadata)) throw new TypeError("Expected Bazis Metadata.");
|
|
124
|
+
if (this.sentHeaders || this.ended) throw new Error("gRPC response headers have already been sent.");
|
|
125
|
+
this.stream.respond({ ...metadata.toHttp2Headers(), ":status": 200, "content-type": "application/grpc+proto", "grpc-accept-encoding": "identity" }, { waitForTrailers: true });
|
|
126
|
+
this.sentHeaders = true;
|
|
127
|
+
}
|
|
128
|
+
async write(value: unknown): Promise<void> {
|
|
129
|
+
this.abort.signal.throwIfAborted();
|
|
130
|
+
if (this.ended) return;
|
|
131
|
+
const body = this.method.responseSerialize(value);
|
|
132
|
+
if (!Buffer.isBuffer(body)) throw new TypeError("gRPC codecs must return Buffer.");
|
|
133
|
+
if (body.length > this.sendLimit) throw new GrpcError(GrpcStatus.RESOURCE_EXHAUSTED, "gRPC response message is too large.");
|
|
134
|
+
if (!this.sentHeaders) this.sendMetadata(new Metadata());
|
|
135
|
+
const frame = Buffer.allocUnsafe(body.length + 5);
|
|
136
|
+
frame[0] = 0; frame.writeUInt32BE(body.length, 1); body.copy(frame, 5);
|
|
137
|
+
await abortable(new Promise<void>((resolve, reject) => {
|
|
138
|
+
this.stream.write(frame, (error?: Error | null) => error ? reject(error) : resolve());
|
|
139
|
+
}), this.abort.signal);
|
|
140
|
+
}
|
|
141
|
+
fail(error: unknown): void {
|
|
142
|
+
const failure = error instanceof GrpcError ? error : new GrpcError(GrpcStatus.INTERNAL, "Internal server error.");
|
|
143
|
+
this.finish(failure.code, failure.message, failure.metadata);
|
|
144
|
+
}
|
|
145
|
+
finish(code = GrpcStatus.OK, message = "", metadata = new Metadata()): void {
|
|
146
|
+
if (this.ended) return;
|
|
147
|
+
const trailers = { ...metadata.toHttp2Headers(), "grpc-status": String(code),
|
|
148
|
+
"grpc-message": encodeURIComponent(Buffer.from(message, "utf8").toString("utf8")) };
|
|
149
|
+
this.ended = true; this.clearTimer();
|
|
150
|
+
this.trailers = trailers;
|
|
151
|
+
if (this.stream.destroyed || this.stream.closed) return;
|
|
152
|
+
try {
|
|
153
|
+
if (!this.sentHeaders) {
|
|
154
|
+
this.stream.respond({ ":status": 200, "content-type": "application/grpc+proto", ...this.trailers }, { endStream: true });
|
|
155
|
+
} else this.stream.end();
|
|
156
|
+
} catch { this.disconnected(); }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function parseDeadline(value: string | string[] | undefined): number {
|
|
161
|
+
if (value === undefined) return Infinity;
|
|
162
|
+
if (typeof value !== "string" || !/^\d{1,8}[HMSmun]$/.test(value)) throw new GrpcError(GrpcStatus.INVALID_ARGUMENT, "Invalid grpc-timeout.");
|
|
163
|
+
const scale: Record<string, number> = { H: 3_600_000, M: 60_000, S: 1000, m: 1, u: 0.001, n: 0.000001 };
|
|
164
|
+
return Date.now() + Math.ceil(Number(value.slice(0, -1)) * scale[value.at(-1)!]!);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function abortable<T>(pending: Promise<T>, signal: AbortSignal): Promise<T> {
|
|
168
|
+
return new Promise<T>((resolve, reject) => {
|
|
169
|
+
const cancel = (): void => reject(signal.reason);
|
|
170
|
+
if (signal.aborted) { void pending.catch(() => {}); reject(signal.reason); return; }
|
|
171
|
+
signal.addEventListener("abort", cancel, { once: true });
|
|
172
|
+
pending.then(resolve, reject).finally(() => signal.removeEventListener("abort", cancel));
|
|
173
|
+
});
|
|
174
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { OutgoingHttpHeaders } from "node:http2";
|
|
2
|
+
|
|
3
|
+
export type MetadataValue = string | Buffer;
|
|
4
|
+
|
|
5
|
+
/** gRPC metadata. Binary values use -bin keys and base64 only on the wire. */
|
|
6
|
+
export class Metadata {
|
|
7
|
+
private readonly values = new Map<string, MetadataValue[]>();
|
|
8
|
+
|
|
9
|
+
set(key: string, value: MetadataValue): void {
|
|
10
|
+
key = this.validate(key, value);
|
|
11
|
+
this.values.set(key, [Buffer.isBuffer(value) ? Buffer.from(value) : value]);
|
|
12
|
+
}
|
|
13
|
+
add(key: string, value: MetadataValue): void {
|
|
14
|
+
key = this.validate(key, value);
|
|
15
|
+
const values = this.values.get(key) ?? [];
|
|
16
|
+
values.push(Buffer.isBuffer(value) ? Buffer.from(value) : value);
|
|
17
|
+
this.values.set(key, values);
|
|
18
|
+
}
|
|
19
|
+
get(key: string): MetadataValue[] { return [...(this.values.get(key.toLowerCase()) ?? [])]; }
|
|
20
|
+
remove(key: string): void { this.values.delete(key.toLowerCase()); }
|
|
21
|
+
getMap(): Record<string, MetadataValue> {
|
|
22
|
+
return Object.fromEntries([...this.values].map(([key, values]) => [key, values[0]!]));
|
|
23
|
+
}
|
|
24
|
+
clone(): Metadata {
|
|
25
|
+
const result = new Metadata();
|
|
26
|
+
for (const [key, values] of this.values) for (const value of values) result.add(key, value);
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
merge(other: Metadata): void {
|
|
30
|
+
for (const [key, values] of other.values) for (const value of values) this.add(key, value);
|
|
31
|
+
}
|
|
32
|
+
toHttp2Headers(): OutgoingHttpHeaders {
|
|
33
|
+
const headers: OutgoingHttpHeaders = Object.create(null);
|
|
34
|
+
for (const [key, values] of this.values) {
|
|
35
|
+
// Application metadata cannot override transport control headers.
|
|
36
|
+
if (reserved(key)) continue;
|
|
37
|
+
headers[key] = values.map((value) => Buffer.isBuffer(value) ? value.toString("base64") : value);
|
|
38
|
+
}
|
|
39
|
+
return headers;
|
|
40
|
+
}
|
|
41
|
+
static fromHttp2Headers(headers: Readonly<Record<string, string | string[] | number | undefined>>): Metadata {
|
|
42
|
+
const result = new Metadata();
|
|
43
|
+
for (const [key, raw] of Object.entries(headers)) {
|
|
44
|
+
if (raw === undefined || reserved(key)) continue;
|
|
45
|
+
for (const value of Array.isArray(raw) ? raw : [String(raw)]) {
|
|
46
|
+
if (key.endsWith("-bin")) {
|
|
47
|
+
for (const part of value.split(",")) {
|
|
48
|
+
const encoded = part.trim();
|
|
49
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(encoded) || encoded.replace(/=+$/, "").length % 4 === 1) {
|
|
50
|
+
throw new TypeError("Invalid binary gRPC metadata.");
|
|
51
|
+
}
|
|
52
|
+
result.add(key, Buffer.from(encoded, "base64"));
|
|
53
|
+
}
|
|
54
|
+
} else if (/^[\x20-\x7e]*$/.test(value)) result.add(key, value);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
private validate(key: string, value: MetadataValue): string {
|
|
60
|
+
if (typeof key !== "string" || !/^[0-9a-z_.-]+$/i.test(key)) throw new TypeError("Invalid gRPC metadata key.");
|
|
61
|
+
key = key.toLowerCase();
|
|
62
|
+
if (key.endsWith("-bin") ? !Buffer.isBuffer(value) : typeof value !== "string" || !/^[\x20-\x7e]*$/.test(value)) {
|
|
63
|
+
throw new TypeError("gRPC metadata requires printable text, or Buffer for a -bin key.");
|
|
64
|
+
}
|
|
65
|
+
return key;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function reserved(key: string): boolean {
|
|
70
|
+
return key.startsWith(":") || key.startsWith("grpc-")
|
|
71
|
+
&& key !== "grpc-status-details-bin"
|
|
72
|
+
|| ["content-type", "te", "user-agent"].includes(key);
|
|
73
|
+
}
|