@cosmicdrift/kumiko-framework 0.389.0 → 0.390.1

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 (617) hide show
  1. package/AGENTS.md +9 -4
  2. package/README.md +1 -1
  3. package/agents/examples/apex-surface-auth.md +2 -3
  4. package/agents/examples/api-worker-split.md +9 -2
  5. package/agents/examples/auth-claims.md +1 -1
  6. package/agents/examples/cross-feature-events.md +12 -12
  7. package/agents/examples/crypto-shredding-hr.md +2 -2
  8. package/agents/examples/encrypted-tenant-config.md +1 -1
  9. package/agents/examples/error-contract.md +12 -7
  10. package/agents/examples/event-sourcing.md +29 -29
  11. package/agents/examples/files-post-processing.md +10 -8
  12. package/agents/examples/inbound-mail-triage.md +1 -0
  13. package/agents/examples/lane-routing.md +2 -1
  14. package/agents/examples/legal-pages.md +2 -2
  15. package/agents/examples/lifecycle-hooks.md +3 -3
  16. package/agents/examples/managed-config.md +4 -4
  17. package/agents/examples/managed-pages.md +1 -1
  18. package/agents/examples/relations.md +19 -4
  19. package/agents/examples/search.md +4 -4
  20. package/agents/examples/secrets-demo.md +9 -6
  21. package/agents/examples/state-machine.md +2 -2
  22. package/agents/examples/tenant-settings.md +1 -1
  23. package/agents/examples/user-data-rights.md +3 -6
  24. package/agents/examples/workflow-engine.md +53 -47
  25. package/agents/examples.md +4 -4
  26. package/agents/features.md +1 -1
  27. package/dist/api/auth-middleware.js +1 -1
  28. package/dist/api/auth-routes-invite.js +2 -1
  29. package/dist/api/entity-read-access.d.ts +3 -1
  30. package/dist/api/export/export-columns.d.ts +3 -1
  31. package/dist/api/export/export-route.js +3 -2
  32. package/dist/api/export/export-screen.d.ts +2 -1
  33. package/dist/api/extra-route.d.ts +3 -1
  34. package/dist/api/index.d.ts +23 -27
  35. package/dist/api/index.js +13 -15
  36. package/dist/api/jwt.js +1 -1
  37. package/dist/api/observability-middleware.d.ts +1 -1
  38. package/dist/api/observability-middleware.js +2 -1
  39. package/dist/api/request-context-internals.d.ts +31 -0
  40. package/dist/api/request-context-internals.js +11 -0
  41. package/dist/api/request-context-scopes.d.ts +14 -0
  42. package/dist/api/request-context-scopes.js +69 -0
  43. package/dist/api/request-context.d.ts +1 -40
  44. package/dist/api/request-context.js +22 -51
  45. package/dist/api/request-id-middleware.d.ts +1 -1
  46. package/dist/api/route-registrars.d.ts +2 -1
  47. package/dist/api/route-registrars.js +1 -1
  48. package/dist/api/routes.js +7 -3
  49. package/dist/api/server-boot-checks.d.ts +1 -1
  50. package/dist/api/server-boot-checks.js +2 -2
  51. package/dist/api/server-event-dispatcher.d.ts +6 -1
  52. package/dist/api/server-event-dispatcher.js +3 -4
  53. package/dist/api/server-extra-routes.d.ts +1 -1
  54. package/dist/api/server-extra-routes.js +2 -1
  55. package/dist/api/server-middleware.js +3 -1
  56. package/dist/api/server-routes.d.ts +2 -1
  57. package/dist/api/server-wiring.js +6 -4
  58. package/dist/api/server.d.ts +2 -1
  59. package/dist/api/sse-route.d.ts +2 -1
  60. package/dist/api/tenant-scoped-secrets.d.ts +2 -0
  61. package/dist/api/tenant-scoped-secrets.js +13 -0
  62. package/dist/api/websocket-route.d.ts +1 -1
  63. package/dist/auth/index.d.ts +0 -1
  64. package/dist/auth/roles.d.ts +0 -5
  65. package/dist/compliance/duration-spec.d.ts +0 -1
  66. package/dist/compliance/duration-spec.js +0 -5
  67. package/dist/compliance/index.d.ts +4 -4
  68. package/dist/compliance/index.js +2 -2
  69. package/dist/compliance/profiles.d.ts +0 -15
  70. package/dist/compliance/profiles.js +0 -23
  71. package/dist/config/index.d.ts +10 -10
  72. package/dist/config/index.js +13 -1
  73. package/dist/config/internals.d.ts +9 -0
  74. package/dist/config/internals.js +1 -0
  75. package/dist/consumer-cli-internals.d.ts +4 -0
  76. package/dist/consumer-cli-internals.js +1 -0
  77. package/dist/consumer-cli.d.ts +1 -4
  78. package/dist/consumer-cli.js +5 -1
  79. package/dist/crypto/event-pii.js +1 -1
  80. package/dist/crypto/index.d.ts +8 -9
  81. package/dist/crypto/index.js +7 -8
  82. package/dist/crypto/pg-kms-adapter.js +6 -1
  83. package/dist/db/apply-entity-event.d.ts +1 -1
  84. package/dist/db/apply-entity-event.js +15 -47
  85. package/dist/db/blind-index-cleanup.js +91 -77
  86. package/dist/db/collect-table-metas.js +1 -1
  87. package/dist/db/compound-types.d.ts +1 -1
  88. package/dist/db/compound-types.js +0 -7
  89. package/dist/db/config-seed.d.ts +2 -1
  90. package/dist/db/config-seed.js +6 -1
  91. package/dist/db/connection.d.ts +3 -1
  92. package/dist/db/connection.js +15 -5
  93. package/dist/db/cross-tenant-reads.d.ts +1 -1
  94. package/dist/db/cross-tenant-reads.js +1 -1
  95. package/dist/db/dialect.d.ts +0 -5
  96. package/dist/db/dialect.js +0 -3
  97. package/dist/db/driver-value-preparation.js +23 -2
  98. package/dist/db/eagerload.d.ts +2 -1
  99. package/dist/db/entity-field-encryption.d.ts +1 -1
  100. package/dist/db/entity-table-meta.d.ts +1 -1
  101. package/dist/db/event-store-executor-context.d.ts +3 -1
  102. package/dist/db/event-store-executor-context.js +4 -3
  103. package/dist/db/event-store-executor-read-filter.d.ts +1 -1
  104. package/dist/db/event-store-executor-read-rows.d.ts +2 -1
  105. package/dist/db/event-store-executor-read-search.d.ts +2 -1
  106. package/dist/db/event-store-executor-read-sort.d.ts +2 -1
  107. package/dist/db/event-store-executor-read.js +2 -2
  108. package/dist/db/event-store-executor-write-atomic.js +8 -7
  109. package/dist/db/event-store-executor-write-steps.d.ts +3 -2
  110. package/dist/db/event-store-executor-write.js +7 -4
  111. package/dist/db/event-store-executor.d.ts +1 -1
  112. package/dist/db/executor-managed-tables.d.ts +7 -1
  113. package/dist/db/executor-managed-tables.js +11 -2
  114. package/dist/db/index.d.ts +29 -28
  115. package/dist/db/index.js +22 -20
  116. package/dist/db/known-table-names.d.ts +6 -0
  117. package/dist/db/known-table-names.js +21 -0
  118. package/dist/db/list-aggregate-labels.d.ts +1 -1
  119. package/dist/db/located-timestamp.d.ts +1 -1
  120. package/dist/db/located-timestamp.js +0 -5
  121. package/dist/db/migrate-generator.d.ts +17 -1
  122. package/dist/db/migrate-generator.js +50 -20
  123. package/dist/db/migrate-primary-key.d.ts +12 -0
  124. package/dist/db/migrate-primary-key.js +45 -0
  125. package/dist/db/migrate-rls-diff.d.ts +20 -0
  126. package/dist/db/migrate-rls-diff.js +61 -0
  127. package/dist/db/migrate-runner.js +4 -0
  128. package/dist/db/money.d.ts +1 -1
  129. package/dist/db/money.js +1 -1
  130. package/dist/db/parent-ref-clause.d.ts +2 -1
  131. package/dist/db/pg-error.d.ts +2 -0
  132. package/dist/db/pg-error.js +11 -0
  133. package/dist/db/queries/ddl.d.ts +3 -0
  134. package/dist/db/queries/ddl.js +34 -1
  135. package/dist/db/queries/event-store.js +4 -2
  136. package/dist/db/queries/event-tenants.d.ts +2 -0
  137. package/dist/db/queries/event-tenants.js +17 -0
  138. package/dist/db/queries/rebuild-rls-guard.d.ts +2 -0
  139. package/dist/db/queries/rebuild-rls-guard.js +29 -0
  140. package/dist/db/queries/rls-boot-catalog.d.ts +16 -0
  141. package/dist/db/queries/rls-boot-catalog.js +39 -0
  142. package/dist/db/queries/rls-catalog.d.ts +6 -0
  143. package/dist/db/queries/rls-catalog.js +82 -0
  144. package/dist/db/queries/rls-scope.d.ts +3 -0
  145. package/dist/db/queries/rls-scope.js +17 -0
  146. package/dist/db/queries/rls-shape.d.ts +10 -0
  147. package/dist/db/queries/rls-shape.js +42 -0
  148. package/dist/db/queries/session-advisory-lock.d.ts +2 -0
  149. package/dist/db/queries/session-advisory-lock.js +23 -0
  150. package/dist/db/queries/shadow-swap.d.ts +1 -2
  151. package/dist/db/queries/shadow-swap.js +9 -25
  152. package/dist/db/queries/test-stack.d.ts +4 -1
  153. package/dist/db/queries/test-stack.js +84 -2
  154. package/dist/db/query.js +11 -7
  155. package/dist/db/reference-data.d.ts +1 -1
  156. package/dist/db/reference-data.js +6 -1
  157. package/dist/db/render-ddl.d.ts +6 -2
  158. package/dist/db/render-ddl.js +7 -7
  159. package/dist/db/render-rls-ddl.d.ts +35 -0
  160. package/dist/db/render-rls-ddl.js +127 -0
  161. package/dist/db/replay-migration-sql.d.ts +3 -1
  162. package/dist/db/replay-migration-sql.js +22 -5
  163. package/dist/db/replay-rls.d.ts +25 -0
  164. package/dist/db/replay-rls.js +156 -0
  165. package/dist/db/row-level-security-boot-check.d.ts +6 -0
  166. package/dist/db/row-level-security-boot-check.js +86 -0
  167. package/dist/db/row-level-security-connection.d.ts +3 -0
  168. package/dist/db/row-level-security-connection.js +213 -0
  169. package/dist/db/row-level-security-scope.d.ts +33 -0
  170. package/dist/db/row-level-security-scope.js +127 -0
  171. package/dist/db/scope-tenant-db-to-tenant.d.ts +3 -0
  172. package/dist/db/scope-tenant-db-to-tenant.js +14 -0
  173. package/dist/db/sql-identifier.d.ts +1 -0
  174. package/dist/db/sql-identifier.js +3 -0
  175. package/dist/db/table-builder.d.ts +2 -1
  176. package/dist/db/table-builder.js +1 -1
  177. package/dist/db/table-info.js +1 -1
  178. package/dist/db/table-push.d.ts +11 -2
  179. package/dist/db/table-push.js +15 -2
  180. package/dist/db/tenant-db-runner.d.ts +3 -1
  181. package/dist/db/tenant-db-runner.js +4 -3
  182. package/dist/db/tenant-db-scoped-rebind.d.ts +10 -0
  183. package/dist/db/tenant-db-scoped-rebind.js +33 -0
  184. package/dist/db/tenant-db-statement-runner.d.ts +13 -0
  185. package/dist/db/tenant-db-statement-runner.js +48 -0
  186. package/dist/db/tenant-db.d.ts +17 -12
  187. package/dist/db/tenant-db.js +49 -120
  188. package/dist/db/tenant-id-filter.d.ts +3 -0
  189. package/dist/db/tenant-id-filter.js +30 -0
  190. package/dist/db/transaction-serialization.d.ts +6 -0
  191. package/dist/db/transaction-serialization.js +60 -0
  192. package/dist/derivatives/delete-stored-file.js +1 -1
  193. package/dist/derivatives/index.d.ts +2 -5
  194. package/dist/derivatives/index.js +2 -3
  195. package/dist/engine/access.d.ts +2 -1
  196. package/dist/engine/access.js +1 -1
  197. package/dist/engine/boot-validator/entity-handler.d.ts +2 -1
  198. package/dist/engine/boot-validator/entity-handler.js +1 -1
  199. package/dist/engine/boot-validator/entity-list-aggregate-options.d.ts +1 -1
  200. package/dist/engine/boot-validator/list-table-aggregate-options.d.ts +1 -1
  201. package/dist/engine/boot-validator/list-table-aggregate-query.d.ts +2 -1
  202. package/dist/engine/boot-validator/nav.d.ts +3 -1
  203. package/dist/engine/boot-validator/nav.js +1 -1
  204. package/dist/engine/boot-validator/ownership.d.ts +2 -1
  205. package/dist/engine/boot-validator/parent-ref.d.ts +2 -1
  206. package/dist/engine/boot-validator/projection-list-screens.d.ts +2 -1
  207. package/dist/engine/boot-validator/query-refs.d.ts +2 -1
  208. package/dist/engine/boot-validator/screens-forms.d.ts +2 -1
  209. package/dist/engine/boot-validator/screens-kind-shared.d.ts +3 -1
  210. package/dist/engine/boot-validator/screens.d.ts +2 -1
  211. package/dist/engine/boot-validator/workspaces.d.ts +3 -1
  212. package/dist/engine/bridge-stub.d.ts +5 -2
  213. package/dist/engine/bridge-stub.js +39 -24
  214. package/dist/engine/config-helpers.d.ts +2 -2
  215. package/dist/engine/config-helpers.js +0 -9
  216. package/dist/engine/constants.d.ts +0 -10
  217. package/dist/engine/constants.js +0 -8
  218. package/dist/engine/create-app.js +1 -1
  219. package/dist/engine/define-feature.d.ts +2 -1
  220. package/dist/engine/define-feature.js +1 -2
  221. package/dist/engine/define-handler.d.ts +1 -1
  222. package/dist/engine/define-step.d.ts +0 -1
  223. package/dist/engine/define-step.js +0 -3
  224. package/dist/engine/embedded-derived.d.ts +1 -1
  225. package/dist/engine/entity-handlers.d.ts +2 -1
  226. package/dist/engine/entity-handlers.js +7 -7
  227. package/dist/engine/entity-list-runtime-options.js +3 -1
  228. package/dist/engine/entity-list-searchability.d.ts +2 -1
  229. package/dist/engine/event-def.d.ts +2 -1
  230. package/dist/engine/event-helpers.d.ts +3 -1
  231. package/dist/engine/event-helpers.js +3 -3
  232. package/dist/engine/extension-names.d.ts +2 -8
  233. package/dist/engine/extension-names.js +2 -2
  234. package/dist/engine/extension-selector-plugins.d.ts +1 -1
  235. package/dist/engine/extensions/storage-provider.d.ts +1 -2
  236. package/dist/engine/extensions/user-data.d.ts +0 -8
  237. package/dist/engine/factories.d.ts +2 -1
  238. package/dist/engine/feature-builder-state.d.ts +8 -1
  239. package/dist/engine/feature-changelog.d.ts +1 -1
  240. package/dist/engine/feature-config-events-jobs.d.ts +4 -1
  241. package/dist/engine/feature-entity-handlers.d.ts +5 -1
  242. package/dist/engine/feature-manifest.js +1 -1
  243. package/dist/engine/feature-ui-extensions.d.ts +7 -1
  244. package/dist/engine/feature-ui-extensions.js +6 -4
  245. package/dist/engine/field-access.d.ts +2 -1
  246. package/dist/engine/handler-field-references.d.ts +0 -4
  247. package/dist/engine/index.d.ts +57 -54
  248. package/dist/engine/index.js +22 -27
  249. package/dist/engine/instruction-fields.d.ts +1 -1
  250. package/dist/engine/personal-data-fields.d.ts +1 -1
  251. package/dist/engine/qualified-name.d.ts +0 -1
  252. package/dist/engine/read-claim.d.ts +2 -1
  253. package/dist/engine/registry-facade.js +0 -3
  254. package/dist/engine/registry-ingest.d.ts +2 -1
  255. package/dist/engine/registry-ingest.js +1 -2
  256. package/dist/engine/registry-state.d.ts +12 -2
  257. package/dist/engine/registry-state.js +2 -3
  258. package/dist/engine/registry-validate.d.ts +3 -1
  259. package/dist/engine/registry-validate.js +7 -7
  260. package/dist/engine/run-in.d.ts +1 -1
  261. package/dist/engine/run-in.js +0 -1
  262. package/dist/engine/schema-builder.d.ts +1 -1
  263. package/dist/engine/schema-builder.js +1 -1
  264. package/dist/engine/screen-filter-ops.d.ts +2 -1
  265. package/dist/engine/screen-filter-ops.js +0 -5
  266. package/dist/engine/soft-delete-cleanup.d.ts +1 -1
  267. package/dist/engine/soft-delete-cleanup.js +4 -4
  268. package/dist/engine/state-machine.js +2 -1
  269. package/dist/engine/steps/_read-source.js +1 -1
  270. package/dist/engine/steps/aggregate-append-event.js +2 -2
  271. package/dist/engine/steps/call-feature.js +2 -2
  272. package/dist/engine/steps/mail-send.js +1 -1
  273. package/dist/engine/steps/retry.js +1 -1
  274. package/dist/engine/steps/wait-for-event.js +1 -1
  275. package/dist/engine/steps/wait.js +1 -1
  276. package/dist/engine/steps/webhook-send.js +1 -1
  277. package/dist/engine/template-variables.d.ts +2 -1
  278. package/dist/engine/transfer-adjacency.d.ts +1 -1
  279. package/dist/engine/types/index.d.ts +8 -31
  280. package/dist/engine/types/index.js +2 -10
  281. package/dist/engine/validation.d.ts +2 -1
  282. package/dist/entrypoint/index.d.ts +7 -35
  283. package/dist/entrypoint/index.js +7 -1
  284. package/dist/entrypoint/internals.d.ts +40 -0
  285. package/dist/entrypoint/internals.js +1 -0
  286. package/dist/entrypoint/row-level-security-mode-check.d.ts +3 -0
  287. package/dist/entrypoint/row-level-security-mode-check.js +19 -0
  288. package/dist/env/dry-run-internals.d.ts +10 -0
  289. package/dist/env/dry-run-internals.js +1 -0
  290. package/dist/env/dry-run.d.ts +1 -10
  291. package/dist/env/dry-run.js +2 -1
  292. package/dist/env/index.d.ts +1 -85
  293. package/dist/env/index.js +6 -50
  294. package/dist/env/internals.d.ts +87 -0
  295. package/dist/env/internals.js +53 -0
  296. package/dist/errors/classes.js +2 -2
  297. package/dist/errors/index.d.ts +5 -14
  298. package/dist/errors/index.js +4 -9
  299. package/dist/errors/member-resolution.js +1 -1
  300. package/dist/errors/reasons.d.ts +0 -1
  301. package/dist/errors/reasons.js +7 -7
  302. package/dist/errors/serialize.d.ts +0 -18
  303. package/dist/errors/serialize.js +0 -27
  304. package/dist/es-ops/index.d.ts +4 -4
  305. package/dist/es-ops/index.js +2 -2
  306. package/dist/es-ops/operations-schema.d.ts +1 -1
  307. package/dist/es-ops/types.d.ts +4 -5
  308. package/dist/event-store/admin-api-internals.d.ts +14 -0
  309. package/dist/event-store/admin-api-internals.js +1 -0
  310. package/dist/event-store/admin-api.d.ts +1 -14
  311. package/dist/event-store/admin-api.js +11 -6
  312. package/dist/event-store/archive.js +4 -2
  313. package/dist/event-store/errors.js +2 -2
  314. package/dist/event-store/event-store.js +41 -34
  315. package/dist/event-store/events-schema.js +4 -1
  316. package/dist/event-store/index.d.ts +11 -13
  317. package/dist/event-store/index.js +7 -9
  318. package/dist/event-store/rebuild-dead-letter.js +4 -2
  319. package/dist/event-store/snapshot.js +4 -3
  320. package/dist/event-store/stream-scope.d.ts +2 -0
  321. package/dist/event-store/stream-scope.js +6 -0
  322. package/dist/event-store/upcaster-dead-letter.js +4 -2
  323. package/dist/event-store/upcaster.d.ts +2 -1
  324. package/dist/files/action-form-upload.d.ts +2 -1
  325. package/dist/files/action-form-upload.js +1 -1
  326. package/dist/files/assert-file-refs-belong-to-tenant.d.ts +2 -1
  327. package/dist/files/assert-file-refs-belong-to-tenant.js +1 -1
  328. package/dist/files/bind-pending-file-refs.d.ts +2 -1
  329. package/dist/files/bind-pending-file-refs.js +2 -1
  330. package/dist/files/bind-referenced-files.write.js +1 -1
  331. package/dist/files/create-file-ref-from-bytes.d.ts +6 -3
  332. package/dist/files/create-file-ref-from-bytes.js +3 -3
  333. package/dist/files/create-file-ref.d.ts +3 -2
  334. package/dist/files/create-file-ref.js +2 -2
  335. package/dist/files/dispose-removed-file-refs.d.ts +2 -1
  336. package/dist/files/dispose-removed-file-refs.js +1 -1
  337. package/dist/files/feature.js +2 -2
  338. package/dist/files/file-handle.js +1 -1
  339. package/dist/files/file-routes.d.ts +1 -1
  340. package/dist/files/file-routes.js +12 -2
  341. package/dist/files/index.d.ts +11 -13
  342. package/dist/files/index.js +5 -7
  343. package/dist/files/load-save-file-refs.d.ts +2 -1
  344. package/dist/files/load-save-file-refs.js +1 -1
  345. package/dist/files/provider-resolver.d.ts +5 -4
  346. package/dist/files/provider-resolver.js +4 -4
  347. package/dist/files/storage-tracking.js +4 -2
  348. package/dist/files/sweep-orphaned-uploads.d.ts +1 -1
  349. package/dist/files/sweep-orphaned-uploads.js +1 -1
  350. package/dist/i18n/index.d.ts +2 -8
  351. package/dist/i18n/index.js +1 -1
  352. package/dist/i18n/internals.d.ts +7 -0
  353. package/dist/i18n/internals.js +1 -0
  354. package/dist/i18n/required-surface-keys.d.ts +4 -1
  355. package/dist/internal/row-level-security.d.ts +3 -0
  356. package/dist/internal/row-level-security.js +3 -0
  357. package/dist/internal/stack-support.d.ts +1 -1
  358. package/dist/internal/stack-support.js +1 -1
  359. package/dist/internal/testing.d.ts +1 -0
  360. package/dist/internal/testing.js +9 -5
  361. package/dist/jobs/index.d.ts +1 -1
  362. package/dist/jobs/job-append-event.d.ts +12 -0
  363. package/dist/jobs/job-append-event.js +30 -0
  364. package/dist/jobs/job-runner-context.d.ts +2 -2
  365. package/dist/jobs/job-runner-context.js +46 -16
  366. package/dist/jobs/job-runner-execution.d.ts +1 -1
  367. package/dist/jobs/job-runner-execution.js +6 -4
  368. package/dist/jobs/job-runner-gates.d.ts +1 -1
  369. package/dist/jobs/job-runner-gates.js +0 -1
  370. package/dist/jobs/job-runner-helpers.d.ts +11 -4
  371. package/dist/jobs/job-runner-helpers.js +10 -1
  372. package/dist/jobs/job-runner-scope.d.ts +5 -2
  373. package/dist/jobs/job-runner-types.d.ts +2 -1
  374. package/dist/jobs/job-runner.js +19 -6
  375. package/dist/jobs/max-per-tenant.d.ts +1 -1
  376. package/dist/logging/index.d.ts +1 -2
  377. package/dist/logging/index.js +1 -1
  378. package/dist/logging/pino-logger.js +1 -1
  379. package/dist/logging/utils.d.ts +1 -0
  380. package/dist/logging/utils.js +6 -0
  381. package/dist/migrations/index.d.ts +3 -3
  382. package/dist/migrations/index.js +3 -3
  383. package/dist/migrations/pending-rebuilds.d.ts +1 -1
  384. package/dist/migrations/pending-rebuilds.js +1 -1
  385. package/dist/migrations/pii-event-backfill.js +4 -1
  386. package/dist/observability/escape-hatch-report.d.ts +2 -1
  387. package/dist/observability/index.d.ts +7 -15
  388. package/dist/observability/index.js +4 -12
  389. package/dist/observability/metric-validator.js +1 -1
  390. package/dist/observability/metrics-handle.d.ts +0 -1
  391. package/dist/observability/metrics-handle.js +2 -15
  392. package/dist/observability/recording-tracer.d.ts +1 -2
  393. package/dist/observability/recording-tracer.js +0 -6
  394. package/dist/observability/sensitive-filter.d.ts +0 -2
  395. package/dist/observability/sensitive-filter.js +0 -29
  396. package/dist/pipeline/active-membership.d.ts +2 -1
  397. package/dist/pipeline/active-membership.js +6 -2
  398. package/dist/pipeline/append-event-core.d.ts +2 -1
  399. package/dist/pipeline/append-event-core.js +2 -2
  400. package/dist/pipeline/attach-outside-transaction.d.ts +3 -0
  401. package/dist/pipeline/attach-outside-transaction.js +13 -0
  402. package/dist/pipeline/auth-claims-resolver.d.ts +2 -1
  403. package/dist/pipeline/cascade-handler.d.ts +2 -1
  404. package/dist/pipeline/cascade-handler.js +3 -2
  405. package/dist/pipeline/config-accessor-factory.d.ts +1 -1
  406. package/dist/pipeline/config-accessor-factory.js +1 -1
  407. package/dist/pipeline/context-services.d.ts +56 -0
  408. package/dist/pipeline/context-services.js +73 -0
  409. package/dist/pipeline/dispatch-batch.d.ts +2 -1
  410. package/dist/pipeline/dispatch-batch.js +31 -18
  411. package/dist/pipeline/dispatch-query.js +5 -4
  412. package/dist/pipeline/dispatch-scope.d.ts +6 -0
  413. package/dist/pipeline/dispatch-scope.js +27 -0
  414. package/dist/pipeline/dispatch-shared.d.ts +13 -9
  415. package/dist/pipeline/dispatch-shared.js +75 -198
  416. package/dist/pipeline/dispatch-stream.js +8 -4
  417. package/dist/pipeline/dispatch-write.d.ts +2 -1
  418. package/dist/pipeline/dispatch-write.js +10 -8
  419. package/dist/pipeline/dispatcher-utils.d.ts +3 -1
  420. package/dist/pipeline/dispatcher-utils.js +8 -2
  421. package/dist/pipeline/dispatcher.d.ts +3 -2
  422. package/dist/pipeline/dispatcher.js +4 -3
  423. package/dist/pipeline/event-consumer-state.d.ts +0 -1
  424. package/dist/pipeline/event-dispatcher-delivery.d.ts +1 -1
  425. package/dist/pipeline/event-dispatcher-delivery.js +4 -2
  426. package/dist/pipeline/event-dispatcher.d.ts +2 -2
  427. package/dist/pipeline/event-dispatcher.js +7 -3
  428. package/dist/pipeline/event-retention.js +59 -56
  429. package/dist/pipeline/handler-access-error.js +2 -1
  430. package/dist/pipeline/handler-context-auth.d.ts +5 -6
  431. package/dist/pipeline/handler-context-auth.js +7 -4
  432. package/dist/pipeline/handler-context-config.d.ts +2 -5
  433. package/dist/pipeline/handler-context-config.js +2 -2
  434. package/dist/pipeline/handler-context-db.d.ts +32 -7
  435. package/dist/pipeline/handler-context-db.js +26 -17
  436. package/dist/pipeline/handler-context-dispatch.d.ts +13 -7
  437. package/dist/pipeline/handler-context-dispatch.js +9 -8
  438. package/dist/pipeline/handler-context-events.d.ts +22 -36
  439. package/dist/pipeline/handler-context-events.js +36 -31
  440. package/dist/pipeline/handler-context-member-resolution.d.ts +2 -0
  441. package/dist/pipeline/handler-context-member-resolution.js +110 -0
  442. package/dist/pipeline/handler-context-notify.d.ts +3 -2
  443. package/dist/pipeline/handler-context-notify.js +1 -1
  444. package/dist/pipeline/handler-context-observability.d.ts +2 -4
  445. package/dist/pipeline/handler-context-observability.js +7 -5
  446. package/dist/pipeline/handler-context-phase-denials.d.ts +11 -0
  447. package/dist/pipeline/handler-context-phase-denials.js +98 -0
  448. package/dist/pipeline/handler-context-scope.d.ts +7 -10
  449. package/dist/pipeline/handler-context-scope.js +18 -20
  450. package/dist/pipeline/index.d.ts +9 -21
  451. package/dist/pipeline/index.js +5 -7
  452. package/dist/pipeline/irreversible-operation-gate.d.ts +1 -1
  453. package/dist/pipeline/irreversible-operation-gate.js +2 -2
  454. package/dist/pipeline/lifecycle-pipeline.d.ts +8 -7
  455. package/dist/pipeline/lifecycle-pipeline.js +9 -15
  456. package/dist/pipeline/member-read-only-transaction.js +11 -3
  457. package/dist/pipeline/member-reader.d.ts +1 -1
  458. package/dist/pipeline/member-reader.js +2 -2
  459. package/dist/pipeline/msp-rebuild.js +5 -3
  460. package/dist/pipeline/multi-stream-apply-context.js +3 -2
  461. package/dist/pipeline/notify-scope.d.ts +2 -1
  462. package/dist/pipeline/pre-handler-gates.d.ts +2 -1
  463. package/dist/pipeline/pre-transaction-reservations.d.ts +3 -3
  464. package/dist/pipeline/pre-transaction-reservations.js +9 -7
  465. package/dist/pipeline/projection-rebuild.js +12 -5
  466. package/dist/pipeline/projection-state.d.ts +0 -6
  467. package/dist/pipeline/projection-state.js +0 -5
  468. package/dist/pipeline/projections-runner.d.ts +2 -1
  469. package/dist/pipeline/projections-runner.js +10 -6
  470. package/dist/pipeline/system-hooks.d.ts +3 -1
  471. package/dist/pipeline/system-hooks.js +6 -4
  472. package/dist/pipeline/system-identity-switch.d.ts +4 -2
  473. package/dist/pipeline/system-identity-switch.js +51 -36
  474. package/dist/pipeline/typed-append-event.d.ts +2 -0
  475. package/dist/pipeline/typed-append-event.js +4 -0
  476. package/dist/pipeline/write-origin.js +2 -2
  477. package/dist/pipeline/write-result-masking.d.ts +1 -1
  478. package/dist/random/index.d.ts +1 -2
  479. package/dist/random/index.js +1 -2
  480. package/dist/rate-limit/bucket.d.ts +2 -1
  481. package/dist/rate-limit/middleware.js +2 -1
  482. package/dist/redis/index.d.ts +3 -4
  483. package/dist/redis/index.js +2 -2
  484. package/dist/schema-cli/apply-command.js +5 -2
  485. package/dist/schema-cli/baseline-command.js +7 -2
  486. package/dist/schema-cli/generate-command.js +7 -9
  487. package/dist/schema-cli/shared.d.ts +12 -2
  488. package/dist/schema-cli/shared.js +24 -2
  489. package/dist/schema-cli/status-command.js +8 -2
  490. package/dist/schema-cli/validate-command.js +48 -4
  491. package/dist/schema-cli.d.ts +0 -1
  492. package/dist/search/configure-tenant-index.d.ts +1 -3
  493. package/dist/search/configure-tenant-index.js +0 -4
  494. package/dist/search/index.d.ts +1 -4
  495. package/dist/search/index.js +0 -2
  496. package/dist/search/meilisearch-adapter-internals.d.ts +8 -0
  497. package/dist/search/meilisearch-adapter-internals.js +9 -0
  498. package/dist/search/meilisearch-adapter.d.ts +1 -8
  499. package/dist/search/meilisearch-adapter.js +6 -7
  500. package/dist/search/meilisearch-peer.d.ts +2 -0
  501. package/dist/search/meilisearch-peer.js +5 -0
  502. package/dist/search/reindex-entity.js +93 -87
  503. package/dist/secrets/index.d.ts +3 -4
  504. package/dist/secrets/index.js +2 -3
  505. package/dist/seeding/index.d.ts +2 -2
  506. package/dist/seeding/index.js +1 -2
  507. package/dist/time/index.d.ts +2 -2
  508. package/dist/time/index.js +1 -1
  509. package/dist/time/tz-context.d.ts +0 -5
  510. package/dist/time/tz-context.js +0 -9
  511. package/dist/ui-types/index.d.ts +8 -11
  512. package/dist/ui-types/index.js +3 -3
  513. package/dist/upgrade-changelog.d.ts +3 -2
  514. package/dist/upgrade-changelog.js +12 -8
  515. package/dist/upgrade-cli-internals.d.ts +8 -0
  516. package/dist/upgrade-cli-internals.js +63 -0
  517. package/dist/upgrade-cli.d.ts +2 -9
  518. package/dist/upgrade-cli.js +28 -70
  519. package/dist/utils/import-optional-peer.d.ts +6 -0
  520. package/dist/utils/import-optional-peer.js +19 -0
  521. package/dist/utils/index.d.ts +1 -4
  522. package/dist/utils/index.js +1 -4
  523. package/package.json +23 -19
  524. package/src/changes.json +236 -0
  525. package/dist/crypto/request-kms-cache.d.ts +0 -8
  526. package/dist/crypto/request-kms-cache.js +0 -21
  527. package/dist/engine/codemod/index.d.ts +0 -2
  528. package/dist/engine/codemod/index.js +0 -1
  529. package/dist/engine/codemod/pipeline-codemod.d.ts +0 -50
  530. package/dist/engine/codemod/pipeline-codemod.js +0 -536
  531. package/dist/engine/feature-ast/entity-field-types.d.ts +0 -13
  532. package/dist/engine/feature-ast/entity-field-types.js +0 -35
  533. package/dist/engine/feature-ast/extractors/ai-steps.d.ts +0 -6
  534. package/dist/engine/feature-ast/extractors/ai-steps.js +0 -254
  535. package/dist/engine/feature-ast/extractors/events.d.ts +0 -5
  536. package/dist/engine/feature-ast/extractors/events.js +0 -275
  537. package/dist/engine/feature-ast/extractors/handlers.d.ts +0 -24
  538. package/dist/engine/feature-ast/extractors/handlers.js +0 -358
  539. package/dist/engine/feature-ast/extractors/hooks.d.ts +0 -24
  540. package/dist/engine/feature-ast/extractors/hooks.js +0 -248
  541. package/dist/engine/feature-ast/extractors/index.d.ts +0 -14
  542. package/dist/engine/feature-ast/extractors/index.js +0 -13
  543. package/dist/engine/feature-ast/extractors/jobs-routes.d.ts +0 -7
  544. package/dist/engine/feature-ast/extractors/jobs-routes.js +0 -145
  545. package/dist/engine/feature-ast/extractors/projections-screens.d.ts +0 -10
  546. package/dist/engine/feature-ast/extractors/projections-screens.js +0 -195
  547. package/dist/engine/feature-ast/extractors/resolve-import.d.ts +0 -21
  548. package/dist/engine/feature-ast/extractors/resolve-import.js +0 -180
  549. package/dist/engine/feature-ast/extractors/round1.d.ts +0 -11
  550. package/dist/engine/feature-ast/extractors/round1.js +0 -87
  551. package/dist/engine/feature-ast/extractors/round2.d.ts +0 -7
  552. package/dist/engine/feature-ast/extractors/round2.js +0 -191
  553. package/dist/engine/feature-ast/extractors/round3.d.ts +0 -23
  554. package/dist/engine/feature-ast/extractors/round3.js +0 -292
  555. package/dist/engine/feature-ast/extractors/round5.d.ts +0 -9
  556. package/dist/engine/feature-ast/extractors/round5.js +0 -68
  557. package/dist/engine/feature-ast/extractors/round6.d.ts +0 -4
  558. package/dist/engine/feature-ast/extractors/round6.js +0 -17
  559. package/dist/engine/feature-ast/extractors/shared.d.ts +0 -94
  560. package/dist/engine/feature-ast/extractors/shared.js +0 -343
  561. package/dist/engine/feature-ast/index.d.ts +0 -16
  562. package/dist/engine/feature-ast/index.js +0 -10
  563. package/dist/engine/feature-ast/parse.d.ts +0 -31
  564. package/dist/engine/feature-ast/parse.js +0 -485
  565. package/dist/engine/feature-ast/patch.d.ts +0 -212
  566. package/dist/engine/feature-ast/patch.js +0 -611
  567. package/dist/engine/feature-ast/patcher.d.ts +0 -217
  568. package/dist/engine/feature-ast/patcher.js +0 -281
  569. package/dist/engine/feature-ast/pattern-change-schema.d.ts +0 -1238
  570. package/dist/engine/feature-ast/pattern-change-schema.js +0 -1072
  571. package/dist/engine/feature-ast/pattern-id-matchers.d.ts +0 -7
  572. package/dist/engine/feature-ast/pattern-id-matchers.js +0 -211
  573. package/dist/engine/feature-ast/pattern-id-schema.d.ts +0 -251
  574. package/dist/engine/feature-ast/pattern-id-schema.js +0 -166
  575. package/dist/engine/feature-ast/patterns.d.ts +0 -322
  576. package/dist/engine/feature-ast/patterns.js +0 -107
  577. package/dist/engine/feature-ast/render.d.ts +0 -47
  578. package/dist/engine/feature-ast/render.js +0 -666
  579. package/dist/engine/feature-ast/source-location.d.ts +0 -17
  580. package/dist/engine/feature-ast/source-location.js +0 -27
  581. package/dist/engine/pattern-library/index.d.ts +0 -2
  582. package/dist/engine/pattern-library/index.js +0 -3
  583. package/dist/engine/pattern-library/library.d.ts +0 -14
  584. package/dist/engine/pattern-library/library.js +0 -94
  585. package/dist/engine/pattern-library/mixed-schemas.d.ts +0 -16
  586. package/dist/engine/pattern-library/mixed-schemas.js +0 -559
  587. package/dist/engine/pattern-library/opaque-schemas.d.ts +0 -7
  588. package/dist/engine/pattern-library/opaque-schemas.js +0 -115
  589. package/dist/engine/pattern-library/shared-fields.d.ts +0 -122
  590. package/dist/engine/pattern-library/shared-fields.js +0 -88
  591. package/dist/engine/pattern-library/static-schemas.d.ts +0 -21
  592. package/dist/engine/pattern-library/static-schemas.js +0 -487
  593. package/dist/engine/pattern-library/types.d.ts +0 -158
  594. package/dist/engine/pattern-library/types.js +0 -36
  595. package/dist/engine/resolve-config-or-param.d.ts +0 -18
  596. package/dist/engine/resolve-config-or-param.js +0 -76
  597. package/src/scripts/codemod/README.md +0 -187
  598. package/src/scripts/codemod/codemod-support.ts +0 -334
  599. package/src/scripts/codemod/collect-source-files.ts +0 -48
  600. package/src/scripts/codemod/crypto-shredding-testing-move.ts +0 -119
  601. package/src/scripts/codemod/db-factory.ts +0 -810
  602. package/src/scripts/codemod/feature-factory.ts +0 -429
  603. package/src/scripts/codemod/feature-layout.ts +0 -424
  604. package/src/scripts/codemod/import-extensions.ts +0 -118
  605. package/src/scripts/codemod/migrate-cross-tenant.ts +0 -328
  606. package/src/scripts/codemod/migrate-db-raw.ts +0 -401
  607. package/src/scripts/codemod/migrate-open-to-all.ts +0 -235
  608. package/src/scripts/codemod/object-form.ts +0 -746
  609. package/src/scripts/codemod/package-merge.ts +0 -299
  610. package/src/scripts/codemod/pii-personal-migration.ts +0 -816
  611. package/src/scripts/codemod/remove-engine-payload-casts.ts +0 -318
  612. package/src/scripts/codemod/s3-imports.ts +0 -236
  613. package/src/scripts/codemod/tenant-db-options.ts +0 -454
  614. package/src/scripts/codemod/testing-injection.ts +0 -215
  615. package/src/scripts/codemod/testing-move-targets.ts +0 -161
  616. package/src/scripts/codemod/testing-move.ts +0 -542
  617. package/src/scripts/codemod/unprocessable-error-details-reason.ts +0 -411
@@ -17,41 +17,47 @@ import { toSnakeCase } from "../utils/case.js";
17
17
  import { resolveTableName } from "./entity-table-meta.js";
18
18
  import { toPostgresArrayLiteral } from "./queries/postgres-array-literal.js";
19
19
  import { executeRawQuery, executeRawQueryRead } from "./queries/raw-sql.js";
20
+ import { runInSystemRowLevelSecurityScope } from "./row-level-security-scope.js";
20
21
  import { columnNamesOf, tableExists } from "./schema-inspection.js";
21
22
  export async function nullBlindIndexesForSubject(db, features, subjectKey) {
22
- const likePattern = subjectCiphertextLikePattern(subjectKey);
23
- const candidates = [];
24
- for (const feature of features.values()) {
25
- for (const [entityName, entity] of Object.entries(feature.entities ?? {})) {
26
- const lookupable = collectLookupableFields(entity);
27
- if (lookupable.length === 0)
28
- continue;
29
- // No featureName prefix — the dispatcher builds entity tables without
30
- // one (buildEntityTable with no featureName option), the sweep has to
31
- // hit the same names.
32
- candidates.push({ tableName: resolveTableName(entityName, entity, undefined), lookupable });
23
+ return runInSystemRowLevelSecurityScope("blind index nulling for a forgotten subject spans tenants", async () => {
24
+ const likePattern = subjectCiphertextLikePattern(subjectKey);
25
+ const candidates = [];
26
+ for (const feature of features.values()) {
27
+ for (const [entityName, entity] of Object.entries(feature.entities ?? {})) {
28
+ const lookupable = collectLookupableFields(entity);
29
+ if (lookupable.length === 0)
30
+ continue;
31
+ // No featureName prefix — the dispatcher builds entity tables without
32
+ // one (buildEntityTable with no featureName option), the sweep has to
33
+ // hit the same names.
34
+ candidates.push({
35
+ tableName: resolveTableName(entityName, entity, undefined),
36
+ lookupable,
37
+ });
38
+ }
33
39
  }
34
- }
35
- // Skip entities whose projection was never migrated — same class as
36
- // subjectRowExistsInTenant. A throw here runs AFTER eraseKey
37
- // in forget-subject and would leave deterministic bidx columns still
38
- // linkable. One batched existence query instead of one
39
- // roundtrip per entity.
40
- const existing = await existingTableNames(db, candidates.map((c) => c.tableName));
41
- for (const { tableName, lookupable } of candidates) {
42
- if (!existing.has(tableName))
43
- continue;
44
- // Same hazard for a migrated table that predates a later-added
45
- // lookupable field: skip fields whose columns are missing instead of
46
- // aborting the sweep for the remaining ones.
47
- const columns = await columnNamesOf(db, tableName);
48
- for (const fieldName of lookupable) {
49
- const snake = toSnakeCase(fieldName);
50
- if (!columns.has(snake) || !columns.has(`${snake}_bidx`))
40
+ // Skip entities whose projection was never migrated — same class as
41
+ // subjectRowExistsInTenant. A throw here runs AFTER eraseKey
42
+ // in forget-subject and would leave deterministic bidx columns still
43
+ // linkable. One batched existence query instead of one
44
+ // roundtrip per entity.
45
+ const existing = await existingTableNames(db, candidates.map((c) => c.tableName));
46
+ for (const { tableName, lookupable } of candidates) {
47
+ if (!existing.has(tableName))
51
48
  continue;
52
- await executeRawQuery(db, `UPDATE ${quoteIdent(tableName)} SET ${quoteIdent(`${snake}_bidx`)} = NULL WHERE ${quoteIdent(snake)} LIKE $1`, [likePattern]);
49
+ // Same hazard for a migrated table that predates a later-added
50
+ // lookupable field: skip fields whose columns are missing instead of
51
+ // aborting the sweep for the remaining ones.
52
+ const columns = await columnNamesOf(db, tableName);
53
+ for (const fieldName of lookupable) {
54
+ const snake = toSnakeCase(fieldName);
55
+ if (!columns.has(snake) || !columns.has(`${snake}_bidx`))
56
+ continue;
57
+ await executeRawQuery(db, `UPDATE ${quoteIdent(tableName)} SET ${quoteIdent(`${snake}_bidx`)} = NULL WHERE ${quoteIdent(snake)} LIKE $1`, [likePattern]);
58
+ }
53
59
  }
54
- }
60
+ });
55
61
  }
56
62
  async function existingTableNames(db, tableNames) {
57
63
  if (tableNames.length === 0)
@@ -75,37 +81,39 @@ async function existingTableNames(db, tableNames) {
75
81
  // (`aggregate_id = subjectId` under the actor tenant) when apps need
76
82
  // client-supplied ids.
77
83
  export async function subjectRowExistsInTenant(db, features, subjectId, tenantId) {
78
- // Prefetch which self-PII projection tables actually exist — probing a
79
- // missing relation used to throw (and get swallowed), which both hid
80
- // real schema bugs and risked poisoning the driver connection for the
81
- // rest of the forget TX.
82
- const candidateTables = [];
83
- for (const feature of features.values()) {
84
- for (const [entityName, entity] of Object.entries(feature.entities ?? {})) {
85
- const hasSelfPiiField = Object.values(entity.fields).some(isSelfPiiField);
86
- if (!hasSelfPiiField)
87
- continue;
88
- // Subject ids are uuids; comparing one to a serial id column makes
89
- // postgres throw (invalid input syntax for integer) mid forget-TX.
90
- if (entity.idType === "serial")
84
+ return runInSystemRowLevelSecurityScope("subject existence check spans tenants", async () => {
85
+ // Prefetch which self-PII projection tables actually exist — probing a
86
+ // missing relation used to throw (and get swallowed), which both hid
87
+ // real schema bugs and risked poisoning the driver connection for the
88
+ // rest of the forget TX.
89
+ const candidateTables = [];
90
+ for (const feature of features.values()) {
91
+ for (const [entityName, entity] of Object.entries(feature.entities ?? {})) {
92
+ const hasSelfPiiField = Object.values(entity.fields).some(isSelfPiiField);
93
+ if (!hasSelfPiiField)
94
+ continue;
95
+ // Subject ids are uuids; comparing one to a serial id column makes
96
+ // postgres throw (invalid input syntax for integer) mid forget-TX.
97
+ if (entity.idType === "serial")
98
+ continue;
99
+ candidateTables.push(resolveTableName(entityName, entity, undefined));
100
+ }
101
+ }
102
+ const existing = new Set();
103
+ for (const tableName of candidateTables) {
104
+ const rows = await executeRawQueryRead(db, `SELECT to_regclass(quote_ident($1)) IS NOT NULL AS exists`, [tableName]);
105
+ if (rows[0]?.exists === true)
106
+ existing.add(tableName);
107
+ }
108
+ for (const tableName of candidateTables) {
109
+ if (!existing.has(tableName))
91
110
  continue;
92
- candidateTables.push(resolveTableName(entityName, entity, undefined));
111
+ const rows = await executeRawQueryRead(db, `SELECT 1 FROM ${quoteIdent(tableName)} WHERE id = $1 AND tenant_id = $2 LIMIT 1`, [subjectId, tenantId]);
112
+ if (rows.length > 0)
113
+ return true;
93
114
  }
94
- }
95
- const existing = new Set();
96
- for (const tableName of candidateTables) {
97
- const rows = await executeRawQueryRead(db, `SELECT to_regclass(quote_ident($1)) IS NOT NULL AS exists`, [tableName]);
98
- if (rows[0]?.exists === true)
99
- existing.add(tableName);
100
- }
101
- for (const tableName of candidateTables) {
102
- if (!existing.has(tableName))
103
- continue;
104
- const rows = await executeRawQueryRead(db, `SELECT 1 FROM ${quoteIdent(tableName)} WHERE id = $1 AND tenant_id = $2 LIMIT 1`, [subjectId, tenantId]);
105
- if (rows.length > 0)
106
- return true;
107
- }
108
- return false;
115
+ return false;
116
+ });
109
117
  }
110
118
  function findEntityByExactName(features, entityName) {
111
119
  for (const feature of features.values()) {
@@ -123,14 +131,16 @@ function findEntityByExactName(features, entityName) {
123
131
  // table (globally unique, unspoofable via a client-supplied explicitId
124
132
  // at create) — a composite PK or shared table would break this.
125
133
  export async function recordRowExistsInTenant(db, features, entityName, recordId, tenantId) {
126
- const entity = findEntityByExactName(features, entityName);
127
- if (!entity)
128
- return false;
129
- const tableName = resolveTableName(entityName, entity, undefined);
130
- if (!(await tableExists(db, tableName)))
131
- return false;
132
- const rows = await executeRawQueryRead(db, `SELECT 1 FROM ${quoteIdent(tableName)} WHERE id = $1 AND tenant_id = $2 LIMIT 1`, [recordId, tenantId]);
133
- return rows.length > 0;
134
+ return runInSystemRowLevelSecurityScope("record existence check spans tenants", async () => {
135
+ const entity = findEntityByExactName(features, entityName);
136
+ if (!entity)
137
+ return false;
138
+ const tableName = resolveTableName(entityName, entity, undefined);
139
+ if (!(await tableExists(db, tableName)))
140
+ return false;
141
+ const rows = await executeRawQueryRead(db, `SELECT 1 FROM ${quoteIdent(tableName)} WHERE id = $1 AND tenant_id = $2 LIMIT 1`, [recordId, tenantId]);
142
+ return rows.length > 0;
143
+ });
134
144
  }
135
145
  // Provenance fallback for record subjects whose projection row is gone (hard
136
146
  // delete) or that have no registered entity at all (custom aggregates): the
@@ -138,20 +148,24 @@ export async function recordRowExistsInTenant(db, features, entityName, recordId
138
148
  // and the subject key is global, so another tenant could open a stream under
139
149
  // the same aggregate id; any foreign event voids the proof.
140
150
  export async function recordEventsOwnedExclusivelyByTenant(db, aggregateType, aggregateId, tenantId) {
141
- const rows = await executeRawQueryRead(db, `SELECT 1 FROM "kumiko_events" WHERE aggregate_type = $1 AND aggregate_id = $2::uuid AND tenant_id = $3::uuid
151
+ return runInSystemRowLevelSecurityScope("record event ownership check spans tenants", async () => {
152
+ const rows = await executeRawQueryRead(db, `SELECT 1 FROM "kumiko_events" WHERE aggregate_type = $1 AND aggregate_id = $2::uuid AND tenant_id = $3::uuid
142
153
  AND NOT EXISTS (SELECT 1 FROM "kumiko_events" WHERE aggregate_type = $1 AND aggregate_id = $2::uuid AND tenant_id <> $3::uuid)
143
154
  LIMIT 1`, [aggregateType, aggregateId, tenantId]);
144
- return rows.length > 0;
155
+ return rows.length > 0;
156
+ });
145
157
  }
146
158
  // Owning tenant of a record subject's row, for callers that must evaluate
147
159
  // tenant-scoped policy for the row's real owner (not the acting user's tenant).
148
160
  export async function recordRowOwningTenantId(db, features, entityName, recordId) {
149
- const entity = findEntityByExactName(features, entityName);
150
- if (!entity)
151
- return undefined;
152
- const tableName = resolveTableName(entityName, entity, undefined);
153
- if (!(await tableExists(db, tableName)))
154
- return undefined;
155
- const rows = await executeRawQueryRead(db, `SELECT tenant_id FROM ${quoteIdent(tableName)} WHERE id = $1 LIMIT 1`, [recordId]);
156
- return rows[0]?.tenant_id;
161
+ return runInSystemRowLevelSecurityScope("record owner lookup spans tenants", async () => {
162
+ const entity = findEntityByExactName(features, entityName);
163
+ if (!entity)
164
+ return undefined;
165
+ const tableName = resolveTableName(entityName, entity, undefined);
166
+ if (!(await tableExists(db, tableName)))
167
+ return undefined;
168
+ const rows = await executeRawQueryRead(db, `SELECT tenant_id FROM ${quoteIdent(tableName)} WHERE id = $1 LIMIT 1`, [recordId]);
169
+ return rows[0]?.tenant_id;
170
+ });
157
171
  }
@@ -1,7 +1,7 @@
1
1
  // Canonical table source for both `kumiko schema generate` and setupTestStack
2
2
  // (entities, storeTables, projections, multiStreamProjections). One shared
3
3
  // source so the test-DB push and migrations cannot drift apart again.
4
- import { compareByCodepoint } from "../utils/index.js";
4
+ import { compareByCodepoint } from "../utils/compare.js";
5
5
  import { assertBackingTableSuperset, deriveEntityTableMeta, } from "./entity-table-meta.js";
6
6
  import { enumerateFeatureTableSources } from "./feature-table-sources.js";
7
7
  import { asEntityTableMeta } from "./query.js";
@@ -1,4 +1,4 @@
1
- import type { EntityDefinition } from "../engine/types/index.ts";
1
+ import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
2
2
  /**
3
3
  * API-Form (combined) → DB-Form (flat). Wird vor jedem Insert/Update aufgerufen.
4
4
  */
@@ -1,10 +1,3 @@
1
- // Compound-type pipeline for the executor.
2
- //
3
- // A compound type is a field represented by ONE API object and MULTIPLE
4
- // DB columns. Examples:
5
- // - locatedTimestamp: { at, tz, utc } ↔ <name>Utc + <name>Tz
6
- // - money: { amount, currency } ↔ <name> + <name>Currency
7
- // - (upcoming) address: { street, zip, city } ↔ <name>Street + <name>Zip + <name>City
8
1
  //
9
2
  // Instead of nesting every helper in the executor at four different points,
10
3
  // this function pipelines all compound-type conversions in one pass. Adding
@@ -1,5 +1,6 @@
1
+ import type { ConfigSeedDef } from "@cosmicdrift/kumiko-types/config";
1
2
  import type { EntityDefinition } from "../engine/index.ts";
2
- import type { ConfigSeedDef, Registry } from "../engine/types/index.ts";
3
+ import type { Registry } from "../engine/types/index.ts";
3
4
  import type { EnvelopeCipher } from "../secrets/envelope-cipher.ts";
4
5
  import type { DbConnection } from "./connection.ts";
5
6
  import type { EntityTable } from "./table-builder.ts";
@@ -24,7 +24,12 @@ export async function seedConfigValues(seeds, table, entity, registry, db, ciphe
24
24
  return { created, skipped };
25
25
  const systemUser = createSystemUser(SYSTEM_TENANT_ID);
26
26
  const executor = createEventStoreExecutor(table, entity, { entityName: "config-value" });
27
- const tdb = createTenantDb({ db, tenantId: SYSTEM_TENANT_ID, mode: "system" });
27
+ const tdb = createTenantDb({
28
+ db,
29
+ tenantId: SYSTEM_TENANT_ID,
30
+ mode: "system",
31
+ reason: "config seeds are system-tenant defaults",
32
+ });
28
33
  for (const seed of seeds) {
29
34
  const keyDef = registry.getConfigKey(seed.key);
30
35
  if (!keyDef) {
@@ -1,4 +1,5 @@
1
1
  import type { DbConnection, PgClient, PgListenClient } from "@cosmicdrift/kumiko-types/db-connection";
2
+ import { type RowLevelSecurityMode } from "./render-rls-ddl.ts";
2
3
  export type * from "@cosmicdrift/kumiko-types/db-connection";
3
4
  type BunSqlOptions = Bun.SQL.PostgresOrMySQLOptions;
4
5
  export type DbConnectionOptions = {
@@ -13,6 +14,7 @@ export type DbConnectionOptions = {
13
14
  };
14
15
  export type CreateDbConnectionOptions = DbConnectionOptions & {
15
16
  readonly url: string;
17
+ readonly rowLevelSecurity?: RowLevelSecurityMode;
16
18
  };
17
19
  export type DbConnectionHandle = {
18
20
  db: DbConnection;
@@ -26,7 +28,7 @@ export type UnboundedSiblingPool = {
26
28
  readonly close: (timeoutSeconds: number) => Promise<void>;
27
29
  };
28
30
  export declare function openUnboundedSiblingPool(db: unknown): UnboundedSiblingPool | undefined;
29
- export declare function createDbConnection({ url, ...options }: CreateDbConnectionOptions): DbConnectionHandle;
31
+ export declare function createDbConnection({ url, rowLevelSecurity, ...options }: CreateDbConnectionOptions): DbConnectionHandle;
30
32
  export declare function dbConnectionOptionsFromEnv(env?: Readonly<Record<string, string | undefined>>): DbConnectionOptions;
31
33
  export declare const DEFAULT_SERVER_STATEMENT_TIMEOUT_MS = 60000;
32
34
  export declare const DEFAULT_SERVER_IDLE_IN_TRANSACTION_TIMEOUT_MS = 300000;
@@ -1,4 +1,6 @@
1
1
  import { readPositiveIntEnv } from "../utils/env-parse.js";
2
+ import { DEFAULT_ROW_LEVEL_SECURITY_MODE } from "./render-rls-ddl.js";
3
+ import { enforceRowLevelSecurity } from "./row-level-security-connection.js";
2
4
  // A DB outage can leave an in-flight query referenced on a dead connection,
3
5
  // so a plain close() (no timeout = wait for queries to finish) hangs forever —
4
6
  // close() must always bound how long shutdown waits.
@@ -20,7 +22,7 @@ function verifiedTls(ssl) {
20
22
  }
21
23
  return ssl;
22
24
  }
23
- export function createDbConnection({ url, ...options }) {
25
+ export function createDbConnection({ url, rowLevelSecurity = DEFAULT_ROW_LEVEL_SECURITY_MODE, ...options }) {
24
26
  const sqlOptions = {};
25
27
  if (options.maxConnections !== undefined)
26
28
  sqlOptions.max = options.maxConnections;
@@ -45,11 +47,18 @@ export function createDbConnection({ url, ...options }) {
45
47
  if (Object.keys(serverParameters).length > 0)
46
48
  sqlOptions.connection = serverParameters;
47
49
  // Bun.SQL opens all `max` connections on the first query, so a pool always costs `max` backends.
48
- const client = new Bun.SQL(url, sqlOptions);
50
+ const rawClient = new Bun.SQL(url, sqlOptions);
51
+ const enforced = rowLevelSecurity === "on";
52
+ const client = enforced ? enforceRowLevelSecurity(rawClient) : rawClient;
49
53
  // A run that never settles (a caller gave up on it via its own timeout) never reaches the
50
54
  // sibling's own close; close() ends it so shutdown and DROP DATABASE are not blocked by it.
51
55
  const openSiblings = new Set();
52
- unboundedSiblingFactories.set(client, () => {
56
+ const registerFactory = (factory) => {
57
+ unboundedSiblingFactories.set(rawClient, factory);
58
+ if (client !== rawClient)
59
+ unboundedSiblingFactories.set(client, factory);
60
+ };
61
+ registerFactory(() => {
53
62
  // Two connections are enough for lock-in-tx plus DDL; the cap keeps parallel rebuilds from exhausting max_connections.
54
63
  const sibling = new Bun.SQL(url, {
55
64
  ...sqlOptions,
@@ -58,7 +67,7 @@ export function createDbConnection({ url, ...options }) {
58
67
  });
59
68
  openSiblings.add(sibling);
60
69
  return {
61
- client: sibling,
70
+ client: enforced ? enforceRowLevelSecurity(sibling) : sibling,
62
71
  close: async (timeoutSeconds) => {
63
72
  openSiblings.delete(sibling);
64
73
  await sibling.close({ timeout: timeoutSeconds });
@@ -71,8 +80,9 @@ export function createDbConnection({ url, ...options }) {
71
80
  listenClient: client,
72
81
  close: async () => {
73
82
  const timeout = options.closeTimeoutSeconds ?? DEFAULT_DB_CLOSE_TIMEOUT_SECONDS;
83
+ unboundedSiblingFactories.delete(rawClient);
74
84
  unboundedSiblingFactories.delete(client);
75
- const pools = [client, ...openSiblings];
85
+ const pools = [rawClient, ...openSiblings];
76
86
  openSiblings.clear();
77
87
  const results = await Promise.allSettled(pools.map((pool) => pool.close({ timeout })));
78
88
  const failure = results.find((result) => result.status === "rejected");
@@ -1,5 +1,5 @@
1
+ import type { EscapeHatchReporter } from "@cosmicdrift/kumiko-types/handlers";
1
2
  import type { CrossTenantReads } from "@cosmicdrift/kumiko-types/tenant-db-types";
2
- import type { EscapeHatchReporter } from "../engine/types/index.ts";
3
3
  import { type CreateTenantDbOptions, type TenantDb } from "./tenant-db.ts";
4
4
  export declare function createCrossTenantReads(systemModeDb: TenantDb, reason: string, report: EscapeHatchReporter): CrossTenantReads;
5
5
  export declare function createHandlerCrossTenantReads(options: Omit<CreateTenantDbOptions, "mode"> & {
@@ -18,6 +18,6 @@ export function createCrossTenantReads(systemModeDb, reason, report) {
18
18
  // Own system-mode TenantDb without unsafeRaw/globalWrites: the handler gets the read surface only.
19
19
  export function createHandlerCrossTenantReads(options) {
20
20
  const { reason, report, ...dbOptions } = options;
21
- const systemModeDb = createTenantDb({ ...dbOptions, mode: "system" });
21
+ const systemModeDb = createTenantDb({ ...dbOptions, mode: "system", reason });
22
22
  return createCrossTenantReads(systemModeDb, reason, report);
23
23
  }
@@ -4,7 +4,6 @@ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
4
4
  import type { PgType } from "./entity-table-meta.ts";
5
5
  export type { ColumnHandle, SchemaTable } from "@cosmicdrift/kumiko-types/schema-table-types";
6
6
  export type TableColumns<_T = unknown> = SchemaTable;
7
- export type SelectQuery = unknown;
8
7
  export declare function pgTypeToSqlType(pgType: PgType): string;
9
8
  type ColumnFinal = {
10
9
  readonly sqlName: string;
@@ -46,10 +45,6 @@ export declare function timestamp(name: string, _opts?: {
46
45
  withTimezone?: boolean;
47
46
  mode?: "string" | "date";
48
47
  }): ColumnBuilder<Temporal.Instant | string>;
49
- export declare function numeric(name: string, _opts?: {
50
- precision?: number;
51
- scale?: number;
52
- }): ColumnBuilder<string>;
53
48
  export declare function instant(name: string, opts?: {
54
49
  precision?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
55
50
  }): ColumnBuilder<Temporal.Instant>;
@@ -180,9 +180,6 @@ export function timestamp(name, _opts) {
180
180
  }
181
181
  // numeric → text (we don't currently use Decimal); kept for compat with
182
182
  // legacy schema imports that won't actually instantiate at runtime.
183
- export function numeric(name, _opts) {
184
- return buildColumn(name, "text");
185
- }
186
183
  export function instant(name, opts) {
187
184
  const pgType = opts?.precision === 3 ? "timestamptz(3)" : "timestamptz";
188
185
  return buildColumn(name, pgType);
@@ -65,9 +65,30 @@ const INTEGER_PG_TYPES = new Set([
65
65
  export function instantToTimestamptzParam(instant) {
66
66
  return Temporal.Instant.fromEpochMilliseconds(instant.epochMilliseconds).toString();
67
67
  }
68
+ function instantOfIsoString(value) {
69
+ try {
70
+ return Temporal.Instant.from(value);
71
+ }
72
+ catch {
73
+ // skip: not an ISO instant, Postgres parses (or rejects) the text itself
74
+ return undefined;
75
+ }
76
+ }
77
+ // Event payloads carry instants as ISO strings with microseconds; a projection rebuild binds them
78
+ // as-is, so without the same truncation the replayed row differs from the live one.
79
+ function timestamptzInstantOf(value) {
80
+ if (isTemporalInstant(value))
81
+ return value;
82
+ if (typeof value === "string")
83
+ return instantOfIsoString(value);
84
+ return undefined;
85
+ }
68
86
  function prepareDriverNeutralScalar(value, pgType) {
69
- if ((pgType === "timestamptz" || pgType === "timestamptz(3)") && isTemporalInstant(value)) {
70
- return { kind: "param", sql: "", bound: instantToTimestamptzParam(value) };
87
+ if (pgType === "timestamptz" || pgType === "timestamptz(3)") {
88
+ const instant = timestamptzInstantOf(value);
89
+ if (instant !== undefined) {
90
+ return { kind: "param", sql: "", bound: instantToTimestamptzParam(instant) };
91
+ }
71
92
  }
72
93
  // Sent as text, so Postgres rejects 3.5 for an integer column instead of rounding it.
73
94
  if (typeof value === "number" && pgType !== undefined && INTEGER_PG_TYPES.has(pgType)) {
@@ -1,4 +1,5 @@
1
- import type { EntityDefinition, SessionUser } from "../engine/types/index.ts";
1
+ import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
2
+ import type { SessionUser } from "../engine/types/index.ts";
2
3
  import { type ParentVisibilityOption } from "./parent-ref-clause.ts";
3
4
  import type { TenantDb } from "./tenant-db.ts";
4
5
  export type EagerLoadEntityResolver = (entityName: string) => EntityDefinition | undefined;
@@ -1,4 +1,4 @@
1
- import type { EntityDefinition } from "../engine/types/index.ts";
1
+ import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
2
2
  import { type Logger } from "../logging/index.ts";
3
3
  import type { EnvelopeCipher } from "../secrets/envelope-cipher.ts";
4
4
  export declare function collectEncryptedFieldNames(entity: EntityDefinition): ReadonlySet<string>;
@@ -1,6 +1,6 @@
1
1
  import type { Writable } from "@cosmicdrift/kumiko-types/executor-brand";
2
+ import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
2
3
  import type { EntityTenancy, TenancyBrand } from "@cosmicdrift/kumiko-types/tenancy-brand";
3
- import type { EntityDefinition } from "../engine/types/index.ts";
4
4
  import type { BuildEntityTableMetaOptions, EntityTableMeta, UnmanagedTableInput } from "./entity-table-meta-types.ts";
5
5
  export type { BuildEntityTableMetaOptions, ColumnMeta, CompositePrimaryKeyMeta, EntityTableMeta, IndexMeta, PgType, UnmanagedTableInput, } from "./entity-table-meta-types.ts";
6
6
  export declare function resolveTableName(entityName: string, entity: EntityDefinition, featureName: string | undefined): string;
@@ -1,6 +1,8 @@
1
+ import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
2
+ import type { EntityId } from "@cosmicdrift/kumiko-types/identifiers";
1
3
  import type { ErasedSubjectHandling } from "../crypto/pii-field-encryption.ts";
2
4
  import type { WhereObject } from "../db/query.ts";
3
- import type { EntityDefinition, EntityId, SessionUser, TenantId } from "../engine/types/index.ts";
5
+ import type { SessionUser, TenantId } from "../engine/types/index.ts";
4
6
  import { type WriteFailure } from "../errors/index.ts";
5
7
  import { type EventMetadata } from "../event-store/index.ts";
6
8
  import type { EntityCache } from "../pipeline/entity-cache.ts";
@@ -5,7 +5,8 @@ import { executeRawQueryRead } from "../db/queries/raw-sql.js";
5
5
  import { shiftParams } from "../engine/ownership.js";
6
6
  import { SYSTEM_TENANT_ID } from "../engine/types/identifiers.js";
7
7
  import { AccessDeniedError, InternalError, subjectErasedConflict, UniqueViolationError, writeFailure, } from "../errors/index.js";
8
- import { ArchivedStreamError, isStreamArchived } from "../event-store/index.js";
8
+ import { ArchivedStreamError } from "../event-store/errors.js";
9
+ import { isStreamArchived } from "../event-store/index.js";
9
10
  import { assertUnreachable } from "../utils/index.js";
10
11
  import { rehydrateCompoundTypes } from "./compound-types.js";
11
12
  import { collectEncryptedFieldNames, decryptEntityFieldValues, encryptEntityFieldValues, logUndecryptableField, resolveEntityFieldEncryption, } from "./entity-field-encryption.js";
@@ -120,7 +121,7 @@ export function buildExecutorContext(table, entity, options) {
120
121
  // old hardcoded user.tenantId). Single source of truth for the stream key.
121
122
  const streamTenantFor = (user, requestedStreamTenantId) => entity.systemStream ? SYSTEM_TENANT_ID : (requestedStreamTenantId ?? user.tenantId);
122
123
  // Only a system-mode db (r.systemScope() / acknowledged cross-tenant) or a tenant-mode db bound
123
- // to exactly that stream tenant (ctx.forTenant) may address another tenant's stream, and only
124
+ // to exactly that stream tenant (ctx.tenants.writeTarget) may address another tenant's stream, and only
124
125
  // the tenant of the row it just loaded.
125
126
  const streamTenantOverrideFailure = (db, row, requestedStreamTenantId) => {
126
127
  if (requestedStreamTenantId === undefined)
@@ -293,7 +294,7 @@ export function buildExecutorContext(table, entity, options) {
293
294
  return await decryptForRead(rehydrateCompoundTypes(row, entity));
294
295
  }
295
296
  // Archive guard for the CRUD write paths. Archived streams are read-only —
296
- // ctx.appendEvent (append-event-core) already enforces this, but the
297
+ // ctx.events.append (append-event-core) already enforces this, but the
297
298
  // executor appends directly via append() and getStreamVersion() ignores
298
299
  // the archive flag, so without this check a PATCH/DELETE on an archived
299
300
  // entity would silently land an event and break the read-only contract
@@ -1,5 +1,5 @@
1
+ import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
1
2
  import { type OwnershipClause } from "../engine/ownership.ts";
2
- import type { EntityDefinition } from "../engine/types/index.ts";
3
3
  import { type Table } from "./event-store-executor-context.ts";
4
4
  export type ListWhere = {
5
5
  readonly whereSql: string[];
@@ -1,4 +1,5 @@
1
- import type { EntityDefinition, TenantId } from "../engine/types/index.ts";
1
+ import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
2
+ import type { TenantId } from "../engine/types/index.ts";
2
3
  import type { TableInfo } from "./table-info.ts";
3
4
  export declare function withStreamVersion(row: Record<string, unknown>, streamVersion: number): Record<string, unknown>;
4
5
  export declare function coerceAndRehydrateRow(raw: Record<string, unknown>, tableInfo: TableInfo, entity: EntityDefinition): Record<string, unknown>;
@@ -1,5 +1,6 @@
1
+ import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
1
2
  import { type OwnershipClause } from "../engine/ownership.ts";
2
- import type { EntityDefinition, SessionUser, TenantId } from "../engine/types/index.ts";
3
+ import type { SessionUser, TenantId } from "../engine/types/index.ts";
3
4
  import type { SearchAdapter } from "../search/types.ts";
4
5
  import type { EventStoreExecutor } from "./event-store-executor.ts";
5
6
  import type { Table } from "./event-store-executor-context.ts";
@@ -1,5 +1,6 @@
1
+ import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
1
2
  import { type OwnershipClause } from "../engine/ownership.ts";
2
- import type { EntityDefinition, SessionUser } from "../engine/types/index.ts";
3
+ import type { SessionUser } from "../engine/types/index.ts";
3
4
  import type { EventStoreExecutor } from "./event-store-executor.ts";
4
5
  import type { Table } from "./event-store-executor-context.ts";
5
6
  import type { ListWhere } from "./event-store-executor-read-filter.ts";
@@ -59,7 +59,7 @@ export function createReadVerbs(ctx) {
59
59
  const tableInfo = extractTableInfo(table);
60
60
  const encryptedRows = rawRows.map((r) => coerceAndRehydrateRow(r, tableInfo, entity));
61
61
  const decryptedRows = await Promise.all(encryptedRows.map((r) => decryptForRead(r, { degradeUndecryptable: () => { } })));
62
- // Stream version is authoritative (see detail()): ctx.appendEvent bumps the
62
+ // Stream version is authoritative (see detail()): ctx.events.append bumps the
63
63
  // stream without touching row.version, so the raw row version would hand
64
64
  // clients a stale optimistic-lock base. Rows without events keep theirs.
65
65
  const rows = decryptedRows.map((r, i) => withStreamVersion(r, streamVersions[i] ?? 0));
@@ -101,7 +101,7 @@ export function createReadVerbs(ctx) {
101
101
  return null;
102
102
  const idWhere = idFilter(payload.id);
103
103
  // Stream-version authoritative (same policy as update/Block 0):
104
- // ctx.appendEvent (lifecycle-writes like incident:post-update) bumps
104
+ // ctx.events.append (lifecycle-writes like incident:post-update) bumps
105
105
  // the stream WITHOUT touching row.version — a detail-read that hands
106
106
  // out the stale row.version dooms the next CRUD update built on it
107
107
  // (entityEdit loads detail.version as its optimistic-lock base) to a
@@ -1,21 +1,22 @@
1
1
  import { runnerCanBeginTransaction } from "./query.js";
2
- import { runInOwnTransaction } from "./tenant-db.js";
3
2
  import { isBoundTenantDb, tenantDbRunner, tenantDbWritesDeniedReason } from "./tenant-db-runner.js";
3
+ import { runInOwnSavepoint, runInOwnTransaction } from "./tenant-db-scoped-rebind.js";
4
4
  // Aborts the surrounding transaction when a verb returns a failure instead of throwing.
5
5
  class RollbackOnFailure extends Error {
6
6
  }
7
- // Pool runners have no savepoint, so append, projection and hooks would otherwise commit as
8
- // separate autocommit statements. Tx and savepoint runners already provide atomicity.
7
+ // Append, projection and hooks commit or roll back together, also when a handler swallows a failed
8
+ // verb inside its transaction. Savepoints on one tx are serialized, so parallel verbs cannot interleave.
9
9
  async function runVerbAtomically(db, run) {
10
10
  // Unbound and denied handles keep their original error, a fresh transaction would reword it.
11
- if (!isBoundTenantDb(db) ||
12
- tenantDbWritesDeniedReason(db) !== undefined ||
13
- !runnerCanBeginTransaction(tenantDbRunner(db))) {
11
+ if (!isBoundTenantDb(db) || tenantDbWritesDeniedReason(db) !== undefined) {
14
12
  return run(db);
15
13
  }
14
+ const runInOwnScope = runnerCanBeginTransaction(tenantDbRunner(db))
15
+ ? runInOwnTransaction
16
+ : runInOwnSavepoint;
16
17
  let failure;
17
18
  try {
18
- return await runInOwnTransaction(db, async (txDb) => {
19
+ return await runInOwnScope(db, async (txDb) => {
19
20
  const result = await run(txDb);
20
21
  if (!result.isSuccess) {
21
22
  failure = result;
@@ -1,8 +1,9 @@
1
+ import type { EntityId } from "@cosmicdrift/kumiko-types/identifiers";
1
2
  import type { TenantDb } from "@cosmicdrift/kumiko-types/tenant-db-types";
2
3
  import type { ZodType } from "zod";
3
4
  import type { UnavailableSelectOption } from "../engine/screen-helpers.ts";
4
- import type { EntityId } from "../engine/types/index.ts";
5
- import { type UnprocessableOpts, type WriteFailure, writeFailure } from "../errors/index.ts";
5
+ import type { UnprocessableOpts } from "../errors/classes.ts";
6
+ import { type WriteFailure, writeFailure } from "../errors/index.ts";
6
7
  import type { StoredEvent } from "../event-store/index.ts";
7
8
  import type { DbRow, DbRunner } from "./connection.ts";
8
9
  export declare function projectEntityEvent(db: TenantDb, runner: DbRunner, event: StoredEvent): Promise<void>;