@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
@@ -21,7 +21,13 @@
21
21
  // Per-pattern extractors fill in iteratively (C1.5) — each round adds
22
22
  // one extractor + a focused test.
23
23
 
24
- import type { ArrowFunction, CallExpression, ParameterDeclaration, SourceFile } from "ts-morph";
24
+ import type {
25
+ ArrowFunction,
26
+ CallExpression,
27
+ Node,
28
+ ParameterDeclaration,
29
+ SourceFile,
30
+ } from "ts-morph";
25
31
  import { Project, SyntaxKind } from "ts-morph";
26
32
 
27
33
  import {
@@ -32,9 +38,7 @@ import {
32
38
  extractDefineEvent,
33
39
  extractDescribe,
34
40
  extractEntity,
35
- extractEntityHook,
36
41
  extractEnvSchema,
37
- extractEventMigration,
38
42
  extractExposesApi,
39
43
  extractExtendsRegistrar,
40
44
  extractHook,
@@ -53,17 +57,18 @@ import {
53
57
  extractRequires,
54
58
  extractScreen,
55
59
  extractSecret,
60
+ extractStoreTable,
61
+ extractStreamHandler,
56
62
  extractSystemScope,
57
63
  extractToggleable,
58
64
  extractTranslations,
59
- extractTree,
60
65
  extractTreeActions,
61
66
  extractUiHints,
62
- extractUnmanagedTable,
63
67
  extractUseExtension,
64
68
  extractUsesApi,
65
69
  extractWorkspace,
66
70
  extractWriteHandler,
71
+ findFunctionLiteral,
67
72
  } from "./extractors";
68
73
  import type { FeaturePattern, UnknownPattern } from "./patterns";
69
74
  import { type SourceLocation, sourceLocationFromNode } from "./source-location";
@@ -146,7 +151,7 @@ export function parseSourceFile(sourceFile: SourceFile): ParseResult {
146
151
  const patterns: FeaturePattern[] = [];
147
152
  const errors: ParseError[] = [];
148
153
 
149
- walkSetupCallback(setupCallback, registrarParamName, sourceFile, patterns, errors);
154
+ walkSetupCallback(setupCallback.getBody(), registrarParamName, sourceFile, patterns, errors);
150
155
 
151
156
  return { featureName, patterns, errors };
152
157
  }
@@ -229,25 +234,131 @@ function extractRegistrarParamName(setup: ArrowFunction): string | undefined {
229
234
  * structurally impossible.
230
235
  */
231
236
  function walkSetupCallback(
232
- setup: ArrowFunction,
237
+ body: Node,
233
238
  registrarParamName: string,
234
239
  sourceFile: SourceFile,
235
240
  patterns: FeaturePattern[],
236
241
  errors: ParseError[],
242
+ visitedWrapperDecls: Set<Node> = new Set(),
237
243
  ): void {
238
- const body = setup.getBody();
239
244
  for (const call of body.getDescendantsOfKind(SyntaxKind.CallExpression)) {
240
245
  const methodName = extractRegistrarMethodName(call, registrarParamName);
241
- if (!methodName) continue; // Not a registrar call — ignore.
242
- const result = dispatchExtractor(methodName, call, sourceFile);
243
- if (result.kind === "pattern") {
244
- patterns.push(result.pattern);
245
- } else {
246
- errors.push(result.error);
246
+ if (methodName) {
247
+ const result = dispatchExtractor(methodName, call, sourceFile);
248
+ if (result.kind === "pattern") {
249
+ patterns.push(result.pattern);
250
+ } else {
251
+ errors.push(result.error);
252
+ }
253
+ continue;
247
254
  }
255
+
256
+ // Not a direct `<registrarParam>.<method>(...)` call. Check whether it's
257
+ // a registrar-wrapper call — a function that receives the registrar as
258
+ // a bare argument, e.g. `registerShowPonyScreens(r)`. Such wrappers are
259
+ // invisible to a receiver-shape match, so we resolve the callee (same
260
+ // file, or the declaring file of a named import — #1008) and recurse
261
+ // into its body with its own parameter name for the registrar slot.
262
+ const wrapper = resolveRegistrarWrapperCall(call, registrarParamName);
263
+ if (!wrapper) continue; // Free function call unrelated to the registrar — ignore.
264
+ if (visitedWrapperDecls.has(wrapper.declNode)) continue; // Cycle guard.
265
+ visitedWrapperDecls.add(wrapper.declNode);
266
+ // wrapper.sourceFile, not the outer `sourceFile` param: a cross-file
267
+ // wrapper's body lives in a different file, and sourceLocationFromNode
268
+ // uses whichever SourceFile it's given for both the `file` path and
269
+ // the line/column table — passing the wrong one silently corrupts
270
+ // both for every pattern found inside the wrapper.
271
+ walkSetupCallback(
272
+ wrapper.body,
273
+ wrapper.paramName,
274
+ wrapper.sourceFile,
275
+ patterns,
276
+ errors,
277
+ visitedWrapperDecls,
278
+ );
248
279
  }
249
280
  }
250
281
 
282
+ /**
283
+ * Resolves a bare call like `registerShowPonyScreens(r)` to the callee's
284
+ * body + its own parameter name for the registrar slot. The callee may be
285
+ * declared in the same file (function declaration, or a const initialized
286
+ * with an arrow/function expression), or imported by name from another
287
+ * file — resolved via the module specifier's SourceFile (#1008; only
288
+ * works against a real-filesystem Project, e.g. parseFeatureFile's. The
289
+ * in-memory Designer Project has no files to resolve against and
290
+ * getModuleSpecifierSourceFile() returns undefined there, same as any
291
+ * other unresolvable reference).
292
+ */
293
+ function resolveRegistrarWrapperCall(
294
+ call: CallExpression,
295
+ registrarParamName: string,
296
+ ):
297
+ | {
298
+ readonly body: Node;
299
+ readonly paramName: string;
300
+ readonly declNode: Node;
301
+ readonly sourceFile: SourceFile;
302
+ }
303
+ | undefined {
304
+ const callee = call.getExpression().asKind(SyntaxKind.Identifier);
305
+ if (!callee) return undefined;
306
+
307
+ const args = call.getArguments();
308
+ const argIndex = args.findIndex((arg) => arg.getText() === registrarParamName);
309
+ if (argIndex === -1) return undefined; // Registrar not passed to this call.
310
+
311
+ const callSourceFile = call.getSourceFile();
312
+ const name = callee.getText();
313
+
314
+ const local = resolveWrapperDeclarationInFile(callSourceFile, name, argIndex);
315
+ if (local) return { ...local, sourceFile: callSourceFile };
316
+
317
+ const importDecl = callSourceFile
318
+ .getImportDeclarations()
319
+ .find((d) =>
320
+ d.getNamedImports().some((ni) => (ni.getAliasNode()?.getText() ?? ni.getName()) === name),
321
+ );
322
+ if (!importDecl) return undefined;
323
+ const exportedName =
324
+ importDecl
325
+ .getNamedImports()
326
+ .find((ni) => (ni.getAliasNode()?.getText() ?? ni.getName()) === name)
327
+ ?.getName() ?? name;
328
+ const targetFile = importDecl.getModuleSpecifierSourceFile();
329
+ if (!targetFile) return undefined; // Unresolvable: in-memory project, external package, or missing file.
330
+
331
+ const imported = resolveWrapperDeclarationInFile(targetFile, exportedName, argIndex);
332
+ return imported ? { ...imported, sourceFile: targetFile } : undefined;
333
+ }
334
+
335
+ function resolveWrapperDeclarationInFile(
336
+ sourceFile: SourceFile,
337
+ name: string,
338
+ argIndex: number,
339
+ ): { readonly body: Node; readonly paramName: string; readonly declNode: Node } | undefined {
340
+ const fnDecl = sourceFile.getFunction(name);
341
+ if (fnDecl) {
342
+ const param = fnDecl.getParameters()[argIndex];
343
+ const body = fnDecl.getBody();
344
+ if (!param || !body) return undefined;
345
+ return { body, paramName: param.getName(), declNode: fnDecl };
346
+ }
347
+
348
+ const varDecl = sourceFile.getVariableDeclaration(name);
349
+ const init = varDecl?.getInitializer();
350
+ const literal = init ? findFunctionLiteral(init) : undefined;
351
+ const fnLiteral =
352
+ literal?.asKind(SyntaxKind.ArrowFunction) ?? literal?.asKind(SyntaxKind.FunctionExpression);
353
+ if (fnLiteral) {
354
+ const param = fnLiteral.getParameters()[argIndex];
355
+ if (!param) return undefined;
356
+ return { body: fnLiteral.getBody(), paramName: param.getName(), declNode: fnLiteral };
357
+ }
358
+
359
+ return undefined;
360
+ }
361
+
251
362
  /**
252
363
  * Returns the method name when the call has the form
253
364
  * `<registrarParamName>.<method>(...)`, otherwise undefined.
@@ -331,22 +442,20 @@ function dispatchExtractor(
331
442
  // Round 4 — mixed (header + body) patterns
332
443
  case "hook":
333
444
  return extractHook(call, sourceFile);
334
- case "entityHook":
335
- return extractEntityHook(call, sourceFile);
336
445
  case "authClaims":
337
446
  return extractAuthClaims(call, sourceFile);
338
447
  case "writeHandler":
339
448
  return extractWriteHandler(call, sourceFile);
340
449
  case "queryHandler":
341
450
  return extractQueryHandler(call, sourceFile);
451
+ case "streamHandler":
452
+ return extractStreamHandler(call, sourceFile);
342
453
  case "job":
343
454
  return extractJob(call, sourceFile);
344
455
  case "httpRoute":
345
456
  return extractHttpRoute(call, sourceFile);
346
457
  case "defineEvent":
347
458
  return extractDefineEvent(call, sourceFile);
348
- case "eventMigration":
349
- return extractEventMigration(call, sourceFile);
350
459
  case "notification":
351
460
  return extractNotification(call, sourceFile);
352
461
  case "projection":
@@ -362,13 +471,11 @@ function dispatchExtractor(
362
471
  return extractUsesApi(call, sourceFile);
363
472
  case "exposesApi":
364
473
  return extractExposesApi(call, sourceFile);
365
- case "unmanagedTable":
366
- return extractUnmanagedTable(call, sourceFile);
367
- // Round 6 — Visual-Tree patterns
474
+ case "storeTable":
475
+ return extractStoreTable(call, sourceFile);
476
+ // Round 6 — Tree-Actions pattern
368
477
  case "treeActions":
369
478
  return extractTreeActions(call, sourceFile);
370
- case "tree":
371
- return extractTree(call, sourceFile);
372
479
  // Round 7 — env-schema contract (opaque, Zod-expression argument)
373
480
  case "envSchema":
374
481
  return extractEnvSchema(call, sourceFile);
@@ -53,8 +53,12 @@ export type PatternId =
53
53
  | { readonly kind: "screen"; readonly id: string }
54
54
  | { readonly kind: "writeHandler"; readonly handlerName: string }
55
55
  | { readonly kind: "queryHandler"; readonly handlerName: string }
56
- | { readonly kind: "hook"; readonly hookType: string; readonly target: string }
57
- | { readonly kind: "entityHook"; readonly hookType: string; readonly entityName: string }
56
+ | { readonly kind: "streamHandler"; readonly handlerName: string }
57
+ | {
58
+ readonly kind: "hook";
59
+ readonly hookType: string;
60
+ readonly target: string | { readonly allOf: string };
61
+ }
58
62
  | { readonly kind: "metric"; readonly shortName: string }
59
63
  | { readonly kind: "secret"; readonly shortName: string }
60
64
  | { readonly kind: "claimKey"; readonly shortName: string }
@@ -66,12 +70,6 @@ export type PatternId =
66
70
  | { readonly kind: "projection"; readonly name: string }
67
71
  | { readonly kind: "multiStreamProjection"; readonly name: string }
68
72
  | { readonly kind: "defineEvent"; readonly eventName: string }
69
- | {
70
- readonly kind: "eventMigration";
71
- readonly eventName: string;
72
- readonly fromVersion: number;
73
- readonly toVersion: number;
74
- }
75
73
  | { readonly kind: "extendsRegistrar"; readonly extensionName: string }
76
74
  // Singleton patterns — only one per feature, kind alone identifies them.
77
75
  | { readonly kind: "requires" }
@@ -84,8 +82,7 @@ export type PatternId =
84
82
  | { readonly kind: "config" }
85
83
  | { readonly kind: "translations" }
86
84
  | { readonly kind: "authClaims" }
87
- | { readonly kind: "treeActions" }
88
- | { readonly kind: "tree" };
85
+ | { readonly kind: "treeActions" };
89
86
 
90
87
  // =============================================================================
91
88
  // Change ops — generic apply API
@@ -277,10 +274,9 @@ export const SINGLETON_KINDS: ReadonlySet<PatternId["kind"]> = new Set([
277
274
  "config",
278
275
  "translations",
279
276
  "authClaims",
280
- // Visual-Tree slots — at-most-one per feature, mirrors the registrar's
277
+ // Tree-Actions slot — at-most-one per feature, mirrors the registrar's
281
278
  // only-once-guard in define-feature.ts.
282
279
  "treeActions",
283
- "tree",
284
280
  ]);
285
281
 
286
282
  /**
@@ -336,7 +332,6 @@ function callMatchesId(call: CallExpression, id: PatternId): boolean {
336
332
  case "translations":
337
333
  case "authClaims":
338
334
  case "treeActions":
339
- case "tree":
340
335
  return true;
341
336
 
342
337
  case "entity":
@@ -359,12 +354,25 @@ function callMatchesId(call: CallExpression, id: PatternId): boolean {
359
354
  return matchObjectProperty(call, "id", id.id);
360
355
  case "writeHandler":
361
356
  case "queryHandler":
357
+ case "streamHandler":
362
358
  return (
363
359
  matchFirstArgString(call, id.handlerName) ||
364
360
  matchObjectProperty(call, "name", id.handlerName)
365
361
  );
366
362
  case "hook":
367
363
  // Positional: r.hook(type, target, fn) | Object: { type, target }
364
+ if (typeof id.target === "object") {
365
+ // Entity-wide { allOf } target — only ever appears as the object
366
+ // literal { allOf: entity } (positional or nested in the object
367
+ // form), never as a bare string, so check both call shapes.
368
+ if (matchFirstArgString(call, id.hookType)) {
369
+ return matchAllOfArg(call.getArguments()[1], id.target.allOf);
370
+ }
371
+ return (
372
+ matchObjectProperty(call, "type", id.hookType) &&
373
+ matchObjectAllOfProperty(call, "target", id.target.allOf)
374
+ );
375
+ }
368
376
  if (matchFirstArgString(call, id.hookType)) {
369
377
  const target = call.getArguments()[1]?.asKind(SyntaxKind.StringLiteral)?.getLiteralValue();
370
378
  return target === id.target;
@@ -373,15 +381,6 @@ function callMatchesId(call: CallExpression, id: PatternId): boolean {
373
381
  matchObjectProperty(call, "type", id.hookType) &&
374
382
  matchObjectProperty(call, "target", id.target)
375
383
  );
376
- case "entityHook":
377
- if (matchFirstArgString(call, id.hookType)) {
378
- const ent = call.getArguments()[1]?.asKind(SyntaxKind.StringLiteral)?.getLiteralValue();
379
- return ent === id.entityName;
380
- }
381
- return (
382
- matchObjectProperty(call, "type", id.hookType) &&
383
- matchObjectProperty(call, "entity", id.entityName)
384
- );
385
384
  case "metric":
386
385
  case "secret":
387
386
  case "claimKey":
@@ -422,20 +421,6 @@ function callMatchesId(call: CallExpression, id: PatternId): boolean {
422
421
  return (
423
422
  matchFirstArgString(call, id.eventName) || matchObjectProperty(call, "name", id.eventName)
424
423
  );
425
- case "eventMigration": {
426
- // Positional: r.eventMigration(name, from, to, fn)
427
- if (matchFirstArgString(call, id.eventName)) {
428
- const from = numericArg(call, 1);
429
- const to = numericArg(call, 2);
430
- return from === id.fromVersion && to === id.toVersion;
431
- }
432
- // Object: { event, fromVersion, toVersion }
433
- return (
434
- matchObjectProperty(call, "event", id.eventName) &&
435
- matchObjectNumericProperty(call, "fromVersion", id.fromVersion) &&
436
- matchObjectNumericProperty(call, "toVersion", id.toVersion)
437
- );
438
- }
439
424
  case "extendsRegistrar":
440
425
  return matchFirstArgString(call, id.extensionName);
441
426
  default: {
@@ -462,25 +447,29 @@ function matchObjectProperty(call: CallExpression, propName: string, expected: s
462
447
  return init?.getLiteralValue() === expected;
463
448
  }
464
449
 
465
- function matchObjectNumericProperty(
466
- call: CallExpression,
467
- propName: string,
468
- expected: number,
469
- ): boolean {
470
- const obj = call.getArguments()[0]?.asKind(SyntaxKind.ObjectLiteralExpression);
471
- if (!obj) return false;
450
+ // Matches an r.hook `{ allOf: entity }` target — as the arg node directly
451
+ // (positional call form) or as a nested object property (object call form).
452
+ function matchAllOfArg(node: Node | undefined, expectedEntity: string): boolean {
453
+ const obj = node?.asKind(SyntaxKind.ObjectLiteralExpression);
472
454
  const init = obj
473
- .getProperty(propName)
455
+ ?.getProperty("allOf")
474
456
  ?.asKind(SyntaxKind.PropertyAssignment)
475
457
  ?.getInitializer()
476
- ?.asKind(SyntaxKind.NumericLiteral);
477
- return init !== undefined && Number(init.getText()) === expected;
458
+ ?.asKind(SyntaxKind.StringLiteral);
459
+ return init?.getLiteralValue() === expectedEntity;
478
460
  }
479
461
 
480
- function numericArg(call: CallExpression, idx: number): number | undefined {
481
- const lit = call.getArguments()[idx]?.asKind(SyntaxKind.NumericLiteral);
482
- if (!lit) return undefined;
483
- return Number(lit.getText());
462
+ function matchObjectAllOfProperty(
463
+ call: CallExpression,
464
+ propName: string,
465
+ expectedEntity: string,
466
+ ): boolean {
467
+ const obj = call.getArguments()[0]?.asKind(SyntaxKind.ObjectLiteralExpression);
468
+ const propInit = obj
469
+ ?.getProperty(propName)
470
+ ?.asKind(SyntaxKind.PropertyAssignment)
471
+ ?.getInitializer();
472
+ return matchAllOfArg(propInit, expectedEntity);
484
473
  }
485
474
 
486
475
  // =============================================================================
@@ -104,17 +104,21 @@ export type AddQueryHandlerArgs = {
104
104
  readonly rateLimit?: RateLimitOption;
105
105
  };
106
106
 
107
- export type AddHookArgs = {
108
- readonly type: LifecycleHookType | "validation";
109
- readonly target: string | readonly string[];
110
- /** Source text of the closure, e.g. `"async (event, ctx) => { ... }"`. */
107
+ export type AddStreamHandlerArgs = {
108
+ readonly name: string;
109
+ readonly schemaSource: string;
111
110
  readonly handlerSource: string;
112
- readonly phase?: HookPhase;
111
+ readonly access?: AccessRule;
112
+ readonly rateLimit?: RateLimitOption;
113
113
  };
114
114
 
115
- export type AddEntityHookArgs = {
116
- readonly type: "postSave" | "preDelete" | "postDelete";
117
- readonly entity: string;
115
+ export type AddHookArgs = {
116
+ readonly type: LifecycleHookType | "validation";
117
+ // `{ allOf: entity }` — entity-wide target, fires for every write/query
118
+ // handler of that entity; replaces the old addEntityHook(). Only valid
119
+ // for postSave/preDelete/postDelete/postQuery, same as r.hook() itself.
120
+ readonly target: string | readonly string[] | { readonly allOf: string };
121
+ /** Source text of the closure, e.g. `"async (event, ctx) => { ... }"`. */
118
122
  readonly handlerSource: string;
119
123
  readonly phase?: HookPhase;
120
124
  };
@@ -144,13 +148,10 @@ export type AddDefineEventArgs = {
144
148
  readonly name: string;
145
149
  readonly schemaSource: string;
146
150
  readonly version?: number;
147
- };
148
-
149
- export type AddEventMigrationArgs = {
150
- readonly event: string;
151
- readonly fromVersion: number;
152
- readonly toVersion: number;
153
- readonly transformSource: string;
151
+ // Keyed by fromVersion (as a string) → the transform source text for the
152
+ // fromVersion -> fromVersion+1 step. Folded in from the former
153
+ // addEventMigration (#1082 step 8).
154
+ readonly migrations?: Readonly<Record<string, string>>;
154
155
  };
155
156
 
156
157
  export type AddProjectionArgs = {
@@ -240,8 +241,8 @@ export type FeaturePatcher = {
240
241
  readonly addScreen: (args: AddScreenArgs) => void;
241
242
  readonly addWriteHandler: (args: AddWriteHandlerArgs) => void;
242
243
  readonly addQueryHandler: (args: AddQueryHandlerArgs) => void;
244
+ readonly addStreamHandler: (args: AddStreamHandlerArgs) => void;
243
245
  readonly addHook: (args: AddHookArgs) => void;
244
- readonly addEntityHook: (args: AddEntityHookArgs) => void;
245
246
  readonly addJob: (args: AddJobArgs) => void;
246
247
  readonly addNotification: (args: AddNotificationArgs) => void;
247
248
  readonly addAuthClaims: (args: AddAuthClaimsArgs) => void;
@@ -249,7 +250,6 @@ export type FeaturePatcher = {
249
250
  readonly addProjection: (args: AddProjectionArgs) => void;
250
251
  readonly addMultiStreamProjection: (args: AddMultiStreamProjectionArgs) => void;
251
252
  readonly addDefineEvent: (args: AddDefineEventArgs) => void;
252
- readonly addEventMigration: (args: AddEventMigrationArgs) => void;
253
253
  // --- Symmetric ops (id-driven) ---
254
254
  readonly replace: (id: PatternId, pattern: FeaturePattern) => void;
255
255
  readonly remove: (id: PatternId) => void;
@@ -401,23 +401,24 @@ export function createFeaturePatcher(sourceFile: SourceFile): FeaturePatcher {
401
401
  });
402
402
  },
403
403
 
404
- addHook({ type, target, handlerSource, phase }) {
404
+ addStreamHandler({ name, schemaSource, handlerSource, access, rateLimit }) {
405
405
  add({
406
- kind: "hook",
406
+ kind: "streamHandler",
407
407
  source: SYNTHETIC_LOC,
408
- hookType: type,
409
- target,
410
- fnBody: rawLoc(handlerSource),
411
- ...(phase !== undefined && { phase }),
408
+ handlerName: name,
409
+ schemaSource: rawLoc(schemaSource),
410
+ handlerBody: rawLoc(handlerSource),
411
+ ...(access !== undefined && { access }),
412
+ ...(rateLimit !== undefined && { rateLimit }),
412
413
  });
413
414
  },
414
415
 
415
- addEntityHook({ type, entity, handlerSource, phase }) {
416
+ addHook({ type, target, handlerSource, phase }) {
416
417
  add({
417
- kind: "entityHook",
418
+ kind: "hook",
418
419
  source: SYNTHETIC_LOC,
419
420
  hookType: type,
420
- entityName: entity,
421
+ target,
421
422
  fnBody: rawLoc(handlerSource),
422
423
  ...(phase !== undefined && { phase }),
423
424
  });
@@ -495,24 +496,22 @@ export function createFeaturePatcher(sourceFile: SourceFile): FeaturePatcher {
495
496
  });
496
497
  },
497
498
 
498
- addDefineEvent({ name, schemaSource, version }) {
499
+ addDefineEvent({ name, schemaSource, version, migrations }) {
500
+ const migrationLocs = migrations
501
+ ? Object.fromEntries(
502
+ Object.entries(migrations).map(([fromVersion, source]) => [
503
+ fromVersion,
504
+ rawLoc(source),
505
+ ]),
506
+ )
507
+ : undefined;
499
508
  add({
500
509
  kind: "defineEvent",
501
510
  source: SYNTHETIC_LOC,
502
511
  eventName: name,
503
512
  schemaSource: rawLoc(schemaSource),
504
513
  ...(version !== undefined && { version }),
505
- });
506
- },
507
-
508
- addEventMigration({ event, fromVersion, toVersion, transformSource }) {
509
- add({
510
- kind: "eventMigration",
511
- source: SYNTHETIC_LOC,
512
- eventName: event,
513
- fromVersion,
514
- toVersion,
515
- transformBody: rawLoc(transformSource),
514
+ ...(migrationLocs !== undefined && { migrations: migrationLocs }),
516
515
  });
517
516
  },
518
517