@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,109 @@
1
+ // Central re-export for all PostgreSQL-specific imports.
2
+ // No other file in the framework should import from "drizzle-orm/pg-core" directly.
3
+
4
+ import { customType } from "drizzle-orm/pg-core";
5
+
6
+ export type {
7
+ PgSelect as SelectQuery,
8
+ PgTableWithColumns as TableColumns,
9
+ } from "drizzle-orm/pg-core";
10
+ export {
11
+ bigint,
12
+ bigserial,
13
+ boolean,
14
+ index,
15
+ integer,
16
+ jsonb,
17
+ numeric,
18
+ pgTable as table,
19
+ primaryKey,
20
+ serial,
21
+ text,
22
+ timestamp,
23
+ uniqueIndex,
24
+ uuid,
25
+ } from "drizzle-orm/pg-core";
26
+
27
+ /**
28
+ * Money column: BIGINT storing the integer minor unit (cents for EUR, yen
29
+ * for JPY). Range ±9.2e18 in the DB; JS `number` is safe to 2^53 ≈ 9e15
30
+ * minor units = ~90 trillion EUR. INTEGER would cap at ~21 million EUR per
31
+ * row which is too tight for real-world invoices, bank balances, or bills
32
+ * of sale — hence bigint.
33
+ */
34
+ export const moneyAmount = customType<{ data: number; driverData: string | number }>({
35
+ dataType() {
36
+ return "bigint";
37
+ },
38
+ fromDriver(value: string | number): number {
39
+ // node-postgres returns BIGINT as string by default; Bun's pg returns
40
+ // number. Cast via Number() either way — safe because we stay under 2^53.
41
+ return typeof value === "number" ? value : Number(value);
42
+ },
43
+ toDriver(value: number): number {
44
+ return value;
45
+ },
46
+ });
47
+
48
+ /**
49
+ * Instant column: TIMESTAMPTZ storing a UTC instant, surfaced to JS as
50
+ * `Temporal.Instant`. Replaces the old dual-mode situation (`mode:"date"`
51
+ * for base fields vs `mode:"string"` for user-defined timestamp fields)
52
+ * with a single round-trip type. See sprint-f-temporal.md for the migration.
53
+ *
54
+ * Driver-data is the ISO-8601 string the postgres driver actually exchanges.
55
+ * `fromDriver` reads what the driver returns (postgres-js gives strings for
56
+ * timestamptz) and parses through Temporal. `toDriver` writes the canonical
57
+ * Temporal.Instant.toString() — the spike confirmed `eq/lte/gt/orderBy/
58
+ * returning` accept Temporal.Instant directly without manual `.toString()`
59
+ * at the call site.
60
+ *
61
+ * Boot-order note: `Temporal` must exist on globalThis before any
62
+ * fromDriver/toDriver call. `ensureTemporalPolyfill()` runs at framework
63
+ * boot. The closures here are lazy — they fire on read/write, not on
64
+ * module load — so importing this file before the polyfill is safe.
65
+ *
66
+ * Optional `precision` (0..6) — fractional-second digits. Default 6 matches
67
+ * PG's default `timestamptz`. Pass 3 for the events-table (ms precision —
68
+ * matches what asOf-queries can compare reliably). Affects only CREATE TABLE
69
+ * DDL via drizzle-kit; runtime parse handles any precision via Temporal.
70
+ */
71
+ // Forgiving overload: payloads from custom write-handlers sometimes
72
+ // arrive as ISO strings rather than Temporal.Instant (Zod insert-
73
+ // schemas use z.iso.datetime, not a Temporal validator). Coerce here
74
+ // at the boundary so the DB always sees a normalised string — and
75
+ // Temporal.Instant.from throws on bad input, which is the right failure
76
+ // mode (vs. the obscure "x.toString is not a function" crash that hit
77
+ // feature authors before this overload existed).
78
+ //
79
+ // Date-only strings (YYYY-MM-DD) coercen wir auf start-of-day UTC.
80
+ // Hintergrund: type:"date" ist heute auf instant() aliased (table-
81
+ // builder.ts TODO Sprint G), aber die Zod-Validation akzeptiert nur
82
+ // YYYY-MM-DD. Ohne diesen Coerce wirft Temporal mit "Cannot parse:
83
+ // 2026-04-10" und der Author bekommt einen internal_error 500 statt
84
+ // einen sauberen DB-Roundtrip.
85
+ //
86
+ // Exportiert für Tests; production-call läuft über instantBuilder.
87
+ export function instantToDriver(value: Temporal.Instant | string): string {
88
+ if (typeof value === "string") {
89
+ const dateOnly = /^\d{4}-\d{2}-\d{2}$/.test(value);
90
+ const iso = dateOnly ? `${value}T00:00:00Z` : value;
91
+ return Temporal.Instant.from(iso).toString();
92
+ }
93
+ return value.toString();
94
+ }
95
+
96
+ const instantBuilder = (config?: { precision?: 0 | 1 | 2 | 3 | 4 | 5 | 6 }) =>
97
+ customType<{ data: Temporal.Instant; driverData: string }>({
98
+ dataType() {
99
+ const p = config?.precision;
100
+ return p !== undefined ? `timestamp(${p}) with time zone` : "timestamptz";
101
+ },
102
+ fromDriver(value: string): Temporal.Instant {
103
+ return Temporal.Instant.from(value);
104
+ },
105
+ toDriver: instantToDriver,
106
+ });
107
+ export function instant(name: string, config?: { precision?: 0 | 1 | 2 | 3 | 4 | 5 | 6 }) {
108
+ return instantBuilder(config)(name);
109
+ }
@@ -0,0 +1,174 @@
1
+ // Tier 2.7e Server-Side Eagerload für Reference-Felder.
2
+ //
3
+ // Nach `executor.list`/`detail` scannen wir die zurückgelieferten
4
+ // rows nach reference-Field-Values, sammeln pro Reference die UUIDs
5
+ // (deduped), führen einen einzigen WHERE id IN (...)-Lookup pro
6
+ // Referenced-Entity aus, und hängen die resolved Rows als
7
+ // `_refs.<fieldName>` (single) bzw. `_refs.<fieldName>: Row[]`
8
+ // (multiple) an die Original-Rows.
9
+ //
10
+ // Tenant-Scope: TenantDb hat den Tenant-Filter eingebaut (mode:
11
+ // "tenant"); der Lookup erbt das transparent. Cross-Feature-Refs
12
+ // landen automatisch im selben Tenant — falls ein referenced Item
13
+ // dem User nicht gehört, kommt es aus dem Lookup nicht zurück
14
+ // (TenantDb filtert), und der Renderer fällt auf UUID zurück.
15
+ //
16
+ // Limit: kein expliziter limit auf den Lookup-SELECT — wir
17
+ // fragen genau die UUIDs ab die in den main-Rows vorkommen, also
18
+ // O(n) pro Page (bei pageSize:50 mit 2 ref-Spalten = max 100 IDs).
19
+ // Render-Side limit:200-Workaround entfällt damit komplett.
20
+ //
21
+ // Diese Datei lebt im framework/db damit sie an einer Stelle
22
+ // zwischen executor und entity-handlers gemounted ist; sie nutzt
23
+ // keine framework-engine-Internals und kann auch von custom
24
+ // query-handlern manuell aufgerufen werden.
25
+
26
+ import { inArray } from "drizzle-orm";
27
+ import type { EntityDefinition, FieldDefinition, ReferenceFieldDef } from "../engine/types";
28
+ import { buildDrizzleTable } from "./table-builder";
29
+ import type { TenantDb } from "./tenant-db";
30
+
31
+ // Minimaler Registry-Lookup-Contract: pro entity-name → EntityDefinition.
32
+ // Wir importieren NICHT den ganzen Registry-Type weil das einen
33
+ // circular import zwischen db/ und engine/ erzeugen würde — der
34
+ // Caller (entity-handlers.ts) hat ctx.registry und reicht hier eine
35
+ // Closure rein.
36
+ export type EagerLoadEntityResolver = (entityName: string) => EntityDefinition | undefined;
37
+
38
+ // Tier 2.7e Audit-Fix #6: zentral typed Row-Shape mit _refs. Der
39
+ // `_refs`-Property ist Server-Eagerload-Output: pro reference-Field
40
+ // die resolved Row (single) oder ein Array resolved Rows (multiple).
41
+ // Eine reference-Spalte mit value=null hat _refs[fieldName]=undefined.
42
+ //
43
+ // Renderer/Cell-Code liest `row._refs?.[fieldName]` statt inline-Cast;
44
+ // Server-Code stempelt `_refs` über enrichWithReferences. Type ist
45
+ // strukturell — auch Apps die ihre eigenen Refs setzen (Custom-
46
+ // Handler) sollten das hier wiederverwenden.
47
+ export type EagerloadedRow<T extends Record<string, unknown> = Record<string, unknown>> = T & {
48
+ readonly _refs?: Readonly<
49
+ Record<string, Record<string, unknown> | ReadonlyArray<Record<string, unknown>> | undefined>
50
+ >;
51
+ };
52
+
53
+ type ReferenceFieldEntry = {
54
+ readonly fieldName: string;
55
+ readonly refEntityName: string;
56
+ readonly multiple: boolean;
57
+ };
58
+
59
+ function isReferenceField(field: FieldDefinition): field is ReferenceFieldDef {
60
+ return field.type === "reference";
61
+ }
62
+
63
+ function parseRefEntity(raw: string): string {
64
+ // Same-feature ("user") oder cross-feature ("users:user") — wir
65
+ // brauchen nur den entity-name (Names sind global eindeutig in
66
+ // entityMap). Der feature-prefix dient nur der Author-Klarheit.
67
+ const idx = raw.indexOf(":");
68
+ return idx < 0 ? raw : raw.slice(idx + 1);
69
+ }
70
+
71
+ export function collectReferenceFields(entity: EntityDefinition): readonly ReferenceFieldEntry[] {
72
+ const out: ReferenceFieldEntry[] = [];
73
+ for (const [fieldName, fieldDef] of Object.entries(entity.fields)) {
74
+ if (!isReferenceField(fieldDef)) continue;
75
+ out.push({
76
+ fieldName,
77
+ refEntityName: parseRefEntity(fieldDef.entity),
78
+ multiple: fieldDef.multiple === true,
79
+ });
80
+ }
81
+ return out;
82
+ }
83
+
84
+ /** Eagerload für eine Liste von Rows. Mutiert nicht — gibt eine
85
+ * flache Kopie der Rows mit hinzugefügtem `_refs`-Property zurück. */
86
+ export async function enrichWithReferences(
87
+ rows: ReadonlyArray<Record<string, unknown>>,
88
+ entity: EntityDefinition,
89
+ resolveEntity: EagerLoadEntityResolver,
90
+ db: TenantDb,
91
+ ): Promise<Array<Record<string, unknown>>> {
92
+ const refFields = collectReferenceFields(entity);
93
+ if (refFields.length === 0 || rows.length === 0) {
94
+ return rows.map((r) => ({ ...r }));
95
+ }
96
+
97
+ // Pro reference-Field: deduped Set der IDs sammeln, dann ein
98
+ // einziger SELECT WHERE id IN (...). Maps werden parallel gebaut
99
+ // damit die Lookups nicht serialisieren (Promise.all).
100
+ const lookupMaps = await Promise.all(
101
+ refFields.map(async (rf) => {
102
+ const ids = new Set<string>();
103
+ for (const row of rows) {
104
+ const v = row[rf.fieldName];
105
+ if (rf.multiple) {
106
+ if (Array.isArray(v)) {
107
+ for (const item of v) {
108
+ if (typeof item === "string" && item.length > 0) ids.add(item);
109
+ }
110
+ }
111
+ } else if (typeof v === "string" && v.length > 0) {
112
+ ids.add(v);
113
+ }
114
+ }
115
+ if (ids.size === 0) return { fieldName: rf.fieldName, multiple: rf.multiple, map: new Map() };
116
+ const refEntity = resolveEntity(rf.refEntityName);
117
+ if (refEntity === undefined) {
118
+ // Author-Fehler oder Race-Condition (entity gerade umbenannt
119
+ // ohne registry-Reload). Boot-Validator hat das normalerweise
120
+ // gepinnt; Runtime-Defense: leere Map → Renderer fällt auf
121
+ // UUID zurück, kein Crash.
122
+ return { fieldName: rf.fieldName, multiple: rf.multiple, map: new Map() };
123
+ }
124
+ const refTable = buildDrizzleTable(rf.refEntityName, refEntity);
125
+ const idCol = refTable["id"];
126
+ if (idCol === undefined) {
127
+ return { fieldName: rf.fieldName, multiple: rf.multiple, map: new Map() };
128
+ }
129
+ const idArray = [...ids];
130
+ const refRows = (await db
131
+ .select()
132
+ .from(refTable)
133
+ .where(inArray(idCol, idArray as never /* @cast-boundary db-operator */))) as Array<
134
+ Record<string, unknown>
135
+ >; // @cast-boundary db-row
136
+ const map = new Map<string, Record<string, unknown>>();
137
+ for (const r of refRows) {
138
+ const id = r["id"];
139
+ if (typeof id === "string") map.set(id, r);
140
+ }
141
+ return { fieldName: rf.fieldName, multiple: rf.multiple, map };
142
+ }),
143
+ );
144
+
145
+ return rows.map((row) => {
146
+ const refs: Record<string, unknown> = {};
147
+ for (const lookup of lookupMaps) {
148
+ const v = row[lookup.fieldName];
149
+ if (lookup.multiple) {
150
+ const ids = Array.isArray(v) ? v : [];
151
+ const resolved = ids
152
+ .map((id) => (typeof id === "string" ? lookup.map.get(id) : undefined))
153
+ .filter((r) => r !== undefined);
154
+ refs[lookup.fieldName] = resolved;
155
+ } else if (typeof v === "string" && v.length > 0) {
156
+ refs[lookup.fieldName] = lookup.map.get(v);
157
+ } else {
158
+ refs[lookup.fieldName] = undefined;
159
+ }
160
+ }
161
+ return { ...row, _refs: refs };
162
+ });
163
+ }
164
+
165
+ /** Single-Row-Variante für detail-Calls. */
166
+ export async function enrichRowWithReferences(
167
+ row: Record<string, unknown>,
168
+ entity: EntityDefinition,
169
+ resolveEntity: EagerLoadEntityResolver,
170
+ db: TenantDb,
171
+ ): Promise<Record<string, unknown>> {
172
+ const enriched = await enrichWithReferences([row], entity, resolveEntity, db);
173
+ return enriched[0] ?? { ...row, _refs: {} };
174
+ }
@@ -0,0 +1,39 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
2
+
3
+ const ALGORITHM = "aes-256-gcm";
4
+ const IV_LENGTH = 12;
5
+ const TAG_LENGTH = 16;
6
+
7
+ export type EncryptionProvider = {
8
+ encrypt(plaintext: string): string;
9
+ decrypt(ciphertext: string): string;
10
+ };
11
+
12
+ export function createEncryptionProvider(key: string): EncryptionProvider {
13
+ // Key must be 32 bytes for AES-256
14
+ const keyBuffer = Buffer.from(key, "base64");
15
+ if (keyBuffer.length !== 32) {
16
+ throw new Error("ENCRYPTION_KEY must be 32 bytes (base64 encoded)");
17
+ }
18
+
19
+ return {
20
+ encrypt(plaintext: string): string {
21
+ const iv = randomBytes(IV_LENGTH);
22
+ const cipher = createCipheriv(ALGORITHM, keyBuffer, iv);
23
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
24
+ const tag = cipher.getAuthTag();
25
+ // Format: base64(iv + tag + ciphertext)
26
+ return Buffer.concat([iv, tag, encrypted]).toString("base64");
27
+ },
28
+
29
+ decrypt(ciphertext: string): string {
30
+ const data = Buffer.from(ciphertext, "base64");
31
+ const iv = data.subarray(0, IV_LENGTH);
32
+ const tag = data.subarray(IV_LENGTH, IV_LENGTH + TAG_LENGTH);
33
+ const encrypted = data.subarray(IV_LENGTH + TAG_LENGTH);
34
+ const decipher = createDecipheriv(ALGORITHM, keyBuffer, iv);
35
+ decipher.setAuthTag(tag);
36
+ return decipher.update(encrypted) + decipher.final("utf8");
37
+ },
38
+ };
39
+ }