@homeflare/alchemy 0.43.0 → 0.45.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 (612) hide show
  1. package/README.md +10 -1
  2. package/dist/caddy/index.js +35 -1
  3. package/dist/caddy/index.js.map +3 -3
  4. package/dist/launchd/index.js +35 -1
  5. package/dist/launchd/index.js.map +3 -3
  6. package/dist/linux/index.js +35 -1
  7. package/dist/linux/index.js.map +3 -3
  8. package/dist/litellm/access-group-form.d.ts +26 -0
  9. package/dist/litellm/access-group-form.d.ts.map +1 -0
  10. package/dist/litellm/access-group-operations.d.ts +37 -0
  11. package/dist/litellm/access-group-operations.d.ts.map +1 -0
  12. package/dist/litellm/access-group-types.d.ts +49 -0
  13. package/dist/litellm/access-group-types.d.ts.map +1 -0
  14. package/dist/litellm/access-group.d.ts +55 -0
  15. package/dist/litellm/access-group.d.ts.map +1 -0
  16. package/dist/litellm/credential-errors.d.ts +131 -0
  17. package/dist/litellm/credential-errors.d.ts.map +1 -0
  18. package/dist/litellm/credential-form.d.ts +79 -0
  19. package/dist/litellm/credential-form.d.ts.map +1 -0
  20. package/dist/litellm/credential-operations.d.ts +84 -0
  21. package/dist/litellm/credential-operations.d.ts.map +1 -0
  22. package/dist/litellm/credential-rewrite.d.ts +6 -0
  23. package/dist/litellm/credential-rewrite.d.ts.map +1 -0
  24. package/dist/litellm/credential-types.d.ts +87 -0
  25. package/dist/litellm/credential-types.d.ts.map +1 -0
  26. package/dist/litellm/credential-values.d.ts +53 -0
  27. package/dist/litellm/credential-values.d.ts.map +1 -0
  28. package/dist/litellm/credential.d.ts +83 -0
  29. package/dist/litellm/credential.d.ts.map +1 -0
  30. package/dist/litellm/fake-access-group-litellm.d.ts +34 -0
  31. package/dist/litellm/fake-access-group-litellm.d.ts.map +1 -0
  32. package/dist/litellm/fake-credential-litellm.d.ts +54 -0
  33. package/dist/litellm/fake-credential-litellm.d.ts.map +1 -0
  34. package/dist/litellm/fake-keys.d.ts +53 -0
  35. package/dist/litellm/fake-keys.d.ts.map +1 -0
  36. package/dist/litellm/fake-litellm.d.ts +4 -1
  37. package/dist/litellm/fake-litellm.d.ts.map +1 -1
  38. package/dist/litellm/fake-mcp-litellm.d.ts +67 -0
  39. package/dist/litellm/fake-mcp-litellm.d.ts.map +1 -0
  40. package/dist/litellm/fake-model-litellm-post-update.d.ts +49 -0
  41. package/dist/litellm/fake-model-litellm-post-update.d.ts.map +1 -0
  42. package/dist/litellm/fake-model-litellm.d.ts +48 -0
  43. package/dist/litellm/fake-model-litellm.d.ts.map +1 -0
  44. package/dist/litellm/fake-policy-attachment-litellm.d.ts +33 -0
  45. package/dist/litellm/fake-policy-attachment-litellm.d.ts.map +1 -0
  46. package/dist/litellm/fake-policy-litellm.d.ts +32 -0
  47. package/dist/litellm/fake-policy-litellm.d.ts.map +1 -0
  48. package/dist/litellm/fake-registry-base.d.ts +42 -0
  49. package/dist/litellm/fake-registry-base.d.ts.map +1 -0
  50. package/dist/litellm/fake-stack.d.ts +8 -1
  51. package/dist/litellm/fake-stack.d.ts.map +1 -1
  52. package/dist/litellm/fake-team-litellm.d.ts +47 -0
  53. package/dist/litellm/fake-team-litellm.d.ts.map +1 -0
  54. package/dist/litellm/fake-tool-litellm.d.ts +28 -0
  55. package/dist/litellm/fake-tool-litellm.d.ts.map +1 -0
  56. package/dist/litellm/fake-toolset-litellm.d.ts +29 -0
  57. package/dist/litellm/fake-toolset-litellm.d.ts.map +1 -0
  58. package/dist/litellm/index.d.ts +18 -0
  59. package/dist/litellm/index.d.ts.map +1 -1
  60. package/dist/litellm/index.js +3078 -2
  61. package/dist/litellm/index.js.map +65 -4
  62. package/dist/litellm/key-errors.d.ts +169 -0
  63. package/dist/litellm/key-errors.d.ts.map +1 -0
  64. package/dist/litellm/key-form.d.ts +119 -0
  65. package/dist/litellm/key-form.d.ts.map +1 -0
  66. package/dist/litellm/key-harness.d.ts +41 -0
  67. package/dist/litellm/key-harness.d.ts.map +1 -0
  68. package/dist/litellm/key-metadata-errors.d.ts +30 -0
  69. package/dist/litellm/key-metadata-errors.d.ts.map +1 -0
  70. package/dist/litellm/key-metadata.d.ts +22 -0
  71. package/dist/litellm/key-metadata.d.ts.map +1 -0
  72. package/dist/litellm/key-operations.d.ts +63 -0
  73. package/dist/litellm/key-operations.d.ts.map +1 -0
  74. package/dist/litellm/key-secret.d.ts +33 -0
  75. package/dist/litellm/key-secret.d.ts.map +1 -0
  76. package/dist/litellm/key.d.ts +78 -0
  77. package/dist/litellm/key.d.ts.map +1 -0
  78. package/dist/litellm/mcp-server-credential.d.ts +50 -0
  79. package/dist/litellm/mcp-server-credential.d.ts.map +1 -0
  80. package/dist/litellm/mcp-server-errors.d.ts +101 -0
  81. package/dist/litellm/mcp-server-errors.d.ts.map +1 -0
  82. package/dist/litellm/mcp-server-form.d.ts +56 -0
  83. package/dist/litellm/mcp-server-form.d.ts.map +1 -0
  84. package/dist/litellm/mcp-server-locate.d.ts +26 -0
  85. package/dist/litellm/mcp-server-locate.d.ts.map +1 -0
  86. package/dist/litellm/mcp-server-operations.d.ts +63 -0
  87. package/dist/litellm/mcp-server-operations.d.ts.map +1 -0
  88. package/dist/litellm/mcp-server-shadow.d.ts +24 -0
  89. package/dist/litellm/mcp-server-shadow.d.ts.map +1 -0
  90. package/dist/litellm/mcp-server-types.d.ts +118 -0
  91. package/dist/litellm/mcp-server-types.d.ts.map +1 -0
  92. package/dist/litellm/mcp-server-url.d.ts +11 -0
  93. package/dist/litellm/mcp-server-url.d.ts.map +1 -0
  94. package/dist/litellm/mcp-server.d.ts +95 -0
  95. package/dist/litellm/mcp-server.d.ts.map +1 -0
  96. package/dist/litellm/model-claim.d.ts +24 -0
  97. package/dist/litellm/model-claim.d.ts.map +1 -0
  98. package/dist/litellm/model-credential.d.ts +12 -0
  99. package/dist/litellm/model-credential.d.ts.map +1 -0
  100. package/dist/litellm/model-errors.d.ts +119 -0
  101. package/dist/litellm/model-errors.d.ts.map +1 -0
  102. package/dist/litellm/model-form.d.ts +78 -0
  103. package/dist/litellm/model-form.d.ts.map +1 -0
  104. package/dist/litellm/model-locate.d.ts +13 -0
  105. package/dist/litellm/model-locate.d.ts.map +1 -0
  106. package/dist/litellm/model-operations.d.ts +59 -0
  107. package/dist/litellm/model-operations.d.ts.map +1 -0
  108. package/dist/litellm/model-types.d.ts +91 -0
  109. package/dist/litellm/model-types.d.ts.map +1 -0
  110. package/dist/litellm/model.d.ts +88 -0
  111. package/dist/litellm/model.d.ts.map +1 -0
  112. package/dist/litellm/policy-attachment-form.d.ts +19 -0
  113. package/dist/litellm/policy-attachment-form.d.ts.map +1 -0
  114. package/dist/litellm/policy-attachment-operations.d.ts +31 -0
  115. package/dist/litellm/policy-attachment-operations.d.ts.map +1 -0
  116. package/dist/litellm/policy-attachment-types.d.ts +48 -0
  117. package/dist/litellm/policy-attachment-types.d.ts.map +1 -0
  118. package/dist/litellm/policy-attachment.d.ts +61 -0
  119. package/dist/litellm/policy-attachment.d.ts.map +1 -0
  120. package/dist/litellm/policy-form.d.ts +22 -0
  121. package/dist/litellm/policy-form.d.ts.map +1 -0
  122. package/dist/litellm/policy-operations.d.ts +42 -0
  123. package/dist/litellm/policy-operations.d.ts.map +1 -0
  124. package/dist/litellm/policy-types.d.ts +57 -0
  125. package/dist/litellm/policy-types.d.ts.map +1 -0
  126. package/dist/litellm/policy.d.ts +65 -0
  127. package/dist/litellm/policy.d.ts.map +1 -0
  128. package/dist/litellm/providers.d.ts +1 -1
  129. package/dist/litellm/providers.d.ts.map +1 -1
  130. package/dist/litellm/registry-errors.d.ts +62 -0
  131. package/dist/litellm/registry-errors.d.ts.map +1 -0
  132. package/dist/litellm/registry-support.d.ts +42 -0
  133. package/dist/litellm/registry-support.d.ts.map +1 -0
  134. package/dist/litellm/team-form.d.ts +26 -0
  135. package/dist/litellm/team-form.d.ts.map +1 -0
  136. package/dist/litellm/team-members.d.ts +10 -0
  137. package/dist/litellm/team-members.d.ts.map +1 -0
  138. package/dist/litellm/team-operations.d.ts +38 -0
  139. package/dist/litellm/team-operations.d.ts.map +1 -0
  140. package/dist/litellm/team-permission.d.ts +35 -0
  141. package/dist/litellm/team-permission.d.ts.map +1 -0
  142. package/dist/litellm/team-types.d.ts +101 -0
  143. package/dist/litellm/team-types.d.ts.map +1 -0
  144. package/dist/litellm/team.d.ts +67 -0
  145. package/dist/litellm/team.d.ts.map +1 -0
  146. package/dist/litellm/tool-policy-form.d.ts +21 -0
  147. package/dist/litellm/tool-policy-form.d.ts.map +1 -0
  148. package/dist/litellm/tool-policy-operations.d.ts +30 -0
  149. package/dist/litellm/tool-policy-operations.d.ts.map +1 -0
  150. package/dist/litellm/tool-policy-types.d.ts +46 -0
  151. package/dist/litellm/tool-policy-types.d.ts.map +1 -0
  152. package/dist/litellm/tool-policy.d.ts +62 -0
  153. package/dist/litellm/tool-policy.d.ts.map +1 -0
  154. package/dist/litellm/toolset-form.d.ts +20 -0
  155. package/dist/litellm/toolset-form.d.ts.map +1 -0
  156. package/dist/litellm/toolset-operations.d.ts +39 -0
  157. package/dist/litellm/toolset-operations.d.ts.map +1 -0
  158. package/dist/litellm/toolset-types.d.ts +46 -0
  159. package/dist/litellm/toolset-types.d.ts.map +1 -0
  160. package/dist/litellm/toolset.d.ts +55 -0
  161. package/dist/litellm/toolset.d.ts.map +1 -0
  162. package/dist/openbao/index.js +35 -1
  163. package/dist/openbao/index.js.map +3 -3
  164. package/dist/ownership/rows.d.ts +11 -0
  165. package/dist/ownership/rows.d.ts.map +1 -1
  166. package/dist/postgres/connection.d.ts +14 -2
  167. package/dist/postgres/connection.d.ts.map +1 -1
  168. package/dist/postgres/database-sql.d.ts +27 -8
  169. package/dist/postgres/database-sql.d.ts.map +1 -1
  170. package/dist/postgres/fake-grants-parse.d.ts +37 -0
  171. package/dist/postgres/fake-grants-parse.d.ts.map +1 -0
  172. package/dist/postgres/fake-grants-read.d.ts +61 -0
  173. package/dist/postgres/fake-grants-read.d.ts.map +1 -0
  174. package/dist/postgres/fake-grants-sql.d.ts +46 -0
  175. package/dist/postgres/fake-grants-sql.d.ts.map +1 -0
  176. package/dist/postgres/fake-grants-write.d.ts +29 -0
  177. package/dist/postgres/fake-grants-write.d.ts.map +1 -0
  178. package/dist/postgres/fake-role-sql.d.ts +41 -0
  179. package/dist/postgres/fake-role-sql.d.ts.map +1 -0
  180. package/dist/postgres/fake-schema-sql.d.ts +42 -0
  181. package/dist/postgres/fake-schema-sql.d.ts.map +1 -0
  182. package/dist/postgres/fake-sql-parse.d.ts +23 -0
  183. package/dist/postgres/fake-sql-parse.d.ts.map +1 -0
  184. package/dist/postgres/fake-sql-quote.d.ts +12 -0
  185. package/dist/postgres/fake-sql-quote.d.ts.map +1 -0
  186. package/dist/postgres/fake-sql.d.ts +42 -0
  187. package/dist/postgres/fake-sql.d.ts.map +1 -1
  188. package/dist/postgres/grants-attrs.d.ts +115 -0
  189. package/dist/postgres/grants-attrs.d.ts.map +1 -0
  190. package/dist/postgres/grants-declare.d.ts +88 -0
  191. package/dist/postgres/grants-declare.d.ts.map +1 -0
  192. package/dist/postgres/grants-diff.d.ts +25 -0
  193. package/dist/postgres/grants-diff.d.ts.map +1 -0
  194. package/dist/postgres/grants-errors.d.ts +137 -0
  195. package/dist/postgres/grants-errors.d.ts.map +1 -0
  196. package/dist/postgres/grants-existence.d.ts +14 -0
  197. package/dist/postgres/grants-existence.d.ts.map +1 -0
  198. package/dist/postgres/grants-ops.d.ts +65 -0
  199. package/dist/postgres/grants-ops.d.ts.map +1 -0
  200. package/dist/postgres/grants-ownership.d.ts +18 -0
  201. package/dist/postgres/grants-ownership.d.ts.map +1 -0
  202. package/dist/postgres/grants-plan-restore.d.ts +22 -0
  203. package/dist/postgres/grants-plan-restore.d.ts.map +1 -0
  204. package/dist/postgres/grants-plan.d.ts +65 -0
  205. package/dist/postgres/grants-plan.d.ts.map +1 -0
  206. package/dist/postgres/grants-read-defaults.d.ts +17 -0
  207. package/dist/postgres/grants-read-defaults.d.ts.map +1 -0
  208. package/dist/postgres/grants-read.d.ts +77 -0
  209. package/dist/postgres/grants-read.d.ts.map +1 -0
  210. package/dist/postgres/grants-refuse.d.ts +25 -0
  211. package/dist/postgres/grants-refuse.d.ts.map +1 -0
  212. package/dist/postgres/grants-sql.d.ts +42 -0
  213. package/dist/postgres/grants-sql.d.ts.map +1 -0
  214. package/dist/postgres/grants-words.d.ts +29 -0
  215. package/dist/postgres/grants-words.d.ts.map +1 -0
  216. package/dist/postgres/grants.d.ts +37 -0
  217. package/dist/postgres/grants.d.ts.map +1 -0
  218. package/dist/postgres/index.d.ts +31 -6
  219. package/dist/postgres/index.d.ts.map +1 -1
  220. package/dist/postgres/index.js +1998 -37
  221. package/dist/postgres/index.js.map +46 -7
  222. package/dist/postgres/installed-classifier.d.ts +13 -0
  223. package/dist/postgres/installed-classifier.d.ts.map +1 -0
  224. package/dist/postgres/providers.d.ts +16 -8
  225. package/dist/postgres/providers.d.ts.map +1 -1
  226. package/dist/postgres/psql-executor.d.ts +4 -0
  227. package/dist/postgres/psql-executor.d.ts.map +1 -1
  228. package/dist/postgres/role-attrs.d.ts +112 -0
  229. package/dist/postgres/role-attrs.d.ts.map +1 -0
  230. package/dist/postgres/role-diff.d.ts +22 -0
  231. package/dist/postgres/role-diff.d.ts.map +1 -0
  232. package/dist/postgres/role-errors.d.ts +132 -0
  233. package/dist/postgres/role-errors.d.ts.map +1 -0
  234. package/dist/postgres/role-membership-sql.d.ts +78 -0
  235. package/dist/postgres/role-membership-sql.d.ts.map +1 -0
  236. package/dist/postgres/role-provider.d.ts +11 -0
  237. package/dist/postgres/role-provider.d.ts.map +1 -0
  238. package/dist/postgres/role-scram.d.ts +8 -0
  239. package/dist/postgres/role-scram.d.ts.map +1 -0
  240. package/dist/postgres/role-secrets.d.ts +38 -0
  241. package/dist/postgres/role-secrets.d.ts.map +1 -0
  242. package/dist/postgres/role-sql.d.ts +68 -0
  243. package/dist/postgres/role-sql.d.ts.map +1 -0
  244. package/dist/postgres/role.d.ts +57 -0
  245. package/dist/postgres/role.d.ts.map +1 -0
  246. package/dist/postgres/schema-assert.d.ts +50 -0
  247. package/dist/postgres/schema-assert.d.ts.map +1 -0
  248. package/dist/postgres/schema-attrs.d.ts +51 -0
  249. package/dist/postgres/schema-attrs.d.ts.map +1 -0
  250. package/dist/postgres/schema-delete.d.ts +28 -0
  251. package/dist/postgres/schema-delete.d.ts.map +1 -0
  252. package/dist/postgres/schema-diff.d.ts +14 -0
  253. package/dist/postgres/schema-diff.d.ts.map +1 -0
  254. package/dist/postgres/schema-errors.d.ts +139 -0
  255. package/dist/postgres/schema-errors.d.ts.map +1 -0
  256. package/dist/postgres/schema-identity.d.ts +8 -0
  257. package/dist/postgres/schema-identity.d.ts.map +1 -0
  258. package/dist/postgres/schema-reconcile.d.ts +12 -0
  259. package/dist/postgres/schema-reconcile.d.ts.map +1 -0
  260. package/dist/postgres/schema-sql.d.ts +45 -0
  261. package/dist/postgres/schema-sql.d.ts.map +1 -0
  262. package/dist/postgres/schema-test-kit.d.ts +67 -0
  263. package/dist/postgres/schema-test-kit.d.ts.map +1 -0
  264. package/dist/postgres/schema.d.ts +54 -0
  265. package/dist/postgres/schema.d.ts.map +1 -0
  266. package/dist/proxmox/index.js +35 -1
  267. package/dist/proxmox/index.js.map +3 -3
  268. package/dist/release/fake-engine.d.ts +10 -0
  269. package/dist/release/fake-engine.d.ts.map +1 -1
  270. package/dist/release/index.js +35 -1
  271. package/dist/release/index.js.map +3 -3
  272. package/dist/valkey/acl-attrs.d.ts +94 -0
  273. package/dist/valkey/acl-attrs.d.ts.map +1 -0
  274. package/dist/valkey/acl-compare.d.ts +28 -0
  275. package/dist/valkey/acl-compare.d.ts.map +1 -0
  276. package/dist/valkey/acl-connection.d.ts +6 -0
  277. package/dist/valkey/acl-connection.d.ts.map +1 -0
  278. package/dist/valkey/acl-form.d.ts +62 -0
  279. package/dist/valkey/acl-form.d.ts.map +1 -0
  280. package/dist/valkey/acl-managed.d.ts +4 -0
  281. package/dist/valkey/acl-managed.d.ts.map +1 -0
  282. package/dist/valkey/acl-ops.d.ts +37 -0
  283. package/dist/valkey/acl-ops.d.ts.map +1 -0
  284. package/dist/valkey/acl-profiles.d.ts +53 -0
  285. package/dist/valkey/acl-profiles.d.ts.map +1 -0
  286. package/dist/valkey/acl-reconcile.d.ts +26 -0
  287. package/dist/valkey/acl-reconcile.d.ts.map +1 -0
  288. package/dist/valkey/acl.d.ts +24 -0
  289. package/dist/valkey/acl.d.ts.map +1 -0
  290. package/dist/valkey/connection-service.d.ts +22 -0
  291. package/dist/valkey/connection-service.d.ts.map +1 -0
  292. package/dist/valkey/connection.d.ts +59 -0
  293. package/dist/valkey/connection.d.ts.map +1 -0
  294. package/dist/valkey/errors.d.ts +165 -0
  295. package/dist/valkey/errors.d.ts.map +1 -0
  296. package/dist/valkey/fake-acl.d.ts +29 -0
  297. package/dist/valkey/fake-acl.d.ts.map +1 -0
  298. package/dist/valkey/fake-valkey.d.ts +35 -0
  299. package/dist/valkey/fake-valkey.d.ts.map +1 -0
  300. package/dist/valkey/handler-test-fixture.d.ts +20 -0
  301. package/dist/valkey/handler-test-fixture.d.ts.map +1 -0
  302. package/dist/valkey/index.d.ts +18 -0
  303. package/dist/valkey/index.d.ts.map +1 -0
  304. package/dist/valkey/index.js +964 -0
  305. package/dist/valkey/index.js.map +27 -0
  306. package/dist/valkey/instance-attrs.d.ts +46 -0
  307. package/dist/valkey/instance-attrs.d.ts.map +1 -0
  308. package/dist/valkey/instance-form.d.ts +22 -0
  309. package/dist/valkey/instance-form.d.ts.map +1 -0
  310. package/dist/valkey/instance.d.ts +48 -0
  311. package/dist/valkey/instance.d.ts.map +1 -0
  312. package/dist/valkey/provider-socket-fixture.d.ts +8 -0
  313. package/dist/valkey/provider-socket-fixture.d.ts.map +1 -0
  314. package/dist/valkey/providers.d.ts +16 -0
  315. package/dist/valkey/providers.d.ts.map +1 -0
  316. package/dist/valkey/resp.d.ts +38 -0
  317. package/dist/valkey/resp.d.ts.map +1 -0
  318. package/dist/valkey/transport-error.d.ts +36 -0
  319. package/dist/valkey/transport-error.d.ts.map +1 -0
  320. package/dist/valkey/transport.d.ts +50 -0
  321. package/dist/valkey/transport.d.ts.map +1 -0
  322. package/docs/litellm-access-group.md +105 -0
  323. package/docs/litellm-credential.md +60 -0
  324. package/docs/litellm-key-metadata.md +56 -0
  325. package/docs/litellm-key.md +194 -0
  326. package/docs/litellm-mcp.md +187 -0
  327. package/docs/litellm-model.md +111 -0
  328. package/docs/litellm-policy-attachment.md +86 -0
  329. package/docs/litellm-policy.md +103 -0
  330. package/docs/litellm-team.md +117 -0
  331. package/docs/litellm-tool-policy.md +90 -0
  332. package/docs/litellm-toolset.md +92 -0
  333. package/docs/litellm.md +16 -9
  334. package/docs/peers.md +15 -6
  335. package/docs/postgres-grants-example.md +55 -0
  336. package/docs/postgres-grants.md +157 -0
  337. package/docs/postgres-role.md +126 -0
  338. package/docs/postgres-schema.md +139 -0
  339. package/docs/postgres.md +20 -3
  340. package/docs/valkey-transport.md +29 -0
  341. package/docs/valkey.md +187 -0
  342. package/package.json +6 -2
  343. package/src/ceph/ceph-argv.test.ts +1 -1
  344. package/src/launchd/sudo-allowlist.test.ts +2 -2
  345. package/src/linux/sudo-allowlist.test.ts +3 -3
  346. package/src/linux/sudo-runner.test.ts +2 -2
  347. package/src/litellm/access-group-form.ts +105 -0
  348. package/src/litellm/access-group-operations.ts +92 -0
  349. package/src/litellm/access-group-types.ts +49 -0
  350. package/src/litellm/access-group.test.ts +197 -0
  351. package/src/litellm/access-group.ts +151 -0
  352. package/src/litellm/credential-errors.ts +183 -0
  353. package/src/litellm/credential-form.test.ts +151 -0
  354. package/src/litellm/credential-form.ts +211 -0
  355. package/src/litellm/credential-guards.test.ts +241 -0
  356. package/src/litellm/credential-operations.ts +208 -0
  357. package/src/litellm/credential-recovery.test.ts +93 -0
  358. package/src/litellm/credential-rewrite.test.ts +164 -0
  359. package/src/litellm/credential-rewrite.ts +50 -0
  360. package/src/litellm/credential-types.ts +117 -0
  361. package/src/litellm/credential-values.ts +94 -0
  362. package/src/litellm/credential.test.ts +218 -0
  363. package/src/litellm/credential.ts +227 -0
  364. package/src/litellm/fake-access-group-litellm.ts +127 -0
  365. package/src/litellm/fake-credential-litellm.ts +216 -0
  366. package/src/litellm/fake-keys.ts +190 -0
  367. package/src/litellm/fake-litellm.ts +19 -9
  368. package/src/litellm/fake-mcp-litellm.ts +214 -0
  369. package/src/litellm/fake-model-litellm-post-update.ts +103 -0
  370. package/src/litellm/fake-model-litellm.ts +233 -0
  371. package/src/litellm/fake-policy-attachment-litellm.ts +97 -0
  372. package/src/litellm/fake-policy-litellm.ts +186 -0
  373. package/src/litellm/fake-registry-base.ts +69 -0
  374. package/src/litellm/fake-stack.ts +17 -1
  375. package/src/litellm/fake-team-litellm.ts +229 -0
  376. package/src/litellm/fake-tool-litellm.ts +88 -0
  377. package/src/litellm/fake-toolset-litellm.ts +110 -0
  378. package/src/litellm/index.ts +140 -0
  379. package/src/litellm/key-delete.test.ts +212 -0
  380. package/src/litellm/key-errors.ts +241 -0
  381. package/src/litellm/key-form.ts +200 -0
  382. package/src/litellm/key-harness.ts +121 -0
  383. package/src/litellm/key-metadata-errors.ts +49 -0
  384. package/src/litellm/key-metadata.test.ts +156 -0
  385. package/src/litellm/key-metadata.ts +74 -0
  386. package/src/litellm/key-operations.ts +184 -0
  387. package/src/litellm/key-rotation.test.ts +110 -0
  388. package/src/litellm/key-scope.test.ts +135 -0
  389. package/src/litellm/key-secret.test.ts +248 -0
  390. package/src/litellm/key-secret.ts +137 -0
  391. package/src/litellm/key-transport.test.ts +166 -0
  392. package/src/litellm/key.test.ts +231 -0
  393. package/src/litellm/key.ts +213 -0
  394. package/src/litellm/mcp-server-adopt.test.ts +86 -0
  395. package/src/litellm/mcp-server-alias.test.ts +184 -0
  396. package/src/litellm/mcp-server-credential.ts +85 -0
  397. package/src/litellm/mcp-server-errors.ts +141 -0
  398. package/src/litellm/mcp-server-exports.test.ts +15 -0
  399. package/src/litellm/mcp-server-form.test.ts +241 -0
  400. package/src/litellm/mcp-server-form.ts +233 -0
  401. package/src/litellm/mcp-server-handlers.test.ts +150 -0
  402. package/src/litellm/mcp-server-locate.ts +66 -0
  403. package/src/litellm/mcp-server-operations.ts +111 -0
  404. package/src/litellm/mcp-server-registry.test.ts +146 -0
  405. package/src/litellm/mcp-server-retype.test.ts +114 -0
  406. package/src/litellm/mcp-server-shadow.test.ts +91 -0
  407. package/src/litellm/mcp-server-shadow.ts +45 -0
  408. package/src/litellm/mcp-server-tools.test.ts +85 -0
  409. package/src/litellm/mcp-server-types.ts +143 -0
  410. package/src/litellm/mcp-server-url.ts +66 -0
  411. package/src/litellm/mcp-server.test.ts +213 -0
  412. package/src/litellm/mcp-server.ts +225 -0
  413. package/src/litellm/model-claim.ts +69 -0
  414. package/src/litellm/model-credential.ts +45 -0
  415. package/src/litellm/model-errors.ts +168 -0
  416. package/src/litellm/model-foreign-row.test.ts +173 -0
  417. package/src/litellm/model-form.test.ts +228 -0
  418. package/src/litellm/model-form.ts +223 -0
  419. package/src/litellm/model-locate.test.ts +128 -0
  420. package/src/litellm/model-locate.ts +67 -0
  421. package/src/litellm/model-operations.test.ts +93 -0
  422. package/src/litellm/model-operations.ts +146 -0
  423. package/src/litellm/model-params-preserve.test.ts +119 -0
  424. package/src/litellm/model-recovery.test.ts +57 -0
  425. package/src/litellm/model-types.ts +112 -0
  426. package/src/litellm/model.test.ts +222 -0
  427. package/src/litellm/model.ts +237 -0
  428. package/src/litellm/policy-attachment-form.ts +103 -0
  429. package/src/litellm/policy-attachment-operations.ts +74 -0
  430. package/src/litellm/policy-attachment-types.ts +48 -0
  431. package/src/litellm/policy-attachment.test.ts +198 -0
  432. package/src/litellm/policy-attachment.ts +178 -0
  433. package/src/litellm/policy-form.ts +122 -0
  434. package/src/litellm/policy-operations.ts +112 -0
  435. package/src/litellm/policy-types.ts +58 -0
  436. package/src/litellm/policy.test.ts +214 -0
  437. package/src/litellm/policy.ts +146 -0
  438. package/src/litellm/providers.ts +24 -3
  439. package/src/litellm/registry-errors.ts +88 -0
  440. package/src/litellm/registry-exports.test.ts +66 -0
  441. package/src/litellm/registry-support.ts +109 -0
  442. package/src/litellm/team-form.ts +140 -0
  443. package/src/litellm/team-members.test.ts +150 -0
  444. package/src/litellm/team-members.ts +58 -0
  445. package/src/litellm/team-operations.ts +90 -0
  446. package/src/litellm/team-permission.ts +159 -0
  447. package/src/litellm/team-read.test.ts +89 -0
  448. package/src/litellm/team-types.ts +106 -0
  449. package/src/litellm/team.test.ts +206 -0
  450. package/src/litellm/team.ts +159 -0
  451. package/src/litellm/tool-policy-form.ts +94 -0
  452. package/src/litellm/tool-policy-operations.ts +58 -0
  453. package/src/litellm/tool-policy-types.ts +48 -0
  454. package/src/litellm/tool-policy.test.ts +172 -0
  455. package/src/litellm/tool-policy.ts +125 -0
  456. package/src/litellm/toolset-form.ts +104 -0
  457. package/src/litellm/toolset-operations.ts +90 -0
  458. package/src/litellm/toolset-types.ts +47 -0
  459. package/src/litellm/toolset.test.ts +198 -0
  460. package/src/litellm/toolset.ts +151 -0
  461. package/src/ownership/ownership.test.ts +33 -1
  462. package/src/ownership/rows.ts +53 -0
  463. package/src/postgres/connection.ts +42 -7
  464. package/src/postgres/database-bigint-serialization.test.ts +1 -0
  465. package/src/postgres/database-refusals.test.ts +2 -2
  466. package/src/postgres/database-sql.ts +36 -9
  467. package/src/postgres/fake-grants-parse.ts +189 -0
  468. package/src/postgres/fake-grants-read.ts +250 -0
  469. package/src/postgres/fake-grants-sql.ts +207 -0
  470. package/src/postgres/fake-grants-write.ts +90 -0
  471. package/src/postgres/fake-role-sql.ts +240 -0
  472. package/src/postgres/fake-schema-semantics.test.ts +36 -0
  473. package/src/postgres/fake-schema-sql.ts +146 -0
  474. package/src/postgres/fake-sql-parse.ts +86 -0
  475. package/src/postgres/fake-sql-quote.ts +36 -0
  476. package/src/postgres/fake-sql.ts +166 -46
  477. package/src/postgres/fixtures/pg-REL_18_6-acl.h.txt +147 -0
  478. package/src/postgres/fixtures/pg-REL_18_6-aclexplode-func.txt +31 -0
  479. package/src/postgres/fixtures/pg-REL_18_6-alter-default-privileges-synopsis.txt +82 -0
  480. package/src/postgres/fixtures/pg-REL_18_6-alter_role.sgml.txt +44 -0
  481. package/src/postgres/fixtures/pg-REL_18_6-create_role.sgml.txt +26 -0
  482. package/src/postgres/fixtures/pg-REL_18_6-grant-synopsis.txt +95 -0
  483. package/src/postgres/fixtures/pg-REL_18_6-pg_attribute.h.txt +24 -0
  484. package/src/postgres/fixtures/pg-REL_18_6-pg_auth_members.h.txt +62 -0
  485. package/src/postgres/fixtures/pg-REL_18_6-pg_authid.h.txt +70 -0
  486. package/src/postgres/fixtures/pg-REL_18_6-pg_class.h.txt +33 -0
  487. package/src/postgres/fixtures/pg-REL_18_6-pg_default_acl.h.txt +81 -0
  488. package/src/postgres/fixtures/pg-REL_18_6-pg_namespace.h.txt +28 -0
  489. package/src/postgres/fixtures/pg-REL_18_6-revoke-synopsis.txt +124 -0
  490. package/src/postgres/grants-acl.test.ts +61 -0
  491. package/src/postgres/grants-attrs.ts +185 -0
  492. package/src/postgres/grants-database.test.ts +158 -0
  493. package/src/postgres/grants-declare.ts +172 -0
  494. package/src/postgres/grants-delete-proof.test.ts +154 -0
  495. package/src/postgres/grants-diff.ts +101 -0
  496. package/src/postgres/grants-errors.ts +215 -0
  497. package/src/postgres/grants-existence-guards.test.ts +113 -0
  498. package/src/postgres/grants-existence.ts +101 -0
  499. package/src/postgres/grants-lifecycle-guards.test.ts +208 -0
  500. package/src/postgres/grants-lifecycle-repair.test.ts +208 -0
  501. package/src/postgres/grants-lifecycle.test.ts +181 -0
  502. package/src/postgres/grants-ops.ts +229 -0
  503. package/src/postgres/grants-owner-revoke.test.ts +121 -0
  504. package/src/postgres/grants-ownership.ts +49 -0
  505. package/src/postgres/grants-plan-collateral.test.ts +202 -0
  506. package/src/postgres/grants-plan-diff.test.ts +204 -0
  507. package/src/postgres/grants-plan-restore.ts +43 -0
  508. package/src/postgres/grants-plan-revocations.test.ts +69 -0
  509. package/src/postgres/grants-plan.test.ts +148 -0
  510. package/src/postgres/grants-plan.ts +245 -0
  511. package/src/postgres/grants-provenance.test.ts +134 -0
  512. package/src/postgres/grants-psql-lifecycle.test.ts +105 -0
  513. package/src/postgres/grants-read-absent.test.ts +91 -0
  514. package/src/postgres/grants-read-defaults.ts +51 -0
  515. package/src/postgres/grants-read.ts +250 -0
  516. package/src/postgres/grants-refusals.test.ts +136 -0
  517. package/src/postgres/grants-refuse.ts +119 -0
  518. package/src/postgres/grants-relkind-wire.test.ts +111 -0
  519. package/src/postgres/grants-repair-plan.test.ts +201 -0
  520. package/src/postgres/grants-sql.test.ts +128 -0
  521. package/src/postgres/grants-sql.ts +144 -0
  522. package/src/postgres/grants-words.ts +34 -0
  523. package/src/postgres/grants.ts +110 -0
  524. package/src/postgres/index.ts +129 -5
  525. package/src/postgres/installed-classifier.ts +36 -0
  526. package/src/postgres/providers.ts +33 -9
  527. package/src/postgres/psql-executor-escaping.test.ts +67 -0
  528. package/src/postgres/psql-executor.test.ts +63 -2
  529. package/src/postgres/psql-executor.ts +74 -34
  530. package/src/postgres/role-adopt.test.ts +98 -0
  531. package/src/postgres/role-attrs.ts +137 -0
  532. package/src/postgres/role-diff.ts +93 -0
  533. package/src/postgres/role-errors.ts +206 -0
  534. package/src/postgres/role-guard.test.ts +60 -0
  535. package/src/postgres/role-identity.test.ts +197 -0
  536. package/src/postgres/role-lifecycle.test.ts +228 -0
  537. package/src/postgres/role-membership-options.test.ts +73 -0
  538. package/src/postgres/role-membership-repair.test.ts +153 -0
  539. package/src/postgres/role-membership-sql.ts +223 -0
  540. package/src/postgres/role-password-ascii.test.ts +53 -0
  541. package/src/postgres/role-password-transports.test.ts +123 -0
  542. package/src/postgres/role-password.test.ts +158 -0
  543. package/src/postgres/role-privilege.test.ts +107 -0
  544. package/src/postgres/role-provenance.test.ts +200 -0
  545. package/src/postgres/role-provider.ts +105 -0
  546. package/src/postgres/role-refusals.test.ts +225 -0
  547. package/src/postgres/role-scram.test.ts +38 -0
  548. package/src/postgres/role-scram.ts +40 -0
  549. package/src/postgres/role-secrets.ts +61 -0
  550. package/src/postgres/role-sql.ts +213 -0
  551. package/src/postgres/role-valid-until.test.ts +68 -0
  552. package/src/postgres/role.ts +226 -0
  553. package/src/postgres/schema-assert.ts +139 -0
  554. package/src/postgres/schema-attrs.ts +71 -0
  555. package/src/postgres/schema-delete-proofs.test.ts +192 -0
  556. package/src/postgres/schema-delete.ts +96 -0
  557. package/src/postgres/schema-diff.ts +44 -0
  558. package/src/postgres/schema-drop-2bp01.test.ts +146 -0
  559. package/src/postgres/schema-errors.ts +209 -0
  560. package/src/postgres/schema-handlers.test.ts +193 -0
  561. package/src/postgres/schema-identity.ts +39 -0
  562. package/src/postgres/schema-lifecycle.test.ts +246 -0
  563. package/src/postgres/schema-race.test.ts +70 -0
  564. package/src/postgres/schema-reconcile.ts +100 -0
  565. package/src/postgres/schema-refusals.test.ts +144 -0
  566. package/src/postgres/schema-socket.test.ts +162 -0
  567. package/src/postgres/schema-sql.ts +130 -0
  568. package/src/postgres/schema-takeover.test.ts +149 -0
  569. package/src/postgres/schema-test-kit.ts +85 -0
  570. package/src/postgres/schema.ts +130 -0
  571. package/src/release/binary-alias.test.ts +3 -3
  572. package/src/release/binary-claims.test.ts +3 -3
  573. package/src/release/binary-directory-umask.test.ts +65 -0
  574. package/src/release/fake-engine.ts +15 -0
  575. package/src/valkey/acl-attrs.ts +98 -0
  576. package/src/valkey/acl-compare.ts +57 -0
  577. package/src/valkey/acl-connection.ts +13 -0
  578. package/src/valkey/acl-delete.test.ts +127 -0
  579. package/src/valkey/acl-form.ts +239 -0
  580. package/src/valkey/acl-managed.ts +13 -0
  581. package/src/valkey/acl-ops.ts +122 -0
  582. package/src/valkey/acl-ownership.test.ts +118 -0
  583. package/src/valkey/acl-prefix.test.ts +38 -0
  584. package/src/valkey/acl-profiles.ts +81 -0
  585. package/src/valkey/acl-reconcile.ts +180 -0
  586. package/src/valkey/acl-reserved.test.ts +69 -0
  587. package/src/valkey/acl-revocation.test.ts +94 -0
  588. package/src/valkey/acl-seat-prefix.test.ts +111 -0
  589. package/src/valkey/acl-validation.test.ts +54 -0
  590. package/src/valkey/acl.test.ts +215 -0
  591. package/src/valkey/acl.ts +169 -0
  592. package/src/valkey/connection-service.ts +46 -0
  593. package/src/valkey/connection.ts +180 -0
  594. package/src/valkey/errors.ts +239 -0
  595. package/src/valkey/fake-acl.ts +128 -0
  596. package/src/valkey/fake-valkey.ts +224 -0
  597. package/src/valkey/handler-test-fixture.ts +43 -0
  598. package/src/valkey/handlers.test.ts +205 -0
  599. package/src/valkey/index.ts +54 -0
  600. package/src/valkey/instance-attrs.ts +48 -0
  601. package/src/valkey/instance-form.ts +104 -0
  602. package/src/valkey/instance.test.ts +108 -0
  603. package/src/valkey/instance.ts +160 -0
  604. package/src/valkey/provider-socket-fixture.ts +71 -0
  605. package/src/valkey/providers.test.ts +129 -0
  606. package/src/valkey/providers.ts +21 -0
  607. package/src/valkey/resp.ts +212 -0
  608. package/src/valkey/transport-bounds.test.ts +150 -0
  609. package/src/valkey/transport-error.ts +37 -0
  610. package/src/valkey/transport.test.ts +158 -0
  611. package/src/valkey/transport.ts +109 -0
  612. package/src/valkey/upstream-bounds.test.ts +14 -0
@@ -0,0 +1,201 @@
1
+ /**
2
+ * The pure statement planners: `planRepair`'s per-class repair (no-op, drift, the
3
+ * revoke-only delete twin, the grant-option split, the two PUBLIC clears), the measured
4
+ * rules the convergence proof depends on (a table revoke clears its declared columns;
5
+ * owned objects are left alone). Removal revokes live in `grants-plan-revocations.test.ts`.
6
+ * `resolveProps` and the refusals live in `grants-plan.test.ts`.
7
+ */
8
+ import { describe, expect, test } from 'bun:test';
9
+ import type { PostgresGrantsProps } from './grants-attrs.ts';
10
+ import { resolveProps } from './grants-declare.ts';
11
+ import { planRepair } from './grants-plan.ts';
12
+ import type { LiveGrants } from './grants-read.ts';
13
+
14
+ const props: PostgresGrantsProps = {
15
+ role: 'seat_writer',
16
+ database: 'agents',
17
+ schema: 'app',
18
+ };
19
+
20
+ const live = (over: Partial<LiveGrants> = {}): LiveGrants => ({
21
+ schemaExists: true,
22
+ schema: { role: [], public: [] },
23
+ tables: [],
24
+ columns: [],
25
+ defaults: [],
26
+ schemaOwnedByRole: false,
27
+ ownedTables: [],
28
+ ...over,
29
+ });
30
+
31
+ describe('planRepair', () => {
32
+ test('a class whose live set equals the declaration contributes nothing', () => {
33
+ expect(
34
+ planRepair(
35
+ resolveProps({ ...props, tables: [{ table: 'widgets', privileges: ['select'] }] }),
36
+ live({
37
+ schema: { role: ['usage'], public: [] },
38
+ tables: [{ table: 'widgets', relkind: 'r', role: ['select'], public: [] }],
39
+ }),
40
+ ),
41
+ ).toEqual([]);
42
+ });
43
+
44
+ test('drift on a table is REVOKE ALL then GRANT the declared words', () => {
45
+ expect(
46
+ planRepair(
47
+ resolveProps({ ...props, tables: [{ table: 'widgets', privileges: ['select'] }] }),
48
+ live({
49
+ tables: [{ table: 'widgets', relkind: 'r', role: ['insert', 'update'], public: [] }],
50
+ }),
51
+ ),
52
+ ).toEqual([
53
+ 'REVOKE ALL ON SCHEMA "app" FROM "seat_writer"',
54
+ 'GRANT usage ON SCHEMA "app" TO "seat_writer"',
55
+ 'REVOKE ALL ON "app"."widgets" FROM "seat_writer"',
56
+ 'GRANT select ON "app"."widgets" TO "seat_writer"',
57
+ ]);
58
+ });
59
+
60
+ test('a class that declares nothing but holds live grants gets the revoke only', () => {
61
+ expect(
62
+ planRepair(
63
+ resolveProps({ ...props, schemaUsage: false }),
64
+ live({ schema: { role: ['usage'], public: [] } }),
65
+ ),
66
+ ).toEqual(['REVOKE ALL ON SCHEMA "app" FROM "seat_writer"']);
67
+ });
68
+
69
+ test('a mixed grant-option list splits into one plain grant and one option grant', () => {
70
+ const plan = planRepair(
71
+ resolveProps({
72
+ ...props,
73
+ tables: [{ table: 'widgets', privileges: ['select', 'insert*'] }],
74
+ }),
75
+ live({ tables: [{ table: 'widgets', relkind: 'r', role: ['delete'], public: [] }] }),
76
+ );
77
+ expect(plan).toContain('GRANT select ON "app"."widgets" TO "seat_writer"');
78
+ expect(plan).toContain('GRANT insert ON "app"."widgets" TO "seat_writer" WITH GRANT OPTION');
79
+ expect(plan.some((s) => s === 'GRANT select, insert ON "app"."widgets" TO "seat_writer"')).toBe(
80
+ false,
81
+ );
82
+ });
83
+
84
+ test('a table revoke also re-grants the table\u2019s declared columns in the SAME pass — measured on PG 18.6, a table REVOKE ALL clears that grantee\u2019s column entries', () => {
85
+ const plan = planRepair(
86
+ resolveProps({
87
+ ...props,
88
+ tables: [{ table: 'widgets', privileges: ['select', 'insert'] }],
89
+ columnGrants: [{ table: 'widgets', column: 'id', privileges: ['select'] }],
90
+ }),
91
+ live({
92
+ schema: { role: ['usage'], public: [] },
93
+ tables: [{ table: 'widgets', relkind: 'r', role: ['select'], public: [] }],
94
+ columns: [
95
+ { table: 'widgets', column: 'id', role: ['select'], public: [], restorable: ['select'] },
96
+ ],
97
+ }),
98
+ );
99
+ expect(plan).toEqual([
100
+ 'REVOKE ALL ON "app"."widgets" FROM "seat_writer"',
101
+ 'GRANT insert, select ON "app"."widgets" TO "seat_writer"',
102
+ 'REVOKE ALL ("id") ON "app"."widgets" FROM "seat_writer"',
103
+ 'GRANT select ("id") ON "app"."widgets" TO "seat_writer"',
104
+ ]);
105
+ });
106
+
107
+ test('drift on a default-privileges entry is the ALTER DEFAULT PRIVILEGES revoke-then-grant pair', () => {
108
+ expect(
109
+ planRepair(
110
+ resolveProps({
111
+ ...props,
112
+ defaultPrivileges: [{ forRole: 'owner_role', privileges: ['select'] }],
113
+ }),
114
+ live({
115
+ schema: { role: ['usage'], public: [] },
116
+ defaults: [{ forRole: 'owner_role', role: ['insert'] }],
117
+ }),
118
+ ),
119
+ ).toEqual([
120
+ 'ALTER DEFAULT PRIVILEGES FOR ROLE "owner_role" IN SCHEMA "app" REVOKE ALL ON TABLES FROM "seat_writer"',
121
+ 'ALTER DEFAULT PRIVILEGES FOR ROLE "owner_role" IN SCHEMA "app" GRANT select ON TABLES TO "seat_writer"',
122
+ ]);
123
+ });
124
+
125
+ test('objects the declared role OWNS contribute nothing — an owner holds every privilege implicitly and a revoke cannot take it away', () => {
126
+ expect(
127
+ planRepair(
128
+ resolveProps({
129
+ ...props,
130
+ tables: [{ table: 'notes', privileges: ['select', 'insert'] }],
131
+ }),
132
+ live({
133
+ schemaOwnedByRole: true,
134
+ ownedTables: ['notes'],
135
+ tables: [{ table: 'notes', relkind: 'r', role: [], public: [] }],
136
+ }),
137
+ ),
138
+ ).toEqual([]);
139
+ });
140
+
141
+ test('ownership is per object: a table the role does NOT own is still repaired beside the owned one', () => {
142
+ const plan = planRepair(
143
+ resolveProps({
144
+ ...props,
145
+ tables: [
146
+ { table: 'notes', privileges: ['select'] },
147
+ { table: 'shared', privileges: ['select'] },
148
+ ],
149
+ }),
150
+ live({
151
+ ownedTables: ['notes'],
152
+ tables: [
153
+ { table: 'notes', relkind: 'r', role: [], public: [] },
154
+ { table: 'shared', relkind: 'r', role: ['delete'], public: [] },
155
+ ],
156
+ }),
157
+ );
158
+ expect(plan.some((s) => s.includes('"notes"'))).toBe(false);
159
+ expect(plan).toContain('REVOKE ALL ON "app"."shared" FROM "seat_writer"');
160
+ expect(plan).toContain('GRANT select ON "app"."shared" TO "seat_writer"');
161
+ });
162
+ });
163
+
164
+ describe('the PUBLIC clears', () => {
165
+ const declared = resolveProps({ ...props, revokeFromPublic: true });
166
+
167
+ test('PUBLIC schema words earn the schema revoke; PUBLIC table words earn the tables revoke', () => {
168
+ expect(planRepair(declared, live({ schema: { role: ['usage'], public: ['usage'] } }))).toEqual([
169
+ 'REVOKE ALL ON SCHEMA "app" FROM PUBLIC',
170
+ ]);
171
+ expect(
172
+ planRepair(
173
+ declared,
174
+ live({
175
+ schema: { role: ['usage'], public: [] },
176
+ columns: [
177
+ { table: 'widgets', column: 'id', role: [], public: ['select'], restorable: [] },
178
+ ],
179
+ }),
180
+ ),
181
+ ).toEqual(['REVOKE ALL ON ALL TABLES IN SCHEMA "app" FROM PUBLIC']);
182
+ });
183
+
184
+ test('a PUBLIC grant on a SEQUENCE is outside the bulk revoke\u2019s reach (relkind S) and never blocks convergence', () => {
185
+ expect(
186
+ planRepair(
187
+ declared,
188
+ live({
189
+ schema: { role: ['usage'], public: [] },
190
+ tables: [{ table: 'app_seq', relkind: 'S', role: [], public: ['usage'] }],
191
+ }),
192
+ ),
193
+ ).toEqual([]);
194
+ });
195
+
196
+ test('without the flag, PUBLIC holds are never drift', () => {
197
+ expect(
198
+ planRepair(resolveProps(props), live({ schema: { role: ['usage'], public: ['usage'] } })),
199
+ ).toEqual([]);
200
+ });
201
+ });
@@ -0,0 +1,128 @@
1
+ /**
2
+ * The statement builders' exact output: every identifier `quoteIdent`ed, every clause in the
3
+ * one order the parser (`fake-grants-parse.ts`, in tests) and a real server accept, and the
4
+ * mixed grant-option refusal that protects the declaration from over-granting (one
5
+ * `WITH GRANT OPTION` clause would grant the option to EVERY listed privilege).
6
+ */
7
+ import { describe, expect, test } from 'bun:test';
8
+ import {
9
+ grantColumnSql,
10
+ grantDefaultSql,
11
+ grantSchemaSql,
12
+ grantTableSql,
13
+ revokeColumnSql,
14
+ revokeDefaultSql,
15
+ revokePublicSchemaSql,
16
+ revokePublicTablesSql,
17
+ revokeSchemaSql,
18
+ revokeTableSql,
19
+ } from './grants-sql.ts';
20
+
21
+ describe('grant builders', () => {
22
+ test('a table grant lists words comma-separated on the qualified relation', () => {
23
+ expect(grantTableSql('app', 'widgets', 'seat_writer', ['select', 'insert'])).toBe(
24
+ 'GRANT select, insert ON "app"."widgets" TO "seat_writer"',
25
+ );
26
+ });
27
+
28
+ test('a grant-option list carries exactly one WITH GRANT OPTION clause at the end', () => {
29
+ expect(grantTableSql('app', 'widgets', 'seat_writer', ['select*'])).toBe(
30
+ 'GRANT select ON "app"."widgets" TO "seat_writer" WITH GRANT OPTION',
31
+ );
32
+ });
33
+
34
+ test('a MIXED list throws instead of over-granting the option to the plain words', () => {
35
+ expect(() => grantTableSql('app', 'widgets', 'seat_writer', ['select', 'insert*'])).toThrow(
36
+ /mixed grant-option list/,
37
+ );
38
+ });
39
+
40
+ test('an out-of-vocabulary word is refused before it reaches a GRANT clause', () => {
41
+ // The builders are exported API, so the vocabulary check lives inside them, not only
42
+ // in the resource path: an injected fragment can never be concatenated into SQL.
43
+ expect(() => grantTableSql('app', 'widgets', 'seat_writer', ['select', 'execute'])).toThrow(
44
+ /not a privilege/,
45
+ );
46
+ expect(() => grantTableSql('app', 'widgets', 'seat_writer', ['*'])).toThrow(/not a privilege/);
47
+ });
48
+
49
+ test('a column grant parenthesizes the column after the words', () => {
50
+ expect(grantColumnSql('app', 'widgets', 'id', 'seat_writer', ['select'])).toBe(
51
+ 'GRANT select ("id") ON "app"."widgets" TO "seat_writer"',
52
+ );
53
+ });
54
+
55
+ test('a multi-word column grant repeats the synopsis per word', () => {
56
+ // One trailing column list binds only to the privilege it follows (gram.y@REL_18_6):
57
+ // `GRANT select, update ("id")` is a table-level SELECT plus a column-level UPDATE.
58
+ // Every word carries its own list, so all of them land on the column and nothing on
59
+ // the relation — a grant the declaration did not name is never issued.
60
+ expect(grantColumnSql('app', 'widgets', 'amount', 'seat_writer', ['select', 'update'])).toBe(
61
+ 'GRANT select ("amount"), update ("amount") ON "app"."widgets" TO "seat_writer"',
62
+ );
63
+ expect(grantColumnSql('app', 'widgets', 'amount', 'seat_writer', ['select*', 'update*'])).toBe(
64
+ 'GRANT select ("amount"), update ("amount") ON "app"."widgets" TO "seat_writer" WITH GRANT OPTION',
65
+ );
66
+ });
67
+
68
+ test('a schema grant names SCHEMA, not a relation', () => {
69
+ expect(grantSchemaSql('app', 'seat_writer', ['usage'])).toBe(
70
+ 'GRANT usage ON SCHEMA "app" TO "seat_writer"',
71
+ );
72
+ });
73
+
74
+ test('a default-privileges grant carries FOR ROLE (the creator) IN SCHEMA, grantee last', () => {
75
+ expect(grantDefaultSql('app', 'ledger_owner', 'seat_writer', ['select'])).toBe(
76
+ 'ALTER DEFAULT PRIVILEGES FOR ROLE "ledger_owner" IN SCHEMA "app" GRANT select ON TABLES TO "seat_writer"',
77
+ );
78
+ });
79
+ });
80
+
81
+ describe('revoke builders', () => {
82
+ test('a table revoke', () => {
83
+ expect(revokeTableSql('app', 'widgets', 'seat_writer')).toBe(
84
+ 'REVOKE ALL ON "app"."widgets" FROM "seat_writer"',
85
+ );
86
+ });
87
+
88
+ test('a column revoke', () => {
89
+ expect(revokeColumnSql('app', 'widgets', 'id', 'seat_writer')).toBe(
90
+ 'REVOKE ALL ("id") ON "app"."widgets" FROM "seat_writer"',
91
+ );
92
+ });
93
+
94
+ test('a schema revoke', () => {
95
+ expect(revokeSchemaSql('app', 'seat_writer')).toBe(
96
+ 'REVOKE ALL ON SCHEMA "app" FROM "seat_writer"',
97
+ );
98
+ });
99
+
100
+ test('a default-privileges revoke', () => {
101
+ expect(revokeDefaultSql('app', 'ledger_owner', 'seat_writer')).toBe(
102
+ 'ALTER DEFAULT PRIVILEGES FOR ROLE "ledger_owner" IN SCHEMA "app" REVOKE ALL ON TABLES FROM "seat_writer"',
103
+ );
104
+ });
105
+
106
+ test('the PUBLIC schema revoke uses the bare keyword, never a quoted role', () => {
107
+ expect(revokePublicSchemaSql('app')).toBe('REVOKE ALL ON SCHEMA "app" FROM PUBLIC');
108
+ });
109
+
110
+ test('the PUBLIC tables revoke clears every relation in the schema', () => {
111
+ expect(revokePublicTablesSql('app')).toBe(
112
+ 'REVOKE ALL ON ALL TABLES IN SCHEMA "app" FROM PUBLIC',
113
+ );
114
+ });
115
+ });
116
+
117
+ describe('quoting', () => {
118
+ test('embedded quotes and hyphens survive in every position', () => {
119
+ expect(grantTableSql('a"b', 'weird-name', 'x y', ['select'])).toBe(
120
+ 'GRANT select ON "a""b"."weird-name" TO "x y"',
121
+ );
122
+ // A single quote needs no doubling inside a double-quoted identifier — only the
123
+ // identifier quote character ("") is doubled (grant.sgml@REL_18_6, SQL syntax).
124
+ expect(grantColumnSql('a"b', 'weird-name', "it's", 'x y', ['select'])).toBe(
125
+ `GRANT select ("it's") ON "a""b"."weird-name" TO "x y"`,
126
+ );
127
+ });
128
+ });
@@ -0,0 +1,144 @@
1
+ /**
2
+ * The statements `Postgres.Grants` ever ISSUES: ten builders, one statement each. The READ
3
+ * half lives in `grants-read.ts` (it goes through `aclexplode`, never `aclitem` text); this
4
+ * file is writes only. Every free-form value is bound (`$n`) or `quoteIdent`ed, never
5
+ * hand-concatenated, and every privilege word is checked against the vocabularies before
6
+ * it is concatenated — the builders are exported API, so the check cannot live only in
7
+ * the resource path.
8
+ *
9
+ * ⛔ NO `CASCADE`, EVER: a revoke whose grantee re-granted onward is refused by the server
10
+ * rather than silently removing a third role's grant (the declaration does not name that
11
+ * role). The refusal surfaces as the driver's own statement error; the operator adds the
12
+ * grant to their declaration or revokes by hand.
13
+ * ⛔ A GRANT LIST UNDER ONE `WITH GRANT OPTION` OPTION CLAUSE GRANTS THE OPTION TO EVERY
14
+ * LISTED PRIVILEGE. The builders therefore throw on a MIXED list (some words marked `*`,
15
+ * some not): `planRepair` splits the words with `splitGrantWords` and issues two grants —
16
+ * the split is the only shape that cannot over-grant beyond the declaration.
17
+ */
18
+ import { ALL_GRANT_WORDS } from './grants-attrs.ts';
19
+ import { quoteIdent } from './database-sql.ts';
20
+
21
+ const splitParts = (
22
+ words: ReadonlyArray<string>,
23
+ ): { readonly parts: ReadonlyArray<string>; readonly anyOption: boolean } => {
24
+ const parts: string[] = [];
25
+ let optionCount = 0;
26
+ for (const word of words) {
27
+ const base = word.endsWith('*') ? word.slice(0, -1) : word;
28
+ // The builders are exported API, so the vocabulary check does not only live in the
29
+ // resource path: a word outside every class vocabulary (a bare `*`, an injected
30
+ // fragment) is refused here instead of concatenated into a GRANT clause.
31
+ if (!ALL_GRANT_WORDS.includes(base)) {
32
+ throw new Error(
33
+ `grants-sql: "${base}" is not a privilege this family can grant — see the three ` +
34
+ 'vocabularies in grants-attrs.ts (traced to acl.h at REL_18_6)',
35
+ );
36
+ }
37
+ if (word.endsWith('*')) optionCount += 1;
38
+ parts.push(base);
39
+ }
40
+ if (optionCount > 0 && optionCount < words.length) {
41
+ throw new Error(
42
+ `grants-sql: a mixed grant-option list cannot be one GRANT statement: ${words.join(', ')}` +
43
+ ' — split it with splitGrantWords first (one option clause grants the option to every word)',
44
+ );
45
+ }
46
+ return { parts, anyOption: optionCount > 0 };
47
+ };
48
+
49
+ const optionClause = (anyOption: boolean): string => (anyOption ? ' WITH GRANT OPTION' : '');
50
+
51
+ /** `GRANT` on one relation. `words` are already validated lowercase declared words; a
52
+ * trailing `*` (WITH GRANT OPTION) becomes the option clause — the list must share one
53
+ * flag (see the header). */
54
+ export const grantTableSql = (
55
+ schema: string,
56
+ table: string,
57
+ role: string,
58
+ words: ReadonlyArray<string>,
59
+ ): string => {
60
+ const { parts, anyOption } = splitParts(words);
61
+ return `GRANT ${parts.join(', ')} ON ${quoteIdent(schema)}.${quoteIdent(table)} TO ${quoteIdent(role)}${optionClause(anyOption)}`;
62
+ };
63
+
64
+ /** `GRANT` on one column of one relation.
65
+ *
66
+ * Each word is emitted as its own synopsis inside one statement — `GRANT select ("col"),
67
+ * update ("col") ON …`. A column list binds only to the privilege it follows
68
+ * (gram.y@REL_18_6), so a word list under ONE trailing column list parses as a table-level
69
+ * grant of every word but the last (which alone lands on the column) — a silent over-grant
70
+ * the declaration did not name. Repeating the synopsis is the one shape that puts every
71
+ * declared word on the column and nothing on the relation. */
72
+ export const grantColumnSql = (
73
+ schema: string,
74
+ table: string,
75
+ column: string,
76
+ role: string,
77
+ words: ReadonlyArray<string>,
78
+ ): string => {
79
+ const { parts, anyOption } = splitParts(words);
80
+ const columnList = `(${quoteIdent(column)})`;
81
+ return `GRANT ${parts.map((word) => `${word} ${columnList}`).join(', ')} ON ${quoteIdent(schema)}.${quoteIdent(table)} TO ${quoteIdent(role)}${optionClause(anyOption)}`;
82
+ };
83
+
84
+ /** `GRANT` on the schema itself (`USAGE` / `CREATE`). */
85
+ export const grantSchemaSql = (
86
+ schema: string,
87
+ role: string,
88
+ words: ReadonlyArray<string>,
89
+ ): string => {
90
+ const { parts, anyOption } = splitParts(words);
91
+ return `GRANT ${parts.join(', ')} ON SCHEMA ${quoteIdent(schema)} TO ${quoteIdent(role)}${optionClause(anyOption)}`;
92
+ };
93
+
94
+ /** `ALTER DEFAULT PRIVILEGES FOR ROLE <creator> IN SCHEMA <schema> GRANT … ON TABLES TO
95
+ * <role>` — the creator is `forRole`, the grantee is always the resource's role. */
96
+ export const grantDefaultSql = (
97
+ schema: string,
98
+ forRole: string,
99
+ role: string,
100
+ words: ReadonlyArray<string>,
101
+ ): string => {
102
+ const { parts, anyOption } = splitParts(words);
103
+ return `ALTER DEFAULT PRIVILEGES FOR ROLE ${quoteIdent(forRole)} IN SCHEMA ${quoteIdent(schema)} GRANT ${parts.join(', ')} ON TABLES TO ${quoteIdent(role)}${optionClause(anyOption)}`;
104
+ };
105
+
106
+ /** `REVOKE ALL ON <schema>.<table> FROM <role>` — the repair's first half for a drifted
107
+ * table. Revoking a privilege the grantee does not hold is a silent no-op on the server, so
108
+ * the repair's revoke is idempotent; what the server REFUSES is a revoke that would have to
109
+ * cascade (see the header). */
110
+ export const revokeTableSql = (schema: string, table: string, role: string): string =>
111
+ `REVOKE ALL ON ${quoteIdent(schema)}.${quoteIdent(table)} FROM ${quoteIdent(role)}`;
112
+
113
+ /** `REVOKE ALL (<column>) ON <schema>.<table> FROM <role>` — same reasoning as
114
+ * {@link revokeTableSql}. */
115
+ export const revokeColumnSql = (
116
+ schema: string,
117
+ table: string,
118
+ column: string,
119
+ role: string,
120
+ ): string =>
121
+ `REVOKE ALL (${quoteIdent(column)}) ON ${quoteIdent(schema)}.${quoteIdent(table)} FROM ${quoteIdent(role)}`;
122
+
123
+ /** `REVOKE ALL ON SCHEMA <schema> FROM <role>` — the repair's first half for the schema's
124
+ * own privileges. */
125
+ export const revokeSchemaSql = (schema: string, role: string): string =>
126
+ `REVOKE ALL ON SCHEMA ${quoteIdent(schema)} FROM ${quoteIdent(role)}`;
127
+
128
+ /** `ALTER DEFAULT PRIVILEGES FOR ROLE <creator> IN SCHEMA <schema> REVOKE ALL ON TABLES
129
+ * FROM <role>`. */
130
+ export const revokeDefaultSql = (schema: string, forRole: string, role: string): string =>
131
+ `ALTER DEFAULT PRIVILEGES FOR ROLE ${quoteIdent(forRole)} IN SCHEMA ${quoteIdent(schema)} REVOKE ALL ON TABLES FROM ${quoteIdent(role)}`;
132
+
133
+ /** `REVOKE ALL ON SCHEMA <schema> FROM PUBLIC` — the `revokeFromPublic` half for the
134
+ * schema itself. PUBLIC is the bare keyword (`aclitem`'s empty grantee, acl.h@REL_18_6
135
+ * `ACL_ID_PUBLIC`). */
136
+ export const revokePublicSchemaSql = (schema: string): string =>
137
+ `REVOKE ALL ON SCHEMA ${quoteIdent(schema)} FROM PUBLIC`;
138
+
139
+ /** `REVOKE ALL ON ALL TABLES IN SCHEMA <schema> FROM PUBLIC` — the `revokeFromPublic` half
140
+ * for every relation in the schema, including PUBLIC's column privileges on those relations
141
+ * (revoke.sgml@REL_18_6: revoking table privileges also revokes the corresponding column
142
+ * privileges). */
143
+ export const revokePublicTablesSql = (schema: string): string =>
144
+ `REVOKE ALL ON ALL TABLES IN SCHEMA ${quoteIdent(schema)} FROM PUBLIC`;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The vocabulary primitives of `Postgres.Grants`: the aclexplode row as it crosses the
3
+ * wire, the word it encodes, the word set a read answers — and the `relkind` vocabulary
4
+ * the PUBLIC-tables clear is scoped to. Pure — no client, no `Effect` — so the reads
5
+ * (`grants-read.ts`), the plans (`grants-plan.ts`, `grants-diff.ts`) and the test fake
6
+ * (`fake-grants-read.ts`) all share one definition of what a word is.
7
+ */
8
+ /** One aclexplode row as it crosses the wire: lowercase declared word with `*` appended
9
+ * when the privilege carries WITH GRANT OPTION (`is_grantable`). */
10
+ export interface AclRow {
11
+ readonly public: boolean;
12
+ readonly privilege: string;
13
+ readonly grantable: boolean;
14
+ }
15
+
16
+ /** Lowercase word, `*` appended when the privilege carries WITH GRANT OPTION. */
17
+ export const encodeWord = (row: {
18
+ readonly privilege: string;
19
+ readonly grantable: boolean;
20
+ }): string => `${row.privilege.toLowerCase()}${row.grantable ? '*' : ''}`;
21
+
22
+ /** Union of one grantee's distinct words over all its aclexplode rows, sorted — two reads
23
+ * of the same state must compare equal, which the re-run-is-a-no-op rule needs. */
24
+ export const wordsOf = (rows: ReadonlyArray<AclRow>, wantPublic: boolean): ReadonlyArray<string> =>
25
+ [...new Set(rows.filter((row) => row.public === wantPublic).map(encodeWord))].sort();
26
+
27
+ /** The relkinds `GRANT/REVOKE … ON ALL TABLES IN SCHEMA` covers — every pg_class relation
28
+ * class except sequences (`S`), composite types (`c`) and indexes (`i`), per
29
+ * `revoke.sgml@REL_18_6` ("ALL TABLES also affects views and foreign tables") and measured
30
+ * on PG 18.6. */
31
+ export const PUBLIC_REVOCABLE_RELKINDS: ReadonlyArray<string> = ['r', 'v', 'm', 'f', 'p'];
32
+
33
+ export const relkindIsPublicRevocable = (relkind: string): boolean =>
34
+ PUBLIC_REVOCABLE_RELKINDS.includes(relkind);
@@ -0,0 +1,110 @@
1
+ /**
2
+ * `Postgres.Grants` — one declarative grant set for one role in one schema of one database:
3
+ * schema USAGE/CREATE, per-table privileges, per-column privileges, default privileges for
4
+ * future tables per creator role, and an optional clear of PUBLIC. Computed as a diff
5
+ * against the catalogs (`grants-read.ts`, through `aclexplode`), so a re-run with nothing
6
+ * changed emits nothing, and drift on any NAMED object is repaired. The lifecycle cores
7
+ * against any client live in `grants-ops.ts`; the refusals in `grants-refuse.ts`; the
8
+ * repair plans in `grants-plan.ts` and the offline diff in `grants-diff.ts`.
9
+ *
10
+ * ★ A GRANT SET CARRIES NO OWNERSHIP MARK (H1, same as `Postgres.Database`): `read` answers
11
+ * `Unowned` for a match, so already-live grants need `adopt(true)` in the stack that
12
+ * declares them. A target that holds NOTHING anywhere reads as ABSENT — that is a first
13
+ * create, never an adoption of an empty schema.
14
+ * ★ OBJECTS THE DECLARED ROLE OWNS ARE LEFT ALONE (H2): the owner holds every privilege
15
+ * implicitly and a `REVOKE` cannot take that away, so the repair and the delete skip
16
+ * objects the catalogs say the role owns (grants-plan.ts, measured on PG 18.6).
17
+ * ⛔ SCOPE IS EXACTLY THE DECLARATION. Objects the declaration does not name are never
18
+ * touched; removing an entry from the declaration is a DRIFT the reconcile repairs — the
19
+ * removed names are revoked before the new declaration's plan runs, so taking an entry
20
+ * away takes the privilege away. `delete` revokes what the LAST declaration named. PUBLIC
21
+ * is only ever cleared, only when `revokeFromPublic: true`, and never re-granted.
22
+ */
23
+ import { Resource } from 'alchemy';
24
+ import { Unowned } from 'alchemy/AdoptPolicy';
25
+ import * as Provider from 'alchemy/Provider';
26
+ import * as Effect from 'effect/Effect';
27
+ import type { PostgresGrantsAttributes, PostgresGrantsProps } from './grants-attrs.ts';
28
+ import { declaredNames, namesFromAttrs, resolveProps } from './grants-declare.ts';
29
+ import {
30
+ deleteWithClient,
31
+ diffPostgresGrants,
32
+ readWithClient,
33
+ reconcileWithClient,
34
+ } from './grants-ops.ts';
35
+ import type { PostgresConnection } from './connection.ts';
36
+ import { withPg } from './connection.ts';
37
+ import { databaseExists } from './database-sql.ts';
38
+ import { PostgresGrantsDatabaseMissing } from './grants-errors.ts';
39
+
40
+ export interface PostgresGrants extends Resource<
41
+ 'Postgres.Grants',
42
+ PostgresGrantsProps,
43
+ PostgresGrantsAttributes,
44
+ never,
45
+ PostgresConnection
46
+ > {}
47
+
48
+ export const PostgresGrants = Resource<PostgresGrants>('Postgres.Grants', {
49
+ defaultRemovalPolicy: 'retain',
50
+ });
51
+
52
+ /** Same dual `typeof` guard as `isPostgresDatabase` (see `database.ts` for the measured
53
+ * regression it exists to prevent). */
54
+ export const isPostgresGrants = (value: unknown): value is PostgresGrants =>
55
+ (typeof value === 'object' || typeof value === 'function') &&
56
+ value !== null &&
57
+ (value as { Type?: unknown }).Type === 'Postgres.Grants';
58
+
59
+ /** The five lifecycle handlers. Exported on its own so a test drives the real implementation
60
+ * (see `database.ts` for why that matters). */
61
+ export const postgresGrantsHandlers = PostgresGrants.Provider.of({
62
+ // ⚠️ EMPTY, NOT A LIVE SWEEP — the same reasoning as Postgres.Database's list: a sweep of
63
+ // every ACL row would enumerate grants nothing declared, across every role and schema.
64
+ list: () => Effect.succeed([]),
65
+ nuke: { skip: true },
66
+
67
+ read: ({ olds, output }) =>
68
+ Effect.gen(function* () {
69
+ const names =
70
+ output !== undefined ? namesFromAttrs(output) : declaredNames(resolveProps(olds));
71
+ // Probe over the FAMILY connection. Opening a database that does not exist yet
72
+ // fails untyped (`ConnectionError` over psql, `UnknownError` 3D000 over the socket);
73
+ // absence is a first create, the same rule `Postgres.Schema` uses.
74
+ const present = yield* withPg((pg) => databaseExists(pg, names.database));
75
+ if (!present) return undefined;
76
+ const live = yield* withPg(
77
+ (pg) => readWithClient(pg, names, output !== undefined),
78
+ names.database,
79
+ );
80
+ return live === undefined ? undefined : Unowned(live);
81
+ }),
82
+
83
+ diff: ({ news, output }) => diffPostgresGrants(news, output),
84
+
85
+ reconcile: ({ news, output }) =>
86
+ Effect.gen(function* () {
87
+ // Probe the declared database BEFORE opening it, like `read`/`delete`: a declaration
88
+ // whose database was dropped would otherwise fail untyped on connect (`ConnectionError`
89
+ // over psql, `UnknownError` 3D000 over the socket). A reconcile must write into an
90
+ // existing container, so a missing database is a typed refusal, not absence.
91
+ const present = yield* withPg((pg) => databaseExists(pg, news.database));
92
+ if (!present) {
93
+ return yield* Effect.fail(new PostgresGrantsDatabaseMissing({ database: news.database }));
94
+ }
95
+ return yield* withPg((pg) => reconcileWithClient(pg, news, output), news.database);
96
+ }),
97
+
98
+ delete: ({ olds }) =>
99
+ Effect.gen(function* () {
100
+ // Same missing-database rule as `read`: the probe stays on the family connection,
101
+ // so a dropped database never becomes a connect failure. `deleteWithClient` then
102
+ // proves `current_database()` on the declared database.
103
+ const present = yield* withPg((pg) => databaseExists(pg, olds.database));
104
+ if (!present) return;
105
+ return yield* withPg((pg) => deleteWithClient(pg, olds), olds.database);
106
+ }),
107
+ });
108
+
109
+ export const PostgresGrantsProvider = () =>
110
+ Provider.succeed(PostgresGrants, postgresGrantsHandlers);