@cosmicdrift/kumiko-framework 0.389.0 → 0.390.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 (616) 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/eagerload.d.ts +2 -1
  98. package/dist/db/entity-field-encryption.d.ts +1 -1
  99. package/dist/db/entity-table-meta.d.ts +1 -1
  100. package/dist/db/event-store-executor-context.d.ts +3 -1
  101. package/dist/db/event-store-executor-context.js +4 -3
  102. package/dist/db/event-store-executor-read-filter.d.ts +1 -1
  103. package/dist/db/event-store-executor-read-rows.d.ts +2 -1
  104. package/dist/db/event-store-executor-read-search.d.ts +2 -1
  105. package/dist/db/event-store-executor-read-sort.d.ts +2 -1
  106. package/dist/db/event-store-executor-read.js +2 -2
  107. package/dist/db/event-store-executor-write-atomic.js +8 -7
  108. package/dist/db/event-store-executor-write-steps.d.ts +3 -2
  109. package/dist/db/event-store-executor-write.js +7 -4
  110. package/dist/db/event-store-executor.d.ts +1 -1
  111. package/dist/db/executor-managed-tables.d.ts +7 -1
  112. package/dist/db/executor-managed-tables.js +11 -2
  113. package/dist/db/index.d.ts +29 -28
  114. package/dist/db/index.js +22 -20
  115. package/dist/db/known-table-names.d.ts +6 -0
  116. package/dist/db/known-table-names.js +21 -0
  117. package/dist/db/list-aggregate-labels.d.ts +1 -1
  118. package/dist/db/located-timestamp.d.ts +1 -1
  119. package/dist/db/located-timestamp.js +0 -5
  120. package/dist/db/migrate-generator.d.ts +17 -1
  121. package/dist/db/migrate-generator.js +50 -20
  122. package/dist/db/migrate-primary-key.d.ts +12 -0
  123. package/dist/db/migrate-primary-key.js +45 -0
  124. package/dist/db/migrate-rls-diff.d.ts +20 -0
  125. package/dist/db/migrate-rls-diff.js +61 -0
  126. package/dist/db/migrate-runner.js +4 -0
  127. package/dist/db/money.d.ts +1 -1
  128. package/dist/db/money.js +1 -1
  129. package/dist/db/parent-ref-clause.d.ts +2 -1
  130. package/dist/db/pg-error.d.ts +2 -0
  131. package/dist/db/pg-error.js +11 -0
  132. package/dist/db/queries/ddl.d.ts +3 -0
  133. package/dist/db/queries/ddl.js +34 -1
  134. package/dist/db/queries/event-store.js +4 -2
  135. package/dist/db/queries/event-tenants.d.ts +2 -0
  136. package/dist/db/queries/event-tenants.js +17 -0
  137. package/dist/db/queries/rebuild-rls-guard.d.ts +2 -0
  138. package/dist/db/queries/rebuild-rls-guard.js +29 -0
  139. package/dist/db/queries/rls-boot-catalog.d.ts +16 -0
  140. package/dist/db/queries/rls-boot-catalog.js +39 -0
  141. package/dist/db/queries/rls-catalog.d.ts +6 -0
  142. package/dist/db/queries/rls-catalog.js +82 -0
  143. package/dist/db/queries/rls-scope.d.ts +3 -0
  144. package/dist/db/queries/rls-scope.js +17 -0
  145. package/dist/db/queries/rls-shape.d.ts +10 -0
  146. package/dist/db/queries/rls-shape.js +42 -0
  147. package/dist/db/queries/session-advisory-lock.d.ts +2 -0
  148. package/dist/db/queries/session-advisory-lock.js +23 -0
  149. package/dist/db/queries/shadow-swap.d.ts +1 -2
  150. package/dist/db/queries/shadow-swap.js +9 -25
  151. package/dist/db/queries/test-stack.d.ts +4 -1
  152. package/dist/db/queries/test-stack.js +83 -2
  153. package/dist/db/query.js +11 -7
  154. package/dist/db/reference-data.d.ts +1 -1
  155. package/dist/db/reference-data.js +6 -1
  156. package/dist/db/render-ddl.d.ts +6 -2
  157. package/dist/db/render-ddl.js +7 -7
  158. package/dist/db/render-rls-ddl.d.ts +35 -0
  159. package/dist/db/render-rls-ddl.js +127 -0
  160. package/dist/db/replay-migration-sql.d.ts +3 -1
  161. package/dist/db/replay-migration-sql.js +22 -5
  162. package/dist/db/replay-rls.d.ts +25 -0
  163. package/dist/db/replay-rls.js +156 -0
  164. package/dist/db/row-level-security-boot-check.d.ts +5 -0
  165. package/dist/db/row-level-security-boot-check.js +85 -0
  166. package/dist/db/row-level-security-connection.d.ts +3 -0
  167. package/dist/db/row-level-security-connection.js +213 -0
  168. package/dist/db/row-level-security-scope.d.ts +33 -0
  169. package/dist/db/row-level-security-scope.js +127 -0
  170. package/dist/db/scope-tenant-db-to-tenant.d.ts +3 -0
  171. package/dist/db/scope-tenant-db-to-tenant.js +14 -0
  172. package/dist/db/sql-identifier.d.ts +1 -0
  173. package/dist/db/sql-identifier.js +3 -0
  174. package/dist/db/table-builder.d.ts +2 -1
  175. package/dist/db/table-builder.js +1 -1
  176. package/dist/db/table-info.js +1 -1
  177. package/dist/db/table-push.d.ts +11 -2
  178. package/dist/db/table-push.js +15 -2
  179. package/dist/db/tenant-db-runner.d.ts +3 -1
  180. package/dist/db/tenant-db-runner.js +4 -3
  181. package/dist/db/tenant-db-scoped-rebind.d.ts +10 -0
  182. package/dist/db/tenant-db-scoped-rebind.js +33 -0
  183. package/dist/db/tenant-db-statement-runner.d.ts +13 -0
  184. package/dist/db/tenant-db-statement-runner.js +48 -0
  185. package/dist/db/tenant-db.d.ts +17 -12
  186. package/dist/db/tenant-db.js +49 -120
  187. package/dist/db/tenant-id-filter.d.ts +3 -0
  188. package/dist/db/tenant-id-filter.js +30 -0
  189. package/dist/db/transaction-serialization.d.ts +6 -0
  190. package/dist/db/transaction-serialization.js +60 -0
  191. package/dist/derivatives/delete-stored-file.js +1 -1
  192. package/dist/derivatives/index.d.ts +2 -5
  193. package/dist/derivatives/index.js +2 -3
  194. package/dist/engine/access.d.ts +2 -1
  195. package/dist/engine/access.js +1 -1
  196. package/dist/engine/boot-validator/entity-handler.d.ts +2 -1
  197. package/dist/engine/boot-validator/entity-handler.js +1 -1
  198. package/dist/engine/boot-validator/entity-list-aggregate-options.d.ts +1 -1
  199. package/dist/engine/boot-validator/list-table-aggregate-options.d.ts +1 -1
  200. package/dist/engine/boot-validator/list-table-aggregate-query.d.ts +2 -1
  201. package/dist/engine/boot-validator/nav.d.ts +3 -1
  202. package/dist/engine/boot-validator/nav.js +1 -1
  203. package/dist/engine/boot-validator/ownership.d.ts +2 -1
  204. package/dist/engine/boot-validator/parent-ref.d.ts +2 -1
  205. package/dist/engine/boot-validator/projection-list-screens.d.ts +2 -1
  206. package/dist/engine/boot-validator/query-refs.d.ts +2 -1
  207. package/dist/engine/boot-validator/screens-forms.d.ts +2 -1
  208. package/dist/engine/boot-validator/screens-kind-shared.d.ts +3 -1
  209. package/dist/engine/boot-validator/screens.d.ts +2 -1
  210. package/dist/engine/boot-validator/workspaces.d.ts +3 -1
  211. package/dist/engine/bridge-stub.d.ts +5 -2
  212. package/dist/engine/bridge-stub.js +39 -24
  213. package/dist/engine/config-helpers.d.ts +2 -2
  214. package/dist/engine/config-helpers.js +0 -9
  215. package/dist/engine/constants.d.ts +0 -10
  216. package/dist/engine/constants.js +0 -8
  217. package/dist/engine/create-app.js +1 -1
  218. package/dist/engine/define-feature.d.ts +2 -1
  219. package/dist/engine/define-feature.js +1 -2
  220. package/dist/engine/define-handler.d.ts +1 -1
  221. package/dist/engine/define-step.d.ts +0 -1
  222. package/dist/engine/define-step.js +0 -3
  223. package/dist/engine/embedded-derived.d.ts +1 -1
  224. package/dist/engine/entity-handlers.d.ts +2 -1
  225. package/dist/engine/entity-handlers.js +7 -7
  226. package/dist/engine/entity-list-runtime-options.js +3 -1
  227. package/dist/engine/entity-list-searchability.d.ts +2 -1
  228. package/dist/engine/event-def.d.ts +2 -1
  229. package/dist/engine/event-helpers.d.ts +3 -1
  230. package/dist/engine/event-helpers.js +3 -3
  231. package/dist/engine/extension-names.d.ts +2 -8
  232. package/dist/engine/extension-names.js +2 -2
  233. package/dist/engine/extension-selector-plugins.d.ts +1 -1
  234. package/dist/engine/extensions/storage-provider.d.ts +1 -2
  235. package/dist/engine/extensions/user-data.d.ts +0 -8
  236. package/dist/engine/factories.d.ts +2 -1
  237. package/dist/engine/feature-builder-state.d.ts +8 -1
  238. package/dist/engine/feature-changelog.d.ts +1 -1
  239. package/dist/engine/feature-config-events-jobs.d.ts +4 -1
  240. package/dist/engine/feature-entity-handlers.d.ts +5 -1
  241. package/dist/engine/feature-manifest.js +1 -1
  242. package/dist/engine/feature-ui-extensions.d.ts +7 -1
  243. package/dist/engine/feature-ui-extensions.js +6 -4
  244. package/dist/engine/field-access.d.ts +2 -1
  245. package/dist/engine/handler-field-references.d.ts +0 -4
  246. package/dist/engine/index.d.ts +57 -54
  247. package/dist/engine/index.js +22 -27
  248. package/dist/engine/instruction-fields.d.ts +1 -1
  249. package/dist/engine/personal-data-fields.d.ts +1 -1
  250. package/dist/engine/qualified-name.d.ts +0 -1
  251. package/dist/engine/read-claim.d.ts +2 -1
  252. package/dist/engine/registry-facade.js +0 -3
  253. package/dist/engine/registry-ingest.d.ts +2 -1
  254. package/dist/engine/registry-ingest.js +1 -2
  255. package/dist/engine/registry-state.d.ts +12 -2
  256. package/dist/engine/registry-state.js +2 -3
  257. package/dist/engine/registry-validate.d.ts +3 -1
  258. package/dist/engine/registry-validate.js +7 -7
  259. package/dist/engine/run-in.d.ts +1 -1
  260. package/dist/engine/run-in.js +0 -1
  261. package/dist/engine/schema-builder.d.ts +1 -1
  262. package/dist/engine/schema-builder.js +1 -1
  263. package/dist/engine/screen-filter-ops.d.ts +2 -1
  264. package/dist/engine/screen-filter-ops.js +0 -5
  265. package/dist/engine/soft-delete-cleanup.d.ts +1 -1
  266. package/dist/engine/soft-delete-cleanup.js +4 -4
  267. package/dist/engine/state-machine.js +2 -1
  268. package/dist/engine/steps/_read-source.js +1 -1
  269. package/dist/engine/steps/aggregate-append-event.js +2 -2
  270. package/dist/engine/steps/call-feature.js +2 -2
  271. package/dist/engine/steps/mail-send.js +1 -1
  272. package/dist/engine/steps/retry.js +1 -1
  273. package/dist/engine/steps/wait-for-event.js +1 -1
  274. package/dist/engine/steps/wait.js +1 -1
  275. package/dist/engine/steps/webhook-send.js +1 -1
  276. package/dist/engine/template-variables.d.ts +2 -1
  277. package/dist/engine/transfer-adjacency.d.ts +1 -1
  278. package/dist/engine/types/index.d.ts +8 -31
  279. package/dist/engine/types/index.js +2 -10
  280. package/dist/engine/validation.d.ts +2 -1
  281. package/dist/entrypoint/index.d.ts +7 -35
  282. package/dist/entrypoint/index.js +7 -1
  283. package/dist/entrypoint/internals.d.ts +40 -0
  284. package/dist/entrypoint/internals.js +1 -0
  285. package/dist/entrypoint/row-level-security-mode-check.d.ts +3 -0
  286. package/dist/entrypoint/row-level-security-mode-check.js +19 -0
  287. package/dist/env/dry-run-internals.d.ts +10 -0
  288. package/dist/env/dry-run-internals.js +1 -0
  289. package/dist/env/dry-run.d.ts +1 -10
  290. package/dist/env/dry-run.js +2 -1
  291. package/dist/env/index.d.ts +1 -85
  292. package/dist/env/index.js +6 -50
  293. package/dist/env/internals.d.ts +87 -0
  294. package/dist/env/internals.js +53 -0
  295. package/dist/errors/classes.js +2 -2
  296. package/dist/errors/index.d.ts +5 -14
  297. package/dist/errors/index.js +4 -9
  298. package/dist/errors/member-resolution.js +1 -1
  299. package/dist/errors/reasons.d.ts +0 -1
  300. package/dist/errors/reasons.js +7 -7
  301. package/dist/errors/serialize.d.ts +0 -18
  302. package/dist/errors/serialize.js +0 -27
  303. package/dist/es-ops/index.d.ts +4 -4
  304. package/dist/es-ops/index.js +2 -2
  305. package/dist/es-ops/operations-schema.d.ts +1 -1
  306. package/dist/es-ops/types.d.ts +4 -5
  307. package/dist/event-store/admin-api-internals.d.ts +14 -0
  308. package/dist/event-store/admin-api-internals.js +1 -0
  309. package/dist/event-store/admin-api.d.ts +1 -14
  310. package/dist/event-store/admin-api.js +11 -6
  311. package/dist/event-store/archive.js +4 -2
  312. package/dist/event-store/errors.js +2 -2
  313. package/dist/event-store/event-store.js +41 -34
  314. package/dist/event-store/events-schema.js +4 -1
  315. package/dist/event-store/index.d.ts +11 -13
  316. package/dist/event-store/index.js +7 -9
  317. package/dist/event-store/rebuild-dead-letter.js +4 -2
  318. package/dist/event-store/snapshot.js +4 -3
  319. package/dist/event-store/stream-scope.d.ts +2 -0
  320. package/dist/event-store/stream-scope.js +6 -0
  321. package/dist/event-store/upcaster-dead-letter.js +4 -2
  322. package/dist/event-store/upcaster.d.ts +2 -1
  323. package/dist/files/action-form-upload.d.ts +2 -1
  324. package/dist/files/action-form-upload.js +1 -1
  325. package/dist/files/assert-file-refs-belong-to-tenant.d.ts +2 -1
  326. package/dist/files/assert-file-refs-belong-to-tenant.js +1 -1
  327. package/dist/files/bind-pending-file-refs.d.ts +2 -1
  328. package/dist/files/bind-pending-file-refs.js +2 -1
  329. package/dist/files/bind-referenced-files.write.js +1 -1
  330. package/dist/files/create-file-ref-from-bytes.d.ts +6 -3
  331. package/dist/files/create-file-ref-from-bytes.js +3 -3
  332. package/dist/files/create-file-ref.d.ts +3 -2
  333. package/dist/files/create-file-ref.js +2 -2
  334. package/dist/files/dispose-removed-file-refs.d.ts +2 -1
  335. package/dist/files/dispose-removed-file-refs.js +1 -1
  336. package/dist/files/feature.js +2 -2
  337. package/dist/files/file-handle.js +1 -1
  338. package/dist/files/file-routes.d.ts +1 -1
  339. package/dist/files/file-routes.js +12 -2
  340. package/dist/files/index.d.ts +11 -13
  341. package/dist/files/index.js +5 -7
  342. package/dist/files/load-save-file-refs.d.ts +2 -1
  343. package/dist/files/load-save-file-refs.js +1 -1
  344. package/dist/files/provider-resolver.d.ts +5 -4
  345. package/dist/files/provider-resolver.js +4 -4
  346. package/dist/files/storage-tracking.js +4 -2
  347. package/dist/files/sweep-orphaned-uploads.d.ts +1 -1
  348. package/dist/files/sweep-orphaned-uploads.js +1 -1
  349. package/dist/i18n/index.d.ts +2 -8
  350. package/dist/i18n/index.js +1 -1
  351. package/dist/i18n/internals.d.ts +7 -0
  352. package/dist/i18n/internals.js +1 -0
  353. package/dist/i18n/required-surface-keys.d.ts +4 -1
  354. package/dist/internal/row-level-security.d.ts +3 -0
  355. package/dist/internal/row-level-security.js +3 -0
  356. package/dist/internal/stack-support.d.ts +1 -1
  357. package/dist/internal/stack-support.js +1 -1
  358. package/dist/internal/testing.d.ts +1 -0
  359. package/dist/internal/testing.js +9 -5
  360. package/dist/jobs/index.d.ts +1 -1
  361. package/dist/jobs/job-append-event.d.ts +12 -0
  362. package/dist/jobs/job-append-event.js +30 -0
  363. package/dist/jobs/job-runner-context.d.ts +2 -2
  364. package/dist/jobs/job-runner-context.js +46 -16
  365. package/dist/jobs/job-runner-execution.d.ts +1 -1
  366. package/dist/jobs/job-runner-execution.js +6 -4
  367. package/dist/jobs/job-runner-gates.d.ts +1 -1
  368. package/dist/jobs/job-runner-gates.js +0 -1
  369. package/dist/jobs/job-runner-helpers.d.ts +11 -4
  370. package/dist/jobs/job-runner-helpers.js +10 -1
  371. package/dist/jobs/job-runner-scope.d.ts +5 -2
  372. package/dist/jobs/job-runner-types.d.ts +2 -1
  373. package/dist/jobs/job-runner.js +19 -6
  374. package/dist/jobs/max-per-tenant.d.ts +1 -1
  375. package/dist/logging/index.d.ts +1 -2
  376. package/dist/logging/index.js +1 -1
  377. package/dist/logging/pino-logger.js +1 -1
  378. package/dist/logging/utils.d.ts +1 -0
  379. package/dist/logging/utils.js +6 -0
  380. package/dist/migrations/index.d.ts +3 -3
  381. package/dist/migrations/index.js +3 -3
  382. package/dist/migrations/pending-rebuilds.d.ts +1 -1
  383. package/dist/migrations/pending-rebuilds.js +1 -1
  384. package/dist/migrations/pii-event-backfill.js +4 -1
  385. package/dist/observability/escape-hatch-report.d.ts +2 -1
  386. package/dist/observability/index.d.ts +7 -15
  387. package/dist/observability/index.js +4 -12
  388. package/dist/observability/metric-validator.js +1 -1
  389. package/dist/observability/metrics-handle.d.ts +0 -1
  390. package/dist/observability/metrics-handle.js +2 -15
  391. package/dist/observability/recording-tracer.d.ts +1 -2
  392. package/dist/observability/recording-tracer.js +0 -6
  393. package/dist/observability/sensitive-filter.d.ts +0 -2
  394. package/dist/observability/sensitive-filter.js +0 -29
  395. package/dist/pipeline/active-membership.d.ts +2 -1
  396. package/dist/pipeline/active-membership.js +6 -2
  397. package/dist/pipeline/append-event-core.d.ts +2 -1
  398. package/dist/pipeline/append-event-core.js +2 -2
  399. package/dist/pipeline/attach-outside-transaction.d.ts +3 -0
  400. package/dist/pipeline/attach-outside-transaction.js +13 -0
  401. package/dist/pipeline/auth-claims-resolver.d.ts +2 -1
  402. package/dist/pipeline/cascade-handler.d.ts +2 -1
  403. package/dist/pipeline/cascade-handler.js +3 -2
  404. package/dist/pipeline/config-accessor-factory.d.ts +1 -1
  405. package/dist/pipeline/config-accessor-factory.js +1 -1
  406. package/dist/pipeline/context-services.d.ts +56 -0
  407. package/dist/pipeline/context-services.js +73 -0
  408. package/dist/pipeline/dispatch-batch.d.ts +2 -1
  409. package/dist/pipeline/dispatch-batch.js +31 -18
  410. package/dist/pipeline/dispatch-query.js +5 -4
  411. package/dist/pipeline/dispatch-scope.d.ts +6 -0
  412. package/dist/pipeline/dispatch-scope.js +27 -0
  413. package/dist/pipeline/dispatch-shared.d.ts +13 -9
  414. package/dist/pipeline/dispatch-shared.js +75 -198
  415. package/dist/pipeline/dispatch-stream.js +8 -4
  416. package/dist/pipeline/dispatch-write.d.ts +2 -1
  417. package/dist/pipeline/dispatch-write.js +10 -8
  418. package/dist/pipeline/dispatcher-utils.d.ts +3 -1
  419. package/dist/pipeline/dispatcher-utils.js +8 -2
  420. package/dist/pipeline/dispatcher.d.ts +3 -2
  421. package/dist/pipeline/dispatcher.js +4 -3
  422. package/dist/pipeline/event-consumer-state.d.ts +0 -1
  423. package/dist/pipeline/event-dispatcher-delivery.d.ts +1 -1
  424. package/dist/pipeline/event-dispatcher-delivery.js +4 -2
  425. package/dist/pipeline/event-dispatcher.d.ts +2 -2
  426. package/dist/pipeline/event-dispatcher.js +7 -3
  427. package/dist/pipeline/event-retention.js +59 -56
  428. package/dist/pipeline/handler-access-error.js +2 -1
  429. package/dist/pipeline/handler-context-auth.d.ts +5 -6
  430. package/dist/pipeline/handler-context-auth.js +7 -4
  431. package/dist/pipeline/handler-context-config.d.ts +2 -5
  432. package/dist/pipeline/handler-context-config.js +2 -2
  433. package/dist/pipeline/handler-context-db.d.ts +32 -7
  434. package/dist/pipeline/handler-context-db.js +26 -17
  435. package/dist/pipeline/handler-context-dispatch.d.ts +13 -7
  436. package/dist/pipeline/handler-context-dispatch.js +9 -8
  437. package/dist/pipeline/handler-context-events.d.ts +22 -36
  438. package/dist/pipeline/handler-context-events.js +36 -31
  439. package/dist/pipeline/handler-context-member-resolution.d.ts +2 -0
  440. package/dist/pipeline/handler-context-member-resolution.js +110 -0
  441. package/dist/pipeline/handler-context-notify.d.ts +3 -2
  442. package/dist/pipeline/handler-context-notify.js +1 -1
  443. package/dist/pipeline/handler-context-observability.d.ts +2 -4
  444. package/dist/pipeline/handler-context-observability.js +7 -5
  445. package/dist/pipeline/handler-context-phase-denials.d.ts +11 -0
  446. package/dist/pipeline/handler-context-phase-denials.js +98 -0
  447. package/dist/pipeline/handler-context-scope.d.ts +7 -10
  448. package/dist/pipeline/handler-context-scope.js +18 -20
  449. package/dist/pipeline/index.d.ts +9 -21
  450. package/dist/pipeline/index.js +5 -7
  451. package/dist/pipeline/irreversible-operation-gate.d.ts +1 -1
  452. package/dist/pipeline/irreversible-operation-gate.js +2 -2
  453. package/dist/pipeline/lifecycle-pipeline.d.ts +8 -7
  454. package/dist/pipeline/lifecycle-pipeline.js +9 -15
  455. package/dist/pipeline/member-read-only-transaction.js +11 -3
  456. package/dist/pipeline/member-reader.d.ts +1 -1
  457. package/dist/pipeline/member-reader.js +2 -2
  458. package/dist/pipeline/msp-rebuild.js +5 -3
  459. package/dist/pipeline/multi-stream-apply-context.js +3 -2
  460. package/dist/pipeline/notify-scope.d.ts +2 -1
  461. package/dist/pipeline/pre-handler-gates.d.ts +2 -1
  462. package/dist/pipeline/pre-transaction-reservations.d.ts +3 -3
  463. package/dist/pipeline/pre-transaction-reservations.js +9 -7
  464. package/dist/pipeline/projection-rebuild.js +12 -5
  465. package/dist/pipeline/projection-state.d.ts +0 -6
  466. package/dist/pipeline/projection-state.js +0 -5
  467. package/dist/pipeline/projections-runner.d.ts +2 -1
  468. package/dist/pipeline/projections-runner.js +10 -6
  469. package/dist/pipeline/system-hooks.d.ts +3 -1
  470. package/dist/pipeline/system-hooks.js +6 -4
  471. package/dist/pipeline/system-identity-switch.d.ts +4 -2
  472. package/dist/pipeline/system-identity-switch.js +51 -36
  473. package/dist/pipeline/typed-append-event.d.ts +2 -0
  474. package/dist/pipeline/typed-append-event.js +4 -0
  475. package/dist/pipeline/write-origin.js +2 -2
  476. package/dist/pipeline/write-result-masking.d.ts +1 -1
  477. package/dist/random/index.d.ts +1 -2
  478. package/dist/random/index.js +1 -2
  479. package/dist/rate-limit/bucket.d.ts +2 -1
  480. package/dist/rate-limit/middleware.js +2 -1
  481. package/dist/redis/index.d.ts +3 -4
  482. package/dist/redis/index.js +2 -2
  483. package/dist/schema-cli/apply-command.js +5 -2
  484. package/dist/schema-cli/baseline-command.js +7 -2
  485. package/dist/schema-cli/generate-command.js +7 -9
  486. package/dist/schema-cli/shared.d.ts +4 -0
  487. package/dist/schema-cli/shared.js +21 -2
  488. package/dist/schema-cli/status-command.js +8 -2
  489. package/dist/schema-cli/validate-command.js +52 -4
  490. package/dist/schema-cli.d.ts +0 -1
  491. package/dist/search/configure-tenant-index.d.ts +1 -3
  492. package/dist/search/configure-tenant-index.js +0 -4
  493. package/dist/search/index.d.ts +1 -4
  494. package/dist/search/index.js +0 -2
  495. package/dist/search/meilisearch-adapter-internals.d.ts +8 -0
  496. package/dist/search/meilisearch-adapter-internals.js +9 -0
  497. package/dist/search/meilisearch-adapter.d.ts +1 -8
  498. package/dist/search/meilisearch-adapter.js +6 -7
  499. package/dist/search/meilisearch-peer.d.ts +2 -0
  500. package/dist/search/meilisearch-peer.js +5 -0
  501. package/dist/search/reindex-entity.js +93 -87
  502. package/dist/secrets/index.d.ts +3 -4
  503. package/dist/secrets/index.js +2 -3
  504. package/dist/seeding/index.d.ts +2 -2
  505. package/dist/seeding/index.js +1 -2
  506. package/dist/time/index.d.ts +2 -2
  507. package/dist/time/index.js +1 -1
  508. package/dist/time/tz-context.d.ts +0 -5
  509. package/dist/time/tz-context.js +0 -9
  510. package/dist/ui-types/index.d.ts +8 -11
  511. package/dist/ui-types/index.js +3 -3
  512. package/dist/upgrade-changelog.d.ts +3 -2
  513. package/dist/upgrade-changelog.js +12 -8
  514. package/dist/upgrade-cli-internals.d.ts +8 -0
  515. package/dist/upgrade-cli-internals.js +63 -0
  516. package/dist/upgrade-cli.d.ts +1 -9
  517. package/dist/upgrade-cli.js +10 -64
  518. package/dist/utils/import-optional-peer.d.ts +6 -0
  519. package/dist/utils/import-optional-peer.js +19 -0
  520. package/dist/utils/index.d.ts +1 -4
  521. package/dist/utils/index.js +1 -4
  522. package/package.json +22 -18
  523. package/src/changes.json +206 -0
  524. package/dist/crypto/request-kms-cache.d.ts +0 -8
  525. package/dist/crypto/request-kms-cache.js +0 -21
  526. package/dist/engine/codemod/index.d.ts +0 -2
  527. package/dist/engine/codemod/index.js +0 -1
  528. package/dist/engine/codemod/pipeline-codemod.d.ts +0 -50
  529. package/dist/engine/codemod/pipeline-codemod.js +0 -536
  530. package/dist/engine/feature-ast/entity-field-types.d.ts +0 -13
  531. package/dist/engine/feature-ast/entity-field-types.js +0 -35
  532. package/dist/engine/feature-ast/extractors/ai-steps.d.ts +0 -6
  533. package/dist/engine/feature-ast/extractors/ai-steps.js +0 -254
  534. package/dist/engine/feature-ast/extractors/events.d.ts +0 -5
  535. package/dist/engine/feature-ast/extractors/events.js +0 -275
  536. package/dist/engine/feature-ast/extractors/handlers.d.ts +0 -24
  537. package/dist/engine/feature-ast/extractors/handlers.js +0 -358
  538. package/dist/engine/feature-ast/extractors/hooks.d.ts +0 -24
  539. package/dist/engine/feature-ast/extractors/hooks.js +0 -248
  540. package/dist/engine/feature-ast/extractors/index.d.ts +0 -14
  541. package/dist/engine/feature-ast/extractors/index.js +0 -13
  542. package/dist/engine/feature-ast/extractors/jobs-routes.d.ts +0 -7
  543. package/dist/engine/feature-ast/extractors/jobs-routes.js +0 -145
  544. package/dist/engine/feature-ast/extractors/projections-screens.d.ts +0 -10
  545. package/dist/engine/feature-ast/extractors/projections-screens.js +0 -195
  546. package/dist/engine/feature-ast/extractors/resolve-import.d.ts +0 -21
  547. package/dist/engine/feature-ast/extractors/resolve-import.js +0 -180
  548. package/dist/engine/feature-ast/extractors/round1.d.ts +0 -11
  549. package/dist/engine/feature-ast/extractors/round1.js +0 -87
  550. package/dist/engine/feature-ast/extractors/round2.d.ts +0 -7
  551. package/dist/engine/feature-ast/extractors/round2.js +0 -191
  552. package/dist/engine/feature-ast/extractors/round3.d.ts +0 -23
  553. package/dist/engine/feature-ast/extractors/round3.js +0 -292
  554. package/dist/engine/feature-ast/extractors/round5.d.ts +0 -9
  555. package/dist/engine/feature-ast/extractors/round5.js +0 -68
  556. package/dist/engine/feature-ast/extractors/round6.d.ts +0 -4
  557. package/dist/engine/feature-ast/extractors/round6.js +0 -17
  558. package/dist/engine/feature-ast/extractors/shared.d.ts +0 -94
  559. package/dist/engine/feature-ast/extractors/shared.js +0 -343
  560. package/dist/engine/feature-ast/index.d.ts +0 -16
  561. package/dist/engine/feature-ast/index.js +0 -10
  562. package/dist/engine/feature-ast/parse.d.ts +0 -31
  563. package/dist/engine/feature-ast/parse.js +0 -485
  564. package/dist/engine/feature-ast/patch.d.ts +0 -212
  565. package/dist/engine/feature-ast/patch.js +0 -611
  566. package/dist/engine/feature-ast/patcher.d.ts +0 -217
  567. package/dist/engine/feature-ast/patcher.js +0 -281
  568. package/dist/engine/feature-ast/pattern-change-schema.d.ts +0 -1238
  569. package/dist/engine/feature-ast/pattern-change-schema.js +0 -1072
  570. package/dist/engine/feature-ast/pattern-id-matchers.d.ts +0 -7
  571. package/dist/engine/feature-ast/pattern-id-matchers.js +0 -211
  572. package/dist/engine/feature-ast/pattern-id-schema.d.ts +0 -251
  573. package/dist/engine/feature-ast/pattern-id-schema.js +0 -166
  574. package/dist/engine/feature-ast/patterns.d.ts +0 -322
  575. package/dist/engine/feature-ast/patterns.js +0 -107
  576. package/dist/engine/feature-ast/render.d.ts +0 -47
  577. package/dist/engine/feature-ast/render.js +0 -666
  578. package/dist/engine/feature-ast/source-location.d.ts +0 -17
  579. package/dist/engine/feature-ast/source-location.js +0 -27
  580. package/dist/engine/pattern-library/index.d.ts +0 -2
  581. package/dist/engine/pattern-library/index.js +0 -3
  582. package/dist/engine/pattern-library/library.d.ts +0 -14
  583. package/dist/engine/pattern-library/library.js +0 -94
  584. package/dist/engine/pattern-library/mixed-schemas.d.ts +0 -16
  585. package/dist/engine/pattern-library/mixed-schemas.js +0 -559
  586. package/dist/engine/pattern-library/opaque-schemas.d.ts +0 -7
  587. package/dist/engine/pattern-library/opaque-schemas.js +0 -115
  588. package/dist/engine/pattern-library/shared-fields.d.ts +0 -122
  589. package/dist/engine/pattern-library/shared-fields.js +0 -88
  590. package/dist/engine/pattern-library/static-schemas.d.ts +0 -21
  591. package/dist/engine/pattern-library/static-schemas.js +0 -487
  592. package/dist/engine/pattern-library/types.d.ts +0 -158
  593. package/dist/engine/pattern-library/types.js +0 -36
  594. package/dist/engine/resolve-config-or-param.d.ts +0 -18
  595. package/dist/engine/resolve-config-or-param.js +0 -76
  596. package/src/scripts/codemod/README.md +0 -187
  597. package/src/scripts/codemod/codemod-support.ts +0 -334
  598. package/src/scripts/codemod/collect-source-files.ts +0 -48
  599. package/src/scripts/codemod/crypto-shredding-testing-move.ts +0 -119
  600. package/src/scripts/codemod/db-factory.ts +0 -810
  601. package/src/scripts/codemod/feature-factory.ts +0 -429
  602. package/src/scripts/codemod/feature-layout.ts +0 -424
  603. package/src/scripts/codemod/import-extensions.ts +0 -118
  604. package/src/scripts/codemod/migrate-cross-tenant.ts +0 -328
  605. package/src/scripts/codemod/migrate-db-raw.ts +0 -401
  606. package/src/scripts/codemod/migrate-open-to-all.ts +0 -235
  607. package/src/scripts/codemod/object-form.ts +0 -746
  608. package/src/scripts/codemod/package-merge.ts +0 -299
  609. package/src/scripts/codemod/pii-personal-migration.ts +0 -816
  610. package/src/scripts/codemod/remove-engine-payload-casts.ts +0 -318
  611. package/src/scripts/codemod/s3-imports.ts +0 -236
  612. package/src/scripts/codemod/tenant-db-options.ts +0 -454
  613. package/src/scripts/codemod/testing-injection.ts +0 -215
  614. package/src/scripts/codemod/testing-move-targets.ts +0 -161
  615. package/src/scripts/codemod/testing-move.ts +0 -542
  616. package/src/scripts/codemod/unprocessable-error-details-reason.ts +0 -411
package/dist/db/money.js CHANGED
@@ -8,6 +8,7 @@
8
8
  // MINOR_UNIT_SCALE units for callers that need scale-exact
9
9
  // comparisons (e.g. invoice sums) instead of round-tripping the
10
10
  // float amount through /100·*100.
11
+ import { DEFAULT_CURRENCIES } from "../engine/field-helpers.js";
11
12
  // `amountMinor` stays as a @deprecated alias of the same
12
13
  // value until the remaining consumers
13
14
  // (renderer/web/primitives/index.tsx,
@@ -31,7 +32,6 @@
31
32
  // amount column (legacy DB convention for money — `SUM(buying_price)` remains
32
33
  // idiomatic). `<name>Currency` is the additional column.
33
34
  import { MINOR_UNIT_SCALE, toMinorUnits } from "../engine/minor-units.js";
34
- import { DEFAULT_CURRENCIES } from "../engine/types/index.js";
35
35
  const FRAMEWORK_DEFAULT_CURRENCY = DEFAULT_CURRENCIES[0]; // "EUR"
36
36
  export { toMinorUnits };
37
37
  function toMajorUnits(amountScaled) {
@@ -1,6 +1,7 @@
1
1
  import type { ParentVisibilityOption } from "@cosmicdrift/kumiko-types/event-store-executor-types";
2
+ import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
2
3
  import { type OwnershipClause } from "../engine/ownership.ts";
3
- import type { EntityDefinition, SessionUser } from "../engine/types/index.ts";
4
+ import type { SessionUser } from "../engine/types/index.ts";
4
5
  import type { Table } from "./event-store-executor-context.ts";
5
6
  import type { TenantDb } from "./tenant-db.ts";
6
7
  export type { ParentVisibilityOption } from "@cosmicdrift/kumiko-types/event-store-executor-types";
@@ -3,8 +3,10 @@ export type PgErrorInfo = {
3
3
  readonly constraint_name: string | undefined;
4
4
  };
5
5
  export declare function extractPgError(e: unknown): PgErrorInfo | null;
6
+ export declare function isInvalidDateTimeValue(e: unknown): boolean;
6
7
  export declare function isUniqueViolation(e: unknown): boolean;
7
8
  export declare function isTableAlreadyExists(e: unknown): boolean;
8
9
  export declare function isLockNotAvailable(e: unknown): boolean;
9
10
  export declare function isUndefinedTable(e: unknown): boolean;
11
+ export declare function isRowLevelSecurityViolation(e: unknown): boolean;
10
12
  export declare function constraintOf(e: unknown): string | undefined;
@@ -31,6 +31,13 @@ export function extractPgError(e) {
31
31
  }
32
32
  return null;
33
33
  }
34
+ // PG SQLSTATE 22007 invalid_datetime_format and 22008 datetime_field_overflow: the
35
+ // column type rejects the value ("not-a-date", or "2026-02-30" for a date column).
36
+ const INVALID_DATETIME_VALUE_CODES = new Set(["22007", "22008"]);
37
+ export function isInvalidDateTimeValue(e) {
38
+ const code = extractPgError(e)?.code;
39
+ return code !== undefined && INVALID_DATETIME_VALUE_CODES.has(code);
40
+ }
34
41
  export function isUniqueViolation(e) {
35
42
  return extractPgError(e)?.code === "23505";
36
43
  }
@@ -55,6 +62,10 @@ export function isLockNotAvailable(e) {
55
62
  export function isUndefinedTable(e) {
56
63
  return extractPgError(e)?.code === "42P01";
57
64
  }
65
+ // PG SQLSTATE 42501 insufficient_privilege: a row failing a row-level-security WITH CHECK policy.
66
+ export function isRowLevelSecurityViolation(e) {
67
+ return extractPgError(e)?.code === "42501";
68
+ }
58
69
  export function constraintOf(e) {
59
70
  return extractPgError(e)?.constraint_name;
60
71
  }
@@ -1,4 +1,7 @@
1
+ import type { DbConnection } from "../connection.ts";
2
+ import type { EntityTableMeta } from "../entity-table-meta.ts";
1
3
  import type { AnyDb } from "../query.ts";
2
4
  export declare function executeDdlStatement(db: AnyDb, sqlText: string): Promise<void>;
3
5
  export declare function alterTableAddColumn(db: AnyDb, tableName: string, columnName: string, columnType: string, defaultClause: string, notNull: string, ifNotExists?: boolean): Promise<void>;
4
6
  export declare function createIndexIfNotExists(db: AnyDb, indexKind: "UNIQUE INDEX" | "INDEX", indexName: string, tableName: string, columnList: string, whereSql?: string): Promise<void>;
7
+ export declare function applyTableRowLevelSecurity(db: DbConnection, meta: EntityTableMeta): Promise<void>;
@@ -1,4 +1,7 @@
1
- import { asRawClient } from "../query.js";
1
+ import { asRawClient, transaction } from "../query.js";
2
+ import { renderTableRowLevelSecurityDdl, tenantRowLevelSecurityFor } from "../render-rls-ddl.js";
3
+ import { quoteIdent } from "../sql-identifier.js";
4
+ import { readRowLevelSecurityShape } from "./rls-shape.js";
2
5
  import { quoteTableIdent } from "./table-ops.js";
3
6
  // Generic DDL helpers used on the prod-boot path (db/table-push.ts's
4
7
  // unsafePushTables, event-consumer-state.ts's multi-instance backfill) —
@@ -20,3 +23,33 @@ export async function createIndexIfNotExists(db, indexKind, indexName, tableName
20
23
  const where = whereSql !== undefined ? ` WHERE ${whereSql}` : "";
21
24
  await asRawClient(db).unsafe(`CREATE ${indexKind} IF NOT EXISTS ${quoteTableIdent(indexName)} ON ${quoteTableIdent(tableName)} (${columnList})${where}`);
22
25
  }
26
+ // Policy texts are not compared: a hand-edited kumiko policy is the operator's to keep, and the
27
+ // catalog check of the boot path reports it. Foreign policies are ignored.
28
+ function hasKumikoRowLevelSecurity(state, meta) {
29
+ const spec = tenantRowLevelSecurityFor(meta);
30
+ if (state === undefined || spec === undefined || !state.enabled || !state.forced)
31
+ return false;
32
+ return spec.policies.every((expected) => state.policies.some((actual) => actual.name === expected.name &&
33
+ actual.kind === expected.kind &&
34
+ actual.command === expected.command));
35
+ }
36
+ export async function applyTableRowLevelSecurity(db, meta) {
37
+ const statements = renderTableRowLevelSecurityDdl(meta);
38
+ // skip: no tenant_id column, nothing to enforce
39
+ if (statements.length === 0)
40
+ return;
41
+ // skip: already applied, so a repeated push takes no lock and runs no DDL
42
+ if (hasKumikoRowLevelSecurity(await readRowLevelSecurityShape(db, meta.tableName), meta)) {
43
+ return;
44
+ }
45
+ await transaction(db, async (tx) => {
46
+ // Self-conflicting lock: parallel pushes queue up instead of failing CREATE POLICY with duplicate_object.
47
+ await tx.unsafe(`LOCK TABLE ${quoteIdent(meta.tableName)} IN SHARE ROW EXCLUSIVE MODE`);
48
+ // skip: a parallel push applied it while this one waited for the lock
49
+ if (hasKumikoRowLevelSecurity(await readRowLevelSecurityShape(tx, meta.tableName), meta)) {
50
+ return;
51
+ }
52
+ for (const statement of statements)
53
+ await tx.unsafe(statement);
54
+ });
55
+ }
@@ -1,6 +1,7 @@
1
1
  import { createFallbackLogger } from "../../logging/utils.js";
2
2
  import { constraintOf, isLockNotAvailable, isTableAlreadyExists, isUniqueViolation, } from "../pg-error.js";
3
3
  import { asRawClient, runInNewTransaction, runnerCanBeginTransaction, unsafeReadRetrying, } from "../query.js";
4
+ import { innerOfScopedRunner, outsideRowLevelSecurityScope } from "../row-level-security-scope.js";
4
5
  import { withoutStatementTimeout } from "../statement-timeout.js";
5
6
  import { toPostgresArrayLiteral } from "./postgres-array-literal.js";
6
7
  const log = createFallbackLogger("event-store");
@@ -51,8 +52,9 @@ async function indexValidity(client, indexName = IDEMPOTENCY_INDEX_NAME) {
51
52
  `WHERE c.relname = $1 AND i.indrelid = '"kumiko_events"'::regclass`, [indexName]);
52
53
  return rows[0]?.indisvalid;
53
54
  }
55
+ // Index DDL carries no tenant data and CONCURRENTLY cannot run in a transaction, so both the pinned runner and the ambient scope are left.
54
56
  export function ensureIdempotencyKeyIndex(db) {
55
- return withoutStatementTimeout(db, buildIdempotencyKeyIndex);
57
+ return outsideRowLevelSecurityScope(() => withoutStatementTimeout(innerOfScopedRunner(db) ?? db, buildIdempotencyKeyIndex));
56
58
  }
57
59
  async function buildIdempotencyKeyIndex(db) {
58
60
  const client = asRawClient(db);
@@ -96,7 +98,7 @@ async function buildIdempotencyKeyIndex(db) {
96
98
  // tenant), which no other index covers: without this it scans every event.
97
99
  // Same CONCURRENTLY + INVALID-rebuild handling as the idempotency index.
98
100
  export function ensureMembershipPayloadTenantIndex(db) {
99
- return withoutStatementTimeout(db, buildMembershipPayloadTenantIndex);
101
+ return outsideRowLevelSecurityScope(() => withoutStatementTimeout(innerOfScopedRunner(db) ?? db, buildMembershipPayloadTenantIndex));
100
102
  }
101
103
  async function buildMembershipPayloadTenantIndex(db) {
102
104
  const client = asRawClient(db);
@@ -0,0 +1,2 @@
1
+ import type { AnyDb } from "../query.ts";
2
+ export declare function listEventTenantIds(db: AnyDb): Promise<readonly string[]>;
@@ -0,0 +1,17 @@
1
+ import { unsafeReadRetrying } from "../query.js";
2
+ // Every tenant_id that owns at least one event. Loose index scan: one probe on
3
+ // events_tenant_type_idx (leading column tenant_id) per distinct value, so the cost follows the
4
+ // number of tenants, not the number of events.
5
+ export async function listEventTenantIds(db) {
6
+ const rows = await unsafeReadRetrying(db, `WITH RECURSIVE tenants AS (
7
+ (SELECT "tenant_id" FROM "kumiko_events" ORDER BY "tenant_id" LIMIT 1)
8
+ UNION ALL
9
+ SELECT (SELECT e."tenant_id" FROM "kumiko_events" e
10
+ WHERE e."tenant_id" > tenants."tenant_id"
11
+ ORDER BY e."tenant_id" LIMIT 1)
12
+ FROM tenants
13
+ WHERE tenants."tenant_id" IS NOT NULL
14
+ )
15
+ SELECT "tenant_id"::text AS tenant_id FROM tenants WHERE "tenant_id" IS NOT NULL`, []);
16
+ return rows.map((row) => row.tenant_id);
17
+ }
@@ -0,0 +1,2 @@
1
+ import { type AnyDb } from "../query.ts";
2
+ export declare function assertRebuildableRowLevelSecurity(tx: AnyDb, tableName: string): Promise<void>;
@@ -0,0 +1,29 @@
1
+ // First statement of a rebuild tx. The rebuild runs in system scope, which the tenant isolation
2
+ // policy admits for every row; a table whose policies differ from the framework's shape could hide
3
+ // rows from the guards and let the swap drop them, so it is refused before anything is read.
4
+ import { asRawClient } from "../query.js";
5
+ import { TENANT_ISOLATION_POLICY } from "../render-rls-ddl.js";
6
+ import { readRowLevelSecurityShape } from "./rls-shape.js";
7
+ import { quoteTableIdent } from "./table-ops.js";
8
+ export async function assertRebuildableRowLevelSecurity(tx, tableName) {
9
+ // SHARE UPDATE EXCLUSIVE conflicts with CREATE/ALTER/DROP POLICY and ENABLE/FORCE (ACCESS EXCLUSIVE) but not with readers, and with itself: a second rebuild waits instead of both holding a shared lock and deadlocking on the swap's ACCESS EXCLUSIVE.
10
+ await asRawClient(tx).unsafe(`LOCK TABLE public.${quoteTableIdent(tableName)} IN SHARE UPDATE EXCLUSIVE MODE`);
11
+ const state = await readRowLevelSecurityShape(tx, tableName);
12
+ // skip: table has no row-level security, nothing to verify
13
+ if (state === undefined || !state.enabled)
14
+ return;
15
+ const isolation = state.policies.find((policy) => policy.name === TENANT_ISOLATION_POLICY);
16
+ const hasRestrictive = state.policies.some((policy) => policy.kind === "RESTRICTIVE");
17
+ const isolationIntact = isolation !== undefined &&
18
+ isolation.kind === "PERMISSIVE" &&
19
+ isolation.command === "ALL" &&
20
+ isolation.appliesToPublic;
21
+ // skip: framework policy intact, rebuild reads are safe
22
+ if (isolationIntact && !hasRestrictive)
23
+ return;
24
+ const listed = state.policies
25
+ .map((policy) => `${policy.name} (${policy.kind} ${policy.command})`)
26
+ .join(", ");
27
+ throw new Error(`rebuild: public.${tableName} has row level security that a system scope rebuild cannot rely on ` +
28
+ `(needs a PERMISSIVE ALL ${TENANT_ISOLATION_POLICY} policy for PUBLIC and no RESTRICTIVE policy); policies: ${listed || "(none)"}`);
29
+ }
@@ -0,0 +1,16 @@
1
+ import type { AnyDb } from "../query.ts";
2
+ export type ConnectedRoleAttributes = {
3
+ readonly roleName: string;
4
+ readonly superuser: boolean;
5
+ readonly bypassRowLevelSecurity: boolean;
6
+ };
7
+ export type TenantTableRowLevelSecurity = {
8
+ readonly tableName: string;
9
+ readonly enabled: boolean;
10
+ readonly forced: boolean;
11
+ readonly hasIsolationPolicy: boolean;
12
+ readonly hasSystemReadPolicy: boolean;
13
+ };
14
+ export declare function readConnectedRoleAttributes(db: AnyDb): Promise<ConnectedRoleAttributes | undefined>;
15
+ export declare function readTenantTablesRowLevelSecurity(db: AnyDb): Promise<readonly TenantTableRowLevelSecurity[]>;
16
+ export declare function readTablesWithRowLevelSecurityEnabled(db: AnyDb): Promise<readonly string[]>;
@@ -0,0 +1,39 @@
1
+ import { asRawClient } from "../query.js";
2
+ import { SYSTEM_TENANT_READ_POLICY, TENANT_ISOLATION_POLICY } from "../render-rls-ddl.js";
3
+ export async function readConnectedRoleAttributes(db) {
4
+ const rows = await asRawClient(db).unsafe(`SELECT rolname, rolsuper, rolbypassrls FROM pg_roles WHERE rolname = current_user`);
5
+ const row = rows[0];
6
+ if (row === undefined)
7
+ return undefined;
8
+ return {
9
+ roleName: row.rolname,
10
+ superuser: row.rolsuper,
11
+ bypassRowLevelSecurity: row.rolbypassrls,
12
+ };
13
+ }
14
+ export async function readTenantTablesRowLevelSecurity(db) {
15
+ const rows = await asRawClient(db).unsafe(`SELECT c.relname, c.relrowsecurity, c.relforcerowsecurity,
16
+ EXISTS (SELECT 1 FROM pg_policies p
17
+ WHERE p.schemaname = 'public' AND p.tablename = c.relname AND p.policyname = $1) AS has_isolation,
18
+ EXISTS (SELECT 1 FROM pg_policies p
19
+ WHERE p.schemaname = 'public' AND p.tablename = c.relname AND p.policyname = $2) AS has_system_read
20
+ FROM pg_class c
21
+ JOIN pg_namespace n ON n.oid = c.relnamespace
22
+ WHERE n.nspname = 'public' AND c.relkind IN ('r', 'p')
23
+ AND EXISTS (SELECT 1 FROM pg_attribute a
24
+ WHERE a.attrelid = c.oid AND a.attname = 'tenant_id' AND a.attnum > 0 AND NOT a.attisdropped)
25
+ ORDER BY c.relname COLLATE "C"`, [TENANT_ISOLATION_POLICY, SYSTEM_TENANT_READ_POLICY]);
26
+ return rows.map((row) => ({
27
+ tableName: row.relname,
28
+ enabled: row.relrowsecurity,
29
+ forced: row.relforcerowsecurity,
30
+ hasIsolationPolicy: row.has_isolation,
31
+ hasSystemReadPolicy: row.has_system_read,
32
+ }));
33
+ }
34
+ export async function readTablesWithRowLevelSecurityEnabled(db) {
35
+ const rows = await asRawClient(db).unsafe(`SELECT c.relname FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
36
+ WHERE n.nspname = 'public' AND c.relkind IN ('r', 'p') AND c.relrowsecurity
37
+ ORDER BY c.relname COLLATE "C"`);
38
+ return rows.map((row) => row.relname);
39
+ }
@@ -0,0 +1,6 @@
1
+ import { type AnyDb } from "../query.ts";
2
+ import { type RowLevelSecurityFlags, type TableRowLevelSecurity } from "../render-rls-ddl.ts";
3
+ export type RowLevelSecurityState = RowLevelSecurityFlags & TableRowLevelSecurity;
4
+ export declare function readRowLevelSecurityState(db: AnyDb, schemaName: string, tableName: string): Promise<RowLevelSecurityState | undefined>;
5
+ export declare function describeRowLevelSecurityDifference(expected: RowLevelSecurityState, actual: RowLevelSecurityState | undefined): string | undefined;
6
+ export declare function restoreRowLevelSecurityState(tx: AnyDb, tableName: string, expected: RowLevelSecurityState): Promise<void>;
@@ -0,0 +1,82 @@
1
+ // Reads and restores a table's row level security state from the Postgres catalog.
2
+ // The shadow swap rebuilds a table from EntityTableMeta, which carries no policies:
3
+ // the live state is read before the swap and re-applied (then verified) afterwards.
4
+ import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
5
+ import { asRawClient } from "../query.js";
6
+ import { POLICY_COMMANDS, POLICY_KINDS, renderCreatePolicy, } from "../render-rls-ddl.js";
7
+ import { quoteTableIdent } from "./table-ops.js";
8
+ function toPolicy(row) {
9
+ if (!isOneOf(POLICY_COMMANDS, row.cmd) || !isOneOf(POLICY_KINDS, row.permissive)) {
10
+ throw new Error(`row level security: policy "${row.policyname}" has unsupported command "${row.cmd}" or kind "${row.permissive}"`);
11
+ }
12
+ return {
13
+ name: row.policyname,
14
+ command: row.cmd,
15
+ kind: row.permissive,
16
+ rolesSql: row.roles_sql,
17
+ ...(row.qual !== null && { using: row.qual }),
18
+ ...(row.with_check !== null && { withCheck: row.with_check }),
19
+ };
20
+ }
21
+ export async function readRowLevelSecurityState(db, schemaName, tableName) {
22
+ const raw = asRawClient(db);
23
+ const flagRows = await raw.unsafe(`SELECT c.relrowsecurity, c.relforcerowsecurity
24
+ FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
25
+ WHERE n.nspname = $1 AND c.relname = $2`, [schemaName, tableName]);
26
+ const flags = flagRows[0];
27
+ if (flags === undefined)
28
+ return undefined;
29
+ const policyRows = await raw.unsafe(`SELECT policyname, permissive, cmd, qual, with_check,
30
+ array_to_string(ARRAY(SELECT CASE WHEN r = 'public' THEN 'PUBLIC' ELSE quote_ident(r) END
31
+ FROM unnest(roles) AS r ORDER BY r::text COLLATE "C"), ', ') AS roles_sql
32
+ FROM pg_policies WHERE schemaname = $1 AND tablename = $2
33
+ ORDER BY policyname COLLATE "C"`, [schemaName, tableName]);
34
+ return {
35
+ enabled: flags.relrowsecurity,
36
+ forced: flags.relforcerowsecurity,
37
+ policies: policyRows.map(toPolicy),
38
+ };
39
+ }
40
+ function describePolicyDifference(expected, actual) {
41
+ if (actual === undefined)
42
+ return `policy ${expected.name} missing`;
43
+ const fields = ["command", "kind", "rolesSql", "using", "withCheck"];
44
+ const differing = fields.filter((field) => expected[field] !== actual[field]);
45
+ return differing.length > 0
46
+ ? `policy ${expected.name} differs in ${differing.join(", ")}`
47
+ : undefined;
48
+ }
49
+ export function describeRowLevelSecurityDifference(expected, actual) {
50
+ if (actual === undefined)
51
+ return "table missing";
52
+ const differences = [];
53
+ if (expected.enabled !== actual.enabled)
54
+ differences.push(`enabled ${expected.enabled} -> ${actual.enabled}`);
55
+ if (expected.forced !== actual.forced)
56
+ differences.push(`forced ${expected.forced} -> ${actual.forced}`);
57
+ const actualByName = new Map(actual.policies.map((policy) => [policy.name, policy]));
58
+ for (const policy of expected.policies) {
59
+ const difference = describePolicyDifference(policy, actualByName.get(policy.name));
60
+ if (difference !== undefined)
61
+ differences.push(difference);
62
+ }
63
+ const expectedNames = new Set(expected.policies.map((policy) => policy.name));
64
+ for (const policy of actual.policies) {
65
+ if (!expectedNames.has(policy.name))
66
+ differences.push(`unexpected policy ${policy.name}`);
67
+ }
68
+ return differences.length > 0 ? differences.join("; ") : undefined;
69
+ }
70
+ export async function restoreRowLevelSecurityState(tx, tableName, expected) {
71
+ const raw = asRawClient(tx);
72
+ const tableSql = `public.${quoteTableIdent(tableName)}`;
73
+ for (const policy of expected.policies) {
74
+ await raw.unsafe(renderCreatePolicy(tableSql, policy));
75
+ }
76
+ // One ALTER TABLE for both flags: each ALTER TABLE is a separate DDL command (and event-trigger firing) on the swap's critical path.
77
+ await raw.unsafe(`ALTER TABLE ${tableSql} ${expected.enabled ? "ENABLE" : "DISABLE"} ROW LEVEL SECURITY, ${expected.forced ? "FORCE" : "NO FORCE"} ROW LEVEL SECURITY`);
78
+ const difference = describeRowLevelSecurityDifference(expected, await readRowLevelSecurityState(tx, "public", tableName));
79
+ if (difference !== undefined) {
80
+ throw new Error(`shadow swap: row level security of public.${tableName} differs after the swap: ${difference}`);
81
+ }
82
+ }
@@ -0,0 +1,3 @@
1
+ import type { RowLevelSecurityScope } from "../row-level-security-scope.ts";
2
+ export declare function buildRowLevelSecurityScopeQuery(runner: unknown, scope: RowLevelSecurityScope | undefined): PromiseLike<unknown>;
3
+ export declare function applyRowLevelSecurityScope(runner: unknown, scope: RowLevelSecurityScope | undefined): Promise<void>;
@@ -0,0 +1,17 @@
1
+ import { asRawClient } from "../query.js";
2
+ import { SCOPE_SETTING, SYSTEM_SCOPE, TENANT_ID_SETTING } from "../render-rls-ddl.js";
3
+ function scopeSettings(scope) {
4
+ const tenantId = scope?.kind === "tenant" ? scope.tenantId : "";
5
+ const systemScope = scope?.kind === "system" ? SYSTEM_SCOPE : "";
6
+ return [TENANT_ID_SETTING, tenantId, SCOPE_SETTING, systemScope];
7
+ }
8
+ const SET_SCOPE_SQL = `SELECT set_config($1, $2, true), set_config($3, $4, true)`;
9
+ // Returns the lazy driver query without sending it, so a caller can pipeline it right behind BEGIN;
10
+ // the caller must start it itself and in order.
11
+ export function buildRowLevelSecurityScopeQuery(runner, scope) {
12
+ return asRawClient(runner).unsafe(SET_SCOPE_SQL, [...scopeSettings(scope)]);
13
+ }
14
+ // set_config(..., true) is transaction-local; undefined clears both settings to ''.
15
+ export async function applyRowLevelSecurityScope(runner, scope) {
16
+ await asRawClient(runner).unsafe(SET_SCOPE_SQL, [...scopeSettings(scope)]);
17
+ }
@@ -0,0 +1,10 @@
1
+ import { type AnyDb } from "../query.ts";
2
+ import type { RowLevelSecurityPolicy } from "../render-rls-ddl.ts";
3
+ export type RowLevelSecurityShape = {
4
+ readonly enabled: boolean;
5
+ readonly forced: boolean;
6
+ readonly policies: readonly (Pick<RowLevelSecurityPolicy, "name" | "kind" | "command"> & {
7
+ readonly appliesToPublic: boolean;
8
+ })[];
9
+ };
10
+ export declare function readRowLevelSecurityShape(db: AnyDb, tableName: string): Promise<RowLevelSecurityShape | undefined>;
@@ -0,0 +1,42 @@
1
+ // Flags plus policy name, kind and command of a table, read from the catalogs without deparsing
2
+ // policy expressions. pg_get_expr (behind the pg_policies view) opens the table with ACCESS SHARE,
3
+ // which queues behind a pending ACCESS EXCLUSIVE; this read never touches the table itself.
4
+ import { asRawClient } from "../query.js";
5
+ const COMMAND_BY_CODE = {
6
+ "*": "ALL",
7
+ r: "SELECT",
8
+ a: "INSERT",
9
+ w: "UPDATE",
10
+ d: "DELETE",
11
+ };
12
+ function commandOf(code) {
13
+ const command = Object.entries(COMMAND_BY_CODE).find(([key]) => key === code)?.[1];
14
+ // Fail closed: a policy command this check does not know cannot be judged harmless.
15
+ if (command === undefined)
16
+ throw new Error(`unknown pg_policy.polcmd code "${code}"`);
17
+ return command;
18
+ }
19
+ export async function readRowLevelSecurityShape(db, tableName) {
20
+ const rows = await asRawClient(db).unsafe(`SELECT c.relrowsecurity, c.relforcerowsecurity, p.polname, p.polpermissive, p.polcmd::text AS polcmd, (p.polroles = '{0}'::oid[]) AS polpublic
21
+ FROM pg_class c
22
+ JOIN pg_namespace n ON n.oid = c.relnamespace
23
+ LEFT JOIN pg_policy p ON p.polrelid = c.oid
24
+ WHERE n.nspname = 'public' AND c.relname = $1`, [tableName]);
25
+ const first = rows[0];
26
+ if (first === undefined)
27
+ return undefined;
28
+ const policies = rows.flatMap((row) => {
29
+ if (row.polname === null || row.polcmd === null)
30
+ return [];
31
+ const command = commandOf(row.polcmd);
32
+ return [
33
+ {
34
+ name: row.polname,
35
+ kind: row.polpermissive ? "PERMISSIVE" : "RESTRICTIVE",
36
+ command,
37
+ appliesToPublic: row.polpublic === true,
38
+ },
39
+ ];
40
+ });
41
+ return { enabled: first.relrowsecurity, forced: first.relforcerowsecurity, policies };
42
+ }
@@ -0,0 +1,2 @@
1
+ import type { AnyDb } from "../query.ts";
2
+ export declare function withSessionAdvisoryLock<T>(db: AnyDb, key: number, fn: () => Promise<T>): Promise<T>;
@@ -0,0 +1,23 @@
1
+ import { asRawClient } from "../query.js";
2
+ // Blocks until the lock is free. The lock is session-scoped, so lock, work and
3
+ // unlock share one reserved connection; pool calls may land on different sessions.
4
+ export async function withSessionAdvisoryLock(db, key, fn) {
5
+ const client = asRawClient(db);
6
+ if (typeof client.reserve !== "function") {
7
+ throw new Error("withSessionAdvisoryLock: db has no .reserve() (need a Bun.SQL pool)");
8
+ }
9
+ const conn = await client.reserve();
10
+ try {
11
+ await conn.unsafe(`SELECT pg_advisory_lock($1)`, [key]);
12
+ try {
13
+ return await fn();
14
+ }
15
+ finally {
16
+ // A dropped session already released its lock; failing here would mask fn's outcome.
17
+ await conn.unsafe(`SELECT pg_advisory_unlock($1)`, [key]).catch(() => undefined);
18
+ }
19
+ }
20
+ finally {
21
+ await conn.release();
22
+ }
23
+ }
@@ -6,7 +6,6 @@ export declare const PROJECTION_REBUILD_SCHEMA = "kumiko_rebuild";
6
6
  export declare function ensureRebuildSchema(db: AnyDb): Promise<void>;
7
7
  export declare function rebuildMetaOrThrow(table: unknown, projectionName: string): EntityTableMeta;
8
8
  export declare function assertLiveColumnsMatchMeta(db: DbConnection | DbTx, meta: EntityTableMeta, projectionName: string): Promise<void>;
9
- export declare function assertLiveTableHasNoRowLevelSecurity(tx: AnyDb, tableName: string, projectionName?: string): Promise<void>;
10
9
  export declare function buildShadowTable(tx: AnyDb, meta: EntityTableMeta): Promise<void>;
11
10
  export declare function fenceLiveTable(tx: AnyDb, tableName: string, lockTimeoutMs: number): Promise<void>;
12
11
  export declare function assertNoUnreachableLiveRows(tx: AnyDb, projectionName: string, tableName: string, aggregateTypes: readonly string[], derivedRowIds?: Readonly<Record<string, ProjectionRowIdOf>>, derivedRowIdBatchSize?: number): Promise<void>;
@@ -16,4 +15,4 @@ export type ColumnDriftResult = {
16
15
  readonly sample: readonly string[];
17
16
  };
18
17
  export declare function countColumnDrift(tx: AnyDb, tableName: string, meta: EntityTableMeta): Promise<ColumnDriftResult>;
19
- export declare function swapShadowIntoLive(tx: AnyDb, tableName: string): Promise<void>;
18
+ export declare function swapShadowIntoLive(tx: DbTx, tableName: string): Promise<void>;
@@ -17,6 +17,7 @@
17
17
  // Boundary: the shadow table is rebuilt from EntityTableMeta, so any index NOT
18
18
  // expressed in meta (hand-added in a migration) is not reconstructed, and a
19
19
  // partial index whose WHERE the renderer can't express is rejected up-front.
20
+ // RLS flags and policies are copied from the live catalog at swap time.
20
21
  import { isUuid, parseTenantId } from "@cosmicdrift/kumiko-types/identifiers";
21
22
  import { configuredBlindIndexKey } from "../../crypto/index.js";
22
23
  import { extractPgError } from "../pg-error.js";
@@ -24,6 +25,7 @@ import { asEntityTableMeta, asRawClient } from "../query.js";
24
25
  import { renderTableDdl } from "../render-ddl.js";
25
26
  import { columnNamesOf, tableExists } from "../schema-inspection.js";
26
27
  import { toPostgresArrayLiteral } from "./postgres-array-literal.js";
28
+ import { readRowLevelSecurityState, restoreRowLevelSecurityState } from "./rls-catalog.js";
27
29
  import { quoteTableIdent } from "./table-ops.js";
28
30
  export const PROJECTION_REBUILD_SCHEMA = "kumiko_rebuild";
29
31
  const SCHEMA_IDENT = quoteTableIdent(PROJECTION_REBUILD_SCHEMA);
@@ -92,27 +94,6 @@ export async function assertLiveColumnsMatchMeta(db, meta, projectionName) {
92
94
  "Rebuilding would swap away the difference. Likely cause: this pod runs a build whose registry is behind (or ahead of) the applied migrations — rolling deploy in progress? — or DDL was applied by hand. " +
93
95
  "Rebuild aborted; retry from a pod whose code matches the migrated schema.");
94
96
  }
95
- // EntityTableMeta carries no RLS/policies, so the swap would silently drop them.
96
- export async function assertLiveTableHasNoRowLevelSecurity(tx, tableName, projectionName) {
97
- const rows = await asRawClient(tx).unsafe(`SELECT c.relrowsecurity AS rls_enabled, c.relforcerowsecurity AS rls_forced,
98
- (SELECT count(*)::int FROM pg_policy p WHERE p.polrelid = c.oid) AS policy_count
99
- FROM pg_class c
100
- JOIN pg_namespace n ON n.oid = c.relnamespace
101
- WHERE n.nspname = 'public' AND c.relname = $1`, [tableName]);
102
- const row = rows[0];
103
- // skip: no live table yet — nothing to have RLS on
104
- if (!row)
105
- return;
106
- // skip: no RLS flags and no policies — nothing the swap could drop
107
- if (!row.rls_enabled && !row.rls_forced && row.policy_count === 0)
108
- return;
109
- const context = projectionName !== undefined ? `projection-rebuild "${projectionName}"` : "shadow swap";
110
- throw new Error(`${context}: live table "${tableName}" has row level security (enabled: ${row.rls_enabled}, ` +
111
- `forced: ${row.rls_forced}, policies: ${row.policy_count}). The shadow swap rebuilds the table ` +
112
- "from EntityTableMeta and would silently drop RLS and every policy. Kumiko does not support RLS " +
113
- "on rebuildable tables: drop the RLS flags/policies on this table or exclude it from online " +
114
- "rebuild. Rebuild aborted; live table untouched.");
115
- }
116
97
  // Runs INSIDE the rebuild tx, AFTER the state/consumer row lock is taken.
117
98
  // Points search_path at the shadow schema (SET LOCAL → auto-reset on commit or
118
99
  // rollback), drops any leftover shadow from a crashed run, then builds the
@@ -396,11 +377,14 @@ export async function countColumnDrift(tx, tableName, meta) {
396
377
  export async function swapShadowIntoLive(tx, tableName) {
397
378
  const raw = asRawClient(tx);
398
379
  const ident = quoteTableIdent(tableName);
399
- // Held before the re-check so a concurrent ENABLE ROW LEVEL SECURITY either commits first
400
- // (and is seen) or waits behind the DROP. Without it the catalog read takes no lock, and
401
- // the unfenced paths (MSP rebuild, projection with no subscribed events) would drop RLS.
380
+ // Held before the catalog read so a concurrent ENABLE ROW LEVEL SECURITY / CREATE POLICY either
381
+ // commits first (and is carried over) or waits behind the DROP.
402
382
  await raw.unsafe(`LOCK TABLE public.${ident} IN ACCESS EXCLUSIVE MODE`);
403
- await assertLiveTableHasNoRowLevelSecurity(tx, tableName);
383
+ const live = await readRowLevelSecurityState(tx, "public", tableName);
384
+ if (live === undefined) {
385
+ throw new Error(`shadow swap: live table public.${tableName} does not exist`);
386
+ }
404
387
  await raw.unsafe(`DROP TABLE public.${ident}`);
405
388
  await raw.unsafe(`ALTER TABLE ${SCHEMA_IDENT}.${ident} SET SCHEMA public`);
389
+ await restoreRowLevelSecurityState(tx, tableName, live);
406
390
  }
@@ -2,7 +2,10 @@ import type { AnyDb } from "../query.ts";
2
2
  export { alterTableAddColumn, createIndexIfNotExists, executeDdlStatement } from "./ddl.ts";
3
3
  export declare function truncateTablesRestartIdentity(db: AnyDb, tableNames: readonly string[]): Promise<void>;
4
4
  export declare function databaseExists(db: AnyDb, dbName: string): Promise<boolean>;
5
- export declare function createDatabase(db: AnyDb, dbName: string, templateDbName?: string): Promise<void>;
5
+ export declare function createDatabase(db: AnyDb, dbName: string, templateDbName?: string, owner?: string): Promise<void>;
6
+ export declare function createApplicationRole(admin: AnyDb, role: string, password: string): Promise<void>;
7
+ export declare function transferDatabaseOwnership(admin: AnyDb, dbName: string, owner: string): Promise<void>;
8
+ export declare function transferPublicSchemaOwnership(db: AnyDb, owner: string): Promise<void>;
6
9
  export declare function renameDatabase(db: AnyDb, fromName: string, toName: string): Promise<void>;
7
10
  export declare function listDatabaseNamesWithPrefix(db: AnyDb, prefix: string): Promise<readonly string[]>;
8
11
  export declare function acquireSessionAdvisoryLock(db: AnyDb, key: string): Promise<void>;