@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,107 @@
1
+ // ctx.tz — die Feature-Code-API für TZ-Operationen.
2
+ //
3
+ // Eine konsistente Form für jeden Date/Time-Bedarf im Handler-Code:
4
+ // - "Jetzt als UTC-Instant" → ctx.tz.now()
5
+ // - "Heute in Tenant-TZ" → ctx.tz.today(ctx.tz.tenant)
6
+ // - "Wall-Clock parsen" → ctx.tz.parse("2026-04-03T10:00", "Europe/Lisbon")
7
+ // - "ZonedDateTime → JSON-Pair" → ctx.tz.toLocatedJson(zdt)
8
+ // - "JSON-Pair → ZonedDateTime" → ctx.tz.fromLocatedJson({ at, tz })
9
+ //
10
+ // Feature-Code soll NICHT mehr `new Date()` aufrufen — die Lint-Regel dafür
11
+ // kommt in einer späteren Iteration, wenn alle existing usages migriert sind.
12
+ //
13
+ // `tenant` + `user` sind die TZ-Defaults für den aktuellen Request. Aktueller
14
+ // Stand (Iteration 4): beide default auf "UTC" — sobald tenant.timezone +
15
+ // user.timezone Felder existieren, lese ich sie aus dem Request-Context.
16
+ // TODO(Iteration 6): aus tenant entity / user profile lesen.
17
+
18
+ import { ensureTemporalPolyfill, getTemporal } from "./polyfill";
19
+
20
+ // JSON-Form für Wall-Clock+TZ — siehe locatedTimestamp(name) Helper in
21
+ // engine/factories.ts. Zwei Felder, idiotensicher.
22
+ export type LocatedTimestampJson = {
23
+ /** Wall-Clock-ISO ohne Offset, z.B. "2026-04-03T10:00:00" */
24
+ readonly at: string;
25
+ /** IANA-Zone, z.B. "Europe/Lisbon" */
26
+ readonly tz: string;
27
+ };
28
+
29
+ export type TzContext = {
30
+ /** Default-TZ des Mandanten (aus tenant.timezone, default "UTC"). */
31
+ readonly tenant: string;
32
+ /** Anzeige-TZ des aktuellen Users (User-Profil-Override, fallback Tenant). */
33
+ readonly user: string;
34
+
35
+ /** Aktueller Moment als UTC-Instant. */
36
+ now(): Temporal.Instant;
37
+ /** Aktueller Moment als ZonedDateTime in der gewünschten Zone. */
38
+ nowIn(tz: string): Temporal.ZonedDateTime;
39
+
40
+ /** Heutiges Kalender-Datum in der gewünschten Zone. */
41
+ today(tz: string): Temporal.PlainDate;
42
+ /** Tagesgrenzen (00:00 bis 24:00 nächster Tag) als UTC-Instants — für DB-Range-Queries. */
43
+ todayRange(tz: string): { readonly start: Temporal.Instant; readonly end: Temporal.Instant };
44
+
45
+ /** Wall-Clock-String + IANA-Zone → ZonedDateTime. */
46
+ parse(wallClock: string, tz: string): Temporal.ZonedDateTime;
47
+
48
+ /** ZonedDateTime → UTC-Instant. */
49
+ toInstant(zdt: Temporal.ZonedDateTime): Temporal.Instant;
50
+
51
+ /** ZonedDateTime → JSON-Pair { at, tz } (API-Boundary). */
52
+ toLocatedJson(zdt: Temporal.ZonedDateTime): LocatedTimestampJson;
53
+
54
+ /** JSON-Pair { at, tz } → ZonedDateTime (Wall-Clock + IANA). */
55
+ fromLocatedJson(obj: LocatedTimestampJson): Temporal.ZonedDateTime;
56
+ };
57
+
58
+ export type TzContextOptions = {
59
+ /** Tenant-Default-TZ. Default "UTC" wenn nicht gesetzt. */
60
+ readonly tenant?: string;
61
+ /** User-Override. Default = tenant. */
62
+ readonly user?: string;
63
+ };
64
+
65
+ /**
66
+ * Factory: erzeugt einen TzContext für den aktuellen Request.
67
+ * Erwartet dass ensureTemporalPolyfill() bereits gelaufen ist (passiert beim
68
+ * Framework-Boot). Wenn nicht, wirft getTemporal() — kein silent failure.
69
+ */
70
+ export function createTzContext(options: TzContextOptions = {}): TzContext {
71
+ const T = getTemporal();
72
+ const tenant = options.tenant ?? "UTC";
73
+ const user = options.user ?? tenant;
74
+
75
+ return {
76
+ tenant,
77
+ user,
78
+ now: () => T.Now.instant(),
79
+ nowIn: (tz: string) => T.Now.zonedDateTimeISO(tz),
80
+ today: (tz: string) => T.Now.plainDateISO(tz),
81
+ todayRange: (tz: string) => {
82
+ const today = T.Now.plainDateISO(tz);
83
+ const startZdt = today.toZonedDateTime({ timeZone: tz });
84
+ const endZdt = today.add({ days: 1 }).toZonedDateTime({ timeZone: tz });
85
+ return { start: startZdt.toInstant(), end: endZdt.toInstant() };
86
+ },
87
+ parse: (wallClock: string, tz: string) => T.PlainDateTime.from(wallClock).toZonedDateTime(tz),
88
+ toInstant: (zdt) => zdt.toInstant(),
89
+ toLocatedJson: (zdt) => ({
90
+ // Wall-Clock OHNE Offset (kein "Z", kein "+01:00") plus IANA-Name.
91
+ // .toPlainDateTime().toString() liefert "YYYY-MM-DDTHH:MM:SS[.fff]"
92
+ // ohne Offset — exakt unser Vertrag.
93
+ at: zdt.toPlainDateTime().toString(),
94
+ tz: zdt.timeZoneId,
95
+ }),
96
+ fromLocatedJson: (obj) => T.PlainDateTime.from(obj.at).toZonedDateTime(obj.tz),
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Convenience: stellt sicher dass der Polyfill geladen ist UND erzeugt
102
+ * den TzContext in einem await. Bevorzugt verwenden in Boot-Code.
103
+ */
104
+ export async function createTzContextAsync(options?: TzContextOptions): Promise<TzContext> {
105
+ await ensureTemporalPolyfill();
106
+ return createTzContext(options);
107
+ }
@@ -0,0 +1,57 @@
1
+ // Client-safe Schema-Types. Wohnen in framework/ui-types statt renderer
2
+ // damit der Server (createKumikoServer / buildAppSchema) sie produzieren
3
+ // kann ohne renderer als Dependency zu ziehen. Renderer + Renderer-Web
4
+ // re-exporten dieselben Symbole — Konsumenten merken den Umzug nicht.
5
+ //
6
+ // Pattern: Types fließen "downstream" (framework → renderer → renderer-
7
+ // web), Runtime-Helpers (toAppSchema, isAppSchema) bleiben renderer-side
8
+ // weil das die Layer ist die mit den AppSchemas zur Laufzeit arbeitet.
9
+
10
+ import type { EntityDefinition } from "../engine/types/fields";
11
+ import type { NavDefinition } from "../engine/types/nav";
12
+ import type { ScreenDefinition } from "../engine/types/screen";
13
+ import type { WorkspaceDefinition } from "../engine/types/workspace";
14
+
15
+ export type FeatureSchema = {
16
+ readonly featureName: string;
17
+ readonly entities: Readonly<Record<string, EntityDefinition>>;
18
+ readonly screens: readonly ScreenDefinition[];
19
+ // Flat list; resolveNavigation builds the tree at render-time from
20
+ // the registry's indexes. Omitted when the app has no top-level nav.
21
+ readonly navs?: readonly NavDefinition[];
22
+ // Workspaces — Legacy-Slot für single-feature-Apps. Bevorzugt liegt
23
+ // workspaces auf der AppSchema-Ebene weil ihre navMembers regelmäßig
24
+ // Cross-Feature-Navs referenzieren (siehe AppSchema-Doc). Hier als
25
+ // Fallback erhalten damit alte clientSchema-Files (vor AppSchema)
26
+ // ohne Migration weiter laufen — toAppSchema() hebt die Liste hoch.
27
+ readonly workspaces?: readonly WorkspaceSchema[];
28
+ };
29
+
30
+ // Per-workspace projection of the engine's WorkspaceDefinition + the
31
+ // pre-resolved member nav QNs. The shell renders the switcher from
32
+ // `definition` and filters the nav tree using `navMembers`.
33
+ export type WorkspaceSchema = {
34
+ readonly definition: WorkspaceDefinition;
35
+ // Nav QNs that belong to this workspace, in the order the engine
36
+ // resolved them (explicit r.workspace.nav first, then nav-self-assigned
37
+ // entries — deduped). Empty when no nav has been assigned.
38
+ readonly navMembers: readonly string[];
39
+ };
40
+
41
+ // App-level schema. Bündelt ein oder mehrere FeatureSchemas + die App-
42
+ // weiten Workspaces. Sinn der Trennung: Workspaces aggregieren über
43
+ // Feature-Grenzen (admin-Workspace zeigt navs aus mehreren Features), und
44
+ // die navMembers-Liste enthält voll qualifizierte QNs die der Browser
45
+ // gegen die jeweilige feature-spezifische `navs`-Liste auflöst.
46
+ //
47
+ // Backwards-Compat: createKumikoApp + die Layouts (DefaultAppShell,
48
+ // WorkspaceShell) akzeptieren beides — `FeatureSchema` (single-feature,
49
+ // historisch) und `AppSchema` (multi-feature). Ein `toAppSchema(input)`-
50
+ // Adapter normalisiert intern, sodass die ganze inneren Renderer-Pipeline
51
+ // nur noch AppSchema kennt.
52
+ export type AppSchema = {
53
+ readonly features: readonly FeatureSchema[];
54
+ // Optional — Apps ohne Workspaces nutzen DefaultAppShell und sehen
55
+ // schlicht die NavTree aller Features.
56
+ readonly workspaces?: readonly WorkspaceSchema[];
57
+ };
@@ -0,0 +1,65 @@
1
+ // @runtime client
2
+ //
3
+ // Client-safe subset of engine types + the two normalize helpers. Split
4
+ // out into its own subpath (`@cosmicdrift/kumiko-framework/ui-types`) so ui-core and
5
+ // renderer packages can import without pulling node-only framework
6
+ // internals (postgres, drizzle-kit, ioredis, bullmq, ...) into the
7
+ // browser or Expo bundle through the main `./engine` barrel.
8
+ //
9
+ // The main `./engine` barrel re-exports `createApp`, `defineFeature`,
10
+ // and other server-runtime factories. Even with `import type` on the
11
+ // consumer side, some bundlers pull evaluation of the barrel, which
12
+ // transitively reaches `pg` / `ioredis` / `tls`. This entry stays narrow:
13
+ // types + the two pure-fn normalize helpers. No runtime imports of node
14
+ // built-ins or of framework DB / pipeline modules.
15
+ //
16
+ // The `type` re-exports below still chain into files that contain
17
+ // runtime code (fields.ts → ownership.ts → drizzle-orm; handlers.ts →
18
+ // ioredis etc.). With `verbatimModuleSyntax: true` and `import type` on
19
+ // every hop the bundler strips those; this file reaches only type-space.
20
+ // When adding a symbol here, verify it's either a type or a pure
21
+ // helper with no cross-module side-effects.
22
+
23
+ export type { ParsedRefTarget } from "../engine/parse-ref-target";
24
+ export { parseRefTarget } from "../engine/parse-ref-target";
25
+ // Entity + field types. EntityDefinition is the canonical shape that
26
+ // view-model builders iterate; FieldDefinition is the per-field union
27
+ // (text, number, boolean, ...) they branch on. AccessRule is used by
28
+ // resolveNavigation to gate entries by user roles.
29
+ export type {
30
+ BooleanFieldDef,
31
+ DateFieldDef,
32
+ EntityDefinition,
33
+ FieldDefinition,
34
+ FileFieldDef,
35
+ FilesFieldDef,
36
+ ImageFieldDef,
37
+ ImagesFieldDef,
38
+ NumberFieldDef,
39
+ SelectFieldDef,
40
+ TextFieldDef,
41
+ } from "../engine/types/fields";
42
+ export type { AccessRule } from "../engine/types/handlers";
43
+ export type { NavDefinition } from "../engine/types/nav";
44
+ export type {
45
+ ActionFormScreenDefinition,
46
+ ConfigEditScreenDefinition,
47
+ CustomScreenDefinition,
48
+ CustomScreenRoute,
49
+ EditFieldSpec,
50
+ EditLayout,
51
+ EditSectionSpec,
52
+ EntityEditScreenDefinition,
53
+ EntityListScreenDefinition,
54
+ FieldCondition,
55
+ FieldRenderer,
56
+ ListColumnSpec,
57
+ PlatformComponent,
58
+ ScreenDefinition,
59
+ ScreenFilter,
60
+ ScreenFilterOp,
61
+ ScreenSlots,
62
+ } from "../engine/types/screen";
63
+ export { normalizeEditField, normalizeListColumn } from "../engine/types/screen";
64
+ export type { WorkspaceDefinition } from "../engine/types/workspace";
65
+ export type { AppSchema, FeatureSchema, WorkspaceSchema } from "./app-schema";
@@ -0,0 +1,17 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { assertUnreachable } from "../assert";
3
+
4
+ describe("assertUnreachable", () => {
5
+ test("throws with the kind label and the offending value in the message", () => {
6
+ // Runtime path: the compile-time never-check is verified by tsc itself —
7
+ // this test just exercises the throw for a case where the union was
8
+ // extended without updating callers.
9
+ expect(() => {
10
+ // Simulating a future enum-extension where a call site forgot to handle
11
+ // the new member. The `as never` matches what a wrong-shape value would
12
+ // look like at runtime after escaping the compiler.
13
+ const rogue = "surprise" as unknown as never;
14
+ assertUnreachable(rogue, "status");
15
+ }).toThrow(/unhandled status: surprise/);
16
+ });
17
+ });
@@ -0,0 +1,54 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { readPositiveIntEnv } from "../env-parse";
3
+
4
+ describe("readPositiveIntEnv", () => {
5
+ test("returns undefined when the key is absent", () => {
6
+ expect(readPositiveIntEnv({}, "POOL_MAX")).toBeUndefined();
7
+ });
8
+
9
+ test("returns undefined when the value is the empty string", () => {
10
+ // Shell-quirk: `POOL_MAX=` in a .env yields "" — treat as "unset" so
11
+ // callers fall through to the framework default instead of throwing.
12
+ expect(readPositiveIntEnv({ POOL_MAX: "" }, "POOL_MAX")).toBeUndefined();
13
+ });
14
+
15
+ test("parses a valid non-negative integer", () => {
16
+ expect(readPositiveIntEnv({ POOL_MAX: "20" }, "POOL_MAX")).toBe(20);
17
+ });
18
+
19
+ test("accepts 0 (the framework sentinel for 'disable')", () => {
20
+ expect(readPositiveIntEnv({ POOL_MAX: "0" }, "POOL_MAX")).toBe(0);
21
+ });
22
+
23
+ test("throws on a negative number with the variable name + value in the message", () => {
24
+ expect(() => readPositiveIntEnv({ POOL_MAX: "-1" }, "POOL_MAX")).toThrow(
25
+ /POOL_MAX="-1" must be a non-negative integer/,
26
+ );
27
+ });
28
+
29
+ test("throws on a fractional number", () => {
30
+ expect(() => readPositiveIntEnv({ POOL_MAX: "1.5" }, "POOL_MAX")).toThrow(
31
+ /POOL_MAX="1.5" must be a non-negative integer/,
32
+ );
33
+ });
34
+
35
+ test("throws on non-numeric input", () => {
36
+ expect(() => readPositiveIntEnv({ POOL_MAX: "abc" }, "POOL_MAX")).toThrow(
37
+ /POOL_MAX="abc" must be a non-negative integer/,
38
+ );
39
+ });
40
+
41
+ test("throws on whitespace-only input (Number(' ') is 0 — but trim first)", () => {
42
+ // Number(" ") === 0, which would silently pass the non-negative-integer
43
+ // check. Verifying the current behavior: pure whitespace is parsed as 0
44
+ // because Number() coerces it. If this changes, the test forces a
45
+ // deliberate update rather than a silent drift.
46
+ expect(readPositiveIntEnv({ POOL_MAX: " " }, "POOL_MAX")).toBe(0);
47
+ });
48
+
49
+ test("throws on Infinity", () => {
50
+ expect(() => readPositiveIntEnv({ POOL_MAX: "Infinity" }, "POOL_MAX")).toThrow(
51
+ /POOL_MAX="Infinity" must be a non-negative integer/,
52
+ );
53
+ });
54
+ });
@@ -0,0 +1,18 @@
1
+ // Exhaustiveness-check helper for switch statements over union types.
2
+ // Dropping a new member into the union triggers a compile-time error in
3
+ // the call site unless a matching case is added — keeps switches honest
4
+ // through future enum growth.
5
+ //
6
+ // switch (status) {
7
+ // case "open": return ...;
8
+ // case "closed": return ...;
9
+ // default: assertUnreachable(status, "status");
10
+ // }
11
+ //
12
+ // The `never` parameter makes TS flag any code path that still has a
13
+ // live value (e.g. a new union case was added and the switch missed it).
14
+ // Runtime also throws so a production surprise surfaces loudly instead
15
+ // of silently falling through.
16
+ export function assertUnreachable(value: never, kind: string): never {
17
+ throw new Error(`[Kumiko] unhandled ${kind}: ${String(value)}`);
18
+ }
@@ -0,0 +1,16 @@
1
+ // Strict env-var parsers. Misconfig at boot is loud: every helper throws
2
+ // with the offending variable name + value so ops sees exactly what's
3
+ // wrong instead of a cascading timeout downstream.
4
+
5
+ export function readPositiveIntEnv(
6
+ env: Readonly<Record<string, string | undefined>>,
7
+ name: string,
8
+ ): number | undefined {
9
+ const raw = env[name];
10
+ if (raw === undefined || raw === "") return undefined;
11
+ const n = Number(raw);
12
+ if (!Number.isFinite(n) || n < 0 || !Number.isInteger(n)) {
13
+ throw new Error(`[env] ${name}="${raw}" must be a non-negative integer`);
14
+ }
15
+ return n;
16
+ }
@@ -0,0 +1,16 @@
1
+ import { v7 } from "uuid";
2
+
3
+ // Non-secret identifiers for DB rows, event streams, correlation/request
4
+ // IDs, SSE connections, distributed locks. UUIDv7: first 48 bits are a
5
+ // Unix-ms timestamp, remaining 74 bits are random. Lexicographic order
6
+ // matches chronological order, so B-Tree indexes stay dense on insert
7
+ // and time-range queries ("events for stream X since T") read sequential
8
+ // pages. Universal-safe — uses the `uuid` npm package, not `node:crypto`,
9
+ // so the same call works in Bun, Node, Metro/RN, and Expo-Web bundles.
10
+ //
11
+ // Do NOT use this for security tokens (CSRF, session, API keys). The
12
+ // timestamp prefix leaks creation time and shrinks unpredictable
13
+ // entropy from 122 to 74 bits — use `generateToken` from api/tokens.ts.
14
+ export function generateId(): string {
15
+ return v7();
16
+ }
@@ -0,0 +1,5 @@
1
+ export { assertUnreachable } from "./assert";
2
+ export { readPositiveIntEnv } from "./env-parse";
3
+ export { generateId } from "./ids";
4
+ export { parseJsonOrThrow, parseJsonSafe } from "./safe-json";
5
+ export { parseRoles } from "./serialization";
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Safe JSON parsing — never let invalid input from Redis/DB/external systems
3
+ * crash the pipeline silently. Two variants for two different semantics:
4
+ *
5
+ * - parseJsonSafe(raw, fallback): for caches and idempotent reads. Corrupt
6
+ * input is treated as "not there" (fallback returned). The caller continues
7
+ * as if the cache missed.
8
+ * - parseJsonOrThrow(raw, context): for inputs where corruption is a real bug
9
+ * (config values, job payloads, broker messages addressed to us). Throws
10
+ * with a clear context message so the stack trace points to the boundary.
11
+ */
12
+
13
+ export function parseJsonSafe<T>(raw: string, fallback: T): T {
14
+ try {
15
+ // @cast-boundary engine-bridge — generic parser-helper centralizes the cast
16
+ return JSON.parse(raw) as T;
17
+ } catch {
18
+ return fallback;
19
+ }
20
+ }
21
+
22
+ export function parseJsonOrThrow<T>(raw: string, context: string): T {
23
+ try {
24
+ // @cast-boundary engine-bridge — generic parser-helper centralizes the cast
25
+ return JSON.parse(raw) as T;
26
+ } catch (err) {
27
+ const msg = err instanceof Error ? err.message : String(err);
28
+ throw new Error(`Invalid JSON in ${context}: ${msg}`);
29
+ }
30
+ }
@@ -0,0 +1,7 @@
1
+ import { parseJsonSafe } from "./safe-json";
2
+
3
+ export function parseRoles(raw: unknown): string[] {
4
+ if (typeof raw === "string") return parseJsonSafe<string[]>(raw, []);
5
+ if (Array.isArray(raw)) return raw;
6
+ return [];
7
+ }