@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,154 @@
1
+ import { and, getTableName, inArray, lt, sql } from "drizzle-orm";
2
+ import type { DbConnection } from "../db/connection";
3
+ import { eventsTable } from "../event-store";
4
+ import { eventConsumerStateTable } from "./event-consumer-state";
5
+
6
+ // Retention for the events-table. Aggregate events are source of truth —
7
+ // they power loadAggregate, projection rebuilds, asOf queries, audit.
8
+ // Pruning them is destructive and cannot be reversed, so the caller MUST
9
+ // name the aggregateTypes explicitly; there is no default.
10
+ //
11
+ // Typical use: ops prunes archived aggregate streams (see archive.ts) after
12
+ // legal/compliance retention has elapsed, or drops a specific aggregate
13
+ // type that's been deprecated and replaced.
14
+ //
15
+ // Safety guard: before deleting, we check every row in
16
+ // `kumiko_event_consumers`. If the minimum `lastProcessedEventId` across
17
+ // non-disabled consumers is below the largest event id we'd delete, we
18
+ // refuse with ConsumerLagError. A lagging consumer must either catch up,
19
+ // be disabled, or the retention call must opt around it; pruning past its
20
+ // cursor would silently drop deliveries.
21
+ //
22
+ // No background scheduler: the framework exposes the function. Ops wires
23
+ // it into a cron, BullMQ repeating job, or pg_cron entry — whatever the
24
+ // deployment already runs. That keeps the framework dependency-free for
25
+ // retention and lets ops reason about timing alongside existing jobs.
26
+
27
+ export type PruneEventsOptions = {
28
+ // Delete events whose createdAt is strictly older than this.
29
+ // Pass EITHER olderThan (explicit Temporal.Instant) OR olderThanDays.
30
+ readonly olderThan?: Temporal.Instant;
31
+ readonly olderThanDays?: number;
32
+ // Which aggregateTypes to prune. REQUIRED and non-empty. There is no
33
+ // default — pruning the event log is destructive, so the caller has to
34
+ // name what they're destroying.
35
+ readonly aggregateTypes: readonly string[];
36
+ // Dry-run: compute what would be deleted, return count, delete nothing.
37
+ readonly dryRun?: boolean;
38
+ };
39
+
40
+ export type PruneEventsResult = {
41
+ readonly deletedCount: number;
42
+ readonly cutoff: Temporal.Instant;
43
+ readonly aggregateTypes: readonly string[];
44
+ readonly dryRun: boolean;
45
+ };
46
+
47
+ export class ConsumerLagError extends Error {
48
+ constructor(
49
+ readonly laggingConsumer: string,
50
+ readonly consumerCursor: bigint,
51
+ readonly maxCandidateId: bigint,
52
+ ) {
53
+ super(
54
+ `Consumer "${laggingConsumer}" is behind the prune candidates ` +
55
+ `(cursor=${consumerCursor}, max candidate event id=${maxCandidateId}). ` +
56
+ `Pruning would silently drop deliveries. Catch up, disable, or skip the consumer first.`,
57
+ );
58
+ this.name = "ConsumerLagError";
59
+ }
60
+ }
61
+
62
+ function resolveCutoff(opts: PruneEventsOptions): Temporal.Instant {
63
+ if (opts.olderThan) return opts.olderThan;
64
+ const days = opts.olderThanDays;
65
+ if (days === undefined || days <= 0) {
66
+ throw new Error(
67
+ "pruneEvents: pass olderThan (Temporal.Instant) or olderThanDays (positive number).",
68
+ );
69
+ }
70
+ return Temporal.Now.instant().subtract({ hours: days * 24 });
71
+ }
72
+
73
+ export async function pruneEvents(
74
+ db: DbConnection,
75
+ options: PruneEventsOptions,
76
+ ): Promise<PruneEventsResult> {
77
+ const cutoff = resolveCutoff(options);
78
+ if (!options.aggregateTypes || options.aggregateTypes.length === 0) {
79
+ throw new Error(
80
+ "pruneEvents: aggregateTypes is required and must be non-empty. Pruning the event log is destructive — name the aggregate types to delete explicitly.",
81
+ );
82
+ }
83
+ const aggregateTypes = options.aggregateTypes;
84
+ const dryRun = options.dryRun === true;
85
+
86
+ return db.transaction(async (tx) => {
87
+ // Serialise against consumer-bootstrap INSERTs. Without this, the race
88
+ // is: prune reads consumers (snapshot misses a consumer bootstrapping
89
+ // in a parallel tx) → consumer commits its row with
90
+ // lastProcessedEventId=0 → prune deletes events below its new cursor
91
+ // → first pass of the new consumer silently skips the deleted events.
92
+ //
93
+ // SHARE is the lightest table-mode that conflicts with ROW EXCLUSIVE
94
+ // (the mode INSERT/UPDATE/DELETE take). Concurrent INSERTs on the
95
+ // consumer table queue until this TX commits; concurrent UPDATEs
96
+ // (cursor advances) do too, but prune is measured in milliseconds and
97
+ // pausing cursor advances for that window is cheap insurance against
98
+ // a silent data-loss bug.
99
+ //
100
+ // Drizzle can't express LOCK TABLE — drop to raw SQL with the table
101
+ // name identifier so a future table-rename is caught at compile time.
102
+ await tx.execute(sql.raw(`LOCK TABLE ${getTableName(eventConsumerStateTable)} IN SHARE MODE`));
103
+
104
+ // Step 1 — collect candidate event ids.
105
+ const candidates = await tx
106
+ .select({ id: eventsTable.id })
107
+ .from(eventsTable)
108
+ .where(
109
+ and(
110
+ inArray(eventsTable.aggregateType, [...aggregateTypes]),
111
+ lt(eventsTable.createdAt, cutoff),
112
+ ),
113
+ );
114
+
115
+ if (candidates.length === 0) {
116
+ return { deletedCount: 0, cutoff, aggregateTypes, dryRun };
117
+ }
118
+
119
+ const maxCandidateId = candidates.reduce(
120
+ (acc, row) => (row.id > acc ? row.id : acc),
121
+ candidates[0]?.id ?? 0n,
122
+ );
123
+
124
+ // Step 2 — safety guard: check every ACTIVE consumer has moved past
125
+ // the candidate set. Disabled consumers are intentionally excluded —
126
+ // ops disables them precisely to park them while pruning happens.
127
+ // The SHARE lock above guarantees this SELECT sees a complete view:
128
+ // no new consumer can INSERT a fresh-cursor row between here and the
129
+ // DELETE below.
130
+ const activeConsumers = await tx
131
+ .select()
132
+ .from(eventConsumerStateTable)
133
+ .where(sql`${eventConsumerStateTable.status} <> 'disabled'`);
134
+
135
+ for (const consumer of activeConsumers) {
136
+ if (consumer.lastProcessedEventId < maxCandidateId) {
137
+ throw new ConsumerLagError(consumer.name, consumer.lastProcessedEventId, maxCandidateId);
138
+ }
139
+ }
140
+
141
+ if (dryRun) {
142
+ return { deletedCount: candidates.length, cutoff, aggregateTypes, dryRun: true };
143
+ }
144
+
145
+ // Step 3 — actual delete, bounded to the candidate set.
146
+ const candidateIds = candidates.map((c) => c.id);
147
+ const deleted = await tx
148
+ .delete(eventsTable)
149
+ .where(inArray(eventsTable.id, candidateIds))
150
+ .returning({ id: eventsTable.id });
151
+
152
+ return { deletedCount: deleted.length, cutoff, aggregateTypes, dryRun: false };
153
+ });
154
+ }
@@ -0,0 +1,76 @@
1
+ import type Redis from "ioredis";
2
+ import { RedisKeys } from "./redis-keys";
3
+
4
+ export type IdempotencyGuard = {
5
+ check(requestId: string): Promise<string | null>;
6
+ store(requestId: string, result: unknown): Promise<void>;
7
+ };
8
+
9
+ // Sentinel stored under the key while the handler is running. A second
10
+ // request that sees this waits for the real result instead of racing.
11
+ const PENDING_MARKER = "__pending__";
12
+
13
+ export function createIdempotencyGuard(
14
+ redis: Redis,
15
+ options: {
16
+ ttlSeconds?: number;
17
+ pendingTtlSeconds?: number;
18
+ waitTimeoutMs?: number;
19
+ pollIntervalMs?: number;
20
+ } = {},
21
+ ): IdempotencyGuard {
22
+ const ttl = options.ttlSeconds ?? 300;
23
+ // Max time a single handler is allowed to hold the in-progress lock before
24
+ // a parallel retry is allowed to try again. Short enough that a crashed
25
+ // handler doesn't permanently block retries, long enough to cover normal
26
+ // batch durations.
27
+ const pendingTtl = options.pendingTtlSeconds ?? 30;
28
+ const waitTimeoutMs = options.waitTimeoutMs ?? 25_000;
29
+ const pollIntervalMs = options.pollIntervalMs ?? 50;
30
+ const prefix = RedisKeys.idempotency;
31
+
32
+ return {
33
+ // Returns:
34
+ // null — caller owns the in-progress lock, proceed to run the handler
35
+ // and then call store() with the real result.
36
+ // string — serialized cached result from a concurrent or prior request.
37
+ //
38
+ // The old behaviour (pure GET + SET-NX-store) let two parallel requests
39
+ // both see a cache miss, both execute side-effects, and only one persist
40
+ // the result. This version uses a pending-marker lock so the second caller
41
+ // waits for the first to finish and reuses its result.
42
+ async check(requestId) {
43
+ const key = `${prefix}${requestId}`;
44
+
45
+ // Try to acquire the in-progress lock.
46
+ const acquired = await redis.set(key, PENDING_MARKER, "EX", pendingTtl, "NX");
47
+ if (acquired === "OK") return null;
48
+
49
+ // Lost the race. Poll until the lock holder stores a result, or the
50
+ // lock expires (handler crashed) and we can try again.
51
+ const deadline = Date.now() + waitTimeoutMs;
52
+ while (Date.now() < deadline) {
53
+ const value = await redis.get(key);
54
+ if (value === null) {
55
+ // Lock expired before a result was stored — try to claim it
56
+ // ourselves and proceed as the new owner.
57
+ const reclaimed = await redis.set(key, PENDING_MARKER, "EX", pendingTtl, "NX");
58
+ if (reclaimed === "OK") return null;
59
+ continue;
60
+ }
61
+ if (value !== PENDING_MARKER) return value;
62
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
63
+ }
64
+
65
+ // Gave up waiting. Treat as a fresh request — forces the caller to
66
+ // re-run the handler rather than hang indefinitely.
67
+ return null;
68
+ },
69
+
70
+ async store(requestId, result) {
71
+ // Overwrite the pending marker with the real result. Plain SET (no NX)
72
+ // on purpose: we own the lock; writing the result is the final step.
73
+ await redis.set(`${prefix}${requestId}`, JSON.stringify(result), "EX", ttl);
74
+ },
75
+ };
76
+ }
@@ -0,0 +1,66 @@
1
+ export type { ResolveAuthClaimsArgs } from "./auth-claims-resolver";
2
+ export { resolveAuthClaims } from "./auth-claims-resolver";
3
+ export { createCascadeDeleteHook } from "./cascade-handler";
4
+ export type { Dispatcher } from "./dispatcher";
5
+ export { createDispatcher } from "./dispatcher";
6
+ export type { DistributedLock } from "./distributed-lock";
7
+ export { createDistributedLock } from "./distributed-lock";
8
+ export type { EntityCache, EntityCacheOptions } from "./entity-cache";
9
+ export { createEntityCache } from "./entity-cache";
10
+ export type { ConsumerStatus } from "./event-consumer-state";
11
+ export {
12
+ CONSUMER_STATUSES,
13
+ ConsumerStatuses,
14
+ createEventConsumerStateTable,
15
+ eventConsumerStateTable,
16
+ } from "./event-consumer-state";
17
+ export type { EventDedup } from "./event-dedup";
18
+ export { createEventDedup } from "./event-dedup";
19
+ export type {
20
+ ConsumerProgress,
21
+ ConsumerRecoveryState,
22
+ DispatcherPassResult,
23
+ EventConsumer,
24
+ EventConsumerHandler,
25
+ EventDispatcher,
26
+ EventDispatcherOptions,
27
+ } from "./event-dispatcher";
28
+ export {
29
+ createEventDispatcher,
30
+ disableConsumer,
31
+ enableConsumer,
32
+ getAllConsumerProgress,
33
+ getConsumerState,
34
+ listConsumersWithState,
35
+ restartConsumer,
36
+ skipPoisonEvent,
37
+ } from "./event-dispatcher";
38
+ export type { PruneEventsOptions, PruneEventsResult } from "./event-retention";
39
+ export { ConsumerLagError, pruneEvents } from "./event-retention";
40
+ export type { IdempotencyGuard } from "./idempotency";
41
+ export { createIdempotencyGuard } from "./idempotency";
42
+ export type { LifecycleHooks, SystemHookDef, SystemHooks } from "./lifecycle-pipeline";
43
+ export { createLifecycleHooks } from "./lifecycle-pipeline";
44
+ export type { MspRebuildDeps } from "./msp-rebuild";
45
+ export { rebuildMultiStreamProjection } from "./msp-rebuild";
46
+ export type { ProjectionProgress, RebuildResult } from "./projection-rebuild";
47
+ export {
48
+ getAllProjectionProgress,
49
+ getProjectionState,
50
+ listProjectionsWithState,
51
+ rebuildProjection,
52
+ } from "./projection-rebuild";
53
+ export type { ProjectionStatus } from "./projection-state";
54
+ export {
55
+ createProjectionStateTable,
56
+ PROJECTION_STATUSES,
57
+ ProjectionStatuses,
58
+ projectionStateTable,
59
+ } from "./projection-state";
60
+ export { runProjectionsForEvent } from "./projections-runner";
61
+ export {
62
+ createSearchEventConsumer,
63
+ createSseBroadcastEventConsumer,
64
+ SEARCH_CONSUMER_NAME,
65
+ SSE_BROADCAST_CONSUMER_NAME,
66
+ } from "./system-hooks";