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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alexander Alenin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# bazis
|
|
2
|
+
|
|
3
|
+
A modular backend framework for [Bun](https://bun.com), written in TypeScript:
|
|
4
|
+
constructor dependency injection wired by code generation, HTTP controllers
|
|
5
|
+
with validated request models, an ORM for PostgreSQL, configuration with
|
|
6
|
+
per-environment defaults, JWT, WebSocket, gRPC, background services, AI agents
|
|
7
|
+
and a CLI that scaffolds, runs, tests and compiles your app into a single
|
|
8
|
+
executable.
|
|
9
|
+
|
|
10
|
+
The package ships TypeScript sources and runs on Bun only (≥ 1.4.0). There is
|
|
11
|
+
no build step and no separate type package.
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
bunx bazis new MyApp
|
|
17
|
+
cd my-app
|
|
18
|
+
bun install
|
|
19
|
+
bunx bazis dev # GET http://127.0.0.1:3000/health
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`bazis new` creates a project with `src/index.ts`, a root `AppModule`,
|
|
23
|
+
`bazis.config.json`, scripts, a `/health` test and an `.env.example`.
|
|
24
|
+
|
|
25
|
+
## CLI
|
|
26
|
+
|
|
27
|
+
| Command | What it does |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `bazis new <Name>` | Create a project in `./<name>` |
|
|
30
|
+
| `bazis g module <Name> --empty\|--minimal\|--full` | Add a module: empty; CRUD with ORM, validation and paging; or the same with auth guards |
|
|
31
|
+
| `bazis g pack <Name> --parts a,b` | Add a composite module made of several atomic ones |
|
|
32
|
+
| `bazis codegen` | Regenerate dependency wiring into `src/generated/bazis` |
|
|
33
|
+
| `bazis dev [--watch]` | Codegen, then run the app from source with `BAZIS_ENV=development` (unless set); `--watch` restarts on changes in `src/` |
|
|
34
|
+
| `bazis test [args]` | Codegen, then `bun test` |
|
|
35
|
+
| `bazis build` | Codegen and typecheck |
|
|
36
|
+
| `bazis build --bin` | Also compile `bin/<name>`, a standalone executable |
|
|
37
|
+
|
|
38
|
+
`dev`, `test` and `build` run codegen themselves. If you start the app some
|
|
39
|
+
other way after changing code, it warns at startup that the generated code is
|
|
40
|
+
out of date. A compiled binary carries everything it needs and skips that
|
|
41
|
+
check.
|
|
42
|
+
|
|
43
|
+
## A module
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
// src/app/modules/greeting/Greeting.module.ts
|
|
47
|
+
import { Module, scoped } from "bazis/core/di";
|
|
48
|
+
import { Controller, Get } from "bazis/core/http";
|
|
49
|
+
|
|
50
|
+
export class GreetingService {
|
|
51
|
+
hello(name: string) {
|
|
52
|
+
return { message: `Hello, ${name}` };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Controller("greetings")
|
|
57
|
+
export class GreetingController {
|
|
58
|
+
// No registration code: `bazis codegen` reads the constructor.
|
|
59
|
+
constructor(private readonly greetings: GreetingService) {}
|
|
60
|
+
|
|
61
|
+
@Get(":name")
|
|
62
|
+
get(name: string) {
|
|
63
|
+
return this.greetings.hello(name);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@Module({
|
|
68
|
+
controllers: [GreetingController],
|
|
69
|
+
providers: [scoped(GreetingService)],
|
|
70
|
+
exports: [GreetingService],
|
|
71
|
+
})
|
|
72
|
+
export class GreetingModule {}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`bunx bazis g module Greeting --empty` creates this file with an empty module
|
|
76
|
+
and adds `GreetingModule` to the `imports` of `AppModule`. With the code above,
|
|
77
|
+
`GET /greetings/world` answers `{"message":"Hello, world"}`. Another module
|
|
78
|
+
that imports `GreetingModule` can take `GreetingService` in its own
|
|
79
|
+
constructor.
|
|
80
|
+
|
|
81
|
+
## Database
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
// src/app/infra/App.infra.ts
|
|
85
|
+
import { Infra } from "bazis/core/infra";
|
|
86
|
+
import { ormBazisConnect } from "bazis/core/orm";
|
|
87
|
+
import { dbConfig } from "../config/db.config"; // defineConfig("db", { default: { host, port, ... } })
|
|
88
|
+
|
|
89
|
+
@Infra({ db: ormBazisConnect(dbConfig) })
|
|
90
|
+
export class AppInfra {}
|
|
91
|
+
|
|
92
|
+
// src/index.ts
|
|
93
|
+
await runApp(AppModule, { infra: AppInfra, http: { port: 3000, health: true } });
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
A feature module attaches its own `DbContext` and entities with
|
|
97
|
+
`ormBazis: { context, entities }`; its services take that context in their
|
|
98
|
+
constructors. `bunx bazis g module Task --minimal` generates such a module.
|
|
99
|
+
Configuration values can be overridden from the environment
|
|
100
|
+
(`BAZIS_DB__HOST`, `BAZIS_DB__PASSWORD`, ...). Invalid configuration or a
|
|
101
|
+
missing production secret stops startup with an error that names the key.
|
|
102
|
+
|
|
103
|
+
A unique index violation in `saveChanges()` rejects with
|
|
104
|
+
`UniqueViolationError` (with `constraint` and `table`); catch it to answer 409.
|
|
105
|
+
Request-body fields declared as `string`, `number` or `boolean` are checked
|
|
106
|
+
against the JSON type automatically (400 on mismatch); other rules come from
|
|
107
|
+
`@Validator`.
|
|
108
|
+
|
|
109
|
+
## Example
|
|
110
|
+
|
|
111
|
+
The source repository contains `examples/todo`: three modules (projects,
|
|
112
|
+
tasks and a report), PostgreSQL, cross-module injection, validation, JSON:API
|
|
113
|
+
lists, an end-to-end test and a binary build.
|
|
114
|
+
|
|
115
|
+
## Entry points
|
|
116
|
+
|
|
117
|
+
Import from the subpaths listed in `exports` of `package.json`:
|
|
118
|
+
`bazis/core/app`, `bazis/core/di`, `bazis/core/http`, `bazis/core/orm`,
|
|
119
|
+
`bazis/core/infra`, `bazis/core/kernel`, `bazis/library/validation`,
|
|
120
|
+
`bazis/library/jsonapi` and others. Anything not listed there is internal.
|
|
121
|
+
|
|
122
|
+
## Language of built-in texts
|
|
123
|
+
|
|
124
|
+
Built-in texts are English. Russian sets ship with the package; apply them
|
|
125
|
+
once at startup:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { MessageRegistry, RU_VALIDATION_MESSAGES } from "bazis/library/validation";
|
|
129
|
+
import { CodexError, RU_CODEX_MESSAGES } from "bazis/core/infra";
|
|
130
|
+
import { RU_UI_LABELS } from "bazis/core/app";
|
|
131
|
+
|
|
132
|
+
MessageRegistry.setDefaults(RU_VALIDATION_MESSAGES); // validation errors
|
|
133
|
+
CodexError.useMessages(RU_CODEX_MESSAGES); // Codex connector errors
|
|
134
|
+
await runApp(AppModule, { ui: { labels: RU_UI_LABELS, surfaces: [/* ... */] } }); // generated UI texts
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Either call also accepts your own texts for some codes.
|
|
138
|
+
|
|
139
|
+
## Status
|
|
140
|
+
|
|
141
|
+
Pre-1.0: a minor version (0.x) can contain breaking changes; a patch version
|
|
142
|
+
does not.
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT
|
package/cli/build.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { existsSync, mkdtempSync, rmSync, watch } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { resolveBun } from "./codegen";
|
|
5
|
+
|
|
6
|
+
type Codegen = (cwd: string, target?: string) => Promise<number>;
|
|
7
|
+
|
|
8
|
+
export interface BuildOptions {
|
|
9
|
+
/** Also compile the entrypoint into a standalone executable. */
|
|
10
|
+
readonly bin: boolean;
|
|
11
|
+
/** Executable path relative to the project; default `bin/<package name>`. */
|
|
12
|
+
readonly outfile?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `bazis dev`: codegen, then run the default target's entrypoint from source.
|
|
17
|
+
* With `watch`, a change under src/ (except src/generated) stops the app,
|
|
18
|
+
* reruns codegen and starts it again; a failed codegen waits for the next change.
|
|
19
|
+
* The app runs as `development` unless BAZIS_ENV is set in the shell.
|
|
20
|
+
*/
|
|
21
|
+
/** `bazis dev` means development; an BAZIS_ENV from the shell still wins. */
|
|
22
|
+
export function devEnvironment(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
23
|
+
return { ...env, BAZIS_ENV: env.BAZIS_ENV || "development" };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function runDev(cwd: string, codegen: Codegen, log: (message: string) => void, options: { readonly watch?: boolean } = {}): Promise<number> {
|
|
27
|
+
const bun = await resolveBun(cwd);
|
|
28
|
+
const entry = await projectEntry(cwd);
|
|
29
|
+
const env = devEnvironment(process.env);
|
|
30
|
+
if (!options.watch) {
|
|
31
|
+
const generated = await codegen(cwd);
|
|
32
|
+
if (generated !== 0) return generated;
|
|
33
|
+
const child = Bun.spawn([bun, "run", entry], { cwd, env, stdin: "inherit", stdout: "inherit", stderr: "inherit" });
|
|
34
|
+
// The app owns graceful shutdown; forward the signal instead of dying first.
|
|
35
|
+
const forward = (signal: NodeJS.Signals) => () => child.kill(signal);
|
|
36
|
+
const onInterrupt = forward("SIGINT"), onTerminate = forward("SIGTERM");
|
|
37
|
+
process.on("SIGINT", onInterrupt);
|
|
38
|
+
process.on("SIGTERM", onTerminate);
|
|
39
|
+
try {
|
|
40
|
+
return await child.exited;
|
|
41
|
+
} finally {
|
|
42
|
+
process.off("SIGINT", onInterrupt);
|
|
43
|
+
process.off("SIGTERM", onTerminate);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let child: ReturnType<typeof Bun.spawn> | undefined;
|
|
48
|
+
let queue = Promise.resolve();
|
|
49
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
50
|
+
const stop = async () => {
|
|
51
|
+
if (!child) return;
|
|
52
|
+
const running = child;
|
|
53
|
+
child = undefined;
|
|
54
|
+
running.kill("SIGTERM");
|
|
55
|
+
await running.exited;
|
|
56
|
+
};
|
|
57
|
+
const start = async () => {
|
|
58
|
+
if (await codegen(cwd) !== 0) { log("[bazis] codegen failed; waiting for changes..."); return; }
|
|
59
|
+
child = Bun.spawn([bun, "run", entry], { cwd, env, stdin: "inherit", stdout: "inherit", stderr: "inherit" });
|
|
60
|
+
};
|
|
61
|
+
const watcher = watch(path.join(cwd, "src"), { recursive: true }, (_event, file) => {
|
|
62
|
+
// codegen writes src/generated: watching it would restart forever.
|
|
63
|
+
if (!file || /^generated([\\/]|$)/.test(String(file))) return;
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
timer = setTimeout(() => {
|
|
66
|
+
queue = queue.then(async () => { log(`[bazis] ${file} changed, restarting...`); await stop(); await start(); });
|
|
67
|
+
}, 150);
|
|
68
|
+
});
|
|
69
|
+
const finished = Promise.withResolvers<number>();
|
|
70
|
+
const shutdown = () => {
|
|
71
|
+
watcher.close();
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
queue = queue.then(stop).then(() => finished.resolve(0), () => finished.resolve(1));
|
|
74
|
+
};
|
|
75
|
+
process.once("SIGINT", shutdown);
|
|
76
|
+
process.once("SIGTERM", shutdown);
|
|
77
|
+
log("[bazis] watching src/ for changes (Ctrl+C to stop)");
|
|
78
|
+
queue = queue.then(start);
|
|
79
|
+
try {
|
|
80
|
+
return await finished.promise;
|
|
81
|
+
} finally {
|
|
82
|
+
process.off("SIGINT", shutdown);
|
|
83
|
+
process.off("SIGTERM", shutdown);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** `bazis test [args]`: codegen, then `bun test` with the given arguments. */
|
|
88
|
+
export async function runTest(cwd: string, args: readonly string[], codegen: Codegen): Promise<number> {
|
|
89
|
+
const generated = await codegen(cwd);
|
|
90
|
+
if (generated !== 0) return generated;
|
|
91
|
+
return await Bun.spawn([await resolveBun(cwd), "test", ...args], { cwd, stdin: "inherit", stdout: "inherit", stderr: "inherit" }).exited;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** `bazis build [--bin]`: codegen and typecheck; with `bin`, also compile. */
|
|
95
|
+
export async function runBuild(cwd: string, options: BuildOptions, codegen: Codegen, log: (message: string) => void): Promise<number> {
|
|
96
|
+
const generated = await codegen(cwd);
|
|
97
|
+
if (generated !== 0) return generated;
|
|
98
|
+
const bun = await resolveBun(cwd);
|
|
99
|
+
const tsc = path.join(cwd, "node_modules/typescript/bin/tsc");
|
|
100
|
+
if (!existsSync(tsc)) throw new Error("TypeScript is not installed in this project. Run: bun add -d typescript");
|
|
101
|
+
log("[bazis] typecheck...");
|
|
102
|
+
const typecheck = await Bun.spawn([bun, tsc, "--noEmit"], { cwd, stdout: "inherit", stderr: "inherit" }).exited;
|
|
103
|
+
if (typecheck !== 0 || !options.bin) return typecheck;
|
|
104
|
+
const outfile = path.resolve(cwd, options.outfile ?? path.join("bin", await binaryName(cwd)));
|
|
105
|
+
log(`[bazis] compile ${path.relative(cwd, outfile)}...`);
|
|
106
|
+
return compileBinary(bun, await projectEntry(cwd), outfile);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* `bun build --compile` clones the running Bun executable into a temporary
|
|
111
|
+
* `.<hash>.bun-build` file in its working directory and cannot remove it when
|
|
112
|
+
* that executable is read-only or carries `uchg` (scripts/bazis-bun). Compile
|
|
113
|
+
* from a private directory and remove it afterwards.
|
|
114
|
+
*/
|
|
115
|
+
export function compileBinary(bun: string, entry: string, outfile: string): number {
|
|
116
|
+
const workDir = mkdtempSync(path.join(tmpdir(), "bazis-compile-"));
|
|
117
|
+
try {
|
|
118
|
+
const result = Bun.spawnSync([bun, "build", "--compile", path.resolve(entry), "--outfile", path.resolve(outfile)],
|
|
119
|
+
{ cwd: workDir, stdout: "inherit", stderr: "inherit" });
|
|
120
|
+
return result.exitCode ?? 1;
|
|
121
|
+
} finally {
|
|
122
|
+
if (existsSync("/usr/bin/chflags")) Bun.spawnSync(["/usr/bin/chflags", "-R", "nouchg", workDir]);
|
|
123
|
+
try {
|
|
124
|
+
rmSync(workDir, { recursive: true, force: true });
|
|
125
|
+
} catch (error) {
|
|
126
|
+
// The binary itself is complete; report the leftover without failing the build.
|
|
127
|
+
console.error(`[bazis] temporary directory was not removed: ${workDir}`, error);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function projectEntry(cwd: string): Promise<string> {
|
|
133
|
+
const configFile = Bun.file(path.join(cwd, "bazis.config.json"));
|
|
134
|
+
if (!await configFile.exists()) throw new Error("bazis.config.json not found in the current directory.");
|
|
135
|
+
const config = await configFile.json() as { defaultTarget?: string; targets?: Record<string, { entrypoints?: unknown }> };
|
|
136
|
+
const entry = (config.targets?.[config.defaultTarget ?? ""]?.entrypoints as unknown[] | undefined)?.[0];
|
|
137
|
+
if (typeof entry !== "string" || !entry) throw new Error("bazis.config.json has no entrypoint for its default target.");
|
|
138
|
+
return path.resolve(cwd, entry);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function binaryName(cwd: string): Promise<string> {
|
|
142
|
+
const manifest = Bun.file(path.join(cwd, "package.json"));
|
|
143
|
+
const name = await manifest.exists() ? (await manifest.json() as { name?: unknown }).name : undefined;
|
|
144
|
+
const base = typeof name === "string" ? name.replace(/^@[^/]+\//, "").replace(/[^a-z0-9._-]+/gi, "-") : "";
|
|
145
|
+
if (!base) throw new Error("package.json needs a name for the default binary path; pass --outfile.");
|
|
146
|
+
return base;
|
|
147
|
+
}
|
package/cli/codegen.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const GENERATOR = "core/scripts/di-generate.ts";
|
|
5
|
+
|
|
6
|
+
/** Run the framework generator for the project in `cwd`; returns its exit code. */
|
|
7
|
+
export async function runCodegen(cwd: string, target?: string): Promise<number> {
|
|
8
|
+
if (target !== undefined) {
|
|
9
|
+
const configFile = Bun.file(path.join(cwd, "bazis.config.json"));
|
|
10
|
+
if (!await configFile.exists()) throw new Error("--target requires bazis.config.json in the current directory.");
|
|
11
|
+
const config = await configFile.json();
|
|
12
|
+
if (target !== "all" && !Object.hasOwn(config.targets ?? {}, target)) throw new Error(`Unknown codegen target: ${target}`);
|
|
13
|
+
}
|
|
14
|
+
const args = [await resolveBun(cwd), resolveGenerator(cwd), ...(target === undefined ? [] : ["--target", target])];
|
|
15
|
+
return await Bun.spawn(args, { cwd, stdout: "inherit", stderr: "inherit" }).exited;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The generator of the framework this project uses: the installed package,
|
|
20
|
+
* the framework source checkout, or the package this CLI runs from.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveGenerator(cwd: string): string {
|
|
23
|
+
const candidates = [
|
|
24
|
+
path.join(cwd, "node_modules/bazis", GENERATOR),
|
|
25
|
+
path.join(cwd, "src/bazis", GENERATOR),
|
|
26
|
+
path.resolve(import.meta.dir, "..", GENERATOR),
|
|
27
|
+
];
|
|
28
|
+
const generator = candidates.find((candidate) => existsSync(candidate));
|
|
29
|
+
if (!generator) throw new Error("bazis code generator not found. Install the framework in this project: bun add bazis");
|
|
30
|
+
return generator;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Bun for child processes: the project's qualified launcher when present. */
|
|
34
|
+
export async function resolveBun(cwd: string): Promise<string> {
|
|
35
|
+
const wrapper = path.join(cwd, "scripts/bazis-bun");
|
|
36
|
+
// process.execPath can be the compiled CLI itself. Never invoke it as Bun.
|
|
37
|
+
const executable = await Bun.file(wrapper).exists() ? wrapper : (process.env.BAZIS_BUN_BIN || Bun.which("bun"));
|
|
38
|
+
if (!executable) throw new Error("Bun was not found. Set BAZIS_BUN_BIN or put Bun on PATH.");
|
|
39
|
+
return executable;
|
|
40
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { lstat, mkdir, readFile, realpath, rename, rm, rmdir, writeFile } from "node:fs/promises";
|
|
4
|
+
import { buildModuleTemplates, type ModuleTemplateFiles, type ModuleTemplateProfile } from "./templates/module";
|
|
5
|
+
import { buildPackTemplates } from "./templates/pack";
|
|
6
|
+
import { parseModuleName, type ModuleNaming } from "./naming";
|
|
7
|
+
import { moduleImportPath, registerModuleInSource } from "./moduleRegistration";
|
|
8
|
+
|
|
9
|
+
export interface GenerateModuleOptions {
|
|
10
|
+
/** User, users, order-item. */
|
|
11
|
+
readonly name: string;
|
|
12
|
+
/** Default: src/app/modules, relative to cwd. */
|
|
13
|
+
readonly modulesRoot?: string;
|
|
14
|
+
/** Default: {modulesRoot}/App.module.ts, relative to cwd. */
|
|
15
|
+
readonly appModulePath?: string;
|
|
16
|
+
readonly register?: boolean;
|
|
17
|
+
readonly force?: boolean;
|
|
18
|
+
readonly profile?: ModuleTemplateProfile;
|
|
19
|
+
/** Validate and return the plan without writing files. */
|
|
20
|
+
readonly dryRun?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface GenerateModulePackOptions extends Omit<GenerateModuleOptions, "profile"> {
|
|
24
|
+
readonly parts: readonly string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface GenerateModuleResult {
|
|
28
|
+
readonly moduleDir: string;
|
|
29
|
+
readonly files: readonly string[];
|
|
30
|
+
/** In dry-run, whether the resulting plan connects the module. */
|
|
31
|
+
readonly registered: boolean;
|
|
32
|
+
readonly dryRun: boolean;
|
|
33
|
+
readonly changes: readonly { readonly path: string; readonly action: "create" | "update" }[];
|
|
34
|
+
readonly warnings: readonly string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface PlannedWrite {
|
|
38
|
+
readonly absolute: string;
|
|
39
|
+
readonly content: string;
|
|
40
|
+
readonly original: string | undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function generateModule(options: GenerateModuleOptions): Promise<GenerateModuleResult> {
|
|
44
|
+
const naming = parseModuleName(options.name);
|
|
45
|
+
const profile = options.profile ?? "minimal";
|
|
46
|
+
if (!["empty", "minimal", "full"].includes(profile)) throw new Error(`Unknown profile: ${profile}`);
|
|
47
|
+
const modulesRoot = await canonicalDirectory(path.resolve(options.modulesRoot ?? "src/app/modules"));
|
|
48
|
+
let authImportPath: string | null | undefined;
|
|
49
|
+
if (profile === "full") {
|
|
50
|
+
// The host's auth helpers are optional: without them the controller is
|
|
51
|
+
// generated without @Authorize and the command says so.
|
|
52
|
+
const authDir = path.resolve("src/app/modules/auth");
|
|
53
|
+
const helpers = await Promise.all(["tokenKinds.ts", "jwtAuth.ts"].map(async (helper) => (await fileInfo(path.join(authDir, helper)))?.isFile() === true));
|
|
54
|
+
authImportPath = helpers.every(Boolean)
|
|
55
|
+
? moduleImportPath(path.join(modulesRoot, naming.folder, "http", `${naming.module}.controller.ts`), await canonicalDirectory(authDir))
|
|
56
|
+
: null;
|
|
57
|
+
}
|
|
58
|
+
const result = await generateFiles(options, naming, naming.folder, buildModuleTemplates(naming, profile, authImportPath));
|
|
59
|
+
return authImportPath === null
|
|
60
|
+
? { ...result, warnings: [...result.warnings, "No auth helpers in src/app/modules/auth: the full profile controller has no @Authorize. Protect its routes before exposing them."] }
|
|
61
|
+
: result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function generateModulePack(options: GenerateModulePackOptions): Promise<GenerateModuleResult> {
|
|
65
|
+
const naming = parseModuleName(options.name);
|
|
66
|
+
return generateFiles(options, naming, `${naming.folder}_modules`, buildPackTemplates(naming, options.parts));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function generateFiles(options: GenerateModuleOptions, naming: ModuleNaming, folder: string, templates: readonly ModuleTemplateFiles[]): Promise<GenerateModuleResult> {
|
|
70
|
+
const modulesRoot = await canonicalDirectory(path.resolve(options.modulesRoot ?? "src/app/modules"));
|
|
71
|
+
const moduleDir = path.join(modulesRoot, folder);
|
|
72
|
+
const requestedAppPath = path.resolve(options.appModulePath ?? path.join(modulesRoot, "App.module.ts"));
|
|
73
|
+
const appModulePath = path.join(await canonicalDirectory(path.dirname(requestedAppPath)), path.basename(requestedAppPath));
|
|
74
|
+
const moduleInfo = await fileInfo(moduleDir);
|
|
75
|
+
if (moduleInfo && !options.force) throw new Error(`Module folder already exists: ${moduleDir}. Use --force to overwrite.`);
|
|
76
|
+
if (moduleInfo && !moduleInfo.isDirectory()) throw new Error(`Module path is not a directory: ${moduleDir}.`);
|
|
77
|
+
|
|
78
|
+
const featureWrites: PlannedWrite[] = [];
|
|
79
|
+
for (const file of templates) {
|
|
80
|
+
const absolute = path.join(moduleDir, file.relativePath);
|
|
81
|
+
const info = await fileInfo(absolute);
|
|
82
|
+
if (info && !options.force) throw new Error(`File already exists: ${absolute}. Use --force to overwrite.`);
|
|
83
|
+
if (info && !info.isFile()) throw new Error(`Refusing to overwrite a non-regular file: ${absolute}`);
|
|
84
|
+
featureWrites.push({ absolute, content: file.content, original: info ? await readFile(absolute, "utf8") : undefined });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Complete host preflight before the first write, including on --force.
|
|
88
|
+
const writes = [...featureWrites];
|
|
89
|
+
const warnings: string[] = [];
|
|
90
|
+
let registered = false;
|
|
91
|
+
if (options.register !== false) {
|
|
92
|
+
const appInfo = await fileInfo(appModulePath);
|
|
93
|
+
if (!appInfo) {
|
|
94
|
+
warnings.push(`App module not found, skipped registration: ${appModulePath}`);
|
|
95
|
+
} else {
|
|
96
|
+
if (!appInfo.isFile()) throw new Error(`App module must be a regular file: ${appModulePath}`);
|
|
97
|
+
const original = await readFile(appModulePath, "utf8");
|
|
98
|
+
const content = registerModuleInSource(original, appModulePath, path.join(moduleDir, `${naming.module}.module.ts`), naming.moduleClass);
|
|
99
|
+
if (content !== original) writes.push({ absolute: appModulePath, content, original });
|
|
100
|
+
registered = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const changed = writes.filter((write) => write.content !== write.original);
|
|
104
|
+
if (!options.dryRun) await commitWrites(changed);
|
|
105
|
+
return {
|
|
106
|
+
moduleDir,
|
|
107
|
+
files: featureWrites.map((write) => path.relative(process.cwd(), write.absolute)),
|
|
108
|
+
registered, dryRun: options.dryRun === true, warnings,
|
|
109
|
+
changes: changed.map((write) => ({ path: path.relative(process.cwd(), write.absolute), action: write.original === undefined ? "create" : "update" })),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function fileInfo(absolute: string) {
|
|
114
|
+
try {
|
|
115
|
+
return await lstat(absolute);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
118
|
+
if (code === "ENOENT" || code === "ENOTDIR") return undefined;
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Relative imports must survive runtime realpath resolution (e.g. /var -> /private/var). */
|
|
124
|
+
async function canonicalDirectory(directory: string): Promise<string> {
|
|
125
|
+
try {
|
|
126
|
+
return await realpath(directory);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
129
|
+
const parent = path.dirname(directory);
|
|
130
|
+
if (parent === directory) throw error;
|
|
131
|
+
return path.join(await canonicalDirectory(parent), path.basename(directory));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function commitWrites(writes: readonly PlannedWrite[]): Promise<void> {
|
|
136
|
+
const completed: PlannedWrite[] = [];
|
|
137
|
+
const createdDirectories = new Set<string>();
|
|
138
|
+
try {
|
|
139
|
+
for (const write of writes) {
|
|
140
|
+
const current = await fileInfo(write.absolute);
|
|
141
|
+
if ((current ? await readFile(write.absolute, "utf8") : undefined) !== write.original) {
|
|
142
|
+
throw new Error(`File changed after generation was planned: ${write.absolute}`);
|
|
143
|
+
}
|
|
144
|
+
await writeAtomically(write.absolute, write.content, createdDirectories);
|
|
145
|
+
completed.push(write);
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
const rollbackErrors: unknown[] = [];
|
|
149
|
+
for (const write of completed.reverse()) {
|
|
150
|
+
try {
|
|
151
|
+
if (write.original === undefined) await rm(write.absolute, { force: true });
|
|
152
|
+
else await writeAtomically(write.absolute, write.original, createdDirectories);
|
|
153
|
+
} catch (rollbackError) {
|
|
154
|
+
rollbackErrors.push(rollbackError);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
for (const directory of [...createdDirectories].sort((a, b) => b.length - a.length)) {
|
|
158
|
+
try {
|
|
159
|
+
await rmdir(directory);
|
|
160
|
+
} catch (cleanupError) {
|
|
161
|
+
const code = (cleanupError as NodeJS.ErrnoException).code;
|
|
162
|
+
// Preserve files another writer may have added to a new directory.
|
|
163
|
+
if (code !== "ENOTEMPTY" && code !== "ENOENT") rollbackErrors.push(cleanupError);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (rollbackErrors.length) throw new AggregateError([error, ...rollbackErrors], "Module generation failed and rollback was incomplete.");
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function writeAtomically(absolute: string, content: string, createdDirectories: Set<string>): Promise<void> {
|
|
172
|
+
const parent = path.dirname(absolute);
|
|
173
|
+
const firstCreated = await mkdir(parent, { recursive: true });
|
|
174
|
+
if (firstCreated) {
|
|
175
|
+
for (let directory = parent; ; directory = path.dirname(directory)) {
|
|
176
|
+
createdDirectories.add(directory);
|
|
177
|
+
if (directory === firstCreated) break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const temporary = path.join(parent, `.${path.basename(absolute)}.bazis-${randomUUID()}.tmp`);
|
|
181
|
+
try {
|
|
182
|
+
await writeFile(temporary, content, { encoding: "utf8", flag: "wx" });
|
|
183
|
+
await rename(temporary, absolute);
|
|
184
|
+
} finally {
|
|
185
|
+
await rm(temporary, { force: true });
|
|
186
|
+
}
|
|
187
|
+
}
|