@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,208 @@
1
+ /**
2
+ * The ownership and refusal guards of `Postgres.Grants`, replayed against the fake server
3
+ * model: objects the declared role OWNS are never touched (an owner holds every privilege
4
+ * implicitly and a REVOKE cannot take that away — measured on PG 18.6, where a repair left
5
+ * the docs' own seat example with an empty ACL on its own schema), the three
6
+ * database-mismatch refusals fire before any statement, the reconcile-side retarget guard
7
+ * closes the plan-time bypass, and `delete` is a no-op when the role or schema is missing or
8
+ * the target holds no grants. The read path's absent-vs-projection faces live in
9
+ * `grants-read-absent.test.ts`.
10
+ */
11
+ import { describe, expect, test } from 'bun:test';
12
+ import * as Effect from 'effect/Effect';
13
+ import { makeFakeGrants } from './fake-grants-sql.ts';
14
+ import { deleteWithClient, readWithClient, reconcileWithClient } from './grants-ops.ts';
15
+ import type { PostgresGrantsAttributes, PostgresGrantsProps } from './grants-attrs.ts';
16
+ import { PostgresGrantsDatabaseMismatch, PostgresGrantsRetargetRefused } from './grants-errors.ts';
17
+
18
+ const run = <A, E>(eff: Effect.Effect<A, E>): Promise<A> => Effect.runPromise(eff);
19
+ const fails = <A, E>(eff: Effect.Effect<A, E>): Promise<E> => Effect.runPromise(Effect.flip(eff));
20
+
21
+ const isWrite = (text: string): boolean => /^(GRANT|REVOKE|ALTER)/.test(text);
22
+
23
+ const baseProps: PostgresGrantsProps = {
24
+ role: 'seat_writer',
25
+ database: 'agents',
26
+ schema: 'app',
27
+ schemaUsage: true,
28
+ };
29
+
30
+ /** The seat model: `notes` (with its column) and its catalog are the seat's OWN objects;
31
+ * `shared` is someone else's. The schema is still owned by the executor. */
32
+ const ownedCatalog = {
33
+ schemas: ['app'],
34
+ roles: ['postgres', 'seat_writer'],
35
+ databases: ['agents'],
36
+ tables: [
37
+ { schema: 'app', table: 'notes', owner: 'seat_writer', columns: ['id'] },
38
+ { schema: 'app', table: 'shared' },
39
+ ],
40
+ };
41
+
42
+ const writesOf = (fake: ReturnType<typeof makeFakeGrants>): ReadonlyArray<string> =>
43
+ fake.statements.map((s) => s.text).filter(isWrite);
44
+
45
+ const applied = (over: Partial<PostgresGrantsAttributes>): PostgresGrantsAttributes => ({
46
+ role: 'seat_writer',
47
+ database: 'agents',
48
+ schema: 'app',
49
+ schemaPrivileges: [],
50
+ tables: [],
51
+ columns: [],
52
+ defaults: [],
53
+ publicSchemaRevoked: false,
54
+ publicTablesRevoked: false,
55
+ schemaOwnedByRole: false,
56
+ ownedTables: [],
57
+ ...over,
58
+ });
59
+
60
+ describe('reconcile: objects the role owns are left alone', () => {
61
+ test('an owned table is skipped by repair, delete and the projection', async () => {
62
+ const fake = makeFakeGrants(ownedCatalog);
63
+ const props: PostgresGrantsProps = {
64
+ ...baseProps,
65
+ tables: [
66
+ { table: 'notes', privileges: ['select'] },
67
+ { table: 'shared', privileges: ['select'] },
68
+ ],
69
+ columnGrants: [{ table: 'notes', column: 'id', privileges: ['select'] }],
70
+ };
71
+ const attrs = await run(reconcileWithClient(fake, props, undefined));
72
+ // Only the NOT-owned table is repaired; the owned one records an empty word list and
73
+ // its name in ownedTables (the owner holds everything implicitly, whatever the ACL says).
74
+ expect(attrs.tables).toEqual([
75
+ { table: 'notes', privileges: [] },
76
+ { table: 'shared', privileges: ['select'] },
77
+ ]);
78
+ expect(attrs.columns).toEqual([{ table: 'notes', column: 'id', privileges: [] }]);
79
+ expect(attrs.ownedTables).toEqual(['notes']);
80
+ expect(attrs.schemaOwnedByRole).toBe(false);
81
+ expect(writesOf(fake)).toEqual([
82
+ 'REVOKE ALL ON SCHEMA "app" FROM "seat_writer"',
83
+ 'GRANT usage ON SCHEMA "app" TO "seat_writer"',
84
+ 'REVOKE ALL ON "app"."shared" FROM "seat_writer"',
85
+ 'GRANT select ON "app"."shared" TO "seat_writer"',
86
+ ]);
87
+ // delete revokes the schema grant and `shared` only: stripping the owner's ACL would
88
+ // strip rights that existed before this resource (the round-1 finding's empty relacl).
89
+ const writesBefore = fake.statements.filter((s) => isWrite(s.text)).length;
90
+ await run(deleteWithClient(fake, props));
91
+ expect(writesOf(fake).slice(writesBefore)).toEqual([
92
+ 'REVOKE ALL ON SCHEMA "app" FROM "seat_writer"',
93
+ 'REVOKE ALL ON "app"."shared" FROM "seat_writer"',
94
+ ]);
95
+ });
96
+
97
+ test('a schema the role owns is skipped entirely by reconcile and delete', async () => {
98
+ const fake = makeFakeGrants({
99
+ schemas: ['app'],
100
+ roles: ['postgres', 'seat_writer'],
101
+ schemaOwner: 'seat_writer',
102
+ tables: [{ schema: 'app', table: 'notes', owner: 'seat_writer' }],
103
+ });
104
+ const props: PostgresGrantsProps = { ...baseProps, schemaCreate: true };
105
+ const attrs = await run(reconcileWithClient(fake, props, undefined));
106
+ expect(attrs.schemaOwnedByRole).toBe(true);
107
+ expect(attrs.schemaPrivileges).toEqual([]);
108
+ // A revoke on the seat's own schema would leave it without USAGE on the very schema it
109
+ // owns — the regression the round-1 review measured — so nothing runs at all.
110
+ expect(writesOf(fake)).toEqual([]);
111
+ await run(deleteWithClient(fake, props));
112
+ expect(writesOf(fake)).toEqual([]);
113
+ });
114
+ });
115
+
116
+ describe('current_database() must be the declared database', () => {
117
+ test('reconcile, read and delete each refuse before any write', async () => {
118
+ const fake = makeFakeGrants({ ...ownedCatalog, connected: 'other_db' });
119
+ const reconcileError = await fails(
120
+ reconcileWithClient(
121
+ fake,
122
+ { ...baseProps, tables: [{ table: 'shared', privileges: ['select'] }] },
123
+ undefined,
124
+ ),
125
+ );
126
+ expect(reconcileError).toBeInstanceOf(PostgresGrantsDatabaseMismatch);
127
+ expect((reconcileError as PostgresGrantsDatabaseMismatch).connected).toBe('other_db');
128
+ const readError = await fails(
129
+ readWithClient(
130
+ fake,
131
+ {
132
+ role: 'seat_writer',
133
+ database: 'agents',
134
+ schema: 'app',
135
+ tables: [],
136
+ columns: [],
137
+ defaults: [],
138
+ },
139
+ false,
140
+ ),
141
+ );
142
+ expect(readError).toBeInstanceOf(PostgresGrantsDatabaseMismatch);
143
+ const deleteError = await fails(deleteWithClient(fake, baseProps));
144
+ expect(deleteError).toBeInstanceOf(PostgresGrantsDatabaseMismatch);
145
+ expect(fake.statements.map((s) => s.text).filter(isWrite)).toEqual([]);
146
+ expect(fake.statements.some((s) => s.text.startsWith('SELECT current_database()'))).toBe(true);
147
+ });
148
+ });
149
+
150
+ describe('reconcile: the retarget guard cannot be bypassed by an unresolved plan', () => {
151
+ test('an output naming a different role, database or schema is refused before any statement', async () => {
152
+ const fake = makeFakeGrants(ownedCatalog);
153
+ const props: PostgresGrantsProps = {
154
+ ...baseProps,
155
+ tables: [{ table: 'shared', privileges: ['select'] }],
156
+ };
157
+ // `news` unresolved at plan time would let the engine's own comparison answer update;
158
+ // reconcile, where news IS resolved, re-checks the persisted identity against the
159
+ // declaration (a same-deploy role rename would otherwise land on the new role while
160
+ // the old one keeps every privilege).
161
+ const variants: ReadonlyArray<readonly [string, PostgresGrantsAttributes]> = [
162
+ ['role', applied({ role: 'seat_old' })],
163
+ ['database', applied({ database: 'other_db' })],
164
+ ['schema', applied({ schema: 'other_schema' })],
165
+ ];
166
+ for (const [prop, output] of variants) {
167
+ const error = await fails(reconcileWithClient(fake, props, output));
168
+ expect(error, prop).toBeInstanceOf(PostgresGrantsRetargetRefused);
169
+ expect((error as PostgresGrantsRetargetRefused).prop).toBe(prop);
170
+ }
171
+ expect(fake.statements).toEqual([]);
172
+ });
173
+ });
174
+
175
+ describe('delete: a no-op when the target is gone or holds nothing', () => {
176
+ test('writes nothing when the grantee role no longer exists', async () => {
177
+ const fake = makeFakeGrants({ ...ownedCatalog, roles: [] });
178
+ await run(
179
+ deleteWithClient(fake, {
180
+ ...baseProps,
181
+ tables: [{ table: 'shared', privileges: ['select'] }],
182
+ }),
183
+ );
184
+ expect(fake.statements.filter((s) => isWrite(s.text))).toEqual([]);
185
+ });
186
+
187
+ test('writes nothing when the schema no longer exists', async () => {
188
+ const fake = makeFakeGrants({ ...ownedCatalog, schemas: [] });
189
+ await run(
190
+ deleteWithClient(fake, {
191
+ ...baseProps,
192
+ tables: [{ table: 'shared', privileges: ['select'] }],
193
+ }),
194
+ );
195
+ expect(fake.statements.filter((s) => isWrite(s.text))).toEqual([]);
196
+ });
197
+
198
+ test('writes nothing when the target holds no grants (the idempotent re-delete)', async () => {
199
+ const fake = makeFakeGrants(ownedCatalog);
200
+ await run(
201
+ deleteWithClient(fake, {
202
+ ...baseProps,
203
+ tables: [{ table: 'shared', privileges: ['select'] }],
204
+ }),
205
+ );
206
+ expect(fake.statements.filter((s) => isWrite(s.text))).toEqual([]);
207
+ });
208
+ });
@@ -0,0 +1,208 @@
1
+ /**
2
+ * The repair-specific lifecycles the round-1 review measured against real PG 18.6, replayed
3
+ * against the fake server model: (1) a table-level REVOKE ALL also clears the grantee's
4
+ * column entries on that table, so a table-words change on a table with declared column
5
+ * grants must re-grant the columns in the SAME pass to converge; (2) removing an entry from
6
+ * the declaration revokes the privilege instead of silently retaining it; (3) default
7
+ * privileges grant, read back and converge; (4) a PUBLIC grant on a SEQUENCE survives the
8
+ * bulk revoke, so it must never block `revokeFromPublic`'s convergence; (5) a multi-word
9
+ * column grant repeats the synopsis per word — every word lands on the column, nothing on
10
+ * the relation, and the parser refuses the single-trailing-list shape PG 18.6 reads as a
11
+ * table-level grant.
12
+ */
13
+ import { describe, expect, test } from 'bun:test';
14
+ import * as Effect from 'effect/Effect';
15
+ import { makeFakeGrants } from './fake-grants-sql.ts';
16
+ import { parseGrantStatement } from './fake-grants-parse.ts';
17
+ import { reconcileWithClient } from './grants-ops.ts';
18
+ import type { PostgresGrantsAttributes, PostgresGrantsProps } from './grants-attrs.ts';
19
+
20
+ const run = <A, E>(eff: Effect.Effect<A, E>): Promise<A> => Effect.runPromise(eff);
21
+
22
+ const isWrite = (text: string): boolean => /^(GRANT|REVOKE|ALTER)/.test(text);
23
+
24
+ const baseProps: PostgresGrantsProps = {
25
+ role: 'seat_writer',
26
+ database: 'agents',
27
+ schema: 'app',
28
+ schemaUsage: true,
29
+ };
30
+
31
+ /** Three relations and both auxiliary roles: `widgets` carries the declared column,
32
+ * `ledger` the later-removed table grant, `app_seq` a sequence for the relkind-scoped
33
+ * PUBLIC check, and `owner_role` the default-privileges creator. */
34
+ const catalog = {
35
+ schemas: ['app'],
36
+ roles: ['postgres', 'seat_writer', 'owner_role'],
37
+ tables: [
38
+ { schema: 'app', table: 'widgets', columns: ['id'] },
39
+ { schema: 'app', table: 'ledger' },
40
+ { schema: 'app', table: 'app_seq', relkind: 'S' },
41
+ ],
42
+ };
43
+
44
+ const writesOf = (fake: ReturnType<typeof makeFakeGrants>): ReadonlyArray<string> =>
45
+ fake.statements.map((s) => s.text).filter(isWrite);
46
+
47
+ describe('reconcile: a table revoke clears that grantee\u2019s column entries', () => {
48
+ test('a table-words change on a table with declared column grants converges in ONE pass', async () => {
49
+ const fake = makeFakeGrants(catalog);
50
+ const declared = (privileges: ReadonlyArray<string>): PostgresGrantsProps => ({
51
+ ...baseProps,
52
+ tables: [{ table: 'widgets', privileges }],
53
+ columnGrants: [{ table: 'widgets', column: 'id', privileges: ['select'] }],
54
+ });
55
+ const first = await run(reconcileWithClient(fake, declared(['select']), undefined));
56
+ const writesBefore = fake.statements.filter((s) => isWrite(s.text)).length;
57
+ // The table's words change while its column grant stays declared: the server's REVOKE
58
+ // ALL also takes the column grant away, so the same pass must re-grant it — planned
59
+ // against an empty column set — or the convergence re-read answers drift and refuses.
60
+ const again = await run(reconcileWithClient(fake, declared(['select', 'insert']), first));
61
+ expect(writesOf(fake).slice(writesBefore)).toEqual([
62
+ 'REVOKE ALL ON "app"."widgets" FROM "seat_writer"',
63
+ 'GRANT insert, select ON "app"."widgets" TO "seat_writer"',
64
+ 'REVOKE ALL ("id") ON "app"."widgets" FROM "seat_writer"',
65
+ 'GRANT select ("id") ON "app"."widgets" TO "seat_writer"',
66
+ ]);
67
+ expect(again.tables).toEqual([{ table: 'widgets', privileges: ['insert', 'select'] }]);
68
+ expect(again.columns).toEqual([{ table: 'widgets', column: 'id', privileges: ['select'] }]);
69
+ });
70
+ });
71
+
72
+ describe('reconcile: removal takes the privilege away', () => {
73
+ test('removing table, column and default entries revokes exactly what they held', async () => {
74
+ const fake = makeFakeGrants(catalog);
75
+ const first = await run(
76
+ reconcileWithClient(
77
+ fake,
78
+ {
79
+ ...baseProps,
80
+ tables: [{ table: 'ledger', privileges: ['select'] }],
81
+ columnGrants: [{ table: 'widgets', column: 'id', privileges: ['select'] }],
82
+ defaultPrivileges: [{ forRole: 'owner_role', privileges: ['select'] }],
83
+ },
84
+ undefined,
85
+ ),
86
+ );
87
+ const writesBefore = fake.statements.filter((s) => isWrite(s.text)).length;
88
+ // The operator deletes every entry: the seat keeps nothing the resource granted, and
89
+ // the schema grant it still declares stays.
90
+ const after = await run(reconcileWithClient(fake, { ...baseProps }, first));
91
+ expect(writesOf(fake).slice(writesBefore)).toEqual([
92
+ 'REVOKE ALL ON "app"."ledger" FROM "seat_writer"',
93
+ 'REVOKE ALL ("id") ON "app"."widgets" FROM "seat_writer"',
94
+ 'ALTER DEFAULT PRIVILEGES FOR ROLE "owner_role" IN SCHEMA "app" REVOKE ALL ON TABLES FROM "seat_writer"',
95
+ ]);
96
+ expect(after.tables).toEqual([]);
97
+ expect(after.columns).toEqual([]);
98
+ expect(after.defaults).toEqual([]);
99
+ expect(after.schemaPrivileges).toEqual(['usage']);
100
+ });
101
+ });
102
+
103
+ describe('reconcile: default privileges', () => {
104
+ test('grant, read back, and converge on a re-run with the previous output', async () => {
105
+ const fake = makeFakeGrants(catalog);
106
+ const props: PostgresGrantsProps = {
107
+ ...baseProps,
108
+ defaultPrivileges: [{ forRole: 'owner_role', privileges: ['select'] }],
109
+ };
110
+ const attrs: PostgresGrantsAttributes = await run(reconcileWithClient(fake, props, undefined));
111
+ expect(attrs.defaults).toEqual([{ forRole: 'owner_role', privileges: ['select'] }]);
112
+ expect(writesOf(fake)).toEqual([
113
+ 'REVOKE ALL ON SCHEMA "app" FROM "seat_writer"',
114
+ 'GRANT usage ON SCHEMA "app" TO "seat_writer"',
115
+ 'ALTER DEFAULT PRIVILEGES FOR ROLE "owner_role" IN SCHEMA "app" REVOKE ALL ON TABLES FROM "seat_writer"',
116
+ 'ALTER DEFAULT PRIVILEGES FOR ROLE "owner_role" IN SCHEMA "app" GRANT select ON TABLES TO "seat_writer"',
117
+ ]);
118
+ const writesBefore = fake.statements.filter((s) => isWrite(s.text)).length;
119
+ const again = await run(reconcileWithClient(fake, props, attrs));
120
+ expect(again.defaults).toEqual([{ forRole: 'owner_role', privileges: ['select'] }]);
121
+ expect(fake.statements.filter((s) => isWrite(s.text)).length).toBe(writesBefore);
122
+ });
123
+ });
124
+
125
+ describe('reconcile: PUBLIC on a sequence', () => {
126
+ test('revokeFromPublic converges with a PUBLIC grant on a sequence in the schema', async () => {
127
+ const fake = makeFakeGrants({
128
+ ...catalog,
129
+ acl: [
130
+ {
131
+ object: { schema: 'app', table: 'app_seq' },
132
+ grantee: 'PUBLIC',
133
+ grantor: 'postgres',
134
+ words: ['usage'],
135
+ },
136
+ ],
137
+ });
138
+ const props: PostgresGrantsProps = { ...baseProps, revokeFromPublic: true };
139
+ const attrs = await run(reconcileWithClient(fake, props, undefined));
140
+ // REVOKE ALL ON ALL TABLES IN SCHEMA does not reach sequences (measured), so the
141
+ // sequence's PUBLIC grant is never counted: the reconcile converges without any
142
+ // PUBLIC statement, and the recorded fact stays relkind-scoped.
143
+ expect(writesOf(fake)).toEqual([
144
+ 'REVOKE ALL ON SCHEMA "app" FROM "seat_writer"',
145
+ 'GRANT usage ON SCHEMA "app" TO "seat_writer"',
146
+ ]);
147
+ expect(attrs.publicSchemaRevoked).toBe(true);
148
+ expect(attrs.publicTablesRevoked).toBe(true);
149
+ const writesBefore = fake.statements.filter((s) => isWrite(s.text)).length;
150
+ await run(reconcileWithClient(fake, props, attrs));
151
+ expect(fake.statements.filter((s) => isWrite(s.text)).length).toBe(writesBefore);
152
+ });
153
+ });
154
+
155
+ describe('reconcile: multi-word column grants land on the column, not the table', () => {
156
+ test('a two-word column declaration converges and leaves no table-level grant', async () => {
157
+ const fake = makeFakeGrants(catalog);
158
+ const props: PostgresGrantsProps = {
159
+ ...baseProps,
160
+ columnGrants: [{ table: 'widgets', column: 'id', privileges: ['select', 'update'] }],
161
+ };
162
+ const attrs = await run(reconcileWithClient(fake, props, undefined));
163
+ // Each word carries its own column list, so `select` and `update` both land on the
164
+ // column and nothing lands on the relation — the re-read projection proves it.
165
+ expect(attrs.columns).toEqual([
166
+ { table: 'widgets', column: 'id', privileges: ['select', 'update'] },
167
+ ]);
168
+ expect(attrs.tables).toEqual([]);
169
+ // The statement that got the words there is the synopsis repeat, not a word list under
170
+ // one trailing column list (which PG 18.6 reads as a table-level grant of all but the last).
171
+ expect(writesOf(fake)).toContain(
172
+ 'GRANT select ("id"), update ("id") ON "app"."widgets" TO "seat_writer"',
173
+ );
174
+ // A re-run with the projection back in is a no-op — the shape converges.
175
+ const writesBefore = fake.statements.filter((s) => isWrite(s.text)).length;
176
+ await run(reconcileWithClient(fake, props, attrs));
177
+ expect(fake.statements.filter((s) => isWrite(s.text)).length).toBe(writesBefore);
178
+ });
179
+ });
180
+
181
+ describe('fake parser: the single-trailing-column-list shape is refused', () => {
182
+ test('a word list under one column list is not a column grant', () => {
183
+ // On PG 18.6 this is a table-level SELECT plus a column-level UPDATE; the builders
184
+ // never emit it, so the parser must not file it as a column-only grant.
185
+ expect(parseGrantStatement('GRANT select, update ("id") ON "app"."widgets" TO "seat"')).toBe(
186
+ undefined,
187
+ );
188
+ });
189
+
190
+ test('the synopsis-repeat shape parses as one column grant with every word', () => {
191
+ const parsed = parseGrantStatement(
192
+ 'GRANT select ("id"), update ("id") ON "app"."widgets" TO "seat"',
193
+ );
194
+ expect(parsed).toEqual({
195
+ kind: 'grant',
196
+ object: { schema: 'app', table: 'widgets', column: 'id' },
197
+ grantee: 'seat',
198
+ words: ['select', 'update'],
199
+ option: false,
200
+ });
201
+ });
202
+
203
+ test('mixed columns in one statement are refused', () => {
204
+ expect(
205
+ parseGrantStatement('GRANT select ("id"), update ("name") ON "app"."widgets" TO "seat"'),
206
+ ).toBe(undefined);
207
+ });
208
+ });
@@ -0,0 +1,181 @@
1
+ /**
2
+ * The base lifecycles of `Postgres.Grants` against `fake-grants-sql.ts`'s server-model
3
+ * fake: greenfield emits REVOKE-then-GRANT per class, a re-run with the previous `output`
4
+ * writes nothing, `read` answers the projection recovered from the attributes, a third
5
+ * grantor's grant survives as `PostgresGrantsRepairRefused`, and the missing role/schema
6
+ * guards refuse before any statement. The repair-specific lifecycles (a table revoke's
7
+ * column collateral, removal revocation, default privileges, a PUBLIC sequence) live in
8
+ * `grants-lifecycle-repair.test.ts`; the ownership and refusal guards in
9
+ * `grants-lifecycle-guards.test.ts`.
10
+ */
11
+ import { describe, expect, test } from 'bun:test';
12
+ import * as Effect from 'effect/Effect';
13
+ import { makeFakeGrants } from './fake-grants-sql.ts';
14
+ import { deleteWithClient, readWithClient, reconcileWithClient } from './grants-ops.ts';
15
+ import { namesFromAttrs } from './grants-declare.ts';
16
+ import type { PostgresGrantsProps } from './grants-attrs.ts';
17
+ import {
18
+ PostgresGrantsRepairRefused,
19
+ PostgresGrantsRoleMissing,
20
+ PostgresGrantsSchemaMissing,
21
+ } from './grants-errors.ts';
22
+
23
+ const run = <A, E>(eff: Effect.Effect<A, E>): Promise<A> => Effect.runPromise(eff);
24
+ const fails = <A, E>(eff: Effect.Effect<A, E>): Promise<E> => Effect.runPromise(Effect.flip(eff));
25
+
26
+ const isWrite = (text: string): boolean => /^(GRANT|REVOKE|ALTER)/.test(text);
27
+
28
+ const baseProps: PostgresGrantsProps = {
29
+ role: 'seat_writer',
30
+ database: 'agents',
31
+ schema: 'app',
32
+ schemaUsage: true,
33
+ };
34
+
35
+ /** The catalog a reconcile needs: the schema and grantee role exist, and one table with two
36
+ * columns so column and table grants have a target. */
37
+ const catalog = {
38
+ schemas: ['app'],
39
+ roles: ['postgres', 'seat_writer'],
40
+ tables: [{ schema: 'app', table: 'widgets', columns: ['id', 'name'] }],
41
+ };
42
+
43
+ describe('reconcile: greenfield', () => {
44
+ test('issues REVOKE-then-GRANT for each declared class and returns the read-back', async () => {
45
+ const fake = makeFakeGrants(catalog);
46
+ const attrs = await run(
47
+ reconcileWithClient(
48
+ fake,
49
+ {
50
+ ...baseProps,
51
+ tables: [{ table: 'widgets', privileges: ['select'] }],
52
+ columnGrants: [{ table: 'widgets', column: 'id', privileges: ['select'] }],
53
+ },
54
+ undefined,
55
+ ),
56
+ );
57
+ expect(attrs.role).toBe('seat_writer');
58
+ expect(attrs.schemaPrivileges).toEqual(['usage']);
59
+ expect(attrs.tables).toEqual([{ table: 'widgets', privileges: ['select'] }]);
60
+ expect(attrs.columns).toEqual([{ table: 'widgets', column: 'id', privileges: ['select'] }]);
61
+ // Reads (the existence checks and the aclexplode projections) are recorded beside the
62
+ // writes; the assertion is on the writes, which are exactly REVOKE-then-GRANT per class.
63
+ const writes = fake.statements.map((s) => s.text).filter(isWrite);
64
+ expect(writes).toEqual([
65
+ 'REVOKE ALL ON SCHEMA "app" FROM "seat_writer"',
66
+ 'GRANT usage ON SCHEMA "app" TO "seat_writer"',
67
+ 'REVOKE ALL ON "app"."widgets" FROM "seat_writer"',
68
+ 'GRANT select ON "app"."widgets" TO "seat_writer"',
69
+ 'REVOKE ALL ("id") ON "app"."widgets" FROM "seat_writer"',
70
+ 'GRANT select ("id") ON "app"."widgets" TO "seat_writer"',
71
+ ]);
72
+ // One transaction: a live grantee never observes the revoke without the grant.
73
+ expect(fake.transactions).toEqual([writes]);
74
+ });
75
+ });
76
+
77
+ describe('reconcile: convergence', () => {
78
+ test('a re-run with the previous output writes nothing', async () => {
79
+ const fake = makeFakeGrants(catalog);
80
+ const props = { ...baseProps, tables: [{ table: 'widgets', privileges: ['select'] }] };
81
+ const attrs = await run(reconcileWithClient(fake, props, undefined));
82
+ const writesBefore = fake.statements.filter((s) => isWrite(s.text)).length;
83
+ // The engine's real update flow: `output` (the persisted attributes) is passed back in,
84
+ // and its names are all still declared, so no removed-entries revocation may fire.
85
+ const again = await run(reconcileWithClient(fake, props, attrs));
86
+ expect(again.tables).toEqual([{ table: 'widgets', privileges: ['select'] }]);
87
+ expect(fake.statements.filter((s) => isWrite(s.text)).length).toBe(writesBefore);
88
+ });
89
+
90
+ test('readWithClient answers the projection recovered from the attributes', async () => {
91
+ const fake = makeFakeGrants(catalog);
92
+ const attrs = await run(
93
+ reconcileWithClient(
94
+ fake,
95
+ {
96
+ ...baseProps,
97
+ tables: [{ table: 'widgets', privileges: ['select'] }],
98
+ columnGrants: [{ table: 'widgets', column: 'id', privileges: ['select'] }],
99
+ },
100
+ undefined,
101
+ ),
102
+ );
103
+ const live = await run(readWithClient(fake, namesFromAttrs(attrs), true));
104
+ expect(live?.tables).toEqual([{ table: 'widgets', privileges: ['select'] }]);
105
+ expect(live?.columns).toEqual([{ table: 'widgets', column: 'id', privileges: ['select'] }]);
106
+ });
107
+
108
+ test('a third grantor\u2019s grant survives REVOKE and surfaces as PostgresGrantsRepairRefused', async () => {
109
+ const fake = makeFakeGrants({
110
+ ...catalog,
111
+ acl: [
112
+ {
113
+ object: { schema: 'app', table: 'widgets' },
114
+ grantee: 'seat_writer',
115
+ grantor: 'someone_else',
116
+ words: ['delete'],
117
+ },
118
+ ],
119
+ });
120
+ const error = await fails(
121
+ reconcileWithClient(
122
+ fake,
123
+ { ...baseProps, tables: [{ table: 'widgets', privileges: ['select'] }] },
124
+ undefined,
125
+ ),
126
+ );
127
+ expect(error).toBeInstanceOf(PostgresGrantsRepairRefused);
128
+ // The surviving grant is a third grantor's 'delete' the executor cannot revoke, so the
129
+ // re-read still plans the table's REVOKE-then-GRANT — that statement list is the refusal.
130
+ expect((error as PostgresGrantsRepairRefused).remaining).toContain(
131
+ 'REVOKE ALL ON "app"."widgets" FROM "seat_writer"',
132
+ );
133
+ });
134
+ });
135
+
136
+ describe('reconcile: guards', () => {
137
+ test('refuses before any write when the grantee role is missing', async () => {
138
+ const fake = makeFakeGrants({ ...catalog, roles: [] });
139
+ const error = await fails(reconcileWithClient(fake, baseProps, undefined));
140
+ expect(error).toBeInstanceOf(PostgresGrantsRoleMissing);
141
+ expect(fake.statements.filter((s) => isWrite(s.text))).toEqual([]);
142
+ });
143
+
144
+ test('refuses when the schema does not exist', async () => {
145
+ const fake = makeFakeGrants({ ...catalog, schemas: [] });
146
+ const error = await fails(reconcileWithClient(fake, baseProps, undefined));
147
+ expect(error).toBeInstanceOf(PostgresGrantsSchemaMissing);
148
+ });
149
+ });
150
+
151
+ describe('delete', () => {
152
+ test('revokes exactly the last declaration\u2019s objects and nothing PUBLIC', async () => {
153
+ const fake = makeFakeGrants(catalog);
154
+ await run(
155
+ reconcileWithClient(
156
+ fake,
157
+ {
158
+ ...baseProps,
159
+ tables: [{ table: 'widgets', privileges: ['select'] }],
160
+ revokeFromPublic: true,
161
+ },
162
+ undefined,
163
+ ),
164
+ );
165
+ const before = fake.statements.length;
166
+ await run(
167
+ deleteWithClient(fake, {
168
+ ...baseProps,
169
+ tables: [{ table: 'widgets', privileges: ['select'] }],
170
+ }),
171
+ );
172
+ const writes = fake.statements
173
+ .slice(before)
174
+ .map((s) => s.text)
175
+ .filter(isWrite);
176
+ expect(writes).toEqual([
177
+ 'REVOKE ALL ON SCHEMA "app" FROM "seat_writer"',
178
+ 'REVOKE ALL ON "app"."widgets" FROM "seat_writer"',
179
+ ]);
180
+ });
181
+ });