@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,245 @@
1
+ /**
2
+ * The repair plan: the statement list a reconcile executes, as one pure function of the
3
+ * declared grant set and the live catalogs — no client, no `Effect` — so `reconcile`, the
4
+ * engine's `diff` and the tests share one implementation (the family's S10 rule holds at
5
+ * the callers: `reconcile` re-reads live after the statements run, and re-plans: an EMPTY
6
+ * plan is the no-op proof). The read-back projection and the plan-time comparison live in
7
+ * `grants-diff.ts`.
8
+ *
9
+ * ★ REPAIR SHAPE PER CLASS: on drift, `REVOKE ALL` (clears extras and grant options) then
10
+ * `GRANT` the declared words (only when any are declared), split by the grant-option
11
+ * flag. A class that declares nothing and holds nothing live is a no-op; a class that
12
+ * declares nothing but holds live grants gets only the revoke — that is how `delete`
13
+ * (a cleared declaration, `grants-declare.ts`) revokes exactly what the old declaration
14
+ * named. The one thing it restores is the collateral a table `REVOKE ALL` clears on
15
+ * columns the declaration never named (see `restoredColumnGrants` below): a delete takes
16
+ * away what it managed and leaves a DBA's or another resource's column grant untouched.
17
+ * ★ OBJECTS THE DECLARED ROLE OWNS ARE LEFT ALONE (H2): an owner holds every privilege
18
+ * implicitly and a `REVOKE` cannot take that away, so both plans skip owned objects —
19
+ * repairing them would only add ACL rows the state can never converge on, and revoking
20
+ * them would strip rights that existed before the resource (measured on PG 18.6: the
21
+ * docs' own seat example had its owner rights churned). The projection
22
+ * (`grants-diff.ts`) records the ownership facts so the plan-time diff skips the same
23
+ * objects.
24
+ * ★ A TABLE'S `REVOKE ALL` ALSO CLEARS THAT GRANTEE'S COLUMN ENTRIES ON IT (measured on
25
+ * PG 18.6, and cited by the resource's own convergence check): every declared column of
26
+ * a table whose revoke this plan emits is therefore planned against an empty column
27
+ * set, so the declared words are re-granted after the table revoke in the SAME pass —
28
+ * without this, the re-read would answer drift and the repair would refuse.
29
+ */
30
+ import type { DeclaredGrants } from './grants-declare.ts';
31
+ import { splitGrantWords } from './grants-declare.ts';
32
+ import type { LiveGrants } from './grants-read.ts';
33
+ import { removalRevokes } from './grants-plan-restore.ts';
34
+ import { relkindIsPublicRevocable } from './grants-words.ts';
35
+ import {
36
+ grantColumnSql,
37
+ grantDefaultSql,
38
+ grantSchemaSql,
39
+ grantTableSql,
40
+ revokeColumnSql,
41
+ revokeDefaultSql,
42
+ revokePublicSchemaSql,
43
+ revokePublicTablesSql,
44
+ revokeSchemaSql,
45
+ revokeTableSql,
46
+ } from './grants-sql.ts';
47
+
48
+ /** Both sides are sorted by construction (declared by `cleanWords`, live by the reads), so
49
+ * set equality is positional — grant-option marks included: `select` and `select*` are
50
+ * DIFFERENT states and the repair must converge on the declared one. */
51
+ export const wordsDiffer = (
52
+ declared: ReadonlyArray<string>,
53
+ live: ReadonlyArray<string>,
54
+ ): boolean =>
55
+ declared.length !== live.length || declared.some((word, index) => word !== live[index]);
56
+
57
+ const grantStatements = (
58
+ words: ReadonlyArray<string>,
59
+ grant: (words: ReadonlyArray<string>) => string,
60
+ ): ReadonlyArray<string> => {
61
+ const { plain, grantable } = splitGrantWords(words);
62
+ return [
63
+ ...(plain.length > 0 ? [grant(plain)] : []),
64
+ ...(grantable.length > 0 ? [grant(grantable)] : []),
65
+ ];
66
+ };
67
+
68
+ export const liveTableWords = (live: LiveGrants, table: string): ReadonlyArray<string> =>
69
+ live.tables.find((entry) => entry.table === table)?.role ?? [];
70
+
71
+ export const liveColumnWords = (
72
+ live: LiveGrants,
73
+ table: string,
74
+ column: string,
75
+ ): ReadonlyArray<string> =>
76
+ live.columns.find((entry) => entry.table === table && entry.column === column)?.role ?? [];
77
+
78
+ export const liveDefaultWords = (live: LiveGrants, forRole: string): ReadonlyArray<string> =>
79
+ live.defaults.find((entry) => entry.forRole === forRole)?.role ?? [];
80
+
81
+ const columnKey = (table: string, column: string): string => `${table}\u0000${column}`;
82
+
83
+ /** Re-grant the column entries a table `REVOKE ALL` cleared that the declaration does
84
+ * not name. Only `restorable` words: revoke.sgml@REL_18_6, a role revokes grants it made,
85
+ * and a superuser's REVOKE is performed as the owner — a third grantor's entry survives,
86
+ * and granting it again would add an owner grant on top of that one. */
87
+ const restoredColumnGrants = (
88
+ declared: DeclaredGrants,
89
+ live: LiveGrants,
90
+ table: string,
91
+ skip: ReadonlySet<string>,
92
+ ): ReadonlyArray<string> => {
93
+ const statements: string[] = [];
94
+ for (const column of live.columns) {
95
+ if (column.table !== table || skip.has(columnKey(column.table, column.column))) continue;
96
+ if (column.restorable.length === 0) continue;
97
+ statements.push(
98
+ ...grantStatements(column.restorable, (words) =>
99
+ grantColumnSql(declared.schema, column.table, column.column, declared.role, words),
100
+ ),
101
+ );
102
+ }
103
+ return statements;
104
+ };
105
+
106
+ /** Every statement the repair runs, in order. Classes whose live set already equals the
107
+ * declared one contribute NOTHING — an already-correct declaration emits zero statements,
108
+ * which is the re-run-is-a-no-op rule; the same function re-planned against the post-repair
109
+ * read-back is the convergence proof (`PostgresGrantsRepairRefused` when it is not empty). */
110
+ export const planRepair = (
111
+ declared: DeclaredGrants,
112
+ live: LiveGrants,
113
+ prior: {
114
+ /** Tables whose `REVOKE ALL` this same plan already emits (an update removed them). */
115
+ readonly revokedTables?: ReadonlyArray<string>;
116
+ /** Columns that removal is revoking on purpose — do not restore them. */
117
+ readonly revokedColumns?: ReadonlyArray<{ readonly table: string; readonly column: string }>;
118
+ } = {},
119
+ ): ReadonlyArray<string> => {
120
+ const statements: string[] = [];
121
+ const ownedTables = new Set(live.ownedTables);
122
+ if (!live.schemaOwnedByRole && wordsDiffer(declared.schemaPrivileges, live.schema.role)) {
123
+ statements.push(revokeSchemaSql(declared.schema, declared.role));
124
+ statements.push(
125
+ ...grantStatements(declared.schemaPrivileges, (words) =>
126
+ grantSchemaSql(declared.schema, declared.role, words),
127
+ ),
128
+ );
129
+ }
130
+ // A table whose REVOKE is emitted ALSO loses the grantee's column privileges on it
131
+ // (the header's measured rule) — the ones the revoking role or the owner made
132
+ // (`restoredColumnGrants`). Declared columns of such a table are planned against an
133
+ // empty column set. Undeclared entries the revoke clears are re-granted in the same
134
+ // plan, including tables an update removed (`prior.revokedTables`): those revokes are
135
+ // planned offline with this repair so they can share one transaction.
136
+ const revokedTables = new Set(prior.revokedTables ?? []);
137
+ const skipColumns = new Set([
138
+ ...declared.columns.map((column) => columnKey(column.table, column.column)),
139
+ ...(prior.revokedColumns ?? []).map((column) => columnKey(column.table, column.column)),
140
+ ]);
141
+ const declaredTableNames = new Set(declared.tables.map((table) => table.table));
142
+ for (const table of declared.tables) {
143
+ if (ownedTables.has(table.table)) continue;
144
+ if (wordsDiffer(table.privileges, liveTableWords(live, table.table))) {
145
+ revokedTables.add(table.table);
146
+ statements.push(revokeTableSql(declared.schema, table.table, declared.role));
147
+ statements.push(
148
+ ...grantStatements(table.privileges, (words) =>
149
+ grantTableSql(declared.schema, table.table, declared.role, words),
150
+ ),
151
+ );
152
+ statements.push(...restoredColumnGrants(declared, live, table.table, skipColumns));
153
+ }
154
+ }
155
+ for (const table of prior.revokedTables ?? []) {
156
+ if (declaredTableNames.has(table) || ownedTables.has(table)) continue;
157
+ statements.push(...restoredColumnGrants(declared, live, table, skipColumns));
158
+ }
159
+ for (const column of declared.columns) {
160
+ if (ownedTables.has(column.table)) continue;
161
+ const liveWords = revokedTables.has(column.table)
162
+ ? []
163
+ : liveColumnWords(live, column.table, column.column);
164
+ if (wordsDiffer(column.privileges, liveWords)) {
165
+ statements.push(revokeColumnSql(declared.schema, column.table, column.column, declared.role));
166
+ statements.push(
167
+ ...grantStatements(column.privileges, (words) =>
168
+ grantColumnSql(declared.schema, column.table, column.column, declared.role, words),
169
+ ),
170
+ );
171
+ }
172
+ }
173
+ for (const entry of declared.defaults) {
174
+ if (wordsDiffer(entry.privileges, liveDefaultWords(live, entry.forRole))) {
175
+ statements.push(revokeDefaultSql(declared.schema, entry.forRole, declared.role));
176
+ statements.push(
177
+ ...grantStatements(entry.privileges, (words) =>
178
+ grantDefaultSql(declared.schema, entry.forRole, declared.role, words),
179
+ ),
180
+ );
181
+ }
182
+ }
183
+ if (declared.publicSchemaRevoked && live.schema.public.length > 0) {
184
+ statements.push(revokePublicSchemaSql(declared.schema));
185
+ }
186
+ // The PUBLIC-tables check is scoped to the relkinds the bulk revoke actually reaches:
187
+ // a PUBLIC grant on a SEQUENCE in the schema survives `REVOKE ALL ON ALL TABLES IN
188
+ // SCHEMA` (measured on PG 18.6), so counting it would refuse to converge forever.
189
+ if (
190
+ declared.publicTablesRevoked &&
191
+ (live.tables.some(
192
+ (table) => relkindIsPublicRevocable(table.relkind) && table.public.length > 0,
193
+ ) ||
194
+ live.columns.some((column) => column.public.length > 0))
195
+ ) {
196
+ statements.push(revokePublicTablesSql(declared.schema));
197
+ }
198
+ return statements;
199
+ };
200
+
201
+ /** Revoke-only statements for the entries an update REMOVED from the declaration: one
202
+ * `REVOKE ALL` per removed object where the catalogs still show the role's words, so
203
+ * taking an entry away takes the privilege away (a removal is a drift this resource
204
+ * repairs, never a silent retention). Never the schema class (a role/database/schema
205
+ * retarget is refused, so the schema can never gain or lose an entry), never PUBLIC,
206
+ * never a grant, never an object the role OWNS (an owner's implicit rights are not this
207
+ * resource's to revoke), and nothing for an object that no longer exists — it shows no
208
+ * words. */
209
+ export const planRevocations = (
210
+ removed: Pick<DeclaredGrants, 'schema' | 'role' | 'tables' | 'columns' | 'defaults'>,
211
+ live: LiveGrants,
212
+ ): ReadonlyArray<string> => {
213
+ const statements: string[] = [];
214
+ const revoked = removalRevokes(removed, live);
215
+ for (const table of revoked.tables) {
216
+ statements.push(revokeTableSql(removed.schema, table, removed.role));
217
+ }
218
+ for (const column of revoked.columns) {
219
+ statements.push(revokeColumnSql(removed.schema, column.table, column.column, removed.role));
220
+ }
221
+ for (const entry of removed.defaults) {
222
+ if (liveDefaultWords(live, entry.forRole).length > 0) {
223
+ statements.push(revokeDefaultSql(removed.schema, entry.forRole, removed.role));
224
+ }
225
+ }
226
+ return statements;
227
+ };
228
+
229
+ /** Removal revokes and the new declaration's repair, planned against one read. Removed
230
+ * tables count as revoked inside `planRepair`, so the column re-grants that follow a
231
+ * table `REVOKE ALL` are in this list — the caller runs it as one transaction. */
232
+ export const planReconcile = (
233
+ declared: DeclaredGrants,
234
+ live: LiveGrants,
235
+ removed: Pick<DeclaredGrants, 'schema' | 'role' | 'tables' | 'columns' | 'defaults'> | undefined,
236
+ ): ReadonlyArray<string> => {
237
+ const revoked = removed === undefined ? undefined : removalRevokes(removed, live);
238
+ return [
239
+ ...(removed !== undefined ? planRevocations(removed, live) : []),
240
+ ...planRepair(declared, live, {
241
+ revokedTables: revoked?.tables ?? [],
242
+ revokedColumns: removed?.columns ?? [],
243
+ }),
244
+ ];
245
+ };
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Provenance for `Postgres.Grants`: every vocabulary traces to a real constant in the
3
+ * committed `acl.h@REL_18_6` excerpt, the column vocabulary to the GRANT synopsis' own
4
+ * column form, the default-privileges shape to the ALTER DEFAULT PRIVILEGES synopsis, the
5
+ * read path to `aclexplode`'s documented row — and every object class the family does NOT
6
+ * express is named with a reason. Both directions fail on a fixture edit (S38).
7
+ */
8
+ import { describe, expect, test } from 'bun:test';
9
+ import { readFileSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+ import {
12
+ COLUMN_PRIVILEGES,
13
+ SCHEMA_PRIVILEGES,
14
+ TABLE_PRIVILEGES,
15
+ letterForPrivilege,
16
+ } from './grants-attrs.ts';
17
+
18
+ const FIXTURES = join(import.meta.dir, 'fixtures');
19
+ const aclHeader = readFileSync(join(FIXTURES, 'pg-REL_18_6-acl.h.txt'), 'utf8');
20
+ const grantSynopsis = readFileSync(join(FIXTURES, 'pg-REL_18_6-grant-synopsis.txt'), 'utf8');
21
+ const defaultSynopsis = readFileSync(
22
+ join(FIXTURES, 'pg-REL_18_6-alter-default-privileges-synopsis.txt'),
23
+ 'utf8',
24
+ );
25
+ const explodeFunc = readFileSync(join(FIXTURES, 'pg-REL_18_6-aclexplode-func.txt'), 'utf8');
26
+ const defaultAclHeader = readFileSync(join(FIXTURES, 'pg-REL_18_6-pg_default_acl.h.txt'), 'utf8');
27
+
28
+ const ACL_BLOB = '13bb8128747139d9cae66777e4782359ead0a760';
29
+
30
+ /** Pull each `#define ACL_<PRIVILEGE>_CHR '<letter>'` constant out of the committed acl.h. */
31
+ const aclLetter = (name: string): string => {
32
+ const m = new RegExp(`#define ACL_${name}_CHR\\s+'(.)'`).exec(aclHeader);
33
+ if (m === null) throw new Error(`no ACL_${name}_CHR in the committed acl.h excerpt`);
34
+ return m[1] as string;
35
+ };
36
+
37
+ const WORD_LETTER: Readonly<Record<string, string>> = {
38
+ insert: 'INSERT',
39
+ select: 'SELECT',
40
+ update: 'UPDATE',
41
+ delete: 'DELETE',
42
+ truncate: 'TRUNCATE',
43
+ references: 'REFERENCES',
44
+ trigger: 'TRIGGER',
45
+ maintain: 'MAINTAIN',
46
+ usage: 'USAGE',
47
+ create: 'CREATE',
48
+ };
49
+
50
+ describe('grants provenance', () => {
51
+ test('the acl.h excerpt is the pinned REL_18_6 blob', () => {
52
+ expect(aclHeader).toContain(ACL_BLOB);
53
+ expect(aclHeader).toContain('src/include/utils/acl.h');
54
+ expect(explodeFunc).toContain('aclexplode');
55
+ expect(explodeFunc).toContain('is_grantable');
56
+ });
57
+
58
+ test('every table word maps to the acl.h letter the server really stores', () => {
59
+ for (const privilege of TABLE_PRIVILEGES) {
60
+ expect(letterForPrivilege(privilege)).toBe(aclLetter(WORD_LETTER[privilege] as string));
61
+ }
62
+ });
63
+
64
+ test('schema and column words too, CREATE above the lowercase CONNECT trap', () => {
65
+ for (const privilege of SCHEMA_PRIVILEGES) {
66
+ expect(letterForPrivilege(privilege)).toBe(aclLetter(WORD_LETTER[privilege] as string));
67
+ }
68
+ for (const privilege of COLUMN_PRIVILEGES) {
69
+ expect(letterForPrivilege(privilege)).toBe(aclLetter(WORD_LETTER[privilege] as string));
70
+ }
71
+ expect(aclLetter('CREATE')).toBe('C');
72
+ });
73
+
74
+ test('the column vocabulary is exactly the GRANT synopsis column form (table-wide only excluded)', () => {
75
+ expect(grantSynopsis).toContain(
76
+ 'GRANT { { SELECT | INSERT | UPDATE | REFERENCES } ( <replaceable class="parameter">column_name</replaceable>',
77
+ );
78
+ expect([...COLUMN_PRIVILEGES]).toEqual(['select', 'insert', 'update', 'references']);
79
+ for (const tableWideOnly of ['truncate', 'trigger', 'maintain']) {
80
+ expect(COLUMN_PRIVILEGES.includes(tableWideOnly as never)).toBe(false);
81
+ }
82
+ });
83
+
84
+ test('the table vocabulary is the relation grant form of the synopsis', () => {
85
+ expect(grantSynopsis).toContain(
86
+ 'GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | MAINTAIN }',
87
+ );
88
+ expect([...TABLE_PRIVILEGES]).toEqual([
89
+ 'select',
90
+ 'insert',
91
+ 'update',
92
+ 'delete',
93
+ 'truncate',
94
+ 'references',
95
+ 'trigger',
96
+ 'maintain',
97
+ ]);
98
+ });
99
+
100
+ test('default privileges are FOR ROLE per creator, ON TABLES, defaclobjtype r', () => {
101
+ // The synopsis wraps the FOR ROLE clause onto its own line, so assert on that line.
102
+ expect(defaultSynopsis).toContain(
103
+ '[ FOR { ROLE | USER } <replaceable>target_role</replaceable>',
104
+ );
105
+ expect(defaultSynopsis).toContain('ON TABLES');
106
+ expect(defaultAclHeader).toContain('DEFACLOBJ_RELATION');
107
+ expect(defaultAclHeader).toMatch(/#define\s+DEFACLOBJ_RELATION\s+'r'/);
108
+ });
109
+
110
+ test('aclexplode is the read path: one row per privilege, PUBLIC at grantee zero', () => {
111
+ expect(explodeFunc).toContain('<parameter>privilege_type</parameter> <type>text</type>');
112
+ expect(explodeFunc).toContain('pseudo-role PUBLIC');
113
+ });
114
+ });
115
+
116
+ /** Every GRANT synopsis line this family does NOT express, and why. */
117
+ describe('out of scope', () => {
118
+ const notModelled: Readonly<Record<string, string>> = {
119
+ SEQUENCE:
120
+ 'sequences are relations the same ON statement reaches, but no sequence word set is declared',
121
+ FUNCTION:
122
+ 'routine privileges (EXECUTE) are a different vocabulary (ACL_ALL_RIGHTS_FUNCTION); no declaration shape',
123
+ DATABASE:
124
+ 'ALTER DATABASE privileges are cluster-level, not one schema\u2019s grant set; the docs name the boundary',
125
+ LARGE: 'large objects (b/r in acl.h) have no declaration shape and no schema container',
126
+ TYPE: 'types are granted through USAGE on the type, deferred until a consumer needs it',
127
+ };
128
+ test('named with a reason, so a future vocabulary must answer one', () => {
129
+ for (const [name, reason] of Object.entries(notModelled)) {
130
+ expect(reason.length).toBeGreaterThan(10);
131
+ expect(grantSynopsis).toContain(name);
132
+ }
133
+ });
134
+ });
@@ -0,0 +1,105 @@
1
+ /**
2
+ * The grants lifecycle over `makePsqlExecutor`, whose parameter rule the in-memory fake
3
+ * does not share: only strings are inlined (`psql-executor.ts`). An existence check that
4
+ * binds a JS array fails here with "param $2 must be a string" on every reconcile that
5
+ * names a table or column — the transport CT100 actually uses.
6
+ */
7
+ import { describe, expect, test } from 'bun:test';
8
+ import * as Effect from 'effect/Effect';
9
+ import type { PsqlRunner } from './psql-executor.ts';
10
+ import { makePsqlExecutor } from './psql-executor.ts';
11
+ import { reconcileWithClient } from './grants-ops.ts';
12
+ import { requireDeclaredObjectsExist } from './grants-existence.ts';
13
+ import { resolveProps } from './grants-declare.ts';
14
+ import type { PostgresGrantsProps } from './grants-attrs.ts';
15
+
16
+ const props: PostgresGrantsProps = {
17
+ role: 'seat_writer',
18
+ database: 'agents',
19
+ schema: 'app',
20
+ tables: [{ table: 'a.b', privileges: ['select'] }],
21
+ columnGrants: [{ table: 'a.b', column: 'c', privileges: ['select'] }],
22
+ };
23
+
24
+ /** Answers by SQL text. `granted` flips when the repair's `BEGIN` script is seen, so the
25
+ * convergence re-read reports the privileges that script would have written. */
26
+ const runner = (): { run: PsqlRunner; stdins: string[] } => {
27
+ const stdins: string[] = [];
28
+ let granted = false;
29
+ const run: PsqlRunner = ({ stdin }) => {
30
+ stdins.push(stdin);
31
+ if (stdin.startsWith('BEGIN')) granted = true;
32
+ return Promise.resolve({ code: 0, stdout: answer(stdin, granted), stderr: '' });
33
+ };
34
+ return { run, stdins };
35
+ };
36
+
37
+ const answer = (stdin: string, granted: boolean): string => {
38
+ if (stdin.includes('current_database()')) return '[{"database":"agents"}]';
39
+ if (stdin.includes('json_array_elements_text')) return '[{"table":"a"},{"table":"a.b"}]';
40
+ if (stdin.includes('json_array_elements')) {
41
+ return '[{"table":"a","column":"b.c"},{"table":"a.b","column":"c"}]';
42
+ }
43
+ if (stdin.includes('SELECT 1 AS present FROM pg_roles')) return '[{"present":1}]';
44
+ if (stdin.includes('SELECT 1 AS present FROM pg_namespace')) return '[{"present":1}]';
45
+ if (stdin.includes('aclexplode(n.nspacl)')) {
46
+ return granted ? '[{"public":false,"privilege":"usage","grantable":false}]' : '[]';
47
+ }
48
+ if (stdin.includes('aclexplode(c.relacl)')) {
49
+ return granted
50
+ ? '[{"table":"a.b","relkind":"r","public":false,"privilege":"select","grantable":false}]'
51
+ : '[]';
52
+ }
53
+ if (stdin.includes('aclexplode(v.attacl)')) {
54
+ return granted
55
+ ? '[{"table":"a.b","column":"c","public":false,"privilege":"select","grantable":false,"grantor":"postgres","owner":"postgres","revoker":"postgres"}]'
56
+ : '[]';
57
+ }
58
+ if (stdin.includes('aclexplode(d.defaclacl)')) return '[]';
59
+ if (stdin.includes('nspowner')) return '[{"role_owns":false}]';
60
+ if (stdin.includes('relowner')) return '[]';
61
+ return '';
62
+ };
63
+
64
+ describe('grants lifecycle over the psql executor', () => {
65
+ test('existence parameters are one JSON string, and the repair is one BEGIN script', async () => {
66
+ const { run, stdins } = runner();
67
+ const pg = makePsqlExecutor(run, { database: 'agents', username: 'postgres' });
68
+ const attrs = await Effect.runPromise(reconcileWithClient(pg, props, undefined));
69
+ expect(attrs.tables).toEqual([{ table: 'a.b', privileges: ['select'] }]);
70
+ expect(attrs.columns).toEqual([{ table: 'a.b', column: 'c', privileges: ['select'] }]);
71
+ const existence = stdins.filter((stdin) => stdin.includes('json_array_elements'));
72
+ expect(existence.length).toBeGreaterThan(0);
73
+ // Pairs, not a concatenated `a.b.c`: table `a.b` + column `c` stays two JSON elements.
74
+ expect(existence.some((stdin) => stdin.includes('["a.b","c"]'))).toBe(true);
75
+ expect(existence.some((stdin) => stdin.includes('||'))).toBe(false);
76
+ const script = stdins.find((stdin) => stdin.startsWith('BEGIN'));
77
+ expect(script).toContain('COMMIT');
78
+ expect(script).toContain('REVOKE ALL ON "app"."a.b" FROM "seat_writer"');
79
+ expect(script).toContain('GRANT select ("c") ON "app"."a.b" TO "seat_writer"');
80
+ });
81
+
82
+ test('a dotted table and a dotted column are separate JSON pairs', async () => {
83
+ const { run, stdins } = runner();
84
+ const pg = makePsqlExecutor(run, { database: 'agents', username: 'postgres' });
85
+ await Effect.runPromise(
86
+ requireDeclaredObjectsExist(
87
+ pg,
88
+ resolveProps({
89
+ ...props,
90
+ tables: [
91
+ { table: 'a.b', privileges: ['select'] },
92
+ { table: 'a', privileges: ['select'] },
93
+ ],
94
+ columnGrants: [
95
+ { table: 'a.b', column: 'c', privileges: ['select'] },
96
+ { table: 'a', column: 'b.c', privileges: ['select'] },
97
+ ],
98
+ }),
99
+ ),
100
+ );
101
+ const columns = stdins.find((stdin) => stdin.includes('x->>1'));
102
+ expect(columns).toContain('["a.b","c"]');
103
+ expect(columns).toContain('["a","b.c"]');
104
+ });
105
+ });
@@ -0,0 +1,91 @@
1
+ /**
2
+ * The two faces of `read`'s ABSENT answer, replayed against the fake server model: a
3
+ * target that holds nothing and has NO stored output reads as absent (that is a first
4
+ * create, never the adoption of an empty schema — H1), while the same all-empty catalog on
5
+ * the update path — the resource already has applied state — is a real projection (the
6
+ * grantee may own everything outright, so `aclexplode` has no rows), so `read` must not
7
+ * flip the next apply back into a first create. Extracted from
8
+ * `grants-lifecycle-guards.test.ts` to keep both files under the code-size cap.
9
+ */
10
+ import { describe, expect, test } from 'bun:test';
11
+ import * as Effect from 'effect/Effect';
12
+ import { makeFakeGrants } from './fake-grants-sql.ts';
13
+ import { readWithClient } from './grants-ops.ts';
14
+
15
+ const run = <A, E>(eff: Effect.Effect<A, E>): Promise<A> => Effect.runPromise(eff);
16
+
17
+ /** The seat model: `notes` (with its column) and its catalog are the seat's OWN objects;
18
+ * `shared` is someone else's. The schema is still owned by the executor. */
19
+ const ownedCatalog = {
20
+ schemas: ['app'],
21
+ roles: ['postgres', 'seat_writer'],
22
+ tables: [
23
+ { schema: 'app', table: 'notes', owner: 'seat_writer', columns: ['id'] },
24
+ { schema: 'app', table: 'shared' },
25
+ ],
26
+ };
27
+
28
+ describe('read: an empty target is absent', () => {
29
+ test('answers undefined when the role holds nothing anywhere', async () => {
30
+ const fake = makeFakeGrants(ownedCatalog);
31
+ const live = await run(
32
+ readWithClient(
33
+ fake,
34
+ {
35
+ role: 'seat_writer',
36
+ database: 'agents',
37
+ schema: 'app',
38
+ tables: ['notes', 'shared'],
39
+ columns: [{ table: 'notes', column: 'id' }],
40
+ defaults: [],
41
+ },
42
+ false,
43
+ ),
44
+ );
45
+ // A first create, never an adoption of an empty schema: only grants mark the target
46
+ // as live, so the stack declares this grant set with adopt(true) when it is already
47
+ // applied and without it on a first deploy.
48
+ expect(live).toBeUndefined();
49
+ });
50
+
51
+ test('answers the projection when stored output exists and the role holds nothing', async () => {
52
+ const fake = makeFakeGrants(ownedCatalog);
53
+ const live = await run(
54
+ readWithClient(
55
+ fake,
56
+ {
57
+ role: 'seat_writer',
58
+ database: 'agents',
59
+ schema: 'app',
60
+ tables: ['notes', 'shared'],
61
+ columns: [{ table: 'notes', column: 'id' }],
62
+ defaults: [],
63
+ },
64
+ true,
65
+ ),
66
+ );
67
+ // The update path: the resource already has applied state, so an all-empty read is a
68
+ // real answer — the grantee owns everything outright and `aclexplode` has no rows —
69
+ // not an absent target. Answering undefined here would flip the next apply into a
70
+ // first create. The projection still names the declared objects with empty word
71
+ // lists, the way the reconcile's output does for owned tables.
72
+ expect(live).toEqual({
73
+ role: 'seat_writer',
74
+ database: 'agents',
75
+ schema: 'app',
76
+ schemaOwnedByRole: false,
77
+ schemaPrivileges: [],
78
+ tables: [
79
+ { table: 'notes', privileges: [] },
80
+ { table: 'shared', privileges: [] },
81
+ ],
82
+ columns: [{ table: 'notes', column: 'id', privileges: [] }],
83
+ defaults: [],
84
+ // PUBLIC holds no rows at all in this all-empty ACL, so both revoked flags answer
85
+ // true: the projection reports the absence the same way reconcile's output does.
86
+ publicSchemaRevoked: true,
87
+ publicTablesRevoked: true,
88
+ ownedTables: ['notes'],
89
+ });
90
+ });
91
+ });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The default-privilege half of the grants read: what `pg_default_acl` says the declared
3
+ * role receives on relations (`defaclobjtype = 'r'`, the only object type this family
4
+ * declares — `pg_default_acl.h@REL_18_6#DEFACLOBJ_RELATION`) created in the schema,
5
+ * whatever role created the entry. Extracted from `grants-read.ts` so that file stays
6
+ * under the 250-line cap; the read still goes through `aclexplode` (see that file's
7
+ * header for the doctrine), never the `aclitem` text.
8
+ */
9
+ import * as Effect from 'effect/Effect';
10
+ import type { SqlError } from 'effect/unstable/sql/SqlError';
11
+ import type { PgExecutor } from './database-sql.ts';
12
+ import { type AclRow, encodeWord } from './grants-words.ts';
13
+
14
+ /** One row per default-privilege entry on relations in the schema, with the declared
15
+ * role's words, whatever role created the entry. Sorted by forRole so two reads of the
16
+ * same state compare equal. */
17
+ const DEFAULTS_SQL = `SELECT
18
+ pg_get_userbyid(d.defaclrole) AS for_role,
19
+ a.privilege_type AS privilege,
20
+ a.is_grantable AS grantable
21
+ FROM pg_default_acl d
22
+ CROSS JOIN LATERAL aclexplode(d.defaclacl) AS a
23
+ WHERE d.defaclnamespace = (SELECT oid FROM pg_namespace WHERE nspname = $1)
24
+ AND d.defaclobjtype = 'r'
25
+ AND a.grantee = (SELECT oid FROM pg_roles WHERE rolname = $2)
26
+ ORDER BY for_role`;
27
+
28
+ export interface LiveDefault {
29
+ readonly forRole: string;
30
+ readonly role: ReadonlyArray<string>;
31
+ }
32
+
33
+ export const readDefaultAcls = (
34
+ pg: PgExecutor,
35
+ schema: string,
36
+ role: string,
37
+ ): Effect.Effect<ReadonlyArray<LiveDefault>, SqlError> =>
38
+ Effect.map(
39
+ pg.unsafe<{ readonly for_role: string } & AclRow>(DEFAULTS_SQL, [schema, role]),
40
+ (rows) => {
41
+ const byRole = new Map<string, string[]>();
42
+ for (const row of rows) {
43
+ const words = byRole.get(row.for_role) ?? [];
44
+ words.push(encodeWord(row));
45
+ byRole.set(row.for_role, words);
46
+ }
47
+ return [...byRole.entries()]
48
+ .map(([forRole, words]) => ({ forRole, role: [...new Set(words)].sort() }))
49
+ .sort((a, b) => a.forRole.localeCompare(b.forRole));
50
+ },
51
+ );