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.
Files changed (513) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -0
  3. package/cli/build.ts +147 -0
  4. package/cli/codegen.ts +40 -0
  5. package/cli/generateModule.ts +187 -0
  6. package/cli/generateProject.ts +183 -0
  7. package/cli/main.ts +125 -0
  8. package/cli/moduleRegistration.ts +127 -0
  9. package/cli/naming.ts +124 -0
  10. package/cli/parseCli.ts +151 -0
  11. package/cli/templates/module.ts +497 -0
  12. package/cli/templates/pack.ts +76 -0
  13. package/cli/templates/passport.ts +178 -0
  14. package/core/agent/AgentContextBuilder.ts +567 -0
  15. package/core/agent/AgentDataDefinition.ts +8 -0
  16. package/core/agent/AgentRegistry.ts +539 -0
  17. package/core/agent/AgentRuntime.ts +47 -0
  18. package/core/agent/AgentToolExecutor.ts +2130 -0
  19. package/core/agent/AgentToolHooks.ts +39 -0
  20. package/core/agent/ModelProvider.ts +21 -0
  21. package/core/agent/README.md +229 -0
  22. package/core/agent/Tool.contract.ts +20 -0
  23. package/core/agent/decorators/Agent.ts +11 -0
  24. package/core/agent/decorators/Prompt.ts +11 -0
  25. package/core/agent/decorators/Task.ts +29 -0
  26. package/core/agent/decorators/Tool.ts +12 -0
  27. package/core/agent/errors.ts +43 -0
  28. package/core/agent/generated/catalog.ts +26 -0
  29. package/core/agent/index.ts +170 -0
  30. package/core/agent/internal/AgentExecutionDriver.ts +1497 -0
  31. package/core/agent/internal/AgentExecutionState.ts +775 -0
  32. package/core/agent/internal/AgentJsonSchema.ts +150 -0
  33. package/core/agent/internal/AgentModelBinding.ts +57 -0
  34. package/core/agent/internal/Schema.contract.ts +36 -0
  35. package/core/agent/internal/ToolAudit.projector.ts +163 -0
  36. package/core/agent/internal/ToolContract.validator.ts +102 -0
  37. package/core/agent/metadata.ts +398 -0
  38. package/core/agent/moduleContributions-v1.ts +113 -0
  39. package/core/agent/semantic.ts +903 -0
  40. package/core/agent/session/codec.ts +33 -0
  41. package/core/agent/session/contracts.ts +96 -0
  42. package/core/agent/session/entities.ts +84 -0
  43. package/core/agent/session/protector.ts +92 -0
  44. package/core/app/index.ts +5 -0
  45. package/core/app/runApp.ts +293 -0
  46. package/core/app/ui/index.ts +29 -0
  47. package/core/app/ui/uiProfileResolver.ts +966 -0
  48. package/core/app/ui/uiSurfaceHosting.ts +38 -0
  49. package/core/app/ui/uiSurfaceHttp.ts +476 -0
  50. package/core/background/BackgroundService.ts +223 -0
  51. package/core/background/backgroundModule.ts +42 -0
  52. package/core/background/decorator.ts +43 -0
  53. package/core/background/delay.ts +27 -0
  54. package/core/background/index.ts +15 -0
  55. package/core/cache/COMPATIBILITY.md +13 -0
  56. package/core/cache/ICache.ts +71 -0
  57. package/core/cache/MemoryCache.ts +394 -0
  58. package/core/cache/README.md +83 -0
  59. package/core/cache/SPEC.md +1355 -0
  60. package/core/cache/cacheModule.ts +21 -0
  61. package/core/cache/decorators/Cacheable.ts +38 -0
  62. package/core/cache/decorators/CacheableRedis.ts +39 -0
  63. package/core/cache/decorators/OutputCache.ts +33 -0
  64. package/core/cache/decorators/OutputRedisCache.ts +33 -0
  65. package/core/cache/decorators/cacheableMetadata.ts +78 -0
  66. package/core/cache/decorators/cacheableRedisMetadata.ts +81 -0
  67. package/core/cache/decorators/outputCacheMetadata.ts +71 -0
  68. package/core/cache/decorators/outputRedisCacheMetadata.ts +77 -0
  69. package/core/cache/di/classProviderHook.ts +26 -0
  70. package/core/cache/distributed/CacheCodec.ts +5 -0
  71. package/core/cache/distributed/DistributedCache.ts +403 -0
  72. package/core/cache/distributed/DistributedCacheDriver.ts +83 -0
  73. package/core/cache/distributed/DistributedCacheStores.ts +28 -0
  74. package/core/cache/distributed/IDistributedCache.ts +40 -0
  75. package/core/cache/distributed/NamedCacheRegistry.ts +23 -0
  76. package/core/cache/distributed/codecs.ts +32 -0
  77. package/core/cache/distributed/index.ts +19 -0
  78. package/core/cache/errors/CacheError.ts +16 -0
  79. package/core/cache/http/CachedHttpPayload.ts +213 -0
  80. package/core/cache/http/applyClientCacheHeaders.ts +61 -0
  81. package/core/cache/http/buildOutputCacheKey.ts +154 -0
  82. package/core/cache/http/buildRedisOutputCacheKey.ts +16 -0
  83. package/core/cache/http/composeOutputCache.ts +130 -0
  84. package/core/cache/http/outputCacheMiddleware.ts +111 -0
  85. package/core/cache/http/outputCacheSecurityWarning.ts +58 -0
  86. package/core/cache/http/outputRedisCacheMiddleware.ts +118 -0
  87. package/core/cache/index.ts +97 -0
  88. package/core/cache/internal/AutomaticCacheKey.ts +106 -0
  89. package/core/cache/internal/CacheKeyGuard.ts +56 -0
  90. package/core/cache/internal/buildCacheProviders.ts +146 -0
  91. package/core/cache/internal/normalizeCacheKey.ts +9 -0
  92. package/core/cache/internal/resolveCachePolicy.ts +32 -0
  93. package/core/cache/internal/resolveCacheableRedisPolicy.ts +17 -0
  94. package/core/cache/internal/resolveOutputRedisCachePolicy.ts +17 -0
  95. package/core/cache/providers/cachedProviders.ts +68 -0
  96. package/core/cache/services/cacheProxy.ts +243 -0
  97. package/core/cache/tokens/CACHE_POLICIES.ts +5 -0
  98. package/core/cache/tokens/DISTRIBUTED_CACHE.ts +30 -0
  99. package/core/cache/types/CacheEntry.ts +7 -0
  100. package/core/cache/types/CacheOptions.ts +60 -0
  101. package/core/cache/types/CachePolicy.ts +74 -0
  102. package/core/cache/types/CacheableRedisOptions.ts +23 -0
  103. package/core/cache/types/OutputRedisCacheOptions.ts +25 -0
  104. package/core/di/README.md +104 -0
  105. package/core/di/ServiceCollection.ts +185 -0
  106. package/core/di/ServiceProvider.ts +689 -0
  107. package/core/di/ServiceScope.ts +61 -0
  108. package/core/di/container.ts +38 -0
  109. package/core/di/errors/AmbiguousNamedDependencyError.ts +10 -0
  110. package/core/di/errors/AsyncResolutionRequiredError.ts +12 -0
  111. package/core/di/errors/CircularDependencyError.ts +10 -0
  112. package/core/di/errors/ClassDependenciesMismatchError.ts +15 -0
  113. package/core/di/errors/DiError.ts +6 -0
  114. package/core/di/errors/InvalidProviderError.ts +9 -0
  115. package/core/di/errors/ModuleEncapsulationError.ts +7 -0
  116. package/core/di/errors/ModuleOwnedProviderConflictError.ts +8 -0
  117. package/core/di/errors/NamedDependencyNotFoundError.ts +7 -0
  118. package/core/di/errors/OptionsValidationError.ts +7 -0
  119. package/core/di/errors/ProviderNotFoundError.ts +10 -0
  120. package/core/di/errors/ScopeDisposedError.ts +7 -0
  121. package/core/di/errors/ScopedServiceFromRootError.ts +9 -0
  122. package/core/di/errors/ServiceValidationError.ts +7 -0
  123. package/core/di/errors/index.ts +14 -0
  124. package/core/di/extensions/activator.ts +39 -0
  125. package/core/di/extensions/application.ts +117 -0
  126. package/core/di/extensions/hosted-service.ts +124 -0
  127. package/core/di/extensions/index.ts +23 -0
  128. package/core/di/extensions/options-reloadable.ts +137 -0
  129. package/core/di/extensions/options.ts +91 -0
  130. package/core/di/extensions/service-provider-token.ts +21 -0
  131. package/core/di/index.ts +97 -0
  132. package/core/di/internal/GraphValidator.ts +119 -0
  133. package/core/di/internal/HostedServicePlan.ts +158 -0
  134. package/core/di/internal/NamedTokenIndex.ts +55 -0
  135. package/core/di/internal/OpenGenericRegistration.ts +14 -0
  136. package/core/di/internal/ResolutionPlan.ts +18 -0
  137. package/core/di/internal/ResolutionScopeState.ts +19 -0
  138. package/core/di/internal/ResolutionTracker.ts +153 -0
  139. package/core/di/internal/ScopeLifecycle.ts +159 -0
  140. package/core/di/internal/ServiceRegistration.ts +14 -0
  141. package/core/di/internal/ServiceRegistry.ts +252 -0
  142. package/core/di/internal/classDeps.ts +38 -0
  143. package/core/di/internal/disposal.ts +58 -0
  144. package/core/di/internal/providerDeps.ts +26 -0
  145. package/core/di/module/DI.ts +146 -0
  146. package/core/di/module/Global.ts +25 -0
  147. package/core/di/module/Module.ts +44 -0
  148. package/core/di/module/ModuleRegistrar.ts +69 -0
  149. package/core/di/module/autoDeps.ts +39 -0
  150. package/core/di/module/classProviderHooks.ts +66 -0
  151. package/core/di/module/collectModuleConfigs.ts +57 -0
  152. package/core/di/module/collectModuleControllers.ts +53 -0
  153. package/core/di/module/collectModuleUiProfiles.ts +53 -0
  154. package/core/di/module/createContainer.ts +261 -0
  155. package/core/di/module/encapsulation.ts +416 -0
  156. package/core/di/module/encapsulationHooks.ts +36 -0
  157. package/core/di/module/generatedProviderAttachments.ts +102 -0
  158. package/core/di/module/index.ts +41 -0
  159. package/core/di/module/moduleExtensions.ts +35 -0
  160. package/core/di/module/moduleNameBinding.ts +136 -0
  161. package/core/di/module/moduleOwnedProviderContributors.ts +409 -0
  162. package/core/di/module/providerSelection.ts +90 -0
  163. package/core/di/module/shortcuts.ts +111 -0
  164. package/core/di/module/types/BazisModule.ts +71 -0
  165. package/core/di/module/types/DiRegistrar.ts +24 -0
  166. package/core/di/module/types/index.ts +2 -0
  167. package/core/di/provider/index.ts +26 -0
  168. package/core/di/provider/providerGuards.ts +17 -0
  169. package/core/di/provider/types/AsyncFactoryProvider.ts +15 -0
  170. package/core/di/provider/types/ClassProvider.ts +18 -0
  171. package/core/di/provider/types/FactoryProvider.ts +20 -0
  172. package/core/di/provider/types/KeyedDependency.ts +20 -0
  173. package/core/di/provider/types/LazyDependency.ts +31 -0
  174. package/core/di/provider/types/NamedDependency.ts +16 -0
  175. package/core/di/provider/types/Provider.ts +10 -0
  176. package/core/di/provider/types/ProviderDefinition.ts +11 -0
  177. package/core/di/provider/types/ProviderDependencyList.ts +11 -0
  178. package/core/di/provider/types/ProviderLifetime.ts +1 -0
  179. package/core/di/provider/types/ProviderOwnership.ts +7 -0
  180. package/core/di/provider/types/ResolvedDeps.ts +18 -0
  181. package/core/di/provider/types/ValueProvider.ts +6 -0
  182. package/core/di/provider/types/index.ts +13 -0
  183. package/core/di/token.ts +84 -0
  184. package/core/di/types/BuildServiceProviderOptions.ts +4 -0
  185. package/core/di/types/ServiceKey.ts +1 -0
  186. package/core/di/types/ServiceResolver.ts +16 -0
  187. package/core/di/types/index.ts +3 -0
  188. package/core/generatedFingerprint.ts +60 -0
  189. package/core/generatedRuntime.ts +187 -0
  190. package/core/grpc/Grpc.module.ts +23 -0
  191. package/core/grpc/GrpcBinding.contract.ts +35 -0
  192. package/core/grpc/GrpcCall.ts +73 -0
  193. package/core/grpc/GrpcClient.ts +172 -0
  194. package/core/grpc/GrpcClientCall.ts +267 -0
  195. package/core/grpc/GrpcError.ts +19 -0
  196. package/core/grpc/GrpcRequest.binding.ts +45 -0
  197. package/core/grpc/GrpcServer.ts +204 -0
  198. package/core/grpc/GrpcStatus.ts +8 -0
  199. package/core/grpc/GrpcTransport.ts +174 -0
  200. package/core/grpc/Metadata.ts +73 -0
  201. package/core/grpc/ProtoCodec.ts +225 -0
  202. package/core/grpc/ProtoParser.ts +192 -0
  203. package/core/grpc/ProtoReader.ts +34 -0
  204. package/core/grpc/ProtoSchema.ts +52 -0
  205. package/core/grpc/ServerCredentials.ts +30 -0
  206. package/core/grpc/clientContracts.ts +39 -0
  207. package/core/grpc/contracts.ts +45 -0
  208. package/core/grpc/decorators.ts +82 -0
  209. package/core/grpc/grpcClientProvider.ts +9 -0
  210. package/core/grpc/index.ts +17 -0
  211. package/core/grpc/moduleControllers.ts +38 -0
  212. package/core/grpc/protobuf.ts +80 -0
  213. package/core/grpc/serviceDefinition.ts +20 -0
  214. package/core/http/Authorization/Authorize.ts +52 -0
  215. package/core/http/Authorization/authorizeComposer.ts +31 -0
  216. package/core/http/Authorization/metadata.ts +158 -0
  217. package/core/http/Binding/ParameterBinder.ts +109 -0
  218. package/core/http/Binding/RequestModel.ts +27 -0
  219. package/core/http/Binding/autoBindings.ts +125 -0
  220. package/core/http/Binding/bindings.ts +19 -0
  221. package/core/http/Binding/convert.ts +48 -0
  222. package/core/http/Binding/generatedSpec.ts +18 -0
  223. package/core/http/Binding/listModelRegistry.ts +47 -0
  224. package/core/http/Binding/modelBinder.ts +345 -0
  225. package/core/http/Binding/modelValidator.ts +30 -0
  226. package/core/http/Binding/requestModelRegistry.ts +117 -0
  227. package/core/http/Decorators/attributes.ts +92 -0
  228. package/core/http/Decorators/controller.ts +24 -0
  229. package/core/http/Decorators/metadata.ts +107 -0
  230. package/core/http/Decorators/routes.ts +131 -0
  231. package/core/http/Errors/HttpError.ts +92 -0
  232. package/core/http/HttpContext/HttpContext.ts +148 -0
  233. package/core/http/HttpContext/ResponseBuilder.ts +37 -0
  234. package/core/http/HttpContext/inspectableRedirects.ts +47 -0
  235. package/core/http/HttpContext/principal.ts +24 -0
  236. package/core/http/HttpContext/responseLifetime.ts +45 -0
  237. package/core/http/HttpServer.ts +627 -0
  238. package/core/http/Middleware/accessLog.ts +90 -0
  239. package/core/http/Middleware/cors.ts +160 -0
  240. package/core/http/Middleware/errorHandler.ts +100 -0
  241. package/core/http/Middleware/pipeline.ts +21 -0
  242. package/core/http/Middleware/rateLimit.ts +104 -0
  243. package/core/http/Middleware/securityHeaders.ts +118 -0
  244. package/core/http/Middleware/types.ts +19 -0
  245. package/core/http/OpenApi/generatedOpenApiRegistry.ts +102 -0
  246. package/core/http/OpenApi/openApiDocs.ts +75 -0
  247. package/core/http/OpenApi/openApiDocument.ts +356 -0
  248. package/core/http/README.md +139 -0
  249. package/core/http/Results/HttpResult.ts +19 -0
  250. package/core/http/Results/normalize.ts +93 -0
  251. package/core/http/Results/results.ts +70 -0
  252. package/core/http/Routing/Router.ts +201 -0
  253. package/core/http/Routing/RouterBuilder.ts +184 -0
  254. package/core/http/Routing/template.ts +112 -0
  255. package/core/http/SPEC.md +981 -0
  256. package/core/http/Versioning/VersionReader.ts +20 -0
  257. package/core/http/WebSocketUpgrade.ts +29 -0
  258. package/core/http/composeRouteMiddlewareComposers.ts +21 -0
  259. package/core/http/correlation/createCorrelationIdMiddleware.ts +79 -0
  260. package/core/http/httpModule.ts +48 -0
  261. package/core/http/index.ts +140 -0
  262. package/core/http/middlewareTokens.ts +48 -0
  263. package/core/http/options.ts +134 -0
  264. package/core/http-client/index.ts +105 -0
  265. package/core/infra/Infra.ts +158 -0
  266. package/core/infra/InfraConnector.ts +86 -0
  267. package/core/infra/InfraLifecycle.ts +86 -0
  268. package/core/infra/cache/RedisDistributedCacheBackend.ts +88 -0
  269. package/core/infra/cache/RedisDistributedCacheDriver.ts +186 -0
  270. package/core/infra/cache/index.ts +7 -0
  271. package/core/infra/connectorConfig.ts +29 -0
  272. package/core/infra/connectorIdentity.ts +39 -0
  273. package/core/infra/connectors/codex/CodexAppServer.ts +146 -0
  274. package/core/infra/connectors/codex/CodexAppServerClient.ts +258 -0
  275. package/core/infra/connectors/codex/CodexPolicy.ts +114 -0
  276. package/core/infra/connectors/codex/CodexTurn.ts +133 -0
  277. package/core/infra/connectors/codex/contracts.ts +82 -0
  278. package/core/infra/connectors/codex.ts +26 -0
  279. package/core/infra/connectors/llm.ts +78 -0
  280. package/core/infra/connectors/llmRouter.ts +443 -0
  281. package/core/infra/connectors/openaiCompatible.ts +590 -0
  282. package/core/infra/connectors/openaiTextStream.ts +134 -0
  283. package/core/infra/connectors/opensearch.ts +307 -0
  284. package/core/infra/connectors/postgres.ts +105 -0
  285. package/core/infra/connectors/redis.ts +132 -0
  286. package/core/infra/index.ts +58 -0
  287. package/core/kernel/ApplicationLifetime.ts +91 -0
  288. package/core/kernel/Bazis.ts +73 -0
  289. package/core/kernel/Environment.ts +53 -0
  290. package/core/kernel/Kernel.ts +391 -0
  291. package/core/kernel/KernelBuilder.ts +172 -0
  292. package/core/kernel/LifecycleCoordinator.ts +204 -0
  293. package/core/kernel/README.md +66 -0
  294. package/core/kernel/SupervisedHostedService.ts +147 -0
  295. package/core/kernel/config/ConfigRegistry.ts +48 -0
  296. package/core/kernel/config/Configuration.ts +110 -0
  297. package/core/kernel/config/README.md +62 -0
  298. package/core/kernel/config/Secret.ts +29 -0
  299. package/core/kernel/config/addConfigOptions.ts +63 -0
  300. package/core/kernel/config/defineConfig.ts +211 -0
  301. package/core/kernel/config/sources.ts +123 -0
  302. package/core/kernel/correlation/requestContext.ts +57 -0
  303. package/core/kernel/errors/ConfigKeyMissingError.ts +7 -0
  304. package/core/kernel/errors/KernelError.ts +7 -0
  305. package/core/kernel/errors/ShutdownTimeoutError.ts +7 -0
  306. package/core/kernel/errors/StartupAbortedError.ts +7 -0
  307. package/core/kernel/errors/StartupTimeoutError.ts +7 -0
  308. package/core/kernel/errors/index.ts +5 -0
  309. package/core/kernel/events/EventBus.ts +140 -0
  310. package/core/kernel/events/EventToken.ts +17 -0
  311. package/core/kernel/events/eventSubscribers.ts +126 -0
  312. package/core/kernel/events/kernelEvents.ts +16 -0
  313. package/core/kernel/events/onEvent.ts +32 -0
  314. package/core/kernel/health/HealthCheckContracts.ts +42 -0
  315. package/core/kernel/health/HealthService.ts +93 -0
  316. package/core/kernel/index.ts +96 -0
  317. package/core/kernel/internal/awaitAbortable.ts +18 -0
  318. package/core/kernel/internal/awaitShutdown.ts +21 -0
  319. package/core/kernel/internal/reportDiagnosticFailure.ts +8 -0
  320. package/core/kernel/internal/validateTimeout.ts +8 -0
  321. package/core/kernel/lifecycleHooks.ts +18 -0
  322. package/core/kernel/logging/ConsoleLogger.ts +68 -0
  323. package/core/kernel/logging/Logger.ts +22 -0
  324. package/core/kernel/types/ConfigSource.ts +9 -0
  325. package/core/kernel/types/EnvironmentName.ts +1 -0
  326. package/core/kernel/types/KernelOptions.ts +23 -0
  327. package/core/kernel/types/LifecycleHook.ts +18 -0
  328. package/core/kernel/types/index.ts +4 -0
  329. package/core/orm/DATABASE_PROVIDER.ts +5 -0
  330. package/core/orm/OrmHostedPlan.validator.ts +55 -0
  331. package/core/orm/OrmLifecycle.ts +146 -0
  332. package/core/orm/OrmOwnedStoreLifecycle.ts +96 -0
  333. package/core/orm/buildOrmProviders.ts +152 -0
  334. package/core/orm/databaseConnector.ts +108 -0
  335. package/core/orm/encapsulationHook.ts +19 -0
  336. package/core/orm/index.ts +95 -0
  337. package/core/orm/listQuery.ts +198 -0
  338. package/core/orm/ormModule.ts +173 -0
  339. package/core/orm/ownedStoreContributions.ts +195 -0
  340. package/core/orm/registerRepositories.ts +27 -0
  341. package/core/orm/repository.ts +45 -0
  342. package/core/orm/strictSchemaHostedIdentity.ts +17 -0
  343. package/core/scripts/agent-codegen.ts +1166 -0
  344. package/core/scripts/di-generate-target.ts +90 -0
  345. package/core/scripts/di-generate.ts +1837 -0
  346. package/core/scripts/orm-predicate-codegen.ts +33 -0
  347. package/core/scripts/request-model-codegen.ts +326 -0
  348. package/core/websocket/DESIGN.md +297 -0
  349. package/core/websocket/WsRateLimiter.ts +101 -0
  350. package/core/websocket/adapter/adapter.interface.ts +45 -0
  351. package/core/websocket/adapter/in-memory.adapter.ts +190 -0
  352. package/core/websocket/adapter/redis-delivery-scripts.ts +135 -0
  353. package/core/websocket/adapter/redis-delivery-store.ts +86 -0
  354. package/core/websocket/adapter/redis-operations.ts +33 -0
  355. package/core/websocket/adapter/redis.adapter.ts +218 -0
  356. package/core/websocket/codec/binary.codec.ts +87 -0
  357. package/core/websocket/codec/json.codec.ts +34 -0
  358. package/core/websocket/codec/packet-codec.interface.ts +17 -0
  359. package/core/websocket/decorators.ts +165 -0
  360. package/core/websocket/explorer.ts +126 -0
  361. package/core/websocket/index.ts +78 -0
  362. package/core/websocket/middleware.ts +146 -0
  363. package/core/websocket/packet-codec.ts +75 -0
  364. package/core/websocket/reliable-delivery.ts +56 -0
  365. package/core/websocket/session-creation.ts +16 -0
  366. package/core/websocket/session-manager.ts +856 -0
  367. package/core/websocket/socket-wrapper.ts +314 -0
  368. package/core/websocket/topic-cache.ts +59 -0
  369. package/core/websocket/types.ts +122 -0
  370. package/core/websocket/websocketModule.ts +96 -0
  371. package/core/websocket/ws-dispatch.ts +123 -0
  372. package/core/websocket/ws-server.ts +1707 -0
  373. package/index.ts +281 -0
  374. package/library/boundary/diagnostics-v1.ts +136 -0
  375. package/library/boundary/index.ts +73 -0
  376. package/library/boundary/json/decodeBoundedJsonV1.ts +518 -0
  377. package/library/boundary/json/limits-v1.ts +46 -0
  378. package/library/boundary/json/normalizeBoundedJsonV1.ts +256 -0
  379. package/library/boundary/json/types-v1.ts +11 -0
  380. package/library/boundary/schema/boundarySchemaHashV1.ts +6 -0
  381. package/library/boundary/schema/collectBoundaryValueDiagnosticsV1.ts +325 -0
  382. package/library/boundary/schema/decodeBoundarySchemaV1.ts +646 -0
  383. package/library/boundary/schema/index.ts +8 -0
  384. package/library/boundary/schema/types-v1.ts +99 -0
  385. package/library/boundary/schema/validateBoundaryValueV1.ts +58 -0
  386. package/library/boundary/serialization/canonicalJsonV1.ts +66 -0
  387. package/library/http-client/HttpClient.ts +969 -0
  388. package/library/http-client/HttpClientFactory.ts +45 -0
  389. package/library/http-client/errors.ts +68 -0
  390. package/library/http-client/index.ts +43 -0
  391. package/library/http-client/interceptors.ts +40 -0
  392. package/library/http-client/progress.ts +62 -0
  393. package/library/http-client/redirectProtocol.ts +6 -0
  394. package/library/http-client/serialize.ts +133 -0
  395. package/library/http-client/types.ts +174 -0
  396. package/library/jsonapi/ListQueryError.ts +25 -0
  397. package/library/jsonapi/ListRequest.ts +37 -0
  398. package/library/jsonapi/decorators.ts +161 -0
  399. package/library/jsonapi/document.ts +124 -0
  400. package/library/jsonapi/index.ts +40 -0
  401. package/library/jsonapi/parseListQuery.ts +291 -0
  402. package/library/jsonapi/types.ts +125 -0
  403. package/library/jwt/JwtEncoder.ts +40 -0
  404. package/library/jwt/JwtKeyRing.ts +164 -0
  405. package/library/jwt/JwtValidator.ts +210 -0
  406. package/library/jwt/TokenIssuer.ts +149 -0
  407. package/library/jwt/TokenService.ts +51 -0
  408. package/library/jwt/base64url.ts +61 -0
  409. package/library/jwt/claims.ts +36 -0
  410. package/library/jwt/errors.ts +44 -0
  411. package/library/jwt/index.ts +59 -0
  412. package/library/jwt/limits.ts +8 -0
  413. package/library/jwt/signing/Hs256Algorithm.ts +62 -0
  414. package/library/jwt/signing/Rs256Algorithm.ts +133 -0
  415. package/library/jwt/signing/SigningAlgorithm.ts +18 -0
  416. package/library/jwt/signing/pem.ts +30 -0
  417. package/library/openapi/codegen.ts +875 -0
  418. package/library/openapi/document.ts +326 -0
  419. package/library/openapi/index.ts +13 -0
  420. package/library/openapi/types.ts +69 -0
  421. package/library/openapi/ui.ts +182 -0
  422. package/library/orm/DatabaseFacade.ts +209 -0
  423. package/library/orm/DbContext.ts +107 -0
  424. package/library/orm/DbContextFactory.ts +25 -0
  425. package/library/orm/DbContextOptions.ts +33 -0
  426. package/library/orm/Metadata/DynamicModelBuilder.ts +497 -0
  427. package/library/orm/Metadata/KeyTuple.ts +36 -0
  428. package/library/orm/Metadata/ModelBuilder.ts +299 -0
  429. package/library/orm/Metadata/OrmModel.ts +80 -0
  430. package/library/orm/Metadata/ValueConverter.ts +57 -0
  431. package/library/orm/Metadata/conventions.ts +54 -0
  432. package/library/orm/Metadata/decorators.ts +396 -0
  433. package/library/orm/Metadata/types.ts +140 -0
  434. package/library/orm/Providers/OwnedStoreCatalog.reader.ts +1802 -0
  435. package/library/orm/Providers/PostgresDialect.ts +234 -0
  436. package/library/orm/Providers/PostgresProvider.ts +1494 -0
  437. package/library/orm/Providers/PostgresSchema.decoder.ts +107 -0
  438. package/library/orm/Providers/limits.ts +29 -0
  439. package/library/orm/Providers/operationDeadline.ts +43 -0
  440. package/library/orm/Providers/ormOwnedStoreRuntime.ts +19 -0
  441. package/library/orm/Providers/ormTransactionRuntime.ts +117 -0
  442. package/library/orm/Providers/propertyConversion.ts +14 -0
  443. package/library/orm/Providers/resolvedForeignKey.ts +16 -0
  444. package/library/orm/Providers/transactionCallbacks.ts +45 -0
  445. package/library/orm/Providers/transactionOutcome.ts +82 -0
  446. package/library/orm/Providers/transactionScopes.ts +78 -0
  447. package/library/orm/Providers/types.ts +207 -0
  448. package/library/orm/Query/DbSet.ts +78 -0
  449. package/library/orm/Query/EntityQuery.ts +247 -0
  450. package/library/orm/Query/ImmediateMutations.ts +226 -0
  451. package/library/orm/Query/IncludeLoader.ts +200 -0
  452. package/library/orm/Query/QueryPlan.ts +55 -0
  453. package/library/orm/Query/SqlTranslator.ts +195 -0
  454. package/library/orm/Query/conditions.ts +127 -0
  455. package/library/orm/Query/materialize.ts +39 -0
  456. package/library/orm/README.md +256 -0
  457. package/library/orm/Repository/README.md +684 -0
  458. package/library/orm/Repository/Repository.ts +68 -0
  459. package/library/orm/Saving/CommandBuilder.ts +139 -0
  460. package/library/orm/Saving/ExecutionStrategy.ts +128 -0
  461. package/library/orm/Saving/SaveExecutor.ts +413 -0
  462. package/library/orm/Saving/applyConventions.ts +64 -0
  463. package/library/orm/Schema/CheckExpression.ts +327 -0
  464. package/library/orm/Schema/ExactSchemaVerifier.ts +168 -0
  465. package/library/orm/Schema/ExpectedSchema.ts +43 -0
  466. package/library/orm/Schema/MigrationRunner.ts +146 -0
  467. package/library/orm/Schema/Migrator.ts +73 -0
  468. package/library/orm/Schema/OrmOwnedStore.ts +123 -0
  469. package/library/orm/Schema/OwnedStoreAdmission.ts +752 -0
  470. package/library/orm/Schema/OwnedStoreCanonical.ts +273 -0
  471. package/library/orm/Schema/OwnedStoreCatalog.ts +1325 -0
  472. package/library/orm/Schema/SafeAdditiveSchema.ts +100 -0
  473. package/library/orm/Schema/SchemaAdmission.ts +62 -0
  474. package/library/orm/Schema/SchemaDiffer.ts +78 -0
  475. package/library/orm/Schema/formatSchemaDifferences.ts +62 -0
  476. package/library/orm/Schema/introspection.ts +52 -0
  477. package/library/orm/Schema/physicalColumnTypes.ts +37 -0
  478. package/library/orm/Schema/tableKey.ts +26 -0
  479. package/library/orm/Tracking/ChangeTracker.ts +448 -0
  480. package/library/orm/Tracking/EntityState.ts +13 -0
  481. package/library/orm/Transactions/OrmTransaction.ts +30 -0
  482. package/library/orm/Transactions/TransactionScopeCoordinator.ts +377 -0
  483. package/library/orm/errors.ts +190 -0
  484. package/library/orm/index.ts +162 -0
  485. package/library/orm/runtime.ts +14 -0
  486. package/library/redaction/index.ts +166 -0
  487. package/library/ui/UiProfile.ts +77 -0
  488. package/library/ui/compiled-v1.ts +131 -0
  489. package/library/ui/compiler/compileUiSurfaceV1.ts +717 -0
  490. package/library/ui/compiler/contracts-v1.ts +25 -0
  491. package/library/ui/compiler/index.ts +12 -0
  492. package/library/ui/compiler/openApiIndex.ts +246 -0
  493. package/library/ui/defineUiProfile.ts +340 -0
  494. package/library/ui/diagnostics-v1.ts +33 -0
  495. package/library/ui/index.ts +129 -0
  496. package/library/ui/profile-v1.ts +191 -0
  497. package/library/ui/serialization.ts +130 -0
  498. package/library/ui/uiProfileAuthoring.ts +289 -0
  499. package/library/ui/uiProfileAuthoringMetadata.ts +78 -0
  500. package/library/validation/MessageRegistry.ts +144 -0
  501. package/library/validation/README.md +62 -0
  502. package/library/validation/RuleCompiler.ts +183 -0
  503. package/library/validation/RuleEngine.ts +401 -0
  504. package/library/validation/SPEC.md +579 -0
  505. package/library/validation/ValidationError.ts +37 -0
  506. package/library/validation/ValidationResult.ts +63 -0
  507. package/library/validation/Validator.ts +101 -0
  508. package/library/validation/index.ts +16 -0
  509. package/library/validation/metadata.ts +69 -0
  510. package/library/validation/modelValidatorAdapter.ts +119 -0
  511. package/library/validation/types/ValidationCode.ts +69 -0
  512. package/library/validation/types/ValidatorOptions.ts +153 -0
  513. package/package.json +80 -0
@@ -0,0 +1,966 @@
1
+ import { resolveGeneratedBindings } from "../../http/Binding/autoBindings";
2
+ import type { ParameterBinding } from "../../http/Binding/bindings";
3
+ import {
4
+ controllerMetaOf,
5
+ type ActionMeta,
6
+ type ControllerMeta,
7
+ type RouteDeclaration,
8
+ } from "../../http/Decorators/metadata";
9
+ import { getGeneratedOpenApiSchemaName } from "../../http/OpenApi/generatedOpenApiRegistry";
10
+ import type { OpenApiSchema } from "../../../library/openapi";
11
+ import {
12
+ listFilterCapabilitiesV1,
13
+ requestEntitySchemaV1,
14
+ responseEntitySchemaV1,
15
+ } from "../../../library/ui/compiler/openApiIndex";
16
+ import {
17
+ UI_PROFILE_V1_API_VERSION,
18
+ UI_PROFILE_V1_KIND,
19
+ compileUiSurfaceV1,
20
+ defineUiProfile,
21
+ indexOpenApiV1,
22
+ normalizeUiJsonObject,
23
+ uiDiagnosticV1,
24
+ uiProfileAuthoringMetadataOf,
25
+ type AnyUiProfileAuthoringOptions,
26
+ type UiCustomPageAuthoring,
27
+ type UiCustomPagesProfileAuthoringOptions,
28
+ type UiControllerClass,
29
+ type UiEndpointReference,
30
+ type UiDiagnosticV1,
31
+ type UiFieldSelection,
32
+ type UiFormFieldMap,
33
+ type UiProfileAuthoringOptions,
34
+ type UiFieldProfileV1,
35
+ type UiOpenApiIndexV1,
36
+ type UiOpenApiOperationV1,
37
+ type UiProfileV1,
38
+ type CompiledUiSurfaceLinksV1,
39
+ type CompiledUiSurfaceV1,
40
+ type UiSchemaElementV1,
41
+ } from "../../../library/ui";
42
+
43
+ export interface ResolveUiProfileAuthoringV1Options {
44
+ readonly declaration: object;
45
+ readonly openApi: OpenApiSchema;
46
+ /** Texts generated when a profile does not set its own; default English. */
47
+ readonly labels?: UiLabels;
48
+ }
49
+
50
+ /** Default texts of generated UI profiles (detail section, form and delete titles). */
51
+ export interface UiLabels {
52
+ readonly detailSection: string;
53
+ readonly create: (resource: string) => string;
54
+ readonly edit: (resource: string) => string;
55
+ readonly delete: string;
56
+ readonly deleteConfirm: (resource: string) => string;
57
+ /** Resource name when a profile has no title. */
58
+ readonly resource: string;
59
+ /** Locale used to lower-case the resource title inside the texts above. */
60
+ readonly locale: string;
61
+ }
62
+
63
+ export const EN_UI_LABELS: UiLabels = Object.freeze({
64
+ detailSection: "General",
65
+ create: (resource: string) => `Create ${resource}`,
66
+ edit: (resource: string) => `Edit ${resource}`,
67
+ delete: "Delete",
68
+ deleteConfirm: (resource: string) => `Delete ${resource}?`,
69
+ resource: "resource",
70
+ locale: "en-US",
71
+ });
72
+
73
+ /** Russian texts: `runApp(App, { ui: { labels: RU_UI_LABELS, ... } })`. */
74
+ export const RU_UI_LABELS: UiLabels = Object.freeze({
75
+ detailSection: "Основное",
76
+ create: (resource: string) => `Создать ${resource}`,
77
+ edit: (resource: string) => `Редактировать ${resource}`,
78
+ delete: "Удалить",
79
+ deleteConfirm: (resource: string) => `Удалить ${resource}?`,
80
+ resource: "ресурс",
81
+ locale: "ru-RU",
82
+ });
83
+
84
+ export type ResolveUiProfileAuthoringV1Result =
85
+ | {
86
+ readonly ok: true;
87
+ readonly profiles: readonly UiProfileV1[];
88
+ readonly diagnostics: readonly UiDiagnosticV1[];
89
+ }
90
+ | {
91
+ readonly ok: false;
92
+ readonly diagnostics: readonly UiDiagnosticV1[];
93
+ };
94
+
95
+ /** @internal Resolved, JSON-safe profiles collected from the active module graph. */
96
+ export class UiProfileV1Registry {
97
+ readonly #profiles: readonly UiProfileV1[];
98
+ readonly #openApi?: OpenApiSchema;
99
+
100
+ public constructor(profiles: readonly UiProfileV1[] = [], openApi?: OpenApiSchema) {
101
+ this.#profiles = Object.freeze([...profiles]);
102
+ this.#openApi = openApi;
103
+ }
104
+
105
+ public bySurface(surface: string): readonly UiProfileV1[] {
106
+ return this.#profiles.filter((profile) => profile.metadata.surface === surface);
107
+ }
108
+
109
+ /** Canonical OpenAPI projection used to resolve these profiles. */
110
+ public openApi(): OpenApiSchema {
111
+ if (this.#openApi === undefined) {
112
+ throw new TypeError("UiProfileV1Registry has no HTTP OpenAPI catalog in worker mode.");
113
+ }
114
+ return this.#openApi;
115
+ }
116
+
117
+ /** Compiles a deterministic static surface without applying request policy. */
118
+ public compile(
119
+ surface: string,
120
+ links: CompiledUiSurfaceLinksV1,
121
+ openApi: OpenApiSchema = this.openApi(),
122
+ ): CompiledUiSurfaceV1 {
123
+ const result = compileUiSurfaceV1({
124
+ surface,
125
+ profiles: this.bySurface(surface),
126
+ openApi,
127
+ links,
128
+ });
129
+ if (!result.ok) {
130
+ const details = result.diagnostics.map((item) => `${item.code}: ${item.message}`).join("\n");
131
+ throw new TypeError(`UI surface ${surface} compilation failed:\n${details}`);
132
+ }
133
+ return result.document;
134
+ }
135
+ }
136
+
137
+ type CrudRole = "list" | "read" | "create" | "update" | "delete";
138
+
139
+ interface ResolvedUiAction {
140
+ readonly role: CrudRole;
141
+ readonly controller: UiControllerClass;
142
+ readonly methodName: string | symbol;
143
+ readonly action: ActionMeta;
144
+ readonly route: RouteDeclaration;
145
+ readonly operation: UiOpenApiOperationV1;
146
+ }
147
+
148
+ type UiActionCandidate = Omit<ResolvedUiAction, "controller" | "operation">;
149
+
150
+ const CRUD_ROLES: readonly CrudRole[] = Object.freeze([
151
+ "list",
152
+ "read",
153
+ "create",
154
+ "update",
155
+ "delete",
156
+ ]);
157
+
158
+ /**
159
+ * Resolves a process-local @UiProfile declaration into the JSON-safe V1 IR.
160
+ * Controller/model references are consumed here and never reach the wire.
161
+ */
162
+ export function resolveUiProfileAuthoringV1(
163
+ options: ResolveUiProfileAuthoringV1Options,
164
+ ): ResolveUiProfileAuthoringV1Result {
165
+ const labels = options.labels ?? EN_UI_LABELS;
166
+ const titleOf = (value: string) => lowercaseTitle(value, labels);
167
+ const diagnostics: UiDiagnosticV1[] = [];
168
+ const metadata = uiProfileAuthoringMetadataOf(options.declaration);
169
+ if (metadata === undefined) {
170
+ return failure([
171
+ uiDiagnosticV1(
172
+ "error",
173
+ "UIV1_AUTHORING_DECLARATION_REQUIRED",
174
+ "UI profile declaration must be a class decorated with @UiProfile().",
175
+ ),
176
+ ]);
177
+ }
178
+
179
+ const authoring = metadata.profile;
180
+ if (isCustomPagesAuthoring(authoring)) {
181
+ return resolveCustomPagesAuthoring(metadata.targetName, authoring, options.openApi);
182
+ }
183
+ const resourceAuthoring = authoring as UiProfileAuthoringOptions<any, any, any, any, any>;
184
+ const controllerMeta = controllerMetaOf(resourceAuthoring.controller);
185
+ if (!controllerMeta?.isController) {
186
+ return failure([
187
+ uiDiagnosticV1(
188
+ "error",
189
+ "UIV1_CONTROLLER_REQUIRED",
190
+ `${metadata.targetName} references a class that is not decorated with @Controller().`,
191
+ { profile: metadata.targetName },
192
+ ),
193
+ ]);
194
+ }
195
+
196
+ const openApiResult = indexOpenApiV1(options.openApi);
197
+ diagnostics.push(...openApiResult.diagnostics);
198
+ const resolvedActions = resolveCrudActions(
199
+ metadata.targetName,
200
+ resourceAuthoring,
201
+ controllerMeta,
202
+ openApiResult.index,
203
+ diagnostics,
204
+ );
205
+
206
+ const responseSchema = resolveAndValidateResponseSchema(
207
+ metadata.targetName,
208
+ resourceAuthoring,
209
+ resolvedActions,
210
+ openApiResult.index,
211
+ diagnostics,
212
+ );
213
+
214
+ validateRequestAssertions(metadata.targetName, resourceAuthoring, resolvedActions, diagnostics);
215
+ if (hasErrors(diagnostics)) {
216
+ return failure(diagnostics);
217
+ }
218
+
219
+ const surface = normalizeSurface(resourceAuthoring.surface, metadata.targetName, diagnostics);
220
+ const resourceId = resourceIdOf(resourceAuthoring.id, controllerMeta, metadata.targetName, diagnostics);
221
+ if (hasErrors(diagnostics) || surface === undefined || resourceId === undefined) {
222
+ return failure(diagnostics);
223
+ }
224
+
225
+ const responseFields = readableScalarFields(responseSchema, openApiResult.index);
226
+ const columns = resourceAuthoring.list?.columns === undefined
227
+ ? responseFields
228
+ : authoringFields(resourceAuthoring.list.columns);
229
+
230
+ const listAction = resolvedActions.get("list");
231
+ const createAction = resolvedActions.get("create");
232
+ const updateAction = resolvedActions.get("update");
233
+ const readAction = resolvedActions.get("read");
234
+ const deleteAction = resolvedActions.get("delete");
235
+ const listFilters = listAction === undefined
236
+ ? []
237
+ : [...listFilterCapabilitiesV1(listAction.operation).keys()].sort();
238
+
239
+ const profiles: UiProfileV1[] = [];
240
+ for (const targetSurface of [surface]) {
241
+ const profile = defineUiProfile({
242
+ apiVersion: UI_PROFILE_V1_API_VERSION,
243
+ kind: UI_PROFILE_V1_KIND,
244
+ metadata: {
245
+ name: `${metadata.targetName}:${targetSurface}`,
246
+ surface: targetSurface,
247
+ owner: metadata.targetName,
248
+ },
249
+ spec: {
250
+ resources: [{
251
+ id: resourceId,
252
+ ...(resourceAuthoring.title !== undefined ? { title: resourceAuthoring.title } : {}),
253
+ ...(resourceAuthoring.singularTitle !== undefined ? { singularTitle: resourceAuthoring.singularTitle } : {}),
254
+ ...(resourceAuthoring.navigation !== undefined ? { navigation: resourceAuthoring.navigation } : {}),
255
+ operations: {
256
+ ...(listAction !== undefined ? { list: operationRef(listAction) } : {}),
257
+ ...(readAction !== undefined ? { read: operationRef(readAction) } : {}),
258
+ ...(createAction !== undefined ? { create: operationRef(createAction) } : {}),
259
+ ...(updateAction !== undefined ? { update: operationRef(updateAction) } : {}),
260
+ ...(deleteAction !== undefined ? { delete: operationRef(deleteAction) } : {}),
261
+ },
262
+ ...(listAction !== undefined
263
+ ? {
264
+ list: {
265
+ columns,
266
+ ...(resourceAuthoring.list?.filters !== undefined
267
+ ? { selectionFields: visibleAuthoringFieldNames(resourceAuthoring.list.filters) }
268
+ : listFilters.length > 0 ? { selectionFields: listFilters } : {}),
269
+ ...(resourceAuthoring.list?.defaultSort !== undefined
270
+ ? { defaultSort: resourceAuthoring.list.defaultSort }
271
+ : {}),
272
+ pageSize: resourceAuthoring.list?.pageSize ?? listDefaultPageSize(listAction) ?? 20,
273
+ ...(resourceAuthoring.list?.title !== undefined ? { title: resourceAuthoring.list.title } : {}),
274
+ },
275
+ }
276
+ : {}),
277
+ ...((readAction !== undefined || listAction !== undefined) && responseFields.length > 0
278
+ ? {
279
+ detail: {
280
+ ...(defaultTitleField(responseFields) !== undefined
281
+ ? { titleField: defaultTitleField(responseFields) }
282
+ : {}),
283
+ sections: [{ id: "main", title: labels.detailSection, fields: responseFields }],
284
+ },
285
+ }
286
+ : {}),
287
+ ...((createAction !== undefined || updateAction !== undefined)
288
+ ? {
289
+ forms: {
290
+ ...(createAction !== undefined
291
+ ? {
292
+ create: {
293
+ title: resourceAuthoring.create?.title
294
+ ?? labels.create(titleOf(resourceAuthoring.singularTitle ?? resourceAuthoring.title ?? resourceId)),
295
+ uiSchema: formUiSchema(
296
+ resourceAuthoring.create?.fields,
297
+ requestEntitySchemaV1(openApiResult.index, createAction.operation),
298
+ ),
299
+ },
300
+ }
301
+ : {}),
302
+ ...(updateAction !== undefined
303
+ ? {
304
+ edit: {
305
+ title: resourceAuthoring.edit?.title
306
+ ?? labels.edit(titleOf(resourceAuthoring.singularTitle ?? resourceAuthoring.title ?? resourceId)),
307
+ uiSchema: formUiSchema(
308
+ resourceAuthoring.edit?.fields,
309
+ requestEntitySchemaV1(openApiResult.index, updateAction.operation),
310
+ ),
311
+ },
312
+ }
313
+ : {}),
314
+ },
315
+ }
316
+ : {}),
317
+ ...(deleteAction !== undefined && resourceAuthoring.delete?.hidden !== true
318
+ ? {
319
+ actions: [{
320
+ id: "delete",
321
+ title: resourceAuthoring.delete?.title ?? labels.delete,
322
+ operation: operationRef(deleteAction),
323
+ placements: resourceAuthoring.delete?.placements ?? ["list.row", "detail.header"],
324
+ intent: resourceAuthoring.delete?.intent ?? "danger",
325
+ confirm: resourceAuthoring.delete?.confirm
326
+ ?? labels.deleteConfirm(titleOf(resourceAuthoring.singularTitle ?? resourceAuthoring.title ?? resourceId)),
327
+ refresh: resourceAuthoring.delete?.refresh ?? "resource",
328
+ }],
329
+ }
330
+ : {}),
331
+ }],
332
+ },
333
+ });
334
+ profiles.push(profile);
335
+ }
336
+
337
+ return Object.freeze({
338
+ ok: true,
339
+ profiles: Object.freeze(profiles),
340
+ diagnostics: Object.freeze(diagnostics),
341
+ });
342
+ }
343
+
344
+ function isCustomPagesAuthoring(
345
+ authoring: AnyUiProfileAuthoringOptions,
346
+ ): authoring is UiCustomPagesProfileAuthoringOptions {
347
+ return authoring.customPages !== undefined;
348
+ }
349
+
350
+ function resolveCustomPagesAuthoring(
351
+ profileName: string,
352
+ authoring: UiCustomPagesProfileAuthoringOptions,
353
+ openApi: OpenApiSchema,
354
+ ): ResolveUiProfileAuthoringV1Result {
355
+ const diagnostics: UiDiagnosticV1[] = [];
356
+ const indexed = indexOpenApiV1(openApi);
357
+ diagnostics.push(...indexed.diagnostics);
358
+ const pages = authoring.customPages.map((page) =>
359
+ resolveCustomPage(profileName, page, indexed.index, diagnostics));
360
+ if (hasErrors(diagnostics)) {
361
+ return failure(diagnostics);
362
+ }
363
+ const surface = normalizeSurface(authoring.surface, profileName, diagnostics);
364
+ if (hasErrors(diagnostics) || surface === undefined) {
365
+ return failure(diagnostics);
366
+ }
367
+ const profiles: UiProfileV1[] = [];
368
+ for (const targetSurface of [surface]) {
369
+ profiles.push(defineUiProfile({
370
+ apiVersion: UI_PROFILE_V1_API_VERSION,
371
+ kind: UI_PROFILE_V1_KIND,
372
+ metadata: { name: `${profileName}:${targetSurface}`, surface: targetSurface, owner: profileName },
373
+ spec: {
374
+ resources: [],
375
+ customPages: pages,
376
+ },
377
+ }));
378
+ }
379
+ return Object.freeze({
380
+ ok: true,
381
+ profiles: Object.freeze(profiles),
382
+ diagnostics: Object.freeze(diagnostics),
383
+ });
384
+ }
385
+
386
+ function resolveCustomPage(
387
+ profileName: string,
388
+ page: UiCustomPageAuthoring,
389
+ index: UiOpenApiIndexV1,
390
+ diagnostics: UiDiagnosticV1[],
391
+ ): NonNullable<UiProfileV1["spec"]["customPages"]>[number] {
392
+ const base = {
393
+ id: page.id,
394
+ title: page.title,
395
+ ...(page.navigation !== undefined ? { navigation: page.navigation } : {}),
396
+ };
397
+ switch (page.kind) {
398
+ case "feature":
399
+ return { ...base, kind: page.kind, renderer: page.renderer };
400
+ case "settings": {
401
+ const read = resolveEndpoint(profileName, page.id, "read", page.read, ["GET"], index, diagnostics);
402
+ const update = resolveEndpoint(profileName, page.id, "update", page.update, ["PUT", "PATCH"], index, diagnostics);
403
+ return {
404
+ ...base,
405
+ kind: page.kind,
406
+ operations: {
407
+ read: endpointOperationRef(read),
408
+ update: endpointOperationRef(update),
409
+ },
410
+ ...(page.form !== undefined || update !== undefined
411
+ ? {
412
+ form: {
413
+ ...(page.form?.title !== undefined ? { title: page.form.title } : {}),
414
+ uiSchema: formUiSchema(page.form?.fields, requestEntitySchemaV1(index, update?.operation)),
415
+ },
416
+ }
417
+ : {}),
418
+ };
419
+ }
420
+ case "document": {
421
+ const load = resolveEndpoint(profileName, page.id, "load", page.load, ["GET"], index, diagnostics);
422
+ return { ...base, kind: page.kind, operation: endpointOperationRef(load) };
423
+ }
424
+ case "dashboard":
425
+ return {
426
+ ...base,
427
+ kind: page.kind,
428
+ blocks: page.blocks.map((block) => {
429
+ const load = resolveEndpoint(
430
+ profileName,
431
+ page.id,
432
+ `blocks.${block.id}`,
433
+ block.load,
434
+ ["GET"],
435
+ index,
436
+ diagnostics,
437
+ );
438
+ return {
439
+ id: block.id,
440
+ kind: block.kind,
441
+ title: block.title,
442
+ operation: endpointOperationRef(load),
443
+ };
444
+ }),
445
+ };
446
+ default:
447
+ return assertNeverCustomPageAuthoring(page);
448
+ }
449
+ }
450
+
451
+ function assertNeverCustomPageAuthoring(value: never): never {
452
+ throw new TypeError(`Unsupported UI custom page authoring declaration: ${String(value)}`);
453
+ }
454
+
455
+ interface ResolvedUiEndpoint extends UiActionCandidate {
456
+ readonly controller: UiEndpointReference["controller"];
457
+ readonly operation: UiOpenApiOperationV1;
458
+ }
459
+
460
+ function resolveEndpoint(
461
+ profileName: string,
462
+ pageId: string,
463
+ slot: string,
464
+ endpoint: UiEndpointReference,
465
+ methods: readonly string[],
466
+ index: UiOpenApiIndexV1,
467
+ diagnostics: UiDiagnosticV1[],
468
+ ): ResolvedUiEndpoint | undefined {
469
+ const controllerMeta = controllerMetaOf(endpoint.controller);
470
+ const path = `customPages.${pageId}.${slot}`;
471
+ if (!controllerMeta?.isController) {
472
+ diagnostics.push(uiDiagnosticV1(
473
+ "error",
474
+ "UIV1_ENDPOINT_CONTROLLER_REQUIRED",
475
+ `${profileName} ${path} references a class that is not an HTTP controller.`,
476
+ { profile: profileName, path },
477
+ ));
478
+ return undefined;
479
+ }
480
+ const methodName = controllerMethodName(endpoint.controller, controllerMeta, endpoint);
481
+ const action = methodName === undefined ? undefined : controllerMeta.actions.get(methodName);
482
+ if (methodName === undefined || action === undefined) {
483
+ diagnostics.push(uiDiagnosticV1(
484
+ "error",
485
+ "UIV1_ENDPOINT_ACTION_MISSING",
486
+ `${profileName} ${path} does not reference an action on its controller.`,
487
+ { profile: profileName, path },
488
+ ));
489
+ return undefined;
490
+ }
491
+ const routes = action.routes.filter((route) => methods.includes(route.httpMethod));
492
+ if (routes.length !== 1) {
493
+ diagnostics.push(uiDiagnosticV1(
494
+ "error",
495
+ "UIV1_ENDPOINT_ROUTE_AMBIGUOUS",
496
+ `${profileName} ${path} must resolve to exactly one ${methods.join(" or ")} route.`,
497
+ { profile: profileName, path },
498
+ ));
499
+ return undefined;
500
+ }
501
+ const candidate: UiActionCandidate = {
502
+ role: methods.includes("GET") ? "read" : "update",
503
+ methodName,
504
+ action,
505
+ route: routes[0] as RouteDeclaration,
506
+ };
507
+ const operation = openApiOperationFor(candidate, controllerMeta, index);
508
+ if (operation === undefined) {
509
+ diagnostics.push(uiDiagnosticV1(
510
+ "error",
511
+ "UIV1_HTTP_CATALOG_OPERATION_MISSING",
512
+ `${profileName} ${path} is missing from the OpenAPI projection.`,
513
+ { profile: profileName, path },
514
+ ));
515
+ return undefined;
516
+ }
517
+ const resolved = { ...candidate, controller: endpoint.controller, operation };
518
+ validateEndpointAssertions(profileName, path, endpoint, resolved, index, diagnostics);
519
+ return resolved;
520
+ }
521
+
522
+ function validateEndpointAssertions(
523
+ profileName: string,
524
+ path: string,
525
+ endpoint: UiEndpointReference,
526
+ resolved: ResolvedUiEndpoint,
527
+ index: UiOpenApiIndexV1,
528
+ diagnostics: UiDiagnosticV1[],
529
+ ): void {
530
+ if (endpoint.request !== undefined) {
531
+ const binding = (resolveGeneratedBindings(resolved.controller, resolved.methodName)
532
+ ?? [])
533
+ .find((item) => item.source === "body" || item.source === "list");
534
+ if (binding?.model !== endpoint.request) {
535
+ diagnostics.push(uiDiagnosticV1(
536
+ "error",
537
+ "UIV1_ENDPOINT_REQUEST_MODEL_MISMATCH",
538
+ `${profileName} ${path} request class is not bound to the controller action.`,
539
+ { profile: profileName, path: path + ".request" },
540
+ ));
541
+ }
542
+ }
543
+ if (typeof endpoint.response === "function") {
544
+ const schemaName = getGeneratedOpenApiSchemaName(endpoint.response as abstract new (...args: any[]) => object);
545
+ const expected = schemaName === undefined ? undefined : index.schemas[schemaName];
546
+ const actual = responseEntitySchemaV1(index, resolved.operation);
547
+ if (schemaName === undefined || expected === undefined) {
548
+ diagnostics.push(uiDiagnosticV1(
549
+ "error",
550
+ "UIV1_ENDPOINT_RESPONSE_MODEL_NOT_REGISTERED",
551
+ `${profileName} ${path} response class is not bound to a generated OpenAPI schema.`,
552
+ { profile: profileName, path: path + ".response" },
553
+ ));
554
+ } else if (actual !== expected) {
555
+ diagnostics.push(uiDiagnosticV1(
556
+ "error",
557
+ "UIV1_ENDPOINT_RESPONSE_MODEL_MISMATCH",
558
+ `${profileName} ${path} response class does not match the controller action response.`,
559
+ { profile: profileName, path: path + ".response" },
560
+ ));
561
+ }
562
+ }
563
+ }
564
+
565
+ function controllerMethodName(
566
+ controller: UiEndpointReference["controller"],
567
+ meta: ControllerMeta,
568
+ reference: { readonly action: (...args: any[]) => unknown },
569
+ ): string | symbol | undefined {
570
+ const matches = [...meta.actions.keys()].filter((methodName) =>
571
+ (controller.prototype as Record<string | symbol, unknown>)[methodName] === reference.action);
572
+ return matches.length === 1 ? matches[0] : undefined;
573
+ }
574
+
575
+ function endpointOperationRef(endpoint: ResolvedUiEndpoint | undefined): { readonly operationId: string } {
576
+ return { operationId: endpoint?.operation.operationId ?? "<unresolved>" };
577
+ }
578
+
579
+ function resolveCrudActions(
580
+ profileName: string,
581
+ authoring: UiProfileAuthoringOptions<any, any, any, any, any>,
582
+ controllerMeta: ControllerMeta,
583
+ index: UiOpenApiIndexV1,
584
+ diagnostics: UiDiagnosticV1[],
585
+ ): ReadonlyMap<CrudRole, ResolvedUiAction> {
586
+ const out = new Map<CrudRole, ResolvedUiAction>();
587
+ for (const role of CRUD_ROLES) {
588
+ if (authoring.readonly === true && role !== "list" && role !== "read") {
589
+ continue;
590
+ }
591
+ const explicit = authoring.operations?.[role];
592
+ const candidates = explicit === undefined
593
+ ? conventionalCandidates(role, controllerMeta)
594
+ : explicitCandidates(profileName, role, authoring, controllerMeta, explicit, diagnostics);
595
+ if (candidates.length === 0) {
596
+ continue;
597
+ }
598
+ if (candidates.length > 1) {
599
+ diagnostics.push(uiDiagnosticV1(
600
+ "error",
601
+ "UIV1_CRUD_OPERATION_AMBIGUOUS",
602
+ `${profileName} has ${candidates.length} candidates for ${role}; declare an explicit uiOperation().`,
603
+ { profile: profileName, path: `operations.${role}` },
604
+ ));
605
+ continue;
606
+ }
607
+ const candidate = candidates[0] as UiActionCandidate;
608
+ const operation = openApiOperationFor(candidate, controllerMeta, index);
609
+ if (operation === undefined) {
610
+ diagnostics.push(uiDiagnosticV1(
611
+ "error",
612
+ "UIV1_HTTP_CATALOG_OPERATION_MISSING",
613
+ `OpenAPI projection is missing the resolved ${role} controller action.`,
614
+ { profile: profileName, path: `operations.${role}` },
615
+ ));
616
+ continue;
617
+ }
618
+ out.set(role, Object.freeze({ ...candidate, controller: authoring.controller, operation }));
619
+ }
620
+ return out;
621
+ }
622
+
623
+ function conventionalCandidates(
624
+ role: CrudRole,
625
+ controllerMeta: ControllerMeta,
626
+ ): UiActionCandidate[] {
627
+ const candidates: UiActionCandidate[] = [];
628
+ for (const [methodName, action] of controllerMeta.actions) {
629
+ for (const route of action.routes) {
630
+ if (routeMatchesRole(route, role)) {
631
+ candidates.push({ role, methodName, action, route });
632
+ }
633
+ }
634
+ }
635
+ return candidates;
636
+ }
637
+
638
+ function explicitCandidates(
639
+ profileName: string,
640
+ role: CrudRole,
641
+ authoring: UiProfileAuthoringOptions<any, any, any, any, any>,
642
+ controllerMeta: ControllerMeta,
643
+ explicit: {
644
+ readonly controller: UiEndpointReference["controller"];
645
+ readonly action: (...args: any[]) => unknown;
646
+ },
647
+ diagnostics: UiDiagnosticV1[],
648
+ ): UiActionCandidate[] {
649
+ if (explicit.controller !== authoring.controller) {
650
+ diagnostics.push(uiDiagnosticV1(
651
+ "error",
652
+ "UIV1_OPERATION_CONTROLLER_MISMATCH",
653
+ `${profileName} ${role} operation references another controller.`,
654
+ { profile: profileName, path: `operations.${role}` },
655
+ ));
656
+ return [];
657
+ }
658
+ const methodName = controllerMethodName(explicit.controller, controllerMeta, explicit);
659
+ const action = methodName === undefined ? undefined : controllerMeta.actions.get(methodName);
660
+ if (action === undefined || action.routes.length === 0) {
661
+ diagnostics.push(uiDiagnosticV1(
662
+ "error",
663
+ "UIV1_CONTROLLER_ACTION_MISSING",
664
+ `${profileName} references a controller action without an HTTP route.`,
665
+ { profile: profileName, path: `operations.${role}` },
666
+ ));
667
+ return [];
668
+ }
669
+ const routes = action.routes.filter((route) => methodAllowedForRole(route.httpMethod, role));
670
+ if (routes.length === 0) {
671
+ diagnostics.push(uiDiagnosticV1(
672
+ "error",
673
+ "UIV1_CONTROLLER_ACTION_METHOD_MISMATCH",
674
+ `${profileName} action ${String(methodName)} has no HTTP method compatible with ${role}.`,
675
+ { profile: profileName, path: `operations.${role}` },
676
+ ));
677
+ }
678
+ return routes.map((route) => ({ role, methodName: methodName as string | symbol, action, route }));
679
+ }
680
+
681
+ function routeMatchesRole(route: RouteDeclaration, role: CrudRole): boolean {
682
+ const itemRoute = route.template.split("/").some((segment) => segment.startsWith(":") || segment.startsWith("*"));
683
+ switch (role) {
684
+ case "list": return route.httpMethod === "GET" && !itemRoute;
685
+ case "read": return route.httpMethod === "GET" && itemRoute;
686
+ case "create": return route.httpMethod === "POST" && !itemRoute;
687
+ case "update": return (route.httpMethod === "PUT" || route.httpMethod === "PATCH") && itemRoute;
688
+ case "delete": return route.httpMethod === "DELETE" && itemRoute;
689
+ }
690
+ }
691
+
692
+ function methodAllowedForRole(method: string, role: CrudRole): boolean {
693
+ return routeMatchesRole({ httpMethod: method, template: role === "list" || role === "create" ? "" : ":id" }, role);
694
+ }
695
+
696
+ function openApiOperationFor(
697
+ action: UiActionCandidate,
698
+ controllerMeta: ControllerMeta,
699
+ index: UiOpenApiIndexV1,
700
+ ): UiOpenApiOperationV1 | undefined {
701
+ const suffix = openApiPathSuffix(controllerMeta.prefix, action.route.template);
702
+ const expectedTag = controllerMeta.prefix?.split("/").find((segment) => segment.length > 0);
703
+ const matches = [...index.operations.values()].filter((operation) =>
704
+ operation.method === action.route.httpMethod
705
+ && (operation.path === suffix || operation.path.endsWith(suffix))
706
+ && (expectedTag === undefined || operationTags(operation).includes(expectedTag)));
707
+ return matches.length === 1 ? matches[0] : undefined;
708
+ }
709
+
710
+ function operationTags(operation: UiOpenApiOperationV1): readonly string[] {
711
+ const tags = operation.operation["tags"];
712
+ return Array.isArray(tags) ? tags.filter((tag): tag is string => typeof tag === "string") : [];
713
+ }
714
+
715
+ function openApiPathSuffix(prefix: string | undefined, template: string): string {
716
+ const segments = [prefix ?? "", template]
717
+ .join("/")
718
+ .split("/")
719
+ .filter((segment) => segment.length > 0)
720
+ .map((segment) => {
721
+ const route = /^:([A-Za-z_][A-Za-z0-9_]*)(?:\([^)]+\))?$/.exec(segment);
722
+ if (route !== null) {
723
+ return `{${route[1] as string}}`;
724
+ }
725
+ const wildcard = /^\*([A-Za-z_][A-Za-z0-9_]*)?$/.exec(segment);
726
+ return wildcard === null ? segment : `{${(wildcard[1] as string | undefined) ?? "rest"}}`;
727
+ });
728
+ return `/${segments.join("/")}`;
729
+ }
730
+
731
+ function resolveAndValidateResponseSchema(
732
+ profileName: string,
733
+ authoring: UiProfileAuthoringOptions<any, any, any, any, any>,
734
+ actions: ReadonlyMap<CrudRole, ResolvedUiAction>,
735
+ index: UiOpenApiIndexV1,
736
+ diagnostics: UiDiagnosticV1[],
737
+ ): OpenApiSchema | undefined {
738
+ const sourceAction = actions.get("read") ?? actions.get("list") ?? actions.get("create") ?? actions.get("update");
739
+ const actual = responseEntitySchemaV1(index, sourceAction?.operation);
740
+ const response = authoring.response;
741
+ if (typeof response !== "function") {
742
+ return actual;
743
+ }
744
+ const schemaName = getGeneratedOpenApiSchemaName(response);
745
+ if (schemaName === undefined) {
746
+ diagnostics.push(uiDiagnosticV1(
747
+ "error",
748
+ "UIV1_RESPONSE_MODEL_NOT_REGISTERED",
749
+ `${profileName} response class is not bound to a generated OpenAPI schema. Run bun run di:generate.`,
750
+ { profile: profileName, path: "response" },
751
+ ));
752
+ return actual;
753
+ }
754
+ const expected = index.schemas[schemaName];
755
+ if (expected === undefined) {
756
+ diagnostics.push(uiDiagnosticV1(
757
+ "error",
758
+ "UIV1_RESPONSE_SCHEMA_MISSING",
759
+ `${profileName} generated response schema ${schemaName} is absent from this OpenAPI document.`,
760
+ { profile: profileName, path: "response" },
761
+ ));
762
+ return actual;
763
+ }
764
+ for (const role of ["list", "read", "create", "update"] as const) {
765
+ const action = actions.get(role);
766
+ const roleSchema = responseEntitySchemaV1(index, action?.operation);
767
+ if (roleSchema !== undefined && roleSchema !== expected) {
768
+ diagnostics.push(uiDiagnosticV1(
769
+ "error",
770
+ "UIV1_RESPONSE_MODEL_MISMATCH",
771
+ `${profileName} response class does not match the resolved ${role} response schema.`,
772
+ { profile: profileName, path: `operations.${role}.response` },
773
+ ));
774
+ }
775
+ }
776
+ return expected;
777
+ }
778
+
779
+ function validateRequestAssertions(
780
+ profileName: string,
781
+ authoring: UiProfileAuthoringOptions<any, any, any, any, any>,
782
+ actions: ReadonlyMap<CrudRole, ResolvedUiAction>,
783
+ diagnostics: UiDiagnosticV1[],
784
+ ): void {
785
+ validateRequestAssertion(profileName, "list", "list", authoring.list?.request, actions.get("list"), diagnostics);
786
+ validateRequestAssertion(profileName, "create", "body", authoring.create?.request, actions.get("create"), diagnostics);
787
+ validateRequestAssertion(profileName, "edit", "body", authoring.edit?.request, actions.get("update"), diagnostics);
788
+ }
789
+
790
+ function validateRequestAssertion(
791
+ profileName: string,
792
+ path: string,
793
+ source: "list" | "body",
794
+ expected: unknown,
795
+ action: ResolvedUiAction | undefined,
796
+ diagnostics: UiDiagnosticV1[],
797
+ ): void {
798
+ if (expected === undefined || action === undefined) {
799
+ return;
800
+ }
801
+ const binding = bindingsOf(action).find((item) => item.source === source);
802
+ if (binding?.model !== expected) {
803
+ diagnostics.push(uiDiagnosticV1(
804
+ "error",
805
+ "UIV1_REQUEST_MODEL_MISMATCH",
806
+ `${profileName} ${path} request class is not the model bound to the resolved controller action.`,
807
+ { profile: profileName, path: `${path}.request` },
808
+ ));
809
+ }
810
+ }
811
+
812
+ function bindingsOf(action: ResolvedUiAction): readonly ParameterBinding[] {
813
+ return resolveGeneratedBindings(action.controller, action.methodName)
814
+ ?? [];
815
+ }
816
+
817
+ function readableScalarFields(
818
+ schema: OpenApiSchema | undefined,
819
+ index: UiOpenApiIndexV1,
820
+ ): string[] {
821
+ const properties = record(schema?.["properties"]);
822
+ return Object.keys(properties).filter((name) => {
823
+ const property = resolvePropertySchema(record(properties[name]), index);
824
+ if (property["writeOnly"] === true) {
825
+ return false;
826
+ }
827
+ const type = property["type"];
828
+ return type === "string" || type === "number" || type === "integer" || type === "boolean";
829
+ });
830
+ }
831
+
832
+ function resolvePropertySchema(
833
+ property: Readonly<Record<string, unknown>>,
834
+ index: UiOpenApiIndexV1,
835
+ ): Readonly<Record<string, unknown>> {
836
+ const ref = property["$ref"];
837
+ const prefix = "#/components/schemas/";
838
+ return typeof ref === "string" && ref.startsWith(prefix)
839
+ ? record(index.schemas[ref.slice(prefix.length)])
840
+ : property;
841
+ }
842
+
843
+ function authoringFields(
844
+ fields: UiFieldSelection<any>,
845
+ ): UiFieldProfileV1[] {
846
+ return visibleAuthoringFieldNames(fields);
847
+ }
848
+
849
+ function visibleAuthoringFieldNames(fields: UiFieldSelection<any> | UiFormFieldMap<any>): string[] {
850
+ return Object.entries(fields)
851
+ .filter(([, override]) => record(override)["hidden"] !== true)
852
+ .map(([field]) => field);
853
+ }
854
+
855
+ function formUiSchema(
856
+ fields: UiFormFieldMap<any> | undefined,
857
+ schema: OpenApiSchema | undefined,
858
+ ): UiSchemaElementV1 {
859
+ const names = fields === undefined
860
+ ? Object.keys(record(schema?.["properties"]))
861
+ : visibleAuthoringFieldNames(fields);
862
+ return {
863
+ type: "VerticalLayout",
864
+ elements: names.map((name) => {
865
+ const override = fields === undefined ? {} : record(fields[name]);
866
+ return {
867
+ type: "Control" as const,
868
+ scope: `#/properties/${name}`,
869
+ ...(typeof override["label"] === "string" ? { label: override["label"] } : {}),
870
+ ...(override["options"] !== undefined
871
+ ? { options: normalizeUiJsonObject(record(override["options"]), `uiForm.${name}.options`) }
872
+ : {}),
873
+ };
874
+ }),
875
+ };
876
+ }
877
+
878
+ function listDefaultPageSize(action: ResolvedUiAction): number | undefined {
879
+ const binding = bindingsOf(action).find((item) => item.source === "list");
880
+ return binding?.listOptions?.page?.defaultSize;
881
+ }
882
+
883
+ function operationRef(action: ResolvedUiAction): { readonly operationId: string } {
884
+ return { operationId: action.operation.operationId };
885
+ }
886
+
887
+ function defaultTitleField(fields: readonly string[]): string | undefined {
888
+ for (const candidate of ["name", "title", "label", "id"]) {
889
+ if (fields.includes(candidate)) {
890
+ return candidate;
891
+ }
892
+ }
893
+ return fields[0];
894
+ }
895
+
896
+ function normalizeSurface(
897
+ value: string,
898
+ profileName: string,
899
+ diagnostics: UiDiagnosticV1[],
900
+ ): string | undefined {
901
+ const surface = value.trim();
902
+ if (surface.length === 0) {
903
+ diagnostics.push(uiDiagnosticV1(
904
+ "error",
905
+ "UIV1_SURFACE_REQUIRED",
906
+ `${profileName} must declare one non-empty surface.`,
907
+ { profile: profileName, path: "surface" },
908
+ ));
909
+ return undefined;
910
+ }
911
+ return surface;
912
+ }
913
+
914
+ function resourceIdOf(
915
+ explicit: string | undefined,
916
+ controllerMeta: ControllerMeta,
917
+ profileName: string,
918
+ diagnostics: UiDiagnosticV1[],
919
+ ): string | undefined {
920
+ if (explicit !== undefined) {
921
+ const normalized = explicit.trim();
922
+ if (normalized.length > 0) {
923
+ return normalized;
924
+ }
925
+ diagnostics.push(uiDiagnosticV1(
926
+ "error",
927
+ "UIV1_RESOURCE_ID_INVALID",
928
+ `${profileName} resource id must not be empty.`,
929
+ { profile: profileName, path: "id" },
930
+ ));
931
+ return undefined;
932
+ }
933
+ const id = controllerMeta.prefix?.split("/").filter((segment) =>
934
+ segment.length > 0 && !segment.startsWith(":") && !segment.startsWith("*")).at(-1);
935
+ if (id === undefined) {
936
+ diagnostics.push(uiDiagnosticV1(
937
+ "error",
938
+ "UIV1_RESOURCE_ID_NOT_INFERRED",
939
+ `${profileName} controller prefix has no stable resource segment.`,
940
+ { profile: profileName, path: "controller" },
941
+ ));
942
+ }
943
+ return id;
944
+ }
945
+
946
+ function lowercaseTitle(value: string, labels: UiLabels): string {
947
+ const trimmed = value.trim();
948
+ return trimmed.length === 0 ? labels.resource : `${trimmed[0]?.toLocaleLowerCase(labels.locale) ?? ""}${trimmed.slice(1)}`;
949
+ }
950
+
951
+ function record(value: unknown): Readonly<Record<string, unknown>> {
952
+ return typeof value === "object" && value !== null && !Array.isArray(value)
953
+ ? value as Readonly<Record<string, unknown>>
954
+ : {};
955
+ }
956
+
957
+ function hasErrors(diagnostics: readonly UiDiagnosticV1[]): boolean {
958
+ return diagnostics.some((diagnostic) => diagnostic.severity === "error");
959
+ }
960
+
961
+ function failure(diagnostics: readonly UiDiagnosticV1[]): ResolveUiProfileAuthoringV1Result {
962
+ return Object.freeze({
963
+ ok: false,
964
+ diagnostics: Object.freeze([...diagnostics]),
965
+ });
966
+ }