@cosmicdrift/kumiko-framework 0.386.0 → 0.388.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 (468) hide show
  1. package/AGENTS.md +6 -1
  2. package/LICENSE +21 -57
  3. package/README.md +2 -2
  4. package/agents/examples/anonymous-access.md +1 -1
  5. package/agents/examples/apex-landing.md +204 -0
  6. package/agents/examples/api-worker-split.md +64 -0
  7. package/agents/examples/app-config.md +14 -0
  8. package/agents/examples/auth-mfa.md +115 -0
  9. package/agents/examples/env-composition.md +28 -1
  10. package/agents/examples/legal-pages.md +1 -1
  11. package/agents/examples/prod-boot-options.md +16 -0
  12. package/agents/examples/rate-limiting.md +48 -0
  13. package/agents/examples/record-detail-layout.md +692 -0
  14. package/agents/examples/seed-migration.md +212 -0
  15. package/agents/examples/wizard-form.md +941 -0
  16. package/agents/examples/writeform-section.md +548 -0
  17. package/agents/features.md +19 -2
  18. package/agents/widgets.md +40 -39
  19. package/dist/api/api-constants.js +2 -2
  20. package/dist/api/auth-middleware.js +7 -7
  21. package/dist/api/auth-rate-limiters.js +7 -3
  22. package/dist/api/auth-routes-mfa.js +1 -1
  23. package/dist/api/auth-routes-session.js +1 -1
  24. package/dist/api/auth-routes-shared.js +7 -3
  25. package/dist/api/auth-routes-signup.js +1 -1
  26. package/dist/api/client-error-route.js +8 -2
  27. package/dist/api/client-ip.js +3 -1
  28. package/dist/api/extra-route.js +1 -1
  29. package/dist/api/index.d.ts +3 -1
  30. package/dist/api/index.js +3 -1
  31. package/dist/api/pii-leak-guard.js +4 -2
  32. package/dist/api/readiness.js +5 -5
  33. package/dist/api/redis-sse-broker.js +8 -6
  34. package/dist/api/request-context.d.ts +1 -1
  35. package/dist/api/request-context.js +1 -1
  36. package/dist/api/routes.js +2 -2
  37. package/dist/api/server-boot-checks.d.ts +10 -0
  38. package/dist/api/server-boot-checks.js +157 -0
  39. package/dist/api/server-event-dispatcher.d.ts +26 -0
  40. package/dist/api/server-event-dispatcher.js +208 -0
  41. package/dist/api/server-extra-routes.d.ts +11 -0
  42. package/dist/api/server-extra-routes.js +203 -0
  43. package/dist/api/server-middleware.d.ts +20 -0
  44. package/dist/api/server-middleware.js +211 -0
  45. package/dist/api/server-routes.d.ts +19 -0
  46. package/dist/api/server-routes.js +293 -0
  47. package/dist/api/server-websocket-routes.d.ts +4 -0
  48. package/dist/api/server-websocket-routes.js +108 -0
  49. package/dist/api/server-wiring.d.ts +32 -0
  50. package/dist/api/server-wiring.js +236 -0
  51. package/dist/api/server.d.ts +10 -15
  52. package/dist/api/server.js +48 -1250
  53. package/dist/api/session-cookies.js +4 -4
  54. package/dist/api/sse-broker.js +1 -1
  55. package/dist/api/sse-route.d.ts +13 -13
  56. package/dist/api/sse-route.js +31 -27
  57. package/dist/auth/index.js +4 -4
  58. package/dist/auth/roles.d.ts +4 -4
  59. package/dist/auth/roles.js +24 -24
  60. package/dist/compliance/duration-spec.js +18 -20
  61. package/dist/compliance/index.js +2 -3
  62. package/dist/compliance/override-schema.d.ts +4 -4
  63. package/dist/compliance/override-schema.js +19 -22
  64. package/dist/compliance/profiles.d.ts +26 -29
  65. package/dist/compliance/profiles.js +62 -69
  66. package/dist/compliance/sub-processors.d.ts +28 -30
  67. package/dist/compliance/sub-processors.js +17 -21
  68. package/dist/consumer-cli.js +4 -4
  69. package/dist/crypto/blind-index.js +18 -17
  70. package/dist/crypto/ciphertext-pattern.js +1 -1
  71. package/dist/crypto/event-pii.js +3 -3
  72. package/dist/crypto/kek-source.d.ts +1 -1
  73. package/dist/crypto/kek-source.js +3 -2
  74. package/dist/crypto/kms-adapter.d.ts +1 -1
  75. package/dist/crypto/kms-adapter.js +4 -6
  76. package/dist/crypto/kms-wiring.d.ts +1 -2
  77. package/dist/crypto/kms-wiring.js +2 -2
  78. package/dist/crypto/owner-transition-rekey.d.ts +5 -1
  79. package/dist/crypto/owner-transition-rekey.js +1 -1
  80. package/dist/crypto/pg-kms-adapter.js +2 -6
  81. package/dist/crypto/pii-field-encryption.d.ts +5 -1
  82. package/dist/crypto/pii-field-encryption.js +7 -5
  83. package/dist/crypto/subject-resolver.d.ts +1 -1
  84. package/dist/crypto/subject-resolver.js +2 -2
  85. package/dist/db/aggregate-sql.d.ts +21 -0
  86. package/dist/db/aggregate-sql.js +155 -0
  87. package/dist/db/apply-entity-event.d.ts +6 -6
  88. package/dist/db/apply-entity-event.js +55 -55
  89. package/dist/db/blind-index-cleanup.js +7 -7
  90. package/dist/db/closed-connection.d.ts +9 -0
  91. package/dist/db/closed-connection.js +95 -0
  92. package/dist/db/collect-table-metas.js +10 -11
  93. package/dist/db/compound-types.d.ts +1 -1
  94. package/dist/db/compound-types.js +12 -12
  95. package/dist/db/cursor.js +3 -4
  96. package/dist/db/dialect.d.ts +2 -2
  97. package/dist/db/dialect.js +3 -3
  98. package/dist/db/distinct-on.d.ts +1 -1
  99. package/dist/db/driver-row-coercion.d.ts +2 -0
  100. package/dist/db/driver-row-coercion.js +186 -0
  101. package/dist/db/driver-value-preparation.d.ts +16 -0
  102. package/dist/db/driver-value-preparation.js +99 -0
  103. package/dist/db/eagerload.d.ts +3 -3
  104. package/dist/db/eagerload.js +21 -12
  105. package/dist/db/entity-field-encryption.d.ts +1 -0
  106. package/dist/db/entity-field-encryption.js +1 -1
  107. package/dist/db/entity-table-meta.d.ts +2 -2
  108. package/dist/db/entity-table-meta.js +105 -112
  109. package/dist/db/event-store-executor-context.d.ts +3 -2
  110. package/dist/db/event-store-executor-context.js +13 -13
  111. package/dist/db/event-store-executor-read-aggregate.d.ts +8 -0
  112. package/dist/db/event-store-executor-read-aggregate.js +112 -0
  113. package/dist/db/event-store-executor-read-filter.d.ts +31 -0
  114. package/dist/db/event-store-executor-read-filter.js +203 -0
  115. package/dist/db/event-store-executor-read-paging.d.ts +14 -0
  116. package/dist/db/event-store-executor-read-paging.js +99 -0
  117. package/dist/db/event-store-executor-read-rows.d.ts +6 -0
  118. package/dist/db/event-store-executor-read-rows.js +32 -0
  119. package/dist/db/event-store-executor-read-search.d.ts +28 -0
  120. package/dist/db/event-store-executor-read-search.js +221 -0
  121. package/dist/db/event-store-executor-read-sort.d.ts +41 -0
  122. package/dist/db/event-store-executor-read-sort.js +130 -0
  123. package/dist/db/event-store-executor-read-where.d.ts +24 -0
  124. package/dist/db/event-store-executor-read-where.js +81 -0
  125. package/dist/db/event-store-executor-read.d.ts +2 -9
  126. package/dist/db/event-store-executor-read.js +50 -704
  127. package/dist/db/event-store-executor-write-steps.d.ts +20 -0
  128. package/dist/db/event-store-executor-write-steps.js +100 -0
  129. package/dist/db/event-store-executor-write.js +401 -510
  130. package/dist/db/index.d.ts +6 -2
  131. package/dist/db/index.js +5 -1
  132. package/dist/db/list-aggregate-labels.d.ts +14 -0
  133. package/dist/db/list-aggregate-labels.js +26 -0
  134. package/dist/db/located-timestamp.d.ts +11 -11
  135. package/dist/db/located-timestamp.js +29 -30
  136. package/dist/db/migrate-generator.d.ts +1 -1
  137. package/dist/db/migrate-generator.js +97 -105
  138. package/dist/db/migrate-runner.js +29 -26
  139. package/dist/db/money.d.ts +6 -6
  140. package/dist/db/money.js +29 -27
  141. package/dist/db/queries/backfill-pii.js +5 -5
  142. package/dist/db/queries/ddl.js +1 -1
  143. package/dist/db/queries/es-ops.d.ts +1 -1
  144. package/dist/db/queries/es-ops.js +1 -1
  145. package/dist/db/queries/event-consumer.js +3 -3
  146. package/dist/db/queries/event-store.js +4 -2
  147. package/dist/db/queries/projection-rebuild.js +3 -3
  148. package/dist/db/queries/raw-sql.d.ts +2 -2
  149. package/dist/db/queries/raw-sql.js +2 -2
  150. package/dist/db/queries/shadow-swap.js +19 -19
  151. package/dist/db/query.d.ts +3 -16
  152. package/dist/db/query.js +8 -761
  153. package/dist/db/rebuild-marker.js +20 -21
  154. package/dist/db/reference-data.js +6 -1
  155. package/dist/db/replay-migration-sql.js +2 -2
  156. package/dist/db/schema-inspection.js +4 -5
  157. package/dist/db/table-builder.d.ts +1 -2
  158. package/dist/db/table-builder.js +137 -190
  159. package/dist/db/table-info.d.ts +10 -0
  160. package/dist/db/table-info.js +45 -0
  161. package/dist/db/temporal-value-guards.d.ts +6 -0
  162. package/dist/db/temporal-value-guards.js +15 -0
  163. package/dist/db/tenant-db-runner.d.ts +1 -0
  164. package/dist/db/tenant-db-runner.js +3 -0
  165. package/dist/db/tenant-db.d.ts +2 -2
  166. package/dist/db/tenant-db.js +48 -11
  167. package/dist/db/where-clause.d.ts +6 -0
  168. package/dist/db/where-clause.js +236 -0
  169. package/dist/derivatives/delete-stored-file.js +6 -2
  170. package/dist/derivatives/derivatives-context.js +1 -1
  171. package/dist/engine/boot-validator/access-roles.js +4 -3
  172. package/dist/engine/boot-validator/api-ext.js +14 -14
  173. package/dist/engine/boot-validator/boot-check.js +1 -1
  174. package/dist/engine/boot-validator/config-deps.js +7 -6
  175. package/dist/engine/boot-validator/detail-screens.js +4 -4
  176. package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
  177. package/dist/engine/boot-validator/entity-handler.js +77 -61
  178. package/dist/engine/boot-validator/form-derivations.js +35 -1
  179. package/dist/engine/boot-validator/gdpr-storage.js +4 -4
  180. package/dist/engine/boot-validator/i18n-keys.js +6 -4
  181. package/dist/engine/boot-validator/index.d.ts +2 -2
  182. package/dist/engine/boot-validator/index.js +22 -20
  183. package/dist/engine/boot-validator/nav.js +5 -5
  184. package/dist/engine/boot-validator/ownership.js +12 -9
  185. package/dist/engine/boot-validator/parent-ref.js +1 -1
  186. package/dist/engine/boot-validator/pii-retention.js +258 -187
  187. package/dist/engine/boot-validator/projection-list-screens.js +7 -7
  188. package/dist/engine/boot-validator/query-refs.js +1 -1
  189. package/dist/engine/boot-validator/screens-actions.d.ts +1 -0
  190. package/dist/engine/boot-validator/screens-actions.js +23 -17
  191. package/dist/engine/boot-validator/screens-dashboard.js +2 -5
  192. package/dist/engine/boot-validator/screens-forms.js +12 -17
  193. package/dist/engine/boot-validator/screens-kind-config-edit.d.ts +3 -0
  194. package/dist/engine/boot-validator/screens-kind-config-edit.js +88 -0
  195. package/dist/engine/boot-validator/screens-kind-entity-edit.d.ts +3 -0
  196. package/dist/engine/boot-validator/screens-kind-entity-edit.js +55 -0
  197. package/dist/engine/boot-validator/screens-kind-entity-list.d.ts +3 -0
  198. package/dist/engine/boot-validator/screens-kind-entity-list.js +228 -0
  199. package/dist/engine/boot-validator/screens-kind-projection-detail.d.ts +3 -0
  200. package/dist/engine/boot-validator/screens-kind-projection-detail.js +235 -0
  201. package/dist/engine/boot-validator/screens-kind-projection-list.d.ts +3 -0
  202. package/dist/engine/boot-validator/screens-kind-projection-list.js +52 -0
  203. package/dist/engine/boot-validator/screens-kind-secret-mint.d.ts +3 -0
  204. package/dist/engine/boot-validator/screens-kind-secret-mint.js +28 -0
  205. package/dist/engine/boot-validator/screens-kind-shared.d.ts +37 -0
  206. package/dist/engine/boot-validator/screens-kind-shared.js +142 -0
  207. package/dist/engine/boot-validator/screens-lists.d.ts +2 -1
  208. package/dist/engine/boot-validator/screens-lists.js +26 -24
  209. package/dist/engine/boot-validator/screens.d.ts +0 -2
  210. package/dist/engine/boot-validator/screens.js +102 -869
  211. package/dist/engine/boot-validator/security-baseline.js +3 -2
  212. package/dist/engine/boot-validator/transfer-graph.js +2 -2
  213. package/dist/engine/boot-validator/workspaces.js +3 -3
  214. package/dist/engine/bridge-stub.js +3 -4
  215. package/dist/engine/build-app-schema.js +130 -170
  216. package/dist/engine/build-config-feature-schema.js +50 -50
  217. package/dist/engine/build-target.js +20 -23
  218. package/dist/engine/codemod/pipeline-codemod.js +20 -13
  219. package/dist/engine/config-helpers.js +4 -4
  220. package/dist/engine/constants.js +2 -2
  221. package/dist/engine/create-app.js +2 -2
  222. package/dist/engine/define-feature.js +21 -13
  223. package/dist/engine/define-handler.js +13 -11
  224. package/dist/engine/define-step.js +1 -1
  225. package/dist/engine/define-workflow.d.ts +5 -5
  226. package/dist/engine/define-workflow.js +6 -6
  227. package/dist/engine/entity-handlers.d.ts +13 -0
  228. package/dist/engine/entity-handlers.js +22 -34
  229. package/dist/engine/entity-list-runtime-options.d.ts +8 -0
  230. package/dist/engine/entity-list-runtime-options.js +18 -0
  231. package/dist/engine/event-def.js +2 -2
  232. package/dist/engine/extension-names.d.ts +16 -17
  233. package/dist/engine/extension-names.js +37 -38
  234. package/dist/engine/extensions/escape-hatch-usage.js +1 -1
  235. package/dist/engine/extensions/tenant-data.js +2 -2
  236. package/dist/engine/extensions/user-data.d.ts +23 -24
  237. package/dist/engine/factories.d.ts +14 -13
  238. package/dist/engine/factories.js +38 -37
  239. package/dist/engine/feature-ast/entity-field-types.js +1 -1
  240. package/dist/engine/feature-ast/extractors/handlers.js +2 -1
  241. package/dist/engine/feature-ast/extractors/hooks.js +1 -1
  242. package/dist/engine/feature-ast/extractors/round2.js +25 -6
  243. package/dist/engine/feature-ast/extractors/shared.d.ts +2 -3
  244. package/dist/engine/feature-ast/extractors/shared.js +2 -3
  245. package/dist/engine/feature-ast/parse.js +13 -13
  246. package/dist/engine/feature-ast/patch.js +4 -182
  247. package/dist/engine/feature-ast/pattern-change-schema.d.ts +3 -0
  248. package/dist/engine/feature-ast/pattern-change-schema.js +2 -1
  249. package/dist/engine/feature-ast/pattern-id-matchers.d.ts +7 -0
  250. package/dist/engine/feature-ast/pattern-id-matchers.js +210 -0
  251. package/dist/engine/feature-ast/patterns.d.ts +11 -2
  252. package/dist/engine/feature-ast/patterns.js +43 -51
  253. package/dist/engine/feature-ast/render.js +50 -89
  254. package/dist/engine/feature-config-events-jobs.js +15 -12
  255. package/dist/engine/feature-entity-handlers.js +12 -5
  256. package/dist/engine/feature-manifest.d.ts +9 -9
  257. package/dist/engine/feature-manifest.js +6 -7
  258. package/dist/engine/feature-ui-extensions.d.ts +3 -3
  259. package/dist/engine/feature-ui-extensions.js +30 -22
  260. package/dist/engine/field-access.js +5 -6
  261. package/dist/engine/form-derivations.js +18 -7
  262. package/dist/engine/handler-field-references.js +1 -1
  263. package/dist/engine/index.d.ts +1 -0
  264. package/dist/engine/index.js +4 -4
  265. package/dist/engine/object-form.d.ts +1 -0
  266. package/dist/engine/object-form.js +14 -4
  267. package/dist/engine/ownership.js +3 -3
  268. package/dist/engine/parse-ref-target.js +5 -7
  269. package/dist/engine/pattern-library/index.js +2 -2
  270. package/dist/engine/pattern-library/library.js +2 -2
  271. package/dist/engine/pattern-library/types.js +3 -3
  272. package/dist/engine/pipeline.js +2 -2
  273. package/dist/engine/project-app-schema-for-roles.d.ts +2 -0
  274. package/dist/engine/project-app-schema-for-roles.js +7 -5
  275. package/dist/engine/projection-helpers.js +9 -9
  276. package/dist/engine/qualified-name.js +2 -2
  277. package/dist/engine/registry-facade.js +1 -1
  278. package/dist/engine/registry-ingest.js +13 -13
  279. package/dist/engine/registry-state.js +17 -19
  280. package/dist/engine/registry-validate.js +12 -12
  281. package/dist/engine/run-in.js +2 -2
  282. package/dist/engine/run-pipeline.js +7 -0
  283. package/dist/engine/schema-builder.js +251 -233
  284. package/dist/engine/screen-filter-ops.js +17 -20
  285. package/dist/engine/screen-helpers.js +4 -3
  286. package/dist/engine/screen-kind.d.ts +7 -0
  287. package/dist/engine/screen-kind.js +1 -0
  288. package/dist/engine/state-machine.d.ts +12 -12
  289. package/dist/engine/state-machine.js +7 -7
  290. package/dist/engine/steps/_resolver-utils.js +4 -3
  291. package/dist/engine/steps/for-each.js +4 -4
  292. package/dist/engine/steps/unsafe-projection-delete.js +1 -1
  293. package/dist/engine/steps/unsafe-projection-upsert.js +4 -4
  294. package/dist/engine/steps/wait-for-event.js +4 -4
  295. package/dist/engine/steps/workflow-system-events.js +3 -3
  296. package/dist/engine/strip-disabled-features-from-app-schema.js +51 -14
  297. package/dist/engine/tier-resolver-extension.d.ts +29 -27
  298. package/dist/engine/tier-resolver-extension.js +26 -26
  299. package/dist/engine/transfer-adjacency.js +2 -2
  300. package/dist/engine/validate-projection-allowlist.js +9 -28
  301. package/dist/engine/walk-step-instances.d.ts +2 -0
  302. package/dist/engine/walk-step-instances.js +38 -0
  303. package/dist/engine/where-rule-lint.js +1 -1
  304. package/dist/engine/write-handler-qn-extract.js +1 -1
  305. package/dist/entrypoint/index.d.ts +4 -1
  306. package/dist/entrypoint/index.js +10 -4
  307. package/dist/env/index.d.ts +7 -6
  308. package/dist/env/index.js +10 -9
  309. package/dist/errors/docs-url.d.ts +1 -1
  310. package/dist/errors/docs-url.js +22 -2
  311. package/dist/errors/transition-details.js +7 -7
  312. package/dist/errors/write-error-info.d.ts +6 -6
  313. package/dist/errors/write-error-info.js +6 -6
  314. package/dist/es-ops/context.d.ts +9 -9
  315. package/dist/es-ops/context.js +29 -29
  316. package/dist/es-ops/index.js +7 -9
  317. package/dist/es-ops/operations-schema.js +4 -10
  318. package/dist/es-ops/runner.d.ts +11 -11
  319. package/dist/es-ops/runner.js +49 -53
  320. package/dist/es-ops/types.d.ts +55 -55
  321. package/dist/es-ops/types.js +11 -11
  322. package/dist/event-store/admin-api.js +7 -8
  323. package/dist/event-store/errors.js +1 -1
  324. package/dist/event-store/event-store.js +12 -6
  325. package/dist/event-store/rebuild-dead-letter.js +1 -1
  326. package/dist/files/bind-pending-file-refs.js +3 -3
  327. package/dist/files/create-file-ref-from-bytes.js +4 -1
  328. package/dist/files/feature.js +9 -10
  329. package/dist/files/file-handle.js +7 -9
  330. package/dist/files/file-ref-entity.js +15 -15
  331. package/dist/files/file-ref-table.js +7 -8
  332. package/dist/files/file-routes.d.ts +7 -0
  333. package/dist/files/file-routes.js +22 -13
  334. package/dist/files/in-memory-provider.js +8 -9
  335. package/dist/files/local-provider.js +18 -25
  336. package/dist/files/provider-resolver.d.ts +7 -7
  337. package/dist/files/resolve-content-type.js +43 -0
  338. package/dist/files/storage-tracking.js +2 -2
  339. package/dist/files/upload-ledger.js +7 -0
  340. package/dist/files/zip-stream.d.ts +12 -19
  341. package/dist/files/zip-stream.js +44 -63
  342. package/dist/i18n/required-surface-keys.d.ts +1 -1
  343. package/dist/i18n/required-surface-keys.js +188 -212
  344. package/dist/internal/stack-support.d.ts +2 -1
  345. package/dist/internal/stack-support.js +1 -1
  346. package/dist/jobs/job-runner-context.d.ts +32 -0
  347. package/dist/jobs/job-runner-context.js +305 -0
  348. package/dist/jobs/job-runner-execution.d.ts +7 -0
  349. package/dist/jobs/job-runner-execution.js +160 -0
  350. package/dist/jobs/job-runner-gates.d.ts +19 -0
  351. package/dist/jobs/job-runner-gates.js +198 -0
  352. package/dist/jobs/job-runner-helpers.d.ts +17 -3
  353. package/dist/jobs/job-runner-helpers.js +69 -4
  354. package/dist/jobs/job-runner-schedulers.d.ts +1 -1
  355. package/dist/jobs/job-runner-schedulers.js +3 -4
  356. package/dist/jobs/job-runner-scope.d.ts +44 -0
  357. package/dist/jobs/job-runner-scope.js +102 -0
  358. package/dist/jobs/job-runner-types.d.ts +12 -0
  359. package/dist/jobs/job-runner.d.ts +0 -1
  360. package/dist/jobs/job-runner.js +57 -669
  361. package/dist/lifecycle/lifecycle.js +3 -5
  362. package/dist/lifecycle/signal-handlers.js +5 -4
  363. package/dist/logging/utils.d.ts +2 -1
  364. package/dist/logging/utils.js +38 -10
  365. package/dist/migrations/index.js +1 -1
  366. package/dist/migrations/kumiko-drift.js +14 -13
  367. package/dist/migrations/pending-rebuilds.d.ts +21 -20
  368. package/dist/migrations/pending-rebuilds.js +27 -27
  369. package/dist/migrations/projection-table-index.d.ts +3 -3
  370. package/dist/migrations/projection-table-index.js +8 -8
  371. package/dist/observability/escape-hatch-report.d.ts +3 -0
  372. package/dist/observability/escape-hatch-report.js +12 -1
  373. package/dist/observability/metric-validator.js +1 -1
  374. package/dist/observability/metrics-handle.js +3 -3
  375. package/dist/pipeline/append-event-core.js +11 -2
  376. package/dist/pipeline/cascade-handler.d.ts +1 -1
  377. package/dist/pipeline/cascade-handler.js +165 -138
  378. package/dist/pipeline/dispatch-batch.js +2 -2
  379. package/dist/pipeline/dispatch-query.js +20 -13
  380. package/dist/pipeline/dispatch-shared.d.ts +14 -3
  381. package/dist/pipeline/dispatch-shared.js +67 -721
  382. package/dist/pipeline/dispatch-stream.js +1 -1
  383. package/dist/pipeline/dispatch-write.js +297 -190
  384. package/dist/pipeline/dispatcher-utils.d.ts +1 -1
  385. package/dist/pipeline/dispatcher-utils.js +1 -1
  386. package/dist/pipeline/event-consumer-state.js +2 -2
  387. package/dist/pipeline/event-dispatcher-admin.js +2 -1
  388. package/dist/pipeline/event-dispatcher-delivery.js +4 -6
  389. package/dist/pipeline/event-dispatcher.js +17 -28
  390. package/dist/pipeline/handler-context-auth.d.ts +9 -0
  391. package/dist/pipeline/handler-context-auth.js +54 -0
  392. package/dist/pipeline/handler-context-config.d.ts +14 -0
  393. package/dist/pipeline/handler-context-config.js +113 -0
  394. package/dist/pipeline/handler-context-db.d.ts +24 -0
  395. package/dist/pipeline/handler-context-db.js +149 -0
  396. package/dist/pipeline/handler-context-dispatch.d.ts +21 -0
  397. package/dist/pipeline/handler-context-dispatch.js +89 -0
  398. package/dist/pipeline/handler-context-events.d.ts +43 -0
  399. package/dist/pipeline/handler-context-events.js +237 -0
  400. package/dist/pipeline/handler-context-notify.d.ts +12 -0
  401. package/dist/pipeline/handler-context-notify.js +55 -0
  402. package/dist/pipeline/handler-context-observability.d.ts +7 -0
  403. package/dist/pipeline/handler-context-observability.js +21 -0
  404. package/dist/pipeline/handler-context-scope.d.ts +48 -0
  405. package/dist/pipeline/handler-context-scope.js +88 -0
  406. package/dist/pipeline/irreversible-operation-gate.js +1 -1
  407. package/dist/pipeline/lifecycle-pipeline.js +9 -9
  408. package/dist/pipeline/member-read-only-transaction.js +1 -3
  409. package/dist/pipeline/msp-rebuild.js +1 -1
  410. package/dist/pipeline/notify-scope.js +12 -1
  411. package/dist/pipeline/pre-handler-gates.js +20 -4
  412. package/dist/pipeline/pre-transaction-reservations.d.ts +1 -0
  413. package/dist/pipeline/pre-transaction-reservations.js +9 -3
  414. package/dist/pipeline/projection-rebuild.js +15 -13
  415. package/dist/pipeline/system-hooks.js +39 -41
  416. package/dist/pipeline/system-identity-switch.js +1 -3
  417. package/dist/pipeline/tenant-timezone-cache.js +1 -1
  418. package/dist/random/generate.d.ts +29 -29
  419. package/dist/random/generate.js +30 -30
  420. package/dist/random/words.js +20 -20
  421. package/dist/rate-limit/middleware.js +9 -5
  422. package/dist/redis/client.js +2 -2
  423. package/dist/redis/pubsub-signal.js +14 -17
  424. package/dist/schema-cli/apply-command.d.ts +2 -0
  425. package/dist/schema-cli/apply-command.js +97 -0
  426. package/dist/schema-cli/baseline-command.d.ts +2 -0
  427. package/dist/schema-cli/baseline-command.js +33 -0
  428. package/dist/schema-cli/generate-command.d.ts +2 -0
  429. package/dist/schema-cli/generate-command.js +90 -0
  430. package/dist/schema-cli/shared.d.ts +39 -0
  431. package/dist/schema-cli/shared.js +95 -0
  432. package/dist/schema-cli/status-command.d.ts +2 -0
  433. package/dist/schema-cli/status-command.js +73 -0
  434. package/dist/schema-cli/validate-boot-input.d.ts +5 -0
  435. package/dist/schema-cli/validate-boot-input.js +130 -0
  436. package/dist/schema-cli/validate-command.d.ts +2 -0
  437. package/dist/schema-cli/validate-command.js +130 -0
  438. package/dist/schema-cli.d.ts +2 -18
  439. package/dist/schema-cli.js +47 -510
  440. package/dist/search/index.js +4 -4
  441. package/dist/search/meilisearch-adapter.js +2 -1
  442. package/dist/search/purge-subject.js +4 -5
  443. package/dist/search/reindex-entity.js +6 -9
  444. package/dist/time/boot-tz-warning.d.ts +2 -2
  445. package/dist/time/boot-tz-warning.js +8 -8
  446. package/dist/time/iana.js +10 -10
  447. package/dist/time/index.js +9 -9
  448. package/dist/time/legacy-date.d.ts +2 -2
  449. package/dist/time/legacy-date.js +6 -8
  450. package/dist/time/polyfill.js +1 -1
  451. package/dist/time/tz-context.d.ts +1 -1
  452. package/dist/time/tz-context.js +1 -1
  453. package/dist/ui-types/app-schema.js +7 -7
  454. package/dist/ui-types/list-row-meta.js +4 -4
  455. package/dist/upgrade-cli.js +159 -130
  456. package/dist/utils/compare.js +1 -1
  457. package/package.json +5 -5
  458. package/src/changes.json +358 -0
  459. package/src/scripts/codemod/crypto-shredding-testing-move.ts +2 -2
  460. package/src/scripts/codemod/feature-layout.ts +10 -2
  461. package/src/scripts/codemod/migrate-cross-tenant.ts +1 -1
  462. package/src/scripts/codemod/migrate-open-to-all.ts +1 -1
  463. package/src/scripts/codemod/object-form.ts +41 -4
  464. package/src/scripts/codemod/package-merge.ts +58 -8
  465. package/src/scripts/codemod/pii-personal-migration.ts +192 -153
  466. package/src/scripts/codemod/testing-injection.ts +122 -42
  467. package/src/scripts/codemod/testing-move.ts +29 -7
  468. package/src/scripts/codemod/unprocessable-error-details-reason.ts +2 -2
@@ -22,151 +22,178 @@ export function createCascadeDeleteHook(registry, tables) {
22
22
  return;
23
23
  }
24
24
  const writeDb = isBoundTenantDb(db) ? db : undefined;
25
- const cascadeUser = createSystemUser(writeDb?.tenantId ?? payload.event.tenantId);
26
- const executors = new Map();
27
- const executorFor = (name, entity) => {
28
- const cached = executors.get(name);
29
- if (cached)
30
- return cached;
31
- const table = tables.get(name);
32
- if (!table) {
33
- throw new InternalError({ message: `cascadeDelete: no table registered for "${name}"` });
34
- }
35
- const created = createEventStoreExecutor(table, entity, { entityName: name });
36
- executors.set(name, created);
37
- return created;
38
- };
39
- const requireWriteDb = () => {
40
- if (!writeDb) {
41
- throw new InternalError({
42
- message: "cascadeDelete: a TenantDb is required to write children",
43
- });
44
- }
45
- return writeDb;
46
- };
47
- const unwrap = (result) => {
48
- if (!result.isSuccess)
49
- throw reraiseAsKumikoError(result.error);
50
- };
51
- const childIds = async (targetTable, where, entity) => {
52
- // A soft-deleted child is already invisible; deleting it again would fail.
53
- const rows = await selectMany(db, targetTable, {
54
- ...where,
55
- ...(entity?.softDelete === true && { isDeleted: false }),
56
- });
57
- return rows.map((row) => row.id);
25
+ const run = {
26
+ registry,
27
+ db,
28
+ entityName,
29
+ parentId: payload.id,
30
+ lockGuardBypassed: payload.lockGuardBypassed === true,
31
+ cascadeUser: createSystemUser(writeDb?.tenantId ?? payload.event.tenantId),
32
+ executorFor: createExecutorCache(tables),
33
+ writeDb,
58
34
  };
59
35
  // Cascade applies to outgoing hasMany / manyToMany relations only —
60
36
  // the parent side of the link is where `onDelete` lives (see
61
37
  // relations.ts). Incoming belongsTo edges are covered because their
62
38
  // counterpart hasMany declares the strategy.
63
- const outgoing = registry.getRelations(entityName);
64
39
  // Outgoing: e.g. department.users (hasMany target: user) — users have FK to department
65
- for (const [, relation] of Object.entries(outgoing)) {
66
- // Only hasMany / manyToMany carry cascade semantics. belongsTo points
67
- // at a parent; the parent's onDelete decides what happens to this node.
68
- if (relation.type !== "hasMany" && relation.type !== "manyToMany")
69
- continue;
70
- const strategy = relation.onDelete ?? OnDeleteStrategies.nothing;
71
- if (strategy === OnDeleteStrategies.nothing)
72
- continue;
73
- if (relation.type === "hasMany" && relation.foreignKey) {
74
- const targetTable = tables.get(relation.target);
75
- if (!targetTable)
76
- continue;
77
- const childEntity = registry.getEntity(relation.target);
78
- if (!childEntity &&
79
- (strategy === OnDeleteStrategies.cascade || strategy === OnDeleteStrategies.setNull)) {
80
- throw new InternalError({
81
- message: `cascadeDelete: child "${relation.target}" is not an entity; changing its rows needs an executor`,
82
- });
83
- }
84
- if (strategy === OnDeleteStrategies.restrict) {
85
- const row = await fetchOne(db, targetTable, { [relation.foreignKey]: payload.id });
86
- if (row) {
87
- throw new ConflictError({
88
- message: `${relation.target} has records referencing ${entityName}#${payload.id}`,
89
- i18nKey: "errors.deleteRestricted",
90
- details: {
91
- reason: FrameworkReasons.deleteRestricted,
92
- blockingEntity: relation.target,
93
- entity: entityName,
94
- entityId: payload.id,
95
- },
96
- });
97
- }
98
- }
99
- // The child writes below skip the executor's lock guard (decided here once), so a locked
100
- // child must block the parent delete instead of changing with it. The error names
101
- // the parent only: the caller may delete the parent without reading the child.
102
- if (strategy === OnDeleteStrategies.cascade || strategy === OnDeleteStrategies.setNull) {
103
- const childLock = childEntity?.lockedWhen;
104
- if (childLock !== undefined && payload.lockGuardBypassed !== true) {
105
- // A soft-deleted child is invisible to the caller and must not block.
106
- const children = await selectMany(db, targetTable, {
107
- [relation.foreignKey]: payload.id,
108
- ...(childEntity?.softDelete === true && { isDeleted: false }),
109
- });
110
- if (children.some((child) => isRecordLocked(childLock, child))) {
111
- throw new RecordLockedError({ entityName, entityId: payload.id });
112
- }
113
- }
114
- }
115
- if (strategy === OnDeleteStrategies.cascade && childEntity) {
116
- const executor = executorFor(relation.target, childEntity);
117
- for (const id of await childIds(targetTable, { [relation.foreignKey]: payload.id }, childEntity)) {
118
- unwrap(await executor.delete({ id }, cascadeUser, requireWriteDb(), CASCADE_CHILD_WRITE_OPTIONS));
119
- }
120
- }
121
- if (strategy === OnDeleteStrategies.setNull && childEntity) {
122
- const executor = executorFor(relation.target, childEntity);
123
- const rows = await selectMany(db, targetTable, {
124
- [relation.foreignKey]: payload.id,
125
- });
126
- for (const row of rows) {
127
- unwrap(await executor.update({ id: row.id, changes: { [relation.foreignKey]: null } }, cascadeUser, requireWriteDb(), {
128
- ...CASCADE_CHILD_WRITE_OPTIONS,
129
- skipOptimisticLock: true,
130
- includeDeleted: true,
131
- }));
132
- }
133
- }
134
- }
135
- if (relation.type === "manyToMany" && relation.through) {
136
- const throughTable = tables.get(relation.through.table);
137
- if (!throughTable)
138
- continue;
139
- // sourceKey points at the owner side (the entity being deleted).
140
- const sourceKey = relation.through.sourceKey;
141
- if (strategy === OnDeleteStrategies.restrict) {
142
- const row = await fetchOne(db, throughTable, { [sourceKey]: payload.id });
143
- if (row) {
144
- throw new ConflictError({
145
- message: `${relation.through.table} has records referencing ${entityName}#${payload.id}`,
146
- i18nKey: "errors.deleteRestricted",
147
- details: {
148
- reason: FrameworkReasons.deleteRestricted,
149
- blockingEntity: relation.through.table,
150
- entity: entityName,
151
- entityId: payload.id,
152
- },
153
- });
154
- }
155
- }
156
- if (strategy === OnDeleteStrategies.cascade) {
157
- const throughEntity = registry.getEntity(relation.through.table);
158
- if (!throughEntity) {
159
- throw new InternalError({
160
- message: `cascadeDelete: through table "${relation.through.table}" is not an entity; deleting its rows needs an executor`,
161
- });
162
- }
163
- const executor = executorFor(relation.through.table, throughEntity);
164
- for (const id of await childIds(throughTable, { [sourceKey]: payload.id }, throughEntity)) {
165
- unwrap(await executor.delete({ id }, cascadeUser, requireWriteDb(), CASCADE_CHILD_WRITE_OPTIONS));
166
- }
167
- }
168
- }
40
+ for (const relation of Object.values(registry.getRelations(entityName))) {
41
+ await applyOnDelete(run, tables, relation);
169
42
  }
170
43
  },
171
44
  };
172
45
  }
46
+ function createExecutorCache(tables) {
47
+ const executors = new Map();
48
+ return (name, entity) => {
49
+ const cached = executors.get(name);
50
+ if (cached)
51
+ return cached;
52
+ const table = tables.get(name);
53
+ if (!table) {
54
+ throw new InternalError({ message: `cascadeDelete: no table registered for "${name}"` });
55
+ }
56
+ const created = createEventStoreExecutor(table, entity, { entityName: name });
57
+ executors.set(name, created);
58
+ return created;
59
+ };
60
+ }
61
+ function requireCascadeWriteDb(writeDb) {
62
+ if (!writeDb) {
63
+ throw new InternalError({
64
+ message: "cascadeDelete: a TenantDb is required to write children",
65
+ });
66
+ }
67
+ return writeDb;
68
+ }
69
+ function unwrap(result) {
70
+ if (!result.isSuccess)
71
+ throw reraiseAsKumikoError(result.error);
72
+ }
73
+ async function childIds(run, targetTable, where, entity) {
74
+ // A soft-deleted child is already invisible; deleting it again would fail.
75
+ const rows = await selectMany(run.db, targetTable, { ...where, ...(entity.softDelete === true && { isDeleted: false }) }, { fields: ["id"] });
76
+ return rows.map((row) => row.id);
77
+ }
78
+ // Only hasMany / manyToMany carry cascade semantics. belongsTo points
79
+ // at a parent; the parent's onDelete decides what happens to this node.
80
+ async function applyOnDelete(run, tables, relation) {
81
+ if (relation.type === "hasMany" && relation.foreignKey) {
82
+ const table = tables.get(relation.target);
83
+ if (table) {
84
+ const link = { target: relation.target, foreignKey: relation.foreignKey, table };
85
+ await HAS_MANY_ON_DELETE[onDeleteStrategyOf(relation)](run, link);
86
+ }
87
+ }
88
+ if (relation.type === "manyToMany" && relation.through) {
89
+ const table = tables.get(relation.through.table);
90
+ if (table) {
91
+ // sourceKey points at the owner side (the entity being deleted).
92
+ const link = {
93
+ throughTable: relation.through.table,
94
+ sourceKey: relation.through.sourceKey,
95
+ table,
96
+ };
97
+ await THROUGH_ON_DELETE[onDeleteStrategyOf(relation)](run, link);
98
+ }
99
+ }
100
+ }
101
+ function onDeleteStrategyOf(relation) {
102
+ return relation.onDelete ?? OnDeleteStrategies.nothing;
103
+ }
104
+ async function leaveChildrenUnchanged() { }
105
+ const HAS_MANY_ON_DELETE = {
106
+ nothing: leaveChildrenUnchanged,
107
+ restrict: rejectReferencedParent,
108
+ cascade: deleteHasManyChildren,
109
+ setNull: detachHasManyChildren,
110
+ };
111
+ // A link row has no nullable side: setNull on a manyToMany leaves the through rows in place.
112
+ const THROUGH_ON_DELETE = {
113
+ nothing: leaveChildrenUnchanged,
114
+ restrict: rejectReferencedThroughRows,
115
+ cascade: deleteThroughRows,
116
+ setNull: leaveChildrenUnchanged,
117
+ };
118
+ async function rejectReferencedParent(run, link) {
119
+ const row = await fetchOne(run.db, link.table, { [link.foreignKey]: run.parentId });
120
+ if (row)
121
+ throw deleteRestrictedError(run, link.target);
122
+ }
123
+ async function rejectReferencedThroughRows(run, link) {
124
+ const row = await fetchOne(run.db, link.table, { [link.sourceKey]: run.parentId });
125
+ if (row)
126
+ throw deleteRestrictedError(run, link.throughTable);
127
+ }
128
+ function deleteRestrictedError(run, blockingEntity) {
129
+ return new ConflictError({
130
+ message: `${blockingEntity} has records referencing ${run.entityName}#${run.parentId}`,
131
+ i18nKey: "errors.deleteRestricted",
132
+ details: {
133
+ reason: FrameworkReasons.deleteRestricted,
134
+ blockingEntity,
135
+ entity: run.entityName,
136
+ entityId: run.parentId,
137
+ },
138
+ });
139
+ }
140
+ // ponytail: one executor call per child inside the parent's transaction, batch when child counts need it
141
+ async function deleteHasManyChildren(run, link) {
142
+ const childEntity = requireChildEntity(run, link);
143
+ await rejectLockedChildren(run, link, childEntity);
144
+ const executor = run.executorFor(link.target, childEntity);
145
+ for (const id of await childIds(run, link.table, { [link.foreignKey]: run.parentId }, childEntity)) {
146
+ unwrap(await executor.delete({ id }, run.cascadeUser, requireCascadeWriteDb(run.writeDb), CASCADE_CHILD_WRITE_OPTIONS));
147
+ }
148
+ }
149
+ async function detachHasManyChildren(run, link) {
150
+ const childEntity = requireChildEntity(run, link);
151
+ await rejectLockedChildren(run, link, childEntity);
152
+ const executor = run.executorFor(link.target, childEntity);
153
+ const rows = await selectMany(run.db, link.table, { [link.foreignKey]: run.parentId }, { fields: ["id"] });
154
+ for (const row of rows) {
155
+ unwrap(await executor.update({ id: row.id, changes: { [link.foreignKey]: null } }, run.cascadeUser, requireCascadeWriteDb(run.writeDb), {
156
+ ...CASCADE_CHILD_WRITE_OPTIONS,
157
+ skipOptimisticLock: true,
158
+ includeDeleted: true,
159
+ }));
160
+ }
161
+ }
162
+ function requireChildEntity(run, link) {
163
+ const childEntity = run.registry.getEntity(link.target);
164
+ if (!childEntity) {
165
+ throw new InternalError({
166
+ message: `cascadeDelete: child "${link.target}" is not an entity; changing its rows needs an executor`,
167
+ });
168
+ }
169
+ return childEntity;
170
+ }
171
+ // The child writes skip the executor's lock guard (decided here once), so a locked
172
+ // child must block the parent delete instead of changing with it. The error names
173
+ // the parent only: the caller may delete the parent without reading the child.
174
+ async function rejectLockedChildren(run, link, childEntity) {
175
+ const childLock = childEntity.lockedWhen;
176
+ // skip: an unlockable child or an already bypassed parent delete has nothing to block
177
+ if (childLock === undefined || run.lockGuardBypassed)
178
+ return;
179
+ // A soft-deleted child is invisible to the caller and must not block.
180
+ const children = await selectMany(run.db, link.table, {
181
+ [link.foreignKey]: run.parentId,
182
+ ...(childEntity.softDelete === true && { isDeleted: false }),
183
+ });
184
+ if (children.some((child) => isRecordLocked(childLock, child))) {
185
+ throw new RecordLockedError({ entityName: run.entityName, entityId: run.parentId });
186
+ }
187
+ }
188
+ async function deleteThroughRows(run, link) {
189
+ const throughEntity = run.registry.getEntity(link.throughTable);
190
+ if (!throughEntity) {
191
+ throw new InternalError({
192
+ message: `cascadeDelete: through table "${link.throughTable}" is not an entity; deleting its rows needs an executor`,
193
+ });
194
+ }
195
+ const executor = run.executorFor(link.throughTable, throughEntity);
196
+ for (const id of await childIds(run, link.table, { [link.sourceKey]: run.parentId }, throughEntity)) {
197
+ unwrap(await executor.delete({ id }, run.cascadeUser, requireCascadeWriteDb(run.writeDb), CASCADE_CHILD_WRITE_OPTIONS));
198
+ }
199
+ }
@@ -3,7 +3,7 @@ import { transaction } from "../db/query.js";
3
3
  import { InternalError, toWriteErrorInfo, UnprocessableError, writeFailure, } from "../errors/index.js";
4
4
  import { createFallbackLogger } from "../logging/utils.js";
5
5
  import { parseJsonSafe } from "../utils/safe-json.js";
6
- import { resolveDbSource } from "./dispatch-shared.js";
6
+ import { resolveAppPoolDb } from "./dispatch-shared.js";
7
7
  import { executeNestedWrite, executeWrite } from "./dispatch-write.js";
8
8
  import { BatchRollback, isLifecycleResult, wrapToKumiko, } from "./dispatcher-utils.js";
9
9
  import { reserveBeforeTransaction, runAsCommand, } from "./pre-transaction-reservations.js";
@@ -209,7 +209,7 @@ async function runBatchBody(ctx, commands, user, requestId, inheritedOrigin) {
209
209
  }
210
210
  // batch() opens its own outer transaction — needs the top-level
211
211
  // connection's `.begin()` (TransactionSql exposes only `.savepoint()`).
212
- const db = resolveDbSource(ctx, undefined);
212
+ const db = resolveAppPoolDb(ctx);
213
213
  if (!db || runsWithoutTransaction) {
214
214
  // Without a DB connection (or for a perCommand handler, whose writes open their own transactions)
215
215
  // there is no transaction to open. Fall back to
@@ -1,4 +1,5 @@
1
- import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
1
+ import { isListAggregateResult } from "@cosmicdrift/kumiko-types/list-aggregate-types";
2
+ import { isPlainObject, propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
2
3
  import { hasAccess } from "../engine/access.js";
3
4
  import { tenantOverrideDenied } from "../engine/cross-tenant.js";
4
5
  import { filterReadFields } from "../engine/field-access.js";
@@ -58,18 +59,22 @@ async function executeQueryInner(ctx, type, payload, user, origin, tx, outerNoti
58
59
  }
59
60
  async function runQueryHandler(ctx, type, handler, payload, includeDeleted, user, origin, tx, outerNotifySink) {
60
61
  const { registry } = ctx;
61
- const handlerContext = await buildHandlerContext(ctx, type, user, origin, tx, undefined, includeDeleted, undefined, outerNotifySink);
62
+ const handlerContext = await buildHandlerContext(ctx, type, user, origin, {
63
+ tx,
64
+ includeDeleted,
65
+ outerNotifySink,
66
+ });
62
67
  let result = await handler.handler({ type, payload, user }, handlerContext);
63
- // postQuery-Hooks: fire BEFORE field-access-filter so hooks see raw data
64
- // and can merge custom-fields/computed-counts/tags/etc. Each hook is
65
- // responsible for its own field-access on values it adds (the filter
66
- // below only knows the entity's stammfields).
68
+ // postQuery hooks run BEFORE field-access filtering so they see raw data and can
69
+ // merge custom fields, computed counts, tags, and similar values. Each hook is
70
+ // responsible for applying field access to values it adds; the filter below
71
+ // only knows the entity's standard fields.
67
72
  //
68
- // Two firing-pfade kombiniert in dieser Reihenfolge:
73
+ // Two hook scopes run in this order:
69
74
  // 1. Handler-keyed hooks via r.hook({ type: "postQuery", target: "ns:query:list", handler })
70
- // — feuern nur für genau diesen handler
75
+ // — run only for that handler
71
76
  // 2. Entity-keyed hooks via r.entityHook("postQuery", "property", fn)
72
- // — feuern für ALLE query-handlers des entity
77
+ // — run for all query handlers of the entity
73
78
  const entityName = registry.getHandlerEntity(type);
74
79
  // Handler-keyed postQuery hooks fire for any query (incl. entity-less
75
80
  // standalone queries like "ns:dashboard"). Entity-keyed hooks only apply
@@ -78,7 +83,9 @@ async function runQueryHandler(ctx, type, handler, payload, includeDeleted, user
78
83
  const handlerHooks = registry.getPostQueryHooks(type);
79
84
  const entityHooks = entityName ? registry.getEntityPostQueryHooks(entityName) : [];
80
85
  const postQueryHooks = [...handlerHooks, ...entityHooks];
81
- if (postQueryHooks.length > 0 && result && typeof result === "object") {
86
+ // Aggregate groups are not rows: their values are already read-gated and the hooks are row-shaped.
87
+ const isAggregate = isListAggregateResult(result);
88
+ if (!isAggregate && postQueryHooks.length > 0 && result && typeof result === "object") {
82
89
  if (Array.isArray(result)) {
83
90
  let rows = result; // @cast-boundary engine-payload
84
91
  for (const hook of postQueryHooks) {
@@ -87,7 +94,7 @@ async function runQueryHandler(ctx, type, handler, payload, includeDeleted, user
87
94
  }
88
95
  result = rows;
89
96
  }
90
- else if (Array.isArray(result.rows)) {
97
+ else if (isPlainObject(result) && Array.isArray(result["rows"])) {
91
98
  // @cast-boundary engine-payload
92
99
  const r = result;
93
100
  let rows = r.rows;
@@ -115,13 +122,13 @@ async function runQueryHandler(ctx, type, handler, payload, includeDeleted, user
115
122
  }
116
123
  // Field-level read filter — only applies to entity-bound results.
117
124
  const entity = entityName ? registry.getEntity(entityName) : undefined;
118
- if (entity && result && typeof result === "object") {
125
+ if (entity && !isAggregate && result && typeof result === "object") {
119
126
  if (Array.isArray(result)) {
120
127
  result = result.map((row) => filterReadFields(entity, row, user));
121
128
  }
122
129
  else {
123
130
  const resultAsDbRow = result; // @cast-boundary engine-payload
124
- if (Array.isArray(resultAsDbRow.rows)) {
131
+ if (Array.isArray(resultAsDbRow["rows"])) {
125
132
  // generic handler-result shape narrow
126
133
  const r = result; // @cast-boundary engine-payload
127
134
  result = {
@@ -2,12 +2,13 @@ import type { CacheSyncBus } from "@cosmicdrift/kumiko-types/cache-sync-types";
2
2
  import type { SseBroker } from "../api/sse-broker.ts";
3
3
  import type { DbConnection, DbTx } from "../db/connection.ts";
4
4
  import type { buildEntityTable } from "../db/table-builder.ts";
5
+ import { type TenantDb } from "../db/tenant-db.ts";
5
6
  import type { defineTransitions } from "../engine/state-machine.ts";
6
7
  import type { EffectiveFeaturesResolver } from "../engine/tier-resolver-extension.ts";
7
8
  import type { TenantId } from "../engine/types/identifiers.ts";
8
9
  import type { AppContext, HandlerContext, JobRunnerRef, PayloadRateLimitOption, RateLimitDeclaration, Registry, SessionUser, WriteResult } from "../engine/types/index.ts";
9
10
  import { InternalError, type WriteErrorInfo } from "../errors/index.ts";
10
- import { type EscapeHatchReportWindow, type getFallbackMeter, getFallbackTracer } from "../observability/index.ts";
11
+ import { type EscapeHatchReportWindow, type getFallbackMeter, type getFallbackTracer } from "../observability/index.ts";
11
12
  import { type AfterCommitHook } from "./dispatcher-utils.ts";
12
13
  import type { IdempotencyGuard } from "./idempotency.ts";
13
14
  import type { LifecycleHooks } from "./lifecycle-pipeline.ts";
@@ -52,18 +53,28 @@ export type DispatchContext = {
52
53
  };
53
54
  export declare const DEFAULT_MEMBER_READ_TIMEOUT_MS = 10000;
54
55
  export declare function resolveDbSource(ctx: DispatchContext, tx: DbTx | undefined): DbConnection | DbTx | undefined;
56
+ export declare function resolveAppPoolDb(ctx: DispatchContext): DbConnection | undefined;
57
+ export declare function createSystemScopedDbGuard(handlerType: string, fieldName: "db" | "dbOutsideTransaction", hint: string): TenantDb;
55
58
  export declare function isMemberResolutionPrincipal(user: SessionUser): boolean;
56
59
  export declare function applyMemberResolutionReadOnly(handlerContext: HandlerContext): HandlerContext;
57
60
  export declare function perCommandNestedDenied(targetType: string): InternalError;
58
61
  export declare function denyTransactionBoundSurfaces(handlerContext: HandlerContext): HandlerContext;
59
62
  export declare function applyBeforeTransactionReadOnly(handlerContext: HandlerContext): HandlerContext;
60
63
  export type HandlerTransactionScope = "inTransaction" | "withoutTransaction" | "postCommit";
61
- export declare function buildHandlerContext(ctx: DispatchContext, type: string, user: SessionUser, origin: WriteOrigin, tx?: DbTx, afterCommitHooks?: AfterCommitHook[], includeDeleted?: boolean, transactionScope?: HandlerTransactionScope, outerNotifySink?: AfterCommitHook[], readOnlyForTenant?: boolean): Promise<HandlerContext>;
64
+ export type BuildHandlerContextOptions = {
65
+ tx?: DbTx;
66
+ afterCommitHooks?: AfterCommitHook[];
67
+ includeDeleted?: boolean;
68
+ transactionScope?: HandlerTransactionScope;
69
+ outerNotifySink?: AfterCommitHook[];
70
+ readOnlyForTenant?: boolean;
71
+ };
72
+ export declare function buildHandlerContext(ctx: DispatchContext, type: string, user: SessionUser, origin: WriteOrigin, options?: BuildHandlerContextOptions): Promise<HandlerContext>;
62
73
  export declare function createHandlerBoundDigest(digest: (value: string) => string, handlerName: string): (value: string) => string;
63
74
  export declare function runHandlerInstrumented<T>(ctx: DispatchContext, type: string, operation: "query" | "write" | "stream", user: SessionUser, inner: () => Promise<T>): Promise<T>;
64
75
  export declare function runStreamInstrumented<T>(ctx: DispatchContext, type: string, user: SessionUser, inner: () => AsyncGenerator<T>): AsyncGenerator<T>;
65
76
  export declare function checkFeatureEnabled(ctx: DispatchContext, qualifiedHandler: string, tenantId: TenantId): Promise<import("../errors/index.ts").FeatureDisabledError | undefined>;
66
77
  export declare function ensureFeatureEnabled(ctx: DispatchContext, qualifiedHandler: string, tenantId: TenantId): Promise<void>;
67
- export declare function enforceRateLimit(ctx: DispatchContext, rateLimit: RateLimitDeclaration | undefined, handlerName: string, user: SessionUser, isSystemScope: boolean): Promise<void>;
78
+ export declare function enforceRateLimit(ctx: DispatchContext, rateLimit: RateLimitDeclaration | undefined, handlerName: string, user: SessionUser, isSystemScope: boolean, costOverride?: number): Promise<void>;
68
79
  export declare function enforcePayloadRateLimits(ctx: DispatchContext, limits: readonly PayloadRateLimitOption[] | undefined, handlerName: string, user: SessionUser, data: unknown): Promise<void>;
69
80
  export declare function resolveAuthClaimsFn(ctx: DispatchContext, user: SessionUser): Promise<Record<string, unknown>>;