@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,250 @@
1
+ /**
2
+ * The read half of `Postgres.Grants`: what the catalogs say the declared role and PUBLIC
3
+ * may do on one schema, its relations, its columns and its default privileges — plus the
4
+ * existence check the plan refuses on before any statement runs. The ownership facts
5
+ * (`nspowner`/`relowner`) live in `grants-ownership.ts`; the word vocabulary and the
6
+ * PUBLIC relkind set live in `grants-words.ts`.
7
+ *
8
+ * ★ READS GO THROUGH `aclexplode`, NOT THE `aclitem` TEXT. Measured at
9
+ * `func.sgml@REL_18_6` ("Access Privilege Inquiry Functions", committed as
10
+ * `fixtures/pg-REL_18_6-aclexplode-func.txt`): `aclexplode(aclitem[])` returns
11
+ * `(grantor oid, grantee oid, privilege_type text, is_grantable boolean)`, one row per
12
+ * granted privilege, with the PUBLIC pseudo-role at grantee oid zero — so the read path
13
+ * never parses `grantee=privs/grantor` letters at all. The letters still appear in
14
+ * `grants-provenance.test.ts`, where the declared words are traced to the `ACL_*_CHR`
15
+ * constants the server really stores, but no shipped query depends on them.
16
+ * ★ `grantee = (SELECT oid FROM pg_roles WHERE rolname = $1)` IS MISSING-ROLE-SAFE BY
17
+ * CONSTRUCTION: a role that does not exist yields `NULL`, `NULL = anything` is `NULL`,
18
+ * and `WHERE NULL` selects nothing — the role simply reads as holding no privileges.
19
+ * ★ `relkind` IS NOT FILTERED. A schema's `pg_class` rows carry `relacl` whether the
20
+ * relation is a table, a view or a sequence, and the seat model grants SELECT on a
21
+ * shared ledger VIEW in another schema through the same `GRANT … ON` statement the docs'
22
+ * table form accepts for views (`grant.sgml@REL_18_6`: "ALL TABLES also affects views
23
+ * and foreign tables"). A word the relkind cannot hold (e.g. INSERT on a sequence) is
24
+ * refused by the server itself as a statement error, never by a word check: deciding
25
+ * that at plan time would need a second relkind query this family does not read.
26
+ * ★ `relkind` IS CAST TO TEXT (`c.relkind::text`) BECAUSE THE COLUMN IS TYPE `"char"`
27
+ * (OID 18), which `@effect/sql-pg`'s socket transport does not decode: `PgTypes` has
28
+ * no codec for OID 18, so the raw `bytea`-shaped bytes arrive (a plain table reads as
29
+ * `Uint8Array([114])`, never `'r'`) and `relkindIsPublicRevocable` (`grants-words.ts`)
30
+ * would say PUBLIC holds nothing a `REVOKE ALL ON ALL TABLES IN SCHEMA` reaches.
31
+ * `::text` returns OID 25, which decodes as a string. Same trap as the `datlocprovider`
32
+ * CASE in `database-sql.ts`, which casts `"char"` columns to text for the same reason.
33
+ */
34
+ import * as Effect from 'effect/Effect';
35
+ import type { SqlError } from 'effect/unstable/sql/SqlError';
36
+ import type { PgExecutor } from './database-sql.ts';
37
+ import { readOwnedTables, readSchemaOwnership } from './grants-ownership.ts';
38
+ import { type LiveDefault, readDefaultAcls } from './grants-read-defaults.ts';
39
+ import { type AclRow, encodeWord, wordsOf } from './grants-words.ts';
40
+
41
+ const SCHEMA_PRESENT_SQL = 'SELECT 1 AS present FROM pg_namespace WHERE nspname = $1';
42
+
43
+ export const schemaExists = (pg: PgExecutor, schema: string): Effect.Effect<boolean, SqlError> =>
44
+ Effect.map(
45
+ pg.unsafe<{ readonly present: number }>(SCHEMA_PRESENT_SQL, [schema]),
46
+ (rows) => rows.length > 0,
47
+ );
48
+
49
+ /** Schema-level rows for the declared role and for PUBLIC in one query: the repair target
50
+ * (`role`) and the `revokeFromPublic` check (`public`) share one round trip. */
51
+ const SCHEMA_ACL_SQL = `SELECT
52
+ a.grantee = 0 AS public,
53
+ a.privilege_type AS privilege,
54
+ a.is_grantable AS grantable
55
+ FROM pg_namespace n
56
+ CROSS JOIN LATERAL aclexplode(n.nspacl) AS a
57
+ WHERE n.nspname = $1
58
+ AND (a.grantee = 0 OR a.grantee = (SELECT oid FROM pg_roles WHERE rolname = $2))`;
59
+
60
+ export interface LiveSchemaAcl {
61
+ readonly role: ReadonlyArray<string>;
62
+ readonly public: ReadonlyArray<string>;
63
+ }
64
+
65
+ export const readSchemaAcl = (
66
+ pg: PgExecutor,
67
+ schema: string,
68
+ role: string,
69
+ ): Effect.Effect<LiveSchemaAcl, SqlError> =>
70
+ Effect.map(pg.unsafe<AclRow>(SCHEMA_ACL_SQL, [schema, role]), (rows) => ({
71
+ role: wordsOf(rows, false),
72
+ public: wordsOf(rows, true),
73
+ }));
74
+
75
+ /** One row per relation in the schema that holds ANY acl entry for the declared role or
76
+ * PUBLIC, with each grantee's words and the relation's `relkind` — sorted by table name.
77
+ * The relkind is NOT a filter on what is read (see the header): it scopes the
78
+ * `revokeFromPublic` CHECK to the relations `REVOKE ALL ON ALL TABLES IN SCHEMA` actually
79
+ * reaches (measured on PG 18.6: a PUBLIC grant on a sequence survives that statement, so
80
+ * counting it would refuse to converge). */
81
+ const TABLES_SQL = `SELECT
82
+ c.relname AS table,
83
+ c.relkind::text AS relkind,
84
+ a.grantee = 0 AS public,
85
+ a.privilege_type AS privilege,
86
+ a.is_grantable AS grantable
87
+ FROM pg_class c
88
+ CROSS JOIN LATERAL aclexplode(c.relacl) AS a
89
+ WHERE c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = $1)
90
+ AND (a.grantee = 0 OR a.grantee = (SELECT oid FROM pg_roles WHERE rolname = $2))
91
+ ORDER BY c.relname`;
92
+
93
+ export interface LiveTable {
94
+ readonly table: string;
95
+ readonly relkind: string;
96
+ readonly role: ReadonlyArray<string>;
97
+ readonly public: ReadonlyArray<string>;
98
+ }
99
+
100
+ export const readTableAcls = (
101
+ pg: PgExecutor,
102
+ schema: string,
103
+ role: string,
104
+ ): Effect.Effect<ReadonlyArray<LiveTable>, SqlError> =>
105
+ Effect.map(
106
+ pg.unsafe<AclRow & { readonly table: string; readonly relkind: string }>(TABLES_SQL, [
107
+ schema,
108
+ role,
109
+ ]),
110
+ (rows) => {
111
+ const byTable = new Map<string, { relkind: string; role: string[]; public: string[] }>();
112
+ for (const row of rows) {
113
+ const entry = byTable.get(row.table) ?? { relkind: row.relkind, role: [], public: [] };
114
+ entry[row.public ? 'public' : 'role'].push(encodeWord(row));
115
+ byTable.set(row.table, entry);
116
+ }
117
+ return [...byTable.entries()]
118
+ .map(([table, entry]) => ({
119
+ table,
120
+ relkind: entry.relkind,
121
+ role: [...new Set(entry.role)].sort(),
122
+ public: [...new Set(entry.public)].sort(),
123
+ }))
124
+ .sort((a, b) => a.table.localeCompare(b.table));
125
+ },
126
+ );
127
+
128
+ /** One row per column ACL in the schema (user columns only: `attnum > 0`, not dropped),
129
+ * for the declared role and PUBLIC — sorted by table then column. PUBLIC column rows are
130
+ * read so `revokeFromPublic` converges on them too: `REVOKE ALL ON ALL TABLES IN SCHEMA`
131
+ * clears them in the same statement, but only grants made by the revoking role or by the
132
+ * owner (which holds every grant option) — a third grantor's grant would otherwise survive
133
+ * past a repair with nothing re-read to notice it.
134
+ *
135
+ * `grantor` is selected for the same rule on a table-level `REVOKE ALL` (revoke.sgml@REL_18_6:
136
+ * a role revokes only what it granted; a superuser's REVOKE is performed as the owner).
137
+ * `restorable` is those words — the collateral re-grant restores them and leaves a third
138
+ * grantor's entry where the revoke left it, instead of adding an owner grant on top.
139
+ * `revoker_as_owner` gates the `grantor === owner` arm: the revoker clears the owner's grant
140
+ * only when it revokes AS the owner — a superuser or a member of the owning role
141
+ * (`pg_has_role(..., 'USAGE')`, `select_best_grantor` acl.c@REL_18_6). A non-superuser,
142
+ * non-member revoker clears only grants it made, so an owner grant marked restorable would be re-granted over an entry the revoke never removed. */
143
+ const COLUMNS_SQL = `SELECT
144
+ c.relname AS table,
145
+ v.attname AS column,
146
+ a.grantee = 0 AS public,
147
+ a.privilege_type AS privilege,
148
+ a.is_grantable AS grantable,
149
+ pg_get_userbyid(a.grantor) AS grantor,
150
+ pg_get_userbyid(c.relowner) AS owner,
151
+ current_user AS revoker,
152
+ pg_has_role(current_user, c.relowner, 'USAGE') AS revoker_as_owner
153
+ FROM pg_attribute v
154
+ JOIN pg_class c ON c.oid = v.attrelid
155
+ CROSS JOIN LATERAL aclexplode(v.attacl) AS a
156
+ WHERE c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = $1)
157
+ AND v.attnum > 0
158
+ AND NOT v.attisdropped
159
+ AND (a.grantee = 0 OR a.grantee = (SELECT oid FROM pg_roles WHERE rolname = $2))
160
+ ORDER BY c.relname, v.attname`;
161
+
162
+ export interface LiveColumn {
163
+ readonly table: string;
164
+ readonly column: string;
165
+ readonly role: ReadonlyArray<string>;
166
+ readonly public: ReadonlyArray<string>;
167
+ /** Role words granted by `current_user`, or by the relation owner when this session revokes
168
+ * AS the owner (superuser or owner-member) — what a table `REVOKE ALL` here actually clears. */
169
+ readonly restorable: ReadonlyArray<string>;
170
+ }
171
+
172
+ export const readColumnAcls = (
173
+ pg: PgExecutor,
174
+ schema: string,
175
+ role: string,
176
+ ): Effect.Effect<ReadonlyArray<LiveColumn>, SqlError> =>
177
+ Effect.map(
178
+ pg.unsafe<
179
+ AclRow & {
180
+ readonly table: string;
181
+ readonly column: string;
182
+ readonly grantor: string;
183
+ readonly owner: string;
184
+ readonly revoker: string;
185
+ readonly revoker_as_owner: boolean;
186
+ }
187
+ >(COLUMNS_SQL, [schema, role]),
188
+ (rows) => {
189
+ const byColumn = new Map<
190
+ string,
191
+ { role: string[]; public: string[]; restorable: string[] }
192
+ >();
193
+ for (const row of rows) {
194
+ const key = `${row.table}\u0000${row.column}`;
195
+ const entry = byColumn.get(key) ?? { role: [], public: [], restorable: [] };
196
+ const word = encodeWord(row);
197
+ entry[row.public ? 'public' : 'role'].push(word);
198
+ if (
199
+ !row.public &&
200
+ (row.grantor === row.revoker || (row.grantor === row.owner && row.revoker_as_owner))
201
+ ) {
202
+ entry.restorable.push(word);
203
+ }
204
+ byColumn.set(key, entry);
205
+ }
206
+ return [...byColumn.entries()]
207
+ .map(([key, entry]) => {
208
+ const [table, column] = key.split('\u0000');
209
+ return {
210
+ table: table as string,
211
+ column: column as string,
212
+ role: [...new Set(entry.role)].sort(),
213
+ public: [...new Set(entry.public)].sort(),
214
+ restorable: [...new Set(entry.restorable)].sort(),
215
+ };
216
+ })
217
+ .sort((a, b) => a.table.localeCompare(b.table) || a.column.localeCompare(b.column));
218
+ },
219
+ );
220
+
221
+ /** Everything the reconcile and diff paths need about one schema, read in one bounded
222
+ * burst of parallel queries (one pool per operation — `withPg` opens and closes it).
223
+ * `schemaOwnedByRole`/`ownedTables` are the ownership facts (pg_namespace.nspowner,
224
+ * pg_class.relowner, `grants-ownership.ts`) the repair needs so it can LEAVE objects the
225
+ * declared role owns alone: an owner holds every privilege implicitly, and a `REVOKE`
226
+ * cannot take that away (`grants-plan.ts`). */
227
+ export interface LiveGrants {
228
+ readonly schemaExists: boolean;
229
+ readonly schema: LiveSchemaAcl;
230
+ readonly tables: ReadonlyArray<LiveTable>;
231
+ readonly columns: ReadonlyArray<LiveColumn>;
232
+ readonly defaults: ReadonlyArray<LiveDefault>;
233
+ readonly schemaOwnedByRole: boolean;
234
+ readonly ownedTables: ReadonlyArray<string>;
235
+ }
236
+
237
+ export const readGrants = (
238
+ pg: PgExecutor,
239
+ schema: string,
240
+ role: string,
241
+ ): Effect.Effect<LiveGrants, SqlError> =>
242
+ Effect.all({
243
+ schemaExists: schemaExists(pg, schema),
244
+ schema: readSchemaAcl(pg, schema, role),
245
+ tables: readTableAcls(pg, schema, role),
246
+ columns: readColumnAcls(pg, schema, role),
247
+ defaults: readDefaultAcls(pg, schema, role),
248
+ schemaOwnedByRole: readSchemaOwnership(pg, schema, role),
249
+ ownedTables: readOwnedTables(pg, schema, role),
250
+ });
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Every plan-time refusal and the `diff` sweep, checked without a client: the retarget
3
+ * refusal (role/database/schema), `diff` never answering `replace`, the privilege/duplicate/
4
+ * name refusals through `diff`, and `defaultRemovalPolicy: 'retain'` declared in source.
5
+ */
6
+ import { describe, expect, test } from 'bun:test';
7
+ import { readFileSync } from 'node:fs';
8
+ import * as Effect from 'effect/Effect';
9
+ import { diffPostgresGrants } from './grants-ops.ts';
10
+ import type { PostgresGrantsAttributes, PostgresGrantsProps } from './grants-attrs.ts';
11
+ import {
12
+ PostgresGrantsDuplicateObject,
13
+ PostgresGrantsNameRefused,
14
+ PostgresGrantsPrivilegeRefused,
15
+ PostgresGrantsRetargetRefused,
16
+ } from './grants-errors.ts';
17
+
18
+ const base: PostgresGrantsProps = {
19
+ role: 'seat_writer',
20
+ database: 'agents',
21
+ schema: 'app',
22
+ };
23
+
24
+ const output: PostgresGrantsAttributes = {
25
+ role: 'seat_writer',
26
+ database: 'agents',
27
+ schema: 'app',
28
+ schemaPrivileges: ['usage'],
29
+ tables: [{ table: 'widgets', privileges: ['select'] }],
30
+ columns: [],
31
+ defaults: [],
32
+ publicSchemaRevoked: true,
33
+ publicTablesRevoked: true,
34
+ schemaOwnedByRole: false,
35
+ ownedTables: [],
36
+ };
37
+
38
+ describe('diff: retarget refusals', () => {
39
+ test('a changed role, database or schema is refused, never a silent update', async () => {
40
+ for (const [prop, value] of [
41
+ ['role', 'other_role'],
42
+ ['database', 'other_db'],
43
+ ['schema', 'other_schema'],
44
+ ] as const) {
45
+ const error = await Effect.runPromise(
46
+ Effect.flip(diffPostgresGrants({ ...base, [prop]: value }, output)),
47
+ );
48
+ expect(error, prop).toBeInstanceOf(PostgresGrantsRetargetRefused);
49
+ expect((error as PostgresGrantsRetargetRefused).prop).toBe(prop);
50
+ }
51
+ });
52
+ });
53
+
54
+ describe('diff: declarations', () => {
55
+ test('a declaration matching the attributes answers noop', async () => {
56
+ const result = await Effect.runPromise(
57
+ diffPostgresGrants(
58
+ { ...base, tables: [{ table: 'widgets', privileges: ['select'] }] },
59
+ output,
60
+ ),
61
+ );
62
+ expect(result).toEqual({ action: 'noop' });
63
+ });
64
+
65
+ test('any word change, new table or new PUBLIC revoke answers update, NEVER replace', async () => {
66
+ const variants: ReadonlyArray<Partial<PostgresGrantsProps>> = [
67
+ { tables: [{ table: 'widgets', privileges: ['select', 'insert'] }] },
68
+ {
69
+ tables: [
70
+ { table: 'widgets', privileges: ['select'] },
71
+ { table: 'gadgets', privileges: ['select'] },
72
+ ],
73
+ },
74
+ { schemaUsage: false },
75
+ { columnGrants: [{ table: 'widgets', column: 'id', privileges: ['select'] }] },
76
+ { defaultPrivileges: [{ forRole: 'owner', privileges: ['select'] }] },
77
+ ];
78
+ for (const variant of variants) {
79
+ const result = await Effect.runPromise(
80
+ diffPostgresGrants(
81
+ { ...base, tables: [{ table: 'widgets', privileges: ['select'] }], ...variant },
82
+ output,
83
+ ),
84
+ );
85
+ expect(result?.action, `variant ${JSON.stringify(variant)}`).toBe('update');
86
+ expect(result).not.toEqual({ action: 'replace' });
87
+ }
88
+ });
89
+
90
+ test('the plan-time refusals surface through diff: privilege, duplicate, name', async () => {
91
+ const privilege = await Effect.runPromise(
92
+ Effect.flip(
93
+ diffPostgresGrants(
94
+ { ...base, tables: [{ table: 'widgets', privileges: ['execute'] }] },
95
+ output,
96
+ ),
97
+ ),
98
+ );
99
+ expect(privilege).toBeInstanceOf(PostgresGrantsPrivilegeRefused);
100
+
101
+ const duplicate = await Effect.runPromise(
102
+ Effect.flip(
103
+ diffPostgresGrants(
104
+ {
105
+ ...base,
106
+ tables: [
107
+ { table: 'widgets', privileges: ['select'] },
108
+ { table: 'widgets', privileges: ['insert'] },
109
+ ],
110
+ },
111
+ output,
112
+ ),
113
+ ),
114
+ );
115
+ expect(duplicate).toBeInstanceOf(PostgresGrantsDuplicateObject);
116
+
117
+ // The over-long name must not also be a retarget (role/database/schema), so the byte
118
+ // limit is exercised through a table name while identity stays unchanged.
119
+ const name = await Effect.runPromise(
120
+ Effect.flip(
121
+ diffPostgresGrants(
122
+ { ...base, tables: [{ table: 'a'.repeat(64), privileges: ['select'] }] },
123
+ output,
124
+ ),
125
+ ),
126
+ );
127
+ expect(name).toBeInstanceOf(PostgresGrantsNameRefused);
128
+ });
129
+ });
130
+
131
+ describe('removal policy', () => {
132
+ test('defaultRemovalPolicy is retain (declared alongside PostgresGrants, checked in source)', () => {
133
+ const source = readFileSync(new URL('./grants.ts', import.meta.url), 'utf8');
134
+ expect(source).toContain("defaultRemovalPolicy: 'retain'");
135
+ });
136
+ });
@@ -0,0 +1,119 @@
1
+ /**
2
+ * The plan-time refusals of `Postgres.Grants`, checked without a client: a word outside
3
+ * its vocabulary, a word declared both plain and `*`-marked, a duplicate object and the
4
+ * over-long name. Pure — no client, no `Effect` — so `reconcile` and the engine's `diff`
5
+ * refuse exactly the same way.
6
+ */
7
+ import {
8
+ COLUMN_PRIVILEGES,
9
+ SCHEMA_PRIVILEGES,
10
+ TABLE_PRIVILEGES,
11
+ grantsNameByteRefusal,
12
+ } from './grants-attrs.ts';
13
+ import { type DeclaredGrants, baseWord } from './grants-declare.ts';
14
+
15
+ /** The first refusal inside one word list: a word outside the vocabulary (the base word of
16
+ * a `*`-marked one, and a bare `*` is never a privilege). `undefined` when the list is
17
+ * in scope. */
18
+ const wordRefusal = (
19
+ words: ReadonlyArray<string>,
20
+ vocabulary: ReadonlyArray<string>,
21
+ ): string | undefined => {
22
+ for (const word of words) {
23
+ if (word === '*') return word;
24
+ const bare = baseWord(word);
25
+ if (!vocabulary.includes(bare as never)) return bare;
26
+ }
27
+ return undefined;
28
+ };
29
+
30
+ /** One refusal inside the resolved declaration, already discriminated for its typed error.
31
+ * Words are checked against their vocabulary; names are checked for duplicates (two
32
+ * entries for one table, one column pair, or one `forRole` would silently merge in the
33
+ * sort and hide an operator's mistake). */
34
+ export type DeclarationRefusal =
35
+ | { readonly kind: 'privilege'; readonly prop: string; readonly word: string }
36
+ | { readonly kind: 'duplicate'; readonly prop: string; readonly name: string };
37
+
38
+ /** One refusal inside one validated word list: first a word outside its vocabulary, then
39
+ * a base word declared both plain and `*`-marked — the server keeps ONE aclitem per
40
+ * grantee per grantor, so `select` and `select*` cannot both hold and the set could never
41
+ * converge (`grants-plan.test.ts` pins the refusal). */
42
+ const listRefusal = (
43
+ words: ReadonlyArray<string>,
44
+ vocabulary: ReadonlyArray<string>,
45
+ prop: string,
46
+ ): DeclarationRefusal | undefined => {
47
+ const word = wordRefusal(words, vocabulary);
48
+ if (word !== undefined) return { kind: 'privilege', prop, word };
49
+ const seen = new Set<string>();
50
+ for (const entry of words) {
51
+ const base = baseWord(entry);
52
+ if (seen.has(base)) return { kind: 'duplicate', prop, name: base };
53
+ seen.add(base);
54
+ }
55
+ return undefined;
56
+ };
57
+
58
+ export const declarationRefusal = (declared: DeclaredGrants): DeclarationRefusal | undefined => {
59
+ const schema = listRefusal(declared.schemaPrivileges, SCHEMA_PRIVILEGES, 'schemaPrivileges');
60
+ if (schema !== undefined) return schema;
61
+ for (const table of declared.tables) {
62
+ const refusal = listRefusal(table.privileges, TABLE_PRIVILEGES, `tables.${table.table}`);
63
+ if (refusal !== undefined) return refusal;
64
+ }
65
+ for (const column of declared.columns) {
66
+ const refusal = listRefusal(
67
+ column.privileges,
68
+ COLUMN_PRIVILEGES,
69
+ `columnGrants.${column.table}.${column.column}`,
70
+ );
71
+ if (refusal !== undefined) return refusal;
72
+ }
73
+ for (const entry of declared.defaults) {
74
+ const refusal = listRefusal(
75
+ entry.privileges,
76
+ TABLE_PRIVILEGES,
77
+ `defaultPrivileges.${entry.forRole}`,
78
+ );
79
+ if (refusal !== undefined) return refusal;
80
+ }
81
+ const duplicate = (names: ReadonlyArray<string>): string | undefined => {
82
+ const seen = new Set<string>();
83
+ for (const name of names) {
84
+ if (seen.has(name)) return name;
85
+ seen.add(name);
86
+ }
87
+ return undefined;
88
+ };
89
+ const table = duplicate(declared.tables.map((entry) => entry.table));
90
+ if (table !== undefined) return { kind: 'duplicate', prop: 'tables', name: table };
91
+ const column = duplicate(declared.columns.map((entry) => `${entry.table}\0${entry.column}`));
92
+ if (column !== undefined)
93
+ return { kind: 'duplicate', prop: 'columnGrants', name: column.replace('\0', '.') };
94
+ const forRole = duplicate(declared.defaults.map((entry) => entry.forRole));
95
+ if (forRole !== undefined) return { kind: 'duplicate', prop: 'defaultPrivileges', name: forRole };
96
+ return undefined;
97
+ };
98
+
99
+ /** Refuse the first declared name the server would silently truncate — the grantee role,
100
+ * the database, the schema, every table, every table-and-column pair, every `forRole`
101
+ * (roles, schemas, tables and columns are all `NameData`; the same limit
102
+ * `Postgres.Database` enforces). `undefined` when every name is in range. */
103
+ export const grantsNamesRefusal = (
104
+ declared: DeclaredGrants,
105
+ ): { readonly name: string; readonly byteLength: number; readonly limit: number } | undefined => {
106
+ const names = [
107
+ declared.role,
108
+ declared.database,
109
+ declared.schema,
110
+ ...declared.tables.map((table) => table.table),
111
+ ...declared.columns.flatMap((column) => [column.table, column.column]),
112
+ ...declared.defaults.map((entry) => entry.forRole),
113
+ ];
114
+ for (const name of names) {
115
+ const refusal = grantsNameByteRefusal(name);
116
+ if (refusal !== undefined) return { name, ...refusal };
117
+ }
118
+ return undefined;
119
+ };
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Regression for the critical finding: `TABLES_SQL` selecting `c.relkind` raw made
3
+ * `@effect/sql-pg`'s socket transport hand back the `"char"` column (OID 18) as raw
4
+ * bytes — `PgTypes` has no codec for that OID, so a plain table read as
5
+ * `Uint8Array([114])`, never `'r'` — and `relkindIsPublicRevocable` (`grants-words.ts`)
6
+ * then saw nothing a `REVOKE … ON ALL TABLES IN SCHEMA` reaches: the PUBLIC clear was
7
+ * never planned and `attributesOf` recorded `publicTablesRevoked: true` while PUBLIC
8
+ * still held its words. The fix casts `c.relkind::text` (OID 25, decoded as a string),
9
+ * same shape as the `datlocprovider` CASE in `database-sql.ts`.
10
+ *
11
+ * ⛔ THIS DOES NOT HIT A LIVE SOCKET. It stays faithful to the real defect by pinning
12
+ * the vendor's own decode for both OIDs (`PgTypes.decode` returns the raw bytes
13
+ * unchanged for the codec-less OID 18 — measured `PgTypes.ts@4.0.0-rc.115`) and by
14
+ * driving the production `readGrants` through `makeFakeGrants`, whose tables branch
15
+ * (`fake-grants-read.ts`) shapes `relkind` per the query TEXT exactly as the wire
16
+ * would: a string only because the SQL casts `::text`. Reverting the cast makes the
17
+ * fake answer raw bytes again, and every assertion below fails.
18
+ */
19
+ import { describe, expect, test } from 'bun:test';
20
+ import * as Effect from 'effect/Effect';
21
+ import * as Result from 'effect/Result';
22
+ import * as PgTypes from '@effect/sql-pg/PgTypes';
23
+ import { readGrants } from './grants-read.ts';
24
+ import { planRepair } from './grants-plan.ts';
25
+ import { attributesOf } from './grants-diff.ts';
26
+ import { declaredNames, resolveProps } from './grants-declare.ts';
27
+ import { makeFakeGrants } from './fake-grants-sql.ts';
28
+
29
+ const declared = resolveProps({
30
+ role: 'seat_writer',
31
+ database: 'widgets',
32
+ schema: 'app',
33
+ tables: [{ table: 'widgets', privileges: ['select'] }],
34
+ revokeFromPublic: true,
35
+ });
36
+
37
+ /** PUBLIC holds SELECT on a plain table (`relkind` `'r'`): the exact state the raw-bytes
38
+ * bug misread. */
39
+ const makeFake = () =>
40
+ makeFakeGrants({
41
+ schemas: ['app'],
42
+ roles: ['seat_writer'],
43
+ tables: [{ schema: 'app', table: 'widgets', columns: ['id'], relkind: 'r' }],
44
+ acl: [
45
+ {
46
+ object: { schema: 'app', table: 'widgets' },
47
+ grantee: 'PUBLIC',
48
+ grantor: 'postgres',
49
+ words: ['select'],
50
+ },
51
+ ],
52
+ });
53
+
54
+ describe('measured: the "char" relkind column decodes raw on the socket transport', () => {
55
+ test('PgTypes.decode returns the raw bytes for OID 18 — there is no "char" codec', () => {
56
+ const decoded = PgTypes.decode(new Uint8Array([114]), 18, 1);
57
+ if (!Result.isSuccess(decoded)) throw new Error('fixture: decode failed for OID 18');
58
+ expect(decoded.success instanceof Uint8Array).toBe(true);
59
+ expect(decoded.success).not.toBe('r');
60
+ });
61
+
62
+ test('the same byte string decodes as a string through the OID 25 text codec', () => {
63
+ const decoded = PgTypes.decode(new Uint8Array([114]), PgTypes.OID.text, 1);
64
+ if (!Result.isSuccess(decoded)) throw new Error('fixture: decode failed for OID 25');
65
+ expect(decoded.success).toBe('r');
66
+ });
67
+ });
68
+
69
+ describe('readGrants: the ::text cast is what makes relkind a string', () => {
70
+ test('the tables read selects c.relkind::text and answers a string relkind', async () => {
71
+ const fake = makeFake();
72
+ const live = await Effect.runPromise(readGrants(fake, 'app', 'seat_writer'));
73
+
74
+ const tablesRead = fake.statements.find((statement) =>
75
+ statement.text.includes('aclexplode(c.relacl)'),
76
+ );
77
+ expect(tablesRead?.text).toContain('c.relkind::text AS relkind');
78
+
79
+ expect(live.tables).toHaveLength(1);
80
+ const row = live.tables[0] as (typeof live.tables)[number];
81
+ expect(typeof row.relkind).toBe('string');
82
+ expect(row.relkind).toBe('r');
83
+ expect(row.public).toEqual(['select']);
84
+ });
85
+
86
+ test('a PUBLIC grant on a plain table is planned for revoke and recorded as not-yet-revoked', async () => {
87
+ const live = await Effect.runPromise(readGrants(makeFake(), 'app', 'seat_writer'));
88
+
89
+ expect(planRepair(declared, live)).toContain(
90
+ 'REVOKE ALL ON ALL TABLES IN SCHEMA "app" FROM PUBLIC',
91
+ );
92
+ expect(attributesOf(live, declaredNames(declared)).publicTablesRevoked).toBe(false);
93
+ });
94
+
95
+ test('the raw-bytes row shape the uncast column produces would hide the PUBLIC grant again', async () => {
96
+ const live = await Effect.runPromise(readGrants(makeFake(), 'app', 'seat_writer'));
97
+ // What the pre-fix SQL received on the socket: relkind as the codec-less OID 18 bytes.
98
+ const buggyLive = {
99
+ ...live,
100
+ tables: live.tables.map((table) => ({
101
+ ...table,
102
+ relkind: new Uint8Array([114]) as unknown as string,
103
+ })),
104
+ };
105
+
106
+ expect(planRepair(declared, buggyLive)).not.toContain(
107
+ 'REVOKE ALL ON ALL TABLES IN SCHEMA "app" FROM PUBLIC',
108
+ );
109
+ expect(attributesOf(buggyLive, declaredNames(declared)).publicTablesRevoked).toBe(true);
110
+ });
111
+ });