@cosmicdrift/kumiko-framework 1.0.0 → 2.0.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 (424) hide show
  1. package/README.md +9 -38
  2. package/package.json +21 -2
  3. package/src/__tests__/anonymous-access.integration.test.ts +185 -0
  4. package/src/__tests__/consumer-cli.integration.test.ts +172 -0
  5. package/src/__tests__/entity-permalink-open.integration.test.ts +94 -0
  6. package/src/__tests__/full-stack.integration.test.ts +1 -1
  7. package/src/__tests__/schema-cli-temporal-polyfill.test.ts +48 -0
  8. package/src/__tests__/schema-cli.integration.test.ts +50 -0
  9. package/src/__tests__/store-table.integration.test.ts +93 -0
  10. package/src/api/__tests__/api.test.ts +373 -0
  11. package/src/api/__tests__/auth-middleware-anonymous-access-boot.test.ts +40 -0
  12. package/src/api/__tests__/auth-routes-cookie.test.ts +17 -1
  13. package/src/api/__tests__/auth-routes-invalid-body-invite.test.ts +253 -0
  14. package/src/api/__tests__/auth-routes-mfa-preauth-confirm.test.ts +222 -0
  15. package/src/api/__tests__/auth-routes-mfa-preauth-enable-start.test.ts +249 -0
  16. package/src/api/__tests__/auth-routes-mfa-verify.test.ts +202 -0
  17. package/src/api/__tests__/auth-routes-trusted-proxy.test.ts +135 -0
  18. package/src/api/__tests__/batch.integration.test.ts +101 -11
  19. package/src/api/__tests__/csrf-constants-sync.test.ts +20 -0
  20. package/src/api/__tests__/dispatcher-live.integration.test.ts +74 -0
  21. package/src/api/__tests__/jwt.test.ts +200 -1
  22. package/src/api/__tests__/login-rate-limiter-sweep.test.ts +50 -0
  23. package/src/api/__tests__/pat-scope.test.ts +36 -0
  24. package/src/api/__tests__/pii-leak-guard.integration.test.ts +103 -0
  25. package/src/api/__tests__/redis-login-rate-limiter.integration.test.ts +138 -0
  26. package/src/api/__tests__/request-id-middleware.test.ts +51 -0
  27. package/src/api/__tests__/server-boot-guards.test.ts +71 -0
  28. package/src/api/__tests__/server-jwt-ttl.test.ts +58 -0
  29. package/src/api/__tests__/sse-broker.test.ts +57 -0
  30. package/src/api/__tests__/sse-route.test.ts +4 -0
  31. package/src/api/api-constants.ts +11 -0
  32. package/src/api/auth-middleware.ts +248 -40
  33. package/src/api/auth-routes.ts +576 -95
  34. package/src/api/index.ts +13 -4
  35. package/src/api/jwt.ts +170 -11
  36. package/src/api/pat-scope.ts +14 -0
  37. package/src/api/pii-leak-guard.ts +47 -0
  38. package/src/api/request-context.ts +3 -0
  39. package/src/api/request-id-middleware.ts +2 -0
  40. package/src/api/routes.ts +169 -2
  41. package/src/api/server.ts +112 -16
  42. package/src/api/sse-broker.ts +39 -0
  43. package/src/bun-db/__tests__/PATTERN.md +0 -1
  44. package/src/bun-db/__tests__/coerce-row-temporal.test.ts +41 -0
  45. package/src/bun-db/__tests__/select-many-retry.test.ts +79 -0
  46. package/src/bun-db/__tests__/write-brand.test.ts +21 -0
  47. package/src/bun-db/connection.ts +3 -3
  48. package/src/bun-db/index.ts +2 -0
  49. package/src/bun-db/query.ts +105 -32
  50. package/src/consumer-cli.ts +134 -0
  51. package/src/crypto/__tests__/blind-index.test.ts +130 -0
  52. package/src/crypto/__tests__/event-pii.test.ts +161 -0
  53. package/src/crypto/__tests__/kek-rotation.integration.test.ts +180 -0
  54. package/src/crypto/__tests__/kms-adapter-contract.ts +134 -0
  55. package/src/crypto/__tests__/kms-adapter.contract.test.ts +4 -0
  56. package/src/crypto/__tests__/pg-kms-adapter.integration.test.ts +117 -0
  57. package/src/crypto/__tests__/pii-field-encryption.test.ts +376 -0
  58. package/src/crypto/__tests__/request-kms-cache.test.ts +74 -0
  59. package/src/crypto/__tests__/subject-resolver.test.ts +108 -0
  60. package/src/crypto/blind-index.ts +118 -0
  61. package/src/crypto/event-pii.ts +70 -0
  62. package/src/crypto/in-memory-kms-adapter.ts +50 -0
  63. package/src/crypto/index.ts +67 -0
  64. package/src/crypto/kms-adapter.ts +2 -0
  65. package/src/crypto/pg-kms-adapter.ts +295 -0
  66. package/src/crypto/pii-field-encryption.ts +248 -0
  67. package/src/crypto/request-kms-cache.ts +38 -0
  68. package/src/crypto/subject-resolver.ts +91 -0
  69. package/src/db/__tests__/assert-no-unreachable-live-rows.integration.test.ts +191 -0
  70. package/src/db/__tests__/blind-index.integration.test.ts +248 -0
  71. package/src/db/__tests__/build-filter-where.test.ts +34 -0
  72. package/src/db/__tests__/collect-table-metas.test.ts +10 -10
  73. package/src/db/__tests__/config-seed.integration.test.ts +13 -5
  74. package/src/db/__tests__/dialect-instant.test.ts +1 -4
  75. package/src/db/__tests__/entity-field-encryption.test.ts +7 -7
  76. package/src/db/__tests__/entity-table-meta-source.test.ts +50 -0
  77. package/src/db/__tests__/event-store-executor-context.pii-roundtrip.test.ts +67 -0
  78. package/src/db/__tests__/event-store-executor-write-verbs.integration.test.ts +402 -0
  79. package/src/db/__tests__/event-store-executor.integration.test.ts +299 -29
  80. package/src/db/__tests__/feature-table-sources.test.ts +34 -0
  81. package/src/db/__tests__/implicit-projection-equivalence.integration.test.ts +118 -39
  82. package/src/db/__tests__/instant-to-driver-temporal.test.ts +21 -0
  83. package/src/db/__tests__/located-timestamp.test.ts +19 -0
  84. package/src/db/__tests__/migrate-generator.test.ts +21 -0
  85. package/src/db/__tests__/migrate-runner.test.ts +61 -0
  86. package/src/db/__tests__/number-field-fractional.integration.test.ts +58 -0
  87. package/src/db/__tests__/rebuild-marker.test.ts +13 -3
  88. package/src/db/__tests__/replay-migration-sql.test.ts +384 -0
  89. package/src/db/__tests__/schema-inspection.test.ts +7 -0
  90. package/src/db/__tests__/schema-migration.integration.test.ts +1 -1
  91. package/src/db/__tests__/table-builder-meta-lockstep.test.ts +39 -0
  92. package/src/db/__tests__/tenant-db-where-merge.test.ts +49 -3
  93. package/src/db/__tests__/tenant-db.integration.test.ts +6 -2
  94. package/src/db/api.ts +2 -2
  95. package/src/db/apply-entity-event.ts +18 -14
  96. package/src/db/blind-index-cleanup.ts +55 -0
  97. package/src/db/bun-provider.ts +2 -2
  98. package/src/db/collect-table-metas.ts +6 -7
  99. package/src/db/config-seed.ts +9 -9
  100. package/src/db/connection.ts +7 -12
  101. package/src/db/cursor.ts +1 -18
  102. package/src/db/dialect.ts +20 -26
  103. package/src/db/entity-field-encryption.ts +81 -24
  104. package/src/db/entity-table-meta-types.ts +2 -0
  105. package/src/db/entity-table-meta.ts +63 -90
  106. package/src/db/event-store-executor-context.ts +404 -0
  107. package/src/db/event-store-executor-read.ts +275 -0
  108. package/src/db/event-store-executor-write.ts +644 -0
  109. package/src/db/event-store-executor.ts +28 -1155
  110. package/src/db/feature-table-sources.ts +6 -5
  111. package/src/db/index.ts +20 -3
  112. package/src/db/located-timestamp.ts +4 -0
  113. package/src/db/migrate-generator.ts +39 -6
  114. package/src/db/migrate-runner.ts +107 -11
  115. package/src/db/pg-error.ts +9 -1
  116. package/src/db/postgres-provider.ts +2 -2
  117. package/src/db/queries/__tests__/event-store-idempotency-index.integration.test.ts +80 -0
  118. package/src/db/queries/backfill-pii.ts +277 -0
  119. package/src/db/queries/ddl.ts +45 -0
  120. package/src/db/queries/event-consumer.ts +35 -2
  121. package/src/db/queries/event-store.ts +126 -19
  122. package/src/db/queries/projection-rebuild.ts +22 -8
  123. package/src/db/queries/shadow-swap.ts +183 -0
  124. package/src/db/queries/test-stack.ts +4 -30
  125. package/src/db/query-api.ts +1 -0
  126. package/src/db/query.ts +1 -0
  127. package/src/db/rebuild-marker.ts +18 -2
  128. package/src/db/reference-data.ts +2 -3
  129. package/src/db/replay-migration-sql.ts +257 -0
  130. package/src/db/schema-inspection.ts +1 -1
  131. package/src/db/table-builder.ts +102 -71
  132. package/src/db/tenant-db.ts +15 -53
  133. package/src/engine/__tests__/boot-validator-action-wiring.test.ts +242 -0
  134. package/src/engine/__tests__/boot-validator-boot-check.test.ts +99 -0
  135. package/src/engine/__tests__/boot-validator-dashboard.test.ts +237 -0
  136. package/src/engine/__tests__/boot-validator-entity-list.test.ts +104 -0
  137. package/src/engine/__tests__/boot-validator-gdpr-storage.test.ts +7 -69
  138. package/src/engine/__tests__/boot-validator-i18n-keys.test.ts +26 -5
  139. package/src/engine/__tests__/boot-validator-located-timestamps.test.ts +3 -19
  140. package/src/engine/__tests__/boot-validator-pii-retention.test.ts +246 -4
  141. package/src/engine/__tests__/boot-validator.test.ts +219 -15
  142. package/src/engine/__tests__/build-app-schema.test.ts +82 -0
  143. package/src/engine/__tests__/build-config-feature-schema.test.ts +125 -0
  144. package/src/engine/__tests__/build-target.test.ts +3 -11
  145. package/src/engine/__tests__/codemod-pipeline.test.ts +152 -21
  146. package/src/engine/__tests__/config-helpers.test.ts +16 -0
  147. package/src/engine/__tests__/define-feature-entity-mapping.test.ts +6 -0
  148. package/src/engine/__tests__/define-roles.test.ts +21 -0
  149. package/src/engine/__tests__/engine.test.ts +163 -1
  150. package/src/engine/__tests__/entity-handlers.test.ts +80 -0
  151. package/src/engine/__tests__/event-migration-declarative.test.ts +65 -0
  152. package/src/engine/__tests__/event-type-map-augmentation.test.ts +24 -0
  153. package/src/engine/__tests__/extend-entity-projection.test.ts +123 -0
  154. package/src/engine/__tests__/factories-time.test.ts +2 -66
  155. package/src/engine/__tests__/feature-crud-shorthand.test.ts +28 -0
  156. package/src/engine/__tests__/feature-manifest.test.ts +31 -1
  157. package/src/engine/__tests__/field-access.test.ts +23 -1
  158. package/src/engine/__tests__/hook-phases.test.ts +5 -5
  159. package/src/engine/__tests__/membership-roles.test.ts +4 -10
  160. package/src/engine/__tests__/nav.test.ts +86 -1
  161. package/src/engine/__tests__/pipeline-engine.test.ts +9 -9
  162. package/src/engine/__tests__/pipeline-handler.integration.test.ts +18 -18
  163. package/src/engine/__tests__/pipeline-observability.integration.test.ts +2 -2
  164. package/src/engine/__tests__/pipeline-performance.integration.test.ts +3 -3
  165. package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +9 -9
  166. package/src/engine/__tests__/post-query-hook.test.ts +7 -7
  167. package/src/engine/__tests__/registrar-object-form.test.ts +141 -0
  168. package/src/engine/__tests__/registry.test.ts +144 -0
  169. package/src/engine/__tests__/schema-builder.test.ts +18 -0
  170. package/src/engine/__tests__/screen.test.ts +147 -1
  171. package/src/engine/__tests__/soft-delete-cleanup.test.ts +3 -0
  172. package/src/engine/__tests__/store-table.test.ts +229 -0
  173. package/src/engine/__tests__/tier-resolver-extension.test.ts +19 -1
  174. package/src/engine/__tests__/{visual-tree-patterns.test.ts → tree-actions-patterns.test.ts} +7 -95
  175. package/src/engine/__tests__/validate-projection-allowlist.test.ts +15 -15
  176. package/src/engine/boot-validator/__tests__/config-deps.test.ts +92 -0
  177. package/src/engine/boot-validator/action-wiring.ts +149 -0
  178. package/src/engine/boot-validator/boot-check.ts +21 -0
  179. package/src/engine/boot-validator/config-deps.ts +40 -4
  180. package/src/engine/boot-validator/entity-handler.ts +20 -21
  181. package/src/engine/boot-validator/entity-list-screens.ts +88 -0
  182. package/src/engine/boot-validator/gdpr-storage.ts +0 -20
  183. package/src/engine/boot-validator/i18n-keys.ts +58 -4
  184. package/src/engine/boot-validator/index.ts +33 -12
  185. package/src/engine/boot-validator/nav.ts +125 -0
  186. package/src/engine/boot-validator/pii-retention.ts +95 -10
  187. package/src/engine/boot-validator/{screens-nav.ts → screens.ts} +214 -191
  188. package/src/engine/boot-validator/workspaces.ts +68 -0
  189. package/src/engine/build-app-schema.ts +16 -0
  190. package/src/engine/build-config-feature-schema.ts +44 -7
  191. package/src/engine/codemod/pipeline-codemod.ts +5 -5
  192. package/src/engine/config-helpers.ts +15 -0
  193. package/src/engine/constants.ts +32 -6
  194. package/src/engine/create-app.ts +11 -0
  195. package/src/engine/define-feature.ts +95 -947
  196. package/src/engine/define-handler.ts +28 -94
  197. package/src/engine/define-workflow.ts +1 -1
  198. package/src/engine/effective-features.ts +12 -2
  199. package/src/engine/entity-handlers.ts +76 -11
  200. package/src/engine/extensions/tenant-data.ts +19 -0
  201. package/src/engine/extensions/user-data.ts +29 -2
  202. package/src/engine/factories.ts +8 -49
  203. package/src/engine/feature-ast/__tests__/canonical-form.test.ts +26 -29
  204. package/src/engine/feature-ast/__tests__/fixtures/cross-file-registrar/feature.ts +9 -0
  205. package/src/engine/feature-ast/__tests__/fixtures/cross-file-registrar/screens.ts +4 -0
  206. package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +1 -2
  207. package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +18 -8
  208. package/src/engine/feature-ast/__tests__/parse.test.ts +1640 -160
  209. package/src/engine/feature-ast/__tests__/patch.test.ts +206 -12
  210. package/src/engine/feature-ast/__tests__/patcher.test.ts +20 -23
  211. package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +380 -5
  212. package/src/engine/feature-ast/extractors/events.ts +322 -0
  213. package/src/engine/feature-ast/extractors/handlers.ts +234 -0
  214. package/src/engine/feature-ast/extractors/hooks.ts +243 -0
  215. package/src/engine/feature-ast/extractors/index.ts +34 -31
  216. package/src/engine/feature-ast/extractors/jobs-routes.ts +221 -0
  217. package/src/engine/feature-ast/extractors/projections-screens.ts +269 -0
  218. package/src/engine/feature-ast/extractors/round1.ts +3 -3
  219. package/src/engine/feature-ast/extractors/round5.ts +3 -3
  220. package/src/engine/feature-ast/extractors/round6.ts +2 -36
  221. package/src/engine/feature-ast/extractors/shared.ts +64 -6
  222. package/src/engine/feature-ast/index.ts +2 -4
  223. package/src/engine/feature-ast/parse.ts +130 -23
  224. package/src/engine/feature-ast/patch.ts +39 -50
  225. package/src/engine/feature-ast/patcher.ts +37 -38
  226. package/src/engine/feature-ast/patterns.ts +50 -62
  227. package/src/engine/feature-ast/render.ts +67 -44
  228. package/src/engine/feature-builder-state.ts +168 -0
  229. package/src/engine/feature-config-events-jobs.ts +403 -0
  230. package/src/engine/feature-entity-handlers.ts +208 -0
  231. package/src/engine/feature-manifest.ts +2 -1
  232. package/src/engine/feature-ui-extensions.ts +500 -0
  233. package/src/engine/field-access.ts +13 -2
  234. package/src/engine/field-helpers.ts +31 -0
  235. package/src/engine/handler-helpers.ts +26 -0
  236. package/src/engine/hook-helpers.ts +16 -0
  237. package/src/engine/index.ts +23 -7
  238. package/src/engine/membership-roles.ts +13 -0
  239. package/src/engine/object-form.ts +27 -0
  240. package/src/engine/ownership.ts +26 -79
  241. package/src/engine/pattern-library/__tests__/library.test.ts +7 -20
  242. package/src/engine/pattern-library/library.ts +44 -1152
  243. package/src/engine/pattern-library/mixed-schemas.ts +450 -0
  244. package/src/engine/pattern-library/opaque-schemas.ts +124 -0
  245. package/src/engine/pattern-library/shared-fields.ts +75 -0
  246. package/src/engine/pattern-library/static-schemas.ts +456 -0
  247. package/src/engine/pipeline.ts +6 -11
  248. package/src/engine/registry-facade.ts +362 -0
  249. package/src/engine/registry-ingest.ts +478 -0
  250. package/src/engine/registry-state.ts +388 -0
  251. package/src/engine/registry-validate.ts +642 -0
  252. package/src/engine/registry.ts +78 -1658
  253. package/src/engine/run-pipeline.ts +1 -1
  254. package/src/engine/schema-builder.ts +1 -0
  255. package/src/engine/screen-helpers.ts +54 -0
  256. package/src/engine/soft-delete-cleanup.ts +6 -2
  257. package/src/engine/steps/__tests__/duration-utils.test.ts +20 -0
  258. package/src/engine/steps/_duration-utils.ts +2 -0
  259. package/src/engine/steps/unsafe-projection-upsert.ts +1 -4
  260. package/src/engine/tier-resolver-extension.ts +3 -2
  261. package/src/engine/types/config.ts +2 -482
  262. package/src/engine/types/define-handler.ts +2 -0
  263. package/src/engine/types/entity-handlers.ts +2 -0
  264. package/src/engine/types/event-type-map.ts +1 -37
  265. package/src/engine/types/feature.ts +2 -972
  266. package/src/engine/types/fields.ts +2 -675
  267. package/src/engine/types/handlers.ts +2 -774
  268. package/src/engine/types/hooks.ts +2 -184
  269. package/src/engine/types/http-route.ts +1 -54
  270. package/src/engine/types/identifiers.ts +1 -47
  271. package/src/engine/types/index.ts +86 -40
  272. package/src/engine/types/nav.ts +2 -63
  273. package/src/engine/types/ownership.ts +2 -0
  274. package/src/engine/types/projection.ts +2 -138
  275. package/src/engine/types/relations.ts +1 -51
  276. package/src/engine/types/screen.ts +2 -574
  277. package/src/engine/types/step.ts +2 -334
  278. package/src/engine/types/target-ref.ts +1 -21
  279. package/src/engine/types/tree-node.ts +1 -132
  280. package/src/engine/types/workspace.ts +2 -49
  281. package/src/engine/validate-projection-allowlist.ts +6 -6
  282. package/src/entrypoint/__tests__/entrypoint-job-wiring.integration.test.ts +120 -1
  283. package/src/entrypoint/index.ts +49 -6
  284. package/src/errors/classes.ts +22 -1
  285. package/src/errors/field-issue.ts +0 -3
  286. package/src/errors/index.ts +1 -1
  287. package/src/errors/write-error-info.ts +10 -22
  288. package/src/es-ops/README.md +1 -1
  289. package/src/es-ops/__tests__/runner.integration.test.ts +74 -0
  290. package/src/es-ops/context.ts +4 -2
  291. package/src/es-ops/types.ts +8 -1
  292. package/src/event-store/__tests__/admin-api.integration.test.ts +27 -1
  293. package/src/event-store/__tests__/backfill-pii.integration.test.ts +279 -0
  294. package/src/event-store/__tests__/event-store.integration.test.ts +201 -0
  295. package/src/event-store/__tests__/row-to-stored-event.test.ts +2 -2
  296. package/src/event-store/__tests__/snapshot.integration.test.ts +86 -0
  297. package/src/event-store/__tests__/unscoped-stream-primitives.guard.test.ts +58 -0
  298. package/src/event-store/__tests__/upcaster.integration.test.ts +77 -45
  299. package/src/event-store/admin-api.ts +11 -4
  300. package/src/event-store/errors.ts +2 -35
  301. package/src/event-store/event-store.ts +64 -78
  302. package/src/event-store/events-schema.ts +11 -13
  303. package/src/event-store/index.ts +18 -3
  304. package/src/event-store/rebuild-dead-letter.ts +111 -0
  305. package/src/event-store/snapshot.ts +63 -40
  306. package/src/event-store/types.ts +2 -0
  307. package/src/files/__tests__/build-storage-key.test.ts +28 -0
  308. package/src/files/__tests__/file-ref-entity.test.ts +8 -0
  309. package/src/files/__tests__/files.integration.test.ts +24 -0
  310. package/src/files/__tests__/in-memory-provider.contract.test.ts +4 -0
  311. package/src/files/__tests__/local-provider.test.ts +31 -0
  312. package/src/files/__tests__/provider-resolver.test.ts +70 -0
  313. package/src/files/__tests__/write-stream.test.ts +13 -0
  314. package/src/files/file-handle.ts +2 -19
  315. package/src/files/file-ref-entity.ts +2 -2
  316. package/src/files/file-routes.ts +13 -0
  317. package/src/files/index.ts +1 -1
  318. package/src/files/local-provider.ts +27 -8
  319. package/src/files/provider-resolver.ts +31 -20
  320. package/src/files/types.ts +13 -55
  321. package/src/i18n/__tests__/required-surface-keys.test.ts +17 -0
  322. package/src/i18n/required-surface-keys.ts +120 -1
  323. package/src/jobs/__tests__/job-queue-depth.integration.test.ts +82 -0
  324. package/src/jobs/__tests__/jobs.integration.test.ts +329 -3
  325. package/src/jobs/job-runner.ts +82 -12
  326. package/src/logging/types.ts +1 -7
  327. package/src/migrations/pending-rebuilds.ts +1 -1
  328. package/src/observability/__tests__/observability.integration.test.ts +4 -1
  329. package/src/observability/__tests__/recording-tracer.test.ts +6 -4
  330. package/src/observability/index.ts +2 -0
  331. package/src/observability/noop-provider.ts +0 -9
  332. package/src/observability/recording-tracer.ts +0 -12
  333. package/src/observability/standard-metrics.ts +64 -2
  334. package/src/observability/types/index.ts +1 -29
  335. package/src/observability/types/metric.ts +1 -56
  336. package/src/observability/types/provider.ts +1 -32
  337. package/src/observability/types/span.ts +1 -64
  338. package/src/pipeline/__tests__/ctx-bridge.integration.test.ts +2 -2
  339. package/src/pipeline/__tests__/dispatcher.test.ts +366 -1
  340. package/src/pipeline/__tests__/event-consumer-state.integration.test.ts +31 -0
  341. package/src/pipeline/__tests__/event-dispatcher-delivery-max-attempts.test.ts +126 -0
  342. package/src/pipeline/__tests__/event-dispatcher-rearm.integration.test.ts +263 -0
  343. package/src/pipeline/__tests__/event-dispatcher.integration.test.ts +10 -0
  344. package/src/pipeline/__tests__/job-trigger-consumer.integration.test.ts +106 -0
  345. package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +308 -77
  346. package/src/pipeline/__tests__/load-aggregate-query.integration.test.ts +16 -8
  347. package/src/pipeline/__tests__/post-query-hook.integration.test.ts +3 -3
  348. package/src/pipeline/__tests__/projection-rebuild.integration.test.ts +80 -2
  349. package/src/pipeline/__tests__/rebuild-poison-quarantine.integration.test.ts +274 -0
  350. package/src/pipeline/__tests__/try-append-event.integration.test.ts +166 -0
  351. package/src/pipeline/dispatch-batch.ts +187 -0
  352. package/src/pipeline/dispatch-query.ts +165 -0
  353. package/src/pipeline/dispatch-shared.ts +826 -0
  354. package/src/pipeline/dispatch-stream.ts +90 -0
  355. package/src/pipeline/dispatch-write.ts +451 -0
  356. package/src/pipeline/dispatcher-utils.ts +1 -1
  357. package/src/pipeline/dispatcher.ts +44 -1372
  358. package/src/pipeline/entity-cache.ts +2 -33
  359. package/src/pipeline/event-consumer-state.ts +30 -2
  360. package/src/pipeline/event-dispatcher-admin.ts +293 -0
  361. package/src/pipeline/event-dispatcher-delivery.ts +305 -0
  362. package/src/pipeline/event-dispatcher.ts +85 -542
  363. package/src/pipeline/index.ts +2 -0
  364. package/src/pipeline/msp-rebuild.ts +42 -3
  365. package/src/pipeline/multi-stream-apply-context.ts +4 -42
  366. package/src/pipeline/projection-rebuild.ts +105 -3
  367. package/src/pipeline/system-hooks.ts +144 -1
  368. package/src/rate-limit/__tests__/resolver.integration.test.ts +18 -0
  369. package/src/rate-limit/resolver.ts +16 -32
  370. package/src/schema-cli.ts +76 -16
  371. package/src/search/__tests__/meilisearch-adapter.integration.test.ts +207 -185
  372. package/src/search/__tests__/meilisearch-ids.test.ts +30 -0
  373. package/src/search/__tests__/reindex-entity.integration.test.ts +144 -0
  374. package/src/search/index.ts +6 -0
  375. package/src/search/meilisearch-adapter.ts +13 -12
  376. package/src/search/reindex-entity.ts +177 -0
  377. package/src/search/types.ts +1 -39
  378. package/src/secrets/__tests__/contains-secret.test.ts +34 -0
  379. package/src/secrets/__tests__/envelope-cipher.test.ts +59 -0
  380. package/src/secrets/__tests__/envelope.test.ts +1 -1
  381. package/src/secrets/dek-cache.ts +26 -5
  382. package/src/secrets/envelope-cipher.ts +53 -0
  383. package/src/secrets/envelope.ts +5 -3
  384. package/src/secrets/index.ts +13 -1
  385. package/src/secrets/stored-envelope.ts +46 -0
  386. package/src/secrets/types.ts +2 -162
  387. package/src/stack/__tests__/event-collector.test.ts +42 -0
  388. package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +125 -0
  389. package/src/stack/db.ts +2 -1
  390. package/src/stack/push-entity-projection-tables.ts +4 -3
  391. package/src/stack/redis.ts +8 -0
  392. package/src/stack/request-helper.ts +38 -2
  393. package/src/stack/table-helpers.ts +6 -4
  394. package/src/stack/test-stack.ts +249 -131
  395. package/src/testing/__tests__/late-bound.test.ts +32 -0
  396. package/src/testing/__tests__/wait-for.test.ts +59 -0
  397. package/src/testing/boot-validator-fixture.ts +121 -0
  398. package/src/testing/e2e-generator.ts +8 -0
  399. package/src/testing/file-provider-contract.ts +104 -0
  400. package/src/testing/handler-context.ts +3 -1
  401. package/src/testing/index.ts +5 -0
  402. package/src/testing/late-bound.ts +5 -3
  403. package/src/testing/mutable-master-key-provider.ts +29 -3
  404. package/src/testing/wait-for.ts +3 -0
  405. package/src/testing/without-ambient-temporal.ts +14 -0
  406. package/src/time/__tests__/tz-dateline.test.ts +8 -10
  407. package/src/time/geo-tz.ts +1 -32
  408. package/src/time/index.ts +1 -0
  409. package/src/time/legacy-date.ts +18 -0
  410. package/src/time/polyfill.ts +21 -38
  411. package/src/time/tz-context.ts +44 -85
  412. package/src/ui-types/app-schema.ts +12 -0
  413. package/src/ui-types/index.ts +20 -7
  414. package/src/utils/__tests__/safe-json-temporal.test.ts +18 -0
  415. package/src/utils/safe-json.ts +13 -1
  416. package/src/__tests__/raw-table.integration.test.ts +0 -116
  417. package/src/bun-db/__tests__/bun-test-stack.ts +0 -6
  418. package/src/db/__tests__/encryption.test.ts +0 -39
  419. package/src/db/encryption.ts +0 -39
  420. package/src/db/row-helpers.ts +0 -4
  421. package/src/engine/__tests__/raw-table.test.ts +0 -150
  422. package/src/engine/__tests__/unmanaged-table.test.ts +0 -127
  423. package/src/engine/feature-ast/__tests__/visual-tree-parse.test.ts +0 -184
  424. package/src/engine/feature-ast/extractors/round4.ts +0 -1366
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { checkWriteFieldRoles, filterReadFields } from "../field-access";
2
+ import { checkWriteFieldRoles, filterReadFields, PII_MASKED_VALUE } from "../field-access";
3
3
  import type { EntityDefinition } from "../types";
4
4
 
5
5
  const entity: EntityDefinition = {
@@ -25,6 +25,28 @@ describe("filterReadFields", () => {
25
25
  const filtered = filterReadFields(entity, row, admin);
26
26
  expect(filtered["secret"]).toBe("visible");
27
27
  });
28
+
29
+ test("masks piiEncrypted fields instead of stripping them (kumiko-platform#463)", () => {
30
+ const entityWithPii: EntityDefinition = {
31
+ fields: {
32
+ ...entity.fields,
33
+ iban: {
34
+ type: "text",
35
+ piiEncrypted: true,
36
+ tenantOwned: true,
37
+ access: { read: { admin: "all" } },
38
+ },
39
+ },
40
+ };
41
+ const row = { id: 1, title: "Hello", secret: "hidden", iban: "DE89370400440532013000" };
42
+
43
+ const filteredForEditor = filterReadFields(entityWithPii, row, editor);
44
+ expect(filteredForEditor["secret"]).toBeUndefined();
45
+ expect(filteredForEditor["iban"]).toBe(PII_MASKED_VALUE);
46
+
47
+ const filteredForAdmin = filterReadFields(entityWithPii, row, admin);
48
+ expect(filteredForAdmin["iban"]).toBe("DE89370400440532013000");
49
+ });
28
50
  });
29
51
 
30
52
  describe("checkWriteFieldRoles", () => {
@@ -59,11 +59,11 @@ describe("HookPhases defaults", () => {
59
59
  expect(entry?.[0]?.phase).toBe(HookPhases.inTransaction);
60
60
  });
61
61
 
62
- test("entityHook postSave defaults to afterCommit, preDelete is forced inTransaction", () => {
62
+ test("hook({allOf}) postSave defaults to afterCommit, preDelete is forced inTransaction", () => {
63
63
  const feature = defineFeature("test", (r) => {
64
64
  const thing = r.entity("thing", createEntity({ table: "things", fields: {} }));
65
- r.entityHook("postSave", thing, noopSave);
66
- r.entityHook("preDelete", thing, async () => undefined);
65
+ r.hook("postSave", { allOf: thing }, noopSave);
66
+ r.hook("preDelete", { allOf: thing }, async () => undefined);
67
67
  });
68
68
 
69
69
  expect(feature.entityHooks?.postSave?.["thing"]?.[0]?.phase).toBe(HookPhases.afterCommit);
@@ -120,8 +120,8 @@ describe("Registry phase filtering", () => {
120
120
 
121
121
  const feature = defineFeature("test", (r) => {
122
122
  const thing = r.entity("thing", createEntity({ table: "things", fields: {} }));
123
- r.entityHook("postSave", thing, inTxFn, { phase: HookPhases.inTransaction });
124
- r.entityHook("postSave", thing, afterFn);
123
+ r.hook("postSave", { allOf: thing }, inTxFn, { phase: HookPhases.inTransaction });
124
+ r.hook("postSave", { allOf: thing }, afterFn);
125
125
  });
126
126
 
127
127
  const registry = createRegistry([feature]);
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
  import {
3
+ buildSessionRoles,
3
4
  FORBIDDEN_MEMBERSHIP_ROLES,
4
5
  findForbiddenMembershipRole,
5
6
  isForbiddenMembershipRole,
@@ -41,23 +42,16 @@ describe("forbidden membership roles", () => {
41
42
  // ONLY the membership portion, never the merged result — so a legitimate
42
43
  // SystemAdmin in globalRoles survives, a resurrected one in membership does not.
43
44
  describe("merge semantics (globalRoles never filtered)", () => {
44
- function merge(
45
- globalRoles: readonly string[],
46
- membershipRoles: readonly string[],
47
- ): readonly string[] {
48
- return Array.from(new Set([...globalRoles, ...stripForbiddenMembershipRoles(membershipRoles)]));
49
- }
50
-
51
45
  test("global SystemAdmin survives (no regression for real admins)", () => {
52
- expect(merge(["SystemAdmin"], [])).toContain("SystemAdmin");
46
+ expect(buildSessionRoles(["SystemAdmin"], [])).toContain("SystemAdmin");
53
47
  });
54
48
 
55
49
  test("membership SystemAdmin is stripped (resurrected role neutralised)", () => {
56
- expect(merge([], ["SystemAdmin"])).not.toContain("SystemAdmin");
50
+ expect(buildSessionRoles([], ["SystemAdmin"])).not.toContain("SystemAdmin");
57
51
  });
58
52
 
59
53
  test("global admin + tenant membership keeps both, deduped", () => {
60
- expect([...merge(["SystemAdmin"], ["Admin", "SystemAdmin"])].sort()).toEqual(
54
+ expect([...buildSessionRoles(["SystemAdmin"], ["Admin", "SystemAdmin"])].sort()).toEqual(
61
55
  ["Admin", "SystemAdmin"].sort(),
62
56
  );
63
57
  });
@@ -1,9 +1,14 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { validateBoot } from "../boot-validator";
2
+ import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
3
+ import { validateBoot as validateBootRaw } from "../boot-validator";
3
4
  import { defineFeature } from "../define-feature";
4
5
  import { createEntity, createTextField } from "../factories";
5
6
  import { createRegistry } from "../registry";
6
7
 
8
+ function validateBoot(features: Parameters<typeof validateBootRaw>[0]): void {
9
+ validateBootRaw(withBootValidatorFixture(features));
10
+ }
11
+
7
12
  function productEntity() {
8
13
  return createEntity({
9
14
  table: "products",
@@ -75,6 +80,86 @@ describe("r.nav() — registration", () => {
75
80
  });
76
81
  });
77
82
 
83
+ describe("r.screen({ nav }) — inline nav sugar", () => {
84
+ test("synthesizes a nav entry from the screen's id", () => {
85
+ const feature = defineFeature("shop", (r) => {
86
+ r.entity("product", productEntity());
87
+ r.screen({
88
+ id: "products",
89
+ type: "entityList",
90
+ entity: "product",
91
+ columns: ["name"],
92
+ nav: { label: "shop:nav.products", icon: "box", order: 5 },
93
+ });
94
+ });
95
+ expect(feature.navs["products"]).toMatchObject({
96
+ id: "products",
97
+ label: "shop:nav.products",
98
+ icon: "box",
99
+ order: 5,
100
+ screen: "shop:screen:products",
101
+ });
102
+ });
103
+
104
+ test("supports parent like a standalone r.nav()", () => {
105
+ const feature = defineFeature("shop", (r) => {
106
+ r.nav({ id: "catalog", label: "x" });
107
+ r.entity("product", productEntity());
108
+ r.screen({
109
+ id: "products",
110
+ type: "entityList",
111
+ entity: "product",
112
+ columns: ["name"],
113
+ nav: { label: "y", parent: "shop:nav:catalog" },
114
+ });
115
+ });
116
+ expect(feature.navs["products"]?.parent).toBe("shop:nav:catalog");
117
+ });
118
+
119
+ test("passes the same boot-validation as a standalone r.nav()", () => {
120
+ const feature = defineFeature("shop", (r) => {
121
+ r.entity("product", productEntity());
122
+ r.screen({
123
+ id: "products",
124
+ type: "entityList",
125
+ entity: "product",
126
+ columns: ["name"],
127
+ nav: { label: "y" },
128
+ });
129
+ });
130
+ expect(() => validateBoot([feature])).not.toThrow();
131
+ });
132
+
133
+ test("screen without nav registers no nav entry", () => {
134
+ const feature = defineFeature("shop", (r) => {
135
+ r.entity("product", productEntity());
136
+ r.screen({
137
+ id: "products",
138
+ type: "entityList",
139
+ entity: "product",
140
+ columns: ["name"],
141
+ });
142
+ });
143
+ expect(feature.navs["products"]).toBeUndefined();
144
+ });
145
+
146
+ test("rejects when a standalone r.nav() already used the screen's id", () => {
147
+ expect(() =>
148
+ defineFeature("shop", (r) => {
149
+ r.nav({ id: "products", label: "standalone" });
150
+ r.entity("product", productEntity());
151
+ r.screen({
152
+ id: "products",
153
+ type: "entityList",
154
+ entity: "product",
155
+ columns: ["name"],
156
+ nav: { label: "inline" },
157
+ });
158
+ }),
159
+ ).toThrow(/already registered/);
160
+ });
161
+ });
162
+
78
163
  describe("createRegistry — nav indexing", () => {
79
164
  test("indexes nav entries by qualified name", () => {
80
165
  const feature = defineFeature("shop", (r) => {
@@ -1,4 +1,4 @@
1
- // Pipeline-engine unit tests — defineWriteHandler({ perform: pipeline(...) })
1
+ // Pipeline-engine unit tests — defineWriteHandler({ perform: stepsPipeline(...) })
2
2
  // boundary, run-pipeline runner contract, defineStep registry guards.
3
3
  // Sub-step-builders (branch, forEach) live in pipeline-sub-pipelines.test.ts;
4
4
  // boot-validator in validate-projection-allowlist.test.ts.
@@ -9,7 +9,7 @@ import { z } from "zod";
9
9
  import { TestUsers } from "../../stack";
10
10
  import { defineWriteHandler } from "../define-handler";
11
11
  import { defineStep } from "../define-step";
12
- import { pipeline } from "../pipeline";
12
+ import { stepsPipeline } from "../pipeline";
13
13
  import type { WriteEvent } from "../types/handlers";
14
14
  import { buildMinimalCtx } from "./_pipeline-test-utils";
15
15
 
@@ -19,7 +19,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
19
19
  name: "demo:noop",
20
20
  schema: z.object({ greeting: z.string() }),
21
21
  access: { roles: ["User"] },
22
- perform: pipeline<{ greeting: string }, { echoed: string }>(({ event, r }) => [
22
+ perform: stepsPipeline<{ greeting: string }, { echoed: string }>(({ event, r }) => [
23
23
  r.step.return(() => ({
24
24
  isSuccess: true as const,
25
25
  data: { echoed: event.payload.greeting },
@@ -49,7 +49,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
49
49
  name: "demo:static",
50
50
  schema: z.object({}),
51
51
  access: { roles: ["User"] },
52
- perform: pipeline<Record<string, never>, { ok: boolean }>(({ r }) => [
52
+ perform: stepsPipeline<Record<string, never>, { ok: boolean }>(({ r }) => [
53
53
  r.step.return({ isSuccess: true as const, data: { ok: true } }),
54
54
  ]),
55
55
  });
@@ -89,7 +89,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
89
89
  name: "demo:no-return",
90
90
  schema: z.object({}),
91
91
  access: { roles: ["User"] },
92
- perform: pipeline<Record<string, never>, never>(() => []),
92
+ perform: stepsPipeline<Record<string, never>, never>(() => []),
93
93
  });
94
94
 
95
95
  await expect(
@@ -105,7 +105,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
105
105
  name: "demo:thread",
106
106
  schema: z.object({ base: z.number() }),
107
107
  access: { roles: ["User"] },
108
- perform: pipeline<{ base: number }, { sum: number }>(({ event, r }) => [
108
+ perform: stepsPipeline<{ base: number }, { sum: number }>(({ event, r }) => [
109
109
  r.step.compute("offset", () => 10),
110
110
  r.step.compute("doubledBase", () => event.payload.base * 2),
111
111
  r.step.return(({ steps }) => ({
@@ -133,7 +133,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
133
133
  name: "demo:fresh",
134
134
  schema: z.object({}),
135
135
  access: { roles: ["User"] },
136
- perform: pipeline<Record<string, never>, { tick: number }>(({ r }) => [
136
+ perform: stepsPipeline<Record<string, never>, { tick: number }>(({ r }) => [
137
137
  r.step.compute("tick", () => ++counter),
138
138
  r.step.return(({ steps }) => ({
139
139
  isSuccess: true as const,
@@ -161,7 +161,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
161
161
  name: "demo:unknown-kind",
162
162
  schema: z.object({}),
163
163
  access: { roles: ["User"] },
164
- perform: pipeline<Record<string, never>, never>(() => [
164
+ perform: stepsPipeline<Record<string, never>, never>(() => [
165
165
  // Hand-crafted instance with a kind that's never been registered —
166
166
  // simulates a typo in a future step-builder factory.
167
167
  { kind: "this-step-does-not-exist", args: {} },
@@ -190,7 +190,7 @@ describe("pipeline engine (return / compute / registry guards)", () => {
190
190
  schema: z.object({}),
191
191
  access: { roles: ["User"] },
192
192
  handler: async () => ({ isSuccess: true as const, data: {} }),
193
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
193
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
194
194
  r.step.return({ isSuccess: true as const, data: { ok: true } }),
195
195
  ]),
196
196
  } as unknown as Parameters<typeof defineWriteHandler>[0];
@@ -60,7 +60,7 @@ import {
60
60
  import { defineFeature } from "../define-feature";
61
61
  import { defineWriteHandler } from "../define-handler";
62
62
  import { createEntity, createTextField } from "../factories";
63
- import { pipeline } from "../pipeline";
63
+ import { stepsPipeline } from "../pipeline";
64
64
 
65
65
  const echoSchema = z.object({ greeting: z.string() });
66
66
 
@@ -70,7 +70,7 @@ const echoHandler = defineWriteHandler({
70
70
  name: "echo",
71
71
  schema: echoSchema,
72
72
  access: { roles: ["Admin"] },
73
- perform: pipeline<z.infer<typeof echoSchema>, { echoed: string; from: string }>(
73
+ perform: stepsPipeline<z.infer<typeof echoSchema>, { echoed: string; from: string }>(
74
74
  ({ event, r }) => [
75
75
  r.step.return(() => ({
76
76
  isSuccess: true as const,
@@ -90,7 +90,7 @@ const explodeHandler = defineWriteHandler({
90
90
  name: "explode",
91
91
  schema: explodeSchema,
92
92
  access: { roles: ["Admin"] },
93
- perform: pipeline<z.infer<typeof explodeSchema>, never>(({ r }) => [
93
+ perform: stepsPipeline<z.infer<typeof explodeSchema>, never>(({ r }) => [
94
94
  r.step.return(() => {
95
95
  throw new Error("boom");
96
96
  }),
@@ -106,7 +106,7 @@ const compoundHandler = defineWriteHandler({
106
106
  name: "compound",
107
107
  schema: compoundSchema,
108
108
  access: { roles: ["Admin"] },
109
- perform: pipeline<z.infer<typeof compoundSchema>, { sum: number; userId: string }>(
109
+ perform: stepsPipeline<z.infer<typeof compoundSchema>, { sum: number; userId: string }>(
110
110
  ({ event, r }) => [
111
111
  r.step.compute("offset", () => 100),
112
112
  r.step.compute("doubledBase", () => event.payload.base * 2),
@@ -147,7 +147,7 @@ const logHandler = defineWriteHandler({
147
147
  schema: logSchema,
148
148
  access: { roles: ["Admin"] },
149
149
  // Outer-`event`-capture pattern — see compoundHandler above for the why.
150
- perform: pipeline<z.infer<typeof logSchema>, { correlationId: string }>(({ event, r }) => [
150
+ perform: stepsPipeline<z.infer<typeof logSchema>, { correlationId: string }>(({ event, r }) => [
151
151
  r.step.unsafeProjectionUpsert({
152
152
  table: pipelineDemoLogTable,
153
153
  on: ["correlationId"],
@@ -182,7 +182,7 @@ const widgetCreateHandler = defineWriteHandler({
182
182
  name: "widget:create",
183
183
  schema: widgetSchema,
184
184
  access: { roles: ["Admin"] },
185
- perform: pipeline<z.infer<typeof widgetSchema>, { id: string }>(({ event, r }) => [
185
+ perform: stepsPipeline<z.infer<typeof widgetSchema>, { id: string }>(({ event, r }) => [
186
186
  r.step.aggregate.create("widget", {
187
187
  executor: widgetExecutor,
188
188
  data: () => ({ label: event.payload.label }),
@@ -202,7 +202,7 @@ const annotateHandler = defineWriteHandler({
202
202
  name: "widget:annotate",
203
203
  schema: annotateSchema,
204
204
  access: { roles: ["Admin"] },
205
- perform: pipeline<z.infer<typeof annotateSchema>, { id: string }>(({ event, r }) => [
205
+ perform: stepsPipeline<z.infer<typeof annotateSchema>, { id: string }>(({ event, r }) => [
206
206
  r.step.aggregate.create("widget", {
207
207
  executor: widgetExecutor,
208
208
  data: () => ({ label: event.payload.label }),
@@ -230,7 +230,7 @@ const widgetUpdateHandler = defineWriteHandler({
230
230
  name: "widget:update",
231
231
  schema: widgetUpdateSchema,
232
232
  access: { roles: ["Admin"] },
233
- perform: pipeline<z.infer<typeof widgetUpdateSchema>, { id: string }>(({ event, r }) => [
233
+ perform: stepsPipeline<z.infer<typeof widgetUpdateSchema>, { id: string }>(({ event, r }) => [
234
234
  r.step.aggregate.update("widget", {
235
235
  executor: widgetExecutor,
236
236
  id: () => event.payload.id,
@@ -254,7 +254,7 @@ const widgetBrokenHandler = defineWriteHandler({
254
254
  name: "widget:create-broken",
255
255
  schema: z.object({}),
256
256
  access: { roles: ["Admin"] },
257
- perform: pipeline<Record<string, never>, { id: string }>(({ r }) => [
257
+ perform: stepsPipeline<Record<string, never>, { id: string }>(({ r }) => [
258
258
  r.step.aggregate.create("widget", {
259
259
  executor: widgetExecutor,
260
260
  // Intentionally omits the `label` field that the entity declares
@@ -278,7 +278,7 @@ const widgetCreateThenThrowHandler = defineWriteHandler({
278
278
  name: "widget:create-then-throw",
279
279
  schema: z.object({ label: z.string() }),
280
280
  access: { roles: ["Admin"] },
281
- perform: pipeline<{ label: string }, never>(({ event, r }) => [
281
+ perform: stepsPipeline<{ label: string }, never>(({ event, r }) => [
282
282
  r.step.aggregate.create("widget", {
283
283
  executor: widgetExecutor,
284
284
  data: () => ({ label: event.payload.label }),
@@ -303,7 +303,7 @@ const forEachThenThrowHandler = defineWriteHandler({
303
303
  name: "widget:foreach-then-throw",
304
304
  schema: forEachThenThrowSchema,
305
305
  access: { roles: ["Admin"] },
306
- perform: pipeline<{ labels: string[] }, never>(({ event, r }) => [
306
+ perform: stepsPipeline<{ labels: string[] }, never>(({ event, r }) => [
307
307
  r.step.forEach({
308
308
  over: () => event.payload.labels,
309
309
  as: "label",
@@ -337,7 +337,7 @@ const lookupHandler = defineWriteHandler({
337
337
  name: "widget:lookup",
338
338
  schema: lookupSchema,
339
339
  access: { roles: ["Admin"] },
340
- perform: pipeline<z.infer<typeof lookupSchema>, { found: boolean; label: string | null }>(
340
+ perform: stepsPipeline<z.infer<typeof lookupSchema>, { found: boolean; label: string | null }>(
341
341
  ({ event, r }) => [
342
342
  r.step.read.findOne("widget", {
343
343
  table: widgetTable,
@@ -358,7 +358,7 @@ const listAllHandler = defineWriteHandler({
358
358
  name: "widget:list",
359
359
  schema: z.object({}),
360
360
  access: { roles: ["Admin"] },
361
- perform: pipeline<Record<string, never>, { count: number }>(({ r }) => [
361
+ perform: stepsPipeline<Record<string, never>, { count: number }>(({ r }) => [
362
362
  r.step.read.findMany("widgets", { table: widgetTable }),
363
363
  r.step.return(({ steps }) => ({
364
364
  isSuccess: true as const,
@@ -374,7 +374,7 @@ const listLimitedHandler = defineWriteHandler({
374
374
  name: "widget:list-one",
375
375
  schema: z.object({}),
376
376
  access: { roles: ["Admin"] },
377
- perform: pipeline<Record<string, never>, { count: number }>(({ r }) => [
377
+ perform: stepsPipeline<Record<string, never>, { count: number }>(({ r }) => [
378
378
  r.step.read.findMany("widgets", { table: widgetTable, limit: 1 }),
379
379
  r.step.return(({ steps }) => ({
380
380
  isSuccess: true as const,
@@ -388,7 +388,7 @@ const purgeLogHandler = defineWriteHandler({
388
388
  name: "log:purge",
389
389
  schema: purgeLogSchema,
390
390
  access: { roles: ["Admin"] },
391
- perform: pipeline<z.infer<typeof purgeLogSchema>, { ok: true }>(({ event, r }) => [
391
+ perform: stepsPipeline<z.infer<typeof purgeLogSchema>, { ok: true }>(({ event, r }) => [
392
392
  r.step.unsafeProjectionDelete({
393
393
  table: pipelineDemoLogTable,
394
394
  where: () => ({ correlationId: event.payload.correlationId }),
@@ -403,7 +403,7 @@ const conditionalLogHandler = defineWriteHandler({
403
403
  name: "log:conditional",
404
404
  schema: conditionalLogSchema,
405
405
  access: { roles: ["Admin"] },
406
- perform: pipeline<z.infer<typeof conditionalLogSchema>, { ok: true }>(({ event, r }) => [
406
+ perform: stepsPipeline<z.infer<typeof conditionalLogSchema>, { ok: true }>(({ event, r }) => [
407
407
  r.step.branch({
408
408
  if: () => event.payload.message.length > 0,
409
409
  onTrue: [
@@ -428,7 +428,7 @@ const bulkLogHandler = defineWriteHandler({
428
428
  name: "log:bulk",
429
429
  schema: bulkLogSchema,
430
430
  access: { roles: ["Admin"] },
431
- perform: pipeline<z.infer<typeof bulkLogSchema>, { count: number }>(({ event, r }) => [
431
+ perform: stepsPipeline<z.infer<typeof bulkLogSchema>, { count: number }>(({ event, r }) => [
432
432
  r.step.forEach({
433
433
  over: () => event.payload.correlationIds,
434
434
  as: "correlationId",
@@ -476,7 +476,7 @@ const demoPipelineFeature = defineFeature("demoPipeline", (r) => {
476
476
  let stack: TestStack;
477
477
  const admin = TestUsers.admin;
478
478
 
479
- describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher path", () => {
479
+ describe("defineWriteHandler({ perform: stepsPipeline(...) }) — real dispatcher path", () => {
480
480
  beforeAll(async () => {
481
481
  stack = await setupTestStack({ features: [demoPipelineFeature] });
482
482
  // Push the read-side-projection table — not registered as an entity,
@@ -20,7 +20,7 @@ import { createRecordingProvider, type RecordingProvider } from "../../testing";
20
20
  import { defineFeature } from "../define-feature";
21
21
  import { defineWriteHandler } from "../define-handler";
22
22
  import { createEntity, createTextField } from "../factories";
23
- import { pipeline } from "../pipeline";
23
+ import { stepsPipeline } from "../pipeline";
24
24
 
25
25
  // Handler whose step bodies emit a span + a metric. The recording
26
26
  // provider afterwards lets us assert both landed.
@@ -30,7 +30,7 @@ const observedHandler = defineWriteHandler({
30
30
  name: "observed",
31
31
  schema: observedSchema,
32
32
  access: { roles: ["Admin"] },
33
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
33
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
34
34
  r.step.compute("traced", (ctx) => {
35
35
  // Span emitted from inside a step. End it explicitly because
36
36
  // step.run is sync-or-async-but-not-otel-instrumented; the
@@ -1,6 +1,6 @@
1
1
  // Pipeline-engine performance smoke-test.
2
2
  //
3
- // Compares the M.1 pipeline-form handler ({ perform: pipeline(...) })
3
+ // Compares the M.1 pipeline-form handler ({ perform: stepsPipeline(...) })
4
4
  // against the equivalent free-form handler ({ handler: async (...) })
5
5
  // over N identical writes against the real Postgres stack. The point
6
6
  // is NOT to optimise — the pipeline-form is a thin wrapper, not a
@@ -28,7 +28,7 @@ import { setupTestStack, type TestStack, TestUsers, unsafeCreateEntityTable } fr
28
28
  import { defineFeature } from "../define-feature";
29
29
  import { defineWriteHandler } from "../define-handler";
30
30
  import { createEntity, createNumberField, createTextField } from "../factories";
31
- import { pipeline } from "../pipeline";
31
+ import { stepsPipeline } from "../pipeline";
32
32
 
33
33
  // Same logical operation in both handler-forms: read input, return a
34
34
  // trivial transformed payload. No DB-write — the goal is to compare
@@ -41,7 +41,7 @@ const trivialPipeline = defineWriteHandler({
41
41
  name: "trivial:pipeline",
42
42
  schema: trivialSchema,
43
43
  access: { roles: ["Admin"] },
44
- perform: pipeline<{ n: number }, { doubled: number }>(({ event, r }) => [
44
+ perform: stepsPipeline<{ n: number }, { doubled: number }>(({ event, r }) => [
45
45
  r.step.return(() => ({
46
46
  isSuccess: true as const,
47
47
  data: { doubled: event.payload.n * 2 },
@@ -9,7 +9,7 @@ import { describe, expect, it } from "bun:test";
9
9
  import { z } from "zod";
10
10
  import { TestUsers } from "../../stack";
11
11
  import { defineWriteHandler } from "../define-handler";
12
- import { pipeline } from "../pipeline";
12
+ import { stepsPipeline } from "../pipeline";
13
13
  import { buildBranchStep } from "../steps/branch";
14
14
  import { buildForEachStep } from "../steps/for-each";
15
15
  import { buildReturnStep } from "../steps/return";
@@ -21,7 +21,7 @@ describe("r.step.branch", () => {
21
21
  name: "demo:branch-then",
22
22
  schema: z.object({}),
23
23
  access: { roles: ["User"] },
24
- perform: pipeline<Record<string, never>, { value: number }>(({ r }) => [
24
+ perform: stepsPipeline<Record<string, never>, { value: number }>(({ r }) => [
25
25
  r.step.branch({
26
26
  if: () => true,
27
27
  onTrue: [r.step.compute("inThen", () => 42)],
@@ -47,7 +47,7 @@ describe("r.step.branch", () => {
47
47
  name: "demo:branch-else",
48
48
  schema: z.object({}),
49
49
  access: { roles: ["User"] },
50
- perform: pipeline<Record<string, never>, { value: number }>(({ r }) => [
50
+ perform: stepsPipeline<Record<string, never>, { value: number }>(({ r }) => [
51
51
  r.step.branch({
52
52
  if: () => false,
53
53
  onTrue: [r.step.compute("inThen", () => 42)],
@@ -72,7 +72,7 @@ describe("r.step.branch", () => {
72
72
  name: "demo:branch-noop",
73
73
  schema: z.object({}),
74
74
  access: { roles: ["User"] },
75
- perform: pipeline<Record<string, never>, { ran: boolean }>(({ r }) => [
75
+ perform: stepsPipeline<Record<string, never>, { ran: boolean }>(({ r }) => [
76
76
  r.step.branch({
77
77
  if: () => false,
78
78
  onTrue: [r.step.compute("ran", () => true)],
@@ -122,7 +122,7 @@ describe("r.step.forEach", () => {
122
122
  name: "demo:foreach",
123
123
  schema: z.object({}),
124
124
  access: { roles: ["User"] },
125
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
125
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
126
126
  r.step.forEach({
127
127
  over: () => [10, 20, 30],
128
128
  as: "n",
@@ -153,7 +153,7 @@ describe("r.step.forEach", () => {
153
153
  name: "demo:foreach-cleanup",
154
154
  schema: z.object({}),
155
155
  access: { roles: ["User"] },
156
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
156
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
157
157
  r.step.forEach({
158
158
  over: () => [1],
159
159
  as: "tmp",
@@ -180,7 +180,7 @@ describe("r.step.forEach", () => {
180
180
  name: "demo:foreach-empty",
181
181
  schema: z.object({}),
182
182
  access: { roles: ["User"] },
183
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
183
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
184
184
  r.step.forEach({
185
185
  over: () => [],
186
186
  as: "n",
@@ -233,7 +233,7 @@ describe("r.step.forEach", () => {
233
233
  name: "demo:foreach-throws",
234
234
  schema: z.object({}),
235
235
  access: { roles: ["User"] },
236
- perform: pipeline<Record<string, never>, never>(({ r }) => [
236
+ perform: stepsPipeline<Record<string, never>, never>(({ r }) => [
237
237
  r.step.forEach({
238
238
  over: () => [1, 2, 3],
239
239
  as: "item",
@@ -266,7 +266,7 @@ describe("r.step.forEach", () => {
266
266
  name: "demo:foreach-bad-over",
267
267
  schema: z.object({}),
268
268
  access: { roles: ["User"] },
269
- perform: pipeline<Record<string, never>, { ok: true }>(({ r }) => [
269
+ perform: stepsPipeline<Record<string, never>, { ok: true }>(({ r }) => [
270
270
  r.step.forEach({
271
271
  // Cast: TypeScript would normally catch this; the test exercises
272
272
  // the runtime guard for hand-crafted / dynamically built cases.
@@ -10,7 +10,7 @@ const stubContext = {} as unknown as AppContext;
10
10
  // postQuery-Hook (F1) — feuert nach Query-Handler-Execute, vor Field-Access-
11
11
  // Read-Filter. Zwei Registrierungs-Pfade:
12
12
  // - r.hook("postQuery", "ns:query:list", fn) — handler-keyed
13
- // - r.entityHook("postQuery", "thing", fn) — entity-keyed (alle Queries)
13
+ // - r.hook("postQuery", { allOf: "thing" }, fn) — entity-keyed (alle Queries)
14
14
  //
15
15
  // Diese Tests pinnen die Invarianten:
16
16
  // 1. Beide Registrierungs-Pfade landen in unabhängigen Maps
@@ -35,10 +35,10 @@ describe("postQuery hook registration", () => {
35
35
  expect(entry?.[0]?.featureName).toBe("test");
36
36
  });
37
37
 
38
- test("r.entityHook('postQuery', entity, fn) lands in entity-keyed entityHooks map", () => {
38
+ test("r.hook('postQuery', { allOf: entity }, fn) lands in entity-keyed entityHooks map", () => {
39
39
  const feature = defineFeature("test", (r) => {
40
40
  const thing = r.entity("thing", createEntity({ table: "things", fields: {} }));
41
- r.entityHook("postQuery", thing, noop);
41
+ r.hook("postQuery", { allOf: thing }, noop);
42
42
  });
43
43
 
44
44
  const entry = feature.entityHooks?.postQuery?.["thing"];
@@ -52,8 +52,8 @@ describe("postQuery hook registration", () => {
52
52
 
53
53
  const feature = defineFeature("test", (r) => {
54
54
  const thing = r.entity("thing", createEntity({ table: "things", fields: {} }));
55
- r.entityHook("postQuery", thing, hookA);
56
- r.entityHook("postQuery", thing, hookB);
55
+ r.hook("postQuery", { allOf: thing }, hookA);
56
+ r.hook("postQuery", { allOf: thing }, hookB);
57
57
  });
58
58
 
59
59
  expect(feature.entityHooks?.postQuery?.["thing"]).toHaveLength(2);
@@ -81,7 +81,7 @@ describe("Registry getters", () => {
81
81
 
82
82
  const feature = defineFeature("test", (r) => {
83
83
  const thing = r.entity("thing", createEntity({ table: "things", fields: {} }));
84
- r.entityHook("postQuery", thing, fn);
84
+ r.hook("postQuery", { allOf: thing }, fn);
85
85
  });
86
86
 
87
87
  const registry = createRegistry([feature]);
@@ -109,7 +109,7 @@ describe("Hook function semantics", () => {
109
109
 
110
110
  const feature = defineFeature("test", (r) => {
111
111
  const thing = r.entity("thing", createEntity({ table: "things", fields: {} }));
112
- r.entityHook("postQuery", thing, enrich);
112
+ r.hook("postQuery", { allOf: thing }, enrich);
113
113
  });
114
114
 
115
115
  const registry = createRegistry([feature]);