@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,125 @@
1
+ // Erzeugt aus einer Server-Registry das client-safe AppSchema das die
2
+ // Browser-Renderer-Pipeline konsumiert. Genauer Zweck: dev-server kann
3
+ // die ganze hand-geschriebene `clientSchema`-Spiegelung abschaffen, der
4
+ // Server schickt einfach das aufgelöste AppSchema beim Boot mit.
5
+ //
6
+ // JSON-Safety: Wir projezieren explizit auf eine Whitelist statt
7
+ // JSON.stringify-roundtripping. Functions würden silent gedroppt und
8
+ // Zod-Schemas (v4 hat .toJSON, aber emittiert ein _zod-Envelope) würden
9
+ // als komische Ghost-Properties auftauchen. Das hier ist ein bewusster
10
+ // Vertrag: was die Browser-Renderer-Pipeline liest, taucht hier auf.
11
+ // Neue Browser-needed-Fields müssen explizit erweitert werden.
12
+ //
13
+ // Aktuell projeziert (Stand 2026-04-25):
14
+ // Entity: { table?, fields: { type, required?, sortable?, default? } }
15
+ // Screen: verbatim (ScreenDefinition ist von Haus aus JSON-safe —
16
+ // custom-screens haben keine functions, layout/columns/etc.
17
+ // sind plain literals)
18
+ // Nav: verbatim (NavDefinition ist nur strings + literals)
19
+ // Workspace: verbatim definition + getWorkspaceNavs() von der Registry
20
+ //
21
+ // Feature-Toggles: BISHER NICHT GEFILTERT. Wenn ein Feature über die
22
+ // feature-toggles-bundled-feature global deaktiviert ist, erscheint es
23
+ // trotzdem im AppSchema. Reason: die Toggle-Auflösung lebt im pipeline-
24
+ // dispatcher, nicht in der Registry, und wir haben hier keinen TenantDb-
25
+ // Kontext um sie zu lesen. TODO wenn das ein realer Use-Case wird:
26
+ // `effectiveFeatures` Argument annehmen und über alle iterations filtern.
27
+
28
+ import type { AppSchema, EntityDefinition, FeatureSchema, WorkspaceSchema } from "../ui-types";
29
+ import type { Registry } from "./types/feature";
30
+ import type { FieldDefinition } from "./types/fields";
31
+
32
+ export function buildAppSchema(registry: Registry): AppSchema {
33
+ const features: FeatureSchema[] = [];
34
+ for (const [featureName, feature] of registry.features) {
35
+ const navs = Object.values(feature.navs);
36
+ const featureSchema: FeatureSchema = {
37
+ featureName,
38
+ entities: projectEntities(feature.entities),
39
+ screens: Object.values(feature.screens),
40
+ ...(navs.length > 0 && { navs }),
41
+ };
42
+ features.push(featureSchema);
43
+ }
44
+
45
+ // Workspaces: getAllWorkspaces() liefert mit QUALIFIZIERTEN ids (das
46
+ // schreibt die Registry beim Store-Overwrite ein). Die Browser-
47
+ // Renderer erwartet aber kurze ids (matcht gegen URL-Segment, gegen
48
+ // navigate({ workspaceId })). Wir gehen direkt durch `feature.workspaces`
49
+ // — dort sind die ids noch in der Autor-Form (short) — und ziehen die
50
+ // pre-resolved navMembers aus der Registry.
51
+ const workspaces: WorkspaceSchema[] = [];
52
+ for (const [featureName, feature] of registry.features) {
53
+ for (const [shortId, definition] of Object.entries(feature.workspaces)) {
54
+ const qualified = `${featureName}:workspace:${shortId}`;
55
+ workspaces.push({
56
+ definition: { ...definition, id: shortId },
57
+ navMembers: registry.getWorkspaceNavs(qualified),
58
+ });
59
+ }
60
+ }
61
+
62
+ return {
63
+ features,
64
+ ...(workspaces.length > 0 && { workspaces }),
65
+ };
66
+ }
67
+
68
+ function projectEntities(
69
+ entities: Readonly<Record<string, EntityDefinition>>,
70
+ ): Readonly<Record<string, EntityDefinition>> {
71
+ const out: Record<string, EntityDefinition> = {};
72
+ for (const [name, entity] of Object.entries(entities)) {
73
+ out[name] = projectEntity(entity);
74
+ }
75
+ return out;
76
+ }
77
+
78
+ // EntityDefinition ist eine Discriminated-Union an Field-Types — wir
79
+ // kennen alle JSON-safe Properties pro Field-Type. Statt jede Variante
80
+ // einzeln auszuhandeln, walken wir die Field-Map und filtern auf die
81
+ // Whitelist. Was nicht durchkommt: Server-only-runtime wie ZodValidate-
82
+ // Functions, Computed-Functions, Default-Functions.
83
+ function projectEntity(entity: EntityDefinition): EntityDefinition {
84
+ const fieldsOut: Record<string, FieldDefinition> = {};
85
+ for (const [fieldName, fieldDef] of Object.entries(entity.fields)) {
86
+ fieldsOut[fieldName] = projectField(fieldDef);
87
+ }
88
+ // EntityDefinition akzeptiert idType/access/searchWeight als optional —
89
+ // wir lassen die weg weil der Browser-Renderer sie nicht liest. `table`
90
+ // schicken wir mit, falls Apps `entity.table` direkt referenzieren.
91
+ // Kein Cast nötig: alle weggelassenen Felder sind `?`-optional.
92
+ return {
93
+ fields: fieldsOut,
94
+ ...(typeof entity.table === "string" && { table: entity.table }),
95
+ };
96
+ }
97
+
98
+ // Whitelist pro Field. `default` darf nur durch wenn Literal (string/
99
+ // number/boolean/null) — auch wenn die FieldDefinition-Types „default"
100
+ // nur als Literal typisieren, hat das Sample-Pattern
101
+ // `as unknown as EntityDefinition` Authorinnen schon Function-Defaults
102
+ // reinschmuggeln lassen. Diese Defense-in-Depth fängt sie ab BEVOR
103
+ // JSON.stringify sie in der Browser-Injection-Pipeline droppt.
104
+ //
105
+ // Cast am Exit `as FieldDefinition`: type-system-wise erfüllt unsere
106
+ // Out-Map die Discriminated-Union nur mit unverengtem `type`-String —
107
+ // der Cast bridged die Variant-Inferenz, die TS aus einem Generic
108
+ // Record nicht zurückrechnet.
109
+ function projectField(fieldDef: FieldDefinition): FieldDefinition {
110
+ const def = fieldDef as Record<string, unknown>; // @cast-boundary schema-walk
111
+ const out: Record<string, unknown> = {};
112
+ if (typeof def["type"] === "string") out["type"] = def["type"];
113
+ if (typeof def["required"] === "boolean") out["required"] = def["required"];
114
+ if (typeof def["sortable"] === "boolean") out["sortable"] = def["sortable"];
115
+ if (isLiteral(def["default"])) out["default"] = def["default"];
116
+ // Select: options-Liste ist plain JSON, durchschicken.
117
+ if (Array.isArray(def["options"])) out["options"] = def["options"];
118
+ return out as FieldDefinition;
119
+ }
120
+
121
+ function isLiteral(value: unknown): boolean {
122
+ if (value === null) return true;
123
+ const t = typeof value;
124
+ return t === "string" || t === "number" || t === "boolean";
125
+ }
@@ -0,0 +1,115 @@
1
+ import type { ConfigScope } from "./constants";
2
+ import type {
3
+ ConfigBounds,
4
+ ConfigComputedFn,
5
+ ConfigKeyDefinition,
6
+ ConfigKeyType,
7
+ ConfigValue,
8
+ } from "./types";
9
+
10
+ // --- Access Presets ---
11
+
12
+ export const access = {
13
+ all: ["all"] as readonly string[],
14
+ admin: ["Admin", "SystemAdmin"] as readonly string[],
15
+ systemAdmin: ["SystemAdmin"] as readonly string[],
16
+ system: ["system"] as readonly string[],
17
+ // system + SystemAdmin — use for field-access on identity columns that
18
+ // framework auth code (SYSTEM_USER) writes during login/registration, but
19
+ // that a SystemAdmin should also be able to fix manually.
20
+ privileged: ["system", "SystemAdmin"] as readonly string[],
21
+ // Any signed-in user role. Use on authenticated-but-not-privileged handlers
22
+ // (change-password, logout, me-style queries). Does NOT include "system"
23
+ // since an unauthenticated system call shouldn't be able to hit these.
24
+ authenticated: ["User", "Admin", "SystemAdmin"] as readonly string[],
25
+ // Unauthenticated callers reaching public endpoints (server must opt in
26
+ // via `anonymousAccess`). Combine with authenticated roles when an
27
+ // endpoint should serve both — e.g. `roles: ["anonymous", "customer"]`
28
+ // for a product-listing that personalises when a session is present.
29
+ anonymous: ["anonymous"] as readonly string[],
30
+ roles: (...roles: string[]): readonly string[] => roles,
31
+ } as const;
32
+
33
+ // --- Config Key Options ---
34
+
35
+ // Generic so `default` narrows per type-tag — without it,
36
+ // `createUserConfig("boolean", { default: 19 })` would compile.
37
+ //
38
+ // `bounds` is conditional: only `type="number"` admits it. For any other
39
+ // type-tag the field is `never`, so `createTenantConfig("text", { bounds })`
40
+ // fails at the call site. Matches the same pattern as `default`.
41
+ //
42
+ // `computed` is a fallback-resolver the registry calls when no row + no
43
+ // app-override exists — used for plan-based limits (see
44
+ // configuration-layers.md, "Frage 3: Hängt Geld dran?").
45
+ //
46
+ // `allowPerRequest` is conditional against `text`: text keys can never
47
+ // opt in to per-request overrides (XSS/SQL/Shell risk). For other types
48
+ // it's a plain boolean opt-in consumed by resolveConfigOrParam.
49
+ type ConfigKeyOptions<T extends ConfigKeyType> = {
50
+ write?: readonly string[];
51
+ read?: readonly string[];
52
+ default?: ConfigValue<T>;
53
+ encrypted?: boolean;
54
+ options?: readonly string[]; // for select type
55
+ bounds?: T extends "number" ? ConfigBounds : never;
56
+ computed?: ConfigComputedFn<T>;
57
+ allowPerRequest?: T extends "text" ? never : boolean;
58
+ };
59
+
60
+ // --- Scope Defaults ---
61
+
62
+ const SCOPE_DEFAULTS: Record<ConfigScope, { write: readonly string[]; read: readonly string[] }> = {
63
+ tenant: { write: access.admin, read: access.all },
64
+ system: { write: access.system, read: access.admin },
65
+ user: { write: access.all, read: access.all },
66
+ };
67
+
68
+ // --- Factory ---
69
+
70
+ function createConfigKey<T extends ConfigKeyType>(
71
+ scope: ConfigScope,
72
+ type: T,
73
+ opts: ConfigKeyOptions<T> = {},
74
+ ): ConfigKeyDefinition<T> {
75
+ const defaults = SCOPE_DEFAULTS[scope];
76
+ return {
77
+ type,
78
+ scope,
79
+ access: {
80
+ write: opts.write ?? defaults.write,
81
+ read: opts.read ?? defaults.read,
82
+ },
83
+ default: opts.default,
84
+ ...(opts.encrypted ? { encrypted: true } : {}),
85
+ ...(opts.options ? { options: opts.options } : {}),
86
+ bounds: opts.bounds as ConfigBounds | undefined,
87
+ computed: opts.computed,
88
+ ...(opts.allowPerRequest === true ? { allowPerRequest: true } : {}),
89
+ };
90
+ }
91
+
92
+ // --- Public API ---
93
+ // Generic on the type-tag so `r.config({keys})` can propagate it into the
94
+ // returned `ConfigKeyHandle<T>` — that's what narrows `ctx.config(handle)`.
95
+
96
+ export function createTenantConfig<T extends ConfigKeyType>(
97
+ type: T,
98
+ opts?: ConfigKeyOptions<T>,
99
+ ): ConfigKeyDefinition<T> {
100
+ return createConfigKey("tenant", type, opts);
101
+ }
102
+
103
+ export function createSystemConfig<T extends ConfigKeyType>(
104
+ type: T,
105
+ opts?: ConfigKeyOptions<T>,
106
+ ): ConfigKeyDefinition<T> {
107
+ return createConfigKey("system", type, opts);
108
+ }
109
+
110
+ export function createUserConfig<T extends ConfigKeyType>(
111
+ type: T,
112
+ opts?: ConfigKeyOptions<T>,
113
+ ): ConfigKeyDefinition<T> {
114
+ return createConfigKey("user", type, opts);
115
+ }
@@ -0,0 +1,85 @@
1
+ import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
2
+
3
+ // All framework constants as `as const` objects with inferred union types.
4
+ // No enums — only const objects + typeof inference.
5
+
6
+ // Error codes — the canonical list lives on the KumikoError subclasses in
7
+ // `errors/classes.ts`. Features that need to surface a feature-specific reason
8
+ // attach it under `details.reason` on the relevant Kumiko error class.
9
+
10
+ // --- System Hook Names ---
11
+
12
+ export const SystemHookNames = {
13
+ cascadeDelete: "system:hook:cascade-delete",
14
+ } as const;
15
+
16
+ export type SystemHookName = (typeof SystemHookNames)[keyof typeof SystemHookNames];
17
+
18
+ // --- System Hook Priorities ---
19
+
20
+ export const SystemHookPriorities = {
21
+ cascadeDelete: 500,
22
+ } as const;
23
+
24
+ // --- Message Kinds ---
25
+
26
+ export const MessageKind = {
27
+ write: "write",
28
+ query: "query",
29
+ command: "command",
30
+ shared: "shared",
31
+ broadcast: "broadcast",
32
+ } as const;
33
+
34
+ export type MessageKind = (typeof MessageKind)[keyof typeof MessageKind];
35
+
36
+ // --- Lifecycle Hook Types ---
37
+
38
+ export const LifecycleHookTypes = {
39
+ preSave: "preSave",
40
+ postSave: "postSave",
41
+ preDelete: "preDelete",
42
+ postDelete: "postDelete",
43
+ preQuery: "preQuery",
44
+ } as const;
45
+
46
+ export type LifecycleHookType = (typeof LifecycleHookTypes)[keyof typeof LifecycleHookTypes];
47
+
48
+ // --- Config Scopes ---
49
+
50
+ export const ConfigScopes = {
51
+ system: "system",
52
+ tenant: "tenant",
53
+ user: "user",
54
+ } as const;
55
+
56
+ export type ConfigScope = (typeof ConfigScopes)[keyof typeof ConfigScopes];
57
+
58
+ // --- On Delete Strategies ---
59
+
60
+ export const OnDeleteStrategies = {
61
+ cascade: "cascade",
62
+ restrict: "restrict",
63
+ setNull: "setNull",
64
+ nothing: "nothing",
65
+ } as const;
66
+
67
+ export type OnDeleteStrategy = (typeof OnDeleteStrategies)[keyof typeof OnDeleteStrategies];
68
+
69
+ // --- Concurrency Modes ---
70
+
71
+ export const ConcurrencyModes = {
72
+ parallel: "parallel",
73
+ skip: "skip",
74
+ replace: "replace",
75
+ sequential: "sequential",
76
+ debounce: "debounce",
77
+ } as const;
78
+
79
+ export type ConcurrencyMode = (typeof ConcurrencyModes)[keyof typeof ConcurrencyModes];
80
+
81
+ // --- SSE Channels ---
82
+
83
+ export function tenantChannel(tenantId: TenantId): string {
84
+ return `tenant:${tenantId}`;
85
+ }
@@ -0,0 +1,98 @@
1
+ import { validateBoot } from "./boot-validator";
2
+ import { createRegistry } from "./registry";
3
+ import type { FeatureDefinition, Registry } from "./types";
4
+ import { DEFAULT_CURRENCIES } from "./types";
5
+
6
+ export type AppConfig = {
7
+ roles: readonly string[];
8
+ features: readonly FeatureDefinition[];
9
+ softDelete?: boolean; // Global default for all entities (default: true)
10
+ currencies?: readonly string[]; // Extends DEFAULT_CURRENCIES
11
+ };
12
+
13
+ export type App = {
14
+ registry: Registry;
15
+ roles: readonly string[];
16
+ softDeleteDefault: boolean;
17
+ currencies: readonly string[];
18
+ };
19
+
20
+ export function createApp(config: AppConfig): App {
21
+ const validRoles = new Set(config.roles);
22
+
23
+ // "system" is reserved for SYSTEM_USER — cannot be used as an app role
24
+ if (validRoles.has("system")) {
25
+ throw new Error('Role "system" is reserved for SYSTEM_USER and cannot be used as an app role');
26
+ }
27
+
28
+ // Special roles that don't need to be in the app's role list
29
+ const systemRoles = new Set(["all", "system"]);
30
+
31
+ // Validate all roles referenced by features exist in app-defined roles.
32
+ // openToAll access has no role list — nothing to validate there.
33
+ for (const feature of config.features) {
34
+ for (const handler of Object.values(feature.writeHandlers)) {
35
+ if (handler.access && "roles" in handler.access) {
36
+ for (const role of handler.access.roles) {
37
+ if (!validRoles.has(role)) {
38
+ throw new Error(
39
+ `Unknown role "${role}" in write handler "${handler.name}" of feature "${feature.name}". Valid roles: ${config.roles.join(", ")}`,
40
+ );
41
+ }
42
+ }
43
+ }
44
+ }
45
+ for (const handler of Object.values(feature.queryHandlers)) {
46
+ if (handler.access && "roles" in handler.access) {
47
+ for (const role of handler.access.roles) {
48
+ if (!validRoles.has(role)) {
49
+ throw new Error(
50
+ `Unknown role "${role}" in query handler "${handler.name}" of feature "${feature.name}". Valid roles: ${config.roles.join(", ")}`,
51
+ );
52
+ }
53
+ }
54
+ }
55
+ }
56
+ for (const [key, keyDef] of Object.entries(feature.configKeys)) {
57
+ for (const role of [...keyDef.access.read, ...keyDef.access.write]) {
58
+ if (!systemRoles.has(role) && !validRoles.has(role)) {
59
+ throw new Error(
60
+ `Unknown role "${role}" in config key "${feature.name}.${key}" of feature "${feature.name}". Valid roles: ${config.roles.join(", ")}`,
61
+ );
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ const softDeleteDefault = config.softDelete ?? true;
68
+
69
+ // Merge default + custom currencies, deduplicate
70
+ const currencies = [...new Set([...DEFAULT_CURRENCIES, ...(config.currencies ?? [])])];
71
+
72
+ // Validate defaultCurrency on entities that have money fields
73
+ for (const feature of config.features) {
74
+ for (const [entityName, entity] of Object.entries(feature.entities)) {
75
+ const hasMoneyField = Object.values(entity.fields).some((f) => f.type === "money");
76
+ if (entity.defaultCurrency && !currencies.includes(entity.defaultCurrency)) {
77
+ throw new Error(
78
+ `Entity "${entityName}" in feature "${feature.name}" has defaultCurrency "${entity.defaultCurrency}" which is not in the currencies list. Available: ${currencies.join(", ")}`,
79
+ );
80
+ }
81
+ if (hasMoneyField && !entity.defaultCurrency) {
82
+ throw new Error(
83
+ `Entity "${entityName}" in feature "${feature.name}" has money fields but no defaultCurrency. Set defaultCurrency on the entity definition.`,
84
+ );
85
+ }
86
+ }
87
+ }
88
+
89
+ // Run boot-time validation before creating registry
90
+ validateBoot(config.features);
91
+
92
+ return {
93
+ registry: createRegistry(config.features),
94
+ roles: config.roles,
95
+ softDeleteDefault,
96
+ currencies,
97
+ };
98
+ }