@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
@@ -0,0 +1,322 @@
1
+ import type { CallExpression, Node, SourceFile } from "ts-morph";
2
+ import { SyntaxKind } from "ts-morph";
3
+ import type { DefineEventPattern, NotificationPattern } from "../patterns";
4
+ import type { SourceLocation } from "../source-location";
5
+ import { sourceLocationFromNode } from "../source-location";
6
+ import {
7
+ type ExtractOutput,
8
+ fail,
9
+ findFunctionLiteral,
10
+ ok,
11
+ readDataLiteralNode,
12
+ readNameOrRef,
13
+ readPropertyKey,
14
+ } from "./shared";
15
+
16
+ // Reads defineEvent's `migrations` option — either the array-of-steps shape
17
+ // used by hand-authored calls (`[{ fromVersion, toVersion, transform }]`,
18
+ // toVersion is redundant on-disk since it is always fromVersion + 1) or the
19
+ // keyed-object canonical shape the Designer renders (`{ "1": transform }`).
20
+ // Keyed by fromVersion (as a string) → the transform closure's location.
21
+ // Skips malformed entries rather than failing the whole defineEvent extract
22
+ // — same "best-effort, degrade gracefully" posture as readDataLiteralNode.
23
+ function extractEventMigrationsFromArray(
24
+ arr: Node,
25
+ sourceFile: SourceFile,
26
+ ): Record<string, SourceLocation> {
27
+ const result: Record<string, SourceLocation> = {};
28
+ const arrLit = arr.asKindOrThrow(SyntaxKind.ArrayLiteralExpression);
29
+ for (const el of arrLit.getElements()) {
30
+ const obj = el.asKind(SyntaxKind.ObjectLiteralExpression);
31
+ if (!obj) continue;
32
+ const fromInit = obj
33
+ .getProperty("fromVersion")
34
+ ?.asKind(SyntaxKind.PropertyAssignment)
35
+ ?.getInitializer();
36
+ const fromVersion = fromInit ? readDataLiteralNode(fromInit) : undefined;
37
+ if (typeof fromVersion !== "number") continue;
38
+ const transformInit = obj
39
+ .getProperty("transform")
40
+ ?.asKind(SyntaxKind.PropertyAssignment)
41
+ ?.getInitializer();
42
+ const fn = transformInit ? findFunctionLiteral(transformInit) : undefined;
43
+ if (!fn) continue;
44
+ result[String(fromVersion)] = sourceLocationFromNode(fn, sourceFile);
45
+ }
46
+ return result;
47
+ }
48
+
49
+ function extractEventMigrationsFromKeyedObject(
50
+ objLit: Node,
51
+ sourceFile: SourceFile,
52
+ ): Record<string, SourceLocation> {
53
+ const result: Record<string, SourceLocation> = {};
54
+ const obj = objLit.asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
55
+ for (const prop of obj.getProperties()) {
56
+ const propAssign = prop.asKind(SyntaxKind.PropertyAssignment);
57
+ const initializer = propAssign?.getInitializer();
58
+ const fn = initializer ? findFunctionLiteral(initializer) : undefined;
59
+ if (!propAssign || !fn) continue;
60
+ result[readPropertyKey(propAssign)] = sourceLocationFromNode(fn, sourceFile);
61
+ }
62
+ return result;
63
+ }
64
+
65
+ // Reads defineEvent's `migrations` option — either the array-of-steps shape
66
+ // used by hand-authored calls (`[{ fromVersion, toVersion, transform }]`,
67
+ // toVersion is redundant on-disk since it is always fromVersion + 1) or the
68
+ // keyed-object canonical shape the Designer renders (`{ "1": transform }`).
69
+ // Keyed by fromVersion (as a string) → the transform closure's location.
70
+ // Skips malformed entries rather than failing the whole defineEvent extract
71
+ // — same "best-effort, degrade gracefully" posture as readDataLiteralNode.
72
+ function extractEventMigrationsField(
73
+ node: Node,
74
+ sourceFile: SourceFile,
75
+ ): Readonly<Record<string, SourceLocation>> | undefined {
76
+ const result = node.asKind(SyntaxKind.ArrayLiteralExpression)
77
+ ? extractEventMigrationsFromArray(node, sourceFile)
78
+ : node.asKind(SyntaxKind.ObjectLiteralExpression)
79
+ ? extractEventMigrationsFromKeyedObject(node, sourceFile)
80
+ : undefined;
81
+ return result && Object.keys(result).length > 0 ? result : undefined;
82
+ }
83
+
84
+ export function extractDefineEvent(
85
+ call: CallExpression,
86
+ sourceFile: SourceFile,
87
+ ): ExtractOutput<DefineEventPattern> {
88
+ const args = call.getArguments();
89
+ const first = args[0];
90
+ if (!first) {
91
+ return fail(
92
+ "defineEvent",
93
+ sourceLocationFromNode(call, sourceFile),
94
+ "expected at least one argument",
95
+ );
96
+ }
97
+
98
+ const obj = first.asKind(SyntaxKind.ObjectLiteralExpression);
99
+ if (obj && args.length === 1) {
100
+ const nameInit = obj
101
+ .getProperty("name")
102
+ ?.asKind(SyntaxKind.PropertyAssignment)
103
+ ?.getInitializer()
104
+ ?.asKind(SyntaxKind.StringLiteral);
105
+ if (!nameInit) {
106
+ return fail(
107
+ "defineEvent",
108
+ sourceLocationFromNode(call, sourceFile),
109
+ "object form requires a string-literal `name` property",
110
+ );
111
+ }
112
+ const schemaInit = obj
113
+ .getProperty("schema")
114
+ ?.asKind(SyntaxKind.PropertyAssignment)
115
+ ?.getInitializer();
116
+ if (!schemaInit) {
117
+ return fail(
118
+ "defineEvent",
119
+ sourceLocationFromNode(call, sourceFile),
120
+ "object form requires a `schema` property",
121
+ );
122
+ }
123
+ let version: number | undefined;
124
+ const versionInit = obj
125
+ .getProperty("version")
126
+ ?.asKind(SyntaxKind.PropertyAssignment)
127
+ ?.getInitializer();
128
+ if (versionInit) {
129
+ const v = readDataLiteralNode(versionInit);
130
+ if (typeof v === "number") version = v;
131
+ }
132
+ const migrationsInit = obj
133
+ .getProperty("migrations")
134
+ ?.asKind(SyntaxKind.PropertyAssignment)
135
+ ?.getInitializer();
136
+ const migrations = migrationsInit
137
+ ? extractEventMigrationsField(migrationsInit, sourceFile)
138
+ : undefined;
139
+ return ok({
140
+ kind: "defineEvent",
141
+ source: sourceLocationFromNode(call, sourceFile),
142
+ eventName: nameInit.getLiteralValue(),
143
+ schemaSource: sourceLocationFromNode(schemaInit, sourceFile),
144
+ ...(version !== undefined && { version }),
145
+ ...(migrations !== undefined && { migrations }),
146
+ });
147
+ }
148
+
149
+ const nameArg = first.asKind(SyntaxKind.StringLiteral);
150
+ if (!nameArg) {
151
+ return fail(
152
+ "defineEvent",
153
+ sourceLocationFromNode(call, sourceFile),
154
+ "first argument must be a string literal event name (or use the object form)",
155
+ );
156
+ }
157
+ const schemaArg = args[1];
158
+ if (!schemaArg) {
159
+ return fail(
160
+ "defineEvent",
161
+ sourceLocationFromNode(call, sourceFile),
162
+ "expected a Zod schema as second argument",
163
+ );
164
+ }
165
+ let version: number | undefined;
166
+ let migrations: Readonly<Record<string, SourceLocation>> | undefined;
167
+ const optionsArg = args[2];
168
+ const optionsObj = optionsArg?.asKind(SyntaxKind.ObjectLiteralExpression);
169
+ if (optionsObj) {
170
+ const versionInit = optionsObj
171
+ .getProperty("version")
172
+ ?.asKind(SyntaxKind.PropertyAssignment)
173
+ ?.getInitializer();
174
+ if (versionInit) {
175
+ const v = readDataLiteralNode(versionInit);
176
+ if (typeof v === "number") version = v;
177
+ }
178
+ const migrationsInit = optionsObj
179
+ .getProperty("migrations")
180
+ ?.asKind(SyntaxKind.PropertyAssignment)
181
+ ?.getInitializer();
182
+ if (migrationsInit) {
183
+ migrations = extractEventMigrationsField(migrationsInit, sourceFile);
184
+ }
185
+ }
186
+ return ok({
187
+ kind: "defineEvent",
188
+ source: sourceLocationFromNode(call, sourceFile),
189
+ eventName: nameArg.getLiteralValue(),
190
+ schemaSource: sourceLocationFromNode(schemaArg, sourceFile),
191
+ ...(version !== undefined && { version }),
192
+ ...(migrations !== undefined && { migrations }),
193
+ });
194
+ }
195
+
196
+ export function extractNotification(
197
+ call: CallExpression,
198
+ sourceFile: SourceFile,
199
+ ): ExtractOutput<NotificationPattern> {
200
+ const args = call.getArguments();
201
+ const first = args[0];
202
+ if (!first) {
203
+ return fail(
204
+ "notification",
205
+ sourceLocationFromNode(call, sourceFile),
206
+ "expected at least one argument",
207
+ );
208
+ }
209
+
210
+ let nameLiteral: ReturnType<typeof first.asKind<SyntaxKind.StringLiteral>>;
211
+ let defObj: ReturnType<typeof first.asKind<SyntaxKind.ObjectLiteralExpression>>;
212
+
213
+ const firstObj = first.asKind(SyntaxKind.ObjectLiteralExpression);
214
+ if (firstObj && args.length === 1) {
215
+ nameLiteral = firstObj
216
+ .getProperty("name")
217
+ ?.asKind(SyntaxKind.PropertyAssignment)
218
+ ?.getInitializer()
219
+ ?.asKind(SyntaxKind.StringLiteral);
220
+ if (!nameLiteral) {
221
+ return fail(
222
+ "notification",
223
+ sourceLocationFromNode(call, sourceFile),
224
+ "object form requires a string-literal `name` property",
225
+ );
226
+ }
227
+ defObj = firstObj;
228
+ } else {
229
+ nameLiteral = first.asKind(SyntaxKind.StringLiteral);
230
+ if (!nameLiteral) {
231
+ return fail(
232
+ "notification",
233
+ sourceLocationFromNode(call, sourceFile),
234
+ "first argument must be a string literal notification name (or use the object form)",
235
+ );
236
+ }
237
+ defObj = args[1]?.asKind(SyntaxKind.ObjectLiteralExpression);
238
+ if (!defObj) {
239
+ return fail(
240
+ "notification",
241
+ sourceLocationFromNode(call, sourceFile),
242
+ "second argument must be an inline definition object",
243
+ );
244
+ }
245
+ }
246
+ const nameArg = nameLiteral;
247
+ const triggerObj = defObj
248
+ .getProperty("trigger")
249
+ ?.asKind(SyntaxKind.PropertyAssignment)
250
+ ?.getInitializer()
251
+ ?.asKind(SyntaxKind.ObjectLiteralExpression);
252
+ if (!triggerObj) {
253
+ return fail(
254
+ "notification",
255
+ sourceLocationFromNode(call, sourceFile),
256
+ "missing or non-object `trigger` property",
257
+ );
258
+ }
259
+ const onInit = triggerObj
260
+ .getProperty("on")
261
+ ?.asKind(SyntaxKind.PropertyAssignment)
262
+ ?.getInitializer();
263
+ const onName = onInit ? readNameOrRef(onInit) : undefined;
264
+ if (!onName) {
265
+ return fail(
266
+ "notification",
267
+ sourceLocationFromNode(call, sourceFile),
268
+ "trigger.on must be a string literal or inline { name } object",
269
+ );
270
+ }
271
+ const recipientInit = defObj
272
+ .getProperty("recipient")
273
+ ?.asKind(SyntaxKind.PropertyAssignment)
274
+ ?.getInitializer();
275
+ const recipientFn = recipientInit ? findFunctionLiteral(recipientInit) : undefined;
276
+ if (!recipientFn) {
277
+ return fail(
278
+ "notification",
279
+ sourceLocationFromNode(call, sourceFile),
280
+ "recipient must be an inline arrow function or function expression",
281
+ );
282
+ }
283
+ const dataInit = defObj
284
+ .getProperty("data")
285
+ ?.asKind(SyntaxKind.PropertyAssignment)
286
+ ?.getInitializer();
287
+ const dataFn = dataInit ? findFunctionLiteral(dataInit) : undefined;
288
+ if (!dataFn) {
289
+ return fail(
290
+ "notification",
291
+ sourceLocationFromNode(call, sourceFile),
292
+ "data must be an inline arrow function or function expression",
293
+ );
294
+ }
295
+ let templates: Record<string, SourceLocation> | undefined;
296
+ const templatesObj = defObj
297
+ .getProperty("templates")
298
+ ?.asKind(SyntaxKind.PropertyAssignment)
299
+ ?.getInitializer()
300
+ ?.asKind(SyntaxKind.ObjectLiteralExpression);
301
+ if (templatesObj) {
302
+ templates = {};
303
+ for (const prop of templatesObj.getProperties()) {
304
+ const propAssign = prop.asKind(SyntaxKind.PropertyAssignment);
305
+ if (!propAssign) continue;
306
+ const init = propAssign.getInitializer();
307
+ if (!init) continue;
308
+ const tfn = findFunctionLiteral(init);
309
+ if (!tfn) continue;
310
+ templates[readPropertyKey(propAssign)] = sourceLocationFromNode(tfn, sourceFile);
311
+ }
312
+ }
313
+ return ok({
314
+ kind: "notification",
315
+ source: sourceLocationFromNode(call, sourceFile),
316
+ notificationName: nameArg.getLiteralValue(),
317
+ trigger: { on: onName },
318
+ recipientBody: sourceLocationFromNode(recipientFn, sourceFile),
319
+ dataBody: sourceLocationFromNode(dataFn, sourceFile),
320
+ ...(templates !== undefined && { templates }),
321
+ });
322
+ }
@@ -0,0 +1,234 @@
1
+ import type { CallExpression, Node, SourceFile } from "ts-morph";
2
+ import { SyntaxKind } from "ts-morph";
3
+ import type { AccessRule, RateLimitOption } from "../../types/handlers";
4
+ import type { QueryHandlerPattern, StreamHandlerPattern, WriteHandlerPattern } from "../patterns";
5
+ import type { SourceLocation } from "../source-location";
6
+ import { sourceLocationFromNode } from "../source-location";
7
+ import { readOptionalAccessRule, readOptionalRateLimit } from "./hooks";
8
+ import {
9
+ type ExtractOutput,
10
+ fail,
11
+ findFunctionLiteral,
12
+ isPlainObject,
13
+ isRawRefSentinel,
14
+ ok,
15
+ readBooleanProperty,
16
+ readDataLiteralNode,
17
+ } from "./shared";
18
+
19
+ export type ParsedHandlerCall = {
20
+ readonly source: SourceLocation;
21
+ readonly handlerName?: string;
22
+ readonly schemaSource?: SourceLocation;
23
+ readonly handlerBody?: SourceLocation;
24
+ readonly access?: AccessRule;
25
+ readonly rateLimit?: RateLimitOption;
26
+ readonly unsafeSkipTransitionGuard?: boolean;
27
+ };
28
+
29
+ /**
30
+ * Resolves an argument standing in for a handler-call's object-form body:
31
+ * either it's already an object literal, or a bare identifier declared
32
+ * locally (same file only) with one as its initializer. Anything else
33
+ * (imported binding, factory call, ...) is not resolvable here.
34
+ */
35
+ function resolveObjectLiteralArg(node: Node) {
36
+ const direct = node.asKind(SyntaxKind.ObjectLiteralExpression);
37
+ if (direct) return direct;
38
+ const identifier = node.asKind(SyntaxKind.Identifier);
39
+ if (!identifier) return undefined;
40
+ const varDecl = node.getSourceFile().getVariableDeclaration(identifier.getText());
41
+ return varDecl?.getInitializer()?.asKind(SyntaxKind.ObjectLiteralExpression);
42
+ }
43
+
44
+ export function parseHandlerCall(
45
+ call: CallExpression,
46
+ sourceFile: SourceFile,
47
+ methodName: "writeHandler" | "queryHandler" | "streamHandler",
48
+ ): ExtractOutput<ParsedHandlerCall> {
49
+ const args = call.getArguments();
50
+ const first = args[0];
51
+ if (!first) {
52
+ return fail(
53
+ methodName,
54
+ sourceLocationFromNode(call, sourceFile),
55
+ "expected at least one argument",
56
+ );
57
+ }
58
+
59
+ const obj = args.length === 1 ? resolveObjectLiteralArg(first) : undefined;
60
+ if (obj) {
61
+ const nameLiteral = obj
62
+ .getProperty("name")
63
+ ?.asKind(SyntaxKind.PropertyAssignment)
64
+ ?.getInitializer()
65
+ ?.asKind(SyntaxKind.StringLiteral);
66
+ if (!nameLiteral) {
67
+ return fail(
68
+ methodName,
69
+ sourceLocationFromNode(call, sourceFile),
70
+ "object form requires a string-literal `name` property",
71
+ );
72
+ }
73
+ const schemaInit = obj
74
+ .getProperty("schema")
75
+ ?.asKind(SyntaxKind.PropertyAssignment)
76
+ ?.getInitializer();
77
+ if (!schemaInit) {
78
+ return fail(
79
+ methodName,
80
+ sourceLocationFromNode(call, sourceFile),
81
+ "object form requires a `schema` property",
82
+ );
83
+ }
84
+ const handlerInit = obj
85
+ .getProperty("handler")
86
+ ?.asKind(SyntaxKind.PropertyAssignment)
87
+ ?.getInitializer();
88
+ if (!handlerInit) {
89
+ return fail(
90
+ methodName,
91
+ sourceLocationFromNode(call, sourceFile),
92
+ "object form requires a `handler` property",
93
+ );
94
+ }
95
+ const fn = findFunctionLiteral(handlerInit);
96
+ if (!fn) {
97
+ return fail(
98
+ methodName,
99
+ sourceLocationFromNode(call, sourceFile),
100
+ "handler must be an inline arrow function or function expression",
101
+ );
102
+ }
103
+ const accessInit = obj
104
+ .getProperty("access")
105
+ ?.asKind(SyntaxKind.PropertyAssignment)
106
+ ?.getInitializer();
107
+ const access = accessInit ? readOptionalAccessRule(readDataLiteralNode(accessInit)) : undefined;
108
+ const rateLimitInit = obj
109
+ .getProperty("rateLimit")
110
+ ?.asKind(SyntaxKind.PropertyAssignment)
111
+ ?.getInitializer();
112
+ const rateLimit = rateLimitInit
113
+ ? readOptionalRateLimit(readDataLiteralNode(rateLimitInit))
114
+ : undefined;
115
+ const skip = readBooleanProperty(obj, "unsafeSkipTransitionGuard");
116
+ return ok({
117
+ source: sourceLocationFromNode(call, sourceFile),
118
+ handlerName: nameLiteral.getLiteralValue(),
119
+ schemaSource: sourceLocationFromNode(schemaInit, sourceFile),
120
+ handlerBody: sourceLocationFromNode(fn, sourceFile),
121
+ ...(access !== undefined && { access }),
122
+ ...(rateLimit !== undefined && { rateLimit }),
123
+ ...(skip === true && { unsafeSkipTransitionGuard: true }),
124
+ });
125
+ }
126
+
127
+ // A single reference standing in for the whole handler argument set
128
+ // (`r.writeHandler(eventCreateHandler)`, `r.queryHandler(someQuery())`)
129
+ // that resolveObjectLiteralArg above couldn't turn into an object literal
130
+ // (imported binding, factory call, ...). Keep it recognised as this kind
131
+ // instead of ParseErroring — see #1007. Opaque: renderWriteHandler/
132
+ // renderQueryHandler re-emit `source.raw` verbatim when handlerName is
133
+ // undefined.
134
+ if (args.length === 1 && isRawRefSentinel(readDataLiteralNode(first))) {
135
+ return ok({ source: sourceLocationFromNode(call, sourceFile) });
136
+ }
137
+ const nameLiteral = first.asKind(SyntaxKind.StringLiteral);
138
+ if (!nameLiteral) {
139
+ return fail(
140
+ methodName,
141
+ sourceLocationFromNode(call, sourceFile),
142
+ "first argument must be a string literal handler name (or use the object form)",
143
+ );
144
+ }
145
+ const schemaArg = args[1];
146
+ if (!schemaArg) {
147
+ return fail(
148
+ methodName,
149
+ sourceLocationFromNode(call, sourceFile),
150
+ "expected a Zod schema as second argument",
151
+ );
152
+ }
153
+ const handlerArg = args[2];
154
+ if (!handlerArg) {
155
+ return fail(
156
+ methodName,
157
+ sourceLocationFromNode(call, sourceFile),
158
+ "expected a handler function as third argument",
159
+ );
160
+ }
161
+ const fn = findFunctionLiteral(handlerArg);
162
+ if (!fn) {
163
+ return fail(
164
+ methodName,
165
+ sourceLocationFromNode(call, sourceFile),
166
+ "third argument must be an inline arrow function or function expression",
167
+ );
168
+ }
169
+ const optionsArg = args[3];
170
+ let access: AccessRule | undefined;
171
+ let rateLimit: RateLimitOption | undefined;
172
+ if (optionsArg) {
173
+ const options = readDataLiteralNode(optionsArg);
174
+ if (isPlainObject(options)) {
175
+ access = readOptionalAccessRule(options["access"]);
176
+ rateLimit = readOptionalRateLimit(options["rateLimit"]);
177
+ }
178
+ }
179
+ return ok({
180
+ source: sourceLocationFromNode(call, sourceFile),
181
+ handlerName: nameLiteral.getLiteralValue(),
182
+ schemaSource: sourceLocationFromNode(schemaArg, sourceFile),
183
+ handlerBody: sourceLocationFromNode(fn, sourceFile),
184
+ ...(access !== undefined && { access }),
185
+ ...(rateLimit !== undefined && { rateLimit }),
186
+ });
187
+ }
188
+
189
+ export function extractWriteHandler(
190
+ call: CallExpression,
191
+ sourceFile: SourceFile,
192
+ ): ExtractOutput<WriteHandlerPattern> {
193
+ const parsed = parseHandlerCall(call, sourceFile, "writeHandler");
194
+ if (parsed.kind === "error") return parsed;
195
+ return ok({
196
+ kind: "writeHandler",
197
+ source: parsed.pattern.source,
198
+ handlerName: parsed.pattern.handlerName,
199
+ schemaSource: parsed.pattern.schemaSource,
200
+ handlerBody: parsed.pattern.handlerBody,
201
+ ...(parsed.pattern.access !== undefined && { access: parsed.pattern.access }),
202
+ ...(parsed.pattern.rateLimit !== undefined && { rateLimit: parsed.pattern.rateLimit }),
203
+ ...(parsed.pattern.unsafeSkipTransitionGuard === true && { unsafeSkipTransitionGuard: true }),
204
+ });
205
+ }
206
+
207
+ function readHandlerFields(parsed: Extract<ExtractOutput<ParsedHandlerCall>, { kind: "pattern" }>) {
208
+ return {
209
+ source: parsed.pattern.source,
210
+ handlerName: parsed.pattern.handlerName,
211
+ schemaSource: parsed.pattern.schemaSource,
212
+ handlerBody: parsed.pattern.handlerBody,
213
+ ...(parsed.pattern.access !== undefined && { access: parsed.pattern.access }),
214
+ ...(parsed.pattern.rateLimit !== undefined && { rateLimit: parsed.pattern.rateLimit }),
215
+ };
216
+ }
217
+
218
+ export function extractQueryHandler(
219
+ call: CallExpression,
220
+ sourceFile: SourceFile,
221
+ ): ExtractOutput<QueryHandlerPattern> {
222
+ const parsed = parseHandlerCall(call, sourceFile, "queryHandler");
223
+ if (parsed.kind === "error") return parsed;
224
+ return ok({ kind: "queryHandler", ...readHandlerFields(parsed) });
225
+ }
226
+
227
+ export function extractStreamHandler(
228
+ call: CallExpression,
229
+ sourceFile: SourceFile,
230
+ ): ExtractOutput<StreamHandlerPattern> {
231
+ const parsed = parseHandlerCall(call, sourceFile, "streamHandler");
232
+ if (parsed.kind === "error") return parsed;
233
+ return ok({ kind: "streamHandler", ...readHandlerFields(parsed) });
234
+ }