@cosmicdrift/kumiko-framework 0.1.0

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 (388) hide show
  1. package/README.md +159 -0
  2. package/package.json +91 -0
  3. package/src/__tests__/anonymous-access.integration.ts +325 -0
  4. package/src/__tests__/error-contract.integration.ts +435 -0
  5. package/src/__tests__/field-access.integration.ts +269 -0
  6. package/src/__tests__/full-stack.integration.ts +914 -0
  7. package/src/__tests__/ownership.integration.ts +449 -0
  8. package/src/__tests__/reference-data.integration.ts +198 -0
  9. package/src/__tests__/transition-guard.integration.ts +340 -0
  10. package/src/api/__tests__/api.test.ts +337 -0
  11. package/src/api/__tests__/auth-middleware-transport.test.ts +80 -0
  12. package/src/api/__tests__/auth-routes-cookie.test.ts +179 -0
  13. package/src/api/__tests__/batch.integration.ts +404 -0
  14. package/src/api/__tests__/body-limit.test.ts +88 -0
  15. package/src/api/__tests__/csrf-middleware.test.ts +97 -0
  16. package/src/api/__tests__/dispatcher-live.integration.ts +216 -0
  17. package/src/api/__tests__/metrics-endpoint.test.ts +126 -0
  18. package/src/api/__tests__/nested-write.integration.ts +213 -0
  19. package/src/api/__tests__/readiness.test.ts +76 -0
  20. package/src/api/__tests__/request-id-middleware.test.ts +72 -0
  21. package/src/api/__tests__/sse-broker.test.ts +58 -0
  22. package/src/api/__tests__/sse-route.test.ts +112 -0
  23. package/src/api/anonymous-cookie.ts +60 -0
  24. package/src/api/api-constants.ts +64 -0
  25. package/src/api/auth-middleware.ts +418 -0
  26. package/src/api/auth-routes.ts +982 -0
  27. package/src/api/csrf-middleware.ts +77 -0
  28. package/src/api/index.ts +31 -0
  29. package/src/api/jwt.ts +66 -0
  30. package/src/api/observability-middleware.ts +89 -0
  31. package/src/api/readiness.ts +132 -0
  32. package/src/api/request-context.ts +49 -0
  33. package/src/api/request-id-middleware.ts +50 -0
  34. package/src/api/route-registrars.ts +195 -0
  35. package/src/api/routes.ts +135 -0
  36. package/src/api/server.ts +640 -0
  37. package/src/api/sse-broker.ts +71 -0
  38. package/src/api/sse-route.ts +62 -0
  39. package/src/api/tokens.ts +16 -0
  40. package/src/db/__tests__/apply-entity-event-tenant.integration.ts +159 -0
  41. package/src/db/__tests__/compound-types.test.ts +114 -0
  42. package/src/db/__tests__/connection-options.test.ts +68 -0
  43. package/src/db/__tests__/cursor.test.ts +41 -0
  44. package/src/db/__tests__/db-helpers.test.ts +369 -0
  45. package/src/db/__tests__/dialect-instant.test.ts +50 -0
  46. package/src/db/__tests__/drizzle-helpers.integration.ts +186 -0
  47. package/src/db/__tests__/drizzle-table-types.test.ts +162 -0
  48. package/src/db/__tests__/encryption.test.ts +39 -0
  49. package/src/db/__tests__/event-store-executor-list.integration.ts +313 -0
  50. package/src/db/__tests__/event-store-executor.integration.ts +235 -0
  51. package/src/db/__tests__/implicit-projection-equivalence.integration.ts +304 -0
  52. package/src/db/__tests__/located-timestamp.test.ts +184 -0
  53. package/src/db/__tests__/money.test.ts +199 -0
  54. package/src/db/__tests__/multi-row-insert.integration.ts +76 -0
  55. package/src/db/__tests__/parse-auto-verb.test.ts +70 -0
  56. package/src/db/__tests__/required-not-null-migration-safety.integration.ts +105 -0
  57. package/src/db/__tests__/row-helpers.test.ts +59 -0
  58. package/src/db/__tests__/schema-migration.integration.ts +273 -0
  59. package/src/db/__tests__/table-builder-indexes.test.ts +153 -0
  60. package/src/db/__tests__/table-builder-required.test.ts +216 -0
  61. package/src/db/__tests__/tenant-db.integration.ts +606 -0
  62. package/src/db/__tests__/unique-violation-mapping.integration.ts +166 -0
  63. package/src/db/apply-entity-event.ts +188 -0
  64. package/src/db/assert-exists-in.ts +59 -0
  65. package/src/db/compound-types.ts +47 -0
  66. package/src/db/connection.ts +104 -0
  67. package/src/db/cursor.ts +83 -0
  68. package/src/db/dialect.ts +109 -0
  69. package/src/db/eagerload.ts +174 -0
  70. package/src/db/encryption.ts +39 -0
  71. package/src/db/event-store-executor.ts +906 -0
  72. package/src/db/index.ts +55 -0
  73. package/src/db/located-timestamp.ts +114 -0
  74. package/src/db/money.ts +120 -0
  75. package/src/db/pg-error.ts +46 -0
  76. package/src/db/reference-data.ts +77 -0
  77. package/src/db/row-helpers.ts +53 -0
  78. package/src/db/schema-inspection.ts +25 -0
  79. package/src/db/table-builder.ts +475 -0
  80. package/src/db/tenant-db.ts +434 -0
  81. package/src/engine/__tests__/auth-claims-registrar.test.ts +74 -0
  82. package/src/engine/__tests__/boot-validator-located-timestamps.test.ts +108 -0
  83. package/src/engine/__tests__/boot-validator.test.ts +1865 -0
  84. package/src/engine/__tests__/build-app-schema.test.ts +154 -0
  85. package/src/engine/__tests__/claim-keys.test.ts +274 -0
  86. package/src/engine/__tests__/config-helpers.test.ts +236 -0
  87. package/src/engine/__tests__/effective-features.test.ts +86 -0
  88. package/src/engine/__tests__/engine.test.ts +1461 -0
  89. package/src/engine/__tests__/entity-handlers.test.ts +274 -0
  90. package/src/engine/__tests__/event-helpers.test.ts +68 -0
  91. package/src/engine/__tests__/extends-registrar.test.ts +159 -0
  92. package/src/engine/__tests__/factories-long-text.test.ts +84 -0
  93. package/src/engine/__tests__/factories-time.test.ts +158 -0
  94. package/src/engine/__tests__/field-predicates.test.ts +48 -0
  95. package/src/engine/__tests__/hook-phases.test.ts +132 -0
  96. package/src/engine/__tests__/identifiers.test.ts +35 -0
  97. package/src/engine/__tests__/lifecycle-hooks.test.ts +237 -0
  98. package/src/engine/__tests__/nav.test.ts +267 -0
  99. package/src/engine/__tests__/ownership.test.ts +421 -0
  100. package/src/engine/__tests__/parse-ref-target.test.ts +43 -0
  101. package/src/engine/__tests__/projection-helpers.test.ts +62 -0
  102. package/src/engine/__tests__/projection.test.ts +191 -0
  103. package/src/engine/__tests__/qualified-name.test.ts +264 -0
  104. package/src/engine/__tests__/resolve-config-or-param.test.ts +315 -0
  105. package/src/engine/__tests__/run-in.test.ts +38 -0
  106. package/src/engine/__tests__/schema-builder.test.ts +380 -0
  107. package/src/engine/__tests__/screen.test.ts +408 -0
  108. package/src/engine/__tests__/state-machine.test.ts +148 -0
  109. package/src/engine/__tests__/system-user.test.ts +57 -0
  110. package/src/engine/__tests__/validation-hooks.test.ts +71 -0
  111. package/src/engine/access.ts +23 -0
  112. package/src/engine/boot-validator.ts +1528 -0
  113. package/src/engine/build-app-schema.ts +125 -0
  114. package/src/engine/config-helpers.ts +115 -0
  115. package/src/engine/constants.ts +85 -0
  116. package/src/engine/create-app.ts +98 -0
  117. package/src/engine/define-feature.ts +702 -0
  118. package/src/engine/define-handler.ts +78 -0
  119. package/src/engine/define-roles.ts +19 -0
  120. package/src/engine/effective-features.ts +87 -0
  121. package/src/engine/entity-handlers.ts +364 -0
  122. package/src/engine/event-helpers.ts +73 -0
  123. package/src/engine/factories.ts +328 -0
  124. package/src/engine/feature-ast/__tests__/canonical-form.test.ts +416 -0
  125. package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +197 -0
  126. package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +128 -0
  127. package/src/engine/feature-ast/__tests__/parse.test.ts +888 -0
  128. package/src/engine/feature-ast/__tests__/patch.test.ts +360 -0
  129. package/src/engine/feature-ast/__tests__/patcher.test.ts +469 -0
  130. package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +287 -0
  131. package/src/engine/feature-ast/extractors.ts +2562 -0
  132. package/src/engine/feature-ast/index.ts +105 -0
  133. package/src/engine/feature-ast/parse.ts +369 -0
  134. package/src/engine/feature-ast/patch.ts +525 -0
  135. package/src/engine/feature-ast/patcher.ts +518 -0
  136. package/src/engine/feature-ast/patterns.ts +434 -0
  137. package/src/engine/feature-ast/render.ts +602 -0
  138. package/src/engine/feature-ast/source-location.ts +45 -0
  139. package/src/engine/field-access.ts +120 -0
  140. package/src/engine/index.ts +254 -0
  141. package/src/engine/ownership.ts +337 -0
  142. package/src/engine/parse-ref-target.ts +22 -0
  143. package/src/engine/pattern-library/__tests__/library.test.ts +351 -0
  144. package/src/engine/pattern-library/index.ts +24 -0
  145. package/src/engine/pattern-library/library.ts +1117 -0
  146. package/src/engine/pattern-library/types.ts +255 -0
  147. package/src/engine/projection-helpers.ts +85 -0
  148. package/src/engine/qualified-name.ts +122 -0
  149. package/src/engine/read-claim.ts +31 -0
  150. package/src/engine/registry.ts +1325 -0
  151. package/src/engine/resolve-config-or-param.ts +153 -0
  152. package/src/engine/run-in.ts +29 -0
  153. package/src/engine/schema-builder.ts +175 -0
  154. package/src/engine/screen-filter-ops.ts +51 -0
  155. package/src/engine/state-machine.ts +70 -0
  156. package/src/engine/system-user.ts +32 -0
  157. package/src/engine/types/config.ts +306 -0
  158. package/src/engine/types/event-type-map.ts +37 -0
  159. package/src/engine/types/feature.ts +574 -0
  160. package/src/engine/types/fields.ts +422 -0
  161. package/src/engine/types/handlers.ts +742 -0
  162. package/src/engine/types/hooks.ts +142 -0
  163. package/src/engine/types/http-route.ts +54 -0
  164. package/src/engine/types/identifiers.ts +47 -0
  165. package/src/engine/types/index.ts +208 -0
  166. package/src/engine/types/nav.ts +46 -0
  167. package/src/engine/types/projection.ts +132 -0
  168. package/src/engine/types/relations.ts +51 -0
  169. package/src/engine/types/screen.ts +452 -0
  170. package/src/engine/types/workspace.ts +42 -0
  171. package/src/engine/validation.ts +33 -0
  172. package/src/entrypoint/__tests__/entrypoint-job-wiring.integration.ts +173 -0
  173. package/src/entrypoint/__tests__/split-deploy.integration.ts +297 -0
  174. package/src/entrypoint/index.ts +442 -0
  175. package/src/errors/__tests__/classes.test.ts +371 -0
  176. package/src/errors/__tests__/write-failures.test.ts +109 -0
  177. package/src/errors/classes.ts +249 -0
  178. package/src/errors/i18n/de.yaml +83 -0
  179. package/src/errors/i18n/en.yaml +80 -0
  180. package/src/errors/index.ts +41 -0
  181. package/src/errors/kumiko-error.ts +67 -0
  182. package/src/errors/reasons.ts +36 -0
  183. package/src/errors/serialize.ts +136 -0
  184. package/src/errors/transition-details.ts +30 -0
  185. package/src/errors/write-error-info.ts +123 -0
  186. package/src/errors/zod-bridge.ts +49 -0
  187. package/src/event-store/__tests__/admin-api.integration.ts +361 -0
  188. package/src/event-store/__tests__/event-store.integration.ts +584 -0
  189. package/src/event-store/__tests__/get-stream-version-perf.integration.ts +83 -0
  190. package/src/event-store/__tests__/perf.integration.ts +255 -0
  191. package/src/event-store/__tests__/snapshot.integration.ts +267 -0
  192. package/src/event-store/__tests__/upcaster-dead-letter.integration.ts +204 -0
  193. package/src/event-store/__tests__/upcaster.integration.ts +460 -0
  194. package/src/event-store/admin-api.ts +257 -0
  195. package/src/event-store/archive.ts +106 -0
  196. package/src/event-store/errors.ts +35 -0
  197. package/src/event-store/event-store.ts +405 -0
  198. package/src/event-store/events-schema.ts +90 -0
  199. package/src/event-store/index.ts +50 -0
  200. package/src/event-store/snapshot.ts +210 -0
  201. package/src/event-store/upcaster-dead-letter.ts +119 -0
  202. package/src/event-store/upcaster.ts +147 -0
  203. package/src/files/__tests__/content-disposition.test.ts +123 -0
  204. package/src/files/__tests__/file-field-column.integration.ts +103 -0
  205. package/src/files/__tests__/file-field-pipeline.integration.ts +211 -0
  206. package/src/files/__tests__/file-handle.test.ts +122 -0
  207. package/src/files/__tests__/files.integration.ts +830 -0
  208. package/src/files/__tests__/storage-tracking.integration.ts +153 -0
  209. package/src/files/content-disposition.ts +55 -0
  210. package/src/files/file-handle.ts +63 -0
  211. package/src/files/file-ref-table.ts +22 -0
  212. package/src/files/file-routes.ts +353 -0
  213. package/src/files/in-memory-provider.ts +62 -0
  214. package/src/files/index.ts +29 -0
  215. package/src/files/local-provider.ts +35 -0
  216. package/src/files/storage-tracking.ts +60 -0
  217. package/src/files/types.ts +118 -0
  218. package/src/i18n/__tests__/i18n.test.ts +72 -0
  219. package/src/i18n/index.ts +29 -0
  220. package/src/jobs/__tests__/job-event-trigger.integration.ts +172 -0
  221. package/src/jobs/__tests__/job-multi-trigger.integration.ts +144 -0
  222. package/src/jobs/__tests__/jobs.integration.ts +566 -0
  223. package/src/jobs/index.ts +2 -0
  224. package/src/jobs/job-runner.ts +574 -0
  225. package/src/lifecycle/__tests__/create-test-lifecycle.ts +19 -0
  226. package/src/lifecycle/__tests__/lifecycle-server.integration.ts +108 -0
  227. package/src/lifecycle/__tests__/lifecycle.test.ts +212 -0
  228. package/src/lifecycle/__tests__/signal-handlers.test.ts +106 -0
  229. package/src/lifecycle/index.ts +13 -0
  230. package/src/lifecycle/lifecycle.ts +160 -0
  231. package/src/lifecycle/signal-handlers.ts +62 -0
  232. package/src/logging/__tests__/pino-trace-bridge.test.ts +50 -0
  233. package/src/logging/index.ts +3 -0
  234. package/src/logging/pino-logger.ts +64 -0
  235. package/src/logging/types.ts +7 -0
  236. package/src/migrations/__tests__/compare-snapshots.test.ts +150 -0
  237. package/src/migrations/__tests__/detect-drift.integration.ts +320 -0
  238. package/src/migrations/__tests__/detect-projections-to-rebuild.integration.ts +134 -0
  239. package/src/migrations/__tests__/rebuild-marker.test.ts +79 -0
  240. package/src/migrations/index.ts +28 -0
  241. package/src/migrations/projection-detection.ts +149 -0
  242. package/src/migrations/rebuild-marker.ts +64 -0
  243. package/src/migrations/schema-drift.ts +395 -0
  244. package/src/observability/__tests__/console-provider.test.ts +67 -0
  245. package/src/observability/__tests__/metric-validator.test.ts +87 -0
  246. package/src/observability/__tests__/noop-provider.test.ts +82 -0
  247. package/src/observability/__tests__/observability.integration.ts +559 -0
  248. package/src/observability/__tests__/prometheus-meter.test.ts +144 -0
  249. package/src/observability/__tests__/recording-meter.test.ts +101 -0
  250. package/src/observability/__tests__/recording-tracer.test.ts +110 -0
  251. package/src/observability/__tests__/sensitive-filter.test.ts +98 -0
  252. package/src/observability/console-provider.ts +130 -0
  253. package/src/observability/context.ts +26 -0
  254. package/src/observability/fallback.ts +34 -0
  255. package/src/observability/ids.ts +25 -0
  256. package/src/observability/index.ts +79 -0
  257. package/src/observability/metric-validator.ts +86 -0
  258. package/src/observability/metrics-handle.ts +56 -0
  259. package/src/observability/noop-provider.ts +146 -0
  260. package/src/observability/prometheus-meter.ts +284 -0
  261. package/src/observability/recording-meter.ts +156 -0
  262. package/src/observability/recording-tracer.ts +198 -0
  263. package/src/observability/redis-wrapper.ts +132 -0
  264. package/src/observability/sensitive-filter.ts +108 -0
  265. package/src/observability/standard-metrics.ts +213 -0
  266. package/src/observability/types/index.ts +29 -0
  267. package/src/observability/types/metric.ts +56 -0
  268. package/src/observability/types/provider.ts +32 -0
  269. package/src/observability/types/span.ts +64 -0
  270. package/src/pipeline/__tests__/archive-stream.integration.ts +220 -0
  271. package/src/pipeline/__tests__/auth-claims-resolver.test.ts +279 -0
  272. package/src/pipeline/__tests__/cascade-handler.integration.ts +419 -0
  273. package/src/pipeline/__tests__/cascade-handler.test.ts +52 -0
  274. package/src/pipeline/__tests__/causation-chain.integration.ts +206 -0
  275. package/src/pipeline/__tests__/ctx-bridge.integration.ts +234 -0
  276. package/src/pipeline/__tests__/dispatcher.test.ts +379 -0
  277. package/src/pipeline/__tests__/distributed-lock.integration.ts +67 -0
  278. package/src/pipeline/__tests__/domain-events-projections.integration.ts +323 -0
  279. package/src/pipeline/__tests__/event-dedup.integration.ts +153 -0
  280. package/src/pipeline/__tests__/event-define-event-strict.integration.ts +202 -0
  281. package/src/pipeline/__tests__/event-dispatcher-lifecycle.integration.ts +220 -0
  282. package/src/pipeline/__tests__/event-dispatcher-multi-instance.integration.ts +423 -0
  283. package/src/pipeline/__tests__/event-dispatcher-pg-listen.integration.ts +123 -0
  284. package/src/pipeline/__tests__/event-dispatcher-recovery.integration.ts +202 -0
  285. package/src/pipeline/__tests__/event-dispatcher-second-audit.integration.ts +290 -0
  286. package/src/pipeline/__tests__/event-dispatcher-strict.test.ts +65 -0
  287. package/src/pipeline/__tests__/event-dispatcher.integration.ts +287 -0
  288. package/src/pipeline/__tests__/event-retention.integration.ts +239 -0
  289. package/src/pipeline/__tests__/fetch-for-writing.integration.ts +281 -0
  290. package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +430 -0
  291. package/src/pipeline/__tests__/load-aggregate-query.integration.ts +266 -0
  292. package/src/pipeline/__tests__/msp-error-mode.integration.ts +149 -0
  293. package/src/pipeline/__tests__/msp-multi-hop.integration.ts +228 -0
  294. package/src/pipeline/__tests__/msp-rebuild.integration.ts +368 -0
  295. package/src/pipeline/__tests__/multi-stream-projection.integration.ts +341 -0
  296. package/src/pipeline/__tests__/perf-rebuild.integration.ts +147 -0
  297. package/src/pipeline/__tests__/projection-rebuild.integration.ts +551 -0
  298. package/src/pipeline/__tests__/query-projection.integration.ts +201 -0
  299. package/src/pipeline/__tests__/redis-pipeline.integration.ts +306 -0
  300. package/src/pipeline/append-event-core.ts +117 -0
  301. package/src/pipeline/auth-claims-resolver.ts +103 -0
  302. package/src/pipeline/cascade-handler.ts +113 -0
  303. package/src/pipeline/dispatcher.ts +1585 -0
  304. package/src/pipeline/distributed-lock.ts +37 -0
  305. package/src/pipeline/entity-cache.ts +113 -0
  306. package/src/pipeline/event-consumer-state.ts +108 -0
  307. package/src/pipeline/event-dedup.ts +23 -0
  308. package/src/pipeline/event-dispatcher.ts +1016 -0
  309. package/src/pipeline/event-retention.ts +154 -0
  310. package/src/pipeline/idempotency.ts +76 -0
  311. package/src/pipeline/index.ts +66 -0
  312. package/src/pipeline/lifecycle-pipeline.ts +409 -0
  313. package/src/pipeline/msp-rebuild.ts +242 -0
  314. package/src/pipeline/multi-stream-apply-context.ts +115 -0
  315. package/src/pipeline/projection-rebuild.ts +334 -0
  316. package/src/pipeline/projection-state.ts +72 -0
  317. package/src/pipeline/projections-runner.ts +56 -0
  318. package/src/pipeline/redis-keys.ts +11 -0
  319. package/src/pipeline/system-hooks.ts +190 -0
  320. package/src/random/__tests__/generate.test.ts +149 -0
  321. package/src/random/generate.ts +141 -0
  322. package/src/random/index.ts +8 -0
  323. package/src/random/words.ts +392 -0
  324. package/src/rate-limit/__tests__/dispatcher-l3.integration.ts +111 -0
  325. package/src/rate-limit/__tests__/middleware.integration.ts +189 -0
  326. package/src/rate-limit/__tests__/resolver.integration.ts +189 -0
  327. package/src/rate-limit/bucket.ts +36 -0
  328. package/src/rate-limit/index.ts +14 -0
  329. package/src/rate-limit/middleware.ts +152 -0
  330. package/src/rate-limit/resolver.ts +267 -0
  331. package/src/redis/__tests__/redis-options.test.ts +54 -0
  332. package/src/redis/index.ts +74 -0
  333. package/src/search/__tests__/meilisearch-adapter.integration.ts +236 -0
  334. package/src/search/__tests__/search-adapter.test.ts +256 -0
  335. package/src/search/in-memory-adapter.ts +123 -0
  336. package/src/search/index.ts +12 -0
  337. package/src/search/meilisearch-adapter.ts +106 -0
  338. package/src/search/types.ts +39 -0
  339. package/src/secrets/__tests__/dek-cache.test.ts +213 -0
  340. package/src/secrets/__tests__/env-master-key-provider.test.ts +119 -0
  341. package/src/secrets/__tests__/envelope.test.ts +74 -0
  342. package/src/secrets/__tests__/leak-guard.test.ts +92 -0
  343. package/src/secrets/__tests__/rotation.test.ts +149 -0
  344. package/src/secrets/dek-cache.ts +116 -0
  345. package/src/secrets/env-master-key-provider.ts +162 -0
  346. package/src/secrets/envelope.ts +55 -0
  347. package/src/secrets/index.ts +19 -0
  348. package/src/secrets/leak-guard.ts +87 -0
  349. package/src/secrets/rotation.ts +34 -0
  350. package/src/secrets/types.ts +107 -0
  351. package/src/stack/db.ts +104 -0
  352. package/src/stack/event-collector.ts +23 -0
  353. package/src/stack/index.ts +32 -0
  354. package/src/stack/redis.ts +44 -0
  355. package/src/stack/request-helper.ts +168 -0
  356. package/src/stack/table-helpers.ts +104 -0
  357. package/src/stack/test-stack.ts +357 -0
  358. package/src/stack/test-users.ts +37 -0
  359. package/src/testing/__tests__/e2e-generator.test.ts +230 -0
  360. package/src/testing/__tests__/ensure-entity-table.integration.ts +54 -0
  361. package/src/testing/access-assertions.ts +15 -0
  362. package/src/testing/assertions.ts +35 -0
  363. package/src/testing/e2e-generator.ts +465 -0
  364. package/src/testing/expect-error.ts +25 -0
  365. package/src/testing/handler-context.ts +125 -0
  366. package/src/testing/http-cookies.ts +52 -0
  367. package/src/testing/index.ts +41 -0
  368. package/src/testing/late-bound.ts +39 -0
  369. package/src/testing/mutable-master-key-provider.ts +31 -0
  370. package/src/testing/observability-recorder.ts +54 -0
  371. package/src/testing/shared-entities.ts +49 -0
  372. package/src/testing/utils.ts +1 -0
  373. package/src/testing/wait-for.ts +31 -0
  374. package/src/time/__tests__/polyfill.test.ts +73 -0
  375. package/src/time/__tests__/tz-context.test.ts +121 -0
  376. package/src/time/index.ts +21 -0
  377. package/src/time/polyfill.ts +70 -0
  378. package/src/time/tz-context.ts +107 -0
  379. package/src/ui-types/app-schema.ts +57 -0
  380. package/src/ui-types/index.ts +65 -0
  381. package/src/utils/__tests__/assert.test.ts +17 -0
  382. package/src/utils/__tests__/env-parse.test.ts +54 -0
  383. package/src/utils/assert.ts +18 -0
  384. package/src/utils/env-parse.ts +16 -0
  385. package/src/utils/ids.ts +16 -0
  386. package/src/utils/index.ts +5 -0
  387. package/src/utils/safe-json.ts +30 -0
  388. package/src/utils/serialization.ts +7 -0
@@ -0,0 +1,574 @@
1
+ import type { ZodType, z } from "zod";
2
+ import type { QueryHandlerDefinition, WriteHandlerDefinition } from "../define-handler";
3
+ import type {
4
+ ConfigKeyDefinition,
5
+ ConfigKeyHandle,
6
+ ConfigKeyType,
7
+ JobDefinition,
8
+ JobHandlerFn,
9
+ NotificationDataFn,
10
+ NotificationDefinition,
11
+ NotificationRecipientFn,
12
+ NotificationTemplateFn,
13
+ ReferenceDataDef,
14
+ RegistrarExtensionDef,
15
+ RegistrarExtensionRegistration,
16
+ TranslationKeys,
17
+ TranslationsDef,
18
+ } from "./config";
19
+ import type { EntityDefinition } from "./fields";
20
+ import type {
21
+ AccessRule,
22
+ AuthClaimsFn,
23
+ AuthClaimsHookDef,
24
+ ClaimKeyDefinition,
25
+ ClaimKeyHandle,
26
+ ClaimKeyType,
27
+ EntityRef,
28
+ EventDef,
29
+ EventMigrationDef,
30
+ EventUpcastFn,
31
+ HandlerRef,
32
+ NameOrRef,
33
+ QualifiedEventName,
34
+ QueryHandlerDef,
35
+ QueryHandlerFn,
36
+ RateLimitOption,
37
+ WriteHandlerDef,
38
+ WriteHandlerFn,
39
+ } from "./handlers";
40
+ import type {
41
+ EntityHookMap,
42
+ HookMap,
43
+ HookPhase,
44
+ PostDeleteHookFn,
45
+ PostSaveHookFn,
46
+ PreDeleteHookFn,
47
+ PreQueryHookFn,
48
+ PreSaveHookFn,
49
+ ValidationHookFn,
50
+ } from "./hooks";
51
+ import type { HttpRouteDefinition } from "./http-route";
52
+ import type { NavDefinition } from "./nav";
53
+ import type { MultiStreamProjectionDefinition, ProjectionDefinition } from "./projection";
54
+ import type { EntityRelations, RelationDefinition } from "./relations";
55
+ import type { ScreenDefinition } from "./screen";
56
+ import type { WorkspaceDefinition } from "./workspace";
57
+
58
+ // --- Metrics (declared by features via r.metric()) ---
59
+
60
+ export type FeatureMetricType = "counter" | "histogram" | "gauge";
61
+
62
+ // The user-facing short form written in a feature. The Framework prefixes it
63
+ // with `kumiko_<featureName>_` to produce the fully-qualified Prometheus name.
64
+ export type FeatureMetricDef = {
65
+ readonly shortName: string;
66
+ readonly type: FeatureMetricType;
67
+ readonly description?: string;
68
+ readonly labels?: readonly string[];
69
+ readonly buckets?: readonly number[];
70
+ readonly unit?: string;
71
+ // When true, Framework auto-adds tenant_id to labels (ctx-driven). Default
72
+ // false — cardinality multiplies by tenant count, so opt-in.
73
+ readonly tenantLabel?: boolean;
74
+ };
75
+
76
+ export type MetricOptions = Omit<FeatureMetricDef, "shortName">;
77
+
78
+ // --- Secret Keys (declared by features via r.secret()) ---
79
+
80
+ // A feature-declared secret. The fully-qualified name is
81
+ // `<featureName>:<shortName>` — the Framework prefixes. Ops see the
82
+ // qualified name in list / audit; feature code reads it via
83
+ // ctx.secrets.get(tenantId, SecretKeys.stripeKey) with the typed handle.
84
+ export type SecretKeyDefinition = {
85
+ // Short name inside the feature (e.g. "stripe.apiKey"). Qualified to
86
+ // `<feature>:<shortName>` at registry-build time.
87
+ readonly shortName: string;
88
+ // Qualified name — `<feature>:<shortName>`. Set during registry build.
89
+ readonly qualifiedName: string;
90
+ // i18n label for TenantAdmin UI.
91
+ readonly label: { readonly [locale: string]: string };
92
+ // Optional redaction function. Takes the plaintext, returns the preview
93
+ // shown in list handlers. Default is first-3-chars + bullets.
94
+ readonly redact?: (plaintext: string) => string;
95
+ // Short human hint shown in UI ("Find this in your Stripe dashboard ...").
96
+ readonly hint?: { readonly [locale: string]: string };
97
+ // Per-secret scope. v1 only "tenant" — user / system scopes ship in v2.
98
+ readonly scope: "tenant";
99
+ };
100
+
101
+ export type SecretOptions = Omit<SecretKeyDefinition, "shortName" | "qualifiedName">;
102
+
103
+ // Typed reference returned by r.secret(). Lets feature code pass a
104
+ // strongly-named handle to ctx.secrets.get instead of retyping the
105
+ // qualified string. Parallels ConfigKeyHandle from the config system.
106
+ export type SecretKeyHandle = {
107
+ readonly name: string;
108
+ };
109
+
110
+ // --- Feature Definition (output of defineFeature) ---
111
+
112
+ export type FeatureDefinition = {
113
+ readonly name: string;
114
+ readonly systemScope: boolean;
115
+ // Set from the setup-callback return — typed via `defineFeature<TExports>`.
116
+ // `undefined` for setups that return nothing.
117
+ readonly exports?: unknown;
118
+ readonly requires: readonly string[];
119
+ readonly optionalRequires: readonly string[];
120
+ // Declared via r.toggleable({ default }). Presence makes the feature
121
+ // operator-switchable via the feature-toggles bundled feature; absence
122
+ // means the feature is always-on (e.g. auth, tenant, user — core infra
123
+ // that would brick the system if switchable).
124
+ readonly toggleableDefault?: boolean;
125
+ readonly entities: Readonly<Record<string, EntityDefinition>>;
126
+ readonly relations: Readonly<Record<string, EntityRelations>>;
127
+ readonly writeHandlers: Readonly<Record<string, WriteHandlerDef>>;
128
+ readonly queryHandlers: Readonly<Record<string, QueryHandlerDef>>;
129
+ readonly translations: TranslationKeys;
130
+ readonly hooks: HookMap;
131
+ readonly entityHooks: EntityHookMap;
132
+ readonly configKeys: Readonly<Record<string, ConfigKeyDefinition>>;
133
+ readonly jobs: Readonly<Record<string, JobDefinition>>;
134
+ readonly registrarExtensions: Readonly<Record<string, RegistrarExtensionDef>>;
135
+ readonly extensionUsages: readonly RegistrarExtensionRegistration[];
136
+ readonly referenceData: readonly ReferenceDataDef[];
137
+ readonly notifications: Readonly<Record<string, NotificationDefinition>>;
138
+ readonly events: Readonly<Record<string, EventDef>>;
139
+ // Event schema migrations declared via r.eventMigration(). Keyed by event
140
+ // short-name; each entry carries the step transforms (fromVersion →
141
+ // toVersion). The registry stitches these with the defineEvent-declared
142
+ // current version and exposes a per-qualified-name upcaster chain.
143
+ readonly eventMigrations: Readonly<Record<string, readonly EventMigrationDef[]>>;
144
+ readonly configReads: readonly string[];
145
+ // Handler → entity mapping inferred from the colon convention
146
+ // ("entityName:verb") via tryMapEntity in defineFeature.
147
+ readonly handlerEntityMappings: Readonly<Record<string, string>>;
148
+ // Metrics declared via r.metric(). Short names — Framework prefixes on boot.
149
+ readonly metrics: Readonly<Record<string, FeatureMetricDef>>;
150
+ // Secret keys declared via r.secret(). Short names — Framework prefixes to
151
+ // "<feature>:<short>" during registry build.
152
+ readonly secretKeys: Readonly<Record<string, SecretKeyDefinition>>;
153
+ // Projections declared via r.projection(). Keyed by projection name; executor
154
+ // looks them up by source-entity at write-time.
155
+ readonly projections: Readonly<Record<string, ProjectionDefinition>>;
156
+ // Multi-stream projections — cross-aggregate async read-models. Keyed by
157
+ // short name; the dispatcher wraps each into an EventConsumer with its
158
+ // own cursor.
159
+ readonly multiStreamProjections: Readonly<Record<string, MultiStreamProjectionDefinition>>;
160
+ // Auth-claims hooks declared via r.authClaims(). Executed at login (and
161
+ // switch-tenant) time; their returned records are merged into
162
+ // SessionUser.claims under the auto-prefix "<featureName>:<key>".
163
+ readonly authClaimsHooks: readonly AuthClaimsFn[];
164
+ // Declared claim keys via r.claimKey(). Shorts keyed by their JS-side
165
+ // short name, qualified name qualified at registration time.
166
+ readonly claimKeys: Readonly<Record<string, ClaimKeyDefinition>>;
167
+ // Screen definitions declared via r.screen(). Keyed by the feature-local
168
+ // short id; the registry qualifies to "<feature>:screen:<id>" on boot.
169
+ // Pure data — ui-core + renderer packages interpret; engine only stores
170
+ // and validates entity/field references against the feature's entities.
171
+ readonly screens: Readonly<Record<string, ScreenDefinition>>;
172
+ // Nav entries declared via r.nav(). Keyed by the feature-local short id;
173
+ // registry qualifies to "<feature>:nav:<id>". Flat list — the renderer's
174
+ // resolveNavigation assembles the tree from parent refs at mount time.
175
+ readonly navs: Readonly<Record<string, NavDefinition>>;
176
+ // Workspaces declared via r.workspace(). Keyed by feature-local short id;
177
+ // registry qualifies to "<feature>:workspace:<id>". Pure UI metadata —
178
+ // shellWorkspaces consumes the resolved per-workspace nav list at mount
179
+ // time; engine validates roles + nav refs at boot.
180
+ readonly workspaces: Readonly<Record<string, WorkspaceDefinition>>;
181
+ // HTTP-Routes declared via r.httpRoute(). Index is "METHOD path"
182
+ // (z.B. "GET /feed.xml") — eindeutig pro Feature. Die App-Server-
183
+ // Boot-Stage iteriert getAllHttpRoutes() und mountet jede Route auf
184
+ // den Hono-app (außerhalb /api/*). Pattern symmetrisch zu queryHandlers/
185
+ // writeHandlers — Routes leben mit dem Feature, nicht im Bootstrap.
186
+ readonly httpRoutes: Readonly<Record<string, HttpRouteDefinition>>;
187
+ };
188
+
189
+ // --- Feature Registrar (the "r" object in defineFeature) ---
190
+
191
+ type RefOrRefs = NameOrRef | readonly NameOrRef[];
192
+
193
+ /**
194
+ * `TFeature` is the literal feature-name from `defineFeature("foo", ...)` —
195
+ * default-`string` keeps every existing usage zero-config. Strict-typed
196
+ * features (apps that opt into the literal-name flavour) get propagated
197
+ * through to `defineEvent` so the returned `EventDef.name` is a literal
198
+ * `${CamelToKebab<TFeature>}:event:${CamelToKebab<TInner>}`. That literal
199
+ * threads through `ctx.appendEvent({ type: eventDef.name, ... })`,
200
+ * keeping strict-mode alive even when handlers route via `eventDef.name`
201
+ * instead of hand-typed string literals.
202
+ */
203
+ export type FeatureRegistrar<TFeature extends string = string> = {
204
+ systemScope(): void;
205
+ requires(...featureNames: string[]): void;
206
+ optionalRequires(...featureNames: string[]): void;
207
+ // Declare the feature as operator-togglable. `default` is the effective
208
+ // state when no global-toggle row exists. Must be called at most once per
209
+ // feature; calling on an always-on feature (e.g. auth/tenant/user) is a
210
+ // bug the registry catches at boot.
211
+ toggleable(options: { default: boolean }): void;
212
+
213
+ entity(name: string, definition: EntityDefinition): EntityRef;
214
+
215
+ writeHandler<TName extends string, TSchema extends ZodType>(
216
+ def: WriteHandlerDefinition<TName, TSchema>,
217
+ ): HandlerRef;
218
+ writeHandler<TSchema extends ZodType>(
219
+ name: string,
220
+ schema: TSchema,
221
+ handler: WriteHandlerFn<z.infer<TSchema>>,
222
+ options?: { access?: AccessRule; rateLimit?: RateLimitOption },
223
+ ): HandlerRef;
224
+
225
+ queryHandler<TName extends string, TSchema extends ZodType>(
226
+ def: QueryHandlerDefinition<TName, TSchema>,
227
+ ): HandlerRef;
228
+ queryHandler<TSchema extends ZodType>(
229
+ name: string,
230
+ schema: TSchema,
231
+ handler: QueryHandlerFn<z.infer<TSchema>>,
232
+ options?: { access?: AccessRule; rateLimit?: RateLimitOption },
233
+ ): HandlerRef;
234
+
235
+ relation(entity: NameOrRef, relationName: string, definition: RelationDefinition): void;
236
+
237
+ hook(type: "validation", target: RefOrRefs, fn: ValidationHookFn): void;
238
+ hook(type: "preSave", target: RefOrRefs, fn: PreSaveHookFn): void;
239
+ hook(
240
+ type: "postSave",
241
+ target: RefOrRefs,
242
+ fn: PostSaveHookFn,
243
+ options?: { phase?: HookPhase },
244
+ ): void;
245
+ // preDelete always runs in-transaction (it guards the delete — there is no
246
+ // meaningful "after" for a pre-hook). No phase option.
247
+ hook(type: "preDelete", target: RefOrRefs, fn: PreDeleteHookFn): void;
248
+ hook(
249
+ type: "postDelete",
250
+ target: RefOrRefs,
251
+ fn: PostDeleteHookFn,
252
+ options?: { phase?: HookPhase },
253
+ ): void;
254
+ hook(type: "preQuery", target: RefOrRefs, fn: PreQueryHookFn): void;
255
+
256
+ entityHook(
257
+ type: "postSave",
258
+ entity: NameOrRef,
259
+ fn: PostSaveHookFn,
260
+ options?: { phase?: HookPhase },
261
+ ): void;
262
+ entityHook(type: "preDelete", entity: NameOrRef, fn: PreDeleteHookFn): void;
263
+ entityHook(
264
+ type: "postDelete",
265
+ entity: NameOrRef,
266
+ fn: PostDeleteHookFn,
267
+ options?: { phase?: HookPhase },
268
+ ): void;
269
+
270
+ // Returns a handle map keyed exactly like the input. Pass any handle to
271
+ // `ctx.config(handle)` to get the value type narrowed by the key's `type`.
272
+ config<TKeys extends Readonly<Record<string, ConfigKeyDefinition<ConfigKeyType>>>>(definition: {
273
+ readonly keys: TKeys;
274
+ }): { readonly [K in keyof TKeys]: ConfigKeyHandle<TKeys[K]["type"]> };
275
+
276
+ job(name: string, options: Omit<JobDefinition, "name" | "handler">, handler: JobHandlerFn): void;
277
+
278
+ notification(
279
+ name: string,
280
+ definition: {
281
+ readonly trigger: { readonly on: NameOrRef };
282
+ readonly recipient: NotificationRecipientFn;
283
+ readonly data: NotificationDataFn;
284
+ readonly templates?: Readonly<Record<string, NotificationTemplateFn>>;
285
+ },
286
+ ): void;
287
+
288
+ translations(def: TranslationsDef): void;
289
+
290
+ // Register an event payload shape. Returns the qualified def so callers
291
+ // can pass `.name` to ctx.appendEvent without hand-building the
292
+ // "<feature>:event:<short>" string.
293
+ //
294
+ // `options.version` declares the CURRENT schema generation. Defaults to 1
295
+ // on first registration. When you bump the payload shape, raise version
296
+ // AND register r.eventMigration(shortName, N, N+1, transform) — the
297
+ // framework refuses to boot if the chain from 1 → version has gaps.
298
+ defineEvent<const TInner extends string, TPayload>(
299
+ name: TInner,
300
+ schema: ZodType<TPayload>,
301
+ options?: { readonly version?: number },
302
+ ): EventDef<TPayload, QualifiedEventName<TFeature, TInner>>;
303
+
304
+ // Register a step-wise payload transform for event-schema evolution.
305
+ // `eventName` is the SHORT name (same as defineEvent). `toVersion` must
306
+ // be `fromVersion + 1` — chain larger jumps by registering each step.
307
+ // Transforms are pure functions: old payload in, new payload out. They
308
+ // run once per read (not once per event persisted), so keep them cheap.
309
+ eventMigration(
310
+ eventName: string,
311
+ fromVersion: number,
312
+ toVersion: number,
313
+ transform: EventUpcastFn,
314
+ ): void;
315
+
316
+ readsConfig(...qualifiedKeys: string[]): void;
317
+
318
+ referenceData(
319
+ entity: NameOrRef,
320
+ data: readonly Record<string, unknown>[],
321
+ options?: { upsertKey?: string },
322
+ ): void;
323
+
324
+ extendsRegistrar(name: string, def: RegistrarExtensionDef): void;
325
+
326
+ useExtension(extensionName: string, entity: NameOrRef, options?: Record<string, unknown>): void;
327
+
328
+ // Declare a metric. Short name (without kumiko_<feature>_ prefix) — Framework
329
+ // qualifies it on boot. Validation (snake_case + typ-suffix) runs at boot.
330
+ // Usage at runtime: ctx.metrics.inc("created_total", { status: "new" }).
331
+ metric(shortName: string, options: MetricOptions): void;
332
+
333
+ // Declare a secret key. Qualified name follows "<feature>:secret:<kebab>"
334
+ // via the QN helper. Returns a typed handle so feature code can pass it
335
+ // to ctx.secrets.get without retyping the qualified string — same
336
+ // ergonomics as r.config's handle.
337
+ secret(shortName: string, options: SecretOptions): SecretKeyHandle;
338
+
339
+ // Register a projection driven by events of one or more source entities.
340
+ // The runtime fires projection.apply[event.type] inside the event-store's
341
+ // transaction, so projections stay consistent with the events that feed them.
342
+ projection(definition: ProjectionDefinition): void;
343
+
344
+ // Register a cross-aggregate async projection. The event-dispatcher owns
345
+ // delivery via a dedicated cursor — at-least-once, strictly-ordered by
346
+ // events.id. Handlers must be idempotent. Marten's MultiStreamProjection
347
+ // equivalent: customer billing summaries, cross-feature audit views,
348
+ // saga state machines where a single view spans many aggregate types.
349
+ // Omit `table` for pure side-effect handlers (notifications, webhooks,
350
+ // external-system sync) — the dispatcher still delivers at-least-once with
351
+ // per-consumer ordering and dead-letter behaviour.
352
+ multiStreamProjection(definition: MultiStreamProjectionDefinition): void;
353
+
354
+ // Register a function that contributes claims into SessionUser.claims at
355
+ // login time. Multiple features (and multiple calls within one feature)
356
+ // are allowed; returns are merged. Keys are auto-prefixed with the feature
357
+ // name ("<feature>:<key>") — cross-feature collisions are impossible by
358
+ // construction. Same-feature duplicate keys follow last-wins.
359
+ //
360
+ // Hooks run in parallel. If one throws, the error is logged and that
361
+ // feature's claims are simply missing from the merged record — login
362
+ // still succeeds. This is a deliberate best-effort policy: identity-facts
363
+ // are convenience, not access-gates (that's what `roles` + field-access
364
+ // rules are for).
365
+ authClaims(fn: AuthClaimsFn): void;
366
+
367
+ // Declare a claim key. Qualified name follows "<feature>:<kebab-short>"
368
+ // via the QN helper — same convention as r.secret / r.config. Returns a
369
+ // typed handle so feature code can pass it to `readClaim(user, handle)`
370
+ // without retyping the qualified string and with the right narrowed
371
+ // return type.
372
+ //
373
+ // Declaring claim keys also turns on a runtime check: when the feature's
374
+ // r.authClaims hooks return an inner-key not in the declared list, the
375
+ // resolver logs a warning (the claim still lands in the JWT — declared
376
+ // or not — so strict-mode isn't on; this is typo-drift protection).
377
+ claimKey<T extends ClaimKeyType>(
378
+ shortName: string,
379
+ options: { readonly type: T },
380
+ ): ClaimKeyHandle<T>;
381
+
382
+ // Register a screen. The id is the feature-local short name (kebab-case);
383
+ // the registry qualifies to "<feature>:screen:<id>". Boot-validation checks
384
+ // that entity-bound screens reference a registered entity and that the
385
+ // columns / form-field refs name real fields — cross-feature component-QN
386
+ // validation (r.uiComponent) comes in M4/M5.
387
+ screen(definition: ScreenDefinition): void;
388
+
389
+ // Register a nav entry. The id is the feature-local short name (kebab-case);
390
+ // the registry qualifies to "<feature>:nav:<id>". Boot-validation checks
391
+ // that `screen` and `parent` refs exist (cross-feature QNs allowed) and
392
+ // that parent chains don't contain cycles.
393
+ nav(definition: NavDefinition): void;
394
+
395
+ // Register a workspace — a persona-/role-scoped UI surface. Pure UI
396
+ // composition; the registry qualifies the short id to
397
+ // "<feature>:workspace:<id>". Boot-validation checks that any nav refs
398
+ // exist, that workspace ids referenced from r.nav() are real, and that
399
+ // at most one workspace per app declares `default: true`.
400
+ workspace(definition: WorkspaceDefinition): void;
401
+
402
+ // Register an HTTP-route owned by this feature. The route is mounted
403
+ // outside the dispatcher pipeline (= außerhalb /api/write|query|batch),
404
+ // direkt an die app — Use-Case: RSS/Atom-Feeds, OG-Images, OpenAPI-Specs.
405
+ // Boot-validation rejects duplicate "method path"-Combinations.
406
+ // Symmetric to queryHandler/writeHandler — Routes leben mit dem Feature,
407
+ // nicht im Bootstrap. Escape-hatch für nicht-feature-bound Routes
408
+ // bleibt runProdApp.extraRoutes.
409
+ httpRoute(definition: HttpRouteDefinition): void;
410
+ };
411
+
412
+ // --- Registry (created from features) ---
413
+
414
+ export type Registry = {
415
+ readonly features: ReadonlyMap<string, FeatureDefinition>;
416
+
417
+ getFeature(name: string): FeatureDefinition | undefined;
418
+ getEntity(name: string): EntityDefinition | undefined;
419
+ getWriteHandler(name: string): WriteHandlerDef | undefined;
420
+ getQueryHandler(name: string): QueryHandlerDef | undefined;
421
+ getSearchableFields(entityName: string): readonly string[];
422
+ getSortableFields(entityName: string): readonly string[];
423
+ getRelations(entityName: string): EntityRelations;
424
+ getSearchIncludes(entityName: string): ReadonlyMap<string, readonly string[]>;
425
+ getIncomingRelations(entityName: string): ReadonlyArray<{
426
+ sourceEntity: string;
427
+ relationName: string;
428
+ relation: RelationDefinition;
429
+ }>;
430
+ // Hook getters — pass `effectiveFeatures` to drop hooks registered by
431
+ // globally-disabled features. Omit the arg to get all hooks (legacy
432
+ // callers + places where the feature-toggles feature isn't wired).
433
+ getPreSaveHooks(name: string, effectiveFeatures?: ReadonlySet<string>): readonly PreSaveHookFn[];
434
+ getPostSaveHooks(
435
+ name: string,
436
+ phase?: HookPhase,
437
+ effectiveFeatures?: ReadonlySet<string>,
438
+ ): readonly PostSaveHookFn[];
439
+ getPreDeleteHooks(
440
+ name: string,
441
+ phase?: HookPhase,
442
+ effectiveFeatures?: ReadonlySet<string>,
443
+ ): readonly PreDeleteHookFn[];
444
+ getPostDeleteHooks(
445
+ name: string,
446
+ phase?: HookPhase,
447
+ effectiveFeatures?: ReadonlySet<string>,
448
+ ): readonly PostDeleteHookFn[];
449
+ getPreQueryHooks(
450
+ name: string,
451
+ effectiveFeatures?: ReadonlySet<string>,
452
+ ): readonly PreQueryHookFn[];
453
+ getEntityPostSaveHooks(
454
+ entityName: string,
455
+ phase?: HookPhase,
456
+ effectiveFeatures?: ReadonlySet<string>,
457
+ ): readonly PostSaveHookFn[];
458
+ getEntityPreDeleteHooks(
459
+ entityName: string,
460
+ phase?: HookPhase,
461
+ effectiveFeatures?: ReadonlySet<string>,
462
+ ): readonly PreDeleteHookFn[];
463
+ getEntityPostDeleteHooks(
464
+ entityName: string,
465
+ phase?: HookPhase,
466
+ effectiveFeatures?: ReadonlySet<string>,
467
+ ): readonly PostDeleteHookFn[];
468
+ getHandlerEntity(qualifiedHandler: string): string | undefined;
469
+ isHandlerSystemScoped(qualifiedHandler: string): boolean;
470
+ getHandlerFeature(qualifiedHandler: string): string | undefined;
471
+ // True iff at least one registered handler declares a `rateLimit`
472
+ // option. Pre-computed at registry-build so the boot path can skip
473
+ // wiring the RateLimitResolver (and its Lua-script registration on
474
+ // Redis) entirely when nobody opted in. Per-request cost stays zero
475
+ // for apps that don't use the feature.
476
+ hasRateLimitedHandler(): boolean;
477
+ // All metrics from all features, keyed by fully-qualified name
478
+ // (kumiko_<feature>_<shortName>). Consumed at boot to register them on the
479
+ // active Meter.
480
+ getAllMetrics(): ReadonlyMap<string, FeatureMetricDef & { readonly featureName: string }>;
481
+ getAllTranslations(): TranslationKeys;
482
+ getConfigKey(qualifiedKey: string): ConfigKeyDefinition | undefined;
483
+ getAllConfigKeys(): ReadonlyMap<string, ConfigKeyDefinition>;
484
+ // Feature-declared secrets, aggregated across all registered features.
485
+ // Keyed by qualified name ("<feature>:<shortName>"). Used by the rotation
486
+ // job (to iterate "known" secrets) and admin-UIs to list available keys.
487
+ getAllSecretKeys(): ReadonlyMap<string, SecretKeyDefinition>;
488
+ getSecretKey(qualifiedName: string): SecretKeyDefinition | undefined;
489
+ getJob(qualifiedName: string): JobDefinition | undefined;
490
+ getAllJobs(): ReadonlyMap<string, JobDefinition>;
491
+ getEvent(qualifiedName: string): EventDef | undefined;
492
+
493
+ // Upcaster chain per qualified event name. Entries describe the current
494
+ // schema version and the step-wise transforms that upgrade older stored
495
+ // payloads. Empty chain when an event has never been migrated (version=1).
496
+ getEventUpcasters(): ReadonlyMap<
497
+ string,
498
+ { readonly currentVersion: number; readonly chain: ReadonlyMap<number, EventUpcastFn> }
499
+ >;
500
+ getExtension(name: string): RegistrarExtensionDef | undefined;
501
+ getExtensionUsages(extensionName: string): readonly RegistrarExtensionRegistration[];
502
+ getAllNotifications(): ReadonlyMap<string, NotificationDefinition>;
503
+ getAllReferenceData(): readonly ReferenceDataDef[];
504
+ // Look up projections by source-entity name. Empty list when no projection
505
+ // feeds off the entity — event-store-executor uses this as the hot-path.
506
+ getProjectionsForSource(entityName: string): readonly ProjectionDefinition[];
507
+ getAllProjections(): ReadonlyMap<string, ProjectionDefinition>;
508
+
509
+ // Multi-stream projections registered via r.multiStreamProjection().
510
+ // Keyed by qualified name. The server wires each into the event-dispatcher
511
+ // as its own EventConsumer with a dedicated cursor.
512
+ getAllMultiStreamProjections(): ReadonlyMap<string, MultiStreamProjectionDefinition>;
513
+ // The feature that registered the given MSP. Used by the event-dispatcher
514
+ // to pause MSP-consumers whose owning feature is globally disabled.
515
+ getMultiStreamProjectionFeature(qualifiedName: string): string | undefined;
516
+
517
+ // All r.authClaims() hooks across all features, tagged with the declaring
518
+ // feature name so the resolver can apply the auto-prefix. Pre-aggregated
519
+ // at registry-build so the login hot path is a single Map read.
520
+ getAuthClaimsHooks(): readonly AuthClaimsHookDef[];
521
+
522
+ // Feature-declared claim keys, aggregated across all features. Keyed by
523
+ // qualified name ("<feature>:<short>"). Ops-UI + Boot-Validator use this
524
+ // to introspect what claims the app can produce.
525
+ getAllClaimKeys(): ReadonlyMap<string, ClaimKeyDefinition>;
526
+ getClaimKey(qualifiedName: string): ClaimKeyDefinition | undefined;
527
+
528
+ // Screens declared via r.screen() across all features. Keyed by qualified
529
+ // name ("<feature>:screen:<id>"). ui-core / renderer consume this to build
530
+ // navigation + screen-tree at mount time.
531
+ getAllScreens(): ReadonlyMap<string, ScreenDefinition>;
532
+ getScreen(qualifiedName: string): ScreenDefinition | undefined;
533
+ // The feature that registered the given screen. Consumed by the nav
534
+ // resolver to gate a nav-entry whose screen belongs to a disabled feature.
535
+ getScreenFeature(qualifiedName: string): string | undefined;
536
+ // All entity-bound screens (entityList / entityEdit) that target the given
537
+ // entity. Pre-grouped so ui-core's view-model builders don't re-filter
538
+ // getAllScreens() on every render. Custom screens have no entity and are
539
+ // never returned here — walk getAllScreens() for those.
540
+ getScreensByEntity(entityName: string): readonly ScreenDefinition[];
541
+
542
+ // Nav entries declared via r.nav() across all features. Keyed by qualified
543
+ // name ("<feature>:nav:<id>"). Flat list — the renderer's resolveNavigation
544
+ // assembles the tree from parent refs and gates by effective-features.
545
+ getAllNavs(): ReadonlyMap<string, NavDefinition>;
546
+ getNav(qualifiedName: string): NavDefinition | undefined;
547
+ // The feature that registered the given nav entry. Used by the nav
548
+ // resolver to drop entries whose owning feature is globally disabled.
549
+ getNavFeature(qualifiedName: string): string | undefined;
550
+ // Direct children of the given parent nav entry. Empty array when the
551
+ // parent has no children. Pre-grouped for O(1) tree-walk — resolveNavigation
552
+ // recurses with getNavsByParent(child.qn) instead of filtering getAllNavs().
553
+ getNavsByParent(parentQualifiedName: string): readonly NavDefinition[];
554
+ // Nav entries that declare no parent — the roots of the navigation tree.
555
+ // resolveNavigation starts its walk here and descends via getNavsByParent.
556
+ getTopLevelNavs(): readonly NavDefinition[];
557
+
558
+ // Workspaces declared via r.workspace() across all features. Keyed by
559
+ // qualified name ("<feature>:workspace:<id>"). The active web shell
560
+ // (shellWorkspaces) consumes this to render the switcher.
561
+ getAllWorkspaces(): ReadonlyMap<string, WorkspaceDefinition>;
562
+ getWorkspace(qualifiedName: string): WorkspaceDefinition | undefined;
563
+ // The feature that registered the workspace. Mirrors getNavFeature —
564
+ // lets the resolver drop workspaces whose owning feature is disabled.
565
+ getWorkspaceFeature(qualifiedName: string): string | undefined;
566
+ // Resolved nav QNs that belong to the given workspace. Pre-computed at
567
+ // boot from BOTH r.workspace.nav AND r.nav.workspaces — the shell
568
+ // doesn't have to merge sources at render time.
569
+ getWorkspaceNavs(workspaceQualifiedName: string): readonly string[];
570
+ // The single workspace whose `default: true` is set, if any. Boot
571
+ // validator rejects more than one. Apps without a default fall back to
572
+ // the first workspace the user has access to.
573
+ getDefaultWorkspace(): WorkspaceDefinition | undefined;
574
+ };